---
name: trekexecute
description: Disciplined plan executor — single-session or multi-session with parallel orchestration, failure recovery, and headless support
argument-hint: "[--project <dir>] [--fg | --resume | --dry-run | --validate | --step N | --session N] [plan.md]"
model: opus
allowed-tools: Read, Write, Edit, Bash, Glob, Grep, AskUserQuestion
---

# Ultraexecute Local

Disciplined executor for trekplan plans. Reads a plan file, detects if it has
an Execution Strategy (multi-session), and either executes directly or
orchestrates parallel headless sessions — all to realize one plan.

Designed to work identically in interactive and headless (`claude -p`) mode.

## Phase 1 — Parse mode and validate input

Parse `$ARGUMENTS` for mode flags:

1. If arguments contain `--project <dir>`: extract the directory path.
   - Resolve `{dir}` (trim trailing slash).
   - Set **project_dir = {dir}**.
   - Derive implicit file path: `{dir}/plan.md`.
   - If `{dir}` does not exist or `{dir}/plan.md` is missing:
     ```
     Error: project directory missing plan.md: {dir}
     Run /trekplan --project {dir} to produce it.
     ```
   - If no explicit `<plan.md>` argument is provided after flag parsing,
     use the derived `{dir}/plan.md`. If a `<plan.md>` argument IS provided,
     use that path but keep `project_dir` set (user may be pointing at a
     recovery session spec inside the project).
   - Continue parsing remaining flags (they combine with `--project`).
2. If arguments contain `--fg`: extract the file path. Set **mode = foreground**.
3. If arguments contain `--resume`: extract the file path. Set **mode = resume**.
4. If arguments contain `--dry-run`: extract the file path. Set **mode = dry-run**.
5. If arguments contain `--validate`: extract the file path. Set **mode = validate**.
6. If arguments contain `--step N` (N is a positive integer): extract N and the file path.
   Set **mode = step**, **target-step = N**.
7. If arguments contain `--session N` (N is a positive integer): extract N and the file path.
   Set **mode = session**, **target-session = N**.
8. If arguments contain `--gates`: set `gates_mode = true`. Pause for
   operator confirmation after each wave's worktrees finish but before
   merge-back, AND before the main-merge gate. (The MAIN_MERGE_GATE in
   Phase 8 ALWAYS pauses regardless of `gates_mode` — `--gates` re-enables
   the per-wave pauses that auto mode otherwise skips.) Default
   `gates_mode = false`. The flag is consumed by the autonomy-gate state
   machine via the CLI shim:
   `node ${CLAUDE_PLUGIN_ROOT}/lib/util/autonomy-gate.mjs --state X --event Y --gates {true|false}`.
9. Otherwise: the entire argument string is the file path. Set **mode = execute**.

If no path is provided (and `--project` was not used to derive one), output
usage and stop:

```
Usage: /trekexecute <plan.md>
       /trekexecute --project <dir>
       /trekexecute --fg <plan.md>
       /trekexecute --resume <plan.md>
       /trekexecute --dry-run <plan.md>
       /trekexecute --validate <plan.md>
       /trekexecute --step N <plan.md>
       /trekexecute --session N <plan.md>
       /trekexecute --project <dir> --resume
       /trekexecute --project <dir> --session N

Modes:
  (default)      Auto — multi-session if plan has Execution Strategy, else foreground
  --project      Resolve plan.md inside an trekbrief project folder; progress.json
                 is written there too
  --fg           Force foreground — all steps sequentially, ignore Execution Strategy
  --resume       Resume from last progress checkpoint
  --dry-run      Validate plan and show execution strategy without running
  --validate     Schema-only check — parse steps + manifests, no security scan, no execution
  --step N       Execute only step N (foreground)
  --session N    Execute only session N from the plan's Execution Strategy

Examples:
  /trekexecute .claude/plans/trekplan-2026-04-06-auth-refactor.md
  /trekexecute --project .claude/projects/2026-04-18-jwt-auth
  /trekexecute --project .claude/projects/2026-04-18-jwt-auth --resume
  /trekexecute --fg .claude/plans/trekplan-2026-04-06-auth-refactor.md
  /trekexecute --session 2 .claude/plans/trekplan-2026-04-06-auth-refactor.md
  /trekexecute --dry-run .claude/plans/trekplan-2026-04-06-auth-refactor.md
  /trekexecute --validate .claude/plans/trekplan-2026-04-06-auth-refactor.md
```

If the file does not exist, report and stop:
```
Error: file not found: {path}
```

Report detected mode:
```
Mode: {execute | resume | dry-run | step N | session N}
Project: {project_dir or "-"}
File: {path}
```

## Phase 2 — Detect file type and parse structure

Read the file. Determine whether it is an **trekplan** or a **session spec**:

- **Session spec**: contains `## Dependencies` with `Entry condition:` AND `## Scope Fence`
  AND `## Exit Condition` sections.
- **Voyage**: contains `## Implementation Plan` with numbered `### Step N:` headings
  but no `## Scope Fence`.

If neither structure is detected, report and stop:
```
Error: unrecognized file format. Expected an trekplan or session spec.
```

### Parse steps

Extract every `### Step N: {description}` heading (in order). For each step, extract:
- **Files** — file paths to create or modify
- **Changes** — what to modify
- **Reuses** — existing code to leverage (informational)
- **Test first** — test to run before implementation (optional)
- **Verify** — command to run after implementation
- **On failure** — recovery action (revert/retry/skip/escalate)
- **Checkpoint** — git commit command after success
- **Manifest** — YAML block following Checkpoint (v1.7+)

If a step is missing `On failure`, default to `escalate` and record a parse warning.
If a step is missing `Verify`, record a parse warning.

### Parse the Manifest block (v1.7+)

Read the plan header for a `plan_version` marker. Treat ≥ `1.7` as strict
mode; absence or <1.7 as legacy mode.

**Strict mode (plan_version ≥ 1.7):**
- Every step MUST have a `Manifest:` block with a YAML fenced payload.
- Parse the YAML. Required keys: `expected_paths` (list), `min_file_count`
  (integer), `commit_message_pattern` (string), `bash_syntax_check` (list),
  `forbidden_paths` (list), `must_contain` (list of `{path, pattern}` dicts).
- Validate each `commit_message_pattern` compiles as a regex (use a tolerant
  parser — do not execute untrusted input against shell).
- If any step is missing its Manifest, or YAML is malformed, STOP with:
  ```
  Error: plan_version=1.7 but step {N} has invalid/missing Manifest.
  Re-run planning-orchestrator — plan is not executable.
  ```

**Legacy mode (no plan_version, or < 1.7):**
- Synthesize a minimal manifest per step:
  - `expected_paths` ← step's `Files:` list
  - `min_file_count` ← `len(expected_paths)`
  - `commit_message_pattern` ← regex-escape first 3 words of Checkpoint msg
  - `bash_syntax_check` ← auto-detect `.sh` in Files
  - `forbidden_paths` ← []
  - `must_contain` ← []
- Record `legacy_plan: true` in the progress file.
- Emit warning: `Legacy plan (v1.6 or earlier) — manifests synthesized with
  reduced audit precision.`

### Parse Session Manifest (session specs only)

If the file is a session spec (v1.7+), parse the `## Session Manifest` block
into a YAML dict. Preserve `session_manifest.*` fields for Phase 7.5 audit.
If missing and `plan_version ≥ 1.7`, record a parse warning but continue —
the per-step manifests are still available for audit.

### Parse session spec fields (if applicable)

- **Entry condition** from `## Dependencies`
- **Touch list** and **Never-touch list** from `## Scope Fence`
- **Exit condition** checklist from `## Exit Condition`

### Parse Execution Strategy (if present)

If the plan contains an `## Execution Strategy` section, extract:
- Each `### Session N: {title}` with its Steps, Wave, Depends on, and Scope fence
- The `### Execution Order` with wave definitions

Set **has_execution_strategy = true**.

Report:
```
Type: {plan | session-spec}
Steps: {N}
{if has_execution_strategy}: Execution Strategy: {S} sessions across {W} waves
{if session spec}: Entry condition: {text}
{if session spec}: Scope fence: {N} touch, {N} never-touch
{if warnings}: Warnings: {list}
```

## Phase 2.3 — Validate-only mode exit (if mode = validate)

**If mode = validate, stop after Phase 2 parsing** and emit a schema-only
report. Do NOT run security scan, do NOT touch progress files, do NOT
execute any steps. This gives the user a fast sanity-check of plan
schema compliance without side effects.

**Preferred path (since v3.1.0):** invoke the plan validator directly. It
returns the same diagnostic info Phase 2 derives in prose, with stable
error codes for downstream tooling:

```bash
node ${CLAUDE_PLUGIN_ROOT}/lib/validators/plan-validator.mjs --strict --json "{path}"

# When --project is in scope and progress.json exists, also validate it:
[ -f "{project_dir}/progress.json" ] && \
  node ${CLAUDE_PLUGIN_ROOT}/lib/validators/progress-validator.mjs --json "{project_dir}/progress.json"
```

Map the validator's `code` field to the error templates below (e.g.
`PLAN_FORBIDDEN_HEADING` → "Detected heading format" branch). When both
calls exit 0, render the READY report. Otherwise render FAIL with the
validator's first error code + message.

If Phase 2 parsing succeeded (no fatal errors, every step has a valid
Manifest block in strict mode, or synthesized manifests in legacy mode):

```
=== Schema Validation: READY ===
File: {path}
Type: {plan | session-spec}
plan_version: {1.7 | legacy}
Steps: {N}
Manifests: {N valid | N synthesized (legacy)}
Warnings: {count}
{if warnings}: - {each warning on own line}

Plan is schema-compliant. Safe to run:
  /trekexecute {path}
```

If Phase 2 parsing failed (unrecognized format, missing Manifest in strict
mode, malformed YAML, invalid regex):

```
=== Schema Validation: FAIL ===
File: {path}
Reason: {specific error from Phase 2}

{if format not recognized}:
  Detected heading format: {e.g. "### Fase 1:", "## Phase 1"}
  Expected: "### Step N: <description>"
  Fix: re-run /trekplan — planning-orchestrator must emit v1.7 format

{if missing manifest}:
  Step {N} has no Manifest block (plan_version=1.7 requires one per step)
  Fix: re-run /trekplan — planning-orchestrator must include manifest YAML

{if malformed YAML or invalid regex}:
  Step {N}: {specific YAML/regex error}
  Fix: edit the plan manually or re-run /trekplan
```

Exit after emitting the report. Do not continue to Phase 2.4 or later.

## Phase 2.4 — Pre-execution security scan

**Runs for all modes except dry-run and validate** (those modes exit earlier or have their own report format).

Scan every `Verify:` and `Checkpoint:` command in the parsed plan against the
executor security denylist. This catches dangerous commands before execution begins.

### Extract commands

For each step in the plan, extract the command string from:
- `Verify:` field (the shell command after the backtick-quoted command)
- `Checkpoint:` field (the git commit command)

Also extract Exit Condition commands if present.

### Check against denylist

For each extracted command, check against these patterns:

**BLOCK patterns (stop execution immediately):**

| Pattern | Threat |
|---------|--------|
| `rm` with both `-r` and `-f` flags (any order) | Recursive force delete |
| `chmod 777` or `chmod -R 777` | World-writable permissions |
| `curl`/`wget` piped to `bash`/`sh`/`zsh` | Remote code execution |
| `eval` with `$`, backtick, or `$(` | Code injection via eval |
| `mkfs` or `dd` writing to `/dev/sd*`, `/dev/nvme*`, `/dev/hd*` | Disk destruction |
| `shutdown`, `reboot`, `halt`, `poweroff` | System shutdown |
| `:(){ :\|:& };:` pattern | Fork bomb |
| `base64` piped to `bash`/`sh` | Obfuscated code execution |
| `crontab -e` or writing to `/etc/cron*` | Persistence via cron |
| `kill -9 -1` or `pkill -9 -1` | Kill all user processes |
| `history -c` or truncating `~/.bash_history` | Evidence destruction |

**WARN patterns (report but continue):**

| Pattern | Concern |
|---------|---------|
| `npm install --save`, `pip install`, `cargo add` | Dependency changes during execution |
| `git push --force` | History rewrite |
| `git reset --hard` | Discard uncommitted changes |

### Scan output

For each match:
```
Security scan: Step {N} — {description}
  Command: {command}
  {BLOCKED | WARNING}: {pattern name}
```

**If ANY BLOCK pattern is found:**

```
SECURITY SCAN FAILED: {count} dangerous command(s) found in plan.

Blocked commands:
  Step {N}: {command} → {reason}

This plan contains commands blocked by the executor security policy.
The plan may have been tampered with or contain hallucinated dangerous commands.

Options:
  1. Review and fix the plan file: {path}
  2. Use --dry-run to inspect all commands without executing
  3. Use --fg for interactive execution (hooks provide additional protection)
```

Stop execution. Do NOT continue to Phase 2.5.

**If only WARN patterns found:** Continue execution but include warnings in the
pre-execution summary. Report them in the final output under "Security advisories."

**If clean:** Report `Security scan: PASS ({N} commands checked)` and continue.

### Hooks-fire precheck (advisory)

Verify the operator's Claude Code version supports cross-model hook firing
(v2.1.117+ shipped the cross-model malware-reminder fix per research/04 D4
+ R5 + research/06 R1). Run `claude --version` (or read
`${CLAUDE_CODE_VERSION}` if available) and compare against `2.1.117`.

If unavailable or older: WARN (do not block) —
`Hooks-fire precheck: CC version {X} predates v2.1.117. Plugin hooks may
not fire reliably in headless child sessions; the in-prompt safety
preamble (Phase 2.6 Step 2b) is the primary defense for affected runs.`

If version >= 2.1.117 or unparseable: continue silently.

## Phase 2.5 — Execution strategy decision

Determine how to execute this plan:

**Run as single session (foreground)** when ANY of these are true:
- `--fg` flag is set
- `--step N` mode
- `--resume` mode
- `--session N` mode (runs only that session's steps, foreground)
- Plan has no `## Execution Strategy` section
- Plan has Execution Strategy with only 1 session

**Run as multi-session (parallel orchestration)** when ALL of these are true:
- mode = `execute` (default, no --fg)
- Plan has `## Execution Strategy` with 2+ sessions
- At least one wave has 2+ sessions (parallelism possible)

**Run as multi-session (sequential orchestration)** when:
- mode = `execute` (default, no --fg)
- Plan has `## Execution Strategy` with 2+ sessions
- All sessions are in different waves (no parallelism, but still separate sessions)

For single-session: continue to Phase 3.
For multi-session: jump to Phase 2.6.

Report:
```
Strategy: {single session | N sessions (M parallel, K sequential)}
```

## Phase 2.55 — Pre-flight safety checks

**Only runs for multi-session parallel execution.** These checks prevent the
catastrophic data loss that occurs when parallel sessions share a working directory.

### Check 1 — Clean working tree

Run `git status --porcelain`. If there are ANY uncommitted or untracked changes:

```
Error: working tree is not clean. Parallel execution requires a clean git state.
Uncommitted changes are invisible to worktrees and will be lost during merge.

Untracked/modified files:
{output of git status --porcelain}

Commit or stash your changes, then re-run.
To run sequentially instead: /trekexecute --fg {plan-path}
```

Stop execution. Update progress with `status: "stopped"`.

**Also write `.session-state.local.json`** (Handover 7) — this surfaces the
stopped state to `/trekcontinue` so the next session can prompt the user to
clean the working tree before resuming. Write atomically alongside `progress.json`:

```json
{
  "schema_version": 1,
  "project": "{project_dir}",
  "next_session_brief_path": "{project_dir}/brief.md",
  "next_session_label": "{current session label, or 'Continue'}",
  "status": "stopped",
  "updated_at": "{ISO-8601 now}"
}
```

Verify with `node lib/validators/session-state-validator.mjs --json
{project_dir}/.session-state.local.json`. On validator failure, emit a warning
to stderr but do NOT block the stop; `progress.json` is still authoritative.

**Also write sibling `NEXT-SESSION-PROMPT.local.md`** with frontmatter
(`produced_by: trekexecute`, `produced_at: <ISO-8601>`,
`status: stopped`) so the next-session producer-mismatch check has both
candidates available. Use the same combined ESM block pattern as Phase 8.

### Check 2 — Plan file is tracked by git

Run `git ls-files --error-unmatch {plan-path} 2>/dev/null`. If the plan file is
untracked (exit code != 0):

```bash
git add {plan-path}
git commit -m "chore: track plan file for parallel execution"
```

Report: `Plan file committed for worktree visibility.`

This ensures every worktree created from HEAD will have the plan file.

### Check 3 — Scope fence overlap validation

For each wave that has 2+ sessions, validate that no file appears in the Touch
list of two different sessions in the same wave:

1. For each session in the wave, extract the "Touch" list from the Execution Strategy.
2. For each pair of sessions (A, B) in the same wave, compute the intersection
   of their Touch lists.
3. If any intersection is non-empty:

```
Error: scope fence overlap detected in Wave {W}.
Sessions {A} and {B} both touch: {overlapping files}
These sessions cannot safely run in parallel.

Fix the Execution Strategy in the plan, or use --fg for sequential execution.
```

Stop execution. This is a defense-in-depth check — the planning-orchestrator
should have prevented this, but verifying at execution time catches plans
that were manually edited or have bugs.

### Check 4 — Stale worktree cleanup

Run `git worktree list`. If any worktrees with paths containing
`trekplan-sessions/{slug}/worktrees/` exist from a previous failed run:

```bash
git worktree remove --force {stale-path} 2>/dev/null
git worktree prune
```

Also check for stale branches:
```bash
git branch --list "trek/{slug}/*" | while read b; do
  git branch -D "$b" 2>/dev/null
done
```

Report: `Cleaned {N} stale worktrees and {N} branches from previous run.`

If cleanup fails, report the manual commands and stop.

After all 4 checks pass:
```
Pre-flight: PASS (clean tree, plan tracked, no overlaps, no stale worktrees)
```

## Phase 2.6 — Multi-session orchestration (worktree-isolated)

**Only runs for multi-session execution.** This phase creates isolated git
worktrees for each parallel session, launches headless child sessions in their
own worktrees, merges results back sequentially, and cleans up. After this
phase, jump directly to Phase 8 (final report).

**CRITICAL SAFETY RULE:** Every parallel `claude -p` session MUST run in its own
git worktree. Never launch two sessions in the same working directory. This rule
exists because parallel git operations in a shared worktree cause index corruption,
race conditions, and repository destruction.

### Step 0 — Billing safety check (MANDATORY)

Before launching ANY `claude -p` process, check the environment:

```bash
echo "${ANTHROPIC_API_KEY:+SET}"
```

If the result is `SET`, **STOP** and warn the user. `claude -p` sessions with
`ANTHROPIC_API_KEY` in the environment bill the **API account** (pay-per-token),
not the user's Claude subscription (Max/Pro). Parallel Opus sessions can cost
$50–100+ per run.

Use AskUserQuestion with these options:

**Question:** "ANTHROPIC_API_KEY is set in your environment. Parallel `claude -p`
sessions will bill your API account, not your Claude subscription. How do you
want to proceed?"

| Option | Description |
|--------|-------------|
| **Use --fg instead (Recommended)** | Run all steps sequentially in this session using your subscription. No extra cost. |
| **Continue with API billing** | Launch parallel sessions. Each session bills your API account at token rates. |
| **Stop** | Cancel execution. Unset ANTHROPIC_API_KEY first, then re-run. |

If the user chooses `--fg`: restart execution with mode = foreground (jump back
to Phase 3, single-session).

If the user chooses `Continue`: proceed with Phase 2.6 Step 1.

If the user chooses `Stop`: report "Execution cancelled — billing safety check"
and stop.

If `ANTHROPIC_API_KEY` is NOT set: proceed silently to Step 1.

### Step 1 — Create session infrastructure

```bash
REPO_ROOT="$(git rev-parse --show-toplevel)"
SESSION_DIR="$REPO_ROOT/.claude/trekplan-sessions/{slug}"
WORKTREE_DIR="$SESSION_DIR/worktrees"
LOG_DIR="$SESSION_DIR/logs"
mkdir -p "$WORKTREE_DIR" "$LOG_DIR"
ORIGINAL_BRANCH="$(git rev-parse --abbrev-ref HEAD)"
```

Record `REPO_ROOT`, `WORKTREE_DIR`, `LOG_DIR`, and `ORIGINAL_BRANCH` for use
in subsequent steps. All paths must be absolute.

### Step 2 — Execute waves with worktree isolation

For each wave (in order):

**2a-pre. Build shared context + budget caps (runs once per wave, before
worktree creation):**

```bash
WORKTREE_BASE="$(realpath "${WORKTREE_BASE:-${SESSION_DIR:-$REPO_ROOT}/worktrees}")"
mkdir -p "${WORKTREE_BASE}"
SHARED_CONTEXT_FILE="${WORKTREE_BASE}/.shared-context.md"
cat "${PROJECT_DIR}/brief.md" > "${SHARED_CONTEXT_FILE}"
if [ -f "${PROJECT_DIR}/architecture/overview.md" ]; then
  printf '\n\n---\n\n' >> "${SHARED_CONTEXT_FILE}"
  cat "${PROJECT_DIR}/architecture/overview.md" >> "${SHARED_CONTEXT_FILE}"
fi
MAX_TURNS="${TREKEXECUTE_MAX_TURNS:-50}"
MAX_BUDGET_USD="${TREKEXECUTE_MAX_BUDGET_USD:-5}"
```

The budget caps default to safe values for normal-length steps. Operators may
override for long runs:
`TREKEXECUTE_MAX_TURNS=120 TREKEXECUTE_MAX_BUDGET_USD=20 /trekexecute --project ...`

**2a. Create worktrees for this wave's sessions:**

For each session N in this wave:
```bash
BRANCH_NAME="trek/{slug}/session-{N}"
WORKTREE_PATH="$WORKTREE_DIR/session-{N}"
GIT_OPTIONAL_LOCKS=0 git worktree add -b "$BRANCH_NAME" "$WORKTREE_PATH" HEAD
```

`GIT_OPTIONAL_LOCKS=0` disables `.git/index.lock` background polling races
during parallel worktree operations (research/02 R2; GH #47721).

If `git worktree add` fails (e.g., branch exists from a crashed run):
```bash
GIT_OPTIONAL_LOCKS=0 git branch -D "$BRANCH_NAME" 2>/dev/null
GIT_OPTIONAL_LOCKS=0 git worktree add -b "$BRANCH_NAME" "$WORKTREE_PATH" HEAD
```

If it still fails, report the error, mark this session as failed, and skip it.

Report:
```
Worktree created: session-{N} → {WORKTREE_PATH} (branch: {BRANCH_NAME})
```

**2a'. Copy gitignored project artifacts into each worktree (addresses
plugin-in-monorepo + gitignored-state topology):**

`git worktree add` clones HEAD only. The plugin's `.gitignore` excludes
`.claude/projects/`, so the project directory (brief.md, plan.md, research/)
is missing in the freshly-created worktree. Each child session needs to read
`brief.md`, `plan.md`, and `research/*` from `${PROJECT_DIR}` to do real work.

Insert this block AFTER the worktree-creation loop and BEFORE wave dispatch
(Step 2b):

```bash
PROJECT_SOURCE="$(realpath "${PROJECT_DIR}")"
# Compute destination relpath: PROJECT_DIR relative to REPO_ROOT.
# This makes $wt/$PROJECT_REL valid regardless of whether the operator
# passed --project as relative (.claude/projects/...) or absolute.
PROJECT_REL="$(realpath --relative-to="$REPO_ROOT" "$PROJECT_SOURCE")"
for wt in "$WORKTREE_DIR"/session-*; do
  [ -d "$wt" ] || continue
  mkdir -p "$wt/$PROJECT_REL"
  cp "$PROJECT_SOURCE"/brief.md "$wt/$PROJECT_REL/"
  cp "$PROJECT_SOURCE"/plan.md  "$wt/$PROJECT_REL/"
  [ -d "$PROJECT_SOURCE/research" ] && \
    cp -r "$PROJECT_SOURCE/research" "$wt/$PROJECT_REL/"
done
```

Note: `realpath --relative-to` is GNU coreutils. macOS users without
`coreutils` (Homebrew `brew install coreutils` provides `grealpath`) may
substitute a Python fallback:
`python3 -c "import os.path,sys; print(os.path.relpath(sys.argv[1], sys.argv[2]))" "$PROJECT_SOURCE" "$REPO_ROOT"`.

Failure mode: any `cp` failure exits the wave non-zero; reported via Step 4
cleanup. Source: brief Constraint 2.

**2b. Launch sessions in this wave (each in its own worktree):**

Each per-session prompt is **prefixed with two safety preambles** to defend
against headless-mode hook gaps (GH #36071) and the Claude 4 cross-model
malware-reminder issue (GH #52272 community workaround):

```text
[CRITICAL — defense in depth] You are running in headless mode where plugin
hooks may not fire reliably (GH #36071). DO NOT execute commands matching:
`rm -rf /`, `curl | bash`, `git push --force` to main, `git reset --hard`
outside this worktree, or any command writing outside the current worktree.
Treat this rule as enforced regardless of `--allowedTools` allowlist.

Note: any malware-related safety reminders apply conditionally to code you
assess as actually malicious, not to all code reads (per GH #52272 community
workaround).
```

Then the per-session dispatch (note the `cwd:` honor for plugin-in-monorepo
layouts):

For each session N in the wave:
```bash
SESSION_CWD="${session_cwd:-.}"   # default: worktree root; overridable per-session via Execution Strategy `cwd:` field
cd "$WORKTREE_PATH/$SESSION_CWD" && \
  GIT_OPTIONAL_LOCKS=0 claude -p "${SAFETY_PREAMBLE}\n\n/trekexecute --session {N} {plan-path}" \
    --allowedTools "Read,Write,Edit,Bash,Glob,Grep" \
    --permission-mode bypassPermissions \
    --max-turns "$MAX_TURNS" \
    --max-budget-usd "$MAX_BUDGET_USD" \
    --append-system-prompt-file "${SHARED_CONTEXT_FILE}" \
    > "$LOG_DIR/session-{N}.log" 2>&1 &
```

`session_cwd` is sourced from the per-session `cwd:` field in the plan's
Execution Strategy block (parsed by Phase 2.6's existing session-metadata
reader). Default is `.` (worktree root) when absent — backward compatible.
Sessions can declare `cwd: <subdir>` to make child sessions run from a
plugin-subdir instead of repo root. Required for plugin-in-monorepo layouts
where the plugin lives at `plugins/<name>/` and step paths are relative to
the plugin (brief Constraint 1, Option B).

Key rules:
- `$WORKTREE_PATH` is the absolute path to the session's worktree
- `$LOG_DIR` is an absolute path in the main worktree (NOT inside the session worktree)
- `{plan-path}` is the same relative path — it works because the worktree has
  the same repo content from HEAD
- `${SHARED_CONTEXT_FILE}` is absolute (from Step 2a-pre) so child sessions
  can resolve it after `cd`
- If the wave has only 1 session, run without `&` (no background needed)
- Track PIDs for parallel sessions
- `--max-turns` + `--max-budget-usd` are guardrails (research/06 R3 + R4)
- `--append-system-prompt-file` injects shared brief + architecture context
  into every child without bloating the per-session prompt

**2c. Wait for wave completion:**

```bash
wait {PID1} {PID2} ...
```

**2d. Check results after each wave:**

For each session in the wave, read its log file (in `$LOG_DIR`, always accessible
from the main worktree) and grep for `"trekexecute_summary"`. Parse the JSON to
determine:
- Did the session complete? (`result: "completed"`)
- Did it fail? (`result: "failed"` or `"stopped"`)

If ANY session in the wave failed:
```
Wave {W} FAILED: Session {N} failed at step {S}.
Stopping — later waves depend on this wave.
See log: {LOG_DIR}/session-{N}.log
```
Do NOT merge. Do NOT start later waves. Jump to Step 4 (cleanup), then Phase 8.

If all sessions in the wave passed: continue to Step 2e.

**2e. Merge session branches back (SEQUENTIAL, one at a time):**

Return to the main worktree:
```bash
cd "$REPO_ROOT"
```

For each session N in the wave (in order), push BEFORE merge so the wave
branch survives even if cleanup races ahead (research/02 R3 — converts an
unrecoverable failure to a recoverable one). Push failure is non-fatal
(no remote, offline, etc.):
```bash
GIT_OPTIONAL_LOCKS=0 git push origin "trek/{slug}/session-{N}" 2>/dev/null || true
GIT_OPTIONAL_LOCKS=0 git merge --no-ff "trek/{slug}/session-{N}" \
  -m "merge: trekplan session {N} — {session-title}"
```

If the merge succeeds (exit code 0): continue to next session.

If the merge fails (conflict):
```bash
CONFLICTS="$(git diff --name-only --diff-filter=U)"
git merge --abort
```

Report:
```
Wave {W} MERGE CONFLICT: Session {N} branch conflicts with merged state.
Conflicting files:
{CONFLICTS}

Session {N} log: {LOG_DIR}/session-{N}.log
Aborting further merges. Sessions already merged in this wave are preserved.
```

Mark remaining sessions as "merge-failed". Jump to Step 4 (cleanup), then Phase 8.

**2f. Remove worktrees for completed wave:**

After successful merge of all sessions in the wave:
```bash
for each session N in the wave:
  GIT_OPTIONAL_LOCKS=0 git worktree remove "$WORKTREE_DIR/session-{N}" --force
  GIT_OPTIONAL_LOCKS=0 git branch -d "trek/{slug}/session-{N}"
done
GIT_OPTIONAL_LOCKS=0 git worktree prune
```

Report: `Wave {W}: {N} sessions merged, worktrees cleaned up.`

Continue to the next wave.

### Step 3 — Run master verification

After all waves complete and merge successfully, run the plan's `## Verification`
section commands to verify the integrated result.

### Step 4 — Cleanup (ALWAYS runs, even on failure)

This step MUST execute regardless of how Step 2 exited — success, failure, or
merge conflict. It is the worktree equivalent of a `finally` block.

```bash
cd "$REPO_ROOT"

# Remove any remaining worktrees (GIT_OPTIONAL_LOCKS=0 to avoid lock-file races)
for wt in "$WORKTREE_DIR"/session-*; do
  [ -d "$wt" ] && GIT_OPTIONAL_LOCKS=0 git worktree remove "$wt" --force 2>/dev/null
done
GIT_OPTIONAL_LOCKS=0 git worktree prune

# Remove session branches
GIT_OPTIONAL_LOCKS=0 git branch --list "trek/{slug}/*" | while read branch; do
  GIT_OPTIONAL_LOCKS=0 git branch -D "$branch" 2>/dev/null
done

# Clean up empty directories
rmdir "$WORKTREE_DIR" 2>/dev/null
```

Report:
```
Cleanup: {N} worktrees removed, {N} branches deleted.
```

If cleanup fails for any worktree, report but do not fail:
```
Warning: failed to remove worktree {path}. Manual cleanup:
  git worktree remove {path} --force && git worktree prune
```

### Step 5 — Aggregate results

Collect all session summaries into an aggregated report. Jump to Phase 8.

### --session N mode

When mode = `session N`:
1. Find session N in the Execution Strategy
2. Extract its step numbers (e.g., Steps: 4, 5, 6)
3. Extract its scope fence (Touch / Never touch lists)
4. Execute ONLY those steps, in order, using the single-session protocol (Phase 3→7)
5. Enforce the session's scope fence as if it were a session spec's scope fence
6. Report results for those steps only

This mode is used internally by Phase 2.6 when launching child sessions.
It can also be used manually to re-run a specific session.

When `--session N` is invoked inside a git worktree (as done by Phase 2.6), all
git operations (add, commit) apply to the worktree's branch. The session does not
need to know it is in a worktree — git handles this transparently.

## Phase 3 — Progress file setup

The progress file location depends on whether `--project` is in use:

- **With `--project <dir>`:** progress lives at `{project_dir}/progress.json`.
- **Without `--project`:** progress lives at
  `{plan-dir}/.trekexecute-progress-{slug}.json`, where `{slug}` is the plan
  filename without extension.

**Session-scoped naming:** When `mode = session N`:
- With `--project`: `{project_dir}/progress-session-{N}.json`.
- Without `--project`: `{plan-dir}/.trekexecute-progress-{slug}-session-{N}.json`.

This prevents merge conflicts when parallel sessions each write their own
progress file.

### Progress file schema

```json
{
  "schema_version": "1",
  "plan": "{path}",
  "plan_type": "{plan | session-spec}",
  "started_at": "{ISO-8601}",
  "updated_at": "{ISO-8601}",
  "mode": "{execute | resume | step}",
  "total_steps": 0,
  "current_step": 0,
  "status": "{in-progress | completed | failed | stopped}",
  "steps": {
    "1": { "status": "pending", "attempts": 0, "error": null, "completed_at": null, "commit": null }
  },
  "entry_condition_checked": false,
  "exit_condition_checked": false,
  "summary": null
}
```

### Mode-specific behavior

**mode = execute (fresh):**
- If a progress file exists with status `in-progress` or `failed`: warn that
  `--resume` is available, then wait 3 seconds (`sleep 3`) and start fresh.
  This allows headless runs to proceed without blocking.
- Otherwise: create the progress file with all steps in `pending` status.

**mode = resume:**
- If no progress file exists: start from step 1 (same as fresh execute).
- If progress file exists: find the first step with status != `passed`.
  ```
  Resuming from step {N}. {M}/{total} steps already completed.
  ```

**mode = dry-run:**
- Do NOT create or modify the progress file.

**mode = step N:**
- Create the progress file if it does not exist.
- Only step N will be executed.

## Phase 4 — Entry condition check (session specs only)

**Skip for trekplans.** Skip in dry-run mode (report what would be checked instead).

Read the entry condition. Evaluate it:

- `"none"` or similar → pass immediately
- References git state (e.g., "git status clean") → run `git status --porcelain`
- References passing tests → run the specified command
- References a previous session → check `git log --oneline` for commit pattern

If the entry condition **fails**:
```
Entry condition FAILED: {condition text}
Reason: {what was checked, what was found}
Complete the prerequisite first, then re-run.
```
Update progress file with `status: "stopped"`. Stop execution.

**Also write `.session-state.local.json`** (Handover 7) with the same
`status: "stopped"` and `next_session_brief_path` pointing at the brief that
the failed entry-condition session was supposed to consume. This lets
`/trekcontinue` surface the stop in the next session. Use the same atomic
write pattern + validator check as Phase 2.55. On validator failure, warn
but do not block.

**Also write sibling `NEXT-SESSION-PROMPT.local.md`** with frontmatter
(`produced_by: trekexecute`, `produced_at: <ISO-8601>`,
`status: stopped`) — same combined ESM pattern as Phase 8 — so Phase 1.5
of `/trekcontinue` can compare project-dir and plugin-root candidates.

If the entry condition **passes**:
```
Entry condition: PASS
```
Update `entry_condition_checked: true` in the progress file.

## Phase 5 — Dry-run report (dry-run mode only)

**Only runs when mode = dry-run.** Produces a validation report, then stops.

```
## Dry Run Report: {filename}

**Type:** {plan | session-spec}
**Steps:** {N}

### Step Validation

| Step | Description | Verify | On failure | Checkpoint | Issues |
|------|-------------|--------|------------|------------|--------|
| 1 | {desc} | {cmd} | {action} | {msg} | {none / missing X} |

### File References

{For each file in Files: fields, check existence with Glob}
- {path}: EXISTS | NOT FOUND {(marked as new file) | (unexpected — may be missing)}

### Entry / Exit Conditions (session specs)

{What would be checked}

### Execution Preview (only when plan has Execution Strategy)

If `has_execution_strategy = true`, show a preview of multi-session orchestration:

```
**Sessions:** {S} across {W} waves

| Wave | Session | Steps | Depends on | Command |
|------|---------|-------|------------|---------|
| 1 | Session 1: {title} | {nums} | none | `claude -p "/trekexecute --session 1 {path}"` |
| 1 | Session 2: {title} | {nums} | none | `claude -p "/trekexecute --session 2 {path}"` |
| 2 | Session 3: {title} | {nums} | S1, S2 | `claude -p "/trekexecute --session 3 {path}"` |
```

Check billing status via `echo "${ANTHROPIC_API_KEY:+SET}"` and report:
```
Billing: ANTHROPIC_API_KEY is {SET — parallel sessions will bill API account | NOT SET — sessions will use subscription}
```

### Verdict

{READY | NEEDS ATTENTION — N issues found}
```

Stop after the dry-run report. Do not execute anything.

## Phase 6 — Step execution loop

The core execution phase. Runs for modes: `execute`, `resume`, `step`.

### Determine starting step

- **execute**: step 1
- **resume**: first step where status != `passed`
- **step N**: step N only

### For each step

Update progress: `steps.{N}.status = "running"`, `current_step = N`, `updated_at = now`.

```
--- Step {N}/{total}: {description} ---
```

#### Sub-step A — Scope fence check (session specs only)

Before touching any file, verify that every file in the step's `Files:` field is
in the session spec's Touch list (or is a new file to create). If ANY file is in
the Never-touch list:

```
SCOPE VIOLATION: Step {N} requires {file} which is in the never-touch list.
Escalating — this step cannot be executed within this session's scope.
```

Treat this as an automatic `escalate`. Jump to the stop-and-report logic.

#### Sub-step B — Test first (if present)

If the step has a `Test first:` field:
1. If test file is marked `(new)`: note it will be created during implementation.
2. If test file exists: run it. Expect failure (RED state).
3. If test unexpectedly passes: warn but continue — step may already be done.

Do not block on test-first failures — they are expected.

#### Sub-step C — Implement changes

Read the step's `Files:` and `Changes:` fields. Implement exactly as described.

**Rules:**
- Follow `Changes:` exactly — do not improvise, add scope, or optimize
- Use Edit for modifications, Write for new files
- If `Reuses:` references existing code, read that code first for context
- Only touch files listed in `Files:` — nothing else

#### Sub-step D1 — Command verification

**Security check (mandatory):** Before running the Verify command, check it against
the executor security denylist. If the command matches ANY of these patterns,
**refuse to execute** — treat as `On failure: escalate` regardless of the plan's
On failure setting:

- `rm -rf` or `rm -fr` with any path
- `chmod 777` or `chmod -R 777`
- Pipe-to-shell: `curl ... | bash`, `wget ... | sh`, `base64 ... | bash`
- `eval` with variable expansion: `eval $VAR`, `eval $(cmd)`, `` eval `cmd` ``
- `mkfs`, `dd` writing to block devices (`/dev/sd*`, `/dev/nvme*`)
- `shutdown`, `reboot`, `halt`, `poweroff`
- Fork bomb patterns
- `crontab` writes, `/etc/cron*` modifications
- `kill -9 -1` or `pkill -9 -1` (kill all processes)
- `history -c` or truncating `~/.bash_history`

If matched:
1. Do NOT execute the command
2. Set step status = "failed"
3. Log: `SECURITY: Verify command blocked — matches executor denylist: {pattern name}`
4. Apply `On failure: escalate` regardless of the plan's On failure setting
5. Include in final report under a "Security blocks" section

If the command passes the security check, run it:

Run the `Verify:` command exactly as written, via Bash.

**Rules:**
- Always a fresh run — never trust prior results
- Exit code is the authoritative truth:
  - Exit 0 + expected output (if specified) = **PASS**
  - Exit non-zero = **FAIL** regardless of output text
  - Exit 0 but wrong output = **FAIL**

```
Verify: {command}
Result: {PASS | FAIL} (exit code {N})
{if FAIL}: Output (first 10 lines): {output}
```

**Step 0 sentinel:** if the Verify command exits `77` AND the step's manifest
has `sandbox_preflight: true`, do NOT treat as a normal failure. Set step
status = `blocked` and apply `On failure: escalate`. Skip D2 manifest
verification (nothing to verify for a read-only sandbox test). Commit none —
jump straight to Phase 7 with a structured "sandbox-blocked" reason.

If **PASS**: proceed to Sub-step D2 (manifest verification).

#### Sub-step D2 — Manifest verification

After the Verify command passes, verify the step's Manifest block. This is
the objective completion predicate: a step is not passed until its manifest
holds, regardless of Verify exit code.

**Checks to run (in order):**

1. **Expected paths exist:** for each `expected_paths` entry, verify the
   file exists in the repo. Count how many exist.
2. **min_file_count satisfied:** count from (1) must be ≥ `min_file_count`.
3. **Forbidden paths untouched:** for each `forbidden_paths` entry, run
   `git diff --name-only HEAD~{attempts} HEAD -- {path}` (since this step
   began). Any modified forbidden path = fail.
4. **Bash syntax check:** for each entry in `bash_syntax_check` AND any
   `.sh` file that appears in `git diff --name-only HEAD~1 HEAD` (safety
   net for unlisted scripts), run `bash -n {path}`. Non-zero exit = fail.
5. **must_contain patterns:** for each `{path, pattern}` pair, run
   `grep -E "{pattern}" {path}`. No match = fail.

**On manifest failure:**

```
Manifest verification FAIL for step {N}:
- {which check failed with detail}
```

Apply the step's `On failure:` clause (same as Sub-step D1 failure). Increment
attempts. Manifest failures count against the retry cap equally with command
failures.

If all manifest checks pass: proceed to Sub-step F (checkpoint).

Record per-step manifest audit result in progress file:
`steps.{N}.manifest_audit = "pass" | "fail"`,
`steps.{N}.manifest_drift = [{check: reason, ...}]` on fail.

#### Sub-step E — On failure handling

If **FAIL**, read the `On failure:` clause. Apply the retry cap: **maximum 2 retries**
(3 total attempts). Track attempts in `steps.{N}.attempts`.

**`On failure: revert`**
- If attempts < 3: analyze the failure, re-implement with adjustments, re-verify.
  ```
  Attempt {A}/3 failed. Retrying...
  ```
- If attempts == 3: revert this step's changes:
  ```bash
  git checkout -- {files from Files: field}
  ```
  Record failure. **Do NOT proceed to next step.** Jump to Phase 7.

**`On failure: retry`**
- If attempts < 3: use the alternative approach described in the On failure clause.
- If attempts == 3: revert and stop. Jump to Phase 7.

**`On failure: skip`**
- Mark step as skipped regardless of attempt count. Continue to next step.
  ```
  Step {N}: SKIPPED (non-critical per plan)
  ```
  Update `steps.{N}.status = "skipped"`.

**`On failure: escalate`**
- Stop immediately regardless of attempt count.
  ```
  Step {N}: ESCALATED — requires human judgment
  ```
  Commit all completed work before stopping. Stage ONLY files from steps with
  `status: "passed"` in the progress file — collect their `Files:` fields. Never
  use `git add -A` (risks staging secrets, binaries, or unrelated work).
  ```bash
  git add {files from passed steps' Files: fields} && git commit -m "wip: trekexecute stopped at step {N} — escalation needed"
  ```
  Jump to Phase 7.

#### Sub-step F — Checkpoint

Run the `Checkpoint:` git commit command exactly as written in the plan.

If the commit fails (nothing to commit, etc.): warn but do NOT fail the step.
The step's verification already passed — the commit is bookkeeping.

```
Step {N}: PASS (committed: {hash})
```

**Commit-message-pattern check:** after the commit succeeds, read the HEAD
commit message (`git log -1 --pretty=%s`) and match it against the step's
`commit_message_pattern`. Mismatch does NOT fail the step (verification
already passed) but is recorded as `checkpoint_drift: {expected_pattern,
actual_message}` in the progress file. Phase 7.5 audit reports drift as
an advisory.

Update progress: `steps.{N}.status = "passed"`, `steps.{N}.commit = {hash}`,
`steps.{N}.completed_at = now`, `steps.{N}.manifest_audit = "pass"`.

### Step mode exit

If mode = `step N`: after completing step N (pass or fail), skip remaining steps
and jump to Phase 8 (final report).

## Phase 7 — Exit condition check (session specs only)

**Skip for trekplans.** Run only when all steps passed (not on early stop).

Run each exit condition command from the `## Exit Condition` checklist:

```
Exit condition check:
- [ ] {command} → {PASS | FAIL}
- [ ] {command} → {PASS | FAIL}
```

If all pass: `exit_condition_checked: true` in progress file.
If any fail: record which failed. Include in final report.

## Phase 7.5 — Manifest audit (independent)

**Runs for all modes except dry-run.** This is the last-line-of-defense
check: it ignores the executor's own per-step bookkeeping and re-verifies
session-wide state directly from the filesystem and git log. If the audit
disagrees with the executor's self-report, the audit wins.

This phase exists because agents can hallucinate completion. Phase 7.5
produces an independent truth based on objective state, not self-narrative.

**Steps:**

1. **Enumerate expected paths:** aggregate `expected_paths` across every
   step manifest (and the session_manifest, if present). Deduplicate.

2. **Filesystem check:** for each expected_path, confirm the file exists.

3. **Commit-count check:** count commits since session-start
   (`git rev-list --count {start_sha}..HEAD`). Expected count =
   number of steps with `status=passed` (excludes Step 0 if sandbox_preflight).

4. **Commit-message pattern sweep:** walk `git log {start_sha}..HEAD` and
   confirm each commit matches one of the declared
   `commit_message_patterns` (in any order — order is advisory).

5. **Bash syntax sweep:** for every `.sh` file in
   `git diff --name-only {start_sha} HEAD`, run `bash -n`. Collect failures.

6. **Forbidden-path sweep:** for each `scope_forbidden` entry, check
   `git diff --name-only {start_sha} HEAD -- {path}` is empty.

Compare the audit result against the executor's `progress.status`:

- **Audit pass + progress=completed:** status stays `completed`.
- **Audit fail + progress=completed:** OVERRIDE to `partial`. The executor
  believed it was done; the filesystem says otherwise. This is the Wave 1
  hallucination case — the audit is the defense.
- **Audit fail + progress=failed/stopped:** status stays as-is; drift is
  informational.

Record in progress file:
- `manifest_audit.status = "pass" | "drift"`
- `manifest_audit.drift_details = [{check, expected, actual}, ...]`

## Phase 7.6 — Recovery dispatch (multi-session parent context only)

**Preconditions:**
- This is the parent trekexecute invocation (not a child `--session N`)
- Phase 7.5 reported `drift`
- `recovery_depth < 2` (hard cap to prevent infinite loops)

**Skip otherwise.** Recovery in child context or at depth 2+ escalates to
the user for manual resolution.

**Synthesize a recovery session spec:**

1. Determine the missing step numbers from `manifest_audit.drift_details`
   (steps whose expected_paths are absent or whose commits are missing).
2. Read the original session spec. Copy only the missing steps (preserving
   their Manifest blocks) into a new file:
   `{output_dir}/session-{N}-recovery-{depth}.md`
3. Populate `## Recovery Metadata`:
   - `recovery_of: {original session spec path}`
   - `recovery_depth: {current depth + 1}`
   - `missing_steps: [N, M, ...]`
   - `entry_condition_override: "previous partial session committed at {sha}"`
   - `parent_progress_file: {path}`
4. Prepend the synthetic Step 0 pre-flight (same as normal session specs).
5. Set `recovery_dispatched = true` in parent progress file.

**Invoke the recovery session:**

```bash
cd "$WORKTREE_PATH" && claude -p "/trekexecute --session {N} {recovery spec path}" \
  --allowedTools "Read,Write,Edit,Bash,Glob,Grep" \
  --permission-mode bypassPermissions \
  > "$LOG_DIR/session-{N}-recovery-{depth}.log" 2>&1
```

Wait for the recovery session to complete. After it returns, re-run Phase
7.5 audit one more time. If it still drifts at `recovery_depth=2`:

```
RECOVERY EXHAUSTED: session {N} drifted after 2 recovery attempts.
Missing: {steps and details}
Status: partial (recovery_depth=2, escalated to user)
```

Do NOT dispatch a third recovery. Report to the user.

## Phase 8 — Final report

Always produce a final report.

### Main-merge gate (MAIN_MERGE_GATE — always pauses)

Before writing the final progress + state files (and before any "merge to
main" prose), emit the `main-merge-gate` lifecycle event so observability
and operator tooling can see that the pipeline reached its terminal
boundary:

```bash
node ${CLAUDE_PLUGIN_ROOT}/lib/stats/event-emit.mjs \
  --event main-merge-gate \
  --payload "{\"plan\":\"${PLAN_PATH}\",\"wave_count\":${WAVE_COUNT}}"
```

Pause for operator confirmation via `AskUserQuestion`:

**Question:** "All waves merged. Ready to proceed with the main-merge step
and finalize the run?"

| Option | Action |
|--------|--------|
| **Confirm — main-merge** | Emit `main-merge-approved`, proceed to write progress + state files. |
| **Decline — hold for review** | Emit `main-merge-declined`. Wave commits remain on their session branches for inspection. The run is recorded as `partial`. |

This pause is **always on**, regardless of `gates_mode`. The `--gates` flag
re-enables earlier per-wave pauses; this gate is the one boundary that
ALWAYS pauses on every run — it is the safety stop between completed waves
and the merge that publishes the integrated result.

On confirm:
```bash
node ${CLAUDE_PLUGIN_ROOT}/lib/stats/event-emit.mjs \
  --event main-merge-approved \
  --payload "{\"plan\":\"${PLAN_PATH}\"}"
```

On decline:
```bash
node ${CLAUDE_PLUGIN_ROOT}/lib/stats/event-emit.mjs \
  --event main-merge-declined \
  --payload "{\"plan\":\"${PLAN_PATH}\",\"reason\":\"${reason}\"}"
```

**Recovery surface:** if declined, `--resume re-enters at the gate` —
re-running `/trekexecute --resume --project <dir>` jumps directly
back to the main-merge gate AskUserQuestion (skipping completed waves).
The wave session branches are preserved for inspection (Hard Rule 19's
push-before-cleanup ensures they survive on the remote even if local
cleanup ran). To inspect: `git log trekplan/<slug>/session-N` per wave.

### Progress + state-file writes

Update progress file: `status` to `completed`/`failed`/`stopped`, `updated_at`, `summary`.

**Also atomically write `.session-state.local.json`** (Handover 7) at this
convergence point — every successful, failed, stopped, or partial run that
reaches Phase 8 must produce or refresh the state file. Schema:

```json
{
  "schema_version": 1,
  "project": "{project_dir}",
  "next_session_brief_path": "{determined from Execution Strategy: next session's brief path, or current brief path if last session}",
  "next_session_label": "{label of next session from Execution Strategy, or 'Complete' if last}",
  "status": "{same as progress.json status — completed | failed | stopped | partial}",
  "updated_at": "{ISO-8601 now}"
}
```

Use `lib/util/atomic-write.mjs` (`atomicWriteJson`) — same crash-safety as
`progress.json`. Then verify: `node lib/validators/session-state-validator.mjs
--json {project_dir}/.session-state.local.json`. On validator failure, warn
to stderr but do NOT block — Phase 8 must always reach the final report.

**Also write sibling `NEXT-SESSION-PROMPT.local.md`** (Bug 3 frontmatter
contract — see `docs/HANDOVER-CONTRACTS.md` § Handover 7 Lifecycle) in the
project directory. The frontmatter MUST contain `produced_by: trekexecute`
and `produced_at: <ISO-8601>` so `/trekcontinue` Phase 1.5 can detect
producer drift between project-dir and plugin-root candidates. Use a single
ESM inline block so state-file + prompt-file writes succeed or fail together:

```bash
!`node --input-type=module -e "
import path from 'node:path';
import { writeFileSync } from 'node:fs';
import { atomicWriteJson } from './lib/util/atomic-write.mjs';
const [, dir, briefPath, label, status] = process.argv;
const now = new Date().toISOString();
const stateObj = { schema_version: 1, project: dir, next_session_brief_path: briefPath, next_session_label: label, status, updated_at: now };
atomicWriteJson(path.join(dir, '.session-state.local.json'), stateObj);
const promptBody = '---\\nproduced_by: trekexecute\\nproduced_at: ' + now + '\\nproject: ' + dir + '\\nstatus: ' + status + '\\n---\\n\\n# ' + label + '\\n\\nResume via /trekcontinue.\\n';
writeFileSync(path.join(dir, 'NEXT-SESSION-PROMPT.local.md'), promptBody);
" '{project_dir}' '{next_session_brief_path}' '{next_session_label}' '{status}'`
```

This single insertion covers every multi-session execution path that
converges here (Path A: successful single session, Path B: `--session N`
explicit, Path C: compaction-survival recovery, Path D: standard plan
completion). Phase 2.55 and Phase 4 cover the early-stop paths E1/E2.
Phase 2.3 (validate exit) and Phase 5 (dry-run) intentionally do not write
— neither path is resumable.

```
## Ultraexecute Local Complete

**Plan:** {path}
**Type:** {plan | session-spec}
**Mode:** {execute | resume | step N}
**Result:** {COMPLETED | FAILED at step N | STOPPED (escalation) | PARTIAL (N/total passed)}

### Step Results

| Step | Description | Result | Attempts | Commit | Manifest |
|------|-------------|--------|----------|--------|----------|
| 0 | Sandbox pre-flight | PASS | 1 | — | n/a |
| 1 | {desc} | PASS | 1 | abc1234 | pass |
| 2 | {desc} | FAIL | 3 | — | — |
| 3 | {desc} | — | 0 | — | — |

### Manifest Audit (Phase 7.5)

- **Status:** {pass | drift}
- **Drift details:** {enumerated; empty on pass}
- **Recovery dispatched:** {true | false}
- **Recovery depth:** {N}
- **Legacy plan:** {true | false}

### Summary

- Passed: {N}/{total}
- Skipped: {N}
- Failed: {N}
- Not reached: {N}
- Blocked (sandbox): {N}

{if all passed + exit condition passed}:
All steps completed. Exit condition: PASS.

{if failed/stopped}:
### Failure Details

Step {N}: {description}
On failure: {action}
Error: {error output, first 20 lines}
Attempts: {N}

### What Remains

{Numbered list of unexecuted steps}

To resume: /trekexecute --resume {path}
```

**Result vocabulary (v1.7, strict):**
- `completed` — all steps passed AND Phase 7.5 manifest audit passed
- `partial` — steps passed per executor but Phase 7.5 found drift, OR
  Phase 7.6 recovery incomplete
- `blocked` — Step 0 sandbox pre-flight exited 77; no real work attempted
- `failed` — a step failed and On failure was revert/retry (retry cap hit)
- `stopped` — On failure: escalate triggered

**JSON summary block** (always at the end, machine-parseable):

```json
{
  "trekexecute_summary": {
    "plan": "{path}",
    "plan_type": "{plan | session-spec}",
    "plan_version": "{1.7 | 1.6 | legacy}",
    "result": "{completed | partial | blocked | failed | stopped}",
    "steps_total": 0,
    "steps_passed": 0,
    "steps_failed": 0,
    "steps_skipped": 0,
    "steps_not_reached": 0,
    "steps_blocked": 0,
    "failed_at_step": null,
    "exit_condition": "{pass | fail | skipped | n/a}",
    "manifest_audit": "{pass | drift | n/a}",
    "drift_details": [],
    "recovery_dispatched": false,
    "recovery_depth": 0,
    "legacy_plan": false,
    "progress_file": "{path}"
  }
}
```

The `trekexecute_summary` key makes it grep-able in log files from headless runs.

## Phase 9 — Stats tracking

Append one record to `${CLAUDE_PLUGIN_DATA}/trekexecute-stats.jsonl`:

```json
{
  "ts": "{ISO-8601}",
  "plan": "{filename only}",
  "plan_type": "{plan | session-spec}",
  "mode": "{execute | resume | dry-run | step}",
  "result": "{completed | failed | stopped | partial}",
  "steps_total": 0,
  "steps_passed": 0,
  "steps_failed": 0,
  "steps_skipped": 0,
  "failed_at_step": null
}
```

If `${CLAUDE_PLUGIN_DATA}` is not set or not writable, skip silently.
Never let stats failures block the workflow.

## Hard rules

1. **No AskUserQuestion for execution decisions.** All execution decisions come
   from the plan's On failure clauses. If the plan says escalate, stop and
   report — never ask. **Exception:** the billing safety check in Phase 2.6
   Step 0 MUST ask before spending money on the user's API account.

2. **No scope creep.** Only touch files listed in the step's `Files:` field.
   If a file outside the list seems to need changing, record it as a finding
   in the final report — do not touch it.

3. **Exit code is truth.** The Verify command's exit code is authoritative.
   Non-zero = FAIL regardless of output. Zero with wrong output = FAIL.

4. **Fresh verification.** Re-run the Verify command from scratch every time.
   Never trust cached or prior results.

5. **Retry cap = 3 attempts.** Initial + 2 retries, then stop. Never loop forever.

6. **Never corrupt completed work.** Only revert files from the failing step.
   Never touch files from earlier passed steps.

7. **Checkpoint discipline.** Run the Checkpoint commit exactly as written.
   Do not combine, reorder, or skip checkpoints on passed steps.

8. **Scope fence enforcement.** For session specs: never modify files in the
   Never-touch list, regardless of what the Changes field says.

9. **Progress file is ground truth.** Resume uses the progress file, not git log.

10. **No sub-agents.** The executor reads and implements directly.
    No Agent tool, no TeamCreate, no delegation.

11. **Worktree isolation is mandatory for parallel execution.** Every parallel
    `claude -p` session MUST run in its own git worktree. Never launch two or
    more sessions in the same working directory. This rule has no exceptions.
    Sequential (single-session) execution does not require worktrees.

12. **Worktree cleanup is unconditional.** Before producing the final report
    (Phase 8), always remove all worktrees and session branches created during
    this execution, even if the run failed or was stopped. Leaked worktrees
    consume disk space and block future runs. If automated cleanup fails,
    report the manual cleanup commands in the final report.

13. **Merge sequentially, abort on conflict.** After a parallel wave completes,
    merge each session's branch into the main branch one at a time with
    `--no-ff`. If any merge produces a conflict, run `git merge --abort`,
    report the conflicting files, and do not attempt further merges. Never use
    `--force` or `--strategy-option theirs/ours` to silently resolve conflicts.

14. **Verify command security check.** Before executing any `Verify:` or
    `Checkpoint:` command, check it against the executor security denylist
    (Sub-step D). If the command matches a blocked pattern, escalate
    immediately — do not execute, do not retry.

15. **No writing outside the repository.** During step execution, never write
    files outside the git repository root (`git rev-parse --show-toplevel`).
    Exception: `.claude/` paths for plans, progress files, and stats,
    AND `~/.claude/projects/*/memory/` paths for memory files when the
    step's manifest declares `memory_write: true`. The `memory_write: true`
    opt-in is a second gate: unauthorized memory writes still fail. This
    prevents escape-from-repo attacks where a plan step modifies home
    directory or system files, while permitting the narrow case of
    deliberate memory updates.

16. **No writing to security-sensitive paths.** Never write to `.git/hooks/`
    (git hook injection), `~/.ssh/`, `~/.aws/`, `~/.gnupg/`, `.env` files,
    shell configs (`~/.zshrc`, `~/.bashrc`, `~/.profile`), or
    `.claude/settings.json` / `.claude/hooks/` (infrastructure self-modification).

17. **Manifest is the completion predicate.** A step may not be marked passed
    if its manifest does not verify, regardless of the Verify command's exit
    code. The manifest is the objective contract — Verify is necessary but
    not sufficient. For v1.7+ plans: a Verify pass with a manifest fail sets
    step result to `failed` and triggers the On-failure clause. For legacy
    v1.6 plans: synthesized manifests apply with the same force, but
    `legacy_plan: true` is logged in progress.

18. **Last-activity rule.** The executor's final tool call before writing
    Phase 8 must be a manifest check (Phase 7.5 audit), never an arbitrary
    file review. This prevents the "hallucinated completion" failure mode
    where a transcript ends on an unrelated Read and the agent self-reports
    `completed` without verifying. If Phase 7.5 has not run, the executor
    may not emit `result: completed` under any circumstances.

19. **push-before-cleanup.** After successful `git merge --no-ff` of a wave
    branch, run `git push origin <branch>` BEFORE `git worktree remove` and
    `git branch -d`. Push failure is non-fatal (no remote, offline, etc.) —
    cleanup proceeds regardless. Rationale: this converts an unrecoverable
    failure (worktree removed, branch deleted, work lost) into a recoverable
    one (push succeeded, branch preserved on remote). Source: research/02 R3.
