Submatter/.convoys/second-brain-os-v01/brief-3-harness-registry-adapter.md
Randall Stillwell 6ccb060bea feat(second-brain): open self-hosted Obsidian for crews — harness-agnostic v0.1
- MCP create-backlog-item for natural language Plan/Epic/Task authoring
- Worker non-code: optional worktree + per-harness allowlist + doc_review→published
- Harness registry: harness_providers/role_harness_routes, WorkerAdapter (minimal|crewai|langgraph|remote-ag-ui), AG-UI POST /api/ag-ui, Settings → Harnesses with Test
- Plans tree: backlog.listTree + createPlan/Epic/Task + Start→Convoy, web CRUD without CLI
- Fleet: /fleet rollup across workspaces + SSE /api/fleet/events

Decision: docs/decisions/2026-08-25-second-brain-os-pivot.md
Convoy: .convoys/second-brain-os-v01

Co-authored-by: orchestrator
2026-08-25 16:20:53 -05:00

2.9 KiB

brief convoy role title status priority slice_dependencies recommended_model model_tier
3 second-brain-os-v01 role-implementer Harness registry + WorkerAdapter + AG-UI + Settings → Harnesses ready P0
1
2
qwen-coder local

Brief 3 — Harness registry + WorkerAdapter (2-3d) — THE big one

Goal

Workspace admins CRUD harness providers (minimal|crewai|langgraph|remote-ag-ui) + map role_id → harness_alias, worker dispatches via adapter, any harness via AG-UI /api/ag-ui/[workspace], test button proves reachability.

Files

  • packages/database/src/schema/harness_providers.ts NEW — mirror model_providers docs/adr-model-routing.md:78: id, workspace_id fk, alias unique per workspace, display_name, harness_type enum, endpoint_url nullable, auth_env_var varchar nullable, config jsonb, enabled bool, created_at/updated_at. + role_harness_routes workspace_id, role_id varchar, harness_provider_id fk, enabled, sort_order, unique (workspace_id, role_id).
  • packages/database/migrations/0015_* via pnpm db:generate.
  • packages/shared/src/worker/executor.ts NEW — WorkerExecutor {id, run(ctx, model):Promise<...>}, resolveHarnessForRole() pure like resolveModel packages/shared/src/model-routing/index.ts.
  • apps/worker/src/handle-job.ts:40 — replace hard createMinimalSession with registry.get(harnessAlias).run(...); keep MinimalSession as minimal adapter.
  • apps/web/server/routers/harnesses.ts NEW — list/create/update/delete/testHarness (admin only, never return auth value, test does fetch health on endpoint with Authorization: Bearer ${process.env[AUTH_ENV_VAR]}).
  • apps/web/app/(app)/[workspaceSlug]/settings/harnesses/page.tsx NEW — table + add/edit dialog + test.
  • apps/web/app/api/ag-ui/[workspace]/route.ts NEW — POST ag-ui envelope, validate workspace_id, write convoy_events/agent_runs, enforce tenant fence.
  • packages/database/src/schema/index.ts exports.

Acceptance

  • Migration applies pnpm db:generate && pnpm db:migrate locally (no drift).
  • Settings → Harnesses CRUD works; Test on remote-ag-ui https://crew.example.com/ag-ui with HARNESS_AUTH_TOKEN env shows latency or error detail, never leaks token to browser.
  • resolveHarnessForRole(workspaceId, "role-implementer") returns crewai when mapped, minimal fallback.
  • Worker dispatched role-implementer via crewai adapter still claim_task/complete_task and writes harness_alias to convoy_events.harness_alias + agent_runs.
  • pnpm lint && pnpm type-check && pnpm test pass.

Prompt

You are role-implementer brief 3. Read docs/adr-model-routing.md, docs/decisions/2026-08-25-second-brain-os-pivot.md, packages/database/src/schema/model_providers.ts, packages/shared/src/model-routing/index.ts. Keep diff minimal, follow existing Drizzle/tRPC patterns in server/routers/ai-models.ts.

Depends

Briefs 1,2 — needs optional worktree done.