Files
stack/docs/requirements/tool-gateway-mapping.md
fred 19ebc422aa
ci/woodpecker/push/publish Pipeline was successful
docs: tool-gateway mapping contract (S2 contract 5) (#1438)
2026-08-28 22:04:03 +00:00

15 KiB
Raw Permalink Blame History

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 F1F5): 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)
  1. 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.
  2. 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 §§24, 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 16 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.