fix: fail closed on stale/terminal assignments; canonicalize incident_links
Address REQUEST_CHANGES on PR #619: - require_valid_assignment rejects terminal work states and head drift - incident_links scope keys normalize NULL/blank to '' for UNIQUE - remove trailing whitespace in control-plane-db-substrate.md
This commit is contained in:
@@ -1,7 +1,9 @@
|
||||
# Control-plane DB substrate (#613)
|
||||
|
||||
**Status:** Implemented (SQLite single-writer MVP)
|
||||
**ADR:** [`mcp-allocator-control-plane-observability-adr.md`](mcp-allocator-control-plane-observability-adr.md)
|
||||
**Status:** Implemented (SQLite single-writer MVP)
|
||||
|
||||
**ADR:** [`mcp-allocator-control-plane-observability-adr.md`](mcp-allocator-control-plane-observability-adr.md)
|
||||
|
||||
**Module:** `control_plane_db.py`
|
||||
|
||||
## Architecture statement
|
||||
@@ -14,17 +16,19 @@
|
||||
|------------|--------|
|
||||
| Schema | `sessions`, `work_items`, `leases`, `assignments`, `terminal_locks`, `events`, `incident_links` |
|
||||
| Atomic assign+lease | `ControlPlaneDB.assign_and_lease` — one `BEGIN IMMEDIATE` transaction |
|
||||
| Mutation gate | `require_valid_assignment` — workers need a live assignment/lease |
|
||||
| Mutation gate | `require_valid_assignment` — live lease + allowed action + non-terminal work + non-stale head |
|
||||
| Heartbeat / release / expire | Lease lifecycle helpers |
|
||||
| Terminal-lock index | Routing signal for #600 (terminal path first) |
|
||||
| `incident_links` | Provider-neutral link model for #612 — **not** assignable work |
|
||||
| `incident_links` | Provider-neutral link model for #612 — **not** assignable work; scope keys NULL-safe |
|
||||
|
||||
## Hard rules (enforced in code)
|
||||
|
||||
1. Assignable `work_items.kind` ∈ {`issue`, `pr`} only — **never** raw Sentry/GlitchTip incidents.
|
||||
2. Two concurrent sessions cannot both receive an active assignment on the same open work item (second gets `wait`).
|
||||
3. Merged/closed work items return `no_safe_work`.
|
||||
4. SQLite path is the **single-writer MVP** (`GITEA_CONTROL_PLANE_DB`, default under `~/.cache/gitea-tools/control-plane/`). Multi-session multi-host production requires **Postgres** or a **single allocator daemon** (ADR §6).
|
||||
3. Merged/closed work items return `no_safe_work` at assign time, and `require_valid_assignment` fails closed if the work item becomes terminal later.
|
||||
4. Assignments pin `expected_head_sha`; mutations fail closed if the work item head drifts.
|
||||
5. SQLite path is the **single-writer MVP** (`GITEA_CONTROL_PLANE_DB`, default under `~/.cache/gitea-tools/control-plane/`). Multi-session multi-host production requires **Postgres** or a **single allocator daemon** (ADR §6).
|
||||
6. `incident_links` optional scope fields are stored as empty strings (never NULL) so UNIQUE is canonical across minimal upserts.
|
||||
|
||||
## Dependency chain
|
||||
|
||||
|
||||
Reference in New Issue
Block a user