harness-everything 0.40.0 → 0.41.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (160) hide show
  1. package/.agents/harness-everything/project-docs.json +9 -0
  2. package/.claude-plugin/marketplace.json +1 -1
  3. package/.claude-plugin/plugin.json +1 -1
  4. package/CHANGELOG.md +43 -0
  5. package/benchmarks/results/f1-doc-path-baseline/1-source-clean.stderr.txt +0 -0
  6. package/benchmarks/results/f1-doc-path-baseline/1-source-clean.stdout.json +20 -0
  7. package/benchmarks/results/f1-doc-path-baseline/2-injected-architecture.md +229 -0
  8. package/benchmarks/results/f1-doc-path-baseline/2-source-injected.stderr.txt +0 -0
  9. package/benchmarks/results/f1-doc-path-baseline/2-source-injected.stdout.json +33 -0
  10. package/benchmarks/results/f1-doc-path-baseline/3-source-reverted.stderr.txt +0 -0
  11. package/benchmarks/results/f1-doc-path-baseline/3-source-reverted.stdout.json +20 -0
  12. package/benchmarks/results/f1-doc-path-baseline/4a-packaged-clean.stderr.txt +0 -0
  13. package/benchmarks/results/f1-doc-path-baseline/4a-packaged-clean.stdout.json +20 -0
  14. package/benchmarks/results/f1-doc-path-baseline/4b-packaged-injected.stderr.txt +0 -0
  15. package/benchmarks/results/f1-doc-path-baseline/4b-packaged-injected.stdout.json +33 -0
  16. package/benchmarks/results/f1-doc-path-baseline/5-source-missing-config.stderr.txt +0 -0
  17. package/benchmarks/results/f1-doc-path-baseline/5-source-missing-config.stdout.json +15 -0
  18. package/benchmarks/results/f1-doc-path-baseline/5b-packaged-missing-config.stderr.txt +0 -0
  19. package/benchmarks/results/f1-doc-path-baseline/5b-packaged-missing-config.stdout.json +15 -0
  20. package/benchmarks/results/f1-doc-path-baseline/README.md +54 -0
  21. package/benchmarks/results/f1-doc-path-baseline/before-fix/1-source-clean.stderr.txt +0 -0
  22. package/benchmarks/results/f1-doc-path-baseline/before-fix/1-source-clean.stdout.json +115 -0
  23. package/benchmarks/results/f1-doc-path-baseline/before-fix/2-injected-document.md +328 -0
  24. package/benchmarks/results/f1-doc-path-baseline/before-fix/2-source-injected.stderr.txt +0 -0
  25. package/benchmarks/results/f1-doc-path-baseline/before-fix/2-source-injected.stdout.json +127 -0
  26. package/benchmarks/results/f1-doc-path-baseline/before-fix/3-source-reverted.stderr.txt +0 -0
  27. package/benchmarks/results/f1-doc-path-baseline/before-fix/3-source-reverted.stdout.json +115 -0
  28. package/benchmarks/results/f1-doc-path-baseline/before-fix/4a-packaged-clean.stderr.txt +0 -0
  29. package/benchmarks/results/f1-doc-path-baseline/before-fix/4a-packaged-clean.stdout.json +115 -0
  30. package/benchmarks/results/f1-doc-path-baseline/before-fix/4b-packaged-injected.stderr.txt +0 -0
  31. package/benchmarks/results/f1-doc-path-baseline/before-fix/4b-packaged-injected.stdout.json +127 -0
  32. package/benchmarks/results/f1-doc-path-baseline/before-fix/5-source-missing-config.stderr.txt +0 -0
  33. package/benchmarks/results/f1-doc-path-baseline/before-fix/5-source-missing-config.stdout.json +15 -0
  34. package/benchmarks/results/f1-doc-path-baseline/before-fix/5b-packaged-missing-config.stderr.txt +0 -0
  35. package/benchmarks/results/f1-doc-path-baseline/before-fix/5b-packaged-missing-config.stdout.json +15 -0
  36. package/benchmarks/results/f1-doc-path-baseline/before-fix/6-supplementary-fixture-ok.input.md +8 -0
  37. package/benchmarks/results/f1-doc-path-baseline/before-fix/6-supplementary-fixture-ok.stdout.json +16 -0
  38. package/benchmarks/results/f1-doc-path-baseline/before-fix/README.md +34 -0
  39. package/benchmarks/results/f1-doc-path-baseline/probe-31-docs/after.json +248 -0
  40. package/benchmarks/results/f1-doc-path-baseline/probe-31-docs/before.json +254 -0
  41. package/benchmarks/results/f1-doc-path-baseline/probe-31-docs/probe.js +26 -0
  42. package/benchmarks/results/f2-doc-craft-baseline/README.md +32 -0
  43. package/benchmarks/results/f2-doc-craft-baseline/arm-f1-report.md +84 -0
  44. package/benchmarks/results/f2-doc-craft-baseline/arm-f1-task.md +21 -0
  45. package/benchmarks/results/f2-doc-craft-baseline/arm-f1.diff +19 -0
  46. package/benchmarks/results/f2-doc-craft-baseline/arm-f2-report.md +110 -0
  47. package/benchmarks/results/f2-doc-craft-baseline/arm-f2-task.md +21 -0
  48. package/benchmarks/results/f2-doc-craft-baseline/arm-f2.diff +19 -0
  49. package/benchmarks/results/f3-derived-views-baseline/README.md +34 -0
  50. package/benchmarks/results/f3-derived-views-baseline/arm-f2-report.md +43 -0
  51. package/benchmarks/results/f3-derived-views-baseline/arm-f2-task.md +21 -0
  52. package/benchmarks/results/f3-derived-views-baseline/arm-f2.diff +21 -0
  53. package/benchmarks/results/f3-derived-views-baseline/arm-f3-report.md +57 -0
  54. package/benchmarks/results/f3-derived-views-baseline/arm-f3-task.md +21 -0
  55. package/benchmarks/results/f3-derived-views-baseline/arm-f3.diff +21 -0
  56. package/benchmarks/results/live-host/claude-325-3b-s2b/README.md +99 -0
  57. package/benchmarks/results/live-host/claude-325-3b-s2b/capture.sanitized.jsonl +26 -0
  58. package/ci/archive/325-prep/README.md +71 -0
  59. package/ci/{prep → archive/325-prep}/doc-reconciliation-red.js +9 -7
  60. package/ci/archive/325-prep/f4-expected-red.json +34 -0
  61. package/ci/archive/325-prep/historical-qualification.test.js +143 -0
  62. package/ci/fixtures/doc-obligations/reconciliation-prep/cases.json +17 -4
  63. package/ci/fixtures/doc-obligations/reconciliation-prep/pending-red.json +8 -56
  64. package/ci/fixtures/doc-obligations/reconciliation-prep/trust-contract.json +2 -2
  65. package/ci/fixtures/doc-obligations/reconciliation-s1/cases.json +17 -5
  66. package/ci/fixtures/doc-obligations/reconciliation-s2b/capture/harness.js +415 -0
  67. package/ci/fixtures/doc-obligations/reconciliation-s2b/capture/off-handoff-golden.json +16 -0
  68. package/ci/fixtures/doc-obligations/reconciliation-s2b/capture/payload-post-tool-use-failure.json +11 -0
  69. package/ci/fixtures/doc-obligations/reconciliation-s2b/capture/payload-post-tool-use-success-minimal.json +9 -0
  70. package/ci/fixtures/doc-obligations/reconciliation-s2b/capture/payload-post-tool-use-success.json +13 -0
  71. package/ci/fixtures/doc-obligations/reconciliation-s2b/capture/payload-subagent-start.json +7 -0
  72. package/ci/fixtures/doc-obligations/reconciliation-s2b/capture/payload-subagent-stop-freetext-reject.json +11 -0
  73. package/ci/fixtures/doc-obligations/reconciliation-s2b/capture/payload-subagent-stop-reject.json +11 -0
  74. package/ci/fixtures/doc-obligations/reconciliation-s2b/capture/payload-subagent-stop.json +11 -0
  75. package/ci/fixtures/doc-obligations/reconciliation-s2b/capture/payload-task-result.json +19 -0
  76. package/ci/fixtures/doc-obligations/reconciliation-s2b/corpus/off-capture-only.json +99 -0
  77. package/ci/fixtures/doc-obligations/reconciliation-s2b/corpus/off-legacy.json +67 -0
  78. package/ci/fixtures/doc-obligations/reconciliation-s2b/corpus/on-concurrent-writers.json +62 -0
  79. package/ci/fixtures/doc-obligations/reconciliation-s2b/corpus/on-lookalikes-runtime-mutations.json +67 -0
  80. package/ci/fixtures/doc-obligations/reconciliation-s2b/corpus/on-malformed.json +85 -0
  81. package/ci/fixtures/doc-obligations/reconciliation-s2b/corpus/on-normal-session.json +74 -0
  82. package/ci/fixtures/doc-obligations/reconciliation-s2b/corpus/on-runtime-drift-later-npm-test.json +58 -0
  83. package/ci/fixtures/doc-obligations/reconciliation-s2b/corpus/on-valid-direct-quoted-review.json +85 -0
  84. package/ci/fixtures/doc-obligations/reconciliation-s2b/f4/cases.json +2267 -0
  85. package/ci/fixtures/doc-obligations/reconciliation-s2b/wiring/cases.json +1110 -0
  86. package/ci/fixtures/doc-obligations/reconciliation-s2b/wiring/legacy-off-golden.json +674 -0
  87. package/ci/lib/doc-reconciliation-file-spy.js +23 -0
  88. package/ci/lib/doc-reconciliation-prep-fixtures.js +136 -10
  89. package/ci/lib/doc-reconciliation-qualification-preload.js +37 -0
  90. package/ci/lib/doc-reconciliation-replay.js +104 -0
  91. package/ci/lib/doc-reconciliation-staged-tree.js +102 -0
  92. package/ci/mechanism-2p-opencode-v2-entrypoint.test.js +179 -0
  93. package/ci/mechanism-325-3b-preparation.test.js +27 -101
  94. package/ci/mechanism-325-3b-s2b-capture.test.js +561 -0
  95. package/ci/mechanism-325-3b-s2b-wiring.test.js +668 -0
  96. package/ci/mechanism-325-f1-doc-check.test.js +674 -0
  97. package/ci/mechanism-325-f2-doc-craft.test.js +597 -0
  98. package/ci/mechanism-325-f3-derived-views.test.js +798 -0
  99. package/ci/mechanism-325-f4-invariants.test.js +606 -0
  100. package/ci/mechanism-325-f4-projections.test.js +382 -0
  101. package/ci/mechanism-325-f4-qualification.test.js +228 -0
  102. package/ci/mechanism-325-f4-trust-boundary.test.js +880 -0
  103. package/ci/mutation-check.js +26 -3
  104. package/docs/architecture.md +5 -0
  105. package/docs/doc-workflow-reconciliation.md +331 -4
  106. package/docs/document-lifecycle.md +11 -0
  107. package/docs/governed-index.md +20 -0
  108. package/docs/platform-capabilities.md +22 -2
  109. package/grill-with-docs/SKILL.md +1 -1
  110. package/grill-with-docs/references/session-playbook.md +4 -0
  111. package/harness-everything/SKILL.md +1 -1
  112. package/harness-everything/references/doc-check.md +186 -0
  113. package/harness-everything/references/documentation/_shared/maintenance.md +41 -0
  114. package/harness-everything/references/documentation/_shared/writing-craft.md +59 -0
  115. package/harness-everything/references/triage-and-tiers.md +4 -0
  116. package/harness-everything/scripts/doc-check.js +442 -0
  117. package/harness-everything/scripts/doc-lifecycle.js +3 -0
  118. package/hooks/hooks.json +46 -0
  119. package/hooks/scripts/lib/doc-reconciliation-capture.sanitized.jsonl +26 -0
  120. package/hooks/scripts/lib/doc-reconciliation-pilot.js +171 -2
  121. package/hooks/scripts/lib/doc-reconciliation-provider.js +1059 -0
  122. package/hooks/scripts/lib/doc-reconciliation-qualification.json +34 -0
  123. package/hooks/scripts/lib/workflow-obligations.js +7 -0
  124. package/hooks/scripts/state-persist.js +78 -2
  125. package/hooks/scripts/workflow-stop-gate.js +7 -2
  126. package/opencode-plugin/README.md +15 -0
  127. package/opencode-plugin/index.mjs +77 -0
  128. package/opencode-plugin/plugin.json +1 -1
  129. package/package.json +1 -1
  130. package/plugins/harness-everything/.codex-plugin/plugin.json +1 -1
  131. package/plugins/harness-everything/hooks/scripts/lib/doc-reconciliation-capture.sanitized.jsonl +26 -0
  132. package/plugins/harness-everything/hooks/scripts/lib/doc-reconciliation-pilot.js +171 -2
  133. package/plugins/harness-everything/hooks/scripts/lib/doc-reconciliation-provider.js +1059 -0
  134. package/plugins/harness-everything/hooks/scripts/lib/doc-reconciliation-qualification.json +34 -0
  135. package/plugins/harness-everything/hooks/scripts/lib/workflow-obligations.js +7 -0
  136. package/plugins/harness-everything/hooks/scripts/state-persist.js +78 -2
  137. package/plugins/harness-everything/hooks/scripts/workflow-stop-gate.js +7 -2
  138. package/plugins/harness-everything/plugin.json +1 -1
  139. package/plugins/harness-everything/skills/grill-with-docs/SKILL.md +1 -1
  140. package/plugins/harness-everything/skills/grill-with-docs/references/session-playbook.md +4 -0
  141. package/plugins/harness-everything/skills/harness-everything/SKILL.md +1 -1
  142. package/plugins/harness-everything/skills/harness-everything/references/doc-check.md +186 -0
  143. package/plugins/harness-everything/skills/harness-everything/references/documentation/_shared/maintenance.md +41 -0
  144. package/plugins/harness-everything/skills/harness-everything/references/documentation/_shared/writing-craft.md +59 -0
  145. package/plugins/harness-everything/skills/harness-everything/references/triage-and-tiers.md +4 -0
  146. package/plugins/harness-everything/skills/harness-everything/scripts/doc-check.js +442 -0
  147. package/plugins/harness-everything/skills/harness-everything/scripts/doc-lifecycle.js +3 -0
  148. package/plugins/harness-everything/skills/repo-docs/SKILL.md +1 -1
  149. package/plugins/harness-everything/skills/repo-docs/references/process-guide.md +4 -0
  150. package/plugins/harness-everything/skills/to-spec/SKILL.md +1 -1
  151. package/plugins/harness-everything/skills/to-spec/references/process.md +4 -0
  152. package/plugins/harness-everything/skills/to-tickets/SKILL.md +1 -1
  153. package/plugins/harness-everything/skills/to-tickets/references/publishing-and-templates.md +4 -0
  154. package/repo-docs/SKILL.md +1 -1
  155. package/repo-docs/references/process-guide.md +4 -0
  156. package/sbom.cdx.json +7 -7
  157. package/to-spec/SKILL.md +1 -1
  158. package/to-spec/references/process.md +4 -0
  159. package/to-tickets/SKILL.md +1 -1
  160. package/to-tickets/references/publishing-and-templates.md +4 -0
