feat(mc): Pool B Health + Install Funnel ('auto-sync') + claude CLI prereq#28
Merged
Conversation
…e CLI prereq
Two operator-visible additions, plus one prereq cleanup:
1. **Pool B Health + Install Funnel** — new Mission Control subnav tab
that answers the five questions the operator wants in their face:
- Is the Mac actually serving customers right now? (online dot)
- Which devices are paired + when did each last serve? (table)
- Are pillar-suggest chips populating? (24h success rate)
- Are customers following the install path?
(pair → claim → first-inference funnel)
- What's Pool B costing this week per subscription?
(daily roll-up, last 14d)
The "auto-sync" framing — operator can glance and confirm both that
the customer-facing chat onboarding's chips are alive AND that
downloading users are not silently dropping off mid-pair.
Added:
- server: `packages/wavex-os-server/src/mission-control/pool-b-health.ts`
(5 query functions, 30s in-memory cache, service-role Supabase
client; queries `wavex_os.{usage_ledger, os_devices, os_device_pairings}`)
- routes: `packages/wavex-os-server/src/routes/pool-b-health.ts`
(5 GET endpoints under `/api/pool-b-health/*`, board-gated,
`?fresh=1` bypasses cache for live QA)
- plugin worker: 5 new `ctx.data.register("pool-b-health-*")` handlers
- plugin UI: `PoolBHealthWidget.tsx` (KPI row + 4 stacked sections,
30s auto-refresh, manual ↻ refresh button)
- subnav: new "Pool B" tab in MissionControlPage (between Operations
and the polish tab fallback)
2. **Claude CLI as a hard prereq in install.sh**
The repo runs in BYOC (Bring Your Own Claude) mode — the tier-router
T2 calls spawn the local `claude` binary. Without it, the dev server
boots but T2 inference halts and the operator sees nothing useful
come back from pillar-suggest / narrate / etc. install.sh was
silently leaving this gap.
Now installs `@anthropic-ai/claude-code` globally via npm during the
prereq pass (works on both macOS + Linux uniformly — same path that
`npm install -g pnpm@8` already uses). Bypass with
`WAVEX_SKIP_CLAUDE_CLI=1` if you manage it elsewhere (asdf, system
package, etc.). The post-install message now lists
`claude login` as step 1 before `wavex-os login` at step 2.
Verification:
- bash -n install.sh → clean
- TypeScript transpile on all 3 new files → parsed ok
- Test CI runs on push.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
CI on PR #28 failed because src/mission-control/pool-b-health.ts imports @supabase/supabase-js but the package wasn't declared in wavex-os-server's own package.json (vitest's resolver doesn't hoist transitive workspace deps the way the runtime can). Pinned to ^2.46.1 to match the version used by claude-code-proxy, cloud-client, inference-server, and onboarding-ui — keeps the workspace consistent on a single supabase-js major. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
CI runs `pnpm install --frozen-lockfile` by default. Adding @supabase/supabase-js to wavex-os-server's package.json without updating the lockfile makes that step fail with "specifiers in the lockfile don't match specs in package.json". Regenerated via `pnpm install --lockfile-only` so node_modules isn't touched but the lockfile picks up the new specifier. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
aimerdoux
added a commit
that referenced
this pull request
May 19, 2026
…29) Two bugs that surfaced trying to build the wavex plugin locally after #27 + #28. Neither one was caught by OSS CI because the plugin build isn't run on the test workflow; the build is only exercised at dev boot time via `pnpm dev:wavex-plugin:build`. Both bugs hard-fail the build so the plugin's dist/ never refreshes — the Mission Control UI loads a stale bundle that doesn't include any of the F1-F6 or Pool B work. 1. **Hardcoded user path in build-ui.mjs** The esbuild import was pinned to `/Users/dylanriedweg/wavex-os/node_modules/.pnpm/esbuild@0.27.7/...` — works only on the original author's machine. esbuild isn't a direct dep of this package so a bare `import "esbuild"` also fails (pnpm's isolated store hides it). Replaced with a glob over the workspace root's `.pnpm` store that picks up whichever esbuild version is installed: ```js const pnpmStore = resolve(__dirname, "../../node_modules/.pnpm"); const esbuildDir = readdirSync(pnpmStore).find((d) => /^esbuild@\d/.test(d)); const esbuildMain = pathToFileURL(join(pnpmStore, esbuildDir, ...)); const { build } = await import(esbuildMain.href); ``` Robust across machines and esbuild minor-version bumps. 2. **`Record<View, number>` missing the new `"pool-b"` key** #28 added `"pool-b"` to the `View` type union but didn't update `countByTab`, which is typed `Record<View, number>`. tsc: MissionControlPage.tsx(94,9): error TS2741: Property '"pool-b"' is missing in type ... but required in type 'Record<View, number>' Added `"pool-b": 0` with a comment noting that Pool B Health doesn't have a backend-driven badge yet — the `mission-control-tab-counts` RPC predates it. A follow-up can surface `pillar_suggest_calls_24h - pillar_suggest_success_24h` as the badge value when chips are failing. After both fixes, plugin builds to a fresh 235.8kb dist/ui/index.js that includes the PoolBHealthWidget. Paperclip dev server now loads the new "Pool B" subnav tab. Co-authored-by: aimerdoux <aimerdoux94@gmail.com> Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
aimerdoux
added a commit
that referenced
this pull request
May 19, 2026
Bundles four operator-visible improvements to the Pool B inference stack that together let WaveX run as a SaaS instead of a self-hosted toy. Companion to #28 (Pool B Health widget + claude CLI prereq). A. Composio managed mode (operator-managed key, not customer-managed) ───────────────────────────────────────────────────────────────────── The directory modal at /<company>/connectors used to demand each customer paste their own Composio API key. For a hosted WaveX deploy that's wrong: the operator runs the Composio account, the customer just gets the connectors as part of their subscription tier. - New env: WAVEX_COMPOSIO_MANAGED=1 on wavex-os-server - /api/connectors/setup-status now returns { managed: boolean } - ConnectorsSidebar's SetupScreen gate skips the key-entry modal when managed: true — falls through to the catalog (or degraded catalog when Composio is transiently down) instead of demanding a key the customer doesn't have Self-hosted users keep BYOC by leaving WAVEX_COMPOSIO_MANAGED unset. B. UUID→slug translation in plugin worker (fixes "0/0 ready" + fleet showing 0 agents even when 35 are alive) ───────────────────────────────────────────────────────────────────── The plugin context gives the worker paperclip's company UUID, but wavex-os-server's loadCompanyManifest() / getOnboardingDir() index by the wavex slug (~/.wavex-os/instances/<slug>/). Every single ${base}/api/.../<uuid>/... call was silently returning [] because the manifest lookup failed. Diagnosed live: /api/companies/<uuid>/agents → [] /api/companies/<slug>/agents → 35 agents /api/companies/<uuid>/agents on paperclip core → 35 agents - New helper resolveWavexSlug(uuid) in worker.ts that calls paperclip on first miss, caches the mapping in-process (immutable post-finalize) - Replaces every `const id = String(companyId ?? "")` with the async resolver (~30 sites mechanical, plus the inception-status handler's inline pattern) - Falls back to the raw input on lookup failure for partial-wiring test cases — degraded behavior is the same as before this change Net effect: Inception Status sidebar now shows the real agent count, Mission Control's Stream / Scoreboard / Map all hydrate correctly, everything that hit ${base}/api/.../<id>/... starts working. C. Inference life bar + throttle slider in Pool B Health ───────────────────────────────────────────────────────────────────── Operator wants a glance check before triggering big customer flows — "am I about to blow my Claude Max quota?" — plus a control surface to throttle Pool B request rate to spread load. - New endpoint GET /api/pool-b-health/operator-quota returning {tokens_used_24h, tokens_used_7d, tokens_used_30d, cost_usd_*, requests_24h, requests_7d, last_inference_at}. Aggregates wavex_os.usage_ledger rows over the windows; 30s cache. - New worker data registration pool-b-health-operator-quota. - UI: life bar with green/yellow/red zones (cap at $5/day soft visual ramp), 24h/7d/30d numeric readout, throttle slider 1-120 req/min persisted to localStorage. Throttle ENFORCEMENT (queue/reject above cap on the inference-server side) lands in a follow-up PR — for now the slider is a planning surface. The life bar is fully live. D. Supabase Edge Function: Pool B inference fallback ───────────────────────────────────────────────────────────────────── When the operator's Mac is offline or rate-limited, the customer's 60s Realtime timeout currently fails silently (empty pillar suggestions, hardcoded narrate copy). This PR adds an Edge Function that the consumer-side cloudInference() can call as a fallback after its existing timeout fires: POST /functions/v1/wavex-inference-fallback body: { prompt, max_output_tokens, purpose, model? } → { ok: true, content, usage, source: "fallback" } - Auth-gated: requires the caller's JWT + an active/trialing subscription. Free tier returns 402 — no anonymous Anthropic calls on the operator's dime. - Defaults to claude-haiku-4-5 (cheap; ~$0.005 per pillar-suggest). Whitelist also allows haiku-4-5-20251001 + sonnet-4-6. - Logs each call to wavex_os.usage_ledger with device_id=NULL so fallback usage shows up in the Pool B Health widget separately from Mac-served calls. - Returns 503 with structured error if ANTHROPIC_API_KEY is not set in Supabase project secrets — browser gracefully falls back to its existing failure mode. Consumer-side cloudInference.ts update (browser calls this on timeout) lands in a follow-up PR in wavex-experience-architect. Deploy steps for D: 1. Generate Anthropic API key at console.anthropic.com (separate from CLI session token — backend use only). 2. supabase secrets set ANTHROPIC_API_KEY=sk-ant-... 3. supabase functions deploy wavex-inference-fallback Verification ───────────────────────────────────────────────────────────────────── - pnpm --filter @wavex-os/paperclip-plugin-wavex build → 240.0kb dist - packages/wavex-os-server tsc → clean - Diff: 7 files, ~330 insertions, ~30 deletions - Test CI runs on push Co-authored-by: aimerdoux <aimerdoux94@gmail.com> Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Two operator-visible additions on top of #27, plus one quality-of-life prereq cleanup.
1. Pool B Health + Install Funnel — new Mission Control subnav tab
The operator wants five questions answered at a glance. This adds a "Pool B" tab to
MissionControlPagethat surfaces all five:os_devices⋈max(usage_ledger.ran_at)— green dot if any device served within 5musage_ledgerrowsusage_ledger.cost_centsroll-up grouped bysubscription_id, 14dThis is the "auto-sync" framing — one glance confirms both that the customer-facing chat onboarding's chips are alive AND that downloading users are not silently dropping off mid-pair.
Wiring
Files
packages/wavex-os-server/src/mission-control/pool-b-health.ts— five query functions + 30s cache + service-role clientpackages/wavex-os-server/src/routes/pool-b-health.ts— five GET endpoints, board-gated,?fresh=1for live QActx.data.register("pool-b-health-*")handlers inpaperclip-plugin-wavex/src/worker.tspaperclip-plugin-wavex/src/ui/PoolBHealthWidget.tsx— KPI row + 4 stacked sections + 30s auto-refresh + manual ↻ buttonMissionControlPage.tsx(between Operations and Polish)2. Claude CLI as a hard prereq in
install.shThe repo runs in BYOC mode — the tier-router T2 calls spawn the local
claudebinary. Without it, the dev server boots but T2 inference paths halt and the operator sees nothing come back from pillar-suggest / narrate / etc.install.shwas silently leaving this gap.Now:
command -v claudecheck during prereq passnpm install -g @anthropic-ai/claude-code(works on macOS + Linux uniformly, same path as the existingnpm install -g pnpm@8)WAVEX_SKIP_CLAUDE_CLI=1env to bypass if you manage it elsewhere (asdf, system package)claude loginas step 1 beforewavex-os loginat step 2Why these belong together
The whole point of the Pool B Health tab is to answer "are chips still working?" — but if
claudeCLI isn't installed, chips can never work, and the dashboard will just show a flat zero forever. Makingclaudea hard prereq closes the loop: a fresh install either works end-to-end or surfaces the missing dep explicitly.Verification
bash -n install.shcleanLive QA after merge
Once landed + redeployed, hit the new endpoints directly to confirm wiring:
Then in MC:
MissionControlPage→ Pool B tab → should see "no devices paired" empty state. Once a customer runswavex-os login+ completes onboarding, the dots light up.🤖 Generated with Claude Code