arkgate 4.8.23 → 4.8.24

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (131) hide show
  1. package/CHANGELOG.md +68 -0
  2. package/README.md +7 -5
  3. package/bin/ark-check-runtime.mjs +24 -77
  4. package/bin/ark-dashboard.mjs +56 -53
  5. package/bin/ark-layer-match.mjs +596 -112
  6. package/bin/ark-mcp-runtime.mjs +412 -138
  7. package/bin/ark-mcp.mjs +68 -4
  8. package/bin/ark-shared.mjs +39 -0
  9. package/bin/ark.mjs +65 -97
  10. package/bin/lib/adapter-contract-types.mjs +5 -0
  11. package/bin/lib/adapter-contract.mjs +15 -0
  12. package/bin/lib/agent-projection-command.mjs +5 -1
  13. package/bin/lib/analysis-engine.mjs +15 -11
  14. package/bin/lib/architecture-scan.mjs +50 -6
  15. package/bin/lib/ark-order-doctor.mjs +1 -1
  16. package/bin/lib/ark-order-facts.mjs +410 -15
  17. package/bin/lib/ark-order-invariants.mjs +51 -9
  18. package/bin/lib/ark-order-sensors.mjs +28 -101
  19. package/bin/lib/ark-order-types.mjs +2 -0
  20. package/bin/lib/ark-run-doctor.mjs +14 -0
  21. package/bin/lib/ark-run-facts.mjs +434 -11
  22. package/bin/lib/ark-run-sensors.mjs +5 -3
  23. package/bin/lib/arkgate-pins.mjs +91 -0
  24. package/bin/lib/arkrules-sensors.mjs +50 -249
  25. package/bin/lib/check-args.mjs +23 -1
  26. package/bin/lib/ci-and-commands.mjs +16 -6
  27. package/bin/lib/class-source-scan.mjs +479 -0
  28. package/bin/lib/codex-home.mjs +4 -2
  29. package/bin/lib/config-contract-slices.mjs +701 -0
  30. package/bin/lib/config-contract.mjs +5 -493
  31. package/bin/lib/config-warnings.mjs +32 -0
  32. package/bin/lib/dashboard-cli.mjs +62 -0
  33. package/bin/lib/design-delta.mjs +1 -1
  34. package/bin/lib/diagnostic-catalog.mjs +16 -9
  35. package/bin/lib/doctor-advisories.mjs +25 -1
  36. package/bin/lib/doctor-next-actions.mjs +17 -2
  37. package/bin/lib/doctor-plan.mjs +6 -8
  38. package/bin/lib/effective-contract-load.mjs +121 -59
  39. package/bin/lib/enforcement-profiles.mjs +40 -4
  40. package/bin/lib/first-run-help.mjs +3 -2
  41. package/bin/lib/flat-parent-importers.mjs +137 -0
  42. package/bin/lib/flat-parent-pilot.mjs +40 -100
  43. package/bin/lib/gate-files.mjs +29 -2
  44. package/bin/lib/git-change-scope.mjs +109 -0
  45. package/bin/lib/github-enforcement.mjs +1 -1
  46. package/bin/lib/hook-templates.mjs +18 -16
  47. package/bin/lib/host-support-matrix.mjs +3 -1
  48. package/bin/lib/import-resolve.mjs +13 -1
  49. package/bin/lib/install-migrate.mjs +50 -73
  50. package/bin/lib/invariant-coverage.mjs +59 -11
  51. package/bin/lib/mcp-adoption.mjs +9 -1
  52. package/bin/lib/mcp-hook-payload.mjs +401 -61
  53. package/bin/lib/package-manager.mjs +59 -1
  54. package/bin/lib/passthrough-commands.mjs +83 -0
  55. package/bin/lib/persistence-write-hint.mjs +81 -0
  56. package/bin/lib/pin-dependent-gates.mjs +117 -0
  57. package/bin/lib/policy-delta-io.mjs +168 -10
  58. package/bin/lib/prepare-change.mjs +120 -6
  59. package/bin/lib/prepare-write.mjs +1 -0
  60. package/bin/lib/presets.mjs +3 -5
  61. package/bin/lib/remediation.mjs +23 -4
  62. package/bin/lib/resolved-candidate-facts.mjs +315 -55
  63. package/bin/lib/rules-inventory.mjs +22 -3
  64. package/bin/lib/rules-under-contract.mjs +179 -5
  65. package/bin/lib/scan-files.mjs +32 -0
  66. package/bin/lib/snippet-analysis.mjs +140 -6
  67. package/bin/lib/source-policy.mjs +34 -0
  68. package/bin/lib/stable-hash.mjs +90 -0
  69. package/bin/lib/start-install-recovery.mjs +71 -1
  70. package/bin/lib/start-preview.mjs +30 -0
  71. package/bin/lib/status-command.mjs +33 -4
  72. package/bin/lib/status-manifest.mjs +32 -4
  73. package/bin/lib/team-parliament-io.mjs +77 -75
  74. package/bin/lib/team-parliament.mjs +6 -1
  75. package/bin/lib/write-path-capabilities.mjs +4 -2
  76. package/dist/chunk-A5EIY3HI.cjs +5 -0
  77. package/dist/chunk-MBXCFLO4.js +16 -0
  78. package/dist/chunk-RNQHRDCQ.cjs +16 -0
  79. package/dist/chunk-SVSQVGFJ.js +5 -0
  80. package/dist/{configTypes-Bbf_MFGe.d.ts → configTypes-BJbaeoj4.d.cts} +18 -3
  81. package/dist/configTypes-BJbaeoj4.d.ts +246 -0
  82. package/dist/eslint/index.cjs +17 -4
  83. package/dist/eslint/index.d.cts +359 -0
  84. package/dist/eslint/index.d.ts +130 -156
  85. package/dist/eslint/index.js +10 -4
  86. package/dist/index.cjs +35 -34
  87. package/dist/index.d.cts +5475 -0
  88. package/dist/index.d.ts +3312 -299
  89. package/dist/index.js +35 -34
  90. package/dist/inspectorListen-6DQVJ4DC.js +1 -0
  91. package/dist/inspectorListen-KGMIQ2TF.cjs +1 -0
  92. package/dist/nestjs/index.cjs +1 -18
  93. package/dist/nestjs/index.d.cts +22 -0
  94. package/dist/nestjs/index.d.ts +1 -3
  95. package/dist/nestjs/index.js +1 -18
  96. package/dist/order/index.cjs +1 -1
  97. package/dist/order/index.d.cts +155 -0
  98. package/dist/order/index.d.ts +15 -3
  99. package/dist/order/index.js +1 -1
  100. package/dist/runtime/index.cjs +13 -30
  101. package/dist/{diagnosticCatalog-35mrwAHe.d.ts → runtime/index.d.cts} +804 -290
  102. package/dist/runtime/index.d.ts +2678 -108
  103. package/dist/runtime/index.js +13 -30
  104. package/dist/{types-Bdj32QwA.d.ts → types-DRinG-vT.d.cts} +576 -140
  105. package/dist/types-DRinG-vT.d.ts +1792 -0
  106. package/docs/README.md +2 -1
  107. package/docs/agent-guide.md +84 -15
  108. package/docs/ai-gates.md +160 -26
  109. package/docs/arkorder.md +32 -15
  110. package/docs/brownfield-adoption.md +5 -1
  111. package/docs/configuration.md +196 -26
  112. package/docs/develop.md +2 -1
  113. package/docs/diagnostics.md +96 -19
  114. package/docs/package-surface.md +31 -8
  115. package/docs/product-voice.md +1 -1
  116. package/package.json +59 -18
  117. package/schemas/ark.analysis-result.schema.json +15 -0
  118. package/schemas/ark.config.schema.json +47 -10
  119. package/schemas/ark.resolved-candidate-facts.schema.json +1 -1
  120. package/schemas/ark.status-manifest.schema.json +22 -0
  121. package/server.json +3 -9
  122. package/templates/agent-skills/README.md +1 -1
  123. package/templates/agent-skills/ark-adopt/SKILL.md +4 -1
  124. package/templates/agent-skills/ark-order/SKILL.md +4 -2
  125. package/templates/agent-skills/ark-runtime/SKILL.md +6 -1
  126. package/templates/arkrules/PersistenceAdapters.json +1 -1
  127. package/templates/arkrules/PresentationAdapters.json +1 -1
  128. package/templates/skills/ark-adopt.md +4 -1
  129. package/templates/skills/ark-order.md +4 -2
  130. package/templates/skills/ark-runtime.md +6 -1
  131. package/dist/types-Ik_OakcI.d.ts +0 -183
@@ -27,6 +27,7 @@ Link form for agents: `docs/diagnostics.md#RULE_ID` (exact-case HTML anchors bel
27
27
  | [`LAYER_IMPORT_VIOLATION`](#LAYER_IMPORT_VIOLATION) | layer | This import is not allowed |
28
28
  | [`LAYER_INTENT_REFERENCE_VIOLATION`](#LAYER_INTENT_REFERENCE_VIOLATION) | layer | Intent referenced across a blocked layer edge |
29
29
  | [`LAYER_REFERENCE_VIOLATION`](#LAYER_REFERENCE_VIOLATION) | layer | Layer reference blocked (snippet / AI gate) |
30
+ | [`SHARED_IMPORTS_SLICE`](#SHARED_IMPORTS_SLICE) | layer | A shared root imports a slice |
30
31
  | [`CIRCULAR_DEPENDENCY`](#CIRCULAR_DEPENDENCY) | layer | Dependency cycle |
31
32
  | [`FORBIDDEN_GLOBAL`](#FORBIDDEN_GLOBAL) | capability | Forbidden ambient global or dual import |
32
33
  | [`CAPABILITY_VIOLATION`](#CAPABILITY_VIOLATION) | capability | Denied effect capability |
@@ -44,6 +45,7 @@ Link form for agents: `docs/diagnostics.md#RULE_ID` (exact-case HTML anchors bel
44
45
  | [`ARKRULE_INVARIANT`](#ARKRULE_INVARIANT) | arkrules | ArkRule invariant failed |
45
46
  | [`ARKRULE_SCOPE_EMPTY`](#ARKRULE_SCOPE_EMPTY) | arkrules | ArkRule appliesTo matched zero files |
46
47
  | [`ARKRULE_HINT_BUDGET_EXHAUSTED`](#ARKRULE_HINT_BUDGET_EXHAUSTED) | arkrules | Structural-hint budget exhausted |
48
+ | [`ARKRULE_FILE_UNREFERENCED`](#ARKRULE_FILE_UNREFERENCED) | arkrules | ArkRules file not referenced |
47
49
  | [`INVARIANT_CATALOG_EMPTY`](#INVARIANT_CATALOG_EMPTY) | arkrules | Domain invariant catalog is empty |
48
50
  | [`INVARIANT_UNCOVERED`](#INVARIANT_UNCOVERED) | arkrules | Invariant without coverage evidence |
49
51
  | [`INVARIANT_COVERAGE_OUTSIDE_ROOTS`](#INVARIANT_COVERAGE_OUTSIDE_ROOTS) | arkrules | Covering test outside the declared coverage roots |
@@ -62,6 +64,7 @@ Link form for agents: `docs/diagnostics.md#RULE_ID` (exact-case HTML anchors bel
62
64
  | [`ARKORDER_TOO_MANY_PARAMS`](#ARKORDER_TOO_MANY_PARAMS) | arkorder | Too many slow keys |
63
65
  | [`ARKORDER_INGEST_WRITES_XI`](#ARKORDER_INGEST_WRITES_XI) | arkorder | ingest assigned into ξ |
64
66
  | [`ARKORDER_UNVALVED_RELEASE`](#ARKORDER_UNVALVED_RELEASE) | arkorder | Second freeze without the valve |
67
+ | [`ARKORDER_STALE_PROPOSAL`](#ARKORDER_STALE_PROPOSAL) | arkorder | Proposal is not bound to the current Release |
65
68
  | [`INVALID_CHANGE_PATH`](#INVALID_CHANGE_PATH) | preflight | Unsafe change path |
66
69
  | [`DUPLICATE_CHANGE_PATH`](#DUPLICATE_CHANGE_PATH) | preflight | Duplicate path in change set |
67
70
  | [`DELETE_TARGET_MISSING`](#DELETE_TARGET_MISSING) | preflight | Delete target missing |
@@ -72,6 +75,7 @@ Link form for agents: `docs/diagnostics.md#RULE_ID` (exact-case HTML anchors bel
72
75
  | [`CANDIDATE_CONTENT_HASH_MISMATCH`](#CANDIDATE_CONTENT_HASH_MISMATCH) | preflight | Candidate content hash mismatch |
73
76
  | [`UNDECLARED_CANDIDATE_CHANGE`](#UNDECLARED_CANDIDATE_CHANGE) | preflight | Undeclared candidate change |
74
77
  | [`ATOMIC_PREFLIGHT_UNAVAILABLE`](#ATOMIC_PREFLIGHT_UNAVAILABLE) | preflight | Atomic preflight unavailable |
78
+ | [`WRITE_GATE_UNAVAILABLE`](#WRITE_GATE_UNAVAILABLE) | preflight | Write gate could not run |
75
79
  | [`DESIGN_SMELL_REGRESSION`](#DESIGN_SMELL_REGRESSION) | preflight | Design smell regression on base-relative ratchet |
76
80
  | [`ANALYSIS_PARSE_INCOMPLETE`](#ANALYSIS_PARSE_INCOMPLETE) | analysis | Parse incomplete |
77
81
  | [`LEXICAL_EVIDENCE_INCOMPLETE`](#LEXICAL_EVIDENCE_INCOMPLETE) | analysis | Lexical evidence incomplete |
@@ -102,9 +106,11 @@ Link form for agents: `docs/diagnostics.md#RULE_ID` (exact-case HTML anchors bel
102
106
  | [`CONFIG_UNCLASSIFIED_FILES`](#CONFIG_UNCLASSIFIED_FILES) | config | Unclassified included files |
103
107
  | [`CONFIG_LAYER_MISSING_OWNER`](#CONFIG_LAYER_MISSING_OWNER) | config | Layer missing owner |
104
108
  | [`CONFIG_SLICE_IDENTITY_COLLISION`](#CONFIG_SLICE_IDENTITY_COLLISION) | config | Starred slice prefixes share one id |
105
- | [`CONFIG_CHILD_SLICES_VERSION`](#CONFIG_CHILD_SLICES_VERSION) | config | childSlices needs a newer arkgate |
109
+ | [`CONFIG_CHILD_SLICES_VERSION`](#CONFIG_CHILD_SLICES_VERSION) | config | Config needs a newer arkgate than a pinned copy |
106
110
  | [`CONFIG_CHILD_SLICE_EXTENDS`](#CONFIG_CHILD_SLICE_EXTENDS) | config | Child slice id does not extend its universe |
107
111
  | [`CONFIG_CHILD_SLICE_CROSS_UNIVERSE`](#CONFIG_CHILD_SLICE_CROSS_UNIVERSE) | config | Child slice allowance cannot cross the universe wall |
112
+ | [`CONFIG_CHILD_SLICES_INERT`](#CONFIG_CHILD_SLICES_INERT) | config | childSlices on a rule that cannot run it |
113
+ | [`CONFIG_SLICE_LEGACY_STARS_ID`](#CONFIG_SLICE_LEGACY_STARS_ID) | config | Slice id written in the 4.8.23 stars form |
108
114
  | [`ARK_UNKNOWN`](#ARK_UNKNOWN) | meta | Unknown diagnostic |
109
115
 
110
116
  ## Layer and dependency graph
@@ -117,8 +123,8 @@ Link form for agents: `docs/diagnostics.md#RULE_ID` (exact-case HTML anchors bel
117
123
 
118
124
  - **Why:** This file imported a folder it may not reach. The write doesn’t land. The same check fails the pull request.
119
125
  - **Fix:** Branch by import kind: constants/types/pure → adopt into DomainModel or SharedKernel (do not invent a port); kernel/events/bootstrap from Persistence → inject a port or move the map to SharedTypes (Persistence must not emit); define a port only when the target is a real use-case. Type-only edges use `import type`. Then preflight again. Do not weaken the layer rule without a hash-bound policy acknowledgement.
120
- - **Slice reasons:** the ruleId stays `LAYER_IMPORT_VIOLATION`. When a rule sets `childSlices`, the finding may also carry `reasonId` `CROSS_PARENT_SLICE` (this import crosses two universe slices; the child wall cannot allow another universe) or `CROSS_SIBLING_SLICE` (this import crosses two feature slices inside one universe). `siblings` may be `"deny"`, `"advisory"`, or `{ "default", "enforce" }`. An enforced importer (`enforce` entry is that child id or a subtree path of the file) is an error. Every other sibling crossing is a warning, and a new one past the baseline still fails. `default: "deny"` denies every sibling crossing. `childSlices.allowedCrossSlice` may use a whole-segment `*` and clears only a sibling crossing. It does not clear `CROSS_PARENT_SLICE` or `CROSS_PARENT_VIA_SHARED`. The universe `allowedCrossSlice` still treats `*` as a literal. `childSlices.sliceAliases` maps a source path glob onto a child slice id (the universe id plus one child segment). An aliased file takes that universe id and that child id for both walls, so an import into another universe is still `CROSS_PARENT_SLICE`. A bare name, a universe id alone, an unknown universe, and a wildcard in `to` are rejected at config load. A glob that overlaps a slice folder is rejected. Two aliases that can match the same file are rejected. Doctor lists each alias as an owed move — the files, the target slice, and the destination folder — and does not count those files as finished. Adding aliases is a strengthening policy delta. Removing them is weakening. Additions and removals together, including a retarget, need a judgment. The baseline key does not include `reasonId`. Without `childSlices`, `reasonId` is absent on a direct edge. The write hook, ESLint, snippet analysis, ark-check, and CI all see this per-edge decision. arkgate 4.8.22 and older reject `childSlices`. A build that still types `siblings` as a string enum rejects the object at config load (`must be one of deny, advisory`). A build that still rejects unknown `childSlices` fields rejects `allowedCrossSlice` and `sliceAliases` at config load (`unknown field`). No published release through 4.8.22 accepts `sliceAliases`. Doctor may also suggest moving a flat universe-level file that exactly one child imports (`doctor.flatParentPilot`). That suggestion is not a finding, not a `ruleId`, and not part of the baseline key. A file imported by two children, or by none, is not suggested.
121
- - **`CROSS_PARENT_VIA_SHARED`:** `sharedImportsSlice` is `"deny-cross-parent"`. A slice reaches another universe only through a shared root. The finding's `file` is the slice that imported the shared root and `target` is the file in the other universe. The baseline key stays `ruleId|file|fromLayer|toLayer|target` and does not include `reasonId`. A shared hop that stays inside one universe is not this finding. The direct shared-root hop stays the `SHARED_IMPORTS_SLICE` warning. `"deny"` is unchanged and does not emit this reason. Doctor slice counts stay on `CROSS_PARENT_SLICE` and `CROSS_SIBLING_SLICE`. **ark-check and CI report this path. The write hook and ESLint see one edge at a time and do not block it.** arkgate 4.8.22 and older reject `"deny-cross-parent"` at config load.
126
+ - **Slice reasons:** the ruleId stays `LAYER_IMPORT_VIOLATION`. When a rule sets `childSlices`, the finding may also carry `reasonId` `CROSS_PARENT_SLICE` (this import crosses two universe slices; the child wall cannot allow another universe) or `CROSS_SIBLING_SLICE` (this import crosses two feature slices inside one universe). `siblings` may be `"deny"`, `"advisory"`, or `{ "default", "enforce", "ratchet" }`. An enforced importer (`enforce` entry is that child id or a subtree path of the file) is an error. Every other sibling crossing is a warning. With a baseline in use, a new advisory crossing fails only when the baseline already records an advisory crossing of that rule and that rule's advisory count grows past the recorded count (judged per rule; a recorded key whose crossing was removed still counts), or when `ratchet` is `true`; `ratchet: false` measures only. `default: "deny"` denies every sibling crossing. A `commonFolders` name counts only directly under the universe; the same folder name inside a child belongs to the child. The rule `message` is the universe-wall text; `childSlices.message` is the text for `CROSS_SIBLING_SLICE` and universe common importing a child (absent: ArkGate's default, never the rule message). `childSlices.allowedCrossSlice` may use a whole-segment `*` and clears only a sibling crossing. It does not clear `CROSS_PARENT_SLICE` or `CROSS_PARENT_VIA_SHARED`. The universe `allowedCrossSlice` still treats `*` as a literal. `childSlices.sliceAliases` maps a source path glob onto a child slice id (the universe id plus one child segment). A `from` with no wildcard in its last segment also covers its subtree. An aliased file takes that universe id and that child id for both walls, so an import into another universe is still `CROSS_PARENT_SLICE`. A bare name, a universe id alone, a target outside every universe shape, and a wildcard in `to` are rejected at config load; config load checks only the shape, and doctor flags a target universe no file belongs to (`unknownUniverse`). A glob that overlaps a slice folder is rejected. Two aliases that can match the same file are rejected. Doctor lists each alias as an owed move — the files, the target slice, and the destination folder — and does not count those files as finished. Adding aliases is a strengthening policy delta. Removing them is weakening. Additions and removals together, including a retarget, need a judgment. The baseline key does not include `reasonId`. Without `childSlices`, `reasonId` is absent on a direct edge. The write hook, ESLint, snippet analysis, ark-check, and CI all see this per-edge decision. Adapter diagnostics carry `evidence.reasonId` (plus `evidence.universeFrom` / `universeTo`) and a `nextAction` taken from that reason's own hint (an advisory `CROSS_SIBLING_SLICE` also gets the ratchet note; a blocking one gets only the action). arkgate 4.8.22 and older reject `childSlices`. A build that still types `siblings` as a string enum rejects the object at config load (`must be one of deny, advisory`). A build that still rejects unknown `childSlices` fields rejects `allowedCrossSlice` and `sliceAliases` at config load (`unknown field`). No published release through 4.8.22 accepts `sliceAliases`. Doctor may also suggest moving a flat universe-level file that exactly one child imports (`doctor.flatParentPilot`). That suggestion is not a finding, not a `ruleId`, and not part of the baseline key. A file imported by two children, by none, or by anything outside its one importing child (universe common, a shared root, another universe, tsconfig-alias importers included) is not suggested.
127
+ - **`CROSS_PARENT_VIA_SHARED`:** `sharedImportsSlice` is `"deny-cross-parent"`. A slice reaches another universe only through a shared root. The finding's `file` is the slice that imported the shared root and `target` is the file in the other universe. The baseline key stays `ruleId|file|fromLayer|toLayer|target` and does not include `reasonId`. A shared hop that stays inside one universe is not this finding. The direct shared-root hop stays the `SHARED_IMPORTS_SLICE` warning. `"deny"` is unchanged and does not emit this reason. Doctor slice counts stay on `CROSS_PARENT_SLICE` and `CROSS_SIBLING_SLICE`. **ark-check and CI report this path. The write hook and ESLint see one edge at a time and do not block it.** A universe edge the importer's rule declares in `allowedCrossSlice` is not reported through a shared root; `childSlices.allowedCrossSlice` never clears it. A composition root listed in `sharedImportsSlice.stopAt` (object form) stops the walk: a path through it is not a crossing. Config load rejects a stop that covers the whole tree or a whole layer root of the rule. Each finding carries `via` (the shared hops); doctor `sharedWalkHubs` names a shared file on at least half of ten or more such findings. `ark-check --changed` runs this pass full-tree when a changed file sits under such a rule's `sharedRoots`. arkgate 4.8.22 and older reject `"deny-cross-parent"` at config load; 4.8.23 and older reject the object form.
122
128
 
123
129
  <a id="LAYER_INTENT_REFERENCE_VIOLATION"></a>
124
130
 
@@ -138,6 +144,15 @@ Link form for agents: `docs/diagnostics.md#RULE_ID` (exact-case HTML anchors bel
138
144
  - **Why:** Snippet analysis found an intent or string reference that would couple layers in a direction the architecture profile forbids.
139
145
  - **Fix:** Move the reference to an allowed layer or introduce a port/event boundary, then re-run the snippet gate.
140
146
 
147
+ <a id="SHARED_IMPORTS_SLICE"></a>
148
+
149
+ ### `SHARED_IMPORTS_SLICE`
150
+
151
+ **A shared root imports a slice**
152
+
153
+ - **Why:** A declared shared root imports a slice directly. That hop is allowed while `sharedImportsSlice` is unset or `"deny-cross-parent"`, so this is advisory: the universe wall is direct-only.
154
+ - **Fix:** Move the code the shared root needs into the shared root, or invert the dependency. Set `sharedImportsSlice: "deny"` to make these edges errors. With `"deny-cross-parent"`, a slice that reaches another universe through a shared root is reported as `LAYER_IMPORT_VIOLATION` with `reasonId` `CROSS_PARENT_VIA_SHARED`. `stopAt` does not silence this warning. Advisory — it does not fail the check.
155
+
141
156
  <a id="CIRCULAR_DEPENDENCY"></a>
142
157
 
143
158
  ### `CIRCULAR_DEPENDENCY`
@@ -299,6 +314,15 @@ Link form for agents: `docs/diagnostics.md#RULE_ID` (exact-case HTML anchors bel
299
314
  - **Why:** `orchestration-only`, `thin-adapter`, and `writes-via-aggregate` only evaluate files the hint loader preloaded. When eligible governed files exceed that budget (`coverage.maxFiles`, default `400` — there is no `arkrules.hintBudget`), those sensors never saw the rest of their scope. Enforced + unreviewed is not green. The finding names exact hinted/governed counts and per-sensor reviewed N/M of scope.
300
315
  - **Fix:** Raise `coverage.maxFiles` in ark.config.json (this cap also bounds structural-hint preload; `--doctor` names the coupling) so hinted/governed counts match, then re-run with `--strict-config`. An enforced hint sensor that cannot see its scope fails strict.
301
316
 
317
+ <a id="ARKRULE_FILE_UNREFERENCED"></a>
318
+
319
+ ### `ARKRULE_FILE_UNREFERENCED`
320
+
321
+ **ArkRules file not referenced** · often advisory
322
+
323
+ - **Why:** A JSON file under `arkrules/` is not referenced by the `arkRules` map in ark.config.json (or no map exists), so none of its rules are enforced or reported. Drift looks like governance until someone notices.
324
+ - **Fix:** Reference the file from `arkRules` (`"<Layer>": "arkrules/<file>.json"`), or delete it. Advisory only: it never fails the check.
325
+
302
326
  <a id="INVARIANT_CATALOG_EMPTY"></a>
303
327
 
304
328
  ### `INVARIANT_CATALOG_EMPTY`
@@ -314,7 +338,7 @@ Link form for agents: `docs/diagnostics.md#RULE_ID` (exact-case HTML anchors bel
314
338
 
315
339
  **Invariant without coverage evidence**
316
340
 
317
- - **Why:** An ArkRules invariant is under contract but no covering test title or declared symbol evidence was found (or coverage is partial). A title is a `describe` / `it` / `test` / `context` string. A comment, string, test body, or import that only mentions the id does not count. `coverage.symbol` is a non-test declaration: `function`, `class`, `const`, `method`, `type`, `interface`, or `enum`. Kind is `never-had-tests` (adopt residual) vs `tests-disappeared` (suite exists). The message names what the scan found.
341
+ - **Why:** An ArkRules invariant is under contract but no covering test title or declared symbol evidence was found (or coverage is partial). A title is a `describe` / `it` / `test` / `context` string. A comment, string, test body, or import that only mentions the id does not count. `coverage.symbol` is a non-test declaration: `function`, `class`, `const`, `method`, `type`, `interface`, or `enum`; a `Class.member` symbol must be a member of `class Class` (or of a `const Class = class {}`, `namespace Class {}`, or `const Class = {}` object literal). An enforced invariant cannot opt out with `coverage.test: false` unless it names a `coverage.symbol`: that shape is reported here as "enforced but declares no evidence". Kind is `never-had-tests` (adopt residual) vs `tests-disappeared` (suite exists). The message names what the scan found.
318
342
  - **Fix:** Put the invariant id in a `describe` / `it` title, or declare the `coverage.symbol` name in a non-test file, then preflight again. A comment is not a title — move the id into the `it()` title. Treat never-had-tests as adopt residual; treat tests-disappeared as a regression. Missing test globs report partial — never fake green. When the message reports an exhausted file budget, raise `coverage.maxFiles` (or narrow `coverage.testGlobs`) in ark.config.json. The message also names every file the scan discarded and why (budget, per-file byte cap, unreadable, walk depth limit, symlink resolving outside the project root, no catalogued invariant named) — nothing is dropped in silence.
319
343
 
320
344
  <a id="INVARIANT_COVERAGE_OUTSIDE_ROOTS"></a>
@@ -355,13 +379,28 @@ Not adopted, no catalogued invariants, or every invariant sets `coverage.test: f
355
379
  Live adapters specialize `nextAction` with the call-site name or specifier when present
356
380
  (casual `enthusiastHint` + engineer `nextAction`). Catalog **Fix** is the stable no-target form.
357
381
 
382
+ The undeclared-* sensors only count a call whose receiver is **traced to the kernel**: a local
383
+ bound from a kernel factory, an identifier imported from a `kernelRoots` / `compositionRoots`
384
+ module (resolved through tsconfig `paths`, and through barrels that re-export a root), a plain
385
+ alias (`const kernel = ark`) or `typeof ark` binding, a binding typed `ArkKernel` / `EventBus` / `EventPublisher` / `ArkRunPublisher` imported
386
+ from `arkgate/runtime` or `arkgate/nestjs` (including `this.ark` constructor injection), its
387
+ `.eventBus`, a `publisher(..)` result, or destructured kernel members. `res.send`,
388
+ `require.resolve`, `subject.subscribe`, and other same-named methods are not kernel calls — unless
389
+ the receiver cannot be traced (an untyped parameter) **and** the literal name is a kernel-valid
390
+ intent (`Domain.…`, `Application.…`, the only names a kernel accepts); then it counts.
391
+ The call name is a string literal or a same-file `define(..)` / `defineIntent(..)` creator
392
+ (or string constant). When an enforced call names neither (for example an imported creator),
393
+ analysis reports completeness reason `ARKRUN_INTERACTION_NAME_INCOMPLETE` (partial, never green).
394
+ A `publisher(source)` binding without a literal is not incomplete — the emitted name is checked
395
+ on the chained `.publish` / `.send`.
396
+
358
397
  <a id="ARKRUN_MISSING_ROOT"></a>
359
398
 
360
399
  ### `ARKRUN_MISSING_ROOT`
361
400
 
362
401
  **No kernel factory in composition roots**
363
402
 
364
- - **Why:** The ArkRun extra is on but no createArkKernel / createStrictArkKernel / createArkKernelFromConfig / createStrictArkKernelFromConfig factory was found in arkRun.compositionRoots, so agents can skip the kernel while the write gate stays green.
403
+ - **Why:** The ArkRun extra is on but no kernel factory (createArkKernel / createStrictArkKernel / createLenientArkKernel, their *FromConfig variants, or ArkModule.forRoot / forRootAsync imported from arkgate/nestjs) was found in arkRun.compositionRoots, so agents can skip the kernel while the write gate stays green.
365
404
  - **Fix:** Import createStrictArkKernel from arkgate/runtime (same npm package; @arkgate/runtime is deprecated) and call it in a composition root listed in arkRun.compositionRoots, then preflight again. Never mechanical-safe — factory placement is a design decision.
366
405
 
367
406
  <a id="ARKRUN_KERNEL_IN_DOMAIN"></a>
@@ -370,7 +409,7 @@ Live adapters specialize `nextAction` with the call-site name or specifier when
370
409
 
371
410
  **Domain-role layer imports the kernel**
372
411
 
373
- - **Why:** A Domain-role layer imports arkgate/runtime, @arkgate/runtime, or kernel types. Domain stays kernel-free; composition roots and adapters own the factory.
412
+ - **Why:** A Domain-role layer imports arkgate/runtime, arkgate/nestjs, @arkgate/runtime, or kernel types. Domain stays kernel-free; composition roots and adapters own the factory.
374
413
  - **Fix:** Move the kernel import out of the Domain-role layer into a composition root or adapter. Import from arkgate/runtime (same npm package; @arkgate/runtime is deprecated), then preflight again. Never mechanical-safe.
375
414
 
376
415
  <a id="ARKRUN_DIRECT_NEW"></a>
@@ -388,7 +427,7 @@ Live adapters specialize `nextAction` with the call-site name or specifier when
388
427
 
389
428
  **Emit name not in raises/sends**
390
429
 
391
- - **Why:** A publisher / publish / raise / send call-site literal is not listed in the file’s raises or sends declaration.
430
+ - **Why:** A publisher / publish / raise / send call on a receiver traced to the kernel names an intent (string literal or same-file define/defineIntent binding) that is not listed in the file’s raises or sends declaration.
392
431
  - **Fix:** Add the existing call-site name to raises or sends on the managed component, then preflight again. Mechanical-safe only when that literal already exists and the edit is the declaration list; inventing a new emit stays judgment.
393
432
 
394
433
  <a id="ARKRUN_UNDECLARED_HANDLE"></a>
@@ -397,7 +436,7 @@ Live adapters specialize `nextAction` with the call-site name or specifier when
397
436
 
398
437
  **Handle name not in reactsTo**
399
438
 
400
- - **Why:** A subscribe / registerHandler call-site literal is not listed in the file’s reactsTo declaration.
439
+ - **Why:** A subscribe / registerHandler call on a receiver traced to the kernel names an intent (string literal or same-file define/defineIntent binding) that is not listed in the file’s reactsTo declaration.
401
440
  - **Fix:** Add the existing call-site name to reactsTo on the managed component, then preflight again. Mechanical-safe only when that literal already exists and the edit is the declaration list; inventing a new handle stays judgment.
402
441
 
403
442
  <a id="ARKRUN_UNDECLARED_DEPEND"></a>
@@ -406,7 +445,7 @@ Live adapters specialize `nextAction` with the call-site name or specifier when
406
445
 
407
446
  **Depend name not in uses**
408
447
 
409
- - **Why:** A resolve / resolveSingleton call-site literal is not listed in the file’s uses declaration.
448
+ - **Why:** A resolve / resolveSingleton call on a receiver traced to the kernel names a component that is not listed in the file’s uses declaration.
410
449
  - **Fix:** Add the existing call-site name to uses on the managed component, then preflight again. Mechanical-safe only when that literal already exists and the edit is the declaration list; inventing a new depend stays judgment.
411
450
 
412
451
  <a id="ARKRUN_TRANSPORT_BYPASS"></a>
@@ -446,7 +485,7 @@ Haken slaving: few slow keys (ξ) determine derived fast state. Field ingest nev
446
485
 
447
486
  **Generic update of ξ**
448
487
 
449
- - **Why:** A call to update/patch/set on the order plane rewrites the slow pattern. Haken slaving forbids generic ξ mutation.
488
+ - **Why:** A call to update/patch/set on the order plane rewrites the slow pattern. Haken slaving forbids generic ξ mutation. The static sensor only reports a receiver that is a plane on direct evidence: an identifier bound to `createOrderPlane(...)` in the same file, annotated `: OrderPlane`, a `plane` / `*Plane` named import from a declared `planeRoots` module, or a `plane` / `*Plane` name in a file that imports `arkgate/order` (also through `?.`, `!`, or `(x as T)`). A name alone is not evidence: `clipPlane.set(...)` / `controlPlane.update(...)` with no ArkOrder import stay silent, and so do `new Map().set(...)` or `prisma.x.update(...)` in the plane-root file.
450
489
  - **Fix:** Use release() for the first freeze of ξ. Later pattern change is proposeRelease then apply(ProposeResult). Never update/patch/set. Never mechanical-safe.
451
490
 
452
491
  <a id="ARKORDER_TOO_MANY_PARAMS"></a>
@@ -464,7 +503,7 @@ Haken slaving: few slow keys (ξ) determine derived fast state. Field ingest nev
464
503
 
465
504
  **ingest assigned into ξ**
466
505
 
467
- - **Why:** An ingest() result is written into a Release or ξ store. ingest may absorb, escalate_up, or hold; it never mints a pattern.
506
+ - **Why:** An ingest() result is written into a Release or ξ store. ingest may absorb, escalate_up, or hold; it never mints a pattern. Static evidence is a whole-word `xi` / `release` / `pattern` / `house` / `current` holder, an `xi` camelCase head (`xiNext`), or a `…Xi` / `…Release` / `…Pattern` tail (`nextXi`, `currentRelease`), as a binding or a property write such as `store.xi =` / `this.nextXi =`. Names that only start with those words (`currentResidual`, `patternResult`, `releaseState`) and comparisons (`===`) are not evidence — a deliberate false negative.
468
507
  - **Fix:** Keep ingest results as absorb/escalate_up/hold only. Change ξ with proposeRelease then apply(ProposeResult). Never mechanical-safe.
469
508
 
470
509
  <a id="ARKORDER_XI_FIELD_WRITE"></a>
@@ -473,7 +512,7 @@ Haken slaving: few slow keys (ξ) determine derived fast state. Field ingest nev
473
512
 
474
513
  **Slow key written around the order plane**
475
514
 
476
- - **Why:** A managed-layer file imports a persistence driver and writes a declared arkOrder.xiKeys name. Field events absorb or escalate; they do not PATCH the slow pattern.
515
+ - **Why:** A managed-layer file imports a persistence driver and writes a declared arkOrder.xiKeys name. Field events absorb or escalate; they do not PATCH the slow pattern. The write must go through a persistence client: `db` / `tx` / `client` / `prisma` / `drizzle`, an inline `new PrismaClient()` / `drizzle(...)`, a name bound in the file to a driver constructor (`const orm = new PrismaClient()`, a class field, or `this.orm = ...`), a name annotated with a driver client type (`constructor(private readonly orm: PrismaClient)`), or a named import from a local `db` / `database` / `prisma` / `drizzle` / `orm` module. `repo.update(...)` and a client from any other module name are not evidence.
477
516
  - **Fix:** Keep invoices, seats, hours, and logs on ingest. Change the slow key with proposeRelease then apply(ProposeResult), then preflight again. Never mechanical-safe.
478
517
 
479
518
  <a id="ARKORDER_INFORMATION_BUDGET"></a>
@@ -482,7 +521,7 @@ Haken slaving: few slow keys (ξ) determine derived fast state. Field ingest nev
482
521
 
483
522
  **Projection observes a forbidden kind**
484
523
 
485
- - **Why:** h(ξ) allowedKinds includes a kind listed in informationBudget.cannotObserve. A scale may not look at what it was told not to see.
524
+ - **Why:** h(ξ) allowedKinds includes a kind listed in informationBudget.cannotObserve. A scale may not look at what it was told not to see. Static sensor (`arkorder-information-budget`): a literal `allowedKinds: [...]` entry that a literal `cannotObserve: [...]` denies, in a file that calls createOrderPlane; computed arrays stay silent. Runtime: release, proposeRelease, apply, refreshSigma, and restore throw before the Release is offered or persisted; project and ingest re-check.
486
525
  - **Fix:** Cut that kind from the projector or from cannotObserve, then preflight again. Never mechanical-safe.
487
526
 
488
527
  <a id="ARKORDER_XI_TTL"></a>
@@ -491,7 +530,7 @@ Haken slaving: few slow keys (ξ) determine derived fast state. Field ingest nev
491
530
 
492
531
  **Slow key carries a freshness field**
493
532
 
494
- - **Why:** ξ named ttl/freshUntil/maxAge. Freshness belongs on σ. A slow parameter that expires per transaction is not slow.
533
+ - **Why:** ξ named ttl/freshUntil/maxAge. Freshness belongs on σ. A slow parameter that expires per transaction is not slow. Static sensor (`arkorder-xi-ttl`): such a key (including shorthand `{ plan, ttl }`) in the ξ literal (first argument) of `plane.release({...})` / `plane.proposeRelease({...})` on a plane receiver (same evidence as `ARKORDER_GENERIC_UPDATE`); σ (second argument) may carry freshUntil. The runtime plane throws the same code.
495
534
  - **Fix:** Move freshness onto σ (freshUntil) and keep ξ stable, then preflight again. Never mechanical-safe.
496
535
 
497
536
  <a id="ARKORDER_STALE_SIGMA"></a>
@@ -500,7 +539,7 @@ Haken slaving: few slow keys (ξ) determine derived fast state. Field ingest nev
500
539
 
501
540
  **σ is stale**
502
541
 
503
- - **Why:** ingest ran after σ.freshUntil (or sigmaMaxAgeMs). ξ does not TTL.
542
+ - **Why:** ingest ran after σ.freshUntil (or sigmaMaxAgeMs). ξ does not TTL. A numeric σ.freshUntil is honored on its own and wins over sigmaMaxAgeMs. Runtime only: ingest returns `hold` with reason `stale-sigma`; this is not a static sensor and `arkOrder.mode` does not promote it.
504
543
  - **Fix:** Call refreshSigma and ingest again, or proposeRelease then apply(ProposeResult) if the pattern changed. Never mechanical-safe.
505
544
 
506
545
  <a id="ARKORDER_UNVALVED_RELEASE"></a>
@@ -512,6 +551,17 @@ Haken slaving: few slow keys (ξ) determine derived fast state. Field ingest nev
512
551
  - **Why:** release() already froze the big choice. A later release() with a different value does not land. First freeze is release(); later change is proposeRelease then apply.
513
552
  - **Fix:** Change the choice with proposeRelease then apply. release() is only the first freeze. Never mechanical-safe.
514
553
 
554
+ `restore()` raises the same code on a live plane when the Release would change ξ or carry a version other than the current one or the next one (no rollback, no jump).
555
+
556
+ <a id="ARKORDER_STALE_PROPOSAL"></a>
557
+
558
+ ### `ARKORDER_STALE_PROPOSAL`
559
+
560
+ **Proposal is not bound to the current Release**
561
+
562
+ - **Why:** apply() got a ProposeResult computed against another Release (another apply landed first), one missing its base binding, or one whose reviewed blast radius is not the transition that would commit. The binding is data, not a capability: a proposal that states the current base and the exact transition applies whoever built it. Runtime only.
563
+ - **Fix:** Run proposeRelease again against the current Release, review the new blast radius, then apply that proposal. Never mechanical-safe.
564
+
515
565
  ## Atomic preflight and change sets
516
566
 
517
567
  <a id="INVALID_CHANGE_PATH"></a>
@@ -604,6 +654,15 @@ Haken slaving: few slow keys (ξ) determine derived fast state. Field ingest nev
604
654
  - **Why:** The host/MCP path could not run the atomic preflight engine (missing facts, incomplete setup, or unsupported mode).
605
655
  - **Fix:** Use resolved-candidate facts / ark_prepare_change with a complete batch, or fall back to ark-check on disk. Do not treat missing preflight as green.
606
656
 
657
+ <a id="WRITE_GATE_UNAVAILABLE"></a>
658
+
659
+ ### `WRITE_GATE_UNAVAILABLE`
660
+
661
+ **Write gate could not run**
662
+
663
+ - **Why:** The write gate could not load its own inputs (`ark.config.json` missing, unreadable, or invalid; a referenced ArkRules file missing or invalid; or the built library missing), so it cannot judge this governed source write (existing files included), or a write would leave `ark.config.json` or a referenced ArkRules file unloadable. No checker, no write: the hook blocks instead of letting the write through unchecked.
664
+ - **Fix:** Fix the reported input (repair `ark.config.json` or the ArkRules file, or run `npm run build` / reinstall arkgate from npm), then retry the same write. Do not remove the hook to get past it.
665
+
607
666
  <a id="DESIGN_SMELL_REGRESSION"></a>
608
667
 
609
668
  ### `DESIGN_SMELL_REGRESSION`
@@ -631,7 +690,7 @@ Haken slaving: few slow keys (ξ) determine derived fast state. Field ingest nev
631
690
  **Lexical evidence incomplete**
632
691
 
633
692
  - **Why:** This check only saw one file, so it cannot fully prove how the import resolves. The result is provisional — `ark-check` on the project is the authority.
634
- - **Fix:** Run `npx arkgate-check --root . --config ark.config.json` to confirm. Do not call `ark_prepare_change` from a hook deny.
693
+ - **Fix:** For a complete verdict, run `npx arkgate-check --root . --config ark.config.json` (or `ark_prepare_change` over MCP with the full candidate batch). Read `lexicalValid` for the one-file result. The write hook adds its own hook-only note to its deny text.
635
694
 
636
695
  <a id="ANALYSIS_COVERS_NO_FILES"></a>
637
696
 
@@ -913,10 +972,10 @@ never opting out of knowing.
913
972
 
914
973
  ### `CONFIG_CHILD_SLICES_VERSION`
915
974
 
916
- **childSlices needs a newer arkgate**
975
+ **Config needs a newer arkgate than a pinned copy**
917
976
 
918
- - **Why:** The rule schema sets additionalProperties to false. arkgate 4.8.22 and older reject a config that contains childSlices instead of ignoring the key.
919
- - **Fix:** Pin arkgate newer than 4.8.22 before shipping a config that sets childSlices. This warning does not fail the check.
977
+ - **Why:** `ark.config.json` uses a slice key (`childSlices`, `sharedImportsSlice` `"deny-cross-parent"` or its object form, `sliceIdentity`, `sharedImportsSlice`, `childSlices.message`, `siblings.ratchet`) that an arkgate pinned in this repo rejects at config load. Emitted only with evidence of that pin: an exact `package.json` dependency, the installed `node_modules/arkgate`, `package-lock.json`, a `scripts/*hook*` or host hook command, or a CI workflow. Silent otherwise. The finding's location is that file and line.
978
+ - **Fix:** Bump the named file to the version the finding's `nextAction` gives (or newer), then run Ark again. This warning does not fail the check.
920
979
 
921
980
  <a id="CONFIG_CHILD_SLICE_EXTENDS"></a>
922
981
 
@@ -936,6 +995,24 @@ never opting out of knowing.
936
995
  - **Why:** childSlices.allowedCrossSlice names a pattern that can match two universes. That list clears only a sibling crossing inside one universe. The universe wall still denies the edge.
937
996
  - **Fix:** Narrow the pattern so both sides share one universe prefix (features/projects/* to features/projects/d2d-item), or remove the entry. This warning does not fail the check.
938
997
 
998
+ <a id="CONFIG_CHILD_SLICES_INERT"></a>
999
+
1000
+ ### `CONFIG_CHILD_SLICES_INERT`
1001
+
1002
+ **childSlices on a rule that cannot run it**
1003
+
1004
+ - **Why:** A rule sets childSlices without peerIsolation: true and allowed: false. The child wall runs only inside a universe wall, so on this rule childSlices enforces nothing. Config load still accepts it (arkgate 4.8.23 did too).
1005
+ - **Fix:** Add "peerIsolation": true and "allowed": false to the same rule so the child wall runs, or remove childSlices. This warning does not fail the check.
1006
+
1007
+ <a id="CONFIG_SLICE_LEGACY_STARS_ID"></a>
1008
+
1009
+ ### `CONFIG_SLICE_LEGACY_STARS_ID`
1010
+
1011
+ **Slice id written in the 4.8.23 stars form**
1012
+
1013
+ - **Why:** Under sliceIdentity "stars", a sliceAliases target or an allowedCrossSlice entry names the 4.8.23 stars id, which dropped a star binding before the last literal (modules/*/api/* bound api/v1). The id now keeps that binding (orders/api/v1). For one release the entry keeps its 4.8.23 meaning, so verdicts do not change.
1014
+ - **Fix:** Rewrite the entry with the new id the warning names (for example orders/api/v1/x instead of api/v1/x). A sliceAliases target that maps to more than one universe shape is rejected at config load. This warning does not fail the check.
1015
+
939
1016
  ## Meta
940
1017
 
941
1018
  <a id="ARK_UNKNOWN"></a>
@@ -66,25 +66,25 @@ hardening guide remains repository-hosted rather than duplicated in the gate tar
66
66
  | **Governance weight** | `ark-check --doctor --json` → `doctor.contractHealth.governanceWeight` | Additive, **advisory only** — raw facts (`declaredLayers`, `populatedLayers`, `governedFiles`, `rules`, `deniedEdges`, `allowedEdges`, `filesPerLayer`, `rulesPerLayer`) plus a fixed comparative band `weight: heavy | typical | light | unknown` and its fixed `note`. Fixed deterministic thresholds: **heavy** = fewer than 25 governed files per declared layer AND (6+ layers OR 4+ well-formed rules per layer); **light** = at most 2 layers over 150+ governed files; **unknown** = no layers or no governed files; everything else is **typical** (banding uses raw ratios; the reported ratios are rounded for display). `notAScore: true` is explicit: never a composite score, ranking, or gate input; the heavy note asks to justify NEW layers/rules and never suggests deleting working ones. Human doctor prints a line only for `heavy`/`light`. |
67
67
  | **Report parity and snapshot evidence (4.2)** | `ark-check --report` → advisory sections (`data-advisory="contractHealth\|ambientState\|parseHealth\|arkRun"`, nested `governanceWeight`) + layer wall badges; `.ark/reports/*.json` | The report is a rendering of doctor truth. **Standing rule:** every doctor advisory ships with its report section — enforced by the `reportParity` guard, which enumerates the doctor's advisory keys and fails on any missing section. Snapshots add best-effort Git `HEAD`/branch/dirty provenance without a shell; unavailable Git is explicit. Evolution renders the Ark score delta only when both snapshots name the same ArkGate version, while retaining raw facts across versions. Thin `arkRun` on `latest.json` is `notAScore` residual honesty for `ark status`. |
68
68
  | **MCP project identity (4.2)** | `ark_identity`; `arkgate/schema/project-identity` or `arkgate/schema/ark.project-identity.schema.json`; root API constants/helpers/types | Schema `1.0`. `projectId` hashes canonical root + config path and stays stable across contract edits/restarts; runtime id/start time are separate. Every project-bound tool result and error carries `projectIdentity`, `binding` (`matched` / `unverified` / `mismatch`), and `authoritative`. Canonical out-of-root config/file evidence fails before project data. |
69
- | **MCP tools and compatibility resource** | `arkgate-mcp`; `ark_manifest`; `ark_status`; `ark://manifest` | Tool names and primary argument shapes are stable within a major. Every tool accepts additive `project.expectedRoot` / optional `expectedProjectId`. The initial handshake requires the exact project root; a contained descendant is authoritative only together with the matching project id. Legacy tool calls remain callable but `unverified` and non-authoritative. `ark_manifest` is the authoritative contract surface after binding. **`ark_status`** returns the status manifest envelope (parity with `ark status --json`). Standard `resources/read` cannot portably carry the expectation, so `ark://manifest` remains compatibility-only and always unverified/non-authoritative. The server never retargets from input. |
69
+ | **MCP tools and compatibility resource** | `arkgate-mcp`; `ark_manifest`; `ark_status`; `ark://manifest` | Tool names and primary argument shapes are stable within a major. Every tool accepts additive `project.expectedRoot` / optional `expectedProjectId`. The initial handshake requires the exact project root; a contained descendant is authoritative only together with the matching project id. Legacy tool calls remain callable but `unverified` and non-authoritative. `ark_manifest` is the authoritative contract surface after binding. **`ark_status`** returns the status manifest envelope (parity with `ark status --json`). Standard `resources/read` cannot portably carry the expectation, so `ark://manifest` remains compatibility-only and always unverified/non-authoritative. The server never retargets from input. Every published `outputSchema` has top-level `type: "object"` and the returned `structuredContent` validates against it (MCP SDK clients check both). A contract edit after startup makes project tools return `CONTRACT_STALE` until restart. `arkgate-mcp --help` / `--version` print and exit. |
70
70
  | **`ark.config.json`** | Layer globs, optional `layers[].description`, optional `layers[].trustBoundary`, optional `layers[].owners`, optional `requireLayerOwners`, rules, include/exclude, forbiddenGlobals, intent prefixes, `peerIsolation`, `dynamicImportAllowlist`, `safety` thresholds; optional **`coverage`** controls (`testGlobs`, `maxFiles`); optional **`arkRules`** map (schema `1.1+`); optional **`arkRun`** extra (schema `1.2+`); optional **`arkOrder`** extra (schema `1.3+`) | Versioned by `schemaVersion`; unknown fields fail closed and migrations preserve the previous supported major. Absence of `coverage`, `arkRules`, `arkRun`, or `arkOrder` is byte-for-byte silent on Layers / ArkRules verdicts. Enforced extra teeth share the CLI / MCP / hook / preflight / CI verdict and arm only when the layer plane is classified (same ArkRules floor). |
71
71
  | **Layer caption (`layers[].description`, 4.8.7)** | Optional string on each layer. Projected onto `ark_place` / prepare-write / MCP place JSON, doctor JSON + human, coverage JSON, and the HTML Purpose column when present. `/ark-adopt` writes it from the product map or glossary; `/ark-place` prints it next to layer name + globs. | Existing optional field — **no `schemaVersion` bump**, no new key, no 14th skill. Copy is **app context** (a product sentence such as `Purchase requests — from asked to received.`), not architecture jargon. Stripped from `policyHash` like `stewards`; caption-only edits are neutral. Absence is silent: never a residual, never a score, never `--strict-config` fail, never flips `valid`. Compact starters may omit. |
72
72
  | **Layer trust (`layers[].trustBoundary`)** | Optional closed tag on each layer: `public` \| `auth` \| `admin` \| `internal`. Projected onto `ark_place` / prepare-write / MCP place JSON, doctor JSON + human, coverage JSON, and the HTML report when present. `/ark-adopt` writes it when the product map names that door; `/ark-place` prints `trust: public`. | Additive optional field — **no `schemaVersion` bump**, no new skill. Stripped from `policyHash` like `stewards` and `layers[].description`; tag-only edits are neutral. Absence is silent: never a residual, never a score, never `--strict-config` fail, never flips `valid`. Invalid values fail the schema. Compact starters may omit. Not host/CI TLS. Not an import-rule matrix. |
73
73
  | **Layer owners (`layers[].owners` + `requireLayerOwners`)** | Optional GitHub handles or emails on each layer. Projected onto place / prepare-write / MCP / doctor / coverage / HTML when present. `requireLayerOwners: true` asks for owners on every live (non-reserved) layer: doctor names the first missing house, `--strict-config` refuses, and the write gate denies a write into that house. | Additive optional fields — **no `schemaVersion` bump**, no new skill. Owners are stripped from `policyHash` like captions; owner-only edits are neutral. The require flag stays in the hash. Absence of owners is silent unless the require flag is on. Invalid identity (display name) fails the schema. Compact starters may omit. Not import-rule teeth. No `/ark-owners`. |
74
- | **ArkRules inventory / under-contract (4.0; layer context 4.2)** | `ark-check --rules-inventory [--json]`; doctor `rulesUnderContract`; MCP `ark_rules_inventory` | Additive. Honest counts (inventoried / under-contract / frozen) — **never a score**. When configured layer evidence exists it overrides filename role guesses: a Domain file named `handler` is not a controller candidate. Test/fixture/seed/migration/exclusion surfaces plus narrow development-identity, PostgreSQL OID, and technical I/O constants are silent. Without layer evidence, backward-compatible path/content heuristics remain. Structure/invariant diagnostics use adapter `1.4` provenance. |
74
+ | **ArkRules inventory / under-contract (4.0; layer context 4.2)** | `ark-check --rules-inventory [--json]`; doctor `rulesUnderContract` + `rulesMigration`; MCP `ark_rules_inventory` | Additive. Honest counts (inventoried / under-contract / frozen) — **never a score**. Structure candidates count as under contract by declared (layer, sensor); frozen counts `ARKRULE_*` / `INVARIANT_*` baseline keys. CLI and MCP payloads share one builder (same keys, incl. `coverageEvidence`). When configured layer evidence exists it overrides filename role guesses: a Domain file named `handler` is not a controller candidate. Test/fixture/seed/migration/exclusion surfaces plus narrow development-identity, PostgreSQL OID, and technical I/O constants are silent. Without layer evidence, backward-compatible path/content heuristics remain. Structure/invariant diagnostics use adapter `1.4` provenance. |
75
75
  | **`arkgate/schema/project-identity`** or **`arkgate/schema/ark.project-identity.schema.json`** | MCP canonical project, contract, runtime, expectation, and binding envelope | Schema `1.0`. Initial `expectedRoot` must be the exact project root. A contained descendant can match only when `expectedProjectId` is also present and correct; id-only matching stays non-authoritative. Mismatch codes are `PROJECT_ROOT_MISMATCH`, `PROJECT_ID_MISMATCH`, and `INVALID_PROJECT_EXPECTATION`. |
76
76
  | **Package pin dual-truth (4.0)** | doctor JSON `packageVersionTruth`; upgrade JSON/human note when pin behind CLI | Additive, advisory. Surfaces after `upgrade --no-install` when managed CLI is ahead of package.json. |
77
77
  | **Managed upgrade self-service honesty (4.5 / DF05)** | `ark upgrade [--json]` → `selfService` (+ human “Self-service honesty” lines) | Additive, advisory. Answers without a maintainer: write-path activation labels per selected host (`hard`\|`advisory`\|`unavailable`) and customized content-identity preserve (`customizedPaths` / `customizedContentPreserved`). Soft hosts never hard; upgrade never invents `hardWriteActive` from disk alone. Always `notAScore: true`. Not a gate input; not part of `planDigest`. |
78
78
  | **Product honesty readiness split (4.1.1)** | doctor JSON `productHonesty` | Additive. `unfinished` / `headline` / `primaryNextAction` / `reasonIds` remain; EH adds `contractReadiness` (`ready`\|`partial`\|`not-ready`), `localWriteBoundary` (`advisory`\|`hard`\|`unverified`\|`unknown`), `architectureReasonIds`, `environmentResidualIds` / `environmentResiduals`. Soft-write hosts stay in evidence without alone forcing global **Not finished**. `notAScore: true` always. A physical-cohesion residual that reshape decisions have not suppressed adds `physical-cohesion-residual`, which clears `elegant` and `finished` and sets the headline to residual remaining; the gate verdict, `designFitness`, `designWeak`, and `patternBets` stay unchanged. When that residual still proposes `reshapePilot.nextPilot`, `pilotLoop` is active with that one extraction card. |
79
79
  | **Policy transition analysis (3.1.0)** | `analyzePolicyDelta(...)`; MCP `ark_policy_delta`; CLI `--policy-base` / `--policy-base-ref` / `--policy-ack`; check JSON `policyDelta` | Additive schema `1.0`. Classifications and finding ids are deterministic. Weakening/judgment requires an acknowledgement bound to both policy hashes and the exact blocking finding set. A new layer or new allow edge is `judgment-required`. On the CLI/MCP policy-ack plane, `adrPath` must name a short note under `docs/adr/` or `docs/decisions/` (`policyDelta.adrNote`). Domain hash match stays I/O-free. |
80
- | **Team parliament (law vs feature)** | Optional `stewards` on `ark.config.json` (GitHub handle or email); CLI `--local` / `--changed` / `--against` / `--base` / `--contract-diff` / `--contract-session` / `--persona` / `--author`; check JSON `teamParliament`; `ark status --vs`; write-gate mixed-batch deny | Additive. Law files must not mix with product source. Loosen / baseline-grow are steward-only when `stewards` is set. `--against` ratchets vs the base-ref baseline. `--changed` scans touched sources plus their import closure. `--local` (or `ARK_CHECK_LOCAL=1`) is the same cheap path and stays per `--root`; it cannot combine with `--strict-merge`. Check JSON may add `local`, `scope: "changed"`, `analysisRoot`. `stewards`, `layers[].description`, `layers[].trustBoundary`, and `layers[].owners` are excluded from policy hash. `requireLayerOwners` stays in the hash. Identity is handle or email, not git `user.name`. No org plane. |
80
+ | **Team parliament (law vs feature)** | Optional `stewards` on `ark.config.json` (GitHub handle or email); CLI `--local` / `--changed` / `--against` / `--base` / `--contract-diff` / `--contract-session` / `--persona` / `--author`; check JSON `teamParliament`; `ark status --vs`; write-gate mixed-batch deny | Additive. Law files must not mix with product source. Loosen / baseline-grow are steward-only when `stewards` is set. `--against` ratchets vs the base-ref baseline. `--changed` scans touched sources plus their import closure. `--local` (or `ARK_CHECK_LOCAL=1`) is the same cheap path and stays per `--root`; it cannot combine with `--strict-merge` or `--update-baseline`. Changed paths are relative to `--root` (monorepo packages included). A missing base ref or failed git listing is exit 2 (`changed-needs-base` / `local-needs-base` / `contract-diff-needs-base` for `--contract-diff` and `--persona steward` / `changed-paths-unavailable`), never a pass. `--report` applies the committed baseline implicitly except under `--strict` / `--strict-merge` / `--contract-diff` / `--against`. Check JSON may add `local`, `scope: "changed"`, `analysisRoot`. `stewards`, `layers[].description`, `layers[].trustBoundary`, and `layers[].owners` are excluded from policy hash. `requireLayerOwners` stays in the hash. Identity is handle or email, not git `user.name`. No org plane. |
81
81
  | **Atomic change preflight (3.1.0)** | `preflightChange(...)`; CLI `ark preflight --changes <file> --json`; MCP `ark_prepare_change` | Additive schema `1.0`. One complete governed production-source `{path,content}` / `{path,delete:true}` batch; read-only; returns operation, content/tree/policy/compiler fingerprints and stable graph findings. MCP availability alone is advisory. |
82
82
  | **Architecture change map (3.1.0)** | `arkgate/schema/change-map` or `arkgate/schema/ark.change-map.schema.json`; CLI `ark preflight --change-map <file>`; MCP `ark_prepare_change.changeMap` | Optional strict schema `1.0`. Canonical planned paths + operations + resolved Ark layers + dependencies between planned files. Preflight returns `changeMapHash`; absence is normal and adds no project file. Structural intent only, never behavioral completion. |
83
83
  | **Structural convergence (3.1.0)** | `analyzeArchitectureConvergence(...)`; map-enabled `preflightChange(...)`; existing CLI/MCP preflight adapters | Additive `convergence` result with stable `satisfied`, `missing`, `contradictory`, and `unplanned` findings. Uses the supplied/current project tree as base and the explicit complete change set as candidate; no implicit Git or LLM input. `readOnly: true`; `behavioralCompletion: "not-evaluated"`. Structural mismatch makes preflight invalid. |
84
84
  | **Enforcement ladder + fixed journey (3.1.0)** | `doctor.writePath.enforcementLadder`; hook repair `enforcement`; `npm run eval:change-integrity` | Additive schema `1.0` separates supported/installed/active/bypassable state and evidence. Hard is operation-scoped only for a supported covered hook; MCP is advisory; required CI status stays unverified locally. Fixed no-context fixture proves CLI/MCP/hook/final parity, one casual denial, acceptance behavior, and strict Ark. |
85
85
  | **Enforcement state** | `doctor.writePath.enforcementState`; schema/type | Schema `1.1`: runtime observation, operation coverage, and operation-scoped `hard`. Only fresh covered active-host evidence permits `hard:true`; unverified assets and MCP remain non-hard. |
86
86
  | **Design delta** | `--strict-merge` / `--strict` (created paths when a Git base exists); `--fail-on-new-smells --base-ref <ref>` (full ratchet); hook/MCP; schema/types | Schema `1.0`: identities, touched paths, optional `enforcementScope`. Default merge blocks **created** `domain-logic-in-ui` files vs merge-base; missing base skips (does not exit 2). Full new+worsened on touched paths remains opt-in `--fail-on-new-smells` (missing base fails closed). Historical residual and worsened-in-existing-file stay green on `--strict-merge`. Global doctor smells stay advisory. |
87
- | **`arkgate/schema/analysis-result`** or **`arkgate/schema/ark.analysis-result.schema.json`** | Public CLI/MCP/hook diagnostic envelope (`schemaVersion`, `mode`, `valid`, `completeness`, `completenessReasons`, `diagnostics`, resolved identities) | Schema **`1.5`** adds optional stable finding refs on diagnostics: `findingRef` (`fnv1a-` + hex), `targetKey` (baseline-compatible freeze identity), `docsCodePath` (`docs/diagnostics.md#RULE_ID`). Factory-emitted diagnostics always include them; consumer-owned 1.0–1.4 values remain valid without them. `1.4` added optional `evidence.arkruleId` / `evidence.arkruleSource` for ArkRules; `1.3` distinguished `resolved-candidate-facts` from `lexical-compatibility`; partial/unavailable analysis is always non-green, and resolved complete/partial results require policy/resolver/facts/tree identities. `1.2` added completeness and remains accepted alongside consumer-owned 1.0/1.1 values. |
87
+ | **`arkgate/schema/analysis-result`** or **`arkgate/schema/ark.analysis-result.schema.json`** | Public CLI/MCP/hook diagnostic envelope (`schemaVersion`, `mode`, `valid`, `completeness`, `completenessReasons`, `diagnostics`, resolved identities) | Schema **`1.5`** adds optional stable finding refs on diagnostics: `findingRef` (`fnv1a-` + hex), `targetKey` (baseline-compatible freeze identity), `docsCodePath` (`docs/diagnostics.md#RULE_ID`). Factory-emitted diagnostics always include them; consumer-owned 1.0–1.4 values remain valid without them. Optional `evidence.reasonId` (`CROSS_PARENT_SLICE` / `CROSS_SIBLING_SLICE` / `CROSS_PARENT_VIA_SHARED`) with `evidence.universeFrom` / `universeTo` is additive within `1.5` and outside the baseline key. `1.4` added optional `evidence.arkruleId` / `evidence.arkruleSource` for ArkRules; `1.3` distinguished `resolved-candidate-facts` from `lexical-compatibility`; partial/unavailable analysis is always non-green, and resolved complete/partial results require policy/resolver/facts/tree identities. `1.2` added completeness and remains accepted alongside consumer-owned 1.0/1.1 values. |
88
88
  | **Stable finding refs (4.3)** | Root API `adapterFindingTargetKey` / `adapterFindingRefFromTargetKey` / `toAdapterDiagnostic` / `createAdapterResult`; CLI/MCP/repair envelopes via analysis-result diagnostics | Multi-turn re-address without fuzzy message match. `targetKey` **is** the baseline (occurrence) key so freeze identity is never orphaned; `findingRef` is a compact FNV-1a of that key. Line/message drift does not change the ref. Multi-turn fixture: `tests/fixtures/finding-refs/multi-turn-stability.json`. |
89
89
  | **Diagnostic code catalog** | Root API `DIAGNOSTIC_CATALOG` / `getDiagnosticCatalogEntry` / `diagnosticDocsPath`; docs [diagnostics.md](diagnostics.md) (`#RULE_ID` anchors) | Closed vocabulary of public `ruleId`s with why/fix anchors. Cataloguing only — no new rule semantics. Remediation parity is test-guarded. ArkRun `ARKRUN_*` codes (RN05) share dual-depth `nextAction` / `enthusiastHint`; declaration-list adds are mechanical-safe only when the call-site literal already exists. Docs ship in the npm tarball. |
90
90
  | **Status manifest** | CLI `ark status [--json] [--vs <ref>]`; MCP `ark_status`; `arkgate/schema/status-manifest`; root API `buildStatusManifest` / `ARK_STATUS_MANIFEST_SCHEMA` / `projectStatusImprovementCompass` | Schema `1.0`. One session/project snapshot: identity binding, honest write-path activation (`hard`\|`advisory`\|`unavailable`), last-check summary, rules residual counts, primary next action, **`improvementCompass`** with honesty **`mode`** `full`\|`subset`\|`unavailable` (residual ids only; always `notAScore: true`; optional `reasonCode`/`reason`/`factsSource`/`contractHash`), optional **`vsBase`** (pin / contract / baseline grow vs a git ref; advisory only), and additive **`arkRun`** (`notAScore`; `present` / `mode` / `extraMergeTeeth` / residual count — null residual is unknown, not green). **Not a score.** Residual never changes gate verdicts. Never prompts (`CI=1` forces JSON). Optional `--expected-root` / `--expected-project-id` (MCP `project`) for matched vs stale identity. |
@@ -94,7 +94,7 @@ hardening guide remains repository-hosted rather than duplicated in the gate tar
94
94
  | **`arkgate/schema/resolved-candidate-facts`** or **`arkgate/schema/ark.resolved-candidate-facts.schema.json`** | Versioned parity-capable input for `analyzeResolvedProject` / `preflightResolvedChange` | Schema `1.2` is additive: optional `classShapes` (1.1) plus ArkRun `arkRunKernelCalls` / `arkRunManagedNews` / `arkRunCompositionRootHits` / `arkRunDeclarations` (RN03–RN04) and ArkOrder `arkOrderPlaneCalls` / `arkOrderGenericUpdates` / `arkOrderRootHits` (OR05). `1.0`/`1.1` payloads remain loadable. Tooling owns filesystem/compiler resolution; Domain/Kernel validate and evaluate supplied facts without importing those effects. Facts name resolver/compiler inputs, governed files, dependency evidence, completeness reasons, candidate tree/facts hashes, and (when present) ArkRun/ArkOrder call-site evidence. Tier-1 sensors emit `ARKRUN_*` / `ARKORDER_*` diagnostics from those facts: advisory never flips `valid`; enforced blocks. Extra absence is silent. |
95
95
  | **Config JSON Schema** | `arkgate/schema` or `arkgate/schema/ark.config.schema.json` | Stable package resource subpaths for editor completion and contract tooling. |
96
96
  | **Agent skills** | `/ark-*` templates; install via `--install-agent-gates` (often `--skills-only` on top of compact) **or** Agent Skills ecosystem path | **Day zero** is the compact router from `ark start` / `start --apply` + doctor — not the full skill pack. Closed catalog (`ARK_SKILL_NAMES`): first-class doors (including `/ark-order`) plus one-release stubs. Guided expert path remains `/ark-autopilot` after pack install. **4.0:** first-class doors except experimental `/ark-runtime` / `/ark-order` integrate **layers + ArkRules** and must label residual `[Layer]` vs `[ArkRules]`. **4.2:** repo catalogs are content-idempotent; the optional shared Codex home catalog is monotonic across 4.2.0+ installers. Pre-4.2 writers are outside that protocol and must be upgraded first. A durable pending-catalog journal preserves the floor across an interrupted install and is cleared only by its owning same/newer recovery. **4.3:** Agent Skills–compatible layout at `templates/agent-skills/<name>/SKILL.md` (1:1 with flat `templates/skills/*.md`); install via `npx skills add ./node_modules/arkgate/templates/agent-skills` (or the GitHub tree). **#216:** first-class `/ark-order`; ACS05 freeze opened (ADR 0036). Domain `ARK_SKILL_NAMES` + `validateAgentSkillsPackage` + `validateSkillProductCapacity`; drift `npm run check:agent-skills` (layout **and** 100% product coverage). Skills never enforce. |
97
- | **ESLint subpath** | `arkgate/eslint` | Config-driven layer/import/purity rules plus ArkRun import/`new` envelope (`ark/no-arkrun-kernel-in-domain`, `ark/no-arkrun-direct-new`, `ark/no-arkrun-transport-bypass`) when `arkRun` is on, and ArkOrder envelope (`ark/no-arkorder-kernel-in-domain`, `ark/no-arkorder-generic-update`) when `arkOrder` is on; loads consumer `ark.config.json`. Absence of an extra is silent. Missing-root / missing-plane and undeclared-* stay CLI/MCP. |
97
+ | **ESLint subpath** | `arkgate/eslint` | Config-driven layer/import/purity rules plus ArkRun import/`new` envelope (`ark/no-arkrun-kernel-in-domain`, `ark/no-arkrun-direct-new`, `ark/no-arkrun-transport-bypass`) when `arkRun` is on, ArkOrder envelope (`ark/no-arkorder-kernel-in-domain`, `ark/no-arkorder-generic-update`) when `arkOrder` is on, and ArkRules structure sensors (`ark/arkrules-structure`) when `arkRules` is on; loads consumer `ark.config.json`. Non-blocking findings (ark-check warnings) report on the warn-level `ark/architecture-advisory` (when that rule is not enabled they fall back to the blocking rule id, tagged advisory). `require('arkgate/eslint')` and the ESM default are the same plugin object (`meta`, `rules`, `configs`). Absence of an extra is silent. Missing-root / missing-plane, undeclared-*, and ArkRules invariant coverage stay CLI/MCP. |
98
98
  | **GitHub Action** | `pedroknigge/arkgate` (see `action.yml`) | The `uses:` tag/SHA selects the checker source; `version` remains an optional exact npm compatibility override. |
99
99
  | **Package metadata** | `arkgate/package.json` | Stable resource subpath for tooling that needs the installed manifest. |
100
100
 
@@ -141,6 +141,8 @@ are grouped below.
141
141
  | Design delta contract | `ARK_DESIGN_DELTA_SCHEMA_VERSION` |
142
142
  | Analysis IR + resolved facts | `ANALYSIS_IR_SCHEMA_VERSION`, `RESOLVED_CANDIDATE_FACTS_SCHEMA_VERSION`, `RESOLVED_CANDIDATE_FACTS_SCHEMA`, `createResolvedCandidateFacts`, `loadResolvedCandidateFacts`, `resolvedFactsEvidenceRequirementsHash`, `deterministicHash`, `stableSerialize` |
143
143
  | Config contract | `ARK_CONFIG_SCHEMA`, `ARK_CONFIG_SCHEMA_VERSION`, `loadArkConfigContract`, `parseArkConfigJson` |
144
+ | ArkRules contract | `ARK_RULES_SCHEMA`, `ARK_RULES_SCHEMA_VERSION`, `ARK_RULE_SENSORS`, `ArkRulesValidationError`, `buildEffectiveArkRules`, `emptyEffectiveArkRules`, `loadArkRulesContract`, `parseArkRulesJson` (`parseArkRulesJson` / `loadArkRulesContract` throw `ArkRulesValidationError` with `.issues: ArkRulesIssue[]`) |
145
+ | Effective contract | `EffectiveContractError`, `effectiveContractPolicyPayload`, `resolveEffectiveContract` |
144
146
 
145
147
  The type-only root exports are also semver-supported:
146
148
 
@@ -178,6 +180,7 @@ The type-only root exports are also semver-supported:
178
180
  `AnalysisFile`, `AnalysisImportEdge`, `AnalysisCapabilityUse`, `AnalysisEvidence`,
179
181
  `AnalysisViolation`, `AnalysisIr`.
180
182
  - Config contract: `ArkConfig`, `ArkConfigLoadResult`.
183
+ - ArkRules contract: `ArkRulesFile`, `ArkRulesIssue`, `EffectiveArkRules`.
181
184
 
182
185
  Runtime-kernel factories, `CAPABILITY_IDS`, `collectCapabilityUses`, and Domain capability mapping
183
186
  helpers are deliberately absent from this root. Use `arkgate/runtime` for the
@@ -195,9 +198,9 @@ claims. Static architecture enforcement does not depend on them.
195
198
 
196
199
  | Surface | Import path | Notes |
197
200
  |---------|-------------|--------|
198
- | **ArkRun kernel** | **`arkgate/runtime`** | Public brand **ArkRun**. Same npm package `arkgate` (ADR 0031). Factory `createStrictArkKernel` (each call is an isolated instance; no process-wide `getKernel()` singleton). Root export does **not** include the factory. Optional extra `arkRun` on schema `1.2+`. Event bus, intents, policies, sagas, event buffer, projections, and strict helpers. Managed components declare `uses` / `reactsTo` / `raises` / `sends` on `register()`; `getDependencyInformationPackage()` is a JSON snapshot of ids, lifetime, and declarations and never includes factories, live instances, or input DTOs (ADR 0023). `requestGraph()` slices that snapshot into **process** or **technical** graphs with optional `nodeIds`, `degreesOfSeparation`, and include/exclude query; `formatArkRunGraphMermaid()` (also `graph.mermaid`) is a helper string, never a score. `send()` is the transport port (local / localBlocking / broker); missing broker falls back to in-process local delivery, `ephemeral` defaults true, and **no cloud SDKs ship** in the package (ADR 0024). Opt-in `startInspector()` / `startArkRunInspector()` binds **`127.0.0.1` only**, refuses `NODE_ENV=production`, lazy-loads HTTP, and serves JSON snapshots, SSE, `/graph` slices of the information package, plus queue monitors **`GET /outbox`** and **`GET /workflows`** (full counts + sanitized samples capped at 32; no full event payloads; missing ports are unavailable; no public / authless bind). Snapshot `hardening.durability` classifies the explicit outbox/audit/workflow store ports; default `InMemory*` stores stay visibly `memory`, never durable. Dual bins **`ark-dashboard`** / **`arkgate-dashboard`** poll those JSON facts (ANSI TUI in `bin/` only — presentation is not in the kernel); `ark dashboard` / `arkgate dashboard` dispatch to the same executable. **Shadow / replay / compare** (`shadowInformationPackage`, `compareInformationPackages`, `replayInformationPackages`) are in-memory helpers on that snapshot — not durable, not a second bus (ADR 0033). **Decision tape** `decisionTape` `{ xiHash, event, residual }` via `appendDecisionTape` (ADR 0034). Built-in stores are **InMemory reference only**. Branding ArkRun is not a production-durability claim. **`@arkgate/runtime` is deprecated** leftover 0.x (`experimental` dist-tag). |
199
- | **NestJS adapter** | **`arkgate/nestjs`** | Experimental optional peer `@nestjs/common` for the ArkRun kernel. Same npm package. `@arkgate/runtime/nestjs` is deprecated. |
200
- | **ArkOrder plane** | **`arkgate/order`** | Public brand **ArkOrder**. Same npm package `arkgate` (ADR 0030) — not `@arkgate/order`. Factory `createOrderPlane`. Valved verbs: `release` / `project` / `ingest` / `proposeRelease` / `apply` / `refreshSigma` / `restore`. No `update`. First freeze is `release()`; later ξ change is `apply` (`ARKORDER_UNVALVED_RELEASE`). `restore(release)` installs a previously frozen `Release` (hash remains identity; not durable). `hashOf` / `hashReleasePayload` verify a stored hash without a second freeze. Default Kernel clock is `Date.now()`. Optional `arkOrder.appliesTo` globs; `maxXiKeys` caps one `release()`, not `xiKeys.length`. Haken: few slow keys; ingest residual `absorb | escalate_up | hold` + closed `reasonCode`; empty blast fails closed. Capacity pack as data (`kind` / `sigmaKey` / `payloadKey` / `op`). Factory options (not config keys): `informationBudget.cannotObserve`, `sigmaMaxAgeMs`, `store` (`ReleaseStore` / `createMemoryReleaseStore`), `catalogDigest`. Thin travel: `ingestTravelAction` absorb→`send` / escalate_up human→`raises`. `IngestEscalate.target` includes `human`. Root `arkgate` export does **not** include the factory. Optional extra `arkOrder` on schema `1.3`. In-memory; not durable; does not close K01. Does not replace ArkRun. Runtime half (shadow/replay/compare + `decisionTape` / `appendDecisionTape`) is ArkRun (ADR 0033 / 0034). Billing gallery is git-only: [examples/arkorder-billing](https://github.com/pedroknigge/arkgate/tree/main/examples/arkorder-billing) (not in the npm tarball). Canonical: [ArkOrder](arkorder.md). |
201
+ | **ArkRun kernel** | **`arkgate/runtime`** | Public brand **ArkRun**. Same npm package `arkgate` (ADR 0031). Factory `createStrictArkKernel` (each call is an isolated instance; no process-wide `getKernel()` singleton). Root export does **not** include the factory. Optional extra `arkRun` on schema `1.2+`. Event bus, intents, policies, sagas, event buffer, projections, and strict helpers. Managed components declare `uses` / `reactsTo` / `raises` / `sends` on `register()`; `getDependencyInformationPackage()` is a JSON snapshot of ids, lifetime, and declarations and never includes factories, live instances, or input DTOs (ADR 0023). `requestGraph()` slices that snapshot into **process** or **technical** graphs with optional `nodeIds`, `degreesOfSeparation`, and include/exclude query; `formatArkRunGraphMermaid()` (also `graph.mermaid`) is a helper string, never a score. `send()` is the transport port (local / localBlocking / broker); missing broker falls back to in-process local delivery, `ephemeral` defaults true, and **no cloud SDKs ship** in the package (ADR 0024). Opt-in `startInspector()` / `startArkRunInspector()` binds **`127.0.0.1` only**, refuses `NODE_ENV=production`, lazy-loads HTTP, and serves JSON snapshots, SSE, `/graph` slices of the information package, plus queue monitors **`GET /outbox`** and **`GET /workflows`** (full counts + sanitized samples capped at 32; no full event payloads; missing ports are unavailable; no public / authless bind). Snapshot `hardening.durability` classifies the explicit outbox/audit/workflow store ports; default `InMemory*` stores stay visibly `memory`, never durable. Dual bins **`ark-dashboard`** / **`arkgate-dashboard`** poll those JSON facts (ANSI TUI in `bin/` only — presentation is not in the kernel); `ark dashboard` / `arkgate dashboard` dispatch to the same executable. **Shadow / replay / compare** (`shadowInformationPackage`, `compareInformationPackages`, `replayInformationPackages`) are in-memory helpers on that snapshot — not durable, not a second bus (ADR 0033). **Decision tape** `decisionTape` `{ xiHash, event, residual }` via `appendDecisionTape` (ADR 0034). Built-in stores are **InMemory reference only**; the default event buffer is capped at `maxHistorySize` and settled after local delivery, and broker handoff outcomes (including failures, as `event.handoffFailed`) are recorded. `createStrictArkKernelFromConfig` gives canonical layer names (what `ark init` writes) their built-in intent prefixes; a custom-named deny-rule layer without `intentPrefixes` is recorded as a `layer.observedFlowUnresolvable` audit record, and throws `ArkKernelConfigError` only when `enforceObservedLayerFlow: 'hard'` is passed explicitly. Branding ArkRun is not a production-durability claim. **`@arkgate/runtime` is deprecated** leftover 0.x (`experimental` dist-tag). |
202
+ | **NestJS adapter** | **`arkgate/nestjs`** | Experimental optional peer `@nestjs/common` for the ArkRun kernel. Same npm package. `arkgate/runtime` and `arkgate/nestjs` load **one shared kernel chunk**, so errors thrown by an `ArkModule` kernel pass `instanceof` against the classes exported from `arkgate/runtime`. `ArkModule.forRoot()` / `forRootAsync()` count as an ArkRun composition-root factory; a Domain-role import of `arkgate/nestjs` is `ARKRUN_KERNEL_IN_DOMAIN`. `@arkgate/runtime/nestjs` is deprecated. |
203
+ | **ArkOrder plane** | **`arkgate/order`** | Public brand **ArkOrder**. Same npm package `arkgate` (ADR 0030) — not `@arkgate/order`. Factory `createOrderPlane`. Valved verbs: `release` / `project` / `ingest` / `proposeRelease` / `apply` / `refreshSigma` / `restore`. No `update`. First freeze is `release()`; later ξ change is `apply` (`ARKORDER_UNVALVED_RELEASE`). `apply` rejects a proposal not computed against the current Release (`ARKORDER_STALE_PROPOSAL`). `restore(release)` reinstalls a validated `Release` (frozen or JSON-deserialized; hash remains identity; not durable) onto an empty plane or at the same ξ at the current or next version (no rollback, no jump; a freeze time after the plane clock fails closed); `ReleaseStore.load()` output is validated the same way (hashes are a consistency check, not a signature), and the plane advances only after `save()` returns. `hashOf` / `hashReleasePayload` verify a stored hash without a second freeze. Default Kernel clock is `Date.now()`. Optional `arkOrder.appliesTo` globs; `maxXiKeys` caps one `release()`, not `xiKeys.length`. Haken: few slow keys; ingest residual `absorb | escalate_up | hold` + closed `reasonCode`; empty blast fails closed. Capacity pack as data (`kind` / `sigmaKey` / `payloadKey` / `op`). Factory options (not config keys): `informationBudget.cannotObserve`, `sigmaMaxAgeMs`, `store` (`ReleaseStore` / `createMemoryReleaseStore`), `catalogDigest`. Thin travel: `ingestTravelAction` absorb→`send` / escalate_up human→`raises`. `IngestEscalate.target` includes `human`. Root `arkgate` export does **not** include the factory. Optional extra `arkOrder` on schema `1.3`. In-memory; not durable; does not close K01. Does not replace ArkRun. Runtime half (shadow/replay/compare + `decisionTape` / `appendDecisionTape`) is ArkRun (ADR 0033 / 0034). Billing gallery is git-only: [examples/arkorder-billing](https://github.com/pedroknigge/arkgate/tree/main/examples/arkorder-billing) (not in the npm tarball). Canonical: [ArkOrder](arkorder.md). |
201
204
 
202
205
  ---
203
206
 
@@ -212,6 +215,26 @@ import { createOrderPlane, hashOf } from 'arkgate/order';
212
215
 
213
216
  One install: `npm install arkgate`. `@arkgate/runtime` is deprecated.
214
217
 
218
+ **Types for ESM and CommonJS consumers.** Every code entry (`arkgate`, `arkgate/eslint`,
219
+ `arkgate/order`, `arkgate/runtime`, `arkgate/nestjs`) maps `import` to `index.js` +
220
+ `index.d.ts` and `require` to `index.cjs` + `index.d.cts`, and the package ships both
221
+ declaration sets. A CommonJS TypeScript project (`.cts`, or `module` / `moduleResolution`
222
+ `node16` without `"type": "module"`, the usual NestJS shape) type-checks `require` /
223
+ `import x = require(...)` against the CJS declarations. `typesVersions` maps the subpaths for
224
+ legacy `moduleResolution: node10`. Through 4.8.23 the tarball dropped `*.d.cts`, so those
225
+ consumers got TS1471 / TS1479 / TS2307 although runtime `require()` worked.
226
+
227
+ ### MCP server via npx and the MCP Registry
228
+
229
+ `npx arkgate@<version> <args>` always runs the bin named after the package, `arkgate`. So
230
+ `arkgate mcp [--root <dir>] [--config <path>] [--hook]` starts the same stdio MCP server as
231
+ `arkgate-mcp`, and forwards every flag to it. The MCP Registry descriptor (`server.json`) launches
232
+ `npx arkgate@<version> mcp --root .` with no `--config`, so it starts in a fresh project too
233
+ (the server reads `ark.config.json` when present; an explicit `--config` to a missing file exits
234
+ 1). `arkgate arkgate-mcp` and `arkgate ark-mcp` are accepted as well. Registry entries published
235
+ through 4.8.23 pin arkgate versions that have no `mcp` route, so they keep failing until the
236
+ descriptor is republished for a version that has it.
237
+
215
238
  ---
216
239
 
217
240
  ## Installing from git
@@ -80,7 +80,7 @@ On the seven lane openings — `docs/use.md`, `docs/develop.md`, `docs/README.md
80
80
  `README.md` is the first-contact module. Its first screen is four beats: what
81
81
  (**Contener · Guiar · Ordenar**), why (design the boundary before you implement;
82
82
  a green check is **needs a refactor** when leftover mess remains), one command
83
- (`npx arkgate@4.8.23 start`), and one honest red result (fail-open named; required
83
+ (`npx arkgate@4.8.24 start`), and one honest red result (fail-open named; required
84
84
  CI is the hard line). The four paragraphs, the not-that line, the host matrix,
85
85
  and the release index sit below `## Appendix`. The not-that line does not lead
86
86
  the README.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "arkgate",
3
- "version": "4.8.23",
3
+ "version": "4.8.24",
4
4
  "description": "When the agent writes a bad import, the write doesn’t land. The same check fails the pull request.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -8,29 +8,54 @@
8
8
  "types": "./dist/index.d.ts",
9
9
  "exports": {
10
10
  ".": {
11
- "types": "./dist/index.d.ts",
12
- "import": "./dist/index.js",
13
- "require": "./dist/index.cjs"
11
+ "import": {
12
+ "types": "./dist/index.d.ts",
13
+ "default": "./dist/index.js"
14
+ },
15
+ "require": {
16
+ "types": "./dist/index.d.cts",
17
+ "default": "./dist/index.cjs"
18
+ }
14
19
  },
15
20
  "./eslint": {
16
- "types": "./dist/eslint/index.d.ts",
17
- "import": "./dist/eslint/index.js",
18
- "require": "./dist/eslint/index.cjs"
21
+ "import": {
22
+ "types": "./dist/eslint/index.d.ts",
23
+ "default": "./dist/eslint/index.js"
24
+ },
25
+ "require": {
26
+ "types": "./dist/eslint/index.d.cts",
27
+ "default": "./dist/eslint/index.cjs"
28
+ }
19
29
  },
20
30
  "./order": {
21
- "types": "./dist/order/index.d.ts",
22
- "import": "./dist/order/index.js",
23
- "require": "./dist/order/index.cjs"
31
+ "import": {
32
+ "types": "./dist/order/index.d.ts",
33
+ "default": "./dist/order/index.js"
34
+ },
35
+ "require": {
36
+ "types": "./dist/order/index.d.cts",
37
+ "default": "./dist/order/index.cjs"
38
+ }
24
39
  },
25
40
  "./runtime": {
26
- "types": "./dist/runtime/index.d.ts",
27
- "import": "./dist/runtime/index.js",
28
- "require": "./dist/runtime/index.cjs"
41
+ "import": {
42
+ "types": "./dist/runtime/index.d.ts",
43
+ "default": "./dist/runtime/index.js"
44
+ },
45
+ "require": {
46
+ "types": "./dist/runtime/index.d.cts",
47
+ "default": "./dist/runtime/index.cjs"
48
+ }
29
49
  },
30
50
  "./nestjs": {
31
- "types": "./dist/nestjs/index.d.ts",
32
- "import": "./dist/nestjs/index.js",
33
- "require": "./dist/nestjs/index.cjs"
51
+ "import": {
52
+ "types": "./dist/nestjs/index.d.ts",
53
+ "default": "./dist/nestjs/index.js"
54
+ },
55
+ "require": {
56
+ "types": "./dist/nestjs/index.d.cts",
57
+ "default": "./dist/nestjs/index.cjs"
58
+ }
34
59
  },
35
60
  "./schema": "./schemas/ark.config.schema.json",
36
61
  "./schema/ark.config.schema.json": "./schemas/ark.config.schema.json",
@@ -52,6 +77,22 @@
52
77
  "./schema/ark.status-manifest.schema.json": "./schemas/ark.status-manifest.schema.json",
53
78
  "./package.json": "./package.json"
54
79
  },
80
+ "typesVersions": {
81
+ "*": {
82
+ "eslint": [
83
+ "./dist/eslint/index.d.ts"
84
+ ],
85
+ "order": [
86
+ "./dist/order/index.d.ts"
87
+ ],
88
+ "runtime": [
89
+ "./dist/runtime/index.d.ts"
90
+ ],
91
+ "nestjs": [
92
+ "./dist/nestjs/index.d.ts"
93
+ ]
94
+ }
95
+ },
55
96
  "bin": {
56
97
  "arkgate": "bin/ark.mjs",
57
98
  "arkgate-check": "bin/ark-check.mjs",
@@ -74,7 +115,6 @@
74
115
  "!bin/lib/safety-diagnostics.mjs",
75
116
  "!bin/lib/ts-resolve.mjs",
76
117
  "dist",
77
- "!dist/**/*.d.cts",
78
118
  "schemas",
79
119
  "!schemas/*.source.json",
80
120
  "docs/README.md",
@@ -168,7 +208,8 @@
168
208
  "clean": "rm -rf dist",
169
209
  "release:npm": "node scripts/release-npm.mjs",
170
210
  "prepublishOnly": "npm run build",
171
- "bench:hook-path": "node scripts/hook-path-bench.mjs"
211
+ "bench:hook-path": "node scripts/hook-path-bench.mjs",
212
+ "bench:memory": "node scripts/memory-bench.mjs"
172
213
  },
173
214
  "dependencies": {
174
215
  "typescript-ark-host": "npm:typescript@6.0.3"