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
@@ -0,0 +1,221 @@
1
+ /**
2
+ * First-run CLI help (setup + check). Encyclopedia text stays behind --help --all.
3
+ */
4
+
5
+ export function setupUsage() {
6
+ return `arkgate (alias ark) — One architecture config. One check. One coach.
7
+
8
+ arkgate start preview what will change (no writes)
9
+ arkgate start --apply write the compact contract + host router + CI
10
+ arkgate-check --doctor status light + primary next action
11
+
12
+ Then session 0 in your agent: /ark-adopt
13
+ Stuck? Run doctor. Do #1.
14
+
15
+ More commands and flags: arkgate --help --all
16
+ `;
17
+ }
18
+
19
+ export function upgradeUsage() {
20
+ return `arkgate upgrade (alias ark upgrade) — preview vs apply.
21
+
22
+ arkgate upgrade preview managed updates (no writes)
23
+ arkgate upgrade --apply apply the previewed bytes (needs --plan-digest when applying managed files)
24
+
25
+ Customized files stay unless you pass --accept-conflicts or --refresh-skills.
26
+ Then: arkgate-check --doctor
27
+
28
+ Every flag: arkgate --help --all
29
+ `;
30
+ }
31
+
32
+ export function setupUsageAll() {
33
+ return `arkgate (alias ark) — One architecture config. One check. One coach.
34
+
35
+ Usage:
36
+ arkgate start [--root <project>] [--tools <host>] [--require-write-hook <host>] [--install] [--apply] [--json]
37
+ arkgate init [--root <project>] [--preset hexagonal|layered|feature-sliced|monorepo|ui-surface|vertical-slice|ddd-bounded-contexts|clean-architecture|onion-architecture]
38
+ [--archetype <playbook-id>] [--tools <list>] [--require-write-hook <host>] [--yes] [--force] [--no-strict]
39
+ arkgate upgrade [--root <project>] [--tools <list>] [--apply] [--plan-digest <sha256>] [--accept-conflicts] [--refresh-skills] [--json] [--no-install] [--no-strict]
40
+ arkgate preflight --changes <change-set.json> [--change-map <map.json>] [--root <project>] [--config ark.config.json] [--manifest <manifest.json>] [--tsconfig <tsconfig.json>] [--json]
41
+ arkgate status [--root <project>] [--config ark.config.json] [--json] [--vs <git-ref>]
42
+ [--expected-root <abs>] [--expected-project-id sha256:…] [--tools <host>]
43
+ arkgate agents-md [--root <project>] [--config ark.config.json] [--write] [--check] [--stdout] [--json]
44
+ [--tools <host>]
45
+
46
+ Commands:
47
+ start New here? Analyze and preview the complete setup. Read-only unless --apply.
48
+ init Configure Ark project enforcement with explicit prompts.
49
+ upgrade Preview identity-proven Ark-managed asset updates. With package install,
50
+ --apply bumps toward registry latest when behind (not only when CLI ≠ pin)
51
+ and recomputes the preview; a second explicit --apply --no-install applies
52
+ those exact bytes and verifies them. --refresh-skills opts in to rewrite
53
+ customized managed skills to package templates (never silent default).
54
+ (alias: ark update)
55
+ preflight Validate one atomic create/update/delete set without writing project files.
56
+ status Unified session/project manifest (identity, activation, last check, rules).
57
+ Never prompts. Prefer --json for agents; CI=1 forces JSON.
58
+ agents-md Version-matched agent contract projection (ACS04). Stamps package version +
59
+ contract summary into a managed AGENTS.md block. Non-authoritative — not a
60
+ gate input. Preview by default; --write merges without clobbering outside
61
+ regions; --check fails on version drift; --stdout prints the block only.
62
+ (aliases: agents-md, agent-projection)
63
+
64
+ Options:
65
+ --yes Non-interactive defaults: create config if needed, install gate templates, run strict check.
66
+ (Also the implicit default when stdin/stdout are not a TTY — agents never hang on prompts.)
67
+ --force Allow generated files to overwrite existing files.
68
+ --no-strict Skip the final strict ark-check run.
69
+ --install Pin and install arkgate as a project devDependency (default for start).
70
+ --no-install Skip adding/installing arkgate as a project devDependency (start/upgrade).
71
+ --apply Apply a start plan; for upgrade, update/repreview or apply managed bytes.
72
+ --accept-conflicts
73
+ Allow upgrade to recreate deleted managed assets or replace recorded conflicts.
74
+ --plan-digest Digest emitted by an upgrade preview; required to apply managed bytes.
75
+ --json Emit the start/upgrade/status/agents-md preview as deterministic machine-readable JSON.
76
+ --write For agents-md: merge the version-matched projection into AGENTS.md.
77
+ --check For agents-md: exit 1 when projection stamp drifts from package version.
78
+ --stdout For agents-md: print the projection block only (no file write).
79
+ --expected-root / --expected-project-id
80
+ Optional project expectation for status (MCP-compatible binding check).
81
+ --preset Start from a named architecture preset instead of detection.
82
+ --archetype Application shape from templates/architecture-playbook.json (maps to the matching preset).
83
+ Valid ids: crud-product, api-backend, frontend-surface, library-sdk, cli-utility,
84
+ worker-pipeline, event-coordinator, integration-bridge, multi-app-workspace, prototype-spike,
85
+ vertical-slice-product, ddd-bounded-contexts.
86
+ --tools One active agent host for start (claude,cursor,codex,grok,windsurf,cline,copilot,kiro,roo,continue,gemini).
87
+ Omit to use the active host; an unknown host creates only the shared compact router.
88
+ --remove-host <host>
89
+ Preview or apply removal of that compact host integration; re-add it with --tools <host>.
90
+ --require-write-hook <host>
91
+ Require and verify a hard local write hook for Claude, Grok, Antigravity, Cursor, or Codex.
92
+ Codex covers complete local apply_patch only; OpenCode is advisory-write plus hard CI merge.
93
+
94
+ Interactive mode (TTY, no --yes): asks what application shape you are building and maps it to a preset.
95
+ Non-interactive (no TTY): uses the same defaults as --yes — never calls readline on a null interface.
96
+ `;
97
+ }
98
+
99
+ export function checkUsage() {
100
+ return [
101
+ 'arkgate-check (alias ark-check) — the architecture check.',
102
+ '',
103
+ ' arkgate-check --doctor where you are: one status light, one next action',
104
+ ' arkgate-check --strict-merge CI / merge gate (required GitHub status)',
105
+ '',
106
+ 'Every flag and command: arkgate-check --help --all',
107
+ ].join('\n');
108
+ }
109
+
110
+ export function checkUsageAll() {
111
+ return [
112
+ 'arkgate-check (alias ark-check) — the architecture check.',
113
+ '',
114
+ 'Usage: arkgate-check | ark-check (identical bins; product name ArkGate)',
115
+ ' arkgate-check --version',
116
+ ' arkgate-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]',
117
+ ' ark-check --doctor [--json] [--resident] [--fail-on-new-smells --base-ref <git-ref>] read-only diagnosis; resident JSON falls back cold',
118
+ ' ark-check --coverage [--json] per-layer file counts + full unclassified list (report only, exit 0)',
119
+ ' ark-check --plan [--json] classified remediation plan (mechanical-safe / judgment / deferred) + goal; report only',
120
+ ' ark-check --rules-inventory [--json] brownfield rules inventory (AR13; deterministic candidates, not a score)',
121
+ ' ark-check --recommend [--json] [--write-plan] application-shape plan; --write-plan emits ark-adoption-plan.json',
122
+ ' ark-check --list-policy-packs enthusiast packs (hexagonal, layered, feature-sliced, monorepo, ui-surface, vertical-slice, ddd-bounded-contexts)',
123
+ ' ark-check --apply-policy-pack <id> [--force] write ark.config.json from templates/policy-packs/ (uses preset factory)',
124
+ ' ark-check --suggest-include [--json] propose include roots (TS packages / workspaces)',
125
+ ' ark-check --adopt-contract [--write] expand include + layer patterns from ungoverned dirs (never bare lib→Presentation)',
126
+ ' ark-check --migrate-contract [--write] additive P0-A retrofit: inject app/api/** → Application when missing',
127
+ ' ark-check --ratchet-cores when raw graph is green (0 violations; baseline ignored), set optional:false on populated cores only (writes ark.config.json)',
128
+ ' ark-check --watch re-run the check when governed files change (debounced)',
129
+ ' ark-check --report [file.html] [--beginner] [--reset-origin] [--no-archive] [--open|--no-open]',
130
+ ' HTML report + snapshots under .ark/reports/ (origin once, latest each run, history JSON)',
131
+ ' Best-effort open in browser (local TTY). No-op if open fails. --no-open / ARK_NO_OPEN_REPORT=1 to skip; --open forces open.',
132
+ ' 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]',
133
+ ' --follow-config-root On writes (init/install-agent-gates/migrate --write/…), adopt walked-up monorepo config root (default: keep explicit --root)',
134
+ ' 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]',
135
+ ' ark-check --update-baseline [file] freeze current violations (default .ark-baseline.json)',
136
+ ' ark-check --print-config eleven-layer',
137
+ '',
138
+ 'Adopting Ark in an existing codebase? Run --update-baseline once to freeze existing',
139
+ 'violations, commit the baseline file, and gate CI with --baseline: only NEW violations',
140
+ 'fail the check, so the ratchet only moves toward zero.',
141
+ '',
142
+ 'Team parliament: law files (ark.config / arkrules / .ark-baseline.json) cannot ship in',
143
+ 'the same diff as product source. --changed --base <ref> checks touched files only.',
144
+ '--against <ref> ratchets new keys vs that ref\'s baseline. --contract-session is a',
145
+ 'steward law-only PR. Loosen / baseline-grow need stewards[] + --author when set.',
146
+ '',
147
+ '--init scans the project for the built-in layer directory conventions (src/domain,',
148
+ 'src/application, src/adapters/persistence, ...) and writes an ark.config.json covering',
149
+ 'only the layers that actually exist, with the default rules filtered to those layers.',
150
+ 'Undetected profile layers are printed as suggestions with their conventional',
151
+ 'directories. When nothing is detected, the full 11-layer starter profile is written',
152
+ 'instead (all layers optional, anchored at src/), so the strict check passes today and',
153
+ 'each layer starts being enforced as soon as its directory gains source files.',
154
+ '',
155
+ 'Resolves relative, tsconfig path-alias, and package imports via the TypeScript',
156
+ 'module resolver, then checks each resolved cross-layer import against the rules.',
157
+ 'Path aliases resolve against the NEAREST tsconfig.json above each source file, so',
158
+ 'monorepo packages with per-package configs work under a single --root. Pass',
159
+ '--tsconfig to force one config for every file. If no tsconfig is found, path',
160
+ 'aliases are unavailable but relative/package imports still resolve.',
161
+ '',
162
+ 'The correctness path resolves and parses one complete candidate on every invocation.',
163
+ 'Legacy node_modules/.cache/ark-check.json files are ignored. --no-cache remains an',
164
+ 'accepted compatibility no-op; the identity-keyed warm snapshot is introduced in Z07.',
165
+ '',
166
+ 'Config shape:',
167
+ '{',
168
+ ' "include": ["src"],',
169
+ ' // optional: "exclude": ["**/vendor/**"], "excludeGenerated": false (default skips *.gen.ts / *.generated.ts)',
170
+ ' "layers": [',
171
+ ' { "name": "DomainModel", "patterns": ["src/domain/**"], "intentPrefixes": ["Domain."],',
172
+ ' "forbiddenGlobals": ["fetch", "process", "Date.now", "Math.random"] }',
173
+ ' ],',
174
+ ' "rules": [{ "from": "DomainModel", "to": "PersistenceAdapters", "allowed": false }]',
175
+ '}',
176
+ '',
177
+ 'Config warnings are advisory by default and are included in JSON output.',
178
+ 'Use --strict-config to make config warnings fail the check.',
179
+ 'Use --strict-merge for the fail-closed CI profile: --strict-config + --require-gates',
180
+ 'plus the security diagnostics surfaced by doctor. --strict is a compatibility alias.',
181
+ 'This merge profile never depends on an editor/agent hook.',
182
+ 'When a Git merge base is available, --strict-merge classifies the ark.config.json',
183
+ 'transition. Weakening or judgment-required findings fail unless --policy-ack names',
184
+ 'every finding and is bound to both policy hashes. Use --policy-base/--policy-base-ref',
185
+ 'for an explicit comparison; ARK_POLICY_BASE_REF is the CI environment equivalent.',
186
+ 'Add --require-write-hook claude|grok|antigravity|cursor|codex to validate a hard local',
187
+ 'write boundary for that specific host. Codex covers complete local apply_patch only;',
188
+ 'hosted/specialized/direct-write paths and OpenCode remain CI-backed. Merge blocking requires',
189
+ 'repository policy to make the shared CI status required.',
190
+ '',
191
+ '--require-gates implies --strict-config and fails when the Ark contract in AGENTS.md,',
192
+ 'the project-rooted Ark server in .mcp.json, or fail-closed CI is missing/invalid.',
193
+ 'Included but unclassified source files therefore stay red instead of false-green.',
194
+ '',
195
+ '--install-agent-gates writes AGENTS.md, .mcp.json, and the CI workflow for every',
196
+ 'project, plus tool-specific templates. Known tools: claude, cursor, codex, grok',
197
+ '(Claude/Grok/Antigravity/Cursor hard-write hooks when covered; Codex hard local apply_patch;',
198
+ 'shared CI check for all) and',
199
+ 'windsurf, cline, copilot, kiro, roo, continue, gemini',
200
+ '(instruction-tier rule files derived from the same contract).',
201
+ 'It also installs the /ark-* skills shipped in templates/skills/ into each',
202
+ 'detected tool\'s command location (.claude/skills/, .cursor/commands/,',
203
+ '.agents/skills/ (Codex REPO catalog), .grok/skills/, .windsurf/workflows/,',
204
+ '.clinerules/workflows/, .github/prompts/).',
205
+ 'Kiro, Roo, Continue, and Gemini have no command mechanism and receive only their',
206
+ 'rule file. Existing files are never overwritten without --force, so re-running',
207
+ 'after an update only adds what is missing. --skills-only restricts the write to',
208
+ 'just the /ark-* skills (safe to --force-refresh — it leaves a customized AGENTS.md,',
209
+ 'settings, and CI workflow untouched).',
210
+ 'Pass --tools to pick which tool configs to write; otherwise they are auto-detected',
211
+ 'from their config directories (.claude/, .cursor/, .codex/, .grok/, .windsurf/,',
212
+ '.clinerules/, .kiro/, .roo/, .continue/, .gemini/; copilot is explicit-only).',
213
+ 'claude+cursor+codex+grok are written when nothing is detected.',
214
+ '',
215
+ 'Generate a starter 11-layer config:',
216
+ ' ark-check --print-config eleven-layer > ark.config.json',
217
+ '',
218
+ 'Install agent + CI enforcement templates:',
219
+ ' ark-check --install-agent-gates',
220
+ ].join('\n');
221
+ }
@@ -17,7 +17,7 @@
17
17
  */
