Files
konstruct/.planning/phases/03-operator-experience/03-05-PLAN.md

9.3 KiB

phase, plan, type, wave, depends_on, files_modified, autonomous, requirements, gap_closure, must_haves
phase plan type wave depends_on files_modified autonomous requirements gap_closure must_haves
03-operator-experience 05 execute 1
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
true
AGNT-07
LLM-03
PRTA-03
PRTA-04
PRTA-05
PRTA-06
true
truths artifacts key_links
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
path provides contains
packages/gateway/gateway/main.py All Phase 3 API routers mounted on gateway FastAPI app include_router
path provides contains
packages/portal/app/api/slack/callback/route.ts Correct Slack OAuth callback field check data.ok
path provides contains
packages/portal/lib/api.ts SlackInstallResponse with correct field name url: string
path provides contains
packages/portal/lib/queries.ts BudgetAlert with correct field name current_usd
from to via pattern
packages/gateway/gateway/main.py shared.api.* include_router calls app.include_router(
from to via pattern
packages/portal/app/(dashboard)/onboarding/steps/connect-channel.tsx /api/portal/channels/slack/install useSlackInstallUrl → slackInstallData.url slackInstallData?.url
from to via pattern
packages/portal/app/(dashboard)/usage/[tenantId]/page.tsx /api/portal/usage/{tenantId}/budget-alerts useBudgetAlerts → alert.current_usd 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.

<execution_context> @/home/adelorenzo/.claude/get-shit-done/workflows/execute-plan.md @/home/adelorenzo/.claude/get-shit-done/templates/summary.md </execution_context>

@.planning/PROJECT.md @.planning/ROADMAP.md @.planning/STATE.md @.planning/phases/03-operator-experience/03-VERIFICATION.md
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
class SlackInstallResponse(BaseModel):
    url: str       # NOT authorize_url
    state: str

Backend callback returns: {"ok": True, "workspace_id": ..., "team_name": ..., "tenant_id": ...} NOT: {"success": true, ...}

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

<success_criteria>

  • 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 </success_criteria>
After completion, create `.planning/phases/03-operator-experience/03-05-SUMMARY.md`