Skip to content

Guard native exits against uncollected child work - #148

Merged
Git-on-my-level merged 27 commits into
mainfrom
feat/child-work-exit-guard
Nov 11, 2026
Merged

Git-on-my-level merged 27 commits into
mainfrom
feat/child-work-exit-guard

Conversation

@Git-on-my-level

@Git-on-my-level Git-on-my-level commented Nov 11, 2026 •

Copy link
Copy Markdown
Owner

Native parents can exit with child work that still needs collection. This adds bounded exit evidence and exact collect commands, one durably claimed automatic continuation through a supported frozen native recipe, and actionable inbox entries for routes that cannot resume. Native CLIs and Multica retain execution authority. The original parent stays terminal; collect an automatic successor through child_exit_guard.continuation_id.

Integrated main fd950e73600611165ada674fba35754877c69903 normally, including #147's final strict evidence/membership guards and #150's OpenCode recovery. This PR now targets main; #147 is merged and is no longer a stacked dependency.

Exit, coverage and collection contract

  • Terminal execution/outcome commits retain immutable, bounded exit evidence. Incomplete queries are explicit unknown coverage, never an empty success or a partial automatic continuation.
  • Immutable child_exit_recheck survives mixed-version projection rewrites, including covered-only exits. Covered children cause no present-uncollected warning or unnecessary continuation. Inbox rechecks after waiter death/cancellation or subscription expiry/cancellation, even after collection of the parent's own result. Inbox never launches continuations.
  • Exact-ID await and foreground delegate/continue --wait hold unique kernel waiter locks on macOS/Linux through delivery. Process death releases the lock; unlocked files prove nothing. Registration is best effort. Attention and terminal unacknowledged rows remain actionable. A parent waiter does not cover children; await-any is not per-child coverage. Local exact-ID awaits can cover remote forwarding rows; local subscriptions cannot prove remote completion.
  • Eligible parents get at most one attempt through ordinary keyed continue and the frozen recipe, with no fresh-session fallback. The claim is durable before launch and immutable successor provenance prevents recursion. Cancellation and the original deadline remain binding. delegation.child_exit_auto_continue: false disables automatic continuation while retaining exit evidence/inbox actions (default on). Childless runs do not change.
Route Behavior
Codex, Cursor, Claude Code, OMP One continuation when recorded recipe/session supports it; verified failed-turn recovery follows existing adapter rules.
ZCode Eligible coding recipes/version only; disposable read-only sessions remain flag-only.
OpenCode Verified 2.2.6 background-service recipe only, preserving #150's version and session-existence checks; other recipes/versions stay flagged.
Devin ACP Conditional session-load attempt; initialization must advertise support and verify frozen configuration. Print mode stays flag-only.
Hermes, OpenClaw, generic process, expert run without recipe Flag/inbox; no verified automatic route.
Multica Existing task/run authority; no agentctl Multica auto-continuation.

Review repairs

Collected history no longer consumes active child-recheck capacity. Inbox selects parents from its existing complete hydrated execution and explicit acknowledgement snapshots, under one held read-only handle. Every retained nonterminal child remains an obligation regardless of waiter coverage. Terminal children settle only on actual validated collection. There is no new mutable settlement state, read-side write, speculative global scan or remote authority.

The additional false-settlement defect is repaired: acknowledgement JSON identity cannot override its authoritative typed bucket key. Inbox and scoped child queries share strict identity, source and nonzero-time validation. Corruption returns exit 70 with inbox_acknowledgement_corrupt, complete:false and no success result. The new CLI regression demonstrably fails on d4c94e1's old production paths for wrong-key, invalid-source and missing-time records; valid collected/uncollected controls and read-only byte comparisons pass after repair.

Independent round 4 found one retained-membership negative-evidence gap: missing execution rows could make the snapshot prefilter skip the authoritative query and report a clean inbox. The repair validates selected parent-index membership before retiring a marked parent. Live members must resolve in the complete hydrated snapshot; missing rows require matching valid expiry tombstones, even when a collection stamp exists. Invalid or incomplete evidence retains children_query_incomplete and a read-only scoped recheck action. Query/proof share the strict expiry decoder. No schema, epoch, migration, mutable settlement, read-side repair or external/API shape change is introduced.

The new precise CLI regression fails on 336e8e94897af3b8e58fee20a682f7b829a73ef7 for missing rows, missing acknowledged rows, missing children covered at exit, and invalid expiry identity/scope. Retained collected/uncollected and valid compact/full expiry controls pass. Complete journal bytes are compared before/after. Writable-handle, partial-snapshot and cancelled-proof controls fail closed. Committed round-4 repair evidence.

Independent round 5 refined the same retained-membership negative-evidence mechanism: scope-only/key-only, present-null/empty metadata, and missing/invalid digests were being accepted as expiry. The shared decoder now accepts compact expiry only when both scope/key fields are absent, or full expiry only when both match the authoritative index. Both require the attribution producer's sha256: plus 64 lowercase hex digest representation. A tombstone cannot hide a retained execution. No other record semantics or API/schema shapes change.

The extended CLI regression reproduced false-clean Children/inbox output on 3d5bb77841f67250e0e9d2e3e9511c9de818d533 before the production fix. It now requires ErrCorrupt from Children and the precise incomplete inbox guard/reason/read-only action, with full journal byte comparisons. Valid controls include actual attribution metadata and a real compact replay record produced by retention Plan/Apply; the full compatibility control adds the attribution producer's exact scope/key. Committed round-5 producer/repair evidence. This remains the round-4/5 mechanism, not a new root by field name.

The membership proof only seeks selected parent prefixes under the same held read-only journal handle. It validates certification/generations in one stable transaction, with shared 1.5s/100k scope-entry/64MiB index/1MiB value bounds and no subscription/waiter/event/outcome reads. The existing separate 1.5s live-child recheck budget starts afterward, so validated history cannot spend it. These bounded phases may add up to 3s after existing snapshot loads.

The deterministic scale regression retains 1,200 collected historical pairs, an older active parent and both 1,024/1,025 valid-subscription cases. Historical markers cannot crowd that parent off a 200-item inbox page. Subscription loading remains lazy and bounded, using #147's shared strict fanout decoder/compatibility filter. Required-bucket/tombstone corruption handling, targeted membership generations, all certified writer paths, legacy fences, ABA/cleanup and remote semantics are preserved.

Committed reproduction/integration evidence: PR148 collection repair. Full contract: child work. Schemas, help, portable skill and embedded assets are synchronized.

Validation

Frozen head: 7c80c548a53918bdab7da8401e4afe3eef9183a6; main base: fd950e73600611165ada674fba35754877c69903.

  • Focused stateful go test -race -p 1 -count=1 passed: cmd/agentctl 105.718s, store 60.342s, runtime 1.552s. This includes producer-generated expiry and malformed-field controls, membership/acknowledgement corruption, the 1,200-history/1,024–1,025 subscription cases, fanout, hydration/legacy-writer/cleanup/ABA fences, waiter loss and continuation/CAS/recursion/cancellation/deadline/native-recovery controls.
  • Normal pre-push and push passed (CLI 415.262s; store 119.369s). The checked-in hook runs make pre-push, the shared make ci subset. All three CI-only targets (check-distribution, check-release-skew, build) also passed separately, avoiding a duplicate unchanged local full suite. Schema/portable-asset checks and git diff --check passed; the worktree is clean. No force or hook bypass.
  • Exact-head hosted CI passed on attempt 1: Verify (make ci), Go vulnerability scan and full stateful races. CodeQL Actions/Go/Python analyses and aggregate check passed. All seven reported checks are successful before ready-for-review.
  • Devin Review passed at this exact frozen head. All eight reported checks are successful and all four actual review threads are resolved; no unresolved automated finding is hidden. The parent owns mandatory independent round 6; this lane launched no independent review.
  • Historical anomaly retained: at prior head 3d5bb778, the first hosted race attempt failed only the source-unchanged TestReview4ANXExitTerminatesDescendants/0/closed pulse-file assertion. Its failed-job retry at the same head and 20 isolated local race repetitions passed. No ANX source changes or unrelated production repair are claimed. Current-head local and hosted gates passed on their first attempts; the ANX anomaly did not recur.
  • Local tests/pre-push and hosted workflows use Go 1.26.11. Tests/pre-push use plain Apple Git 2.54.0 first on PATH, hermetic HOME/XDG and fake provider/SSH/notification fixtures. No real pages, Telegram, model/provider, GCP or production calls.

Limits and remaining gate

Read-only child queries/rechecks retain the existing 1.5s shared processing budget and count/byte/page/lock limits; active overload remains explicitly incomplete. Waiter evidence proves a live collector, not consumption of its output. Remote state remains cached and lost receipts retain identical-key recovery. A process killed before terminal commit cannot perform an owner-exit check; a claimed automatic attempt is never retried after owner loss. Parent-graph retention is unchanged.

The parent owns mandatory fresh independent review round 6 and the merge decision. This lane launched no independent review. If the same mechanism remains unresolved there, the parent owns the requested ANX ask/recommendation/default; no waiver is implied. No merge, tag, release or host installation was performed by this lane.

@cursor

cursor Bot commented Nov 11, 2026 •

Copy link
Copy Markdown

PR Summary

High Risk
Touches terminal commit paths, automatic continuation admission, and journal-side waiter semantics—coordination bugs could mis-report coverage or launch duplicate collection turns.

Overview
Native parent exits now atomically snapshot outstanding delegated children on terminal execution/outcome (uncollected_children, child_exit_guard), surface warnings, and keep inbox actionable for child collection even after the parent's result is acknowledged.

On macOS/Linux, collectors register kernel-exclusive waiter locks (await, delegate/continue --wait, terminal await-any without --since); the children query treats those locks (plus durable terminal subscriptions) as known collector evidence instead of assuming every running child lacks a waiter.

Eligible completed—or verified failed-with-route—native parents may launch one automatic keyed continue turn listing exact collection commands; auto_continuation_of blocks recursion. Profile flag delegation.child_exit_auto_continue (default true) disables only the auto-turn, not flags/inbox. Schemas, help, portable assets, and docs are updated accordingly.

Reviewed by Cursor Bugbot for commit 7ae3de5. Configure here.

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Nov 11, 2026 •

Copy link
Copy Markdown
Codex Review Summary: ✅ Completed

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-11-11T14:33:03.385657Z 3d5bb77 Draft marked ready
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

devin-ai-integration[bot]

This comment was marked as resolved.

* chore: build with Go 1.26.11 (GO-2026-6859 in net/http)

The v0.21.9 release build failed its vulnerability scan: net/http in
go1.26.10 is affected by GO-2026-6859, fixed in go1.26.11.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>

* fix: reject Go 1.27.3 release builds for GO-2026-6859

The per-line release floor still accepted go1.27.3 after the net/http
bump. GO-2026-6859 is fixed in go1.26.11 and go1.27.4.

---------

Co-authored-by: Claude Fable 5.2 <noreply@anthropic.com>
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
cursor[bot]

This comment was marked as resolved.

@Git-on-my-level
Git-on-my-level marked this pull request as draft November 11, 2026 09:56
@Git-on-my-level
Git-on-my-level changed the base branch from feat/uncollected-children-at-exit to main November 11, 2026 13:00
@Git-on-my-level
Git-on-my-level marked this pull request as ready for review November 11, 2026 13:30
@Git-on-my-level
Git-on-my-level marked this pull request as draft November 11, 2026 13:47
@Git-on-my-level
Git-on-my-level marked this pull request as ready for review November 11, 2026 14:30
@Git-on-my-level
Git-on-my-level marked this pull request as draft November 11, 2026 14:42
@Git-on-my-level
Git-on-my-level marked this pull request as ready for review November 11, 2026 15:11
@Git-on-my-level
Git-on-my-level marked this pull request as draft November 11, 2026 15:36
@Git-on-my-level
Git-on-my-level marked this pull request as ready for review November 11, 2026 16:09
@Git-on-my-level

Git-on-my-level commented Nov 11, 2026 •

Copy link
Copy Markdown
Owner Author

The review-6 duplicate-member finding is repaired in 69976734d2a8b6712dac807fd8f164f048329798. Expiry evidence now crosses one shared member-uniqueness check before the existing producer-form/identity/digest/retained-row checks. It rejects duplicate authoritative names and aliases as well as nested duplicate members; supported producer forms and unique extensions remain compatible. Other journal record decoders are unchanged.

New regressions reproduce 11 semantic false-clean cases against 7c80c54 using a Go overlay, and pass on the repair. Focused tests and races (including large-history/subscription controls) pass. The normal pre-push gate passed with Go1.26.11. Full source CI is running on GitHub. Source evidence is in docs/evidence/2026-11-11-pr148-expiry-json-class.md.

A fresh independent review is running on this exact frozen head. Hosted checks and that review remain required; this comment does not claim a merge verdict or release.

@Git-on-my-level
Git-on-my-level merged commit 686da4d into main Nov 11, 2026
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant