fix(ci): key the queue-flake anchor lookup on the file identity, not on finding + open - #14740

Merged
baozhoutao merged 2 commits into
mainfrom
claude/issue-14682-queue-flake-anchor-lookup
Sep 3, 2026
Merged

fix(ci): key the queue-flake anchor lookup on the file identity, not on finding + open#14740
baozhoutao merged 2 commits into
mainfrom
claude/issue-14682-queue-flake-anchor-lookup

Conversation

@claude

@claudeclaudeBot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

Fixes#14682

The mechanism, as found on origin/main (4d0d9445a)

.github/workflows/merge-queue-triage.yml:624-627 looked the anchor up with

constres=awaitgithub.rest.issues.listForRepo({
owner, repo,state: 'open',labels: ANCHOR_LABEL,// ANCHOR_LABEL = 'finding' (:552)sort: 'created',direction: 'desc',per_page: 100,page: p,});

and matched on the body marker queue-signature-anchor:KEY or the exact title
Queue-flake anchor: KEY (:632-633). The counting was never involved and is untouched here.

finding is a triage-state label: first-touch grading takes it off by definition. So the
moment 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:

issuestatestate_reasonlabelsbody marker
#14648opennulltests, priority:p1, pm:dispatched, domain:clipresent
#14679closedduplicatefindingpresent

#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: finding is gone. That
is 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:

constANCHOR_QUERIES=[{state: 'all',labels: ANCHOR_IDENTITY_LABEL,firstMatchWins: false,adopts: false},{state: 'open',firstMatchWins: true,adopts: true},];

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 scanComplete argument (an unestablished absence still
refuses 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.addLabels so pass 1 owns them from the next ejection on. A
later 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

  1. an OPEN anchor for the key (any labels) — refresh it; the oldest open one wins, because
    that is the issue the duplicates were closed against.
  2. the lookup could not be completed — announce and create nothing (unchanged; a duplicate
    anchor is worse than a late one).
  3. no open anchor and the newest closed one has 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.
  4. no open anchor and the newest closed one was closed on its merits — the flake was answered
    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).
  5. nothing at all — create, now labelled 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 no duplicate_of (the only related key on the payload is
sub_issues_summary). Where the canonical shares the key it is already found as an OPEN anchor by
branch 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:

  • A14 — GRADED anchor: an OPEN anchor whose finding was taken off is REFRESHED, not
    duplicated. 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 addLabels call.
  • A15 — the newest anchor is closed as a duplicate and an older one is open: the OLDER OPEN
    one is refreshed. The shape the incident actually reached.
  • A16 — every anchor for the key is closed as a duplicate: nothing is filed, the run is
    annotated, and the victim's comment names the closed anchor.
  • A17 — the only anchor was closed on its merits: a regression anchor IS filed, it links the
    closed one, and it carries the identity label.

The listForRepo double now honours state and labels. It did not before, and that is
load-bearing: a double that ignores the filters lets a lookup keyed on open + finding satisfy a
scenario 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):

  • M17 — the lookup reverts to selecting open + finding. Expects A14, A15, A16, A17 RED;
    keepGreen A1, A2 — A1 has no prior anchor and A2's anchor still carries finding, so both
    are 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.
  • M18 — the closed-as-duplicate branch collapses into create. Expects A16 RED, keepGreen A14, A17.
  • M19 — a regression anchor stops naming the anchor it regressed from. Expects A17 RED,
    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):

anchor present in extracted source: yes
CLEAN failures: 0
M17 failures by scenario: A14, A15, A16, A17
[A14] NO second anchor is filed for a key that already has an open one, got 1: ["Queue-flake anchor: src/dev-plugin-security-enforcement-warning.test.ts"]
[A14] the graded anchor #14648 is the one refreshed, got []
[A15] the surviving OPEN anchor #14648 is refreshed -- not the closed duplicate, got []
[A16] no anchor is filed into a closed-as-duplicate chain, got 1
[A17] the new anchor LINKS the closed one, got: ...

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/main and byte-identical to the pre-merge derivation), plus this
script's own --self-test, which the derived list does not name. Every exit code captured by
redirect-then-read, never through a pipe.

33 of 34 green, 1 NOT MEASURED. No reds.

node scripts/check-merge-queue-triage-outcome.mjs
check-merge-queue-triage-outcome: OK (90 assertions over 24 scenarios, driving the
33918-char script extracted from .github/workflows/merge-queue-triage.yml against real
captured logs).
node scripts/check-merge-queue-triage-outcome.mjs --self-test
check-merge-queue-triage-outcome --self-test: 126 assertions, 23 mutations of the shipped
script each driven to red.
node scripts/check-self-test-wired.mjs
check-self-test-wired: every one of the 164 script(s) CI runs that ship a `--self-test` has
that self-test run by CI.
pnpm check:workflow-status-functions
check-workflow-status-functions: OK (scanned 30 workflow file(s), 53 job(s), 26 job-level
if: expression(s); 10 read needs.*.outputs.*, all naming a status function).
pnpm check:pm-dispatch-gates
dispatch-gates self-test: 1240 cases pass.
node scripts/check-self-test-workflow-commands.mjs
check-self-test-workflow-commands: no self-test CI runs prints a line the Actions runner
would parse as a workflow command.
node scripts/check-nul-bytes.mjs
check-nul-bytes: OK (scanned 8042 text file(s); no raw ASCII control bytes).
plus a direct sweep of the two edited files for control bytes -- no hits.

NOT MEASURED (not a red):node scripts/check-test-completeness.mjs exits 3 with
PREREQUISITE NOT MET -- it grades a saved turbo run test log and the derived family names it
with 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:

node scripts/pm/check-governed-merges.mjs --test .github/workflows/merge-queue-triage.yml scripts/check-merge-queue-triage-outcome.mjs
governed-surface predicate: 0 of 2 path(s) hit the register (5 surfaces, repo-agnostic).
NOT governed -- ordinary queue landing applies to a PR with exactly this file list.

skip-changeset: the diff publishes nothing from any package — one workflow and the gate script
that 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.

git diff --stat dbf115284 HEAD -- .github/workflows/merge-queue-triage.yml scripts/check-merge-queue-triage-outcome.mjs
.github/workflows/merge-queue-triage.yml | 153 +++++++++++++++----
scripts/check-merge-queue-triage-outcome.mjs | 214 +++++++++++++++++++++++++--
2 files changed, 330 insertions(+), 37 deletions(-)

🤖 Generated with Claude Code

https://claude.ai/code/session_01WLJQhde67SeTccsmnBVarV


Generated by Claude Code

…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
@claudeclaudeBot added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Sep 2, 2026
@baozhoutao
baozhoutao marked this pull request as ready for review September 2, 2026 23:25
@baozhoutao
baozhoutao added this pull request to the merge queueSep 2, 2026
Merged via the queue into main with commit 96b7723Sep 3, 2026
36 checks passed
@baozhoutao
baozhoutao deleted the claude/issue-14682-queue-flake-anchor-lookup branch September 3, 2026 00:49
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ci/cdsize/mskip-changesetPR has no user-facing published change; bypasses the changeset gate

Projects

None yet

2 participants

@baozhoutao@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

