Repository navigation
scripts: keep the qualification harness within its open-file limit (P1R-C06) - #113
Merged
Merged
Conversation
…1R-C06) The recovery rehearsal on the qualification host ran out of open files under Terminal's default limit of 256: every gateway left its three pipes and its transcript open for the rest of the run, so the 27th functional scenario and every later one failed with "Too many open files", and a journal read at the load interval's drain then ended the run. - The readers of a gateway's output and log close its pipes when it ends, and end_gateway, close and the recovery bench's teardown wait for them. - The fixture authority's reader closes its output; kill and stop close its input and log and wait for the reader. - The evidence writer keeps at most 32 files open, closing the least recently written first and appending again when it reopens one. A recovery interval writes about 17 files at once, so it closes none of them. - The parent's copies of two worker logs are closed after the start. - A journal that cannot be read at the drain makes the interval inconclusive, as the protocol says, instead of ending the run. - Each report records the open-file limit the harness and what it started ran under. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This was referenced Oct 17, 2026
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.
Issue
Part of #101: P1R-C06. The first recovery rehearsal on the qualification host failed in the harness itself, so this fixes the harness before the candidate is frozen again. It follows #112 and does not close an issue.
mainatb2cb042(the merge of #112)p1r-c06-recovery-rehearsal-2026-10-17-b2cb042on the qualification host,MANIFEST.jsonSHA-2565f58673c69f35c993852953daf4d5a09ff0dbaf2c857a7fca343aeb507fac631;verifyaccepts it (recordedharness_failed, recomputedfailed)6e7e065; both unchangedSummary
Before: the harness kept a descriptor of everything it started until the run ended: each gateway's three pipes, the transcript the evidence writer opened for it, and each fixture authority's output. Run from Terminal, whose default limit is 256 open files, the
recoveryrehearsal ran out at its 27th functional scenario (recovery-fence … concurrent-gateways): that scenario and every later one failed withOSError: [Errno 24] Too many open filesin the harness's ownPopen, the idle interval of the measurement passed once the functional writer had closed its files, and a journal read at the load interval's drain then failed withunable to open database fileand ended the run asharness_failed. The morning's checks of the same suites passed because the session that ran them had a limit of 1,048,576. No failure came from CodeSpace or DevGuard.After: the harness stays far below 256 open files however long it runs (at most 43 in a whole rehearsal), and a
recovery --rehearsalpasses the same way under a limit of 256 as without one. A journal that cannot be read at the drain makes the interval inconclusive, as the protocol says, instead of ending the run, and every report records the open-file limit the harness and what it started ran under.How.
scripts/qualify_host.py): the reader of a gateway's output closes its output and input pipes once the connection ends, and the reader of its log closes its error pipe.end_gateway,Gateway.closeand the recovery bench's teardown wait for both readers throughGateway.join_readers, so the pipes are closed when they return.killandstopclose the input and the log and wait for the reader.WRITER_OPEN_FILES), closes the least recently written first, and appends again when it opens one anew. A governed recovery interval writes about 17 files at once, so it closes none of them; the functional scenarios' writer, which gets one transcript per gateway, no longer keeps them all.scripts/qualify-devguard.py): the parent's copies of the standalone and managed workers' logs are closed once the workers have started.OSErrororsqlite3.Errorwhile it waits for the Runner's instance to retire is kept asjournal_errorand adds "the journal could not be read" to the interval's invalid reasons.harness.open_files_limitin eachreport.jsonholds the soft and hard limits.The protocol freezes the harness with it; this change comes before the qualification run, and the candidate is frozen again on its merge, so every qualification run uses it.
Contract changes
None. The product is unchanged; only the harness and its tests change. Each
report.jsongainsharness.open_files_limit, whichverifydoes not compare (same_harnesscompares the harness files only).Tests
Verified here at the head of this branch, in a Linux cloud container, with debug builds of
codespace-mcpandcodespace-codex-runtime(the product is unchanged sinceb2cb042):python3 -B -m unittest discover -s scripts/tests: 122 passed, 5 of them new: ended gateways leave none of their pipes open, whether their input closed, SIGTERM, SIGUSR1, SIGKILL, a kill at teardown orcloseended them, while each stays referenced as a run keeps it; stopped and killed fixture authorities likewise; the writer never holds more than its limit and appends every row in order across reopening; a journal that cannot be opened at the drain is recorded as the interval's invalid reason, the error the qualification host hit; and the report records the limit. Without the fix the fixture test fails with 14 descriptors left instead of 5, and the drain test fails with the host'sOperationalError.pyflakes on the four scripts.
recovery --rehearsal --participation offunder a soft limit of 256 (ulimit -Sn 256), with the harness's descriptors sampled every 2 seconds:off)verifyb2cb042, beforerecovery-reconcile … kill-during-exec) failed withErrno 24failed(exit 1), verifiedinconclusive(exit 2), verifiedThe load interval is inconclusive by design here ("no load ran: there is no DevGuard service to run it"), as in the rehearsal of scripts, docs: the independent Runner's qualification harness and protocol (P1R-C06) #112. This branch's report records
open_files_limit[256, 20000]and the harness commit4297da4, not dirty; itsMANIFEST.jsonSHA-256 is9b6e9b355fac9c1892d8116bf5b0bbf8508ee751294cd0f8d95c430a148a32c7.Without a limit, the unfixed harness held 22 descriptors after the first functional scenario and 429 after the last.
Not verified here:
required, which runs only on macOS, and the measurement against the installed service. On the merge, the qualification host repeats the check suites under a limit of 256 before the candidate is frozen again.Security scenarios
Out of scope