Skip to content

fix(bundles): warm mpak cache before manifest read so cold-install placements register (#60)#367

Merged
mgoldsborough merged 1 commit into
mainfrom
fix/cold-cache-placements-60
Jun 3, 2026
Merged

fix(bundles): warm mpak cache before manifest read so cold-install placements register (#60)#367
mgoldsborough merged 1 commit into
mainfrom
fix/cold-cache-placements-60

Conversation

@mgoldsborough
Copy link
Copy Markdown
Contributor

Summary

Closes #60. Named bundles that declare UI placements (_meta["ai.nimblebrain/host"].placements, e.g. slot: "sidebar.apps") silently failed to register them on a cold/first-ever install — the bundle spawned and its tools worked (it showed under Connectors), but it never appeared under Apps until a full process restart.

Confirmed in prod: tenant webcaredigital, bundle @nimblebraininc/synapse-collateral — present as a connector, absent from the Apps nav, the platform logging the manifest cache miss warning.

Root cause

startBundleSource (src/bundles/startup.ts, named-bundle branch) reads the manifest from the mpak bundle cache once, up front, to derive placement/UI metadata. On a cold cache that read returns null, so meta/manifest stay null and placement registration (plus user_config resolution) silently no-op. prepareServer populates the cache as a side effect — but only after the read, and nothing re-registers placements. A restart re-reads the now-warm cache and the placement appears.

installNamed (admin/CLI path) masked the bug by pre-warming with loadBundle before calling startBundleSource (lifecycle.ts). The Connectors-UI path (installBundleInWorkspace) didn't, so UI installs hit it.

The issue's "log a warning when the read misses" recommendation already shipped — that's the manifest cache miss warning. This PR lands the actual fix.

Fix

Warm the cache at the one chokepoint every named install/respawn path funnels through (connector UI, installNamed, boot reload, JIT), immediately before the up-front read:

if (!mpak.bundleCache.getBundleManifest(ref.name)) {
  await mpak.bundleCache.loadBundle(ref.name);
}
  • Guard on getBundleManifest (the same manifest.json the read consumes), not on loadBundle's internal .mpak-meta.json short-circuit. A manifest-only cache (warm boots, offline starts, test fixtures) therefore adds no network call — warm-cache spawns stay network-free, exactly as today.
  • Removes the now-redundant pre-warm from installNamed; the contract is enforced in one place instead of relied on per-caller.

Tests

New test/integration/startup-cold-cache-placements.test.ts reproduces the cold→warm transition deterministically by stubbing loadBundle to materialize the cache (no network):

  • cold cache → placements register on first start without a restart. Proven to fail without the fix (meta === null, emits the prod warning) and pass with it.
  • warm cache → the guard adds no extra cache pull (an unguarded await loadBundle would make this 2 and hit the network on manifest-only caches).

verify:static green; 127/127 across the bundle/placement/install suites pass.

Review

Root-cause analysis run through the bug-fix workflow with an independent adversarial review (ENDORSE-WITH-REFINEMENTS) — the reviewer caught the .mpak-meta.json vs manifest.json short-circuit mismatch, which is why the warm is guarded on getBundleManifest.

…acements register (#60)

Named bundles declare UI placements under
`_meta["ai.nimblebrain/host"].placements`. `startBundleSource` read the
manifest from the mpak bundle cache once, up front, to derive that metadata —
but on a cold/first-ever install the cache is empty, so the read returned
null, `meta`/`manifest` stayed null, and placement registration (plus
user_config resolution) silently no-op'd. The bundle spawned and its tools
worked (it showed under Connectors), but its `sidebar.apps` placement never
registered, so it never appeared under Apps until a process restart re-read
the now-warm cache. `prepareServer` populates the cache as a side effect, but
only *after* the up-front read, and nothing re-registered placements.

`installNamed` masked this by pre-warming with `loadBundle` before calling
`startBundleSource`; the Connectors-UI path (`installBundleInWorkspace`)
didn't, so UI installs hit the bug.

Fix at the single chokepoint every named install/respawn path funnels
through: warm the cache before the up-front read. Guard on `getBundleManifest`
(the same manifest.json the read consumes), not on `loadBundle`'s internal
`.mpak-meta.json` short-circuit — so a manifest-only cache (warm boots,
offline starts, test fixtures) adds no network call. Remove the now-redundant
pre-warm from `installNamed`.

Regression test reproduces the cold→warm transition deterministically by
stubbing `loadBundle` to materialize the cache; it fails without the fix
(meta null) and passes with it, and a second case locks the no-extra-pull
guarantee on warm caches.
@mgoldsborough mgoldsborough added the qa-reviewed QA review completed with no critical issues label Jun 3, 2026
@mgoldsborough mgoldsborough merged commit 0156dd0 into main Jun 3, 2026
5 checks passed
@mgoldsborough mgoldsborough deleted the fix/cold-cache-placements-60 branch June 3, 2026 03:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

qa-reviewed QA review completed with no critical issues

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Bundle placements silently fail to register on cold cache

1 participant