ark-runtime-kernel 1.2.0 → 1.4.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.
package/README.md CHANGED
@@ -3,7 +3,9 @@
3
3
  # 🏛️ Ark — Architectural Runtime Kernel
4
4
 
5
5
  **Stop AI agents (and humans) from quietly breaking your architecture.**<br/>
6
- One machine-readable contract — enforced at write time, merge time, and (optionally) runtime.
6
+ One machine-readable contract — enforced at write time, merge time, and (optionally) runtime.<br/>
7
+ Ships a complete 11-layer architecture you can adopt one layer at a time.
8
+ Gates **Claude Code, Cursor, and Codex** natively — plus rule files for Windsurf, Cline, Copilot, Kiro, and Gemini CLI.
7
9
 
8
10
  [![CI](https://github.com/pedroknigge/ark-runtime-kernel/actions/workflows/ci.yml/badge.svg)](https://github.com/pedroknigge/ark-runtime-kernel/actions/workflows/ci.yml)
9
11
  [![npm](https://img.shields.io/npm/v/ark-runtime-kernel?color=cb3837&label=npm)](https://www.npmjs.com/package/ark-runtime-kernel)
@@ -12,7 +14,7 @@ One machine-readable contract — enforced at write time, merge time, and (optio
12
14
  ![TypeScript](https://img.shields.io/badge/TypeScript-first-3178c6?logo=typescript&logoColor=white)
13
15
  ![Zero deps](https://img.shields.io/badge/dependencies-0-success)
14
16
 
15
- [2-Minute Setup](#2-minute-setup) · [Why Ark](#why-ark-and-not-just-a-linter) · [AI Write Gate](#the-ai-write-gate) · [CI Gate](#ark-check--the-ci-gate) · [Runtime Kernel](#the-runtime-kernel-opt-in) · [Docs](#documentation)
17
+ [2-Minute Setup](#2-minute-setup) · [Why Ark](#why-ark-and-not-just-a-linter) · [11 Layers](#batteries-included-the-11-layer-profile-all-optional) · [AI Write Gate](#the-ai-write-gate) · [CI Gate](#ark-check--the-ci-gate) · [Runtime Kernel](#the-runtime-kernel-opt-in) · [Docs](#documentation)
16
18
 
17
19
  </div>
18
20
 
@@ -34,6 +36,15 @@ npx ark init # asks before generating config, agent gates, and
34
36
  npx ark-check # done: cross-layer imports now fail the check
35
37
  ```
36
38
 
39
+ `ark init` detects your existing layer directories and suggests the missing ones from
40
+ Ark's default 11-layer profile (with their conventional directories), so you see the
41
+ full division before deciding what to adopt. On an empty project it generates the
42
+ complete profile with every layer optional: the check passes immediately, and each
43
+ layer starts being enforced as soon as its directory gains source files. Agents get
44
+ the same guidance — the `ark://manifest` resource includes `suggestedLayers`, and the
45
+ generated `AGENTS.md` carries the placement table, so an agent asked for a saga or a
46
+ background job knows where it belongs before writing it.
47
+
37
48
  Adopting on a codebase that already has violations? Freeze them and ratchet down:
38
49
 
39
50
  ```bash
@@ -41,7 +52,9 @@ npx ark-check --update-baseline # writes .ark-baseline.json — commit it
41
52
  npx ark-check --baseline # only NEW violations fail from now on
42
53
  ```
43
54
 
44
- Then gate your agents (Claude Code shown; [Cursor / Codex / others](docs/ai-gates.md)):
55
+ Then gate your agents (Claude Code shown; [Cursor / Codex / others](docs/ai-gates.md)). If you use
56
+ Codex in an Ark project, register the MCP server early so `ark://manifest` is available during
57
+ generation:
45
58
 
46
59
  ```json
47
60
  // .claude/settings.json
@@ -71,6 +84,39 @@ GitHub Actions, and agent instructions. Existing files are skipped unless you pa
71
84
  The package `postinstall` only prints the next command; it never prompts or writes files
72
85
  during `npm install`. Use `npx ark init --yes` for non-interactive setup.
73
86
 
87
+ ### Updating Ark
88
+
89
+ For projects that already use Ark:
90
+
91
+ ```bash
92
+ npm install -D ark-runtime-kernel@latest
93
+ npx ark-check --root . --config ark.config.json --strict-config
94
+ npm run check:architecture
95
+ ```
96
+
97
+ This updates the local `ark`, `ark-check`, and `ark-mcp` binaries used by npm scripts
98
+ and CI. `npm run check:architecture` is the recommended alias, but it is optional:
99
+ the direct `npx ark-check --root . --config ark.config.json --strict-config` command
100
+ is the real check and works even if the alias has not been added yet.
101
+
102
+ The lockfile controls the version CI gets, so commit the updated `package-lock.json`,
103
+ `pnpm-lock.yaml`, or `yarn.lock`.
104
+
105
+ Generated setup files are intentionally not rewritten during package updates:
106
+ `AGENTS.md`, MCP config, Claude/Cursor settings, Codex notes, and GitHub Actions
107
+ templates stay under your project's control. To add any new starter templates:
108
+
109
+ ```bash
110
+ npx ark-check --install-agent-gates
111
+ ```
112
+
113
+ Existing files are skipped. To regenerate them from the latest templates, review
114
+ your local changes first, then run:
115
+
116
+ ```bash
117
+ npx ark-check --install-agent-gates --force
118
+ ```
119
+
74
120
  ## Why Ark (and not just a linter)?
75
121
 
76
122
  If you only need import-boundary linting in CI, [dependency-cruiser](https://github.com/sverweij/dependency-cruiser), [eslint-plugin-boundaries](https://github.com/javierbrea/eslint-plugin-boundaries), and Nx module boundaries are solid tools. Ark's reason to exist is the **write-time, agent-native half** they don't cover:
@@ -80,6 +126,8 @@ If you only need import-boundary linting in CI, [dependency-cruiser](https://git
80
126
  | Cross-layer import checks in CI | ✅ (TS resolver) | ✅ | ✅ | ✅ |
81
127
  | Blocks AI agents **before** code lands (MCP + hook) | ✅ | ❌ | ❌ | ❌ |
82
128
  | Machine-readable contract for agents (`ark://manifest`) | ✅ | ❌ | ❌ | ❌ |
129
+ | Injects the contract into agent context at session start | ✅ | ❌ | ❌ | ❌ |
130
+ | Forbidden ambient globals per layer (`Date.now` in domain, ...) | ✅ | ❌ | ➖ (generic ESLint) | ❌ |
83
131
  | Event/intent governance (who may publish what) | ✅ | ❌ | ❌ | ❌ |
84
132
  | Baseline ratchet for existing codebases | ✅ | ❌ | ➖ (via ESLint) | ❌ |
85
133
  | Optional runtime enforcement | ✅ | ❌ | ❌ | ❌ |
@@ -89,19 +137,57 @@ If you only need import-boundary linting in CI, [dependency-cruiser](https://git
89
137
 
90
138
  | Gate | Tool | When it runs | What it enforces |
91
139
  |--------------|---------------|-------------------------------|-----------------------------------------------|
92
- | **Write** | `ark-mcp` | Agent PreToolUse (Write/Edit) | Layer rules, unknown intents, forbidden patterns |
93
- | **Merge** | `ark-check` | CI (GitHub Actions etc.) | Cross-layer imports + intent references (real TS resolver) |
140
+ | **Write** | `ark-mcp` | Agent PreToolUse (Write/Edit) | Layer rules, unknown intents, forbidden patterns + globals |
141
+ | **Merge** | `ark-check` | CI (GitHub Actions etc.) | Cross-layer imports + intent references (real TS resolver) + forbidden globals |
94
142
  | **Runtime** | `createArkKernel()` | Running process (opt-in) | Intent registry, event contracts, observed layer flow, policies |
95
143
 
144
+ ## Batteries included: the 11-layer profile (all optional)
145
+
146
+ You don't have to design a layer model before adopting Ark — it ships a complete,
147
+ production-shaped division for Hexagonal + Event-Driven + DDD systems. Every layer is
148
+ **optional by design**: on a fresh project the strict check passes immediately, and each
149
+ layer starts being enforced the moment its directory gains source files. Adopt two
150
+ layers or all eleven; `ark.config.json` is always authoritative and you can rename,
151
+ remove, or re-map any of it.
152
+
153
+ | Layer | Conventional directories | Intent prefixes |
154
+ |-------|--------------------------|-----------------|
155
+ | DomainModel | `domain/` | `Domain.` |
156
+ | ApplicationOrchestration | `application/`, `app/` | `Application.` |
157
+ | PersistenceAdapters | `adapters/persistence/`, `repositories/`, ... | `Adapter.Persistence.`, `Adapter.Repository.` |
158
+ | IntegrationAdapters | `adapters/integration/`, `integrations/`, ... | `Adapter.Integration.`, `Adapter.External.` |
159
+ | WorkflowSagaEngine | `workflows/`, `sagas/` | `Workflow.` |
160
+ | BackgroundJobsScheduling | `jobs/`, `schedules/` | `Job.` |
161
+ | PresentationAdapters | `presentation/`, `adapters/api/`, ... | `Presentation.`, `Adapter.Api.`, ... |
162
+ | ReportingReadModels | `reporting/`, `read-models/`, `projections/` | `Reporting.` |
163
+ | ExtensibilityMetadata | `metadata/`, `extensions/` | `Metadata.` |
164
+ | SecurityAuditObservability | `security/`, `audit/`, `observability/` | `Security.`, `Audit.`, `Observability.` |
165
+ | Kernel | `kernel/` | `Kernel.` |
166
+
167
+ The default rule matrix is strict-deny: only the classic flows are open
168
+ (Presentation→Application, Application→Domain, Workflow→Application/Domain,
169
+ Jobs→Application) and everything else is a violation until you allow it explicitly.
170
+ The profile isn't just for the linter — agents get it too: `ark://manifest` lists the
171
+ layers your project hasn't adopted yet as `suggestedLayers`, so when an agent needs to
172
+ create its first saga or background job, it puts it in the conventional place and adds
173
+ the layer to the config instead of inventing an ungoverned location.
174
+
175
+ ```bash
176
+ npx ark-check --print-config eleven-layer > ark.config.json # the full profile, ready to edit
177
+ ```
178
+
96
179
  ## The AI Write Gate
97
180
 
98
181
  `ark-mcp` is a zero-dependency MCP server + one-shot hook:
99
182
 
100
183
  - **`ark-mcp --hook`** — PreToolUse gate: computes the **post-edit** file content, validates it against your layers, exits 2 with the violations when the write must be blocked. The agent self-corrects.
184
+ - **`ark-mcp --session-context`** — SessionStart injection: prints a compact contract summary (layers, forbidden globals, baseline state) into the agent's context, so it knows the architecture from the first token instead of learning by rejection. Silent no-op outside Ark projects, so it can't leak into other repos.
101
185
  - **`validate_code` tool** — on-demand validation of a snippet, for runtimes without hooks.
102
- - **`ark://manifest` resource** — the architecture as JSON, so agents read the rules *before* generating code instead of learning by rejection.
186
+ - **`ark://manifest` resource** — the architecture as JSON, so agents read the rules *before* generating code.
103
187
 
104
- Copy-paste setups for **Claude Code, Cursor, and OpenAI Codex**: [docs/ai-gates.md](docs/ai-gates.md).
188
+ Copy-paste setups for **Claude Code, Cursor, and OpenAI Codex**, plus instruction-tier
189
+ rule files for **Windsurf, Cline, GitHub Copilot, and Kiro** (Gemini CLI reads the
190
+ generated `AGENTS.md` directly): [docs/ai-gates.md](docs/ai-gates.md).
105
191
 
106
192
  ## `ark-check` — The CI Gate
107
193
 
@@ -117,6 +203,7 @@ npx ark-check --baseline # ratchet mode
117
203
  - String intent references across forbidden layers
118
204
  - Raw `publish()` calls that bypass registered intent creators
119
205
  - Missing / mismatched publish `source` metadata
206
+ - Forbidden ambient globals per layer (`fetch`, `Date.now`, `Math.random`, ...) — see below
120
207
 
121
208
  Violations come with the layer edge, the resolved target, and a fix hint:
122
209
 
@@ -127,6 +214,35 @@ Violations come with the layer edge, the resolved target, and a fix hint:
127
214
  fix: Depend on a port/interface owned by an inner layer instead, or move this code.
128
215
  ```
129
216
 
217
+ ### Domain purity: `forbiddenGlobals`
218
+
219
+ Import rules can't catch code that reaches for an ambient global — an agent can call
220
+ `fetch()` or `Date.now()` in your domain layer without importing anything. Declare the
221
+ globals a layer must not touch and both the write gate and CI enforce it:
222
+
223
+ ```jsonc
224
+ // ark.config.json
225
+ {
226
+ "name": "DomainModel",
227
+ "patterns": ["src/domain/**"],
228
+ "intentPrefixes": ["Domain."],
229
+ "forbiddenGlobals": ["fetch", "process", "Date.now", "Math.random"]
230
+ }
231
+ ```
232
+
233
+ ```
234
+ ✖ FORBIDDEN_GLOBAL src/domain/order.ts:12
235
+ DomainModel must not use the ambient global "Date.now".
236
+ fix: Inject the capability through a port (e.g. a Clock, IdGenerator, or HttpPort).
237
+ ```
238
+
239
+ Entries are either dotted (`"Date.now"` flags exactly that property access) or bare
240
+ (`"console"` flags `console.*`, `fetch(...)`, `new WebSocket(...)`). Detection is
241
+ positional, not scope-aware: mentions in types or import names are never flagged.
242
+ `npx ark init` seeds the domain layer with `["fetch", "process", "Date.now", "Math.random"]`
243
+ (a pure domain does no I/O and is deterministic); add `"console"` or any other global per
244
+ project. Violations participate in the `--baseline` ratchet like every other rule.
245
+
130
246
  ### GitHub Action
131
247
 
132
248
  ```yaml
@@ -145,7 +261,15 @@ import ark from 'ark-runtime-kernel/eslint';
145
261
  export default [ark.configs.recommended];
146
262
  ```
147
263
 
148
- Rules: `ark/no-domain-infra-imports`, `ark/no-raw-event-publish`, `ark/require-publish-source`.
264
+ Rules: `ark/no-domain-infra-imports`, `ark/no-raw-event-publish`, `ark/require-publish-source`,
265
+ `ark/no-forbidden-globals` (not in `recommended` — scope it to your layer directories):
266
+
267
+ ```js
268
+ {
269
+ files: ['src/domain/**'],
270
+ rules: { 'ark/no-forbidden-globals': ['error', { globals: ['fetch', 'process', 'Date.now', 'Math.random'] }] },
271
+ }
272
+ ```
149
273
 
150
274
  ## The Runtime Kernel (opt-in)
151
275