codehere 1.0.1 → 1.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (163) hide show
  1. package/README.md +35 -193
  2. package/dist/application/use-cases/check-claimed-tests.js +81 -0
  3. package/dist/application/use-cases/detect-test-weakening.js +237 -0
  4. package/dist/application/use-cases/rerun-tests.js +110 -7
  5. package/dist/index.js +38 -1212
  6. package/dist/infrastructure/orchestration/artifacts-extractor.js +2 -323
  7. package/dist/presentation/cli/commands/check-command.js +178 -23
  8. package/dist/presentation/cli/commands/help-command.js +21 -285
  9. package/dist/ui.js +5 -236
  10. package/dist/utils/version.js +0 -6
  11. package/package.json +9 -38
  12. package/dist/application/services/verdict-service.js +0 -154
  13. package/dist/application/use-cases/assess-cross-vendor.js +0 -126
  14. package/dist/application/use-cases/build-cross-vendor-verdict.js +0 -118
  15. package/dist/application/use-cases/domain-trust.js +0 -110
  16. package/dist/application/use-cases/export-aat.js +0 -107
  17. package/dist/application/use-cases/generate-acceptance-criteria.js +0 -223
  18. package/dist/application/use-cases/generate-final-report.js +0 -259
  19. package/dist/application/use-cases/generate-narration.js +0 -130
  20. package/dist/application/use-cases/generate-thread-title.js +0 -181
  21. package/dist/application/use-cases/narrator-prompt-template.js +0 -158
  22. package/dist/application/use-cases/plan-auto-review.js +0 -57
  23. package/dist/application/use-cases/reconcile-audit.js +0 -100
  24. package/dist/application/use-cases/search-sessions.js +0 -70
  25. package/dist/application/use-cases/should-auto-review.js +0 -71
  26. package/dist/application/use-cases/thread-export.js +0 -245
  27. package/dist/application/use-cases/verify-claim.js +0 -456
  28. package/dist/application/use-cases/write-pass.js +0 -277
  29. package/dist/domain/audit.js +0 -11
  30. package/dist/domain/services/codebase-detector.js +0 -85
  31. package/dist/domain/task.js +0 -91
  32. package/dist/error-handler.js +0 -139
  33. package/dist/infrastructure/completion/bash-completion.js +0 -29
  34. package/dist/infrastructure/completion/fish-completion.js +0 -17
  35. package/dist/infrastructure/completion/zsh-completion.js +0 -25
  36. package/dist/infrastructure/config/config-manager.js +0 -174
  37. package/dist/infrastructure/config/product-context.js +0 -49
  38. package/dist/infrastructure/context/adapters/drawio-source.js +0 -157
  39. package/dist/infrastructure/context/adapters/figma-source.js +0 -198
  40. package/dist/infrastructure/context/adapters/github-issue-source.js +0 -160
  41. package/dist/infrastructure/context/adapters/linear-source.js +0 -190
  42. package/dist/infrastructure/context/context-resolver.js +0 -144
  43. package/dist/infrastructure/context/context-source.js +0 -114
  44. package/dist/infrastructure/context/github-api.js +0 -110
  45. package/dist/infrastructure/context/github-device-flow.js +0 -65
  46. package/dist/infrastructure/context/oauth-flow.js +0 -207
  47. package/dist/infrastructure/context/oauth-store.js +0 -122
  48. package/dist/infrastructure/context/registry-bootstrap.js +0 -95
  49. package/dist/infrastructure/context/write-pass-resolver.js +0 -106
  50. package/dist/infrastructure/cost/budget-config.js +0 -114
  51. package/dist/infrastructure/cost/cost-router.js +0 -97
  52. package/dist/infrastructure/cost/spend-aggregator.js +0 -222
  53. package/dist/infrastructure/cost/team-ledger.js +0 -355
  54. package/dist/infrastructure/discovery/discovery-log.js +0 -85
  55. package/dist/infrastructure/hooks/audit-tip-signature.js +0 -336
  56. package/dist/infrastructure/hooks/claude-sdk-hook.js +0 -1018
  57. package/dist/infrastructure/hooks/hook-mode.js +0 -87
  58. package/dist/infrastructure/hooks/mirror-tool-call-to-audit.js +0 -49
  59. package/dist/infrastructure/hooks/pre-prompt-scan.js +0 -122
  60. package/dist/infrastructure/hooks/session-analyzer.js +0 -177
  61. package/dist/infrastructure/observability/execution-tracer.js +0 -227
  62. package/dist/infrastructure/orchestration/agent-adapter.js +0 -13
  63. package/dist/infrastructure/orchestration/agent-installer.js +0 -101
  64. package/dist/infrastructure/orchestration/agents-config-seed.js +0 -68
  65. package/dist/infrastructure/orchestration/aider-adapter.js +0 -611
  66. package/dist/infrastructure/orchestration/cassette-recorder.js +0 -120
  67. package/dist/infrastructure/orchestration/claude-code-adapter.js +0 -592
  68. package/dist/infrastructure/orchestration/codex-adapter.js +0 -574
  69. package/dist/infrastructure/orchestration/conductor-adapter.js +0 -416
  70. package/dist/infrastructure/orchestration/context-handoff.js +0 -162
  71. package/dist/infrastructure/orchestration/control/codex-control-surface.js +0 -99
  72. package/dist/infrastructure/orchestration/control/control-surface.js +0 -15
  73. package/dist/infrastructure/orchestration/detect-ollama.js +0 -75
  74. package/dist/infrastructure/orchestration/failover-classifier.js +0 -151
  75. package/dist/infrastructure/orchestration/failover-config.js +0 -67
  76. package/dist/infrastructure/orchestration/failover-policy.js +0 -106
  77. package/dist/infrastructure/orchestration/gemini-adapter.js +0 -663
  78. package/dist/infrastructure/orchestration/generic-cli-adapter.js +0 -689
  79. package/dist/infrastructure/orchestration/graphify-context.js +0 -209
  80. package/dist/infrastructure/orchestration/memory-injector.js +0 -380
  81. package/dist/infrastructure/orchestration/mocks/mock-agent-repl.js +0 -388
  82. package/dist/infrastructure/orchestration/mocks/mock-codex-app-server.js +0 -73
  83. package/dist/infrastructure/orchestration/mocks/mock-synthesis.js +0 -83
  84. package/dist/infrastructure/orchestration/mocks/resolve-agent-spawn.js +0 -37
  85. package/dist/infrastructure/orchestration/opencode-adapter.js +0 -580
  86. package/dist/infrastructure/orchestration/progress-aggregator.js +0 -906
  87. package/dist/infrastructure/orchestration/readiness.js +0 -103
  88. package/dist/infrastructure/orchestration/session-manager.js +0 -1590
  89. package/dist/infrastructure/orchestration/session-persistence.js +0 -394
  90. package/dist/infrastructure/orchestration/thread-key-store.js +0 -127
  91. package/dist/infrastructure/orchestration/write-pass-store.js +0 -38
  92. package/dist/infrastructure/protocols/cloud-sync.js +0 -207
  93. package/dist/infrastructure/protocols/mcp-helpers.js +0 -25
  94. package/dist/infrastructure/protocols/mcp-server-impl.js +0 -3935
  95. package/dist/infrastructure/protocols/mcp-tools.js +0 -401
  96. package/dist/infrastructure/protocols/probe-port.js +0 -36
  97. package/dist/infrastructure/protocols/thread-phase.js +0 -105
  98. package/dist/infrastructure/protocols/ui/app-script-artifacts.js +0 -121
  99. package/dist/infrastructure/protocols/ui/app-script-audit-footer.js +0 -44
  100. package/dist/infrastructure/protocols/ui/app-script-audit-verify.js +0 -56
  101. package/dist/infrastructure/protocols/ui/app-script-composer-cost.js +0 -588
  102. package/dist/infrastructure/protocols/ui/app-script-context-sources.js +0 -168
  103. package/dist/infrastructure/protocols/ui/app-script-conversation.js +0 -67
  104. package/dist/infrastructure/protocols/ui/app-script-drawer.js +0 -115
  105. package/dist/infrastructure/protocols/ui/app-script-failover.js +0 -133
  106. package/dist/infrastructure/protocols/ui/app-script-github.js +0 -113
  107. package/dist/infrastructure/protocols/ui/app-script-observer.js +0 -104
  108. package/dist/infrastructure/protocols/ui/app-script-onboarding.js +0 -80
  109. package/dist/infrastructure/protocols/ui/app-script-overlay.js +0 -203
  110. package/dist/infrastructure/protocols/ui/app-script-recovery.js +0 -136
  111. package/dist/infrastructure/protocols/ui/app-script-render.js +0 -166
  112. package/dist/infrastructure/protocols/ui/app-script-share.js +0 -78
  113. package/dist/infrastructure/protocols/ui/app-script-sidebar.js +0 -194
  114. package/dist/infrastructure/protocols/ui/app-script-tour.js +0 -78
  115. package/dist/infrastructure/protocols/ui/app-script-wpass.js +0 -312
  116. package/dist/infrastructure/protocols/ui/app-script.js +0 -2998
  117. package/dist/infrastructure/protocols/ui/font-faces.js +0 -7
  118. package/dist/infrastructure/protocols/ui/styles.js +0 -2147
  119. package/dist/infrastructure/protocols/ui/tokens.js +0 -96
  120. package/dist/infrastructure/protocols/ui-page-v2.js +0 -320
  121. package/dist/infrastructure/protocols/verify-page.js +0 -1677
  122. package/dist/infrastructure/security/file-collection.js +0 -118
  123. package/dist/infrastructure/security/fix-code.js +0 -144
  124. package/dist/infrastructure/security/fix-secret.js +0 -89
  125. package/dist/infrastructure/security/permissions-config.js +0 -235
  126. package/dist/infrastructure/security/scan-types.js +0 -10
  127. package/dist/infrastructure/security/ship-scan.js +0 -195
  128. package/dist/infrastructure/security/trust-scorer.js +0 -724
  129. package/dist/infrastructure/security/vibe-checks.js +0 -99
  130. package/dist/infrastructure/storage/atomic-write.js +0 -86
  131. package/dist/infrastructure/storage/cross-tool-agents.js +0 -268
  132. package/dist/infrastructure/storage/project-thread-migration.js +0 -237
  133. package/dist/infrastructure/storage/project-thread-mirror.js +0 -671
  134. package/dist/infrastructure/storage/project-thread-reader.js +0 -409
  135. package/dist/infrastructure/storage/project-thread-title-backfill.js +0 -214
  136. package/dist/monitoring.js +0 -212
  137. package/dist/presentation/cli/commands/audit-command.js +0 -201
  138. package/dist/presentation/cli/commands/budget-command.js +0 -79
  139. package/dist/presentation/cli/commands/config-command.js +0 -125
  140. package/dist/presentation/cli/commands/context-source-command.js +0 -217
  141. package/dist/presentation/cli/commands/hook-command.js +0 -265
  142. package/dist/presentation/cli/commands/init-command.js +0 -590
  143. package/dist/presentation/cli/commands/memory-command.js +0 -1031
  144. package/dist/presentation/cli/commands/narrator-command.js +0 -101
  145. package/dist/presentation/cli/commands/permission-command.js +0 -105
  146. package/dist/presentation/cli/commands/protocols-builder.js +0 -254
  147. package/dist/presentation/cli/commands/protocols-command.js +0 -84
  148. package/dist/presentation/cli/commands/repo-map-builder.js +0 -247
  149. package/dist/presentation/cli/commands/report-command.js +0 -403
  150. package/dist/presentation/cli/commands/scan-command.js +0 -52
  151. package/dist/presentation/cli/commands/scan-tool-command.js +0 -110
  152. package/dist/presentation/cli/commands/search-command.js +0 -51
  153. package/dist/presentation/cli/commands/setup-command.js +0 -330
  154. package/dist/presentation/cli/commands/status-command.js +0 -614
  155. package/dist/presentation/cli/commands/team-command.js +0 -256
  156. package/dist/presentation/cli/commands/thread-command.js +0 -370
  157. package/dist/presentation/cli/commands/trust-command.js +0 -328
  158. package/dist/presentation/cli/commands/verify-command.js +0 -298
  159. package/dist/presentation/cli/error-display.js +0 -328
  160. package/dist/presentation/cli/repl.js +0 -805
  161. package/dist/presentation/cli/task-deliverable.js +0 -153
  162. package/dist/testing/cassette.js +0 -125
  163. package/dist/testing/replay-adapter.js +0 -147
