Skip to content

fix(hud): stop showing the kill count twice on the commit summary - #165

Merged
mcdope merged 3 commits into
masterfrom
fix/commit-summary-duplicate-kills
Aug 27, 2026
Merged

mcdope merged 3 commits into
masterfrom
fix/commit-summary-duplicate-kills

Conversation

@mcdope

@mcdope mcdope commented Aug 26, 2026 •

Copy link
Copy Markdown
Owner

Spotted while playtesting: the COMMIT SUMMARY screen shows the same number twice, one line apart.

Row Source
Bugs squashed main.ts → bugsSquashed: stats.kills
Kills statRows → levelPlayerStats.kills

Both resolve to local.kills for the level just cleared — buildPlayerFacingStats is handed the same variable that EngineStats.kills carries. Not two related figures, literally one value rendered twice.

Why it survived

"Bugs squashed" is the older row. The summary used to be exactly two lines — Lines refactored and Bugs squashed — which is what EngineStats.kills' doc comment still described ("bugs squashed for the commit summary", now corrected). beta-9 switched the full stats block back on, and it brought its own generic Kills row with it.

The two rows are built in different files and only meet at runtime, so nothing in review or in the type system put them next to each other. It took playing a level.

The change

Keep Kills, drop Bugs squashed.

The duplication is why it was spotted; the name is why Kills won.

This game reads somebody's real source code. "Bugs squashed: 47" tells them their file contained 47 defects. It did not — it contained enemies derived from cyclomatic complexity, and complexity is not defects. A file can be gnarly and correct, or trivial and broken. A tool pointed at your codebase should not assert a defect count it has no basis for; Kills describes what happened and claims nothing about the code.

A second, smaller argument points the same way: the game has a literal Bug. placeTodoEncounter spawns one beside a TODO/FIXME terminal, and it is rare — on the demo campaign the tech-debt mix is trap 4 / mine 3 / Bug 1 across all 17 levels. So the row named the one entity that genuinely is a bug, while counting everything except it.

Consistency is a bonus rather than the reason: statRows is shared with both run-end screens, where the flavour name never appeared, so all three screens now call the row the same thing.

CommitSummaryInfo.bugsSquashed goes with it; the single production caller was the only thing supplying it. stats stays optional — nothing omits it today, but removing that affordance is wider than this needs. Say the word if you'd rather it were required.

On the test

It asserts the count, not just the absence of the old label:

expect(texts).not.toContain("Bugs squashed");
expect(texts.filter((t) => t === "Kills")).toHaveLength(1);

so re-introducing a duplicate under any other name still fails it.

I verified it actually catches the bug rather than trusting a green run — reinstated the row, watched the test go red, removed it again, watched it pass. A regression test that passes before and after is worth nothing.

Verification

  • npm run build — clean, bundle hygiene clean
  • npx vitest run --dir src — 3,683 passing (up 1: the new regression test), 135 files
  • Coverage unchanged: 99.9 / 99.52 / 99.84 / 99.99, all four gates clear

🤖 Generated with Claude Code

https://claude.ai/code/session_017ncJfux8GDacSTeLDcqrhr

mcdope and others added 3 commits August 27, 2026 01:16
The COMMIT SUMMARY screen rendered the same number in two adjacent rows under
two names: "Bugs squashed" and, one line below it, the stats block's "Kills".
Both resolve to `local.kills` for the level just cleared —
`main.ts` passed `bugsSquashed: stats.kills`, and `statRows`' "Kills" reads
`levelPlayerStats.kills`, which `buildPlayerFacingStats` is handed from the
same variable.

"Bugs squashed" is the older of the two: the summary was once just Lines
refactored and Bugs squashed, which is what `EngineStats.kills`' doc comment
still described. beta-9 switched the full stats block back on and it brought
its own generic Kills row along, so the two ended up side by side. Neither
author saw it, because the rows are built in different files and the screen is
only assembled at runtime — it took playing a level to notice.

Keeping "Kills" and dropping "Bugs squashed", per the request. That loses a bit
of flavour on a screen called COMMIT SUMMARY, but "Kills" is the one that
generalises: `statRows` is shared with both run-end screens, where the flavour
name never appeared, so this also makes the three screens agree on what the row
is called.

`CommitSummaryInfo.bugsSquashed` goes with it — the single production caller was
the only thing supplying it. `stats` stays optional; nothing in the app omits it
today, but removing the affordance is a wider change than this needs.

The regression test asserts the *count*, not just the absence of the old label:
`texts.filter((t) => t === "Kills")` must be exactly 1, so re-introducing the
duplicate under any other name still fails it. Verified it fails on the bug by
reinstating the row and watching it go red, rather than trusting a green run.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The previous commit justified dropping "Bugs squashed" as losing "a bit of
flavour". That undersold it, per the user: the flavour was making a claim.

An enemy is a *function* — that is the whole code-to-level mapping — so killing
one is not fixing a bug, and a row called "Bugs squashed" asserted a defect
where there need not be one. The game also has a literal `Bug`:
`placeTodoEncounter` spawns one beside a TODO/FIXME terminal, and it is rare —
measured on the demo campaign the tech-debt mix is trap 4 / mine 3 / Bug 1
across all 17 levels. So the row named the one entity in the game that really
is a bug, while counting everything except it.

Comment only; no behaviour change. Kept as a followup rather than an amend
since the branch is already pushed.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…er's code

Correcting my own previous commit, which led with an in-fiction argument (an
enemy is a function, so killing one is not fixing a bug). That was the lesser
half. The user's actual objection is about the *analysed source*, not the
fiction:

This game reads somebody's real codebase. "Bugs squashed: 47" tells them their
file contained 47 defects. It did not. It contained enemies derived from
cyclomatic complexity, and complexity is not defects — a file can be gnarly and
correct, or trivial and broken. A tool that points at your code should not
assert a defect count it has no basis for, and "Kills" describes what happened
without claiming anything about the code.

The literal-`Bug` observation stays, demoted to what it is: a second, smaller
argument pointing the same way.

Comment only; no behaviour change.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@mcdope
mcdope merged commit d439797 into master Aug 27, 2026
22 checks passed
@mcdope
mcdope deleted the fix/commit-summary-duplicate-kills branch August 27, 2026 00:04
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.

1 participant