harness-everything 0.46.3 → 0.47.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 (32) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/CHANGELOG.md +23 -1
  4. package/benchmarks/results/live-host/claude-341-outline-dispatch-2026-10-28/README.md +33 -8
  5. package/benchmarks/results/live-host/claude-341-outline-dispatch-2026-10-28/inventory/2741bd4514d2cd68f57d93a2b3ce94cbe597ca62d30e26f0ca58e882df5b03c6.md +128 -0
  6. package/benchmarks/results/live-host/claude-341-outline-dispatch-2026-10-28/sanitize-outline-capture.js +105 -0
  7. package/ci/mechanism-e1-derived-regenerate.test.js +333 -0
  8. package/ci/mechanism-outline-sanitizer.test.js +79 -0
  9. package/ci/mechanism-prb-outline-review.test.js +278 -31
  10. package/docs/doc-workflow-reconciliation.md +12 -8
  11. package/docs/platform-capabilities.md +1 -1
  12. package/harness-everything/SKILL.md +1 -1
  13. package/harness-everything/scripts/doc-check.js +106 -8
  14. package/harness-everything/scripts/doc-lifecycle.js +49 -1
  15. package/hooks/scripts/lib/doc-reconciliation-events.js +22 -0
  16. package/hooks/scripts/lib/doc-reconciliation-outline-capture.sanitized.jsonl +24 -0
  17. package/hooks/scripts/lib/doc-reconciliation-pilot.js +3 -1
  18. package/hooks/scripts/lib/doc-reconciliation-provider.js +168 -15
  19. package/hooks/scripts/lib/doc-reconciliation-qualification.json +20 -0
  20. package/opencode-plugin/plugin.json +1 -1
  21. package/package.json +1 -1
  22. package/plugins/harness-everything/.codex-plugin/plugin.json +1 -1
  23. package/plugins/harness-everything/hooks/scripts/lib/doc-reconciliation-events.js +22 -0
  24. package/plugins/harness-everything/hooks/scripts/lib/doc-reconciliation-outline-capture.sanitized.jsonl +24 -0
  25. package/plugins/harness-everything/hooks/scripts/lib/doc-reconciliation-pilot.js +3 -1
  26. package/plugins/harness-everything/hooks/scripts/lib/doc-reconciliation-provider.js +168 -15
  27. package/plugins/harness-everything/hooks/scripts/lib/doc-reconciliation-qualification.json +20 -0
  28. package/plugins/harness-everything/plugin.json +1 -1
  29. package/plugins/harness-everything/skills/harness-everything/SKILL.md +1 -1
  30. package/plugins/harness-everything/skills/harness-everything/scripts/doc-check.js +106 -8
  31. package/plugins/harness-everything/skills/harness-everything/scripts/doc-lifecycle.js +49 -1
  32. package/sbom.cdx.json +7 -7
@@ -8,6 +8,7 @@ const path = require('path');
8
8
  const { execFileSync } = require('child_process');
9
9
  const { spawnSync } = require('child_process');
10
10
  const { canonical } = require(path.resolve(__dirname, '../hooks/scripts/lib/workflow-isolation.js'));
11
+ const { stage: stageQualificationTree } = require('./lib/doc-reconciliation-staged-tree.js');
11
12
 
12
13
  const ROOT = path.resolve(__dirname, '..');
13
14
  const provider = require(path.join(ROOT, 'hooks/scripts/lib/doc-reconciliation-provider.js'));
@@ -76,7 +77,10 @@ function outlineText({ complete = false, decisionPlaceholder = false, duplicateD
76
77
 
77
78
  function createOutlineFixture(options = {}) {
78
79
  const root = canonical(fs.mkdtempSync(path.join(os.tmpdir(), 'prb-outline-fixture-')));
79
- const target = options.target || 'docs/adr/DOCS-001.md';
80
+ const retainedInventory = options.retainedInventory === true;
81
+ const target = options.target || (retainedInventory ? 'docs/adr/EVID-001.md' : 'docs/adr/DOCS-001.md');
82
+ const rowId = options.rowId || (retainedInventory ? '17' : '20');
83
+ const domain = options.domain || (retainedInventory ? 'EVID' : 'DOCS');
80
84
  const taskId = options.taskId || 'task-prb';
81
85
  const attemptId = options.attemptId || 'attempt-1';
82
86
  const sessionId = options.sessionId || 'author-session';
@@ -85,7 +89,7 @@ function createOutlineFixture(options = {}) {
85
89
  ? `${root}${path.sep}..${path.sep}${path.basename(root)}` : root;
86
90
  const workflowId = 'workflow-prb';
87
91
  const workflowRevision = 2;
88
- const config = JSON.stringify({ governed: [{ path: 'docs/adr/*.md', role: 'adr' }], domains: ['DOCS'], derived: [] }, null, 2) + '\n';
92
+ const config = JSON.stringify({ governed: [{ path: 'docs/adr/*.md', role: 'adr' }], domains: [domain], derived: [] }, null, 2) + '\n';
89
93
  const configDigest = sha(config);
90
94
  fs.mkdirSync(path.join(root, '.agents/harness-everything'), { recursive: true });
91
95
  fs.mkdirSync(path.dirname(path.join(root, target)), { recursive: true });
@@ -110,7 +114,7 @@ function createOutlineFixture(options = {}) {
110
114
  decisionPlaceholder: options.decisionPlaceholder === true,
111
115
  duplicateDecision: options.duplicateDecision === true,
112
116
  indentedDecision: options.indentedDecision === true,
113
- rowId: options.rowId || '20', target, domain: options.domain || 'DOCS',
117
+ rowId, target, domain,
114
118
  }));
115
119
  fixtureGit(root, ['add', '-A']);
116
120
  fixtureGit(root, ['commit', '-q', '-m', 'O outline']);
@@ -119,16 +123,20 @@ function createOutlineFixture(options = {}) {
119
123
  const outlineBytes = fs.readFileSync(path.join(root, target));
120
124
  const outlineBlob = fixtureGit(root, ['rev-parse', `HEAD:${target}`]);
121
125
 
122
- const pinDigest = sha(syntheticInventory);
123
- const expectedPin = {
126
+ const inventoryBytes = retainedInventory
127
+ ? fs.readFileSync(path.join(ROOT, 'benchmarks/results/live-host/claude-341-outline-dispatch-2026-10-28/inventory/2741bd4514d2cd68f57d93a2b3ce94cbe597ca62d30e26f0ca58e882df5b03c6.md'))
128
+ : Buffer.from(syntheticInventory);
129
+ const pinDigest = sha(inventoryBytes);
130
+ const expectedPin = retainedInventory ? internals.OUTLINE_INVENTORY_PIN : {
124
131
  source: 'synthetic://approved-inventory', sha256: pinDigest, runId: 'synthetic-run', inputSha: 'abcdef012345',
125
132
  approvalRef: { issue: 325, commentId: 'synthetic-comment', decision: 1 },
126
133
  retainedArtifactPath: `inventory/${pinDigest}.md`,
127
134
  };
135
+ if (retainedInventory) assert.strictEqual(pinDigest, expectedPin.sha256, 'retained inventory bytes must match the pinned digest');
128
136
  const outlineRoot = path.join(sessionDir, 'doc-reconciliation/outline-confirmation');
129
137
  const inventoryDir = path.join(outlineRoot, 'inventory');
130
138
  fs.mkdirSync(inventoryDir, { recursive: true });
131
- fs.writeFileSync(path.join(inventoryDir, `${pinDigest}.md`), syntheticInventory);
139
+ fs.writeFileSync(path.join(inventoryDir, `${pinDigest}.md`), inventoryBytes);
132
140
  writeJson(path.join(inventoryDir, 'inventory-pin.json'), expectedPin);
133
141
  const requirementIds = ['prb'];
134
142
  const workspaceId = 'ws-' + internals.digestText(internals.captureJson({ workspaceRoot: root }));
@@ -183,7 +191,7 @@ function createOutlineFixture(options = {}) {
183
191
  'Review verdict: approve',
184
192
  `Reviewed artifact: ${target} sha256:${sha(outlineBytes)}`,
185
193
  `Reviewed artifact: ${inventoryPath} sha256:${pinDigest}`,
186
- `Summary: Approved inventory row 20 for ${target} in domain DOCS.`,
194
+ `Summary: Approved inventory row ${rowId} for ${target} in domain ${domain}.`,
187
195
  ].join('\n');
188
196
  const reviewResultPayload = {
189
197
  session_id: sessionId, cwd: root, hook_event_name: 'PostToolUse', tool_name: 'Task',
@@ -229,7 +237,7 @@ function createOutlineFixture(options = {}) {
229
237
  baseline: { root, commit: baselineCommit, tree: baselineTree, configDigest },
230
238
  configDigest, targetPaths: [target], draftWorktreeRoot: workerRoot,
231
239
  outline: { commit: outlineCommit, tree: outlineTree, targetBlobs: { [target]: { blobOid: outlineBlob, storedBytesSha256: sha(outlineBytes) } } },
232
- inventory: { ...expectedPin, rowId: options.rowId || '20', domain: options.domain || 'DOCS', targetPaths: [target] },
240
+ inventory: { ...expectedPin, rowId, domain, targetPaths: [target] },
233
241
  reviewCaptureRef: reviewStop.ref, reviewCaptureDigest: reviewRecordDigest, reviewDigest: reviewStop.reviewDigest,
234
242
  coordinatorPrincipal: sessionId, writerLeaseId: lease.id, writerLeaseDigest: leaseDigest,
235
243
  draftDispatchToken: options.token || 'token-1',
@@ -311,9 +319,10 @@ function createOutlineFixture(options = {}) {
311
319
  function finishDraft({ draft = true, nonDescendant = false, finalReview = false, baselineRoot = root,
312
320
  omitWorkerStart = false, omitWorkerResult = false, omitWorkerStop = false, tamperHandoff = false,
313
321
  providerSessionId = sessionId, providerTaskId = taskId, providerBaseline = { root, commit: baselineCommit },
314
- runPilot = false, handAuthoredCapture = false, removeSeam = false, mutateAfterFinalReview = false } = {}) {
322
+ runPilot = false, handAuthoredCapture = false, removeSeam = false, mutateAfterFinalReview = false,
323
+ postWorkerCaptures = [] } = {}) {
315
324
  if (draft) {
316
- fs.writeFileSync(path.join(root, target), `---\nid: DOCS-001\nstatus: proposed\n---\n\n## Decision\nFinal D for row 20.\n`);
325
+ fs.writeFileSync(path.join(root, target), `---\nid: ${domain}-001\nstatus: proposed\n---\n\n## Decision\nFinal D for row ${rowId}.\n`);
317
326
  fixtureGit(root, ['add', '-A']);
318
327
  fixtureGit(root, ['commit', '-q', '-m', 'D final draft']);
319
328
  }
@@ -321,7 +330,7 @@ function createOutlineFixture(options = {}) {
321
330
  fixtureGit(root, ['checkout', '--orphan', 'unrelated']);
322
331
  fixtureGit(root, ['rm', '-rf', '.']);
323
332
  fs.mkdirSync(path.dirname(path.join(root, target)), { recursive: true });
324
- fs.writeFileSync(path.join(root, target), `---\nid: DOCS-001\nstatus: proposed\n---\n\n## Decision\nUnrelated D.\n`);
333
+ fs.writeFileSync(path.join(root, target), `---\nid: ${domain}-001\nstatus: proposed\n---\n\n## Decision\nUnrelated D for row ${rowId}.\n`);
325
334
  fixtureGit(root, ['add', '-A']);
326
335
  fixtureGit(root, ['-c', 'user.name=outline-fixture', '-c', 'user.email=outline-fixture@invalid', 'commit', '-q', '-m', 'unrelated D']);
327
336
  }
@@ -381,8 +390,8 @@ function createOutlineFixture(options = {}) {
381
390
  tool_response: { agentId: options.workerPrincipal || 'draft-worker', interrupted: false, status: 'completed' },
382
391
  }, 'PostToolUse', root)] : []), ...(!omitWorkerStop ? [provider.buildCaptureRecord({
383
392
  session_id: sessionId, cwd: root, hook_event_name: 'SubagentStop', agent_id: options.workerPrincipal || 'draft-worker', agent_type: 'worker',
384
- }, 'SubagentStop', root)] : [])];
385
- const qualification = { source: 'fake-test-fixture', faked: true, toolUseIdPopulated: true,
393
+ }, 'SubagentStop', root)] : []), ...postWorkerCaptures];
394
+ const qualification = options.noFakeQualification ? undefined : { source: 'fake-test-fixture', faked: true, toolUseIdPopulated: true,
386
395
  agentIdPopulated: true, outlineDispatchCaptureVerified: true };
