agentsclimarketplace

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

Install
npx -y skills add scullxbones/armature --skill armature-coordinator

Assembled 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

  1. If arm is not found, stop and resolve this before proceeding.

  2. Worker identity required. Run arm worker-init once per clone before claiming any tasks:

    arm worker-init --check || arm worker-init
    

    arm claim calls resolveWorkerAndLog, which fails with "worker not initialized" if no worker ID is set in git config.

  3. 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 check
    

    Fix any doctor errors 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:

  1. 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
    

    --worktree is 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 .git directory to resolve from, breaking the isolation that makes per-task enforcement possible.

    When you pass --worktree <path> to arm claim:

    • arm claim creates 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-id file 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; let arm claim handle creation (it sets up binding and branch correctly).

    Set --ttl to exceed your expected worker runtime. Default is 60 minutes; use --ttl 240 or 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.

  2. Dispatch each task to a worker agent using your platform's agent dispatch capability. Pass the full render-context output as the task specification.

  3. 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:

  1. receives the pre-claimed issue context
  2. implements and transitions to done
  3. does NOT run arm claim again

Claim collisions are handled at pre-claim time by the coordinator.


Dispatch Protocol

Each worker's context package must contain:

  1. 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.

  2. Log slot (second instruction, before any arm command):

    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.

  3. Full render-context output — this is the worker's complete task spec. Do not summarize it; pass it verbatim.

  4. Pre-claimed notice — tell the worker the issue is already claimed and it must NOT run arm claim again:

    This issue has been pre-claimed. Do NOT run `arm claim`. Do NOT run `arm worker-init`.
    
  5. 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
    
  6. Task-specific branch: The task-specific branch was created and is already checked out by arm claim --worktree. Do NOT run git 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).

  7. Commit instruction — instruct the worker to stage files explicitly using the task's scope field, not git 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:

  1. Identify the gap — run arm list --parent STORY-ID and look for tasks with "status": "in-progress" or "status": "done" that do not appear in the wave manifest or were not marked merged in step (c) below.
  2. Understand what the worker did — run arm review commits TASK-ID --branch task/TASK-ID to find the delivery commits and review the scope files modified. Use git diff to confirm the work is complete.
  3. 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%".
  4. 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:

  1. 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"
    done
    

    Important — 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 HEAD or feat/STORY-ID for task/$TASK_ID here — until the merge in step (b), those refs do not contain the task's commits.

  2. 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_FILE for 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).

  1. 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 IDs
    

    The 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 ConformanceAssessment JSON. 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.
    
  2. 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 note on 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:

  1. arm merged checks the worktree's armature-hook.log for violation: entries.
  2. If violations are found and --force is not specified:
    • arm merged exits with an error
    • The worktree is preserved (not torn down) as evidence
    • The task remains in done status (not promoted to merged)
    • You must review the violations and remediate or explicitly override with --force
  3. If violations are found and --force is specified:
    • Violations are acknowledged and overridden
    • The task is marked merged
    • The worktree is torn down
  4. 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:

  1. Investigate — review the hook log, identify which files were written unbound, and confirm they were in-scope anyway (violation was a false alarm).
  2. Remediate — if files were genuinely out-of-scope, update the task implementation to keep all writes within scope, then re-run the task.
  3. Override — if you have reviewed the violations and accept the risk, use:
    arm merged --issue TASK-ID --force
    
    Use --force only 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:

  1. Citation integrity (arm validate — zero ERRORs, COVERAGE: N/N cited)
  2. Source freshness (arm sources verify — zero MISSING)
  3. Outcome quality (concrete outcomes against acceptance criteria)
  4. Scope overlap (arm validate --strict — zero overlap warnings)
  5. 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

FailureCauseFix
Parallel agents share one log, attribution lostForgot to embed ARM_LOG_SLOT in each agent's promptInclude export ARM_LOG_SLOT=<slot> as the first instruction in each agent's prompt before dispatch
Build breaks after merging parallel branchesSkipped integration verificationAfter each wave, run make check before claiming the next wave
Semantic revert when merging parallel task branchesMultiple parallel tasks touched the same file; merge did not account for interdependenciesAfter 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 nodesStory transitioned before all issues were citedRun arm validate; for each uncited node: ID, run arm sources link or arm sources accept-citation --ci; then retry transition
Armature ops not committedForgot mop-up commit before pushAfter 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/

Keep looking

Skills are one crate of 326,851. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.