diff --git a/extensions/taskplane/engine.ts b/extensions/taskplane/engine.ts index 43db3812..c54252cb 100644 --- a/extensions/taskplane/engine.ts +++ b/extensions/taskplane/engine.ts @@ -4225,14 +4225,23 @@ export async function executeOrchBatch( "info", ); - // TP-040: Emit merge_success event + // TP-040: Emit merge_success event. + // + // `waveIndex` is the segment-round index (0-based), and the supervisor + // formatter renders the (N/M) counter using `waveIndex + 1` for N. + // For unit consistency we therefore pair it with the segment-level + // `batchState.totalWaves` (segment-expanded round count), not + // `taskLevelWaveCount` (pre-expansion). Using the task-level count as + // the denominator while the numerator counts segment rounds produced + // `(4/3)`, `(5/3)`, `(6/3)` style overflow once segments expanded — + // see issue #562. emitEvent( stateRoot, { ...buildEngineEventBase("merge_success", batchState.batchId, waveIdx, batchState.phase), laneCount: mergedCount, durationMs: mergeResult.totalDurationMs, - totalWaves: taskLevelWaveCount, + totalWaves: batchState.totalWaves, }, onEngineEvent, ); diff --git a/extensions/tests/supervisor.test.ts b/extensions/tests/supervisor.test.ts index d00ce6c3..701328d6 100644 --- a/extensions/tests/supervisor.test.ts +++ b/extensions/tests/supervisor.test.ts @@ -924,6 +924,33 @@ describe("5.x — formatEventNotification", () => { expect(text).toContain("42"); }); + it("5.8a: merge_success counter denominator stays >= numerator across segment-expanded waves (regression for #562)", () => { + // Polyrepo scenario from #562: 3 task-level waves expand into 6 + // segment-level merge rounds. The engine emits one merge_success + // per segment round, with waveIndex = segment-round index (0..5) + // and totalWaves = segment-expanded round count (6). The formatter + // must therefore render (1/6) through (6/6), never (4/3), (5/3), + // or (6/3) as observed in the bug report. + const denominator = 6; + for (let waveIdx = 0; waveIdx < denominator; waveIdx++) { + const event = { + timestamp: "t", + type: "merge_success" as any, + batchId: "b", + waveIndex: waveIdx, + totalWaves: denominator, + }; + const text = formatEventNotification(event, "supervised"); + const expectedNumerator = waveIdx + 1; + expect(text).toContain(`(${expectedNumerator}/${denominator})`); + // Belt-and-suspenders: the bug surfaced as a numerator that + // exceeded the denominator. Pin that the rendered text never + // contains `(N/3)` for any N — the only valid denominator in + // the segment-expanded case is the segment count. + expect(text).not.toMatch(/\(\d+\/3\)/); + } + }); + it("5.9: formats merge_failed differently for autonomous vs interactive", () => { const event = { timestamp: "t",