Draft or edit Product Requirements Documents. Covers document structure, user flows, flowchart generation, scope discipline, and the full competitor-analysis-to-tracking-spec workflow.
Use this when drafting or editing a Product Requirements Document.
.claude/skills/design-api/SKILL.md for format and principlesAlways a table, never prose:
| Role | Action | Outcome |
|---|---|---|
| Trader | Place a BBO order on the perpetual market | Order pegs to best bid/ask automatically |
Include one flow per primary user journey. Only cover journeys that involve meaningful decision points or multi-step state changes — skip trivial read-only flows.
Each flow must show:
Format each step as a two-column row: step number (highlighted) | step title + sub-details. Decision points use a distinct diamond/decision style with labelled branches.
Standard flows to include for any feature with a creation + consumption pattern:
Add additional flows for non-obvious lifecycle events (e.g. resolution, expiry, error recovery) if they involve user-visible steps.
Decisions go in the relevant body section, not in Open Questions. If a decision was made during discussion, weave it into Functional Requirements, API Spec, or Error Handling. Open Questions is strictly for items pending engineering or stakeholder input.
.claude/skills/crypto-exchange-competitor-analysis/SKILL.md first, deliver report.docx file.claude/skills/write-posthog-events/SKILL.md to define analytics events for the featureUser flows must be delivered as PNG image files (not table-based layouts in the docx).
from PIL import Image, ImageDraw, ImageFont)flow-<slug>.png, e.g. flow-create-conviction.png, flow-place-order.png.docx in the same /docs/ folder.docx at §5 (User Flows section)SCALE = 3 for hi-dpi output; all coordinates go through def S(x): return int(round(x * SCALE))| Node type | Use when | Fill | Border | Text |
|---|---|---|---|---|
PAL_START pill | First/last node | #EDE9FF | #9B8EC4 | #5C3D9E |
PAL_STEP rounded-rect | User action / regular step | #F4F4F6 | #C4C4CC | #333344 |
PAL_SYSTEM rounded-rect | System acts without user input | #EAF4FF | #7AB0DC | #1A5C8A |
PAL_DECISION diamond | Branch point | #FEF3CD | #F0B429 | #7A5200 |
PAL_SUCCESS rounded-rect | Terminal success state | #E6F9EE | #6CC08B | #1B5E35 |
PAL_ERROR rounded-rect | Error / rejection box | #FFE8E8 | #E07070 | #8B1A1A |
Branch labels: Yes/Pass in #2E7D32, No/Fail in #C62828.
Never use a fixed width for all nodes. Each node's width must be computed from its text content:
def node_size(n):
pad_x = S(16) # padding each side
# Measure title (FONT_BODY) and all sub-items (FONT_SMALL)
max_text_w = max(text_width(title), text_width(sub1), text_width(sub2), ...)
w = max_text_w + pad_x * 2
# Decision diamonds need ~1.7× text width (text sits inside rotated shape)
if diamond: w = int(text_width * 1.7) + pad_x
return max(w, S(60)) # minimum width
Boxes are always centered on CX regardless of individual width. This gives a clean, content-hugging look — short labels get narrow boxes, long sub-items get wider boxes.
All lines must be thin — use absolute pixel widths, never scaled values:
width = 2 (absolute pixels, NOT S(2) which would be 6px at SCALE=3)width = 2 (absolute pixels, NOT S(1))width = 2 (absolute pixels)This produces clean, Whimsical-style diagrams. Thick lines make the diagram look chunky and unprofessional.
#AAAAAA) vertical arrows, width = 2PAL_ERROR border colour, width = 2PAL_ERROR border colour, width = 2AH = S(2.5) # half-size — keep proportional to thin lines (NOT S(4))
def arrowhead(draw, x, y, direction, color):
if direction == "down": pts = [(x,y),(x-AH,y-AH*2),(x+AH,y-AH*2)]
elif direction == "right": pts = [(x,y),(x-AH*2,y-AH),(x-AH*2,y+AH)] # tip at x, base left
elif direction == "left": pts = [(x,y),(x+AH*2,y-AH),(x+AH*2,y+AH)] # tip at x, base right
elif direction == "up": pts = [(x,y),(x-AH,y+AH*2),(x+AH,y+AH*2)]
draw.polygon(pts, fill=color)
Use "right" for the loop-back arrowhead (entering from the left side of a node).
Line terminates at arrowhead_base = loop_x_target - AH*2 so the line never enters the node.
The loop-back path from the error box to the retry target must never cross or overlap any node. Route as four segments:
① DOWN error-box bottom-centre → y_route_bottom (= E_TOP + EH + S(14), guaranteed below error box)
② LEFT y_route_bottom → pipe_x (= target_left - S(24), left of main flow)
③ UP pipe_x → loop_y_target (mid-height of target node)
④ RIGHT pipe_x → loop_x_target - AH*2 (stop at arrowhead base, outside node)
Then draw arrowhead with direction="right", tip at loop_x_target (left edge of target node).
This means the arrowhead points rightward into the node and the dashed line never enters the box.
Short punchy opening, emoji-accented bullets, benefit-focused, approachable non-technical tone. Target: end users, not engineers.