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
Written at the end of a domain:ui dispatch shift (session session_013hfmP9hoMd3dJwTh85J4yB, 2026-08-31). Ordered by "what will burn you", not by topic. Everything here is measured on this repo today unless marked otherwise.
1. Things that will actively mislead you
The pm:blocked → pm:queue unlock scan is not the real instrument. Its own comment says so: check-half-states.mjs (H19's implementation) is not installed in this repo, so the unlock is triage recomputing the same criterion by API. Both cards it unlocked for me this shift had moved substantially since filing:
⇒ Read the type and the code, not the card. A card body is a snapshot of a tree that has since moved.
The zero-quota payload channel drops comments. Measured: reading #6521 returned 2 of 3 comments and the one it dropped was the newest — the PM's own dispatch order. It also showed assignees: [] where REST showed the card assigned. Independently reproduced in a second repo (objectstack#13373). ⇒ Do not assume your dispatch order was seen.
The REST issues-list Link: rel="next" cursor silently truncates — 102 of 287 open issues (objectstack#13900). Use &page=N and cross-check your total against open_issues_count.
⭐ The part that matters: a known-hit control cannot detect this class. A truncated corpus still returns your known hit. Only a count cross-check does. I made exactly this mistake — ran a control, proved the channel discriminates, and wrote it up as though I had proved the corpus complete. Those are different claims.
2. Merge-queue mechanics
Confirm enqueue by prefix match on refs/heads/gh-readonly-queue/main/pr-<n>-*, never from the API return (the return is uninformative).
⭐ But ref-absent is NOT evidence of not-enqueued. Propagation lag measured 29s / 31s / 29s on three occasions — and then 7.5–14 minutes on a fourth. Prefix match confirms positives only. Confirm a negative only by the queue demonstrably moving past the entry, or by reading the PR's merge-queue state via API. I nearly re-armed a PR that was already queued; not doing so was the single most useful restraint of the shift.
Ejection vs merge: a front merge moves main; an ejection rebuilds every entry with a new sha. Happened twice today (once freezing main ~80 minutes).
Prediction judge:git merge-base --is-ancestor <QUEUE-BUILD sha> origin/main — never a branch head. Squash merges mean branch heads never become ancestors.
Conflict pre-check before arming:git merge-tree --write-tree --name-only origin/main <head>; exit 0 = clean. This caught a real conflict earlier in the week that would otherwise have landed in the queue.
Queue state, main, branch heads, PR diffs and file contents are all readable over git at ZERO API quota. When the API rate-limits (it did, for ~30 minutes today), you can still read everything and only writes are blocked. Use it.
3. Repo rules that are mechanically enforced
⛔ major is refused by scripts/check-changeset-no-major.mjs. objectui's major is a cross-repo pin to @objectstack's major, not a severity dial. In-repo breaking changes ship minor/patch with the break spelled out in the changeset body. Do not set OBJECTUI_ALLOW_MAJOR=1; do not edit the gate.
Governed surfaces (never ready/queue/auto-merge without a pinned GOVERNED_APPROVERS approval on the current head): docs/adr/, .claude/, skills/, AGENTS.md, CLAUDE.md. Read directly from scripts/check-governed-queue-guard.mjs:199-203. .github/** and content/docs/** are NOT governed.
⛔ Never edit content/docs/releases/ in a code PR.
vitest: root-relative form ONLY — a package-directory run silently re-roots onto apps/console. eslint: plain form; ⛔ never --no-inline-config.
#6709 — waits on a @objectstack/specpublish that does not exist. The source landed on objectstack main (8c6a7fc0b) but packages/spec/package.json there still reads 17.2.0. Restart-when: is keyed to the publish, not the source landing.
#7072 — #6810 (a catalog sweep) was never checked as a possible absorber. #6902/#6933 are fold-only, awaiting a carrier.
⭐ #7081 is not a docs nit.zod-mirror-parity.test.ts asserts one direction only (the mirror accepts everything TS declares), so a mirror wider than the declaration is invisible to it. It is graded priority:p2 for that reason: the docs rows are consequences, the blind gate is the cause.
5. Two rulings of mine you may want to overturn
① A maintainer ruling discharges the human floor — but only the floor its own text names. I extended #6881's 条款② clause (「PR 建立时挂 needs:contract-review 走复审链」) to #5934, whose ruling instead says 「已发布类型成员的 breaking 删除属人工地板,本裁定即该地板动作的记录」 — the floor is discharged by the ruling itself, with no review-chain clause anywhere in it. I held PR #7065 for a gate that was never prescribed. Read each ruling literally; do not generalise a clause across cards.
② assignees is PM-written; devs post claim comments only. All agents share one GitHub identity, so the assignee field cannot prove a claim. A claim is assign plus a claim comment carrying a session ID and branch — and you must re-read the comments before writing code, because an earlier claim with a different session ID means it is taken whatever the assignee says.
6. ⭐ The one pattern worth carrying forward
Every substantive defect this shift was the same shape: a one-directional check.
A known-hit dedupe control proves discrimination ⇒ corpus truncation is invisible (objectstack#13900)
My own dispatch orders enumerated only the over-stated rows ⇒ the under-stated half nearly shipped untouched
And it applies to the PM seat itself. I was corrected six times this shift — the patch instruction twice, a type I read as boolean that is boolean | string, a fixture count short by 24 occurrences, a hold placed on a card whose ruling forbade it, and a census whose pathspec returned zero for every query including a literal I knew was present (caught only because I ran a control that also returned zero).
The root of all six is the same: treating "I read a result" as "I measured". My dispatch orders required devs to verify the card's premises. They did not require verifying mine. ⇒ Put your own readings in the premise-verification gate, and tell devs that a clean stop on a falsified PM premise is a full success. Four of five orders this shift came back with a premise corrected, and every one of those corrections improved the result.
Written at the end of a
domain:uidispatch shift (sessionsession_013hfmP9hoMd3dJwTh85J4yB, 2026-08-31). Ordered by "what will burn you", not by topic. Everything here is measured on this repo today unless marked otherwise.1. Things that will actively mislead you
The
pm:blocked→pm:queueunlock scan is not the real instrument. Its own comment says so:check-half-states.mjs(H19's implementation) is not installed in this repo, so the unlock is triage recomputing the same criterion by API. Both cards it unlocked for me this shift had moved substantially since filing:overlay/menubar.mdxpublishes an inventedMenubarIteminterface — it is what taught the fixture its wrong spellings #6521's entire six-row "actually shipped" evidence table was obsolete — The three menu renderers run two different separator dialects against oneMenuItem, and the declared one cannot parse green #6523 had restructuredMenuIteminto a discriminated union, satisfying four of its rows.sonnerandbutton-groupstill document four more props no shipped type declares #6347's sonner half was already landed (PR fix(examples,docs): correct the toast demos to spellings the engine executes #6498 under finding(types,examples): every toast demo hangs an action object offonClick, which is declared as a function and read by no dispatcher #6250).⇒ Read the type and the code, not the card. A card body is a snapshot of a tree that has since moved.
The zero-quota payload channel drops comments. Measured: reading #6521 returned 2 of 3 comments and the one it dropped was the newest — the PM's own dispatch order. It also showed
assignees: []where REST showed the card assigned. Independently reproduced in a second repo (objectstack#13373). ⇒ Do not assume your dispatch order was seen.The REST issues-list
Link: rel="next"cursor silently truncates — 102 of 287 open issues (objectstack#13900). Use&page=Nand cross-check your total againstopen_issues_count.⭐ The part that matters: a known-hit control cannot detect this class. A truncated corpus still returns your known hit. Only a count cross-check does. I made exactly this mistake — ran a control, proved the channel discriminates, and wrote it up as though I had proved the corpus complete. Those are different claims.
2. Merge-queue mechanics
refs/heads/gh-readonly-queue/main/pr-<n>-*, never from the API return (the return is uninformative).main; an ejection rebuilds every entry with a new sha. Happened twice today (once freezingmain~80 minutes).git merge-base --is-ancestor <QUEUE-BUILD sha> origin/main— never a branch head. Squash merges mean branch heads never become ancestors.git merge-tree --write-tree --name-only origin/main <head>; exit 0 = clean. This caught a real conflict earlier in the week that would otherwise have landed in the queue.main, branch heads, PR diffs and file contents are all readable over git at ZERO API quota. When the API rate-limits (it did, for ~30 minutes today), you can still read everything and only writes are blocked. Use it.3. Repo rules that are mechanically enforced
majoris refused byscripts/check-changeset-no-major.mjs. objectui's major is a cross-repo pin to@objectstack's major, not a severity dial. In-repo breaking changes shipminor/patchwith the break spelled out in the changeset body. Do not setOBJECTUI_ALLOW_MAJOR=1; do not edit the gate.patch.apps/siteis"private": true, socontent/docs/**publishes nothing andcheck-changeset-presencereports "no changeset is owed"; apatchbump falsely claims a released package changed. I got this wrong twice in one shift (finding(docs):overlay/menubar.mdxpublishes an inventedMenubarIteminterface — it is what taught the fixture its wrong spellings #6521 and docs(components):sonnerandbutton-groupstill document four more props no shipped type declares #6347) and both devs corrected me independently.GOVERNED_APPROVERSapproval on the current head):docs/adr/,.claude/,skills/,AGENTS.md,CLAUDE.md. Read directly fromscripts/check-governed-queue-guard.mjs:199-203..github/**andcontent/docs/**are NOT governed.content/docs/releases/in a code PR.apps/console. eslint: plain form; ⛔ never--no-inline-config.plaintextfence (finding: JSON doc snippets are checked by nothing — the ts/tsx gate cannot see them, andBaseSchema.passthrough()makesobjectui validateaccept arbitrary undeclared keys #5250).check-doc-component-typesreads type string literals;check-doc-snippet-typescompilests/tsxonly. A docs correction inside such a fence is unverified by CI — green means "nothing else broke", not "the correction is right". PR docs(components): publish button-group's shipped surface, in both directions #7078 added the first pin test that closes this for one page; it is the model.skip-changesetexists as a label but no workflow or script reads it.4. Open state you inherit
needs:contract-review). Green and unarmed on purpose.@objectstack/specpublish that does not exist. The source landed on objectstackmain(8c6a7fc0b) butpackages/spec/package.jsonthere still reads 17.2.0.Restart-when:is keyed to the publish, not the source landing.⭐ #7081 is not a docs nit.
zod-mirror-parity.test.tsasserts one direction only (the mirror accepts everything TS declares), so a mirror wider than the declaration is invisible to it. It is gradedpriority:p2for that reason: the docs rows are consequences, the blind gate is the cause.5. Two rulings of mine you may want to overturn
① A maintainer ruling discharges the human floor — but only the floor its own text names. I extended #6881's 条款② clause (「PR 建立时挂
needs:contract-review走复审链」) to #5934, whose ruling instead says 「已发布类型成员的 breaking 删除属人工地板,本裁定即该地板动作的记录」 — the floor is discharged by the ruling itself, with no review-chain clause anywhere in it. I held PR #7065 for a gate that was never prescribed. Read each ruling literally; do not generalise a clause across cards.②
assigneesis PM-written; devs post claim comments only. All agents share one GitHub identity, so the assignee field cannot prove a claim. A claim is assign plus a claim comment carrying a session ID and branch — and you must re-read the comments before writing code, because an earlier claim with a different session ID means it is taken whatever the assignee says.6. ⭐ The one pattern worth carrying forward
Every substantive defect this shift was the same shape: a one-directional check.
zod-mirror-parity.test.tschecks one direction ⇒ a wider mirror is invisible (finding(types):DropdownMenuSchema.triggerdeclares a singleSchemaNodewhile its zod mirror, its siblingContextMenuSchema, and its own shippeddefaultPropsall use the array form #7081)sonnerandbutton-groupstill document four more props no shipped type declares #6347)And it applies to the PM seat itself. I was corrected six times this shift — the
patchinstruction twice, a type I read asbooleanthat isboolean | string, a fixture count short by 24 occurrences, a hold placed on a card whose ruling forbade it, and a census whose pathspec returned zero for every query including a literal I knew was present (caught only because I ran a control that also returned zero).The root of all six is the same: treating "I read a result" as "I measured". My dispatch orders required devs to verify the card's premises. They did not require verifying mine. ⇒ Put your own readings in the premise-verification gate, and tell devs that a clean stop on a falsified PM premise is a full success. Four of five orders this shift came back with a premise corrected, and every one of those corrections improved the result.