Bug
When a worker's first PR merges, the lifecycle completion reaction terminates the session (sessionComplete: ≥1 merged, none open → MarkTerminated). But because merge-termination is flag-only (see #2811), the agent process is still alive and still working. Any PR it opens after that instant is invisible: the SCM observer does not observe terminated sessions, so the new PR is never attributed to the session, never enriched (no sidebar entry, no checks/review tracking, no nudges), and no lifecycle reaction will ever fire for it.
Source: maintainer local report (desktop app, macOS) | Date: 2026-07-21 | Analyzed against: dec77c61b | Daemon: app-owned, :3001
Confidence: High — timeline reconstructed from change_log + GitHub timestamps
Priority: high — silent loss of PR tracking for any multi-PR worker whose first PR merges quickly (fast-merge workflows make this common)
Live repro (session agent-orchestrator-34, codex worker)
| Time (UTC, 2026-07-21) |
Event |
| 07:09:48 |
Agent opens PR #2876 (branch ao/agent-orchestrator-34/landing-docs-trailing-slash) |
| 07:10:12 |
Observer attributes it — pr_created in change_log ✓ (enrichment works, including on a non-root branch) |
| 07:11:47 |
#2876 merges |
| 07:11:49 |
Session terminated by the merged-PR completion reaction (change_log: isTerminated: true) |
| 08:11:51 |
Agent — still alive in its tmux session, an hour into further work — opens PR #2877 (branch ao/agent-orchestrator-34/landing-orchestrator-cta) |
| now |
pr table has no row for #2877; sidebar shows nothing; PR is fully untracked |
Root Cause
Two interacting defects:
- Premature completion inference.
sessionComplete (backend/internal/lifecycle/reactions.go:277-292) infers "the session is done" purely from PR state (≥1 merged ∧ 0 open). That inference races the agent: an agent mid-task with one merged PR and the next branch not yet pushed is not done, but the reaction terminates the session anyway (reactions.go:152-157 → MarkTerminated).
- Observation cutoff on termination. The SCM observer skips terminated sessions, so any PR created after the termination instant is never discovered. Combined with #2811's flag-only termination (agent left running), the system actively manufactures this state: it terminates the bookkeeping while leaving the worker alive to keep producing untracked work.
Fix
Suggested directions:
- Gate completion on agent state, not just PR state: only auto-terminate when the session is also quiescent (activity exited/idle for a grace window) — an agent that is
active with one merged PR should stay alive and observed.
- Grace period / late-PR sweep: after merge-triggered termination, keep observing the session's branch namespace (
ao/<session-id>/*) for N minutes; if a new PR appears, revive attribution (or un-terminate the session).
- Attribution fallback: on any observer scan, attribute an unowned open PR whose head branch matches
ao/<session-id>/* to that session even if terminated, and surface it (at minimum ao session claim-pr should work on terminated sessions so a human can recover).
Impact
- Agent work product silently vanishes from AO's tracking: no sidebar presence, no CI/review routing, no nudges — humans discover the PR only by luck on GitHub.
- Compounds #2811: the leaked-alive agent keeps working and its output is systematically dropped.
Related
- #2811: flag-only merge termination (enables the race); fix PRs in flight #2819/#2840/#2853 — whichever lands should consider this issue's completion-gating too
- #2390: terminated-session status derivation with open PRs (adjacent status bug)
- #1821 (closed): multi-PR support — this is a lifecycle-era regression of the same spirit
- #2834: merged sessions disappearing from sidebar
Bug
When a worker's first PR merges, the lifecycle completion reaction terminates the session (
sessionComplete: ≥1 merged, none open →MarkTerminated). But because merge-termination is flag-only (see #2811), the agent process is still alive and still working. Any PR it opens after that instant is invisible: the SCM observer does not observe terminated sessions, so the new PR is never attributed to the session, never enriched (no sidebar entry, no checks/review tracking, no nudges), and no lifecycle reaction will ever fire for it.Source: maintainer local report (desktop app, macOS) | Date: 2026-07-21 | Analyzed against:
dec77c61b| Daemon: app-owned, :3001Confidence: High — timeline reconstructed from
change_log+ GitHub timestampsPriority: high — silent loss of PR tracking for any multi-PR worker whose first PR merges quickly (fast-merge workflows make this common)
Live repro (session
agent-orchestrator-34, codex worker)ao/agent-orchestrator-34/landing-docs-trailing-slash)pr_createdinchange_log✓ (enrichment works, including on a non-root branch)change_log:isTerminated: true)ao/agent-orchestrator-34/landing-orchestrator-cta)prtable has no row for #2877; sidebar shows nothing; PR is fully untrackedRoot Cause
Two interacting defects:
sessionComplete(backend/internal/lifecycle/reactions.go:277-292) infers "the session is done" purely from PR state (≥1 merged ∧ 0 open). That inference races the agent: an agent mid-task with one merged PR and the next branch not yet pushed is not done, but the reaction terminates the session anyway (reactions.go:152-157→MarkTerminated).Fix
Suggested directions:
activewith one merged PR should stay alive and observed.ao/<session-id>/*) for N minutes; if a new PR appears, revive attribution (or un-terminate the session).ao/<session-id>/*to that session even if terminated, and surface it (at minimumao session claim-prshould work on terminated sessions so a human can recover).Impact
Related