| name | release-manual |
| description | End-to-end release of a P2 manual to the public deliverables area — VERIFY the generated PDF is complete (page count, outline, key sections, compile-log; guards against silent content-drop), promote the CHANGELOG + update the deliverables README release index (add new manuals, force-download links), record the Platform Freshness Ledger PUBLISH line + roster status, then surface the commit/tag/push. Use when the user says "release the <manual>", "publish the <manual>", "promote <manual> to deliverables", "get <manual> released", or invokes /release-manual. Assumes the PDF was generated by PDF Forge and placed in deliverables/documents/DOCs/. Suggests git commands but does NOT execute them. Handles a single manual or a multi-manual release wave. |
Release Manual — end-to-end document release
You are taking one or more P2 manuals from "PDF generated" to "publicly released." The
PDF(s) have already been produced by PDF Forge and placed in deliverables/documents/DOCs/
by the user. This skill is the single authoritative release process: verify → promote →
record → hand off git.
App-note elements release their YAML companion alongside the PDF. An app
note is a document-production element (see HEAD-DISPATCH-DRAFT.md
§"Artifact-type model") that ships as doc + first-party YAML companion.
When releasing an app note: the companion under deliverables/ai/P2/ is
published with the PDF, and the doc↔companion agreement gate (owned by
document-audit) must be green first — a divergence blocks release exactly as
the YAML-HEAD drain gate does. Everything else (PDF completeness verify,
CHANGELOG promotion, README index, freshness ledger) is identical to a manual.
The cardinal rule: never trust the generation success flag. PDF Forge runs xelatex in
batch mode and can emit a PDF + report "100% success" while silently dropping large spans
of content (a real incident: a PASM2 build dropped instruction sections A–F — 156 pages —
from an unbalanced [ in prose that made pandoc swallow content; the compile log was
clean, 336pp instead of 492pp). Phase 1 verification is the heart of this skill — a
release that skips it can publish a broken book.
Quick form (experienced operator — the fast path)
Per manual being released:
- Verify the PDF is complete (Phase 1): right build (mtime), page count sane vs. prior
release, outline complete (all chapters/instruction-letters/appendices present), key
sections text-present, compile log clean. Stop on any shortfall.
- Audit the changelog (Phase 2a — MANDATORY gate): run
/audit-changelog <slug>; stop
and fix opus-master/CHANGELOG.md until it clears (NOT READY = any CRITICAL/HIGH) before
promoting. Then promote: copy the post-audit opus-master/CHANGELOG.md →
DOCs/<slug>-changelog.md; update (or add) the manual's entry in
deliverables/documents/README.md (version, date, force-download PDF link).
- Record freshness + sync dashboards: append/update the manual's
PUBLISH line in the
Platform Freshness Ledger (PUBLICATION-ROSTER.md) at the PDF's mtime; update its roster
row/status; prune absorbed PLATFORM lines; and (3c, mandatory) sync the two roster-mirror
dashboards — engineering/document-production/README.md (the status matrices — a release
moves the doc into Done) and the engineering/README.md front-door Manual row.
- Update the community-review announcement draft (Phase 3d): add the doc's row + warm blurb
(or bump its version) in the gitignored
DRAFTS/COMMUNITY-REVIEW-ANNOUNCEMENT-v*.md, and spot-check the
other rows' versions against the roster. Not committed — the user publishes it.
- Enclosing-doc audit: grep for stale version/date refs elsewhere; surface, don't edit.
- Hand off git: suggest the
git add / commit / tag (and push, which activates the
raw.githubusercontent.com download links). Do NOT execute.
The phases below are the full form of each of these steps.
Repo layout
engineering/document-production/
├── manuals/<slug>/opus-master/CHANGELOG.md # SOURCE changelog — copy this up
├── workspace/<slug>/request.json # documents[0].output = the PDF filename
├── outbound/<slug>/ # may hold the .tex + .compile.log hand-back
└── PUBLICATION-ROSTER.md # roster rows + Platform Freshness Ledger
deliverables/documents/
├── README.md # PUBLIC release index (all manuals)
└── DOCs/
├── <DocName>.pdf # user places this after Forge
└── <slug>-changelog.md # DESTINATION changelog copy
Phase 0 — Identify + scope
Accept slug(s) as argument. If absent, list manuals that have a
manuals/<slug>/opus-master/CHANGELOG.md AND a placed DOCs/<PDF>.pdf newer than their
last release, and ask which to release. A release wave (several at once) is normal —
loop Phases 1–4 per manual, then do ONE git hand-off (Phase 5) covering all of them.
Resolve per manual: the slug, the PDF filename (workspace/<slug>/request.json →
documents[0].output), the topmost CHANGELOG version, and the existing README entry (if
any). A manual with no README entry yet is a new release — Phase 2 adds the entry.
Phase 1 — VERIFY the generated PDF (the silent-drop guard) — DO NOT SKIP
The PDF lives at deliverables/documents/DOCs/<PDF>. Verify CONTENT, not just presence.
Your container has no poppler — use PyMuPDF (pip install --user pymupdf; pure Python).
1a — right build. ls -l the PDF: confirm its mtime is the new generation (not a
leftover). Report size + mtime so the user can sanity-check.
1b — page count vs. expectation. Open with fitz; print doc.page_count. Compare to the
prior release's page count (the previous PDF, or the value recorded in the last ledger
PUBLISH line / README). A sudden DROP is the #1 red flag for a silent content-drop —
investigate before proceeding. (A large increase is normal when a prior build was broken.)
1c — outline completeness. doc.get_toc() — confirm the structural skeleton is all
there: every chapter, every instruction-letter section (Instructions: A … Z), every
appendix. A gap in the alphabetical/numeric run (e.g., outline jumps Chapter 2 → Instructions: G)
means content was swallowed.
1d — key-section text presence. Pick ~6–10 strings unique to sections you expect, spread
across the doc (first/middle/last chapter, an early + late instruction entry, an appendix
total). page_of(s) each; any ABSENT that should be present = dropped content. (Beware
extraction artifacts: a leading capital in a styled box can split — Absolute→bsolute;
the × sign and ligatures don't always extract. Confirm a suspicious ABSENT by rendering
the page and looking, before calling it a drop.)
import fitz
doc = fitz.open("deliverables/documents/DOCs/<PDF>")
print("pages:", doc.page_count)
print("sections:", [t for _,t,_ in doc.get_toc() if t.startswith(("Chapter","Instructions:","Appendix"))])
def page_of(s): return next((i+1 for i,p in enumerate(doc) if s in p.get_text()), None)
for label,needle in CHECKS: print(f"{'p'+str(page_of(needle)) if page_of(needle) else 'ABSENT':8} | {label}")
1e — compile-log scan (when available). Generation hands back the .tex and
.compile.log into outbound/<slug>/ (a .tex in outbound is inbound-for-debugging, per
the prepare-manual rules — read it, don't deploy it). If present:
- Scan the log for serious signatures:
! LaTeX Error, ! Undefined control sequence,
Runaway argument, Emergency stop, Float(s) lost, No pages of output,
! TeX capacity. Any hit ⇒ investigate. (Overfull/Underfull hbox are normal — ignore.)
- Read the
Output written … (N pages) line — cross-check against 1b.
1f — if content is missing, locate the failure stage. The drop is either pre-xelatex
(pandoc/escape) or in xelatex:
- Grep the generated
.tex for the missing section headers. Missing from the .tex
itself ⇒ pre-xelatex (pandoc) drop — usually an unbalanced [ / ::: / ``` in
the markdown that made pandoc swallow until it rebalanced. Check the assembled source:
count [ vs ] (excluding inline-code spans and fenced blocks) and :::/ `` parity;
the unmatched delimiter is the culprit. Fix it in opus-master, re-prepare, regenerate.
- Present in the
.tex but absent from the PDF ⇒ a real xelatex error in the log (1e).
Gate: all of 1b–1e clean ⇒ proceed. Any shortfall ⇒ stop, report exactly what's
missing and the likely stage/cause, and do NOT promote. This phase existing is the reason
a broken book doesn't ship.
Phase 2 — Promote: CHANGELOG + README index
2a — MANDATORY changelog audit gate (run EVERY release; block on failure). Before
promoting anything, audit the manual's top CHANGELOG entry by running the audit-changelog
skill (/audit-changelog <slug>) — invoke it, do not merely recommend it. The audit applies
engineering/document-production/methodology/changelog-style-guide.md (current-state voice, no
prior-state phrasing, aggregate-not-itemize, the excluded-category list) and cross-checks the
entry's claims against the commits/diff since the last published tag.
Uncommitted-release note: much of a release's work is often still in the working tree (not
yet committed at promote time). Audit the entry against the baseline tag → working tree diff
(git diff <slug>-v<prior> -- …/opus-master/), not just <baseline>..HEAD, or the content
audit misses the about-to-ship changes.
Gate:
- Verdict READY / READY WITH NOTES → proceed to 2b.
- Verdict NOT READY (any CRITICAL or HIGH finding) → STOP. Do not promote. Fix the entry
in
opus-master/CHANGELOG.md (never the workspace render — the CHANGELOG is authored, not
generated), then re-run the audit until it clears. A changelog fix needs no PDF regen (the
CHANGELOG is not part of the PDF), so this loop is cheap — there is no excuse to ship an
unaudited changelog. Surface the findings and your fixes to the user.
This gate exists because the published changelog is the reader's release notes: shipping
prior-state phrasing ("corrected from X to Y"), itemized confessionals, or excluded editorial
churn is a visible quality defect. Recommending the audit was not enough — the release runs it.
The CHANGELOG copy promoted in 2c must be the post-audit version.
2b — parse the version + date. From opus-master/CHANGELOG.md, the topmost entry:
## v<X.Y.Z> (<YYYY-MM-DD>). Normalize to three-part semver (append .0 if patch missing —
the README uses three-part). Convert the ISO date → Month Year (2026-06-10 → June 2026).
2c — copy the CHANGELOG up.
cp engineering/document-production/manuals/<slug>/opus-master/CHANGELOG.md \
deliverables/documents/DOCs/<slug>-changelog.md
2d — update the README release index (deliverables/documents/README.md). The README is
the public catalog of ALL manuals — released (in a "Documents in Community Review" / released
section, with download links) and upcoming (an "Also in the Pipeline" section, named, no
links). Use mcp__filesystem__edit_file; preserve surrounding prose exactly.
Phase 3 — Platform Freshness Ledger + roster
In engineering/document-production/PUBLICATION-ROSTER.md:
3a — Platform Freshness Ledger (the "## Platform Freshness Ledger" code block). This
step is the sole writer of the PUBLISH line (prepare-manual deliberately does not
write it — the PDF must be verified clean first). Append/
update this manual's PUBLISH line at the PDF's mtime (the authoritative generation
time), newest on top, collapsing any older PUBLISH for the same manual:
2026-06-10 23:41 PUBLISH <slug> (vX.Y.Z, NNNpp — note)
Then prune: drop any PLATFORM line that now sits below a PUBLISH line for every
consuming manual (fully absorbed — git keeps the history). Update the freshness status table
so this manual reads ✅ current.
3b — roster row + status. The roster is organized by lifecycle status (Done · In progress
· Upcoming · Abandoned) with a Type column. On a release, move the document's checkbox row into
the ## Done matrix (from ## In progress/## Upcoming if it was there), set its version + gate
columns (e.g., Platform ⏳ → ✅; Released ✅), and update its entry in the Done ### Detail
list. Fix any now-stale prose the release falsifies (e.g., "awaiting migration", "only X remains").
3c — sync the manual-status dashboards (MANDATORY — these MIRROR the roster). Two
engineering dashboards restate the roster's per-manual status and drift on every release if
not updated in lockstep. They are deterministic roster mirrors — NOT the "broader docs" Phase 4
deliberately leaves alone — so update them HERE, every release (this is the manual-release
analogue of release-yamls' dashboard-ledger step):
engineering/document-production/README.md — the status-mirror tables (Done / In
progress / Upcoming / Abandoned). Move this manual's row into Done and set version/pp to
match the roster row you wrote in 3b. If it was previously in In progress/Upcoming, remove it
from there.
engineering/README.md — the front-door Manual row. Update its one-line manual-head
summary (e.g., the <Manual> vX.Y.Z ✅ shipped fragment) to the new version; while you're in
that cell, correct any sibling manual it still misstates (these front-door summaries lag).
- Verify:
grep -rn "v<PRIOR_VERSION>" engineering/README.md engineering/document-production/README.md
returns nothing for this manual.
3d — Update the community-review announcement draft (MANDATORY every manual/app-note release).
The review-announcement post is a living draft in DRAFTS/
(DRAFTS/COMMUNITY-REVIEW-ANNOUNCEMENT-v*.md, gitignored — the user publishes it by hand). It carries
a summary table (Manuals / App Notes, each with Download · View Changes · Examples links + version)
and a warm "why you'd reach for each one" blurb per document. A release that updates the README index
(Phase 2) but not this draft leaves the announcement lying about the current set — and because the
draft isn't committed, the drift is silent (it has lagged multiple releases before this step
existed). So, in the same pass:
- New document → add a table row (
**NEW:** <Title> | [Download] <br>[vX.Y.Z] | [View Changes] | [Examples] (.zip)) in the Manuals or App Notes block, AND a warm reach-for-this blurb in the
matching "why you'd reach for each one" section. Match the post's voice and its COG all-caps
convention.
- Existing document → bump its row's version (the transition endpoint, e.g.
[v1.0.0 -> v1.0.3]);
the evergreen blurb usually needs no change.
- Spot-check every other row's version against the roster while you're in the file — this draft
drifts across releases that forgot it; fix any stale version you find.
This is the community-facing twin of the README index (Phase 2d): the README is committed and pushed;
this draft is NOT part of the git hand-off (Phase 5) — it stays a local draft for the user to post.
Phase 4 — Enclosing-doc audit (verification, not auto-edit)
Sweep for stale references the Phase-2/3 edits don't catch — i.e. everything except the
public index (Phase 2), the roster/ledger (3a/3b), and the two manual-status dashboards (3c,
already updated). Surface candidates; do NOT auto-edit the rest (broader docs need explicit user
authorization per CLAUDE.md).
grep -rn "Version <PRIOR_VERSION>" --include="*.md" \
| grep -vE "deliverables/documents/DOCs/|\.backup\.|opus-master/CHANGELOG.md"
grep -rn "<PRIOR_DATE> - Community Review" --include="*.md" | grep -v "\.backup\."
grep -rln "<MANUAL_NAME>" --include="README.md" | grep -v "engineering/document-production/"
Classify: top-level repo docs (README.md, CHANGELOG.md) and other deliverables READMEs
are candidates — surface them. Report tightly; if zero, "Enclosing-doc audit: clean."
Phase 5 — Hand off git (suggest, do NOT execute)
Per CLAUDE.md, git actions need explicit user authorization. End with the suggested commands
covering everything the release touched (one commit per wave is fine):
git add deliverables/documents/README.md \
deliverables/documents/DOCs/<slug>-changelog.md \
deliverables/documents/DOCs/<PDF> \
engineering/document-production/PUBLICATION-ROSTER.md \
engineering/document-production/README.md \
engineering/README.md
# plus, if uncommitted from prior sessions: the manual's opus-master/CHANGELOG.md (post-audit) +
# source, MANUAL-DESCRIPTOR.md, any shared platform edit, the workspace render, and a force-add
# for a gitignored example ZIP: git add -f deliverables/documents/DOCs/<slug>-src.zip
# ^ the ZIP name carries NO date — a stable, permanent filename overwritten in place each
# release (git tracks history). A dated name mints a new URL every rebuild, forcing a
# hand-edit of every download link at two sites (roster post + review-page README) and
# orphaning the prior dated copy in DOCs/. Version lives in the roster row + changelog.
git commit -m "Release <slug> vX.Y.Z"
git tag -a <slug>-vX.Y.Z -m "<Manual Name> vX.Y.Z"
Tell the user: review the diff, then run these; the push activates the
raw.githubusercontent.com download links; tagging gives audit-changelog a baseline for the next release. If Phase 4 surfaced
candidates, tell them to decide on those before committing so the release is captured in one
logical commit.
What this skill does NOT do
- Does not generate the PDF (PDF Forge does) or place it in
DOCs/ (the user does).
- Does not edit
opus-master/CHANGELOG.md content (audit-changelog audits; the user authors).
- Does not run
git add / commit / tag / push (suggests only).
- Does not auto-edit broader docs beyond the release artifacts it owns: the deliverables README
index (Phase 2),
PUBLICATION-ROSTER.md (Phase 3a/3b), the two roster-mirror dashboards
(engineering/README.md + engineering/document-production/README.md, Phase 3c), and the
gitignored community-review announcement draft (Phase 3d). Phase 4 surfaces stale refs in other
top-level/deliverables READMEs but does not modify them.
Error handling
- PDF content incomplete / page-count drop / outline gap (Phase 1) → STOP. Report what's
missing + the likely stage (pandoc-swallow vs. xelatex error). This is the gate; never
promote a book that failed verification.
- Changelog audit
NOT READY (Phase 2a) → STOP. Fix opus-master/CHANGELOG.md, re-run
/audit-changelog <slug> until READY / READY WITH NOTES, THEN copy the fixed source up to
DOCs/. Never promote (copy up) a changelog that hasn't passed the audit.
- PDF missing → stop: generate via Forge and place it first.
- CHANGELOG missing → nothing to release.
- CHANGELOG topmost version == already-published version in README → stop, ask (re-release?).
- README entry has unexpected structure → stop, ask; don't guess line positions.
- Multiple
(DOCs/<PDF>) matches → ambiguous; ask.
Tool preferences (per CLAUDE.md)
- Filesystem reads/edits →
mcp__filesystem__* (use mcp__filesystem__edit_file for README,
not sed/echo).
- File copies,
git (status/log/suggested commands), pip --user, PyMuPDF scripts → Bash.