arkgate 4.6.1 → 4.6.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/CHANGELOG.md +50 -1
  2. package/README.md +17 -11
  3. package/bin/ark-check-runtime.mjs +23 -127
  4. package/bin/ark-mcp-runtime.mjs +75 -49
  5. package/bin/ark.mjs +19 -78
  6. package/bin/lib/ci-and-commands.mjs +4 -3
  7. package/bin/lib/doctor-next-actions.mjs +92 -0
  8. package/bin/lib/doctor-plan.mjs +59 -69
  9. package/bin/lib/first-run-help.mjs +221 -0
  10. package/bin/lib/host-support-matrix.mjs +13 -15
  11. package/bin/lib/start-preview.mjs +17 -10
  12. package/bin/lib/status-command.mjs +6 -1
  13. package/bin/lib/status-manifest.mjs +6 -0
  14. package/bin/lib/write-path-capabilities.mjs +9 -5
  15. package/bin/lib/write-path-detect.mjs +3 -3
  16. package/dist/index.cjs +19 -19
  17. package/dist/index.d.ts +6 -1
  18. package/dist/index.js +22 -22
  19. package/docs/README.md +5 -5
  20. package/docs/agent-guide.md +19 -14
  21. package/docs/ai-gates.md +29 -20
  22. package/docs/develop.md +4 -1
  23. package/docs/enthusiast/how-to-agent-gates.md +2 -2
  24. package/docs/package-surface.md +4 -3
  25. package/docs/product-voice.md +4 -4
  26. package/docs/threat-model.md +5 -1
  27. package/docs/use.md +9 -4
  28. package/package.json +2 -2
  29. package/server.json +3 -3
  30. package/templates/agent-skills/README.md +1 -1
  31. package/templates/agent-skills/ark-adopt/SKILL.md +14 -5
  32. package/templates/agent-skills/ark-architect/SKILL.md +2 -2
  33. package/templates/agent-skills/ark-autopilot/SKILL.md +12 -5
  34. package/templates/agent-skills/ark-contract/SKILL.md +1 -1
  35. package/templates/agent-skills/ark-coverage/SKILL.md +10 -8
  36. package/templates/agent-skills/ark-explain/SKILL.md +3 -2
  37. package/templates/agent-skills/ark-explore/SKILL.md +13 -4
  38. package/templates/agent-skills/ark-fix/SKILL.md +1 -1
  39. package/templates/agent-skills/ark-loop/SKILL.md +1 -1
  40. package/templates/agent-skills/ark-place/SKILL.md +10 -1
  41. package/templates/agent-skills/ark-think/SKILL.md +3 -2
  42. package/templates/agent-skills/ark-upgrade/SKILL.md +10 -3
  43. package/templates/skills/ark-adopt.md +14 -5
  44. package/templates/skills/ark-architect.md +2 -2
  45. package/templates/skills/ark-autopilot.md +12 -5
  46. package/templates/skills/ark-contract.md +1 -1
  47. package/templates/skills/ark-coverage.md +10 -8
  48. package/templates/skills/ark-explain.md +3 -2
  49. package/templates/skills/ark-explore.md +13 -4
  50. package/templates/skills/ark-fix.md +1 -1
  51. package/templates/skills/ark-loop.md +1 -1
  52. package/templates/skills/ark-place.md +10 -1
  53. package/templates/skills/ark-think.md +3 -2
  54. package/templates/skills/ark-upgrade.md +10 -3
package/bin/ark.mjs CHANGED
@@ -35,6 +35,7 @@ import {
35
35
  } from './lib/prepare-change.mjs';
36
36
  import { runStatusCommand } from './lib/status-command.mjs';
37
37
  import { runAgentProjectionCommand } from './lib/agent-projection-command.mjs';
38
+ import { setupUsage, setupUsageAll, upgradeUsage } from './lib/first-run-help.mjs';
38
39
 
39
40
  const here = path.dirname(fileURLToPath(import.meta.url));
40
41
  const arkCheck = path.join(here, 'ark-check.mjs');
@@ -90,13 +91,14 @@ function parseArgs(argv) {
90
91
  check: false,
91
92
  stdout: false,
92
93
  help: false,
94
+ all: false,
93
95
  version: false,
94
96
  };
95
97
 
96
98
  const requireValue = (flag, index) => {
97
99
  const value = argv[index + 1];
98
100
  if (value === undefined || value.startsWith('-')) {
99
- throw new Error(`Missing value for ${flag}. Run ark --help for usage.`);
101
+ throw new Error(`Missing value for ${flag}. Run arkgate --help for usage.`);
100
102
  }
101
103
  return value;
102
104
  };
