Context
Filed during take-15's aftermath (2026-07-25 late): the operator's v0.5 verdict needed a flicker count, and no instrument existed to produce one. A hand-rolled frame-delta census (downsample + mean-abs frame diff per consecutive pair, spike pairs = events) found ~7 flash events with timestamps in ~2 minutes — but it lived in a one-off shell heredoc, and #15927's isolation matrix needs it as a repeatable measurement layer: run a candidate fix, census the boot/repro, get a number, not an eyeball.
The defect itself is not this ticket — #15927 (scene-1 presented-frame continuity, @neo-gpt-emmy) owns it. This ticket is the instrument the matrix consumes, and the take-16 acceptance gate will consume it again.
The Problem
One-frame presentation blanks are invisible to the specs that guard the film path (assertions hold worker truth, never camera truth — the take-15 review established this), and they are invisible to 1fps sampling (my first handback claimed "one flash" from a sample that structurally cannot count one-frame events). The measurement that works is position-independent frame-delta analysis: a flash is a boundary, and boundaries show up as mean-abs-diff spikes regardless of where on screen they happen — no window geometry, no color keying (screencapture's pipeline softens saturated colors, so color masks miss; delta energy does not).
The Architectural Reality
- The probe core is a pure video-file analyzer: ffmpeg rawvideo gray frames → per-pair mean-abs delta → spike list → event pairing, with the median-relative threshold tonight's census used (
> max(25, median×6)).
- Two consumption shapes named at creation; the engine is shared: (a) standalone script (take-16 acceptance on any
.mov/.webm), (b) spec-util the #15927 isolation matrix calls per boot. Shape finalized with @neo-gpt-emmy (the matrix owner) before implementation; the two-layer shape (script as engine, util as wrapper) is the default unless she rules otherwise.
- Precedent for placement:
test/playwright/util/ (RmaHelpers.mjs — harness utilities) for the spec-side wrapper; the standalone form as a sibling or a test/playwright/util/ export with a CLI shim. No ai/ surface.
- Grain proven tonight: whole-frame at 53.8fps (one-frame events). Whether half-frame exposure flickers matter is @neo-gpt-emmy's call (question on record).
The Fix
- The probe engine with its threshold contract documented (median-relative, not absolute — absolute thresholds lie across content brightness).
- A spec pinning it against a known-blank fixture (synthesized: N clean frames + K injected black frames → exactly the injected events detected) and a clean control (a motion-heavy clip with no flashes → zero events).
- The wrapper the matrix consumes, per the shape call.
Contract Ledger
| Surface |
Owner |
Consumers |
Contract |
detectFlickerFrames({frames, width, height, fps, times?, absFloor?, relFactor?, pairGapSec?, pairGapMax?}) |
test/playwright/util/flickerCensus.mjs |
censusRawGrayFile · censusVideoFlicker · the #15927 isolation matrix (direct import) |
Pure detector over decoded consecutive gray frames. With times (source PTS), timestamps + the ~0.1s fusion window run in elapsed source time; without it, index/fps (CFR-only). Returns events (fusion view) + the COMPLETE spikes list (frame, sec, delta) — fusion never hides a spike. |
censusRawGrayFile({filePath, width, height, fps, times?}) |
same |
the unit suite |
Raw-gray disk adapter; no ffmpeg anywhere in the path — the CI-owned adapter contract. |
censusVideoFlicker({videoPath, scale?}) |
same |
CLI, take-16 acceptance, local receipts |
ffmpeg adapter: -fps_mode passthrough (never synthesized cadence) + showinfo PTS parse (source time, never one average). Detector throws on PTS/frame-count mismatch — loud, never a silent fps fallback. |
CLI node test/playwright/util/flickerCensus.mjs <video> [--scale WxH] |
same |
operators, the roll script |
Prints every spike (sec, frame, delta) and the fused events. --scale parsed and honored. |
Acceptance Criteria
Out of Scope
- The defect itself (#15927) and any fix for it — this ticket measures, it does not repair.
- The vessel tokens-vs-skin gap (#15945, Grace's lane from the same verdict).
- The take-16 roll-script sticky-hide (banked with Fable).
Avoided Traps
- fps-sampling as measurement — catches a flash by luck, never counts it; the false precision is worse than no number.
- Color-keyed detection — capture pipelines soften saturated UI accents (the film cursor's 255,90,0 reads as ~200-230 red on screencapture); delta energy has no color to soften.
- Absolute delta thresholds — a dark-themed app and a bright desktop live in different energy classes; the threshold must derive from the clip's own median.
Decision Record impact
none — test-harness measurement tooling; no runtime surface.
Related
- #15927 (the matrix this serves) · #15252 (the film) · #15945 (the vessel-parity sibling from the same verdict) · take-15 receipts (session a76464c2)
Live latest-open sweep: checked latest 20 open issues at 2026-07-25T23:00Z; KB semantic + local content grep found no equivalent (flicker-adjacent tickets #15137/#15136/#14653/#14660 are different surfaces). A2A in-flight sweep: no claim on a measurement probe; #15927's defect lane is Emmy's and untouched by this.
Origin Session ID: a76464c2-c4b7-48b4-a2cc-30ae42ab3dd0
Retrieval Hint: query_raw_memories("flicker census frame-delta take-15 flash events measurement")
Context
Filed during take-15's aftermath (2026-07-25 late): the operator's v0.5 verdict needed a flicker count, and no instrument existed to produce one. A hand-rolled frame-delta census (downsample + mean-abs frame diff per consecutive pair, spike pairs = events) found ~7 flash events with timestamps in ~2 minutes — but it lived in a one-off shell heredoc, and #15927's isolation matrix needs it as a repeatable measurement layer: run a candidate fix, census the boot/repro, get a number, not an eyeball.
The defect itself is not this ticket —
#15927(scene-1 presented-frame continuity, @neo-gpt-emmy) owns it. This ticket is the instrument the matrix consumes, and the take-16 acceptance gate will consume it again.The Problem
One-frame presentation blanks are invisible to the specs that guard the film path (assertions hold worker truth, never camera truth — the take-15 review established this), and they are invisible to 1fps sampling (my first handback claimed "one flash" from a sample that structurally cannot count one-frame events). The measurement that works is position-independent frame-delta analysis: a flash is a boundary, and boundaries show up as mean-abs-diff spikes regardless of where on screen they happen — no window geometry, no color keying (screencapture's pipeline softens saturated colors, so color masks miss; delta energy does not).
The Architectural Reality
> max(25, median×6))..mov/.webm), (b) spec-util the #15927 isolation matrix calls per boot. Shape finalized with @neo-gpt-emmy (the matrix owner) before implementation; the two-layer shape (script as engine, util as wrapper) is the default unless she rules otherwise.test/playwright/util/(RmaHelpers.mjs — harness utilities) for the spec-side wrapper; the standalone form as a sibling or atest/playwright/util/export with a CLI shim. Noai/surface.The Fix
Contract Ledger
detectFlickerFrames({frames, width, height, fps, times?, absFloor?, relFactor?, pairGapSec?, pairGapMax?})test/playwright/util/flickerCensus.mjscensusRawGrayFile·censusVideoFlicker· the #15927 isolation matrix (direct import)times(source PTS), timestamps + the ~0.1s fusion window run in elapsed source time; without it, index/fps (CFR-only). Returnsevents(fusion view) + the COMPLETEspikeslist (frame, sec, delta) — fusion never hides a spike.censusRawGrayFile({filePath, width, height, fps, times?})censusVideoFlicker({videoPath, scale?})-fps_mode passthrough(never synthesized cadence) +showinfoPTS parse (source time, never one average). Detector throws on PTS/frame-count mismatch — loud, never a silent fps fallback.node test/playwright/util/flickerCensus.mjs <video> [--scale WxH]--scaleparsed and honored.Acceptance Criteria
Out of Scope
Avoided Traps
Decision Record impact
none — test-harness measurement tooling; no runtime surface.
Related
Live latest-open sweep: checked latest 20 open issues at 2026-07-25T23:00Z; KB semantic + local content grep found no equivalent (flicker-adjacent tickets #15137/#15136/#14653/#14660 are different surfaces). A2A in-flight sweep: no claim on a measurement probe; #15927's defect lane is Emmy's and untouched by this.
Origin Session ID: a76464c2-c4b7-48b4-a2cc-30ae42ab3dd0 Retrieval Hint:
query_raw_memories("flicker census frame-delta take-15 flash events measurement")