Skip to content

Contacts and paperwork forms: what a failed submit exposes (A11Y-001); contact focus and paperwork composer refusal, blank paste, focus and draft fixed - #778

Merged
NewWorldVenture merged 24 commits into
mainfrom
claude/form-a11y-etzqg7
Oct 4, 2026
Merged

NewWorldVenture merged 24 commits into
mainfrom
claude/form-a11y-etzqg7

Conversation

@NewWorldVenture

@NewWorldVenture NewWorldVenture commented Oct 1, 2026 •

Copy link
Copy Markdown
Owner

This is JIMMY-BLK-FORM-A11Y-20261001, assigned in owner review 5383861397 on #777: the two-form slice of A11Y-001, branched from main at 01b2c380, continued under FLEET-NEXT-BLACKSTONE-20261002-1340. It started test-only.

Three files. #775 (046bc33b) has landed on main, and main (d25e39ea) is merged in as 7c2bb3e3 with no conflicts. The copy of #775's patch carried here (e0062338) therefore drops out of the diff. What remains:

  • tests/e2e/form-error-semantics.spec.ts: new, the walk and its regressions.
  • components/modules/paperwork-module.tsx: the composer fixes, in b5d75c80, 361a9717, 65b6cf24 and 915eb158.
  • components/modules/contacts-module.tsx: the contact submit fix, in bd4b711a.

What was walked

  • Pages: /dashboard/contacts (components/modules/contacts-module.tsx, the Field-based form in a Modal) and /dashboard/paperwork (components/modules/paperwork-module.tsx, an inline composer with native required and a server action).
  • Fixture: a signed-in Plus parent of a disposable local household. It reuses tests/e2e/helpers/durable-session.ts createOwnedAccount unchanged and uses synthetic records only. Nothing reaches a provider.
  • Widths: 390 and 1280 px.
  • Accessibility tree: read from Chromium's own tree over CDP (Accessibility.getFullAXTree). That is browser evidence of what assistive technology is given, not evidence from a person using a screen reader.

Controls: pass on main

Form Observed
Contacts, no name One role=alert reading "Name is required". It is not hidden from Chromium's tree by the aria-modal dialog (ignored=false), focus stays on Full name (aria-required=true), and nothing is written.
Contacts, malformed email The browser's own constraint validation: typeMismatch, a non-empty validationMessage, focus on Email, nothing written.
Contacts, database refusal (POST /rest/v1/family_contacts answered 409/23505) One alert in the reader's words ("That already exists. Try a different value."), exposed in the tree. The dialog stays open and the typed name is kept.
Contacts, recovery role=status "Contact added", the dialog closes, focus returns to the opener, and the contact reads back after a reload.
Paperwork, empty paste The browser's required-field refusal: valueMissing, focus on the textarea, no action request.
Paperwork, real paste Saved, and it reads back after a reload.

Not treated as defects: the paperwork form has no Field or aria-invalid, but its required-field refusal uses the browser's native semantics. A contact validation error is a toast that names the field in text. The live-region checks found no duplicate announcements: one alert node per refusal.

Defects

# Defect Where (existing register rows) Status
1 A refused contact save drops focus to <body>, outside the aria-modal dialog. The contact dialog's submit used the shared Button's loading, which sets disabled (COMPONENT-2795B661F080). Fixed in the contact form (bd4b711a): the submit is aria-disabled while saving, with its own spinner, and a ref refuses a second submit while one is in flight. The shared components/ui/button.tsx is unchanged, so its other loading users keep the old behaviour.
2 At 1280×800, after "no name, then refused", the stacked error toasts cover the dialog's Add Contact button, and the stack pauses its own dismissal on hover. components/ui/toast.tsx (COMPONENT-433AE57D4A2F); the meal-toast class recorded 2026-09-27. Fixed (8dcd67b3, cleared by the owner in 5968640347). Only the two desktop placement class lists in toast.tsx changed: from lg the stack sits bottom-right and each toast is at most 14rem (lg:max-w-56), so it clears a centred dialog. While a toast shows, it covers Quick capture and the AI button in that corner. Mobile placement and #788's lines are unchanged.
3 A refused paperwork paste showed "Minified React error #441; visit https://react.dev/errors/441 …". The composer catch (COMPONENT-E0312E0A8050; ACTION-CD1E12585CD4). Fixed (b5d75c80). The catch reads the error with refusalForThrown and shows actionRefusal.*, as useActionError does.
4 Focus dropped to <body> after a refused and after a saved paste. The pending disabled submit, then the composer unmounting around focus. Fixed (361a9717, refined in 65b6cf24 after review 5941818072). The pending submit is aria-disabled, and a ref refuses a second submit. A save hands focus to the Add paperwork toggle only while the composer that sent it still holds the focus.
5 A whitespace-only paste closed as done, with nothing saved and nothing said. Native required accepts spaces; addPaperworkAction trims to empty and answers { ok: true } (ACTION-CD1E12585CD4). Fixed, option (a), client only (b5d75c80). A blank paste is cleared and refused by the browser like an empty one, and no action request is sent. addPaperworkAction is unchanged.
6 A paste the server refused was wiped, sender included. <form action>: React resets a form's fields when its action settles, refused or not. Fixed (915eb158). The composer submits from onSubmit with FormData and the same transition, so nothing resets the fields; it still closes on success. Reproduced without a stub: a paste over the 1 MB server-action body limit is refused by the framework.
7 A save from a composer that was closed and reopened closed the new one (pre-existing). setComposerOpen(false) on every success. Fixed (915eb158). A save closes only the composer that sent it.

