diff --git a/docs/requirements/tool-gateway-mapping.md b/docs/requirements/tool-gateway-mapping.md new file mode 100644 index 00000000..663ba8a8 --- /dev/null +++ b/docs/requirements/tool-gateway-mapping.md @@ -0,0 +1,197 @@ +# Tool↔Gateway Mapping Contract (D8) + +Status: DRAFT — awaiting ratification (webui-audit S2, contract 5 of 9). +Authority: PRD D8/D12 (Part I §8) — the webUI sits OVER official tooling: +every webUI operation goes through the Gateway API backed by the same +official framework tooling the CLI uses, and a webUI operation with no +backing tool is scored **blocked on tooling** and the tool is built +first. Measured input: the webui-audit A5 tooling baseline +(operation-by-operation inventory of the current Gateway surface and the +P1 gaps, cross-reviewed; `fleet/lanes/webui-audit/findings/ +A5-tooling-baseline.md` in the estate brain). The T10 ruling adopted the +targeted-update plan including building the D8 tools in A5's rank order. + +Revision 2 (GLM review F1–F5): the §2 table completed against an +independent re-measurement of the live `apps/web` surface (mission +reads, coordination status, capability-gated `turn:send` added); rank-6 +composition corrected to ranks 1 and 4; SOT citations corrected to §3 +invariant 11 / REQ-TASK-001 / §5+A1; the §3.2 retirement clause +softened to match what the owning contracts actually schedule; §6.1 +scoped to outbound calls with an extractability lint, and §6.3 given +static companions for §4.1 and §4.3. + +This contract binds three things: the operation→tool mapping itself +(§2–§3), the command envelope every mapped operation satisfies +(§4), and the process rule that keeps the mapping closed (§5). Domain +semantics stay with their owning contracts — hierarchy (contract 1, +`hierarchy-schema.md`), grants (contract 2, `rbac-grant-model.md`), +wizard (contract 3, `onboarding-wizard.md`), identity +(`identity-lifecycle.md`), kanban lifecycle (`native-kanban-sot.md` +§5 and Amendment A1), roll-up (contract 8), API artifact format +(contract 9). + +## 1. Definitions + +1. **Official tool**: a command implemented in the framework packages and + exposed through the Gateway API; the CLI remains the primary execution + method for the same command (D8). The webUI is a Gateway client only. +2. **Mapped operation**: a webUI operation with a named official path in + §2 or §3. Anything else the webUI wants to do is unmapped and follows + §5. +3. **Legacy non-substitute**: an existing endpoint that resembles a P1 + need but is contractually barred from backing it (§3.2). + +## 2. P0 mapping (current operations, ratified as-is) + +This table is the complete measured P0 surface: every Gateway call the +web app's production sources make at this revision's head appears as a +row (independently re-measured at review; the three calls the first +measurement missed — mission reads, coordination status, and the +capability-gated `turn:send` emit — are rows below). The surface stays +bound to these paths: + +| WebUI operation | Official path | +| ----------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Register / log in / log out / OIDC callback | better-auth mount `/api/auth/*`; `GET /api/sso/providers` | +| List/show projects (legacy read) | `GET /api/projects`, `GET /api/projects/:id` | +| List tasks / task detail (legacy read) | `GET /api/tasks`, `GET /api/tasks/:id` — with the filtered legacy project/mission reads the same surfaces use | +| Mission list (legacy read) | `GET /api/missions` | +| Coordination status (legacy read) | `GET /api/coord/status` | +| Conversation CRUD/search/messages | `/api/conversations*` | +| Chat turn / stop / thinking / command execute+approve / streaming | `/chat` socket events `message`, `abort`, `set:thinking`, `command:execute`, `command:approve`; `turn:send` (capability-gated — emitted only when the server advertises the pi turn-runtime capability, which the current Gateway does not) | +| Harness/model selection | `GET /api/harnesses*`, `GET/PUT /api/chat/preferences/selection` | +| Preferences; provider inspect/test | `/api/memory/preferences`, `GET /api/providers`, `POST /api/providers/test` | +| Admin users / roles / ban / health | `/api/admin/users*`, `/api/admin/health` | + +P0 rows inherit §4 obligations as their backing controllers are next +touched; they are not required to be retrofitted in one sweep. + +## 3. P1 mapping (bound to the build-first tools) + +1. Every P1 operation maps to exactly one build-first command family, in + the T10-ruled rank order: + +| Rank | Command family (owning contract) | P1 webUI operations it backs | +| ---- | ---------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- | +| 1 | Hierarchy command family (contract 1 §5; grants attach per contract 2) | Company/estate/platform-project/workspace CRUD, parentage and reparenting, hierarchy reads; the wizard's initial-hierarchy step (contract 3 §3.4) | +| 2 | Hierarchy RBAC command/evaluator (contract 2) | Grant create/change/revoke at company/estate/platform-project; inherited evaluation down to workspace; authorization-safe hierarchy queries | +| 3 | Typed kanban command/query surface (SOT §5, Amendment A1) | Workspace task lifecycle (create/edit/cancel/archive/move), board rank, typed queries | +| 4 | Agent enrollment command | Enroll one agent: harness, credential reference/API-key intake (values never echoed), name/persona, assignment scope (contract 3 §3.5) | +| 5 | Authorized roll-up query (contract 8) | Read-only aggregated task counts/statuses at every hierarchy level over readable workspaces only | +| 6 | Onboarding orchestration (contract 3) | The re-runnable wizard flow, composing ranks 1 and 4 (its only grant write rides inside the rank-1 company-create command, contract 2 §4.3) | + +2. **Legacy non-substitutes.** The following MUST NOT back any P1 + operation, matching the audit findings: legacy `/api/projects` and + `/api/tasks` CRUD (planning-data records, not hierarchy nodes and not + the typed kanban boundary); `POST /api/workspaces` (filesystem + bootstrap, not audited hierarchy parentage); `/api/teams` reads (no + grants, no inheritance); `POST /api/bootstrap/setup` (one-shot + epoch transition, identity §3 — not the re-runnable wizard); the MCP + `brain_*` task mutations (legacy Brain writes, not the typed kanban + commands). These stay serving their existing P0/host consumers until + the owning contract (or a successor amendment) schedules each + retirement — no such migration is scheduled at this revision; the + freeze stands on its own. +3. New P1 mapping rows (operations this table does not list) are added by + amending this contract, not ad hoc (§5). + +## 4. Command envelope (request / result / error / audit) + +Binding on every mapped operation the build-first families expose: + +1. **Typed request and result.** Each command and query has an explicit + request DTO and result DTO in the shared types package, validated at + the Gateway boundary; unvalidated pass-through and `any`-typed + payloads are non-conformant. Mutations on records with an + expected-version rule in their owning contract carry the expected + version in the request and fail on mismatch with the conflict error + class (SOT §3 invariant 11 and REQ-TASK-001's concurrent-update + conflict acceptance; hierarchy per contract 1). +2. **Error taxonomy.** Every error result carries a stable + machine-readable code from a closed per-family enum plus an HTTP + status mapping, distinguishing at minimum: validation failure, + authentication failure, authorization refusal, not-found, conflict + (version/uniqueness), precondition/state refusal (e.g. bootstrap + epoch, suspended team subjects), and internal fault. Where contract + 2's no-existence-oracle rule applies, authorization refusal and + not-found are indistinguishable on the wire for unauthorized readers + — same code, same status, same shape. +3. **Audit linkage.** A mutating mapped operation emits exactly the + audit events its owning contract defines (contract 1 §5.2, contract 2 + §4.4, identity §§2–4, SOT audit rules); the envelope contributes the + correlation: every request accepts/generates a correlation id, + carried into the audit events and returned in the result, so a UI + action is traceable end to end. The mapping layer itself adds no + second audit stream. +4. **Fail-closed.** A mapped operation that cannot evaluate its + authorization or reach its owning tool refuses (contract 2 §3.5); the + envelope never degrades to an unauthorized fallback read or a direct + data access. +5. **CLI parity.** Each build-first family is invocable through the + official CLI against the same Gateway commands with the same + request/result/error contracts. No webUI-only command exists; a + Gateway command without CLI exposure is a conformance gap tracked at + the family's implementing issue. + +## 5. Closure rule (blocked on tooling) + +1. A webUI change that needs an operation with no mapping row is + **blocked on tooling**: the backing tool is built and mapped first + (D8). Scoring a gap "blocked on tooling" is mandatory, not + discretionary; working around it in the UI (direct DB or filesystem + access, calling a legacy non-substitute, embedding domain logic in + the web app) is non-conformant. +2. The mapping is enforced closed by §6.1's inventory witness: the web + app's network surface must be a subset of the mapped paths. + +## 6. Verification requirements + +Binding on the implementing PRs: + +1. **Network-surface inventory witness:** a CI assertion extracting the + web app's outbound Gateway calls — route literals at request call + sites and outbound socket emits in `apps/web` sources (inbound + handler registrations are not calls and are out of scope) — and + failing on any call outside the §2/§3 mapped paths. The inventory is + closed like contract 1 §6.3's allowlist: a new call fails until a + mapping row exists in the same PR. Dynamic route construction that + evades extraction is resolved toward the witness, enforced by an + extractability lint: every request call site takes a literal or + template-literal path, and a call site that does not fails the + assertion itself (the web-side analogue of contract 1's + raw-execution prong), never an exemption for the caller. +2. **Non-substitute witness:** the P1 surfaces (hierarchy, RBAC, kanban, + enrollment, roll-up, wizard UI) make zero calls to the §3.2 legacy + endpoints — asserted by the same inventory, scoped per surface. +3. **Envelope witnesses per family:** for each build-first family — a + request with an invalid DTO is refused with the validation code; a + version-mismatch mutation returns the conflict code; an unauthorized + read of an existing node and a read of a nonexistent node return + indistinguishable results where the no-existence-oracle rule applies; + a correlation id submitted on a mutation appears in its audit + event(s) and result. Two static companions: a type-level assertion + that the family's boundary accepts no `any`-typed or unvalidated + pass-through payload (§4.1), and a single-emitter assertion that the + mapped operation's audit events originate only from the owning + contract's audit emitter (§4.3's no-second-audit-stream, made + checkable). +4. **CLI-parity witness:** for each family, a CLI smoke invocation of at + least one command and one query against the Gateway succeeds with the + same typed result the web client receives. +5. **Fail-closed witness:** with the owning tool or grant state + unreachable (fault injection), the mapped operation returns the + internal-fault or authorization-refusal class and performs no + fallback read/write (extends contract 2 §7.6 to the mapping layer). + +## Ruling request + +Ratify sections 1–6 as written, with one decision embedded: + +- Decision (§3.2): the legacy endpoints named there are **frozen for new + consumers** as of ratification — existing P0/host consumers keep + working, new UI or tool code may not call them, and each is retired by + the migration its owning contract schedules. Alternative if rejected: + allow P1 surfaces to reuse legacy endpoints as interim backends — + rejected by the audit's finding that they cannot satisfy the + hierarchy/kanban/RBAC contracts, so the interim would ship + non-conformant semantics.