fix(ci): key the queue-flake anchor lookup on the file identity, not on finding + open - #14740

Merged
baozhoutao merged 2 commits into
mainfrom
claude/issue-14682-queue-flake-anchor-lookup
Sep 3, 2026
Merged

fix(ci): key the queue-flake anchor lookup on the file identity, not on finding + open#14740
baozhoutao merged 2 commits into
mainfrom
claude/issue-14682-queue-flake-anchor-lookup

Conversation

@claude

@claudeclaudeBot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

Fixes#14682

The mechanism, as found on origin/main (4d0d9445a)

.github/workflows/merge-queue-triage.yml:624-627 looked the anchor up with

constres=awaitgithub.rest.issues.listForRepo({
owner, repo,state: 'open',labels: ANCHOR_LABEL,// ANCHOR_LABEL = 'finding' (:552)sort: 'created',direction: 'desc',per_page: 100,page: p,});

and matched on the body marker queue-signature-anchor:KEY or the exact title
Queue-flake anchor: KEY (:632-633). The counting was never involved and is untouched here.

finding is a triage-state label: first-touch grading takes it off by definition. So the
moment 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:

issuestatestate_reasonlabelsbody marker
#14648opennulltests, priority:p1, pm:dispatched, domain:clipresent
#14679closedduplicatefindingpresent

#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: finding is gone. That
is 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:

constANCHOR_QUERIES=[{state: 'all',labels: ANCHOR_IDENTITY_LABEL,firstMatchWins: false,adopts: false},{state: 'open',firstMatchWins: true,adopts: true},];

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 scanComplete argument (an unestablished absence still
refuses 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.addLabels so pass 1 owns them from the next ejection on. A
later 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

  1. an OPEN anchor for the key (any labels) — refresh it; the oldest open one wins, because
    that is the issue the duplicates were closed against.
  2. the lookup could not be completed — announce and create nothing (unchanged; a duplicate
    anchor is worse than a late one).
  3. no open anchor and the newest closed one has 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.
  4. no open anchor and the newest closed one was closed on its merits — the flake was answered
    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).
  5. nothing at all — create, now labelled 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 no duplicate_of (the only related key on the payload is
sub_issues_summary). Where the canonical shares the key it is already found as an OPEN anchor by
branch 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:

  • A14 — GRADED anchor: an OPEN anchor whose finding was taken off is REFRESHED, not
    duplicated. 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 addLabels call.
  • A15 — the newest anchor is closed as a duplicate and an older one is open: the OLDER OPEN
    one is refreshed. The shape the incident actually reached.
  • A16 — every anchor for the key is closed as a duplicate: nothing is filed, the run is
    annotated, and the victim's comment names the closed anchor.
  • A17 — the only anchor was closed on its merits: a regression anchor IS filed, it links the
    closed one, and it carries the identity label.

The listForRepo double now honours state and labels. It did not before, and that is
load-bearing: a double that ignores the filters lets a lookup keyed on open + finding satisfy a
scenario 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):

  • M17 — the lookup reverts to selecting open + finding. Expects A14, A15, A16, A17 RED;
    keepGreen A1, A2 — A1 has no prior anchor and A2's anchor still carries finding, so both
    are 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.
  • M18 — the closed-as-duplicate branch collapses into create. Expects A16 RED, keepGreen A14, A17.
  • M19 — a regression anchor stops naming the anchor it regressed from. Expects A17 RED,
    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):

anchor present in extracted source: yes
CLEAN failures: 0
M17 failures by scenario: A14, A15, A16, A17
[A14] NO second anchor is filed for a key that already has an open one, got 1: ["Queue-flake anchor: src/dev-plugin-security-enforcement-warning.test.ts"]
[A14] the graded anchor #14648 is the one refreshed, got []
[A15] the surviving OPEN anchor #14648 is refreshed -- not the closed duplicate, got []
[A16] no anchor is filed into a closed-as-duplicate chain, got 1
[A17] the new anchor LINKS the closed one, got: ...

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/main and byte-identical to the pre-merge derivation), plus this
script's own --self-test, which the derived list does not name. Every exit code captured by
redirect-then-read, never through a pipe.

33 of 34 green, 1 NOT MEASURED. No reds.

node scripts/check-merge-queue-triage-outcome.mjs
check-merge-queue-triage-outcome: OK (90 assertions over 24 scenarios, driving the
33918-char script extracted from .github/workflows/merge-queue-triage.yml against real
captured logs).
node scripts/check-merge-queue-triage-outcome.mjs --self-test
check-merge-queue-triage-outcome --self-test: 126 assertions, 23 mutations of the shipped
script each driven to red.
node scripts/check-self-test-wired.mjs
check-self-test-wired: every one of the 164 script(s) CI runs that ship a `--self-test` has
that self-test run by CI.
pnpm check:workflow-status-functions
check-workflow-status-functions: OK (scanned 30 workflow file(s), 53 job(s), 26 job-level
if: expression(s); 10 read needs.*.outputs.*, all naming a status function).
pnpm check:pm-dispatch-gates
dispatch-gates self-test: 1240 cases pass.
node scripts/check-self-test-workflow-commands.mjs
check-self-test-workflow-commands: no self-test CI runs prints a line the Actions runner
would parse as a workflow command.
node scripts/check-nul-bytes.mjs
check-nul-bytes: OK (scanned 8042 text file(s); no raw ASCII control bytes).
plus a direct sweep of the two edited files for control bytes -- no hits.

NOT MEASURED (not a red):node scripts/check-test-completeness.mjs exits 3 with
PREREQUISITE NOT MET -- it grades a saved turbo run test log and the derived family names it
with 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:

node scripts/pm/check-governed-merges.mjs --test .github/workflows/merge-queue-triage.yml scripts/check-merge-queue-triage-outcome.mjs
governed-surface predicate: 0 of 2 path(s) hit the register (5 surfaces, repo-agnostic).
NOT governed -- ordinary queue landing applies to a PR with exactly this file list.

skip-changeset: the diff publishes nothing from any package — one workflow and the gate script
that 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.

git diff --stat dbf115284 HEAD -- .github/workflows/merge-queue-triage.yml scripts/check-merge-queue-triage-outcome.mjs
.github/workflows/merge-queue-triage.yml | 153 +++++++++++++++----
scripts/check-merge-queue-triage-outcome.mjs | 214 +++++++++++++++++++++++++--
2 files changed, 330 insertions(+), 37 deletions(-)

🤖 Generated with Claude Code

https://claude.ai/code/session_01WLJQhde67SeTccsmnBVarV


Generated by Claude Code

…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
@claudeclaudeBot added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Sep 2, 2026
@baozhoutao
baozhoutao marked this pull request as ready for review September 2, 2026 23:25
@baozhoutao
baozhoutao added this pull request to the merge queueSep 2, 2026
Merged via the queue into main with commit 96b7723Sep 3, 2026
36 checks passed
@baozhoutao
baozhoutao deleted the claude/issue-14682-queue-flake-anchor-lookup branch September 3, 2026 00:49
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ci/cdsize/mskip-changesetPR has no user-facing published change; bypasses the changeset gate

Projects

None yet

2 participants

