arkgate 4.6.7 → 4.7.1

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 (68) hide show
  1. package/CHANGELOG.md +140 -0
  2. package/README.md +20 -9
  3. package/SECURITY.md +1 -1
  4. package/bin/ark-check-runtime.mjs +13 -1
  5. package/bin/ark-mcp-runtime.mjs +65 -3
  6. package/bin/lib/adapter-contract.mjs +17 -36
  7. package/bin/lib/agent-skills-package.mjs +29 -0
  8. package/bin/lib/analysis-engine.mjs +6 -6
  9. package/bin/lib/ark-run-doctor.mjs +144 -0
  10. package/bin/lib/ark-run-facts.mjs +472 -0
  11. package/bin/lib/ark-run-report.mjs +57 -0
  12. package/bin/lib/ark-run-sensors.mjs +309 -0
  13. package/bin/lib/check-args.mjs +1 -0
  14. package/bin/lib/config-contract.mjs +86 -11
  15. package/bin/lib/diagnostic-catalog.mjs +8 -0
  16. package/bin/lib/doctor-advisories.mjs +45 -8
  17. package/bin/lib/doctor-human.mjs +10 -0
  18. package/bin/lib/doctor-next-actions.mjs +5 -1
  19. package/bin/lib/doctor-plan.mjs +20 -16
  20. package/bin/lib/extra-merge-teeth.mjs +187 -0
  21. package/bin/lib/first-run-help.mjs +2 -2
  22. package/bin/lib/html-report-advisories.mjs +2 -0
  23. package/bin/lib/html-report-depth.mjs +22 -2
  24. package/bin/lib/html-report.mjs +16 -0
  25. package/bin/lib/install-migrate.mjs +29 -57
  26. package/bin/lib/managed-upgrade.mjs +2 -7
  27. package/bin/lib/remediation.mjs +132 -0
  28. package/bin/lib/resolved-candidate-facts.mjs +67 -2
  29. package/bin/lib/rules-under-contract.mjs +37 -89
  30. package/bin/lib/skill-catalog-apply.mjs +126 -0
  31. package/bin/lib/skill-install.mjs +264 -33
  32. package/bin/lib/skill-write.mjs +3 -0
  33. package/bin/lib/snippet-analysis.mjs +43 -2
  34. package/bin/lib/status-command.mjs +28 -0
  35. package/bin/lib/status-manifest.mjs +23 -0
  36. package/dist/{configTypes-l6XiwiC1.d.ts → configTypes-CgJimx9o.d.ts} +17 -3
  37. package/dist/eslint/index.cjs +6 -2
  38. package/dist/eslint/index.d.ts +70 -2
  39. package/dist/eslint/index.js +6 -2
  40. package/dist/index.cjs +35 -35
  41. package/dist/index.d.ts +804 -272
  42. package/dist/index.js +35 -35
  43. package/docs/README.md +3 -3
  44. package/docs/agent-guide.md +22 -16
  45. package/docs/ai-gates.md +15 -2
  46. package/docs/configuration.md +24 -11
  47. package/docs/develop.md +12 -3
  48. package/docs/diagnostics.md +75 -0
  49. package/docs/enthusiast/README.md +4 -3
  50. package/docs/enthusiast/how-to-agent-gates.md +1 -1
  51. package/docs/package-surface.md +16 -13
  52. package/docs/product-voice.md +6 -3
  53. package/docs/threat-model.md +1 -1
  54. package/docs/use.md +5 -4
  55. package/package.json +1 -1
  56. package/schemas/ark.config.schema.json +41 -2
  57. package/schemas/ark.resolved-candidate-facts.schema.json +1 -1
  58. package/schemas/ark.status-manifest.schema.json +47 -0
  59. package/server.json +2 -2
  60. package/templates/agent-skills/README.md +1 -1
  61. package/templates/agent-skills/ark-adopt/SKILL.md +23 -2
  62. package/templates/agent-skills/ark-contract/SKILL.md +8 -7
  63. package/templates/agent-skills/ark-place/SKILL.md +26 -2
  64. package/templates/agent-skills/ark-runtime/SKILL.md +66 -24
  65. package/templates/skills/ark-adopt.md +23 -2
  66. package/templates/skills/ark-contract.md +8 -7
  67. package/templates/skills/ark-place.md +26 -2
  68. package/templates/skills/ark-runtime.md +66 -24
package/CHANGELOG.md CHANGED
@@ -5,6 +5,146 @@ in the immutable pre-2.0 archive linked below.
5
5
 
6
6
  ## Unreleased
7
7
 
8
+ ## 4.7.1 — 2026-08-25
9
+
10
+ **Patch** over **4.7.0**. One project skill catalog, visible package version in the
11
+ skill picker, no home duplicates, ArkRun routed through existing skill names.
12
+ **No required config migration.** Does not close Z09 / K01.
13
+
14
+ **Status: unpublished** (implementation on `main`; npm `latest` remains **4.7.0** until publish).
15
+
16
+ ### Added
17
+
18
+ - **Visible skill version (picker):** install stamps `description` with
19
+ `arkgate@<version>. ` so Codex/Claude/Cursor/Grok show the package pin without
20
+ opening the file. `arkVersion:` in YAML stays for doctor. Same-body stamp drift
21
+ refreshes without `--force` (`stamp-refresh`).
22
+ - **`--prune-home-duplicates`:** removes frozen `/ark-*` copies from
23
+ `$CODEX_HOME/skills`, `~/.claude/skills`, and `~/.grok/skills` when the project
24
+ already has `.agents/skills`. Never deletes non-Ark skills.
25
+
26
+ ### Changed
27
+
28
+ - **One project catalog:** `.agents/skills/<name>/SKILL.md` is the byte source.
29
+ Claude / Grok / OpenCode get relative adapter links. Cursor/Codex/Antigravity
30
+ already read `.agents/skills` — no second copy. `.cursor/commands/ark-*.md` is
31
+ no longer written (Cursor listed commands + skills as two copies).
32
+ - **`--codex-home` / `--agent-homes`:** skip home skill write (and home MCP bind)
33
+ when the project catalog or `.codex/config.toml` already exists. Codex lists
34
+ user+repo; a home copy is why `/ark-*` appeared twice and stayed old.
35
+ - **Doctor:** when home `ark-*` and project `.agents/skills` both exist, next
36
+ action is prune, not `--codex-home --force`.
37
+ - **`/ark-contract`:** routes ArkRun extra edits (first extra `/ark-adopt`,
38
+ companion `/ark-runtime`, new files `/ark-place`). No new skill names.
39
+
40
+ ## 4.7.0 — 2026-08-25
41
+
42
+ **Minor** over **4.6.7**. Ships **ArkRun**: an opt-in extra on schema `1.2` for kernel
43
+ usage and complete declarations, plus companion `@arkgate/runtime` DX. Absence is
44
+ silent (Layers / ArkRules verdicts unchanged). In-memory stores remain
45
+ reference-only. **No required config migration.** Does not close Z09 / K01.
46
+
47
+ **Status: published** (on npm `latest`; see `docs/releases/4.7.0.md`).
48
+
49
+ ### Added
50
+
51
+ - **`arkRun` extra on `ark.config.json` schema `1.2` (RN02):** optional inline
52
+ `{ mode, compositionRoots, managedLayers, requireDeclarations }`. `1.1` and
53
+ earlier configs migrate in memory; absence is silent (Layers / ArkRules
54
+ verdicts unchanged). Unknown keys, unknown `managedLayers` names, and empty
55
+ `compositionRoots` in `enforced` mode fail closed (`ARKRUN_MISSING_ROOT`).
56
+ Advisory → enforced is a strengthening policy delta; demotion or deletion is
57
+ weakening and needs the existing hash-bound ack. ESLint envelope is RN06
58
+ (landed below); CI extra teeth landed in RN07. Does not close Z09 / K01.
59
+
60
+ - **ArkRun resolver facts on resolved-candidate-facts schema `1.2` (RN03):**
61
+ additive optional `arkRunKernelCalls`, `arkRunManagedNews`, and
62
+ `arkRunCompositionRootHits`. `1.0`/`1.1` payloads stay loadable (empty
63
+ arrays). Syntax evidence only — sensors consume these in RN04. Absence of
64
+ `arkRun` still leaves Layers / ArkRules verdicts unchanged. Does not close
65
+ Z09 / K01.
66
+
67
+ - **ArkRun tier-1 sensors (RN04):** when `arkRun` is present, closed sensors
68
+ emit `ARKRUN_MISSING_ROOT`, `ARKRUN_KERNEL_IN_DOMAIN`, `ARKRUN_DIRECT_NEW`,
69
+ `ARKRUN_UNDECLARED_EMIT`, `ARKRUN_UNDECLARED_HANDLE`, `ARKRUN_UNDECLARED_DEPEND`,
70
+ and `ARKRUN_TRANSPORT_BYPASS`. Advisory findings never flip `valid`; enforced
71
+ blocks. Absence of the extra is still silent on Layers / ArkRules verdicts.
72
+ Optional `arkRunDeclarations` facts stay additive on schema `1.2`. Dual-depth
73
+ catalog nextAction is RN05 (landed below). Does not close Z09 / K01.
74
+
75
+ - **ArkRun diagnostic catalog dual-depth (RN05):** closed `ARKRUN_*` catalog
76
+ entries have dual-depth remediation: casual `enthusiastHint` plus engineer
77
+ `nextAction` (target interpolates the call-site literal or specifier). Adding
78
+ an existing declaration-list string is `mechanical-safe` (`arkrun-declaration-list`)
79
+ only when that literal is already present; other ArkRun findings stay
80
+ `judgment`. Sensors, adapter fallback, and CLI remediation share
81
+ `deterministicNextAction`. Does not close Z09 / K01.
82
+
83
+ - **ArkRun ESLint envelope (RN06):** `arkgate/eslint` recommended config adds
84
+ `ark/no-arkrun-kernel-in-domain`, `ark/no-arkrun-direct-new`, and
85
+ `ark/no-arkrun-transport-bypass`. Same `ARKRUN_*` sensors as ark-check for
86
+ the import / `new` envelope; silent when `arkRun` is absent. Missing-root and
87
+ undeclared-* stay CLI/MCP/preflight. Does not close Z09 / K01.
88
+
89
+ - **ArkRun extra-teeth parity (RN07):** CLI `--strict-merge`, MCP `ark_check` /
90
+ snippet write, PreToolUse hook, atomic preflight, and CI share one ArkRun
91
+ verdict. Enforced extra teeth arm only when the layer plane is classified
92
+ (same ≥50% governed / ≥1 populated-layer floor as ArkRules); advisory and
93
+ absence stay silent on `valid`. Doctor/status `arkRun` section landed in
94
+ RN08 below. Does not close Z09 / K01.
95
+
96
+ - **ArkRun doctor / status / report (RN08):** `ark-check --doctor`, HTML
97
+ `--report`, and `ark status` / MCP `ark_status` expose an `arkRun` section
98
+ that is always `notAScore`. Residual is a finding-id count, never a score or
99
+ LLM verdict. `mergePlanes.arkRun` states whether the extra can fail merge;
100
+ advisory and absence never arm extra teeth. Report parity requires
101
+ `data-advisory="arkRun"`. Does not close Z09 / K01.
102
+
103
+ - **ArkRun companion branding (RN09):** `@arkgate/runtime` README and public
104
+ docs brand the kernel **ArkRun**. `createStrictArkKernel` stays the factory
105
+ (per-instance; no process-wide singleton). Kernel implementation stays out of
106
+ the `arkgate` tarball. Branding is not a production-durability claim. Does not
107
+ close Z09 / K01.
108
+
109
+ - **ArkRun interaction declarations (RN10):** `@arkgate/runtime` `register()`
110
+ accepts `uses` / `reactsTo` / `raises` / `sends` plus optional tooling-only
111
+ `extendedInfo`. `getDependencyInformationPackage()` returns a JSON-serializable
112
+ snapshot of ids, lifetime, and declarations — never factories, live instances,
113
+ or input DTOs. Companion registrations may omit declarations for local
114
+ experiments; enforced `arkRun` on the gate still requires them. Does not close
115
+ Z09 / K01.
116
+
117
+ - **ArkRun transport ports (RN11):** `@arkgate/runtime` `send()` is one call site
118
+ for `local` / `localBlocking` / `broker`. `ephemeral` defaults true (await local
119
+ recording or adapter accept — not a durability claim). Missing broker adapter
120
+ falls back to in-process local delivery, not cloud portability. No cloud SDKs
121
+ ship in the package. Does not close Z09 / K01.
122
+
123
+ - **ArkRun dev inspector (RN12):** `@arkgate/runtime` `startInspector()` /
124
+ `startArkRunInspector()` is opt-in. Default bind is `127.0.0.1`; `NODE_ENV=production`
125
+ vetoes start; public hosts (`0.0.0.0`, `::`) are rejected. HTTP is lazy-loaded.
126
+ `GET /snapshot` and `GET /events` (SSE) serve the information package plus
127
+ transport facts (no factories, no shipped cloud SDKs). Does not close Z09 / K01.
128
+
129
+ - **ArkRun graph slices (RN13):** `@arkgate/runtime` `requestGraph()` slices the
130
+ information package into `process` (raises / reactsTo / sends) or `technical`
131
+ (`uses`) graphs. Optional `nodeIds`, `degreesOfSeparation`, and include/exclude
132
+ query keep a neighborhood. `formatArkRunGraphMermaid()` / `graph.mermaid` is a
133
+ helper string, never a score. Inspector `GET /graph` serves the same slice.
134
+ Does not close Z09 / K01.
135
+
136
+ - **ArkRun skip corpus (RN14):** `tests/fixtures/arkrun-skip-corpus/` is the
137
+ executable proof: Application `new`, same-layer peer import, and homemade
138
+ `EventEmitter` stay green when `arkRun` is absent (Layers / ArkRules match
139
+ schema `1.1`) and fail write path, CLI, MCP, and `--strict-merge` when the
140
+ extra is enforced. Does not close Z09 / K01.
141
+
142
+ - **ArkRun skill-body deepen (RN15):** `/ark-runtime`, `/ark-place`, and
143
+ `/ark-adopt` teach the extra vs companion (advisory adopt, kernel-only
144
+ scaffold, composition-root wiring). Frozen **13** names — no `/ark-run`.
145
+ Skills never enforce; doctor `arkRun` stays `notAScore`. Agent Skills layout
146
+ stays 1:1 with `templates/skills`. Does not close Z09 / K01.
147
+
8
148
  ## 4.6.7 — 2026-08-24
