Summary
The Brain Console's provider checklist gives false negatives in the published/persistent runtime: OpenAI and Google/Gemini are shown as unavailable even when their environment variables are present and Brain's own subscription detector sees them.
This is a release-boundary regression in the implementation delivered for #24 / PR #26, not a credential-configuration problem.
Reproduction
With the published 4.0.2 runtime and synthetic or real OPENAI_API_KEY plus GOOGLE_API_KEY:
- Start the Brain Console.
- Request
GET /api/state.
- Compare
sections.savings.routerEngine.keys with sections.savings.routerEngine.subscriptions.
Observed on a live packaged runtime:
{
"keys": { "openrouter": true },
"subscriptions": {
"openai": { "apiKey": true },
"google": { "apiKey": true }
}
}
No credential values are exposed. The contradiction is enough to demonstrate the defect.
Root cause
Current main and the published package have a broken asset boundary:
scripts/model-catalog.mjs loads ../data/model-catalog.json.
package.json#files includes scripts/ but does not publish data/model-catalog.json.
beginConsoleRuntimeTransaction() copies the Console and scripts into the persistent runtime, but not the catalog data.
gatherRouterEngine() performs catalog loading and provider-key detection in one try.
- When
loadCatalog() throws, the catch supplies only the fallback house provider; providerKeys remains empty.
- The API then returns only the separately detected OpenRouter entry, although
detectSubscriptions() correctly sees OpenAI and Google/Gemini.
The same missing asset was reproduced in clean installed/npx 4.0.2 package roots and in the persistent .console-runtime.
Why the previous fix is incomplete
#24 and PR #26 added the correct detect_env-driven provider checks. Those checks work from a developer checkout containing data/model-catalog.json, but the released npm artifact and staged Console runtime omit the required catalog. The feature therefore fails at the package/runtime boundary.
Expected behavior
A clean published install and the persistent Console runtime must both contain every required provider-catalog asset. The Console must report the same provider availability that Brain's native detectors observe.
If a required catalog cannot be loaded, the runtime must expose an explicit degraded/error state or fail staging. It must not silently convert an internal asset failure into apparently valid “key absent” results.
Proposed upstream implementation
- Add the exact required
data/model-catalog.json asset to the npm files allow-list.
- Add the catalog to
beginConsoleRuntimeTransaction()'s required asset set.
- Before activating a staged persistent runtime, load and validate the catalog from the staged location.
- Separate catalog-load health from credential detection:
- preserve the catalog-driven provider matrix when available;
- use the existing
detectSubscriptions() result as the known-provider fallback if catalog loading fails;
- expose that the catalog-dependent view is degraded rather than presenting false negatives.
- Keep all credential handling boolean-only; never return key values.
Required regression coverage
- Pack the release artifact and assert
data/model-catalog.json is present.
- Build the persistent Console runtime from that packed artifact and assert the catalog is copied and loadable there.
- Launch the packed/staged runtime with dummy
OPENAI_API_KEY and GOOGLE_API_KEY; assert /api/state reports both as available.
- Cover
GEMINI_API_KEY as the Google-provider alias.
- Include unset-key negative controls.
- Remove/withhold the catalog and assert staging fails explicitly, or the API reports a degraded catalog state while retaining correct native subscription detections.
- Run these checks in release CI so a package cannot be published when source-checkout behavior and packed-runtime behavior diverge.
Verified against
- ruvnet-brain 4.0.2
- current upstream
main at 924d29a8a5d7b5c09effdd21d75250ea15140c60
- macOS persistent Console runtime
Related: #24, PR #26.
Summary
The Brain Console's provider checklist gives false negatives in the published/persistent runtime: OpenAI and Google/Gemini are shown as unavailable even when their environment variables are present and Brain's own subscription detector sees them.
This is a release-boundary regression in the implementation delivered for #24 / PR #26, not a credential-configuration problem.
Reproduction
With the published 4.0.2 runtime and synthetic or real
OPENAI_API_KEYplusGOOGLE_API_KEY:GET /api/state.sections.savings.routerEngine.keyswithsections.savings.routerEngine.subscriptions.Observed on a live packaged runtime:
{ "keys": { "openrouter": true }, "subscriptions": { "openai": { "apiKey": true }, "google": { "apiKey": true } } }No credential values are exposed. The contradiction is enough to demonstrate the defect.
Root cause
Current
mainand the published package have a broken asset boundary:scripts/model-catalog.mjsloads../data/model-catalog.json.package.json#filesincludesscripts/but does not publishdata/model-catalog.json.beginConsoleRuntimeTransaction()copies the Console and scripts into the persistent runtime, but not the catalog data.gatherRouterEngine()performs catalog loading and provider-key detection in onetry.loadCatalog()throws, the catch supplies only the fallback house provider;providerKeysremains empty.detectSubscriptions()correctly sees OpenAI and Google/Gemini.The same missing asset was reproduced in clean installed/npx 4.0.2 package roots and in the persistent
.console-runtime.Why the previous fix is incomplete
#24 and PR #26 added the correct
detect_env-driven provider checks. Those checks work from a developer checkout containingdata/model-catalog.json, but the released npm artifact and staged Console runtime omit the required catalog. The feature therefore fails at the package/runtime boundary.Expected behavior
A clean published install and the persistent Console runtime must both contain every required provider-catalog asset. The Console must report the same provider availability that Brain's native detectors observe.
If a required catalog cannot be loaded, the runtime must expose an explicit degraded/error state or fail staging. It must not silently convert an internal asset failure into apparently valid “key absent” results.
Proposed upstream implementation
data/model-catalog.jsonasset to the npmfilesallow-list.beginConsoleRuntimeTransaction()'s required asset set.detectSubscriptions()result as the known-provider fallback if catalog loading fails;Required regression coverage
data/model-catalog.jsonis present.OPENAI_API_KEYandGOOGLE_API_KEY; assert/api/statereports both as available.GEMINI_API_KEYas the Google-provider alias.Verified against
mainat924d29a8a5d7b5c09effdd21d75250ea15140c60Related: #24, PR #26.