@@ -1,1677 +0,0 @@
1
- // Copyright 2026
2
- /**
3
- * /verify · verify-at-the-surface dashboard (the Claude-team way).
4
- *
5
- * Modeled on Anthropic's how-we-claude-code/phase-3-verify harness, adapted to
6
- * codehere's reality: the product isn't React components, it's a server-rendered
7
- * page. So the "unit × fixture" matrix becomes "honesty-invariant × seeded
8
- * session", and the surface we observe is the REAL product page (`/`) loaded in
9
- * a same-origin iframe — never a clone. We drive it via the real showD(), read
10
- * the data-verify-* DOM contract back, and compute PASS / FAIL / BLOCKED.
11
- *
12
- * - window.__verify.manifest() — discover every invariant × fixture
13
- * - window.__verify.runAll() — drive the whole matrix, return structured results
14
- * - window.__verify.current() — the last single result (what replay is showing)
15
- * Agent, human (Run all), and CI (scripts/verify.mjs) all read this one handle.
16
- *
17
- * The probe fixture (demo-probe-lie-v1) is DESIGNED to fail H5: its outcome
18
- * headline claims 3 files but renders 1. A green there means the harness is
19
- * blind, so CI treats a non-failing probe as a hard failure.
20
- *
21
- * Replay mode (?mode=replay) drives the real iframe in slow motion with a
22
- * highlight ring + caption (the visibleAct pattern) and can record one .webm
23
- * per fixture in-browser, or be driven headless by scripts/record-replay.mjs.
24
- */
25
- import { violatesGitInvariant } from '../../application/use-cases/verify-claim.js';
26
- import { DESIGN_TOKENS } from './ui/tokens.js';
27
- import { FONT_FACES } from './ui/font-faces.js';
28
- export const VERIFY_HTML = `<!doctype html>
29
- <html lang="en">
30
- <head>
31
- <meta charset="utf-8" />
32
- <meta name="viewport" content="width=device-width, initial-scale=1" />
33
- <link rel="icon" type="image/svg+xml" href="/icon.svg" />
34
- <title>codehere · /verify</title>
35
- <style>${FONT_FACES}${DESIGN_TOKENS}
36
- /* /verify used to declare its own :root here: cool-gray surfaces from the
37
- pre-v1 palette, Inter as the voice face, and macOS traffic lights for the
38
- verdict colours (--allow was var(--verified)). That made three different "verified"
39
- greens in one product, on a page linked directly from codehere start.
40
- It now takes the same tokens and the same self-hosted faces as the
41
- dashboard. DESIGN.md §4.5 always said it should. */
42
- *{box-sizing:border-box}
43
- body{margin:0;background:var(--bg);color:var(--fg);font-family:var(--sans);font-size:13px;line-height:1.55;-webkit-font-smoothing:antialiased;
44
- background-image:radial-gradient(circle,rgba(74,144,226,.045) 1px,transparent 1px);background-size:24px 24px;}
45
- a{color:inherit}
46
- header.top{display:flex;align-items:center;gap:14px;padding:10px 20px;border-bottom:1px solid var(--border-soft);background:rgba(0,0,0,.55);backdrop-filter:blur(8px);position:sticky;top:0;z-index:5;flex-wrap:wrap}
47
- .logo{font-size:14.5px;font-weight:700;display:inline-flex;align-items:center;gap:7px;letter-spacing:-.01em}
48
- /* S16 brand-sync: gradient name only, no dot — the landing nav wordmark carries none */
49
- .logo .name{background:linear-gradient(135deg,#ff9a76 0%,#4a90e2 100%);-webkit-background-clip:text;background-clip:text;-webkit-text-fill-color:transparent;color:transparent}
50
- .route{color:var(--fg-40);font-family:var(--mono);font-size:12px}
51
- .pill{font-size:10.5px;padding:3px 8px;border:1px solid var(--border);border-radius:999px;color:var(--fg-50)}
52
- .pill.local{border-color:var(--border);color:var(--fg-70)}
53
- .spacer{flex:1}
54
- .hint{color:var(--fg-40);font-size:12px}
55
- .hint code{font-family:var(--mono);color:var(--info)}
56
- .wrap{max-width:1180px;margin:0 auto;padding:22px 20px 80px}
57
- .actions{display:flex;align-items:center;gap:12px;margin:18px 0 8px;flex-wrap:wrap}
58
- /* S17 · same token system as the product page: ONE flat accent fill per
59
- action row (no gradient, no glow), calm hover, visible focus ring.
60
- Buttons don't inherit type by default — pull every control into the system. */
61
- button,select,input{font:inherit}
62
- button.run{background:var(--accent);color:#1a0f08;border:0;font-weight:600;font-size:13px;padding:9px 18px;border-radius:var(--radius);cursor:pointer;transition:filter var(--dur) var(--ease)}
63
- button.run:hover{filter:brightness(1.06)}
64
- button.run:disabled{opacity:.6;cursor:default;filter:none}
65
- button.run:focus-visible,.linkbtn:focus-visible,.cell:focus-visible,.steprow:focus-visible{outline:2px solid var(--accent);outline-offset:2px}
66
- .linkbtn{color:var(--fg-70);text-decoration:none;border:1px solid var(--border);padding:8px 14px;border-radius:var(--radius);font-size:12px;cursor:pointer;background:transparent}
67
- .linkbtn:hover{border-color:var(--accent);color:var(--fg)}
68
- .tally{display:inline-flex;gap:14px;align-items:center;font-family:var(--mono);font-size:13px;margin-left:6px}
69
- .tally .p{color:var(--verified)} .tally .f{color:var(--caught)} .tally .b{color:var(--fg-40)} .tally .t{color:var(--fg-50)}
70
- .verdict-line{font-size:12px;margin:4px 0 0}
71
- .verdict-line.green[data-evidence='measured']{color:var(--verified)} .verdict-line.red{color:var(--caught)}
72
- .legend{display:flex;gap:18px;margin:8px 0 0;font-size:12px;color:var(--fg-50);flex-wrap:wrap}
73
- .legend b{font-family:var(--mono)}
74
- h2.section{font-size:12px;text-transform:uppercase;letter-spacing:.12em;color:var(--fg-40);margin:26px 0 10px;font-weight:600}
75
- .verifiers{display:flex;gap:8px;flex-wrap:wrap;margin-bottom:6px}
76
- .vchip{border:1px solid var(--border);border-radius:999px;padding:4px 11px;font-size:12px;color:var(--fg-70)}
77
- .vchip code{font-family:var(--mono);color:var(--accent)}
78
- table.matrix{width:100%;border-collapse:collapse;font-size:12px;border:1px solid var(--border-soft);border-radius:var(--radius);overflow:hidden}
79
- table.matrix th,table.matrix td{padding:9px 11px;text-align:left;border-bottom:1px solid var(--border-soft)}
80
- table.matrix thead th{font-size:10.5px;text-transform:uppercase;letter-spacing:.08em;color:var(--fg-40);background:var(--bg-1);font-weight:600;vertical-align:bottom}
81
- table.matrix tbody tr:hover{background:rgba(255,255,255,.02)}
82
- .unit-id{font-family:var(--mono);color:var(--fg)}
83
- .unit-desc{color:var(--fg-50);font-size:12px}
84
- .needs{font-family:var(--mono);color:var(--fg-30);font-size:12px}
85
- .cell{display:inline-flex;align-items:center;justify-content:center;width:26px;height:22px;border-radius:5px;font-size:12px;cursor:pointer;border:0;background:transparent;color:var(--fg-30);font-family:var(--mono)}
86
- .cell.pass{background:rgba(127,195,154,.12);color:var(--verified)}
87
- .cell.fail{background:rgba(226,130,127,.16);color:var(--caught);box-shadow:0 0 0 1px rgba(226,130,127,.5)}
88
- .cell.fail.expected{animation:flag 1.6s infinite}
89
- .cell.blocked{background:rgba(255,255,255,.05);color:var(--fg-40)}
90
- @keyframes flag{0%,100%{box-shadow:0 0 0 1px rgba(226,130,127,.5)}50%{box-shadow:0 0 0 1px rgba(226,130,127,1),0 0 12px rgba(226,130,127,.5)}}
91
- .fixhdr{font-family:var(--mono);font-size:10.5px;color:var(--fg-50);writing-mode:vertical-rl;transform:rotate(180deg);white-space:nowrap;height:96px}
92
- .fixhdr.probe{color:var(--caught)}
93
- .probe-row td{background:rgba(226,130,127,.04)}
94
- .probe-tag{font-size:10.5px;color:var(--caught);border:1px solid rgba(226,130,127,.4);border-radius:4px;padding:1px 5px;margin-left:8px;font-family:var(--mono)}
95
- .note{color:var(--fg-40);font-size:12px;margin-top:8px}
96
- .note code{font-family:var(--mono);color:var(--info)}
97
- /* replay */
98
- .grid2{display:grid;grid-template-columns:1.4fr 1fr;gap:18px;margin-top:6px}
99
- .stage-wrap{border:1px solid var(--border);border-radius:10px;overflow:hidden;background:var(--bg-1);position:relative}
100
- .stage-wrap.v-pass{box-shadow:0 0 0 1px rgba(127,195,154,.4)}
101
- .stage-wrap.v-fail{box-shadow:0 0 0 1px rgba(226,130,127,.55)}
102
- .stage-wrap.v-blocked{box-shadow:0 0 0 1px rgba(255,255,255,.12)}
103
- .titlebar{display:flex;align-items:center;gap:7px;padding:8px 12px;background:var(--bg-2);border-bottom:1px solid var(--border-soft)}
104
- .tl-dot{width:11px;height:11px;border-radius:50%}.tl-dot.r{background:var(--caught)}.tl-dot.y{background:var(--warn)}.tl-dot.g{background:var(--verified)}
105
- .tl-title{margin-left:8px;font-size:12px;color:var(--fg-70)}.tl-title .dim{color:var(--fg-30);font-family:var(--mono)}
106
- iframe#surface{width:100%;height:560px;border:0;display:block;background:var(--bg)}
107
- .checklist{position:absolute;top:46px;right:14px;width:226px;background:rgba(0,0,0,.82);border:1px solid var(--border);border-radius:10px;padding:10px 12px;z-index:52;font-size:12px;backdrop-filter:blur(6px);opacity:0;transition:opacity .2s}
108
- .checklist.on{opacity:1}
109
- .checklist .cl-h{font-family:var(--mono);font-size:10.5px;letter-spacing:.1em;text-transform:uppercase;color:var(--fg-40);margin-bottom:7px}
110
- .checklist .cl-row{display:flex;align-items:flex-start;gap:7px;padding:3px 0;color:var(--fg-50);transition:color .2s}
111
- .checklist .cl-row.run{color:var(--fg)}
112
- .checklist .cl-row .cl-id{font-family:var(--mono);font-weight:700;width:22px}
113
- .checklist .cl-row .cl-st{width:14px;text-align:center}
114
- .checklist .cl-row.pass .cl-id{color:var(--verified)} .checklist .cl-row.fail .cl-id{color:var(--caught)} .checklist .cl-row.blocked .cl-id{color:var(--fg-40)}
115
- .ring{position:fixed;border:2px solid var(--accent);border-radius:8px;box-shadow:0 0 0 4px rgba(255,154,118,.25),0 0 18px rgba(255,154,118,.6);pointer-events:none;z-index:50;animation:ringpulse 1s infinite;transition:all .25s ease}
116
- @keyframes ringpulse{0%,100%{opacity:1}50%{opacity:.55}}
117
- .caption{position:absolute;bottom:14px;left:50%;transform:translateX(-50%);background:rgba(0,0,0,.88);border:1px solid var(--border);border-radius:999px;padding:7px 16px;font-size:12px;color:var(--fg);font-family:var(--mono);white-space:nowrap;z-index:51;opacity:0;transition:opacity .2s}
118
- .caption.on{opacity:1}
119
- .framestatus{padding:10px 14px;border-top:1px solid var(--border-soft);font-size:12px;display:flex;align-items:center;gap:12px;flex-wrap:wrap}
120
- .framestatus .badge{font-family:var(--mono);font-weight:700;padding:2px 9px;border-radius:5px}
121
- .badge.pass{color:var(--verified);background:rgba(127,195,154,.12)} .badge.fail{color:var(--caught);background:rgba(226,130,127,.14)} .badge.blocked{color:var(--fg-40);background:rgba(255,255,255,.05)}
122
- .framestatus .issues{color:var(--caught);font-size:12px}
123
- .framestatus .issues.blk{color:var(--fg-40)}
124
- .replay-side h3{font-size:12px;color:var(--fg-70);margin:0 0 10px}
125
- .ctrlrow{display:flex;gap:8px;align-items:center;margin-bottom:12px;flex-wrap:wrap}
126
- .progress{height:5px;background:var(--bg-2);border-radius:3px;overflow:hidden;margin:8px 0 14px}
127
- .progress>div{height:100%;width:0;background:var(--accent);transition:width var(--dur-max) var(--ease)}
128
- .steplist{font-size:12px}
129
- .steprow{display:flex;align-items:center;gap:8px;padding:5px 7px;border-radius:6px;cursor:pointer}
130
- .steprow.active{background:var(--accent-wash)}
131
- .steprow .st{width:16px;text-align:center}
132
- .steprow .nm{font-family:var(--mono);font-size:12px;color:var(--fg-70)}
133
- .steprow.fail .nm{color:var(--caught)}
134
- .clipnote{margin-top:14px;font-size:12px;color:var(--fg-40)}
135
- .clipnote code{font-family:var(--mono);color:var(--info)}
136
- details.manifest{margin-top:26px;border:1px solid var(--border-soft);border-radius:var(--radius);padding:10px 14px}
137
- details.manifest summary{cursor:pointer;color:var(--fg-50);font-size:12px}
138
- details.manifest pre{font-family:var(--mono);font-size:12px;color:var(--fg-50);overflow:auto;margin:10px 0 0;max-height:340px}
139
- .hidden{display:none !important}
140
- /* S17 · prefers-reduced-motion: ALL motion collapses, information stays
141
- (same rule the product page ships). */
142
- @media (prefers-reduced-motion: reduce){
143
- *,*::before,*::after{animation-duration:.01ms !important;animation-iteration-count:1 !important;transition-duration:.01ms !important;scroll-behavior:auto !important}
144
- }
145
- </style>
146
- </head>
147
- <body data-verify-page="dashboard">
148
- <header class="top">
149
- <span class="logo"><span class="name">codehere</span></span>
150
- <span class="route">/verify</span>
151
- <span class="pill local">&#9679; local &middot; zero telemetry</span>
152
- <span class="spacer"></span>
153
- <span class="hint">agent handle: <code>window.__verify</code> &mdash; <code>__verify.manifest()</code> &middot; <code>await __verify.runAll()</code></span>
154
- </header>
155
-
156
- <div class="wrap">
157
- <div class="actions">
158
- <button class="run" id="run-all">&#9654; Run all</button>
159
- <button class="linkbtn" id="toggle-replay">&#9654; Replay all (slow-mo)</button>
160
- <span class="tally" id="tally"><span class="t">not run yet &mdash; click Run all</span></span>
161
- </div>
162
- <div class="verdict-line" id="verdict-line"></div>
163
- <div class="legend">
164
- <span><b style="color:var(--verified)">PASS</b> observed &amp; honest</span>
165
- <span><b style="color:var(--caught)">FAIL</b> observed &amp; lying</span>
166
- <span><b style="color:var(--fg-40)">BLOCKED</b> 0 units to observe &mdash; never a silent green</span>
167
- <span><b>probe</b> designed-to-fail self-test</span>
168
- </div>
169
-
170
- <h2 class="section">Pluggable verifiers</h2>
171
- <div class="verifiers">
172
- <span class="vchip"><code>contract</code> &mdash; reads data-verify-* off the rendered surface</span>
173
- <span class="vchip"><code>invariants</code> &mdash; runs each honesty predicate against the DOM</span>
174
- <span class="vchip"><code>counts</code> &mdash; claimed numbers must equal rendered elements</span>
175
- </div>
176
-
177
- <h2 class="section">Matrix &middot; honesty invariant &times; seeded session</h2>
178
- <table class="matrix" id="matrix"><thead></thead><tbody></tbody></table>
179
- <p class="note" id="probe-note"></p>
180
-
181
- <h2 class="section">Feature coverage &middot; v0 &#8250; v1 MVP &mdash; does each PRODUCT_STATE claim actually render?</h2>
182
- <div class="actions">
183
- <button class="run" id="run-cov">&#9654; Run coverage</button>
184
- <span class="tally" id="cov-tally"><span class="t">not run yet</span></span>
185
- </div>
186
- <div class="verdict-line" id="cov-verdict"></div>
187
- <div class="legend">
188
- <span><b style="color:var(--verified)">OBSERVED</b> claimed shipped &amp; rendered</span>
189
- <span><b style="color:var(--caught)">MISSING</b> claimed shipped, NOT rendered (doc lies)</span>
190
- <span><b style="color:var(--warn)">PARTIAL-GAP</b> ◐ honest, not yet visible</span>
191
- <span><b style="color:var(--fg-40)">SKIP</b> roadmap / future (correctly absent)</span>
192
- </div>
193
- <table class="matrix" id="cov-matrix"><thead><tr><th style="width:52%">Feature (PRODUCT_STATE id)</th><th class="needs" style="width:12%">tier</th><th class="needs" style="width:12%">surface</th><th>state</th></tr></thead><tbody></tbody></table>
194
-
195
- <div id="replay" class="hidden">
196
- <h2 class="section">Replay &middot; drive the real UI in slow motion</h2>
197
- <div class="grid2">
198
- <div class="stage-wrap" id="stage-wrap">
199
- <div class="titlebar">
200
- <span class="tl-dot r"></span><span class="tl-dot y"></span><span class="tl-dot g"></span>
201
- <span class="tl-title" id="stage-title">feature &middot; <strong>session thread</strong> <span class="dim">/ &mdash;</span></span>
202
- </div>
203
- <iframe id="surface" title="codehere product surface" src="/"></iframe>
204
- <div class="checklist" id="checklist"></div>
205
- <div class="caption" id="caption"></div>
206
- <div class="framestatus" id="framestatus"><span class="sub" style="color:var(--fg-40)">press Play</span></div>
207
- </div>
208
- <div class="replay-side">
209
- <h3>Replay controls</h3>
210
- <div class="ctrlrow">
211
- <button class="linkbtn" id="play">&#9654; Play</button>
212
- <button class="linkbtn" id="skip">Skip &#8250;</button>
213
- <button class="linkbtn" id="record">&#9679; Record clips</button>
214
- <span class="hint" id="dwell-label">dwell 1500ms</span>
215
- </div>
216
- <div class="progress"><div id="prog"></div></div>
217
- <div class="steplist" id="steplist"></div>
218
- <div class="clipnote">Each fixture gets its own <code>.webm</code> (in-browser, pick this tab). Headless: <code>node scripts/record-replay.mjs</code> hands you one webm, no setup.</div>
219
- </div>
220
- </div>
221
- </div>
222
-
223
- <details class="manifest">
224
- <summary>Raw manifest (what an agent / CI sees &mdash; same truth as this page)</summary>
225
- <pre id="manifest-json"></pre>
226
- </details>
227
- </div>
228
-
229
- <script>
230
- (function(){
231
- "use strict";
232
-
233
- /* -------- fixtures: the seeded sessions (the product's real surfaces) --------
234
- S20 · the two lens columns re-observe the FAILED fixture with the Review /
235
- Status lens active (sid = the session, lens = applied via the page's own
236
- applyLens before reading): every invariant — H9/H13/H14/H15 included —
237
- must hold under each lens. Honesty must survive the recomposition. */
238
- var FIXTURES = [
239
- { id:"demo-rate-limit-shipped-v0.9", label:"rate-limit-shipped" },
240
- { id:"demo-recovery-test-failed-v0.9", label:"recovery-failed" },
241
- { id:"demo-crash-orphaned-mid-write-v0.9",label:"crash-orphaned" },
242
- { id:"demo-hook-block-bulk-rm-v0.9", label:"hook-block-rm" },
243
- { id:"demo-stalled-opencode-hung-v1", label:"stalled-hung" },
244
- { id:"demo-non-git-honest-v1", label:"non-git-honest" },
245
- { id:"demo-running-streaming-v0.9", label:"running-stream" },
246
- { id:"lens-review-failed", sid:"demo-recovery-test-failed-v0.9", lens:"review", label:"review lens \\u00b7 failed" },
247
- { id:"lens-status-failed", sid:"demo-recovery-test-failed-v0.9", lens:"status", label:"status lens \\u00b7 failed" },
248
- { id:"demo-probe-lie-v1", label:"probe-lie", probe:true, expectFail:"H5" }
249
- ];
250
-
251
- /* -------- invariants: the honesty contracts (the "units") --------
252
- needs(s) -> can this invariant observe anything here? (else BLOCKED)
253
- check(s) -> [] when honest, or violation strings when lying (FAIL) */
254
- /* LOW #23 (atomic audit) · there is no H4 — it was never built; numbering
255
- jumps H3->H5 and is kept STABLE because H-ids are referenced by docs,
256
- fixtures (expectFail) and evidence bundles. Do not renumber. */
257
- var INVARIANTS = [
258
- { id:"H1", title:"no-verified-without-git", needs:"verifier",
259
- desc:"verdict is not 'verified' when git is untracked",
260
- can:function(s){ return s.verifiers.length>0; },
261
- /* R11/M2 · the rule itself is the VerdictService's, interpolated at
262
- build time — this harness EXECUTES verify-claim's own predicate
263
- instead of re-encoding it, so engine and harness can't drift. */
264
- check:function(s){ var violates=(${violatesGitInvariant.toString()}); return s.verifiers.filter(function(v){ return violates(v.verdict, v.gittracked!=="false"); }).map(function(){ return "verifier verdict=verified while git untracked (overclaim)"; }); } },
265
- { id:"H2", title:"say-never-bare", needs:"say",
266
- desc:"every reasoning bubble has real words, not the bare label",
267
- can:function(s){ return s.says.length>0; },
268
- check:function(s){ return s.says.filter(function(x){ return x.len===0 || x.text.toLowerCase()==="say"; }).map(function(){ return "bare 'say' bubble with no agent words"; }); } },
269
- { id:"H3", title:"no-empty-outcome", needs:"terminal outcome",
270
- desc:"a finished outcome panel renders observable content, not an empty box",
271
- can:function(s){ return s.hasOutcome && s.terminal; },
272
- check:function(s){ return s.outcomeItems>0 ? [] : ["terminal Outcome panel rendered 0 items (empty container)"]; } },
273
- { id:"H5", title:"counts-consistent", needs:"outcome+files",
274
- desc:"the 'N files changed' headline equals the rendered file rows",
275
- can:function(s){ return s.hasOutcome && s.filesClaimed>0 && s.hasFiletree; },
276
- check:function(s){ return s.filesClaimed===s.filetreeRendered ? [] : ["claims "+s.filesClaimed+" files changed but renders "+s.filetreeRendered+" file row(s)"]; } },
277
- { id:"H6", title:"fail-has-honest-reason", needs:"terminal-fail",
278
- desc:"a failed / killed / crashed run shows a plain reason, never a bare dot",
279
- can:function(s){ return s.terminalFail; },
280
- check:function(s){ return s.hasExplanation ? [] : ["failed session shows no recovery / crash / stall explanation"]; } },
281
- { id:"H8", title:"observer-honest", needs:"observer rail",
282
- desc:"the observer never shows a stale session as live, never renders a blank box, never seeds fake rows (Thariq loop-2 design B)",
283
- can:function(s){ return !!s.observer; },
284
- check:function(s){
285
- var o=s.observer, out=[];
286
- if(o.mode==='empty'){
287
- if(o.rowStates.length>0) out.push("empty observer rendered "+o.rowStates.length+" row(s) · should seed NO fake rows");
288
- if(o.teachLen<20) out.push("empty observer is a blank box · it must teach the 60-second path");
289
- } else if(o.mode==='active'){
290
- var liveRows=o.rowStates.filter(function(st){ return st==='live'; }).length;
291
- if(o.liveClaimed!==liveRows) out.push("header claims "+o.liveClaimed+" live but "+liveRows+" row(s) are marked live (stale-as-live)");
292
- if(o.textLen<10) out.push("active observer rendered with no content (empty container)");
293
- }
294
- return out;
295
- } },
296
- { id:"H12", title:"follow-up-never-lies", needs:"detail composer",
297
- desc:"the follow-up box is enabled only when the server can deliver the message; a disabled box carries a plain reason \u00b7 never an invitation to send into a void (S1)",
298
- can:function(s){ return !!s.followUp && !!s.followUp.state; },
299
- check:function(s){
300
- var f=s.followUp, out=[];
301
- if(f.state==='disabled-honest' && f.placeholderLen<20) out.push("follow-up disabled but the reason placeholder is missing/too thin");
302
- if(f.state==='disabled-honest' && !f.disabled) out.push("composer claims disabled-honest but the box is still enabled (the invitation lie)");
303
- if(f.state==='enabled' && f.disabled) out.push("composer stamped enabled but the box is disabled (silent dead composer)");
304
- return out;
305
- } },
306
- { id:"H11", title:"running-task-stoppable", needs:"a running session",
307
- desc:"a running task shows a Stop control on its detail · you can stop an agent the moment you stop trusting it (S2)",
308
- can:function(s){ return s.status==='running'; },
309
- check:function(s){ return s.hasStopControl ? [] : ["running task renders no Stop control on the detail (unstoppable runaway)"]; } },
310
- { id:"H10", title:"cost-estimate-honest", needs:"composer cost chip",
311
- desc:"the cost chip is computed (free / recent-real / typical), labeled as what it is, and its tooltip never claims a computation that didn't happen (S7)",
312
- can:function(s){ return s.costEst && s.costEst.length>0; },
313
- check:function(s){
314
- var out=[];
315
- s.costEst.forEach(function(c){
316
- if(!c.mode) out.push("cost chip rendered with no computed mode (estimator never ran) \\u00b7 text: '"+c.text+"'");
317
- else if(c.mode==='free' && c.text.indexOf('$0')===-1) out.push("free-mode chip does not say $0");
318
- else if(c.mode==='typical' && c.text.indexOf('typical')===-1) out.push("flat-fallback chip not labeled 'typical' \\u00b7 a flat figure dressed as a computation");
319
- else if(c.mode==='typical' && /prompt length/i.test(c.title)) out.push("typical-mode tooltip claims prompt-length computation that did not happen");
320
- else if(c.mode==='recent' && c.text.indexOf('~$')===-1) out.push("recent-mode chip missing the computed ~$ figure");
321
- });
322
- return out;
323
- } },
324
- { id:"H9", title:"no-green-on-failed", needs:"verifier on a failed run",
325
- desc:"a failed / killed / crashed task never wears the green 'verified' block · an honest report about a failure is not a success (S5)",
326
- can:function(s){ return s.terminalFail && s.verifiers.length>0; },
327
- check:function(s){ return s.verifiers.filter(function(v){ return v.verdict==="verified"; }).map(function(){ return "status=failed task renders a green verdict=verified block (fake-green on the proof surface)"; }); } },
328
- { id:"H13", title:"hero-numbers-honest", needs:"home hero",
329
- desc:"the home hero's claimed counts (agents/verified/caught/running) equal the counts derivable from the task list the page itself holds; the first-run hero never hides real work (S12)",
330
- can:function(s){ return !!(s.hero && s.hero.expected); },
331
- check:function(s){
332
- var h=s.hero, e=h.expected, out=[];
333
- if(h.mode==="first-run"){
334
- if(e.today>0) out.push("hero shows the first-run story while "+e.today+" task(s) started today (real work hidden)");
335
- return out;
336
- }
337
- ["agents","verified","caught","stopped","running"].forEach(function(k){
338
- if(h[k]!==e[k]) out.push("hero claims "+k+"="+h[k]+" but the task list derives "+e[k]);
339
- });
340
- return out;
341
- } },
342
- { id:"H14", title:"decisions-honest", needs:"decision-queue state",
343
- desc:"the NEEDS-YOUR-DECISION queue's claimed N equals the count derivable from the page's own state (pending write-passes + verifier-broken verdicts today + failed runs with retry available today); zero derivable decisions means the section is absent entirely (S13)",
344
- can:function(s){ return !!(s.decisions && s.decisions.expected!==null); },
345
- check:function(s){
346
- var d=s.decisions, out=[];
347
- if(d.expected===0){
348
- if(d.present) out.push("decision queue renders ("+d.rows+" row(s), claims N="+d.n+") while 0 decisions are derivable \\u00b7 the section must be absent when nothing needs you");
349
- return out;
350
- }
351
- if(!d.present){ out.push(d.expected+" decision(s) derivable from page state but the queue is absent (real decisions hidden)"); return out; }
352
- if(d.n!==d.expected) out.push("queue claims N="+d.n+" but the page state derives "+d.expected);
353
- if(d.rows!==d.n) out.push("queue claims N="+d.n+" but renders "+d.rows+" row(s)");
354
- return out;
355
- } },
356
- { id:"H15", title:"glyph-discipline", needs:"rendered surface",
357
- desc:"no emoji/pictograph codepoint anywhere in the rendered CHROME, observed in the default AND expanded history/practice states \\u00b7 status and meaning are carried by the sanctioned geometric glyph set (\\u2713 \\u2715 \\u25CF \\u25CB \\u2022 \\u2298 \\u203A), words and color, never emoji-as-icon; user/run content ([data-user-content]) is the user's own text, not chrome, and is excluded (S19)",
358
- can:function(s){ return !!(s.glyphScan && s.glyphScan.scanned); },
359
- check:function(s){ return s.glyphScan.hits; } },
360
- { id:"H7", title:"recovery-buttons-must-not-lie", needs:"recovery card",
361
- desc:"every ENABLED recovery action does a real thing; a disabled one carries its reason · no button narrates an action it doesn't perform (Thariq loop-1)",
362
- can:function(s){ return s.recoveryButtons && s.recoveryButtons.length>0; },
363
- check:function(s){
364
- var out = [];
365
- s.recoveryButtons.forEach(function(b){
366
- if(b.bareToast) out.push("recovery '"+b.action+"' is enabled but only fires a toast · it lies about acting");
367
- if(b.disabled && !b.hasReason) out.push("recovery '"+b.action+"' is disabled with no reason (silent dead button)");
368
- });
369
- return out;
370
- } }
371
- ];
372
-
373
- var PROTO_VERSION = "codehere-verify/1";
374
-
375
- /* -------- feature coverage: every granular PRODUCT_STATE claim, v0->v1 --------
376
- Turns the ~150-row inventory's claims into watchable checks against the REAL
377
- surface. status drives the verdict:
378
- shipped ([shipped]/[green]) -> element MUST render -> OBSERVED, else MISSING (the claim is a lie)
379
- partial ([partial]) -> render if present -> OBSERVED, else PARTIAL-GAP (honest, not a lie)
380
- roadmap ([roadmap]) -> SKIP-ROADMAP (planned, not built — correctly absent)
381
- future ([future]/cut) -> SKIP-FUTURE (deferred by design / cut)
382
- surface: 'list' = dashboard · 'detail' = opens demo-rate-limit-shipped-v0.9.
383
- sel = real CSS selector confirmed against the live DOM (not invented). */
384
- var COV_FIXTURE = "demo-rate-limit-shipped-v0.9";
385
- /* Inject a synthetic 3-task batch into the page's task STATE (the banner-
386
- drive honesty bar: state occurs -> the real render path must produce the
387
- surface; never DOM injection). Restored by __unseedCovBatch. Without this,
388
- a home that never ran the batch seed reported the two v1.0 rows MISSING
389
- even though the render path is real (found in the It-4 joint ceremony). */
390
- function __seedCovBatch(w){
391
- try{
392
- var tk = w._cached && w._cached.tasks; if(!tk) return false;
393
- if (w.__covSavedPersisted !== undefined) return true; // already seeded this run
394
- w.__covSavedPersisted = tk.persisted;
395
- var now = Date.now()-300000;
396
- var mk = function(id, ag, st){ return { sessionId:id, agent:ag, prompt:"coverage probe "+id, cwd:"/cov-batch-probe", startedAt:now, status:st, batchId:"cov-batch-1", steps:[], artifacts:{}, provenance:"persisted", durationMs:1000, completedAt: st==="running"?0:now+1000 }; };
397
- tk.persisted = (tk.persisted||[]).concat([ mk("cov-b1","claude-code","completed"), mk("cov-b2","codex","running"), mk("cov-b3","gemini","failed") ]);
398
- return true;
399
- }catch(e){ return false; }
400
- }
401
- function __unseedCovBatch(w){
402
- try{
403
- if (w.__covSavedPersisted === undefined) return;
404
- if (w._cached && w._cached.tasks) w._cached.tasks.persisted = w.__covSavedPersisted;
405
- delete w.__covSavedPersisted;
406
- }catch(e){}
407
- }
408
-
409
- /* It-4 V1-MULTITASK/V1-FANOUT-ALL drive helper. Finds a cwd in the
410
- /api/tasks data (real or coverage-injected) whose turns include a batchId
411
- shared by >=2 tasks, then drives the real openRoom() so renderRoom()
412
- produces the live .project-rollup + .fanout-all surface. Returns the cwd
413
- it opened, or null if no batch is present. */
414
- function __openBatchRoom(w){
415
- try{
416
- var all = (w.allS ? w.allS(w._cached && w._cached.tasks || {}) : []);
417
- var byBatch = {};
418
- for(var i=0;i<all.length;i++){
419
- /* S18 · practice (mockRun) batches collapse behind the practice row in
420
- rooms — they no longer render a rollup, so the drive must open a
421
- REAL batch room (the seeded cov-batch qualifies). Same honest rule
422
- as renderRoom, applied to both sides. */
423
- var t=all[i]; if(!t.batchId || t.mockRun) continue;
424
- (byBatch[t.batchId]=byBatch[t.batchId]||[]).push(t);
425
- }
426
- for(var bid in byBatch){
427
- if(byBatch[bid].length>=2){
428
- var cwd=byBatch[bid][0].cwd; if(!cwd) continue;
429
- if(w.openRoom){ w.openRoom(encodeURIComponent(cwd)); return cwd; }
430
- }
431
- }
432
- }catch(e){}
433
- return null;
434
- }
435
- var FEATURES = [
436
- { id:"F-AUDIT-FOOTER", name:"Audit chain footer", surface:"list", sel:".audit-chain-panel", status:"shipped", tier:"v0.7" },
437
- { id:"F-BUDGET-CHIP", name:"Budget pill", surface:"list", sel:".budget", status:"shipped", tier:"v0.7",
438
- /* .budget fills its text only when a daily budget is set (upBud). Real
439
- ~/.codehere often has no cap set, so it flaps empty. Drive the REAL
440
- upBud() over a set-budget pricing state (60% spent · safe), restore. */
441
- drive:function(w,d){ try{ w.__covBud=w._cached.pricing; w._cached.pricing={dailyBudgetCents:500,spentTodayCents:300}; w.upBud(); }catch(e){} },
442
- undrive:function(w,d){ try{ w._cached.pricing=w.__covBud; w.upBud(); }catch(e){} } },
443
- { id:"F-BUDGET-WARN-BANNER", name:"Budget warning banner", surface:"list", sel:".budget-banner", status:"partial", tier:"v0.6",
444
- /* drive the REAL upBud() over an over-budget pricing state, observe the
445
- banner, restore. Same legitimacy as F-CUSTOM-VIEW-BANNER's class drive. */
446
- drive:function(w,d){ try{ w.__covPricing=w._cached.pricing; w._cached.pricing={dailyBudgetCents:500,spentTodayCents:500}; w.upBud(); }catch(e){} },
447
- undrive:function(w,d){ try{ w._cached.pricing=w.__covPricing; w.upBud(); }catch(e){} } },
448
- /* S13 · at rest the home shows TODAY one-line rows only; demos + earlier
449
- work collapse behind the history row. Card rows are observed by
450
- expanding history through the REAL toggle (window.__histExpand), the
451
- same path the user's click takes — never DOM injection. */
452
- { id:"F-CARD-AGENT-BORDER", name:"Per-vendor class on task row (S12: identity is the monochrome name, no hue)", surface:"list", sel:".task-card.agent-claude-code", status:"shipped", tier:"v0.9",
453
- drive:function(w,d){ try{ w.__histExpand(); }catch(e){} },
454
- undrive:function(w,d){ try{ w.__histCollapse(); }catch(e){} } },
455
- { id:"F-CARD-CLICK-DETAIL", name:"Click card/row -> detail view", surface:"list", sel:".task-card", status:"shipped", tier:"v0.6",
456
- drive:function(w,d){ try{ w.__histExpand(); }catch(e){} },
457
- undrive:function(w,d){ try{ w.__histCollapse(); }catch(e){} } },
458
- { id:"F-CARD-OUTCOME-CHIP", name:"Outcome on the card (the .tc-stat pill: verdict word + checks, right-aligned with cost)", surface:"list", sel:".tcard .tc-stat", status:"shipped", tier:"v0.7",
459
- drive:function(w,d){ try{ w.__histExpand(); }catch(e){} },
460
- undrive:function(w,d){ try{ w.__histCollapse(); }catch(e){} } },
461
- { id:"F-CUSTOM-VIEW-BANNER", name:"Custom view banner", surface:"list", sel:".custom-view-banner", status:"shipped", tier:"v0.8",
462
- drive:function(w,d){ d.body.classList.add("custom-view"); },
463
- undrive:function(w,d){ d.body.classList.remove("custom-view"); } },
464
- { id:"F-DAEMON-HEARTBEAT", name:"Local pill auto-update", surface:"list", sel:".local-pill", status:"shipped", tier:"v0.6" },
465
- { id:"F-FAILOVER-BANNER", name:"Failover banner", surface:"list", sel:".failover-banner.show", status:"partial", tier:"v0.7",
466
- /* drive the banner's OWN render (window.__fbRender) with a real offer
467
- shape — same object /api/failover-offers returns — so we observe the
468
- live render path, not a fake DOM injection. */
469
- drive:function(w,d){ try{ w.__fbRender([{ sessionId:"cov-failover", target:"codex", signal:{ fromAgent:"claude-code", errorCode:"rate_limited" } }]); }catch(e){} },
470
- undrive:function(w,d){ try{ w.__fbRender([]); }catch(e){} } },
471
- /* S13 · filters moved into the ⌘K palette (rule 9). Observed by opening
472
- the REAL palette (window.openPalette — the same function ⌘K runs) and
473
- asserting the filter commands render; the command's Enter runs the
474
- same setFilter() the tabs ran. */
475
- { id:"F-FILTER-PILLS", name:"All/Running/Done/Failed filters (S13: \u2318K command EXECUTED \u203A the removable token is the observed effect)", surface:"list", sel:".ftoken", status:"shipped", tier:"v0.7",
476
- /* Refuter (S13) · presence-only re-pointing was vacuous: a lobotomized
477
- command element would still pass. This drive runs the REAL command
478
- (palette open \u2192 click filter-failed) and the row observes its
479
- CONSEQUENCE (.ftoken) \u2014 a broken run closure now reads MISSING. */
480
- drive:function(w,d){ try{ w.openPalette(); var c=d.querySelector("[data-verify-cmd='filter-failed']"); if(c) c.click(); }catch(e){} },
481
- undrive:function(w,d){ try{ var x=d.querySelector(".ftoken .x"); if(x) x.click(); w.closePalette(); }catch(e){} } },
482
- { id:"F-INDEXING-BANNER", name:"First-run indexing banner", surface:"list", sel:".indexing-banner", status:"partial", tier:"v0.7",
483
- /* partial by nature: this banner only shows on a genuinely fresh home
484
- (<3 sessions). The drive reproduces that fresh-home state — empties
485
- _cached.tasks + clears the sessionStorage dismissal — runs the REAL
486
- upIdx(), observes, then restores. */
487
- drive:function(w,d){ try{ w.__covTasks=w._cached.tasks; w._cached.tasks={spawned:[],persisted:[],sessions:[]}; try{w.sessionStorage.removeItem("codehere_indexing_dismissed");}catch(e){} w.upIdx(); }catch(e){} },
488
- undrive:function(w,d){ try{ w._cached.tasks=w.__covTasks; w.upIdx(); }catch(e){} } },
489
- { id:"F-LOCAL-PILL", name:"Local-first / zero-telemetry pill", surface:"list", sel:".local-pill", status:"shipped", tier:"v0.7" },
490
- { id:"F-LOGO", name:"Brand wordmark", surface:"list", sel:".logo .name", status:"shipped", tier:"v0.7" },
491
- { id:"F-NEW-TASK-BTN", name:"New task (S13: first ⌘K command, focuses the composer)", surface:"list", sel:"[data-verify-cmd='new-task']", status:"shipped", tier:"v0.7",
492
- drive:function(w,d){ try{ w.openPalette(); }catch(e){} },
493
- undrive:function(w,d){ try{ w.closePalette(); }catch(e){} } },
494
- { id:"F-OBSERVED-THREAD", name:"Observed session as thread", surface:"list", sel:".obs-row", status:"shipped", tier:"v0.7",
495
- /* S9 made practice mode honest: the mock chain starts EMPTY, so the
496
- observer correctly renders its teach-state and this row has no .obs-row
497
- to observe — it used to pass only because past mock runs had POLLUTED
498
- the real chain with host sessions. Banner-drive honesty bar (same as
499
- __seedCovBatch): seed the STATE through the REAL render path (the
500
- exact function /api/host-sessions feeds), clearly labeled as a
501
- coverage probe, restored from the real API after. Never DOM. */
502
- /* S13 · the observer rests as one calm line; .obs-row renders in the
503
- EXPANDED rail — drive the real expand flag with the probe session. */
504
- drive:function(w,d){ w._obsOpen=true; w.renderHostSessions({ sessions:[{ sessionId:"coverage-probe-host-session", shortId:"cov-probe", lastSeen:new Date().toISOString(), toolMix:{ Bash:3, Read:2 }, blockedCalls:0, criticalCalls:0, totalCalls:5, durationMs:60000, risk:"low" }] }); },
505
- undrive:function(w,d){ try{ w._obsOpen=false; fetch("/api/host-sessions").then(function(r){ return r.json(); }).then(function(h){ w.renderHostSessions(h); }); }catch(e){} } },
506
- { id:"F-OSS-FOOTER", name:"OSS footer", surface:"list", sel:".oss-footer", status:"shipped", tier:"v0.7" },
507
- /* S20 · the five personas died; three lenses replace them. Each row is
508
- EFFECT-observing (the F-FILTER-PILLS bar): the drive EXECUTES the real
509
- ⌘K lens command (palette open -> click -> applyLens) and the sel pins
510
- the RECOMPOSITION that command must produce — a lobotomized command, a
511
- missing body[data-lens], or a lens that failed to recompose all read
512
- MISSING. State captured + restored in undrive. */
513
- // F-LENS-DEFAULT/REVIEW/STATUS removed 2026-06-20 — the lens mode-switch was
514
- // cut for ONE consistent conversation-first view (no more UI reshaping
515
- // between visits). The detail's honest verdict + evidence disclosure carry
516
- // what Review/Status used to surface.
517
- { id:"F-PIN-DEMO", name:"Demo-shape sessions pinned to FEATURED (S13: inside expanded history)", surface:"list", sel:".task-card.tc-pinned", status:"shipped", tier:"v0.9",
518
- drive:function(w,d){ try{ w.__histExpand(); }catch(e){} },
519
- undrive:function(w,d){ try{ w.__histCollapse(); }catch(e){} } },
520
- { id:"F-PORT-FAILOVER-NOTICE", name:"Port-collision UI surface (banner render path shared with failover; the PORT-collision trigger itself is not simulated)", surface:"list", sel:".failover-banner.show", status:"partial", tier:"v0.7", // shares the failover-banner surface; driven via window.__fbRender (see F-FAILOVER-BANNER). Honest = partial: the port-collision PATH itself isn't simulated, only the shared banner render.
521
- drive:function(w,d){ try{ w.__fbRender([{ sessionId:"cov-port", target:"codex", signal:{ fromAgent:"claude-code", errorCode:"port_in_use" } }]); }catch(e){} },
522
- undrive:function(w,d){ try{ w.__fbRender([]); }catch(e){} } },
523
- // F-RESHAPE-BTN removed 2026-06-20 — Reshape mode cut for one consistent view.
524
- { id:"F-SETTINGS-COG", name:"Settings drawer trigger (S13: ⌘K Settings command)", surface:"list", sel:"[data-verify-cmd='settings']", status:"shipped", tier:"v0.8",
525
- drive:function(w,d){ try{ w.openPalette(); }catch(e){} },
526
- undrive:function(w,d){ try{ w.closePalette(); }catch(e){} } },
527
- { id:"F-SHARE-BTN", name:"Share thread (S13: ⌘K Share command -> same signed-link modal)", surface:"list", sel:"[data-verify-cmd='share']", status:"shipped", tier:"v0.8",
528
- drive:function(w,d){ try{ w.openPalette(); }catch(e){} },
529
- undrive:function(w,d){ try{ w.closePalette(); }catch(e){} } },
530
- { id:"F-SQUAD-ROW", name:"Agent readiness (S13: strip died · ⌘K Agents command carries the real readiness)", surface:"list", sel:"[data-verify-cmd='agents']", status:"shipped", tier:"v0.7",
531
- drive:function(w,d){ try{ w.openPalette(); }catch(e){} },
532
- undrive:function(w,d){ try{ w.closePalette(); }catch(e){} } },
533
- { id:"F-TASK-CARD", name:"Task row (one-line, keeps .task-card contract)", surface:"list", sel:".task-card", status:"shipped", tier:"v0.7",
534
- drive:function(w,d){ try{ w.__histExpand(); }catch(e){} },
535
- undrive:function(w,d){ try{ w.__histCollapse(); }catch(e){} } },
536
- { id:"F-TIME-GAP-PILL", name:"Brand pill + relative-time sub", surface:"list", sel:".time-gap", status:"shipped", tier:"v0.7",
537
- /* Data-dependent (same hazard verify-page already calls out for goal groups):
538
- .time-gap renders only BETWEEN tasks >2h apart (renderL). Real ~/.codehere
539
- can legitimately have no such gap — a busy session clusters every task into
540
- one <2h window (this exact flap, 2026-06-08) — so the live-DOM check flaps
541
- MISSING on honest data. Observe it DETERMINISTICALLY instead, the same way
542
- F-WORKTREE-ALERT does: inject two same-goal tasks 6h apart, render, observe,
543
- restore. The render path is the REAL renderL — not a faked element. */
544
- /* S12 · both fixtures sit 3 days back so they land in ONE chapter
545
- (EARLIER) deterministically — the home now splits NOW/TODAY/EARLIER
546
- and a now/now-6h pair could straddle midnight and split chapters. */
547
- /* S13 · the gap rows live inside the EARLIER goal groups, which sit
548
- behind the history row now — expand through the REAL toggle. */
549
- /* S21 · same-title runs now collapse into ONE thread (groupByTitle), so a
550
- time-gap between two runs of the SAME task no longer renders (they're one
551
- thread) — correct. The .time-gap feature renders between DISTINCT tasks
552
- >2h apart; the fixture uses two distinct-title tasks 6h apart to exercise
553
- it (still same goal/cwd, so still one goal group). */
554
- drive:function(w,d){ try{ w.__covTG=w._cached.tasks; var now=Date.now(); var mk=function(id,ts,p){return{sessionId:id,status:"completed",cwd:"/repo/cov-tg",startedAt:ts,prompt:p,agent:"claude-code",steps:[]};}; var base=now-3*24*3600*1000; w._cached.tasks={spawned:[],persisted:[mk("cov-tg-new",base,"coverage fixture new"),mk("cov-tg-old",base-6*3600*1000,"coverage fixture old")],sessions:[]}; w.__histExpand(); w.renderL(); }catch(e){} },
555
- undrive:function(w,d){ try{ w._cached.tasks=w.__covTG; w.__histCollapse(); w.renderL(); }catch(e){} } },
556
- { id:"F-WORKTREE-ALERT", name:"Worktree alert", surface:"list", sel:".worktree-alert", status:"partial", tier:"v0.7",
557
- /* upWk() fires when >=2 running sessions span different cwds and the odd
558
- one has been running >5min. Drive that exact predicate over the REAL
559
- upWk() with a synthetic 2-running-session state, observe, restore. */
560
- drive:function(w,d){ try{ w.__covTasks=w._cached.tasks; var old=Date.now()-10*60*1000; w._cached.tasks={spawned:[],persisted:[{sessionId:"cov-wk-main",status:"running",cwd:"/repo/main",startedAt:old},{sessionId:"cov-wk-odd",status:"running",cwd:"/repo/feature-x",startedAt:old}],sessions:[]}; w.upWk(); }catch(e){} },
561
- undrive:function(w,d){ try{ w._cached.tasks=w.__covTasks; w.upWk(); }catch(e){} } },
562
- /* goal-first home + goal room (v1 spec · shared-room hand-off). Goal groups
563
- render from REAL non-demo tasks (same data dependency as F-TIME-GAP-PILL);
564
- room features are observed by driving the REAL openRoom() on the first
565
- goal-head's data-goal-cwd, then closeRoom() — same pattern as showD(). */
566
- /* S13 · goal groups own the EXPANDED history (today is flat one-line
567
- rows); observed deterministically with the same injected-state drive
568
- F-TIME-GAP-PILL uses — real renderL, real toggle, restored after. */
569
- { id:"F-GOAL-GROUP", name:"Goal grouping (S13: inside expanded history; heads still open rooms)", surface:"list", sel:".goal-group", status:"shipped", tier:"v1",
570
- drive:function(w,d){ try{ w.__covGG=w._cached.tasks; var now=Date.now(); var mk=function(id,ts){return{sessionId:id,status:"completed",cwd:"/repo/cov-gg",startedAt:ts,prompt:"coverage fixture",agent:"claude-code",steps:[]};}; var base=now-3*24*3600*1000; w._cached.tasks={spawned:[],persisted:[mk("cov-gg-a",base),mk("cov-gg-b",base-3600*1000)],sessions:[]}; w.__histExpand(); w.renderL(); }catch(e){} },
571
- undrive:function(w,d){ try{ w._cached.tasks=w.__covGG; w.__histCollapse(); w.renderL(); }catch(e){} } },
572
- { id:"F-GOAL-HEAD-OPEN", name:"Goal head opens its room (carries cwd)", surface:"list", sel:".goal-head[data-goal-cwd]", status:"shipped", tier:"v1",
573
- drive:function(w,d){ try{ w.__covGH=w._cached.tasks; var now=Date.now(); var mk=function(id,ts){return{sessionId:id,status:"completed",cwd:"/repo/cov-gh",startedAt:ts,prompt:"coverage fixture",agent:"claude-code",steps:[]};}; var base=now-3*24*3600*1000; w._cached.tasks={spawned:[],persisted:[mk("cov-gh-a",base),mk("cov-gh-b",base-3600*1000)],sessions:[]}; w.__histExpand(); w.renderL(); }catch(e){} },
574
- undrive:function(w,d){ try{ w._cached.tasks=w.__covGH; w.__histCollapse(); w.renderL(); }catch(e){} } },
575
- { id:"F-AGENT-PICK", name:"Composer agent picker (Auto + 5 agents)", surface:"list", sel:"button#agentpick-btn", status:"shipped", tier:"v1" },
576
- /* S13 · the two new surfaces. The palette is driven through the REAL
577
- opener (the same function ⌘K runs); the decision queue renders ONLY
578
- when real decisions exist, so it's observed by injecting a failed-task
579
- state through the real renderDecisions(), then restored. */
580
- { id:"F-CMDK-PALETTE", name:"⌘K command palette · grouped Act/View/Trust/Go, Enter runs the real function", surface:"list", sel:"[data-verify-unit='palette'] .prow[data-verify-cmd]", status:"shipped", tier:"v1",
581
- drive:function(w,d){ try{ w.openPalette(); }catch(e){} },
582
- undrive:function(w,d){ try{ w.closePalette(); }catch(e){} } },
583
- { id:"F-DECISION-QUEUE", name:"Needs-your-decision queue · absent when nothing needs you (H14 pins the count; this drive pins the PRESENT branch deterministically through the real renderDecisions)", surface:"list", sel:"#decision-queue .dq-row", status:"shipped", tier:"v1",
584
- drive:function(w,d){ try{ w.__covDQ=w._cached.tasks; var now=Date.now()-600000; w._cached.tasks={spawned:[],persisted:[{sessionId:"cov-dq-fail",status:"failed",cwd:"/repo/cov-dq",startedAt:now,prompt:"coverage decision probe",agent:"claude-code",steps:[]}],sessions:[]}; w.renderDecisions(); }catch(e){} },
585
- undrive:function(w,d){ try{ w._cached.tasks=w.__covDQ; w.renderDecisions(); }catch(e){} } },
586
- /* S18 · the practice group's own regression net (the board must be able to
587
- catch a regression in the exact story it ships). Two assertions in ONE
588
- drive, both through the REAL render path:
589
- (1) absent <=> 0 practice — with zero practice fixtures renderL must
590
- render NO .prac-row; a phantom row aborts the drive, sel stays
591
- unmatched, the row reads MISSING;
592
- (2) with 2 mockRun fixtures, the REAL window.__pracExpand must render a
593
- row whose claimed data-verify-practice-n=2 AND exactly 2 rows inside
594
- .practice-rows (sel pins both via :nth-child(2):last-child) — a
595
- "practice runs · 2" label over 0 rows, or practice flooding back
596
- into the real list, reads MISSING. State restored in undrive. */
597
- { id:"F-PRACTICE-ROW", name:"Practice-runs group · quiet collapsed row, claimed N == rendered rows, absent when no practice exists (S18)", surface:"list", sel:"#task-list .prac-row[data-verify-practice-n='2'][aria-expanded='true'] + .practice-rows > .task-card:nth-child(2):last-child", status:"shipped", tier:"v1",
598
- drive:function(w,d){ try{ w.__covPRAC=w._cached.tasks; w.__pracCollapse(); w._cached.tasks={spawned:[],persisted:[],sessions:[]}; w.renderL(); if(d.querySelector("#task-list .prac-row")) return; var base=Date.now()-3*24*3600*1000; var mkp=function(id,ts){return{sessionId:id,status:"completed",cwd:"/repo/cov-prac",startedAt:ts,prompt:"coverage practice fixture "+id,agent:"claude-code",steps:[],mockRun:true,provenance:"persisted",durationMs:1000,completedAt:ts+1000};}; w._cached.tasks={spawned:[],persisted:[mkp("cov-prac-a",base),mkp("cov-prac-b",base-3600*1000)],sessions:[]}; w.__pracExpand(); }catch(e){} },
599
- undrive:function(w,d){ try{ w._cached.tasks=w.__covPRAC; w.__pracCollapse(); w.renderL(); }catch(e){} } },
600
- /* S18 · H13's aggregation path, pinned. Under CODEHERE_MOCK every live run
601
- is practice, so the board's hero compare is honestly 0==0 — vacuous.
602
- This drive injects deterministic NON-practice fixtures (one running ·
603
- one completed-verified · one caught, three distinct agents, started
604
- today) through the REAL upHero(); sel pins every claimed
605
- data-verify-hero-* attr to the fixture-derived counts. A double-counted
606
- agent or mis-bucketed running session reads MISSING. */
607
- { id:"F-HERO-COUNTS", name:"Day-line hero · real aggregation path pinned (agents=3 · verified=1 · caught=1 · stopped=0 · running=1) (S18)", surface:"list", sel:"[data-verify-unit='home-hero'][data-verify-hero-mode='counts'][data-verify-hero-agents='3'][data-verify-hero-verified='1'][data-verify-hero-caught='1'][data-verify-hero-stopped='0'][data-verify-hero-running='1']", status:"shipped", tier:"v1",
608
- drive:function(w,d){ try{ w.__covHERO=w._cached.tasks; var hnow=Date.now(); var mkh=function(id,ag,st,vd){ var t={sessionId:id,status:st,cwd:"/repo/cov-hero",startedAt:hnow,prompt:"coverage hero fixture "+id,agent:ag,steps:[],provenance:"persisted"}; if(vd){ t.verdict={claim:{status:vd}}; t.durationMs=1000; t.completedAt=hnow; } return t; }; w._cached.tasks={spawned:[],persisted:[mkh("cov-hero-run","claude-code","running",null),mkh("cov-hero-ok","codex","completed","verified"),mkh("cov-hero-bad","gemini","completed","broken")],sessions:[]}; w.upHero(); }catch(e){} },
609
- undrive:function(w,d){ try{ w._cached.tasks=w.__covHERO; w.upHero(); }catch(e){} } },
610
- /* room selectors are scoped to #task-list: the hidden detail view's DOM
611
- persists (display:none still matches querySelectorAll) and also has an
612
- .audit-line — unscoped, a dead room would false-OBSERVE off it. */
613
- { id:"F-ROOM-HEAD", name:"Goal room header (title + outcome sub)", surface:"room", sel:"#task-list .room-head .room-title", status:"shipped", tier:"v1" },
614
- // F-ROOM-BACK removed 2026-07-07 — the '← Projects' back-link died for the
615
- // rail's Today row (nav option B); the rail + Esc + the logo are the way out.
616
- { id:"F-ROOM-TURNS", name:"Turns in order (how agents worked the goal)", surface:"room", sel:"#task-list .turns-label", status:"partial", tier:"v1" },
617
- { id:"F-ROOM-AUDIT", name:"Room audit line (steps in local chain)", surface:"room", sel:"#task-list .audit-line", status:"partial", tier:"v1" },
618
- /* partial by nature: renders ONLY when the daemon reports project memory
619
- OFF for the opened room (initialized projects correctly show nothing). */
620
- { id:"F-ROOM-MEM-NUDGE", name:"Project-memory init nudge (turn on memory)", surface:"room", sel:"#task-list .mem-nudge", status:"partial", tier:"v1" },
621
- /* on-demand surfaces: hidden until a state class the product itself
622
- toggles. The harness DRIVES that exact state (same honesty bar as the
623
- seeded fixtures: state occurs -> surface renders), then restores it.
624
- Before AUD-05 these were "observed" while display:none — a blind spot. */
625
- { id:"M-OVERLAY-LINEAR", name:"Overlay-renderer Linear", surface:"list", sel:".or-overlay", status:"shipped", tier:"v0.9",
626
- drive:function(w,d){ d.getElementById("or-overlay").classList.add("open"); },
627
- undrive:function(w,d){ d.getElementById("or-overlay").classList.remove("open"); } },
628
- { id:"M-PRIMITIVES-NOTE", name:"Primitives note", surface:"list", sel:".primitives-note .prims .p", status:"shipped", tier:"v0.8",
629
- drive:function(w,d){ w.openReshape(); },
630
- undrive:function(w,d){ var m=d.getElementById("reshape-modal"); if(m) m.classList.remove("open"); } },
631
- { id:"M-RESHAPE-AGENT-COMPOSE", name:"Agent-compose animation", surface:"list", sel:".agent-compose .line", status:"shipped", tier:"v0.8",
632
- drive:function(w,d){ w.openReshape(); d.getElementById("agent-compose").classList.add("show"); },
633
- undrive:function(w,d){ d.getElementById("agent-compose").classList.remove("show"); var m=d.getElementById("reshape-modal"); if(m) m.classList.remove("open"); } },
634
- { id:"M-SETTINGS-DRAWER", name:"Settings drawer", surface:"list", sel:".drawer", status:"shipped", tier:"v0.8" },
635
- { id:"M-SHARE-TOAST", name:"Share toast notification", surface:"list", sel:".share-toast", status:"shipped", tier:"v0.8" },
636
- { id:"M-WPASS-APPROVAL", name:"W-PASS approval modal", surface:"list", sel:".wp-modal", status:"shipped", tier:"v0.9",
637
- drive:function(w,d){ d.getElementById("wp-modal").classList.add("open"); },
638
- undrive:function(w,d){ d.getElementById("wp-modal").classList.remove("open"); } },
639
- { id:"P-AB-BADGE", name:"Agent identity badge (S13: on history cards + detail; rows carry the mono name)", surface:"list", sel:".ab", status:"shipped", tier:"v0.6",
640
- drive:function(w,d){ try{ w.__histExpand(); }catch(e){} },
641
- undrive:function(w,d){ try{ w.__histCollapse(); }catch(e){} } },
642
- { id:"P-STATUS-PILL", name:"Per-status pill on the card (.tc-stat: running pulse / verified / reported / caught)", surface:"list", sel:".tcard .tc-stat", status:"shipped", tier:"v0.7",
643
- drive:function(w,d){ try{ w.__histExpand(); }catch(e){} },
644
- undrive:function(w,d){ try{ w.__histCollapse(); }catch(e){} } },
645
- { id:"F-DETAIL-HEADER", name:"Detail view header (back button + title + agent badge + status line)", surface:"detail", sel:".back-btn", status:"shipped", tier:"v0.7" },
646
- { id:"F-DETAIL-KICKER", name:"Detail kicker + failover context", surface:"detail", sel:".detail-kicker", status:"shipped", tier:"v0.7" },
647
- { id:"F-OUTCOME-PANEL", name:"Outcome section (files/steps/Final Report)", surface:"detail", sel:"[data-verify-unit='outcome']", status:"shipped", tier:"v0.7" },
648
- { id:"F-SHIMMER-TITLE", name:"Animated gradient title", surface:"detail", sel:".detail-title", status:"shipped", tier:"v0.7" },
649
- { id:"P-CODE-CARD", name:"Inline syntax-tinted code preview", surface:"detail", sel:".code-card", status:"shipped", tier:"v0.7" },
650
- { id:"P-CTX-URL-CHIP", name:"Clickable context-source URL preview chip", surface:"detail", sel:".ctx-url-chip", status:"shipped", tier:"v0.8" },
651
- /* S14 · the celebration card died with the calm redesign — the verified
652
- state is now the dot+word verdict line (the only green). Honest
653
- re-point: observe the SAME state (verdict=verified) on its new dress. */
654
- { id:"P-VERIFIED-BLOCK", name:"Verified verdict line (S14: dot + word, the only green)", surface:"detail", sel:".vline[data-verify-verdict='verified']", status:"shipped", tier:"v0.9",
655
- /* the verified verdict line renders in the detail for a completed,
656
- git-tracked, non-mock task with a verified claim (verdictState==='verified').
657
- Mock seeds are practice/reported. Mutate the ALREADY-OPEN detail task
658
- in place (no navigate/no _cached swap — that corrupts the shared detail
659
- other detail probes observe), re-render, observe, restore. */
660
- drive:function(w,d){ try{ var t=w.findS?w.findS(COV_FIXTURE):null; if(t){ w.__covVB={v:t.verdict,m:t.mockRun,s:t.status,g:(t.artifacts?t.artifacts.noGitTracking:undefined),r:t.finalReport}; t.verdict={claim:{status:"verified"}}; t.mockRun=false; t.status="completed"; if(t.artifacts)t.artifacts.noGitTracking=false; t.finalReport={summary:"coverage fixture \\u00b7 claims match reality",source:"agent-summary"}; w.renderD(t); } }catch(e){} },
661
- undrive:function(w,d){ try{ var t=w.findS?w.findS(COV_FIXTURE):null; if(t&&w.__covVB){ t.verdict=w.__covVB.v; t.mockRun=w.__covVB.m; t.status=w.__covVB.s; if(t.artifacts)t.artifacts.noGitTracking=w.__covVB.g; t.finalReport=w.__covVB.r; w.renderD(t); } }catch(e){} } },
662
- { id:"P-WRITE-PASS", name:"Write-pass inline card (blue-accented)", surface:"detail", sel:".write-pass .head + h4", status:"shipped", tier:"v0.9",
663
- drive:function(w,d){ try{ var c=d.querySelector("details.conv-wrap"); if(c) c.open=true; }catch(e){} },
664
- undrive:function(w,d){ try{ var c=d.querySelector("details.conv-wrap"); if(c) c.open=false; }catch(e){} } },
665
- { id:"F-RECOVERY-CARD", name:"failed-task recovery card", surface:"detail", sel:".recovery-card", status:"shipped", tier:"v0.7", fixture:"demo-recovery-test-failed-v0.9" },
666
- { id:"F-CRASH-RECOVERY", name:"orphaned-session banner", surface:"detail", sel:".crash-recovery", status:"shipped", tier:"v0.7", fixture:"demo-crash-orphaned-mid-write-v0.9" },
667
- { id:"F-HOOK-BLOCK-CARD", name:"hook-block card + override", surface:"detail", sel:".hook-block-card", status:"shipped", tier:"v0.7", fixture:"demo-hook-block-bulk-rm-v0.9" },
668
- { id:"F-OVERRIDE-FLOW", name:"override + log reason", surface:"detail", sel:".hook-block-actions", status:"shipped", tier:"v0.7", fixture:"demo-hook-block-bulk-rm-v0.9" },
669
- { id:"F-GIT-NOTE", name:"non-git honest note (S14: .verifier-panel died · the partial verdict line carries it)", surface:"detail", sel:".git-note,.vline[data-verify-verdict='partial']", status:"shipped", tier:"v0.7", fixture:"demo-non-git-honest-v1" },
670
- /* S14 · the conversation rests as one collapsed row ("conversation · N
671
- turns · every tool call recorded"); these rows drive the REAL expand
672
- (details.conv-wrap.open — the same toggle the user's click flips),
673
- observe inside, then collapse back. */
674
- { id:"F-CONVERSATION", name:"multi-speaker conversation (S14: behind the collapsed conversation row)", surface:"detail", sel:".conv-turn,.turn,.speaker", status:"shipped", tier:"v0.7", fixture:"demo-rate-limit-shipped-v0.9",
675
- drive:function(w,d){ try{ var c=d.querySelector("details.conv-wrap"); if(c) c.open=true; }catch(e){} },
676
- undrive:function(w,d){ try{ var c=d.querySelector("details.conv-wrap"); if(c) c.open=false; }catch(e){} } },
677
- { id:"P-ARTIFACTS-STRUCTURED", name:"structured artifacts", surface:"detail", sel:".art-cmp,.artifact,.art-card,.struct-art", status:"partial", tier:"v0.7", fixture:"demo-rate-limit-shipped-v0.9" },
678
- { id:"P-PANEL-TITLE", name:"uppercase panel title", surface:"detail", sel:".panel-title", status:"shipped", tier:"v0.7", fixture:"demo-rate-limit-shipped-v0.9" },
679
- { id:"F-THREAD-PHASED", name:"phased narrative timeline", surface:"detail", sel:".tl-div,.chapter-card,.phase-pos", status:"shipped", tier:"v0.7", fixture:"demo-rate-limit-shipped-v0.9",
680
- drive:function(w,d){ try{ var c=d.querySelector("details.conv-wrap"); if(c) c.open=true; }catch(e){} },
681
- undrive:function(w,d){ try{ var c=d.querySelector("details.conv-wrap"); if(c) c.open=false; }catch(e){} } },
682
- { id:"F-FILES-TESTS", name:"files & tests panel", surface:"detail", sel:".file-tree", status:"shipped", tier:"v0.7", fixture:"demo-rate-limit-shipped-v0.9" },
683
- { id:"P-TOOL-CARD", name:"tool-call activity card", surface:"detail", sel:".tl-acts", status:"shipped", tier:"v0.7", fixture:"demo-rate-limit-shipped-v0.9",
684
- drive:function(w,d){ try{ var c=d.querySelector("details.conv-wrap"); if(c) c.open=true; }catch(e){} },
685
- undrive:function(w,d){ try{ var c=d.querySelector("details.conv-wrap"); if(c) c.open=false; }catch(e){} } },
686
- { id:"F-THINKING-EMITTER", name:"live thinking dots (needs SSE token stream)", surface:"detail", sel:".thinking", status:"partial", tier:"v0.7", fixture:"demo-running-streaming-v0.9",
687
- drive:function(w,d){ try{ var c=d.querySelector("details.conv-wrap"); if(c) c.open=true; }catch(e){} },
688
- undrive:function(w,d){ try{ var c=d.querySelector("details.conv-wrap"); if(c) c.open=false; }catch(e){} } },
689
- { id:"P-REPL-PRIMITIVES", name:"streaming repl-beat (needs SSE token stream)", surface:"detail", sel:".repl-beat", status:"partial", tier:"v0.7", fixture:"demo-running-streaming-v0.9",
690
- drive:function(w,d){ try{ var c=d.querySelector("details.conv-wrap"); if(c) c.open=true; }catch(e){} },
691
- undrive:function(w,d){ try{ var c=d.querySelector("details.conv-wrap"); if(c) c.open=false; }catch(e){} } },
692
- { id:"P-AUDIT-LINE", name:"inline audit beat (host-thread / live audit)", surface:"detail", sel:".audit-line", status:"partial", tier:"v0.7", fixture:"demo-rate-limit-shipped-v0.9",
693
- drive:function(w,d){ try{ var c=d.querySelector("details.conv-wrap"); if(c) c.open=true; }catch(e){} },
694
- undrive:function(w,d){ try{ var c=d.querySelector("details.conv-wrap"); if(c) c.open=false; }catch(e){} } },
695
- { id:"F-TOKEN-TABLE", name:"per-agent token table (needs artifacts.usage)", surface:"detail", sel:".token-table", status:"partial", tier:"v0.7", fixture:"demo-rate-limit-shipped-v0.9" },
696
- /* overlay-renderer per-source cards · the drive renders the REAL per-source
697
- render branch over a fixture payload (same shape the adapter unit fixtures
698
- produce) so .overlay-figma/.overlay-drawio are observable without a live
699
- Figma token or network fetch. */
700
- { id:"M-OVERLAY-FIGMA", name:"overlay-renderer · Figma", surface:"list", sel:".overlay-figma", status:"shipped", tier:"v1.0",
701
- drive:function(w,d){ w.codehereRenderOverlayPayload({ source:"figma", url:"figma://file/abc123", text:"Figma file · API Design\\nCANVAS · Mobile\\n FRAME · Home 375×812\\n TEXT · Title · \\"Welcome to the app\\"", metadata:{ fileKey:"abc123", name:"API Design", lastModified:"2026-05-21T09:00:00Z", frames:[{ name:"Home", width:375, height:812, text:"Welcome to the app" }] } }); },
702
- undrive:function(w,d){ var o=d.getElementById("or-overlay"); if(o) o.classList.remove("open"); } },
703
- { id:"M-OVERLAY-DRAWIO", name:"overlay-renderer · draw.io", surface:"list", sel:".overlay-drawio", status:"shipped", tier:"v1.0",
704
- drive:function(w,d){ w.codehereRenderOverlayPayload({ source:"drawio", url:"drawio://gh/owner/repo/docs/arch.drawio", text:"draw.io diagram · github.com/owner/repo/docs/arch.drawio\\n\\nstructure: 5 cells · 1 edges · 1 page\\n--- raw xml (truncated) ---\\n<mxfile><diagram><mxGraphModel>...", metadata:{ label:"github.com/owner/repo/docs/arch.drawio", bytes:842, cellCount:5, edgeCount:1, pageCount:1 } }); },
705
- undrive:function(w,d){ var o=d.getElementById("or-overlay"); if(o) o.classList.remove("open"); } },
706
- { id:"V1-MULTITASK", name:"multi-task projects · parent/child", surface:"list", sel:".project-rollup", status:"shipped", tier:"v1.0",
707
- /* It-4 · banner-drive precedent: inject a known 3-task batch STATE
708
- (__seedCovBatch, never DOM), then the REAL openRoom() -> renderRoom()
709
- derives the .project-rollup row live. Restored in undrive. */
710
- drive:function(w,d){ try{ __seedCovBatch(w); w.__covBatchCwd=__openBatchRoom(w); }catch(e){} },
711
- undrive:function(w,d){ try{ if(w.closeRoom) w.closeRoom(); __unseedCovBatch(w); }catch(e){} } },
712
- { id:"V1-FANOUT-ALL", name:"@all fanout to ready adapters", surface:"list", sel:".fanout-all", status:"shipped", tier:"v1.0",
713
- /* Same injected batch room renders the .fanout-all grid (one column/adapter). */
714
- drive:function(w,d){ try{ __seedCovBatch(w); __openBatchRoom(w); }catch(e){} },
715
- undrive:function(w,d){ try{ if(w.closeRoom) w.closeRoom(); __unseedCovBatch(w); }catch(e){} } },
716
- { id:"F-AGENT-SWITCHER", name:"'reply as' agent picker", surface:"detail", sel:".agent-switcher", status:"future", tier:"cut" }
717
- ];
718
-
719
- /* -------- read the data-verify-* contract off the real surface -------- */
720
- function readSurface(doc){
721
- var dv = doc.getElementById("task-detail-view");
722
- var visible = dv && dv.style.display !== "none";
723
- var meta = doc.querySelector('[data-verify-unit="detail"]');
724
- var outcome = doc.querySelector('[data-verify-unit="outcome"]');
725
- var filetree = doc.querySelector('[data-verify-unit="filetree"]');
726
- var verifiers = Array.prototype.slice.call(doc.querySelectorAll('[data-verify-unit="verifier"]')).map(function(el){
727
- return { verdict: el.getAttribute("data-verify-verdict"), gittracked: el.getAttribute("data-verify-gittracked") };
728
- });
729
- var says = Array.prototype.slice.call(doc.querySelectorAll('.tl-say')).map(function(el){
730
- // A glance turn carries its words in .gl-line (the one-sentence glance);
731
- // a plain reasoning bubble in .tl-say-text. First match in document
732
- // order wins — an actually-wordless bubble still reads len 0 and FAILS.
733
- var w = el.querySelector(".tl-say-text, .gl-line");
734
- var t = (w ? w.textContent : "") || "";
735
- t = t.replace(/^\\s+|\\s+$/g, "");
736
- return { len:t.length, text:t };
737
- });
738
- // Thariq loop-1 · recovery-card buttons. Capture each action button's
739
- // enabled state + whether its onclick is a bare al() toast — so the
740
- // recovery-buttons-must-not-lie probe can FAIL on a relapse.
741
- var recoveryButtons = Array.prototype.slice.call(doc.querySelectorAll('.recovery-card [data-verify-action]')).map(function(el){
742
- var oc = el.getAttribute("onclick") || "";
743
- return {
744
- action: el.getAttribute("data-verify-action"),
745
- disabled: el.hasAttribute("disabled"),
746
- // a "lie" = an ENABLED button whose only behavior is a toast
747
- bareToast: !el.hasAttribute("disabled") && /^\\s*al\\(/.test(oc),
748
- hasReason: !!el.getAttribute("title"),
749
- };
750
- });
751
- // Thariq loop-2 · the observer rail. Capture its mode (empty/active), the
752
- // claimed live count, per-row states, and whether the empty state actually
753
- // teaches (non-blank) — so H8 can pin: never a stale "live", never a blank
754
- // empty box, never fake rows.
755
- var obsRail = doc.querySelector('[data-verify-unit="observer"]');
756
- var observer = obsRail ? {
757
- mode: obsRail.getAttribute("data-verify-observer"),
758
- liveClaimed: Number(obsRail.getAttribute("data-verify-observer-live") || 0),
759
- rowStates: Array.prototype.slice.call(obsRail.querySelectorAll('[data-verify-obs-state]')).map(function(r){ return r.getAttribute("data-verify-obs-state"); }),
760
- teachLen: (obsRail.querySelector('.obs-teach') ? (obsRail.querySelector('.obs-teach').textContent||"").trim().length : 0),
761
- textLen: (obsRail.textContent||"").trim().length
762
- } : null;
763
- // S7 · the composer cost chips. Capture each chip's computed mode + what
764
- // it says, so H10 can pin: the chip never claims a computation that
765
- // didn't happen (the pre-S7 chip was a hardcoded ~$0.02 under a
766
- // "based on prompt length + pricing" tooltip).
767
- var costEst = Array.prototype.slice.call(doc.querySelectorAll('[data-verify-unit="cost-est"]')).map(function(el){
768
- return {
769
- mode: el.getAttribute("data-verify-mode"),
770
- text: (el.textContent||"").trim(),
771
- title: el.getAttribute("title") || ""
772
- };
773
- });
774
- var found = !!(meta && visible);
775
- // S12 · H13 — the home hero's CLAIMED counts (stamped data-verify-hero-*)
776
- // vs an INDEPENDENT re-derivation from the same task state the page holds
777
- // (_cached via the iframe window). The hero element persists in the DOM
778
- // while a detail is open, so the check is readable from any fixture.
779
- var heroEl = doc.querySelector('[data-verify-unit="home-hero"]');
780
- var hero = null;
781
- if (heroEl){
782
- hero = {
783
- mode: heroEl.getAttribute("data-verify-hero-mode"),
784
- agents: Number(heroEl.getAttribute("data-verify-hero-agents")||0),
785
- verified: Number(heroEl.getAttribute("data-verify-hero-verified")||0),
786
- caught: Number(heroEl.getAttribute("data-verify-hero-caught")||0),
787
- stopped: Number(heroEl.getAttribute("data-verify-hero-stopped")||0),
788
- running: Number(heroEl.getAttribute("data-verify-hero-running")||0),
789
- expected: null
790
- };
791
- try {
792
- var hw = doc.defaultView;
793
- var hAll = (hw && typeof hw.allS==="function") ? hw.allS((hw._cached && hw._cached.tasks)||{}) : [];
794
- /* S18 · PAIRED with ui-page-v2 heroCounts' top filter: practice runs
795
- (mockRun) never feed the day-line. Both sides exclude them
796
- identically — the honest-rule update, not a weakened probe. */
797
- var hReal = hAll.filter(function(t){ return !/^demo-/.test(t.sessionId||"") && !t.mockRun; });
798
- var hSod = new Date(); hSod.setHours(0,0,0,0);
799
- var hToday = hReal.filter(function(t){ return (t.startedAt||0) >= hSod.getTime(); });
800
- var hLive = ["running","queued","paused","blocked"];
801
- // Refuter C2 · this used to copy heroCounts' own (then-broken)
802
- // expressions — a probe that re-derives the claim pipeline is probe
803
- // theater. Derive from the VERDICT ground truth: verified = the
804
- // verdict says claims match reality, on a real (non-mock) completed
805
- // git-tracked run; caught = the verdict says broken; stopped =
806
- // terminal-unclean statuses; running ignores the day boundary.
807
- var hLiveAll = hReal.filter(function(t){ return hLive.indexOf(t.status)>=0; });
808
- var hAg = {};
809
- hToday.concat(hLiveAll).forEach(function(t){ var a=String(t.agent||""); if(a) hAg[a]=1; });
810
- hero.expected = {
811
- today: hToday.length,
812
- agents: Object.keys(hAg).length,
813
- // PAIRED with heroCounts' composite-grade rule (2.2): green needs an
814
- // INDEPENDENT signal when a grade is attached; the legacy claim check
815
- // applies only to verdicts without one. The sides drifted when the
816
- // hero adopted grades and this stayed on claim-status — first exposed
817
- // 2026-07-02 by a real --ship run (claim verified, self-report only):
818
- // hero said 1, this derived 2. Change BOTH sides identically.
819
- verified: hToday.filter(function(t){
820
- return !!(t.verdict && (t.verdict.grade ? t.verdict.grade.green : (t.verdict.claim && t.verdict.claim.status==="verified")))
821
- && !t.mockRun
822
- && ["completed","done"].indexOf(t.status)>=0
823
- && !(t.artifacts && t.artifacts.noGitTracking);
824
- }).length,
825
- caught: hToday.filter(function(t){ var v=t.verdict; return !!(v && (v.grade ? v.grade.headline==="broken" : (v.claim && v.claim.status==="broken"))); }).length,
826
- stopped: hToday.filter(function(t){ return ["failed","error","crashed","killed"].indexOf(t.status)>=0; }).length,
827
- running: hLiveAll.length
828
- };
829
- } catch(e){ /* state unreachable — H13 reports BLOCKED, never a silent green */ }
830
- }
831
- // S13 · H14 — the decision queue's CLAIMED N (data-verify-decisions-n)
832
- // vs an INDEPENDENT re-derivation from the same state the page holds:
833
- // pending write-passes (window._wpPendingAll, the list wpPoll feeds the
834
- // queue AND the modal from) + verifier-broken verdicts today + failed
835
- // runs with the prompt recorded (Retry available), non-demo, today.
836
- // Like the hero, the container persists in the DOM while a detail is
837
- // open, so the check is readable from any fixture.
838
- var dqEl = doc.querySelector('[data-verify-unit="decisions"]');
839
- var decisions = {
840
- present: !!dqEl,
841
- n: dqEl ? Number(dqEl.getAttribute("data-verify-decisions-n")||0) : 0,
842
- rows: dqEl ? dqEl.querySelectorAll(".dq-row").length : 0,
843
- expected: null
844
- };
845
- try {
846
- var dw = doc.defaultView;
847
- if (dw && typeof dw.allS === "function"){
848
- /* S18 · PAIRED with ui-page-v2 decisionItems' top filter: a practice
849
- (mockRun) failure never demands a real decision. Both sides exclude
850
- them identically — the honest-rule update, not a weakened probe. */
851
- var dAll = dw.allS((dw._cached && dw._cached.tasks)||{}).filter(function(t){ return !/^demo-/.test(t.sessionId||"") && !t.mockRun; });
852
- var dSod = new Date(); dSod.setHours(0,0,0,0);
853
- var dToday = function(t){ return (t.startedAt||0) >= dSod.getTime(); };
854
- var dBrokenIs = function(t){ return !!(t.verdict && t.verdict.claim && t.verdict.claim.status==="broken"); };
855
- var dPasses = (dw._wpPendingAll||[]).length;
856
- var dBroken = dAll.filter(function(t){ return dToday(t) && dBrokenIs(t); }).length;
857
- var dFailed = dAll.filter(function(t){ return dToday(t) && ["failed","error","crashed","killed"].indexOf(t.status)>=0 && t.prompt && !dBrokenIs(t); }).length;
858
- decisions.expected = dPasses + dBroken + dFailed;
859
- }
860
- } catch(e){ /* state unreachable — H14 reports BLOCKED, never a silent green */ }
861
- // S12 · the detail verdict hero stamps which staging it chose; it ALSO
862
- // carries data-verify-unit="verifier" so H1/H9 already read it above.
863
- var vhEl = doc.querySelector("[data-verify-verdict-hero]");
864
- var verdictHero = vhEl ? vhEl.getAttribute("data-verify-verdict-hero") : null;
865
- // S2 · a running task must carry a Stop control on its detail.
866
- var hasStopControl = !!doc.querySelector('.detail-meta [data-verify-action="stop"]');
867
- // S1 · the detail composer's follow-up honesty: enabled means the server
868
- // says the message can be DELIVERED; disabled must carry a plain reason.
869
- var dta = doc.getElementById('detail-ta');
870
- var followUp = dta ? {
871
- state: dta.getAttribute('data-verify-followup'),
872
- disabled: dta.disabled,
873
- placeholderLen: (dta.getAttribute('placeholder')||'').trim().length
874
- } : null;
875
- // S19 · glyph discipline — scan the REAL rendered surface (visible text
876
- // nodes + human-facing attributes, script/style excluded) for emoji /
877
- // pictograph codepoints outside the sanctioned geometric glyph set
878
- // (\\u2713 \\u2715 \\u25CF \\u25CB \\u2022 \\u2298 \\u203A \\u26A0 \\u2014 \\u00B7, \\u2191 send, \\u2190 back,
879
- // \\u2318/\\u23CE keyboard hints). Effect-observing: a re-introduced emoji
880
- // anywhere in the driven chrome FAILS the board via H15.
881
- var glyphScan = { scanned:false, hits:[] };
882
- try {
883
- // S19 R2 · the token contract bans emoji-AS-ICON (chrome), never emoji
884
- // a user typed: [data-user-content] containers (prompts, say/think
885
- // text, reports, step labels/outputs) are run CONTENT and excluded —
886
- // an honest "fix the bug" prompt with an emoji must not void the board.
887
- var gIsContent = function(el){
888
- while (el && el.nodeType===1){
889
- if (el.hasAttribute && el.hasAttribute("data-user-content")) return true;
890
- el = el.parentNode;
891
- }
892
- return false;
893
- };
894
- var gParts = [];
895
- var gCollect = function(){
896
- var gWalk = doc.createTreeWalker(doc.body, NodeFilter.SHOW_TEXT, { acceptNode: function(nd){
897
- var pn = nd.parentNode && nd.parentNode.nodeName;
898
- if (pn==="SCRIPT"||pn==="STYLE") return NodeFilter.FILTER_REJECT;
899
- return gIsContent(nd.parentNode) ? NodeFilter.FILTER_REJECT : NodeFilter.FILTER_ACCEPT;
900
- } });
901
- while (gWalk.nextNode()) gParts.push(gWalk.currentNode.nodeValue);
902
- Array.prototype.slice.call(doc.body.querySelectorAll("*")).forEach(function(el){
903
- if (el.nodeName==="SCRIPT"||el.nodeName==="STYLE") return;
904
- if (gIsContent(el)) return;
905
- ["title","placeholder","aria-label","alt","value"].forEach(function(at){
906
- var v = el.getAttribute && el.getAttribute(at);
907
- if (v) gParts.push(v);
908
- });
909
- });
910
- };
911
- gCollect();
912
- // S19 R2 · the chrome claim must be OBSERVED in the one-click-away
913
- // collapse states too: drive the REAL history/practice toggles (the
914
- // same render path the S18 coverage drives use), collect again, then
915
- // restore whichever state the surface was in. Pure UI state — no data
916
- // is changed, and the duplicate text just re-feeds the dedup'd regex.
917
- var gw = doc.defaultView;
918
- var gHistBtn = doc.querySelector(".hist-row:not(.prac-row)");
919
- var gPracBtn = doc.querySelector(".prac-row");
920
- var gHistWas = gHistBtn ? gHistBtn.getAttribute("aria-expanded")==="true" : false;
921
- var gPracWas = gPracBtn ? gPracBtn.getAttribute("aria-expanded")==="true" : false;
922
- try {
923
- if (gw && gw.__histExpand) gw.__histExpand();
924
- if (gw && gw.__pracExpand) gw.__pracExpand();
925
- gCollect();
926
- } finally {
927
- try { if (gw && !gHistWas && gw.__histCollapse) gw.__histCollapse(); } catch(e2){}
928
- try { if (gw && !gPracWas && gw.__pracCollapse) gw.__pracCollapse(); } catch(e2){}
929
- }
930
- var gText = gParts.join("\\n");
931
- var gRe = /[\\u2192-\\u21FF]|[\\u2300-\\u2317]|[\\u2319-\\u23CD]|[\\u23CF-\\u23FF]|[\\u2600-\\u269F]|[\\u26A1-\\u26FF]|[\\u2700-\\u2712]|\\u2714|[\\u2716-\\u27BF]|[\\u2B00-\\u2BFF]|\\uFE0F|[\\uD83A-\\uD83F][\\uDC00-\\uDFFF]|[\\uD83C-\\uD83E][\\uDC00-\\uDFFF]/g;
932
- var gm, gSeen = {};
933
- while ((gm = gRe.exec(gText))){
934
- var gc = gm[0];
935
- if (gSeen[gc]) continue;
936
- gSeen[gc] = 1;
937
- var gcp = gc.codePointAt(0).toString(16).toUpperCase();
938
- glyphScan.hits.push("U+"+gcp+" ('"+gc+"') rendered in the chrome \\u00b7 emoji/pictograph outside the sanctioned glyph set");
939
- }
940
- glyphScan.scanned = true;
941
- } catch(e){ /* unscannable -> H15 reports BLOCKED, never a silent green */ }
942
- return {
943
- glyphScan: glyphScan,
944
- found: found,
945
- hero: hero,
946
- decisions: decisions,
947
- verdictHero: verdictHero,
948
- observer: observer,
949
- costEst: costEst,
950
- hasStopControl: hasStopControl,
951
- followUp: followUp,
952
- recoveryButtons: recoveryButtons,
953
- recoveryState: (doc.querySelector('.recovery-card') ? doc.querySelector('.recovery-card').getAttribute('data-verify-recovery') : null),
954
- status: meta ? meta.getAttribute("data-verify-status") : null,
955
- terminalFail: meta ? meta.getAttribute("data-verify-terminal-fail")==="true" : false,
956
- hasExplanation: meta ? meta.getAttribute("data-verify-has-explanation")==="true" : false,
957
- hasOutcome: !!outcome,
958
- outcomeItems: outcome ? outcome.querySelectorAll(".outcome-item").length : 0,
959
- filesClaimed: outcome ? Number(outcome.getAttribute("data-verify-files-claimed")||0) : 0,
960
- terminal: outcome ? outcome.getAttribute("data-verify-terminal")==="true" : false,
961
- hasReport: outcome ? outcome.getAttribute("data-verify-has-report")==="true" : false,
962
- steps: outcome ? Number(outcome.getAttribute("data-verify-steps")||0) : 0,
963
- hasFiletree: !!filetree,
964
- filetreeRendered: filetree ? Number(filetree.getAttribute("data-verify-rendered")||0) : 0,
965
- verifiers: verifiers,
966
- says: says
967
- };
968
- }
969
-
970
- /* -------- iframe surface plumbing -------- */
971
- var iframe = document.getElementById("surface");
972
- function frameWin(){ return iframe.contentWindow; }
973
- function frameDoc(){ return iframe.contentDocument || (iframe.contentWindow && iframe.contentWindow.document); }
974
-
975
- function sleep(ms){ return new Promise(function(r){ setTimeout(r, ms); }); }
976
-
977
- // Wait until the product page inside the iframe is ready to drive.
978
- function surfaceReady(){
979
- return new Promise(function(resolve, reject){
980
- var tries = 0;
981
- (function poll(){
982
- tries++;
983
- try {
984
- var w = frameWin(), d = frameDoc();
985
- /* S13 · at rest the home may legitimately have NO task-cards (today
986
- empty -> first-run hero; demos collapsed behind the history row).
987
- Ready = task state loaded AND renderL produced a real at-rest
988
- surface (rows, observer, the history row, or the empty state). */
989
- if (w && typeof w.showD === "function" && w._cached && w._cached.tasks && d && d.querySelectorAll(".task-card, .obs-row, .hist-row, .empty-state").length > 0) return resolve();
990
- // The dashboard auto-opens a running task's detail on load, which
991
- // hides the list and left runAll dead-on-arrival ("surface never
992
- // became ready") whenever anything was running — found cold by the
993
- // S5-S8 acceptance walk. Close the auto-opened detail and re-poll.
994
- if (w && typeof w.hideD === "function" && d && d.body && d.body.classList.contains("detail-open")) { try { w.hideD(); } catch(e2){} }
995
- } catch(e){ /* not loaded yet */ }
996
- if (tries > 80) return reject(new Error("surface never became ready (no showD / no task cards) · is the daemon serving seeded sessions?"));
997
- setTimeout(poll, 150);
998
- })();
999
- });
1000
- }
1001
-
1002
- // Navigate the REAL surface to a session and read its contract. Honest about
1003
- // the not-seeded case: returns found=false rather than inventing a result.
1004
- // S20 · a fixture may carry { sid, lens }: the lens is applied through the
1005
- // page's OWN applyLens (the same function the ⌘K command runs — a real
1006
- // recomposition, never harness CSS), observed, then restored to default.
1007
- function observe(fx){
1008
- var sid = (fx && typeof fx === "object") ? (fx.sid || fx.id) : fx;
1009
- var lens = (fx && typeof fx === "object") ? fx.lens : null;
1010
- var w = frameWin(), d = frameDoc();
1011
- try { if (typeof w.hideD === "function") w.hideD(); } catch(e){}
1012
- try {
1013
- if (lens && typeof w.applyLens === "function") w.applyLens(lens);
1014
- w.showD(sid);
1015
- } catch(e){ return Promise.resolve({ found:false, error:String(e), verifiers:[], says:[] }); }
1016
- return sleep(140).then(function(){
1017
- var s = readSurface(frameDoc());
1018
- if (lens){ try { var w2 = frameWin(); if (typeof w2.applyLens === "function") w2.applyLens("default"); } catch(e){} }
1019
- return s;
1020
- });
1021
- }
1022
-
1023
- /* -------- run one (invariant × fixture) into a structured result -------- */
1024
- function runOne(inv, fx, s){
1025
- var base = { unitId: inv.id, fixtureId: fx.id, fixtureLabel: fx.label, probe: !!fx.probe, timestamp: new Date().toISOString(), domSnapshot: snapshot(s) };
1026
- if (!s.found){
1027
- return Object.assign(base, { verdict:"BLOCKED", checks:[], blockedReason: s.error ? ("surface error: "+s.error) : "session not rendered (not seeded?)" });
1028
- }
1029
- if (!inv.can(s)){
1030
- return Object.assign(base, { verdict:"BLOCKED", checks:[], blockedReason: "0 units to observe (no "+inv.needs+")" });
1031
- }
1032
- var violations = inv.check(s);
1033
- if (violations.length){
1034
- return Object.assign(base, { verdict:"FAIL", checks: violations.map(function(v){ return { status:"fail", label:v }; }) });
1035
- }
1036
- return Object.assign(base, { verdict:"PASS", checks: [{ status:(fx.probe?"probe":"ok"), label: inv.desc }] });
1037
- }
1038
-
1039
- function snapshot(s){
1040
- if (!s.found) return {};
1041
- return { status:s.status, terminalFail:s.terminalFail, hasExplanation:s.hasExplanation, filesClaimed:s.filesClaimed, filetreeRendered:s.filetreeRendered, outcomeItems:s.outcomeItems, steps:s.steps, verifiers:s.verifiers.length, says:s.says.length };
1042
- }
1043
-
1044
- /* -------- the matrix UI -------- */
1045
- var resultMap = {}; // key "H1::sid" -> result
1046
- function key(u,f){ return u+"::"+f; }
1047
-
1048
- function buildMatrix(){
1049
- var thead = document.querySelector("#matrix thead");
1050
- var tbody = document.querySelector("#matrix tbody");
1051
- var hrow = "<tr><th style=\\"width:40%\\">Invariant (unit)</th><th class=\\"needs\\" style=\\"width:13%\\">needs</th>";
1052
- FIXTURES.forEach(function(fx){
1053
- hrow += "<th><div class=\\"fixhdr"+(fx.probe?" probe":"")+"\\">"+(fx.probe?"probe \\u00b7 ":"")+fx.label+"</div></th>";
1054
- });
1055
- hrow += "</tr>";
1056
- thead.innerHTML = hrow;
1057
- tbody.innerHTML = INVARIANTS.map(function(inv){
1058
- var cells = FIXTURES.map(function(fx){
1059
- return "<td><button class=\\"cell\\" id=\\"c-"+inv.id+"-"+cssId(fx.id)+"\\" data-u=\\""+inv.id+"\\" data-f=\\""+fx.id+"\\">&middot;</button></td>";
1060
- }).join("");
1061
- var probeTag = inv.id==="H5" ? "<span class=\\"probe-tag\\">self-test</span>" : "";
1062
- return "<tr"+(inv.id==="H5"?" class=\\"probe-row\\"":"")+"><td><span class=\\"unit-id\\">"+inv.id+" &middot; "+inv.title+"</span>"+probeTag+"<div class=\\"unit-desc\\">"+inv.desc+"</div></td><td class=\\"needs\\">"+inv.needs+"</td>"+cells+"</tr>";
1063
- }).join("");
1064
- // clicking a cell jumps replay to that fixture
1065
- Array.prototype.slice.call(document.querySelectorAll(".cell")).forEach(function(btn){
1066
- btn.addEventListener("click", function(){
1067
- var fid = btn.getAttribute("data-f");
1068
- var i = FIXTURES.map(function(f){return f.id;}).indexOf(fid);
1069
- if (i>=0){ openReplay(); jumpTo(i); }
1070
- });
1071
- });
1072
- }
1073
-
1074
- function cssId(s){ return s.replace(/[^a-zA-Z0-9_-]/g,"_"); }
1075
-
1076
- function paintCell(r){
1077
- var el = document.getElementById("c-"+r.unitId+"-"+cssId(r.fixtureId));
1078
- if (!el) return;
1079
- el.className = "cell " + r.verdict.toLowerCase();
1080
- if (r.verdict==="FAIL" && r.probe) el.className += " expected";
1081
- el.textContent = r.verdict==="PASS" ? "\\u2713" : r.verdict==="FAIL" ? "\\u2715" : r.verdict==="BLOCKED" ? "\\u00B7" : "\\u2013";
1082
- el.title = r.blockedReason || (r.checks[0] && r.checks[0].label) || r.verdict;
1083
- }
1084
-
1085
- /* -------- expected-vs-actual: the honesty self-check on the harness -------- */
1086
- function classify(results){
1087
- var pass=0, fail=0, blocked=0, unexpectedFail=[], probeMissed=[];
1088
- results.forEach(function(r){
1089
- if (r.verdict==="PASS") pass++;
1090
- else if (r.verdict==="BLOCKED") blocked++;
1091
- else if (r.verdict==="FAIL"){
1092
- fail++;
1093
- if (!r.probe) unexpectedFail.push(r.unitId+"\\u00d7"+r.fixtureLabel);
1094
- }
1095
- });
1096
- // every fixture that declared expectFail MUST have produced that FAIL
1097
- FIXTURES.forEach(function(fx){
1098
- if (!fx.expectFail) return;
1099
- var r = resultMap[key(fx.expectFail, fx.id)];
1100
- if (!r || r.verdict!=="FAIL") probeMissed.push(fx.expectFail+"\\u00d7"+fx.label);
1101
- });
1102
- var ok = unexpectedFail.length===0 && probeMissed.length===0;
1103
- return { pass:pass, fail:fail, blocked:blocked, total:results.length, ok:ok, unexpectedFail:unexpectedFail, probeMissed:probeMissed };
1104
- }
1105
-
1106
- function paintTally(c){
1107
- document.getElementById("tally").innerHTML =
1108
- "<span class=\\"p\\">\\u2713 "+c.pass+"</span><span class=\\"f\\">\\u2715 "+c.fail+"</span><span class=\\"b\\">\\u2298 "+c.blocked+"</span><span class=\\"t\\">/ "+c.total+"</span>";
1109
- var line = document.getElementById("verdict-line");
1110
- if (c.ok){
1111
- line.className = "verdict-line green"; line.setAttribute("data-evidence","measured");
1112
- line.textContent = "\\u2713 honest: every PASS was observed, and the probe FAILED exactly as designed (the harness can see a lie).";
1113
- } else {
1114
- line.className = "verdict-line red"; line.setAttribute("data-evidence","measured");
1115
- var parts = [];
1116
- if (c.unexpectedFail.length) parts.push("unexpected FAIL: "+c.unexpectedFail.join(", "));
1117
- if (c.probeMissed.length) parts.push("PROBE DID NOT FAIL ("+c.probeMissed.join(", ")+") \\u00b7 the harness is blind, treat the whole board as void");
1118
- line.textContent = "\\u2715 " + parts.join(" \\u00b7 ");
1119
- }
1120
- }
1121
-
1122
- /* -------- the agent / CI handle -------- */
1123
- var lastSingle = null;
1124
- function buildManifest(){
1125
- return INVARIANTS.map(function(inv){
1126
- return { unit: inv.id+"-"+inv.title, needs: inv.needs, description: inv.desc,
1127
- fixtures: FIXTURES.map(function(fx){ return { id:fx.id, label:fx.label, probe:!!fx.probe, expectFail:fx.expectFail||null }; }) };
1128
- });
1129
- }
1130
-
1131
- // S20 · the board observes the DEFAULT lens unless a fixture says otherwise:
1132
- // pin it before driving (the iframe shares localStorage with the dashboard,
1133
- // so a user's saved lens would silently reshape every fixture) and restore
1134
- // the user's saved lens after.
1135
- function pinLensDefault(){
1136
- try {
1137
- var w = frameWin(), d = frameDoc();
1138
- if (window.__verifyLensSaved === undefined) window.__verifyLensSaved = (d.body.getAttribute("data-lens")) || "default";
1139
- if (typeof w.applyLens === "function") w.applyLens("default");
1140
- } catch(e){}
1141
- }
1142
- function restoreLens(){
1143
- try {
1144
- var w = frameWin();
1145
- if (typeof w.applyLens === "function") w.applyLens(window.__verifyLensSaved || "default");
1146
- delete window.__verifyLensSaved;
1147
- } catch(e){}
1148
- }
1149
-
1150
- function runAll(){
1151
- var btn = document.getElementById("run-all");
1152
- btn.disabled = true; btn.textContent = "Running\\u2026";
1153
- var results = [];
1154
- return surfaceReady().then(function(){
1155
- pinLensDefault();
1156
- // iterate fixtures, observe each once, run all invariants against it
1157
- return FIXTURES.reduce(function(p, fx){
1158
- return p.then(function(){
1159
- return observe(fx).then(function(s){
1160
- INVARIANTS.forEach(function(inv){
1161
- var r = runOne(inv, fx, s);
1162
- resultMap[key(inv.id, fx.id)] = r;
1163
- results.push(r);
1164
- paintCell(r);
1165
- });
1166
- });
1167
- });
1168
- }, Promise.resolve());
1169
- }).then(function(){
1170
- restoreLens();
1171
- var c = classify(results);
1172
- paintTally(c);
1173
- lastSingle = results[results.length-1] || null;
1174
- window.__verify_lastRun = results;
1175
- window.__verify_summary = c;
1176
- btn.disabled = false; btn.textContent = "\\u25b6 Run all";
1177
- return results;
1178
- }).catch(function(err){
1179
- restoreLens();
1180
- btn.disabled = false; btn.textContent = "\\u25b6 Run all";
1181
- document.getElementById("verdict-line").className = "verdict-line red";
1182
- document.getElementById("verdict-line").textContent = "\\u2298 BLOCKED: "+String(err && err.message || err);
1183
- throw err;
1184
- });
1185
- }
1186
-
1187
- /* ===================== feature coverage ===================== */
1188
- function covVerdict(feat, present){
1189
- if (feat.status==="shipped") return present ? "OBSERVED" : "MISSING";
1190
- if (feat.status==="partial") return present ? "OBSERVED" : "PARTIAL-GAP";
1191
- if (feat.status==="roadmap") return "SKIP-ROADMAP";
1192
- return "SKIP-FUTURE"; // future / cut
1193
- }
1194
- function covClass(v){
1195
- return v==="OBSERVED"?"pass":v==="MISSING"?"fail":v==="PARTIAL-GAP"?"warn":"blocked";
1196
- }
1197
- function covGlyph(v){
1198
- return v==="OBSERVED"?"\\u2713":v==="MISSING"?"\\u2715":v==="PARTIAL-GAP"?"\\u25d0":"\\u00b7";
1199
- }
1200
-
1201
- var covResults = [];
1202
- function paintCov(r){
1203
- var el = document.getElementById("cov-"+cssId(r.id));
1204
- if (!el) return;
1205
- el.className = "cell " + covClass(r.verdict);
1206
- el.textContent = covGlyph(r.verdict);
1207
- el.title = r.verdict + (r.present?" · rendered":" · not in DOM");
1208
- }
1209
-
1210
- function buildCovMatrix(){
1211
- var tb = document.querySelector("#cov-matrix tbody");
1212
- if (!tb) return;
1213
- tb.innerHTML = FEATURES.map(function(f){
1214
- var statusTag = f.status==="shipped"?"":f.status==="partial"?"<span class=\\"probe-tag\\" style=\\"border-color:rgba(217,164,65,.5);color:var(--warn)\\">partial</span>":f.status==="roadmap"?"<span class=\\"probe-tag\\" style=\\"border-color:var(--border);color:var(--fg-40)\\">roadmap</span>":"<span class=\\"probe-tag\\" style=\\"border-color:var(--border);color:var(--fg-40)\\">"+(f.tier==="cut"?"cut":"future")+"</span>";
1215
- return "<tr><td><span class=\\"unit-id\\">"+f.id+"</span> <span class=\\"unit-desc\\">"+f.name+"</span>"+statusTag+"</td>"+
1216
- "<td class=\\"needs\\">"+f.tier+"</td>"+
1217
- "<td class=\\"needs\\">"+f.surface+"</td>"+
1218
- "<td><button class=\\"cell\\" id=\\"cov-"+cssId(f.id)+"\\">&middot;</button></td></tr>";
1219
- }).join("");
1220
- }
1221
-
1222
- function classifyCov(results){
1223
- var observed=0, missing=0, partialGap=0, roadmap=0, future=0;
1224
- var missingIds=[];
1225
- results.forEach(function(r){
1226
- if (r.verdict==="OBSERVED") observed++;
1227
- else if (r.verdict==="MISSING"){ missing++; missingIds.push(r.id); }
1228
- else if (r.verdict==="PARTIAL-GAP") partialGap++;
1229
- else if (r.verdict==="SKIP-ROADMAP") roadmap++;
1230
- else future++;
1231
- });
1232
- var shipped = observed + missing;
1233
- return { observed:observed, missing:missing, partialGap:partialGap, roadmap:roadmap, future:future,
1234
- shipped:shipped, total:results.length, ok: missing===0, missingIds:missingIds };
1235
- }
1236
-
1237
- function paintCovTally(c){
1238
- var t = document.getElementById("cov-tally");
1239
- if (t) t.innerHTML =
1240
- "<span class=\\"p\\">\\u2713 "+c.observed+" observed</span>"+
1241
- "<span class=\\"f\\">\\u2715 "+c.missing+" missing</span>"+
1242
- "<span style=\\"color:var(--warn)\\">\\u25d0 "+c.partialGap+" partial-gap</span>"+
1243
- "<span class=\\"b\\">\\u00b7 "+(c.roadmap+c.future)+" roadmap/future</span>"+
1244
- "<span class=\\"t\\">/ "+c.total+"</span>";
1245
- var line = document.getElementById("cov-verdict");
1246
- if (!line) return;
1247
- if (c.ok){
1248
- line.className = "verdict-line green"; line.setAttribute("data-evidence","measured");
1249
- line.textContent = "\\u2713 every feature claimed shipped was OBSERVED on the real surface · "+c.shipped+" shipped checked · "+(c.roadmap+c.future)+" correctly absent (roadmap/future).";
1250
- } else {
1251
- line.className = "verdict-line red"; line.setAttribute("data-evidence","measured");
1252
- line.textContent = "\\u2715 "+c.missing+" feature(s) claimed shipped but NOT rendered: "+c.missingIds.join(", ")+" · PRODUCT_STATE overclaims, fix the doc or the code.";
1253
- }
1254
- }
1255
-
1256
- // OBSERVED requires a VISIBLE element, not just a DOM match — display:none
1257
- // matched querySelectorAll and green-lit four dead state banners for weeks
1258
- // (2026-06-07 audit, AUD-05). NOTE: layout-based checks (getClientRects /
1259
- // offsetParent) cannot work here — the observed iframe sits inside the
1260
- // harness's own hidden replay panel, so the product surface never has
1261
- // layout. Instead walk computed styles WITHIN the iframe document: the
1262
- // product hiding its own element is what we test; harness chrome isn't.
1263
- function visibleInDoc(el, d){
1264
- var node = el;
1265
- while (node && node !== d.documentElement) {
1266
- var cs = (d.defaultView || window).getComputedStyle(node);
1267
- if (cs.display === "none" || cs.visibility === "hidden") return false;
1268
- node = node.parentElement;
1269
- }
1270
- return true;
1271
- }
1272
- function anyVisible(d, sel){
1273
- var els = d.querySelectorAll(sel);
1274
- for (var i = 0; i < els.length; i++) {
1275
- if (visibleInDoc(els[i], d)) return true;
1276
- }
1277
- return false;
1278
- }
1279
-
1280
- function runCoverage(){
1281
- var btn = document.getElementById("run-cov");
1282
- if (btn){ btn.disabled = true; btn.textContent = "Running\\u2026"; }
1283
- covResults = [];
1284
- return surfaceReady().then(function(){
1285
- var w = frameWin();
1286
- pinLensDefault();
1287
- try { if (typeof w.hideD==="function") w.hideD(); } catch(e){}
1288
- return sleep(140);
1289
- }).then(function(){
1290
- // list-surface features (dashboard visible)
1291
- var d = frameDoc();
1292
- var listFeats = FEATURES.filter(function(f){ return f.surface==="list" && f.status!=="roadmap" && f.status!=="future"; });
1293
- listFeats.filter(function(f){ return !f.drive; }).forEach(function(f){
1294
- var present = anyVisible(d, f.sel);
1295
- var r = { id:f.id, present:present, verdict:covVerdict(f, present) };
1296
- covResults.push(r); paintCov(r);
1297
- });
1298
- // driven on-demand surfaces: enter the state, observe, restore.
1299
- return listFeats.filter(function(f){ return !!f.drive; }).reduce(function(p, f){
1300
- return p.then(function(){
1301
- var w = frameWin(), dd = frameDoc();
1302
- try { f.drive(w, dd); } catch(e){}
1303
- return sleep(120).then(function(){
1304
- var present = anyVisible(frameDoc(), f.sel);
1305
- var r = { id:f.id, present:present, verdict:covVerdict(f, present) };
1306
- covResults.push(r); paintCov(r);
1307
- try { f.undrive(w, frameDoc()); } catch(e){}
1308
- return sleep(60);
1309
- });
1310
- });
1311
- }, Promise.resolve());
1312
- }).then(function(){
1313
- // detail-surface features, grouped by the fixture that TRIGGERS them.
1314
- // Conditional surfaces (recovery/crash/hook-block/...) only render on the
1315
- // right seeded session — open each fixture once, check its group.
1316
- var detail = FEATURES.filter(function(f){ return f.surface==="detail" && f.status!=="roadmap" && f.status!=="future"; });
1317
- var byFix = {};
1318
- detail.forEach(function(f){ var fx = f.fixture || COV_FIXTURE; (byFix[fx] = byFix[fx] || []).push(f); });
1319
- var fixtures = Object.keys(byFix);
1320
- return fixtures.reduce(function(p, fx){
1321
- return p.then(function(){
1322
- try { var w = frameWin(); if (w.hideD) w.hideD(); w.showD(fx); } catch(e){}
1323
- return sleep(200).then(function(){
1324
- var d = frameDoc();
1325
- byFix[fx].forEach(function(f){
1326
- /* S14 · detail rows gained drive/undrive support (same contract
1327
- as list rows): the conversation rests collapsed, so timeline
1328
- rows drive the REAL expand (the user's click on the row),
1329
- observe, then restore. Never DOM injection of content. */
1330
- var wd = frameWin();
1331
- if (f.drive) { try { f.drive(wd, d); } catch(e){} }
1332
- var present = anyVisible(d, f.sel);
1333
- var r = { id:f.id, present:present, verdict:covVerdict(f, present), fixture:fx };
1334
- covResults.push(r); paintCov(r);
1335
- if (f.undrive) { try { f.undrive(wd, d); } catch(e){} }
1336
- });
1337
- });
1338
- });
1339
- }, Promise.resolve());
1340
- }).then(function(){
1341
- // room-surface features · drive the REAL openRoom() (same pattern as
1342
- // showD above) on the first goal-head's data-goal-cwd, observe, close.
1343
- // No goal-head on the home = no real tasks; the room genuinely cannot
1344
- // be opened, so room features go MISSING/PARTIAL-GAP per their status —
1345
- // honest, and F-GOAL-GROUP fails alongside so the cause is visible.
1346
- var room = FEATURES.filter(function(f){ return f.surface==="room" && f.status!=="roadmap" && f.status!=="future"; });
1347
- if (!room.length) return;
1348
- var w0 = frameWin();
1349
- /* S13 · goal-heads sit inside the EXPANDED history now — expand through
1350
- the real toggle first. If the home has no earlier real tasks at all,
1351
- fall back to the seeded coverage batch (same precedent as
1352
- V1-MULTITASK) so the room render path is still observed for real. */
1353
- try { if (w0.__histExpand) w0.__histExpand(); } catch(e){}
1354
- var gh = frameDoc().querySelector(".goal-head[data-goal-cwd]");
1355
- var check = function(){
1356
- var d = frameDoc();
1357
- room.forEach(function(f){
1358
- var present = anyVisible(d, f.sel);
1359
- var r = { id:f.id, present:present, verdict:covVerdict(f, present) };
1360
- covResults.push(r); paintCov(r);
1361
- });
1362
- };
1363
- var seededRoom = false;
1364
- if (gh){ try { w0.openRoom(gh.getAttribute("data-goal-cwd")); } catch(e){} }
1365
- else {
1366
- try { if (__seedCovBatch(w0) && __openBatchRoom(w0)) seededRoom = true; } catch(e){}
1367
- if (!seededRoom){ check(); try{ if(w0.__histCollapse) w0.__histCollapse(); }catch(e){} return; }
1368
- }
1369
- return sleep(200).then(function(){
1370
- check();
1371
- try { if (w0.closeRoom) w0.closeRoom(); } catch(e){}
1372
- try { if (seededRoom) __unseedCovBatch(w0); } catch(e){}
1373
- try { if (w0.__histCollapse) w0.__histCollapse(); } catch(e){}
1374
- return sleep(120);
1375
- });
1376
- }).then(function(){
1377
- // roadmap / future: SKIP (correctly absent), no observation
1378
- FEATURES.filter(function(f){ return f.status==="roadmap" || f.status==="future"; }).forEach(function(f){
1379
- var r = { id:f.id, present:false, verdict:covVerdict(f, false) };
1380
- covResults.push(r); paintCov(r);
1381
- });
1382
- restoreLens();
1383
- var c = classifyCov(covResults);
1384
- paintCovTally(c);
1385
- window.__verify_coverage = { results:covResults, summary:c };
1386
- if (btn){ btn.disabled = false; btn.textContent = "\\u25b6 Run coverage"; }
1387
- return covResults;
1388
- }).catch(function(err){
1389
- restoreLens();
1390
- if (btn){ btn.disabled = false; btn.textContent = "\\u25b6 Run coverage"; }
1391
- throw err;
1392
- });
1393
- }
1394
-
1395
- window.__verify = {
1396
- version: PROTO_VERSION,
1397
- manifest: buildManifest,
1398
- current: function(){ return lastSingle; },
1399
- runAll: runAll,
1400
- coverage: runCoverage,
1401
- features: function(){ return FEATURES.map(function(f){ return { id:f.id, name:f.name, surface:f.surface, status:f.status, tier:f.tier }; }); }
1402
- };
1403
-
1404
- document.getElementById("manifest-json").textContent =
1405
- JSON.stringify(buildManifest(), null, 2) +
1406
- "\\n\\nawait window.__verify.runAll() -> one result per invariant\\u00d7fixture {verdict, checks, blockedReason, domSnapshot}\\n" +
1407
- "window.__verify_summary -> {pass, fail, blocked, ok, unexpectedFail, probeMissed}\\n" +
1408
- "ok===false (unexpected FAIL, or the probe failed to fail) -> scripts/verify.mjs exits 1.";
1409
-
1410
- /* ===================== replay (visible, slow-motion) ===================== */
1411
- var replayOpen = false, playing = false, idx = 0, replayResults = [];
1412
- var DWELL = clampInt(qs("dwell"), 1500, 200, 30000);
1413
- var KEY_HOLD = clampInt(qs("hold"), 700, 100, 5000);
1414
- document.getElementById("dwell-label").textContent = "dwell "+DWELL+"ms";
1415
-
1416
- function qs(k){ var m = new RegExp("[?&]"+k+"=([^&]+)").exec(location.search); return m ? decodeURIComponent(m[1]) : null; }
1417
- function clampInt(raw, fb, min, max){ var n = raw?parseInt(raw,10):NaN; if(isNaN(n))return fb; return Math.min(max,Math.max(min,n)); }
1418
-
1419
- function openReplay(){ replayOpen = true; document.getElementById("replay").classList.remove("hidden"); buildStepList(); }
1420
- function buildStepList(){
1421
- document.getElementById("steplist").innerHTML = FIXTURES.map(function(fx,i){
1422
- return "<div class=\\"steprow"+(fx.probe?" fail":"")+"\\" data-i=\\""+i+"\\"><span class=\\"st\\">\\u25cb</span><span class=\\"nm\\">"+(fx.probe?"probe \\u00b7 ":"")+fx.label+"</span></div>";
1423
- }).join("");
1424
- Array.prototype.slice.call(document.querySelectorAll(".steprow")).forEach(function(row){
1425
- row.addEventListener("click", function(){ jumpTo(Number(row.getAttribute("data-i"))); });
1426
- });
1427
- }
1428
- function markStep(i, verdict){
1429
- var row = document.querySelector('.steprow[data-i="'+i+'"]');
1430
- if (!row) return;
1431
- Array.prototype.slice.call(document.querySelectorAll(".steprow")).forEach(function(r){ r.classList.remove("active"); });
1432
- if (verdict==null){ row.classList.add("active"); row.querySelector(".st").textContent="\\u25b8"; return; }
1433
- row.querySelector(".st").textContent = verdict==="PASS"?"\\u2713":verdict==="FAIL"?"\\u2715":"\\u2298";
1434
- }
1435
-
1436
- var caption = document.getElementById("caption");
1437
- var ring = null;
1438
- function showCaption(t){ if(t){ caption.textContent=t; caption.classList.add("on"); } else { caption.classList.remove("on"); } }
1439
- function drawRing(el){
1440
- removeRing();
1441
- if (!el) return;
1442
- var ir = iframe.getBoundingClientRect();
1443
- var er = el.getBoundingClientRect();
1444
- ring = document.createElement("div");
1445
- ring.className = "ring";
1446
- ring.style.left = (ir.left + er.left - 3) + "px";
1447
- ring.style.top = (ir.top + er.top - 3) + "px";
1448
- ring.style.width = (er.width + 6) + "px";
1449
- ring.style.height = (Math.min(er.height, 120) + 6) + "px";
1450
- document.body.appendChild(ring);
1451
- }
1452
- function removeRing(){ if(ring){ ring.remove(); ring=null; } }
1453
-
1454
- function setStageVerdict(v){
1455
- var w = document.getElementById("stage-wrap");
1456
- w.className = "stage-wrap" + (v ? " v-"+v.toLowerCase() : "");
1457
- }
1458
- function setFrameStatus(results, sid){
1459
- var fs = document.getElementById("framestatus");
1460
- var fails = results.filter(function(r){ return r.verdict==="FAIL"; });
1461
- var blocked = results.filter(function(r){ return r.verdict==="BLOCKED"; });
1462
- var pass = results.filter(function(r){ return r.verdict==="PASS"; });
1463
- var verdict = fails.length ? "FAIL" : (pass.length ? "PASS" : "BLOCKED");
1464
- var html = "<span class=\\"badge "+verdict.toLowerCase()+"\\">"+verdict+"</span>";
1465
- html += "<span class=\\"sub\\" style=\\"color:var(--fg-50)\\">\\u2713 "+pass.length+" \\u00b7 \\u2715 "+fails.length+" \\u00b7 \\u2298 "+blocked.length+"</span>";
1466
- if (fails.length) html += "<span class=\\"issues\\">"+fails.map(function(f){ return f.unitId+": "+(f.checks[0]?f.checks[0].label:""); }).join(" \\u00b7 ")+"</span>";
1467
- else if (!pass.length && blocked.length) html += "<span class=\\"issues blk\\">"+blocked[0].blockedReason+"</span>";
1468
- fs.innerHTML = html;
1469
- }
1470
-
1471
- // Hide the app chrome inside the iframe so the stage shows ONLY the session
1472
- // detail being verified — an isolated mount, not the whole app in a tab.
1473
- var staged = false;
1474
- function stageClean(){
1475
- var d = frameDoc(); if (!d || staged) return;
1476
- var st = d.createElement("style");
1477
- st.textContent =
1478
- ".header,.custom-view-banner,.failover-banner,.indexing-banner,.budget-banner,#task-detail-view .input-area{display:none!important}" +
1479
- "body{overflow:auto!important;height:auto!important}" +
1480
- ".content{padding:0!important}#task-detail-view{padding:18px 22px!important}";
1481
- d.head.appendChild(st);
1482
- staged = true;
1483
- }
1484
-
1485
- // The per-case checklist that fills in live (the granular part the human watches).
1486
- function clReset(){
1487
- var box = document.getElementById("checklist");
1488
- box.classList.add("on");
1489
- box.innerHTML = "<div class=\\"cl-h\\">honesty checks</div>" + INVARIANTS.map(function(inv){
1490
- return "<div class=\\"cl-row\\" id=\\"cl-"+inv.id+"\\"><span class=\\"cl-id\\">"+inv.id+"</span><span class=\\"cl-st\\">\\u25cb</span><span class=\\"cl-tx\\">"+inv.title+"</span></div>";
1491
- }).join("");
1492
- }
1493
- function clSet(invId, verdict, text){
1494
- var row = document.getElementById("cl-"+invId);
1495
- if (!row) return;
1496
- row.className = "cl-row run " + verdict.toLowerCase();
1497
- row.querySelector(".cl-st").textContent = verdict==="PASS"?"\\u2713":verdict==="FAIL"?"\\u2715":"\\u2298";
1498
- if (text) row.querySelector(".cl-tx").textContent = text;
1499
- }
1500
-
1501
- // The element each invariant reads from — so the ring lands on what's observed.
1502
- function targetFor(invId, d){
1503
- if (invId==="H1") return d.querySelector('[data-verify-unit="verifier"]');
1504
- if (invId==="H2") return d.querySelector('.tl-say');
1505
- if (invId==="H3") return d.querySelector('[data-verify-unit="outcome"]');
1506
- if (invId==="H5") return d.querySelector('[data-verify-unit="filetree"]') || d.querySelector('[data-verify-unit="outcome"]');
1507
- if (invId==="H6") return d.querySelector('.recovery-card,.crash-recovery,.hook-block-card') || d.querySelector('[data-verify-unit="detail"]');
1508
- if (invId==="H13") return d.querySelector('[data-verify-unit="home-hero"]') || d.querySelector('[data-verify-unit="detail"]');
1509
- if (invId==="H14") return d.querySelector('[data-verify-unit="decisions"]') || d.querySelector('[data-verify-unit="home-hero"]') || d.querySelector('[data-verify-unit="detail"]');
1510
- return d.querySelector('[data-verify-unit="detail"]');
1511
- }
1512
- // A granular, human-readable line for what THIS check observed.
1513
- function narrate(inv, s, r){
1514
- if (r.verdict==="BLOCKED") return inv.id+" \\u00b7 n/a here (no "+inv.needs+")";
1515
- if (inv.id==="H1") return "H1 \\u00b7 verifier verdict='"+(s.verifiers[0]||{}).verdict+"' git="+(s.verifiers[0]||{}).gittracked;
1516
- if (inv.id==="H2") return "H2 \\u00b7 "+s.says.length+" reasoning bubble(s), all have words";
1517
- if (inv.id==="H3") return "H3 \\u00b7 outcome renders "+s.outcomeItems+" item(s)";
1518
- if (inv.id==="H5") return "H5 \\u00b7 claims "+s.filesClaimed+" files, renders "+s.filetreeRendered+" row(s)";
1519
- if (inv.id==="H6") return "H6 \\u00b7 status='"+s.status+"', reason shown="+s.hasExplanation;
1520
- if (inv.id==="H13" && s.hero && s.hero.expected) return "H13 \\u00b7 hero claims a="+s.hero.agents+" v="+s.hero.verified+" c="+s.hero.caught+" r="+s.hero.running+" \\u00b7 derived a="+s.hero.expected.agents+" v="+s.hero.expected.verified+" c="+s.hero.expected.caught+" r="+s.hero.expected.running;
1521
- if (inv.id==="H14" && s.decisions) return "H14 \\u00b7 queue "+(s.decisions.present?("claims N="+s.decisions.n+", renders "+s.decisions.rows+" row(s)"):"absent")+" \\u00b7 derived "+s.decisions.expected+" decision(s)";
1522
- return inv.desc;
1523
- }
1524
-
1525
- // Drive ONE invariant visibly: scroll its target into view, ring it, show a
1526
- // caption with the observed numbers, land the verdict on the checklist + matrix.
1527
- function playCheck(inv, fx, s){
1528
- var r = runOne(inv, fx, s);
1529
- resultMap[key(inv.id, fx.id)] = r;
1530
- paintCell(r);
1531
- var d = frameDoc();
1532
- if (r.verdict==="BLOCKED"){
1533
- // brief, dim — honest "nothing to observe", no theatrics
1534
- clSet(inv.id, "BLOCKED", inv.title+" \\u00b7 n/a");
1535
- showCaption("\\u2298 " + narrate(inv, s, r));
1536
- return sleep(Math.round(KEY_HOLD*0.45)).then(function(){ showCaption(null); return r; });
1537
- }
1538
- var el = targetFor(inv.id, d);
1539
- if (el && el.scrollIntoView) try { el.scrollIntoView({block:"center"}); } catch(e){}
1540
- return sleep(120).then(function(){
1541
- drawRing(el);
1542
- showCaption("\\u25CF " + narrate(inv, s, r));
1543
- return sleep(KEY_HOLD);
1544
- }).then(function(){
1545
- removeRing();
1546
- var verdictWord = r.verdict==="FAIL" ? " \\u203A \\u2715 FAIL" : " \\u203A \\u2713 PASS";
1547
- showCaption((r.verdict==="FAIL"?"\\u26a0 ":"") + narrate(inv, s, r) + verdictWord);
1548
- clSet(inv.id, r.verdict, inv.title);
1549
- return sleep(Math.round(KEY_HOLD*0.7));
1550
- }).then(function(){ showCaption(null); return r; });
1551
- }
1552
-
1553
- // Drive one fixture: open it on a clean stage, then walk each honesty check
1554
- // one at a time so the human SEES each case tested granularly.
1555
- function playFixture(i){
1556
- var fx = FIXTURES[i];
1557
- idx = i;
1558
- markStep(i, null);
1559
- clReset();
1560
- document.getElementById("stage-title").innerHTML = "case " + (i+1) + "/" + FIXTURES.length + " &middot; <strong>" + (fx.probe?"probe \\u00b7 ":"") + fx.label + "</strong> <span class=\\"dim\\">/ "+fx.id+"</span>";
1561
- document.getElementById("framestatus").innerHTML = "<span class=\\"sub\\" style=\\"color:var(--fg-40)\\">opening case\\u2026</span>";
1562
- setStageVerdict(null);
1563
- document.getElementById("prog").style.width = ((i)/FIXTURES.length*100)+"%";
1564
- return surfaceReady().then(function(){
1565
- var w = frameWin();
1566
- try { if (typeof w.hideD==="function") w.hideD(); } catch(e){}
1567
- return sleep(120);
1568
- }).then(function(){
1569
- stageClean();
1570
- showCaption("open case \\u203A showD('"+fx.label+"')");
1571
- // S20 · lens fixtures replay through the page's own applyLens — the
1572
- // human watches the recomposed surface, not the default one.
1573
- try {
1574
- var w = frameWin();
1575
- if (fx.lens && typeof w.applyLens === "function") w.applyLens(fx.lens);
1576
- else pinLensDefault();
1577
- w.showD(fx.sid || fx.id);
1578
- } catch(e){}
1579
- return sleep(Math.round(KEY_HOLD*0.7));
1580
- }).then(function(){
1581
- showCaption(null);
1582
- var s = readSurface(frameDoc());
1583
- // walk the invariants sequentially, visibly
1584
- var results = [];
1585
- return INVARIANTS.reduce(function(p, inv){
1586
- return p.then(function(){ return playCheck(inv, fx, s).then(function(r){ results.push(r); }); });
1587
- }, Promise.resolve()).then(function(){ return results; });
1588
- }).then(function(results){
1589
- replayResults = replayResults.concat(results);
1590
- var verdict = results.some(function(r){return r.verdict==="FAIL";}) ? "FAIL" : (results.some(function(r){return r.verdict==="PASS";}) ? "PASS" : "BLOCKED");
1591
- setStageVerdict(verdict);
1592
- setFrameStatus(results, fx.id);
1593
- markStep(i, verdict);
1594
- lastSingle = results[results.length-1];
1595
- document.getElementById("prog").style.width = ((i+1)/FIXTURES.length*100)+"%";
1596
- });
1597
- }
1598
-
1599
- function jumpTo(i){ playing=false; openReplay(); playFixture(i); }
1600
- function advance(){
1601
- if (idx+1 >= FIXTURES.length){ playing=false; document.getElementById("play").innerHTML="&#9654; Replay again"; finishReplay(); return; }
1602
- playFixture(idx+1).then(function(){ if(playing) setTimeout(advance, DWELL); });
1603
- }
1604
- function startReplay(){
1605
- replayResults = []; idx = 0; playing = true;
1606
- document.getElementById("play").innerHTML = "&#9646;&#9646; Pause";
1607
- playFixture(0).then(function(){ if(playing) setTimeout(advance, DWELL); });
1608
- }
1609
- function finishReplay(){
1610
- restoreLens();
1611
- var c = classify(replayResults);
1612
- window.__verify_replay = { done:true, results:replayResults, summary:c };
1613
- }
1614
-
1615
- document.getElementById("toggle-replay").addEventListener("click", function(){ openReplay(); document.getElementById("replay").scrollIntoView({behavior:"smooth"}); });
1616
- document.getElementById("play").addEventListener("click", function(){
1617
- if (playing){ playing=false; this.innerHTML="&#9654; Play"; }
1618
- else { startReplay(); }
1619
- });
1620
- document.getElementById("skip").addEventListener("click", function(){ advance(); });
1621
-
1622
- /* in-browser clip recorder (getDisplayMedia, one webm per fixture) */
1623
- var recStream=null, recorder=null, recChunks=[], recClips=[];
1624
- document.getElementById("record").addEventListener("click", function(){
1625
- var btn = this;
1626
- if (recStream){ stopRecording(); btn.innerHTML="&#9679; Record clips"; return; }
1627
- navigator.mediaDevices.getDisplayMedia({ video:{frameRate:30}, audio:false, preferCurrentTab:true })
1628
- .then(function(stream){
1629
- recStream = stream; btn.innerHTML="&#9679; Recording \\u00b7 stop";
1630
- stream.getVideoTracks()[0].addEventListener("ended", function(){ stopRecording(); btn.innerHTML="&#9679; Record clips"; });
1631
- // record one continuous clip across the whole replay (simple + reliable);
1632
- // download offered on stop.
1633
- recChunks = [];
1634
- recorder = new MediaRecorder(stream, pickMime());
1635
- recorder.ondataavailable = function(e){ if(e.data.size>0) recChunks.push(e.data); };
1636
- recorder.onstop = function(){
1637
- var blob = new Blob(recChunks, {type:"video/webm"});
1638
- var a = document.createElement("a"); a.href = URL.createObjectURL(blob); a.download = "codehere-verify-replay.webm";
1639
- document.body.appendChild(a); a.click(); a.remove();
1640
- };
1641
- recorder.start(200);
1642
- startReplay();
1643
- })
1644
- .catch(function(e){ btn.innerHTML="&#9679; Record clips"; alert("recording cancelled: "+e); });
1645
- });
1646
- function stopRecording(){
1647
- try { if(recorder && recorder.state!=="inactive") recorder.stop(); } catch(e){}
1648
- if (recStream){ recStream.getTracks().forEach(function(t){t.stop();}); recStream=null; }
1649
- }
1650
- function pickMime(){
1651
- var cs = ["video/webm;codecs=vp9","video/webm;codecs=vp8","video/webm"];
1652
- for (var i=0;i<cs.length;i++){ if (window.MediaRecorder && MediaRecorder.isTypeSupported(cs[i])) return {mimeType:cs[i]}; }
1653
- return undefined;
1654
- }
1655
-
1656
- /* -------- boot -------- */
1657
- buildMatrix();
1658
- buildCovMatrix();
1659
- // Run all = honesty matrix THEN feature coverage (one click, both truths).
1660
- document.getElementById("run-all").addEventListener("click", function(){ runAll().then(runCoverage); });
1661
- document.getElementById("run-cov").addEventListener("click", function(){ runCoverage(); });
1662
-
1663
- // Auto modes for headless drivers (scripts/verify.mjs, scripts/record-replay.mjs).
1664
- // Replay mode is selected by the ROUTE (/verify/replay) or ?mode=replay.
1665
- var bootDone = false;
1666
- iframe.addEventListener("load", function(){
1667
- if (bootDone) return; bootDone = true; // the iframe reloads as we drive it; boot once
1668
- var isReplay = /\\/verify\\/replay/.test(location.pathname) || qs("mode")==="replay";
1669
- var auto = qs("auto");
1670
- if (isReplay){ openReplay(); if (auto!=="0") surfaceReady().then(function(){ setTimeout(startReplay, 400); }); }
1671
- else if (auto==="1"){ surfaceReady().then(function(){ setTimeout(function(){ runAll().then(runCoverage); }, 300); }); }
1672
- });
1673
- })();
1674
- </script>
1675
- </body>
1676
- </html>`;
1677
- //# sourceMappingURL=verify-page.js.map