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 — 🟢 zhuangjianguo
session session_01ARYe3yQTQCUFm5qPYNgKaJ · GitHub login zhuangjianguo · seated 2026-09-04T05:52Z via /pm-dispatch engine on the predecessor's shift-end brief (comment 5534857428, an explicit 「本席不再写」 release) · round R17 (opened 2026-09-04T05:52Z, marker 5536331483).
Standing parameters, carried forward: in-flight ceiling 5 (maintainer 2026-09-03, 「任务很多,并发保持5」, superseding 「后续并发降到4」). Devs AND isolated review subagents both count. ⛔ A Clause-② dispatch must leave a slot free for its reviewer.
⛔⛔ THIS SEAT IS OFF CONTRACT_REVIEW_TIER — re-measured 2026-09-04T05:5xZ, ⛔ not inherited
Tier is claude-fable-5-1 (scripts/pm/dispatch-gates.mjs:8659, read on origin/main). get_session returns session_context.modelclaude-opus-5 and last_served_modelclaude-opus-5.
⇒ ⛔ No Clause-② PR may be reviewed in-seat; each goes to an isolated subagent explicitly passed model: fable, fed card + rulings + PR only, adopted verbatim or voided whole. ⛔ Re-read the fuse with get_session every review session — it is a per-session fact, not a lane fact.
⚠️Two different states the needs:contract-review label cannot tell apart — write which one it is, every time: gate unmet because no review happened (quota exhaustion, off-tier seat) vs gate unmet because a review FAILed. The predecessor's whole shift was the first kind.
2. Ledger — current values, 2026-09-04T06:2xZ
origin/main = 50d6c92. ⛔ Every landing below verified by CONTENT on origin/main, never by the API's merged field.
4 probe scripts present under scripts/audits/14423-*
⇒ The predecessor's entire "in the merge queue" and "owed" list is discharged.#15131 did land (its brief said it could not; the brief's own correction 5534934123 records the maintainer merging #15051, and #15131 followed).
⭐⭐ Two of this round's five corrections were cards whose Blocked-by: target had closed while the condition it stood for had NOT been met. The unlock predicate is "the target closed"; the conditions were "the validator reads the key" and "the ledger still exists". ⇒ On every unlock candidate, read what the conversion comment actually made the wait ABOUT, then verify THAT against the tree. A closed blocker is a prompt to check, never the answer.
Decision box — ⛔ re-verify from labels every round, a ruling flips a label with no notification here
#14423 (new this round) · #14666 · #14147 · #14273 · #14025 · #13933 (pm:retriage, routing deadlock — carrier is triage's) · #13889 · #13636 (pm:awaiting-maintainer; PR #14923 voided on the maintainer's 「14923 直接作废」 — ⛔ do not touch either; superseded under ADR-0131 §1.6 / C11).
⚠️ New lane inventory this seat did not file and has not graded
#15194 / #15195 / #15196 — cut 05:44Z as the ADR-0131 execution tree, target:v18, priority:p1, security, all pm:blocked behind #15193 ("the v18 development line is not open"). #15195 carries an explicit ⛔ "Do not claim, assign or dispatch this card while #15193 is open, whatever its other labels say." #15161 — the engine half of #14168, filed by the domain:spec seat 04:10Z. pm:queue with no domain:* ⇒ half-annotated, invisible to this lane's candidate query; grading is triage's.
4. Notes — platform facts and lane disciplines that are current
⭐⭐ The rate limit splits REST vs GraphQL, not read vs write. REST (pull_request_read, issue_write, add_issue_comment, actions_*) keeps working while GraphQL (issue_read, list_issues, search_issues, and update_pull_request — it does a GraphQL lookup before its REST write) all fail together. ⇒ draft→ready is structurally blocked when that bucket is out: GitHub exposes it only as GraphQL markPullRequestReadyForReview; REST PATCH /pulls/{n} has no draft field. ⛔ Do not route around it by merging directly.
⭐⭐ Merge-queue membership: the only reliable POSITIVE reading is actions_list + event: merge_group (branch gh-readonly-queue/main/pr-<n>-<parent>). There is no cheap negative reading. ⛔ The arm receipt's filled/empty distinction carries no established meaning — the predecessor recorded one as a ⭐⭐ platform fact and then withdrew it (5530403273); both forms have preceded successful landings. ⚠️mergeable_state: "unknown" is lazy computation, and is also exactly the shape a real conflict arrives in.
⭐⭐ Conflict resolution depends on the artifact's KIND; getting it backwards corrupts other people's work. A generated line-anchor table must be re-derived with its own tool's --fix (on a branch that shifts lines, take-theirs and take-ours are both wrong); an append-only ledger keeps BOTH rows; hand-written source preserves each side's intent.
⭐⭐ Route the agent 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" has twice caught a ruling the seat had mis-framed as an open question.
⭐⭐ A zero-hit grep is not a result without a positive control. Same for a dedup search: 「查不了」 is never 「查过了干净」.
⭐ A one-shot agent has no mid-run question channel and no background wake. Stop conditions must ask for evidence and close inside the agent's own round, and this belongs in dev prompts as well as reviewer prompts. ⛔ Never tell an agent to background long work and await a notification — the predecessor stalled three times on exactly that.
⭐ In-flight count is a READING, not a memory.ListAgents answers it in one call. The predecessor reported a round as dispatched that never was, and repeated it for several rounds, because the list was memory.
⭐ 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 Zone 2.
⭐ 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.
Semver: envelope on a published verb ⇒ minor · new public-entry export ⇒ minor · published type narrowing ⇒ minor + BREAKING + adr-0087: · envelope on an existing refusal / accept-set widening ⇒ patch · published /meta read-door row-set change ⇒ patch · repairing an implementation that silently violated its own already-published declared type ⇒ patch · comment-and-test-only ⇒ skip-changeset. ⛔ major forbidden by check-changeset-no-major.mjs.
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 · 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 — ⚠️ it also warns when run against a stale local tree, and that warning is load-bearing · use a three-dot diff against main.
Governed surfaces (docs/adr/**, .claude/**, skills/**, AGENTS.md, CLAUDE.md): draft-only. ⛔ Never flip ready / enqueue / arm 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 — 🟢 zhuangjianguo
session
session_01ARYe3yQTQCUFm5qPYNgKaJ· GitHub loginzhuangjianguo· seated 2026-09-04T05:52Z via/pm-dispatch engineon the predecessor's shift-end brief (comment5534857428, an explicit 「本席不再写」 release) · round R17 (opened 2026-09-04T05:52Z, marker5536331483).Standing parameters, carried forward: in-flight ceiling 5 (maintainer 2026-09-03, 「任务很多,并发保持5」, superseding 「后续并发降到4」). Devs AND isolated review subagents both count. ⛔ A Clause-② dispatch must leave a slot free for its reviewer.
⛔⛔ THIS SEAT IS OFF
CONTRACT_REVIEW_TIER— re-measured 2026-09-04T05:5xZ, ⛔ not inheritedTier is
claude-fable-5-1(scripts/pm/dispatch-gates.mjs:8659, read onorigin/main).get_sessionreturnssession_context.modelclaude-opus-5andlast_served_modelclaude-opus-5.⇒ ⛔ No Clause-② PR may be reviewed in-seat; each goes to an isolated subagent explicitly passed
model: fable, fed card + rulings + PR only, adopted verbatim or voided whole. ⛔ Re-read the fuse withget_sessionevery review session — it is a per-session fact, not a lane fact.needs:contract-reviewlabel cannot tell apart — write which one it is, every time: gate unmet because no review happened (quota exhaustion, off-tier seat) vs gate unmet because a review FAILed. The predecessor's whole shift was the first kind.2. Ledger — current values, 2026-09-04T06:2xZ
origin/main=50d6c92. ⛔ Every landing below verified by CONTENT onorigin/main, never by the API'smergedfield.Inherited from R16, verified landed this round
e37456eonorigin/mainfcc42e6a54855005; disclosure follow-up #15131 merged 03:56:10Zscripts/audits/14423-*⇒ The predecessor's entire "in the merge queue" and "owed" list is discharged. #15131 did land (its brief said it could not; the brief's own correction
5534934123records the maintainer merging #15051, and #15131 followed).In flight — 1 of 5
opus,mode:subagent)priority:p0securitypackages/objectql/src/engine.ts—publishDataEvent:5629+ its 3 call sites:10286/:11615/:13069, plus a pin testState corrections made this round — each paired with a comment in the same stroke
pm:queue(H19 release). Released against triage's own two controls, both re-run:recordRejectedMessageand"rejected before delivery"flipped 0 → firing, so the widening is now genuinely owed. XS /sonnet.:2470→:2479.SqlDriver.update()keeps an explicitPromise[any]while returningnullon a miss — the same published-type mask #14434 removed from driver-memory, inherited by TursoDriver and SqliteWasmDriver #14438 →pm:queue(H19 release). Both things triage named as missing now exist (pin templatememory-update-declared-null.test.ts;IDataDriver.update()atdata-driver.ts:198), and the masked door is still live atsql-driver.ts:6908(drifted from:6820).CONTRACT_REVIEW_TIER⇒ this seat cannot dispatch it.sys_business_unit_memberis unadjudicated inPLATFORM_OBJECT_TENANCY, so seed-replayed and system-written membership rows land organization-less #14570 → re-aimed,Blocked-by:#14096→#15195, body-synced. Its blocker closed by a ruling that retires the ledger it asks to adjudicate (ADR-0131 D13/C8), so an unlock scan would have released it wrongly. Rows 2/3 of its table are subsumed by feat(objectql,plugin-auth): the Default Organization is load-bearing undersingle; an unstamped write is derived there and refused everywhere else (ADR-0131 D3/D9/D11) #15195's scope. Wait is transitive — feat(objectql,plugin-auth): the Default Organization is load-bearing undersingle; an unstamped write is derived there and refused everywhere else (ADR-0131 D3/D9/D11) #15195 is itself blocked on [gate] the v18 development line is not open — ADR-0131 execution cards are blocked on this card #15193.Blocked-by:#14168→#15161, body-synced (discharges the sync the predecessor and the director both left to this seat).iana_time_zone), and neither extension point an app can reach can express it #14168 closed 04:05Z but PR feat(spec): Field.valueDomain, one closed standard-domain vocabulary and one membership predicate shared by settings specifiers and object fields #15133 shipped only the slot — liveness rowvalueDomainis stillplanned(packages/spec/liveness/field.json:224), so the ruling's "⛔ not unvalidated" condition still binds. The next unlock scan would have released this wrongly too.loadManywhile the router's isloadby name, andunboundDeclarationsstill reads two sources where the undeclared-handler half now reads three #14423 →needs-user-decisionwith a four-facet block. Step 1 landed and falsified the ruling's own named default: option A does not close C3 (MetadataManager.listNameshas no per-loader try/catch). ⛔ Not this seat's option to pick.⭐⭐ Two of this round's five corrections were cards whose
Blocked-by:target had closed while the condition it stood for had NOT been met. The unlock predicate is "the target closed"; the conditions were "the validator reads the key" and "the ledger still exists". ⇒ On every unlock candidate, read what the conversion comment actually made the wait ABOUT, then verify THAT against the tree. A closed blocker is a prompt to check, never the answer.Decision box — ⛔ re-verify from labels every round, a ruling flips a label with no notification here
#14423 (new this round) · #14666 · #14147 · #14273 · #14025 · #13933 (
pm:retriage, routing deadlock — carrier is triage's) · #13889 · #13636 (pm:awaiting-maintainer; PR #14923 voided on the maintainer's 「14923 直接作废」 — ⛔ do not touch either; superseded under ADR-0131 §1.6 / C11).#15194 / #15195 / #15196 — cut 05:44Z as the ADR-0131 execution tree,
target:v18,priority:p1,security, allpm:blockedbehind #15193 ("the v18 development line is not open"). #15195 carries an explicit ⛔ "Do not claim, assign or dispatch this card while #15193 is open, whatever its other labels say."#15161 — the engine half of #14168, filed by the
domain:specseat 04:10Z.pm:queuewith nodomain:*⇒ half-annotated, invisible to this lane's candidate query; grading is triage's.3. Hot-file serial queue — 2026-09-04T06:2xZ
⭐⭐ READ THIS BEFORE THE CLAIM, NOT AFTER.
packages/objectql/src/engine.ts— HELD by objectql:publishDataEventdoes not stamp theorganizationIdthe spec now declares — the producer half of the #13566 p0 cross-tenant webhook leak, and nothing is filed for it #14970 (p0, in flight). Queue behind it, serial: [Decision] Two SOURCE registrars disagree on a view container whosedata.namediffers from the derived key — one silently rewrites the author's field, the other hard-fails the whole artifact load #14666 → 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 → 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. ⭐priority:p0bought objectql:publishDataEventdoes not stamp theorganizationIdthe spec now declares — the producer half of the #13566 p0 cross-tenant webhook leak, and nothing is filed for it #14970 the front of the queue; it did not exempt it from the same-file serial rule.data.namediffers from the derived key — one silently rewrites the author's field, the other hard-fails the whole artifact load #14666 and 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 both exit through contract-review tier, which this seat is off, so dispatching them now would produce unlandable work. ⭐ Re-appraise the moment the tier recovers.single; an unstamped write is derived there and refused everywhere else (ADR-0131 D3/D9/D11) #15195 will also touch this area (resolveSystemInsertOrganization, though it now lives atpackages/objectql/src/tenancy/system-write-organization.ts) once [gate] the v18 development line is not open — ADR-0131 execution cards are blocked on this card #15193 opens. Sequence it against this queue then.packages/drivers/driver-sql/src/sql-driver.ts— [finding]SqlDriver.update()keeps an explicitPromise[any]while returningnullon a miss — the same published-type mask #14434 removed from driver-memory, inherited by TursoDriver and SqliteWasmDriver #14438 queued, undispatched, needs a seat at tier.packages/platform-objects/src/audit/sys-email.object.ts+ its generated i18n bundle — sys_email.error description says "transport error" but the column now also carries pre-delivery rejections #14372 queued, XS/sonnet. ⛔ The generated file is hand-edit-forbidden; regenerate.packages/objectql/src/action-governance.ts·plugin.ts— free; [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 is back in the decision box, so step 2 holds nothing.packages/metadata-protocol/src/protocol.ts·docs/audits/**·scripts/qa/**·packages/metadata/src/loaders/**·platform-objects/**·examples/**·packages/core/**— free.4. Notes — platform facts and lane disciplines that are current
pull_request_read,issue_write,add_issue_comment,actions_*) keeps working while GraphQL (issue_read,list_issues,search_issues, andupdate_pull_request— it does a GraphQL lookup before its REST write) all fail together. ⇒ draft→ready is structurally blocked when that bucket is out: GitHub exposes it only as GraphQLmarkPullRequestReadyForReview; RESTPATCH /pulls/{n}has no draft field. ⛔ Do not route around it by merging directly.actions_list+event: merge_group(branchgh-readonly-queue/main/pr-<n>-<parent>). There is no cheap negative reading. ⛔ The arm receipt's filled/empty distinction carries no established meaning — the predecessor recorded one as a ⭐⭐ platform fact and then withdrew it (5530403273); both forms have preceded successful landings.mergeable_state: "unknown"is lazy computation, and is also exactly the shape a real conflict arrives in.mergeable_state: "dirty"— auto-merge does not fire on a conflicted PR and nothing announces it. Only a positive diagnosis sees it.--fix(on a branch that shifts lines, take-theirs and take-ours are both wrong); an append-only ledger keeps BOTH rows; hand-written source preserves each side's intent.engine.tsandprotocol.tsline anchors move constantly. Measured this round: the card's anchors on objectql:publishDataEventdoes not stamp theorganizationIdthe spec now declares — the producer half of the #13566 p0 cross-tenant webhook leak, and nothing is filed for it #14970 were off by one, [finding]SqlDriver.update()keeps an explicitPromise[any]while returningnullon a miss — the same published-type mask #14434 removed from driver-memory, inherited by TursoDriver and SqliteWasmDriver #14438's by 88, sys_email.error description says "transport error" but the column now also carries pre-delivery rejections #14372's generated anchor by 9. ⛔ Re-derive every anchor by symbol on your own head. ⛔ Never reason from a number written in a card, a brief, or this post.no(precedent metadata-protocol: the three recovery doors run no ADR-0094 mutation projector — a rollback restores the row and leaves the derived read-model on the rolled-back-FROM state #14415 atprotocol.ts:20271; applied again this round on objectql:publishDataEventdoes not stamp theorganizationIdthe spec now declares — the producer half of the #13566 p0 cross-tenant webhook leak, and nothing is filed for it #14970, where the spec's own TSDoc states the producer obligation as part of the already-landed contract).yes. Unsure ⇒yes.ListAgentsanswers it in one call. The predecessor reported a round as dispatched that never was, and repeated it for several rounds, because the list was memory.needs:contract-reviewis ⛔ never pre-hung (maintainer 2026-08-28): an open carrier must always mean a real pending review. It goes on the card and the PR in one stroke when the diff exists. Two lane cards were corrected for this by the director seat while this round ran ([finding]SqlDriver.update()keeps an explicitPromise[any]while returningnullon a miss — the same published-type mask #14434 removed from driver-memory, inherited by TursoDriver and SqliteWasmDriver #14438, No platform object carries a timezone, so every app that computes a date boundary has to invent one — and each will invent it differently #14238) — ⛔ do not re-hang either by reflex.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· 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 ·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 —docs/adr/**,.claude/**,skills/**,AGENTS.md,CLAUDE.md): draft-only. ⛔ Never flip ready / enqueue / arm auto-merge on own judgment; ⛔ never approve from an agent seat.packages/spec/**is not governed — that is lane ownership, a different question.