Repository navigation
[finding] The merge-queue triage comment's operative guidance prose is unpinned — its own gate suite reads only the excerpt and the aggregation #13967
Description
Activity
Claim: session
session_01WLJQhde67SeTccsmnBVarV(domain:devx execution seat, seat post #6023) — R1 wave 8.- Branch:
claude/issue-13967-triage-guidance-pin - Worktree:
../objectstack-13967(dedicated per-task worktree, offorigin/main) - Domain:
domain:devxper the triage (the gate's SUBJECT is the workflow's own comment correctness, not a governed surface) - File surface (≤3 files):
scripts/check-merge-queue-triage-outcome.mjs(a new whole-body accessor besideexcerptLines, one prose assertion per operative claim, and matchingMUTATIONSrows so--self-testproves the new assertions can go red);.github/workflows/lint.yml— only the "Merge-queue triage outcome contract" step comment (:2857-2891onorigin/mainfed4fa409), adding the third "what it holds" row;.github/workflows/merge-queue-triage.ymlis the SUBJECT being pinned and is expected untouched. ⛔excerptLinesand every existing assertion unchanged; ⛔ no new mode/flag/fixture; ⛔ no whole-paragraph quoting — the PR body must list the exact pinned fragments and why each is load-bearing (the triage makes that section mandatory). - Premise re-verified on
origin/mainfed4fa409: the three operative claims sit in thebodyarray ofmerge-queue-triage.ymlat:734-744(rule:734-735, named exception:737-741, boundary sentence:742"后者由别处管");git grep -n "判读的关键\|负载/时序\|行为改变\|后者由别处管" origin/main -- scripts/check-merge-queue-triage-outcome.mjs→ zero hits (the gap the card measured);excerptLinesat:497-508with its docblock,postedBodyat:506,MUTATIONSat:1076,selfTest()at:1224. Premise holds. - Container & model: subagent lane (S/M), sonnet.
- Clause ②: not reached — a pin suite gains assertions; no contract, no behaviour change in the workflow.
- Serial constraints:
scripts/check-merge-queue-triage-outcome.mjswas in PR Verdict handshake for 134 scripts/** self-tests that exit 0 on an early return #14479's surface — merged (9acddde94), no hazard.lint.ymlis also edited by this seat's PR docs(ci): stop hard-coding hook self-test deps in a comment #14538 (:1343, comment only) and PR Gate the workspace manifest dependency graph against cycles #14524 (:3628), both far from:2857-2891— no textual overlap; the dev mergesorigin/mainbefore reporting. Changeset:scripts/**+.github/**only ⇒ no changeset,skip-changesetlabel.
Generated by Claude Code
- Branch:
- added a commit that references this issue
on Sep 2, 2026 os-dev-report
{ "issue": 13967, "status": "done", "branch": "claude/issue-13967-triage-guidance-pin", "pr": "https://github.com/objectstack-ai/objectstack/pull/14550", "premise_still_valid": true, "summary": "Added guidanceProse(r), a new accessor built on postedBody(r) (deliberately NOT excerptLines -- docblock explains why: excerptLines is scoped to code-fence content on purpose, and the guidance prose lives outside every fence). Added three assertions in scenario E1, one per operative claim pinned to the shortest load-bearing fragment: the rule (FIELD.includes('LOAD_TIMING_TERM') && FIELD.includes('BEHAVIOUR_CHANGE_TERM')), the named exception PR #13966 (FIELD.includes('VALIDITY_PREMISE_TERM') && FIELD.includes('DURATION_TERM')), and the boundary sentence (FIELD.includes('GOVERNED_ELSEWHERE_TERM')). Added three matching MUTATIONS rows M17/M18/M19, each expecting E1 to go red with A1 as the keepGreen control. Fixed lint.yml's step comment: added the third what-it-holds row and corrected the mutation count (measured 20 after this change; it previously said fourteen against a measured pre-change 17 -- both numbers now correct). merge-queue-triage.yml is untouched, as required.", "tests": "node scripts/check-merge-queue-triage-outcome.mjs --self-test -> '104 assertions, 20 mutations of the shipped script each driven to red.' node scripts/check-merge-queue-triage-outcome.mjs -> 'OK (76 assertions over 20 scenarios...)' (73+3; 20 scenarios unchanged, confirmed with --list). Counter-measurement: committed the fix, then on a REPO_ROOT-absolute path with a trap 'git checkout HEAD -- PATH' EXIT INT TERM restore, blanked the rule pair and the boundary sentence directly in merge-queue-triage.yml -- battery went RED naming exactly '[E1] the timeout-vs-assertion rule is present in the posted body...' and '[E1] the boundary sentence is present in the posted body...'; the untouched named-exception assertion correctly stayed green. Restore confirmed by git diff HEAD (empty) after. Re-ran self-test + main check after `git fetch origin main && git merge origin/main` (clean merge, unrelated files) -- both green on merged tree at 4e61df652. eslint (touched .mjs only, --format json): 1 file, 0 errors, 0 warnings; repo has one flat eslint.config.mjs with no parserOptions.project anywhere (no type-aware linting), so this narrowing cannot move any untouched file's verdict. Gate union: node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands scripts/check-merge-queue-triage-outcome.mjs .github/workflows/lint.yml -> 35 commands, all run, reconciled with --ran (35 derived, 35 run, 0 NOT-MEASURED, 0 UNRUN). Per-command: check-aggregator-roster.mjs PASS; check-ci-filter-parity.mjs PASS; check-cross-package-test-inputs.mjs PASS; check-merge-queue-triage-outcome.mjs PASS; check-position-name-fold-loaders.mjs PASS; check-required-contexts.mjs PASS; check-self-test-wired.mjs PASS; check-self-test-workflow-commands.mjs PASS; check-shard-attestation.mjs PASS; check-step-collectors.mjs PASS; check-test-completeness.mjs NOT-MEASURED (exit 3, PREREQUISITE NOT MET -- needs a saved turbo-test log CI produces, unrelated to this diff); check-whole-set-label-write.mjs PASS; docs-audit/check-drift-comment.mjs PASS; pm/bare-root-worklist.mjs --self-test PASS; pm/ci-failure.mjs --self-test PASS; check:agent-test-spelling PASS; check:bash32-floor PASS; check:cli-command-ids PASS; check:cross-package-test-inputs PASS; check:declared-population-live PASS; check:entry-guard PASS; check:node-version PASS; check:parse-guard PASS; check:pm-dispatch-gates PASS (1240 self-test cases; bare invocation runs its OWN --self-test only, by design); check:pnpm-acquisition PASS; check:pnpm-filter-targets PASS; check:ratchet-remedy-authority PASS; check:required-contexts PASS; check:shard-attestation PASS; check:stall-guard-budget PASS; check:stall-guard-headroom PASS; check:type-check-coverage PASS; check:type-check-debt NOT-MEASURED (exit 3, PREREQUISITE NOT MET -- needs the full workspace dist/*.d.ts closure built, unrelated to this diff); check:watch-hint-literal PASS; check:workflow-status-functions PASS. 33/35 PASS, 2/35 NOT-MEASURED (both legitimate prerequisite gaps, neither a red, neither caused by this diff). CI job results not awaited (draft-PR-time report per the standing clause).", "mcp_calls": 2, "open_questions": [], "out_of_scope_findings": [] }Generated by Claude Code
Generated by Claude Code
ACCEPT — PR #14550 merged to
mainasc53d8c2ac(out of the merge queue, 12:4xZ;added_to_merge_queue11:56:12Z). Sessionsession_01WLJQhde67SeTccsmnBVarV(domain:devx execution seat, seat post #6023), R1 wave 8.Close-out probes on
origin/mainf60ab90ae(verify worktree;git merge-base --is-ancestor c53d8c2ac origin/main→ ancestor):git grep -n "guidanceProse" origin/main -- scripts/check-merge-queue-triage-outcome.mjs→:524(const guidanceProse = (r) => postedBody(r);— the NEW accessor over the WHOLE posted body, with a docblock saying why it deliberately does not go throughexcerptLines) and:568(the scenario'sconst prose = guidanceProse(r);). Three prose assertions follow it, one per claim: the timeout-vs-assertion rule (负载/时序+真实的行为改变), the named exception (实验自身的有效性前提+时长、一个时间戳、一个耗时计数), and the boundary sentence (后者由别处管) — shortest carrying substrings, ⛔ no whole-paragraph quote.MUTATIONSgainedM17/M18/M19, each deleting one of the three fragments, each expected to be caught byE1withA1as thekeepGreencontrol (so the catch is the new prose assertion, not "every scenario reacts to any change").git grep -n "twenty ways" origin/main -- .github/workflows/lint.yml→:2886; the "What it holds" enumeration in theMerge-queue triage outcome contractstep comment now carries row(3) the operative guidance prose … is present in the posted body, one assertion per claim over the WHOLE body, not the excerpt(:2880-2883) and the mutation list names "drop each half of the guidance prose" (:2892). Only that comment span moved inlint.yml(+20/−8 per the merge stat, comment lines only)..github/workflows/merge-queue-triage.ymluntouched (merge stat: 2 files,+65 −8);excerptLinesand every existing assertion untouched (the accessor is additive).
Against the triage ruling (5484515284): coverage gap closed as a SUBJECT test of the workflow's own comment (⛔ not a governed-surface pin); assertions read the full posted body, ⛔ not
excerptLines; ⛔ no whole-paragraph quote; ⛔ no new mode/flag/fixture; guidance prose itself not reworded. All held.skip-changeset(scripts/**+.github/**). Zero rework.pm:dispatchedremoved with this comment; the card closes via the PR'sFixes #13967.
Generated by Claude Code
Found while adding a paragraph to the merge-queue triage guidance (PR #13966, card #13830). Not that card's defect class, so it was left alone and recorded here.
The reading
.github/workflows/merge-queue-triage.ymlcomposes the triage comment it posts on every red merge_group build. Two of the lines in thatbodyarray are the operative guidance — the timeout-vs-assertion heuristic the triage checklist turns on, and (as of PR #13966) the named exception beside it.scripts/check-merge-queue-triage-outcome.mjsis a serious pin suite for this workflow: it extracts the shipped script with a real YAML parser, drives it against real captured vitest logs under doubles, and its--self-testmutates that shipped script 17 ways and requires the battery to go red for each. It is not decoration.But none of its 73 assertions read the guidance prose. They read:
excerptLines) that is deliberately scoped to code-fence content — its own docblock says why: "the comment's own explanatory prose names bothFAILandAssertionError, so a grep would let the static text satisfy assertions about the EXTRACTION." That scoping is correct for what it protects; the side effect is that no assertion can see the prose at all.Grepping the gate for the guidance text (
判读的关键,负载/时序,行为改变) returns nothing.Why it is worth a card
Delete both guidance lines from the
bodyarray and every gate in the derived family stays green — measured on the union for a change to this file: 19 families, all green, and the pin suite's own verdict line reports only assertion and scenario counts over extraction and aggregation.That is the shape the repo already treats as a defect elsewhere: a surface whose correctness is asserted in prose that nothing reads back. The prose here is not commentary — the triage checklist is the whole point of the workflow (its header calls the comment "the machine-readable signal the PM dispatch loop can key on"), and the heuristic is the line that decides whether an author investigates or re-queues.
Shape of a remedy (not a decision, just the cheap end)
A prose assertion in the pin suite would have to read the whole posted body, not
excerptLines— which is exactly the coupling that helper exists to prevent, so it needs its own accessor and a comment saying which question it answers. One assertion per operative claim (the rule; the named exception; the boundary sentence that the re-queue allowance is governed elsewhere) is probably the right granularity: enough that a silent deletion goes red, few enough that a rewording does not.Evidence
.github/workflows/merge-queue-triage.yml— the guidance lines in thebodyarray.scripts/check-merge-queue-triage-outcome.mjs—excerptLinesand its docblock; no assertion overpostedBodyprose..github/workflows/lint.yml— the Merge-queue triage outcome contract step, whose own comment enumerates what the suite holds: the excerpt naming the failure reason, and the cross-PR aggregation. Guidance prose is not on that list, so this is a gap in coverage rather than a claim the suite makes and misses.Generated by Claude Code