arkgate 4.8.24 → 4.8.26
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 +95 -0
- package/README.md +2 -2
- package/bin/ark-check-runtime.mjs +17 -14
- package/bin/ark-layer-match.mjs +115 -13
- package/bin/ark-mcp-runtime.mjs +36 -31
- package/bin/ark-shared.mjs +4 -23
- package/bin/lib/adoption-stance.mjs +2 -2
- package/bin/lib/adr-path.mjs +3 -3
- package/bin/lib/agent-gates.mjs +0 -61
- package/bin/lib/agent-homes.mjs +12 -21
- package/bin/lib/agent-projection-command.mjs +4 -4
- package/bin/lib/ambient-state.mjs +9 -30
- package/bin/lib/analysis-engine.mjs +14 -14
- package/bin/lib/architecture-scan.mjs +11 -9
- package/bin/lib/ark-order-doctor.mjs +3 -28
- package/bin/lib/ark-order-facts.mjs +6 -99
- package/bin/lib/ark-order-types.mjs +1 -2
- package/bin/lib/ark-run-doctor.mjs +3 -28
- package/bin/lib/ark-run-facts.mjs +30 -40
- package/bin/lib/ark-run-sensors.mjs +1 -1
- package/bin/lib/arkrule-file-hints.mjs +2 -47
- package/bin/lib/arkrules-contract.mjs +124 -133
- package/bin/lib/arkrules-sensors.mjs +7 -5
- package/bin/lib/check-args.mjs +63 -1
- package/bin/lib/ci-and-commands.mjs +8 -72
- package/bin/lib/ci-merge-boundary.mjs +1 -1
- package/bin/lib/class-source-scan.mjs +5 -5
- package/bin/lib/clone-detection.mjs +474 -0
- package/bin/lib/codex-home.mjs +1 -1
- package/bin/lib/config-contract-slices.mjs +44 -6
- package/bin/lib/config-contract.mjs +40 -117
- package/bin/lib/config-extras.mjs +16 -22
- package/bin/lib/contract-smells.mjs +33 -15
- package/bin/lib/core-ratchet.mjs +0 -2
- package/bin/lib/dashboard-cli.mjs +2 -2
- package/bin/lib/deep-module-coach.mjs +2 -4
- package/bin/lib/deploy-path.mjs +13 -38
- package/bin/lib/design-smells.mjs +2 -2
- package/bin/lib/diagnostic-catalog.mjs +8 -1
- package/bin/lib/doctor-advisories.mjs +55 -9
- package/bin/lib/doctor-green-cite.mjs +1 -1
- package/bin/lib/doctor-human.mjs +1 -1
- package/bin/lib/doctor-next-actions.mjs +2 -2
- package/bin/lib/doctor-plan.mjs +4 -6
- package/bin/lib/duplication-io.mjs +572 -0
- package/bin/lib/effective-contract-load.mjs +181 -44
- package/bin/lib/entry-points-io.mjs +424 -0
- package/bin/lib/extra-merge-teeth.mjs +36 -0
- package/bin/lib/field-install.mjs +38 -14
- package/bin/lib/first-run-help.mjs +7 -3
- package/bin/lib/flat-parent-importers.mjs +5 -3
- package/bin/lib/gate-files.mjs +30 -89
- package/bin/lib/github-enforcement.mjs +165 -28
- package/bin/lib/golden-pattern.mjs +1 -1
- package/bin/lib/hook-templates.mjs +16 -16
- package/bin/lib/html-report-advisories.mjs +4 -0
- package/bin/lib/html-report-depth.mjs +2 -2
- package/bin/lib/html-report.mjs +2 -2
- package/bin/lib/import-graph-projection.mjs +112 -0
- package/bin/lib/improvement-compass-doctor.mjs +1 -24
- package/bin/lib/invariant-coverage-io.mjs +4 -3
- package/bin/lib/invariant-coverage.mjs +26 -0
- package/bin/lib/invariant-probe-cli.mjs +347 -0
- package/bin/lib/invariant-probe-io.mjs +165 -0
- package/bin/lib/invariant-probe-runner.mjs +268 -0
- package/bin/lib/invariant-probe-sites.mjs +174 -0
- package/bin/lib/invariant-probe-workspace.mjs +183 -0
- package/bin/lib/invariant-probe.mjs +461 -0
- package/bin/lib/invariant-tests-path.mjs +16 -22
- package/bin/lib/literal-path-drift.mjs +4 -2
- package/bin/lib/managed-upgrade.mjs +2 -8
- package/bin/lib/mcp-adoption.mjs +4 -5
- package/bin/lib/mcp-hook-payload.mjs +4 -4
- package/bin/lib/orphan-modules-io.mjs +458 -0
- package/bin/lib/orphan-modules.mjs +251 -0
- package/bin/lib/outside-importers.mjs +197 -0
- package/bin/lib/package-manager.mjs +3 -3
- package/bin/lib/parse-health.mjs +1 -1
- package/bin/lib/persistence-write-hint.mjs +33 -8
- package/bin/lib/pin-dependent-gates.mjs +2 -2
- package/bin/lib/policy-delta-io.mjs +60 -12
- package/bin/lib/post-green-path.mjs +1 -1
- package/bin/lib/prepare-change.mjs +3 -9
- package/bin/lib/prepare-write.mjs +1 -2
- package/bin/lib/presets.mjs +56 -68
- package/bin/lib/product-copy.mjs +1 -6
- package/bin/lib/project-root.mjs +43 -85
- package/bin/lib/resolved-candidate-facts.mjs +90 -62
- package/bin/lib/rules-under-contract.mjs +72 -8
- package/bin/lib/scan-files.mjs +4 -4
- package/bin/lib/schema-validation.mjs +130 -0
- package/bin/lib/sensor-promote-cli.mjs +2 -11
- package/bin/lib/sensor-promote-io.mjs +33 -22
- package/bin/lib/sensor-promotion.mjs +1 -1
- package/bin/lib/skill-catalog-apply.mjs +1 -1
- package/bin/lib/skill-install.mjs +42 -38
- package/bin/lib/snippet-analysis.mjs +1 -1
- package/bin/lib/start-install-recovery.mjs +1 -1
- package/bin/lib/start-preview.mjs +1 -1
- package/bin/lib/states-transitions-presence.mjs +3 -3
- package/bin/lib/status-command.mjs +2 -18
- package/bin/lib/status-manifest.mjs +18 -26
- package/bin/lib/status-transition-catalog.mjs +0 -6
- package/bin/lib/suggestions.mjs +3 -3
- package/bin/lib/team-parliament-io.mjs +3 -17
- package/bin/lib/team-parliament.mjs +15 -9
- package/bin/lib/upgrade-behavior-changes.mjs +2 -2
- package/bin/lib/upgrade-command.mjs +2 -24
- package/bin/lib/upgrade-outcome.mjs +1 -1
- package/bin/lib/upstream-report.mjs +21 -10
- package/bin/lib/violations.mjs +10 -9
- package/bin/lib/weakest-link.mjs +13 -0
- package/bin/lib/write-path-capabilities.mjs +2 -2
- package/dist/chunk-G3JRBJGH.js +16 -0
- package/dist/chunk-R3AJ5RSZ.cjs +16 -0
- package/dist/{configTypes-BJbaeoj4.d.cts → configTypes-GPvvdleM.d.cts} +12 -1
- package/dist/{configTypes-BJbaeoj4.d.ts → configTypes-GPvvdleM.d.ts} +12 -1
- package/dist/eslint/index.cjs +11 -11
- package/dist/eslint/index.d.cts +10 -1
- package/dist/eslint/index.d.ts +10 -1
- package/dist/eslint/index.js +11 -11
- package/dist/index.cjs +34 -34
- package/dist/index.d.cts +587 -515
- package/dist/index.d.ts +587 -515
- package/dist/index.js +34 -34
- package/dist/nestjs/index.cjs +1 -1
- package/dist/nestjs/index.d.cts +1 -1
- package/dist/nestjs/index.d.ts +1 -1
- package/dist/nestjs/index.js +1 -1
- package/dist/order/index.cjs +1 -1
- package/dist/order/index.js +1 -1
- package/dist/runtime/index.cjs +12 -12
- package/dist/runtime/index.d.cts +174 -136
- package/dist/runtime/index.d.ts +174 -136
- package/dist/runtime/index.js +12 -12
- package/dist/{types-DRinG-vT.d.cts → types-CnauMQQL.d.cts} +33 -25
- package/dist/{types-DRinG-vT.d.ts → types-CnauMQQL.d.ts} +33 -25
- package/docs/README.md +3 -1
- package/docs/agent-guide.md +140 -6
- package/docs/ai-gates.md +15 -1
- package/docs/configuration.md +40 -11
- package/docs/diagnostics.md +119 -3
- package/docs/package-surface.md +4 -1
- package/docs/product-voice.md +7 -1
- package/package.json +11 -2
- package/schemas/ark.config.schema.json +31 -3
- package/schemas/ark.invariant-probe.schema.json +368 -0
- package/server.json +2 -2
- package/templates/agent-skills/README.md +1 -1
- package/templates/agent-skills/ark-adopt/SKILL.md +1 -0
- package/templates/agent-skills/ark-contract/SKILL.md +17 -0
- package/templates/agent-skills/ark-coverage/SKILL.md +2 -0
- package/templates/agent-skills/ark-explore/SKILL.md +7 -0
- package/templates/agent-skills/ark-fix/SKILL.md +20 -0
- package/templates/agent-skills/ark-place/SKILL.md +8 -0
- package/templates/skills/ark-adopt.md +1 -0
- package/templates/skills/ark-contract.md +17 -0
- package/templates/skills/ark-coverage.md +2 -0
- package/templates/skills/ark-explore.md +7 -0
- package/templates/skills/ark-fix.md +20 -0
- package/templates/skills/ark-place.md +8 -0
- package/bin/lib/ark-order-error.mjs +0 -18
- package/bin/lib/ark-order-invariants.mjs +0 -388
- package/bin/lib/stable-hash.mjs +0 -90
- package/dist/chunk-MBXCFLO4.js +0 -16
- package/dist/chunk-RNQHRDCQ.cjs +0 -16
package/docs/diagnostics.md
CHANGED
|
@@ -29,6 +29,8 @@ Link form for agents: `docs/diagnostics.md#RULE_ID` (exact-case HTML anchors bel
|
|
|
29
29
|
| [`LAYER_REFERENCE_VIOLATION`](#LAYER_REFERENCE_VIOLATION) | layer | Layer reference blocked (snippet / AI gate) |
|
|
30
30
|
| [`SHARED_IMPORTS_SLICE`](#SHARED_IMPORTS_SLICE) | layer | A shared root imports a slice |
|
|
31
31
|
| [`CIRCULAR_DEPENDENCY`](#CIRCULAR_DEPENDENCY) | layer | Dependency cycle |
|
|
32
|
+
| [`CROSS_WALL_DUPLICATE`](#CROSS_WALL_DUPLICATE) | layer | Code copied across a wall |
|
|
33
|
+
| [`CROSS_LAYER_DUPLICATE`](#CROSS_LAYER_DUPLICATE) | layer | Code copied in two layers |
|
|
32
34
|
| [`FORBIDDEN_GLOBAL`](#FORBIDDEN_GLOBAL) | capability | Forbidden ambient global or dual import |
|
|
33
35
|
| [`CAPABILITY_VIOLATION`](#CAPABILITY_VIOLATION) | capability | Denied effect capability |
|
|
34
36
|
| [`RAW_EVENT_PUBLISH`](#RAW_EVENT_PUBLISH) | publish | Raw event publish |
|
|
@@ -46,10 +48,13 @@ Link form for agents: `docs/diagnostics.md#RULE_ID` (exact-case HTML anchors bel
|
|
|
46
48
|
| [`ARKRULE_SCOPE_EMPTY`](#ARKRULE_SCOPE_EMPTY) | arkrules | ArkRule appliesTo matched zero files |
|
|
47
49
|
| [`ARKRULE_HINT_BUDGET_EXHAUSTED`](#ARKRULE_HINT_BUDGET_EXHAUSTED) | arkrules | Structural-hint budget exhausted |
|
|
48
50
|
| [`ARKRULE_FILE_UNREFERENCED`](#ARKRULE_FILE_UNREFERENCED) | arkrules | ArkRules file not referenced |
|
|
51
|
+
| [`ARKRULE_DUPLICATE_ID`](#ARKRULE_DUPLICATE_ID) | arkrules | ArkRule id declared more than once |
|
|
52
|
+
| [`ARKRULE_SCOPE_ESCAPES_SLICE`](#ARKRULE_SCOPE_ESCAPES_SLICE) | arkrules | Slice ArkRule appliesTo leaves its slice |
|
|
49
53
|
| [`INVARIANT_CATALOG_EMPTY`](#INVARIANT_CATALOG_EMPTY) | arkrules | Domain invariant catalog is empty |
|
|
50
54
|
| [`INVARIANT_UNCOVERED`](#INVARIANT_UNCOVERED) | arkrules | Invariant without coverage evidence |
|
|
51
55
|
| [`INVARIANT_COVERAGE_OUTSIDE_ROOTS`](#INVARIANT_COVERAGE_OUTSIDE_ROOTS) | arkrules | Covering test outside the declared coverage roots |
|
|
52
56
|
| [`INVARIANT_COVERAGE_ROOTS_MISSING`](#INVARIANT_COVERAGE_ROOTS_MISSING) | arkrules | Coverage roots missing while an invariant is enforced |
|
|
57
|
+
| [`INVARIANT_PROBE_SURVIVED`](#INVARIANT_PROBE_SURVIVED) | arkrules | Covering tests do not pin the invariant |
|
|
53
58
|
| [`INVARIANT_TESTS_PATH_MISSING`](#INVARIANT_TESTS_PATH_MISSING) | arkrules | Domain-invariant tests path missing under adopted |
|
|
54
59
|
| [`ARKRUN_MISSING_ROOT`](#ARKRUN_MISSING_ROOT) | arkrun | No kernel factory in composition roots |
|
|
55
60
|
| [`ARKRUN_KERNEL_IN_DOMAIN`](#ARKRUN_KERNEL_IN_DOMAIN) | arkrun | Domain-role layer imports the kernel |
|
|
@@ -83,6 +88,8 @@ Link form for agents: `docs/diagnostics.md#RULE_ID` (exact-case HTML anchors bel
|
|
|
83
88
|
| [`ANALYSIS_HOST_UNAVAILABLE`](#ANALYSIS_HOST_UNAVAILABLE) | analysis | Analysis host unavailable |
|
|
84
89
|
| [`LITERAL_PATH_DRIFT`](#LITERAL_PATH_DRIFT) | drift | Literal path moved by a rename |
|
|
85
90
|
| [`LITERAL_PATH_UNRESOLVED`](#LITERAL_PATH_UNRESOLVED) | drift | Literal path does not resolve |
|
|
91
|
+
| [`ORPHAN_MODULE`](#ORPHAN_MODULE) | drift | File nothing imports |
|
|
92
|
+
| [`UNUSED_EXPORT`](#UNUSED_EXPORT) | drift | Export nothing imports by name |
|
|
86
93
|
| [`ADAPTER_NOT_ALLOWED_FOR_PORT`](#ADAPTER_NOT_ALLOWED_FOR_PORT) | adapter | Adapter not allowed for port |
|
|
87
94
|
| [`FORBIDDEN_PATTERN`](#FORBIDDEN_PATTERN) | snippet-policy | Forbidden regex pattern |
|
|
88
95
|
| [`FORBIDDEN_SUBSTRING`](#FORBIDDEN_SUBSTRING) | snippet-policy | Forbidden substring |
|
|
@@ -320,8 +327,26 @@ Link form for agents: `docs/diagnostics.md#RULE_ID` (exact-case HTML anchors bel
|
|
|
320
327
|
|
|
321
328
|
**ArkRules file not referenced** · often advisory
|
|
322
329
|
|
|
323
|
-
- **Why:** A JSON file under `arkrules
|
|
324
|
-
- **Fix:** Reference
|
|
330
|
+
- **Why:** A JSON file under `arkrules/`, or a slice file named `arkrules.<Layer>.json` beside governed code, is not in the effective catalog, so none of its rules are enforced. Drift looks like governance until someone notices.
|
|
331
|
+
- **Fix:** Reference a central file from `arkRules`, set `childSlices.arkRulesFile` so a slice file is discovered, or delete it. Advisory only: it never fails the check.
|
|
332
|
+
|
|
333
|
+
<a id="ARKRULE_DUPLICATE_ID"></a>
|
|
334
|
+
|
|
335
|
+
### `ARKRULE_DUPLICATE_ID`
|
|
336
|
+
|
|
337
|
+
**ArkRule id declared more than once**
|
|
338
|
+
|
|
339
|
+
- **Why:** Two ArkRules files in one layer declare the same effective id. Central ids stay bare. A slice file is namespaced as `<childId>#<localId>`, so the same local id in two slices is not a duplicate.
|
|
340
|
+
- **Fix:** Rename one id, or split the files onto different slices. Config load fails closed. The catalog is not enforced until the id is unique.
|
|
341
|
+
|
|
342
|
+
<a id="ARKRULE_SCOPE_ESCAPES_SLICE"></a>
|
|
343
|
+
|
|
344
|
+
### `ARKRULE_SCOPE_ESCAPES_SLICE`
|
|
345
|
+
|
|
346
|
+
**Slice ArkRule appliesTo leaves its slice**
|
|
347
|
+
|
|
348
|
+
- **Why:** A file discovered from `childSlices.arkRulesFile` set `appliesTo` to a glob that can match outside that child slice. The default, when `appliesTo` is omitted, is the slice directory.
|
|
349
|
+
- **Fix:** Narrow `appliesTo` to the slice directory, or omit it. Config load fails closed. The escaping rule is not enforced.
|
|
325
350
|
|
|
326
351
|
<a id="INVARIANT_CATALOG_EMPTY"></a>
|
|
327
352
|
|
|
@@ -363,6 +388,17 @@ Declaring nothing is silent unless any catalogued invariant is enforced — then
|
|
|
363
388
|
|
|
364
389
|
No enforced invariant, or roots already declared, stays silent. This is not a new config key. Promotion to enforced also refuses without roots.
|
|
365
390
|
|
|
391
|
+
<a id="INVARIANT_PROBE_SURVIVED"></a>
|
|
392
|
+
|
|
393
|
+
### `INVARIANT_PROBE_SURVIVED`
|
|
394
|
+
|
|
395
|
+
**Covering tests do not pin the invariant** · often advisory
|
|
396
|
+
|
|
397
|
+
- **Why:** The committed invariant probe (`.ark/invariant-probe.json`, written by `arkgate-check --probe-invariants --write`) shows, for files that have not changed since, that the covering tests still pass with the invariant's `coverage.symbol` broken on purpose (`survived`), or never call it (`not-reached`). The test names the rule but does not pin it.
|
|
398
|
+
- **Fix:** Add a test case that fails when the rule is broken — the line names the change that went unnoticed — then re-run `arkgate-check --probe-invariants --write` and commit the file. Promotion to `enforced` refuses while the fresh artifact says `survived` or `not-reached`.
|
|
399
|
+
|
|
400
|
+
A status (`--doctor`) and `--rules-inventory` line only. It never enters the check, so `valid`, `--strict-merge` and the exit code do not change. A stale row (the symbol file, a covering test or the invariant changed since the run), `killed`, `inconclusive`, `unprobeable`, or no artifact at all changes nothing. The probe is opt-in, runs your own test runner in a temporary copy, and is not a mutation score — see [Probe an invariant](agent-guide.md#probe-an-invariant).
|
|
401
|
+
|
|
366
402
|
<a id="INVARIANT_TESTS_PATH_MISSING"></a>
|
|
367
403
|
|
|
368
404
|
### `INVARIANT_TESTS_PATH_MISSING`
|
|
@@ -698,7 +734,7 @@ Haken slaving: few slow keys (ξ) determine derived fast state. Field ingest nev
|
|
|
698
734
|
|
|
699
735
|
**Analysis covered no files**
|
|
700
736
|
|
|
701
|
-
- **Why:** No file matched the contract `include` and layer patterns under the analyzed root, so the run had nothing to check. Every rule is vacuously satisfied on an empty set: a green here would read exactly like a green over a governed tree while certifying nothing. Usual causes are a `--root`
|
|
737
|
+
- **Why:** No file matched the contract `include` and layer patterns under the analyzed root, so the run had nothing to check. Every rule is vacuously satisfied on an empty set: a green here would read exactly like a green over a governed tree while certifying nothing. Usual causes are a `--root` the contract misses, basename walk-up adopting a parent that governs nothing, or `include` / `exclude` / layer patterns that match nothing. An explicit `--config` path stays a file: the check runs on `--root`.
|
|
702
738
|
- **Fix:** Point `--root` at the tree the contract describes, or keep the contract inside that tree, or fix the `include` / `exclude` / layer patterns so they match real files — then re-run `npx arkgate-check --root . --config ark.config.json`. `--plan` and `--coverage` report the empty scope without refusing, and `--adopt-contract --write` proposes an `include` that matches the tree. This is a refusal about ArkGate's own inputs, not a finding about your code; no baseline or policy acknowledgement can suppress it, and `exclude` cannot silence it — the "is there source here" probe deliberately ignores the contract's own `exclude`, skips dot-directories, never follows a symlink, and skips `*.config.*` tooling files so a polyglot repo is not mistaken for a mismatch.
|
|
703
739
|
|
|
704
740
|
<a id="ANALYSIS_HOST_UNAVAILABLE"></a>
|
|
@@ -755,6 +791,86 @@ by default would be ArkGate's inability to resolve a string presented as a fact
|
|
|
755
791
|
about your code. The count is always printed, so opting out of the list is
|
|
756
792
|
never opting out of knowing.
|
|
757
793
|
|
|
794
|
+
## Files nothing imports
|
|
795
|
+
|
|
796
|
+
Status (`arkgate-check --doctor`) reads the import edges the check already
|
|
797
|
+
resolved. A governed file that nothing imports — no governed file, no test,
|
|
798
|
+
no project script, no known entry point — is listed in status details
|
|
799
|
+
(`--doctor --all`), in `--json` under `doctor.orphanModules`, and in the
|
|
800
|
+
HTML report. The compact status prints one count line at most. It is not a
|
|
801
|
+
score and never changes the check.
|
|
802
|
+
|
|
803
|
+
Entry points come from a closed list: `package.json` (`main`, `module`,
|
|
804
|
+
`types`, `exports`, `bin`, scripts) and CI workflow run steps, framework
|
|
805
|
+
conventions (Next, Vite, Nest, Vercel, Storybook), the arkRun / arkOrder /
|
|
806
|
+
slice-wall roots in `ark.config.json`, config and setup files, ambient
|
|
807
|
+
declaration files, and `.ark/entry-points.json`:
|
|
808
|
+
|
|
809
|
+
```json
|
|
810
|
+
{
|
|
811
|
+
"schemaVersion": "1",
|
|
812
|
+
"entryPoints": [
|
|
813
|
+
{ "glob": "src/plugins/**", "reason": "loaded by name at runtime", "reviewBy": "2027-01-01" }
|
|
814
|
+
]
|
|
815
|
+
}
|
|
816
|
+
```
|
|
817
|
+
|
|
818
|
+
<a id="ORPHAN_MODULE"></a>
|
|
819
|
+
|
|
820
|
+
### `ORPHAN_MODULE`
|
|
821
|
+
|
|
822
|
+
**File nothing imports** · often advisory
|
|
823
|
+
|
|
824
|
+
- **Why:** No governed file, test or entry point imports this module. It is the leftover an agent leaves when a rewrite lands next to the old file. Status reads it from the import facts the check already resolved, so it never changes the verdict. When a dynamic import, an unresolved import or an unmapped entry could reach the file, the item says so (`maybe-dynamic`, `maybe-unresolved`) and the list is `partial`.
|
|
825
|
+
- **Fix:** Delete the file through the write gate, one file at a time. If a framework, a script or a runtime loader uses it, add a glob with a reason to `.ark/entry-points.json` (optional `reviewBy`) instead. Advisory only — it never fails a run.
|
|
826
|
+
|
|
827
|
+
<a id="UNUSED_EXPORT"></a>
|
|
828
|
+
|
|
829
|
+
### `UNUSED_EXPORT`
|
|
830
|
+
|
|
831
|
+
**Export nothing imports by name** · often advisory
|
|
832
|
+
|
|
833
|
+
- **Why:** An export no governed file or test imports by name. Listed only in status details (`--doctor --all`) and the report. Default, namespace, star, dynamic and require use count as using every export, so those files are skipped.
|
|
834
|
+
- **Fix:** Drop the export keyword (or the code) if nothing outside the file needs it, or keep it when it is public API an entry point exposes. Advisory only — it never fails a run.
|
|
835
|
+
|
|
836
|
+
## Copies across a wall
|
|
837
|
+
|
|
838
|
+
A slice wall denies the import. The cheapest way around it is to copy the
|
|
839
|
+
code, and a copy is not an import. Status details (`arkgate-check --doctor --all`)
|
|
840
|
+
and the HTML report list near-identical code whose copies sit on two sides of
|
|
841
|
+
a wall, or in two layers, with a place the shared code could live. `--json`
|
|
842
|
+
carries it under `doctor.crossWallDuplication`; without `--all` it says
|
|
843
|
+
`not-run` and names the command. It is not a score and never changes the check.
|
|
844
|
+
|
|
845
|
+
The copies come from token fingerprints taken while status runs: one parse per
|
|
846
|
+
governed file with the TypeScript ArkGate already loaded. Tests, `.d.ts`,
|
|
847
|
+
generated files (default globs, or a `@generated` / `GENERATED FILE` /
|
|
848
|
+
`DO NOT EDIT` header in the first five lines) and files over 256 KB are never
|
|
849
|
+
read for this. A copy is listed only when the import between its two files is
|
|
850
|
+
one the check would deny, or when it sits in two layers. Copies inside one
|
|
851
|
+
slice are counted, never listed. A path the wall cannot classify is counted,
|
|
852
|
+
never listed. The constants are fixed: at least 50 tokens and 5 lines, and at
|
|
853
|
+
least 60% of the names matching. When a cap stops the pass, the section says
|
|
854
|
+
`partial` and how much was left.
|
|
855
|
+
|
|
856
|
+
<a id="CROSS_WALL_DUPLICATE"></a>
|
|
857
|
+
|
|
858
|
+
### `CROSS_WALL_DUPLICATE`
|
|
859
|
+
|
|
860
|
+
**Code copied across a wall** · often advisory
|
|
861
|
+
|
|
862
|
+
- **Why:** Near-identical code sits on two sides of a slice wall (`cross-slice`, `cross-parent`, `cross-sibling`) or in two layers that may not import each other (`cross-layer-walled`). The wall would deny the import, so the code was copied instead. Fingerprints are taken at status time and never enter the check, so the verdict does not change.
|
|
863
|
+
- **Fix:** Move one copy to a shared home the wall allows — the declared shared root, the universe common folder, or a layer both sides may import — then import it from both sides. One move at a time, through the write gate (`/ark-place` names the destination). Advisory only — it never fails a run.
|
|
864
|
+
|
|
865
|
+
<a id="CROSS_LAYER_DUPLICATE"></a>
|
|
866
|
+
|
|
867
|
+
### `CROSS_LAYER_DUPLICATE`
|
|
868
|
+
|
|
869
|
+
**Code copied in two layers** · often advisory
|
|
870
|
+
|
|
871
|
+
- **Why:** Near-identical code sits in two layers, and at least one of them may import the other (`cross-layer`).
|
|
872
|
+
- **Fix:** Keep one copy in the lower layer both sides may import, and import it from the other. One move at a time, through the write gate. Advisory only — it never fails a run.
|
|
873
|
+
|
|
758
874
|
## Port adapters
|
|
759
875
|
|
|
760
876
|
<a id="ADAPTER_NOT_ALLOWED_FOR_PORT"></a>
|
package/docs/package-surface.md
CHANGED
|
@@ -61,13 +61,15 @@ hardening guide remains repository-hosted rather than duplicated in the gate tar
|
|
|
61
61
|
| **Ambient state (pure layers)** | `ark-check --doctor --json` → `doctor.ambientState`; optional `.ark/ambient-state-acks.json` | Additive, **advisory only and opt-in**: only layers declared `pure: true` are scanned; the MVP shape is module-scope `let`/`var`. Findings carry `file`/`line`/`name`/`kind` (sorted, capped with honest `truncated` count). Acknowledgments live in the bounded sidecar (`{ acks: [{ file, name, reason }] }`, ≤64 KB / ≤200 entries); `acknowledged` counts applied acks; malformed file suppresses nothing. When TypeScript is unavailable the sensor reports `available: false` instead of guessing. **No strict mode exists** — A5: strictness requires a completed corpus and an explicit later decision. |
|
|
62
62
|
| **Parse health + analysis completeness** | `ark-check --doctor --json` → `doctor.parseHealth` + `doctor.completeness`; check JSON → `completeness`; report section `data-advisory="parseHealth"` | The resolved candidate facts contribute only `parseDiagnosticCount` per governed file (no raw diagnostics, second parser pass, or `tsc`). Z04's correctness path ignores legacy v9 caches and parses the complete candidate on every invocation; Z07 owns any future identity-keyed warm snapshot. Doctor remains diagnostic: parse health adds no architecture violation and does not change `designFitness` or `patternBets`. Verdict surfaces consume the evidence fail-closed: affected governed files mean `partial`, plan `goal.met: false`, normal JSON `valid:false`/`ok:false`, and strict merge exit `1`; the non-strict process exit remains advisory for compatibility. No usable host means `unavailable`, plan false, and CLI exit `2`. JSON reports `scannedFiles`, `affectedFiles`, `diagnosticCount`, deterministic top-12 `{ file, diagnosticCount }` entries, and honest `truncated`/`overflow`; missing/unsafe evidence never becomes a clean claim. |
|
|
63
63
|
| **Physical cohesion + reshape pilot** | `ark-check --doctor --json` → `doctor.physicalCohesion` (`reshapePilot`, `reshapeDecisions`); optional `.ark/reshape-decisions.json`; report section `data-advisory="physicalCohesion"` | Additive, **advisory only** — `notAScore`; never feeds the verdict, `designFitness`, or `patternBets`. Signal is **concentration, not volume**: concept clusters per anchor directory (deterministic path/name tokenization; framework filenames like `route.ts` take the topmost meaningful path segment — ADR 0010 D2). Fixed corpus-calibrated thresholds (`maxCluster ≥ 40` OR ≥2 anchors ≥ 20, ADR 0010 D3); findings ranked and capped (top 5, honest `truncated`). Anchors under `app/`/`pages/` are `fixedByConvention` and never move (D7). `reshapePilot` is **proposed, never applied** (`neverMechanicalSafe`): one Q04-style pilot card at a time targeting the smallest convention-free anchor, with `moveSample`/`movesTotal`, `successSignal`, `killSwitch`, `doNot[]`; each `to` is `concept/` under the source anchor and is kept only when layer and slice id are unchanged — if every candidate fails, `nextPilot` is null; real moves run only through the write gate + atomic preflight via `/ark-loop`; merges are `/ark-architect` judgment cards, never a codemod (D6). **Y01 verdict memory:** bounded sidecar `{ schemaVersion?: "1", decisions: [{ concept, anchors, verdict: "accepted"|"deferred"|"rejected", reason, reviewBy? }] }` (≤64 KiB / ≤200 unique targets). Identity is concept + complete sorted anchor set, never counts/change-map evidence. Current rejected/deferred records suppress pilot pressure only; accepted keeps the existing path. Expired/malformed/stale/invalid records suppress nothing; lifecycle and decisions render in doctor/report. Explicit only — golden-pattern prose never infers a verdict. |
|
|
64
|
+
| **Files nothing imports** | `ark-check --doctor --json` → `doctor.orphanModules` (nested `unusedExports`); human status details (`--doctor --all`) section **Files nothing imports (not a score)** and at most one dim count line in compact status; HTML report `data-advisory="orphanModules"`; optional `.ark/entry-points.json` | Additive schema `1.0`, **advisory only** — always `notAScore: true`; never flips `valid`, strict-merge, `goal.met` or a compass lens; never runs in the write hook, MCP write tools, ESLint, `--changed` or `--strict-merge`. Projects the resolved import edges the check already built (ADR 0037): a governed file with zero incoming project edges (type-only imports and `export … from` count) that no entry source covers. Closed entry sources, each labelled in `entryEvidence.bySource`: `package-json` (`main`/`module`/`types`/`browser`/`exports`/`bin` per workspace; built paths map back through a bundler entry map in a `*.config.*` file, tsconfig `outDir`/`rootDir`, then `dist\|build\|lib\|out` → `src`; an unmapped path forces `partial`), `package-scripts` (package scripts and CI workflow run steps), `framework` (Next route files, `pages/**`, `middleware`/`proxy`/`instrumentation`; Vite `index.html`; Nest `main`; Vercel root `api/**`; Storybook stories), `ark-config` (arkRun / arkOrder roots, `stopAt`), `config-file` (config and setup files, and any source file a config file names), `ambient` (`declare global` / `declare module`), `sidecar`. Files only tests import are counted in `testOnly`; files only non-governed source or a generated copy uses count as `outsideOnly`. `status` is `complete` \| `partial` \| `deferred` \| `unavailable`; under `partial` each item carries `certainty` `no-importer` \| `maybe-dynamic` \| `maybe-unresolved` and `honesty[]` names what could not be followed. Lists capped at 20 with honest `truncated`; with no framework recognised and more than a quarter of governed files listed, the list is cut to 5 and the headline says entry points were not recognised. Outside pass bounded to 5000 files / 256 KB each. `unusedExports` (`UNUSED_EXPORT`) is `deferred` in compact status and filled only by `--doctor --all` and `--report`. Sidecar `{ "schemaVersion": "1", "entryPoints": [{ "glob", "reason", "reviewBy?" }] }` (≤64 KiB / ≤200 entries); an expired `reviewBy` stops applying and annotates the file; a malformed file suppresses nothing and a line says so. Not a config key and not in `policyHash`. Domain pure + CLI gen mirror (`orphanModules.ts` / `bin/lib/orphan-modules.mjs`); not a root package export. |
|
|
65
|
+
| **Copies across a wall** | `ark-check --doctor --all --json` → `doctor.crossWallDuplication`; human status details (`--doctor --all`) section **Copies across a wall (not a score)**; HTML report `data-advisory="crossWallDuplication"` | Additive schema `1.0`, **advisory only** — always `notAScore: true`; never flips `valid`, strict-merge, `goal.met` or a compass lens; never runs in the write hook, MCP, ESLint, compact status, `--changed` or `--strict-merge`. Without `--all` (and under `--changed`) the key says `status: "not-run"` with `next: "arkgate-check --doctor --all"`. Token fingerprints taken at status time (ADR 0038) from the TypeScript already loaded — one `createSourceFile` per governed file, a JSX-safe node walk (identifiers and literals collapse, imports and `export … from` skipped); they never enter facts, `factsHash` or the verdict. Tests, `.d.ts`, default generated globs, files whose first five lines say `@generated` / `GENERATED FILE` / `DO NOT EDIT` / `generated from|by`, and files over 256 KB are never read for it. A family is listed only when a pair crosses a boundary the gate's own classifier (`findDeniedEdgeDecision`, both directions) draws: `crossing` `cross-slice` \| `cross-parent` \| `cross-sibling` \| `cross-layer-walled` (`CROSS_WALL_DUPLICATE`) or `cross-layer` (`CROSS_LAYER_DUPLICATE`). Same-slice pairs (`totals.pairsSameSliceUnexamined`), unclassifiable (`fail-closed`) pairs (`pairsUnclassifiable`) and files outside every layer (`pairsUnplaced`) are counted, never listed. Each family carries `members[]` (path, layer, `startLine`/`endLine`, tokens), `pairs[]`, `names` (`same` of `total` identifiers matching), a plain `destination` (`shared-root` \| `universe-common` \| `lower-layer` \| `shared-layer` \| `either-layer` \| `ask-place`; never applied) and `line`. Fixed constants in `method` (20-token grams, 31-gram winnowing window, ≥50 tokens, ≥5 lines, ≥60% names, bucket cap 16); not configurable. Caps: `graphScanLimit(n)` files, 1 000 000 fingerprints, 50 000 seed pairs, 1000 compared file pairs; any cap → `status: "partial"` with `honesty[]` counts. Families capped at 8 with honest `truncated`. No config key. Domain pure + CLI gen mirror (`cloneDetection.ts` / `bin/lib/clone-detection.mjs`); not a root package export. |
|
|
64
66
|
| **Capability walls, every adapter** | CLI scan, pure IR engine, atomic preflight, `ark-mcp --hook` / MCP gate (`capabilityWalls`), ESLint `ark/no-denied-capabilities` | The same opt-in deny set enforces across every surface: hook/MCP and CLI cover ambient + import evidence (symbol-aware); the pure engine, preflight, and ESLint cover the import dimension (documented envelope). Dual depth everywhere: plain port hint (`FIX_HINTS`/`suggestion`) + stable JSON (`ruleId`, `capability`, `fixClass: inject-port`, deterministic `nextAction`). |
|
|
65
67
|
| **Hook-path budgets (maintainer)** | Repository-only evidence: `npm run bench:hook-path`; `eval/performance/hook-budgets.v1.json`; CI job "Hook-path end-to-end budgets" | Measures the COMPLETE pre-tool paths as fresh child processes (hook cold/warm, doctor cold) at 1k/10k. D5 method locked: ceilings are Linux-baseline p95 + fixed headroom, set once per cycle, never ratcheted; scenarios without a recorded baseline stay in RECORDING mode and cannot fail CI. |
|
|
66
68
|
| **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
69
|
| **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
70
|
| **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
71
|
| **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
|
-
| **`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+`)
|
|
72
|
+
| **`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 of one path or a list of paths (schema `1.1+`), plus optional `childSlices.arkRulesFile` and `sliceAliases.pinned`; 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
73
|
| **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
74
|
| **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
75
|
| **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`. |
|
|
@@ -91,6 +93,7 @@ hardening guide remains repository-hosted rather than duplicated in the gate tar
|
|
|
91
93
|
| **Agent contract projection** | CLI `ark agents-md [--write] [--check] [--stdout] [--json]`; install/upgrade AGENTS templates; root API `buildAgentProjectionBlock` / `mergeAgentProjectionDocument` | Schema `1.0` (projection markers). Version-stamped managed block (`arkgateVersion` + contract summary + diagnostic short list). **Non-authoritative** — not a gate input; enforcement is ark-check / hooks / CI. Content-identity merge preserves customized regions outside markers. Drift: `--check` vs package version. |
|
|
92
94
|
| **Agent Skills packaging** | `templates/agent-skills/<name>/SKILL.md` (+ package README); root API `ARK_SKILL_NAMES` / `ARK_FIRST_CLASS_SKILL_NAMES` / `validateAgentSkillsPackage` / `validateSkillProductCapacity`; `npm run check:agent-skills` | Schema `1.0` (package contract). Closed catalog (`ARK_SKILL_NAMES`): first-class doors plus one-release stubs, 1:1 with flat `templates/skills/*.md`. First-class `/ark-order` (issue #216 / ADR 0036). `check:agent-skills` fails closed when a product plane or the north star drops out of skill bodies or living hubs. Add a name only with a live ROADMAP item. |
|
|
93
95
|
| **`arkgate/schema/arkrules`** or **`arkgate/schema/ark.arkrules.schema.json`** | Per-layer structure sensors + invariant catalog (ADR 0012) | Schema `1.0`. Opt-in via root `arkRules` map (`ark.config` schema `1.1`). |
|
|
96
|
+
| **Invariant probe** | CLI `arkgate-check --probe-invariants[=<id>] [--write] [--json] [--runner vitest\|jest\|node]` → JSON `probeInvariants`; optional committed `.ark/invariant-probe.json`; **`arkgate/schema/invariant-probe`** or **`arkgate/schema/ark.invariant-probe.schema.json`**; coverage evidence `probe` (type `InvariantProbeSummary`); promotion blocker `probe-survived`; doctor `rulesUnderContract.probe` and `--rules-inventory` `coverageEvidence.probe` when the file exists; catalog `INVARIANT_PROBE_SURVIVED` | Schema `1.0`, always `notAScore: true`. **Owner-invoked only** ([ADR 0039](adr/0039-invariant-mutation-probe.md)): runs the project's own covering tests (vitest, jest or `node --test`) in a temporary copy; never in the write hook, MCP, ESLint, the GitHub Action or `--strict-merge` (flag pairings refused). Closed operator set `ip-ops@1`, at most 3 changes per invariant after a green baseline and two wiring checks; rows `killed` \| `survived` \| `not-reached` \| `inconclusive` \| `unprobeable`, each bound to SHA-256 hashes of the symbol file, the covering tests and the invariant (without `mode`). The artifact can **only subtract**: a fresh `survived` / `not-reached` row refuses promotion on `--promote` and the policy delta; stale, killed, inconclusive, unprobeable, malformed or absent changes nothing. Exit 0 / 1 survived or not reached / 2 could not run (`reasonCode`). Best-effort environment allowlist — not a sandbox. Not a mutation score. No config key. |
|
|
94
97
|
| **`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
98
|
| **Config JSON Schema** | `arkgate/schema` or `arkgate/schema/ark.config.schema.json` | Stable package resource subpaths for editor completion and contract tooling. |
|
|
96
99
|
| **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. |
|
package/docs/product-voice.md
CHANGED
|
@@ -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.
|
|
83
|
+
(`npx arkgate@4.8.26 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.
|
|
@@ -134,6 +134,9 @@ Three beats when a line teaches:
|
|
|
134
134
|
| ArkRun is experimental. In-memory. Data is gone on restart. | The kernel is production-ready |
|
|
135
135
|
| The agent PATCHed the billing plan as if it were a seat count. The write didn’t land. Next: `proposeRelease` then `apply`, not `update`. | Freeze ξ. Four verbs. Haken slaving. |
|
|
136
136
|
| Status: one light, one next step. | Become an architect in 60 seconds |
|
|
137
|
+
| Nothing imports src/lib/legacy-pricing.ts, and no entry point covers it. Next: delete it through the write gate. | Dead code detected! Auto-removing unused files |
|
|
138
|
+
| This code is copied between features/billing and features/invoices. The wall stops the import, not the copy. Next: move it to shared/ with `/ark-place`. | 12.4% duplication! Refactor score: C |
|
|
139
|
+
| INV-REFUND-WINDOW: the tests still pass when the refund guard is negated. The test names the rule but does not pin it. Next: add a case that fails when the window is crossed. | Mutation score 67%! 2 of 3 mutants killed |
|
|
137
140
|
|
|
138
141
|
**Brands, then the common word:** ArkGate, ArkRules, ArkRun, ArkOrder. Gloss once.
|
|
139
142
|
|
|
@@ -212,6 +215,9 @@ a product PR — that is an owner `--contract-session` (config change).
|
|
|
212
215
|
- ArkRun as Postgres, an outbox, or Temporal
|
|
213
216
|
- ArkOrder as durable storage, a second npm package, or a replacement for ArkRun
|
|
214
217
|
- New skill *names* without a live queue item
|
|
218
|
+
- “Dead code” for a file nothing imports — say what was seen (nothing imports it), and say when a dynamic import may still load it
|
|
219
|
+
- A duplication percentage or score for copies — name the two places and the wall between them, and where the shared code could live
|
|
220
|
+
- A mutation score or percentage for the invariant probe — say which change went unnoticed, where, and which test to add
|
|
215
221
|
- “the house stays up” / “training wheels” / “mimo” on first contact
|
|
216
222
|
|
|
217
223
|
---
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "arkgate",
|
|
3
|
-
"version": "4.8.
|
|
3
|
+
"version": "4.8.26",
|
|
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",
|
|
@@ -75,6 +75,8 @@
|
|
|
75
75
|
"./schema/ark.arkrules.schema.json": "./schemas/ark.arkrules.schema.json",
|
|
76
76
|
"./schema/status-manifest": "./schemas/ark.status-manifest.schema.json",
|
|
77
77
|
"./schema/ark.status-manifest.schema.json": "./schemas/ark.status-manifest.schema.json",
|
|
78
|
+
"./schema/invariant-probe": "./schemas/ark.invariant-probe.schema.json",
|
|
79
|
+
"./schema/ark.invariant-probe.schema.json": "./schemas/ark.invariant-probe.schema.json",
|
|
78
80
|
"./package.json": "./package.json"
|
|
79
81
|
},
|
|
80
82
|
"typesVersions": {
|
|
@@ -165,6 +167,8 @@
|
|
|
165
167
|
"security:audit": "npm audit --omit=dev --audit-level=high",
|
|
166
168
|
"check:architecture": "node bin/ark-check.mjs --root . --config ark.config.json --strict",
|
|
167
169
|
"check:js": "node scripts/check-js-syntax.mjs",
|
|
170
|
+
"check:knip": "knip",
|
|
171
|
+
"check:duplication": "jscpd --config .jscpd.json",
|
|
168
172
|
"generate:layer-match": "node scripts/generate-layer-match.mjs",
|
|
169
173
|
"check:layer-match": "node scripts/generate-layer-match.mjs --check",
|
|
170
174
|
"generate:cli-pure": "node scripts/generate-cli-pure.mjs",
|
|
@@ -234,7 +238,7 @@
|
|
|
234
238
|
"browserslist": "4.28.8",
|
|
235
239
|
"nanoid": "3.3.18",
|
|
236
240
|
"brace-expansion@2": "2.1.4",
|
|
237
|
-
"brace-expansion@5": "5.0.
|
|
241
|
+
"brace-expansion@5": "5.0.12",
|
|
238
242
|
"tsup": {
|
|
239
243
|
"esbuild": "0.28.1"
|
|
240
244
|
},
|
|
@@ -244,12 +248,17 @@
|
|
|
244
248
|
},
|
|
245
249
|
"devDependencies": {
|
|
246
250
|
"@nestjs/common": "^11.1.27",
|
|
251
|
+
"@stryker-mutator/api": "9.6.1",
|
|
247
252
|
"@stryker-mutator/core": "^9.6.1",
|
|
248
253
|
"@stryker-mutator/vitest-runner": "^9.6.1",
|
|
249
254
|
"@types/node": "^20.14.0",
|
|
250
255
|
"@vitest/coverage-v8": "^4.1.11",
|
|
256
|
+
"acorn": "8.17.0",
|
|
257
|
+
"ajv": "8.18.0",
|
|
251
258
|
"esbuild": "0.28.1",
|
|
252
259
|
"fast-check": "^4.9.0",
|
|
260
|
+
"knip": "6.39.0",
|
|
261
|
+
"jscpd": "5.4.0",
|
|
253
262
|
"reflect-metadata": "^0.2.2",
|
|
254
263
|
"rxjs": "^7.8.2",
|
|
255
264
|
"tsup": "^8.1.0",
|
|
@@ -631,8 +631,22 @@
|
|
|
631
631
|
"arkRules": {
|
|
632
632
|
"type": "object",
|
|
633
633
|
"additionalProperties": {
|
|
634
|
-
"
|
|
635
|
-
"
|
|
634
|
+
"description": "A project-relative ArkRules path, or a list of paths merged into one catalog for this layer. A string keeps today's behaviour. An older build that types this value as a string rejects an array at config load.",
|
|
635
|
+
"oneOf": [
|
|
636
|
+
{
|
|
637
|
+
"type": "string",
|
|
638
|
+
"minLength": 1
|
|
639
|
+
},
|
|
640
|
+
{
|
|
641
|
+
"type": "array",
|
|
642
|
+
"minItems": 1,
|
|
643
|
+
"uniqueItems": true,
|
|
644
|
+
"items": {
|
|
645
|
+
"type": "string",
|
|
646
|
+
"minLength": 1
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
]
|
|
636
650
|
},
|
|
637
651
|
"default": {}
|
|
638
652
|
},
|
|
@@ -954,10 +968,15 @@
|
|
|
954
968
|
"minLength": 1,
|
|
955
969
|
"description": "Text for inner-wall findings (CROSS_SIBLING_SLICE and universe common importing a child). The rule message stays the universe-wall text (CROSS_PARENT_SLICE and fail-closed denies). Absent: ArkGate default text, never the rule message. arkgate 4.8.23 and older reject this field (unknown field)."
|
|
956
970
|
},
|
|
971
|
+
"arkRulesFile": {
|
|
972
|
+
"type": "string",
|
|
973
|
+
"minLength": 1,
|
|
974
|
+
"description": "Filename read at each child slice root this wall already resolved. The only token is <Layer>, replaced with a declared layer name (arkrules.<Layer>.json). No slash and no wildcard. A discovered file that omits appliesTo is scoped to that slice directory. Explicit appliesTo outside the slice fails config load (ARKRULE_SCOPE_ESCAPES_SLICE). The effective id is <childId>#<localId>. A build that rejects unknown childSlices fields fails at config load (unknown field)."
|
|
975
|
+
},
|
|
957
976
|
"sliceAliases": {
|
|
958
977
|
"type": "array",
|
|
959
978
|
"minItems": 1,
|
|
960
|
-
"description": "Maps a source path glob onto a child slice id (universe id plus one child segment) so files outside the slice trees take that universe and child for both walls. A path without a wildcard also covers everything under it, as sharedRoots does. A bare name, a universe id alone, a target outside every universe shape, and a wildcard in to are rejected. Under stars identity a target written against the 4.8.23 stars id (star bindings before the last literal dropped, e.g. api/v1/x for modules/*/api/*) still loads when it maps to one universe shape, and ark-check warns CONFIG_SLICE_LEGACY_STARS_ID with the new id; it is rejected only when it maps to more than one shape. Config load checks only the shape; doctor reports a target universe no file belongs to. The glob may not overlap a slice folder, and two aliases may not match the same file. Doctor lists each alias as an owed move. A build that rejects unknown childSlices fields fails at config load (unknown field). arkgate 4.8.22 and older reject childSlices.",
|
|
979
|
+
"description": "Maps a source path glob onto a child slice id (universe id plus one child segment) so files outside the slice trees take that universe and child for both walls. A path without a wildcard also covers everything under it, as sharedRoots does. A bare name, a universe id alone, a target outside every universe shape, and a wildcard in to are rejected. Under stars identity a target written against the 4.8.23 stars id (star bindings before the last literal dropped, e.g. api/v1/x for modules/*/api/*) still loads when it maps to one universe shape, and ark-check warns CONFIG_SLICE_LEGACY_STARS_ID with the new id; it is rejected only when it maps to more than one shape. Config load checks only the shape; doctor reports a target universe no file belongs to. The glob may not overlap a slice folder, and two aliases may not match the same file. Doctor lists each unpinned alias as an owed move. pinned true lists the alias separately and does not count it as debt. reason is a doctor label and does not pin by itself. A build that rejects unknown childSlices fields fails at config load (unknown field). arkgate 4.8.22 and older reject childSlices.",
|
|
961
980
|
"items": {
|
|
962
981
|
"type": "object",
|
|
963
982
|
"additionalProperties": false,
|
|
@@ -973,6 +992,15 @@
|
|
|
973
992
|
"to": {
|
|
974
993
|
"type": "string",
|
|
975
994
|
"minLength": 1
|
|
995
|
+
},
|
|
996
|
+
"pinned": {
|
|
997
|
+
"type": "boolean",
|
|
998
|
+
"description": "True when a framework forces the path to stay. The alias is not an owed move and does not clear other honesty debt."
|
|
999
|
+
},
|
|
1000
|
+
"reason": {
|
|
1001
|
+
"type": "string",
|
|
1002
|
+
"minLength": 1,
|
|
1003
|
+
"description": "Doctor label, such as framework-route. A label alone does not pin."
|
|
976
1004
|
}
|
|
977
1005
|
}
|
|
978
1006
|
}
|