@minionry/minion 0.7.36 → 0.7.39

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 (120) hide show
  1. package/bin/commands/minions.js +19 -0
  2. package/bin/minion.js +33 -4
  3. package/dist/server/cli/headless/claude-invoker-process.js +23 -7
  4. package/dist/server/cli/headless/claude-invoker-stream.js +8 -3
  5. package/dist/server/cli/headless/haiku-assessments.js +2 -1
  6. package/dist/server/cli/headless/mcp-config.js +2 -1
  7. package/dist/server/engines/claude/claude-command.js +1 -0
  8. package/dist/server/engines/factory.js +2 -0
  9. package/dist/server/engines/hermes/HermesEngine.js +112 -219
  10. package/dist/server/engines/hermes/hermes-gateway-registry.js +467 -0
  11. package/dist/server/index.js +10 -4
  12. package/dist/server/mcp/bouncer-integration.js +3 -2
  13. package/dist/server/mcp/classifier/ClaudeBouncerClassifier.js +35 -8
  14. package/dist/server/mcp/classifier/shadow-eval-scheduler.js +42 -0
  15. package/dist/server/mcp/classifier/telemetry.js +0 -0
  16. package/dist/server/mcp/permission-channels.js +176 -0
  17. package/dist/server/mcp/security-analysis.js +9 -1
  18. package/dist/server/mcp/security-audit.js +22 -2
  19. package/dist/server/mcp/security-patterns.js +73 -1
  20. package/dist/server/mcp/server.js +80 -50
  21. package/dist/server/routes/internal.js +6 -1
  22. package/dist/server/routes/notifications.js +6 -18
  23. package/dist/server/server-setup.js +24 -48
  24. package/dist/server/services/analytics.js +4 -2
  25. package/dist/server/services/browser/host-mock.js +6 -1
  26. package/dist/server/services/browser/host.js +27 -3
  27. package/dist/server/services/browser/reasoning-agent.js +3 -1
  28. package/dist/server/services/chain/chain-engine.js +494 -0
  29. package/dist/server/services/chain/chain-store.js +542 -0
  30. package/dist/server/services/git/haiku.js +2 -2
  31. package/dist/server/services/plan/agents/check-injection.md +43 -2
  32. package/dist/server/services/plan/agents/review-code.md +62 -3
  33. package/dist/server/services/plan/agents/review-quality.md +52 -2
  34. package/dist/server/services/plan/board-export.js +4 -0
  35. package/dist/server/services/plan/composer-prompt.js +27 -8
  36. package/dist/server/services/plan/composer.js +10 -4
  37. package/dist/server/services/plan/config-installer.js +4 -83
  38. package/dist/server/services/plan/executor.js +155 -73
  39. package/dist/server/services/plan/issue-effort.js +9 -21
  40. package/dist/server/services/plan/issue-prompt-builder.js +72 -9
  41. package/dist/server/services/plan/parser-core.js +6 -1
  42. package/dist/server/services/plan/quality-delta.js +6 -24
  43. package/dist/server/services/plan/readiness-planner.js +12 -4
  44. package/dist/server/services/plan/record-file.js +52 -0
  45. package/dist/server/services/plan/review-approval.js +64 -0
  46. package/dist/server/services/plan/review-gate.js +186 -77
  47. package/dist/server/services/plan/review-outcome.js +146 -0
  48. package/dist/server/services/plan/review-report.js +28 -1
  49. package/dist/server/services/plan/review-target.js +57 -0
  50. package/dist/server/services/plan/state-reconciler.js +3 -3
  51. package/dist/server/services/plan/template-diff.js +2 -0
  52. package/dist/server/services/plan/template-instantiator.js +4 -0
  53. package/dist/server/services/plan/watcher.js +11 -1
  54. package/dist/server/services/platform-reconnect.js +7 -0
  55. package/dist/server/services/platform-token-lifecycle.js +2 -1
  56. package/dist/server/services/platform.js +293 -50
  57. package/dist/server/services/relay-http-client.js +107 -0
  58. package/dist/server/services/schedule/agent-schedule-store.js +69 -8
  59. package/dist/server/services/schedule/agent-scheduler.js +48 -7
  60. package/dist/server/services/schedule/daily-timing.js +52 -0
  61. package/dist/server/services/schedule/prompt-file.js +55 -0
  62. package/dist/server/services/schedule/run-bootstrap.js +20 -5
  63. package/dist/server/services/schedule/schedule-store.js +54 -4
  64. package/dist/server/services/schedule/schedule-wire.js +17 -0
  65. package/dist/server/services/schedule/scheduler.js +43 -9
  66. package/dist/server/services/sdk/agent-schedule.js +294 -0
  67. package/dist/server/services/sdk/agents.js +95 -13
  68. package/dist/server/services/sdk/app-tools.js +44 -7
  69. package/dist/server/services/sdk/atomic-write.js +75 -0
  70. package/dist/server/services/sdk/browser.js +5 -1
  71. package/dist/server/services/sdk/composer.js +2 -0
  72. package/dist/server/services/sdk/file-change-notify.js +60 -13
  73. package/dist/server/services/sdk/files-app.js +12 -7
  74. package/dist/server/services/sdk/files-search.js +14 -0
  75. package/dist/server/services/sdk/files-transfer.js +1 -1
  76. package/dist/server/services/sdk/files-watch.js +721 -0
  77. package/dist/server/services/sdk/files-workspace.js +37 -11
  78. package/dist/server/services/sdk/inference-claude.js +4 -2
  79. package/dist/server/services/sdk/inference.js +16 -2
  80. package/dist/server/services/sdk/pm-chain.js +0 -0
  81. package/dist/server/services/sdk/pm-schedule.js +72 -15
  82. package/dist/server/services/sdk/pm.js +22 -0
  83. package/dist/server/services/sdk/rate-limits.js +32 -0
  84. package/dist/server/services/sdk/registry.js +40 -2
  85. package/dist/server/services/sdk/terminal.js +1 -1
  86. package/dist/server/services/sentry.js +5 -1
  87. package/dist/server/services/terminal/pty-manager.js +26 -11
  88. package/dist/server/services/terminal/pty-utils.js +12 -1
  89. package/dist/server/services/terminal/terminal-modes.js +87 -0
  90. package/dist/server/services/timeline/assembler.js +2 -2
  91. package/dist/server/services/timeline/index.js +1 -1
  92. package/dist/server/services/websocket/agent-schedule-handlers.js +62 -4
  93. package/dist/server/services/websocket/ask-user-question-bridge.js +2 -2
  94. package/dist/server/services/websocket/browser-agent-runtime.js +3 -1
  95. package/dist/server/services/websocket/browser-handlers.js +5 -2
  96. package/dist/server/services/websocket/browser-viewer-lifecycle.js +8 -0
  97. package/dist/server/services/websocket/file-explorer-handlers.js +17 -0
  98. package/dist/server/services/websocket/file-transfer-http.js +12 -6
  99. package/dist/server/services/websocket/handler.js +185 -33
  100. package/dist/server/services/websocket/msg-id-tracker.js +94 -19
  101. package/dist/server/services/websocket/plan-execution-handlers.js +33 -5
  102. package/dist/server/services/websocket/plan-handlers.js +10 -5
  103. package/dist/server/services/websocket/plan-helpers.js +23 -0
  104. package/dist/server/services/websocket/plan-issue-handlers.js +161 -4
  105. package/dist/server/services/websocket/plan-sprint-handlers.js +3 -2
  106. package/dist/server/services/websocket/schedule-handlers.js +8 -2
  107. package/dist/server/services/websocket/sdk-agent-host.js +5 -1
  108. package/dist/server/services/websocket/sdk-agent-schedule-host.js +30 -0
  109. package/dist/server/services/websocket/sdk-browser-host.js +11 -4
  110. package/dist/server/services/websocket/sdk-handlers.js +6 -1
  111. package/dist/server/services/websocket/sdk-pm-chain-host.js +20 -0
  112. package/dist/server/services/websocket/sdk-pm-execution-host.js +21 -14
  113. package/dist/server/services/websocket/sdk-pm-schedule-host.js +9 -0
  114. package/dist/server/services/websocket/sdk-terminal-host.js +7 -0
  115. package/dist/server/services/websocket/settings-handlers.js +6 -18
  116. package/dist/server/services/websocket/tab-broadcast.js +5 -1
  117. package/dist/server/services/websocket/terminal-handlers.js +51 -6
  118. package/dist/server/services/websocket/types.js +6 -5
  119. package/dist/server/services/websocket/viewer-interest.js +30 -0
  120. package/package.json +6 -6
@@ -11,6 +11,10 @@ checks: [criteria_met, code_quality, no_obvious_bugs]
11
11
 
12
12
  You are a reviewer. Review the work done for issue {{issue_id}}: {{issue_title}}.
13
13
 
14
+ Verification is your whole job — not redesign. Decide whether what was built
15
+ meets the criteria below, prove each decision against the code you read, and
16
+ write feedback the implementing agent can act on without guessing.
17
+
14
18
  ## Files Modified
15
19
  {{files_modified}}
16
20
 
@@ -21,8 +25,11 @@ You are a reviewer. Review the work done for issue {{issue_id}}: {{issue_title}}
21
25
  {{quality_delta}}
22
26
 
23
27
  ## Instructions
24
- 1. Read each modified file listed above
25
- 2. Check if all acceptance criteria are met by the changes
28
+ 1. Read each modified file listed above. Read the code, not the summary — the
29
+ output file says what the agent BELIEVES it did.
30
+ 2. Take the acceptance criteria one at a time. For each, find the specific code
31
+ that satisfies it (or establish that nothing does) and note where you found
32
+ it. A criterion you did not check is not a criterion that passed.
26
33
  3. Evaluate the quality of the changes:
27
34
  - For source code files: look for obvious bugs, security vulnerabilities, or code quality issues
