Add control-plane DB for atomic LLM work assignment, leases, and session coordination #613

Closed
opened 2026-07-09 18:50:45 -05:00 by jcwalker3 · 2 comments
Owner

Problem

The current Gitea/MCP workflow allows multiple LLMs to independently inspect the queue and choose the same issue or PR. Existing leases and guards often catch unsafe actions after work has already started, but they do not prevent duplicate work up front. This causes reviewer collisions, stale lease confusion, duplicate comments, contested approvals, and wasted validation.

Goal

Add a small MCP Control Plane database that provides transactional coordination for sessions, assignments, leases, heartbeats, terminal locks, and Sentry-linked incidents. Gitea remains the durable source of truth for issue/PR history, comments, reviews, and labels.

Related work (do not treat as duplicates)

  • #600 — controller-owned work allocator API and assignment routing (gitea_allocate_next_work). This issue is the durable DB substrate that makes #600 atomic under concurrency.
  • #612 — multi-project Sentry/GlitchTip incident bridge with Gitea linking. The control-plane sentry_links / incident work-item kinds should integrate with that bridge; Gitea remains the durable work record.

Recommended backend

  • MVP may use SQLite if the control plane is single-process.
  • Long-term server deployment should use Postgres, especially if running on Proxmox with multiple concurrent LLM sessions.
  • Do not use the DB to bypass Gitea workflow rules.

Required architecture

  • LLM workers ask the allocator for work.
  • Allocator uses the DB to atomically reserve exactly one safe work item.
  • Gitea is updated with durable comments/labels/handoffs after assignment or state changes.
  • Sentry issues/events may be linked into the DB and mirrored to Gitea issues.
  • No LLM self-selects work outside allocator assignment.

Authority model

