docs(03): create gap closure plan for router mounting and field name fixes
This commit is contained in:
@@ -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 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 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
|
## 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
|
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
|
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
|
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:
|
Plans:
|
||||||
- [ ] 03-01-PLAN.md — Backend foundation: DB migrations, billing models, encryption service, channel/billing/usage API endpoints, audit logger token metadata
|
- [ ] 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-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-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-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
|
## Progress
|
||||||
|
|
||||||
@@ -83,7 +84,7 @@ Phases execute in numeric order: 1 -> 2 -> 3
|
|||||||
|-------|----------------|--------|-----------|
|
|-------|----------------|--------|-----------|
|
||||||
| 1. Foundation | 4/4 | Complete | 2026-03-23 |
|
| 1. Foundation | 4/4 | Complete | 2026-03-23 |
|
||||||
| 2. Agent Features | 6/6 | Complete | 2026-03-24 |
|
| 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 | — |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
220
.planning/phases/03-operator-experience/03-05-PLAN.md
Normal file
220
.planning/phases/03-operator-experience/03-05-PLAN.md
Normal file
@@ -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"
|
||||||
|
---
|
||||||
|
|
||||||
|
<objective>
|
||||||
|
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.
|
||||||
|
</objective>
|
||||||
|
|
||||||
|
<execution_context>
|
||||||
|
@/home/adelorenzo/.claude/get-shit-done/workflows/execute-plan.md
|
||||||
|
@/home/adelorenzo/.claude/get-shit-done/templates/summary.md
|
||||||
|
</execution_context>
|
||||||
|
|
||||||
|
<context>
|
||||||
|
@.planning/PROJECT.md
|
||||||
|
@.planning/ROADMAP.md
|
||||||
|
@.planning/STATE.md
|
||||||
|
@.planning/phases/03-operator-experience/03-VERIFICATION.md
|
||||||
|
|
||||||
|
<interfaces>
|
||||||
|
<!-- Backend routers to mount (from packages/shared/shared/api/__init__.py) -->
|
||||||
|
|
||||||
|
```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
|
||||||
|
|
||||||
|
<!-- Backend Slack response (from packages/shared/shared/api/channels.py:129) -->
|
||||||
|
|
||||||
|
```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, ...}`
|
||||||
|
|
||||||
|
<!-- Backend BudgetAlert (from packages/shared/shared/api/usage.py:161) -->
|
||||||
|
|
||||||
|
```python
|
||||||
|
class BudgetAlert(BaseModel):
|
||||||
|
agent_id: str
|
||||||
|
agent_name: str
|
||||||
|
budget_limit_usd: float
|
||||||
|
current_usd: float # NOT current_cost_usd
|
||||||
|
status: str
|
||||||
|
```
|
||||||
|
</interfaces>
|
||||||
|
</context>
|
||||||
|
|
||||||
|
<tasks>
|
||||||
|
|
||||||
|
<task type="auto">
|
||||||
|
<name>Task 1: Mount Phase 3 API routers on gateway FastAPI app</name>
|
||||||
|
<files>packages/gateway/gateway/main.py</files>
|
||||||
|
<action>
|
||||||
|
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.
|
||||||
|
</action>
|
||||||
|
<verify>
|
||||||
|
<automated>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')"</automated>
|
||||||
|
</verify>
|
||||||
|
<done>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.</done>
|
||||||
|
</task>
|
||||||
|
|
||||||
|
<task type="auto">
|
||||||
|
<name>Task 2: Fix Slack OAuth and budget alert field name mismatches</name>
|
||||||
|
<files>
|
||||||
|
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
|
||||||
|
</files>
|
||||||
|
<action>
|
||||||
|
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`
|
||||||
|
</action>
|
||||||
|
<verify>
|
||||||
|
<automated>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"</automated>
|
||||||
|
</verify>
|
||||||
|
<done>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.</done>
|
||||||
|
</task>
|
||||||
|
|
||||||
|
</tasks>
|
||||||
|
|
||||||
|
<verification>
|
||||||
|
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
|
||||||
|
</verification>
|
||||||
|
|
||||||
|
<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>
|
||||||
|
|
||||||
|
<output>
|
||||||
|
After completion, create `.planning/phases/03-operator-experience/03-05-SUMMARY.md`
|
||||||
|
</output>
|
||||||
Reference in New Issue
Block a user