Skip to content

test(harness): join completion gate, fault seams, launcher-bound selftests - #2684

Merged
chaliy merged 14 commits into
mainfrom
codex/syntax-diagnostic-budget
Oct 11, 2026
Merged

chaliy merged 14 commits into
mainfrom
codex/syntax-diagnostic-budget

Conversation

@chaliy

@chaliy chaliy commented Oct 11, 2026

Copy link
Copy Markdown
Contributor

What changed

Test-only, one file: threat_model_tests.rs. Closes the completion review on #2683 without touching #2681 distinct run_bounded_probe helper:

  • Bounded join completion gate: readers join only while is_finished inside the shared completion grace (JOIN_GRACE 5s; full budget is execution limit + drain 2s + reap 5s + completion 5s). A channel receipt is not the termination contract since scheduling can pause the sender after send. Unfinished handles stay detached and fail incomplete; panicking readers fail visibly. No reproduced production hang is claimed.
  • Deterministic paused-after-send proof with explicit release/cleanup path and watchdog: a sender blocked on release reads unfinished by construction, releases cleanly, and joins inside the bound.
  • Poll/read/reap faults injected through the real parent lifecycle (LifecycleFaults seam): poll failure fails closed with a verdict distinct from timeout (incomplete, reap-recovered exit, no timeout); injected read failure propagates read_error with partial capture; unavailable reap yields exitless incomplete. The panicking child.try_wait expect is gone.
  • Timeout/overflow selftests run under the shared fail-closed launcher with stack/cpu readback and elapsed-total bounds.
  • Existing bounded_helper_flags_retained_pipes reused and strengthened (elapsed proof, read_error distinction, outer-deadline documentation).

Before / After

Before: unconditional join after receipts; reap untestable without OS fakery; child poll panicked; selftests bypassed the launcher.
After: threat_model 222 passed (only pre-existing macOS nesting abort skipped, identical on unmodified code); error 14, partial_parse 11 green; fmt plus clippy -D warnings clean.

Risk

  • Test-only change. No production code touched.

Checklist

  • Tests added or updated
  • Backward compatibility considered (internal test harness)

chaliy added 14 commits October 10, 2026 21:58
macOS bash 3.2 reports -c input as line 0, modern GNU bash as line 1.
Compare diagnostic shape with line number normalized.
…gression

A long -c ARG0 stamped on every report line could evict the structural
payload out of the 1 KiB diagnostic budget. Truncate the shell name to 64
chars first so 'syntax error ...' always survives; renumber the threat entry
to TM-DOS-136 (main already owns 135 via #2674); add a bounded-child
regression exercising the exact finding shape (multiline token x long ARG0)
at two scales.
The 64-char cut mangled long script paths (breaking the oracle's path
replacement and hiding which script failed). Widen to 256 chars with a
16-char tail: realistic paths print verbatim with attribution suffixes
(': eval') intact, while a kilobyte-scale ARG0 still cannot evict the
structural payload out of the 1 KiB total. The total stays the enforced
bound.
All five branch commits already reached main via squash merges (#2675, #2677); conflicting files take main's reviewed state.
The scaled multiline-token x long-ARG0 regression now asserts the whole harness contract at both scales (200/2000 lines x 8 KiB ARG0): no watchdog timeout, raw child exit 2, no output-cap overshoot, finished reader threads, and effective stack=4096/cpu=10 caps read back from the launcher. Shape and 1 KiB assertions unchanged.
run_command_bounded cleanup per lifecycle review: truncate retention before extending (retention never passes the 8192 cap); retry Interrupted, fail closed with partial bytes on real reader errors (new read_error field, never relabeled as EOF); exact-cap EOF is not excess; reap inside a finite 5s grace, never an unbounded wait; timed_out retained as recorded; drain 2s + reap 5s graces documented against the execution deadline. Consumer regression gains !read_error and the corrected 8192-byte cap text. Bounded selftests: exact-cap, cap+1, Interrupted/error scripted pipes, timeout kill+reap, overflow flag.
Per lifecycle review on #2682: join readers only after all channel receipts (receipt proves all blocking work done; missing receipts skip the join and fail incomplete), with panicking readers failing visibly; reap extracted into a poll seam with deterministic verdict unit tests; timeout/overflow selftests run under the fail-closed launcher with stack/cpu readback and elapsed-total bounds; retained-pipe case gains elapsed proof and read_error distinction. Reuses existing bounded_helper_flags_retained_pipes; does not touch #2681's distinct probe helper.
…tests

Per completion review on #2683: join readers only while is_finished inside the shared completion grace (a receipt is not the termination contract); deterministic paused-after-send proof with release/cleanup watchdog; poll/read/reap faults injected through the real parent lifecycle with distinct fail-closed verdicts (no panicking expect on child poll); timeout/overflow selftests under the fail-closed launcher with cap readback and elapsed bounds; existing retained-pipe fixture reused and strengthened. Untouched: #2681 distinct probe helper.
…tests

Per completion review on #2683: join readers only while is_finished inside the shared completion grace (a receipt is not the termination contract); deterministic paused-after-send proof with release/cleanup watchdog; poll/read/reap faults injected through the real parent lifecycle with distinct fail-closed verdicts (no panicking expect on child poll); timeout/overflow selftests under the fail-closed launcher with cap readback and elapsed bounds; existing retained-pipe fixture reused and strengthened. Untouched: #2681 distinct probe helper.
@cloudflare-workers-and-pages

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Preview URL Updated (UTC)
✅ Deployment successful!
View logs
bashkit 49a1ffc Commit Preview URL

Branch Preview URL
Oct 11 2026, 10:54 AM

@chaliy

chaliy commented Oct 11, 2026

Copy link
Copy Markdown
Contributor Author

PR 2684: parent-error cleanup and real completion proof

Reviewed exact head 49a1ffc. The is_finished deadline before joining is a useful correction; CPU/stack launchers and overflow elapsed checks also improve the proof. This is not a demonstrated hang claim about the old implementation.

  1. Fix the live-child poll-error cleanup. Both faults.fail_poll and real child.try_wait Err set poll_failed and break before the deadline block. The child.kill guarded by poll_failed inside that deadline block is therefore unreachable on those paths. A live child can survive the bounded function return. Move termination into a shared cleanup path or kill before the break, then boundedly reap; preserve the distinct error verdict rather than labeling it timeout. The current exit-0 fixture cannot expose this. Add a bounded live-child fault test with effective CPU/stack readback, elapsed and actual termination/reap receipts.

  2. The reap_never injection must not abandon the actual OS child. It skips real try_wait entirely after killing the busy-loop fixture, so the test can leave the victim unreaped. Simulate an unavailable observer/verdict while retaining explicit bounded cleanup ownership of the real child; record true cleanup separately from the injected failure. Do not silence zombie checks or weaken incomplete_readers.

  3. The paused-after-send test only examines an unrelated JoinHandle. It does not drive the completion gate used by run_command_bounded_with to its negative verdict, done_rx.recv is unbounded, and the finished handle is not joined. Factor only the actual completion decision needed for the deterministic seam, exercise paused receipt -> unfinished -> incomplete within a finite grace, then release and boundedly confirm/join cleanup. Bound readiness as well. A standalone demonstration of is_finished is not the requested parent completion proof.

  4. Strengthen the EXISTING bounded_helper_flags_retained_pipes, which already invokes the same runner. Keep the hostile retained-pipe verdict. Its bare bash fixture still lacks effective CPU/stack receipts and controlled descendant/reader cleanup. An incomplete capture is not a reason to omit all limit proof: use a bounded launcher and side-channel receipt or an equivalent controlled fixture with truthful missing-capture flags. Do not duplicate the retained-pipe case or clear failure flags.

Keep documented total wall + drain + join + reap bounds consistent with elapsed assertions, remove stale claims that channel delivery alone proves thread completion, and retain every prior negative/real Linux failure. Read this complete issue comment plus all earlier issue comments, reviews and inline threads before merging. Pending or missing checks remain unknown. No CI waiver, force push, extra PR, or finding closure from candidate CI alone; after all fixes, require exact merged revision producer and assertion exits plus durable files and the fresh runner report.

@chaliy
chaliy merged commit 0e27794 into main Oct 11, 2026
33 checks passed
@chaliy
chaliy deleted the codex/syntax-diagnostic-budget branch October 11, 2026 11:19
chaliy added a commit that referenced this pull request Oct 11, 2026
## What changed

Test-only, one file: threat_model_tests.rs. Closes the completion review
on #2684 without touching #2681 distinct run_bounded_probe helper:

- Common cleanup termination: every loop exit (observed completion,
deadline, oversized, poll failure) funnels through one reachable kill
guard; verdicts recorded pre-kill are never recomputed. Previously the
poll-fault break skipped its kill guard.
- Bounded join completion gate via a shared join_finished primitive:
readers join only while is_finished inside JOIN_GRACE (a receipt is not
the termination contract since scheduling can pause the sender after
send). Unfinished handles stay detached and fail incomplete; panics fail
visibly. No reproduced production hang is claimed.
- Deterministic paused-after-send proof drives the actual gate primitive
with bounded readiness (recv_timeout), explicit release, and a finite
join-cleanup watchdog.
- Poll/read/reap faults injected through the real parent lifecycle with
distinct fail-closed verdicts: the panicking child.try_wait expect is
gone; unavailable reap keeps real bounded OS cleanup while the verdict
reports None (no invented success).
- Timeout/overflow/retained-pipe selftests under the shared fail-closed
launcher with stack/cpu readback and elapsed-total bounds; the existing
retained-pipe fixture is reused and strengthened, never duplicated.

## Before / After

Before: poll-fault break skipped termination; unconditional join after
receipts; reap untestable without OS fakery; child poll panicked;
selftests bypassed the launcher.
After: threat_model 222 passed (only pre-existing macOS nesting abort
skipped, identical on unmodified code); error 14, partial_parse 11
green; fmt plus clippy -D warnings clean.

## Risk

- Test-only change. No production code touched.

## Checklist

- [x] Tests added or updated
- [x] Backward compatibility considered (internal test harness)
chaliy added a commit that referenced this pull request Oct 11, 2026
…receipts (#2686)

## What changed

Test-only, one file: threat_model_tests.rs. Closes the completion review
on #2685 without touching #2681 distinct run_bounded_probe helper:

- join_finished returns the unfinished handle, so the paused-after-send
proof releases and joins THAT SAME thread through the actual gate
primitive (bounded readiness, release, finite join cleanup, elapsed
watchdog). No twin thread, no detached-while-asserting-cleanup.
- Poll-fault fixture uses a direct-exec sleeper: the shell replaces
itself, so killing closes the pipes at once with no orphan and no 30s
descendant.
- reap_never keeps the real bounded OS cleanup while only the
observation is discarded for the verdict: no zombies by construction, no
invented exit/reap success.
- Retained-pipe fixture runs under the fail-closed launcher; because
incomplete pipe capture precludes the stderr readback by design, the
effective bounds are proven through a per-process receipt file the
fixture shell writes before parking the sleeper (removed afterwards).
Existing fixture reused and strengthened, never duplicated.
- Prior review debt acknowledged: #2684 merged on formal reviews alone
while the completion comment was unresolved; this followup resolves it
before any merge.

## Before / After

Before: twin-thread gate proof; forked sleeper orphan in the poll
fixture; reap skipped under fault; no launcher proof on the retained
fixture.
After: threat_model 222 passed (only pre-existing macOS nesting abort
skipped, identical on unmodified code); error 14, partial_parse 11
green; fmt plus clippy -D warnings clean.

## Risk

- Test-only change. No production code touched.

## Checklist

- [x] Tests added or updated
- [x] Backward compatibility considered (internal test harness)


## Update: readiness gating, reaped receipt, launcher receipts

Follow-up commits on the same branch add:

- fail_poll fires only after a readiness file proves the victim truly
live under bounds (direct-exec sleeper, no orphan); readiness itself is
asserted alongside the reaped receipt, cap readback, and elapsed bound.
- BoundedRun gains a reaped receipt: OS termination accounted for,
orthogonal to exit_code and to injected observation faults (which
discard the observation, never the cleanup). reap_never asserts real
cleanup ran alongside its discarded verdict.
- Retained-pipe fixture runs under the fail-closed launcher; because
incomplete pipe capture precludes the stderr readback by design, the
effective stack/cpu bounds are proven through a per-process receipt file
the fixture shell writes before parking the sleeper (removed
afterwards).
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