arkgate 4.4.0 → 4.5.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/CHANGELOG.md +71 -2
  2. package/README.md +7 -4
  3. package/bin/ark-check-runtime.mjs +38 -13
  4. package/bin/ark-layer-match.mjs +25 -12
  5. package/bin/lib/adapter-contract.mjs +5 -5
  6. package/bin/lib/analysis-engine.mjs +5 -5
  7. package/bin/lib/ci-and-commands.mjs +5 -0
  8. package/bin/lib/deep-module-coach.mjs +177 -0
  9. package/bin/lib/deepening-coach.mjs +177 -0
  10. package/bin/lib/doctor-plan.mjs +14 -0
  11. package/bin/lib/html-report-advisories.mjs +33 -0
  12. package/bin/lib/html-report-depth.mjs +9 -0
  13. package/bin/lib/html-report.mjs +8 -1
  14. package/bin/lib/improvement-compass-map.mjs +507 -0
  15. package/bin/lib/improvement-compass-types.mjs +85 -0
  16. package/bin/lib/improvement-compass.mjs +10 -561
  17. package/bin/lib/managed-upgrade-honesty.mjs +201 -0
  18. package/bin/lib/managed-upgrade.mjs +54 -4
  19. package/bin/lib/remediation.mjs +5 -5
  20. package/bin/lib/status-command.mjs +127 -2
  21. package/bin/lib/status-manifest.mjs +163 -14
  22. package/bin/lib/upgrade-whats-new.mjs +110 -0
  23. package/dist/eslint/index.cjs +2 -2
  24. package/dist/eslint/index.js +2 -2
  25. package/dist/index.cjs +28 -28
  26. package/dist/index.d.ts +126 -21
  27. package/dist/index.js +28 -28
  28. package/docs/README.md +5 -5
  29. package/docs/agent-guide.md +50 -8
  30. package/docs/brownfield-adoption.md +12 -0
  31. package/docs/develop.md +3 -1
  32. package/docs/package-surface.md +8 -6
  33. package/docs/product-voice.md +25 -1
  34. package/docs/use.md +33 -0
  35. package/package.json +1 -1
  36. package/schemas/ark.status-manifest.schema.json +28 -1
  37. package/server.json +2 -2
  38. package/templates/agent-skills/README.md +2 -2
  39. package/templates/agent-skills/ark-adopt/SKILL.md +13 -0
  40. package/templates/agent-skills/ark-explore/SKILL.md +21 -0
  41. package/templates/agent-skills/ark-fix/SKILL.md +7 -0
  42. package/templates/agent-skills/ark-loop/SKILL.md +7 -0
  43. package/templates/agent-skills/ark-place/SKILL.md +7 -0
  44. package/templates/agent-skills/ark-think/SKILL.md +7 -0
  45. package/templates/agent-skills/ark-upgrade/SKILL.md +14 -0
  46. package/templates/skills/ark-adopt.md +13 -0
  47. package/templates/skills/ark-explore.md +21 -0
  48. package/templates/skills/ark-fix.md +7 -0
  49. package/templates/skills/ark-loop.md +7 -0
  50. package/templates/skills/ark-place.md +7 -0
  51. package/templates/skills/ark-think.md +7 -0
  52. package/templates/skills/ark-upgrade.md +14 -0
package/CHANGELOG.md CHANGED
@@ -5,13 +5,82 @@ in the immutable pre-2.0 archive linked below.
5
5
 
6
6
  ## Unreleased
7
7
 