System Owns
Control-plane DB Sessions, atomic assignment, leases, heartbeats, terminal lock index, event log
Gitea Durable issue/PR history, comments, reviews, labels, merge/close
Sentry/GlitchTip Incidents/events (observed; linked via bridge — see #612)

Rule of thumb: DB coordinates, Gitea records, Sentry observes.

Required tables/entities

1. sessions

  • session_id
  • role/profile
  • namespace
  • pid
  • started_at
  • last_heartbeat_at
  • status

2. work_items

  • repo/org
  • kind: issue/pr/sentry_incident
  • number
  • state
  • priority
  • current_head_sha
  • updated_at

3. leases

  • lease_id
  • work_item_id
  • session_id
  • role
  • phase
  • expires_at
  • heartbeat_at
  • status

4. assignments

  • assignment_id
  • work_item_id
  • session_id
  • allowed_action
  • expected_head_sha
  • status

5. terminal_locks

  • repo/org
  • terminal_pr
  • review_id
  • decision
  • status
  • cleanup_state

6. events

  • event_id
  • work_item_id
  • type
  • message
  • created_at

7. sentry_links

  • sentry_issue_id
  • sentry_url
  • gitea_issue_number
  • fingerprint
  • status

Required allocator behavior

  • gitea_allocate_next_work(apply=true) must atomically assign one safe item or return WAIT.
  • Two concurrent sessions cannot receive the same issue/PR.
  • Current-head REQUEST_CHANGES routes to author, not reviewer/merger.
  • Stale approval routes to reviewer.
  • Contested/contaminated approval routes to controller/reconciler diagnosis.
  • Active foreign leases return WAIT or owner-resume.
  • Terminal-review lock routes to terminal-path resolution before downstream review work.
  • Merged/closed PRs are never assigned.
  • Locked issues are never assigned to another worker unless lease cleanup/adoption is sanctioned.

Non-goals

  • Do not replace Gitea as the durable work/history store.
  • Do not let the DB authorize merges, approvals, or label transitions that Gitea gates forbid.
  • Do not allow LLM workers to self-select work outside the allocator.
  • Do not treat Gitea comments alone as the concurrency lock (they mirror state; DB is authoritative for assignment).

Acceptance criteria

  1. DB-backed allocator prevents two workers from receiving the same assignment.
  2. Assignment creation and lease creation happen in one transaction.
  3. Every assignment includes role, issue/PR number, expected head SHA, lease ID, expiry, allowed actions, and forbidden actions.
  4. Worker mutations require a valid assignment/lease.
  5. Gitea comments/labels mirror assignment state but are not the only lock source.
  6. Heartbeats update the DB and optionally summarize to Gitea without comment spam.
  7. Expired/abandoned leases can be reconciled through sanctioned tooling.
  8. Tests simulate four concurrent LLMs asking for work and prove they receive different assignments or WAIT.
  9. Tests cover active lease, stale lease, terminal lock, stale approval, contested approval, current-head REQUEST_CHANGES, and merge-ready approval.
  10. Documentation states: DB coordinates, Gitea records, Sentry observes.
## Problem The current Gitea/MCP workflow allows multiple LLMs to independently inspect the queue and choose the same issue or PR. Existing leases and guards often catch unsafe actions after work has already started, but they do not prevent duplicate work up front. This causes reviewer collisions, stale lease confusion, duplicate comments, contested approvals, and wasted validation. ## Goal Add a small MCP Control Plane database that provides transactional coordination for sessions, assignments, leases, heartbeats, terminal locks, and Sentry-linked incidents. Gitea remains the durable source of truth for issue/PR history, comments, reviews, and labels. ## Related work (do not treat as duplicates) * **#600** — controller-owned work allocator API and assignment routing (`gitea_allocate_next_work`). This issue is the **durable DB substrate** that makes #600 atomic under concurrency. * **#612** — multi-project Sentry/GlitchTip incident bridge with Gitea linking. The control-plane `sentry_links` / incident work-item kinds should integrate with that bridge; Gitea remains the durable work record. ## Recommended backend * MVP may use SQLite if the control plane is single-process. * Long-term server deployment should use Postgres, especially if running on Proxmox with multiple concurrent LLM sessions. * Do not use the DB to bypass Gitea workflow rules. ## Required architecture * LLM workers ask the allocator for work. * Allocator uses the DB to atomically reserve exactly one safe work item. * Gitea is updated with durable comments/labels/handoffs after assignment or state changes. * Sentry issues/events may be linked into the DB and mirrored to Gitea issues. * No LLM self-selects work outside allocator assignment. ### Authority model | System | Owns | |--------|------| | **Control-plane DB** | Sessions, atomic assignment, leases, heartbeats, terminal lock index, event log | | **Gitea** | Durable issue/PR history, comments, reviews, labels, merge/close | | **Sentry/GlitchTip** | Incidents/events (observed; linked via bridge — see #612) | **Rule of thumb:** DB coordinates, Gitea records, Sentry observes. ## Required tables/entities ### 1. sessions * session_id * role/profile * namespace * pid * started_at * last_heartbeat_at * status ### 2. work_items * repo/org * kind: issue/pr/sentry_incident * number * state * priority * current_head_sha * updated_at ### 3. leases * lease_id * work_item_id * session_id * role * phase * expires_at * heartbeat_at * status ### 4. assignments * assignment_id * work_item_id * session_id * allowed_action * expected_head_sha * status ### 5. terminal_locks * repo/org * terminal_pr * review_id * decision * status * cleanup_state ### 6. events * event_id * work_item_id * type * message * created_at ### 7. sentry_links * sentry_issue_id * sentry_url * gitea_issue_number * fingerprint * status ## Required allocator behavior * `gitea_allocate_next_work(apply=true)` must atomically assign one safe item or return WAIT. * Two concurrent sessions cannot receive the same issue/PR. * Current-head REQUEST_CHANGES routes to author, not reviewer/merger. * Stale approval routes to reviewer. * Contested/contaminated approval routes to controller/reconciler diagnosis. * Active foreign leases return WAIT or owner-resume. * Terminal-review lock routes to terminal-path resolution before downstream review work. * Merged/closed PRs are never assigned. * Locked issues are never assigned to another worker unless lease cleanup/adoption is sanctioned. ## Non-goals * Do not replace Gitea as the durable work/history store. * Do not let the DB authorize merges, approvals, or label transitions that Gitea gates forbid. * Do not allow LLM workers to self-select work outside the allocator. * Do not treat Gitea comments alone as the concurrency lock (they mirror state; DB is authoritative for assignment). ## Acceptance criteria 1. DB-backed allocator prevents two workers from receiving the same assignment. 2. Assignment creation and lease creation happen in one transaction. 3. Every assignment includes role, issue/PR number, expected head SHA, lease ID, expiry, allowed actions, and forbidden actions. 4. Worker mutations require a valid assignment/lease. 5. Gitea comments/labels mirror assignment state but are not the only lock source. 6. Heartbeats update the DB and optionally summarize to Gitea without comment spam. 7. Expired/abandoned leases can be reconciled through sanctioned tooling. 8. Tests simulate four concurrent LLMs asking for work and prove they receive different assignments or WAIT. 9. Tests cover active lease, stale lease, terminal lock, stale approval, contested approval, current-head REQUEST_CHANGES, and merge-ready approval. 10. Documentation states: DB coordinates, Gitea records, Sentry observes.
Owner

Canonical Issue State

STATE: open; architecture-aligned after ADR land; ready for substrate implementation
WHO_IS_NEXT: author
NEXT_ACTION: Implement #613 control-plane DB per ADR before #600/#612 claim completion
NEXT_PROMPT:

Implement #613 on prgs Scaled-Tech-Consulting/Gitea-Tools as author.
Canonical ADR: docs/architecture/mcp-allocator-control-plane-observability-adr.md (merged PR #614, merge 08c3488d7c35ba27efcd5119514aeeac37b657f7).
#613 is first: DB substrate for atomic assign+lease. DB primary for leases; Gitea comments/files mirror. Assignable work = Gitea issues/PRs only (no raw Sentry/GlitchTip). SQLite single-writer MVP only; prefer Postgres or single allocator daemon for multi-session. Use provider-neutral incident_links (not a second Sentry-only store). Do not implement #600 on file locks alone.

WHAT_HAPPENED: PR #614 merged ADR. This durable comment records #613 as substrate / first dependency and restates ADR hard rules on concurrency, leases, topology, and assignable work.
WHY: Without a single post-merge architecture comment, #613 body still mixes older sentry_links / sentry_incident language that the ADR supersedes for implementation.
RELATED_PRS: #614 (merged, merge_commit_sha=08c3488d7c35ba27efcd5119514aeeac37b657f7); ADR path docs/architecture/mcp-allocator-control-plane-observability-adr.md; dependency chain #613#600#612
BLOCKERS: none for starting #613 itself; #600 and #612 are blocked on this substrate for production multi-session claims
VALIDATION: gitea_view_pr #614 state=closed merged_at set merge_commit_sha=08c3488d7c35ba27efcd5119514aeeac37b657f7; git fetch prgs master; prgs/master == 08c3488… and contains merge commit (merge-base --is-ancestor exit 0); ADR path present on master tip
LAST_UPDATED_BY: sysadmin (prgs-reconciler)

Global architecture statement

DB coordinates, Gitea records, Sentry/GlitchTip observe; the bridge is the only path that turns observations into Gitea work.

Role of #613 (substrate / first dependency)

#613 control-plane DB  →  #600 allocator API  →  #612 incident bridge

Hard decisions (must conform)

  1. DB coordinates live concurrency — sessions, atomic assignment+lease, heartbeats, terminal-lock index, events, incident_links.
  2. Assignable work is Gitea issues/PRs only — never raw Sentry/GlitchTip incidents.
  3. DB primary for leases; Gitea comment leases / local lock files are mirrors.
  4. SQLite = single-writer MVP only; multi-session prefers Postgres or single allocator daemon.
  5. One provider-neutral incident_links model (not duplicate Sentry-only stores).
  6. Durable work state conflict: Gitea wins; DB reconciles.

Body schema sketches remain useful; ADR is authoritative for topology, lease migration, and dependency order.

## Canonical Issue State STATE: open; architecture-aligned after ADR land; ready for substrate implementation WHO_IS_NEXT: author NEXT_ACTION: Implement #613 control-plane DB per ADR before #600/#612 claim completion NEXT_PROMPT: ```text Implement #613 on prgs Scaled-Tech-Consulting/Gitea-Tools as author. Canonical ADR: docs/architecture/mcp-allocator-control-plane-observability-adr.md (merged PR #614, merge 08c3488d7c35ba27efcd5119514aeeac37b657f7). #613 is first: DB substrate for atomic assign+lease. DB primary for leases; Gitea comments/files mirror. Assignable work = Gitea issues/PRs only (no raw Sentry/GlitchTip). SQLite single-writer MVP only; prefer Postgres or single allocator daemon for multi-session. Use provider-neutral incident_links (not a second Sentry-only store). Do not implement #600 on file locks alone. ``` WHAT_HAPPENED: PR #614 merged ADR. This durable comment records #613 as substrate / first dependency and restates ADR hard rules on concurrency, leases, topology, and assignable work. WHY: Without a single post-merge architecture comment, #613 body still mixes older sentry_links / sentry_incident language that the ADR supersedes for implementation. RELATED_PRS: #614 (merged, merge_commit_sha=08c3488d7c35ba27efcd5119514aeeac37b657f7); ADR path docs/architecture/mcp-allocator-control-plane-observability-adr.md; dependency chain #613 → #600 → #612 BLOCKERS: none for starting #613 itself; #600 and #612 are blocked on this substrate for production multi-session claims VALIDATION: gitea_view_pr #614 state=closed merged_at set merge_commit_sha=08c3488d7c35ba27efcd5119514aeeac37b657f7; git fetch prgs master; prgs/master == 08c3488… and contains merge commit (merge-base --is-ancestor exit 0); ADR path present on master tip LAST_UPDATED_BY: sysadmin (prgs-reconciler) ### Global architecture statement > **DB coordinates, Gitea records, Sentry/GlitchTip observe; the bridge is the only path that turns observations into Gitea work.** ### Role of #613 (substrate / first dependency) ```text #613 control-plane DB → #600 allocator API → #612 incident bridge ``` ### Hard decisions (must conform) 1. **DB coordinates live concurrency** — sessions, atomic assignment+lease, heartbeats, terminal-lock index, events, `incident_links`. 2. **Assignable work is Gitea issues/PRs only** — never raw Sentry/GlitchTip incidents. 3. **DB primary for leases**; Gitea comment leases / local lock files are **mirrors**. 4. **SQLite = single-writer MVP only**; multi-session prefers **Postgres** or **single allocator daemon**. 5. **One provider-neutral `incident_links` model** (not duplicate Sentry-only stores). 6. Durable work state conflict: **Gitea wins**; DB reconciles. Body schema sketches remain useful; **ADR is authoritative** for topology, lease migration, and dependency order.
jcwalker3 added status:in-progress and removed status:ready labels 2026-07-09 21:26:59 -05:00
Author
Owner

Issue claim heartbeat

<!-- gitea-issue-claim-heartbeat:v1 --> **Issue claim heartbeat** - kind: claim - issue: #613 - branch: feat/issue-613-allocator-db-substrate - phase: claimed - profile: prgs-author - pr: none - blocker: none - next_action: create worktree and begin implementation
jcwalker3 added status:pr-open and removed status:in-progress labels 2026-07-09 21:28:07 -05:00
jcwalker3 removed the status:pr-open label 2026-07-10 09:40:29 -05:00
Sign in to join this conversation.
2 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: Scaled-Tech-Consulting/Gitea-Tools#613