backlog

star 0

Owns `BACKLOG.json` — the project-level coordination file consumed by `/base:orient`, `/base:next`, `base:project-curator`, `/base:feature`, and `/base:bug`. Single authority for the file's format (`plugins/base/schemas/backlog.schema.json`) and the canonical write path (`scripts/`). Operations: `init` (scaffold + `CLAUDE.md` pointer + one-shot epic seeding), `add-finding`, `add-archive`, `mark-archive-adr`, `resolve <slug>`, `defer-stamp <slug>`, `add-epic`, `list`, `get`, `pick-next`, `render`, `query`, `migrate-v3` (one-shot v2-MD → v3-JSON conversion). Use when the user wants to bootstrap a backlog, append a finding outside a `/feature` or `/bug` run, close a finding inter-run, query backlog state, or migrate a legacy v2 BACKLOG.md. **All BACKLOG.json mutations from `/base:feature`, `/base:bug`, `/base:next`, `/base:triage`, `base:project-curator`, `triage` agent, `/base:orient`, and `/base:next-epic` MUST go through this skill via the `Skill` tool — direct shell-outs to the underlying scripts are a contr

941design By 941design schedule Updated 5/22/2026

name: backlog description: >- Owns BACKLOG.json — the project-level coordination file consumed by /base:orient, /base:next, base:project-curator, /base:feature, and /base:bug. Single authority for the file's format (plugins/base/schemas/backlog.schema.json) and the canonical write path (scripts/). Operations: init (scaffold + CLAUDE.md pointer + one-shot epic seeding), add-finding, add-archive, mark-archive-adr, resolve <slug>, defer-stamp <slug>, add-epic, list, get, pick-next, render, query, migrate-v3 (one-shot v2-MD → v3-JSON conversion). Use when the user wants to bootstrap a backlog, append a finding outside a /feature or /bug run, close a finding inter-run, query backlog state, or migrate a legacy v2 BACKLOG.md. All BACKLOG.json mutations from /base:feature, /base:bug, /base:next, /base:triage, base:project-curator, triage agent, /base:orient, and /base:next-epic MUST go through this skill via the Skill tool — direct shell-outs to the underlying scripts are a contract violation. user-invocable: true argument-hint: " [op-specific args] — ops: init | add-finding | enrich-finding | add-archive | mark-archive-adr | resolve | defer-stamp | add-epic | list | get | pick-next | render | query | migrate-v3" allowed-tools: Read, Bash, AskUserQuestion

Format authority

The canonical format of BACKLOG.json is defined by the JSON Schema at plugins/base/schemas/backlog.schema.json. All readers and writers cite that schema rather than restating the rules. Every write script in scripts/ validates against the schema before committing a mutation — the file cannot land in a malformed state.

The semantic policy that does not fit in a JSON Schema — resolution paths, scope axis, scale axis, deferred-reason semantics, tonality — lives in references/format.md.

Where the writes happen

All mutations go through scripts in ${CLAUDE_SKILL_DIR}/scripts/:

Operation Script Purpose
init init.sh Scaffold BACKLOG.json + CLAUDE.md pointer; seed epics from specs/epic-*/
add-finding add-finding.sh Append a finding with slug derivation + scope inference + collision handling
enrich-finding <slug> enrich-finding.sh In-place update of enrichment fields (motivation, behavior, approach, success_signal) on an existing finding
add-archive add-archive.sh Append a free-standing rejection entry to archive[] (no source finding)
mark-archive-adr mark-archive-adr.sh Tag existing archive entries with an ADR identifier when a rejection cluster has been promoted
add-epic add-epic.sh Append (or update) an epic entry
resolve <slug> resolve.sh Close a finding via done, done-mechanical, rejected, or promoted
defer-stamp <slug> defer-stamp.sh Set/clear findings[i].deferred from a worker abort
list list.sh Filter and print findings (compact, table, or JSON)
get <slug> get.sh Fetch a single finding
pick-next pick-next.sh Deterministic top-candidate selection for programmatic /base:next auto
render render.sh Terminal-friendly view (used by /base:orient)
query <jq> query.sh Arbitrary jq expression passthrough
migrate-v3 migrate-v3.sh One-shot v2-MD → v3-JSON conversion

Every script:

  • Refuses to act outside a git repository.
  • Uses atomic tmp+rename so concurrent runs cannot tear the file.
  • Validates against the schema after every write.
  • Stamps updated_at with an ISO 8601 timestamp.
  • Requires jq (a hard dependency; checked at startup).

Consumers (the curator, /base:bug defer-stamp step, /base:feature epic creation, /base:next un-stamp / resolve dispatch, /base:triage's agent for retro→backlog promotion, /base:orient render, /base:next-epic registered-epic read) invoke this skill via the Skill tool — they do not shell out to the underlying scripts directly. Direct shell-outs to plugins/base/skills/backlog/scripts/*.sh from outside this skill are a contract violation: the relative path only resolves when cwd is the claude-plugins source repo, so consumer-project invocations silently fail and the workflow falls back to a raw Edit that bypasses schema validation and atomic-write guarantees. Direct Edit of BACKLOG.json from outside this skill is the same violation by another path. The schema, atomic-write, and validation guarantees only hold when every writer takes the Skill("base:backlog", args: "<op> …") path.


Dispatch

Read $ARGUMENTS. The first whitespace-separated token is the operation; the rest are op-specific arguments passed through to the script. Run the matching script with bash, capture output, surface to the user.

init                                  → scripts/init.sh
add-finding [args...]                 → scripts/add-finding.sh [args...]
enrich-finding <slug> [args...]       → scripts/enrich-finding.sh <slug> [args...]
merge-findings [args...]              → scripts/merge-findings.sh [args...]
split-finding [args...]               → scripts/split-finding.sh [args...]
add-archive [args...]                 → scripts/add-archive.sh [args...]
mark-archive-adr [args...]            → scripts/mark-archive-adr.sh [args...]
add-epic [args...]                    → scripts/add-epic.sh [args...]
resolve <slug> [args...]              → scripts/resolve.sh <slug> [args...]
defer-stamp <slug> [args...]          → scripts/defer-stamp.sh <slug> [args...]
list [args...]                        → scripts/list.sh [args...]
get <slug> [args...]                  → scripts/get.sh <slug> [args...]
pick-next [args...]                   → scripts/pick-next.sh [args...]
render [args...]                      → scripts/render.sh [args...]
query <jq-expr>                       → scripts/query.sh <jq-expr>
migrate-v3 [args...]                  → scripts/migrate-v3.sh [args...]
anything else (or empty)              → list ops with one-line summaries, exit

The skill is a thin dispatcher: it does not encode validation, format rules, or routing in prose. Each script is self-documenting via --help and set -e-driven error surfaces. When the user wants to know what arguments an op takes, run the script with --help.


Operation summaries

init

Idempotent bootstrap. Creates BACKLOG.json if missing, seeds epics[] from existing specs/epic-*/ directories on first creation, adds the project-root CLAUDE.md pointer. Reports one of four outcomes: "Initialized", "Repaired: …", "Already initialised".

add-finding

Interactive when invoked with no args. With args, takes --text "<one-line text>" (required), --anchor <path[:line]|path:N-M|-> (required), --motivation "<why this matters>" (required), --behavior "<what currently happens>" (required), --approach "<how to address it>" (optional), --success-signal "<short observable that confirms resolution>" (optional), --scope <X> (optional; inferred from anchor when omitted), --slug <slug> (optional; derived from text when omitted), --created <YYYY-MM-DD> (optional; defaults to today), --evidence <retro-path>:<finding-anchor> (optional, repeatable; each appends one entry to the new finding's evidence[]), --evidence-captured-at <YYYY-MM-DD> (optional; defaults to --created when omitted; triage callers pass the source retro's completed: date here so evidence is dated to when the observation was made, not when triage promoted it), --kind <enum-value> (optional; closed enum from finding.kind in the backlog schema — currently only spec-gap, set by base:mutation-testing / base:property-based-testing when a Real-gap surviving mutant maps to no acceptance criterion).

--text is the one-line headline used for slug derivation, list rendering, and terminal display. --motivation/--behavior/--approach/--success-signal are the structured enrichment context — never collapse one into the other.

For interactive use, prompt the user via AskUserQuestion for text and anchor, then call add-finding.sh with the collected args. Tonality rules and slug-derivation refusals are enforced by the script.

enrich-finding <slug>

In-place update of enrichment fields on an existing finding. All four enrichment flags are optional; supplying none is a no-op (exit 0). Only the supplied fields are written — unsupplied fields are left unchanged. Identity fields (slug, anchor, text, created_at, scope, deferred) are immutable and explicitly refused (exit 1 if attempted).

enrich-finding <slug> --motivation "<why this finding matters>"
enrich-finding <slug> --behavior "<what currently happens>"
enrich-finding <slug> --approach "<how to address it>"
enrich-finding <slug> --success-signal "<short observable confirming resolution>"
enrich-finding <slug> --motivation "..." --behavior "..." --approach "..."
enrich-finding <slug> --add-evidence <retro-path>:<finding-anchor> [...] [--evidence-captured-at <YYYY-MM-DD>]

--add-evidence is append-only and repeatable. Each value is <retro_path>:<finding_anchor>; captured_at defaults to today, or to --evidence-captured-at when supplied (triage passes the source retro's completed: date). Duplicate entries (same retro_path + finding_anchor + captured_at) are silently deduped.

Exit codes: 0 success (including no-op); 1 invalid input (unknown or identity-field flag); 3 slug not found (BACKLOG.json is not modified).

Used by /base:next Step 3.5 when finding-researcher (Mode A) returns enrichment data for a thin finding selected for dispatch, and by the triage agent on the ENRICHED outcome.

merge-findings

Collapse N findings into one survivor. Used by the triage agent on the MERGED outcome when a retro item reveals that two or more existing findings describe the same root issue from different angles.

merge-findings --into <survivor-slug> --from <absorbed-slug> [--from <absorbed-slug> ...]

Behavior:

  • Union of all evidence entries (deduped by composite key) lands on the survivor's evidence[].
  • The absorbed slugs themselves (plus any prior superseded_slugs[] they carried) are appended to the survivor's superseded_slugs[], deduped.
  • Soft fields (motivation, behavior, approach, success_signal) carry over from an absorbed finding to the survivor only when the survivor lacks the field AND exactly one absorbed value exists for it. Conflicting absorbed values leave the survivor's field absent — the caller is expected to rewrite via enrich-finding afterwards.
  • The survivor's identity (slug, anchor, text, created_at, scope, deferred) is never modified.
  • Absorbed findings are removed from findings[] atomically with the survivor update.

Exit codes: 0 success; 1 invalid input (missing flags, unknown slug, self-merge); 2 schema violation in proposed mutation.

split-finding

Split one finding into N children. Used by the triage agent on the SPLIT outcome when a retro item reveals that an existing finding has been carrying two-or-more distinct problems under one name.

split-finding --from <parent-slug> \
  --into-text "<child-1 text>" --into-anchor <path-or-->|<path:line>|<path:N-M> \
  --into-text "<child-2 text>" --into-anchor <...> \
  [--into-text "..." --into-anchor "..." ...]

Behavior:

  • Each child inherits the parent's scope; created_at is today.
  • Each child's slug is derived from its text (with collision suffix), and the parent's slug becomes the first entry in each child's superseded_slugs[] (any prior superseded_slugs[] on the parent are also carried into each child).
  • The parent's evidence[] is copied in full to the FIRST child; subsequent children start with empty evidence[]. Callers who need a different distribution should reattach evidence via enrich-finding --add-evidence after the split.
  • The parent's soft fields are NOT carried into the children — the split implies distinct semantics, and the caller is expected to re-articulate each child via enrich-finding.
  • The parent finding is removed from findings[] atomically with the child insertions.

Exit codes: 0 success (one child slug per line on stdout, in order); 1 invalid input (missing flags, unknown parent slug, fewer than two child pairs); 2 slug derivation failed for one of the child texts.

add-archive

Append a free-standing rejection entry to archive[]. Used by base:project-curator's append_rejection action when the rejection has no in-flight source finding to close — pure shelf-aging knowledge capture. Takes --text "<one-line rejected approach>" (required), --reason "<why rejected>" (required), optional --date <YYYY-MM-DD> (defaults to today).

For rejected-path closure of an existing finding, prefer resolve <slug> --as rejected --reason "..." — the resolve script does the archive append plus finding removal atomically.

mark-archive-adr

Tag one or more existing archive entries with an ADR identifier. Used by base:project-curator's promote_rejections_to_adr action after Skill("base:adr", ...) has created the supersession ADR. Takes --adr ADR-NNN (required) and one or more --marker "<text>" arguments identifying the archive entries by substring match.

add-epic

Append or update an entry in epics[]. Takes --path <specs/epic-foo/>, --status <PLANNED|IN_PROGRESS|DONE|ESCALATED|UNKNOWN>, optional --next-action "...".

resolve <slug>

Close a finding via one of four paths:

resolve <slug> --as done-mechanical                    # remove only
resolve <slug> --as done --target <spec-path>          # remove; caller amends spec
resolve <slug> --as rejected --reason "..."            # remove + archive
resolve <slug> --as promoted --target <spec-path>      # remove; caller creates epic

The interactive form (just resolve <slug>) prompts the user via AskUserQuestion for which path applies and gathers the missing arguments, then re-invokes the script with --as.

For done and promoted, the spec amendment / epic creation is the caller's responsibility. The script only mutates BACKLOG.json.

defer-stamp <slug>

Set or clear findings[i].deferred. Used by /base:bug and /base:feature workers when they abort with ABORT:DEFERRED:<reason>:<detail>. The structured field replaces the v2 markdown stamp (a positional [DEFERRED:…] prefix in the bullet text).

defer-stamp <slug> --reason <ENUM> --detail "<text>" [--stamped-at YYYY-MM-DD]
defer-stamp <slug> --clear

Reason enum: spec-gap | already-resolved | escalated | arch-debate-required | legacy-orphan.

list

Filter and print findings.

list [--status open|deferred|all] [--scope <X>] [--scope-prefix <X>]
     [--format compact|table|json] [--include-archive]

Defaults: --status open --format compact. The --scope-prefix form matches the scope token OR the anchor.path prefix — useful for "show me everything targeting plugins/base/".

get <slug>

Single-finding lookup. --field <name> to print one field as a raw string.

pick-next

Deterministic top-candidate selector for programmatic dispatch.

pick-next [--scope <X>] [--scope-prefix <X>] [--include-deferred]
          [--format slug|json]

Selection: scope filter → exclude deferred → sort by created_at ascending → first. Exit code 1 with "No actionable findings." when the filter is empty.

This is the entrypoint a non-LLM /base:next auto driver uses. The interactive form of /base:next may still rank with prose reasoning.

render

Terminal-friendly view.

render --format orient            # full picture (epics + findings + archive)
render --format short             # one-line summary (counts)

/base:orient consumes render --format orient.

query <jq-expr>

Thin passthrough for ad-hoc queries. The expression runs against the full BACKLOG.json document.

migrate-v3

One-shot v2-MD → v3-JSON conversion.

migrate-v3                # ./BACKLOG.md → ./BACKLOG.json, then `git rm BACKLOG.md`
migrate-v3 --dry-run      # emit to stdout, don't write
migrate-v3 --keep-md      # write JSON, don't delete BACKLOG.md
migrate-v3 --force        # overwrite an existing BACKLOG.json

Idempotent. Auto-invoked by /base:orient and /base:next on detection of a v2 BACKLOG.md without a corresponding BACKLOG.json.


Non-goals (skill-wide)

  • No promotion to epic. Promotion is /base:feature backlog:<slug>'s job — that flow scaffolds the spec dir, calls resolve.sh --as promoted to remove the finding, and calls add-epic.sh to register the new epic.
  • No batch operations. One finding per add-finding or resolve invocation. Batching reintroduces silent-edit failure modes.
  • No archive entry for done, done-mechanical, or promoted. Only rejected writes to archive[]. Promotions and resolutions live in the spec or in git, not in the rejection log.

Relationship to other components

  • base:project-curator — the autonomous write-side at the end of /feature / /bug. Invokes Skill("base:backlog", args: "resolve <slug> …"), Skill("base:backlog", args: "add-epic …"), and Skill("base:backlog", args: "mark-archive-adr …") — never the underlying scripts and never Edit on BACKLOG.json.
  • triage agent — the retro → backlog promotion side, invoked once per /base:triage run. Invokes Skill("base:backlog", args: "add-finding …"), Skill("base:backlog", args: "enrich-finding <slug> …"), Skill("base:backlog", args: "merge-findings …"), and Skill("base:backlog", args: "split-finding …") — the four write surfaces by which retro evidence reaches BACKLOG.
  • /base:orient — read-side renderer. Consumes render --format orient. Auto-invokes migrate-v3 on detection of a v2 BACKLOG.md.
  • /base:next — dispatch driver. Auto mode calls pick-next; interactive mode reads list --format json and ranks with prose reasoning. Auto-invokes migrate-v3 on detection of a v2 BACKLOG.md.
  • /base:bug and /base:feature workers — when aborting with ABORT:DEFERRED:<reason>:<detail>, call defer-stamp.sh to record the structured defer state.
  • /base:feature backlog:<slug> — the promotion path. Calls resolve.sh --as promoted to atomically remove the source finding, then add-epic.sh to register the new epic.
Install via CLI
npx skills add https://github.com/941design/claude-plugins --skill backlog
Repository Details
star Stars 0
call_split Forks 0
navigation Branch main
article Path SKILL.md
More from Creator