@lannguyensi/harness 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/CHANGELOG.md +1245 -0
- package/dist/cli/adopt/derive.d.ts +16 -7
- package/dist/cli/adopt/derive.js +19 -2
- package/dist/cli/adopt/derive.js.map +1 -1
- package/dist/cli/adopt/index.js +18 -3
- package/dist/cli/adopt/index.js.map +1 -1
- package/dist/cli/apply/apply.d.ts +1 -0
- package/dist/cli/apply/apply.js +62 -6
- package/dist/cli/apply/apply.js.map +1 -1
- package/dist/cli/apply/generate-codex-config.js +5 -6
- package/dist/cli/apply/generate-codex-config.js.map +1 -1
- package/dist/cli/apply/generate-opencode-config.d.ts +58 -0
- package/dist/cli/apply/generate-opencode-config.js +210 -0
- package/dist/cli/apply/generate-opencode-config.js.map +1 -0
- package/dist/cli/apply/generate-settings.d.ts +90 -32
- package/dist/cli/apply/generate-settings.js +133 -10
- package/dist/cli/apply/generate-settings.js.map +1 -1
- package/dist/cli/apply/index.d.ts +2 -1
- package/dist/cli/apply/index.js +2 -1
- package/dist/cli/apply/index.js.map +1 -1
- package/dist/cli/apply/next-steps.d.ts +20 -0
- package/dist/cli/apply/next-steps.js +60 -0
- package/dist/cli/apply/next-steps.js.map +1 -1
- package/dist/cli/approve/stdin-report.d.ts +15 -0
- package/dist/cli/approve/stdin-report.js +1 -1
- package/dist/cli/approve/stdin-report.js.map +1 -1
- package/dist/cli/approve/understanding.d.ts +75 -0
- package/dist/cli/approve/understanding.js +150 -2
- package/dist/cli/approve/understanding.js.map +1 -1
- package/dist/cli/audit.d.ts +1 -1
- package/dist/cli/audit.js +3 -2
- package/dist/cli/audit.js.map +1 -1
- package/dist/cli/doctor/claude-mcp.d.ts +19 -1
- package/dist/cli/doctor/claude-mcp.js +36 -10
- package/dist/cli/doctor/claude-mcp.js.map +1 -1
- package/dist/cli/doctor/codex.d.ts +3 -1
- package/dist/cli/doctor/codex.js +6 -9
- package/dist/cli/doctor/codex.js.map +1 -1
- package/dist/cli/doctor/format.js +94 -8
- package/dist/cli/doctor/format.js.map +1 -1
- package/dist/cli/doctor/index.d.ts +23 -0
- package/dist/cli/doctor/index.js +106 -2
- package/dist/cli/doctor/index.js.map +1 -1
- package/dist/cli/doctor/opencode.d.ts +24 -0
- package/dist/cli/doctor/opencode.js +107 -0
- package/dist/cli/doctor/opencode.js.map +1 -0
- package/dist/cli/doctor/target-checks.d.ts +11 -0
- package/dist/cli/doctor/target-checks.js +25 -0
- package/dist/cli/doctor/target-checks.js.map +1 -0
- package/dist/cli/doctor/toolchain-parity.d.ts +63 -0
- package/dist/cli/doctor/toolchain-parity.js +154 -0
- package/dist/cli/doctor/toolchain-parity.js.map +1 -0
- package/dist/cli/doctor/types.d.ts +85 -1
- package/dist/cli/doctor/types.js +1 -1
- package/dist/cli/doctor/types.js.map +1 -1
- package/dist/cli/doctor/understanding-mode-env.d.ts +39 -0
- package/dist/cli/doctor/understanding-mode-env.js +83 -0
- package/dist/cli/doctor/understanding-mode-env.js.map +1 -0
- package/dist/cli/dry-run.js +32 -17
- package/dist/cli/dry-run.js.map +1 -1
- package/dist/cli/explain.d.ts +1 -1
- package/dist/cli/explain.js +1 -1
- package/dist/cli/explain.js.map +1 -1
- package/dist/cli/index.js +64 -5
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/init/composer.js +36 -13
- package/dist/cli/init/composer.js.map +1 -1
- package/dist/cli/init/interactive.d.ts +7 -4
- package/dist/cli/init/interactive.js +79 -27
- package/dist/cli/init/interactive.js.map +1 -1
- package/dist/cli/init/profiles.d.ts +2 -2
- package/dist/cli/init/profiles.js +32 -1
- package/dist/cli/init/profiles.js.map +1 -1
- package/dist/cli/init/templates.d.ts +2 -1
- package/dist/cli/init/templates.js +130 -20
- package/dist/cli/init/templates.js.map +1 -1
- package/dist/cli/pack/approve-escape.js +69 -8
- package/dist/cli/pack/approve-escape.js.map +1 -1
- package/dist/cli/pack/hook-branch-protection.js +1 -1
- package/dist/cli/pack/hook-branch-protection.js.map +1 -1
- package/dist/cli/pack/hook-codex-pre-tool-use.js +27 -1
- package/dist/cli/pack/hook-codex-pre-tool-use.js.map +1 -1
- package/dist/cli/pack/hook-post-merge-gate.js +1 -1
- package/dist/cli/pack/hook-post-merge-gate.js.map +1 -1
- package/dist/cli/pack/hook-pre-tool-use.js +91 -4
- package/dist/cli/pack/hook-pre-tool-use.js.map +1 -1
- package/dist/cli/pack/hook-solution-acceptance.js +19 -2
- package/dist/cli/pack/hook-solution-acceptance.js.map +1 -1
- package/dist/cli/pack/understanding-report-schema-hint.d.ts +13 -0
- package/dist/cli/pack/understanding-report-schema-hint.js +52 -3
- package/dist/cli/pack/understanding-report-schema-hint.js.map +1 -1
- package/dist/cli/pause/index.js +1 -1
- package/dist/cli/pause/index.js.map +1 -1
- package/dist/cli/policy/intercept.d.ts +148 -0
- package/dist/cli/policy/intercept.js +379 -48
- package/dist/cli/policy/intercept.js.map +1 -1
- package/dist/cli/session-start/index.d.ts +12 -3
- package/dist/cli/session-start/index.js +67 -11
- package/dist/cli/session-start/index.js.map +1 -1
- package/dist/cli/session-start/stale-base-check.d.ts +71 -0
- package/dist/cli/session-start/stale-base-check.js +379 -0
- package/dist/cli/session-start/stale-base-check.js.map +1 -0
- package/dist/cli/session-start/toolchain-parity.d.ts +142 -8
- package/dist/cli/session-start/toolchain-parity.js +244 -36
- package/dist/cli/session-start/toolchain-parity.js.map +1 -1
- package/dist/cli/smoke/assertions.d.ts +9 -2
- package/dist/cli/smoke/assertions.js +9 -2
- package/dist/cli/smoke/assertions.js.map +1 -1
- package/dist/cli/uninstall/index.js +18 -2
- package/dist/cli/uninstall/index.js.map +1 -1
- package/dist/cli/validate/checks.d.ts +3 -1
- package/dist/cli/validate/checks.js +298 -3
- package/dist/cli/validate/checks.js.map +1 -1
- package/dist/io/claude-mcp.d.ts +122 -1
- package/dist/io/claude-mcp.js +214 -12
- package/dist/io/claude-mcp.js.map +1 -1
- package/dist/io/duration.js.map +1 -0
- package/dist/io/expand-home.js.map +1 -0
- package/dist/io/extract.js.map +1 -0
- package/dist/io/harness-lock.js +1 -1
- package/dist/io/harness-lock.js.map +1 -1
- package/dist/{runtime → io}/ledger-record.d.ts +1 -1
- package/dist/io/ledger-record.js.map +1 -0
- package/dist/policies/index.d.ts +2 -2
- package/dist/policies/index.js +2 -2
- package/dist/policies/index.js.map +1 -1
- package/dist/policies/ledger-client.js +10 -6
- package/dist/policies/ledger-client.js.map +1 -1
- package/dist/policies/producers.js +1 -1
- package/dist/policies/producers.js.map +1 -1
- package/dist/policies/requires.js +2 -2
- package/dist/policies/requires.js.map +1 -1
- package/dist/policy-packs/builtin/branch-protection.js +28 -3
- package/dist/policy-packs/builtin/branch-protection.js.map +1 -1
- package/dist/policy-packs/builtin/post-merge-gate.js +45 -9
- package/dist/policy-packs/builtin/post-merge-gate.js.map +1 -1
- package/dist/policy-packs/builtin/solution-acceptance-runtime.d.ts +139 -8
- package/dist/policy-packs/builtin/solution-acceptance-runtime.js +246 -12
- package/dist/policy-packs/builtin/solution-acceptance-runtime.js.map +1 -1
- package/dist/policy-packs/builtin/solution-acceptance.js +17 -3
- package/dist/policy-packs/builtin/solution-acceptance.js.map +1 -1
- package/dist/policy-packs/builtin/understanding-before-execution/active-claim.d.ts +25 -0
- package/dist/policy-packs/builtin/understanding-before-execution/active-claim.js +89 -0
- package/dist/policy-packs/builtin/understanding-before-execution/active-claim.js.map +1 -0
- package/dist/policy-packs/builtin/understanding-before-execution/index.d.ts +13 -0
- package/dist/policy-packs/builtin/understanding-before-execution/index.js +39 -0
- package/dist/policy-packs/builtin/understanding-before-execution/index.js.map +1 -0
- package/dist/policy-packs/builtin/understanding-before-execution/ledger.d.ts +25 -0
- package/dist/policy-packs/builtin/understanding-before-execution/ledger.js +56 -0
- package/dist/policy-packs/builtin/understanding-before-execution/ledger.js.map +1 -0
- package/dist/policy-packs/builtin/understanding-before-execution/lifecycle.d.ts +20 -0
- package/dist/policy-packs/builtin/understanding-before-execution/lifecycle.js +79 -0
- package/dist/policy-packs/builtin/understanding-before-execution/lifecycle.js.map +1 -0
- package/dist/policy-packs/builtin/understanding-before-execution/markers.d.ts +103 -0
- package/dist/policy-packs/builtin/understanding-before-execution/markers.js +203 -0
- package/dist/policy-packs/builtin/understanding-before-execution/markers.js.map +1 -0
- package/dist/policy-packs/builtin/understanding-before-execution/persisted-reports.d.ts +177 -0
- package/dist/policy-packs/builtin/understanding-before-execution/persisted-reports.js +309 -0
- package/dist/policy-packs/builtin/understanding-before-execution/persisted-reports.js.map +1 -0
- package/dist/policy-packs/builtin/understanding-before-execution/post-tool-use-boundary.d.ts +106 -0
- package/dist/policy-packs/builtin/understanding-before-execution/post-tool-use-boundary.js +209 -0
- package/dist/policy-packs/builtin/understanding-before-execution/post-tool-use-boundary.js.map +1 -0
- package/dist/policy-packs/builtin/understanding-before-execution/task-markers.d.ts +78 -0
- package/dist/policy-packs/builtin/understanding-before-execution/task-markers.js +185 -0
- package/dist/policy-packs/builtin/understanding-before-execution/task-markers.js.map +1 -0
- package/dist/policy-packs/builtin/understanding-before-execution-runtime.d.ts +1 -536
- package/dist/policy-packs/builtin/understanding-before-execution-runtime.js +9 -1095
- package/dist/policy-packs/builtin/understanding-before-execution-runtime.js.map +1 -1
- package/dist/policy-packs/builtin/understanding-before-execution.d.ts +109 -1
- package/dist/policy-packs/builtin/understanding-before-execution.js +183 -15
- package/dist/policy-packs/builtin/understanding-before-execution.js.map +1 -1
- package/dist/policy-packs/registry.d.ts +11 -0
- package/dist/policy-packs/registry.js +1 -1
- package/dist/policy-packs/registry.js.map +1 -1
- package/dist/policy-packs/runtime.d.ts +1 -1
- package/dist/policy-packs/runtime.js +10 -1
- package/dist/policy-packs/runtime.js.map +1 -1
- package/dist/runtime/agent-facing.js +1 -1
- package/dist/runtime/agent-facing.js.map +1 -1
- package/dist/runtime/bash-match-facts.d.ts +9 -7
- package/dist/runtime/bash-match-facts.js +9 -7
- package/dist/runtime/bash-match-facts.js.map +1 -1
- package/dist/runtime/bash-match-registry.d.ts +75 -38
- package/dist/runtime/bash-match-registry.js +279 -232
- package/dist/runtime/bash-match-registry.js.map +1 -1
- package/dist/runtime/bash-prefix-parse.d.ts +7 -0
- package/dist/runtime/bash-prefix-parse.js +205 -17
- package/dist/runtime/bash-prefix-parse.js.map +1 -1
- package/dist/runtime/command-normalize.d.ts +112 -0
- package/dist/runtime/command-normalize.js +411 -38
- package/dist/runtime/command-normalize.js.map +1 -1
- package/dist/runtime/hermetic-spawn-guard.js +6 -0
- package/dist/runtime/hermetic-spawn-guard.js.map +1 -1
- package/dist/runtime/index.d.ts +2 -2
- package/dist/runtime/index.js +2 -2
- package/dist/runtime/index.js.map +1 -1
- package/dist/runtime/intercept.d.ts +78 -3
- package/dist/runtime/intercept.js +151 -39
- package/dist/runtime/intercept.js.map +1 -1
- package/dist/runtime/ledger-add.js +1 -1
- package/dist/runtime/ledger-add.js.map +1 -1
- package/dist/runtime/read-only-bash.js +303 -24
- package/dist/runtime/read-only-bash.js.map +1 -1
- package/dist/schema/doctor.d.ts +25 -0
- package/dist/schema/doctor.js +23 -0
- package/dist/schema/doctor.js.map +1 -0
- package/dist/schema/extract.js +1 -1
- package/dist/schema/extract.js.map +1 -1
- package/dist/schema/index.d.ts +76 -0
- package/dist/schema/index.js +9 -0
- package/dist/schema/index.js.map +1 -1
- package/dist/schema/requires.js +2 -2
- package/dist/schema/requires.js.map +1 -1
- package/dist/schema/risk.d.ts +7 -0
- package/dist/schema/risk.js +23 -0
- package/dist/schema/risk.js.map +1 -1
- package/dist/schema/stale-base-check.d.ts +18 -0
- package/dist/schema/stale-base-check.js +37 -0
- package/dist/schema/stale-base-check.js.map +1 -0
- package/dist/schema/toolchain-parity.d.ts +3 -0
- package/dist/schema/toolchain-parity.js +13 -0
- package/dist/schema/toolchain-parity.js.map +1 -1
- package/package.json +3 -2
- package/dist/policies/duration.js.map +0 -1
- package/dist/policies/extract.js.map +0 -1
- package/dist/runtime/expand-home.js.map +0 -1
- package/dist/runtime/ledger-record.js.map +0 -1
- /package/dist/{policies → io}/duration.d.ts +0 -0
- /package/dist/{policies → io}/duration.js +0 -0
- /package/dist/{runtime → io}/expand-home.d.ts +0 -0
- /package/dist/{runtime → io}/expand-home.js +0 -0
- /package/dist/{policies → io}/extract.d.ts +0 -0
- /package/dist/{policies → io}/extract.js +0 -0
- /package/dist/{runtime → io}/ledger-record.js +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// Registration point for every engine-side constant set that mirrors
|
|
2
2
|
// FULL_TEMPLATE's `bash_match` facts (task `074acf5d`, run
|
|
3
|
-
// `2026-07-28-manifest-facts-drift-guard`).
|
|
3
|
+
// `2026-07-28-manifest-facts-drift-guard`; redesigned by task `209e6dc4`).
|
|
4
4
|
//
|
|
5
5
|
// WHY A REGISTRY, NOT ONE HARD-CODED COMPARISON: the guard this replaces
|
|
6
6
|
// (`tests/runtime/bash-match-head-token-drift.test.ts`, pre-migration)
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
// NOT registered is itself a finding.
|
|
17
17
|
//
|
|
18
18
|
// TWO INTENTS (minimum required — see the two historical incidents this
|
|
19
|
-
//
|
|
19
|
+
// registry exists to close):
|
|
20
20
|
// - "covers-gated-head-tokens": the set exists so the engine recognises
|
|
21
21
|
// these head tokens (`NON_GIT_HEAD_TOKENS`'s shape). Checked for
|
|
22
22
|
// COMPLETENESS: every head token the manifest actually gates (barring
|
|
@@ -29,178 +29,227 @@
|
|
|
29
29
|
// gates — the run `dbc6d303` CRITICAL's shape (an "inert consumer"
|
|
30
30
|
// allowlist that accidentally absorbed the kill-switch's own gated
|
|
31
31
|
// head token). Checked by testing every LIVE gated head token against
|
|
32
|
-
// every such registered set's
|
|
32
|
+
// every such registered set's coverage.
|
|
33
33
|
//
|
|
34
|
-
//
|
|
35
|
-
//
|
|
36
|
-
//
|
|
37
|
-
//
|
|
38
|
-
//
|
|
39
|
-
//
|
|
40
|
-
//
|
|
41
|
-
//
|
|
42
|
-
//
|
|
43
|
-
//
|
|
44
|
-
//
|
|
45
|
-
//
|
|
46
|
-
//
|
|
47
|
-
//
|
|
48
|
-
//
|
|
49
|
-
//
|
|
50
|
-
//
|
|
51
|
-
// purpose is something else. It works only for a gated token that some
|
|
52
|
-
// covers-set legitimately covers, and is STRUCTURALLY SILENT for the
|
|
53
|
-
// `documented-uncovered` subset. MEASURED (review round 3): a
|
|
54
|
-
// registration declaring `members: ["tee"]` — or any subset of
|
|
55
|
-
// `env`/`unset`/`tee`/`cp`, four of the eight live gated head tokens —
|
|
56
|
-
// passes checks (a) through (e) with an entirely honest, live-coupled
|
|
57
|
-
// `has()` and `members`, end to end on the real tree. That is the
|
|
58
|
-
// `dbc6d303` shape (an inert allowlist absorbing a gated head token)
|
|
59
|
-
// and this registry does not see it. Redesign filed as a follow-up;
|
|
60
|
-
// see the LIMITS paragraph at the end of this header.
|
|
61
|
-
// (S4) check (e), COVERS-PREDICATE-PURITY, below: closes the escape
|
|
62
|
-
// where a registration's `has()` PREDICATE accepts more than its
|
|
63
|
-
// declared `members` claim (measured: widening `GIT_TOKEN_RE` to also
|
|
64
|
-
// match "docker" while leaving `members: ["git"]` untouched left the
|
|
65
|
-
// full suite green) — this guard reproducing, inside itself, the exact
|
|
66
|
-
// "engine-side constant mirrors manifest content with no coupling"
|
|
67
|
-
// defect class it exists to eliminate elsewhere.
|
|
68
|
-
// Each violation now carries a `check` field (see `RegistrationViolation`)
|
|
69
|
-
// naming exactly which of the five checks fired, so a fixture test can
|
|
70
|
-
// assert on ONE check's output without an unrelated check's finding on the
|
|
71
|
-
// same (setId, token) pair silently changing what the array contains.
|
|
34
|
+
// REDESIGN (task `209e6dc4`) — WHY: the ORIGINAL registration surface (fix
|
|
35
|
+
// rounds 1 and 2 of task `074acf5d`, both superseded by this redesign) let a
|
|
36
|
+
// registration hand the guard a freely-chosen `has: (token) => boolean`
|
|
37
|
+
// closure plus an independently hand-typed `members: readonly string[]`
|
|
38
|
+
// array — TWO self-descriptions of the SAME underlying constant, asserted
|
|
39
|
+
// by the registrant, never derived from it. Three adversarial review
|
|
40
|
+
// passes attacked that gap; two fix rounds narrowed the escape family by
|
|
41
|
+
// adding checks c/d/e over the self-descriptions (covers-purity,
|
|
42
|
+
// covers-redundancy, covers-predicate-purity) without closing it — every
|
|
43
|
+
// added check produced the next bypass. Review round 3 measured the
|
|
44
|
+
// residual: a `covers-gated-head-tokens` registration declaring
|
|
45
|
+
// `members: ["tee"]` (or any subset of the four `documented-uncovered`
|
|
46
|
+
// tokens `env`/`unset`/`tee`/`cp`) passed every one of the five checks with
|
|
47
|
+
// an entirely honest, live-coupled `has()` and `members` — because nothing
|
|
48
|
+
// in the old design distinguished "genuinely not gated" from "gated but
|
|
49
|
+
// consciously uncovered", and `members` could say anything a NEW closure
|
|
50
|
+
// also happened to agree with.
|
|
72
51
|
//
|
|
73
|
-
//
|
|
74
|
-
//
|
|
75
|
-
//
|
|
76
|
-
//
|
|
77
|
-
//
|
|
78
|
-
//
|
|
79
|
-
//
|
|
80
|
-
//
|
|
81
|
-
//
|
|
82
|
-
// registered set's own members inward to the facts. An engine set
|
|
83
|
-
// gaining an ungated member (e.g. "docker" added to
|
|
84
|
-
// `NON_GIT_HEAD_TOKENS`) is real DEAD CODE at best and a sign the
|
|
85
|
-
// manifest and the engine have silently diverged at worst; either way
|
|
86
|
-
// it is a regression this registry exists to catch.
|
|
87
|
-
// (F2) MIS-DECLARED INTENT: a `must-not-contain-gated-head-token` set
|
|
88
|
-
// relabeled `covers-gated-head-tokens` to dodge check (a) — cheap,
|
|
89
|
-
// because `intent` is self-declared and was, before this check,
|
|
90
|
-
// cross-checked against nothing. A set that exists for any purpose
|
|
91
|
-
// OTHER than recognising gated head tokens (an inert-consumer
|
|
92
|
-
// allowlist, say) necessarily contains members that are not
|
|
93
|
-
// themselves gated head tokens, so it fails THIS check the moment it
|
|
94
|
-
// declares `members` — the decision rule is: pick the intent that
|
|
95
|
-
// describes the set's PURPOSE; a set whose purpose is anything other
|
|
96
|
-
// than recognising gated head tokens is
|
|
97
|
-
// "must-not-contain-gated-head-token", never "covers-gated-head-tokens".
|
|
52
|
+
// HALT CRITERION (recorded before this redesign's first line, task
|
|
53
|
+
// `209e6dc4` brief, quoted verbatim): "Findet ein spaeterer Review eine
|
|
54
|
+
// weitere Registrierung, die akzeptiert wird und nicht sollte, wird NICHT
|
|
55
|
+
// nachgebessert (keine fuenfte/sechste Pruefung), sondern die Oberflaeche
|
|
56
|
+
// erneut vorgelegt. Drei Passes haben gezeigt: jede zusaetzliche Pruefung
|
|
57
|
+
// produziert die naechste Umgehung. Der Umbau muss die Formen STRUKTURELL
|
|
58
|
+
// unausdrueckbar machen, nicht einzeln fangen." This redesign follows that
|
|
59
|
+
// instruction literally: it does not add a sixth check on top of the old
|
|
60
|
+
// five. It removes the SURFACE the old checks were inspecting.
|
|
98
61
|
//
|
|
99
|
-
//
|
|
100
|
-
//
|
|
101
|
-
//
|
|
102
|
-
//
|
|
103
|
-
//
|
|
104
|
-
//
|
|
105
|
-
//
|
|
106
|
-
//
|
|
107
|
-
//
|
|
108
|
-
//
|
|
62
|
+
// THE STRUCTURAL FIX: `has()` as a freely-chosen closure, and `members` as
|
|
63
|
+
// an independently hand-typed array, are BOTH GONE. A registration now
|
|
64
|
+
// supplies a single `source: HeadTokenSource` — a discriminated union that
|
|
65
|
+
// holds the ACTUAL constant (`{ kind: "set"; set: NON_GIT_HEAD_TOKENS }` or
|
|
66
|
+
// `{ kind: "regex"; re: GIT_TOKEN_RE }`), imported and passed by reference,
|
|
67
|
+
// not re-described. `sourceHas` (below) derives membership testing FROM
|
|
68
|
+
// `source` — there is no code path left where a registration's declared
|
|
69
|
+
// coverage and its actual behaviour can diverge, because there is only one
|
|
70
|
+
// value now, not two. See the six-point closure list below for exactly
|
|
71
|
+
// which of the three passes' findings this makes unexpressible vs. which
|
|
72
|
+
// residual boundary (documented, not hidden) remains.
|
|
109
73
|
//
|
|
110
|
-
//
|
|
111
|
-
//
|
|
74
|
+
// SIX FORMS MEASURED OPEN BEFORE THIS REDESIGN (task `209e6dc4` brief,
|
|
75
|
+
// reconstructed from review round 3 since the run's own `.ai/runs/` docs
|
|
76
|
+
// are gitignored and not present in this checkout) AND THEIR DISPOSITION:
|
|
112
77
|
//
|
|
113
|
-
//
|
|
114
|
-
//
|
|
115
|
-
//
|
|
116
|
-
//
|
|
117
|
-
//
|
|
118
|
-
//
|
|
119
|
-
//
|
|
120
|
-
//
|
|
121
|
-
//
|
|
122
|
-
//
|
|
123
|
-
//
|
|
124
|
-
//
|
|
125
|
-
//
|
|
126
|
-
//
|
|
127
|
-
//
|
|
128
|
-
//
|
|
129
|
-
//
|
|
130
|
-
//
|
|
131
|
-
//
|
|
132
|
-
//
|
|
133
|
-
//
|
|
134
|
-
//
|
|
135
|
-
//
|
|
136
|
-
//
|
|
137
|
-
//
|
|
138
|
-
//
|
|
139
|
-
//
|
|
140
|
-
//
|
|
78
|
+
// 1. Documented-uncovered laundering WITHOUT dishonesty: a covers
|
|
79
|
+
// registration whose declared members are all `documented-uncovered`
|
|
80
|
+
// gated tokens (env/unset/tee/cp) passed all five checks with an
|
|
81
|
+
// honest, live-coupled has()/members (the dbc6d303-CRITICAL shape,
|
|
82
|
+
// concretely reproduced with `members: ["tee"]`).
|
|
83
|
+
// CLOSED, structurally + a live probe: `checkRegisteredSets`'s (now
|
|
84
|
+
// merged) covers-purity check probes the registration's actual
|
|
85
|
+
// `source` — not a hand-typed members list, there isn't one any
|
|
86
|
+
// more — against every live gated head token AND treats a
|
|
87
|
+
// `documented-uncovered` token as ILLEGITIMATE to claim, exactly like
|
|
88
|
+
// a token that is not gated at all. A registration cannot "declare"
|
|
89
|
+
// its way around this: the probe reads the real Set/RegExp.
|
|
90
|
+
// Pinned by the `["tee"]`-shaped regression test in
|
|
91
|
+
// `tests/runtime/bash-match-registry.test.ts` (the exact form review
|
|
92
|
+
// round 3 measured).
|
|
93
|
+
// 2. `has()` as an arbitrary closure with no coupling to the constant
|
|
94
|
+
// `id`/`module` name (stale hand-copied predicate; `has: () => false`;
|
|
95
|
+
// no predicate check at all on the must-not-contain arm).
|
|
96
|
+
// STRUCTURALLY UNEXPRESSIBLE: there is no `has()` field any more.
|
|
97
|
+
// `source` holds the constant directly; `sourceHas` is the ONLY way
|
|
98
|
+
// membership is ever tested, for both intents.
|
|
99
|
+
// 3. `members` claiming coverage `has()` rejects (each walked in only one
|
|
100
|
+
// direction), and `members: []`, which type-checked and passed.
|
|
101
|
+
// STRUCTURALLY UNEXPRESSIBLE (the divergence half): there is no
|
|
102
|
+
// `members` field to disagree with `source` — a registration has
|
|
103
|
+
// exactly one thing to say about its coverage. CHECKED, not merely
|
|
104
|
+
// unexpressible (the empty half): an empty/vacuous `source` (e.g.
|
|
105
|
+
// `new Set([])`, or a regex matching none of the live vocabulary) is
|
|
106
|
+
// still a value a registrant CAN write — `checkRegisteredSets`'s new
|
|
107
|
+
// covers-empty check flags a covers registration whose `source`
|
|
108
|
+
// matches none of the legitimately-coverable live gated head tokens.
|
|
109
|
+
// 4. Check (d) COVERS-REDUNDANCY (fix round 2) forbade ALL double
|
|
110
|
+
// coverage, so it would also convict a legitimate future case (two
|
|
111
|
+
// registered sets consciously, by design, both keying the same head
|
|
112
|
+
// token) as if it were laundering.
|
|
113
|
+
// RESOLVED: `CoversHeadTokenSet.intentionalOverlaps` is an explicit,
|
|
114
|
+
// conscious per-token declaration. Covers-redundancy now only clears
|
|
115
|
+
// a multiply-covered token when EVERY registered set currently
|
|
116
|
+
// covering it lists that token in ITS OWN `intentionalOverlaps` — a
|
|
117
|
+
// set that silently overlaps (the round-2 laundering shape: a new
|
|
118
|
+
// set quietly re-covering a token an existing set already owns)
|
|
119
|
+
// still trips the check, because it has declared nothing. Legitimate
|
|
120
|
+
// double coverage requires touching BOTH registrations, not one.
|
|
121
|
+
// 5. `PLAUSIBLE_NON_GATED_HEAD_TOKENS` (the probe vocabulary check (e)
|
|
122
|
+
// used) was itself an uncoupled hand-written constant — 5 of its 8
|
|
123
|
+
// entries were exercised by no test at the time, and a token outside
|
|
124
|
+
// BOTH vocabularies (e.g. `yarn`/`pnpm`) went unprobed.
|
|
125
|
+
// PARTIALLY CLOSED (acceptance's declared "OR" branch — the vocabulary
|
|
126
|
+
// cannot be DERIVED from the manifest, which only enumerates what IS
|
|
127
|
+
// gated, never the negative space of what is not): every entry is now
|
|
128
|
+
// individually pinned by a live regression test in
|
|
129
|
+
// `tests/runtime/bash-match-head-token-drift.test.ts` asserting it is
|
|
130
|
+
// NOT a live gated head token — a future policy gating one of these
|
|
131
|
+
// words turns that test red, forcing a conscious update instead of a
|
|
132
|
+
// silent drift. The residual boundary (a probe token entirely outside
|
|
133
|
+
// both vocabularies stays unprobed) is named here AND at the constant.
|
|
134
|
+
// 6. A registration could name a `module`/`id` to which no exported
|
|
135
|
+
// constant actually corresponds.
|
|
136
|
+
// STRUCTURALLY UNEXPRESSIBLE, by the TypeScript compiler: `id` and
|
|
137
|
+
// `module` are now purely descriptive strings (used only in failure
|
|
138
|
+
// messages) — the CHECKED value is `source.set` / `source.re`, a
|
|
139
|
+
// direct reference to the real imported binding. Writing
|
|
140
|
+
// `source: { kind: "set", set: SOME_NAME_THAT_DOES_NOT_EXIST }`
|
|
141
|
+
// is `TS2304: Cannot find name` — a compile error, before any test
|
|
142
|
+
// runs. Manually verified (task `209e6dc4`): temporarily editing a
|
|
143
|
+
// real registration's `source.set` reference to an undeclared
|
|
144
|
+
// identifier fails `npm run typecheck` immediately; reverted right
|
|
145
|
+
// after. `id`/`module` can still be a MISLEADING label for a real,
|
|
146
|
+
// existing `source` (e.g. call `NON_GIT_HEAD_TOKENS`'s registration
|
|
147
|
+
// `id: "TOTALLY_WRONG_NAME"` while `source.set` still correctly
|
|
148
|
+
// references it) — that is a cosmetic residual, not a behavioural
|
|
149
|
+
// one: every check still evaluates the REAL constant regardless of
|
|
150
|
+
// the label.
|
|
151
|
+
//
|
|
152
|
+
// WHAT IS STILL A RESIDUAL, NAMED HONESTLY, NOT HIDDEN:
|
|
153
|
+
// - The probe vocabulary (point 5) is finite; a token outside both
|
|
154
|
+
// `facts.gatedHeadTokens` and `PLAUSIBLE_NON_GATED_HEAD_TOKENS` is not
|
|
155
|
+
// probed by covers-purity. This is inherent to any predicate-based
|
|
156
|
+
// check over an unbounded domain (a `RegExp`'s match set is not
|
|
157
|
+
// enumerable) and is not something the source-based redesign changes;
|
|
158
|
+
// it narrows the practical exposure (both arms are now checked with
|
|
159
|
+
// the SAME probe set, no separate unguarded closure) without claiming
|
|
160
|
+
// to enumerate "every string that is not gated".
|
|
161
|
+
// - `id`/`module` remain free-text labels, decoupled from `source`'s
|
|
162
|
+
// actual identity (point 6's cosmetic half, above).
|
|
163
|
+
// - `intentionalOverlaps` is itself a hand-declared field. A registrant
|
|
164
|
+
// COULD mark an overlap "intentional" when it is not. The bar this
|
|
165
|
+
// redesign sets is that doing so requires editing BOTH covering
|
|
166
|
+
// registrations (a two-registration diff, visible in review), not
|
|
167
|
+
// silently adding one new registration next to an untouched existing
|
|
168
|
+
// one — a materially higher bar than the round-2 escape, not a
|
|
169
|
+
// mathematical impossibility.
|
|
170
|
+
// - The must-not-contain arm has no purity check of its own (never did):
|
|
171
|
+
// its whole point is that its content is unrelated to gated head
|
|
172
|
+
// tokens, so there is nothing to compare its coverage against.
|
|
173
|
+
//
|
|
174
|
+
// AXIS SCOPE (carried forward, unchanged by this redesign): this registry
|
|
175
|
+
// models the HEAD-TOKEN axis only — which literal head token a
|
|
176
|
+
// `bash_match` anchors on. A shipped policy's `bash_match` can equally
|
|
177
|
+
// anchor on a VERB with no head-token significance of its own (`pause` in
|
|
178
|
+
// `deny-kill-switch-bypass`), checked today only by `HARNESS_READ_ONLY_SUBS`
|
|
179
|
+
// in `read-only-bash.ts`, a set this registry does not see and does not
|
|
180
|
+
// check. The VERB axis is explicitly OUT OF SCOPE here, unchanged from the
|
|
181
|
+
// prior design: the exact same defect shape (an engine-side allowlist
|
|
182
|
+
// silently absorbing a gated token) is unmodellable here today, one axis
|
|
183
|
+
// over. Do not assume verb coverage exists because head-token coverage
|
|
184
|
+
// does.
|
|
185
|
+
//
|
|
186
|
+
// WHY src/runtime/, NOT tests/: see `bash-match-facts.ts`'s module header —
|
|
187
|
+
// the same reasoning applies here.
|
|
141
188
|
//
|
|
142
189
|
// TODAY'S REAL REGISTRATIONS are both "covers" sets — `GIT_TOKEN_RE` and
|
|
143
190
|
// `NON_GIT_HEAD_TOKENS`, both from `command-normalize.ts`, the two sets
|
|
144
|
-
// the pre-migration guard already knew about
|
|
145
|
-
//
|
|
146
|
-
//
|
|
147
|
-
//
|
|
148
|
-
//
|
|
149
|
-
//
|
|
150
|
-
//
|
|
151
|
-
//
|
|
152
|
-
//
|
|
153
|
-
//
|
|
154
|
-
//
|
|
191
|
+
// the pre-migration guard already knew about, each now registered by
|
|
192
|
+
// handing this module the ACTUAL constant (`source: { kind: "regex", re:
|
|
193
|
+
// GIT_TOKEN_RE }` / `source: { kind: "set", set: NON_GIT_HEAD_TOKENS }`),
|
|
194
|
+
// not a re-description of it. No real "must-not-contain-gated-head-token"
|
|
195
|
+
// consumer exists in this tree today (run `dbc6d303`'s own module is
|
|
196
|
+
// explicit non-goal / future work) — the intent is fully implemented and
|
|
197
|
+
// covered by the fixture regression tests in
|
|
198
|
+
// `tests/runtime/bash-match-registry.test.ts` (reproducing the CRITICAL's
|
|
199
|
+
// exact shape) and by a live mutation probe against this file's own
|
|
200
|
+
// `REGISTERED_HEAD_TOKEN_SETS` array, not against a second production
|
|
201
|
+
// consumer: temporarily registering a fixture
|
|
202
|
+
// (`intent: "must-not-contain-gated-head-token"`,
|
|
203
|
+
// `source: { kind: "set", set: new Set(["harness"]) }`) reddens exactly
|
|
204
|
+
// "every registered engine-side set is clean against the live facts ..."
|
|
205
|
+
// and the defaults test in
|
|
155
206
|
// `tests/runtime/bash-match-head-token-drift.test.ts`, with the violation
|
|
156
207
|
// naming the fixture's own `setId` and the offending token — the probe was
|
|
157
208
|
// reverted immediately after.
|
|
158
|
-
//
|
|
159
|
-
// `has()` rather than an enumerable `ReadonlySet<string>` uniformly for
|
|
160
|
-
// checks (a) and (b): the live gated-head-token vocabulary is small and
|
|
161
|
-
// fully known from `BashMatchFacts.gatedHeadTokens` (never more than ~8
|
|
162
|
-
// entries), so both checks only ever need to ask "does this registered set
|
|
163
|
-
// answer YES for token X", which a `RegExp.test` (`GIT_TOKEN_RE`) and a
|
|
164
|
-
// `Set.has` (`NON_GIT_HEAD_TOKENS`) can equally answer. Check (c)
|
|
165
|
-
// (COVERS-PURITY, above) is different in kind — it asks "does this
|
|
166
|
-
// registered set contain something the facts DON'T know about", which no
|
|
167
|
-
// predicate over the ~8 known tokens can answer; that is why `members` is
|
|
168
|
-
// a separate, enumerable field rather than folded into `has()` — NON-
|
|
169
|
-
// OPTIONAL on the "covers-gated-head-tokens" arm as of fix round 2 (S1;
|
|
170
|
-
// see `CoversHeadTokenSet`'s doc), optional on "must-not-contain", which
|
|
171
|
-
// this field's checks never apply to.
|
|
172
209
|
import { GIT_TOKEN_RE, NON_GIT_HEAD_TOKENS } from "./command-normalize.js";
|
|
173
210
|
import { DOCUMENTED_UNCOVERED_HEAD_TOKENS, isHeadTokenGated } from "./bash-match-facts.js";
|
|
211
|
+
/** Membership test derived FROM `source` — never independently supplied. */
|
|
212
|
+
export function sourceHas(source, token) {
|
|
213
|
+
return source.kind === "set" ? source.set.has(token) : source.re.test(token);
|
|
214
|
+
}
|
|
174
215
|
export const REGISTERED_HEAD_TOKEN_SETS = [
|
|
175
216
|
{
|
|
176
217
|
id: "GIT_TOKEN_RE",
|
|
177
218
|
module: "src/runtime/command-normalize.ts",
|
|
178
219
|
intent: "covers-gated-head-tokens",
|
|
179
|
-
|
|
180
|
-
members: ["git"],
|
|
220
|
+
source: { kind: "regex", re: GIT_TOKEN_RE },
|
|
181
221
|
description: "canonicalizeSegment's git-basename recognizer (matches `git`, and any `\\S*/git` path-qualified spelling, by basename)",
|
|
182
222
|
},
|
|
183
223
|
{
|
|
184
224
|
id: "NON_GIT_HEAD_TOKENS",
|
|
185
225
|
module: "src/runtime/command-normalize.ts",
|
|
186
226
|
intent: "covers-gated-head-tokens",
|
|
187
|
-
|
|
188
|
-
members: [...NON_GIT_HEAD_TOKENS],
|
|
227
|
+
source: { kind: "set", set: NON_GIT_HEAD_TOKENS },
|
|
189
228
|
description: "canonicalizeSegment's closed non-git head-token set (today: gh, npm, harness)",
|
|
190
229
|
},
|
|
191
230
|
];
|
|
192
231
|
/**
|
|
193
|
-
* Fixed, deliberately small vocabulary of plausible
|
|
194
|
-
* (fix round 2
|
|
195
|
-
*
|
|
196
|
-
* `
|
|
197
|
-
* `
|
|
198
|
-
*
|
|
199
|
-
*
|
|
200
|
-
*
|
|
201
|
-
*
|
|
232
|
+
* Fixed, deliberately small, PER-ENTRY-PINNED vocabulary of plausible
|
|
233
|
+
* NON-gated head tokens (originally fix round 2 S4/D-004; each entry
|
|
234
|
+
* individually pinned by a live regression test in
|
|
235
|
+
* `tests/runtime/bash-match-head-token-drift.test.ts` as of task
|
|
236
|
+
* `209e6dc4` — see the module header's point 5): commands a reasonable
|
|
237
|
+
* engine-side set might plausibly mirror or accidentally absorb, but that
|
|
238
|
+
* no shipped `bash_match` policy gates today. Combined with the live
|
|
239
|
+
* `facts.gatedHeadTokens` (every token that IS gated, verified fresh — see
|
|
240
|
+
* `bash-match-facts.ts`) this gives covers-purity a concrete probe domain
|
|
241
|
+
* to test every registered "covers" source against, instead of trusting a
|
|
242
|
+
* registration to honestly describe what it covers.
|
|
243
|
+
*
|
|
244
|
+
* RESIDUAL, named not hidden: this list cannot be exhaustively DERIVED from
|
|
245
|
+
* the manifest — the manifest enumerates what IS gated, never the infinite
|
|
246
|
+
* negative space of what is not. A token entirely outside both this list
|
|
247
|
+
* and `facts.gatedHeadTokens` (e.g. `yarn`, `pnpm`) is not probed by
|
|
248
|
+
* covers-purity. Widening this list narrows that residual; it cannot close
|
|
249
|
+
* it completely for a `RegExp`-backed source, whose true match set is not
|
|
250
|
+
* enumerable at all.
|
|
202
251
|
*/
|
|
203
|
-
const PLAUSIBLE_NON_GATED_HEAD_TOKENS = [
|
|
252
|
+
export const PLAUSIBLE_NON_GATED_HEAD_TOKENS = [
|
|
204
253
|
"docker",
|
|
205
254
|
"kubectl",
|
|
206
255
|
"ls",
|
|
@@ -213,42 +262,45 @@ const PLAUSIBLE_NON_GATED_HEAD_TOKENS = [
|
|
|
213
262
|
/**
|
|
214
263
|
* Check every registered set against the live facts. Returns an EMPTY
|
|
215
264
|
* array when clean. `registrations` and `documentedUncovered` default to
|
|
216
|
-
* the real, shipped values
|
|
217
|
-
* `
|
|
218
|
-
*
|
|
219
|
-
*
|
|
220
|
-
* `tests/runtime/bash-match-head-token-drift.test.ts`'s
|
|
221
|
-
* "checkRegisteredSets(liveFacts())" defaults test) but accept fixtures —
|
|
222
|
-
* see `tests/runtime/bash-match-registry.test.ts`'s regression tests for
|
|
223
|
-
* the historical incidents, which pass deliberately-broken fixtures to
|
|
224
|
-
* prove this function catches their exact shape.
|
|
265
|
+
* the real, shipped values but accept fixtures — see
|
|
266
|
+
* `tests/runtime/bash-match-registry.test.ts`'s regression tests for the
|
|
267
|
+
* historical incidents, which pass deliberately-broken fixtures to prove
|
|
268
|
+
* this function catches their exact shape.
|
|
225
269
|
*/
|
|
226
270
|
export function checkRegisteredSets(facts, registrations = REGISTERED_HEAD_TOKEN_SETS, documentedUncovered = DOCUMENTED_UNCOVERED_HEAD_TOKENS) {
|
|
227
271
|
const violations = [];
|
|
228
272
|
// (a) must-not-contain: no registered set with this intent may answer
|
|
229
|
-
// `true`
|
|
273
|
+
// `true` (via `sourceHas`, the ONLY membership test that exists now) for
|
|
274
|
+
// ANY live gated head token — the dbc6d303 CRITICAL shape.
|
|
230
275
|
for (const reg of registrations) {
|
|
231
276
|
if (reg.intent !== "must-not-contain-gated-head-token")
|
|
232
277
|
continue;
|
|
233
278
|
for (const token of facts.gatedHeadTokens) {
|
|
234
|
-
if (reg.
|
|
279
|
+
if (sourceHas(reg.source, token)) {
|
|
235
280
|
violations.push({
|
|
236
281
|
setId: reg.id,
|
|
237
282
|
intent: reg.intent,
|
|
238
283
|
token,
|
|
239
284
|
check: "must-not-contain",
|
|
240
|
-
reason: `"${reg.id}" (${reg.module} — ${reg.description}) declares intent "must-not-contain-gated-head-token" but contains "${token}", which IS gated by a shipped bash_match policy`,
|
|
285
|
+
reason: `"${reg.id}" (${reg.module} — ${reg.description}) declares intent "must-not-contain-gated-head-token" but its source contains "${token}", which IS gated by a shipped bash_match policy`,
|
|
241
286
|
});
|
|
242
287
|
}
|
|
243
288
|
}
|
|
244
289
|
}
|
|
290
|
+
// Is `token` something a "covers" registration is LEGITIMATELY allowed to
|
|
291
|
+
// claim: gated, AND not consciously carved out as documented-uncovered?
|
|
292
|
+
// Point 1 of the module header's six forms lived exactly in the gap this
|
|
293
|
+
// predicate closes — the old design treated "gated" as the only bar to
|
|
294
|
+
// clear, so a covers set claiming ONLY documented-uncovered tokens
|
|
295
|
+
// passed cleanly.
|
|
296
|
+
const isLegitimatelyCoverable = (token) => isHeadTokenGated(facts, token) && !documentedUncovered.has(token);
|
|
245
297
|
// (b) covers-completeness: every gated head token NOT consciously
|
|
246
298
|
// documented as uncovered must be answered `true` by at least one
|
|
247
|
-
// registered "covers" set — the 432db3d3 HIGH shape.
|
|
299
|
+
// registered "covers" set's `source` — the 432db3d3 HIGH shape.
|
|
248
300
|
for (const token of facts.gatedHeadTokens) {
|
|
249
301
|
if (documentedUncovered.has(token))
|
|
250
302
|
continue;
|
|
251
|
-
const covered = registrations.some((reg) => reg.intent === "covers-gated-head-tokens" && reg.
|
|
303
|
+
const covered = registrations.some((reg) => reg.intent === "covers-gated-head-tokens" && sourceHas(reg.source, token));
|
|
252
304
|
if (!covered) {
|
|
253
305
|
const cls = facts.classify(token);
|
|
254
306
|
violations.push({
|
|
@@ -260,81 +312,45 @@ export function checkRegisteredSets(facts, registrations = REGISTERED_HEAD_TOKEN
|
|
|
260
312
|
});
|
|
261
313
|
}
|
|
262
314
|
}
|
|
263
|
-
// (c) covers-
|
|
264
|
-
//
|
|
265
|
-
// gated head
|
|
266
|
-
//
|
|
267
|
-
//
|
|
268
|
-
//
|
|
269
|
-
//
|
|
270
|
-
//
|
|
271
|
-
//
|
|
272
|
-
// necessarily contains a member that fails this check. `members` is
|
|
273
|
-
// non-optional on this arm as of fix round 2 (S1) — every registration
|
|
274
|
-
// that reaches here has one to walk.
|
|
315
|
+
// (c) covers-empty (task `209e6dc4`, module header point 3, empty half):
|
|
316
|
+
// a covers registration whose `source` matches NONE of the legitimately
|
|
317
|
+
// coverable live gated head tokens contributes zero real coverage — the
|
|
318
|
+
// structural echo of the old `members: []` escape, which type-checked
|
|
319
|
+
// and passed silently because nothing walked an empty declaration back
|
|
320
|
+
// to a finding. There is no `members` field to leave empty any more, but
|
|
321
|
+
// an empty backing `Set`, or a regex that matches nothing in the known
|
|
322
|
+
// vocabulary, is still a value a registrant can write; this check makes
|
|
323
|
+
// that visible instead of silently accepted.
|
|
275
324
|
for (const reg of registrations) {
|
|
276
325
|
if (reg.intent !== "covers-gated-head-tokens")
|
|
277
326
|
continue;
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
}
|
|
290
|
-
// (d) covers-redundancy (fix round 2, S2): a gated head token answered
|
|
291
|
-
// `true` by MORE THAN ONE registered "covers-gated-head-tokens" set is a
|
|
292
|
-
// violation. Closes the escape check (c) cannot: a laundered set (its
|
|
293
|
-
// TRUE purpose is something other than recognising gated head tokens,
|
|
294
|
-
// relabeled "covers-gated-head-tokens" to dodge check (a)) whose
|
|
295
|
-
// declared `members` are HONESTLY all gated — e.g. `new
|
|
296
|
-
// Set(["harness"])` or `new Set(["harness", "gh"])` — passes check (c)
|
|
297
|
-
// cleanly (every declared member IS a live gated head token). The tell
|
|
298
|
-
// is redundancy: a gated head token legitimately has exactly ONE
|
|
299
|
-
// registered "covers" set responsible for it.
|
|
300
|
-
//
|
|
301
|
-
// SCOPE OF THIS CHECK, measured, not assumed (review round 3): the tell
|
|
302
|
-
// only exists for a token some covers-set ALREADY covers. For the four
|
|
303
|
-
// `documented-uncovered` tokens (`env`, `unset`, `tee`, `cp`) no
|
|
304
|
-
// covers-set covers them by definition, so `coveringSets.length` is 1
|
|
305
|
-
// and this check is structurally silent — while check (b) skips them and
|
|
306
|
-
// checks (c)/(e) pass because they ARE gated. A laundered set declaring
|
|
307
|
-
// only those tokens therefore passes everything. Do not read this check
|
|
308
|
-
// as a completeness proof; an earlier version of this comment did, and
|
|
309
|
-
// it was false for half the live vocabulary.
|
|
310
|
-
for (const token of facts.gatedHeadTokens) {
|
|
311
|
-
const coveringSets = registrations.filter((reg) => reg.intent === "covers-gated-head-tokens" && reg.has(token));
|
|
312
|
-
if (coveringSets.length > 1) {
|
|
313
|
-
for (const reg of coveringSets) {
|
|
314
|
-
const others = coveringSets.filter((r) => r.id !== reg.id).map((r) => r.id);
|
|
315
|
-
violations.push({
|
|
316
|
-
setId: reg.id,
|
|
317
|
-
intent: reg.intent,
|
|
318
|
-
token,
|
|
319
|
-
check: "covers-redundancy",
|
|
320
|
-
reason: `"${reg.id}" (${reg.module} — ${reg.description}) declares intent "covers-gated-head-tokens" and answers true for "${token}", but "${token}" is ALSO covered by ${others.length} other registered covers-set(s) (${others.join(", ")}) — a gated head token must be covered by exactly one registered covers-set; redundant coverage is the signature of an intent-laundered set whose declared members happen to already be genuinely gated elsewhere`,
|
|
321
|
-
});
|
|
322
|
-
}
|
|
327
|
+
const coversSomething = [...facts.gatedHeadTokens]
|
|
328
|
+
.filter(isLegitimatelyCoverable)
|
|
329
|
+
.some((token) => sourceHas(reg.source, token));
|
|
330
|
+
if (!coversSomething) {
|
|
331
|
+
violations.push({
|
|
332
|
+
setId: reg.id,
|
|
333
|
+
intent: reg.intent,
|
|
334
|
+
token: "(none)",
|
|
335
|
+
check: "covers-empty",
|
|
336
|
+
reason: `"${reg.id}" (${reg.module} — ${reg.description}) declares intent "covers-gated-head-tokens" but its source matches NONE of the live, legitimately-coverable gated head tokens — an empty or vacuous registration`,
|
|
337
|
+
});
|
|
323
338
|
}
|
|
324
339
|
}
|
|
325
|
-
// (
|
|
326
|
-
//
|
|
327
|
-
//
|
|
328
|
-
//
|
|
329
|
-
//
|
|
330
|
-
//
|
|
331
|
-
//
|
|
332
|
-
//
|
|
333
|
-
//
|
|
334
|
-
//
|
|
335
|
-
//
|
|
336
|
-
//
|
|
337
|
-
//
|
|
340
|
+
// (d) covers-purity (task `209e6dc4` redesign, merging the old checks
|
|
341
|
+
// (c) covers-purity, (e) covers-predicate-purity, and closing the
|
|
342
|
+
// previously-unchecked documented-uncovered laundering gap — module
|
|
343
|
+
// header points 1 and 3): probe the registration's ACTUAL `source`
|
|
344
|
+
// directly — never a hand-typed `members` list, there isn't one any
|
|
345
|
+
// more — against every live gated head token PLUS
|
|
346
|
+
// `PLAUSIBLE_NON_GATED_HEAD_TOKENS`. A covers registration must answer
|
|
347
|
+
// `true` ONLY for tokens that are BOTH gated AND not consciously
|
|
348
|
+
// documented-uncovered (i.e. `isLegitimatelyCoverable`). Because coverage
|
|
349
|
+
// is read straight off `source` instead of a separate declared list,
|
|
350
|
+
// "declaring coverage the predicate rejects" and "claiming a
|
|
351
|
+
// documented-uncovered token honestly" collapse into the same probe:
|
|
352
|
+
// there is nothing left to declare independently of what the constant
|
|
353
|
+
// actually does.
|
|
338
354
|
const probeVocabulary = new Set([
|
|
339
355
|
...facts.gatedHeadTokens,
|
|
340
356
|
...PLAUSIBLE_NON_GATED_HEAD_TOKENS,
|
|
@@ -343,17 +359,48 @@ export function checkRegisteredSets(facts, registrations = REGISTERED_HEAD_TOKEN
|
|
|
343
359
|
if (reg.intent !== "covers-gated-head-tokens")
|
|
344
360
|
continue;
|
|
345
361
|
for (const token of probeVocabulary) {
|
|
346
|
-
if (reg.
|
|
362
|
+
if (sourceHas(reg.source, token) && !isLegitimatelyCoverable(token)) {
|
|
363
|
+
const reason = documentedUncovered.has(token)
|
|
364
|
+
? `"${reg.id}" (${reg.module} — ${reg.description}) declares intent "covers-gated-head-tokens" and its source answers true for "${token}", but "${token}" IS gated while also being consciously documented-uncovered — a covers set may not claim coverage of a token this codebase has deliberately marked as NOT covered by any engine-side set (the dbc6d303-shape laundering this check exists to close)`
|
|
365
|
+
: `"${reg.id}" (${reg.module} — ${reg.description}) declares intent "covers-gated-head-tokens" and its source answers true for "${token}", but "${token}" is not a live gated head token at all — the source accepts more than it should, reproducing this registry's own defect class (an engine-side constant mirroring manifest content with no coupling) inside the guard itself`;
|
|
347
366
|
violations.push({
|
|
348
367
|
setId: reg.id,
|
|
349
368
|
intent: reg.intent,
|
|
350
369
|
token,
|
|
351
|
-
check: "covers-
|
|
352
|
-
reason
|
|
370
|
+
check: "covers-purity",
|
|
371
|
+
reason,
|
|
353
372
|
});
|
|
354
373
|
}
|
|
355
374
|
}
|
|
356
375
|
}
|
|
376
|
+
// (e) covers-redundancy: a gated head token answered `true` by MORE THAN
|
|
377
|
+
// ONE registered "covers-gated-head-tokens" set's `source` is a
|
|
378
|
+
// violation UNLESS every one of those covering sets consciously declares
|
|
379
|
+
// that token in its own `intentionalOverlaps` (task `209e6dc4`,
|
|
380
|
+
// resolving module header point 4: a silent new registration re-covering
|
|
381
|
+
// a token an existing set already owns still trips this — only a
|
|
382
|
+
// declaration on BOTH sides clears it).
|
|
383
|
+
const isCoversSet = (reg) => reg.intent === "covers-gated-head-tokens";
|
|
384
|
+
for (const token of facts.gatedHeadTokens) {
|
|
385
|
+
const coveringSets = registrations
|
|
386
|
+
.filter(isCoversSet)
|
|
387
|
+
.filter((reg) => sourceHas(reg.source, token));
|
|
388
|
+
if (coveringSets.length <= 1)
|
|
389
|
+
continue;
|
|
390
|
+
const allAcknowledged = coveringSets.every((reg) => reg.intentionalOverlaps?.has(token));
|
|
391
|
+
if (allAcknowledged)
|
|
392
|
+
continue;
|
|
393
|
+
for (const reg of coveringSets) {
|
|
394
|
+
const others = coveringSets.filter((r) => r.id !== reg.id).map((r) => r.id);
|
|
395
|
+
violations.push({
|
|
396
|
+
setId: reg.id,
|
|
397
|
+
intent: reg.intent,
|
|
398
|
+
token,
|
|
399
|
+
check: "covers-redundancy",
|
|
400
|
+
reason: `"${reg.id}" (${reg.module} — ${reg.description}) declares intent "covers-gated-head-tokens" and answers true for "${token}", but "${token}" is ALSO covered by ${others.length} other registered covers-set(s) (${others.join(", ")}) without every covering set declaring "${token}" in its own intentionalOverlaps — a gated head token must be covered by exactly one registered covers-set unless ALL covering sets consciously declare the overlap`,
|
|
401
|
+
});
|
|
402
|
+
}
|
|
403
|
+
}
|
|
357
404
|
return violations;
|
|
358
405
|
}
|
|
359
406
|
//# sourceMappingURL=bash-match-registry.js.map
|