Existing substrate — what is already built, and where it is unreachable
Status: Reference · Created: 2026-09-06 · Re-verified: 2026-09-06 against develop @ a2edec3d4
Audience: anyone sizing or implementing an Agent Workspace epic
Read this warning first. An earlier revision of this document was compiled against a stale checkout and asserted that several capabilities had "no UI". Four of those claims were wrong — the UI had shipped. Every row below has since been re-verified against current
developwith file-level evidence, and the falsified rows are kept, struck through, so nobody re-discovers them the hard way.The standing rule this produced: before you build a screen this document says is missing,
rgfor it first. A "we don't have this" claim decays; a "we already have this" claim does not.
A full pass over the platform found a pattern worth stating before any epic is estimated:
A large part of this program is not "build the feature". It is "bind a finished backend to a screen" — or "consume a hook that already exists".
0. Claims that were WRONG — do not scope these as new work
| Ref | The claim that was made | The truth on develop |
|---|---|---|
It shipped. AgentFleetSection.tsx (284 lines) renders a Preferred node picker — "Any node", each enrolled node as name·platform·status, plus a synthetic option for a dangling binding — with warning hints for offline/drained/removed nodes and a personal-scope refusal branch. Policy in agent-fleet.shared.ts; mounted from AgentCapabilitiesClient.tsx; data via agents/[id]/capabilities/agent-fleet-data.ts; server actions in app/actions/settings/fleet.ts; 21 i18n keys at dashboard.agentsPage.capabilities.fleet, present in all 20 locales. | ||
Both hooks exist. lib/hooks/use-inbox-stream.ts opens the EventSource and degrades to a 30 s poll; lib/hooks/use-agent-inbox.ts holds the store via useSyncExternalStore. The BFF proxies exist too. The real gap is narrower: no component imports them yet. | ||
It has one, deliberately. SchedulesList.tsx + TriggersManager.tsx are mounted in activity-client.tsx behind a Log | Schedules toggle, localStorage-persisted, deep-linkable via ?view=schedules, with a full dashboard.schedules i18n block and five e2e specs. A merged spec (docs/specs/features/schedules/) chose the in-Activity toggle over a standalone route. Do not build a parallel view. | ||
cmdk@^1.1.1 is already a direct dependency and a working palette ships today: components/kb/workbench/KbSearchPalette.tsx — Cmd/Ctrl+K toggle, debounced server search, filter chips, locale-aware routing, unit spec. AW-01 is the promotion of an existing palette to global scope, not a greenfield build. | ||
A working Task Kanban already ships. components/tasks/TasksKanbanView.tsx (712 lines) — one column per TaskStatus, native drag-to-transition against a client mirror of the real transition lattice, optimistic move with rollback, per-card Run plus an r shortcut, per-column Run all capped at 20, branch/PR/CI/run/gate chips, a diff sheet, and run-state polling that merges only run fields. Reachable from /tasks, /missions/[id]/tasks, /works/[id]/tasks, /ideas/[id]/tasks. |
Net effect: AW-01, AW-02 and AW-11 are all materially smaller than first scoped, and AW-05's live-inbox work is a tick rather than a slice. AW-02 dropped L → M and now ships zero schema change.
What AW-02 actually turned out to be
Once the board was found, the epic stopped being "build a board" and became "fix the four things that stop the shipped board answering the question" — each verified in code:
- Every column count is a lie.
tasks/page.tsxfetches one 50-row page with no status filter, and the board renders{tasks.length}per column. Nothing on screen says so. - Priority sorts nothing.
task.repository.tslist()endsorderBy('task.updatedAt','DESC')andListTasksFilterhas no ordering field — priority is stored, filterable and chipped, but never orders the board. - A filtered board cannot be shared. View mode is
useState('cards')— not in the URL, not persisted, and the board is not the default. - It is not translated. Every board string is hardcoded English while
dashboard.tasksPage.status.*(all 7) and.priority.*(all 5) already exist and are already translated into 20 locales.
A live defect found during re-verification
Ctrl+K currently has two competing handlers. The global one (use-keyboard-shortcuts.ts)
listens on document; the KB palette listens on window. Both are bubble-phase and document
bubbles to window, so on the three /works/[id]/kb/* routes the global handler wins and
router.push('/works?focus=search') navigates the operator out of the KB workbench while the
palette also toggles open on a component being unmounted. Neither calls stopPropagation(), so
preventDefault() on either does not help.
AW-01 must own the fix — a single shortcut registry with scope and priority — otherwise the new global palette becomes a third colliding listener.
1. Finished backends with no UI — verified still true
| Ref | What exists | What is missing | Epic |
|---|---|---|---|
| S1 | Workflows — a graph-orchestration backend: create/update/run a saved graph, list runs, read a run trace, output truncation, failure node id, step count. 8 routes, 2 entities. | No web route, no component. Confirmed by a full enumeration of the route tree. | AW-21 |
| S2 | Escalation queue — GET /api/escalations, GET /:id, POST /:id/resolve, confidence ranking with a deterministic fallback, dedup, CAS-guarded resolve. | No web surface calls it. A search of apps/web/src finds only two comments. (Note: the backend is not unreachable — the agent runtime uses it; it is the operator who has no view.) | AW-03 |
| S4 | Per-agent Inbox — list, detail and compose pages, with real recipient validation and a hardened server action. | The pages exist; the tab entry is still absent from AgentDetailTabs.tsx (which lists ten other tabs), and there is no DASHBOARD_AGENT_INBOX route constant where every sibling tab has one. | AW-05 |
| S19 | Three platform-admin tools — cross-user × cross-Work usage, the plugin allow-list, the per-tenant runtime allow-list. | No nav entry, no index page, and no way to discover a tenant id from the product. | AW-01 |
| G19 | Agent↔email assignment — entity and repository, read by two internal factories. | No controller route and no UI. Writable only by direct SQL. | AW-05 |
| G50 | Email send path | No per-agent daily cap, per-inbox burst ceiling or address-count cap. The route is auth-gated and covered by the global per-user throttler, but nothing bounds how much one agent may send. | AW-05 · AW-24 |
| S18 | Digest, personal and organization — deterministic counts across runs, tasks, PRs, ingested events, goals and open escalations; a narrative that degrades with a visible reason; a quiet window that deliberately does not suppress open escalations. | The dispatch summary is computed, logged, and surfaced nowhere. | AW-13 |
| S8 | Tool grants across four scopes — tenant, organization, work, agent, with narrow-only merge over a permissive default. | UI only at agent scope. | AW-15 · AW-24 |
2. Partly true — the claim moved
| Ref | Corrected position | Epic |
|---|---|---|
| S11 | Local model execution on a node is a live shipped path, not a dormant one: model-cli.ts runs a local CLI in the task worktree, called by agent-task.ts, registered for the real agent-task job kind. What is not dispatched is the hardened, Windows-Job-Object-contained executor under model-execution/, which waits on a signed helper. The remaining gap is containment for untrusted nodes, not execution. | AW-11 |
| G25 | The live-view half is still entirely true. A repo-wide search for vnc|rdp|screen.share|desktopCapturer|getDisplayMedia finds only an unrelated e2e permissions spec. No node-scoped terminal, no screen capture, no node log upload. | AW-11 |
| G26 | Bulk cancel exists (POST /api/fleet/cancel-…); per-job cancel, retry and payload/result/error inspection do not. | AW-11 |
| G27 | Node pause exists as a first-class sticky status and the settings table exposes only disable/enable — confirmed, with the correction that the pause route has different exposure than first recorded. | AW-11 |
| S12 | The dual-catalog stacking on the templates page is exactly as described; the task-workflow-template instantiation (parent Task + one sub-task per step with dependsOn edges, in one transaction, acyclic-validated) is real. | AW-21 |
| S15 | The KB retrieval trail is workbench-only as stated; the citations half differs — check the re-verification note before scoping. | AW-06 · AW-09 |
3. Stronger than expected — protect these, do not flatten them
| Ref | Strength | Why it matters here |
|---|---|---|
| S17 | The streaming terminal stack — attach-token minting, a WS gateway that refuses tokens in the query string, an in-memory relay with byte-bounded scrollback replay, retained pre-attach error banners, a pinned non-evictable exit frame, sequence dedup, driver/viewer/worker roles, persisted transcripts with GC, xterm.js with a dependency-free DOM fallback. Confirmed keyed by AgentRun, not nodeId. | AW-11 needs no new streaming architecture — it needs this repointed at a node, plus cross-replica fan-out (already a declared seam). |
| S9 | Billing is fully implemented behind a vendor-neutral provider abstraction — checkout, payment methods, subscription mutation, seat quantity, metered events, invoice sync, webhook signature verification. Confirmed. | The "coming soon" cards are a deployment flag, not missing code. AW-17 is mostly surfacing. |
| S22 | 21 locales with a real pipeline — cookie-based locale, English deep-merge fallback, RTL, and a parity-sync script that seeds full paths because a missing parent key collapses a whole subtree in next-intl. Confirmed. | Every epic's i18n cost is lower than it looks — but respect the parent-key rule. |
| S16 | Two production vector stores behind a real port, with a coordinates table driving re-embedding when the model or its dimensions change. | AW-07 inherits multi-tenant RAG infrastructure rather than designing it. |
| S10 | Whole-account export / import / GitHub sync, already shipped and reachable. | AW-22 is largely surfacing an existing feature. |
| S6 | An OpenAI-compatible completions endpoint that resolves providers, injects knowledge and redacts secrets, already serving two clients. | A shippable third-party API surface today. |
| S20 | Account-level repository registry with a credentialMode that is always a pointer, never a token, and encrypted per-path seed env files behind an owner-gated endpoint. | "Give this agent this repo with these env files" is solved. |
| S21 | Machine-readable platform self-description, with the works schema generated from the same schema the server validates with, so the two cannot drift. | Agent-to-agent onboarding is one unblocked pipeline away. |
| S7 | The job-runtime provider layer — 6 provider plugins, a binding factory routing all 11 dispatcher symbols, a per-tenant overlay with credential rotation, versioning and an append-only audit trail. | Two docs still claim this is unbuilt. The win is deleting the stale docs. |
4. How to use this document
rgbefore you build. Section 0 exists because four "missing" claims were wrong. Search the current tree for the concept — several spellings — before scoping any screen as new.- Before estimating, check §1 and §2. If the backend is there, the epic is a binding exercise.
- Before designing a mechanism, check §3. We have production-grade streaming, RAG, billing, job-runtime and i18n substrate. Reusing it is faster and keeps behaviour consistent.
- Do not remove any of it (NN #20). Where something is invisible, the fix is a route constant, a tab entry, a nav link or an i18n key — never a rewrite.
- Anything you surface must earn its keep: i18n keys, an empty state, and a test. That last mile is precisely why these never shipped.
Two recurring hazards, both observed in this codebase: a feature can be invisible because a route constant or tab entry is missing rather than because anything is broken; and a second surface over the same row will silently drift from the first unless both revalidate each other.