arkgate 4.6.1 → 4.6.3

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 (54) hide show
  1. package/CHANGELOG.md +50 -1
  2. package/README.md +17 -11
  3. package/bin/ark-check-runtime.mjs +23 -127
  4. package/bin/ark-mcp-runtime.mjs +75 -49
  5. package/bin/ark.mjs +19 -78
  6. package/bin/lib/ci-and-commands.mjs +4 -3
  7. package/bin/lib/doctor-next-actions.mjs +92 -0
  8. package/bin/lib/doctor-plan.mjs +59 -69
  9. package/bin/lib/first-run-help.mjs +221 -0
  10. package/bin/lib/host-support-matrix.mjs +13 -15
  11. package/bin/lib/start-preview.mjs +17 -10
  12. package/bin/lib/status-command.mjs +6 -1
  13. package/bin/lib/status-manifest.mjs +6 -0
  14. package/bin/lib/write-path-capabilities.mjs +9 -5
  15. package/bin/lib/write-path-detect.mjs +3 -3
  16. package/dist/index.cjs +19 -19
  17. package/dist/index.d.ts +6 -1
  18. package/dist/index.js +22 -22
  19. package/docs/README.md +5 -5
  20. package/docs/agent-guide.md +19 -14
  21. package/docs/ai-gates.md +29 -20
  22. package/docs/develop.md +4 -1
  23. package/docs/enthusiast/how-to-agent-gates.md +2 -2
  24. package/docs/package-surface.md +4 -3
  25. package/docs/product-voice.md +4 -4
  26. package/docs/threat-model.md +5 -1
  27. package/docs/use.md +9 -4
  28. package/package.json +2 -2
  29. package/server.json +3 -3
  30. package/templates/agent-skills/README.md +1 -1
  31. package/templates/agent-skills/ark-adopt/SKILL.md +14 -5
  32. package/templates/agent-skills/ark-architect/SKILL.md +2 -2
  33. package/templates/agent-skills/ark-autopilot/SKILL.md +12 -5
  34. package/templates/agent-skills/ark-contract/SKILL.md +1 -1
  35. package/templates/agent-skills/ark-coverage/SKILL.md +10 -8
  36. package/templates/agent-skills/ark-explain/SKILL.md +3 -2
  37. package/templates/agent-skills/ark-explore/SKILL.md +13 -4
  38. package/templates/agent-skills/ark-fix/SKILL.md +1 -1
  39. package/templates/agent-skills/ark-loop/SKILL.md +1 -1
  40. package/templates/agent-skills/ark-place/SKILL.md +10 -1
  41. package/templates/agent-skills/ark-think/SKILL.md +3 -2
  42. package/templates/agent-skills/ark-upgrade/SKILL.md +10 -3
  43. package/templates/skills/ark-adopt.md +14 -5
  44. package/templates/skills/ark-architect.md +2 -2
  45. package/templates/skills/ark-autopilot.md +12 -5
  46. package/templates/skills/ark-contract.md +1 -1
  47. package/templates/skills/ark-coverage.md +10 -8
  48. package/templates/skills/ark-explain.md +3 -2
  49. package/templates/skills/ark-explore.md +13 -4
  50. package/templates/skills/ark-fix.md +1 -1
  51. package/templates/skills/ark-loop.md +1 -1
  52. package/templates/skills/ark-place.md +10 -1
  53. package/templates/skills/ark-think.md +3 -2
  54. package/templates/skills/ark-upgrade.md +10 -3
package/CHANGELOG.md CHANGED
@@ -5,6 +5,53 @@ in the immutable pre-2.0 archive linked below.
5
5
 
6
6
  ## Unreleased
7
7
 
