| name | pages-publishing |
| description | Create and publish Plane pages — sprint reports, retro notes, release notes, roadmap, meeting notes, decision logs (ADRs), specs, runbooks, and any general-purpose page as formatted HTML. Use when the user wants to publish a report, write a sprint summary, create a retro page, generate release notes, share a roadmap, document a decision, write a spec, capture meeting notes, or create any Plane page. Includes reusable HTML templates and Plane editor compatibility rules. |
Pages Publishing
Plane pages are rich HTML documents attached to a project or the workspace. Use them to publish durable artifacts: sprint reports, retros, release notes, roadmaps, ADRs, and stakeholder updates.
Tool Name Resolution
Resolve real tool names via the connector-bootstrap skill.
Available Actions
| Action | Purpose |
|---|
create_workspace_page | Create a page at the workspace level |
retrieve_workspace_page | Get a workspace page |
create_project_page | Create a page inside a project |
retrieve_project_page | Get a project page |
HTML Formatting Rules
Plane pages accept a restricted HTML subset in description_html. The Plane editor parses HTML through a Tiptap/ProseMirror schema — anything outside the schema is silently transformed or stripped. What you send is not always what you get.
Safe tags:
- Structure:
<h1>, <h2>, <h3>, <p>, <hr>
- Lists:
<ul>, <ol>, <li> (see List Rendering Gotchas below)
- Ordered list start offset:
<ol start="5"> is preserved
- Emphasis:
<strong>, <em>, <code>, <pre>
- Tables:
<table>, <thead>, <tbody>, <tr>, <th>, <td>
- Links:
<a href="…">
- Hard break inside text:
<br> (becomes a hardBreak node inside the surrounding paragraph)
Avoid: inline <style>, <script>, <iframe>, raw markdown, any custom data-* attributes (see gotchas below).
List Rendering Gotchas — Critical
Plane's editor is strict about list structure. Several "valid HTML" patterns silently produce broken, disjointed, or empty-bullet output. These were verified by roundtrip testing (submit HTML → retrieve stored ProseMirror doc → compare). Follow these rules or your published page will look wrong.
Gotcha 1 — <li> must start with text, not a nested list
Broken: a <li> whose first child is <ul> or <ol> (no own text).
<ul>
<li><ul><li>Orphan child</li></ul></li>
<li>Normal sibling</li>
</ul>
Stored result: three separate top-level bulletList nodes — one empty, one with the "Orphan child", one with the "Normal sibling". The outer list is torn apart at every child-first <li>.
Correct: always put at least one text character in the parent <li> before the nested list.
<ul>
<li>Topic<ul><li>Sub-point</li></ul></li>
<li>Normal sibling</li>
</ul>
This is the root cause of "broken lists with gaps" that most users see.
Gotcha 2 — Task lists / checkboxes via data-checked are silently stripped
<ul>
<li data-checked="true">Done action</li>
<li data-checked="false">Pending action</li>
</ul>
Plane's schema does not recognize data-checked on <li> at the HTML import layer — there is no taskList / taskItem node produced. The attribute is cosmetically preserved in description_html but the stored ProseMirror doc is plain bulletList.
Correct: use Unicode status markers as text instead. They render identically in any viewer and do not depend on editor features.
<ul>
<li>✅ Done action</li>
<li>⏳ In progress action</li>
<li>⬜ Not started action</li>
</ul>
Gotcha 3 — Never emit empty <li></li>
<ul>
<li>Before</li>
<li></li>
<li>After</li>
</ul>
An empty <li> is stored as a listItem with an empty paragraph and renders as an empty bulleted line. When a template placeholder like {{NOT_DELIVERED_ITEMS}} has no data, do not substitute an empty string into <ul>…</ul> — either emit the whole <ul> block conditionally or substitute <li><em>None</em></li>.
Gotcha 4 — Don't mix inline text with block elements inside <li>
<li>Text then<p>paragraph inside</p>and more text</li>
Each text run and each <p> becomes its own paragraph inside the listItem, stacking them vertically under one bullet marker. If you need a paragraph, put it outside the list. If you want a single bullet with multi-line text, use <br> inside one text run:
<li>First line<br>second line of the same bullet</li>
Gotcha 5 — Two <ul> blocks back-to-back stay separate
This is not a bug, but worth knowing: if you emit two adjacent <ul> blocks without a heading or <hr> between them, they stay as two separate lists (not merged). Fine for most cases, but if you want merged output, put the items in one <ul>.
Quick Checklist for Template Authors
Before publishing any generated HTML:
- No
<li> starts with <ul> or <ol> — every parent list item has leading text
- No empty
<li></li> — conditional render the whole <ul> block for empty data
- No
data-checked, data-*, or other custom attributes — use Unicode markers
- No mixed text +
<p> inside a single <li> — use <br> for multi-line items
- Nested lists use at most 3 levels (deeper works but is hard to read)
<ol start="N"> is preserved if you need numbered lists starting mid-sequence
Cleanup Limitation — Write-Once Pages
Many Plane MCP connectors expose only create_*_page and retrieve_*_page — no update_*_page, no delete_*_page, no archive_*_page. Pages published through this plugin are effectively write-once via the API: to edit or delete, users must open the page in the Plane web UI and do it manually.
Consequences:
- Do not use
/publish-report in a loop that overwrites the same target — each run creates a new page.
- Roadmap pages that the team edits weekly are best created once and then updated manually in the Plane UI, not regenerated every week.
- When testing publishing flows, use a throwaway project (like a "Sandbox") because the test pages will remain until manually cleaned up.
If your specific connector does expose update or delete, you can use it — but do not assume it is available.
HTML Templates
Reusable templates live as separate files — load only the one you need:
Each template uses {{PLACEHOLDER}} tokens. Render by replacing tokens with concrete values gathered from Plane. Inlined previews follow for quick reference.
Sprint Report Template
<h1>Sprint 14 Report — Billing v2</h1>
<p><strong>Dates:</strong> 2026-03-24 → 2026-04-04 · <strong>Goal:</strong> Users can see their Stripe invoices in-app</p>
<h2>Summary</h2>
<ul>
<li>Completed: <strong>34 / 40 points</strong> (85%)</li>
<li>Velocity vs last 3 sprints avg: 34 vs 32 (+6%)</li>
<li>Goal achieved: <strong>Yes</strong></li>
</ul>
<h2>Delivered</h2>
<table>
<thead><tr><th>ID</th><th>Title</th>PointsOwner
PROJ-148Stripe webhook receiver5@alice
Not Delivered
PROJ-152 — PDF export (3 pts) — blocked by vendor API, transferred to Sprint 15
Metrics
Cycle time: 2.4 days avg
WIP peak: 6 (limit 7)
Blockers encountered: 2
Next Sprint
Goal: …
Retrospective Template
<h1>Sprint 14 Retrospective</h1>
<p><strong>Date:</strong> 2026-04-04 · <strong>Attendees:</strong> @alice, @bob, @carol</p>
<h2>Previous Action Items</h2>
<ul>
<li>✅ Enable daily standup async thread</li>
<li>⏳ Document Stripe webhook schema — carried over</li>
</ul>
<h2>What Went Well</h2>
<ul><li>…</li></ul>
<h2>What Didn't</h2>
<ul><li>…</li></ul>
<h2>Action Items</h2>
<table>
<>ActionOwnerDue
Add Stripe mock to local dev@aliceSprint 15
Release Notes Template
<h1>Release v2.0 — 2026-04-08</h1>
<h2>Highlights</h2>
<ul>
<li>Multi-tenant support</li>
<li>New billing portal powered by Stripe</li>
</ul>
<h2>New Features</h2>
<ul><li>…</li></ul>
<h2>Improvements</h2>
<ul><li>…</li></ul>
<h2>Bug Fixes</h2>
<ul><li>…</li></ul>
<h2>Breaking Changes</h2>
<ul><li>…</li>
Upgrade Notes
…
Roadmap Page Template
<h1>Roadmap — Q2 2026</h1>
<h2>Now (this sprint)</h2>
<ul><li><strong>Billing v2</strong> — Stripe migration, 65% complete</li></ul>
<h2>Next (next sprint)</h2>
<ul><li><strong>Multi-tenant support</strong> — design done, implementation starts Sprint 16</li></ul>
<h2>Later (this quarter)</h2>
<ul><li><strong>Enterprise SSO</strong> — scoping</li></ul>
<h2>Parked</h2>
<ul><li><strong>Mobile app</strong> — revisit Q3
Publishing Workflow
1. connector-bootstrap → resolve tools
2. list_projects → pick project_id
3. Gather data from Plane (cycle data, work items, metrics)
4. Render HTML using a template above
5. create_project_page({
project_id,
name: "Sprint 14 Report",
description_html: "<...>"
})
6. Share the page URL with stakeholders
General-Purpose Page Templates
The templates above are tied to specific reporting workflows. The /page command also supports general-purpose templates for everyday documentation needs. Use these when the user wants any Plane page that is not a sprint/retro/release/roadmap/milestone report.
Meeting Notes
<h1>{{MEETING_TITLE}}</h1>
<p><strong>Date:</strong> {{DATE}} · <strong>Attendees:</strong> {{ATTENDEES}}</p>
<h2>Agenda</h2>
<ol>
<li>{{AGENDA_ITEM_1}}</li>
</ol>
<h2>Discussion</h2>
<p>{{NOTES}}</p>
<h2>Decisions</h2>
<ul>
<li>{{DECISION_1}}</li>
</ul>
<h2>Action Items</h2>
<table>
<thead><tr><th>Action</th><th>OwnerDue
{{ACTION_1}}{{OWNER_1}}{{DUE_1}}
Decision Log (ADR)
<h1>ADR-{{NUMBER}} — {{TITLE}}</h1>
<p><strong>Status:</strong> {{STATUS}} · <strong>Date:</strong> {{DATE}} · <strong>Deciders:</strong> {{DECIDERS}}</p>
<h2>Context</h2>
<p>{{CONTEXT}}</p>
<h2>Decision</h2>
<p>{{DECISION}}</p>
<h2>Alternatives Considered</h2>
<ul>
<li><strong>{{ALT_1}}</strong> — {{ALT_1_REASON}}</li>
</ul>
<h2>Consequences</h2>
<p><strong>Positive:</strong> {{POSITIVE}}
Negative: {{NEGATIVE}}
Risks: {{RISKS}}
Use ADR status values: Proposed, Accepted, Deprecated, Superseded by ADR-N.
Spec / Design Doc
<h1>{{FEATURE_NAME}} — Design Doc</h1>
<p><strong>Author:</strong> {{AUTHOR}} · <strong>Status:</strong> {{STATUS}} · <strong>Last updated:</strong> {{DATE}}</p>
<h2>Problem</h2>
<p>{{PROBLEM_STATEMENT}}</p>
<h2>Goals</h2>
<ul><li>{{GOAL_1}}</li></ul>
<h2>Non-goals</h2>
<ul><li>{{NON_GOAL_1}}</li></ul>
<h2>Proposed Solution</h2>
<p>{{SOLUTION}}</p>
<h2>API Changes
{{API_SAMPLE}}
Rollout Plan
{{ROLLOUT_STEP_1}}
Open Questions
{{QUESTION_1}}
Linked Work
Epic: {{EPIC_LINK}}
Tracking issues: {{ISSUE_LINKS}}
Runbook
<h1>Runbook — {{SCENARIO}}</h1>
<p><strong>Owner:</strong> {{OWNER}} · <strong>Severity:</strong> {{SEVERITY}} · <strong>Last verified:</strong> {{DATE}}</p>
<h2>Symptoms</h2>
<ul><li>{{SYMPTOM_1}}</li></ul>
<h2>Diagnosis</h2>
<ol>
<li>{{DIAGNOSIS_STEP_1}}</li>
</ol>
<h2>Mitigation</h2>
<ol>
<li>{{MITIGATION_STEP_1}}</li>
</ol>
<h2>Recovery</h2>
<ol>
{{RECOVERY_STEP_1}}
Postmortem Trigger
{{WHEN_TO_FILE_POSTMORTEM}}
Related Dashboards
{{DASHBOARD_NAME}}
Runbook discipline: every action in Mitigation/Recovery is a single, copy-paste-runnable command — no "configure the thing" sentences.
Blank
<h1>{{TITLE}}</h1>
<p><em>Last updated: {{DATE}}</em></p>
<p>{{BODY}}</p>
The blank template is for when the user wants control of the body. Always include the "Last updated" line at the top — Plane does not surface page freshness in the sidebar.
Template Selection Map
/page create ... --from-template <name> resolves these template names:
| Name | Template | Best for |
|---|
sprint-report | Sprint Report | end-of-cycle summary |
retro | Retrospective | sprint retro notes |
release-notes | Release Notes | version release |
roadmap | Roadmap | quarterly planning |
milestone-update | Milestone Update | release tracking |
meeting-notes | Meeting Notes | any meeting |
decision-log | Decision Log (ADR) | architectural decisions |
spec | Spec / Design Doc | feature design before build |
runbook | Runbook | incident response |
blank | Blank | freeform content |
Best Practices
- Publish the sprint report within 24 hours of sprint close — memory fades fast.
- Link the report from the cycle's description for easy discovery.
- Keep release notes audience-appropriate: customer-facing pages omit internal work items.
- Roadmap pages should be updated weekly, not created from scratch.
- Never publish PII or secrets on workspace pages — they may be broadly visible.
- Always include a "Last updated" line — Plane does not show page freshness in the sidebar.
- For ADRs, use a numbered prefix (
ADR-001, ADR-002) so they sort naturally.