Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion QUICK_START.md
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,9 @@ curl http://localhost:4111/health

### Check Database
```bash
curl http://localhost:4111/api/linear/issues
```bash
# Replace {projectId} with your project's id
curl http://localhost:4111/projects/{projectId}/linear/issues
# Should return grouped issues (or empty list if no issues yet)
```

Expand Down
13 changes: 9 additions & 4 deletions docs/HANDOFF-MULTI-TENANT-HARDENING-2026-04-17.md
Original file line number Diff line number Diff line change
Expand Up @@ -313,10 +313,15 @@ Aterrizó la card de Linear + cleanup del `/settings` legacy + flip del `scoped-
- **Resend card**: `fromEmail` como input manual (la API no tiene `/me`); test probando `GET /domains`. Meta = `{ fromEmail }`. Migrar `reporter_email` de `localStorage` (hoy en `chat.tsx:352,498,515`) a `meta.defaultReporterEmail`.
- **Slack card**: `auth.test` + `conversations.list` → picker de channel → guardar `channelId` + `channelName` en meta.
- **GitHub card**: `GET /user` + owner/repo como 2do paso (picker tipo GitHub App, o input manual con validación contra `GET /repos/:owner/:repo`).
- **Borrar endpoints legacy del runtime**: `/api/config/status`, `/api/linear/members` (global), `/api/linear/webhook/setup` + la ruta `/settings` completa + los imports en `mastra/index.ts`. Hacerlo después de los 3 cards arriba.
- **`webhook_secrets` refactor a PK compuesta**: sigue global. Cuando Linear card agregue "Register webhook" (dentro de #5c o slice dedicado), migrar a `(project_id, provider)` con `setWebhookSecret(projectId, provider, secret)`. Incluye cambio en el handler `/webhooks/linear` para lookup por project.
- **Drop columnas plaintext en `projects`**: `linear_token`, `linear_webhook_id/url`, `github_token`, `github_repo_owner/name`, `slack_enabled`, `slack_channel_id`, `slack_webhook_url`, `resend_api_key`, `linear_team_id` (leído solo por env-fallback hoy; una vez que todos los projects migren, se puede dropear). Después del cleanup de endpoints.

> **COMPLETADO en TRI-61 / TRI-66-68 (2026-05-12)**: Los endpoints legacy fueron eliminados:
> - `/api/config/status`, `/api/linear/members` (global), `POST /api/wiki/generate`, `GET /api/wiki/status`
> - `/api/linear/issues`, `/api/linear/sync`, `/api/linear/sync/status`, `/api/linear/cycle/active`
> - Ruta frontend `/settings` eliminada
> - Webhook per-tenant implementado en `/projects/:id/linear/webhook/setup`

- **Borrar endpoints legacy del runtime**: ✅ HECHO en TRI-61/TRI-66 (ver arriba).
- **`webhook_secrets` refactor a PK compuesta**: ✅ HECHO en TRI-67. `setWebhookSecret` ahora acepta `projectId`; el handler `/webhooks/linear` lee `?projectId=` del query string.
- **Drop columnas plaintext en `projects`**: ✅ HECHO en TRI-61.
### 5c. UI BYO keys — Slack/Resend/GitHub ✅ DONE (2026-04-22)

Cerró el bucle de 5 providers en `/integrations`. 2 commits: `d3d9c7c` (backend) + `4a350be` (frontend).
Expand Down
48 changes: 27 additions & 21 deletions docs/HANDOFF-PR-READINESS-2026-04-09.md
Original file line number Diff line number Diff line change
Expand Up @@ -130,21 +130,21 @@ curl -s --max-time 15 -N http://127.0.0.1:4111/api/agents/orchestrator/stream \
```
✅ Must return `text-delta` events with actual text content.

2. **Linear issues endpoint**:
2. **Linear issues endpoint** (per-project since TRI-66):
```bash
curl -s http://127.0.0.1:4111/api/linear/issues | python3 -m json.tool | head -20
curl -s http://127.0.0.1:4111/projects/{projectId}/linear/issues | python3 -m json.tool | head -20
```
✅ Must return `{"success":true,"data":{...}}` with grouped issues (already works, verified).
✅ Must return `{"success":true,"data":{...}}` with grouped issues.

3. **Linear members endpoint**:
3. **Linear members endpoint** (per-project since TRI-66):
```bash
curl -s http://127.0.0.1:4111/api/linear/members | python3 -m json.tool
curl -s http://127.0.0.1:4111/projects/{projectId}/linear/members | python3 -m json.tool
```
✅ Must return team members array.
✅ Must return team members array for the project's Linear team.

4. **Wiki status endpoint** (stub):
4. **Wiki status endpoint** (per-project since TRI-61):
```bash
curl -s http://127.0.0.1:4111/api/wiki/status
curl -s http://127.0.0.1:4111/projects/{projectId}/wiki/status
```
✅ Must return `{"success":true,"data":{"total":0,"processed":0,"done":true}}`.

Expand Down Expand Up @@ -181,17 +181,17 @@ curl -s http://127.0.0.1:4111/health
- Issues should populate from Linear API
- If LINEAR_API_KEY is missing, should show error gracefully (not crash)

10. **Settings page** — open `http://localhost:3001/settings`:
- Team members section should list Linear team members
- Wiki generation form should be present
- Token validation UI should work
10. **Settings page** — was replaced by `/integrations` and `/onboarding` (TRI-61):
- Go to `http://localhost:3001/integrations` to manage per-project API keys
- Go to `http://localhost:3001/onboarding` for the setup wizard

11. **Routing through Caddy** — verify all these return 200:
```bash
curl -s -o /dev/null -w '%{http_code}' http://127.0.0.1:3001/ # SPA
curl -s -o /dev/null -w '%{http_code}' http://127.0.0.1:3001/chat # SPA route
curl -s -o /dev/null -w '%{http_code}' http://127.0.0.1:3001/board # SPA route
curl -s -o /dev/null -w '%{http_code}' http://127.0.0.1:3001/settings # SPA route
curl -s -o /dev/null -w '%{http_code}' http://127.0.0.1:3001/onboarding # SPA route
curl -s -o /dev/null -w '%{http_code}' http://127.0.0.1:3001/integrations # SPA route
curl -s -o /dev/null -w '%{http_code}' http://127.0.0.1:3001/config.json # Config
curl -s -o /dev/null -w '%{http_code}' http://127.0.0.1:3001/health # Proxy
```
Expand Down Expand Up @@ -265,8 +265,9 @@ Body should include:
```
Frontend (Caddy :3001)
├── /chat → POST /chat → runtime:4111/chat (SSE stream)
├── /board → GET /api/linear/issues → runtime:4111/api/linear/issues
├── /settings → GET /api/linear/members, POST /api/wiki/generate
├── /board → GET /projects/:id/linear/issues → runtime:4111/projects/:id/linear/issues
├── /onboarding → Setup wizard for per-project integrations
├── /integrations → Per-project API key management
└── /config.json → {"apiUrl": "/api"}

Runtime (Mastra/Hono :4111)
Expand All @@ -284,14 +285,19 @@ Runtime (Mastra/Hono :4111)
│ └── GitHub: commentOnGitHubPR
├── Workflows
│ └── triage-workflow (8 steps: intake→triage→dedup→ticket→notify→suspend→verify→notify-resolution)
├── API Routes (custom Hono)
│ ├── GET /api/linear/issues
│ ├── GET /api/linear/members
│ ├── POST /api/wiki/generate (stub)
│ ├── GET /api/wiki/status (stub)
│ ├── POST /api/webhooks/linear (stub)
├── API Routes (custom Hono) — all per-project since TRI-61/TRI-66
│ ├── GET /projects/:id/linear/issues
│ ├── GET /projects/:id/linear/cycle
│ ├── GET /projects/:id/linear/members
│ ├── POST /projects/:id/linear/sync
│ ├── GET /projects/:id/linear/sync/status
│ ├── POST /projects/:id/linear/webhook/setup
│ ├── POST /projects/:id/wiki/generate
│ ├── GET /projects/:id/wiki/status
│ ├── POST /api/webhooks/linear
│ └── POST /api/workflows/triage-workflow/trigger
└── Storage: LibSQL (libsql:8080)
```

Langfuse (6 containers, observability)
├── langfuse-web :3000
Expand Down
121 changes: 64 additions & 57 deletions docs/superpowers/specs/2026-04-08-mvp-pipeline-design.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
# MVP Pipeline Design — Triage SRE Agent

**Date:** 2026-04-08
> **DEPRECATED — Historical reference only (2026-04-08)**
> Updated 2026-05-12 to reflect what was actually built:
> - All global endpoints (`/api/linear/*`, `/api/wiki/*`) were replaced by per-project scoped routes (`/projects/:id/linear/*`, `/projects/:id/wiki/*`) in TRI-61.
> - The `/settings` page was replaced by `/integrations` + `/onboarding` in TRI-61.
> - Webhook setup moved from global `/api/linear/webhook/setup` to per-project `/projects/:id/linear/webhook/setup` in TRI-67.
> - Board data fetching migrated from `/api/linear/issues` to `/projects/:id/linear/issues` in TRI-66.

**Date:** 2026-04-08
**Authors:** Lalo, Koki
**Session:** planning
**Session:** planning
**Status:** Approved for implementation

---

## 1. Scope
Expand Down Expand Up @@ -100,28 +106,27 @@ const orchestratorModel = openrouter(MODELS.orchestrator, {

## 3. First-time Onboarding

### Settings page (`/settings`)

Fields:
- **Linear API Token** — text input, validated by calling `GET /api/linear/members` on save. Badge: "Connected" / "Invalid token".
- **GitHub Repo URL** — text input (public repos only for MVP). Button: "Import & Generate Wiki".
- **Team Members** — "Sync from Linear" button → fetches and displays list with name + email.
> ### Onboarding wizard (`/onboarding`) — reemplazó `/settings` en TRI-61
> Campos por provider en wizard de 5 pasos:
> - **OpenRouter** — API key, test con `GET /models`.
> - **Linear** — PAT, test con `viewer { teams { nodes { id name key } } }`, picker de team → save con `meta.teamId/teamName/teamKey`.
> - **GitHub** — PAT, auto-skipped si repo no es github.com, probe con `GET /user` + `GET /repos/:owner/:repo`.
> - **Slack** — webhook URL, test con `auth.test` + `conversations.list`, picker de channel.
> - **Resend** — API key, test con `GET /domains`, input de `fromEmail`.
> Todo vive en `frontend/src/routes/onboarding.lazy.tsx` (~870 líneas).

### Wiki generation endpoint
> **Actualizado TRI-61:** `POST /projects/:id/wiki/generate` — per-project Hono route.
`POST /projects/:id/wiki/generate` — Hono route en el servidor Mastra (per-project):

`POST /api/wiki/generate` — new Hono route on the Mastra server:

1. Accept `{ repoUrl: string }` in request body
1. Accept `{ repoUrl: string }` in request body (reads from project's `repository_url`)
2. `git clone --depth 3 <repoUrl> /tmp/wiki-repo-<timestamp>`
3. Walk the cloned repo (ignore `node_modules`, `.git`, binary files)
4. For each file: call `generateWikiTool` with file content → LLM produces a structured summary (purpose, key functions, dependencies)
5. Chunk summaries → embed → store in LibSQL `wiki_chunks` table with DiskANN index
6. Update progress counter in a simple in-memory or LibSQL status row

`GET /api/wiki/status` — returns `{ total: number, processed: number, done: boolean }`.

Frontend polls every 2 seconds while wiki is generating; shows progress bar.
4. For each file: call `generateWikiTool` with file content → LLM produces a structured summary
5. Chunk summaries → embed → store in LibSQL `wiki_chunks` table
6. Update `projects.status` and `projects.documents_count/chunks_count`

`GET /projects/:id/wiki/status` — returns `{ total: number, processed: number, done: boolean }` (per-project).
**Graphify (separate, demo only):** Run `graphify` CLI on the same cloned repo to produce `graph.html` for the interactive knowledge graph visualization in the demo video. Not part of the runtime query pipeline.

---
Expand Down Expand Up @@ -372,39 +377,41 @@ If `verdict === 'approve'` (no issues found): **do not post** a comment. Only po

---

## 8. Frontend: Kanban Board

### New runtime endpoint: `GET /api/linear/issues`

Hono route in Mastra server:
- Calls `linearClient.issues({ filter: { team: { id: { eq: LINEAR_CONSTANTS.TEAM_ID } } }, first: 50 })`
- Groups by `state.name`
- Returns `{ backlog: Issue[], todo: Issue[], inProgress: Issue[], inReview: Issue[], done: Issue[] }`

### `board.lazy.tsx` update

- TanStack Query: `useQuery({ queryKey: ['linear-issues'], queryFn: () => apiFetch('/api/linear/issues'), refetchInterval: 30_000 })`
- Render each column with real issue cards
- Issue card: title, severity label badge, assignee initials avatar, Linear URL link

---

## 9. Frontend: Settings Page

### New runtime endpoints

- `GET /api/linear/members` — proxies `getLinearTeamMembers` for the configured team
- `POST /api/wiki/generate` — triggers wiki generation (see §3)
- `GET /api/wiki/status` — returns generation progress

### `settings.lazy.tsx` update

Three sections:
1. **Integrations** — Linear token input + validation badge, GitHub repo URL input + "Generate Wiki" button
2. **Wiki** — progress bar (polling `/api/wiki/status`), "last generated" timestamp, file count
3. **Team Members** — "Sync from Linear" button + member list (name, email, displayName)

---
> ## 8. Frontend: Kanban Board
> **Actualizado TRI-66:** Todos los endpoints ahora son per-project.

> ### Endpoint per-project: `GET /projects/:id/linear/issues`
> Hono route en Mastra server (per-project):
> - Resuelve API key + teamId desde `project_integrations` (no más `LINEAR_CONSTANTS.TEAM_ID` global)
> - Llama `linearClient.issues({ filter: { team: { id: { eq: teamId } } }, first: 50 })`
> - Groups by `state.name`
> - Returns grouped issues

> ### `board.lazy.tsx`
> - TanStack Query: `useQuery({ queryKey: ['linear-issues', projectId], queryFn: () => apiFetch('/projects/\${projectId}/linear/issues') })`
> - Sync: `POST /projects/\${projectId}/linear/sync`
> - Cycle: `GET /projects/\${projectId}/linear/cycle`
> - Render each column with real issue cards
> - Issue card: title, severity label badge, assignee initials avatar, Linear URL link

> ---
>
> ## 9. Frontend: Integrations Page
> **Actualizado TRI-61:** `/settings` fue reemplazado por `/integrations` + `/onboarding`.

> ### Endpoints per-project disponibles
> - `GET /projects/:id/linear/members` — proxies `getLinearTeamMembers` para el team del proyecto
> - `POST /projects/:id/wiki/generate` — triggers wiki generation
> - `GET /projects/:id/wiki/status` — returns generation progress
> - `POST /projects/:id/linear/webhook/setup` — registers per-project Linear webhook (TRI-67)
> - `POST /projects/:id/linear/sync` — manual sync (TRI-66)
> - `GET /projects/:id/linear/sync/status` — sync progress (TRI-66)

> ### `integrations.lazy.tsx`
> - Cards por provider: OpenRouter, Linear, GitHub, Slack, Resend
> - Cada card: test → save → reconfigure/delete
> - `WizardCta` redirige a `/onboarding` si faltan integraciones requeridas
> - `onboarding.lazy.tsx`: wizard de 5 pasos con auto-skip de GitHub para repos no-github

## 10. Multiple Issues (MVP vs Stretch)

Expand Down Expand Up @@ -463,8 +470,8 @@ Note: Minimax via OpenRouter uses `OPENROUTER_API_KEY` — no separate key neede
| `commentOnGitHubPRTool` | `runtime/src/mastra/tools/github.ts` | TODO |
| Update orchestrator (model + tools) | `runtime/src/mastra/agents/orchestrator.ts` | TODO |
| Wire all 8 workflow steps | `runtime/src/mastra/workflows/triage-workflow.ts` | TODO |
| Hono routes: /api/linear/issues, /api/linear/members, /api/wiki/generate, /api/wiki/status | `runtime/src/mastra/index.ts` | TODO |
| Workflow trigger endpoint | Mastra v1.24 exposes `POST /api/workflows/:id/trigger` by default — verify at runtime startup; if not present, add custom Hono route that calls `mastra.getWorkflow('triage-workflow').createRun().start(input)` | TODO |
| `onCreateTicket` in chat.tsx | `frontend/src/routes/chat.tsx` | TODO |
| Kanban data in board.lazy.tsx | `frontend/src/routes/board.lazy.tsx` | TODO |
| Settings page | `frontend/src/routes/settings.lazy.tsx` | TODO |
|| Hono routes per-project: `/projects/:id/linear/{issues,cycle,members,sync,sync/status,webhook/setup}`, `/projects/:id/wiki/{generate,status}` | `runtime/src/lib/scoped-routes.ts` | ✅ DONE (TRI-61/TRI-66) |
|| Workflow trigger endpoint | `POST /api/workflows/triage-workflow/trigger` expuesto por Mastra | ✅ DONE |
|| `onCreateTicket` in chat.tsx | `frontend/src/routes/chat.tsx` | ✅ DONE |
|| Kanban data in board.lazy.tsx — per-project endpoints | `frontend/src/routes/board.lazy.tsx` | ✅ DONE (TRI-66) |
|| Integrations page + onboarding wizard | `frontend/src/routes/integrations.lazy.tsx` + `onboarding.lazy.tsx` | ✅ DONE (TRI-61) |
24 changes: 12 additions & 12 deletions frontend/src/routes/board.lazy.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -530,33 +530,33 @@ function BoardPage() {
const queryClient = useQueryClient();

const { data, isLoading, error } = useQuery<GroupedIssues>({
queryKey: ['linear-issues'],
queryFn: () => apiFetch('/linear/issues'),
queryKey: ['linear-issues', currentProjectId],
queryFn: () => apiFetch(`/projects/${currentProjectId}/linear/issues`),
staleTime: 300_000, // 5 minutes — data is cached on the server
enabled: !!currentProjectId,
});

const { data: cycleData } = useQuery<CycleData | null>({
queryKey: ['linear-cycle-active'],
queryFn: () => apiFetch('/linear/cycle/active'),
queryKey: ['linear-cycle-active', currentProjectId],
queryFn: () => apiFetch(`/projects/${currentProjectId}/linear/cycle`),
staleTime: 300_000,
enabled: !!currentProjectId,
});

// Sync status query
const { data: syncStatus } = useQuery<{ lastSyncedAt: string | null; syncInProgress: boolean }>({
queryKey: ['linear-sync-status'],
queryFn: () => apiFetch('/linear/sync/status'),
queryKey: ['linear-sync-status', currentProjectId],
queryFn: () => apiFetch(`/projects/${currentProjectId}/linear/sync/status`),
refetchInterval: 30_000,
enabled: !!currentProjectId,
});

// Manual sync mutation
const syncMutation = useMutation({
mutationFn: () => apiFetch('/linear/sync', { method: 'POST' }),
mutationFn: () => apiFetch(`/projects/${currentProjectId}/linear/sync`, { method: 'POST' }),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['linear-issues'] });
queryClient.invalidateQueries({ queryKey: ['linear-sync-status'] });
queryClient.invalidateQueries({ queryKey: ['linear-issues', currentProjectId] });
queryClient.invalidateQueries({ queryKey: ['linear-sync-status', currentProjectId] });
},
});

Expand All @@ -569,10 +569,10 @@ function BoardPage() {
// Trigger a sync on mount (session start)
useEffect(() => {
if (currentProjectId) {
apiFetch('/linear/sync', { method: 'POST' })
apiFetch(`/projects/${currentProjectId}/linear/sync`, { method: 'POST' })
.then(() => {
queryClient.invalidateQueries({ queryKey: ['linear-issues'] });
queryClient.invalidateQueries({ queryKey: ['linear-sync-status'] });
queryClient.invalidateQueries({ queryKey: ['linear-issues', currentProjectId] });
queryClient.invalidateQueries({ queryKey: ['linear-sync-status', currentProjectId] });
})
.catch(() => { /* sync failure is non-fatal */ });
}
Expand Down
Loading
Loading