8
+ ## 4.6.3 — 2026-08-18
9
+
10
+ **Patch** over **4.6.2**. Codex CLI and local ChatGPT Desktop/App Server now get a
11
+ runtime-proven pre-write block for complete `apply_patch` calls. ArkGate accepts the current
12
+ `tool_input.command` payload, while incomplete, hosted, specialized, shell/direct, and human
13
+ write paths remain CI-backed. **No required config migration.**
14
+
15
+ **Status: prepared** (not yet on npm `latest`; see `docs/releases/4.6.3.md`).
16
+
17
+ ### Changed
18
+
19
+ - **Codex hook payload:** current `PreToolUse` `apply_patch` bodies are read from
20
+ `tool_input.command`; historical patch/input/content fields stay compatible.
21
+ - **Operation-scoped hard write:** a complete trusted and runtime-observed local patch can report
22
+ `hard:true` and exit `2` before disk mutation. Hook files alone stay unverified.
23
+ - **Honesty surfaces:** host matrix, doctor/status, `--require-write-hook codex`, onboarding,
24
+ upgrade self-service, skills, and public docs now share the same boundary.
25
+ - **All-path backstop:** required `arkgate-check --strict-merge` CI remains mandatory. MCP stays
26
+ advisory and repair reinjection is not claimed.
27
+
28
+ ## 4.6.2 — 2026-08-16
29
+
30
+ **Patch** over **4.6.1**. First-contact copy: a newcomer (human or coding agent) sees what
31
+ to do in a few lines — `arkgate` / `arkgate-check --help`, start wrap-up, doctor light +
32
+ #1, write-gate deny, SessionStart, MCP tool order, and the five doors. Same 13 skill names.
33
+ **No required config migration.**
34
+
35
+ **Status: published** (on npm `latest`; see `docs/releases/4.6.2.md`).
36
+
37
+ ### Changed
38
+
39
+ - **First-run help:** `arkgate --help` and `arkgate-check --help` are short; encyclopedia
40
+ text is `--help --all`. `arkgate upgrade --help` is preview vs apply.
41
+ - **Start wrap-up:** doctor → `/ark-adopt` session 0 (not `/ark-autopilot` as step 1).
42
+ - **Doctor:** operating-mode light + primary next action #1 print first.
43
+ - **Write-gate deny:** `blocked {file} — {reason}` then `Next:` (move the import / `/ark-place`).
44
+ Rule id on a following line. No “call ark_manifest”.
45
+ - **Agents:** SessionStart points at `/ark-adopt` or `arkgate-check --doctor`. `ark_identity`
46
+ is first. `ark_check` is a scan (pass/fail/incomplete), not a yes/no architecture score.
47
+ `server.json` first sentence is the layers definition.
48
+ - **Skills:** five doors open with when + steps. Shortcuts are not the first-run menu.
49
+ - **Status:** `nextAction` is `map-leftover-design` when leftover design work remains
50
+ (never `stay-enforced`).
51
+ - **npm `description`:** `One architecture config. One check. One coach.` (not “co-pilot”).
52
+ - **`docs/use.md`:** Cursor hard-blocks Write/StrReplace when hooks are trusted;
53
+ Codex/OpenCode stay advisory.
54
+
8
55
  ## 4.6.1 — 2026-08-14
9
56
 
10
57
  **Patch** over **4.6.0**. Five-door autonomy (skills write or map in-turn; CLI is sensor +
@@ -12,7 +59,7 @@ gate) plus team parliament (law vs feature: stewards, mixed-PR deny, ratchet vs
12
59
  base, cheap `--changed` check). Same 13 skill names. Steward identity is a GitHub handle or
13
60
  email, not git `user.name`. **No required config migration.**
14
61
 
15
- **Status: prepared** (not yet on npm `latest`; see `docs/releases/4.6.1.md`).
62
+ **Status: published** (on npm `latest`; see `docs/releases/4.6.1.md`).
16
63
 
17
64
  ### Added
18
65
 
@@ -28,6 +75,8 @@ email, not git `user.name`. **No required config migration.**
28
75
 
29
76
  - Doctor, compact router, and public lanes prefer the five doors. Historical changelogs stay
30
77
  as shipped.
78
+ - Published 4.6.1 tarball `README.md` still banners 4.6.0 (packed at `1eadc96` before the
79
+ pointer flip). Tree README on `main` is current. No 4.6.2 for that banner.
31
80
 
32
81
  ## 4.6.0 — 2026-08-12
33
82
 
package/README.md CHANGED
@@ -16,8 +16,9 @@ and makes sure a “green” check means something real.
16
16
 
17
17
  </div>
18
18
 
19
- > **ArkGate 4.6.0** is on npm `latest` — clearer language + shared agent home skills.
20
- > [4.6.0 notes](docs/releases/4.6.0.md) · [4.5.7](docs/releases/4.5.7.md) · [4.5.6](docs/releases/4.5.6.md) · [Docs hub](docs/README.md) · [Product voice](docs/product-voice.md)
19
+ > **ArkGate 4.6.2** is on npm `latest` — first-contact copy (what to do in a few lines).
20
+ > **4.6.3 is prepared** Codex CLI/local Desktop hard-block complete trusted `apply_patch`.
21
+ > [4.6.3 notes](docs/releases/4.6.3.md) · [4.6.2](docs/releases/4.6.2.md) · [4.6.1](docs/releases/4.6.1.md) · [4.6.0](docs/releases/4.6.0.md) · [Docs hub](docs/README.md) · [Product voice](docs/product-voice.md)
21
22
 
22
23
  ---
23
24
 
@@ -116,14 +117,15 @@ Details: [docs/use.md](docs/use.md).
116
117
  | Claude Code | **Hard** block for listed ops (PreToolUse `Write` / `Edit` / `MultiEdit`) when installed + trusted | Advisory; the agent must call it | **Required GitHub status context** running `arkgate-check --strict-merge` (alias `ark-check`) | Emitted on hook deny; host must re-inject (hard path when installed + trusted) |
117
118
  | Grok Build | **Hard** block for listed ops (PreToolUse `write` / `search_replace` (plus aliases)) when installed + trusted | Advisory; the agent must call it | **Required GitHub status context** running `arkgate-check --strict-merge` (alias `ark-check`) | Emitted on hook deny; host must re-inject (hard path when installed + trusted) |
118
119
  | Google Antigravity | **Hard** block for listed ops (PreToolUse `write_to_file` / `replace_file_content` / `multi_replace_file_content`) when installed + trusted | Advisory; the agent must call it | **Required GitHub status context** running `arkgate-check --strict-merge` (alias `ark-check`) | Emitted on hook deny; host must re-inject (hard path when installed + trusted) |
