arkgate 4.8.8 → 4.8.9

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 (47) hide show
  1. package/CHANGELOG.md +64 -3
  2. package/README.md +9 -6
  3. package/bin/ark-check-runtime.mjs +22 -0
  4. package/bin/ark-layer-match.mjs +25 -10
  5. package/bin/lib/agent-homes.mjs +1 -1
  6. package/bin/lib/analysis-engine.mjs +8 -8
  7. package/bin/lib/ark-order-sensors.mjs +103 -3
  8. package/bin/lib/config-extras.mjs +1 -0
  9. package/bin/lib/contract-smells.mjs +12 -6
  10. package/bin/lib/doctor-human.mjs +32 -7
  11. package/bin/lib/doctor-next-actions.mjs +21 -2
  12. package/bin/lib/gate-files.mjs +108 -22
  13. package/bin/lib/managed-upgrade.mjs +9 -1
  14. package/bin/lib/upgrade-command.mjs +17 -4
  15. package/dist/{configTypes-0eHpocR3.d.ts → configTypes-j7so8B4O.d.ts} +12 -0
  16. package/dist/{diagnosticCatalog-DxKCTBbp.d.ts → diagnosticCatalog-BrkOiwCk.d.ts} +3 -3
  17. package/dist/eslint/index.cjs +5 -5
  18. package/dist/eslint/index.d.ts +6 -4
  19. package/dist/eslint/index.js +5 -5
  20. package/dist/index.cjs +30 -30
  21. package/dist/index.d.ts +13 -4
  22. package/dist/index.js +31 -31
  23. package/dist/nestjs/index.cjs +3 -3
  24. package/dist/nestjs/index.d.ts +3 -3
  25. package/dist/nestjs/index.js +2 -2
  26. package/dist/order/index.cjs +1 -1
  27. package/dist/order/index.d.ts +6 -2
  28. package/dist/order/index.js +1 -1
  29. package/dist/runtime/index.cjs +11 -11
  30. package/dist/runtime/index.d.ts +6 -6
  31. package/dist/runtime/index.js +11 -11
  32. package/dist/{types-BK47clMl.d.ts → types-Djbs3KjE.d.ts} +1 -1
  33. package/dist/{types-DxvmJO-D.d.ts → types-tGhZUiGX.d.ts} +1 -1
  34. package/docs/README.md +4 -3
  35. package/docs/agent-guide.md +2 -1
  36. package/docs/arkorder.md +30 -7
  37. package/docs/configuration.md +47 -13
  38. package/docs/develop.md +4 -2
  39. package/docs/package-surface.md +5 -4
  40. package/package.json +1 -1
  41. package/schemas/ark.config.schema.json +9 -0
  42. package/server.json +2 -2
  43. package/templates/agent-skills/README.md +1 -1
  44. package/templates/agent-skills/ark-contract/SKILL.md +1 -1
  45. package/templates/agent-skills/ark-explore/SKILL.md +2 -2
  46. package/templates/skills/ark-contract.md +1 -1
  47. package/templates/skills/ark-explore.md +2 -2
package/CHANGELOG.md CHANGED
@@ -3,6 +3,68 @@
3
3
  All notable changes to ArkGate (`arkgate`; formerly `ark-runtime-kernel`) are documented here or
4
4
  in the immutable pre-2.0 archive linked below.
5
5
 
