video-evidence¶
Provenance¶
- Source:
.spec/spexcode/spec-eval/video-evidence/spec.md - Source SHA-256:
c88ee1c34b87ccf5d0877cabc66f23f3aca0e5d2ca42849d59c695ae8aa9162a
A eval reading's evidence is a list of content-addressed entries, each typed by its kind ([[eval-core]],
[[evidence-kind-taxonomy]]) — image | transcript | video | data. A video is a screenshot with a time
axis: the same primitive, one more kind of entry. For a scenario whose loss is a temporal user loop (a UI surface), a recording of the loop is
the truest evidence — the author's choice per scenario, routed by its tag, not a forced default — and it can
ride in the same reading as N stills of the same run.
When to record is taught, not left to taste — a capability nobody is told to use decays into a dead
enum (every re-measure quietly files an --image, and the video pipeline starves). The routing rule is
one sentence, stated where every measuring agent actually reads: behaviour that MOVES or is timed
(terminal scroll/redraw, an animation or transition, media playback, a multi-step interaction flow,
keyboard timing) records a --video — a still of a moving thing proves the wrong thing; a static end
state (layout, an icon, copy, one frame) screenshots --image; backend/CLI files a --result
transcript. The rule lives on three surfaces that must agree: spex guide eval's MEASURING AND FILING
section (the manual, with the full flag row — repeatable --image, --result, --video [--timeline]),
spex help eval's usage block (the map), and the .plugins/core system prompt's eval paragraph (the
always-on clue every dispatched or self-launched agent carries). Guide and help must show --video in
the eval usage they print — a manual that only teaches --image is how the gap happened.
The whole point is that almost nothing is new. spex eval add --video <clip> stores the bytes in the same
shared cache and pushes one video entry onto the reading's evidence list (spex blob put is the same
transport WITHOUT a reading, [[evidence-put]]); the MIME is sniffed from content
(WebM / MP4) so /api/evidence streams a playable type — and answers byte ranges, without which a browser
clamps every seek to 0. The endpoint also accepts an ignored trailing .<ext> on the hash
(/api/evidence/<hash>.webm): third-party markdown renderers (GitLab, GitHub) decide image-vs-video by the
URL's extension and sanitize raw <video> HTML away, so a suffix is the only way an MR note embeds a
playable clip — the suffix is pure decoration, stripped before lookup, and never influences the served
bytes or MIME (a wrong suffix still serves the true content); every dashboard home renders the <video> inline through the ONE shared evidence
renderer ([[event-detail]]'s Evidence.jsx — the eval tab [[eval-tab]], the session proof
([[session-eval]]), and an issue/eval thread's blob links alike), lazy on
expand, with the same miss original file when the blob is pruned; spex eval ls labels it. A clip is
heavier bytes, so [[eval-core]]'s clean (which walks every evidence entry) is the intended prune.
An optional refinement — anchoring named steps to positions on the evidence so an annotation can land on a
step — is [[step-timeline]], a separate format built only when a real annotation workflow needs it. It
OUTGREW this node: a step-map anchors to the evidence's own axis, and a video is merely its time-axis
instance — the same map rides a transcript (line) or a still sequence (frame) just as well, so
step-timeline is modality-neutral and no longer welded to the clip (a video step-map's legacy { tMs } shape
still reads losslessly as the time axis). eval still runs nothing: it records a clip something else
recorded, and the measuring hand stays a metadata tag.
A human who disagrees with this node's verdict simply files their own manual@1 reading — the existing
supersede-by-a-newer-reading path, not a new lifecycle. A finding that is not this node's clean fail — a
cross-cutting problem, or one belonging to another node — is instead a concern raised on the
responsible node (a local or forge issue through the unified Issue port — [[local-issues]]'s one Issue
type, whose typed evidence[] carries the hash), pointing at the
clip by its evidence hash — and the thread PLAYS that clip inline through the same shared renderer, so the
concern's evidence is watchable where the concern is read; not a hedged verdict here. So video keeps eval's
verdict binary and routes the "needs another look" elsewhere it belongs.