harness-everything 0.38.0 → 0.40.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.
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/AGENTS.md +79 -63
- package/CHANGELOG.md +32 -0
- package/benchmarks/results/reviews/pr353-r6-2026-10-22/README.md +76 -0
- package/benchmarks/results/reviews/pr353-r6-2026-10-22/a-reviews-b.md +191 -0
- package/benchmarks/results/reviews/pr353-r6-2026-10-22/b-reviews-a.md +100 -0
- package/benchmarks/results/reviews/pr353-r6-2026-10-22/baseline-probes.log +47 -0
- package/benchmarks/results/reviews/pr353-r6-2026-10-22/bom-red.log +85 -0
- package/benchmarks/results/reviews/pr353-r6-2026-10-22/bom-transition.txt +25 -0
- package/benchmarks/results/reviews/pr353-r6-2026-10-22/coordinator-probes.txt +84 -0
- package/benchmarks/results/reviews/pr353-r6-2026-10-22/cross-suite.txt +18 -0
- package/benchmarks/results/reviews/pr353-r6-2026-10-22/final-bom.log +3 -0
- package/benchmarks/results/reviews/pr353-r6-2026-10-22/final-consistency.log.gz +0 -0
- package/benchmarks/results/reviews/pr353-r6-2026-10-22/final-core.log +198 -0
- package/benchmarks/results/reviews/pr353-r6-2026-10-22/final-probes.log +27 -0
- package/benchmarks/results/reviews/pr353-r6-2026-10-22/final-producer.log +83 -0
- package/benchmarks/results/reviews/pr353-r6-2026-10-22/final-review.md +89 -0
- package/benchmarks/results/reviews/pr353-r6-2026-10-22/final-transitions.log +5 -0
- package/benchmarks/results/reviews/pr353-r6-2026-10-22/implementation-a.md +166 -0
- package/benchmarks/results/reviews/pr353-r6-2026-10-22/implementation-b.md +264 -0
- package/benchmarks/results/reviews/pr353-r6-2026-10-22/logs.sha256 +15 -0
- package/benchmarks/results/reviews/pr353-r6-2026-10-22/peer-a-probe.log +62 -0
- package/benchmarks/results/reviews/pr353-r6-2026-10-22/peer-a-probe.txt +313 -0
- package/benchmarks/results/reviews/pr353-r6-2026-10-22/peer-b-probe.log +51 -0
- package/benchmarks/results/reviews/pr353-r6-2026-10-22/peer-b-probe.txt +94 -0
- package/benchmarks/results/reviews/pr353-r6-2026-10-22/semantic-transitions.txt +25 -0
- package/benchmarks/results/reviews/pr353-r6-2026-10-22/source.sha256 +9 -0
- package/benchmarks/results/reviews/pr353-r6-2026-10-22/tests-a-on-b-core.log +201 -0
- package/benchmarks/results/reviews/pr353-r6-2026-10-22/tests-a-on-b-producer.log +82 -0
- package/benchmarks/results/reviews/pr353-r6-2026-10-22/tests-b-on-a-core.log +197 -0
- package/benchmarks/results/reviews/pr353-r6-2026-10-22/tests-b-on-a-producer.log +82 -0
- package/benchmarks/results/reviews/pr353-r6-2026-10-22/verify-all.log.gz +0 -0
- package/benchmarks/results/reviews/pr353-r6-2026-10-22/verify-gate-summary.json +22 -0
- package/ci/fixtures/doc-obligations/reconciliation-s2a/harness.js +489 -0
- package/ci/fixtures/doc-obligations/reconciliation-s2a/vectors.json +617 -0
- package/ci/mechanism-325-3b-s2a-core.test.js +840 -0
- package/ci/mechanism-325-3b-s2a-producer.test.js +564 -0
- package/code-review/SKILL.md +3 -3
- package/code-review/references/review-playbook.md +4 -0
- package/code-review/templates/finding.template.md +6 -0
- package/docs/doc-workflow-reconciliation.md +357 -0
- package/evals/zoom-out/tasks/negative-first-edge-case-review.yaml +16 -0
- package/evals/zoom-out/tasks/positive-repair-loop.yaml +15 -0
- package/harness-everything/SKILL.md +1 -1
- package/harness-everything/scripts/project-docs-resolver.js +42 -8
- package/harness-everything/scripts/routing-keywords.json +4 -0
- package/hooks/scripts/lib/doc-reconciliation-core.js +280 -0
- package/hooks/scripts/lib/doc-reconciliation-events.js +141 -0
- package/hooks/scripts/lib/doc-reconciliation-producer.js +206 -0
- package/multi-agent-workspace/SKILL.md +1 -1
- package/multi-agent-workspace/scripts/project-docs-resolver.js +42 -8
- package/opencode-plugin/plugin.json +1 -1
- package/package.json +2 -1
- package/plugins/harness-everything/.codex-plugin/plugin.json +1 -1
- package/plugins/harness-everything/hooks/scripts/lib/doc-reconciliation-core.js +280 -0
- package/plugins/harness-everything/hooks/scripts/lib/doc-reconciliation-events.js +141 -0
- package/plugins/harness-everything/hooks/scripts/lib/doc-reconciliation-producer.js +206 -0
- package/plugins/harness-everything/plugin.json +1 -1
- package/plugins/harness-everything/skills/code-review/SKILL.md +3 -3
- package/plugins/harness-everything/skills/code-review/references/review-playbook.md +4 -0
- package/plugins/harness-everything/skills/code-review/templates/finding.template.md +6 -0
- package/plugins/harness-everything/skills/harness-everything/SKILL.md +1 -1
- package/plugins/harness-everything/skills/harness-everything/scripts/project-docs-resolver.js +42 -8
- package/plugins/harness-everything/skills/harness-everything/scripts/routing-keywords.json +4 -0
- package/plugins/harness-everything/skills/multi-agent-workspace/SKILL.md +1 -1
- package/plugins/harness-everything/skills/multi-agent-workspace/scripts/project-docs-resolver.js +42 -8
- package/plugins/harness-everything/skills/to-spec/SKILL.md +1 -1
- package/plugins/harness-everything/skills/to-spec/scripts/project-docs-resolver.js +42 -8
- package/plugins/harness-everything/skills/zoom-out/SKILL.md +4 -3
- package/plugins/harness-everything/skills/zoom-out/references/circuit-breaker.md +7 -0
- package/plugins/harness-everything/skills/zoom-out/templates/zoom-out-report.template.md +4 -2
- package/sbom.cdx.json +7 -7
- package/scripts/errors-sync.js +90 -0
- package/to-spec/SKILL.md +1 -1
- package/to-spec/scripts/project-docs-resolver.js +42 -8
- package/zoom-out/SKILL.md +4 -3
- package/zoom-out/references/circuit-breaker.md +7 -0
- package/zoom-out/templates/zoom-out-report.template.md +4 -2
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
PASS snapshot:required-fields-present [source]
|
|
2
|
+
PASS snapshot:required-fields-present [packaged]
|
|
3
|
+
PASS snapshot:identity-tuple-types-and-digest-equalities [source]
|
|
4
|
+
PASS snapshot:identity-tuple-types-and-digest-equalities [packaged]
|
|
5
|
+
PASS snapshot:obligations-equal-unchanged-derive-api-on-captured-bytes [source]
|
|
6
|
+
PASS snapshot:obligations-equal-unchanged-derive-api-on-captured-bytes [packaged]
|
|
7
|
+
PASS snapshot:derivation-reasons-cover-every-obligation [source]
|
|
8
|
+
PASS snapshot:derivation-reasons-cover-every-obligation [packaged]
|
|
9
|
+
PASS A04:docs-prd-root-positive-produces-ready-snapshot [source]
|
|
10
|
+
PASS A04:docs-prd-root-positive-produces-ready-snapshot [packaged]
|
|
11
|
+
PASS A04:custom-prd-root-unavailable-with-no-fallback [source]
|
|
12
|
+
PASS A04:custom-prd-root-unavailable-with-no-fallback [packaged]
|
|
13
|
+
PASS A05:whole-candidate-identity-is-not-the-prd-set-digest [source]
|
|
14
|
+
PASS A05:whole-candidate-identity-is-not-the-prd-set-digest [packaged]
|
|
15
|
+
PASS A05:deletion-and-rename-endpoints-are-both-bound [source]
|
|
16
|
+
PASS A05:deletion-and-rename-endpoints-are-both-bound [packaged]
|
|
17
|
+
PASS A05:deleted-tombstones-omitted-from-checker-artifacts [source]
|
|
18
|
+
PASS A05:deleted-tombstones-omitted-from-checker-artifacts [packaged]
|
|
19
|
+
PASS A05:deleted-path-differs-from-empty-file-in-candidate-digest [source]
|
|
20
|
+
PASS A05:deleted-path-differs-from-empty-file-in-candidate-digest [packaged]
|
|
21
|
+
PASS framing:path-boundary-concatenation-does-not-collide [source]
|
|
22
|
+
PASS framing:path-boundary-concatenation-does-not-collide [packaged]
|
|
23
|
+
PASS framing:mode-drift-changes-candidate-digest [source]
|
|
24
|
+
PASS framing:mode-drift-changes-candidate-digest [packaged]
|
|
25
|
+
PASS A09:pinned-baseline-remains-after-fixture-commit [source]
|
|
26
|
+
PASS A09:pinned-baseline-remains-after-fixture-commit [packaged]
|
|
27
|
+
PASS A09:head-anchored-capture-has-a-different-baseline-identity [source]
|
|
28
|
+
PASS A09:head-anchored-capture-has-a-different-baseline-identity [packaged]
|
|
29
|
+
PASS A09:rename-endpoints-stable-across-repeated-captures [source]
|
|
30
|
+
PASS A09:rename-endpoints-stable-across-repeated-captures [packaged]
|
|
31
|
+
PASS R2d-P1:reference-only-provider-context-is-baseline-unavailable [source]
|
|
32
|
+
PASS R2d-P1:reference-only-provider-context-is-baseline-unavailable [packaged]
|
|
33
|
+
PASS R2d-P1:input-baseline-re-anchored-to-moved-head-is-never-ready [source]
|
|
34
|
+
PASS R2d-P1:input-baseline-re-anchored-to-moved-head-is-never-ready [packaged]
|
|
35
|
+
PASS R2d-P1:input-baseline-equal-to-the-provider-pin-stays-ready-after-head-moved [source]
|
|
36
|
+
PASS R2d-P1:input-baseline-equal-to-the-provider-pin-stays-ready-after-head-moved [packaged]
|
|
37
|
+
PASS R2d-P2a:nested-scope-policy-status-is-a-semantic-change [source]
|
|
38
|
+
PASS R2d-P2a:nested-scope-policy-status-is-a-semantic-change [packaged]
|
|
39
|
+
PASS R2d-P2a:nested-requirement-and-binding-semantics-change-the-digest [source]
|
|
40
|
+
PASS R2d-P2a:nested-requirement-and-binding-semantics-change-the-digest [packaged]
|
|
41
|
+
PASS A10:missing-baseline-commit-yields-baseline-unavailable [source]
|
|
42
|
+
PASS A10:missing-baseline-commit-yields-baseline-unavailable [packaged]
|
|
43
|
+
PASS A10:non-repository-baseline-root-yields-baseline-unavailable [source]
|
|
44
|
+
PASS A10:non-repository-baseline-root-yields-baseline-unavailable [packaged]
|
|
45
|
+
PASS A10:normal-git-capture-succeeds [source]
|
|
46
|
+
PASS A10:normal-git-capture-succeeds [packaged]
|
|
47
|
+
PASS A11:ignored-governed-path-included-or-unavailable-never-silent [source]
|
|
48
|
+
PASS A11:ignored-governed-path-included-or-unavailable-never-silent [packaged]
|
|
49
|
+
PASS A11:index-removed-ignored-file-included-or-unavailable [source]
|
|
50
|
+
PASS A11:index-removed-ignored-file-included-or-unavailable [packaged]
|
|
51
|
+
PASS A11:ignored-irrelevant-control-has-no-new-must-and-a-justified-exclusion [source]
|
|
52
|
+
PASS A11:ignored-irrelevant-control-has-no-new-must-and-a-justified-exclusion [packaged]
|
|
53
|
+
PASS A03:captured-resolution-equals-existing-resolver-project-config [source]
|
|
54
|
+
PASS A03:captured-resolution-equals-existing-resolver-project-config [packaged]
|
|
55
|
+
PASS A03:project-overrides-host-positive-equals-existing-resolver [source]
|
|
56
|
+
PASS A03:project-overrides-host-positive-equals-existing-resolver [packaged]
|
|
57
|
+
PASS A03:equal-host-positive-equals-existing-resolver [source]
|
|
58
|
+
PASS A03:equal-host-positive-equals-existing-resolver [packaged]
|
|
59
|
+
PASS A03:capture-binds-exact-config-bytes-and-selection [source]
|
|
60
|
+
PASS A03:capture-binds-exact-config-bytes-and-selection [packaged]
|
|
61
|
+
PASS A03:absent-project-config-yields-config-unavailable [source]
|
|
62
|
+
PASS A03:absent-project-config-yields-config-unavailable [packaged]
|
|
63
|
+
PASS purity:producer-capture-writes-nothing-spawns-only-git [source]
|
|
64
|
+
PASS purity:producer-capture-writes-nothing-spawns-only-git [packaged]
|
|
65
|
+
PASS R3-P2:unchanged-binary-asset-captures-without-changing-applicability [source]
|
|
66
|
+
PASS R3-P2:unchanged-binary-asset-captures-without-changing-applicability [packaged]
|
|
67
|
+
PASS R3-P2:changed-binary-asset-changes-the-candidate-digest-without-changing-applicability [source]
|
|
68
|
+
PASS R3-P2:changed-binary-asset-changes-the-candidate-digest-without-changing-applicability [packaged]
|
|
69
|
+
PASS R3-P2:undecodable-governed-document-record-fails-closed [source]
|
|
70
|
+
PASS R3-P2:undecodable-governed-document-record-fails-closed [packaged]
|
|
71
|
+
PASS R3-P2:undecodable-derived-output-record-fails-closed [source]
|
|
72
|
+
PASS R3-P2:undecodable-derived-output-record-fails-closed [packaged]
|
|
73
|
+
PASS R3-P2:undecodable-code-input-record-fails-closed [source]
|
|
74
|
+
PASS R3-P2:undecodable-code-input-record-fails-closed [packaged]
|
|
75
|
+
PASS R3-P2:undecodable-test-input-record-fails-closed [source]
|
|
76
|
+
PASS R3-P2:undecodable-test-input-record-fails-closed [packaged]
|
|
77
|
+
PASS R3-P2:undecodable-frontmatter-plain-fails-closed [source]
|
|
78
|
+
PASS R3-P2:undecodable-frontmatter-plain-fails-closed [packaged]
|
|
79
|
+
PASS R3-P2:undecodable-frontmatter-bom-fails-closed [source]
|
|
80
|
+
PASS R3-P2:undecodable-frontmatter-bom-fails-closed [packaged]
|
|
81
|
+
|
|
82
|
+
S2a producer summary: 80 PASS, 0 FAIL, 0 INFRA across 80 assertion-legs
|
|
83
|
+
S2a producer suite: all assertions PASS.
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# Final integration review — PR #353 (selected tree)
|
|
2
|
+
|
|
3
|
+
Scope: **read-only**, tightly scoped to the selected tree `/Users/daniel.chang/Desktop/Skills/Harness-everything/.worktrees/fix-353`
|
|
4
|
+
compared against frozen A `/Users/daniel.chang/Desktop/Skills/Harness-everything/.worktrees/353-r6-a`.
|
|
5
|
+
No file was edited, no test suite or gate was executed, no background command was launched, no scope was widened.
|
|
6
|
+
|
|
7
|
+
## Verdict
|
|
8
|
+
|
|
9
|
+
**ACCEPT — no blocker found.** The only functional delta is the BOM-corrected frontmatter classifier, it
|
|
10
|
+
agrees with the fatal `TextDecoder` leading-BOM semantics, and the new producer regressions pin it.
|
|
11
|
+
The two known non-blocking items (malformed-record diagnostic drift, mirrored-rules maintenance risk)
|
|
12
|
+
are unchanged and remain documented. **No PR-readiness claim is made here; the owner decides.**
|
|
13
|
+
|
|
14
|
+
## What was compared (read-only)
|
|
15
|
+
|
|
16
|
+
`diff -u` of every file in the two worktrees plus `git status --porcelain -uall` and `shasum -a 256`:
|
|
17
|
+
|
|
18
|
+
| File | Delta vs frozen A |
|
|
19
|
+
| --- | --- |
|
|
20
|
+
| `hooks/scripts/lib/doc-reconciliation-core.js` | `opensFrontmatter` only, plus the rule comment: `subarray(0,8).toString('latin1')` → `subarray(0,11).toString('utf8').replace(/^\uFEFF/,'')`; comment gains "Match TextDecoder's BOM handling" |
|
|
21
|
+
| `hooks/scripts/lib/doc-reconciliation-core.js` (rule comment) | wording tightened + `// shortcut: mirrors derive path rules, update them together until a shared classifier is exposed.` |
|
|
22
|
+
| `hooks/scripts/lib/doc-reconciliation-producer.js` | **identical** to frozen A |
|
|
23
|
+
| `hooks/scripts/lib/doc-reconciliation-events.js` | **identical** to frozen A |
|
|
24
|
+
| `plugins/harness-everything/hooks/scripts/lib/doc-reconciliation-{core,producer}.js` | regenerated copies of the same deltas; SHA-256 identical to the canonical sources (`b4345651…`, `f8f5683f…`); events copy identical (`5fd2808a…`) |
|
|
25
|
+
| `ci/mechanism-325-3b-s2a-producer.test.js` | +2 assertions: `R3-P2:undecodable-frontmatter-plain-fails-closed`, `R3-P2:undecodable-frontmatter-bom-fails-closed` (loop over prefix `''` / `'\uFEFF'`, writing `assets/role.dat` = prefix + `---\nrole: prd\n---\n` + binary bytes) |
|
|
26
|
+
| `ci/mechanism-325-3b-s2a-core.test.js`, `ci/fixtures/.../harness.js`, `CHANGELOG.md` | **identical** to frozen A |
|
|
27
|
+
| `docs/doc-workflow-reconciliation.md` | wording only: adds "(including one after a leading UTF-8 BOM)"; replaces "a binary asset never changes document applicability" with "irrelevant binary assets remain usable" |
|
|
28
|
+
|
|
29
|
+
No public derive/checker API, identity/digest schema, S1 runtime, workflow wiring, version or dependency
|
|
30
|
+
changed. Scope is exactly the stated delta — **source-traced**.
|
|
31
|
+
|
|
32
|
+
## Findings
|
|
33
|
+
|
|
34
|
+
1. **The BOM classifier agrees with the fatal `TextDecoder` leading-BOM semantics** — *source-traced*.
|
|
35
|
+
The derive contract reads a file through `TextDecoder('utf-8', {fatal:true})`, whose WHATWG UTF-8 decoder
|
|
36
|
+
removes a leading `EF BB BF` from the byte stream, so the decoded text starts at the byte after the BOM.
|
|
37
|
+
The classifier reproduces exactly that: `Buffer.toString('utf8')` keeps U+FEFF as a character (it does
|
|
38
|
+
**not** strip the BOM), so the explicit `replace(/^\uFEFF/, '')` restores the decoder's behaviour; a
|
|
39
|
+
leading `EF BB BF` is by definition the BOM, so the classifier never strips something the decoder would
|
|
40
|
+
keep. The 11-byte window covers the longest relevant first line — BOM(3) + `---`(3) + CRLF(2) = 8 bytes
|
|
41
|
+
— and a longer first line cannot be `---`. A truncated BOM (`EF`, `EF BB`) decodes to U+FFFD in both
|
|
42
|
+
decoders (neither skips bytes), so the classifier's `false` matches the decoder's "no frontmatter".
|
|
43
|
+
The trailing-`\r` strip mirrors `parseFrontmatter`'s own rule exactly.
|
|
44
|
+
|
|
45
|
+
2. **The new tests pin the fix, and the RED arithmetic confirms the RED was the BOM case** — *source-traced
|
|
46
|
+
+ coordinator-executed*. `assets/role.dat` is at a path no config glob classifies, so only the frontmatter
|
|
47
|
+
rule can catch it; the frozen latin1 classifier saw `'\u00EF\u00BB\u00BF---'` and returned `false`, i.e.
|
|
48
|
+
it dropped a document-semantic record — exactly the defect the coordinator's independent BOM transition
|
|
49
|
+
probe reported for both candidates. The frozen tree's plain variant passed and only the BOM variant
|
|
50
|
+
failed, which is what the reported **78 PASS / 2 FAIL** (one failing assertion-leg per leg) and
|
|
51
|
+
**80 / 0** GREEN over 80 legs imply; the final classifier returns `true` for both variants
|
|
52
|
+
(`'---'` and `'\uFEFF---\n…'` → stripped → `'---'`). The tests fail loudly if the BOM handling regresses
|
|
53
|
+
(the producer would become `ready` and the assertion `undecodable frontmatter must not disappear from
|
|
54
|
+
semantic inputs` would fire).
|
|
55
|
+
|
|
56
|
+
3. **The change is confined to the classifier; no behaviour elsewhere moved** — *source-traced*.
|
|
57
|
+
`semanticRelevance`, `deriveFiles`, `originBound`, `configBytesBound`, the identity framing and the
|
|
58
|
+
producer/event modules are untouched, so the previously accepted P1 binding, the binary byte identity
|
|
59
|
+
(`candidateDigest`/`baselineDigest` framing), producer/core parity and the fail-closed reason codes are
|
|
60
|
+
unchanged. Packaged copies are byte-identical to the canonical files.
|
|
61
|
+
|
|
62
|
+
4. **The docs delta is wording-only and removes an over-claim** — *source-traced*. The frozen sentence
|
|
63
|
+
"a binary asset never changes document applicability" was inaccurate (a *relevant* binary asset does
|
|
64
|
+
change applicability — it fails closed); the replacement "irrelevant binary assets remain usable while
|
|
65
|
+
the identity still covers their bytes" is the accurate statement, and the BOM case is now named in the
|
|
66
|
+
contract text. No normative claim was weakened.
|
|
67
|
+
|
|
68
|
+
5. **The shortcut comment follows the repository convention** — *source-traced*. It states the limit
|
|
69
|
+
(the mirrored derive-contract path rules) and the upgrade condition ("until a shared classifier is
|
|
70
|
+
exposed"), matching the `shortcut: <limit>, <when to upgrade>` form. It also names the real risk my
|
|
71
|
+
earlier cross-review raised, so the drift exposure is now visible in the code itself.
|
|
72
|
+
|
|
73
|
+
6. **The two known non-blocking items are unchanged and remain non-blocking** — *source-traced*.
|
|
74
|
+
(a) *Malformed-record diagnostic drift*: the core catch still maps the plain `Error('inventory-unsupported')`
|
|
75
|
+
thrown by the shared `bytes()` helper to `inventory-unsupported` where the base reported `invalid-input`;
|
|
76
|
+
credit is withheld either way because any diagnostic makes `reconcileDocResults` return before the
|
|
77
|
+
eligibility branch. (b) *Mirrored-rules maintenance risk*: `CODE_EXTENSION`/`TEST_PATH`/glob semantics are
|
|
78
|
+
still mirrored inside the reconciliation module; the drift guard remains a follow-up, now documented
|
|
79
|
+
in-code. Neither is a blocker for this change, which does not touch that surface.
|
|
80
|
+
|
|
81
|
+
## Not assessed in this review
|
|
82
|
+
|
|
83
|
+
The coordinator's executed evidence was taken as given (32 independent probes pass; `verify:all`
|
|
84
|
+
13 PASS / 0 FAIL / 3 UNCHECKED; consistency rerun PASS; producer RED 78/2 → GREEN 80/0; core 195/0).
|
|
85
|
+
This review executed no tests, gates or suites, made no file edits, and did not examine live-host
|
|
86
|
+
provenance, the S1 runtime, workflow wiring or E1 readiness — all outside S2a. PR readiness is not
|
|
87
|
+
certified here.
|
|
88
|
+
|
|
89
|
+
STATUS: DONE
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
# PR #353 — Leg A implementation report
|
|
2
|
+
|
|
3
|
+
Worktree: `/Users/daniel.chang/Desktop/Skills/Harness-everything/.worktrees/353-r6-a`
|
|
4
|
+
Base: `59421f41a8ce2b00250c96634a4a43ae0ba782bf`
|
|
5
|
+
Node: v24.14.1 (`/Users/daniel.chang/.nvm/versions/node/v24.14.1/bin/node`), TMPDIR isolated to
|
|
6
|
+
`/tmp/harness-353-r6-a/legA/tmp`.
|
|
7
|
+
Evidence dir: `/tmp/harness-353-r6-a/legA` (logs in `legA/logs`).
|
|
8
|
+
No git/gh write commands were run; the coordinator owns git, packaging upload and PR readiness.
|
|
9
|
+
|
|
10
|
+
## Changed files (write-set only)
|
|
11
|
+
|
|
12
|
+
| File | Change |
|
|
13
|
+
| --- | --- |
|
|
14
|
+
| `hooks/scripts/lib/doc-reconciliation-core.js` | P1: `originBound()` binds the captured origin identity to `snapshot.identity`, the captured plan and the selected active host; `configBytesBound()` binds the captured config bytes to the captured inventory. P2: `deriveFiles()` converts only decodable records and fails closed for records the derive contract can observe; `semanticRelevance()` + `opensFrontmatter()`; coded error mapping in `validateSnapshot`. |
|
|
15
|
+
| `hooks/scripts/lib/doc-reconciliation-producer.js` | P2: the producer uses the shared `U.deriveFiles()` with the captured config (identical handling to the core re-derivation). |
|
|
16
|
+
| `plugins/harness-everything/hooks/scripts/lib/doc-reconciliation-{core,producer}.js` | Generated by `npm run plugin:sync` (byte-identical to the canonical files). |
|
|
17
|
+
| `ci/mechanism-325-3b-s2a-core.test.js` | P1 identity/plan/host/config-link regressions; P2 binary-record regressions. |
|
|
18
|
+
| `ci/mechanism-325-3b-s2a-producer.test.js` | P2 binary capture and fail-closed regressions. |
|
|
19
|
+
| `ci/fixtures/doc-obligations/reconciliation-s2a/harness.js` | Fixture-only binary support: `writeBinary()`, `makeRepo(base, binary)`, `BINARY_BYTES`, `BINARY_BYTES_CHANGED`. |
|
|
20
|
+
| `docs/doc-workflow-reconciliation.md` | S2a contract text for the origin-identity binding and the binary-record rule. |
|
|
21
|
+
| `CHANGELOG.md` | Two new topmost `[Unreleased]` entries. |
|
|
22
|
+
|
|
23
|
+
`hooks/scripts/lib/doc-reconciliation-events.js` was **not** changed: it already compares the provider
|
|
24
|
+
context with the captured origin context and calls `validateSnapshot`, so it inherits the P1 binding.
|
|
25
|
+
No public derive/checker API, identity/digest schema, S1 runtime, workflow wiring, version or dependency
|
|
26
|
+
changed. No scratch `.js` file was left in the repo (`git status` shows no untracked files).
|
|
27
|
+
|
|
28
|
+
## Design choices
|
|
29
|
+
|
|
30
|
+
### P1 — the captured origin identity must be the snapshot identity
|
|
31
|
+
|
|
32
|
+
`validateSnapshot()` now calls `originBound(snapshot)` (new, pure):
|
|
33
|
+
|
|
34
|
+
1. `originContext.{workspaceId,sessionId,workflowId,workflowRevision,taskId}` must equal
|
|
35
|
+
`identity.{...}` — the five fields of the origin-correlated identity.
|
|
36
|
+
2. The captured plan must agree with that identity: `taskSemantics.workflow.{workflowId,
|
|
37
|
+
workflowRevision,sessionId}`, `taskSemantics.task.taskId`, `taskSemantics.prdRoot`,
|
|
38
|
+
`taskSemantics.configDigest`.
|
|
39
|
+
3. The captured `originContext.activeHost` must equal the host the captured config was selected for
|
|
40
|
+
(`config.capture.activeHost`); the comparison is skipped only when neither side represents a host.
|
|
41
|
+
|
|
42
|
+
This closes the disconnect: events compare provider↔captured origin context and event identity↔
|
|
43
|
+
`snapshot.identity`, so binding the captured origin context to the identity connects the two halves. A
|
|
44
|
+
joint substitution of the captured origin context and the provider context now yields
|
|
45
|
+
`identity-mismatch` in **both** `verification` and `review` mode, in the fresh-acceptance path and in the
|
|
46
|
+
cached-acceptance path (the cached acceptance is re-validated against the snapshot on every core call, so
|
|
47
|
+
it cannot bypass the invariant).
|
|
48
|
+
|
|
49
|
+
Adjacent link closed in the same class: `configBytesBound()` requires the captured config source bytes to
|
|
50
|
+
be the bytes the captured inventory holds for the same path — the producer's own rule
|
|
51
|
+
(`doc-reconciliation-producer.js`), which the core previously did not re-check. A substituted config
|
|
52
|
+
digest is `config-capture-mismatch`.
|
|
53
|
+
|
|
54
|
+
### P2 — a whole-project inventory may contain binary records
|
|
55
|
+
|
|
56
|
+
* Stored bytes are untouched: every record's bytes stay in the `candidateDigest`/`baselineDigest`
|
|
57
|
+
framing, so a changed binary asset changes `candidateDigest`.
|
|
58
|
+
* Semantic derivation needs decoded text. A record whose bytes are not valid UTF-8 is **omitted** from
|
|
59
|
+
derivation only when the derive contract cannot observe it, and **fails closed**
|
|
60
|
+
(`inventory-unsupported`) when it can.
|
|
61
|
+
* Relevance is taken from the derive contract's own surface, not from a binary-extension allowlist:
|
|
62
|
+
the configured `governed`/`legacy` globs, the `derived` output/source globs, the derive contract's
|
|
63
|
+
code-extension and REQ-test path rules (`doc-obligations.js` `CODE_EXTENSION`, `TEST_PATH`), plus any
|
|
64
|
+
record that opens a frontmatter block (the derive contract reads the frontmatter of every captured
|
|
65
|
+
file and treats a frontmatter `role` as governed).
|
|
66
|
+
* Producer and core re-derivation share one implementation (`_internals.deriveFiles`), so both handle
|
|
67
|
+
binary records identically.
|
|
68
|
+
* The lifecycle reader, its byte-conversion rule (`workingStoredBytes`: a NUL byte means no text
|
|
69
|
+
conversion) and the producer's explicit `MAX_FILES`/`MAX_BYTES` budgets were not altered; no public
|
|
70
|
+
lifecycle helper behavior changed.
|
|
71
|
+
|
|
72
|
+
## RED / GREEN evidence
|
|
73
|
+
|
|
74
|
+
| Stage | Command | Result | Log |
|
|
75
|
+
| --- | --- | --- | --- |
|
|
76
|
+
| RED core (tests first, production unchanged) | `node ci/mechanism-325-3b-s2a-core.test.js` | **175 PASS, 18 FAIL, 0 INFRA** / 193 assertion-legs | `legA/logs/red-core.log` |
|
|
77
|
+
| RED producer | `node ci/mechanism-325-3b-s2a-producer.test.js` | **72 PASS, 4 FAIL, 0 INFRA** / 76 assertion-legs | `legA/logs/red-producer.log` |
|
|
78
|
+
| GREEN core (run 1) | same | **195 PASS, 0 FAIL, 0 INFRA** / 195 assertion-legs | `legA/logs/final-core-run1.log` |
|
|
79
|
+
| GREEN core (run 2, identical input) | same | **195 PASS, 0 FAIL, 0 INFRA**, assertion lines byte-identical to run 1 | `legA/logs/final-core-run2.log` |
|
|
80
|
+
| GREEN producer (run 1) | same | **76 PASS, 0 FAIL, 0 INFRA** / 76 assertion-legs | `legA/logs/final-producer-run1.log` |
|
|
81
|
+
| GREEN producer (run 2, identical input) | same | **76 PASS, 0 FAIL, 0 INFRA**, assertion lines byte-identical to run 1 | `legA/logs/final-producer-run2.log` |
|
|
82
|
+
|
|
83
|
+
Actual RED failure reasons (from the unchanged production code):
|
|
84
|
+
|
|
85
|
+
* Core — `12 × verification: a substituted origin identity must not earn credit`
|
|
86
|
+
(joint substitution of `workspaceId`, `sessionId`, `workflowId`, `workflowRevision`, `taskId` and
|
|
87
|
+
`activeHost` in the captured origin context **and** the provider context earned credit).
|
|
88
|
+
* Core — `2 × verification: a substituted plan and identity must not earn credit`.
|
|
89
|
+
* Core — `2 × capture with an irrelevant binary record: no decision` (the producer was unavailable for a
|
|
90
|
+
tracked binary asset).
|
|
91
|
+
* Core — `2 × the core must fail closed on a relevant undecodable record: [{"code":"invalid-input",…}]`
|
|
92
|
+
(the core had no relevance-specific reason).
|
|
93
|
+
* Producer — `4 × capture with an unchanged binary asset: state unavailable with codes
|
|
94
|
+
["inventory-unsupported"]`.
|
|
95
|
+
|
|
96
|
+
Non-vacuity probe for the added config↔inventory link (the check was temporarily disabled in the
|
|
97
|
+
production file, then restored byte-for-byte): the new assertion failed with
|
|
98
|
+
`config bytes the captured inventory contradicts must not earn credit` on both legs
|
|
99
|
+
(`legA/logs/red-config-link-3.log`), i.e. the substituted-config snapshot was **eligible** without the
|
|
100
|
+
new check. After restoring the file: 195 PASS / 0 FAIL (`legA/logs/green-core-3.log`).
|
|
101
|
+
|
|
102
|
+
## Tests executed
|
|
103
|
+
|
|
104
|
+
* `node ci/mechanism-325-3b-s2a-core.test.js` — both legs (canonical + packaged), twice.
|
|
105
|
+
* `node ci/mechanism-325-3b-s2a-producer.test.js` — both legs (canonical + packaged), twice.
|
|
106
|
+
* `npm run plugin:sync` — regenerated `plugins/harness-everything/hooks/scripts/lib/`; the packaged core
|
|
107
|
+
and producer are byte-identical to the canonical files (sha256 verified).
|
|
108
|
+
* `npm run test:consistency` — PASS (`legA/logs/final-gate-consistency.log`).
|
|
109
|
+
* `npm run test:plugin:openai` — PASS (`legA/logs/final-gate-plugin-openai.log`).
|
|
110
|
+
* `npm run test:portability` — PASS (`legA/logs/final-gate-portability.log`).
|
|
111
|
+
|
|
112
|
+
Not run (coordinator owns the full gates): `npm test`, `npm run verify:all`,
|
|
113
|
+
`ci/mechanism-test.js`, `npm run test:mechanism`.
|
|
114
|
+
|
|
115
|
+
## Source-anchored findings
|
|
116
|
+
|
|
117
|
+
* **executed** — On base `59421f4`, a joint substitution of the captured origin context and the provider
|
|
118
|
+
context (identity, events and cached acceptance left at the original tuple) earned
|
|
119
|
+
`eligible` credit in verification mode: the provider half and the event half of the trust contract were
|
|
120
|
+
disconnected (`legA/logs/red-core.log`).
|
|
121
|
+
* **executed** — On base `59421f4`, a normal tracked binary asset (`assets/logo.png`, invalid UTF-8)
|
|
122
|
+
made the producer return `inventory-unsupported`, so the whole-project capture was unusable for an
|
|
123
|
+
ordinary repository (`legA/logs/red-producer.log`).
|
|
124
|
+
* **executed** — With the P2 fix, a changed binary asset changes `candidateDigest` while the MUST set
|
|
125
|
+
stays the normative set; an unchanged binary asset leaves the capture usable and the core's
|
|
126
|
+
re-derivation accepts the snapshot.
|
|
127
|
+
* **source-traced** — The derive contract's applicability surface is governed/legacy globs, derived
|
|
128
|
+
output/source globs, `CODE_EXTENSION`, `TEST_PATH` and frontmatter `role`
|
|
129
|
+
(`harness-everything/scripts/doc-obligations.js`: `CODE_EXTENSION`/`TEST_PATH` lines 17-18,
|
|
130
|
+
`governedEntry`/`matchesAny` lines 92-95, `roleAt`/`isGoverned` lines 1055-1068,
|
|
131
|
+
`derivedEntryAffected` lines 1313-1322). These are the paths whose bytes can change applicability and
|
|
132
|
+
therefore the relevance surface used by the fix.
|
|
133
|
+
* **source-traced** — The producer already required the captured config bytes to equal the captured
|
|
134
|
+
inventory bytes for the same path (`doc-reconciliation-producer.js`, config-source loop); the core
|
|
135
|
+
re-checks the same rule now.
|
|
136
|
+
* **source-traced** — The lifecycle reader's conversion rule and the producer's explicit capture budgets
|
|
137
|
+
(`MAX_FILES`, `MAX_BYTES`) are unchanged; no public lifecycle helper was touched.
|
|
138
|
+
* **inconclusive** — Whether a production origin provider context always carries an `activeHost` is not
|
|
139
|
+
observable from this repository (the S2a fake provider carries one). The host check is skipped only
|
|
140
|
+
when neither the captured context nor the captured config represents a host.
|
|
141
|
+
|
|
142
|
+
## Remaining risks
|
|
143
|
+
|
|
144
|
+
1. The relevance rules mirror the derive contract's path rules inside the reconciliation module, because
|
|
145
|
+
`doc-obligations.js` is outside this write-set. A future change to the derive contract's code/test
|
|
146
|
+
path rules or glob semantics must be mirrored here. The duplication is two regexes plus the glob
|
|
147
|
+
semantics; the behaviour is pinned by the new tests.
|
|
148
|
+
2. A record whose first line is exactly `---` fails closed even when it is an irrelevant binary asset.
|
|
149
|
+
This is deliberate (the derive contract observes such a file's frontmatter) but can turn an exotic
|
|
150
|
+
binary asset into `inventory-unsupported`.
|
|
151
|
+
3. In the core, a relevant undecodable record is reported as `inventory-unsupported` and the decision is
|
|
152
|
+
`unresolved` (not `unavailable`). Credit is withheld either way; the reason code differs from the
|
|
153
|
+
producer's `state: "unavailable"`.
|
|
154
|
+
4. The producer's whole-project walk still traverses `node_modules` and is bounded by the pre-existing
|
|
155
|
+
`MAX_FILES`/`MAX_BYTES` limits. That is unchanged pre-existing behavior and out of scope here.
|
|
156
|
+
5. Evidence is deterministic mechanism evidence only: the origin provider is an explicit test fake, so
|
|
157
|
+
nothing here proves live-host provenance, reviewer independence in deployment, host loading or E1
|
|
158
|
+
readiness.
|
|
159
|
+
|
|
160
|
+
## Handover
|
|
161
|
+
|
|
162
|
+
Leg A is complete and self-contained in its worktree. Nothing was committed, staged or pushed; the
|
|
163
|
+
coordinator owns git and PR readiness. This implementation is independent of leg B: it was developed
|
|
164
|
+
without reading the other worktree or its artifacts.
|
|
165
|
+
|
|
166
|
+
STATUS: DONE
|
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
# PR #353 — Implementation report (leg B)
|
|
2
|
+
|
|
3
|
+
Base: `59421f41a8ce2b00250c96634a4a43ae0ba782bf` (read-only `git show`/`git log` only; no git/gh write
|
|
4
|
+
commands were run, no commits, no worktree mutation, no remote messages).
|
|
5
|
+
|
|
6
|
+
Worktree: `/Users/daniel.chang/Desktop/Skills/Harness-everything/.worktrees/353-r6-b`
|
|
7
|
+
Evidence dir: `/tmp/harness-353-r6-b` (logs under `logs/`, scratch probes under `probe/`, both outside the repo)
|
|
8
|
+
|
|
9
|
+
## Round 2 — coordinator defect (executed omission defect) and its fix
|
|
10
|
+
|
|
11
|
+
**Defect (executed, coordinator-found).** The coordinator's transition probe
|
|
12
|
+
(`/tmp/harness-353-r6-coordinator/semantic-transitions.js`, log `b-transitions.log`) failed all four
|
|
13
|
+
source/packaged cases. My first implementation probed relevance with
|
|
14
|
+
`base.undecodable[path] → same text + "\n"` only: it read the **baseline** record's lossy text and never the
|
|
15
|
+
candidate's. A capture whose baseline record is nonsemantic while the candidate record's bytes carry a
|
|
16
|
+
`role: prd` frontmatter or a REQ-citing test therefore looked irrelevant, and the candidate's document
|
|
17
|
+
meaning was silently dropped from the derivation. Two further weaknesses: a same-text modification cannot
|
|
18
|
+
show new/deleted test identity, and no addition/deletion transition was probed at all.
|
|
19
|
+
|
|
20
|
+
**Fix (minimal, in the shared helper).** `deriveCapturedFacts` now probes relevance through the unchanged
|
|
21
|
+
derivation with each undecodable record's **own** bytes and three transition witnesses:
|
|
22
|
+
|
|
23
|
+
| Witness | Baseline side | Candidate side | Shows |
|
|
24
|
+
| --- | --- | --- | --- |
|
|
25
|
+
| actual | the record's baseline lossy text (if present) | the record's candidate lossy text (if present) | the real transition of this capture, carrying the candidate's own semantics |
|
|
26
|
+
| addition | the record absent | the record's lossy text | new document / new REQ-citing test identity |
|
|
27
|
+
| deletion | the record's lossy text | the record absent | deleted document / deleted test identity |
|
|
28
|
+
|
|
29
|
+
If any witness changes the derived obligations (applicability **and** scope included), the record is
|
|
30
|
+
semantically relevant and the capture fails closed as `inventory-unsupported`; otherwise it is omitted from
|
|
31
|
+
the derivation inputs. The stored bytes of every record stay in the whole-project hash, so the inventory
|
|
32
|
+
byte identity is intact (`candidateDigest` still covers binary bytes; `baselineDigest` unchanged).
|
|
33
|
+
|
|
34
|
+
**Honest scope of the claim.** These witnesses are lossy-text probes of the unchanged derivation, not a
|
|
35
|
+
proof about the true bytes of an undecodable file. They establish that the unchanged derivation finds no
|
|
36
|
+
document meaning for the record under the actual, addition and deletion transitions; they do **not**
|
|
37
|
+
establish full decision equivalence for every possible undecodable byte sequence. Where the witnesses
|
|
38
|
+
disagree the implementation fails closed, which is the safe branch.
|
|
39
|
+
|
|
40
|
+
### Round-2 evidence
|
|
41
|
+
|
|
42
|
+
| Suite | RED (v1 implementation, new tests) | GREEN (fixed) | Log |
|
|
43
|
+
| --- | --- | --- | --- |
|
|
44
|
+
| S2a core | **191 PASS, 2 FAIL, 0 INFRA** / 193 assertion-legs | **193 PASS, 0 FAIL, 0 INFRA** | `logs/RED2-core.log` → `logs/GREEN-core.log` |
|
|
45
|
+
| S2a producer | **74 PASS, 4 FAIL, 0 INFRA** / 78 assertion-legs | **78 PASS, 0 FAIL, 0 INFRA** | `logs/RED2-producer.log` → `logs/GREEN-producer.log` |
|
|
46
|
+
|
|
47
|
+
RED2 failure reason, identical on the `source` and `packaged` legs and equal to the coordinator's finding:
|
|
48
|
+
|
|
49
|
+
```
|
|
50
|
+
FAIL R2h-P2:prd-frontmatter-binary-semantics-are-never-omitted — the candidate semantics of the binary record at assets/role.dat were omitted
|
|
51
|
+
FAIL R2h-P2:req-citing-test-binary-semantics-are-never-omitted — the candidate semantics of the binary record at tests/fixture.dat were omitted
|
|
52
|
+
FAIL R2h-P2:candidate-binary-semantics-never-earn-credit-while-omitted — the candidate semantics of the binary record at assets/role.dat were omitted
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Shipped regression tests (run on both legs by the S2a runner):
|
|
56
|
+
|
|
57
|
+
- `ci/mechanism-325-3b-s2a-producer.test.js` — `R2h-P2:prd-frontmatter-binary-semantics-are-never-omitted`
|
|
58
|
+
and `R2h-P2:req-citing-test-binary-semantics-are-never-omitted`: a capture is acceptable only if it fails
|
|
59
|
+
closed with `inventory-unsupported`, or if the derived obligations still carry the candidate's semantics
|
|
60
|
+
(`role: prd` in a MUST scope / a non-N/A `requirement-sync`).
|
|
61
|
+
- `ci/mechanism-325-3b-s2a-core.test.js` — `R2h-P2:candidate-binary-semantics-never-earn-credit-while-omitted`:
|
|
62
|
+
the same property through the full produce → accept → decide flow, and no credit without accepted evidence.
|
|
63
|
+
|
|
64
|
+
Coordinator probes re-executed read-only against the fixed implementation:
|
|
65
|
+
|
|
66
|
+
```
|
|
67
|
+
node /tmp/harness-353-r6-coordinator/semantic-transitions.js <worktree> → 4 passed, 0 failed (all four now FAIL-CLOSED: state "unavailable")
|
|
68
|
+
node /tmp/harness-353-r6-coordinator/probes.js <worktree> → 26 passed, 0 failed
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
Logs: `logs/GATE-coordinator-transitions.log`, `logs/GATE-coordinator-probes.log`.
|
|
72
|
+
|
|
73
|
+
## Changed files (write-set respected)
|
|
74
|
+
|
|
75
|
+
| File | Change |
|
|
76
|
+
| --- | --- |
|
|
77
|
+
| `hooks/scripts/lib/doc-reconciliation-core.js` | P1 origin↔identity binding in `validateSnapshot`; P2 binary-safe shared derivation (`readRecords`, `decodeUtf8`, `deriveCapturedFacts`); `inventory-unsupported` mapped out of the validation catch |
|
|
78
|
+
| `hooks/scripts/lib/doc-reconciliation-producer.js` | Producer now derives through the shared `U.deriveCapturedFacts`; local `filesOf` and the now-unused derive import removed |
|
|
79
|
+
| `plugins/harness-everything/hooks/scripts/lib/doc-reconciliation-core.js` | generated copy (`npm run plugin:sync`), byte-identical to source |
|
|
80
|
+
| `plugins/harness-everything/hooks/scripts/lib/doc-reconciliation-producer.js` | generated copy (`npm run plugin:sync`), byte-identical to source |
|
|
81
|
+
| `ci/mechanism-325-3b-s2a-core.test.js` | 8 new P1/P2 assertions (7 round 1 + 1 round 2); `withVector` now returns the callback value |
|
|
82
|
+
| `ci/mechanism-325-3b-s2a-producer.test.js` | 7 new P2 assertions (5 round 1 + 2 round 2) |
|
|
83
|
+
| `ci/fixtures/doc-obligations/reconciliation-s2a/harness.js` | shared `BINARY_ASSET` / `BINARY_ASSET_CHANGED` byte fixtures (raw bytes, invalid UTF-8) |
|
|
84
|
+
| `docs/doc-workflow-reconciliation.md` | S2a contract: origin-identity binding paragraph; binary-record derivation paragraph |
|
|
85
|
+
| `CHANGELOG.md` | topmost `[Unreleased]` entry only |
|
|
86
|
+
|
|
87
|
+
Not changed (deliberately): `doc-reconciliation-events.js` needed no change — it already compares the
|
|
88
|
+
provider context with `snapshot.originContext` and calls `validateSnapshot`, so it inherits both fixes.
|
|
89
|
+
No public derive/checker API, identity/digest schema, S1 runtime, workflow wiring, version or dependency
|
|
90
|
+
was touched. No scratch `.js` file was left inside the repo (`git status --porcelain -uall` shows only the
|
|
91
|
+
nine modified files above).
|
|
92
|
+
|
|
93
|
+
## P1 — the whole captured origin identity must agree with the hashed identity
|
|
94
|
+
|
|
95
|
+
**Finding (source-traced → executed).** `validateSnapshot` bound only the baseline pin
|
|
96
|
+
(`context.baseline.commit` / `context.baseline.root`); `acceptDocEvents` compared the provider context with
|
|
97
|
+
`snapshot.originContext`, and the event's `identity` was compared with `snapshot.identity`. The
|
|
98
|
+
`originContext ↔ identity` link was missing, so the two halves never met. `snapshot.originContext` is not
|
|
99
|
+
hashed by anything (`snapshotDigest` covers baseline/coverage/records, `configDigest` covers the config,
|
|
100
|
+
`taskScopeDigest` covers the plan), so it was an unbound field.
|
|
101
|
+
|
|
102
|
+
**Fix.** In `validateSnapshot` (pure, no I/O):
|
|
103
|
+
|
|
104
|
+
- every origin-correlated identity field — `workspaceId`, `sessionId`, `workflowId`, `workflowRevision`,
|
|
105
|
+
`taskId` — must equal the same field in `snapshot.identity`; a disagreement is `identity-mismatch`;
|
|
106
|
+
- the captured `activeHost` must equal the selected config capture's `activeHost`
|
|
107
|
+
(`snapshot.config.capture.activeHost`), which is the selection context already hashed into
|
|
108
|
+
`configDigest`.
|
|
109
|
+
|
|
110
|
+
Because `validateSnapshot` runs in `acceptDocEvents` **and** in `reconcileDocResults`, the binding holds at
|
|
111
|
+
event acceptance, in `verification` mode, in `review` mode, and for a cached `accepted` report replayed
|
|
112
|
+
against the tampered snapshot. The plan side was already bound: `taskScopeDigest` covers task id, workflow
|
|
113
|
+
id/revision/session, scope, coverage and the PRD root, so a joint plan/digest change is caught by the event
|
|
114
|
+
identity check (pinned as a regression, it already passed on the RED base).
|
|
115
|
+
|
|
116
|
+
Represented tuple links inspected (not just two examples): identity↔originContext (5 fields),
|
|
117
|
+
identity↔activeHost, identity↔plan (via `taskScopeDigest`), identity↔baseline pin, identity↔baseline/
|
|
118
|
+
candidate digests, identity↔config digest, identity↔PRD root.
|
|
119
|
+
|
|
120
|
+
## P2 — a binary asset must not make the whole-project capture unsupported
|
|
121
|
+
|
|
122
|
+
**Finding (source-traced → executed).** `filesOf` decoded every captured record with
|
|
123
|
+
`new TextDecoder('utf-8', {fatal: true})`, so one ordinary tracked asset (`assets/logo.png`) made the whole
|
|
124
|
+
producer `inventory-unsupported` — fatal decoding was applied to a whole-project inventory.
|
|
125
|
+
|
|
126
|
+
**Relevance is decided by the unchanged derivation, not by an extension allowlist.** The relevance rule is
|
|
127
|
+
the derivation's own path classification (governed/legacy globs, derived paths and sources, code
|
|
128
|
+
extensions, test paths), and the unchanged `deriveDocFacts`/`deriveDocObligations` decide it: an
|
|
129
|
+
undecodable record is **probed** through the derivation with its own bytes, under the actual, addition and
|
|
130
|
+
deletion transitions (round 2), and the derived obligations (applicability *and* scope) must stay
|
|
131
|
+
identical. Only then is the record omitted from the derivation inputs; otherwise the record is semantically
|
|
132
|
+
relevant and the capture fails closed as `inventory-unsupported`. The stored bytes of every record remain
|
|
133
|
+
in the whole-project hash, so `candidateDigest` still changes when a binary asset changes.
|
|
134
|
+
|
|
135
|
+
Consequences, verified: `assets/logo.png` (irrelevant) → capture stays ready, MUST set unchanged,
|
|
136
|
+
`candidateDigest` changes; `docs/prd/logo.png` (governed), `docs/roadmap/index.md` (derived output) and
|
|
137
|
+
`src/logo.js` (code) → `inventory-unsupported`; a candidate binary carrying a `role: prd` frontmatter or a
|
|
138
|
+
REQ-citing test → `inventory-unsupported` (round 2, coordinator defect).
|
|
139
|
+
|
|
140
|
+
The bounded readers are untouched: the producer's `MAX_BYTES`, the lifecycle's bounded blob read and the
|
|
141
|
+
public lifecycle helpers (`parseFrontmatter`, `scanWorkspaceFiles`, `analyzeDocs`) are unchanged; only the
|
|
142
|
+
byte-to-text step in the S2a modules changed. Producer and core re-derivation call the same shared
|
|
143
|
+
`deriveCapturedFacts`, so a captured snapshot and its re-derivation always agree (asserted by comparing the
|
|
144
|
+
derived facts and obligations of the same repository with and without the asset).
|
|
145
|
+
|
|
146
|
+
## RED → GREEN evidence (deterministic mechanism only)
|
|
147
|
+
|
|
148
|
+
Logs: `/tmp/harness-353-r6-b/logs/`. RED was produced by restoring the two production modules (and their
|
|
149
|
+
packaged copies) from `HEAD` with read-only `git show`, running the **final** test files, then restoring the
|
|
150
|
+
implementation. Every RED failure below is the stated reason, not an artefact.
|
|
151
|
+
|
|
152
|
+
| Suite | RED (base code, final tests) | GREEN (implementation) | Log |
|
|
153
|
+
| --- | --- | --- | --- |
|
|
154
|
+
| S2a core | **177 PASS, 14 FAIL, 0 INFRA** / 191 assertion-legs | **193 PASS, 0 FAIL, 0 INFRA** / 193 | `logs/RED-core.log` → `logs/GREEN-core.log` |
|
|
155
|
+
| S2a producer | **70 PASS, 4 FAIL, 0 INFRA** / 74 assertion-legs | **78 PASS, 0 FAIL, 0 INFRA** / 78 | `logs/RED-producer.log` → `logs/GREEN-producer.log` |
|
|
156
|
+
|
|
157
|
+
Round 2 (after the coordinator defect) is reported in its own section above: `logs/RED2-*.log` →
|
|
158
|
+
`logs/GREEN-*.log`.
|
|
159
|
+
|
|
160
|
+
RED failure reasons (identical on the `source` and `packaged` legs):
|
|
161
|
+
|
|
162
|
+
```
|
|
163
|
+
FAIL R2g-P1:joint-origin-and-provider-workspaceId-substitution-rejected — expected one of ["identity-mismatch"] in []
|
|
164
|
+
FAIL R2g-P1:joint-origin-and-provider-sessionId-substitution-rejected — expected one of ["identity-mismatch"] in []
|
|
165
|
+
FAIL R2g-P1:joint-origin-and-provider-workflowId-substitution-rejected — expected one of ["identity-mismatch"] in []
|
|
166
|
+
FAIL R2g-P1:joint-origin-and-provider-workflowRevision-substitution-rejected — expected one of ["identity-mismatch"] in []
|
|
167
|
+
FAIL R2g-P1:joint-origin-and-provider-taskId-substitution-rejected — expected one of ["identity-mismatch"] in []
|
|
168
|
+
FAIL R2g-P1:joint-origin-and-provider-active-host-substitution-rejected — expected one of ["identity-mismatch"] in []
|
|
169
|
+
FAIL R2g-P2:binary-asset-capture-stays-eligible-and-derives-as-without-the-asset — no decision (producer unavailable: inventory-unsupported)
|
|
170
|
+
FAIL R2g-P2:unchanged-binary-asset-captures-and-keeps-the-document-must-set — state unavailable with codes ["inventory-unsupported"]
|
|
171
|
+
FAIL R2g-P2:changed-binary-asset-changes-the-candidate-digest-and-keeps-the-must-set — state unavailable with codes ["inventory-unsupported"]
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
i.e. on the base code the joint origin/provider substitution produced **no** diagnostic at all and stayed
|
|
175
|
+
eligible, and a binary asset made the capture unavailable.
|
|
176
|
+
|
|
177
|
+
Repeat-identical-input evidence: the two suites were run twice after the fix and the per-assertion
|
|
178
|
+
PASS/FAIL lines are identical (`logs/GREEN-core-run2.log`, `logs/GREEN-producer-run2.log`; `diff` clean).
|
|
179
|
+
|
|
180
|
+
## Tests executed
|
|
181
|
+
|
|
182
|
+
```
|
|
183
|
+
node ci/mechanism-325-3b-s2a-core.test.js PASS (193/193) logs/GREEN-core.log
|
|
184
|
+
node ci/mechanism-325-3b-s2a-producer.test.js PASS (78/78) logs/GREEN-producer.log
|
|
185
|
+
node ci/mechanism-325-3b-s1.test.js PASS logs/GATE-mechanism-325-3b-s1.log
|
|
186
|
+
node ci/mechanism-325-3b-s1-review.test.js PASS logs/GATE-mechanism-325-3b-s1-review.log
|
|
187
|
+
node ci/mechanism-325-3b-preparation.test.js PASS logs/GATE-mechanism-325-3b-preparation.log
|
|
188
|
+
node ci/mechanism-2ax-doc-lifecycle.test.js PASS logs/GATE-mechanism-2ax-doc-lifecycle.log
|
|
189
|
+
npm run test:consistency PASS logs/GATE-consistency3.log (and 1, 2)
|
|
190
|
+
npm run test:plugin:openai PASS logs/GATE-plugin-openai.log
|
|
191
|
+
npm run test:portability PASS logs/GATE-portability.log
|
|
192
|
+
npm run test:repo-contract PASS logs/GATE-repo-contract.log
|
|
193
|
+
npm run test:docs:capabilities PASS logs/GATE-docs-capabilities.log
|
|
194
|
+
npm run plugin:sync generated copies synced
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
Round-2 additions: the coordinator's `semantic-transitions.js` (4/4 PASS) and `probes.js` (26/26 PASS),
|
|
198
|
+
both re-executed read-only — `logs/GATE-coordinator-transitions.log`, `logs/GATE-coordinator-probes.log`.
|
|
199
|
+
Source and packaged copies of all three S2a modules are byte-identical (SHA-256 checked).
|
|
200
|
+
|
|
201
|
+
Not run by instruction: `npm test`, `npm run verify:all`, `ci/mechanism-test.js` (the coordinator runs the
|
|
202
|
+
full gates once).
|
|
203
|
+
|
|
204
|
+
## Findings by evidence layer
|
|
205
|
+
|
|
206
|
+
- **executed** — P1 joint substitution of every origin-correlated identity field and of `activeHost` is
|
|
207
|
+
rejected at event acceptance, in `verification`, in `review`, and against a cached accepted report; the
|
|
208
|
+
unchanged-context control stays eligible.
|
|
209
|
+
- **executed** — a tracked binary asset no longer makes the producer unavailable; `candidateDigest` still
|
|
210
|
+
changes when the asset changes; the document MUST set is unchanged; producer and core re-derivation agree.
|
|
211
|
+
- **executed** — a binary record at a governed path, a derived output path or a code path fails closed with
|
|
212
|
+
`inventory-unsupported`; the unchanged-binary and changed-binary variants are both covered.
|
|
213
|
+
- **executed (round 2)** — a candidate binary record whose bytes carry a `role: prd` frontmatter or a
|
|
214
|
+
REQ-citing test is no longer omitted: the capture fails closed, on both the source and the packaged leg.
|
|
215
|
+
The coordinator's four transition cases now pass; its 26-case probe suite still passes.
|
|
216
|
+
- **source-traced** — the plan side of the tuple (`taskId`, workflow id/revision/session, scope, coverage,
|
|
217
|
+
PRD root) is bound by `taskScopeDigest`; a joint plan/digest change is rejected by the event identity
|
|
218
|
+
check. The regression test for it already passed on the RED base (it is a pin, not a new RED).
|
|
219
|
+
- **source-traced** — the root half of the workspace binding stays out of scope, exactly as the S2a
|
|
220
|
+
contract already states: root metadata is not in the hashed identity and external worktree
|
|
221
|
+
authentication remains outside this guarantee (`docs/doc-workflow-reconciliation.md`).
|
|
222
|
+
- **inconclusive** — the completeness of the relevance witnesses. They are lossy-text probes of the
|
|
223
|
+
unchanged derivation under the actual, addition and deletion transitions. They show that the derivation
|
|
224
|
+
finds no document meaning for the record under those transitions; they do **not** prove decision
|
|
225
|
+
equivalence for every possible undecodable byte sequence, and they are not a proof about the true bytes
|
|
226
|
+
of an undecodable file.
|
|
227
|
+
- **inconclusive** — live-host provenance. Every origin in these suites is an explicit fake provider
|
|
228
|
+
(`H.FakeOrigin`). The suites prove the acceptance/decision algorithm only; they prove nothing about
|
|
229
|
+
authentic host provenance, reviewer independence in deployment, live-host loading or E1 readiness.
|
|
230
|
+
- **inconclusive** — platform coverage. The suites ran on macOS only. `test:portability` passed (no
|
|
231
|
+
Windows-hostile paths in the scanned test sources), but the Windows lane and the Node 22 runtime floor
|
|
232
|
+
were not executed locally; CI owns them.
|
|
233
|
+
|
|
234
|
+
## Remaining risks
|
|
235
|
+
|
|
236
|
+
1. **Binary relevance is decided by lossy-text witnesses of the unchanged derivation** (round 2). A record
|
|
237
|
+
whose bytes the witnesses cannot read as meaningful document structure is omitted, so
|
|
238
|
+
`facts.changedPaths` and `facts.documentAnalysis` do not mention it. Applicability and scope are
|
|
239
|
+
unchanged under every probed transition, but the informational facts no longer list the omitted path.
|
|
240
|
+
Where the witnesses disagree the capture fails closed, which is the safe branch. Documented in
|
|
241
|
+
`docs/doc-workflow-reconciliation.md`.
|
|
242
|
+
2. **The witnesses cost up to three extra `deriveDocFacts` calls** when a capture contains undecodable
|
|
243
|
+
records (batched over all such records, short-circuiting on the first difference). No extra call for
|
|
244
|
+
text-only repositories.
|
|
245
|
+
3. **`workspaceRoot` is still not hashed into the identity.** The joint provider/context pin-root
|
|
246
|
+
replacement remains detectable only for snapshot-internal consistency, as the contract already states.
|
|
247
|
+
Closing it needs a root-bound identity, which would change the identity schema — out of scope here.
|
|
248
|
+
4. **`activeHost` comparison treats "absent" on both sides as equal**, because the producer's own check
|
|
249
|
+
requires the provider's and the input's `activeHost` to be strictly equal, and an undefined input host
|
|
250
|
+
is captured as `null` by the resolver. A snapshot with an absent host on one side and a present host on
|
|
251
|
+
the other is rejected.
|
|
252
|
+
5. **Cross-leg coordination.** This leg edits `ci/fixtures/doc-obligations/reconciliation-s2a/harness.js`
|
|
253
|
+
(shared constants). If leg A edited the same fixture, the coordinator's selection must take one harness,
|
|
254
|
+
not merge both.
|
|
255
|
+
6. **The coordinator's transition probe asserts "fail closed or semantics retained"**, not which of the two
|
|
256
|
+
happens. This implementation always fails closed on those inputs. If a later revision keeps such a
|
|
257
|
+
capture ready, the shipped regression tests still require the semantics to be present in the obligations.
|
|
258
|
+
|
|
259
|
+
## Not certified
|
|
260
|
+
|
|
261
|
+
PR readiness is not certified here. The owner decides. The coordinator still owes the full `npm test` /
|
|
262
|
+
`npm run verify:all` run (including the Windows and Node 22 lanes) and the cross-validation against leg A.
|
|
263
|
+
|
|
264
|
+
STATUS: DONE
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
220657f085fd148dc8e5fc0523a3d01070b2ae8a1a3eae04ed9da17f34e031d7 baseline-probes.log
|
|
2
|
+
90349aceb2bdb4df1444d89f80494754c3a4d1e323afca5d02cacf7a3c930147 bom-red.log
|
|
3
|
+
7209e2448ea0b9ab78aa0df98d6be76facf023a50a5bc0c0825cd678f8035ec5 final-bom.log
|
|
4
|
+
d83f299638acec8476ec0801aa3cf58db98f466a226e587fa16c0a3b8e482674 final-consistency.log.gz
|
|
5
|
+
7bea7e1ff27064f04d2f5268c1e65cae547716b4970a606c2ac7316ec2d5a896 final-core.log
|
|
6
|
+
ac922d20de98c4c247d764d77b2dd4164200885e83a8cff7ef5588a9ef9a0dc2 final-probes.log
|
|
7
|
+
5b6de979d4342827c2bd0ef49867c60c60fc0e017b3ced9eb5a88b4db4f9ea78 final-producer.log
|
|
8
|
+
279830701f9804c77c778bf11d3e858c5cb20dfa8ab75288a47655bebd87855e final-transitions.log
|
|
9
|
+
67d2e42ca0a5258a7f497c1a0bb1ab9c4b743de451c57b89645339b67536d7be peer-a-probe.log
|
|
10
|
+
8ed56e3da122ebc3cacf3550b9cc2e0b71df895968cddfd30e8047f485db24f7 peer-b-probe.log
|
|
11
|
+
1c7068c0f0aa12e2eb6e4e6f59945b7b934bb38f12764380d6a8837c211c5a28 tests-a-on-b-core.log
|
|
12
|
+
30f403cd24e448bec96767f0ff42efede99d74fc2f25a59ff8e4112f9a9b9f6f tests-a-on-b-producer.log
|
|
13
|
+
7fc384d85d9f48175b7920c292772a6607f73bd458f8b3a8a20767e56c2879d1 tests-b-on-a-core.log
|
|
14
|
+
dacd3b67443223b400ce283883c20831cce1063211a3c22868477b5a28bcaec7 tests-b-on-a-producer.log
|
|
15
|
+
71d59834355033a53db6311ed53fd735a948f53fc9ac914ae76fc6a9f022cf46 verify-all.log.gz
|