harness-everything 0.44.0 → 0.46.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/.agents/harness-everything/project-docs.json +3 -1
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/CHANGELOG.md +27 -0
- package/ci/fixtures/doc-obligations/read-boundary/boundary-policy.json +2 -2
- package/ci/fixtures/doc-obligations/reconciliation-s2a/harness.js +19 -1
- package/ci/mechanism-2ax-doc-lifecycle.test.js +2 -1
- package/ci/mechanism-325-3b-s2b-capture.test.js +6 -1
- package/ci/mechanism-325-f2-doc-craft.test.js +2 -2
- package/ci/mechanism-325-f3-derived-views.test.js +5 -2
- package/ci/mechanism-325-f4-projections.test.js +11 -1
- package/ci/mechanism-pr-a-conflict-check.test.js +507 -0
- package/ci/mechanism-prb-outline-review.test.js +1265 -0
- package/docs/adr/DOCS-001-project-docs-config.md +32 -0
- package/docs/adr/DOCS-002-adr-identity-supersession-index.md +34 -0
- package/docs/adr/DOCS-003-prd-revision-contract.md +29 -0
- package/docs/adr/DOCS-004-doc-change-step-type.md +28 -0
- package/docs/adr/DOCS-005-doc-obligations-evidence-producer.md +29 -0
- package/docs/adr/DOCS-006-reconciliation-pilot.md +50 -0
- package/docs/adr/DOCS-007-shared-doc-craft-derived-views.md +31 -0
- package/docs/adr/DOCS-008-obligation-abstraction-bounded-consumer.md +30 -0
- package/docs/doc-workflow-reconciliation.md +32 -1
- package/docs/governed-index.md +8 -0
- package/docs/platform-capabilities.md +6 -0
- package/harness-everything/SKILL.md +1 -1
- package/harness-everything/scripts/doc-check.js +84 -6
- package/harness-everything/scripts/doc-evidence.js +55 -0
- package/harness-everything/scripts/doc-lifecycle.js +51 -6
- package/hooks/hooks.json +1 -1
- package/hooks/scripts/lib/doc-reconciliation-events.js +138 -9
- package/hooks/scripts/lib/doc-reconciliation-pilot.js +131 -15
- package/hooks/scripts/lib/doc-reconciliation-provider.js +941 -13
- package/hooks/scripts/state-persist.js +10 -0
- package/opencode-plugin/plugin.json +1 -1
- package/package.json +1 -1
- package/plugins/harness-everything/.codex-plugin/plugin.json +1 -1
- package/plugins/harness-everything/hooks/scripts/lib/doc-reconciliation-events.js +138 -9
- package/plugins/harness-everything/hooks/scripts/lib/doc-reconciliation-pilot.js +131 -15
- package/plugins/harness-everything/hooks/scripts/lib/doc-reconciliation-provider.js +941 -13
- package/plugins/harness-everything/hooks/scripts/state-persist.js +10 -0
- package/plugins/harness-everything/plugin.json +1 -1
- package/plugins/harness-everything/skills/harness-everything/SKILL.md +1 -1
- package/plugins/harness-everything/skills/harness-everything/scripts/doc-check.js +84 -6
- package/plugins/harness-everything/skills/harness-everything/scripts/doc-evidence.js +55 -0
- package/plugins/harness-everything/skills/harness-everything/scripts/doc-lifecycle.js +51 -6
- package/sbom.cdx.json +7 -7
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: DOCS-001
|
|
3
|
+
status: accepted
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# DOCS-001 — Project docs config resolution (D5)
|
|
7
|
+
|
|
8
|
+
## Decision
|
|
9
|
+
|
|
10
|
+
- The committed, host-neutral `.agents/harness-everything/project-docs.json` wholly overrides differing host `projectDocs` values.
|
|
11
|
+
- `AGENTS.md` is never a configuration source.
|
|
12
|
+
- When the project file overrides a differing host value, the resolver reports `project-overrides-host`.
|
|
13
|
+
|
|
14
|
+
## Context
|
|
15
|
+
|
|
16
|
+
| Issue | Subject | State |
|
|
17
|
+
| --- | --- | --- |
|
|
18
|
+
| #325 | Documentation family, PRD/ADR governance and doc-change workflow | OPEN |
|
|
19
|
+
|
|
20
|
+
- Contract: [document-lifecycle.md § Configuration (D5)](../document-lifecycle.md#configuration-d5).
|
|
21
|
+
|
|
22
|
+
## Evidence
|
|
23
|
+
|
|
24
|
+
- Implementation: [project-docs-resolver.js](../../harness-everything/scripts/project-docs-resolver.js) (`project-overrides-host`, around line 390), [doc-check.js](../../harness-everything/scripts/doc-check.js).
|
|
25
|
+
- Test: [mechanism-2aw-project-docs-config.test.js](../../ci/mechanism-2aw-project-docs-config.test.js).
|
|
26
|
+
- Repo-wide gate, not row-specific: [reference-check.js](../../ci/reference-check.js).
|
|
27
|
+
- `AGENTS.md` exclusion is stated in [document-lifecycle.md](../document-lifecycle.md) (configuration table). The resolver has no `AGENTS.md` read path.
|
|
28
|
+
- Config in this repo: [project-docs.json](../../.agents/harness-everything/project-docs.json).
|
|
29
|
+
|
|
30
|
+
## Open gates
|
|
31
|
+
|
|
32
|
+
- #325 is OPEN as the documentation-family umbrella. Shipped slices do not close it.
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: DOCS-002
|
|
3
|
+
status: accepted
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# DOCS-002 — ADR/REQ identity, supersession and generated index (D6.1–D6.4)
|
|
7
|
+
|
|
8
|
+
## Decision
|
|
9
|
+
|
|
10
|
+
- ADR IDs are `<DOMAIN>-NNN` (domain 2–10 uppercase alphanumerics, at least 3 digits). The domain must be declared in `domains`.
|
|
11
|
+
- `status` is required and uses `proposed` / `accepted` / `rejected` / `superseded`.
|
|
12
|
+
- Aliases and `legacy:` references resolve to one document. Self-aliases and collisions are diagnosed.
|
|
13
|
+
- A `supersedes` edge takes effect only from an `accepted` ADR to a target already `superseded`. Cycles are diagnosed.
|
|
14
|
+
- `conflicts_with` stays a recorded conflict until an effective `supersedes` edge exists.
|
|
15
|
+
- The index in `docs/governed-index.md` is generated, not hand-written.
|
|
16
|
+
|
|
17
|
+
## Context
|
|
18
|
+
|
|
19
|
+
| Issue | Subject | State |
|
|
20
|
+
| --- | --- | --- |
|
|
21
|
+
| #325 | Documentation family, PRD/ADR governance and doc-change workflow | OPEN |
|
|
22
|
+
|
|
23
|
+
- Contract: [document-lifecycle.md § IDs](../document-lifecycle.md#ids-aliases-and-references-d62-d68), [§ Supersession](../document-lifecycle.md#supersession-d63-d64).
|
|
24
|
+
|
|
25
|
+
## Evidence
|
|
26
|
+
|
|
27
|
+
- Implementation: [doc-lifecycle.js](../../harness-everything/scripts/doc-lifecycle.js) (`ADR_ID` at line 20; `unknown-domain` at line 1078; `supersede-cycle` at line 1220).
|
|
28
|
+
- Test: [mechanism-2ax-doc-lifecycle.test.js](../../ci/mechanism-2ax-doc-lifecycle.test.js).
|
|
29
|
+
- Repo-wide gate, not row-specific: [reference-check.js](../../ci/reference-check.js).
|
|
30
|
+
- Generator: [governed-index.md](../governed-index.md), produced by `doc-lifecycle.js index` (see `derived` in [project-docs.json](../../.agents/harness-everything/project-docs.json)).
|
|
31
|
+
|
|
32
|
+
## Open gates
|
|
33
|
+
|
|
34
|
+
- #325 is OPEN as the documentation-family umbrella. Shipped slices do not close it.
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: DOCS-003
|
|
3
|
+
status: accepted
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# DOCS-003 — Contract-style PRD revision chain (D6.9)
|
|
7
|
+
|
|
8
|
+
## Decision
|
|
9
|
+
|
|
10
|
+
- An effective PRD is a frozen revision chain with an approval record.
|
|
11
|
+
- A change creates a new complete revision copied from the current effective revision. The effective snapshot is never edited in place.
|
|
12
|
+
- This replaces the D6.1 living-PRD model for PRDs only. ADRs, tickets and other roles are unchanged.
|
|
13
|
+
|
|
14
|
+
## Context
|
|
15
|
+
|
|
16
|
+
| Issue | Subject | State |
|
|
17
|
+
| --- | --- | --- |
|
|
18
|
+
| #325 | Documentation family, PRD/ADR governance and doc-change workflow | OPEN |
|
|
19
|
+
|
|
20
|
+
- Contract: [document-lifecycle.md § Contract-style PRD revisions (D6.9)](../document-lifecycle.md#contract-style-prd-revisions-d69).
|
|
21
|
+
|
|
22
|
+
## Evidence
|
|
23
|
+
|
|
24
|
+
- Implementation: [doc-lifecycle.js](../../harness-everything/scripts/doc-lifecycle.js) (revision identity, approval, immutability and `immutable-revision-modified` diagnostics).
|
|
25
|
+
- Test: [mechanism-2ay-prd-revisions.test.js](../../ci/mechanism-2ay-prd-revisions.test.js). Its header cites the D6.9 contract section.
|
|
26
|
+
|
|
27
|
+
## Open gates
|
|
28
|
+
|
|
29
|
+
- #325 is OPEN as the documentation-family umbrella. Shipped slices do not close it.
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: DOCS-004
|
|
3
|
+
status: accepted
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# DOCS-004 — `doc-change` step type
|
|
7
|
+
|
|
8
|
+
## Decision
|
|
9
|
+
|
|
10
|
+
- `doc-change` is an explicit documentation-edit step type.
|
|
11
|
+
- It has no implicit TDD binding. Step types are declared in a registry, not inferred.
|
|
12
|
+
|
|
13
|
+
## Context
|
|
14
|
+
|
|
15
|
+
| Issue | Subject | State |
|
|
16
|
+
| --- | --- | --- |
|
|
17
|
+
| #326 | Extract step-type registry and add doc-change step (split from #320 for #325) | CLOSED |
|
|
18
|
+
| #328 | Declarative step-type registry with declared-only doc-change (#326) | MERGED |
|
|
19
|
+
|
|
20
|
+
## Evidence
|
|
21
|
+
|
|
22
|
+
- Implementation: [step-types.json](../../hooks/scripts/lib/step-types.json) (`"id": "doc-change"`), [workflow-obligations.js](../../hooks/scripts/lib/workflow-obligations.js).
|
|
23
|
+
- Test: [mechanism-2az-step-type-registry.test.js](../../ci/mechanism-2az-step-type-registry.test.js).
|
|
24
|
+
|
|
25
|
+
## Open gates
|
|
26
|
+
|
|
27
|
+
- None for #326 and #328.
|
|
28
|
+
- #325 is OPEN as the documentation-family umbrella (see DOCS-001).
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: DOCS-005
|
|
3
|
+
status: accepted
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# DOCS-005 — Doc obligations and trusted evidence producer (phase 3a)
|
|
7
|
+
|
|
8
|
+
## Decision
|
|
9
|
+
|
|
10
|
+
- Documentation MUST obligations fail closed: missing or untrusted evidence yields a failing result, never a pass.
|
|
11
|
+
- Evidence is produced by a trusted producer. Baseline-bound evidence is refused when the baseline has no whole-baseline digest.
|
|
12
|
+
|
|
13
|
+
## Context
|
|
14
|
+
|
|
15
|
+
| Issue | Subject | State |
|
|
16
|
+
| --- | --- | --- |
|
|
17
|
+
| #334 | Phase 3a doc-change obligation derivation and evidence producer (#325) | MERGED |
|
|
18
|
+
| #335 | Bind deleted and renamed paths to baseline evidence | CLOSED |
|
|
19
|
+
| #342 | Read baseline and destination identities exactly once (fix for #335) | MERGED |
|
|
20
|
+
|
|
21
|
+
## Evidence
|
|
22
|
+
|
|
23
|
+
- Implementation: [doc-obligations.js](../../harness-everything/scripts/doc-obligations.js), [doc-evidence.js](../../harness-everything/scripts/doc-evidence.js) (digest refusal near line 165), [doc-evidence-capture.js](../../harness-everything/scripts/doc-evidence-capture.js).
|
|
24
|
+
- Tests: [mechanism-325-3a-doc-obligations.test.js](../../ci/mechanism-325-3a-doc-obligations.test.js), [mechanism-325-3a-doc-evidence.test.js](../../ci/mechanism-325-3a-doc-evidence.test.js), [mechanism-335-baseline-evidence.test.js](../../ci/mechanism-335-baseline-evidence.test.js).
|
|
25
|
+
|
|
26
|
+
## Open gates
|
|
27
|
+
|
|
28
|
+
- None specific to #334, #335 or #342.
|
|
29
|
+
- #325 is OPEN as the documentation-family umbrella (see DOCS-001).
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: DOCS-006
|
|
3
|
+
status: accepted
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# DOCS-006 — Doc reconciliation pilot (S1 default-OFF, F4 limited credit)
|
|
7
|
+
|
|
8
|
+
## Decision
|
|
9
|
+
|
|
10
|
+
- The S1 runtime is default OFF. It runs only when `HARNESS_DOC_RECONCILIATION_PILOT=1`.
|
|
11
|
+
- Fail-closed: evidence that is not trusted is withheld, not credited.
|
|
12
|
+
- F4 gives positive credit for `doc-lint` and `link-check`, from a host-captured run of the installed doc-check. The contract limits positive credit to Claude Code ([F4 contract](../doc-workflow-reconciliation.md#f4-credited-reconciliation-claude-only-pilot-on), line 1158).
|
|
13
|
+
- F4 also gives credit for `quality-review` from an independent host reviewer (`SubagentStop`).
|
|
14
|
+
- `conflict-check` (PR #385): `doc-check.js check --conflict-assessment` requires an exact `--base` SHA and runs lifecycle lint with `--all`. It credits only a complete corpus with no lint error diagnostics, and checks recorded and structural conflicts only. Prose contradictions stay with `quality-review`. Plain `check` earns no conflict credit.
|
|
15
|
+
- The observed live runs (Claude Code 2.1.309, runs 4–5) credited only `doc-lint` and `link-check`. `quality-review` and `conflict-check` have mechanism tests only.
|
|
16
|
+
- Other capabilities stay withheld. Expansion beyond the limited pilot is gated on #341 E1.
|
|
17
|
+
|
|
18
|
+
```mermaid
|
|
19
|
+
flowchart LR
|
|
20
|
+
E["HARNESS_DOC_RECONCILIATION_PILOT=1"] --> P["doc-reconciliation-pilot.js"]
|
|
21
|
+
P -->|"host-captured doc-check"| L["doc-lint + link-check: credited"]
|
|
22
|
+
P -->|"independent SubagentStop reviewer"| Q["quality-review: credited (mechanism tests)"]
|
|
23
|
+
P -->|"check --conflict-assessment, complete corpus"| C["conflict-check: credited (mechanism tests)"]
|
|
24
|
+
P -. "all other capabilities" .-> W["withheld"]
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Context
|
|
28
|
+
|
|
29
|
+
| Issue | Subject | State |
|
|
30
|
+
| --- | --- | --- |
|
|
31
|
+
| #341 | Phased snapshot boundary, conformance suite, obligation contract | OPEN |
|
|
32
|
+
| #350 | Docuvia pilot limitations (code-to-doc dependency edge, derivation hygiene) | OPEN |
|
|
33
|
+
| #358 | Credit host-captured doc evidence on Claude Code (#325) | MERGED |
|
|
34
|
+
| #375 | Retain adjudicated E1 blockers and Phase 3c handoff | MERGED |
|
|
35
|
+
| #385 | Conflict-check producer from a full-corpus assessment | MERGED |
|
|
36
|
+
|
|
37
|
+
- #375 records zero qualifying E1 cycles, so expansion stays gated.
|
|
38
|
+
- Contract: [doc-workflow-reconciliation.md § S1 switch](../doc-workflow-reconciliation.md#s1--switch-trusted-scope-and-fail-closed-no-credit-gate).
|
|
39
|
+
|
|
40
|
+
## Evidence
|
|
41
|
+
|
|
42
|
+
- Implementation: [doc-reconciliation-pilot.js](../../hooks/scripts/lib/doc-reconciliation-pilot.js) (env gate at line 8; `doc-lint` / `link-check` credit at line 246; `quality-review` collection from `SubagentStop` at lines 244 and 254; `conflict-check` collection at lines 251–252), [doc-check.js](../../harness-everything/scripts/doc-check.js) (flag parse at lines 86–88; `--base` required at line 102; `--all` lint at line 108; assessment at lines 468–498; credit at line 501), [doc-reconciliation-provider.js](../../hooks/scripts/lib/doc-reconciliation-provider.js) (corpus binding check at lines 763–785), [doc-reconciliation-core.js](../../hooks/scripts/lib/doc-reconciliation-core.js), [doc-reconciliation-producer.js](../../hooks/scripts/lib/doc-reconciliation-producer.js), [doc-reconciliation-events.js](../../hooks/scripts/lib/doc-reconciliation-events.js).
|
|
43
|
+
- F4 contract: [doc-workflow-reconciliation.md § F4](../doc-workflow-reconciliation.md#f4-credited-reconciliation-claude-only-pilot-on). `conflict-check` row: [doc-workflow-reconciliation.md](../doc-workflow-reconciliation.md) line 1174.
|
|
44
|
+
- Tests: [mechanism-pr-a-conflict-check.test.js](../../ci/mechanism-pr-a-conflict-check.test.js) (17/17 PASS at this SHA; mechanism only, no live-host run), [mechanism-325-3b-preparation.test.js](../../ci/mechanism-325-3b-preparation.test.js), [mechanism-325-3b-s1-review.test.js](../../ci/mechanism-325-3b-s1-review.test.js), [mechanism-325-3b-s2a-core.test.js](../../ci/mechanism-325-3b-s2a-core.test.js), [mechanism-325-3b-s2b-wiring.test.js](../../ci/mechanism-325-3b-s2b-wiring.test.js), [mechanism-341-opencode-lane.test.js](../../ci/mechanism-341-opencode-lane.test.js).
|
|
45
|
+
|
|
46
|
+
## Open gates
|
|
47
|
+
|
|
48
|
+
- #341 OPEN: E1 GO / REVISE / NO-GO gates any expansion beyond the limited pilot.
|
|
49
|
+
- #350 OPEN: Docuvia pilot limitations.
|
|
50
|
+
- #325 OPEN as the documentation-family umbrella (see DOCS-001).
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: DOCS-007
|
|
3
|
+
status: accepted
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# DOCS-007 — Shared doc craft and derived-view regeneration (F2/F3)
|
|
7
|
+
|
|
8
|
+
## Decision
|
|
9
|
+
|
|
10
|
+
- Each declared derived view in `derived` MUST be regenerated and must match its source when its inputs change (`derived-regenerate`).
|
|
11
|
+
- Doc-skill craft guidance is shared from one place under `references/documentation/_shared/`, not copied into each skill.
|
|
12
|
+
|
|
13
|
+
## Context
|
|
14
|
+
|
|
15
|
+
| Issue | Subject | State |
|
|
16
|
+
| --- | --- | --- |
|
|
17
|
+
| #356 | Share documentation craft across doc skills (#325) | MERGED |
|
|
18
|
+
| #357 | Check derived views with regenerate-equals (#325) | MERGED |
|
|
19
|
+
|
|
20
|
+
- #325 is OPEN as the documentation-family umbrella (see DOCS-001). It is the commit-title parent of both merged PRs.
|
|
21
|
+
|
|
22
|
+
## Evidence
|
|
23
|
+
|
|
24
|
+
- Implementation: [doc-obligations.js](../../harness-everything/scripts/doc-obligations.js) (`derived-regenerate` capability at line 10), [writing-craft.md](../../harness-everything/references/documentation/_shared/writing-craft.md), [maintenance.md](../../harness-everything/references/documentation/_shared/maintenance.md).
|
|
25
|
+
- Tests: [mechanism-325-f3-derived-views.test.js](../../ci/mechanism-325-f3-derived-views.test.js), [mechanism-325-f2-doc-craft.test.js](../../ci/mechanism-325-f2-doc-craft.test.js).
|
|
26
|
+
- Repo-wide gate, not row-specific: [reference-check.js](../../ci/reference-check.js).
|
|
27
|
+
|
|
28
|
+
## Open gates
|
|
29
|
+
|
|
30
|
+
- None specific to #356 or #357.
|
|
31
|
+
- #325 is OPEN as the documentation-family umbrella (see DOCS-001).
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: DOCS-008
|
|
3
|
+
status: accepted
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# DOCS-008 — #320 obligation abstraction as a bounded doc consumer
|
|
7
|
+
|
|
8
|
+
## Decision
|
|
9
|
+
|
|
10
|
+
- The doc workflow reconciliation consumes the workflow obligation abstraction in a bounded way: the documentation path only, under the DOCS-006 gate.
|
|
11
|
+
- Broad promotion of the abstraction to generic workflow use is not approved. It is gated on the Phase 3c H1–H4 paired-task evidence and owner sign-off.
|
|
12
|
+
|
|
13
|
+
## Context
|
|
14
|
+
|
|
15
|
+
| Issue | Subject | State |
|
|
16
|
+
| --- | --- | --- |
|
|
17
|
+
| #320 | Compile workflow/skill metadata into a dynamic decision graph | OPEN |
|
|
18
|
+
|
|
19
|
+
- Phase 3c is planned, not shipped: [document-lifecycle.md § Phase 3c](../document-lifecycle.md#phase-3c--architecture-promotion-gate-for-320-planned-not-shipped).
|
|
20
|
+
|
|
21
|
+
## Evidence
|
|
22
|
+
|
|
23
|
+
- Implementation: [doc-reconciliation-core.js](../../hooks/scripts/lib/doc-reconciliation-core.js), [doc-obligations.js](../../harness-everything/scripts/doc-obligations.js) (the obligation table consumed by the doc path).
|
|
24
|
+
- Consumer tests (exercise `doc-reconciliation-core.js` consuming `deriveDocObligations`): [mechanism-325-3b-s2a-core.test.js](../../ci/mechanism-325-3b-s2a-core.test.js), [mechanism-325-3b-s2b-wiring.test.js](../../ci/mechanism-325-3b-s2b-wiring.test.js).
|
|
25
|
+
- Fail-closed controls (not evidence of the consumer): [mechanism-325-3b-preparation.test.js](../../ci/mechanism-325-3b-preparation.test.js), [mechanism-325-3b-s1-review.test.js](../../ci/mechanism-325-3b-s1-review.test.js), [mechanism-341-opencode-lane.test.js](../../ci/mechanism-341-opencode-lane.test.js).
|
|
26
|
+
|
|
27
|
+
## Open gates
|
|
28
|
+
|
|
29
|
+
- #320 OPEN. Its own promotion gate is unmet: H1–H4 paired-task evidence has not been collected.
|
|
30
|
+
- Pilot gate (#341 E1) applies, see DOCS-006.
|
|
@@ -1171,7 +1171,8 @@ Candidate classification follows command roles, not arbitrary text matches. A di
|
|
|
1171
1171
|
| `link-check` | A separate projection of the same invocation, scoped to the expected paths: no broken, skipped, unreadable or unsupported link syntax |
|
|
1172
1172
|
| `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 |
|
|
1173
1173
|
| `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 |
|
|
1174
|
-
| `conflict-check
|
|
1174
|
+
| `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 |
|
|
1175
|
+
| `requirement-sync` | Stay unresolved. F4 gives it no credit |
|
|
1175
1176
|
| SHOULD reminders | Never earn credit, and their absence never withholds it |
|
|
1176
1177
|
|
|
1177
1178
|
### Live capture gate
|
|
@@ -1212,3 +1213,33 @@ claude -p "<fixed capture prompt, containing the exact check command>" \
|
|
|
1212
1213
|
- **The live gate is a repository-pinned field-availability check, not a running-host compatibility check (F10).** The qualification export records Claude Code `2.1.308`, but the retained Claude hook payloads expose no host-version field for the runtime to compare. The export and sibling sanitized capture validate the recorded payload kinds and correlations for that host version; they do not establish compatibility with the current session. Editing both repository artifacts and updating the capture digest can reopen the gate without a new live capture.
|
|
1213
1214
|
- **The unsupported-host check is vacuous in production (F11).** The only production caller asserts the Claude host id. OpenCode and Codex stay unavailable because their plugins do not mount `state-persist.js`, not because the host is detected.
|
|
1214
1215
|
- **Event files are not tamper-proof (F13).** Event files are created exclusively, and the journal is checked against them. The local agent can still rewrite event files and rebuild the journal. This is the owner's bounded claim, not a guarantee of tamper resistance.
|
|
1216
|
+
|
|
1217
|
+
## PR-B: dedicated pre-draft outline confirmation
|
|
1218
|
+
|
|
1219
|
+
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.
|
|
1220
|
+
|
|
1221
|
+
```mermaid
|
|
1222
|
+
flowchart LR
|
|
1223
|
+
B["Original baseline B<br/>task and config pinned"] --> O["Proposed outline O<br/>new target absent at B"]
|
|
1224
|
+
I["Coordinator-pinned inventory"] --> R["Independent review<br/>exact O + cited row"]
|
|
1225
|
+
O --> R
|
|
1226
|
+
R --> H["Immutable handoff +<br/>exclusive writer lease"]
|
|
1227
|
+
H --> P["Task/Agent PreToolUse<br/>exact prompt marker"]
|
|
1228
|
+
P --> S["Synchronous seam<br/>clean worker root at O<br/>one-use token consumed"]
|
|
1229
|
+
S --> W["Correlated worker start/result"]
|
|
1230
|
+
W --> D["Final candidate D<br/>strict descendant of O"]
|
|
1231
|
+
D --> A["Outline result and<br/>aggregate B→D MUSTs"]
|
|
1232
|
+
```
|
|
1233
|
+
|
|
1234
|
+
| Evidence | Required binding |
|
|
1235
|
+
|---|---|
|
|
1236
|
+
| 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. |
|
|
1237
|
+
| 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. |
|
|
1238
|
+
| 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. |
|
|
1239
|
+
| 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. 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. |
|
|
1240
|
+
| Worker and D | Worker start must correlate to the dispatch token/tool ID, principal and declared root; the eventual Task/Agent result must name that same worker. 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. |
|
|
1241
|
+
| 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. |
|
|
1242
|
+
|
|
1243
|
+
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. Retries use a fresh attempt, token, clean worktree and immutable handoff; only one non-superseded leaf is current. Exact same-tool-ID/input duplicates may resolve to one seam; changed input or token reuse is unresolved. OpenCode and Codex remain unsupported for positive outline credit.
|
|
1244
|
+
|
|
1245
|
+
The current retained Claude qualification does not establish the frozen prompt field, coordinator principal, synchronous PreToolUse boundary or worker-root mapping. The outline provider therefore grants no positive live-host credit until a retained Claude capture validates those mappings. 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.
|
package/docs/governed-index.md
CHANGED
|
@@ -4,6 +4,14 @@
|
|
|
4
4
|
|
|
5
5
|
- `(no id)` — `docs/architecture.md` (role: reference; status: active)
|
|
6
6
|
- `(no id)` — `docs/doc-workflow-reconciliation.md` (role: reference; status: active)
|
|
7
|
+
- `DOCS-001` — `docs/adr/DOCS-001-project-docs-config.md` (role: adr; status: accepted)
|
|
8
|
+
- `DOCS-002` — `docs/adr/DOCS-002-adr-identity-supersession-index.md` (role: adr; status: accepted)
|
|
9
|
+
- `DOCS-003` — `docs/adr/DOCS-003-prd-revision-contract.md` (role: adr; status: accepted)
|
|
10
|
+
- `DOCS-004` — `docs/adr/DOCS-004-doc-change-step-type.md` (role: adr; status: accepted)
|
|
11
|
+
- `DOCS-005` — `docs/adr/DOCS-005-doc-obligations-evidence-producer.md` (role: adr; status: accepted)
|
|
12
|
+
- `DOCS-006` — `docs/adr/DOCS-006-reconciliation-pilot.md` (role: adr; status: accepted)
|
|
13
|
+
- `DOCS-007` — `docs/adr/DOCS-007-shared-doc-craft-derived-views.md` (role: adr; status: accepted)
|
|
14
|
+
- `DOCS-008` — `docs/adr/DOCS-008-obligation-abstraction-bounded-consumer.md` (role: adr; status: accepted)
|
|
7
15
|
|
|
8
16
|
## PRD revision chains
|
|
9
17
|
|
|
@@ -39,6 +39,10 @@ permissions stay authoritative; an explicit required `actionGate` hint still gat
|
|
|
39
39
|
This is a deterministic mechanism change, not live-host proof, and does not
|
|
40
40
|
upgrade any platform row's live-host status.
|
|
41
41
|
|
|
42
|
+
### Full-corpus conflict-check (mechanism only)
|
|
43
|
+
|
|
44
|
+
The registered `doc-check check --conflict-assessment` mode runs document lifecycle lint across the full governed and legacy corpus and requires `--base` as an exact commit SHA. It emits `conflict-check` only when collection is complete and there are no lifecycle errors. Its event preserves the exact obligation scope separately from full-corpus paths and digests, config bytes and base commit, then checks those bindings against the workflow snapshot. The acceptance is limited to recorded and structural conflicts; prose contradictions remain with independent `quality-review`. Plain `check` never earns this credit, and OpenCode still refuses the capability. Deterministic mechanism coverage does not establish live-host behavior or change the platform matrix's live-verification status.
|
|
45
|
+
|
|
42
46
|
## Current capability summary
|
|
43
47
|
|
|
44
48
|
| Platform surface | Standalone skills | Plugin support | Hooks/lifecycle | Project scope | Global scope | Current evidence boundary |
|
|
@@ -206,6 +210,8 @@ The editorial doc check (`harness-everything/scripts/doc-check.js`, F1) is reach
|
|
|
206
210
|
|
|
207
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.
|
|
208
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.
|
|
214
|
+
|
|
209
215
|
### Own-repository governance guard (PR #376)
|
|
210
216
|
|
|
211
217
|
On `pull_request`, a standalone `doc-governance` job extracts `scripts/doc-governance-guard.js` from the event base commit and runs those bytes against the exact event base/head SHAs. Its nonzero result fails that job; `self-regression` and the other CI jobs run independently. The ordinary test suite may still run on GitHub's PR checkout SHA, which can be a synthetic merge commit; it is reported separately. The guard never falls back to the candidate copy. PR #376's reviewed base lacked the verifier; that historical bootstrap reported BOOTSTRAP_UNCHECKED without executing candidate bytes. Bases containing the guard (including main at 0ef0541c) execute the base-owned verifier. If a selected PR base lacks the guard, the wrapper retains the warning/step-summary-only bootstrap path with exit 0; this is unverified governance, not PASS. Every nonzero exit from an extracted verifier, including FAIL and BOOTSTRAP_UNCHECKED, fails the governance job. Other CI jobs run independently.
|
|
@@ -6,14 +6,16 @@
|
|
|
6
6
|
// Prints one JSON result. Exit 0 = both pass, 1 = defect, 2 = usage/config error.
|
|
7
7
|
const fs = require('fs');
|
|
8
8
|
const path = require('path');
|
|
9
|
+
const crypto = require('crypto');
|
|
9
10
|
const { execFileSync, spawnSync } = require('child_process');
|
|
10
11
|
const { collectDocs } = require('./doc-lifecycle');
|
|
11
12
|
|
|
12
13
|
// Logical command IDs (A3 section 3): stdout that repeats an ID is not trusted as credit by itself.
|
|
13
14
|
const CHECK_ID = 'harness.doc-check.check.v1';
|
|
14
15
|
const COMPARE_ID = 'harness.doc-check.compare-derived.v1';
|
|
15
|
-
const USAGE = 'usage: doc-check.js check --workspace <dir> --config <json-file, relative to --workspace> --changed-path <path> [--changed-path <path> ...] [--base <git-rev>]';
|
|
16
|
+
const USAGE = 'usage: doc-check.js check --workspace <dir> --config <json-file, relative to --workspace> --changed-path <path> [--changed-path <path> ...] [--base <git-rev>] [--conflict-assessment]';
|
|
16
17
|
const VALUE_FLAGS = ['--workspace', '--config', '--changed-path', '--base'];
|
|
18
|
+
const CONFLICT_SCHEMA = 'harness.doc-check.conflict-assessment.v1';
|
|
17
19
|
const EXTERNAL = /^(?:https?:|mailto:)/i;
|
|
18
20
|
const SCHEME = /^[a-z][a-z0-9+.-]*:/i;
|
|
19
21
|
|
|
@@ -79,10 +81,15 @@ function parseArgs(argv) {
|
|
|
79
81
|
const [command, ...rest] = argv;
|
|
80
82
|
if (command !== 'check') throw new Error(USAGE);
|
|
81
83
|
const options = { changedPaths: [] };
|
|
82
|
-
for (let index = 0; index < rest.length; index
|
|
84
|
+
for (let index = 0; index < rest.length; index++) {
|
|
83
85
|
const flag = rest[index];
|
|
84
|
-
|
|
85
|
-
|
|
86
|
+
if (flag === '--conflict-assessment') {
|
|
87
|
+
if (options.conflictAssessment) throw new Error(`${USAGE}\nrepeated argument: ${flag}`);
|
|
88
|
+
options.conflictAssessment = true;
|
|
89
|
+
continue;
|
|
90
|
+
}
|
|
91
|
+
if (!VALUE_FLAGS.includes(flag) || rest[index + 1] === undefined) throw new Error(`${USAGE}\nunknown or incomplete argument: ${flag}`);
|
|
92
|
+
const value = rest[++index];
|
|
86
93
|
if (value.startsWith('--')) throw new Error(`${USAGE}\n${flag} requires a value, not a flag: ${value}`);
|
|
87
94
|
if (flag === '--changed-path') options.changedPaths.push(value);
|
|
88
95
|
else {
|
|
@@ -92,16 +99,47 @@ function parseArgs(argv) {
|
|
|
92
99
|
}
|
|
93
100
|
if (!options.workspace || !options.config) throw new Error(`${USAGE}\n--workspace and --config are required`);
|
|
94
101
|
if (!options.changedPaths.length) throw new Error(`${USAGE}\n--changed-path is required`);
|
|
102
|
+
if (options.conflictAssessment && options.base === undefined) throw new Error(`${USAGE}\n--conflict-assessment requires --base`);
|
|
95
103
|
return options;
|
|
96
104
|
}
|
|
97
105
|
|
|
98
106
|
function runLint(workspace, options) {
|
|
99
107
|
const args = [path.join(__dirname, 'doc-lifecycle.js'), 'lint', '--workspace', workspace, '--config', options.config];
|
|
100
|
-
|
|
108
|
+
if (options.conflictAssessment) args.push('--all');
|
|
109
|
+
else for (const changed of options.changedPaths) args.push('--changed-path', changed);
|
|
101
110
|
if (options.base !== undefined) args.push('--base', options.base);
|
|
102
111
|
return spawnSync(process.execPath, args, { encoding: 'utf8' });
|
|
103
112
|
}
|
|
104
113
|
|
|
114
|
+
function workspaceRelative(workspace, value) {
|
|
115
|
+
const absolute = path.resolve(workspace, value);
|
|
116
|
+
const relative = path.relative(workspace, absolute);
|
|
117
|
+
if (!relative || relative === '..' || relative.startsWith(`..${path.sep}`) || path.isAbsolute(relative)) return null;
|
|
118
|
+
return relative.split(path.sep).join('/');
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function resolvedBaseCommit(workspace, base) {
|
|
122
|
+
if (typeof base !== 'string' || !base || base.startsWith('-')) return null;
|
|
123
|
+
try {
|
|
124
|
+
const value = execFileSync('git', ['-C', workspace, 'rev-parse', '--verify', `${base}^{commit}`], {
|
|
125
|
+
encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore']
|
|
126
|
+
}).trim();
|
|
127
|
+
return /^[0-9a-f]{40,64}$/.test(value) ? value : null;
|
|
128
|
+
} catch { return null; }
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function baselineArtifacts(workspace, base, paths) {
|
|
132
|
+
const artifacts = {};
|
|
133
|
+
for (const relative of paths) {
|
|
134
|
+
if (fs.existsSync(path.join(workspace, relative))) continue;
|
|
135
|
+
try {
|
|
136
|
+
const bytes = execFileSync('git', ['-C', workspace, 'show', `${base}:${relative}`], { encoding: 'buffer', stdio: ['ignore', 'pipe', 'ignore'] });
|
|
137
|
+
artifacts[relative] = crypto.createHash('sha256').update(bytes).digest('hex');
|
|
138
|
+
} catch { /* Paths absent from both snapshots need no baseline artifact. */ }
|
|
139
|
+
}
|
|
140
|
+
return artifacts;
|
|
141
|
+
}
|
|
142
|
+
|
|
105
143
|
// Scan nested label spans depth-first, then resume after each destination. Matching brackets are precomputed per line,
|
|
106
144
|
// and an explicit stack keeps nesting from consuming the JS call stack. A nested link inside a label remains an
|
|
107
145
|
// intentional over-check: CommonMark keeps only the inner link, so both targets are checked and neither is silent.
|
|
@@ -340,7 +378,8 @@ function check(options) {
|
|
|
340
378
|
try { analysis = JSON.parse(lint.stdout); } catch {
|
|
341
379
|
return emit({ schema: CHECK_ID, command: CHECK_ID, lint: { exit: 2, error: 'lint output was not JSON', diagnostics: [] }, links: { checked: 0, broken: [], skipped: 'lint-usage-error' }, coverage: { unsupported: [] } }, 2);
|
|
342
380
|
}
|
|
343
|
-
const
|
|
381
|
+
const configBytes = fs.readFileSync(configPath);
|
|
382
|
+
const config = JSON.parse(configBytes.toString('utf8'));
|
|
344
383
|
const docs = collectDocs(workspace, config);
|
|
345
384
|
const governed = new Set(docs.filter(doc => doc.role).map(doc => doc.path));
|
|
346
385
|
|
|
@@ -408,6 +447,7 @@ function check(options) {
|
|
|
408
447
|
const result = {
|
|
409
448
|
schema: CHECK_ID,
|
|
410
449
|
command: CHECK_ID,
|
|
450
|
+
...(options.conflictAssessment ? { mode: 'conflict-assessment' } : {}),
|
|
411
451
|
lint: { exit: lintExit, evidence: analysis.evidence, diagnostics: analysis.diagnostics },
|
|
412
452
|
links: { checked, broken },
|
|
413
453
|
coverage: { unsupported },
|
|
@@ -425,6 +465,44 @@ function check(options) {
|
|
|
425
465
|
},
|
|
426
466
|
}
|
|
427
467
|
};
|
|
468
|
+
if (options.conflictAssessment) {
|
|
469
|
+
const scopePaths = [...new Set(options.changedPaths.map(value => workspaceRelative(workspace, value)).filter(Boolean))].sort();
|
|
470
|
+
const corpus = analysis.corpus;
|
|
471
|
+
const baseCommit = resolvedBaseCommit(workspace, options.base);
|
|
472
|
+
const configRelative = workspaceRelative(workspace, options.config);
|
|
473
|
+
let reason = null;
|
|
474
|
+
if (!scopePaths.length || scopePaths.length !== new Set(options.changedPaths.map(value => workspaceRelative(workspace, value))).size) reason = 'scope-unavailable';
|
|
475
|
+
else if (!Array.isArray(config.domains) || config.domains.length === 0) reason = 'domains-unknown';
|
|
476
|
+
else if (!corpus || corpus.complete !== true || !Array.isArray(corpus.paths) || !corpus.paths.length) reason = 'corpus-incomplete';
|
|
477
|
+
else if (!corpus.digests || Object.keys(corpus.digests).length !== corpus.paths.length
|
|
478
|
+
|| corpus.paths.some(relative => !/^[0-9a-f]{64}$/.test(corpus.digests[relative] || ''))) reason = 'corpus-digests-incomplete';
|
|
479
|
+
else if (!baseCommit) reason = 'base-unavailable';
|
|
480
|
+
else if (options.base !== baseCommit) reason = 'base-not-pinned';
|
|
481
|
+
else if (!configRelative) reason = 'config-outside-workspace';
|
|
482
|
+
else if (!corpus.config || corpus.config.path !== configRelative) reason = 'config-unavailable';
|
|
483
|
+
else if (corpus.config.digest !== crypto.createHash('sha256').update(configBytes).digest('hex')) reason = 'config-changed-during-check';
|
|
484
|
+
const hasErrors = lintExit !== 0 || !Array.isArray(analysis.diagnostics)
|
|
485
|
+
|| analysis.diagnostics.some(item => item && item.severity === 'error');
|
|
486
|
+
const corpusIdentity = corpus && {
|
|
487
|
+
complete: corpus.complete === true,
|
|
488
|
+
paths: corpus.paths,
|
|
489
|
+
digests: corpus.digests,
|
|
490
|
+
config: corpus.config,
|
|
491
|
+
baseCommit,
|
|
492
|
+
};
|
|
493
|
+
const state = reason ? 'unavailable' : hasErrors ? 'fail' : 'pass';
|
|
494
|
+
result.conflictAssessment = {
|
|
495
|
+
schema: CONFLICT_SCHEMA,
|
|
496
|
+
state,
|
|
497
|
+
...(reason ? { reason } : {}),
|
|
498
|
+
...(corpusIdentity ? { corpus: corpusIdentity } : {}),
|
|
499
|
+
...(state === 'pass' ? { baselineArtifacts: baselineArtifacts(workspace, baseCommit, scopePaths) } : {}),
|
|
500
|
+
};
|
|
501
|
+
if (state === 'pass') result.capabilities['conflict-check'] = {
|
|
502
|
+
state: 'pass', checkedPaths: scopePaths, corpus: corpusIdentity,
|
|
503
|
+
};
|
|
504
|
+
return emit(result, state === 'unavailable' ? 2 : lintExit === 1 || broken.length ? 1 : 0);
|
|
505
|
+
}
|
|
428
506
|
return emit(result, lintExit === 1 || broken.length ? 1 : 0);
|
|
429
507
|
}
|
|
430
508
|
|
|
@@ -433,6 +433,61 @@ function evaluateObligation(obligation, context) {
|
|
|
433
433
|
return entry ? baselineBound(item, entry) : candidateBound(item, path);
|
|
434
434
|
};
|
|
435
435
|
|
|
436
|
+
if (capability === 'outline-confirmation') {
|
|
437
|
+
const candidates = matching.filter(item => reviewList.includes(item) && item.kind === 'outline-confirmation');
|
|
438
|
+
if (candidates.length !== 1) {
|
|
439
|
+
return result('unresolved', 'none',
|
|
440
|
+
'outline-confirmation requires one validated pre-draft O/review/handoff/dispatch record and a final D; generic execution and waivers do not qualify',
|
|
441
|
+
candidates.map(item => item.id));
|
|
442
|
+
}
|
|
443
|
+
const item = candidates[0];
|
|
444
|
+
const evidence = isPlainObject(item.outlineEvidence) ? item.outlineEvidence : null;
|
|
445
|
+
const sha = value => isSha256(value);
|
|
446
|
+
const digestsMatch = map => isPlainObject(map)
|
|
447
|
+
&& Object.keys(map).sort(compareText).join('\0') === scopePaths.slice().sort(compareText).join('\0')
|
|
448
|
+
&& scopePaths.every(path => sha(map[path]));
|
|
449
|
+
const identityValid = Boolean(evidence)
|
|
450
|
+
&& evidence.schema === 'harness.outline-confirmation.v1' && evidence.state === 'validated'
|
|
451
|
+
&& nonEmptyString(evidence.taskId) && nonEmptyString(evidence.attemptId)
|
|
452
|
+
&& /^\d{1,2}$/.test(String(evidence.inventoryRowId || ''))
|
|
453
|
+
&& /^[A-Z][A-Z0-9]{1,9}$/.test(String(evidence.domain || ''))
|
|
454
|
+
&& sha(evidence.inventoryPinDigest) && sha(evidence.inventoryArtifactDigest)
|
|
455
|
+
&& /^[0-9a-f]{40,64}$/.test(String(evidence.baselineCommit || ''))
|
|
456
|
+
&& /^[0-9a-f]{40,64}$/.test(String(evidence.outlineCommit || ''))
|
|
457
|
+
&& /^[0-9a-f]{40,64}$/.test(String(evidence.draftCommit || ''))
|
|
458
|
+
&& /^[0-9a-f]{40,64}$/.test(String(evidence.mergedCommit || ''))
|
|
459
|
+
&& evidence.outlineCommit !== evidence.draftCommit
|
|
460
|
+
&& evidence.ancestry === 'strict-descendant'
|
|
461
|
+
&& sha(evidence.handoffDigest) && sha(evidence.reviewCaptureDigest) && sha(evidence.reviewDigest)
|
|
462
|
+
&& sha(evidence.seamDigest) && sha(evidence.dispatchInputDigest) && sha(evidence.writerLeaseDigest)
|
|
463
|
+
&& nonEmptyString(evidence.dispatchToolUseId) && nonEmptyString(evidence.coordinatorPrincipal)
|
|
464
|
+
&& nonEmptyString(evidence.reviewerPrincipal) && nonEmptyString(evidence.workerPrincipal)
|
|
465
|
+
&& evidence.reviewerPrincipal !== evidence.coordinatorPrincipal
|
|
466
|
+
&& evidence.reviewerPrincipal !== evidence.workerPrincipal
|
|
467
|
+
&& Array.isArray(evidence.targetPaths)
|
|
468
|
+
&& evidence.targetPaths.slice().sort(compareText).join('\0') === scopePaths.slice().sort(compareText).join('\0')
|
|
469
|
+
&& digestsMatch(evidence.outlineArtifactDigests) && digestsMatch(evidence.draftArtifactDigests)
|
|
470
|
+
&& digestsMatch(item.artifactDigests)
|
|
471
|
+
&& scopePaths.every(path => evidence.draftArtifactDigests[path] === item.artifactDigests[path]
|
|
472
|
+
&& candidateBound(item, path));
|
|
473
|
+
const mergeValid = evidence && (evidence.mergeCorrespondence === null
|
|
474
|
+
|| (isPlainObject(evidence.mergeCorrespondence)
|
|
475
|
+
&& ['accepted', 'changed'].includes(evidence.mergeCorrespondence.state)
|
|
476
|
+
&& Array.isArray(evidence.mergeCorrespondence.targetPaths)
|
|
477
|
+
&& evidence.mergeCorrespondence.targetPaths.slice().sort(compareText).join('\0')
|
|
478
|
+
=== scopePaths.slice().sort(compareText).join('\0')));
|
|
479
|
+
const outlineProblems = [];
|
|
480
|
+
if (!identityValid) outlineProblems.push('envelope-or-current-D-binding');
|
|
481
|
+
if (!mergeValid) outlineProblems.push('merge-correspondence');
|
|
482
|
+
if (item.baseDigest !== baselineDigest) outlineProblems.push('original-B-digest');
|
|
483
|
+
if (outlineProblems.length) {
|
|
484
|
+
diagnostics.push({ code: 'outline-evidence-unresolved', obligationId,
|
|
485
|
+
message: `the outline evidence is incomplete or inconsistent: ${outlineProblems.join(', ')}` });
|
|
486
|
+
return result('unresolved', 'none', 'outline evidence is incomplete or unbound to the original B and final D', [item.id]);
|
|
487
|
+
}
|
|
488
|
+
return result('observed', 'pass', 'validated independent review of exact O, coordinator handoff, clean one-use dispatch, pinned inventory, and strict O-to-D evidence', [item.id]);
|
|
489
|
+
}
|
|
490
|
+
|
|
436
491
|
for (const item of matching) {
|
|
437
492
|
if (executionList.includes(item) && !completeExecution(item)) {
|
|
438
493
|
diagnostics.push({
|