@claude — cross-repo delegation from FuzePicker (tracking: FuzePicker Jira FPK-7, epic FPK-6). Please fix the host-backend app-registry split-brain described below and open a PR.
Why
FuzePicker's module-federation remote (picker-app, https://picker.prod.fuzefront.com/remoteEntry.js) is healthy but never renders in the FuzeFront portal — because the shell's app list doesn't contain picker. This is a host-backend registry bug and affects every self-registering MFE, not just picker.
Root cause (in this repo)
Two routers are mounted at /api/v1/app-registry (backend/src/index.ts:287-293): a local-DB adapter (backend/src/routes/appRegistry.ts) first, then a proxy to the applications-service (backend/src/routes/app-registry.ts). The mount comment says the adapter should pass unmatched requests through to the proxy via next() — but the adapter's GET /apps (backend/src/routes/appRegistry.ts:53-73) answers unconditionally from the local apps table and never calls next(). So in prod it shadows the proxy for the one read the shell uses.
Meanwhile MFE self-registration (e.g. FuzePicker's init container: POST /apps, PUT /apps/<slug>, POST /apps/<slug>/activate) only defines GET /apps on the adapter, so those verbs fall through to the proxy → the applications-service registry.
Net: writes land in the applications-service store; the read (frontend/src/platform/appRegistry.tsx:86 → client.listApps({status:'activated'}) → GET /api/v1/app-registry/apps) is served from the host backend's local apps table. Two different stores → self-registered apps are active in the applications-service but absent from what the shell reads → getBySlug('picker') undefined → FederatedAppLoader (frontend/src/components/FederatedAppLoader.tsx:98) errors "App 'picker' not found or not activated" → no UI.
Proposed fix
In backend/src/routes/appRegistry.ts, make GET /apps (and any other reads the shell uses) return next() when the applications-service is configured (e.g. process.env.APPLICATIONS_SERVICE_URL present), so the read path proxies to the same store the write path targets. Keep the local adapter as the CI / no-applications-service fallback only (an explicit flag like APP_REGISTRY_LOCAL_ADAPTER is also fine). This realizes the pass-through the mount comment already promises.
Acceptance criteria
- With the applications-service configured,
GET /api/v1/app-registry/apps returns the applications-service registry contents, not the host backend's local apps table.
- A self-registered MFE (registered via an init-container to the applications-service, not seeded into the local table) appears in the shell's activated-apps list and mounts at
/app/<slug>.
- Picker renders in the portal at
/app/picker.
- Regression coverage: a unit test asserting the adapter delegates to the proxy when
APPLICATIONS_SERVICE_URL is set; an e2e asserting a self-registered remote mounts in the shell.
- The CI path (no applications-service) still works via the local adapter.
STATE
- Originator: FuzePicker, Jira FPK-7 (epic FPK-6). FuzePicker's half (removing a non-contract
nav field from its registration manifest) is already merged: izzywdev/FuzePicker#46.
- Done when: the 5 acceptance criteria pass and a PR is merged here; then picker (and other self-registered MFEs) render in the portal.
- Verify note: live store contents couldn't be inspected externally (registry list is auth-gated 401) — the split is proven from code paths above.
- If blocked, reply here with
BLOCKED: <question> + an updated STATE block.
@claude — cross-repo delegation from FuzePicker (tracking: FuzePicker Jira FPK-7, epic FPK-6). Please fix the host-backend app-registry split-brain described below and open a PR.
Why
FuzePicker's module-federation remote (
picker-app,https://picker.prod.fuzefront.com/remoteEntry.js) is healthy but never renders in the FuzeFront portal — because the shell's app list doesn't containpicker. This is a host-backend registry bug and affects every self-registering MFE, not just picker.Root cause (in this repo)
Two routers are mounted at
/api/v1/app-registry(backend/src/index.ts:287-293): a local-DB adapter (backend/src/routes/appRegistry.ts) first, then a proxy to the applications-service (backend/src/routes/app-registry.ts). The mount comment says the adapter should pass unmatched requests through to the proxy vianext()— but the adapter'sGET /apps(backend/src/routes/appRegistry.ts:53-73) answers unconditionally from the localappstable and never callsnext(). So in prod it shadows the proxy for the one read the shell uses.Meanwhile MFE self-registration (e.g. FuzePicker's init container: POST
/apps, PUT/apps/<slug>, POST/apps/<slug>/activate) only definesGET /appson the adapter, so those verbs fall through to the proxy → the applications-service registry.Net: writes land in the applications-service store; the read (
frontend/src/platform/appRegistry.tsx:86→client.listApps({status:'activated'})→GET /api/v1/app-registry/apps) is served from the host backend's localappstable. Two different stores → self-registered apps are active in the applications-service but absent from what the shell reads →getBySlug('picker')undefined →FederatedAppLoader(frontend/src/components/FederatedAppLoader.tsx:98) errors "App 'picker' not found or not activated" → no UI.Proposed fix
In
backend/src/routes/appRegistry.ts, makeGET /apps(and any other reads the shell uses)return next()when the applications-service is configured (e.g.process.env.APPLICATIONS_SERVICE_URLpresent), so the read path proxies to the same store the write path targets. Keep the local adapter as the CI / no-applications-service fallback only (an explicit flag likeAPP_REGISTRY_LOCAL_ADAPTERis also fine). This realizes the pass-through the mount comment already promises.Acceptance criteria
GET /api/v1/app-registry/appsreturns the applications-service registry contents, not the host backend's localappstable./app/<slug>./app/picker.APPLICATIONS_SERVICE_URLis set; an e2e asserting a self-registered remote mounts in the shell.STATE
navfield from its registration manifest) is already merged: izzywdev/FuzePicker#46.BLOCKED: <question>+ an updated STATE block.