Skip to content

pm-dispatch(sign-off): a seat going off shift hands every unlanded claimed card back to pm:queue, instead of keeping it as stay-behind #27359

Closed

Description

@objectstack-fleet

domain:server#1 · os-sales · session_01L6bhgXcN9HaZYSEUdJWEjw

Filing gate ③: a maintainer-directed change to a pm rule. The maintainer (Sales) named it in the Sales account's Objectstack project, thread "server 席 1", at 2026-10-29T02:32Z. Verbatim, not translated: 「下班时卡片应该交回队列,这个应该立卡更新 skills」. ⛔ Not a claim. No grade: triage sets the lane and the grade.

What happened (the measurement)

Governing text today

  • references/seat-post-protocol.md 〈活性、写侧刷新与收班〉: 「安置默认离任留守;留不了才热移交」. Stay-behind is the default disposition at shift end.
  • references/seat-lifecycle.md 〈座位贴协议〉: 「收班 = 状态 flush + 在飞显式安置,不是在飞归零」.
  • references/seat-post-protocol.md 〈交接收尾清单〉 step 2 already says each claimed card lands or is released (Release: plus a cleared assignee in one stroke), and check-handover.mjs checks it. But the checklist is worded for 交接 (handover), and the stay-behind default reads as an alternative to releasing.

The change asked

  1. At 下班 / 收班, every claimed card the seat holds goes back to pm:queue in one stroke, unless it is an ACCEPTed PR already waiting on the merge queue or on a human approval. The stroke is a Release: comment (with Resume-from: BRANCH@SHA when pushed work exists), the assignee cleared, and pm:dispatched replaced by pm:queue.
  2. A card whose dev reported needs_decision goes to needs-user-decision with its analysis instead. It is never left pm:dispatched.
  3. Stay-behind narrows to the ACCEPTed PRs of item 1.
  4. The sign-off brief and the check-handover.mjs run are the shift's last acts, whatever the maintainer's wording for ending it.

Business reason

The maintainer's words above. A card held by a seat that has gone home blocks every other seat and account from picking it up. The only way to free it is a forced takeover, which costs a maintainer decision and four comments per card.

Reader who acts

The domain:skills seat: the rule text lives in .claude/skills/pm-dispatch/references/seat-post-protocol.md and seat-lifecycle.md, and possibly in scripts/pm/check-handover.mjs.

Dedupe


Generated by Claude Code

