arkgate 2.7.0 → 2.8.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 (40) hide show
  1. package/CHANGELOG.md +45 -1
  2. package/README.md +8 -0
  3. package/bin/lib/agent-gates.mjs +48 -228
  4. package/bin/lib/architecture-scan.mjs +20 -0
  5. package/bin/lib/ast-scan.mjs +232 -4
  6. package/bin/lib/codex-home.mjs +320 -0
  7. package/bin/lib/doctor-plan.mjs +2 -0
  8. package/bin/lib/remediation.mjs +44 -12
  9. package/bin/lib/ts-resolve.mjs +5 -4
  10. package/dist/index.cjs +417 -338
  11. package/dist/index.cjs.map +1 -1
  12. package/dist/index.d.cts +29 -9
  13. package/dist/index.d.ts +29 -9
  14. package/dist/index.js +417 -338
  15. package/dist/index.js.map +1 -1
  16. package/dist/nestjs/index.cjs +452 -373
  17. package/dist/nestjs/index.cjs.map +1 -1
  18. package/dist/nestjs/index.d.cts +1 -1
  19. package/dist/nestjs/index.d.ts +1 -1
  20. package/dist/nestjs/index.js +452 -373
  21. package/dist/nestjs/index.js.map +1 -1
  22. package/dist/runtime/index.cjs +417 -338
  23. package/dist/runtime/index.cjs.map +1 -1
  24. package/dist/runtime/index.d.cts +1 -1
  25. package/dist/runtime/index.d.ts +1 -1
  26. package/dist/runtime/index.js +417 -338
  27. package/dist/runtime/index.js.map +1 -1
  28. package/dist/{types-CP3KkwZt.d.cts → types-CSJhEOk2.d.cts} +34 -0
  29. package/dist/{types-CP3KkwZt.d.ts → types-CSJhEOk2.d.ts} +34 -0
  30. package/docs/agent-guide.md +1 -1
  31. package/docs/ai-gates.md +41 -7
  32. package/docs/brownfield-adoption.md +7 -0
  33. package/docs/demos/03-copilot-autopilot.md +3 -2
  34. package/docs/enthusiast/reference-commands.md +2 -2
  35. package/docs/package-surface.md +1 -1
  36. package/docs/production-hardening.md +11 -4
  37. package/package.json +2 -1
  38. package/server.json +2 -2
  39. package/templates/skills/ark-explain.md +3 -2
  40. package/templates/skills/ark-loop.md +2 -1
@@ -412,17 +412,29 @@ interface AuditQuery {
412
412
  until?: string;
413
413
  limit?: number;
414
414
  }
415
+ /**
416
+ * Pluggable persistence for audit records.
417
+ *
418
+ * **Durability stance (R9):** Default is `InMemoryAuditStore` — reference only, not
419
+ * production durability (lost on restart). Implement this interface for durable audit.
420
+ * See `docs/production-hardening.md`.
421
+ */
415
422
  interface AuditStore {
416
423
  append(record: AuditRecord): MaybePromise<void>;
417
424
  query(query?: AuditQuery): MaybePromise<AuditRecord[]>;
418
425
  clear(): MaybePromise<void>;
419
426
  }
427
+ /**
428
+ * High-level audit API used by the event bus / kernel.
429
+ * Durability is that of the injected `AuditStore` (default in-memory).
430
+ */
420
431
  interface AuditTrail {
421
432
  record(input: AuditRecordInput): Promise<AuditRecord>;
422
433
  query(query?: AuditQuery): Promise<AuditRecord[]>;
423
434
  clear(): Promise<void>;
424
435
  }
425
436
  interface CreateAuditTrailOptions {
437
+ /** Durable store when provided; otherwise `InMemoryAuditStore` (not production durability). */
426
438
  store?: AuditStore;
427
439
  maxRecords?: number;
428
440
  }
@@ -512,6 +524,14 @@ interface OutboxRecord {
512
524
  updatedAt: string;
513
525
  error?: string;
514
526
  }
527
+ /**
528
+ * Pluggable outbox for publish handoff.
529
+ *
530
+ * **Durability stance (R9):** ArkGate ships only a reference in-process store
531
+ * (`InMemoryOutboxStore`) for tests, demos, and single-process development — it does
532
+ * not survive process restarts and is **not production durability**. Inject your own
533
+ * `OutboxStore` (DB, queue, etc.) for real systems. See `docs/production-hardening.md`.
534
+ */
515
535
  interface OutboxStore {
516
536
  enqueue(event: DomainEvent): Promise<OutboxRecord>;
517
537
  markDispatched(id: string): Promise<void>;
@@ -807,6 +827,13 @@ interface ProjectionCheckpoint {
807
827
  lastCorrelationId?: string;
808
828
  updatedAt?: string;
809
829
  }
830
+ /**
831
+ * Pluggable projection/read-model state.
832
+ *
833
+ * **Durability stance (R9):** Default `InMemoryReadModelStore` is reference-only (not
834
+ * production durability). Inject a durable store for production. See
835
+ * `docs/production-hardening.md`.
836
+ */
810
837
  interface ReadModelStore {
811
838
  load<State = unknown>(name: string): MaybePromise<State | undefined>;
812
839
  save<State = unknown>(name: string, state: State): MaybePromise<void>;
@@ -933,6 +960,13 @@ interface WorkflowSnapshot<P extends SagaContext = SagaContext> {
933
960
  completedAt?: string;
934
961
  error?: string;
935
962
  }
963
+ /**
964
+ * Pluggable saga/workflow snapshot store.
965
+ *
966
+ * **Durability stance (R9):** Default `InMemoryWorkflowStore` is reference-only (not
967
+ * production durability). Inject a durable store for production. See
968
+ * `docs/production-hardening.md`.
969
+ */
936
970
  interface WorkflowStore {
937
971
  save<P extends SagaContext>(snapshot: WorkflowSnapshot<P>): MaybePromise<void>;
938
972
  get<P extends SagaContext = SagaContext>(id: string): MaybePromise<WorkflowSnapshot<P> | undefined>;
@@ -412,17 +412,29 @@ interface AuditQuery {
412
412
  until?: string;
413
413
  limit?: number;
414
414
  }
415
+ /**
416
+ * Pluggable persistence for audit records.
417
+ *
418
+ * **Durability stance (R9):** Default is `InMemoryAuditStore` — reference only, not
419
+ * production durability (lost on restart). Implement this interface for durable audit.
420
+ * See `docs/production-hardening.md`.
421
+ */
415
422
  interface AuditStore {
416
423
  append(record: AuditRecord): MaybePromise<void>;
417
424
  query(query?: AuditQuery): MaybePromise<AuditRecord[]>;
418
425
  clear(): MaybePromise<void>;
419
426
  }
427
+ /**
428
+ * High-level audit API used by the event bus / kernel.
429
+ * Durability is that of the injected `AuditStore` (default in-memory).
430
+ */
420
431
  interface AuditTrail {
421
432
  record(input: AuditRecordInput): Promise<AuditRecord>;
422
433
  query(query?: AuditQuery): Promise<AuditRecord[]>;
423
434
  clear(): Promise<void>;
424
435
  }
425
436
  interface CreateAuditTrailOptions {
437
+ /** Durable store when provided; otherwise `InMemoryAuditStore` (not production durability). */
426
438
  store?: AuditStore;
427
439
  maxRecords?: number;
428
440
  }
@@ -512,6 +524,14 @@ interface OutboxRecord {
512
524
  updatedAt: string;
513
525
  error?: string;
514
526
  }
527
+ /**
528
+ * Pluggable outbox for publish handoff.
529
+ *
530
+ * **Durability stance (R9):** ArkGate ships only a reference in-process store
531
+ * (`InMemoryOutboxStore`) for tests, demos, and single-process development — it does
532
+ * not survive process restarts and is **not production durability**. Inject your own
533
+ * `OutboxStore` (DB, queue, etc.) for real systems. See `docs/production-hardening.md`.
534
+ */
515
535
  interface OutboxStore {
516
536
  enqueue(event: DomainEvent): Promise<OutboxRecord>;
517
537
  markDispatched(id: string): Promise<void>;
@@ -807,6 +827,13 @@ interface ProjectionCheckpoint {
807
827
  lastCorrelationId?: string;
808
828
  updatedAt?: string;
809
829
  }
830
+ /**
831
+ * Pluggable projection/read-model state.
832
+ *
833
+ * **Durability stance (R9):** Default `InMemoryReadModelStore` is reference-only (not
834
+ * production durability). Inject a durable store for production. See
835
+ * `docs/production-hardening.md`.
836
+ */
810
837
  interface ReadModelStore {
811
838
  load<State = unknown>(name: string): MaybePromise<State | undefined>;
812
839
  save<State = unknown>(name: string, state: State): MaybePromise<void>;
@@ -933,6 +960,13 @@ interface WorkflowSnapshot<P extends SagaContext = SagaContext> {
933
960
  completedAt?: string;
934
961
  error?: string;
935
962
  }
963
+ /**
964
+ * Pluggable saga/workflow snapshot store.
965
+ *
966
+ * **Durability stance (R9):** Default `InMemoryWorkflowStore` is reference-only (not
967
+ * production durability). Inject a durable store for production. See
968
+ * `docs/production-hardening.md`.
969
+ */
936
970
  interface WorkflowStore {
937
971
  save<P extends SagaContext>(snapshot: WorkflowSnapshot<P>): MaybePromise<void>;
938
972
  get<P extends SagaContext = SagaContext>(id: string): MaybePromise<WorkflowSnapshot<P> | undefined>;
@@ -195,7 +195,7 @@ npx arkgate-check --install-agent-gates --tools claude,cursor,codex,grok
195
195
  |------|------------|-----|-------------|
196
196
  | Claude Code | PreToolUse hook | `.mcp.json` / `claude mcp add` | `.claude/skills/<name>/SKILL.md` |
197
197
  | Cursor | Advisory (rules + MCP) | `.cursor/mcp.json` | `.cursor/commands/` |
198
- | OpenAI Codex | MCP + CI | `~/.codex/config.toml` | `$CODEX_HOME/prompts` (`--codex-home`) |
198
+ | OpenAI Codex | MCP + CI | `$CODEX_HOME/config.toml` (global; absolute `--root`; multi-project → secondary `ark_<slug>` unless `--force`) | `$CODEX_HOME/prompts` (`--codex-home`) |
199
199
  | **Grok Build** | PreToolUse hook (`.grok/hooks/`) | `.grok/config.toml` + `.mcp.json` | `.grok/skills/<name>/SKILL.md` |
200
200
 
201
201
  Full copy-paste setups: [ai-gates.md](ai-gates.md). Skill inventory: main [README](../README.md#agent-skills-ark-).
package/docs/ai-gates.md CHANGED
@@ -189,20 +189,54 @@ Your hard backstop in Cursor is CI: `ark-check` fails the PR on anything that sl
189
189
 
190
190
  Recommended for Ark projects.
191
191
 
192
- `~/.codex/config.toml`:
192
+ Unlike Claude/Cursor (project-local MCP files), **Codex loads MCP servers only from
193
+ `$CODEX_HOME/config.toml`** (default `~/.codex/config.toml`) — a **global** home file.
194
+ Hand-editing with relative `--root .` is wrong: Codex does not use the project as cwd, so
195
+ `.` resolves against the launch directory. Prefer absolute paths, or let Ark write them:
196
+
197
+ ```bash
198
+ npx ark-check --install-agent-gates --tools codex
199
+ # optional: install /ark-* slash prompts into $CODEX_HOME/prompts
200
+ npx ark-check --install-agent-gates --codex-home
201
+ ```
202
+
203
+ Example shape (absolute paths — also what install writes):
193
204
 
194
205
  ```toml
195
206
  [mcp_servers.ark]
196
207
  command = "npx"
197
- args = ["ark-mcp", "--root", ".", "--config", "ark.config.json"]
208
+ args = ["arkgate-mcp", "--root", "/absolute/path/to/project", "--config", "/absolute/path/to/project/ark.config.json"]
209
+ ```
210
+
211
+ Then **restart Codex** — it does not hot-load MCP servers. Expect resource `ark://manifest`
212
+ and tools `validate_code`, `ark_check`, `ark_coverage`, `ark_place`.
213
+
214
+ Same model as Cursor for enforcement: MCP for discovery/validation, `ark-check` in CI as
215
+ the hard gate. Register the MCP server as soon as the repo is adopted.
216
+
217
+ ### Multi-project Codex (home config last-wins)
218
+
219
+ `[mcp_servers.ark]` is a **single primary** binding. If project A is already registered and
220
+ you install gates for project B **without** `--force`, Ark does **not** silently steal
221
+ primary A. It writes a **scoped secondary** table:
222
+
223
+ ```toml
224
+ [mcp_servers.ark] # primary — still project A
225
+ # ...
226
+
227
+ [mcp_servers.ark_proj-b_a1b2c3d4] # secondary — basename + path hash (no slug collisions)
228
+ # absolute --root for B
198
229
  ```
199
230
 
200
- Same model as Cursor: MCP for discovery/validation, `ark-check` in CI as the hard gate.
201
- For Ark projects, register the MCP server as soon as the repo is adopted so the agent
202
- has the contract available from the first edit.
231
+ | Goal | Command |
232
+ |------|---------|
233
+ | Add B without moving primary | `ark-check --install-agent-gates --tools codex` (no `--force`) |
234
+ | Make B the primary binding | `ark-check --install-agent-gates --tools codex --force` |
235
+ | Doctor: primary points at another permanent project | gap id `codex-home-multi-project` (warn if no secondary yet; info if scoped table already present) |
203
236
 
204
- `ark-check --install-agent-gates --tools codex` auto-merges absolute paths into
205
- `~/.codex/config.toml` and can install `/ark-*` prompts with `--codex-home`.
237
+ `ark-check --doctor` surfaces the multi-project state so you are not left thinking B owns
238
+ `ark://manifest` when only a secondary table exists. Temp/upgrade primary roots are still
239
+ rewritten fail-closed (not multi-project).
206
240
 
207
241
  ## Grok Build (xAI)
208
242
 
@@ -71,6 +71,13 @@ violations — the ratchet only moves toward zero.
71
71
  - **Value import of pure type module** (`targetTypeOnlyExports` — plan:
72
72
  `import-type-from-pure-type-module`): convert static import to `import type`. Not safe for
73
73
  `require()` / dynamic `import()`.
74
+ - **Named type exports from mixed modules** (`namedBindingsTypeOnly` — plan:
75
+ `import-type-of-type-exports`): value-syntax `import { Row }` / `export { Row }` where every
76
+ binding is an `export type` / `interface` on the target — convert to `import type` /
77
+ `export type`. Still **judgment** when any binding is a value, dual-space name
78
+ (`export type Foo` + `export const Foo`), the target has top-level side effects (including
79
+ impure value-export initializers like `export const db = connect()`), or the edge is
80
+ `require` / dynamic `import()`.
74
81
  - **Raw infrastructure access** (value coupling — always **judgment**): relocate data-access
75
82
  **verbatim** into a repository/adapter. Same query bytes = same behavior; do NOT rewrite the
76
83
  query. If CODEOWNERS reserves the data layer, migrate one route as a pattern and hand bulk
@@ -40,8 +40,9 @@ npx ark-check --plan --json # { ok, plan: { goal, counts, steps } }
40
40
  ```
41
41
 
42
42
  Each step is tagged `mechanical-safe` / `judgment` / `deferred` with a `confidence`,
43
- `rationale`, and often `remediationKind`. Only three kinds are auto-safe: type-only type move,
44
- pure-type **file** relocate, and `import type` of pure-type modules. `goal.met` is true only when
43
+ `rationale`, and often `remediationKind`. Auto-safe kinds: type-only type move, pure-type **file**
44
+ relocate, `import type` of pure-type modules, and named type-export imports from mixed modules
45
+ (`import-type-of-type-exports`). `goal.met` is true only when
45
46
  there are no active violations **and** governed coverage is meaningful — so a clean plan that
46
47
  checks almost nothing is not "done."
47
48
 
@@ -51,7 +51,7 @@ arkgate-check --watch
51
51
 
52
52
  | `class` | Agent may auto-apply? | Examples (`remediationKind`) |
53
53
  |---------|----------------------|------------------------------|
54
- | `mechanical-safe` | Yes (validate + rollback) | `type-only-import-move`, `pure-type-file-relocate`, `import-type-from-pure-type-module` |
54
+ | `mechanical-safe` | Yes (validate + rollback) | `type-only-import-move`, `pure-type-file-relocate`, `import-type-from-pure-type-module`, `import-type-of-type-exports` |
55
55
  | `judgment` | No — propose | value imports, ports, infra relocate, cycles |
56
56
  | `deferred` | No | unclear shape |
57
57
 
@@ -62,4 +62,4 @@ When present on violations:
62
62
  - `fixClass` — e.g. `port-inversion`, `file-move`
63
63
  - `effort` — `small` | `medium`
64
64
  - `enthusiastHint` — plain English fix guidance
65
- - plan enrichment: `class`, `remediationKind`, `typeOnly`, `sourcePureTypeModule`, `targetTypeOnlyExports`
65
+ - plan enrichment: `class`, `remediationKind`, `typeOnly`, `sourcePureTypeModule`, `targetTypeOnlyExports`, `namedBindingsTypeOnly`
@@ -26,7 +26,7 @@ Gates need **no application code imports**. Most projects only use the CLI + MCP
26
26
 
27
27
  | Surface | Import path | Notes |
28
28
  |---------|-------------|--------|
29
- | **Runtime kernel** | **`arkgate/runtime`** (preferred) | Event bus, intents, policies, sagas, outbox, projections, `createArkKernel` / strict helpers. Optional. Not required for architecture enforcement. |
29
+ | **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). |
30
30
  | **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**. |
31
31
  | **NestJS adapter** | `arkgate/nestjs` | Optional peer `@nestjs/common`. Wires a kernel into Nest DI. |
32
32
 
@@ -3,11 +3,17 @@
3
3
  The optional runtime kernel is imported from **`arkgate/runtime`** (preferred). See
4
4
  [package-surface.md](package-surface.md).
5
5
 
6
- Ark's built-in stores are intentionally in-memory defaults. They are appropriate for tests,
7
- local development, examples, and single-process demos. Production systems should provide
6
+ ## Durability stance (R9)
7
+
8
+ **ArkGate does not ship production-durable adapters.** Built-in stores are **reference
9
+ InMemory-only** — appropriate for tests, local development, examples, and single-process
10
+ demos. They lose all state on process restart. Production systems **must** inject their
11
+ own implementations of the store interfaces (or accept that data is ephemeral).
12
+
13
+ Ark's built-in stores are intentionally in-memory defaults. Production systems should provide
8
14
  stores that match their durability, ordering, retention, and operational requirements.
9
15
 
10
- ## In-Memory Defaults
16
+ ## In-Memory Defaults (reference only — not production durability)
11
17
 
12
18
  These defaults do not survive process restarts:
13
19
 
@@ -16,7 +22,8 @@ These defaults do not survive process restarts:
16
22
  - `InMemoryReadModelStore`
17
23
  - `InMemoryWorkflowStore`
18
24
 
19
- Use them only when losing state is acceptable.
25
+ Use them only when losing state is acceptable. JSDoc on `OutboxStore`, `AuditStore`,
26
+ `ReadModelStore`, and `WorkflowStore` restates this stance at the type level.
20
27
 
21
28
  ## Production Store Checklist
22
29
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "arkgate",
3
- "version": "2.7.0",
3
+ "version": "2.8.1",
4
4
  "description": "ArkGate — architecture co-pilot for AI TypeScript (write gate, CI gate, plan/loop)",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -75,6 +75,7 @@
75
75
  "check:cli-pure": "node scripts/generate-cli-pure.mjs --check",
76
76
  "test:ts-compat": "node scripts/ts-compat-matrix.mjs 5.9.3 && node scripts/ts-compat-matrix.mjs 6.0.3 && node scripts/ts-compat-matrix.mjs 7.0.2",
77
77
  "eval:agent": "node eval/run.mjs",
78
+ "eval:corpus": "node eval/validate-corpus.mjs",
78
79
  "eval:comparative": "node eval/comparative-run.mjs",
79
80
  "clean": "rm -rf dist",
80
81
  "release:npm": "node scripts/release-npm.mjs",
package/server.json CHANGED
@@ -6,12 +6,12 @@
6
6
  "url": "https://github.com/pedroknigge/arkgate",
7
7
  "source": "github"
8
8
  },
9
- "version": "2.7.0",
9
+ "version": "2.8.1",
10
10
  "packages": [
11
11
  {
12
12
  "registryType": "npm",
13
13
  "identifier": "arkgate",
14
- "version": "2.7.0",
14
+ "version": "2.8.1",
15
15
  "runtimeHint": "npx",
16
16
  "transport": {
17
17
  "type": "stdio"
@@ -47,8 +47,9 @@ dependency direction, matrix, violations, enforcement points, Ark fitness score,
47
47
  **Senior diagnostics** block (coupling fan-in/out, deny density, purity surface, pattern
48
48
  forensics, baseline taxonomy) for tech leads.
49
49
 
50
- When explaining the **plan**, name the three `mechanical-safe` remediation kinds only
51
- (type-only move, pure-type file relocate, `import type` of pure-type modules) — everything
50
+ When explaining the **plan**, name the four `mechanical-safe` remediation kinds only
51
+ (type-only move, pure-type file relocate, `import type` of pure-type modules,
52
+ `import-type-of-type-exports` for named type exports from mixed modules) — everything
52
53
  else is judgment/deferred and must not be auto-applied.
53
54
 
54
55
  ## Spoken / written explanation
@@ -32,8 +32,9 @@ validating every change with `ark-check` and rolling back regressions.
32
32
  | `type-only-import-move` | Move type to owning layer; re-export for back-compat |
33
33
  | `pure-type-file-relocate` | Relocate pure-type file to owning layer (or rename out of false Domain globs) |
34
34
  | `import-type-from-pure-type-module` | Convert value import of pure-type module to `import type` |
35
+ | `import-type-of-type-exports` | Convert value-syntax named import/export of type-only exports from a mixed module to `import type` / `export type` |
35
36
 
36
- Never auto: value imports, dynamic import/require, mixed modules, forbidden globals, cycles, infra moves.
37
+ Never auto: value imports (including mixed bindings with values), dynamic import/require, forbidden globals, cycles, infra moves.
37
38
 
38
39
  ## Steps
39
40