Efforts, waves & worktrees
How cckit runs many issues at once without the changes colliding.
A big piece of work is an effort. cckit breaks it into sub-issues, then sorts them into waves so the ones that don’t depend on each other run together, and the ones that do wait their turn.
Efforts and sub-issues
Section titled “Efforts and sub-issues”An effort is one goal that’s too big for a single issue. It becomes a parent issue with
sub-issues under it — each sub-issue is one shippable step. cckit effort new creates the
effort and its subs: it fills the parent’s four-section plan body (Goal / Scope / For agents /
Verification), applies the ctx/kind/priority/role/flow labels, and lints every title. The
/kit-effort-new skill produces the identical effort — both call one shared core, so there is no
drift between the verb and the skill.
Slug handles
Section titled “Slug handles”The GitHub issue number is the canonical key for an effort — the sub-issue links, PRs, labels,
and dependencies all reference it. But a bare number is hard to remember, so every effort also has a
memorable slug handle: the one that already lives in its effort/<N>-<slug> branch.
cckit effort start, pr, and close accept the slug or the number interchangeably:
cckit effort new "Slug handles for efforts" --slug slug-handles # optional explicit handlecckit effort start slug-handles # identical to: cckit effort start 93cckit effort pr slug-handlescckit effort close slug-handlesA pure-digits argument is always treated as a number; anything else is resolved to the canonical
number by matching the effort/* branches (local and remote), then the slug:<slug> label, then
the titles of open efforts. An unknown or ambiguous slug fails with a clear error instead of guessing
the wrong effort. Listings and titles render efforts as slug #N.
The lifecycle: start → pr → close
Section titled “The lifecycle: start → pr → close”cckit effort new— flags (--flow,--role,--priority,--par, …) may appear in any position, before or after the name and sub specs.--par seq | wide | <N>records how the effort’s subs are meant to run as apar:label on the parent. The--flowvalues (the[Flow]title tags) come from a controlled vocabulary your project sets once aseffort.flowsincckit.config.json/.claude/kit.config.json— e.g."effort": { "flows": ["Growth", "Checkout"] }. Unset, it defaults toCore UI API Docs Infra Auth Data Web App; an explicitEFFORT_FLOWSenv var still overrides both for one invocation.cckit effort startgives the effort’s worktree the same full setup ascckit start: it copies your gitignored env files, assigns a per-worktree dev port, and installs dependencies (opt out withKIT_WT_INSTALL=0). The worktree lives at.claude/worktrees/effort+<N>-<slug>, a namecckit gcrecognizes so it is never pruned while the effort issue is open.cckit effort prand the/kit-effort-prskill compose the PR title from one shared composer, and both refuse to open a PR whose title lacks the mandatory[#N]effort id.cckit effort closeis the single close implementation — the/kit-effort-closeskill is a thin caller of the same function, so the two can never drift apart. It snapshots each sub-issue’s diff and captures effort metrics before the squash, refuses to squash-merge when no work trace was captured (the squash is irreversible — override withKIT_FORCE=1), squash-merges, closes the parent and every sub and sets the board Status=Done for each, garbage-collects the worktree/branch, runs an optional, config-gated knowledge-ingest hook (effort.knowledgeIngestHook), and finishes with an advisory kit-sync drift check that flags any kit-managed files the effort touched (they belong upstream via/kit-contribute).- Wave-driven efforts close too. A wave run ships each sub as its own task PR squash-merged
straight to the base branch, so no
effort/<N>integration branch ever exists.cckit effort close <N>detects that (no such branch locally or on origin) and closes wave-style: it verifies every sub is closed with a merged PR (refusing and listing the stragglers otherwise), snapshots the work trace from the merged sub PR diffs instead of branch commits, and then runs the same close tail — metrics judge/sync, parent close, board Status=Done for parent and subs, the knowledge-ingest hook, and the kit-sync drift check. If the integration branch does exist, the close still asks you to run from it.
Some sub-issues depend on others: sub #4 can’t start until sub #1 is merged. cckit reads those blocked-by links and groups the work into waves:
- Wave 0 — every sub-issue that nothing is blocking. These can all run at the same time.
- Wave 1 — sub-issues whose blockers were all in wave 0. They start once wave 0 merges.
- …and so on, until everything is merged.
cckit plan shows the waves. Each wave is exactly “what can run in parallel right now”.
Worktrees keep parallel work apart
Section titled “Worktrees keep parallel work apart”Running several issues at once raises one question: how do the changes not overwrite each other? The answer is a worktree — a separate working copy of the repo on disk, on its own branch.
Each issue gets its own branch and worktree, so each piece of work edits its own copy. The changes
only meet again when their pull requests merge into main — and because the issues in a wave touch
different files, they merge cleanly.
Independent, educational project — not affiliated with or endorsed by Anthropic. Claude and Claude Code are trademarks of Anthropic PBC. Disclaimer & trademarks ·
From Mexico with love by josegtz