A visual tool for the Mikado method.
Users create a single goal, decompose it into sub-tasks, and work from leaf nodes up.
No backend: all state lives in the URL.
pnpm dev # Start Vite dev server
pnpm test # Vitest in watch mode
pnpm test --run # Single run (use before committing)
pnpm build # TypeScript check + Vite production build
pnpm lint # ESLint, zero warnings allowed
pnpm format --write # PrettierGotchas:
console-fail-testis active. Anyconsole.log/warn/errorin tests will fail them.- Pre-commit hook runs lint-staged (Prettier on staged files).
Three layers, strictly separated. See docs/architecture.md for details.
src/model/ Pure functions & types. No React, no side effects. Fully testable.
src/store/ Zustand store. Thin adapter over model functions. Exposes actions.
src/components/ React + ReactFlow rendering. Minimal logic.
src/App.tsx Top-level composition.
Data flow: User interaction > Component > Store action > Model function (pure) > New state > ReactFlow re-renders
Key constraints:
- One goal (root task) per graph
- Deleting a task cascade-deletes its entire sub-tree
- The graph is a DAG, no cycles
- Named exports only, no
export default typeoverinterface; discriminated unions for state- Immutability:
const, no mutations,.map/.filter/.reduceover loops - No
any. Useunknown+ narrowing - Let TypeScript infer types and return types
- Exported functions first, private helpers below
- Extract logic into well-named functions instead of comments; comments only for "why"
- Install dependencies with exact versions (
--save-exact) - Tabs for indentation (Prettier config)
- Integration tests are the default: render components, interact via Testing Library, assert visible outcomes
- Unit tests for pure model functions in
src/model/, with structural assertions (toEqual,toMatchObject) - Query by role, text, or label. Avoid querying by CSS class names.
- Assert whole results, not individual properties
- Test files colocated:
Foo.tsx/Foo.test.tsx,graph.ts/graph.test.ts
- Version 12. Import from
"@xyflow/react"(named exports, no default import) - Custom node types go in
src/components/nodes/ - Memoize custom node components to prevent full-graph re-renders
panOnDrag={false}is required for Testing Library compatibility (see App.tsx comment)ResizeObservermock is insrc/test-setup.ts(required for jsdom)
Detailed specs live in docs/features/. They are numbered by dependency order. Implement them in sequence. Update the number below after completing each feature.
Last completed feature: 19
- Parking slot (park tasks blocked by external factors)
- Timebox timer (set/start/stop, default 15 min, time tracking per task/day)
- Real-time collaboration (y.js + WebRTC, no server)
- Local storage persistence to allow N graphs (pro-feature?)
For all docs, specs, and comments in this project:
- Short sentences. Get to the point.
- No em-dashes. Use periods, colons, or parentheses instead.
- Don't repeat yourself. Say it once clearly.
- Use active voice. "The store updates state" not "State is updated by the store."
- Bullet points over paragraphs when listing facts or rules.
- No filler ("In other words", "It's worth noting", "Essentially").
- Titles follow conventional commits:
feat:,fix:,refactor:,test:,docs:,chore: - Keep PRs single-purpose
- CI runs: build, lint, test, type-check. All must pass.