@baozhoutao@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(ci): key the queue-flake anchor lookup on the file identity, not on finding + open - #14740

Merged
baozhoutao merged 2 commits into
mainfrom
claude/issue-14682-queue-flake-anchor-lookup
Sep 3, 2026
Merged

fix(ci): key the queue-flake anchor lookup on the file identity, not on finding + open#14740
baozhoutao merged 2 commits into
mainfrom
claude/issue-14682-queue-flake-anchor-lookup

Conversation

@claude

@claudeclaudeBot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

Fixes#14682

The mechanism, as found on origin/main (4d0d9445a)

.github/workflows/merge-queue-triage.yml:624-627 looked the anchor up with

constres=awaitgithub.rest.issues.listForRepo({
owner, repo,state: 'open',labels: ANCHOR_LABEL,// ANCHOR_LABEL = 'finding' (:552)sort: 'created',direction: 'desc',per_page: 100,page: p,});

and matched on the body marker queue-signature-anchor:KEY or the exact title
Queue-flake anchor: KEY (:632-633). The counting was never involved and is untouched here.

finding is a triage-state label: first-touch grading takes it off by definition. So the
moment 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:

issuestatestate_reasonlabelsbody marker
#14648opennulltests, priority:p1, pm:dispatched, domain:clipresent
#14679closedduplicatefindingpresent

#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: finding is gone. That
is 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:

constANCHOR_QUERIES=[{state: 'all',labels: ANCHOR_IDENTITY_LABEL,firstMatchWins: false,adopts: false},{state: 'open',firstMatchWins: true,adopts: true},];

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 scanComplete argument (an unestablished absence still
refuses 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.addLabels so pass 1 owns them from the next ejection on. A
later 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

  1. an OPEN anchor for the key (any labels) — refresh it; the oldest open one wins, because
    that is the issue the duplicates were closed against.
  2. the lookup could not be completed — announce and create nothing (unchanged; a duplicate
    anchor is worse than a late one).
  3. no open anchor and the newest closed one has 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.
  4. no open anchor and the newest closed one was closed on its merits — the flake was answered
    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).
  5. nothing at all — create, now labelled 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 no duplicate_of (the only related key on the payload is
sub_issues_summary). Where the canonical shares the key it is already found as an OPEN anchor by
branch 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:

  • A14 — GRADED anchor: an OPEN anchor whose finding was taken off is REFRESHED, not
    duplicated. 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 addLabels call.
  • A15 — the newest anchor is closed as a duplicate and an older one is open: the OLDER OPEN
    one is refreshed. The shape the incident actually reached.
  • A16 — every anchor for the key is closed as a duplicate: nothing is filed, the run is
    annotated, and the victim's comment names the closed anchor.
  • A17 — the only anchor was closed on its merits: a regression anchor IS filed, it links the
    closed one, and it carries the identity label.

The listForRepo double now honours state and labels. It did not before, and that is
load-bearing: a double that ignores the filters lets a lookup keyed on open + finding satisfy a
scenario 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):

  • M17 — the lookup reverts to selecting open + finding. Expects A14, A15, A16, A17 RED;
    keepGreen A1, A2 — A1 has no prior anchor and A2's anchor still carries finding, so both
    are 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.
  • M18 — the closed-as-duplicate branch collapses into create. Expects A16 RED, keepGreen A14, A17.
  • M19 — a regression anchor stops naming the anchor it regressed from. Expects A17 RED,
    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):

anchor present in extracted source: yes
CLEAN failures: 0
M17 failures by scenario: A14, A15, A16, A17
[A14] NO second anchor is filed for a key that already has an open one, got 1: ["Queue-flake anchor: src/dev-plugin-security-enforcement-warning.test.ts"]
[A14] the graded anchor #14648 is the one refreshed, got []
[A15] the surviving OPEN anchor #14648 is refreshed -- not the closed duplicate, got []
[A16] no anchor is filed into a closed-as-duplicate chain, got 1
[A17] the new anchor LINKS the closed one, got: ...

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/main and byte-identical to the pre-merge derivation), plus this
script's own --self-test, which the derived list does not name. Every exit code captured by
redirect-then-read, never through a pipe.

33 of 34 green, 1 NOT MEASURED. No reds.

node scripts/check-merge-queue-triage-outcome.mjs
check-merge-queue-triage-outcome: OK (90 assertions over 24 scenarios, driving the
33918-char script extracted from .github/workflows/merge-queue-triage.yml against real
captured logs).
node scripts/check-merge-queue-triage-outcome.mjs --self-test
check-merge-queue-triage-outcome --self-test: 126 assertions, 23 mutations of the shipped
script each driven to red.
node scripts/check-self-test-wired.mjs
check-self-test-wired: every one of the 164 script(s) CI runs that ship a `--self-test` has
that self-test run by CI.
pnpm check:workflow-status-functions
check-workflow-status-functions: OK (scanned 30 workflow file(s), 53 job(s), 26 job-level
if: expression(s); 10 read needs.*.outputs.*, all naming a status function).
pnpm check:pm-dispatch-gates
dispatch-gates self-test: 1240 cases pass.
node scripts/check-self-test-workflow-commands.mjs
check-self-test-workflow-commands: no self-test CI runs prints a line the Actions runner
would parse as a workflow command.
node scripts/check-nul-bytes.mjs
check-nul-bytes: OK (scanned 8042 text file(s); no raw ASCII control bytes).
plus a direct sweep of the two edited files for control bytes -- no hits.

NOT MEASURED (not a red):node scripts/check-test-completeness.mjs exits 3 with
PREREQUISITE NOT MET -- it grades a saved turbo run test log and the derived family names it
with 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:

node scripts/pm/check-governed-merges.mjs --test .github/workflows/merge-queue-triage.yml scripts/check-merge-queue-triage-outcome.mjs
governed-surface predicate: 0 of 2 path(s) hit the register (5 surfaces, repo-agnostic).
NOT governed -- ordinary queue landing applies to a PR with exactly this file list.

skip-changeset: the diff publishes nothing from any package — one workflow and the gate script
that 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.

git diff --stat dbf115284 HEAD -- .github/workflows/merge-queue-triage.yml scripts/check-merge-queue-triage-outcome.mjs
.github/workflows/merge-queue-triage.yml | 153 +++++++++++++++----
scripts/check-merge-queue-triage-outcome.mjs | 214 +++++++++++++++++++++++++--
2 files changed, 330 insertions(+), 37 deletions(-)

🤖 Generated with Claude Code

https://claude.ai/code/session_01WLJQhde67SeTccsmnBVarV


Generated by Claude Code

…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
@claudeclaudeBot added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Sep 2, 2026
@baozhoutao
baozhoutao marked this pull request as ready for review September 2, 2026 23:25
@baozhoutao
baozhoutao added this pull request to the merge queueSep 2, 2026
Merged via the queue into main with commit 96b7723Sep 3, 2026
36 checks passed
@baozhoutao
baozhoutao deleted the claude/issue-14682-queue-flake-anchor-lookup branch September 3, 2026 00:49
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ci/cdsize/mskip-changesetPR has no user-facing published change; bypasses the changeset gate

Projects

None yet

2 participants