119
- | Cursor | **Hard** block for listed ops (preToolUse `Write` / `StrReplace`) when installed + trusted | Advisory; the agent must call it | **Required GitHub status context** running `arkgate-check --strict-merge` (alias `ark-check`) | Envelope may emit (`--hook-repair`); reinjection **not** guaranteed (advisory host) |
120
- | OpenAI Codex | **Advisory / best-effort** at write (not equivalent to Claude/Grok/Cursor hard block) | Advisory; the agent must call it | **Required GitHub status context** running `arkgate-check --strict-merge` (alias `ark-check`) | Envelope may emit (`--hook-repair`); reinjection **not** guaranteed (advisory host) |
120
+ | Cursor | **Hard** block for listed ops (preToolUse `Write` / `StrReplace`) when installed + trusted | Advisory; the agent must call it | **Required GitHub status context** running `arkgate-check --strict-merge` (alias `ark-check`) | Envelope may emit (`--hook-repair`); reinjection **not** guaranteed |
121
+ | OpenAI Codex | **Hard** block for listed ops (PreToolUse `apply_patch` in Codex CLI and local ChatGPT Desktop/App Server) when installed + trusted | Advisory; the agent must call it | **Required GitHub status context** running `arkgate-check --strict-merge` (alias `ark-check`) | Envelope may emit (`--hook-repair`); reinjection **not** guaranteed |
121
122
  | OpenCode | **Advisory / best-effort** at write (MCP + optional plugin; not a hard boundary) | Advisory; the agent must call it | **Required GitHub status context** running `arkgate-check --strict-merge` (alias `ark-check`) | No hard-boundary payload |
122
123
 
123
124
  **Read the CI column:** for every host, the repository-wide hard guarantee is a **required**
124
125
  GitHub **status context** that runs the CLI — not “CI file present,” and not the CLI binary name alone.
125
- Codex/OpenCode never get a fake hard write claim. Cursor hard write covers only listed
126
- `preToolUse` ops when `.cursor/hooks.json` is installed and trusted Shell/Tab/human edits still rely on CI.
126
+ Codex hard write covers only a complete local `apply_patch`; Cursor covers only listed
127
+ `preToolUse` ops. In both cases the project hook must be installed + trusted, while shell/direct
128
+ filesystem writes, hosted or specialized opt-out paths, and human edits still rely on CI.
127
129
 
128
130
  This table describes the supported profile **after its files are installed and the host loads/trusts them**. A hard local boundary covers only the listed hook operations; alternate tools, direct filesystem writes, and human edits still rely on CI. MCP validation is advisory because the agent must call it. The CI check blocks a merge only when the repository makes that status required. Repair **envelopes** may be emitted without reinjection being guaranteed; silent auto-apply never happens. Run `arkgate-check --doctor` (or `ark-check --doctor`) for the evidence actually detected in the current repository.
129
131
  <!-- arkgate-host-support:end -->
@@ -132,10 +134,11 @@ This table describes the supported profile **after its files are installed and t
132
134
 
133
135
  The split above is a deliberate trade-off, not a gap. ArkGate validates at the earliest boundary
134
136
  each host offers and enforces at the earliest boundary a repository can make non-bypassable: the
135
- required merge status. Hard hooks (Claude Code, Grok Build, Google Antigravity) deny the listed
136
- write operations at write time; advisory surfaces (MCP, rules, OpenCode plugins) coach the agent
137
- while it works. But any local boundary can be routed around — another tool, a direct filesystem
138
- write, a human edit so the only guarantee ArkGate claims for every path is the
137
+ required merge status. Hard hooks (Claude Code, Grok Build, Google Antigravity, Cursor, and
138
+ Codex’s complete local `apply_patch`) deny their listed write operations at write time; advisory
139
+ surfaces (MCP, rules, OpenCode plugins) coach the agent while it works. But any local boundary can
140
+ be routed aroundanother tool, a hosted/specialized path, a direct filesystem write, or a human
141
+ edit — so the only guarantee ArkGate claims for every path is the
139
142
  `arkgate-check --strict-merge` check, and only when the repository makes that status required.
140
143
  Local checks optimize feedback speed; the merge gate owns correctness.
141
144
 