9
149
 
10
150
  **Patch** over **4.6.6**. Production-hardening: CODEOWNERS, eval/pack honesty, CLI extracts,
package/README.md CHANGED
@@ -16,11 +16,11 @@ and makes sure a “green” check means something real.
16
16
 
17
17
  </div>
18
18
 
19
- > **ArkGate 4.6.7** is current on npm `latest`.
19
+ > **ArkGate 4.7.0** is on npm `latest`. Optional **ArkRun** extra on schema `1.2`.
20
20
  > A tree is **adopted** only with a required GitHub status running `arkgate-check --strict-merge`,
21
21
  > or `.ark/adoption-stance.json` `stance: "advisory-only"`. Doctor is compact (`--doctor --all`
22
- > for Details). [4.6.7 notes](docs/releases/4.6.7.md) · [4.6.6](docs/releases/4.6.6.md) ·
23
- > [Docs hub](docs/README.md) · [Product voice](docs/product-voice.md)
22
+ > for Details). [4.7.0 notes](docs/releases/4.7.0.md) · [4.6.7](docs/releases/4.6.7.md) ·
23
+ > [4.6.6](docs/releases/4.6.6.md) · [Docs hub](docs/README.md) · [Product voice](docs/product-voice.md)
24
24
 
25
25
  ---
