fix(ci): read the ci-red label back, and unflake the arm that ejected PR 669 (BACKLOG #1385) - #789
Merged
Conversation
… PR 669 (BACKLOG #1385) THE CENSUS IN #1385 IS WRONG BY ONE. There were FOUR failing merge_group CI runs on PR 669, not three. An unpaginated per_page=100 query returns 100 of total_count 190 and silently drops half the population; --paginate finds 33249679477, 33250553925, 33253197221 and 33253973185. Each is attributed below from its own job log, not inferred. 11:15:01Z test (windows-2025) hung 55 min, no FAILED line 11:37:13Z test (windows-2025) test_a_fast_handler_is_untouched -- assert 503 == 200 12:42:51Z windows + sql server 2022 hostile-disposition ratio; cipher-invocations upsert 13:01:54Z windows + sql server 2025 dr/connscale; cipher-invocations upsert So "three DIFFERENT reasons with no overlap" is not what the logs say: the SQL Server test failed in BOTH of the last two runs, identically. TWO THINGS BUILT. 1. tests/test_api_request_timeout.py. The 0.1s deadline is gone from the arms asserting a prompt handler is UNTOUCHED -- the arm that actually ejected #669 at 11:37 with assert 503 == 200. A trivial handler's real cost is ~1ms, so 0.1s was not a margin, it was inside the range of a GC pause on a shared runner. The arms asserting the bound FIRES keep 0.1s, because there the margin runs the forgiving way: a stall can only delay a refusal, never turn it into a pass. Raising it costs no discrimination, and that is checked rather than asserted. Mutation A (refuse unconditionally) reds test_a_fast_handler_is_untouched at the new 5.0s deadline: 3 failed, 6 passed. While in the file: test_a_disabled_deadline_lets_a_slow_handler_finish asked the FAST route, so it could not fail for its stated reason. It asks the slow route now. Proven with a controlled arm -- Mutation B (clamp a disabled deadline instead of disabling it) with the NEW body reds 1 of 9; with the OLD body, same mutation, 9 passed. One variable, mutation held constant. 2. scripts/ci/report_ci_red.py plus 15 tests. failure-signal.yml has written the ci-red label since PR #716 and nothing read it. This reads it back and names the run, marking merge_group runs as NOT VISIBLE ON THE PR PAGE -- which is the finding, because that is the state PR 669 was in. It copies three rules from the writer on purpose: the watched workflow list, failure-only (a cancelled run is not a red), and the attribution order -- pull_requests[0] first, then the pr-<N> ref parse GATED ON event == merge_group. That gate is a security control: a branch name is chosen by whoever opens the branch, and a fork cannot raise a merge_group event. Mutation C (drop the gate) reds the spoof test; mutation D (count cancelled) reds the cancellation test. Proven firing, not just unit-tested. Live against this repo it found PR 764, attributed it to backlog-hygiene run 33802457076, exit 1 -- verified against the API (event=pull_request, pull_requests=[764]). Against PR 669's real captured runs it reports the merge_group ejection and the hidden-run error. ALREADY FIXED BY #738, NOT REBUILT HERE. test_cipher_invocations_upsert_is_atomic_and_additive failed StoreAcquireTimeout after 30s because the aioodbc pool shared the loop's default executor (8 threads on a 4-vCPU runner) against pool_size 40, so 20 concurrent upserts deadlocked on the executor queue. cc5e09e (2026-09-02) gave the store its own executor and names this exact test and error. STILL OPEN. test_hostile_disposition_header_parses_in_linear_time was waved through as "already a ratio assertion". It is one, and it ejected #669 anyway, at ratio 8.02 against a bound of 8.0. Left alone per the brief; recorded in the item because the dismissal does not survive the log. ruff, ruff format and mypy strict clean; 57 tests green across the touched and neighbouring suites. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
tests/test_tooling_partition.py::test_every_non_engine_test_is_classified reds all three required test legs when a test that does not import the engine is absent from tests/tooling_manifest.txt. This PR adds such a test, so CI could not go green as it stood. The manifest is read as a set, so this is a single inserted line at its alphabetical slot; no existing line moves.
Collaborator
Author
|
Lander: added the missing
Verified before pushing: the test fails on the previous head naming this exact file, and passes with Re-applied the |
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.
Closes two limbs of BACKLOG #1385, finds a third already closed by someone else, and corrects the item's own run census.
What I re-measured at HEAD before touching anything
failure-signal.yml:71recovers the PR number from amerge_groupref,:92appliesci-redci-redCLAUDE.md:302,docs/METHOD.md:365,zizmor.yml:72and backlog text. No readerci.yml:852passes--max-worker-restart=0, landed042ef7ff5, comment:826-:850records four hangs of 25-46 mintests/test_multipart.py:142is a ratio assertiontests/test_sqlserver_store.py:4039istest_cipher_invocations_upsert_is_atomic_and_additiveci.yml:1772under the native-crash-only wrapper at:1769The census in #1385 is wrong by one, and my first query reproduced the error
There were four failing
merge_groupCI runs on PR 669, not three. An unpaginatedper_page=100query returns 100 oftotal_count190 and silently drops half the population -- I hit that trap myself and nearly published "the timeout test appears nowhere in the failures", which is false.--paginategives the real set. Each row below is read from that job's own log, not inferred:test_a_fast_handler_is_untouched--assert 503 == 200"Three DIFFERENT reasons with no overlap" is not what the logs say. The SQL Server test failed in both of the last two runs, identically. The no-overlap claim held only across the three runs the original pass happened to sample.
Limb 1 -- the request-timeout wall clock
test_a_fast_handler_is_untouchedis not a hypothetical flake: it is the arm that ejected PR 669 at 11:37, withassert 503 == 200. A trivial handler's real cost is ~1ms, so 0.1s was never a margin -- it is inside the range of a GC pause on a shared runner.The new bound is 5.0s, and it still discriminates. Tightness was never what that arm measured. Its job is to catch a middleware that refuses everything; a 5s deadline catches that identically. The claim that the configured value is in force is carried by the trips arms, and carried better -- a 3.0s handler under a 0.1s deadline reds if the middleware ever falls back to the shipped 120s default, which no fast-path assertion at any deadline can see. The arms asserting the bound fires keep 0.1s, because there a stall can only delay a refusal, never turn it into a pass.
I also found an arm that could not fail for its stated reason.
test_a_disabled_deadline_lets_a_slow_handler_finishasked the fast route, so it passed identically with the control it exists to test switched on. It asks the slow route now.Mutation numbers
RequestTimeoutMiddlewarerefuses unconditionallytest_a_fast_handler_is_untouchedat the new 5.0s bound<= 0deadline to 0.1 instead of disablingevent != "merge_group"gate in_pr_for_runcancelledas a redB and its control are the load-bearing pair: same mutation, same everything else, only the requested route differs.
Limb 2 -- already fixed by #738, nothing rebuilt
Established from the log, not guessed. Both SQL Server failures are:
raised from the
asyncio.gatherof 20 concurrentadd_cipher_invocationsattests/test_sqlserver_store.py:4049. The cause is the aioodbc pool sharing the event loop's default executor -- 8 threads on a 4-vCPU runner -- against apool_sizeof 40, so 20 concurrent upserts park inside a blockedexecuteand the lock holder's owncommitqueues behind them with no worker left.cc5e09e1e(2026-09-02, PR #738) gave the store its own executor sizedmaxsize + 4. Its commit message names this exact test and this exact error. It is an ancestor of HEAD (checked). No SQL Server leg failure appears in the completed CI runs I sampled since it landed -- the two failures in that window wererepo harness testsandweb console tests, neither on this leg.Limb 3 -- the reader, proven firing
scripts/ci/report_ci_red.pyreads the label back and names the run, markingmerge_groupruns asNOT VISIBLE ON THE PR PAGE. That mark is the finding: it is the state PR 669 was in for three CI cycles.It copies three rules from the writer deliberately, because a reader that classifies differently reports causes the label was never applied for: the watched workflow list, failure-only (a cancelled run is not a red -- an ejection cancels its siblings), and the attribution order --
pull_requests[0]first, then thepr-<N>ref parse gated onevent == "merge_group". That gate is a security control, not tidiness: a branch name is chosen by whoever opens the branch, and a fork cannot raise amerge_groupevent. Two tests readfailure-signal.ymlitself rather than restating its list, so they cannot pass by agreeing with a stale copy.Live proof, not just unit tests. Run against this repository it found PR 764, attributed it to
backlog-hygienerun 33802457076, exit 1 -- and I verified that attribution against the API rather than trusting my own output (event=pull_request,pull_requests=[764], PR 764 does carry the label). Run against PR 669's real captured runs, with the label state constructed:Open questions for the Console
db53fd45d, so the code that failed is the code in the tree today. Being a ratio rather than a wall-clock budget did not save it. This needs its own read.wshallwshall/claude-multisession#108. If you want it on a cron, that is a separate decision.MERGE ... WITH (HOLDLOCK)on this path has noUPDLOCKand no 1205 retry, and five other MERGE HOLDLOCK sites would need the same read. Raising real concurrency could turn the fixed deadlock into a deadlock-victim flake. Out of scope here, unfiled.Checks
Ran, all green:
ruff check .,ruff format --check .,mypy messagefoundry messagefoundry_webconsole --exclude 'messagefoundry/tray/'(283 files, exactly asci.yml:417runs it), the fullpre-commithook set on the changed files, and pytest ontest_api_request_timeout.py,test_ci_red_reader.py,test_failure_signal.py,test_ci_tooling_gate.py(57 passed) plus the citation and required-context gates (94 passed). Also green: the gates that ENUMERATEscripts/, since this PR adds a file there --test_cp1252_console_safety.py(its scope isscripts/**/*.py),test_negative_controls.py,test_merge_gate_controls.py,test_backlog_status_check.py,test_conftest_name_collision_guard.py(110 passed).Did not run: the full suite. I also started the 2879-test
-m toolingsweep and it did not produce a usable result -- it ended in asubprocess.communicatetimeout with no pytest summary line at all. Its harness annotation readsexited with code 0, and that is not a pass: the code answers a different question than the one asked, which is the SDS-3.8 trap, so I am not reporting it as green. The captured output was truncated to the tail, so I cannot name the test that hung and will not guess at one.It is not this change. No test in the suite executes the scripts it enumerates --
test_serverdb_ci_coverage.py:91-99readsscripts/ci/**as text, and the other enumerating gates do the same -- so the new script cannot be the subprocess that timed out. Checked directly by running every gate that globsscripts/:test_cp1252_console_safety.py,test_serverdb_ci_coverage.py,test_dast_claims.py,test_sds_rule_ids_are_stable.py-- all green (47 passed in the last three, plus the 110 above). No workflow or branch-protection change here, so.github/required-contexts.txtand the pinned count intests/test_required_contexts.pyare untouched and that test passes.Legs a reader must check after my process exits, since none of them are reachable from a Builder's turn:
sql server (store + connector)on both 2022 and 2025,test (windows-2025, py3.14),windows-service-smoke, and themerge_groupruns themselves -- which is, with some irony, exactly the blind spot this PR is about.🤖 Generated with Claude Code