@baozhoutao@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(ci): key the queue-flake anchor lookup on the file identity, not on finding + open - #14740

Merged
baozhoutao merged 2 commits into
mainfrom
claude/issue-14682-queue-flake-anchor-lookup
Sep 3, 2026
Merged

fix(ci): key the queue-flake anchor lookup on the file identity, not on finding + open#14740
baozhoutao merged 2 commits into
mainfrom
claude/issue-14682-queue-flake-anchor-lookup

Conversation

@claude

@claudeclaudeBot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

Fixes#14682

The mechanism, as found on origin/main (4d0d9445a)

.github/workflows/merge-queue-triage.yml:624-627 looked the anchor up with

constres=awaitgithub.rest.issues.listForRepo({
owner, repo,state: 'open',labels: ANCHOR_LABEL,// ANCHOR_LABEL = 'finding' (:552)sort: 'created',direction: 'desc',per_page: 100,page: p,});

and matched on the body marker queue-signature-anchor:KEY or the exact title
Queue-flake anchor: KEY (:632-633). The counting was never involved and is untouched here.

finding is a triage-state label: first-touch grading takes it off by definition. So the
moment 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:

issuestatestate_reasonlabelsbody marker
#14648opennulltests, priority:p1, pm:dispatched, domain:clipresent
#14679closedduplicatefindingpresent

#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: finding is gone. That
is 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:

constANCHOR_QUERIES=[{state: 'all',labels: ANCHOR_IDENTITY_LABEL,firstMatchWins: false,adopts: false},{state: 'open',firstMatchWins: true,adopts: true},];

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 scanComplete argument (an unestablished absence still
refuses 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.addLabels so pass 1 owns them from the next ejection on. A
later 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

  1. an OPEN anchor for the key (any labels) — refresh it; the oldest open one wins, because
    that is the issue the duplicates were closed against.
  2. the lookup could not be completed — announce and create nothing (unchanged; a duplicate
    anchor is worse than a late one).
  3. no open anchor and the newest closed one has 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.
  4. no open anchor and the newest closed one was closed on its merits — the flake was answered
    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).
  5. nothing at all — create, now labelled 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 no duplicate_of (the only related key on the payload is
sub_issues_summary). Where the canonical shares the key it is already found as an OPEN anchor by
branch 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:

  • A14 — GRADED anchor: an OPEN anchor whose finding was taken off is REFRESHED, not
    duplicated. 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 addLabels call.
  • A15 — the newest anchor is closed as a duplicate and an older one is open: the OLDER OPEN
    one is refreshed. The shape the incident actually reached.
  • A16 — every anchor for the key is closed as a duplicate: nothing is filed, the run is
    annotated, and the victim's comment names the closed anchor.
  • A17 — the only anchor was closed on its merits: a regression anchor IS filed, it links the
    closed one, and it carries the identity label.

The listForRepo double now honours state and labels. It did not before, and that is
load-bearing: a double that ignores the filters lets a lookup keyed on open + finding satisfy a
scenario 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):

  • M17 — the lookup reverts to selecting open + finding. Expects A14, A15, A16, A17 RED;
    keepGreen A1, A2 — A1 has no prior anchor and A2's anchor still carries finding, so both
    are 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.
  • M18 — the closed-as-duplicate branch collapses into create. Expects A16 RED, keepGreen A14, A17.
  • M19 — a regression anchor stops naming the anchor it regressed from. Expects A17 RED,
    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):

anchor present in extracted source: yes
CLEAN failures: 0
M17 failures by scenario: A14, A15, A16, A17
[A14] NO second anchor is filed for a key that already has an open one, got 1: ["Queue-flake anchor: src/dev-plugin-security-enforcement-warning.test.ts"]
[A14] the graded anchor #14648 is the one refreshed, got []
[A15] the surviving OPEN anchor #14648 is refreshed -- not the closed duplicate, got []
[A16] no anchor is filed into a closed-as-duplicate chain, got 1
[A17] the new anchor LINKS the closed one, got: ...

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/main and byte-identical to the pre-merge derivation), plus this
script's own --self-test, which the derived list does not name. Every exit code captured by
redirect-then-read, never through a pipe.

33 of 34 green, 1 NOT MEASURED. No reds.

node scripts/check-merge-queue-triage-outcome.mjs
check-merge-queue-triage-outcome: OK (90 assertions over 24 scenarios, driving the
33918-char script extracted from .github/workflows/merge-queue-triage.yml against real
captured logs).
node scripts/check-merge-queue-triage-outcome.mjs --self-test
check-merge-queue-triage-outcome --self-test: 126 assertions, 23 mutations of the shipped
script each driven to red.
node scripts/check-self-test-wired.mjs
check-self-test-wired: every one of the 164 script(s) CI runs that ship a `--self-test` has
that self-test run by CI.
pnpm check:workflow-status-functions
check-workflow-status-functions: OK (scanned 30 workflow file(s), 53 job(s), 26 job-level
if: expression(s); 10 read needs.*.outputs.*, all naming a status function).
pnpm check:pm-dispatch-gates
dispatch-gates self-test: 1240 cases pass.
node scripts/check-self-test-workflow-commands.mjs
check-self-test-workflow-commands: no self-test CI runs prints a line the Actions runner
would parse as a workflow command.
node scripts/check-nul-bytes.mjs
check-nul-bytes: OK (scanned 8042 text file(s); no raw ASCII control bytes).
plus a direct sweep of the two edited files for control bytes -- no hits.

NOT MEASURED (not a red):node scripts/check-test-completeness.mjs exits 3 with
PREREQUISITE NOT MET -- it grades a saved turbo run test log and the derived family names it
with 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:

node scripts/pm/check-governed-merges.mjs --test .github/workflows/merge-queue-triage.yml scripts/check-merge-queue-triage-outcome.mjs
governed-surface predicate: 0 of 2 path(s) hit the register (5 surfaces, repo-agnostic).
NOT governed -- ordinary queue landing applies to a PR with exactly this file list.

skip-changeset: the diff publishes nothing from any package — one workflow and the gate script
that 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.

git diff --stat dbf115284 HEAD -- .github/workflows/merge-queue-triage.yml scripts/check-merge-queue-triage-outcome.mjs
.github/workflows/merge-queue-triage.yml | 153 +++++++++++++++----
scripts/check-merge-queue-triage-outcome.mjs | 214 +++++++++++++++++++++++++--
2 files changed, 330 insertions(+), 37 deletions(-)

🤖 Generated with Claude Code

https://claude.ai/code/session_01WLJQhde67SeTccsmnBVarV


Generated by Claude Code

…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
@claudeclaudeBot added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Sep 2, 2026
@baozhoutao
baozhoutao marked this pull request as ready for review September 2, 2026 23:25
@baozhoutao
baozhoutao added this pull request to the merge queueSep 2, 2026
Merged via the queue into main with commit 96b7723Sep 3, 2026
36 checks passed
@baozhoutao
baozhoutao deleted the claude/issue-14682-queue-flake-anchor-lookup branch September 3, 2026 00:49
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ci/cdsize/mskip-changesetPR has no user-facing published change; bypasses the changeset gate

Projects

None yet

2 participants

@baozhoutao@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

fix(ci): key the queue-flake anchor lookup on the file identity, not on finding + open - #14740

Merged
baozhoutao merged 2 commits into
mainfrom
claude/issue-14682-queue-flake-anchor-lookup
Sep 3, 2026
Merged

fix(ci): key the queue-flake anchor lookup on the file identity, not on finding + open#14740
baozhoutao merged 2 commits into
mainfrom
claude/issue-14682-queue-flake-anchor-lookup

Conversation

@claude

@claudeclaudeBot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

Fixes#14682

The mechanism, as found on origin/main (4d0d9445a)

.github/workflows/merge-queue-triage.yml:624-627 looked the anchor up with

constres=awaitgithub.rest.issues.listForRepo({
owner, repo,state: 'open',labels: ANCHOR_LABEL,// ANCHOR_LABEL = 'finding' (:552)sort: 'created',direction: 'desc',per_page: 100,page: p,});

and matched on the body marker queue-signature-anchor:KEY or the exact title
Queue-flake anchor: KEY (:632-633). The counting was never involved and is untouched here.

finding is a triage-state label: first-touch grading takes it off by definition. So the
moment 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:

issuestatestate_reasonlabelsbody marker
#14648opennulltests, priority:p1, pm:dispatched, domain:clipresent
#14679closedduplicatefindingpresent

#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: finding is gone. That
is 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:

constANCHOR_QUERIES=[{state: 'all',labels: ANCHOR_IDENTITY_LABEL,firstMatchWins: false,adopts: false},{state: 'open',firstMatchWins: true,adopts: true},];

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 scanComplete argument (an unestablished absence still
refuses 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.addLabels so pass 1 owns them from the next ejection on. A
later 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

  1. an OPEN anchor for the key (any labels) — refresh it; the oldest open one wins, because
    that is the issue the duplicates were closed against.
  2. the lookup could not be completed — announce and create nothing (unchanged; a duplicate
    anchor is worse than a late one).
  3. no open anchor and the newest closed one has 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.
  4. no open anchor and the newest closed one was closed on its merits — the flake was answered
    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).
  5. nothing at all — create, now labelled 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 no duplicate_of (the only related key on the payload is
sub_issues_summary). Where the canonical shares the key it is already found as an OPEN anchor by
branch 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:

  • A14 — GRADED anchor: an OPEN anchor whose finding was taken off is REFRESHED, not
    duplicated. 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 addLabels call.
  • A15 — the newest anchor is closed as a duplicate and an older one is open: the OLDER OPEN
    one is refreshed. The shape the incident actually reached.
  • A16 — every anchor for the key is closed as a duplicate: nothing is filed, the run is
    annotated, and the victim's comment names the closed anchor.
  • A17 — the only anchor was closed on its merits: a regression anchor IS filed, it links the
    closed one, and it carries the identity label.

The listForRepo double now honours state and labels. It did not before, and that is
load-bearing: a double that ignores the filters lets a lookup keyed on open + finding satisfy a
scenario 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):

  • M17 — the lookup reverts to selecting open + finding. Expects A14, A15, A16, A17 RED;
    keepGreen A1, A2 — A1 has no prior anchor and A2's anchor still carries finding, so both
    are 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.
  • M18 — the closed-as-duplicate branch collapses into create. Expects A16 RED, keepGreen A14, A17.
  • M19 — a regression anchor stops naming the anchor it regressed from. Expects A17 RED,
    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):

anchor present in extracted source: yes
CLEAN failures: 0
M17 failures by scenario: A14, A15, A16, A17
[A14] NO second anchor is filed for a key that already has an open one, got 1: ["Queue-flake anchor: src/dev-plugin-security-enforcement-warning.test.ts"]
[A14] the graded anchor #14648 is the one refreshed, got []
[A15] the surviving OPEN anchor #14648 is refreshed -- not the closed duplicate, got []
[A16] no anchor is filed into a closed-as-duplicate chain, got 1
[A17] the new anchor LINKS the closed one, got: ...

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/main and byte-identical to the pre-merge derivation), plus this
script's own --self-test, which the derived list does not name. Every exit code captured by
redirect-then-read, never through a pipe.

33 of 34 green, 1 NOT MEASURED. No reds.

node scripts/check-merge-queue-triage-outcome.mjs
check-merge-queue-triage-outcome: OK (90 assertions over 24 scenarios, driving the
33918-char script extracted from .github/workflows/merge-queue-triage.yml against real
captured logs).
node scripts/check-merge-queue-triage-outcome.mjs --self-test
check-merge-queue-triage-outcome --self-test: 126 assertions, 23 mutations of the shipped
script each driven to red.
node scripts/check-self-test-wired.mjs
check-self-test-wired: every one of the 164 script(s) CI runs that ship a `--self-test` has
that self-test run by CI.
pnpm check:workflow-status-functions
check-workflow-status-functions: OK (scanned 30 workflow file(s), 53 job(s), 26 job-level
if: expression(s); 10 read needs.*.outputs.*, all naming a status function).
pnpm check:pm-dispatch-gates
dispatch-gates self-test: 1240 cases pass.
node scripts/check-self-test-workflow-commands.mjs
check-self-test-workflow-commands: no self-test CI runs prints a line the Actions runner
would parse as a workflow command.
node scripts/check-nul-bytes.mjs
check-nul-bytes: OK (scanned 8042 text file(s); no raw ASCII control bytes).
plus a direct sweep of the two edited files for control bytes -- no hits.

NOT MEASURED (not a red):node scripts/check-test-completeness.mjs exits 3 with
PREREQUISITE NOT MET -- it grades a saved turbo run test log and the derived family names it
with 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:

node scripts/pm/check-governed-merges.mjs --test .github/workflows/merge-queue-triage.yml scripts/check-merge-queue-triage-outcome.mjs
governed-surface predicate: 0 of 2 path(s) hit the register (5 surfaces, repo-agnostic).
NOT governed -- ordinary queue landing applies to a PR with exactly this file list.

skip-changeset: the diff publishes nothing from any package — one workflow and the gate script
that 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.

git diff --stat dbf115284 HEAD -- .github/workflows/merge-queue-triage.yml scripts/check-merge-queue-triage-outcome.mjs
.github/workflows/merge-queue-triage.yml | 153 +++++++++++++++----
scripts/check-merge-queue-triage-outcome.mjs | 214 +++++++++++++++++++++++++--
2 files changed, 330 insertions(+), 37 deletions(-)

🤖 Generated with Claude Code

https://claude.ai/code/session_01WLJQhde67SeTccsmnBVarV


Generated by Claude Code

…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
@claudeclaudeBot added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Sep 2, 2026
@baozhoutao
baozhoutao marked this pull request as ready for review September 2, 2026 23:25
@baozhoutao
baozhoutao added this pull request to the merge queueSep 2, 2026
Merged via the queue into main with commit 96b7723Sep 3, 2026
36 checks passed
@baozhoutao
baozhoutao deleted the claude/issue-14682-queue-flake-anchor-lookup branch September 3, 2026 00:49
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ci/cdsize/mskip-changesetPR has no user-facing published change; bypasses the changeset gate

Projects

None yet

2 participants