8
+ ## 4.5.5 — 2026-08-11
9
+
10
+ **Patch** over **4.5.0**. Deep-module coach train plus upgrade **suggested improvements /
11
+ what’s new** so consumers know what to try after install. **No required config migration.**
12
+ No new skill names, sensors, or scores. Freezes held.
13
+
14
+ **Status: prepared** (implementation only; not yet on npm `latest` — remains `4.5.0` until
15
+ the publication checklist completes).
16
+
17
+ ### Added
18
+
19
+ - **Deep-module coach (advisory):** `ark-check --doctor` JSON `doctor.deepModuleCoach` + human
20
+ section + HTML `data-advisory="deepModuleCoach"`. Hot paths from bounded git history
21
+ (`unavailable` + empty when history missing — never invent). Deepening candidates projected
22
+ only from existing design smells / physical cohesion / pilot / residual compass evidence —
23
+ empty when no evidence. Always `notAScore: true`; never flips gate verdicts. Pure helper
24
+ `buildDeepeningCandidates` (+ CLI gen mirror).
25
+ - **Product voice + skills:** lexicon for depth / seam / deletion test / test-at-public-interface
26
+ / domain glossary / two-axis done. Existing skills (explore, think, place, fix, loop, adopt)
27
+ deepened; agent-skills 1:1. Compact router + agent-guide **two-axis done** (Enforce green ≠
28
+ feature done). Remediation port/inject `nextAction` prose includes test at the public interface.
29
+ - **Upgrade suggested improvements / what’s new:** `ark upgrade` JSON `whatsNew` + human block
30
+ lists concrete try/inspect actions (deep-module coach, improvement compass, session/status
31
+ honesty, two-axis done, self-service honesty). Always `notAScore`; never a gate input.
32
+
33
+ ## 4.5.0 — 2026-08-10
34
+
35
+ **Minor** over 4.4.0. Session control-plane honesty (status/MCP improvement compass with explicit
36
+ modes), domain module fitness (split oversized pure modules), selective pure verification ratchet,
37
+ self-service upgrade activation honesty, and a short session recipe in product docs. **No required
38
+ config migration.** Codex remains advisory at write time. Skills and AGENTS projection never
39
+ enforce. **Status: published** (`arkgate@4.5.0` on npm `latest`; see `docs/releases/4.5.0.md`).
40
+
41
+ ### Added
42
+
43
+ - **Status / MCP improvement compass honesty:** `ark status --json` and MCP `ark_status` always
44
+ project `improvementCompass` with explicit mode `full` \| `subset` \| `unavailable`, residual
45
+ lens ids, and `notAScore: true`. Incomplete or missing session facts never invent green residual.
46
+ When mode is `full`, residual ids are a subset of doctor residual for the same facts (report
47
+ snapshots store the thin slice). Residual never flips `valid` / strict-merge / `goal.met`.
48
+ Root helpers: `projectStatusImprovementCompass`, `STATUS_COMPASS_MODES`.
49
+ - **Session recipe (product docs):** identity bind → `ark status` → act on residual / `findingRef`;
50
+ run doctor when status compass mode is not `full`. Documented in use / agent-guide / develop —
51
+ no new skill names.
52
+ - **Managed upgrade self-service honesty:** `ark upgrade [--json]` surfaces `selfService` with
53
+ per-host write-path activation labels (`hard` \| `advisory` \| `unavailable`) and customized
54
+ content-identity preserve (`customizedPaths` / `customizedContentPreserved`). Soft hosts never
55
+ hard; upgrade never invents `hardWriteActive` from disk alone. Always `notAScore: true`. Human
56
+ output: “Self-service honesty” lines.
57
+
58
+ ### Changed
59
+
60
+ - **Domain module fitness:** pure Domain `improvementCompass` monolith split into
61
+ `improvementCompassTypes` + `improvementCompassMap` + facade `improvementCompass` (public
62
+ re-exports unchanged). All three tracked in `check:module-budgets`; over-ceiling domain modules
63
+ require a behavior-preserving split — raising max alone is not done. CLI pure mirrors:
64
+ `improvement-compass-types.mjs`, `improvement-compass-map.mjs`, `improvement-compass.mjs`.
65
+ - **Critical pure verification ratchet:** selective verification islands on
66
+ `layerMatch` peerIsolation fail-closed (missing paths/slices),
67
+ `policyDeltaAcknowledgementMatches` (hash + finding-id set), and
68
+ `canPromoteInvariant` promote honesty. Property suites under `tests/property/` plus unit fixtures;
69
+ Stryker mutate ranges + critical groups `peer-isolation-fail-closed`,
70
+ `policy-delta-ack-match`, `invariant-promote-honesty`. Mutation remains cost-gated to full-matrix
71
+ CI / publish (`npm run test:mutation` via `test:confidence`) — **not** a whole-repo mutation claim.
72
+ Strict semantics unchanged.
73
+
74
+ **Freezes held:** no principle scores/ranks; no new skill names; no new ArkRules sensors; no LLM
75
+ verdict; projection never enforces; no false hard-write for soft hosts.
76
+
8
77
  ## 4.4.0 — 2026-08-09
9
78
 
10
79
  **Minor** over 4.3.0. Improvement compass (architecture lenses as a `notAScore` projection of
11
80
  existing sensors), doctor/HTML residual coaching, deeper skills + compact router for vibe-coder
12
81
  and full-AI workflows, and product-only public docs. **No required config migration.** Codex remains
13
- advisory at write time. Skills and AGENTS projection never enforce. **Status: prepared in tree —
14
- not published on npm until OIDC publish + `npm view` succeed** (see `docs/releases/4.4.0.md`).
82
+ advisory at write time. Skills and AGENTS projection never enforce. **Status: published**
83
+ (`arkgate@4.4.0` on npm `latest`; see `docs/releases/4.4.0.md`).
15
84
 
16
85
  ### Added
17
86
 
package/README.md CHANGED
@@ -16,8 +16,9 @@ and makes sure a “green” check means something real.
16
16
 
17
17
  </div>
18
18
 
19
- > **ArkGate 4.3.0** is on npm `latest`. Tree is preparing **4.4.0** (improvement compass) see [4.4.0 notes](docs/releases/4.4.0.md) (Status: prepared until publish).
20
- > [4.4.0 notes](docs/releases/4.4.0.md) · [4.3.0](docs/releases/4.3.0.md) · [4.2.1](docs/releases/4.2.1.md) · [Docs hub](docs/README.md) · [Product voice](docs/product-voice.md)
19
+ > **ArkGate 4.5.5** is **prepared** (deep-module coach + upgrade what’s new). npm `latest` remains
20
+ > **4.5.0** until publish. [4.5.5 notes](docs/releases/4.5.5.md) · [4.5.0](docs/releases/4.5.0.md) ·
21
+ > [4.4.0](docs/releases/4.4.0.md) · [Docs hub](docs/README.md) · [Product voice](docs/product-voice.md)
21
22
 
22
23
  ---
23
24
 
@@ -208,8 +209,10 @@ for real systems. Details: [docs/production-hardening.md](docs/production-harden
208
209
  | Config · package surface · TS | [configuration](docs/configuration.md) · [package-surface](docs/package-surface.md) · [typescript-support](docs/typescript-support.md) |
209
210
  | Brownfield | [docs/brownfield-adoption.md](docs/brownfield-adoption.md) |
210
211
  | Security | [SECURITY.md](SECURITY.md) |
211
- | Current release (4.3.0 on npm `latest`) | [docs/releases/4.3.0.md](docs/releases/4.3.0.md) · [CHANGELOG](CHANGELOG.md) |
212
- | Next prepare (4.4.0) | [docs/releases/4.4.0.md](docs/releases/4.4.0.md) (prepared — not published until npm verify) |
212
+ | Prepared (4.5.5) | [docs/releases/4.5.5.md](docs/releases/4.5.5.md) · [CHANGELOG](CHANGELOG.md) |
213
+ | Current published (4.5.0 on npm `latest`) | [docs/releases/4.5.0.md](docs/releases/4.5.0.md) |
214
+ | Prior (4.4.0) | [docs/releases/4.4.0.md](docs/releases/4.4.0.md) |
215
+ | Prior (4.3.0) | [docs/releases/4.3.0.md](docs/releases/4.3.0.md) |
213
216
  | Prior (4.2.1) | [docs/releases/4.2.1.md](docs/releases/4.2.1.md) |
214
217
  | Previous (4.2.0) | [docs/releases/4.2.0.md](docs/releases/4.2.0.md) |
215
218
  | Previous (4.1.1) | [docs/releases/4.1.1.md](docs/releases/4.1.1.md) |
@@ -1557,19 +1557,6 @@ async function main() {
1557
1557
  enforcement: enforcementForReport,
1558
1558
  config,
1559
1559
  });
