# Native Kanban/SOT — Remediated Shared Contract v1

**Status:** CONTROL-PLANE SI-001 AMENDMENT AUTHORIZED; prior KCR-001–016 independent-review GO retained; rc.4 requires independent schema/SecReview before KBN-100
**Version:** 1.0.0-rc.4
**Date:** 2026-07-14
**Change authority:** Mosaic control plane/Jason only
**SI-001 amendment authority:** `web1:mosaic-100` control-plane decision under issue #753

## Amendment record

### 1.0.0-rc.4 — KBN010-SI-001

- **Choice:** add the explicitly named, non-partial unique candidate key `missions_workspace_id_uidx` on `missions(workspace_id, id)` and retain `missions_workspace_project_id_uidx` on `(workspace_id, project_id, id)`.
- **Rationale:** mission `id` remains globally unique, while the composite candidate key makes the frozen tenant-safe generic mission relations valid. `artifacts` and `approval_decisions` are polymorphic exactly-one-target records and do not consistently carry `project_id`; widening both children would unnecessarily broaden v1 and its target semantics.
- **Exact effect:** `artifacts_workspace_mission_fk` and `approval_decisions_workspace_mission_fk` continue to reference the exact ordered columns `missions(workspace_id, id)` with RESTRICT deletion, now backed by a matching candidate key.
- **Non-effect:** no SOT, tenancy, project-congruence, proposal-audit, approval, fencing, immutability, no-cascade, API, or wire-version invariant changes. The `SuccessEnvelopeV1.contractVersion` remains `1.0.0`.
- **Historical evidence boundary:** `KBN-010-THREAT-AUTH-CONSTRAINT-GATE.md` intentionally remains the immutable rc.3 blocker verdict that detected SI-001; this rc.4 record and the #753 scratchpad append are the authorized disposition. Rewriting the gate verdict is outside this amendment's exclusive scope.
- **Gate:** this amendment resolves the DDL defect identified by KBN010-SI-001 but does not itself lift KBN-100; independent schema/SecReview remains required.

## 1. Authority

Concrete contracts are the four `contracts/*.v1.ts` files. PostgreSQL/current-main Drizzle is the sole writable SOT. Public health, Valkey, files, exports, providers, browser state, and outage notes cannot authorize/reconstruct writes. Mechanical Coordinator is non-LLM with no scope/gate/certification/merge authority. Certifier is final independent gate with no merge authority. No feature lane starts until this canon merges and the KBN-010/KBN-105 prerequisites are satisfied.

## 2. Health proof and exact failures

`KanbanHealthResponseV1` is a discriminated union:

| State                |  read | write | Capability                                          |
| -------------------- | ----: | ----: | --------------------------------------------------- |
| `healthy`            |  true |  true | reads; public state still cannot authorize mutation |
| `read-only-degraded` |  true | false | reads only                                          |
| `write-unavailable`  | false | false | diagnostics only                                    |

Every response has `checkedAt`, `validUntil`, `policyRevision`; contradictory booleans fail validation.

For a mutation, Gateway opens the PostgreSQL transaction, executes the live write probe on that transaction/connection, mints the internal branded `PostgresWriteHealthProofV1`, and revalidates time/policy/transaction identity immediately before mutation. Public REST/MCP/CLI DTOs never accept health/proof fields. Valkey/caller assertions cannot mint proof. Pure Coordinator takes `KanbanEvaluationContextV1`; persistence takes `InternalKanbanMutationContextV1` or probes internally.

| Case                         |                        HTTP | Frozen result                                                       | Retry                |
| ---------------------------- | --------------------------: | ------------------------------------------------------------------- | -------------------- |
| degraded write               |                         503 | `KANBAN_WRITE_HEALTH_UNPROVEN`, `read-only-degraded`, `not_applied` | false                |
| write unavailable            |                         503 | `KANBAN_WRITE_UNAVAILABLE`, `write-unavailable`, `not_applied`      | false                |
| version conflict             |                         409 | `AGGREGATE_VERSION_CONFLICT`, actual version, `not_applied`         | false                |
| timeout/unreachable          |             timeout/502/504 | `retryable_transport_error`, `unknown`                              | same idempotency key |
| stale fence/session/approval | coordinator rejection union | `not_applied`                                                       | false                |

Required negatives: contradictory state, expired/policy-mismatched/wrong-transaction proof, Valkey-only health, forged healthy, and exhaustive non-cross-mapping of 503 vs 502/504/timeout vs 409.

## 3. Canonical schema invariants

Complete declaration: `contracts/kanban-schema.v1.ts`.

- Tables: tenant/identity (`workspaces`, members, teams/members, agents/sessions); planning (`projects`, `milestones`, current-milestone join, `missions`, mission-milestones, `tasks`, normalized tags, dependencies); orchestration (`task_assignments`, durable execution state, leases, checkpoints/evidence); governance (`change_proposals`, immutable artifacts/evidence, events, approvals, outbox, external links).
- Task statuses: `backlog | ready | in_progress | blocked | in_review | done | cancelled`.
- Assignment states everywhere: `awaiting_approval | policy_pre_authorized | approved | rejected | leased | released | expired | superseded`.
- Specialist roles everywhere: `planning | enhance | coder | review | security-review | pr-monitor | certifier`.
- Owner uses exactly-one user/team; assignment principal exactly-one user/team/agent; users require active membership; agent/session and all evidence are workspace-bound.
- Task→mission/milestone/parent, mission→milestone, and project→current-milestone are project-congruent composite relations.
- Mission `id` remains globally unique. The additional non-partial `missions_workspace_id_uidx` candidate key on `(workspace_id, id)` exists only to support the frozen workspace-safe polymorphic artifact and approval-decision mission relations; the project-congruent `(workspace_id, project_id, id)` key remains authoritative wherever `project_id` is present.
- Dependency identity is workspace+predecessor+successor independent of type.
- Approval evidence and checkpoint evidence are workspace-scoped joins to immutable artifacts, never JSON ID arrays.
- Proposal audit links are composite relations: `(workspace_id, submitted_audit_event_id)` and `(workspace_id, accepted_command_audit_event_id)` reference `task_events(workspace_id, id)` with RESTRICT deletion.
- Assignment is persisted with task/version, exact target/session, expiry/state/policy/proposer/reason. Approval relates to assignment. Lease acquisition accepts IDs, then reloads/locks and validates every relation.
- `tasks.fencing_counter` is bigint; locked atomic increment/RETURNING creates a decimal-string lease token. Lease/checkpoint composites bind exact workspace+task+assignment/session+fence.
- `task_execution_states` durably records retry/quarantine/exhaustion.
- Tags are normalized; legacy `tasks.tags` remains through N-1. Archive is explicit actor/reason/time and does not change lifecycle.
- Canonical parents use RESTRICT. Events/checkpoints/artifacts/evidence are INSERT/SELECT-only for application roles. Normal flow archives/cancels; purge is audited break-glass retention work.

## 4. Outage proposal contract

`change_proposals` stores workspace, active-member proposer, source-note digest, target/version, typed command/payload, idempotency, lifecycle, decision actor/reason/time, proposal version, and submit/accepted event IDs. Both event IDs are workspace-aware composite foreign keys to `task_events(workspace_id, id)`; a bare UUID is never sufficient.

Submission preallocates the proposal ID. One transaction inserts `change_proposal.submitted` with the proposal workspace, `aggregate_type='change_proposal'`, `aggregate_id=<new proposal ID>`, `previous_version=NULL`, and `new_version=1`, then inserts the proposal referencing that event. Missing, foreign-workspace, wrong-type, or unrelated-proposal events abort the transaction.

Submit/list/get/accept/reject are explicit Gateway commands. Pending/rejected proposals are inert: no scheduling, dependency/gate satisfaction, or direct target mutation. Acceptance locks proposal+target, obtains fresh transaction-local proof, verifies pending/expected version, invokes the normal command handler, and atomically stores the emitted normal-command event ID. That event must share the proposal workspace, match `target_aggregate_type` and `target_aggregate_id`, use `causation_id=submitted_audit_event_id`, and carry `payload.changeProposalId=<locked proposal ID>`. Missing, foreign-workspace, unrelated-target, unrelated-proposal, or unrelated-command events abort acceptance.

## 5. Concrete current-main N-1 migration delta

**Inspected:** `origin/main:packages/db/src/schema.ts` at `e72388b2cbfe400842fe940fa6cabf984ed43711` (2026-07-13). It has global teams/no workspace keys, legacy project/mission/task statuses, nullable task project/mission, `tasks.assignee/tags/due_date`, mission JSON/config, duplicated `mission_tasks.status`, legacy agent fields, and separate fleet `backlog` claims.

Legacy columns remain declared in unified `schema.ts` for expand + full N-1/rollback window. Generation must not infer early drops.

### 5.1 Ordered phases

1. **Pre-expand:** N-1 patch stops `mission_tasks.status` as write source; inventory writers; backup/checksum.
2. **Expand:** add enums/tables and nullable-first columns; retain legacy declarations/uniques; emit no v1-only status.
3. **Backfill:** bootstrap workspace; bounded idempotent cursor/checksum batches; quarantine ambiguous rows.
4. **Validate:** no null tenant, cross-project link, ambiguous owner; status/tag/date/config retention; then constraints/NOT NULL.
5. **Compatibility:** N-1 reads legacy; same-DB transaction mirrors only unavoidable fields; never file/Valkey dual write.
6. **Switch:** stop N-1 writers; Gateway sole command boundary; enable canonical statuses.
7. **Contract release:** later release after rollback/N-1; remove compatibility/global uniques/legacy fields.

### 5.2 Mission candidate-key and dependent-FK DDL order

KBN-100 migration DDL must execute the SI-001 portion in this order:

1. expand/backfill `missions.workspace_id` and `missions.project_id` while preserving the global `missions.id` primary key and the project-congruent `missions_workspace_project_id_uidx` key;
2. prove duplicate-key feasibility on the production-shape dataset: `(workspace_id, id)` has no duplicate groups and global `id` uniqueness remains intact;
3. create the non-partial unique index `missions_workspace_id_uidx` on exact ordered columns `(workspace_id, id)`;
4. only after step 3, create/alter `artifacts` and add `artifacts_workspace_mission_fk` from `(workspace_id, mission_id)` to exact `missions(workspace_id, id)` with `ON DELETE RESTRICT`;
5. only after step 3, create/alter `approval_decisions` and add `approval_decisions_workspace_mission_fk` from `(workspace_id, mission_id)` to exact `missions(workspace_id, id)` with `ON DELETE RESTRICT`;
6. validate both constraints and prove a mission ID paired with a foreign workspace is rejected for each child.

The candidate key is intentionally redundant with globally unique `missions.id`, but PostgreSQL requires a matching unique candidate key for the exact two-column FK target. It is additive and N-1-safe. Pre-switch rollback drops the two dependent FKs/tables before dropping this candidate key, preserves the global primary key and project-congruent key, and follows the existing freeze/reconciliation rule after the first canonical mutation.

### 5.3 New audit/proposal DDL order

KBN-100 migration DDL must execute in this order:

1. create `task_events` and its unique `(workspace_id, id)` key;
2. create `change_proposals` with nullable acceptance-event ID and required submission-event ID;
3. add `change_proposals_workspace_submitted_event_fk` from `(workspace_id, submitted_audit_event_id)` to `task_events(workspace_id, id)` with `ON DELETE RESTRICT`;
4. add `change_proposals_workspace_accepted_command_event_fk` from `(workspace_id, accepted_command_audit_event_id)` to the same composite key with `ON DELETE RESTRICT`;
5. install application-role immutability privileges and same-transaction semantic validation before enabling proposal commands.

The submission transaction inserts the event first using a preallocated proposal UUID, then the proposal. Acceptance inserts the normal command event before updating the locked proposal. Neither FK is omitted or replaced by a bare UUID/index check.

### 5.4 Field map

| Current                                        | Expand/backfill                                                                                                       | N-1 compatibility                                     | Switch/contract                                                                    |
| ---------------------------------------------- | --------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------- | ---------------------------------------------------------------------------------- |
| global `teams`, `team_members`                 | add workspace nullable; bootstrap; validate active owners                                                             | retain global slug/FKs                                | workspace composites; global unique contracts later                                |
| `projects.status`                              | add `canonical_status`; map active/paused/completed/archived                                                          | mirror representable values; no `planning`            | canonical authority; legacy contracts later                                        |
| project `owner_id/team_id/owner_type`          | add exact accountable user/team; deterministic map or quarantine                                                      | preserve old reads and compare drift                  | canonical exact-one; remove legacy after parity                                    |
| current milestone                              | create milestones then join table (no circular DDL)                                                                   | absent to N-1                                         | join is authority                                                                  |
| nullable `missions.project_id`                 | derive workspace/project; null/orphan exception, never guess                                                          | keep nullable legacy read                             | canonical required; validate/set NOT NULL later                                    |
| mission relational candidate keys              | retain global `id` PK and project-congruent key; add non-partial `(workspace_id,id)` key before artifact/approval FKs | additive key is ignored safely by N-1 readers/writers | retain both composite keys; generic mission children use exact workspace+ID target |
| mission `description`                          | add objective; preserve description; reviewed nonblank mapping                                                        | N-1 description                                       | objective authority; retain until signed review                                    |
| `missions.status`                              | add canonical; planning→draft, active/paused/completed/failed same                                                    | no new-only statuses emitted                          | canonical authority                                                                |
| mission `milestones` JSON                      | normalize with source digest; preserve malformed/original                                                             | N-1 reads JSON; no reverse sync                       | normalized authority; JSON removed after checksum sign-off                         |
| mission config/metadata/phase/user             | retain all; map known typed policy only                                                                               | all remain declared                                   | remove only by signed consumer inventory                                           |
| nullable `tasks.project_id`                    | derive explicit/mission project; orphan quarantine                                                                    | retain nullable read/write during compatibility       | canonical required; NOT NULL later                                                 |
| `tasks.mission_id`                             | add project-congruent composite                                                                                       | old relation readable                                 | composite authority                                                                |
| `tasks.status`                                 | canonical: not-started→backlog, in-progress→in_progress, others same                                                  | no ready/in_review emission                           | canonical authority                                                                |
| `tasks.assignee`                               | deterministic active user/team/agent assignment; raw value preserved if ambiguous                                     | mirror text only if unambiguous                       | canonical owner/assignment; remove after no-loss sign-off                          |
| `tasks.tags` JSON                              | normalize trim/case/dedupe with original digest                                                                       | transactionally mirror normalized rows                | normalized authority; JSON later removed                                           |
| `tasks.due_date`                               | copy exactly to `due_at`                                                                                              | mirror                                                | due_at authority; legacy later                                                     |
| task common fields                             | preserve metadata byte-for-byte; add criteria/rank/retry/archive/version/fence                                        | old reads valid                                       | new fields canonical                                                               |
| `mission_tasks.status`                         | keep; prohibit as write source; linked status ignored; unlinked becomes task or reject                                | read-only compatibility value                         | membership uses task mission; status dropped after no readers                      |
| mission-task notes/PR/user                     | map to metadata/artifact/event/link/attribution; preserve                                                             | read-only                                             | remove after parity                                                                |
| `agents.status`                                | add workspace/lifecycle/runtime/roles; status remains presence                                                        | retain all legacy fields                              | lifecycle/roles authority; status may remain telemetry                             |
| agent project/owner/prompt/tools/skills/config | preserve; validate tenant; derive typed capabilities without loss                                                     | N-1 reads                                             | removal only by separate inventory                                                 |
| fleet `backlog`                                | map to designated-project tasks; edges; claimed rows quarantine                                                       | freeze claims before switch; read-only compare        | task/lease authority; retire after stabilization                                   |

### 5.5 Required migration tests

