Skip to content

fix(orchestration): never settle a thread that is still live - #167

Merged
johnnyelwailer merged 1 commit into
mainfrom
fix/settle-live-guard
Sep 14, 2026
Merged

johnnyelwailer merged 1 commit into
mainfrom
fix/settle-live-guard

Conversation

@johnnyelwailer

Copy link
Copy Markdown
Owner

What

A thread is now refused by thread.settle / thread.auto-settle when it is still live. Enforced at the single engine dispatch choke point, so every settle path is covered — the client "Settle (N)" batch, the idle-days auto-settle, and the child sweepers.

A thread is refused when any of:

  1. it owns a non‑terminal workflow run (queued / running / suspended / sleeping / paused);
  2. it has a child that is live — the child is actively running or running its own workflow (one concept);
  3. a parent has a durable, unresolved t3team.child_wait on it (the deterministic "parent still needs this child" signal).

Genuinely‑inactive threads settle exactly as before.

Why

Threads that still had work in flight were being settled and vanishing from the active roster: a launch thread with a paused/sleeping workflow, a parent while a sub‑thread was still working, and a child while a parent was still waiting on its result. All three are now refused.

All signals are read from durable projection tables (workflow_runs, projection_thread_sessions + handoff activities, and child_wait activities), so they hold across restarts and need no timers or new resident state.

Tests

  • New engine test: a thread with a live workflow_runs row is refused (with the "still live" detail) while a control thread with no live relation settles normally.
  • Full affected suites green (orchestration, server‑startup, provider reaper, checkpoint, project‑setup, child‑settle‑sweeper, start‑child execution scope). The only failures are 2 pre‑existing Normalizer.attachments cases, confirmed to fail on a clean base as well.

Guard

One choke point, three clean predicates (Effect.all + a single OR) — no nested branch logic.

Refuse thread.settle / thread.auto-settle at the single dispatch choke point
when any of:
  - the thread owns a non-terminal workflow run (queued/running/suspended/
    sleeping/paused),
  - the thread has a child that is live (actively running, or running its own
    workflow — one concept), or
  - a parent has a durable, unresolved t3team.child_wait on the thread.

All three are read from the durable projection tables (workflow_runs,
projection_thread_sessions + handoff activities, child_wait activities), so
they hold across restarts and cover every settle path: the client batch-settle,
the idle-days auto-settle, and the child sweepers. Genuinely-inactive threads
still settle as before.
@github-actions github-actions Bot added vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. size:L labels Sep 5, 2026
@github-actions

github-actions Bot commented Sep 7, 2026

Copy link
Copy Markdown
Contributor

Thread transfer impact

⚠️ The latest CI run did not produce a thread transfer result for ccc9e86.

This comment will update automatically after the next completed run.

@johnnyelwailer
johnnyelwailer merged commit 7fcdfe6 into main Sep 14, 2026
8 of 16 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:L vouch:trusted PR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant