PR #144 (`31da384`, 2026-06-13) shipped a runtime `ReferenceError: useFocusTrap is not defined` to production because the component called the hook without importing it. The sibling `enable-no-undef-eslint-rule` convoy closes that bug class at LINT time. This PR locks the same regression at RENDER time so the bug would still fail CI even if the lint rule were dropped or disabled. ## What changes - `test/components/ScanDisambiguationDialog.test.js` — 8 tests: 1. `renders without crashing (PR #144 regression-lock)` — the direct lock-in. Mutation-tested: commenting out the `useFocusTrap` import causes all 8 tests to fail with the same `ReferenceError` shape that hit prod. 2. `returns null when disambiguation is falsy` 3. ARIA shape (`role`, `aria-modal`, `aria-labelledby`) 4. One button per candidate with accessible labels 5. `onPick` callback receives the selected candidate 6. Vision-hint branch renders when provided 7. Submitting state disables the "send for review" button 8. `onCancel` callback fires on Cancel click ## Why vitest + jsdom and not Playwright smoke | Path | Catches PR #144 | Setup | Runtime | |------|-----------------|-------|---------| | Playwright smoke | ✓ if disambiguation mounts in the smoke run | High (auth bypass, stable multi-candidate fixture image) | ~10s + browser | | Vitest render | ✓ directly — render-throw → test fail | Low | <100ms | Re-scoped the queued `scanner-disambiguation-smoke-test` task to the vitest shape because a render test catches the exact same bug class at 1/100th the cost and matches the existing `test/components/*.test.js` pattern (`Modal.test.js`, `ScannedCardItem.test.js`, etc.). A Playwright disambiguation smoke is still useful as integration-layer coverage and is queued as `scanner-disambiguation-playwright-smoke`. ## Verification - [x] `npm run test:run` — 26 files / 131 tests pass (up from 25/123) - [x] Mutation test: with `useFocusTrap` import commented out, all 8 tests fail with `ReferenceError`. With import restored, all pass. ## Test plan - [ ] CI on this PR green - [ ] Squash + merge - [ ] Smoke test post-merge: scan a card that triggers disambiguation in prod and confirm no console errors (the original PR #144 bug shape) ## Convoy doc `.convoys/scanner-disambiguation-render-test.md` documents D1 (cover the early-return branch explicitly), D2 (`fireEvent` not `userEvent`), D3 (do NOT mock `useFocusTrap` — the missing-hook is exactly what we're locking), and the two queued follow-ups (`add-component-render-smoke-pattern`, `scanner-disambiguation-playwright-smoke`). Co-authored-by: Cursor <cursoragent@cursor.com>
76 lines
14 KiB
JSON
76 lines
14 KiB
JSON
{"ts": "2026-05-23T04:18:51Z", "role": "role-conductor", "convoy": "fix-auth-bypass", "repo": "tcg-vault", "skip_flags": ["ia", "ux", "visual", "a11y", "design"], "classification": "server-only", "duration_s": 0}
|
|
{"ts": "2026-05-23T04:44:56Z", "role": "role-conductor", "convoy": "bump-next-js", "repo": "tcg-vault", "skip_flags": ["ia", "ux", "flag"], "classification": "feature", "duration_s": 0}
|
|
{"ts": "2026-05-23T04:54:59Z", "role": "role-architect", "convoy": "bump-next-js", "repo": "tcg-vault", "skip_flags": ["ia", "ux", "flag"], "classification": "feature", "duration_s": 101}
|
|
{"ts": "2026-05-23T05:37:11Z", "role": "role-architect", "convoy": "bump-next-js", "repo": "tcg-vault", "skip_flags": ["ia", "ux", "flag"], "classification": "feature", "duration_s": 311, "outcome": "scope-expanded"}
|
|
{"ts": "2026-05-23T05:50:00Z", "role": "role-architect", "convoy": "bump-next-js", "repo": "tcg-vault", "skip_flags": ["ia", "ux", "flag"], "classification": "feature", "duration_s": 357, "outcome": "eslint-v10-pivot"}
|
|
{"ts": "2026-05-23T05:55:05Z", "role": "role-implementer", "convoy": "bump-next-js", "repo": "tcg-vault", "skip_flags": [], "brief": 1, "duration_s": 180}
|
|
{"ts": "2026-05-23T06:12:57Z", "role": "role-architect", "convoy": "bump-next-js", "repo": "tcg-vault", "skip_flags": [], "duration_s": 319, "outcome": "typescript-devdep-add"}
|
|
{"ts": "2026-05-23T06:28:25Z", "role": "role-implementer", "convoy": "bump-next-js", "repo": "tcg-vault", "skip_flags": [], "brief": 1, "duration_s": 78}
|
|
{"ts": "2026-05-23T06:48:30Z", "role": "role-architect", "convoy": "bump-next-js", "repo": "tcg-vault", "skip_flags": [], "duration_s": 347, "outcome": "eslint-v9-fallback-decision-d"}
|
|
{"ts": "2026-05-23T06:51:22Z", "role": "role-implementer", "convoy": "bump-next-js", "repo": "tcg-vault", "skip_flags": [], "brief": 1, "duration_s": 50}
|
|
{"ts": "2026-05-23T07:07:35Z", "role": "role-design-system-auditor", "convoy": "bump-next-js", "repo": "tcg-vault", "skip_flags": [], "duration_s": 114, "multitask_group": "audit-bump-next-js-4"}
|
|
{"ts": "2026-05-23T07:08:15Z", "role": "role-a11y-auditor", "convoy": "bump-next-js", "repo": "tcg-vault", "skip_flags": [], "duration_s": 180, "multitask_group": "audit-bump-next-js-4"}
|
|
{"ts": "2026-05-23T07:08:55Z", "role": "role-reviewer", "convoy": "bump-next-js", "repo": "tcg-vault", "skip_flags": [], "brief": 1, "duration_s": 42, "multitask_group": "audit-bump-next-js-4"}
|
|
{"ts": "2026-05-23T07:34:56Z", "role": "role-doc-writer", "convoy": "bump-next-js", "repo": "tcg-vault", "skip_flags": [], "duration_s": 26, "outcome": "complete"}
|
|
{"ts": "2026-05-23T07:57:10Z", "role": "role-architect", "convoy": "fix-auth-bypass", "repo": "tcg-vault", "skip_flags": ["ia", "ux", "visual", "a11y", "design"], "classification": "server-only", "duration_s": 900}
|
|
{"ts": "2026-05-23T14:44:38Z", "role": "role-implementer", "convoy": "fix-auth-bypass", "repo": "tcg-vault", "skip_flags": [], "brief": 1, "duration_s": 192}
|
|
{"ts": "2026-05-23T14:52:56Z", "role": "role-implementer", "convoy": "fix-auth-bypass", "repo": "tcg-vault", "skip_flags": [], "brief": 3, "duration_s": 240}
|
|
{"ts": "2026-05-23T15:00:15Z", "role": "role-reviewer", "convoy": "fix-auth-bypass", "repo": "tcg-vault", "skip_flags": [], "brief": 3, "duration_s": 129}
|
|
{"ts": "2026-05-23T15:52:10Z", "role": "role-reviewer", "convoy": "fix-auth-bypass", "repo": "tcg-vault", "skip_flags": [], "brief": 2, "duration_s": 209}
|
|
{"ts": "2026-05-23T15:56:43Z", "role": "role-reviewer", "convoy": "fix-auth-bypass", "repo": "tcg-vault", "skip_flags": [], "brief": 4, "duration_s": 227}
|
|
{"ts": "2026-05-23T16:04:17Z", "role": "role-reviewer", "convoy": "fix-auth-bypass", "repo": "tcg-vault", "skip_flags": [], "brief": 6, "duration_s": 34}
|
|
{"ts": "2026-05-23T16:10:21Z", "role": "role-reviewer", "convoy": "fix-auth-bypass", "repo": "tcg-vault", "skip_flags": [], "brief": 5, "duration_s": 131}
|
|
{"ts": "2026-05-23T16:25:52Z", "role": "role-doc-writer", "convoy": "fix-auth-bypass", "repo": "tcg-vault", "skip_flags": [], "brief": 0, "duration_s": 724, "outcome": "complete"}
|
|
{"ts": "2026-05-23T17:31:18Z", "role": "role-architect", "convoy": "drop-public-setup", "repo": "tcg-vault", "skip_flags": [], "duration_s": 720}
|
|
{"ts": "2026-05-23T19:58:01Z", "role": "role-implementer", "convoy": "drop-public-setup", "repo": "tcg-vault", "skip_flags": [], "brief": 1, "duration_s": 327}
|
|
{"ts": "2026-05-23T20:07:35Z", "role": "role-implementer", "convoy": "drop-public-setup", "repo": "tcg-vault", "skip_flags": [], "brief": 2, "duration_s": 174}
|
|
{"ts": "2026-05-23T23:00:44Z", "role": "role-architect", "convoy": "fix-layout-default-user", "repo": "tcg-vault", "skip_flags": [], "duration_s": 1500}
|
|
{"ts": "2026-05-24T14:14:24Z", "role": "role-implementer", "convoy": "fix-layout-default-user", "repo": "tcg-vault", "skip_flags": [], "brief": 1, "duration_s": 403}
|
|
{"ts": "2026-05-24T15:05:32Z", "role": "role-implementer", "convoy": "fix-layout-default-user", "repo": "tcg-vault", "skip_flags": [], "brief": 2, "duration_s": 1800}
|
|
{"ts": "2026-05-24T19:37:56Z", "role": "role-doc-writer", "convoy": "fix-layout-default-user", "repo": "tcg-vault", "skip_flags": [], "duration_s": 242, "outcome": "complete"}
|
|
{"ts": "2026-05-24T20:26:29Z", "role": "role-implementer", "convoy": "fix-vercel-deployment-protection-in-ci", "repo": "tcg-vault", "skip_flags": [], "brief": 1, "duration_s": 101}
|
|
{"ts": "2026-05-24T21:32:57Z", "role": "role-doc-writer", "convoy": "fix-vercel-deployment-protection-in-ci", "repo": "tcg-vault", "skip_flags": [], "duration_s": 207, "outcome": "complete"}
|
|
{"ts": "2026-05-24T23:44:57Z", "role": "role-implementer", "convoy": "adopt-playwright-smoke", "repo": "tcg-vault", "skip_flags": [], "brief": 1, "duration_s": 310}
|
|
{"ts": "2026-05-25T01:25:55Z", "role": "role-implementer", "convoy": "cors-tighten", "repo": "tcg-vault", "skip_flags": [], "brief": 1, "duration_s": 900}
|
|
{"ts": "2026-05-25T03:46:40Z", "role": "role-implementer", "convoy": "add-rate-limiting", "repo": "tcg-vault", "skip_flags": [], "brief": 1, "duration_s": 600}
|
|
{"ts": "2026-05-27T12:55:50Z", "role": "role-conductor", "convoy": "scanner-audit-portfolio", "repo": "tcg-vault", "skip_flags": [], "classification": "docs-only", "duration_s": 0}
|
|
{"ts": "2026-05-27T19:09:46Z", "role": "role-reviewer", "convoy": "redesign-scanner-flow", "repo": "tcg-vault", "skip_flags": [], "duration_s": 120, "outcome": "comment-only", "multitask_group": "audit-redesign-scanner-flow-44"}
|
|
{"ts": "2026-05-27T19:09:46Z", "role": "role-design-system-auditor", "convoy": "redesign-scanner-flow", "repo": "tcg-vault", "skip_flags": [], "duration_s": 120, "outcome": "comment-only", "multitask_group": "audit-redesign-scanner-flow-44"}
|
|
{"ts": "2026-05-27T19:09:46Z", "role": "role-a11y-auditor", "convoy": "redesign-scanner-flow", "repo": "tcg-vault", "skip_flags": [], "duration_s": 120, "outcome": "comment-only", "multitask_group": "audit-redesign-scanner-flow-44"}
|
|
{"ts": "2026-06-02T05:33:45Z", "role": "role-implementer", "convoy": "collection-vocabulary", "repo": "tcg-vault", "skip_flags": [], "brief": 3, "duration_s": 120}
|
|
{"ts": "2026-06-02T05:37:04Z", "role": "role-implementer", "convoy": "seed-visual-baselines-on-linux", "repo": "tcg-vault", "skip_flags": [], "brief": 1, "duration_s": 180}
|
|
{"ts": "2026-06-02T05:37:36Z", "role": "role-implementer", "convoy": "purge-neondatabase-serverless-fully", "repo": "tcg-vault", "skip_flags": [], "brief": 1, "duration_s": 300}
|
|
{"ts": "2026-06-02T13:48:03Z", "role": "role-implementer", "convoy": "wire-migrate-into-ci", "repo": "tcg-vault", "skip_flags": [], "brief": 1, "duration_s": 300}
|
|
{"ts": "2026-06-02T13:48:43Z", "role": "role-doc-writer", "convoy": "convoy-doc-housekeeping", "repo": "tcg-vault", "skip_flags": [], "duration_s": 0, "outcome": "complete"}
|
|
{"ts": "2026-06-02T14:37:35Z", "role": "role-implementer", "convoy": "seed-visual-baselines-on-linux", "repo": "tcg-vault", "skip_flags": [], "brief": 3, "duration_s": 120}
|
|
{"ts": "2026-06-03T22:42:56Z", "role": "role-conductor", "convoy": "liquid-glass-design-tokens", "repo": "tcg-vault", "skip_flags": ["ux", "ia", "qa", "flag"], "classification": "feature", "duration_s": 180}
|
|
{"ts": "2026-06-03T23:23:20Z", "role": "role-design-system-auditor", "convoy": "liquid-glass-design-tokens", "repo": "tcg-vault", "skip_flags": [], "duration_s": 420}
|
|
{"ts": "2026-06-03T23:43:49Z", "role": "role-architect", "convoy": "liquid-glass-design-tokens", "repo": "tcg-vault", "skip_flags": [], "duration_s": 540}
|
|
{"ts": "2026-06-03T23:48:23Z", "role": "role-a11y-auditor", "convoy": "liquid-glass-design-tokens", "repo": "tcg-vault", "skip_flags": [], "duration_s": 120}
|
|
{"ts": "2026-06-03T23:48:23Z", "role": "role-implementer", "convoy": "liquid-glass-design-tokens", "repo": "tcg-vault", "skip_flags": [], "brief": 1, "duration_s": 600, "outcome": "merged"}
|
|
{"ts": "2026-06-03T23:48:23Z", "role": "role-reviewer", "convoy": "liquid-glass-design-tokens", "repo": "tcg-vault", "skip_flags": [], "brief": 1, "duration_s": 180, "outcome": "approved"}
|
|
{"ts": "2026-06-03T23:54:18Z", "role": "role-architect", "convoy": "liquid-glass-modal-and-surface-primitive", "repo": "tcg-vault", "skip_flags": [], "duration_s": 720}
|
|
{"ts": "2026-06-03T23:54:18Z", "role": "role-implementer", "convoy": "liquid-glass-modal-and-surface-primitive", "repo": "tcg-vault", "skip_flags": [], "brief": 1, "duration_s": 1800, "outcome": "merged"}
|
|
{"ts": "2026-06-03T23:58:42Z", "role": "role-architect", "convoy": "liquid-glass-form-primitives", "repo": "tcg-vault", "skip_flags": [], "duration_s": 600}
|
|
{"ts": "2026-06-03T23:58:42Z", "role": "role-implementer", "convoy": "liquid-glass-form-primitives", "repo": "tcg-vault", "skip_flags": [], "brief": 1, "duration_s": 1500, "outcome": "merged"}
|
|
{"ts": "2026-06-04T00:00:32Z", "role": "role-architect", "convoy": "liquid-glass-layout-shell", "repo": "tcg-vault", "skip_flags": [], "duration_s": 300}
|
|
{"ts": "2026-06-04T00:00:32Z", "role": "role-implementer", "convoy": "liquid-glass-layout-shell", "repo": "tcg-vault", "skip_flags": [], "brief": 1, "duration_s": 900, "outcome": "merged"}
|
|
{"ts": "2026-06-04T00:02:28Z", "role": "role-architect", "convoy": "motion-system-pass", "repo": "tcg-vault", "skip_flags": [], "duration_s": 300}
|
|
{"ts": "2026-06-04T00:02:29Z", "role": "role-implementer", "convoy": "motion-system-pass", "repo": "tcg-vault", "skip_flags": [], "brief": 1, "duration_s": 420, "outcome": "merged"}
|
|
{"ts": "2026-06-04T00:03:43Z", "role": "role-architect", "convoy": "liquid-glass-card-surfaces", "repo": "tcg-vault", "skip_flags": [], "duration_s": 240, "outcome": "architecture-only"}
|
|
{"ts": "2026-06-04T00:04:10Z", "role": "role-architect", "convoy": "liquid-glass-public-and-auth", "repo": "tcg-vault", "skip_flags": [], "duration_s": 180, "outcome": "architecture-only"}
|
|
{"ts": "2026-06-04T00:07:19Z", "role": "role-implementer", "convoy": "cleanup-legacy-design-css", "repo": "tcg-vault", "skip_flags": [], "brief": 1, "duration_s": 900, "outcome": "merged"}
|
|
{"ts": "2026-06-04T18:36:17Z", "role": "role-conductor", "convoy": "unify-glass-panel-surfaces", "repo": "tcg-vault", "skip_flags": ["ia"], "classification": "feature", "duration_s": 0}
|
|
{"ts": "2026-06-04T18:36:17Z", "role": "role-conductor", "convoy": "cleanup-card-item-list-and-share-modal-palette", "repo": "tcg-vault", "skip_flags": ["ia", "ux", "arch"], "classification": "feature", "duration_s": 0}
|
|
{"ts": "2026-06-04T18:45:32Z", "role": "role-architect", "convoy": "unify-glass-panel-surfaces", "repo": "tcg-vault", "skip_flags": [], "duration_s": 0}
|
|
{"ts": "2026-06-13T02:21:43Z", "role": "role-conductor", "convoy": "harden-visual-diff-gate", "repo": "tcg-vault", "skip_flags": [], "classification": "ci", "duration_s": 120, "outcome": "routed-to-architect"}
|
|
{"ts": "2026-06-13T02:21:43Z", "role": "role-architect", "convoy": "harden-visual-diff-gate", "repo": "tcg-vault", "skip_flags": [], "classification": "ci", "duration_s": 420, "outcome": "architecture-only"}
|
|
{"ts": "2026-06-13T02:21:43Z", "role": "role-implementer", "convoy": "harden-visual-diff-gate", "repo": "tcg-vault", "skip_flags": [], "brief": 1, "classification": "ci", "duration_s": 900, "outcome": "pr-open"}
|
|
{"ts": "2026-06-13T02:57:31Z", "role": "role-implementer", "convoy": "harden-visual-diff-gate", "repo": "tcg-vault", "skip_flags": [], "brief": 2, "classification": "ci", "duration_s": 1200, "outcome": "pr-open"}
|
|
{"ts": "2026-06-13T02:57:31Z", "role": "role-reviewer", "convoy": "harden-visual-diff-gate", "repo": "tcg-vault", "skip_flags": [], "brief": 2, "classification": "ci", "duration_s": 300, "outcome": "approved"}
|
|
{"ts": "2026-06-13T04:14:48Z", "role": "role-architect", "convoy": "rotate-default-admin", "repo": "tcg-vault", "skip_flags": [], "classification": "security", "duration_s": 240, "outcome": "option-B-chosen"}
|
|
{"ts": "2026-06-13T04:14:48Z", "role": "role-implementer", "convoy": "rotate-default-admin", "repo": "tcg-vault", "skip_flags": [], "classification": "security", "duration_s": 900, "outcome": "pr-open"}
|
|
{"ts": "2026-06-13T06:16:56Z", "role": "role-architect", "convoy": "enable-no-undef-eslint-rule", "repo": "tcg-vault", "skip_flags": [], "classification": "ci", "duration_s": 180, "outcome": "architecture-only"}
|
|
{"ts": "2026-06-13T06:16:56Z", "role": "role-implementer", "convoy": "enable-no-undef-eslint-rule", "repo": "tcg-vault", "skip_flags": [], "classification": "ci", "duration_s": 900, "outcome": "pr-open"}
|
|
{"ts": "2026-06-13T06:20:00Z", "role": "role-architect", "convoy": "scanner-disambiguation-render-test", "repo": "tcg-vault", "skip_flags": [], "classification": "ci", "duration_s": 180, "outcome": "architecture-only"}
|
|
{"ts": "2026-06-13T06:20:00Z", "role": "role-implementer", "convoy": "scanner-disambiguation-render-test", "repo": "tcg-vault", "skip_flags": [], "classification": "ci", "duration_s": 600, "outcome": "pr-open"}
|