1560
- const currentSnapshot = buildReportSnapshot({
1561
- root,
1562
- config,
1563
- coverage,
1564
- violations: activeViolations,
1565
- ok,
1566
- suppressed: suppressed.length,
1567
- version: arkPackageVersion(),
1568
- fileCountByLayer,
1569
- enforcement: enforcementForReport,
1570
- score: fitness.score,
1571
- mode: fitness.mode,
1572
- });
1573
1560
  // Origin is read before archive so the HTML can show "just created" vs deltas.
1574
1561
  const existingOrigin = args.resetOrigin
1575
1562
  ? null
@@ -1599,6 +1586,40 @@ async function main() {
1599
1586
  baselineStale: analysisComplete ? reportBaselineStale : null,
1600
1587
  }
1601
1588
  );
1589
+ // DF02 — thin status compass on report snapshot so `ark status` residual ⊆ doctor.
1590
+ const reportCompass =
1591
+ designDepth?.improvementCompass && designDepth.improvementCompass.notAScore === true
1592
+ ? {
1593
+ schemaVersion: '1.0',
1594
+ notAScore: true,
1595
+ mode: analysisComplete ? 'full' : 'subset',
1596
+ topResidual: Array.isArray(designDepth.improvementCompass.topResidual)
1597
+ ? designDepth.improvementCompass.topResidual
1598
+ : [],
1599
+ factsSource: 'report-snapshot',
1600
+ ...(analysisComplete
1601
+ ? {}
1602
+ : {
1603
+ reasonCode: 'FACTS_PARTIAL',
1604
+ reason:
1605
+ 'Report analysis was incomplete — status compass is a subset; re-run doctor/report for full residual.',
1606
+ }),
1607
+ }
1608
+ : null;
1609
+ const currentSnapshot = buildReportSnapshot({
1610
+ root,
1611
+ config,
1612
+ coverage,
1613
+ violations: activeViolations,
1614
+ ok,
1615
+ suppressed: suppressed.length,
1616
+ version: arkPackageVersion(),
1617
+ fileCountByLayer,
1618
+ enforcement: enforcementForReport,
1619
+ score: fitness.score,
1620
+ mode: fitness.mode,
1621
+ improvementCompass: reportCompass,
1622
+ });
1602
1623
  const reportPayload = {
1603
1624
  root,
1604
1625
  config,
@@ -1623,6 +1644,10 @@ async function main() {
1623
1644
  ...(designDepth?.improvementCompass
1624
1645
  ? { improvementCompass: designDepth.improvementCompass }
1625
1646
  : {}),
1647
+ // Doctor parity: deep-module coach advisory (hot paths + deepening; notAScore).
1648
+ ...(designDepth?.deepModuleCoach
1649
+ ? { deepModuleCoach: designDepth.deepModuleCoach }
1650
+ : {}),
1626
1651
  },
1627
1652
  };
1628
1653
  const html = args.beginner
@@ -258,6 +258,21 @@ function resolveSliceFolders(rule, layerName, layers) {
258
258
  const layer = (layers ?? []).find((l) => l.name === layerName);
259
259
  return inferSliceFoldersFromPatterns(layer?.patterns);
260
260
  }
