Armature coordinator
Skill scullxbones/armature/internal/skillsembed/skills/armature-coordinator
Use when operating orchestration in an armature-managed repository — surveys the story DAG, dispatches workers wave by wave, integrates outcomes, validates citation coverage, and closes stories with a pull request. Requires a worker identity (arm worker-init) and arm on PATH.From its SKILL.md
npx -y skills add scullxbones/armature --skill armature-coordinatorAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
2 things to look at
- no licenseNo license file was found in the repository. Code published without one is not open source by default, so using it at work is a question for whoever answers licensing questions where you are.
- 2 stars2 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
SKILL.md
31.8 KB, ~7.7k tokens by cl100k_base, as published. Nobody here has run it
Armature Coordinator
The coordinator manages execution flow — it does not implement features itself. Its job is to survey the story DAG, dispatch workers for each wave of ready tasks, and close the story when all tasks are done.
Prerequisites
-
If
armis not found, stop and resolve this before proceeding. -
Worker identity required. Run
arm worker-initonce per clone before claiming any tasks:arm worker-init --check || arm worker-initarm claimcallsresolveWorkerAndLog, which fails with "worker not initialized" if no worker ID is set in git config. -
Understand the story DAG before dispatching. Run:
arm list --parent STORY-ID # all tasks + statuses arm list --status blocked # diagnose any blockers arm doctor # repo health checkFix any
doctorerrors before claiming work.
DAG Hygiene Mandate
arm validate and arm doctor must exit clean at all times. This is non-negotiable.
Before dispatching any worker and after each wave completes, run:
arm validate # zero ERRORs; all issues cited
arm doctor # zero errors; no broken refs, orphaned ops, or cycles
If either exits non-zero, stop. Fix the reported issues before proceeding. 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 starting your coordination wave. DAG health is cumulative.
The Coordinator Loop
digraph coordinator_loop {
"arm ready" [shape=box];
"Empty?" [shape=diamond];
"Parallel?" [shape=diamond];
"Sequential wave" [shape=box];
"Parallel wave" [shape=box];
"Claim + render-context all" [shape=box];
"dispatch workers" [shape=box];
"wait + integrate" [shape=box];
"arm validate" [shape=box];
"transition story" [shape=box];
"push + PR" [shape=box];
"Done" [shape=doublecircle];
"arm ready" -> "Empty?";
"Empty?" -> "arm validate" [label="yes — all done"];
"Empty?" -> "Parallel?" [label="no"];
"Parallel?" -> "Sequential wave" [label="deps between tasks"];
"Parallel?" -> "Parallel wave" [label="independent tasks"];
"Sequential wave" -> "dispatch workers";
"Parallel wave" -> "Claim + render-context all";
"Claim + render-context all" -> "dispatch workers";
"dispatch workers" -> "wait + integrate";
"wait + integrate" -> "arm ready";
"arm validate" -> "transition story";
"transition story" -> "push + PR";
"push + PR" -> "Done";
}
Step-by-Step
1. Survey the Story and Create a Feature Branch
arm list --parent STORY-ID
arm doctor
git checkout -b feat/STORY-ID # create the story branch NOW, before any worker is dispatched
Identify which tasks are open and which have blocked_by dependencies. Group
tasks into waves — tasks within the same wave have no dependencies on each other
and can run in parallel. Tasks in different waves must run sequentially.
Create the feature branch before dispatching any worker. This is the shared
story branch, but workers do not commit to it directly: each worker commits to
its own per-task branch (task/TASK-ID) in an isolated worktree created by
arm claim --worktree (see Dispatch Protocol steps 4-5). The coordinator later
merges each completed task branch into feat/STORY-ID (see "After Workers
Return", section b). If the story branch does not exist before dispatch, there
is nothing for the coordinator to merge task branches into, and the story
cannot be reviewed via PR.
2. Find Ready Work
arm ready # unblocked, unclaimed tasks
If arm ready returns nothing and not all tasks are done, check for
dependency cycles or stalled in-progress tasks:
arm ready --explain # why each open task is NOT ready (blocked/claimed/missing dep)
arm list --status in-progress # claims that may have expired
arm list --status blocked # diagnose blockers
arm ready --explain prints a per-task diagnosis for every open task that
did not make it into the ready queue. Use it as the first step whenever the
queue looks unexpectedly empty.
3. Record Wave Manifest
Before dispatching any worker, record the wave manifest so the verification gate has a stable baseline to diff against:
WAVE_TASK_IDS="TASK-A TASK-B ..." # exact IDs in dispatch order
WAVE_BASE_SHA=$(git rev-parse HEAD) # commit HEAD at wave start
WAVE_BRANCH=$(git rev-parse --abbrev-ref HEAD) # story feature branch
# Classify wave type (determines which verification profile to run)
WAVE_TYPE=docs-skill-only # default; promoted below if code files present
Wave type auto-promotion rule: inspect the ready-task scope fields. If any
task touches files matching *.go, go.mod, go.sum, Makefile, cmd/**,
or internal/** outside of internal/skillsembed/, set WAVE_TYPE=code.
A wave is docs-skill-only only when every changed file is a SKILL.md,
references/*.md, or other non-compiled documentation.
# Collect scope files from arm render-context output for each task in WAVE_TASK_IDS,
# or use `git diff --name-only "$WAVE_BASE_SHA"..HEAD` after workers return.
# Example: auto-promote based on task scope fields before dispatch:
WAVE_SCOPE_FILES=$(arm ready --parent STORY-ID --format json | python3 -c "import sys,json; [print(f) for t in json.load(sys.stdin) for f in t.get('scope',[])]")
if echo "$WAVE_SCOPE_FILES" | grep -E '\.(go|mod|sum)$' | grep -q . || \
echo "$WAVE_SCOPE_FILES" | grep -E '^(Makefile|cmd/|internal/)' | grep -qvE 'internal/skillsembed'; then
WAVE_TYPE=code
fi
4. Dispatch Workers
For each wave of ready tasks:
-
Claim and get context for each task:
arm claim TASK-ID --ttl 120 --worktree /tmp/arm-task-TASK-ID arm render-context TASK-ID --format agent--worktreeis REQUIRED for worker dispatch. This is an invariant, not merely a best practice for permissions. The binding-resolution logic in the harness hook depends on the binding-identity invariant: each agent must operate under exactly one issue binding, and that binding must follow the artifact being touched, not the process touching it. Without a worktree, the hook's four-step resolution chain (file path → event cwd → session cwd → env var) has no per-task.gitdirectory to resolve from, breaking the isolation that makes per-task enforcement possible.When you pass
--worktree <path>toarm claim:arm claimcreates an isolated git worktree on a task-specific branch- The task ID is written to
<worktree-git-dir>/armature-issue-id - Workers edit files inside the worktree (step 1 of binding resolution)
- The hook reads the binding from the file path being edited (step 1 succeeds)
- Events are evaluated under the correct task's policy
Without a worktree:
- No task-specific
.git/armature-issue-idfile exists - Step 1 of binding resolution finds no file and falls through to steps 2–4
- All events resolve to the session's binding (or env var) regardless of which agent's code is being changed
- Scope enforcement becomes meaningless; multiple agents cannot be parallelized safely
Do not pre-create the worktree with
git worktree add; letarm claimhandle creation (it sets up binding and branch correctly).Set
--ttlto exceed your expected worker runtime. Default is 60 minutes; use--ttl 240or higher for complex tasks. If the TTL expires while a worker is still running, the claim becomes stale and another coordinator may re-dispatch the same task. Workers send periodic heartbeats (arm heartbeat TASK-ID) to reset the TTL — the worker skill handles this — but the coordinator's initial TTL must cover the time until the first heartbeat. -
Dispatch each task to a worker agent using your platform's agent dispatch capability. Pass the full
render-contextoutput as the task specification. -
For parallel waves, assign each worker a log slot before dispatch:
export ARM_LOG_SLOT=<slot-number>
See Dispatch Protocol below for the full worker prompt format.
5. Parallel Dispatch (independent tasks in one wave)
Pre-claim all tasks in the wave, then dispatch workers concurrently. Each worker:
- receives the pre-claimed issue context
- implements and transitions to
done - does NOT run
arm claimagain
Claim collisions are handled at pre-claim time by the coordinator.
Dispatch Protocol
Each worker's context package must contain:
-
Skill invocation (VERY FIRST instruction):
You are an armature worker. Invoke the `armature-worker` skill via the Skill tool before proceeding.This must appear before everything else — the skill loads the worker's operating procedure and pre-flight checks.
-
Log slot (second instruction, before any
armcommand):Before running any arm command, run: export ARM_LOG_SLOT=<assigned-slot>This must be the second line of the worker's prompt — immediately after the skill invocation.
-
Full
render-contextoutput — this is the worker's complete task spec. Do not summarize it; pass it verbatim. -
Pre-claimed notice — tell the worker the issue is already claimed and it must NOT run
arm claimagain:This issue has been pre-claimed. Do NOT run `arm claim`. Do NOT run `arm worker-init`. -
Repository location: Use the isolated git worktree created for this task by
arm claim --worktree, not the main repository:Working directory: /tmp/arm-task-TASK-ID -
Task-specific branch: The task-specific branch was created and is already checked out by
arm claim --worktree. Do NOT rungit checkout feat/STORY-ID(the shared story branch) — this causes collisions with parallel workers. Commit directly to the current branch:Working branch: (task-specific branch from render-context) — do not run `git checkout feat/STORY-ID`See
docs/conventions.md(branch naming section) in the armature repo for the full branch naming convention (feature branches, task branches, and ops branches). -
Commit instruction — instruct the worker to stage files explicitly using the task's
scopefield, notgit commit -am:Commit: git add <each file listed in scope> && git commit -m "feat(ISSUE-ID): ..."
Background agent Bash limitation: Background agents dispatched without an active terminal session cannot inherit the parent session's Bash permissions. Shell commands will block silently, causing the worker to hang indefinitely. To avoid this, prefer:
- Direct implementation — have the coordinator implement small, well-scoped tasks itself rather than dispatching a background agent.
- Foreground worktrees — create a git worktree manually and run the worker in a foreground terminal session so it inherits Bash permissions.
After Workers Return
Run this integration checklist after each wave completes:
a. Check task status
arm list --parent STORY-ID # confirm all wave tasks are done
arm list --status in-progress # any stragglers?
a.1. Worker Recovery — Unkept arm transition
If a worker returned but their task remains in-progress or done without running arm transition (e.g., the worker forgot or the agent timed out), manually transition the task:
# List all tasks still in-progress or done
arm list --parent STORY-ID --format json | grep -E '"status":\s*"(in-progress|done)"'
# For each task that should be transitioned, manually run:
arm transition TASK-ID --to done --outcome "CONCRETE_OUTCOME_DESCRIPTION"
The recovery step:
- Identify the gap — run
arm list --parent STORY-IDand look for tasks with"status": "in-progress"or"status": "done"that do not appear in the wave manifest or were not markedmergedin step (c) below. - Understand what the worker did — run
arm review commits TASK-ID --branch task/TASK-IDto find the delivery commits and review the scope files modified. Usegit diffto confirm the work is complete. - Write a concrete outcome — do not re-use generic phrases like "Done" or "Completed". Reference specific files changed, tests added, or commands verified. Example:
"Implemented TokenParser.Parse() method; all 8 token types pass new tests; coverage 82%". - Transition manually — run
arm transition TASK-ID --to done --outcome "..."with the specific outcome. This unblocks dependent tasks and prepares the issue for merge validation.
This is common when workers return from background dispatch without explicit handoff, or when TTL expiration causes a race with the heartbeat mechanism. Recovery is safe — arm transition is idempotent once an issue is already done.
a.2. Semantic Review (Reviewer Dispatch)
For each task that completed in the wave, dispatch semantic conformance review using task-scoped delivery bundles:
Task-Scoped Semantic Review — each task's review bundle must contain only that task's changes, not the cumulative wave diff. This ensures:
- Scope violations are detected correctly (task didn't modify unrelated files)
- Acceptance criteria are matched to the right task's delivery
- Code quality assessment applies to the right code
- Clear audit trail of which task changed what
Workflow:
-
Capture per-task commit ranges — each task was completed in its own isolated worktree on branch
task/TASK-ID(Dispatch Protocol steps 4-5), so the task's commit range is simply that branch relative to the wave's base commit. No commit-message scanning or git-history reconciliation is required, because each task's commits already live on their own branch rather than interleaved on a shared one:declare -A TASK_COMMITS # TASK_ID -> "$WAVE_BASE_SHA..task/TASK-ID" for TASK_ID in $WAVE_TASK_IDS; do if ! git rev-parse --verify "task/$TASK_ID" >/dev/null 2>&1; then echo "ERROR: branch task/$TASK_ID not found. Did the worker commit before returning?" >&2 exit 1 fi TASK_COMMITS["$TASK_ID"]="$WAVE_BASE_SHA..task/$TASK_ID" doneImportant — ordering: at this point the task branches have not yet been merged into the story branch (that happens in step (b) below, which runs after this semantic review and the overlap audit in a.3). Do not substitute
HEADorfeat/STORY-IDfortask/$TASK_IDhere — until the merge in step (b), those refs do not contain the task's commits. -
Prepare per-task review bundles — use task-specific commit ranges, not wave-combined ranges:
# For each task, capture its delivery diff (task-scoped, not wave-scoped) TASK_BASE="<task's base commit from step 1>" TASK_HEAD="<task's head commit from step 1>" BUNDLE_FILE=$(mktemp) arm review prepare --issue TASK-ID \ --base "$TASK_BASE" --head "$TASK_HEAD" \ --output "$BUNDLE_FILE"This creates a JSON bundle file containing the issue's acceptance criteria, scope, and the diff of only that task's changed files. The bundle is written to
$BUNDLE_FILEfor later use in both the reviewer dispatch and assessment recording steps.
2.1. Activity Index (if bundle has activity section) — when the bundle includes execution evidence:
arm review prepare has no --activity-log or --activity-digest flags — it discovers
the worktree's activity log itself and attaches an activity section to the bundle
automatically when a log is present. Check for it after prepare:
HAS_ACTIVITY=$(jq -r 'if .activity then "yes" else "no" end' "$BUNDLE_FILE")
If HAS_ACTIVITY is yes, dispatch the armature-activity-indexer as a subagent
before dispatching the reviewer:
Dispatch armature-activity-indexer with:
- the bundle file: $BUNDLE_FILE (or at minimum, the bundle's activity.log_path,
activity.digest, activity.delivery_head_count, and activity.earlier_count fields —
read them out with jq if passing the whole file is inconvenient)
The indexer reads the log at activity.log_path, verifies its digest against
activity.digest, and returns an Activity Index JSON (schema_version, log_path,
log_digest, entry_count, delivery_head_count, earlier_count, entries[]) as its
final text output.
Capture the indexer's returned text into a temp file:
INDEX_OUTPUT=$(mktemp)
# The indexer subagent's returned text IS the Activity Index JSON.
# Write it directly to $INDEX_OUTPUT, e.g.:
# echo "$INDEXER_OUTPUT" > "$INDEX_OUTPUT"
# where $INDEXER_OUTPUT is the text returned by the indexer subagent.
The Activity Index is a finding aid only — it summarizes the activity log to help the reviewer locate raw entries by category and exit status. The index itself is never citable; citations must reference raw activity log entry IDs (0-based physical line numbers, e.g. "0", "1" — see the reviewer skill).
-
Dispatch the armature-reviewer agent — pass both the bundle and activity index (if available):
Dispatch armature-reviewer with: - bundle file: $BUNDLE_FILE (the reviewer reads the bundle from the file) - activity index (if $HAS_ACTIVITY was "yes"): pass the contents of $INDEX_OUTPUT as additional context so the reviewer can route to raw entry IDsThe reviewer assesses whether the delivery conforms to the issue contract (acceptance criteria, scope adherence, code quality). For behavioral criteria, execution evidence from the activity log can lift indeterminate verdicts to satisfied or partially satisfied, but it never substitutes for diff citations on implementation criteria and never suppresses a not_satisfied the diff supports.
It is a subagent whose final text output is the
ConformanceAssessmentJSON. After the subagent returns, write its output text to a temp file:RESULT_FILE=$(mktemp) # The reviewer subagent's returned text IS the ConformanceAssessment JSON. # Write it directly to $RESULT_FILE, e.g.: # echo "$REVIEWER_OUTPUT" > "$RESULT_FILE" # where $REVIEWER_OUTPUT is the text returned by the reviewer subagent. -
Record the assessment — persist the reviewer's findings:
arm review record --issue TASK-ID --assessment "$RESULT_FILE" --bundle "$BUNDLE_FILE"This links the assessment to the issue and updates its review status. Red ratings may block further wave progression until remediated. Pass both
--assessment "$RESULT_FILE"and--bundle "$BUNDLE_FILE"as file paths (not raw JSON content) so the recorded assessment is bound to the exact bundle (and its durable identity) the reviewer evaluated, preventing a stale or mismatched bundle from being credited.
Note: The reviewer checks semantic conformance to the contract — whether the code solves the stated problem cleanly. Activity evidence informs behavioral criteria only and is never citable directly (citations must reference raw log entry IDs). This is independent of the auditor's checks (citation coverage, repo health). Both gates must pass before story sign-off.
a.3. Parallel Branch Overlap Audit
When multiple tasks run in parallel (same wave), there is a risk of semantic revert: one task may undo, contradict, or invalidate changes from another task in files they both touched.
Identify overlapping files:
After all parallel wave tasks have transitioned to done, audit for files modified by multiple tasks in the same wave:
# Build a list of files changed by each task
declare -A TASK_FILES
for TASK_ID in $WAVE_TASK_IDS; do
TASK_BASE="${TASK_COMMITS[$TASK_ID]%%\.\.*}" # extract base from range
TASK_HEAD="${TASK_COMMITS[$TASK_ID]##*\.\.}" # extract head from range
TASK_FILES["$TASK_ID"]=$(git diff --name-only "$TASK_BASE".."$TASK_HEAD")
done
# Find overlaps: files touched by >1 task
# NOTE: use the union of each task's own file list, not "$WAVE_BASE_SHA"..HEAD —
# task branches are not yet merged into HEAD at this point (merge happens in step b).
OVERLAPPING_FILES=""
ALL_CHANGED_FILES=$(for TASK_ID in $WAVE_TASK_IDS; do echo "${TASK_FILES[$TASK_ID]}"; done | sort -u)
for FILE in $ALL_CHANGED_FILES; do
TASK_COUNT=0
for TASK_ID in $WAVE_TASK_IDS; do
if echo "${TASK_FILES[$TASK_ID]}" | grep -q "^$FILE$"; then
((TASK_COUNT++))
fi
done
if [ "$TASK_COUNT" -gt 1 ]; then
OVERLAPPING_FILES="$OVERLAPPING_FILES $FILE"
fi
done
if [ -n "$OVERLAPPING_FILES" ]; then
echo "WARNING: Files modified by multiple parallel tasks in wave $WAVE_TASK_IDS:"
echo "$OVERLAPPING_FILES" | tr ' ' '\n' | sort -u
fi
Audit semantic compatibility:
For each overlapping file, manually review the diffs from each task to confirm:
- Changes are additive, not contradictory (e.g., both tasks add to a list, not delete the same item)
- The combined effect preserves intended semantics (e.g., a refactoring in task A doesn't invalidate a bug fix in task B)
- Test coverage is sufficient to catch regressions (integration tests should exercise the overlapped file in multiple contexts)
Failure mode: If any overlapping file shows contradictory changes (e.g., task A sets a flag to false, task B sets it to true), the semantic revert risk is HIGH. Escalate to reviewer dispatch with explicit test evidence before marking tasks merged.
b. Check for scope conflicts and merge conflicts
Now that semantic review (a.2) and the overlap audit (a.3) are complete, merge
each task's branch (task/TASK-ID) into the story feature branch. Resolve any
conflicts before proceeding. Only after this merge do the task branches' commits
become reachable from feat/STORY-ID's HEAD.
c. Wave Verification Gate
After confirming all wave tasks are done, run the verification gate against the
wave manifest recorded in step 3 before dispatch.
Do not run arm merged until this gate passes. If the gate fails, tasks must
remain in done (not merged) so the coordinator retains visibility into which
tasks need remediation.
Terminal sanity check:
echo "Wave: $WAVE_TASK_IDS"
echo "Base SHA: $WAVE_BASE_SHA"
echo "Branch: $WAVE_BRANCH"
echo "Wave type: $WAVE_TYPE"
If any variable is unset, stop — the manifest was not recorded before dispatch.
Reconstruct it from arm list --status done and arm review commits TASK-ID --branch task/TASK-ID before proceeding.
Determine changed-file set:
CHANGED_FILES=$(git diff --name-only "$WAVE_BASE_SHA"..HEAD)
Auto-promote wave type:
if echo "$CHANGED_FILES" | grep -E '\.(go|mod|sum)$' | grep -q . || \
echo "$CHANGED_FILES" | grep -E '^(Makefile|cmd/|internal/)' | grep -qvE 'internal/skillsembed'; then
WAVE_TYPE=code
fi
Code profile (run when WAVE_TYPE=code):
go build ./... # compilation gate
make check # lint + test + coverage-check + mutate + validate-skills + build
arm validate --quiet # citation integrity
arm doctor # repo health
If go build fails and make is unavailable, fall back to:
go run ./cmd/armature --help # confirms the binary compiles
Docs-skill-only profile (run when WAVE_TYPE=docs-skill-only):
make validate-skills # skills must reference arm, not install steps
arm validate --quiet # citation integrity
arm doctor # repo health
If any *.go, go.mod, go.sum, Makefile, cmd/, or internal/ file
(outside internal/skillsembed/) appears in $CHANGED_FILES, auto-promote to
the code profile and re-run.
Bounded remediation (2 attempts max):
- Attempt 1: Fix reported failures. Be strict — address every error and warning before re-running the gate.
- Attempt 2: If failures persist, escalate: add an
arm noteon the story describing the blocker, do not transition, and surface the issue to the user before proceeding to the next wave.
Do not proceed to the next wave or story transition if the gate is red after 2 remediation attempts.
d. Mark completed tasks merged (with violation gate)
Once the verification gate passes, promote all completed wave tasks from done to merged.
Before merging, check for enforcement gaps in the hook log.
Check for violations:
Each task's worktree maintains an armature-hook.log recording all binding-resolution
decisions. The log contains three types of entries:
decision:— all resolved events (scope allow/block decisions)pass-through:— events with no binding (warnings only)violation:— file writes that resolved to no binding (enforcement gaps)
Violations represent scenarios where the hook was unable to enforce scope: a file was written but no binding was found during resolution. They are not the hook blocking an operation; they are enforcement gaps that slipped through.
You do not need to inspect the logs by hand: the violation gate is built into
arm merged --issue TASK-ID, which locates the task's worktree, resolves its git
dir (worktree .git is a file pointing at the real git dir), and fails if the log
contains violation: entries. To inspect a log manually:
# Locate the worktree for the task's branch, then read its hook log
WT=$(git worktree list --porcelain | awk '/^worktree /{p=$2} /^branch refs\/heads\/task\/TASK-ID$/{print p}')
if [ -n "$WT" ]; then
GIT_DIR=$(git -C "$WT" rev-parse --git-dir)
grep "violation:" "$GIT_DIR/armature-hook.log" 2>/dev/null && echo "WARNING: TASK-ID has violations"
fi
Violation gate:
When you run arm merged --issue TASK-ID:
arm mergedchecks the worktree'sarmature-hook.logforviolation:entries.- If violations are found and
--forceis not specified:arm mergedexits with an error- The worktree is preserved (not torn down) as evidence
- The task remains in
donestatus (not promoted tomerged) - You must review the violations and remediate or explicitly override with
--force
- If violations are found and
--forceis specified:- Violations are acknowledged and overridden
- The task is marked
merged - The worktree is torn down
- Pass-through entries do not block merging — they are warnings, not violations. A message is emitted to stderr, but the merge proceeds.
Merging a wave with violations:
A wave whose tasks contain violation: entries must not be integrated (merged to main)
without explicit operator review and override. Violations indicate that the harness was
unable to enforce task scope on one or more file writes, raising risk for the story
integration. Remediation options:
- Investigate — review the hook log, identify which files were written unbound, and confirm they were in-scope anyway (violation was a false alarm).
- Remediate — if files were genuinely out-of-scope, update the task implementation to keep all writes within scope, then re-run the task.
- Override — if you have reviewed the violations and accept the risk, use:
Usearm merged --issue TASK-ID --force--forceonly when violations have been explicitly reviewed and approved.
# Promote all tasks to merged (with violation gate)
for TASK_ID in $WAVE_TASK_IDS; do
arm merged --issue TASK_ID
# If this exits with an error about violations, either remediate or run:
# arm merged --issue TASK_ID --force # (with explicit review)
done
This allows dependent work to unblock cleanly before the next wave begins, while ensuring enforcement gaps are surfaced and reviewed.
e. Check citation coverage
arm validate
If validate shows uncited node: ID, run:
arm sources link --issue ID --source-id SOURCE-UUID # if a source doc exists
# or
arm sources accept-citation --issue ID --rationale "No external source; self-citing" --ci # if no source, mark as self-citing
f. Clean up worktrees
If workers used git worktrees, remove them after their branches are merged.
Ordering caveat: arm review prepare/arm review record for a task must complete
(and the assessment must be recorded) before that task's worktree is removed.
arm review prepare reads the activity log from the worktree's own git dir
(<repo>/.git/worktrees/<name>/armature-activity.log), and arm review record
re-reads the log from the path the bundle recorded to re-verify its digest. Removing
the worktree first deletes that private git dir — the log becomes unreadable and
activity citations for that task can no longer be validated (surfacing as a "log
missing or unreadable" error, not a "tampering" one). Sequence review-then-teardown
per task, not teardown-then-review for the whole wave.
git worktree list
git worktree remove <path> --force
git branch -d <worker-branch>
g. Continue to next wave
arm ready # next wave should now be unblocked
Story Completion
When arm ready returns empty and all tasks are done:
1. Run the Auditor (pre-merge gate)
Dispatch the armature-auditor skill as a subagent before any story transition. The auditor is a five-step pre-merge gate — it must give all-clear before you proceed.
Invoke via the Skill tool:
Skill("armature-auditor")
The auditor checks:
- Citation integrity (
arm validate— zero ERRORs,COVERAGE: N/N cited) - Source freshness (
arm sources verify— zero MISSING) - Outcome quality (concrete outcomes against acceptance criteria)
- Scope overlap (
arm validate --strict— zero overlap warnings) - Repo health (
arm doctor --strict— exit zero)
Do not proceed to step 2 until the auditor reports all five checks green.
2. Transition the story
arm transition STORY-ID --to done --outcome "brief summary of what was delivered"
3. Verify armature ops
Armature automatically commits ops to the separate _armature ops branch after each
command. No manual ops commit is required — ops are already persisted and will be
delivered separately.
4. Push and open PR
git push -u origin HEAD
# Open a PR targeting your main/base branch
# PR title: the story title
# PR body: list each task ISSUE-ID and its one-line outcome
One PR per story.
Common Failure Modes
| Failure | Cause | Fix |
|---|---|---|
| Parallel agents share one log, attribution lost | Forgot to embed ARM_LOG_SLOT in each agent's prompt | Include export ARM_LOG_SLOT=<slot> as the first instruction in each agent's prompt before dispatch |
| Build breaks after merging parallel branches | Skipped integration verification | After each wave, run make check before claiming the next wave |
| Semantic revert when merging parallel task branches | Multiple parallel tasks touched the same file; merge did not account for interdependencies | After each parallel wave, run the Parallel Branch Overlap Audit (section a.3); review semantic compatibility of overlapping files before marking tasks merged; add integration tests if needed to exercise combined changes |
arm transition STORY-ID --to done errors with uncited nodes | Story transitioned before all issues were cited | Run arm validate; for each uncited node: ID, run arm sources link or arm sources accept-citation --ci; then retry transition |
| Armature ops not committed | Forgot mop-up commit before push | After story transition, run git status; if .armature/ has changes, commit them (single-branch mode only) |
What ships with it: 2 files
6.9 KB alongside SKILL.md
references/
- commands.md2.3 KB
- parallel-dispatch.md4.6 KB