diag(build): per-build GUID stamp + resolve/blit timing split (take-6 attribution failure)

Take 6 measured render 35-41ms — WORSE than take 5's 25.5 — and the run could
not be attributed to a binary: exe mtime != build contents (incremental builds
serve stale exes; a source edit without rebuild is a silent old binary). Three
takes of a perf saga had been judged against builds nobody could prove.

- ytLive.csproj GenerateBuildStamp target: fresh GUID per compile (writes
  obj/BuildStamp.g.cs -> Helpers/BuildStamp.Id/BuiltLocal). Deliberately defeats
  incremental lies: every 'dotnet build' recompiles the app project.
- Wordmark shows the id as a superscript (TopBar.xaml, x:Static, 9px grey
  BaselineAlignment=Superscript); startup.log records 'Build <id> (compiled
  <time>)' so every take is cross-readable with the visible UI.
- FramePump stats split the tick: 'avg render Xms (resolve Y), avg submit Z' —
  the resolver is timed separately (wrapper resolver on per-tick paths; bake
  keeps the raw one) so take 7 names the hot half of 'render' with data.
- Fixed a latent transition-clock bug found on the way: lastTick now restarts
  every frame (the branch rework had restarted it only during transitions,
  letting a transition begun after idle complete instantly on its first Tick).

ONE integration test family: BuildStampTests (unit: shape) +
BuildStampDisplayTests (RealApp, namescoped FindName on TopBar proves the
wordmark SHOWS the id). 47/47 per-class green, clean build 0 warnings. Docs
same commit. User's top-bar/session spec (re-sent twice) + settled Q&A
decisions folded into HANDOFF Unit B — next work unit after take 7 verdict.
This commit is contained in:
2026-09-04 11:26:46 -07:00
parent 1c48849853
commit 27bf74389d
8 changed files with 102 additions and 17 deletions
+18 -5
View File
@@ -44,11 +44,18 @@ RealApp boot-smoke. Scope-check passed.
raster on message/config change, blit the cached frame every tick (OBS text-source pattern).
Tests: `ChatOverlayLayerCacheTests` (the ONE, RealApp) + full regression green (62 across
touched classes), clean build 0 warnings.
2. **Take 6 (user, ~30s record-only):** expect `≈300/300 frames per 5s, avg render ≤ ~10ms`. If
chat-burst drops one frame per message (avg fine but `n/300` sags during live chat): debounced
off-tick re-render is the next slice. If steady at ~300 with render <10 → recording saga
CLOSED, go Unit B (below).
3. **Unit B — the top bar + session logic (user spec 2026-09-04, decisions settled):**
2. **BUILD STAMP shipped (slice 4, same day):** take 6 came back 35-41ms — WORSE than take 5 —
and attribution was impossible (exe timestamp ≠ binary contents; incremental builds served
unverified exes). Every build now stamps a fresh GUID (`ytLive.csproj GenerateBuildStamp` →
`Helpers/BuildStamp`), shown as the wordmark superscript (build id) + `Build xxxxxxxx (compiled
...)` in startup.log; stats report `avg render Xms (resolve Y)` so the hot half of the tick is
named. Tests `BuildStampTests` + `BuildStampDisplayTests` (real window, namescoped FindName).
3. **Take 7 (user, ~30s record-only):** FIRST read the superscript + startup.log Build line — the
take is meaningless without it. Expect slice 3 in any post-stamp build: `avg render ≤ ~10ms
(resolve ≪ render), ≈300/300`. If resolve dominates → resolver-side surprise (chat config thrash
/ web frame); if blit dominates → measure the general-path element sizes. If chat-burst sags
`n/300`: debounced off-tick re-render slice. If clean → recording saga CLOSED, Unit B.
4. **Unit B — the top bar + session logic (user spec 2026-09-04 re-sent twice + decisions settled in Q&A):**
- Two-line top bar. Line 1: center = REC + **LIVE** pills (text renamed from ON-AIR; pills become
mutually-exclusive RADIOS — record-OR-stream ruling), right = avatar + **Login/Logout** button
(no account status light). Line 2: centered primary **Start** (grayed while NO pill armed —
@@ -67,6 +74,12 @@ RealApp boot-smoke. Scope-check passed.
live is scary — confirmation allows back-out + testing up to go-live").
- Bottom-bar metrics init/maintain: `ResetHealth` + `HealthUpdated` exist — verify on take 6.
- F6 "start/end" hotkey routes through `HandleHotkey` — check it honors the new grayed-Start gate.
- Login button text: "Login" (disconnected, LIVE pill greyed) → "Logout" (connected, avatar
appears left of it). The account status LIGHT is deleted per spec 2a.
- Primary button: grayed "Start" when NO pill armed (inverts 2026-09-01 rule — fix the map in
the landing commit); enabled when either armed; REC path = file dialog flow; LIVE path =
go-live after dialog confirm; becomes the Stop/End face while active (user's "Stop button
never active" complaint gets a hermetic test pinning visibility+CanExecute).
- ONE integration test (hermetic): pills↔button state machine + record-path seam (an
`internal static Func<SaveFileDialog-ish prompt>` override seam mirroring `RegistrarOverride`
— never pop real dialogs in tests).