A complete, buildable checklist/runbook canvas, verified against the real canvas builder.
Start from it whenever the request is a checklist, QA runbook, launch plan, onboarding sequence, or any list of steps people work through and tick off — keep its structure and replace the content.
The parts that break when improvised, in the order they matter:
Content lives in its own data module (src/plan.ts), typed, separate from the component. Editing the steps later is a data edit, not a component rewrite — and a follow-up request like “add a step” touches one file.
One shared ph.state key per step (step:<id>), holding { done, note }. Progress is team-visible and survives reloads; per-key writes mean two people ticking different steps don’t clobber each other the way one big list value would. An entry that returns to blank is deleted with a null write, so state never accumulates empty rows. Declare state: ["shared"] in capabilities.posthog.
A ref alongside React state for updates. Note saves are debounced per step; checkbox saves are immediate. Both go through one update() that reads the latest entry from a ref, so a keystroke and a checkbox click in the same debounce window can’t overwrite each other with stale values.
Loading and failure are visible states. Skeletons while ph.state.list resolves; a load failure says progress won’t be remembered; a save failure says a change didn’t save. Save errors are keyed per step, so one step’s later success can’t hide another step’s failed write. Never fall through to an empty checklist that looks freshly reset.
Every step states its expected outcome. A checkbox alone tells the runner what to do, not how to know it worked — the expect line is what makes the list a runbook instead of a todo list. A notes field per step captures deviations where they happened.
Destructive reset asks twice, then cancels pending saves. The reset button swaps to a confirm button instead of clearing shared progress on one click, and the reset clears every pending debounced timer first so an in-flight note save can’t write itself back after the keys are deleted.
Capabilities for this project: the full capabilities.posthog shape with state: ["shared"] and everything else empty (insights: [], inlineQueries: false, captureEvents: [], actions: [], network.origins: []). Keep index.html and dependencies exactly as canvas-source-retrieve returned them.
Replace the sample sections with the user’s actual steps. Keep the shape: stable ids (they key the persisted state — renaming an id orphans its saved progress), an expect on every step, optional detail, cmd, and tag.
ts
export type Step = { id: string title: string detail?: string cmd?: string expect: string tag?: string}export type Section = { id: string title: string blurb: string steps: Step[]}export const SECTIONS: Section[] = [ { id: 'setup', title: 'Environment', blurb: 'Get a clean build running before touching the flows below.', steps: [ { id: 'setup-build', title: 'Build and boot the app', cmd: 'pnpm install && pnpm dev', expect: 'The app opens with no errors in the console.', }, { id: 'setup-health', title: 'Check the API is up', cmd: 'curl -s https://api.example.com/healthz', expect: 'A 200 response with {"status":"ok"}.', tag: 'blocking', }, ], }, { id: 'flows', title: 'Core flows', blurb: 'The paths most users hit. Each step assumes the environment section passed.', steps: [ { id: 'flows-signup', title: 'Sign up a fresh account', detail: 'Use a throwaway address so the welcome email path runs end to end.', expect: 'The account lands on the onboarding screen and the welcome email arrives.', }, { id: 'flows-invite', title: 'Invite a teammate', expect: 'The invite email arrives and its accept link joins the right workspace.', }, { id: 'flows-export', title: 'Export a report', detail: 'Any dashboard will do; the point is that the download completes.', expect: 'A CSV downloads and opens with the expected columns.', tag: 'flaky area', }, ], },]export const TOTAL_STEPS = SECTIONS.reduce((n, s) => n + s.steps.length, 0)
Adapt freely above the persistence layer: a “Before you start” card of prerequisites under the header, a per-section owner badge, or ordering hints all fit the same shape. What should survive every adaptation: the per-step shared-state keys, the ref-alongside-state update path, the visible load/save failure states, and the expect line on every step.