Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,3 +62,4 @@ is at the bottom; the current roadmap lives in the [README](README.md#roadmap).
- **v3.3.1 — The whole sentence airs (gh-#424)** — patch: cue-out now comes only from silence that **actually extends to EOF**. `FfmpegCueAnalyzer` took the *last* silence region as cue-out unconditionally, so an interior pause that happened to be last — a 0.6 s injected TTS sentence gap (they clear the 0.5 s silencedetect floor), a quiet mid-track break — became `liq_cue_out`, and **the final sentence of every multi-sentence patter clip was cut on air** while booth logs, the cached wav, and the preview all looked complete (`DurationMs` derives from the same cue-out, so even the progress bar agreed). Trailing now means open-ended at EOF or closed within 0.25 s of the container duration; interior-only files report the full audible extent so durations stay measured for straddle boundary-fit; misclassification degrades safe-only (untrimmed tail → `blank.eat` backstop). Analyzer-level on purpose: music with a quiet interior break stops mis-trimming too. Playout self-heals on deploy (blurb cues re-measure per process); persisted cue rows — music enrichment, authored imaging — keep old values until re-enriched/re-authored. Found in Dean's T229 listening pass (both v2.9.0 and v3.3.0 sightings were this one path); regression spec red-checked against the pre-fix parser.
- **v3.3.2 — Nothing grim, nothing clipped (gh-#433, gh-#424 rollout)** — patch, two halves. **The history airability gate (gh-#433)**: `HistoryFactHygiene` sits between the On-This-Day day cache and either delivery lane at **vend** time — somber facts (violent-death / disaster / atrocity vocabulary, word-boundary matched, deliberately fail-closed) never air, and half-stripped `[[wikilink]]` bracket residue is cleaned from what does. Driven by a live sighting: a fatal plane crash aired as chill-morning patter color, stray bracket included. An all-somber day — Wikimedia's curated `selected` feed skews grim; the real captured reply was five-for-five — is an ordinary F107.6 skip, one Information line; pool-widening to the fuller `events` feed is the recorded follow-up if coverage thins too far. **The stale-cue retirement (db/34)**: every cue point persisted before v3.3.1's analyzer fix could carry a mid-file `cue_out` (music with a quiet break clipped at the pause — heard live on the demo). A marker-guarded one-time migration (`library.one_time_fix` ledger, claim-CTE, twice-run-proven against a real postgres:16.4) nulls the stale cue columns at deploy — instantly curative, NULL cue = full-file playback — and the F13 backfill predicate re-measures the whole library with the fixed analyzer at the worker's own pace. Zero operator action, per the standing zero-manual-release-steps rule.
- **v3.4.0 — Dayparting: named shows (SPEC F115–F121, gh-#383)** — the hours get names. **Shows are identity packages** (`station.show` widened, db/35: slug/tagline/flavor/provenance + the dormant schedulable-bundle columns, ruled unread until that slice designs their readers): authored on a new **Shows page** (60/120/400 budgets, guarded delete naming referencing blocks), assigned from the schedule grid's side panel with **run-span default** — one click names a whole contiguous same-persona run, and music-only runs span too (ruled: one click names the overnight stretch). **The ear knows**: sign-on ceremony carries the incoming show's name + flavor, a same-DJ show flip airs exactly one transition piece (F92.3 dedupe now keys persona AND show), a **cadence-gated flavor line** shares F107.5's single extra-line slot (context facts win; the prompt ceiling never grows), and top-of-hour idents go **show-scoped-first** — authored rows scoped to the show, then the station's authored pool, then a templated "You're listening to ⟨show⟩ on ⟨station⟩." floor (ladder ruled: authored beats template). **The public face knows**: spectator now-playing carries `show {name, tagline}` and `upNext.show` (name-only by type shape; flavor is prompt-config, structurally absent from every public payload — value-level pinned). **The catalog grows its third community kind**: `show` entries (schemas + budget lint + 12 seed cards, one per demo DJ, shipped as 12 one-entry PRs per the moderation model) import through the F79 shell with an **atomic authored-protection upsert** (`ON CONFLICT … WHERE imported_from IS NOT NULL` — the gh-#394 conditional-write form; an authored show's slug survives any import byte-for-byte) and a **soft "also hire ⟨DJ⟩" offer** that opens the persona's own full-card review (never a silent second import); imported Name/Flavor are sanitized at the parser seam (control chars flattened, fence runs collapsed) with the F90 full-card confirm as the ruled trust gate. **Dated specials land as the ruled-droppable tail, delivered** (db/36, own migration): per-date EXCLUDE-guarded one-offs shadow the weekly grid for their span with honest boundaries both directions (mid-block resume; a midnight special still arms the outgoing sign-off via the tomorrow-boundary-peek), authored on a dated-list form — deliberately not a second paint grid. **Airings are show-countable** (`booth_log.show_id`, air-time stamp, no FK) — the demo-hour gate gains its show halves as one indexed query. Rider: **time announcements join the ident** ("It's two o'clock on ⟨station⟩." — gh-#453, ruled on the first live listen; the forever-cache re-keys by construction). Built via /build-loop's adversarial gate across five stacked PRs: catches included a grid-save wipe of every show assignment (killed at three layers), a published-NuGet binary break, a boundary-fit estimator corruption, and an upNext erasure at exactly the boundary this epic ruled real — plus two display bugs only the live wire smokes caught.
- **v3.5.0 — Every kind in its place (gh-#372, gh-#393, catalog#33 prep)** — the mixed piles get shelves. **The Wardrobe silos by kind** (gh-#393): Personas | Themes | Fonts | Shows, URL-driven tabs with every tab present even when empty (ruled on the issue: an empty kind shows its own empty state, never a hidden tab); fonts keep their founding cards + uninstall, the other kinds list genuinely-imported rows only (`importedFrom`-gated, the two-provenance-class rule) under their own verbs — Hired / Imported / Installed — with actions deliberately staying on each kind's own page. **The community shelf gets the same treatment** (gh-#372): `?kind=` tabs over the catalog grid — a persona card and a show card were visually identical without opening them — while the cross-kind machinery (the soft hire offer including its failure path, the install modals, imported-state maps) keeps reading the full index untouched. The tab strip itself is extracted once (`components/ui/tab-strip.tsx` — CatalogTabs and BoothLogTabs were already byte-identical copies; four consumers now share one). **The validator learns the catalog's next layout** (genwave-catalog#33): `entries/<kind-plural>/<slug>/` admitted alongside the flat layout — the kind folder, when present, must name the entry's own kind; slug ownership reads the second-to-last path segment; and a new one-directory invariant pins an entry's manifest/meta/assets to a single directory (what keeps bare filenames unique for the filename-keyed asset route once one slug can legally exist under two layouts). The catalog-side move (genwave-catalog#50) holds until this release is on the box — an older app rejects a migrated index wholesale, fail-closed.
10 changes: 5 additions & 5 deletions compose.demo.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ services:
restart: unless-stopped

admin_ui:
image: ghcr.io/genwave-org/genwave-admin-ui:home-v3.4.0 # bump per release
image: ghcr.io/genwave-org/genwave-admin-ui:home-v3.5.0 # bump per release
pull_policy: always
# `!reset` drops the base `build:` context entirely: a demo box must pull this image or
# fail loudly — never silently fall back to building (and then forever running) local
Expand All @@ -61,7 +61,7 @@ services:
- "127.0.0.1:3000:3000"

engine:
image: ghcr.io/genwave-org/genwave-engine:home-v3.4.0 # bump per release
image: ghcr.io/genwave-org/genwave-engine:home-v3.5.0 # bump per release
pull_policy: always
# Same !reset rationale as admin_ui above (gh-#93). Note genwave.liq is a bind mount
# from the checkout (see compose.yaml), NOT baked into this image — the image carries
Expand All @@ -74,7 +74,7 @@ services:
# silently source-build and freeze. NOTE the first genwave-piper publish happens at the FIRST
# TAG CUT AFTER gh-#240/#241 — until that release's pin bump this tag does not exist and the
# pull fails loudly by design. Bump with the other four pins per release.
image: ghcr.io/genwave-org/genwave-piper:home-v3.4.0 # bump per release
image: ghcr.io/genwave-org/genwave-piper:home-v3.5.0 # bump per release
pull_policy: always
# Same !reset rationale as admin_ui above (gh-#93).
build: !reset null
Expand Down Expand Up @@ -123,7 +123,7 @@ services:
restart: "no"

icecast:
image: ghcr.io/genwave-org/genwave-icecast:home-v3.4.0 # bump per release
image: ghcr.io/genwave-org/genwave-icecast:home-v3.5.0 # bump per release
pull_policy: always
# Base compose publishes 8000:8000 on 0.0.0.0 — right for LAN listening in
# Operator/Standard, wrong on a public box: Icecast serves status.xsl (listener
Expand All @@ -134,7 +134,7 @@ services:
ports: !override []

api:
image: ghcr.io/genwave-org/genwave:home-v3.4.0 # bump per release
image: ghcr.io/genwave-org/genwave:home-v3.5.0 # bump per release
pull_policy: always
# compose.yaml's base `ports:` publishes BOTH 8080 and 8081 on 0.0.0.0 — fine for
# Operator/Standard (LAN reachability is the point there), wrong for a public box.
Expand Down
Loading