26
26
 
@@ -78,11 +78,12 @@ A machine-readable architecture file (`ark.config.json`) plus enforcement:
78
78
  |-------|----------------|--------|
79
79
  | **Layers** (always) | Who may import whom — imports, placement, purity, isolation | `ark.config.json` layers + rules |
80
80
  | **ArkRules** (opt-in; structure rules inside a layer) | Habits *inside* a layer — structure sensors + domain invariants as data | `arkRules` → `arkrules/<Layer>.json` |
81
+ | **ArkRun** (opt-in extra) | Kernel usage + complete declarations | `arkRun` on schema `1.2` |
81
82
 
82
- Absence of ArkRules changes no inter-layer verdict. Label residual **`[Layer]`** vs **`[ArkRules]`**.
83
- Details: [configuration](docs/configuration.md#arkrules-intra-layer-opt-in) · [use path](docs/use.md).
83
+ Absence of ArkRules or ArkRun changes no inter-layer verdict. Label residual **`[Layer]`** vs **`[ArkRules]`**.
84
+ Details: [configuration](docs/configuration.md) · [use path](docs/use.md).
84
85
 
85
- **Not** a web framework, ORM, or job runner. Optional experimental runtime is separate and not required for the gate.
86
+ **Not** a web framework, ORM, or job runner. Optional **ArkRun** extra and companion kernel (`@arkgate/runtime`) are separate and not required for the gate. In-memory stores are not production durability.
86
87
 
87
88
  **Name note:** npm package `arkgate` — not affiliated with the separate Archgate CLI project.
88
89
 
@@ -192,9 +193,18 @@ More: [docs/develop.md](docs/develop.md) · skills install: [docs/agent-guide.md
192
193
 
193
194
  ---
194
195
 
195
- ## Optional experimental runtime
196
+ ## Optional ArkRun kernel
196
197
 
197
- Gates need **no** app runtime. The experimental `@arkgate/runtime` companion is separate and is not a production-readiness claim.
198
+ Gates need **no** app runtime. The experimental **ArkRun** companion (`@arkgate/runtime`) is separate
199
+ and is not a production-readiness claim. `createStrictArkKernel` is the factory: each call creates
200
+ an isolated instance (no process-wide singleton). Managed components declare `uses` / `reactsTo` /
201
+ `raises` / `sends`; `getDependencyInformationPackage()` is a JSON snapshot and never leaks factories.
202
+ `requestGraph()` slices that snapshot into process or technical graphs (`nodeIds`,
203
+ `degreesOfSeparation`, include/exclude query) with a Mermaid helper. `send()` is local /
204
+ localBlocking / broker (broker falls back to in-process local; `ephemeral`
205
+ defaults true; no cloud SDKs in the package). Opt-in `startInspector()` binds `127.0.0.1`,
206
+ refuses `NODE_ENV=production`, and lazy-loads HTTP for JSON snapshots, SSE, and `/graph`. The kernel is
207
+ not bundled in the `arkgate` tarball.
198
208
 
199
209
  ### Durability stance
200
210
 
@@ -217,7 +227,8 @@ for real systems. Details: [docs/production-hardening.md](docs/production-harden
217
227
  | Config · package surface · TS | [configuration](docs/configuration.md) · [package-surface](docs/package-surface.md) · [typescript-support](docs/typescript-support.md) |
218
228
  | Brownfield | [docs/brownfield-adoption.md](docs/brownfield-adoption.md) |
219
229
  | Security | [SECURITY.md](SECURITY.md) |
220
- | Current published (4.6.7 on npm `latest`) | [docs/releases/4.6.7.md](docs/releases/4.6.7.md) · [CHANGELOG](CHANGELOG.md) |
230
+ | Current published (4.7.0 on npm `latest`) | [docs/releases/4.7.0.md](docs/releases/4.7.0.md) · [CHANGELOG](CHANGELOG.md) |
231
+ | Prior published (4.6.7) | [docs/releases/4.6.7.md](docs/releases/4.6.7.md) |
221
232
  | Prior published (4.6.6) | [docs/releases/4.6.6.md](docs/releases/4.6.6.md) |
222
233
  | Prior published (4.6.5) | [docs/releases/4.6.5.md](docs/releases/4.6.5.md) |
223
234
  | Prior published (4.6.3) | [docs/releases/4.6.3.md](docs/releases/4.6.3.md) |
package/SECURITY.md CHANGED
@@ -18,7 +18,7 @@ Preferred path: use GitHub's private vulnerability reporting for this repository
18
18
  - reproduction steps
19
19
  - expected impact
20
20
  - whether the issue affects `arkgate-check` / `ark-check`, `arkgate-mcp` / `ark-mcp`,
21
- generated agent gates, the GitHub Action, or the optional runtime kernel
21
+ generated agent gates, the GitHub Action, or the optional ArkRun kernel
22
22
 
23
23
  If private vulnerability reporting is unavailable, open a minimal public issue asking for
24
24
  a private security contact without including exploit details.
@@ -1471,6 +1471,7 @@ async function main() {
1471
1471
  score: fitness.score,
1472
1472
  mode: fitness.mode,
1473
1473
  improvementCompass: reportCompass,
1474
+ arkRun: designDepth?.arkRun ?? null,
1474
1475
  }),
1475
1476
  leftoverDesignWork: designDepth?.designFitness?.designWeak === true,
1476
1477
  };
@@ -1493,7 +1494,17 @@ async function main() {
1493
1494
  adoption: adoptionForReport,
1494
1495
  designDepth,
1495
1496
  advisories: {
1496
- ...computeDoctorAdvisories(root, config, coverage, rules, files, ts, parseHealth),
1497
+ ...computeDoctorAdvisories(
1498
+ root,
1499
+ config,
1500
+ coverage,
1501
+ rules,
1502
+ files,
1503
+ ts,
1504
+ parseHealth,
1505
+ undefined,
1506
+ activeViolations
1507
+ ),
1497
1508
  // Doctor parity: always emit improvement compass when doctor would (reportParity).
1498
1509
  ...(designDepth?.improvementCompass
1499
1510
  ? { improvementCompass: designDepth.improvementCompass }
@@ -1502,6 +1513,7 @@ async function main() {
1502
1513
  ...(designDepth?.deepModuleCoach
1503
1514
  ? { deepModuleCoach: designDepth.deepModuleCoach }
1504
1515
  : {}),
1516
+ ...(designDepth?.arkRun ? { arkRun: designDepth.arkRun } : {}),
1505
1517
  },
1506
1518
  };
1507
1519
  const html = args.beginner
@@ -308,6 +308,42 @@ function hookEnforcement(root, host, operation, completePatch = false) {
308
308
  }).enforcementLadder;
309
309
  }
310
310
 
311
+ function extraMergeTeethClassification(root, config) {
312
+ const files = collectGovernedFiles(root, config);
313
+ const layers = config.layers ?? [];
314
+ let classified = 0;
315
+ const populated = new Set();
316
+ for (const abs of files) {
317
+ const layer = layerForFile(root, abs, layers);
318
+ if (layer) {
319
+ classified += 1;
320
+ populated.add(layer);
321
+ }
322
+ }
323
+ return {
324
+ governedPercent: files.length > 0 ? Math.round((classified / files.length) * 100) : 0,
325
+ populatedLayerCount: populated.size,
326
+ };
327
+ }
328
+
329
+ function arkRunSnippetContext({ root, config, filePath, layer, relFile, classification }) {
330
+ const extra = config?.arkRun;
331
+ if (!extra) return { layer, filePath };
332
+ const relative =
333
+ relFile ||
334
+ (typeof filePath === 'string'
335
+ ? path.relative(root, path.resolve(root, filePath)).split(path.sep).join('/')
336
+ : undefined);
337
+ return {
338
+ layer,
339
+ filePath,
340
+ relFile: relative,
341
+ arkRun: extra,
342
+ layers: config.layers ?? [],
343
+ classification: classification ?? extraMergeTeethClassification(root, config),
344
+ };
345
+ }
346
+
311
347
  function designDeltaViolations(delta) {
312
348
  return (delta?.changes ?? []).map((change) => ({
313
349
  ruleId: 'DESIGN_SMELL_REGRESSION',
@@ -565,7 +601,18 @@ function runHookPayload(payload, gate, config, args, ts, attemptContext, output
565
601
 
566
602
  const layer = inferLayer(filePath, config, args.root);
567
603
  const validateOnce = (src) =>
568
- validateSnippetAnalysis({ gate, ts, source: src, context: { layer, filePath } });
604
+ validateSnippetAnalysis({
605
+ gate,
606
+ ts,
607
+ source: src,
608
+ context: arkRunSnippetContext({
609
+ root: args.root,
610
+ config,
611
+ filePath,
612
+ layer,
613
+ relFile: normalizedRel,
614
+ }),
615
+ });
569
616
  // W1: one validation pass (+ optional autoPatch). Original write still blocked when
570
617
  // invalid; hosts must apply autoPatch explicitly (never silent write).
571
618
  const result = ts
@@ -2136,7 +2183,17 @@ export async function runArkMcp({ hookInput } = {}) {
2136
2183
  const filePath = params.arguments.filePath;
2137
2184
  const layer = params.arguments.layer ?? inferLayer(filePath, config, args.root);
2138
2185
  const validateOnce = (src) =>
2139
- validateSnippetAnalysis({ gate, ts, source: src, context: { layer, filePath } });
2186
+ validateSnippetAnalysis({
2187
+ gate,
2188
+ ts,
2189
+ source: src,
2190
+ context: arkRunSnippetContext({
2191
+ root: args.root,
2192
+ config,
2193
+ filePath,
2194
+ layer,
2195
+ }),
2196
+ });
2140
2197
  // W1: attempt mechanical-safe single-file autoPatch (import type), re-validate or discard.
2141
2198
  const result = validateWithAutoPatch({
2142
2199
  source,
@@ -2429,7 +2486,12 @@ export async function runArkMcp({ hookInput } = {}) {
2429
2486
  gate,
2430
2487
  ts,
2431
2488
  source: src,
2432
- context: { layer, filePath: placement.filePath },
2489
+ context: arkRunSnippetContext({
2490
+ root: args.root,
2491
+ config,
2492
+ filePath: placement.filePath,
2493
+ layer,
2494
+ }),
2433
2495
  });
2434
2496
  const result = composePrepareWrite({
2435
2497
  source,
@@ -8,7 +8,7 @@
8
8
  * Pure CLI helper (bin/lib/adapter-contract.mjs). Zero Node I/O.
9
9
  */
10
10
 
11
- import { layerImportNextAction } from './remediation.mjs';
11
+ import { deterministicNextAction } from './remediation.mjs';
12
12
  /** Versioned public result contract shared by every ArkGate enforcement adapter. */
13
13
  /**
14
14
  * 1.5 adds stable finding refs on every factory-emitted diagnostic (ACS06):
@@ -77,41 +77,22 @@ export function adapterDocsCodePath(ruleId) {
77
77
  return `${ADAPTER_DIAGNOSTIC_DOCS_RELATIVE_PATH}#${ruleId}`;
78
78
  }
79
79
  function nextActionForDiagnostic(ruleId, evidence, violation) {
80
- if (ruleId === 'LAYER_IMPORT_VIOLATION') {
81
- return layerImportNextAction({
82
- ruleId,
83
- typeOnly: evidence.typeOnly === true,
84
- targetTypeOnlyExports: violation.targetTypeOnlyExports === true,
85
- namedBindingsTypeOnly: violation.namedBindingsTypeOnly === true,
86
- peerIsolation: violation.peerIsolation === true,
87
- portProofEligible: violation.portProofEligible === true,
88
- fromLayer: text(evidence.fromLayer) ?? undefined,
89
- toLayer: text(evidence.toLayer) ?? undefined,
90
- target: text(evidence.target) ?? text(violation.target) ?? undefined,
91
- });
92
- }
93
- if (ruleId === 'FORBIDDEN_GLOBAL') {
94
- return `Inject ${evidence.target ?? 'the capability'} through a port, test at the public interface, then preflight again.`;
95
- }
96
- if (ruleId === 'CAPABILITY_VIOLATION') {
97
- 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.`;
98
- }
99
- if (ruleId === 'CIRCULAR_DEPENDENCY') {
100
- return 'Extract the shared dependency into a third module, test at the public interface, then preflight again.';
101
- }
102
- if (ruleId === 'RAW_EVENT_PUBLISH')
103
- return 'Publish through a registered intent creator, then run Ark again.';
104
- if (ruleId === 'PUBLISH_MISSING_SOURCE')
105
- return 'Add metadata.source to the publish call, then run Ark again.';
106
- if (ruleId === 'ARKRULE_STRUCTURE' ||
107
- ruleId === 'ARKRULE_INVARIANT' ||
108
- ruleId === 'INVARIANT_UNCOVERED' ||
109
- ruleId.startsWith('ARKRULE_')) {
110
- const source = evidence.arkruleSource ?? 'arkrules/<Layer>.json';
111
- const id = evidence.arkruleId ?? 'the ArkRule';
112
- return `Fix the structure or invariant for ${id} (declared in ${source}), then preflight again. Do not demote the rule without a hash-bound policy acknowledgement.`;
113
- }
114
- return `Resolve ${ruleId} without weakening ark.config.json, then run Ark again.`;
80
+ return deterministicNextAction({
81
+ ruleId,
82
+ target: text(evidence.target) ?? text(violation.target) ?? undefined,
83
+ fromLayer: text(evidence.fromLayer) ?? undefined,
84
+ toLayer: text(evidence.toLayer) ?? undefined,
85
+ typeOnly: evidence.typeOnly === true,
86
+ targetTypeOnlyExports: evidence.targetTypeOnlyExports === true,
87
+ namedBindingsTypeOnly: evidence.namedBindingsTypeOnly === true,
88
+ portProofEligible: evidence.portProofEligible === true,
89
+ peerIsolation: evidence.peerIsolation === true,
90
+ sourcePureTypeModule: evidence.sourcePureTypeModule === true,
91
+ edgeKind: text(evidence.edgeKind) ?? undefined,
92
+ capability: text(evidence.capability) ?? text(violation.capability) ?? undefined,
93
+ arkruleId: text(evidence.arkruleId) ?? undefined,
94
+ arkruleSource: text(evidence.arkruleSource) ?? undefined,
95
+ });
115
96
  }
116
97
  export function toAdapterDiagnostic(violation, fallbackSeverity = 'error',
117
98
  /**
@@ -234,6 +234,35 @@ export function validateAgentSkillsPackage(entries) {
234
234
  presentCount: names.length,
235
235
  };
236
236
  }
237
+ /**
238
+ * Visible package stamp at the start of Agent Skills `description`.
239
+ * Hosts show `description` in the picker; `arkVersion:` in YAML is invisible there.
240
+ * Example: `arkgate@4.7.1. Session 0 — mark the Ark path.`
241
+ */
242
+ export const ARK_SKILL_DESCRIPTION_VERSION_PATTERN = /^arkgate@(\S+)\.\s/;
243
+ /** Prefix written at install time (`arkgate@<version>. `). */
244
+ export function skillDescriptionVersionPrefix(version) {
245
+ const v = String(version ?? '').trim();
246
+ return v ? `arkgate@${v}. ` : '';
247
+ }
248
+ /** Drop a leading `arkgate@<version>. ` stamp; other text is unchanged. */
249
+ export function stripSkillDescriptionVersion(description) {
250
+ return String(description ?? '').replace(ARK_SKILL_DESCRIPTION_VERSION_PATTERN, '');
251
+ }
252
+ /** Version inside a stamped description, or null when the prefix is absent. */
253
+ export function parseSkillDescriptionVersion(description) {
254
+ const match = String(description ?? '').match(ARK_SKILL_DESCRIPTION_VERSION_PATTERN);
255
+ return match?.[1] ?? null;
256
+ }
257
+ /**
258
+ * Idempotent: replace an existing `arkgate@…` prefix or add one.
259
+ * Empty `version` strips the prefix (authoring templates stay unversioned).
260
+ */
261
+ export function stampSkillDescription(description, version) {
262
+ const rest = stripSkillDescriptionVersion(description);
263
+ const v = typeof version === 'string' ? version.trim() : '';
264
+ return v ? `${skillDescriptionVersionPrefix(v)}${rest}` : rest;
265
+ }
237
266
  /**
238
267
  * Normalize skill file content for identity compare (LF newlines, strip BOM).
239
268
  * Does not strip or rewrite frontmatter — Agent Skills export is 1:1 with flat templates.