arkgate 4.8.3 → 4.8.5
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 +279 -1
- package/README.md +17 -7
- package/bin/ark-check-runtime.mjs +340 -5
- package/bin/ark-layer-match.mjs +170 -13
- package/bin/ark-mcp-runtime.mjs +9 -2
- package/bin/lib/agent-homes.mjs +41 -6
- package/bin/lib/analysis-completeness.mjs +86 -0
- package/bin/lib/analysis-engine.mjs +8 -8
- package/bin/lib/architecture-scan.mjs +2 -0
- package/bin/lib/ark-order-invariants.mjs +37 -0
- package/bin/lib/ark-order-sensors.mjs +4 -0
- package/bin/lib/arkrules-contract.mjs +8 -1
- package/bin/lib/check-args.mjs +68 -0
- package/bin/lib/config-contract.mjs +26 -0
- package/bin/lib/design-smells.mjs +85 -0
- package/bin/lib/diagnostic-catalog.mjs +9 -1
- package/bin/lib/first-run-help.mjs +16 -4
- package/bin/lib/invariant-coverage-io.mjs +175 -19
- package/bin/lib/invariant-coverage.mjs +110 -7
- package/bin/lib/literal-path-drift-io.mjs +569 -0
- package/bin/lib/literal-path-drift.mjs +761 -0
- package/bin/lib/policy-delta-io.mjs +5 -0
- package/bin/lib/remediation.mjs +25 -1
- package/bin/lib/rules-under-contract.mjs +5 -0
- package/bin/lib/scan-files.mjs +54 -0
- package/bin/lib/sensor-promote-cli.mjs +372 -0
- package/bin/lib/sensor-promote-io.mjs +246 -0
- package/bin/lib/sensor-promotion.mjs +363 -0
- package/bin/lib/skill-catalog-apply.mjs +18 -1
- package/bin/lib/skill-install.mjs +4 -3
- package/bin/lib/upgrade-whats-new.mjs +1 -1
- package/dist/{configTypes-dNJ2C0yx.d.ts → configTypes-dy5PfTqS.d.ts} +31 -0
- package/dist/{diagnosticCatalog-C5GgeyEE.d.ts → diagnosticCatalog-DMO30svh.d.ts} +75 -7
- package/dist/eslint/index.cjs +5 -5
- package/dist/eslint/index.d.ts +34 -1
- package/dist/eslint/index.js +5 -5
- package/dist/index.cjs +30 -30
- package/dist/index.d.ts +68 -5
- package/dist/index.js +29 -29
- package/dist/nestjs/index.cjs +5 -5
- package/dist/nestjs/index.d.ts +3 -3
- package/dist/nestjs/index.js +5 -5
- package/dist/order/index.cjs +1 -1
- package/dist/order/index.d.ts +11 -2
- package/dist/order/index.js +1 -1
- package/dist/runtime/index.cjs +11 -11
- package/dist/runtime/index.d.ts +30 -7
- package/dist/runtime/index.js +11 -11
- package/dist/{types-dK24fDZa.d.ts → types-BuM8WNqe.d.ts} +1 -1
- package/dist/{types-DeK7SYGC.d.ts → types-CzE6LMaW.d.ts} +2 -2
- package/docs/README.md +5 -4
- package/docs/agent-guide.md +186 -4
- package/docs/ai-gates.md +13 -2
- package/docs/arkorder.md +210 -0
- package/docs/configuration.md +88 -5
- package/docs/develop.md +4 -2
- package/docs/diagnostics.md +97 -1
- package/docs/enthusiast/how-to-agent-gates.md +1 -1
- package/docs/package-surface.md +37 -5
- package/docs/use.md +1 -1
- package/package.json +3 -2
- package/schemas/ark.config.schema.json +63 -0
- package/server.json +3 -3
- package/templates/agent-skills/README.md +1 -1
- package/templates/agent-skills/ark-adopt/SKILL.md +5 -0
- package/templates/agent-skills/ark-coverage/SKILL.md +1 -0
- package/templates/agent-skills/ark-upgrade/SKILL.md +1 -1
- package/templates/skills/ark-adopt.md +5 -0
- package/templates/skills/ark-coverage.md +1 -0
- package/templates/skills/ark-upgrade.md +1 -1
package/docs/configuration.md
CHANGED
|
@@ -86,11 +86,38 @@ Top-level fields:
|
|
|
86
86
|
- `include`, `exclude`, `excludeGenerated`, `frameworkOverlay`
|
|
87
87
|
- `layers`, `rules`, `cyclePolicy`
|
|
88
88
|
- `dynamicImportAllowlist`, `safety`
|
|
89
|
+
- **`coverage`** (optional) — invariant-coverage scan controls: `testGlobs` (globs that decide
|
|
90
|
+
which files count as tests, replacing the built-in `*.test.*` / `tests/` name heuristic),
|
|
91
|
+
`maxFiles` (evidence file budget, default `400`) and `coverageRoots` (path prefixes where the
|
|
92
|
+
project declares its runner actually executes tests). Absence is silent and changes no verdict.
|
|
93
|
+
Unknown keys fail closed. When the budget is hit, `INVARIANT_UNCOVERED` reports the numbers
|
|
94
|
+
(files loaded, tests retained, files discarded at the cap) and names `coverage.maxFiles` as
|
|
95
|
+
the knob that raises it — coverage never claims "never had tests" because of our own cap.
|
|
96
|
+
`maxFiles` is clamped to a hard ceiling of 20000 (the config validator has no
|
|
97
|
+
`maximum` keyword, so a schema bound would be accepted and then ignored). The cap
|
|
98
|
+
bounds files **retained as evidence**, not files opened: a test is read before it can
|
|
99
|
+
be judged for naming an invariant, so the diagnostic reports files read alongside
|
|
100
|
+
files retained.
|
|
101
|
+
Nothing is dropped in silence: files past the budget, files over the 256KB per-file cap,
|
|
102
|
+
unreadable files or directories (permissions, broken symlinks), directories past the walk depth
|
|
103
|
+
limit (8), symlinks whose target resolves outside the project root, and tests naming no
|
|
104
|
+
catalogued invariant are each counted and named in the diagnostic. A symlinked test is read only
|
|
105
|
+
when its target is inside the root: a file that is not in this repo never proves an invariant
|
|
106
|
+
covered.
|
|
107
|
+
|
|
108
|
+
`coverageRoots` closes a false green ArkGate could otherwise produce. Coverage is proven by
|
|
109
|
+
matching an invariant id in a test title — a filesystem walk plus a text match. **ArkGate never
|
|
110
|
+
executes tests and never reads a runner config**, so a test in a folder no runner runs certifies
|
|
111
|
+
the invariant just as well as one that runs. Declaring `coverageRoots` gives ArkGate a second
|
|
112
|
+
declaration to compare the first against: when the only covering test falls outside them, it
|
|
113
|
+
reports `INVARIANT_COVERAGE_OUTSIDE_ROOTS` (advisory) and refuses to promote that invariant to
|
|
114
|
+
`enforced`. Declaring nothing keeps the old silence — without a declaration there is nothing to
|
|
115
|
+
compare, and ArkGate makes no claim about where tests run.
|
|
89
116
|
- **`arkRules`** (optional, schema `1.1+`) — map of layer name → project-relative path to an
|
|
90
117
|
ArkRules file (e.g. `"DomainModel": "arkrules/DomainModel.json"`). Keys must match a declared
|
|
91
118
|
layer. Missing/invalid referenced files **fail closed**.
|
|
92
|
-
- **`arkRun`** (optional, schema `1.2+`) — inline ArkRun extra (`mode`, `
|
|
93
|
-
`managedLayers`, `requireDeclarations`). Absence is silent. Unknown keys fail closed.
|
|
119
|
+
- **`arkRun`** (optional, schema `1.2+`) — inline ArkRun extra (`mode`, `kernelRoots`
|
|
120
|
+
(`compositionRoots` alias), `managedLayers`, `requireDeclarations`). Absence is silent. Unknown keys fail closed.
|
|
94
121
|
`managedLayers` must name existing `layers[].name` values. Empty `compositionRoots` in
|
|
95
122
|
`enforced` mode fails closed (`ARKRUN_MISSING_ROOT`); empty `managedLayers` in `enforced`
|
|
96
123
|
mode also fails closed (direct-new / undeclared / transport-bypass would otherwise no-op).
|
|
@@ -103,8 +130,15 @@ Top-level fields:
|
|
|
103
130
|
Import `createOrderPlane` from `arkgate/order` (same package). Empty `planeRoots` in
|
|
104
131
|
`enforced` mode fails closed (`ARKORDER_MISSING_PLANE`). `xiKeys` are the 3–5 slow
|
|
105
132
|
names the product already knows (plan, protocol, cost-code bound). Empty `xiKeys`
|
|
106
|
-
leaves `ARKORDER_XI_FIELD_WRITE` silent. Membership ids are not keys.
|
|
107
|
-
|
|
133
|
+
leaves `ARKORDER_XI_FIELD_WRITE` silent. Membership ids are not keys. Factory options
|
|
134
|
+
`informationBudget` and `sigmaMaxAgeMs` belong on `createOrderPlane`, not this extra
|
|
135
|
+
object. Demotion or deletion is a policy-delta **weakening**. Field ingest never mints
|
|
136
|
+
a pattern.
|
|
137
|
+
|
|
138
|
+
**Activation is one shape.** ArkRun and ArkOrder both use `mode` + `managedLayers`.
|
|
139
|
+
Absence of either extra is silent. They keep different *root* names because they
|
|
140
|
+
name different factories: `arkRun.kernelRoots` (`compositionRoots` alias) vs
|
|
141
|
+
`arkOrder.planeRoots`. Do not fold them. Canonical: [arkorder.md](arkorder.md#activation-same-shape-as-arkrun).
|
|
108
142
|
|
|
109
143
|
Layer fields:
|
|
110
144
|
|
|
@@ -130,11 +164,60 @@ process module-capability family must be denied.
|
|
|
130
164
|
|
|
131
165
|
Rule fields:
|
|
132
166
|
|
|
133
|
-
- `from`, `to`, `allowed`, `message`, `peerIsolation`, `sliceFolders`
|
|
167
|
+
- `from`, `to`, `allowed`, `message`, `peerIsolation`, `sliceFolders`, `sharedRoots`,
|
|
168
|
+
`allowedCrossSlice`
|
|
134
169
|
- `peerIsolation: true` + `allowed: false`: deny only when slice ids differ; same-slice allows
|
|
135
170
|
when both paths classify. Missing paths, empty slice folders, or unclassifiable slices
|
|
136
171
|
**fail closed** (deny — cannot prove same-slice).
|
|
137
172
|
|
|
173
|
+
#### Declared peerIsolation exceptions (4.8.4)
|
|
174
|
+
|
|
175
|
+
Fail-closed denies on **absence of evidence**, so in a repo that legitimately keeps shared code
|
|
176
|
+
outside `features/<slice>/` (a `ui/`, `hooks/`, `lib/permissions/` tree) every shared file reads as
|
|
177
|
+
a violation — thousands of them, none a real cross-slice import. That is ArkGate reporting *our*
|
|
178
|
+
inability to place a file as a fact about *your* code. Two declarations fix it, and a declaration
|
|
179
|
+
is evidence:
|
|
180
|
+
|
|
181
|
+
```jsonc
|
|
182
|
+
{
|
|
183
|
+
"from": "Features", "to": "Features", "allowed": false, "peerIsolation": true,
|
|
184
|
+
"sliceFolders": ["features"],
|
|
185
|
+
// Roots this repo keeps shared on purpose — no longer "unclassifiable".
|
|
186
|
+
"sharedRoots": ["ui", "layout", "providers", "hooks", "lib/permissions"],
|
|
187
|
+
// Directed slice→slice edges this repo wants. One entry = one direction.
|
|
188
|
+
"allowedCrossSlice": [{ "from": "features/checkout", "to": "features/catalog" }]
|
|
189
|
+
}
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
- `sharedRoots` is **anchored**: the root must start the repo-relative path, optionally after a
|
|
193
|
+
single conventional source folder (`src/`, `app/`). So `ui` covers `ui/button.tsx` and
|
|
194
|
+
`src/ui/button.tsx` but **not** `modules/a/ui/x.tsx` — an unanchored root would exempt a whole
|
|
195
|
+
tree you never declared. Write a deeper or monorepo root out (`packages/web/src/ui`) or glob it
|
|
196
|
+
(`packages/*/src/ui`). Matching is case-insensitive; a bare `*` or `**` is refused, because one
|
|
197
|
+
character must not disable fail-closed. A path that still resolves to a slice keeps its slice —
|
|
198
|
+
`features/auth/ui/form.tsx` stays `features/auth` — so a shared root can never launder a real
|
|
199
|
+
cross-slice edge.
|
|
200
|
+
- `allowedCrossSlice` entries match a full slice id (`features/catalog`) or a bare slice name
|
|
201
|
+
(`catalog`), and only in the direction written. The reverse edge still denies. A bare name
|
|
202
|
+
matches that name under **any** slice folder, so in a repo with several slice parents
|
|
203
|
+
(`features/auth` and `modules/auth`) write the full id — `features/auth` — or the
|
|
204
|
+
declaration allows more edges than you meant.
|
|
205
|
+
- Everything else is unchanged: two different slices with nothing declared still deny, and a file
|
|
206
|
+
that is neither in a slice nor under a declared shared root still **fails closed**.
|
|
207
|
+
- The denial now names which reason fired — `cross-slice edge features/a → features/b` (a fact
|
|
208
|
+
about your code) versus `unclassifiable path (src/widgets/x.tsx)` (a fact about our evidence).
|
|
209
|
+
`no slice folders` and `no path evidence` are the two remaining evidence reasons. A rule-level
|
|
210
|
+
`message` override no longer hides it: the reason is appended to your text, not replaced by it.
|
|
211
|
+
- Both declarations are **weakening** changes in `ark policy-delta`
|
|
212
|
+
(`shared-roots-added`, `cross-slice-allowance-added`), so a policy review sees them. Both are
|
|
213
|
+
inert on a rule without `peerIsolation: true`, and policy-delta stays silent about them until
|
|
214
|
+
the wall exists.
|
|
215
|
+
|
|
216
|
+
**The recommended model is still to promote a genuinely shared slice to its own layer** and let
|
|
217
|
+
the layer edges carry it: a one-way peer import between slices is a DAG the layer graph cannot
|
|
218
|
+
see. `sharedRoots` and `allowedCrossSlice` exist so ArkGate can enforce a design that made the
|
|
219
|
+
other choice deliberately, not so slices can drift into a mesh.
|
|
220
|
+
|
|
138
221
|
### Type-only edges (placement debt)
|
|
139
222
|
|
|
140
223
|
`import type` / `export type` and pure type-only named bindings are **type placement debt**, not
|
package/docs/develop.md
CHANGED
|
@@ -10,7 +10,8 @@ request. Not an API Gateway. Not a folder linter. Without a required CI status,
|
|
|
10
10
|
and ArkOrder are optional — policies, an experimental runtime, and the extra that
|
|
11
11
|
stops slow product decisions being CRUD. Not a second install.
|
|
12
12
|
|
|
13
|
-
If you only want the happy path, start at [use.md](use.md).
|
|
13
|
+
If you only want the happy path, start at [use.md](use.md). Optional ArkOrder
|
|
14
|
+
(library + sensors, not a service): [arkorder.md](arkorder.md).
|
|
14
15
|
|
|
15
16
|
### Why it exists
|
|
16
17
|
|
|
@@ -60,7 +61,7 @@ Full matrix and install commands: [ai-gates.md](ai-gates.md) · canonical table
|
|
|
60
61
|
|
|
61
62
|
```bash
|
|
62
63
|
# All common hosts (examples)
|
|
63
|
-
npx arkgate-check --install-agent-gates --tools claude,cursor,codex,grok
|
|
64
|
+
npx arkgate-check --install-agent-gates --tools claude,cursor,codex,grok,antigravity
|
|
64
65
|
npx arkgate-check --install-agent-gates --tools antigravity # alias: agy
|
|
65
66
|
npx arkgate-check --install-agent-gates --tools opencode
|
|
66
67
|
# Full /ark-* skill pack (optional expert depth)
|
|
@@ -86,6 +87,7 @@ can show `runtimeObserved: true` even when branch-protection policy is plan-unav
|
|
|
86
87
|
| Layers, rules, globs | [configuration.md](configuration.md) · `ark.config.json` |
|
|
87
88
|
| ArkRules (structure + invariants) | [configuration.md](configuration.md#arkrules-intra-layer-opt-in) · `arkRules` / `arkrules/*` · skill `/ark-adopt` (session 0) or `/ark-autopilot` |
|
|
88
89
|
| Rules inventory (brownfield) | `arkgate-check --rules-inventory` · MCP `ark_rules_inventory` |
|
|
90
|
+
| Which rules can be enforced | `arkgate-check --sensors` (the map) · `arkgate-check --promote [<ruleId>] [--apply]` (the price) · [agent-guide](agent-guide.md#which-rules-can-be-enforced---sensors---promote) |
|
|
89
91
|
| Stable package API | [package-surface.md](package-surface.md) |
|
|
90
92
|
| Diagnostic codes (`ruleId` why/fix) | [diagnostics.md](diagnostics.md) · root `DIAGNOSTIC_CATALOG` |
|
|
91
93
|
| Session / project status snapshot | `ark status --json` · MCP `ark_status` · [session recipe](agent-guide.md#session-recipe-agent-turn) · schema `arkgate/schema/status-manifest` |
|
package/docs/diagnostics.md
CHANGED
|
@@ -44,6 +44,7 @@ Link form for agents: `docs/diagnostics.md#RULE_ID` (exact-case HTML anchors bel
|
|
|
44
44
|
| [`ARKRULE_INVARIANT`](#ARKRULE_INVARIANT) | arkrules | ArkRule invariant failed |
|
|
45
45
|
| [`ARKRULE_SCOPE_EMPTY`](#ARKRULE_SCOPE_EMPTY) | arkrules | ArkRule appliesTo matched zero files |
|
|
46
46
|
| [`INVARIANT_UNCOVERED`](#INVARIANT_UNCOVERED) | arkrules | Invariant without coverage evidence |
|
|
47
|
+
| [`INVARIANT_COVERAGE_OUTSIDE_ROOTS`](#INVARIANT_COVERAGE_OUTSIDE_ROOTS) | arkrules | Covering test outside the declared coverage roots |
|
|
47
48
|
| [`ARKRUN_MISSING_ROOT`](#ARKRUN_MISSING_ROOT) | arkrun | No kernel factory in composition roots |
|
|
48
49
|
| [`ARKRUN_KERNEL_IN_DOMAIN`](#ARKRUN_KERNEL_IN_DOMAIN) | arkrun | Domain-role layer imports the kernel |
|
|
49
50
|
| [`ARKRUN_DIRECT_NEW`](#ARKRUN_DIRECT_NEW) | arkrun | Managed type constructed with new |
|
|
@@ -69,7 +70,10 @@ Link form for agents: `docs/diagnostics.md#RULE_ID` (exact-case HTML anchors bel
|
|
|
69
70
|
| [`DESIGN_SMELL_REGRESSION`](#DESIGN_SMELL_REGRESSION) | preflight | Design smell regression on base-relative ratchet |
|
|
70
71
|
| [`ANALYSIS_PARSE_INCOMPLETE`](#ANALYSIS_PARSE_INCOMPLETE) | analysis | Parse incomplete |
|
|
71
72
|
| [`LEXICAL_EVIDENCE_INCOMPLETE`](#LEXICAL_EVIDENCE_INCOMPLETE) | analysis | Lexical evidence incomplete |
|
|
73
|
+
| [`ANALYSIS_COVERS_NO_FILES`](#ANALYSIS_COVERS_NO_FILES) | analysis | Analysis covered no files |
|
|
72
74
|
| [`ANALYSIS_HOST_UNAVAILABLE`](#ANALYSIS_HOST_UNAVAILABLE) | analysis | Analysis host unavailable |
|
|
75
|
+
| [`LITERAL_PATH_DRIFT`](#LITERAL_PATH_DRIFT) | drift | Literal path moved by a rename |
|
|
76
|
+
| [`LITERAL_PATH_UNRESOLVED`](#LITERAL_PATH_UNRESOLVED) | drift | Literal path does not resolve |
|
|
73
77
|
| [`ADAPTER_NOT_ALLOWED_FOR_PORT`](#ADAPTER_NOT_ALLOWED_FOR_PORT) | adapter | Adapter not allowed for port |
|
|
74
78
|
| [`FORBIDDEN_PATTERN`](#FORBIDDEN_PATTERN) | snippet-policy | Forbidden regex pattern |
|
|
75
79
|
| [`FORBIDDEN_SUBSTRING`](#FORBIDDEN_SUBSTRING) | snippet-policy | Forbidden substring |
|
|
@@ -281,7 +285,18 @@ Link form for agents: `docs/diagnostics.md#RULE_ID` (exact-case HTML anchors bel
|
|
|
281
285
|
**Invariant without coverage evidence**
|
|
282
286
|
|
|
283
287
|
- **Why:** An ArkRules invariant is under contract but no covering test title or declared symbol evidence was found (or coverage is partial). Kind is `never-had-tests` (adopt residual) vs `tests-disappeared` (suite exists).
|
|
284
|
-
- **Fix:** Add a test title or declared symbol covering the arkruleId, then preflight again. Treat never-had-tests as adopt residual; treat tests-disappeared as a regression. Missing test globs report partial — never fake green.
|
|
288
|
+
- **Fix:** Add a test title or declared symbol covering the arkruleId, then preflight again. 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.
|
|
289
|
+
|
|
290
|
+
<a id="INVARIANT_COVERAGE_OUTSIDE_ROOTS"></a>
|
|
291
|
+
|
|
292
|
+
### `INVARIANT_COVERAGE_OUTSIDE_ROOTS`
|
|
293
|
+
|
|
294
|
+
**Covering test outside the declared coverage roots** · often advisory
|
|
295
|
+
|
|
296
|
+
- **Why:** The only test naming this invariant sits outside `coverage.coverageRoots` — the places the project declares its runner executes. ArkGate matches declared text and never executes tests, so it cannot tell whether that file is ever run: coverage there is a test that *exists*, not a test that *runs*.
|
|
297
|
+
- **Fix:** Move the test under a declared coverage root, or add its root to `coverage.coverageRoots` in ark.config.json. Advisory: it never fails strict, but promotion to enforced refuses on it.
|
|
298
|
+
|
|
299
|
+
Declaring nothing is silent — without `coverage.coverageRoots` there is no second declaration to compare against, and ArkGate makes no claim about where tests run.
|
|
285
300
|
|
|
286
301
|
## ArkRun (opt-in extra)
|
|
287
302
|
|
|
@@ -409,6 +424,33 @@ Haken slaving: few slow keys (ξ) determine derived fast state. Field ingest nev
|
|
|
409
424
|
- **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.
|
|
410
425
|
- **Fix:** Keep invoices, seats, hours, and logs on ingest. Change the slow key with proposeRelease + release, then preflight again. Never mechanical-safe.
|
|
411
426
|
|
|
427
|
+
<a id="ARKORDER_INFORMATION_BUDGET"></a>
|
|
428
|
+
|
|
429
|
+
### `ARKORDER_INFORMATION_BUDGET`
|
|
430
|
+
|
|
431
|
+
**Projection observes a forbidden kind**
|
|
432
|
+
|
|
433
|
+
- **Why:** h(ξ) allowedKinds includes a kind listed in informationBudget.cannotObserve. A scale may not look at what it was told not to see.
|
|
434
|
+
- **Fix:** Cut that kind from the projector or from cannotObserve, then preflight again. Never mechanical-safe.
|
|
435
|
+
|
|
436
|
+
<a id="ARKORDER_XI_TTL"></a>
|
|
437
|
+
|
|
438
|
+
### `ARKORDER_XI_TTL`
|
|
439
|
+
|
|
440
|
+
**Slow key carries a freshness field**
|
|
441
|
+
|
|
442
|
+
- **Why:** ξ named ttl/freshUntil/maxAge. Freshness belongs on σ. A slow parameter that expires per transaction is not slow.
|
|
443
|
+
- **Fix:** Move freshness onto σ (freshUntil) and keep ξ stable, then preflight again. Never mechanical-safe.
|
|
444
|
+
|
|
445
|
+
<a id="ARKORDER_STALE_SIGMA"></a>
|
|
446
|
+
|
|
447
|
+
### `ARKORDER_STALE_SIGMA`
|
|
448
|
+
|
|
449
|
+
**σ is stale**
|
|
450
|
+
|
|
451
|
+
- **Why:** ingest ran after σ.freshUntil (or sigmaMaxAgeMs). ξ does not TTL.
|
|
452
|
+
- **Fix:** Refresh σ and ingest again, or freeze a new release if the pattern changed. Never mechanical-safe.
|
|
453
|
+
|
|
412
454
|
## Atomic preflight and change sets
|
|
413
455
|
|
|
414
456
|
<a id="INVALID_CHANGE_PATH"></a>
|
|
@@ -530,6 +572,15 @@ Haken slaving: few slow keys (ξ) determine derived fast state. Field ingest nev
|
|
|
530
572
|
- **Why:** Single-file validation cannot prove project module resolution. The write hook is already the verdict.
|
|
531
573
|
- **Fix:** Re-run `npx arkgate-check --root . --config ark.config.json`, or treat the hook deny as final. Do not call `ark_prepare_change` from a hook deny.
|
|
532
574
|
|
|
575
|
+
<a id="ANALYSIS_COVERS_NO_FILES"></a>
|
|
576
|
+
|
|
577
|
+
### `ANALYSIS_COVERS_NO_FILES`
|
|
578
|
+
|
|
579
|
+
**Analysis covered no files**
|
|
580
|
+
|
|
581
|
+
- **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` that is not the tree the contract describes (including a contract found outside the requested root, whose directory is then adopted as the project root), `include` / `exclude` patterns that match nothing, or layer patterns written for a different layout.
|
|
582
|
+
- **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.
|
|
583
|
+
|
|
533
584
|
<a id="ANALYSIS_HOST_UNAVAILABLE"></a>
|
|
534
585
|
|
|
535
586
|
### `ANALYSIS_HOST_UNAVAILABLE`
|
|
@@ -539,6 +590,51 @@ Haken slaving: few slow keys (ξ) determine derived fast state. Field ingest nev
|
|
|
539
590
|
- **Why:** No usable TypeScript / analysis host was available for this invocation.
|
|
540
591
|
- **Fix:** Install a supported TypeScript version visible to the project, then re-run. Unavailable analysis is fail-closed.
|
|
541
592
|
|
|
593
|
+
## Literal path drift
|
|
594
|
+
|
|
595
|
+
Reported by `arkgate-check --path-drift` only — this pass is not part of the
|
|
596
|
+
architecture verdict. A path written inside a string, a comment or a docstring
|
|
597
|
+
is invisible to the rest of the gate: `tsc` resolves imports, not strings, and
|
|
598
|
+
ESLint does not either, so a rename compiles green and the reference lies
|
|
599
|
+
afterwards. Field data from a 783-rename migration found the drift in four
|
|
600
|
+
forms — the tsconfig alias, a relative literal, a path written without the
|
|
601
|
+
include-root prefix, and prose (the largest class, and the only one with no
|
|
602
|
+
detector anywhere; it turned up in `.ts`, `.tsx` and `.css`).
|
|
603
|
+
|
|
604
|
+
The pass reads every text format where a repo path is written by hand
|
|
605
|
+
(`.ts .tsx .mts .cts .js .jsx .mjs .cjs .css .scss .json .md`), skips generated
|
|
606
|
+
files, and reports every file it refused to read, by reason.
|
|
607
|
+
|
|
608
|
+
<a id="LITERAL_PATH_DRIFT"></a>
|
|
609
|
+
|
|
610
|
+
### `LITERAL_PATH_DRIFT`
|
|
611
|
+
|
|
612
|
+
**Literal path moved by a rename**
|
|
613
|
+
|
|
614
|
+
- **Why:** A repo path written inside a string, a comment or a docstring no longer resolves, and the rename set says where it went. Nothing in the gate sees this class: `tsc` resolves imports, not strings, and ESLint does not either, so the rename compiles green and the reference lies afterwards. It appears in four forms — the tsconfig alias, a relative literal, a path written without the include-root prefix, and prose — and a hand sweep reliably covers one of them.
|
|
615
|
+
- **Fix:** Apply the suggested replacement, or re-run `npx arkgate-check --path-drift --base-ref <ref> --write` to apply every writable anchored replacement at once. The rewrite is mechanical and one-directional: the destination comes from the rename, it must itself resolve and be path-shaped, and the token is rewritten in the form the author wrote it in. A destination that leaves the alias root of the literal is reported with the target only and must be rewritten by hand.
|
|
616
|
+
|
|
617
|
+
Only a rename whose source really is gone and that has exactly one destination
|
|
618
|
+
may anchor a finding. A source that maps to two destinations is not a
|
|
619
|
+
one-directional fix, so it anchors nothing and its references fall through to
|
|
620
|
+
the advisory list below; the count is printed.
|
|
621
|
+
|
|
622
|
+
<a id="LITERAL_PATH_UNRESOLVED"></a>
|
|
623
|
+
|
|
624
|
+
### `LITERAL_PATH_UNRESOLVED`
|
|
625
|
+
|
|
626
|
+
**Literal path does not resolve** · often advisory
|
|
627
|
+
|
|
628
|
+
- **Why:** A literal that looks like a repo path does not resolve under this root, and no rename explains where it went. Unlike `LITERAL_PATH_DRIFT` this is a candidate, not a verdict: with nothing to anchor it, ArkGate cannot tell a dead reference from an illustrative path in a comment, an example in documentation, or a path belonging to another tree.
|
|
629
|
+
- **Fix:** Read the candidate and decide: fix the path, or leave it. Advisory only — it never fails a run and is never rewritten by `--write`, because there is no destination to propose. Run `--path-drift --all` to list the sweep.
|
|
630
|
+
|
|
631
|
+
The sweep is opt-in for exactly the reason the coverage budget is reported
|
|
632
|
+
rather than hidden: on a repository that *writes about* paths it produced 4085
|
|
633
|
+
candidates out of 9536 literals, nearly all of them illustrative. Listing that
|
|
634
|
+
by default would be ArkGate's inability to resolve a string presented as a fact
|
|
635
|
+
about your code. The count is always printed, so opting out of the list is
|
|
636
|
+
never opting out of knowing.
|
|
637
|
+
|
|
542
638
|
## Port adapters
|
|
543
639
|
|
|
544
640
|
<a id="ADAPTER_NOT_ALLOWED_FOR_PORT"></a>
|
package/docs/package-surface.md
CHANGED
|
@@ -35,7 +35,7 @@ hardening guide remains repository-hosted rather than duplicated in the gate tar
|
|
|
35
35
|
| **Field upgrade truth (4.5.6)** | `ark upgrade` registry-aware install; JSON `reasonCode` / `suggestedInstallCmd`; `skillDrift`; `--refresh-skills`; `postUpgradeChecks`; `hostSelection`. | No false-skip when registry ahead; offline honesty; customized skills preserved unless opt-in refresh; checks are advisory only. |
|
|
36
36
|
| **MCP process package honesty (4.5.6; fail-closed in 4.6.4)** | Every MCP tool context → `processPackage` (`processPackageMismatch` / `processStale`, versions, `nextAction`). | Multi-checkout users: restart MCP after pin bump. From 4.6.4, stale process context is non-authoritative; `ark_identity` remains diagnostic and project tools return `PROCESS_PACKAGE_STALE` until restart/retarget. Prefer project-local CLI until identity matched and versions align. |
|
|
37
37
|
| **Doctor design fitness** | `ark-check --doctor --json` → `doctor.designFitness`, `doctor.designSmells[]` | Additive. Stable smell `id`s: `io-under-application`, `handler-in-persistence`, `god-module`, `domain-logic-in-ui`, `facade-sql-in-routes`, `mixed-pattern-cluster`, `soft-contract`. `handler-in-persistence` covers static ES imports/re-exports of framework HTTP surfaces (`next/server`), `defineRoute` calls, and existing handler bodies inside Persistence-role layers or specific persistence paths; `require()` and dynamic `import()` are outside this narrow advisory, and a generic `Infrastructure` role alone is not Persistence. Persistence candidates are filtered and sorted before the bounded content scan so large application prefixes cannot hide the advisory. The detector inspects the first 800 sorted Persistence candidates; later candidates are uninspected, so **absence of a smell is not full-tree proof** above that envelope (incomplete/`partial` analysis also never proves “no smells”). **4.2 feedback hardening:** mode labels preserve the observed SUGGEST/ADAPT/ENFORCE state; a local permission/UI-state `canEdit` name alone is not a domain smell; real UI business rules route Domain → Application → UI; seed/fixture/demo/migration/generated files are not god-module pilots. Each smell has `evidence[]`, `fix`, technical `message`, and plain-language **`outcome`**. Does **not** fail the gate by itself. |
|
|
38
|
-
| **Post-green Shape door** | `doctor.postGreenPath`, `doctor.primaryNextAction`, `doctor.healthyFinishedForbidden` | Additive when `designFitness.designWeak`. Single Shape door (`id: clarify-for-ai`): explore shape-focus → dual-plan B → autopilot only with OK. Never empty plan A = healthy finished. |
|
|
38
|
+
| **Post-green Shape door** | `doctor.postGreenPath`, `doctor.primaryNextAction`, `doctor.healthyFinishedForbidden` | Additive when `designFitness.designWeak`. Single Shape door (`id: clarify-for-ai`): explore shape-focus → dual-plan B → autopilot only with OK. Never empty plan A = healthy finished. A **passing** `ark-check` human run also prints one dim line naming `--plan`, the smell count and the smell ids. Its weakness test is `isDesignWeak` over the **blocking** violation count — the same input doctor uses, so the two surfaces cannot disagree about `designWeak`, and non-blocking type-only placement debt still gets the line. Report only: never a warning, never an exit-code change, silent when no smell remains, silent on `--changed` (a partial scan would report a slice as the tree) and on `--watch`. A baselined run is not called clean: the opening clause names the suppressed count instead. `--json` is unchanged. |
|
|
39
39
|
| **Golden pattern (new code)** | Optional `.ark/golden-pattern.json`; doctor JSON `doctor.goldenPattern`; MCP `ark_place` / `ark_prepare_write` → `goldenPattern` | Additive, **advisory for NEW code only**. Required fields: `name`, `norm`; optional `newCodeHome`, `examplePath`, `schemaVersion`. **Absent is normal** (no claim). Never ENFORCE; never clears design-weak. Malformed → `invalid: true`, not silent guidance. |
|
|
40
40
|
| **Plan pattern B (Shape bets)** | `ark-check --plan --json` → `plan.patternBets[]`, `plan.goal.designWeak` | Additive. Each bet: `id`, `smellId`, `pilot`, `evidence`, `successSignal`, `killSwitch`, **`neverMechanicalSafe: true`**, `class: "judgment"`. **Never** auto-applied by loop/autoPatch; not a `remediationKind` mechanical-safe. `goal.met` remains edge honesty only. |
|
|
41
41
|
| **Pilot loop (one at a time)** | `plan.pilotLoop` / `doctor.pilotLoop` | Additive. When design-weak: `active`, `oneAtATime`, `neverMechanicalSafe`, **`nextPilot`** extraction-card fields (`pilotTarget`, `smellId`, `move`, `successSignal`, `killSwitch`, `doNot[]`). **One pilot → re-doctor**; never multi-pilot batch; never mechanical-safe. |
|
|
@@ -51,7 +51,7 @@ hardening guide remains repository-hosted rather than duplicated in the gate tar
|
|
|
51
51
|
| **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`. |
|
|
52
52
|
| **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. |
|
|
53
53
|
| **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. |
|
|
54
|
-
| **`ark.config.json`** | Layer globs, rules, include/exclude, forbiddenGlobals, intent prefixes, `peerIsolation`, `dynamicImportAllowlist`, `safety` thresholds; 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 `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). |
|
|
54
|
+
| **`ark.config.json`** | Layer globs, 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). |
|
|
55
55
|
| **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. |
|
|
56
56
|
| **`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`. |
|
|
57
57
|
| **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. |
|
|
@@ -173,9 +173,9 @@ product claims**. Static architecture enforcement does not depend on them.
|
|
|
173
173
|
|
|
174
174
|
| Surface | Import path | Notes |
|
|
175
175
|
|---------|-------------|--------|
|
|
176
|
-
| **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, and `/graph` slices of the information package (no public / authless bind). 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). |
|
|
176
|
+
| **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, and `/graph` slices of the information package (no public / authless bind). **Shadow / replay / compare** (`shadowInformationPackage`, `compareInformationPackages`, `replayInformationPackages`) are in-memory helpers on that snapshot — not durable, not a second bus (ADR 0033). 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). |
|
|
177
177
|
| **NestJS adapter** | **`arkgate/nestjs`** | Experimental optional peer `@nestjs/common` for the ArkRun kernel. Same npm package. `@arkgate/runtime/nestjs` is deprecated. |
|
|
178
|
-
| **ArkOrder plane** | **`arkgate/order`** | Public brand **ArkOrder**. Same npm package `arkgate` (ADR 0030) — not `@arkgate/order`. Factory `createOrderPlane`. Four verbs: `release` / `project` / `ingest` / `proposeRelease`. No `update`. Haken: few slow keys; ingest never mints a pattern; empty blast fails closed. Root `arkgate` export does **not** include the factory. Optional extra `arkOrder` on schema `1.3`. In-memory; not durable. Does not replace ArkRun. |
|
|
178
|
+
| **ArkOrder plane** | **`arkgate/order`** | Public brand **ArkOrder**. Same npm package `arkgate` (ADR 0030) — not `@arkgate/order`. Factory `createOrderPlane`. Four verbs: `release` / `project` / `ingest` / `proposeRelease`. No `update`. Haken: few slow keys; ingest never mints a pattern; empty blast fails closed. Factory options (not config keys): `informationBudget.cannotObserve`, `sigmaMaxAgeMs` (freshness on σ, never ξ). `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 replace ArkRun. Runtime half (shadow/replay/compare) is ArkRun (ADR 0033). Canonical: [ArkOrder](arkorder.md). |
|
|
179
179
|
|
|
180
180
|
---
|
|
181
181
|
|
|
@@ -190,6 +190,36 @@ import { createOrderPlane } from 'arkgate/order';
|
|
|
190
190
|
|
|
191
191
|
One install: `npm install arkgate`. `@arkgate/runtime` is deprecated.
|
|
192
192
|
|
|
193
|
+
---
|
|
194
|
+
|
|
195
|
+
## Installing from git
|
|
196
|
+
|
|
197
|
+
`npm install github:pedroknigge/arkgate` and `pnpm add git+https://github.com/pedroknigge/arkgate`
|
|
198
|
+
are supported for pinning an unreleased commit, and they need **no build-allowlist entry**:
|
|
199
|
+
this package declares no `prepack`, no `prepare`, and no install script. Through 4.8.3 the
|
|
200
|
+
`prepack` hook made a pnpm git install fail closed with
|
|
201
|
+
`ERR_PNPM_GIT_DEP_PREPARE_NOT_ALLOWED` — the whole install, not just the build — and the only
|
|
202
|
+
way through was adding `arkgate` to `allowBuilds` for a script that shells out to `npm`.
|
|
203
|
+
The publish path builds explicitly instead (`scripts/release-npm.mjs`, plus `prepublishOnly`
|
|
204
|
+
as a backstop, which pnpm does not run when it prepares a git dependency).
|
|
205
|
+
|
|
206
|
+
A git install is **not** the same package as the npm tarball, because `dist/` is a build
|
|
207
|
+
output and is not committed:
|
|
208
|
+
|
|
209
|
+
| From a git install | Works | Why |
|
|
210
|
+
|---|---|---|
|
|
211
|
+
| `arkgate` / `arkgate-check` (and `ark` / `ark-check`) | **yes** | `bin/` and `bin/lib/*.mjs` are committed sources — the deliberate zero-build CLI |
|
|
212
|
+
| `arkgate/schema*` subpath exports | **yes** | `schemas/*.json` are committed |
|
|
213
|
+
| `import … from 'arkgate'`, `arkgate/eslint`, `arkgate/order`, `arkgate/runtime`, `arkgate/nestjs` | **no** | all resolve into `dist/` |
|
|
214
|
+
| `ark-mcp` / `arkgate-mcp` | **no** | loads `dist/index.js`; it says so and names this section |
|
|
215
|
+
|
|
216
|
+
So: install from git when you want the **CLI or the CI check** at a specific commit, install
|
|
217
|
+
from npm (`npm i arkgate`) when you want the **library, the MCP server, or the ESLint plugin**.
|
|
218
|
+
To get everything from a checkout, clone and run `npm install && npm run build` — that is the
|
|
219
|
+
maintainer path in [develop.md](https://github.com/pedroknigge/arkgate/blob/main/docs/develop.md),
|
|
220
|
+
and the build stays *your* explicit command, never a script your package manager has to be
|
|
221
|
+
allowed to run.
|
|
222
|
+
|
|
193
223
|
See [production-hardening.md](https://github.com/pedroknigge/arkgate/blob/main/docs/production-hardening.md) for requirements an eventual
|
|
194
224
|
production deployment would need to satisfy; it is not a readiness certification.
|
|
195
225
|
|
|
@@ -219,7 +249,9 @@ production deployment would need to satisfy; it is not a readiness certification
|
|
|
219
249
|
## Release notes (maintainers)
|
|
220
250
|
|
|
221
251
|
Ship notes for a version live under [releases/](https://github.com/pedroknigge/arkgate/tree/main/docs/releases)
|
|
222
|
-
(current
|
|
252
|
+
(current tree prepared: [4.8.5.md](https://github.com/pedroknigge/arkgate/blob/main/docs/releases/4.8.5.md);
|
|
253
|
+
current published: [4.8.4.md](https://github.com/pedroknigge/arkgate/blob/main/docs/releases/4.8.4.md);
|
|
254
|
+
prior published: [4.8.3.md](https://github.com/pedroknigge/arkgate/blob/main/docs/releases/4.8.3.md);
|
|
223
255
|
prior published: [4.8.2.md](https://github.com/pedroknigge/arkgate/blob/main/docs/releases/4.8.2.md);
|
|
224
256
|
prior published: [4.8.1.md](https://github.com/pedroknigge/arkgate/blob/main/docs/releases/4.8.1.md);
|
|
225
257
|
prior published: [4.8.0.md](https://github.com/pedroknigge/arkgate/blob/main/docs/releases/4.8.0.md);
|
package/docs/use.md
CHANGED
|
@@ -81,7 +81,7 @@ The config only binds when the write doesn’t land and CI is required.
|
|
|
81
81
|
| **ArkGate** (layers) | Import rules. The write doesn’t land. The PR fails. | Always — this is the product |
|
|
82
82
|
| **ArkRules** | Optional policies *inside* a layer. | Off until you turn it on (start may ship advisory templates) |
|
|
83
83
|
| **ArkRun** | Optional experimental runtime (`arkgate/runtime`) | Off. In-memory. Not Postgres. |
|
|
84
|
-
| **ArkOrder** | Stops the agent rewriting the few slow product decisions as CRUD (`arkgate/order`) | Off. Name `xiKeys` (plan / protocol, not `projectId`). Invoices and seats still flow. In-memory. Not durable. |
|
|
84
|
+
| **ArkOrder** | Stops the agent rewriting the few slow product decisions as CRUD (`arkgate/order`). Library + sensors, [not a service](arkorder.md). | Off. Name `xiKeys` (plan / protocol, not `projectId`). Invoices and seats still flow. In-memory. Not durable. |
|
|
85
85
|
|
|
86
86
|
Start always gives you **layers**. Compact starters do **not** turn on ArkRun or
|
|
87
87
|
ArkOrder. No extras is fine — only ArkGate runs. Leftovers are labeled
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "arkgate",
|
|
3
|
-
"version": "4.8.
|
|
3
|
+
"version": "4.8.5",
|
|
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",
|
|
@@ -88,6 +88,7 @@
|
|
|
88
88
|
"docs/assets",
|
|
89
89
|
"docs/brownfield-adoption.md",
|
|
90
90
|
"docs/configuration.md",
|
|
91
|
+
"docs/arkorder.md",
|
|
91
92
|
"docs/package-surface.md",
|
|
92
93
|
"docs/diagnostics.md",
|
|
93
94
|
"docs/threat-model.md",
|
|
@@ -163,7 +164,7 @@
|
|
|
163
164
|
"audit:beta-exit": "node scripts/beta-exit-audit.mjs",
|
|
164
165
|
"clean": "rm -rf dist",
|
|
165
166
|
"release:npm": "node scripts/release-npm.mjs",
|
|
166
|
-
"
|
|
167
|
+
"prepublishOnly": "npm run build",
|
|
167
168
|
"bench:hook-path": "node scripts/hook-path-bench.mjs"
|
|
168
169
|
},
|
|
169
170
|
"dependencies": {
|
|
@@ -625,6 +625,9 @@
|
|
|
625
625
|
"allowDisabledPeerIsolation": false
|
|
626
626
|
}
|
|
627
627
|
},
|
|
628
|
+
"coverage": {
|
|
629
|
+
"$ref": "#/$defs/coverage"
|
|
630
|
+
},
|
|
628
631
|
"arkRules": {
|
|
629
632
|
"type": "object",
|
|
630
633
|
"additionalProperties": {
|
|
@@ -773,6 +776,37 @@
|
|
|
773
776
|
},
|
|
774
777
|
"uniqueItems": true,
|
|
775
778
|
"minItems": 1
|
|
779
|
+
},
|
|
780
|
+
"sharedRoots": {
|
|
781
|
+
"type": "array",
|
|
782
|
+
"items": {
|
|
783
|
+
"type": "string",
|
|
784
|
+
"minLength": 1
|
|
785
|
+
},
|
|
786
|
+
"uniqueItems": true,
|
|
787
|
+
"minItems": 1
|
|
788
|
+
},
|
|
789
|
+
"allowedCrossSlice": {
|
|
790
|
+
"type": "array",
|
|
791
|
+
"minItems": 1,
|
|
792
|
+
"items": {
|
|
793
|
+
"type": "object",
|
|
794
|
+
"additionalProperties": false,
|
|
795
|
+
"required": [
|
|
796
|
+
"from",
|
|
797
|
+
"to"
|
|
798
|
+
],
|
|
799
|
+
"properties": {
|
|
800
|
+
"from": {
|
|
801
|
+
"type": "string",
|
|
802
|
+
"minLength": 1
|
|
803
|
+
},
|
|
804
|
+
"to": {
|
|
805
|
+
"type": "string",
|
|
806
|
+
"minLength": 1
|
|
807
|
+
}
|
|
808
|
+
}
|
|
809
|
+
}
|
|
776
810
|
}
|
|
777
811
|
}
|
|
778
812
|
},
|
|
@@ -800,6 +834,35 @@
|
|
|
800
834
|
}
|
|
801
835
|
}
|
|
802
836
|
},
|
|
837
|
+
"coverage": {
|
|
838
|
+
"type": "object",
|
|
839
|
+
"additionalProperties": false,
|
|
840
|
+
"description": "Invariant coverage scan controls. testGlobs replaces the built-in test-name heuristic; maxFiles raises or lowers the evidence file budget; coverageRoots declares where the project runs its tests, so a covering test found outside them is reported instead of silently certifying an invariant.",
|
|
841
|
+
"properties": {
|
|
842
|
+
"testGlobs": {
|
|
843
|
+
"type": "array",
|
|
844
|
+
"items": {
|
|
845
|
+
"type": "string",
|
|
846
|
+
"minLength": 1
|
|
847
|
+
},
|
|
848
|
+
"uniqueItems": true,
|
|
849
|
+
"minItems": 1
|
|
850
|
+
},
|
|
851
|
+
"maxFiles": {
|
|
852
|
+
"type": "integer",
|
|
853
|
+
"minimum": 1
|
|
854
|
+
},
|
|
855
|
+
"coverageRoots": {
|
|
856
|
+
"type": "array",
|
|
857
|
+
"items": {
|
|
858
|
+
"type": "string",
|
|
859
|
+
"minLength": 1
|
|
860
|
+
},
|
|
861
|
+
"uniqueItems": true,
|
|
862
|
+
"minItems": 1
|
|
863
|
+
}
|
|
864
|
+
}
|
|
865
|
+
},
|
|
803
866
|
"arkRun": {
|
|
804
867
|
"type": "object",
|
|
805
868
|
"additionalProperties": false,
|
package/server.json
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json",
|
|
3
3
|
"name": "io.github.pedroknigge/arkgate",
|
|
4
|
-
"description": "When the agent writes a bad import, the write doesn
|
|
4
|
+
"description": "When the agent writes a bad import, the write doesn\u2019t land. The same check fails the pull request.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"url": "https://github.com/pedroknigge/arkgate",
|
|
7
7
|
"source": "github"
|
|
8
8
|
},
|
|
9
|
-
"version": "4.8.
|
|
9
|
+
"version": "4.8.5",
|
|
10
10
|
"packages": [
|
|
11
11
|
{
|
|
12
12
|
"registryType": "npm",
|
|
13
13
|
"identifier": "arkgate",
|
|
14
|
-
"version": "4.8.
|
|
14
|
+
"version": "4.8.5",
|
|
15
15
|
"runtimeHint": "npx",
|
|
16
16
|
"transport": {
|
|
17
17
|
"type": "stdio"
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
This directory is the **Agent Skills–compatible** packaging of the same **13**
|
|
8
8
|
`/ark-*` skills shipped as flat templates for Ark install. **No new skill names.**
|
|
9
9
|
|
|
10
|
-
Package version when last generated context: **arkgate@4.8.
|
|
10
|
+
Package version when last generated context: **arkgate@4.8.5**
|
|
11
11
|
Schema: agent-skills package contract `1.0`
|
|
12
12
|
|
|
13
13
|
## Skills (frozen catalog)
|
|
@@ -237,6 +237,11 @@ Ark protects the **boundary around** a framework, not its internals. Nest/DI pub
|
|
|
237
237
|
to a compact starter. Do not promote to enforced as the session-0 default.
|
|
238
238
|
2. **Check + diagnose** — `summary.concentrated` / dominant edge → fix contract first, don’t freeze.
|
|
239
239
|
Cross-slice / cross-context `peerIsolation` hits are judgment: extract shared or events.
|
|
240
|
+
The denial names its reason. `unclassifiable path` in bulk means shared code lives outside
|
|
241
|
+
the slice folders — declare those roots (`sharedRoots`) on the rule instead of treating
|
|
242
|
+
thousands of shared files as violations; `cross-slice edge a → b` is the real one, and a
|
|
243
|
+
deliberate directed edge goes in `allowedCrossSlice`. Promoting the shared slice to its own
|
|
244
|
+
layer is still the preferred fix.
|
|
240
245
|
If one edge dominates residual debt: **STOP — do not continue this skill as complete.** **STOP — concentrated edge:** rewrite `ark.config.json` **in this turn** with source evidence (do not freeze a wrong config or grind N freezes).
|
|
241
246
|
Empty Domain/Persistence + I/O under Application → false-green.
|
|
242
247
|
**STOP — do not continue this skill as complete.** **STOP — false-green:** fix the config **in this turn** before claiming ENFORCE. Do not claim goal.met / ENFORCE from type-only cleanup while doctor reports `contract-false-green-io-under-application`.
|
|
@@ -92,6 +92,7 @@ ArkGate has **always-on Layers** plus opt-in extras. The user chooses extras; yo
|
|
|
92
92
|
3. Absence of `arkRules` is **valid** — do not force ArkRules unless the user wants them or residual inventory clearly wants a pilot.
|
|
93
93
|
4. Editing `arkrules/*` or promoting modes is **`/ark-adopt`** / leftover **`/ark-contract`**; fixing code under a structure sensor is leftover **`/ark-fix`** / **`/ark-loop`** or **`/ark-autopilot`** (judgment, never invent mechanical-safe).
|
|
94
94
|
5. CLI helpers: `ark-check --rules-inventory --json`, doctor JSON `rulesUnderContract`, sensors emit `ARKRULE_*` / `INVARIANT_UNCOVERED` with `evidence.arkruleId`.
|
|
95
|
+
5b. Invariant coverage is a **text match, never an execution**: a file walk plus the invariant id in a `describe`/`it` title. Never report `INVARIANT_UNCOVERED: 0` as “the tests pass” or “the tests run” — ArkGate does not run tests and reads no runner config. When the project declares `coverage.coverageRoots`, a covering test found outside them raises the advisory `INVARIANT_COVERAGE_OUTSIDE_ROOTS`; without that declaration ArkGate makes no claim about where tests run.
|
|
95
96
|
6. Extras silent when off. Doctor `arkRun` is `notAScore`. Do not force extras. Do not invent `/ark-run` or `/ark-order`.
|
|
96
97
|
|
|
97
98
|
|
|
@@ -86,7 +86,7 @@ Never invent gate verdicts from these suggestions. Missing residual is honest em
|
|
|
86
86
|
| Skills customized after install | Preserved by default. Preview `skillDrift` shows counts. **`--refresh-skills`** rewrites customized *skills* only with consent. |
|
|
87
87
|
| Conflicted managed assets | Still need `--accept-conflicts`. Never silent overwrite of true edits. |
|
|
88
88
|
| Multiple checkouts / monorepo packages | One `expectedRoot` per project; upgrade **each** pin; restart MCP after bump; prefer project-local CLI until identity matched **and** process version aligns. |
|
|
89
|
-
| Stale `~/.claude/skills
|
|
89
|
+
| Stale `~/.claude/skills`, `~/.grok/skills`, or `~/.gemini/config/skills` | Shared homes should be the newest ArkGate on the machine (additive; never downgrade). Refresh: `--install-agent-gates --skills-only --agent-homes --force`. Project skills may lag with the pin. Antigravity’s global catalog still refreshes when the project `.agents/skills` already exists. |
|
|
90
90
|
| Active host not in `--tools` / manifest | Preview `hostSelection` notes it and suggests `--tools` expansion. |
|
|
91
91
|
|
|
92
92
|
**Post-apply:** read `postUpgradeChecks` (advisory). Confirm pin↔CLI, run doctor (compass + deepModuleCoach),
|
|
@@ -237,6 +237,11 @@ Ark protects the **boundary around** a framework, not its internals. Nest/DI pub
|
|
|
237
237
|
to a compact starter. Do not promote to enforced as the session-0 default.
|
|
238
238
|
2. **Check + diagnose** — `summary.concentrated` / dominant edge → fix contract first, don’t freeze.
|
|
239
239
|
Cross-slice / cross-context `peerIsolation` hits are judgment: extract shared or events.
|
|
240
|
+
The denial names its reason. `unclassifiable path` in bulk means shared code lives outside
|
|
241
|
+
the slice folders — declare those roots (`sharedRoots`) on the rule instead of treating
|
|
242
|
+
thousands of shared files as violations; `cross-slice edge a → b` is the real one, and a
|
|
243
|
+
deliberate directed edge goes in `allowedCrossSlice`. Promoting the shared slice to its own
|
|
244
|
+
layer is still the preferred fix.
|
|
240
245
|
If one edge dominates residual debt: **STOP — do not continue this skill as complete.** **STOP — concentrated edge:** rewrite `ark.config.json` **in this turn** with source evidence (do not freeze a wrong config or grind N freezes).
|
|
241
246
|
Empty Domain/Persistence + I/O under Application → false-green.
|
|
242
247
|
**STOP — do not continue this skill as complete.** **STOP — false-green:** fix the config **in this turn** before claiming ENFORCE. Do not claim goal.met / ENFORCE from type-only cleanup while doctor reports `contract-false-green-io-under-application`.
|
|
@@ -92,6 +92,7 @@ ArkGate has **always-on Layers** plus opt-in extras. The user chooses extras; yo
|
|
|
92
92
|
3. Absence of `arkRules` is **valid** — do not force ArkRules unless the user wants them or residual inventory clearly wants a pilot.
|
|
93
93
|
4. Editing `arkrules/*` or promoting modes is **`/ark-adopt`** / leftover **`/ark-contract`**; fixing code under a structure sensor is leftover **`/ark-fix`** / **`/ark-loop`** or **`/ark-autopilot`** (judgment, never invent mechanical-safe).
|
|
94
94
|
5. CLI helpers: `ark-check --rules-inventory --json`, doctor JSON `rulesUnderContract`, sensors emit `ARKRULE_*` / `INVARIANT_UNCOVERED` with `evidence.arkruleId`.
|
|
95
|
+
5b. Invariant coverage is a **text match, never an execution**: a file walk plus the invariant id in a `describe`/`it` title. Never report `INVARIANT_UNCOVERED: 0` as “the tests pass” or “the tests run” — ArkGate does not run tests and reads no runner config. When the project declares `coverage.coverageRoots`, a covering test found outside them raises the advisory `INVARIANT_COVERAGE_OUTSIDE_ROOTS`; without that declaration ArkGate makes no claim about where tests run.
|
|
95
96
|
6. Extras silent when off. Doctor `arkRun` is `notAScore`. Do not force extras. Do not invent `/ark-run` or `/ark-order`.
|
|
96
97
|
|
|
97
98
|
|
|
@@ -86,7 +86,7 @@ Never invent gate verdicts from these suggestions. Missing residual is honest em
|
|
|
86
86
|
| Skills customized after install | Preserved by default. Preview `skillDrift` shows counts. **`--refresh-skills`** rewrites customized *skills* only with consent. |
|
|
87
87
|
| Conflicted managed assets | Still need `--accept-conflicts`. Never silent overwrite of true edits. |
|
|
88
88
|
| Multiple checkouts / monorepo packages | One `expectedRoot` per project; upgrade **each** pin; restart MCP after bump; prefer project-local CLI until identity matched **and** process version aligns. |
|
|
89
|
-
| Stale `~/.claude/skills
|
|
89
|
+
| Stale `~/.claude/skills`, `~/.grok/skills`, or `~/.gemini/config/skills` | Shared homes should be the newest ArkGate on the machine (additive; never downgrade). Refresh: `--install-agent-gates --skills-only --agent-homes --force`. Project skills may lag with the pin. Antigravity’s global catalog still refreshes when the project `.agents/skills` already exists. |
|
|
90
90
|
| Active host not in `--tools` / manifest | Preview `hostSelection` notes it and suggests `--tools` expansion. |
|
|
91
91
|
|
|
92
92
|
**Post-apply:** read `postUpgradeChecks` (advisory). Confirm pin↔CLI, run doctor (compass + deepModuleCoach),
|