Skip to content

fix(webui): load the model catalogue for the session-less home slot - #168

Merged
Fectivnfy112357 merged 1 commit into
webuifrom
fix/webui-model-picker-disabled-on-new-task
Oct 19, 2026
Merged

Fectivnfy112357 merged 1 commit into
webuifrom
fix/webui-model-picker-disabled-on-new-task

Conversation

@antianqi

Copy link
Copy Markdown
Collaborator

What

The composer's Model picker rendered disabled, showing only the bare Model placeholder, whenever the app booted straight into the new-task screen. Opening a conversation made it work.

Why

start() treats the home slot as a catalogue context like any other, but nothing ever read it.

  • selectSession only points shownCatalogueKey at the home key.
  • refreshInspection requires a sessionId, so it stays inert on a session-less boot.
  • loadModels had exactly one caller — the settings modal — which only runs once the user opens settings.

So catalogue.read(shownKey) returned undefined, the picker projected an empty models array, and ModelPicker.public.tsx:184 evaluated disabled={models.length === 0} to true. Opening a conversation called loadModels(sessionId) and hid the defect.

One line, right after selectSession:

void modelsWorkflows.loadModels(initialSessionId);

Forwarding initialSessionId rather than making a separate no-arg call covers the home boot and the routed boot with one code path, so the two cannot drift apart.

Validation

Unit — tests written before the fix, both failed against it

webui-application-lifecycle.test.ts, 2 new cases (before: 2 failed / 29 passed; after: 31 passed / 0 failed):

  • a session-less start() must read the catalogue, and must not attach a sessionId to that read — the home context asks for the runtime-wide catalogue, not some session's;
  • a routed start({ initialSessionId }) must read with that session's id.

Negative injection — 4/4 killed, 0 survivors, each restored

# Mutation What it proves
A delete the added line the test tracks the line, not some incidental effect
B swap loadModels → acquireModels loads without calling selectCatalogue, so the shown key never updates
C move the call inside the if (initialSessionId !== undefined) block reinstates the exact original defect
D pass literal undefined instead of forwarding initialSessionId only distinguishable on the routed path

B is why the fix calls loadModels and not acquireModels. D is why the second test exists — a literal undefined is indistinguishable from the real thing on the home path and is caught only by the routed case.

An earlier draft of mutation C was written as an insertion on the line after a braceless if, which is a no-op, and the test correctly did not catch it. The mutation was wrong, not the test; rewritten with an explicit block before being counted.

Browser — with a control, so the positive result means something

Served by dev:server on 127.0.0.1:8787 against the rebuilt dist-webui bundle (confirmed to contain the call). Reading .webui-model-selector-trigger directly rather than judging by colour:

Build disabled attribute Label Menu
with fix absent deepseek-v4.1-flash-free full catalogue: MiniMax M3.1-Flash-Preview / M3 / M2.7-highspeed / M2.7, opencode-zen space-bunny-free / deepseek-v4.1-flash-free
call removed disabled="" Model — reproduces the report

The second build is what gives the first one meaning: it proves the DOM read can see disabled at all, so "no disabled attribute" is an observation rather than a blind spot.

Console: 0 errors, 0 warnings.

Disclosure: the first attempt at this control was invalid — the // marker was appended after the call, so the call still executed and the control build was identical to the fixed one. Caught by checking the served bundle for the call rather than trusting the source edit, and redone.

Gates — all five on baseline 33016455

Gate Result
check:source 5188 files
typecheck:webui exit 0
check:webui-dependency 435 files / 1557 refs / 0 violations
check:webui-boundary 432 inputs
test:webui 19 failed / 2886 passed / 4 skipped

The 19 failures are pre-existing and unchanged by this patch — the same run before the change reported 19 failed / 2884 passed / 4 skipped. This adds 2 passes, 0 failures.

They look Windows-specific. Two are unambiguous: webui-design-tokens.test.ts:84 asserts a path endsWith("src/client/styles/tokens.css"), and webui-dependency-check.test.ts:939 expects C:\Users\... but receives C:/Users/... — both compare POSIX-shaped paths against fileURLToPath output. A third (plugin-action-inventory.test.ts:121-123) reports pplication\port-slices.ts, a path missing its first character. Not addressed here — flagging them so the count is not mistaken for a regression, and so they can get a Linux run once Actions is back.

⚠️ This PR will show no checks

Actions have been disabled repository-wide since 2026-10-07:

$ gh workflow run ci.yml
HTTP 422: Actions has been disabled for this repository

Zero runs repo-wide since then. Every number above is a local Windows run. The webui baseline has not been exercised on Linux or macOS since. Re-enabling requires a repository admin (Settings → Actions → General) — asking for that separately.

Related: #33 was merged and then reverted at @MCpok's request while the webui architecture was being reworked; that branch is untouched by this PR.

