mixdog 0.9.39 → 0.9.41

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 (59) hide show
  1. package/package.json +2 -1
  2. package/scripts/agent-terminal-reap-test.mjs +6 -6
  3. package/scripts/anthropic-oauth-refresh-race-test.mjs +338 -0
  4. package/scripts/compact-pressure-test.mjs +128 -0
  5. package/scripts/compact-trigger-migration-smoke.mjs +8 -8
  6. package/scripts/execution-resume-esc-integration-test.mjs +4 -2
  7. package/scripts/internal-comms-smoke.mjs +66 -25
  8. package/scripts/max-output-recovery-persist-test.mjs +81 -0
  9. package/scripts/max-output-recovery-test.mjs +222 -0
  10. package/scripts/provider-toolcall-test.mjs +32 -0
  11. package/scripts/steering-drain-buckets-test.mjs +140 -5
  12. package/scripts/tui-transcript-perf-test.mjs +279 -0
  13. package/src/agents/reviewer/AGENT.md +4 -0
  14. package/src/rules/lead/lead-brief.md +11 -3
  15. package/src/rules/lead/lead-tool.md +0 -2
  16. package/src/rules/shared/01-tool.md +4 -0
  17. package/src/runtime/agent/orchestrator/agent-runtime/agent-progress-watchdog.mjs +20 -2
  18. package/src/runtime/agent/orchestrator/agent-trace-format.mjs +7 -0
  19. package/src/runtime/agent/orchestrator/context/collect.mjs +7 -3
  20. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-credentials.mjs +144 -7
  21. package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +15 -2
  22. package/src/runtime/agent/orchestrator/providers/anthropic.mjs +2 -2
  23. package/src/runtime/agent/orchestrator/providers/openai-ws-stream.mjs +57 -25
  24. package/src/runtime/agent/orchestrator/session/agent-loop.mjs +54 -12
  25. package/src/runtime/agent/orchestrator/session/context-utils.mjs +4 -3
  26. package/src/runtime/agent/orchestrator/session/loop/compact-policy.mjs +106 -8
  27. package/src/runtime/agent/orchestrator/session/loop/steering-ladder.mjs +12 -1
  28. package/src/runtime/agent/orchestrator/session/loop/termination.mjs +22 -7
  29. package/src/runtime/agent/orchestrator/session/manager/ask-session.mjs +26 -1
  30. package/src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs +17 -1
  31. package/src/runtime/agent/orchestrator/session/manager/pending-messages.mjs +26 -0
  32. package/src/runtime/agent/orchestrator/session/manager/runtime-liveness.mjs +31 -4
  33. package/src/runtime/agent/orchestrator/session/manager/usage-metrics.mjs +2 -0
  34. package/src/runtime/agent/orchestrator/session/pre-send-compact.mjs +11 -2
  35. package/src/runtime/agent/orchestrator/session/send-with-recovery.mjs +45 -0
  36. package/src/runtime/agent/orchestrator/session/store.mjs +9 -1
  37. package/src/runtime/agent/orchestrator/session/tool-result-offload.mjs +43 -1
  38. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +49 -34
  39. package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +1 -1
  40. package/src/runtime/agent/orchestrator/tools/shell-command.mjs +9 -10
  41. package/src/runtime/shared/buffered-appender.mjs +13 -2
  42. package/src/session-runtime/config-helpers.mjs +6 -6
  43. package/src/session-runtime/lifecycle-api.mjs +4 -0
  44. package/src/session-runtime/runtime-core.mjs +1 -0
  45. package/src/session-runtime/session-turn-api.mjs +12 -0
  46. package/src/standalone/agent-tool.mjs +8 -1
  47. package/src/tui/App.jsx +2 -1
  48. package/src/tui/app/transcript-window.mjs +9 -10
  49. package/src/tui/app/use-transcript-window.mjs +38 -56
  50. package/src/tui/dist/index.mjs +354 -163
  51. package/src/tui/engine/agent-job-feed.mjs +76 -6
  52. package/src/tui/engine/session-api.mjs +7 -1
  53. package/src/tui/engine/session-flow.mjs +3 -1
  54. package/src/tui/engine/tool-card-results.mjs +10 -5
  55. package/src/tui/engine/turn.mjs +96 -36
  56. package/src/tui/engine.mjs +136 -37
  57. package/src/tui/index.jsx +2 -2
  58. package/src/workflows/bench/WORKFLOW.md +51 -27
  59. package/src/workflows/default/WORKFLOW.md +29 -24
