Skip to content

release: v0.0.40 — dock bridge fixes + render quality pass#30

Merged
JRemitz merged 9 commits into
mainfrom
release/v0.0.40
Jun 9, 2026
Merged

release: v0.0.40 — dock bridge fixes + render quality pass#30
JRemitz merged 9 commits into
mainfrom
release/v0.0.40

Conversation

@JRemitz

@JRemitz JRemitz commented Jun 8, 2026

Copy link
Copy Markdown
Contributor

Summary

Three lines of work bundled into one release:

  1. Dock-bridge fixes — every one had the dock failing silently

    • _resolve_player_numbers now reads event.metadata["team"] as the team_hint for resolve_scoring_team. Dock-tagged events (event_type="goal" + team metadata) previously all resolved to the home roster — away goals rendered with the wrong players.
    • _config_base_dir() honors the explicit --config path before falling back to REELN_CONFIG / platform default. Fixes "Team profile not found" when the dock invoked the CLI from a GUI launch without inheriting the shell env.
    • validate_config() accepts both string and {name, team_specific} object forms for event_types (the dock writes object form by default) and includes both shapes in iterations cross-validation.
  2. Multi-profile rendering + quality pass

    • --render-profile / -r is now repeatable on render short and render apply. Renders each profile and concatenates via the existing render_iterations engine, producing one queue entry and one render history entry.
    • Smart-zoom smoothness: input keyframes are pre-smoothed and resampled along a Catmull-Rom spline before downsampling to ffmpeg's 8-segment limit. Motion has continuous velocity through every keyframe instead of the visible "kicks" the previous straight downsample produced at zoom_frames=16+.
    • Encoder quality defaults: -pix_fmt yuv420p, -tune film, -movflags +faststart. CRF default lowered 18 → 16 (~30% larger files, visibly crisper detail after vertical crop+upscale). Each flag is configurable via new VideoConfig fields; set to "" to opt out.
    • Team-colors plumbed through to the overlay engine. Goal overlays use saved team palette instead of flat gray.
  3. State-mutation boundary

    • GameState mutations (events, segments, finish flag, livestreams, highlight merge, tournament) now route through reeln_native JSON mutations instead of direct Python assignment — matches the dock's state-mutation-boundary contract so CLI and dock share one canonical implementation.
    • New reeln game set-tournament <name> command.

Test plan

  • uv run pytest tests/unit -q2511 unit tests pass (28 new)
    • Catmull-Rom: endpoint anchoring, smoothness (max velocity-delta < 0.5 vs ~1.6 for raw linear), constant-path preservation, outlier dampening at zoom_frames=18
    • Encoder: flag emission via plan_short, empty-string opt-out, custom values, end-to-end flag plumbing through build_short_command
    • Validator: object-form event_types accepted, cross-validation includes object names
    • Team-hint: metadata["team"]="away" resolves to away team; missing event_id falls back to event_type prefix
    • Multi-profile: short + apply route through render_iterations; single profile preserved on existing path
    • Team-colors: flow through to build_overlay_context.home_colors; empty list falls back to default (None) instead of []
  • Manual: render a goal from the dock at zoom_frames=18 with two profiles selected — verify both pass through, smoother tracking, correct team roster on away events

🤖 Generated with Claude Code

JRemitz and others added 2 commits April 24, 2026 07:47
Co-Authored-By: Claude <noreply@anthropic.com>
Highlights
- Multi-profile renders: --render-profile / -r is now repeatable on
  render short and render apply. The dock passes both player-overlay
  and slowmo-ten-second-clip as separate flags; the previous single
  str option silently kept only the last value, dropping one of the
  user's selected profiles.
- Smart-zoom smoothness: input keyframes are pre-smoothed and then
  resampled along a Catmull-Rom spline before downsampling to ffmpeg's
  8-segment limit. Motion has continuous velocity through every
  keyframe instead of the visible "kicks" the previous straight
  downsample produced at zoom_frames=16+.
- Encoder quality defaults for short-form output: -pix_fmt yuv420p
  (universal player compatibility), -tune film (motion estimation
  tuned for live action), -movflags +faststart (web-streaming
  moov-atom placement). CRF default lowered 18 → 16. Each flag is
  configurable via new VideoConfig fields; set to "" to opt out.
- Team-colors plumbed through to the overlay engine. Goal overlays
  now use the saved team palette instead of falling back to flat gray.
- New: reeln game set-tournament <name> command.

Dock-bridge fixes (every one had the dock failing silently)
- _resolve_player_numbers now reads event.metadata["team"] as
  team_hint for resolve_scoring_team. Without this, dock-tagged
  events (generic event_type="goal" + team metadata) all resolved
  to the home team — away goals rendered with the home roster.
- _config_base_dir() honors the explicit --config path before
  REELN_CONFIG / platform default. Fixes "Team profile not found"
  when the dock invoked reeln from a GUI launch without inheriting
  the user's shell env.
- validate_config() accepts both string and {name, team_specific}
  object forms for event_types, and includes both shapes in
  iterations cross-validation. The dock writes the object form by
  default; the validator was incorrectly flagging every entry.

State-mutation boundary
- GameState mutations (events, segments, finish flag, livestreams,
  highlight merge state, tournament) now route through reeln_native
  JSON mutations instead of direct Python assignment. Matches the
  dock's state-mutation-boundary contract so CLI and dock share one
  canonical implementation.

Tests
- 2511 unit tests pass (28 new): Catmull-Rom smoothness regressions,
  encoder flag emission, validator object-form, team-hint
  resolution, multi-profile routing, team-color plumbing.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@pytest.fixture(autouse=True)
def _reset_active_config_path() -> Iterator[None]:
"""Ensure tests don't leak ``_ACTIVE_CONFIG_PATH`` between runs."""
import reeln.core.config as _cfg_mod
path) without exporting REELN_CONFIG. Even if a stale REELN_CONFIG is
present, the explicit --config must win for team/roster lookup.
"""
import reeln.core.config as _cfg_mod
monkeypatch: pytest.MonkeyPatch,
) -> None:
"""Active config path is used when REELN_CONFIG is absent."""
import reeln.core.config as _cfg_mod

def test_load_config_sets_active_config_path(tmp_path: Path) -> None:
"""``load_config`` records the resolved path so teams/rosters resolve."""
import reeln.core.config as _cfg_mod
tmp_path: Path,
) -> None:
"""Implicit (default) load_config must not poison the active path."""
import reeln.core.config as _cfg_mod
Reflects the companion plugin releases:
- reeln-plugin-google 0.13.1 — suppress discovery_cache warning
- reeln-plugin-openai 0.10.1 — graceful per-frame zoom skip

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@JRemitz JRemitz self-assigned this Jun 8, 2026
JRemitz and others added 4 commits June 8, 2026 07:12
_build_speed_segments_chain previously hard-coded a height-based,
crop-like scale even when the user explicitly set crop_mode=PAD.
Result: in a multi-profile render where one profile has
speed_segments and another doesn't, the speed_segments iteration
filled the 9:16 frame while the no-speed iteration letterboxed —
exact dock-reported symptom: "the first of the replay iterations
was a small clip".

Fix routes the three cases distinctly:
- smart pad: height-based scale (smart needs wider video for panning)
- non-smart pad: width-based scale + pad filter (true letterbox,
  matches the non-speed-segments path)
- crop: height-based scale + crop filter (matches non-speed path)

New regression test asserts that PAD-mode framing dimensions match
exactly between speed_segments and non-speed_segments paths.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The TeamLine in goal_overlay.ass renders ``{{goal_scorer_team}}  -
{{team_level}}`` on a single line. When a tournament is configured the
template promoted the tournament to ``goal_scorer_team`` and built
``team_level`` as ``{TEAM}/{LEVEL}`` — e.g. ``2026 SUMMER SHOWDOWN  -
MACHINE ORANGE/2014``. The clip region for that line stops at x=1717
(200px reserved for the logo), so the level digits got chopped to
``MACHINE ORANGE/2`` whenever the team name was long.

Drop the level suffix when a tournament is present — the tournament
name already carries the season/division context. Users who want it
back can override the bundled template.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- RUF003 reeln/core/zoom.py:171 -- ambiguous `×` in comment
- SIM300 tests/unit/core/test_config.py:174 -- Yoda condition
- B007  tests/unit/core/test_zoom.py:203 -- unused loop var `v` -> `_v`
- RUF003 tests/unit/core/test_zoom.py:273 -- ambiguous `×` in comment

Pure cleanup, no behavioural change. `make check` passes.

Co-Authored-By: Claude <noreply@anthropic.com>
…dings

release/v0.0.40 calls reeln_native.add_event, mark_highlighted, add_render,
mark_finished, update_event_field, bulk_update_event_type -- functions
introduced in reeln-native 0.3.0 (StreamnDad/reeln-core#5). The previous
constraint allowed resolution to 0.2.3, which lacks them, breaking CI.

Co-Authored-By: Claude <noreply@anthropic.com>
@JRemitz JRemitz force-pushed the release/v0.0.40 branch from 7029a78 to 46b45cd Compare June 9, 2026 13:22
Bumping reeln-native >=0.3.0 made several call sites reachable that
weren't exercised on the 0.2.3 stub path. Adds focused tests and marks
two genuinely-defensive branches as no-cover:

- game.py:set-tournament: happy path + config-error path
- core/events.py: tag_event metadata walk over multi-event state
  (89->88 branch); the "event id present in state but absent from JSON
  dict" branch (88->92) is marked `pragma: no branch` since
  resolve_event_id has already proven the id exists.
- core/zoom.py: float-drift timestamp smoke test; the `u < 0` / `u > 1`
  clamps stay marked `pragma: no cover` -- defensive code documented as
  guarding against float wiggle that the monotone segment walk already
  prevents.
- core/shorts.py: extend empty-quality-flag test to also cover the
  ``pix_fmt=""`` opt-out path.
- commands/render.py: --player-numbers with an unknown --event id and
  with an event whose metadata lacks a usable ``team`` key both fall
  back to event-type-prefix resolution.

Coverage: 99.68% -> 100.00% (2533 passed).

Co-Authored-By: Claude <noreply@anthropic.com>
@JRemitz JRemitz merged commit d412dab into main Jun 9, 2026
8 checks passed
@JRemitz JRemitz deleted the release/v0.0.40 branch June 9, 2026 13:45
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