fix: register the settings card instead of throwing while building it
Release / release (push) Successful in 7s

The card never appeared, and nothing said why: the plugin's configuration
section was missing from its card in the Plugins panel while a sibling
plugin's showed, on a deployment where the Host demonstrably served both
namespaces (`settings/describe` over the app's own RPC answered
`dsh-recall fields=32 secrets=[embeddingApiKey set]`) and the browser was
demonstrably serving this bundle.

The cause was construction order. `SettingsFormModel.bind()` is not a
subscription setter — it builds its snapshot store from one immediate
`project()` call — so the projection runs inside the constructor, and the
projection reads the describe mirror to report whether the API key is set.
`this.describe` was assigned *after* `bind`, so that first projection read
`undefined.getSnapshot()`:

  TypeError: Cannot read properties of undefined (reading 'getSnapshot')
      at RecallSettingsController.secretIsSet (client/client.js:428)
      at RecallSettingsController.projection (client/client.js:413)

It was thrown from inside the `ctx.inject(['configForms'], …)` callback while
the card was being registered, so the registration was abandoned: no ledger
entry, no section on the card, and no error in the UI — the one failure mode
this file's own header set out to prevent.

Everything the projection reads is now assigned before anything projects. The
sibling plugin was never affected because it reads the same face lazily from a
field it assigns first.

The test stub's `bind()` only *stored* the projection, which is why the suite
was green through all of this. It now projects once, as the framework does —
and with the old ordering every mounting test fails with that TypeError, so
this cannot regress silently again.
This commit is contained in:
NotExpectedYet committed 2026-09-26 07:43:31 +01:00
1 parent a350178434
commit fbf1da7452
2 files changed
+15 -2

No files matched your search

+5
View File
@@ -109,6 +109,11 @@ function makePrimitives(record) {
bind(project) {
this.project = project
// Faithful to the framework: `bind` is not a subscription setter, it
// builds the snapshot store from ONE immediate projection. A stub that
// only stored the projection would let a controller read a field it has
// not assigned yet — which is exactly the bug this reproduces.
project()
return { getSnapshot: () => project(), subscribe: () => () => {} }
}