261
+ /**
262
+ * PeerIsolation deny decision given resolved path/slice evidence (DF04 pure core).
263
+ *
264
+ * Fail-closed: missing path, no classifiable folders, or unclassifiable either
265
+ * side → deny. Same-slice → allow (return false). Cross-slice → deny.
266
+ */
267
+ export function peerIsolationMustDeny(input) {
268
+ if (!input.fromPath || !input.toPath)
269
+ return true;
270
+ if (input.folderCount <= 0)
271
+ return true;
272
+ if (!input.fromSlice || !input.toSlice)
273
+ return true;
274
+ return input.fromSlice !== input.toSlice;
275
+ }
261
276
  /**
262
277
  * Find the first denying rule for a layer edge.
263
278
  *
@@ -279,20 +294,18 @@ export function findDeniedEdgeRule(rules, from, to, options) {
279
294
  if (rule.peerIsolation) {
280
295
  const fromPath = options?.fromPath;
281
296
  const toPath = options?.toPath;
282
- // Isolation is active: without both paths we cannot prove same-slice.
283
- if (!fromPath || !toPath)
284
- return rule;
285
297
  const folders = resolveSliceFolders(rule, from, options?.layers);
286
- // Configured isolation without classifiable folders cannot allow.
287
- if (folders.length === 0)
288
- return rule;
289
- const fromSlice = sliceIdForPath(fromPath, folders);
290
- const toSlice = sliceIdForPath(toPath, folders);
291
- // Unclassifiable either side: cannot prove same-slice → deny.
292
- if (!fromSlice || !toSlice)
293
- return rule;
294
- if (fromSlice !== toSlice)
298
+ const fromSlice = fromPath && toPath ? sliceIdForPath(fromPath, folders) : undefined;
299
+ const toSlice = fromPath && toPath ? sliceIdForPath(toPath, folders) : undefined;
300
+ if (peerIsolationMustDeny({
301
+ fromPath,
302
+ toPath,
303
+ folderCount: folders.length,
304
+ fromSlice,
305
+ toSlice,
306
+ })) {
295
307
  return rule;
308
+ }
296
309
  continue; // same slice: this peerIsolation rule does not deny
297
310
  }
298
311
  // Classic deny — same-layer always allowed without peerIsolation
@@ -82,18 +82,18 @@ function nextActionForDiagnostic(ruleId, evidence, violation) {
82
82
  return 'Move the referenced type to a mutually allowed layer, use `import type`, then preflight again.';
83
83
  }
84
84
  if (violation.peerIsolation === true) {
85
- return 'Extract the shared dependency to a shared layer, then preflight again.';
85
+ return 'Extract the shared dependency to a shared layer, test at the public interface, then preflight again.';
86
86
  }
87
- return `Define a port in ${evidence.fromLayer ?? 'the source layer'}, inject the ${evidence.toLayer ?? 'outer-layer'} implementation, then preflight again.`;
87
+ return `Define a port in ${evidence.fromLayer ?? 'the source layer'}, inject the ${evidence.toLayer ?? 'outer-layer'} implementation, test at the public interface, then preflight again.`;
88
88
  }
89
89
  if (ruleId === 'FORBIDDEN_GLOBAL') {
90
- return `Inject ${evidence.target ?? 'the capability'} through a port, then preflight again.`;
90
+ return `Inject ${evidence.target ?? 'the capability'} through a port, test at the public interface, then preflight again.`;
91
91
  }
92
92
  if (ruleId === 'CAPABILITY_VIOLATION') {
93
- return `Define a ${text(violation.capability) ?? 'capability'} port in ${evidence.fromLayer ?? 'the walled layer'}, bind the implementation outside it, then preflight again.`;
93
+ return `Define a ${text(violation.capability) ?? 'capability'} port in ${evidence.fromLayer ?? 'the walled layer'}, bind the implementation outside it, test at the public interface, then preflight again.`;
94
94
  }
95
95
  if (ruleId === 'CIRCULAR_DEPENDENCY') {
96
- return 'Extract the shared dependency into a third module, then preflight again.';
96
+ return 'Extract the shared dependency into a third module, test at the public interface, then preflight again.';
97
97
  }
98
98
  if (ruleId === 'RAW_EVENT_PUBLISH')
99
99
  return 'Publish through a registered intent creator, then run Ark again.';