Spaces:
Running
Running
Commit History
-
docs(experiments): note archived launchers run at recorded commit only
Saved copies pin internal APIs at their commit; running them on a newer tree can fail or mislead. Decision approved 2026-08-28.44fa8c8
-
chore(analyzer): archive the polish-r4 launchers
The two runs that produced numbers this round: the real-frame check of the static-box departure (shot_05's 8 goes from 217 boxes on an empty spot to 1) and the r4 demo regeneration itself. Both copies are byte-identical to the launchers, CRLF on both sides so a checkout does not break that.2829057 -
chore(analyzer): archive the eyeball-review polish launchers
Both have run, so both get their byte-for-byte copy. The gate check measured the three real-frame numbers of steps 1-3; the r3 driver regenerated the demo and came back RED on one line -- shot_07's init_idx 5 track disappears, a BRIEF-listed stop condition -- so no bundle enters the repo and `ad534bb8` stays the packaged set. Every other gate held, including 0 leaves differing outside `ball_tracks` across all fourteen shots. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>2b1d64e -
chore(data): regenerate CPU demo with hybrid far-zone detect
The detector changed, so the shipped demo had to come from the pipeline that now exists. First regeneration whose gate is not "nothing may change": boundaries 14/14, no pocket flag lost and the one new flag only on shot_10 (10 total, eye-checked), suspect flagged 0/14, ball 5 present at conf 0.688, shot_05 coverage 1.000, rack_guess on 09+10 only. Reproduces the first run leaf for leaf outside timings and rack_guess.46857ab -
chore(analyzer): archive the far-zone demo regeneration driver
The run happened and its gate came back RED, so the bundle was not shipped and step 6 has no data commit -- but the driver and the thresholds it measured belong in git either way. Also drops two stray blank lines above rack_cluster. Red: rack_guess also fires on shot_07, pocketed_guess gains a tenth flag on shot_10, shot_09 v0 moves -41.7%. Numbers in the HANDOFF.2e493d6 -
fix(analyzer): overlay BallID honours ball-set profile
The overlay called identify_balls with the standard palette while balls_init used the job's profile, so a Dynaspheres set (5 purple, not orange) would be numbered one way in the video and another on the metric table -- one statement to the viewer, two answers. Read the profile once per job and hand it to both. Gate: 2 tests, both layers pass the same table; pytest 2x green, 2 venvs.5b4ac58 -
feat(analyzer): rack_guess display flag for occluded break racks
Down the table the rack is a diamond seen edge on, so balls hide behind balls and 9 of 9 is unreachable -- at 1280 the shot_09 cluster even returns ten boxes for nine balls. Flag it instead of inventing points: 3+ balls inside 0.15 m of one centre with the cue 0.5 m away. Gate: on for shot_09 (4 of 9 seen) and shot_10 (5 of 9), off for shot_02/03/05; 9 unit tests; pytest 2x green on both venvs.2bb6493 -
feat(analyzer): track-gated low-conf recovery for live tracks
Detect at DET_CONF_FLOOR 0.05 and read the answer in two bands: above op_conf nothing changes anywhere, below it a detection may only extend a track already alive, inside that track's gate, when no high candidate is in it. IN_TOL_M gates the low band too -- the probe found boxes past the far cushion projecting 1.7 m off the cloth. Gate: 13 unit tests over both trackers; pytest 2x green on both venvs.562bec5 -
feat(analyzer): hybrid detect imgsz 960 with 1280 first-frame pass
Balls at the far rail are ~22 px wide and imgsz 640 shrinks them to 7, which is why shot_02 never had ball 5. 960 for every frame costs 2.1x, and the one frame balls_init is built from is read at 1280, where that ball reads 0.688 instead of 0.208. Segmentation stays at 640. Gate: shot_02 balls_init has a ball 0.0158 m from (1.145, 2.468) at conf 0.688 >= OP_CONF 0.2993; pytest 2x green on both venvs.9694f55 -
feat(dataset): add ND1 frame harvest script for archive VODs
The real repair for 20 px far balls and for a rack seen down its own axis is a fine-tuned detector, and that starts with frames whose provenance is recorded. One frame every 2 s, decoded sequentially so the PTS in the manifest belongs to the image beside it, plus the source SHA-256. First run on rack_a1: 160 frames against a duration of 320.0 s, 65311 archive files identical before and after. New commit scope "dataset" (approved in the BRIEF of 28/08): research data that never enters a repository.3ce4659 -
feat(analyzer): add BallID colour-profile calibration tool
Dynaspheres publishes no colour table, so the palette is read off the footage: click cue then 1..9, measured through ballid._features with the same crop shrink and cue white balance the scorer uses. Every measured vector is turned into a cost vector and checked against score_balls, so the tool cannot silently drift from the module. Mechanism run on shot_02 frame 0 of the demo VOD: 6 balls, purple ball at hue 253 deg where the standard palette has orange at 27, JSON read back by load_profile with no warning.45730a1 -
feat(analyzer): load ball-set colour profile for BallID
The tournament uses Dynaspheres Titan (the 5 is purple, not orange), so a correctly detected ball still gets the wrong number. load_profile reads a JSON of palette / white_range / the ball-8 thresholds; the method's own constants stay unreachable from it. With POOLCOACH_BALLID_PROFILE unset nothing moves: no key in the job JSON, the default palette, and the 14 demo shots still serialise without one. pytest green 3x in both venvs.d5faecf -
chore(analyzer): add far-zone detection probe (640/960/1280/band)
Design doc 7 section 3.1: measure, do not choose. Ball 5 of shot_02 is invisible at 640 (no box at all, conf floor 0.05), 0.208 at 960, 0.266 on the native far-band pass, 0.688 at 1280 -- only 1280 clears OP_CONF 0.2993. Cue at the far cushion: 5/16 frames at 640, 16/16 everywhere else. Cost per frame 56.4 / 118.4 / 212.7 / 117.0 ms = 1.0 / 2.1 / 3.8 / 2.07x. No configuration meets all three clauses; the numbers go to Cowork.9e5b26f
-
chore(data): regenerate the CPU demo bundle with pocket-bound flags
The shipped flags came from the near-pocket rule alone. Same VOD and corners; this round leaves the tracker alone, so the comparison walked into ball_tracks too: 0/84 headline cells, 0 other pre-existing leaves down to every track point, 0 shot boundaries, all 14 overlays byte for byte identical. pocketed_guess 7 -> 9 (shot_03 and shot_14); tracks, points, steal-ends and longest hole all unchanged.b33d4a2 -
chore(data): regenerate the CPU demo bundle with capped ball tracks
The shipped tracks came from the uncapped gate. Same VOD and corners; only ball_tracks was allowed to move, and nothing else did: 0/84 headline cells, 0 other pre-existing leaves, 0 shot boundaries. Tracks 19 -> 17, pocketed_guess 1 -> 7, ends within 3 cm of another ball 6 -> 3, longest hole in a track 5.267s -> 0.467s.e837cee -
chore(data): regenerate the CPU demo bundle with ball tracks
The shipped demo still showed only the cue moving. Same VOD and corners as the 25/08 bundle; object-ball tracking may only ADD, so the gate was that nothing else moved: 0/84 headline cells, 0 leaves over a FULL comparison of every pre-existing key, 0 shot boundaries. Adds 19 ball_tracks over 14 shots, 1 flagged pocketed.0abf5e0
-
docs(repo): point the experiments README at launchers/
The intro still described the pre-25/08 convention of keeping hand-run launchers at the workspace root. Last place in the app repo saying so. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>1a8b8c8 -
feat(analyzer): rebaseline the demo bundle on the cpu pipeline
The shipped set came from a CUDA worker, which does not reproduce (131 cells differed between two runs, 13-14/08). CPU does, so the Space now ships what its own hardware produces. Same VOD and corners, run 25/08 at 4c84e05: 14/14 shots equal to column C of the 24/08 three-way check on every field, 14/14 shot boundaries equal to the 14/08 manifest. Last Vietnamese strings gone -- regenerated, never hand-edited.1c0d153
-
refactor(analyzer): translate server-generated strings to English
The app is going one language; Vietnamese error and warning text in an otherwise English code base made every reader switch twice. Gate: pytest 68 passed; AST identical after blanking every string on all 7 modules, cv_worker only by the _CONF_LABEL_VI -> _CONF_LABEL rename (the VI suffix stops meaning anything once the labels are English). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>fe8d0e2 -
refactor(analyzer): translate identifiers and comments to English
Comments, docstrings, identifiers and test names are now English, including the Dockerfile and .dockerignore reasoning. Strings the user reads stay Vietnamese, because the product's UI language is Vietnamese: HTTP error details, per-shot reasons, warnings and the CSV headers. Semantics are unchanged -- an AST comparison with string constants blanked is identical for every module except three renamed locals; pytest 68 passed.876aff5 -
feat(analyzer): port analyzer pipeline from poolcoach-rl
Traced the import graph from the Analyzer routes: app store/transport/ embedded worker, src/poolcoach_cv end to end, cv_worker, the multi-shot contract test, the packaged demo video and the Hugging Face deploy set. The suggestion engine, drills, QR/TV and the image-scan job stay behind, so their routes, schemas, tables and front-end sections are gone with them; /api/table now reads pooltool directly (src/poolcoach_sim/table.py).da25347 -
chore(repo): scaffold poolcoach-app with layered requirements
Fresh repository for the Analyzer, split off from poolcoach-rl. Git LFS tracks *.pt/*.mp4/*.png from this first commit because Hugging Face (Xet) rejects any binary that never went through LFS, even in an old commit -- the previous repository had to push an orphan snapshot branch instead. Requirements are rebuilt from the import graph of the code being ported, not copied: core / app / cv / cv-cpu / dev.27b06b1