The composer's Model picker rendered disabled and showed the bare "Model"
placeholder when the app booted straight into the new-task screen. It only
recovered once the user opened a conversation.

Root cause: `start()` treats the home slot as a catalogue context like any
other, but nothing ever *read* it. `selectSession` only points
`shownCatalogueKey` at the home key, and the two readers that could fill it
were both unreachable on a session-less boot:

- `refreshInspection` requires a `sessionId`, so it stays inert without one.
- `loadModels` had exactly one caller, the settings modal, which only runs
  once the user opens settings.

So `catalogue.read(shownKey)` returned undefined, the picker projected an
empty `models` array, and `ModelPicker.public.tsx:184` evaluated
`disabled={models.length === 0}` to true. Opening a conversation called
`loadModels(sessionId)` and hid the defect.

The fix is one line: after `selectSession`, read whichever context it just
chose. Passing `initialSessionId` rather than a bare no-arg call covers both
the home boot and the routed boot, so the two paths cannot drift apart.

Validation
----------
Unit level, `webui-application-lifecycle.test.ts`, two new cases:

- a session-less `start()` must read the catalogue and must not attach a
  `sessionId` to that read (the home context asks for the runtime-wide
  catalogue, not some session's);
- a routed `start({ initialSessionId })` must read with that session's id.

Both were written before the fix and both failed against it (2 failed /
29 passed). After the fix the file is 31 passed / 0 failed.

Negative injection, 4/4 killed, 0 survivors, each restored afterwards:

- A. delete the added line outright;
- B. swap `loadModels` for `acquireModels` — loads without calling
  `selectCatalogue`, so the shown key never updates;
- C. move the call inside the `if (initialSessionId !== undefined)` block,
  which reinstates the exact original defect;
- D. pass a literal `undefined` instead of forwarding `initialSessionId`.

Mutation B is why the fix calls `loadModels` rather than `acquireModels`,
and mutation D is why the second test exists: a literal `undefined` is
indistinguishable from the real thing on the home path and is only caught by
the routed case.

An earlier draft of mutation C was written as an insertion on the line after
a braceless `if`, which is a no-op, and the test correctly did not catch it.
The mutation was wrong, not the test; it was rewritten with an explicit block
before being counted.

Browser verification
--------------------
Served by `dev:server` on 127.0.0.1:8787 against the rebuilt `dist-webui`
bundle, confirmed to contain the call. Reading
`.webui-model-selector-trigger` directly rather than judging by colour:

- with the fix: no `disabled` attribute, label `deepseek-v4.1-flash-free`,
  and the menu lists the full catalogue (MiniMax M3.1-Flash-Preview, M3,
  M2.7-highspeed, M2.7; opencode-zen space-bunny-free, deepseek-v4.1-flash-free);
- with the call removed: `disabled=""` and the bare `Model` placeholder,
  reproducing the report.

The second build is the control that gives the first one meaning: it proves
the DOM read can see `disabled` at all, so "no disabled attribute" is an
observation rather than a blind spot. Console: 0 errors, 0 warnings.

Gates
-----
All five run on the new architecture baseline `33016455`:

- `check:source`             5188 files
- `typecheck:webui`          exit 0
- `check:webui-dependency`   435 files / 1557 refs / 0 violations
- `check:webui-boundary`     432 inputs
- `test:webui`               19 failed / 2886 passed / 4 skipped

The 19 failures are the pre-existing baseline, unchanged by this patch: the
same run before the change reported 19 failed / 2884 passed / 4 skipped, so
this adds 2 passes and 0 failures. They look Windows-specific — two of them
compare POSIX-shaped paths against `fileURLToPath` output
(`webui-design-tokens.test.ts:84` asserts a path `endsWith`
`src/client/styles/tokens.css`, and `webui-dependency-check.test.ts:939`
expects `C:\...` but receives `C:/...`). Not addressed here; flagging them so
the count is not mistaken for a regression.

CI caveat
---------
Actions have been disabled repository-wide since 2026-10-07
(`gh workflow run` returns `HTTP 422: Actions has been disabled for this
repository`), so this PR will show no checks and the numbers above are local
Windows runs only. The baseline has had no Linux or macOS run since.
Re-enabling Actions needs a repository admin.
@Fectivnfy112357

Copy link
Copy Markdown

Reviewed against the WebUI source and runtime contract. The startup read is correctly scoped: undefined loads the home/global catalogue, while initialSessionId loads the route's catalogue. loadModels also selects the catalogue that the UI displays, and the added tests cover both startup paths. I found no blocking correctness or standards issues.

There are still no CI checks attached to this head. I did not dispatch a workflow; this merge proceeds based on the source review and the validation reported in the PR description.

@Fectivnfy112357
Fectivnfy112357 merged commit a63a6c9 into webui Oct 19, 2026
@modacker
modacker deleted the fix/webui-model-picker-disabled-on-new-task branch October 23, 2026 17:42
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.

2 participants