15 KiB
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
- 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.
- 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.
- 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)
- 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) |
- Legacy non-substitutes. The following MUST NOT back any P1
operation, matching the audit findings: legacy
/api/projectsand/api/tasksCRUD (planning-data records, not hierarchy nodes and not the typed kanban boundary);POST /api/workspaces(filesystem bootstrap, not audited hierarchy parentage);/api/teamsreads (no grants, no inheritance);POST /api/bootstrap/setup(one-shot epoch transition, identity §3 — not the re-runnable wizard); the MCPbrain_*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. - 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:
- 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). - 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.
- 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.
- 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.
- 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)
- 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.
- 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:
- 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/websources (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. - 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.
- 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). - 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.
- 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.