Skip to content

fix: one click should mark your child for the game, and the ball should be findable - #46

Merged
ralyodio merged 3 commits into
masterfrom
fix/coverage-and-cleanup
Aug 11, 2026
Merged

fix: one click should mark your child for the game, and the ball should be findable#46
ralyodio merged 3 commits into
masterfrom
fix/coverage-and-cleanup

Conversation

@ralyodio

Copy link
Copy Markdown
Contributor

Four fixes, each measured against the production project rather than argued.

Your athlete was on 2 tracks out of 1125

Stitching a pick into the fragments either side of it has existed since the appearance matcher landed — but the only route to it was the candidate grid, which offers proposals to tick. The scrubber, where people actually identify because pointing at your own child needs no explanation, bound the one track under the cursor and stopped.

Measured on the live project, for the athlete who is actually in it:

reference: 2 track(s), candidates: 880
before:  2 track(s), 10.6s
after : 12 track(s), 54.6s      (5s to compute)

One click now covers 54.6s instead of 10.6s. The proposals accepted are the same ones the grid pre-ticks, so this is that surface's existing judgement, reached from the surface people use. It is best-effort: a failure to widen never loses the pick, which is the part the user made and the part scoring cannot do without.

The ball, twice

Only a confident detection may open a track; a faint one can extend a track that already exists. Per-class floors were therefore half a fix — a 0.2 ball landed in the low pile, where it could only attach to a ball track lucky enough to have started at 0.4, and usually there was none. That is exactly the shape the floor sweep showed: positions up 69%, track count flat.

Second, geometry. Overlap is the wrong question to ask about a thrown ball: a small box that clears several of its own widths has an IoU of exactly zero, so association fails, the track dies, and a new one is born further along the same flight.

A/B against the deployed worker, same 20s of real game, same flags:

                       ball        hoop      player      referee    time
  deployed today     21 / 382    7 / 387   297 / 8604   39 / 803    89s
  + birth at floor   71 / 831   15 / 425   297 / 8604   39 / 803    93s
  + buffered IoU     66 / 891   15 / 425   297 / 8604   39 / 803    88s

Ball positions +133% for no measurable runtime. The rim doubled too — its 0.15 floor was under the same 0.4 birth bar. Player and referee counts are identical to the digit, which is the invariant: loosening the ball must never loosen anybody.

The buffer was swept, not guessed — 3.0 joins more but buys ~1% more coverage while roughly doubling the radius in which a stray blob can be taken for the ball, and a track that teleports is worse than two that stop. --class-buffer is exposed for the same reason the floors are: the value belongs to a measurement, not an opinion.

A silent bug in #41

The live refresh re-mounted the review scrubber after swapping data-live="moments". The scrubber is not in that region — it is not on that page — while the island that is, the interactive Keep/Reject list, was never re-mounted at all.

Both halves fail invisibly. The moment list came back looking exactly right with nothing behind its buttons, and it came back that way at the worst moment, since that region is swapped precisely when a job finishes and new suggestions appear. A page holding both would have grown a second canvas and a second set of controls.

Knowing whether to wait

Identity — name, number, shirt colour, team — is collected in the scrubber, where the user is already looking at the child. Production has a kid recorded as team Triton (white) because the only form offering a colour was one they never reached. A spinner marks the one click that genuinely waits, and the empty state no longer says "run analysis" while analysis is running.


606 tests pass, lint and build clean.

ralyodio and others added 3 commits August 10, 2026 17:59
Production has an athlete bound to 2 tracks out of 1125, and one suggested
moment, in a game they played all of. Three separate causes, all measured
against that project's own data.

Stitching a pick into the fragments either side of it has existed since the
appearance matcher landed, but the only route to it was the candidate grid,
which offers proposals to tick. The scrubber — where people actually identify,
because pointing at your own child needs no explanation — bound the one track
under the cursor and stopped. It now asks the server to follow them, and the
server accepts the same proposals the grid pre-ticks. Best-effort: a failure to
widen must never lose the pick, which is the part the user made.

The ball, second. Only a confident detection may open a track; a faint one can
extend a track that already exists. So per-class floors were half a fix — a 0.2
ball landed in the low pile, where it could only attach to a ball track lucky
enough to have started at 0.4, and there usually was none. That is exactly the
shape the floor sweep showed: positions up 69%, track count flat. Track birth
now honours each class's own floor, and nothing else moves: a weak *player*
detection still cannot start a track.

Third, geometry. Overlap is the wrong question to ask about a thrown ball. It
is a small box that clears several of its own widths between sampled frames,
and boxes that do not touch have an IoU of exactly zero however close they are
— so association fails, the track dies, and a new one is born further along the
same flight. Both boxes are now grown before overlap is measured, per class,
which leaves everyone with a buffer of 0 associating on identical geometry.
Exposed as --class-buffer for the same reason the floors are: the value that
belongs there is a measurement, not an opinion.

Identity is collected where the user is already looking at the child, so the
colour field is reachable from the surface they use — production has a kid
recorded as team "Triton (white)" because the only form offering a colour was
one they never reached. And the empty state no longer says "run analysis" while
analysis is running, which was the page telling you to act when the only right
move was to wait.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The live-region refresh re-mounted the review scrubber after swapping
data-live="moments". The scrubber is not in that region — it is not on that
page — while the island that *is*, the interactive Keep/Reject list, was never
re-mounted at all.

Both halves fail silently. The moment list came back as the server rendered it,
looking exactly right, with nothing behind its buttons; and it came back that
way at the worst possible moment, since that region is swapped precisely when a
job finishes and new suggestions appear. Meanwhile a page holding both would
have attached a second canvas and a second set of controls over the same video.

The list moves into its own module so the entry point and the refresh can share
one mount, the map points at it, and mountReview refuses to attach twice.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Replaces the reasoning that motivated the buffer with the sweep that chose it.
1.5 is kept over 3.0 because the extra joining buys ~1% more coverage while
roughly doubling the radius in which a stray blob can be taken for the ball,
and a track that teleports is worse than two that stop.

Measured against the deployed worker on identical footage and flags:

                       ball        hoop      player      referee    time
  deployed today     21 / 382    7 / 387   297 / 8604   39 / 803    89s
  + birth at floor   71 / 831   15 / 425   297 / 8604   39 / 803    93s
  + buffered IoU     66 / 891   15 / 425   297 / 8604   39 / 803    88s

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@ralyodio
ralyodio merged commit 951abc4 into master Aug 11, 2026
4 checks passed
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.

1 participant