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.
- package/CHANGELOG.md +50 -1
- package/README.md +17 -11
- package/bin/ark-check-runtime.mjs +23 -127
- package/bin/ark-mcp-runtime.mjs +75 -49
- package/bin/ark.mjs +19 -78
- package/bin/lib/ci-and-commands.mjs +4 -3
- package/bin/lib/doctor-next-actions.mjs +92 -0
- package/bin/lib/doctor-plan.mjs +59 -69
- package/bin/lib/first-run-help.mjs +221 -0
- package/bin/lib/host-support-matrix.mjs +13 -15
- package/bin/lib/start-preview.mjs +17 -10
- package/bin/lib/status-command.mjs +6 -1
- package/bin/lib/status-manifest.mjs +6 -0
- package/bin/lib/write-path-capabilities.mjs +9 -5
- package/bin/lib/write-path-detect.mjs +3 -3
- package/dist/index.cjs +19 -19
- package/dist/index.d.ts +6 -1
- package/dist/index.js +22 -22
- package/docs/README.md +5 -5
- package/docs/agent-guide.md +19 -14
- package/docs/ai-gates.md +29 -20
- package/docs/develop.md +4 -1
- package/docs/enthusiast/how-to-agent-gates.md +2 -2
- package/docs/package-surface.md +4 -3
- package/docs/product-voice.md +4 -4
- package/docs/threat-model.md +5 -1
- package/docs/use.md +9 -4
- package/package.json +2 -2
- package/server.json +3 -3
- package/templates/agent-skills/README.md +1 -1
- package/templates/agent-skills/ark-adopt/SKILL.md +14 -5
- package/templates/agent-skills/ark-architect/SKILL.md +2 -2
- package/templates/agent-skills/ark-autopilot/SKILL.md +12 -5
- package/templates/agent-skills/ark-contract/SKILL.md +1 -1
- package/templates/agent-skills/ark-coverage/SKILL.md +10 -8
- package/templates/agent-skills/ark-explain/SKILL.md +3 -2
- package/templates/agent-skills/ark-explore/SKILL.md +13 -4
- package/templates/agent-skills/ark-fix/SKILL.md +1 -1
- package/templates/agent-skills/ark-loop/SKILL.md +1 -1
- package/templates/agent-skills/ark-place/SKILL.md +10 -1
- package/templates/agent-skills/ark-think/SKILL.md +3 -2
- package/templates/agent-skills/ark-upgrade/SKILL.md +10 -3
- package/templates/skills/ark-adopt.md +14 -5
- package/templates/skills/ark-architect.md +2 -2
- package/templates/skills/ark-autopilot.md +12 -5
- package/templates/skills/ark-contract.md +1 -1
- package/templates/skills/ark-coverage.md +10 -8
- package/templates/skills/ark-explain.md +3 -2
- package/templates/skills/ark-explore.md +13 -4
- package/templates/skills/ark-fix.md +1 -1
- package/templates/skills/ark-loop.md +1 -1
- package/templates/skills/ark-place.md +10 -1
- package/templates/skills/ark-think.md +3 -2
- 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
|
|
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
|
-
'
|
|
97
|
+
'PreToolUse `apply_patch` in Codex CLI and local ChatGPT Desktop/App Server',
|
|
98
98
|
['apply_patch'],
|
|
99
|
-
|
|
99
|
+
true,
|
|
100
100
|
false,
|
|
101
101
|
{
|
|
102
|
-
// Install writes --hook-repair; envelope can
|
|
102
|
+
// Install writes --hook-repair; envelope can emit, but host reinjection is not guaranteed.
|
|
103
103
|
repairEnvelopeEmitted: true,
|
|
104
104
|
operationCoverage: {
|
|
105
|
-
apply_patch:
|
|
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:
|
|
156
|
-
//
|
|
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
|
|
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
|
|
192
|
-
\`preToolUse\` ops
|
|
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:
|
|
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}
|
|
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.
|
|
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 (
|
|
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
|
|
482
|
-
//
|
|
483
|
-
//
|
|
484
|
-
//
|
|
485
|
-
codex: hostRecord(
|
|
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
|
|
115
|
-
'
|
|
116
|
-
'
|
|
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). ' +
|