6
+ ## 4.8.9 — 2026-09-01
7
+
8
+ **Patch** over **4.8.8**. Field diagnostic honesty: doctor default homedir, live
9
+ `peerIsolation` walls are not dead rules, skippable CI is `ci-not-fail-closed`
10
+ not `missing-gates`, blocked upgrade JSON carries `reasonCode` + `nextCommand`,
11
+ content identity vs raw `afterHash`, cross-layer `peerIsolation` docs, and
12
+ intent-reference checks share the import classifier. Amarilla ArkOrder:
13
+ `appliesTo`, watchlist vs `maxXiKeys`, `restore`, default clock, `hashOf`,
14
+ `.d.ts` honesty, packaged example URL, and ingest-payload honesty.
15
+ **No required config migration.** No `ark.config.json` schema bump. Does not close `K01` / `Z09`.
16
+ No new skill names.
17
+
18
+ **Status: prepared** (tree candidate; npm `latest` remains **4.8.8** until the
19
+ signed-tag provenance workflow succeeds).
20
+
21
+ ### Added
22
+ - Optional `arkOrder.appliesTo` globs (same engine as layers). `managedLayers`
23
+ still required; empty or absent `appliesTo` keeps today's all-files-in-layer
24
+ emit; a Persistence write outside the globs stays silent.
25
+ - `OrderPlane.restore(release)` installs a previously frozen `Release` (current
26
+ + version). Hash remains the identity. Not durable. Does not close `K01`.
27
+ - Public `arkgate/order` exports `hashOf` (alias of `hashReleasePayload`) so a
28
+ stored `Release.hash` can be verified without calling `release()`.
29
+
30
+ ### Fixed
31
+ - `--doctor` no longer throws when `CLAUDE_HOME` / `GROK_HOME` / `ANTIGRAVITY_HOME`
32
+ are unset (`skillsDirFor` now defaults `os.homedir()`).
33
+ - `contract-dead-rule` skips a live `peerIsolation` wall (`allowed: false` on a
34
+ same-layer or cross-layer pair). A self-allow stays dead. The fix text never
35
+ tells an agent to delete that wall.
36
+ - `--strict-merge` / `--require-gates` still fail skippable CI (`if:` other than
37
+ `true` / `always()`), but as `ci-not-fail-closed` (names the workflow file).
38
+ `missing-gates` remains only for files that are actually absent.
39
+ - Blocked `ark upgrade --apply --json` includes `reasonCode: managed-consent-required`
40
+ and a digest-bound `nextCommand` already carrying `--accept-conflicts`. Apply stays
41
+ atomic.
42
+ - `LAYER_INTENT_REFERENCE_VIOLATION` uses the same `peerIsolation` classifier as
43
+ imports (`fromPath` + `sharedRoots`). Shared-root files do not inherit the
44
+ slice-wall message. Cross-slice file imports still deny.
45
+ - Default `createOrderPlane` clock is Kernel `Date.now()` (was `0` with no
46
+ warning). Domain stays clock-free. Tests that need determinism still inject.
47
+
48
+ ### Changed
49
+ - Public managed-upgrade assets expose `beforeIdentity` / `afterIdentity`.
50
+ `afterHash` is the SHA-256 of the raw desired bytes; `state: current` is content
51
+ identity, so stamp-only `arkVersion` drift can be current with unequal raw hashes.
52
+ - Docs: a slice wall on a cross-layer edge is a `peerIsolation` rule on that
53
+ `from`/`to` pair (`allowed: false`). Domain files without a slice fail-closed
54
+ unless listed in `sharedRoots`. No new engine mode, key, or skill.
55
+ - `maxXiKeys` caps one `release()` / `assertXiKeyCap` only. `xiKeys` is a
56
+ repo-wide watchlist; eight named keys with `maxXiKeys` 7 is valid config.
57
+ Sensor `arkorder-too-many-params` fires on `release()` `keyCount`, not on
58
+ config list length.
59
+ - Public `dist/order` `.d.ts` no longer says declarations-only / no runtime.
60
+ - Packaged docs point the billing gallery at the GitHub tree
61
+ (`https://github.com/pedroknigge/arkgate/tree/main/examples/arkorder-billing`).
62
+ The folder is not in the npm tarball; `examples/` is not added to
63
+ `package.json` `files`.
64
+ - Ingest payload-dependent escalation stays deliberate: `classifyIngest` uses
65
+ `kind`; `ConstraintPack` forbids user predicates. "Second week failing a
66
+ goal" is a domain/projector kind, not a pack function.
67
+
6
68
  ## 4.8.8 — 2026-09-01
7
69
 
8
70
  **Patch** over **4.8.7**. ArkRun gains an opt-in terminal dashboard and bounded,
@@ -10,8 +72,7 @@ payload-free inspector monitors. ArkOrder and first-contact docs gain honesty co
10
72
  **No required config migration.** No `ark.config.json` schema bump. Does not close
11
73
  `K01` / `Z09`.
12
74
 