@@ -213,7 +216,10 @@ for real systems. Details: [docs/production-hardening.md](docs/production-harden
213
216
  | Config · package surface · TS | [configuration](docs/configuration.md) · [package-surface](docs/package-surface.md) · [typescript-support](docs/typescript-support.md) |
214
217
  | Brownfield | [docs/brownfield-adoption.md](docs/brownfield-adoption.md) |
215
218
  | Security | [SECURITY.md](SECURITY.md) |
216
- | Current release (4.6.0 on npm `latest`) | [docs/releases/4.6.0.md](docs/releases/4.6.0.md) · [CHANGELOG](CHANGELOG.md) |
219
+ | Current release (4.6.2 on npm `latest`) | [docs/releases/4.6.2.md](docs/releases/4.6.2.md) · [CHANGELOG](CHANGELOG.md) |
220
+ | Prepared patch (4.6.3) | [docs/releases/4.6.3.md](docs/releases/4.6.3.md) |
221
+ | Prior (4.6.1) | [docs/releases/4.6.1.md](docs/releases/4.6.1.md) |
222
+ | Prior (4.6.0) | [docs/releases/4.6.0.md](docs/releases/4.6.0.md) |
217
223
  | Prior (4.5.7) | [docs/releases/4.5.7.md](docs/releases/4.5.7.md) |
218
224
  | Prior (4.5.0) | [docs/releases/4.5.0.md](docs/releases/4.5.0.md) |
219
225
  | Prior (4.4.0) | [docs/releases/4.4.0.md](docs/releases/4.4.0.md) |
@@ -95,6 +95,7 @@ import {
95
95
  writeArkRulesTemplates,
96
96
  } from './lib/presets.mjs';
97
97
  import { loadArkConfigContract, parseArkConfigJson } from './lib/config-contract.mjs';
98
+ import { checkUsage, checkUsageAll } from './lib/first-run-help.mjs';
98
99
  import { createAdapterResult } from './lib/adapter-contract.mjs';
99
100
  import { collectGovernedFiles, normalize, walk } from './lib/scan-files.mjs';
100
101
  import { configWarning } from './lib/config-warnings.mjs';
@@ -166,12 +167,13 @@ function parseArgs(argv) {
166
167
  noOpenReport: false,
167
168
  version: false,
168
169
  help: false,
170
+ all: false,
169
171
  followConfigRoot: false,
170
172
  };
171
173
  const requireValue = (flag, index) => {
172
174
  const value = argv[index + 1];
173
175
  if (value === undefined || value.startsWith('-')) {
174
- throw new Error(`Missing value for ${flag}. Run ark-check --help for usage.`);
176
+ throw new Error(`Missing value for ${flag}. Run arkgate-check --help for usage.`);
175
177
  }
176
178
  return value;
177
179
  };
@@ -269,8 +271,9 @@ function parseArgs(argv) {
269
271
  else if (arg === '--print-config') args.printConfig = requireValue(arg, i++);
270
272
  else if (arg === '--tsconfig') args.tsconfig = requireValue(arg, i++);
271
273
  else if (arg === '--help' || arg === '-h') args.help = true;
274
+ else if (arg === '--all') args.all = true;
272
275
  else if (arg === '--version' || arg === '-V') args.version = true;
273
- else throw new Error(`Unknown argument: ${arg}. Run ark-check --help for usage.`);
276
+ else throw new Error(`Unknown argument: ${arg}. Run arkgate-check --help for usage.`);
274
277
  }
275
278
  return args;
276
279
  }
@@ -283,116 +286,6 @@ function displayPathFromRoot(root, absPath) {
283
286
  }
284
287
  return rel.split(path.sep).join('/');
285
288
  }
