Capxul #934 — S14 payroll screen on CapxulPayroll + CapxulPayrollRun

Infrastructure #1629 / #1631. Dev server of branch codex/1629-payroll-s14-refit (head 8ec9f780) on http://localhost:3000 against Railway staging, embedded Openfort signer (no dev seed). Driven by apps/web/tests/e2e/payroll-s14.spec.ts (Playwright, headed Chromium). Desktop 1440×900 and mobile 390×844 per frame. E-mail echoes masked per person. Every frame was opened and inspected; findings are written under each.

ItemValue
Staging Site / APIconvex-site-production-29eb.up.railway.app / convex-backend-production-f220.up.railway.app, backend head c1b9f62b
Packages@capxul/sdk@2.5.0, @capxul/sdk-react@2.5.0
Organizationorg_01M1FP6XCFF82BZWSR706SEZXQ ("Payroll Run mtjbnso7"), created through the product by a reserved-lane owner on 2026-09-01 23:56Z; treasury 10,000 USD (operator faucet); two payees invited and active
Run2026-09-02 00:20Z; journey stopped at the confirm step, see frame 4

1. Payroll page

Payroll page, desktopPayroll page, mobile

Shows: "Wallet Balance 10,000 USD · Funds Available" and "Paid This Month 0 USD" from CapxulPayroll.Summary; "Active Streams" and "Scheduled Payroll" on their frozen "Not available yet" (STREAMS); three action cards; "Ongoing Payrolls" empty state "Payroll schedules: Not available yet · Use Pay now" (row 14.11, frozen); "Payroll Groups" with "Create Group". Mobile stacks the cards and keeps the bottom navigation.
No defects. The "N" badge bottom-left is the Next.js dev indicator, not product. Ruling needed: the "Paid This Month" helper reads "Organization payments" once the value is real; the pre-refit copy was "Payment history unavailable" (that branch was unreachable before).

2. Create payroll group sheet

Group sheet, desktopGroup sheet, mobile

Shows: "Create payroll group", name, colour picker, "Add Recipients" with two members selected from the real roster (CapxulPayrollRun.Recipients), one amount row per member, the two-row group summary "Recipients 2 People · Total default payout 2 USD", "Save changes". Mobile renders the same sheet as a full-height modal.
Defect (not this PR): recipient names render as mailboxes because the address book labels members by mailbox — infrastructure #1651. The "Full-time" badge replaces the guessed "Admin" label (flagged for ruling in the PR body).

3. Group card after save

Group card, desktopGroup card, mobile

Shows: the saved server-side group "2 members · 2 USD/payroll · Manage" under "Payroll Groups", toast "Payroll group created". This group is stored by org(id).payroll.groups.save, not in browser storage.
No defects.

4. Pay-now sheet

Pay now sheet, desktopPay now sheet, mobile

Shows: two recipients with 1 USD each, Token "USDC (Base) - 10,000 USD" (label frozen, value from .Asset), summary "Recipients 2 People · Total payout 2 USD · Platform fee $0.00 (Free tier - 1% /month) · Total deducted 2 USD · Balance after 9,998 USD", the onchain notice, confirm button.
Blocker: the confirm reads "Preparing signer…" and never enables, because client.signer.status() stays unknown on a fresh session — the browser signer's readiness cycle starts only on the first signature (infrastructure #1667). The run, the "Payroll sent" frame and the group deletion frame are therefore missing; this gallery is regenerated once #1667 lands.

5. Recording