Skip to content

fix(#562): align merge_success counter denominator with segment-level numerator#595

Merged
HenryLach merged 1 commit into
mainfrom
fix/562-wave-merge-counter-denominator
May 25, 2026
Merged

fix(#562): align merge_success counter denominator with segment-level numerator#595
HenryLach merged 1 commit into
mainfrom
fix/562-wave-merge-counter-denominator

Conversation

@HenryLach

Copy link
Copy Markdown
Owner

Closes #562.

The bug

In a polyrepo batch where task-level waves expand into segment-level merge rounds (3 task waves → 6 segment rounds in the reporter's case), the operator-visible merge alerts overflowed their denominator:

✅ Wave 1 merged successfully (1/3). Tests pass.
✅ Wave 2 merged successfully (2/3). Tests pass.
✅ Wave 3 merged successfully (3/3). Tests pass.
✅ Wave 4 merged successfully (4/3). Tests pass.   ← stale denominator
✅ Wave 5 merged successfully (5/3). Tests pass.
✅ Wave 6 merged successfully (6/3). Tests pass.

Purely cosmetic — every wave merged successfully. Functionality unchanged.

Root cause

The supervisor's formatEventNotification renders Wave N merged successfully (X/Y) where:

  • N and X both derive from event.waveIndex + 1 (set by the engine to the segment-round index 0..5)
  • Y came from event.totalWaves which the engine was populating with taskLevelWaveCount (the pre-expansion task-wave count = 3)

Once segments expanded, N/X overflowed Y. The fix is to source the denominator from batchState.totalWaves (assigned rawWaves.length at engine.ts:2767 — the segment-expanded round count) instead.

The fix

One-field source-of-truth swap at the merge_success event emission site in engine.ts:

- totalWaves: taskLevelWaveCount,
+ totalWaves: batchState.totalWaves,

Plus an inline comment explaining the segment/task-wave distinction so a future reader doesn't reintroduce the bug.

Test

Adds 5.8a to supervisor.test.ts: emits 6 successive merge_success events with waveIndex 0..5 against totalWaves: 6 and asserts:

  1. Each rendered counter reads (waveIdx+1)/6.
  2. None of the rendered counters contain the buggy /3 pattern from the report.

Validation

Gate Result
npm run typecheck ✅ pass
npm run lint ✅ 286 warnings / 671 infos — identical to main (zero new)
npm run format:check ✅ pass
supervisor.test.ts (114 tests) ✅ all pass, new 5.8a included

Note on the design choice

The reporter offered two options: (a) flip the denominator to segment-level (what this PR does), or (b) reword the alert to disambiguate units. I went with (a) because:

  1. The numerator was already segment-level — this just aligns the denominator. Minimal surface area, no message rewording.
  2. The supervisor's existing leading Wave N is also segment-level (uses waveIndex + 1), so changing the denominator gives the whole message a consistent unit system without touching any other emission sites.
  3. The reporter explicitly endorsed this as the preferred fix in the issue body ("one-character source-of-truth swap").

… numerator

The merge_success event emission was pairing a segment-round numerator
(waveIndex = 0..N-1 of the segment-expanded merge rounds) with a task-
level denominator (taskLevelWaveCount, the pre-expansion wave count).
In a clean polyrepo run with 3 task-level waves expanded into 6
segment-level rounds, the operator-visible alert overflowed:

  Wave 1 merged successfully (1/3). Tests pass.
  Wave 2 merged successfully (2/3). Tests pass.
  Wave 3 merged successfully (3/3). Tests pass.
  Wave 4 merged successfully (4/3). Tests pass.   <- denominator stale
  Wave 5 merged successfully (5/3). Tests pass.
  Wave 6 merged successfully (6/3). Tests pass.

The supervisor formatter renders 'Wave N merged successfully (X/Y)'
where N and X both derive from event.waveIndex + 1, but Y comes from
event.totalWaves. Both N/X were already segment-level, so the
denominator needs to be segment-level too — that's batchState.totalWaves
(set to rawWaves.length, the segment-expanded round count) and NOT
batchState.taskLevelWaveCount (the pre-expansion count). One-field
source-of-truth swap at the engine.ts emission site, fully described
inline as a comment so the segment/task-wave distinction is preserved
for future readers.

Functional behavior was unaffected — every wave merged successfully —
this is purely a cosmetic display fix. Reporter's preferred fix (per
issue body) and the same approach taken: keep both numerator and
denominator in segment-round units.

Adds a regression test (5.8a) that emits 6 successive merge_success
events with waveIndex 0..5 against a denominator of 6 and asserts:

  1. Each rendered counter reads (waveIdx+1)/6.
  2. None of the rendered counters contain the buggy '/3' denominator
     pattern, which was the visual signature in the original report.

Validation:
  npm run typecheck      pass
  npm run lint           no new warnings (286/671 identical to main)
  npm run format:check   pass
  supervisor.test.ts     114/114 pass (new 5.8a included)

Closes #562
@HenryLach HenryLach enabled auto-merge May 25, 2026 18:26
@HenryLach HenryLach merged commit 8786266 into main May 25, 2026
1 check passed
@HenryLach HenryLach deleted the fix/562-wave-merge-counter-denominator branch May 25, 2026 18:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Wave-merge alert counter overflows denominator: '(N/3)' for N up to totalWaves

1 participant