Workflow authoring
Conventions and hard-won gotchas for authoring/maintaining the whiteboard repo's Dynamic Workflows (.claude/workflows/*.workflow.mjs) — dev-loop, review, dogfood-triage, reconcile. Use when editing or adding a Workflow script, debugging args not arriving, composing workflows, or running parallel dev-loops.From its SKILL.md
npx -y skills add kamiazya/whiteboard --skill workflow-authoringAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 5 stars5 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
SKILL.md
6.7 KB, as published. Nobody here has run it
Workflow Authoring (whiteboard)
The repo's Dynamic Workflows live in .claude/workflows/*.workflow.mjs and are run via the Workflow tool. Roles: dev-loop (one task's inner loop) composes review (change-review gate, which has an optional inline dogfood phase); plan-initiative (multi-perspective planning) optionally composes consult-adversarial (Vet) and investigate (ground policy/hygiene/portability questions); dogfood-triage (standalone persona dogfooding); reconcile (pre-merge intent/conflict reconciliation); investigate (read-only, lightweight: fan out one investigator per concern dimension → synthesize a go/no-go with required-before-adopting steps; reusable for any "is it safe to commit/track X" question). The main session is the single integrator — it holds human gates (design approval, commit, PR, merge), git, and CI monitoring; workflows automate the rest and return needsHumanGate.
Gotchas (verified)
-
argsarrives as a JSON string, not an object.args.foois alwaysundefined→ silent fallback to defaults. Normalize at the top of every script:const A = (() => { try { return typeof args === 'string' ? JSON.parse(args) : (args && typeof args === 'object' ? args : {}) } catch { return {} } })()Then read
A.foo. (A no-argargs-probeworkflow thatreturn { typeofArgs: typeof args }confirms this.) -
Nesting is ONE level.
workflow()inside a child throws. Sodev-loop → reviewis fine, butreviewmust NOT callworkflow()— its dogfood is an inline phase (agent + Playwright), not a nested workflow.reconcileonly detects/judges; the integrator does the actual fold. -
Parallel dev = main session fans out, not a parent workflow. A parent-workflow → dev-loop → review would be 2 levels. Instead the main session launches several
dev-loopruns concurrently (each its own task), and folds results viareconcile. -
Parallel dev-loops run in isolated worktrees. A bare
git worktreehas nonode_modules, butnode .claude/scripts/new-worktree.mjs <name> [baseRef]doesgit worktree add .claude/worktrees/<name>+pnpm install --prefer-offline(~6s, warm pnpm store) so tests/typecheck run isolated there. Parallel pattern: the integrator (main session) creates one ready worktree per independent item, launches adev-loopwithcwd=<that worktree>(cwd-aware:git -C <cwd>+ agents edit under it), runs several concurrently, thenreconcilebefore folding. The main session stays at repo root (nevercds away — see #5). A single-task dev-loop may still run on the main tree (implement stages only its files, never-A). -
Compose with a REPO-ROOT-RELATIVE
scriptPath(.claude/workflows/review.workflow.mjs) and NEVERcdaway from repo root while workflows run. The runtime resolves a relative scriptPath against the session cwd at call time. Because the repo mandates that the main session stays at repo root (it passes absolute paths to Bash / usesgit -C, nevercds away), a repo-relative scriptPath always resolves. An absolute/Users/...path is banned for shared workflows — it breaks for every other contributor and clone location (this repo is public OSS). The workflow sandbox has noimport.meta/__dirname/process.cwd/fs, so there is no programmatic path anchor; relative-path-plus-no-cd-discipline is the only portable mechanism. (History: an unguarded relative path once broke a 43-min/3M-token run when a Bashcdchanged cwd mid-run — the fix is the no-cd discipline, not an absolute path.) -
.claude/workflows/is NOT name-registered.Workflow({name:'review'})fails; launch withWorkflow({scriptPath:'.claude/workflows/review.workflow.mjs'}). The dir is tracked in git like the rest of.claude/'s shared tooling — a new/edited workflow script lands as a normal commit, nogit add -fneeded. -
Custom agents added mid-session are NOT in the
agentTyperegistry until a session reload. Writing.claude/agents/foo.mdand immediately launching a workflow that doesagent({agentType:'foo'})throwsagent type 'foo' not found(the registry is snapshotted at session start) — the workflow still runs but every suchagent()call fails (verified:investigatefanned out 6 dimensions, all failed, only the registeredarchitectsynth survived). Default workflowagentTypes to already-registered agents (architect, Explore, developer, general-purpose, reviewer-dimension, plan-reviewer, qa-scenario, security-scanner, code-simplifier:code-simplifier, the planning panel, codex:codex-rescue…) and thread an override arg for the tuned custom one (e.g.investigatedefaults its investigator toExplore, overridable viaargs.investigatorAgent). Reload the session before relying on a newly-authored agent as a workflowagentType. -
Fold from the branch TIP, not
implReport.headSha. A dev-loop commits again after the implement phase (simplify, fix rounds, an extra docs/test fix), so the returnedimplReport.headShais mid-run and stale. When the integrator folds (cherry-pick/merge), resolve the range from the live branch tip —git -C <worktree> rev-parse HEADorgit rev-parse <branch>— never the reported impl sha. Cherry-pickingbase..<implHeadSha>silently drops every later commit. After folding, verify withgit cherry HEAD <oldTip>(any+line = an unapplied commit) before deleting the branch.
Conventions
- Specialize phases via
agentType. Pass{agentType:'reviewer-dimension'|'qa-scenario'|'security-scanner'|'code-simplifier:code-simplifier'|'plan-reviewer'|'developer'|...}. It composes withschema(StructuredOutput appended). Default (no agentType) = generic agent + inherited AGENTS.md. - Structured output: pass a JSON-Schema
schemaso the agent returns validated data, not prose. - cwd/baseRef: cwd-aware scripts prepend
git -C <cwd>; review diffs${baseRef}..HEAD. The integrator capturesbaseRef(pre-task HEAD) before launching. - Cost: tune
dimensions/qaScenarios/persona counts down for small increments. A fullreviewis ~600k tokens; a fulldev-loopmore. - Iterate by editing the script file and re-invoking
Workflow({scriptPath, resumeFromRunId})— unchangedagent()calls return cached. - Syntax-check locally without running: wrap the body in
async function _wf(){ ...stubs... }andnew Function(...).