18
18
  function hostProfile(label, hookPath, hookSurface, hookOperations, hardWrite, repairPayload, extras = {}) {
19
19
  // EH07: repair envelope emission ≠ reinjection guarantee.
20
- // Codex hooks may emit --hook-repair JSON while reinjection stays host-dependent / not hard.
20
+ // Cursor/Codex hooks may emit --hook-repair JSON while reinjection stays host-dependent.
21
21
  const repairEnvelopeEmitted =
22
22
  extras.repairEnvelopeEmitted === true || repairPayload === true;
23
23
  const repairReinjectionGuaranteed = hardWrite === true && repairPayload === true;
@@ -94,15 +94,15 @@ export const HOST_SUPPORT_MATRIX = Object.freeze({
94
94
  codex: hostProfile(
95
95
  'OpenAI Codex',
96
96
  '.codex/hooks.json',
97
- 'Best-effort PreToolUse `apply_patch`; Code Mode hosts may bypass the event',
97
+ 'PreToolUse `apply_patch` in Codex CLI and local ChatGPT Desktop/App Server',
98
98
  ['apply_patch'],
99
- false,
99
+ true,
100
100
  false,
101
101
  {
102
- // Install writes --hook-repair; envelope can be emitted; reinjection is not guaranteed.
102
+ // Install writes --hook-repair; envelope can emit, but host reinjection is not guaranteed.
103
103
  repairEnvelopeEmitted: true,
104
104
  operationCoverage: {
105
- apply_patch: false,
105
+ apply_patch: true,
106
106
  shell: false,
107
107
  'pre-commit': false,
108
108
  },
@@ -152,15 +152,12 @@ export function renderHostSupportMatrixMarkdown() {
152
152
  const rows = HOST_SUPPORT_HOSTS.map((host) => {
153
153
  const profile = HOST_SUPPORT_MATRIX[host];
154
154
  const capabilities = profile.capabilities;
155
- // Fail-closed honesty: Codex/OpenCode never claim hard write; CI is required-status.
156
- // Cursor claims hard only for listed preToolUse ops when hooks are installed + trusted.
155
+ // Fail-closed honesty: hard hosts claim only listed operations when hooks are
156
+ // installed + trusted. OpenCode remains advisory; CI is required-status.
157
157
  // hookSurface already includes "PreToolUse/preToolUse …" — do not prefix again.
158
158
  let local;
159
159
  if (capabilities['hard-write']) {
160
160
  local = `**Hard** block for listed ops (${profile.hookSurface}) when installed + trusted`;
161
- } else if (host === 'codex') {
162
- local =
163
- '**Advisory / best-effort** at write (not equivalent to Claude/Grok/Cursor hard block)';
164
161
  } else if (host === 'opencode') {
165
162
  local =
166
163
  '**Advisory / best-effort** at write (MCP + optional plugin; not a hard boundary)';
@@ -172,7 +169,7 @@ export function renderHostSupportMatrixMarkdown() {
172
169
  if (capabilities['repair-reinjection-guaranteed']) {
173
170
  repair = 'Emitted on hook deny; host must re-inject (hard path when installed + trusted)';
174
171
  } else if (capabilities['repair-envelope-emitted']) {
175
- repair = 'Envelope may emit (`--hook-repair`); reinjection **not** guaranteed (advisory host)';
172
+ repair = 'Envelope may emit (`--hook-repair`); reinjection **not** guaranteed';
176
173
  } else {
177
174
  repair = 'No hard-boundary payload';
178
175
  }
@@ -188,8 +185,9 @@ ${rows}
188
185
 
189
186
  **Read the CI column:** for every host, the repository-wide hard guarantee is a **required**
190
187
  GitHub **status context** that runs the CLI — not “CI file present,” and not the CLI binary name alone.
191
- Codex/OpenCode never get a fake hard write claim. Cursor hard write covers only listed
192
- \`preToolUse\` ops when \`.cursor/hooks.json\` is installed and trusted Shell/Tab/human edits still rely on CI.
188
+ Codex hard write covers only a complete local \`apply_patch\`; Cursor covers only listed
189
+ \`preToolUse\` ops. In both cases the project hook must be installed + trusted, while shell/direct
190
+ filesystem writes, hosted or specialized opt-out paths, and human edits still rely on CI.
193
191
 
194
192
  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.`;
195
193
  }
@@ -226,8 +224,8 @@ export function doctorWritePathHonestyMessage(activeHost, hardWriteActive) {
226
224
  if (host === 'cursor' && !hardWriteActive) {
227
225
  return `Cursor: pre-write block is supported for Write/StrReplace when .cursor/hooks.json is installed + trusted; without runtime-observed hook evidence, the block is unverified. ${mergeBoundary}.`;
228
226
  }
229
- if (host === 'codex') {
230
- return `Codex: edits are warning only (not blocked) at write time. ${mergeBoundary}.`;
227
+ if (host === 'codex' && !hardWriteActive) {
228
+ return `Codex: a trusted PreToolUse hook can block complete local apply_patch calls in CLI and Desktop; without fresh runtime-observed apply_patch evidence, the block is unverified. Specialized/hosted paths and direct writes still rely on CI. ${mergeBoundary}.`;
231
229
  }
232
230
  if (host === 'opencode') {
233
231
  return `OpenCode: edits are warning only (not blocked). ${mergeBoundary}.`;
@@ -147,21 +147,16 @@ export function renderStartPreview(preview, options = {}) {
147
147
  } else {
148
148
  console.log('Ark start preview — no files were changed.');
149
149
  }
150
+ if (!applying) {
151
+ console.log('Apply this plan with: arkgate start --apply');
152
+ }
150
153
  if (preview.analysis) {
151
154
  console.log(`Your project looks like: ${preview.analysis.label} (${preview.analysis.archetype}, confidence ${preview.analysis.confidence}).`);
152
155
  }
153
- console.log(`Projected governed coverage: ${preview.projectedCoverage.percent ?? 'unknown'}% (${preview.projectedCoverage.classifiedFiles}/${preview.projectedCoverage.totalFiles} files)`);
154
- const budget = preview.setupBudget;
155
- const arkrulesNote =
156
- budget.arkrulesFiles > 0 ? ` (+${budget.arkrulesFiles} arkrules)` : '';
157
- const gateCount = budget.gateFiles ?? budget.files;
158
- console.log(
159
- `Compact setup budget: ${gateCount}/${budget.maxFiles} gate files${arkrulesNote}, ${budget.bytes}/${budget.maxBytes} bytes${budget.ok ? '' : ' (exceeded)'}.`
160
- );
161
156
  console.log(applying ? 'Files create/edit/delete:' : 'Files to create/edit/delete:');
162
157
  if (preview.changes.length === 0) console.log(' (none)');
163
158
  for (const change of preview.changes) {
164
- console.log(` ${change.action.padEnd(6)} ${change.path} ${change.afterHash ?? '(deleted)'}`);
159
+ console.log(` ${change.action.padEnd(6)} ${change.path}`);
165
160
  }
166
161
  if (!applying) {
167
162
  console.log('Commands in the approved setup plan:');
@@ -179,8 +174,20 @@ export function renderStartPreview(preview, options = {}) {
179
174
  console.log('Unresolved decisions:');
180
175
  for (const decision of preview.unresolvedDecisions) console.log(` ${decision}`);
181
176
  }
177
+ console.log('Details (optional):');
178
+ console.log(`Projected governed coverage: ${preview.projectedCoverage.percent ?? 'unknown'}% (${preview.projectedCoverage.classifiedFiles}/${preview.projectedCoverage.totalFiles} files)`);
179
+ const budget = preview.setupBudget;
180
+ const arkrulesNote =
181
+ budget.arkrulesFiles > 0 ? ` (+${budget.arkrulesFiles} arkrules)` : '';
182
+ const gateCount = budget.gateFiles ?? budget.files;
183
+ console.log(
184
+ `Compact setup budget: ${gateCount}/${budget.maxFiles} gate files${arkrulesNote}, ${budget.bytes}/${budget.maxBytes} bytes${budget.ok ? '' : ' (exceeded)'}.`
185
+ );
186
+ for (const change of preview.changes) {
187
+ console.log(` ${change.action.padEnd(6)} ${change.path} ${change.afterHash ?? '(deleted)'}`);
188
+ }
182
189
  if (!applying) {
183
- console.log('Review complete file contents with --json. Apply this plan with: ark start --apply');
190
+ console.log('Review complete file contents with --json.');
184
191
  }
185
192
  }
186
193
 
@@ -299,7 +299,7 @@ export function collectStatusFacts(options = {}) {
299
299
  ? writePath.activeHost.trim().toLowerCase()
300
300
  : String(activeHost).trim().toLowerCase();
301
301
  const matrix = HOST_SUPPORT_MATRIX[hostKey] ?? null;
302
- // Soft only when the host is known and matrix hard-write is false (Cursor/Codex/OpenCode).
302
+ // Soft only when the host is known and matrix hard-write is false (currently OpenCode).
303
303
  const softWriteHost = Boolean(matrix && matrix.capabilities?.['hard-write'] !== true);
304
304
  const writePathHonesty = buildWritePathHonesty(hostKey, hardWriteActive, {
305
305
  packageInstalled: writePath?.enforcementState?.localWrite?.installed !== false,
@@ -382,6 +382,11 @@ export function collectStatusFacts(options = {}) {
382
382
  : arkRulesLoaded
383
383
  ? 0
384
384
  : null,
385
+ leftoverDesignWork:
386
+ options.leftoverDesignWork === true ||
387
+ latest?.leftoverDesignWork === true ||
388
+ latest?.designFitness?.designWeak === true ||
389
+ latest?.doctor?.designFitness?.designWeak === true,
385
390
  improvementCompass,
386
391
  vsBase: (() => {
387
392
  const vsRef = typeof options.vs === 'string' ? options.vs.trim() : '';
@@ -230,6 +230,12 @@ export function resolveStatusNextAction(facts, binding, activation, lastCheck, r
230
230
  summary: 'Local write is advisory for this host — keep a required GitHub status on arkgate-check --strict-merge as the hard merge boundary.',
231
231
  };
232
232
  }
233
+ if (facts.leftoverDesignWork === true) {
234
+ return {
235
+ id: 'map-leftover-design',
236
+ summary: 'Leftover design work remains. Map with /ark-explore, then apply one small refactor with /ark-autopilot. Green imports are not done.',
237
+ };
238
+ }
233
239
  if (rules.arkRulesLoaded && (rules.frozenResidual ?? 0) > 0) {
234
240
  return {
235
241
  id: 'review-arkrules-residual',
@@ -219,6 +219,9 @@ function requiredWriteOperations(relativePath) {
219
219
  if (relativePath === '.cursor/hooks.json' || relativePath.startsWith('.cursor/hooks')) {
220
220
  return ['Write', 'StrReplace'];
221
221
  }
222
+ if (relativePath === '.codex/hooks.json' || relativePath.startsWith('.codex/hooks')) {
223
+ return ['apply_patch'];
224
+ }
222
225
  return ['Write', 'Edit', 'MultiEdit'];
223
226
  }
224
227
 
@@ -445,6 +448,7 @@ export function detectWritePathInventory(root) {
445
448
  const grokHook = hookEvidence(root, '.grok/hooks/ark-write-gate.json');
446
449
  const antigravityHook = hookEvidence(root, '.agents/hooks.json');
447
450
  const cursorHook = cursorHookEvidence(root);
451
+ const codexHook = hookEvidence(root, '.codex/hooks.json');
448
452
  const hosts = {
449
453
  claude: hostRecord(
450
454
  claudeHook.hard,
@@ -478,11 +482,11 @@ export function detectWritePathInventory(root) {
478
482
  [],
479
483
  merge
480
484
  ),
481
- // Codex 0.123+ emits PreToolUse for the native apply_patch handler, but some
482
- // Code Mode hosts execute deferred nested writes without dispatching that
483
- // project hook. Keep the installed hook as best-effort protection; do not
484
- // report a hard boundary that cannot be verified for every write surface.
485
- codex: hostRecord([], codexMcpEvidence(root), [], merge),
485
+ // Current Codex CLI and local ChatGPT Desktop/App Server synchronously run
486
+ // PreToolUse for apply_patch. Disk evidence remains unverified until a fresh
487
+ // covered invocation; hosted/specialized/direct-write paths stay outside scope.
488
+ // Repair envelopes may emit, but host reinjection is not guaranteed.
489
+ codex: hostRecord(codexHook.hard, codexMcpEvidence(root), [], merge),
486
490
  // OpenCode: MCP only (plugin hooks are incomplete / subagent-bypassable).
487
491
  opencode: hostRecord([], opencodeMcpEvidence(root), [], merge),
488
492
  };
@@ -111,9 +111,9 @@ export function detectWritePathCapabilities(root, explicitHost, attempt) {
111
111
  } else if (mode === 'mcp-only') {
112
112
  const honesty =
113
113
  activeHost === 'codex'
114
- ? 'Codex local write is advisory (MCP + best-effort hooks.json not a hard boundary; ' +
115
- 'not equivalent to Claude/Grok PreToolUse hard-write + repair). ' +
116
- 'The hard merge backstop is CI --strict-merge plus a required status check.'
114
+ ? 'Codex MCP is advisory because no complete Ark apply_patch hook was detected. ' +
115
+ 'Install and trust .codex/hooks.json for the operation-scoped pre-write block; ' +
116
+ 'CI --strict-merge plus a required status remains the all-path merge boundary.'
117
117
  : activeHost === 'opencode'
118
118
  ? 'OpenCode local write is advisory (MCP + optional experimental plugin — not a hard boundary; ' +
119
119
  'not equivalent to Claude/Grok/Antigravity PreToolUse hard-write). ' +