@@ -143,79 +145,15 @@ function parseArgs(argv) {
143
145
  else if (arg === '--check') args.check = true;
144
146
  else if (arg === '--stdout') args.stdout = true;
145
147
  else if (arg === '--help' || arg === '-h' || arg === 'help') args.help = true;
148
+ else if (arg === '--all') args.all = true;
146
149
  else if (arg === '--version' || arg === '-V') args.version = true;
147
150
  else if (!arg.startsWith('-') && args.command === undefined) args.command = arg;
148
- else throw new Error(`Unknown argument: ${arg}. Run ark --help for usage.`);
151
+ else throw new Error(`Unknown argument: ${arg}. Run arkgate --help for usage.`);
149
152
  }
150
153
 
151
154
  return args;
152
155
  }
153
156
 
154
- function usage() {
155
- return `Usage:
156
- ark start [--root <project>] [--tools <host>] [--require-write-hook <host>] [--install] [--apply] [--json]
157
- ark init [--root <project>] [--preset hexagonal|layered|feature-sliced|monorepo|ui-surface|vertical-slice|ddd-bounded-contexts|clean-architecture|onion-architecture]
158
- [--archetype <playbook-id>] [--tools <list>] [--require-write-hook <host>] [--yes] [--force] [--no-strict]
159
- ark upgrade [--root <project>] [--tools <list>] [--apply] [--plan-digest <sha256>] [--accept-conflicts] [--refresh-skills] [--json] [--no-install] [--no-strict]
160
- ark preflight --changes <change-set.json> [--change-map <map.json>] [--root <project>] [--config ark.config.json] [--manifest <manifest.json>] [--tsconfig <tsconfig.json>] [--json]
161
- ark status [--root <project>] [--config ark.config.json] [--json] [--vs <git-ref>]
162
- [--expected-root <abs>] [--expected-project-id sha256:…] [--tools <host>]
163
- ark agents-md [--root <project>] [--config ark.config.json] [--write] [--check] [--stdout] [--json]
164
- [--tools <host>]
165
-
166
- Commands:
167
- start New here? Analyze and preview the complete setup. Read-only unless --apply.
168
- init Configure Ark project enforcement with explicit prompts.
169
- upgrade Preview identity-proven Ark-managed asset updates. With package install,
170
- --apply bumps toward registry latest when behind (not only when CLI ≠ pin)
171
- and recomputes the preview; a second explicit --apply --no-install applies
172
- those exact bytes and verifies them. --refresh-skills opts in to rewrite
173
- customized managed skills to package templates (never silent default).
174
- (alias: ark update)
175
- preflight Validate one atomic create/update/delete set without writing project files.
176
- status Unified session/project manifest (identity, activation, last check, rules).
177
- Never prompts. Prefer --json for agents; CI=1 forces JSON.
178
- agents-md Version-matched agent contract projection (ACS04). Stamps package version +
179
- contract summary into a managed AGENTS.md block. Non-authoritative — not a
180
- gate input. Preview by default; --write merges without clobbering outside
181
- regions; --check fails on version drift; --stdout prints the block only.
182
- (aliases: agents-md, agent-projection)
183
-
184
- Options:
185
- --yes Non-interactive defaults: create config if needed, install gate templates, run strict check.
186
- (Also the implicit default when stdin/stdout are not a TTY — agents never hang on prompts.)
187
- --force Allow generated files to overwrite existing files.
188
- --no-strict Skip the final strict ark-check run.
189
- --install Pin and install arkgate as a project devDependency (default for start).
190
- --no-install Skip adding/installing arkgate as a project devDependency (start/upgrade).
191
- --apply Apply a start plan; for upgrade, update/repreview or apply managed bytes.
192
- --accept-conflicts
193
- Allow upgrade to recreate deleted managed assets or replace recorded conflicts.
194
- --plan-digest Digest emitted by an upgrade preview; required to apply managed bytes.
195
- --json Emit the start/upgrade/status/agents-md preview as deterministic machine-readable JSON.
196
- --write For agents-md: merge the version-matched projection into AGENTS.md.
197
- --check For agents-md: exit 1 when projection stamp drifts from package version.
198
- --stdout For agents-md: print the projection block only (no file write).
199
- --expected-root / --expected-project-id
200
- Optional project expectation for status (MCP-compatible binding check).
201
- --preset Start from a named architecture preset instead of detection.
202
- --archetype Application shape from templates/architecture-playbook.json (maps to the matching preset).
203
- Valid ids: crud-product, api-backend, frontend-surface, library-sdk, cli-utility,
204
- worker-pipeline, event-coordinator, integration-bridge, multi-app-workspace, prototype-spike,
205
- vertical-slice-product, ddd-bounded-contexts.
206
- --tools One active agent host for start (claude,cursor,codex,grok,windsurf,cline,copilot,kiro,roo,continue,gemini).
207
- Omit to use the active host; an unknown host creates only the shared compact router.
208
- --remove-host <host>
209
- Preview or apply removal of that compact host integration; re-add it with --tools <host>.
210
- --require-write-hook <host>
211
- Require and verify a hard local write hook for Claude, Grok, Antigravity, or Cursor.
212
- Codex/OpenCode are advisory-write plus hard CI merge only; impossible requests fail before any write.
213
-
214
- Interactive mode (TTY, no --yes): asks what application shape you are building and maps it to a preset.
215
- Non-interactive (no TTY): uses the same defaults as --yes — never calls readline on a null interface.
216
- `;
217
- }
218
-
219
157
  function cliVersion() {
220
158
  try {
221
159
  const pkg = JSON.parse(fs.readFileSync(path.join(here, '..', 'package.json'), 'utf8'));
@@ -796,23 +734,22 @@ async function start(args) {
796
734
  }
797
735
  console.log('');
798
736
  console.log('Next (the only flow you need):');
737
+ console.log(` 1. Status: ${arkCommand(root, 'arkgate-check', '--doctor')} — do primary next action #1`);
799
738
  if (falseGreenGap) {
800
- console.log(' 1. In your agent: /ark-adopt — fix the architecture config first');
801
- console.log(' → reclassify I/O dirs out of Application; then /ark-autopilot for residual debt.');
739
+ console.log(' 2. Session 0 in your agent: /ark-adopt — fix the architecture config first');
740
+ console.log(' → reclassify I/O dirs out of Application; leftover design later via /ark-explore then /ark-autopilot.');
802
741
  } else {
803
- console.log(' 1. In your agent: /ark-autopilot');
804
- console.log(' → explore first, dual plan (remediation + pattern bets), safe fixes, leave gates on.');
742
+ console.log(' 2. Session 0 in your agent: /ark-adopt');
743
+ console.log(' → mark the path (greenfield or brownfield). Day-to-day new files: /ark-place.');
805
744
  }
806
- console.log(` 2. Status anytime: ${arkCommand(root, 'ark-check', '--doctor')}`);
807
- console.log(` 3. After edits: ${arkCommand(root, 'ark-check', '--root . --config ark.config.json --strict-merge')}`);
745
+ console.log(` 3. After edits: ${arkCommand(root, 'arkgate-check', '--root . --config ark.config.json --strict-merge')}`);
808
746
  if (mode === 'adapt' && planOk && !falseGreenGap) {
809
747
  console.log(
810
- ` 4. When green but cores still optional: ${arkCommand(root, 'ark-check', '--ratchet-cores')} → honest ENFORCE`
748
+ ` 4. When green but cores still optional: ${arkCommand(root, 'arkgate-check', '--ratchet-cores')} → honest ENFORCE`
811
749
  );
812
750
  }
813
751
  console.log('');
814
- console.log('Optional later: ark-check --report ark-report.html (captures a day-zero/evolution report).');
815
- console.log('Optional later: --plan · --coverage · /ark-explore · /ark-autopilot · /ark-place · ark upgrade');
752
+ console.log('Optional later: leftover design → /ark-explore then /ark-autopilot; bump arkgate upgrade.');
816
753
  return 0;
817
754
  } finally {
818
755
  rl?.close();
@@ -831,8 +768,12 @@ async function main() {
831
768
  console.log(cliVersion());
832
769
  return 0;
833
770
  }
771
+ if (args.help && (args.command === 'upgrade' || args.command === 'update')) {
772
+ console.log(upgradeUsage());
773
+ return 0;
774
+ }
834
775
  if (args.help || !args.command) {
835
- console.log(usage());
776
+ console.log(args.all ? setupUsageAll() : setupUsage());
836
777
  return 0;
837
778
  }
838
779
 
@@ -961,7 +902,7 @@ async function main() {
961
902
  }
962
903
 
963
904
  console.error(`Unknown command: ${args.command}`);
964
- console.error(usage());
905
+ console.error(setupUsage());
965
906
  return 2;
966
907
  }
967
908
 
@@ -370,9 +370,10 @@ ${steps}
370
370
  ${placementBody}
371
371
 
372
372
  The project is only considered Ark-enforced when its host-appropriate write path is configured
373
- and the CI check passes. Only Claude/Grok/Antigravity/Cursor provide a hard local write boundary
374
- (Cursor: Write/StrReplace when .cursor/hooks.json is trusted); Codex/OpenCode use
375
- advisory MCP plus CI. The experimental runtime is not required.
373
+ and the CI check passes. Claude/Grok/Antigravity/Cursor provide hard local boundaries for their
374
+ listed operations. Codex provides a hard boundary for complete local apply_patch when
375
+ .codex/hooks.json is trusted and runtime-observed; hosted/specialized/direct-write paths remain
376
+ CI-backed. OpenCode uses advisory MCP plus CI. The experimental runtime is not required.
376
377
  `;
377
378
  }
378
379
 
@@ -0,0 +1,92 @@
1
+ /**
2
+ * Rank doctor next actions from already-computed facts (no I/O).
3
+ * Lets the human printer show light + #1 before honesty/compass sections.
4
+ */
5
+ import { arkCommand } from '../ark-shared.mjs';
6
+ import { skillGapsForActiveHost } from './agent-gates.mjs';
7
+ import { agentHomeConcernIsActive, agentHomeRefreshCommand } from './agent-homes.mjs';
8
+ import { mergePostGreenTopActions } from './post-green-path.mjs';
9
+
10
+ export function collectDoctorNextActions(ctx) {
11
+ const actions = [];
12
+ if (!ctx.analysisComplete) actions.push('restore complete analysis, then rerun ark-check --doctor');
13
+ if (ctx.designSmells.length > 0 && ctx.postGreenPath) actions.push(ctx.postGreenPath.action);
14
+ if (ctx.coverageHonesty.greenIsNotEnforcement && ctx.coverageHonesty.worseThanNoGate) {
15
+ actions.push('raise governed coverage above a minority slice before treating green as enforcement');
16
+ }
17
+ if (ctx.cov.suggestions.length > 0) actions.push('classify the ungoverned directories (/ark-adopt)');
18
+ if (ctx.packageVersionTruth?.dualTruth) {
19
+ actions.push(
20
+ ctx.dualTruthNext ||
21
+ 'bump package.json arkgate pin to match this CLI (or install without --no-install)'
22
+ );
23
+ } else if (ctx.packageVersionTruth?.code === 'PACKAGE_PIN_ABSENT') {
24
+ actions.push(
25
+ ctx.dualTruthNext ||
26
+ 'Add arkgate to package.json and install so CI/npx resolve this CLI (PACKAGE_PIN_ABSENT)'
27
+ );
28
+ }
29
+ if (ctx.activeCount > 0) {
30
+ actions.push(
31
+ `resolve the non-baselined violations — see the classified plan (${arkCommand(ctx.root, 'ark-check', '--plan')}), then /ark-autopilot`
32
+ );
33
+ }
34
+ if (ctx.writePath?.gap?.fix) actions.push(ctx.writePath.gap.fix);
35
+ if (ctx.gatesMissing.length > 0) {
36
+ actions.push(`install gates (${arkCommand(ctx.root, 'ark-check', '--install-agent-gates')})`);
37
+ }
38
+ const humanSkillGaps = skillGapsForActiveHost(ctx.skillGaps);
39
+ const legacyCodex = humanSkillGaps.some((g) => g.tool === 'codex' && g.legacyPromptsOnly);
40
+ const remainingGaps = humanSkillGaps.filter(
41
+ (g) => !(g.tool === 'codex' && (g.legacyPromptsOnly || g.legacyAdvisory))
42
+ );
43
+ const remMiss = remainingGaps.reduce((s, g) => s + g.missing, 0);
44
+ const remStale = remainingGaps.reduce((s, g) => s + g.stale, 0);
45
+ if (legacyCodex) {
46
+ actions.push('install Codex SKILL.md catalog (--install-agent-gates --skills-only --tools codex --force)');
47
+ }
48
+ if (remMiss + remStale > 0) {
49
+ actions.push('refresh /ark-* skills (--install-agent-gates --skills-only --force)');
50
+ }
51
+ if (ctx.codexHomeGap && ctx.codexConcernActive) {
52
+ actions.push(
53
+ ctx.codexHomeGap.catalogMetadataInvalid
54
+ ? 'repair invalid Codex home catalog metadata after verifying the newest installed version'
55
+ : 'refresh Codex home skills (--install-agent-gates --skills-only --codex-home --force)'
56
+ );
57
+ }
58
+ for (const gap of ctx.agentHomeGaps) {
59
+ if (agentHomeConcernIsActive(gap.host)) {
60
+ actions.push(
61
+ gap.catalogMetadataInvalid
62
+ ? `repair invalid ${gap.label} home catalog metadata after verifying the newest installed version`
63
+ : `refresh ${gap.label} shared agent skills (${agentHomeRefreshCommand(ctx.root, gap)})`
64
+ );
65
+ }
66
+ }
67
+ if (ctx.analysisComplete && ctx.baselineHonesty?.dirtyBaselineRisk) {
68
+ actions.push('review dirty baseline freezes — fix the contract before trusting green-via-freeze');
69
+ }
70
+ if (ctx.analysisComplete && ctx.staleBaseline > 0) {
71
+ actions.push('tighten the baseline (--update-baseline)');
72
+ }
73
+ if (ctx.staleRunners.length > 0) {
74
+ actions.push(
75
+ `migrate command runners (${arkCommand(ctx.root, 'ark-check', '--install-agent-gates --migrate-commands')})`
76
+ );
77
+ }
78
+ for (const gap of ctx.adoption.gaps) {
79
+ if (!gap.deferred) actions.push(gap.fix || gap.message);
80
+ }
81
+ if (ctx.safety && ctx.safetyHasEntries) {
82
+ actions.push('resolve strict safety diagnostics before treating CI as enforcement');
83
+ }
84
+ if (ctx.showNewHere) {
85
+ actions.unshift('finish ark start (preview + --apply), then re-run --doctor');
86
+ }
87
+ const unique = mergePostGreenTopActions(actions, ctx.postGreenPath);
88
+ if (ctx.designFitness.designWeak && unique.length === 0 && ctx.postGreenPath) {
89
+ unique.push(ctx.postGreenPath.action);
90
+ }
91
+ return unique;
92
+ }
@@ -15,9 +15,9 @@ import { describePackageVersionDualTruth } from './field-install.mjs';
15
15
  import {
16
16
  detectAgentHomeGaps,
17
17
  agentHomeConcernIsActive,
18
- agentHomeRefreshCommand,
19
18
  } from './agent-homes.mjs';
20
19
  import { operatingModeTitle } from './product-copy.mjs';
20
+ import { collectDoctorNextActions } from './doctor-next-actions.mjs';
21
21
  export { summarizeRulesUnderContract };
22
22
 
23
23
  /** Optional S3 dual-match classifier when ark-shared exports it (soft dep for S5 landing). */
@@ -50,7 +50,6 @@ import {
50
50
  } from './design-smells.mjs';
51
51
  import {
52
52
  buildPostGreenNextAction,
53
- mergePostGreenTopActions,
54
53
  isDoctorHealthyNothingToDo,
55
54
  DESIGN_WEAK_HONESTY_FLAGS,
56
55
  } from './post-green-path.mjs';
@@ -825,9 +824,7 @@ export function runDoctor(root, config, files, rules, violations, asJson, option
825
824
  const ok = color.green('✓');
826
825
  const warn = color.yellow('!');
827
826
  const bad = color.red('✗');
828
- const actions = [];
829
827
  const line = (mark, text) => console.log(` ${mark} ${text}`);
830
- if (!analysisComplete) actions.push('restore complete analysis, then rerun ark-check --doctor');
831
828
  console.log(color.bold(`Ark doctor — ${path.basename(path.resolve(root)) || '.'}`));
832
829
  if (!analysisComplete) line(warn, analysisIncompleteStatement(completeness));
833
830
 
@@ -866,6 +863,63 @@ export function runDoctor(root, config, files, rules, violations, asJson, option
866
863
  );
867
864
  }
868
865
 
866
+ const safetyHasEntries = Boolean(
867
+ options.safety &&
868
+ [
869
+ options.safety.nonLiteralDynamicImports,
870
+ options.safety.tsSuppressions,
871
+ options.safety.anyCasts,
872
+ options.safety.inMemoryProductionStores,
873
+ options.safety.disabledPeerIsolationRules,
874
+ ].some((entries) => Array.isArray(entries) && entries.length > 0)
875
+ );
876
+ const uniqueActions = collectDoctorNextActions({
877
+ root,
878
+ analysisComplete,
879
+ designSmells,
880
+ postGreenPath,
881
+ coverageHonesty,
882
+ cov,
883
+ packageVersionTruth,
884
+ dualTruthNext,
885
+ activeCount,
886
+ writePath,
887
+ gatesMissing,
888
+ skillGaps,
889
+ codexHomeGap: detectCodexHomeGap(root),
890
+ codexConcernActive: codexConcernIsActive(),
891
+ agentHomeGaps,
892
+ baselineHonesty,
893
+ staleBaseline,
894
+ staleRunners,
895
+ adoption,
896
+ safety: options.safety,
897
+ safetyHasEntries,
898
+ showNewHere,
899
+ designFitness,
900
+ });
901
+ console.log('');
902
+ if (isDoctorHealthyNothingToDo(designFitness, uniqueActions)) {
903
+ console.log(color.green('✔ Healthy — nothing to do.'));
904
+ console.log(color.dim(' Contract edges and design residual are clear. Keep write path + CI.'));
905
+ } else {
906
+ console.log(color.bold('Primary next action'));
907
+ console.log(` 1. ${uniqueActions[0]}`);
908
+ if (uniqueActions.length > 1) {
909
+ console.log(color.bold(`Also (${uniqueActions.length - 1}):`));
910
+ uniqueActions.slice(1).forEach((action, index) => console.log(` ${index + 2}. ${action}`));
911
+ }
912
+ if (postGreenPath) {
913
+ console.log(
914
+ color.dim(
915
+ ` Shape residual is the primary door under ${modeTitle} — do not skill-shop explore vs coverage vs think.`
916
+ )
917
+ );
918
+ } else {
919
+ console.log(color.dim(' Doctor is the control plane: do #1 first, then re-run --doctor.'));
920
+ }
921
+ }
922
+
869
923
  // P0-B — single honesty surface (never a score; never "all good" when residual remains).
870
924
  if (productHonesty) {
871
925
  console.log('');
@@ -907,11 +961,7 @@ export function runDoctor(root, config, files, rules, violations, asJson, option
907
961
  line(' ', color.dim(`evidence: ${smell.evidence.slice(0, 4).join(', ')}`));
908
962
  }
909
963
  }
910
- if (postGreenPath) {
911
- // Rank first via mergePostGreenTopActions at the end (Q01 single door).
912
- actions.push(postGreenPath.action);
913
- }
914
- // Q04 — surface one next pilot under design-weak.
964
+ // Q04 — surface one next pilot under leftover design work.
915
965
  if (pilotLoop?.active && pilotLoop.nextPilot) {
916
966
  const np = pilotLoop.nextPilot;
917
967
  line(
@@ -967,13 +1017,9 @@ export function runDoctor(root, config, files, rules, violations, asJson, option
967
1017
  line(govMark, `Governed: ${cov.governed.percent}% (${cov.governed.classifiedFiles}/${cov.governed.totalFiles} files)`);
968
1018
  if (coverageHonesty.greenIsNotEnforcement) {
969
1019
  line(coverageHonesty.worseThanNoGate ? bad : warn, coverageHonesty.message);
970
- if (coverageHonesty.worseThanNoGate) {
971
- actions.push('raise governed coverage above a minority slice before treating green as enforcement');
972
- }
973
1020
  }
974
1021
  if (cov.suggestions.length > 0) {
975
1022
  line(warn, `${cov.suggestions.length} ungoverned director(y/ies) — proposals: ${arkCommand(root, 'ark-check', '--coverage')}`);
976
- actions.push('classify the ungoverned directories (/ark-adopt)');
977
1023
  }
978
1024
  if (cov.emptyLayers.length > 0) line(warn, `Empty layers (pattern matches nothing): ${cov.emptyLayers.join(', ')}`);
979
1025
  if (cov.layersWithoutRules.length > 0) line(warn, `Layers with no rule edge: ${cov.layersWithoutRules.join(', ')}`);
@@ -989,18 +1035,10 @@ export function runDoctor(root, config, files, rules, violations, asJson, option
989
1035
  console.log('');
990
1036
  console.log(color.bold('Package pin (dual-truth)'));
991
1037
  line(warn, packageVersionTruth.note);
992
- actions.push(
993
- dualTruthNext ||
994
- 'bump package.json arkgate pin to match this CLI (or install without --no-install)'
995
- );
996
1038
  } else if (packageVersionTruth?.code === 'PACKAGE_PIN_ABSENT') {
997
1039
  console.log('');
998
1040
  console.log(color.bold('Package pin'));
999
1041
  line(warn, packageVersionTruth.note);
1000
- actions.push(
1001
- dualTruthNext ||
1002
- 'Add arkgate to package.json and install so CI/npx resolve this CLI (PACKAGE_PIN_ABSENT)'
1003
- );
1004
1042
  }
1005
1043
  if (options.configWalkedUp && options.configRoot) {
1006
1044
  line(
@@ -1030,7 +1068,6 @@ export function runDoctor(root, config, files, rules, violations, asJson, option
1030
1068
  line(warn, 'Low governed coverage or fresh config — finish start, then re-run doctor before adding layers of code.');
1031
1069
  }
1032
1070
  line(ok, `Optional sensor detail: ${arkCommand(root, 'ark-check', '--recommend')}`);
1033
- actions.unshift('finish ark start (preview + --apply), then re-run --doctor');
1034
1071
  }
1035
1072
 
1036
1073
  console.log('');
@@ -1058,11 +1095,6 @@ export function runDoctor(root, config, files, rules, violations, asJson, option
1058
1095
  if (summary.concentrated) {
1059
1096
  line(warn, color.dim(`${Math.round(summary.dominantShare * 100)}% on one edge (${summary.dominant}) — likely a contract fix, not debt`));
1060
1097
  }
1061
- if (activeCount > 0) {
1062
- actions.push(
1063
- `resolve the non-baselined violations — see the classified plan (${arkCommand(root, 'ark-check', '--plan')}), then /ark-autopilot`
1064
- );
1065
- }
1066
1098
  }
1067
1099
 
1068
1100
  console.log('');
@@ -1105,7 +1137,6 @@ export function runDoctor(root, config, files, rules, violations, asJson, option
1105
1137
  line(writePath.gap.severity === 'warn' ? warn : warn, writePath.gap.message);
1106
1138
  if (writePath.gap.fix) {
1107
1139
  line(' ', color.dim(`Fix: ${writePath.gap.fix}`));
1108
- actions.push(writePath.gap.fix);
1109
1140
  }
1110
1141
  }
1111
1142
 
@@ -1114,7 +1145,6 @@ export function runDoctor(root, config, files, rules, violations, asJson, option
1114
1145
  if (gatesMissing.length === 0) line(ok, 'Shared gate artifacts found on disk (AGENTS.md, .mcp.json, CI); runtime activation is reported separately');
1115
1146
  else {
1116
1147
  line(bad, `Missing gates: ${gatesMissing.join(', ')}`);
1117
- actions.push(`install gates (${arkCommand(root, 'ark-check', '--install-agent-gates')})`);
1118
1148
  }
1119
1149
  const humanSkillGaps = skillGapsForActiveHost(skillGaps);
1120
1150
  const legacyCodex = humanSkillGaps.some((g) => g.tool === 'codex' && g.legacyPromptsOnly);
@@ -1129,7 +1159,6 @@ export function runDoctor(root, config, files, rules, violations, asJson, option
1129
1159
  if (remMiss + remStale === 0 && !legacyCodex) line(ok, '/ark-* skills current for detected tools');
1130
1160
  if (legacyCodex) {
1131
1161
  line(warn, 'Codex: legacy flat .codex/prompts only (not a loadable skill catalog)');
1132
- actions.push('install Codex SKILL.md catalog (--install-agent-gates --skills-only --tools codex --force)');
1133
1162
  }
1134
1163
  if (codexLegacySafeDelete) {
1135
1164
  line(
@@ -1144,7 +1173,6 @@ export function runDoctor(root, config, files, rules, violations, asJson, option
1144
1173
  warn,
1145
1174
  `${remMiss} missing / ${remStale} content-behind-package /ark-* skill(s) for ${remainingGaps.map((g) => g.tool).join(', ')}`
1146
1175
  );
1147
- actions.push('refresh /ark-* skills (--install-agent-gates --skills-only --force)');
1148
1176
  }
1149
1177
  const codexHomeGap = detectCodexHomeGap(root);
1150
1178
  if (codexHomeGap) {
@@ -1159,7 +1187,6 @@ export function runDoctor(root, config, files, rules, violations, asJson, option
1159
1187
  line(color.dim('·'), color.dim(`Codex home skills ${parts.join(', ')} (deferred — not on Codex session)`));
1160
1188
  } else {
1161
1189
  line(warn, `Codex home skills ${parts.join(', ')}`);
1162
- actions.push(codexHomeGap.catalogMetadataInvalid ? 'repair invalid Codex home catalog metadata after verifying the newest installed version' : 'refresh Codex home skills (--install-agent-gates --skills-only --codex-home --force)');
1163
1190
  }
1164
1191
  }
1165
1192
  for (const gap of agentHomeGaps) {
@@ -1174,11 +1201,6 @@ export function runDoctor(root, config, files, rules, violations, asJson, option
1174
1201
  line(color.dim('·'), color.dim(`${summary} (deferred — not this session)`));
1175
1202
  } else {
1176
1203
  line(warn, summary);
1177
- actions.push(
1178
- gap.catalogMetadataInvalid
1179
- ? `repair invalid ${gap.label} home catalog metadata after verifying the newest installed version`
1180
- : `refresh ${gap.label} shared agent skills (${agentHomeRefreshCommand(root, gap)})`
1181
- );
1182
1204
  }
1183
1205
  }
1184
1206
 
@@ -1193,11 +1215,9 @@ export function runDoctor(root, config, files, rules, violations, asJson, option
1193
1215
  line(baseMark, `${baseline.keys.size} frozen key(s)${analysisComplete ? '' : ' — stale comparison not verified'}`);
1194
1216
  if (analysisComplete && baselineHonesty.dirtyBaselineRisk) {
1195
1217
  line(warn, baselineHonesty.message);
1196
- actions.push('review dirty baseline freezes — fix the contract before trusting green-via-freeze');
1197
1218
  }
1198
1219
  if (analysisComplete && staleBaseline > 0) {
1199
1220
  line(warn, `${staleBaseline} stale entr(y/ies) no longer occur — tighten with --update-baseline`);
1200
- actions.push('tighten the baseline (--update-baseline)');
1201
1221
  }
1202
1222
  }
1203
1223
 
@@ -1206,7 +1226,6 @@ export function runDoctor(root, config, files, rules, violations, asJson, option
1206
1226
  if (staleRunners.length === 0) line(ok, 'Emitted commands match the package manager');
1207
1227
  else {
1208
1228
  line(warn, `Stale runner in ${staleRunners.join(', ')}`);
1209
- actions.push(`migrate command runners (${arkCommand(root, 'ark-check', '--install-agent-gates --migrate-commands')})`);
1210
1229
  }
1211
1230
 
1212
1231
  // Adoption completeness (hosts, MCP health, codex home, core optionality, origin, baseline policy)
@@ -1232,7 +1251,6 @@ export function runDoctor(root, config, files, rules, violations, asJson, option
1232
1251
  if (gap.fix) {
1233
1252
  line(' ', color.dim(gap.deferred ? `When using Codex: ${gap.fix}` : `Fix: ${gap.fix}`));
1234
1253
  }
1235
- if (!gap.deferred) actions.push(gap.fix || gap.message);
1236
1254
  }
1237
1255
  if (adoption.layerBalance) {
1238
1256
  line(warn, color.dim(adoption.layerBalance.educational));
@@ -1269,34 +1287,6 @@ export function runDoctor(root, config, files, rules, violations, asJson, option
1269
1287
  for (const [label, entries] of rows) {
1270
1288
  line(entries.length === 0 ? ok : warn, `${label}: ${entries.length}`);
1271
1289
  }
1272
- if (rows.some(([, entries]) => entries.length > 0)) {
1273
- actions.push('resolve strict safety diagnostics before treating CI as enforcement');
1274
- }
1275
1290
  }
1276
1291
 
1277
- console.log('');
1278
- const uniqueActions = mergePostGreenTopActions(actions, postGreenPath);
1279
- if (isDoctorHealthyNothingToDo(designFitness, uniqueActions)) {
1280
- console.log(color.green('✔ Healthy — nothing to do.'));
1281
- console.log(color.dim(' Contract edges and design residual are clear. Keep write path + CI.'));
1282
- } else {
1283
- if (designFitness.designWeak && uniqueActions.length === 0 && postGreenPath) {
1284
- uniqueActions.push(postGreenPath.action);
1285
- }
1286
- console.log(color.bold(`Primary next action`));
1287
- console.log(` 1. ${uniqueActions[0]}`);
1288
- if (uniqueActions.length > 1) {
1289
- console.log(color.bold(`Also (${uniqueActions.length - 1}):`));
1290
- uniqueActions.slice(1).forEach((action, index) => console.log(` ${index + 2}. ${action}`));
1291
- }
1292
- if (postGreenPath) {
1293
- console.log(
1294
- color.dim(
1295
- ` Shape residual is the primary door under ${modeTitle} — do not skill-shop explore vs coverage vs think.`
1296
- )
1297
- );
1298
- } else {
1299
- console.log(color.dim(' Doctor is the control plane: do #1 first, then re-run --doctor.'));
1300
- }
1301
- }
1302
1292
  }