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 @@ -63,3 +63,4 @@ is at the bottom; the current roadmap lives in the [README](README.md#roadmap).
- **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.
- **v4.0.0 — Make the DJs sound human (SPEC F96–F100, F123–F126 · the wince-list, closed)** — the epic that resumed the parked voice work and shipped it as five slices. **Copy fits its break** (gh-#277): the completion request finally carries a generation cap derived from `Llm:MaxCopyChars` — killing the 30–87s free-running holds — and over-length copy is trimmed to the last complete sentence (abbreviation-aware, so "a brand new one from St." can never air) instead of rejected, which turns the SignOff/SignOn/ContextSegment over-length *silence* edge into aired persona copy; the prompt states its own word budget. **The boundary respects the backlog** (gh-#469): a queued tail crossing a schedule boundary now classifies as a straddle and *holds* the incoming DJ's sign-on behind a not-before gate honored by both the drain and the planner's peek against the real clock — the 22:00Z inversion where a sign-on aired over six minutes of buffered outgoing content is structurally dead — and a `TimeDate` deferral that would drain past its armed hour expires with a WARN rather than announcing a dead hour. **Storms stay somber, facts rotate** (gh-#468): the tone gate gains the wind-storm noun family (a tornado touchdown will never again air as chill-morning color), and context facts rotate at vend time against a day-scoped aired-set instead of the provider handing out the same `chosen[0]` all day — which reshaped `ContextContent` in the published `GenWave.Abstractions` package to carry the airable fact *list*, **the breaking contract change this major version marks**. **Pronunciation rides the wire**: heteronyms and names say themselves right via engine-adapter `[word](/ipa/)` markup (canonicalized at one seam), persona **pace** reaches Kokoro's `speed` (11 of 12 catalog personas ship a non-1.0 pace ignored until now — stations sound different, by design), and rule/correction hits plus per-persona render outcomes are finally visible at Information in Loki — *fired means aired*, never annotated-then-failed, with engine timeouts no longer vanishing from the failure rate. **The pronunciation editor**: an operator who doesn't speak IPA teaches the DJ a word entirely in the browser — a content-addressed rules API, a rows-not-JSON editor, a **"Hear it"** audition that plays the rule being authored through the real air chain (auditions excluded from the hit counter), an **espeak-ng respell oracle** (type "muh-KLOWD", get IPA — argv-only and option-inert after a review caught a key-ring-reading argument injection), all held shut by two new fitness laws (L7 the context-overload seam, L8 the one resolve seam). Built task-by-task through /build-loop's adversarial review gate across twelve rounds: catches included a hold that lasted zero seconds, a nested form real browsers silently strip, a CWE-88 file read reproduced in-container, and a preview surface that lied about the rules it auditioned. The voice contract (right-voice-or-no-speech, opt-in failover) and the manual ear-gate are held to a dedicated session.
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.5.0 # bump per release
image: ghcr.io/genwave-org/genwave-admin-ui:home-v4.0.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.5.0 # bump per release
image: ghcr.io/genwave-org/genwave-engine:home-v4.0.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.5.0 # bump per release
image: ghcr.io/genwave-org/genwave-piper:home-v4.0.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.5.0 # bump per release
image: ghcr.io/genwave-org/genwave-icecast:home-v4.0.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.5.0 # bump per release
image: ghcr.io/genwave-org/genwave:home-v4.0.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