arkgate 4.1.0 → 4.2.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.
Files changed (63) hide show
  1. package/CHANGELOG.md +124 -2
  2. package/README.md +24 -14
  3. package/bin/ark-check-runtime.mjs +16 -5
  4. package/bin/ark-mcp-runtime.mjs +766 -64
  5. package/bin/lib/agent-gates.mjs +1 -0
  6. package/bin/lib/ark-gitignore.mjs +88 -0
  7. package/bin/lib/ci-and-commands.mjs +33 -8
  8. package/bin/lib/codex-home.mjs +90 -8
  9. package/bin/lib/design-smells.mjs +71 -9
  10. package/bin/lib/doctor-plan.mjs +47 -39
  11. package/bin/lib/effective-contract-load.mjs +73 -9
  12. package/bin/lib/enforcement-honesty.mjs +78 -22
  13. package/bin/lib/enforcement-state.mjs +1 -1
  14. package/bin/lib/gate-files.mjs +441 -9
  15. package/bin/lib/github-enforcement.mjs +168 -7
  16. package/bin/lib/hook-templates.mjs +12 -11
  17. package/bin/lib/host-support-matrix.mjs +91 -17
  18. package/bin/lib/html-report-depth.mjs +13 -2
  19. package/bin/lib/html-report-evolution.mjs +114 -0
  20. package/bin/lib/html-report.mjs +18 -97
  21. package/bin/lib/import-resolve.mjs +33 -11
  22. package/bin/lib/install-activation.mjs +87 -0
  23. package/bin/lib/install-migrate.mjs +66 -50
  24. package/bin/lib/managed-upgrade.mjs +10 -41
  25. package/bin/lib/mcp-adoption.mjs +15 -5
  26. package/bin/lib/pilot-loop.mjs +25 -8
  27. package/bin/lib/project-identity.mjs +103 -0
  28. package/bin/lib/report-snapshot-context.mjs +28 -0
  29. package/bin/lib/resident-hook.mjs +33 -9
  30. package/bin/lib/rules-inventory.mjs +100 -8
  31. package/bin/lib/skill-install.mjs +272 -22
  32. package/bin/lib/skill-write.mjs +899 -0
  33. package/bin/lib/start-preview.mjs +84 -1
  34. package/bin/lib/upgrade-command.mjs +2 -5
  35. package/bin/lib/write-path-detect.mjs +2 -2
  36. package/dist/index.cjs +13 -13
  37. package/dist/index.d.ts +194 -2
  38. package/dist/index.js +13 -13
  39. package/docs/README.md +6 -4
  40. package/docs/agent-guide.md +115 -17
  41. package/docs/ai-gates.md +133 -25
  42. package/docs/assets/ark-write-gate.svg +2 -2
  43. package/docs/develop.md +16 -6
  44. package/docs/enthusiast/how-to-agent-gates.md +6 -0
  45. package/docs/package-surface.md +16 -9
  46. package/docs/product-voice.md +22 -4
  47. package/docs/use.md +3 -1
  48. package/package.json +3 -1
  49. package/schemas/ark.project-identity.schema.json +116 -0
  50. package/server.json +2 -2
  51. package/templates/skills/ark-adopt.md +9 -0
  52. package/templates/skills/ark-architect.md +12 -2
  53. package/templates/skills/ark-autopilot.md +9 -0
  54. package/templates/skills/ark-contract.md +11 -1
  55. package/templates/skills/ark-coverage.md +9 -0
  56. package/templates/skills/ark-explain.md +13 -1
  57. package/templates/skills/ark-explore.md +9 -0
  58. package/templates/skills/ark-fix.md +10 -1
  59. package/templates/skills/ark-loop.md +11 -2
  60. package/templates/skills/ark-place.md +17 -6
  61. package/templates/skills/ark-runtime.md +8 -0
  62. package/templates/skills/ark-think.md +14 -2
  63. package/templates/skills/ark-upgrade.md +9 -0
package/docs/README.md CHANGED
@@ -52,13 +52,15 @@ These are **not** the day-to-day product path. They stay in the repo for evidenc
52
52
  | Area | Path |
53
53
  |------|------|
54
54
  | Release notes (by version) | [releases/](releases/) · [CHANGELOG.md](../CHANGELOG.md) |
55
- | Epic plans (seeded + shipped) | [plans/](plans/) |
55
+ | Epic plans (seeded + shipped) | [plans/](plans/) · active: [workspace identity and activation truth](plans/workspace-identity-activation-truth/README.md) (`WI01`, 4.2.0 corrective minor) |
56
56
  | Claims audit | [audit/claims-matrix.md](audit/claims-matrix.md) |
57
57
  | Field adoption kit (scaffolding, not closed) | [field/](field/) |
58
58
  | Runtime hardening (experimental) | [production-hardening.md](production-hardening.md) |
59
59
 
60
- Current published: [releases/4.0.1.md](releases/4.0.1.md) (`arkgate@4.0.1` on npm `latest`).
61
- Next prepared: [releases/4.1.0.md](releases/4.1.0.md) (`arkgate@4.1.0` — field product phases + CI PR slim; not published until registry).
60
+ Prepared candidate: [releases/4.2.0.md](releases/4.2.0.md) (`arkgate@4.2.0`, not published).
61
+
62
+ Current published: [releases/4.1.1.md](releases/4.1.1.md) (`arkgate@4.1.1` on npm `latest`).
63
+ Previous: [releases/4.1.0.md](releases/4.1.0.md) (`arkgate@4.1.0`).
62
64
  Previous major: [releases/4.0.0.md](releases/4.0.0.md) (`arkgate@4.0.0`).
63
65
  Config: [configuration.md](configuration.md) · Agent skills dual-plane: [agent-guide.md](agent-guide.md).
64
66
 
