| name | feature-to-backlog |
| description | Drives the Proposal-to-Backlog pipeline for a software feature: two architect-reviewed cycles (one for Proposal, one for Plan), each converging on APPROVED within 8 rounds. Outputs a planSet entry ready for implementation; does not create branches or open PRs. |
| argument-hint | ["feature-topic-or-description"] |
| allowed-tools | Read, Glob, Grep, Bash, Agent |
| contracts | [{"grep":"proposalLoop","target":"self"},{"grep":"planLoop","target":"self"},{"grep":"APPROVED","target":"self"},{"not-grep":"git worktree add","target":"self"}] |
λ(topic) → featureToBacklog(topic)
Spec
BuildConfig :: {
testCmd : String, -- per-phase test runner; becomes DoD[0] in generated plans
testAll : String, -- full suite; becomes Acceptance Gate[0]
docPath : String -- root for proposals/ and plans/ subdirectories
}
loadConfig :: () → BuildConfig -- see spec-stdlib § loadConfig
loadConfig() =
| fromClaudeMd() -- explicit: "## L0 Config" section in CLAUDE.md
| autoDetect() -- implicit: probe package.json, go.mod, Cargo.toml, etc.
autoDetect :: () → BuildConfig
autoDetect() = -- see spec-stdlib § loadConfig
detectLang :: () → Lang -- see spec-stdlib § detectLang
-- see spec-stdlib § detectLang
-- Core document types
Proposal :: {
background : String, -- WHY this feature is needed (3-8 lines)
goals : [VerifiableGoal], -- each Goal checkable by inspection or shell command
approach : String, -- high-level design; no implementation code
tradeoffs : String -- what we are NOT doing; known risks
}
Phase :: {
title : String,
tests : [TestSpec], -- written before implementation; must fail first
impl : [FileChange], -- code that makes the tests pass
dod : [ShellCmd] -- dod[0] MUST be cfg.testCmd (red→green proof)
}
Plan :: {
phases : [Phase], -- ordered; earlier phases feed later ones
constraints : [String], -- non-executable criteria (NOT in dod)
acceptance : [ShellCmd] -- acceptance[0] is cfg.testAll
}
-- Input: topic is either a task ID (e.g. TASK-12, task-12) or a free-form description.
-- Task ID → resolve existing task; resume from its current status using its description as draft.
-- Description → create a new task and run the full proposal → plan workflow.
EntryPoint = ProposalLoop | PlanLoop
-- ProposalLoop : enter proposal reviewLoop (new task, or Proposal status)
-- PlanLoop : skip proposal; enter plan reviewLoop (Plan status)
resolveOrCreate :: Topic → (Task, EntryPoint)
resolveOrCreate(T) =
| isTaskId(T) → (lookupTask(T), fromStatus(lookupTask(T).status))
| otherwise → (createTask(T), ProposalLoop)
fromStatus :: Status → EntryPoint
fromStatus("Basic: Plan") = PlanLoop
fromStatus(_) = ProposalLoop -- Basic: Proposal or other
-- Workflow
featureToBacklog :: Topic → BacklogTask
featureToBacklog(T) = {
cfg: loadConfig(),
(task, entry): resolveOrCreate(T),
-- ProposalLoop: use existing description as proposal draft (skips fresh draft if task ID given)
-- PlanLoop: skip proposal stage entirely
proposal: case entry of
ProposalLoop → reviewLoop(task, task.description, 8)
PlanLoop → task.description -- not used; plan stage reads task.description directly
plan: case entry of
ProposalLoop → reviewLoop(task, draftPlan(task, proposal, cfg), 8)
PlanLoop → reviewLoop(task, task.description, 8) -- description IS the plan draft
_: finalise(task, proposal, plan, cfg),
return: task -- status: Backlog
}
-- see spec-stdlib § reviewLoop (MaxRounds = 8)
-- Plan review invariants (all must hold for APPROVED)
reviewPlan :: (Plan, BuildConfig) → Verdict
reviewPlan(P, cfg) = {
∀phase ∈ P.phases: {
assert: ¬empty(phase.tests), -- TDD: Tests section must exist
assert: phase.dod[0] starts with cfg.testCmd, -- TDD: first DoD proves red→green
assert: ∀cmd ∈ phase.dod: isShellCmd(cmd)
},
assert: P.acceptance[0] == cfg.testAll,
assert: ∀goal ∈ proposal.goals: coveredBy(goal, P.phases ∪ P.acceptance),
assert: ∀phase ∈ P.phases: allFilesExist(phase.impl),
return: APPROVED | NEEDS_REVISION
}
Implementation
loadConfig
REPO_ROOT=$(git rev-parse --show-toplevel)
resolveBaimeScripts() {
local proj_scripts="${REPO_ROOT}/.claude/plugins/baime/scripts"
if [ -d "$proj_scripts" ]; then echo "$proj_scripts"; return 0; fi
for settings_file in "${HOME}/.claude/settings.json" "${HOME}/.claude/settings.local.json"; do
if [ -f "$settings_file" ]; then
local baime_path
baime_path=$(python3 -c "
import json, sys
try:
d = json.load(open('$settings_file'))
for mp in d.get('extraKnownMarketplaces', []):
p = mp.get('path', '')
if 'baime' in p:
import os
scripts = os.path.join(p, 'scripts')
if os.path.isdir(scripts):
print(scripts)
sys.exit(0)
except Exception:
pass
" 2>/dev/null || true)
if [ -n "$baime_path" ]; then echo "$baime_path"; return 0; fi
fi
done
local xdg_scripts="${XDG_DATA_HOME:-${HOME}/.local/share}/baime/scripts"
if [ -d "$xdg_scripts" ]; then echo "$xdg_scripts"; return 0; fi
echo "resolveBaimeScripts: ERROR — cannot locate BAIME plugin scripts directory." >&2
return 1
}
BAIME_SCRIPTS=$(resolveBaimeScripts) || exit 1
L0_SECTION=$(awk '/^## L0 Config/{found=1; next} found && /^## /{exit} found{print}' \
"${REPO_ROOT}/CLAUDE.md" 2>/dev/null)
parse_cfg() { echo "$L0_SECTION" | grep -oP "(?<=^$1:\s)\S.*" | head -1 | xargs; }
CFG_TEST_CMD=$(parse_cfg "test-cmd")
CFG_TEST_ALL=$(parse_cfg "test-all")
CFG_DOC_PATH=$(parse_cfg "doc-path")
if [ -z "$L0_SECTION" ]; then
if [ -f "${REPO_ROOT}/package.json" ]; then LANG=node
elif [ -f "${REPO_ROOT}/go.mod" ]; then LANG=go
elif [ -f "${REPO_ROOT}/Cargo.toml" ]; then LANG=rust
elif [ -f "${REPO_ROOT}/pyproject.toml" ] || [ -f "${REPO_ROOT}/setup.py" ]; then LANG=python
else LANG=unknown; fi
case "$LANG" in
node) CFG_TEST_CMD="${CFG_TEST_CMD:-npm test -- --run}"; CFG_TEST_ALL="${CFG_TEST_ALL:-npm test}" ;;
go) CFG_TEST_CMD="${CFG_TEST_CMD:-go test ./...}"; CFG_TEST_ALL="${CFG_TEST_ALL:-go test ./...}" ;;
rust) CFG_TEST_CMD="${CFG_TEST_CMD:-cargo test}"; CFG_TEST_ALL="${CFG_TEST_ALL:-cargo test --workspace}" ;;
python) CFG_TEST_CMD="${CFG_TEST_CMD:-pytest -k}"; CFG_TEST_ALL="${CFG_TEST_ALL:-pytest}" ;;
*) CFG_TEST_CMD="${CFG_TEST_CMD:-make test}"; CFG_TEST_ALL="${CFG_TEST_ALL:-make test}" ;;
esac
CFG_DOC_PATH="${CFG_DOC_PATH:-docs}"
echo "L0 auto-detected config: lang=${LANG} testCmd=${CFG_TEST_CMD} testAll=${CFG_TEST_ALL} docPath=${CFG_DOC_PATH}"
else
CFG_TEST_CMD="${CFG_TEST_CMD:-make test}"
CFG_TEST_ALL="${CFG_TEST_ALL:-make test}"
CFG_DOC_PATH="${CFG_DOC_PATH:-docs}"
echo "L0 config loaded from CLAUDE.md: testCmd=${CFG_TEST_CMD} testAll=${CFG_TEST_ALL} docPath=${CFG_DOC_PATH}"
fi
Derive slug and title from <topic>:
SLUG=$(echo "<topic>" | tr '[:upper:] ' '[:lower:]-' | tr -cd '[:alnum:]-' | cut -c1-50)
TITLE=$(echo "<topic>" | cut -c1-70)
If <topic> is empty: print usage and stop.
Phase 0: Manifest Generation and Lint
Before executing any phase, generate a manifest JSON that describes the planned execution:
{
"skill": "feature-to-backlog",
"task_id": null,
"entry_point": "createTask",
"skip_draft": false,
"field_writes": [
{ "tool": "backlog task edit", "field": "plan", "source": "$TMPDIR/ftb-plan.md" },
{ "tool": "backlog task edit", "field": "status", "value": "Basic: Backlog" }
],
"phases_to_execute": ["createTask", "proposalLoop", "planLoop", "finalise"]
}
Write the manifest to $TMPDIR/feature-to-backlog-manifest.json, then validate it:
bash "$BAIME_SCRIPTS/skill-lint.sh" --manifest "$TMPDIR/feature-to-backlog-manifest.json"
If validation fails, stop and report the error before proceeding.
Phase 1: resolveOrCreate + maybe draftProposal
1a. resolveOrCreate (orchestrator runs directly):
if echo "<topic>" | grep -qiP '^task-\d+$'; then
TASK_ID=$(echo "<topic>" | tr '[:lower:]' '[:upper:]')
backlog task view "$TASK_ID" --plain > $TMPDIR/ftb-existing-task.txt
echo "$TASK_ID" > $TMPDIR/ftb-task-id.txt
awk '/^Description:/{found=1;next} found && /^-{10,}/{if(!sep){sep=1;next};exit} found && sep{print}' \
$TMPDIR/ftb-existing-task.txt > $TMPDIR/ftb-proposal.md
TASK_STATUS=$(grep -oP '(?<=Status:).*' $TMPDIR/ftb-existing-task.txt | head -1 | grep -oP '(Basic|Epic): [A-Za-z ]+' | head -1 | xargs)
case "$TASK_STATUS" in
"Basic: Plan") echo "PlanLoop" > $TMPDIR/ftb-entry-point.txt ;;
*) echo "ProposalLoop" > $TMPDIR/ftb-entry-point.txt ;;
esac
else
backlog task create "$TITLE" \
--status "Basic: Proposal" \
--label "kind:basic" \
--description "<topic>" \
--plain
echo "ProposalLoop" > $TMPDIR/ftb-entry-point.txt
fi
If $TMPDIR/ftb-entry-point.txt contains PlanLoop: skip phase 1b and phases 2–3; proceed directly to Phase 4 using $TMPDIR/ftb-proposal.md as the plan draft (rename it to $TMPDIR/ftb-plan.md).
1b. draftAndReview — spawn Task agent "Draft proposal with built-in self-review" (only when entry point is ProposalLoop AND topic is a new description; skip if existing task ID was given — its description is already in $TMPDIR/ftb-proposal.md):
Draft a technical proposal and immediately self-review it against all criteria.
Task ID: <TASK_ID>
-
Search the codebase to understand current architecture relevant to: <topic>
-
Write $TMPDIR/ftb-proposal.md:
# Proposal: <title>
## Background
(3-8 lines: WHY this feature is needed, what problem it solves)
## Goals
1. (concrete, verifiable outcome)
2. ...
## Proposed Approach
(High-level design: what to build, key components — no implementation code)
## Trade-offs and Risks
(What we are not doing, known risks, alternatives considered)
-
Self-review: check each criterion against the draft you just wrote:
- Motivation: Does Background explain WHY (not just WHAT)? Is it 3-8 lines?
- Goals: All numbered and concretely verifiable? No vague language?
- Feasibility: Does Approach align with the codebase? Search to verify.
- Completeness: Are trade-offs and risks identified?
- Consistency: No contradictions between sections?
If ANY criterion fails: fix the failing sections inline in $TMPDIR/ftb-proposal.md and
repeat self-review. Repeat up to 3 total rounds (initial draft + 2 revision rounds).
-
After all criteria pass (or after 3 rounds), update the task and stop:
backlog task edit <TASK_ID> \
--plan "$(cat $TMPDIR/ftb-proposal.md)" \
--status "Basic: Proposal"
echo "APPROVED" > $TMPDIR/ftb-proposal-verdict.txt
在写 APPROVED 时,把 verdict 扩展为结构化前提账本(premise-ledger):
对你检查的每条 criterion,追加一行 [E|C|H] <criterion>: <你判断所凭的前提>,
分类规则:E=该前提可直接从本任务 task 文件读到;C=须跳转外部 task/doc/parent 才能确认;
H=任何 artifact 都没有、靠背景知识或记忆推断。
末尾追加一行 GCL-self-report: E=<n> C=<n> H=<n>(各类计数)。
写法示例:
backlog task edit <TASK_ID> --append-notes "Proposal self-review: APPROVED
premise-ledger:
[E] background lines: 背景段行数从 proposal 文件直接数
[C] goal verifiability: 须对照每条 Goal 判断是否可验证
[H] feasibility 基准: 何为'可行'靠背景知识判断
GCL-self-report: E=1 C=1 H=1"
Immediately after writing the GCL-self-report note, append one line to docs/research/gcl-events.jsonl
(gate_type=proposal, task_kind=basic):
python3 -c "
import json, datetime
record = {
'task_id': '<TASK_ID>',
'gate_type': 'proposal',
'task_kind': 'basic',
'timestamp': datetime.datetime.utcnow().strftime('%Y-%m-%dT%H:%M:%SZ'),
'E': , 'C': , 'H': , 'GCL': ++,
'reviewer_model': 'claude-sonnet-4-6',
'sample_run_id': None,
'evidence_independence': 'low',
'gate_actor_type': 'llm',
'premise_lines':
}
with open('docs/research/gcl-events.jsonl', 'a') as f:
f.write(json.dumps(record) + '\n')
"
Replace `<TASK_ID>`, `<E>`, `<C>`, `<H>` with the actual values from the self-report.
**Reliability sampling (~10% of gates):** After appending the primary JSONL record above,
check whether this task falls in the ~10% reliability sample:
```python
import hashlib
if int(hashlib.md5('<TASK_ID>'.encode()).hexdigest(), 16) % 10 == 0:
If and only if the condition is true, perform a second independent GCL self-report pass:
If the condition is false, skip this block entirely (no second pass, no extra JSONL line).
Print: "Proposal APPROVED. Proceeding to plan draft."
Rules: Background must state WHY, not just WHAT. Each Goal must be verifiable.
No implementation phases or DoD commands in this document.
Phase 2: self-review (integrated into Phase 1b)
For new-description topics (ProposalLoop entry), proposal self-review is handled inside
the draftAndReview agent in Phase 1b — no separate review agent is spawned.
For existing task ID topics (PlanLoop entry), no proposal review is needed; proceed
directly to Phase 3 (draftPlan) or Phase 4 (planLoop).
Phase 3: draftPlan
If $TMPDIR/ftb-entry-point.txt contains PlanLoop: $TMPDIR/ftb-plan.md already holds the task's existing description as the plan draft — skip this phase and proceed directly to Phase 4.
backlog task edit $TASK_ID \
--status "Basic: Plan" \
--append-notes "Proposal approved. Starting plan draft."
Spawn Task agent (pass CFG_TEST_CMD, CFG_TEST_ALL, CFG_DOC_PATH as literal values):
Draft a TDD implementation plan and update the backlog task.
Task ID: <TASK_ID>
Test command (per phase): <CFG_TEST_CMD>
Full suite command: <CFG_TEST_ALL>
Doc root: <CFG_DOC_PATH>
-
Read the approved proposal from $TMPDIR/ftb-proposal.md
-
Search the codebase to identify exact file paths to create or modify.
-
Write $TMPDIR/ftb-plan.md:
# Plan: <title>
Proposal: <CFG_DOC_PATH>/proposals/proposal-<slug>.md
## Phase A: <title>
### Tests (write first)
(Test file paths and test case names to add; these must fail before implementation)
### Implementation
(Files to create or modify; code that makes the tests pass)
### DoD
- [ ] `<CFG_TEST_CMD> <test-file-or-pattern>` ← first item MUST use testCmd
- [ ] `<other verification command>`
## Phase B: <title>
### Tests (write first)
...
### Implementation
...
### DoD
- [ ] `<CFG_TEST_CMD> <test-file-or-pattern>`
- [ ] `<other verification command>`
## Constraints
(Non-executable criteria — goes here, NOT in DoD)
## Acceptance Gate
- [ ] `<CFG_TEST_ALL>` ← first item MUST be full suite
- [ ] `<final verification command>`
DoD rules (STRICT):
- Every
### DoD and ## Acceptance Gate item MUST be an executable shell command
(exit 0 = pass)
### Tests section MUST exist in every Phase — this is the TDD specification
- First
### DoD item MUST use <CFG_TEST_CMD> — proves red→green
- First
## Acceptance Gate item MUST be <CFG_TEST_ALL>
- Each Phase ≤ 200 lines of code change
- Absence check:
! grep -q <pattern> <file> (NOT grep -qv)
- Natural-language criteria →
## Constraints only, never in DoD
- Phases ordered so earlier phases produce what later phases need
-
Update task:
backlog task edit <TASK_ID> \
--plan "$(cat $TMPDIR/ftb-plan.md)" \
--status "Basic: Plan"
Phase 4: reviewLoop(plan)
Soft limit: 8 iterations. On exhaustion: move to Needs Human, print plan, stop.
Each iteration — spawn Task agent with run_in_background=true (self-chaining background agent;
pass CFG_TEST_CMD, CFG_TEST_ALL as literal values):
You are a strict software architect reviewing a TDD implementation plan.
Task ID: <TASK_ID> — Iteration: <N> — Max iterations: 8
Expected test command: <CFG_TEST_CMD>
Expected full suite: <CFG_TEST_ALL>
-
Read $TMPDIR/ftb-proposal.md and $TMPDIR/ftb-plan.md
-
Check each item:
- Goal coverage: Every proposal Goal addressed by at least one Phase or
Acceptance Gate item
- TDD structure: Every Phase has a
### Tests section AND
### Implementation section (in that order)
- TDD order: First
### DoD item uses <CFG_TEST_CMD> (proves red→green)
- Acceptance gate: First
## Acceptance Gate item is <CFG_TEST_ALL>
- DoD executability: All
### DoD and ## Acceptance Gate items are shell
commands. Flag natural-language items and move to ## Constraints
- Absence checks:
! grep -q pattern used, not grep -qv
- Phase ordering: Earlier phases produce what later phases need; no circular deps
- Scope discipline: No Phase implements something not backed by a Goal
- File paths: Referenced files exist in the codebase (search to verify)
3a. ALL pass:
backlog task edit <TASK_ID> \
--append-notes "Plan review iteration <N>: APPROVED"
echo "APPROVED" > $TMPDIR/ftb-plan-verdict.txt
在写 APPROVED 时,把 verdict 扩展为结构化前提账本(premise-ledger):
对你检查的每条 criterion,追加一行 [E|C|H] <criterion>: <你判断所凭的前提>,
分类规则:E=该前提可直接从本任务 task 文件读到;C=须跳转外部 task/doc/parent 才能确认;
H=任何 artifact 都没有、靠背景知识或记忆推断。
末尾追加一行 GCL-self-report: E=<n> C=<n> H=<n>(各类计数)。
写法示例:
backlog task edit <TASK_ID> --append-notes "Plan review iteration : APPROVED
premise-ledger:
[E] goal coverage: 3 goals 映射到 Phase A/B/C
[C] file paths exist: 引用的外部文件经 search 确认
[H] DoD 充分性基准: 何为'足够'靠背景知识判断
GCL-self-report: E=2 C=1 H=1"
Immediately after writing the GCL-self-report note, append one line to docs/research/gcl-events.jsonl:
python3 -c "
import json, datetime
record = {
'task_id': '<TASK_ID>',
'gate_type': 'plan',
'task_kind': 'basic',
'timestamp': datetime.datetime.utcnow().strftime('%Y-%m-%dT%H:%M:%SZ'),
'E': <E>, 'C': <C>, 'H': <H>, 'GCL': <E>+<C>+<H>,
'reviewer_model': 'claude-sonnet-4-6',
'sample_run_id': None,
'evidence_independence': 'low',
'gate_actor_type': 'llm',
'premise_lines': <count of premise-ledger lines>
}
with open('docs/research/gcl-events.jsonl', 'a') as f:
f.write(json.dumps(record) + '\n')
"
Replace <TASK_ID>, <E>, <C>, <H> with the actual values from the self-report.
Reliability sampling (~10% of gates): After appending the primary JSONL record above,
check whether this task falls in the ~10% reliability sample:
import hashlib
if int(hashlib.md5('<TASK_ID>'.encode()).hexdigest(), 16) % 10 == 0:
If and only if the condition is true, perform a second independent GCL self-report pass:
If the condition is false, skip this block entirely (no second pass, no extra JSONL line).
Print: "Plan APPROVED. Proceeding to finalise."
3b. ANY fail: fix $TMPDIR/ftb-plan.md (and $TMPDIR/ftb-proposal.md if needed),
update task plan:
backlog task edit <TASK_ID> --plan "$(cat $TMPDIR/ftb-plan.md)"
echo "NEEDS_REVISION" > $TMPDIR/ftb-plan-verdict.txt
If <N> < 8: spawn next iteration as background agent (run_in_background=true) with N+1 and STOP.
If <N> == 8: move to Basic: Needs Human, print plan, STOP.
After the first iteration is spawned (run_in_background=true), the orchestrator exits.
The background agent self-chains by spawning the next iteration on NEEDS_REVISION.
On APPROVED, the background agent proceeds directly to Phase 5 (finalise).
Phase 5: finalise
Run the following bash commands directly (no agent spawn needed — all steps are mechanical):
Step B — Write combined proposal+plan into task and add DoD:
grep -oP '(?<=- \[ \] `)[^`]+(?=`)' $TMPDIR/ftb-plan.md \
> $TMPDIR/ftb-dod-cmds.txt
DOD_ARGS=()
while IFS= read -r cmd; do
DOD_ARGS+=("--dod" "$cmd")
done < $TMPDIR/ftb-dod-cmds.txt
{
cat $TMPDIR/ftb-proposal.md
printf '\n\n---\n\n'
cat $TMPDIR/ftb-plan.md
} > $TMPDIR/ftb-combined.md
backlog task edit <TASK_ID> \
--plan "$(cat $TMPDIR/ftb-combined.md)" \
--status "Basic: Backlog" \
"${DOD_ARGS[@]}"
Step D — Run Layer 0-2 validation gate:
bash "$BAIME_SCRIPTS/validate-plugin.sh"
If validation fails, fix the SKILL.md contracts or internals before proceeding.
Add the following DoD items to the task:
backlog task edit <TASK_ID> \
--dod "bash "$BAIME_SCRIPTS/validate-plugin.sh"" \
--dod "grep -q 'contracts:' plugin/skills/<skill-slug>/SKILL.md"
Step E — Print completion:
✅ Task <TASK_ID> is now in Backlog.
两轮起草 + 两轮迭代审查已完成。
请在 web UI 审阅 Definition of Done 中的命令:
backlog browser --no-open --port 6421
确认无误后,将任务移入执行队列:
backlog task edit <TASK_ID> --status "Basic: Ready"
启动 L0 执行:
/loop-backlog
(print the standard completion message as text output)
Constraints
- Scope gate (epic vs basic): If the feature naturally decomposes into ≥2 independent
deliverables with ordering/dependencies and a combined acceptance, stop and use
/epic-to-backlog instead. A Basic Task is NOT small — its plan may have Phase + Stage
two levels and span thousands of LOC. Only reach for an epic when one worker in one
worktree genuinely cannot finish the whole goal. See
docs/proposals/proposal-epic-split-board.md § 颗粒度.
- This skill outputs docs and a backlog task only — it does not implement code
- No branch creation, no worktree operations, no git push, no PR creation
- One task per feature throughout; the same TASK_ID moves through all columns
- Phase count in generated plans: minimum 1, maximum 8
- Must run from the project root of a git repository
- Proposal and plan text live in the task's Implementation Plan field; no docs/ files are written