Repository navigation
fix(distribute): drop num_cpus for std::thread::available_parallelism — the 7 selections the tokio gate leaves red - #3266
Merged
Merged
Conversation
… — the 7 selections the tokio gate left red
The `feature = "tokio"` gate in the previous commit is necessary but not
sufficient. Measured on that commit: 17 of 24 single-feature selections go
green and seven stay RED with
error[E0433]: failed to resolve: use of unresolved module or unlinked crate `num_cpus`
checkpoint, microvm, remote, remote-tls, serverless, tokio, tui — because only
`cpu` pulled `num_cpus` while `src/executor/cpu.rs` compiles under any feature
that enables `tokio`.
DECIDED BY 3-LANE QUORUM (agy, gemini-3.1-pro / 3.8-flash / 3.7-flash), 3/3 for
this option over the three alternatives it was given — gate on `all(tokio,
num_cpus)`, add `num_cpus` to each of the six sibling features, or make it
non-optional. Its grounds, which I re-ran rather than took:
* `num_cpus` is called in EXACTLY ONE place in the crate,
`executor/cpu.rs:41`. The six other textual hits are a struct field, a
constructor parameter and a getter, all plain `usize`.
* `std::thread::available_parallelism` is stable since Rust 1.59; this
workspace's MSRV is 1.91.
* Deleting the optional dependency removes the defect CLASS, not the
instance: there is no longer a dependency for a sibling feature to forget.
The three alternatives all leave a declaration that can drift again.
`map_or(1, NonZeroUsize::get)` preserves the old behaviour exactly: `num_cpus::get()`
returns 1 when it cannot determine the count, and `available_parallelism()`
returns `Err` in the same situation.
Verified here, not quoted from the lanes: all 9 acceptance selections plus every
other feature of this crate check clean, default checks clean, clippy is clean,
and aprender-distribute --lib is 84/84.
Refs #3262
Pmat-Ticket: PMAT-1098
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
§13.11 rung 1 — quorum shadow verdict Shadow mode: this records a verdict and merges nothing. A refusal |
noahgift
disabled auto-merge
September 14, 2026 17:42
noahgift
enabled auto-merge
September 14, 2026 18:38
noahgift
disabled auto-merge
September 14, 2026 22:41
noahgift
added a commit
that referenced
this pull request
Sep 16, 2026
…roup is gone Run 35099392561 built gh-readonly-queue/main/pr-3266-2261757fd8a4... from 13:02:23Z to 14:30:59Z -- eighty-eight minutes -- after the queue had already discarded that entry and rebuilt #3266 on base f5d02de. GitHub does not cancel the run on a discarded ref: it keeps building, draws runners, and then holds `gate` queued for fifty minutes on a concurrency group, computing a verdict nothing can ever read. THE KEY IS (PR NUMBER, BASE OID), AND BOTH HALVES ARE LOAD-BEARING. A merge queue ref is `gh-readonly-queue/<base>/pr-<N>-<40 hex>` and the 40 hex IS the entry's `baseCommit.oid` -- read live from both sides and committed as the fixture (o1_mq_entries_live.json is the verbatim answer to MQ_QUERY, so the field names in the predicate cannot drift from the ones GitHub returns). Matching on the PR number alone answers LEAVE on the one run this rule exists to cancel -- the zombie and its replacement share the number (row O5). Matching on the base alone reads one group as licence to judge its neighbour: #3266's base oid IS #3270's head oid. NOT REDUNDANT WITH THE DEAD-REF PASS. That pass asks whether the git ref resolves; this asks whether the GROUP is still an entry of the live queue. They disagree in both directions -- a discarded group's ref can linger, a live group's ref can 404 mid-rebuild. ABSENCE OF EVIDENCE IS NOT ABSENCE OF THE GROUP. Unreadable, wrong-shape, and EMPTY entries all REFUSE. Empty is the dangerous one: read as "the queue is empty" it licenses cancelling every merge_group run in flight -- the whole queue, in one sweep (rows O3/O3b/O3c). Verified live, against a freshly fetched payload rather than the fixture: pr-3270-eb262f8e -> LEAVE (entry at position 1) pr-3266-f5d02deb -> LEAVE (entry at position 2) pr-3266-2261757f -> CANCEL (the measured zombie) pr-9999-eb262f8e -> CANCEL (a live base, a PR not in the queue at all) Mutation proof: disabling the absence check turns O2 red (self-test exit 1, "got LEAVE, wanted CANCEL"); restored, exit 0. 53 rows, 0 red. Pmat-Ticket: PMAT-3292 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This was referenced Sep 16, 2026
Merged
guyernest
pushed a commit
to guyernest/aprender
that referenced
this pull request
Sep 29, 2026
…cancelled; arming refuses --auto unless guard-tree is green on the current head (paiml#3292, paiml#3358) (paiml#3403) * feat(unwedge): paiml#3292 rule 3 -- cancel a merge_group run whose queue group is gone Run 35099392561 built gh-readonly-queue/main/pr-3266-2261757fd8a4... from 13:02:23Z to 14:30:59Z -- eighty-eight minutes -- after the queue had already discarded that entry and rebuilt paiml#3266 on base f5d02de. GitHub does not cancel the run on a discarded ref: it keeps building, draws runners, and then holds `gate` queued for fifty minutes on a concurrency group, computing a verdict nothing can ever read. THE KEY IS (PR NUMBER, BASE OID), AND BOTH HALVES ARE LOAD-BEARING. A merge queue ref is `gh-readonly-queue/<base>/pr-<N>-<40 hex>` and the 40 hex IS the entry's `baseCommit.oid` -- read live from both sides and committed as the fixture (o1_mq_entries_live.json is the verbatim answer to MQ_QUERY, so the field names in the predicate cannot drift from the ones GitHub returns). Matching on the PR number alone answers LEAVE on the one run this rule exists to cancel -- the zombie and its replacement share the number (row O5). Matching on the base alone reads one group as licence to judge its neighbour: paiml#3266's base oid IS paiml#3270's head oid. NOT REDUNDANT WITH THE DEAD-REF PASS. That pass asks whether the git ref resolves; this asks whether the GROUP is still an entry of the live queue. They disagree in both directions -- a discarded group's ref can linger, a live group's ref can 404 mid-rebuild. ABSENCE OF EVIDENCE IS NOT ABSENCE OF THE GROUP. Unreadable, wrong-shape, and EMPTY entries all REFUSE. Empty is the dangerous one: read as "the queue is empty" it licenses cancelling every merge_group run in flight -- the whole queue, in one sweep (rows O3/O3b/O3c). Verified live, against a freshly fetched payload rather than the fixture: pr-3270-eb262f8e -> LEAVE (entry at position 1) pr-3266-f5d02deb -> LEAVE (entry at position 2) pr-3266-2261757f -> CANCEL (the measured zombie) pr-9999-eb262f8e -> CANCEL (a live base, a PR not in the queue at all) Mutation proof: disabling the absence check turns O2 red (self-test exit 1, "got LEAVE, wanted CANCEL"); restored, exit 0. 53 rows, 0 red. Pmat-Ticket: PMAT-3292 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * feat(arm): refuse --auto unless guard-tree is green on the CURRENT head (paiml#3292) paiml#3278 was armed on 2026-09-16 while its guard-tree was RED. The merge queue took it, put it at POSITION 1, and when it failed there it ejected the batch queued behind it. An arming decision is not local: a PR armed on a red guard costs every PR behind it a rebuild, and the queue is deepest exactly when that hurts most. THE STALE-CHECK TRAP IS THE WHOLE POINT. "guard-tree is green on this PR" is a claim about a SHA, not about a PR, and every name-keyed surface (`gh pr checks`, `statusCheckRollup`) will show a success from two pushes ago next to the newer head. So the head sha is read FIRST, the checks come from repos/{owner}/{repo}/commits/<that sha>/check-runs -- an endpoint that cannot answer about another commit -- and every row is filtered on `head_sha == <that sha>` again anyway. A success that exists only on an older sha is the named state `stale`, not a silent `absent`: the two want different fixes. ABSENT IS A REFUSAL. No row, no failure, arm -- that is the fail-open shape this repo keeps shipping. Here it exits 3, as do in_progress, queued, skipped, and a re-run in flight beside an earlier success on the same sha. WHY NOT pr_review_quorum_arm.sh. It was searched for first and it IS an arming path, but it evaluates S13's review-quorum predicate and refuses with Q1 when there is no signed review receipt, so it cannot be the general arming entry point; and it reads statusCheckRollup, the surface this trap lives in. The two compose -- a quorum PERMIT still has to clear this precondition. 12 rows, 0 red, fixtures only, with A1 as the positive control (without it a function that always refuses satisfies every other row). Verified live in all three polarities against open PRs, --dry-run: paiml#3366 refuse: guard-tree is failure on 179c69b... exit 3 paiml#3361 refuse: guard-tree is in_progress on 062e471... exit 3 paiml#3363 WOULD-ARM on e5cf234... exit 0 #99999999 (no such PR) exit 2 bashrs: 0 errors. Pmat-Ticket: PMAT-3292 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
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.
What
The seven
aprender-distributeselections that #3257'sfeature = "tokio"gate leaves red. Measured against that PR's actual head (f18e257dc): 17 of 24 single-feature selections go green and these seven stay red withcheckpoint,microvm,remote,remote-tls,serverless,tokio,tui— because onlycpupullednum_cpus, whilesrc/executor/cpu.rscompiles under any feature that enablestokio.Decided by quorum
Three agy lanes (gemini-3.1-pro-high / 3.8-flash-high / 3.7-flash-high), 3/3 for this option over the three alternatives they were given: gate on
all(tokio, num_cpus), addnum_cpusto each of the six sibling features, or make it non-optional. Their grounds, which I re-ran rather than took on trust:num_cpusis called in exactly one place in the crate,executor/cpu.rs:41. The six other textual hits are a struct field, a constructor parameter and a getter, all plainusize.std::thread::available_parallelismis stable since Rust 1.59; this workspace's MSRV is 1.91.map_or(1, NonZeroUsize::get)preserves the old behaviour exactly:num_cpus::get()returns 1 when it cannot determine the count, andavailable_parallelism()returnsErrin the same situation.One lane also asserted that
num_cpusmishandles cgroup v2 whileavailable_parallelismdoes not. I have not verified that and it is not load-bearing here — the decision rests on the three grounds above, all of which are checkable from the tree.Why a separate PR
The quorum suggested landing this inside #3257. That turned out not to be possible: #3257 is in the merge queue, so
git pushto its branch is refused (GH006), and dequeuing a group whose required checks are passing is not something I'll do to save a rebase. This is independent and safe onmainon its own — verified below — so it lands beside #3257 rather than inside it.Neither PR alone turns the seven green: #3257 removes the
tokiohalf, this removes thenum_cpushalf. Both are needed.Verified
On this branch, based on
mainwith #3257 not present:--features cpuand default both check cleanaprender-distribute --lib84/84On #3257's branch with this commit applied — the configuration that matters — all nine acceptance selections (
checkpoint microvm remote remote-tls serverless tokio tui cpu full) plus every other feature of the crate check clean, and tests and clippy are clean.Refs #3262, #3257
no-close: #3262 is the umbrella measurement and stays open until the last two
cudapairs are green; #3257 lands the other half of this crate's fix.🤖 Generated with Claude Code