fix(executors): truthful executor rows, startup reconciliation, resume-safe cleanup (#1597) (#1608)

Co-authored-by: Carlos Florencio <carlosmflorencio@gmail.com>
This commit is contained in:
point-source
2026-07-08 15:37:02 -07:00
committed by GitHub
parent bd22a1318c
commit e1cf99f821
37 changed files with 1855 additions and 99 deletions
@@ -1,9 +1,46 @@
# 0025: Runtime Cleanup Uses `executors_running`
**Status:** accepted
**Status:** accepted (amended 2026-07-06 — see "Update")
**Date:** 2026-06-22
**Area:** backend
## Update (2026-07-06, #1597 executor-row-desync)
This decision **stays**: `executors_running` remains the authoritative durable
runtime inventory. It was made *trustworthy* rather than reverted. Three
clarifications now hold:
- **Events are the primary producer; startup reconciliation heals what events
cannot.** Every lifecycle transition writes the row (launch, boot-ready,
turn-complete, cancel, process-exit/crash, stop), populating a host-local
liveness handle (`executors_running.local_pid`) for local/standalone rows. A
startup pass repairs rows whose process is confirmed dead and prunes only
terminal ones — a backend restart is exactly the moment events could not
have fired. A periodic in-run polling pass was prototyped and deliberately
not merged: it defended against failure modes (OOM kill, dropped event)
that have not been observed, and polling never updates a row the moment an
event could.
- **`local_pid` is a NEW column, deliberately not the existing `pid`.**
`executors_running.pid` holds the agentctl PID *on the remote host* for SSH
rows; overloading it with a host-local pid would silently change that
column's meaning and invite local process checks against remote rows. The
two handles stay separate, and liveness is runtime-aware
(`lifecycle.RowProcessLiveness`): a host-local process check never runs
against a remote/SSH or containerized row — those report Unknown, never
Dead.
- **One ironclad deletion invariant governs every reconciliation cleanup
path.** A row backing a resumable session, or holding a `resume_token`, is
**repaired in place** (status=stopped, `local_pid` cleared,
`resume_token`/worktree preserved) — never deleted; only a
finished/never-started row with no `resume_token` may be pruned.
Reconciliation repairs rather than deletes-and-relaunches because
`resume_token` is single-sourced in this table — an erroneous delete costs
the operator the only handle to a resumable conversation. Duplicating the
token into a second table was rejected: two writers of the same fact is the
divergence pattern this decision exists to eliminate. The rule is
`models.RowMustBePreserved`; see #1597 for the measured evidence and
expected behavior this implements.
## Context
Archiving and deleting tasks can remove task/worktree records while ACP agent
+1 -1
View File
@@ -30,7 +30,7 @@ Read individual ADRs for full context. Create new ones via `/record decision` or
| 0022 | [Embedded Vite assets](0022-embedded-vite-assets.md) | accepted | backend, frontend, cli | 2026-06-17 |
| 0023 | [Active workspace cookie for boot state](0023-active-workspace-cookie.md) | accepted | backend, frontend | 2026-06-18 |
| 0024 | [Go-fronted Vite dev mode](0024-go-fronted-vite-dev-mode.md) | accepted | backend, frontend, cli | 2026-06-18 |
| 0025 | [Runtime cleanup uses `executors_running`](0025-runtime-cleanup-uses-executors-running.md) | accepted | backend | 2026-06-22 |
| 0025 | [Runtime cleanup uses `executors_running`](0025-runtime-cleanup-uses-executors-running.md) | accepted (amended 2026-07-06) | backend | 2026-06-22 |
| 0026 | [Tauri desktop shell over native runtime](0026-tauri-desktop-shell.md) | accepted | frontend, backend, cli, infra | 2026-06-23 |
| 0027 | [Replayable schema migrations across SQLite and Postgres](0027-replayable-schema-migrations.md) | accepted | backend | 2026-06-24 |
| 0028 | [Backend-owned task-create last-used preferences](0028-task-create-last-used-source-of-truth.md) | accepted | backend, frontend | 2026-06-29 |