arkgate 4.8.20 → 4.8.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (75) hide show
  1. package/CHANGELOG.md +124 -5
  2. package/README.md +107 -117
  3. package/bin/ark-check-runtime.mjs +3 -4
  4. package/bin/ark-layer-match.mjs +110 -18
  5. package/bin/lib/analysis-engine.mjs +13 -6
  6. package/bin/lib/config-contract.mjs +5 -0
  7. package/bin/lib/diagnostic-catalog.mjs +1 -0
  8. package/bin/lib/doctor-advisories.mjs +20 -0
  9. package/bin/lib/doctor-human.mjs +11 -9
  10. package/bin/lib/doctor-plan.mjs +15 -14
  11. package/bin/lib/first-run-help.mjs +7 -1
  12. package/bin/lib/html-report-advisories.mjs +8 -0
  13. package/bin/lib/html-report-depth.mjs +9 -8
  14. package/bin/lib/invariant-coverage.mjs +396 -75
  15. package/bin/lib/managed-upgrade.mjs +59 -14
  16. package/bin/lib/pilot-loop.mjs +183 -37
  17. package/bin/lib/upgrade-behavior-changes.mjs +83 -0
  18. package/bin/lib/upgrade-command.mjs +69 -31
  19. package/bin/lib/upgrade-outcome.mjs +359 -0
  20. package/bin/lib/violations.mjs +109 -0
  21. package/dist/{configTypes-VVknqcTJ.d.ts → configTypes-CTELo87-.d.ts} +7 -0
  22. package/dist/{diagnosticCatalog-BPYWNyJR.d.ts → diagnosticCatalog-B7BXlet6.d.ts} +73 -5
  23. package/dist/eslint/index.cjs +4 -4
  24. package/dist/eslint/index.d.ts +26 -3
  25. package/dist/eslint/index.js +4 -4
  26. package/dist/index.cjs +24 -17
  27. package/dist/index.d.ts +9 -4
  28. package/dist/index.js +25 -18
  29. package/dist/nestjs/index.cjs +9 -9
  30. package/dist/nestjs/index.d.ts +3 -3
  31. package/dist/nestjs/index.js +9 -9
  32. package/dist/runtime/index.cjs +18 -11
  33. package/dist/runtime/index.d.ts +6 -6
  34. package/dist/runtime/index.js +18 -11
  35. package/dist/{types-p_fCp6P-.d.ts → types-fQ9-KrBj.d.ts} +1 -1
  36. package/dist/{types-Cs-46DRu.d.ts → types-yDNEQ603.d.ts} +1 -1
  37. package/docs/README.md +4 -3
  38. package/docs/agent-guide.md +15 -7
  39. package/docs/brownfield-adoption.md +4 -3
  40. package/docs/configuration.md +34 -11
  41. package/docs/diagnostics.md +12 -2
  42. package/docs/package-surface.md +3 -2
  43. package/docs/product-voice.md +11 -3
  44. package/package.json +1 -1
  45. package/schemas/ark.config.schema.json +8 -0
  46. package/server.json +2 -2
  47. package/templates/agent-skills/README.md +1 -1
  48. package/templates/agent-skills/ark-adopt/SKILL.md +5 -2
  49. package/templates/agent-skills/ark-architect/SKILL.md +2 -0
  50. package/templates/agent-skills/ark-autopilot/SKILL.md +6 -4
  51. package/templates/agent-skills/ark-contract/SKILL.md +2 -0
  52. package/templates/agent-skills/ark-coverage/SKILL.md +2 -0
  53. package/templates/agent-skills/ark-explain/SKILL.md +2 -0
  54. package/templates/agent-skills/ark-explore/SKILL.md +4 -2
  55. package/templates/agent-skills/ark-fix/SKILL.md +2 -0
  56. package/templates/agent-skills/ark-loop/SKILL.md +2 -0
  57. package/templates/agent-skills/ark-order/SKILL.md +2 -0
  58. package/templates/agent-skills/ark-place/SKILL.md +2 -0
  59. package/templates/agent-skills/ark-runtime/SKILL.md +2 -0
  60. package/templates/agent-skills/ark-think/SKILL.md +2 -0
  61. package/templates/agent-skills/ark-upgrade/SKILL.md +20 -7
  62. package/templates/skills/ark-adopt.md +5 -2
  63. package/templates/skills/ark-architect.md +2 -0
  64. package/templates/skills/ark-autopilot.md +6 -4
  65. package/templates/skills/ark-contract.md +2 -0
  66. package/templates/skills/ark-coverage.md +2 -0
  67. package/templates/skills/ark-explain.md +2 -0
  68. package/templates/skills/ark-explore.md +4 -2
  69. package/templates/skills/ark-fix.md +2 -0
  70. package/templates/skills/ark-loop.md +2 -0
  71. package/templates/skills/ark-order.md +2 -0
  72. package/templates/skills/ark-place.md +2 -0
  73. package/templates/skills/ark-runtime.md +2 -0
  74. package/templates/skills/ark-think.md +2 -0
  75. package/templates/skills/ark-upgrade.md +20 -7
@@ -1,4 +1,4 @@
1
- import { a as ArkConfigRule, f as ArkConfigLayer, A as ArkConfig } from './configTypes-VVknqcTJ.js';
1
+ import { a as ArkConfigRule, f as ArkConfigLayer, A as ArkConfig } from './configTypes-CTELo87-.js';
2
2
 
3
3
  /**
4
4
  * Policy types for the Ark kernel.
@@ -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-p_fCp6P-.js';
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-fQ9-KrBj.js';
2
2
 
3
3
  /**
4
4
  * PolicyEngine
package/docs/README.md CHANGED
@@ -73,12 +73,13 @@ 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.20` on npm `latest`; does not close `K01`).
76
+ Current release: [CHANGELOG](../CHANGELOG.md) (`arkgate@4.8.21`; confirm npm with `npm view arkgate@4.8.21 version`; does not close `K01`).
77
+ Prior published: [CHANGELOG](../CHANGELOG.md) (`arkgate@4.8.20`). Plain-English history: [arkgate.online/changelog](https://www.arkgate.online/changelog/).
77
78
  Prior published: [releases/4.8.11.md](releases/4.8.11.md) (`arkgate@4.8.11`).
78
79
  Prior published: [releases/4.8.10.md](releases/4.8.10.md) (`arkgate@4.8.10`).
79
80
  Prior published: [releases/4.8.9.md](releases/4.8.9.md) (`arkgate@4.8.9`).
80
- Prior: [releases/4.8.6.md](releases/4.8.6.md) · [releases/4.8.5.md](releases/4.8.5.md) · [releases/4.8.4.md](releases/4.8.4.md) · [releases/4.8.3.md](releases/4.8.3.md) · [releases/4.8.2.md](releases/4.8.2.md) · [releases/4.8.1.md](releases/4.8.1.md) · [4.8.0](releases/4.8.0.md) · [4.7.6](releases/4.7.6.md) · [4.7.5](releases/4.7.5.md) · [4.7.4](releases/4.7.4.md) · [4.7.3](releases/4.7.3.md) · [4.7.2](releases/4.7.2.md) · [4.7.1](releases/4.7.1.md) · [4.7.0](releases/4.7.0.md) · [4.6.7](releases/4.6.7.md) · [4.6.6](releases/4.6.6.md) · [4.6.5](releases/4.6.5.md) · [4.6.4](releases/4.6.4.md) · [4.6.3](releases/4.6.3.md) · [4.6.2](releases/4.6.2.md) · [4.6.1](releases/4.6.1.md) · [4.6.0](releases/4.6.0.md).
81
- Older notes: [releases/](releases/). Config: [configuration.md](configuration.md).
81
+ Prior: [releases/4.8.8.md](releases/4.8.8.md) · [releases/4.8.7.md](releases/4.8.7.md) · [releases/4.8.6.md](releases/4.8.6.md) · [releases/4.8.5.md](releases/4.8.5.md) · [releases/4.8.4.md](releases/4.8.4.md) · [releases/4.8.3.md](releases/4.8.3.md) · [releases/4.8.2.md](releases/4.8.2.md) · [releases/4.8.1.md](releases/4.8.1.md) · [4.8.0](releases/4.8.0.md) · [4.7.6](releases/4.7.6.md) · [4.7.5](releases/4.7.5.md) · [4.7.4](releases/4.7.4.md) · [4.7.3](releases/4.7.3.md) · [4.7.2](releases/4.7.2.md) · [4.7.1](releases/4.7.1.md) · [4.7.0](releases/4.7.0.md) · [4.6.7](releases/4.6.7.md) · [4.6.6](releases/4.6.6.md) · [4.6.5](releases/4.6.5.md) · [4.6.4](releases/4.6.4.md) · [4.6.3](releases/4.6.3.md) · [4.6.2](releases/4.6.2.md) · [4.6.1](releases/4.6.1.md) · [4.6.0](releases/4.6.0.md).
82
+ Older notes: [releases/4.5.7.md](releases/4.5.7.md) · [releases/4.5.6.md](releases/4.5.6.md) · [releases/4.5.5.md](releases/4.5.5.md) · [releases/](releases/). Config: [configuration.md](configuration.md).
82
83
 
83
84
  ---
84
85
 
@@ -161,6 +161,8 @@ npx arkgate upgrade --apply # registry-aware package step, then re-previ
161
161
  npx arkgate upgrade --apply --no-install --plan-digest sha256:… --refresh-skills
162
162
  ```
163
163
 
164
+ `upgrade --apply` exits `0` when the managed files were applied and the architecture check is green, `3` when they were applied but the check is red, `2` when a recorded file still needs consent, and `1` on error (including a stale plan digest). Scripts comparing the status against `1` must switch to non-zero. JSON adds `outcome` and `postUpgrade` (`verdict`, `failing[{ruleId,count,sample}]`, `behaviorChanges`). `behaviorChanges` is the shipped per-version table, the same sentences as the changelog migration notes. A missing gitignored managed file is `absent-local` and is recreated without `--accept-conflicts`. Every blocked asset has `reason` and `nextCommand`. If `postUpgrade.verdict` is `red`, stop: report `failing` and `behaviorChanges`, and do not edit tests, titles, or config in that turn.
165
+
164
166
  ### Two-axis done recipe
165
167
 
166
168
  Architecture residual and feature residual are **separate axes**. Never collapse them:
@@ -361,13 +363,16 @@ npx ark-check --doctor --fail-on-new-smells --base-ref origin/main --json # opt-
361
363
  (`clarify-for-ai`) — **one** Shape door: `/ark-explore` shape-focus → dual-plan B, then
362
364
  `/ark-autopilot` applies one pilot. Do not skill-shop coverage/think for the same residual.
363
365
 
364
- **Pilot loop (Q04):** when design-weak, `pilotLoop.nextPilot` is **one** extraction card
365
- (pilot target, move, success, kill-switch). Apply **that one pilot only**, then re-doctor.
366
+ **Pilot loop (Q04):** `pilotLoop` is active when a pilot candidate exists. Design-weak
367
+ pattern bets are one source. A proposed physical-cohesion reshape card is the other, even
368
+ when the tree is not design-weak. `pilotLoop.nextPilot` / `extractionCard` is **one**
369
+ extraction card (pilot target, move, success, kill-switch). Apply **that one pilot only**,
370
+ then re-doctor. Further candidates stay queued. Inactive reason is `no-pilot-candidates`.
366
371
  Success = reduced smell evidence on pilot paths; residual outside the pilot may remain.
367
372
  Never select seed/fixture/demo/migration/generated files as god-module pilots. A real UI business
368
373
  rule moves Domain → Application → UI; local permission/UI-state helpers are not selected by their
369
374
  `canEdit`-style name alone. Never multi-pilot batch; never mechanical-safe; never claim healthy
370
- finished while design-weak.
375
+ finished while `doctor.productHonesty.finished` is false.
371
376
 
372
377
  **AI-velocity evidence (Q05):** deterministic fixture bench (no live LLM) compares the same
373
378
  feature add on design-weak vs golden-path trees. Run `npm run eval:ai-velocity`; metric is
@@ -432,9 +437,11 @@ port. Advisory only — never blocks, never feeds `designFitness`; no strict mod
432
437
  clusters per anchor directory (concentration, not volume — dispersed hooks never fire) with
433
438
  fixed corpus-calibrated thresholds; anchors under `app/`/`pages/` are `fixedByConvention` and
434
439
  never move. `reshapePilot.nextPilot` is a **proposed** one-at-a-time card (`moveSample`,
435
- `movesTotal`, `successSignal`, `killSwitch`, `doNot[]`): run it only via `/ark-loop` through the
436
- write gate + atomic preflight; merges are `/ark-adopt` / `/ark-autopilot` judgment cards. `notAScore`, never a
437
- verdict/`designFitness` input; there is no apply path.
440
+ `movesTotal`, `successSignal`, `killSwitch`, `doNot[]`) and, when proposed, the single
441
+ `pilotLoop` extraction card if no design-weak pattern bet is ahead of it. Run it only via
442
+ `/ark-loop` through the write gate + atomic preflight; merges are `/ark-adopt` /
443
+ `/ark-autopilot` judgment cards. `notAScore`, never a verdict/`designFitness` input; there
444
+ is no apply path.
438
445
 
439
446
  **Reshape decision memory (Y01):** when the team accepts, defers, or rejects that target, record
440
447
  the explicit verdict in `.ark/reshape-decisions.json` using the card's exact
@@ -1135,7 +1142,8 @@ edges are not denied by that rule.
1135
1142
 
1136
1143
  - **Denied:** `src/features/auth/**` → `src/features/payments/**` (different slice id).
1137
1144
  - **Allowed:** same-slice imports when both paths classify; classic non-peerIsolation denies still apply across layers.
1138
- - **`sliceFolders`:** optional parent segments (default: inferred from layer globs).
1145
+ - **`sliceFolders`:** optional parent segments (default: inferred from layer globs). A bare name stays an unanchored one-segment match (`features` on `src/features/auth` is `features/auth`). A starred anchored pattern with `sliceIdentity` `path` (the default) includes the literal prefix plus the star bindings (`lib/features/*/*` → `lib/features/projects/rfi`), so parallel trees get different ids.
1146
+ - **`sliceIdentity`:** `path` (default, also when the key is absent) or `stars`. `path` keeps today's slice ids. `stars` names a starred prefix as the last literal plus the star bindings (`lib/features/*/*` and `lib/repositories/features/*/*` both yield `features/projects/rfi`). Doctor names both prefixes when they collapse onto one id. Additive — no migration.
1139
1147
  - **Fail-closed:** missing paths, empty/unresolvable slice folders, or unclassifiable either side → **deny** via peerIsolation (cannot prove same-slice).
1140
1148
  - **`sharedRoots`** (4.8.4): roots the repo declares shared on purpose (`["ui", "hooks", "lib/permissions"]`). A file under a declared shared root is evidence, not an unclassifiable path, so fail-closed stops firing on every shared file. **Anchored** — the root starts the path, optionally after one `src/` or `app/`; write deeper or monorepo roots out (`packages/web/src/ui`) or glob them, and a bare `*` / `**` is refused. A path that still resolves to a slice keeps its slice.
1141
1149
  - **`allowedCrossSlice`** (4.8.4): `[{ "from": "features/checkout", "to": "features/catalog" }]` — one directed slice→slice edge the repo declares on purpose. The reverse still denies.
@@ -228,12 +228,13 @@ ark-check --plan --json # patternBets[] with neverMechanicalSafe: true
228
228
 
229
229
  ### Pilot loop (Q04) — one pilot at a time → re-doctor
230
230
 
231
- When doctor/plan report **ENFORCE · design-weak**, JSON also includes **`pilotLoop`**:
231
+ When a pilot candidate exists, doctor/plan JSON includes **`pilotLoop`**:
232
232
 
233
233
  | Field | Meaning |
234
234
  |-------|---------|
235
- | `pilotLoop.active` | `true` when design-weak and at least one pattern bet exists |
236
- | `pilotLoop.nextPilot` | **One** extraction card (same fields as §6) ranked from `patternBets` |
235
+ | `pilotLoop.active` | `true` when a candidate exists: a design-weak pattern bet, or a proposed reshape card |
236
+ | `pilotLoop.nextPilot` / `extractionCard` | **One** extraction card (same fields as §6). Further candidates stay queued |
237
+ | `pilotLoop.reason` | `no-pilot-candidates` when the list is empty |
237
238
  | `pilotLoop.oneAtATime` | Always true — do not multi-pilot batch |
238
239
  | `pilotLoop.neverMechanicalSafe` | Always true — judgment only; re-doctor is the success sensor |
239
240
 
@@ -250,8 +250,8 @@ process module-capability family must be denied.
250
250
 
251
251
  Rule fields:
252
252
 
253
- - `from`, `to`, `allowed`, `message`, `peerIsolation`, `sliceFolders`, `sharedRoots`,
254
- `allowedCrossSlice`, `sharedImportsSlice`
253
+ - `from`, `to`, `allowed`, `message`, `peerIsolation`, `sliceFolders`, `sliceIdentity`,
254
+ `sharedRoots`, `allowedCrossSlice`, `sharedImportsSlice`
255
255
  - `peerIsolation: true` + `allowed: false`: deny only when slice ids differ; same-slice allows
256
256
  when both paths classify. Applies to **any** declared `from`→`to` pair, not only self-edges.
257
257
  Missing paths, empty slice folders, or unclassifiable slices **fail closed** (deny — cannot
@@ -262,15 +262,38 @@ Rule fields:
262
262
  `src/components/features/projects/rfi-status-pill.tsx` with `["projects"]` is not a slice.
263
263
  A starred prefix (`lib/features/*/*`) is anchored like `sharedRoots` — it starts the
264
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.
265
+ directory and never the filename. With `sliceIdentity` `path` (the default, also
266
+ when the key is absent) the slice id includes the literal prefix plus those
267
+ star bindings (`lib/features/projects/rfi`), so parallel trees get different ids.
268
+ A file directly under `lib/features/projects/` stays `lib/features/projects`. A path
269
+ off that prefix (`src/app/api/projects/...`) is not that slice.
270
+ - `sliceIdentity` is `path` or `stars`. Absent and `path` are the same: today's ids,
271
+ byte for byte, so baselines and `allowedCrossSlice` keep working with no migration.
272
+ `stars` names a starred prefix as the last literal plus the star bindings.
273
+ `lib/features/*/*` and `lib/repositories/features/*/*` both yield
274
+ `features/projects/rfi`, so one feature has one id across parallel trees. Bare names
275
+ (`features`) are unchanged. Doctor warns when two different prefixes bind as the
276
+ same id, and the warning names both paths (`admin/features/*` and `public/features/*`
277
+ both bind as `features/*`). That warning is advisory. Unrelated trees that share a
278
+ last folder name should stay on `path`.
279
+
280
+ ```jsonc
281
+ {
282
+ "from": "ApplicationOrchestration",
283
+ "to": "PersistenceAdapters",
284
+ "allowed": false,
285
+ "peerIsolation": true,
286
+ "sliceFolders": ["lib/features/*/*", "lib/repositories/features/*/*"],
287
+ "sliceIdentity": "stars"
288
+ }
289
+ ```
268
290
 
269
291
  #### Cross-layer slice walls (already in the engine)
270
292
 
271
293
  A slice wall on a cross-layer edge is a `peerIsolation` rule on that `from`/`to` pair
272
- (`allowed: false`). There is no slice-wide engine mode, no new config key, and no new skill
273
- name. `findDeniedEdgeDecision` already applies `peerIsolation` to any declared pair
294
+ (`allowed: false`). There is no slice-wide engine mode and no new skill name.
295
+ `sliceIdentity` only changes how a starred prefix is named. `findDeniedEdgeDecision`
296
+ already applies `peerIsolation` to any declared pair
274
297
  (same-layer or cross-layer) — locked on `EdgeRule` and `findDeniedEdgeDecision` in
275
298
  [`src/domain/layerMatch.ts`](../src/domain/layerMatch.ts). There is no dedicated ADR to add.
276
299
 
@@ -320,9 +343,9 @@ is evidence:
320
343
  character must not disable fail-closed. A path that still resolves to a slice keeps its slice —
321
344
  `features/auth/ui/form.tsx` stays `features/auth`. Shared roots are a sink: a slice may import
322
345
  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.
346
+ to `"deny"`. That hop is direct only — `allowedCrossSlice` does not excuse it. A green
347
+ `ark-check` prints one line per layer edge (the count and three examples) and says the
348
+ wall is direct-only. Every edge stays in `ark-check --json` and `ark-check --doctor`.
326
349
  - `allowedCrossSlice` entries match a full slice id (`features/catalog`) or a bare slice name
327
350
  (`catalog`), and only in the direction written. The reverse edge still denies. A bare name
328
351
  matches that name under **any** slice folder, so in a repo with several slice parents
@@ -413,7 +436,7 @@ Each `arkrules/<Layer>.json` may declare:
413
436
  | Section | Purpose | Modes | What it really enforces |
414
437
  |---------|---------|--------|-------------------------|
415
438
  | `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). |
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.” |
439
+ | `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`; additive `shape`: `function`, `class`, `const`, `method`, `type`, `interface`, or `enum`). Imports and test calls do not count. A `describe` / `it` / `test` / `context` title may also name the invariant id. A comment, string, or other bare mention does not. 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.” |
417
440
 
418
441
  **Reporting:** diagnostics carry `evidence.arkruleId` + `evidence.arkruleSource`. Label residual
419
442
  **`[Layer]`** vs **`[ArkRules]`** in agent output. Doctor / HTML: `rulesUnderContract` (catalog +
@@ -101,6 +101,7 @@ Link form for agents: `docs/diagnostics.md#RULE_ID` (exact-case HTML anchors bel
101
101
  | [`CONFIG_AMBIGUOUS_LAYERS`](#CONFIG_AMBIGUOUS_LAYERS) | config | Ambiguous layer classification |
102
102
  | [`CONFIG_UNCLASSIFIED_FILES`](#CONFIG_UNCLASSIFIED_FILES) | config | Unclassified included files |
103
103
  | [`CONFIG_LAYER_MISSING_OWNER`](#CONFIG_LAYER_MISSING_OWNER) | config | Layer missing owner |
104
+ | [`CONFIG_SLICE_IDENTITY_COLLISION`](#CONFIG_SLICE_IDENTITY_COLLISION) | config | Starred slice prefixes share one id |
104
105
  | [`ARK_UNKNOWN`](#ARK_UNKNOWN) | meta | Unknown diagnostic |
105
106
 
106
107
  ## Layer and dependency graph
@@ -308,8 +309,8 @@ Link form for agents: `docs/diagnostics.md#RULE_ID` (exact-case HTML anchors bel
308
309
 
309
310
  **Invariant without coverage evidence**
310
311
 
311
- - **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).
312
- - **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.
312
+ - **Why:** An ArkRules invariant is under contract but no covering test title or declared symbol evidence was found (or coverage is partial). A title is a `describe` / `it` / `test` / `context` string. A comment, string, test body, or import that only mentions the id does not count. `coverage.symbol` is a non-test declaration: `function`, `class`, `const`, `method`, `type`, `interface`, or `enum`. Kind is `never-had-tests` (adopt residual) vs `tests-disappeared` (suite exists). The message names what the scan found.
313
+ - **Fix:** Put the invariant id in a `describe` / `it` title, or declare the `coverage.symbol` name in a non-test file, then preflight again. A comment is not a title — move the id into the `it()` title. Treat never-had-tests as adopt residual; treat tests-disappeared as a regression. Missing test globs report partial — never fake green. When the message reports an exhausted file budget, raise `coverage.maxFiles` (or narrow `coverage.testGlobs`) in ark.config.json. The message also names every file the scan discarded and why (budget, per-file byte cap, unreadable, walk depth limit, symlink resolving outside the project root, no catalogued invariant named) — nothing is dropped in silence.
313
314
 
314
315
  <a id="INVARIANT_COVERAGE_OUTSIDE_ROOTS"></a>
315
316
 
@@ -894,6 +895,15 @@ never opting out of knowing.
894
895
  - **Why:** `requireLayerOwners` is on and this layer has no owners. Writes to that house fail closed, like a folder with no name on the door.
895
896
  - **Fix:** Add a GitHub handle or email to that layer’s owners in ark.config.json (`/ark-adopt`), then re-run. Reserved/allowEmpty houses may stay unnamed.
896
897
 
898
+ <a id="CONFIG_SLICE_IDENTITY_COLLISION"></a>
899
+
900
+ ### `CONFIG_SLICE_IDENTITY_COLLISION`
901
+
902
+ **Starred slice prefixes share one id**
903
+
904
+ - **Why:** `sliceIdentity` is `stars` and two different `sliceFolders` prefixes bind as the same slice id. Parallel trees of one feature do that on purpose; unrelated trees that share the last folder name merge by accident. The warning names both paths.
905
+ - **Fix:** Keep `stars` when those prefixes are one feature. Otherwise use `path`, or rename the last literal so the prefixes do not bind as the same id. Advisory — it does not fail the check.
906
+
897
907
  ## Meta
898
908
 
899
909
  <a id="ARK_UNKNOWN"></a>
@@ -48,12 +48,13 @@ hardening guide remains repository-hosted rather than duplicated in the gate tar
48
48
  | **ArkRun doctor / status / report (RN08)** | `ark-check --doctor --json` → `doctor.arkRun`; HTML `data-advisory="arkRun"`; `ark status --json` / MCP `ark_status` → `arkRun`; `rulesUnderContract.mergePlanes.arkRun` | Additive. Always **`notAScore: true`**. Residual is a finding-id count (status residual `null` = unknown, not green). Advisory and absence never arm extra merge teeth; enforced teeth follow the classified-layer floor. Never a score or LLM verdict. |
49
49
  | **Upgrade what’s new (4.5.6+)** | `ark upgrade --json` → `whatsNew` (+ human **Suggested improvements** block; also on preview). | Always **`notAScore: true`**, **`neverGateInput: true`**. Closed try/inspect list: deep-module coach, improvement compass, session/status honesty, two-axis done, self-service honesty, registry-aware upgrade, skill drift/refresh, multi-project MCP, Codex hard-write refresh/trust/restart/verify, stale MCP/global CLI recovery. Never invents residual or flips gates. |
50
50
  | **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. |
51
+ | **Upgrade outcome (4.8.21)** | `ark upgrade --apply` JSON → additive `outcome`, `postUpgrade` (`verdict`, `failing[{ruleId,count,sample}]`, `behaviorChanges`), and on each blocked asset `reason` + `nextCommand`. State `absent-local` recreates a recorded gitignored file. | Exit `0` applied and green, `3` applied but red, `2` blocked, `1` error. Scripts comparing against `1` must switch to non-zero. `behaviorChanges` is the shipped per-version table (same sentences as the changelog). JSON fields are additive. `/ark-upgrade` stops when verification is red and does not edit tests, titles, or config in that turn. |
51
52
  | **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. |
52
53
  | **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. |
53
54
  | **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. |
54
55
  | **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. |
55
56
  | **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. |
56
- | **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. |
57
+ | **Pilot loop (one at a time)** | `plan.pilotLoop` / `doctor.pilotLoop` | Additive. Active when at least one pilot candidate exists: a design-weak pattern bet, or a proposed physical-cohesion reshape card. Inactive reason is `no-pilot-candidates`. `oneAtATime`, `neverMechanicalSafe`, **one** `nextPilot` / `extractionCard` (same extraction-card fields: `pilotTarget`, `smellId`, `move`, `successSignal`, `killSwitch`, `doNot[]`). Further candidates stay queued. **One pilot → re-doctor**; never multi-pilot batch; never mechanical-safe. Gate verdict unchanged. |
57
58
  | **AI-velocity eval (maintainer)** | Repository-only evidence: `npm run eval:ai-velocity` → `eval/ai-velocity-report.json` | Fixture-measured (no live LLM). Same feature scenario on design-weak vs golden-path arms; metric **`placementTurns`** (agent-equivalent). Golden must be strictly better. Method string lives next to the number. Does not weaken the gate. |
58
59
  | **Contract health** | `ark-check --doctor --json` → `doctor.contractHealth`; optional `.ark/contract-smell-acks.json` | Additive, **advisory only** — meta-lint of the contract itself (layer-name heuristics; imprecision costs a warning line, never a verdict); never changes the verdict, `designFitness`, or `patternBets`. Stable smell ids: `contract-bidirectional-allow`, `contract-peripheral-depends-core`, `contract-lateral-adapter-allow`, `contract-dead-rule`; each smell has `severity`, `evidence[]` (sorted, honest `…(+N more)` truncation), `fix`, `message`, plain-language `outcome`, and `acknowledgedEdges` (acks applied to that id). **X03/X06**: the lateral smell does not fire on an adapter reaching its **own family's infra base** — the target reads `<Family><InfraWords…>` (**every** remaining target token an infra word: `Infra(structure)`/`Base`/`Core`/`Shared`/`Common`/`Kernel`/`Platform`/`Foundation`) and the source carries the family token **anywhere** in its name (X06, field: `HoursPersistenceAdapters -> PersistenceInfrastructure` — mid-name families). `PaymentsCoreAdapters` is still a sibling; cross-family edges, non-infra siblings, and the reverse (base → member) still fire. Acknowledgments live in the bounded sidecar (`{ acks: [{ id, edge, reason, reviewBy? }] }`, ≤64 KB / ≤200 entries; bidirectional edges order-insensitive, exact two segments); `contractHealth.acknowledged` counts **applied** acks only (stale acks count 0). **X02 ack lifecycle**: optional `reviewBy` (`YYYY-MM-DD`, strict round-trip validation — `2026-02-30` is malformed) — past the date the ack **stops applying** and the smell returns with `(ack expired …)` annotated evidence; among dated entries a fresh re-ack wins over a dead one, but once ANY dated ack exists for an edge the dated entries govern — a leftover undated duplicate cannot resurrect an expired exception. `detectContractSmells` defaults `today` to the real clock (pass `null` to disable expiry); `analyzeContractSmells` stays pure (clock injected). `contractHealth.ackLifecycle` reports `{ undated, malformed, expiredCount, expired[], staleCount, stale[] (lists capped at 12) }`; undated acks apply (backward compatible) but surface in doctor, report, and the fossilization note even when every smell is suppressed. **X05**: an ack matching **no detected edge** (orphaned by a fixed contract or quieted heuristic, unknown id, or typo'd edge) is `stale` — it suppresses nothing and doctor/report list the exact entries to fix or delete, even at zero visible smells. Malformed `reviewBy` never applies (fail-loud, like a sloppy edge); non-string `reviewBy` → whole file `invalid`. **Absent is normal**; malformed file or edge grammar → ignored + `ackFile.invalid` where applicable, never silent suppression. |
59
60
  | **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. |
@@ -74,7 +75,7 @@ hardening guide remains repository-hosted rather than duplicated in the gate tar
74
75
  | **`arkgate/schema/project-identity`** or **`arkgate/schema/ark.project-identity.schema.json`** | MCP canonical project, contract, runtime, expectation, and binding envelope | Schema `1.0`. Initial `expectedRoot` must be the exact project root. A contained descendant can match only when `expectedProjectId` is also present and correct; id-only matching stays non-authoritative. Mismatch codes are `PROJECT_ROOT_MISMATCH`, `PROJECT_ID_MISMATCH`, and `INVALID_PROJECT_EXPECTATION`. |
75
76
  | **Package pin dual-truth (4.0)** | doctor JSON `packageVersionTruth`; upgrade JSON/human note when pin behind CLI | Additive, advisory. Surfaces after `upgrade --no-install` when managed CLI is ahead of package.json. |
76
77
  | **Managed upgrade self-service honesty (4.5 / DF05)** | `ark upgrade [--json]` → `selfService` (+ human “Self-service honesty” lines) | Additive, advisory. Answers without a maintainer: write-path activation labels per selected host (`hard`\|`advisory`\|`unavailable`) and customized content-identity preserve (`customizedPaths` / `customizedContentPreserved`). Soft hosts never hard; upgrade never invents `hardWriteActive` from disk alone. Always `notAScore: true`. Not a gate input; not part of `planDigest`. |
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
+ | **Product honesty readiness split (4.1.1)** | doctor JSON `productHonesty` | Additive. `unfinished` / `headline` / `primaryNextAction` / `reasonIds` remain; EH adds `contractReadiness` (`ready`\|`partial`\|`not-ready`), `localWriteBoundary` (`advisory`\|`hard`\|`unverified`\|`unknown`), `architectureReasonIds`, `environmentResidualIds` / `environmentResiduals`. Soft-write hosts stay in evidence without alone forcing global **Not finished**. `notAScore: true` always. A physical-cohesion residual that reshape decisions have not suppressed adds `physical-cohesion-residual`, which clears `elegant` and `finished` and sets the headline to residual remaining; the gate verdict, `designFitness`, `designWeak`, and `patternBets` stay unchanged. When that residual still proposes `reshapePilot.nextPilot`, `pilotLoop` is active with that one extraction card. |
78
79
  | **Policy transition analysis (3.1.0)** | `analyzePolicyDelta(...)`; MCP `ark_policy_delta`; CLI `--policy-base` / `--policy-base-ref` / `--policy-ack`; check JSON `policyDelta` | Additive schema `1.0`. Classifications and finding ids are deterministic. Weakening/judgment requires an acknowledgement bound to both policy hashes and the exact blocking finding set. A new layer or new allow edge is `judgment-required`. On the CLI/MCP policy-ack plane, `adrPath` must name a short note under `docs/adr/` or `docs/decisions/` (`policyDelta.adrNote`). Domain hash match stays I/O-free. |
79
80
  | **Team parliament (law vs feature)** | Optional `stewards` on `ark.config.json` (GitHub handle or email); CLI `--local` / `--changed` / `--against` / `--base` / `--contract-diff` / `--contract-session` / `--persona` / `--author`; check JSON `teamParliament`; `ark status --vs`; write-gate mixed-batch deny | Additive. Law files must not mix with product source. Loosen / baseline-grow are steward-only when `stewards` is set. `--against` ratchets vs the base-ref baseline. `--changed` scans touched sources plus their import closure. `--local` (or `ARK_CHECK_LOCAL=1`) is the same cheap path and stays per `--root`; it cannot combine with `--strict-merge`. Check JSON may add `local`, `scope: "changed"`, `analysisRoot`. `stewards`, `layers[].description`, `layers[].trustBoundary`, and `layers[].owners` are excluded from policy hash. `requireLayerOwners` stays in the hash. Identity is handle or email, not git `user.name`. No org plane. |
80
81
  | **Atomic change preflight (3.1.0)** | `preflightChange(...)`; CLI `ark preflight --changes <file> --json`; MCP `ark_prepare_change` | Additive schema `1.0`. One complete governed production-source `{path,content}` / `{path,delete:true}` batch; read-only; returns operation, content/tree/policy/compiler fingerprints and stable graph findings. MCP availability alone is advisory. |
@@ -66,9 +66,9 @@ Skills never enforce. ξ / Haken stay below the fold.
66
66
 
67
67
  ## Locked first-contact
68
68
 
69
- On the eight canonical public openings — `README.md`, `docs/use.md`,
70
- `docs/develop.md`, `docs/README.md`, `docs/enthusiast/README.md`,
71
- `docs/agent-guide.md`, `docs/ai-gates.md`, and `docs/package-surface.md` — use:
69
+ On the seven lane openings — `docs/use.md`, `docs/develop.md`, `docs/README.md`,
70
+ `docs/enthusiast/README.md`, `docs/agent-guide.md`, `docs/ai-gates.md`, and
71
+ `docs/package-surface.md` — use:
72
72
 
73
73
  1. **Verbs:** `Write. Check. Ship.`
74
74
  2. **Deny:** `When the agent writes a bad import, the write doesn’t land. The same check fails the pull request.`
@@ -77,6 +77,14 @@ On the eight canonical public openings — `README.md`, `docs/use.md`,
77
77
  5. **Caption (immediately after the four paragraphs):** `That is **Contener · Guiar · Ordenar**.`
78
78
  6. **Technical nouns (below the introduction):** ArkGate is import rules. ArkRules is optional policies. ArkRun is an optional experimental runtime. ArkOrder is the extra for the few big product choices (billing plan, not seat counts) — named when the consumer opts in, never as the first noun. No ξ / Haken / slaving on the first screen.
79
79
 
80
+ `README.md` is the first-contact module. Its first screen is four beats: what
81
+ (**Contener · Guiar · Ordenar**), why (design the boundary before you implement;
82
+ a green check is **needs a refactor** when leftover mess remains), one command
83
+ (`npx arkgate@4.8.21 start`), and one honest red result (fail-open named; required
84
+ CI is the hard line). The four paragraphs, the not-that line, the host matrix,
85
+ and the release index sit below `## Appendix`. The not-that line does not lead
86
+ the README.
87
+
80
88
  Do not lead with folders, `ark.config.json`, “contract”, “gate”, “house”, or “doctor”
81
89
  as the first noun. Historical: `If the AI writes an illegal import, the write is rejected`
82
90
  and `One architecture config. One check. One coach.` — never first.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "arkgate",
3
- "version": "4.8.20",
3
+ "version": "4.8.21",
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",
@@ -798,6 +798,14 @@
798
798
  "uniqueItems": true,
799
799
  "minItems": 1
800
800
  },
801
+ "sliceIdentity": {
802
+ "type": "string",
803
+ "enum": [
804
+ "path",
805
+ "stars"
806
+ ],
807
+ "description": "How a starred sliceFolders prefix is named. Absent and path keep today's ids (every literal and star binding). stars keeps the last literal plus the star bindings, so parallel trees share one feature id. Bare names are unchanged."
808
+ },
801
809
  "sharedRoots": {
802
810
  "type": "array",
803
811
  "items": {
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.20",
9
+ "version": "4.8.21",
10
10
  "packages": [
11
11
  {
12
12
  "registryType": "npm",
13
13
  "identifier": "arkgate",
14
- "version": "4.8.20",
14
+ "version": "4.8.21",
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.20**
12
+ Package version when last generated context: **arkgate@4.8.21**
13
13
  Schema: agent-skills package contract `1.0`
14
14
 
15
15
  ## Skills (closed catalog)
@@ -366,8 +366,9 @@ Ark protects the **boundary around** a framework, not its internals. Nest/DI pub
366
366
  `sliceFolders`: a bare name stays an unanchored one-segment match (`features` on
367
367
  `src/lib/features/projects/rfi/x.ts` is `features/projects`); a filename is never the
368
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
369
+ (offset 0 or 1). The slice id includes the literal prefix plus the directories
370
+ the stars bind (`lib/features/projects/rfi`), so parallel trees get different ids
371
+ (tracked in #308). Before trusting green, add one deliberate
371
372
  feature-to-feature import and expect `cross-slice`.
372
373
  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).
373
374
  Empty Domain/Persistence + I/O under Application → false-green.
@@ -437,5 +438,7 @@ End with **exactly** these headings (markdown `###`):
437
438
  - **Handoff:** `/ark-…` / CLI / `none`
438
439
  - **Incomplete?** `no` | `yes — <what is missing>`
439
440
 
441
+ When `doctor.productHonesty.finished` is `false`, **Incomplete? no** is disallowed. Write `yes — <pilotLoop.extractionCard.move or residual>`.
442
+
440
443
  If a **STOP** handoff applies and you continued as if done, set **Incomplete?** to `yes`.
441
444
  **Skill incomplete if missing** any of the bullets above.
@@ -59,3 +59,5 @@ Skill incomplete if missing any field below.
59
59
  - **Compass:** top residual lenses | `n/a`
60
60
  - **Handoff:** `/ark-adopt` / `/ark-place` / `none`
61
61
  - **Incomplete?** `no` | `yes — <what is missing>`
62
+
63
+ When `doctor.productHonesty.finished` is `false`, **Incomplete? no** is disallowed. Write `yes — <pilotLoop.extractionCard.move or residual>`.
@@ -110,10 +110,10 @@ 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.
115
-
116
-
113
+ A cohesion residual starts **one** reshape judgment card on `pilotLoop.nextPilot`
114
+ (one pilot, then re-doctor). The loop is active when a pilot candidate exists,
115
+ including that reshape card when the tree is not design-weak. It does not start
116
+ a pattern-bet batch.
117
117
 
118
118
  ## MCP workspace binding (mandatory)
119
119
 
@@ -308,5 +308,7 @@ End with **exactly** these headings (markdown `###`):
308
308
  - **Handoff:** `/ark-…` / CLI / `none`
309
309
  - **Incomplete?** `no` | `yes — <what is missing>`
310
310
 
311
+ When `doctor.productHonesty.finished` is `false`, **Incomplete? no** is disallowed. Write `yes — <pilotLoop.extractionCard.move or residual>`.
312
+
311
313
  If a **STOP** handoff applies and you continued as if done, set **Incomplete?** to `yes`.
312
314
  **Skill incomplete if missing** any of the bullets above.
@@ -64,3 +64,5 @@ Skill incomplete if missing any field below.
64
64
  - **Compass:** top residual lenses | `n/a`
65
65
  - **Handoff:** `/ark-adopt` / `/ark-autopilot` / `/ark-order` / `none`
66
66
  - **Incomplete?** `no` | `yes — <what is missing>`
67
+
68
+ When `doctor.productHonesty.finished` is `false`, **Incomplete? no** is disallowed. Write `yes — <pilotLoop.extractionCard.move or residual>`.
@@ -200,5 +200,7 @@ End with **exactly** these headings (markdown `###`):
200
200
  - **Handoff:** `/ark-…` / CLI / `none`
201
201
  - **Incomplete?** `no` | `yes — <what is missing>`
202
202
 
203
+ When `doctor.productHonesty.finished` is `false`, **Incomplete? no** is disallowed. Write `yes — <pilotLoop.extractionCard.move or residual>`.
204
+
203
205
  If a **STOP** handoff applies and you continued as if done, set **Incomplete?** to `yes`.
204
206
  **Skill incomplete if missing** any of the bullets above.
@@ -244,6 +244,8 @@ End with **exactly** these headings (markdown `###`):
244
244
  - **Handoff:** `/ark-…` / CLI / `none`
245
245
  - **Incomplete?** `no` | `yes — <what is missing>`
246
246
 
247
+ When `doctor.productHonesty.finished` is `false`, **Incomplete? no** is disallowed. Write `yes — <pilotLoop.extractionCard.move or residual>`.
248
+
247
249
  Prefer **Incomplete?** `yes` if the showcase report was generated but the browser-open step was
248
250
  skipped without CI/headless/user-opt-out reason.
249
251
 
@@ -355,8 +355,8 @@ stays in the same layer and the same slice id. If no governed destination keeps
355
355
  `nextPilot` is null — do not invent a folder outside the contract. Success is a smaller
356
356
  cluster with the layer and slice id unchanged and the card still proposed; a green gate is
357
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.
358
+ judgment card as `pilotLoop.nextPilot`. It does not start the pattern-bet apply
359
+ loop. Apply that one card, then re-doctor.
360
360
 
361
361
  ### C. Coupling & debt (measure, don’t guess)
362
362
  Prefer evidence over aesthetics:
@@ -534,5 +534,7 @@ End with **exactly** these headings (markdown `###`):
534
534
  - **Handoff:** `/ark-…` / CLI / `none`
535
535
  - **Incomplete?** `no` | `yes — <what is missing>`
536
536
 
537
+ When `doctor.productHonesty.finished` is `false`, **Incomplete? no** is disallowed. Write `yes — <pilotLoop.extractionCard.move or residual>`.
538
+
537
539
  If a **STOP** handoff applies and you continued as if done, set **Incomplete?** to `yes`.
538
540
  **Skill incomplete if missing** any of the bullets above.
@@ -82,3 +82,5 @@ Skill incomplete if missing any field below.
82
82
  - **Compass:** top residual lenses | `n/a`
83
83
  - **Handoff:** `/ark-autopilot` / `/ark-adopt` / `none`
84
84
  - **Incomplete?** `no` | `yes — <what is missing>`
85
+
86
+ When `doctor.productHonesty.finished` is `false`, **Incomplete? no** is disallowed. Write `yes — <pilotLoop.extractionCard.move or residual>`.
@@ -77,3 +77,5 @@ Skill incomplete if missing any field below.
77
77
  - **Compass:** top residual lenses | `n/a`
78
78
  - **Handoff:** `/ark-autopilot` / `/ark-explore` / `none`
79
79
  - **Incomplete?** `no` | `yes — <what is missing>`
80
+
81
+ When `doctor.productHonesty.finished` is `false`, **Incomplete? no** is disallowed. Write `yes — <pilotLoop.extractionCard.move or residual>`.
@@ -196,5 +196,7 @@ End with **exactly** these headings (markdown `###`):
196
196
  - **Handoff:** `/ark-…` / CLI / `none`
197
197
  - **Incomplete?** `no` | `yes — <what is missing>`
198
198
 
199
+ When `doctor.productHonesty.finished` is `false`, **Incomplete? no** is disallowed. Write `yes — <pilotLoop.extractionCard.move or residual>`.
200
+
199
201
  If a **STOP** handoff applies and you continued as if done, set **Incomplete?** to `yes`.
200
202
  **Skill incomplete if missing** any of the bullets above.
@@ -317,5 +317,7 @@ End with **exactly** these headings (markdown `###`):
317
317
  - **Handoff:** `/ark-…` / CLI / `none`
318
318
  - **Incomplete?** `no` | `yes — <what is missing>`
319
319
 
320
+ When `doctor.productHonesty.finished` is `false`, **Incomplete? no** is disallowed. Write `yes — <pilotLoop.extractionCard.move or residual>`.
321
+
320
322
  If a **STOP** handoff applies and you continued as if done, set **Incomplete?** to `yes`.
321
323
  **Skill incomplete if missing** any of the bullets above.
@@ -180,5 +180,7 @@ End with **exactly** these headings (markdown `###`):
180
180
  - **Handoff:** `/ark-…` / CLI / `none`
181
181
  - **Incomplete?** `no` | `yes — <what is missing>`
182
182
 
183
+ When `doctor.productHonesty.finished` is `false`, **Incomplete? no** is disallowed. Write `yes — <pilotLoop.extractionCard.move or residual>`.
184
+
183
185
  If a **STOP** handoff applies and you continued as if done, set **Incomplete?** to `yes`.
184
186
  **Skill incomplete if missing** any of the bullets above.
@@ -66,3 +66,5 @@ Skill incomplete if missing any field below.
66
66
  - **Compass:** top residual lenses | `n/a`
67
67
  - **Handoff:** `/ark-explore` / `/ark-autopilot` / `none`
68
68
  - **Incomplete?** `no` | `yes — <what is missing>`
69
+
70
+ When `doctor.productHonesty.finished` is `false`, **Incomplete? no** is disallowed. Write `yes — <pilotLoop.extractionCard.move or residual>`.
@@ -40,6 +40,8 @@ Invoking this skill **is** the approval. Preview, then `--apply` **in this turn*
40
40
  Stopping at preview is incomplete unless the probe failed or a conflict needs
41
41
  `--accept-conflicts` / `--refresh-skills` consent.
42
42
 
43
+ If post-upgrade verification is red: **STOP**. Do not edit tests, titles, or config in the upgrade turn. Report `failing[]` and `behaviorChanges[]` and hand off. Incomplete? yes.
44
+
43
45
  For session 0, start with `/ark-adopt` (or `ark-check --recommend`); brownfield
44
46
  honesty is `/ark-adopt` before this upgrade flow.
45
47
 
@@ -171,7 +173,11 @@ ArkGate has **always-on Layers** plus opt-in extras. The user chooses extras; yo
171
173
  `--plan-digest <sha256:…>` and applies only that exact candidate. A changed file
172
174
  or selection invalidates the digest instead of being overwritten.
173
175
  - Missing or conflicted assets previously recorded as managed require explicit
174
- `--accept-conflicts`. Stop and obtain user consent before using it.
176
+ `--accept-conflicts`, unless the missing file is gitignored (`absent-local`,
177
+ for example a per-machine `.mcp.json` in a fresh worktree). That file is
178
+ recreated without consent. Stop and obtain user consent before using
179
+ `--accept-conflicts` on a real deletion or conflict. Every blocked asset
180
+ carries `reason` and `nextCommand`.
175
181
  - Customized files are preserved. Unrelated source files and similar filenames
176
182
  are never adopted. The command never writes a Codex home or another global
177
183
  directory implicitly.
@@ -184,7 +190,8 @@ ArkGate has **always-on Layers** plus opt-in extras. The user chooses extras; yo
184
190
  |---|---|---|
185
191
  | `current` | Content identity matches the candidate. | Record/adopt safely; metadata-only stamps may refresh. |
186
192
  | `stale` | Recorded managed content still matches its old identity. | Safe candidate replacement. |
187
- | `missing` | Candidate is absent. | Create if new; require consent if a recorded asset was deleted. |
193
+ | `missing` | Candidate is absent and is not a gitignored local file. | Create if new; require consent if a recorded asset was deleted. Read `reason` and `nextCommand`. |
194
+ | `absent-local` | Recorded file is missing and gitignored (per-machine file in a fresh worktree). | Recreate it. No consent. |
188
195
  | `customized` | User content diverged without a competing managed base. | Preserve it. Opt-in rewrite for **skills only**: `--refresh-skills`. |
189
196
  | `conflicted` | Both managed base and user content diverged. | Preserve and require explicit consent (`--accept-conflicts`). |
190
197
  | `retired` | A recorded asset is no longer selected by the candidate. | Preserve its file and manifest identity; take no action. |
@@ -285,16 +292,19 @@ ArkGate has **always-on Layers** plus opt-in extras. The user chooses extras; yo
285
292
  (unless more deliberate refreshes remain).
286
293
 
287
294
  5. **Verify enforcement and architecture (post-upgrade checks).** Read apply JSON
288
- `postUpgradeChecks` when present. Also run:
295
+ `postUpgrade` (`verdict`, `failing`, `behaviorChanges`) and `postUpgradeChecks` when present.
296
+ Exit 3 means the files were applied and the architecture check is red. Also run:
289
297
  `npx arkgate-check --doctor --json` (or the project-local `ark-check`) and
290
298
  the same fail-closed architecture command used by managed apply (normally
291
299
  `npx arkgate-check --root . --config ark.config.json --strict-merge --json`).
292
- Require `completeness: "complete"` and `ok: true`. Confirm `doctor.improvementCompass` and
300
+ Require `completeness: "complete"` and `ok: true` before calling the upgrade done.
301
+ If post-upgrade verification is red: **STOP**. Do not edit tests, titles, or config in the upgrade turn. Report `failing[]` and `behaviorChanges[]` and hand off. Incomplete? yes.
302
+ Handoff is `/ark-autopilot` after the report; do not regenerate a baseline without
303
+ explicit approval. Confirm `doctor.improvementCompass` and
293
304
  `doctor.deepModuleCoach` honesty. Run `npx arkgate agents-md --check` and
294
305
  `npx arkgate status --json`. If MCP was used, restart MCP after package bump and re-bind
295
306
  identity. Treat provider-unavailable CI required-check evidence as `unverified`, never as proof
296
- that merges are blocked. If new violations appear, hand off to `/ark-autopilot`; do not regenerate a baseline without
297
- explicit approval.
307
+ that merges are blocked.
298
308
 
299
309
  ## Active host vs deferred hosts
300
310
 
@@ -332,5 +342,8 @@ End with exactly this structure:
332
342
  - **Handoff:** `/ark-…`, CLI action, or `none`
333
343
  - **Incomplete?** `no` or `yes — <missing work>`
334
344
 
345
+ When `doctor.productHonesty.finished` is `false`, **Incomplete? no** is disallowed. Write `yes — <pilotLoop.extractionCard.move or residual>`.
346
+
335
347
  If a required verification did not run or a conflict remains blocked, report the
336
- task incomplete. Deferred hosts (including Codex when inactive) never make Incomplete? yes.
348
+ task incomplete. If post-upgrade verification is red: **STOP**. Do not edit tests, titles, or config in the upgrade turn. Report `failing[]` and `behaviorChanges[]` and hand off. Incomplete? yes.
349
+ Deferred hosts (including Codex when inactive) never make Incomplete? yes.