Adds the session_checkpoints section to the control-plane DB substrate
architecture doc: schema version and row identity, the column groups, the
public API surface, and the hard rules (redaction at write, reconcile
rather than blind restore, unknown live state is not a mismatch, drain
fails closed on an incomplete checkpoint, no transcript storage).
Satisfies acceptance criterion 1 of #660 ("Schema documented and
versioned") for the implementation added in 7e18dcc.
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
Base-sync of the #660 durable session checkpoint schema branch onto current
master. The only conflicting file was control_plane_db.py, where master added
the #651 usage_events table, migration, and indexes while this branch added the
Resolution keeps both sides in full. Verified against both merge parents: the
resolved file contains every line of master's control_plane_db.py with zero
removals, plus exactly the #660 additions (session_checkpoints table, its two
indexes, _session_checkpoint_row, write_session_checkpoint,
get_session_checkpoint, list_session_checkpoints, reconcile_session_checkpoint,
and checkpoint_completeness).
Conflicts:
control_plane_db.py
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
Add pure post_restart_reconcile.reconcile_after_restart classifier with a
machine-readable completion proof covering service health, sessions, leases,
capabilities, worktrees, interrupted mutations (never auto-resumed),
duplicates, and queue state. Soft-depends on #660 checkpoints (skipped with
reason when the schema module is absent).
Wire read-only MCP tool gitea_reconcile_after_restart, boot-once hook via
gitea_assess_master_parity, log_only/enforce modes (mutation_hold), and docs.
Closes#662
Related: #655#652#653#660#661
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
Add a versioned, redacted, reconcile-on-boot session checkpoint store to
control_plane_db so a restart can recover session identity, stage, lease
ownership, and next valid action instead of forcing human reconstruction
(umbrella #655; #628 autonomous-handoff goal).
- Bump SCHEMA_VERSION 4->5. New `session_checkpoints` table + two indexes,
added via `CREATE TABLE IF NOT EXISTS` so table creation is itself the
additive, idempotent v4->v5 migration (dependency_edges precedent).
- Writer `write_session_checkpoint` upserts the current recoverable state
per (remote, org, repo, session_id, work_kind, work_number); stage
transitions audit to `events`. Readers `get_session_checkpoint` /
`list_session_checkpoints`.
- Every free-text and JSON field is passed through `gitea_audit.redact`
before storage — no tokens/credential URLs can land in a checkpoint (AC4).
- `reconcile_session_checkpoint` is pure and never restores: it diagnoses a
stored checkpoint against live head/lease state and flags staleness (AC3).
- Drain gate: `require_complete=True` fails closed (writes nothing) when a
checkpoint is missing a drain-required field, so drain cannot complete on
an unrecoverable record.
- `lease_id`/`assignment_id` are soft references (no enforced FK) so a
checkpoint survives deletion of the lease it names; `work_number=0` is the
NULL-safe session-level sentinel.
Tests: 13 new cases (schema/version, multi-role fixtures, upsert+audit,
JSON round-trip, secret redaction, reconcile stale-head/dead-lease/
reassigned-lease/clean/unknown, drain fail-closed, sentinel key). Existing
schema_version assertion updated 4->5. Full tests/test_control_plane_db.py
suite: 33/33 pass.
Links #652#653#655.
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
resolve_canonical_repo_root fallback now uses commonpath only — no
string split and no os.path.isdir gate — so MCP project_root =
branches/<wt> still resolves to the repo root when the path is not yet
on disk (#274 / review #551 regression).
- Remove /branches/ string-split fallback in resolve_canonical_repo_root;
recover roots via commonpath ancestry only (review #531 F2).
- Refuse existing branches that do not contain live master; no weak
merge-base acceptance (F3).
- Verify caller-supplied assignment_id/lease_id against the control plane
or fail closed (F4).
- Compensating recovery releases bound workflow leases via lease_lifecycle (F5).
- Regression tests for each finding.
assess_same_issue_lease_conflict entered the reclaim branch only under is_lease_expired, so a heartbeat-lifecycle lease that is non-live but whose expires_at is still in the future (stale_absolute_cap under default policy; stale_missed_heartbeat under TTL>grace) fell through to the foreign active-lease block and never reached assess_expired_lock_reclaim, which already permits those bands. The gate now also enters reclaim/renewal when a heartbeat-lifecycle lease is not is_lease_live even with a future expires_at; legacy locks are excluded and keep their absolute expires_at clock (AC-N8). Adds tests/test_issue_794_conflict_gate_reclaim.py.
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
Address REQUEST_CHANGES on PR #795: stop claiming all 21 umbrella ACs
from a single unit-test module. Scope docs and cases to child issue #878
(CTH/format, ownership classify_skip, dependency edges). Does not close
umbrella #628.
No content conflicts. Master advanced with the #658 restart coordinator;
inventory API surfaces auto-merge cleanly.
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>