codehere 1.0.1 → 1.0.2
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.
- package/README.md +36 -192
- package/dist/application/use-cases/check-claimed-tests.js +81 -0
- package/dist/application/use-cases/detect-test-weakening.js +237 -0
- package/dist/application/use-cases/rerun-tests.js +110 -7
- package/dist/index.js +38 -1212
- package/dist/infrastructure/orchestration/artifacts-extractor.js +2 -323
- package/dist/presentation/cli/commands/check-command.js +178 -23
- package/dist/presentation/cli/commands/help-command.js +21 -285
- package/dist/ui.js +5 -236
- package/dist/utils/version.js +0 -6
- package/package.json +9 -38
- package/dist/application/services/verdict-service.js +0 -154
- package/dist/application/use-cases/assess-cross-vendor.js +0 -126
- package/dist/application/use-cases/build-cross-vendor-verdict.js +0 -118
- package/dist/application/use-cases/domain-trust.js +0 -110
- package/dist/application/use-cases/export-aat.js +0 -107
- package/dist/application/use-cases/generate-acceptance-criteria.js +0 -223
- package/dist/application/use-cases/generate-final-report.js +0 -259
- package/dist/application/use-cases/generate-narration.js +0 -130
- package/dist/application/use-cases/generate-thread-title.js +0 -181
- package/dist/application/use-cases/narrator-prompt-template.js +0 -158
- package/dist/application/use-cases/plan-auto-review.js +0 -57
- package/dist/application/use-cases/reconcile-audit.js +0 -100
- package/dist/application/use-cases/search-sessions.js +0 -70
- package/dist/application/use-cases/should-auto-review.js +0 -71
- package/dist/application/use-cases/thread-export.js +0 -245
- package/dist/application/use-cases/verify-claim.js +0 -456
- package/dist/application/use-cases/write-pass.js +0 -277
- package/dist/domain/audit.js +0 -11
- package/dist/domain/services/codebase-detector.js +0 -85
- package/dist/domain/task.js +0 -91
- package/dist/error-handler.js +0 -139
- package/dist/infrastructure/completion/bash-completion.js +0 -29
- package/dist/infrastructure/completion/fish-completion.js +0 -17
- package/dist/infrastructure/completion/zsh-completion.js +0 -25
- package/dist/infrastructure/config/config-manager.js +0 -174
- package/dist/infrastructure/config/product-context.js +0 -49
- package/dist/infrastructure/context/adapters/drawio-source.js +0 -157
- package/dist/infrastructure/context/adapters/figma-source.js +0 -198
- package/dist/infrastructure/context/adapters/github-issue-source.js +0 -160
- package/dist/infrastructure/context/adapters/linear-source.js +0 -190
- package/dist/infrastructure/context/context-resolver.js +0 -144
- package/dist/infrastructure/context/context-source.js +0 -114
- package/dist/infrastructure/context/github-api.js +0 -110
- package/dist/infrastructure/context/github-device-flow.js +0 -65
- package/dist/infrastructure/context/oauth-flow.js +0 -207
- package/dist/infrastructure/context/oauth-store.js +0 -122
- package/dist/infrastructure/context/registry-bootstrap.js +0 -95
- package/dist/infrastructure/context/write-pass-resolver.js +0 -106
- package/dist/infrastructure/cost/budget-config.js +0 -114
- package/dist/infrastructure/cost/cost-router.js +0 -97
- package/dist/infrastructure/cost/spend-aggregator.js +0 -222
- package/dist/infrastructure/cost/team-ledger.js +0 -355
- package/dist/infrastructure/discovery/discovery-log.js +0 -85
- package/dist/infrastructure/hooks/audit-tip-signature.js +0 -336
- package/dist/infrastructure/hooks/claude-sdk-hook.js +0 -1018
- package/dist/infrastructure/hooks/hook-mode.js +0 -87
- package/dist/infrastructure/hooks/mirror-tool-call-to-audit.js +0 -49
- package/dist/infrastructure/hooks/pre-prompt-scan.js +0 -122
- package/dist/infrastructure/hooks/session-analyzer.js +0 -177
- package/dist/infrastructure/observability/execution-tracer.js +0 -227
- package/dist/infrastructure/orchestration/agent-adapter.js +0 -13
- package/dist/infrastructure/orchestration/agent-installer.js +0 -101
- package/dist/infrastructure/orchestration/agents-config-seed.js +0 -68
- package/dist/infrastructure/orchestration/aider-adapter.js +0 -611
- package/dist/infrastructure/orchestration/cassette-recorder.js +0 -120
- package/dist/infrastructure/orchestration/claude-code-adapter.js +0 -592
- package/dist/infrastructure/orchestration/codex-adapter.js +0 -574
- package/dist/infrastructure/orchestration/conductor-adapter.js +0 -416
- package/dist/infrastructure/orchestration/context-handoff.js +0 -162
- package/dist/infrastructure/orchestration/control/codex-control-surface.js +0 -99
- package/dist/infrastructure/orchestration/control/control-surface.js +0 -15
- package/dist/infrastructure/orchestration/detect-ollama.js +0 -75
- package/dist/infrastructure/orchestration/failover-classifier.js +0 -151
- package/dist/infrastructure/orchestration/failover-config.js +0 -67
- package/dist/infrastructure/orchestration/failover-policy.js +0 -106
- package/dist/infrastructure/orchestration/gemini-adapter.js +0 -663
- package/dist/infrastructure/orchestration/generic-cli-adapter.js +0 -689
- package/dist/infrastructure/orchestration/graphify-context.js +0 -209
- package/dist/infrastructure/orchestration/memory-injector.js +0 -380
- package/dist/infrastructure/orchestration/mocks/mock-agent-repl.js +0 -388
- package/dist/infrastructure/orchestration/mocks/mock-codex-app-server.js +0 -73
- package/dist/infrastructure/orchestration/mocks/mock-synthesis.js +0 -83
- package/dist/infrastructure/orchestration/mocks/resolve-agent-spawn.js +0 -37
- package/dist/infrastructure/orchestration/opencode-adapter.js +0 -580
- package/dist/infrastructure/orchestration/progress-aggregator.js +0 -906
- package/dist/infrastructure/orchestration/readiness.js +0 -103
- package/dist/infrastructure/orchestration/session-manager.js +0 -1590
- package/dist/infrastructure/orchestration/session-persistence.js +0 -394
- package/dist/infrastructure/orchestration/thread-key-store.js +0 -127
- package/dist/infrastructure/orchestration/write-pass-store.js +0 -38
- package/dist/infrastructure/protocols/cloud-sync.js +0 -207
- package/dist/infrastructure/protocols/mcp-helpers.js +0 -25
- package/dist/infrastructure/protocols/mcp-server-impl.js +0 -3935
- package/dist/infrastructure/protocols/mcp-tools.js +0 -401
- package/dist/infrastructure/protocols/probe-port.js +0 -36
- package/dist/infrastructure/protocols/thread-phase.js +0 -105
- package/dist/infrastructure/protocols/ui/app-script-artifacts.js +0 -121
- package/dist/infrastructure/protocols/ui/app-script-audit-footer.js +0 -44
- package/dist/infrastructure/protocols/ui/app-script-audit-verify.js +0 -56
- package/dist/infrastructure/protocols/ui/app-script-composer-cost.js +0 -588
- package/dist/infrastructure/protocols/ui/app-script-context-sources.js +0 -168
- package/dist/infrastructure/protocols/ui/app-script-conversation.js +0 -67
- package/dist/infrastructure/protocols/ui/app-script-drawer.js +0 -115
- package/dist/infrastructure/protocols/ui/app-script-failover.js +0 -133
- package/dist/infrastructure/protocols/ui/app-script-github.js +0 -113
- package/dist/infrastructure/protocols/ui/app-script-observer.js +0 -104
- package/dist/infrastructure/protocols/ui/app-script-onboarding.js +0 -80
- package/dist/infrastructure/protocols/ui/app-script-overlay.js +0 -203
- package/dist/infrastructure/protocols/ui/app-script-recovery.js +0 -136
- package/dist/infrastructure/protocols/ui/app-script-render.js +0 -166
- package/dist/infrastructure/protocols/ui/app-script-share.js +0 -78
- package/dist/infrastructure/protocols/ui/app-script-sidebar.js +0 -194
- package/dist/infrastructure/protocols/ui/app-script-tour.js +0 -78
- package/dist/infrastructure/protocols/ui/app-script-wpass.js +0 -312
- package/dist/infrastructure/protocols/ui/app-script.js +0 -2998
- package/dist/infrastructure/protocols/ui/font-faces.js +0 -7
- package/dist/infrastructure/protocols/ui/styles.js +0 -2147
- package/dist/infrastructure/protocols/ui/tokens.js +0 -96
- package/dist/infrastructure/protocols/ui-page-v2.js +0 -320
- package/dist/infrastructure/protocols/verify-page.js +0 -1677
- package/dist/infrastructure/security/file-collection.js +0 -118
- package/dist/infrastructure/security/fix-code.js +0 -144
- package/dist/infrastructure/security/fix-secret.js +0 -89
- package/dist/infrastructure/security/permissions-config.js +0 -235
- package/dist/infrastructure/security/scan-types.js +0 -10
- package/dist/infrastructure/security/ship-scan.js +0 -195
- package/dist/infrastructure/security/trust-scorer.js +0 -724
- package/dist/infrastructure/security/vibe-checks.js +0 -99
- package/dist/infrastructure/storage/atomic-write.js +0 -86
- package/dist/infrastructure/storage/cross-tool-agents.js +0 -268
- package/dist/infrastructure/storage/project-thread-migration.js +0 -237
- package/dist/infrastructure/storage/project-thread-mirror.js +0 -671
- package/dist/infrastructure/storage/project-thread-reader.js +0 -409
- package/dist/infrastructure/storage/project-thread-title-backfill.js +0 -214
- package/dist/monitoring.js +0 -212
- package/dist/presentation/cli/commands/audit-command.js +0 -201
- package/dist/presentation/cli/commands/budget-command.js +0 -79
- package/dist/presentation/cli/commands/config-command.js +0 -125
- package/dist/presentation/cli/commands/context-source-command.js +0 -217
- package/dist/presentation/cli/commands/hook-command.js +0 -265
- package/dist/presentation/cli/commands/init-command.js +0 -590
- package/dist/presentation/cli/commands/memory-command.js +0 -1031
- package/dist/presentation/cli/commands/narrator-command.js +0 -101
- package/dist/presentation/cli/commands/permission-command.js +0 -105
- package/dist/presentation/cli/commands/protocols-builder.js +0 -254
- package/dist/presentation/cli/commands/protocols-command.js +0 -84
- package/dist/presentation/cli/commands/repo-map-builder.js +0 -247
- package/dist/presentation/cli/commands/report-command.js +0 -403
- package/dist/presentation/cli/commands/scan-command.js +0 -52
- package/dist/presentation/cli/commands/scan-tool-command.js +0 -110
- package/dist/presentation/cli/commands/search-command.js +0 -51
- package/dist/presentation/cli/commands/setup-command.js +0 -330
- package/dist/presentation/cli/commands/status-command.js +0 -614
- package/dist/presentation/cli/commands/team-command.js +0 -256
- package/dist/presentation/cli/commands/thread-command.js +0 -370
- package/dist/presentation/cli/commands/trust-command.js +0 -328
- package/dist/presentation/cli/commands/verify-command.js +0 -298
- package/dist/presentation/cli/error-display.js +0 -328
- package/dist/presentation/cli/repl.js +0 -805
- package/dist/presentation/cli/task-deliverable.js +0 -153
- package/dist/testing/cassette.js +0 -125
- 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">● local · zero telemetry</span>
|
|
152
|
-
<span class="spacer"></span>
|
|
153
|
-
<span class="hint">agent handle: <code>window.__verify</code> — <code>__verify.manifest()</code> · <code>await __verify.runAll()</code></span>
|
|
154
|
-
</header>
|
|
155
|
-
|
|
156
|
-
<div class="wrap">
|
|
157
|
-
<div class="actions">
|
|
158
|
-
<button class="run" id="run-all">▶ Run all</button>
|
|
159
|
-
<button class="linkbtn" id="toggle-replay">▶ Replay all (slow-mo)</button>
|
|
160
|
-
<span class="tally" id="tally"><span class="t">not run yet — 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 & honest</span>
|
|
165
|
-
<span><b style="color:var(--caught)">FAIL</b> observed & lying</span>
|
|
166
|
-
<span><b style="color:var(--fg-40)">BLOCKED</b> 0 units to observe — 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> — reads data-verify-* off the rendered surface</span>
|
|
173
|
-
<span class="vchip"><code>invariants</code> — runs each honesty predicate against the DOM</span>
|
|
174
|
-
<span class="vchip"><code>counts</code> — claimed numbers must equal rendered elements</span>
|
|
175
|
-
</div>
|
|
176
|
-
|
|
177
|
-
<h2 class="section">Matrix · honesty invariant × 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 · v0 › v1 MVP — does each PRODUCT_STATE claim actually render?</h2>
|
|
182
|
-
<div class="actions">
|
|
183
|
-
<button class="run" id="run-cov">▶ 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 & 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 · 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 · <strong>session thread</strong> <span class="dim">/ —</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">▶ Play</button>
|
|
212
|
-
<button class="linkbtn" id="skip">Skip ›</button>
|
|
213
|
-
<button class="linkbtn" id="record">● 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 — 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+"\\">·</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+" · "+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)+"\\">·</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 + " · <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="▶ 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 = "▮▮ 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="▶ 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="● Record clips"; return; }
|
|
1627
|
-
navigator.mediaDevices.getDisplayMedia({ video:{frameRate:30}, audio:false, preferCurrentTab:true })
|
|
1628
|
-
.then(function(stream){
|
|
1629
|
-
recStream = stream; btn.innerHTML="● Recording \\u00b7 stop";
|
|
1630
|
-
stream.getVideoTracks()[0].addEventListener("ended", function(){ stopRecording(); btn.innerHTML="● 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="● 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
|