387
396
  if (removeSeam && capture.outlineDispatch?.seamRef) {
388
397
  fs.rmSync(path.join(sessionDir, capture.outlineDispatch.seamRef), { force: true });
@@ -396,7 +405,8 @@ function createOutlineFixture(options = {}) {
396
405
  const origin = provider.createClaudeOriginProvider({
397
406
  hostId: 'claude-code', sessionId: providerSessionId, workspaceRoot: root, workflowId, workflowRevision, taskId: providerTaskId,
398
407
  principals: { author: providerSessionId, executors: [] }, baseline: providerBaseline,
399
- captures, sessionDir, taskBinding, qualification, outlineFixture: { expectedPin },
408
+ captures, sessionDir, taskBinding,
409
+ ...(qualification ? { qualification, outlineFixture: { expectedPin } } : {}),
400
410
  });
401
411
  const identity = {
402
412
  workspaceId: origin.resolveContext({}).context.workspaceId, sessionId: providerSessionId, workflowId, workflowRevision, taskId: providerTaskId,
@@ -408,8 +418,8 @@ function createOutlineFixture(options = {}) {
408
418
  if (runPilot) {
409
419
  const previousSwitch = process.env.HARNESS_DOC_RECONCILIATION_PILOT;
410
420
  const previousFactory = provider.createClaudeOriginProvider;
411
- provider.createClaudeOriginProvider = input => previousFactory({ ...input, qualification,
412
- outlineFixture: { expectedPin }, taskBinding });
421
+ provider.createClaudeOriginProvider = input => previousFactory({ ...input,
422
+ ...(qualification ? { qualification, outlineFixture: { expectedPin } } : {}), taskBinding });
413
423
  process.env.HARNESS_DOC_RECONCILIATION_PILOT = '1';
414
424
  const context = { root, sessionId, sessionDir, activeHost: 'claude-code', workflow: {
415
425
  workflowId, revision: workflowRevision, strategy: 'iterative-single', state: 'running',
@@ -518,6 +528,55 @@ test('coordinator-seeded inventory pin requires exact bytes, all fixed fields, a
518
528
  assert.strictEqual(internals.resolveInventoryRow(multiDomainResult, '20').state, 'unresolved', 'ambiguous domain mapping is unresolved');
519
529
  });
520
530
 
531
+ test('the trusted inventory pin maps only owner-approved VERI rows 15–18 to EVID', () => {
532
+ const pin = internals.OUTLINE_INVENTORY_PIN;
533
+ const bytes = fs.readFileSync(path.join(ROOT,
534
+ 'benchmarks/results/live-host/claude-341-outline-dispatch-2026-10-28/inventory/2741bd4514d2cd68f57d93a2b3ce94cbe597ca62d30e26f0ca58e882df5b03c6.md'));
535
+ const mapping = [{ rowIds: ['15', '16', '17', '18'], sourceDomain: 'VERI', domain: 'EVID',
536
+ approvalRef: '../e1-feasibility-2026-10-28/owner-decision-veri-evid.md' }];
537
+ assert.deepStrictEqual(pin.rowDomainMappings, mapping, 'the owner approval is part of the trusted pin');
538
+ const valid = internals.validateOutlineInventoryPin({ bytes, pin, expectedPin: pin });
539
+ assert.strictEqual(valid.state, 'accepted');
540
+ for (const rowId of ['15', '16', '17', '18']) {
541
+ assert.deepStrictEqual(internals.resolveInventoryRow(valid, rowId).row, { rowId, domain: 'EVID' });
542
+ }
543
+ const substituted = { ...pin, rowDomainMappings: [{ ...mapping[0], domain: 'VERI' }] };
544
+ const stale = internals.validateOutlineInventoryPin({ bytes, pin: substituted, expectedPin: pin });
545
+ assert.strictEqual(stale.state, 'unresolved', 'a substituted domain cannot change the trusted mapping');
546
+ assert.strictEqual(stale.reason, 'inventory-pin-mismatch');
547
+ });
548
+
549
+ test('pilot offers structured SubagentStop reviews only and preserves drafter independence rejection', () => {
550
+ const sessionDir = fs.mkdtempSync(path.join(os.tmpdir(), 'prb-review-candidates-'));
551
+ const author = 'author-session';
552
+ const draft = provider.buildCaptureRecord({ session_id: author, hook_event_name: 'PostToolUse', tool_name: 'Task',
553
+ tool_use_id: 'draft-dispatch', tool_response: { agentId: 'draft-worker', status: 'completed' } }, 'PostToolUse', '/tmp');
554
+ const ordinary = provider.buildCaptureRecord({ session_id: author, hook_event_name: 'SubagentStop', agent_id: 'draft-worker',
555
+ last_assistant_message: 'Draft complete. The document is ready.' }, 'SubagentStop', '/tmp');
556
+ const reviewResult = provider.buildCaptureRecord({ session_id: author, hook_event_name: 'PostToolUse', tool_name: 'Task',
557
+ tool_use_id: 'review-dispatch', tool_response: { agentId: 'independent-reviewer', status: 'completed' } }, 'PostToolUse', '/tmp');
558
+ const approval = provider.buildCaptureRecord({ session_id: author, hook_event_name: 'SubagentStop', agent_id: 'independent-reviewer',
559
+ last_assistant_message: `Review verdict: approve\nReviewed artifact: docs/adr/EVID-001.md sha256:${'a'.repeat(64)}\nSummary: exact ADR reviewed.` }, 'SubagentStop', '/tmp');
560
+ for (const record of [draft, ordinary, reviewResult, approval]) provider.writeImmutableEvent(sessionDir, record);
561
+ provider.rebuildJournalView(sessionDir);
562
+ const context = { sessionDir, activeHost: 'claude-code', workflow: { docReconciliationTask: { taskId: 'task-review-selection' } } };
563
+ const captures = provider.readCaptureView(sessionDir).records;
564
+ const reviewRefs = pilot._internals.captureReferences(context).filter(ref => ref.endsWith('#quality-review'));
565
+ assert.deepStrictEqual(reviewRefs, [`${approval.ref}#quality-review`], 'ordinary draft completion is not a review candidate');
566
+ assert.strictEqual(provider.reviewRecordIndependent(approval, captures, { author, executors: ['draft-worker'] }), true);
567
+
568
+ const drafterApproval = provider.buildCaptureRecord({ session_id: author, hook_event_name: 'SubagentStop', agent_id: 'draft-worker',
569
+ last_assistant_message: `Review verdict: approve\nReviewed artifact: docs/adr/EVID-001.md sha256:${'a'.repeat(64)}\nSummary: self approval.` }, 'SubagentStop', '/tmp');
570
+ provider.writeImmutableEvent(sessionDir, drafterApproval);
571
+ provider.rebuildJournalView(sessionDir);
572
+ const withDrafterApproval = provider.readCaptureView(sessionDir).records;
573
+ assert.ok(pilot._internals.captureReferences(context).includes(`${drafterApproval.ref}#quality-review`),
574
+ 'an explicit approval remains visible to the provider independence check');
575
+ assert.strictEqual(provider.reviewRecordIndependent(drafterApproval, withDrafterApproval,
576
+ { author, executors: ['draft-worker'] }), false, 'a drafter cannot approve its own work');
577
+ fs.rmSync(sessionDir, { recursive: true, force: true });
578
+ });
579
+
521
580
  test('generic successful execution or waiver cannot pass outline-confirmation', () => {
522
581
  const target = 'docs/adr/DOCS-001.md';
523
582
  const digest = sha('final D bytes');
@@ -921,6 +980,74 @@ test('outline evidence binds exact task, B, O blobs, independent review, worker
921
980
  assert.strictEqual(completed.result.status, 'accepted',
922
981
  `a reviewer that started and stopped in O before the dispatch is not a competing writer: ${JSON.stringify(completed.result)}`);
923
982
  } finally { stoppedReviewer.cleanup(); }
983
+
984
+ const postDraftReviewer = createOutlineFixture();
985
+ try {
986
+ const reviewer = 'post-draft-reviewer';
987
+ const captures = [
988
+ provider.buildCaptureRecord({ session_id: postDraftReviewer.sessionId, cwd: postDraftReviewer.root,
989
+ hook_event_name: 'SubagentStart', agent_id: reviewer, agent_type: 'reviewer' }, 'SubagentStart', postDraftReviewer.root),
990
+ provider.buildCaptureRecord({ session_id: postDraftReviewer.sessionId, cwd: postDraftReviewer.root,
991
+ hook_event_name: 'PostToolUse', tool_name: 'Read', tool_use_id: 'post-draft-read', agent_id: reviewer,
992
+ tool_input: { file_path: postDraftReviewer.target }, tool_response: { interrupted: false } }, 'PostToolUse', postDraftReviewer.root),
993
+ provider.buildCaptureRecord({ session_id: postDraftReviewer.sessionId, cwd: postDraftReviewer.root,
994
+ hook_event_name: 'PostToolUse', tool_name: 'Task', tool_use_id: 'post-draft-review',
995
+ tool_response: { agentId: reviewer, status: 'completed' } }, 'PostToolUse', postDraftReviewer.root),
996
+ provider.buildCaptureRecord({ session_id: postDraftReviewer.sessionId, cwd: postDraftReviewer.root,
997
+ hook_event_name: 'SubagentStop', agent_id: reviewer, agent_type: 'reviewer' }, 'SubagentStop', postDraftReviewer.root),
998
+ ];
999
+ const completed = postDraftReviewer.finishDraft({ postWorkerCaptures: captures });
1000
+ assert.strictEqual(completed.result.status, 'accepted',
1001
+ `a same-root reviewer starting after the draft worker's Stop does not overlap the draft interval: ${JSON.stringify(completed.result)}`);
1002
+ } finally { postDraftReviewer.cleanup(); }
1003
+
1004
+ for (const [name, arrange] of [
1005
+ ['post-stop reviewer missing Stop', fixture => [
1006
+ provider.buildCaptureRecord({ session_id: fixture.sessionId, cwd: fixture.root,
1007
+ hook_event_name: 'SubagentStart', agent_id: 'post-draft-reviewer', agent_type: 'reviewer' }, 'SubagentStart', fixture.root),
1008
+ ]],
1009
+ ['post-stop reviewer with duplicate Starts', fixture => {
1010
+ const start = provider.buildCaptureRecord({ session_id: fixture.sessionId, cwd: fixture.root,
1011
+ hook_event_name: 'SubagentStart', agent_id: 'post-draft-reviewer', agent_type: 'reviewer' }, 'SubagentStart', fixture.root);
1012
+ return [start, { ...start, ref: `${start.ref}:dup`, captureId: `${start.captureId}:dup` }, provider.buildCaptureRecord({
1013
+ session_id: fixture.sessionId, cwd: fixture.root, hook_event_name: 'SubagentStop',
1014
+ agent_id: 'post-draft-reviewer', agent_type: 'reviewer' }, 'SubagentStop', fixture.root)];
1015
+ }],
1016
+ ['post-stop reviewer Stop before Start', fixture => [
1017
+ provider.buildCaptureRecord({ session_id: fixture.sessionId, cwd: fixture.root,
1018
+ hook_event_name: 'SubagentStop', agent_id: 'post-draft-reviewer', agent_type: 'reviewer' }, 'SubagentStop', fixture.root),
1019
+ provider.buildCaptureRecord({ session_id: fixture.sessionId, cwd: fixture.root,
1020
+ hook_event_name: 'SubagentStart', agent_id: 'post-draft-reviewer', agent_type: 'reviewer' }, 'SubagentStart', fixture.root),
1021
+ ]],
1022
+ ]) {
1023
+ const fixture = createOutlineFixture();
1024
+ try {
1025
+ const completed = fixture.finishDraft({ postWorkerCaptures: arrange(fixture) });
1026
+ assert.ok(completed.result.diagnostics?.some(item => item.code === 'outline-competing-writer-start'),
1027
+ `${name} cannot use the post-stop exception: ${JSON.stringify(completed.result)}`);
1028
+ } finally { fixture.cleanup(); }
1029
+ }
1030
+
1031
+ const postDraftWriter = createOutlineFixture();
1032
+ try {
1033
+ const reviewer = 'post-draft-reviewer';
1034
+ const captures = [
1035
+ provider.buildCaptureRecord({ session_id: postDraftWriter.sessionId, cwd: postDraftWriter.root,
1036
+ hook_event_name: 'SubagentStart', agent_id: reviewer, agent_type: 'reviewer' }, 'SubagentStart', postDraftWriter.root),
1037
+ provider.buildCaptureRecord({ session_id: postDraftWriter.sessionId, cwd: postDraftWriter.root,
1038
+ hook_event_name: 'PostToolUse', tool_name: 'Edit', tool_use_id: 'post-draft-edit', agent_id: reviewer,
1039
+ tool_input: { file_path: postDraftWriter.target }, tool_response: { interrupted: false } }, 'PostToolUse', postDraftWriter.root),
1040
+ provider.buildCaptureRecord({ session_id: postDraftWriter.sessionId, cwd: postDraftWriter.root,
1041
+ hook_event_name: 'PostToolUse', tool_name: 'Task', tool_use_id: 'post-draft-review',
1042
+ tool_response: { agentId: reviewer, status: 'completed' } }, 'PostToolUse', postDraftWriter.root),
1043
+ provider.buildCaptureRecord({ session_id: postDraftWriter.sessionId, cwd: postDraftWriter.root,
1044
+ hook_event_name: 'SubagentStop', agent_id: reviewer, agent_type: 'reviewer' }, 'SubagentStop', postDraftWriter.root),
1045
+ ];
1046
+ const completed = postDraftWriter.finishDraft({ postWorkerCaptures: captures });
1047
+ assert.ok(completed.result.diagnostics?.some(item => item.code === 'outline-writer-activity-ambiguous'),
1048
+ `a write after the post-stop reviewer start remains ambiguous: ${JSON.stringify(completed.result)}`);
1049
+ } finally { postDraftWriter.cleanup(); }
1050
+
924
1051
  for (const [name, arrange] of [
925
1052
  ['reviewer stop after the dispatch', fixture => {
926
1053
  const stop = fixture.reviewCaptures.findIndex(item => item.event === 'SubagentStop');
@@ -1049,6 +1176,23 @@ const outlineMappingV2 = {
1049
1176
  workerRootField: 'cwd',
1050
1177
  };
1051
1178
 
1179
+ test('valid separate outline export opens outline qualification', () => {
1180
+ const staged = stageQualificationTree('source');
1181
+ try {
1182
+ const records = [qualificationDispatch(), qualificationStart(), qualificationResult(), qualificationStop()];
1183
+ const bytes = Buffer.from(records.map(record => JSON.stringify(record)).join('\n') + '\n');
1184
+ const file = 'doc-reconciliation-outline-capture.sanitized.jsonl';
1185
+ fs.writeFileSync(path.join(staged.libDir, file), bytes);
1186
+ staged.writeExport(staged.validExport({
1187
+ outlineDispatchCapture: { mapping: outlineMappingV2, file, sha256: sha(bytes), hostVersion: '2.1.312',
1188
+ run: 'run8', baseline: '1bd0f805031c5577a6e7f72a9bc5b41618b71fc1' },
1189
+ }));
1190
+ const outcome = require(path.join(staged.libDir, 'doc-reconciliation-provider.js'))
1191
+ ._internals.validateOutlineCaptureQualification({ hostId: 'claude-code' });
1192
+ assert.strictEqual(outcome.open, true, JSON.stringify(outcome));
1193
+ } finally { staged.cleanup(); }
1194
+ });
1195
+
1052
1196
  function qualificationDispatch({ id = 'dispatch-1', promptId = 'shared-prompt', background = false, hasBackground = true,
1053
1197
  isolation, hasIsolation = false, session = 'coordinator' } = {}) {
1054
1198
  const toolInput = { prompt: dispatchLine, ...(hasBackground ? { run_in_background: background } : {}) };
@@ -1071,6 +1215,118 @@ function qualificationResult(id = 'dispatch-1', agentId = 'worker', status = 'co
1071
1215
  ...(legacyAgentId !== undefined ? { agent_id: legacyAgentId } : {}), tool_response: toolResponse };
1072
1216
  }
1073
1217
 
1218
+ function stagedOutlineOutcome(staged) {
1219
+ return require(path.join(staged.libDir, 'doc-reconciliation-provider.js'))
1220
+ ._internals.validateOutlineCaptureQualification({ hostId: 'claude-code' });
1221
+ }
1222
+
1223
+ function writeStagedOutlineEvidence(staged, records) {
1224
+ const file = 'doc-reconciliation-outline-capture.sanitized.jsonl';
1225
+ const bytes = Buffer.from(records.map(record => JSON.stringify(record)).join('\n') + '\n');
1226
+ fs.writeFileSync(path.join(staged.libDir, file), bytes);
1227
+ const wrapper = { mapping: outlineMappingV2, file, sha256: sha(bytes), hostVersion: '2.1.312', run: 'run8',
1228
+ baseline: '1bd0f805031c5577a6e7f72a9bc5b41618b71fc1' };
1229
+ staged.writeExport(staged.validExport({ outlineDispatchCapture: wrapper }));
1230
+ return { wrapper, filePath: path.join(staged.libDir, file), bytes };
1231
+ }
1232
+
1233
+ test('shipped run8 capture opens production qualification without DI', () => {
1234
+ const qualification = internals.validateOutlineCaptureQualification({ hostId: 'claude-code' });
1235
+ assert.strictEqual(qualification.open, true, JSON.stringify(qualification));
1236
+ assert.strictEqual(qualification.source, 'validated-export');
1237
+ assert.strictEqual(qualification.captureFile, 'doc-reconciliation-outline-capture.sanitized.jsonl');
1238
+ });
1239
+
1240
+ test('shipped run8 export resolves the real-hook integrated fixture without fake qualification', () => {
1241
+ const originalFactory = provider.createClaudeOriginProvider;
1242
+ let originInput;
1243
+ provider.createClaudeOriginProvider = input => {
1244
+ originInput = input;
1245
+ return originalFactory(input);
1246
+ };
1247
+ const fixture = createOutlineFixture({ realHook: true, retainedInventory: true, noFakeQualification: true });
1248
+ try {
1249
+ const completed = fixture.finishDraft();
1250
+ assert.strictEqual(completed.result.status, 'accepted', JSON.stringify(completed.result));
1251
+ assert.strictEqual(Object.hasOwn(originInput, 'qualification'), false, 'production resolution must use the shipped export');
1252
+ } finally {
1253
+ provider.createClaudeOriginProvider = originalFactory;
1254
+ fixture.cleanup();
1255
+ }
1256
+ });
1257
+
1258
+ test('mapped row 17 dispatch accepts EVID-001 while the old VERI request and a substituted domain reject', () => {
1259
+ const valid = createOutlineFixture({ retainedInventory: true, noFakeQualification: true });
1260
+ try {
1261
+ const completed = valid.finishDraft();
1262
+ assert.strictEqual(completed.result.status, 'accepted', JSON.stringify(completed.result));
1263
+ assert.strictEqual(completed.result.event.outlineEvidence.inventoryRowId, '17');
1264
+ assert.strictEqual(completed.result.event.outlineEvidence.domain, 'EVID');
1265
+ } finally { valid.cleanup(); }
1266
+
1267
+ const unapprovedVeri = createOutlineFixture({ retainedInventory: true, rowId: '16',
1268
+ target: 'docs/adr/VERI-001.md', domain: 'VERI' });
1269
+ try {
1270
+ assert.strictEqual(unapprovedVeri.capture.outlineDispatch.state, 'unresolved');
1271
+ assert.ok(unapprovedVeri.capture.outlineDispatch.reasons.includes('outline-inventory-binding-mismatch'));
1272
+ } finally { unapprovedVeri.cleanup(); }
1273
+
1274
+ const unchangedRun8 = createOutlineFixture({ retainedInventory: true, rowId: '17',
1275
+ target: 'docs/adr/EVID-001-verify-all-evidence.md', domain: 'EVID' });
1276
+ try {
1277
+ assert.strictEqual(unchangedRun8.capture.outlineDispatch.state, 'unresolved');
1278
+ assert.ok(unchangedRun8.capture.outlineDispatch.reasons.includes('outline-domain-target-mismatch'),
1279
+ 'the original descriptive filename still fails the citation-id/basename check');
1280
+ } finally { unchangedRun8.cleanup(); }
1281
+
1282
+ const substituted = createOutlineFixture({ retainedInventory: true,
1283
+ handoffOverrides: { inventory: { ...internals.OUTLINE_INVENTORY_PIN, rowId: '17', domain: 'VERI',
1284
+ targetPaths: ['docs/adr/EVID-001.md'] } } });
1285
+ try {
1286
+ assert.strictEqual(substituted.capture.outlineDispatch.state, 'unresolved');
1287
+ assert.ok(substituted.capture.outlineDispatch.reasons.includes('outline-inventory-binding-mismatch'));
1288
+ } finally { substituted.cleanup(); }
1289
+ });
1290
+
1291
+ test('separate outline export failures stay closed for digest, path, file, wrapper, mapping, and chain corruption', () => {
1292
+ const staged = stageQualificationTree('source');
1293
+ const validRecords = [qualificationDispatch(), qualificationStart(), qualificationResult(), qualificationStop()];
1294
+ try {
1295
+ const closed = (name, mutate, records = validRecords) => {
1296
+ const { wrapper, filePath } = writeStagedOutlineEvidence(staged, records);
1297
+ mutate(wrapper, filePath);
1298
+ const outcome = stagedOutlineOutcome(staged);
1299
+ assert.strictEqual(outcome.open, false, `${name}: ${JSON.stringify(outcome)}`);
1300
+ };
1301
+ closed('wrong digest', wrapper => {
1302
+ wrapper.sha256 = '0'.repeat(64);
1303
+ staged.writeExport(staged.validExport({ outlineDispatchCapture: wrapper }));
1304
+ });
1305
+ closed('unsafe filename', wrapper => {
1306
+ wrapper.file = '../doc-reconciliation-outline-capture.sanitized.jsonl';
1307
+ staged.writeExport(staged.validExport({ outlineDispatchCapture: wrapper }));
1308
+ });
1309
+ closed('missing file', (_wrapper, filePath) => fs.rmSync(filePath));
1310
+ closed('malformed file', (wrapper, filePath) => {
1311
+ const bytes = Buffer.from('{malformed json}\n');
1312
+ fs.writeFileSync(filePath, bytes);
1313
+ wrapper.sha256 = sha(bytes);
1314
+ staged.writeExport(staged.validExport({ outlineDispatchCapture: wrapper }));
1315
+ });
1316
+ closed('malformed wrapper', () => staged.writeExport(staged.validExport({ outlineDispatchCapture: null })));
1317
+ closed('mapping mismatch', wrapper => {
1318
+ wrapper.mapping = { ...outlineMappingV2, workerRootField: 'cwd.unobserved' };
1319
+ staged.writeExport(staged.validExport({ outlineDispatchCapture: wrapper }));
1320
+ });
1321
+ const crossSession = validRecords.map(record => ({ ...record }));
1322
+ crossSession[2].session_id = 'other-coordinator';
1323
+ closed('only dispatch chain crosses coordinator sessions', () => {}, crossSession);
1324
+ const wrongRoot = validRecords.map(record => ({ ...record }));
1325
+ wrongRoot[1].cwd = '/tmp/other-worker';
1326
+ closed('only dispatch chain has a worker-root mismatch', () => {}, wrongRoot);
1327
+ } finally { staged.cleanup(); }
1328
+ });
1329
+
1074
1330
  function outlineCorrelation(records, anchor = records.find(item => item.event === 'PreToolUse')) {
1075
1331
  return internals.correlateOutlineDispatch(records, anchor, '/tmp/prb-worker');
1076
1332
  }
@@ -1215,22 +1471,13 @@ test('runtime resolution accepts Stop after the completed result through the syn
1215
1471
  } finally { fixture.cleanup(); }
1216
1472
  });
1217
1473
 
1218
- test('production export without an outline mapping keeps outline-live-gate-unmet', () => {
1219
- const fixture = createOutlineFixture();
1474
+ test('missing outline wrapper keeps outline-live-gate-unmet', () => {
1475
+ const staged = stageQualificationTree('source');
1220
1476
  try {
1221
- const completed = fixture.finishDraft();
1222
- const production = provider.createClaudeOriginProvider({
1223
- hostId: 'claude-code', sessionId: fixture.sessionId, workspaceRoot: fixture.root,
1224
- workflowId: fixture.workflowId, workflowRevision: fixture.workflowRevision, taskId: fixture.taskId,
1225
- principals: { author: fixture.sessionId, executors: [] },
1226
- baseline: { root: fixture.root, commit: fixture.baselineCommit }, captures: completed.captures,
1227
- sessionDir: fixture.sessionDir, taskBinding: fixture.taskBinding,
1228
- outlineFixture: { expectedPin: fixture.expectedPin },
1229
- });
1230
- const result = production.resolveEvent({ reference: `${fixture.capture.ref}#outline-confirmation`, identity: completed.identity });
1231
- assert.strictEqual(result.status, 'unavailable');
1232
- assert.ok(result.diagnostics.some(item => item.code === 'outline-live-gate-unmet'), JSON.stringify(result));
1233
- } finally { fixture.cleanup(); }
1477
+ staged.writeExport(staged.validExport());
1478
+ const qualification = stagedOutlineOutcome(staged);
1479
+ assert.strictEqual(qualification.open, false, JSON.stringify(qualification));
1480
+ } finally { staged.cleanup(); }
1234
1481
  });
1235
1482
 
1236
1483
  test('original B and immutable attempt chain survive reconfirmation; stale attempts/tokens never cover new scope', () => {
@@ -1181,12 +1181,14 @@ Only direct, single, registered Node invocations are credited. Compound shell co
1181
1181
 
1182
1182
  Candidate classification follows command roles, not arbitrary text matches. A direct `doc-check`/`doc-check.js` target, a checker script operand to a recognized launcher, or a checker command in a recognized shell command-string remains a candidate when its launcher, subcommand, flags, quoting or composition is unsupported; it is refused as `unregistered-command` and cannot fall through to generic verification. An unrelated command whose data argument mentions `doc-check`—including workflow-plan JSON, `echo` text or a supplied filename—is not a candidate. Candidate detection never authorizes a wrapper, and opaque aliases or dynamically constructed targets remain unsupported.
1183
1183
 
1184
+ Default `check` remains non-generating, and `compare-derived` remains a pure comparison of caller-captured bytes. One explicitly requested, registered `check --derived-view <path>` invocation may generate the configured lifecycle index in memory and compare it with the declared view; it never writes the view. Automatic collection and reconciliation still do not execute generators.
1185
+
1184
1186
  | Capability | Credit needs |
1185
1187
  |---|---|
1186
1188
  | `doc-lint` | One projection from an observed successful `check`: lint exit 0, valid schema, the exact expected governed and baseline paths, and complete relevant coverage |
1187
1189
  | `link-check` | A separate projection of the same invocation, scoped to the expected paths: no broken, skipped, unreadable or unsupported link syntax |
1188
- | `derived-regenerate` | An observed successful generation **and** an observed successful pure comparison, bound to the same source and config identity and to the same generated bytes. `--generation-status ok` is caller-reported and cannot establish generation alone |
1189
- | `quality-review` | A completed review from a correlated host `agent_id` distinct from the author session and every executor. Executors are the author session and the principals of registered doc-check runs in the task, which are the execution evidence a doc obligation uses (see the rationale below). An Agent/Task result must name the same `agent_id`. The verdict needs an explicit approve line, a non-empty summary and one `Reviewed artifact: <path> sha256:<hex>` line per reviewed path, which must match the candidate. SubagentStop alone is never approval |
1190
+ | `derived-regenerate` | An observed successful generation and an observed successful pure byte comparison, bound to the same source inventory, config identity and generated bytes. Both operations MAY occur inside one explicitly requested, registered checker invocation: the checker executes the supported configured generator, retains its success and generated-output digest, and compares that output with the declared view without writing the view. Credit requires unchanged input identities and an observed matching target digest. Caller-reported `--generation-status ok` cannot establish generation alone |
1191
+ | `quality-review` | A completed review from a correlated host `agent_id` distinct from the author session and every executor. Executors are the author session and the principals of registered doc-check runs in the task, which are the execution evidence a doc obligation uses (see the rationale below). An Agent/Task result must name the same `agent_id`. The pilot offers only SubagentStop records with an explicit structured `Review verdict: approve|reject`; ordinary completion summaries are not review candidates, while missing or malformed required review remains unresolved. An approve needs a non-empty summary and `Reviewed artifact: <path> sha256:<hex>` lines whose exact path set matches the obligation scope and candidate. SubagentStop alone is never approval |
1190
1192
  | `conflict-check` | A registered `check --conflict-assessment` runs lifecycle lint with `--all` and requires an exact `--base` commit SHA; the corpus collection must be complete and lint must have no error diagnostics. The event keeps the exact obligation `normativePaths` as its scope and binds the full corpus paths/digests, config bytes and base commit separately to the workflow snapshot. This checks recorded and structural conflicts only; prose contradictions stay with independent `quality-review`. Plain `check` earns no conflict credit |
1191
1193
  | `requirement-sync` | Stay unresolved. F4 gives it no credit |
1192
1194
  | SHOULD reminders | Never earn credit, and their absence never withholds it |
@@ -1213,7 +1215,7 @@ claude -p "<fixed capture prompt, containing the exact check command>" \
1213
1215
 
1214
1216
  **Retained artifacts** go to `benchmarks/results/live-host/claude-325-3b-s2b/`: `capture.sanitized.jsonl`, `README.md`, the relevant stream-json transcript, settings, manifest and wrapper digests, the host version, the runtime and tree fingerprint, and the command and exit summary. Raw payloads stay private, outside the repository. Transcript text alone is not hook evidence.
1215
1217
 
1216
- **Qualification export** is a sanitized file at the canonical path `hooks/scripts/lib/doc-reconciliation-qualification.json`, copied by plugin sync, with a byte-identical copy of its sanitized capture beside it (`doc-reconciliation-capture.sanitized.jsonl`). The runtime validates schema, host version, payload kinds, correlation and the capture digest against that sibling file. It does not search ancestor directories and does not accept a file by size.
1218
+ **Qualification export** is the sanitized file at `hooks/scripts/lib/doc-reconciliation-qualification.json`, copied by plugin sync. Its base `sanitizedCapture` and validation stay unchanged. Outline qualification uses a separate `outlineDispatchCapture` wrapper and sibling file (`doc-reconciliation-outline-capture.sanitized.jsonl`); the runtime checks the wrapper's sibling filename, SHA-256, object records and exact outline mapping. Neither path searches ancestor directories or accepts a file by size.
1217
1219
 
1218
1220
  ### Scope and limits
1219
1221
 
@@ -1232,7 +1234,7 @@ claude -p "<fixed capture prompt, containing the exact check command>" \
1232
1234
 
1233
1235
  ## PR-B: dedicated pre-draft outline confirmation
1234
1236
 
1235
- Status: **implemented behind the existing pilot switch; mechanism and package checks pass; no positive live-host credit is enabled.** This capability follows owner decisions 9 and 11: an independent reviewer approves exact O before drafting; the outline cites one row from the pinned inventory; final D is a strict descendant of O; a coordinator handoff records review-complete → fresh-draft-dispatch. Owner approval is provenance only and never a runtime reviewer principal.
1237
+ Status: **implemented behind the existing pilot switch; mechanism and package checks pass; the retained Claude Code 2.1.312 run8 profile is qualified for outline evidence.** Run8 was captured at main baseline `1bd0f805031c5577a6e7f72a9bc5b41618b71fc1`. This does not establish runtime version enforcement or an E1 qualifying cycle. OpenCode, Codex and other Claude Code versions remain unqualified. The capability follows owner decisions 9 and 11: an independent reviewer approves exact O before drafting; the outline cites one row from the pinned inventory; final D is a strict descendant of O; a coordinator handoff records review-complete → fresh-draft-dispatch. Owner approval is provenance only and never a runtime reviewer principal.
1236
1238
 
1237
1239
  ```mermaid
1238
1240
  flowchart LR
@@ -1250,12 +1252,14 @@ flowchart LR
1250
1252
  | Evidence | Required binding |
1251
1253
  |---|---|
1252
1254
  | B and task | Keep the workflow-start B commit/root/tree and ordered requirement IDs for every attempt. Config scope must still match B. An attempt cannot reset B to O. |
1253
- | Inventory | The coordinator supplies exact bytes and `inventory-pin.json` outside the candidate tree. The pinned source is `report-inv.md`, SHA-256 `2741bd4514d2cd68f57d93a2b3ce94cbe597ca62d30e26f0ca58e882df5b03c6`, run `e1-inv-1`, input SHA `0ef0541c6cae550e8269ba712e03797e0e987dfb`, approval `#325` comment `6436207729`, decision 1. The provider rehashes the bytes and compares every pin field before resolving one row and domain. Missing, stale, duplicate, ambiguous, wrong-target or unavailable evidence is unresolved with no fallback. Synthetic test pins prove parser/mechanism binding only. |
1254
- | O and review | O is bound to its commit, tree, target blobs and stored-byte digests. Its `proposed` ADR frontmatter cites one `Inventory row: <id>`, target and domain, and contains no completed Decision text. A host-captured `SubagentStop` review must approve those exact O bytes and the pinned inventory, with a distinct host `agent_id` and correlated Agent/Task result. |
1255
+ | Inventory | The coordinator supplies exact bytes and `inventory-pin.json` outside the candidate tree. The pinned source is `report-inv.md`, SHA-256 `2741bd4514d2cd68f57d93a2b3ce94cbe597ca62d30e26f0ca58e882df5b03c6`, run `e1-inv-1`, input SHA `0ef0541c6cae550e8269ba712e03797e0e987dfb`, approval `#325` comment `6436207729`, decision 1. The trusted pin also records the owner-approved `VERI`→`EVID` mapping for rows 15–18 by reference to `../e1-feasibility-2026-10-28/owner-decision-veri-evid.md`; inventory bytes and digest stay unchanged. The provider rehashes the bytes and compares every pin field before resolving one row and domain. Missing, stale, duplicate, ambiguous, wrong-target or unavailable evidence is unresolved with no fallback. Synthetic test pins prove parser/mechanism binding only. |
1256
+ | O and review | O is bound to its commit, tree, target blobs and stored-byte digests. Its `proposed` ADR frontmatter cites one `Inventory row: <id>`, target and domain, with `id` equal to the target basename, and contains no completed Decision text. A host-captured `SubagentStop` review must approve those exact O bytes and the pinned inventory, with a distinct host `agent_id` and correlated Agent/Task result. |
1255
1257
  | Handoff and dispatch | One immutable attempt handoff binds B, O, scope, inventory, accepted review, coordinator principal, lease and one-use token. Agent/Task dispatch accepts exactly one `HARNESS_OUTLINE_DISPATCH_V1` block in `tool_input.prompt`, whose token must equal the handoff token; it does not search free-form text or use another field or cwd as a fallback. The outline anchor also requires explicit `run_in_background === false` and absence of any `isolation` key, including null or empty values. Before worker start, PreToolUse verifies the handoff, review, pin, exclusive lease, canonical root, HEAD/tree at O and clean index/worktree including untracked files; it creates a write-once handoff-digest seal, stores validation digests/reasons, then consumes the token by exclusive creation. Every predecessor seal, seam and token remains bound to the original handoff bytes, so an overwritten predecessor invalidates the chain. |
1256
1258
  | Worker and D | The dispatch key is `(session_id, tool_use_id)`. Exactly one matching Task/Agent result must carry `tool_response.status === 'completed'`; its observed `tool_response.agentId` must map to exactly one `SubagentStart` and one `SubagentStop` with the same `session_id` and `agent_id`. Require PreToolUse < SubagentStart < SubagentStop in journal order, but do not order the completed result against Stop. Both worker events need exact canonical `cwd === workerRoot`. `prompt_id` and `parent_tool_use_id` are not correlation keys; `interrupted` is not completion proof. Async launches, failures, cancellation, missing/duplicate events, replayed IDs, worktree isolation and root mismatch stay unresolved. D is checked against the current candidate separately from O. O must be a strict ancestor of D. A squash may retain the cycle only when a coordinator correspondence record proves identical target blobs between retained pre-merge D and the merged tree; changed bytes need fresh final quality review. |
1257
1259
  | Checker outcome | `outline-confirmation` has its own pass/unresolved lane. Generic command success and waivers never satisfy it. No D is incomplete/unresolved. Its result is separate from the aggregate result over every MUST derived from original B→D; an outline pass never waives lint, conflict, quality review, requirement-sync or other MUSTs. |
1258
1260
 
1259
- Capture and validation failures stay fail-open to host tools and withhold evidence credit. Unclassified shell commands and writes outside the correlated worker/root remain unresolved. Any other subagent start in the worker root rejects the outline as a competing writer, unless that start and its unique stop are both journaled before the dispatch PreToolUse (for example, a non-isolated outline reviewer). Retries use a fresh attempt, token, clean worktree and immutable handoff; only one non-superseded leaf is current. An identical same-tool-ID/input callback may reuse its stored seam, but repeated PreToolUse records remain ambiguous for evidence credit; changed input or token reuse is unresolved. OpenCode and Codex remain unsupported for positive outline credit.
1261
+ Capture and validation failures stay fail-open to host tools and withhold evidence credit. Unclassified shell commands and writes outside the correlated worker/root remain unresolved. A same-root subagent start is unresolved as a competing writer when it may overlap the exclusive draft interval, from accepted dispatch PreToolUse through the uniquely correlated draft worker’s Stop. A uniquely identified agent whose Start and unique Stop both precede dispatch, or whose Start occurs strictly after that worker Stop, does not establish overlap by its Start alone. Missing, duplicated or inconsistent lifecycle evidence stays unresolved. This exception does not authorize writes outside the correlated draft worker/root or relax independent review, artifact identity or final-byte validation.
1262
+
1263
+ Retries use a fresh attempt, token, clean worktree and immutable handoff; only one non-superseded leaf is current. An identical same-tool-ID/input callback may reuse its stored seam, but repeated PreToolUse records remain ambiguous for evidence credit; changed input or token reuse is unresolved. OpenCode and Codex remain unsupported for positive outline credit.
1260
1264
 
1261
- The current retained Claude qualification does not establish the versioned `claude-outline-dispatch-mapping/2` contract. The outline provider therefore grants no positive live-host credit until a retained Claude capture validates the prompt, coordinator, result status/agent ID, explicit non-background boundary, isolation absence and exact worker-root mapping. Deterministic fixtures exercise the real hook/provider/adapter/checker/pilot path with a synthetic inventory pin; they are mechanism evidence, not live-host behavior or E1 readiness.
1265
+ The separate run8 capture establishes the versioned `claude-outline-dispatch-mapping/2` field contract for the observed Claude Code 2.1.312 profile: prompt and coordinator identity, completed result status and agent ID, explicit non-background dispatch, isolation absence, and matching worker root. The production export reads this capture from its own sibling file and keeps the base qualification capture unchanged. The retained run8 assessment reports `resolveIfQualified.status: accepted`; it is not an independent rerun. Deterministic fixtures continue to exercise the real hook/provider/adapter/checker/pilot path with a synthetic inventory pin as mechanism evidence. Neither evidence source is an E1 qualifying cycle or a claim for OpenCode, Codex or other Claude Code versions.
@@ -210,7 +210,7 @@ The editorial doc check (`harness-everything/scripts/doc-check.js`, F1) is reach
210
210
 
211
211
  Credited reconciliation for F4 (Claude Code only, pilot ON) is **implemented, with mechanism and package evidence and one live field-availability capture**. Credit works for `doc-lint` and `link-check` through a registered `doc-check` run, and for an independent `quality-review` with bound path digests. The capture under `benchmarks/results/live-host/claude-325-3b-s2b/` proves field availability on Claude Code 2.1.308 for one session. It is not live-host credit, and no live-host credit run exists yet. Deterministic mechanism tests (replay through the real hook entry points, with a CI-only qualification preloader) prove the chain.
212
212
 
213
- PR-B adds a dedicated `outline-confirmation` evidence lane for Claude captures with separate B/O/D checks, a coordinator-sealed pre-dispatch handoff, pinned-inventory row binding, and an independent exact-O review. It is **mechanism and package evidence only**. The retained qualification does not yet establish `tool_input.prompt`, the coordinator identity, synchronous PreToolUse boundary or worker-root mapping, so positive live-host outline credit remains disabled. OpenCode and Codex receive no outline credit; missing or ambiguous capture stays unresolved and host tools remain fail-open. The outline result does not replace the aggregate B→D MUST result or the existing exact-D `quality-review` contract.
213
+ PR-B adds a dedicated `outline-confirmation` evidence lane for Claude captures with separate B/O/D checks, a coordinator-sealed pre-dispatch handoff, pinned-inventory row binding, and an independent exact-O review. A separate sanitized capture now qualifies the **observed Claude Code 2.1.312 run8 profile**, recorded at main baseline `1bd0f805031c5577a6e7f72a9bc5b41618b71fc1`. This is retained field-mapping evidence, not runtime version enforcement or an E1 qualifying cycle. OpenCode, Codex and other Claude Code versions receive no outline qualification. Missing or ambiguous capture stays unresolved and host tools remain fail-open. The outline result does not replace the aggregate B→D MUST result or the existing exact-D `quality-review` contract.
214
214
 
215
215
  ### Own-repository governance guard (PR #376)
216
216
 
@@ -4,7 +4,7 @@ description: "Route software/project work through Harness: classify tier/topolog
4
4
  license: Apache-2.0
5
5
  metadata:
6
6
  author: Miya Daniel
7
- version: 0.46.0
7
+ version: 0.47.0
8
8
  ---
9
9
 
10
10
  # Harness Everything