Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
4cb0120
fix(delegation): preserve live child delegation links across extensio…
Sep 2, 2026
717e233
test(task-persistence): cover mutation edge cases for live child live…
Sep 3, 2026
289c822
test(task-persistence): kill surviving static-mutant arithmetic mutan…
Sep 3, 2026
55ab370
chore(gitignore): ignore local worktrees (.wt-*) and scratch files
Sep 3, 2026
0c35045
test(task-persistence): make liveness-boundary tests filesystem-preci…
Sep 3, 2026
85b4e16
fix(delegation): guard replayDelegationRepairIntent against live cros…
Sep 4, 2026
795da5e
fix(delegation): run delegation reconciliation on periodic reconcile …
Sep 5, 2026
d2e67f9
fix(delegation): use console.warn for live-child skip log
Sep 5, 2026
1f88b8b
test(delegation): route undefined child mtime through initialize() en…
Sep 5, 2026
7206d6f
test(lifecycle): model cross-window child liveness guard in lifecycle…
Sep 5, 2026
ccf801b
chore(gitignore): drop local worktree and scratch ignore patterns
Sep 5, 2026
df7b644
test(delegation): kill 15 surviving changed-code mutants
Sep 5, 2026
40e06ca
test(delegation): replace fixed-count timer pump with condition polling
Sep 5, 2026
d4c619b
fix(delegation): harden cross-window liveness guards per CodeRabbit r…
Sep 8, 2026
cdfc136
fix(delegation): roll back ownership claim on non-started resume path…
Sep 8, 2026
2375b10
test(delegation): add markLocallyInactive to wholesale TaskHistorySto…
Sep 8, 2026
5708994
test(delegation): kill 19 changed-code mutants in claim/rollback and …
Sep 8, 2026
05278f0
fix(delegation): close ownership race in reconciliation; CodeRabbit r…
Sep 8, 2026
1794520
test(delegation): strengthen resolved-task assertion in hookless crea…
Sep 8, 2026
8c0ff22
refactor(webview): eliminate all 12 no-explicit-any violations from C…
Sep 8, 2026
e9a1820
refactor(task): eliminate all 17 no-explicit-any violations from Task.ts
Sep 8, 2026
3f10771
test(task): kill 43 changed-code mutants surfaced by Task.ts type-cle…
Sep 8, 2026
0ce3e21
test(task): pin reasoning-block id key-absence with explicit property…
Sep 8, 2026
b9896d2
fix(delegation): release local ownership when continuation scheduling…
Sep 20, 2026
0190a54
test(delegation): cover continuation scheduling rejection and cancell…
Sep 20, 2026
ed792d3
test(task): pass threaded request model snapshot in buildCleanConvers…
Sep 20, 2026
4fc47fc
docs(lifecycle): restrict invariant 8 to boolean model behavior
Sep 20, 2026
6d05781
fix(task-persistence): drop evicted task ids from local ownership on …
Sep 22, 2026
09f7f46
fix(task-persistence): keep live delegated children during reconcile
Sep 23, 2026
5534add
Merge remote-tracking branch 'refs/remotes/upstream/main' into fix/re…
myk1yt Sep 27, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 15 additions & 11 deletions docs/architecture/task-lifecycle-model.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,17 +41,20 @@ TLA+/PlusCal or Quint with TLC becomes a better fit when the lifecycle needs tem

## Production mapping

| Model concept | Production concept |
| ------------------------- | ------------------------------------------------------------------------------------ |
| Task record and status | `HistoryItem` persisted by `TaskHistoryStore` |
| `delegate(parent, child)` | `ClineProvider.delegateParentAndOpenChild` |
| `interrupt(child)` | cancellation or eviction through `markDelegatedChildInterrupted` |
| `complete(child)` | `ClineProvider.reopenParentFromDelegation` |
| `abandon(child)` | `ClineProvider.abandonSubtask` |
| Atomic event step | `atomicReadAndUpdate`, `atomicUpdatePair`, and per-parent delegation transition lock |
| Event interleaving | Competing completion, cancellation, abandonment, and new delegation calls |

The model has three fixed task slots, enough to cover competing siblings and a nested parent-child-grandchild chain. It explores every reachable interleaving through depth 12, deduplicating canonical states. Representative checks also exercise rejected operations that do not create a new state: a second concurrent delegation while the first child is active, stale completion after re-delegation, late completion after abandonment, completion after interruption, and nested completion. Named semantic landmarks require the graph to retain interrupted-child re-delegation and nested delegation even when the raw state total changes.
| Model concept | Production concept |
| --------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------ |
| Task record and status | `HistoryItem` persisted by `TaskHistoryStore` |
| `delegate(parent, child)` | `ClineProvider.delegateParentAndOpenChild` |
| `interrupt(child)` | cancellation or eviction through `markDelegatedChildInterrupted` |
| `complete(child)` | `ClineProvider.reopenParentFromDelegation` |
| `abandon(child)` | `ClineProvider.abandonSubtask` |
| `reconcileStartup(parent)` | startup/periodic `TaskHistoryStore.reconcileDelegationStateCore` orphan repair |
| `markLiveElsewhere(child)` / `expireLiveElsewhere(child)` | child history-file mtime recent vs stale past `LIVE_CHILD_MTIME_THRESHOLD_MS` (abstracted; no wall clock in model) |
| `heartbeat(child)` / `expireHeartbeat(child)` | persisted `lastActivityAt` liveness heartbeat fresh vs stale past `LIVE_CHILD_MTIME_THRESHOLD_MS` (production: the owning session's throttled heartbeat during a long streaming turn, shared predicate `isDelegatedChildLive`) |
| Atomic event step | `atomicReadAndUpdate`, `atomicUpdatePair`, and per-parent delegation transition lock |
| Event interleaving | Competing completion, cancellation, abandonment, and new delegation calls |

The model has three fixed task slots, enough to cover competing siblings and a nested parent-child-grandchild chain, plus two abstract booleans per slot recording whether an active child's session shows life: one for ownership by another window (recent history-file mtime) and one for a fresh persisted `lastActivityAt` heartbeat from the owning session. It explores every reachable interleaving through depth 12, deduplicating canonical states. Representative checks also exercise rejected operations that do not create a new state: a second concurrent delegation while the first child is active, stale completion after re-delegation, late completion after abandonment, completion after interruption, and nested completion. Named semantic landmarks require the graph to retain interrupted-child re-delegation and nested delegation even when the raw state total changes, a delegated parent whose active child is live in another window surviving startup reconciliation unchanged, a delegated parent whose active child heartbeats through a long streaming turn (stale mtime, fresh `lastActivityAt`) likewise surviving unchanged, and a stale-mtime, stale-heartbeat (crash-orphan) active child being repaired to `interrupted` with the parent returned to `active` only through `reconcileStartup`.

Production completion also accepts a recovery-compatible `active` parent that still awaits the returning child, then clears the stale pointers. Normal model transitions never create that intermediate state, so it is covered by a focused reducer test rather than admitted as a generally valid reachable state.

Expand Down Expand Up @@ -134,6 +137,7 @@ The task delegation checker currently enforces:
5. Parent-child lineage is acyclic.
6. Completed task records cannot be changed by later lifecycle events.
7. Active-child re-delegation, stale completion after ownership moves to another child, duplicate/late completion, and abandonment of a live child are rejected by the shared production guards.
8. No transition may clear a delegated parent's link to a child that is active and live, where live means either marked live-elsewhere (`ModelState.liveElsewhere` true, recent history-file mtime) or heartbeat-alive (`ModelState.heartbeatAlive` true, fresh persisted `lastActivityAt`); reconciliation repairs the link only when both flags are false. This encodes the PR #1495 cross-window misrepair bug class — which broke delegation links so subtask completion could not return to the parent — extended by the liveness-heartbeat fix so a child streaming a long turn (minutes without any other history-file write) is likewise preserved.

The completion persistence checker additionally enforces:

Expand Down
1 change: 1 addition & 0 deletions packages/types/src/history.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ export const historyItemSchema = z.object({
awaitingChildId: z.string().optional(), // Child currently awaited (set when delegated)
completedByChildId: z.string().optional(), // Child that completed and resumed this parent
completionResultSummary: z.string().optional(), // Summary from completed child
lastActivityAt: z.number().optional(), // Liveness heartbeat: last time the owning session persisted activity
pendingAction: pendingTaskActionSchema.optional(),
})

Expand Down
Loading
Loading