28
35
  - For content files (markdown, docs, config, copy): check for accuracy, completeness, and appropriate structure
@@ -32,7 +39,59 @@ You are a reviewer. Review the work done for issue {{issue_id}}: {{issue_title}}
32
39
  - "Unavailable" or "Not recorded" is not evidence either way and never fails a review.
33
40
  5. Check if the output artifact exists at: {{output_path}}
34
41
 
42
+ ## Rules that make a verdict usable
43
+
44
+ - **Evidence, not impression.** Every failed check needs an `evidence` string
45
+ naming what you actually read: `path/to/file.ts:42`, a quoted criterion, a
46
+ command and its output. If you cannot cite it, you have not checked it —
47
+ either go and check it, or do not fail on it.
48
+ - **Remediation, not complaint.** Every failed check needs a `remediation`
49
+ string naming the concrete change that would make it pass. "Error handling is
50
+ insufficient" is not actionable; "wrap the `fetch` in `api.ts:88` in
51
+ try/catch and surface the failure through the existing `onError` callback"
52
+ is. The next attempt gets your remediation verbatim and little else.
53
+ - **Fail on the criteria, not on taste.** An issue that meets every acceptance
54
+ criterion passes, even if you would have built it differently. Style
55
+ preferences, extra tests you would have liked, and refactors outside the
56
+ issue's scope are not failures. Only fail `code_quality` for a real defect:
57
+ a bug, a security problem, a broken contract.
58
+ - **Say how sure you are.** Set `confidence` honestly: `high` when you read the
59
+ relevant code and the verdict is clear; `medium` when you checked but some
60
+ part rests on inference; `low` when you could not reach the evidence you
61
+ needed, the change is large relative to what you could read, or you are
62
+ genuinely torn. A `low` verdict is re-reviewed by a stronger model, so
63
+ marking one is cheap and free of blame — an overconfident wrong verdict is
64
+ what actually costs.
65
+
66
+ ## When the issue CANNOT pass, however many times it is retried
67
+
68
+ Some issues are not failing — they are impossible here. If the work requires a
69
+ privileged (sudo/root) command, a credential or account this machine does not
70
+ hold, a package, service or device that cannot be installed, an unreachable
71
+ external service, or the acceptance criteria contradict each other or the code
72
+ they name, then say so instead of failing it again: set
73
+ `"disposition": "blocked"` and fill in `blocker`. The board records the reason
74
+ and stops re-dispatching the issue, rather than spending its remaining attempts
75
+ rediscovering the same wall.
76
+
77
+ `blocker.kind` must be one of: `needs_privileged_command`, `needs_credentials`,
78
+ `missing_dependency`, `external_service`, `contradictory_criteria`,
79
+ `out_of_scope_environment`.
80
+
81
+ Use it only for a genuine dead end. "The agent did it wrong", "this is hard",
82
+ or "the tests fail" are ordinary failures — return `revise` for those. A
83
+ blocked verdict cancels the issue, so it is checked by a second reviewer before
84
+ it takes effect.
85
+
86
+ ## Output
87
+
35
88
  Output EXACTLY one JSON object on its own line (no markdown fencing):
