| name | skill-orchestrate-hard |
| description | Full structural hard-mode orchestration state machine with per-phase dispatch (H1), adversarial verification (H4), convergence policing (H6), territory contracts (H7), and churn detection (H5). Invoke for /orchestrate --hard. |
| allowed-tools | Agent, Bash, Read |
Orchestrate Hard Skill
IMPORTANT: This is a FULL STRUCTURAL VARIANT, not a thin wrapper over skill-orchestrate.
Loop-level changes (per-phase dispatch, churn detection, adversarial verification gate) cannot
be expressed as prompt injection into the base skill. Maintenance: both skills should evolve
together -- changes to escalation ladder and handoff schema in skill-orchestrate should be
reflected here.
Hard-mode additions over base skill-orchestrate:
- H1 Per-Phase Dispatch: Each implement cycle dispatches exactly one phase, not the whole plan
- H4 Adversarial Verification Gate: Research output is verified before plan/implement dispatch
- H5 Divergence Audit: Three-strikes on any target triggers a dedicated audit research dispatch
- H6 Convergence Policing: Churn detection with per-target counters (defect_claims, sorry_relocations)
- H7 Territory Contracts: Territory context informs single-phase dispatch prompts (parallel-wave
dispatch is disabled — see "Tool Constraints (Pure Dispatcher)" below and Stage 4's Per-Phase
Dispatch handler; exactly one blocking
Agent call happens per cycle)
Tool Constraints (Pure Dispatcher)
The orchestrator is a pure dispatcher: it reads state, dispatches exactly one implementation
agent per cycle, and reads that agent's handoff. It never edits implementation source, never
runs build/test/compiler tooling, and never reads implementation source files itself. This
section is the human/audit-readable statement of intent backing the frontmatter tool scoping
above (allowed-tools: Agent, Bash, Read — Edit intentionally absent).
Permitted Bash: orchestration bookkeeping only — jq reads/writes against state.json and
handoff/loop-guard/churn JSON files, file bookkeeping (mkdir, mv, rm, ls), text utilities
(sort, tail, grep, cat, date, echo), and source .claude/scripts/*.sh helper
scripts. These are the 12 commands the state machine above actually issues: jq, mkdir, mv, rm, ls, sort, tail, grep, cat, date, echo, source.
Forbidden Bash Operations: lake build (or any lake invocation), lean / lean-lsp /
mcp__lean-lsp__*, nvim --headless, npm / pytest / cargo test / go test, or ANY
language build/test/compiler/linter tool. These belong exclusively to the dispatched
implementation agent ($IMPLEMENT_AGENT) — the orchestrator must never invoke them directly,
even to "verify" a phase before or after dispatch.
Read allowlist (4 categories):
specs/state.json — task status and metadata.
specs/{NNN}_{SLUG}/.orchestrator-handoff.json and its siblings
.orchestrator-loop-guard / .orchestrator-churn-state.json.
specs/{NNN}_{SLUG}/plans/*.md and specs/{NNN}_{SLUG}/reports/*.md. Plan- and
report-file access covers exactly three bounded, grep-only uses, none of which is a
full-file comprehension read: (a) the H4 adversarial-verification grep over reports in
Stage 4; (b) Stage 4's ### Phase N: ... [STATUS] next-phase selection grep over the plan;
and (c) Stage 5's count-only phase-marker recovery grep over the plan, bounded to ≤10 tokens
per recovery event (two grep -c integers) and fired from exactly three bounded uses: the
missing/stale-handoff branch's own diagnostic-only recovery grep (still the raw inline
two-grep -c idiom directly — it has no recoverable dispatch_status to corroborate
against, so it never calls the shared function below); the recovered=true branch's
evidence-corroboration call, fired only when return-meta recovery's own
evidence_suspect/evidence_reason report PHASES_ZERO_ON_SUCCESS for a claimed
implemented status; and the handoff-present branch's corroboration call, which fires only
when the handoff itself reports dispatch_status = "implemented" AND phases_total -eq 0 —
never on the normal path of a fresh handoff with populated phase accounting. The latter two
uses both call the SAME shared skill_corroborate_phase_counts (scripts/skill-base.sh),
which performs the same two grep -c calls inside itself rather than inline — the single
anchor both uses (and their base-mode and multi-task mirrors) now share, in the same way
scripts/lib/phase-heading-patterns.sh is the single grammar anchor every phase-heading
consumer sources rather than re-deriving. Any other use of these files is outside the
allowlist. (The recovered=true branch's site also carries a sibling elif arm on
evidence_reason="ARTIFACTS_SHAPE_MISMATCH" — Deliverable 2(a), a non-fatal
system-defect-record.sh consumer call. It shares the recovered=true precondition but
performs no grep -c of any kind and calls no new Read, so it is not a fifth bounded use of
this allowlist.)
.claude/context/contracts/*.md and .claude/docs/architecture/*.md (this skill's own
contracts and architecture docs, per Context References above).
Forbidden Reads: implementation source of any kind — lua/**, after/**, or any
per-project source root an $IMPLEMENT_AGENT would modify. If the orchestrator finds itself
about to Read a source file to "check" an implementation, that is a signal it has drifted from
pure-dispatcher behavior; the correct action is to dispatch (or re-dispatch) the implementation
agent and read its handoff instead.
Context References
Architecture documentation (load as needed):
.claude/context/contracts/convergence.md - H6 convergence policing rules
.claude/context/contracts/territory.md - H7 territory contract for parallel dispatch
.claude/context/contracts/anti-analysis.md - H2 contract injected into each implement dispatch
.claude/context/contracts/wrap-up.md - H9 contract for handoff discipline
.claude/context/contracts/recovery.md - Fix-forward recovery ladder referenced by the
Recovery Discipline contract slot (see CONTRACT SLOTS below)
.claude/context/contracts/orchestrator-discipline.md - Orchestrator-role discipline
contract governing this state-machine loop itself (Stage 1c preamble, Stage 3c burnout
circuit-breaker gate) — not the implement dispatches that anti-analysis.md governs
.claude/docs/architecture/orchestrate-state-machine.md - Base state table (reference)
.claude/docs/architecture/handoff-schema.md - Handoff JSON schema
Execution Flow
Stage 0: Multi-Task Mode Detection
Same as base skill-orchestrate. Parse multi_task_mode. If true, use base multi-task
stages (per-phase dispatch applies to each task independently within the wave).
source .claude/scripts/skill-base.sh
multi_task_mode=$(echo "$delegation_context" | jq -r '.multi_task_mode // false')
session_id=$(echo "$delegation_context" | jq -r '.session_id')
focus_prompt=$(echo "$delegation_context" | jq -r '.focus_prompt // ""')
effort_flag=$(echo "$delegation_context" | jq -r '.effort_flag // "hard"')
continue_budget_flag=$(echo "$delegation_context" | jq -r '.continue_budget // false')
Stage 1: Input Validation
task_number=$(echo "$delegation_context" | jq -r '.task_context.task_number')
PADDED_NUM=$(printf "%03d" "$task_number")
TASK_DATA=$(jq -r --argjson num "$task_number" \
'.active_projects[] | select(.project_number == $num)' \
specs/state.json)
if [ -z "$TASK_DATA" ]; then
echo "ERROR: Task $task_number not found in state.json" >&2
exit 1
fi
PROJECT_NAME=$(echo "$TASK_DATA" | jq -r '.project_name')
TASK_TYPE=$(echo "$TASK_DATA" | jq -r '.task_type // "general"')
DESCRIPTION=$(echo "$TASK_DATA" | jq -r '.description // ""')
TASK_DIR="specs/${PADDED_NUM}_${PROJECT_NAME}"
TASK_DIR_ABS="${TASK_DIR_ABS:-${SKILL_REPO_ROOT:-$(pwd)}/${TASK_DIR}}"
HANDOFF_PATH_ABS="${TASK_DIR_ABS}/.orchestrator-handoff.json"
Stage 1b: Resolve Hard-Mode Agent Routing
Map task_type to hard-mode research, plan, and implementation agents via the single canonical
agent resolver, command-route-agent.sh — the same script skill-orchestrate's (base) Stage 1b
calls, sourced here with effort "hard" and hard-mode defaults. Resolution runs against each
manifest's routing_agents_hard declarations; a miss falls through directly to the
caller-supplied hard-mode default below (NOT to the standard, non-hard routing_agents block),
preserving today's default-to-general-hard-agent behavior exactly. The two engines now differ
only in the effort argument and these three defaults — no case table, no manifest loop, no sed
derivation.
source .claude/scripts/command-route-agent.sh "research" "$TASK_TYPE" "general-research-hard-agent" "hard"
RESEARCH_AGENT="$AGENT_NAME"
source .claude/scripts/command-route-agent.sh "plan" "$TASK_TYPE" "planner-hard-agent" "hard"
PLANNER_AGENT="$AGENT_NAME"
source .claude/scripts/command-route-agent.sh "implement" "$TASK_TYPE" "general-implementation-hard-agent" "hard"
IMPLEMENT_AGENT="$AGENT_NAME"
echo "[hard-orchestrate] Routing: research=$RESEARCH_AGENT, implement=$IMPLEMENT_AGENT, plan=$PLANNER_AGENT"
Dispatch Context Anchor Invariant
Every delegation_context in this file states orchestrator_mode explicitly, giving two
checkable properties: (I1) no site relies on the reader's // "false" default — the key is
always present, true or false; (I2) task_dir / handoff_path are present on a context if
and only if it declares orchestrator_mode: true. The one indirection: delegation_context: $dispatch_context (Stage 4, per-phase implement dispatch) refers to the JSON literal built
immediately above it — that literal, not the reference line, carries orchestrator_mode and the
anchors. orchestrator_mode is dual-consumer (handoff-write gate plus the literature Stage 4a
autonomy gate — see the Dual-Consumer Note in docs/architecture/handoff-schema.md), so a future
edit here weighs both; the false sub-dispatches (H4 verification, H5 divergence audit, Stage 6
blocker research) pass no lit_flag at all, leaving the literature path disabled there regardless.
F=agent-system/extensions/core/skills/skill-orchestrate-hard/SKILL.md
PAT='^[[:space:]]*delegation_context: \{'
[ "$(grep -cE "$PAT" "$F")" = "$(grep -cE "$PAT.*orchestrator_mode" "$F")" ] && echo "I1 holds"
grep -E "$PAT.*orchestrator_mode: true" "$F" | grep -qv 'task_dir' && echo "I2 VIOLATED (true without task_dir)" || echo "I2 holds (true sites)"
grep -E "$PAT.*orchestrator_mode: false" "$F" | grep -q 'task_dir\|handoff_path' && echo "I2 VIOLATED (false with anchor)" || echo "I2 holds (false sites)"
Stage 1c: Orchestrator Discipline Preamble
Runs ONCE per invocation, immediately after Stage 1b and before the loop begins.
Read .claude/context/contracts/orchestrator-discipline.md
State (to yourself, in your own transcript) that this session is bound by the orchestrator
discipline contract just read: no inline design/proof analysis, no reading implementation
source, no running builds, no mid-cycle strategy reconsideration without a fresh dispatch; when
a phase cannot complete in a bounded dispatch, the only allowed responses are (a) dispatch a
fresh research/audit agent or (b) escalate via the blocker ladder — never absorb the work
inline. This preamble is the pointer; the enforceable checklist is inlined at every loop
iteration in Stage 3c below.
Stage 2: Loop Guard and Churn State Initialization
Create or read the loop guard file with hard-mode churn counters.
Both .orchestrator-loop-guard and .orchestrator-churn-state.json are ephemeral, gitignored,
and never committed. Neither has a freshness check on read — the resume branch below trusts any
syntactically valid file at these paths unconditionally, with no session_id or mtime comparison
against the current dispatch. A git-restored copy of either would silently resume a wrong cycle
count, burnout-signal count, or churn history. See
context/standards/orchestrator-runtime-files.md for the full two-class policy and rationale.
MAX_CYCLES=13
loop_guard_init_json=$(bash .claude/scripts/orchestrate-loop-guard-init.sh "$TASK_DIR" "${HANDOFF_PATH_ABS}")
loop_guard_file=$(echo "$loop_guard_init_json" | jq -r '.loop_guard_file')
handoff_file=$(echo "$loop_guard_init_json" | jq -r '.handoff_file')
MAX_INFRA_FAILURES=$(echo "$loop_guard_init_json" | jq -r '.max_infra_failures')
churn_file="${TASK_DIR}/.orchestrator-churn-state.json"
current_plan_version=$(basename "$(ls -1 "${TASK_DIR}/plans/"*.md 2>/dev/null | sort -V | tail -1)" 2>/dev/null)
current_plan_version="${current_plan_version:-none}"
loop_guard_stale=false