Shape a problem into stories
A shape workflow turns a typed problem into a brief, an answered interview and an epic of sized, dependency-checked stories, ready to queue.
codeloop start wants one story already sized S, M or L. A person with a problem, "let a
workspace publish a skill pack from the web app", has to do that sizing by hand first. The shape
workflow does it instead:
codeloop shape "Let a workspace publish a skill pack from the web app"What it does
Shape is a lane with four stages, run in order:
| Stage | Who | Writes | Check |
|---|---|---|---|
| brief | agent | shape/{id}/brief.md |
problem, users, at least one exists: line, three sources |
| interview | agent asks, owner answers | shape/{id}/interview.md |
at least three questions answered |
| breakdown | agent | shape/{id}/breakdown.md |
the rules below |
| rank | agent | rice: on every story in breakdown.md |
the rules below, plus RICE on every line |
The owner approves the ranked plan at the last stage. Everything up to there runs unattended.
The brief
Before any story gets written, the brief stage searches this codebase, the wiki and any sibling repo the problem names, and writes what it finds:
# pack-publish brief: Let a workspace publish a skill pack from the web app
problem: A workspace that has installed or built several skills in Acme has no way to turn a
chosen set of them into one thing a developer installs elsewhere.
users: team
## What exists
exists: have, the workspace skill library and the one-server install page for a single skill
already ship (apps/web/app/skills/page.tsx, apps/web/app/install/[slug]/page.tsx)
exists: unlock, `pack build` on the CLI already assembles a skill pack and uploads it to the
catalog (packages/cli/src/commands/pack.ts); the web app has no UI for it
exists: build, there is no way in the web app to select skills into a draft pack, save it, or
reopen it
## Sources
- source: https://acme.example/docs/skills — the library page today, one card per skill
- source: packages/cli/src/commands/pack.ts — `pack build` already shipped on the CLI
- source: .codeloop/wiki/decisions/role-packs.md — packs are an explicit install, the web
app is the storeexists: is the same have/unlock/port/build verdict the story standard
uses everywhere else. A capability the brief finds already shipped never gets a story; one it finds
half-built gets a story to expose it, not rebuild it.
The breakdown
Once the brief and the interview are in, the breakdown stage writes an epic:
# Epic: Publish a skill pack from the web app
hypothesis: letting a workspace turn its own skills into one installable pack gets its developers
using the hosted MCP endpoint instead of wiring up each skill by hand.
metric: packs published per workspace
feature: skill-pack-publishing
## Stories
- S1 [S] See my skills listed in the web app · exists: unlock apps/web/app/skills/page.tsx · done_when: open /skills, see the list · depends_on: none · rice: R=8 I=5 C=9 E=1
- S2 [M] Pick skills into a draft pack · exists: build · done_when: a draft pack with two skills saved and reopened · depends_on: S1 · rice: R=7 I=7 C=7 E=2
- S3 [S] Publish the pack to the workspace catalog · exists: unlock packages/cli/src/commands/pack.ts · done_when: the pack appears in the catalog with its skills · depends_on: S1, S2 · rice: R=7 I=8 C=8 E=1
- S4 [S] Install page for a published pack · exists: unlock apps/web/app/install/[slug]/page.tsx · done_when: open the install page, copy the MCP URL · depends_on: S3 · rice: R=6 I=6 C=9 E=1
## Later
- Versions and deprecationFields separate with · (space, middle dot, space). Size is [S] or [M] in brackets.
depends_on is none or an earlier story in the same file. The rank stage appends
rice: R=<n> I=<n> C=<n> E=<n> (reach, impact, confidence on 1–10, effort in weeks, the same scale
as a feature's RICE) to every line once the stories are settled.
What the check refuses, and why
| Refused | Why |
|---|---|
A story with no size, or [L] |
A card that takes three days or more, or needs design, is a feature with several stories, not one. Split it with SPIDR (paths, interfaces, data, rules, spike) before it reaches this file. |
A missing or placeholder done_when ("all tasks complete") |
A story nobody can tell is finished isn't a story. |
A missing exists: verdict |
The whole point of shaping first is to not build what already exists; a story with no verdict skipped that question. |
A depends_on naming a later or unknown story |
Every story has to be shippable the moment its listed dependencies are done. A forward reference would let an unshippable story queue anyway. |
More than seven stories in ## Stories |
Past seven, it's an epic that needs cutting, not a longer list. The rest goes under ## Later, deferred, not queued. |
No hypothesis: or metric: on the epic |
An epic with no stated belief and no number it's meant to move can't be checked against reality later. |
(with --ranked) a story with no rice: |
Ranking has to cover every story once breakdown is done, not just the ones that got to it first. |
What happens on approval
When the owner approves the ranked plan, the engine writes wiki/epics/<slug>.md with the stories
as a table, a wiki/features/<slug>.md page per feature named in the breakdown, and starts one
build story per line, carrying split_from: <epic>, its size, metric and done_when into the new
card. The first story goes active; the rest wait in the backlog in the order they were written.
When a story reaches done, the next one starts.
Not yet
- Pulling a problem from a tracker (Linear, Jira, GitHub). The problem is typed or pasted into
codeloop shape. - Re-planning an epic after a story has already shipped. The breakdown is written once; changes after that are made by hand.
- Cost or time estimates beyond size and RICE's effort-in-weeks.
See Cards and gates for how an approval moves a card once it's queued, and the story standard for the size, RICE and unlock-before-build rules this workflow shares with the rest of the board.