36
- {"passed": true, "checks": [{"name": "criteria_met", "passed": true, "details": "..."}]}
89
+ {"passed": true, "confidence": "high", "checks": [{"name": "criteria_met", "passed": true, "details": "...", "evidence": "..."}]}
90
+
91
+ A failing verdict adds `remediation` to each failed check:
92
+ {"passed": false, "confidence": "high", "checks": [{"name": "criteria_met", "passed": false, "details": "AC2 is not met", "evidence": "web/src/x.ts:42 has no retry path", "remediation": "Add the retry in web/src/x.ts:42 using the existing backoff helper"}]}
93
+
94
+ A blocked verdict adds the disposition and the blocker:
95
+ {"passed": false, "confidence": "high", "disposition": "blocked", "blocker": {"kind": "needs_privileged_command", "detail": "AC1 requires installing a system package via apt-get, which needs root", "humanAction": "Install <pkg> yourself, then re-run the issue"}, "checks": [{"name": "criteria_met", "passed": false, "details": "...", "evidence": "..."}]}
37
96
 
38
97
  Include checks for: criteria_met, code_quality, no_obvious_bugs.
@@ -11,6 +11,10 @@ checks: [criteria_met, output_quality, completeness]
11
11
 
12
12
  You are a quality reviewer. Review the work done for issue {{issue_id}}: {{issue_title}}.
13
13
 
14
+ Verification is your whole job — not rewriting. Decide whether the deliverable
15
+ meets the criteria below, prove each decision against what you actually read,
16
+ and write feedback the author agent can act on without guessing.
17
+
14
18
  ## Output File
15
19
  {{output_path}}
16
20
 
