Write, check, repair and publish a plan page: a project's plans and subject files under its plans directory, rendered by .agents/pstack/plan-page.mjs and rendered as one local HTML page per subject with a local index of every subject, and published to claude.ai at each hand-back for comments and feedback. The page is how planned work hands back to the user. Use before writing or changing a plan, a subject file in <plans>/topics or its page; at every stop that hands back work with a plan, such as a review verdict other than a review-only panel's, a next answer or a playbook's close; before writing or updating a PR body, which renders from the PR's plan, or a comment about your commits on someone else's PR; when a plan page or its claude.ai artifact looks wrong or refuses to render; to republish a page; or to change the page shape or a playbook's page sections.
81%
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
Passed
No findings from the security scan
The user reads pages, not replies; the block's Plan pages rule says when to publish and what the reply holds. A subject is one thing the work changes, and it has one page. Its file, <plans>/topics/<slug>.md, holds the current state. Each plan that continues it is an iteration carrying only its delta. node .agents/pstack/plan-page.mjs <plan> renders the page from both into <plans>/artifacts/, a local copy that regenerates from the committed plan and subject files, which are the record. node .agents/pstack/plan-page.mjs --index renders every subject's page there and an index linking them. A published claude.ai page is where the user comments on one hand-back; it is not the record. sync-pstack ships that renderer and status.mjs into every project it manages.
Read references/shape.md before writing a plan or a subject file. It holds every file shape, the page order and the renderer's refusals.
| Mode | Use it to |
|---|---|
| Render | publish a page at every stop the block's Plan pages rule lists |
| Refresh | render the named plan's page again with Render steps 5, 7 and 8 while the plan is not done; with no plan named, use the open plan of the subject the session works on, and ask when there is none |
| Check | audit a plan and its subject before handing the page back |
| Repair | fix a page whose shape is wrong |
| PR | write or update a PR body as the page of the PR's plan, or a comment about your commits on someone else's PR |
| Change | change the page shape, the renderer, or a playbook's page sections |
Page: URL, never to a second page.Topic: <slug> under Status:, or through the first entry of the frontmatter list the project names in pageTopic.field, so list the scope that owns the change first. A plan whose list-named subject has no file yet keeps its own page until someone creates that file; the renderer refuses a Topic: line whose subject has no file until that file exists.Playbook: <name> under Status:, so the rail shows it. When a project playbook in .agents/playbooks/ writes the plan, name that one, so its page sections lead and its required sections apply; otherwise name pstack's, such as feature or bug-fix. The renderer refuses a name that is neither.## Brief first, then its ## Teach, per references/shape.md. They are the parts the owner reads first. Load pstack:teach and follow its method for Teach, reusing what the run's how and why work found instead of running them again. A page with a brief then shows How it works, Demo, Needs you, Picked for you, the plan's changes, Verification once the plan is done, and Open work, and the plan file keeps the steps and history. The PR body walks the same order.node .agents/pstack/plan-page.mjs <plan>. It refuses every shape it can check and names the fix. Fix the plan or the subject file, never the HTML.## Demo per references/shape.md before the hand-back, reusing the frames verification captured. At a close, write the plan's ## Close before rendering: what landed, the proof and its limits, the counts the block's Todo list and close rule requires, reversals and deviations first, and open work with owners and stops. The page shows the brief, not the Close, so rewrite Changes and Risks to tell the owner what the Close holds.node .agents/pstack/plan-page.mjs --index, then render the plan again. --index renders every subject's page and the index locally, and it leads each page with its newest open iteration. The second render restores the page of the plan this stop is for. The index is never published.Page: URL, or to the one-off plan's own, so the user can comment on it. In Claude Code the publish goes through page-stop.mjs, as the Plan pages rule says: read every file it names in full, then send what it prints, and never publish the whole page the renderer prints. A checkout publishes to its own Page: line when that line changed since the checkout forked from origin/<branch>, as long as origin/<branch>'s line still matches the fork or names a link the clone recorded as gone. Otherwise it publishes to origin/<branch>'s line, which the render prints. A subject's first publish makes its page, and the helper then tells the lead to write Page: <url> under the subject file's title, or under a one-off plan's Status: line. When that link no longer opens, because the page was deleted or belongs to another account, run the helper again with --new without asking the owner, send what it prints and replace the line as it says. --new records the old link as gone in a file that every worktree of the clone reads. The owner may have deleted it on purpose, so the plan the reply links also gets a Defaults row that names the old and new links, keeps the new page and gives the word that drops it. To read a page, open its rendered file under the plans directory or its plan and subject files, never the published page, except the index.html read by path that a refused publish's --base needs. The block's Plan pages rule says what the reply holds and what a Codex session does instead. On a subject page's first publish, offer once to pin it. When the owner asks in chat where a page is, answer with the link and offer the pin again.Run node .agents/pstack/plan-page.mjs <plan> --check. It runs every refusal and writes nothing. Then read the plan and its subject for what the renderer cannot judge:
pstack:bro voice. Run the plan's prose pass, such as unslop, first. Then, as the last pass, load pstack:bro and restate the brief, Teach, each open question, each Defaults row and each sentence above a before and after pair through it, so no later pass undoes it. Keep the plainer version, and drop a detail the owner does not need when the plan's other sections already hold it. The pass may change a claim's words but never its scope, so keep every count and every qualifier that limits a claim, such as which cases were tried. After the pass, compare each restated claim with the sentence it replaced and with the log row that records it. One bro pass turned "no better on the tasks tried" into "added nothing" and two trials into three.before fence is a real call site at the commit before the plan, and its after fence the same call in the checkout, or the planned call while the plan is unbuilt. Each fence names its path in a comment, with one sentence above the pair.Proof: clause, on the exact bytes and in the place the proof ran. A change to the shared source counts as live in a project only after that project syncs from its pushed commit..md name in a plan or subject file as a doc link and refuse one under the ignored run directory. Then move that pointer to a decision-log row's proof: label, as the block's Plans and trails rule says, and never replace it with prose.Status: is one short line: the state word, then what the plan waits on. Open items go in their own section, each with its owner, where it is tracked and its stop.Status: was not flipped yet; flip it instead of restoring. For an early fold, restore the subject file to its state before the open plan from the plan's before fences and git show <base>:<path>, and move the delta into the plan.A PR body is the page of the PR's plan, rendered as GitHub Markdown, so a reviewer reads the same brief, rail, models, How it works, Demo, picks and API pairs the owner reads.
Plans: line under Status: lists each plan it carries, by path relative to it. Title it after the change, as the PR title reads; the renderer refuses a PR number in the title, which says nothing inside that PR. Write it with Render steps 3 to 6 so its brief, Teach, Demo, Public API pairs and Defaults cover the whole diff, and give each change no plan covers its place in those sections. Whichever plan the PR uses, rewrite its Changes and Risks for a reviewer at PR time, since a brief written mid-run speaks to the owner; the renderer refuses a PR number in the brief. Its Demo gives one step per change a reviewer can see, with that change's before and after clips as frames and any limit of the clip in the step. Its ## Verification names each run and where proof stops in at most three bullets, and its ## Open work names what the PR leaves out. Drop local run-folder paths from every section the PR shows, because they do not open on GitHub. Run Check on it, pstack:bro last.node .agents/pstack/plan-page.mjs <plan> --pr from the repository root, so its links point at the pushed commit. It runs every refusal a page render runs, refuses a plan without a brief whatever its state, and writes <plans>/artifacts/<name>.pr.md. It prints that path, then one --attach <file> line for each local Demo or Teach frame the body has not uploaded yet. With --public it also leaves out the run's models and stages, Open questions, Defaults and the plan link, for a PR whose body a reader outside the team sees, such as one opened on another person's fork or in place of their PR. A line those sections do not hold still follows the Writing rule's limits on public text, so check the rendered file against them. Pass --public again on every rerender of that body. land.mjs renders the PRs it opens without --public.land.mjs, land.mjs renders this body and opens the new PR itself, and its guard denies an agent's own gh pr create into the trunk branch. Elsewhere a new PR opens with gh pr create --body-file <file> and the printed --attach lines. To update a PR, save its body with gh pr view <number> --json body --jq .body > <run directory>/body.md, render with --into <run directory>/body.md, and run gh pr edit <number> --body-file <file> with the printed --attach lines. gh uploads each attached frame and rewrites its reference. The render replaces only the text between its plan-page markers, keeps the rest of the body, such as a release block or hand-attached videos, and reuses each frame the body already uploaded. Rerender after each push that changes the plan or its diff, so the body stays true to the pushed diff.On a PR someone else opened, leave their description alone. One comment from you covers only your commits on top of theirs:
## On top of the PR section that lists what you kept, changed and dropped from their version. The comment has no other list of what your commits changed, so the renderer refuses a comment without it. The plan's Verification names where the proof of your commits stops, such as a case only a throwaway probe checked, because the comment shows no Risks.gh pr view <number> --json body --jq .body > <run directory>/description.md. Then run node .agents/pstack/plan-page.mjs <plan> --comment <author's last commit> --against <run directory>/description.md. It writes <plans>/artifacts/<name>.comment.md and links DiffsHub's compare view from that commit to your head. The comment holds that link, On top of the PR, the Demo, Public API, the playbook's design sections and Verification. It leaves out what the author already knows or only the owner reads: the brief, How it works, the run's models and stages, open questions, Defaults, open work and the plan link. It also leaves out each Demo clip the description already shows, and each step whose clips all are.gh pr comment <number> --body-file <file> and the printed --attach lines. After a later push, save that comment's body to the run directory, render with --into it as well, and update it with gh pr comment <number> --edit-last --body-file <file> while it is still your last comment. Once someone has replied below it, post a new one.The renderer source is skills/sync-pstack/assets/pstack/plan-page.mjs in the dotai checkout, with status.mjs beside it. Its tests are the plan-page tests in skills/sync-pstack/scripts/sync-pstack.test.mjs. A section that only one project's plans write belongs in that project's playbook frontmatter, never in the renderer.
Run sync-pstack's Lesson mode, which owns the gates, the corpus rules and the delivery. This mode adds the renderer's specifics inside its steps:
main, never a worktree, even when another session holds uncommitted edits there, as dotai's AGENTS.md says..agents/pstack.json and playbooks to scratch once, for the old renderer, then cp -R that copy and apply the change's migration to it for the new one, and diff the two plan lists before any body. When the live trees hold no instance of a state a refusal gates, also replay subject files at past commits where an iteration was open, and the scratch copies Repair kept. Give a renderer that predates --check an adapter that exits before it writes. Run both over every plan, list each newly refused plan with its owner, then render every plan with both and diff the bodies, ignoring the Updated line.Status: flipped when no live subject is in a mode. Publish both versions for the owner to compare. Before the dotai commit, also render the change's own plan in its closed state on its real subject page.A project playbook in .agents/playbooks/<name>.md names the review sections its plans write, as comma lists in its frontmatter beside extends and when:
page-lead: What other editors do, Document shape, Layer and owner
page-pairs: Document shape
page-require: What other editors do
page-pr: What other editors do, Document shapepage-lead sections render right after Public API, the leading plan's playbook first.page-pairs sections hold before and after pairs, like Public API.page-require sections must exist in the subject file, or in the plan that leads the page when that plan has a brief, whenever the project's pageTopic.hub file exists for that subject.page-pr sections are the page-lead sections a reviewer judges the design by. The PR body shows them after Public API, in page-lead order. A PR shows each section that the playbook of any plan it carries names, and a PR plan without one shows each carried plan's under that plan's title. A title missing from the same playbook's page-lead is refused.When the leading plan names no playbook, every playbook's sections and requirements apply. The playbook, or the skill it names, defines what each section holds.
udecode/plate
f0c616a
If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.