About the held I18N-011 work (#705, review 5384023815): fixes #1 and #4–#7 touch no action, error or toast call. Fix #3 changes only which text the composer's own catch hands to toastError; the addPaperworkAction / describeActionError calls are unchanged. The held commits are unpublished, so I could not diff their exact hunks. If they also rewrite this catch, the two meet there.

Local evidence only: a real thrown refusal from a temporary NOT VALID check constraint (removed afterwards) showed the #441 text before #3. With #3 it reads "Some of what was entered can't be accepted. Check the form and try again." (actionRefusal.invalid, read from the digest).

Also observed, outside this slice:

  • The paperwork toggle reads a hard-coded English "Add paperwork" / "Close" and has no aria-expanded.
  • A save sent while the dashboard sidebar's mount read (loadSidebarPrefs) is still in flight waits for it, because Next runs one server action at a time. It's normally fast; see 52b30a8d below.

Runs

Local production build (next build --webpack) plus next start, local Supabase on current migrations, pinned Chromium 149.0.7827.55, --retries=0. No timeouts or retries changed, and no assertions weakened. Each regression was shown to fail on the head before its fix.

Head Local spec Hosted CI
e0062338 (test-only, main source) 13/13 as expected, ×3 36911902381: all four jobs passed
b5d75c80 (#3, #5a) 13/13 as expected, ×3 36916524997: all four jobs passed
361a9717 (#4) 13/13 as expected, ×3 36920596558: all four jobs passed, E2E 1,922
65b6cf24 (#4 tied to focus ownership) 15/15 as expected, ×3 36936324068: all four jobs passed, E2E 1,924
915eb158 (#6, #7) 19/19 as expected, ×3 36942913853: all four jobs passed, E2E 1,927 + 1 flaky (this spec's Plus-plan setup step; see below)
81ba4dc9 (spec only: the setup step reports and retries) 19/19, then 38/38 at --repeat-each=2 with 2 workers 36946449483: Web, Database and E2E 1,928 passed; Mobile red at npm audit (node-forge GHSA-86w9-cpqp-85rv, not this PR's; see 5943332101); the one re-run failed at the same step
7c2bb3e3 (main d25e39ea merged) 19/19 on a fresh build of the merged tree, local Supabase at migration 0474 37016542658: Web, Database and E2E 1,928, 0 flaky passed; Mobile red on the same advisory
bd4b711a (#1 fixed in the contact form) 21/21, ×3: 20 ordinary passes and 1 expected failure (#2). #1 failed at both widths on 7c2bb3e3. The changed cases pass 30/30 at --repeat-each=5. 37025780260: Web and Database passed; E2E 1,929 + 1 flaky (this spec's paperwork focus case; see below); Mobile red on the same advisory
52b30a8d (spec only: paperwork cases start once the page's own actions settle) Full spec 21/21, ×2. The flaky case's reproduction (4 workers, 40 runs): 4–6 failed before, 40/40 after. 37037469109: Web, Database and E2E 1,930, 0 flaky passed; Mobile red on the same advisory
d316a6ba (#2 fixed in 8dcd67b3; main 1421c745 merged, with ci.yml and the mobile audit allowlist taken from main) Full spec 26/26, ×2; the toast cases 30/30 at --repeat-each=5 with 4 workers. The desktop toast cases failed at the obstruction/width assertions on 0afe6974, while 390 px passed. The 6 other specs that load the real toast: 192/192. The 64 unit test files that read toast.tsx: 1,329 passed. tsc and eslint clean. 37120858007: Web, Database, Mobile and E2E 1,944, 0 flaky passed
b5bf7551 (toast stack lifted to lg:bottom-40 above the corner controls; main 4b9a1361 merged) Full spec 37/38 then 38/38 (the miss: the unchanged 390x844 long corner row, 1 of 15 runs); the toast and corner cases 54/54 at --repeat-each=3 with 4 workers. The desktop CORNER rows matched the characterization on 736e1ffd and now expect nothing covered. Real-toast specs 83/83. The 64 unit test files that read toast.tsx: 1,330 passed. tsc and eslint clean. See 5969754039. 37126950007: Web, Database and Mobile passed; E2E 1,970 passed, 1 failed: the unchanged 390x844 long corner row, on both attempts. The pointer at the orb landed on the notice's body, not its Ausblenden button
0f6f495c (spec only: the corner pointer columns compare who takes the tap, not which part of the notice) The corner cases 36/36 at --repeat-each=3 with 4 workers on the b5bf7551 build. A hit outside the stack still has to match by name, so the desktop rows are as strict as before. tsc and eslint clean. 37129161936: Web, Database, Mobile and E2E 2,350 passed, 3 skipped, 0 failed
03eecdec (spec only: the blocked/clear invariant made explicit per button, notices read at rest, and a moved-clear negative control at 390 px) First reproduced: with the fade-in held mid-slide, b5bf7551's spec failed 3/3 with CI's exact diff and 0f6f495c's spec passed 3/3. The negative control with the move removed failed 3/3. Corner describe (12 + 3 controls) 75/75 at --repeat-each=5 with 4 workers; full spec 41/41, ×2. tsc and eslint clean. See 5970327350. 37131441755: Web, Database, Mobile and E2E 2,353 passed, 3 skipped, 0 failed, 0 flaky
f3988ccb (below lg the stack stops left of the corner buttons: px-4 → pl-4 pr-[calc(5rem+var(--safe-right))]; lg: unchanged) Baseline-red on the old-class build: 7 failed (the 390 corner rows, the 390 placement and the 390 controls), then fix-green 25/25. Full spec 50/50, ×2. Corner describe (12 cases + 12 replaced-placement negative controls) 124/125 then 125/125 at --repeat-each=5 with 4 workers; the miss was a desktop keyboard navigation timeout under load. Real-toast specs 83/83; 64 unit files 1,331 passed; tsc and eslint clean. Remaining occlusion: at 390 the language bar (long, stacked notices) and the open dialog's Phone / Alternate phone, both as before. At 360 the narrower toast makes a long German notice also reach "Ersten Kontakt hinzufügen" and the dialog's Freund / Sonstiges chips. See 5971353434. 37138934479: Web, Database, Mobile and E2E 2,361 passed, 3 skipped, 1 flaky (paperwork closed-composer case: server-action wait timeout, passed on retry; not the toast)
d3b3a51c (spec only: corner cases also at 360x780, recorded as RESIDUAL) 360 rows as measured on f3988ccb: both corner buttons clear; a long German notice also reaches "Ersten Kontakt hinzufügen", and three reach "Add First Contact". Full-width 360 rows added to BEFORE with matching negative controls. 360 cases 6/6; corner describe (15 + 15 controls) 93/93 at --repeat-each=3 with 4 workers; full spec 56/56; tsc and eslint clean. Options measured (insets, max-height, compact, clamp) in 5971751746. 37141287524: Web, Database, Mobile and E2E 2,368 passed, 3 skipped, 0 flaky
93c306f6 (below lg the right inset is 4.75rem, not 5rem: pr-[calc(4.75rem+var(--safe-right))]) Baseline-red on the 5rem build: at 360x780 one long notice covered the dialog's Freund and Sonstiges chips, took Sonstiges, and took the centre of "Ersten Kontakt hinzufügen". Fix-green on the 4.75rem build. Full spec 62/62, ×2. Corner describe (15 cases + 15 negative controls + 6 dialog-open + placement) 185/185 at --repeat-each=5 with 4 workers. Real-toast specs 83/83; 64 unit files 1,331 passed; tsc and eslint clean; no flaky. Residual, not closed: at 360 a long notice still overlaps 229x15 px of "Ersten Kontakt hinzufügen" (centre clear); three cover all of "Add First Contact" (184x44) and, with the dialog open, Friend, Other and Phone. The open dialog's Phone fields and the language bar are covered as before. See 5972088670. 37143611911: Web, Database, Mobile and E2E 2,374 passed, 3 skipped, 0 flaky
dd22990b (new spec tests/e2e/assistant-composer-width.spec.ts: the welcome composer either side of lg) Reproduces with standard fixtures by every path (direct load, sidebar, orb), with the service worker on or off and with or without a notice. The "Ask Bubaly" box is 0px wide at 1024, 1100 and 1180 (test.fail(), a reproduced defect), 100px at 1280 and 526px below lg. Control: an opened conversation's docked composer works at 1024. 36/36 at --repeat-each=3 (9 expected failures); tsc and eslint clean. Proposed workspace.tsx fix (not made) in 5972638789. 37147363901: in progress

The flaky setup step (81ba4dc9): on 915eb158, the beforeEach upsert that gives the household its Plus plan returned an error once and passed on retry. It had thrown away the response.

  • It now reads the row back and reports only the HTTP status and error code.
  • It retries once, and only when nothing answered (status 0) or the gateway did (5xx). A database refusal is thrown at once.
  • Local controls, reverted before commit: a dropped first request retries and passes; a missing household is thrown once as (409 23503); a wrong plan read back is thrown once.

The flaky paperwork focus case (52b30a8d): on bd4b711a, the composer was still open 5 s after a real save. Request logs from a local reproduction showed the cause.

  • The save wasn't sent until the dashboard sidebar's mount read (loadSidebarPrefs) answered. Under load that read took 2–4 s, and Next runs one server action at a time.
  • openComposer now waits, once the composer has opened, for the page's in-flight server actions to settle.
  • The refusal stub and action counter now match only the composer's save, by its _1_text field.
  • No product code changed and no timeout raised. A/B at 3 workers on the focus and plain-save cases, 15 repeats: 1 of 60 failed before, 0 of 60 after.

Other checks:

  • On 915eb158: Full Vitest 1,823 files, 23,928 passed, plus 2 expected failures, including the pin a-thrown-action-does-not-leave-a-button-spinning.
  • On bd4b711a:
    • tsc --noEmit, eslint and git diff --check are clean.
    • Full Vitest: 24,137 passed, plus 2 expected failures. This was after rewording one comment that the aria-modal ratchet in consent-preference-centre-focus.test.ts reads.
  • On 52b30a8d: tsc --noEmit, eslint and git diff --check are clean (spec-only change).

Out of scope

  • No edits to shared Field/Input/ActionError/Button, or to the sidebar. Toast: only its two desktop placement class lists changed, with the owner's clearance (5968640347).
  • No financial, shared-auth, locale or migration work.
  • No finalaudit.md edits; NWV keeps ledger ownership.
  • No change to mobile/ or the CI audit step for the node-forge advisory; that is the owner's call.

#775 has merged; #774, #776 and #777 are tracked on their own PRs. This PR stays a draft.

🤖 Generated with Claude Code

https://claude.ai/code/session_01TRY21ZKsFrfB3qtoP972A4


Generated by Claude Code

claude added 2 commits October 1, 2026 19:06
…(A11Y-001)

Test and evidence only (JIMMY-BLK-FORM-A11Y-20261001); no source change.
Real /dashboard/contacts and /dashboard/paperwork pages, a signed-in Plus
parent of a disposable local household, synthetic records, 390 and 1280 px.

Controls that pass on main: the contact form's validation alert is said
once and stays in Chromium's accessibility tree inside the aria-modal
dialog, focus stays on the field, a malformed email is the browser's own
constraint validation, a database refusal is one alert in the reader's
words with the dialog and values kept, and a save announces, closes,
returns focus to the opener and reads back. An empty paperwork paste is
the browser's required-field refusal; a real paste is saved.

Reproduced defects, marked test.fail() so they run every time and flip
when fixed:
- contacts: a refused save drops focus to <body>, outside the dialog
  (the shared Button is natively disabled while loading);
- contacts, 1280x800: the stacked error toasts cover the dialog's own
  submit button (the toast-obstruction class recorded 2026-09-27);
- paperwork: a refused paste shows React's redacted #441 text, not the
  not-saved message useActionError already gives;
- paperwork: focus drops to <body> after a refused and after a saved paste;
- paperwork: a whitespace-only paste closes as done with nothing saved.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TRY21ZKsFrfB3qtoP972A4
social-publish-consumers.spec.ts:269 filled the schedule field with
'2026-10-01T12:00'. From 12:00 UTC on 2026-10-01 that is a past time, which
the studio rightly refuses, so the test fails on every branch, main
included (01b2c38 passed at 10:35 UTC; the same commit fails after 12:00,
1 failed / 11 passed locally, pinned Chromium 149).

The time is now computed in the page as a week ahead, in the browser's own
local time, which is what a datetime-local field takes. No assertion
changed. Pinned Chromium, retries 0: 36/36 (x3).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TRY21ZKsFrfB3qtoP972A4
(cherry picked from commit 046bc33)
@vercel

vercel Bot commented Oct 1, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated
bubaly Ignored Ignored Oct 3, 2026 7:17pm UTC

Request Review

Copy link
Copy Markdown
Owner Author

Intersection check against the held I18N-011 work (review 5384023815 on #777, receipt on #705). Probes and spec ran on actual main 01b2c380; none of the held commits were used or imported. Nothing below has been edited. Here is where each proposed patch meets the held error and toast call sites:

# Proposed patch Touches a held I18N-011 call site?
1 Shared Button: aria-disabled while loading No. It is markup and focus in components/ui/button.tsx; no message or translation is involved.
2 Shared toast stack position No. It is layout in components/ui/toast.tsx, with no message text. The contacts describeDbError(...) toast calls stay as they are.
3 Paperwork composer catch → refusalForThrown Possibly adjacent. It changes which text the composer's catch passes to toastError (paperwork-module.tsx), not the addPaperworkAction / describeActionError calls. If the held work also rewrites that catch, the two meet there.
4 Paperwork focus (aria-disabled pending submit; focus the toggle on close) No. Markup and focus only.
5 Whitespace-only paste Option (a), client: a trimmed-empty check in the composer; no intersection. Option (b), server: ok:false from addPaperworkAction would intersect with the held paperwork action calls. (a) is preferred for that reason.

I won't edit any of these without an assignment. Where an assignment covers #3 or #5(b), I'll check the held commits' exact hunks against the patch first.


Generated by Claude Code

…nk paste (A11Y-001)

Two of the five defects #778 reproduced, fixed in the composer only
(components/modules/paperwork-module.tsx):

- A refused paste showed a thrown action's message as-is, which a
  production build replaces with React's redacted "Minified React error
  #441 ..." text. The catch now reads it with refusalForThrown and shows
  the actionRefusal.* message, as useActionError already does. A real
  thrown refusal (a temporary local check constraint) now reads "Some of
  what was entered can't be accepted. Check the form and try again."
- A paste of only spaces passed native `required`, the action trimmed it
  to nothing and answered ok without saving, and the composer closed with
  nothing said. The form now clears a blank paste and reports it the way
  the browser reports an empty one (valueMissing, focus on the field), so
  no action request is sent. The action and its error calls are unchanged.

Both regressions in tests/e2e/form-error-semantics.spec.ts drop their
test.fail() marker and assert the fixed behaviour. The other three
(shared Button focus, shared toast obstruction, composer focus) stay
marked and unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TRY21ZKsFrfB3qtoP972A4
@NewWorldVenture NewWorldVenture changed the title E2E: what the contact and paperwork forms expose when a submit fails (A11Y-001, test-only) Contacts and paperwork forms: what a failed submit exposes (A11Y-001); paperwork refusal text and blank paste fixed Oct 1, 2026

Copy link
Copy Markdown
Owner Author

Fixes #3 and #5(a) are in, as authorized: commit b5d75c80, components/modules/paperwork-module.tsx only.

Tests: the two spec cases dropped test.fail() and now assert the fixed behavior. The other three (#1 shared Button, #2 shared toast, #4 composer focus) stay marked and unchanged.

Results:

  • 13/13 as expected, three runs in a row, on a fresh production build of this exact source.
  • tsc and eslint are clean.
  • Full Vitest: 23,928 passed, plus 2 expected failures.

Receipts and limits:

  • The prior test-only head e0062338 passed all four jobs in 36911902381 before this push.
  • Held I18N-011: the action and describeActionError calls are untouched. The held commits aren't visible to me, so if they also rewrite the composer's catch, the two meet there.

Exact-head CI on b5d75c80 is pending.


Generated by Claude Code

…ste (A11Y-001)

Fix #4 from #778, in components/modules/paperwork-module.tsx only.

- The pending submit was natively `disabled`, so the focus that pressed it
  fell to <body> while the action ran and stayed there after a refusal.
  It is now aria-disabled while pending (styled with aria-disabled:), and
  a ref refuses a second submit while one is in flight.
- A save closed the composer around the focused submit, dropping focus to
  <body>. Focus now moves to the "Add paperwork" toggle that opened it.

The #4 case in tests/e2e/form-error-semantics.spec.ts drops test.fail()
and asserts focus on the submit after a refusal and on the toggle after a
save. #1 (shared Button) and #2 (shared toast) stay marked.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TRY21ZKsFrfB3qtoP972A4
@NewWorldVenture NewWorldVenture changed the title Contacts and paperwork forms: what a failed submit exposes (A11Y-001); paperwork refusal text and blank paste fixed Contacts and paperwork forms: what a failed submit exposes (A11Y-001); paperwork refusal text, blank paste and focus fixed Oct 1, 2026

Copy link
Copy Markdown
Owner Author

Fix #4 is in, as authorized: commit 361a9717, components/modules/paperwork-module.tsx only.

  • The pending submit is aria-disabled, not disabled. Focus stays on it while the action runs and after a refusal. A ref refuses a second submit while one is in flight.
  • A save hands focus to the Add paperwork toggle, instead of letting it fall to <body> when the composer closes.

Tests: the #4 spec case dropped test.fail(). It now asserts focus on the submit after a refusal and on the toggle after a save. #1 (shared Button) and #2 (shared toast) stay marked.

Results:

  • 13/13 as expected, three runs in a row, on a fresh production build of this exact source.
  • tsc and eslint are clean.
  • Full Vitest: 23,928 passed, plus 2 expected failures.

Receipts: the previous head b5d75c80 (fixes #3 and #5a) passed all four jobs in 36916524997 before this push. Exact-head CI on 361a9717 is pending.

Held I18N-011 work: #4 touches no action or error call.


Generated by Claude Code

Copy link
Copy Markdown
Owner Author

CI on the current head is green. Head 361a97173abfd2b123876f201044f4913b75c143 (fixes #3, #4 and #5a), run 36920596558:

Earlier heads, recorded separately, all four jobs green:

Still open:

This PR stays a draft. #774–#777 are untouched.


Generated by Claude Code

@NewWorldVenture

Copy link
Copy Markdown
Owner Author

[P2] Restore focus only while the saving composer still owns it. PR778 frozen 361a97173abfd2b123876f201044f4913b75c143,components/modules/paperwork-module.tsx:171,blob 4461f09888f8425a19f619d9b8da22f05a9d5438.

The new onDone={() => { setComposerOpen(false); composerToggle.current?.focus(); }} always focuses the opener after a response. Reproduction: submit a valid synthetic paste while the action is held; explicitly close the composer using its toggle; focus/type in another field; resolve the original save with {ok:true}. The late callback pulls focus back to “Add paperwork,” interrupting the current keyboard task. The exact prior callback preserves the other field's focus under the identical schedule. Tie restoration to the originating composer/focus ownership while retaining ordinary successful-save opener focus.

Receipt 90809a,clean exit0,2026-10-01 22:18:42.291–22:18:43.032Z: actual AST-extracted Composer and exact parent callbacks mounted in production React19.3/Chromium149. Seven characterized cases,26 assertions: five passing controls,two causal observations, not seven desired PASS. Controls cover three whitespace forms/native required refusal, returned refusal and safe thrown-error wording, pending duplicate-submit prevention, and normal save focus. Zero page errors/network. Actual refusal helper 8b5c5f57b4326f508ae63e9ef72cf690a8ab504b executes.

The author-disclosed caught-action draft reset remains a separate open issue; this reproduction is the newly added unconditional focus behavior. Scope: minimal parent wrapper with the same toggle behavior; actions/translations/toasts are synthetic. No full PaperworkModule/dashboard/Next transport/family-transition/SQL/Auth/provider proof.

Initial harness 7d72a9 stopped on an invalid test assumption: Playwright's disabled predicate includes aria-disabled. Corrected checks inspect native el.disabled and use actual DOM click, preserving pending-state/duplicate-submit assertions; no application edit. A proposed broader harness failed parsing before execution and was not used as evidence.

Source/ledger verification 0c5f8a: existing COMPONENT-E0312E0A8050/A11Y-001 remain NOT STARTED, ACTION-CD1E12585CD4 IN PROGRESS. No new IDs/status credit, files, branches, shared-ledger or configuration changes.

NewWorldVenture pushed a commit that referenced this pull request Oct 1, 2026
…s regression, #776 parser fidelity, #771 delta clearance)

Reviewer-reported synthetic evidence only; no PASS or closure credit;
PRODUCTION READY: NO retained.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KRUgA6hD6QgzmtpSP6TUmP

@NewWorldVenture NewWorldVenture left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ACCESS-BLK-20261001-2227: The owner has explicitly requested a fresh account-access check. Status only for Blackstone, existing Claude session session_01TRY21ZKsFrfB3qtoP972A4. Please acknowledge this exact probe ID, confirm whether it reached that existing session automatically, and state whether you are currently working, waiting, rate-limited, or blocked by a local permission prompt. Include your current branch/head and next blocker. Do not edit files, start a new coding task, change permissions, log in/out, merge, deploy or call production providers for this check. Reply only from the named session; if your local policy requires owner approval to reply, preserve that requirement and report the prompt rather than bypassing it. This probe does not authorize the pending source repairs, main merges or credential changes.

…lds it

Review 5941818072 on #778 (P2): fix #4 focused the "Add paperwork" toggle
on every successful save. A save answered after the reader had closed
the composer and moved to another control pulled focus back to the
toggle, interrupting them.

The composer now reports whether it still owns the focus when its save
lands: it is still mounted, and focus is inside it or has fallen to
<body> with its submit. Only then does the module focus the toggle; an
ordinary save still lands on the toggle as before.

New regression (390 and 1280 px): hold the save, close the composer
with its toggle, focus the Archived filter, release the save; focus
stays on the filter. It fails on 361a971 at both widths and passes now.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TRY21ZKsFrfB3qtoP972A4

Copy link
Copy Markdown
Owner Author

Re: [P2] focus restoration (comment 5941818072): fixed in 65b6cf24, components/modules/paperwork-module.tsx only.

  • Ownership check: when its save lands, the composer now reports whether it still holds the focus: it is still mounted, and focus is inside it or has fallen to <body> with its submit.
  • Restoration: the module focuses Add paperwork only in that case, so an ordinary save still lands on the toggle as before.
  • Unchanged: the actions, translations and toasts. The thrown-message pin a-thrown-action-does-not-leave-a-button-spinning still passes.

Regression added to form-error-semantics.spec.ts, at 390 and 1280 px. It follows your schedule on the real page and the real server action:

  1. Hold the save.
  2. Close the composer with its toggle.
  3. Focus the Archived filter.
  4. Release the save.
  5. Expect focus to stay on Archived.

Results:

Pre-existing and unchanged: if the reader closes the composer and reopens it before a late save lands, that save still closes the reopened composer, because setComposerOpen(false) predates fix #4. Your finding covers the focus, not this, so I left it. I can tie the close to the originating composer the same way if you want.

The caught-action draft reset remains separately open, as you noted. Exact-head CI on 65b6cf24 is pending.


Generated by Claude Code

Copy link
Copy Markdown
Owner Author

ACCESS-BLK-20261001-2227: acknowledged from the existing session session_01TRY21ZKsFrfB3qtoP972A4.

No files edited, no permissions or login changes, no merges or deploys, and no production or provider calls for this check.


Generated by Claude Code

Copy link
Copy Markdown
Owner Author

CI on the current head is green. Head 65b6cf24307a86085523a76afb12243655d39d0a (P2 focus-ownership fix), run 36936324068:

  • Web: lint, unit tests in UTC, DST-observing and east-of-Greenwich zones, build and typecheck all pass (job 110617327506).
  • Database: passes (job 110617327533).
  • Mobile: passes (job 110617327389).
  • E2E: 1,924 passed, 0 failed, 0 flaky, 0 skipped (job 110617327650). That is main's 1,909 plus this spec's 15, including the new late-save focus regression at both widths.

Earlier heads, recorded separately, all four jobs green:

This PR stays a draft.


Generated by Claude Code

… own composer

Two remaining composer defects on #778, in components/modules/paperwork-
module.tsx only.

- A paste the server refused was wiped. The composer submitted through
  <form action>, and React resets a form's fields when its action
  settles, refused or not. It now submits from onSubmit (FormData plus
  the same transition), so nothing resets the fields; on success the
  composer closes as before. Reproduced without a stub: a paste over the
  1 MB server-action body limit is refused by the framework, and the
  1.2 MB letter and its sender were both cleared.
- A save from a composer that had been closed (and reopened) closed the
  new one. A save now closes only the composer that sent it.

Two regressions at 390 and 1280 px: an oversize paste keeps its text and
sender with the not-saved alert; a held save released after close and
reopen leaves the reopened composer and its draft. Both fail on 65b6cf2
and pass now.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TRY21ZKsFrfB3qtoP972A4

Copy link
Copy Markdown
Owner Author

Composer reset fixed. The open "caught-action draft reset" is fixed in 915eb158, along with the pre-existing late save closes a reopened composer. Both changes are in components/modules/paperwork-module.tsx only.

1. A refused paste is kept.

  • Cause: the composer submitted through <form action>, and React resets a form's fields when its action settles, refused or not.
  • Fix: it now submits from onSubmit, with FormData and the same transition, so nothing resets the fields. On success the composer closes as before.
  • Faithful CI reproduction, no stub: a paste over Next's 1 MB server-action body limit is refused by the framework with a 500. On 65b6cf24 the 1.2 MB letter and its sender were both cleared, behind the correct "Nothing was changed…" alert from fix Build out admin console: Support Tickets, Audit Logs, and account controls #3.

2. A late save closes only its own composer. A save from a composer that was closed and then reopened used to close the new one. That code predates fix #4. Saves now go through saved(), which acts only if the sending form is still mounted.

Regressions added to form-error-semantics.spec.ts, at 390 and 1280 px:

  • An oversize paste keeps its full text and sender, with one not-saved alert.
  • A held save released after close and reopen leaves the reopened composer and its draft.

Both fail on 65b6cf24 at both widths (text length 0; composer closed) and pass now.

Results:

Ownership:

Exact-head CI is pending.


Generated by Claude Code

@NewWorldVenture NewWorldVenture changed the title Contacts and paperwork forms: what a failed submit exposes (A11Y-001); paperwork refusal text, blank paste and focus fixed Contacts and paperwork forms: what a failed submit exposes (A11Y-001); paperwork composer refusal, blank paste, focus and draft fixed Oct 1, 2026
@NewWorldVenture

Copy link
Copy Markdown
Owner Author

Independent repair retest — paperwork draft, reopen and focus

At current draft head 915eb158dc072f1f97f009f1b5ebd1ec0b4fdb45, PaperworkModule blob 390f1573acd314125f268824aec7e53ef1a04679, the late-success focus finding and previously open caught-refusal draft/reopen behavior are addressed in bounded browser tests.

62e352, clean exit0,2026-10-02 00:14:20.879–00:14:29.391Z: full actual PaperworkModule and Composer mounted with React19.3/native Chromium149.0.7827.55 at390px/1280px. 19 characterized scenarios/80 assertions:16 current desired scenarios/74 assertions,three causal old-source observations/four assertions, plus zero-page-error/zero-network assertions. Current cases retain draft and sender after a synthetic1.248MB thrown refusal with safe named/numeric digest messages; returned refusal/retry shows one safe alert; native Enter, three whitespace variants and duplicate DOM submit/click are covered; normal successful save closes/focuses opener; success after focus moves within/away from the module preserves that focus; explicit dismissal followed by reopening preserves the new draft/sender/focus, and a subsequent save closes correctly.

Exact earlier sources reproduce the issues: 361a971… blob 4461f098… unconditionally moves unrelated focus; 65b6cf24… blob cdb4e… clears draft on caught refusal and closes a reopened composer. Original assertions are retained in the new915 repair. Actual refusal helper 8b5c5f57… executes. End guard ba09fc,00:15:27.469Z, verified unchanged head/main01b2/ledger e9b908.

Limits: items=[], synthesized action/toast/report/router/translations/capture/icon imports; no CSS, Next server-action transport, live framework1MB refusal, Auth, SQL/RLS, provider, refresh persistence or screen-reader verification. No source/ledger edits. Shared Button/toast integration remains separately held. Existing COMPONENT-E0312E0A8050, ACTION-CD1E12585CD4 and A11Y-001 are not broadly closed by this retest.

Exact-head CI36942913853 was live at the end guard: Web/DB/Mobile passed, E2E still running. This is not an all-green or complete-paperwork workflow claim.

NewWorldVenture pushed a commit that referenced this pull request Oct 2, 2026
…778 retests

Reviewer- and author-reported synthetic evidence; #781 CI live with a
mobile dependency-audit failure; no PASS or row closure; PRODUCTION
READY: NO retained.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KRUgA6hD6QgzmtpSP6TUmP
…ing answered

CI run 36942913853 on 915eb15 passed with one flaky case: the beforeEach
upsert that gives the disposable household a Plus plan returned an error once
(00:03:35Z, 390 px contacts) and passed on retry. The step threw away the
response, so the log cannot say what it hit. No other spec writes
subscriptions or reloads the schema, and the database side is an ordinary
update (provision already made the row; no subscriptions trigger refuses a
service-role write).

The step now reads the row back and reports the HTTP status and error code
(never the message or details). It retries once, and only when nothing
answered (status 0) or the gateway did (5xx): the upsert is idempotent, and
postgrest-js retries a GET on those but never a POST. A database refusal is
thrown at once.

Local controls, reverted before commit: a dropped first request retries and
passes ([0]); a missing household is thrown once as (409 23503); a wrong plan
read back is thrown once. Spec 19/19, then 38/38 at --repeat-each=2, 2 workers.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TRY21ZKsFrfB3qtoP972A4

Copy link
Copy Markdown
Owner Author

Exact-head CI: 915eb158 — green, with one flaky setup step (mine)

CI 36942913853, head 915eb158dc072f1f97f009f1b5ebd1ec0b4fdb45: Web, Database, Mobile and E2E all succeeded. E2E: 1927 passed, 1 flaky (1909 + 19 = 1928 run).

The flaky case is in this PR's own spec, not the product: form-error-semantics.spec.ts › 390 px › contacts failed once at 00:03:35Z in beforeEach, on the service-role upsert that gives the disposable household a Plus plan, and passed on retry. The step threw away the response, so the log can't say what it hit. No other spec writes subscriptions or reloads the schema. The database side is an ordinary update of the row provisioning already made, with no trigger that refuses a service-role write.

Next commit, 81ba4dc9 (spec only, no source change):

  • The step reads the row back and reports the HTTP status and error code. It never reports the message or details.
  • It retries once, and only when nothing answered (status 0) or the gateway did (5xx). The upsert is idempotent, and postgrest-js retries a GET on those but never a POST.
  • A database refusal is thrown at once.

Local controls, reverted before commit:

Control Result
Dropped first request retries, passes ([0])
Missing household thrown once, (409 23503)
Wrong plan read back thrown once

Spec 19/19, then 38/38 at --repeat-each=2 with 2 workers.

If it recurs, the error will now name the status, so it can be fixed for real instead of retried.


Generated by Claude Code

Copy link
Copy Markdown
Owner Author

81ba4dc9: Mobile (Expo) red, from a new advisory, not this PR

Failing: Mobile (Expo) · Typecheck · Config → step npm audit --omit=dev --audit-level=high (job 110649565870), exit 1.

Cause: a newly published high advisory on node-forge, GHSA-86w9-cpqp-85rv (RSA PKCS#1 v1.5 signature verification accepts extra nested DigestAlgorithm elements). It is reached through expo → @expo/cli / @expo/code-signing-certificates → node-forge. The same Mobile job passed on 915eb158 at 23:50Z; the audit result changed between the two runs, not the code.

Why it is not this PR's:

  • 81ba4dc9 changes only tests/e2e/form-error-semantics.spec.ts, and the PR has no diff under mobile/.
  • main's own mobile/package-lock.json at 01b2c380 fails identically when audited locally: exit 1, the same node-forge * high, 16 vulnerabilities (12 moderate, 4 high). Any branch whose Mobile job runs now will be red the same way.

No fix exists to port:

  • The advisory range is *.
  • The locked node-forge is 1.4.0, which is the latest published release.
  • npm audit fix --force proposes expo@44.0.6, a downgrade, not a fix.

What's left is a policy call on mobile/ for the repo owner: a reviewed audit exception for this advisory until a patched node-forge or Expo release lands, or an Expo dependency change. It is outside this slice, so I'm not changing mobile/ or the CI audit step here.

The other jobs on this head are still running. I'll re-run the failed Mobile job once when the run completes and report the result here.


Generated by Claude Code

NewWorldVenture pushed a commit that referenced this pull request Oct 2, 2026
…ions (#712 PG 16.15, #778 head)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KRUgA6hD6QgzmtpSP6TUmP

Copy link
Copy Markdown
Owner Author

Exact-head CI: 81ba4dc9

CI 36946449483, head 81ba4dc9a5e797ebd8773dc13f14436f119c5268:

Job Result
Typecheck · Lint · Test · Build passed
Database (migration replay · RLS probes) passed
E2E passed: 1928 passed, 0 flaky, 0 failed (24.9 min)
Mobile (Expo) failed at npm audit --omit=dev --audit-level=high; the one re-run (attempt 2, job 110661373157) failed at the same step

The Mobile failure is the node-forge high advisory explained in 5943332101. It reproduces on main's own mobile/ lockfile, and there is no patched release to port. My re-run for it is now spent. I'm leaving mobile/ and the CI audit step alone. The Mobile check will stay red until the owner decides on that advisory or a patched release lands. I'm keeping this PR watched until then.


Generated by Claude Code

Copy link
Copy Markdown
Owner Author

Blackstone: exact03eecdec now passes all four hosted CI jobs. Next bounded product task: resolve the remaining390x844 mobile toast interception of Quick capture/Ask AI, using the same three owned toast/source-test files after checking claims. Preserve the accepted desktop lg:bottom-40 behavior, localization, toast semantics/timers, Dismiss/Undo and keyboard access. Measure the smallest mobile placement/layout adjustment that clears real short/long/stacked pointer targets without creating new footer or form-control obstruction; don't merely change expected blocked results. Require baseline-red/fix-green and a matched negative control, retain all desktop/dialog tests, and disclose any remaining content occlusion. If no small placement adjustment avoids the trade-off, return measured options and proposed scope rather than a broad redesign. Publish a tested bounded draft/update and additive evidence, no merge/deploy or session/cache/security/exception changes. Acknowledge and continue without waiting for keep-going.

Copy link
Copy Markdown
Owner Author

Acknowledged 5970654661. Starting the 390x844 mobile slice, limited to the same three files: components/ui/toast.tsx, tests/mobile-toast-safe-area.test.ts and tests/e2e/form-error-semantics.spec.ts.

  1. Check the current claims at 03eecde. Measure what the short, long (de) and stacked notices cover, and which real pointer targets they hit, at 390x844.
  2. Measure candidate mobile-only placements, below lg only. For each, record Quick capture, Ask AI, the tab bar/footer, the language bar, and the contact dialog's own fields and footer.
  3. Apply the smallest option that clears the real targets without new obstruction. Prove it with baseline-red then fix-green, plus a matched negative control. All desktop, dialog, Dismiss/Undo and keyboard tests stay.
  4. If no small adjustment avoids the trade-off, I'll post the measured options and a proposed scope here instead of a broad redesign.

lg:bottom-40, localization, toast semantics and timers stay unchanged. No session, cache, security or exception changes, and no merge or deploy. Results will follow on this PR.


Generated by Claude Code

At 390x844 the full-width mobile stack sat level with Quick capture
(bottom 5rem) and, when taller, the AI orb (bottom 9rem). Both are fixed
in a 3.5rem column at right 1rem. A short notice took the tap meant for
Quick capture; a long German notice or three took the orb's as well.
The stack now keeps 5rem clear on the right below lg: px-4 becomes
pl-4 pr-[calc(5rem+var(--safe-right))]. That is the column plus a
0.5rem gap, safe-area-aware like the buttons. lg:px-0 drops the inset
from lg, so lg:bottom-40 and the desktop placement are unchanged. So
are the bottom offset above the tab bar, the toast width cap,
localization, roles/live regions, timers, hover/focus pause,
Dismiss/Undo and the keyboard path. Requested on #778 (5970654661).

Options measured at 390x844 with a runtime style override on the stack
(measurement only; real notices; dialog open and closed):
- full width (as was): Quick capture covered and intercepted (short);
  plus the orb and the language bar (long, stacked); dialog Phone /
  Alternate phone.
- right inset 5rem (chosen): both corner buttons clear in every state.
  Covered set is a subset of before in every state: the language bar
  (long, stacked) and dialog Phone / Alternate phone, as before.
- lifted above the orb (bottom 13rem): corner clear, but newly covers the
  empty-state "Add First Contact" and up to 8 relationship chips in the
  open dialog.
- top, below the header: corner clear, but covers search, "Add Contact",
  the filter and, with the dialog open, its Close button and Full name.

Disclosed trade-off at 360x780: the narrower toast (264px) wraps the long
German notice to more lines, which newly reaches "Ersten Kontakt
hinzufügen" and, in the open dialog, the Freund / Sonstiges chips. 3
stacked notices covered those at full width already. At 768x1024 nothing
changes, since the 384px cap is narrower than the room left.

Tests:
- 390 CORNER rows now read clear. BEFORE keeps the replaced placements'
  rows for every size (from 736e1ff).
- A negative control per size and state puts the replaced placement
  back on the stack, as an inline style in that page only. It must read
  as blocked: Quick capture intercepted, the BEFORE row matching. It
  must also no longer match the clear row.
- The 390 placement test expects the right edge at 390 - 80.
- The safe-area unit test asserts pl-4, pr-[calc(5rem+var(--safe-right))],
  no px-4 and lg:px-0 on the stack.

Baseline-red, on the 03eecde build (old classes), with these tests:
- The corner describe and 390 placement case: 7 failed, 18 passed. The
  failures were the 390 corner rows x3 (covered set), the 390 placement
  (right edge) and the 390 controls x3 (clear row precondition).
- The new unit case failed against the old toast.tsx.
Fix-green, after the rebuild: 25/25, and the unit test 3/3.

Validation (local production build, local Supabase, --retries=0):
- form-error-semantics.spec.ts full, twice at --workers=2: 50/50, 50/50.
- Corner describe (12 cases + 12 controls), --repeat-each=5
  --workers=4: 124/125, then 125/125. The miss was 1440x900 long's
  keyboard path: Enter on the orb didn't reach /dashboard/assistant
  within the 30s test timeout under 4 workers. That is desktop and
  unaffected by this change.
- weekly-meal-planner + quick-capture-task: 83/83.
- 64 unit files that read toast.tsx: 1,331 passed.
- tsc --noEmit, eslint and git diff --check: clean.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TRY21ZKsFrfB3qtoP972A4

Copy link
Copy Markdown
Owner Author

Re 5970654661: the 390x844 corner interception is cleared by one mobile-only class change. It's pushed as f3988ccbe7eaf302ee4dd831034225ed2c3524f6 on 03eecdec and touches the same three files only.

For the record, 03eecdec passed all four hosted jobs (37131441755): E2E 2,353 passed, 3 skipped. main is at 40bf5ff3 and isn't merged in.

Claims at 03eecdec, measured at 390x844 on /dashboard/contacts with real notices. Each cell lists the controls a toast box covers; every one of them was also intercepted by elementFromPoint at its centre.

State Dialog closed Contact dialog open (bottom sheet)
short Quick capture Quick capture, Alternate phone
long (de) Schnellerfassung, KI-Assistent, Sprache ändern same + Telefon, Alternative Telefonnummer
stacked (3) Quick capture, Ask the AI assistant, Change language same + Phone, Alternate phone

None of the states covered the tab bar or the dialog's footer (Cancel / Add Contact).

Mobile-only options, measured with a runtime style override on the stack element only; nothing of the override was committed.

Option (below lg) Corner buttons Language bar Dialog fields Other new coverage
full width, px-4 (as was) covered (short: QC; long/stacked: QC + orb) long, stacked Phone / Alt phone none
right inset 5rem, pl-4 pr-[calc(5rem+var(--safe-right))] (chosen) clear in all states long, stacked (as before) Phone / Alt phone (as before) none: a strict subset of before in every state
lifted above the orb, bottom 13rem clear clear new: up to 8 relationship chips (Dentist … Other) new: "Add First Contact" (long, stacked)
top, below the header clear clear new: Close dialog, Full name new: Search, "Add Contact", "All (0)" filter

Change

  • components/ui/toast.tsx: on the stack, px-4 → pl-4 pr-[calc(5rem+var(--safe-right))]. That is the corner column (3.5rem at right 1rem) plus a 0.5rem gap, safe-area-aware like the buttons. The comment says so, along with the trade-off below.
  • From lg, lg:px-0 drops the inset, so lg:bottom-40 and all desktop behaviour are unchanged.
  • Also unchanged: the bottom offset above the tab bar, the toast width cap, localization, role/aria-live, timers, hover/focus pause, Dismiss/Undo and keyboard access.
  • At 390 a toast is now 294px wide (16 → 310) instead of 358.

Tests (form-error-semantics.spec.ts, mobile-toast-safe-area.test.ts)

  • 390 CORNER rows, as measured after the change:

    • short: nothing covered, QC / AI under their own centres.
    • long: Sprache ändern covered, both buttons under their own centres.
    • stacked: Change language covered, both buttons under their own centres.
  • BEFORE keeps the replaced placements' rows for every size: from lg bottom-6, below lg full width (736e1ffd's measurements).

  • Negative control, one per size and state (12). It confirms the clear row, then puts the replaced placement back on the stack as an inline style in that page only (bottom: 1.5rem from lg, padding-right: 1rem below lg). It then requires:

    • Quick capture covered and its tap intercepted by a notice;
    • the BEFORE row matching;
    • the clear row not matching.

    This replaces the earlier 390 "moved clear" control, since no blocked rows remain.

  • 390 placement test: the right edge is at 390 − 80, and the stack bottom is still 844 − 80.

  • Unit: the stack has pl-4 and pr-[calc(5rem+var(--safe-right))], no px-4, and keeps lg:px-0. The M-033 and lg:bottom-40 assertions are kept.

  • Desktop rows, dialog-footer cases at 1024/1280/1440, the German notice, the quick-capture Undo + hover/focus pause case, the 1024 zero-width composer assertion and all keyboard paths are unchanged.

Baseline-red → fix-green

  • On the old-class build (03eecdec source), the new corner describe plus the 390 placement case gave 7 failed, 18 passed. The failures:
    • the 390 corner rows ×3, at the covered set;
    • the 390 placement, at the right edge;
    • the 390 controls ×3, at the clear-row precondition.
  • The new unit case failed against the old toast.tsx.
  • After the rebuild: 25/25, and the unit test 3/3.

Validation (local production build of f3988ccb, local Supabase, --retries=0)

  • form-error-semantics.spec.ts full, twice at --workers=2: 50/50, 50/50.
  • Corner describe (12 cases + 12 controls), --repeat-each=5 --workers=4: 124/125, then 125/125.
    • The one miss was 1440x900, long, on its desktop keyboard path. Enter on the orb didn't reach /dashboard/assistant within the 30s test timeout under 4 workers.
    • That's a server navigation under load, after the report had already passed. Desktop is unaffected by this change, and no timeout was changed.
  • weekly-meal-planner + quick-capture-task: 83/83.
  • 64 unit files that read toast.tsx: 1,331 passed.
  • tsc --noEmit, eslint and git diff --check: clean.

Remaining occlusion, disclosed

  • 390x844:
    • The language bar at the foot of this short page is still under a long notice or three. It's page content, as before.
    • With the contact dialog open, Phone / Alternate phone are still under the notices, as before.
    • The tab bar and the dialog footer are not covered.
  • 360x780 (trade-off): the narrower toast (264px) wraps the long German notice to more lines, so it now also reaches content it didn't before:
    • dialog closed: "Ersten Kontakt hinzufügen";
    • dialog open: the Freund / Sonstiges chips.
    • Three stacked notices covered those already at full width.
    • Both corner buttons are clear at 360 in every state.
  • 768x1024: unchanged. The 384px cap is narrower than the room left. Dialog Notes / birthday fields are under the notices with the dialog open, as before.
  • From lg, as before: the raised stack may sit over the right edge of page content, and the 1024 zero-width assistant composer is the assistant layout's issue, not the toast's.
  • docs: verify audit closure counts and current release blockers #869 ledger evidence is still held pending the owner's in-session confirmation.

CI for f3988ccb: 37138934479, in progress. It will be recorded in the Runs table. Not merged, not marked ready, not deployed.


Generated by Claude Code

Copy link
Copy Markdown
Owner Author

Blackstone: mobile completion receipt5971353434 received. While current CI runs, next bounded investigation is the newly disclosed360x780 regression; don't call mobile obstruction closed. Use your existing owned E2E spec to measure Add First Contact, dialog chips and field/language controls with actual short/long/stacked notices at360 and390, retaining keyboard and Dismiss/Undo checks. Before another position-only patch, compare a minimal placement option with a compact/single-visible-notice option and report whether either preserves all tested controls and access to notice actions without changing timers or losing messages. Do not implement a broader notification redesign yet. Preserve accepted desktop behavior and publish test/evidence only if appropriate; report one recommended minimal source scope and trade-offs for coordination. No session/cache/security/config changes, merge or deployment. This is a separate residual investigation, not a request to interrupt or restart CI.

Copy link
Copy Markdown
Owner Author

Acknowledged 5971395586. Mobile obstruction stays open. The 360x780 long-German regression disclosed in 5971353434 is now a separate residual investigation, and CI on f3988cc is left alone.

This round is measurement only, in the owned E2E spec. It runs real short, long (de) and stacked notices at 360x780 and 390x844, with the dialog closed and open. It measures what the notices cover (Add First Contact, the dialog's relationship chips, Phone and Alternate phone, the language control, Quick capture and Ask AI) and what a tap actually hits. Keyboard and Dismiss/Undo checks stay as they are.

Two options will be compared side by side:

  • a minimal placement option;
  • a compact option that shows one notice at a time.

For each I'll report:

  • which tested controls stay clear;
  • whether every notice's Dismiss/Undo stays reachable;
  • whether any message is lost.

Timers stay unchanged, and there is no broader notification redesign.

I'll recommend one minimal source scope with its trade-offs, for coordination. I'll publish test or evidence additions only if they belong in the spec. There is no source change in this round, no session, cache, security or config change, and no merge or deploy.


Generated by Claude Code

Spec only; no source, CSS or timer change. On #778 (5971395586) the
owner asked for the 360x780 regression disclosed with f3988cc to be
measured and kept open, not closed. The corner describe below lg now
also runs at 360x780: short, long (de) and stacked, each with its
replaced-placement negative control.

As measured on f3988cc:
- Both corner buttons are clear in every state at 360.
- The 264px-wide stack lets the long German notice wrap to 7 lines
  (166px; 5 lines, 126px, at full width). It reaches the empty page's
  "Ersten Kontakt hinzufügen" as well as the language bar.
- Three notices reach "Add First Contact", at full width too.

The rows are labelled RESIDUAL, not accepted. BEFORE gains the
full-width 360 rows, which the controls reproduce in the page:
- short: Quick capture intercepted;
- long and stacked: both corner buttons intercepted.

Validation (local production build of f3988cc, --retries=0):
- 360x780 cases: 6/6.
- Corner describe (15 cases + 15 controls) at --repeat-each=3
  --workers=4: 93/93.
- form-error-semantics.spec.ts full: 56/56.
- tsc, eslint and git diff --check: clean.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TRY21ZKsFrfB3qtoP972A4

Copy link
Copy Markdown
Owner Author

Re 5971395586: I measured the 360x780 residual. No source, CSS or timer change in this round. Mobile obstruction isn't closed.

CI for f3988ccb (37138934479):

  • Typecheck, Database, Mobile and E2E passed.
  • E2E: 2,361 passed, 3 skipped, 1 flaky: at 390 px › paperwork: a save from a closed composer does not close the one opened since. Its waitForResponse for the paperwork server action hit the 30s test timeout, then passed on retry. That's the paperwork composer, not the toast placement.

That run wasn't interrupted. The evidence commit below was pushed after it finished.

Method

  • Real notices on /dashboard/contacts: short Name is required; long (de) errors.thatChangeWasNotSaved, 130 characters; stacked = three.
  • Measured at 360x780 and 390x844, contact dialog open (bottom sheet) and closed.
  • Each option is applied as a runtime style or DOM override on the live stack, for measurement only.
  • For every listed control: is it intersected by a visible toast box, and is its centre intercepted (elementFromPoint inside the stack)?
  • For every notice's own Dismiss/Undo: does a pointer at its centre land on it (P), and can it take focus (K)?
  • For every notice: is its role/aria-live text in the DOM, and is it shown?
  • Timers: no override touches them.

Results. Each cell lists the controls a visible notice covers. ° = box overlap only, with the centre still clear. * = new compared with full width.

Width Option (below lg) Long (de) height Closed: short / long / stacked Dialog open: short / long / stacked Notice actions Messages
360 full width px-4 (before f3988ccb) 126 Lang + QC / Lang + QC + AI / Add First + Lang + QC + AI Phone° + QC / Telefon + QC + AI / Friend, Other, Phone + QC + AI all P+K all shown
360 inset 5rem (shipped f3988ccb) 166 Lang / Ersten Kontakt* + Lang / Add First + Lang Phone° / Freund°*, Sonstiges*, Telefon / Friend, Other, Phone all P+K all shown
360 inset 4.75rem 146 Lang / Ersten Kontakt°* + Lang / Add First + Lang Phone° / Telefon / Friend, Other, Phone all P+K all shown
360 pl-3 + inset 4.75rem 146 same as 4.75rem same all P+K all shown
360 inset 4.5rem (0px gap to the buttons) 146 same as 4.75rem same all P+K all shown
360 inset 5rem + stack max-height: 7rem; overflow:auto 166 (clipped) Lang / Lang / Add First° + Lang Phone° / Alt. Telefon°*, Telefon / Friend°, Other°, Phone older notices' Dismiss: K only, not P all in DOM; a long one is clipped
360 (b) compact: newest at full size, older behind a "+N" pill 166 Lang / Ersten Kontakt* + Lang / Lang Phone° / Freund°*, Sonstiges*, Telefon / Phone older notices: neither P nor K (hidden) announced on arrival; older hidden
360 (b) compact + 2-line clamp 66 Lang / Lang / Lang Phone° / Telefon / Phone older: neither P nor K full text in DOM; long one visually truncated
390 full width (before) 106 QC / Lang + QC + AI / Lang + QC + AI Alt phone + QC / Telefon, Alt. Telefon + QC + AI / Phone, Alt phone + QC + AI all P+K all shown
390 inset 5rem / 4.75rem / 4.5rem 146 — / Lang / Lang Alt phone / Telefon, Alt. Telefon / Phone, Alt phone all P+K all shown
390 pl-2 + inset 4.5rem 126 — / Lang / Lang same all P+K all shown
390 (b) compact 146 — / Lang / — Alt phone / Telefon, Alt. Telefon / Phone°, Alt phone older: neither P nor K older hidden

Key: QC = Quick capture / Schnellerfassung; AI = Ask the AI assistant / KI-Assistent; Lang = the language bar at the foot of this short page; Add First / Ersten Kontakt = the empty-state "Add First Contact" button. The tab bar and the dialog footer (Cancel / Add Contact) were covered by no option.

What this shows

  • Every inset option clears both corner buttons at 360 and 390, in every state.

  • The 360 regression is only the long notice, and it's a wrap threshold. At 264px (5rem) the German text takes 7 lines. At 268px (4.75rem) it takes 6.

  • At 6 lines, the dialog's Freund / Sonstiges chips are clear again. "Ersten Kontakt hinzufügen" is only edge-overlapped, so its centre is still tappable.

  • Everything else at 360 matches full width minus the corner buttons: three notices over "Add First Contact", the language bar, and the dialog's chips and Phone.

  • (b) compact does nothing for the long single notice. The 2-line clamp does, but it hides most of the message from sighted users.

  • (b) clears the stacked case, but the older notices' Dismiss can't be reached by pointer or keyboard until something expands them.

  • (b) also needs a timer decision. Hidden notices would either expire unseen, which loses them visually, or be paused, which changes timer behaviour. Either way, (b) needs logic in toast.tsx, not a class:

    • an expanded/collapsed state and a "+N" toggle button with a name;
    • rendering only the newest notice by default;
    • deciding whether queued notices count down;
    • ensuring each notice is still announced once on arrival.

    Rough scope: ~40–60 lines in toast.tsx plus tests. Not implemented.

  • max-height with scrolling clips a single long notice and leaves older notices' Dismiss keyboard-only.

Recommended minimal source scope (for coordination; not applied)

  • One class: pr-[calc(5rem+var(--safe-right))] → pr-[calc(4.75rem+var(--safe-right))] in components/ui/toast.tsx, with its comment.
  • The same string in tests/mobile-toast-safe-area.test.ts.
  • In tests/e2e/form-error-semantics.spec.ts:
    • the 390 placement edge, 390 − 80 → 390 − 76;
    • the 360 long row, to Sprache ändern only, if the run measures it as such. Ersten Kontakt hinzufügen is edge-only, and cornerReport counts box overlap, so the row may keep it.
  • About 8–10 changed lines across the same three files. Desktop is unaffected.
  • Trade-offs:
    • The gap to the corner column shrinks from 8px to 4px.
    • The fix depends on this string's wrap at 268px. Another locale or a longer message can cross the next line threshold. It is not a general guarantee.
    • Three notices at 360 still cover "Add First Contact" and the dialog's relationship chips, as they did at full width.
  • If the owner wants that last trade-off gone too, the next scope is (b), with the timer decision above made first.

Published as standing characterization: d3b3a51c34296089d64325b2799ac53967cdc9b7, spec only.

  • The corner describe below lg now also runs at 360x780: short, long and stacked, each with its replaced-placement negative control.
  • The rows record today's coverage and are labelled RESIDUAL, not accepted:
    • short: language bar;
    • long: "Ersten Kontakt hinzufügen" + language bar;
    • stacked: "Add First Contact" + language bar;
    • both corner buttons clear in every state.
  • BEFORE gains the full-width 360 rows, and the controls reproduce them in the page:
    • short: QC intercepted;
    • long and stacked: QC and the orb intercepted.
  • The dialog-open measurements above stay as evidence only. cornerReport reads the page with no dialog open.
  • Validation (local production build of f3988ccb, --retries=0):
    • 360 cases: 6/6.
    • Corner describe (15 cases + 15 controls) at --repeat-each=3 --workers=4: 93/93.
    • Full spec: 56/56.
    • tsc, eslint and git diff --check: clean.
  • CI for d3b3a51c: 37141287524, in progress.

Residuals as before: 390/360 content under notices (language bar; dialog Phone fields, as before), the raised desktop stack over page content, the 1024 zero-width assistant composer. #869 ledger evidence is still held. Not merged, not marked ready, not deployed.


Generated by Claude Code

Copy link
Copy Markdown
Owner Author

Blackstone: measurement receipt5971751746 received. Proceed with the minimal measured mobile inset adjustment5rem→4.75rem in the same three owned source/test files, after checking current claims. Preserve desktop placement, corner target access, notice actions/timers and all existing keyboard/Undo/Dismiss behavior. Require baseline-red/fix-green on the360 dialog-chip interception and retained360/390 short/long/stacked corner tests with actual source. Measure both center-hit and remaining overlap of Add First Contact; do not mark that or general stacked-content occlusion closed if any remains. Do not implement compact/scroll/timer-policy changes in this batch. Publish the narrow tested update, report flakies separately and retain explicit residuals. No other source/session/cache/config/security changes, merge or deployment. Continue without another keep-going.

Copy link
Copy Markdown
Owner Author

Acknowledged 5971765202. Applying the measured mobile inset change, 5rem → 4.75rem, in the same three files only (components/ui/toast.tsx, tests/mobile-toast-safe-area.test.ts, tests/e2e/form-error-semantics.spec.ts), after re-checking the current claims at d3b3a51.

Proof:

  • Baseline-red, then fix-green, on the 360 dialog-chip interception, built from the actual source.
  • The 360 and 390 short/long/stacked corner tests stay in.
  • Add First Contact: I'll measure both the centre-hit and the remaining overlap. Neither it nor general stacked-content occlusion will be marked closed while any of it remains.

What stays the same:

  • desktop lg:bottom-40;
  • corner target access;
  • notice actions and timers;
  • keyboard, Undo and Dismiss behaviour.

Out of scope for this batch: compact, scroll or timer-policy changes. Flakies will be reported separately. No session, cache, config or security changes, and no merge or deploy.


Generated by Claude Code

…#2)

At 360x780 the 5rem inset made the stack 264px wide. The longest German
notice (errors.thatChangeWasNotSaved) then took a seventh line, which
reached further: with the contact dialog open, it took the tap meant
for the Sonstiges chip and covered Freund. With the dialog closed, it
took the centre of the empty page's "Ersten Kontakt hinzufügen". At
4.75rem the stack is 268px wide and the notice keeps to six lines.
Measured first with runtime overrides on #778 (5971751746); applied on
the owner's go-ahead (5971765202).

- components/ui/toast.tsx: pr-[calc(5rem+var(--safe-right))] becomes
  pr-[calc(4.75rem+var(--safe-right))], a 0.25rem gap to the corner
  column. The comment says why, and what remains. Unchanged:
  lg:bottom-40 and everything from lg, the bottom offset, roles and
  live regions, timers, hover/focus pause, Dismiss/Undo.
- tests/mobile-toast-safe-area.test.ts asserts the 4.75rem inset.
- tests/e2e/form-error-semantics.spec.ts:
  - The 390 placement edge is 390 - 76.
  - New dialog-open cases at 360 and 390, short/long/stacked. They
    record the dialog controls a notice's box covers and those whose
    centre it takes (DIALOG_OPEN). Freund and Sonstiges must stay clear
    of the long notice, and each notice's Dismiss takes its own tap and
    focus.
  - UNDER_NOTICE measures "Ersten Kontakt hinzufügen" / "Add First
    Contact" at 360: where a centre tap lands, and the largest overlap
    in px (+-2).
  - RESIDUAL, not closed:
    - long notice: 229x15 of the 249x44 button, centre clear;
    - three notices: all of the 184x44 button, centre taken;
    - with the dialog open: Telefon under the long notice; Friend,
      Other and Phone under three; Phone / Alternate phone at 390;
    - the language bar, as before.

Baseline-red, on the d3b3a51 build (5rem), with these tests:
- 360 long, dialog open: covered also had Freund and Sonstiges, and
  intercepted also had Sonstiges.
- 360 long, closed: the centre of "Ersten Kontakt hinzufügen" landed on
  the notice ('toast').
- 360 stacked: failed only on the overlap placeholder (184 measured).
  Those values were then recorded from the fixed build, 184x44, the same
  as before.
- The unit case failed against the 5rem toast.tsx.

Fix-green, on the 4.75rem build:
- 360/390 corner, dialog-open and placement cases: all pass.
- Quick capture and Ask AI take their own taps in every state.
- The replaced-placement negative controls still read as blocked.

Validation (local production build, local Supabase, --retries=0):
- form-error-semantics.spec.ts full, twice at --workers=2: 62/62, 62/62.
- Corner describe (15 cases + 15 controls + 6 dialog-open + the 390
  placement) at --repeat-each=5 --workers=4: 185/185. No flaky.
- weekly-meal-planner + quick-capture-task: 83/83.
- 64 unit files that read toast.tsx: 1,331 passed.
- tsc --noEmit, eslint and git diff --check: clean.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TRY21ZKsFrfB3qtoP972A4

Copy link
Copy Markdown
Owner Author

Re 5971765202: the mobile inset is now 4.75rem, in 93c306f6b00214b14efdd0a114a104ce2939f20d. It's on d3b3a51c and touches the same three files only. main (40bf5ff3) isn't merged in.

CI for d3b3a51c (37141287524): Typecheck, Database, Mobile and E2E 2,368 passed, 3 skipped, 0 flaky. That run was left to finish; I pushed only after it completed.

Change

  • components/ui/toast.tsx: on the stack, pr-[calc(5rem+var(--safe-right))] → pr-[calc(4.75rem+var(--safe-right))]. That leaves a 0.25rem gap to the corner column, and the comment explains why and what remains.
  • Unchanged: lg:bottom-40 and everything from lg, the bottom offset, role/aria-live, timers, hover/focus pause, Dismiss/Undo and the keyboard path.
  • No compact, scroll or timer-policy change.

Claims rechecked on the d3b3a51c build (5rem) → baseline-red. The new cases ran against an actual source build, not overrides:

Case 5rem build 4.75rem build
360x780 long, dialog open: controls covered / centre taken covered Freund, Sonstiges, Telefon / taken Sonstiges, Telefon (red) covered Telefon / taken Telefon
360x780 long, closed: "Ersten Kontakt hinzufügen" centre toast (red) the button itself; overlap 229x15 px of 249x44
360x780 stacked, closed: "Add First Contact" centre toast, overlap 184 px wide (red only on the placeholder) centre toast, overlap 184x44 px of 184x44, unchanged

The unit case also failed against the 5rem toast.tsx, then passed.

Fix-green on the 4.75rem build

  • The 360 and 390 corner cases all pass: short, long and stacked. Quick capture and Ask AI take their own taps in every state, and the keyboard path is unchanged.
  • The replaced-placement negative controls still read as blocked.
  • New dialog-open cases at 360 and 390, short/long/stacked:
    • they record the dialog controls a notice covers and those whose centre it takes;
    • Freund and Sonstiges must stay clear of the long notice;
    • each notice's Dismiss/Ausblenden takes its own tap (elementFromPoint) and its own focus.

Validation (local production build of 93c306f6, local Supabase, --retries=0)

  • form-error-semantics.spec.ts full, twice at --workers=2: 62/62, 62/62.
  • Corner describe (15 cases + 15 negative controls + 6 dialog-open + the 390 placement case), --repeat-each=5 --workers=4: 185/185.
  • weekly-meal-planner + quick-capture-task: 83/83.
  • 64 unit files that read toast.tsx: 1,331 passed.
  • tsc --noEmit, eslint and git diff --check: clean.

Flaky: none locally in this batch. The last hosted flake was on f3988ccb: at 390 px › paperwork: a save from a closed composer…, first attempt. waitForResponse for the paperwork server action exceeded the 30s test timeout, then it passed on retry. That's the paperwork composer, not related to the toast placement. It didn't recur on d3b3a51c.

Residuals, recorded in the spec as RESIDUAL and not closed

  • 360x780, dialog closed:
    • a long notice still crosses the top 15 px of "Ersten Kontakt hinzufügen" (229x15 of 249x44). Its centre is clear, but the overlap remains;
    • three notices cover all of "Add First Contact" (184x44), centre included;
    • the language bar is under the notices.
  • 360x780, dialog open:
    • one long notice: Telefon;
    • three notices: Friend, Other (relationship chips) and Phone, as at full width;
    • one short notice: Phone, edge only.
  • 390x844, dialog open: Alternate phone (short); Telefon and Alternative Telefonnummer (long); Phone and Alternate phone (stacked). The language bar is covered when the dialog is closed.
  • The fix relies on this string wrapping to 6 lines at 268px. Another locale or a longer message can cross the next line threshold, so it isn't a general guarantee. Stacked-content occlusion stays open. The next scope is the compact/queued option, which needs a timer decision first (see 5971751746).
  • As before: from lg the raised stack may sit over page content; the 1024 zero-width assistant composer (assistant layout); docs: verify audit closure counts and current release blockers #869 ledger evidence still held.

CI for 93c306f6: 37143611911, in progress. It will be recorded in the Runs table. Not merged, not marked ready, not deployed.


Generated by Claude Code

Copy link
Copy Markdown
Owner Author

Blackstone: latest4.75rem completion received; keep remaining mobile/stacked occlusion open. I have asked Jimmy about a mobile-only single-visible compact queue with hidden timers paused and every Dismiss/Undo action preserved; no approval has arrived, so do not implement that timer/queue policy yet. Useful next bounded work while current CI runs: prepare a concrete acceptance-test plan and minimal component/test scope for that option, covering no lost notices/actions, ordering, new urgent notices, route changes/unmounts, resize across lg, hover/focus/pause, keyboard/screen-reader announcements and exact360/390 content hit targets. Compare against current behavior and identify any unavoidable trade-off; no production/UI policy change or broad redesign. Keep desktop unchanged and publish only bounded evidence/design if appropriate. Do not duplicate other workers' files. Report the plan so implementation can start immediately if approved.

Copy link
Copy Markdown
Owner Author

Acknowledged 5972124516. Mobile and stacked occlusion stay open. The single-visible compact queue is not being implemented: no timer or queue policy change and no source change. This round is a plan only, posted here as a comment.

The plan will cover:

  • the mobile-only behaviour (below lg), with desktop unchanged;
  • a concrete acceptance-test plan:
    • no lost notices or actions; every Dismiss/Undo is still reachable;
    • ordering, and new urgent notices;
    • route changes and unmounts;
    • resize across lg;
    • hover/focus pause, with hidden timers paused;
    • keyboard and screen-reader announcements (aria-live, so queued notices are still announced);
    • exact 360/390 content hit targets, using the existing corner/content harness;
  • the minimal component and test scope (files, functions, expected diff size), kept to the files this PR already owns so it doesn't duplicate other workers' files;
  • a comparison against current behaviour, and any trade-off that can't be avoided.

CI on 93c306f is untouched. No session, cache, security or config changes, and no merge or deploy.


Generated by Claude Code

Copy link
Copy Markdown
Owner Author

Re 5972124516: this is a plan only for a mobile-only (below lg) single-visible toast queue. Nothing in it is implemented, no timer or queue policy has changed, and no code was pushed. The plan is written so work can start as soon as it's approved. Desktop stays as it is. Line numbers refer to components/ui/toast.tsx at 93c306f6.

1. What happens today

  • State (ToastProvider, l.56–110):
    • toasts: { id, tone: 'success'|'error'|'info', message, action?: { label, onClick } }[], ids from a counter ref.
    • New notices are appended at the end and render last, at the bottom of the stack.
    • There is no cap and no dedupe. Two identical refusals show twice, as in the stacked case.
  • Timers (l.72–103):
    • Each notice has its own setTimeout, held in timers (a Map): LIFETIME.plain 4,200 ms, LIFETIME.action 7,000 ms.
    • pauseAll clears every pending timer. It runs on onMouseEnter and on onFocusCapture of the stack.
    • resumeAll reschedules every notice with a full window. It runs on onMouseLeave and onBlurCapture.
    • One quirk: push always schedules. So a notice that arrives while the stack is hovered or focused counts down, even though the others are paused.
  • Actions:
    • Undo calls action.onClick() then dismiss(id).
    • Dismiss (X, aria-label={tr('toast.dismiss')}) calls dismiss(id).
    • Both clear that notice's timer.
  • Semantics: each notice is its own live region: role="alert" + aria-live="assertive" for errors, role="status" + polite otherwise. It's inserted with its text already in it, so it's announced on arrival (as Chromium's tree exposes it; not verified with a real screen reader).
  • Mount:
  • Placement:
    • Below lg: full-width column above the tab bar, pl-4 pr-[calc(4.75rem+var(--safe-right))].
    • From lg: bottom-right, lg:bottom-40, 14rem.
    • Every notice shows at once.

2. Proposed behaviour (below lg only; from lg exactly as today)

  • Mode:

    • "Queue mode" applies when (max-width: 1023px) matches, using the existing useMediaQuery in lib/hooks/use-media-query.ts. It starts false (SSR-safe) and updates in an effect.
    • Stack mode is today's behaviour, unchanged.
  • Which notice is visible: the newest one not yet dismissed.

    • A new notice, urgent or not, shows immediately. That answers the "urgent arrives while others are queued" case without a tone-precedence rule, which could let a stale error hide fresh confirmations.
    • Older notices wait, in order. When the visible one goes, the next-newest shows.
  • Timers:

    • Only the visible notice has a pending timer. Queued notices have none: they are paused, not counting.
    • When a notice becomes visible, it gets a full window (LIFETIME.action / .plain). This is the same rule resumeAll already applies.
    • A notice displaced by a newer one loses its timer and gets a full window again when it shows.
    • Hover or focus on the stack pauses the visible notice (and the expanded list, below), as today.
    • A notice arriving during a pause doesn't start counting until the pause ends. In queue mode only, this fixes the quirk in §1 there.
  • "+N more":

    • A real <button> above the visible notice. Visible text is +N; its accessible name is a new key toast.more, e.g. "{count} more notices", with aria-expanded.
    • Activating it expands to today's full stack (all notices, all actions), paused, because pointer or focus is in the stack.
    • Collapsing returns to the newest notice. Collapse happens on Escape, on activating it again, or when focus and pointer both leave.
    • So every queued notice's Dismiss/Undo can be reached by pointer and by keyboard at any time.
  • Dismiss / Undo / expiry of the visible notice: it is removed (for Undo, onClick runs first, as today). The next-newest shows with a full window. +N updates.

  • Announcements: queued notices are not rendered as live regions, so the queue needs its own channel. Add two visually hidden live regions to the provider:

    • role="status" polite, and role="alert" assertive;
    • each message is written there once, when it is pushed;
    • in queue mode the visible card drops its own role/aria-live, so nothing is announced twice.

    Desktop keeps per-card live regions as today.

  • Ordering guarantees:

    • Visible = newest remaining.
    • The queue keeps arrival order.
    • Nothing is dropped except by Dismiss, Undo or the visible notice's own expiry.
    • No dedupe, since that would be a policy change.
  • Unchanged: ToastApi, LIFETIME, roles/tones, the Undo semantics, haptics, desktop placement and lg:bottom-40.

3. Acceptance tests (concrete; existing harness in tests/e2e/form-error-semantics.spec.ts plus unit)

Unit (fake timers; the same hook-mocking harness as a-toast-you-can-still-reach-waits.test.ts, with useEffect and matchMedia stubbed):

  • U1 Queue mode, 3 pushes. Only the newest has a pending timer, and the 2 queued have none.
  • U2 Expiry of the visible notice removes it. The next-newest gets a full LIFETIME window.
  • U3 Dismiss and Undo of the visible notice: Undo's onClick runs once; the next shows; nothing else is removed.
  • U4 A push during pauseAll starts no timer until resumeAll, which then gives full windows.
  • U5 Draining 3 notices takes the sum of their windows, and each one was visible at some point.
  • U6 Stack mode, desktop: every existing assertion in a-toast-you-can-still-reach-waits.test.ts still holds, unchanged.
  • U7 Each message goes to the hidden live region exactly once, on push, with the right politeness.

E2E (production build, real notices, --retries=0):

  • E1 No lost notices, 390x844 and 360x780:

    • raise 3 refusals; one card plus "+2" is visible;
    • Dismiss each visible card in turn; each one appears, is actionable (hitAt on its Dismiss), then goes;
    • the count goes 3 → 2 → 1 → 0;
    • the order seen is newest → oldest.
  • E2 Undo is preserved, 390:

    • quick capture ×2 (two Undo notices);
    • the second is visible and the first is queued;
    • wait past 7 s: the first is still queued, not gone;
    • Dismiss the second; the first shows, and its Undo undoes its own row (verified by the list or the network).
  • E3 New urgent while queued: with 2 queued, raise a refusal. It shows at once, and the earlier notices keep their places.

  • E4 Route change: with 3 queued, navigate client-side (sidebar link). The queue is intact, with the same order and the same visible notice.

  • E5 Unmount: with notices queued, a full reload leaves no notices and no console errors (same as today, documented).

  • E6 Resize across lg:

    • 390 → 1280 with 3 queued: three cards in the desktop stack, no duplicates (count of [role=alert] cards = 3), each with a timer (all gone after LIFETIME + margin once the pointer is away);
    • 1280 → 390: one card plus "+2", queued notices paused (still present after 7 s).
  • E7 Hover/focus pause:

    • the visible notice is held while hovered or focused past its window, and leaves after release;
    • a notice pushed while hovered doesn't expire while hovered;
    • desktop's existing quick-capture pause case is unchanged.
  • E8 Keyboard:

    • Tab reaches "+N" and then the visible notice's Undo/Dismiss, and Enter works;
    • "+N" exposes aria-expanded, and Enter expands so every card's Dismiss/Undo takes focus;
    • Escape collapses;
    • the corner keyboard path cases stay as they are.
  • E9 Announcements (accessibility tree via CDP, as this spec already does): each message appears exactly once in a live region, at push time, including queued ones. There is no second announcement when a queued notice becomes visible. This is the tree, not a real screen reader; a manual VoiceOver/TalkBack pass stays a separate item.

  • E10 Hit targets, same harness, expected rows to be measured on the build. The compact simulation in 5971751746 suggests:

    Size State Closed (corner rows) Dialog open
    360x780 short, long unchanged from 93c306f6: language bar; long also 229x15 px of "Ersten Kontakt hinzufügen", centre clear short Phone (edge); long Telefon
    360x780 stacked → 1 + "+2" language bar only; "Add First Contact" clear (184x44 today) Phone only; Friend / Other clear
    390x844 short, long unchanged unchanged
    390x844 stacked → 1 + "+2" nothing covered Alternate phone, Phone (edge)
    all expanded (user activated "+2") today's stacked rows, user-initiated today's stacked rows

    Quick capture and Ask AI must take their own taps in every row. The replaced-placement negative controls stay. A new control renders all three notices expanded and must reproduce today's stacked rows.

  • E11 Desktop unchanged: the 1024/1280/1440 corner, dialog-footer, German, quick-capture and lg:bottom-40 cases pass unmodified.

4. Minimal scope

File Change Estimate
components/ui/toast.tsx Queue mode via useMediaQuery('(max-width: 1023px)'); a paused ref; schedule only the visible notice in queue mode; the "+N" button and expanded state; 2 sr-only live regions; a shown list fed to the existing toasts.map +80–100 / −10
lib/i18n/messages/*.json (toast.more, and possibly toast.showAll) one key, in each locale file that carries toast.dismiss (7) +7–14
tests/a-toast-you-can-still-reach-waits.test.ts its React mock needs useEffect and a matchMedia stub; its assertions are unchanged +5–10
new tests/toast-mobile-queue.test.ts U1–U7 +150
tests/e2e/form-error-semantics.spec.ts E1–E10, plus updated 360/390 stacked and dialog rows +150–200 / −20
tests/mobile-toast-safe-area.test.ts none expected 0

Notes:

  • The hook exists at lib/hooks/use-media-query.ts (useMediaQuery, useIsDesktop), so there's no new hook.
  • The i18n files and the existing guard unit test are outside the three files allocated so far, so they need the owner's allocation.

Overlap with other open PRs (all 89 open PRs checked):

5. Today vs proposed (below lg)

Today (93c306f6) Proposed queue
Visible notices all newest one, plus "+N" (all when expanded)
360 "Add First Contact" / dialog chips with 3 notices covered, centre taken clear (expected)
360 long single notice 229x15 px edge overlap unchanged: the queue doesn't help a single long notice
Timers every notice counts at once only the visible one counts; queued ones are paused
Time to drain 3 plain notices (untouched) about 4.2 s about 12.6 s
An older Undo (e.g. 2 captures) visible, 7 s window queued and paused; its 7 s starts when it shows
Screen reader announcement per card, on arrival sr-only region, on arrival, once
Desktop — identical

Unavoidable trade-offs

  • Delayed visibility. Older notices aren't shown until the newer ones go, or the "+N" is opened. Sighted users see later feedback first.
  • Longer Undo windows in practice. A queued Undo can be acted on well after its write, once it shows. That's safer, but the queue's last notice may linger about 3× longer than today.
  • Announcement timing is decoupled from visibility. A screen-reader user hears a queued message before its card, and its actions, are on screen. "+N" gives the path.
  • The single long notice at 360 still edge-overlaps. Only line count, meaning width or copy, addresses that.
  • Timer behaviour changes below lg (paused-while-queued, no countdown for arrivals during a pause). That is a timer-policy change and needs Jimmy's approval, as noted.

Not implemented. No production, UI, session, cache, security or config change, and no merge or deploy.


Generated by Claude Code

Copy link
Copy Markdown
Owner Author

Blackstone: compact-queue plan received; keep implementation paused for the pending decision. Next independent bounded investigation is your earlier1024px zero-width assistant composer observation. Determine whether it is a test-fixture artifact or reproducible in a representative rendered page using existing supported local fixtures. Claim only a new uniquely named layout regression spec/evidence scope before editing; keep assistant-module, shared session/cache, toast source and Surge-owned files read-only. Measure composer width, visible input/send controls, pointer/keyboard reachability at neighboring viewport widths, and include a fixture/control comparison. Do not assert a product defect from a synthetic missing-layout dependency. Return exact repro or explain artifact, with smallest proposed owner/scope if real. No live provider calls, production changes, merge/deploy or queue/timer-policy implementation. Continue while the current CI and decision wait.

Copy link
Copy Markdown
Owner Author

Acknowledged 5972242509. The compact-queue implementation stays paused pending the decision.

Next is the 1024px zero-width assistant composer. Before any edit, I'm claiming one new, uniquely named spec, tests/e2e/assistant-composer-width.spec.ts, as the only file this investigation will write. The assistant module, shared session/cache code, the toast source and Surge-owned files stay read-only.

Method:

  • Use the existing supported local fixtures (local Supabase, seeded persona, production build). No live provider calls.
  • Measure the composer's width, its visible input and send controls, and pointer and keyboard reachability. Run at 1024 and neighbouring widths (e.g. 1000, 1023, 1024, 1100, 1180, 1280, 1440).
  • Compare the fixture against a control: the same page reached through normal navigation versus the corner-orb path the toast spec used, with and without a sidebar or route state that might be synthetic.
  • Report either an exact repro or an explanation of the artifact. I won't call this a product defect if it comes from a synthetic missing layout dependency. If it's real, I'll give the smallest proposed owner and scope.

No production changes, no merge or deploy, and no queue or timer-policy work.


Generated by Claude Code

A new spec only; no product change. The owner asked on #778
(5972242509) whether the 0px-wide assistant composer seen at 1024px is
a test artifact or reproduces on the page as rendered.

It reproduces with the standard local fixtures: a fresh household, a
real sign-in, a production build. It is the same whether the page is
reached by direct load, the sidebar link or the corner orb; with the
service worker allowed or blocked; and with or without a notice
showing.

The page opens on its welcome surface (hero) until a conversation with
one of the reader's own messages is open. That is the state on every
arrival, saved conversations or not, and after "New chat". From lg,
components/assistant/workspace.tsx lays the workspace out as
lg:grid-cols-[320px_1fr_330px] with gap-6. That sits in a main of
W - 280 (sidebar) - 56 (padding). The fixed tracks and gaps take 698px,
so the centre column, where the hero sits, measures W - 1034, and 0
below that. The hero's chrome takes about 146px of it.

Measured on the page:
- centre column: 0 / 66 / 146 / 246 / 406 px at 1024 / 1100 / 1180 /
  1280 / 1440;
- the "Ask Bubaly" text box: 0 / 0 / 0 / 100 / 260 px wide;
- 768-1023: a single pane, text box 526px.

At 1024-1180 the box can't be seen or pointed at. It still takes focus
and text, so a keyboard user types blind. The 44px send button
overflows the column and still takes its own tap.

The spec records:
- per width, whether the text box can be seen, pointed at and typed in.
  At 1024, 1100 and 1180 this is marked test.fail() as a reproduced
  defect; it fails at the box's 0px width, as checked with the marker
  removed;
- a passing record of what the reader gets instead at those widths, so
  the fix flips both;
- a control at 1024: with a seeded conversation opened, the docked
  composer in the 320px left column can be seen, clicked and typed in.

Any request to a host other than localhost is aborted and fails the
test. The page makes none on load, so no AI provider is reached. No AI
request is sent: Send is never clicked.

Validation (local production build of 93c306f, local Supabase,
--retries=0):
- the spec at --repeat-each=3 --workers=3: 36/36, 9 of them the
  expected failures;
- tsc, eslint and git diff --check: clean.

Proposed fix, not made here (components/assistant/workspace.tsx is the
assistant module's, read-only for this branch):
- line 127: lg:grid-cols-[300px_minmax(0,1fr)] 2xl:grid-cols-[320px_1fr_330px];
- line 130: on the context aside, lg:col-span-2 2xl:col-span-1, so it
  sits under the two columns until 1536px.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TRY21ZKsFrfB3qtoP972A4

Copy link
Copy Markdown
Owner Author

Re 5972242509: the 1024px zero-width assistant composer is a real layout defect, not a fixture artifact. Evidence and a new standing spec are in dd22990bf96901fbcab417b25c375d7342da638d, which adds only tests/e2e/assistant-composer-width.spec.ts. No product change.

CI for 93c306f6 (37143611911): Typecheck, Database, Mobile and E2E 2,374 passed, 3 skipped, 0 flaky. That run was left to finish before this push.

Setup

  • Local production build (93c306f6 source), local Supabase, a fresh disposable Plus household, a real sign-in.
  • Every request to a host other than localhost was aborted and recorded. None was made, and no AI request was sent: Send is never clicked, and the page makes none on load.
  • Read-only throughout: components/assistant/*, assistant-module.tsx, session/cache and toast source.

Measured on the page, as a reader arrives (/dashboard/assistant, viewport height 800):

Width Layout Centre column (where the welcome composer sits) "Ask Bubaly" text box Pointer at its centre Keyboard Send (44px)
768 / 1000 / 1023 one pane (below lg) — 526x56 the box Tab reaches it, types its own tap
1024 320px 0px 330px 0px 0x0 nothing (no box) Tab reaches it; types blind its own tap, overflowing into the right rail
1100 320px 66px 330px 66px 0x0 nothing types blind its own tap
1180 320px 146px 330px 146px 0x0 nothing types blind its own tap
1280 320px 246px 330px 246px 100x56 the box types its own tap
1440 320px 406px 330px 406px 260x56 the box types its own tap

Fixture vs control. The result was the same at 1024, 1180 and 1280 in each of these cases:

  • reached by direct load, by the sidebar's "AI Assistant" link, or by the corner orb (the toast spec's path);
  • with the service worker blocked or allowed;
  • with or without a toast notice showing.

The one thing that changes it is the page's own state:

  • The page shows its welcome surface (hero) until a conversation with one of the reader's own messages is open.
  • That is the state on every arrival, with saved conversations or not (checked with a seeded conversation), and after "New chat".
  • With a seeded conversation opened at 1024, the docked composer ("Message Bubaly") sits in the 320px left column at 182x20. It takes its own tap and types. The centre column is still 0px; only the hero lived there.
  • So it isn't a missing-layout or CSS dependency of the test. The workspace's grid is applied (display: grid), as it is for any reader.

Exact repro.

  1. At 1024x800, sign in as any parent with the sidebar expanded (the default, 280px). Open /dashboard/assistant.
  2. The "Ask Bubaly" box is 0px wide and can't be seen or clicked.
  3. Tab still reaches it and typing goes in, unseen.
  4. This holds up to about 1180px.

Root cause (components/assistant/workspace.tsx l.127):

  • From lg, the workspace is lg:grid-cols-[320px_1fr_330px] with gap-6.
  • main is the viewport less the 280px sidebar and 2 x 28px of padding.
  • The fixed tracks and gaps take 698px, so the centre column is W − 1034px, and 0 below that.
  • The hero composer's own chrome (padding, send) takes about 146px of it, so the text box stays at 0 up to about 1180px.

Smallest proposed fix, not made here. The file belongs to the assistant module and was read-only for this task. Two lines in components/assistant/workspace.tsx:

  • l.127: lg:grid-cols-[320px_1fr_330px] → lg:grid-cols-[300px_minmax(0,1fr)] 2xl:grid-cols-[320px_1fr_330px]
  • l.130: the context <aside> gains lg:col-span-2 2xl:col-span-1. The household context rail then sits under the two columns until 1536px.

The same classes, applied as a runtime override for measurement only, gave:

Width Centre column Text box
1024 364px 218x56
1100 440px 294x56
1180 520px 374x56
1280 620px 474x56
1440 780px 526x56

In every case the text box took its own tap and typed.

Trade-off: below 1536px the context rail moves under the conversation and results, so the page gets taller. The phone layout is unchanged. Any open PR? None touches workspace.tsx. #788, #834 and #875 touch assistant-module.tsx and conversation-pane.tsx, not this file.

The spec (tests/e2e/assistant-composer-width.spec.ts), using this repo's convention that reproduced defects are test.fail():

  • Per width, 768–1440: can the text box be seen, pointed at and typed in, and does Send take its own tap?

    • At 1024, 1100 and 1180 this is test.fail().
    • With the marker removed, all three fail at the box's 0px width, the expected reason.
  • At those three widths, a passing record of what the reader gets instead:

    • the exact centre-column width;
    • a 0px box that is hidden yet still focusable and typeable;
    • Send, 44px, taking its own tap.

    The fix flips both halves.

  • Control: at 1024 with a seeded conversation opened, the docked composer can be seen, clicked and typed in.

  • Any request to a non-local host fails the test.

Validation: the spec at --repeat-each=3 --workers=3 passed 36/36, 9 of those being the expected failures; tsc, eslint and git diff --check are clean. One fix during validation: the control first read the docked box while the thread was still smooth-scrolling into view (3 of 3 under load). It now waits for the box to stop moving before measuring. No timeout was changed.

Side finding, not changed: the signIn helper in form-error-semantics.spec.ts waits for toHaveURL(/…\/dashboard\/contacts$/). The login URL itself (/login?redirect=/dashboard/contacts) already matches that, so it can return before sign-in completes. Its callers are rescued by auto-waiting on the next element. The new spec waits for the pathname instead.

CI for dd22990b: 37147363901, in progress. It will be recorded in the Runs table. Compact-queue work stays paused. Not merged, not marked ready, not deployed.


Generated by Claude Code

Copy link
Copy Markdown
Owner Author

Blackstone: composer investigation5972638789 received. Bounded independent review confirms representative route/layout/CSS and no known current-lane ownership collision. Proceed with exactly components/assistant/workspace.tsx and tests/e2e/assistant-composer-width.spec.ts: use the measured two-column layout below2xl with minmax(0,1fr), retain the existing three-column layout at2xl, and place the context aside across both columns below2xl. Proposed classes: lg:grid-cols-[300px_minmax(0,1fr)] 2xl:grid-cols-[320px_1fr_330px]; aside lg:col-span-2 2xl:col-span-1. Recheck current claims before editing. Update the spec's old-class locator and broken-width assertions honestly; require real input/send pointer+keyboard reachability at1024/1100/1180 and neighboring widths, preserve390/desktop2xl behavior, navigation/scroll/context access, and matched baseline-red/fix-green. Do not merely remove test.fail or change fixture dimensions. Report the below2xl context-rail placement trade-off and any new overflow/hidden controls. No assistant engine/session/cache/toast/queue changes, live provider calls, merge or deploy. Publish the narrow tested draft/update and continue.

Copy link
Copy Markdown
Owner Author

Acknowledged 5972691600. Proceeding with exactly two files, components/assistant/workspace.tsx and tests/e2e/assistant-composer-width.spec.ts, after rechecking the current claims at dd22990.

Change

  • l.127: lg:grid-cols-[320px_1fr_330px] becomes lg:grid-cols-[300px_minmax(0,1fr)] 2xl:grid-cols-[320px_1fr_330px].
  • l.130: the context <aside> gains lg:col-span-2 2xl:col-span-1.

Spec

  • The old-class locator and the broken-width assertions are rewritten honestly, not just by removing test.fail() or changing the fixture dimensions.
  • At 1024, 1100 and 1180, and the widths either side, the real input and Send are reachable by pointer and by keyboard.
  • 390 and 2xl desktop behaviour stays as it is, and so do navigation, scroll and context access.
  • I'll show the baseline red then fix green on matched builds.

Reporting

  • the context-rail placement trade-off below 2xl;
  • any new overflow or hidden controls.

No assistant engine, session, cache, toast or queue changes, no live provider calls, and no merge or deploy.


Generated by Claude Code

Copy link
Copy Markdown
Owner Author

Scope correction to5972691600 after the remaining guard review: include only the layout assertion/title in tests/assistant-workspace.test.ts around145 as the third file; it pins the old lg three-column class. Preserve #834's separate stream-parser expectation around230 exactly. Add1023/1024 and1535/1536 boundary checks, Context-row placement and no horizontal overflow; retain docked/phone controls. Qualify hero-state evidence as fresh/no-restored-thread or New chat, since restored threads may show the docked composer. No broader edits to the shared unit file. This is the same layout repair, not another task.

Copy link
Copy Markdown
Owner Author

Acknowledged 5972700336. It is the same layout repair, with one more file: tests/assistant-workspace.test.ts. In that file, only the layout assertion and its title around l.145 change, because they pin the old lg three-column class. #834's stream-parser expectation around l.230 stays exactly as it is, and nothing else in that shared unit file changes.

The E2E spec adds:

  • boundary checks at 1023/1024 and 1535/1536;
  • Context-row placement checks;
  • a no-horizontal-overflow check at every width.

The docked-composer and phone-control checks stay. Hero-state evidence will say whether it comes from a fresh page with no restored thread or from after "New chat".


Generated by Claude Code

Copy link
Copy Markdown
Owner Author

Blackstone: Jimmy approved the proposed mobile compact notification queue: one visible notice at a time on phones, waiting timers paused until shown, every Dismiss/Undo action preserved, desktop behavior unchanged. Queue this AFTER your current composer-layout fix; do not interrupt or combine unrelated source work. Use your completed acceptance plan, claim exact component/test paths and check current ownership before implementation. Preserve all notices/order/actions, keyboard and screen-reader access, safe resize/route/unmount behavior, and verify360/390 hit targets with short/long/stacked inputs plus matched negative controls. Surface any new material trade-off before broadening beyond the approved mobile behavior. Publish a separate bounded tested draft or clearly separated commit with exact CI/evidence and remaining limitations. No production/security/configuration changes, merge or deploy.

NewWorldVenture added a commit that referenced this pull request Oct 4, 2026
* docs: track served receipt repairs and focused validation

* docs: record terminal CI, #768 review and a status-mark repair (ledger checkpoint)

Documentation only, on top of frozen #770 95b785e (unchanged), for
task JIMMY-NWV-LEDGER770-20261001 while the Codex coordinator is stopped.

- main 01b2c38 post-merge CI36846986553: terminal all-four success,
  1,909 clean E2E.
- #768 59b4c98: all-four CI36845154127 plus Support's independent
  review; the original photo-flake cause/fix stays unestablished.
- #769 71e8f88: all-four CI36846162597; Daniel's review not yet posted.
- #770 95b785e: all-four CI36847694663; 602 document checks
  reproduced and SHA256 matched.
- Three #770 Register B rows read `?? IN PROGRESS`; the mark is
  restored to the IN PROGRESS mark they were already counted under.
  No ID, order, status, count or evidence text changes; no new PASS.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KRUgA6hD6QgzmtpSP6TUmP

* docs: add #772 card-UI second-pass evidence and the #768 approval hold to the checkpoint

Owner review 5378367970. #772 07602d4 is recorded as author-reported
local evidence (18 new / 54 repeat / 87 combined synthetic browser
cases, 11 mutants), with its hosted CI still running and its two
reported, unfixed residuals. No one-card-per-child policy, no Register B
row, no PASS. #768 promotion/merge awaits the owner's explicit approval.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KRUgA6hD6QgzmtpSP6TUmP

* docs: record #773 card retry characterization as candidate evidence (ledger checkpoint)

Owner review 5378577116. #773 2526ec2 is recorded as author-reported,
test-only synthetic evidence with its hosted CI still running: R1
remount duplicate issuance, R4 provider-success/mirror-failure orphan
plus duplicate retry, R5 concurrent cardholder unique race. Multiple
cards per child stay legitimate; no one-card policy. Support owns
repairs A and C; R4 stays unresolved with no cancel/adopt/schema
authorization. Open workflow gaps, not a live incident; no Register B
row, no PASS. #768 approval still pending.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KRUgA6hD6QgzmtpSP6TUmP

* docs: timestamp later checkpoint additions accurately; record terminal CI and the A+C execution blocker

Owner review 5379042101. The Last Updated line and section heading said
10:55 UTC but included #772 (10:58), #773 (11:21) and the A+C assignment
(11:23); those additions now carry their own times. Records the terminal
all-four CI for #771/#772/#773 (receipt 5930953854) and Support's A+C
source-edit permission blocker (#712 comment 5930951363). Ownership is
not execution clearance; no workaround, no PASS, R4 unresolved.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KRUgA6hD6QgzmtpSP6TUmP

* docs: record #774 feature-tier entitlement draft as stale-base candidate evidence

Owner review 5379179166. #774 c582d96 (12:13 UTC) is new fail-closed
entitlement work, not a #729 duplicate, but based on 84fe103 (174
behind main); its local /pricing and 22,707-test evidence is from that
stale checkout, not current-main acceptance. Hosted CI36860293995 was
running; Blackstone's retest relay ACK pending. No row, PASS or merge.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KRUgA6hD6QgzmtpSP6TUmP

* docs: record #774 repaired candidate af53da9 and verified relay status

Owner review 5379348558. #774's original c582d96 stays recorded as the
abort-only, stale-base baseline (not the repaired candidate). Current
af53da9 normally merges main 01b2c38 and bounds the whole feature-tier
read; author-reported 10 fail/6 control on the predecessor, 16/16 after,
23,947 tests + 2 expected failures, 89 focused checks. Hosted final-head
CI36863502780 and independent review pending. Blackstone's relay is
verified automatic. No row, PASS, promotion or merge.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KRUgA6hD6QgzmtpSP6TUmP

* docs: record ccdb3b9 CI as blocked by the expired E2E fixture; fix #772 CI wording

Owner review 5379767360. #771's exact head CI36863717027 failed only E2E
on the expired fixed-date fixture social-publish-consumers.spec.ts:269
(1 failed / 1,908 passed); recorded as blocked, not a full gate. Same
on #774 af53da9. Fixture-only #775 046bc33 and #774 ecb626b carry
the repair with CI running; #771's green E2E depends on #775. Last
Updated no longer calls #772's hosted CI pending. No PASS, no fixture
copy in this docs-only branch.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KRUgA6hD6QgzmtpSP6TUmP

* docs: record the 16:19 independent reviewer checkpoint as scoped evidence

#771 comment 5935613991: main 01b2c38 served/CI identity, #771's E2E
failure kept, #767/#769 scoped characterizations, the existing #774
admin-write concurrency obligation, canonical Register B tallies, and
owner reconciliation items. No PASS, no closure from unrelated CI.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KRUgA6hD6QgzmtpSP6TUmP

* docs: record DISPLAY-TZ-01 fixture finding, E2E-DATES sweep, and #771's #775 dependency

Owner review 5383057905. DISPLAY-TZ-01: the display-ownership harness
mocked date-fns parseISO as new Date(value), shifting date-only values
west of UTC (from baf9090, TIME-003); #776 069d508 uses real
date-fns with 9 timezone controls and is all-four green (E2E 1,918).
Its parent's failure was only the expired social fixture and stays
separate. E2E-DATES found no other expired fixed-date fixtures.
#771's E2E depends on #775's fixture. No production defect, row, PASS,
promotion or merge.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KRUgA6hD6QgzmtpSP6TUmP

* docs: record #771 15a54e2 terminal CI with flake qualification and Blackstone self-check claim

E2E on CI36900349399: 1,907 passed, 1 failed (expired social fixture,
base lacks #775), 1 flaky (rewards-readback-review.spec.ts:206, cause
unestablished). Records JIMMY-BLK-SELF-CHECK-20261001 scope and NWV's
no-conflict check. No Register B change, no new PASS.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KRUgA6hD6QgzmtpSP6TUmP

* docs: record #777 display self-check as verified draft evidence for UI-WF-024

Exact ee988ce, CI36904951459 all four green (owner-verified E2E 1,920
clean). Records the aria-disabled + in-flight guard focus repair and keeps
physical endurance, real fullscreen/connectivity, unanswered APIs and
screen-reader acceptance outside proved coverage. No PASS, no Register B
change.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KRUgA6hD6QgzmtpSP6TUmP

* docs: record Blackstone's JIMMY-BLK-FORM-A11Y-20261001 narrow claim and NWV's overlap check

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KRUgA6hD6QgzmtpSP6TUmP

* docs: record #771 3b9c01a terminal CI (expired fixture only, no flaky case)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KRUgA6hD6QgzmtpSP6TUmP

* docs: replace anticipated ledger timestamps with verified commit and check-run times

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KRUgA6hD6QgzmtpSP6TUmP

* docs: record #771 6a2e87a terminal CI (expired fixture only)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KRUgA6hD6QgzmtpSP6TUmP

* docs: record parallel review handoff (#773 guard lifecycle, #778 focus regression, #776 parser fidelity, #771 delta clearance)

Reviewer-reported synthetic evidence only; no PASS or closure credit;
PRODUCTION READY: NO retained.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KRUgA6hD6QgzmtpSP6TUmP

* docs: record #771 9f44db3 terminal CI (expired fixture only)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KRUgA6hD6QgzmtpSP6TUmP

* docs: record the routine-export completeness lane claim and NWV's overlap check

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KRUgA6hD6QgzmtpSP6TUmP

* docs: record #781 routine-export repair checkpoint and qualified #773/#778 retests

Reviewer- and author-reported synthetic evidence; #781 CI live with a
mobile dependency-audit failure; no PASS or row closure; PRODUCTION
READY: NO retained.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KRUgA6hD6QgzmtpSP6TUmP

* docs: record the mobile dependency-audit blocker (node-forge GHSA-86w9-cpqp-85rv)

Reproduced on unchanged main; recorded against SUPPORT-D5FE0185C2FA
with no status change; high gate retained, no suppression.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KRUgA6hD6QgzmtpSP6TUmP

* docs: record the memory notes/evidence privacy lane claim and NWV's overlap check

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KRUgA6hD6QgzmtpSP6TUmP

* docs: record #782 memory privacy repair candidate and handoff corrections (#712 PG 16.15, #778 head)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KRUgA6hD6QgzmtpSP6TUmP

* docs: record the Magic Import tool-domain authorization lane claim and NWV's overlap check

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KRUgA6hD6QgzmtpSP6TUmP

* docs: record #771 b1d2d20 terminal CI (E2E expired fixture, Mobile node-forge audit)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KRUgA6hD6QgzmtpSP6TUmP

* docs: record the queued-push recipient membership lane claim (PUSH-001)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KRUgA6hD6QgzmtpSP6TUmP

* docs: record #771 85b22a4 terminal CI (expired fixture, node-forge audit)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KRUgA6hD6QgzmtpSP6TUmP

* docs: record #783 Magic Import authorization repair candidate and fresh terminal CI

Reviewer/author-reported synthetic evidence; IDs kept; no PASS; #778's
E2E green is attempt 2 and carries the #775 fixture; PRODUCTION READY: NO.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KRUgA6hD6QgzmtpSP6TUmP

* docs: record the storage-removal confirmation lane claim (SEC-015) and NWV's overlap check

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KRUgA6hD6QgzmtpSP6TUmP

* docs: record #784 queued-push membership repair candidate (PUSH-001 stays IN PROGRESS)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KRUgA6hD6QgzmtpSP6TUmP

* docs: record the chore-email recipient membership lane claim and NWV's overlap check

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KRUgA6hD6QgzmtpSP6TUmP

* docs: record #785 storage confirmation repair and assign Register B LIBRARY-E78DACB31EA4

lib/storage/confirm-removal.ts had no distinct Register B row; at the
owner's request it gets a permanent file obligation (IN PROGRESS), ID by
the register's sha256(path) rule, appended after the last row. Register B
14,365 = 11,877 + 2,137 + 185 + 157 + 9; no PASS, coverage unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KRUgA6hD6QgzmtpSP6TUmP

* docs: record the chore-email claim expansion (familyName isolation, digest boundary fixtures)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KRUgA6hD6QgzmtpSP6TUmP

* docs: record the child email-channel follow-up and #784's hosted Web result

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KRUgA6hD6QgzmtpSP6TUmP

* docs: record the queued notification-email membership lane claim and NWV's overlap check

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KRUgA6hD6QgzmtpSP6TUmP

* docs: record #784 follow-up head 6fcca98 (child-consent dispatch tests)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KRUgA6hD6QgzmtpSP6TUmP

* docs: record #786 chore-email repair candidate and the childrenBlockedOn pagination claim

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KRUgA6hD6QgzmtpSP6TUmP

* docs: record #787 notification-email repair candidate and assign SUPPORT-42BAD93E7338

Register B 14,366 = 11,877 + 2,138 + 185 + 157 + 9; no PASS, coverage
unchanged. Other new test files in #781-#786 left unassigned pending owner.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KRUgA6hD6QgzmtpSP6TUmP

* docs: record the child-consent pagination lane's widened test-only scope and first broader run

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KRUgA6hD6QgzmtpSP6TUmP

* docs: record #771 a0a735e terminal CI (expired fixture, node-forge audit)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KRUgA6hD6QgzmtpSP6TUmP

* docs: record #789 child-consent completeness candidate, assign SUPPORT-2C1C4D4A1904, flag 12 NOT STARTED/IN PROGRESS discrepancies

Register B 14,367 = 11,877 + 2,139 + 185 + 157 + 9; no PASS. Twelve IDs
described as IN PROGRESS in handoffs are NOT STARTED in the register;
held for an explicit owner decision rather than changed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KRUgA6hD6QgzmtpSP6TUmP

* docs: record the #786 email-preference follow-up and the missing opt-out UI gap

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KRUgA6hD6QgzmtpSP6TUmP

* docs: transition twelve Register B obligations NOT STARTED -> IN PROGRESS by owner decision

Owner decision #771 comment 5950394179. Earlier 'remain/stay IN PROGRESS'
wording is corrected as a recommendation, not the rows' marks. Totals
recomputed from the Register B section: 14,367 = 11,865 + 2,151 + 185 +
157 + 9 (local); hosted 132d791 remains 14,366 = 11,877 + 2,138 + ...
No PASS, closure or blocked credit.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KRUgA6hD6QgzmtpSP6TUmP

* docs: record #786/#787 follow-up heads, the four-PR local composition, and the #788 allowance finding

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KRUgA6hD6QgzmtpSP6TUmP

* docs: record #787's further preference-completeness scope (small configured cap)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KRUgA6hD6QgzmtpSP6TUmP

* docs: record the weekly-digest email-preference lane claim and register SUPPORT-487204DBD109

Register B 14,368 = 11,865 + 2,152 + 185 + 157 + 9; no PASS.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KRUgA6hD6QgzmtpSP6TUmP

* docs: record the email-preference UI lane claim and register four planned file obligations

Register B 14,372 = 11,865 + 2,156 + 185 + 157 + 9; no PASS.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KRUgA6hD6QgzmtpSP6TUmP

* docs: record #787 preference follow-up head 54e8662; mark the 414/414 composition historical

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KRUgA6hD6QgzmtpSP6TUmP

* docs: record #771 132d791 terminal CI (expired fixture, node-forge audit)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KRUgA6hD6QgzmtpSP6TUmP

* docs: record #790 weekly-digest opt-out candidate; SUPPORT-487204DBD109 now cites it

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KRUgA6hD6QgzmtpSP6TUmP

* docs: record #787's hosted Web failure (total-order registry) and its guard follow-up claim

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KRUgA6hD6QgzmtpSP6TUmP

* docs: record the generator audience claim; two NS->IP transitions and SUPPORT-5BB9D7AE3B60

Register B 14,373 = 11,863 + 2,159 + 185 + 157 + 9; no PASS. Held
I18N-001 worktree edit keeps the document_expiry broadcast shape and must
not be revived as is.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KRUgA6hD6QgzmtpSP6TUmP

* docs: record #791 email-setting candidate and #787 guard follow-up bec49a6; flag the missing total-order test row

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KRUgA6hD6QgzmtpSP6TUmP

* docs: record the generator audience claim's medication-test scope extension

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KRUgA6hD6QgzmtpSP6TUmP

* docs: record the six-candidate preference integration proof; correct the hosted-ledger state

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KRUgA6hD6QgzmtpSP6TUmP

* docs: record #789 total-order follow-up and the catalogue-scanner portability claim; register SUPPORT-76A7600ED42D

Register B 14,374 = 11,863 + 2,160 + 185 + 157 + 9; no PASS.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KRUgA6hD6QgzmtpSP6TUmP

* docs: record #786's hosted Web failure and its standalone total-order guard follow-up

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KRUgA6hD6QgzmtpSP6TUmP

* docs: record #792 generator audience and #793 scanner candidates and the standalone registry follow-ups

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KRUgA6hD6QgzmtpSP6TUmP

* docs: record the eight-head composition result 54b8aba as local UTC evidence

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KRUgA6hD6QgzmtpSP6TUmP

* docs: record #788's response-validation follow-up on F19 and the independent hosted-ledger parse

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KRUgA6hD6QgzmtpSP6TUmP

* docs: record the meal-carousel lane claim; SUPPORT-E8F3F434DAC5 NOT STARTED to IN PROGRESS at the claim request

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KRUgA6hD6QgzmtpSP6TUmP

* docs: record the eight-head hosted CI snapshot (Web 3/8 terminal green, Mobile 8/8 dependency-audit red, #784 E2E fixture)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KRUgA6hD6QgzmtpSP6TUmP

* docs: record #771 head 93b9654 terminal CI (Web/Database pass, Mobile node-forge, E2E fixed-date fixture)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KRUgA6hD6QgzmtpSP6TUmP

* docs: record the dashboard-settings claim, the node-forge decision and the #788 coordination inventory

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KRUgA6hD6QgzmtpSP6TUmP

* docs: record the calendar-navigation lane claim and #771 f40462a's Mobile result

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KRUgA6hD6QgzmtpSP6TUmP

* docs: record meal-carousel candidate #794 a9ca92e; SUPPORT-E8F3F434DAC5 already IN PROGRESS at f40462a

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KRUgA6hD6QgzmtpSP6TUmP

* docs: record dashboard-settings candidate #795 cd6d6b3

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KRUgA6hD6QgzmtpSP6TUmP

* docs: record the #794 recipe-search claim extension

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KRUgA6hD6QgzmtpSP6TUmP

* docs: record the root #794/#795 composition 0c48ad1 as exact-head local evidence

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KRUgA6hD6QgzmtpSP6TUmP

* docs: record #794 ae92d44 (carousel plus unmatched-search copy); row cites the new head

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KRUgA6hD6QgzmtpSP6TUmP

* docs: record calendar-navigation candidate #796 24b9b5e

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KRUgA6hD6QgzmtpSP6TUmP

* docs: record the grocery-toggle error-containment claim stacked on #794

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KRUgA6hD6QgzmtpSP6TUmP

* docs: record grocery-toggle candidate #797 5a0ea15 (stacked on #794)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KRUgA6hD6QgzmtpSP6TUmP

* docs: record the todo-dialog completion claim (COMPONENT-35043C5F192D stays NOT STARTED)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KRUgA6hD6QgzmtpSP6TUmP

* docs: record the TIME-003 reminder-drift finding and NWV's release of that input consumer; #797 root original run

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KRUgA6hD6QgzmtpSP6TUmP

* docs: record owner decisions 5391362628 (F19 user-requested scope, one-request voice, hidden Rate the app) and the #788 handoff

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KRUgA6hD6QgzmtpSP6TUmP

* docs: record #788's unverified mobile audit exception, Daniel's #769 stand-down and the pantry-dialog claim

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KRUgA6hD6QgzmtpSP6TUmP

* docs: record todo-dialog candidate #798 58d67b6

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KRUgA6hD6QgzmtpSP6TUmP

* docs: record pantry-dialog candidate #799 98bae27

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KRUgA6hD6QgzmtpSP6TUmP

* docs: record the packing manual-add claim (rows stay NOT STARTED; I18N-011 overlap noted)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KRUgA6hD6QgzmtpSP6TUmP

* docs: reconcile current audit fixes and investigation status

* docs: record #771 f40462a terminal CI (Web/Database pass, Mobile node-forge, E2E fixed-date fixture)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KRUgA6hD6QgzmtpSP6TUmP

* docs: preserve historical audit receipts after peer review

* docs: record #800 as already integrated and the note-dialog rejected-save claim

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KRUgA6hD6QgzmtpSP6TUmP

* docs: track all four pending regression inventory additions

* docs: record packing candidate #801 a1c4a0f; COMPONENT-3385D14A6F1E is NOT STARTED, not IN PROGRESS

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KRUgA6hD6QgzmtpSP6TUmP

* docs: #800's merged state and root's follow-up #802 (f660053, already merged locally)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KRUgA6hD6QgzmtpSP6TUmP

* docs: record note-dialog candidate #803 50f5757

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KRUgA6hD6QgzmtpSP6TUmP

* docs: record the #788 audit-gate script input-validation findings (read-only)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KRUgA6hD6QgzmtpSP6TUmP

* docs: record the #788 nested flyer response-shape finding (read-only, F19 stays open)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KRUgA6hD6QgzmtpSP6TUmP

* docs: record owner follow-through 5391604223 and #788 3cfadcd's verified improvements

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KRUgA6hD6QgzmtpSP6TUmP

* docs: re-qualify the #788 nested flyer finding at 3cfadcd (2 failures/16 controls)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KRUgA6hD6QgzmtpSP6TUmP

* docs: record verified save fixes and current audit inventory

* docs: record independent flyer acceptance and mobile gate findings

* docs: record the main d25e39e merge and the 21 unregistered #699/#710 files

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KRUgA6hD6QgzmtpSP6TUmP

* docs: record the #804 dependency-gate review (6 desired failures/9 controls, read-only)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KRUgA6hD6QgzmtpSP6TUmP

* docs: record #788 9c33e41's bounded nested-flyer repair (51 independent cases; F19 stays open)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KRUgA6hD6QgzmtpSP6TUmP

* docs: link reproducible mobile gate review findings

* docs: reconcile current audit repairs and admin source inventory

* docs: record Notes lifecycle and current CI audit evidence

* docs: reconcile packing chore and tally audit evidence

* docs: record shopping peer and routine editor investigation

* docs: close verified tally tools and track current UI repairs

* docs: checkpoint 26-source retest and next audit repairs

* docs: record 28-source fixes and ordinary workflow evidence

* docs: record #771 ec0d836 terminal CI (E2E passes with #775's fix; Mobile node-forge) and the #818 fast-forward

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KRUgA6hD6QgzmtpSP6TUmP

* docs: record Wishlist and Projects fixes before edits

* docs: record #771 3df0b39 terminal CI, candidates #819-#821, the unpublished-marker gap and the declutter duplicate path

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KRUgA6hD6QgzmtpSP6TUmP

* docs: preserve current audit checks and Declutter ownership

* docs: record verified repairs and next ordinary audit fixes

* docs: record #771 6b07d7f terminal CI, the #823 fast-forward (gap filled) and candidates #822/#824

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KRUgA6hD6QgzmtpSP6TUmP

* docs: allocate hardest audit fixes before implementation

* docs: record six reviewed fixes and next reference and stream repairs

* docs: record #771 c513de6 terminal CI, fast-forward to root #828 and candidates #825-#830

Path-hash checks on the newly registered spec rows; SUPPORT-53382DE490CC held
until its test is published. No closure, promotion or merge.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KRUgA6hD6QgzmtpSP6TUmP

* docs: record current composition gates and allocate cursor and coverage repairs

* docs: record school member reference before isolated repair

* docs: reconcile three published fixes and Inventory read before edits

* docs: mark malformed complete-read response before isolated repair

* docs: record #771 f931558 terminal CI, fast-forward to root #833 and candidates #831-#837

All newly registered spec IDs verified by path hash, including the
previously held SUPPORT-53382DE490CC. No closure, promotion or merge.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KRUgA6hD6QgzmtpSP6TUmP

* docs: record #771 9527339 terminal CI, read-all shape claim and #838 exception flag

Root head unchanged at a832164. #838 proposes a node-forge exception that
conflicts with the recorded owner gate decision; flagged for the owner. No
closure, promotion or merge.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KRUgA6hD6QgzmtpSP6TUmP

* docs: record published Inventory and collection guard regression

* docs: allocate Reminder changed-content receipt before edits

* docs: allocate Announcements lifetime and draft revision repair

* docs: record Reminder publication and grocery reference ownership

* docs: record Announcements repair and combined source47 regression

* docs: claim Meals receipt boundary before isolated source edits

* docs: record grocery reference guard and source48 regression

* docs: claim settle conversion before isolated edits

* Record published meal receipt guard and source49 regression evidence

* Record published query fallback fix and source50 regression

* Record query and recurrence scope before isolated repairs

* Record published calendar and query fixes with combined regression

* Record three service scopes before parallel receipt and timezone repairs

* docs: record three audited service fixes and source55 regression

* docs: mark notes concurrent edit scope before repair

* docs: record Notes CAS repair and allocate Inventory cursor audit

* docs: reconcile inherited test inventory and allocate task search audit

* docs: record independently reproduced task search collection failures

* docs: record reviewed inventory and task fixes with combined regression gates

* docs: reconcile shared helper provenance before focused service tests

* docs: record accepted calendar and chore originals before source authoring

* docs: record published calendar and chore fixes with source60 combined gates

* docs: allocate calendar bulk and update admission proofs before runtime

* docs: record accepted calendar bulk and update originals before source authoring

* docs: record source62 Calendar fixes and allocate deletion receipt proof

* docs: record accepted Calendar deletion originals before authoring

* docs: publish deletion proof and reconcile Moving dependencies

* docs: accept source63 gates and Moving original cases

* docs: record Moving publication and prepare meal UI refinement

* docs: revise meal UI scope and allocate Sports original

* docs: accept original Sports collection evidence

* docs: record meal and Sports repairs with historical chore eligibility

* docs: qualify exact historical chore calls before publication

* docs: record source66 gates and original chore isolation proof

* docs: record chore866 and reminder audit eligibility

* docs: track safe SDK fixtures and pure helper coverage

* docs(audit): record reminder and SDK fixtures with next receipt leads

* docs(audit): accept idempotency reproduction and allocate reminder receipt proof

* docs(audit): accept reminder collection proof and scoped repair plan

* docs: record verified receipt repairs and mobile audit review

* docs: record composed receipt integration verification

* docs: allocate Calendar proof and task receipt lead

* docs: record Calendar baseline and Todo environment correction

* Record Todo harness findings and Calendar claim

* Record Todo V3 static harness acceptance

* Record verified Todo V3 runtime outcome

* Record isolated completeTodo author claim

* Record Todo candidate and remaining mobile risk

* docs: record fixture evidence and rejected meal race repair

* docs: allocate parallel atomic meal auth and security work

* docs: record source72 integration and published receipt test obligation

* docs: record auth verification fixture repairs and remaining CI work

* docs: record published mobile guard and independent meal race verification

* docs: track atomic meal publication and combined regression results

* docs: record verified production test deploy and meal retry migration fixes

* docs: register atomic meal test adaptation and verified database gates

* docs: record final meal candidate and allocate receipt validation fix

* docs: allocate meal membership serialization after reproduced revocation races

* docs: record green main integration and allocate full meal regression fixtures

* docs: verify main production and allocate delegated meal security repair

* docs: record independent delegated meal SQL and source verification

* docs: record faithful AI integration and allocate atomic meal library repair

* docs: publish meal candidate evidence and allocate chore denial efficacy

* docs: classify unit baseline and allocate isolated meal fixture repairs

* docs: verify atomic meal service and record exact hosted unit failures

* docs: verify atomic library SQL and allocate accepted input lock corrections

* docs(audit): refresh final production blockers

* docs(audit): record current meal probe failure

* docs(audit): record messaging revocation fix candidate

* docs(audit): record messaging cascade race

* docs(audit): record messaging cascade serialization candidate

* docs(audit): record current merges and release gates

* docs(audit): record merges and remaining release gates

* docs(audit): refresh main release blockers

* docs(audit): record reviewed meal fix and CI gates

* docs(audit): record latest main deploy and DB probe finding

* docs(audit): record latest production mainline and checks

* docs(audit): record meal fixture repair and pending replay

* docs(audit): record remaining meal slot race gate

* docs(audit): refresh merge and production gates

* docs(audit): reconcile main and open PR status

* docs(audit): refresh release gate findings

* docs(audit): refresh production and PR gates

* docs: record current main release gate

* docs: record marketplace storage RLS finding

* docs: correct public storage HEAD semantics

* docs: record storage fix disposition

* docs: refresh PR and production gate snapshot

* docs: record all open PR merge triage

---------

Co-authored-by: Claude <noreply@anthropic.com>
@NewWorldVenture
NewWorldVenture marked this pull request as ready for review October 4, 2026 11:49
@NewWorldVenture
NewWorldVenture merged commit bc8ef5c into main Oct 4, 2026
7 checks passed

Copy link
Copy Markdown
Owner Author

Claim, per 5972876311: implementing the approved mobile compact notification queue from plan 5972218163. It goes on a new branch, claude/mobile-toast-queue-etzqg7, from main 36a516d, as a separate draft PR.

Claimed paths

  • components/ui/toast.tsx
  • lib/i18n/messages/{en-US,de-DE,es-ES,fr-FR,it-IT,nl-NL,pt-PT}.json (one key, toast.more)
  • tests/a-toast-you-can-still-reach-waits.test.ts (its React mock only; assertions unchanged)
  • new tests/toast-mobile-queue.test.ts
  • tests/e2e/form-error-semantics.spec.ts (queue cases and re-measured 360/390 stacked rows)

Ownership check

Behaviour

  • Below lg only. From lg, behaviour is identical to today.
  • One notice is visible at a time: the newest not yet dismissed.
  • Queued notices have no running timer, and each gets a full window when it shows.
  • Every Dismiss and Undo stays reachable through "+N", which expands the full stack.
  • Each message is announced once, on push, through hidden live regions.
  • No dedupe and no change to LIFETIME.

New material trade-offs, if any, come back here before I widen anything. No production, security or config changes, and no merge or deploy.


Generated by Claude Code

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.

2 participants