@baozhoutao@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(ci): key the queue-flake anchor lookup on the file identity, not on finding + open - #14740

Merged
baozhoutao merged 2 commits into
mainfrom
claude/issue-14682-queue-flake-anchor-lookup
Sep 3, 2026
Merged

fix(ci): key the queue-flake anchor lookup on the file identity, not on finding + open#14740
baozhoutao merged 2 commits into
mainfrom
claude/issue-14682-queue-flake-anchor-lookup

Conversation

@claude

@claudeclaudeBot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

Fixes#14682

The mechanism, as found on origin/main (4d0d9445a)

.github/workflows/merge-queue-triage.yml:624-627 looked the anchor up with

constres=awaitgithub.rest.issues.listForRepo({
owner, repo,state: 'open',labels: ANCHOR_LABEL,// ANCHOR_LABEL = 'finding' (:552)sort: 'created',direction: 'desc',per_page: 100,page: p,});

and matched on the body marker queue-signature-anchor:KEY or the exact title
Queue-flake anchor: KEY (:632-633). The counting was never involved and is untouched here.

finding is a triage-state label: first-touch grading takes it off by definition. So the
moment 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:

issuestatestate_reasonlabelsbody marker
#14648opennulltests, priority:p1, pm:dispatched, domain:clipresent
#14679closedduplicatefindingpresent

#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: finding is gone. That
is 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:

constANCHOR_QUERIES=[{state: 'all',labels: ANCHOR_IDENTITY_LABEL,firstMatchWins: false,adopts: false},{state: 'open',firstMatchWins: true,adopts: true},];

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 scanComplete argument (an unestablished absence still
refuses 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.addLabels so pass 1 owns them from the next ejection on. A
later 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

  1. an OPEN anchor for the key (any labels) — refresh it; the oldest open one wins, because
    that is the issue the duplicates were closed against.
  2. the lookup could not be completed — announce and create nothing (unchanged; a duplicate
    anchor is worse than a late one).
  3. no open anchor and the newest closed one has 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.
  4. no open anchor and the newest closed one was closed on its merits — the flake was answered
    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).
  5. nothing at all — create, now labelled 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 no duplicate_of (the only related key on the payload is
sub_issues_summary). Where the canonical shares the key it is already found as an OPEN anchor by
branch 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:

  • A14 — GRADED anchor: an OPEN anchor whose finding was taken off is REFRESHED, not
    duplicated. 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 addLabels call.
  • A15 — the newest anchor is closed as a duplicate and an older one is open: the OLDER OPEN
    one is refreshed. The shape the incident actually reached.
  • A16 — every anchor for the key is closed as a duplicate: nothing is filed, the run is
    annotated, and the victim's comment names the closed anchor.
  • A17 — the only anchor was closed on its merits: a regression anchor IS filed, it links the
    closed one, and it carries the identity label.

The listForRepo double now honours state and labels. It did not before, and that is
load-bearing: a double that ignores the filters lets a lookup keyed on open + finding satisfy a
scenario 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):

  • M17 — the lookup reverts to selecting open + finding. Expects A14, A15, A16, A17 RED;
    keepGreen A1, A2 — A1 has no prior anchor and A2's anchor still carries finding, so both
    are 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.
  • M18 — the closed-as-duplicate branch collapses into create. Expects A16 RED, keepGreen A14, A17.
  • M19 — a regression anchor stops naming the anchor it regressed from. Expects A17 RED,
    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):

anchor present in extracted source: yes
CLEAN failures: 0
M17 failures by scenario: A14, A15, A16, A17
[A14] NO second anchor is filed for a key that already has an open one, got 1: ["Queue-flake anchor: src/dev-plugin-security-enforcement-warning.test.ts"]
[A14] the graded anchor #14648 is the one refreshed, got []
[A15] the surviving OPEN anchor #14648 is refreshed -- not the closed duplicate, got []
[A16] no anchor is filed into a closed-as-duplicate chain, got 1
[A17] the new anchor LINKS the closed one, got: ...

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/main and byte-identical to the pre-merge derivation), plus this
script's own --self-test, which the derived list does not name. Every exit code captured by
redirect-then-read, never through a pipe.

33 of 34 green, 1 NOT MEASURED. No reds.

node scripts/check-merge-queue-triage-outcome.mjs
check-merge-queue-triage-outcome: OK (90 assertions over 24 scenarios, driving the
33918-char script extracted from .github/workflows/merge-queue-triage.yml against real
captured logs).
node scripts/check-merge-queue-triage-outcome.mjs --self-test
check-merge-queue-triage-outcome --self-test: 126 assertions, 23 mutations of the shipped
script each driven to red.
node scripts/check-self-test-wired.mjs
check-self-test-wired: every one of the 164 script(s) CI runs that ship a `--self-test` has
that self-test run by CI.
pnpm check:workflow-status-functions
check-workflow-status-functions: OK (scanned 30 workflow file(s), 53 job(s), 26 job-level
if: expression(s); 10 read needs.*.outputs.*, all naming a status function).
pnpm check:pm-dispatch-gates
dispatch-gates self-test: 1240 cases pass.
node scripts/check-self-test-workflow-commands.mjs
check-self-test-workflow-commands: no self-test CI runs prints a line the Actions runner
would parse as a workflow command.
node scripts/check-nul-bytes.mjs
check-nul-bytes: OK (scanned 8042 text file(s); no raw ASCII control bytes).
plus a direct sweep of the two edited files for control bytes -- no hits.

NOT MEASURED (not a red):node scripts/check-test-completeness.mjs exits 3 with
PREREQUISITE NOT MET -- it grades a saved turbo run test log and the derived family names it
with 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:

node scripts/pm/check-governed-merges.mjs --test .github/workflows/merge-queue-triage.yml scripts/check-merge-queue-triage-outcome.mjs
governed-surface predicate: 0 of 2 path(s) hit the register (5 surfaces, repo-agnostic).
NOT governed -- ordinary queue landing applies to a PR with exactly this file list.

skip-changeset: the diff publishes nothing from any package — one workflow and the gate script
that 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.

git diff --stat dbf115284 HEAD -- .github/workflows/merge-queue-triage.yml scripts/check-merge-queue-triage-outcome.mjs
.github/workflows/merge-queue-triage.yml | 153 +++++++++++++++----
scripts/check-merge-queue-triage-outcome.mjs | 214 +++++++++++++++++++++++++--
2 files changed, 330 insertions(+), 37 deletions(-)

🤖 Generated with Claude Code

https://claude.ai/code/session_01WLJQhde67SeTccsmnBVarV


Generated by Claude Code

…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
@claudeclaudeBot added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Sep 2, 2026
@baozhoutao
baozhoutao marked this pull request as ready for review September 2, 2026 23:25
@baozhoutao
baozhoutao added this pull request to the merge queueSep 2, 2026
Merged via the queue into main with commit 96b7723Sep 3, 2026
36 checks passed
@baozhoutao
baozhoutao deleted the claude/issue-14682-queue-flake-anchor-lookup branch September 3, 2026 00:49
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ci/cdsize/mskip-changesetPR has no user-facing published change; bypasses the changeset gate

Projects

None yet

2 participants

