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 +132 -8
- package/bin/ark-check.mjs +355 -48
- package/bin/ark-mcp.mjs +147 -3
- package/bin/ark-shared.mjs +53 -1
- package/dist/eslint/index.cjs +49 -1
- package/dist/eslint/index.cjs.map +1 -1
- package/dist/eslint/index.d.cts +4 -1
- package/dist/eslint/index.d.ts +4 -1
- package/dist/eslint/index.js +49 -2
- package/dist/eslint/index.js.map +1 -1
- package/dist/index.cjs +50 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +7 -1
- package/dist/index.d.ts +7 -1
- package/dist/index.js +50 -2
- package/dist/index.js.map +1 -1
- package/dist/nestjs/index.cjs +1 -1
- package/dist/nestjs/index.cjs.map +1 -1
- package/dist/nestjs/index.js +1 -1
- package/dist/nestjs/index.js.map +1 -1
- package/docs/agent-guide.md +9 -0
- package/docs/ai-gates.md +76 -0
- package/package.json +8 -2
- package/server.json +7 -7
- package/docs/blog/how-i-stopped-claude-from-breaking-my-architecture.md +0 -85
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
|
[](https://github.com/pedroknigge/ark-runtime-kernel/actions/workflows/ci.yml)
|
|
9
11
|
[](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
|

|
|
13
15
|

|
|
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
|
|
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
|
|
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
|
|