@@ -23,7 +27,10 @@ You are a quality reviewer. Review the work done for issue {{issue_id}}: {{issue
23
27
  ## Instructions
24
28
  1. Read the output file at the path above
25
29
  2. Read the full issue spec to understand the original requirements and intent
26
- 3. Evaluate the output against ALL of the following dimensions:
30
+ 3. Take the acceptance criteria one at a time. For each, find the passage that
31
+ satisfies it (or establish that none does) and note where. A criterion you
32
+ did not check is not a criterion that passed.
33
+ 4. Evaluate the output against ALL of the following dimensions:
27
34
 
28
35
  ### Acceptance Criteria
29
36
  - Are all acceptance criteria met? Check each one individually.
@@ -39,7 +46,50 @@ You are a quality reviewer. Review the work done for issue {{issue_id}}: {{issue
39
46
  - Are there obvious gaps, missing sections, or incomplete thoughts?
40
47
  - If the issue requested specific deliverables (e.g., a plan, analysis, document), are all deliverables present?
41
48
 
49
+ ## Rules that make a verdict usable
50
+
51
+ - **Evidence, not impression.** Every failed check needs an `evidence` string
52
+ naming what you actually read: a section heading, a quoted sentence, the
53
+ criterion it fails against. If you cannot cite it, you have not checked it.
54
+ - **Remediation, not complaint.** Every failed check needs a `remediation`
55
+ string naming the concrete change that would make it pass — which section to
56
+ add, which claim to support, which gap to close. The next attempt gets your
57
+ remediation verbatim and little else.
58
+ - **Fail on the criteria, not on taste.** A deliverable that meets every
59
+ acceptance criterion passes, even if you would have written it differently.
60
+ Length, structure and style preferences are not failures unless a criterion
61
+ names them.
62
+ - **Say how sure you are.** Set `confidence` honestly: `high` when you read the
63
+ deliverable and the verdict is clear; `medium` when part of it rests on
64
+ inference; `low` when you could not reach what you needed or you are
65
+ genuinely torn. A `low` verdict is re-reviewed by a stronger model, so
66
+ marking one is cheap — an overconfident wrong verdict is what costs.
67
+
68
+ ## When the issue CANNOT pass, however many times it is retried
69
+
70
+ If the deliverable requires information, access or an action that is not
71
+ available here — a credential or account this machine does not hold, an
72
+ unreachable external source, a privileged command, or acceptance criteria that
73
+ contradict each other — say so instead of failing it again: set
74
+ `"disposition": "blocked"` and fill in `blocker`. The board records the reason
75
+ and stops re-dispatching the issue.
76
+
77
+ `blocker.kind` must be one of: `needs_privileged_command`, `needs_credentials`,
78
+ `missing_dependency`, `external_service`, `contradictory_criteria`,
79
+ `out_of_scope_environment`.
80
+
81
+ Use it only for a genuine dead end. A thin, wrong or incomplete document is an
82
+ ordinary failure — return `revise` for that.
83
+
84
+ ## Output
85
+
42
86
  Output EXACTLY one JSON object on its own line (no markdown fencing):
43
- {"passed": true, "checks": [{"name": "criteria_met", "passed": true, "details": "..."}]}
87
+ {"passed": true, "confidence": "high", "checks": [{"name": "criteria_met", "passed": true, "details": "...", "evidence": "..."}]}
88
+
89
+ A failing verdict adds `remediation` to each failed check:
90
+ {"passed": false, "confidence": "high", "checks": [{"name": "completeness", "passed": false, "details": "No risks section", "evidence": "The document ends at '## Timeline'", "remediation": "Add a '## Risks' section covering the three dependencies named in the spec"}]}
91
+
92
+ A blocked verdict adds the disposition and the blocker:
93
+ {"passed": false, "confidence": "high", "disposition": "blocked", "blocker": {"kind": "needs_credentials", "detail": "AC2 requires pulling figures from the billing dashboard, which needs an account this Space does not have", "humanAction": "Export the figures yourself and attach them, then re-run the issue"}, "checks": [{"name": "criteria_met", "passed": false, "details": "...", "evidence": "..."}]}
44
94
 
45
95
  Include checks for: criteria_met, output_quality, completeness.
@@ -106,6 +106,10 @@ function toTemplateIssue(issue, known, ctx) {
106
106
  out.effort_level = issue.effortLevel;
107
107
  if (issue.fastMode != null)
108
108
  out.fast_mode = issue.fastMode;
109
+ if (issue.reviewModel)
110
+ out.review_model = issue.reviewModel;
111
+ if (issue.reviewEffort)
112
+ out.review_effort = issue.reviewEffort;
109
113
  return out;
110
114
  }
111
115
  export function buildBoardTemplate(boardState, ctx, options = {}) {
@@ -17,7 +17,8 @@ created: "YYYY-MM-DD"
17
17
  blocked_by: [] # Use backlog-relative paths: backlog/IS-NNN.md
18
18
  blocks: [] # Use backlog-relative paths: backlog/IS-NNN.md
19
19
  review_gate: auto
20
- review_model: fable # model that runs the AI review gate — see "review_model rules" below
20
+ review_model: sonnet # model that runs the AI review gate — see "review_model rules" below
21
+ review_effort: high # reasoning effort for the review gate
21
22
  output_type: auto # code = modify source files, document = produce written artifact, auto = infer
22
23
  output_file: null
23
24
  agents: [] # Agent hints — see "agents field rules" below
@@ -71,13 +72,31 @@ detail transfers your judgment; speculation transfers your mistakes.
71
72
 
72
73
  ## review_model rules
73
74
 
74
- \`review_model\` names the model that runs the AI review gate after the
75
- issue's implementation completes. ALWAYS set \`review_model: fable\` on every
76
- issue, regardless of the issue's execution \`model\` the review gate is the
77
- quality equalizer that lets cheaper execution models deliver frontier-quality
78
- results, so it must run on the strongest model. The gate itself is pinned to
79
- fable at max effort and overrides any other value, so never write a different
80
- one.
75
+ \`review_model\` and \`review_effort\` name the model and reasoning effort that
76
+ run the AI review gate after the issue's implementation completes. The gate is
77
+ the quality equalizer that lets cheaper execution models deliver
78
+ frontier-quality results — but the equalizer does not have to be the most
79
+ expensive model on the board to work.
80
+
81
+ Pick the reviewer for a different question than the executor: not "how hard is
82
+ this to build" but **"how hard is this to CHECK"**. Verifying that work meets
83
+ stated acceptance criteria is a substantially easier task than producing that
84
+ work, so the reviewer usually belongs at or below the executor's rung. Write
85
+ acceptance criteria a reviewer can verify objectively and the reviewer can be
86
+ cheap; write vague ones and no reviewer is good enough.
87
+
88
+ - \`haiku\` / \`auto\` — mechanical or documentation issues whose criteria are
89
+ literally checkable.
90
+ - \`sonnet\` / \`high\` — the default for the large majority of issues,
91
+ including most \`opus\` ones.
92
+ - \`opus\` / \`high\` — where a wrong PASS is expensive: security, auth,
93
+ payments, data migrations, concurrency, or criteria needing real judgement.
94
+ - \`fable\` / \`xhigh\` — the rare architecture-critical issue only.
95
+
96
+ The gate escalates a low-confidence verdict one rung higher on its own, so
97
+ routing a review low is a cost decision rather than a quality one. Never write
98
+ \`review_effort: max\`: review is bounded verification work and \`max\`
99
+ overthinks it.
81
100
 
82
101
  ## Issue scoping rules (critical for execution quality)
83
102
 
@@ -15,6 +15,7 @@ import { ISSUE_FORMAT_SPEC } from './composer-prompt.js';
15
15
  import { defaultPmDir, getNextId, parseBoardDirectory, parsePlanDirectory, resolvePmDir } from './parser.js';
16
16
  import { createPlanRunner } from './plan-runner-factory.js';
17
17
  import { PM_COMPOSER_ISSUE_ID } from './pm-timeline.js';
18
+ import { broadcastPlanStateIfChanged } from './watcher.js';
18
19
  const PLANNING_SETTLE_MS = 90_000;
19
20
  const PLANNING_TOKEN_SILENCE_MS = 120_000;
20
21
  const PLANNING_SETTLE_CHECK_INTERVAL_MS = 10_000;
@@ -243,7 +244,7 @@ Add these exact fields to EVERY issue's YAML front matter — identical values f
243
244
  - \`model: ${model}\`
244
245
  - \`effort_level: ${effort}\`
245
246
  - \`fast_mode: ${fast}\`
246
- - \`review_model: fable\` (the review gate always runs on the strongest model see review_model rules)
247
+ - \`review_model: ${model}\` and \`review_effort: high\` — review on the same rung the issue executes on (checking work is cheaper than doing it, so this is the conservative default; see review_model rules)
247
248
  `;
248
249
  }
249
250
  export function buildModelDirectiveBlock(directive, globalDefaults) {
@@ -264,9 +265,14 @@ Assign an execution model to EACH issue based on its complexity by adding these
264
265
  - \`model:\` one of \`haiku\` (trivial/mechanical/doc work), \`sonnet\` (standard implementation — the default for the large majority of issues, including standard bug fixes and features), \`opus\` (difficult agentic coding — hard multi-file features, larger refactors, deep multi-step debugging, and long-horizon work spanning many subsystems; since Opus 5 this band is a real capability step over sonnet at the same Opus price as before), or \`fable\` (reserve for the rare architecture-critical issue where a wrong design decision is expensive to unwind)
265
266
  - \`effort_level:\` \`high\` for standard \`sonnet\` issues; \`xhigh\` for EVERY \`opus\` issue and for the hardest \`sonnet\`/\`fable\` issues (on Opus 5 and Sonnet 5, xhigh is the recommended effort for agentic coding — it outperforms the other levels while spending fewer output tokens than max); \`max\` only when correctness matters more than cost and latency — it can overthink routine work; \`low\` or \`medium\` for trivial/mechanical work (both punch far above their weight on this generation; \`auto\` is NOT the cheap option — it resolves to the model's recommended band). Haiku ignores effort — use \`auto\` for Haiku issues.
266
267
  - \`fast_mode:\` \`false\` by default. Fast mode is Opus-only (latency-prioritized output at premium price) — set \`true\` only on an \`opus\` issue where the user explicitly asked for the fastest possible turnaround.
267
- - \`review_model: fable\` on EVERY issue regardless of its execution model the review gate must run on the strongest model. The gate is pinned to fable at max effort and overrides any other value, so never write a different one.
268
+ - \`review_model:\` and \`review_effort:\` the model that runs the AI review gate for this issue, chosen the same way, but for a DIFFERENT question: not "how hard is this to build" but "how hard is this to CHECK". Verifying that work meets stated criteria is a substantially easier task than producing it, so the reviewer usually belongs at or below the executor's rung:
269
+ - \`review_model: haiku\`, \`review_effort: auto\` — mechanical or documentation issues whose criteria are literally checkable (a file exists, a string appears, a list is complete).
270
+ - \`review_model: sonnet\`, \`review_effort: high\` — **the default for the large majority of issues**, including every standard \`sonnet\` issue and most \`opus\` ones. Reading a diff against explicit acceptance criteria is well within this rung.
271
+ - \`review_model: opus\`, \`review_effort: high\` — issues where a wrong PASS is expensive: security, auth, payments, data migrations, concurrency, or criteria that need real judgement rather than a lookup.
272
+ - \`review_model: fable\`, \`review_effort: xhigh\` — reserve for the same rare architecture-critical issues you would give \`model: fable\`. Do not use it as a blanket safety net; it is the most expensive setting on the board.
273
+ A low-confidence verdict is automatically re-reviewed one rung higher, so routing a review low is a cost decision, not a quality one — the cascade catches an unsure cheap reviewer on its own. Never write \`review_effort: max\`: review is bounded verification work and \`max\` overthinks it.
268
274
 
269
- A well-written issue closes most of the capability gap, and the fable review gate catches what the executor misses — so pick the cheapest model that can do the job well: most issues should be \`sonnet\`; when in doubt between \`sonnet\` and \`opus\` on standard work, pick \`sonnet\`. Do NOT under-route genuinely hard work though: hard multi-file and long-horizon issues belong on \`opus\` at \`xhigh\`.
275
+ A well-written issue closes most of the capability gap, and the review gate catches what the executor misses — so pick the cheapest model that can do the job well: most issues should be \`sonnet\`; when in doubt between \`sonnet\` and \`opus\` on standard work, pick \`sonnet\`. Do NOT under-route genuinely hard work though: hard multi-file and long-horizon issues belong on \`opus\` at \`xhigh\`.
270
276
  `;
271
277
  }
272
278
  export const SYNTHESIS_MODEL = 'sonnet';
@@ -537,7 +543,7 @@ function reportPlanningResult(ctx, args) {
537
543
  }
538
544
  const updatedState = parsePlanDirectory(workingDir);
539
545
  if (updatedState) {
540
- ctx.broadcastToAll({ type: 'planStateUpdated', data: updatedState });
546
+ broadcastPlanStateIfChanged(ctx, workingDir, updatedState);
541
547
  }
542
548
  }
543
549
  export async function handlePlanPrompt(ctx, _ws, userPrompt, workingDir, options = {}) {
@@ -1,17 +1,7 @@
1
1
  // Copyright (c) 2025-present Minionry, Inc.
2
2
  // SPDX-License-Identifier: LicenseRef-Minionry-Software
3
- import { existsSync, mkdirSync, readFileSync, unlinkSync, writeFileSync } from 'node:fs';
3
+ import { existsSync, readFileSync, unlinkSync, writeFileSync } from 'node:fs';
4
4
  import { join } from 'node:path';
5
- const REQUIRED_PERMISSIONS = [
6
- 'Bash',
7
- 'Read',
8
- 'Edit',
9
- 'Write',
10
- 'Glob',
11
- 'Grep',
12
- 'WebFetch',
13
- 'WebSearch',
14
- ];
15
5
  function restoreFromBackup(backupPath, targetPath) {
16
6
  try {
17
7
  if (!existsSync(backupPath))
@@ -28,76 +18,7 @@ function restoreFromBackup(backupPath, targetPath) {
28
18
  }
29
19
  catch { }
30
20
  }
31
- export class ConfigInstaller {
32
- workingDir;
33
- savedClaudeSettings = null;
34
- claudeSettingsInstalled = false;
35
- constructor(workingDir) {
36
- this.workingDir = workingDir;
37
- this.recoverFromCrash();
38
- }
39
- recoverFromCrash() {
40
- restoreFromBackup(join(this.workingDir, '.claude', 'settings.json.pm-backup'), join(this.workingDir, '.claude', 'settings.json'));
41
- restoreFromBackup(join(this.workingDir, '.mcp.json.pm-backup'), join(this.workingDir, '.mcp.json'));
42
- }
43
- installPermissions() {
44
- if (this.claudeSettingsInstalled)
45
- return;
46
- const claudeDir = join(this.workingDir, '.claude');
47
- const settingsPath = join(claudeDir, 'settings.json');
48
- if (!existsSync(claudeDir)) {
49
- mkdirSync(claudeDir, { recursive: true });
50
- }
51
- const backupPath = join(claudeDir, 'settings.json.pm-backup');
52
- try {
53
- if (existsSync(settingsPath)) {
54
- this.savedClaudeSettings = readFileSync(settingsPath, 'utf-8');
55
- writeFileSync(backupPath, this.savedClaudeSettings);
56
- const existing = JSON.parse(this.savedClaudeSettings);
57
- if (!existing.permissions)
58
- existing.permissions = {};
59
- if (!existing.permissions.allow)
60
- existing.permissions.allow = [];
61
- for (const tool of REQUIRED_PERMISSIONS) {
62
- if (!existing.permissions.allow.includes(tool)) {
63
- existing.permissions.allow.push(tool);
64
- }
65
- }
66
- writeFileSync(settingsPath, JSON.stringify(existing, null, 2));
67
- }
68
- else {
69
- this.savedClaudeSettings = null;
70
- writeFileSync(backupPath, '__NONE__');
71
- writeFileSync(settingsPath, JSON.stringify({
72
- permissions: { allow: REQUIRED_PERMISSIONS },
73
- }, null, 2));
74
- }
75
- this.claudeSettingsInstalled = true;
76
- }
77
- catch {
78
- }
79
- }
80
- uninstallPermissions() {
81
- if (!this.claudeSettingsInstalled)
82
- return;
83
- const settingsPath = join(this.workingDir, '.claude', 'settings.json');
84
- const backupPath = join(this.workingDir, '.claude', 'settings.json.pm-backup');
85
- try {
86
- if (this.savedClaudeSettings !== null) {
87
- writeFileSync(settingsPath, this.savedClaudeSettings);
88
- }
89
- else {
90
- unlinkSync(settingsPath);
91
- }
92
- }
93
- catch {
94
- }
95
- try {
96
- if (existsSync(backupPath))
97
- unlinkSync(backupPath);
98
- }
99
- catch { }
100
- this.savedClaudeSettings = null;
101
- this.claudeSettingsInstalled = false;
102
- }
21
+ export function restoreInterruptedWaveSettings(workingDir) {
22
+ restoreFromBackup(join(workingDir, '.claude', 'settings.json.pm-backup'), join(workingDir, '.claude', 'settings.json'));
23
+ restoreFromBackup(join(workingDir, '.mcp.json.pm-backup'), join(workingDir, '.mcp.json'));
103
24
  }