From 60c393b13774da1ff9517a25f6530c6a10005e41 Mon Sep 17 00:00:00 2001 From: Adolfo Delorenzo Date: Mon, 23 Mar 2026 22:39:34 -0600 Subject: [PATCH] docs(03): create gap closure plan for router mounting and field name fixes --- .planning/ROADMAP.md | 7 +- .../03-operator-experience/03-05-PLAN.md | 220 ++++++++++++++++++ 2 files changed, 224 insertions(+), 3 deletions(-) create mode 100644 .planning/phases/03-operator-experience/03-05-PLAN.md diff --git a/.planning/ROADMAP.md b/.planning/ROADMAP.md index 90217f2..83fbd6d 100644 --- a/.planning/ROADMAP.md +++ b/.planning/ROADMAP.md @@ -14,7 +14,7 @@ Decimal phases appear between their surrounding integers in numeric order. - [x] **Phase 1: Foundation** - Secure multi-tenant pipeline with Slack end-to-end and basic agent response (completed 2026-03-23) - [x] **Phase 2: Agent Features** - Persistent memory, tool framework, WhatsApp integration, and human escalation (gap closure in progress) (completed 2026-03-24) -- [x] **Phase 3: Operator Experience** - Admin portal, tenant onboarding, and Stripe billing (completed 2026-03-24) +- [x] **Phase 3: Operator Experience** - Admin portal, tenant onboarding, and Stripe billing (gap closure in progress) ## Phase Details @@ -66,13 +66,14 @@ Plans: 3. A new tenant completes the full onboarding sequence (connect channel -> configure agent -> send test message) in under 15 minutes 4. An operator can subscribe, upgrade, and cancel their plan through Stripe — and feature limits are enforced automatically based on subscription state 5. The portal displays per-tenant agent cost and token usage, giving operators visibility into spending without requiring access to backend logs -**Plans**: 4 plans +**Plans**: 5 plans Plans: - [ ] 03-01-PLAN.md — Backend foundation: DB migrations, billing models, encryption service, channel/billing/usage API endpoints, audit logger token metadata - [ ] 03-02-PLAN.md — Channel connection wizard (Slack OAuth + WhatsApp manual), onboarding flow with 3-step stepper, BYO API key settings page - [ ] 03-03-PLAN.md — Stripe billing page with subscription management, status badges, Checkout and Billing Portal redirects - [ ] 03-04-PLAN.md — Cost tracking dashboard with Recharts charts, budget alert badges, time range filtering +- [ ] 03-05-PLAN.md — Gap closure: mount Phase 3 API routers on gateway, fix Slack OAuth and budget alert field name mismatches ## Progress @@ -83,7 +84,7 @@ Phases execute in numeric order: 1 -> 2 -> 3 |-------|----------------|--------|-----------| | 1. Foundation | 4/4 | Complete | 2026-03-23 | | 2. Agent Features | 6/6 | Complete | 2026-03-24 | -| 3. Operator Experience | 4/4 | Complete | 2026-03-24 | +| 3. Operator Experience | 4/5 | Gap closure | — | --- diff --git a/.planning/phases/03-operator-experience/03-05-PLAN.md b/.planning/phases/03-operator-experience/03-05-PLAN.md new file mode 100644 index 0000000..ec84697 --- /dev/null +++ b/.planning/phases/03-operator-experience/03-05-PLAN.md @@ -0,0 +1,220 @@ +--- +phase: 03-operator-experience +plan: 05 +type: execute +wave: 1 +depends_on: [] +files_modified: + - packages/gateway/gateway/main.py + - packages/portal/app/api/slack/callback/route.ts + - packages/portal/lib/api.ts + - packages/portal/lib/queries.ts + - packages/portal/app/(dashboard)/onboarding/steps/connect-channel.tsx + - packages/portal/app/(dashboard)/usage/[tenantId]/page.tsx +autonomous: true +requirements: [AGNT-07, LLM-03, PRTA-03, PRTA-04, PRTA-05, PRTA-06] +gap_closure: true + +must_haves: + truths: + - "All Phase 3 portal API endpoints (billing, channels, LLM keys, usage, webhooks) return non-404 responses" + - "Slack OAuth flow completes — Add to Slack button is enabled and callback redirects to step 2 on success" + - "Budget alerts table displays actual dollar amounts, not $undefined" + artifacts: + - path: "packages/gateway/gateway/main.py" + provides: "All Phase 3 API routers mounted on gateway FastAPI app" + contains: "include_router" + - path: "packages/portal/app/api/slack/callback/route.ts" + provides: "Correct Slack OAuth callback field check" + contains: "data.ok" + - path: "packages/portal/lib/api.ts" + provides: "SlackInstallResponse with correct field name" + contains: "url: string" + - path: "packages/portal/lib/queries.ts" + provides: "BudgetAlert with correct field name" + contains: "current_usd" + key_links: + - from: "packages/gateway/gateway/main.py" + to: "shared.api.*" + via: "include_router calls" + pattern: "app\\.include_router\\(" + - from: "packages/portal/app/(dashboard)/onboarding/steps/connect-channel.tsx" + to: "/api/portal/channels/slack/install" + via: "useSlackInstallUrl → slackInstallData.url" + pattern: "slackInstallData\\?\\.url" + - from: "packages/portal/app/(dashboard)/usage/[tenantId]/page.tsx" + to: "/api/portal/usage/{tenantId}/budget-alerts" + via: "useBudgetAlerts → alert.current_usd" + pattern: "alert\\.current_usd" +--- + + +Close 3 verification gaps that block all Phase 3 functionality at runtime. + +Purpose: Phase 3 code is complete but has wiring bugs — routers not mounted (all API calls 404), Slack OAuth field mismatches (OAuth always fails), and budget alert field mismatch (shows $undefined). These are all naming/registration fixes, not missing features. + +Output: All Phase 3 API endpoints reachable, Slack OAuth functional, budget alerts display correctly. + + + +@/home/adelorenzo/.claude/get-shit-done/workflows/execute-plan.md +@/home/adelorenzo/.claude/get-shit-done/templates/summary.md + + + +@.planning/PROJECT.md +@.planning/ROADMAP.md +@.planning/STATE.md +@.planning/phases/03-operator-experience/03-VERIFICATION.md + + + + +```python +from shared.api.billing import billing_router, webhook_router +from shared.api.channels import channels_router +from shared.api.llm_keys import llm_keys_router +from shared.api.portal import portal_router +from shared.api.usage import usage_router +``` + +Router prefixes (already set on the routers): +- portal_router: /api/portal +- billing_router: /api/portal/billing +- channels_router: /api/portal/channels +- llm_keys_router: /api/portal/tenants/{tenant_id}/llm-keys +- usage_router: /api/portal/usage +- webhook_router: /api/webhooks + + + +```python +class SlackInstallResponse(BaseModel): + url: str # NOT authorize_url + state: str +``` + +Backend callback returns: `{"ok": True, "workspace_id": ..., "team_name": ..., "tenant_id": ...}` +NOT: `{"success": true, ...}` + + + +```python +class BudgetAlert(BaseModel): + agent_id: str + agent_name: str + budget_limit_usd: float + current_usd: float # NOT current_cost_usd + status: str +``` + + + + + + + Task 1: Mount Phase 3 API routers on gateway FastAPI app + packages/gateway/gateway/main.py + + Add include_router() calls for all 6 Phase 3 routers to the gateway FastAPI app. The routers already have their prefixes set, so no prefix argument is needed. + + After the existing `app.include_router(whatsapp_router)` line (line 89), add: + + ```python + from shared.api import ( + portal_router, + billing_router, + channels_router, + llm_keys_router, + usage_router, + webhook_router, + ) + ``` + + And the corresponding include_router calls: + + ```python + app.include_router(portal_router) + app.include_router(billing_router) + app.include_router(channels_router) + app.include_router(llm_keys_router) + app.include_router(usage_router) + app.include_router(webhook_router) + ``` + + Place the import at the top with other imports. Place the include_router calls in the existing "Register channel routers" section after the whatsapp_router line. + + Update the module docstring to reflect that this service now also serves portal API routes. + + IMPORTANT: The portal_router from shared.api.portal was created in Phase 1 and is already being served — check if it is already mounted. If not, include it. If it is already mounted elsewhere (e.g., a separate service), skip it to avoid duplicate registration. Based on verification, it is NOT mounted, so include it. + + + cd /home/adelorenzo/repos/konstruct && python -c "from gateway.main import app; routes = [r.path for r in app.routes]; assert '/api/portal/billing/checkout' in routes or any('/api/portal/billing' in r for r in routes), f'billing routes missing: {routes}'; print('All Phase 3 routers mounted')" + + All 6 Phase 3 API routers (portal, billing, channels, llm_keys, usage, webhook) are mounted on the gateway FastAPI app. Requests to /api/portal/* no longer return 404. + + + + Task 2: Fix Slack OAuth and budget alert field name mismatches + + packages/portal/app/api/slack/callback/route.ts + packages/portal/lib/api.ts + packages/portal/lib/queries.ts + packages/portal/app/(dashboard)/onboarding/steps/connect-channel.tsx + packages/portal/app/(dashboard)/usage/[tenantId]/page.tsx + + + Fix 3 field name mismatches between frontend TypeScript types and backend Pydantic models: + + **Fix 1 — Slack OAuth callback (route.ts line 42-44):** + Change the type assertion and field check: + - Line 42: `as { success?: boolean; workspace_name?: string }` --> `as { ok?: boolean; workspace_name?: string }` + - Line 44: `if (!data.success)` --> `if (!data.ok)` + + **Fix 2 — SlackInstallResponse (api.ts line 161-163):** + Change the interface to match backend: + ```typescript + export interface SlackInstallResponse { + url: string; + state: string; + } + ``` + Then update ALL references to `authorize_url` in connect-channel.tsx: + - Line 79: `slackInstallData?.authorize_url` --> `slackInstallData?.url` + - Line 81: `slackInstallData.authorize_url` --> `slackInstallData.url` + - Line 135: `!slackInstallData?.authorize_url` --> `!slackInstallData?.url` + - Line 139: `!slackInstallData?.authorize_url` --> `!slackInstallData?.url` + + **Fix 3 — BudgetAlert (queries.ts line 228):** + Change the interface field: + - `current_cost_usd: number` --> `current_usd: number` + Then update the usage page reference: + - page.tsx line 268: `alert.current_cost_usd` --> `alert.current_usd` + + + cd /home/adelorenzo/repos/konstruct && grep -q "data.ok" packages/portal/app/api/slack/callback/route.ts && grep -q "url: string" packages/portal/lib/api.ts && grep -q "current_usd: number" packages/portal/lib/queries.ts && ! grep -q "authorize_url" packages/portal/lib/api.ts && ! grep -q "authorize_url" packages/portal/app/\(dashboard\)/onboarding/steps/connect-channel.tsx && ! grep -q "current_cost_usd" packages/portal/lib/queries.ts && ! grep -q "current_cost_usd" packages/portal/app/\(dashboard\)/usage/\[tenantId\]/page.tsx && echo "All field name mismatches fixed" + + Slack OAuth callback checks data.ok (not data.success). SlackInstallResponse uses url (not authorize_url). BudgetAlert uses current_usd (not current_cost_usd). All frontend field names match backend Pydantic models exactly. + + + + + +1. Gateway app mounts all Phase 3 routers — no portal API call returns 404 +2. Slack install URL field is `url` everywhere in frontend — Add to Slack button is enabled when data loads +3. Slack callback checks `data.ok` — successful OAuth redirects to step 2 +4. Budget alert field is `current_usd` everywhere in frontend — dollar amounts display correctly +5. No stale field names remain: `authorize_url`, `data.success` (in callback), `current_cost_usd` are gone from frontend code + + + +- All 6 Phase 3 API routers are mounted on the gateway FastAPI app +- Zero instances of `authorize_url` in portal frontend code +- Zero instances of `current_cost_usd` in portal frontend code +- Slack callback route checks `data.ok` not `data.success` +- Gateway app imports compile without errors + + + +After completion, create `.planning/phases/03-operator-experience/03-05-SUMMARY.md` +