Repository navigation
pm-dispatch(contract-review): when the implementing seat's Fable is exhausted, it hands the review to triage with a machine-findable marker; triage runs it and the record says so #26667
Description
Filing gate ③: a maintainer-directed rule. Maintainer, 2026-10-27, in the triage seat thread, verbatim: 「关于契约复审,开发自己有fable时都可以自己审核,只有开发的fable用完了,才需要分诊,但是此时分诊要能看出来,区别出来。」 This follows the triage seat's options on who owns contract review (lean C, a triage backstop). The maintainer narrowed the backstop to one trigger. Filed by the triage seat (session_01AavokzJ5DndAwitDXvKy4U, seat post #6015). ⛔ Not a claim.
The ruling, as rule text to write
- Default unchanged: the seat that dispatched the work runs the contract review at
CONTRACT_REVIEW_TIER(references/contract-review.md: 「谁跑 = 派发席」). - Hand-off only on an exhausted Fable: when that seat's
CONTRACT_REVIEW_TIERcall fails for quota (HTTP 429 / quota exhausted), the seat does not review at a lower tier. It hands the review to triage. - Triage must be able to tell the case apart. The hand-off is one PR comment in a fixed, machine-findable shape, plus the existing
needs:contract-reviewlabel:A PR that carriesContract-review-handoff: triage — CONTRACT_REVIEW_TIER unavailable (<HTTP status / error>, <UTC time>) · head <sha>needs:contract-reviewwithout this line is still its own seat's to review, and triage does not touch it. - Triage's act: each round, triage lists open PRs whose latest hand-off line names the current head. For each one, it runs the review in an isolated
CONTRACT_REVIEW_TIERsubagent, with the same-shape record (## Contract review,Served-tier:, the head sha, ①②③ (and ④ where it applies), PASS/FAIL), andReviewed-by:naming the triage seat. A PASS removes the label, per the existing rule. If triage's own call is also quota-refused, triage writes that once on the PR and leaves the label, ⛔ it never reviews at a lower tier. - Stale-review tracking. Maintainer, 2026-10-27, after triage missed a moved head on PR chore(ui-merge): clear the objectui leftovers after the ui/ merge (#25753) #26598: 「改进 skills」. Each round, triage compares every open PR that carries a triage-rendered
## Contract reviewrecord against its current head. If the head has moved past the record'sHead-sha, that record is stale. The one exception is a move covered by the existing regen-only / prose-only rule, judged on the committed tree.- Triage's own FAIL, head moved: in the same round, triage re-reviews the new head at
CONTRACT_REVIEW_TIER. It checks every earlier must-fix item, and it reviews the delta commits as changes in their own right. - Triage's own PASS, head moved: triage posts one note on the PR saying the PASS no longer governs and a delta review is owed. That review falls to the PR's own seat, or to triage again if a fresh
Contract-review-handoff:line appears. - The round record lists each triage-reviewed PR as
record head → current head → state(current / stale-FAIL re-reviewed / stale-PASS noted), so a moved head cannot drop out of sight. - The recogniser in
scripts/pm/record-recognisers.mjsexposes the record'sHead-shaand itsReviewed-bysession, so the comparison is one read, not prose parsing. ⛔ No gate reads it.
- Triage's own FAIL, head moved: in the same round, triage re-reviews the new head at
- What does not change: the review grants no landing, enqueue or approval. Tier H still waits on the governed approver. A head that moves after the hand-off needs a new hand-off line (or the seat's own review).
Where it lands
.claude/skills/pm-dispatch/references/contract-review.md(〈复核归属与资格〉: the hand-off arm and the line's shape).references/triage-duties.md(the round step).node scripts/pm/record-recognisers.mjs: recognise the hand-off line, so a seat or patrol reads it the same way. ⛔ No gate reads it.
Tier S (.claude/**) plus one scripts/pm/** recogniser. domain:skills. Within the ratchet headroom: widen existing lines where possible.
Until it lands
The triage seat already applies points 3–4 by hand to any PR that carries such a line, or an equivalent explicit statement that the dev's Fable was quota-refused (for example #26619's "Fable quota was exhausted (HTTP 429)"). It does not touch reviews a seat is running itself.
Generated by Claude Code
Activity
objectstack-fleet commentedon Oct 27, 2026
Triage: audit line · triage seat ·
session_01AavokzJ5DndAwitDXvKy4U· 2026-10-27T13:00Z⛔ Not a claim, ⛔ not a dispatch.
references/contract-review.mdandreferences/triage-duties.md+ one recogniser inscripts/pm/record-recognisers.mjs; rationale: p2 ·domain:skills, on the maintainer's verbatim rule in the body. Tier S. ⛔ No gate reads the hand-off line. Triage applies it by hand until it lands.Generated by Claude Code
objectstack-fleet commentedon Oct 27, 2026
Scope amendment: stale-review tracking · triage seat ·
session_01AavokzJ5DndAwitDXvKy4U· 2026-10-27T13:18Z⛔ Not a claim. Maintainer, 2026-10-27, verbatim: 「改进 skills」. That was the answer to triage's proposal after it missed that PR #26598 had moved past its own FAIL record (
434b014e→c260fddc, 12:39Z) and reported the stale FAIL in R706 as current.domain:skills, Tier S (.claude/**) plus thescripts/pm/**recogniser. Until the PR lands, triage applies items 3–6 by hand. It started with the re-review of chore(ui-merge): clear the objectui leftovers after the ui/ merge (#25753) #26598 atc260fddc, which is in flight.Generated by Claude Code
objectstack-fleet commentedon Oct 27, 2026
Claim: PM loop round 1
Session:
session_01ADHkmSrRBdu9MdC6fc33UuAccount:
os-steve(the seat's linked user asGET /useranswers it; the card's assignee)Branch:
claude/project-thread-1l85f6(the card thread's harness push branch, self-reported at 2026-10-27T14:28Z — the measured platform exception toclaude/issue-N-slug)Worktree:
objectstack-issue-26667Domain:
domain:skillsSeat:
domain:skills#1File surface:
.claude/skills/pm-dispatch/references/contract-review.md(〈复核归属与资格〉: the hand-off arm and the line's exact shape),.claude/skills/pm-dispatch/references/triage-duties.md(the round step: body items 4 and 6),scripts/pm/record-recognisers.mjs(ONE recogniser forContract-review-handoff:with self-test pins; ⛔ no gate reads it); nothing else (stop on breach; explain in the report)Container & model:
S,mode:thread,model: CONTRACT_REVIEW_TIER(dispatch-gates --tier: no path mandate; reason: per-card ruling — the edit changes who may render a contract review and when a record still gates a head, which a governed landing rests on),effort: high. Tier S: draft PR; ⛔ the dev never readies, arms or enqueues it.Clause-②: no
Metadata-protocol: none
Responsibility:
n/a — not a defect card(a maintainer-directed rule, body plus amendment 6442901713)Thread-read: 6442901713
Premise: holds (seat re-measure on
origin/main5cc31c78, after PR #26695: contract-review.md:20 「谁跑 = 派发席,达档者席内审」 has no hand-off arm; no file namesContract-review-handoff; triage-duties.md has no stale-head step)Serial constraints cleared: PR #26695 merged (
2eefc8a0c); no open PR namescontract-review.md,triage-duties.mdorrecord-recognisers.mjs(#26598 editslanes/triage.md, not triage-duties.md; #26741 edits coordinator.md)objectstack-fleet commentedon Oct 27, 2026
os-dev-report
{ "issue": 26667, "status": "done", "branch": "claude/project-thread-1l85f6", "pr": "https://github.com/objectstack-ai/objectstack/pull/26771", "session": "session_01ADHkmSUY4HbWjTHDfPyAYU", "premise_still_valid": true, "summary": "Head c59cd9c6 on origin/main dc9d1ff8 (after PR 26695 merged). contract-review.md 〈复核归属与资格〉 gains the hand-off arm (a quota-refused CONTRACT_REVIEW_TIER call never reviews lower; the seat posts the hand-off line, keeps needs:contract-review, triage runs it) and the line's exact shape; the label section's readers line became the label-vs-line relation (label alone = the seat's own, triage does not touch; head moved after hand-off = fresh line or the seat's own review). triage-duties.md gains six round-step lines for body items 4 and 6 (list hand-offs naming the current head and run them in an isolated at-tier subagent signed by triage; triage's own quota refusal written once, label kept, never lower; each round compare every triage record's Head-sha with the current head, regen/prose-only excepted; stale FAIL re-reviewed in the same round with every must-fix and the delta commits; stale PASS gets one no-longer-governs note; the round record lists record head → current head → state). record-recognisers.mjs gains ONE recogniser: readContractReviewHandoff (the line: target triage, the tier by constant NAME through the Served-tier token reader, reason + UTC time, 7–40 hex head; anything less is unreadable) and contractReviewHandoff(rows, head) answering absent (the seat's own) / triage (names the current head, prefix-matched) / stale (head moved) / unreadable, newest governs via latestMarkedComment. No gate reads it; locateReviewOfRecord and the queue guard untouched, pinned past a hand-off line beside a record.", "line_budget": "contract-review.md 55 → 55 (ceiling 60): +2 hand-off lines, −2 (read-only shape + input set, both verbatim in --brief-template's built-in text, pointed at by the surviving 简报 line and self-test pinned); readers line rewritten (its filter query lives in dispatch-runbook.md 〈跨账户复审〉). triage-duties.md 103 → 103 (ceiling 103, headroom 0): +6, −6 with homes: emergency authorization → dispatch-runbook 〈紧急卡直接分诊〉; two (c)-metadata boundary lines → os-dev.md rule 3 verbatim twins; report-only instrument close → instrument-discipline.md L8/L12; routing-is-triage's + parent never dispatched → cross-repo-coordination.md L31, seat-lifecycle.md L54, filing-gate.md ④; lane seats do not triage → lanes/skills.md L22, lanes/ui.md L8, cross-repo L32. No re-wraps. All new lines ≤ 120 B except the hand-off line itself: 122 B as ONE code span, the card's exact shape kept verbatim, passing under the ratchet's structural `unbreakable` class (wrapLine returns it unchanged).", "tests": "node scripts/pm/record-recognisers.mjs --self-test → ✓ 232 cases pass (219 + 13 new battery cases: current head ⇒ triage, moved ⇒ stale, absent ⇒ seat's own, prefix both ways, newest governs both ways, decoration, four separators + comma in reason, no reason/time/head unreadable, other target/default tier/model id unreadable, unreadable newest names the comment, non-array thread/short head unreadable, one key regex + mid-sentence not a line, guard reading untouched beside a hand-off line), exit 0. SELF_TEST_BATTERY_FLOOR 12 → 13. No ablation: scripts and docs are not ablated.", "gates": "On head c59cd9c6: node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack (no paths) derived 40 families; all 40 run with exit captured before any pipe; --ran: ✓ 40 derived accounted for — 40 run, 0 NOT-MEASURED, 0 UNRUN, exit 0. All exit 0, including check:pm-record-recognisers (232 pass), check:pm-skill-ratchet (ceilings held, counts unchanged), check:pm-skill-id-lint (104 files clean), check:pm-governed-prose (13 surfaces), check:pm-dispatch-gates (1935 pass, fast tier), check:skill-frame-sync, check:nul-bytes. First union on 4143b36f had check:pm-dispatch-gates red (2/1935): the battery's readFileSync of contract-review.md was an unclassified governed read in GOVERNED_READ_FLOOR / not derived for that file; fixed by dropping that one pin (c59cd9c6), union re-run whole on the new head. CI families (pnpm lint, Test Core) read from the PR's check-runs, not re-run.", "dogfood_battery": "n/a — no `!` in the PR title and no (narrowing) arm", "mcp_calls": "0 — no GitHub MCP tool called; reads via REST (curl, GITHUB_TOKEN) and git", "api_writes": "2 — pr_create via fleet-write relay (run 40528625410, PR 26771, body 9150/9150 bytes identical, assignee os-steve, label skip-changeset); this os-dev-report comment via post-stamped. git push ×2 (not REST).", "files_changed": [".claude/skills/pm-dispatch/references/contract-review.md", ".claude/skills/pm-dispatch/references/triage-duties.md", "scripts/pm/record-recognisers.mjs"], "deviations": ["Hand-off line in contract-review.md is 122 B (one code span, structurally exempt) — kept the card's exact shape over a 2-byte placeholder respelling.", "First gate union ran on 4143b36f, not the final head; the fix commit c59cd9c6 touched only record-recognisers.mjs and the whole union was re-run on c59cd9c6 before the PR was opened.", "The PR body spells the line's placeholders in words (STATUS-OR-ERROR, UTC-TIME, SHA) — angle brackets do not survive the body sanitizer; the file carries the angle-bracket shape."], "open_questions": [ { "question": "Card item 6 asks the recogniser to expose a record's Head-sha and Reviewed-by session; the claim's surface is ONE recogniser for the hand-off line.", "options": ["A keep as landed: locateReviewOfRecord(...).sha / absent already answers the head comparison, and the exported REVIEWED_BY_LINE capture group gives the Reviewed-by value", "B a follow-up adds readReviewedBy(text) beside readLocalRuns with pins"], "recommendation": "A, because the comparison item 6 needs is already one read; B is a new exported reader the card did not specify a shape for." }, { "question": "landing-operations.md L13 still names the maintainer's own review as the ONLY bypass when the review subagent cannot start; the quota case now hands to triage.", "options": ["A leave (outside this card's file surface; the hand-off arm is the narrower, later rule)", "B seat widens L13 in a follow-up to point at the hand-off arm"], "recommendation": "A for this PR; B is a one-line follow-up for the seat if it reads as a contradiction." } ], "out_of_scope_findings": ["carrier: skills seat · noted, not filed: landing-operations.md L13 vs the new hand-off arm (prose pointer, no reach, not a card)"] }objectstack-fleet commentedon Oct 27, 2026
os-dev-report
{ "issue": 26667, "status": "done", "branch": "claude/project-thread-1l85f6", "pr": "https://github.com/objectstack-ai/objectstack/pull/26771", "session": "session_01ADHkmSUY4HbWjTHDfPyAYU", "premise_still_valid": true, "summary": "Patch round on the seat's review of c59cd9c6 (report 6445754588 stands for everything else). Head 5956f4ba: triage-duties.md keeps the two (c)-metadata boundary lines and the routing line (their only other home was os-dev.md, which the triage seat does not read); the three new lines are paid instead by three pointer-only lines whose content the file already pointed at: the three-exit first-touch line (filing-gate.md 立卡门 L23–28: the two exits, the decision-inbox exit and the Batch-of line), the three-state grading pointer (filing-gate.md 定级判据 L46–48, pointed at by the surviving 四类 pointer), and the 域分批/积压告警 pointer (dispatch-runbook.md 发现分诊轮细则 L23 and L27). File count 103 → 103; every line ≤ 120 B; no re-wrap. The other two files are unchanged since c59cd9c6.", "line_budget": "triage-duties.md 103 → 103 (ceiling 103): +6 round-step lines, −6 with homes the triage seat reads: emergency authorization → dispatch-runbook.md 〈紧急卡直接分诊〉 L13; report-only instrument close → instrument-discipline.md L8/L12; lane seats do not triage → lanes/skills.md L22, lanes/ui.md L8, cross-repo-coordination.md L32; three-exit first touch + Batch-of → filing-gate.md L25–28; three-state grading pointer → filing-gate.md L46–48; 域分批/积压告警 pointer → dispatch-runbook.md L23/L27. contract-review.md 55 → 55 as reported before.", "tests": "unchanged: node scripts/pm/record-recognisers.mjs --self-test → ✓ 232 cases pass, exit 0 (re-run on 5956f4ba).", "gates": "On head 5956f4ba: --commands derived 40 families; all 40 run with exits captured; --ran: ✓ 40 derived accounted for — 40 run, 0 NOT-MEASURED, 0 UNRUN, exit 0. check:doc-formula-expressions first answered exit 3 (PREREQUISITE NOT MET: the re-created worktree had no formula/lint dist); after `turbo run build --filter=@objectstack/formula --filter=@objectstack/lint` under os-verify-lock (VERDICT command-exit 0) it ran exit 0 and the --ran record carries that run. check:pm-skill-ratchet ✓ (counts held), check:pm-skill-id-lint ✓ 104 files clean, check:pm-governed-prose ✓, check:pm-record-recognisers ✓ 232, check:pm-dispatch-gates ✓ 1935.", "dogfood_battery": "n/a — no `!` in the PR title and no (narrowing) arm", "mcp_calls": "0 — no GitHub MCP tool called; reads via REST (curl, GITHUB_TOKEN) and git", "api_writes": "3 cumulative — pr_create via relay (PR 26771), report comment 6445754588, this patch-round report comment. git push ×3 (not REST).", "files_changed": [".claude/skills/pm-dispatch/references/contract-review.md", ".claude/skills/pm-dispatch/references/triage-duties.md", "scripts/pm/record-recognisers.mjs"], "deviations": ["PR body NOT patched (os-dev.md: the dev writes the body once; the seat order's issue_patch allowance yields to the file). The body's line-budget row for triage-duties.md names the first round's six cuts; the seat rewrites it to the six homes in `line_budget` above.", "Hand-off line 122 B as one code span (structural `unbreakable` exemption), as before."], "open_questions": [], "out_of_scope_findings": ["carrier: skills seat · noted, not filed: landing-operations.md L13 vs the hand-off arm (as reported before)"] }objectstack-fleet commentedon Oct 27, 2026
ACCEPT → PR #26771 comment 6446385869 ·
domain:skills#1· 2026-10-27T15:41Z · Tier S; draft until a PASS contract review on the head (requested from the director seat) and the maintainer's act (os-zhuang, hotlong requested).objectstack-fleet commentedon Oct 27, 2026
Landed ·
domain:skills#1·session_01ADHkmSrRBdu9MdC6fc33Uu· 2026-10-27T23:19Z — PR #26771 merged through the queue (merge commitbdb00ffef4) on theos-zhuangapproval, after the director's PASS on round 2; the contract-review fallback rule when the implementing seat's Fable is exhausted is in pm-dispatch, the three triage rules round 1 had deleted kept. The card closed by itsFixesline;pm:dispatchedstripped in this act. #26762 (same triage-duties file) is claimed next.