@@ -142,6 +142,87 @@ export { parseBackgroundTaskEnvelope };
142
142
  // so importers/tests keep resolving resolveTuiRuntimeNotificationDelivery from engine.mjs.
143
143
  export { resolveTuiRuntimeNotificationDelivery };
144
144
 
145
+ export function replaceEngineItemsState({
146
+ state,
147
+ items,
148
+ itemIndexById,
149
+ preserveStreamingTail = false,
150
+ extra = {},
151
+ }) {
152
+ const nextItems = Array.isArray(items) ? items : [];
153
+ itemIndexById.clear();
154
+ for (let i = 0; i < nextItems.length; i++) {
155
+ const id = nextItems[i]?.id;
156
+ if (id != null) itemIndexById.set(id, i);
157
+ }
158
+ return {
159
+ ...state,
160
+ ...extra,
161
+ items: nextItems,
162
+ structureRevision: (Number(state.structureRevision) || 0) + 1,
163
+ streamingTail: preserveStreamingTail ? state.streamingTail : null,
164
+ };
165
+ }
166
+
167
+ // Shared by the live engine and focused transcript tests so revision/tail
168
+ // regressions exercise the exact mutation implementation used in production.
169
+ export function createEngineItemMutators({ getState, set, itemIndexById }) {
170
+ const patchItem = (id, patch) => {
171
+ const state = getState();
172
+ let index = itemIndexById.get(id);
173
+ if (!Number.isInteger(index) || state.items[index]?.id !== id) {
174
+ index = state.items.findIndex((it) => it.id === id);
175
+ if (index >= 0) itemIndexById.set(id, index);
176
+ }
177
+ if (index < 0) return false;
178
+ const current = state.items[index];
179
+ let changed = false;
180
+ for (const [key, value] of Object.entries(patch || {})) {
181
+ if (!Object.is(current[key], value)) {
182
+ changed = true;
183
+ break;
184
+ }
185
+ }
186
+ if (!changed) return false;
187
+ const items = state.items.slice();
188
+ items[index] = { ...current, ...patch };
189
+ set({ items, structureRevision: (Number(state.structureRevision) || 0) + 1 });
190
+ return true;
191
+ };
192
+
193
+ const settleStreamingTail = (id, patch = {}, extra = {}) => {
194
+ const state = getState();
195
+ const tail = state.streamingTail?.id === id ? state.streamingTail : null;
196
+ // Bulk transcript replacement/reset owns the new transcript. A stale turn
197
+ // must never append into it after replaceItems deliberately cleared its tail.
198
+ if (!tail) return false;
199
+ let existingIndex = itemIndexById.get(id);
200
+ if (!Number.isInteger(existingIndex) || state.items[existingIndex]?.id !== id) {
201
+ existingIndex = state.items.findIndex((item) => item?.id === id);
202
+ }
203
+ if (existingIndex >= 0) return false;
204
+ const item = {
205
+ ...(tail || {}),
206
+ ...patch,
207
+ kind: 'assistant',
208
+ id,
209
+ streaming: false,
210
+ };
211
+ const index = state.items.length;
212
+ const items = [...state.items, item];
213
+ itemIndexById.set(id, index);
214
+ set({
215
+ items,
216
+ structureRevision: (Number(state.structureRevision) || 0) + 1,
217
+ streamingTail: null,
218
+ ...extra,
219
+ });
220
+ return true;
221
+ };
222
+
223
+ return { patchItem, settleStreamingTail };
224
+ }
225
+
145
226
  export async function createEngineSession({
146
227
  provider: providerName,
147
228
  model,
@@ -205,6 +286,8 @@ export async function createEngineSession({
205
286
  };
206
287
  let state = {
207
288
  items: [],
289
+ structureRevision: 0,
290
+ streamingTail: null,
208
291
  toasts: [],
209
292
  progressHint: null,
210
293
  busy: false,
@@ -283,13 +366,8 @@ export async function createEngineSession({
283
366
  };
284
367
 
285
368
  const itemIndexById = new Map();
286
- const replaceItems = (items) => {
369
+ const replaceItems = (items, { preserveStreamingTail = false } = {}) => {
287
370
  const nextItems = Array.isArray(items) ? items : [];
288
- itemIndexById.clear();
289
- for (let i = 0; i < nextItems.length; i++) {
290
- const id = nextItems[i]?.id;
291
- if (id != null) itemIndexById.set(id, i);
292
- }
293
371
  // Bulk item swap (session load / clear / compact). Derive the prompt-history
294
372
  // list from the NEW items and stage it onto state here so App never rescans;
295
373
  // the callers that invoke replaceItems always follow with a set({items:...,
@@ -297,12 +375,21 @@ export async function createEngineSession({
297
375
  // emit (the accompanying set() diffs the full patch). A bulk swap also
298
376
  // discards the old transcript, so drop any tracked active tool calls.
299
377
  activeToolCalls.clear();
300
- state = {
301
- ...state,
378
+ state = replaceEngineItemsState({
379
+ state,
302
380
  items: nextItems,
303
- promptHistoryList: buildMergedPromptHistory(recomputePromptHistory(nextItems), loadPromptHistory(state.cwd)),
304
- activeToolSummary: null,
305
- };
381
+ itemIndexById,
382
+ preserveStreamingTail,
383
+ extra: {
384
+ promptHistoryList: buildMergedPromptHistory(recomputePromptHistory(nextItems), loadPromptHistory(state.cwd)),
385
+ activeToolSummary: null,
386
+ },
387
+ });
388
+ // replaceItems stages the bulk state before its callers compose their
389
+ // accompanying patch. Emit here as well so an items-only replacement
390
+ // (for example removeNotice) cannot be hidden by the outer set seeing the
391
+ // already-installed array identity.
392
+ emit();
306
393
  return nextItems;
307
394
  };
308
395
  // --- Prompt-history list (newest-first, deduped) maintained incrementally ---
@@ -371,11 +458,39 @@ export async function createEngineSession({
371
458
  // first — set() diffs against the current state, so a pre-assign would make
372
459
  // the references identical and skip emit().
373
460
  const promptHistoryList = buildMergedPromptHistory(recomputePromptHistory(items), loadPromptHistory(state.cwd));
374
- set({ items, promptHistoryList });
461
+ set({ items, structureRevision: state.structureRevision + 1, promptHistoryList });
375
462
  } else {
376
- set({ items });
463
+ set({ items, structureRevision: state.structureRevision + 1 });
464
+ }
465
+ };
466
+ const updateStreamingTail = (id, patch = {}, extra = {}) => {
467
+ if (id == null) return false;
468
+ const current = state.streamingTail?.id === id
469
+ ? state.streamingTail
470
+ : { kind: 'assistant', id, text: '', streaming: true };
471
+ const next = { ...current, ...patch, kind: 'assistant', id, streaming: true };
472
+ let changed = state.streamingTail !== current;
473
+ if (!changed) {
474
+ for (const [key, value] of Object.entries(next)) {
475
+ if (!Object.is(current[key], value)) {
476
+ changed = true;
477
+ break;
478
+ }
479
+ }
480
+ }
481
+ return set(changed ? { streamingTail: next, ...extra } : extra);
482
+ };
483
+ const clearStreamingTail = (id = null, extra = {}) => {
484
+ if (!state.streamingTail || (id != null && state.streamingTail.id !== id)) {
485
+ return set(extra);
377
486
  }
487
+ return set({ streamingTail: null, ...extra });
378
488
  };
489
+ const { patchItem, settleStreamingTail } = createEngineItemMutators({
490
+ getState: () => state,
491
+ set,
492
+ itemIndexById,
493
+ });
379
494
  const upsertSyntheticToolItem = (text, id = nextId(), parsed = null) => {
380
495
  const synthetic = parseSyntheticAgentMessage(text);
381
496
  if (!synthetic) return false;
@@ -513,7 +628,7 @@ export async function createEngineSession({
513
628
  if (id == null) return false;
514
629
  const items = state.items.filter((it) => !(it?.kind === 'notice' && it?.id === id));
515
630
  if (items.length === state.items.length) return false;
516
- set({ items: replaceItems(items) });
631
+ set({ items: replaceItems(items, { preserveStreamingTail: true }) });
517
632
  return true;
518
633
  };
519
634
  // Sticky (non-TTL) input-hint-line progress state, for long-running
@@ -538,27 +653,6 @@ export async function createEngineSession({
538
653
  getDisposed: () => flags.disposed,
539
654
  timeoutMs: TOOL_APPROVAL_TIMEOUT_MS,
540
655
  });
541
- const patchItem = (id, patch) => {
542
- let index = itemIndexById.get(id);
543
- if (!Number.isInteger(index) || state.items[index]?.id !== id) {
544
- index = state.items.findIndex((it) => it.id === id);
545
- if (index >= 0) itemIndexById.set(id, index);
546
- }
547
- if (index < 0) return false;
548
- const current = state.items[index];
549
- let changed = false;
550
- for (const [key, value] of Object.entries(patch || {})) {
551
- if (!Object.is(current[key], value)) {
552
- changed = true;
553
- break;
554
- }
555
- }
556
- if (!changed) return false;
557
- const items = state.items.slice();
558
- items[index] = { ...current, ...patch };
559
- set({ items });
560
- return true;
561
- };
562
656
  const toastTimers = new Set();
563
657
  lifecycle.runtimePulseTimer = setInterval(() => {
564
658
  if (flags.disposed) return;
@@ -587,6 +681,7 @@ export async function createEngineSession({
587
681
  updateAgentJobCard,
588
682
  buildAgentJobCardPatch,
589
683
  subscribeRuntimeNotifications,
684
+ clearExecutionDedupState,
590
685
  } = createAgentJobFeed({
591
686
  runtime,
592
687
  getState: () => state,
@@ -603,6 +698,7 @@ export async function createEngineSession({
603
698
  agentStatusState,
604
699
  displayedExecutionNotificationKeys,
605
700
  pushNotice,
701
+ itemIndexById,
606
702
  });
607
703
  lifecycle.unsubscribeRuntimeNotifications = subscribeRuntimeNotifications();
608
704
 
@@ -627,19 +723,22 @@ export async function createEngineSession({
627
723
  updateAgentJobCard,
628
724
  buildAgentJobCardPatch,
629
725
  agentStatusState,
726
+ itemIndexById,
630
727
  });
631
728
 
632
729
 
633
730
  Object.assign(bag, {
634
731
  runtime, nextId, tuiDebug, LEAD_TURN_TIMEOUT_MS,
635
- flags, lifecycle, pending, pendingNotificationKeys, displayedExecutionNotificationKeys, listeners, itemIndexById,
732
+ flags, lifecycle, pending, pendingNotificationKeys, displayedExecutionNotificationKeys, clearExecutionDedupState, listeners, itemIndexById,
636
733
  getState: () => state, set,
637
- pushItem, patchItem, replaceItems, pushToast, pushNotice, removeNotice, setProgressHint,
734
+ pushItem, patchItem, replaceItems, updateStreamingTail, settleStreamingTail, clearStreamingTail,
735
+ pushToast, pushNotice, removeNotice, setProgressHint,
638
736
  pushUserOrSyntheticItem, pushAsyncAgentResponse, upsertSyntheticToolItem,
639
737
  markToolCallActive, markToolCallDone, clearActiveToolSummary, clearToastTimers,
640
738
  autoClearState, agentStatusState, baseRouteState, routeState, syncContextStats,
641
739
  presentNextToolApproval, finishToolApproval, denyAllToolApprovals, requestToolApproval,
642
740
  patchToolCardResult, flushToolResults,
741
+ clearExecutionDedupState,
643
742
  kickExecutionPendingResume, flushDeferredExecutionPendingResumeKick, scheduleExecutionPendingResumeKick, discardExecutionPendingResume, updateAgentJobCard, subscribeRuntimeNotifications,
644
743
  });
645
744
  Object.assign(bag, createSessionFlow(bag));
package/src/tui/index.jsx CHANGED
@@ -281,7 +281,7 @@ function paintBootSplash() {
281
281
  const rows = Math.max(1, Number(process.stdout.rows) || 24);
282
282
  const windowsLikeTerminal = process.platform === 'win32' || Boolean(process.env.WT_SESSION);
283
283
  const frameCols = Math.max(1, cols - (windowsLikeTerminal ? 1 : 0));
284
- const center = (s) => `${' '.repeat(Math.max(0, Math.floor((frameCols - displayWidth(s)) / 2)))}${s}`;
284
+ const center = (s, reserve = 0) => `${' '.repeat(Math.max(0, Math.floor((frameCols - reserve - displayWidth(s)) / 2)))}${s}`;
285
285
  const logo = [
286
286
  '███╗ ███╗██╗██╗ ██╗██████╗ ██████╗ ██████╗ ',
287
287
  '████╗ ████║██║╚██╗██╔╝██╔══██╗██╔═══██╗██╔════╝ ',
@@ -300,7 +300,7 @@ function paintBootSplash() {
300
300
  out += `${bold}${fg}${center(logo[i])}${reset}\r\n`;
301
301
  }
302
302
  out += '\r\n';
303
- out += `${subtleFg}${center(`mixdog coding agent · v${localPackageVersion()} · ${process.cwd()}`)}${reset}`;
303
+ out += `${subtleFg}${center(`mixdog coding agent · v${localPackageVersion()} · ${process.cwd()}`, 4)}${reset}`;
304
304
 
305
305
  // Park the cursor at home so ink's first frame paints top-down over the
306
306
  // splash instead of starting at the bottom row and scrolling the screen.
@@ -1,36 +1,60 @@
1
1
  ---
2
2
  id: bench
3
3
  name: Bench
4
- description: "Autonomous benchmark workflow loop to completion without user approval."
4
+ description: "Autonomous headless benchmark workflow with directed execution and parallel cross-verification."
5
5
  hidden: true
6
6
  agents: worker, heavy-worker, reviewer, debugger, maintainer
7
7
  ---
8
8
 
9
9
  # Bench
10
10
 
11
- Autonomous: no user. Never wait for approval or ask questions; decide/proceed.
12
- Loop until verified complete or provably blocked.
13
-
14
- Lead supervises, delegates, coordinates, judges, decides. Lead directly handles
15
- simple 1–2-step work, coordination, pre-planning, config, final git deployment.
16
- Worker: several-step implementation; Heavy Worker: high-complexity multi-step
17
- implementation; Reviewer: implementation diff/regression/missing-check review;
18
- Debugger: very high complexity or root cause already failed once.
19
-
20
- 1. Plan: form it and execute immediately; no approval step.
21
- 2. Delegate: split to the maximum independent scopes. Parallelize independent
22
- implementation/analysis/review/debugging, spawning every scope in one turn.
23
- Shared/cross-cutting code does not justify merging: split by path and verify
24
- shared parts yourself. A single scope needs a genuinely inseparable
25
- dependency; state it. Within one complex scope, sequence steps with a
26
- build/test-green gate between them. Briefs follow the Lead brief contract.
27
- After async spawn, end turn: no polling, guessing, or dependent work until
28
- completion notification.
29
- 3. Review: one Reviewer per implementation scope, spawned 1:1 in the same turn,
30
- never deferred/batched; wait. Fact-check agent responses and cross-check
31
- implementation/review yourself before acting. Return fixes to the original
32
- scope; loop verify -> fix -> re-verify until clean. Skip only simple,
33
- low-risk review. If a bug survives 2+ fix cycles, Debugger investigates
34
- before another fix round.
35
- 4. Finish: verify final state yourself; stop only verified complete or hard
36
- blocked. Final states outcome and evidence.
11
+ Autonomous headless run: no user exists. Never ask questions, propose plans
12
+ for approval, or end the turn waiting — decide and proceed. Standing
13
+ pre-approval covers every action: edits, state mutation, delegation, builds.
14
+ Loop until the task is verified complete or provably blocked.
15
+
16
+ The pipeline has exactly three stages. Lead directs and verifies it, but NEVER
17
+ executes the work itself: no edits, answers, or artifact generation, regardless
18
+ of task size, perceived simplicity, or number of steps.
19
+
20
+ ## 1. Lead analysis and direction
21
+ Lead performs real analysis first on EVERY task: task structure, required
22
+ deliverables, boundaries, the task's own verification means, and solution
23
+ direction. Convert it into clear execution direction and delegation briefs
24
+ following the Lead brief contract.
25
+
26
+ Hand the direction and briefs to executors. Analysis is not permission for
27
+ Lead to implement any portion of the result: all edits, answer production, and
28
+ artifact generation belong to executor sessions.
29
+
30
+ ## 2. Executors perform the work
31
+ Use Worker sessions, or Heavy Worker sessions for broad scopes, to execute the
32
+ directed work. Multiple workers MAY fan out in parallel, one per independent
33
+ scope. Keep ownership clear for each assigned deliverable.
34
+
35
+ Executors build the answer or artifact and perform basic does-it-run checks.
36
+ They are not a verification role and do not own acceptance, requirements
37
+ judgment, or final correctness. Each executor reports its result to Lead.
38
+
39
+ ## 3. Lead and Reviewer verify
40
+
41
+ When executors report, Lead and Reviewer start cross-verification IN PARALLEL,
42
+ never serially. Lead personally runs the task's own verification means, such
43
+ as its test suite, grader script, simulator, or self-checkable output.
44
+ Reviewer independently judges the result against every task requirement,
45
+ including intent, correctness, boundaries, and deliverables.
46
+
47
+ Judgment-type answers with no direct ground truth still require parallel
48
+ cross-verification, even for a single small deliverable. Lead evaluates against
49
+ available criteria while Reviewer independently judges requirements;
50
+ perceived simplicity is no exemption.
51
+
52
+ Merge any finding into a clear fix delta and return it to the SAME executor
53
+ session owning the affected scope. The executor applies it and reports again.
54
+ Lead and Reviewer repeat the same parallel cross-verification until both legs
55
+ are clean; Lead never fixes or completes the work directly.
56
+
57
+ Before declaring completion, Lead personally runs the task-required
58
+ verification one final time and checks that every deliverable is present.
59
+ Never end with a question, approval request, or proposed next step. Continue
60
+ until verified complete or stop only when completion is provably blocked.
@@ -13,33 +13,38 @@ Approval is a later explicit user message after the latest plan ("do it",
13
13
  approval with a scope change needs a revised plan and fresh approval. Before
14
14
  approval: no edits, state mutation, or delegation.
15
15
 
16
- Lead supervises, delegates, coordinates, judges, decides. After approval,
17
- delegate by default; Lead only coordinates, does git, or an obvious 1-edit/
18
- 1-check change. Route other implementation/research/debugging to its matching
19
- agent. Worker: bounded established path, low local risk/coupling/verification,
20
- clear local check. Heavy Worker: high risk/coupling/verification (including any
21
- high-risk scope), or coupled staged work needing coordinated verification.
22
- Architecture, contracts, storage, concurrency, security, lifecycle are
23
- indicators, not automatic categories. Reviewer verifies an implementation;
24
- Debugger handles requested debugging or root cause after a failed fix.
16
+ Lead orchestrates and verifies. Lead-direct work is allowed only for pure
17
+ read/analysis, git/configuration, or when the user explicitly supplies both the
18
+ exact target and exact replacement/output. Never infer an exemption from a task
19
+ name, file count, or perceived difficulty. Every other implementation, reverse
20
+ engineering, debugging application, or artifact generation delegates to the
21
+ matching agent. Debugger owns diagnosis and reverse engineering; Worker applies
22
+ an established bounded change or fully specified artifact; Heavy Worker owns
23
+ implementation that must establish the change through investigation or staged
24
+ delivery. Applying a Debugger result is implementation, not diagnosis.
25
25
 
26
26
  1. Plan: draft before any implementation; settle scope/plan, ask if ambiguous,
27
27
  then await the gate.
28
- 2. Delegate: maximize useful fan-out—one suitable Worker/Heavy Worker per ready
29
- independent scope whose parallel gain exceeds coordination/merge cost; spawn
30
- all in one turn, with no count cap. Serialize only a real dependency,
31
- overlapping write, or inseparable coupling. Briefs follow the Lead brief
32
- contract. After async spawn, end the turn.
33
- 3. Review: after approval, complete delegation, review, self-verification, and
34
- in-scope fixes without reapproval. Every delegated implementation gets one
35
- Reviewer (all ready reviewers in one turn) and Lead integration/cross-scope
36
- verification in parallel. Reviewer independently judges risk, intent,
37
- boundaries; Lead checks acceptance/interactions, not duplicate same-scope
38
- work. High-risk scopes add distinct lenses. Synthesize one verdict; send
39
- merged fixes to the original live session; loop fix -> re-verify (same
40
- Reviewer + Lead re-check) until clean. Debugger first for requested debugging
41
- or a bug surviving 2+ fix cycles. Agent reports relay scope, verdict, next
42
- work as in-progress, never conclusions.
28
+ 2. Delegate: one agent per ready independent scope; spawn all ready scopes in
29
+ one turn, with no count cap. Serialize only a real dependency, overlapping
30
+ write, or inseparable coupling. Briefs follow the Lead brief contract.
31
+ After async spawn, end the turn.
32
+ 3. Review: review is exempt only for pure read/analysis with no edit, artifact,
33
+ or state mutation; git/configuration; or a request where the user explicitly
34
+ supplies both the exact target and exact replacement/output. Every
35
+ non-exempt mutation or artifact, whether produced or applied by Worker,
36
+ Heavy Worker, Debugger, or Lead, gets one Reviewer (all ready reviewers in
37
+ one turn) and Lead
38
+ integration/cross-scope verification in parallel. Debugger analysis cannot
39
+ substitute for implementation review: applying a Debugger result triggers
40
+ the same Reviewer + Lead verification. Reviewer independently judges risk,
41
+ intent, boundaries; Lead checks acceptance/interactions, not duplicate
42
+ same-scope work. High-risk scopes add distinct lenses. Synthesize one
43
+ verdict; send merged fixes to the original live session; loop fix ->
44
+ re-verify (same Reviewer + Lead re-check) until clean. Debugger first for
45
+ requested debugging or a bug surviving 2+ fix cycles. Exempt mutations still
46
+ require shell self-verification before report. Agent reports relay scope,
47
+ verdict, next work as in-progress, never conclusions.
43
48
  4. Report: final (not interim) report compares work to approved plan and gives
44
49
  verified result; never forward raw agent output. Ask about ship/deploy when
45
50
  relevant. Build/deploy/commit/push require an explicit user request after