Repository navigation
Roadmap: what is next, and against what test #4
Closed
robert-dean
started this conversation in
Ideas
Replies: 1 comment
|
Closing: every item on both lists is built or closed, and this post already hands ranking to #10. Nothing actionable remains here. |
0 replies
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
This category holds work that was designed against the real tree and then deliberately deferred, plus the seam each piece drops into when it lands. It moved here from
docs/todo/on 2026-09-11. Apart from the ideas marked as coming from the raw list, none of it is a backlog of wishes: each one was scoped during some piece of work, then cut to keep that pass on one thing. None of it needs re-deciding, only building.Two rules for every idea here:
docs/internals/,apps/api/CLAUDE.md,packages/plugin-sdk/CLAUDE.md), under the headings the rootCLAUDE.mdlists.apps/api/src/modules/modules.tsand the migrations before relying on any of it.Five files were already closed when the directory moved and did not become discussions. The code still cites them, so their reasoning stays readable at the commit they last stood at: pick-artist-matching, now-playing-displays, chart-discovery, stream-server-alternatives and provider-audio-failures.
The rest of this post is the ordering section of
docs/todo/README.mdas it last stood.What is next, and against what test
Set 2026-08-09. Most of this directory is ordered by subsystem. The near-term work is not: the
target is a station the operator leaves on all day in place of a streaming service, and that test
ranks these files differently from how they read. Selection is already good enough for it and needs
no pass. What is left, in order:
The deterministic break writer, andBuilt 2026-08-09, alternating with recorded idents. CueBreakPlannerplantingplannedsegments rather thanonly choosing ready idents.
visibility did NOT ride with it, because breaks are planted between records rather than over them;
see dj-voice for what shipped and what that leaves.
Crossfades, once breaks are landing reliably, because the cross bufferBuilt 2026-08-12, on the cue points themoves the clock those breaks are timed against.
sidecar measured the day before, so
buffer = min(outgoing.outro, incoming.intro)is a realnumber per pair. The blend POLICY inside the buffer still wants the beat layer, which is not
built and carries the licence question.
A model as the writer's second binding, with the deterministic one kept underneath as theBuilt 2026-08-12, and it came out with THREE bindingsfloor rather than as scaffolding.
rather than two: the model, the operator's own phrasings, and the station's five underneath both.
The floor stopped being code and became a setting. See dj-voice for what
shipped and the four things that came with it that were not on this list.
The activity feed over the segment transitions the writers produce.Built 2026-08-13,with its other half, the silence diagnosis, a few hours ahead of it. It really was a transport
over rows that already exist:
GET /activityunionssegment_eventsandplay_historywherethey are and adds one table of its own,
station_events, for the facts that happen to the stationas a whole and had no row anywhere — the silence cause changing, an air toggle, a gap that
outlived the loop meant to close it. "Why was the station silent at 3am" is the question the
diagnosis cannot answer and this one now does. See
station-intelligence §8 for the four decisions holding it up, and
note the one thing it deliberately does not carry: plugin call logs, which stay behind
platform.managebecause plugin output can contain a token.This list is done. What comes next is whatever the table above makes the case for.
listening-loop was the one competing on its own terms rather than queueing
behind anything, for the reason at the bottom of this file; that is settled as of 2026-08-16, and
the new order is at the very bottom.
Landed since, 2026-08-11, and none of it was on this list: the measurement layer under item 2.
analysis/is a sidecar,plugins/analyzerthe adapter,analysisa capability of its own ratherthan a kind of enrichment, and
deadair.track_analysisthe row. Two things an operator can hearcame with it and were not deferred behind the crossfade — the dead air trimmed off the head and tail
of every record (
liq_cue_in/liq_cue_out), and a per-record level decided before air rather thanchased by a follower (station-intelligence §4). The beat layer under
the same sidecar is untouched.
One thing does pull itself forward, added the same day: the four above assume listening at the
desk, which is where the station is audible and nowhere else. Replacing a streaming service means
being audible on a phone and in a car too, and that is infrastructure rather than app code. See
listening-loop. It competed with item 1 rather than slotting behind it: the
writer makes the station worth listening to, reachability makes it possible to, and which comes first
depends on whether the next week of listening happens at the desk or not. With every item on that
list built, it is no longer competing with anything. Closed 2026-08-16; see the last section of
this file.
Landed with item 3, 2026-08-12, and none of it was on this list. Four things, because the writer
turned out to sit on plumbing that was not there:
deadair.script_history, one row per write ATTEMPT, soa model that declined and the floor that covered for it are two rows rather than one misleading
one. It outlives its segment, is swept nightly against
render.scriptHistoryDays, and keeps theprompt and the raw answer only while
llm.captureWritesis on.planned → writing → written → rendering → ready), so aretry after a failed render re-speaks the words already written rather than paying for new ones,
and so a console can tell "being written" from "waiting on the renderer".
from about an hour of model and speech work to about fifteen minutes of it.
a live defect in the deterministic writer rather than anything the model introduced.
Selection got a pass after all, 2026-08-12. The premise at the top of this list — "selection is
already good enough for it and needs no pass" — held right up until a second thing was allowed to
choose. station-intelligence §1 is built: a model picks first and the
weighted catalog draw finishes whatever it did not, behind
llm.setGeneratorand off by default.Item 4 is still the whole of what is left on this list; this was not on it and did not displace it.
Two things came with it that are worth knowing before designing anything else near selection:
rules moved to
PickResolver, because a NAME arriving from a second binding had been bypassingevery one of them including the dislike filter, which is an instruction rather than a preference.
Anything that grows a third way to name a record inherits the rules for free and must not add its
own.
first. One call site behind one chokepoint, a self-hosted model with no bill, and an invariant that
turned out structural. The reasoning is in the file so it can be re-opened rather than re-argued.
The reachability question is closed, and what that leaves
2026-08-16. The operator fronted the mount with a Cloudflare tunnel and now listens on a phone
and an AVR. That is listening-loop piece 1, solved outside this repository,
and it is the last thing on this page that was competing with the table rather than sitting in it.
The goal at the top of this section (a station left on all day in place of a streaming service) is no
longer blocked on infrastructure.
So the ranking is the table's, and the table's case is for defects that were measured, ahead of
any feature:
provider-audio-failures, called the blocker under threeother files and with the worst numbers on this page: 13 of 581 tracks measured, four of eight
skipped in a hand-built order, two seconds of digital silence on air. Defused 2026-08-14 by
docs/internals/director.md§ "Nothing airs until its bytes are here": a record is not committeduntil its audio is local, so the silence this describes is no longer reachable by that path.
render-plugin-readiness, a break written off for a pluginthree hundred milliseconds from being up. Pieces 1 and 2 built 2026-08-21, which that file
says are the two worth having; piece 3 is deferred on its own argument and it calls it the weakest
of the three.
pick-artist-matching, starting with the re-measure ratherthan the fix, because the rotation-keying change may already have removed half the cause. It had
removed all of it — closed 2026-08-28 by the re-measure, no code written. The instruction to
measure before building is the whole reason this cost an afternoon instead of a feature.
This list is now empty, which is worth saying out loud because it was the thing ranked above every
feature on this page. All three closed without the fix any of them proposed being built: two by work
done elsewhere for other reasons, one by counting. The next thing is the ranked list in
comparable-stations, which resumes at its rank 2.
Three things sit alongside rather than in that order. Productions are surfaced now: the five
decisions in produced-episodes are all built, a clock band commissions one
ahead of its slot, and as of 2026-08-25 a
callinband produces an actual phone-in — a cast, shortalternating turns, one voice each. And the public mount makes two questions live that were
theoretical while it was on the LAN: whether it stays anonymous
(service-actors) and what a phone on mobile data wants. The second of those
is answered as of 2026-08-28 and the answer was not Opus: a phone moving between wifi and mobile
drops the mount outright, because the connection is bound to a source address that changes, so what
it wanted was HLS (stream-formats).
The station can say why it is quiet, 2026-08-13. station-intelligence
§8's silence half, written up in full there. This is the first item on this list that was taken
because the LIST said so rather than because something pulled it forward, and it came in under item 4
rather than beside it. Three things are worth knowing outside that file:
console could not tell an audience-gated silence from a broken lease. What it actually could not
tell was an empty room from an Icecast whose stats endpoint had gone:
IcecastStatsClient.listeners()answers
undefinedfor "could not read" and documents that as deliberately not0, andAudienceWatchwas discarding it. Inaudiencemode that is permanent silence with the consolesaying
ready.read correctly:
notDrivingclaiming there was an audience underneath the gate saying there wasnone, and
playout.smoke.tsinheriting a player queue that was already at the lead so nothing wasever handed over. Both were found by
apps/api/scripts/silence.smoke.tsand the scripts repair onits heels, which is the argument for keeping smoke scripts that drive real services.
apps/api/scripts/is type-checked now (pnpm --filter @deadair/api typecheck, split intotypecheck:testsandtypecheck:scripts). Four of five scripts had silently stopped compilingagainst the code they drive, because nothing ever compiled them. Anything added there is now held
to the same bar as a caller in
src/.All reactions