fix(ci): key the queue-flake anchor lookup on the file identity, not on finding + open - #14740
Merged
Merged
Conversation
…on `finding` + `open`
The merge-queue-triage workflow looked its anchor up with
`listForRepo({ state: 'open', labels: 'finding' })`. `finding` is a triage-STATE
label -- first-touch grading takes it off by definition -- so the moment an
anchor was graded the lookup stopped seeing it and the next ejection filed a
fresh one. Measured on the live issues: the surviving anchor is open and carries
the body marker, and lost `finding` to grading; its duplicates are closed with
`state_reason: duplicate`. Correct triage was feeding the loop.
Identity is now the file key plus a machine-owned `queue-flake-anchor` label the
triage protocol never touches, applied on create and adopted onto pre-existing
anchors. Resolution order: an OPEN anchor for the key (oldest wins) is
refreshed; an unestablished absence still creates nothing; a closed-as-duplicate
chain with no open anchor is named rather than added to; a closed-on-its-merits
anchor makes the next ejection a REGRESSION, whose new anchor links the old one.
The counting and aggregation limb is untouched.
The contract test gains A14-A17 for the graded and closed cases, and its
`listForRepo` double now honours `state` and `labels` -- without that a lookup
keyed on `open` + `finding` would satisfy a scenario about a graded anchor and
every one of these pins would be unfalsifiable. Mutations M17-M19 drive the
battery red, M17 by reverting the lookup to the shipped selection.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WLJQhde67SeTccsmnBVarV
…eue-flake-anchor-lookup
This was referenced Sep 2, 2026
baozhoutao
marked this pull request as ready for review
September 2, 2026 23:25
baozhoutao
enabled auto-merge
September 2, 2026 23:25
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.
Fixes #14682
The mechanism, as found on
origin/main(4d0d9445a).github/workflows/merge-queue-triage.yml:624-627looked the anchor up withand matched on the body marker
queue-signature-anchor:KEYor the exact titleQueue-flake anchor: KEY(:632-633). The counting was never involved and is untouched here.findingis a triage-state label: first-touch grading takes it off by definition. So themoment an anchor is graded the lookup stops seeing it and the next ejection files a fresh one.
Measured against the live issues today, through the REST API:
state_reasontests,priority:p1,pm:dispatched,domain:cliduplicatefinding#14648 — the surviving anchor, the one the standing disposition names — is open and carries
the marker, and was invisible to the shipped lookup for one reason only:
findingis gone. Thatis the whole defect, and it is what triage 5516748133 suspected: closing the duplicates removes
the only anchors the lookup could still see, so correct triage was feeding the loop.
The fix
The anchor's identity is now the file key plus a machine-owned label the triage protocol
never touches,
queue-flake-anchor, applied on create and adopted onto pre-existing anchors.The lookup is a pass list, so what it selects is one readable literal:
Pass 1 is the identity query: any state, any triage grade, bounded because only anchors carry
the label, and it is the pass that owns the
scanCompleteargument (an unestablished absence stillrefuses to create, unchanged). Pass 2 is transitional adoption — a bounded scan of OPEN issues that
finds anchors filed before the identity label existed (a graded one carries neither label), and puts
the identity label on them with
issues.addLabelsso pass 1 owns them from the next ejection on. Alater pass is skipped only once an open anchor is in hand: a half-migrated key can have a
labelled closed duplicate and an unlabelled open survivor at the same time.
Resolution order
that is the issue the duplicates were closed against.
anchor is worse than a late one).
state_reason: duplicate— create nothing,and name the closed anchor in the victim's comment. Filing here is exactly what feeds the loop:
the new anchor would be closed as a duplicate in turn.
once and the file is ejecting PRs again, so a new anchor is legitimate, and its body links the
issue it regressed from (the link survives refreshes: the lookup now runs before the body is
built, so the note is rebuilt every time rather than only at creation).
finding+queue-flake-anchor.Deviation from the dispatched order, with the measurement behind it. The order as dispatched
had branch 3 "follow to the canonical the duplicate points at, else treat as a regression". GitHub's
issue payload does not carry a duplicate's target: the read of #14679 above returns
state_reason: "duplicate"and noduplicate_of(the only related key on the payload issub_issues_summary). Where the canonical shares the key it is already found as an OPEN anchor bybranch 1 — that is the incident's own shape — and where it does not, it cannot be followed from
this channel at all. So branch 3 stops and says where the conversation was sent, rather than
guessing at a new home for it; the workflow names and decides nothing, which is its declared
boundary. Falling through to "create" instead would restart the exact loop this card is about.
Known transitional gap, stated rather than hidden: adoption is open-only. A pre-identity anchor
that is already closed is not adopted, because a bounded scan of this repo's ~14k closed issues
does not exist. Nothing regresses from that — it is the status quo — and it ages out as anchors
filed by this version carry the label from birth.
Pins added (
scripts/check-merge-queue-triage-outcome.mjs)Scenarios, driving the shipped bytes extracted from the YAML under doubles:
findingwas taken off is REFRESHED, notduplicated. This is Queue-flake anchor: test/run-dev-unbuilt-workspace.e2e.test.ts #14648 as the API returned it, and it is the card's stated acceptance.
Also asserts the adoption
addLabelscall.one is refreshed. The shape the incident actually reached.
annotated, and the victim's comment names the closed anchor.
closed one, and it carries the identity label.
The
listForRepodouble now honoursstateandlabels. It did not before, and that isload-bearing: a double that ignores the filters lets a lookup keyed on
open+findingsatisfy ascenario about a graded anchor, so every one of these pins would have been unfalsifiable. A2's
rerun world now carries the created anchor's labels and state for the same reason.
Mutations added to
--self-test(each asserts its anchor is present in the shipped source first):open+finding. Expects A14, A15, A16, A17 RED;keepGreen A1, A2 — A1 has no prior anchor and A2's anchor still carries
finding, so bothare found either way, which is what makes the four reds a reading of the graded/closed cases
rather than of a lookup that stopped finding anything.
keepGreen A1, A14, A16.
The discriminating mutation, measured
Run over the committed tree, in memory (the substitution is applied to the extracted source, never
to the working tree, so there is no restore leg to get wrong):
A1 and A2 do not appear: the reds come from the graded and closed cases specifically.
Verification
Gate union derived in this worktree and run on the final commit
b897b09da(
node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands, 33 commands;re-derived after merging
origin/mainand byte-identical to the pre-merge derivation), plus thisscript's own
--self-test, which the derived list does not name. Every exit code captured byredirect-then-read, never through a pipe.
33 of 34 green, 1 NOT MEASURED. No reds.
NOT MEASURED (not a red):
node scripts/check-test-completeness.mjsexits 3 withPREREQUISITE NOT MET-- it grades a savedturbo run testlog and the derived family names itwith no argument, so there is nothing local to hand it. Its own text says this branch is
unreachable in CI, which tees the log and passes the path. Recorded as NOT MEASURED, per that
gate's instruction, rather than as a pass or a failure.
The remaining 26 commands of the union all exited 0. The always-runs CI tail (workflows with no
path filter) is not part of this local reading and is CI's to run.
Governed-surface verdict, re-run on the final file list:
skip-changeset: the diff publishes nothing from any package — one workflow and the gate scriptthat tests it.
Out of scope and untouched, per the card's own boundary: the ejection counting and aggregation
(correct 5/5), the anchor body template beyond the regression line the resolution order needs, and
the live anchor issues themselves — #14648 and its duplicates are triage's.
🤖 Generated with Claude Code
https://claude.ai/code/session_01WLJQhde67SeTccsmnBVarV
Generated by Claude Code