@@ -68,5 +70,5 @@ Config: [configuration.md](configuration.md) · Agent skills dual-plane: [agent-
68
70
 
69
71
  1. **Audience first** — every page should be use, develop, or contribute.
70
72
  2. **One primary flow** — `start` → doctor → optional guided work.
71
- 3. **Honest hardness** — host write guarantees differ; CI required status is the shared merge boundary.
73
+ 3. **Honest hardness** — host write guarantees differ; a **required GitHub status context** running the merge CLI is the shared hard boundary.
72
74
  4. **History is not the product** — version archaeology lives under `releases/` and `plans/`, not the front door.
@@ -40,9 +40,18 @@ npx ark-check --install-agent-gates --skills-only --force
40
40
 
41
41
  **Write-path honesty:** Claude/Grok/Antigravity can hard-block listed PreToolUse ops when
42
42
  installed and trusted. Cursor/Codex/OpenCode remain **advisory at write**. For every host, the
43
- repository-wide hard boundary is a **required** CI status (`arkgate-check --strict-merge`)
44
- never claim Cursor/Codex/OpenCode hard write. See [ai-gates.md](ai-gates.md) and the README host
45
- matrix.
43
+ repository-wide hard boundary is a **required GitHub status context** that runs
44
+ `arkgate-check --strict-merge` (alias `ark-check --strict-merge`) the CLI name is not the
45
+ status context name. Never claim Cursor/Codex/OpenCode hard write. Soft-write alone does not mean
46
+ the project is unfinished; doctor keeps it as an environment residual. See [ai-gates.md](ai-gates.md)
47
+ and the README host matrix.
48
+
49
+ **MCP project identity (4.2.0):** before trusting project-specific MCP evidence, call
50
+ `ark_identity` with `project.expectedRoot` set to the exact project's absolute root. Reuse that
51
+ root plus the returned `projectIdentity.projectId` on every later Ark tool call. A descendant
52
+ path is authoritative only when that matching project id is also supplied. Only
53
+ `binding.status: "matched"` with `authoritative: true` is authoritative; calls that omit the
54
+ expectation remain compatible but are explicitly `unverified`.
46
55
 
47
56
  ## Architecture playbook and `ark-check --recommend`
48
57
 
@@ -110,6 +119,10 @@ baseline. Do not `--reset-origin` unless the user explicitly wants a new baselin
110
119
  `ark-check --report --no-archive` still creates `origin.*` on the first report (or on an explicit
111
120
  reset) and refreshes `latest.*`; it skips only the timestamped JSON file under
112
121
  `.ark/reports/history/`.
122
+ Snapshots record best-effort, shell-free Git provenance (`HEAD`, attached branch, and dirty
123
+ worktree state). Evolution keeps raw metrics visible across ArkGate upgrades, but an Ark score
124
+ delta is comparable—and therefore rendered—only when origin and current snapshots use the same
125
+ ArkGate version.
113
126
 
114
127
  Once the **raw** graph has zero violations (the baseline is deliberately ignored) and governed
115
128
  coverage is at least 50%, `ark-check --ratchet-cores` changes `optional: true` to
@@ -128,7 +141,9 @@ brownfield Align/Stabilize + seed Shape B. `/ark-loop` = plan A only. Empty plan
128
141
  “architecture healthy” if design-weak residual remains. Full routing table: full-install
129
142
  `AGENTS.md` / [README expert skills](../README.md#expert-skills-escapes--not-onboarding).
130
143
 
131
- **Design fitness (3.0.1+ / Phase Q 3.0.3):** after edges are clean, doctor can still report **ENFORCE · design-weak**.
144
+ **Design fitness (3.0.1+ / Phase Q 3.0.3):** after checked edges are clean, doctor can still
145
+ report **SUGGEST / ADAPT / ENFORCE · design-weak** using the mode it actually observed; a weak
146
+ design does not imply that enforcement is active.
132
147
 
133
148
  ```bash
134
149
  npx ark-check --doctor --json # designFitness, designSmells[].outcome, postGreenPath, goldenPattern, pilotLoop
@@ -143,7 +158,10 @@ npx ark-check --doctor --fail-on-new-smells --base-ref origin/main --json # opt-
143
158
  **Pilot loop (Q04):** when design-weak, `pilotLoop.nextPilot` is **one** extraction card
144
159
  (pilot target, move, success, kill-switch). Apply **that one pilot only**, then re-doctor.
145
160
  Success = reduced smell evidence on pilot paths; residual outside the pilot may remain.
146
- Never multi-pilot batch; never mechanical-safe; never claim healthy finished while design-weak.
161
+ Never select seed/fixture/demo/migration/generated files as god-module pilots. A real UI business
162
+ rule moves Domain → Application → UI; local permission/UI-state helpers are not selected by their
163
+ `canEdit`-style name alone. Never multi-pilot batch; never mechanical-safe; never claim healthy
164
+ finished while design-weak.
147
165
 
148
166
  **AI-velocity evidence (Q05):** deterministic fixture bench (no live LLM) compares the same
149
167
  feature add on design-weak vs golden-path trees. Run `npm run eval:ai-velocity`; metric is
@@ -454,7 +472,7 @@ npx arkgate-check --install-agent-gates --tools claude,cursor,codex,grok,antigra
454
472
  |------|-----------------|-------------|
455
473
  | Claude Code | `.claude/settings.json` hook + `.mcp.json` / `claude mcp add` | `.claude/skills/<name>/SKILL.md` |
456
474
  | Cursor | `.cursor/mcp.json` + `.cursor/rules/ark.mdc` | `.cursor/commands/` |
457
- | OpenAI Codex | `.codex/config.toml` (project primary, relative `--root .`); optional legacy `$CODEX_HOME/config.toml` fallback uses absolute roots and scoped secondaries — see [ai-gates.md](ai-gates.md) | **Repo:** `.agents/skills/<name>/SKILL.md`; **home:** `$CODEX_HOME/skills/<name>/SKILL.md` (`--codex-home`) |
475
+ | OpenAI Codex | `.codex/config.toml` (project primary, relative `--root .`; configured on disk is not runtime-active until restart + `ark_identity` match); optional legacy `$CODEX_HOME/config.toml` fallback uses absolute roots and scoped secondaries — see [ai-gates.md](ai-gates.md) | **Repo:** `.agents/skills/<name>/SKILL.md`; **home:** `$CODEX_HOME/skills/<name>/SKILL.md` (`--codex-home`) |
458
476
  | **Grok Build** | `.grok/hooks/ark-write-gate.json` + `.grok/config.toml` / `.mcp.json` | `.grok/skills/<name>/SKILL.md` |
459
477
  | Google Antigravity | `.agents/hooks.json` (+ `GEMINI.md` for shared Gemini consumers) | `.agents/skills/<name>/SKILL.md` |
460
478
  | OpenCode | `opencode.json` MCP (`type: local`; advisory) | `.opencode/skills/<name>/SKILL.md` |
@@ -462,6 +480,11 @@ npx arkgate-check --install-agent-gates --tools claude,cursor,codex,grok,antigra
462
480
  This is a path reference, not a guarantee table. Full copy-paste setups:
463
481
  [ai-gates.md](ai-gates.md). Skill inventory: main
464
482
  [README](../README.md#other-skills-only-when-you-need-them).
483
+ When several repositories share one machine, repo catalogs stay pinned and isolated; unchanged
484
+ skill bodies are not rewritten for a version stamp. The optional `$CODEX_HOME/skills` catalog is
485
+ monotonic across ArkGate 4.2.0+ installers. Pre-4.2 binaries ignore its metadata and lock, so
486
+ upgrade legacy repos before they write the optional home catalog. See
487
+ [AI gates — Codex skill catalog](ai-gates.md#codex-skill-catalog-skillmd-not-flat-prompts).
465
488
 
466
489
  For an optional executable adoption check, copy the shipped template into a Vitest/Jest suite
467
490
  after installing ArkGate:
@@ -844,17 +867,41 @@ required, and `behavioralCompletion` remains `not-evaluated` even when structure
844
867
  npx ark-mcp --root . --config ark.config.json [--manifest ark.manifest.json]
845
868
  ```
846
869
 
847
- - **Resource `ark://manifest`**contract discovery. Serve an exported
848
- `ark.manifest().toJSON()` via `--manifest`. Without that flag, the resource uses every active
849
- layer and the effective rules from `ark.config.json`; the strict 11-layer profile is the
850
- fallback only when the project config declares no layers.
870
+ - **Identity handshake**first call `ark_identity` with:
851
871
 
852
- The server exposes these nine tools:
872
+ ```json
873
+ {
874
+ "project": {
875
+ "expectedRoot": "/absolute/exact-project-root"
876
+ }
877
+ }
878
+ ```
879
+
880
+ Then reuse both `expectedRoot` and the returned `projectIdentity.projectId` as
881
+ `project.expectedProjectId` on every later Ark tool call. `expectedProjectId` without
882
+ `expectedRoot` can detect the wrong id, but it remains non-authoritative because it does not
883
+ prove the current workspace root. The first handshake requires the exact project root; a
884
+ contained descendant becomes authoritative only on later calls that also send the matching
885
+ project id.
886
+ - **Tool `ark_manifest`** — authoritative contract discovery after the identity handshake.
887
+ Serve an exported `ark.manifest().toJSON()` via `--manifest`. Without that flag, the tool uses
888
+ every active layer and the effective rules from `ark.config.json`; the strict 11-layer profile
889
+ is the fallback only when the project config declares no layers. Call it with the same root +
890
+ project id expectation.
891
+ - **Resource `ark://manifest`** — compatibility discovery for standard MCP `resources/read`
892
+ clients. That protocol request has no portable project-expectation field, so Ark always marks
893
+ this resource `unverified` and non-authoritative. It never substitutes for `ark_manifest` in
894
+ a project verdict.
895
+
896
+ The server exposes these twelve tools. Every tool accepts the additive
897
+ `project: { expectedRoot, expectedProjectId? }` input:
853
898
 
854
899
  | Tool | Primary input and purpose |
855
900
  |------|---------------------------|
901
+ | `ark_identity` | `{ project: { expectedRoot, expectedProjectId? } }`: return the canonical root/config, stable project id, contract identity, and live runtime identity; use it before every other project-bound surface. |
902
+ | `ark_manifest` | No non-project args: return the machine-readable architecture contract with an authoritative binding after the identity handshake. |
856
903
  | `validate_code` | `{ source, layer?, filePath? }`: validate one snippet; infer the layer from `filePath` when possible; return an error result when invalid. |
857
- | `ark_check` | `{ strict?, baseline? }`: run the full project architecture check with structured diagnostics. |
904
+ | `ark_check` | `{ strict?, baseline? }`: run the full project architecture check. `verdict` separates `identity`, `completeness`, `graph`, `coverage`, `gates`, and `overallOk`; no individual green fact substitutes for the combined verdict. |
858
905
  | `ark_policy_delta` | `{ baseConfig, candidateConfig?, acknowledgement? }`: classify a complete contract transition; never edits the contract. |
859
906
  | `ark_coverage` | No args: report per-layer counts, every unclassified file, unmatched layers, and missing rule edges. |
860
907
  | `ark_place` | `{ filePath?, description? }`: resolve or propose a governed home and return its import/global constraints. |
@@ -862,8 +909,55 @@ The server exposes these nine tools:
862
909
  | `ark_prepare_change` | `{ changes, changeMap? }`: preflight one complete create/update/delete batch in memory; never writes files. |
863
910
  | `ark_recommend` | No args: return the deterministic application-shape plan used by `ark-check --recommend --json`. |
864
911
  | `ark_suggest_include` | No args: propose TypeScript/JavaScript include roots from workspaces and nested packages. |
912
+ | `ark_rules_inventory` | No args: inventory possible intra-layer rules using configured layer evidence when available; test/fixture/seed/migration surfaces and narrow technical constants are excluded from extraction pilots. Counts are not a score. |
913
+
914
+ Every project-bound tool success, tool error, and JSON-RPC error data carries:
865
915
 
866
- Current diagnostic envelopes use schema `1.3` and require `mode`,
916
+ ```json
917
+ {
918
+ "projectIdentity": {
919
+ "schemaVersion": "1.0",
920
+ "projectId": "sha256:…",
921
+ "resolvedRoot": "/absolute/project",
922
+ "resolvedConfigPath": "/absolute/project/ark.config.json",
923
+ "arkgateVersion": "4.2.0",
924
+ "contractHash": "sha256:…",
925
+ "contractSource": "project",
926
+ "runtimeId": "process-specific",
927
+ "processStartedAt": "2026-07-30T00:00:00.000Z"
928
+ },
929
+ "binding": {
930
+ "status": "matched",
931
+ "authoritative": true
932
+ },
933
+ "authoritative": true
934
+ }
935
+ ```
936
+
937
+ `projectId` stays stable across process restarts and contract edits; `runtimeId` and
938
+ `processStartedAt` identify this live process. Binding states are:
939
+
940
+ - `matched` — canonical `expectedRoot` is the exact project root, or it is a contained
941
+ descendant and the caller also supplied the matching project id; `authoritative` is `true`;
942
+ - `unverified` — no expectation was supplied, or only the id matched; callable for legacy
943
+ clients, but `authoritative` is `false`;
944
+ - `mismatch` — invalid/wrong root or id; `authoritative` is `false` and Ark returns
945
+ `PROJECT_ROOT_MISMATCH`, `PROJECT_ID_MISMATCH`, or `INVALID_PROJECT_EXPECTATION`.
946
+
947
+ Roots, configs, manifests, TypeScript configs, and project-bound tool paths are canonicalized
948
+ through real paths. A config or file path outside the bound project fails before Ark returns
949
+ placement, golden-pattern, Layers, or ArkRules evidence. The MCP process never retargets itself
950
+ from tool input; disjoint projects need disjoint processes. The compatibility `ark://manifest`
951
+ resource also carries the identity envelope, but its binding is always `unverified` and
952
+ non-authoritative.
953
+
954
+ For `ark_check`, treat `verdict.overallOk` as the combined control-plane fact. It can be true only
955
+ when the binding is matched, analysis is complete, the graph is valid, coverage is complete
956
+ (non-empty, 100% governed, zero unclassified files), and both local-write and CI-merge gate state
957
+ are active. The underlying CLI fields remain present for diagnosis, but are not an authoritative
958
+ whole-project green on their own.
959
+
960
+ Current diagnostic envelopes use schema `1.4` and require `mode`,
867
961
  `completeness: "complete" | "partial" | "unavailable"`, and structured
868
962
  `completenessReasons`. Resolved results expose `policyHash`, `resolverIdentity`, `factsHash`, and
869
963
  `candidateTreeHash`; MCP `ark_check` mirrors CLI `ok`. Single-file `validate_code`,
@@ -873,7 +967,9 @@ preflight. Consumer-owned 1.0/1.1/1.2 `AdapterResult` values remain accepted by
873
967
 
874
968
  For hook-based enforcement, `ark-mcp --hook` runs one-shot: it reads a PreToolUse payload
875
969
  from stdin, validates the post-edit file content, and exits `2` with violations on stderr
876
- to block the write (`0` to allow). Working Claude Code configuration
970
+ to block the write (`0` to allow). `--root-env` accepts a prioritized comma-separated
971
+ environment-variable list; ArkGate uses the first populated value and otherwise keeps
972
+ the explicit `--root` fallback. Working Claude Code configuration
877
973
  (`.claude/settings.json`):
878
974
 
879
975
  ```json
@@ -885,7 +981,7 @@ to block the write (`0` to allow). Working Claude Code configuration
885
981
  "hooks": [
886
982
  {
887
983
  "type": "command",
888
- "command": "npx ark-mcp --hook --root \"$CLAUDE_PROJECT_DIR\""
984
+ "command": "npx ark-mcp --hook --root . --root-env CLAUDE_PROJECT_DIR"
889
985
  }
890
986
  ]
891
987
  }
@@ -894,8 +990,8 @@ to block the write (`0` to allow). Working Claude Code configuration
894
990
  }
895
991
  ```
896
992
 
897
- Register the server itself in `.mcp.json` so the agent can read `ark://manifest` and call
898
- `validate_code` on demand:
993
+ Register the server itself in `.mcp.json` so the agent can handshake with `ark_identity`, call
994
+ `ark_manifest`, and use `validate_code` on demand:
899
995
 
900
996
  ```json
901
997
  {
@@ -908,6 +1004,8 @@ Register the server itself in `.mcp.json` so the agent can read `ark://manifest`
908
1004
  On Claude/Grok, the installed PreToolUse hook makes matched writes an enforced checkpoint. MCP
909
1005
  registration by itself remains advisory on every host because the agent must call the tool.
910
1006
 
1007
+ Decision rationale: [ADR 0017 — MCP verdicts require explicit project identity](adr/0017-mcp-project-identity-binding.md).
1008
+
911
1009
  ## Experimental runtime kernel workflow (not the default path)
912
1010
 
913
1011
  This section is for adopters who **opt into** the experimental `@arkgate/runtime` / kernel surfaces.
package/docs/ai-gates.md CHANGED
@@ -85,9 +85,12 @@ supported smells on touched paths. Companion plan JSON: `plan.patternBets[]` wit
85
85
  [package-surface.md](package-surface.md) and [brownfield-adoption.md](brownfield-adoption.md) §6.
86
86
 
87
87
  If your project uses Codex or Grok, treat MCP registration as part of the default
88
- setup, not an optional extra. Ark works best when the agent can read `ark://manifest`
88
+ setup, not an optional extra. Ark works best when the agent can call `ark_manifest`
89
89
  before it writes code; that is the fast path to avoiding architecture drift during
90
- generation.
90
+ generation. Registration on disk is not runtime proof: after the host starts the server, call
91
+ `ark_identity` with `project.expectedRoot` set to the exact project's absolute root and require
92
+ a `matched`, authoritative binding. Then call `ark_manifest` with that root plus the returned
93
+ project id before using project evidence.
91
94
 
92
95
  ## Claude Code — hook (recommended, hard block)
93
96
 
@@ -158,7 +161,7 @@ Add to your project's `.claude/settings.json`:
158
161
  "hooks": [
159
162
  {
160
163
  "type": "command",
161
- "command": "npx ark-mcp --hook --hook-repair --root \"$CLAUDE_PROJECT_DIR\" --config ark.config.json"
164
+ "command": "npx ark-mcp --hook --hook-repair --root . --root-env CLAUDE_PROJECT_DIR --config ark.config.json"
162
165
  }
163
166
  ]
164
167
  }
@@ -173,7 +176,7 @@ That's the whole setup. Try asking the agent to import a persistence adapter fro
173
176
  Ark architecture gate blocked this write to src/domain/order.ts (layer: DomainModel):
174
177
  - [FORBIDDEN_PATTERN] Forbidden pattern matched: /from ['"].*\/(infra|adapters|persistence|db)/i (line 1)
175
178
  - [FORBIDDEN_IMPORT] Forbidden import target: "../adapters/persistence/pg-order-repository". (line 1)
176
- Fix the violations and retry. The architecture contract is available as the ark://manifest MCP resource.
179
+ Fix the violations and retry. After ark_identity matches, call ark_manifest with the same project expectation for the authoritative contract.
177
180
  ```
178
181
 
179
182
  ## Claude Code — SessionStart context injection (know the rules before the first token)
@@ -191,7 +194,7 @@ injects into the agent's context at session start:
191
194
  "hooks": [
192
195
  {
193
196
  "type": "command",
194
- "command": "npx ark-mcp --session-context --root \"$CLAUDE_PROJECT_DIR\" --config ark.config.json"
197
+ "command": "npx ark-mcp --session-context --root . --root-env CLAUDE_PROJECT_DIR --config ark.config.json"
195
198
  }
196
199
  ]
197
200
  }
@@ -207,7 +210,7 @@ Ark architecture contract governs this project (ark.config.json is authoritative
207
210
  Layers:
208
211
  - DomainModel: src/domain/** — forbidden globals: fetch, process, Date.now, Math.random
209
212
  - PersistenceAdapters: src/adapters/persistence/**
210
- Rules: 10 denied layer edge(s). Full contract: ark://manifest MCP resource.
213
+ Rules: 10 denied layer edge(s). Full authoritative contract: ark_manifest after ark_identity.
211
214
  Baseline: 3 frozen violation(s) — only NEW violations fail; do not add to them.
212
215
  After edits run: npx ark-check --root . --config ark.config.json --strict
213
216
  ```
@@ -219,9 +222,20 @@ prints nothing and exits 0, so non-Ark projects are untouched.
219
222
 
220
223
  ## Claude Code — MCP server (contract discovery + on-demand validation)
221
224
 
222
- The MCP server exposes a resource and tools agents can use proactively (not an exhaustive list — `tools/list` is authoritative):
223
-
224
- - **`ark://manifest`** (resource) — the machine-readable architecture contract (layers + rules), so the agent can read the architecture before generating code.
225
+ The MCP server exposes tools plus one compatibility resource that agents can use proactively
226
+ (not an exhaustive list — `tools/list` is authoritative):
227
+
228
+ - **`ark_identity`** (tool) — returns the canonical project/config identity and this live MCP
229
+ runtime identity. Call it first with
230
+ `{ "project": { "expectedRoot": "/absolute/exact-project-root" } }`, then reuse the same root
231
+ and returned `projectIdentity.projectId` as `project.expectedProjectId` on later calls. A
232
+ descendant path is authoritative only when that matching id is also supplied.
233
+ - **`ark_manifest`** (tool) — returns the authoritative machine-readable architecture contract
234
+ (layers + rules) after the identity handshake. Call it with the same project expectation before
235
+ generating code.
236
+ - **`ark://manifest`** (resource) — compatibility-only discovery for standard MCP
237
+ `resources/read`. Because that request has no portable project-expectation field, this resource
238
+ is always `unverified` and non-authoritative.
225
239
  - **`validate_code`** (tool) — validates a snippet against the architecture on demand (the write-path gate). May return additive **`autoPatch`** (W1) for mechanical-safe import-type rewrites.
226
240
  - **`ark_prepare_write`** (tool) — **W2:** place + constrain + validate + optional autoPatch + judgmentBrief + contentHash in one call (composes `ark_place` + write gate).
227
241
  - **`ark_prepare_change`** (tool) — **T02–T05:** read-only atomic create/update/delete preflight with cross-file edge/cycle findings and candidate fingerprints. Optional `changeMap` accepts strict schema `1.0` intent and returns its hash plus satisfied/missing/contradictory/unplanned structural convergence; behavioral completion is not evaluated. Omission is supported. MCP registration remains advisory unless the host makes invocation non-bypassable.
@@ -235,7 +249,20 @@ The MCP server exposes a resource and tools agents can use proactively (not an e
235
249
  - **`ark_check`** (tool) — runs the full architecture check and returns structured violations (applies the baseline automatically when one exists).
236
250
  - **`ark_coverage`** (tool) — per-layer file counts, the full unclassified-file list, and layers whose patterns match nothing.
237
251
 
238
- Tools appear in the agent's tool list automatically no skill or doc-reading needed so the agent can query the contract instead of shelling out and parsing.
252
+ Every Ark tool accepts the additive `project` expectation. Project-bound tool successes and
253
+ errors carry `projectIdentity`, `binding`, and `authoritative`. A legacy call with no expectation
254
+ stays callable but returns `binding.status: "unverified"` and `authoritative: false`; a wrong
255
+ root/id returns a mismatch before project evidence. Absolute project paths and
256
+ config/manifest/tsconfig inputs are real-path contained inside the server root. The process never
257
+ switches projects from input. The compatibility resource carries the same identity envelope but
258
+ is always unverified/non-authoritative.
259
+
260
+ `ark_check.verdict` keeps `identity`, `completeness`, `graph`, `coverage`, and `gates` separate.
261
+ Only `overallOk` combines them; a graph-clean result cannot paint an unverified binding,
262
+ incomplete analysis, empty/partial coverage, or inactive gates green.
263
+
264
+ Tools appear in the agent's tool list automatically — no skill or doc-reading needed — so the
265
+ agent can query the contract instead of shelling out and parsing.
239
266
 
240
267
  ```bash
241
268
  claude mcp add ark -- npx ark-mcp --root . --config ark.config.json
@@ -283,7 +310,8 @@ alwaysApply: true
283
310
  Before writing or editing any TypeScript source file, call the `validate_code`
284
311
  tool from the `ark` MCP server with the full post-edit file content and its
285
312
  path. If it reports violations, fix them before writing. The architecture
286
- contract is available as the `ark://manifest` resource.
313
+ contract is available authoritatively from `ark_manifest` after `ark_identity`
314
+ matches. `ark://manifest` is compatibility-only and always unverified.
287
315
  ```
288
316
 
289
317
  Your repository backstop in Cursor is CI: `ark-check` fails its check on anything that slips
@@ -295,8 +323,9 @@ Recommended for Ark projects.
295
323
 
296
324
  Codex 0.123+ dispatches `PreToolUse` for the native `apply_patch` handler. Ark installs
297
325
  `.codex/hooks.json` with `ApplyPatch|apply_patch|Write|Edit|MultiEdit` aliases and reconstructs
298
- every added or updated file in a multi-file patch before allowing it. The hook root uses
299
- `${CODEX_PROJECT_DIR:-${PWD:-.}}`; it must not use Claude-only `CLAUDE_PROJECT_DIR`.
326
+ every added or updated file in a multi-file patch before allowing it. The hook passes
327
+ `--root . --root-env CODEX_PROJECT_DIR`: ArkGate reads the environment directly without
328
+ POSIX shell expansion and safely falls back to the hook working directory.
300
329
 
301
330
  This hook is best-effort in Codex Code Mode: some hosts execute deferred nested `apply_patch`
302
331
  calls without dispatching the project `PreToolUse` event. ArkGate therefore does not treat the
@@ -307,8 +336,10 @@ required CI status is the hard repository boundary.
307
336
  npx ark-check --install-agent-gates --tools codex
308
337
  ```
309
338
 
310
- The generated hook includes `--hook-repair`, so a rejected patch carries the same structured
311
- repair envelope as Claude and Grok. Codex still needs hook trust enabled for the project.
339
+ The generated hook includes `--hook-repair`, so a rejected patch **may emit** a structured
340
+ repair envelope (same JSON shape as Claude/Grok). **Reinjection is not guaranteed** on Codex
341
+ local write stays advisory/bypassable; the host must re-apply any fix, and required CI remains
342
+ the hard merge boundary. Codex still needs hook trust enabled for the project.
312
343
 
313
344
  Modern Codex resolves MCP servers from the active project's `.codex/config.toml`. Ark writes
314
345
  that file with relative paths, so every repository owns its primary `ark` binding without
@@ -328,8 +359,45 @@ command = "npx"
328
359
  args = ["arkgate-mcp", "--root", ".", "--config", "ark.config.json"]
329
360
  ```
330
361
 
331
- Then **restart Codex** it does not hot-load MCP servers. Expect resource `ark://manifest`
332
- and tools `validate_code`, `ark_check`, `ark_coverage`, `ark_place`.
362
+ This file means **configured on disk**, not active. Install and compact
363
+ `ark start --tools codex --json` therefore report the setup-time state explicitly:
364
+
365
+ ```json
366
+ {
367
+ "runtimeActivation": {
368
+ "configuredOnDisk": true,
369
+ "restartRequired": true,
370
+ "runtimeObserved": false,
371
+ "identityMatch": "unverified",
372
+ "active": false
373
+ }
374
+ }
375
+ ```
376
+
377
+ Then **restart Codex** — it does not hot-load MCP servers — and call:
378
+
379
+ ```json
380
+ {
381
+ "tool": "ark_identity",
382
+ "arguments": {
383
+ "project": {
384
+ "expectedRoot": "/absolute/exact-project-root"
385
+ }
386
+ }
387
+ }
388
+ ```
389
+
390
+ The live response must say `binding.status: "matched"` and `authoritative: true`. Reuse its
391
+ `projectIdentity.projectId` with the same root on every subsequent Ark call, starting with
392
+ `ark_manifest`. A missing `ark_identity` or `ark_manifest` means the process is from an older
393
+ ArkGate build; restart the host and use the workspace-local CLI until the new server answers. A
394
+ mismatch means Codex is connected to another project's process; do not use its contract,
395
+ placement, Layers, ArkRules, or check evidence.
396
+
397
+ Setup-time `runtimeActivation` remains intentionally conservative: the installer/doctor cannot
398
+ observe a later MCP conversation from files alone. The matched `ark_identity` response is the
399
+ runtime observation for that caller and live process; it does not mutate the setup JSON.
400
+ `.codex/config.toml` by itself never upgrades `runtimeObserved` or `active`.
333
401
 
334
402
  Codex uses the best-effort local patch hook plus advisory MCP for discovery/validation and
335
403
  `ark-check` as the hard merge backstop. Register all three as soon as the repo is adopted.
@@ -357,9 +425,11 @@ writes a scoped secondary table:
357
425
  | 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) |
358
426
  | When using Codex: refresh home skills | `ark-check --install-agent-gates --skills-only --codex-home --force` |
359
427
 
360
- When a valid project `.codex/config.toml` exists, doctor treats it as the effective binding and
361
- does not report an unrelated home primary. Without a project binding, doctor surfaces the
362
- legacy multi-project state. **Deferred (fix when using Codex):**
428
+ When a valid project `.codex/config.toml` exists, it is the expected binding, but files alone
429
+ cannot prove which already-running process answered. Doctor therefore keeps an unrelated home
430
+ primary as collision evidence until the live `ark_identity` matches; it does not label that home
431
+ entry as the active project. Without a project binding, doctor surfaces the legacy multi-project
432
+ state directly. **Deferred (fix when using Codex):**
363
433
  non-temp Codex-home gaps (`codex-home-multi-project`, stale `$CODEX_HOME/skills`) are
364
434
  severity **info**, marked `deferred: true`, and omitted from doctor **Primary next action** /
365
435
  **Also** list (formerly “Top actions”) when the session host is known and not Codex —
@@ -379,6 +449,23 @@ Flat `.codex/prompts/*.md` files are **not** the invocable skill catalog. Instal
379
449
  repo catalog above so AGENTS.md `/ark-*` references match what Codex can load. After install,
380
450
  Ark verifies those references against each selected host catalog.
381
451
 
452
+ **Several repos on one machine:** repo catalogs are independent and follow each repository's
453
+ locally installed ArkGate package. A managed upgrade rewrites a repo skill only when its normalized
454
+ body changes; a package-version stamp alone is a no-op. The optional home catalog is shared, so
455
+ ArkGate 4.2.0+ installation is monotonic there: an older bundled source cannot replace a newer
456
+ managed home skill, even with `--force`. A pre-4.2 binary ignores the catalog/lock protocol and
457
+ can still overwrite those files, so upgrade every legacy repo before it runs `--codex-home`
458
+ (especially with `--force`). Identical content is idempotent, and preserved customization still
459
+ follows the explicit force contract. The shared skill only routes the workflow; project evidence remains
460
+ bound to the repo through `ark_identity`. Ark records the shared catalog in
461
+ `$CODEX_HOME/skills/.arkgate-catalog.json`, writes a durable
462
+ `.arkgate-catalog.pending.json` floor before mutations, and serializes concurrent installs.
463
+ Doctor suppresses home refresh gaps only when the maximum valid committed/pending floor is newer
464
+ than the repo package; an equal pending version remains actionable for recovery, and corrupt
465
+ metadata never counts as a floor. When a newer package retires a skill, Ark removes it only if the
466
+ bytes still match the prior managed identity; customized retired content is preserved and
467
+ released from Ark ownership.
468
+
382
469
  **Parity & honesty (doctor / install):**
383
470
 
384
471
  - Doctor distinguishes **missing / stale / legacy-prompts-only** for repo (`.agents/skills`) and
@@ -387,7 +474,7 @@ Ark verifies those references against each selected host catalog.
387
474
  `--skills-only --tools codex --force` (repo) or `--codex-home --force` (home) fix.
388
475
  - Codex **write path is advisory**: MCP + best-effort `.codex/hooks.json` is **not** a hard
389
476
  write boundary and is **not** equivalent to Claude/Grok PreToolUse hard-write + repair.
390
- The hard merge backstop is CI `--strict-merge` (or `--strict`) plus a required status check.
477
+ The hard merge backstop is CI `--strict-merge` (or `--strict`) as a **required GitHub status context** (not “workflow file present”).
391
478
  - CI workflows that run ark-check without the fail-closed profile (or with only
392
479
  `--strict-config`) surface gap `enforcement-ci-not-fail-closed`.
393
480
 
@@ -591,13 +678,34 @@ for standalone linting where no project contract applies.
591
678
  Whatever the agent side does, run the merge profile in CI:
592
679
 
593
680
  ```yaml
594
- - run: npx ark-check --root . --config ark.config.json --strict-merge --fail-on-new-smells --base-ref "${{ github.event.pull_request.base.sha || github.event.before }}"
681
+ # EH04: first push may have all-zero github.event.before only pass --base-ref when resolvable.
682
+ - name: Ark architecture check
683
+ env:
684
+ ARK_POLICY_BASE_REF: ${{ github.event.pull_request.base.sha || github.event.before }}
685
+ run: |
686
+ set -euo pipefail
687
+ BASE_REF="${ARK_POLICY_BASE_REF:-}"
688
+ if [[ "$BASE_REF" =~ ^0{40,64}$ ]]; then BASE_REF=""; fi
689
+ if [ -n "$BASE_REF" ] && git cat-file -e "${BASE_REF}^{commit}" 2>/dev/null; then
690
+ export ARK_POLICY_BASE_REF="$BASE_REF"
691
+ npx ark-check --root . --config ark.config.json --strict-merge \
692
+ --fail-on-new-smells --base-ref "$BASE_REF"
693
+ else
694
+ export ARK_POLICY_BASE_REF=""
695
+ npx ark-check --root . --config ark.config.json --strict-merge
696
+ fi
595
697
  ```
596
698
 
597
699
  This explicit brownfield ratchet records schema `1.0` identities, touched paths, and stable
598
- evidence; missing base exits `2`. Its first semantic smell is `domain-logic-in-ui`; residual,
599
- path-only moves, and unrelated work stay green. Generated Claude/Grok hooks share the delta and
600
- golden-pattern repair hint. MCP exposes the result but stays advisory.
700
+ evidence; missing base with `--fail-on-new-smells` exits `2`, so the generated workflow skips the
701
+ delta when the SHA is all-zero or unresolvable while keeping the full merge gate. Its first
702
+ semantic smell is `domain-logic-in-ui`; residual, path-only moves, and unrelated work stay green.
703
+ Generated Claude/Grok hooks share the delta and golden-pattern repair hint. MCP exposes the result
704
+ but stays advisory.
705
+
706
+ **CLI vs required status:** `arkgate-check --strict-merge` / `ark-check --strict-merge` is the
707
+ **command**. The hard merge boundary is making that job a **required GitHub status context** —
708
+ not “workflow file present.”
601
709
 
602
710
  Or use the repository's composite Action at a pinned release or commit:
603
711
 
@@ -17,8 +17,8 @@
17
17
 
18
18
  <text x="24" y="152" fill="#f85149" font-weight="bold">✗ Ark architecture gate blocked this write to src/domain/order.ts (layer: DomainModel):</text>
19
19
  <text x="40" y="174" fill="#f85149">- [FORBIDDEN_IMPORT] Forbidden import target: "../adapters/persistence/pg-order-repository".</text>
20
- <text x="24" y="196" fill="#8b949e">Fix the violations and retry. The architecture contract is available as the</text>
21
- <text x="24" y="214" fill="#8b949e">ark://manifest MCP resource.</text>
20
+ <text x="24" y="196" fill="#8b949e">Fix the violations and retry. The architecture contract is available through</text>
21
+ <text x="24" y="214" fill="#8b949e">the project-bound ark_manifest MCP tool.</text>
22
22
 
23
23
  <text x="24" y="248" fill="#c9d1d9">● The domain layer can't import persistence adapters. I'll define the port in the</text>
24
24
  <text x="24" y="266" fill="#c9d1d9">domain instead and implement it in src/adapters/persistence/.</text>
package/docs/develop.md CHANGED
@@ -14,14 +14,18 @@ npx arkgate start --apply
14
14
  npx arkgate-check --doctor
15
15
  ```
16
16
 
17
- Make the architecture check a **required** merge status (GitHub/GitLab/etc.):
17
+ Make the architecture check a **required** merge **status context** (GitHub/GitLab/etc.). The CLI
18
+ command is `arkgate-check --strict-merge` / `ark-check --strict-merge` — the hard boundary is
19
+ requiring that job’s status, not merely adding a workflow file:
18
20
 
19
21
  ```yaml
20
22
  - run: npx arkgate-check --root . --config ark.config.json --strict-merge
21
23
  # or: uses: pedroknigge/arkgate@<tag-or-SHA>
22
24
  ```
23
25
 
24
- `--strict-merge` (or compatibility `--strict`) is the repository-wide hard boundary for every agent host.
26
+ Generated workflows also gate `--fail-on-new-smells --base-ref` so a first push with an all-zero
27
+ `github.event.before` still runs the full merge gate without a broken delta (see
28
+ [ai-gates.md](ai-gates.md#ci-backstop)).
25
29
 
26
30
  ---
27
31
 
@@ -31,9 +35,9 @@ Local write hardness **differs by host**. CI required status is the shared hard
31
35
 
32
36
  | Host | Local write | MCP | Merge |
33
37
  |------|-------------|-----|-------|
34
- | Claude · Grok · Antigravity | Hard PreToolUse when installed + trusted | Advisory | Required status |
35
- | Codex · OpenCode | Best-effort / advisory | Advisory | Required status |
36
- | Cursor | Advisory only | Advisory | Required status |
38
+ | Claude · Grok · Antigravity | Hard PreToolUse when installed + trusted | Advisory | Required status context |
39
+ | Codex · OpenCode | Best-effort / advisory | Advisory | Required status context |
40
+ | Cursor | Advisory only | Advisory | Required status context |
37
41
 
38
42
  Full matrix and install commands: [ai-gates.md](ai-gates.md) · canonical table in [README](../README.md#host-enforcement-support).
39
43
 
@@ -46,7 +50,13 @@ npx arkgate-check --install-agent-gates --tools opencode
46
50
  npx arkgate-check --install-agent-gates --skills-only --force
47
51
  ```
48
52
 
49
- Doctor reports what is actually installed and observed (`writePath` / enforcement state). Installed files alone do not imply `hard:true` without runtime evidence where the product requires it.
53
+ Doctor reports what is actually installed and observed (`writePath` / enforcement state). Installed
54
+ files alone do not imply `hard:true` without runtime evidence where the product requires it.
55
+
56
+ **Evidence split (Phase EH):** soft-write hosts keep `soft-write-host` in evidence without forcing
57
+ global **Not finished** when the contract is ready. With `ARK_DOCTOR_GITHUB=1`, successful CI runs
58
+ can show `runtimeObserved: true` even when branch-protection policy is plan-unavailable
59
+ (`unavailable-plan` on GitHub Free private); `hard: false` until the status is required.
50
60
 
51
61
  ---
52
62
 
@@ -42,6 +42,12 @@ npx arkgate-check --doctor
42
42
  npx arkgate-check --require-gates
43
43
  ```
44
44
 
45
+ `--require-gates` implies strict config validation. It verifies content, not filenames alone:
46
+ `AGENTS.md` must contain the Ark contract and strict check, MCP registration must launch one Ark
47
+ server with an explicit project root, compact Codex must contain valid project config plus
48
+ SessionStart/PreToolUse Ark hooks, and CI must execute a fail-closed Ark command. Included but
49
+ unclassified source files therefore remain red.
50
+
45
51
  Doctor JSON includes `writePath.mode` plus `enforcementLadder`: support, installation, observed
46
52
  evidence, covered operations, bypassability, and CI honesty. MCP registration stays advisory.
47
53