peaks-loop 4.0.0-beta → 4.0.0-beta.2

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 (115) hide show
  1. package/CHANGELOG.md +105 -87
  2. package/README-en.md +1 -1
  3. package/README.md +1 -1
  4. package/dist/src/cli/commands/{solo-commands.d.ts → code-commands.d.ts} +24 -24
  5. package/dist/src/cli/commands/{solo-commands.js → code-commands.js} +78 -78
  6. package/dist/src/cli/commands/core/skill-command.js +1 -1
  7. package/dist/src/cli/commands/dispatch-from-dag.js +1 -1
  8. package/dist/src/cli/commands/hook-handle.js +7 -7
  9. package/dist/src/cli/commands/job-commands.js +2 -2
  10. package/dist/src/cli/commands/session-auto-compact-hook-command.js +2 -2
  11. package/dist/src/cli/commands/sub-agent-shared.d.ts +1 -1
  12. package/dist/src/cli/commands/user-touchpoint-commands.d.ts +4 -4
  13. package/dist/src/cli/commands/user-touchpoint-commands.js +14 -14
  14. package/dist/src/cli/commands/workflow-commands.js +22 -22
  15. package/dist/src/cli/program.js +6 -6
  16. package/dist/src/services/artifacts/repair-cycle-service.js +1 -1
  17. package/dist/src/services/audit/enforcers/active-skill-resolver.d.ts +1 -1
  18. package/dist/src/services/audit/enforcers/active-skill-resolver.js +1 -1
  19. package/dist/src/services/audit/enforcers/{solo-code-ban.d.ts → code-ban.d.ts} +6 -6
  20. package/dist/src/services/audit/enforcers/{solo-code-ban.js → code-ban.js} +6 -6
  21. package/dist/src/services/audit/red-line-catalog.d.ts +2 -2
  22. package/dist/src/services/audit/red-line-catalog.js +5 -5
  23. package/dist/src/services/audit/types.d.ts +2 -2
  24. package/dist/src/services/{solo → code}/auto-compact-orchestrator.d.ts +1 -1
  25. package/dist/src/services/{solo → code}/auto-compact-orchestrator.js +3 -3
  26. package/dist/src/services/{solo → code}/dag-orchestrator.d.ts +1 -1
  27. package/dist/src/services/{solo → code}/dag-orchestrator.js +1 -1
  28. package/dist/src/services/{solo → code}/emit-handoff.d.ts +1 -1
  29. package/dist/src/services/{solo → code}/emit-handoff.js +1 -1
  30. package/dist/src/services/{solo → code}/job-shape-decision.d.ts +1 -1
  31. package/dist/src/services/{solo → code}/job-shape-decision.js +5 -5
  32. package/dist/src/services/{solo → code}/mode-gate.d.ts +9 -9
  33. package/dist/src/services/{solo → code}/mode-gate.js +5 -5
  34. package/dist/src/services/{solo → code}/post-compact-detector.d.ts +3 -3
  35. package/dist/src/services/{solo → code}/post-compact-detector.js +4 -4
  36. package/dist/src/services/{solo → code}/step-08-gate.d.ts +4 -4
  37. package/dist/src/services/{solo → code}/step-08-gate.js +4 -4
  38. package/dist/src/services/{solo → code}/user-touchpoint-classifier.d.ts +1 -1
  39. package/dist/src/services/{solo → code}/user-touchpoint-classifier.js +1 -1
  40. package/dist/src/services/context/auto-compact-dispatcher.d.ts +2 -2
  41. package/dist/src/services/context/auto-compact-dispatcher.js +2 -2
  42. package/dist/src/services/context/auto-compact-types.d.ts +1 -1
  43. package/dist/src/services/dispatch/slice-dag.d.ts +2 -2
  44. package/dist/src/services/dispatch/test-tool-detection.d.ts +1 -1
  45. package/dist/src/services/dispatch/test-tool-detection.js +1 -1
  46. package/dist/src/services/feedback/feedback-promotion-service.js +2 -2
  47. package/dist/src/services/job/job-progress-store.d.ts +1 -1
  48. package/dist/src/services/job/job-progress-store.js +1 -1
  49. package/dist/src/services/preferences/preferences-service.d.ts +1 -1
  50. package/dist/src/services/preferences/preferences-types.d.ts +1 -1
  51. package/dist/src/services/rd/standards-diagnostic.d.ts +1 -1
  52. package/dist/src/services/rd/standards-diagnostic.js +2 -2
  53. package/dist/src/services/recommendations/capability-seed-items.js +1 -1
  54. package/dist/src/services/recommendations/capability-seed-mappings.js +1 -1
  55. package/dist/src/services/recommendations/recommendation-service.js +1 -1
  56. package/dist/src/services/refactor/refactor-service.d.ts +1 -1
  57. package/dist/src/services/signal/cancel-handler.js +3 -3
  58. package/dist/src/services/skill/resume-detector.js +1 -1
  59. package/dist/src/services/skills/skill-presence-service.d.ts +1 -1
  60. package/dist/src/services/upgrade/1x-detector-service.js +1 -1
  61. package/dist/src/services/workflow/pipeline-verify-service.d.ts +1 -1
  62. package/dist/src/services/workflow/pipeline-verify-service.js +3 -3
  63. package/dist/src/services/workflow/workflow-autonomous-service.d.ts +3 -3
  64. package/dist/src/services/workflow/workflow-autonomous-service.js +2 -2
  65. package/dist/src/services/workflow/workflow-router-service.d.ts +6 -6
  66. package/dist/src/services/workflow/workflow-router-service.js +33 -33
  67. package/dist/src/services/workspace/claude-settings-template.d.ts +1 -1
  68. package/dist/src/services/workspace/claude-settings-template.js +5 -5
  69. package/dist/src/shared/version.d.ts +1 -1
  70. package/dist/src/shared/version.js +1 -1
  71. package/output-styles/peaks-skill-swarm.md +3 -3
  72. package/package.json +1 -1
  73. package/skills/bee/peaks-qa/SKILL.md +1 -1
  74. package/skills/bee/peaks-qa/references/artifact-per-request.md +3 -3
  75. package/skills/bee/peaks-qa/references/qa-sub-agent-dispatch.md +6 -6
  76. package/skills/bee/peaks-rd/references/rd-fanout-contracts.md +1 -1
  77. package/skills/bee/peaks-rd/references/rd-runbook.md +1 -1
  78. package/skills/bee/peaks-rd/references/rd-sub-agent-dispatch.md +11 -11
  79. package/skills/bee/peaks-rd/references/rd-transition-gates.md +1 -1
  80. package/skills/bee/peaks-sc/SKILL.md +1 -1
  81. package/skills/bee/peaks-txt/SKILL.md +3 -3
  82. package/skills/bee/peaks-ui/SKILL.md +8 -8
  83. package/skills/peaks-code/SKILL.md +21 -21
  84. package/skills/peaks-code/references/anchoring-and-session-info.md +2 -2
  85. package/skills/peaks-code/references/artifact-contracts.md +1 -1
  86. package/skills/peaks-code/references/boundaries.md +3 -3
  87. package/skills/peaks-code/references/codegraph-orchestration.md +1 -1
  88. package/skills/peaks-code/references/dag-orchestrator.md +7 -7
  89. package/skills/peaks-code/references/external-references.md +2 -2
  90. package/skills/peaks-code/references/fanout-mandatory.md +3 -3
  91. package/skills/peaks-code/references/fast-mode.md +2 -2
  92. package/skills/peaks-code/references/frontend-only-mode.md +9 -9
  93. package/skills/peaks-code/references/job-loop.md +1 -1
  94. package/skills/peaks-code/references/local-artifact-workspace.md +2 -2
  95. package/skills/peaks-code/references/micro-cycle.md +14 -14
  96. package/skills/peaks-code/references/mode-selection-with-stale-presence.md +5 -5
  97. package/skills/peaks-code/references/mode-selection.md +3 -3
  98. package/skills/peaks-code/references/openspec-workflow.md +16 -16
  99. package/skills/peaks-code/references/periodic-checkpoint.md +4 -4
  100. package/skills/peaks-code/references/quality-gate-cheatsheet.md +1 -1
  101. package/skills/peaks-code/references/refactor-mode.md +1 -1
  102. package/skills/peaks-code/references/runbook.md +15 -15
  103. package/skills/peaks-code/references/step-0-55-1x-detection.md +1 -1
  104. package/skills/peaks-code/references/step-0-8-gate.md +11 -11
  105. package/skills/peaks-code/references/sub-agent-dispatch.md +8 -8
  106. package/skills/peaks-code/references/swarm-dispatch-contract.md +17 -17
  107. package/skills/peaks-code/references/workflow-gates-and-types.md +2 -2
  108. package/skills/peaks-code/references/workflow.md +5 -5
  109. package/skills/peaks-resume/SKILL.md +2 -2
  110. package/skills/peaks-status/SKILL.md +2 -2
  111. package/skills/peaks-test/SKILL.md +2 -2
  112. /package/dist/src/services/{solo → code}/batch-heartbeat-poller.d.ts +0 -0
  113. /package/dist/src/services/{solo → code}/batch-heartbeat-poller.js +0 -0
  114. /package/dist/src/services/{solo → code}/status-line-renderer.d.ts +0 -0
  115. /package/dist/src/services/{solo → code}/status-line-renderer.js +0 -0
package/README-en.md CHANGED
@@ -83,7 +83,7 @@ One sentence after install, and the squad is on the job.
83
83
 
84
84
  This repo used to be [`peaks-cli`](https://github.com/SquabbyZ/peaks-cli). It's called `peaks-loop` now.
85
85
 
86
- Inside the box, the skills evolved from `peaks-solo` (single-role) to `peaks-code` (gate-bearing, code-domain) — both still here, both with their own lane: `peaks-solo` is the older single-task flow entry, and `peaks-code` runs the end-to-end main path.
86
+ Inside the box, the skills evolved from `peaks-code` (single-role) to `peaks-code` (gate-bearing, code-domain) — both still here, both with their own lane: `peaks-code` is the older single-task flow entry, and `peaks-code` runs the end-to-end main path.
87
87
 
88
88
  The job didn't change: you say it, the engineering gates get laid out — fail where it fails, you decide.
89
89
 
package/README.md CHANGED
@@ -73,7 +73,7 @@ npm i -g peaks-loop
73
73
 
74
74
  这个仓库以前叫 [`peaks-cli`](https://github.com/SquabbyZ/peaks-cli),现在叫 `peaks-loop`。
75
75
 
76
- 里面的技能从 `peaks-solo`(单角色)演化到 `peaks-code`(带门禁的代码域).两边都还在,只是分工不同:`peaks-solo` 如今是给单任务跑流程用的那个老入口,`peaks-code` 接管了端到端的主路径。
76
+ 里面的技能从 `peaks-code`(单角色)演化到 `peaks-code`(带门禁的代码域).两边都还在,只是分工不同:`peaks-code` 如今是给单任务跑流程用的那个老入口,`peaks-code` 接管了端到端的主路径。
77
77
 
78
78
  事情一直没换过:你说话,它替你排工程门禁,坏哪道停哪道,你来拍板。
79
79
 
@@ -1,35 +1,35 @@
1
1
  /**
2
- * Slice 2 — `peaks solo [--fast] <change-id>` (peaks-code fast mode).
2
+ * Slice 2 — `peaks code [--fast] <change-id>` (peaks-code fast mode).
3
3
  *
4
4
  * The `peaks-code` SKILL orchestrates an LLM-side workflow:
5
5
  * load-memory -> standards-preflight -> rd-cycle -> qa-cycle -> emit-txt
6
6
  *
7
- * The CLI surface here is intentionally narrow: it builds a SoloPlan and
8
- * runs it via `runSoloFast`. Fast mode skips memory full-load, standards
7
+ * The CLI surface here is intentionally narrow: it builds a CodePlan and
8
+ * runs it via `runCodeFast`. Fast mode skips memory full-load, standards
9
9
  * preflight, and the QA repair loop. Round-trip KPI: ≤ 30s.
10
10
  *
11
- * No new service layer — the orchestrator is the in-file `runSoloFast`
11
+ * No new service layer — the orchestrator is the in-file `runCodeFast`
12
12
  * function. Hooks are injected so tests can mock at the boundary.
13
13
  *
14
14
  * v2.11.0 Group F (Tier 9) — D5 / D7 additions:
15
- * - `peaks solo should-pause --step <step> --mode <mode>` (D5)
16
- * - `peaks solo post-compact-detect --project <path>` (D7)
15
+ * - `peaks code should-pause --step <step> --mode <mode>` (D5)
16
+ * - `peaks code post-compact-detect --project <path>` (D7)
17
17
  */
18
18
  import type { Command } from 'commander';
19
19
  import { type ProgramIO } from '../cli-helpers.js';
20
- export type SoloStepKind = 'memory' | 'preflight' | 'rd' | 'qa' | 'emit';
21
- export interface SoloStep {
20
+ export type CodeStepKind = 'memory' | 'preflight' | 'rd' | 'qa' | 'emit';
21
+ export interface CodeStep {
22
22
  readonly id: 'load-memory' | 'standards-preflight' | 'rd-cycle' | 'qa-cycle' | 'emit-txt';
23
- readonly kind: SoloStepKind;
23
+ readonly kind: CodeStepKind;
24
24
  readonly skipped: boolean;
25
25
  /** QA-only: when true, qa hook retries on failure up to N rounds. */
26
26
  readonly repairLoop?: boolean;
27
27
  }
28
- export interface SoloPlan {
28
+ export interface CodePlan {
29
29
  readonly sessionId: string;
30
- readonly steps: readonly SoloStep[];
30
+ readonly steps: readonly CodeStep[];
31
31
  }
32
- export interface SoloHooks {
32
+ export interface CodeHooks {
33
33
  readonly memory: (ctx: {
34
34
  sessionId: string;
35
35
  }) => Promise<unknown>;
@@ -47,34 +47,34 @@ export interface SoloHooks {
47
47
  sessionId: string;
48
48
  }) => Promise<unknown>;
49
49
  }
50
- export interface SoloRunResult {
50
+ export interface CodeRunResult {
51
51
  readonly sessionId: string;
52
52
  readonly ok: boolean;
53
- readonly steps: readonly SoloStep[];
53
+ readonly steps: readonly CodeStep[];
54
54
  readonly skipped: readonly string[];
55
55
  readonly elapsedMs: number;
56
56
  }
57
57
  /**
58
- * Build a SoloPlan. `fast=true` marks memory + preflight as skipped and
58
+ * Build a CodePlan. `fast=true` marks memory + preflight as skipped and
59
59
  * disables the QA repair loop. Step order is fixed; emit-txt always last.
60
60
  */
61
- export declare function buildSoloPlan(opts: {
61
+ export declare function buildCodePlan(opts: {
62
62
  sessionId: string;
63
63
  fast: boolean;
64
- }): SoloPlan;
64
+ }): CodePlan;
65
65
  /**
66
- * Run the SoloPlan. Skipped steps do NOT invoke their hook; emit-txt is
66
+ * Run the CodePlan. Skipped steps do NOT invoke their hook; emit-txt is
67
67
  * always invoked last. Returns timing + skipped ids for KPI measurement.
68
68
  */
69
- export declare function runSoloFast(opts: {
69
+ export declare function runCodeFast(opts: {
70
70
  sessionId: string;
71
- plan: SoloPlan;
72
- hooks: SoloHooks;
73
- }): Promise<SoloRunResult>;
71
+ plan: CodePlan;
72
+ hooks: CodeHooks;
73
+ }): Promise<CodeRunResult>;
74
74
  /**
75
- * Register the `peaks solo [--fast] <change-id>` command. The actual
75
+ * Register the `peaks code [--fast] <change-id>` command. The actual
76
76
  * LLM-side orchestration is driven by the SKILL.md (LLM-side), so this
77
77
  * CLI command is a thin surface that builds the plan and emits a JSON
78
78
  * envelope for downstream tooling.
79
79
  */
80
- export declare function registerSoloCommands(program: Command, io: ProgramIO): void;
80
+ export declare function registerCodeCommands(program: Command, io: ProgramIO): void;
@@ -1,19 +1,19 @@
1
1
  /**
2
- * Slice 2 — `peaks solo [--fast] <change-id>` (peaks-code fast mode).
2
+ * Slice 2 — `peaks code [--fast] <change-id>` (peaks-code fast mode).
3
3
  *
4
4
  * The `peaks-code` SKILL orchestrates an LLM-side workflow:
5
5
  * load-memory -> standards-preflight -> rd-cycle -> qa-cycle -> emit-txt
6
6
  *
7
- * The CLI surface here is intentionally narrow: it builds a SoloPlan and
8
- * runs it via `runSoloFast`. Fast mode skips memory full-load, standards
7
+ * The CLI surface here is intentionally narrow: it builds a CodePlan and
8
+ * runs it via `runCodeFast`. Fast mode skips memory full-load, standards
9
9
  * preflight, and the QA repair loop. Round-trip KPI: ≤ 30s.
10
10
  *
11
- * No new service layer — the orchestrator is the in-file `runSoloFast`
11
+ * No new service layer — the orchestrator is the in-file `runCodeFast`
12
12
  * function. Hooks are injected so tests can mock at the boundary.
13
13
  *
14
14
  * v2.11.0 Group F (Tier 9) — D5 / D7 additions:
15
- * - `peaks solo should-pause --step <step> --mode <mode>` (D5)
16
- * - `peaks solo post-compact-detect --project <path>` (D7)
15
+ * - `peaks code should-pause --step <step> --mode <mode>` (D5)
16
+ * - `peaks code post-compact-detect --project <path>` (D7)
17
17
  */
18
18
  import { existsSync, readFileSync } from 'node:fs';
19
19
  import { join } from 'node:path';
@@ -21,13 +21,13 @@ import { addJsonOption, getErrorMessage, printResult } from '../cli-helpers.js';
21
21
  import { fail, ok } from '../../shared/result.js';
22
22
  import { emitObservabilityEvent } from '../../services/observability/observability-service.js';
23
23
  import { findProjectRoot } from '../../services/config/config-safety.js';
24
- import { GATED_STEPS, isHardFloorCategory, isSoloMode, isCommitBoundaryAction, shouldPauseAtGate, formatAutoProceedLogLine } from '../../services/solo/mode-gate.js';
24
+ import { GATED_STEPS, isHardFloorCategory, isCodeMode, isCommitBoundaryAction, shouldPauseAtGate, formatAutoProceedLogLine } from '../../services/code/mode-gate.js';
25
25
  import { getSkillPresence, checkStalePresence } from '../../services/skills/skill-presence-service.js';
26
- import { detectPostCompactResume, formatPostCompactResumeLogLine } from '../../services/solo/post-compact-detector.js';
27
- import { runAutoCompact } from '../../services/solo/auto-compact-orchestrator.js';
28
- import { readJobShapeDecision, writeJobShapeDecision, JobShapeDecisionError, JOB_SHAPE_NOT_DECIDED, JOB_SHAPE_ALREADY_DECIDED } from '../../services/solo/job-shape-decision.js';
29
- import { evaluateStep08, STEP_08_BACKUP_REGEX } from '../../services/solo/step-08-gate.js';
30
- import { evaluateEmitHandoff, JOB_NOT_INITIALIZED, JOB_REMAINING_BLOCKED } from '../../services/solo/emit-handoff.js';
26
+ import { detectPostCompactResume, formatPostCompactResumeLogLine } from '../../services/code/post-compact-detector.js';
27
+ import { runAutoCompact } from '../../services/code/auto-compact-orchestrator.js';
28
+ import { readJobShapeDecision, writeJobShapeDecision, JobShapeDecisionError, JOB_SHAPE_NOT_DECIDED, JOB_SHAPE_ALREADY_DECIDED } from '../../services/code/job-shape-decision.js';
29
+ import { evaluateStep08, STEP_08_BACKUP_REGEX } from '../../services/code/step-08-gate.js';
30
+ import { evaluateEmitHandoff, JOB_NOT_INITIALIZED, JOB_REMAINING_BLOCKED } from '../../services/code/emit-handoff.js';
31
31
  const STEP_ORDER = [
32
32
  'load-memory',
33
33
  'standards-preflight',
@@ -43,10 +43,10 @@ const STEP_KIND = {
43
43
  'emit-txt': 'emit'
44
44
  };
45
45
  /**
46
- * Build a SoloPlan. `fast=true` marks memory + preflight as skipped and
46
+ * Build a CodePlan. `fast=true` marks memory + preflight as skipped and
47
47
  * disables the QA repair loop. Step order is fixed; emit-txt always last.
48
48
  */
49
- export function buildSoloPlan(opts) {
49
+ export function buildCodePlan(opts) {
50
50
  const steps = STEP_ORDER.map((id) => {
51
51
  const isSkippable = id === 'load-memory' || id === 'standards-preflight';
52
52
  const step = {
@@ -62,10 +62,10 @@ export function buildSoloPlan(opts) {
62
62
  return { sessionId: opts.sessionId, steps };
63
63
  }
64
64
  /**
65
- * Run the SoloPlan. Skipped steps do NOT invoke their hook; emit-txt is
65
+ * Run the CodePlan. Skipped steps do NOT invoke their hook; emit-txt is
66
66
  * always invoked last. Returns timing + skipped ids for KPI measurement.
67
67
  */
68
- export async function runSoloFast(opts) {
68
+ export async function runCodeFast(opts) {
69
69
  const start = Date.now();
70
70
  const skipped = [];
71
71
  const { sessionId, plan, hooks } = opts;
@@ -101,23 +101,23 @@ export async function runSoloFast(opts) {
101
101
  };
102
102
  }
103
103
  /**
104
- * Register the `peaks solo [--fast] <change-id>` command. The actual
104
+ * Register the `peaks code [--fast] <change-id>` command. The actual
105
105
  * LLM-side orchestration is driven by the SKILL.md (LLM-side), so this
106
106
  * CLI command is a thin surface that builds the plan and emits a JSON
107
107
  * envelope for downstream tooling.
108
108
  */
109
- export function registerSoloCommands(program, io) {
110
- const solo = program
111
- .command('solo')
109
+ export function registerCodeCommands(program, io) {
110
+ const code = program
111
+ .command('code')
112
112
  .description('peaks-code LLM-side workflow planner (slice 2 fast mode)');
113
- solo
113
+ code
114
114
  .command('plan')
115
- .description('Build and print a SoloPlan without executing it')
115
+ .description('Build and print a CodePlan without executing it')
116
116
  .argument('<change-id>', 'change id to plan against')
117
117
  .option('--fast', 'fast mode: skip memory full-load, standards preflight, and QA repair loop', false)
118
118
  .option('--json', 'emit JSON envelope')
119
119
  .action((sessionId, opts) => {
120
- const plan = buildSoloPlan({ sessionId, fast: opts.fast === true });
120
+ const plan = buildCodePlan({ sessionId, fast: opts.fast === true });
121
121
  if (opts.json === true) {
122
122
  process.stdout.write(JSON.stringify({ ok: true, data: plan }) + '\n');
123
123
  }
@@ -130,7 +130,7 @@ export function registerSoloCommands(program, io) {
130
130
  }
131
131
  }
132
132
  });
133
- addJsonOption(solo
133
+ addJsonOption(code
134
134
  .command('should-pause')
135
135
  .description('v2.11.0 D5: ask the mode-gate whether the LLM should pause for an AskUserQuestion at a given step. ' +
136
136
  'full-auto / swarm auto-proceed (recommended = chosen); assisted / strict pause. ' +
@@ -161,19 +161,19 @@ export function registerSoloCommands(program, io) {
161
161
  // to know the mode up front. The gate's hard-pause on
162
162
  // step-1-mode-select will still pause regardless.
163
163
  const mode = opts.mode ?? 'full-auto';
164
- if (!isSoloMode(mode)) {
165
- printResult(io, fail('solo.should-pause', 'INVALID_MODE', `mode must be one of full-auto, assisted, swarm, strict (got "${mode}")`, { provided: mode }, ['Pass --mode full-auto | assisted | swarm | strict']), opts.json);
164
+ if (!isCodeMode(mode)) {
165
+ printResult(io, fail('code.should-pause', 'INVALID_MODE', `mode must be one of full-auto, assisted, swarm, strict (got "${mode}")`, { provided: mode }, ['Pass --mode full-auto | assisted | swarm | strict']), opts.json);
166
166
  process.exitCode = 1;
167
167
  return;
168
168
  }
169
169
  if (!GATED_STEPS.includes(opts.step)) {
170
- printResult(io, fail('solo.should-pause', 'INVALID_STEP', `step must be one of the 14 GATED_STEPS (got "${opts.step}")`, { provided: opts.step, allowed: [...GATED_STEPS] }, ['Pass --step <one of the 14 GATED_STEPS>']), opts.json);
170
+ printResult(io, fail('code.should-pause', 'INVALID_STEP', `step must be one of the 14 GATED_STEPS (got "${opts.step}")`, { provided: opts.step, allowed: [...GATED_STEPS] }, ['Pass --step <one of the 14 GATED_STEPS>']), opts.json);
171
171
  process.exitCode = 1;
172
172
  return;
173
173
  }
174
174
  const hardFloor = opts.hardFloor;
175
175
  if (hardFloor !== undefined && !isHardFloorCategory(hardFloor)) {
176
- printResult(io, fail('solo.should-pause', 'INVALID_HARD_FLOOR', `hard-floor must be one of: irreversible-external-side-effect | authentication-credential | multi-day-investment | commit-boundary-side-effect (got "${hardFloor}")`, { provided: hardFloor }, ['Omit --hard-floor or pass a valid category']), opts.json);
176
+ printResult(io, fail('code.should-pause', 'INVALID_HARD_FLOOR', `hard-floor must be one of: irreversible-external-side-effect | authentication-credential | multi-day-investment | commit-boundary-side-effect (got "${hardFloor}")`, { provided: hardFloor }, ['Omit --hard-floor or pass a valid category']), opts.json);
177
177
  process.exitCode = 1;
178
178
  return;
179
179
  }
@@ -185,7 +185,7 @@ export function registerSoloCommands(program, io) {
185
185
  // (not silently ignored) so typos fail loud.
186
186
  const commitBoundaryActionId = opts.commitBoundaryAction;
187
187
  if (commitBoundaryActionId !== undefined && !isCommitBoundaryAction(commitBoundaryActionId)) {
188
- printResult(io, fail('solo.should-pause', 'INVALID_COMMIT_BOUNDARY_ACTION', `--commit-boundary-action must be one of: git-push | git-tag | npm-publish | npm-install-global | peaks-global-install (got "${commitBoundaryActionId}")`, { provided: commitBoundaryActionId }, ['Omit --commit-boundary-action or pass a valid action id']), opts.json);
188
+ printResult(io, fail('code.should-pause', 'INVALID_COMMIT_BOUNDARY_ACTION', `--commit-boundary-action must be one of: git-push | git-tag | npm-publish | npm-install-global | peaks-global-install (got "${commitBoundaryActionId}")`, { provided: commitBoundaryActionId }, ['Omit --commit-boundary-action or pass a valid action id']), opts.json);
189
189
  process.exitCode = 1;
190
190
  return;
191
191
  }
@@ -194,7 +194,7 @@ export function registerSoloCommands(program, io) {
194
194
  // Step 1 AND the recorded presence is stale, OVERRIDE the
195
195
  // gate decision to PAUSE with reason='stale-presence'. The
196
196
  // hard-pause on step-1-mode-select (defect #1 fix from
197
- // 2026-06-28-solo-mode-bypass-fix) is already in effect, so
197
+ // 2026-06-28-code-mode-bypass-fix) is already in effect, so
198
198
  // this only adds the structured `stale` reason + an
199
199
  // envelope-level `stalePresence` field so downstream tooling
200
200
  // (statusline, sub-agent dispatch) can act on it.
@@ -225,7 +225,7 @@ export function registerSoloCommands(program, io) {
225
225
  }
226
226
  }, { projectRoot: opts.project ?? process.cwd() });
227
227
  }
228
- printResult(io, ok('solo.should-pause', {
228
+ printResult(io, ok('code.should-pause', {
229
229
  shouldPause: true,
230
230
  reason: `stale-presence — re-ask Step 1 (${stalePresence.reason}; recorded outer session id does not match current)`,
231
231
  gateKind: 'mode-selection-itself',
@@ -287,7 +287,7 @@ export function registerSoloCommands(program, io) {
287
287
  // action id in the envelope (when provided) so the LLM-side
288
288
  // caller can echo which boundary was checked. Null when no
289
289
  // --commit-boundary-action flag was passed.
290
- ok('solo.should-pause', {
290
+ ok('code.should-pause', {
291
291
  ...decision,
292
292
  logLine,
293
293
  ...(commitBoundaryActionId !== undefined ? { commitBoundaryAction: commitBoundaryActionId } : {})
@@ -298,11 +298,11 @@ export function registerSoloCommands(program, io) {
298
298
  ]), opts.json);
299
299
  }
300
300
  catch (err) {
301
- printResult(io, fail('solo.should-pause', 'SHOULD_PAUSE_FAILED', getErrorMessage(err), null, ['Re-run with --json for envelope shape']), opts.json);
301
+ printResult(io, fail('code.should-pause', 'SHOULD_PAUSE_FAILED', getErrorMessage(err), null, ['Re-run with --json for envelope shape']), opts.json);
302
302
  process.exitCode = 1;
303
303
  }
304
304
  });
305
- addJsonOption(solo
305
+ addJsonOption(code
306
306
  .command('post-compact-detect')
307
307
  .description('v2.11.0 D7: detect whether the current invocation is a same-day post-compact resume. ' +
308
308
  'Auto-resumes (no AskUserQuestion) when the most-recent checkpoint is from today, has a mode field, ' +
@@ -313,7 +313,7 @@ export function registerSoloCommands(program, io) {
313
313
  try {
314
314
  const sessionId = opts.sessionId ?? readActiveSid(opts.project);
315
315
  if (sessionId === null) {
316
- printResult(io, fail('solo.post-compact-detect', 'NO_ACTIVE_SESSION', 'no active session id; pass --session-id or set presence via `peaks skill presence:set peaks-code`', null, ['Re-run with --session-id <sid>']), opts.json);
316
+ printResult(io, fail('code.post-compact-detect', 'NO_ACTIVE_SESSION', 'no active session id; pass --session-id or set presence via `peaks skill presence:set peaks-code`', null, ['Re-run with --session-id <sid>']), opts.json);
317
317
  process.exitCode = 1;
318
318
  return;
319
319
  }
@@ -323,18 +323,18 @@ export function registerSoloCommands(program, io) {
323
323
  activeSkill: opts.activeSkill
324
324
  });
325
325
  const logLine = formatPostCompactResumeLogLine(probe);
326
- printResult(io, ok('solo.post-compact-detect', { ...probe, logLine }, [...probe.warnings], [
326
+ printResult(io, ok('code.post-compact-detect', { ...probe, logLine }, [...probe.warnings], [
327
327
  probe.shouldAutoResume
328
328
  ? `Post-compact match → auto-resume mode=${probe.mode ?? '?'} checkpoint=${probe.checkpointPath ?? '?'}`
329
329
  : `No auto-resume: ${probe.reason}`
330
330
  ]), opts.json);
331
331
  }
332
332
  catch (err) {
333
- printResult(io, fail('solo.post-compact-detect', 'POST_COMPACT_DETECT_FAILED', getErrorMessage(err), null, ['Verify the project path and try again']), opts.json);
333
+ printResult(io, fail('code.post-compact-detect', 'POST_COMPACT_DETECT_FAILED', getErrorMessage(err), null, ['Verify the project path and try again']), opts.json);
334
334
  process.exitCode = 1;
335
335
  }
336
336
  });
337
- addJsonOption(solo
337
+ addJsonOption(code
338
338
  .command('auto-compact')
339
339
  .description('v2.13.0 AC-4: zero-human-intervention auto-compact. Probes current ' +
340
340
  'context-fill % via the active IDE adapter; ≥ 0.85 writes a pre-compact ' +
@@ -366,20 +366,20 @@ export function registerSoloCommands(program, io) {
366
366
  const data = 'data' in result ? result.data : null;
367
367
  const nextActions = 'nextActions' in result ? result.nextActions : [];
368
368
  const envelope = result.ok
369
- ? ok(`solo.auto-compact`, data ?? {}, [], [result.message, ...nextActions])
370
- : fail(`solo.auto-compact`, code, result.message, data, [...nextActions]);
369
+ ? ok(`code.auto-compact`, data ?? {}, [], [result.message, ...nextActions])
370
+ : fail(`code.auto-compact`, code, result.message, data, [...nextActions]);
371
371
  printResult(io, envelope, opts.json);
372
372
  if (!exitOk)
373
373
  process.exitCode = 1;
374
374
  }
375
375
  catch (err) {
376
- printResult(io, fail('solo.auto-compact', 'AUTO_COMPACT_FAILED', getErrorMessage(err), null, [
376
+ printResult(io, fail('code.auto-compact', 'AUTO_COMPACT_FAILED', getErrorMessage(err), null, [
377
377
  'Verify the project path + session id and try again'
378
378
  ]), opts.json);
379
379
  process.exitCode = 1;
380
380
  }
381
381
  });
382
- addJsonOption(solo
382
+ addJsonOption(code
383
383
  .command('context-now')
384
384
  .description('v2.13.0 AC-1: read the active IDE adapter\'s context-fill % ' +
385
385
  'without requiring the LLM to pass --prompt-size <bytes> manually. ' +
@@ -444,7 +444,7 @@ export function registerSoloCommands(program, io) {
444
444
  const jobModeNotice = isJobMode
445
445
  ? 'Job mode enforced: ≥0.85 is MANDATORY auto-compact (v3.1.2).'
446
446
  : 'Advisory mode (single-rid): ≥0.85 is recommended, not mandatory.';
447
- printResult(io, ok('solo.context-now', {
447
+ printResult(io, ok('code.context-now', {
448
448
  ratio: probe.ratio,
449
449
  ratioPct: `${ratioPct}%`,
450
450
  verdict,
@@ -460,7 +460,7 @@ export function registerSoloCommands(program, io) {
460
460
  action === 'red-line'
461
461
  ? `RED LINE: ≥ 95%. Next: \`${next}\` (PreToolUse hook fires next turn).`
462
462
  : action === 'auto-compact-now'
463
- ? `Job-mode MANDATORY auto-compact. Solo MUST call \`${next}\` WITHOUT confirmation.`
463
+ ? `Job-mode MANDATORY auto-compact. Code MUST call \`${next}\` WITHOUT confirmation.`
464
464
  : action === 'soft-warn'
465
465
  ? isJobMode
466
466
  ? `Job mode soft-warn (50–85%). Continue working; the next \`peaks context now\` will re-check.`
@@ -470,7 +470,7 @@ export function registerSoloCommands(program, io) {
470
470
  ]), true);
471
471
  }
472
472
  catch (err) {
473
- printResult(io, fail('solo.context-now', 'CONTEXT_NOW_FAILED', getErrorMessage(err), null, [
473
+ printResult(io, fail('code.context-now', 'CONTEXT_NOW_FAILED', getErrorMessage(err), null, [
474
474
  'Verify the project path and try again'
475
475
  ]), opts.json);
476
476
  process.exitCode = 1;
@@ -481,11 +481,11 @@ export function registerSoloCommands(program, io) {
481
481
  // (--is-job, --rationale, --suggested-job-id, --suggested-strategy,
482
482
  // --confidence); the CLI validates the shape, stamps `decidedAt`
483
483
  // server-side, and writes to `.peaks/_runtime/<sessionId>/job-shape.json`.
484
- addJsonOption(solo
484
+ addJsonOption(code
485
485
  .command('detect-job')
486
486
  .description('v3.1.1 Step 0.8: record the LLM\'s Job-shape judgement. CLI is a recorder, NOT a detector. ' +
487
487
  'Pass --is-job true|false, --rationale <text>, --suggested-job-id <slug>. ' +
488
- 'Downstream steps call `peaks solo read-job-shape` and refuse to proceed if missing.')
488
+ 'Downstream steps call `peaks code read-job-shape` and refuse to proceed if missing.')
489
489
  .requiredOption('--is-job <bool>', 'true | false (the LLM\'s semantic verdict)')
490
490
  .requiredOption('--rationale <text>', '1-3 sentences the LLM writes explaining the call')
491
491
  .requiredOption('--suggested-job-id <jid>', 'LLM-chosen stable id, slug-safe (/^[a-z0-9][a-z0-9-]{2,40}$/)')
@@ -497,30 +497,30 @@ export function registerSoloCommands(program, io) {
497
497
  .option('--prompt <text>', 'override the prompt hashed into promptHash (default: read last-prompt.txt if present, else empty)')).action(async (opts) => {
498
498
  try {
499
499
  if (opts.isJob !== 'true' && opts.isJob !== 'false') {
500
- printResult(io, fail('solo.detect-job', 'INVALID_FLAG', `--is-job must be true | false (got "${opts.isJob}")`, { provided: opts.isJob }, ['Pass --is-job true or --is-job false']), opts.json);
500
+ printResult(io, fail('code.detect-job', 'INVALID_FLAG', `--is-job must be true | false (got "${opts.isJob}")`, { provided: opts.isJob }, ['Pass --is-job true or --is-job false']), opts.json);
501
501
  process.exitCode = 1;
502
502
  return;
503
503
  }
504
504
  if (opts.suggestedStrategy !== 'single' && opts.suggestedStrategy !== 'rotating') {
505
- printResult(io, fail('solo.detect-job', 'INVALID_FLAG', `--suggested-strategy must be single | rotating (got "${opts.suggestedStrategy}")`, { provided: opts.suggestedStrategy }, ['Pass --suggested-strategy single or --suggested-strategy rotating']), opts.json);
505
+ printResult(io, fail('code.detect-job', 'INVALID_FLAG', `--suggested-strategy must be single | rotating (got "${opts.suggestedStrategy}")`, { provided: opts.suggestedStrategy }, ['Pass --suggested-strategy single or --suggested-strategy rotating']), opts.json);
506
506
  process.exitCode = 1;
507
507
  return;
508
508
  }
509
509
  if (opts.confidence !== 'high' && opts.confidence !== 'medium' && opts.confidence !== 'low') {
510
- printResult(io, fail('solo.detect-job', 'INVALID_FLAG', `--confidence must be high | medium | low (got "${opts.confidence}")`, { provided: opts.confidence }, ['Pass --confidence high | medium | low']), opts.json);
510
+ printResult(io, fail('code.detect-job', 'INVALID_FLAG', `--confidence must be high | medium | low (got "${opts.confidence}")`, { provided: opts.confidence }, ['Pass --confidence high | medium | low']), opts.json);
511
511
  process.exitCode = 1;
512
512
  return;
513
513
  }
514
514
  const jidRe = /^[a-z0-9][a-z0-9-]{2,40}$/;
515
515
  if (!jidRe.test(opts.suggestedJobId)) {
516
- printResult(io, fail('solo.detect-job', 'INVALID_FLAG', `--suggested-job-id must match /^[a-z0-9][a-z0-9-]{2,40}$/ (got "${opts.suggestedJobId}")`, { provided: opts.suggestedJobId }, ['Pass a slug-safe id (lowercase, digits, dashes; 3-41 chars)']), opts.json);
516
+ printResult(io, fail('code.detect-job', 'INVALID_FLAG', `--suggested-job-id must match /^[a-z0-9][a-z0-9-]{2,40}$/ (got "${opts.suggestedJobId}")`, { provided: opts.suggestedJobId }, ['Pass a slug-safe id (lowercase, digits, dashes; 3-41 chars)']), opts.json);
517
517
  process.exitCode = 1;
518
518
  return;
519
519
  }
520
520
  const projectRoot = opts.project ?? findProjectRoot(process.cwd()) ?? process.cwd();
521
521
  const sessionId = opts.sessionId ?? readActiveSid(projectRoot);
522
522
  if (sessionId === null) {
523
- printResult(io, fail('solo.detect-job', 'NO_ACTIVE_SESSION', 'no active session id; pass --session-id or set presence via `peaks skill presence:set peaks-code`', null, ['Re-run with --session-id <sid>']), opts.json);
523
+ printResult(io, fail('code.detect-job', 'NO_ACTIVE_SESSION', 'no active session id; pass --session-id or set presence via `peaks skill presence:set peaks-code`', null, ['Re-run with --session-id <sid>']), opts.json);
524
524
  process.exitCode = 1;
525
525
  return;
526
526
  }
@@ -545,7 +545,7 @@ export function registerSoloCommands(program, io) {
545
545
  confidence: opts.confidence,
546
546
  prompt: promptText
547
547
  }, { force: opts.force === true });
548
- printResult(io, ok('solo.detect-job', {
548
+ printResult(io, ok('code.detect-job', {
549
549
  sessionId: record.sessionId,
550
550
  promptHash: record.promptHash,
551
551
  decision: record.decision,
@@ -558,7 +558,7 @@ export function registerSoloCommands(program, io) {
558
558
  catch (err) {
559
559
  if (err instanceof JobShapeDecisionError) {
560
560
  const code = err.code === JOB_SHAPE_ALREADY_DECIDED ? JOB_SHAPE_ALREADY_DECIDED : err.code === JOB_SHAPE_NOT_DECIDED ? JOB_SHAPE_NOT_DECIDED : err.code;
561
- printResult(io, fail('solo.detect-job', code, err.message, err.details ?? null, [
561
+ printResult(io, fail('code.detect-job', code, err.message, err.details ?? null, [
562
562
  err.code === JOB_SHAPE_ALREADY_DECIDED
563
563
  ? 'Re-run with --force to overwrite the existing decision.'
564
564
  : 'Re-run with valid flags.'
@@ -566,13 +566,13 @@ export function registerSoloCommands(program, io) {
566
566
  process.exitCode = 1;
567
567
  return;
568
568
  }
569
- printResult(io, fail('solo.detect-job', 'DETECT_JOB_FAILED', getErrorMessage(err), null, ['Verify the flags and try again']), opts.json);
569
+ printResult(io, fail('code.detect-job', 'DETECT_JOB_FAILED', getErrorMessage(err), null, ['Verify the flags and try again']), opts.json);
570
570
  process.exitCode = 1;
571
571
  }
572
572
  });
573
573
  // v3.1.1 Step 0.8 — read-only validator. Downstream steps call this
574
574
  // to refuse to proceed if the LLM has not yet recorded a decision.
575
- addJsonOption(solo
575
+ addJsonOption(code
576
576
  .command('read-job-shape')
577
577
  .description('v3.1.1 Step 0.8: return the recorded Job-shape decision. ' +
578
578
  'Returns JOB_SHAPE_NOT_DECIDED when the LLM has not yet recorded a verdict.')
@@ -582,12 +582,12 @@ export function registerSoloCommands(program, io) {
582
582
  const projectRoot = opts.project ?? findProjectRoot(process.cwd()) ?? process.cwd();
583
583
  const sessionId = opts.sessionId ?? readActiveSid(projectRoot);
584
584
  if (sessionId === null) {
585
- printResult(io, fail('solo.read-job-shape', 'NO_ACTIVE_SESSION', 'no active session id; pass --session-id or set presence via `peaks skill presence:set peaks-code`', null, ['Re-run with --session-id <sid>']), opts.json);
585
+ printResult(io, fail('code.read-job-shape', 'NO_ACTIVE_SESSION', 'no active session id; pass --session-id or set presence via `peaks skill presence:set peaks-code`', null, ['Re-run with --session-id <sid>']), opts.json);
586
586
  process.exitCode = 1;
587
587
  return;
588
588
  }
589
589
  const record = readJobShapeDecision(projectRoot, sessionId);
590
- printResult(io, ok('solo.read-job-shape', {
590
+ printResult(io, ok('code.read-job-shape', {
591
591
  sessionId: record.sessionId,
592
592
  promptHash: record.promptHash,
593
593
  decision: record.decision,
@@ -598,15 +598,15 @@ export function registerSoloCommands(program, io) {
598
598
  }
599
599
  catch (err) {
600
600
  if (err instanceof JobShapeDecisionError) {
601
- printResult(io, fail('solo.read-job-shape', err.code, err.message, err.details ?? null, [
601
+ printResult(io, fail('code.read-job-shape', err.code, err.message, err.details ?? null, [
602
602
  err.code === JOB_SHAPE_NOT_DECIDED
603
- ? 'Run `peaks solo detect-job` to record a decision.'
603
+ ? 'Run `peaks code detect-job` to record a decision.'
604
604
  : 'Investigate the decision file integrity.'
605
605
  ]), opts.json);
606
606
  process.exitCode = 1;
607
607
  return;
608
608
  }
609
- printResult(io, fail('solo.read-job-shape', 'READ_JOB_SHAPE_FAILED', getErrorMessage(err), null, ['Verify the project path and try again']), opts.json);
609
+ printResult(io, fail('code.read-job-shape', 'READ_JOB_SHAPE_FAILED', getErrorMessage(err), null, ['Verify the project path and try again']), opts.json);
610
610
  process.exitCode = 1;
611
611
  }
612
612
  });
@@ -615,7 +615,7 @@ export function registerSoloCommands(program, io) {
615
615
  // installer). Exit code is the load-bearing contract:
616
616
  // exit 0 → allow (with structured stdout describing the decision)
617
617
  // exit 2 → block (stderr contains the BLOCKED: ... reason)
618
- addJsonOption(solo
618
+ addJsonOption(code
619
619
  .command('gate-step-08')
620
620
  .description('v3.1.2: PreToolUse gate for Step 0.8 — allow when job-shape.json exists; ' +
621
621
  'fail-closed backup regex when missing. Exit 0 = allow, exit 2 = block. ' +
@@ -630,7 +630,7 @@ export function registerSoloCommands(program, io) {
630
630
  if (sessionId === null) {
631
631
  // No session binding — treat as allow (single-rid mode). The
632
632
  // LLM has not yet anchored; we have nothing to gate against.
633
- const envelope = ok('solo.gate-step-08', {
633
+ const envelope = ok('code.gate-step-08', {
634
634
  allow: true,
635
635
  mode: 'no-session',
636
636
  decision: null,
@@ -650,7 +650,7 @@ export function registerSoloCommands(program, io) {
650
650
  const result = evaluateStep08(evalInput);
651
651
  const verdict = result.verdict;
652
652
  if (verdict.kind === 'allow-job') {
653
- const envelope = ok('solo.gate-step-08', {
653
+ const envelope = ok('code.gate-step-08', {
654
654
  allow: true,
655
655
  mode: 'job',
656
656
  decision: verdict.decision,
@@ -661,7 +661,7 @@ export function registerSoloCommands(program, io) {
661
661
  return;
662
662
  }
663
663
  if (verdict.kind === 'allow-single') {
664
- const envelope = ok('solo.gate-step-08', {
664
+ const envelope = ok('code.gate-step-08', {
665
665
  allow: true,
666
666
  mode: 'single',
667
667
  decision: null,
@@ -676,12 +676,12 @@ export function registerSoloCommands(program, io) {
676
676
  if (verdict.promptHit) {
677
677
  // Block: backup regex hit. Exit code 2 is the load-bearing
678
678
  // signal for the PreToolUse hook.
679
- const blockMessage = 'BLOCKED: prompt looks Job-shaped but peaks solo detect-job has not been called. Run `peaks solo detect-job --is-job true ...` to record your Job-shape verdict, then retry.';
680
- const envelope = fail('solo.gate-step-08', 'STEP_08_BLOCKED', blockMessage, {
679
+ const blockMessage = 'BLOCKED: prompt looks Job-shaped but peaks code detect-job has not been called. Run `peaks code detect-job --is-job true ...` to record your Job-shape verdict, then retry.';
680
+ const envelope = fail('code.gate-step-08', 'STEP_08_BLOCKED', blockMessage, {
681
681
  promptSource: verdict.promptSource,
682
682
  backupRegex: STEP_08_BACKUP_REGEX.toString()
683
683
  }, [
684
- 'Run `peaks solo detect-job --is-job true --rationale <text> --suggested-job-id <slug>` to record the Job-shape verdict.',
684
+ 'Run `peaks code detect-job --is-job true --rationale <text> --suggested-job-id <slug>` to record the Job-shape verdict.',
685
685
  'Then re-run the Bash tool call.'
686
686
  ]);
687
687
  io.stderr(`${blockMessage}\n`);
@@ -690,7 +690,7 @@ export function registerSoloCommands(program, io) {
690
690
  return;
691
691
  }
692
692
  // No decision + no regex hit → allow.
693
- const envelope = ok('solo.gate-step-08', {
693
+ const envelope = ok('code.gate-step-08', {
694
694
  allow: true,
695
695
  mode: 'undecided-no-regex-hit',
696
696
  decision: null,
@@ -703,7 +703,7 @@ export function registerSoloCommands(program, io) {
703
703
  return;
704
704
  }
705
705
  catch (err) {
706
- printResult(io, fail('solo.gate-step-08', 'GATE_STEP_08_FAILED', getErrorMessage(err), null, [
706
+ printResult(io, fail('code.gate-step-08', 'GATE_STEP_08_FAILED', getErrorMessage(err), null, [
707
707
  'Verify the project path and try again'
708
708
  ]), opts.json);
709
709
  process.exitCode = 1;
@@ -711,7 +711,7 @@ export function registerSoloCommands(program, io) {
711
711
  });
712
712
  // v3.1.2 Step 11 / final handoff — Size-fear ban.
713
713
  // Refuses to emit a final handoff while a Job has remaining slices.
714
- addJsonOption(solo
714
+ addJsonOption(code
715
715
  .command('emit-handoff')
716
716
  .description('v3.1.2 Step 11 size-fear ban: under Job mode, refuse to emit a final ' +
717
717
  'handoff while remaining > 0. Exit 0 = allow, exit 1 = block. Pass ' +
@@ -723,7 +723,7 @@ export function registerSoloCommands(program, io) {
723
723
  try {
724
724
  const sessionId = opts.sessionId ?? readActiveSid(opts.project);
725
725
  if (sessionId === null) {
726
- const envelope = ok('solo.emit-handoff', { allow: true, mode: 'no-session' }, [], [
726
+ const envelope = ok('code.emit-handoff', { allow: true, mode: 'no-session' }, [], [
727
727
  'No active session id; gate passes through (single-rid mode).'
728
728
  ]);
729
729
  printResult(io, envelope, opts.json);
@@ -739,35 +739,35 @@ export function registerSoloCommands(program, io) {
739
739
  evalInput.forceUnderJob = true;
740
740
  const verdict = evaluateEmitHandoff(evalInput);
741
741
  if (verdict.kind === 'allow-not-job') {
742
- const envelope = ok('solo.emit-handoff', { allow: true, mode: 'single' }, [], [
742
+ const envelope = ok('code.emit-handoff', { allow: true, mode: 'single' }, [], [
743
743
  'job-shape.json says isJob=false (or absent); normal handoff allowed.'
744
744
  ]);
745
745
  printResult(io, envelope, opts.json);
746
746
  return;
747
747
  }
748
748
  if (verdict.kind === 'allow-done') {
749
- const envelope = ok('solo.emit-handoff', { allow: true, mode: 'job-done', remaining: verdict.remaining }, [], [
749
+ const envelope = ok('code.emit-handoff', { allow: true, mode: 'job-done', remaining: verdict.remaining }, [], [
750
750
  `Job is complete (remaining=0); handoff allowed.`
751
751
  ]);
752
752
  printResult(io, envelope, opts.json);
753
753
  return;
754
754
  }
755
755
  if (verdict.kind === 'allow-force-override') {
756
- const envelope = ok('solo.emit-handoff', { allow: true, mode: 'job-force-override', remaining: verdict.remaining }, [], [
756
+ const envelope = ok('code.emit-handoff', { allow: true, mode: 'job-force-override', remaining: verdict.remaining }, [], [
757
757
  `Job has ${verdict.remaining} remaining slices; --force-under-job override applied. Handoff allowed (explicit user approval).`
758
758
  ]);
759
759
  printResult(io, envelope, opts.json);
760
760
  return;
761
761
  }
762
762
  if (verdict.kind === 'block-not-initialized') {
763
- const envelope = fail('solo.emit-handoff', JOB_NOT_INITIALIZED, `Job ${verdict.jobId} has no state.json; peaks job init was skipped.`, { jobId: verdict.jobId }, [`Run \`peaks job init --job-id ${verdict.jobId} --slice-list <...>\` before emitting handoff.`]);
763
+ const envelope = fail('code.emit-handoff', JOB_NOT_INITIALIZED, `Job ${verdict.jobId} has no state.json; peaks job init was skipped.`, { jobId: verdict.jobId }, [`Run \`peaks job init --job-id ${verdict.jobId} --slice-list <...>\` before emitting handoff.`]);
764
764
  printResult(io, envelope, opts.json);
765
765
  process.exitCode = 1;
766
766
  return;
767
767
  }
768
768
  // block-remaining
769
769
  const blockMessage = `BLOCKED: Job ${verdict.jobId} has ${verdict.remaining} remaining slices. Run \`peaks job status\`. Use --force-under-job only with explicit user approval.`;
770
- const envelope = fail('solo.emit-handoff', JOB_REMAINING_BLOCKED, blockMessage, { jobId: verdict.jobId, remaining: verdict.remaining }, [
770
+ const envelope = fail('code.emit-handoff', JOB_REMAINING_BLOCKED, blockMessage, { jobId: verdict.jobId, remaining: verdict.remaining }, [
771
771
  `Run \`peaks job status --job-id ${verdict.jobId}\` to see remaining slices.`,
772
772
  'Resume Step 0.81 (per-slice checkpoint loop) and continue until remaining === 0.',
773
773
  'Use --force-under-job only with explicit user approval (size-fear ban override).'
@@ -778,7 +778,7 @@ export function registerSoloCommands(program, io) {
778
778
  return;
779
779
  }
780
780
  catch (err) {
781
- printResult(io, fail('solo.emit-handoff', 'EMIT_HANDOFF_FAILED', getErrorMessage(err), null, [
781
+ printResult(io, fail('code.emit-handoff', 'EMIT_HANDOFF_FAILED', getErrorMessage(err), null, [
782
782
  'Verify the project path and try again'
783
783
  ]), opts.json);
784
784
  process.exitCode = 1;