You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
⚠️This post is the RECORD of state, not the state. Every round, re-read the labels. Never read the counts here as current.
📌 Job description is versioned at .claude/skills/pm-dispatch/references/lanes/engine.md. ⛔ Not hand-copied per term.
1. Current PM — 🟢 os-musk
session session_0112hMx9hjJ9BgB28X97DS68 · GitHub login os-musk · seated 2026-09-02T01:45Z via /pm-dispatch engine on 「前任已经在收尾跟进合并,你直接接班。」 · round R16 (opened 2026-09-03T18:0xZ).
Maintainer, most recent first: 17:0xZ 「14923 直接作废」, preceded by 「14923 暂停合并 / 交维护者处理」 ⇒ both executed, see §2. Earlier this shift: 09:2xZ 「任务很多,并发保持5」 ⇒ hard ceiling is 5, superseding 「后续并发降到4」; 「14926 红了,其他除了14923 我都点了merge」; 「pr都绿啦」; 「已完成的pr为什么不合并」.
⛔⛔ THIS SEAT IS OFF CONTRACT_REVIEW_TIER
Tier is claude-fable-5-1 (scripts/pm/dispatch-gates.mjs:7955). get_session reads session_context.model and last_served_model both claude-opus-5. ⇒ ⛔ No Clause-② PR may be reviewed in-seat; each goes to an isolated subagent explicitly passed model: fable, fed only card + rulings + PR, adopted verbatim or voided whole. ⛔ Re-read the fuse with get_session every review session.
⚠️The tier was quota-exhausted through every attempt this shift. That is why #13636's PR carried an unsatisfied needs:contract-review to its end: the gate was unmet by absence of a review, not by a failed one. ⛔ Those are different states and the label cannot tell them apart — write which one it is, every time.
⭐⭐ SEAT ERROR, corrected this shift — I framed a settled question as open in a review brief
For PR #15008 (#14038) I asked the reviewer to judge "local helper vs shared canonicalIsoInstant" as an open design question. Triage had already ruled it — comment 5487875301 rules option 1 and names the route verbatim: 「与 #13997 走的、以及 #14037 将走的是同一条路」. Same shape as the #14486 error (telling a dev something was "genuinely open" that a ruling had settled).
⭐ It did no damage, and the reason is reusable: the same brief said "read the card and every comment first, quote any ruling verbatim, ⛔ do not accept the PR body's paraphrase as evidence." That instruction outranked my framing and the reviewer found the ruling. Route the agent to the source and your own framing stops being load-bearing. The brief also missed a binding constraint I did not know existed (⛔ 「PR说明里不要把这次修复写成「行为变更」」) — the reviewer found and checked it for the same reason.
⛔ Earlier this shift: I read merge-queue membership off the wrong instrument, and reported it
Called enable_pr_auto_merge on five PRs, saw (method: , enabled at ) with empty fields, found no auto_merge field, concluded the arms failed, told the maintainer. Wrong — #14629 and #14649 were in the queue the whole time and landed.
⭐⭐ Sharpened this shift with the other half of the reading: a filled-in confirmation — (method: MERGE, enabled at 2026-09-03T17:51:28Z) — is the positive arm reading, observed on #14926, #14982 and #15008. The empty sentence corresponds to already queued. So the sentence does carry information; it just does not mean what it looks like. ⛔ The absent auto_merge field still supports no conclusion in either direction, and the reliable membership reading is still actions_list + event: merge_group.
⛔ PR #14734 merged while FAILed — recorded, not reverted
Tier review FAILed it, card was pm:blocked, zero reviews existed, this seat never armed it. Six disable_pr_auto_merge attempts all hit the GraphQL rate limit. Merged as dee4dd4ba. ⛔ Not reverted (pre-registered rule). The still-unreleased false changeset claim is flagged on release PR #11336.
2. Ledger (current values, 2026-09-03T18:0xZ)
origin/main = 99b4deba4.
Landed and verified by content this shift — most recent first
Filed this shift, unassigned and ungraded (⛔ domain:*/priority/type are triage's)
#14998 — plugin-authdurability-swallow-repair.test.ts:673 pays a cold dynamic import inside a 10 s testTimeout; reddened an unrelated PR. Six closed same-family precedents; #5421 and #3662 are the same sibling-already-sets-a-longer-timeout shape. #15037 — RemoteLoader.list() declares Promise<string[]> but maps a nameless body straight through, so listNames() can return a literal undefined. Filed on behalf of the #14423 census seat, which correctly declined to file blind when its dedup channel hit the rate limit.
Closed and residue-cleaned this shift
#14415 (Fixes-closed by #14982) — all pm:* stripped, assignee cleared, verified by comparison read-back. #14587 — closed completed after verifying #14931 really carries its part 2; same strip, same read-back.
3. Hot-file serial queue (2026-09-03T18:0xZ)
⭐⭐ READ THIS BEFORE THE CLAIM, NOT AFTER. Reversing that order burned #14623.
⛔ Non-overlap of symbols is not non-overlap of line anchors: both shift lines below their hunks, and this file feeds line-anchored generated artifacts. See §4.
Concurrency 5, hard ceiling on running subagents in total. Devs AND isolated review subagents count. ⛔ A Clause-② dispatch must leave a slot free for its reviewer.
⭐⭐ The rate limit splits REST vs GraphQL, not read vs write. Measured this shift: pull_request_read, issue_write, add_issue_comment, actions_* (REST) keep working while issue_read, list_issues, search_issues and update_pull_request (GraphQL) all fail with API rate limit already exceeded for user ID …. update_pull_request does a GraphQL lookup before its write, which is why it reports Failed to find pull request. ⇒ draft→ready is structurally blocked when that bucket is out: GitHub exposes it only as the GraphQL markPullRequestReadyForReview; REST PATCH /pulls/{n} has no such field. ⛔ Do not try to route around it by merging directly — landing goes through the queue.
⭐⭐ Route the reviewer to the source and your own framing stops being load-bearing."Read the card and every comment first, quote any ruling verbatim, ⛔ do not accept the PR body's paraphrase" caught a ruling this seat had framed as an open question, and a binding prose constraint this seat did not know existed.
⭐⭐ A claim carrying its own repro command and no positive control is the one that fails.qa: full log-volume census — structured logger is 77.1% of test output, not ~45% #14989: 3 of 11 file:line citations were wrong, and the wrong one was the only one that reached a decision input. The repair is not the number — it is stamping the ref beside it so the next reader can reproduce it.
⭐⭐ Read merge-queue membership from actions_list + event: merge_group — branch gh-readonly-queue/main/pr-<n>-<parent>. Cheap positive reading, no cheap negative one. A filled-in(method: …, enabled at …) is a successful arm; an empty one means already queued.
⭐⭐ A dispatch premise handed over as a Zone 2 item gets falsified by the dev; one asserted as Zone 1 ships. When unsure, it is a Zone 2 item.
⭐ Stop conditions ask for EVIDENCE, not a verdict, and close inside the agent's own round (docs(pm-dispatch): make the stop-condition clause carry the executability criterion #14697) — a one-shot agent has no mid-run question channel and no background wake. This belongs in dev prompts as well as reviewer prompts; putting it only in the latter stalled two agents.
⭐ A cross-seat request must be a CARD in the target lane's queue — a seat-post knock or a ruling comment is invisible to that lane's candidate query, sweep and ageing alarms.
⭐ A red check has three classes. Timeout ⇒ load. Assertion on product behaviour ⇒ regression. Assertion on the experiment's own validity premise ⇒ same class as a timeout. ⛔ "Flake" is not a root cause — one re-run confirms, and a standing-down comment names the check and why it is not this PR's.
Readings: list_issues labels is OR and never returns assignees — use search_issues with label: + no:assignee + state:open · issue_read get_labelsrefuses a PR number while issue_write update on it succeeds · issue_read get returns closed_by_pull_requests, the cheapest way to see whether a PR carries a closing keyword · the PR side runs the AFFECTED subset; the queue build runs the FULL suite, so a PR green on its head can still eject · a PR can wait 45+ min for a queue slot · Test Core (1/6) is the long shard (12–26 min) · get_check_runs pages at 30, so perPage: 50 · list_pull_requests at perPage: 50, get_files on a large PR, and get_job_logs at tail_lines: 450 all EXCEED the tool token cap — the log spills to a file; parse it with python3 (the log is ONE line, so re.split on \\n/\\\\n first), ⛔ never read whole · dispatch-gates.mjswith NO path arguments derives the change set from git off the merge base and that derivation is authoritative over any hand-listed set · check-governed-merges.mjs --test must be re-run on the FINAL file list · use a three-dot diff against main.
Governed surfaces (docs/adr/**, .claude/**, skills/**, AGENTS.md, CLAUDE.md): draft-only. ⛔ Never flip ready / enqueue / auto-merge on own judgment; ⛔ never approve from an agent seat. packages/spec/** is not governed — that is lane ownership, a different question.
📌 Job description is versioned at
.claude/skills/pm-dispatch/references/lanes/engine.md. ⛔ Not hand-copied per term.1. Current PM — 🟢 os-musk
session
session_0112hMx9hjJ9BgB28X97DS68· GitHub loginos-musk· seated 2026-09-02T01:45Z via/pm-dispatch engineon 「前任已经在收尾跟进合并,你直接接班。」 · round R16 (opened 2026-09-03T18:0xZ).Maintainer, most recent first: 17:0xZ 「14923 直接作废」, preceded by 「14923 暂停合并 / 交维护者处理」 ⇒ both executed, see §2. Earlier this shift: 09:2xZ 「任务很多,并发保持5」 ⇒ hard ceiling is 5, superseding 「后续并发降到4」; 「14926 红了,其他除了14923 我都点了merge」; 「pr都绿啦」; 「已完成的pr为什么不合并」.
⛔⛔ THIS SEAT IS OFF
CONTRACT_REVIEW_TIERTier is
claude-fable-5-1(scripts/pm/dispatch-gates.mjs:7955).get_sessionreadssession_context.modelandlast_served_modelbothclaude-opus-5. ⇒ ⛔ No Clause-② PR may be reviewed in-seat; each goes to an isolated subagent explicitly passedmodel: fable, fed only card + rulings + PR, adopted verbatim or voided whole. ⛔ Re-read the fuse withget_sessionevery review session.needs:contract-reviewto its end: the gate was unmet by absence of a review, not by a failed one. ⛔ Those are different states and the label cannot tell them apart — write which one it is, every time.⭐⭐ SEAT ERROR, corrected this shift — I framed a settled question as open in a review brief
For PR #15008 (#14038) I asked the reviewer to judge "local helper vs shared
canonicalIsoInstant" as an open design question. Triage had already ruled it — comment 5487875301 rules option 1 and names the route verbatim: 「与 #13997 走的、以及 #14037 将走的是同一条路」. Same shape as the #14486 error (telling a dev something was "genuinely open" that a ruling had settled).⭐ It did no damage, and the reason is reusable: the same brief said "read the card and every comment first, quote any ruling verbatim, ⛔ do not accept the PR body's paraphrase as evidence." That instruction outranked my framing and the reviewer found the ruling. Route the agent to the source and your own framing stops being load-bearing. The brief also missed a binding constraint I did not know existed (⛔ 「PR说明里不要把这次修复写成「行为变更」」) — the reviewer found and checked it for the same reason.
⛔ Earlier this shift: I read merge-queue membership off the wrong instrument, and reported it
Called
enable_pr_auto_mergeon five PRs, saw(method: , enabled at )with empty fields, found noauto_mergefield, concluded the arms failed, told the maintainer. Wrong — #14629 and #14649 were in the queue the whole time and landed.⭐⭐ Sharpened this shift with the other half of the reading: a filled-in confirmation —
(method: MERGE, enabled at 2026-09-03T17:51:28Z)— is the positive arm reading, observed on #14926, #14982 and #15008. The empty sentence corresponds to already queued. So the sentence does carry information; it just does not mean what it looks like. ⛔ The absentauto_mergefield still supports no conclusion in either direction, and the reliable membership reading is stillactions_list+event: merge_group.⛔ PR #14734 merged while FAILed — recorded, not reverted
Tier review FAILed it, card was
pm:blocked, zero reviews existed, this seat never armed it. Sixdisable_pr_auto_mergeattempts all hit the GraphQL rate limit. Merged asdee4dd4ba. ⛔ Not reverted (pre-registered rule). The still-unreleased false changeset claim is flagged on release PR #11336.2. Ledger (current values, 2026-09-03T18:0xZ)
origin/main=99b4deba4.Landed and verified by content this shift — most recent first
95464ed65origin/main;this.runMutationProjector(= 7 call sites9f57f1e3113b52006982252480aca3fd4b1a⛔ Every one read off
origin/mainby content, ⛔ never off the API'smergedfield.In the merge queue — 1
#14038 / PR #15008 — ready 17:5xZ, armed
(method: MERGE, enabled at 17:51:28Z), all 37 check runs green/skipped. Independent review PASS.In flight — 3 of 5
bugprotocol.ts—applyRemoteMetadataMutationreceipt path (~:5062-5199) + a helper besidereadWriteEpochinmeta-overlay-cache.tsdocs/audits/**+scripts/qa/**— three citation corrections, review FAILed itDecision box / handed over
pm:awaiting-maintainer, assignee cleared,needs:contract-reviewretained (⛔ a gate is cleared only at a PASS, never because work stopped). PR feat(objectql): declare a legitimately org-less write instead of inferring it from NULL #14923 closed unmerged on the maintainer's verbatim instruction 「14923 直接作废」; branch left in place, nothing deleted. ⛔ Do not touch either.pm:retriage(coexisting, no label stripped). Its fork goes back to triage, not to the maintainer's inbox — triage's ruling item ③ says 「报 fork 回分诊,附测量结果」.new ObjectKernel()count this seat handed over (85/62) does not reproduce; correction posted, fix round in flight.loadManywhile the router's isloadby name, andunboundDeclarationsstill reads two sources where the undeclared-handler half now reads three #14423 — after census(#14423 step 1): loadMany consumers, per-loader keyed-read cost, C3/C4 re-measured #15033 lands, returns toneeds-user-decisionwith a Chinese four-facet block. Its census falsified the ruling's own named default: switching the audit tolistNames+ by-name closes C2/C6 but not C3 (MetadataManager.listNameshas no per-loader try/catch) or C4 (ctx.getService('metadata')throws before any read). ⛔ This seat does not pick step 2's option.data.namediffers from the derived key — one silently rewrites the author's field, the other hard-fails the whole artifact load #14666, [finding]ResolvedAuthzContext.authRefusalhas zero transport readers — both #8287 refusal reasons collapse to the generic anonymous 401 on every wire #14273, finding: the insert-pathreadonlystrip is a protocol-boundary guard only —engine.insertapplies none of it, andcreate_record'sonFieldsDroppedchannel can never fire for a readonly drop #14147, decision(objectql): anavigationContributions[].groupthat names no group in the target app is silently RELOCATED to the top level — refuse, warn, or leave to the consumer? #14553, [decision] Should the seed loader and #8686's backfill follow #13491's per-object tenancy classification, or stay on the namespace regexp? #14096, [decision] Was the ADR-0030 notification cut-over ever run against a live Postgres/MySQL deployment — and should the migration be registered in the sys_migration ledger so the question stops being unanswerable? #14025, [decision] CEL_STDLIB_FUNCTIONS route: keep 35 as a declared subset, widen to the 39 bare-callables, or rename the symbol #13933, [Decision]severity: 'warning'的校验规则没有受众概念 —— UI 级劝导在 seed/bootstrap 等机器写入路径上照样求值并打爆启动日志,且按写入次数而非按行计数 #13889.Filed this shift, unassigned and ungraded (⛔
domain:*/priority/type are triage's)#14998 —
plugin-authdurability-swallow-repair.test.ts:673pays a cold dynamic import inside a 10 s testTimeout; reddened an unrelated PR. Six closed same-family precedents; #5421 and #3662 are the same sibling-already-sets-a-longer-timeout shape.#15037 —
RemoteLoader.list()declaresPromise<string[]>but maps a nameless body straight through, solistNames()can return a literalundefined. Filed on behalf of the #14423 census seat, which correctly declined to file blind when its dedup channel hit the rate limit.Closed and residue-cleaned this shift
#14415 (
Fixes-closed by #14982) — allpm:*stripped, assignee cleared, verified by comparison read-back. #14587 — closedcompletedafter verifying #14931 really carries its part 2; same strip, same read-back.3. Hot-file serial queue (2026-09-03T18:0xZ)
⭐⭐ READ THIS BEFORE THE CLAIM, NOT AFTER. Reversing that order burned #14623.
packages/objectql/src/engine.ts— FREE. design: a THIRD tenancy state the 批 #9 re-ruling cannot express — objects that are CONDITIONALLY tenant-scoped, where org-less is a property of the ROW, not the object #13636 held it; PR feat(objectql): declare a legitimately org-less write instead of inferring it from NULL #14923 is voided and closed, so the slot is open. Queue behind it: ObjectRepository.execute() dispatches an action with neitherapinorexecutionContext— a readonly write LANDS on the REST/MCP paths and is silently stripped on that one #13866 → Amulti: truehook that writes the SAME key with per-row VALUES still applies one row's value to every matched row — the residue #14099's key-set refusal deliberately leaves open #14744 → [finding]engine.tsreachesderiveViewContainerObjectthrough@objectstack/metadata's ROOT entry, so objectql's lean ADR-0076core.tsclosure now pulls MetadataPlugin, chokidar, glob and js-yaml for a six-line pure function #14680 — all the same file (resolveSystemInsertOrganization:3944,class ObjectRepository:13997,MultiUpdateHookKeyDivergenceError×3, thederiveViewContainerObjectimport:192).packages/metadata-protocol/src/protocol.ts— two, fenced by symbol, verified on the current head each time:listCommits, opens19036closes19128.applyRemoteMetadataMutationreceipt path, ~:5062-5199.packages/objectql/src/action-governance.ts·plugin.ts— read-only under [finding] Two residual audit ↔ router asymmetries after #14123: the audit's third source isloadManywhile the router's isloadby name, andunboundDeclarationsstill reads two sources where the undeclared-handler half now reads three #14423 step 1 (PR census(#14423 step 1): loadMany consumers, per-loader keyed-read cost, C3/C4 re-measured #15033). Step 2 will hold them once ruled.docs/audits/**·scripts/qa/**— qa: full log-volume census — structured logger is 77.1% of test output, not ~45% #14989 (in flight).packages/metadata/src/loaders/**— free; [finding]RemoteLoader.list()declaresPromise<string[]>but maps a nameless body straight through, solistNames()can return a literalundefinedas a name #15037 filed againstremote-loader.tsbut ungraded and undispatched.platform-objects/**,examples/**,packages/core/**— free.4. Notes
loadManywhile the router's isloadby name, andunboundDeclarationsstill reads two sources where the undeclared-handler half now reads three #14423, [finding] QA observed /api/v1/meta/datasource serving a deleted datasource's entry cluster-wide, but MetadataManager's unregister DOES broadcast — the observed prolongation sits in an unidentified seam #13609 A′, metadata-protocol:listCommitsdeclarescreatedAtas a string but emits the raw driver value, so it hands consumers aDateon Postgres/MySQL #14038) were only discoverable there, and one of them had already been amended.pull_request_read,issue_write,add_issue_comment,actions_*(REST) keep working whileissue_read,list_issues,search_issuesandupdate_pull_request(GraphQL) all fail withAPI rate limit already exceeded for user ID ….update_pull_requestdoes a GraphQL lookup before its write, which is why it reportsFailed to find pull request. ⇒ draft→ready is structurally blocked when that bucket is out: GitHub exposes it only as the GraphQLmarkPullRequestReadyForReview; RESTPATCH /pulls/{n}has no such field. ⛔ Do not try to route around it by merging directly — landing goes through the queue.--fix— on a branch that shifts lines, "take theirs" and "take ours" are both wrong. An append-only registry keeps BOTH rows. Confirmed twice this shift (fix(metadata-protocol): listCommits emits the ISO-8601 string createdAt declares #15008's twocheck:system-context-censusre-anchors; theengine-double-contractledger carrying Five more adapter-boundary sites cast a driverDateinto a declared ISO-string timestamp —MetadataEvent.ts,MetadataHistoryRecord.recordedAt,MetadataRecord.createdAt/updatedAt#14037's four rows and metadata-protocol:listCommitsdeclarescreatedAtas a string but emits the raw driver value, so it hands consumers aDateon Postgres/MySQL #14038's one simultaneously).file:linecitations were wrong, and the wrong one was the only one that reached a decision input. The repair is not the number — it is stamping the ref beside it so the next reader can reproduce it.actions_list+event: merge_group— branchgh-readonly-queue/main/pr-<n>-<parent>. Cheap positive reading, no cheap negative one. A filled-in(method: …, enabled at …)is a successful arm; an empty one means already queued.durability-swallow-repair.test.ts's firstadmin-import-userspin pays a cold dynamic import inside a 10 s testTimeout, so it reddens unrelated PRs under shard load #14998 and [finding]RemoteLoader.list()declaresPromise<string[]>but maps a nameless body straight through, solistNames()can return a literalundefinedas a name #15037 were both filed only because the query returned the right family — that is the firing control. ⛔ "Cannot search" is never "searched and clean": the [finding] Two residual audit ↔ router asymmetries after #14123: the audit's third source isloadManywhile the router's isloadby name, andunboundDeclarationsstill reads two sources where the undeclared-handler half now reads three #14423 dev was right to report its finding unfiled rather than file blind.yes. Unsure ⇒yes.protocol.ts:20271).loadManywhile the router's isloadby name, andunboundDeclarationsstill reads two sources where the undeclared-handler half now reads three #14423 ruled "census first … because the fix's shape depends on it", and the census then falsified the default. ⇒ back to the decision box, ⛔ not a seat choice.isoFromValidDatewas recorded on the PR and deliberately not filed: whatever resolves The shared canonical-ISO normaliser turns an InvalidDatefrom a driver into a 500, whereString()served text #14078 replaces all three copies at once.minor· new public-entry export ⇒minor· published type narrowing ⇒minor+ BREAKING +adr-0087:· envelope on an existing refusal / accept-set widening ⇒patch· published/metaread-door row-set change ⇒patch· repairing an implementation that silently violated its own already-published declared type ⇒patch(fix(metadata): emit the declared ISO string at the five adapter boundaries that cast a driverDate#14939 / fix(metadata-protocol): listCommits emits the ISO-8601 string createdAt declares #15008, bothmetadata+metadata-protocolpatch) · comment-and-test-only ⇒skip-changeset. ⛔majorforbidden bycheck-changeset-no-major.mjs.list_issueslabels is OR and never returns assignees — usesearch_issueswithlabel:+no:assignee+state:open·issue_read get_labelsrefuses a PR number whileissue_write updateon it succeeds ·issue_read getreturnsclosed_by_pull_requests, the cheapest way to see whether a PR carries a closing keyword · the PR side runs the AFFECTED subset; the queue build runs the FULL suite, so a PR green on its head can still eject · a PR can wait 45+ min for a queue slot ·Test Core (1/6)is the long shard (12–26 min) ·get_check_runspages at 30, soperPage: 50·list_pull_requestsatperPage: 50,get_fileson a large PR, andget_job_logsattail_lines: 450all EXCEED the tool token cap — the log spills to a file; parse it withpython3(the log is ONE line, sore.spliton\\n/\\\\nfirst), ⛔ never read whole ·dispatch-gates.mjswith NO path arguments derives the change set from git off the merge base and that derivation is authoritative over any hand-listed set ·check-governed-merges.mjs --testmust be re-run on the FINAL file list · use a three-dot diff against main.docs/adr/**,.claude/**,skills/**,AGENTS.md,CLAUDE.md): draft-only. ⛔ Never flip ready / enqueue / auto-merge on own judgment; ⛔ never approve from an agent seat.packages/spec/**is not governed — that is lane ownership, a different question.