Skip to content

feat(payouts): owner walkthrough session 1 — the /payouts/[id] block - #178

Merged
guarzo merged 1 commit into
mainfrom
design-walkthrough-session-1
Aug 8, 2026
Merged

feat(payouts): owner walkthrough session 1 — the /payouts/[id] block#178
guarzo merged 1 commit into
mainfrom
design-walkthrough-session-1

Conversation

@guarzo

@guarzo guarzo commented Aug 8, 2026

Copy link
Copy Markdown
Owner

Session 1 of docs/design-walkthrough.md — the /payouts/[id] block, which carries both P0s.

Acceptance criteria (from the walkthrough), all met: the battle-report edit control is visible at rest and reachable on touch; Finalize's consequence is readable before arming; the notes save confirms visibly; the flat-value path is discoverable without a screen reader.

The two P0s

1.1 — the edit trigger had no affordance. The walkthrough described it as hover-only. It isn't: .btn--quiet renders at --ink-faint with a transparent border at all times, so this is a contrast/affordance problem rather than a display one — which also means it affected every InlineEdit trigger on the page, not just the battle-report one. Fixed with a --rule-strong border scoped to .inline-edit only (verified not a no-op against the .btn / .btn--quiet / :hover cascade, and verified not to leak to the three sibling controls that are not descendants).

1.2 — Finalize's cost sentence was hidden from sighted operators permanently. It was .visually-hidden always, so the copy explaining what Finalize does told nobody. Reported as an ordering problem; it is actually R4 (information living only in the assistive-tech channel). Adds a third ConfirmCost mode, "visible" — rendered plainly at rest, no reveal step, still the aria-describedby target. "reveal" stays wrong here because prose appearing mid-press reads as an error rather than a cost, and the <td>-reflow constraint behind "hidden" (#108/#111/#112) is explicitly not relaxed — DESIGN.md's R2 is amended to record that.

The rest

1.3 notes save confirmation ("· saved", one role="status" node carrying both channels) · 1.4 accessible name vs visible label parity on "Add another paste" · 1.5 drops the non-varying "Price source" column · 1.6 inlines the payment list at N=1 · 1.7 moves the frozen-roster paragraph beside its roster · 1.8 surfaces primaryStage as a · next: hint.

All eight have e2e coverage.

Verification

gate result
format:check clean
typecheck clean
lint clean
npm test 83 files / 1281 tests passed
npm run build clean
check-node-version.sh clean
npm run test:e2e 304 passed / 1 failed — see below

The unit file count matches the pre-change count, so no test file was silently dropped by a load failure.

The one e2e failure is pre-existing, and it hides a real bug

notes save from an always-open textarea, twice running fails intermittently. It is flaky on main, not caused by this branch. Measured at n=5 per variant:

version failures
pre-Session-1 main 1 / 5
this branch (and two other variants tried) 2 / 5 – 2 / 10

An earlier 3-run comparison pointed the other way; at higher n it doesn't hold.

An instrumented probe shows what's happening: the second Save emits no POST at all. useSubmitGuard latches inFlight on click and releases it only from an effect that must observe a pending === true render followed by a pending === false one. When the action settles before React commits the pending render, that transition is missed, the latch never releases, and every subsequent click is preventDefaulted.

So this is a user-facing lost-save bug, not just a test artifact: save, then save again quickly, and the second save silently does nothing. playwright.config.ts sets no retries, so CI on main is already intermittently red from it.

Left unfixed deliberatelysubmit-guard.ts is a shared primitive behind every submit button in the app, and fixing it is well outside this block's scope. Flagging for a separate change.

Also worth a look

  • notes-form.tsx gates its confirmation on comparing the textarea against the value the action actually submitted, rather than a dirty flag. The flag version had a real defect: it cleared unconditionally when the action resolved, but the textarea stays editable during the round trip, so typing through a save left "· saved" standing over text the server never received.
  • 1.5's implementation folds a manual badge into the item-name cell, slightly beyond that finding's literal text.
  • ConfirmCost's "hidden" mode now has zero callers — keep with coverage, or delete?

Findings 1.1-1.8 from docs/design-walkthrough.md, the block carrying both
P0s. Acceptance criteria met: the battle-report edit control is visible at
rest and reachable on touch; Finalize's consequence is readable before
arming; the notes save confirms visibly; the flat-value path is
discoverable without a screen reader.

1.1  .btn--quiet's transparent rest state gave InlineEdit's "edit" trigger
     no visible affordance. It was never hover-only display — it renders at
     --ink-faint always — so this is contrast, not visibility. Adds a
     --rule-strong border under .inline-edit only.

1.2  ConfirmCost's cost sentence was .visually-hidden permanently, so no
     sighted operator ever read it. That is R4's failure (information living
     only in the AT channel), not an ordering problem. Adds a third
     visibility mode, "visible": rendered plainly at rest, no reveal step.
     "reveal" stays wrong here — prose appearing mid-press reads as an error,
     not a cost — and the <td> reflow constraint behind "hidden" is untouched.

1.3  The notes save produced no sighted feedback. Adds a "· saved"
     confirmation in one role="status" span, both channels through one node.

1.4  "Add another paste" named only the appraise path while the accessible
     name also named the flat-value escape hatch — R4 in the other
     direction.

1.5  Drops the items table's non-varying "Price source" column, folding
     unresolved/manual into the item-name cell.

1.6  Inlines the payment list at N=1 rather than collapsing one row behind a
     disclosure.

1.7  Moves the frozen-roster paragraph beside the roster it explains.

1.8  Surfaces primaryStage as a "· next:" hint on the lifecycle control.

Verification: format:check, typecheck, lint, build and check-node-version
all clean; unit suite 83 files / 1281 tests passing (same file count as
before, so nothing was silently dropped); e2e 304 passed / 1 failed.

That one failure is `notes save ... twice running`, and it is pre-existing
and flaky on main, not caused by this branch — measured at n=5, main itself
fails it 1/5 while the variants here fail 2/5 to 2/10. An instrumented probe
shows the second Save emits no POST: useSubmitGuard latches inFlight on
click and releases it only from an effect that must observe a pending=true
render then a pending=false one, so when the action settles before React
commits the pending render the latch never releases and later clicks are
preventDefault'ed. That is a real lost-save bug, not only a test artifact,
and playwright.config.ts sets no retries. Left unfixed deliberately:
submit-guard.ts is a shared primitive behind every submit button and is
outside this block's scope.
@coderabbitai

coderabbitai Bot commented Aug 8, 2026

Copy link
Copy Markdown

Warning

Review limit reached

You’ve reached a temporary PR review limit under our Fair Usage Limits Policy.

Your recent review volume is higher than typical usage, so adaptive limits are currently applied.

Next review available in: 49 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: be38c36b-a7da-4dcc-b896-bbeb59bb63ec

📥 Commits

Reviewing files that changed from the base of the PR and between db60037 and a121b0d.

📒 Files selected for processing (9)
  • DESIGN.md
  • e2e/payouts.spec.ts
  • src/app/_components/confirm-submit.tsx
  • src/app/globals.css
  • src/app/payouts/[id]/appraise-form.tsx
  • src/app/payouts/[id]/lifecycle-submit.tsx
  • src/app/payouts/[id]/notes-form.tsx
  • src/app/payouts/[id]/page.tsx
  • src/app/payouts/[id]/payment-history.tsx

Comment @coderabbitai help to get the list of available commands.

@guarzo
guarzo merged commit 33441a2 into main Aug 8, 2026
7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant