arkgate 2.11.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.
- package/CHANGELOG.md +147 -0
- package/README.md +70 -41
- package/bin/ark-check.mjs +95 -36
- package/bin/ark-mcp.mjs +11 -5
- package/bin/ark-shared.mjs +88 -56
- package/bin/ark.mjs +97 -29
- package/bin/lib/agent-gates.mjs +79 -2093
- package/bin/lib/architecture-scan.mjs +8 -0
- package/bin/lib/ci-and-commands.mjs +392 -0
- package/bin/lib/codex-home.mjs +7 -0
- package/bin/lib/config-contract.mjs +331 -0
- package/bin/lib/deploy-path.mjs +205 -0
- package/bin/lib/doctor-plan.mjs +43 -16
- package/bin/lib/enforcement-profiles.mjs +97 -0
- package/bin/lib/gate-files.mjs +223 -0
- package/bin/lib/hook-templates.mjs +99 -0
- package/bin/lib/host-support-matrix.mjs +77 -0
- package/bin/lib/install-migrate.mjs +473 -0
- package/bin/lib/mcp-adoption.mjs +455 -0
- package/bin/lib/open-html.mjs +75 -0
- package/bin/lib/presets.mjs +6 -2
- package/bin/lib/safety-diagnostics.mjs +31 -11
- package/bin/lib/skill-install.mjs +323 -0
- package/bin/lib/ts-resolve.mjs +2 -1
- package/bin/lib/typescript-host.mjs +88 -0
- package/bin/lib/weakest-link.mjs +417 -0
- package/bin/lib/write-path-capabilities.mjs +182 -0
- package/bin/lib/write-path-detect.mjs +101 -0
- package/dist/configContract-iBLxx5Tz.d.cts +53 -0
- package/dist/configContract-iBLxx5Tz.d.ts +53 -0
- package/dist/eslint/index.cjs +375 -13
- package/dist/eslint/index.cjs.map +1 -1
- package/dist/eslint/index.d.cts +30 -20
- package/dist/eslint/index.d.ts +30 -20
- package/dist/eslint/index.js +375 -13
- package/dist/eslint/index.js.map +1 -1
- package/dist/index.cjs +723 -61
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +95 -5
- package/dist/index.d.ts +95 -5
- package/dist/index.js +716 -61
- package/dist/index.js.map +1 -1
- package/dist/nestjs/index.cjs +150 -42
- package/dist/nestjs/index.cjs.map +1 -1
- package/dist/nestjs/index.d.cts +2 -1
- package/dist/nestjs/index.d.ts +2 -1
- package/dist/nestjs/index.js +150 -42
- package/dist/nestjs/index.js.map +1 -1
- package/dist/runtime/index.cjs +723 -61
- package/dist/runtime/index.cjs.map +1 -1
- package/dist/runtime/index.d.cts +3 -2
- package/dist/runtime/index.d.ts +3 -2
- package/dist/runtime/index.js +716 -61
- package/dist/runtime/index.js.map +1 -1
- package/dist/{types-BZ17b9i5.d.cts → types-BxBwnBpC.d.cts} +9 -36
- package/dist/{types-BZ17b9i5.d.ts → types-Wcs_l1_J.d.ts} +9 -36
- package/docs/agent-guide.md +43 -21
- package/docs/ai-gates.md +53 -18
- package/docs/configuration.md +97 -0
- package/docs/enthusiast/README.md +3 -3
- package/docs/enthusiast/how-to-agent-gates.md +7 -3
- package/docs/migrate-from-ark-runtime-kernel.md +3 -0
- package/docs/package-surface.md +22 -10
- package/docs/production-hardening.md +15 -2
- package/docs/threat-model.md +65 -0
- package/docs/typescript-support.md +3 -3
- package/package.json +15 -2
- package/schemas/ark.config.schema.json +750 -0
- package/server.json +2 -2
- package/templates/hooks/pre-commit-ark +37 -0
- package/templates/skills/ark-autopilot.md +77 -45
- package/templates/skills/ark-coverage.md +2 -2
- package/templates/skills/ark-explain.md +2 -1
- package/templates/skills/ark-explore.md +135 -34
- package/templates/skills/ark-runtime.md +8 -5
- package/templates/skills/ark-upgrade.md +36 -16
- 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
|
-
|
|
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
|
-
|
|
301
|
-
|
|
302
|
-
|
|
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
|
-
|
|
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
|
-
|
|
301
|
-
|
|
302
|
-
|
|
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;
|
package/docs/agent-guide.md
CHANGED
|
@@ -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
|
|
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:
|
|
@@ -49,13 +51,23 @@ MCP `ark_recommend` and the `/ark-architect` skill.
|
|
|
49
51
|
### Terminal onboarding (Phase B)
|
|
50
52
|
|
|
51
53
|
```bash
|
|
52
|
-
npx ark
|
|
54
|
+
npx ark start --yes # preferred: shape → contract → **day-zero origin** → gates → plan
|
|
55
|
+
npx ark init --archetype crud-product --yes # non-interactive: shape → preset → origin → gates
|
|
53
56
|
npx ark init # TTY wizard: pick application shape (1–8), not a framework
|
|
54
57
|
npx ark-check --doctor # includes "New here?" when coverage is low or config is fresh
|
|
55
58
|
npx ark-check --report beginner.html --beginner # simplified HTML for enthusiasts
|
|
56
59
|
npx ark-check --watch # debounced re-check when governed files change
|
|
57
60
|
```
|
|
58
61
|
|
|
62
|
+
**Day-zero origin (2.12+):** as soon as `ark.config.json` exists, `ark start` / `ark init`
|
|
63
|
+
freeze `.ark/reports/origin.*` **before** writing agent docs, skills, or CI templates.
|
|
64
|
+
Later `--report` shows evolution vs that snapshot. Do not `--reset-origin` unless the user
|
|
65
|
+
explicitly wants a new baseline.
|
|
66
|
+
|
|
67
|
+
**Default agent co-pilot:** `/ark-autopilot` (explore-first, dual plan A remediation + B
|
|
68
|
+
pattern bets). Recon without applying: `/ark-explore`. Never treat empty `--plan` as
|
|
69
|
+
“architecture healthy” without the explore pass.
|
|
70
|
+
|
|
59
71
|
`ark init --archetype <id>` maps playbook ids to named presets (`hexagonal`, `layered`,
|
|
60
72
|
`feature-sliced`, `monorepo`). With `--yes` and no archetype, Ark auto-selects from
|
|
61
73
|
`--recommend` scoring.
|
|
@@ -206,18 +218,21 @@ npx arkgate-check --install-agent-gates --tools claude,cursor,codex,grok
|
|
|
206
218
|
# alias: npx ark-check --install-agent-gates --tools claude,cursor,codex,grok
|
|
207
219
|
```
|
|
208
220
|
|
|
209
|
-
| Host |
|
|
210
|
-
|
|
211
|
-
| Claude Code |
|
|
212
|
-
| Cursor |
|
|
213
|
-
| OpenAI Codex |
|
|
214
|
-
| **Grok Build** |
|
|
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` |
|
|
215
227
|
|
|
216
|
-
|
|
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-).
|
|
217
230
|
|
|
218
|
-
##
|
|
231
|
+
## Experimental runtime: contract discovery
|
|
219
232
|
|
|
220
|
-
|
|
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,
|
|
221
236
|
policies, event bus, audit trail, event contracts, outbox, observability,
|
|
222
237
|
projections, metadata, workflow engine, and 11-layer architecture profile:
|
|
223
238
|
|
|
@@ -234,8 +249,8 @@ const contract = ark.manifest().toJSON();
|
|
|
234
249
|
// contract.observability, projections
|
|
235
250
|
```
|
|
236
251
|
|
|
237
|
-
|
|
238
|
-
|
|
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).
|
|
239
254
|
|
|
240
255
|
Agents should read `contract` and `ark.observability.report()` before generating or modifying code.
|
|
241
256
|
|
|
@@ -344,11 +359,17 @@ if (!result.valid) {
|
|
|
344
359
|
}
|
|
345
360
|
```
|
|
346
361
|
|
|
347
|
-
Passing the `typescript` module enables built-in AST checks for
|
|
348
|
-
`metadata.source`, and source-layer mismatches.
|
|
349
|
-
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).
|
|
350
367
|
|
|
351
|
-
|
|
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`.
|
|
352
373
|
|
|
353
374
|
Use `ark-check` in CI for repository-level checks that need real file paths:
|
|
354
375
|
|
|
@@ -451,8 +472,9 @@ Example config:
|
|
|
451
472
|
```
|
|
452
473
|
|
|
453
474
|
`ark-check` resolves imports through the TypeScript module resolver against your
|
|
454
|
-
`tsconfig.json` — relative, path-alias (e.g. `@infra/db`), package imports,
|
|
455
|
-
`import()`, and `require()` — plus string intent
|
|
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
|
|
456
478
|
`publish()` calls, publish calls without `metadata.source`, and source intent literals
|
|
457
479
|
whose resolved layer differs from the publishing file layer. Pass `--tsconfig <path>` to force one config
|
|
458
480
|
for every file; otherwise each source file uses the nearest `tsconfig.json` above it (like
|
|
@@ -613,8 +635,8 @@ Register the server itself in `.mcp.json` so the agent can read `ark://manifest`
|
|
|
613
635
|
}
|
|
614
636
|
```
|
|
615
637
|
|
|
616
|
-
|
|
617
|
-
must
|
|
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.
|
|
618
640
|
|
|
619
641
|
## Recommended Agent Workflow
|
|
620
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
|
-
|
|
5
|
-
|
|
6
|
-
|
|
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
|
|
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)
|
|
44
|
-
|
|
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
|
|
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
|
|
255
|
-
the hard
|
|
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
|
|
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.
|
|
279
|
-
|
|
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
|
|
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)
|
|
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,
|
|
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
|
-
|
|
397
|
-
diagnostics.
|
|
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
|
|
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
|
|
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-
|
|
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`**:
|
|
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';
|
package/docs/package-surface.md
CHANGED
|
@@ -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
|
|
9
|
+
This document is the consumer contract for **what is stable** vs **what is experimental**.
|
|
10
10
|
|
|
11
11
|
---
|
|
12
12
|
|
|
@@ -16,8 +16,9 @@ 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 |
|
|
20
|
-
| **
|
|
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. |
|
|
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. |
|
|
23
24
|
|
|
@@ -25,20 +26,23 @@ Gates need **no application code imports**. Most projects only use the CLI + MCP
|
|
|
25
26
|
|
|
26
27
|
---
|
|
27
28
|
|
|
28
|
-
##
|
|
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) |
|
|
33
|
-
| **Root package barrel** | `arkgate` | Still re-exports the runtime kernel for **compatibility**. Prefer `arkgate/runtime`
|
|
34
|
-
| **NestJS adapter** | `arkgate/nestjs` |
|
|
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
|
|
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
|
|
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
|
|
|
@@ -73,3 +78,10 @@ See [production-hardening.md](./production-hardening.md) for runtime operational
|
|
|
73
78
|
| Bugfix with no contract change | **patch** |
|
|
74
79
|
| Prefer `arkgate/runtime` over root (docs only; root still exports) | **patch/minor** |
|
|
75
80
|
| Remove root kernel re-exports | **major** (with migration notes) |
|
|
81
|
+
|
|
82
|
+
---
|
|
83
|
+
|
|
84
|
+
## Release notes (maintainers)
|
|
85
|
+
|
|
86
|
+
Ship notes for a version live under [releases/](./releases/) (e.g. [2.12.0.md](./releases/2.12.0.md)).
|
|
87
|
+
Publish path: signed annotated tag → GitHub Release → `publish-npm.yml` (see [CONTRIBUTING.md](../CONTRIBUTING.md)).
|