generate-project-plan
Turn a PRD (plus optional codebase grounding) into a FigJam project plan board. Section set is not fixed — the skill proposes candidates from the research and the user picks which to include. For each picked section, the skill proposes content + rendering shape (block) and the user confirms.
Mandatory prerequisites
Foundation skills (load by name; available in figma/mcp-server-guide):
figma-use — Load once per session. Stays in context for all use_figma calls.
figma-use-figjam — Re-load before every use_figma call.
figma-generate-diagram — Re-load before every generate_diagram call.
Foundation references (in this plugin):
foundation/palette.md — section + sticky + text palette constants (hex/255).
foundation/layout.md — canvas geometry, sizing rules, placeholder lifecycle.
foundation/plugin-api-traps.md — documented traps for FigJam use_figma.
foundation/codebase-grounding.md — Step 1 expansion rules.
Section catalog:
section-catalog.md — the ~10 candidate sections with default blocks and palette.
Block subskills (one per content type — re-load the one(s) you need before each use_figma fill call):
| Block |
File |
When to load |
| Top-level section |
blocks/section.md |
Skeleton pass (Step 6) and every fill call (Step 7) |
| Nested section |
blocks/nested-section.md |
Fills that group sub-content (e.g. "Design Decisions 1/2/3") |
| Intro callout |
blocks/intro-callout.md |
Fills that open with a highlighted intro (e.g. Motivation) |
| Text primitives |
blocks/text-primitives.md |
Any fill that uses body paragraphs, H3 subheaders, or bulleted lists |
| Table |
blocks/table.md |
Fills with structured data (Resources, Goals, Dependencies, Rollout, Milestones) |
| Multi-column text |
blocks/multi-column-text.md |
Fills with 2–4 option columns (Design Decisions alternatives) |
| Sticky column |
blocks/sticky-column.md |
Fills with lists of stickies (Success Metrics, Risks, Open Questions) |
| Diagram section |
blocks/diagram-section.md |
Right-column diagram sections (Step 8) |
| Metadata strip |
blocks/metadata-strip.md |
Skeleton pass — one metadata strip at top of board |
Also pass skillNames: "figma-use,figma-use-figjam,generate-project-plan" on use_figma calls (logging only).
Visual UI conventions — STRICT, do not deviate
These are derived from a canonical reference board. Read the source-of-truth files for the full constants; this section is a single-place summary so an agent can answer "what color / size / font / padding?" without hunting.
Colors (two-tone per section)
Every left-column section uses two coordinated colors of the same hue: a very-pale ARCH_PALE background, and a slightly-more-saturated FigJam-SECTION palette color for any table header inside that section. Right-column diagram sections are pure white.
Section bg (ARCH_PALE.X) |
Table header (TABLE_HEADER.lightX) |
Hue |
#F8F5FF |
#DCCCFF |
violet |
#EBFFEE |
#CDF4D3 |
green |
#DBF0FF |
#C2E5FF |
blue |
#F5FBFF (alt) |
#C2E5FF |
pale blue |
#FFF7F0 |
#FFE0C2 |
orange |
#F1FEFD |
#C6FAF6 |
teal |
#FFFBF0 |
#FFEC BD |
yellow |
#FFEEF8 |
#FFC2EC |
pink |
#FFEEE8 |
#FFCDC2 |
red |
Source: references/foundation/palette.md. Never use the dark-saturated palette (#874FFF, #3DADFF, etc.) for table headers — that's for FigJam's standalone tables, not project-plan boards.
Architecture-diagram subgraph colors are auto-applied by generate_diagram and must not be overridden. Their canonical values: client #AFF4C6 rounded-rect, gateway #FFFFFF square (diamond if labeled "Load Balancer"/"ALB"/"LB"), service #E4CCFF square, datastore #BDE3FF cylinder, external #FFFFFF PREDEFINED_PROCESS, async #BDE3FF ENG_QUEUE.
Typography (font sizes)
| Element |
Size |
Font |
Color |
| H1 (board title) |
40 |
Inter Medium |
#1E1E1E |
| H2 (section title — first child of every section) |
40 |
Inter Medium |
#1E1E1E |
| H3 — full-width subhead (e.g. "Resources" inside Motivation) |
40 |
Inter Medium |
#1E1E1E |
| H3 — nested-section header (e.g. "Design Decision 1: …") |
32 |
Inter Medium |
#1E1E1E |
| H3 — column title in 2/3/4-col layouts (Risks col, Goals col) |
24 |
Inter Medium |
#1E1E1E |
| Body text |
16 |
Inter Medium |
#1E1E1E |
| Table cells (header AND body) |
16 |
Inter Bold |
#1E1E1E |
The three different H3 sizes are deliberate. 40 = matches H2 weight when subhead is alone in the section. 32 = sub-section header inside a child section (672px inner width). 24 = column title in narrow contexts (≤ 224px col width). Pick by container width, not by semantic depth.
Always load both Inter Medium AND Inter Bold at the top of any use_figma script that creates tables (Bold) plus any other text (Medium).
Section properties
| Property |
Value |
section.fills |
[{ type: 'SOLID', color: ARCH_PALE.X }] (left column) or ARCH_PALE.white (right column / diagrams) |
section.name |
"" — empty string. NO FigJam title-bar label. The H2 inside is the only title. |
| Inner padding (all 4 sides) |
32 (current default; reference uses 40-50, kept at 32 for now) |
| First child position |
(32, 32) |
| Width (left column) |
800 |
| Width (right column / diagram) |
max(1200, diag.width + 64) after diagram is reparented |
| Vertical gap between sections (inside the wrapper) |
64 |
| Hug behavior |
Manual: call section.resizeWithoutConstraints(w, maxChildBottom + 32) after appending children. Sections do NOT auto-grow. |
| Placeholder during build |
placeholder = true in skeleton pass; placeholder = false at the end of the section's fill. |
Outer wrapper + column alignment (STRICT defaults)
The board has one outer wrapper (unlabeled, white) plus the diagram column:
- Column wrapper — an unlabeled white SECTION at
(_, 0). Contains, in order from top:
- H1 project title (40px Inter Medium, charcoal) at
(64, 64) (= section padding)
- Body row of metadata: Owner / Status / Last updated / Source (16px Inter Medium, charcoal), at
(64, h1.y + h1.height + 16) — 16px gap below H1; 32px gap between each body cell
- 64px gap below the body row, then the 6 left-column sections stacked with 64px gutter
- Diagram column at
(columnWrapperRight + 64, 0). Each diagram is its own un-wrapped white SECTION; the top diagram's y aligns with the column wrapper's y (= 0). Diagrams stack with 64px gutter.
The metadata is embedded in the column wrapper (NOT a separate section). One column wrapper holds everything text-related.
Constants:
- Wrapper inner padding (all 4 sides): 64
- Vertical gap between sections inside the wrapper: 64
- Horizontal gap between wrapper right edge and diagram-column left edge: 64
- Top of the diagram column aligns with
wrapper.y (same horizontal axis as the wrapper top edge)
- Vertical gap between stacked diagram sections: 64 (matches the inner gutter)
const PAD = 64;
const leftIds = [/* all left-column section ids in order */];
const sections = [];
for (const id of leftIds) sections.push(await figma.getNodeByIdAsync(id));
// Compute bbox of the column in page coords
let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
for (const s of sections) {
minX = Math.min(minX, s.x); minY = Math.min(minY, s.y);
maxX = Math.max(maxX, s.x + s.width); maxY = Math.max(maxY, s.y + s.height);
}
const wrapper = figma.createSection();
wrapper.name = ""; // STRICT
wrapper.fills = [{ type: 'SOLID', color: WHITE }]; // ARCH_PALE.white
wrapper.resizeWithoutConstraints((maxX - minX) + 2*PAD, (maxY - minY) + 2*PAD);
wrapper.x = minX - PAD;
wrapper.y = minY - PAD;
// Reparent + translate to keep visual positions
for (const s of sections) {
const newX = (s.x - minX) + PAD;
const newY = (s.y - minY) + PAD;
wrapper.appendChild(s);
s.x = newX;
s.y = newY;
}
The wrapper has no header (no H2 inside) and no name — it's a pure container. Don't repeat the project title here; that lives in the metadata strip.
Position the diagram column after the wrapper exists:
const wrapperRight = wrapper.x + wrapper.width;
const diagramX = wrapperRight + 64; // 64px horizontal gap matches inter-section gutter
let y = wrapper.y; // align top with wrapper's top edge
for (const id of diagramSectionIdsInOrder) {
const d = await figma.getNodeByIdAsync(id);
d.x = diagramX;
d.y = y;
y += d.height + 64; // 64px vertical gutter between stacked diagrams
}
Vertical spacing (STRICT)
| Between |
Gap |
| Section top edge → H2 (first child) |
32 (= padding) |
| H2 → next child (body / intro callout / H3 / table / first column) |
24 |
| Body paragraph → H3 |
24 |
| H3 (any size: 40 / 32 / 24) → next child (body / table / list / column content) |
24 |
| Body → body |
24 |
| List → next block |
24 |
| Last child bottom → section bottom edge |
32 (= padding) |
Always position children using prevChild.y + prevChild.height + 24 — never use a fixed offset like prevChild.y + 60. The H3 has three different sizes (40 / 32 / 24), so a fixed offset is wrong by definition; always read prevChild.height after the font size is set.
When you change a header's font size after the fact, you MUST re-stack downstream children. Setting h3.fontSize = 40 grows the node's height; if the next child's y was computed before the resize, it will overlap.
Tables
- Both header AND body cells use
Inter Bold 16px (NOT Medium).
- Header text is
#1E1E1E charcoal on a light fill (matching the section's hue).
- Body cells leave fill at default white; text is charcoal.
- Headers do NOT use white-on-dark — that's wrong for this board style.
Diagrams (right column)
generate_diagram with useArchitectureLayoutCode: "FIGMA_DIAGRAM_2026" produces multiple page-level nodes (1–2 subgraph SECTIONs + bare SHAPE_WITH_TEXTs + CONNECTORs), NOT a single container. To wrap them in a section:
- Collect all new page-level nodes (exclude known plan-section IDs).
- Compute the bbox.
- Create a new SECTION sized to
bboxW + 64 × bboxH + 64 + 64 (HEADER_BLOCK = 40 H2 + 24 gap), fill ARCH_PALE.white.
- Reparent each diagram node, translating local coords to maintain visual layout.
- CRITICAL — delete and recreate every connector after reparent. The assign-to-self trick (
c.connectorStart = c.connectorStart) is NOT reliable: short connectors re-route fine, but long-bend connectors retain stale elbow waypoints and extend hundreds of pixels outside the section. Delete + figma.createConnector() from captured spec produces a clean route every time. See blocks/diagram-section.md for the spec-capture + recreate pattern.
- Connector labels — explicit
fontName + fontSize + fills ALL required. A fresh connector's text sublayer has no usable defaults. Set ALL FOUR: c.text.fontName = { family: 'Inter', style: 'Medium' }, c.text.fontSize = 14, c.text.characters = label, c.text.fills = [{ type: 'SOLID', color: CHARCOAL }]. Default text.fills is [] (empty array) so the label renders transparent and is invisible — read-back of c.text.characters lies; verify with a screenshot.
Diagram-section convention: section.name = "", H2 text node inside as the title (matches the rest of the board).
What NOT to use (common wrong defaults)
| Don't use |
Use instead |
Why |
#CDF4D3 (FigJam lightGreen) for section bg |
#EBFFEE (ARCH_PALE.green) |
Too saturated next to diagrams |
#C2E5FF (FigJam lightBlue) for section bg |
#DBF0FF or #F5FBFF (ARCH_PALE.blue / blueLite) |
Same |
#874FFF (FigJam dark violet) for table header |
#DCCCFF (FigJam lightViolet) |
Reference uses light-on-pale, not dark-on-pale |
| White text on dark table header |
#1E1E1E charcoal text on light header |
Same — pale-on-pale convention |
Inter Medium for table cells |
Inter Bold |
Reference uses Bold for all cells |
section.name = "Goals" (or any non-empty string) |
section.name = "" |
Reference uses empty names; H2 inside is the title |
| Reparenting only one subgraph from a generated diagram |
Reparent ALL page-level nodes (SECTIONs + SHAPEs + CONNECTORs) |
Architecture diagrams are not single nodes |
Trusting c.connectorStart = c.connectorStart to re-route every connector |
After reparent, delete and recreate every connector from a captured spec |
Long-bend connectors retain stale elbow waypoints; only figma.createConnector() produces a fresh route |
Setting only c.text.characters = label on a fresh connector |
Set c.text.fontName, c.text.fontSize, c.text.characters, AND c.text.fills |
Defaults: fontSize=missing, fills=[] (empty array → transparent) → label invisible despite correct read-back |
Step template
Every step below uses this shape. Read the step, then execute.
## Step N — <Name> [Type: Research | Confirm | Write]
Inputs required: …
Ask if missing: …
Tools / refs loaded: …
Do: (3–6 action bullets)
Checkpoint: (Research → self-check; Confirm → AskUserQuestion; Write → screenshot + AskUserQuestion)
Three step types:
- Research — read-only; checkpoint = self-check list.
- Confirm — no board writes, user decision gate; checkpoint =
AskUserQuestion.
- Write — creates/mutates FigJam; checkpoint = screenshot +
AskUserQuestion.
Step 1 — Gather context [Research]
Inputs required
- PRD file path or pasted text.
- Optional: codebase entry points (file paths, service names, doc paths).
Ask if missing
- "Where's the PRD? (path or paste)."
- "Any codebase entry points I should ground in? (paths / services / docs / 'none')."
Tools / refs loaded
Read, Glob, Grep.
foundation/codebase-grounding.md.
Do
- Read the PRD. Extract: title, problem, goals, non-goals, owner, audience, success metrics, rollout hints, risks.
- If entry points provided: follow
codebase-grounding.md — bounded 20-file cap, depth-1 imports, walk up to CLAUDE.md/ARCHITECTURE.md/OWNERS.
- Produce the tech-context object:
files_read, services, external_deps, key_modules, architecture_notes, ownership, expansion_truncated.
Self-check
- Have: project title, problem statement, at least 1 concrete goal, owner (or "TBD"), services touched (or empty list with a reason).
- Enough signal to draft candidate section cards in Step 2. If not, loop back and ask.
Step 2 — Propose candidate sections [Confirm]
Inputs required
- Tech-context object from Step 1.
Tools / refs loaded
section-catalog.md.
AskUserQuestion.
Do
- For each section in the catalog, decide if there is real content for it from Step 1. Skip catalog entries that would be empty or padding.
- For each qualifying candidate, produce a card:
- Title (catalog name)
- 1-line description (what this section will contain, specific to the PRD)
- Why suggested (which PRD facts or tech-context items justify it)
- Default block shape (from the catalog)
- Print all cards to chat.
- Fire
AskUserQuestion with a multiSelect question per batch of ≤4 candidates (max 4 questions per call, 4 options each → up to 16 candidates per call). Each option's label is the section title; description is the 1-line summary.
Checkpoint (AskUserQuestion)
- The multiSelect questions above. User ticks the sections they want. Store the selected set as
approved_sections.
- If zero sections selected → stop with a clean exit message. No file is created.
Step 3 — Per-section deep research [Research]
Inputs required
approved_sections from Step 2; tech-context from Step 1.
Tools / refs loaded
Read, Grep, Glob (optional).
Do
- For each section in
approved_sections, look up its catalog entry. The catalog declares what the section needs (e.g. Dependencies needs cross-team services + external deps + blockers).
- Compare what the section needs vs. what the tech-context has.
- Produce a gap list per section: specific facts the user must supply, framed as answerable questions (no "figure it out yourself" gaps).
Self-check
- Every
approved_sections entry has either ready (no gaps) or a specific non-empty gap list.
- Gaps are answerable — not vague prompts like "tell me more about X."
Step 4 — Per-section content + block proposal [Confirm]
Inputs required
Tools / refs loaded
AskUserQuestion.
section-catalog.md.
- Block reference(s) for the section's default shape (e.g.
blocks/table.md for Dependencies).
Do, per section (one at a time, or small batch if trivial):
- Fill the gap list — free-text prompt for prose;
AskUserQuestion for bounded choices.
- Propose:
- Content: the concrete bullets / rows / stickies that will appear.
- Block shape: the rendering block (body paragraph / table / multi-column / sticky column / …). Default from
section-catalog.md; offer alternative shapes where sensible (e.g. "As a table, or as a multi-column layout?").
- Show a short preview — section title + first line of body + block type summary.
- Fire
AskUserQuestion: "Use this content + shape? [Yes / Edit / Skip this section]."
- Edit → accept free-text amendments, re-show, re-ask. Skip → mark the section as
skipped; do not write it to the board.
Checkpoint
- Every section is
approved, edited+approved, or skipped. No board writes yet.
Step 5 — Create FigJam file [Write]
Inputs required
approved_sections (non-skipped); project title; planKey (Figma team plan).
Tools / refs loaded
create_new_file MCP tool.
whoami MCP tool (for planKey if not known).
use_figma (once).
figma-use (already in context from Step 5 onward).
figma-use-figjam (re-loaded for the probe).
AskUserQuestion.
Do
- Resolve
planKey: call whoami. If one plan → use it. If multiple → AskUserQuestion which team.
- Call
create_new_file with { planKey, fileName: "<project title>", editorType: "figjam" }. Capture file_key + file_url.
- Run the first-run probe (
use_figma):
const page = figma.currentPage;
return {
rootName: figma.root.name,
editorType: figma.editorType,
pageCount: figma.root.children.length,
firstPageName: figma.root.children[0].name,
currentPageChildrenCount: page.children.length,
};
Expect editorType === "figjam" and empty page. If not, halt and report.
Checkpoint (probe output + AskUserQuestion)
- Print probe return +
file_url.
- AskUserQuestion: "File created at
<file_url> — proceed to skeleton? [Yes / Cancel]." Cancel = stop, leave empty file.
Step 6 — Skeleton pass [Write]
Inputs required
approved_sections in taxonomy order; palette from foundation/palette.md; layout constants from foundation/layout.md.
Tools / refs loaded
use_figma (one call).
- Re-load
figma-use-figjam/SKILL.md.
- Re-load
blocks/section.md and blocks/metadata-strip.md.
foundation/palette.md, foundation/layout.md.
Do
- Create metadata strip per
blocks/metadata-strip.md (H1 + 4 body texts at board (0, 0)–(0, ~100)).
- For each approved section, create a top-level SECTION per
blocks/section.md — colored bg from ARCH_PALE, section.name = "" (no title-bar label — STRICT), placeholder = true, resizeWithoutConstraints(LEFT_COL_W, DEFAULT_H) for left-column sections or resizeWithoutConstraints(RIGHT_COL_W_MIN, DEFAULT_H) for right-column diagram sections.
- Position each left-column section at
(0, SECTION_TOP_Y + cumulative_y). Right-column sections at (832, SECTION_TOP_Y + cumulative_y_right).
- Return all created node IDs:
{ createdNodeIds: { metadataStrip: {...}, sections: { <slug>: "<id>", ... } }, status: "skeleton-complete" }. (The slug is internal, used to look up palette + drive Step 7 fills. It is never written to section.name.)
- Take an inline screenshot at
await figma.currentPage.screenshot({ scale: 0.3 }).
Checkpoint (screenshot + AskUserQuestion)
- AskUserQuestion: "Skeleton looks right? [Yes / Fix / Cancel]." Fix = targeted fix script; Cancel = stop.
Step 7 — Fill pass (one call per section) [Write]
Inputs required
- Approved content + block shape for each section; section ID from Step 6.
Tools / refs loaded
use_figma (one call per section).
- Re-load
figma-use-figjam/SKILL.md.
- Re-load whichever block refs this section uses:
blocks/section.md always.
blocks/text-primitives.md if body/H3/list.
blocks/table.md if table.
blocks/multi-column-text.md if multi-column.
blocks/nested-section.md if nested subsections.
blocks/intro-callout.md if highlighted intro.
blocks/sticky-column.md if stickies.
Do, per section:
await figma.getNodeByIdAsync(sectionId) — confirm type is SECTION.
- Build content: H2 header first, then children per approved block shape. Append to section FIRST, then set x/y.
- Two-pass measure for stickies per
blocks/sticky-column.md.
section.resizeWithoutConstraints(LEFT_COL_W, computed_height).
section.placeholder = false.
await section.screenshot() inline.
return { mutatedNodeIds: [...], sectionHeight, screenshotIncluded: true }.
Checkpoint (screenshot + AskUserQuestion)
- Per section: AskUserQuestion: "Section
<name> done? [Yes / Edit this section / Skip rest]." Edit = targeted fix; Skip rest = exit fill loop (user will finalize manually).
After all left-column fills: run the re-stack pass (single use_figma) to fix cumulative Y based on actual resized heights:
let y = SECTION_TOP_Y;
for (const id of leftColumnSectionIdsInOrder) {
const sec = await figma.getNodeByIdAsync(id);
sec.y = y;
sec.x = 0;
y += sec.height + 32;
}
return { mutatedNodeIds: leftColumnSectionIdsInOrder };
Then run the outer-wrapper pass (single use_figma) — wrap all left-column sections in an unlabeled white outer SECTION (see "Outer column wrapper" in the visual conventions block above). This is STRICT; do not skip.
Step 8 — Diagrams [Write]
Inputs required
- Diagram intents from Step 2/4 (Current State, Target State, 0–N Key Flows); tech-context for Mermaid composition.
Tools / refs loaded
- Re-load
generate-diagram/SKILL.md before each generate_diagram call.
- Re-load
figma-use-figjam/SKILL.md + blocks/diagram-section.md before each reparent use_figma call.
generate_diagram MCP tool, use_figma.
Do, per diagram:
use_figma: create the right-column section (white fill, H2 header, placeholder = true) per blocks/diagram-section.md.
generate_diagram: compose Mermaid from tech-context; architecture diagrams use useArchitectureLayoutCode: "FIGMA_DIAGRAM_2026".
use_figma: locate the generated diagram node, reparent into the section (section.appendChild(diag)), position below H2, resize section to fit. placeholder = false.
await section.screenshot().
Failure handling: if generate_diagram fails, leave a text placeholder in the section reading "Diagram generation failed: <message>. Regenerate manually." Continue to the next diagram.
Checkpoint (screenshot + AskUserQuestion)
- Per diagram: AskUserQuestion: "Diagram
<name> looks right? [Yes / Regenerate / Skip]."
Step 9 — Final review + report [Write, then Read]
Inputs required
Tools / refs loaded
use_figma (one call for full-page screenshot).
Do
await figma.currentPage.screenshot({ scale: 0.25 }) — full board.
- Inspect: two-column structure, no overlaps, no sections left with
placeholder = true, metadata strip visible.
- Fix any issues with a targeted
use_figma script. Do not regenerate from scratch.
- Post to chat:
✅ Project plan written to FigJam.
File: <file_url>
Sections: <N text + N diagram>
Files referenced during grounding: <count>
Checkpoint (screenshot + AskUserQuestion)
- AskUserQuestion: "Done, or tweak? [Done / Tweak]." Tweak = targeted fix script on user's request, not regeneration.
Operational rules
- ≤10 logical operations per
use_figma call.
- Always return
createdNodeIds / mutatedNodeIds from every write script.
- Use
hex/255 notation for all palette colors (see foundation/palette.md).
- STRICT: section backgrounds use the
ARCH_PALE palette, NOT the FigJam standard SECTION palette. ARCH_PALE colors (#EBFFEE, #F8F5FF, #F5FBFF, #FFF7F0, etc.) visually pair with the architecture-diagram subgraph wrappers that generate_diagram produces. The FigJam SECTION palette (#CDF4D3, #C2E5FF, #DCCCFF, #FFE0C2) is too saturated and causes visible color clash next to diagrams. See foundation/palette.md.
- STRICT:
section.name = "" on every project-plan section (left-column, right-column, and nested children). The user-facing title is rendered as the H2 text node inside the section, NOT via FigJam's section title-bar label. The reference board uses empty section names; setting a non-empty name produces a duplicate label that visually clutters the board.
- Read, edit, or cancel at every Confirm/Write checkpoint — never write past an unanswered AskUserQuestion.
- If a
use_figma script errors: atomic — no changes made. Read the error, fix, retry.
Trigger phrases
"/generate-project-plan", "interactive project plan", "project plan", "make a FigJam project plan", "PRD to FigJam".
1---2name: generate-project-plan3description: Generate a FigJam project plan board from a PRD plus codebase context. Interactive flow: research → propose sections → per-section deep research → per-section content + block-shape proposal → create FigJam → skeleton → fill → diagrams → wrap. Each content block (section, nested section, intro callout, table, multi-column text, sticky column, diagram section, metadata strip) has its own subskill reference file. Use when the user asks for 'project plan in FigJam', 'interactive project plan', '/generate-project-plan', or provides a PRD and wants per-section confirmation on content + rendering.4---5
6# generate-project-plan
7
8Turn a PRD (plus optional codebase grounding) into a FigJam project plan board. Section set is not fixed — the skill proposes candidates from the research and the user picks which to include. For each picked section, the skill proposes content + rendering shape (block) and the user confirms.
9
10## Mandatory prerequisites
11
12**Foundation skills** (load by name; available in `figma/mcp-server-guide`):
13
14- `figma-use` — **Load once per session.** Stays in context for all `use_figma` calls.
15- `figma-use-figjam` — **Re-load before every `use_figma` call.**
16- `figma-generate-diagram` — **Re-load before every `generate_diagram` call.**
17
18**Foundation references** (in this plugin):
19
20- [`foundation/palette.md`](references/foundation/palette.md) — section + sticky + text palette constants (`hex/255`).
21- [`foundation/layout.md`](references/foundation/layout.md) — canvas geometry, sizing rules, placeholder lifecycle.
22- [`foundation/plugin-api-traps.md`](references/foundation/plugin-api-traps.md) — documented traps for FigJam `use_figma`.
23- [`foundation/codebase-grounding.md`](references/foundation/codebase-grounding.md) — Step 1 expansion rules.
24
25**Section catalog**:
26
27- [`section-catalog.md`](references/section-catalog.md) — the ~10 candidate sections with default blocks and palette.
28
29**Block subskills** (one per content type — re-load the one(s) you need before each `use_figma` fill call):
30
31| Block | File | When to load |
32|---|---|---|
33| Top-level section | [`blocks/section.md`](references/blocks/section.md) | Skeleton pass (Step 6) and every fill call (Step 7) |
34| Nested section | [`blocks/nested-section.md`](references/blocks/nested-section.md) | Fills that group sub-content (e.g. "Design Decisions 1/2/3") |
35| Intro callout | [`blocks/intro-callout.md`](references/blocks/intro-callout.md) | Fills that open with a highlighted intro (e.g. Motivation) |
36| Text primitives | [`blocks/text-primitives.md`](references/blocks/text-primitives.md) | Any fill that uses body paragraphs, H3 subheaders, or bulleted lists |
37| Table | [`blocks/table.md`](references/blocks/table.md) | Fills with structured data (Resources, Goals, Dependencies, Rollout, Milestones) |
38| Multi-column text | [`blocks/multi-column-text.md`](references/blocks/multi-column-text.md) | Fills with 2–4 option columns (Design Decisions alternatives) |
39| Sticky column | [`blocks/sticky-column.md`](references/blocks/sticky-column.md) | Fills with lists of stickies (Success Metrics, Risks, Open Questions) |
40| Diagram section | [`blocks/diagram-section.md`](references/blocks/diagram-section.md) | Right-column diagram sections (Step 8) |
41| Metadata strip | [`blocks/metadata-strip.md`](references/blocks/metadata-strip.md) | Skeleton pass — one metadata strip at top of board |
42
43**Also pass** `skillNames: "figma-use,figma-use-figjam,generate-project-plan"` on `use_figma` calls (logging only).
44
45## Visual UI conventions — STRICT, do not deviate
46
47These are derived from a canonical reference board. Read the source-of-truth files for the full constants; this section is a single-place summary so an agent can answer "what color / size / font / padding?" without hunting.
48
49### Colors (two-tone per section)
50
51Every left-column section uses **two coordinated colors** of the same hue: a very-pale `ARCH_PALE` background, and a slightly-more-saturated FigJam-`SECTION` palette color for any table header inside that section. Right-column diagram sections are pure white.
52
53| Section bg (`ARCH_PALE.X`) | Table header (`TABLE_HEADER.lightX`) | Hue |
54|---|---|---|
55| `#F8F5FF` | `#DCCCFF` | violet |
56| `#EBFFEE` | `#CDF4D3` | green |
57| `#DBF0FF` | `#C2E5FF` | blue |
58| `#F5FBFF` (alt) | `#C2E5FF` | pale blue |
59| `#FFF7F0` | `#FFE0C2` | orange |
60| `#F1FEFD` | `#C6FAF6` | teal |
61| `#FFFBF0` | `#FFEC BD` | yellow |
62| `#FFEEF8` | `#FFC2EC` | pink |
63| `#FFEEE8` | `#FFCDC2` | red |
64
65Source: `references/foundation/palette.md`. Never use the dark-saturated palette (`#874FFF`, `#3DADFF`, etc.) for table headers — that's for FigJam's standalone tables, not project-plan boards.
66
67Architecture-diagram subgraph colors are auto-applied by `generate_diagram` and **must not be overridden**. Their canonical values: `client #AFF4C6` rounded-rect, `gateway #FFFFFF` square (diamond if labeled "Load Balancer"/"ALB"/"LB"), `service #E4CCFF` square, `datastore #BDE3FF` cylinder, `external #FFFFFF` PREDEFINED_PROCESS, `async #BDE3FF` ENG_QUEUE.
68
69### Typography (font sizes)
70
71| Element | Size | Font | Color |
72|---|---|---|---|
73| H1 (board title) | **40** | Inter Medium | `#1E1E1E` |
74| H2 (section title — first child of every section) | 40 | Inter Medium | `#1E1E1E` |
75| H3 — full-width subhead (e.g. "Resources" inside Motivation) | **40** | Inter Medium | `#1E1E1E` |
76| H3 — nested-section header (e.g. "Design Decision 1: …") | **32** | Inter Medium | `#1E1E1E` |
77| H3 — column title in 2/3/4-col layouts (Risks col, Goals col) | **24** | Inter Medium | `#1E1E1E` |
78| Body text | 16 | Inter Medium | `#1E1E1E` |
79| Table cells (header AND body) | 16 | **Inter Bold** | `#1E1E1E` |
80
81The three different H3 sizes are deliberate. 40 = matches H2 weight when subhead is alone in the section. 32 = sub-section header inside a child section (672px inner width). 24 = column title in narrow contexts (≤ 224px col width). Pick by **container width**, not by semantic depth.
82
83Always load both `Inter Medium` AND `Inter Bold` at the top of any `use_figma` script that creates tables (Bold) plus any other text (Medium).
84
85### Section properties
86
87| Property | Value |
88|---|---|
89| `section.fills` | `[{ type: 'SOLID', color: ARCH_PALE.X }]` (left column) or `ARCH_PALE.white` (right column / diagrams) |
90| `section.name` | `""` — empty string. NO FigJam title-bar label. The H2 inside is the only title. |
91| Inner padding (all 4 sides) | 32 (current default; reference uses 40-50, kept at 32 for now) |
92| First child position | `(32, 32)` |
93| Width (left column) | 800 |
94| Width (right column / diagram) | `max(1200, diag.width + 64)` after diagram is reparented |
95| Vertical gap between sections (inside the wrapper) | **64** |
96| Hug behavior | Manual: call `section.resizeWithoutConstraints(w, maxChildBottom + 32)` after appending children. Sections do NOT auto-grow. |
97| Placeholder during build | `placeholder = true` in skeleton pass; `placeholder = false` at the end of the section's fill. |
98
99### Outer wrapper + column alignment (STRICT defaults)
100
101The board has **one outer wrapper** (unlabeled, white) plus the diagram column:
102
1031. **Column wrapper** — an unlabeled white SECTION at `(_, 0)`. Contains, in order from top:
104 - H1 project title (40px Inter Medium, charcoal) at `(64, 64)` (= section padding)
105 - Body row of metadata: Owner / Status / Last updated / Source (16px Inter Medium, charcoal), at `(64, h1.y + h1.height + 16)` — 16px gap below H1; 32px gap between each body cell
106 - 64px gap below the body row, then the 6 left-column sections stacked with 64px gutter
1072. **Diagram column** at `(columnWrapperRight + 64, 0)`. Each diagram is its own un-wrapped white SECTION; the **top diagram's y aligns with the column wrapper's y (= 0)**. Diagrams stack with 64px gutter.
108
109The metadata is **embedded** in the column wrapper (NOT a separate section). One column wrapper holds everything text-related.
110
111**Constants:**
112- Wrapper inner padding (all 4 sides): **64**
113- Vertical gap between sections inside the wrapper: **64**
114- Horizontal gap between wrapper right edge and diagram-column left edge: **64**
115- Top of the diagram column aligns with `wrapper.y` (same horizontal axis as the wrapper top edge)
116- Vertical gap between stacked diagram sections: **64** (matches the inner gutter)
117
118```js
119const PAD = 64;
120const leftIds = [/* all left-column section ids in order */];
121const sections = [];
122for (const id of leftIds) sections.push(await figma.getNodeByIdAsync(id));
123
124// Compute bbox of the column in page coords
125let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
126for (const s of sections) {
127 minX = Math.min(minX, s.x); minY = Math.min(minY, s.y);
128 maxX = Math.max(maxX, s.x + s.width); maxY = Math.max(maxY, s.y + s.height);
129}
130
131const wrapper = figma.createSection();
132wrapper.name = ""; // STRICT
133wrapper.fills = [{ type: 'SOLID', color: WHITE }]; // ARCH_PALE.white
134wrapper.resizeWithoutConstraints((maxX - minX) + 2*PAD, (maxY - minY) + 2*PAD);
135wrapper.x = minX - PAD;
136wrapper.y = minY - PAD;
137
138// Reparent + translate to keep visual positions
139for (const s of sections) {
140 const newX = (s.x - minX) + PAD;
141 const newY = (s.y - minY) + PAD;
142 wrapper.appendChild(s);
143 s.x = newX;
144 s.y = newY;
145}
146```
147
148The wrapper has no header (no H2 inside) and no name — it's a pure container. Don't repeat the project title here; that lives in the metadata strip.
149
150**Position the diagram column** after the wrapper exists:
151
152```js
153const wrapperRight = wrapper.x + wrapper.width;
154const diagramX = wrapperRight + 64; // 64px horizontal gap matches inter-section gutter
155let y = wrapper.y; // align top with wrapper's top edge
156for (const id of diagramSectionIdsInOrder) {
157 const d = await figma.getNodeByIdAsync(id);
158 d.x = diagramX;
159 d.y = y;
160 y += d.height + 64; // 64px vertical gutter between stacked diagrams
161}
162```
163
164### Vertical spacing (STRICT)
165
166| Between | Gap |
167|---|---|
168| Section top edge → H2 (first child) | 32 (= padding) |
169| H2 → next child (body / intro callout / H3 / table / first column) | **24** |
170| Body paragraph → H3 | **24** |
171| H3 (any size: 40 / 32 / 24) → next child (body / table / list / column content) | **24** |
172| Body → body | **24** |
173| List → next block | **24** |
174| Last child bottom → section bottom edge | 32 (= padding) |
175
176**Always position children using `prevChild.y + prevChild.height + 24`** — never use a fixed offset like `prevChild.y + 60`. The H3 has three different sizes (40 / 32 / 24), so a fixed offset is wrong by definition; always read `prevChild.height` after the font size is set.
177
178**When you change a header's font size after the fact, you MUST re-stack downstream children.** Setting `h3.fontSize = 40` grows the node's height; if the next child's `y` was computed before the resize, it will overlap.
179
180### Tables
181
182- **Both header AND body cells** use `Inter Bold` 16px (NOT Medium).
183- Header text is `#1E1E1E` charcoal on a light fill (matching the section's hue).
184- Body cells leave fill at default white; text is charcoal.
185- Headers do NOT use white-on-dark — that's wrong for this board style.
186
187### Diagrams (right column)
188
189`generate_diagram` with `useArchitectureLayoutCode: "FIGMA_DIAGRAM_2026"` produces **multiple page-level nodes** (1–2 subgraph SECTIONs + bare SHAPE_WITH_TEXTs + CONNECTORs), NOT a single container. To wrap them in a section:
1901. Collect all new page-level nodes (exclude known plan-section IDs).
1912. Compute the bbox.
1923. Create a new SECTION sized to `bboxW + 64 × bboxH + 64 + 64` (HEADER_BLOCK = 40 H2 + 24 gap), fill `ARCH_PALE.white`.
1934. Reparent each diagram node, translating local coords to maintain visual layout.
1945. **CRITICAL — delete and recreate every connector after reparent.** The assign-to-self trick (`c.connectorStart = c.connectorStart`) is NOT reliable: short connectors re-route fine, but long-bend connectors retain stale elbow waypoints and extend hundreds of pixels outside the section. Delete + `figma.createConnector()` from captured spec produces a clean route every time. See [`blocks/diagram-section.md`](references/blocks/diagram-section.md) for the spec-capture + recreate pattern.
1956. **Connector labels — explicit `fontName` + `fontSize` + `fills` ALL required.** A fresh connector's `text` sublayer has no usable defaults. Set ALL FOUR: `c.text.fontName = { family: 'Inter', style: 'Medium' }`, `c.text.fontSize = 14`, `c.text.characters = label`, `c.text.fills = [{ type: 'SOLID', color: CHARCOAL }]`. Default `text.fills` is **`[]` (empty array)** so the label renders **transparent** and is invisible — read-back of `c.text.characters` lies; verify with a screenshot.
196
197Diagram-section convention: `section.name = ""`, H2 text node inside as the title (matches the rest of the board).
198
199### What NOT to use (common wrong defaults)
200
201| Don't use | Use instead | Why |
202|---|---|---|
203| `#CDF4D3` (FigJam lightGreen) for section bg | `#EBFFEE` (ARCH_PALE.green) | Too saturated next to diagrams |
204| `#C2E5FF` (FigJam lightBlue) for section bg | `#DBF0FF` or `#F5FBFF` (ARCH_PALE.blue / blueLite) | Same |
205| `#874FFF` (FigJam dark violet) for table header | `#DCCCFF` (FigJam lightViolet) | Reference uses light-on-pale, not dark-on-pale |
206| White text on dark table header | `#1E1E1E` charcoal text on light header | Same — pale-on-pale convention |
207| `Inter Medium` for table cells | `Inter Bold` | Reference uses Bold for all cells |
208| `section.name = "Goals"` (or any non-empty string) | `section.name = ""` | Reference uses empty names; H2 inside is the title |
209| Reparenting only one subgraph from a generated diagram | Reparent ALL page-level nodes (SECTIONs + SHAPEs + CONNECTORs) | Architecture diagrams are not single nodes |
210| Trusting `c.connectorStart = c.connectorStart` to re-route every connector | After reparent, **delete and recreate every connector** from a captured spec | Long-bend connectors retain stale elbow waypoints; only `figma.createConnector()` produces a fresh route |
211| Setting only `c.text.characters = label` on a fresh connector | Set `c.text.fontName`, `c.text.fontSize`, `c.text.characters`, AND `c.text.fills` | Defaults: fontSize=missing, fills=`[]` (empty array → transparent) → label invisible despite correct read-back |
212
213## Step template
214
215Every step below uses this shape. Read the step, then execute.
216
217```
218## Step N — <Name> [Type: Research | Confirm | Write]
219Inputs required: …
220Ask if missing: …
221Tools / refs loaded: …
222Do: (3–6 action bullets)
223Checkpoint: (Research → self-check; Confirm → AskUserQuestion; Write → screenshot + AskUserQuestion)
224```
225
226Three step types:
227- **Research** — read-only; checkpoint = self-check list.
228- **Confirm** — no board writes, user decision gate; checkpoint = `AskUserQuestion`.
229- **Write** — creates/mutates FigJam; checkpoint = screenshot + `AskUserQuestion`.
230
231---
232
233## Step 1 — Gather context [Research]
234
235**Inputs required**
236- PRD file path or pasted text.
237- Optional: codebase entry points (file paths, service names, doc paths).
238
239**Ask if missing**
240- "Where's the PRD? (path or paste)."
241- "Any codebase entry points I should ground in? (paths / services / docs / 'none')."
242
243**Tools / refs loaded**
244- `Read`, `Glob`, `Grep`.
245- [`foundation/codebase-grounding.md`](references/foundation/codebase-grounding.md).
246
247**Do**
2481. Read the PRD. Extract: title, problem, goals, non-goals, owner, audience, success metrics, rollout hints, risks.
2492. If entry points provided: follow `codebase-grounding.md` — bounded 20-file cap, depth-1 imports, walk up to `CLAUDE.md`/`ARCHITECTURE.md`/`OWNERS`.
2503. Produce the **tech-context object**: `files_read`, `services`, `external_deps`, `key_modules`, `architecture_notes`, `ownership`, `expansion_truncated`.
251
252**Self-check**
253- Have: project title, problem statement, at least 1 concrete goal, owner (or "TBD"), services touched (or empty list with a reason).
254- Enough signal to draft candidate section cards in Step 2. If not, loop back and ask.
255
256---
257
258## Step 2 — Propose candidate sections [Confirm]
259
260**Inputs required**
261- Tech-context object from Step 1.
262
263**Tools / refs loaded**
264- [`section-catalog.md`](references/section-catalog.md).
265- `AskUserQuestion`.
266
267**Do**
2681. For each section in the catalog, decide if there is *real content* for it from Step 1. Skip catalog entries that would be empty or padding.
2692. For each qualifying candidate, produce a card:
270 - **Title** (catalog name)
271 - **1-line description** (what this section will contain, specific to the PRD)
272 - **Why suggested** (which PRD facts or tech-context items justify it)
273 - **Default block shape** (from the catalog)
2743. Print all cards to chat.
2754. Fire `AskUserQuestion` with a multiSelect question **per batch of ≤4 candidates** (max 4 questions per call, 4 options each → up to 16 candidates per call). Each option's label is the section title; description is the 1-line summary.
276
277**Checkpoint (AskUserQuestion)**
278- The multiSelect questions above. User ticks the sections they want. Store the selected set as `approved_sections`.
279- If zero sections selected → stop with a clean exit message. No file is created.
280
281---
282
283## Step 3 — Per-section deep research [Research]
284
285**Inputs required**
286- `approved_sections` from Step 2; tech-context from Step 1.
287
288**Tools / refs loaded**
289- `Read`, `Grep`, `Glob` (optional).
290
291**Do**
2921. For each section in `approved_sections`, look up its catalog entry. The catalog declares what the section needs (e.g. Dependencies needs cross-team services + external deps + blockers).
2932. Compare what the section needs vs. what the tech-context has.
2943. Produce a **gap list per section**: specific facts the user must supply, framed as answerable questions (no "figure it out yourself" gaps).
295
296**Self-check**
297- Every `approved_sections` entry has either `ready` (no gaps) or a specific non-empty gap list.
298- Gaps are answerable — not vague prompts like "tell me more about X."
299
300---
301
302## Step 4 — Per-section content + block proposal [Confirm]
303
304**Inputs required**
305- Gap lists from Step 3.
306
307**Tools / refs loaded**
308- `AskUserQuestion`.
309- [`section-catalog.md`](references/section-catalog.md).
310- Block reference(s) for the section's default shape (e.g. `blocks/table.md` for Dependencies).
311
312**Do**, per section (one at a time, or small batch if trivial):
3131. Fill the gap list — free-text prompt for prose; `AskUserQuestion` for bounded choices.
3142. Propose:
315 - **Content**: the concrete bullets / rows / stickies that will appear.
316 - **Block shape**: the rendering block (body paragraph / table / multi-column / sticky column / …). Default from `section-catalog.md`; offer alternative shapes where sensible (e.g. "As a table, or as a multi-column layout?").
3173. Show a short preview — section title + first line of body + block type summary.
3184. Fire `AskUserQuestion`: "Use this content + shape? [Yes / Edit / Skip this section]."
3195. Edit → accept free-text amendments, re-show, re-ask. Skip → mark the section as `skipped`; do not write it to the board.
320
321**Checkpoint**
322- Every section is `approved`, `edited+approved`, or `skipped`. No board writes yet.
323
324---
325
326## Step 5 — Create FigJam file [Write]
327
328**Inputs required**
329- `approved_sections` (non-skipped); project title; `planKey` (Figma team plan).
330
331**Tools / refs loaded**
332- `create_new_file` MCP tool.
333- `whoami` MCP tool (for `planKey` if not known).
334- `use_figma` (once).
335- `figma-use` (already in context from Step 5 onward).
336- `figma-use-figjam` (re-loaded for the probe).
337- `AskUserQuestion`.
338
339**Do**
3401. Resolve `planKey`: call `whoami`. If one plan → use it. If multiple → `AskUserQuestion` which team.
3412. Call `create_new_file` with `{ planKey, fileName: "<project title>", editorType: "figjam" }`. Capture `file_key` + `file_url`.
3423. Run the first-run probe (`use_figma`):
343
344```js
345const page = figma.currentPage;
346return {
347 rootName: figma.root.name,
348 editorType: figma.editorType,
349 pageCount: figma.root.children.length,
350 firstPageName: figma.root.children[0].name,
351 currentPageChildrenCount: page.children.length,
352};
353```
354
355Expect `editorType === "figjam"` and empty page. If not, halt and report.
356
357**Checkpoint (probe output + AskUserQuestion)**
358- Print probe return + `file_url`.
359- AskUserQuestion: "File created at `<file_url>` — proceed to skeleton? [Yes / Cancel]." Cancel = stop, leave empty file.
360
361---
362
363## Step 6 — Skeleton pass [Write]
364
365**Inputs required**
366- `approved_sections` in taxonomy order; palette from `foundation/palette.md`; layout constants from `foundation/layout.md`.
367
368**Tools / refs loaded**
369- `use_figma` (one call).
370- Re-load `figma-use-figjam/SKILL.md`.
371- Re-load `blocks/section.md` and `blocks/metadata-strip.md`.
372- [`foundation/palette.md`](references/foundation/palette.md), [`foundation/layout.md`](references/foundation/layout.md).
373
374**Do**
3751. Create metadata strip per `blocks/metadata-strip.md` (H1 + 4 body texts at board `(0, 0)`–`(0, ~100)`).
3762. For each approved section, create a top-level SECTION per `blocks/section.md` — colored bg from ARCH_PALE, `section.name = ""` (no title-bar label — STRICT), `placeholder = true`, `resizeWithoutConstraints(LEFT_COL_W, DEFAULT_H)` for left-column sections or `resizeWithoutConstraints(RIGHT_COL_W_MIN, DEFAULT_H)` for right-column diagram sections.
3773. Position each left-column section at `(0, SECTION_TOP_Y + cumulative_y)`. Right-column sections at `(832, SECTION_TOP_Y + cumulative_y_right)`.
3784. Return all created node IDs: `{ createdNodeIds: { metadataStrip: {...}, sections: { <slug>: "<id>", ... } }, status: "skeleton-complete" }`. (The `slug` is internal, used to look up palette + drive Step 7 fills. It is never written to `section.name`.)
3795. Take an inline screenshot at `await figma.currentPage.screenshot({ scale: 0.3 })`.
380
381**Checkpoint (screenshot + AskUserQuestion)**
382- AskUserQuestion: "Skeleton looks right? [Yes / Fix / Cancel]." Fix = targeted fix script; Cancel = stop.
383
384---
385
386## Step 7 — Fill pass (one call per section) [Write]
387
388**Inputs required**
389- Approved content + block shape for each section; section ID from Step 6.
390
391**Tools / refs loaded**
392- `use_figma` (one call per section).
393- Re-load `figma-use-figjam/SKILL.md`.
394- Re-load whichever block refs this section uses:
395 - `blocks/section.md` always.
396 - `blocks/text-primitives.md` if body/H3/list.
397 - `blocks/table.md` if table.
398 - `blocks/multi-column-text.md` if multi-column.
399 - `blocks/nested-section.md` if nested subsections.
400 - `blocks/intro-callout.md` if highlighted intro.
401 - `blocks/sticky-column.md` if stickies.
402
403**Do**, per section:
4041. `await figma.getNodeByIdAsync(sectionId)` — confirm type is SECTION.
4052. Build content: H2 header first, then children per approved block shape. Append to section FIRST, then set x/y.
4063. Two-pass measure for stickies per `blocks/sticky-column.md`.
4074. `section.resizeWithoutConstraints(LEFT_COL_W, computed_height)`.
4085. `section.placeholder = false`.
4096. `await section.screenshot()` inline.
4107. `return { mutatedNodeIds: [...], sectionHeight, screenshotIncluded: true }`.
411
412**Checkpoint (screenshot + AskUserQuestion)**
413- Per section: AskUserQuestion: "Section `<name>` done? [Yes / Edit this section / Skip rest]." Edit = targeted fix; Skip rest = exit fill loop (user will finalize manually).
414
415**After all left-column fills**: run the re-stack pass (single `use_figma`) to fix cumulative Y based on actual resized heights:
416
417```js
418let y = SECTION_TOP_Y;
419for (const id of leftColumnSectionIdsInOrder) {
420 const sec = await figma.getNodeByIdAsync(id);
421 sec.y = y;
422 sec.x = 0;
423 y += sec.height + 32;
424}
425return { mutatedNodeIds: leftColumnSectionIdsInOrder };
426```
427
428**Then run the outer-wrapper pass** (single `use_figma`) — wrap all left-column sections in an unlabeled white outer SECTION (see "Outer column wrapper" in the visual conventions block above). This is STRICT; do not skip.
429
430---
431
432## Step 8 — Diagrams [Write]
433
434**Inputs required**
435- Diagram intents from Step 2/4 (Current State, Target State, 0–N Key Flows); tech-context for Mermaid composition.
436
437**Tools / refs loaded**
438- Re-load `generate-diagram/SKILL.md` before each `generate_diagram` call.
439- Re-load `figma-use-figjam/SKILL.md` + `blocks/diagram-section.md` before each reparent `use_figma` call.
440- `generate_diagram` MCP tool, `use_figma`.
441
442**Do**, per diagram:
4431. `use_figma`: create the right-column section (white fill, H2 header, `placeholder = true`) per `blocks/diagram-section.md`.
4442. `generate_diagram`: compose Mermaid from tech-context; architecture diagrams use `useArchitectureLayoutCode: "FIGMA_DIAGRAM_2026"`.
4453. `use_figma`: locate the generated diagram node, reparent into the section (`section.appendChild(diag)`), position below H2, resize section to fit. `placeholder = false`.
4464. `await section.screenshot()`.
447
448**Failure handling**: if `generate_diagram` fails, leave a text placeholder in the section reading `"Diagram generation failed: <message>. Regenerate manually."` Continue to the next diagram.
449
450**Checkpoint (screenshot + AskUserQuestion)**
451- Per diagram: AskUserQuestion: "Diagram `<name>` looks right? [Yes / Regenerate / Skip]."
452
453---
454
455## Step 9 — Final review + report [Write, then Read]
456
457**Inputs required**
458- `file_url` from Step 5.
459
460**Tools / refs loaded**
461- `use_figma` (one call for full-page screenshot).
462
463**Do**
4641. `await figma.currentPage.screenshot({ scale: 0.25 })` — full board.
4652. Inspect: two-column structure, no overlaps, no sections left with `placeholder = true`, metadata strip visible.
4663. Fix any issues with a targeted `use_figma` script. Do **not** regenerate from scratch.
4674. Post to chat:
468
469```
470✅ Project plan written to FigJam.
471File: <file_url>
472Sections: <N text + N diagram>
473Files referenced during grounding: <count>
474```
475
476**Checkpoint (screenshot + AskUserQuestion)**
477- AskUserQuestion: "Done, or tweak? [Done / Tweak]." Tweak = targeted fix script on user's request, not regeneration.
478
479---
480
481## Operational rules
482
483- ≤10 logical operations per `use_figma` call.
484- Always return `createdNodeIds` / `mutatedNodeIds` from every write script.
485- Use `hex/255` notation for all palette colors (see `foundation/palette.md`).
486- **STRICT: section backgrounds use the `ARCH_PALE` palette, NOT the FigJam standard SECTION palette.** ARCH_PALE colors (`#EBFFEE`, `#F8F5FF`, `#F5FBFF`, `#FFF7F0`, etc.) visually pair with the architecture-diagram subgraph wrappers that `generate_diagram` produces. The FigJam SECTION palette (`#CDF4D3`, `#C2E5FF`, `#DCCCFF`, `#FFE0C2`) is too saturated and causes visible color clash next to diagrams. See `foundation/palette.md`.
487- **STRICT: `section.name = ""` on every project-plan section (left-column, right-column, and nested children).** The user-facing title is rendered as the H2 text node *inside* the section, NOT via FigJam's section title-bar label. The reference board uses empty section names; setting a non-empty `name` produces a duplicate label that visually clutters the board.
488- Read, edit, or cancel at every Confirm/Write checkpoint — never write past an unanswered AskUserQuestion.
489- If a `use_figma` script errors: atomic — no changes made. Read the error, fix, retry.
490
491## Trigger phrases
492
493"/generate-project-plan", "interactive project plan", "project plan", "make a FigJam project plan", "PRD to FigJam".