docs(skills): correct six false mechanism claims in the published pm-dispatch template - #13891
Conversation
…-dispatch template Flight 11 of the published-skills factual sweep. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EnE7G31tqbxN1rqpQmzurT
|
Sanitizer probe for correction F4 — an executed measurement, not a claim about this PR. F4 rewrites the operating template's rule for GitHub's at-rest body sanitizer. The skill's own recorded measurement and its published RULE disagree about the trigger (the rule says "LT followed by a letter"; the measurement includes a span whose second character is a bang), so the correction needed the platform itself as the oracle rather than either document. Four spans written through the API below, each between unique sentinels; the stored body is read back and the result recorded in the flight report. P1-START P1-END P2-START P3-START P4-START P1 is the HTML-comment form the template's rule does not cover and the skill's step-5 text recommended. P2 is the LT-plus-letter form the rule does cover. P3 and P4 are the two escapes the skill prescribes. Whatever is missing between a START/END pair was deleted at rest. Generated by Claude Code |
|
Probe result — read back from the stored comment above. All four cells measured, and F4's correction is settled by the platform rather than by either document.
Four things this establishes, each of which the corrections in this PR depend on:
P3 and P4 come back through the API read path with their angle brackets as numeric entities. That is the known read-side artifact of that path, not corruption — the measurement here is presence, and both are present. Method note, since it cuts the other way too: this comment and its probe are the reason the flight's PR body and report carry no literal tag-shaped fragments anywhere. Writing the poison deliberately, once, inside sentinels is a measurement; writing it in the load-bearing text is how the skill's own report-collection marker was deleted from the very instruction that carried it. Generated by Claude Code Generated by Claude Code |
|
Housekeeping note, found during teardown and reported rather than quietly left: the single commit's message says "correct four false mechanism claims". The final count is six — the message was written mid-sweep and two more corrections landed after it. The PR title, the PR body and the flight report all say six and are correct. Not repaired, deliberately, and the reasoning is the same discipline the rest of this flight ran on. Amending means a force-push, which is banned outright. A fresh commit instead would move HEAD, and every measurement in the PR body and the report is anchored to Squash-merging takes its subject from the PR title, so what lands on the default branch reads "six"; the stale numeral survives only in the squashed body. Happy to push a corrected commit and re-run the union at the new head if you would rather have it clean — that is a cheap round, just not one worth spending unasked. Generated by Claude Code Generated by Claude Code |
Contract review — PASS at head
|
Fixes #13868
Part of #13658
Flight ⑪ of the published-skills factual sweep:
skills/objectstack-pm-dispatch/SKILL.md, one file, 1,077 lines atorigin/main— the PM's face assumption re-derived from the fetched tree and CONFIRMED exactly.Governed surface: this PR stays DRAFT for human merge. It is not made ready, not armed, not enqueued.
Boundary this flight was fenced on
The target is the CUSTOMER-PUBLISHED template written for OTHER repositories.
{default_branch}/{backlog_repo}/{target_repo}/{n}interpolations are correct there and all survive the diff. The internal agent corpus at.claude/skills/pm-dispatch/was neither edited nor used as an oracle for any claim below; every correction is settled against an IMPLEMENTATION in this tree (a workflow, a gate script, a shipped shell script), never against another document.Corrections — 6 distinct false facts, 7 landing sites
F1. Closing keywords were taught in the form GitHub does not honour (2 sites). The dispatch prompt and the operating template both told every dispatched agent to write
Fixes {backlog_repo}#{n}in a PR that lands in{target_repo}— a qualified cross-repository closer. Oracle, first sentence of.github/workflows/cross-repo-issue-closer.yml— quoted with its two example issue numbers replaced byNNN, because the live closer workflow parses merged PR bodies and a verbatim quote here would be an instruction to close somebody else's card: "GitHub's closing keywords (Fixes #NNN) only work WITHIN a repository. A PR here that saysFixes objectstack-ai/objectui#NNNreads exactly like a same-repo close to a human, merges, and leaves that issue open forever — with no reference to the PR on the issue's own page either." That workflow exists only because of this, is live (pull_request_target: [closed],if: merged == true), and carries its own gate,scripts/check-cross-repo-closer-outcome.mjs, which measured zero qualified cross-repo closers across 1,176 merged PRs in two independent windows.This is the falsehood with the largest blast radius in the file, because it fires exactly in the multi-repository routing case the skill is built around: the card never closes, the loop's own terminal state never arrives, and the PM sees no signal that anything went wrong. The document already contradicted itself here — the review step (step 7) verifies the body "references
Fixes #{n}", the unqualified, correct form.{target_repo}. Never merge anything." The PR-body spelling was a duplicate of the operating template pasted verbatim three lines above it, and it is the copy that had drifted; deleting the drifted duplicate leaves one owner for the rule.Fixes #nin-repo,Part of {backlog_repo}#ncross-repo.Part ofis already the skill's own cross-repo backlink spelling in two other sections, so this is the file's existing convention rather than a new one.F2. The state-model terminal row over-claimed (1 site). "merged PR with
Fixes #n→ done (GitHub closes the issue)" now reads "done — GitHub closes it, same repo only". Note the boundary: the row as written was TRUE for the unqualified#nit shows, which is why it is a scope correction and not a reversal.F3. The cloud-mode report marker was the one token this same document measured as destroyed. Step 5 recommended "a machine-findable marker such as" an HTML-comment marker. Sixty lines earlier, step 4 records the measurement: that exact marker "came back as nothing at all", and the file itself says it "cost the most — it was a report-collection marker, so the instruction to sweep for it had been deleted from the very text that carried it." The recommendation is now a machine-findable PLAIN-TEXT marker. A self-refuting instruction: the document could not be right in both places.
F4. The sanitizer's trigger was described more narrowly than the document's own measurement (1 site). The operating template taught that the sanitizer "strips LT followed by a letter as an HTML tag AT REST". The measurement in step 4 includes an HTML-comment span, whose second character is a bang, not a letter — so an author who follows the stated rule and writes an HTML comment believes they are outside the hazard. The template now says the sanitizer deletes tag-shaped spans at rest, LT plus a letter and HTML comments alike. This also matches
AGENTS.md's own clause, which warns that fenced code does NOT protect such tokens.F5. Cross-reference to the wrong decision axis (1 site). The "Adapting this loop" table sent the reader to "the business-need axis" for the capability-expansion stance. Axis ① is business need; the stance is read by Axis ④ (startup scope discipline), whose own text says so in as many words ("declare the capability-expansion stance in your conventions file … and this axis reads it from there"), as does the template's fourth bullet. Now "axis ④". This correction SHRINKS the file by 14 bytes and is the only one that paid for the others.
F6. An arithmetic claim contradicted its own enumeration (1 site). The completion-baseline passage enumerates four text-only cards and four mixed cards, then summarises "Nine cards, one day, one toolchain". Four plus four is eight. Now "Eight cards".
The
--no-trackfix — discharging #13729's published half ONLYThe worktree recipe in the operating template (was
:741) prescribedgit worktree add ../…-issue-n -b claude/issue-n-slug origin/{default_branch}with no--no-track, so a plain-bwritesbranch.NAME.remote/branch.NAME.mergeinto the.git/configthat every worktree of a repository SHARES. Now hardened, with the{default_branch}interpolation preserved verbatim — that was the constraint that made this a scope-split half rather than a copy of this repo's line.Per #13729's scoping comment, this discharges its published half only. Its three internal copies (
.claude/skills/checklist-author/SKILL.md,.claude/agents/os-dev.md,.claude/skills/dogfood-verification/SKILL.md) stay on that card, untouched here — one of them is inside this flight's hard fence. #13729's table also records that this copy, unlikechecklist-author's, was NOT missing the fetch/originhalf, and that reading still holds.Budget — measured BEFORE editing, and the fork it forced
The ceiling was read first, exactly as #13729 required:
skills/objectstack-pm-dispatch/SKILL.md= 14,391 tokens against a ceiling of 14,391, headroom ZERO; the ratchet's convention isceil(utf8 bytes / 4), so the file had one byte of slack.Net line budget ≤ 0 and net tokens ≤ 0, both satisfied. No ceiling was touched — the
CEILINGSrow is unchanged, which is also the posture all ten preceding flights landed with.The fork #13729 anticipated is real and I hit it. Priced at their most natural spelling, the corrections above came to +70 bytes against 1 byte of slack. Nothing was funded by re-wrapping. What paid for the diff instead: F5's shorter-and-more-precise wording (−14) and, above all, the deletion of F1's drifted duplicate instruction (−52) — a deletion the correction itself justified, since that duplicate was the wrong copy of a rule the template beside it already owns. Two intended corrections were priced out and are NOT in this diff, recorded here rather than dropped:
Part ofand no closer, nothing automatic ever closes a cross-repository backlog card, and the file now says nowhere that a human must. Costed ~+44 bytes.Both are the maintainer's call, not mine: ceilings are theirs, and re-wrap funding is banned.
Verified, and the boundary readings
Roughly 250 behavioural claims inventoried across the file; 6 distinct false facts over 7 landing sites is ~2.4% distinct / ~2.8% by site, inside the program's 1.5–10% working range and at its lower end — consistent with a process skill whose claims are mostly about its own self-defined protocol rather than about a schema that moves underneath it.
Checked and CLEAN, each against an implementation:
npx skills add objectstack-ai/objectstack/skills --skill objectstack-pm-dispatchand the--allnote both hold:skills/README.mdships the--allspelling, andpackages/create-objectstack/CHANGELOG.mdrecords that "the skills CLI's--allimplies--skill '*'" — so--skillis a real flag and the/skillssubpath is the published-catalog boundary the file correctly keeps.scripts/pm/ensure-pm-labels.sh.pm:queue/pm:dispatched/needs-user-decisionall real; every-din the file is under GitHub's 100-character description cap;gh label create's-R/-c/-dshort flags are the ones that script's own reconcile shim parses. The routing-label example's colourfbca04is not merely plausible — that script names the published skill as the source it converged on.objectstack-ai/objectstackas the upstream for@objectstack/*, againstpackages/spec/package.json'srepositoryfield. The17.2.0pin example matches the workspace's live version.pnpm list,gh pr list --search/--state/--limit,git log --oneline,vitest --maxWorkers,kill -0,pkill -f,expect(...).toThrow()/rejects.toThrow()all real surfaces.check:skill-compatibility's exemption for this file is self-invalidating and re-matches the live text against/No\s+@objectstack\/spec\s+dependency/i. Editing that sentence would have killed the exemption and turned the gate red.batch3,modesubagent,labelpm:queue,routingLabelPrefixrepo:,conventionsFilefirst-existing-of-three,roundsunbounded — all internally consistent across the Quickstart, config table, args table and zero-config paragraph.Boundary readings recorded rather than "corrected", per the card's instruction that a template may legitimately describe a GENERIC shape:
origin/mainin the stale-premisegit logsnippet, while the worktree recipe usesorigin/{default_branch}. An inconsistency, but that snippet is plainly a fill-in-the-blanks block (its path argument is a prose placeholder), andmainis not a misdescribed mechanism. Costed at +12 bytes; NOT taken, and NOT counted as a falsehood.|| truein the label-setup block.ensure-pm-labels.shdocuments, from live measurement, that|| truealso swallows the HTTP 422 an over-long description earns — so the label is silently never created and no rerun repairs it. The published block's "(idempotent)" claim is TRUE as written, and the missing cap warning is an omission, not a falsehood; adding it costs bytes this budget does not have. Recorded for the roster, no card filed.Blocked-by:body lines are not one of its rows — though the file reads them at selection time. An enumeration gap rather than a false row; the fix is additive and the budget forbids it.Model tier — a half-state the PM should settle
The claim comment on #13868 declares
model: opus, on the reasoning that "the fable mandate covers the INTERNAL pm-dispatch SKILL.md, which is fenced out of this card entirely". The machine derivation disagrees, and names THIS file:This work was constructed at opus, i.e. below the derived floor. I am reporting the contradiction rather than picking a side. The compensating control that does hold:
check:skill-frame-syncis green and neither copy of the decision frame was edited.Gates — 14 families derived from the REAL diff, all at HEAD
9e28a1a5fnode scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstackwith no path argument (the script takes its own change set from the merge base), exit 0; its header confirms the tree it answered for:objectstack-ai/objectstack at 9e28a1a5f. Harvested with--commandsso neither invocation spelling is dropped. Every exit code captured by redirect BEFORE any pipe.13 of 14 exit 0. The 14th,
node scripts/check-test-completeness.mjs, exits 3 and is NOT MEASURED by its own printed verdict — "the local reading for this gate is NOT MEASURED. ⛔ It is not a red" — because it grades a savedturbo run testlog only CI produces. Recorded as not-measured, never as green or red.Verdict lines quoted from the gates themselves:
check-skills-token-ratchet: "skills/objectstack-pm-dispatch/SKILL.md is 14390 tokens (ceiling 14391; headroom 1)" and "38 authored bundle file(s) within their ceilings; 11 generator-owned file(s) measured, not ratcheted."check-skill-frame-sync: "4 copies of the decision frame are structurally isomorphic across 3 files."check-skill-compatibility-version: "11 SKILL.md file(s) reconciled against 78 workspace packages."check-doc-authoring: "48 published skill files clean — no internal issue-id references."check-nul-bytes: "OK (scanned 7608 text file(s) … no raw ASCII control bytes)", plus a manual scan of the edited file over the 0x00–0x1f/0x7f class returning no matches.check-doc-formula-expressions: "22 record-scoped formula example(s) across 427 files / 1451 TS blocks judged clean."Four gates first exited non-zero on PREREQUISITE NOT MET (
yaml/typescriptuninstalled,@objectstack/formulaand@objectstack/lintunbuilt). Those are refusals, not findings; cleared bypnpm installand two targeted turbo builds, then re-run — all four green. Every heavy run went throughbash scripts/pm/os-verify-lock.shwithOS_VERIFY_LOCK_SLOT=issue-13868(waits of 4m39s and 2m51s, spent on unlocked work).Ablation — the ratchet can go red, proven rather than assumed. From the committed state, 43 bytes of ordinary prose appended to the file; the mutation was confirmed ON DISK by blob hash (
3883c5ad…→d89f3e1a…, not by an editor's exit code) before anything was measured. Mutated run: exit 1, "is 14401 tokens; the ratchet ceiling is 14391 (over by 10)". Restore leg given the same treatment:git checkout HEAD -- ABSOLUTE_PATHunder anEXIT INT TERMtrap, then proven by blob hash equality with the HEAD blob AND an emptygit diff HEAD; restored run exit 0, headroom 1. So the headroom-1 reading above is a live measurement on a gate that demonstrably fails on growth.ESLint narrowing, declared with all three legs. (1) The population is read from eslint's OWN config resolution, not from my guess:
npx eslint --no-inline-config --format jsonon the changed path returns "File ignored because no matching configuration was supplied." (2) The count is read from that--format jsonoutput: 1 result, 0 errors, 0 lint warnings (1 ignore notice), exit 0. (3) Invariance:eslint.config.mjssupplies no matcher for.mdat all — everyfiles:entry names TS/JS extensions — and the config carries noparserOptions.projectand no typed rules, so a Markdown-only diff cannot move any judgment on any file eslint does lint. The repo-widepnpm lintrun stays CI's.skip-changeset: pureskills/**diff, publishing nothing from any package. Verified against the three preceding sweep PRs (#13777, #13808, #13867), each of which carries the same label; theCheck Changesetjob has no path filter, so the label is the author's step and is attached in the same stroke as this PR.needs:contract-reviewis attached to this PR AND to card #13868 in the same stroke, per the clause ② CONTENT limb (批 #12). This seat does not self-clear it.Generated by Claude Code
Generated by Claude Code