286
- function usage() {
287
- return [
288
- 'Usage: arkgate-check | ark-check (identical bins; product name ArkGate)',
289
- ' ark-check --version',
290
- ' ark-check --root <project> --config <ark.config.json> [--manifest <ark.manifest.json>] [--tsconfig <tsconfig.json>] [--strict-merge | --strict | --strict-config] [--policy-base <file> | --policy-base-ref <git-ref>] [--policy-ack <file>] [--fail-on-new-smells --base-ref <git-ref>] [--contract-diff] [--contract-session] [--changed] [--against <git-ref>] [--base <git-ref>] [--persona touch|contributor|agent|steward] [--author <id>] [--require-gates] [--require-write-hook <host>] [--json] [--baseline [file]] [--report [file.html]] [--no-cache]',
291
- ' ark-check --doctor [--json] [--resident] [--fail-on-new-smells --base-ref <git-ref>] read-only diagnosis; resident JSON falls back cold',
292
- ' ark-check --coverage [--json] per-layer file counts + full unclassified list (report only, exit 0)',
293
- ' ark-check --plan [--json] classified remediation plan (mechanical-safe / judgment / deferred) + goal; report only',
294
- ' ark-check --rules-inventory [--json] brownfield rules inventory (AR13; deterministic candidates, not a score)',
295
- ' ark-check --recommend [--json] [--write-plan] application-shape plan; --write-plan emits ark-adoption-plan.json',
296
- ' ark-check --list-policy-packs enthusiast packs (hexagonal, layered, feature-sliced, monorepo, ui-surface, vertical-slice, ddd-bounded-contexts)',
297
- ' ark-check --apply-policy-pack <id> [--force] write ark.config.json from templates/policy-packs/ (uses preset factory)',
298
- ' ark-check --suggest-include [--json] propose include roots (TS packages / workspaces)',
299
- ' ark-check --adopt-contract [--write] expand include + layer patterns from ungoverned dirs (never bare lib→Presentation)',
300
- ' ark-check --migrate-contract [--write] additive P0-A retrofit: inject app/api/** → Application when missing',
301
- ' ark-check --ratchet-cores when raw graph is green (0 violations; baseline ignored), set optional:false on populated cores only (writes ark.config.json)',
302
- ' ark-check --watch re-run the check when governed files change (debounced)',
303
- ' ark-check --report [file.html] [--beginner] [--reset-origin] [--no-archive] [--open|--no-open]',
304
- ' HTML report + snapshots under .ark/reports/ (origin once, latest each run, history JSON)',
305
- ' Best-effort open in browser (local TTY). No-op if open fails. --no-open / ARK_NO_OPEN_REPORT=1 to skip; --open forces open.',
306
- ' ark-check --init [--preset hexagonal|layered|feature-sliced|monorepo|ui-surface|vertical-slice|ddd-bounded-contexts|vite-vercel-spa|clean-architecture|onion-architecture] [--force] [--follow-config-root]',
307
- ' --follow-config-root On writes (init/install-agent-gates/migrate --write/…), adopt walked-up monorepo config root (default: keep explicit --root)',
308
- ' ark-check --install-agent-gates [--tools claude,cursor,codex,grok] [--require-write-hook <host>] [--skills-only] [--codex-home] [--claude-home] [--grok-home] [--agent-homes] [--force]',
309
- ' ark-check --update-baseline [file] freeze current violations (default .ark-baseline.json)',
310
- ' ark-check --print-config eleven-layer',
311
- '',
312
- 'Adopting Ark in an existing codebase? Run --update-baseline once to freeze existing',
313
- 'violations, commit the baseline file, and gate CI with --baseline: only NEW violations',
314
- 'fail the check, so the ratchet only moves toward zero.',
315
- '',
316
- 'Team parliament: law files (ark.config / arkrules / .ark-baseline.json) cannot ship in',
317
- 'the same diff as product source. --changed --base <ref> checks touched files only.',
318
- '--against <ref> ratchets new keys vs that ref\'s baseline. --contract-session is a',
319
- 'steward law-only PR. Loosen / baseline-grow need stewards[] + --author when set.',
320
- '',
321
- '--init scans the project for the built-in layer directory conventions (src/domain,',
322
- 'src/application, src/adapters/persistence, ...) and writes an ark.config.json covering',
323
- 'only the layers that actually exist, with the default rules filtered to those layers.',
324
- 'Undetected profile layers are printed as suggestions with their conventional',
325
- 'directories. When nothing is detected, the full 11-layer starter profile is written',
326
- 'instead (all layers optional, anchored at src/), so the strict check passes today and',
327
- 'each layer starts being enforced as soon as its directory gains source files.',
328
- '',
329
- 'Resolves relative, tsconfig path-alias, and package imports via the TypeScript',
330
- 'module resolver, then checks each resolved cross-layer import against the rules.',
331
- 'Path aliases resolve against the NEAREST tsconfig.json above each source file, so',
332
- 'monorepo packages with per-package configs work under a single --root. Pass',
333
- '--tsconfig to force one config for every file. If no tsconfig is found, path',
334
- 'aliases are unavailable but relative/package imports still resolve.',
335
- '',
336
- 'The correctness path resolves and parses one complete candidate on every invocation.',
337
- 'Legacy node_modules/.cache/ark-check.json files are ignored. --no-cache remains an',
338
- 'accepted compatibility no-op; the identity-keyed warm snapshot is introduced in Z07.',
339
- '',
340
- 'Config shape:',
341
- '{',
342
- ' "include": ["src"],',
343
- ' // optional: "exclude": ["**/vendor/**"], "excludeGenerated": false (default skips *.gen.ts / *.generated.ts)',
344
- ' "layers": [',
345
- ' { "name": "DomainModel", "patterns": ["src/domain/**"], "intentPrefixes": ["Domain."],',
346
- ' "forbiddenGlobals": ["fetch", "process", "Date.now", "Math.random"] }',
347
- ' ],',
348
- ' "rules": [{ "from": "DomainModel", "to": "PersistenceAdapters", "allowed": false }]',
349
- '}',
350
- '',
351
- 'Config warnings are advisory by default and are included in JSON output.',
352
- 'Use --strict-config to make config warnings fail the check.',
353
- 'Use --strict-merge for the fail-closed CI profile: --strict-config + --require-gates',
354
- 'plus the security diagnostics surfaced by doctor. --strict is a compatibility alias.',
355
- 'This merge profile never depends on an editor/agent hook.',
356
- 'When a Git merge base is available, --strict-merge classifies the ark.config.json',
357
- 'transition. Weakening or judgment-required findings fail unless --policy-ack names',
358
- 'every finding and is bound to both policy hashes. Use --policy-base/--policy-base-ref',
359
- 'for an explicit comparison; ARK_POLICY_BASE_REF is the CI environment equivalent.',
360
- 'Add --require-write-hook claude|grok|antigravity|cursor to validate a hard local write',
361
- 'boundary for that specific host. Codex and OpenCode expose advisory MCP (plus best-effort',
362
- 'hooks where applicable) and the shared CI check; merge blocking requires repository policy',
363
- 'to make that status required.',
364
- '',
365
- '--require-gates implies --strict-config and fails when the Ark contract in AGENTS.md,',
366
- 'the project-rooted Ark server in .mcp.json, or fail-closed CI is missing/invalid.',
367
- 'Included but unclassified source files therefore stay red instead of false-green.',
368
- '',
369
- '--install-agent-gates writes AGENTS.md, .mcp.json, and the CI workflow for every',
370
- 'project, plus tool-specific templates. Known tools: claude, cursor, codex, grok',
371
- '(Claude/Grok/Antigravity/Cursor hard-write hooks when covered; Codex advisory MCP;',
372
- 'shared CI check for all) and',
373
- 'windsurf, cline, copilot, kiro, roo, continue, gemini',
374
- '(instruction-tier rule files derived from the same contract).',
375
- 'It also installs the /ark-* skills shipped in templates/skills/ into each',
376
- 'detected tool\'s command location (.claude/skills/, .cursor/commands/,',
377
- '.agents/skills/ (Codex REPO catalog), .grok/skills/, .windsurf/workflows/,',
378
- '.clinerules/workflows/, .github/prompts/).',
379
- 'Kiro, Roo, Continue, and Gemini have no command mechanism and receive only their',
380
- 'rule file. Existing files are never overwritten without --force, so re-running',
381
- 'after an update only adds what is missing. --skills-only restricts the write to',
382
- 'just the /ark-* skills (safe to --force-refresh — it leaves a customized AGENTS.md,',
383
- 'settings, and CI workflow untouched).',
384
- 'Pass --tools to pick which tool configs to write; otherwise they are auto-detected',
385
- 'from their config directories (.claude/, .cursor/, .codex/, .grok/, .windsurf/,',
386
- '.clinerules/, .kiro/, .roo/, .continue/, .gemini/; copilot is explicit-only).',
387
- 'claude+cursor+codex+grok are written when nothing is detected.',
388
- '',
389
- 'Generate a starter 11-layer config:',
390
- ' ark-check --print-config eleven-layer > ark.config.json',
391
- '',
392
- 'Install agent + CI enforcement templates:',
393
- ' ark-check --install-agent-gates',
394
- ].join('\n');
395
- }
396
289
 
397
290
  function readConfig(root, configPath) {
398
291
  const fullPath = path.isAbsolute(configPath)
@@ -1148,7 +1041,7 @@ async function main() {
1148
1041
  process.exit(0);
1149
1042
  }
1150
1043
  if (args.help) {
1151
- console.log(usage());
1044
+ console.log(args.all ? checkUsageAll() : checkUsage());
1152
1045
  return;
1153
1046
  }
1154
1047
  if (args.init) {
@@ -1706,20 +1599,23 @@ async function main() {
1706
1599
  }),
1707
1600
  }
1708
1601
  : null;
1709
- const currentSnapshot = buildReportSnapshot({
1710
- root,
1711
- config,
1712
- coverage,
1713
- violations: activeViolations,
1714
- ok,
1715
- suppressed: suppressed.length,
1716
- version: arkPackageVersion(),
1717
- fileCountByLayer,
1718
- enforcement: enforcementForReport,
1719
- score: fitness.score,
1720
- mode: fitness.mode,
1721
- improvementCompass: reportCompass,
1722
- });
1602
+ const currentSnapshot = {
1603
+ ...buildReportSnapshot({
1604
+ root,
1605
+ config,
1606
+ coverage,
1607
+ violations: activeViolations,
1608
+ ok,
1609
+ suppressed: suppressed.length,
1610
+ version: arkPackageVersion(),
1611
+ fileCountByLayer,
1612
+ enforcement: enforcementForReport,
1613
+ score: fitness.score,
1614
+ mode: fitness.mode,
1615
+ improvementCompass: reportCompass,
1616
+ }),
1617
+ leftoverDesignWork: designDepth?.designFitness?.designWeak === true,
1618
+ };
1723
1619
  const reportPayload = {
1724
1620
  root,
1725
1621
  config,
@@ -358,6 +358,7 @@ function mapAntigravityToolCall(toolCall) {
358
358
  * Antigravity: { toolCall: { name, args: { TargetFile, CodeContent, … } } }
359
359
  * Cursor: { tool_name, tool_input, hook_event_name?, workspace_roots? }
360
360
  * Write uses `contents`; StrReplace maps to Edit (path/old_string/new_string).
361
+ * Codex: { tool_name: "apply_patch", tool_input: { command: "*** Begin Patch..." } }
361
362
  */
362
363
  function normalizeHookPayload(payload, grokHookEvent = Boolean(process.env.GROK_HOOK_EVENT)) {
363
364
  const antigravityStyle =
@@ -643,6 +644,25 @@ function emitHostAllow(output, { antigravityStyle, cursorStyle }) {
643
644
  emitCursorAllow(output, cursorStyle);
644
645
  }
645
646
 
647
+ /**
648
+ * Socket-style write-gate deny: two lines first. Pass/fail, no score.
649
+ * Rule id stays on a following line, not the first sentence.
650
+ */
651
+ function formatWriteGateDeny({ file, reason, ruleId, nextAction, extraLines = [] }) {
652
+ const target = file || 'this write';
653
+ const why = String(reason || 'this change breaks the architecture layers').replace(/\s+/g, ' ').trim();
654
+ const next =
655
+ nextAction && /place|move|import|port/i.test(nextAction)
656
+ ? nextAction
657
+ : 'Move the import or run /ark-place. Do not weaken ark.config.json.';
658
+ const lines = [`blocked ${target} — ${why}`, `Next: ${next}`];
659
+ if (ruleId) lines.push(`[${ruleId}]`);
660
+ for (const extra of extraLines) {
661
+ if (extra) lines.push(extra);
662
+ }
663
+ return lines.join('\n');
664
+ }
665
+
646
666
  function runHookPayload(payload, gate, config, args, ts, attemptContext, output = processHookOutput()) {
647
667
  const { toolName, toolInput, grokStyle, antigravityStyle, cursorStyle, operation } =
648
668
  normalizeHookPayload(
@@ -650,7 +670,10 @@ function runHookPayload(payload, gate, config, args, ts, attemptContext, output
650
670
  attemptContext?.grokHookEvent ?? Boolean(process.env.GROK_HOOK_EVENT)
651
671
  );
652
672
  if (toolName === 'ApplyPatch') {
653
- const patch = toolInput.patch ?? toolInput.input ?? toolInput.content;
673
+ // Current Codex CLI/Desktop hook schema uses tool_input.command. Keep the
674
+ // historical fields for older clients and existing integration fixtures.
675
+ const patch =
676
+ toolInput.command ?? toolInput.patch ?? toolInput.input ?? toolInput.content;
654
677
  const parsedPatch = codexPatchWrites(patch, args.root);
655
678
  // Codex ApplyPatch is only preflighted when Ark can reconstruct every file operation.
656
679
  // An incomplete reconstruction must not be mislabeled as atomic or hard enforcement.
@@ -796,17 +819,20 @@ function runHookPayload(payload, gate, config, args, ts, attemptContext, output
796
819
  emitHostAllow(output, { antigravityStyle, cursorStyle });
797
820
  return;
798
821
  }
799
- const message = [
800
- `Ark architecture gate blocked this complete ${toolName} (${changes.length} governed file(s)):`,
801
- ...result.diagnostics.map(
802
- (diagnostic) =>
803
- `- [${diagnostic.ruleId}] ${diagnostic.message}\n Next action: ${diagnostic.nextAction}`
804
- ),
805
- ...(designDelta && !designDelta.valid
806
- ? formatDesignDeltaBlock(designDelta).split('\n').slice(1)
807
- : []),
808
- 'No project file was written. Fix the complete patch and retry.',
809
- ].join('\n');
822
+ const first = result.diagnostics[0];
823
+ const message = formatWriteGateDeny({
824
+ file: `${changes.length} file(s)`,
825
+ reason: first?.message || `this ${toolName} breaks the architecture layers`,
826
+ ruleId: first?.ruleId,
827
+ nextAction: first?.nextAction,
828
+ extraLines: [
829
+ ...result.diagnostics.slice(1).map((d) => `[${d.ruleId}] ${d.message}`),
830
+ ...(designDelta && !designDelta.valid
831
+ ? formatDesignDeltaBlock(designDelta).split('\n').slice(1)
832
+ : []),
833
+ 'No project file was written. Fix the complete patch and retry.',
834
+ ],
835
+ });
810
836
  output.stderr(`${message}\n`);
811
837
  if (args.hookRepair) {
812
838
  output.stderr(
@@ -926,13 +952,7 @@ function runHookPayload(payload, gate, config, args, ts, attemptContext, output
926
952
  })),
927
953
  });
928
954
 
929
- const lines = adapterResult.diagnostics.map(
930
- (diagnostic) =>
931
- `- [${diagnostic.ruleId}] ${diagnostic.message}${diagnostic.location.line ? ` (line ${diagnostic.location.line})` : ''}\n Next action: ${diagnostic.nextAction}`
932
- );
933
- // Surface the per-violation fix hints (the gate carries them in `suggestion`,
934
- // but the hook was dropping them). Dedupe so two infra violations sharing one
935
- // hint — e.g. the mayImportInfrastructure escape hatch — print it once.
955
+ const firstDiagnostic = adapterResult.diagnostics[0];
936
956
  const suggestions = [
937
957
  ...new Set(combinedViolations.map((violation) => violation.suggestion).filter(Boolean)),
938
958
  ];
@@ -940,27 +960,34 @@ function runHookPayload(payload, gate, config, args, ts, attemptContext, output
940
960
  // W4: structured repair payload is opt-in (--hook-repair / ARK_HOOK_REPAIR).
941
961
  // Default remains hard block with prose only — hosts that cannot re-inject stay clean.
942
962
  const repair = Boolean(args.hookRepair);
943
- const message = [
944
- `Ark architecture gate blocked this write to ${rel}${layer ? ` (layer: ${layer})` : ''}:`,
945
- ...lines,
946
- ...(suggestions.length > 0 ? ['Fix:', ...suggestions.map((s) => ` ${s}`)] : []),
947
- ...(autoPatch && repair
948
- ? [
949
- `autoPatch available (${autoPatch.remediationKind}, confidence ${autoPatch.confidence}): ` +
950
- 'apply the patched source from ARK_AUTOPATCH_JSON / ARK_REPAIR_JSON on stderr' +
951
- (grokStyle ? ' (or autoPatch in the deny JSON on stdout)' : '') +
952
- ' instead of re-drafting. Gate still denies this write (never silent apply).',
953
- ]
954
- : []),
955
- ...(autoPatch && !repair
956
- ? [
957
- `Mechanical-safe autoPatch is available (${autoPatch.remediationKind}). ` +
958
- 'Enable repair payload with ARK_HOOK_REPAIR=1 or --hook-repair to receive ' +
959
- 'machine-readable source (still hard-blocks; host re-injects).',
960
- ]
961
- : []),
962
- 'Fix the violations and retry. Call the project-bound ark_manifest MCP tool for the architecture contract.',
963
- ].join('\n');
963
+ const message = formatWriteGateDeny({
964
+ file: rel,
965
+ reason: firstDiagnostic?.message || (layer ? `${layer} write breaks the layers` : 'this write breaks the layers'),
966
+ ruleId: firstDiagnostic?.ruleId,
967
+ nextAction: firstDiagnostic?.nextAction,
968
+ extraLines: [
969
+ ...adapterResult.diagnostics.slice(1).map(
970
+ (diagnostic) =>
971
+ `[${diagnostic.ruleId}] ${diagnostic.message}${diagnostic.location.line ? ` (line ${diagnostic.location.line})` : ''}`
972
+ ),
973
+ ...(suggestions.length > 0 ? suggestions.map((s) => `Fix: ${s}`) : []),
974
+ ...(autoPatch && repair
975
+ ? [
976
+ `autoPatch available (${autoPatch.remediationKind}, confidence ${autoPatch.confidence}): ` +
977
+ 'apply the patched source from ARK_AUTOPATCH_JSON / ARK_REPAIR_JSON on stderr' +
978
+ (grokStyle ? ' (or autoPatch in the deny JSON on stdout)' : '') +
979
+ ' instead of re-drafting. Gate still denies this write (never silent apply).',
980
+ ]
981
+ : []),
982
+ ...(autoPatch && !repair
983
+ ? [
984
+ `Mechanical-safe autoPatch is available (${autoPatch.remediationKind}). ` +
985
+ 'Enable repair payload with ARK_HOOK_REPAIR=1 or --hook-repair to receive ' +
986
+ 'machine-readable source (still hard-blocks; host re-injects).',
987
+ ]
988
+ : []),
989
+ ],
990
+ });
964
991
  output.stderr(message + '\n');
965
992
 
966
993
  if (repair) {
@@ -1403,7 +1430,7 @@ function printSessionContext(config, profile, forbiddenGlobals, args, configPath
1403
1430
  const governedPercent = coverage?.coverage?.governed?.percent ?? coverage?.governed?.percent;
1404
1431
  if (shouldShowNewHereNudge(args.root, configPath, governedPercent, false)) {
1405
1432
  lines.push('');
1406
- lines.push('New to Ark? Run /ark-adopt (or /ark-architect) or: ark-check --recommend');
1433
+ lines.push('New to Ark? /ark-adopt or: arkgate-check --doctor');
1407
1434
  }
1408
1435
 
1409
1436
  process.stdout.write(`${lines.join('\n')}\n`);
@@ -2018,9 +2045,9 @@ export async function runArkMcp({ hookInput } = {}) {
2018
2045
  {
2019
2046
  name: 'ark_identity',
2020
2047
  description:
2021
- 'Return the canonical ArkGate project, config, contract, and live MCP runtime identity. ' +
2022
- 'Pass project.expectedRoot and/or expectedProjectId to verify this process before ' +
2023
- 'trusting any architecture evidence.',
2048
+ 'First call. Prove this MCP process is the right project: pass project.expectedRoot ' +
2049
+ '(exact absolute root) and reuse the returned projectId. Do this before any other Ark tool. ' +
2050
+ 'A missing, unmatched, or different root means restart the host and use the local CLI.',
2024
2051
  inputSchema: { type: 'object', properties: {} },
2025
2052
  },
2026
2053
  {
@@ -2063,11 +2090,10 @@ export async function runArkMcp({ hookInput } = {}) {
2063
2090
  {
2064
2091
  name: 'ark_check',
2065
2092
  description:
2066
- 'Run the full Ark architecture check on the project and return structured results ' +
2067
- '(layer-import violations, forbidden globals, circular deps, config warnings). Use ' +
2068
- 'this to answer "is the architecture currently valid?" instead of shelling out to ' +
2069
- 'ark-check. Preserves legacy ok and adds identity/completeness/graph/coverage/gates/' +
2070
- 'overall verdicts. Applies the baseline automatically when one exists. isError when not ok.',
2093
+ 'Scan the project for architecture findings (layer-import violations, forbidden globals, ' +
2094
+ 'cycles, config warnings). Returns pass/fail/incomplete plus evidence — not a yes/no ' +
2095
+ 'architecture score. Same engine as arkgate-check. Applies the baseline when one exists. ' +
2096
+ 'isError when the scan fails. Prefer after ark_identity.',
2071
2097
  inputSchema: {
2072
2098
  type: 'object',
2073
2099
  properties: {