Skip to content

Federated Clock app does not mount in prod (infinite spinner) — host /api/v1/app-registry proxy + frontend manifest-shape/loader + shared-React + slug migration + e2e #132

Description

@izzywdev

@claudeThe built-in Clock federated app does NOT mount in prod (infinite "Loading application…" spinner). #129 shipped the platform but it has integration bugs that passed the gates yet break at runtime. Fix end-to-end and VERIFY with Playwright that Clock actually mounts. Land via PR (master is deploy-on-push).

Verified diagnostics (already done on prod — build on these, don't re-investigate)

  • Clock MF remote BUILDS (now in release.yml matrix) + is DEPLOYED + SERVES: https://app.fuzefront.com/apps/clock/assets/remoteEntry.js returns 200 application/javascript. (Note the real path is under /assets/.)
  • Clock is REGISTERED (applications-service ensureBuiltins): DB row is correct — remote_url/url = …/apps/clock/assets/remoteEntry.js, integration_type = module-federation, scope = clockApp, module = ./ClockApp, is_active true, visibility public, status activated.
  • ROOT CAUSE of the spinner: the frontend FederatedAppLoader (frontend/src/components/FederatedAppLoader.tsx) reads app.manifest.integration.{type,remoteEntry,scope,module}, but the apps the shell receives come from the host legacy /api/apps, whose objects have NO manifest field (keys: integrationType, remoteUrl, scope, module, url, …). So app.manifest is undefined → the loader can't resolve the remote → infinite spinner. Clock shows in the menu (from /api/apps) but never mounts.
  • The host backend (backend/src/index.ts) only mounts /api/apps; it never proxies /api/v1/app-registry (the new manifest-shaped API feat: Federated App Platform impl (registry+menu-sub+Clock+e2e+standalone) #129 added to applications-service). So the frontend's @fuzefront/app-registry-client is 404 same-origin.

Required fix (make Clock mount)

  1. Consistent data source + shape: Either (preferred) add a host-backend proxy mounting /api/v1/app-registry/* → applications-service (same pattern as the billing proxy / apps.ts), and make the shell's app menu + FederatedAppLoader use the manifest-shaped @fuzefront/app-registry-client consistently; OR include the manifest object in /api/apps and adapt the loader. The menu and the loader MUST read the same shape.
  2. MF dynamic remote loading: verify loadFederatedAppFromManifest actually loads a RUNTIME/dynamic remote (registered at runtime, not in the host's build-time vite.config remotes). The host shell uses @originjs/vite-plugin-federation with a static billing remote; loading Clock by URL at runtime needs proper dynamic-remote handling (__federation_method_* / @module-federation/runtime). Ensure React/react-dom are shared singletons between the host shell and the clock-app remote (clock-app/vite.config.ts shared), or the remote will fail/hang. Confirm the exposed module name (./ClockApp) + scope (clockApp) match.
  3. DB durability: ensureBuiltins does INSERT … ON CONFLICT (slug) DO NOTHING, but apps.slug only had a PARTIAL unique index (WHERE slug IS NOT NULL), which Postgres can't use for ON CONFLICT — it failed until I manually added a NON-partial apps_slug_key unique index in prod. Add a knex migration (backend/applications) creating a non-partial unique index/constraint on apps.slug so fresh DBs + future deploys work.
  4. Verify with Playwright (frontend-test-engineer): an e2e that logs in, opens the app menu, selects Clock, and asserts it actually renders (a clock UI, not the spinner) — run against prod and/or the approved frames in design/frames/federated-apps/. Also the register FuzeMarket → "Market" appears + loads path.

STATE

done = remote built+served+registered, CI matrix wired, applications-service build fixed (Zod), slug index added manually; remaining = host /api/v1/app-registry proxy + frontend menu/loader manifest-shape consistency + MF dynamic-remote/shared-React + slug migration + e2e that Clock mounts. decisions = manifest is the single contract (#107).

End with DONE: <PR link> + confirmation Clock mounts. @izzywdev BLOCKED: <q> + STATE if blocked.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions