feat(settings): the listener's knobs — settings.json + /settings pane [spec 12] - #110
Merged
Conversation
Seven writable intents and not one more: settings.json under env under flags per knob, an engine-owned single-writer store, two additive wire messages, and a /settings pane whose focus grab is the listener's own. Amends spec 10 §3.3 (the pet knob moves into the settings layer) and notes the sanctioned focus exception; master §10 gains row 12. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
settings.json reads with per-key salvage (a broken gap must not cost the mute), writes atomically, and merges below env and flags per knob — a file-set voice carries the same explicit provenance a typed --voice does. The SettingsStore is the single mutation path: validate, apply live, persist the touched keys, notify. The wire schemas land beside it: a patch can mute or clear the voice but can never write 'hosted'. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The Director reads a live settings thunk instead of captured scalars: gap and memory span at each use, anchors at the fire site (the scheduler stays constructed), music at the scheduling site — and off gates the discovery spend, not just the airtime (peer-review find). LiveCadence re-reads the mix per boundary; MutableVoice flips mute per utterance without tearing the provider down. /settings joins /quit as an engine command: a pane-capable host shows the pane, the plain host gets a pointer. The IpcHost grows the settings bridge — snapshot after hello on every attach, store-rejected patches answered with truth, snapshots kept out of the replay backlog. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Seven items, intent labels only — the gear names a mix, never a mode; values the presets cannot express render as an honest custom position. Arrows move and adjust, Esc hands focus back, the broadcast never pauses; every change rides settingsSet immediately and the pane renders only what the engine echoes back. The pet follows the live tuiPet setting, with MURMUR_TUI_PET kept as the client-local final override. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
7 tasks
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…§3.4] The first build muted by routing synthesis to the silent stub — so the clip on air and the look-ahead's pre-synthesized beats kept speaking after the toggle: a mute button that does not mute (user find, during the #111 pass). Re-decided: `muted` is one gain move on the engine's master bus — instant, mid-word, bus-wide, and the program never notices; unmute resumes the sentence in flight. The voice/null clearing dance, the derived-voice thunk, the endpoint greying, and MutableVoice all dissolve; the `voice` provider knob returns to the dev surface it was. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Implements specs/spec12/12-settings.md (new); amends specs/spec10/10-tui.md §3.3 and specs/DESIGN.md §10 (row 12).
What this is
The settings layer: the listener's knobs, persisted and live.
$MURMUR_HOME/settings.json— hand-editable and panel-written; per-key salvage on read (a brokengapSecondsnever costs the mute state), atomic temp+rename writes, merged below env and flags per knob at boot. A file-setvoice: "stub"carries the same explicit-mute provenance a typed--voice stubdoes.SettingsStore— the engine-owned single writer: validate → apply live → persist the user-touched keys → notify. Runtime rule: a live change is the newest user intent and wins over a boot flag; layering re-applies at the next boot.settings(snapshot afterhelloon every attach and after every accepted/rejected set; read-only facts: home, endpoint-configured, music-available — never a key) andsettingsSet(partial patch;voicecan be'stub'or clearednull,'hosted'is inexpressible by schema). Snapshots stay out of the replay backlog.LiveCadence); music off gates the discovery spend too, and toggling on re-enters through the existing boundary cold path; the pet is a client render toggle./settingspane — engine-parsed command (plain host gets a pointer line instead); seven intent-labeled items (noevery_n/random/brainever shown; off-preset values render as custom), ↑↓/←→/space, Esc returns focus, broadcast never pauses.tuiPetmoves into the layer;MURMUR_TUI_PETsurvives as the client-local final override.Evidence
tsc --noEmitclean, barenpx oxlintclean.runApp+ scripted fake client): attach → hello+snapshot →/settings→ open-flagged snapshot →settingsSet→ broadcast echo →settings.jsonon disk holds exactly the touched keys ({"gapSeconds":4,"voice":"stub"}).Peer review (codex gpt-5.5, xhigh): 3 findings, 1 applied (music-off still paid prefetch discovery calls — now gated at the spend, regression-tested), 2 dismissed (untracked-files artifact of pre-commit review; schema-invalid patches are malformed messages by design, spec wording tightened).
Owed to the by-ear pass
Pane feel (focus handoff, gear labels, mid-song Esc) — folded into the existing TUI sensory pass rather than a new issue.
AI coding brief
Original request: Design-first task: a settings layer (
~/.murmur/settings.json) + TUI settings pane, with exactly seven writable knobs pre-decided by the user (anchors, music, mix gear, gap, mute, pet, memory span + read-only home/endpoint facts) and an explicit non-goal list; open points (schema/precedence, writer, wire shape, hot-vs-restart, focus, spec home) to be settled in-session, then shipped via the standard spec→build→PR flow. Why: the radio's listeners need runtime knobs without learning env vars or field names.Manual interventions: The user delegated the open-point discussion ("grill it yourself, then decide") after reviewing an agenda of recommendations; the self-grilling overturned two of the assistant's own initial calls (music toggle upgraded from restart-required to hot; whole-file settings parse downgraded to per-key salvage) and both survived into the build. No mid-build redirections.
Retro: Front-loading the frozen-decision list in the task brief (7 knobs, non-goals, hard constraints) made the design phase converge in one round; the one rework loop (away-stream regression from an over-eager prime-and-yield branch) would have been avoided by reading the existing cold-path semantics before "improving" them — trace first, then optimize.
🤖 Generated with Claude Code