skills(pm-dispatch): push-notify the maintainer when an item moves into the maintainer's hands (a Tier H PR to merge, a decision card, a release step) #24481
Description
Filing gate ③: a task the maintainer directed. In the epic seat's chat (epic:#15194, session_01Y5583bs2RDd53MH6kM1ca3), on 2026-10-19, the maintainer wrote 「24422 等我合并,你改过之后没给我推送消息,是skills的问题吗」, and then 「长期固定下来,立一张skills卡片」.
Reader who acts: whichever seat owns .claude/skills/**. It is a governed path (#7548), so the PR is Tier H, and the maintainer reviews and merges it.
Dedupe: semantic search on objectstack-ai/objectstack for "pm-dispatch skill notify maintainer push notification when action needed merge decision" returned 6 hits, all closed, none about push notifications. The nearest is #23934 (a chat that lists options and waits is a popup), which is a different rule.
What happened (measured)
-
At about 05:46Z on 2026-10-19, PR docs(adr): ADR-0131 D6 charts sharing_rule under regime C; D7 ledger reading and lines for #24377 and #24435; D14 records the 18.0 scope #24422 (Tier H, ADR-0131) became ready for the maintainer's approval and merge. Its at-tier contract review had passed (6260552601), and the seat had corrected the PR body.
-
The seat said so in chat only. It sent no push notification, so a maintainer away from that session learned nothing.
-
.claude/skills/pm-dispatch/SKILL.mdhas no rule for this. The nearest lines are::268, 「卡先于弹窗 …… ⛔ 不 assign 推送」, which forbids using a GitHub assignment as a nudge;:270, 「AskUserQuestion只是在场加速器 …」, which covers a maintainer who is present.
Nothing covers a maintainer who has stepped away, while an item waits on the maintainer's own click or word.
Wanted
Add one rule next to SKILL.md :268–:270. It follows the skill's own style (Chinese bullet, ⛔ markers). Suggested wording, which the implementing seat may tighten:
- 维护者动作推送:事项转入维护者之手时 —— 受管 / Tier H PR 待批准与合并、
needs-user-decision卡待字、发版动作、仅其可解的阻塞 —— 席位在聊天与 GitHub 记录之外,另发一条PushNotification:一行、≤200 字、先写要做的动作与编号(例:「docs(adr): ADR-0131 D6 charts sharing_rule under regime C; D7 ledger reading and lines for #24377 and #24435; D14 records the 18.0 scope #24422 ADR 待您合并(Tier H,评审已过)」)。每项一次;例行进展、刚问刚答、维护者在场即见的不推;工具回 not sent 即视为在场,不重发。⛔ 不以 GitHub assign 代推送(同上条)。
If the hourly wake routine's prompt template, or references/execution-duties.md, carries the round-report duty, mirror the rule there in one line. ⛔ Add no new gate. This is a seat behaviour, not a CI check.
Acceptance
SKILL.mdcarries the rule. The PR body quotes the maintainer's two sentences above verbatim.- On the next item that moves into the maintainer's hands, the seat sends exactly one push. Record that in the seat's next round report.
Activity
objectstack-fleet commentedon Oct 19, 2026
Claim: PM loop round 3
Session:
session_01FM29ZqLTrnPNXxijioZwMpAccount:
os-warren(the seat's linked user asGET /useranswers it; the card's assignee)Branch:
claude/issue-24481-push-notify-maintainer-handsWorktree:
objectstack-issue-24481Domain:
domain:devxSeat:
domain:devx#1File surface:
.claude/skills/pm-dispatch/SKILL.md, one rule bullet beside:268–:270; one mirror line in.claude/skills/pm-dispatch/references/execution-duties.mdonly if that file carries the round-report duty (stop on breach; explain in the report)..claude/skills/**is Tier H governed (#7548): the PR stays draft for an authorised approval.Container & model:
S (one governed rule bullet, wording suggested on the card),mode:subagent,model: CONTRACT_REVIEW_TIER(dispatch-gates --tierMANDATORY path row:.claude/skills/pm-dispatch/SKILL.md, clause ① of the model-tiering ruling)Clause-②: no
Responsibility:
n/a — not a defect card(a seat behaviour the maintainer directed)Thread-read: none
Serial constraints cleared: none — no open PR touches
.claude/skills/pm-dispatch/**or.claude/agents/**(read at 2026-10-19T06:46Z).Generated by Claude Code
objectstack-fleet commentedon Oct 19, 2026
os-dev-report
{ "issue": 24481, "status": "done", "branch": "claude/issue-24481-push-notify-maintainer-hands", "pr": "https://github.com/objectstack-ai/objectstack/pull/24488", "session": "session_01FM29ZqLTrnPNXxijioZwMp", "premise_still_valid": true, "summary": "Added the maintainer-push rule to .claude/skills/pm-dispatch/SKILL.md as two bullets at :272-:273 in 升级与决策, right after the AskUserQuestion pair (:270-:271). Net 0 lines (319 to 319): paid by deleting two director-seat bullets whose content is carried verbatim-equivalent by references/lanes/director.md (its three 职责 headers; :53-:54 for the ≤5-row batch and the maintainer's click, also AGENTS.md Prime Directive #14), and repointing the :56 director arg row at that file. No neighbouring bullet was re-wrapped. Draft PR #24488 opened with Fixes #24481, Clause-②: no, the maintainer's two sentences verbatim, the 维护者速读(草稿) section, assignee os-warren and label skip-changeset; it stays draft.", "rule_text": "- 推送维护者:受管 PR 待批合、决策卡待字、发版、仅其可解之阻 ⇒ 另发 `PushNotification`。\n- 一行 ≤200 字,先写动作与编号;每项恰一次,例行进展、刚问刚答与在场即见的不推。", "files_changed": [".claude/skills/pm-dispatch/SKILL.md (3 insertions, 3 deletions: 2 bullets added at :272-:273; 2 bullets deleted, former :273 三职 =… and former :274 每场召唤…≤5 行…; table row :56 repointed)"], "line_budget": "SKILL.md 319 to 319 lines (ceiling 319, headroom 0); widest table row 342 bytes unchanged (pin 342); 6 diff lines (+3/-3), 5 changed lines; every changed line ≤120 bytes (56: 117, 272: 118, 273: 112) matching the file's prose-bullet norm; dispatch-gates: changed lines 6 vs the 3000 human-merge threshold: under", "tests": "No package touched, so no build/test/typecheck owed. Prose gates at committed HEAD c4341e35c (git rev-parse --short HEAD): pnpm check:pm-skill-ratchet exit 0 — '.claude/skills/pm-dispatch/SKILL.md is 319 lines (ceiling 319; headroom 0)', 'widest table row is 342 bytes (pin 342; headroom 0)'; pnpm check:pm-skill-id-lint exit 0 — '87 file(s) clean (pattern /#[0-9]{3,}/g)'; pnpm check:skill-frame-sync exit 0 — 'the one declared copy of the decision frame is internally coherent', 4 axes, 90 markdown files scanned; pnpm check:pm-governed-prose exit 0. No ablation: a rules-text edit, nothing tested changed behaviour. Control-character scan of SKILL.md and of the PR body: none.", "gates": "Derived at c4341e35c with node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack: 20 runnable families (12 by path, 8 whole-tree) + check-governed-queue-guard.mjs (CI-measured only, not runnable). All 20 run locally, exit codes captured before any pipe (cmd redirected to a log, then EXIT=$?): pnpm check:pm-skill-ratchet :: exit 0 · pnpm check:pm-skill-id-lint :: exit 0 · pnpm check:pm-governed-prose :: exit 0 · pnpm check:skill-frame-sync :: exit 0 · pnpm check:pm-expected-skips :: exit 0 · pnpm check:doc-authoring :: exit 0 · pnpm check:agent-test-spelling :: exit 0 · pnpm check:cross-package-test-inputs :: exit 0 · pnpm check:pm-governed-merges :: exit 0 · pnpm --filter @objectstack/lint run check:doc-formula-expressions :: first run exit 3 PREREQUISITE NOT MET (@objectstack/lint and @objectstack/formula unbuilt, nothing measured), then pnpm exec turbo run build --filter=@objectstack/formula --filter=@objectstack/lint --concurrency=2 under scripts/pm/os-verify-lock.sh (VERDICT command-exit 0, held 159s, waited 0s), rerun :: exit 0 ('22 record-scoped formula example(s) across 476 files / 1393 TS blocks judged clean') · node scripts/pm/check-governed-queue-guard.mjs --self-test :: exit 0 · node scripts/pm/check-harness-current.mjs --self-test :: exit 0 · pnpm check:driver-memory-census :: exit 0 · pnpm check:gitlink-declared :: exit 0 · pnpm check:nul-bytes :: exit 0 · pnpm check:refd-timer-probe :: exit 0 · pnpm check:watch-hint-literal :: exit 0 · node scripts/check-closing-keyword-parity.mjs --self-test :: exit 0 · node scripts/check-closing-keyword-parity.mjs :: exit 0 · node scripts/check-comment-mask-corpus.mjs :: exit 0. --ran reconciliation with exit codes recorded: '20 derived famil(ies) accounted for — 20 run, 0 NOT-MEASURED (a DERIVED zero — all 20 recorded an exit code and none of them is 3)'. Local-only family run because the four-axis frame file was touched: pnpm check:skill-frame-freshness :: exit 0 (reading stays local). The union first ran on the uncommitted tree, byte-identical to c4341e35c which was then committed with no other change; the four SKILL.md-reading prose gates were rerun at c4341e35c. Every family CI runs: the check-runs on https://github.com/objectstack-ai/objectstack/pull/24488 at head c4341e35c are the record; CI status at report time: in_progress (not awaited, per the role file).", "mcp_calls": "0 — no MCP GitHub tool called; reads went through gh api and git. One harness ToolSearch for 'push notification' tested whether a PushNotification tool exists in this container: it does not.", "api_writes": "2 relay strokes = 4 REST requests, all as objectstack-fleet[bot] through scripts/pm: (1) pr_create via with-fleet.sh --via dispatch (run 39215031005, success): POST /repos/objectstack-ai/objectstack/pulls (draft forced), POST /repos/objectstack-ai/objectstack/issues/24488/assignees [os-warren], POST /repos/objectstack-ai/objectstack/issues/24488/labels [skip-changeset]; (2) this os-dev-report comment via post-stamped.mjs --comment=24481: POST /repos/objectstack-ai/objectstack/issues/24481/comments. Plus 2 git pushes (not REST): the empty-branch probe and commit c4341e35c. Read-backs: PR body 9059 bytes sent, 9059 stored, diff identical; REST read of PR 24488: draft true, labels [documentation, size/xs, skip-changeset], assignees [os-warren], auto_merge null, requested_reviewers none.", "deviations": [ "Line-budget mechanism: the dispatch's hypothesis 2 proposed folding neighbouring bullets for a net-zero fit; the role file forbids re-wrap as ratchet currency (「⛔ 不拿 re-wrap(折行合并)当筹行,新增以删减付账」), and the file's prose bullets are all ≤120 bytes, so a two-bullet fold could not keep style either. Resolved by the role file's precedence clause: paid by deleting duplicated content (two director bullets carried by references/lanes/director.md) instead. Candidate folds not taken, for the record: (a) :270+:271 into one 166-byte line 「`AskUserQuestion` 只是在场加速器:维护者本会话 ~30 分钟内有人类输入才可发;每问带推荐项,Skip 或长挂即转卡,⛔ 不重弹。」; (b) :264 into :266 as 「序与权重只排推荐不授权:分歧块照旧升级,四棱同向置信门与代裁面不变;读缩小特例与契约增生,⛔ 不背书投机扩张、不放行人工地板事。」 (192 bytes). Both are re-wraps.", "Tier: the card and the dispatch call the PR Tier H; GOVERNED_SURFACES (scripts/pm/check-governed-merges.mjs row claude-tree), AGENTS.md Prime Directive #14 and SKILL.md :204 place .claude/** in Tier S. Same posture here (draft; never readied, queued or armed by this agent); the PR body states the register's tier. Under Tier S the landing record is the seat's contract-review PASS, not a maintainer approval — the seat should reconcile.", "Hypothesis 4: the card's 「工具回 not sent 即视为在场,不重发」 clause was not written — PushNotification is unreferenced in the tracked tree, .claude/settings.json, .claude/hooks and this container's tool list; the tool name is kept as the card names it (unverified in seat sessions). The card's example 「#24422 ADR 待您合并」 was not written (id-lint pattern /#[0-9]{3,}/ refuses it; an #N example did not fit the 120-byte norm with the exclusions kept). The 「⛔ 不以 GitHub assign 代推送」 clause is the existing :268 bullet and is not restated.", "No mirror line, per the dispatch ruling, verified: the round-report duty is at references/seat-lifecycle.md:66-:68 only; execution-duties.md has none (:54 is a triage-gap line); seat-lifecycle.md not edited.", "Relay read-back for pr_create listed the assignee but printed no label line; the label leg was confirmed by a REST read of the PR at close (skip-changeset present). documentation and size/xs on the PR were applied by other actors and were not touched.", "Commit trailers: the harness reminder asked for a model-named Co-Authored-By; AGENTS.md's model-free pair (Claude-Session plus Co-authored-by: Claude) was used per instruction precedence; no harness-written trailer was appended to c4341e35c.", "origin/main moved between the PM's reading (e11ae4b6b) and BASE fb9c22aa0 (PR 24422 and 24451 landed) and again to 01fafdee2 during the run; none of those commits touch .claude/; SKILL.md :268 and :270 re-measured identical at BASE; no merge of main performed." ], "open_questions": [], "out_of_scope_findings": [ "carrier: the dispatching seat (domain:devx seat 1) · noted, not filed — the card and the dispatch state Tier H for .claude/skills/**; the register, AGENTS.md Prime Directive #14 and SKILL.md :204 say Tier S. Evidence: scripts/pm/check-governed-merges.mjs GOVERNED_SURFACES row claude-tree, tier GOVERNED_TIER_S. Dedupe words: governed tier claude-tree Tier S Tier H register.", "carrier: none · noted in the PR's Acceptance notes only — check-skill-line-ratchet prints a pre-existing info line: .claude/skills/spec-property-retirement/SKILL.md max-table-row-bytes headroom 2 (pin 326, measured 324), inviting a pin lowering. Observation, not a defect." ] }Generated by Claude Code
objectstack-fleet commentedon Oct 19, 2026
Lane correction ·
domain:devxseat 1 ·os-warren·session_01FM29ZqLTrnPNXxijioZwMp· 2026-10-19T10:51ZThis card's change belongs to
domain:skills, notdomain:devx. The domain lane table inSKILL.mdgivesdomain:skillsthe whole governed surface, the pm-dispatch skill included. Thedomain:devxlabel was applied by the filing act (2026-10-19T06:28Z, together withpm:queue). This seat claimed the card on that label and did not check it against the table, which is the claim-time check the table's self-labelled-card exception requires. That was this seat's miss.State, unchanged by this note:
6262172588, PASS, headc4341e35cc). It is Tier S, so it lands through the queue on that record.domain:skillsseat posts ([PM seat] domain:skills — 🟢 os-justin · session_01HEdP5tPhqQfzwRmKDxwn85 #7623, [PM seat] domain:skills · seat 2 — ⏳ vacant #19287) read⏳ vacant, so there is no live holder to hand the card to. It stayspm:dispatchedwith this seat until it lands or the maintainer reroutes it.Generated by Claude Code
objectstack-fleet commentedon Oct 19, 2026
Lane correction:
domain:devx→domain:skills· triage seat ·session_01AavokzJ5DndAwitDXvKy4U· 2026-10-19T10:57ZTriage seat (objectstack-wide, seat post #6015). ⛔ Not a claim. The assignee and
pm:dispatchedare unchanged.domain:skills: the lane table (.claude/skills/pm-dispatch/SKILL.md:164) givesdomain:skillsthe governed surface in full, and:184defines it to include.claude/**. PR skills(pm-dispatch): push-notify the maintainer when an item moves into the maintainer's hands #24488's one file is.claude/skills/pm-dispatch/SKILL.md. The card's own body says the reader is "whichever seat owns.claude/skills/**".domain:devx: the filing act labelled it at creation (2026-10-19T06:28Z:tooling,priority:p2,pm:queue,domain:devx). It arrived already graded, so this seat's sweep, which reads only ungraded cards, never checked it. That is this seat's gap: from this round on, a card that arrives self-labelled has its lane checked against the table at first sight. Thedomain:devxseat's own note6265515884records the claim-time half.domain:skillsseat posts ([PM seat] domain:skills — 🟢 os-justin · session_01HEdP5tPhqQfzwRmKDxwn85 #7623, [PM seat] domain:skills · seat 2 — ⏳ vacant #19287) are vacant, so it waits on the maintainer to merge, as6265515884says.Generated by Claude Code
objectstack-fleet commentedon Oct 19, 2026
Landed ·
domain:devxseat 1 (carrying thisdomain:skillscard; lane note above) ·session_01FM29ZqLTrnPNXxijioZwMp· 2026-10-19T11:35Zgh-readonly-queue/main/pr-24488-*heads)os-zhuangat 2026-10-19T11:18:48Z, on the at-tier review of record6262172588(PASS, Tier S)origin/main, by contentSKILL.md:272reads 「- 推送维护者:受管 PR 待批合、决策卡待字、发版、仅其可解之阻 ⇒ 另发PushNotification。」 and:273reads 「- 一行 ≤200 字,先写动作与编号;每项恰一次,例行进展、刚问刚答与在场即见的不推。」; the file is 319 lines (ceiling 319)State change: PR #24488 closed this card through
Fixes #24481(completed);pm:dispatchedcomes off in this act.Acceptance item 2, the seat's post-landing reading: the rule was applied before it landed, on the maintainer's standing order. This seat sent exactly one
PushNotificationper item that moved into the maintainer's hands: #24445's decision after its reading, #24488's landing block, and #24503's Tier H approval. The round report on #6023 records them.Generated by Claude Code