# Armature Planner

> Use when creating a new story or epic — translates objectives into a well-structured DAG of actionable work. Covers dag apply (with dry-run), dag transition, source registration, dependency linking, and validation before releasing work to workers.

- Skill: `scullxbones/armature-planner` (Agent Skill, multi-file: 3 files)
- Install (CLI): `npx skillmds@latest add scullxbones/armature-planner`
- Raw SKILL.md: https://api.skillmd.com/api/skills/scullxbones/armature-planner/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: scullxbones (https://skillmd.com/u/scullxbones)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/scullxbones/armature-planner

---


# Armature Planner Loop

The Planner translates objectives and specifications into a well-structured DAG
of actionable work. The output is a validated, cited, dependency-resolved set of
issues ready for workers to claim.

## Prerequisites

- If `arm` is not found, stop and resolve this before proceeding.
- **Do NOT run `arm worker-init`** — the Planner does not require a worker
  identity. Skip that step entirely.
- Have a source document, spec, or design doc before you start. Every issue you
  create must be citable. If no source exists yet, write one first or be
  prepared to use `arm sources accept-citation` with a clear rationale.

## DAG Hygiene Mandate

**`arm validate` and `arm doctor` must exit clean at all times.** This is non-negotiable.

Before releasing any plan to the Coordinator and after every decomposition, run:
```bash
arm validate       # zero ERRORs; all issues cited
arm doctor        # zero errors; no broken refs, orphaned ops, or cycles
```

If either exits non-zero, fix the reported issues before releasing. Treat DAG decay the same way you treat failing tests — it is a blocker, not a warning to ignore.

Warnings from other stories must be resolved, not ignored. If `arm doctor` reports a D1 (commits referencing non-done issues) or D2 (stale claims) from unrelated work, clean them up before planning your work. DAG health is cumulative.

---

## The Planner Loop

```dot
digraph planner_loop {
    "Start: objective/spec" [shape=box];
    "Single task?" [shape=diamond];
    "arm create" [shape=box];
    "Write plan.json" [shape=box];
    "dag apply --dry-run" [shape=box];
    "OK?" [shape=diamond];
    "dag apply --plan plan.json" [shape=box];
    "dag transition" [shape=box];
    "sources add/sync/verify" [shape=box];
    "sources link / sources accept-citation" [shape=box];
    "arm link (deps)" [shape=box];
    "arm validate" [shape=box];
    "arm doctor" [shape=box];
    "Release to Coordinator" [shape=doublecircle];

    "Start: objective/spec" -> "Single task?";
    "Single task?" -> "arm create" [label="yes"];
    "Single task?" -> "Write plan.json" [label="no"];
    "arm create" -> "sources add/sync/verify";
    "Write plan.json" -> "dag apply --dry-run";
    "dag apply --dry-run" -> "OK?" ;
    "OK?" -> "Write plan.json" [label="fix errors"];
    "OK?" -> "dag apply --plan plan.json" [label="yes"];
    "dag apply --plan plan.json" -> "dag transition";
    "dag transition" -> "sources add/sync/verify";
    "sources add/sync/verify" -> "sources link / sources accept-citation";
    "sources link / sources accept-citation" -> "arm link (deps)";
    "arm link (deps)" -> "arm validate";
    "arm validate" -> "arm doctor";
    "arm doctor" -> "Release to Coordinator";
}
```

## Step-by-Step

### 1. Register Sources First

Register source documents **before** creating issues. This lets you link issues
at creation time rather than doing a remediation pass later.

```bash
arm sources add --url path/to/spec.md --title "Feature Spec" --type filesystem
arm sources sync       # fetch and fingerprint all registered sources
arm sources verify     # confirm all show OK (not MISSING)
```

If `arm sources verify` shows MISSING entries, re-run `arm sources sync` until
they resolve. Do not proceed with issue creation while sources are MISSING.

### 2. Create or Decompose

**For a single task:**
```bash
arm create --title "Task title" --type task --parent STORY-ID
```

Valid types: `task`, `feature`, `bug`, `story`

**For a full decomposition (most common):**

See `references/decompose-apply.md` for the full dag apply workflow.

### 3. Promote from Draft

After `dag apply`, all created issues are in `draft` state. Promote them
so workers can see them:

```bash
arm dag transition --issue ROOT-ID   # promotes ROOT-ID and all children draft → verified
```

Workers cannot claim draft issues. Do not skip this step.

### 4. Link Issues to Sources

Every issue must be cited before `arm validate` will pass.

```bash
# Link each issue to a registered source
arm sources link --issue ISSUE-ID --source-id UUID

# If no source document exists for this issue
arm sources accept-citation --issue ISSUE-ID --rationale "No external spec; requirements captured in issue body" --ci
```

Do this at creation time — not as a post-hoc remediation pass. Citation debt
accumulates silently and blocks validation.

### 5. Resolve Dependencies

Identify scope overlaps and set blocking dependencies before releasing work.

```bash
arm link --source A --dep B    # A is blocked_by B; A runs after B completes
arm validate                   # scope overlap WARNINGs appear here; resolve each one
```

### 6. Validate and Release

```bash
arm validate --ci   # must exit 0 with no ERRORs; scope overlaps resolved
arm doctor          # repo health check (D1-D6); fix any errors
arm list --group    # final sanity check — all issues visible and in expected states
```

Only release to the Coordinator after both commands are clean.

---

## Writing Good Plan JSON

This section is critical. **Every issue in the plan MUST have a `source` (source
entry ID) or `arm dag apply` will refuse the plan.** Apply is source-atomic:
each create is emitted with its source-link in the same batch. **Every task
MUST have `dod`, `scope`, and `acceptance` fields or Plan Release (`dag
transition` / `confirm`) will fail.** Validate the plan JSON against
[the plan schema](https://github.com/scullxbones/armature/blob/main/docs/schemas/plan.schema.json) before submitting; see `docs/json-schema-examples.md`
for worked examples.

### The Three Mandatory Fields

**`dod` — Definition of Done**

Describes what "complete" looks like. Must be concrete and verifiable by the
worker without asking the Planner. **Limited to 500 characters** (E9 validation error
if exceeded). Summarize the outcome in the DoD; place extended requirements in
the `notes` array instead.

- Good: `"The parser handles all five token types defined in spec §3.2 and returns typed AST nodes. All existing tests pass and new unit tests cover the added branches."`
- Bad: `"Done when it works"` — vague, not verifiable
- Bad: `"Implement the feature"` — restates the title, adds no information
- Bad: Long DoD over 500 chars — summarize and move details to `notes`

**`scope` — Files Affected**

Lists the specific files this task modifies. Use the `(new)` suffix for files
that do not yet exist. Use precise paths, not vague descriptions.

- Good: `"cmd/parse/main.go, internal/ast/node.go (new), internal/ast/node_test.go (new)"`
- Bad: `"the parser files"` — worker cannot determine what to touch
- Bad: `"internal/"` — too broad, enables scope collisions

**`acceptance` — Verifiable Criteria**

JSON array of specific criteria the worker can verify mechanically. Each entry
should name a test, a command output, or an observable behavior.

**Spec traceability:** Name new tests using `Test<Description>_REQ_<RequirementID>`,
where `RequirementID` is the story or task ID (e.g. `STORY-T1`). This makes the
test visible to `make trace-report` and ties it back to the requirement that
motivated it. Use this pattern for every acceptance criterion that corresponds to
a new test function.

- Good: `["TestParseTokenTypes_REQ_STORY_T1 passes", "make check green", "arm validate exits 0"]`
- Bad: `["TestParseTokenTypes passes"]` — test name won't appear in `make trace-report`
- Bad: `[]` — empty array provides no acceptance signal
- Bad: `["looks good"]` — not mechanically verifiable

See `docs/conventions.md` (test naming and traceability section) in the armature repo for comprehensive documentation of test naming and all other naming conventions.

**`notes` — Optional Free-Text Notes**

JSON array of strings (`[]string`) containing optional extended notes or guidance
for the worker. Use `notes` to provide context that does not fit in `dod` or
`acceptance`, or to reference external docs. Initialize as `[]` (empty array)
if not needed.

- Good: `["See RFC-2019-auth for security requirements", "Coordinate with infra team on deployment"]`
- Good: `[]` — empty array if no additional notes
- Bad: Using `notes` to store what should be in `dod` or `acceptance`

### Complete Well-Formed Task Example

> **WARNING:** The plan JSON must be wrapped in the required `{ "version": 1, "title": "...", "issues": [...] }` top-level structure. Omitting the wrapper or using an unsupported plan version will cause `arm dag apply` to fail with a validation error.

```json artifact_type=plan
{
  "version": 1,
  "title": "Example Decomposition Plan",
  "issues": [
    {
      "id": "STORY-001",
      "title": "User authentication story",
      "type": "story",
      "scope": "",
      "priority": "",
      "source": "00000000-0000-0000-0000-000000000001",
      "dod": "Decomposition plan for the story is created, reviewed, and passes arm validate",
      "parent": "",
      "blocked_by": null,
      "notes": [],
      "acceptance": [
        "Decomposition plan created for STORY-001",
        "All child tasks have dod, scope, and acceptance fields",
        "arm validate passes with no errors"
      ]
    },
    {
      "id": "TASK-001",
      "title": "Implement login endpoint",
      "type": "task",
      "scope": "internal/auth/login.go (new)",
      "source": "00000000-0000-0000-0000-000000000001",
      "context_files": [
        "docs/auth-architecture.md"
      ],
      "priority": "high",
      "dod": "Login endpoint returns JWT on valid credentials",
      "parent": "STORY-001",
      "blocked_by": [],
      "notes": [],
      "acceptance": [
        "Implementation complete per dod",
        "TestImplementLoginEndpoint_REQ_TASK_001 passes",
        "make check green"
      ]
    },
    {
      "id": "TASK-002",
      "title": "Write login integration tests",
      "type": "task",
      "scope": "internal/auth/login_test.go (new)",
      "source": "00000000-0000-0000-0000-000000000001",
      "priority": "medium",
      "dod": "Integration tests cover happy path and error cases",
      "parent": "STORY-001",
      "blocked_by": [
        "TASK-001"
      ],
      "notes": [],
      "acceptance": [
        "Implementation complete per dod",
        "TestWriteLoginIntegrationTests_REQ_TASK_002 passes",
        "make check green"
      ]
    }
  ]
}
```

### Anti-Patterns to Avoid

| Anti-pattern | Problem | Fix |
|---|---|---|
| `"dod": "done when it works"` | Not verifiable | Describe the specific outcome |
| `"scope": "various files"` | Worker cannot self-scope | List every file path explicitly |
| `"acceptance": []` | No pass/fail signal | Name at least one test or command |
| `"scope": "internal/"` | Too broad, causes overlaps | Name the specific files |
| Missing `source` field | `arm dag apply` refuses the plan | Add the source entry ID; apply is source-atomic |
| Missing `acceptance` field entirely | Plan Release / `arm validate` ERRORs | Add the field, even if `--example` omits it |
| Plan without `version: 1, title, issues` wrapper | `arm dag apply` fails validation; bare task objects not accepted | Wrap all issues in `{ "version": 1, "title": "...", "issues": [...] }` |
| `"TestFoo passes"` in acceptance | Test skips `make trace-report`; requirement has no traceability | Use `TestFoo_REQ_STORY_TX passes` |

> **Note:** `arm dag apply --example` omits `acceptance` in its output.
> Always add it manually to every task in your plan JSON.

---

## Source Registration

Every issue must have a citation before `arm validate` passes. The two paths:

### Path A: Source document exists

```bash
# 1. Register the source (do this before creating issues)
arm sources add --url docs/design/feature-spec.md --title "Feature Spec" --type filesystem

# 2. Sync to fingerprint it
arm sources sync

# 3. Verify it shows OK
arm sources verify

# 4. Link each issue (get UUID from sources verify output)
arm sources link --issue ISSUE-ID --source-id UUID
```

### Path B: No source document exists

```bash
arm sources accept-citation --issue ISSUE-ID --rationale "Requirements captured in issue body; no external spec exists" --ci
```

To bulk-cite multiple issues at once, pass `--issue` multiple times:
```bash
arm sources accept-citation --issue A --issue B --issue C --rationale "same rationale applies to all" --ci
```

`sources link` also accepts multiple issues in one invocation. Use bulk forms to
reduce citation debt in large plan loads.

Use a specific rationale — vague rationales like "no docs" are harder to audit
later.

### Rules

- Register sources **before** creating issues, not after.
- Do not leave any issue uncited. Check coverage with `arm validate`.
- If `arm validate` reports `uncited node: ID`, either `sources link` the
  issue or use `sources accept-citation` on that issue before releasing to workers.
- If `arm validate` reports `unknown source: UUID`, the source UUID is not in
  the manifest — re-run `arm sources sync` then `arm sources verify`.

For dependency linking and overlap resolution, see `references/dependency-management.md`.

---

## Release Checklist

Run this checklist before handing work off to the Coordinator.

1. **`arm validate`** — no ERRORs, citation coverage complete
   ```bash
   arm validate --ci   # exits non-zero on any error
   ```
   **Note:** If `arm validate` reports `context_files` WARNINGs, treat them as decomposition
   signals—break large tasks into smaller subtasks or add blocking dependencies to reduce
   context size. Re-run until no context_files WARNINGs remain.

2. **`arm doctor`** — repo health checks D1-D6 pass
   ```bash
   arm doctor          # or arm doctor --strict (warnings as errors)
   ```

3. **All issues promoted from draft**
   ```bash
   arm list --group    # no issues should appear in draft state
   ```

4. **All issues cited** — `arm validate` output shows `COVERAGE: N/N cited`

5. **Dependencies correct** — no scope overlap WARNINGs in `arm validate`

6. **Priorities set** — review `arm list --group` to confirm priorities reflect
   intended execution order

7. **Spec traceability check** — confirm acceptance criteria use `_REQ_` naming
   ```bash
   make trace-report   # lists which requirements have tagged tests; gaps mean missing _REQ_ names
   ```
   If a task's acceptance criterion names a test function, that function should
   appear in the `make trace-report` output once the worker delivers it. If it
   does not, the acceptance criterion name is missing the `_REQ_` suffix.

Do not release until all seven checks pass.

---

## Common Failure Modes

| Failure | Symptom | Prevention |
|---|---|---|
| Tasks missing `dod`, `scope`, or `acceptance` | Workers cannot self-verify completion; `arm validate` ERRORs | Write all three fields for every task; use the complete example in this skill as a template |
| Issues created without source links | `arm validate` reports `uncited node: ID`; citation debt accumulates silently | Register sources first; `sources link` every issue at creation time |
| Scope overlaps not resolved with `arm link` | Workers collide on the same files; merge conflicts during story close | Run `arm validate` after dag apply; resolve every scope overlap WARNING before releasing |
| context_files WARNINGs not addressed | `arm validate` reports context_files WARNINGs, indicating tasks exceed context budget | Treat context_files WARNINGs as decomposition signals; break large tasks into smaller subtasks or add blocking dependencies; re-run `arm validate` until clear |
| Draft issues not promoted | Workers see an empty ready queue; work never starts | Always run `arm dag transition --issue ROOT-ID` after `dag apply` |

---

## Quick Reference

```bash
# Single issue creation
arm create --title "X" --type task --parent STORY-ID

# Decomposition
arm dag apply --example                         # inspect schema
arm dag apply --plan plan.json --dry-run        # preview without writing; iterate here
arm dag apply --plan plan.json                  # apply the plan (source-atomic; Introduction check)

# Draft promotion
arm dag transition --issue ROOT-ID                    # promote root + all children

# Source management
arm sources add --url PATH --title "TEXT" --type filesystem
arm sources sync                                      # fetch and fingerprint
arm sources verify                                    # confirm all show OK
arm sources link --issue ID --source-id UUID           # link issue to source
arm sources accept-citation --issue ID --rationale "..." --ci # accept risk (no source)

# Dependency management
arm link --source A --dep B                           # A runs after B
arm unlink --source A --dep B                         # remove dependency

# Validation
arm validate                                          # graph + citation check
arm validate --ci                                     # exit non-zero on errors
arm doctor                                            # repo health check
arm doctor --strict                                   # warnings as errors
arm list --group                                      # grouped by status
arm list --parent STORY-ID                            # tasks under a story

# Scope maintenance (after refactoring renames or deletions)
arm scope-rename OLD-PATH NEW-PATH        # rename path/prefix across all scopes
arm scope-delete PATH                    # remove exact path from all scopes
```

