Skip to content

fix(usage): reconcile empty-session stats contract after #9305 collision - #9319

Merged
bolichen97 merged 1 commit into
mainfrom
fix/heal-usage-empty-stats
Sep 7, 2026
Merged

fix(usage): reconcile empty-session stats contract after #9305 collision#9319
bolichen97 merged 1 commit into
mainfrom
fix/heal-usage-empty-stats

Conversation

@pepmach

@pepmach pepmach commented Sep 7, 2026

Copy link
Copy Markdown
Contributor

Heals broken main

main at 678fc32 is red on test/test_usage.py::TestParseSessions::test_empty_session_stats[False] and [True], both on the same assertion: the dict _parse_sessions() actually returns carries one key the expectation does not.

E         Left contains 1 more item:
E         {'refused_transcripts': 0}

Root cause: a semantic mid-air collision, 74 minutes apart

Two commits, no textual overlap, each green on its own merge ref:

commit PR merged what it did
f654ad072 #9254 13:49 PDT made refused_transcripts an unconditional field of the _parse_sessions() return dict, so the usage page can say the totals are incomplete instead of rendering a silent under-count on a Windows UNC home
5780471f3 #9305 15:04 PDT removed the {"error": "No sessions directory"} early return so a missing transcript directory yields complete zero period counts, and rewrote test_no_directory into an exact-equality test_empty_session_stats

#9305's exact-equality dict was authored against the pre-#9254 shape. Neither CI run ever saw both changes:

Classic both-green-in-isolation, red-together.

Which side is right

The source is right; the test expectation is the stale side.

A present zero is the contract #9254 deliberately established ("refused_transcripts": refused_transcripts is emitted on every return), and it is exactly what #9305 wants for an empty history: nothing was dropped, so the zeros are a fact rather than an under-count. Omitting the key would make "complete" and "unknown" indistinguishable on the wire — the dashboard adapter's refusedTranscripts: s.refused_transcripts ?? 0 (website/src/providers/adapters/acp.ts) would synthesise a completeness promise the payload never made, which is the precise failure #9254 exists to prevent.

So this adds the key to the expectation. It does not relax the assertion to a subset match, and it does not make the field conditional — either of those would re-open the silent-zero hole. The added comment records why the zero is present rather than absent, so the next author of this expectation does not read it as incidental.

Adjacent TestParseSessions cases already assert r["refused_transcripts"] == 0 on non-empty-but-nothing-refused paths, which independently confirms the field is expected present-and-zero rather than absent.

Tests

