Repository navigation
Define the background child run lifecycle contract - #2421
Draft
Aaronontheweb wants to merge 22 commits into
Draft
Aaronontheweb wants to merge 22 commits into
Aaronontheweb wants to merge 22 commits into
Conversation
Aaronontheweb
added this pull request to stack #2423
October 9, 2026 01:13
Aaronontheweb
force-pushed
the
feature/background-subagents
branch
8 times, most recently
from
October 9, 2026 23:00
869d19d to
66b1f14
Compare
| from pathlib import Path, PurePosixPath | ||
| import re | ||
| import subprocess | ||
| import threading |
| var notice = Assert.IsType<CompactionOutput>(await subscriber.FishForMessageAsync<object>( | ||
| message => message is CompactionOutput, Ceiling, cancellationToken: TestContext.Current.CancellationToken)); | ||
| Assert.True(notice.Summarized); | ||
| Assert.True(notice.MessagesAfter < notice.MessagesBefore); |
| "The fixture acknowledged a foreign trial."); | ||
| var binding = snapshot.GetProperty("binding"); | ||
| Require(binding.GetProperty("accepted").GetProperty("run_id").GetString() == Acceptance!.RunId | ||
| && binding.GetProperty("accepted").GetProperty("scope_id").GetString() == Acceptance.ScopeId |
| _armed = false; | ||
| captured = Held.Task.IsCompletedSuccessfully ? Held.Task.Result : null; | ||
| } | ||
| captured?.Receiver.Tell(captured.Envelope.Message, captured.Envelope.Sender); |
| await subscriber.FishForMessageAsync<object>(message => message is TurnCompleted, Ceiling, | ||
| cancellationToken: TestContext.Current.CancellationToken); | ||
| Assert.Contains(_main.ReceivedMessages[^1].SelectMany(message => message.Contents.OfType<FunctionResultContent>()), | ||
| result => result.Result?.ToString()?.Contains(findingText, StringComparison.Ordinal) == true); |
| Assert.Equal("Accepted", body.RootElement.GetProperty("state").GetString()); | ||
| Assert.Equal(accepted.Run.RunId.Value, body.RootElement.GetProperty("run_id").GetString()); | ||
| Assert.Equal("check_agent_run", body.RootElement.GetProperty("control_tool").GetString()); | ||
| Assert.DoesNotContain(_main.ReceivedMessages[^1], message => message.Text?.Contains("child final", StringComparison.Ordinal) == true); |
Aaronontheweb
force-pushed
the
feature/background-subagents
branch
from
October 9, 2026 23:28
66b1f14 to
2da97ac
Compare
| var callbackEntered = NewSignal(); | ||
| var callbackExited = NewSignal(); | ||
| using var callbackRelease = new ManualResetEventSlim(); | ||
| var provider = new UncooperativeChildClient(_child) |
| evidenceSource = "persistent DaemonClient output; not the headless CLI envelope" | ||
| })); | ||
| else | ||
| Console.Write(transcript.ToString()); |
Aaronontheweb
force-pushed
the
feature/background-subagents
branch
from
October 10, 2026 01:52
4db39fe to
832ebff
Compare
Comment on lines
+162
to
+168
| foreach (var id in new[] { "window-probe-1", "window-probe-2" }) | ||
| { | ||
| var actual = Assert.Single(after.OfType<ToolCallRecorded>(), | ||
| evt => evt.ToolResult.ToolCallId == new ToolCallId(id)); | ||
| Assert.False(actual.LoopObservation!.Synthetic); | ||
| Assert.True(Array.IndexOf(after, childAdoption) < Array.IndexOf(after, actual)); | ||
| } |
Comment on lines
+147
to
+155
| foreach (var id in new[] { "report-after-3", "report-after-4" }) | ||
| { | ||
| var read = Assert.Single(events.OfType<ToolCallRecorded>(), | ||
| item => item.ToolResult.ToolCallId == new ToolCallId(id)); | ||
| Assert.True(Position(adoption) < Position(read)); | ||
| Assert.False(read.LoopObservation!.Synthetic); | ||
| Assert.Equal((int)ToolInvocationOutcomeCategory.Success, read.LoopObservation.Category); | ||
| Assert.Equal(report, read.ToolResult.Content); | ||
| } |
Aaronontheweb
force-pushed
the
feature/background-subagents
branch
2 times, most recently
from
October 11, 2026 16:14
7ffc60f to
8d69f2b
Compare
…ill API dev renamed ToolOutputSpillLocation.TryEnsureSessionDirectory to EnsureSessionWorkspaceDirectory, which now returns an error string instead of a bool. Update the cancelled-child report writer to match.
…rminal The child's PostStop death path emitted SubAgentResult (the inline-path message), but a background child's owner only handles BackgroundChildTerminal. Route the ActorStopped result through BackgroundChildTerminal with the run and scope identities when the child runs in the background, so the owner records the terminal instead of dead-lettering it.
Aaronontheweb
force-pushed
the
feature/background-subagents
branch
from
October 12, 2026 02:20
8d5aa38 to
2494a01
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This is the second PR in the stack. Base: #2420.
Child tool calls return durable acceptance receipts. The parent can continue useful work while each child runs. Deferred tools expose recorded status and cancellation. Canonical terminal delivery retains the original requester and task authority.
The first durable consumption of a canonical child result starts a new recurrence window. This permits a report read after earlier reads found no file. Duplicate delivery, status queries, admission, and recovery cannot reset the window. Task identity and receipt failures remain intact. Legacy adoption events retain their prior replay behavior.
Cancellation closes local dispatch before final framework work. A five-second grace period lets the framework write partial evidence. External effects remain unknown. Recovery records child loss without a relaunch.
Why this is safe: The owner commits lifecycle facts before effects. Canonical input and authority checks govern child-result adoption. Durable input identifiers prevent duplicate resets after consumption or compaction. Retained pair checks reject attributed corruption. Transcript text alone cannot authorize a new window.
Validation:
0da400bbpasses one real-model held-child trial and one nonadjacent-cycle recovery trial, with independent review.f6685d5fnow passes both the canonical verifier and a separate read-window predicate. Independent review checks actual provider history, artifact bytes, and continued guard protection.f48e9fberemain false. The coding trial exceeds its observer window and violates exact command limits. The plan trial completes both children and delivers the plan, but alters its source identity and table format.PR2 head:
255c8334. All 29 hosted checks pass for this exact head. The former combined headf6685d5falso passes all 29 checks, including 29 native tapes and 10 scenarios on each native platform. These results do not establish acceptance for a later combined head.The PR remains a draft. Model acceptance and final verification remain open. Merge and rollout require separate decisions.