Turn an accepted kitsoki proposal (or an epic + its children) into a validated, reviewed YAML decomposition of right-sized agent briefs. Use when the user wants to decompose work into briefs, "break this proposal into tickets/tasks", produce a decomposition.yaml, or check out the brief-manifest shape from docs/proposals/work-decomposition.md before that pipeline is built as a story. Drives discovery → manifest → deterministic structural lint → adversarial feasibility/completeness review, by hand.
This skill is the Claude-driven version of the pipeline proposed in
docs/proposals/work-decomposition.md:
it takes an accepted proposal (or an epic + its linked children) and produces
a decomposition.yaml of agent briefs — structurally validated and
adversarially reviewed — so you can check out the manifest shape and the
slicing quality before the stories/decompose/ story is built.
The same discipline the proposal encodes applies here, just run by you instead of the engine: the interpretive decisions (how to slice, is it feasible, is it complete) are your judgment; the structural truth (unique ids, acyclic DAG, coverage, path bounds) is a deterministic script whose exit code is the gate — the kitsoki moat: separate interpretation from deterministic execution.
Scope: this decomposes an accepted proposal. Authoring/scoping the proposal itself is the
proposal-authoringskill. Implementing a single brief is thestories/implementation/pipeline (andkitsoki-story-authoringfor story-layer work). This skill stops at a revieweddecomposition.yaml.
The deliverable is a manifest matching
schemas/decomposition.json: a coverage_note
plus a briefs[] array, each brief carrying id, title, kind, goal, scope[], depends_on[], acceptance[], test_plan, agent_brief (and optional risk).
coverage_note: >
These four briefs fully cover the proposal: #1 adds the engine seam, #2 the
story that uses it, #3 the TUI surface, #4 the docs+flow fixtures. Nothing in
the proposal's "What changes" is left unowned, and no two briefs touch the
same files.
briefs:
- id: engine-once-flag
title: Add `once:` guard to room on_enter effects
kind: runtime
goal: A room with `once: true` re-runs its on_enter effects at most once per visit, surviving /reload.
scope: ["internal/machine/", "internal/app/imports.go"]
depends_on: []
acceptance:
- "A reloaded room with once:true does not re-fire its on_enter oracle call"
- "Existing rooms without once: behave unchanged"
test_plan: "internal/machine/machine_test.go: new TestOnceGuard; existing flow fixtures stay green."
agent_brief: >
Implement an engine-level `once:` boolean on room on_enter effect lists...
(self-contained — everything the implementer needs, no external context).
risk: mediumkind ∈ story | runtime | tui | tracing | test | docs. scope globs are the
write boundary for the brief; new-file briefs legitimately point at paths
that don't exist yet (the validator bounds them rather than requiring a match).
Run these phases in order. They mirror the proposal's rooms; each gate must pass before the next.
Resolve the source the user named (a docs/proposals/<slug>.md, or an epic).
Read the full body. Detect epic vs. focused: an epic has **Kind:** epic
and a Slices table over linked children — if so, read every linked child
proposal too, and warn (non-fatal) on any child file that's missing or renamed.
Confirm back to the user: the title, kind, and (for an epic) the child count, so
they know you pointed at the right thing.
Before slicing, have a short conversation to pin the constraints a static read can't give you:
Capture the answers in .context/decompose-<slug>-scope.md (transient working
notes, per CLAUDE.md). Don't skip this even when the proposal looks complete —
the slicing quality lives here. Stop when you can state the slicing rationale in
two sentences.
Produce the manifest. Slicing rules:
depends_on is the real build order — be honest; a missing edge is what the
reviewer and the board will trip on.scope globs should not overlap on
the same file. Overlap is a slicing bug.agent_brief is self-contained — the implementer agent gets only that
text plus the ticket fields, not this conversation. Spell out the approach,
the key files, and the done-condition.coverage_note is your completeness claim — write it to be attacked:
state explicitly how the briefs together cover every item in the proposal's
"What changes", with nothing unowned.Write it to .artifacts/decompose/<slug>/decomposition.yaml (generated
artifact, not committed — CLAUDE.md).
Run the bundled validator. Its exit code is the gate — do not proceed to review on a non-zero exit:
python3 .agents/skills/work-decomposition/scripts/validate_decomposition.py \
.artifacts/decompose/<slug>/decomposition.yaml --repo-root .It checks schema shape, unique ids, dangling depends_on, an acyclic
dependency DAG, scope paths bounded inside the repo (parent dir exists),
and non-empty acceptance + test_plan per brief. On failure: fix the manifest
(go back to step 3 with the errors as the brief) and re-run until clean. This
gate has teeth independent of any LLM — trust it over your own read.
There is also a Starlark equivalent (validate_decomposition.star) that covers
the pure-logic checks (unique ids, acyclic DAG, acceptance/test_plan) and can be
called via host.starlark.run from a story room — pass the parsed manifest as
inputs.manifest. It does not cover JSON-schema shape or scope-path bounds
(those need filesystem access and the jsonschema library; use the Python script
for those).
Now switch hats and attack your own manifest as a skeptic (or, for a real
second opinion, spawn a sub-agent with the Agent tool to do it independently —
that's closer to the proposal's decomp_adversary):
coverage_note, name anything in "What changes" that no brief owns. Is
there file overlap / double-ownership?revise when uncertain. A confident "looks good" with no
attempted refutation is not a review.Emit a verdict: {verdict: accept | revise, reason, questions[]}. On revise,
fold questions[] back into step 3 and re-run validate + review. Budget the
loop (≈5 passes); if it won't converge, stop and tell the user why —
unconverged slicing is a signal the proposal isn't decomposable as written.
On accept, present:
decomposition.yaml,status · id — title (deps) per brief in dependency
order, with the parallelizable briefs called out,coverage_note and the review verdict + reason.Note for the user that each brief is shaped to become a ticket the
stories/implementation/ pipeline can build (id/title/body = agent_brief + acceptance + scope + test_plan) — which is exactly what the proposal's
dispatch room will mint once the story ships.
| Step | Mechanism | Gate |
|---|---|---|
| Load proposal/epic | read docs/proposals/<slug>.md (+ children) | right source confirmed |
| Discovery | short conversation → .context/ scope note | slicing rationale in 2 sentences |
| Decompose | emit manifest → .artifacts/decompose/<slug>/decomposition.yaml | schema-shaped |
| Validate | validate_decomposition.py (CLI) or host.starlark.run validate_decomposition.star (story room) | exit code 0 / ok: true |
| Review | skeptic pass (or sub-agent) | verdict: accept |
| Hand back | board + coverage note + verdict | — |
Codex discovers this skill directly. Refresh the project-local Claude Code symlink after adding or moving skills:
make setupWhen the stories/decompose/ story from the proposal ships, the
schemas/decomposition.json here and stories/decompose/schemas/decomposition.json
should stay identical — they're the same contract; reconcile them rather than
letting them drift.
1f4abf0
If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.