suite result
test/test_usage.py 95 passed (was 2 failed / 17 passed in TestParseSessions alone)
test_kiro_usage_api.py + test_acp_usage_cost.py + test_hooks_coverage.py 389 passed
test_security_posture.py + test_spawn_audit.py 65 passed
flake8 test/test_usage.py clean
scripts/check_black_formatting.py passed (file is in the 1,146-file black baseline; the explicit black --diff complaints are pre-existing file-wide with-statement restyle drift present on pristine origin/main, and none fall inside this diff's hunk)

No frontend file is touched, so the usage adapter and UsageTab.refusedTranscripts.test.tsx are unaffected by this diff.

Test-expectation change only: one key added to an assertion dict. No rendered surface changes — _parse_sessions() source, the /api/usage/kiro payload, and the usage page are all byte-identical to main.

Pattern harvest

This failure was invisible to both PRs' CI by construction: the mechanism that normally catches a collision (a textual conflict) had nothing to catch, because one PR changed a data shape's producer and the other changed that shape's pinning assertion, in disjoint hunks.

Rule candidate: a PR changing a data shape and its pinning test must be re-run against main tip immediately before merge when neighbors touch the same shape — semantic collisions pass both PRs' own CI.

Two corollaries worth keeping with it:

  • An exact-equality assertion on a payload dict is a shape lock, and any PR that adds a field to that payload is a modifier of every such lock in the repo — even the ones it does not textually touch. grep for the shape's other assertions when adding a field.
  • gh run rerun cannot substitute for this: it re-executes the run's original pinned merge ref, so it would have reproduced the same green. Only a fresh run (push, or close/reopen) sees main tip.

main is red on test_empty_session_stats[False] and [True]: the actual stats
dict carries one key the expectation does not, `refused_transcripts: 0`.

Two commits collided semantically, ~74 minutes apart, with no textual overlap:

- f654ad0 (#9254, 13:49) made `refused_transcripts` an UNCONDITIONAL field
  of the `_parse_sessions()` return dict, so the page can say the totals are
  incomplete instead of rendering a silent under-count on a UNC home.
- 5780471 (#9305, 15:04) removed the `{"error": "No sessions directory"}`
  early return so a missing transcript directory yields complete zero period
  counts, and rewrote `test_no_directory` into an exact-equality
  `test_empty_session_stats`.

#9305's exact-equality dict was authored against the pre-#9254 shape. Each PR
was green on its own merge ref: #9254 never touched the test #9305 rewrote, and
#9305's source hunk applied cleanly over #9254's, so neither CI run ever saw
both changes.

The SOURCE is right and the test expectation is the stale side. A present zero
is the contract #9254 established and exactly what #9305 wants for an empty
history: nothing was dropped, so the zeros are a fact. Omitting the key would
make "complete" and "unknown" indistinguishable on the wire -- the dashboard
adapter's `s.refused_transcripts ?? 0` would synthesise a completeness promise
the payload never made. So the fix adds the key to the expectation rather than
relaxing the assertion or making the field conditional.

Tests: test/test_usage.py 95 passed; test_kiro_usage_api.py +
test_acp_usage_cost.py + test_hooks_coverage.py 389 passed;
test_security_posture.py + test_spawn_audit.py 65 passed.
@pepmach
pepmach requested a review from a team as a code owner September 7, 2026 23:06
@github-actions github-actions Bot added the readiness: checking Automated validation is still running label Sep 7, 2026
@bolichen97

Copy link
Copy Markdown
Collaborator

The fix direction here is right, and it matches what I concluded independently while triaging the same red on #9214: the canonical return emits refused_transcripts unconditionally, 5780471f3 routed the missing-directory case into that same return on purpose ("use the same complete zero statistics as an existing, empty directory"), so the stale side is the expected dict — not the handler. Making the handler drop the key for a missing directory would reintroduce exactly the two-shape divergence #9254 removed.

One gap worth closing before this lands: the body names #9305 and #9254 but never #9317, and carries no closing keyword, so gh pr view 9319 --json closingIssuesReferences comes back empty. #9317 is the tracking issue actually filed for this failure (Backend Tests shard 4, both 3.12 and Windows), and on merge it will stay open with nothing left to report it. Adding Closes #9317 as a line of its own at the bottom fixes that; Related:/a bare #9317 render as links and close nothing.

For whoever is watching the blast radius: this failure reaches every open PR through refs/pull/<n>/merge, where Coverage Gate then fails closed and fails PR Readiness — four reds, one root cause. A rerun cannot clear it on those PRs, because a rerun replays the same commit; each one needs a rebase onto fixed main to rebuild its merge ref once this merges.

@github-actions

github-actions Bot commented Sep 7, 2026

Copy link
Copy Markdown
Contributor

GPT 5.6 Review — ✅ no blocking findings

GPT 5.6 completed its review of 3f46129398d6d8c1e17c6d67d03c583e5f8750ca and found no blocking issues.

This comment is updated in place on each push.

Review details

No findings.
[GPT-REVIEWED] 3f46129

False positive or not applicable? A repository writer can comment:
/ai-review override gpt 3f46129398d6d8c1e17c6d67d03c583e5f8750ca: <one-sentence reason>

@github-actions

github-actions Bot commented Sep 7, 2026

Copy link
Copy Markdown
Contributor

Design Review (Fable 5) — ✅ PASS

Design-level review of 3f46129398d6d8c1e17c6d67d03c583e5f8750ca — updated in place on each push. A BLOCK verdict blocks PR readiness; PASS/CONCERNS are advisory.

The diff is a 7-line test-expectation fix: adding "refused_transcripts": 0 to the exact-equality dict in test_empty_session_stats, with a comment explaining why the zero must be present. I verified the source at src/kiro_crew/dashboard/handlers/usage.py:1947 emits the key unconditionally on the single return path, and adjacent tests in the same class (lines 125, 145, 212, 233) already assert the key present-and-zero — so the expectation was the stale side, exactly as the description claims. The fix keeps the exact-equality shape lock rather than relaxing it to a subset match, which preserves the shape-pinning function of the test. No source, API, or frontend change; nothing undocumented in the diff.

Design-Verdict: PASS

Correct side of the collision fixed: expectation updated to the deliberate contract, shape lock kept exact, no semantics touched.

[DESIGN-REVIEWED] 3f46129

@github-actions

github-actions Bot commented Sep 7, 2026

Copy link
Copy Markdown
Contributor

Opus 4.8 Review — ✅ no blocking findings

Reviewed 3f46129398d6d8c1e17c6d67d03c583e5f8750ca — this comment is updated in place on each push.

Review details

No findings.

[OPUS-REVIEWED] 3f46129

Verdict parsed from the review's SHA-scoped output markers for commit 3f46129398d6d8c1e17c6d67d03c583e5f8750ca.

False positive or not applicable? A repository writer can comment:
/ai-review override fable 3f46129398d6d8c1e17c6d67d03c583e5f8750ca: <one-sentence reason>

@github-actions github-actions Bot added readiness: action required A blocking check or review needs attention and removed readiness: checking Automated validation is still running labels Sep 7, 2026
@bolichen97
bolichen97 enabled auto-merge (rebase) September 7, 2026 23:27
@github-actions github-actions Bot added readiness: checking Automated validation is still running readiness: passed Eligible automated validation passed for the current revision and removed readiness: action required A blocking check or review needs attention readiness: checking Automated validation is still running labels Sep 7, 2026
@bolichen97
bolichen97 merged commit 12ecd45 into main Sep 7, 2026
70 of 71 checks passed
@bolichen97
bolichen97 deleted the fix/heal-usage-empty-stats branch September 7, 2026 23:33
@github-actions github-actions Bot removed the readiness: passed Eligible automated validation passed for the current revision label Sep 7, 2026
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.

2 participants