Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3231b91691 | ||
|
|
be32b693d0 |
@@ -0,0 +1,279 @@
|
|||||||
|
# Deployment Mode and Conversion Contract (D3)
|
||||||
|
|
||||||
|
Status: DRAFT — awaiting ratification (webui-audit S2, contract 6 of 9).
|
||||||
|
Authority: PRD D3 (Part I §3) — two modes chosen at install time,
|
||||||
|
Standalone and Enterprise, with the mode table (brains, user-data
|
||||||
|
isolation, secrets, conversion); Standalone → Enterprise conversion is
|
||||||
|
**one-way** and Enterprise is a **terminal state**. PRD D14 (Part I §7)
|
||||||
|
— the per-user brain split is optional in Standalone and keeping it is
|
||||||
|
the recommended default because it preserves forward-compatibility with
|
||||||
|
the one-way conversion. PRD D11 (Part I §9) — v1 ships the Standalone
|
||||||
|
flow only; Enterprise conversion is explicitly deferred. PRD D3
|
||||||
|
federation clause — federation is intentionally not fully designed,
|
||||||
|
deferred, and nothing in v1 may foreclose it.
|
||||||
|
|
||||||
|
Revision 2 (luna review F1–F7): the identity precondition restated in
|
||||||
|
identity-contract terms with a conversion-local acknowledgment record
|
||||||
|
this contract owns (F1); a durable, keyed preparation state with a
|
||||||
|
Standalone-safe representation rule, an in-transaction re-check fence,
|
||||||
|
and an exact flip boundary (F2); the §5.4 unknown-value rule stated
|
||||||
|
directly without the contradictory non-exhaustiveness clause (F3); the
|
||||||
|
D14 boundary bound here with a stable column-allowlist witness instead
|
||||||
|
of delegated to an unratified layout (F4); the conversion witness
|
||||||
|
matrix extended to every §4.2/§4.4 condition (F5); the mode-record
|
||||||
|
writer coverage imported concretely from contract 1 §6.3 with a named
|
||||||
|
schema, closed writer set, crafted-write probe, and mode-resolution
|
||||||
|
assertion (F6); the mode read command flagged as a §12.1 drafting
|
||||||
|
addition rather than a D8 mandate (F7). Ownership language aligned
|
||||||
|
with contract 3 revision 2: mode is recorded at bootstrap and read by
|
||||||
|
the wizard as input.
|
||||||
|
|
||||||
|
This contract binds the mode as a canonical platform property (§2), the
|
||||||
|
per-mode obligations and which contract owns each (§3), the conversion
|
||||||
|
transition (§4), the v1 non-foreclosure obligations (§5), and their
|
||||||
|
witnesses (§6). Domain semantics stay with their owning contracts:
|
||||||
|
wizard branching (contract 3 §2), identity/SSO
|
||||||
|
(`identity-lifecycle.md`), custody and per-user brain mechanics
|
||||||
|
(contract 7, `custody-schema.md`), tool mapping
|
||||||
|
(`tool-gateway-mapping.md`).
|
||||||
|
|
||||||
|
## 1. Definitions
|
||||||
|
|
||||||
|
1. **Mode**: the platform-wide deployment mode, exactly one of
|
||||||
|
`standalone` or `enterprise`. The vocabulary is closed in v1;
|
||||||
|
extension (e.g. a federation mode) is by amendment to this contract,
|
||||||
|
never ad hoc.
|
||||||
|
2. **Conversion**: the one-way transition `standalone → enterprise`.
|
||||||
|
No other mode transition exists.
|
||||||
|
3. **Conversion preconditions**: the verifiable conditions of §4.2 that
|
||||||
|
must all hold before the mode record may change.
|
||||||
|
4. **Preparation unit**: one re-runnable piece of pre-conversion work —
|
||||||
|
the migration of one secret to the Vault backend, or the partition
|
||||||
|
of one user's brain content (§4.3).
|
||||||
|
|
||||||
|
## 2. Mode is a canonical recorded property
|
||||||
|
|
||||||
|
1. Mode is recorded canonically in the platform database at bootstrap
|
||||||
|
as the operator's install-time choice (D3: modes are "chosen at
|
||||||
|
install time"). The record is a single-row keyed record
|
||||||
|
(`platform_mode`: mode value, recorded-at timestamp, bootstrap epoch
|
||||||
|
reference); this contract owns it, the bootstrap writer performs the
|
||||||
|
one v1 write (§6.2), and the wizard reads it as input (contract 3
|
||||||
|
§2.3). Mode is never derived from feature state (presence of Vault,
|
||||||
|
count of brains, count of users), and no component may infer a
|
||||||
|
different mode than the record states.
|
||||||
|
2. The record is readable by any authenticated user through a Gateway
|
||||||
|
command with CLI exposure. This read command is a **drafting
|
||||||
|
addition** ratified with this contract (PRD §12.1), not a D8
|
||||||
|
mandate: D8 binds only that any surface exposing the value goes
|
||||||
|
through official tooling. When a webUI surface consumes the read, a
|
||||||
|
mapping row is added to `tool-gateway-mapping.md` by amendment —
|
||||||
|
the same route §4.4 already binds for the conversion command.
|
||||||
|
Components branch on the read value only.
|
||||||
|
3. The record is immutable except by the §4 conversion transition.
|
||||||
|
Editing it by direct database access, config file, environment
|
||||||
|
variable, or wizard re-run is non-conformant (contract 3 §2.3:
|
||||||
|
changing mode later is conversion, not a wizard re-run).
|
||||||
|
|
||||||
|
## 3. Per-mode obligations (owner map)
|
||||||
|
|
||||||
|
The PRD mode table binds four rows; this contract assigns each an
|
||||||
|
owning contract so no obligation is unowned and none is bound twice:
|
||||||
|
|
||||||
|
| Obligation | Standalone | Enterprise | Owner |
|
||||||
|
| ------------------- | -------------------------------------- | -------------------------------------------------- | --------------------------------------------- |
|
||||||
|
| Brains | one mosaic-brain (system + user files) | system brain for config + one brain per user | contract 7 (custody/brain mechanics) |
|
||||||
|
| User-data isolation | single user | no user-data leakage between users; sharing opt-in | contract 7 (enforced by architecture, D14) |
|
||||||
|
| Secrets | OpenBao/Vault or flat files | OpenBao/Vault REQUIRED | this contract (§4.2 gate; steady-state check) |
|
||||||
|
| Conversion | may convert to Enterprise, one-way | terminal state | this contract (§4) |
|
||||||
|
|
||||||
|
The Standalone brains row states the default layout, not the only
|
||||||
|
valid one: the D14 per-user split is a MAY in Standalone with keeping
|
||||||
|
it the recommended default (PRD §7, contract 7 §6), and Vault-backed
|
||||||
|
secrets are equally valid Standalone configuration. Both prepared
|
||||||
|
states are therefore themselves valid Standalone states — the fact
|
||||||
|
§4.3 relies on.
|
||||||
|
|
||||||
|
In Enterprise steady state, a flat-file secrets backend is
|
||||||
|
non-conformant; the platform refuses to start Enterprise-mode
|
||||||
|
components against a flat-file secrets configuration (fail-closed, not
|
||||||
|
warn-and-run).
|
||||||
|
|
||||||
|
## 4. Conversion transition
|
||||||
|
|
||||||
|
1. **Direction and terminality.** The only transition is
|
||||||
|
`standalone → enterprise`. `enterprise → standalone` does not exist:
|
||||||
|
there is no command, no admin override, and no support path. An
|
||||||
|
attempt is refused with the precondition/state error class of the
|
||||||
|
command envelope (`tool-gateway-mapping.md` §4.2).
|
||||||
|
2. **Preconditions (all verified before the record changes):**
|
||||||
|
- Secrets: OpenBao/Vault is configured and reachable, and every
|
||||||
|
required secret is served from the Vault backend — none from a
|
||||||
|
flat-file backend. Secret migration completes before conversion;
|
||||||
|
this contract does not define the migration tooling, only the
|
||||||
|
gate.
|
||||||
|
- Brains: the per-user brain split required by the Enterprise row of
|
||||||
|
§3 is established for **every** existing user (or the deployment
|
||||||
|
already kept the split, the D14 recommended default). Brain
|
||||||
|
partitioning mechanics are contract 7; this contract binds only
|
||||||
|
that the split is complete before the mode flips.
|
||||||
|
- Identity: at least one platform administrator account exists that
|
||||||
|
is active in identity-contract terms — authenticated capability,
|
||||||
|
not banned, not deactivated (identity §2, §5). And the conversion
|
||||||
|
request carries a **configuration acknowledgment**: the current
|
||||||
|
canonical values of registration mode and per-provider JIT
|
||||||
|
enablement (identity §2.2, §4.1), echoed back in the request. A
|
||||||
|
mismatch between the echoed values and the canonical values at
|
||||||
|
verification refuses the conversion. This acknowledgment record
|
||||||
|
is conversion-local, owned by this contract, and stored with the
|
||||||
|
§4.4 audit event as the precondition evidence; it adds no
|
||||||
|
identity-contract obligation and no mode-specific identity
|
||||||
|
default — identity's own defaults remain valid states.
|
||||||
|
3. **Preparation state and the flip boundary.** Preparatory work is
|
||||||
|
tracked durably: each preparation unit (§1.4) records its
|
||||||
|
completion in a preparation table keyed by (bootstrap epoch, unit
|
||||||
|
identity — the secret's path, the user's id), written in the same
|
||||||
|
transaction as the unit's own effect where the unit's backend
|
||||||
|
allows it, and reconciled from the backend's actual state where it
|
||||||
|
does not (a secret already served by Vault, a brain already split,
|
||||||
|
is complete regardless of the table). Units are at-most-once per
|
||||||
|
key and re-runnable across attempts. **Standalone-safe
|
||||||
|
representation:** every preparation unit moves the deployment into
|
||||||
|
a state that is itself valid Standalone configuration (§3 note), so
|
||||||
|
an interrupted preparation leaves a fully operational Standalone
|
||||||
|
deployment reading its state through the ordinary contracts — no
|
||||||
|
rollback, fencing, or special Standalone read path is needed, and
|
||||||
|
no component behavior may key on "preparation in progress".
|
||||||
|
**The flip:** one transaction that (a) locks the mode record, (b)
|
||||||
|
re-verifies every §4.2 precondition after acquiring the lock, and
|
||||||
|
(c) writes the mode record and the §4.4 audit event. Any re-check
|
||||||
|
failure aborts with no write. External state that changes after the
|
||||||
|
re-check but before commit is bounded by the transaction window;
|
||||||
|
an external backend (Vault) failing after conversion is an
|
||||||
|
Enterprise runtime fault handled by §3's fail-closed steady-state
|
||||||
|
rule, not a conversion defect. An interrupted or failed conversion
|
||||||
|
leaves the record `standalone` and the platform fully operational;
|
||||||
|
there is no intermediate mode and no half-converted state
|
||||||
|
observable through the record.
|
||||||
|
4. **Authority and audit.** Conversion is a platform-administrator
|
||||||
|
command carrying an explicit irreversibility acknowledgment in its
|
||||||
|
request (distinct from the §4.2 configuration acknowledgment). It
|
||||||
|
is an official Gateway/CLI command (D8): when built, it is added to
|
||||||
|
the tool↔Gateway mapping by amendment (`tool-gateway-mapping.md`
|
||||||
|
§3.3). The transition emits an audit event (actor, prior mode, new
|
||||||
|
mode, precondition evidence reference including the configuration
|
||||||
|
acknowledgment) in the same transaction as the record change; the
|
||||||
|
event survives indefinitely. A refused attempt emits a refusal
|
||||||
|
event naming the failed precondition class and actor, with no
|
||||||
|
mode-change event.
|
||||||
|
|
||||||
|
## 5. v1 obligations (non-foreclosure)
|
||||||
|
|
||||||
|
v1 ships Standalone only (D11); the conversion command is deferred
|
||||||
|
work. v1 still MUST:
|
||||||
|
|
||||||
|
1. Record the mode per §2 at bootstrap, with `enterprise` a reserved,
|
||||||
|
refused value for bootstrap — v1 bootstrap accepts `standalone`
|
||||||
|
only. The wizard reads the record (contract 3 §2.3); nothing in v1
|
||||||
|
writes it after bootstrap.
|
||||||
|
2. Keep the §2.3 immutability rule: no v1 surface mutates the mode
|
||||||
|
record.
|
||||||
|
3. Not foreclose conversion: the v1 platform database holds no
|
||||||
|
sensitive user content — sensitive categories live in the owning
|
||||||
|
user's brain, and postgres holds structure, consent records, and
|
||||||
|
pointers only (the D14 boundary, PRD §7). Custody mechanics are
|
||||||
|
contract 7's; this contract binds the boundary itself here so v1
|
||||||
|
cannot ship a layout that makes the §4.2 brain precondition
|
||||||
|
unsatisfiable, and §6.3 gives it a stable witness that does not
|
||||||
|
depend on contract 7's internals. Conversion implementation
|
||||||
|
additionally requires contract 7 ratified.
|
||||||
|
4. Not foreclose federation: v1 components accept exactly the two §1.1
|
||||||
|
values wherever a mode value is parsed and refuse any other value
|
||||||
|
**before side effects** — a refused configuration, not undefined
|
||||||
|
behavior and not a crash mid-operation. Forward compatibility lives
|
||||||
|
in storage and architecture, not in parser speculation: the mode
|
||||||
|
record's storage is not structurally locked to two values (no
|
||||||
|
database-level two-value enum), and any future value (e.g. a
|
||||||
|
federation mode) is defined by a versioned amendment to this
|
||||||
|
contract before any component accepts it. The PRD defers
|
||||||
|
federation's shape entirely; this contract does not presume it
|
||||||
|
arrives as a third mode value.
|
||||||
|
|
||||||
|
## 6. Verification requirements
|
||||||
|
|
||||||
|
Binding on the implementing PRs:
|
||||||
|
|
||||||
|
1. **Mode-record witness (v1):** after bootstrap the mode is readable
|
||||||
|
via the Gateway command and CLI and equals the bootstrap-recorded
|
||||||
|
choice; bootstrap with mode `enterprise` is refused; bootstrap with
|
||||||
|
any unknown mode value is refused before side effects (§5.4).
|
||||||
|
2. **Writer-coverage witness (v1):** the mode record's writer set is
|
||||||
|
closed by the same three-prong static assertion contract 1 §6.3(b)
|
||||||
|
defines — symbol, class-table literal, and raw-execution prongs
|
||||||
|
with its allowlist composition rules — scoped to the
|
||||||
|
`platform_mode` table, with a writer allowlist containing exactly
|
||||||
|
the bootstrap writer in v1 (and exactly plus the conversion command
|
||||||
|
at the conversion milestone). Companions: a crafted direct write
|
||||||
|
attempted in a test fails and leaves the record unchanged; a
|
||||||
|
mode-resolution assertion that no shipped component derives mode
|
||||||
|
from feature state (mode reads occur only through the §2.2 read
|
||||||
|
surface — static assertion over Gateway, CLI, bootstrap, and
|
||||||
|
repository sources).
|
||||||
|
3. **D14-boundary witness (v1):** a column-allowlist assertion in the
|
||||||
|
style of contract 1 §6.2 that the platform database schema contains
|
||||||
|
no sensitive-content column — the §5.3 boundary — stable regardless
|
||||||
|
of contract 7's internals (contract 7 §7 carries the full custody
|
||||||
|
witnesses).
|
||||||
|
4. **No-downgrade witness (conversion milestone):** with mode
|
||||||
|
`enterprise`, a conversion request to `standalone` (and any crafted
|
||||||
|
mode-write) is refused with the precondition/state error class and
|
||||||
|
no record change.
|
||||||
|
5. **Precondition witnesses (conversion milestone),** each refused
|
||||||
|
with no record change and no partial mode effect, parameterized
|
||||||
|
over both OpenBao and Vault where secrets are involved:
|
||||||
|
(a) secrets backend unreachable; (b) one required secret still
|
||||||
|
flat-file backed (migration incomplete); (c) one unpartitioned user
|
||||||
|
brain in a **multi-user** deployment where every other user is
|
||||||
|
partitioned; (d) no active platform administrator (the only admin
|
||||||
|
banned or deactivated); (e) configuration acknowledgment missing or
|
||||||
|
mismatching the canonical registration/JIT values; (f) actor not a
|
||||||
|
platform administrator (authorization refusal); (g) irreversibility
|
||||||
|
acknowledgment absent. And the steady-state rule: an
|
||||||
|
Enterprise-mode component started against a flat-file secrets
|
||||||
|
configuration refuses to start (§3).
|
||||||
|
6. **Interruption and fence witnesses (conversion milestone):** fault
|
||||||
|
injection aborting conversion after each preparation unit and
|
||||||
|
between preparation and flip leaves the record `standalone` and the
|
||||||
|
platform operational in Standalone semantics (§4.3
|
||||||
|
Standalone-safety), and a re-attempt completes without duplicating
|
||||||
|
prepared state (at-most-once keys); a precondition invalidated
|
||||||
|
after preparation but before the flip (a secret reverted to
|
||||||
|
flat-file) is caught by the in-transaction re-check and refused.
|
||||||
|
7. **Audit witnesses (conversion milestone):** a completed conversion
|
||||||
|
has exactly one mode-change audit event, same-transaction with the
|
||||||
|
record change (transaction linkage asserted), carrying actor, prior
|
||||||
|
mode, new mode, and the precondition evidence reference including
|
||||||
|
the configuration acknowledgment; a failed attempt has a refusal
|
||||||
|
event naming the failed precondition class and no mode-change
|
||||||
|
event; the mode-change event remains queryable after subsequent
|
||||||
|
unrelated audit activity (retention probe).
|
||||||
|
8. **Mapping witness (conversion milestone):** the conversion command
|
||||||
|
and the mode read command each have their
|
||||||
|
`tool-gateway-mapping.md` row (added by amendment per §2.2/§4.4)
|
||||||
|
before the commands ship.
|
||||||
|
|
||||||
|
## Ruling request
|
||||||
|
|
||||||
|
Ratify sections 1–6 as written, with one decision embedded:
|
||||||
|
|
||||||
|
- Decision (§5): v1 implements the **mode record and its immutability
|
||||||
|
only** — bootstrap records `standalone`, the `enterprise` value is
|
||||||
|
reserved and refused, and the conversion command itself is deferred
|
||||||
|
to the Enterprise milestone, consistent with D11's deferred list.
|
||||||
|
v1 carries three obligations beyond the record: the closed writer
|
||||||
|
assertion, the D14 column boundary, and the unknown-value refusal
|
||||||
|
(§6.1–§6.3) — these are the non-foreclosure floor, not hidden
|
||||||
|
conversion work. Alternative if rejected: build the conversion
|
||||||
|
command inside v1 — rejected because D11 scopes v1 to the Standalone
|
||||||
|
slice and conversion depends on contract 7 custody mechanics that
|
||||||
|
are themselves not in the v1 slice.
|
||||||
@@ -1,197 +0,0 @@
|
|||||||
# 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.
|
|
||||||
Reference in New Issue
Block a user