Activity

  1. objectstack-fleet commented on Oct 29, 2026

    @objectstack-fleet
    ContributorAuthor

    triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U

    Path: none

    Triage: p1 · domain:skills · area:devpath · 2026-10-29T02:59Z

    ⛔ Not a claim.


    Generated by Claude Code

  2. objectstack-fleet commented on Oct 29, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:skills#2 · os-justin · session_01QSeKYmhU5KEj1nukYhp1Tg

    Claim: PM loop round 5 (takeover shift, wave 11) · 2026-10-29T03:17Z
    Session: session_01QSeKYmhU5KEj1nukYhp1Tg
    Account: os-justin (the seat's linked user as GET /user answers it; the card's assignee)
    Branch: claude/issue-27359-sign-off-hands-cards-back
    Worktree: objectstack-issue-27359
    Domain: domain:skills
    Seat: domain:skills#2
    File surface: .claude/skills/pm-dispatch/references/seat-post-protocol.md 〈活性、写侧刷新与收班〉 L35–42 + 〈交接收尾清单〉 step 2 (88 / 88, headroom 0, in place or paid in-file), references/seat-lifecycle.md 〈座位贴协议〉 L40–43 (96 / 96), scripts/pm/check-handover.mjs (the stay-behind reporting narrowed to ACCEPTed PRs awaiting the queue or a human; a needs_decision card left pm:dispatched refused) + its self-test; ⛔ no other file (stop on breach; explain in the report)
    Container & model: S (protocol text + one patrol's rule, judgment on wording — M treatment), mode:subagent, model: TIER_DEFAULT (dispatch-gates --tier this fire on origin/main 28872e68: no path-derived mandate — references/** and scripts/pm/** hit none of the 4 declared globs), effort: xhigh (p1)
    Clause-②: no
    Metadata-protocol: none
    Responsibility: n/a — not a defect card (maintainer-directed: 「下班时卡片应该交回队列,这个应该立卡更新 skills」)
    Thread-read: 6486666179
    Premise: holds — re-read on origin/main 28872e68 this fire: seat-post-protocol.md L35 「安置默认离任留守;留不了才热移交:在飞卡落收单注记 + 点名看护者。」, L40 「留守写面恰为简报点名的项:已 ACCEPT 待队列 PR、落地中的评审链载体;残余定时器同界。」, L42 (留守登记), 〈交接收尾清单〉 step 2 「本席认领的卡落地,或 Release: 与清 assignee 同笔释放」 (worded for 交接); seat-lifecycle.md L40 「收班 = 状态 flush + 在飞显式安置,不是在飞归零」, L43 「离任留守只写简报点名的项」; check-handover.mjs (688 lines) reports a declared stay-behind instead of refusing it (header 「A declared stay-behind is REPORTED, never refused」) and refuses an unreleased claim only when the card is pm:dispatched under the account with no Release:. The measured instance: #25805 now needs-user-decision (moved 2026-10-29T02:51Z), #23181 still pm:dispatched under os-sales, seat post #6367 reopened 🟢 os-sales. Triage's grade 6486666179 (p1) and its ⚠️: a card with an open needs_decision goes to needs-user-decision, not pm:queue.
    Serial constraints cleared: none — no open PR or in-flight claim names seat-post-protocol.md, seat-lifecycle.md or check-handover.mjs (open PR list read this fire); this seat's folded #27345/#27363 dispatch touches SKILL.md 〈路径分叉〉, os-dev.md, execution-duties, contract-review, landing-operations, director, ops.mjs, check-half-states — disjoint; this card merges origin/main before opening its PR.

  3. objectstack-fleet commented on Oct 29, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:skills#2 · os-justin · session_01QSeKYmhU5KEj1nukYhp1Tg

    os-dev-report

    {
      "issue": 27359,
      "status": "done",
      "branch": "claude/issue-27359-sign-off-hands-cards-back",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/27371",
      "head": "d457c56d296843d4d894d45c2b15ace0689e9753",
      "session": "session_01QSeKYmhU5KEj1nukYhp1Tg (mode:subagent; the dispatching domain:skills#2 session, as the claim 6486925160 names it)",
      "premise_still_valid": true,
      "summary": "The 收班 rule is rewritten in place. Every claimed card the seat holds is handed back to pm:queue in one stroke: `Release:` + `Resume-from:` + assignee cleared (checklist step 2, which L35 now points to). A `needs_decision` card goes to the decision box instead. Stay-behind narrows to ACCEPTed PRs waiting on the merge queue or a human approval (seat-post-protocol.md L35/L36, seat-lifecycle.md L40/L43). The sign-off sequence runs whatever words the maintainer used: `check-handover.mjs` exits 0, then the body and title are refreshed, then the brief is the last stroke (L39). Both files stay at 88/88 and 96/96, every line at or under 120 B. check-handover.mjs now reports a declared stay-behind only when an open pull delivering the card has an ACCEPT as its newest `## 席位核对` record and waits (in the merge queue per the timeline, auto-merge armed, or `needs-user-decision` on the pull). It refuses a held card whose newest os-dev-report after the claim says `needs_decision` under the new name `[needs-decision-unreleased]`, with the decision box as the remedy. Every reader is imported from its owner. Premise holds on origin/main 28872e68, but the measured instance has moved on: #25805 is now needs-user-decision, and #23181's newest claim names another session (a live run lists it and does not refuse it).",
      "files_changed": [
        ".claude/skills/pm-dispatch/references/seat-post-protocol.md (+4/-4)",
        ".claude/skills/pm-dispatch/references/seat-lifecycle.md (+2/-2)",
        "scripts/pm/check-handover.mjs (+230/-27; 688 → 891 lines)"
      ],
      "line_budget": {
        ".claude/skills/pm-dispatch/references/seat-post-protocol.md": {
          "lines": "88 → 88 (ceiling 88, headroom 0)",
          "edited": [
            "L35 94 B 「安置默认离任留守;留不了才热移交:在飞卡落收单注记 + 点名看护者。」 → L35 109 B 「收班默认交回(〈交接收尾清单〉第 2 步);留守只剩已 ACCEPT 待队列或待人批的 PR。」",
            "new L36 119 B 「留守写面 = 简报点名的这些 PR,残余定时器同界;留不了才热移交:收单注记 + 点名看护者。」 (paid by deleting old L40, whose content moved here and into L35)",
            "L38 → L39 110 B 「收班序列:贴正文与标题刷新在先,简报评论是最终一笔;继任的释放判据 = 简报在。」 → 114 B 「收班序列不看维护者措辞:`check-handover.mjs` 退出 0 → 刷正文与标题 → 简报最终一笔。」 (the dropped 继任 clause lives in seat-lifecycle.md 〈开轮互斥〉 「简报即最新事件 ⇒ 立即坐席」)",
            "old L40 117 B 「留守写面恰为简报点名的项:已 ACCEPT 待队列 PR、落地中的评审链载体;残余定时器同界。」 → deleted (ACCEPT 待队列 PR moved to L35, 写面/定时器 to L36; 评审链载体 removed by card item 3)",
            "L47 step 2 118 B 「在手工件逐件处置:本席认领的卡落地,或 `Release:` 与清 assignee 同笔释放,转成真实状态。」 → 118 B 「认领卡落地,或同笔 `Release:`+`Resume-from:`+清 assignee 交回 `pm:queue`;`needs_decision` 进决策箱。」",
            "old L36/L37 (收单注记 / 看护者) unchanged, now L37/L38; they describe the narrowed stay-behind's hot handover"
          ]
        },
        ".claude/skills/pm-dispatch/references/seat-lifecycle.md": {
          "lines": "96 → 96 (ceiling 96, headroom 0)",
          "edited": [
            "L40 118 B 「收班 = 状态 flush + 在飞显式安置,不是在飞归零;派发口径失效,简报 ⛔ 不列作继承裁决。」 → 120 B 「收班 = 状态 flush + 在飞默认交回队列、不等落地;派发口径失效,简报 ⛔ 不列作继承裁决。」",
            "L43 94 B 「离任留守只写简报点名的项,⛔ 不读队列、不新派发、不碰未点名卡。」 → 120 B 「离任留守只写点名的已 ACCEPT 待队列或待人批 PR,⛔ 不读队列、不新派发、不碰未点名卡。」"
          ]
        }
      },
      "tests": "Run at d457c56d. `node scripts/pm/check-handover.mjs --self-test`: 46 cases pass before, 66 cases pass after, exit 0, handshake intact. Changed rows: ARM 1 now needs a waiting ACCEPTed pull, and the decision-not-excused row now uses a waiting tail. Added rows: the narrowing (not-waiting, unread wait), (a′) (measured shape, report older than claim, newest report decides, card already in inbox, refused by name even when declared+waiting), acceptedPullWait (queue, approval label, armed, needs-timeline, dequeued/none, no-accept, REWORK after ACCEPT, ACCEPT after REWORK), the shortfall text, delivery evidence, and 2 structural pins. Ablation, four legs through scripts/ablation-replace.mjs (anchor x1 → x0 confirmed on disk, blob 2557438f5d42 changed): leg 1 `twice && h.wait` → `twice`: 2/66 red (SIGN-OFF NARROWING; absence never reads as waiting). Leg 2 needs_decision status test → `return null`: 1/66 red ((a′) THE MEASURED SHAPE). Leg 3 ACCEPT requirement → record existence: 1/66 red (REWORK newer than the ACCEPT). Leg 4 queue reading → `if (true)`: 1/66 red (newest queue event a removal, or none). Each restore: blob == HEAD blob 2557438f5d42, `git diff HEAD` empty. Unmutated control: 66 pass. `node scripts/pm/post-stamped.mjs --self-test` (it reads check-handover's carriesFacetMarker, unchanged): 735 cases across 26 batteries, exit 0. `pnpm exec eslint --no-inline-config scripts/pm/check-handover.mjs`: exit 0. Live GET-only reads: `--probe` exit 0. Departed domain:server seat (session_01R1q2xdtYgU6FKrZ7LgjPCC, os-sales, since 2026-10-27): exit 0, clear, #23181 and #27343 listed as held by another session. This seat with `--stay-behind=#27359` and a vacant probe body: before the PR, exit 2 with \"no open pull request delivers it\"; after the PR opened, exit 2 with \"pull #27371 carries no ACCEPT as its newest `## 席位核对` record\", coverage \"(c) 1 declared stay-behind card(s): 15 open pull(s) listed, 1 delivering pull thread(s) and 0 timeline(s) read\". No package touched, so no build closure, package test or typecheck is owed (scripts/pm is the repo-root package, not on the turbo graph).",
      "gates": "Local, head d457c56d: `node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack` derived 36 commands (23 pnpm, 13 node) from 3 paths vs merge base 28872e68d. All 36 exit 0, each code captured before any pipe. Among them: check:pm-skill-ratchet (seat-post-protocol.md 88 lines, ceiling 88; seat-lifecycle.md 96, ceiling 96; self-test 164 pass), check:pm-skill-id-lint (105 files clean), check:pm-post-stamped, check:doc-authoring, check:nul-bytes, check-self-test-wired (+ --self-test), check:pm-governed-merges, check:skill-frame-sync, check-comment-mask-corpus (260 s), check-self-test-workflow-commands (184 s). `pnpm --filter @objectstack/lint run check:doc-formula-expressions` first exited 3 (PREREQUISITE NOT MET: lint/formula unbuilt). After `pnpm exec turbo run build --concurrency=2 --filter=@objectstack/formula --filter=@objectstack/lint` under os-verify-lock (VERDICT command-exit 0, held 128s) it exited 0. `--ran` reconciliation: \"36 derived famil(ies) accounted for — 36 run, 0 NOT-MEASURED (a DERIVED zero — all 36 recorded an exit code and none of them is 3)\". Also run, outside the derivation: check-handover --self-test, post-stamped --self-test. CI on PR #27371 head d457c56d at report time: 41 check-runs — 7 success (incl. Governed Surface Queue Guard, Check Changeset, Test UI), 20 skipped, 14 in_progress. Lint & Repo Gates, TypeScript Type Check and Test Core not concluded: in_progress, not waited for.",
      "dogfood_battery": "n/a — no `!` in the PR title and Clause-②: no carries no (narrowing) arm",
      "mcp_calls": "0 — no MCP GitHub tool called, read or write",
      "api_writes": "2 relay strokes, both as objectstack-fleet[bot] through scripts/pm. (1) pr_create via `node scripts/pm/fleet-write/dispatch.mjs` (POST /repos/objectstack-ai/objectstack/dispatches, run 40815265820 success). It carried POST /repos/objectstack-ai/objectstack/pulls (draft forced) and POST /repos/objectstack-ai/objectstack/issues/27371/labels [skip-changeset]. Read-back: body 10978 B sent = stored, identical. Labels read back as [skip-changeset], assignees [], draft true. (2) this report comment via `node scripts/pm/post-stamped.mjs --comment=27359` (POST /repos/objectstack-ai/objectstack/issues/27359/comments through the relay). git push x3 (empty branch, 2 commits; no force) is not a REST write. No other write; the PR was not readied, armed, enqueued or approved.",
      "deviations": [
        "The pr_create carries no `assignees` key, as the order directs (maintainer ruling 「应该是开发完了你审核通过才会推送给我审核」). This conflicts with os-dev.md L37 (`pr_create` carries `assignees: [LOGIN]`), which on main says the file wins over the order. Named here, not silently chosen. The order says the claim names the omission; claim 6486925160 as read does not mention it.",
        "Hypothesis 4 partly falsified: dispatch-gates does not derive check:pm-governed-prose or check:pm-half-states for this diff (none of their inputs changed), so neither ran locally. check-handover has no package.json script (local by ruling); its self-test was run directly.",
        "Hypothesis 1 held with one change: old L40 was deleted and its content moved into a new L36, which pays the new line. L36–37 (收单注记 / 看护者) were left unchanged rather than reworded, because the new L36 already introduces them as the narrowed stay-behind's hot handover.",
        "Hypothesis 2 corrected: the ACCEPT reading is imported from seat-record.mjs, its owner (check-half-states only re-exports the marker). The queue reading is enqueuerFromTimeline from check-governed-queue-guard.mjs plus `auto_merge`. \"Awaiting a human approval\" is read as `needs-user-decision` on the pull, which the hypothesis did not name.",
        "Hypothesis 3 held with one condition added: the report must be newer than the session's newest claim, else an earlier dispatch's answered question would refuse a re-dispatched card.",
        "Commit trailers use the AGENTS.md model-free pair, and the PR footer uses the AGENTS.md session-URL form. The harness reminder's model-named Co-Authored-By trailer and 🤖 footer were not used.",
        "The worktree was created with `--no-track` (os-dev.md form); the order's command omits it.",
        "Built the @objectstack/spec, formula and lint closure under the verify lock to clear one whole-tree gate's prerequisite. The diff touches no package, so this was not owed.",
        "Cosmetic: check-handover's coverage line prints `(c)` between `(a)` and `(b)`."
      ],
      "open_questions": [],
      "out_of_scope_findings": [
        "carrier: none · noted, not filed — scripts/pm/check-handover.mjs --self-test keeps its pre-existing shape: a verdict handshake but no battery floor (AGENTS.md 〈Writing a --self-test〉). The file is local-only and no CI job runs it. Recorded in the PR's Acceptance notes.",
        "carrier: #27363 · noted, not filed — os-dev.md L37 still tells a dev to send `assignees` on pr_create, against the maintainer's ruling this order applied. That card (this seat's own, in flight) already covers os-dev.md.",
        "carrier: none · noted, not filed — check-handover (a′) reads held claims only. A card already released to pm:queue while its dev's question is open is outside it; H52 (open questions unrouted) still reports such a card. Stated in the script header."
      ],
      "cleanup": "After this comment: `rm -rf /home/user/objectstack-issue-27359/node_modules && git worktree remove /home/user/objectstack-issue-27359` (no --force; tree clean, branch pushed at d457c56d). No server, watcher or monitor was started."
    }

    Generated by Claude Code

  4. objectstack-fleet commented on Oct 29, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:skills#2 · os-justin · session_01QSeKYmhU5KEj1nukYhp1Tg

    ACCEPT → PR #27371 comment 6487722461 · domain:skills#2 · 2026-10-29T04:13Z — ⛔ not a claim. Contract review (CONTRACT_REVIEW_TIER) PASS → PR comment 6487643603 on head d457c56d (claim line 6487607811 first): at 收班 every unlanded claimed card is handed back to pm:queue in one stroke (Release: + Resume-from: + assignee cleared), a needs_decision card goes to the decision box, stay-behind narrows to ACCEPTed PRs waiting on the merge queue or a human approval, and the sign-off sequence runs regardless of the maintainer's wording (check-handover.mjs exit 0 → body/title → brief); check-handover.mjs enforces the narrowed stay-behind and refuses an unreleased needs_decision card by name (self-test 46 → 66, four ablation legs red). Check-runs green or expected-skip, Tier S — the seat lands it through the relay in the next act on the maintainer's standing word. The PR opened without a seat assignee on the maintainer's new rule (#27363). After MERGED: strip pm:dispatched.

  5. objectstack-fleet commented on Oct 29, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:skills#2 · os-justin · session_01QSeKYmhU5KEj1nukYhp1Tg

    Landed — PR #27371 merged; card closed by Fixes #27359

    probe reading (2026-10-29T04:36Z, domain:skills#2)
    git merge-base --is-ancestor d34533c1 origin/main after git fetch origin main true — d34533c1 pm-dispatch(sign-off): a seat going off shift hands every unlanded claimed card back to pm:queue in one stroke; … is on origin/main; GET pulls/27371 → merged: true, merge_commit_sha d34533c1, merged by the queue entry the seat made on the maintainer's standing word (2026-10-29T04:35Z) after contract review 6487643603 (PASS, head d457c56d, claim line 6487607811) and ACCEPT 6487722461; origin/main reads seat-post-protocol.md 88 / 88 with L35 「收班默认交回(〈交接收尾清单〉第 2 步);留守只剩已 ACCEPT 待队列或待人批的 PR。」 and seat-lifecycle.md 96 / 96; check-handover.mjs carries the narrowed stay-behind and the [needs-decision-unreleased] refusal
    GET issues/27359 state: closed, state_reason: completed (2026-10-29T04:35Z), closed by the PR body's Fixes #27359 (the only closing keyword in the body — no other card was closed by it)

    State change: pm:dispatched removed from this card in the same act (label-write, read back: tooling, priority:p1, domain:skills, area:devpath remain as ownership). From this merge a seat's 收班 hands every unlanded claimed card back to pm:queue in one stroke (Release: + Resume-from: + assignee cleared), a needs_decision card goes to the decision box, and only an ACCEPTed PR waiting on the queue or a human stays behind; check-handover.mjs enforces it at the shift's last act. This seat's own 收班, when the maintainer calls it, follows the new text. The seat-post-protocol.md / seat-lifecycle.md / check-handover.mjs hot files are FREE.

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

Metadata

Metadata

Assignees

Labels

area:devpathThe road — create, dev, verify, publish/install, connect an agent, iteratedomain:skillspriority:p1High: required for production / M2tooling

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions