You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Stale teammate notifications keep interrupting the lead after team purge (split from #40, bug 4) #48
After team_cleanup archived the team and a second team_cleanup with purge deleted it, teammates that were already in shutdown state kept delivering notifications to the lead. The lead's turn showed [System: New team message from alpha] on repeat, and the body was never readable:
> team_results()
This session is not in a team.
team_message had zero rows afterwards, so the redelivery is not coming from the database. Likely suspects: in-flight/persisted wake deliveries (synthetic system messages) that reference a team that no longer exists, or the idle-flush backstop re-firing for a member row that was purged between scheduling and delivery. Whatever delivers the notification needs a guard: if the recipient's team no longer exists (or the member row is gone), drop the delivery instead of waking the lead.
Acceptance
After a purge, a lead session must receive zero further teammate notifications.
Any delivery path that runs after team deletion must no-op when the team/member row is missing.
First-hand reproduction just landed while dogfooding v0.20.0 — timeline and DB evidence below.
Reproduction (v0.20.0, opencode 2.0.32, single lead + one member)
Team dogfood-020 with members alpha and beta (GLM 5.3 flash teammates), both completed their tasks and reported
alpha shut down gracefully; beta's graceful shutdown stalled (idle → shutdown_requested → idle), so the lead force-shut beta down
Lead ran team_cleanup — team archived
Minutes later, the lead's session received [System: New team message from beta] — with no body readable:
> team_results()
This session is not in a team.
DB state at the time of the stale notification
team: team_mvf377ul_0000_icdz5j | dogfood-020 | status=archived
team_message (latest, all for the lead):
beta | lead | <task-result>... | delivered=1 | 10:23:06
beta | lead | <task-result>... | delivered=1 | 10:22:10
beta | lead | <task-result>... | delivered=1 | 10:17:15
alpha | lead | <task-result>... | delivered=1 | 10:17:01
Every message row is delivered=1 — the stale wake did not come from the database, exactly as the original #40 bug 4 reporter observed.
Likely mechanism (now with concrete timing)
The wake was an in-flight fire-and-forget promptAsync scheduled before cleanup (beta's final team_message/shutdown processing raced the force shutdown). The delivery path fires the lead wake unconditionally — nothing re-checks at delivery time whether the lead's team still exists. team_cleanup runs between schedule and delivery → the lead is woken into a team that no longer resolves.
The acceptance criterion already written on this issue is confirmed as the right fix: every delivery path must no-op when the recipient's team/member row is missing at delivery time, and lead-bound wakes should resolve the team fresh from the registry rather than trusting the pre-schedule state.
Note
Beta also re-reported its task result three times (10:17, 10:22, 10:23) because of the repeated shutdown requests during the graceful-shutdown stall — the stall behavior that produced the extra wakes is itself worth looking at alongside the delivery guard: a member that has gone idle after processing the shutdown message should terminalize rather than cycle back to idle.
Update: the stale wakes repeat, one per lead turn. A second [System: New team message from beta] arrived on the lead's next turn, with the DB unchanged since the first (latest row still 10:23:06, all delivered=1, team still archived).
This matches the original #40 report's 'on repeat' symptom and supports the in-flight-wake-queue theory: beta sent three lead-bound task results (10:17, 10:22, 10:23), each scheduling a fire-and-forget lead wake; after cleanup, those queued wakes drain into the lead's turns one at a time. Expect the count of stale notifications to roughly equal the number of lead-bound wakes scheduled before cleanup — and the lead can neither read nor dismiss them, because the team no longer resolves.
Reinforces the fix direction: guard delivery against team/member existence at fire time, and consider draining or invalidating pending lead-bound wakes for a team during team_cleanup's archive step.
Activity
hueyexe commentedon Oct 19, 2026
First-hand reproduction just landed while dogfooding v0.20.0 — timeline and DB evidence below.
Reproduction (v0.20.0, opencode 2.0.32, single lead + one member)
dogfood-020with membersalphaandbeta(GLM 5.3 flash teammates), both completed their tasks and reportedalphashut down gracefully;beta's graceful shutdown stalled (idle → shutdown_requested → idle), so the lead force-shut beta downteam_cleanup— team archived[System: New team message from beta]— with no body readable:DB state at the time of the stale notification
Every message row is
delivered=1— the stale wake did not come from the database, exactly as the original #40 bug 4 reporter observed.Likely mechanism (now with concrete timing)
The wake was an in-flight fire-and-forget
promptAsyncscheduled before cleanup (beta's finalteam_message/shutdown processing raced the force shutdown). The delivery path fires the lead wake unconditionally — nothing re-checks at delivery time whether the lead's team still exists.team_cleanupruns between schedule and delivery → the lead is woken into a team that no longer resolves.The acceptance criterion already written on this issue is confirmed as the right fix: every delivery path must no-op when the recipient's team/member row is missing at delivery time, and lead-bound wakes should resolve the team fresh from the registry rather than trusting the pre-schedule state.
Note
Beta also re-reported its task result three times (10:17, 10:22, 10:23) because of the repeated shutdown requests during the graceful-shutdown stall — the stall behavior that produced the extra wakes is itself worth looking at alongside the delivery guard: a member that has gone idle after processing the shutdown message should terminalize rather than cycle back to idle.
hueyexe commentedon Oct 19, 2026
Update: the stale wakes repeat, one per lead turn. A second
[System: New team message from beta]arrived on the lead's next turn, with the DB unchanged since the first (latest row still 10:23:06, alldelivered=1, team stillarchived).This matches the original #40 report's 'on repeat' symptom and supports the in-flight-wake-queue theory: beta sent three lead-bound task results (10:17, 10:22, 10:23), each scheduling a fire-and-forget lead wake; after cleanup, those queued wakes drain into the lead's turns one at a time. Expect the count of stale notifications to roughly equal the number of lead-bound wakes scheduled before cleanup — and the lead can neither read nor dismiss them, because the team no longer resolves.
Reinforces the fix direction: guard delivery against team/member existence at fire time, and consider draining or invalidating pending lead-bound wakes for a team during
team_cleanup's archive step.