Settings: two toggles that never saved, and the surfaces around them (v0.21.270) - #73
Merged
Merged
Conversation
…them (v0.21.270)
**Settings → Images & Audio had two switches that did nothing.** *Enable audio
input* and *Enable speech generation* POSTed `{"audio": {...}}`; no section
named `audio` was declared, so `config_update` dropped it and answered
`{"status": "ok", … "updated": []}`. The client toasted success and the value
reverted on reload. Both flags are genuinely read — `audio.input_enabled` gates
attachments (views.py), `audio.speech_enabled` gates the `generate_speech` tool
— and both default true, so the user-visible effect was that audio input and
agent speech could not be turned off at all.
This is the Images-bug shape a third time. ADR-17 stops a *declared* section
from silently dropping keys; nothing stopped a client writing to a root nobody
declared. So alongside the fix, the general case is now observable: an
undeclared section is named back to the caller as `ignored` and logged with the
remedy. Dropping it is still correct — undeclared means read-only by design —
but it announces itself now instead of waiting to be found by hand.
Verified against a running server: the write persists, survives a reload, shows
as changed-from-default, and resets cleanly; an unknown root reports
`ignored: ["totally_made_up"]` rather than vanishing.
With that fixed, the write-up: 26 keys across Ambassador and Images & Audio.
The Ambassador's help leads with the invariant that makes a read-broad agent
safe — its belt executes no write; reads run automatically and anything that
would change state comes back as a proposal — and explains the aide swarm as
map-reduce (aides condense one conversation each; the Ambassador reduces),
which is why a cross-conversation question is affordable at all. The voice
settings are documented as what they are: fallbacks below the per-profile ones,
which is why this screen has no control for them.
`PromptField` takes the manifest contract — the last field-kit primitive
without it. It needed the least: it already owns a reset that knows the shipped
*text* and a default-vs-yours diff, so it takes `binding` alone (anchor + help)
and keeps its own reset. `vision.refeed_recent_turns` gained a control beside
the vision toggle it belongs with.
178 of 210 settings documented.
Assisted-by: Opus 5
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
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.
Wave 3.6. Starts with a live bug the refit found, then the write-up. 178 of 210 settings documented.
Two settings toggles did nothing
Settings → Images & Audio → "Enable audio input" and "Enable speech generation" never persisted. Confirmed against a running server before fixing:
The screen POSTs
{"audio": {...}}; no section namedaudiowas declared, soconfig_updatedropped it and reported success. The client toasted "saved" and the toggle reverted on reload.The impact is real. Both flags are read —
audio.input_enabledgates audio attachments (views.py:2946,4366),audio.speech_enabledgates thegenerate_speechtool (internal_tools.py:1328) — and both defaulttrue. So audio input and agent speech could not be turned off.There was also no top-level
audioroot inDEFAULT_CONFIGat all; the read sites carried literal defaults. Declared properly now, back-compat safe.The general form, made observable
This is the Images-bug shape for the third time. ADR-17 stops a declared section from silently dropping keys — nothing stopped a client writing to a root nobody declared.
An undeclared section is now named back to the caller as
ignoredand logged with the remedy. Dropping it is still correct (undeclared means read-only by design), but it announces itself instead of waiting to be found by hand:Three regression tests: the audio round-trip, the ignored-report, and that a clean payload carries no
ignoredkey.The write-up — 26 keys
Ambassador (18) leads with the invariant that makes a read-broad agent safe: its belt executes no write. Reads run automatically; anything that would change state comes back as a proposal you confirm — including dispatch, which lands as your user turn, which is what keeps the audit trail honest about who started the work.
The aide swarm is explained as map-reduce — aides each condense one conversation, the Ambassador reduces — which is why a cross-conversation question is affordable rather than requiring every transcript in one prompt. Its eight tuning keys are documented because they're writable, not because they need attention.
The three voice keys are documented as what they are: fallbacks below the per-profile settings, which is why this screen has no control for them.
Images & Audio (8) distinguishes pictures out (generation) from pictures in (vision), and explains why the avatar model is a separate setting — avatars are generated rarely and looked at constantly. The shipped avatar template's circular safe zone is called out, because a rewrite that drops it produces clipped avatars.
Two field-kit items
PromptFieldtakes the manifest contract — the last primitive without it. It needed the least: it already owns a reset that knows the shipped text and a default-vs-yours diff, so it takesbindingalone (anchor + help) and keeps its own reset. The call site passesbindingexplicitly rather than spreading, so the manifest reset can't displace it.vision.refeed_recent_turnsgained a control beside the vision toggle it belongs with.Verification
task test:sterile1246 OKtscclean;ruff check api/clean; pyright baseline 0task docs:checkgreen, zero warnings; production build OKAssisted-by: Opus 5