@pareto4/pp-core 1.13.0 → 1.17.1
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/plugin.json +3 -3
- package/README.md +26 -9
- package/agents/pp-code-reviewer.md +2 -1
- package/agents/pp-debug-session-manager.md +9 -6
- package/agents/pp-executor.md +10 -13
- package/agents/pp-plan-checker.md +4 -4
- package/agents/pp-planner.md +23 -9
- package/agents/pp-vault-reviewer.md +244 -21
- package/agents/pp-verifier.md +7 -3
- package/bin/install.js +10 -3
- package/commands/pp-execute-phase.md +1 -3
- package/commands/pp-ns-review.md +1 -1
- package/commands/pp-plan-phase.md +1 -2
- package/commands/pp-vault-review.md +5 -3
- package/commands/pp-verify-work.md +1 -2
- package/gemini-extension.json +1 -1
- package/hooks/dist/pp-check-update.js +0 -0
- package/hooks/dist/pp-cursor-session-start.js +1 -1
- package/hooks/dist/pp-statusline.js +1 -1
- package/hooks/dist/pp-update-banner.js +0 -0
- package/hooks/pp-check-update.js +0 -0
- package/hooks/pp-cursor-session-start.js +1 -1
- package/hooks/pp-statusline.js +1 -1
- package/hooks/pp-update-banner.js +0 -0
- package/package.json +14 -19
- package/pareto-skills/_shared/create-project-board.sh +0 -0
- package/pareto-skills/pp-project-setup/SKILL.md +1 -1
- package/pp-core/bin/check-latest-version.cjs +0 -0
- package/pp-core/bin/detect-build-commands.sh +2 -2
- package/pp-core/bin/lib/capability-activation.cjs +83 -9
- package/pp-core/bin/lib/capability-registry.cjs +396 -82
- package/pp-core/bin/lib/capability-state.cjs +160 -4
- package/pp-core/bin/lib/capability-writer.cjs +19 -5
- package/pp-core/bin/lib/check-command-router.cjs +3334 -946
- package/pp-core/bin/lib/clusters.cjs +3 -0
- package/pp-core/bin/lib/command-aliases.cjs +0 -35
- package/pp-core/bin/lib/commands.cjs +9 -4
- package/pp-core/bin/lib/compose-bindings.cjs +251 -88
- package/pp-core/bin/lib/config-loader.cjs +7 -103
- package/pp-core/bin/lib/config-schema.cjs +1 -1
- package/pp-core/bin/lib/config.cjs +154 -62
- package/pp-core/bin/lib/decisions.cjs +4 -4
- package/pp-core/bin/lib/discovery-vault-check.cjs +246 -0
- package/pp-core/bin/lib/frontmatter.cjs +1 -38
- package/pp-core/bin/lib/git-base-branch.cjs +13 -11
- package/pp-core/bin/lib/git-provider.cjs +4 -4
- package/pp-core/bin/lib/init-command-router.cjs +5 -8
- package/pp-core/bin/lib/init-contract.cjs +648 -738
- package/pp-core/bin/lib/init-interactive.cjs +76 -4
- package/pp-core/bin/lib/init.cjs +24 -51
- package/pp-core/bin/lib/install-profiles.cjs +1 -1
- package/pp-core/bin/lib/io.cjs +8 -0
- package/pp-core/bin/lib/loop-resolver.cjs +189 -129
- package/pp-core/bin/lib/org-profile.cjs +469 -152
- package/pp-core/bin/lib/package-identity.cjs +3 -3
- package/pp-core/bin/lib/phase-command-router.cjs +1 -29
- package/pp-core/bin/lib/phase-scope.cjs +1 -1
- package/pp-core/bin/lib/phase.cjs +11 -253
- package/pp-core/bin/lib/platform-bindings.cjs +40 -19
- package/pp-core/bin/lib/practices-review-check.cjs +15 -9
- package/pp-core/bin/lib/profile-output.cjs +1 -1
- package/pp-core/bin/lib/shell-command-projection.cjs +104 -1
- package/pp-core/bin/lib/state.cjs +11 -85
- package/pp-core/bin/lib/template.cjs +1 -1
- package/pp-core/bin/lib/vault-audit.cjs +650 -126
- package/pp-core/bin/lib/vault-command-router.cjs +2604 -367
- package/pp-core/bin/lib/vault-coordinates.cjs +13 -4
- package/pp-core/bin/lib/vault-index.cjs +699 -0
- package/pp-core/bin/lib/vault-mode.cjs +235 -0
- package/pp-core/bin/lib/vault-practices.cjs +139 -72
- package/pp-core/bin/lib/vault-review.cjs +194 -451
- package/pp-core/bin/lib/vault-seal.cjs +1936 -0
- package/pp-core/bin/lib/vault-standards.cjs +1505 -0
- package/pp-core/bin/lib/vault-trace.cjs +607 -254
- package/pp-core/bin/lib/vault-update.cjs +9 -33
- package/pp-core/bin/lib/vault-write-back.cjs +70 -42
- package/pp-core/bin/lib/vault.cjs +2734 -920
- package/pp-core/bin/lib/verify.cjs +0 -11
- package/pp-core/bin/portable-timeout.sh +1 -1
- package/pp-core/bin/pp-tools.cjs +184 -54
- package/pp-core/bin/shared/config-defaults.manifest.json +0 -7
- package/pp-core/bin/shared/config-schema.manifest.json +0 -3
- package/pp-core/bin/shared/model-catalog.json +2 -2
- package/pp-core/bin/verify-reapply-patches.cjs +0 -0
- package/pp-core/org-profile.json +26 -0
- package/pp-core/references/autonomous-best-effort.md +8 -7
- package/pp-core/references/loop-hook-dispatch.md +0 -25
- package/pp-core/references/planner-gap-closure.md +0 -1
- package/pp-core/references/planner-tdd.md +0 -1
- package/pp-core/references/planner-vault-practices.md +17 -34
- package/pp-core/references/planner-vault-standards.md +320 -0
- package/pp-core/references/planning-config.md +0 -8
- package/pp-core/references/practice-arithmetic-contract.md +121 -0
- package/pp-core/references/tdd.md +0 -1
- package/pp-core/references/verifier-practice-verdicts.md +263 -0
- package/pp-core/templates/README.md +1 -0
- package/pp-core/templates/VAULT-REVIEW.md +202 -0
- package/pp-core/templates/phase-prompt.md +2 -6
- package/pp-core/templates/practices/README.md +20 -23
- package/pp-core/workflows/_runtime-launcher.snippet.sh +0 -0
- package/pp-core/workflows/add-tests.md +55 -4
- package/pp-core/workflows/audit-fix.md +63 -2
- package/pp-core/workflows/audit-milestone.md +1 -1
- package/pp-core/workflows/autonomous.md +85 -11
- package/pp-core/workflows/check-todos.md +13 -2
- package/pp-core/workflows/code-review-fix.md +59 -3
- package/pp-core/workflows/code-review.md +10 -1
- package/pp-core/workflows/complete-milestone.md +1 -1
- package/pp-core/workflows/debug.md +54 -4
- package/pp-core/workflows/discovery-sprint/prefill.md +19 -7
- package/pp-core/workflows/discovery-sprint/prototype.md +46 -49
- package/pp-core/workflows/discovery-sprint/session-13-gates.md +2 -0
- package/pp-core/workflows/discovery-sprint/sessions/01-kick-off.md +1 -3
- package/pp-core/workflows/discovery-sprint/sessions/02-as-is.md +1 -3
- package/pp-core/workflows/discovery-sprint/sessions/03-to-be.md +1 -3
- package/pp-core/workflows/discovery-sprint/sessions/04-ux-design.md +20 -24
- package/pp-core/workflows/discovery-sprint/sessions/05-architecture.md +1 -3
- package/pp-core/workflows/discovery-sprint/sessions/06-security.md +1 -3
- package/pp-core/workflows/discovery-sprint/sessions/07-identity.md +1 -3
- package/pp-core/workflows/discovery-sprint/sessions/08-integration.md +1 -3
- package/pp-core/workflows/discovery-sprint/sessions/09-qa.md +1 -3
- package/pp-core/workflows/discovery-sprint/sessions/10-infrastructure.md +1 -6
- package/pp-core/workflows/discovery-sprint/sessions/11-roadmap.md +1 -3
- package/pp-core/workflows/discovery-sprint/sessions/12-delivery-model.md +1 -3
- package/pp-core/workflows/discovery-sprint/sessions/13-wrap-up.md +1 -3
- package/pp-core/workflows/discovery-sprint/templates/context-template.md +1 -1
- package/pp-core/workflows/discovery-sprint/vault-context.md +213 -0
- package/pp-core/workflows/discovery-sprint.md +7 -3
- package/pp-core/workflows/discovery-to-build/steps/architecture-deliverables.md +4 -4
- package/pp-core/workflows/discovery-to-build/templates/pp-handoff.md +43 -1
- package/pp-core/workflows/discovery-to-build.md +10 -2
- package/pp-core/workflows/discuss-phase.md +3 -10
- package/pp-core/workflows/execute-phase/steps/check-query-gate.md +329 -0
- package/pp-core/workflows/execute-phase/steps/client-context-gate.md +3 -2
- package/pp-core/workflows/execute-phase/steps/cross-ai-delegation.md +1 -1
- package/pp-core/workflows/execute-phase/steps/execute-pre-dispatch.md +98 -0
- package/pp-core/workflows/execute-phase/steps/per-plan-worktree-gate.md +94 -0
- package/pp-core/workflows/execute-phase/steps/post-merge-gate.md +6 -8
- package/pp-core/workflows/execute-phase/steps/vault-fetch.md +115 -14
- package/pp-core/workflows/execute-phase/steps/vault-load-gate.md +322 -20
- package/pp-core/workflows/execute-phase/steps/verifier-practice-payload.md +165 -0
- package/pp-core/workflows/execute-phase/steps/verify-post-gate.md +163 -0
- package/pp-core/workflows/execute-phase.md +155 -128
- package/pp-core/workflows/execute-plan.md +56 -6
- package/pp-core/workflows/fast.md +53 -4
- package/pp-core/workflows/help/modes/full.md +3 -3
- package/pp-core/workflows/init/steps/client-context-capture.md +101 -0
- package/pp-core/workflows/init/steps/gitlab-provisioning.md +130 -0
- package/pp-core/workflows/init/steps/project-md-deferral-narration.md +66 -0
- package/pp-core/workflows/init/steps/reachability-outcome.md +70 -0
- package/pp-core/workflows/init.md +232 -272
- package/pp-core/workflows/new-project.md +31 -23
- package/pp-core/workflows/plan-phase.md +98 -45
- package/pp-core/workflows/quick.md +117 -3
- package/pp-core/workflows/secure-phase.md +1 -1
- package/pp-core/workflows/ship.md +57 -28
- package/pp-core/workflows/undo.md +1 -1
- package/pp-core/workflows/update.md +3 -3
- package/pp-core/workflows/validate-phase.md +58 -3
- package/pp-core/workflows/vault-review.md +282 -22
- package/pp-core/workflows/verify-work.md +12 -14
- package/scripts/changeset/cli.cjs +6 -5
- package/scripts/changeset/lint.cjs +0 -0
- package/scripts/changeset/new.cjs +33 -50
- package/scripts/dev-env.cjs +25 -85
- package/scripts/gen-capability-registry.cjs +199 -11
- package/scripts/gen-practice-arithmetic-contract.cjs +412 -0
- package/scripts/lib/allowlist-ratchet.cjs +1 -103
- package/scripts/lint-allow-test-rule-refs.allowlist.json +0 -2
- package/scripts/lint-docs-required.cjs +0 -0
- package/scripts/lint-pr-check-project-dir.cjs +1 -3
- package/scripts/lint-test-file-count.allowlist.json +12 -10
- package/scripts/practice-arithmetic-contract.allow.json +35 -0
- package/scripts/prompt-injection-scan.sh +22 -17
- package/scripts/record-vault-mode-golden.cjs +89 -0
- package/scripts/release-channel.cjs +0 -0
- package/scripts/sync-manifest-versions.cjs +5 -4
- package/scripts/sync-rulesets.sh +1 -1
- package/scripts/update-size-baseline.cjs +30 -274
- package/scripts/vault-calibrate.cjs +996 -0
- package/scripts/verify-opengsd-sweep.cjs +3 -5
- package/scripts/workflow-size.cjs +9 -61
- package/pp-core/bin/lib/dispatch-plan.cjs +0 -483
- package/pp-core/bin/lib/install-run.cjs +0 -536
- package/pp-core/bin/lib/loop-gates.cjs +0 -213
- package/pp-core/bin/lib/phase-manifest.cjs +0 -527
- package/pp-core/bin/lib/phase-tier.cjs +0 -480
- package/pp-core/bin/lib/plan-graph.cjs +0 -368
- package/pp-core/references/executor-vault-trace.md +0 -47
- package/pp-core/references/planner-plan-graph.md +0 -66
- package/pp-core/workflows/discovery-sprint/vault-fetch.md +0 -37
- package/pp-core/workflows/execute-phase/steps/checkpoint-handling.md +0 -45
- package/pp-core/workflows/execute-phase/steps/install-run.md +0 -29
- package/pp-core/workflows/execute-phase/steps/phase-close-rebaseline.md +0 -23
- package/pp-core/workflows/execute-phase/steps/serial-group-dispatch.md +0 -73
- package/pp-core/workflows/execute-phase/steps/vault-door-gate.md +0 -19
- package/pp-core/workflows/plan-phase/steps/phase-tier.md +0 -43
- package/scripts/lib/size-budget.cjs +0 -311
- package/scripts/lint-neutrality-names.cjs +0 -694
- package/scripts/lint-org-coordinates.allowlist.json +0 -35
- package/scripts/lint-org-coordinates.cjs +0 -426
|
@@ -3,115 +3,162 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
/**
|
|
6
|
-
* src/vault-review.cts — the DETERMINISTIC half of
|
|
7
|
-
* (
|
|
6
|
+
* src/vault-review.cts — the DETERMINISTIC half of Phase 6's agent tier
|
|
7
|
+
* (Vault Assurance #71924, phase 06 plan 03; VERIFY-01, D-06-09 … D-06-15).
|
|
8
8
|
*
|
|
9
9
|
* ## What this module is
|
|
10
10
|
*
|
|
11
|
-
* The agent
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
* to the genuinely ambiguous remainder and nothing else.
|
|
11
|
+
* The agent tier judges vault practices no regex can settle. A judgement is not
|
|
12
|
+
* reproducible, so everything the agent must NOT invent lives here instead: the
|
|
13
|
+
* input set, both derived-N-A populations, the counts, the rate line, and the
|
|
14
|
+
* git range the review was computed over. Every one of them is an exported,
|
|
15
|
+
* unit-tested pure function, so the agent's discretionary surface shrinks to
|
|
16
|
+
* the genuinely ambiguous remainder and nothing else.
|
|
18
17
|
*
|
|
19
18
|
* Purely a LEAF. It imports leaves (`./vault-audit.cjs`, `./vault-practices.cjs`,
|
|
20
|
-
* `./
|
|
21
|
-
*
|
|
22
|
-
* the world — the oracle run and the range resolver — take
|
|
23
|
-
* (`_audit`, `exec`)
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
19
|
+
* `./git-base-branch.cjs`, `./shell-command-projection.cjs`) and no adapter, and
|
|
20
|
+
* every derivation function is pure text/set/list in, structure out. The two
|
|
21
|
+
* functions that touch the world — the oracle run and the range resolver — take
|
|
22
|
+
* INJECTED seams (`_audit`, `exec`) in the `_check`/`_core` idiom
|
|
23
|
+
* `cmdCheckPracticesReview` established, so a test drives them without a repo.
|
|
24
|
+
*
|
|
25
|
+
* A note on that idiom: the shipped one defers `require` to call time. Here the
|
|
26
|
+
* sibling modules are imported at the top instead and the seam is the injection
|
|
27
|
+
* parameter alone. Reason: this file is linted with
|
|
28
|
+
* `tseslint.configs.recommendedTypeChecked`, under which a bare `require()`
|
|
29
|
+
* returns `any` and every use of the result is a `no-unsafe-*` error, whereas
|
|
29
30
|
* `import x = require('./y.cjs')` resolves to the sibling's `.cts` source and is
|
|
30
|
-
* fully typed.
|
|
31
|
+
* fully typed. Neither sibling performs I/O at load, so nothing is paid for the
|
|
32
|
+
* eager import, and the property the idiom exists for — a test can substitute
|
|
33
|
+
* the module — is preserved exactly.
|
|
31
34
|
*
|
|
32
|
-
* ## The verdict vocabulary is LOCKED
|
|
35
|
+
* ## The verdict vocabulary is LOCKED (D-06-10)
|
|
33
36
|
*
|
|
34
|
-
* `REVIEW_VERDICT` holds exactly PASS / FAIL / N-A / INCONCLUSIVE
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
*
|
|
37
|
+
* `REVIEW_VERDICT` holds exactly PASS / FAIL / N-A / INCONCLUSIVE, with `N-A`
|
|
38
|
+
* spelled with the hyphen D-06-10's own text uses. It is a module constant and
|
|
39
|
+
* is never re-derived from configuration or from the vault — the same rule
|
|
40
|
+
* `src/vault-trace.cts` and `src/vault-audit.cts` state, for the same reason: a
|
|
41
|
+
* verdict vocabulary a client repository could move is not a verdict vocabulary.
|
|
38
42
|
*
|
|
39
|
-
* It shares no token with the mechanical
|
|
43
|
+
* It shares no token with the mechanical tier's `AUDIT_VERDICT`. `NO_CHECK` and
|
|
40
44
|
* `NOT_MATCHED` are facts about a machine-checkable rule; `N-A` and
|
|
41
|
-
* `INCONCLUSIVE` are facts about a judgement
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
*
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
*
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
*
|
|
56
|
-
*
|
|
57
|
-
*
|
|
58
|
-
*
|
|
59
|
-
*
|
|
45
|
+
* `INCONCLUSIVE` are facts about a judgement, and a shared token would invite
|
|
46
|
+
* one to be read as the other.
|
|
47
|
+
*
|
|
48
|
+
* RECORDED DIVERGENCE from `agents/pp-code-reviewer.md`, which deliberately says
|
|
49
|
+
* "likely violates, never certainty": this tier asserts a FLAT `FAIL`. It can,
|
|
50
|
+
* because it gates nothing (D-06-03, VERIFY-03) — a FAIL here moves no score and
|
|
51
|
+
* blocks no ship, so hedging the word buys no safety and costs the reader the
|
|
52
|
+
* one signal the artifact exists to carry. Uncertainty has its own honest home:
|
|
53
|
+
* `INCONCLUSIVE`, which D-06-10 split out precisely so "I could not determine
|
|
54
|
+
* this" never hides inside "not applicable here". The agent-side rationale is
|
|
55
|
+
* plan 06-06's; the vocabulary decision is recorded here, where the constant is.
|
|
56
|
+
*
|
|
57
|
+
* ## STATED DEVIATION — Arm A widens the judged boundary
|
|
58
|
+
*
|
|
59
|
+
* `06-CONTEXT.md`'s `<domain>` fixes this phase's input set to *traced practices
|
|
60
|
+
* that carry no usable `check:` block*. `deriveNaAUniverse` (Arm A, plan 06-03
|
|
61
|
+
* task 2) reports on practices that were NEVER traced — the whole vault practice
|
|
62
|
+
* universe recorded at the pinned ref. That is WIDER than the stated boundary.
|
|
63
|
+
* It is an operator-approved deviation, approved 2026-09-03 when F-2 was
|
|
64
|
+
* resolved, and it is stated here rather than buried in a count because a
|
|
65
|
+
* denominator that silently grew is a denominator nobody can audit.
|
|
66
|
+
*
|
|
67
|
+
* It also surfaces in the ARTIFACT, not only in this comment: every Arm A row's
|
|
68
|
+
* reason text names the vault universe at the pinned ref as its source, so a
|
|
69
|
+
* reader of the rendered review meets the widening at the row that caused it.
|
|
70
|
+
*
|
|
71
|
+
* WHY the widening was necessary. `buildTraceStatement`
|
|
72
|
+
* (`src/vault-trace.cts:645-680`) populates the ledger from
|
|
73
|
+
* `matchPractices(...).matched ++ .global` and nothing else. Every ledger entry
|
|
74
|
+
* therefore ALREADY matched: there is no "never matched" ledger entry to derive
|
|
75
|
+
* D-06-09's N-A from. Arm A derives it from `practiceUniverse \ ledger` — data
|
|
76
|
+
* the statement already carries (`src/vault-trace.cts:618`) — and Arm B
|
|
77
|
+
* re-derives applicability at review time from the phase's real diff. Two arms,
|
|
78
|
+
* two DISTINCTLY LABELLED derived reasons that never blur.
|
|
79
|
+
*
|
|
80
|
+
* ## DECISION — `check:`-presence is read LIVE, with an advisory; NO schema change
|
|
60
81
|
*
|
|
61
82
|
* The trace records no `check:`-presence field, so "traced ∩ NO_CHECK" needs the
|
|
62
|
-
* vault cache at review time. This module reads it
|
|
63
|
-
* evaluator (`evaluatePractices` + `isNoCheckVerdict`); when
|
|
64
|
-
* the cache's `resolvedSha` disagree it emits one advisory
|
|
65
|
-
*
|
|
66
|
-
*
|
|
67
|
-
*
|
|
68
|
-
*
|
|
83
|
+
* vault cache at review time. This module reads it live and harvests the answer
|
|
84
|
+
* through the SHIPPED evaluator (`evaluatePractices` + `isNoCheckVerdict`); when
|
|
85
|
+
* the trace's pin and the cache's `resolvedSha` disagree it emits one advisory
|
|
86
|
+
* shaped like the shipped `PIN_DIVERGED` wording — reports, never blocks,
|
|
87
|
+
* explicit that no reader action is required.
|
|
88
|
+
*
|
|
89
|
+
* Justification, recorded because the alternative was live:
|
|
90
|
+
* (a) adding `checkPresent` to `TracePractice` is a trace-SCHEMA change,
|
|
91
|
+
* carrying the `predicateType`/`schemaVersion` freeze discipline
|
|
92
|
+
* (`docs/vault-trace-format.md`), for a consumer this phase can serve
|
|
93
|
+
* without it;
|
|
94
|
+
* (b) the advisory idiom is shipped and mutation-proved one module over
|
|
95
|
+
* (`src/check-command-router.cts:2687-2725`), including the discipline that
|
|
96
|
+
* an advisory stays in the record and is excluded from every block
|
|
97
|
+
* decision;
|
|
98
|
+
* (c) this tier is not a gate, so environment-dependence is acceptable here —
|
|
99
|
+
* the exact carve-out `docs/vault-trace-format.md`'s own warning permits.
|
|
69
100
|
*
|
|
70
101
|
* ## The oracle is HARVESTED, never re-derived
|
|
71
102
|
*
|
|
72
103
|
* `deriveCandidateSet` learns `check:`-presence by running the shipped
|
|
73
|
-
* `evaluatePractices` and filtering with `isNoCheckVerdict`. It
|
|
74
|
-
* `check:` key, because the
|
|
75
|
-
*
|
|
76
|
-
*
|
|
77
|
-
*
|
|
78
|
-
*
|
|
79
|
-
*
|
|
80
|
-
*
|
|
81
|
-
*
|
|
82
|
-
*
|
|
83
|
-
*
|
|
84
|
-
*
|
|
85
|
-
*
|
|
86
|
-
*
|
|
87
|
-
* -
|
|
88
|
-
*
|
|
89
|
-
*
|
|
90
|
-
*
|
|
91
|
-
*
|
|
92
|
-
*
|
|
93
|
-
*
|
|
94
|
-
*
|
|
95
|
-
*
|
|
96
|
-
*
|
|
97
|
-
*
|
|
104
|
+
* `evaluatePractices` and filtering with the shipped `isNoCheckVerdict`. It
|
|
105
|
+
* never scans for a `check:` key, because the shipped ladder
|
|
106
|
+
* (`src/vault-audit.cts:381-396`) tests `hasInvalidCheck` FIRST: an INVALID
|
|
107
|
+
* check is the mechanical tier's `FAIL`, not a `NO_CHECK`. A hand-rolled scan
|
|
108
|
+
* gets exactly that case backwards and would hand a typo'd practice to the agent
|
|
109
|
+
* as if the mechanical tier had declined it.
|
|
110
|
+
*
|
|
111
|
+
* The oracle runs over an EMPTY file set on purpose. The ladder returns
|
|
112
|
+
* `NO_CHECK` before any file is consulted (`if (!check)` precedes both the pair
|
|
113
|
+
* cap and the matching), so the answer is file-independent — and running it over
|
|
114
|
+
* no files makes it machine-independent too.
|
|
115
|
+
*
|
|
116
|
+
* ## Phase 5 findings this module is planned AROUND, not assumed fixed
|
|
117
|
+
*
|
|
118
|
+
* - **CR-01.** `matchPractices` returns `practicesProcessed: 0` on a
|
|
119
|
+
* never-fetched vault, indistinguishable from an empty one. This module does
|
|
120
|
+
* not modify `src/vault-practices.cts`; Arm B instead consumes a
|
|
121
|
+
* `discoverPracticeFiles` result, whose typed shape keeps `vaultPresent` and
|
|
122
|
+
* `readFailed` DISTINCT (`src/vault-practices.cts:165-175`).
|
|
123
|
+
* - **WR-02.** A parser-rejected practice silently leaves the practice set.
|
|
124
|
+
* `deriveCandidateSet` reports discovery's `skipped` entries as NAMED
|
|
125
|
+
* exclusions and its `degraded` entries as notes. Never silently narrowed.
|
|
126
|
+
* - **WR-04 / F-3, and UAT-6-F1.** The review range now SHARES the mechanical
|
|
127
|
+
* tier's phase-scoped resolution first: `resolvePhaseRange` answers from the
|
|
128
|
+
* phase manifest's `startCommit` (written at execute:pre by `vault manifest
|
|
129
|
+
* start`), then its recorded `commits` (written at verify:post), then the
|
|
130
|
+
* SUMMARY trace digests of a phase built before that writer existed. Only when
|
|
131
|
+
* all three fall back does this module run its OWN `(NN-` commit-token scan
|
|
132
|
+
* over `base..HEAD`, and only then the whole base-branch span. The vocabulary
|
|
133
|
+
* stays SEPARATE: `REVIEW_RANGE_SOURCE` mirrors the three phase-scoped values
|
|
134
|
+
* rather than referencing `AUDIT_RANGE_SOURCE`, because that object belongs to
|
|
135
|
+
* the blocking tier, and a shared vocabulary would couple the wall's two sides
|
|
136
|
+
* and shift producer-audit citations for no gain.
|
|
137
|
+
* - **CR-04 (05-REVIEW:364) — OUT OF SCOPE and NOT assumed fixed.**
|
|
138
|
+
* `pp-core/templates/practices/README.md` tells vault authors that a skipped
|
|
139
|
+
* or check-invalid practice "never breaks the run", while at HEAD both halt
|
|
140
|
+
* the blocking gates. This module builds on the SHIPPED semantics as measured:
|
|
141
|
+
* an invalid `check:` is a mechanical-tier FAIL, excluded from the candidate
|
|
142
|
+
* set and reported as a named exclusion, whatever the README claims. Fixing
|
|
143
|
+
* the README is vault-repo docs work. If it is fixed later nothing here
|
|
144
|
+
* changes, because every judgement below binds to `verdictForPractice` /
|
|
145
|
+
* `isNoCheckVerdict` and never to the README's prose.
|
|
98
146
|
*
|
|
99
147
|
* ## Nothing measured must never render as clean
|
|
100
148
|
*
|
|
101
|
-
* Every degenerate path returns a record that SAYS what did not happen —
|
|
102
|
-
* `oracleSource: 'unavailable'` with a note, a `notDerived` record per arm
|
|
103
|
-
* `reviewRefusesCleanRender
|
|
104
|
-
*
|
|
149
|
+
* Every degenerate path here returns a record that SAYS what did not happen —
|
|
150
|
+
* `oracleSource: 'unavailable'` with a note, a `notDerived` record per arm
|
|
151
|
+
* (task 2), and `reviewRefusesCleanRender` (task 3), the single predicate
|
|
152
|
+
* (D-06-15 / F-3) that forbids a clean review whenever `filesConsidered === 0`.
|
|
105
153
|
*
|
|
106
154
|
* ## Trust boundary
|
|
107
155
|
*
|
|
108
156
|
* Practice ids, `applies-to` globs and practice prose are CLIENT-AUTHORED DATA.
|
|
109
157
|
* They are bounded with the shipped `boundedToken` before they reach any string
|
|
110
158
|
* this module returns, and applicability is decided by the shipped `globMatch`
|
|
111
|
-
* alone. No client-authored byte is ever compiled into a regular expression
|
|
112
|
-
* no second glob grammar is written here (
|
|
113
|
-
*
|
|
114
|
-
* never absolute filesystem paths.
|
|
159
|
+
* alone. No client-authored byte is ever compiled into a regular expression
|
|
160
|
+
* (ADR-71924 §3), and no second glob grammar is written here (§2). Reason texts
|
|
161
|
+
* name practice ids and mechanisms, never absolute filesystem paths.
|
|
115
162
|
*/
|
|
116
163
|
const node_path_1 = __importDefault(require("node:path"));
|
|
117
164
|
const shell_command_projection_cjs_1 = require("./shell-command-projection.cjs");
|
|
@@ -182,12 +229,9 @@ const NOT_DERIVED_GROUND = Object.freeze({
|
|
|
182
229
|
VAULT_STATE_UNKNOWN: 'vault-state-unknown',
|
|
183
230
|
VAULT_NEVER_FETCHED: 'vault-never-fetched',
|
|
184
231
|
VAULT_UNREADABLE: 'vault-unreadable',
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
* Without this ground a miss rendered as an empty, clean review.
|
|
189
|
-
*/
|
|
190
|
-
VAULT_FETCH_MISS: 'vault-fetch-miss',
|
|
232
|
+
// Quick 261015-at3: the vault was read, and it is a standards vault the
|
|
233
|
+
// project reads in practices mode — no practice layer was read at all.
|
|
234
|
+
VAULT_STANDARDS_ONLY: 'vault-standards-only',
|
|
191
235
|
NO_FILES_CONSIDERED: 'no-files-considered',
|
|
192
236
|
MATCHER_UNAVAILABLE: 'matcher-unavailable',
|
|
193
237
|
});
|
|
@@ -209,8 +253,8 @@ const ORACLE_UNAVAILABLE_GROUND = 'no-check-oracle-unavailable';
|
|
|
209
253
|
* difference between fixing D3 and shouting it. That ground says the TRACE
|
|
210
254
|
* records no practice universe — which is true of every reconstruct review by
|
|
211
255
|
* definition, because a phase with no trace has no universe key. Refusing on it
|
|
212
|
-
* would refuse the entire reconstruct arm,
|
|
213
|
-
*
|
|
256
|
+
* would refuse the entire reconstruct arm, and `pp-core/workflows/vault-review.md`
|
|
257
|
+
* §3 calls that arm the primary path, not a degraded one. A review that refuses
|
|
214
258
|
* everything judges exactly as much as one that judges nothing; it is only
|
|
215
259
|
* louder about it.
|
|
216
260
|
*/
|
|
@@ -218,7 +262,7 @@ const UNMEASURED_GROUND = new Set([
|
|
|
218
262
|
NOT_DERIVED_GROUND.VAULT_STATE_UNKNOWN,
|
|
219
263
|
NOT_DERIVED_GROUND.VAULT_NEVER_FETCHED,
|
|
220
264
|
NOT_DERIVED_GROUND.VAULT_UNREADABLE,
|
|
221
|
-
NOT_DERIVED_GROUND.
|
|
265
|
+
NOT_DERIVED_GROUND.VAULT_STANDARDS_ONLY,
|
|
222
266
|
NOT_DERIVED_GROUND.NO_FILES_CONSIDERED,
|
|
223
267
|
NOT_DERIVED_GROUND.MATCHER_UNAVAILABLE,
|
|
224
268
|
ORACLE_UNAVAILABLE_GROUND,
|
|
@@ -231,7 +275,7 @@ const UNMEASURED_GROUND = new Set([
|
|
|
231
275
|
* shared vocabulary would couple the two sides of the wall D-06-01 exists to
|
|
232
276
|
* keep apart — and would shift the producer-audit citations for no gain.
|
|
233
277
|
*
|
|
234
|
-
*
|
|
278
|
+
* 260915-ik2 (UAT-6-F1) added three members whose VALUES mirror the
|
|
235
279
|
* phase-scoped sources of the shared resolution. They are this object's own
|
|
236
280
|
* strings, reached from the audit's through `REVIEW_SOURCE_FOR_AUDIT_SOURCE`,
|
|
237
281
|
* never a reference to the audit's object.
|
|
@@ -266,7 +310,7 @@ const REVIEW_SOURCE_FOR_AUDIT_SOURCE = Object.freeze({
|
|
|
266
310
|
* still has no way to review the phase in front of them.
|
|
267
311
|
*/
|
|
268
312
|
const REVIEW_RANGE_UNRESOLVABLE_REMEDY = 'Anchor the phase by running it through /pp-execute-phase (execute:pre records the start commit ' +
|
|
269
|
-
'via
|
|
313
|
+
'via vault manifest start), or audit it by hand over a range you name: ' +
|
|
270
314
|
'pp-tools vault audit <phase-dir> --range <from>..<to>.';
|
|
271
315
|
/**
|
|
272
316
|
* The opening of the note `resolveReviewRange` pushes when the SHARED
|
|
@@ -344,8 +388,8 @@ function isNonVerdict(verdict) {
|
|
|
344
388
|
* every check-bearing one); crossing that universe with the review's real diff
|
|
345
389
|
* is `deriveNaANoFile`'s job and reaches the SHIPPED `globMatch` through the
|
|
346
390
|
* same `_practices` seam. No second matcher is written for the reconstruct arm,
|
|
347
|
-
* because a second matcher is a second answer to one question (
|
|
348
|
-
*
|
|
391
|
+
* because a second matcher is a second answer to one question (ADR-71924 §2,
|
|
392
|
+
* and CR-02's class). The practices that fall outside the range therefore leave
|
|
349
393
|
* this set as STATED derived N-A rows rather than as silent absences.
|
|
350
394
|
*
|
|
351
395
|
* Three oracle paths, and the third one SAYS so:
|
|
@@ -494,8 +538,8 @@ function deriveCandidateSet(opts) {
|
|
|
494
538
|
/**
|
|
495
539
|
* The pin advisory (recorded decision above; no trace-schema change).
|
|
496
540
|
*
|
|
497
|
-
* Mirrors the wording discipline of the
|
|
498
|
-
*
|
|
541
|
+
* Mirrors the wording discipline of the shipped `PIN_DIVERGED` note at
|
|
542
|
+
* `src/check-command-router.cts:2687-2716` and harvests its KIND from
|
|
499
543
|
* `TRACE_PROBLEM_KIND` so the two cannot drift. It reports; it never blocks;
|
|
500
544
|
* it states that no reader action is required. An ABSENT cache emits NOTHING —
|
|
501
545
|
* an absent cache is not a divergence, and a check that fired on absence would
|
|
@@ -519,8 +563,8 @@ function derivePinAdvisories(tracePinSha, cacheSha) {
|
|
|
519
563
|
}
|
|
520
564
|
// ─── Counts and the rate line ────────────────────────────────────────────────
|
|
521
565
|
/**
|
|
522
|
-
* Count the four verdicts, in the shape
|
|
523
|
-
* established one tier down.
|
|
566
|
+
* Count the four verdicts, in the shape `countVerdicts`
|
|
567
|
+
* (`src/vault-audit.cts:404-414`) established one tier down.
|
|
524
568
|
*
|
|
525
569
|
* `judged` is `total - na`. Recorded because it is a choice: an `INCONCLUSIVE`
|
|
526
570
|
* practice WAS judged — the agent looked at it and could not determine an
|
|
@@ -580,7 +624,7 @@ function countReviewVerdicts(rows) {
|
|
|
580
624
|
counts.malformed.push({
|
|
581
625
|
id: quotableId(id),
|
|
582
626
|
verdict,
|
|
583
|
-
problem: '
|
|
627
|
+
problem: 'D-06-11: a non-verdict must carry a stated reason. A bare N-A or INCONCLUSIVE is ' +
|
|
584
628
|
'indistinguishable from the agent giving up.',
|
|
585
629
|
});
|
|
586
630
|
}
|
|
@@ -669,7 +713,7 @@ function deriveMechanicalCoverageLine(opts) {
|
|
|
669
713
|
*
|
|
670
714
|
* BOUNDARY WIDENING, surfaced in every row (and in the module header): these
|
|
671
715
|
* ids were never traced, so reporting them widens the judged boundary beyond
|
|
672
|
-
*
|
|
716
|
+
* `06-CONTEXT.md`'s stated input set. Operator-approved 2026-09-03. The reason
|
|
673
717
|
* text names the vault universe as its source so a reader of the ARTIFACT meets
|
|
674
718
|
* the widening at the row that caused it, never only in a plan nobody reopens.
|
|
675
719
|
*
|
|
@@ -694,9 +738,9 @@ function deriveNaAUniverse(opts) {
|
|
|
694
738
|
? {
|
|
695
739
|
ground: NOT_DERIVED_GROUND.UNIVERSE_WITHHELD,
|
|
696
740
|
statement: 'Arm A derived NOTHING: this trace DOES record a practice universe, and it was ' +
|
|
697
|
-
'deliberately withheld because the trace is DEGRADED — it records that
|
|
698
|
-
'
|
|
699
|
-
'practices governed that work and may not be subtracted from. This is a ' +
|
|
741
|
+
'deliberately withheld because the trace is DEGRADED — it records that no trusted vault ' +
|
|
742
|
+
'load existed when the work was planned, so its universe proves nothing about which ' +
|
|
743
|
+
'practices governed that work and may not be subtracted from (D-06-15). This is a ' +
|
|
700
744
|
// Same wording constraint as the sibling branch below: the
|
|
701
745
|
// prompt-injection scanner's regex carries no leading word
|
|
702
746
|
// boundary, so "is a different fact from …" is written in place
|
|
@@ -767,8 +811,8 @@ function deriveNaAUniverse(opts) {
|
|
|
767
811
|
* global practice applies to the whole change set by definition, so "no file
|
|
768
812
|
* fell under it" is not a statement that can be true of one.
|
|
769
813
|
*
|
|
770
|
-
* Applicability is decided by the SHIPPED `globMatch` alone (
|
|
771
|
-
*
|
|
814
|
+
* Applicability is decided by the SHIPPED `globMatch` alone (ADR-71924 §2 — no
|
|
815
|
+
* second grammar; §3 — no client-authored byte compiled into a RegExp).
|
|
772
816
|
*/
|
|
773
817
|
function deriveNaANoFile(opts) {
|
|
774
818
|
const arm = NA_DERIVED_REASON.NO_FILE_IN_DIFF;
|
|
@@ -787,20 +831,24 @@ function deriveNaANoFile(opts) {
|
|
|
787
831
|
'honest N-A.');
|
|
788
832
|
}
|
|
789
833
|
if (discovery.vaultPresent === false) {
|
|
790
|
-
return refuse(NOT_DERIVED_GROUND.VAULT_NEVER_FETCHED, 'Arm B derived NOTHING:
|
|
834
|
+
return refuse(NOT_DERIVED_GROUND.VAULT_NEVER_FETCHED, 'Arm B derived NOTHING: the client vault directory does not exist on this machine — the vault was ' +
|
|
791
835
|
'NEVER FETCHED. This is not an empty vault, and no applicability was derived from it. Repair by ' +
|
|
792
836
|
'fetching the vault, then re-run the review.');
|
|
793
837
|
}
|
|
794
|
-
if (discovery.fetchMiss === true) {
|
|
795
|
-
return refuse(NOT_DERIVED_GROUND.VAULT_FETCH_MISS, 'Arm B derived NOTHING: the vault cache records a FETCH MISS (or holds no file), so no client vault ' +
|
|
796
|
-
'was loaded and no applicability was derived from it. An empty practice set read from a miss is ' +
|
|
797
|
-
'not an empty vault. Repair by fetching the vault, then re-run the review.');
|
|
798
|
-
}
|
|
799
838
|
if (discovery.readFailed === true) {
|
|
800
839
|
return refuse(NOT_DERIVED_GROUND.VAULT_UNREADABLE, 'Arm B derived NOTHING: the client vault is PRESENT but a read of it REFUSED — the vault is ' +
|
|
801
840
|
'unreadable. This is not an empty vault and not a missing one, and no applicability was derived ' +
|
|
802
841
|
'from it. Repair by making the cached vault readable, then re-run the review.');
|
|
803
842
|
}
|
|
843
|
+
// Quick 261015-at3 (entry-point sweep): read from the same typed discovery
|
|
844
|
+
// result as the two rungs above, never from prose. A trace an older engine
|
|
845
|
+
// recorded `loaded` over this vault would otherwise take the traced arm and
|
|
846
|
+
// render a clean "0 of 0" review of a phase no practice governed.
|
|
847
|
+
if (discovery.vaultStandardsOnly === true) {
|
|
848
|
+
return refuse(NOT_DERIVED_GROUND.VAULT_STANDARDS_ONLY, 'Arm B derived NOTHING: the client vault holds standards (INDEX.md) and no practice files, and this ' +
|
|
849
|
+
'project reads it in practices mode, so no practice layer was read. This is not an empty vault, and ' +
|
|
850
|
+
'no applicability was derived from it. Repair by setting vault.mode to standards, then re-plan the phase.');
|
|
851
|
+
}
|
|
804
852
|
const diffFiles = asStringList(opts.diffFiles).map((f) => f.replace(/\\/g, '/'));
|
|
805
853
|
if (diffFiles.length === 0) {
|
|
806
854
|
return refuse(NOT_DERIVED_GROUND.NO_FILES_CONSIDERED, 'Arm B derived NOTHING: the review range yielded zero files, so no applicability was measured. ' +
|
|
@@ -811,7 +859,7 @@ function deriveNaANoFile(opts) {
|
|
|
811
859
|
const matcher = opts._practices ?? practicesMod;
|
|
812
860
|
if (typeof matcher.globMatch !== 'function') {
|
|
813
861
|
return refuse(NOT_DERIVED_GROUND.MATCHER_UNAVAILABLE, 'Arm B derived NOTHING: the shipped glob matcher could not be reached, and no second matcher is ' +
|
|
814
|
-
'written here (
|
|
862
|
+
'written here (ADR-71924 §2). No applicability was measured.');
|
|
815
863
|
}
|
|
816
864
|
const byId = new Map();
|
|
817
865
|
for (const p of opts.practices ?? []) {
|
|
@@ -916,7 +964,7 @@ function normalisePhaseToken(v) {
|
|
|
916
964
|
* Resolve the range this review is computed over, and RECORD where it came from
|
|
917
965
|
* (F-3 / WR-04).
|
|
918
966
|
*
|
|
919
|
-
* First — the SHARED phase-scoped resolution (UAT-6-F1). When a
|
|
967
|
+
* First — the SHARED phase-scoped resolution (260915-ik2, UAT-6-F1). When a
|
|
920
968
|
* `phaseDir` is given, the mechanical tier's `resolvePhaseRange` is asked
|
|
921
969
|
* before anything else, and its ladder is the one this tier adopts:
|
|
922
970
|
* `phase-start-commit` (the manifest anchor written at execute:pre),
|
|
@@ -945,7 +993,7 @@ function normalisePhaseToken(v) {
|
|
|
945
993
|
* Fallback — `base-branch`: `resolveBaseBranchResult(cwd)...HEAD`. The 5-tier
|
|
946
994
|
* ladder is never re-derived (#1146 records what the naive `origin/HEAD` +
|
|
947
995
|
* `:-main` form cost) and every git call goes through the timeout-bounded
|
|
948
|
-
* `
|
|
996
|
+
* `execGitLocal` seam (D-116), never `execFileSync`. The RESULT form is used rather than the
|
|
949
997
|
* bare string so a REFUSED `git.base_branch` becomes a stated note instead of a
|
|
950
998
|
* silent change of range (T-6-49).
|
|
951
999
|
*
|
|
@@ -966,7 +1014,7 @@ function normalisePhaseToken(v) {
|
|
|
966
1014
|
* that could not be read must never look like a phase that changed nothing.
|
|
967
1015
|
*/
|
|
968
1016
|
function resolveReviewRange(opts) {
|
|
969
|
-
const exec = typeof opts?.exec === 'function' ? opts.exec : shell_command_projection_cjs_1.
|
|
1017
|
+
const exec = typeof opts?.exec === 'function' ? opts.exec : shell_command_projection_cjs_1.execGitLocal;
|
|
970
1018
|
const cwd = typeof opts?.cwd === 'string' && opts.cwd !== '' ? opts.cwd : process.cwd();
|
|
971
1019
|
const notes = [];
|
|
972
1020
|
let base = 'main';
|
|
@@ -990,7 +1038,7 @@ function resolveReviewRange(opts) {
|
|
|
990
1038
|
}
|
|
991
1039
|
let range = `${base}...HEAD`;
|
|
992
1040
|
let rangeSource = REVIEW_RANGE_SOURCE.BASE_BRANCH;
|
|
993
|
-
// The shared phase-scoped resolution, ahead of the token scan (
|
|
1041
|
+
// The shared phase-scoped resolution, ahead of the token scan (260915-ik2).
|
|
994
1042
|
let sharedResolved = false;
|
|
995
1043
|
if (typeof opts?.phaseDir === 'string' && opts.phaseDir !== '') {
|
|
996
1044
|
try {
|
|
@@ -1224,8 +1272,9 @@ const REVIEW_PROBLEM_KIND = Object.freeze({
|
|
|
1224
1272
|
/** D-06-13: the counts line's numbers are not the ones the rows support. */
|
|
1225
1273
|
COUNTS_LINE_ARITHMETIC: 'counts-line-arithmetic',
|
|
1226
1274
|
// ── MECH-02 / D-10-20: the mechanical coverage line ────────────────────────
|
|
1227
|
-
//
|
|
1228
|
-
//
|
|
1275
|
+
// Phase 10 verification found this line INSTRUCTED (agents/pp-vault-reviewer.md
|
|
1276
|
+
// :56-68) and ENFORCED NOWHERE — an artifact that dropped it validated clean,
|
|
1277
|
+
// while that line is MECH-02's whole deliverable. These four kinds close it.
|
|
1229
1278
|
/** The payload carried a coverage line and the artifact does not. */
|
|
1230
1279
|
COVERAGE_LINE_ABSENT: 'coverage-line-absent',
|
|
1231
1280
|
/** A coverage line is present but is not the engine's sentence, verbatim. */
|
|
@@ -1246,13 +1295,6 @@ const REVIEW_PROBLEM_KIND = Object.freeze({
|
|
|
1246
1295
|
FENCE_UNBALANCED: 'fence-unbalanced',
|
|
1247
1296
|
/** The parse cap was reached; rows beyond it were NOT judged. */
|
|
1248
1297
|
ROWS_TRUNCATED: 'rows-truncated',
|
|
1249
|
-
// ── The population, checked against the payload by `vault review finalize` ──
|
|
1250
|
-
/** A practice of the review's population has no row: dropped rather than judged. */
|
|
1251
|
-
ROW_POPULATION_MISSING: 'row-population-missing',
|
|
1252
|
-
/** A row names a practice outside the population the payload derived. */
|
|
1253
|
-
ROW_POPULATION_UNEXPECTED: 'row-population-unexpected',
|
|
1254
|
-
/** One practice carries more than one row. */
|
|
1255
|
-
ROW_DUPLICATE: 'row-duplicate',
|
|
1256
1298
|
// The four D-06-13 score forms.
|
|
1257
1299
|
FORBIDDEN_PERCENTAGE: 'forbidden-percentage',
|
|
1258
1300
|
FORBIDDEN_RATIO: 'forbidden-ratio',
|
|
@@ -1264,8 +1306,8 @@ const REVIEW_SECTION_HEADING = '## Practice Verdicts (agent-judged)';
|
|
|
1264
1306
|
/** The D-06-12 rate line, as a shape. The renderer above is the only writer. */
|
|
1265
1307
|
const RATE_LINE_SHAPE = /^(\d+) of (\d+) practices judged · (\d+) N-A · (\d+) INCONCLUSIVE$/;
|
|
1266
1308
|
/**
|
|
1267
|
-
* The D-10-03 mechanical coverage line, as a shape.
|
|
1268
|
-
* `
|
|
1309
|
+
* The D-10-03 mechanical coverage line, as a shape. `deriveMechanicalCoverageLine`
|
|
1310
|
+
* (`src/vault-audit.cts:823`) is the only writer; this recognises its output so a
|
|
1269
1311
|
* MISPLACED or UNEXPECTED line can be named even when no expectation was supplied.
|
|
1270
1312
|
*/
|
|
1271
1313
|
const COVERAGE_LINE_SHAPE = /^(\d+) of (\d+) practices mechanically checked · (\d+) PASS · (\d+) FAIL · (\d+) NOT_MATCHED · (\d+) NO_CHECK$/;
|
|
@@ -1346,18 +1388,12 @@ function isSeparatorLine(line) {
|
|
|
1346
1388
|
* empty result.
|
|
1347
1389
|
*/
|
|
1348
1390
|
function locateReviewSection(lines) {
|
|
1349
|
-
|
|
1350
|
-
// structure: a heading-shaped client line can neither open the section nor
|
|
1351
|
-
// end it (the same trust boundary `parseReviewRows` draws for rows).
|
|
1352
|
-
const fenced = fencedLineMap(lines);
|
|
1353
|
-
const start = lines.findIndex((l, i) => !fenced[i] && l.trim() === REVIEW_SECTION_HEADING);
|
|
1391
|
+
const start = lines.findIndex((l) => l.trim() === REVIEW_SECTION_HEADING);
|
|
1354
1392
|
if (start === -1)
|
|
1355
1393
|
return { present: false, start: 0, end: lines.length };
|
|
1356
1394
|
let end = lines.length;
|
|
1357
1395
|
for (let i = start + 1; i < lines.length; i += 1) {
|
|
1358
1396
|
const line = lines[i];
|
|
1359
|
-
if (fenced[i])
|
|
1360
|
-
continue;
|
|
1361
1397
|
if (line.startsWith('## ') || line.trim() === '---') {
|
|
1362
1398
|
end = i;
|
|
1363
1399
|
break;
|
|
@@ -1497,11 +1533,11 @@ function judgeReviewRow(unit) {
|
|
|
1497
1533
|
}
|
|
1498
1534
|
const recognised = isJudgedVerdict(verdict) || isNonVerdict(verdict);
|
|
1499
1535
|
if (!recognised) {
|
|
1500
|
-
problems.push(reviewProblem(REVIEW_PROBLEM_KIND.ROW_UNKNOWN_VERDICT, id === '' ? null : id, line, `"${safe(verdict)}" is not one of the four locked verdicts. A coined fifth verdict ` +
|
|
1536
|
+
problems.push(reviewProblem(REVIEW_PROBLEM_KIND.ROW_UNKNOWN_VERDICT, id === '' ? null : id, line, `"${safe(verdict)}" is not one of the four locked verdicts (D-06-10). A coined fifth verdict ` +
|
|
1501
1537
|
'is counted as unrecognised and is never absorbed into a real one.'));
|
|
1502
1538
|
}
|
|
1503
1539
|
else if (isNonVerdict(verdict) && reason === '') {
|
|
1504
|
-
problems.push(reviewProblem(REVIEW_PROBLEM_KIND.ROW_MISSING_REASON, id === '' ? null : id, line, '
|
|
1540
|
+
problems.push(reviewProblem(REVIEW_PROBLEM_KIND.ROW_MISSING_REASON, id === '' ? null : id, line, 'D-06-11: a non-verdict must carry a stated reason. A bare N-A or INCONCLUSIVE is ' +
|
|
1505
1541
|
'indistinguishable from the agent giving up.'));
|
|
1506
1542
|
}
|
|
1507
1543
|
const derived = DERIVED_SOURCE_SHAPE.test(source);
|
|
@@ -1517,7 +1553,7 @@ function judgeReviewRow(unit) {
|
|
|
1517
1553
|
'and a reader who cannot tell them apart cannot act on either.'));
|
|
1518
1554
|
}
|
|
1519
1555
|
if (isJudgedVerdict(verdict)) {
|
|
1520
|
-
problems.push(reviewProblem(REVIEW_PROBLEM_KIND.ROW_DERIVED_VERDICT_CONFLICT, id === '' ? null : id, line, `a derived row carries "${safe(verdict)}", a judged verdict.
|
|
1556
|
+
problems.push(reviewProblem(REVIEW_PROBLEM_KIND.ROW_DERIVED_VERDICT_CONFLICT, id === '' ? null : id, line, `a derived row carries "${safe(verdict)}", a judged verdict. D-06-09 hands derived rows to ` +
|
|
1521
1557
|
'the agent to reproduce, not to reclassify: a derived N-A promoted to PASS or FAIL is a ' +
|
|
1522
1558
|
'computed fact overwritten by a judgement.'));
|
|
1523
1559
|
}
|
|
@@ -1574,7 +1610,7 @@ function validateReviewArtifact(text, opts) {
|
|
|
1574
1610
|
}
|
|
1575
1611
|
else if (!section.present) {
|
|
1576
1612
|
problems.push(reviewProblem(REVIEW_PROBLEM_KIND.SECTION_ABSENT, null, null, `the artifact carries no "${REVIEW_SECTION_HEADING}" heading, so no verdict row was found ` +
|
|
1577
|
-
'and nothing was judged.
|
|
1613
|
+
'and nothing was judged. D-06-02 locks that heading; a section under any other heading is ' +
|
|
1578
1614
|
'not this section.'));
|
|
1579
1615
|
}
|
|
1580
1616
|
const { units, truncated } = section.present
|
|
@@ -1593,7 +1629,8 @@ function validateReviewArtifact(text, opts) {
|
|
|
1593
1629
|
}
|
|
1594
1630
|
if (section.present && units.length === 0) {
|
|
1595
1631
|
problems.push(reviewProblem(REVIEW_PROBLEM_KIND.NO_ROWS, null, null, 'the section heading is present and carries no verdict row. An empty section is not a clean ' +
|
|
1596
|
-
'review: a gap
|
|
1632
|
+
'review: D-06-14 requires a gap to be reported loudly, and §10 of the verifier contract ' +
|
|
1633
|
+
'requires no input to produce NO section rather than an empty one.'));
|
|
1597
1634
|
}
|
|
1598
1635
|
const counts = countReviewVerdicts(rows);
|
|
1599
1636
|
const expectedRateLine = renderRateLine(counts);
|
|
@@ -1603,19 +1640,19 @@ function validateReviewArtifact(text, opts) {
|
|
|
1603
1640
|
const foundCountsLine = body.map((l) => l.trim()).find((l) => COUNTS_LINE_SHAPE.test(l)) ?? null;
|
|
1604
1641
|
if (section.present) {
|
|
1605
1642
|
if (foundRateLine === null) {
|
|
1606
|
-
problems.push(reviewProblem(REVIEW_PROBLEM_KIND.RATE_LINE_ABSENT, null, null, `
|
|
1643
|
+
problems.push(reviewProblem(REVIEW_PROBLEM_KIND.RATE_LINE_ABSENT, null, null, `D-06-12: the section carries no non-verdict rate line. Expected the computed line ` +
|
|
1607
1644
|
`"${expectedRateLine}" — the denominator is the point, and a list of green rows without ` +
|
|
1608
1645
|
'it cannot tell a reader that a review is hollow.'));
|
|
1609
1646
|
}
|
|
1610
1647
|
else if (foundRateLine !== expectedRateLine) {
|
|
1611
|
-
problems.push(reviewProblem(REVIEW_PROBLEM_KIND.RATE_LINE_ARITHMETIC, null, null, `
|
|
1648
|
+
problems.push(reviewProblem(REVIEW_PROBLEM_KIND.RATE_LINE_ARITHMETIC, null, null, `D-06-12: the rate line reads "${safe(foundRateLine)}" while the rows support ` +
|
|
1612
1649
|
`"${expectedRateLine}". The arithmetic is computed from the rows and never narrated.`));
|
|
1613
1650
|
}
|
|
1614
1651
|
if (foundCountsLine === null) {
|
|
1615
|
-
problems.push(reviewProblem(REVIEW_PROBLEM_KIND.COUNTS_LINE_ABSENT, null, null, `
|
|
1652
|
+
problems.push(reviewProblem(REVIEW_PROBLEM_KIND.COUNTS_LINE_ABSENT, null, null, `D-06-13: the section carries no counts line. Expected "${expectedCountsLine}".`));
|
|
1616
1653
|
}
|
|
1617
1654
|
else if (foundCountsLine !== expectedCountsLine) {
|
|
1618
|
-
problems.push(reviewProblem(REVIEW_PROBLEM_KIND.COUNTS_LINE_ARITHMETIC, null, null, `
|
|
1655
|
+
problems.push(reviewProblem(REVIEW_PROBLEM_KIND.COUNTS_LINE_ARITHMETIC, null, null, `D-06-13: the counts line reads "${safe(foundCountsLine)}" while the rows support ` +
|
|
1619
1656
|
`"${expectedCountsLine}".`));
|
|
1620
1657
|
}
|
|
1621
1658
|
}
|
|
@@ -1628,10 +1665,7 @@ function validateReviewArtifact(text, opts) {
|
|
|
1628
1665
|
const expected = opts.expectedCoverageLine;
|
|
1629
1666
|
const inSection = (i) => section.present && i >= section.start && i < section.end;
|
|
1630
1667
|
let foundIdx = -1;
|
|
1631
|
-
const fencedLines = fencedLineMap(lines);
|
|
1632
1668
|
for (let i = 0; i < lines.length; i += 1) {
|
|
1633
|
-
if (fencedLines[i])
|
|
1634
|
-
continue;
|
|
1635
1669
|
if (COVERAGE_LINE_SHAPE.test(lines[i].trim())) {
|
|
1636
1670
|
foundIdx = i;
|
|
1637
1671
|
break;
|
|
@@ -1640,23 +1674,23 @@ function validateReviewArtifact(text, opts) {
|
|
|
1640
1674
|
const found = foundIdx >= 0 ? lines[foundIdx].trim() : null;
|
|
1641
1675
|
if (typeof expected === 'string' && expected.length > 0) {
|
|
1642
1676
|
if (found === null) {
|
|
1643
|
-
problems.push(reviewProblem(REVIEW_PROBLEM_KIND.COVERAGE_LINE_ABSENT, null, null, `
|
|
1677
|
+
problems.push(reviewProblem(REVIEW_PROBLEM_KIND.COVERAGE_LINE_ABSENT, null, null, `D-10-03: the payload carried the mechanical coverage line "${expected}" and the ` +
|
|
1644
1678
|
'artifact reproduces it nowhere. That line is how a reader learns how much of the ' +
|
|
1645
1679
|
'vault was mechanically checked; without it a page of judged rows cannot say ' +
|
|
1646
1680
|
'whether the mechanical tier ran at all.'));
|
|
1647
1681
|
}
|
|
1648
1682
|
else if (found !== expected) {
|
|
1649
|
-
problems.push(reviewProblem(REVIEW_PROBLEM_KIND.COVERAGE_LINE_MISMATCH, null, null, `
|
|
1683
|
+
problems.push(reviewProblem(REVIEW_PROBLEM_KIND.COVERAGE_LINE_MISMATCH, null, null, `D-10-03: the coverage line reads "${safe(found)}" where the engine computed ` +
|
|
1650
1684
|
`"${expected}". It is the engine's sentence, reproduced verbatim and never restated.`));
|
|
1651
1685
|
}
|
|
1652
1686
|
else if (inSection(foundIdx)) {
|
|
1653
|
-
problems.push(reviewProblem(REVIEW_PROBLEM_KIND.COVERAGE_LINE_MISPLACED, null, null, '
|
|
1687
|
+
problems.push(reviewProblem(REVIEW_PROBLEM_KIND.COVERAGE_LINE_MISPLACED, null, null, 'D-10-03: the coverage line sits inside the verdicts section, where a second bare ' +
|
|
1654
1688
|
'shaped line makes the section contradict itself — its figures count a different ' +
|
|
1655
1689
|
'population from the rate line above them. It belongs under "## Range".'));
|
|
1656
1690
|
}
|
|
1657
1691
|
}
|
|
1658
1692
|
else if (expected === null && found !== null) {
|
|
1659
|
-
problems.push(reviewProblem(REVIEW_PROBLEM_KIND.COVERAGE_LINE_UNEXPECTED, null, null, `
|
|
1693
|
+
problems.push(reviewProblem(REVIEW_PROBLEM_KIND.COVERAGE_LINE_UNEXPECTED, null, null, `D-10-03: the artifact carries "${safe(found)}" while the payload carried no coverage ` +
|
|
1660
1694
|
'line at all. Its absence means the inputs were never measured, so this figure ' +
|
|
1661
1695
|
'states a measurement nobody took.'));
|
|
1662
1696
|
}
|
|
@@ -1686,7 +1720,7 @@ function validateReviewArtifact(text, opts) {
|
|
|
1686
1720
|
'marks it as reference data, which is the escape the fencing exists to prevent.'));
|
|
1687
1721
|
}
|
|
1688
1722
|
for (const finding of scanForbiddenScoreTokens(body.join('\n'))) {
|
|
1689
|
-
problems.push(reviewProblem(finding.kind, null, section.present ? section.start + finding.line : finding.line, `
|
|
1723
|
+
problems.push(reviewProblem(finding.kind, null, section.present ? section.start + finding.line : finding.line, `D-06-13 forbids a single-number roll-up anywhere in this section; "${finding.match}" is one. ` +
|
|
1690
1724
|
'Counts by verdict stay sortable and trackable across phases without inviting a ' +
|
|
1691
1725
|
'measured-sounding claim about a judgement.'));
|
|
1692
1726
|
}
|
|
@@ -1815,7 +1849,7 @@ function buildReviewPayload(opts) {
|
|
|
1815
1849
|
const range = resolveReviewRange({
|
|
1816
1850
|
cwd: opts.cwd,
|
|
1817
1851
|
phaseToken: opts?.phaseToken ?? null,
|
|
1818
|
-
//
|
|
1852
|
+
// 260915-ik2: the check router already supplies `phaseDir: phase.rel`, so
|
|
1819
1853
|
// forwarding it is all the shared phase-scoped resolution needs.
|
|
1820
1854
|
phaseDir: typeof opts?.phaseDir === 'string' && opts.phaseDir !== '' ? opts.phaseDir : null,
|
|
1821
1855
|
...(typeof opts?.exec === 'function' ? { exec: opts.exec } : {}),
|
|
@@ -1825,26 +1859,13 @@ function buildReviewPayload(opts) {
|
|
|
1825
1859
|
'applicability judgement below is scoped to exactly that set.');
|
|
1826
1860
|
for (const note of range.notes)
|
|
1827
1861
|
statements.push(`Range note: ${note}`);
|
|
1828
|
-
// ── The vault verdict owner's answer, stated when it is not `loaded`: the
|
|
1829
|
-
// practices below were read from the cache as it stands, and a reader must
|
|
1830
|
-
// know whether the load gate considers that cache the client's loaded vault.
|
|
1831
|
-
const ownerVerdict = opts?.vaultVerdict ?? null;
|
|
1832
|
-
const vault = ownerVerdict !== null && typeof ownerVerdict === 'object' && typeof ownerVerdict.verdict === 'string'
|
|
1833
|
-
? { verdict: safe(ownerVerdict.verdict), reason: typeof ownerVerdict.reason === 'string' ? safe(ownerVerdict.reason) : '' }
|
|
1834
|
-
: null;
|
|
1835
|
-
if (vault !== null && vault.verdict !== 'loaded') {
|
|
1836
|
-
statements.push(`The vault verdict owner reports this project's vault as ${vault.verdict}` +
|
|
1837
|
-
(vault.reason === '' ? '' : ` (${vault.reason})`) +
|
|
1838
|
-
': the practices reviewed below were read from the local cache as it stands, which the vault load ' +
|
|
1839
|
-
"gate does not treat as the client's loaded vault.");
|
|
1840
|
-
}
|
|
1841
1862
|
// ── D-06-15: an absent trace does not refuse; it says so and RECONSTRUCTS.
|
|
1842
1863
|
if (!tracePresent) {
|
|
1843
1864
|
statements.push('No practice trace was found for this phase' +
|
|
1844
1865
|
(phaseDir === null ? '' : ` in ${phaseDir}`) +
|
|
1845
1866
|
'. This review therefore took the RECONSTRUCT arm: the applicable practice set is rebuilt ' +
|
|
1846
1867
|
"from this phase's real diff and the vault's own practice list rather than from a ledger. " +
|
|
1847
|
-
'Any completed phase is eligible for review and a missing trace is itself a finding, ' +
|
|
1868
|
+
'Any completed phase is eligible for review (D-06-14) and a missing trace is itself a finding, ' +
|
|
1848
1869
|
'so it is recorded here rather than used as a reason to refuse.');
|
|
1849
1870
|
}
|
|
1850
1871
|
// ── G1: a trace that PARSES but records a failed vault load. This is not the
|
|
@@ -1859,9 +1880,9 @@ function buildReviewPayload(opts) {
|
|
|
1859
1880
|
'A practice trace WAS found for this phase' +
|
|
1860
1881
|
(phaseDir === null ? '' : ` in ${phaseDir}`) +
|
|
1861
1882
|
`, and it records a FAILURE — ${traceReason ?? 'no reason recorded'}. A degraded block is a valid ` +
|
|
1862
|
-
'record of a failure — it says
|
|
1863
|
-
'proves nothing about which practices governed that work and may not stand in for a loaded trace
|
|
1864
|
-
'This review therefore took the RECONSTRUCT arm: the applicable practice set is rebuilt ' +
|
|
1883
|
+
'record of a failure — it says no trusted vault load existed when the work was planned — so it ' +
|
|
1884
|
+
'proves nothing about which practices governed that work and may not stand in for a loaded trace ' +
|
|
1885
|
+
'(D-06-15). This review therefore took the RECONSTRUCT arm: the applicable practice set is rebuilt ' +
|
|
1865
1886
|
"from this phase's real diff and the vault's own practice list, and this trace's own ledger and " +
|
|
1866
1887
|
'practice universe are deliberately not consulted.');
|
|
1867
1888
|
}
|
|
@@ -1882,7 +1903,7 @@ function buildReviewPayload(opts) {
|
|
|
1882
1903
|
`${said(o.who)} at ${said(o.when)} with the stated reason "${said(o.reason)}", suppressing the ` +
|
|
1883
1904
|
`verdict ${said(o.suppressedReason)}. The work this phase records was therefore planned or ` +
|
|
1884
1905
|
'executed under a deliberate, attributed bypass of that gate; this review reports the fact ' +
|
|
1885
|
-
'and does not judge it.');
|
|
1906
|
+
'(D-08-11) and does not judge it.');
|
|
1886
1907
|
}
|
|
1887
1908
|
// ── The input set: this arm's universe ∩ NO_CHECK, harvested through the
|
|
1888
1909
|
// shipped ladder. On the reconstruct arm the universe is the vault's own
|
|
@@ -2019,7 +2040,6 @@ function buildReviewPayload(opts) {
|
|
|
2019
2040
|
// the latter and never re-derives it from the former (06-12, T-6-48).
|
|
2020
2041
|
arm: traceLoaded ? 'traced' : 'reconstruct',
|
|
2021
2042
|
phaseDir,
|
|
2022
|
-
vault,
|
|
2023
2043
|
statements,
|
|
2024
2044
|
trace: {
|
|
2025
2045
|
present: tracePresent,
|
|
@@ -2044,278 +2064,6 @@ function buildReviewPayload(opts) {
|
|
|
2044
2064
|
notes,
|
|
2045
2065
|
};
|
|
2046
2066
|
}
|
|
2047
|
-
// ─── The artifact the agent fills in, and the lines only code writes ─────────
|
|
2048
|
-
/**
|
|
2049
|
-
* The verdict cell of a remainder row until the agent judges it. Deliberately
|
|
2050
|
-
* outside the locked vocabulary: a row left unjudged is reported by the
|
|
2051
|
-
* validator as `row-unknown-verdict`, never counted as any verdict.
|
|
2052
|
-
*/
|
|
2053
|
-
const REVIEW_UNJUDGED_VERDICT = 'TODO';
|
|
2054
|
-
/** The `## Range` heading the coverage line is stamped under. */
|
|
2055
|
-
const RANGE_SECTION_HEADING = '## Range';
|
|
2056
|
-
/** A table cell: one line, no column separator, never empty. */
|
|
2057
|
-
function tableCell(v, empty = 'none.') {
|
|
2058
|
-
const s = (typeof v === 'string' ? v : '').replace(/[\r\n]+/g, ' ').replace(/\|/g, '¦').trim();
|
|
2059
|
-
return s === '' ? empty : s;
|
|
2060
|
-
}
|
|
2061
|
-
/**
|
|
2062
|
-
* The gaps the payload recorded, one row each, in a fixed order. Every row is
|
|
2063
|
-
* written on every run; a row with nothing to report says `none.`, so "nothing
|
|
2064
|
-
* to report" and "not looked at" never look alike.
|
|
2065
|
-
*/
|
|
2066
|
-
function reviewGapRows(p) {
|
|
2067
|
-
const notDerived = (arm) => (arm.notDerived ? arm.notDerived.statement : '');
|
|
2068
|
-
const vaultGround = p.naNoFile.notDerived?.ground ?? '';
|
|
2069
|
-
const vaultGap = [
|
|
2070
|
-
NOT_DERIVED_GROUND.VAULT_STATE_UNKNOWN,
|
|
2071
|
-
NOT_DERIVED_GROUND.VAULT_NEVER_FETCHED,
|
|
2072
|
-
NOT_DERIVED_GROUND.VAULT_FETCH_MISS,
|
|
2073
|
-
NOT_DERIVED_GROUND.VAULT_UNREADABLE,
|
|
2074
|
-
].some((g) => g === vaultGround)
|
|
2075
|
-
? notDerived(p.naNoFile)
|
|
2076
|
-
: '';
|
|
2077
|
-
const traceGap = !p.trace.present
|
|
2078
|
-
? 'absent — no practice trace was found, so this review took the reconstruct arm'
|
|
2079
|
-
: p.trace.loaded
|
|
2080
|
-
? ''
|
|
2081
|
-
: `degraded — ${p.trace.reason ?? 'no reason recorded'}`;
|
|
2082
|
-
const said = (v) => v ?? '(not recorded)';
|
|
2083
|
-
const overrides = p.trace.overrides
|
|
2084
|
-
.map((o) => `${said(o.gate)} overridden by ${said(o.who)} at ${said(o.when)} in ${said(o.source)}: "${said(o.reason)}" (suppressed ${said(o.suppressedReason)})`)
|
|
2085
|
-
.join('; ');
|
|
2086
|
-
const rangeGap = p.range.outcome === auditMod.AUDIT_RANGE_OUTCOME.RANGE_UNRESOLVABLE
|
|
2087
|
-
? `${p.range.outcome}: ${REVIEW_RANGE_UNRESOLVABLE_REMEDY}`
|
|
2088
|
-
: '';
|
|
2089
|
-
const skipped = p.inputSet.exclusions
|
|
2090
|
-
.filter((e) => e.reason === AUDIT_FAIL_REASON.PRACTICE_UNLOADABLE)
|
|
2091
|
-
.map((e) => quotableId(e.id))
|
|
2092
|
-
.join(', ');
|
|
2093
|
-
const unmeasured = unmeasuredGrounds({
|
|
2094
|
-
filesConsidered: p.range.filesConsidered,
|
|
2095
|
-
inputSet: p.inputSet,
|
|
2096
|
-
naUniverse: p.naUniverse,
|
|
2097
|
-
naNoFile: p.naNoFile,
|
|
2098
|
-
});
|
|
2099
|
-
const refused = p.refusesCleanRender
|
|
2100
|
-
? `no clean review may be rendered: ${p.range.filesConsidered === 0 ? 'zero files were considered' : `inputs not measured (${unmeasured.join(', ')})`}`
|
|
2101
|
-
: '';
|
|
2102
|
-
return [
|
|
2103
|
-
['Vault absent, never fetched, missed or unreadable', vaultGap],
|
|
2104
|
-
['Practice universe not recorded at the pin', notDerived(p.naUniverse)],
|
|
2105
|
-
['Trace absent or degraded', traceGap],
|
|
2106
|
-
['Gate override recorded (who, when, reason)', overrides],
|
|
2107
|
-
['Range refused or unresolvable', rangeGap],
|
|
2108
|
-
['Empty diff — no file considered', p.range.filesConsidered === 0 ? 'zero files were considered' : ''],
|
|
2109
|
-
['Practices skipped or rejected before matching', skipped],
|
|
2110
|
-
['Clean review refused', refused],
|
|
2111
|
-
];
|
|
2112
|
-
}
|
|
2113
|
-
/**
|
|
2114
|
-
* Render the `NN-VAULT-REVIEW.md` the agent fills in, from the payload alone.
|
|
2115
|
-
*
|
|
2116
|
-
* Everything computed is written here, by code: provenance, the range, every
|
|
2117
|
-
* derived row with its reason and arm label, every gap row, the payload's
|
|
2118
|
-
* statements, the fixed limits, and — through `stampReviewLines` — the rate,
|
|
2119
|
-
* counts and coverage lines. Each remainder practice gets one row whose verdict
|
|
2120
|
-
* reads `TODO` and whose reason is empty: those two cells are the agent's whole
|
|
2121
|
-
* writable surface. The remainder's practice text follows, last, inside the
|
|
2122
|
-
* fences the shipped renderer already put around it.
|
|
2123
|
-
*
|
|
2124
|
-
* Pure: payload and date in, text out.
|
|
2125
|
-
*/
|
|
2126
|
-
function renderReviewScaffold(payload, meta = {}) {
|
|
2127
|
-
const p = payload;
|
|
2128
|
-
const token = typeof meta.phaseToken === 'string' && meta.phaseToken !== '' ? safe(meta.phaseToken) : '?';
|
|
2129
|
-
const date = typeof meta.date === 'string' && /^\d{4}-\d{2}-\d{2}$/.test(meta.date) ? meta.date : '';
|
|
2130
|
-
const traceCell = !p.trace.present
|
|
2131
|
-
? 'absent'
|
|
2132
|
-
: p.trace.loaded
|
|
2133
|
-
? 'present and loaded'
|
|
2134
|
-
: `degraded — ${p.trace.reason ?? 'no reason recorded'}`;
|
|
2135
|
-
const advisories = p.inputSet.advisories.map((a) => a.detail).join(' ');
|
|
2136
|
-
const rows = [];
|
|
2137
|
-
for (const id of p.partition.remainder) {
|
|
2138
|
-
rows.push(`| \`${tableCell(quotableId(id))}\` | ${REVIEW_UNJUDGED_VERDICT} | | agent-judged |`);
|
|
2139
|
-
}
|
|
2140
|
-
for (const r of [...p.partition.naUniverse, ...p.partition.naNoFile]) {
|
|
2141
|
-
rows.push(`| \`${tableCell(quotableId(r.id))}\` | ${r.verdict} | ${tableCell(r.reason)} | derived (${tableCell(r.naDerivedReason ?? '')}) |`);
|
|
2142
|
-
}
|
|
2143
|
-
const lines = [
|
|
2144
|
-
'---',
|
|
2145
|
-
`phase: "${token}"`,
|
|
2146
|
-
'artifact: vault-review',
|
|
2147
|
-
`reviewed: ${date === '' ? 'unknown' : date}`,
|
|
2148
|
-
`arm: ${p.arm}`,
|
|
2149
|
-
'---',
|
|
2150
|
-
'',
|
|
2151
|
-
`# Phase ${token} — Vault Practice Review`,
|
|
2152
|
-
'',
|
|
2153
|
-
'> This review reports and never gates: no verdict here changes a phase status, a score, a gap or a',
|
|
2154
|
-
'> ship. Verdicts are agent-judged, not measured, and two runs may differ. Counts by verdict only.',
|
|
2155
|
-
'',
|
|
2156
|
-
'## Provenance',
|
|
2157
|
-
'',
|
|
2158
|
-
'| Field | Value |',
|
|
2159
|
-
'| --- | --- |',
|
|
2160
|
-
`| Phase directory | ${tableCell(p.phaseDir ?? '')} |`,
|
|
2161
|
-
`| Arm | ${p.arm} |`,
|
|
2162
|
-
`| Trace | ${tableCell(traceCell)} |`,
|
|
2163
|
-
`| Trace sources | ${tableCell(p.trace.sources.join(', '))} |`,
|
|
2164
|
-
`| Vault verdict | ${tableCell(p.vault ? `${p.vault.verdict}${p.vault.reason ? ` (${p.vault.reason})` : ''}` : '', 'not supplied')} |`,
|
|
2165
|
-
`| Pin advisory | ${tableCell(advisories)} |`,
|
|
2166
|
-
'',
|
|
2167
|
-
RANGE_SECTION_HEADING,
|
|
2168
|
-
'',
|
|
2169
|
-
'| Field | Value |',
|
|
2170
|
-
'| --- | --- |',
|
|
2171
|
-
`| \`range\` | \`${tableCell(p.range.range)}\` |`,
|
|
2172
|
-
`| \`rangeSource\` | \`${tableCell(p.range.rangeSource)}\` |`,
|
|
2173
|
-
`| \`filesConsidered\` | ${p.range.filesConsidered} |`,
|
|
2174
|
-
`| \`outcome\` | \`${tableCell(p.range.outcome)}\` |`,
|
|
2175
|
-
'',
|
|
2176
|
-
REVIEW_SECTION_HEADING,
|
|
2177
|
-
'',
|
|
2178
|
-
'| Practice | Verdict | Reason / evidence | Source |',
|
|
2179
|
-
'| --- | --- | --- | --- |',
|
|
2180
|
-
...rows,
|
|
2181
|
-
'',
|
|
2182
|
-
'## Not Derived — the gaps',
|
|
2183
|
-
'',
|
|
2184
|
-
'| Gap | Statement |',
|
|
2185
|
-
'| --- | --- |',
|
|
2186
|
-
...reviewGapRows(p).map(([gap, statement]) => `| ${gap} | ${tableCell(statement)} |`),
|
|
2187
|
-
'',
|
|
2188
|
-
'## Derivation Statements',
|
|
2189
|
-
'',
|
|
2190
|
-
...(p.statements.length + p.notes.length === 0
|
|
2191
|
-
? ['none.']
|
|
2192
|
-
: [...p.statements.map((st) => `- ${st}`), ...p.notes.map((n) => `- Note: ${n}`)]),
|
|
2193
|
-
'',
|
|
2194
|
-
'## Limits of This Review',
|
|
2195
|
-
'',
|
|
2196
|
-
'- Nothing in this artifact gates anything. A `FAIL` is a report to a human, not a halt.',
|
|
2197
|
-
'- A verdict is one agent run\'s judgement; reproducibility is not claimed.',
|
|
2198
|
-
'- The non-verdict rate is reported, not judged: there is no threshold at which a review is declared',
|
|
2199
|
-
' low-confidence.',
|
|
2200
|
-
'- A practice absent from the register was not in the input set; the gaps table says why. Absence',
|
|
2201
|
-
' is not a statement that it was followed.',
|
|
2202
|
-
'',
|
|
2203
|
-
'## Practice Text (client reference data)',
|
|
2204
|
-
'',
|
|
2205
|
-
...(p.practiceEntries.length === 0 ? ['none.'] : p.practiceEntries.flatMap((e) => [e, ''])),
|
|
2206
|
-
'',
|
|
2207
|
-
];
|
|
2208
|
-
return stampReviewLines(lines.join('\n'), { coverageLine: p.mechanicalCoverageLine ?? null }).text;
|
|
2209
|
-
}
|
|
2210
|
-
/** Index of the first unfenced line equal to `heading`, or -1. */
|
|
2211
|
-
function unfencedHeadingIndex(lines, heading) {
|
|
2212
|
-
const fenced = fencedLineMap(lines);
|
|
2213
|
-
return lines.findIndex((l, i) => !fenced[i] && l.trim() === heading);
|
|
2214
|
-
}
|
|
2215
|
-
/**
|
|
2216
|
-
* Write the three computed lines into an artifact — the ONLY writer of them.
|
|
2217
|
-
*
|
|
2218
|
-
* Every unfenced rate- or counts-shaped line inside the verdicts section, and
|
|
2219
|
-
* every unfenced coverage-shaped line anywhere, is removed; then the rate and
|
|
2220
|
-
* counts lines the validator computes from the section's own rows are written
|
|
2221
|
-
* directly under the section heading, and the payload's coverage line (when it
|
|
2222
|
-
* carries one) at the end of `## Range` — or above the verdicts section when
|
|
2223
|
-
* there is no `## Range`. A narrated figure therefore cannot survive, and the
|
|
2224
|
-
* two count populations (the payload's derived rows and the register's rows)
|
|
2225
|
-
* cannot be confused by anyone: the register's lines are always the register's.
|
|
2226
|
-
*
|
|
2227
|
-
* Fenced client prose is never touched. Without the verdicts heading nothing is
|
|
2228
|
-
* stamped and the validator reports the absent section.
|
|
2229
|
-
*/
|
|
2230
|
-
function stampReviewLines(text, opts = {}) {
|
|
2231
|
-
if (typeof text !== 'string')
|
|
2232
|
-
return { text: '', stamped: false };
|
|
2233
|
-
const src = text.split(/\r?\n/);
|
|
2234
|
-
const section = locateReviewSection(src);
|
|
2235
|
-
if (!section.present)
|
|
2236
|
-
return { text, stamped: false };
|
|
2237
|
-
const fenced = fencedLineMap(src);
|
|
2238
|
-
const kept = [];
|
|
2239
|
-
for (let i = 0; i < src.length; i += 1) {
|
|
2240
|
-
const t = src[i].trim();
|
|
2241
|
-
if (!fenced[i]) {
|
|
2242
|
-
if (COVERAGE_LINE_SHAPE.test(t))
|
|
2243
|
-
continue;
|
|
2244
|
-
if (i >= section.start && i < section.end && (RATE_LINE_SHAPE.test(t) || COUNTS_LINE_SHAPE.test(t)))
|
|
2245
|
-
continue;
|
|
2246
|
-
}
|
|
2247
|
-
kept.push(src[i]);
|
|
2248
|
-
}
|
|
2249
|
-
const probe = validateReviewArtifact(kept.join('\n'));
|
|
2250
|
-
const head = unfencedHeadingIndex(kept, REVIEW_SECTION_HEADING);
|
|
2251
|
-
let after = head + 1;
|
|
2252
|
-
while (after < kept.length && kept[after].trim() === '')
|
|
2253
|
-
after += 1;
|
|
2254
|
-
kept.splice(head + 1, after - (head + 1), '', probe.expectedRateLine, probe.expectedCountsLine, '');
|
|
2255
|
-
const coverage = typeof opts.coverageLine === 'string' ? opts.coverageLine.trim() : '';
|
|
2256
|
-
if (coverage !== '' && COVERAGE_LINE_SHAPE.test(coverage)) {
|
|
2257
|
-
const fencedNow = fencedLineMap(kept);
|
|
2258
|
-
const range = unfencedHeadingIndex(kept, RANGE_SECTION_HEADING);
|
|
2259
|
-
let at = unfencedHeadingIndex(kept, REVIEW_SECTION_HEADING);
|
|
2260
|
-
if (range !== -1) {
|
|
2261
|
-
at = kept.length;
|
|
2262
|
-
for (let i = range + 1; i < kept.length; i += 1) {
|
|
2263
|
-
if (!fencedNow[i] && (kept[i].startsWith('## ') || kept[i].trim() === '---')) {
|
|
2264
|
-
at = i;
|
|
2265
|
-
break;
|
|
2266
|
-
}
|
|
2267
|
-
}
|
|
2268
|
-
while (at > range + 1 && kept[at - 1].trim() === '')
|
|
2269
|
-
at -= 1;
|
|
2270
|
-
kept.splice(at, 0, '', coverage);
|
|
2271
|
-
}
|
|
2272
|
-
else {
|
|
2273
|
-
kept.splice(at, 0, coverage, '');
|
|
2274
|
-
}
|
|
2275
|
-
}
|
|
2276
|
-
// Removing a line leaves its blank neighbours behind; collapse every unfenced
|
|
2277
|
-
// run of blank lines to one, so stamping is idempotent.
|
|
2278
|
-
const fencedOut = fencedLineMap(kept);
|
|
2279
|
-
const out = kept.filter((l, i) => fencedOut[i] || l.trim() !== '' || i === 0 || kept[i - 1].trim() !== '');
|
|
2280
|
-
return { text: out.join('\n'), stamped: true };
|
|
2281
|
-
}
|
|
2282
|
-
/**
|
|
2283
|
-
* The register's rows against the population the payload derived: every
|
|
2284
|
-
* practice of `partition.population` (remainder plus both derived arms) has
|
|
2285
|
-
* exactly one row, and no row names anything else. Deleting a row is how a
|
|
2286
|
-
* hard practice disappears from a review; this makes that a reported problem.
|
|
2287
|
-
*/
|
|
2288
|
-
function compareReviewPopulation(rows, partition) {
|
|
2289
|
-
const expected = uniqueInOrder([
|
|
2290
|
-
...partition.remainder,
|
|
2291
|
-
...partition.naUniverse.map((r) => r.id),
|
|
2292
|
-
...partition.naNoFile.map((r) => r.id),
|
|
2293
|
-
]);
|
|
2294
|
-
const want = new Set(expected.map((id) => quotableId(id)));
|
|
2295
|
-
const seen = new Map();
|
|
2296
|
-
for (const r of rows) {
|
|
2297
|
-
if (typeof r?.id !== 'string' || r.id === '')
|
|
2298
|
-
continue;
|
|
2299
|
-
seen.set(r.id, (seen.get(r.id) ?? 0) + 1);
|
|
2300
|
-
}
|
|
2301
|
-
const problems = [];
|
|
2302
|
-
for (const id of want) {
|
|
2303
|
-
if (!seen.has(id)) {
|
|
2304
|
-
problems.push(reviewProblem(REVIEW_PROBLEM_KIND.ROW_POPULATION_MISSING, id, null, `"${id}" is in the population this review derived and has no row: a practice handed to the review ` +
|
|
2305
|
-
'is judged or reproduced, never dropped.'));
|
|
2306
|
-
}
|
|
2307
|
-
}
|
|
2308
|
-
for (const [id, n] of seen) {
|
|
2309
|
-
if (!want.has(id)) {
|
|
2310
|
-
problems.push(reviewProblem(REVIEW_PROBLEM_KIND.ROW_POPULATION_UNEXPECTED, id, null, `"${safe(id)}" has a row but is not in the population this review derived, so its verdict judges ` +
|
|
2311
|
-
'something the review was not handed.'));
|
|
2312
|
-
}
|
|
2313
|
-
if (n > 1) {
|
|
2314
|
-
problems.push(reviewProblem(REVIEW_PROBLEM_KIND.ROW_DUPLICATE, id, null, `"${safe(id)}" carries ${n} rows; each practice carries exactly one verdict.`));
|
|
2315
|
-
}
|
|
2316
|
-
}
|
|
2317
|
-
return problems;
|
|
2318
|
-
}
|
|
2319
2067
|
module.exports = {
|
|
2320
2068
|
// The three frozen vocabularies. These ARE the contract 06-04/06-06/06-08 and
|
|
2321
2069
|
// 06-10 serialize; nothing downstream re-derives a token.
|
|
@@ -2351,9 +2099,4 @@ module.exports = {
|
|
|
2351
2099
|
// `cmdVaultAudit` states about its own leaf: a second composition is a second
|
|
2352
2100
|
// place the report can disagree with the derivation it claims to report.
|
|
2353
2101
|
buildReviewPayload,
|
|
2354
|
-
// The artifact the agent fills in, and the three lines only code writes.
|
|
2355
|
-
REVIEW_UNJUDGED_VERDICT,
|
|
2356
|
-
renderReviewScaffold,
|
|
2357
|
-
stampReviewLines,
|
|
2358
|
-
compareReviewPopulation,
|
|
2359
2102
|
};
|