Skip to content

docs(webui): correct a pending decision that was never a decision - #22

Merged
modacker merged 2 commits into
webuifrom
docs/adr-0012-correction
Oct 5, 2026
Merged

modacker merged 2 commits into
webuifrom
docs/adr-0012-correction

Conversation

@modacker

@modacker modacker commented Oct 5, 2026

Copy link
Copy Markdown
Collaborator

A doc-only correction. The note recorded a pending decision that was never a decision, and acting on it would have changed working code.

The claim: variantForEffort returns "" for "off" while the configured variant is named none-thinking, so switching thinking off "would send a name the model does not have". It was filed as a data-contract change for a later decision.

Why it is wrong: the two spellings are not compared at the same layer. The runtime normalises the catalogue before it reaches the client — local-runtime/src/model-provider/list-models.ts maps none-thinking to "" — so a client reading supportedVariants sees ["", "thinking"], and none-thinking does not exist on the wire. Sending "" is correct. The variants.includes("none-thinking") clause in the read side is there for catalogues that never passed through that normalisation.

What stays true in the note, and is kept:

  • supportedVariants is still absent from the server's WebuiModelEntry — it appears nowhere in server/port.ts — so the hasOff && hasOn inference still never runs.
  • The declaredSwitch escape hatch reads thinkingConfig.default_value, and thinkingConfig is on the server contract, so the switch now surfaces through the runtime's own statement. That is why the original symptom is unverified rather than confirmed: the path that used to fail is no longer the one being taken.

What is actually open, and now narrower than the note claimed: whether a committed choice survives a reload (one browser test, never measured) and whether supportedVariants should be wired through at all.

The note's own reporting discipline is kept — it is a correction of a conclusion, not a deletion of a record. Section 1 (the chip separator question) is untouched.

probe added 2 commits October 5, 2026 15:13
… ends

The WebUI is a loopback process the user starts. Neither cron engine in
the tree can serve it: the v1 path is pinned off for v2-compat hosts and
its table is empty after the v2 migration moved the data out, and the v2
`CronService` is created only for an Electron owner and is not carried on
the host contract in any case. Measured on a real WebUI host; ADR 0012
carries the full chain and the decision.

So the WebUI runs its own: a store at `<dataDir>/webui/scheduled-tasks.sqlite`
with its own table, an in-process tick beside the heartbeat in
`WebuiService`, and a `WebuiScheduledTaskPort` whose six methods name
operations rather than an engine. It never selects from the runtime's cron
tables — that would be a second scheduler with none of v2's concurrency
guards writing into the same agent queue as the desktop client.

Being alive is the execution guarantee. Slots missed while the process was
down are **not** replayed; they are counted (`missed_count`,
`last_missed_at_ms`) so the surface can say so rather than imply the task
never existed.

`better-sqlite3` is declared here for the first time: the WebUI did not
persist anything before, and it had been reaching the module through the
workspace's hoisted `node_modules` without declaring it.

ADR 0012 states the retirement condition as two independently checkable
gates, and the probe test reports on them on every run — quietly while both
are shut, and with a notice when the runtime moves. It never fails a build:
a gate that goes red for something that is not a defect only teaches people
to ignore red. Retirement is the adapter, the table migration, and tearing
down this tick loop in the same change; two schedulers over one queue is
what the decision exists to prevent.
The note claimed `variantForEffort` returns `""` for "off" while the
configured variant is named `none-thinking`, so switching thinking off
"would send a name the model does not have", and filed it as a data
contract change for a later decision.

The two spellings are not compared at the same layer. The runtime
normalises the catalogue before it reaches the client
(`local-runtime/src/model-provider/list-models.ts` maps `none-thinking` to
`""`), so a client reading `supportedVariants` sees `["", "thinking"]` and
`none-thinking` does not exist on the wire. Sending `""` is correct, and the
`includes("none-thinking")` clause in the read side covers catalogues that
never passed through that normalisation. Acting on the note would have
changed working code.

Two things in it are still true and stay. `supportedVariants` is still
absent from the server's `WebuiModelEntry`, so the `hasOff && hasOn`
inference still never runs; and the `declaredSwitch` escape hatch means the
control now surfaces through `thinkingConfig.default_value`, so the
original symptom is unverified rather than confirmed. What is actually
open is narrower: whether a committed choice survives a reload, which no
browser test has measured, and whether `supportedVariants` should be wired
through at all.
@modacker
modacker merged commit 1304e4d into webui Oct 5, 2026
18 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant