arkgate 2.12.0 → 2.13.0

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 (70) hide show
  1. package/CHANGELOG.md +83 -0
  2. package/README.md +55 -31
  3. package/bin/ark-check.mjs +95 -36
  4. package/bin/ark-mcp.mjs +11 -5
  5. package/bin/ark-shared.mjs +88 -56
  6. package/bin/ark.mjs +45 -10
  7. package/bin/lib/agent-gates.mjs +12 -0
  8. package/bin/lib/architecture-scan.mjs +8 -0
  9. package/bin/lib/ci-and-commands.mjs +9 -3
  10. package/bin/lib/codex-home.mjs +7 -0
  11. package/bin/lib/config-contract.mjs +331 -0
  12. package/bin/lib/doctor-plan.mjs +43 -16
  13. package/bin/lib/enforcement-profiles.mjs +97 -0
  14. package/bin/lib/host-support-matrix.mjs +77 -0
  15. package/bin/lib/install-migrate.mjs +45 -14
  16. package/bin/lib/mcp-adoption.mjs +35 -3
  17. package/bin/lib/open-html.mjs +75 -0
  18. package/bin/lib/presets.mjs +3 -2
  19. package/bin/lib/safety-diagnostics.mjs +31 -11
  20. package/bin/lib/skill-install.mjs +64 -0
  21. package/bin/lib/ts-resolve.mjs +2 -1
  22. package/bin/lib/weakest-link.mjs +417 -0
  23. package/bin/lib/write-path-capabilities.mjs +182 -0
  24. package/bin/lib/write-path-detect.mjs +62 -99
  25. package/dist/configContract-iBLxx5Tz.d.cts +53 -0
  26. package/dist/configContract-iBLxx5Tz.d.ts +53 -0
  27. package/dist/eslint/index.cjs +375 -13
  28. package/dist/eslint/index.cjs.map +1 -1
  29. package/dist/eslint/index.d.cts +30 -20
  30. package/dist/eslint/index.d.ts +30 -20
  31. package/dist/eslint/index.js +375 -13
  32. package/dist/eslint/index.js.map +1 -1
  33. package/dist/index.cjs +723 -61
  34. package/dist/index.cjs.map +1 -1
  35. package/dist/index.d.cts +95 -5
  36. package/dist/index.d.ts +95 -5
  37. package/dist/index.js +716 -61
  38. package/dist/index.js.map +1 -1
  39. package/dist/nestjs/index.cjs +150 -42
  40. package/dist/nestjs/index.cjs.map +1 -1
  41. package/dist/nestjs/index.d.cts +2 -1
  42. package/dist/nestjs/index.d.ts +2 -1
  43. package/dist/nestjs/index.js +150 -42
  44. package/dist/nestjs/index.js.map +1 -1
  45. package/dist/runtime/index.cjs +723 -61
  46. package/dist/runtime/index.cjs.map +1 -1
  47. package/dist/runtime/index.d.cts +3 -2
  48. package/dist/runtime/index.d.ts +3 -2
  49. package/dist/runtime/index.js +716 -61
  50. package/dist/runtime/index.js.map +1 -1
  51. package/dist/{types-BZ17b9i5.d.cts → types-BxBwnBpC.d.cts} +9 -36
  52. package/dist/{types-BZ17b9i5.d.ts → types-Wcs_l1_J.d.ts} +9 -36
  53. package/docs/agent-guide.md +32 -20
  54. package/docs/ai-gates.md +53 -18
  55. package/docs/configuration.md +97 -0
  56. package/docs/enthusiast/README.md +3 -3
  57. package/docs/enthusiast/how-to-agent-gates.md +7 -3
  58. package/docs/migrate-from-ark-runtime-kernel.md +3 -0
  59. package/docs/package-surface.md +14 -9
  60. package/docs/production-hardening.md +15 -2
  61. package/docs/threat-model.md +65 -0
  62. package/docs/typescript-support.md +3 -3
  63. package/package.json +15 -2
  64. package/schemas/ark.config.schema.json +750 -0
  65. package/server.json +2 -2
  66. package/templates/hooks/pre-commit-ark +37 -0
  67. package/templates/skills/ark-coverage.md +2 -2
  68. package/templates/skills/ark-runtime.md +8 -5
  69. package/templates/skills/ark-upgrade.md +36 -16
  70. package/tests/fixtures/ts-consumer/ark.config.json +2 -0
@@ -1,3 +1,5 @@
1
+ import { a as ArkConfigRule, b as ArkConfigLayer, A as ArkConfig } from './configContract-iBLxx5Tz.cjs';
2
+
1
3
  /**
2
4
  * Core domain primitives for Ark.
3
5
  * These types are the foundation for all governance concepts.
@@ -257,6 +259,7 @@ declare class PolicyEngine<Context = unknown> {
257
259
  * A profile turns semantic names such as `Domain.Order.Placed` into governed
258
260
  * layer names and dependency rules.
259
261
  */
262
+
260
263
  interface ArchitectureLayer {
261
264
  name: string;
262
265
  prefixes: string[];
@@ -269,19 +272,7 @@ interface ArchitectureLayer {
269
272
  description?: string;
270
273
  order?: number;
271
274
  }
272
- interface ArchitectureRule {
273
- from: string;
274
- to: string;
275
- allowed: boolean;
276
- message?: string;
277
- /**
278
- * When true with allowed:false: deny only when slice ids differ (same or cross layer).
279
- * Requires path resolution at check time (write-gate / CI). Matches domain EdgeRule.
280
- */
281
- peerIsolation?: boolean;
282
- /** Parent folder names that own the slice id as the next path segment. */
283
- sliceFolders?: string[];
284
- }
275
+ type ArchitectureRule = ArkConfigRule;
285
276
  interface ArchitectureProfile {
286
277
  name: string;
287
278
  layers: ArchitectureLayer[];
@@ -297,30 +288,12 @@ interface CreateArchitectureProfileFromArkConfigOptions {
297
288
  /** Runtime profile name. Default: config.name or "ark.config.json". */
298
289
  name?: string;
299
290
  }
300
- interface ArchitectureLayerConfig {
301
- name: string;
302
- patterns: string[];
303
- /**
304
- * Glob(s) carved out of this layer. A file matching any `exclude` glob is not governed by
305
- * this layer even when a `patterns` glob matches — so a broad pattern like
306
- * `src/**​/domain/**` can opt framework internals (`**​/kernel/**`) out of domain-purity
307
- * rules without listing every include. Excluding a file also removes it from this layer's
308
- * rule and `forbiddenGlobals` enforcement, since both key off layer classification.
309
- */
310
- exclude?: string[];
311
- intentPrefixes?: string[];
312
- description?: string;
313
- forbiddenGlobals?: string[];
314
- mayImportInfrastructure?: boolean;
315
- /** Optional layers do not warn when their patterns match no files. */
316
- optional?: boolean;
317
- }
318
- interface ArkCheckConfig {
319
- name?: string;
320
- include: string[];
321
- layers: ArchitectureLayerConfig[];
291
+ type ArchitectureLayerConfig = ArkConfigLayer;
292
+ type ArkCheckConfig = Omit<ArkConfig, '$schema' | 'schemaVersion' | 'rules'> & {
293
+ $schema?: string;
294
+ schemaVersion?: ArkConfig['schemaVersion'];
322
295
  rules?: ArchitectureRule[];
323
- }
296
+ };
324
297
  interface CreateElevenLayerArkConfigOptions {
325
298
  /** Source root used in generated file patterns. Default: "src". */
326
299
  rootDir?: string;
@@ -1,3 +1,5 @@
1
+ import { a as ArkConfigRule, b as ArkConfigLayer, A as ArkConfig } from './configContract-iBLxx5Tz.js';
2
+
1
3
  /**
2
4
  * Core domain primitives for Ark.
3
5
  * These types are the foundation for all governance concepts.
@@ -257,6 +259,7 @@ declare class PolicyEngine<Context = unknown> {
257
259
  * A profile turns semantic names such as `Domain.Order.Placed` into governed
258
260
  * layer names and dependency rules.
259
261
  */
262
+
260
263
  interface ArchitectureLayer {
261
264
  name: string;
262
265
  prefixes: string[];
@@ -269,19 +272,7 @@ interface ArchitectureLayer {
269
272
  description?: string;
270
273
  order?: number;
271
274
  }
272
- interface ArchitectureRule {
273
- from: string;
274
- to: string;
275
- allowed: boolean;
276
- message?: string;
277
- /**
278
- * When true with allowed:false: deny only when slice ids differ (same or cross layer).
279
- * Requires path resolution at check time (write-gate / CI). Matches domain EdgeRule.
280
- */
281
- peerIsolation?: boolean;
282
- /** Parent folder names that own the slice id as the next path segment. */
283
- sliceFolders?: string[];
284
- }
275
+ type ArchitectureRule = ArkConfigRule;
285
276
  interface ArchitectureProfile {
286
277
  name: string;
287
278
  layers: ArchitectureLayer[];
@@ -297,30 +288,12 @@ interface CreateArchitectureProfileFromArkConfigOptions {
297
288
  /** Runtime profile name. Default: config.name or "ark.config.json". */
298
289
  name?: string;
299
290
  }
300
- interface ArchitectureLayerConfig {
301
- name: string;
302
- patterns: string[];
303
- /**
304
- * Glob(s) carved out of this layer. A file matching any `exclude` glob is not governed by
305
- * this layer even when a `patterns` glob matches — so a broad pattern like
306
- * `src/**​/domain/**` can opt framework internals (`**​/kernel/**`) out of domain-purity
307
- * rules without listing every include. Excluding a file also removes it from this layer's
308
- * rule and `forbiddenGlobals` enforcement, since both key off layer classification.
309
- */
310
- exclude?: string[];
311
- intentPrefixes?: string[];
312
- description?: string;
313
- forbiddenGlobals?: string[];
314
- mayImportInfrastructure?: boolean;
315
- /** Optional layers do not warn when their patterns match no files. */
316
- optional?: boolean;
317
- }
318
- interface ArkCheckConfig {
319
- name?: string;
320
- include: string[];
321
- layers: ArchitectureLayerConfig[];
291
+ type ArchitectureLayerConfig = ArkConfigLayer;
292
+ type ArkCheckConfig = Omit<ArkConfig, '$schema' | 'schemaVersion' | 'rules'> & {
293
+ $schema?: string;
294
+ schemaVersion?: ArkConfig['schemaVersion'];
322
295
  rules?: ArchitectureRule[];
323
- }
296
+ };
324
297
  interface CreateElevenLayerArkConfigOptions {
325
298
  /** Source root used in generated file patterns. Default: "src". */
326
299
  rootDir?: string;
@@ -1,7 +1,9 @@
1
1
  # ArkGate — Agent Integration Guide
2
2
 
3
3
  **ArkGate** (`arkgate`) — architecture co-pilot for AI TypeScript. This guide describes how AI
4
- agents and codegen tools safely interact with the write gate, MCP tools, and `/ark-*` skills.
4
+ agents and codegen tools safely interact with write hooks, advisory MCP tools, CI, and `/ark-*`
5
+ skills. Guarantees differ by host; start with the
6
+ [canonical host support matrix](../README.md#host-enforcement-support).
5
7
 
6
8
  CLI names: prefer **`arkgate` / `arkgate-check` / `arkgate-mcp`**; aliases `ark` / `ark-check` /
7
9
  `ark-mcp` still work for one major. TypeScript **5.x / 6.x / 7.x** as the project compiler:
@@ -216,18 +218,21 @@ npx arkgate-check --install-agent-gates --tools claude,cursor,codex,grok
216
218
  # alias: npx ark-check --install-agent-gates --tools claude,cursor,codex,grok
217
219
  ```
218
220
 
219
- | Host | Write gate | MCP | Skills path |
220
- |------|------------|-----|-------------|
221
- | Claude Code | PreToolUse hook | `.mcp.json` / `claude mcp add` | `.claude/skills/<name>/SKILL.md` |
222
- | Cursor | Advisory (rules + MCP) | `.cursor/mcp.json` | `.cursor/commands/` |
223
- | OpenAI Codex | MCP + CI | `$CODEX_HOME/config.toml` (global; absolute `--root`; multi-project → secondary `ark_<slug>` unless `--force`) | `$CODEX_HOME/prompts` (`--codex-home`) |
224
- | **Grok Build** | PreToolUse hook (`.grok/hooks/`) | `.grok/config.toml` + `.mcp.json` | `.grok/skills/<name>/SKILL.md` |
221
+ | Host | Installed paths | Skills path |
222
+ |------|-----------------|-------------|
223
+ | Claude Code | `.claude/settings.json` hook + `.mcp.json` / `claude mcp add` | `.claude/skills/<name>/SKILL.md` |
224
+ | Cursor | `.cursor/mcp.json` + `.cursor/rules/ark.mdc` | `.cursor/commands/` |
225
+ | OpenAI Codex | `$CODEX_HOME/config.toml` (global; absolute `--root`; multi-project → secondary `ark_<slug>` unless `--force`; doctor defers non-temp home gaps when session host ≠ Codex — see [ai-gates.md](ai-gates.md)) | `$CODEX_HOME/prompts` (`--codex-home`; fix when using Codex) |
226
+ | **Grok Build** | `.grok/hooks/ark-write-gate.json` + `.grok/config.toml` / `.mcp.json` | `.grok/skills/<name>/SKILL.md` |
225
227
 
226
- Full copy-paste setups: [ai-gates.md](ai-gates.md). Skill inventory: main [README](../README.md#agent-skills-ark-).
228
+ This is a path reference, not a guarantee table. Full copy-paste setups:
229
+ [ai-gates.md](ai-gates.md). Skill inventory: main [README](../README.md#agent-skills-ark-).
227
230
 
228
- ## Contract Discovery
231
+ ## Experimental runtime: contract discovery
229
232
 
230
- Prefer `createStrictArkKernel()` for strict projects. It wires the registry, graph,
233
+ The runtime kernel is currently **experimental** and is not required for static gate adoption or
234
+ presented as production-ready. If you are evaluating it, prefer `createStrictArkKernel()`. It
235
+ wires the registry, graph,
231
236
  policies, event bus, audit trail, event contracts, outbox, observability,
232
237
  projections, metadata, workflow engine, and 11-layer architecture profile:
233
238
 
@@ -244,8 +249,8 @@ const contract = ark.manifest().toJSON();
244
249
  // contract.observability, projections
245
250
  ```
246
251
 
247
- Prefer `arkgate/runtime` for the optional kernel (root `arkgate` still re-exports for
248
- compat). Package surface policy: [package-surface.md](package-surface.md).
252
+ Use `arkgate/runtime` when evaluating the experimental kernel (root `arkgate` still re-exports
253
+ for compatibility). Package surface policy: [package-surface.md](package-surface.md).
249
254
 
250
255
  Agents should read `contract` and `ark.observability.report()` before generating or modifying code.
251
256
 
@@ -354,11 +359,17 @@ if (!result.valid) {
354
359
  }
355
360
  ```
356
361
 
357
- Passing the `typescript` module enables built-in AST checks for raw publish calls, missing
358
- `metadata.source`, and source-layer mismatches. `ark-mcp` enables these checks
359
- automatically when TypeScript is available.
362
+ Passing the `typescript` module enables built-in AST/symbol checks for dependencies, forbidden
363
+ ambient globals, raw publish calls, missing `metadata.source`, and source-layer mismatches.
364
+ `ark-mcp` enables these checks automatically when TypeScript is available. The exact supported
365
+ syntax and unresolved-dynamic policy are documented in
366
+ [Scanner soundness envelope](ai-gates.md#scanner-soundness-envelope).
360
367
 
361
- Violation codes (from `createAICodeGate`): `RAW_EVENT_PUBLISH`, `PUBLISH_MISSING_SOURCE`, `PUBLISH_SOURCE_LAYER_MISMATCH`, `FORBIDDEN_PATTERN`, `FORBIDDEN_SUBSTRING`, `FORBIDDEN_IMPORT`, `POLICY_VIOLATION`, `UNKNOWN_INTENT`, `LAYER_REFERENCE_VIOLATION`, `EXTENSION_ERROR`, `AST_ANALYZER_ERROR`.
368
+ Relevant violation codes include `LAYER_IMPORT_VIOLATION`, `FORBIDDEN_GLOBAL`,
369
+ `DYNAMIC_IMPORT_NOT_ALLOWLISTED`, `DYNAMIC_REQUIRE_NOT_ALLOWLISTED`, `RAW_EVENT_PUBLISH`,
370
+ `PUBLISH_MISSING_SOURCE`, `PUBLISH_SOURCE_LAYER_MISMATCH`, `FORBIDDEN_PATTERN`,
371
+ `FORBIDDEN_SUBSTRING`, `FORBIDDEN_IMPORT`, `POLICY_VIOLATION`, `UNKNOWN_INTENT`,
372
+ `LAYER_REFERENCE_VIOLATION`, `EXTENSION_ERROR`, and `AST_ANALYZER_ERROR`.
362
373
 
363
374
  Use `ark-check` in CI for repository-level checks that need real file paths:
364
375
 
@@ -461,8 +472,9 @@ Example config:
461
472
  ```
462
473
 
463
474
  `ark-check` resolves imports through the TypeScript module resolver against your
464
- `tsconfig.json` — relative, path-alias (e.g. `@infra/db`), package imports, dynamic
465
- `import()`, and `require()` — plus string intent references. It also flags raw
475
+ `tsconfig.json` — relative, path-alias (e.g. `@infra/db`), package/workspace imports,
476
+ TypeScript `import = require()`, dynamic `import()`, and `require()` — plus string intent
477
+ references. It also flags raw
466
478
  `publish()` calls, publish calls without `metadata.source`, and source intent literals
467
479
  whose resolved layer differs from the publishing file layer. Pass `--tsconfig <path>` to force one config
468
480
  for every file; otherwise each source file uses the nearest `tsconfig.json` above it (like
@@ -623,8 +635,8 @@ Register the server itself in `.mcp.json` so the agent can read `ark://manifest`
623
635
  }
624
636
  ```
625
637
 
626
- This makes the manifest + AI gate an enforced checkpoint rather than a library the agent
627
- must remember to call.
638
+ On Claude/Grok, the installed PreToolUse hook makes matched writes an enforced checkpoint. MCP
639
+ registration by itself remains advisory on every host because the agent must call the tool.
628
640
 
629
641
  ## Recommended Agent Workflow
630
642
 
package/docs/ai-gates.md CHANGED
@@ -1,12 +1,13 @@
1
1
  # Gating AI Agents with ArkGate
2
2
 
3
3
  **ArkGate** (`arkgate`) is the architecture co-pilot for AI TypeScript (write gate · CI · plan/loop).
4
- The write-path gate is what makes it different from every other architecture linter:
5
- generated code is validated against your architecture **before it lands on disk**, not
6
- after the PR is red.
4
+ On Claude Code and Grok Build, an installed and trusted PreToolUse hook can block matched writes
5
+ before they land on disk. Cursor and OpenAI Codex use advisory MCP validation at write time; CI is
6
+ their hard repository check. See the
7
+ [canonical host support matrix](../README.md#host-enforcement-support) before installing.
7
8
 
8
9
  Everything below uses the same `ark.config.json` as `arkgate-check` / `ark-check` (CI) — one
9
- contract, enforced everywhere. Generate it once:
10
+ contract shared by every surface. Generate it once:
10
11
 
11
12
  ```bash
12
13
  npx arkgate-check --init
@@ -40,8 +41,8 @@ you pass `--force`, so review and commit only the templates that match your proj
40
41
 
41
42
  **Doctor (W5):** `ark-check --doctor --json` includes `doctor.writePath`
42
43
  (`mode`: `repair` | `reject-only` | `mcp-only` | `none`, plus `prepareWrite` /
43
- `autoPatch` flags) so leads can see whether the write path is repair-capable or
44
- still reject-only.
44
+ `autoPatch` flags), the supported profile for the active host, and the evidence actually found.
45
+ Supported capability and installed guarantee are deliberately separate.
45
46
 
46
47
  If your project uses Codex or Grok, treat MCP registration as part of the default
47
48
  setup, not an optional extra. Ark works best when the agent can read `ark://manifest`
@@ -223,7 +224,8 @@ path. If it reports violations, fix them before writing. The architecture
223
224
  contract is available as the `ark://manifest` resource.
224
225
  ```
225
226
 
226
- Your hard backstop in Cursor is CI: `ark-check` fails the PR on anything that slips through.
227
+ Your repository backstop in Cursor is CI: `ark-check` fails its check on anything that slips
228
+ through. It blocks the merge only when that status is required by repository policy.
227
229
 
228
230
  ## OpenAI Codex CLI
229
231
 
@@ -251,8 +253,9 @@ args = ["arkgate-mcp", "--root", "/absolute/path/to/project", "--config", "/abso
251
253
  Then **restart Codex** — it does not hot-load MCP servers. Expect resource `ark://manifest`
252
254
  and tools `validate_code`, `ark_check`, `ark_coverage`, `ark_place`.
253
255
 
254
- Same model as Cursor for enforcement: MCP for discovery/validation, `ark-check` in CI as
255
- the hard gate. Register the MCP server as soon as the repo is adopted.
256
+ Same model as Cursor for enforcement: advisory MCP for discovery/validation and `ark-check` as
257
+ the hard CI check. It becomes a merge block only when the status is required. Register the MCP
258
+ server as soon as the repo is adopted.
256
259
 
257
260
  ### Multi-project Codex (home config last-wins)
258
261
 
@@ -272,11 +275,15 @@ primary A. It writes a **scoped secondary** table:
272
275
  |------|---------|
273
276
  | Add B without moving primary | `ark-check --install-agent-gates --tools codex` (no `--force`) |
274
277
  | Make B the primary binding | `ark-check --install-agent-gates --tools codex --force` |
275
- | Doctor: primary points at another permanent project | gap id `codex-home-multi-project` (warn if no secondary yet; info if scoped table already present) |
278
+ | Doctor: primary points at another permanent project | gap id `codex-home-multi-project` (warn if no secondary yet and session host is unknown/Codex; **info + `deferred`** when the session host is known and not Codex — e.g. Grok/Claude/Cursor; info if a scoped secondary is already present) |
279
+ | When using Codex: refresh home skills/MCP | `ark-check --install-agent-gates --skills-only --codex-home --force` |
276
280
 
277
281
  `ark-check --doctor` surfaces the multi-project state so you are not left thinking B owns
278
- `ark://manifest` when only a secondary table exists. Temp/upgrade primary roots are still
279
- rewritten fail-closed (not multi-project).
282
+ `ark://manifest` when only a secondary table exists. **Deferred (fix when using Codex):**
283
+ non-temp Codex-home gaps (`codex-home-multi-project`, stale `$CODEX_HOME/prompts`) are
284
+ severity **info**, marked `deferred: true`, and omitted from Top actions when the session
285
+ host is known and not Codex — `/ark-upgrade` on Grok/Claude is not Incomplete because of
286
+ them. **Temp/upgrade primary roots** stay fail-closed urgent (rewritten, not multi-project).
280
287
 
281
288
  ## Grok Build (xAI)
282
289
 
@@ -320,7 +327,8 @@ command = "npx"
320
327
  args = ["ark-mcp", "--root", ".", "--config", "ark.config.json"]
321
328
  ```
322
329
 
323
- Then restart Grok or refresh via `/mcps`. Pair with CI `ark-check` as the hard merge gate.
330
+ Then restart Grok or refresh via `/mcps`. Pair with CI `ark-check`; require that status if it
331
+ must block merges.
324
332
 
325
333
  ## Instruction-tier agents: Windsurf, Cline, Copilot, Kiro, Roo Code, Continue, Gemini CLI
326
334
 
@@ -345,7 +353,7 @@ npx ark-check --install-agent-gates --tools windsurf,cline,copilot,kiro,roo,cont
345
353
 
346
354
  All of them derive from the same contract as `AGENTS.md` and the Cursor rule, so the
347
355
  steps cannot drift. These are advisory (the agent reads rules; nothing blocks the
348
- write) keep `ark-check` in CI as the hard gate.
356
+ write). Keep `ark-check` in CI and require its status when it must block merges.
349
357
 
350
358
  ## Any other agent runtime with shell hooks
351
359
 
@@ -387,14 +395,16 @@ Prefer keeping editor + CI on the same `ark.config.json` — do not maintain a p
387
395
 
388
396
  ## CI backstop
389
397
 
390
- Whatever the agent side does, gate the merge:
398
+ Whatever the agent side does, run the merge profile in CI:
391
399
 
392
400
  ```yaml
393
- - run: npx ark-check --root . --config ark.config.json --strict
401
+ - run: npx ark-check --root . --config ark.config.json --strict-merge
394
402
  ```
395
403
 
396
- The `--strict` profile also requires the generated CI/write gates and fails on safety
397
- diagnostics. Configure reviewed exceptions explicitly:
404
+ `--strict-merge` requires strict config plus the shared gate files (`AGENTS.md`, MCP config,
405
+ and CI workflow) and fails on safety diagnostics. `--strict` is a compatibility alias. Neither
406
+ profile requires an editor hook; add `--require-write-hook claude|grok` only when CI must verify
407
+ that host-specific local boundary too. Configure reviewed exceptions explicitly:
398
408
 
399
409
  ```json
400
410
  {
@@ -411,6 +421,31 @@ diagnostics. Configure reviewed exceptions explicitly:
411
421
  `ark-check --doctor --json` reports counts under `doctor.safety`. An `any` cast is
412
422
  reported as lost static assurance; it does not imply that a runtime schema was bypassed.
413
423
 
424
+ ### Scanner soundness envelope
425
+
426
+ ArkGate uses the TypeScript compiler API for the governed source files. The repository scanner
427
+ and `createAICodeGate({ typescript })` recognize these dependency forms:
428
+
429
+ - `import ... from 'literal'`, side-effect imports, and `export ... from 'literal'`;
430
+ - TypeScript `import x = require('literal')` external-module references;
431
+ - direct `import('literal')` and direct `require('literal')` calls; and
432
+ - relative, tsconfig path-alias, package, and installed workspace-package targets that resolve
433
+ to source inside the project root. Third-party or escaped targets are deliberately not governed.
434
+
435
+ Direct `import(expr)` emits `DYNAMIC_IMPORT_NOT_ALLOWLISTED`; direct `require(expr)` emits
436
+ `DYNAMIC_REQUIRE_NOT_ALLOWLISTED`. They are warnings in the default reporting profile and fail
437
+ `--strict-config` / `--strict-merge`. The existing `dynamicImportAllowlist` name is retained for
438
+ compatibility and is the reviewed file-level exception for both forms. Aliased loaders (for
439
+ example `const load = require; load(expr)`) and runtime-computed module maps are not resolved.
440
+
441
+ Forbidden globals use single-file TypeScript binding: parameters, variables, functions, classes,
442
+ and imports declared in the file shadow ambient names. Bare ambient value references are reported
443
+ even when assigned to an alias, and static dotted access through `globalThis` is normalized (for
444
+ example `globalThis.Date.now()`). This is not whole-program data-flow analysis: computed property
445
+ names, aliases of dotted members, and cross-file symbol provenance are outside the current
446
+ envelope. When callers omit the `typescript` option, AICodeGate retains its conservative literal
447
+ fallback and does not claim symbol-aware parity; the shipped `ark-mcp` path supplies TypeScript.
448
+
414
449
  Adopting Ark on an existing codebase with violations? Freeze them once and ratchet down:
415
450
 
416
451
  ```bash
@@ -0,0 +1,97 @@
1
+ # Versioned `ark.config.json`
2
+
3
+ ArkGate treats `ark.config.json` as a versioned product contract, not an untyped settings bag.
4
+ The CLI, MCP server, and ESLint plugin all use the same parser, migration, defaults, and validator.
5
+
6
+ ## Start a config
7
+
8
+ `arkgate start`, `arkgate init`, and every preset emit the current metadata:
9
+
10
+ ```json
11
+ {
12
+ "$schema": "https://unpkg.com/arkgate@2/schemas/ark.config.schema.json",
13
+ "schemaVersion": "1.0",
14
+ "include": ["src"],
15
+ "layers": [],
16
+ "rules": []
17
+ }
18
+ ```
19
+
20
+ `$schema` is for editor completion. `schemaVersion` controls ArkGate's runtime contract and is
21
+ independent from the npm package version.
22
+
23
+ For offline editor completion, point `$schema` at the installed file instead:
24
+
25
+ ```json
26
+ {
27
+ "$schema": "./node_modules/arkgate/schemas/ark.config.schema.json",
28
+ "schemaVersion": "1.0"
29
+ }
30
+ ```
31
+
32
+ The same schema is exported through the stable package subpaths `arkgate/schema` and
33
+ `arkgate/schema/ark.config.schema.json`. Node tooling can locate it with
34
+ `require.resolve('arkgate/schema')`.
35
+
36
+ ## Compatibility and migration
37
+
38
+ Configs without `schemaVersion` are the legacy shape shipped through ArkGate 1.x and early 2.x.
39
+ The loader deterministically projects them to schema `1.0` in memory by adding contract metadata
40
+ and the established defaults. It never rewrites the user's file during a check. Newly generated
41
+ configs always contain the metadata, and unsupported future versions fail at
42
+ `$.schemaVersion` instead of being guessed.
43
+
44
+ Current defaults are:
45
+
46
+ | Field | Default |
47
+ |---|---|
48
+ | `include` | `["src"]` |
49
+ | `layers` | `[]` |
50
+ | `rules` | ArkGate's strict 11-layer deny matrix |
51
+ | `exclude` | `[]` |
52
+ | `excludeGenerated` | `true` |
53
+ | `cyclePolicy` | `"strict"` |
54
+ | `dynamicImportAllowlist` | `[]` |
55
+ | safety thresholds | zero; opt-ins disabled |
56
+
57
+ JSON Schema `default` values document the effective behavior. Optional defaults are not written
58
+ back to disk.
59
+
60
+ ## Unknown and invalid fields
61
+
62
+ Unknown fields fail closed at every object level. Diagnostics name the exact JSON path:
63
+
64
+ ```text
65
+ Invalid ArkGate config (/repo/ark.config.json):
66
+ - $.layers[0].forbiddenGlobal: unknown field
67
+ ```
68
+
69
+ The same input cannot pass CI while being silently ignored by MCP or ESLint. Invalid JSON, wrong
70
+ types, empty required strings, duplicate string-array entries, negative safety thresholds, and
71
+ unsupported schema versions also fail before scanning begins.
72
+
73
+ ## Supported fields
74
+
75
+ Top-level fields:
76
+
77
+ - `$schema`, `schemaVersion`, `name`
78
+ - `include`, `exclude`, `excludeGenerated`, `frameworkOverlay`
79
+ - `layers`, `rules`, `cyclePolicy`
80
+ - `dynamicImportAllowlist`, `safety`
81
+
82
+ Layer fields:
83
+
84
+ - `name`, `patterns`, `exclude`, `description`
85
+ - `intentPrefixes`, `forbiddenGlobals`, `mayImportInfrastructure`, `optional`
86
+
87
+ Rule fields:
88
+
89
+ - `from`, `to`, `allowed`, `message`, `peerIsolation`, `sliceFolders`
90
+
91
+ Safety fields:
92
+
93
+ - `maxTsSuppressions`, `maxAnyCasts`
94
+ - `allowInMemory`, `allowDisabledPeerIsolation`
95
+
96
+ The packaged JSON Schema is authoritative for types, constraints, defaults, and the unknown-key
97
+ policy.
@@ -15,7 +15,7 @@ aliases `ark` / `ark-check` still work).
15
15
  |------|---------|
16
16
  | **Suggest** | Propose an application shape and install a starter contract. |
17
17
  | **Adapt** | Match the contract to your real layout / raise governed coverage. |
18
- | **Enforce** | The contract actually governs your code; gates hold the line. |
18
+ | **Enforce** | The contract covers the real code and checked edges are clean; host/CI guarantees remain separate. |
19
19
 
20
20
  `ark start` and `ark-check --plan` will not claim "everything is guarded" while governed coverage is near zero. On Nest/Next/express starters, init also merges **framework filename conventions** into the layer globs so day-one coverage is real.
21
21
 
@@ -27,7 +27,7 @@ aliases `ark` / `ark-check` still work).
27
27
  | **How-to** | [Pick your application shape](how-to-pick-shape.md) | Run `--recommend` / `ark_recommend` and read the plan |
28
28
  | **How-to** | [Use a gallery starter](how-to-gallery-starter.md) | Copy a phase-1 scaffold that matches your archetype |
29
29
  | **How-to** | [Apply an enthusiast policy pack](how-to-policy-pack.md) | Write `ark.config.json` from a named preset |
30
- | **How-to** | [Install agent gates](how-to-agent-gates.md) | Wire the write gate and `/ark-*` skills |
30
+ | **How-to** | [Install agent gates](how-to-agent-gates.md) | Wire the host-appropriate write path and `/ark-*` skills |
31
31
  | **Reference** | [Archetypes and presets](reference-archetypes.md) | Look up playbook ids, presets, and phase-1 layers |
32
32
  | **Reference** | [Commands and artifacts](reference-commands.md) | `--recommend`, `--write-plan`, `ark-adoption-plan.json`, fix-class JSON |
33
33
  | **Explanation** | [Why application shape matters](explanation-application-shape.md) | Understand shapes vs frameworks and progressive phases |
@@ -60,4 +60,4 @@ Deep teaching example (runnable API + break exercises): [hexagonal-order-api](..
60
60
 
61
61
  - [Agent integration guide](../agent-guide.md)
62
62
  - [Public demos](../demos/)
63
- - [Examples index](../../examples/README.md)
63
+ - [Examples index](../../examples/README.md)
@@ -8,7 +8,7 @@ npx arkgate-check --install-agent-gates --tools claude,cursor,codex,grok
8
8
 
9
9
  Installs:
10
10
 
11
- - Write-gate hook configuration (Claude / Grok PreToolUse; Cursor advisory + MCP)
11
+ - Write-path configuration (Claude/Grok hard PreToolUse; Cursor/Codex advisory MCP)
12
12
  — PreToolUse uses **`--hook-repair`** (W4): on deny, stderr may include
13
13
  `ARK_REPAIR_JSON` / `ARK_AUTOPATCH_JSON` for hosts that re-inject a patch.
14
14
  Still exit 2 / hard block; never silent write. Drop `--hook-repair` for
@@ -24,6 +24,9 @@ Installs:
24
24
  | Codex | `docs/ark-codex-config.toml` + home MCP/prompts |
25
25
  | **Grok Build** | `.grok/config.toml`, `.grok/hooks/`, `.grok/skills/` |
26
26
 
27
+ See the [canonical host support matrix](../../README.md#host-enforcement-support) for the exact
28
+ local, MCP, CI, and repair guarantees. The table above only lists installed paths.
29
+
27
30
  ## Session hint
28
31
 
29
32
  `arkgate-mcp --session-context` appends when governed coverage is low:
@@ -39,7 +42,8 @@ npx arkgate-check --doctor
39
42
  npx arkgate-check --require-gates
40
43
  ```
41
44
 
42
- Doctor JSON (`--doctor --json`) includes **`writePath`**: whether PreToolUse is
45
+ Doctor JSON (`--doctor --json`) includes **`writePath`**: the active host's supported profile,
46
+ installed evidence, and whether PreToolUse is
43
47
  `repair` (machine-readable autoPatch on deny), `reject-only`, `mcp-only`, or
44
48
  `none` — plus `prepareWrite` / `autoPatch` booleans for leads.
45
49
 
@@ -49,4 +53,4 @@ After upgrading the package, refresh skills so agents see the latest plan kinds:
49
53
  npx arkgate-check --install-agent-gates --skills-only --force
50
54
  ```
51
55
 
52
- Full copy-paste setups: [docs/ai-gates.md](../ai-gates.md).
56
+ Full copy-paste setups: [docs/ai-gates.md](../ai-gates.md).
@@ -129,6 +129,9 @@ Or edit `.grok/config.toml` → `args` use `arkgate-mcp`.
129
129
 
130
130
  ### TypeScript imports (runtime / Nest / ESLint only)
131
131
 
132
+ The runtime and Nest surfaces are currently **experimental**; migrating the package name does not
133
+ require adopting them. Static CLI/MCP enforcement remains the supported product path.
134
+
132
135
  ```diff
133
136
  - import { createStrictArkKernelFromConfig } from 'ark-runtime-kernel';
134
137
  + import { createStrictArkKernelFromConfig } from 'arkgate/runtime';
@@ -3,10 +3,10 @@
3
3
  **Product wedge:** write gate · CI gate · co-pilot (plan / loop / skills).
4
4
  **Not the wedge:** the optional in-process runtime kernel.
5
5
 
6
- **Public product site:** [arkgate.online](https://www.arkgate.online/) (promise + only flow).
6
+ **Public product site:** [arkgate.online](https://www.arkgate.online/) (promise + only flow).
7
7
  In-repo `docs/` remains the package/agent reference. Source: GitHub; distribution: npm.
8
8
 
9
- This document is the consumer contract for **what is stable** vs **what is opt-in**.
9
+ This document is the consumer contract for **what is stable** vs **what is experimental**.
10
10
 
11
11
  ---
12
12
 
@@ -16,7 +16,8 @@ This document is the consumer contract for **what is stable** vs **what is opt-i
16
16
  |---------|----------------|-----------------|
17
17
  | **CLI** | `arkgate` / `arkgate-check` (aliases `ark` / `ark-check`) | Flags and human text may improve; **JSON output shapes** for `--json` (check, doctor, plan, coverage, recommend) are stable within a major. Additive fields OK; removals/renames are major. |
18
18
  | **MCP tools** | `arkgate-mcp` / `ark://…` resources | Tool names and primary argument shapes are stable within a major. |
19
- | **`ark.config.json`** | Layer globs, rules, include/exclude, forbiddenGlobals, intent prefixes, `peerIsolation`, `dynamicImportAllowlist`, `safety` thresholds | Schema fields documented in playbooks/examples are stable; new optional fields may appear. |
19
+ | **`ark.config.json`** | Layer globs, rules, include/exclude, forbiddenGlobals, intent prefixes, `peerIsolation`, `dynamicImportAllowlist`, `safety` thresholds | Versioned by `schemaVersion`; unknown fields fail closed and migrations preserve the previous supported major. |
20
+ | **Config JSON Schema** | `arkgate/schema` or `arkgate/schema/ark.config.schema.json` | Stable package resource subpaths for editor completion and contract tooling. |
20
21
  | **Agent skills** | `/ark-*` templates installed by `--install-agent-gates` | Skill *names* and “default flow” are stable; internal skill prose may evolve (e.g. explore dual-plan seed, day-zero origin order). |
21
22
  | **ESLint subpath** | `arkgate/eslint` | Config-driven layer/import rules; loads consumer `ark.config.json`. |
22
23
  | **GitHub Action** | `pedroknigge/arkgate` (see `action.yml`) | The `uses:` tag/SHA selects the checker source; `version` remains an optional exact npm compatibility override. |
@@ -25,20 +26,23 @@ Gates need **no application code imports**. Most projects only use the CLI + MCP
25
26
 
26
27
  ---
27
28
 
28
- ## Opt-in surfaces
29
+ ## Experimental opt-in surfaces
30
+
31
+ These APIs are shipped for evaluation and compatibility, but they are **not production-ready
32
+ product claims**. Static architecture enforcement does not depend on them.
29
33
 
30
34
  | Surface | Import path | Notes |
31
35
  |---------|-------------|--------|
32
- | **Runtime kernel** | **`arkgate/runtime`** (preferred) | Event bus, intents, policies, sagas, outbox, projections, `createArkKernel` / strict helpers. Optional. Not required for architecture enforcement. Built-in stores are **InMemory reference only** (not production durability) — see [production-hardening.md](./production-hardening.md). |
33
- | **Root package barrel** | `arkgate` | Still re-exports the runtime kernel for **compatibility**. Prefer `arkgate/runtime` for new code. Root may be thinned in a future **major**. |
34
- | **NestJS adapter** | `arkgate/nestjs` | Optional peer `@nestjs/common`. Wires a kernel into Nest DI. |
36
+ | **Runtime kernel** | **`arkgate/runtime`** (preferred) | Experimental event bus, intents, policies, sagas, outbox, projections, and strict helpers. Not required for architecture enforcement. Built-in stores are **InMemory reference only** (not production durability) — see [production-hardening.md](./production-hardening.md). |
37
+ | **Root package barrel** | `arkgate` | Still re-exports the experimental runtime kernel for **compatibility**. Prefer `arkgate/runtime` when evaluating it. Root may be thinned in a future **major**. |
38
+ | **NestJS adapter** | `arkgate/nestjs` | Experimental optional peer `@nestjs/common`; wires a kernel into Nest DI. |
35
39
 
36
40
  ---
37
41
 
38
42
  ## Recommended imports
39
43
 
40
44
  ```ts
41
- // Preferred opt-in runtime kernel
45
+ // Preferred path when evaluating the experimental runtime kernel
42
46
  import {
43
47
  createStrictArkKernel,
44
48
  createStrictArkKernelFromConfig,
@@ -51,7 +55,8 @@ import { createStrictArkKernel } from 'arkgate';
51
55
  import { ArkModule, InjectArk } from 'arkgate/nestjs';
52
56
  ```
53
57
 
54
- See [production-hardening.md](./production-hardening.md) for runtime operational guidance.
58
+ See [production-hardening.md](./production-hardening.md) for requirements an eventual
59
+ production deployment would need to satisfy; it is not a readiness certification.
55
60
 
56
61
  ---
57
62
 
@@ -1,7 +1,8 @@
1
1
  # Production Hardening
2
2
 
3
- The optional runtime kernel is imported from **`arkgate/runtime`** (preferred). See
4
- [package-surface.md](package-surface.md).
3
+ The optional runtime kernel is currently **experimental**. This page is a requirements checklist
4
+ for teams evaluating **`arkgate/runtime`**, not a claim that the runtime is production-ready.
5
+ Static ArkGate adoption does not require it. See [package-surface.md](package-surface.md).
5
6
 
6
7
  ## Durability stance (R9)
7
8
 
@@ -42,6 +43,18 @@ passed as the third `execute` argument, but JavaScript cannot stop an operation
42
43
  ignores that signal. Production steps must pass it to network/database clients and keep
43
44
  external effects idempotent; otherwise work may finish after the workflow was marked failed.
44
45
 
46
+ ### Workflow retry boundary
47
+
48
+ `RetryPolicy` applies only while `step.execute` is running, including a timeout from that
49
+ execution. Once `execute` resolves, ArkGate marks the step completed before saving the
50
+ snapshot and recording `workflow.step.completed`. If either post-effect operation fails, the
51
+ workflow enters its failure/compensation path; it does not execute the completed step again.
52
+
53
+ The same fail-closed rule applies when the final `workflow.completed` audit record fails:
54
+ completed steps are compensated when handlers exist, and the workflow ends failed. Production
55
+ effects and compensations must therefore be idempotent, and audit/snapshot stores must be
56
+ operational dependencies rather than best-effort telemetry.
57
+
45
58
  ## Interface Targets
46
59
 
47
60
  | Concern | Interface |