arkgate 4.8.18 → 4.8.20
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 +153 -0
- package/README.md +5 -6
- package/bin/ark-check-runtime.mjs +23 -0
- package/bin/ark-layer-match.mjs +141 -9
- package/bin/ark-mcp-runtime.mjs +3 -3
- package/bin/ark-shared.mjs +187 -16
- package/bin/ark.mjs +23 -19
- package/bin/lib/analysis-engine.mjs +6 -6
- package/bin/lib/ci-and-commands.mjs +2 -2
- package/bin/lib/config-contract.mjs +1 -0
- package/bin/lib/doctor-advisories.mjs +2 -1
- package/bin/lib/doctor-human.mjs +2 -0
- package/bin/lib/doctor-next-actions.mjs +35 -0
- package/bin/lib/doctor-plan.mjs +8 -8
- package/bin/lib/enforcement-honesty.mjs +30 -2
- package/bin/lib/gate-files.mjs +22 -1
- package/bin/lib/host-support-matrix.mjs +1 -1
- package/bin/lib/html-report-depth.mjs +6 -2
- package/bin/lib/install-migrate.mjs +3 -4
- package/bin/lib/invariant-coverage.mjs +44 -13
- package/bin/lib/managed-upgrade.mjs +30 -5
- package/bin/lib/mcp-adoption.mjs +4 -1
- package/bin/lib/package-manager.mjs +5 -2
- package/bin/lib/physical-cohesion.mjs +68 -51
- package/bin/lib/presets.mjs +14 -14
- package/bin/lib/reshape-decisions.mjs +2 -2
- package/bin/lib/rules-under-contract.mjs +40 -4
- package/bin/lib/scan-files.mjs +3 -26
- package/bin/lib/skill-install.mjs +3 -2
- package/bin/lib/start-install-recovery.mjs +156 -0
- package/bin/lib/start-preview.mjs +17 -16
- package/bin/lib/write-path-detect.mjs +3 -2
- package/dist/{configTypes-VD0qcubY.d.ts → configTypes-VVknqcTJ.d.ts} +2 -0
- package/dist/{diagnosticCatalog-BNxKdcN4.d.ts → diagnosticCatalog-BPYWNyJR.d.ts} +9 -3
- package/dist/eslint/index.cjs +4 -4
- package/dist/eslint/index.d.ts +12 -4
- package/dist/eslint/index.js +4 -4
- package/dist/index.cjs +18 -18
- package/dist/index.d.ts +8 -4
- package/dist/index.js +20 -20
- package/dist/nestjs/index.cjs +5 -5
- package/dist/nestjs/index.d.ts +3 -3
- package/dist/nestjs/index.js +5 -5
- package/dist/runtime/index.cjs +10 -10
- package/dist/runtime/index.d.ts +6 -6
- package/dist/runtime/index.js +10 -10
- package/dist/{types-BSzRy2X1.d.ts → types-Cs-46DRu.d.ts} +1 -1
- package/dist/{types-D5GT5ZT8.d.ts → types-p_fCp6P-.d.ts} +1 -1
- package/docs/README.md +1 -1
- package/docs/agent-guide.md +3 -1
- package/docs/ai-gates.md +7 -3
- package/docs/configuration.md +20 -5
- package/docs/develop.md +1 -2
- package/docs/package-surface.md +2 -2
- package/docs/use.md +1 -2
- package/package.json +3 -3
- package/schemas/ark.config.schema.json +6 -0
- package/server.json +2 -2
- package/templates/agent-skills/README.md +1 -1
- package/templates/agent-skills/ark-adopt/SKILL.md +15 -1
- package/templates/agent-skills/ark-autopilot/SKILL.md +2 -0
- package/templates/agent-skills/ark-coverage/SKILL.md +1 -1
- package/templates/agent-skills/ark-explain/SKILL.md +1 -1
- package/templates/agent-skills/ark-explore/SKILL.md +13 -1
- package/templates/agent-skills/ark-place/SKILL.md +2 -0
- package/templates/agent-skills/ark-upgrade/SKILL.md +5 -3
- package/templates/hooks/opencode-ark-write-gate.mjs +2 -2
- package/templates/skills/ark-adopt.md +15 -1
- package/templates/skills/ark-autopilot.md +2 -0
- package/templates/skills/ark-coverage.md +1 -1
- package/templates/skills/ark-explain.md +1 -1
- package/templates/skills/ark-explore.md +13 -1
- package/templates/skills/ark-place.md +2 -0
- package/templates/skills/ark-upgrade.md +5 -3
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { i as Policy, P as PolicyViolation, I as IntentName, j as IntentCreator, k as IntentRelationship, b as ArchitectureProfile, D as DomainEvent, E as EventMetadata, h as PolicyEnforcementMode, A as ArchitectureLayer, c as ArchitectureRule, d as ArkCheckConfig } from './types-
|
|
1
|
+
import { i as Policy, P as PolicyViolation, I as IntentName, j as IntentCreator, k as IntentRelationship, b as ArchitectureProfile, D as DomainEvent, E as EventMetadata, h as PolicyEnforcementMode, A as ArchitectureLayer, c as ArchitectureRule, d as ArkCheckConfig } from './types-p_fCp6P-.js';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* PolicyEngine
|
package/docs/README.md
CHANGED
|
@@ -73,7 +73,7 @@ These are **not** the day-to-day product path. They stay in the repo for evidenc
|
|
|
73
73
|
| Field adoption kit (scaffolding, not closed) | [field/](field/) |
|
|
74
74
|
| Runtime hardening (experimental) | [production-hardening.md](production-hardening.md) |
|
|
75
75
|
|
|
76
|
-
Current published: [CHANGELOG](../CHANGELOG.md) (`arkgate@4.8.
|
|
76
|
+
Current published: [CHANGELOG](../CHANGELOG.md) (`arkgate@4.8.20` on npm `latest`; does not close `K01`).
|
|
77
77
|
Prior published: [releases/4.8.11.md](releases/4.8.11.md) (`arkgate@4.8.11`).
|
|
78
78
|
Prior published: [releases/4.8.10.md](releases/4.8.10.md) (`arkgate@4.8.10`).
|
|
79
79
|
Prior published: [releases/4.8.9.md](releases/4.8.9.md) (`arkgate@4.8.9`).
|
package/docs/agent-guide.md
CHANGED
|
@@ -67,7 +67,9 @@ and trusted (Cursor: `Write` / `StrReplace` via `.cursor/hooks.json`). Codex CLI
|
|
|
67
67
|
ChatGPT Desktop/App Server can hard-block a complete `apply_patch` through a trusted
|
|
68
68
|
`.codex/hooks.json`; installed files stay unverified until a fresh covered invocation. Hosted
|
|
69
69
|
tools, specialized opt-out paths, shell/direct writes, incomplete patch reconstruction, and
|
|
70
|
-
OpenCode remain CI-backed or advisory.
|
|
70
|
+
OpenCode remain CI-backed or advisory. Write-gate order: host pre-hook first when
|
|
71
|
+
the host has one; MCP `ark_prepare_write` is fallback if that hook is missing or
|
|
72
|
+
fail-open. For every host, the repository-wide hard boundary is a
|
|
71
73
|
**required GitHub status context** that runs `arkgate-check --strict-merge` (alias
|
|
72
74
|
`ark-check --strict-merge`) — the CLI name is not the status context name. Soft-write or
|
|
73
75
|
unverified-hook evidence alone does not mean the project is unfinished. See
|
package/docs/ai-gates.md
CHANGED
|
@@ -44,6 +44,10 @@ advisory-local / hard-CI split is a deliberate trade-off: local surfaces optimiz
|
|
|
44
44
|
while a required merge status is the one boundary a repository can make every write path share.
|
|
45
45
|
Prefer fail-closed honesty over fake hard guarantees on advisory hosts.
|
|
46
46
|
|
|
47
|
+
**Write-gate order:** install and trust the host pre-hook first (hard when the host
|
|
48
|
+
can do it). MCP `ark_prepare_write` is fallback when that path is missing or
|
|
49
|
+
fail-open. OpenCode is MCP-only. Required CI is always the merge line.
|
|
50
|
+
|
|
47
51
|
Everything below uses the same `ark.config.json` as `arkgate-check` / `ark-check` (CI) — one
|
|
48
52
|
rules file shared by every surface. From **4.0**, optional **ArkRules** (`arkRules` map +
|
|
49
53
|
`arkrules/*.json`) ride the **same** write path, doctor, and CI adapter; absence of ArkRules
|
|
@@ -350,9 +354,9 @@ alwaysApply: true
|
|
|
350
354
|
---
|
|
351
355
|
|
|
352
356
|
Before trusting Ark MCP evidence, call `ark_identity` with `project.expectedRoot`
|
|
353
|
-
set to the exact project root. Then call `ark_manifest`.
|
|
354
|
-
|
|
355
|
-
|
|
357
|
+
set to the exact project root. Then call `ark_manifest`. The write gate is the project `.cursor/hooks.json` pre-hook when installed,
|
|
358
|
+
trusted, and `failClosed`. Call MCP `ark_prepare_write` / `validate_code` only
|
|
359
|
+
as fallback if that hook is missing or fail-open.
|
|
356
360
|
```
|
|
357
361
|
|
|
358
362
|
Your repository backstop remains CI: `ark-check` fails its check on anything that slips
|
package/docs/configuration.md
CHANGED
|
@@ -114,7 +114,10 @@ Top-level fields:
|
|
|
114
114
|
Nothing is dropped in silence: files past the budget, files over the 256KB per-file cap,
|
|
115
115
|
unreadable files or directories (permissions, broken symlinks), directories past the walk depth
|
|
116
116
|
limit (8), symlinks whose target resolves outside the project root, and tests naming no
|
|
117
|
-
catalogued invariant are each counted and named in the diagnostic.
|
|
117
|
+
catalogued invariant are each counted and named in the diagnostic. Those same counts are printed
|
|
118
|
+
on a green doctor ArkRules line and on `--rules-inventory`, not only inside a failing
|
|
119
|
+
`INVARIANT_UNCOVERED` sentence. The walk-depth and per-file byte caps stay internal — they are
|
|
120
|
+
not config keys. A symlinked test is read only
|
|
118
121
|
when its target is inside the root: a file that is not in this repo never proves an invariant
|
|
119
122
|
covered.
|
|
120
123
|
|
|
@@ -248,11 +251,20 @@ process module-capability family must be denied.
|
|
|
248
251
|
Rule fields:
|
|
249
252
|
|
|
250
253
|
- `from`, `to`, `allowed`, `message`, `peerIsolation`, `sliceFolders`, `sharedRoots`,
|
|
251
|
-
`allowedCrossSlice`
|
|
254
|
+
`allowedCrossSlice`, `sharedImportsSlice`
|
|
252
255
|
- `peerIsolation: true` + `allowed: false`: deny only when slice ids differ; same-slice allows
|
|
253
256
|
when both paths classify. Applies to **any** declared `from`→`to` pair, not only self-edges.
|
|
254
257
|
Missing paths, empty slice folders, or unclassifiable slices **fail closed** (deny — cannot
|
|
255
258
|
prove same-slice).
|
|
259
|
+
- `sliceFolders` is a bare name or a starred prefix. A bare name (`features`) stays an
|
|
260
|
+
unanchored one-segment match: `src/lib/features/projects/rfi/x.ts` with `["features"]` is
|
|
261
|
+
`features/projects`. The next segment is never a filename, so
|
|
262
|
+
`src/components/features/projects/rfi-status-pill.tsx` with `["projects"]` is not a slice.
|
|
263
|
+
A starred prefix (`lib/features/*/*`) is anchored like `sharedRoots` — it starts the
|
|
264
|
+
repo-relative path, or sits one segment in after `src/` or `app/`. Each `*` binds one
|
|
265
|
+
directory and never the filename. The slice id is those directories
|
|
266
|
+
(`lib/features/projects/rfi`). A file directly under `lib/features/projects/` stays
|
|
267
|
+
`lib/features/projects`. A path off that prefix (`src/app/api/projects/...`) is not that slice.
|
|
256
268
|
|
|
257
269
|
#### Cross-layer slice walls (already in the engine)
|
|
258
270
|
|
|
@@ -306,8 +318,11 @@ is evidence:
|
|
|
306
318
|
tree you never declared. Write a deeper or monorepo root out (`packages/web/src/ui`) or glob it
|
|
307
319
|
(`packages/*/src/ui`). Matching is case-insensitive; a bare `*` or `**` is refused, because one
|
|
308
320
|
character must not disable fail-closed. A path that still resolves to a slice keeps its slice —
|
|
309
|
-
`features/auth/ui/form.tsx` stays `features/auth
|
|
310
|
-
|
|
321
|
+
`features/auth/ui/form.tsx` stays `features/auth`. Shared roots are a sink: a slice may import
|
|
322
|
+
a shared root, and a shared root may import a slice unless the rule sets `sharedImportsSlice`
|
|
323
|
+
to `"deny"`. That hop is direct only — `allowedCrossSlice` does not excuse it. Doctor lists
|
|
324
|
+
shared-root → slice edges even when the flag is off, so a green run still says the wall is
|
|
325
|
+
direct-only.
|
|
311
326
|
- `allowedCrossSlice` entries match a full slice id (`features/catalog`) or a bare slice name
|
|
312
327
|
(`catalog`), and only in the direction written. The reverse edge still denies. A bare name
|
|
313
328
|
matches that name under **any** slice folder, so in a repo with several slice parents
|
|
@@ -398,7 +413,7 @@ Each `arkrules/<Layer>.json` may declare:
|
|
|
398
413
|
| Section | Purpose | Modes | What it really enforces |
|
|
399
414
|
|---------|---------|--------|-------------------------|
|
|
400
415
|
| `structure[]` | Closed sensor ids (e.g. `orchestration-only`, `thin-adapter`, `writes-via-aggregate`, `aggregate-private-state`, `always-valid-factory`, `domain-event-on-mutation`, `no-anemic-model`) | `advisory` (default) or `enforced` | **Heuristics of module shape** — not proof that logic was extracted to Domain. `writes-via-aggregate` is driver-import + write-token in the declaring layer (ADR 0032). Tier-2 sensors (`no-anemic-model`) stay advisory-only (cannot promote to enforced). |
|
|
401
|
-
| `invariants[]` | Stable ids + description + `coverage` (`test` / `symbol`) + optional `appliesTo` globs | `advisory` or `enforced` | **Named policy + evidence
|
|
416
|
+
| `invariants[]` | Stable ids + description + `coverage` (`test` / `symbol`) + optional `appliesTo` globs | `advisory` or `enforced` | **Named policy + evidence.** `coverage.symbol` means a declaration of that identifier in a non-test file (witness path `symbolEvidenceFile`); imports and test calls do not count. A test title may also name the invariant id. Does **not** execute tests or business logic and does **not** replace behavior/property tests. Doctor and `--rules-inventory` print the witness path and discard counts on green runs too. A declaration match is not “the tests pass.” |
|
|
402
417
|
|
|
403
418
|
**Reporting:** diagnostics carry `evidence.arkruleId` + `evidence.arkruleSource`. Label residual
|
|
404
419
|
**`[Layer]`** vs **`[ArkRules]`** in agent output. Doctor / HTML: `rulesUnderContract` (catalog +
|
package/docs/develop.md
CHANGED
|
@@ -35,12 +35,11 @@ ArkGate solves this by shifting the check to the exact moment of writing. By int
|
|
|
35
35
|
## Default integration
|
|
36
36
|
|
|
37
37
|
```bash
|
|
38
|
-
npm install -D arkgate typescript
|
|
39
38
|
npx arkgate start --apply
|
|
40
39
|
npx --package=arkgate arkgate-check --doctor
|
|
41
40
|
```
|
|
42
41
|
|
|
43
|
-
`arkgate-check` is a command in the `arkgate` package. After a local install, `npx arkgate-check --doctor` also works.
|
|
42
|
+
`start --apply` pins arkgate with this repo's package manager (`pnpm add -w` / `yarn add -W` at a workspace root). Do not run `npm install -D arkgate` at a `workspace:*` root — npm rejects that protocol. `arkgate-check` is a command in the `arkgate` package. After a local install, `npx arkgate-check --doctor` also works. If pnpm Age (`minimumReleaseAge`) blocks a brand-new release, keep using `npx --package=arkgate@<version>` — do not re-run the same add. Put arkgate on `minimumReleaseAgeExclude` in pnpm config or `pnpm-workspace.yaml` (a CLI flag is not enough), or wait until the package is mature.
|
|
44
43
|
|
|
45
44
|
Make the import-rules check a **required** merge **status context** (GitHub/GitLab/etc.). The CLI
|
|
46
45
|
command is `arkgate-check --strict-merge` / `ark-check --strict-merge` — the hard boundary is
|
package/docs/package-surface.md
CHANGED
|
@@ -59,7 +59,7 @@ hardening guide remains repository-hosted rather than duplicated in the gate tar
|
|
|
59
59
|
| **Effect capabilities** | Public root API: `analyzeProject(...).ir.capabilityUses`; the CLI/hook adapters add symbol-aware ambient evidence internally | Additive within IR `1.0`. Seven **closed** ids: `network`, `filesystem`, `clock`, `randomness`, `environment`, `process`, `persistence` (ADR 0009). `collectCapabilityUses` and the Domain vocabulary are internal implementation exports, **not** exports from `arkgate`; the related public low-level helper is `collectForbiddenCapabilityUses`. Direct evidence only — transitive inference never detects. The symbol-aware adapter path covers ambient globals (shadowing/type-only/globalThis-alias precision from the S05/C04 machinery) plus imports; the compiler-free IR engine carries **import-based** uses only (exact module or subpath match, never substring; textual `import type`/`export type` erasure and all-type named lists (`import { type A }`) are type-only there; mixed `{ type A, B }` stays a value import; template-literal bodies are skipped entirely (specifiers inside `${…}` are the symbol path's job); package `require(…)` counts as capability evidence only, while relative `require(…)` also emits a pure-path graph edge). **U04 walls are opt-in:** per-layer `capabilities: { deny: [...] }` or the dual-depth sugar `pure: true` (denies all seven); absence changes no verdict. `CAPABILITY_VIOLATION` is judgment-class (never mechanical-safe) with a port-injection `nextAction`; D7 dedup — evidence already owned by the layer's `forbiddenGlobals` reports only `FORBIDDEN_GLOBAL`. Y08 adds one deliberately narrow import dual: `forbiddenGlobals: ["process"]` owns exact value imports of `process` and `node:process`, but not subpaths or `child_process`; statement-level `import type` / `export type` remains erased on every path (pure-IR residual envelope: mixed `{ type A, B }`, default+named type lists, and comment-interrupted forms stay value imports; symbol path owns full precision). Atomic preflight blocks denied capabilities and that exact dual across a complete multi-file candidate (import-based on the pure path; other ambient evidence adds on the symbol-aware CLI/hook path). T01 policy-delta classifies the surface on **coverage atoms** (`ambient:<entry>` prefix-expanded, narrow `import-exact:<specifier>` duals, and `import:<capability>` for a complete wall): any lost atom is weakening (`fetch`→`XMLHttpRequest`, `Date`→`Date.now`, wall→fg all weaken; finding path `$.layers[name].capabilities`); fg → equivalent-or-stronger wall never needs an acknowledgment; unlowerable custom globals keep raw key comparison. |
|
|
60
60
|
| **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. |
|
|
61
61
|
| **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. |
|
|
62
|
-
| **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[]`; 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. |
|
|
62
|
+
| **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. |
|
|
63
63
|
| **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`). |
|
|
64
64
|
| **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. |
|
|
65
65
|
| **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`. |
|
|
@@ -74,7 +74,7 @@ hardening guide remains repository-hosted rather than duplicated in the gate tar
|
|
|
74
74
|
| **`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`. |
|
|
75
75
|
| **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. |
|
|
76
76
|
| **Managed upgrade self-service honesty (4.5 / DF05)** | `ark upgrade [--json]` → `selfService` (+ human “Self-service honesty” lines) | Additive, advisory. Answers without a maintainer: write-path activation labels per selected host (`hard`\|`advisory`\|`unavailable`) and customized content-identity preserve (`customizedPaths` / `customizedContentPreserved`). Soft hosts never hard; upgrade never invents `hardWriteActive` from disk alone. Always `notAScore: true`. Not a gate input; not part of `planDigest`. |
|
|
77
|
-
| **Product honesty readiness split (4.1.1)** | doctor JSON `productHonesty` | Additive. `unfinished` / `headline` / `primaryNextAction` / `reasonIds` remain; EH adds `contractReadiness` (`ready`\|`partial`\|`not-ready`), `localWriteBoundary` (`advisory`\|`hard`\|`unverified`\|`unknown`), `architectureReasonIds`, `environmentResidualIds` / `environmentResiduals`. Soft-write hosts stay in evidence without alone forcing global **Not finished**. `notAScore: true` always. |
|
|
77
|
+
| **Product honesty readiness split (4.1.1)** | doctor JSON `productHonesty` | Additive. `unfinished` / `headline` / `primaryNextAction` / `reasonIds` remain; EH adds `contractReadiness` (`ready`\|`partial`\|`not-ready`), `localWriteBoundary` (`advisory`\|`hard`\|`unverified`\|`unknown`), `architectureReasonIds`, `environmentResidualIds` / `environmentResiduals`. Soft-write hosts stay in evidence without alone forcing global **Not finished**. `notAScore: true` always. A physical-cohesion residual that reshape decisions have not suppressed adds `physical-cohesion-residual`, which clears `elegant` and `finished` and sets the headline to residual remaining; the gate verdict, `designFitness`, `designWeak`, and `patternBets` stay unchanged, and `pilotLoop` stays inactive. |
|
|
78
78
|
| **Policy transition analysis (3.1.0)** | `analyzePolicyDelta(...)`; MCP `ark_policy_delta`; CLI `--policy-base` / `--policy-base-ref` / `--policy-ack`; check JSON `policyDelta` | Additive schema `1.0`. Classifications and finding ids are deterministic. Weakening/judgment requires an acknowledgement bound to both policy hashes and the exact blocking finding set. A new layer or new allow edge is `judgment-required`. On the CLI/MCP policy-ack plane, `adrPath` must name a short note under `docs/adr/` or `docs/decisions/` (`policyDelta.adrNote`). Domain hash match stays I/O-free. |
|
|
79
79
|
| **Team parliament (law vs feature)** | Optional `stewards` on `ark.config.json` (GitHub handle or email); CLI `--local` / `--changed` / `--against` / `--base` / `--contract-diff` / `--contract-session` / `--persona` / `--author`; check JSON `teamParliament`; `ark status --vs`; write-gate mixed-batch deny | Additive. Law files must not mix with product source. Loosen / baseline-grow are steward-only when `stewards` is set. `--against` ratchets vs the base-ref baseline. `--changed` scans touched sources plus their import closure. `--local` (or `ARK_CHECK_LOCAL=1`) is the same cheap path and stays per `--root`; it cannot combine with `--strict-merge`. Check JSON may add `local`, `scope: "changed"`, `analysisRoot`. `stewards`, `layers[].description`, `layers[].trustBoundary`, and `layers[].owners` are excluded from policy hash. `requireLayerOwners` stays in the hash. Identity is handle or email, not git `user.name`. No org plane. |
|
|
80
80
|
| **Atomic change preflight (3.1.0)** | `preflightChange(...)`; CLI `ark preflight --changes <file> --json`; MCP `ark_prepare_change` | Additive schema `1.0`. One complete governed production-source `{path,content}` / `{path,delete:true}` batch; read-only; returns operation, content/tree/policy/compiler fingerprints and stable graph findings. MCP availability alone is advisory. |
|
package/docs/use.md
CHANGED
|
@@ -24,14 +24,13 @@ For **anyone** shipping TypeScript with an AI coding agent.
|
|
|
24
24
|
## In one minute
|
|
25
25
|
|
|
26
26
|
```bash
|
|
27
|
-
npm install -D arkgate typescript
|
|
28
27
|
npx arkgate start # preview what will change
|
|
29
28
|
npx arkgate start --apply # install compact config + host router + CI; merge write-path gates into existing AGENTS.md / .mcp.json
|
|
30
29
|
npx --package=arkgate arkgate-check --doctor # status — one next step
|
|
31
30
|
npx --package=arkgate arkgate-check --doctor --all # full details
|
|
32
31
|
```
|
|
33
32
|
|
|
34
|
-
`arkgate-check` is a command in the `arkgate` package, not its own npm package.
|
|
33
|
+
`start --apply` pins arkgate with this repo's package manager (`pnpm add -w` / `yarn add -W` at a workspace root). Do not run `npm install -D arkgate` at a `workspace:*` root — npm rejects that protocol. After a local install, `npx arkgate-check --doctor` also works. `arkgate-check` is a command in the `arkgate` package, not its own npm package. If pnpm Age (`minimumReleaseAge`) blocks a brand-new release, keep using `npx --package=arkgate@<version>` — do not re-run the same add. Put arkgate on `minimumReleaseAgeExclude` in pnpm config or `pnpm-workspace.yaml` (a CLI flag is not enough), or wait until the package is mature.
|
|
35
34
|
|
|
36
35
|
`start --apply` refuses when projected governed coverage is below 50% or
|
|
37
36
|
shape confidence is weak (below 0.6 with coverage under 80%), or when the
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "arkgate",
|
|
3
|
-
"version": "4.8.
|
|
3
|
+
"version": "4.8.20",
|
|
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",
|
|
@@ -205,14 +205,14 @@
|
|
|
205
205
|
"@stryker-mutator/core": "^9.6.1",
|
|
206
206
|
"@stryker-mutator/vitest-runner": "^9.6.1",
|
|
207
207
|
"@types/node": "^20.14.0",
|
|
208
|
-
"@vitest/coverage-v8": "^
|
|
208
|
+
"@vitest/coverage-v8": "^4.1.11",
|
|
209
209
|
"esbuild": "0.28.1",
|
|
210
210
|
"fast-check": "^4.9.0",
|
|
211
211
|
"reflect-metadata": "^0.2.2",
|
|
212
212
|
"rxjs": "^7.8.2",
|
|
213
213
|
"tsup": "^8.1.0",
|
|
214
214
|
"typescript": "5.9.3",
|
|
215
|
-
"vitest": "^
|
|
215
|
+
"vitest": "^4.1.11"
|
|
216
216
|
},
|
|
217
217
|
"engines": {
|
|
218
218
|
"node": ">=18"
|
package/server.json
CHANGED
|
@@ -6,12 +6,12 @@
|
|
|
6
6
|
"url": "https://github.com/pedroknigge/arkgate",
|
|
7
7
|
"source": "github"
|
|
8
8
|
},
|
|
9
|
-
"version": "4.8.
|
|
9
|
+
"version": "4.8.20",
|
|
10
10
|
"packages": [
|
|
11
11
|
{
|
|
12
12
|
"registryType": "npm",
|
|
13
13
|
"identifier": "arkgate",
|
|
14
|
-
"version": "4.8.
|
|
14
|
+
"version": "4.8.20",
|
|
15
15
|
"runtimeHint": "npx",
|
|
16
16
|
"transport": {
|
|
17
17
|
"type": "stdio"
|
|
@@ -9,7 +9,7 @@ closed `/ark-*` catalog shipped as flat templates for Ark install.
|
|
|
9
9
|
**Closed catalog** (`ARK_SKILL_NAMES`) — first-class doors plus one-release
|
|
10
10
|
redirect stubs. Add a name only with a live ROADMAP item.
|
|
11
11
|
|
|
12
|
-
Package version when last generated context: **arkgate@4.8.
|
|
12
|
+
Package version when last generated context: **arkgate@4.8.20**
|
|
13
13
|
Schema: agent-skills package contract `1.0`
|
|
14
14
|
|
|
15
15
|
## Skills (closed catalog)
|
|
@@ -63,6 +63,11 @@ has honest houses. Skills never enforce — CLI / hooks / CI do.
|
|
|
63
63
|
`coverage.coverageRoots` to the folder the test runner uses. Fail-closed
|
|
64
64
|
until that path exists. `coverage.testGlobs` alone is not enough. Silent
|
|
65
65
|
when no invariant is enforced. No new skill.
|
|
66
|
+
- `coverage.symbol` means a declaration of that identifier in a non-test file.
|
|
67
|
+
Doctor and `--rules-inventory` print `symbolEvidenceFile` and the discard
|
|
68
|
+
counts even when uncovered is 0. Read that path. Imports and calls in tests
|
|
69
|
+
do not count. That declaration text is not “the tests pass” — ArkGate never
|
|
70
|
+
runs tests. No new config key.
|
|
66
71
|
- CLI-first: if `arkgate-check` already resolved the root, do not wait on MCP.
|
|
67
72
|
- Do not add `arkRun` or `arkOrder` unless the user wants that extra. When they do, write
|
|
68
73
|
**advisory** extra in this turn (`arkRun` schema `1.2+`; `arkOrder` schema `1.3+`).
|
|
@@ -353,8 +358,17 @@ Ark protects the **boundary around** a framework, not its internals. Nest/DI pub
|
|
|
353
358
|
The denial names its reason. `unclassifiable path` in bulk means shared code lives outside
|
|
354
359
|
the slice folders — declare those roots (`sharedRoots`) on the rule instead of treating
|
|
355
360
|
thousands of shared files as violations; `cross-slice edge a → b` is the real one, and a
|
|
356
|
-
deliberate directed edge goes in `allowedCrossSlice`.
|
|
361
|
+
deliberate directed edge goes in `allowedCrossSlice`. Shared roots are a sink: a slice may
|
|
362
|
+
import them, and they may import a slice. Doctor lists that shared-root → slice hop even
|
|
363
|
+
when the check stays green, so the wall is direct-only. `sharedImportsSlice: "deny"` closes
|
|
364
|
+
that one hop; it does not finish the wall. Promoting the shared slice to its own
|
|
357
365
|
layer is still the preferred fix.
|
|
366
|
+
`sliceFolders`: a bare name stays an unanchored one-segment match (`features` on
|
|
367
|
+
`src/lib/features/projects/rfi/x.ts` is `features/projects`); a filename is never the
|
|
368
|
+
child segment. A starred prefix (`lib/features/*/*`) is anchored like `sharedRoots`
|
|
369
|
+
(offset 0 or 1) and the slice id is the directories the stars bind
|
|
370
|
+
(`lib/features/projects/rfi`). Before trusting green, add one deliberate
|
|
371
|
+
feature-to-feature import and expect `cross-slice`.
|
|
358
372
|
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).
|
|
359
373
|
Empty Domain/Persistence + I/O under Application → false-green.
|
|
360
374
|
**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`.
|
|
@@ -110,6 +110,8 @@ decision-grade explore pass **and** without opening violating files.
|
|
|
110
110
|
layout — no pilot”; never reconstruct that dead card from `findings`. When the user accepts,
|
|
111
111
|
defers, or rejects a live card, persist its exact `decisionTarget` with a non-empty reason and
|
|
112
112
|
optional `reviewBy` in `.ark/reshape-decisions.json`; never infer a verdict from golden prose.
|
|
113
|
+
A cohesion residual starts one reshape judgment card, not the pattern-bet apply loop.
|
|
114
|
+
`pilotLoop` stays inactive (`not-design-weak`) unless the tree is design-weak.
|
|
113
115
|
|
|
114
116
|
|
|
115
117
|
|
|
@@ -96,7 +96,7 @@ ArkGate has **always-on Layers** plus opt-in extras. The user chooses extras; yo
|
|
|
96
96
|
3. Absence of `arkRules` is **valid** — do not force ArkRules unless the user wants them or residual inventory clearly wants a pilot.
|
|
97
97
|
4. Editing `arkrules/*` or promoting modes is **`/ark-adopt`**; fixing code under a structure sensor is **`/ark-autopilot`** (judgment, never invent mechanical-safe).
|
|
98
98
|
5. CLI helpers: `ark-check --rules-inventory --json`, doctor JSON `rulesUnderContract`, sensors emit `ARKRULE_*` / `INVARIANT_UNCOVERED` / `INVARIANT_CATALOG_EMPTY` with `evidence.arkruleId`.
|
|
99
|
-
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
|
|
99
|
+
5b. Invariant coverage is a **text match, never an execution**: a file walk plus the invariant id in a `describe`/`it` title. `coverage.symbol` means a declaration of that identifier in a non-test file (`symbolEvidenceFile`); imports and test calls do not count. Doctor and `--rules-inventory` print that witness path and the scan discard counts on a green run too. Never report `INVARIANT_UNCOVERED: 0`, a witness path, or a discard line as “the tests pass” or “the tests run” — ArkGate does not run tests and reads no runner config. Declaration text is not “the tests pass.” 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. Adopted + catalogued invariants without `coverage.testGlobs` or `coverage.coverageRoots` fails closed (`INVARIANT_TESTS_PATH_MISSING`) — write the path (`/ark-adopt`). When any invariant is enforced, missing `coverage.coverageRoots` fails closed (`INVARIANT_COVERAGE_ROOTS_MISSING`) — write the runner roots (`/ark-adopt`). `testGlobs` alone is not enough.
|
|
100
100
|
6. Extras silent when off. Doctor `arkRun` / `arkOrder` is `notAScore`. Do not force extras. Wire one kernel candidate via `/ark-runtime`; one order-plane candidate via `/ark-order`. Do not invent `/ark-run`.
|
|
101
101
|
|
|
102
102
|
|
|
@@ -188,7 +188,7 @@ else is judgment/deferred and must not be auto-applied.
|
|
|
188
188
|
**Type-edge honesty:** type-only denied edges are **placement debt** (`typeOnly` +
|
|
189
189
|
`failsStrict: false`) — prefer SharedTypes / owning layer; they do not fail merge like value
|
|
190
190
|
edges. Doctor `productHonesty` and `violations.typeEdgePolicy` must not be sold as “all done”
|
|
191
|
-
when residual signals remain.
|
|
191
|
+
when residual signals remain (`physical-cohesion-residual` included).
|
|
192
192
|
|
|
193
193
|
## Spoken / written explanation
|
|
194
194
|
|
|
@@ -53,6 +53,11 @@ one bounded decision needs 2–3 enforceable options.
|
|
|
53
53
|
`coverage.coverageRoots` on the folder the test runner uses. Fail-closed.
|
|
54
54
|
`testGlobs` alone is not enough. Silent when no invariant is enforced.
|
|
55
55
|
Handoff `/ark-adopt` to write the roots. No new skill.
|
|
56
|
+
- When an invariant declares `coverage.symbol`, read the witness path
|
|
57
|
+
(`symbolEvidenceFile`) and the discard line on doctor and `--rules-inventory`,
|
|
58
|
+
including when the run is green. A declaration in a non-test file is not
|
|
59
|
+
“the tests pass” — ArkGate never runs tests. Depth and byte caps stay
|
|
60
|
+
internal; the counts are what you read.
|
|
56
61
|
- A doctor green / Healthy line must name a file, config key, or test.
|
|
57
62
|
Uncited green is a lie — treat it as unfinished. No `/ark-cite`.
|
|
58
63
|
- Distinguish **missing** skills vs **stale** catalog. Installed ≠ stale.
|
|
@@ -345,12 +350,19 @@ targets as explicit adopter decisions, not residual to re-fight. Keep their mirr
|
|
|
345
350
|
map. When the user accepts, defers, or rejects a live `reshapePilot.nextPilot`, record the card's
|
|
346
351
|
exact `decisionTarget` with a reason (and optional `reviewBy`) in
|
|
347
352
|
`.ark/reshape-decisions.json`. Never infer or suppress from free-form golden-pattern prose.
|
|
353
|
+
A proposed `to` stays under the source anchor (`concept/`) and is kept only when that path
|
|
354
|
+
stays in the same layer and the same slice id. If no governed destination keeps both,
|
|
355
|
+
`nextPilot` is null — do not invent a folder outside the contract. Success is a smaller
|
|
356
|
+
cluster with the layer and slice id unchanged and the card still proposed; a green gate is
|
|
357
|
+
not success. A cohesion residual (`physical-cohesion-residual`) starts that one reshape
|
|
358
|
+
judgment card. It does not start the pattern-bet apply loop — `pilotLoop` stays inactive
|
|
359
|
+
while design-weak is false.
|
|
348
360
|
|
|
349
361
|
### C. Coupling & debt (measure, don’t guess)
|
|
350
362
|
Prefer evidence over aesthetics:
|
|
351
363
|
- **Fan-in / importers** of hot modules (who imports the suspect file?).
|
|
352
364
|
- **Export surface** (how many public symbols does the orchestration module re-export?).
|
|
353
|
-
- Import cycles, cross-feature leaks (`peerIsolation` candidates).
|
|
365
|
+
- Import cycles, cross-feature leaks (`peerIsolation` candidates). A shared root that imports a slice is a direct-only bridge; doctor lists it. `sharedImportsSlice: "deny"` closes that one hop. It does not finish the wall.
|
|
354
366
|
- Business rules in UI/hooks (`can*`, `calculate*`, policy constants) → Domain / intents.
|
|
355
367
|
- Dead or aspirational globs; layers with files but no rules; **starter/preset rules weaker than sibling archetypes** (soft green).
|
|
356
368
|
- LOC is a **hint** only — pair it with fan-in or export count before calling something a god module.
|
|
@@ -21,6 +21,8 @@ right house. Skills never enforce — CLI / hooks / CI do.
|
|
|
21
21
|
## Checklist
|
|
22
22
|
|
|
23
23
|
- `filePath` is known before the call. Description alone is not a path.
|
|
24
|
+
- Host write gate: prefer the installed pre-hook. MCP `ark_prepare_write` is
|
|
25
|
+
fallback when that hook is missing or fail-open. Skills never enforce.
|
|
24
26
|
- Golden pattern is load-bearing when present. Adopt generates it.
|
|
25
27
|
- An included file that matches no layer is not a pass. The write gate denies it
|
|
26
28
|
(`CONFIG_UNCLASSIFIED_FILES`). Place it in a layer folder, or send include/layer
|
|
@@ -259,9 +259,11 @@ ArkGate has **always-on Layers** plus opt-in extras. The user chooses extras; yo
|
|
|
259
259
|
If already current (`ALREADY_CURRENT`), retain the read-only preview and still read `whatsNew`
|
|
260
260
|
+ `skillDrift`.
|
|
261
261
|
|
|
262
|
-
For pnpm repositories with `minimumReleaseAge`,
|
|
263
|
-
|
|
264
|
-
|
|
262
|
+
For pnpm repositories with `minimumReleaseAge`, put the exception on
|
|
263
|
+
`minimumReleaseAgeExclude` in pnpm config or `pnpm-workspace.yaml` (a CLI
|
|
264
|
+
flag is not enough) when the new release is still cooling off, or keep using
|
|
265
|
+
`npx --package=arkgate@<pin>` until the package is mature. Prove
|
|
266
|
+
`pnpm install --frozen-lockfile` succeeds after a local add.
|
|
265
267
|
|
|
266
268
|
4. **Apply only the reviewed candidate.** When there are no blocked assets, run
|
|
267
269
|
the preview's **exact** `nextCommand` as emitted (JSON field / human “Apply the
|
|
@@ -8,8 +8,8 @@
|
|
|
8
8
|
*
|
|
9
9
|
* Honesty: OpenCode plugin hooks are **not** a complete hard write boundary
|
|
10
10
|
* (subagent and alternate tool paths may bypass). Never treat this plugin as
|
|
11
|
-
* Claude/Grok/Antigravity PreToolUse hard-write.
|
|
12
|
-
* + required merge status.
|
|
11
|
+
* Claude/Grok/Antigravity PreToolUse hard-write. This host has no hard pre-hook,
|
|
12
|
+
* so MCP `ark_prepare_write` is the local write fallback + required merge status.
|
|
13
13
|
*
|
|
14
14
|
* This file is a gallery template shipped with arkgate — not auto-installed.
|
|
15
15
|
*/
|
|
@@ -63,6 +63,11 @@ has honest houses. Skills never enforce — CLI / hooks / CI do.
|
|
|
63
63
|
`coverage.coverageRoots` to the folder the test runner uses. Fail-closed
|
|
64
64
|
until that path exists. `coverage.testGlobs` alone is not enough. Silent
|
|
65
65
|
when no invariant is enforced. No new skill.
|
|
66
|
+
- `coverage.symbol` means a declaration of that identifier in a non-test file.
|
|
67
|
+
Doctor and `--rules-inventory` print `symbolEvidenceFile` and the discard
|
|
68
|
+
counts even when uncovered is 0. Read that path. Imports and calls in tests
|
|
69
|
+
do not count. That declaration text is not “the tests pass” — ArkGate never
|
|
70
|
+
runs tests. No new config key.
|
|
66
71
|
- CLI-first: if `arkgate-check` already resolved the root, do not wait on MCP.
|
|
67
72
|
- Do not add `arkRun` or `arkOrder` unless the user wants that extra. When they do, write
|
|
68
73
|
**advisory** extra in this turn (`arkRun` schema `1.2+`; `arkOrder` schema `1.3+`).
|
|
@@ -353,8 +358,17 @@ Ark protects the **boundary around** a framework, not its internals. Nest/DI pub
|
|
|
353
358
|
The denial names its reason. `unclassifiable path` in bulk means shared code lives outside
|
|
354
359
|
the slice folders — declare those roots (`sharedRoots`) on the rule instead of treating
|
|
355
360
|
thousands of shared files as violations; `cross-slice edge a → b` is the real one, and a
|
|
356
|
-
deliberate directed edge goes in `allowedCrossSlice`.
|
|
361
|
+
deliberate directed edge goes in `allowedCrossSlice`. Shared roots are a sink: a slice may
|
|
362
|
+
import them, and they may import a slice. Doctor lists that shared-root → slice hop even
|
|
363
|
+
when the check stays green, so the wall is direct-only. `sharedImportsSlice: "deny"` closes
|
|
364
|
+
that one hop; it does not finish the wall. Promoting the shared slice to its own
|
|
357
365
|
layer is still the preferred fix.
|
|
366
|
+
`sliceFolders`: a bare name stays an unanchored one-segment match (`features` on
|
|
367
|
+
`src/lib/features/projects/rfi/x.ts` is `features/projects`); a filename is never the
|
|
368
|
+
child segment. A starred prefix (`lib/features/*/*`) is anchored like `sharedRoots`
|
|
369
|
+
(offset 0 or 1) and the slice id is the directories the stars bind
|
|
370
|
+
(`lib/features/projects/rfi`). Before trusting green, add one deliberate
|
|
371
|
+
feature-to-feature import and expect `cross-slice`.
|
|
358
372
|
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).
|
|
359
373
|
Empty Domain/Persistence + I/O under Application → false-green.
|
|
360
374
|
**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`.
|
|
@@ -110,6 +110,8 @@ decision-grade explore pass **and** without opening violating files.
|
|
|
110
110
|
layout — no pilot”; never reconstruct that dead card from `findings`. When the user accepts,
|
|
111
111
|
defers, or rejects a live card, persist its exact `decisionTarget` with a non-empty reason and
|
|
112
112
|
optional `reviewBy` in `.ark/reshape-decisions.json`; never infer a verdict from golden prose.
|
|
113
|
+
A cohesion residual starts one reshape judgment card, not the pattern-bet apply loop.
|
|
114
|
+
`pilotLoop` stays inactive (`not-design-weak`) unless the tree is design-weak.
|
|
113
115
|
|
|
114
116
|
|
|
115
117
|
|
|
@@ -96,7 +96,7 @@ ArkGate has **always-on Layers** plus opt-in extras. The user chooses extras; yo
|
|
|
96
96
|
3. Absence of `arkRules` is **valid** — do not force ArkRules unless the user wants them or residual inventory clearly wants a pilot.
|
|
97
97
|
4. Editing `arkrules/*` or promoting modes is **`/ark-adopt`**; fixing code under a structure sensor is **`/ark-autopilot`** (judgment, never invent mechanical-safe).
|
|
98
98
|
5. CLI helpers: `ark-check --rules-inventory --json`, doctor JSON `rulesUnderContract`, sensors emit `ARKRULE_*` / `INVARIANT_UNCOVERED` / `INVARIANT_CATALOG_EMPTY` with `evidence.arkruleId`.
|
|
99
|
-
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
|
|
99
|
+
5b. Invariant coverage is a **text match, never an execution**: a file walk plus the invariant id in a `describe`/`it` title. `coverage.symbol` means a declaration of that identifier in a non-test file (`symbolEvidenceFile`); imports and test calls do not count. Doctor and `--rules-inventory` print that witness path and the scan discard counts on a green run too. Never report `INVARIANT_UNCOVERED: 0`, a witness path, or a discard line as “the tests pass” or “the tests run” — ArkGate does not run tests and reads no runner config. Declaration text is not “the tests pass.” 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. Adopted + catalogued invariants without `coverage.testGlobs` or `coverage.coverageRoots` fails closed (`INVARIANT_TESTS_PATH_MISSING`) — write the path (`/ark-adopt`). When any invariant is enforced, missing `coverage.coverageRoots` fails closed (`INVARIANT_COVERAGE_ROOTS_MISSING`) — write the runner roots (`/ark-adopt`). `testGlobs` alone is not enough.
|
|
100
100
|
6. Extras silent when off. Doctor `arkRun` / `arkOrder` is `notAScore`. Do not force extras. Wire one kernel candidate via `/ark-runtime`; one order-plane candidate via `/ark-order`. Do not invent `/ark-run`.
|
|
101
101
|
|
|
102
102
|
|
|
@@ -188,7 +188,7 @@ else is judgment/deferred and must not be auto-applied.
|
|
|
188
188
|
**Type-edge honesty:** type-only denied edges are **placement debt** (`typeOnly` +
|
|
189
189
|
`failsStrict: false`) — prefer SharedTypes / owning layer; they do not fail merge like value
|
|
190
190
|
edges. Doctor `productHonesty` and `violations.typeEdgePolicy` must not be sold as “all done”
|
|
191
|
-
when residual signals remain.
|
|
191
|
+
when residual signals remain (`physical-cohesion-residual` included).
|
|
192
192
|
|
|
193
193
|
## Spoken / written explanation
|
|
194
194
|
|
|
@@ -53,6 +53,11 @@ one bounded decision needs 2–3 enforceable options.
|
|
|
53
53
|
`coverage.coverageRoots` on the folder the test runner uses. Fail-closed.
|
|
54
54
|
`testGlobs` alone is not enough. Silent when no invariant is enforced.
|
|
55
55
|
Handoff `/ark-adopt` to write the roots. No new skill.
|
|
56
|
+
- When an invariant declares `coverage.symbol`, read the witness path
|
|
57
|
+
(`symbolEvidenceFile`) and the discard line on doctor and `--rules-inventory`,
|
|
58
|
+
including when the run is green. A declaration in a non-test file is not
|
|
59
|
+
“the tests pass” — ArkGate never runs tests. Depth and byte caps stay
|
|
60
|
+
internal; the counts are what you read.
|
|
56
61
|
- A doctor green / Healthy line must name a file, config key, or test.
|
|
57
62
|
Uncited green is a lie — treat it as unfinished. No `/ark-cite`.
|
|
58
63
|
- Distinguish **missing** skills vs **stale** catalog. Installed ≠ stale.
|
|
@@ -345,12 +350,19 @@ targets as explicit adopter decisions, not residual to re-fight. Keep their mirr
|
|
|
345
350
|
map. When the user accepts, defers, or rejects a live `reshapePilot.nextPilot`, record the card's
|
|
346
351
|
exact `decisionTarget` with a reason (and optional `reviewBy`) in
|
|
347
352
|
`.ark/reshape-decisions.json`. Never infer or suppress from free-form golden-pattern prose.
|
|
353
|
+
A proposed `to` stays under the source anchor (`concept/`) and is kept only when that path
|
|
354
|
+
stays in the same layer and the same slice id. If no governed destination keeps both,
|
|
355
|
+
`nextPilot` is null — do not invent a folder outside the contract. Success is a smaller
|
|
356
|
+
cluster with the layer and slice id unchanged and the card still proposed; a green gate is
|
|
357
|
+
not success. A cohesion residual (`physical-cohesion-residual`) starts that one reshape
|
|
358
|
+
judgment card. It does not start the pattern-bet apply loop — `pilotLoop` stays inactive
|
|
359
|
+
while design-weak is false.
|
|
348
360
|
|
|
349
361
|
### C. Coupling & debt (measure, don’t guess)
|
|
350
362
|
Prefer evidence over aesthetics:
|
|
351
363
|
- **Fan-in / importers** of hot modules (who imports the suspect file?).
|
|
352
364
|
- **Export surface** (how many public symbols does the orchestration module re-export?).
|
|
353
|
-
- Import cycles, cross-feature leaks (`peerIsolation` candidates).
|
|
365
|
+
- Import cycles, cross-feature leaks (`peerIsolation` candidates). A shared root that imports a slice is a direct-only bridge; doctor lists it. `sharedImportsSlice: "deny"` closes that one hop. It does not finish the wall.
|
|
354
366
|
- Business rules in UI/hooks (`can*`, `calculate*`, policy constants) → Domain / intents.
|
|
355
367
|
- Dead or aspirational globs; layers with files but no rules; **starter/preset rules weaker than sibling archetypes** (soft green).
|
|
356
368
|
- LOC is a **hint** only — pair it with fan-in or export count before calling something a god module.
|
|
@@ -21,6 +21,8 @@ right house. Skills never enforce — CLI / hooks / CI do.
|
|
|
21
21
|
## Checklist
|
|
22
22
|
|
|
23
23
|
- `filePath` is known before the call. Description alone is not a path.
|
|
24
|
+
- Host write gate: prefer the installed pre-hook. MCP `ark_prepare_write` is
|
|
25
|
+
fallback when that hook is missing or fail-open. Skills never enforce.
|
|
24
26
|
- Golden pattern is load-bearing when present. Adopt generates it.
|
|
25
27
|
- An included file that matches no layer is not a pass. The write gate denies it
|
|
26
28
|
(`CONFIG_UNCLASSIFIED_FILES`). Place it in a layer folder, or send include/layer
|
|
@@ -259,9 +259,11 @@ ArkGate has **always-on Layers** plus opt-in extras. The user chooses extras; yo
|
|
|
259
259
|
If already current (`ALREADY_CURRENT`), retain the read-only preview and still read `whatsNew`
|
|
260
260
|
+ `skillDrift`.
|
|
261
261
|
|
|
262
|
-
For pnpm repositories with `minimumReleaseAge`,
|
|
263
|
-
|
|
264
|
-
|
|
262
|
+
For pnpm repositories with `minimumReleaseAge`, put the exception on
|
|
263
|
+
`minimumReleaseAgeExclude` in pnpm config or `pnpm-workspace.yaml` (a CLI
|
|
264
|
+
flag is not enough) when the new release is still cooling off, or keep using
|
|
265
|
+
`npx --package=arkgate@<pin>` until the package is mature. Prove
|
|
266
|
+
`pnpm install --frozen-lockfile` succeeds after a local add.
|
|
265
267
|
|
|
266
268
|
4. **Apply only the reviewed candidate.** When there are no blocked assets, run
|
|
267
269
|
the preview's **exact** `nextCommand` as emitted (JSON field / human “Apply the
|