Work type
Bug / regression
Observed behavior
On PR #1033, GitHub Actions macOS live-harnessd run 30551192571 rendered the real prompt/reply and completed the run, but RunSessionLiveTests.submitProducesTranscript observed session.transcript.usage.totalTokens == 0. A focused fresh-harness rerun held the combined completed/nonzero-usage condition for 20 seconds and still timed out, proving this is not a short assertion race.
RunSession.streamConversation fetches durable messages at every terminal event and calls Transcript.reconcile. That method calls load, which constructs a fresh Transcript and therefore clears usage already applied from usage.delta. The duplicate per-run event is then rejected by seenEventIDs, so usage never returns.
Expected behavior
Durable message reconciliation may replace persisted transcript rows, but it must preserve already-observed authoritative run metadata that messages do not contain, including cumulative usage. Completed callback/cron/user runs must retain nonzero accounting after terminal reconciliation and replay.
Reproduction
Preconditions: macOS, key-free harness from macapp/scripts/live-harnessd.sh.
Command: start the live harness, then run HARNESS_TEST_BASE_URL=http://127.0.0.1:8899 swift test --package-path macapp --filter RunSessionLiveTests.
CI evidence: run 30551192571, job 90899990017.
Focused local red: run completed, the combined completion/usage wait timed out after 20 seconds, and the final assertion observed zero tokens.
Reproduction rate: deterministic with the current terminal reconciliation path.
User and operational impact
Severity P1: native users lose the token/cost status after every terminal message reconciliation, and the red live gate blocks the cron/callback GUI merge chain. Transcript content survives; no persistent-data or security impact.
Suspected seam and search evidence
Owning seam: Transcript.reconcile(messages:) in macapp/Sources/HarnessKit/Transcript.swift, called by RunSession.reconcilePersistedMessages after conversation-wide terminal events.
Searched both RunSession SSE tasks, event-ID dedupe, Transcript.applyUsage, harness usage.delta, live fake turns, durable message payloads, and all reconcile callers/tests. Every fake turn has nonzero usage. Durable messages do not carry usage. load resets the whole value, and the subsequent duplicate usage event is intentionally deduped.
Blast-radius impact map
- Callers/data flow: harness usage.delta -> Transcript usage -> terminal conversation event -> durable messages -> reconcile/load.
- Config/env/defaults: None.
- API/CLI/wire formats/tools: no change.
- Persistence/schema/cache: durable messages unchanged; they remain row source only.
- Concurrency/lifecycle: preserve metadata across terminal reconciliation from two SSE streams.
- Security/auth/privacy: None.
- TUI/web: None.
- macOS: usage/cost label and environment inspector retain authoritative values.
- Provider/model/catalog: no routing change; all provider accounting benefits.
- Deployment/observability: GitHub macOS live gate.
- Compatibility: additive state preservation, no wire/schema change.
- Existing tests/fixtures: Transcript reconcile unit tests, conversation-stream integration, RunSession live suite.
- Documentation: engineering log, plan, impact map, indexes.
Regression test first
Test file/name: RunSessionConversationStreamTests.completedReplayPreservesObservedUsage (or the smallest equivalent Transcript reconcile unit).
Red fixture: deliver usage.delta with nonzero cumulative usage, then run.completed; stub durable messages and let terminal reconciliation run.
Expected failure: totalTokens resets from the scripted value to zero.
Why it proves the bug: it drives the exact conversation-wide terminal -> messages -> reconcile path used by GUI callback/cron/user runs.
False-positive controls: assert durable rows still deduplicate, completion remains terminal, usage value is exact, and historical load for a newly opened conversation still starts from zero.
Fix boundaries
In scope: snapshot usage before Transcript.load inside reconciliation and restore it afterward; add exact conversation-stream regression and retain the live combined postcondition.
Out of scope: persisting usage in message rows, serializing SSE streams, changing event IDs/dedupe, changing server accounting, sleeps, or provider scripts.
Existing abstraction: repair Transcript.reconcile; no parallel state owner.
Diagnostic and observability evidence
Preserve the exact CI/local red logs and exact expected usage totals in the regression. The live test remains bounded and fails if accounting is still absent.
Verification plan
- Deterministic conversation-stream red/green test.
- Transcript reconciliation and dual-stream focused suites.
- Focused live suite against a fresh harnessd, repeated.
- Complete Swift suite and strict format.
- Full
./scripts/test-regression.sh normal/race/coverage.
- Re-run macOS CI live job.
Rollout and rollback
Native state-only repair. Land before rebasing #1033. Roll back if reconciliation leaks usage across conversation changes; load/reset controls must remain zero. No data repair.
Documentation and handoff
Update docs/logs/engineering-log.md, docs/logs/long-term-thinking-log.md, docs/plans/INDEX.md, plan, and impact map. Related epic: #1000.
Definition of done
Process acknowledgement
I will preserve the failing regression test, update the issue if the root cause changes, and record any newly discovered bug as its own issue.
Work type
Bug / regression
Observed behavior
On PR #1033, GitHub Actions macOS
live-harnessdrun 30551192571 rendered the real prompt/reply and completed the run, butRunSessionLiveTests.submitProducesTranscriptobservedsession.transcript.usage.totalTokens == 0. A focused fresh-harness rerun held the combined completed/nonzero-usage condition for 20 seconds and still timed out, proving this is not a short assertion race.RunSession.streamConversationfetches durable messages at every terminal event and callsTranscript.reconcile. That method callsload, which constructs a freshTranscriptand therefore clears usage already applied fromusage.delta. The duplicate per-run event is then rejected byseenEventIDs, so usage never returns.Expected behavior
Durable message reconciliation may replace persisted transcript rows, but it must preserve already-observed authoritative run metadata that messages do not contain, including cumulative usage. Completed callback/cron/user runs must retain nonzero accounting after terminal reconciliation and replay.
Reproduction
Preconditions: macOS, key-free harness from
macapp/scripts/live-harnessd.sh.Command: start the live harness, then run
HARNESS_TEST_BASE_URL=http://127.0.0.1:8899 swift test --package-path macapp --filter RunSessionLiveTests.CI evidence: run 30551192571, job 90899990017.
Focused local red: run completed, the combined completion/usage wait timed out after 20 seconds, and the final assertion observed zero tokens.
Reproduction rate: deterministic with the current terminal reconciliation path.
User and operational impact
Severity P1: native users lose the token/cost status after every terminal message reconciliation, and the red live gate blocks the cron/callback GUI merge chain. Transcript content survives; no persistent-data or security impact.
Suspected seam and search evidence
Owning seam:
Transcript.reconcile(messages:)inmacapp/Sources/HarnessKit/Transcript.swift, called byRunSession.reconcilePersistedMessagesafter conversation-wide terminal events.Searched both RunSession SSE tasks, event-ID dedupe,
Transcript.applyUsage, harnessusage.delta, live fake turns, durable message payloads, and allreconcilecallers/tests. Every fake turn has nonzero usage. Durable messages do not carry usage.loadresets the whole value, and the subsequent duplicate usage event is intentionally deduped.Blast-radius impact map
Regression test first
Test file/name:
RunSessionConversationStreamTests.completedReplayPreservesObservedUsage(or the smallest equivalent Transcript reconcile unit).Red fixture: deliver
usage.deltawith nonzero cumulative usage, thenrun.completed; stub durable messages and let terminal reconciliation run.Expected failure: totalTokens resets from the scripted value to zero.
Why it proves the bug: it drives the exact conversation-wide terminal -> messages -> reconcile path used by GUI callback/cron/user runs.
False-positive controls: assert durable rows still deduplicate, completion remains terminal, usage value is exact, and historical
loadfor a newly opened conversation still starts from zero.Fix boundaries
In scope: snapshot usage before
Transcript.loadinside reconciliation and restore it afterward; add exact conversation-stream regression and retain the live combined postcondition.Out of scope: persisting usage in message rows, serializing SSE streams, changing event IDs/dedupe, changing server accounting, sleeps, or provider scripts.
Existing abstraction: repair
Transcript.reconcile; no parallel state owner.Diagnostic and observability evidence
Preserve the exact CI/local red logs and exact expected usage totals in the regression. The live test remains bounded and fails if accounting is still absent.
Verification plan
./scripts/test-regression.shnormal/race/coverage.Rollout and rollback
Native state-only repair. Land before rebasing #1033. Roll back if reconciliation leaks usage across conversation changes;
load/reset controls must remain zero. No data repair.Documentation and handoff
Update
docs/logs/engineering-log.md,docs/logs/long-term-thinking-log.md,docs/plans/INDEX.md, plan, and impact map. Related epic: #1000.Definition of done
Process acknowledgement
I will preserve the failing regression test, update the issue if the root cause changes, and record any newly discovered bug as its own issue.