Empty DB; exact production-shape snapshot; crash/resume; rollback before switch; N-1 startup/read/write; workspace/member negatives; status-shadow/no premature new status; `mission_tasks.status` write prohibition; tags/assignee/date/mission JSON/config/description/agent checksum; project congruence/current-milestone order; backlog freeze/no dispatch; and proof legacy declarations persist until contract release.

SI-001 adds frozen future executable evidence: empty and production-shape migrations create `missions_workspace_id_uidx` before either dependent FK; duplicate-key feasibility preflight returns no `(workspace_id,id)` duplicate groups without weakening global `id` uniqueness; N-1 startup/read/write behavior is unchanged; pre-switch rollback removes dependents before the candidate key; both exact FK column lists reconcile to the candidate key; and foreign-workspace mission references fail for both artifacts and approval decisions. TDD is not applicable to this design-only amendment; KBN-100 must implement these negative migration tests before runtime schema release.

Proposal-specific negatives must attempt: missing submission event, foreign-workspace submission event, foreign-workspace acceptance event, same-workspace event for another proposal, event for another target aggregate, and unrelated normal-command event. Every attempt must fail atomically with no accepted proposal and no target mutation.

## 6. Ownership and Coordinator split

coder2 solely owns `packages/db/src/schema.ts`, `packages/db/drizzle/**`, journal/metadata, and migration tests. No other lane generates migrations. Expand is additive; no drop/rename/narrow; constraints validate before NOT NULL; compatibility is same-DB only; contract is later.

KBN-200/coder4 owns pure `MechanicalCoordinatorDecisionEngineV1`: complete immutable snapshots in, deterministic eligibility/proposal/retry decisions out; no ID loading, SQL, Gateway, Valkey, proof, persistence, restart I/O, or LLM.

KBN-210/coder3 owns `MechanicalCoordinatorServicePortV1`: ID loading, locks, fresh proof, assignment/approval persistence, atomic fencing, lease/checkpoint/outbox, Valkey wakes, durable retry/quarantine, and `recoverFromPostgres`. Cycle: load snapshots → pure decision → persist assignment → authoritative approval/policy → acquire by IDs/locks → increment fence → lease → ack/heartbeat/checkpoint → submit to review or durable retry/quarantine. No completion/certification/merge method exists.

## 7. Exact Gateway/DTO freeze for KBN-105

### 7.1 Common wire rules

Base is `/api/v1/workspaces/:workspaceId`. Mutations require header `Idempotency-Key` (1–128 chars). Existing-aggregate mutations also require `If-Match-Version` (positive integer); create and privileged assignment-cycle requests are the only exceptions, while proposal submission carries `expectedTargetVersion` in its body. Body workspace fields are forbidden. Tenant denial follows one 404/403 policy without foreign existence detail.

```ts
interface SuccessEnvelopeV1<T> {
  contractVersion: '1.0.0';
  data: T;
  aggregateRevision: string;
  correlationId: string;
}
interface ListEnvelopeV1<T> extends SuccessEnvelopeV1<T[]> {
  page: { cursor: string | null; nextCursor: string | null; limit: number };
}
```

Errors are the exact health/transport/version unions in §2 plus validation/auth/not-found. Public DTOs never expose/accept internal write proof.

### 7.2 Exact route registry

| Method/path                                             | Request body/query                                                                                                                                                                                                             | Success data                                                         |
| ------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------- |
| `GET /kanban-health`                                    | none                                                                                                                                                                                                                           | `KanbanHealthResponseV1`                                             |
| `GET /projects`                                         | `status,ownerUserId,ownerTeamId,cursor,limit`                                                                                                                                                                                  | project list                                                         |
| `POST /projects`                                        | `name,key,description,status,priority,ownerUserId XOR ownerTeamId,metadata`                                                                                                                                                    | project                                                              |
| `GET /projects/:projectId`                              | none                                                                                                                                                                                                                           | project                                                              |
| `PATCH /projects/:projectId`                            | editable create fields + expected header                                                                                                                                                                                       | project                                                              |
| `POST /projects/:projectId/archive`                     | `reason`                                                                                                                                                                                                                       | project                                                              |
| `GET /tasks`                                            | `projectId,missionId,milestoneId,status,priority,ownerUserId,ownerTeamId,specialistRole,tag,dueState,archived,cursor,limit`                                                                                                    | task summary list                                                    |
| `POST /tasks`                                           | `projectId,missionId?,milestoneId?,parentTaskId?,title,description?,acceptanceCriteria[],status,priority,rank,ownerUserId XOR ownerTeamId,specialistRole?,dueAt?,notBeforeAt?,estimateMinutes?,retryPolicy?,tagIds[],metadata` | task detail                                                          |
| `GET /tasks/:taskId`                                    | none                                                                                                                                                                                                                           | task detail including readiness/dependencies/assignment/lease/events |
| `PATCH /tasks/:taskId`                                  | editable non-transition fields                                                                                                                                                                                                 | task detail                                                          |
| `POST /tasks/:taskId/transition`                        | `toStatus,reason?`                                                                                                                                                                                                             | task detail                                                          |
| `POST /tasks/:taskId/move`                              | `toStatus?,beforeTaskId?,afterTaskId?`                                                                                                                                                                                         | task detail with persisted rank                                      |
| `POST /tasks/:taskId/archive`                           | `reason`                                                                                                                                                                                                                       | task detail                                                          |
| `PUT /tasks/:taskId/tags`                               | `tagIds[]`                                                                                                                                                                                                                     | task detail                                                          |
| `POST /tasks/:taskId/dependencies`                      | `predecessorTaskId,type`                                                                                                                                                                                                       | dependency                                                           |
| `DELETE /tasks/:taskId/dependencies/:predecessorTaskId` | no body                                                                                                                                                                                                                        | deleted dependency ID                                                |
| `GET /tasks/:taskId/events`                             | `cursor,limit`                                                                                                                                                                                                                 | event list                                                           |
| `GET /tags`                                             | `query,cursor,limit`                                                                                                                                                                                                           | tag list                                                             |
| `POST /tags`                                            | `name,color?`                                                                                                                                                                                                                  | tag                                                                  |
| `GET /change-proposals`                                 | `state,targetType,targetId,cursor,limit`                                                                                                                                                                                       | proposal list                                                        |
| `POST /change-proposals`                                | `sourceNoteDigest,targetType,targetId,expectedTargetVersion,commandType,commandPayload`                                                                                                                                        | inert proposal                                                       |
| `GET /change-proposals/:proposalId`                     | none                                                                                                                                                                                                                           | proposal                                                             |
| `POST /change-proposals/:proposalId/accept`             | `reason`                                                                                                                                                                                                                       | proposal + normal command result                                     |
| `POST /change-proposals/:proposalId/reject`             | `reason`                                                                                                                                                                                                                       | proposal                                                             |
| `GET /coordinator/eligibility`                          | `projectId?,missionId?,cursor,limit`                                                                                                                                                                                           | `EligibilityDecisionV1[]`                                            |
| `POST /coordinator/assignment-cycles`                   | `limit`                                                                                                                                                                                                                        | assignment proposals; privileged internal                            |
| `POST /coordinator/assignments/:assignmentId/approve`   | `decision,reason,policyRevision,artifactIds[]`                                                                                                                                                                                 | approval decision                                                    |
| `POST /coordinator/leases/acquire`                      | `taskId,assignmentId,approvalDecisionId,targetSessionId,leaseTtlSeconds`                                                                                                                                                       | lease with decimal-string fence                                      |
| `POST /coordinator/leases/:leaseId/ack`                 | `taskId,sessionId,fencingToken`                                                                                                                                                                                                | lease                                                                |
| `POST /coordinator/leases/:leaseId/heartbeat`           | `taskId,sessionId,fencingToken,extendSeconds`                                                                                                                                                                                  | lease                                                                |
| `POST /coordinator/leases/:leaseId/checkpoints`         | `taskId,sessionId,fencingToken,sequence,resumableSummary,artifactIds[],contextUsagePercent`                                                                                                                                    | checkpoint                                                           |
| `POST /coordinator/leases/:leaseId/submit-review`       | `taskId,sessionId,fencingToken,artifactIds[],summary`                                                                                                                                                                          | task in `in_review`                                                  |

All Coordinator mutations except human approval are service-identity-only. Generic task PATCH cannot perform claim/heartbeat/checkpoint/review/certification/completion shortcuts. Completion after certification uses a separately gated lifecycle command owned by the Portfolio/Sub-Orchestrator flow, not the Coordinator.

### 7.3 DTO invariants

Task summary/detail use exact schema vocabularies, owner union, `version: number`, `fencingCounter: string`, explicit `archivedAt/by/reason`, normalized tags, computed readiness, and separate assignment/lease. Assignment DTO includes one persisted ID, task/version, exact principal/agent/session, role, state, expiry, policy, proposer/reason. Lease/checkpoint DTOs serialize every fence as decimal string. Proposal DTO exposes no hidden write authority.

### 7.4 MCP ownership and mapping

coder3 exclusively owns:

- `apps/gateway/src/mcp/mcp.dto.ts`
- `mcp.controller.ts`
- `mcp.service.ts`
- `mcp.module.ts`
- `mcp.tokens.ts`
- `mcp.service.spec.ts`

MCP tools are thin maps: `mosaic_projects_{list,get,create,update,archive}`, `mosaic_tasks_{list,get,create,update,transition,move,archive,set_tags,add_dependency,remove_dependency}`, and `mosaic_change_proposals_{list,get,submit,accept,reject}` to the exact routes above. coder4 owns CLI/projection clients only and must not edit Gateway MCP files.

KBN-105 publishes route+DTO fixture digest before KBN-110/120/130. Every web/CLI/MCP call must match this registry and the generated client.

## 8. Recovery contract and bounded delivery slice

Runtime must invoke normative `validateRecoveryPostureV1`; JSON Schema alone is insufficient. It rejects unknown fields, PITR/WAL mismatch, RPO better than mechanism, unsafe storage, and weakened High-assurance. High-assurance is RPO 15m/RTO 4h, WAL ≤5m, PITR ≥35d, base ≤24h, restore test ≤30d, break-glass ≤90d, encrypted separate-failure-domain storage.

KBN-115/coder2 owns `packages/config/src/recovery-posture.ts`, tests, and recovery runbook. It wires parser/refinement, override audit, mechanism assertions, restore test, and break-glass evidence. Any deployment manifest is separately enumerated and Mos-serialized. Recovery config has no SOT/gate/Coordinator authority fields.

## 9. Integration, security, and hold

Required release evidence includes empty/prod/partial/rollback/N-1 migration tests; cross-workspace and same-workspace wrong-project negatives; active-membership owners/principals; proposal inertness/normal acceptance; exact failure mapping; concurrent monotonic bigint fences; relational lease/checkpoint/evidence mismatch; immutability privileges/RESTRICT; recovery validation/mechanism evidence; endpoint registry alignment; accessible web journeys; author≠reviewer; mandatory SecReview; final Certifier pass/no merge authority.

### 9.1 SI-001 amendment gate and #757 boundary

- KBN-100 must provide the §5.2 candidate-key ordering, duplicate-feasibility, exact-FK reconciliation, empty/prod/N-1/rollback, and two-child foreign-workspace evidence before SI-001 can be certified closed.
- All prior KCR-001–016 decisions and fixed SOT/tenant/authority, proposal-audit, approval, task-fencing, immutability, and no-cascade invariants remain unchanged.
- Read-only PR #757 cross-check: its logical-agent connector lease/CAS fencing uses separate runtime tables/contracts and `lease_epoch`; rc.4 changes only the frozen `missions` candidate key. There is no shared table, index, FK, identity, fence, or authority semantic to consume or reconcile, and #757 remains owned by its existing lane.

The build hold remains active until independent re-review reports GO for KCR-001–016 and the rc.4 SI-001 amendment. Mos alone releases waves and serializes integration roots.