@@ -0,0 +1,880 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ // #325 Phase 3b F4-T4: permanent trust-boundary cases from the F4-R reviewer report (F4-R-report.md).
5
+ //
6
+ // Each case is one of the reviewer's executed probes, turned into a permanent check and run through the real hook,
7
+ // workflow and Stop entries (source and packaged legs). Each check states the outcome the contract requires. A check
8
+ // that fails at the current HEAD is RED and names the F4-R finding it covers. F4-W5 closes those findings; when it
9
+ // lands, each RED becomes a strict CONTROL in the same change (the rule used in F4-T2 and F4-T3).
10
+ //
11
+ // Staged trees: every replay runs from a scratch copy of the leg, which carries the committed qualification export and
12
+ // its sibling sanitized capture (the shipped export), so the production gate is open exactly as it is for users.
13
+ //
14
+ // Interface (F4-W5): a review verdict binds the artifacts it reviewed with one line per path,
15
+ // `Reviewed artifact: <repo-relative path> sha256:<hex>`, in the final review artifact (the SubagentStop message).
16
+ //
17
+ // Statuses: CONTROL passes now and at GREEN. RED fails now for its finding. FAIL is an unexpected result, including a
18
+ // RED that already passes. INFRA is a harness fault. Strict by default; `--expect-red` exits 0 only when the RED plan
19
+ // matches.
20
+
21
+ const crypto = require('crypto');
22
+ const fs = require('fs');
23
+ const os = require('os');
24
+ const path = require('path');
25
+ const { spawnSync } = require('child_process');
26
+
27
+ const ROOT = path.resolve(__dirname, '..');
28
+ const fx = require(path.join(__dirname, 'lib', 'doc-reconciliation-prep-fixtures'));
29
+ const { stage, EXPORT_FILE, SIBLING_CAPTURE } = require(path.join(__dirname, 'lib', 'doc-reconciliation-staged-tree'));
30
+ const F4_CASES = path.join(__dirname, 'fixtures', 'doc-obligations', 'reconciliation-s2b', 'f4', 'cases.json');
31
+ const LEGS = ['source', 'packaged'];
32
+ const LEG_LIB = {
33
+ source: path.join(ROOT, 'hooks', 'scripts', 'lib'),
34
+ packaged: path.join(ROOT, 'plugins', 'harness-everything', 'hooks', 'scripts', 'lib'),
35
+ };
36
+
37
+ const POS_CASE = fx.readCaseCorpus(F4_CASES).cases.find(item => item.id === 'F4-POS-body-edit');
38
+ const clone = value => JSON.parse(JSON.stringify(value));
39
+ const sha256 = text => crypto.createHash('sha256').update(text).digest('hex');
40
+ // {{DOC_CHECK}} resolves per leg to the installed doc-check path, so a positive run is one the host would really run.
41
+ const LINT_COMMAND = 'node {{DOC_CHECK}} check --workspace . --config .agents/harness-everything/project-docs.json --changed-path docs/a.md';
42
+ const FM = '---\nrole: reference\nstatus: active\n---\n\n';
43
+ const BASE_A = FM + 'The checkout retries once. See [b](b.md).\n';
44
+ const CAND_NORMATIVE = FM + 'The checkout MUST retry twice. See [b](b.md).\n';
45
+ const REQUIREMENTS = [
46
+ { id: 'req-docs', stepType: 'doc-change', summary: 'Correct the checkout retry rule', acceptance: 'the governed reference body is reviewed' },
47
+ { id: 'req-review', stepType: 'review', summary: 'Review the change', acceptance: 'an independent host review approves it' },
48
+ { id: 'req-verify', stepType: 'verification', summary: 'Verify the change', acceptance: 'objective doc checks are recorded' },
49
+ ];
50
+ const REVIEW_ROUTE = { id: 'route', op: 'route', payload: { prompt: 'Update docs/a.md, review it independently, and verify it.' } };
51
+
52
+ // Copies the committed qualification export and its sibling capture into a staged tree (the shipped export).
53
+ function useShippedExport(staged) {
54
+ const lib = LEG_LIB[staged.leg];
55
+ fs.copyFileSync(path.join(lib, EXPORT_FILE), staged.exportFile);
56
+ fs.copyFileSync(path.join(lib, SIBLING_CAPTURE), staged.captureFile);
57
+ }
58
+
59
+ // The committed export must already bind the retained bytes (the probe depends on it being the shipped one).
60
+ function shippedExportPresent() {
61
+ return LEGS.every(leg => fs.existsSync(path.join(LEG_LIB[leg], EXPORT_FILE)) && fs.existsSync(path.join(LEG_LIB[leg], SIBLING_CAPTURE)));
62
+ }
63
+
64
+ let passCount = 0;
65
+ let redCount = 0;
66
+ let failCount = 0;
67
+ let infraCount = 0;
68
+
69
+ function record(status, id, detail) {
70
+ const suffix = detail ? ` - ${detail}` : '';
71
+ if (status === 'PASS') passCount += 1;
72
+ else if (status === 'RED') redCount += 1;
73
+ else if (status === 'FAIL') failCount += 1;
74
+ else infraCount += 1;
75
+ console.log(` ${status.padEnd(5)} ${id}${suffix}`);
76
+ }
77
+
78
+ // Each check is { id, status: 'CONTROL'|'RED', reason, test(root) -> { pass, actual } }.
79
+ function runChecks(label, root, checks) {
80
+ for (const check of checks) {
81
+ const id = `${label}.${check.id}`;
82
+ let result;
83
+ try {
84
+ result = check.test(root);
85
+ } catch (error) {
86
+ record('INFRA', id, `check threw: ${error.message}`);
87
+ continue;
88
+ }
89
+ if (check.status === 'CONTROL') {
90
+ record(result.pass ? 'PASS' : 'FAIL', id, result.pass ? '' : `got ${JSON.stringify(result.actual).slice(0, 180)}`);
91
+ } else if (result.pass) {
92
+ record('FAIL', id, 'RED check unexpectedly passed now (the finding is already fixed: retire it to CONTROL)');
93
+ } else {
94
+ record('RED', id, `${check.reason}; actual ${JSON.stringify(result.actual).slice(0, 160)}`);
95
+ }
96
+ }
97
+ }
98
+
99
+ function replay(caseDef, dist, options) {
100
+ const item = clone(caseDef);
101
+ const root = fx.replayCase(item, dist, options);
102
+ const faults = fx.infrastructureFaults(item, root, options.pilot === undefined ? 'off' : 'on');
103
+ if (faults.length) throw new Error(faults.map(fault => `${fault.actionId}: ${fault.message}`).join('; '));
104
+ return root;
105
+ }
106
+
107
+ const pilotOf = root => root.pilot || {};
108
+ // An accepted event carries its capability in its ref (`<ref>#<capability>`). Listing a capability is not accepting it.
109
+ const acceptedCapabilityOf = root => (pilotOf(root).acceptedRefs || []).map(ref => String(ref).split('#').pop());
110
+ const creditedOf = root => [...(pilotOf(root).creditedCapabilities || [])].sort();
111
+ const diagnosticsOf = root => pilotOf(root).diagnostics || [];
112
+
113
+ // ---- R1 and R2: the registered command must be this plugin's own doc-check, byte for byte (F4-R F1, F9) --------------
114
+
115
+ function partR1R2(leg) {
116
+ console.log(`\n== R1/R2 (${leg}): a self-authored or tampered doc-check earns nothing ==`);
117
+ const fakeRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'f4t4-fake-'));
118
+ const fakeScript = path.join(fakeRoot, 'harness-everything', 'scripts', 'doc-check.js');
119
+ const report = {
120
+ schema: 'harness.doc-check.check.v1',
121
+ command: 'harness.doc-check.check.v1',
122
+ lint: { exit: 0, evidence: {}, diagnostics: [] },
123
+ links: { checked: 1, broken: [] },
124
+ coverage: { unsupported: [] },
125
+ reminders: [],
126
+ capabilities: {
127
+ 'doc-lint': { state: 'pass', checkedPaths: ['docs/a.md'], skipped: [] },
128
+ 'link-check': { state: 'pass', checkedPaths: ['docs/a.md'], skipped: [] },
129
+ },
130
+ };
131
+ const stdout = JSON.stringify(report, null, 2) + '\n';
132
+ fs.mkdirSync(path.dirname(fakeScript), { recursive: true });
133
+ fs.writeFileSync(fakeScript, `#!/usr/bin/env node\nprocess.stdout.write(${JSON.stringify(stdout)});\n`, 'utf8');
134
+ const fakeCommand = `node ${fakeScript} check --workspace . --config .agents/harness-everything/project-docs.json --changed-path docs/a.md`;
135
+ const fakeCase = clone(POS_CASE);
136
+ fakeCase.id = 'F4T4-R1-fake-impl';
137
+ fakeCase.actions = fakeCase.actions.filter(action => action.id !== 'lint-run' && action.id !== 'post-lint')
138
+ .flatMap(action => (action.id === 'verify-pass'
139
+ ? [{ id: 'post-fake', op: 'hookDeliver', payload: { event: 'PostToolUse', command: fakeCommand, toolUseId: 'toolu_f4t4_fake_01', response: { stdout, stderr: '', interrupted: false } } }, action]
140
+ : [action]));
141
+ const staged = stage(leg);
142
+ try {
143
+ useShippedExport(staged);
144
+ const root = replay(fakeCase, staged.dist, { pilot: '1' });
145
+ const before = root.actions['doc-pass'].state.handoff || {};
146
+ const after = root.actions['post-fake'].state.handoff || {};
147
+ runChecks(`R1.fake-impl@${leg}`, root, [
148
+ { id: 'no-credit', status: 'CONTROL', reason: 'F4-R F1: a self-authored doc-check earns document credit (the path shape alone is matched)', test: () => ({ pass: pilotOf(root).pilotCredit === false, actual: pilotOf(root).pilotCredit }) },
149
+ { id: 'no-capability-accepted', status: 'CONTROL', reason: 'F4-R F1: capability events accepted from the self-authored implementation', test: () => ({ pass: (pilotOf(root).acceptedRefs || []).length === 0, actual: pilotOf(root).acceptedRefs }) },
150
+ { id: 'lastVerifyAt-unchanged', status: 'CONTROL', reason: 'F4-R F9: a non-registered path sets lastVerifyAt', test: () => ({ pass: before.lastVerifyAt === after.lastVerifyAt, actual: { before: before.lastVerifyAt, after: after.lastVerifyAt } }) },
151
+ ]);
152
+ } finally {
153
+ staged.cleanup();
154
+ fs.rmSync(fakeRoot, { recursive: true, force: true });
155
+ }
156
+
157
+ const tamperedCase = clone(POS_CASE);
158
+ const tampered = stage(leg);
159
+ try {
160
+ useShippedExport(tampered);
161
+ const script = leg === 'source'
162
+ ? path.join(tampered.root, 'harness-everything', 'scripts', 'doc-check.js')
163
+ : path.join(tampered.root, 'plugin', 'skills', 'harness-everything', 'scripts', 'doc-check.js');
164
+ fs.appendFileSync(script, '\n// tampered by the F4-T4 digest case\n', 'utf8');
165
+ const root = replay(tamperedCase, tampered.dist, { pilot: '1' });
166
+ const verdict = postLintVerdict(tampered, root);
167
+ runChecks(`R2.tampered-script@${leg}`, root, [
168
+ // Closed by F4-W8 (G0): the shipped doc-check is pinned, so a script changed before the run earns nothing.
169
+ { id: 'no-credit', status: 'CONTROL', reason: 'G0 (F4-A3): the shipped doc-check is not pinned (SHIPPED_DOC_CHECK_DIGEST absent), so a script changed before the run earns credit', test: () => ({ pass: pilotOf(root).pilotCredit === false, actual: pilotOf(root).pilotCredit }) },
170
+ { id: 'refused-implementation-pin-mismatch', status: 'CONTROL', reason: 'G0 (F4-A3): the provider refuses the tampered doc-check with implementation-pin-mismatch', test: () => ({ pass: verdict.status !== 'accepted' && codesOf(verdict).includes('implementation-pin-mismatch'), actual: { status: verdict.status, codes: codesOf(verdict) } }) },
171
+ ]);
172
+ } finally {
173
+ tampered.cleanup();
174
+ }
175
+ }
176
+
177
+ // ---- R3 to R5: the report states exactly what was credited, and why nothing else is (F4-R F2, F3, F12) ------------
178
+
179
+ function partR3R5(leg) {
180
+ console.log(`\n== R3/R4/R5 (${leg}): credit and diagnostics are exact ==`);
181
+ const staged = stage(leg);
182
+ try {
183
+ useShippedExport(staged);
184
+ const root = replay(POS_CASE, staged.dist, { pilot: '1' });
185
+ runChecks(`R3-R5.positive@${leg}`, root, [
186
+ { id: 'R3.credited-exactly-lint-and-links', status: 'CONTROL', reason: 'F4-R F2: the report lists capabilities that were never accepted or that are N/A', test: () => ({ pass: JSON.stringify(creditedOf(root)) === JSON.stringify(['doc-lint', 'link-check']), actual: creditedOf(root) }) },
187
+ { id: 'R4.no-unsatisfied-when-credited', status: 'CONTROL', reason: 'F4-R F3: pilotCredit is true while the report still lists unsatisfied obligations', test: () => ({ pass: (pilotOf(root).unsatisfied || []).length === 0, actual: pilotOf(root).unsatisfied }) },
188
+ { id: 'R5.diagnostics-carry-obligationId', status: 'CONTROL', reason: 'F4-R F12: diagnostics keep only the code and drop the obligation', test: () => ({ pass: diagnosticsOf(root).every(item => item && typeof item === 'object' && typeof item.obligationId === 'string'), actual: diagnosticsOf(root) }) },
189
+ ]);
190
+ } finally {
191
+ staged.cleanup();
192
+ }
193
+ }
194
+
195
+ // ---- R6 and R7: review independence and the correlated surface (F4-R F4, F5) ------------------------------------------
196
+
197
+ const VERDICT_APPROVE = 'Review verdict: approve\nSummary: The sentence is corrected and the link resolves.';
198
+
199
+ function reviewRoot(leg, extraActions, label) {
200
+ const item = clone(POS_CASE);
201
+ item.id = label;
202
+ item.requirements = clone(REQUIREMENTS);
203
+ item.actions = [
204
+ { id: 'route', op: 'route', payload: { prompt: 'Update docs/a.md, review it independently, and verify it.' } },
205
+ { id: 'plan', op: 'plan', payload: { requirements: '$requirements', strategy: 'iterative-single', evidence: 'bounded documentation loop' } },
206
+ { id: 'start', op: 'start', payload: {} },
207
+ { id: 'doc-pass', op: 'step', payload: { stepId: 'req-docs', disposition: 'pass', evidence: 'sentence corrected' } },
208
+ ...extraActions,
209
+ // Exit 2 is the honest outcome while the review is refused: verification cannot complete ahead of an unapproved review.
210
+ { id: 'verify-bind', op: 'binding', payload: { stepId: 'req-verify', bindingId: 'verification-loop', disposition: 'loaded', evidence: 'verification loop skill located' }, expectExit: { on: [0, 2] } },
211
+ { id: 'stop', op: 'stop' },
212
+ { id: 'stop-generic', op: 'stopGeneric' },
213
+ ];
214
+ return item;
215
+ }
216
+
217
+ // The Agent launch's correlated PostToolUse: the result surface that names the reviewer (F5). Its tool is not evidence.
218
+ const agentSurface = (id, agentId) => ({ id, op: 'hookDeliver', payload: { event: 'PostToolUse', toolName: 'Agent', command: 'agent-launch', toolUseId: `toolu_f4t4_${id}`, response: { stdout: '', stderr: '', interrupted: false, agentId } } });
219
+ const subStop = (agentId, message) => ({ id: 'sub-stop', op: 'hookDeliver', payload: { event: 'SubagentStop', agentId, agentType: 'doc-reviewer', lastAssistantMessage: message } });
220
+ const reviewPass = () => ({ id: 'review-pass', op: 'step', payload: { stepId: 'req-review', disposition: 'pass', evidence: 'reviewer approved' }, expectExit: { on: [0, 2] } });
221
+ // Approves and binds the reviewed artifact, so the only variable in each case is the one under test.
222
+ const BOUND_APPROVE = `${VERDICT_APPROVE}\nReviewed artifact: docs/a.md sha256:${sha256(CAND_NORMATIVE)}`;
223
+ const LINT_RUN = { id: 'lint-run', op: 'hostRun', payload: { command: 'doc-check', args: ['check', '--workspace', '.', '--config', '.agents/harness-everything/project-docs.json', '--changed-path', 'docs/a.md'] } };
224
+ const lintBy = agentId => ({ id: 'post-lint', op: 'hookDeliver', payload: { event: 'PostToolUse', command: LINT_COMMAND, toolUseId: 'toolu_f4t4_lint_r6', fromRun: 'lint-run', agentId } });
225
+
226
+ function partR6R7(leg) {
227
+ console.log(`\n== R6/R7 (${leg}): an executor or an unlabelled surface cannot approve the review ==`);
228
+ // Each case runs one review. `runs` are the reviewer's own tool calls; the review then has its Agent surface and verdict.
229
+ const reviewCase = (label, runs, agentId) => {
230
+ const staged = stage(leg);
231
+ try {
232
+ useShippedExport(staged);
233
+ const item = reviewRoot(leg, [...runs, agentSurface('agent-result', agentId), subStop(agentId, BOUND_APPROVE), reviewPass()], label);
234
+ return replay(item, staged.dist, { pilot: '1' });
235
+ } finally {
236
+ staged.cleanup();
237
+ }
238
+ };
239
+ const approved = root => root.actions['review-pass'].exitCode === 0;
240
+ const refused = root => root.actions['review-pass'].exitCode === 2;
241
+
242
+ // R6: the reviewer ran a registered doc-check, so the reviewer is an executor and cannot approve (F4-R F4).
243
+ const registeredRun = { id: 'agent-post', op: 'hookDeliver', payload: { event: 'PostToolUse', command: LINT_COMMAND, toolUseId: 'toolu_f4t4_agent', response: { stdout: '{}', stderr: '', interrupted: false, agentId: 'agent-9' } } };
244
+ const plainRun = { ...registeredRun, id: 'agent-post', payload: { ...registeredRun.payload, command: 'npm test', toolUseId: 'toolu_f4t4_plain' } };
245
+ const root6 = reviewCase('F4T4-R6-executor', [registeredRun], 'agent-9');
246
+ const root6Plain = reviewCase('F4T4-R6-plain-control', [plainRun], 'agent-9');
247
+ // The same executor rule with the registered run as real evidence: the reviewer's own doc-check run is refused.
248
+ const rootEvidence = reviewCase('F4T4-R6-evidence', [LINT_RUN, lintBy('agent-9')], 'agent-9');
249
+ const rootEvidenceOther = reviewCase('F4T4-R6-evidence-other-control', [LINT_RUN, lintBy('agent-other')], 'agent-9');
250
+ runChecks('R6.reviewer-is-executor@' + leg, root6, [
251
+ { id: 'registered-run-refused', status: 'CONTROL', reason: 'F4-R F4: the reviewer agent_id equals an executor of this task and the review is approved', test: () => ({ pass: refused(root6), actual: root6.actions['review-pass'].exitCode }) },
252
+ ]);
253
+ runChecks('R6.plain-tool-use-control@' + leg, root6Plain, [
254
+ { id: 'plain-tool-use-approved', status: 'CONTROL', reason: 'a reviewer whose tool use is not registered doc-check evidence stays independent', test: () => ({ pass: approved(root6Plain), actual: root6Plain.actions['review-pass'].exitCode }) },
255
+ ]);
256
+ runChecks('R6.evidence-run-refused@' + leg, rootEvidence, [
257
+ { id: 'reviewer-own-evidence-refused', status: 'CONTROL', reason: 'F4-R F4: a reviewer whose own registered doc-check run is evidence is an executor', test: () => ({ pass: refused(rootEvidence), actual: rootEvidence.actions['review-pass'].exitCode }) },
258
+ ]);
259
+ runChecks('R6.evidence-run-other-control@' + leg, rootEvidenceOther, [
260
+ { id: 'other-agent-evidence-approved', status: 'CONTROL', reason: 'the same run by another agent leaves the reviewer independent', test: () => ({ pass: approved(rootEvidenceOther), actual: rootEvidenceOther.actions['review-pass'].exitCode }) },
261
+ ]);
262
+
263
+ // R6 compound (A10 §1, owner decision 3): a heredoc whose body runs the checker is not a plain command. The narrow scanner
264
+ // skips the body, so trust must refuse it: the reviewer is an executor, and the command earns no generic verification.
265
+ // The escaped-hyphen spelling defeats a raw substring guard, so it is a separate witness. Its path is relative: the fixture
266
+ // only rewrites the installed-path token. Plain npm test controls keep both trust consumers working.
267
+ const compoundRun = (id, command, agentId) => ({ id, op: 'hookDeliver', payload: { event: 'PostToolUse', command, toolUseId: `toolu_f4t4_${id}`, response: { stdout: '', stderr: '', interrupted: false, ...(agentId ? { agentId } : {}) } } });
268
+ const HEREDOC_SUBST = `npm test <<EOF\n$(${LINT_COMMAND})\nEOF`;
269
+ const HEREDOC_ESCAPED = 'npm test <<EOF\n$(node scripts/doc\\-check.js check --workspace . --config .agents/harness-everything/project-docs.json --changed-path docs/a.md)\nEOF';
270
+ const compoundRoot = (label, run, agentId) => reviewCase(label, [run], agentId);
271
+ const root6Heredoc = compoundRoot('F4T4-R6-heredoc', compoundRun('agent-post', HEREDOC_SUBST, 'agent-9'), 'agent-9');
272
+ const root6Escaped = compoundRoot('F4T4-R6-escaped', compoundRun('agent-post', HEREDOC_ESCAPED, 'agent-9'), 'agent-9');
273
+ runChecks('R6.compound-reviewer-refused@' + leg, root6Heredoc, [
274
+ { id: 'heredoc-reviewer-refused', status: 'CONTROL', reason: 'A10 §1: a reviewer whose compound command runs the checker is an executor, not independent', test: () => ({ pass: refused(root6Heredoc), actual: root6Heredoc.actions['review-pass'].exitCode }) },
275
+ ]);
276
+ runChecks('R6.compound-escaped-reviewer-refused@' + leg, root6Escaped, [
277
+ { id: 'escaped-reviewer-refused', status: 'CONTROL', reason: 'A10 §1: the escaped-hyphen spelling of a compound checker run is refused the same way', test: () => ({ pass: refused(root6Escaped), actual: root6Escaped.actions['review-pass'].exitCode }) },
278
+ ]);
279
+ // Read-only reviewer control: a plain, non-compound read keeps the reviewer independent, so the refusals are specific. It has
280
+ // no doc-check run, so its non-reviewer diagnostics are the baseline for a run that names no doc-check at all.
281
+ const readRun = { id: 'agent-read', op: 'hookDeliver', payload: { event: 'PostToolUse', command: 'cat docs/a.md', toolUseId: 'toolu_f4t4_read', response: { stdout: '', stderr: '', interrupted: false, agentId: 'agent-9' } } };
282
+ const root6Read = reviewCase('F4T4-R6-read-only-control', [readRun], 'agent-9');
283
+ runChecks('R6.read-only-reviewer-control@' + leg, root6Read, [
284
+ { id: 'read-only-reviewer-approved', status: 'CONTROL', reason: 'a reviewer whose only tool use is a plain read stays independent', test: () => ({ pass: approved(root6Read), actual: root6Read.actions['review-pass'].exitCode }) },
285
+ ]);
286
+ // A literal quoted heredoc: the body is data, but the command is compound and names the checker, so trust still refuses it.
287
+ const LITERAL_HEREDOC = `npm test <<'EOF'\n${LINT_COMMAND}\nEOF`;
288
+ const root6Literal = compoundRoot('F4T4-R6-literal-heredoc', compoundRun('agent-post', LITERAL_HEREDOC, 'agent-9'), 'agent-9');
289
+ runChecks('R6.literal-heredoc-refused@' + leg, root6Literal, [
290
+ { id: 'literal-heredoc-reviewer-refused', status: 'CONTROL', reason: 'A10 §1: a literal quoted heredoc that names the checker is compound, so its reviewer is not independent', test: () => ({ pass: refused(root6Literal), actual: root6Literal.actions['review-pass'].exitCode }) },
291
+ { id: 'literal-heredoc-no-generic', status: 'CONTROL', reason: 'A10 §1: a literal quoted heredoc earns no generic verification', test: () => ({ pass: (handoffOf(root6Literal, 'agent-post').lastGenericVerifyAt ?? null) === null, actual: handoffOf(root6Literal, 'agent-post').lastGenericVerifyAt ?? null }) },
292
+ // Not a doc-check candidate: no unregistered-command diagnostic and no doc-lint acceptance for the literal record.
293
+ { id: 'literal-heredoc-no-doc-diagnostic', status: 'CONTROL', reason: 'A10 §1: a literal quoted heredoc is not a doc-check candidate: no unregistered-command diagnostic and no doc-lint acceptance', test: () => ({ pass: !diagnosticsOf(root6Literal).some(item => item && item.code === 'unregistered-command') && !acceptedCapabilityOf(root6Literal).includes('doc-lint'), actual: { codes: diagnosticsOf(root6Literal).map(item => (item && item.code) || item), accepted: acceptedCapabilityOf(root6Literal) } }) },
294
+ ]);
295
+ const genericRoot = (() => {
296
+ const staged = stage(leg);
297
+ try {
298
+ useShippedExport(staged);
299
+ const npmRun = { id: 'gen-npm', op: 'hookDeliver', payload: { event: 'PostToolUse', command: 'npm test', toolUseId: 'toolu_f4t4_gen_npm', response: { stdout: '', stderr: '', interrupted: false } } };
300
+ return replay(reviewRoot(leg, [npmRun, compoundRun('gen-heredoc', HEREDOC_SUBST), compoundRun('gen-escaped', HEREDOC_ESCAPED)], 'F4T4-R6-generic'), staged.dist, { pilot: '1' });
301
+ } finally {
302
+ staged.cleanup();
303
+ }
304
+ })();
305
+ const genericAt = id => handoffOf(genericRoot, id).lastGenericVerifyAt;
306
+ runChecks('R6.compound-no-generic@' + leg, genericRoot, [
307
+ { id: 'plain-npm-records-generic', status: 'CONTROL', reason: 'an ordinary npm test still records generic verification', test: () => ({ pass: typeof genericAt('gen-npm') === 'number', actual: genericAt('gen-npm') }) },
308
+ { id: 'heredoc-keeps-prior-generic', status: 'CONTROL', reason: 'A10 §1: a compound checker attempt adds no generic record and does not overwrite the prior valid one', test: () => ({ pass: typeof genericAt('gen-npm') === 'number' && genericAt('gen-heredoc') === genericAt('gen-npm'), actual: genericAt('gen-heredoc') }) },
309
+ { id: 'escaped-keeps-prior-generic', status: 'CONTROL', reason: 'A10 §1: the escaped-hyphen compound attempt adds no generic record', test: () => ({ pass: genericAt('gen-escaped') === genericAt('gen-npm'), actual: genericAt('gen-escaped') }) },
310
+ ]);
311
+
312
+ // R7: a SubagentStop with no Agent or Task result surface at all is not an approval (F4-R F5).
313
+ const noSurface = (() => {
314
+ const staged = stage(leg);
315
+ try {
316
+ useShippedExport(staged);
317
+ return replay(reviewRoot(leg, [subStop('agent-9', BOUND_APPROVE), reviewPass()], 'F4T4-R7-no-surface'), staged.dist, { pilot: '1' });
318
+ } finally {
319
+ staged.cleanup();
320
+ }
321
+ })();
322
+ runChecks(`R7.no-agent-surface@${leg}`, noSurface, [
323
+ { id: 'not-approved', status: 'CONTROL', reason: 'F4-R F5: a review is approved with no correlated Agent/Task result surface', test: () => ({ pass: refused(noSurface), actual: noSurface.actions['review-pass'].exitCode }) },
324
+ ]);
325
+ }
326
+
327
+ // ---- R8 and R9: an independent review bound to the current candidate, and a stale binding (F4-R F6) ------------------
328
+
329
+ function partR8R9(leg) {
330
+ console.log(`\n== R8/R9 (${leg}): an independent review bound to the candidate is credited; a stale binding is not ==`);
331
+ // Quality-review is a normative-change obligation, and only prd and adr documents carry normative changes (a requirement
332
+ // acceptance edit is one). A reference body is never normative, so these cases govern docs/a.md as a draft prd.
333
+ const PRD = acceptance => `---\nrole: prd\nid: PRD-GRPH-001\nrevision: 1\nstatus: draft\n---\n\n### REQ-GRPH-010: Checkout retry\n\nAcceptance: the checkout retries ${acceptance}.\n`;
334
+ const PRD_BASE = PRD('once');
335
+ const PRD_CAND = PRD('twice');
336
+ const PRD_CONFIG = JSON.stringify({ governed: [{ path: 'docs/a.md', role: 'prd' }], domains: ['GRPH'] }, null, 2) + '\n';
337
+ const candidateDigest = sha256(PRD_CAND);
338
+ const staleDigest = sha256(PRD_BASE);
339
+ const normativeCase = (label, digest) => {
340
+ const item = reviewRoot(leg, [
341
+ LINT_RUN,
342
+ { id: 'post-lint', op: 'hookDeliver', payload: { event: 'PostToolUse', command: LINT_COMMAND, toolUseId: 'toolu_f4t4_check_r8', fromRun: 'lint-run' } },
343
+ agentSurface('agent-result', 'agent-7'),
344
+ subStop('agent-7', `${VERDICT_APPROVE}\nReviewed artifact: docs/a.md sha256:${digest}`),
345
+ { id: 'review-pass', op: 'step', payload: { stepId: 'req-review', disposition: 'pass', evidence: 'independent reviewer approved' } },
346
+ { id: 'verify-pass', op: 'step', payload: { stepId: 'req-verify', disposition: 'pass', evidence: 'objective doc checks recorded' }, expectExit: { on: [0, 2] } },
347
+ ], label);
348
+ item.actions = item.actions.map(action => (action.id === 'doc-pass'
349
+ ? { ...action, payload: { ...action.payload, evidence: 'normative sentence corrected' } }
350
+ : action));
351
+ return item;
352
+ };
353
+ // The normative edit goes to the candidate tree; the base is the committed one.
354
+ const candidateFiles = { 'docs/a.md': PRD_CAND };
355
+ const baseFiles = { ...clone(POS_CASE.input.baseFiles), 'docs/a.md': PRD_BASE, '.agents/harness-everything/project-docs.json': PRD_CONFIG };
356
+ for (const [label, digest, expectCredit] of [['F4T4-R8-bound', candidateDigest, true], ['F4T4-R9-stale', staleDigest, false]]) {
357
+ const staged = stage(leg);
358
+ try {
359
+ useShippedExport(staged);
360
+ const item = normativeCase(label, digest);
361
+ item.input = { baseFiles, candidateFiles };
362
+ const root = replay(item, staged.dist, { pilot: '1' });
363
+ // Non-vacuity: the independent review really reached the chain, so a missing acceptance is the finding, not a broken set-up.
364
+ const reviewCapture = (root.actions['sub-stop'].state.capture || { records: [] }).records.find(record => record.event === 'SubagentStop') || {};
365
+ const setup = [
366
+ { id: 'review-captured', status: 'CONTROL', reason: '', test: () => ({ pass: reviewCapture.reviewVerdict === 'approve' && reviewCapture.agent_id === 'agent-7', actual: { verdict: reviewCapture.reviewVerdict, agent: reviewCapture.agent_id } }) },
367
+ { id: 'review-step-passes', status: 'CONTROL', reason: '', test: () => ({ pass: root.actions['review-pass'].exitCode === 0, actual: root.actions['review-pass'].exitCode }) },
368
+ ];
369
+ const checks = setup.concat(expectCredit
370
+ ? [
371
+ // Closed by F4-W7 (G1-G3): the review reaches the credit chain, and quality-review is accepted. conflict-check has no
372
+ // producer, so pilotCredit stays false by design; this check claims quality-review only.
373
+ { id: 'R8.quality-review-accepted', status: 'CONTROL', reason: 'F4-R F6: a bound independent review is accepted as quality-review (the review enters the credit chain)', test: () => ({ pass: acceptedCapabilityOf(root).includes('quality-review'), actual: { accepted: acceptedCapabilityOf(root) } }) },
374
+ ]
375
+ : [
376
+ { id: 'R9.quality-review-not-accepted', status: 'CONTROL', reason: '', test: () => ({ pass: !acceptedCapabilityOf(root).includes('quality-review'), actual: acceptedCapabilityOf(root) }) },
377
+ // Closed by F4-W7 (G1-G3): the stale review reaches the events adapter, which reports artifact-mismatch.
378
+ { id: 'R9.artifact-mismatch-reported', status: 'CONTROL', reason: 'F4-R F6: a stale reviewed digest is reported as artifact-mismatch (the review enters the credit chain)', test: () => ({ pass: diagnosticsOf(root).some(item => item && item.code === 'artifact-mismatch'), actual: diagnosticsOf(root).map(item => (item && item.code) || item) }) },
379
+ ]);
380
+ runChecks(`${label}@${leg}`, root, checks);
381
+ } finally {
382
+ staged.cleanup();
383
+ }
384
+ }
385
+ }
386
+
387
+ // ---- M2 (F4-T6): the doc-check file changes between capture and assessment --------------------------------------------
388
+ // Capture records the installed doc-check digest; assessment recomputes it. A change after capture must withhold credit
389
+ // with `implementation-digest-mismatch`. The control is the same flow with no change, and it must still be credited.
390
+
391
+ const M2_EDIT = { id: 'edit-check', op: 'editDocCheck', payload: { append: '\n// edited after capture (F4-T6 M2)\n' } };
392
+
393
+ // The provider's verdict on the captured record, from the staged leg's own provider module (the pilot drops rejections from
394
+ // its report, so the rejection code is read here). The staged tree must still exist.
395
+ function providerVerdict(staged, record) {
396
+ const provider = require(path.join(staged.libDir, 'doc-reconciliation-provider.js'));
397
+ const identity = record.observedIdentity;
398
+ const origin = provider.createClaudeOriginProvider({
399
+ hostId: 'claude-code',
400
+ sessionId: record.session_id,
401
+ workspaceRoot: record.cwd,
402
+ workflowId: identity.workflowId,
403
+ workflowRevision: identity.workflowRevision,
404
+ taskId: 'task-f4t6-m2',
405
+ principals: { author: record.session_id, executors: [] },
406
+ baseline: identity.baselinePin,
407
+ captures: [record],
408
+ });
409
+ return origin.resolveEvent({ reference: `${record.ref}#doc-lint`, identity: null });
410
+ }
411
+
412
+ const codesOf = verdict => (verdict.diagnostics || []).map(item => item && item.code);
413
+
414
+ // The provider's verdict on the post-lint capture of a replay (the staged tree must still exist).
415
+ function postLintVerdict(staged, root) {
416
+ const record = ((root.actions['post-lint'].state.capture || { records: [] }).records || []).find(r => r.event === 'PostToolUse' && r.ref);
417
+ return record ? providerVerdict(staged, record) : { status: 'missing-record', diagnostics: [] };
418
+ }
419
+
420
+ function partM2(leg) {
421
+ console.log(`\n== M2 (${leg}): the installed doc-check changed after capture earns nothing ==`);
422
+ const cases = [['F4T6-M2-control', false], ['F4T6-M2-edited', true]];
423
+ for (const [label, edited] of cases) {
424
+ const staged = stage(leg);
425
+ try {
426
+ useShippedExport(staged);
427
+ const item = clone(POS_CASE);
428
+ item.id = label;
429
+ item.actions = item.actions
430
+ .flatMap(action => (action.id === 'post-lint' && edited ? [action, M2_EDIT] : [action]))
431
+ .map(action => (action.id === 'verify-pass' ? { ...action, expectExit: { on: [0, 2] } } : action));
432
+ const root = replay(item, staged.dist, { pilot: '1' });
433
+ const record = ((root.actions['post-lint'].state.capture || { records: [] }).records || []).find(r => r.event === 'PostToolUse' && r.ref);
434
+ const verdict = record ? providerVerdict(staged, record) : { status: 'missing-record', diagnostics: [] };
435
+ const codes = (verdict.diagnostics || []).map(item => item && item.code);
436
+ if (edited) {
437
+ runChecks(`${label}@${leg}`, root, [
438
+ { id: 'no-credit', status: 'CONTROL', reason: 'F4-R M2: a doc-check changed after capture must earn no document credit', test: () => ({ pass: pilotOf(root).pilotCredit === false && !creditedOf(root).includes('doc-lint'), actual: creditedOf(root) }) },
439
+ { id: 'refused-implementation-digest-mismatch', status: 'CONTROL', reason: 'F4-R M2: assessment refuses the capture with implementation-digest-mismatch', test: () => ({ pass: verdict.status !== 'accepted' && codes.includes('implementation-digest-mismatch'), actual: { status: verdict.status, codes } }) },
440
+ ]);
441
+ } else {
442
+ runChecks(`${label}@${leg}`, root, [
443
+ { id: 'credited-lint-and-links', status: 'CONTROL', reason: 'the unchanged doc-check keeps its credit (the paired control for M2)', test: () => ({ pass: JSON.stringify(creditedOf(root)) === JSON.stringify(['doc-lint', 'link-check']), actual: creditedOf(root) }) },
444
+ { id: 'accepted-unchanged-check', status: 'CONTROL', reason: 'the provider accepts the same record when nothing changed', test: () => ({ pass: verdict.status === 'accepted', actual: { status: verdict.status, codes } }) },
445
+ ]);
446
+ }
447
+ } finally {
448
+ staged.cleanup();
449
+ }
450
+ }
451
+ }
452
+
453
+ // ---- G0 (F4-T7): the shipped doc-check is pinned by canonical digest (F4-A3) ------------------------------------------
454
+ // The provider must export SHIPPED_DOC_CHECK_DIGEST: the canonical SHA-256 (UTF-8, CRLF normalised to LF) of the shipped
455
+ // harness-everything/scripts/doc-check.js. F4-W8 implements the pin (closed): a script changed before its run is refused
456
+ // with implementation-pin-mismatch, and an unchanged or CRLF copy stays accepted. These checks assert it and must not be relaxed.
457
+
458
+ const SOURCE_DOC_CHECK = path.join(ROOT, 'harness-everything', 'scripts', 'doc-check.js');
459
+ const PACKAGED_DOC_CHECK = path.join(ROOT, 'plugins', 'harness-everything', 'skills', 'harness-everything', 'scripts', 'doc-check.js');
460
+ const canonicalDigest = file => sha256(fs.readFileSync(file, 'utf8').replace(/\r\n/g, '\n'));
461
+ const bytesDigest = file => crypto.createHash('sha256').update(fs.readFileSync(file)).digest('hex');
462
+ const EXPECTED_PIN = canonicalDigest(SOURCE_DOC_CHECK);
463
+ const pinOf = leg => require(path.join(LEG_LIB[leg], 'doc-reconciliation-provider.js')).SHIPPED_DOC_CHECK_DIGEST;
464
+ const rePinHint = leg => `set SHIPPED_DOC_CHECK_DIGEST = '${EXPECTED_PIN}' in ${path.relative(ROOT, path.join(LEG_LIB[leg], 'doc-reconciliation-provider.js'))}`;
465
+ const shippedDocCheck = leg => (leg === 'source' ? SOURCE_DOC_CHECK : PACKAGED_DOC_CHECK);
466
+
467
+ // Replays the positive case against a staged copy of the leg, after an optional change to its installed doc-check.
468
+ // Returns the replay root and the provider's verdict on its post-lint capture.
469
+ function g0Replay(leg, mutate) {
470
+ const staged = stage(leg);
471
+ try {
472
+ useShippedExport(staged);
473
+ if (mutate) mutate(staged.dist.docCheck);
474
+ const root = replay(clone(POS_CASE), staged.dist, { pilot: '1' });
475
+ return { root, verdict: postLintVerdict(staged, root) };
476
+ } finally {
477
+ staged.cleanup();
478
+ }
479
+ }
480
+
481
+ function partG0() {
482
+ console.log('\n== G0 (both legs): the shipped doc-check is pinned by canonical digest (F4-A3) ==');
483
+ for (const leg of LEGS) {
484
+ const pin = pinOf(leg);
485
+ const pinned = pin === undefined ? null : pin;
486
+ runChecks(`G0.pin@${leg}`, undefined, [
487
+ { id: 'pin-matches-shipped-script', status: 'CONTROL', reason: 'G0 (F4-A3): SHIPPED_DOC_CHECK_DIGEST is absent from the provider, so the shipped doc-check is not pinned', test: () => ({ pass: pin === EXPECTED_PIN, actual: { pin: pinned, expected: EXPECTED_PIN, rePin: rePinHint(leg) } }) },
488
+ ]);
489
+ const staged = stage(leg);
490
+ try {
491
+ useShippedExport(staged);
492
+ runChecks(`G0.install@${leg}`, undefined, [
493
+ { id: 'staged-install-is-shipped', status: 'CONTROL', reason: '', test: () => ({ pass: bytesDigest(staged.dist.docCheck) === bytesDigest(shippedDocCheck(leg)), actual: staged.dist.docCheck }) },
494
+ ]);
495
+ } finally {
496
+ staged.cleanup();
497
+ }
498
+ }
499
+ runChecks('G0.legs@source', undefined, [
500
+ { id: 'pins-agree-across-legs', status: 'CONTROL', reason: 'G0 (F4-A3): the source and packaged providers must carry the same pin (both absent now)', test: () => ({ pass: typeof pinOf('source') === 'string' && pinOf('source') === pinOf('packaged'), actual: { source: pinOf('source') === undefined ? null : pinOf('source'), packaged: pinOf('packaged') === undefined ? null : pinOf('packaged') } }) },
501
+ ]);
502
+
503
+ for (const leg of LEGS) {
504
+ const shipped = g0Replay(leg, null);
505
+ const crlf = g0Replay(leg, file => fs.writeFileSync(file, fs.readFileSync(file, 'utf8').replace(/\n/g, '\r\n'), 'utf8'));
506
+ const oneByte = g0Replay(leg, file => fs.appendFileSync(file, '\n', 'utf8'));
507
+ const credited = result => JSON.stringify(creditedOf(result.root)) === JSON.stringify(['doc-lint', 'link-check']);
508
+ runChecks(`G0.positive@${leg}`, undefined, [
509
+ { id: 'shipped-script-credited', status: 'CONTROL', reason: '', test: () => ({ pass: credited(shipped), actual: creditedOf(shipped.root) }) },
510
+ { id: 'shipped-record-accepted', status: 'CONTROL', reason: 'the provider accepts the unmodified shipped script (non-vacuity for the refusals below)', test: () => ({ pass: shipped.verdict.status === 'accepted', actual: { status: shipped.verdict.status, codes: codesOf(shipped.verdict) } }) },
511
+ { id: 'crlf-copy-credited', status: 'CONTROL', reason: 'a CRLF copy of the shipped script has the same canonical digest, so it stays accepted once pinned', test: () => ({ pass: credited(crlf), actual: creditedOf(crlf.root) }) },
512
+ { id: 'crlf-record-accepted', status: 'CONTROL', reason: 'the provider accepts a CRLF copy of the shipped script (same canonical digest)', test: () => ({ pass: crlf.verdict.status === 'accepted', actual: { status: crlf.verdict.status, codes: codesOf(crlf.verdict) } }) },
513
+ ]);
514
+ runChecks(`G0.one-byte@${leg}`, undefined, [
515
+ { id: 'one-byte-change-refused', status: 'CONTROL', reason: 'G0 (F4-A3): a one-byte change to the shipped doc-check before its run is still credited (no pin)', test: () => ({ pass: pilotOf(oneByte.root).pilotCredit === false && !creditedOf(oneByte.root).includes('doc-lint'), actual: creditedOf(oneByte.root) }) },
516
+ { id: 'refused-implementation-pin-mismatch', status: 'CONTROL', reason: 'G0 (F4-A3): the provider refuses a one-byte-changed doc-check with implementation-pin-mismatch', test: () => ({ pass: oneByte.verdict.status !== 'accepted' && codesOf(oneByte.verdict).includes('implementation-pin-mismatch'), actual: { status: oneByte.verdict.status, codes: codesOf(oneByte.verdict) } }) },
517
+ ]);
518
+ }
519
+ }
520
+
521
+ // ---- R3 (F4-T9): a normal doc-edit session, and a one-byte installed change (F4-R3, luna's e2e) ---------------------------
522
+ // R3-01 (P1): an ordinary Edit and the Agent result are captured as doc refs. Those refs produce unregistered-command
523
+ // diagnostics, so credit is withheld and the verify step refuses a session that the real doc-check and reviewer support.
524
+ // R3-02 (P3): a one-byte change to the installed doc-check, before its run or after capture, still advances lastVerifyAt,
525
+ // which suppresses the generic verification reminder. Each RED has a paired control.
526
+
527
+ const EDIT_FILE = { id: 'edit-doc', op: 'hookDeliver', payload: { event: 'PostToolUse', toolName: 'Edit', command: '', toolUseId: 'toolu_r3_edit', response: { stdout: '', stderr: '', interrupted: false } } };
528
+ const EDIT_CHECK = { id: 'edit-check', op: 'editDocCheck', payload: { append: '\n' } };
529
+
530
+ // A normal doc-edit session: the Edit, the real doc-check, and an independent reviewer with its Agent surface.
531
+ function r3ReviewCase(label, withEdit) {
532
+ const item = clone(POS_CASE);
533
+ item.id = label;
534
+ item.requirements = clone(REQUIREMENTS);
535
+ const reviewed = `${VERDICT_APPROVE}\nReviewed artifact: docs/a.md sha256:${sha256(POS_CASE.input.candidateFiles['docs/a.md'])}`;
536
+ item.actions = [
537
+ { id: 'route', op: 'route', payload: { prompt: 'Update docs/a.md, review it independently, and verify it.' } },
538
+ { id: 'plan', op: 'plan', payload: { requirements: '$requirements', strategy: 'iterative-single', evidence: 'bounded documentation loop' } },
539
+ { id: 'start', op: 'start', payload: {} },
540
+ { id: 'doc-pass', op: 'step', payload: { stepId: 'req-docs', disposition: 'pass', evidence: 'sentence corrected' } },
541
+ ...(withEdit ? [EDIT_FILE] : []),
542
+ LINT_RUN,
543
+ { id: 'post-lint', op: 'hookDeliver', payload: { event: 'PostToolUse', command: LINT_COMMAND, toolUseId: 'toolu_r3_lint', fromRun: 'lint-run' } },
544
+ { id: 'agent-pre', op: 'hookDeliver', payload: { event: 'PreToolUse', toolName: 'Agent', command: 'agent-launch', toolUseId: 'toolu_r3_agent_pre' } },
545
+ { id: 'sub-start', op: 'hookDeliver', payload: { event: 'SubagentStart', agentId: 'agent-r3-rev', agentType: 'doc-reviewer' } },
546
+ agentSurface('agent-result', 'agent-r3-rev'),
547
+ subStop('agent-r3-rev', reviewed),
548
+ reviewPass(),
549
+ { id: 'verify-bind', op: 'binding', payload: { stepId: 'req-verify', bindingId: 'verification-loop', disposition: 'loaded', evidence: 'verification loop skill located' }, expectExit: { on: [0, 2] } },
550
+ { id: 'verify-pass', op: 'step', payload: { stepId: 'req-verify', disposition: 'pass', evidence: 'objective doc check recorded' }, expectExit: { on: [0, 2] } },
551
+ { id: 'stop', op: 'stop' },
552
+ { id: 'stop-generic', op: 'stopGeneric' },
553
+ ];
554
+ return item;
555
+ }
556
+
557
+ // The positive doc-edit session with an ordinary Edit before the run, and an optional one-byte change to the installed script.
558
+ function r3TamperCase(label, where) {
559
+ const item = clone(POS_CASE);
560
+ item.id = label;
561
+ const acts = [];
562
+ for (const action of item.actions) {
563
+ if (action.id === 'verify-bind') acts.push(EDIT_FILE);
564
+ if (action.id === 'lint-run' && where === 'before-run') acts.push(EDIT_CHECK);
565
+ acts.push(action);
566
+ if (action.id === 'post-lint' && where === 'after-capture') acts.push(EDIT_CHECK);
567
+ }
568
+ item.actions = acts;
569
+ return item;
570
+ }
571
+
572
+ const handoffOf = (root, id) => (root.actions[id] && root.actions[id].state && root.actions[id].state.handoff) || {};
573
+ const reminderOf = root => (root.actions['stop-generic'] && root.actions['stop-generic'].stderr) || '';
574
+ const unregisteredCount = root => diagnosticsOf(root).filter(item => item && item.code === 'unregistered-command').length;
575
+
576
+ // A doc-edit session with no assessment: no verify step runs, so only state-persist decides lastVerifyAt. Used by the
577
+ // M6 probe (F4-W9 pin in state-persist): a one-byte-changed doc-check must not advance lastVerifyAt, so the generic Stop
578
+ // still reminds. The control is the unmodified doc-check, which sets lastVerifyAt and suppresses the reminder.
579
+ function r3NoAssessCase(label, tampered) {
580
+ const item = clone(POS_CASE);
581
+ item.id = label;
582
+ const keep = new Set(['route', 'plan', 'start', 'doc-pass', 'lint-run', 'post-lint', 'stop-generic']);
583
+ const acts = [];
584
+ for (const action of item.actions) {
585
+ if (!keep.has(action.id)) continue;
586
+ if (action.id === 'lint-run') { acts.push(EDIT_FILE); if (tampered) acts.push(EDIT_CHECK); }
587
+ acts.push(action);
588
+ }
589
+ item.actions = acts;
590
+ return item;
591
+ }
592
+
593
+ function partR3(leg) {
594
+ console.log(`\n== R3 (${leg}): a normal doc-edit session, and a one-byte installed change ==`);
595
+ const runCase = item => {
596
+ const staged = stage(leg);
597
+ try {
598
+ useShippedExport(staged);
599
+ return replay(item, staged.dist, { pilot: '1' });
600
+ } finally {
601
+ staged.cleanup();
602
+ }
603
+ };
604
+
605
+ const session = runCase(r3ReviewCase(`F4T9-R3-01-edit@${leg}`, true));
606
+ const agentOnly = runCase(r3ReviewCase(`F4T9-R3-01-agent@${leg}`, false));
607
+ runChecks(`R3-01.normal-edit-session@${leg}`, session, [
608
+ { id: 'verify-step-passes', status: 'CONTROL', reason: 'R3-01 (P1): the ordinary Edit and the Agent result become doc refs, which withholds credit, so the verify step exits 2; closed by F4-W9 (4489376)', test: () => ({ pass: session.actions['verify-pass'].exitCode === 0, actual: session.actions['verify-pass'].exitCode }) },
609
+ { id: 'credited-exactly-lint-and-links', status: 'CONTROL', reason: 'R3-01 (P1): a normal edit session withholds doc-lint and link-check credit (unregistered-command diagnostics from the Edit and Agent records); closed by F4-W9 (4489376)', test: () => ({ pass: JSON.stringify(creditedOf(session)) === JSON.stringify(['doc-lint', 'link-check']), actual: creditedOf(session) }) },
610
+ { id: 'no-unregistered-command-from-edit-or-agent', status: 'CONTROL', reason: 'R3-01 (P1): the Edit and Agent PostToolUse records produce unregistered-command diagnostics (four observed: two from the Edit, two from the Agent result); closed by F4-W9 (4489376)', test: () => ({ pass: unregisteredCount(session) === 0, actual: unregisteredCount(session) }) },
611
+ { id: 'reviewer-surface-approved', status: 'CONTROL', reason: '', test: () => ({ pass: session.actions['review-pass'].exitCode === 0, actual: session.actions['review-pass'].exitCode }) },
612
+ { id: 'registered-run-accepted', status: 'CONTROL', reason: '', test: () => ({ pass: ['doc-lint', 'link-check'].every(c => acceptedCapabilityOf(session).includes(c)), actual: acceptedCapabilityOf(session) }) },
613
+ ]);
614
+ runChecks(`R3-01.agent-result-only@${leg}`, agentOnly, [
615
+ { id: 'no-unregistered-command-from-agent', status: 'CONTROL', reason: 'R3-01 (P1): the Agent result PostToolUse alone produces unregistered-command diagnostics (two observed); closed by F4-W9 (4489376)', test: () => ({ pass: unregisteredCount(agentOnly) === 0, actual: unregisteredCount(agentOnly) }) },
616
+ { id: 'reviewer-surface-approved', status: 'CONTROL', reason: '', test: () => ({ pass: agentOnly.actions['review-pass'].exitCode === 0, actual: agentOnly.actions['review-pass'].exitCode }) },
617
+ { id: 'registered-run-accepted', status: 'CONTROL', reason: '', test: () => ({ pass: ['doc-lint', 'link-check'].every(c => acceptedCapabilityOf(agentOnly).includes(c)), actual: acceptedCapabilityOf(agentOnly) }) },
618
+ ]);
619
+
620
+ const control = runCase(r3TamperCase(`F4T9-R3-02-control@${leg}`, 'control'));
621
+ runChecks(`R3-02.unmodified-control@${leg}`, control, [
622
+ { id: 'real-run-sets-last-verify', status: 'CONTROL', reason: '', test: () => ({ pass: typeof handoffOf(control, 'verify-pass').lastVerifyAt === 'number', actual: handoffOf(control, 'verify-pass').lastVerifyAt ?? null }) },
623
+ { id: 'no-reminder-after-real-verification', status: 'CONTROL', reason: '', test: () => ({ pass: !/\[Verification Reminder\]/.test(reminderOf(control)), actual: reminderOf(control).trim().slice(0, 80) }) },
624
+ // A10 doc-only absence: a clean accepted doc-check run sets the doc timestamp and never the generic one (no npm test ran).
625
+ { id: 'doc-only-sets-doc-leaves-generic-absent', status: 'CONTROL', reason: 'A10: a clean accepted doc-check run sets lastDocVerifyAt and leaves lastGenericVerifyAt absent', test: () => ({ pass: typeof handoffOf(control, 'verify-pass').lastDocVerifyAt === 'number' && (handoffOf(control, 'verify-pass').lastGenericVerifyAt ?? null) === null && acceptedCapabilityOf(control).includes('doc-lint'), actual: { doc: handoffOf(control, 'verify-pass').lastDocVerifyAt ?? null, generic: handoffOf(control, 'verify-pass').lastGenericVerifyAt ?? null, accepted: acceptedCapabilityOf(control) } }) },
626
+ ]);
627
+ for (const where of ['before-run', 'after-capture']) {
628
+ const root = runCase(r3TamperCase(`F4T9-R3-02-${where}@${leg}`, where));
629
+ const before = handoffOf(root, 'edit-doc').lastVerifyAt ?? null;
630
+ const after = handoffOf(root, 'verify-pass').lastVerifyAt ?? null;
631
+ runChecks(`R3-02.${where}@${leg}`, root, [
632
+ { id: 'last-verify-unchanged', status: 'CONTROL', reason: 'R3-02 (P3): a one-byte-changed installed doc-check still sets lastVerifyAt (set today, at capture); closed by F4-W9 (4489376)', test: () => ({ pass: before === after, actual: { before, after } }) },
633
+ { id: 'verification-reminder-emitted', status: 'CONTROL', reason: 'R3-02 (P3): the advanced lastVerifyAt suppresses the generic verification reminder; closed by F4-W9 (4489376)', test: () => ({ pass: /\[Verification Reminder\]/.test(reminderOf(root)), actual: reminderOf(root).trim().slice(0, 80) }) },
634
+ ]);
635
+ }
636
+
637
+ // M6 coverage (F4-T10): with no assessment, state-persist alone decides lastVerifyAt, and the generic Stop must still remind.
638
+ const noAssessControl = runCase(r3NoAssessCase(`F4T10-R3-02-noassess-control@${leg}`, false));
639
+ const noAssessTampered = runCase(r3NoAssessCase(`F4T10-R3-02-noassess-tampered@${leg}`, true));
640
+ runChecks(`R3-02.no-assessment-control@${leg}`, noAssessControl, [
641
+ { id: 'real-run-sets-last-verify-without-assessment', status: 'CONTROL', reason: '', test: () => ({ pass: typeof handoffOf(noAssessControl, 'post-lint').lastVerifyAt === 'number', actual: handoffOf(noAssessControl, 'post-lint').lastVerifyAt ?? null }) },
642
+ { id: 'no-reminder-after-real-run', status: 'CONTROL', reason: '', test: () => ({ pass: !/\[Verification Reminder\]/.test(reminderOf(noAssessControl)), actual: reminderOf(noAssessControl).trim().slice(0, 80) }) },
643
+ ]);
644
+ runChecks(`R3-02.no-assessment-tampered@${leg}`, noAssessTampered, [
645
+ { id: 'pinned-run-leaves-last-verify-unset', status: 'CONTROL', reason: 'F4-W9 (state-persist pin): a one-byte-changed doc-check does not advance lastVerifyAt, even without an assessment', test: () => ({ pass: (handoffOf(noAssessTampered, 'post-lint').lastVerifyAt ?? null) === null, actual: handoffOf(noAssessTampered, 'post-lint').lastVerifyAt ?? null }) },
646
+ { id: 'reminder-emitted-without-assessment', status: 'CONTROL', reason: 'F4-W9 (state-persist pin): the generic Stop still reminds after a one-byte-changed doc-check', test: () => ({ pass: /\[Verification Reminder\]/.test(reminderOf(noAssessTampered)), actual: reminderOf(noAssessTampered).trim().slice(0, 80) }) },
647
+ ]);
648
+ }
649
+
650
+ // ---- C1 (F4-W12): command-role candidates, strict malformed-attempt refusal, and identity freshness ---------------
651
+ // locks: A, C
652
+
653
+ const PLAN_MENTIONS_DOC_CHECK = `node hooks/scripts/workflow-disposition.js plan --session-id f4-w12-session --requirements-json '${JSON.stringify([
654
+ { id: 'req-verify', stepType: 'verification', summary: 'Run checks', acceptance: 'run doc-check after the edit' },
655
+ ])}' --strategy iterative-single --evidence "normal documentation trace"`;
656
+ const MALFORMED_DOC_CHECK = 'sh -c "node {{DOC_CHECK}} check"';
657
+ const PLAN_CAPTURE = { id: 'plan-command', op: 'hookDeliver', payload: { event: 'PostToolUse', command: PLAN_MENTIONS_DOC_CHECK, toolUseId: 'toolu_f4_w12_plan', response: { stdout: 'planned', stderr: '', interrupted: false } } };
658
+ const MALFORMED_CAPTURE = { id: 'malformed-shell', op: 'hookDeliver', payload: { event: 'PostToolUse', command: MALFORMED_DOC_CHECK, toolUseId: 'toolu_f4_w12_shell', response: { stdout: '', stderr: '', interrupted: false } } };
659
+
660
+ function moveBeforeStart(item, ...actions) {
661
+ item.actions.splice(item.actions.findIndex(action => action.id === 'start'), 0, ...actions);
662
+ return item;
663
+ }
664
+
665
+ function moveAfterStart(item, ...actions) {
666
+ item.actions.splice(item.actions.findIndex(action => action.id === 'start') + 1, 0, ...actions);
667
+ return item;
668
+ }
669
+
670
+ function c1Replay(leg, item) {
671
+ const staged = stage(leg);
672
+ try {
673
+ useShippedExport(staged);
674
+ return replay(item, staged.dist, { pilot: '1' });
675
+ } finally {
676
+ staged.cleanup();
677
+ }
678
+ }
679
+
680
+ function partC1(leg) {
681
+ console.log(`\n== C1 (${leg}): only checker command targets enter the doc-credit boundary ==`);
682
+ const resolver = require(path.join(LEG_LIB[leg], 'doc-reconciliation-provider.js'));
683
+ const installed = resolver.installedDocCheck();
684
+ const driveSpelling = /^[A-Za-z]:[\\/]/.test(installed)
685
+ ? installed.replaceAll('/', '\\')
686
+ : 'C:' + installed.replaceAll('/', '\\');
687
+ const windowsSpelling = driveSpelling;
688
+ const commandSuffix = ' check --workspace . --config .agents/harness-everything/project-docs.json --changed-path docs/a.md';
689
+ const windowsPathResult = resolver.resolveDocCheckInvocation('node "' + windowsSpelling + '"' + commandSuffix, ROOT);
690
+ const unquotedWindowsPathResult = windowsSpelling.includes(' ')
691
+ ? null
692
+ : resolver.resolveDocCheckInvocation('node ' + windowsSpelling + commandSuffix, ROOT);
693
+ const drivePathResult = resolver.resolveDocCheckInvocation('node "' + driveSpelling + '"' + commandSuffix, ROOT);
694
+ const uncSpelling = '\\\\server\\share\\repo\\harness-everything\\scripts\\doc-check.js';
695
+ const uncPathResult = resolver.resolveDocCheckInvocation('node "' + uncSpelling + '"' + commandSuffix, ROOT);
696
+ const escapedHyphenSpelling = installed.split(path.sep).join('/').replace('doc-check.js', 'doc\\-check.js');
697
+ const escapedHyphenCommand = 'node ' + escapedHyphenSpelling + commandSuffix;
698
+ const escapedHyphenResult = resolver.resolveDocCheckInvocation(escapedHyphenCommand, ROOT);
699
+ const escapedHyphenRisk = resolver.trustRisk(escapedHyphenCommand);
700
+ const escapedHyphenExecutors = resolver.journalExecutors([
701
+ { event: 'PostToolUse', tool_name: 'Bash', command: escapedHyphenCommand, cwd: ROOT, session_id: 'reviewer-session' },
702
+ ], 'author-session');
703
+ const escapedRelativeCommand = 'node doc\\-check.js' + commandSuffix;
704
+ const platformDescriptor = Object.getOwnPropertyDescriptor(process, 'platform');
705
+ let escapedRelativeProbe;
706
+ try {
707
+ Object.defineProperty(process, 'platform', { ...platformDescriptor, value: 'win32' });
708
+ escapedRelativeProbe = {
709
+ result: resolver.resolveDocCheckInvocation(escapedRelativeCommand, ROOT),
710
+ risk: resolver.trustRisk(escapedRelativeCommand),
711
+ executors: resolver.journalExecutors([
712
+ { event: 'PostToolUse', tool_name: 'Bash', command: escapedRelativeCommand, cwd: ROOT, session_id: 'relative-reviewer-session' },
713
+ ], 'author-session'),
714
+ };
715
+ } finally {
716
+ Object.defineProperty(process, 'platform', platformDescriptor);
717
+ }
718
+ const nativePathResult = resolver.resolveDocCheckInvocation('node "' + installed + '"' + commandSuffix, ROOT);
719
+ const lookalikeDirectory = fs.mkdtempSync(path.join(os.tmpdir(), 'f4-doc-check-lookalike-'));
720
+ try {
721
+ const lookalikePath = path.join(lookalikeDirectory, 'doc-check.js');
722
+ fs.copyFileSync(installed, lookalikePath);
723
+ const lookalikeBytesMatch = fs.readFileSync(lookalikePath).equals(fs.readFileSync(installed));
724
+ const outsideLookalike = resolver.resolveDocCheckInvocation('node "' + lookalikePath + '"' + commandSuffix, ROOT);
725
+ const shortNameDirectory = path.join(lookalikeDirectory, 'RUNNER~1-#safe');
726
+ fs.symlinkSync(path.dirname(installed), shortNameDirectory, process.platform === 'win32' ? 'junction' : 'dir');
727
+ const shortNamePath = path.join(shortNameDirectory, path.basename(installed));
728
+ const shortNameResult = resolver.resolveDocCheckInvocation('node ' + shortNamePath + commandSuffix, ROOT);
729
+ const variableDirectory = path.join(lookalikeDirectory, '%TEMP%');
730
+ fs.symlinkSync(path.dirname(installed), variableDirectory, process.platform === 'win32' ? 'junction' : 'dir');
731
+ const variablePath = path.join(variableDirectory, path.basename(installed));
732
+ const variablePathResult = resolver.resolveDocCheckInvocation('node ' + variablePath + commandSuffix, ROOT);
733
+ const expansionControls = [
734
+ ['leading-tilde', 'node ~/doc-check.js' + commandSuffix],
735
+ ['leading-user-tilde', 'node ~runner/doc-check.js' + commandSuffix],
736
+ ['dollar', 'node $RUNNER/doc-check.js' + commandSuffix],
737
+ ['backticks', 'node `echo ignored`/doc-check.js' + commandSuffix],
738
+ ['windows-percent-variable', 'node %TEMP%\\doc-check.js' + commandSuffix],
739
+ ].map(([id, command]) => ({ id, result: resolver.resolveDocCheckInvocation(command, ROOT), risk: resolver.trustRisk(command) }));
740
+ runChecks('C1.windows-path-identity@' + leg, { windowsPathResult, unquotedWindowsPathResult, drivePathResult, uncPathResult, escapedHyphenResult, escapedHyphenRisk, escapedHyphenExecutors, escapedRelativeProbe, nativePathResult, lookalikeBytesMatch, outsideLookalike, shortNamePath, shortNameResult, variablePathResult, expansionControls }, [
741
+ { id: 'native-positive-control', status: 'CONTROL', reason: '', test: value => ({ pass: value.nativePathResult.kind === 'accepted', actual: value.nativePathResult }) },
742
+ { id: 'quoted-backslash-spelling-remains-a-check-target', status: 'CONTROL', reason: '', test: value => ({ pass: value.windowsPathResult.kind !== 'not-candidate' && value.windowsPathResult.argv?.[0] === windowsSpelling, actual: value.windowsPathResult }) },
743
+ { id: 'unquoted-backslash-spelling-remains-a-check-target', status: 'CONTROL', reason: '', test: value => ({ pass: value.unquotedWindowsPathResult === null || (value.unquotedWindowsPathResult.kind !== 'not-candidate' && value.unquotedWindowsPathResult.argv?.[0] === windowsSpelling), actual: value.unquotedWindowsPathResult }) },
744
+ { id: 'drive-rooted-backslash-path-remains-intact', status: 'CONTROL', reason: '', test: value => ({ pass: value.drivePathResult.kind !== 'not-candidate' && value.drivePathResult.argv?.[0] === driveSpelling, actual: value.drivePathResult }) },
745
+ { id: 'quoted-unc-prefix-is-preserved', status: 'CONTROL', reason: '', test: value => ({ pass: value.uncPathResult.kind !== 'not-candidate' && value.uncPathResult.argv?.[0] === uncSpelling, actual: value.uncPathResult }) },
746
+ { id: 'interior-tilde-and-safe-punctuation-reach-installed-identity', status: 'CONTROL', reason: '', test: value => ({ pass: value.shortNameResult.kind === 'accepted' && value.shortNameResult.argv?.[0] === value.shortNamePath, actual: value.shortNameResult }) },
747
+ { id: 'cmd-variable-expansion-path-is-refused', status: 'CONTROL', reason: '', test: value => ({ pass: value.variablePathResult.kind === 'rejected' && value.variablePathResult.reason === 'shell-expansion', actual: value.variablePathResult }) },
748
+ { id: 'leading-tilde-words-remain-expansion-risk', status: 'CONTROL', reason: '', test: value => ({ pass: value.expansionControls.slice(0, 2).every(item => item.result.reason === 'shell-expansion' && item.risk), actual: value.expansionControls.slice(0, 2) }) },
749
+ { id: 'dollar-backtick-and-percent-forms-remain-risky', status: 'CONTROL', reason: '', test: value => ({ pass: value.expansionControls.slice(2).every(item => item.result.kind !== 'accepted' && item.risk), actual: value.expansionControls.slice(2) }) },
750
+ { id: 'unquoted-shell-escaped-hyphen-remains-a-check-target-and-risk', status: 'CONTROL', reason: '', test: value => ({ pass: value.escapedHyphenResult.kind === 'accepted' && value.escapedHyphenRisk === true && value.escapedHyphenExecutors.includes('reviewer-session'), actual: { result: value.escapedHyphenResult, trustRisk: value.escapedHyphenRisk, executors: value.escapedHyphenExecutors } }) },
751
+ { id: 'windows-relative-shell-escape-fails-closed-as-risky-evidence', status: 'CONTROL', reason: '', test: value => ({ pass: value.escapedRelativeProbe.result.kind !== 'accepted' && value.escapedRelativeProbe.risk === true && value.escapedRelativeProbe.executors.includes('relative-reviewer-session'), actual: value.escapedRelativeProbe }) },
752
+ { id: 'outside-lookalike-is-byte-identical', status: 'CONTROL', reason: '', test: value => ({ pass: value.lookalikeBytesMatch, actual: value.lookalikeBytesMatch }) },
753
+ { id: 'outside-lookalike-remains-refused', status: 'CONTROL', reason: '', test: value => ({ pass: value.outsideLookalike.kind === 'rejected' && value.outsideLookalike.reason === 'implementation-not-installed', actual: value.outsideLookalike }) },
754
+ ]);
755
+ } finally {
756
+ fs.rmSync(lookalikeDirectory, { recursive: true, force: true, maxRetries: 20, retryDelay: 50 });
757
+ }
758
+ const roleCases = [
759
+ ['workflow-plan-data', PLAN_MENTIONS_DOC_CHECK, 'not-candidate', 'not-doc-check'],
760
+ ['echo-data', 'echo doc-check', 'not-candidate', 'not-doc-check'],
761
+ ['unrelated-script-data', 'node scripts/format.js --name doc-check', 'not-candidate', 'not-doc-check'],
762
+ ['npx-wrapper-target', 'npx doc-check check', 'rejected', 'direct-launcher-required'],
763
+ ['npx-package-data', 'npx --package doc-check prettier', 'not-candidate', 'not-doc-check'],
764
+ ['env-wrapper-target', `env node ${installed} check`, 'rejected', 'direct-launcher-required'],
765
+ ['env-assignment-data', 'env DOC_CHECK=doc-check echo ready', 'not-candidate', 'not-doc-check'],
766
+ ['shell-comment-data', `echo ok # note; ${installed}`, 'not-candidate', 'not-doc-check'],
767
+ ['executable-after-comment-newline', `echo ok\t#x\nnode ${installed} check --workspace . --config .agents/harness-everything/project-docs.json --changed-path docs/a.md`, 'rejected', 'shell-comment'],
768
+ ['heredoc-checker-data', `cat <<EOF\nnode ${installed} check --workspace . --config .agents/harness-everything/project-docs.json --changed-path docs/a.md\nEOF`, 'not-candidate', 'not-doc-check'],
769
+ ['tab-stripped-heredoc-checker-data', `cat <<-EOF\n\tnode ${installed} check --workspace . --config .agents/harness-everything/project-docs.json --changed-path docs/a.md\n\tEOF`, 'not-candidate', 'not-doc-check'],
770
+ ['quoted-multiline-checker-data', `echo "ok\nnode ${installed} check"`, 'not-candidate', 'not-doc-check'],
771
+ ['escaped-newline-checker-data', `echo ok\\\nnode ${installed} check`, 'not-candidate', 'not-doc-check'],
772
+ ['shell-comment-trailing-note-control', `node ${installed} check --changed-path docs/a.md # trailing note`, 'rejected', 'shell-comment'],
773
+ ['quoted-hash-then-checker-control', `echo "# x" ; node ${installed}`, 'rejected', 'shell-operator'],
774
+ ['shell-wrapper-target', `sh -c "node ${installed} check"`, 'rejected', 'direct-launcher-required'],
775
+ ['shell-operator-target', `node ${installed} check; echo done`, 'rejected', 'shell-operator'],
776
+ ['malformed-trailing-quote-target', `node "${installed}" check "`, 'rejected', 'unclosed-quote'],
777
+ ];
778
+ for (const [id, command, kind, reason] of roleCases) {
779
+ const result = resolver.resolveDocCheckInvocation(command, ROOT);
780
+ runChecks(`C1.target-role.${id}@${leg}`, result, [
781
+ { id: 'classification-preserves-boundary', status: 'CONTROL', reason: '', test: value => ({ pass: value.kind === kind && value.reason === reason, actual: value }) },
782
+ ]);
783
+ }
784
+ const normal = moveBeforeStart(r3ReviewCase(`F4W12-C1-plan-mention@${leg}`, true), PLAN_CAPTURE);
785
+ const normalRoot = c1Replay(leg, normal);
786
+ runChecks(`C1.normal-plan-mention@${leg}`, normalRoot, [
787
+ { id: 'plan-json-does-not-withhold-real-check-credit', status: 'CONTROL', reason: 'F4-W12 C1 (A/C): workflow JSON mentioning doc-check stays ordinary metadata while the current registered check receives exact credit', test: () => ({ pass: normalRoot.pilot?.pilotCredit === true && JSON.stringify(creditedOf(normalRoot)) === JSON.stringify(['doc-lint', 'link-check']), actual: { pilotCredit: normalRoot.pilot?.pilotCredit, credited: creditedOf(normalRoot), diagnostics: diagnosticsOf(normalRoot).map(item => item && item.code) } }) },
788
+ { id: 'installed-check-is-still-accepted', status: 'CONTROL', reason: '', test: () => ({ pass: ['doc-lint', 'link-check'].every(capability => acceptedCapabilityOf(normalRoot).includes(capability)), actual: acceptedCapabilityOf(normalRoot) }) },
789
+ { id: 'review-pass-exits-zero', status: 'CONTROL', reason: 'A10: the normal plan-JSON run has an independent reviewer whose review-pass exits 0', test: () => ({ pass: normalRoot.actions['review-pass'].exitCode === 0, actual: normalRoot.actions['review-pass'].exitCode }) },
790
+ ]);
791
+
792
+ const malformed = moveAfterStart(r3ReviewCase(`F4W12-C1-malformed-shell@${leg}`, true), MALFORMED_CAPTURE);
793
+ const malformedRoot = c1Replay(leg, malformed);
794
+ runChecks(`C1.malformed-current-shell@${leg}`, malformedRoot, [
795
+ { id: 'explicit-attempt-is-unregistered', status: 'CONTROL', reason: '', test: () => ({ pass: unregisteredCount(malformedRoot) > 0, actual: diagnosticsOf(malformedRoot).map(item => item && item.code) }) },
796
+ { id: 'malformed-attempt-withholds-credit', status: 'CONTROL', reason: '', test: () => ({ pass: malformedRoot.pilot?.pilotCredit === false && creditedOf(malformedRoot).length === 0, actual: { pilotCredit: malformedRoot.pilot?.pilotCredit, credited: creditedOf(malformedRoot) } }) },
797
+ { id: 'malformed-attempt-does-not-set-generic-verify-time', status: 'CONTROL', reason: '', test: () => ({ pass: handoffOf(malformedRoot, 'malformed-shell').lastVerifyAt == null, actual: handoffOf(malformedRoot, 'malformed-shell').lastVerifyAt ?? null }) },
798
+ ]);
799
+
800
+ const stale = clone(POS_CASE);
801
+ stale.id = `F4W12-C1-stale-real-check@${leg}`;
802
+ stale.requirements = clone(REQUIREMENTS);
803
+ stale.actions = [
804
+ { id: 'route', op: 'route', payload: { prompt: 'Update docs/a.md and verify it.' } },
805
+ { id: 'plan', op: 'plan', payload: { requirements: '$requirements', strategy: 'iterative-single', evidence: 'bounded documentation loop' } },
806
+ LINT_RUN,
807
+ { id: 'post-lint', op: 'hookDeliver', payload: { event: 'PostToolUse', command: LINT_COMMAND, toolUseId: `toolu_f4_w12_stale_${leg}`, fromRun: 'lint-run' } },
808
+ { id: 'start', op: 'start', payload: {} },
809
+ { id: 'doc-pass', op: 'step', payload: { stepId: 'req-docs', disposition: 'pass', evidence: 'sentence corrected' } },
810
+ { id: 'stop', op: 'stop' },
811
+ { id: 'stop-generic', op: 'stopGeneric' },
812
+ ];
813
+ const staleRoot = c1Replay(leg, stale);
814
+ runChecks(`C1.stale-real-check@${leg}`, staleRoot, [
815
+ { id: 'stale-check-identity-rejected', status: 'CONTROL', reason: '', test: () => ({ pass: staleRoot.pilot?.pilotCredit === false && creditedOf(staleRoot).length === 0 && diagnosticsOf(staleRoot).some(item => item && item.code === 'identity-mismatch'), actual: { pilotCredit: staleRoot.pilot?.pilotCredit, credited: creditedOf(staleRoot), diagnostics: diagnosticsOf(staleRoot).map(item => item && item.code) } }) },
816
+ ]);
817
+ }
818
+
819
+ // ---- C2 (F4-W12): the plain review contract still requires a current artifact binding -------------------------------
820
+ // locks: A, B, C
821
+
822
+ function partC2(leg) {
823
+ console.log(`\n== C2 (${leg}): plain independent review evidence must bind the current artifact ==`);
824
+ const cases = [
825
+ ['plain-current-digest', BOUND_APPROVE, true],
826
+ ['missing-digest', VERDICT_APPROVE, false],
827
+ ['explicit-reject', `Review verdict: reject\nSummary: The requested change is incorrect.\nReviewed artifact: docs/a.md sha256:${sha256(CAND_NORMATIVE)}`, false],
828
+ ];
829
+ for (const [label, message, approved] of cases) {
830
+ const root = c1Replay(leg, reviewRoot(leg, [agentSurface('agent-result', 'agent-w12-review'), subStop('agent-w12-review', message), reviewPass()], `F4W12-C2-${label}@${leg}`));
831
+ const capture = (root.actions['sub-stop'].state.capture || { records: [] }).records.find(record => record.event === 'SubagentStop') || {};
832
+ runChecks(`C2.${label}@${leg}`, root, [
833
+ { id: 'verdict-is-explicit', status: 'CONTROL', reason: '', test: () => ({ pass: capture.reviewVerdict === (approved ? 'approve' : label === 'explicit-reject' ? 'reject' : 'approve'), actual: capture.reviewVerdict }) },
834
+ { id: approved ? 'bound-review-passes' : 'unbound-or-rejected-review-refused', status: 'CONTROL', reason: '', test: () => ({ pass: approved ? root.actions['review-pass'].exitCode === 0 && capture.reviewedArtifactDigests?.['docs/a.md'] === sha256(CAND_NORMATIVE) : root.actions['review-pass'].exitCode === 2 && !acceptedCapabilityOf(root).includes('quality-review'), actual: { reviewExit: root.actions['review-pass'].exitCode, reviewed: capture.reviewedArtifactDigests, accepted: acceptedCapabilityOf(root) } }) },
835
+ ]);
836
+ }
837
+ }
838
+
839
+ function main() {
840
+ if (!shippedExportPresent()) {
841
+ console.log('INFRA: the committed qualification export is missing from a leg; the trust-boundary cases need it');
842
+ return 2;
843
+ }
844
+ for (const leg of LEGS) {
845
+ partR1R2(leg);
846
+ partR3R5(leg);
847
+ partR6R7(leg);
848
+ partR8R9(leg);
849
+ partM2(leg);
850
+ partR3(leg);
851
+ }
852
+ partG0();
853
+ for (const leg of LEGS) {
854
+ partC1(leg);
855
+ partC2(leg);
856
+ }
857
+ console.log('\n== F4-T4 trust-boundary evidence ==');
858
+ console.log(`controls and passing checks: ${passCount}`);
859
+ console.log(`RED checks confirmed: ${redCount}`);
860
+ console.log(`unexpected failures: ${failCount}`);
861
+ console.log(`infrastructure faults: ${infraCount}`);
862
+ const expectRed = process.argv.includes('--expect-red');
863
+ if (infraCount > 0) return 2;
864
+ if (!expectRed) {
865
+ if (failCount === 0 && redCount === 0) {
866
+ console.log('\nSTATUS: GREEN (all trust-boundary checks passed)');
867
+ return 0;
868
+ }
869
+ console.log(`\nSTATUS: NOT-GREEN (red ${redCount}, unexpected ${failCount})`);
870
+ return 1;
871
+ }
872
+ if (failCount === 0) {
873
+ console.log('\nSTATUS: RED-PLAN-MATCHED (all controls passed, all RED checks failed for their F4-R findings)');
874
+ return 0;
875
+ }
876
+ console.log('\nSTATUS: RED-PLAN-MISMATCH');
877
+ return 1;
878
+ }
879
+
880
+ process.exitCode = main();