@baozhoutao@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(ci): key the queue-flake anchor lookup on the file identity, not on finding + open - #14740

Merged
baozhoutao merged 2 commits into
mainfrom
claude/issue-14682-queue-flake-anchor-lookup
Sep 3, 2026
Merged

fix(ci): key the queue-flake anchor lookup on the file identity, not on finding + open#14740
baozhoutao merged 2 commits into
mainfrom
claude/issue-14682-queue-flake-anchor-lookup

Conversation

@claude

@claudeclaudeBot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

Fixes#14682

The mechanism, as found on origin/main (4d0d9445a)

.github/workflows/merge-queue-triage.yml:624-627 looked the anchor up with

constres=awaitgithub.rest.issues.listForRepo({
owner, repo,state: 'open',labels: ANCHOR_LABEL,// ANCHOR_LABEL = 'finding' (:552)sort: 'created',direction: 'desc',per_page: 100,page: p,});

and matched on the body marker queue-signature-anchor:KEY or the exact title
Queue-flake anchor: KEY (:632-633). The counting was never involved and is untouched here.

finding is a triage-state label: first-touch grading takes it off by definition. So the
moment 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:

issuestatestate_reasonlabelsbody marker
#14648opennulltests, priority:p1, pm:dispatched, domain:clipresent
#14679closedduplicatefindingpresent

#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: finding is gone. That
is 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:

constANCHOR_QUERIES=[{state: 'all',labels: ANCHOR_IDENTITY_LABEL,firstMatchWins: false,adopts: false},{state: 'open',firstMatchWins: true,adopts: true},];

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 scanComplete argument (an unestablished absence still
refuses 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.addLabels so pass 1 owns them from the next ejection on. A
later 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

  1. an OPEN anchor for the key (any labels) — refresh it; the oldest open one wins, because
    that is the issue the duplicates were closed against.
  2. the lookup could not be completed — announce and create nothing (unchanged; a duplicate
    anchor is worse than a late one).
  3. no open anchor and the newest closed one has 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.
  4. no open anchor and the newest closed one was closed on its merits — the flake was answered
    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).
  5. nothing at all — create, now labelled 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 no duplicate_of (the only related key on the payload is
sub_issues_summary). Where the canonical shares the key it is already found as an OPEN anchor by
branch 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:

  • A14 — GRADED anchor: an OPEN anchor whose finding was taken off is REFRESHED, not
    duplicated. 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 addLabels call.
  • A15 — the newest anchor is closed as a duplicate and an older one is open: the OLDER OPEN
    one is refreshed. The shape the incident actually reached.
  • A16 — every anchor for the key is closed as a duplicate: nothing is filed, the run is
    annotated, and the victim's comment names the closed anchor.
  • A17 — the only anchor was closed on its merits: a regression anchor IS filed, it links the
    closed one, and it carries the identity label.

The listForRepo double now honours state and labels. It did not before, and that is
load-bearing: a double that ignores the filters lets a lookup keyed on open + finding satisfy a
scenario 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):

  • M17 — the lookup reverts to selecting open + finding. Expects A14, A15, A16, A17 RED;
    keepGreen A1, A2 — A1 has no prior anchor and A2's anchor still carries finding, so both
    are 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.
  • M18 — the closed-as-duplicate branch collapses into create. Expects A16 RED, keepGreen A14, A17.
  • M19 — a regression anchor stops naming the anchor it regressed from. Expects A17 RED,
    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):

anchor present in extracted source: yes
CLEAN failures: 0
M17 failures by scenario: A14, A15, A16, A17
[A14] NO second anchor is filed for a key that already has an open one, got 1: ["Queue-flake anchor: src/dev-plugin-security-enforcement-warning.test.ts"]
[A14] the graded anchor #14648 is the one refreshed, got []
[A15] the surviving OPEN anchor #14648 is refreshed -- not the closed duplicate, got []
[A16] no anchor is filed into a closed-as-duplicate chain, got 1
[A17] the new anchor LINKS the closed one, got: ...

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/main and byte-identical to the pre-merge derivation), plus this
script's own --self-test, which the derived list does not name. Every exit code captured by
redirect-then-read, never through a pipe.

33 of 34 green, 1 NOT MEASURED. No reds.

node scripts/check-merge-queue-triage-outcome.mjs
check-merge-queue-triage-outcome: OK (90 assertions over 24 scenarios, driving the
33918-char script extracted from .github/workflows/merge-queue-triage.yml against real
captured logs).
node scripts/check-merge-queue-triage-outcome.mjs --self-test
check-merge-queue-triage-outcome --self-test: 126 assertions, 23 mutations of the shipped
script each driven to red.
node scripts/check-self-test-wired.mjs
check-self-test-wired: every one of the 164 script(s) CI runs that ship a `--self-test` has
that self-test run by CI.
pnpm check:workflow-status-functions
check-workflow-status-functions: OK (scanned 30 workflow file(s), 53 job(s), 26 job-level
if: expression(s); 10 read needs.*.outputs.*, all naming a status function).
pnpm check:pm-dispatch-gates
dispatch-gates self-test: 1240 cases pass.
node scripts/check-self-test-workflow-commands.mjs
check-self-test-workflow-commands: no self-test CI runs prints a line the Actions runner
would parse as a workflow command.
node scripts/check-nul-bytes.mjs
check-nul-bytes: OK (scanned 8042 text file(s); no raw ASCII control bytes).
plus a direct sweep of the two edited files for control bytes -- no hits.

NOT MEASURED (not a red):node scripts/check-test-completeness.mjs exits 3 with
PREREQUISITE NOT MET -- it grades a saved turbo run test log and the derived family names it
with 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:

node scripts/pm/check-governed-merges.mjs --test .github/workflows/merge-queue-triage.yml scripts/check-merge-queue-triage-outcome.mjs
governed-surface predicate: 0 of 2 path(s) hit the register (5 surfaces, repo-agnostic).
NOT governed -- ordinary queue landing applies to a PR with exactly this file list.

skip-changeset: the diff publishes nothing from any package — one workflow and the gate script
that 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.

git diff --stat dbf115284 HEAD -- .github/workflows/merge-queue-triage.yml scripts/check-merge-queue-triage-outcome.mjs
.github/workflows/merge-queue-triage.yml | 153 +++++++++++++++----
scripts/check-merge-queue-triage-outcome.mjs | 214 +++++++++++++++++++++++++--
2 files changed, 330 insertions(+), 37 deletions(-)

🤖 Generated with Claude Code

https://claude.ai/code/session_01WLJQhde67SeTccsmnBVarV


Generated by Claude Code

…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
@claudeclaudeBot added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Sep 2, 2026
@baozhoutao
baozhoutao marked this pull request as ready for review September 2, 2026 23:25
@baozhoutao
baozhoutao added this pull request to the merge queueSep 2, 2026
Merged via the queue into main with commit 96b7723Sep 3, 2026
36 checks passed
@baozhoutao
baozhoutao deleted the claude/issue-14682-queue-flake-anchor-lookup branch September 3, 2026 00:49
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ci/cdsize/mskip-changesetPR has no user-facing published change; bypasses the changeset gate

Projects

None yet

2 participants

@baozhoutao@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

fix(ci): key the queue-flake anchor lookup on the file identity, not on finding + open - #14740

Merged
baozhoutao merged 2 commits into
mainfrom
claude/issue-14682-queue-flake-anchor-lookup
Sep 3, 2026
Merged

fix(ci): key the queue-flake anchor lookup on the file identity, not on finding + open#14740
baozhoutao merged 2 commits into
mainfrom
claude/issue-14682-queue-flake-anchor-lookup

Conversation

@claude

@claudeclaudeBot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

Fixes#14682

The mechanism, as found on origin/main (4d0d9445a)

.github/workflows/merge-queue-triage.yml:624-627 looked the anchor up with

constres=awaitgithub.rest.issues.listForRepo({
owner, repo,state: 'open',labels: ANCHOR_LABEL,// ANCHOR_LABEL = 'finding' (:552)sort: 'created',direction: 'desc',per_page: 100,page: p,});

and matched on the body marker queue-signature-anchor:KEY or the exact title
Queue-flake anchor: KEY (:632-633). The counting was never involved and is untouched here.

finding is a triage-state label: first-touch grading takes it off by definition. So the
moment 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:

issuestatestate_reasonlabelsbody marker
#14648opennulltests, priority:p1, pm:dispatched, domain:clipresent
#14679closedduplicatefindingpresent

#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: finding is gone. That
is 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:

constANCHOR_QUERIES=[{state: 'all',labels: ANCHOR_IDENTITY_LABEL,firstMatchWins: false,adopts: false},{state: 'open',firstMatchWins: true,adopts: true},];

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 scanComplete argument (an unestablished absence still
refuses 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.addLabels so pass 1 owns them from the next ejection on. A
later 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

  1. an OPEN anchor for the key (any labels) — refresh it; the oldest open one wins, because
    that is the issue the duplicates were closed against.
  2. the lookup could not be completed — announce and create nothing (unchanged; a duplicate
    anchor is worse than a late one).
  3. no open anchor and the newest closed one has 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.
  4. no open anchor and the newest closed one was closed on its merits — the flake was answered
    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).
  5. nothing at all — create, now labelled 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 no duplicate_of (the only related key on the payload is
sub_issues_summary). Where the canonical shares the key it is already found as an OPEN anchor by
branch 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:

  • A14 — GRADED anchor: an OPEN anchor whose finding was taken off is REFRESHED, not
    duplicated. 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 addLabels call.
  • A15 — the newest anchor is closed as a duplicate and an older one is open: the OLDER OPEN
    one is refreshed. The shape the incident actually reached.
  • A16 — every anchor for the key is closed as a duplicate: nothing is filed, the run is
    annotated, and the victim's comment names the closed anchor.
  • A17 — the only anchor was closed on its merits: a regression anchor IS filed, it links the
    closed one, and it carries the identity label.

The listForRepo double now honours state and labels. It did not before, and that is
load-bearing: a double that ignores the filters lets a lookup keyed on open + finding satisfy a
scenario 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):

  • M17 — the lookup reverts to selecting open + finding. Expects A14, A15, A16, A17 RED;
    keepGreen A1, A2 — A1 has no prior anchor and A2's anchor still carries finding, so both
    are 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.
  • M18 — the closed-as-duplicate branch collapses into create. Expects A16 RED, keepGreen A14, A17.
  • M19 — a regression anchor stops naming the anchor it regressed from. Expects A17 RED,
    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):

anchor present in extracted source: yes
CLEAN failures: 0
M17 failures by scenario: A14, A15, A16, A17
[A14] NO second anchor is filed for a key that already has an open one, got 1: ["Queue-flake anchor: src/dev-plugin-security-enforcement-warning.test.ts"]
[A14] the graded anchor #14648 is the one refreshed, got []
[A15] the surviving OPEN anchor #14648 is refreshed -- not the closed duplicate, got []
[A16] no anchor is filed into a closed-as-duplicate chain, got 1
[A17] the new anchor LINKS the closed one, got: ...

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/main and byte-identical to the pre-merge derivation), plus this
script's own --self-test, which the derived list does not name. Every exit code captured by
redirect-then-read, never through a pipe.

33 of 34 green, 1 NOT MEASURED. No reds.

node scripts/check-merge-queue-triage-outcome.mjs
check-merge-queue-triage-outcome: OK (90 assertions over 24 scenarios, driving the
33918-char script extracted from .github/workflows/merge-queue-triage.yml against real
captured logs).
node scripts/check-merge-queue-triage-outcome.mjs --self-test
check-merge-queue-triage-outcome --self-test: 126 assertions, 23 mutations of the shipped
script each driven to red.
node scripts/check-self-test-wired.mjs
check-self-test-wired: every one of the 164 script(s) CI runs that ship a `--self-test` has
that self-test run by CI.
pnpm check:workflow-status-functions
check-workflow-status-functions: OK (scanned 30 workflow file(s), 53 job(s), 26 job-level
if: expression(s); 10 read needs.*.outputs.*, all naming a status function).
pnpm check:pm-dispatch-gates
dispatch-gates self-test: 1240 cases pass.
node scripts/check-self-test-workflow-commands.mjs
check-self-test-workflow-commands: no self-test CI runs prints a line the Actions runner
would parse as a workflow command.
node scripts/check-nul-bytes.mjs
check-nul-bytes: OK (scanned 8042 text file(s); no raw ASCII control bytes).
plus a direct sweep of the two edited files for control bytes -- no hits.

NOT MEASURED (not a red):node scripts/check-test-completeness.mjs exits 3 with
PREREQUISITE NOT MET -- it grades a saved turbo run test log and the derived family names it
with 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:

node scripts/pm/check-governed-merges.mjs --test .github/workflows/merge-queue-triage.yml scripts/check-merge-queue-triage-outcome.mjs
governed-surface predicate: 0 of 2 path(s) hit the register (5 surfaces, repo-agnostic).
NOT governed -- ordinary queue landing applies to a PR with exactly this file list.

skip-changeset: the diff publishes nothing from any package — one workflow and the gate script
that 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.

git diff --stat dbf115284 HEAD -- .github/workflows/merge-queue-triage.yml scripts/check-merge-queue-triage-outcome.mjs
.github/workflows/merge-queue-triage.yml | 153 +++++++++++++++----
scripts/check-merge-queue-triage-outcome.mjs | 214 +++++++++++++++++++++++++--
2 files changed, 330 insertions(+), 37 deletions(-)

🤖 Generated with Claude Code

https://claude.ai/code/session_01WLJQhde67SeTccsmnBVarV


Generated by Claude Code

…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
@claudeclaudeBot added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Sep 2, 2026
@baozhoutao
baozhoutao marked this pull request as ready for review September 2, 2026 23:25
@baozhoutao
baozhoutao added this pull request to the merge queueSep 2, 2026
Merged via the queue into main with commit 96b7723Sep 3, 2026
36 checks passed
@baozhoutao
baozhoutao deleted the claude/issue-14682-queue-flake-anchor-lookup branch September 3, 2026 00:49
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ci/cdsize/mskip-changesetPR has no user-facing published change; bypasses the changeset gate

Projects

None yet

2 participants

@baozhoutao@claude