13
- **Status: prepared** (tree candidate; npm `latest` remains **4.8.7** until the
14
- signed-tag provenance workflow succeeds).
75
+ **Status: published** (on npm `latest`; see `docs/releases/4.8.8.md`).
15
76
 
16
77
  ### Added
17
78
  - Dual **`ark-dashboard` / `arkgate-dashboard`** bins render the loopback ArkRun
@@ -40,7 +101,7 @@ show it when present. Caption-only edits do not change `policyHash`.
40
101
  **No required config migration.** No `schemaVersion` bump. Does not close `K01` / `Z09`.
41
102
  No new skill names.
42
103
 
43
- **Status: published** (on npm `latest`; see `docs/releases/4.8.7.md`).
104
+ **Status: published** (see `docs/releases/4.8.7.md`).
44
105
 
45
106
  ### Added
46
107
  - Public name for existing `layers[].description`. Example:
package/README.md CHANGED
@@ -37,7 +37,7 @@ Works with Cursor, Claude, Codex, and Grok.
37
37
 
38
38
  </div>
39
39
 
40
- > **ArkGate 4.8.8** is prepared on this tree; npm `latest` remains **4.8.7**.
40
+ > **ArkGate 4.8.9** is prepared on this tree; npm `latest` remains **4.8.8**.
41
41
  > Write. Check. Ship. Adopted = required GitHub
42
42
  > status running `arkgate-check --strict-merge`, or an explicit `advisory-only` stance.
43
43
  > Status is compact (`arkgate-check --doctor`; `--all` for Details). Optional **ArkRun**
@@ -45,7 +45,7 @@ Works with Cursor, Claude, Codex, and Grok.
45
45
  > (`arkgate/order`) stops the agent rewriting the few slow product decisions as CRUD;
46
46
  > later pattern change is `proposeRelease` then `apply`.
47
47
  > `@arkgate/runtime` is deprecated.
48
- > [4.8.8 prepared](docs/releases/4.8.8.md) · [4.8.7 published](docs/releases/4.8.7.md) · [4.8.6](docs/releases/4.8.6.md) · [4.8.5](docs/releases/4.8.5.md) · [4.8.4](docs/releases/4.8.4.md) · [4.8.3](docs/releases/4.8.3.md) · [4.8.2](docs/releases/4.8.2.md) · [4.8.1](docs/releases/4.8.1.md) · [4.8.0](docs/releases/4.8.0.md) · [Docs hub](docs/README.md) · [Voice](docs/product-voice.md)
48
+ > [4.8.9 prepared](docs/releases/4.8.9.md) · [4.8.8 published](docs/releases/4.8.8.md) · [4.8.7](docs/releases/4.8.7.md) · [4.8.6](docs/releases/4.8.6.md) · [4.8.5](docs/releases/4.8.5.md) · [4.8.4](docs/releases/4.8.4.md) · [4.8.3](docs/releases/4.8.3.md) · [4.8.2](docs/releases/4.8.2.md) · [4.8.1](docs/releases/4.8.1.md) · [4.8.0](docs/releases/4.8.0.md) · [Docs hub](docs/README.md) · [Voice](docs/product-voice.md)
49
49
 
50
50
  ---
51
51
 
@@ -321,8 +321,10 @@ PATCHes those keys through Prisma is named. Same npm package.
321
321
  In-memory. Not durable. Does not replace ArkRun.
322
322
  **ArkOrder freezes the pattern through a valve. ArkRun is how the residual travels.**
323
323
 
324
- Copy [examples/arkorder-billing/](examples/arkorder-billing/) and rename the
325
- three keys. Compact starters leave it off. Details:
324
+ The billing gallery (`plan` / `cycle` / `tenancy`) lives on GitHub, not in the
325
+ npm tarball:
326
+ [examples/arkorder-billing](https://github.com/pedroknigge/arkgate/tree/main/examples/arkorder-billing).
327
+ Rename the three keys. Compact starters leave it off. Details:
326
328
  [ArkOrder](docs/arkorder.md) · [configuration](docs/configuration.md) ·
327
329
  [package surface](docs/package-surface.md).
328
330
 
@@ -340,8 +342,9 @@ three keys. Compact starters leave it off. Details:
340
342
  | Config · package surface · TS | [configuration](docs/configuration.md) · [package-surface](docs/package-surface.md) · [typescript-support](docs/typescript-support.md) |
341
343
  | Brownfield | [docs/brownfield-adoption.md](docs/brownfield-adoption.md) |
342
344
  | Security | [SECURITY.md](SECURITY.md) |
343
- | Prepared (4.8.8; not published) | [docs/releases/4.8.8.md](docs/releases/4.8.8.md) · [CHANGELOG](CHANGELOG.md) |
344
- | Current published (4.8.7 on npm `latest`) | [docs/releases/4.8.7.md](docs/releases/4.8.7.md) · [CHANGELOG](CHANGELOG.md) |
345
+ | Prepared (4.8.9; not published) | [docs/releases/4.8.9.md](docs/releases/4.8.9.md) · [CHANGELOG](CHANGELOG.md) |
346
+ | Current published (4.8.8 on npm `latest`) | [docs/releases/4.8.8.md](docs/releases/4.8.8.md) · [CHANGELOG](CHANGELOG.md) |
347
+ | Prior published (4.8.7) | [docs/releases/4.8.7.md](docs/releases/4.8.7.md) · [CHANGELOG](CHANGELOG.md) |
345
348
  | Prior published (4.8.6) | [docs/releases/4.8.6.md](docs/releases/4.8.6.md) |
346
349
  | Prior published (4.8.5) | [docs/releases/4.8.5.md](docs/releases/4.8.5.md) |
347
350
  | Prior published (4.8.4) | [docs/releases/4.8.4.md](docs/releases/4.8.4.md) |
@@ -47,6 +47,7 @@ import {
47
47
  REQUIRED_GATE_FILES,
48
48
  detectWritePathCapabilities,
49
49
  } from './lib/agent-gates.mjs';
50
+ import { ciNotFailClosed } from './lib/gate-files.mjs';
50
51
  import { syncBaselineIntoCheckSurfaces } from './lib/field-install.mjs';
51
52
  import {
52
53
  detectEnforcement,
@@ -1307,6 +1308,27 @@ async function main() {
1307
1308
  process.exitCode = 1;
1308
1309
  return;
1309
1310
  }
1311
+ if (args.requireGates) {
1312
+ const ci = ciNotFailClosed(args.root);
1313
+ if (ci) {
1314
+ const payload = {
1315
+ ok: false,
1316
+ error: ci.error,
1317
+ message: ci.message,
1318
+ workflowFile: ci.workflowFile,
1319
+ nextAction: ci.nextAction,
1320
+ ...(writeRequest?.host ? { writeHost: writeRequest.host } : {}),
1321
+ };
1322
+ if (args.json) {
1323
+ console.log(JSON.stringify(payload, null, 2));
1324
+ } else {
1325
+ console.error(`${color.red('\u2716')} ${ci.error} ${ci.message}`);
1326
+ console.error(`Next: ${ci.nextAction}`);
1327
+ }
1328
+ process.exitCode = 1;
1329
+ return;
1330
+ }
1331
+ }
1310
1332
  // Gates present. This is a precondition, not a standalone report: stay quiet
1311
1333
  // in --json mode so the architecture check below owns the single JSON output.
1312
1334
  // When --require-gates is the only intent (no config/architecture run needed),
@@ -362,15 +362,27 @@ export function crossSliceEdgeAllowed(allowedCrossSlice, fromSlice, toSlice) {
362
362
  * Fail-closed stays fail-closed: absent evidence denies. What changed in 4.8.4
363
363
  * is what counts as evidence — a declared shared root, or a declared directed
364
364
  * cross-slice edge, is the repo telling us its design, so it is no longer
365
- * "unclassifiable". Order: no paths → no slice folders → a side that is neither
366
- * in a slice nor declared shared same slice declared cross edge → deny.
365
+ * "unclassifiable". Order: no fromPath → no slice folders → intent (no toPath):
366
+ * shared-root allow / slice fail-closed a side that is neither in a slice nor
367
+ * declared shared → same slice → declared cross edge → deny.
368
+ *
369
+ * Intent/event names are not files. Callers pass fromPath only and must not
370
+ * invent a toPath (that would mis-slice). A declared shared root is classified
371
+ * the same way as on import edges.
367
372
  */
368
373
  export function peerIsolationDecision(input) {
369
- if (!input.fromPath || !input.toPath)
374
+ if (!input.fromPath)
370
375
  return { denied: true, reason: 'missing-path' };
371
376
  if (input.folderCount <= 0)
372
377
  return { denied: true, reason: 'no-slice-folders' };
373
378
  const fromClassified = Boolean(input.fromSlice) || input.fromShared === true;
379
+ if (!input.toPath) {
380
+ if (!fromClassified)
381
+ return { denied: true, reason: 'unclassifiable-path' };
382
+ if (!input.fromSlice)
383
+ return { denied: false };
384
+ return { denied: true, reason: 'missing-path' };
385
+ }
374
386
  const toClassified = Boolean(input.toSlice) || input.toShared === true;
375
387
  if (!fromClassified || !toClassified)
376
388
  return { denied: true, reason: 'unclassifiable-path' };
@@ -387,8 +399,9 @@ export function peerIsolationDecision(input) {
387
399
  /**
388
400
  * Boolean face of {@link peerIsolationDecision}, kept for parity consumers.
389
401
  *
390
- * Fail-closed: missing path, no classifiable folders, or unclassifiable either
391
- * side → deny. Same-slice allow (return false). Cross-slice deny unless the
402
+ * Fail-closed: missing fromPath, no classifiable folders, or unclassifiable
403
+ * either side → deny. Intent refs (no toPath) allow only a declared shared
404
+ * root. Same-slice → allow (return false). Cross-slice → deny unless the
392
405
  * rule declared that directed edge.
393
406
  */
394
407
  export function peerIsolationMustDeny(input) {
@@ -426,9 +439,11 @@ export function peerIsolationDenyExplanation(reason, context) {
426
439
  * Same-layer is always allowed (historical short-circuit).
427
440
  * - `peerIsolation: true` + `allowed: false`: deny only when importer and importee
428
441
  * resolve to **different** slice ids (same or cross layer). Same-slice → allow.
429
- * Missing paths, no slice folders, or unclassifiable slices **fail-closed**
430
- * (deny): isolation is configured, so insufficient evidence must not silently
431
- * allow a possible cross-slice edge.
442
+ * File imports pass fromPath + toPath. Intent references pass fromPath only.
443
+ * A declared shared root is classified (no denial). Missing fromPath, no slice
444
+ * folders, or unclassifiable slices → **fail-closed** (deny): isolation is
445
+ * configured, so insufficient evidence must not silently allow a possible
446
+ * cross-slice edge.
432
447
  */
433
448
  export function findDeniedEdgeRule(rules, from, to, options) {
434
449
  return findDeniedEdgeDecision(rules, from, to, options)?.rule;
@@ -448,8 +463,8 @@ export function findDeniedEdgeDecision(rules, from, to, options) {
448
463
  const fromPath = options?.fromPath;
449
464
  const toPath = options?.toPath;
450
465
  const folders = resolveSliceFolders(rule, from, options?.layers);
451
- const fromSlice = fromPath && toPath ? sliceIdForPath(fromPath, folders) : undefined;
452
- const toSlice = fromPath && toPath ? sliceIdForPath(toPath, folders) : undefined;
466
+ const fromSlice = fromPath ? sliceIdForPath(fromPath, folders) : undefined;
467
+ const toSlice = toPath ? sliceIdForPath(toPath, folders) : undefined;
453
468
  const decision = peerIsolationDecision({
454
469
  fromPath,
455
470
  toPath,
@@ -108,7 +108,7 @@ function usesDefaultHome(spec, env, homeDir) {
108
108
 
109
109
  function skillsDirFor(host, env = process.env) {
110
110
  const spec = HOSTS[host];
111
- return path.join(resolveHomeDir(spec, env), 'skills');
111
+ return path.join(resolveHomeDir(spec, env, os.homedir()), 'skills');
112
112
  }
113
113
 
114
114
  function readHomeCatalogFloor(skillsDir) {