Files
waggle-os/docs/ux-refactor/deltas/backend-api-delta.md
Oleg Maslov 0c3e2ead3b
Some checks failed
Installer Smoke / installer-smoke (push) Has been cancelled
moving
2026-09-02 10:10:29 +02:00

384 lines
39 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Backend API Delta — Waggle OS UX Refactor (Master List)
> **Purpose.** The single, consolidated, de-duplicated, **ordered** master list of every backend
> endpoint the UX refactor must build or extend, plus every `.mind`/relational schema migration
> required. This is the build contract for the LOCKED execution model: **in-place incremental
> refactor** of `apps/web` + targeted **local Fastify sidecar** extensions. Every endpoint is built
> over the existing substrate — no new database, minimal-to-zero SQLite migration (see §M).
>
> **Sources (all read & grounded):** PRD §16 (`docs/.../Waggle_OS_UX_Refactor_PRD.md:1060-1158`),
> PRD §8/§21 phase+sprint structure (`:212-253`, `:1304-1374`), PRD §15 data model (`:918-1057`);
> the 22 gap cards (`docs/ux-refactor/gap-cards/S00S21`); the route/substrate inventories
> (`docs/ux-refactor/_inventory/{backend-routes,substrate-types,frontend}.md`); the audited
> backend-map (`docs/backend-map/sections/03a03g`); and spot-verified live source under
> `packages/server/src/local/routes/*.ts`, `packages/server/src/local/workspace-state.ts`,
> `packages/hive-mind-core/src/{workspace-manager,mind/schema,mind/db}.ts`,
> `packages/core/src/{install-audit,cron-store}.ts`.
>
> **Scope.** Everything is the **Local Sidecar** (`packages/server/src/local/index.ts` →
> `buildLocalServer()`, loopback `:3333`, flat `/api/*`, Bearer session-token). The desktop frontend
> talks ONLY to this server. The Clerk-gated **Cloud** server (`packages/server/src/routes/*.ts`) is
> out of scope — where its routes collide with PRD paths (notably `/api/agents/*`) the sidecar work is
> still **net-new locally** and flagged.
---
## How to read this
Each row carries:
- **Method + Path** — the PRD/refactor contract path (PRD-literal where §16 names it).
- **Disposition** — `NET-NEW` (no route serves this; build it) · `EXTEND` (a real handler exists;
add alias/param/field/behavior) · `NET-NEW (thin dispatcher/alias)` (new path, delegates wholly to
existing handlers, no new logic).
- **Build target** — the exact route file to create or the existing file/handler/builder to extend
(with line where load-bearing). **Verified absent:** `agents.ts`, `artifacts.ts`, `home.ts`,
`command.ts`, `mcps.ts`, `automations.ts`, `quick-capture.ts` do **not** exist under
`packages/server/src/local/routes/` (grep-confirmed) — all are net-new files.
- **Substrate** — the store(s) it reads/writes.
- **Shape** — a 35 line request/response sketch.
- **Screens** — gap-card IDs that consume it.
Counts are in §Counts at the bottom. **De-dup note:** §16 lists 65 endpoints but several are consumed
by multiple screens (e.g. `/api/workspaces/:id/state` → S01+S02; `/api/skills/*` → S06+S19;
`/api/agents/*` → S09+S18; the cron→automations aliases → S11+S20; connector/MCP → S07+S08+S14+S17;
harvest → S15+S16). This master list states each endpoint **once**, attributing all consuming screens.
Endpoints that **EXIST as-is** with zero backend work (e.g. `GET /api/workspaces`, `GET /api/connectors`,
`GET /api/memory/graph`, the team CRUD core, the harvest engine) are **excluded** — they need only FE
wiring. Only NET-NEW + EXTEND backend work is listed.
---
## Phase 0 — Architecture alignment (PRD §8 Phase 0 / §21 Sprint 1)
No new endpoints. Backend-relevant work is **type alignment + shared additive fields** that later
phases write through. Land these first because Phase 15 write paths depend on them.
| Item | Disposition | Build target | Substrate | Notes / shape |
|---|---|---|---|---|
| `WorkspaceConfig` V2 additive fields | EXTEND (no route, no migration) | `packages/hive-mind-core/src/workspace-manager.ts:5-58` (interface) + `CreateWorkspaceOptions :60-95` | `workspace.json` (file, NOT SQLite) | Add optional `description, type(WorkspaceType), status('active'\|'paused'\|'archived'), agentIds[], connectorIds[], mcpIds[], updatedAt, lastActiveAt`. Default `status:'active'`; derive `type` from `templateId`/`group` for existing workspaces. **No DB migration** (JSON file). Consumed by S02/S17. |
| Stamp `updatedAt` on write | EXTEND | `workspace-manager.ts:222` (`update()` currently stamps nothing but `riskClassifiedAt`) | `workspace.json` | One-line write-side touch. |
| Stamp `lastActiveAt` | EXTEND | agent loop / chat route write-back | `workspace.json` | Currently `lastActive` is DERIVED at read from session mtimes (`workspace-context.ts:375-387`); persist it. |
| Shared frontend type unions | EXTEND (FE only) | `apps/web/src/lib/types.ts` | — | Add `WorkspaceType, Scope, Confidence, MemoryKind, ArtifactKind, AgentType, AutonomyLevel, ExtensionType` (PRD §15.2). Reconcile FE `MemoryFrame`/`Workspace` lossy projections against API shapes (substrate-types §e). |
---
## Phase 1 — Core runtime: Home, Workspace Desktop, Command Center (PRD §8 Phase 1 / §21 Sprint 23)
### 1a. Home Cockpit + Quick Capture (S01) — new `home.ts` + extend `memory.ts`
| Method + Path | Disposition | Build target | Substrate | Shape | Screens |
|---|---|---|---|---|---|
| `GET /api/home/briefing` | **NET-NEW** | new `routes/home.ts`; fans `buildWorkspaceState()` (`workspace-state.ts:234`) / `buildWorkspaceNowBlock()` (`workspace-context.ts:191`) over `WorkspaceManager.list()` and ranks | `memory_frames`, session JSONL, `awareness`, `cron_schedules`, `identity` (for name) | `→ { greeting, userName, date, workspaces:[{id,name,rank,summary,pending,nextActions}], suggestedActions[] }`. Cross-workspace ranking aggregator (per-workspace builder is the seed; no cross-WS ranker exists today). | S01 |
| `GET /api/home/overnight` | **NET-NEW** | new `routes/home.ts`; aggregates over since-last-login window | `events`/`ai_interactions`, `notifications`, `cron_execution_history`, `memory_frames` | `?since=<iso> → { memoriesAdded, artifactsCreated, automationsCompleted, failures:[{source,error}], window:{from,to} }`. No time-windowed delta exists today. | S01 |
| `POST /api/quick-capture` | **EXTEND** (thin handler delegating to memory write) | `routes/memory.ts` `POST /api/memory/frames` (`:248`) as the write primitive; new thin handler or alias | `memory_frames` (personal `.mind`) + `awareness` (for `kind:task`) + `POST /api/ingest` (for `kind:file`) | `{ kind:'note'\|'task'\|'link'\|'file', content, workspaceId? } → { frameId }`. Defaults to personal mind, stamps `source:'quick-capture'`; `task` also writes an awareness row so it surfaces in `nextActions`. **No migration.** | S01 |
### 1b. Workspace Desktop (S02) — extend `workspaces.ts`
| Method + Path | Disposition | Build target | Substrate | Shape | Screens |
|---|---|---|---|---|---|
| `GET /api/workspaces/:id/state` | **EXTEND** (thin route) | new thin route in `workspaces.ts` returning the `buildWorkspaceState()` sub-object already computed inside `/context` (`workspaces.ts:311`) | `memory_frames` + session JSONL + `awareness` | `→ WorkspaceState { active, openQuestions, pending, blocked, completed, stale, recentDecisions, nextActions }` (`workspace-state.ts:38-55`). `pending`+`blocked` seed the Tasks tab. No migration. | S01, S02 |
| `GET /api/workspaces/:id/activity` | **EXTEND** (thin alias) | new thin route over `GET /api/events?workspaceId=` (`events.ts`) | `ai_interactions` / `execution_traces` / `audit_events` | `?limit= → { events:[{ts,type,actor,summary}] }`. Per-workspace audit feed. No migration. | S02 |
> **No backend work** for S02 Tasks (`tasks.ts` CRUD EXISTS), Members (`/api/team/members` EXISTS),
> or status-bar feeds (`/api/fleet`, `/api/cron`, `/api/capabilities/status` all EXIST — compose
> client-side; an aggregate `/status` route is optional and deferred). Artifacts tab is **S05's**
> scope; S02 ships an interim file-registry view via existing `GET /api/workspaces/:id/files`.
### 1c. Command Center (Ctrl+K) (S00, S03) — new `command.ts`
| Method + Path | Disposition | Build target | Substrate | Shape | Screens |
|---|---|---|---|---|---|
| `GET /api/command/search?q=` | **NET-NEW** | new `routes/command.ts` federating route | reads `/api/memory/search` (`memory.ts:120`), `WorkspaceManager.list()`, `/api/skills`, `/api/workspaces/:id/sessions/search` (`sessions.ts`) — **no new store** | `?q=&scope= → { results:[CommandResult{id,kind:'search'\|'launch'\|'create'\|'run'\|'navigate'\|'extend', objectType, title, subtitle?, score, requiresApproval?, payload?}] }`. Federates over ~4 substrates. | S00, S03 |
| `POST /api/command/execute` | **EXTEND** | existing `POST /api/commands/execute` (note **plural**, `commands.ts`) — extend for navigate/create/run/extend dispatch, OR add a singular `/command/execute` alias | command runtime + dispatch targets | `{ command, objectType?, payload?, workspaceId? } → { ok, result? }`. Current runs slash-commands with a subset CommandContext; PRD's palette execute is broader. | S00, S03 |
| `GET /api/command/recent` | **NET-NEW** (or client-derive first) | new `routes/command.ts` reading `ai_interactions` (or derive from session/event history) | `ai_interactions` (read-only) | `→ { recent:[{command,ts,objectType}] }`. No schema change. Cheapest v1 = client-side from session history; promote to server when a consumer needs cross-device. | S03 |
| `GET /api/command/suggestions` | **NET-NEW** | new `routes/command.ts` reusing `deriveNextActions` (`workspace-state.ts:182-218`) + folding in `/api/skills/suggestions` | read-only over `memory_frames`/`awareness`/`cron` | `→ { suggestions:[CommandResult] }`. No migration. | S03 |
---
## Phase 2 — Work layer: Memory Center, Artifact Center, Workspace Creation, Onboarding (PRD §8 Phase 2 / §21 Sprint 45)
### 2a. Memory Center (S04, S16) — extend `memory.ts` + ONE optional migration
| Method + Path | Disposition | Build target | Substrate | Shape | Screens |
|---|---|---|---|---|---|
| `GET /api/memory` | **EXTEND** (alias) | accept bare path on existing `GET /api/memory/frames` (`memory.ts:188`) | `memory_frames` | `?scope=&kind=&confidence=&status= → { frames:[...] }`. Alias only. | S04 |
| `GET /api/memory/:id` | **NET-NEW** (thin) | new thin read in `memory.ts` over `FrameStore.getById(id)` (no `GET .../frames/:id` exists today) | `memory_frames` | `→ { frame }`. Drawer detail. No migration. | S04 |
| `POST /api/memory` | **EXTEND** (alias) | alias on `POST /api/memory/frames` (`memory.ts:248`) | `memory_frames` | `{ kind,title,content,scope,tags? } → { id }`. | S04 |
| `PATCH /api/memory/:id` | **EXTEND** | extend `PUT /api/memory/frames/:id` (`memory.ts:448`) to accept `PATCH` + bare `:id` | `memory_frames` (`FrameStore.update`) | `{ content?, importance?, status?, tags? } → { ok }`. | S04 |
| `POST /api/memory/:id/archive` | **NET-NEW** (thin) | new thin route; model archive as `FrameStore.update(id, importance:'deprecated')` OR `status` in the new metadata column | `memory_frames` | `→ { ok }`. No hard delete. | S04 |
| `DELETE /api/memory/:id` | **EXTEND** (alias) | alias bare `:id` over `DELETE /api/memory/frames/:id` (`memory.ts:551`) | `memory_frames` (`FrameStore.delete`) | `→ { ok }`. | S04 |
| `POST /api/memory/merge` | **NET-NEW** | new route in `memory.ts`; real logic (read N frames, synthesize merged content, write one, archive/delete originals) — reuse `FrameStore` + `findDuplicate` dedup | `memory_frames` | `{ frameIds:[...], strategy?:'concat'\|'llm' } → { mergedId, archived:[...] }`. Net-new logic, low schema risk. | S04 |
| Harvest preview/commit confidence + selection | **EXTEND** | `POST /api/harvest/preview` (`harvest.ts:221`) → return ALL items (or paged) + per-item `confidence` + normalized `kind`; `POST /api/harvest/commit` (`harvest.ts:243`) → accept `{ selectedIds?:[] }` filter before the `createIFrame` loop (`:382-409`) | in-memory parse (preview) / `memory_frames` (commit) | Honors the trust-gate AC ("nothing imports without approval"). Preview confidence needs a classifier (LLM or heuristic). Preview-only confidence needs **no** migration. | S16 |
### 2b. Artifact Center (S05) — new `artifacts.ts` (largest net-new domain; aggregation only)
> **Single largest entity gap:** no `Artifact` type, table, or `/api/artifacts*` route exists anywhere
> (substrate-types §e). The backend is a **thin net-new aggregation/normalization layer** over three
> existing stores — **NO new data store**. A lightweight `artifacts.json` index holds title/status/
> tags/relations; the bytes stay in the existing file/document/storage stores.
| Method + Path | Disposition | Build target | Substrate | Shape | Screens |
|---|---|---|---|---|---|
| `GET /api/artifacts` | **NET-NEW** | new `routes/artifacts.ts`; normalize 3 stores | `GET /api/workspaces/:id/files` (`workspaces.ts:594`), document versions (`documents.ts`), storage files (`workspaces.ts /storage/files`) + `artifacts.json` index | `?workspaceId=&kind=&status= → { artifacts:[Artifact{id,title,kind,workspaceId,status,mimeType,storagePath,tags,relatedMemoryIds,...}] }`. | S02, S05 |
| `POST /api/artifacts` | **NET-NEW** | `artifacts.ts`; delegates byte-write to `POST /api/ingest` / `files/upload` / `documents` | file/document stores + `artifacts.json` | `{ title,kind,workspaceId,content?/file? } → { id }`. | S05 |
| `GET /api/artifacts/:id` | **NET-NEW** | `artifacts.ts`; resolve composite id → normalized Artifact + relations + preview meta | 3 stores + `documents.ts` versions | `→ { artifact, relatedVersions[], relatedMemoryIds[] }`. | S05 |
| `PATCH /api/artifacts/:id` | **NET-NEW** | `artifacts.ts`; update title/status/tags/relations in `artifacts.json` (move = re-point storagePath via `files/move`) | `artifacts.json` (+ `files/move`) | `{ title?, status?, tags?, relatedMemoryIds? } → { ok }`. | S05 |
| `DELETE /api/artifacts/:id` | **NET-NEW** (route) | `artifacts.ts`; remove index entry + optionally backing file via `files/delete`/`storage/delete` | `artifacts.json` + file stores | `?deleteBacking=bool → { ok }`. | S05 |
| `GET /api/artifacts/search-related?q=` | **NET-NEW** | `artifacts.ts`; lean on `memory_frames_fts` + wiki search internally | `memory_frames_fts`, sessions, tasks, fleet | `?q=&artifactId= → { related:[{type,id,title,score}] }`. | S05 |
### 2c. Workspace Creation (S17) — extend `workspaces.ts` write path
| Method + Path | Disposition | Build target | Substrate | Shape | Screens |
|---|---|---|---|---|---|
| `POST /api/workspaces` (extend body) | **EXTEND** | `workspaces.ts:116-135` + `WorkspaceManager.create` (`workspace-manager.ts:60-95`) | `workspace.json` | Accept `description, type, status, skills[], agentIds[], connectorIds[], mcpIds[]` (the Phase-0 additive fields). **No DB migration.** | S17 |
| `GET /api/workspace-templates` (extend shape) | **EXTEND** (optional) | `workspace-templates.ts:33` | template store | Add `skills[]`/`mcps[]`/`type` to `WorkspaceTemplate` so a chosen template pre-populates all 4 suggestion panels. | S17 |
### 2d. Onboarding: First Launch, Who-Are-You, Tool Discovery, Memory Import (S12S15)
| Method + Path | Disposition | Build target | Substrate | Shape | Screens |
|---|---|---|---|---|---|
| `PUT /api/profile` (extend merge) | **EXTEND** | `profile.ts:167-228` allow-list (`:172-196`) + `UserProfile` (`:41-94`) + `DEFAULT_PROFILE` (`:96-135`) | `profile.json` (NOT SQLite) | Add `workType, teamSize, goals[]` to the merged-fields allow-list + interface. Already mirrors identity → memory P/I frame (`:201-224`). **No migration.** | S13 |
| `POST /api/harvest/sources/:id/sync` | **NET-NEW** (thin) | new thin route in `harvest.ts`; resolve registered source + re-run commit | `harvest_sources` + `memory_frames` | `→ { runId }`. Sync today = `POST /api/harvest/commit`. NOTE: current sources keyed by `:source` **name** (not `:id`) — keep name key or alias. No new substrate. | S15, S16 |
> **S12 First Launch + S14 Tool Discovery need ZERO net-new backend** — all source catalogs already
> have routes (`GET /api/connectors`, `GET /api/tools/detect`, `GET /api/offline/status`,
> `GET /api/workspaces`); selections persist client-side in `OnboardingState` (localStorage),
> optionally threaded via the extended `PUT /api/profile`. The full harvest engine
> (`preview/commit/sources/progress/scan-claude-code/runs/extract-identity` + `POST /api/ingest`)
> already EXISTS for S15 — only the `/sources/:id/sync` alias (above) and the S16 confidence/selection
> extension (Phase 2a) are new.
---
## Phase 3 — Intelligence layer: Agents, Skills, Automations (PRD §8 Phase 3 / §21 Sprint 6)
### 3a. Agent Center + Agent Builder (S09, S18) — new sidecar `agents.ts` + agent store
> **Naming collision:** `/api/agents/*` CRUD exists ONLY on the Clerk-gated **Cloud** server
> (`packages/server/src/routes/agents.ts`) — NOT the sidecar (confirmed absent). All of §16.7 is
> **net-new locally**: a new `packages/server/src/local/routes/agents.ts` registered in `local/index.ts`.
> **Persistence (recommended v1):** a `{dataDir}/agents.json` file store, mirroring the agent-groups
> JSON precedent (`agent-groups.ts:29`) — **no SQLite migration**. (Alternative: an `agents` table in
> `mind/schema.ts` with SCHEMA_VERSION bump — only if agents must be FTS/relation-queryable. See §M.)
| Method + Path | Disposition | Build target | Substrate | Shape | Screens |
|---|---|---|---|---|---|
| `GET /api/agents` | **NET-NEW** | new `routes/agents.ts`; reads agent store; overlay live status from `/api/agents/active` + `/api/fleet` | `agents.json` (new) | `→ { agents:[Agent{id,name,type,goal,personaId,model,autonomyLevel,status,lastRunAt,successRate,...}] }`. May union saved agents + read-only personas for back-compat. | S09, S18 |
| `POST /api/agents` | **NET-NEW** | `agents.ts`; persist Agent (§15.5 fields); PRO-tier gate like `personas.ts:32` | `agents.json` + `install_audit` (if elevated tools/MCPs claimed) | `{ name,type,goal,model,personaId?,autonomyLevel,memoryScopes,skillIds,connectorIds,mcpIds,permissions } → { id }`. | S18 |
| `GET /api/agents/:id` | **NET-NEW** | `agents.ts` read over store | `agents.json` | `→ { agent }`. | S09, S18 |
| `PATCH /api/agents/:id` | **NET-NEW** | `agents.ts`; mirror `agent-groups.ts:74-90` PATCH shape | `agents.json` | `{ ...partial } → { ok }`. | S18 |
| `POST /api/agents/:id/run` | **EXTEND** | resolve agent → call real executor `POST /api/fleet/spawn` (`fleet.ts:66`, the only path that runs `runAgentLoop :185`). **Do NOT** use `agent-groups/:id/run` (stub `:105`). | fleet/orchestrator + `execution_traces` via `TraceRecorder` | `{ input?, workspaceId? } → { sessionId }`. Map agent persona/model/memoryScope/workspace onto spawn body. | S09, S18 |
| `POST /api/agents/:id/pause` | **EXTEND** | map agent→active session → `POST /api/fleet/:workspaceId/pause` (`fleet.ts:260`) | fleet | `→ { ok }`. | S09, S18 |
| `GET /api/agents/:id/traces` | **NET-NEW** (route over existing store) | new thin read over `execution_traces` (`mind/schema.ts:199`, written by `chat.ts`/`evolution.ts`, **no HTTP read today**), filtered by agent/session; fallback session timeline `sessions.ts` | `execution_traces` + `ai_interactions` | `?limit= → { traces:[{ts,step,tool,outcome,cost}] }`. | S09, S18 |
### 3b. Skills Hub + Skill Builder (S06, S19) — extend `skills.ts`
| Method + Path | Disposition | Build target | Substrate | Shape | Screens |
|---|---|---|---|---|---|
| `PATCH /api/skills/:id` | **EXTEND** | add `PATCH` alias + `:id``:name` over `PUT /api/skills/:name` (`skills.ts:506`) | `~/.waggle/skills/*.md` | `{ content? } → { ok }`. No new substrate. | S06, S19 |
| `POST /api/skills/:id/test` | **EXTEND** | add `:id` path variant routing to existing `POST /api/skills/test` (body-driven) | skill file + `parseSkillFrontmatter` | `{ testInput? } → { wouldInject, frontmatter }`. Sandbox/dry-run only; no execution. | S06, S19 |
| `POST /api/skills/:id/install` | **NET-NEW** (thin dispatcher) | new dispatcher over `POST /api/skills/starter-pack/:id`, `capability-packs/:id`, `marketplace/install` (keep `requireTier('PRO')` for marketplace-sourced) | `marketplace.db` + `MarketplaceInstaller` + `SecurityGate` + `install_audit` | `{ source:'starter'\|'pack'\|'marketplace' } → { installed }`. Resolves source + delegates. | S06 |
> Skill **create** is the existing structured `POST /api/skills/create` (`skills.ts:431` →
> `generateSkillMarkdown` + `redactSkillContent` + audit + hash) — the Builder's real target,
> **EXISTS**. Publish reuses `POST /api/marketplace/publish` (PRO). Optional later: extend
> `SkillFrontmatter` for structured inputs/outputs/memoryAccess (Builder Steps 34) — open question, not
> in §16.8. No `.mind` migration (skills are flat files; marketplace is `marketplace.db`).
### 3c. Automation Center + Automation Builder (S11, S20) — alias cron as automations
> **The capability is cron** (`cron.ts`, `/api/cron/*` — full CRUD + trigger + history). "Automations"
> = a rename/alias surface. **Zero MISSING, all PARTIAL.** Register a real `/api/automations/*` alias
> plugin (PRD vocabulary) OR point the new UI at `/api/cron`. Trigger/condition/actions ride in the
> existing `job_config TEXT` blob → **no `.mind` migration** for v1.
| Method + Path | Disposition | Build target | Substrate | Shape | Screens |
|---|---|---|---|---|---|
| `GET /api/automations` | **EXTEND** (alias) | alias of `GET /api/cron` (`cron.ts:94`); reshape `toResponse` to expose `trigger/condition/actions/status` from `job_type`+`job_config` | `cron_schedules` | `→ { automations:[...] }`. | S11, S20 |
| `POST /api/automations` | **EXTEND** (alias) | alias of `POST /api/cron` (`cron.ts:67`); persist `trigger/condition/actions` into `job_config` (+ `cron_expr` for schedule triggers) | `cron_schedules` | `{ name,trigger,condition?,actions[],schedule? } → { id }`. | S11, S20 |
| `PATCH /api/automations/:id` | **EXTEND** (alias) | alias of `PATCH /api/cron/:id` (`cron.ts:124`) | `cron_schedules` | `{ ...partial } → { ok }`. **Note real bug:** FE `updateCronJob` calls `PUT /api/cron/:id` but only `PATCH` is registered (adapter.ts:836 vs cron.ts) — fix the adapter. | S11, S20 |
| `POST /api/automations/:id/run` | **EXTEND** (alias) | alias of `POST /api/cron/:id/trigger` (`cron.ts:174`; auto-enables + executes + notifies) | `cron_schedules` + executor (`index.ts:1379`) | `→ { runId }`. | S11, S20 |
| `POST /api/automations/:id/pause` | **NET-NEW** (thin) / EXTEND | add thin `/pause` route OR adapter calls `PATCH /api/cron/:id { enabled:false }` | `cron_schedules.enabled` + scheduler | `→ { ok }`. Add `/pause` for PRD contract. | S11, S20 |
| `GET /api/automations/:id/logs` | **EXTEND** (alias) | alias of `GET /api/cron/:id/history` (in `notifications.ts:202``cronStore.getExecutionHistory`) | `cron_execution_history` | `?limit= → { logs:[...] }`. | S11, S20 |
| `POST /api/automations/test` | **NET-NEW** | new dry-run route (PRD §12.10 "test before activate"); current `POST /api/cron/:id/trigger` really executes | cron executor (no-persist mode) | `{ trigger,actions[] } → { previewResult }`. No log/notify side-effects. | S20 |
---
## Phase 4 — Extend layer: Connectors, MCPs, Marketplace, Install Audit (PRD §8 Phase 4 / §21 Sprint 7)
### 4a. Connector Hub (S07, S14) — extend `connectors.ts`
| Method + Path | Disposition | Build target | Substrate | Shape | Screens |
|---|---|---|---|---|---|
| `POST /api/connectors/:id/sync` | **NET-NEW** | new route in `connectors.ts` (`WaggleConnector` has `connect`/`healthCheck`/`execute` but **no `sync()`**) | vault sub-key `connector:<id>:lastSync` (or small store) + activity event + `install_audit` | `→ { lastSyncAt, ok }`. **Phased:** MVP = `healthCheck()` + stamp `lastSyncAt` + emit event; full data re-pull is a larger connector-SDK addition. No `.mind` migration. | S07 |
| `POST /api/connectors/:id/revoke` | **EXTEND** (alias) | alias to `POST /api/connectors/:id/disconnect` (`connectors.ts:107`) + write `install_audit` `action:'rejected'`/revoke | vault + `install_audit` | `→ { ok }`. Same intent, PRD verb. | S07 |
| `POST /api/connectors/:id/connect` (extend) | **EXTEND** | `connectors.ts:55` — add `auditStore.record(...)` on success | vault + `install_audit` | (audit-trail enrichment, no shape change). | S07, S14 |
| `GET /api/connectors` (extend payload) | **EXTEND** (optional) | `connectors.ts:6` — carry `category` (already on type, `types.ts:299`) + `lastSyncAt` so UI drops hardcoded CATEGORIES/sync-shim | connector registry | (payload enrichment). | S07, S14 |
| `GET /api/connectors/health` (aggregate) | **NET-NEW** (optional, mockup) | `connectors.ts` — fan `healthCheck()` across connectors | connector registry | `→ { connectors:[{id,status,lastSyncAt}], systemHealth }`. Optional v1; compose client-side otherwise. | S07 |
| `GET /api/connectors/activity` | **NET-NEW** (or use shared `/api/extend/audit?type=connector`) | reads `install_audit` rows filtered to `type:'connector'` | `install_audit` | `→ { activity:[...] }`. **Prefer the shared `/api/extend/audit`** (4c) which serves S07+S08+S21 with one route. No migration. | S07 |
### 4b. MCP Hub (S08, S17) — new `mcps.ts` + persisted MCP-config store + runtime population
> **The deepest backend gap in the Extend layer.** Today `mcpRuntime` is **empty and never populated**
> (`local/index.ts:911`); there is no `GET /api/mcps`, no persisted MCP-config store, no boot-time
> population. All routes live in a new `packages/server/src/local/routes/mcps.ts`. **Substrate decision:**
> persist installed MCP configs as a JSON file / `.mcp.json` (no migration) — confirm dataDir path +
> multi-workspace scoping (open question). The MCP catalog is static in `@waggle/shared mcp-catalog.ts`.
| Method + Path | Disposition | Build target | Substrate | Shape | Screens |
|---|---|---|---|---|---|
| `GET /api/mcps` | **NET-NEW** | `routes/mcps.ts`; join static catalog (`@waggle/shared mcp-catalog.ts`) + installed-state (`capabilities/status.mcpServers[]`, `.mcp.json`, `marketplace mcp-registry.ts`) | catalog + `.mcp.json` + `install_audit` | `→ { mcps:[{id,name,status,installed,tools[],scope}] }`. Reads existing substrate; no migration. | S08, S17 |
| `POST /api/mcps/install` | **EXTEND** | route through existing marketplace installer `POST /api/marketplace/install` (already handles `installType:'mcp'` → writes `.mcp.json`, `installer.ts:580`) | `marketplace.db` + `.mcp.json` + `install_audit` | `{ mcpId } → { installed }`. Resolve MCP id → marketplace package → install. Audit already recorded. | S08 |
| `POST /api/mcps/:id/test` | **NET-NEW** | `mcps.ts`; resolve server, `start()` if needed, assert `isHealthy()` (`mcp-runtime.ts:94,399`) and/or `tools/list` round-trip | `McpRuntime` | `→ { ok, tools[], error? }`. Open question: live spawn-and-handshake vs static manifest validation. | S08 |
| `POST /api/mcps/:id/revoke` | **NET-NEW** | `mcps.ts`; `mcpRuntime.removeServer(name)` (`mcp-runtime.ts:327`) + delete persisted config + `install_audit` `action:'revoked'` | `McpRuntime` + `.mcp.json` + `install_audit` | `→ { ok }`. | S08 |
| `POST /api/mcps` (add custom) | **NET-NEW** | `mcps.ts`; persist config + add to runtime (blueprint API line 530) | `.mcp.json` + `McpRuntime` | `{ name, command, args[], env{}, workspaceId? } → { id }`. | S08 |
| `POST /api/mcps/:id/start` · `POST /api/mcps/:id/stop` | **NET-NEW** | `mcps.ts`; map to `McpRuntime` start/stop (PRD §12.8 start/stop) | `McpRuntime` | `→ { status }`. | S08 |
| `PATCH /api/mcps/:id/permissions` | **NET-NEW** | `mcps.ts` (blueprint API line 530) | `.mcp.json` config | `{ scope?, permissions? } → { ok }`. | S08 |
| `GET /api/mcps/:id/logs` | **NET-NEW** (phased) | `mcps.ts`; needs a ring-buffer of stderr/stateChange in `McpServerInstance` (no log capture today) | new in-memory ring buffer | `→ { logs:[...] }`. Defer to later phase if log-capture infra not built. | S08 |
> **Boot-time runtime population** (populate `mcpRuntime` from the persisted config at startup,
> `local/index.ts:911`) is the foundational non-route work item that unblocks all of the above.
### 4c. Marketplace / Extend + Install Audit (S21, shared S06/S07/S08)
| Method + Path | Disposition | Build target | Substrate | Shape | Screens |
|---|---|---|---|---|---|
| `GET /api/marketplace` | **EXTEND** (alias) | bare path = alias of `GET /api/marketplace/search` with default params (`marketplace.ts:56`) | `marketplace.db` | `→ { results:[...] }`. | S21 |
| `GET /api/extend/audit` (shared governance read) | **EXTEND** (param) | the install-audit read route `GET /api/audit/installs` **already EXISTS** (`skills.ts:685`); add `?capability=` / `?type=` filter exposing `getByCapability()` (`install-audit.ts:125`) | `install_audit` | `?type=skill\|connector\|mcp\|marketplace&limit= → { entries:[AuditEntry] }`. Serves S06+S07+S08+S21 with one route. No migration. **Note:** substrate-types §d#1 listed this as missing; it is present — the work is the filter param, not a new route. | S06, S07, S08, S21 |
> **No `POST /api/share`** here — that is Phase 5 (Team). Per-workspace install scoping (§12.13) is
> net-new product surface; PRD §22 risk register says "start with catalog + install audit, postpone
> billing/public marketplace" — **defer to Phase 4 polish**.
---
## Phase 5 — Team intelligence: Team Workspace, RBAC, Sharing, Audit (PRD §8 Phase 5 / §21 Sprint 8)
> Team CRUD core EXISTS in `team.ts` (`teams.db`): `GET/POST/PUT/DELETE /api/teams`, `/:id`,
> `/members`, `/members/:userId`, `/activity`. No `.mind` migration for the team core (`teams.db`
> standalone; `team_capability_policies`/`overrides`/`requests` exist in migration `0001`).
| Method + Path | Disposition | Build target | Substrate | Shape | Screens |
|---|---|---|---|---|---|
| `POST /api/teams/:id/invite` | **EXTEND** (alias) | thin `/invite` alias forwarding to `POST /api/teams/:id/members` (`team.ts:581`, owner/admin gated) | `team_members` (`teams.db`) | `{ email?, userId?, role? } → { ok }`. No new substrate. | S10 |
| `GET /api/teams/:id/audit` | **EXTEND** (alias) | alias `/audit` → existing `GET /api/teams/:id/activity` (`team.ts:690`, reads `audit_events` via `getAuditDb`) | `audit_events` | `→ { events:[...] }`. Or add audit-export shape. No new store. | S10 |
| `PATCH /api/teams/:id/members/:memberId` (fix gate) | **EXTEND** (bug fix) | `team.ts:642` (`:userId`==`:memberId`); **fix PUT(owner-only `:615`) vs PATCH(owner/admin `:642`) role-gate inconsistency** | `team_members` | (behavior fix, no shape change). | S10 |
| `POST /api/share` | **NET-NEW** | new route (no `/api/share` anywhere — grep-confirmed); shares memory/artifact with role-appropriate perms | `workspace.json teamId` linkage + (if frame-level) `memory_frames` metadata | `{ objectType:'memory'\|'artifact'\|'workspace', objectId, teamId, role } → { ok }`. **Open:** frame-level scope needs the `memory_frames` metadata migration (§M); v1 may scope implicitly via workspace `teamId`. Gate behind TEAMS tier like `/api/team/*`. | S05, S10 |
| `POST /api/artifacts/:id/share` | **NET-NEW** (blueprint) | maps to `POST /api/share` + artifact scope; gate TEAMS | `artifacts.json` + team scope | `{ teamId, role } → { ok }`. Blueprint action not in §16.6; defer to Phase 5. | S05 |
| `GET /api/teams/:id/governance` (optional) | **NET-NEW** (optional) | surface `team_capability_policies`/`overrides`/`requests` (migration `0001`); Enterprise proxy `GET /api/team/governance/permissions` (`team.ts:418`) is the remote analog | `team_capability_policies` etc. | `→ { policies[], overrides[], requests[] }`. RBAC UI. | S10 |
---
## §M — Schema migrations required
> **Headline: the entire refactor needs AT MOST ONE conditional SQLite migration**, and it is
> deferrable. Every other "schema addition" is to a **JSON file** (`workspace.json`, `profile.json`,
> `agents.json`, `artifacts.json`, `.mcp.json`) — **not** a database — so it is a pure additive
> TypeScript-interface change with **no migration**. The migration runner already does idempotent
> additive `ADD COLUMN` on `memory_frames` (precedent: it added `source`, `mind/db.ts:116-124`;
> pattern = `pragma_table_info` guard + `ALTER TABLE … ADD COLUMN`).
### M1 — `memory_frames.metadata` (CONDITIONAL — Phase 2/Phase 5) — the only `.mind` SQLite migration
- **What:** add one nullable column `metadata TEXT NOT NULL DEFAULT '{}'` to `memory_frames`
(`mind/schema.ts:47`). Store `{kind, title, scope, sourceId, sourceUrl, confidence, tags, evidence,
relatedMemoryIds, relatedArtifactIds, status}` as JSON (PRD §15.4 fields; PRD endorses metadata-first,
`:1013`).
- **Why conditional:** `memory_frames` is the **only** mind table without a JSON blob column (unlike
`awareness.metadata`, `knowledge_entities.properties`, etc.). Needed ONLY when persisted
confidence/provenance/scope/status becomes a real **query/filter axis** (S04 Memory Center filters;
S16 persisted-confidence review; S10/S05 frame-level `/api/share` scope). **NOT needed** if S16
review is preview-only (pre-commit) and S04 filtering is in-app over the existing columns.
- **Risk:** low — single additive nullable column; avoids touching the FTS/vec virtual tables and IPB
scoring. **Promotion path:** if `confidence` becomes a primary indexed filter, a later migration adds
`confidence REAL` as a real column (same ADD-COLUMN pattern).
- **Screens:** S04, S16, S10, S05. **Build target:** `packages/hive-mind-core/src/mind/db.ts` migration
block + `mind/schema.ts`.
### M2 — `install_audit` risk-level CHECK fix (RECOMMENDED — pre-Phase 4, latent bug)
- **What:** the TS `AuditRiskLevel` includes `'critical'` (`install-audit.ts:16`) but **both** DDL CHECK
constraints allow only `('low','medium','high')` (`install-audit.ts:65` AND `schema.ts:130` — duplicated
DDL that must stay in sync). A `record({riskLevel:'critical'})` throws a CHECK violation.
- **Why:** Phase 4 connector/MCP/skill installs all route through `auditStore.record(...)`. The
marketplace route currently side-steps by mapping CRITICAL→`riskLevel:'high'`+`approvalClass:'blocked'`
(`marketplace.ts:224-319`) — but any new Extend caller passing `'critical'` crashes.
- **Fix (pick one):** (a) widen both CHECK constraints to include `'critical'` (additive CHECK migration
— needs table rebuild for SQLite CHECK change, or relax to no-CHECK), OR (b) lock the CRITICAL→`'high'`
mapping as the permanent contract and drop `'critical'` from the TS union. (b) is zero-migration.
- **Risk:** low. **Screens:** S06, S07, S08, S21 (all Extend installs). **Build target:**
`install-audit.ts:65` + `mind/schema.ts:130` (kept in sync) OR the TS union.
### M3 — `agents` table (OPTIONAL — Phase 3, NOT recommended for v1)
- **What:** an `agents` table in `mind/schema.ts` with a `SCHEMA_VERSION` bump.
- **Recommendation: do NOT do this for v1.** Persist agents to `{dataDir}/agents.json` (file store,
mirrors the `agent-groups.json` precedent `agent-groups.ts:29`) — **no migration, reversible.** Only
add the table if agents must be FTS/relation-queryable. PRD §14.4 non-goal favors minimal backend.
- **Screens:** S09, S18.
### Non-migrations (additive JSON-file / interface changes only — listed for completeness, NOT migrations)
- `WorkspaceConfig` V2 fields → `workspace.json` (Phase 0; substrate-types §a).
- `UserProfile` `workType/teamSize/goals``profile.json` (S13).
- `Agent` entity → `agents.json` (Phase 3, M3 alt).
- `Artifact` index → `artifacts.json` (Phase 2b).
- MCP installed configs → `.mcp.json` (Phase 4b).
- Automation `trigger/condition/actions` → existing `job_config TEXT` blob (Phase 3c; no schema change).
- `cron_schedules`/`cron_execution_history`/`notifications` tables already exist with lazy creation
(`cron-store.ts:135-157`) — no migration for Automations.
---
## Counts
> Counted as **distinct backend endpoints** (each method+path = 1). Endpoints that **EXIST as-is** and
> need only frontend wiring are **excluded**. Phase-0 non-route work (V2 fields, FE type unions, write-
> side stamps) is counted separately under "interface/field extensions", not as endpoints.
- **Total endpoints requiring backend work: 53** (NET-NEW + EXTEND, de-duplicated).
- **NET-NEW endpoints: 35**
- Home ×2 (`/home/briefing`, `/home/overnight`)
- Command ×3 (`/command/search`, `/command/recent`, `/command/suggestions`)
- Memory ×3 (`/memory/:id`, `/memory/:id/archive`, `/memory/merge`)
- Artifacts ×6 (GET, POST, `/:id`, PATCH `/:id`, DELETE `/:id`, `/search-related`)
- Agents ×5 (GET, POST, `/:id`, PATCH `/:id`, `/:id/traces`)
- Skills ×1 (`/skills/:id/install`)
- Automations ×1 (`/automations/test`)
- Connectors ×3 (`/:id/sync`, `/connectors/health`, `/connectors/activity`)
- MCPs ×8 (`GET /mcps`, `/:id/test`, `/:id/revoke`, `POST /mcps` custom, `/:id/start`, `/:id/stop`, `PATCH /:id/permissions`, `/:id/logs`)
- Team ×3 (`POST /api/share`, `POST /artifacts/:id/share`, `GET /teams/:id/governance`)
- *(Several MCP/connector/team items are blueprint-implied beyond the §16 literal list; `/api/automations/:id/pause` is counted under EXTEND as a thin alias over the cron `enabled` flag.)*
- **EXTEND endpoints: 18** (distinct backend touch-points; an EXTEND may be an alias, an added param, or added behavior)
- `/quick-capture` (delegates to memory write)
- `/workspaces/:id/state`, `/workspaces/:id/activity` (thin routes over existing builders/events)
- `/command/execute` (broaden dispatch)
- `/memory` GET, `/memory` POST, `/memory/:id` PATCH, `/memory/:id` DELETE (aliases over `/memory/frames*`)
- `/harvest/preview` + `/harvest/commit` (confidence + `selectedIds` selection)
- `/harvest/sources/:id/sync` (thin re-commit alias)
- `/workspaces` POST (richer body)
- `/agents/:id/run`, `/agents/:id/pause` (delegate to fleet)
- `/skills/:id` PATCH, `/skills/:id/test` (`:id` variants)
- 6× `/automations/*` aliases over `/cron/*` (GET, POST, PATCH, run, pause, logs)
- `/connectors/:id/revoke`, `/connectors/:id/connect` (+audit), `/connectors` GET (payload)
- `/mcps/install` (via marketplace installer)
- `/marketplace` (bare-path alias)
- `/extend/audit` filter param (over existing `/audit/installs`)
- `/teams/:id/invite`, `/teams/:id/audit`, `/teams/:id/members/:memberId` (alias + role-gate fix)
- **Phase-0 interface/field extensions (NOT endpoints): 5** — `WorkspaceConfig` V2 fields + `updatedAt`/
`lastActiveAt` write-stamps (`workspace-manager.ts`), FE type unions (`types.ts`), `UserProfile`
`workType/teamSize/goals` (`profile.ts`), `WorkspaceTemplate` shape, `Connector` interface fields.
- **PRD §16 cross-reference (from `_inventory/backend-routes.md`):** of 65 §16-literal endpoints —
**16 EXIST** as-is (FE wiring only), **30 PARTIAL** (→ EXTEND), **19 MISSING** (→ NET-NEW). This
master list adds ~16 blueprint-implied endpoints (MCP start/stop/logs/permissions/custom, connector
health/activity, automations/test, team governance, artifact-share, extend/audit) beyond the §16
literal set.
- **Schema migrations:** **1 conditional** SQLite (M1 `memory_frames.metadata`) + **1 recommended**
CHECK fix (M2 `install_audit` risk-level) + **1 optional/deferred** (M3 `agents` table — recommend
NOT doing in v1). Net likely-to-ship: **1** (`memory_frames.metadata`); **0 strictly required** if
S16 confidence stays preview-only and `/api/share` scopes implicitly via workspace `teamId`.
- **New sidecar route files: 5** (`home.ts`, `command.ts`, `artifacts.ts`, `agents.ts`, `mcps.ts`)
+ 1 alias plugin (`automations.ts` → cron). **New JSON file stores: 2** (`agents.json`,
`artifacts.json`); plus reuse of `.mcp.json`, `workspace.json`, `profile.json`.