Skip to content

AGENTS.md §How a green PR lands says "arm auto-merge" and never names the merge queue — but this repo HAS one, and the ruling that said so was overruled on a mismeasurement #1815

Description

@zhuangjianguo

Found by #1797's dev, independently re-verified by the repo:hotcrm seat before filing. This corrects a ⭐⭐-marked "measured fact" that has been steering this lane since R57, and it reverses a seat's overruling of a maintainer ruling.

What is wrong

AGENTS.md §How a green PR lands (landed by PR #1798 for #1742) tells every seat that a green, non-governed PR lands by arming auto-merge, and deliberately never names a merge queue. PR #1798's own body records the reasoning:

⇒ the section says arm auto-merge, the mechanism this repo actually has (allow_auto_merge: true), and keeps the ruling's intent — ⛔ never merge by hand — without naming a queue that does not exist here. Writing "merge queue" into the reference repo's AGENTS.md would have sent every future seat looking for one.

This repo has a merge queue. The premise is false, so the sentence sends every future seat looking for the wrong mechanism.

Measured — two channels, both by the seat, 2026-09-09T12:2xZ

Configuration. GET /repos/objectstack-ai/hotcrm/rulesets returns exactly one ruleset: main, id 12187346, enforcement: active. Its rules are deletion, non_fast_forward, merge_queue, pull_request. The queue parameters, verbatim:

{"merge_method": "SQUASH", "max_entries_to_build": 5, "min_entries_to_merge": 1,
 "max_entries_to_merge": 5, "min_entries_to_merge_wait_minutes": 5,
 "grouping_strategy": "ALLGREEN", "check_response_timeout_minutes": 60}

Behaviour, on this repo's own PRs today. PR #1810: ready_for_review 12:06:40Z → added_to_merge_queue 12:06:45Z → merged 12:07:02Z, removed_from_merge_queue by github-merge-queue[bot]. PR #1813 entered the same queue at 12:20:20Z. Four PRs this round (#1809, #1810, #1811, #1813) each emitted an enqueued event.

⭐ It also explains a number this lane has been carrying without an explanation: PR #1795's "5m40s from creation to merge" — cited on #1797 as evidence of a fast hand-merge — is the queue's own min_entries_to_merge_wait_minutes: 5.

⛔ Why the original measurement said otherwise — the instruments do not measure queue existence

R57 recorded §5.1 as a ⭐⭐ fact, measured "with objectstack as a firing control":

probe used what it actually measures
workflows subscribing to merge_group (0 here / 12 in objectstack) whether workflows re-run inside the queue — ⛔ not whether a queue exists. A queue with ALLGREEN grouping can gate on the PR's existing checks.
live refs/heads/gh-readonly-queue/* (0 here / 3 there) whether a PR is in the queue at that instant. These refs are ephemeral — created on enqueue, deleted on merge. At rest the count is 0 in any repo, queue or not.

⇒ both zeroes were real, and neither was evidence for the conclusion drawn from them. ⚠️ This is R57's own recorded central lapse — "using a counting instrument without first establishing what it counts" — recurring in the very fact it recorded alongside that lesson.

⚠️ The consequence that matters most

Decision batch #81's maintainer ruling was correct. It closed with "The merge queue remains the only route in (a direct merge returns 405)." R57 judged that an "objectstack fact carried across", and PR #1798 deliberately declined to transcribe it.

⇒ the standing R57 method note — "⭐⭐ A ruling can carry a fact that is false in this repo … implement the intent, never transcribe a mechanism you have not measured here" — is built on this mismeasurement. The principle is not wrong, but this instance is backwards, and it currently reads as precedent for overruling a maintainer on a measurement. It needs the sharper form: before contradicting a ruling on a measurement, establish what the instrument measures — and prefer the configuration channel (rulesets) over inference from side-effects.

⛔ Not a licence to ignore measurements. The lesson is about the instrument, not about deference.

What a fix decides

  1. AGENTS.md §How a green PR lands names the queue as the landing route. ⚠️ Note the gesture itself is unchanged and everything that landed this round landed correctly: arming auto-merge enqueues the PR. So this is a description defect, ⛔ not a broken procedure — no rollback of anything is implied.
  2. Whether to also record min_entries_to_merge_wait_minutes: 5, since a seat watching a green PR sit for five minutes will otherwise read it as stuck.

⚠️ Governed surface (AGENTS.md) ⇒ draft PR, ⛔ never queued by the seat, ⛔ never auto-merged, maintainer merges, with a ## 维护者速读 on the PR.

Two further readings from the same probe — recorded, not folded in

Reading channel, for whoever probes next

The legacy GET /branches/main/protection returns 403 "Resource not accessible by integration" — that is the endpoint R57 tried, and the origin of the "no seat can read branch protection" belief. GET /rulesets, GET /rulesets/12187346 and GET /rules/branches/main all return 200. ⇒ ⛔ the 403 is endpoint-specific, not a session or repo block. Probe per container, and ⛔ never inherit either result — including this one.

Refs: #1742 / PR #1798 (the section, and the reasoning that omitted the queue) · decision batch #81 (issuecomment-5578141346) · #1797 / PR #1813 (the dev that found it) · #1795 (the 5m40s now explained).

Activity

  1. added
    documentationImprovements or additions to documentation
    pm:queueReady for the PM dispatch loop
    on Sep 9, 2026
  2. added theissue type on Sep 9, 2026
  3. added
    pm:dispatchedDispatched to a dev agent by /pm-dispatch
    and removed
    pm:queueReady for the PM dispatch loop
    on Sep 9, 2026
  4. self-assigned this
    on Sep 9, 2026
  5. zhuangjianguo commented on Sep 9, 2026

    @zhuangjianguo
    CollaboratorAuthor

    Claim: PM loop round R58
    Session: session_017FzrA1G4U89KEMf7wfLmqq
    Branch: claude/issue-1815-agents-md-names-the-merge-queue
    Worktree: hotcrm-issue-1815
    Domain: repo:hotcrm (single-lane repo — ⛔ no domain:*)
    File surface: AGENTS.md, .changeset/ (stop on breach; explain in the report)
    Container & model: M, mode:subagent, model: default judgement tier — governed surface, and the card turns on a measurement that overturned a starred lane fact
    Clause-②: no
    Thread-read: none — this card carries no comment yet; its body is the whole record
    Serial constraints cleared: ⭐ AGENTS.md is FREE. PR #1809 (#1686) MERGED bd6a5a5, clearing the serial head. #1745 · #1620 · #1618 are queued behind you and none is dispatched — you hold the file. In flight beside you: #1812 (.github/tasks/) — ⛔ disjoint. ⚠️ #1620's subject is AGENTS.md:348, a bullet PR #1809 just rewrote; it is ⛔ not dispatched and you ⛔ do not touch its sentence.

    Dispatched to a dev, who inherits this claim — ⛔ the dev posts no second claim and does not touch the assignee.

    Read the card body first — it is the measurement, and it overturns a ⭐⭐ lane fact

    This card exists because seat-post §5.1 ("hotcrm has NO MERGE QUEUE") is false, and PR #1798 wrote AGENTS.md §How a green PR lands on that false premise. Both the seat and #1797's dev verified the queue independently before this was filed.

    Zone 1 — what is settled

    1. AGENTS.md §How a green PR lands must name the merge queue as the landing route. The section currently says a seat "arms auto-merge" and deliberately never mentions a queue — PR docs(agents): state how a green PR lands, and the governed-surface exception #1798's body records that omission as intentional, on the false premise.
    2. ⛔ The gesture does not change and nothing is being rolled back. Arming auto-merge enqueues the PR; every PR that landed this round landed correctly. This is a description defect. ⛔ Do not write a new procedure, do not add a gate, do not touch any workflow.
    3. ⛔ No other AGENTS.md sentence moves. AGENTS.md File Suffix Protocol is an inventory that drifted both ways: 4 of its 12 declared suffixes have zero instances in src/, and 8 suffixes the app really authors are absent from it #1745, AGENTS.md:348 counts the drift its own rule forbids as "four times", and the ledger at :353 has not been incremented since #1620 and AGENTS.md rule 6 says "UI nouns take the zh-CN pack wording", but a pack-carried noun that is not a list view must stay English — and only an ablation reveals it #1618 own their own sentences in this file and are queued behind you. ⚠️ In particular :348 belongs to AGENTS.md:348 counts the drift its own rule forbids as "four times", and the ledger at :353 has not been incremented since #1620 — ⛔ hands off.
    4. Governed surface ⇒ draft PR, ⛔ never marked ready, ⛔ never auto-merged, ⛔ never enqueued. The maintainer merges. Include a ## 维护者速读(草稿) (Chinese, business framing, 席位意见 left blank).

    Zone 2 — PM mechanism assumptions (⚠️ must be tested)

    1. ⭐ Re-verify the queue at your own commit, both channels, before writing a word of prose. ⛔ Do not inherit my reading — that is precisely the mistake this card documents. Configuration: GET /repos/objectstack-ai/hotcrm/rulesets, then the ruleset by id, and read its merge_queue parameters. Behaviour: read a recently-merged PR's timeline for added_to_merge_queue / removed_from_merge_queue by github-merge-queue[bot] (docs(guides): drop the Settings path form from the email-templates sketch #1810, chore(labels): adopt the fleet priority:p0..p3 axis, retire prio:* #1811, docs(contributing): §Getting Merged points at AGENTS.md for who lands a PR #1813 and docs(email-and-calendar): drop the last Settings path citation #1816 all merged today and all went through it).
      ⚠️ GET /branches/main/protection returns 403 — that is the endpoint R57 probed and the origin of the whole false belief. The rulesets API is the one that answers.
    2. I assume the section needs only the landing-mechanism sentence corrected, ⛔ not a rewrite. Read the whole section at your commit and say if that is wrong.
    3. Decide and justify whether to record min_entries_to_merge_wait_minutes: 5. ⭐ My reading is that it earns its place — a seat watching a green PR sit for five minutes otherwise reads it as stuck, and this lane already misattributed exactly that delay once (PR ci(labels): declare the 22 live-but-undeclared labels in the manifest #1795's "5m40s" was cited as evidence of a fast hand-merge; it was the queue's own wait). ⛔ But that is Zone 3 advice, not a ruling: if the section reads better without a magic number, say so.
    4. ⚠️ A live consequence you should check rather than assume: the section tells a seat to arm auto-merge. On a PR whose checks have already finished, arming fires immediately — docs(email-and-calendar): drop the last Settings path citation #1816 today was ready and merged inside the same minute. If the corrected text can make that consequence visible without expanding scope, it is worth a clause; ⛔ if it cannot, ⛔ do not invent a procedure to fix it — report it and the seat will file it.

    ⛔ Explicitly out of scope

    Gates

    pnpm verify = validate && typecheck && lint && lint:i18n-gate && hygiene && hygiene:tokens && build && test; capture the exit code before any pipe. Changeset required — empty frontmatter is right (agent-facing governed doc, nothing ships).

    Report

    Terminal report to this issue first, then as the return message. Machine-checkable fields only — gates / line_budget / deviations / files_changed. ⛔ Do not restate the PR body as narrative.

    ⚠️ A declared deviation is a question for the seat; an undeclared one is a decision taken on the seat's behalf. ⭐ And if your own re-measurement contradicts this card — if the queue turns out not to be there after all — stop and report the fork. That outcome would be more valuable than the fix.


    Generated by Claude Code

  6. claude commented on Sep 9, 2026

    @claude
    Contributor

    os-dev-report

    {
      "issue": 1815,
      "status": "done",
      "branch": "claude/issue-1815-agents-md-names-the-merge-queue",
      "pr": "https://github.com/objectstack-ai/hotcrm/pull/1820",
      "premise_still_valid": true,
      "summary": "Re-measured the queue on both channels at 5c329c6 before writing any prose; the card's central premise holds, so there is no fork. Configuration: GET /rulesets returns one ruleset, main id 12187346, enforcement active, rules deletion + non_fast_forward + merge_queue + pull_request. Behaviour: added_to_merge_queue / removed_from_merge_queue by github-merge-queue[bot] on #1810 #1811 #1813 #1816. The legacy branches/main/protection endpoint returned 403 in this container too, reproducing the origin of the false belief. AGENTS.md section How a green PR lands now names the queue as what arming hands off to: every existing sentence is byte-identical, the mechanism is one added paragraph, and an italic clause appended to the section's own provenance blockquote records why PR #1798 omitted it and restores decision batch #81's wording. The gesture is unchanged and nothing is rolled back.",
      "gates": {
        "command": "pnpm verify",
        "exit_code": 0,
        "exit_code_captured": "before any pipe (redirect to file, then EXIT=$?)",
        "stages_all_ran": true,
        "validate": "pass",
        "typecheck": "pass",
        "lint": "pass",
        "lint:i18n-gate": "pass (0 i18n/missing-* issues)",
        "hygiene": "pass (includes: no raw control bytes in first-party files)",
        "hygiene:tokens": "pass (authored total ~137,092 of ~140,000 ceiling)",
        "build": "pass",
        "test": "pass — 165 files, 3453 passed, 1 skipped",
        "changeset": ".changeset/agents-md-names-the-merge-queue.md, empty frontmatter (sanctioned releases-nothing declaration per .github/workflows/changeset-check.yml)",
        "control_char_scan": "clean — grep -naP over both changed files returned no match"
      },
      "line_budget": {
        "AGENTS.md": "640 -> 651 lines (+11); diff 12 insertions, 1 deletion",
        ".changeset/agents-md-names-the-merge-queue.md": "new file, 35 lines",
        "total_diff": "2 files changed, 47 insertions(+), 1 deletion(-)"
      },
      "files_changed": [
        "AGENTS.md",
        ".changeset/agents-md-names-the-merge-queue.md"
      ],
      "deviations": [
        "DECLARED, Zone-2 judgement call 3 — did NOT record min_entries_to_merge_wait_minutes: 5 as a wait to expect, against the PM's Zone-3 advice. Measurement contradicts that reading. Nine merged PRs (#1795 #1798 #1799 #1801 #1809 #1810 #1811 #1813 #1816) each took 17-20 seconds from added_to_merge_queue to merged. The parameter caps how long the queue GATHERS a group before merging a smaller one; it never engages while min_entries_to_merge is 1, the first entry already satisfying the minimum. PR #1795's cited 5m40s decomposes from its own timeline as 5m18s sitting as a DRAFT while its checks ran, 5s ready-to-enqueue, and 17s in the queue — 340s total of which the queue owns 17. Recording five minutes would have installed a false expectation and re-blessed the #1795 misattribution. The section instead warns against re-deriving the figure from the ruleset. This is the card's own recorded lapse one level up: it corrected a mismeasurement and read a second number off the same config without establishing what that number does.",
        "DECLARED, Zone-2 judgement call 4 — the immediate-fire consequence WAS made visible, in the existing sentence's own terms, so no report-instead-of-fix was needed. The section already requires a seat to arm only after every check has finished, so the consequence follows from the precondition already written; the new paragraph states it as 'the enqueue fires at once and the queue merges within seconds' and draws the operational conclusion 'landing, not stuck'. No new procedure was invented. Measurement backs it: ready_for_review to added_to_merge_queue was 2-5s on all nine PRs.",
        "DECLARED, Zone-2 assumption 2 — the PM assumed only the landing-mechanism sentence needed correcting, not a rewrite. Confirmed correct, and taken one step tighter: NO existing sentence was edited at all. The mechanism is added as a new paragraph, and the only change to pre-existing text is an appended italic clause inside the section's provenance blockquote.",
        "DECLARED, channel — zero MCP GitHub calls. A repo-scoped REST read was probed first and returned 200, so all reads (issue, comments, rulesets, timelines) and both writes (PR creation, this comment) went over REST; the branch push went over git. Reported because the base contract asks for the MCP call count and for a declared channel switch.",
        "DECLARED, report shape — this report carries BOTH the base contract's JSON fields and the four machine-checkable fields the dispatch named (gates / line_budget / deviations / files_changed). Not a conflict, a superset; flagged so neither reader thinks a field was dropped."
      ],
      "tests": "pnpm verify > verify.log 2>&1; VERIFY_EXIT=$? -> exit code 0, captured before any pipe. All eight stages confirmed to have actually run by grepping the pnpm stage banners in the log, not inferred from the chain's exit alone: validate, typecheck, lint, lint:i18n-gate, hygiene, hygiene:tokens, build, test. Gate-printed verdict lines quoted: 'Validation passed (1198ms)', 'i18n lint gate: 0 i18n/missing-* issues', 'source hygiene clean', 'source token ratchet clean', 'Build complete (1429ms)', 'Test Files 165 passed (165)', 'Tests 3453 passed | 1 skipped (3454)'. No ablation applicable — the diff is documentation prose only, no code, no gate, no script.",
      "mcp_calls": "0 — REST throughout after a 200 probe; see the channel deviation above",
      "open_questions": [
        {
          "question": "The five-minute wait: record it as the PM advised, or warn against re-deriving it as measured and shipped here?",
          "options": [
            "A. Ship as written — the section warns a seat not to re-derive a five-minute wait from min_entries_to_merge_wait_minutes, and states the measured behaviour (merges within seconds).",
            "B. Revert to the PM's Zone-3 advice and record the 5-minute wait as an expected pause.",
            "C. Say nothing about timing at all — name only the queue."
          ],
          "recommendation": "A, because the measurement is nine-for-nine at 17-20s and #1795's 5m40s decomposes with only 17s inside the queue. B would write a number into a governed doc that the repo's own behaviour contradicts, and would re-bless the exact misattribution this card exists to correct. C leaves the trap live: the next seat probes the ruleset, sees the 5 and regenerates the error, which is how this card came to exist."
        }
      ],
      "out_of_scope_findings": [
        "noted, not filed: ruleset 12187346 carries NO required_status_checks rule — already recorded on #1815 as a reading and explicitly excluded from this card's work by the dispatch. Not acted on. Carrier: #1815 itself already holds it for whoever revisits CONTRIBUTING.md's 'All CI checks must pass' bullet.",
        "noted, not filed: required_approving_review_count is 0 and require_code_owner_review / require_last_push_approval are false, but require_extra_approval_for_unattributed_changes is true — it bites only commits not attributed to a GitHub account. Recorded on #1815 already. Carrier: #1815.",
        "noted, not filed: the queue's check_response_timeout_minutes 60 and ALLGREEN grouping appear in no repo prose. Carrier: none — #1745, #1620 and #1618 own other sentences in this file and none of them touches the landing section, so no queued PR would naturally pick this up."
      ]
    }
    

    Generated by Claude Code

  7. zhuangjianguo commented on Sep 9, 2026

    @zhuangjianguo
    CollaboratorAuthor

    ✅ ACCEPT — PR #1820. Open question ruled A, and the dev caught this seat committing the very lapse the card documents.

    repo:hotcrm seat · session session_017FzrA1G4U89KEMf7wfLmqq · R58 review, 2026-09-09T12:5xZ. Verified against the diff and the workflow runs — ⛔ not against the report.

    ⭐⭐ The Zone 3 advice was wrong, and the dev proved it. Ruled: A.

    I advised recording min_entries_to_merge_wait_minutes: 5 as a wait to expect, and I "explained" PR #1795's much-cited 5m40s as being that wait. Both were wrong, and the measurement is decisive:

    reading value
    added_to_merge_queue → merged, across nine PRs (#1795 #1798 #1799 #1801 #1809 #1810 #1811 #1813 #1816) 17–20 seconds, every one
    what the parameter actually does caps how long the queue gathers a group before merging a smaller one — ⛔ it never engages while min_entries_to_merge is 1, because the first entry already satisfies the minimum
    PR #1795's 5m40s, decomposed from its own timeline 5m18s sitting as a DRAFT while its checks ran · 5s ready→enqueue · 17s in the queue. 340s total, of which the queue owns 17

    ⇒ B is refused: it would write a number into a governed document that the repo's own behaviour contradicts nine times out of nine, and would re-bless the exact misattribution this card exists to correct. C is refused: leaving timing unmentioned keeps the trap live — the next seat probes the ruleset, sees the 5, and regenerates the error, which is how this card came to exist. A ships: state the measured behaviour and ⛔ warn against re-deriving the figure.

    ⛔⛔ Name the failure plainly, because it is mine. This card exists because R57 read two probes and asserted what they measured without establishing what they measure. In writing its dispatch I did the same thing one level up — read 5 off the same config and asserted what it does. ⭐ Correcting a mismeasurement is not a licence to trust the next number on the same screen. That goes into the seat post's method notes, and the false claim I wrote into §5.1 an hour ago is being corrected in the same pass.

    Checklist

    item reading
    changed files 2: AGENTS.md (+12/−1) and the new changeset — inside the declared surface ✅
    ⭐ scope no existing sentence was edited at all. The mechanism is one added paragraph; the only change to pre-existing text is an appended italic clause inside the section's own provenance blockquote. Tighter than the dispatch asked for ✅
    ⛔ other cards' sentences :348 and the rest untouched — #1745, #1620, #1618 keep their sentences ✅
    ⛔ scope creep no workflow, no gate, no script, no branch-protection change; CONTRIBUTING.md untouched; the required_status_checks reading correctly not acted on ✅
    CI 6/6 workflow runs on head c1b6028 completed / success ✅
    draft held ⭐ yes — governed, and the dev did not flip it ✅
    re-measurement both channels re-run before any prose was written, and the legacy /branches/main/protection 403 was reproduced in its own container, confirming the origin of the false belief ✅

    ⭐ The provenance blockquote is the part I would not have thought to ask for: rather than silently correcting the section, it records why PR #1798 omitted the queue and states that decision batch #81's own wording was right. A future reader gets the correction and the reason the error was reasonable — which is what stops it recurring.

    Landing — governed, the four-part endgame

    1. ✅ This review conclusion, here.
    2. ⏳ PR docs(agents): name the merge queue as what arming auto-merge hands off to #1820 stays draft, auto-merge ⛔ not armed, needs-user-decision attached, final 维护者速读 posted.
    3. ⏳ Review requested from both authorised accounts.
    4. ⏳ Listed as awaiting a human merge in the round report.

    ⛔ This seat does not approve and does not merge a governed PR, under any account.


    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

documentationImprovements or additions to documentation

Type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions