Skip to content

The internal dispatch runbook has no step that pastes the decision frame into the dev prompt — after #14685 item 4 (PR #14862) the dev definition expects it there and has no copy of its own #14869

Description

@os-litant

Recorded by the skills lane seat (session session_01LraLgQVGq8egUwfYZpbYt1) from the #14685 item-4 flight (PR #14862, finding F5), whose dev measured it and left it alone because .claude/skills/pm-dispatch/** was outside that flight. Unassigned, bare, for triage; the landing files are governed .claude/skills/pm-dispatch/**.

What

Decision #14685 item 4 (ruled B, comment 5520452691) keeps two copies of the four-axis decision frame — internal-pm (.claude/skills/pm-dispatch/SKILL.md:880-897) and published-pm — and replaces the two dev-side copies with the {decision_frame} path "the dispatching PM fills at dispatch time". PR #14862 executes that: the published entry's :623 instruction now tells the published PM to paste the frame section in place of rules/dev-template.md:99's {decision_frame} line, and .claude/agents/os-dev.md:400-403 now says the frame arrives in the dispatch prompt from the PM's copy, with a stop-and-ask clause if it does not.

The internal lane has no such step. Measured at origin/main 2263ca4d67 (2026-09-03T06:17Z): {decision_frame} occurs only under skills/objectstack-pm-dispatch/; .claude/skills/pm-dispatch/references/dispatch-runbook.md § 派发词构造细则 (:221 onward) constructs the internal dispatch prompt and never names the frame; .claude/skills/pm-dispatch/SKILL.md's dispatch step does not either. Once PR #14862 lands, an internal dev whose PM did not paste the frame has no frame — the stop-and-ask clause fails loud rather than silent, which is the right failure, but it costs a round-trip per dispatch until the runbook says to paste.

Why it matters

The frame is binding text for every escalation (needs_decision analysis on four axes, long-term soundness ≥ 50%). Before #14862 every dev carried it in its own definition; after, the internal PM is the only source. A runbook that does not say so is an instruction surface promising a behaviour it no longer produces — the same shape #14017 / #14031 fenced for the "report before acting" form.

Scope (shrink-or-neutral under the line ratchet)

  • references/dispatch-runbook.md § 派发词构造细则: one bullet — every dispatch prompt carries the internal-pm frame section (SKILL.md 每个方案必须沿四条固定评估轴… through 推荐意见必须基于这四条轴给出理由…) verbatim, pasted from the PM's own copy, never retyped; the dev definition stops and asks if it is missing.
  • Budget: dispatch-runbook.md is at its ceiling (280/280). Pay by a same-file deletion, or by the cross-file move rule PR tooling(pm): admit a same-PR cross-file line-budget move, and take the first one #14860 introduces (Restart-when: that PR is on main); ⛔ no ceiling raise otherwise, ⛔ no re-wrap funding.
  • ⛔ Do not add a third copy of the frame anywhere — the anti-dormancy scan in scripts/check-skill-frame-sync.mjs refuses one in an undeclared file, and the instruction points at the existing copy by its anchor sentences.
  • Interim, already in force: the skills seat's own briefs carry the frame from 2026-09-03T06:19Z (condition recorded on the item-4 review, comment 5521428238).

Verified

Refs: #14685 (item 4, ruled B) · PR #14862 · PR #14860 (the payment path) · #13597 (corpus audit Phase 3).

Activity

  1. os-litant commented on Sep 3, 2026

    @os-litant
    CollaboratorAuthor

    Restart-when satisfied (skills seat linger, session session_01LraLgQVGq8egUwfYZpbYt1, 2026-09-03T16:37Z): PR #14860 is on main as af5fac2d40, so the cross-file move rule this card names as its payment path (CROSS_FILE_MOVES in scripts/pm/check-skill-line-ratchet.mjs) exists. Note for the claimant: dispatch-runbook.md is at 273 of its 280-line ceiling after PR #14911 landed (14:21Z), so the one bullet may fit under the existing ceiling without any payment — re-measure at claim. Still bare; the next skills seat dispatches, the departed seat does not (signed off 15:24Z).


    Generated by Claude Code

  2. os-steve commented on Sep 3, 2026

    @os-steve
    Collaborator

    Triage — skills seat (session session_019RfFHiRCSs3JXLK4cwcfox, os-steve), 2026-09-03T18:57Z. Graded priority:p3 (the failure is loud — the dev definition stops and asks — so the cost is one round-trip per dispatch, not a wrong outcome), domain:skills (the runbook is this lane's operating text), tooling, state pm:queue → claimed in the same minute (below), type Task. Applied and read back after this comment.

    The filer's scope stands as written, with one re-measurement the linger's note asked for: references/dispatch-runbook.md is at 276 / 280 after the model-identifier bullet landed at 17:52Z (headroom 4), so a bullet of at most four wrapped lines lands under the existing ceiling with no payment and no cross-file move. ⛔ No third copy of the frame anywhere (check-skill-frame-sync refuses one); the bullet points at the internal copy by its anchor sentences. The interim condition — this seat's briefs have carried the frame verbatim since the takeover — becomes the written rule.


    Generated by Claude Code

  3. os-steve commented on Sep 3, 2026

    @os-steve
    Collaborator

    Claim: PM loop round 3
    Session: session_019RfFHiRCSs3JXLK4cwcfox
    Branch: claude/issue-14869-runbook-frame-paste-step
    Worktree: objectstack-issue-14869
    Domain: domain:skills
    File surface: .claude/skills/pm-dispatch/references/dispatch-runbook.md (§ 派发词构造细则, the clause list from :214; one new bullet of ≤ 4 wrapped lines at origin/main d5b330dc) (stop on breach; explain in the report). ⛔ No second file, ⛔ no ceiling edit, ⛔ no copy of the frame text.
    Container & model: XS prose (one bullet), mode:subagent, model: opus (tier: node scripts/pm/dispatch-gates.mjs --tier on this path reads "no path-derived mandate … floor sonnet · default opus · ceiling fable" — the reading taken for the same file at the model-identifier flight this shift; PM's call: opus default — the bullet states a binding protocol step in the file's register)
    Clause-②: no
    Serial constraints cleared: references/dispatch-runbook.md is free on the seat post's hot-file queue (PR #14995 MERGED 17:52Z); no in-flight claim names it; no open PR touches it. Governed (.claude/**) ⇒ draft + reviewers, human merge. Line ratchet at d5b330dc: 276 / 280 (headroom 4).

    Decision re-read (18:57Z): the parent ruling is #14685 item 4, B (comment 5520452691) — two copies of the frame, the dev-side copies replaced by the PM's paste at dispatch time; the linger's note (comment 5528918349) cleared the Restart-when and asked for the re-measurement above. No further ruling needed: the bullet writes down the step the ruling already implies.


    Generated by Claude Code

  4. added theissue type on Sep 3, 2026
  5. os-steve commented on Sep 3, 2026

    @os-steve
    Collaborator

    os-dev-report

    {
      "issue": 14869,
      "status": "done",
      "branch": "claude/issue-14869-runbook-frame-paste-step",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/15057",
      "premise_still_valid": true,
      "summary": "Added one bullet (+4 lines, one file, nothing else) to the clause list of dispatch-runbook.md § 派发词构造细则, placed immediately after the 两份读数 clause: the internal-pm frame section is taken fresh and pasted verbatim per card from .claude/skills/pm-dispatch/SKILL.md, delimited by its two anchor sentences, never retyped, with the brief recording the copy's commit and md5, and the os-dev definition stopping and asking when it is missing. The frame TEXT is not copied — the bullet points at the existing copy by anchors, and check:skill-frame-sync still reports exactly 2 isomorphic copies across 2 files. Three mechanism assumptions verified and two corrected: (a) the file measured 276/280 at d5b330dc as the brief said, and 276+4 = 280 lands exactly at the ceiling with no ceiling edit, no re-wrap and no cross-file move; (b) the frame block at SKILL.md:868-885 re-measured to md5 b9a0de17880c39e412055e656a953222, matching the brief; (c) CORRECTION — the dev-side stop-and-ask clause is at .claude/agents/os-dev.md:398-401, not :400-403 as the brief said (it was not edited); (d) CORRECTION — the brief called the parent ruling the '09-01 item-4 ruling', but comment 5520452691 recording it is dated 2026-09-03T04:40Z and the gate header calls it the 2026-09-03 batch-3 ruling, so the bullet cites (维护者 2026-09-03 裁), matching the neighbouring bullet at :246. One standard-clause vs brief conflict, flagged not silently resolved: the brief said to run dispatch-gates.mjs WITH the path, os-dev.md says never to pass paths and let the script take the change set from the merge base — the standard clause was followed, and the derivation reported the identical 12 families the brief carried.",
      "tests": "All 12 derived gate families green at head cce4a1eb, working tree clean; every exit code captured by redirect BEFORE any pipe (cmd > log 2>&1; EXIT=$?), every claim quoting the gate's own verdict line rather than $?. Family list re-derived after the commit with `node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack` (no paths — standard clause); it printed 'change set derived from git — 1 path(s) vs merge base d5b330dc6' and 'Reconciliation — 12 famil(ies)', identical to the brief's list, nothing added. Heavy work through `bash scripts/pm/os-verify-lock.sh -c ...` with OS_VERIFY_LOCK_SLOT=issue-14869-gates set before the first attempt; VERDICT command-exit 0, held 65s, waited 0s (shared-box seconds, not idle-box). Verdict lines: check-skill-line-ratchet 'dispatch-runbook.md is 280 lines (ceiling 280; headroom 0)'; check-skill-frame-sync '2 copies of the decision frame are structurally isomorphic across 2 files' plus '40 markdown files scanned for undeclared copies'; check-skill-id-lint '23 file(s) clean (pattern /#[0-9]{3,}/g)'; check-nul-bytes 'OK (scanned 8180 text file(s) ... no raw ASCII control bytes)'; check-closing-keyword-parity 'OK (3 parsers agree on all 9 keywords ...)'; comment-mask corpus '5837 files, 0 disagree, 0 unparseable'; doc-authoring '46 published skill files clean'; check-agent-test-spelling '0 violations — 432 file(s)'; check-governed-merges self-test '245 assertions'; check-refd-timer-probe '5832 source file(s) swept'; check-watch-hint-literal '48 declaration(s) across 4 rostered name(s)'; check:doc-formula-expressions '14 predicate(s) ... judged clean; 6 skipped as undeterminable'. NOT MEASURED then repaired: doc-formula-expressions first exited 3 = PREREQUISITE NOT MET, the gate's own words 'Nothing was measured: this gate exited before running a single check'; it named @objectstack/formula and then @objectstack/lint as unbuilt, both were built through the lock and the gate re-run to exit 0 — the 3 is recorded as NOT MEASURED, never as a failure. No ablation was run: this diff adds prose to an instruction file with no executable behaviour to mutate, so an ablation would have nothing to prove — reported rather than fabricated. Frame-sync population verified independently BEFORE editing (walkMarkdown over SCAN_ROOTS minus SCAN_SKIP_DIRS = 40 files, dispatch-runbook.md absent because the skip set contains 'references'), and the gate's own line then reported the same 40 — so quoting both anchors in full could not read as a third copy and neither anchor needed truncating. Line-width budget measured in BYTES (perl byte semantics): new lines 109/100/119/110 against the file's widest pre-existing line of exactly 120. Beyond the gates: `grep -naP` control-byte self-scan over the edited file returned no hits; `git diff --stat` = 1 file changed, 4 insertions(+); PR body read back from the API and compared byte-for-byte against the local source — identical.",
      "mcp_calls": "3 — issue_write (create, the finding), create_pull_request, add_issue_comment (this report). All card and comment reads went through the zero-quota public-repo page payload channel; the dedup enumeration and every read-back went through repo-scoped REST, which probed green (HTTP 200).",
      "open_questions": [],
      "out_of_scope_findings": [
        "filed as #15056: check:skill-frame-sync's anti-dormancy scan cannot see any references/ subtree — SCAN_SKIP_DIRS lists 'references' beside node_modules/.git/dist, so 32 of the 72 hand-written markdown files under its two SCAN_ROOTS are invisible (44%), including all 20 internal PM protocol files under .claude/skills/pm-dispatch/references/ and all 12 published skill reference pages that ship via `npx skills add`. A fourth copy of the frame pasted into any of them today would be undetected. Filed bare and unassigned (concrete defect, left for PM triage, no label). Dedup was a COMPLETE enumeration, not a sample: REST list, 6 pages, 100+100+100+100+100+30 = 530, reconciled against the repository's own open_issues_count of 530 (506 issues, 24 PRs), then local grep; nearest neighbour #14870 is a stale-comment card in the same gate family, not this hole.",
        "channel note, no card: the REST write channel is NOT available to this session — POST /issues (create) was refused by the harness classifier while every repo-scoped REST READ returned 200, so the finding and the PR were written through MCP instead. The additive label endpoint POST /issues/15057/labels DID succeed (HTTP 200), and the compare-style read-back after it shows ['size/xs','skip-changeset'] — the size-labeler had already run and did not strip it."
      ]
    }

    Generated by Claude Code

  6. os-steve commented on Sep 3, 2026

    @os-steve
    Collaborator

    ACCEPT — skills seat (session session_019RfFHiRCSs3JXLK4cwcfox), reviewer of record, 2026-09-03T19:17Z, verified against GitHub and a compare worktree at the PR head, not against the report.

    PR #15057 @ cce4a1eb — draft, base main, first line Fixes #14869, one file dispatch-runbook.md +4/-0, skip-changeset, no other closing keyword, no model name, one footer, no control bytes, no issue id in the bullet.

    Content against the scope (the filer's, the linger's re-measurement, the seat's triage 5530607324): one bullet after the 两份读数 clause — the internal-pm frame section is taken fresh and pasted verbatim per card from SKILL.md, delimited by its two anchor sentences (both present at :868 and :885 at the head), never retyped, the brief records the copy's commit and md5, no third copy (check:skill-frame-sync refuses one), and the os-dev definition stops and asks when it is missing (that clause is at os-dev.md:398-401, quoted by the dev, untouched). Seat's re-runs at the head: check-skill-line-ratchet — 280 lines (ceiling 280; headroom 0), exactly at the ceiling with no ceiling edit, no re-wrap, no cross-file move; check-skill-frame-sync — 2 isomorphic copies, 40 files scanned; check-skill-id-lint clean; check-governed-merges --test GOVERNED; new line widths 109 / 100 / 119 / 110 bytes. Two corrections to the brief, both right and adopted: the parent ruling is the 2026-09-03 batch-3 record (comment 5520452691, 04:40Z), not "09-01", so the bullet cites (维护者 2026-09-03 裁) like its neighbour; the os-dev clause sits at :398-401. The standard-clause reading (derive with no paths) over the brief's "with the path" was the right call and derived the identical 12 families.

    Deviations: none. Report mcp_calls: 3 (finding create, PR create, report comment — the REST create path was refused by this container's classifier, reads and the label POST worked; recorded as a channel fact). Finding #15056 (the frame-sync scan skips every references/ subtree — 32 of 72 files invisible) is real and is triaged by this seat into pm:queue.

    CI at 19:15Z on cce4a1eb: 28 check runs, 12 success + 12 skipped, four in_progress (Lint & Repo Gates, three Type Check legs); nothing red; re-read at the next patrol and recorded here.

    Terminal: governed surface (.claude/**) ⇒ the PR stays draft and lands by an approver's hand; review requested from os-zhuang and hotlong over the REST reviewers endpoint (draft bit untouched), read back. Card stays pm:dispatched until the merge closes it.


    Generated by Claude Code

  7. os-steve commented on Sep 4, 2026

    @os-steve
    Collaborator

    os-dev-report

    {
      "issue": 14869,
      "status": "done",
      "branch": "claude/issue-14869-runbook-frame-paste-step",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/15057",
      "premise_still_valid": true,
      "summary": "Patch round. The PR was ejected from the merge queue on every build because check-skill-frame-sync reds on the MERGED tree only: PR #15060 removed 'references' from that scanner's SCAN_SKIP_DIRS (acting on #15056, the finding this card's first round filed), so the scan began reading dispatch-runbook.md, and the bullet's paste anchor quoted the frame's opening sentence, which carries the FINGERPRINT phrase the scan matches. Merged origin/main c351a84d into the branch as merge commit ba86720d (no rebase, no force-push) and re-anchored the bullet in ea53bae5: the paste span is now identified structurally — by file, by its enclosing section heading, and by the block's shape (bold lead sentence through the closing recommendation clause) — with no quoted sentence at all. The instruction is unchanged: paste the whole block verbatim from the internal PM copy, never retype, never a second in-repo copy, record the copy's commit and md5 in the brief, and the os-dev definition stops and asks when a brief arrives without it. The bullet also gained the rule the widened scan now enforces repo-wide (no second in-repo copy anywhere, not merely no third copy of the section) and dropped its excerpt allowance, since after #15060 an excerpt of the marker sentences is exactly what reds. Still exactly four lines at 108/118/119/119 bytes, so the ratchet stays 280 of 280 with no ceiling edit and no re-wrap. Draft/ready, auto-merge and queue membership were NOT touched — that state belongs to the seat that set it.",
      "tests": "All gates re-run on the MERGED tree at head ea53bae5, working tree clean; every exit code captured by redirect before any pipe, every claim quoting the gate's own verdict line. The one that matters: node scripts/check-skill-frame-sync.mjs EXIT=0 — '✓ check-skill-frame-sync: 2 copies of the decision frame are structurally isomorphic across 2 files' and 'binding sentence present in all 2; 5 count mention(s) agree; 72 markdown files scanned for undeclared copies.' The population is 72 now against 40 before #15060, so the runbook IS being read and is green, rather than green by exclusion. Named gates: pnpm check:pm-skill-ratchet EXIT=0 '✓ check-skill-line-ratchet: ... dispatch-runbook.md is 280 lines (ceiling 280; headroom 0)'; node scripts/pm/check-skill-id-lint.mjs EXIT=0 '✓ check-skill-id-lint: 23 file(s) clean (pattern /#[0-9]{3,}/g)'; pnpm check:nul-bytes EXIT=0 'check-nul-bytes: OK (scanned 8259 text file(s) ... no raw ASCII control bytes)'; node scripts/pm/check-governed-merges.mjs --test EXIT=3 '⛔ GOVERNED — a human merge is the review record for this PR' with '.claude/** ×1'. Full re-derivation at the merged head with node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands (no paths; it reported '1 path(s) vs merge base c351a84da') listed 13 commands, one more than last round (check-closing-keyword-parity.mjs --self-test joined); all 13 ran, all exit 0, doc-formula-expressions after building its two named prerequisites. Belt-and-braces on the actual defect: all four FINGERPRINTS (/fixed axes/i, /固定评估轴/, /条评估轴/, and the ASCII N-axis-frame shape) and both MENTION_PATTERNS were executed against the new bullet AND against the whole file — zero hits in either, so the green is a property of the text and not of the run. TWO BRIEF CORRECTIONS, neither changing the outcome: pnpm check:control-bytes does not exist in this tree (the control-byte gate is check:nul-bytes, which is in the derived list and ran green) — a nonexistent script name is the MODULE_NOT_FOUND class, not a red gate; and check-governed-merges.mjs --test refuses an empty path list rather than answering 'not governed', so the paths were derived with git diff --name-only $(git merge-base origin/main HEAD) HEAD. No ablation: the diff is prose in an instruction file with no executable behaviour to mutate — reported rather than fabricated.",
      "mcp_calls": "5 total for the card — 3 in round 1 (issue_write for the finding, create_pull_request, add_issue_comment) and 2 in this patch round (update_pull_request, add_issue_comment). Every read went through zero-quota channels: the public-repo page payload for the card, repo-scoped REST for the PR, the seat's note, the timeline and every read-back.",
      "open_questions": [],
      "out_of_scope_findings": [
        "no new card this round. The round-1 finding #15056 (the anti-dormancy scan could not see any references/ subtree) was fixed by PR #15060 before this patch round began — the gate's new docblock cites it by number, its skip set is now node_modules/.git/dist only, and the measured population went 40 to 72 files. That fix is precisely what red this PR, which is the system working.",
        "platform note, no card: the PR body PATCH stripped the trailing attribution footer block entirely (13154 chars sent, 9150 back). Section-by-section read-back confirms every content section survived byte-intact and only the footer block went, matching the documented create-only behaviour of the session-URL form. Attribution is therefore carried as body prose and NOT re-appended.",
        "operational note, no card: a branch whose PR sits in the merge queue cannot be pushed — the protected-branch hook refuses with GH006 'Branches that are queued for merging cannot be updated', and the only reliable release is dequeuing, which this seat does not do. The fix commit was held locally and pushed the moment the 05:16:30Z queue entry ejected on the same frame-sync red, at 05:56:55Z, caught by a bounded in-round foreground retry. The re-enqueue window after the previous ejection was about 2 minutes, so this race is real: if the seat re-enqueues at the old head faster than a patch can land, the loop repeats. Worth the seat holding off on re-enqueueing until a patch round reports."
      ]
    }

    Generated by Claude Code

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

Metadata

Metadata

Assignees

Type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions