| name | titan-run |
| description | Run the full Titan Paradigm pipeline end-to-end by dispatching each phase to sub-agents with fresh context windows. Orchestrates recon → gauntlet → sync → forge → grind (+ repo-provided parity audit) automatically. |
Titan RUN — End-to-End Pipeline Orchestrator
You are the orchestrator for the full Titan Paradigm pipeline. Your job is to dispatch each phase to a sub-agent (fresh context window), validate the results, and loop phases that require multiple invocations — all without human intervention.
You are lightweight. You do NOT run codegraph commands, audit files, or make code changes yourself. You only: (1) spawn sub-agents, (2) read and validate state files, (3) decide what to run next.
Arguments (from $ARGUMENTS):
- No args → full pipeline from scratch, target
.
<path> → target path (passed to recon)
--skip-recon → skip recon (assumes artifacts exist)
--skip-gauntlet → skip gauntlet (assumes artifacts exist)
--start-from <phase> → jump to phase: recon, gauntlet, sync, forge, grind, parity, close
--gauntlet-batch-size <N> → batch size for gauntlet (default: 5)
--yes → skip all confirmation prompts in the orchestrator (pre-pipeline, forge checkpoint, and resume prompts) and in forge (per-phase confirmation)
Step 0 — Pre-flight
-
Worktree check:
git rev-parse --show-toplevel && git worktree list
The Titan pipeline writes artifacts and makes code changes — worktree isolation is required. If you are NOT in a worktree, create one automatically using the EnterWorktree tool (fetch it via ToolSearch if needed) with name titan-run. Do NOT stop and ask the user — just enter the worktree and continue.
-
Parse arguments. Determine:
targetPath (default: .)
startPhase (default: recon)
gauntletBatchSize (default: 5)
autoConfirm → true if --yes is present, otherwise false
-
Check existing state. Read .codegraph/titan/titan-state.json if it exists.
- If state exists and
--start-from not specified, ask user: "Existing Titan state found (phase: <currentPhase>). Resume from current state, or start fresh with /titan-reset first?"
- If
--yes is set, resume automatically.
Initialize the phase timestamps helper. Throughout the pipeline, you will record wall-clock timestamps for each phase. Use this helper to write them into titan-state.json:
node -e "const fs=require('fs');const s=JSON.parse(fs.readFileSync('.codegraph/titan/titan-state.json','utf8'));s.phaseTimestamps=s.phaseTimestamps||{};s.phaseTimestamps['<PHASE>']=s.phaseTimestamps['<PHASE>']||{};if(!s.phaseTimestamps['<PHASE>'].startedAt){s.phaseTimestamps['<PHASE>'].startedAt=new Date().toISOString();fs.writeFileSync('.codegraph/titan/titan-state.json',JSON.stringify(s,null,2));}"
node -e "const fs=require('fs');const s=JSON.parse(fs.readFileSync('.codegraph/titan/titan-state.json','utf8'));s.phaseTimestamps=s.phaseTimestamps||{};s.phaseTimestamps['<PHASE>']=s.phaseTimestamps['<PHASE>']||{};s.phaseTimestamps['<PHASE>'].completedAt=new Date().toISOString();fs.writeFileSync('.codegraph/titan/titan-state.json',JSON.stringify(s,null,2));"
Replace <PHASE> with recon, gauntlet, sync, forge, grind, parity, or close. Run the start command immediately before dispatching each phase's first sub-agent, and the completion command immediately after post-phase validation passes. If resuming a phase (e.g., gauntlet loop iteration 2+), do NOT overwrite startedAt — only set it if it doesn't already exist.
Timestamp validation: After recording completedAt for any phase, verify startedAt < completedAt:
node -e "const s=JSON.parse(require('fs').readFileSync('.codegraph/titan/titan-state.json','utf8'));const p=s.phaseTimestamps?.['<PHASE>'];if(p?.startedAt&&p?.completedAt){const start=new Date(p.startedAt),end=new Date(p.completedAt);if(end<=start){console.log('WARNING: <PHASE> completedAt ('+p.completedAt+') is not after startedAt ('+p.startedAt+')');process.exit(0);}console.log('<PHASE> duration: '+((end-start)/60000).toFixed(1)+' min');}else{console.log('WARNING: <PHASE> missing startedAt or completedAt');}"
If the check fails, log a warning but do not stop the pipeline — clock skew or immediate completion of short phases can cause this.
-
Install latest codegraph (once, before any sub-agent runs):
npm install -g @optave/codegraph@latest
Log the installed version:
codegraph --version
If the install fails, warn the user but continue with whichever version is currently available.
-
Sync with main (once, before any sub-agent runs):
git fetch origin main && git merge origin/main --no-edit
If merge conflict → stop: "Merge conflict after syncing with main. Resolve conflicts and re-run /titan-run."
-
Bootstrap worktree environment (run when pwd contains .claude/worktrees/ or node_modules is absent/stale):
E1. Node dependencies: Check whether node_modules needs installing:
if [ ! -f node_modules/.package-lock.json ] || [ package.json -nt node_modules/.package-lock.json ]; then
npm install --prefer-offline 2>&1 | tail -5
fi
This also builds WASM grammars (via the prepare script). After this step, all subsequent agent prompts must use TEST_CMD="npx vitest run --config vitest.config.worktree.ts" (see E2).
E2. Vitest worktree config: The default vitest.config.ts excludes **/.claude/**, matching this path and silently breaking test discovery. Write an override if absent:
if [ ! -f vitest.config.worktree.ts ]; then
node -e "
const fs = require('fs');
const src = fs.readFileSync('vitest.config.ts', 'utf8');
fs.writeFileSync('vitest.config.worktree.ts',
src.replace(/'\*\*\/\.claude\/\*\*',?\s*/g, ''));
console.log('vitest.config.worktree.ts written');
"
fi
All test runs in forge/grind/gate agent prompts must reference this file: npx vitest run --config vitest.config.worktree.ts.
E3. Native binary freshness (only if crates/ exists):
NODE_BINARY=$(find node_modules/@optave -name "codegraph-core.node" 2>/dev/null | head -1)
if [ -n "$NODE_BINARY" ]; then
NEWER_RUST=$(find crates -name "*.rs" -newer "$NODE_BINARY" 2>/dev/null | head -1)
if [ -n "$NEWER_RUST" ]; then
echo "Rust source newer than binary — rebuilding native addon..."
npx napi build --platform --release --manifest-path crates/codegraph-core/Cargo.toml --output-dir .
PLATFORM=$(node -e "const os=require('os');const arch=os.arch()==='arm64'?'arm64':'x64';console.log(os.platform()+'-'+arch)")
codesign --sign - --force codegraph-core.node 2>/dev/null || true
cp codegraph-core.node "node_modules/@optave/codegraph-${PLATFORM}/codegraph-core.node"
echo "Native binary rebuilt."
fi
fi
If any bootstrap step fails, warn but continue — the pipeline can still proceed, and forge/grind agents will catch environment issues at test-run time.
-
Print plan:
Titan Pipeline — End-to-End Run
Target: <path>
Starting from: <phase>
Gauntlet batch size: <N>
Phases: recon → gauntlet (loop) → sync → [PAUSE] → forge (loop) → grind (loop) → close
Each phase runs in a sub-agent with a fresh context window.
Forge requires explicit confirmation (analysis phases are safe to automate).
Grind runs after forge to adopt extracted helpers into consumers.
Start immediately — do NOT ask for confirmation before analysis phases. The user invoked /titan-run; that is the confirmation. Analysis phases (recon, gauntlet, sync) are read-only and safe to automate. The forge checkpoint (Step 3.5b) still applies unless --yes is set.
Pre-Agent Gate (run before EVERY sub-agent dispatch)
Before spawning any sub-agent, run these checks. This catches git state drift, concurrent interference, and corruption left by a crashed agent.
G1. Git health check
git status --porcelain
- Unexpected dirty files (files not in
.codegraph/titan/): Print warning with the file list. Ask user to confirm proceeding, or stop. If --yes, log the warning and continue — but do NOT stage or commit these files.
- Merge conflicts (lines starting with
UU, AA, DD, AU, UA, DU, UD): Stop immediately: "Unresolved merge conflict detected. Resolve before continuing."
G2. Worktree still valid
git rev-parse --is-inside-work-tree
If this fails (worktree was pruned or moved), stop: "Worktree is no longer valid. Create a new one with /worktree."
G3. State file integrity
If .codegraph/titan/titan-state.json should exist at this point (i.e., we're past recon):
node -e "try { JSON.parse(require('fs').readFileSync('.codegraph/titan/titan-state.json','utf8')); console.log('OK'); } catch(e) { console.log('CORRUPT: '+e.message); process.exit(1); }"
- If CORRUPT → attempt recovery from backup:
- Check if
.codegraph/titan/titan-state.json.bak exists.
- If the backup exists, validate it is valid JSON:
node -e "try { JSON.parse(require('fs').readFileSync('.codegraph/titan/titan-state.json.bak','utf8')); console.log('BACKUP OK'); } catch(e) { console.log('BACKUP CORRUPT: '+e.message); process.exit(1); }"
- If the backup is valid → restore it:
cp .codegraph/titan/titan-state.json.bak .codegraph/titan/titan-state.json
- If the backup is also corrupt or missing → stop: "State file corrupted with no valid backup. Run
/titan-reset and start over."
G4. State backup
Before every sub-agent dispatch, back up the current state file:
cp .codegraph/titan/titan-state.json .codegraph/titan/titan-state.json.bak 2>/dev/null || true
If a sub-agent corrupts the state, G3 on the next iteration will detect it and restore from .bak.
Step 0.5 — Artifact pre-validation (phase skip)
Run this step if --start-from was specified, --skip-recon is set, or --skip-gauntlet is set. Any of these flags cause phases to be skipped — their artifacts must exist and be valid before proceeding. When --skip-recon is set, validate recon artifacts. When --skip-gauntlet is set, validate both recon and gauntlet artifacts.
For each phase BEFORE startPhase, run the corresponding V-checks:
| Skipped phase | Required artifacts + checks |
|---|
recon | V1 structural fields only (domains, batches, priorityQueue, stats — skip currentPhase == "recon" check since later phases advance it), V2 (GLOBAL_ARCH.md), V3 (snapshot exists — WARN if missing), V4 (cross-check counts) |
gauntlet | V5 (coverage ≥ 50%), V6 (entry completeness sample), V7 (summary consistency); also run NDJSON integrity check (2c) |
sync | V8 (sync.json structure), V9 (targets trace to gauntlet), V10 (dependency order) |
forge | V14 (final state consistency), V15 (gate log consistency); execution block must exist in titan-state.json |
grind | V14, V15; execution.completedPhases must be non-empty in titan-state.json (forge must have run at least one phase) |
If ANY required artifact is missing → stop: "Cannot start from <phase> — <artifact> is missing. Run the full pipeline or start from an earlier phase."
If ANY V-check that is normally VALIDATION FAILED would fail → stop with the same message as it would during normal execution.
WARN-level V-checks from skipped phases are surfaced as prefixed warnings: "[skipped-phase pre-validation] " — they do not stop the pipeline.
Step 1 — RECON
Skip if: --skip-recon, --start-from is after recon, or titan-state.json already has currentPhase beyond "recon".
1a. Run Pre-Agent Gate (G1-G4)
1a.1. Record phase start timestamp
Record phaseTimestamps.recon.startedAt (only if not already set — it may exist from a prior crashed run).
Note: On a fresh run, titan-state.json does not yet exist (titan-recon creates it in Step 12). Use this safe variant that creates a minimal stub if the file is missing:
node -e "const fs=require('fs');const p='.codegraph/titan/titan-state.json';let s;try{s=JSON.parse(fs.readFileSync(p,'utf8'));}catch{fs.mkdirSync('.codegraph/titan',{recursive:true});s={};}s.phaseTimestamps=s.phaseTimestamps||{};s.phaseTimestamps['recon']=s.phaseTimestamps['recon']||{};if(!s.phaseTimestamps['recon'].startedAt){s.phaseTimestamps['recon'].startedAt=new Date().toISOString();fs.writeFileSync(p,JSON.stringify(s,null,2));}"
This ensures recon.startedAt is recorded even on first-time runs. titan-recon Step 12 merges any existing phaseTimestamps into the full state file it writes.
1b. Dispatch sub-agent
Use the Agent tool to spawn a sub-agent:
prompt: |
You are running the Titan RECON phase. Read and follow the skill file at
.claude/skills/titan-recon/SKILL.md exactly. Target path: <targetPath>.
IMPORTANT: Skip the worktree check (Step 0.1) — the orchestrator already verified this.
IMPORTANT: Skip the "sync with main" step (Step 0.2) — the orchestrator already did this.
Execute Steps 1-13 as documented.
1c. Post-phase validation
After the agent returns, validate the artifacts:
V1. titan-state.json structure:
Read .codegraph/titan/titan-state.json and verify ALL of these fields exist and are non-empty:
version — must be a number
initialized — must be an ISO 8601 string
currentPhase — must equal "recon"
stats.totalNodes — must be > 0
stats.totalEdges — must be > 0
stats.totalFiles — must be > 0
domains — must be an array with length > 0
batches — must be an array with length > 0
priorityQueue — must be an array with length > 0
If any field is missing, zero, or wrong type → VALIDATION FAILED. Print which fields failed and stop: "RECON produced incomplete state. Re-run with /titan-run --start-from recon."
V2. GLOBAL_ARCH.md exists and has content:
Read .codegraph/titan/GLOBAL_ARCH.md:
- Must exist
- Must contain
## Domain Map heading
- Must have > 10 lines
If missing or empty → VALIDATION FAILED.
V3. Snapshot created:
codegraph snapshot list 2>/dev/null | grep titan-baseline || echo "NO_SNAPSHOT"
If NO_SNAPSHOT → WARN (not fatal, but note it: "No baseline snapshot — rollback in GATE will not work").
V4. Cross-check counts:
titan-state.json → stats.totalFiles should roughly match the number of targets across all batches (batches are subsets of files, so sum(batch.files.length) should be ≤ totalFiles)
priorityQueue.length should be > 0 and ≤ totalNodes
- Batch size check: Every batch must have ≤ 5 files. If any batch exceeds 5, WARN: "Batch has files (max 5). Large batches cause context overload in gauntlet sub-agents."
If wildly inconsistent (e.g., 0 batches but 500 nodes) → WARN with details.
Print: RECON validated. Domains: <count>, Batches: <count>, Priority targets: <count>, Quality score: <score>
Record phaseTimestamps.recon.completedAt.
Step 2 — GAUNTLET (loop)
Skip if: --skip-gauntlet or --start-from is after gauntlet.
2a. Pre-loop check
Record phaseTimestamps.gauntlet.startedAt (only if not already set — gauntlet may be resuming).
Read .codegraph/titan/gauntlet-summary.json if it exists:
- If
"complete": true → run gauntlet post-validation (2d) and skip loop if it passes
- Otherwise, count completed batches from
titan-state.json for progress tracking
Compute expectedTargetCount from titan-state.json → priorityQueue.length (or sum of batch file counts). This is the ground truth for "how many targets should gauntlet audit."
2b. Gauntlet loop
Set maxIterations = 50 (safety limit).
Set stallCount = 0, maxStalls = 3 (consecutive no-progress iterations before abort).
previousAuditedCount = titan-state.json → progress.audited (or 0)
iteration = 0
while iteration < maxIterations:
iteration += 1
# Run Pre-Agent Gate (G1-G4)
# Dispatch sub-agent
Agent → "Run /titan-gauntlet with batch size <N>.
Read .claude/skills/titan-gauntlet/SKILL.md and follow it exactly.
Batch size: <gauntletBatchSize>.
Skip worktree check and main sync — already handled.
Process as many batches as context allows, then save state and stop."
# Check completion
Read .codegraph/titan/gauntlet-summary.json (if exists)
if "complete": true → break
# Progress tracking
Read .codegraph/titan/titan-state.json
currentAuditedCount = progress.audited
if currentAuditedCount == previousAuditedCount:
stallCount += 1
Print: "WARNING: Gauntlet iteration <iteration> made no progress (stall <stallCount>/<maxStalls>)"
if stallCount >= maxStalls:
Stop: "Gauntlet stalled for <maxStalls> consecutive iterations at <currentAuditedCount>/<expectedTargetCount> targets. Likely stuck on a problematic target. Check gauntlet.ndjson for the last successful entry and investigate the next target in the batch."
else:
stallCount = 0 # reset on any progress
countBeforeUpdate = previousAuditedCount
previousAuditedCount = currentAuditedCount
# Efficiency check: if progress is very slow (< 2 targets per iteration), warn
# Only fire when stallCount == 0 — if stalled, the stall warning already covers it
targetsThisIteration = currentAuditedCount - countBeforeUpdate
if targetsThisIteration == 1 and iteration > 3 and stallCount == 0:
Print: "WARNING: Only 1 target per iteration — agent may be spending too much context. Consider increasing batch size."
Print: "Gauntlet iteration <iteration>: <currentAuditedCount>/<expectedTargetCount> targets audited"
2c. NDJSON integrity check
After the loop completes (or on each iteration if you prefer lightweight checks):
node -e "
const fs = require('fs');
const path = '.codegraph/titan/gauntlet.ndjson';
if (!fs.existsSync(path)) {
console.log(JSON.stringify({ valid: 0, corrupt: 0, total: 0, missing: true }));
process.exit(0);
}
const lines = fs.readFileSync(path,'utf8').trim().split('\n');
let valid = 0, corrupt = 0;
for (const line of lines) {
try { JSON.parse(line); valid++; } catch { corrupt++; }
}
console.log(JSON.stringify({ valid, corrupt, total: lines.length, missing: false }));
"
- If
missing == true: treat as equivalent to valid == 0 — the file does not exist yet (expected on first iteration, error if the loop should have produced entries).
- If
corrupt > 0: Print "WARNING: corrupt lines in gauntlet.ndjson (likely from a crashed sub-agent). These targets may need re-auditing."
- If
valid == 0 and missing == false: Stop: "gauntlet.ndjson has no valid entries. Something went wrong."
2d. Post-loop validation
V5. Gauntlet coverage:
- Count distinct
target values in gauntlet.ndjson (valid lines only)
- Compare against
expectedTargetCount
- If coverage < 80%: WARN "Gauntlet only audited / targets (%). Consider re-running with
/titan-run --start-from gauntlet."
- If coverage < 50%: VALIDATION FAILED. Stop.
V6. Gauntlet entry completeness (sample check):
Read first 5 and last 5 entries from gauntlet.ndjson. Each entry MUST have:
target — non-empty string
file — non-empty string
verdict — one of PASS, WARN, FAIL, DECOMPOSE
pillarVerdicts — object with keys I, II, III, IV
metrics — object with at least cognitive and cyclomatic (numeric)
violations — array
If any sampled entry is missing required fields → WARN: "Gauntlet entry for is incomplete — sub-agent may have skipped rules. Fields missing: ."
V7. Summary consistency:
Read gauntlet-summary.json:
summary.totalAudited should equal the valid NDJSON line count
summary.pass + summary.warn + summary.fail + summary.decompose should equal summary.totalAudited
If mismatched → WARN with details (not fatal — the NDJSON is the source of truth, summary is derived).
Print: GAUNTLET validated. Audited: <N>/<M> targets. Pass: <N>, Warn: <N>, Fail: <N>, Decompose: <N>. NDJSON integrity: <valid>/<total> lines OK.
Record phaseTimestamps.gauntlet.completedAt.
Step 3 — SYNC
Skip if: --start-from is after sync, or titan-state.json has currentPhase: "sync" with existing sync.json.
3a. Run Pre-Agent Gate (G1-G4)
3a.1. Record phase start timestamp
Record phaseTimestamps.sync.startedAt.
3b. Dispatch sub-agent
Agent → "Run /titan-sync. Read .claude/skills/titan-sync/SKILL.md and follow it exactly.
Skip worktree check and main sync — already handled.
Read GAUNTLET artifacts and produce sync.json."
3c. Post-phase validation
Pre-V8. Validate sync.json is parseable JSON:
node -e "try { JSON.parse(require('fs').readFileSync('.codegraph/titan/sync.json','utf8')); console.log('JSON OK'); } catch(e) { console.log('JSON INVALID: '+e.message); process.exit(1); }"
If this fails → VALIDATION FAILED. Stop: "SYNC produced invalid JSON in sync.json (likely a mismatched bracket/brace). Re-run with /titan-run --start-from sync."
V8. sync.json structure:
Read .codegraph/titan/sync.json and verify:
phase — must equal "sync"
executionOrder — must be an array with length > 0
- Each entry in
executionOrder must have: phase (number), label (string), targets (array), commit (string)
executionOrder phases must be in ascending order
- No duplicate phase numbers
If missing or structurally invalid → VALIDATION FAILED. Stop: "SYNC produced invalid plan. Re-run with /titan-run --start-from sync."
V9. Sync targets trace back to gauntlet batches:
Collect all file paths from titan-state.json → batches[*].files (the ground-truth set gauntlet audited).
Flatten all targets from sync.json → executionOrder[*].targets and check each against that set.
const state = JSON.parse(fs.readFileSync('.codegraph/titan/titan-state.json','utf8'));
const batchFiles = new Set((state.batches || []).flatMap(b => b.files || []));
const allSyncTargets = sync.executionOrder.flatMap(e => e.targets);
if (allSyncTargets.length === 0) { console.log('V9 FAIL: sync.json has no targets — execution plan is empty'); process.exit(1); }
const missingCount = allSyncTargets.filter(t => !batchFiles.has(t)).length;
const pct = missingCount / allSyncTargets.length;
if (pct > 0.2) console.log('V9 WARN:', missingCount + '/' + allSyncTargets.length,
'sync targets not in any gauntlet batch (' + (pct*100).toFixed(0) + '%) — agent may have hallucinated targets');
else console.log('V9 OK —', allSyncTargets.length, 'targets, all traced to gauntlet batches');
Note: sync targets are file-level paths (src/foo.ts). Do NOT compare against gauntlet.ndjson → target fields — those are function-level names and will never match.
V10. Execution order dependency check:
For entries with dependencies arrays, verify that each dependency phase number exists in executionOrder and has a lower phase number. Circular dependencies in the execution plan → VALIDATION FAILED.
Print: SYNC validated. Execution phases: <N>, Total targets: <N>, Estimated commits: <N>.
Record phaseTimestamps.sync.completedAt.
Step 3.5 — Pre-forge: Architectural Snapshot + Human Checkpoint
Skip if: --start-from is forge AND .codegraph/titan/arch-snapshot.json already exists. The existing snapshot is the correct pre-forge baseline — re-capturing it mid-forge would overwrite it with a state that already includes prior forge commits, making gate A1/A3 comparisons inaccurate.
If --start-from forge is used without an existing snapshot, run Step 3.5a normally (captures a pre-forge baseline from the current committed state).
3.5a. Capture architectural snapshot
Before any code changes, snapshot the codebase's architectural properties. This becomes the baseline for the architectural comparator in /titan-gate (Step 5.5).
codegraph communities -T --json > .codegraph/titan/arch-snapshot-communities.json
codegraph structure --depth 2 --json > .codegraph/titan/arch-snapshot-structure.json
codegraph communities --drift -T --json > .codegraph/titan/arch-snapshot-drift.json
Combine into a single snapshot file:
TITAN_HEAD_SHA=$(git rev-parse HEAD)
node -e "
const fs = require('fs');
try {
const communities = JSON.parse(fs.readFileSync('.codegraph/titan/arch-snapshot-communities.json','utf8'));
const structure = JSON.parse(fs.readFileSync('.codegraph/titan/arch-snapshot-structure.json','utf8'));
const drift = JSON.parse(fs.readFileSync('.codegraph/titan/arch-snapshot-drift.json','utf8'));
const snapshot = {
timestamp: new Date().toISOString(),
capturedBefore: 'forge',
headSha: '$TITAN_HEAD_SHA',
communities,
structure,
drift
};
fs.writeFileSync('.codegraph/titan/arch-snapshot.json', JSON.stringify(snapshot, null, 2));
} catch (e) {
console.error('WARNING: Failed to build arch-snapshot.json: ' + e.message);
console.error('Architectural comparison (titan-gate A1/A3/A4) will be skipped.');
}
"
Clean up temp files:
rm -f .codegraph/titan/arch-snapshot-communities.json .codegraph/titan/arch-snapshot-structure.json .codegraph/titan/arch-snapshot-drift.json
This snapshot is read by /titan-gate Step 5.5 during every commit validation.
3.5b. Human checkpoint
This is a mandatory pause. Analysis phases (recon, gauntlet, sync) are read-only. FORGE makes real code changes and commits. The user must see the plan.
First, determine the snapshot status from Step 3.5a:
snapshotStatus = file_exists('.codegraph/titan/arch-snapshot.json') ? "captured" : "FAILED — gate A1/A3/A4 will be skipped"
Divergence check — compute before printing the checkpoint so the warning appears inline:
if git fetch origin main 2>/dev/null; then
mergeBase=$(git merge-base HEAD origin/main)
mainAdvance=$(git rev-list --count $mergeBase..origin/main)
else
mainAdvance="unknown"
fi
mainAdvanceNote = mainAdvance == "unknown"
? "NOTE: Could not fetch origin/main — skipping divergence check."
: mainAdvance > 0
? "WARNING: main has advanced <mainAdvance> commits since initial sync.\nIf significant, consider re-running: /titan-run --start-from recon"
: ""
Print:
================================================================
ANALYSIS COMPLETE — FORGE CHECKPOINT
================================================================
The analysis phases (recon → gauntlet → sync) are done.
FORGE will now make code changes and commit them.
Execution plan summary:
Phase 1: <label> — <N> targets
Phase 2: <label> — <N> targets
...
Total: <N> phases, <N> targets, <N> estimated commits
Architectural snapshot: <snapshotStatus>
<mainAdvanceNote>
Validation layers per commit:
1. Diff Review — does the change match the gauntlet recommendation and sync plan?
2. Titan Gate — structural checks, semantic assertions, architectural comparison, lint/build/test
Proceed with /titan-forge? [y/n]
(Use --yes to skip this checkpoint in future runs)
================================================================
If --yes is NOT set: stop and wait for user confirmation. Do NOT proceed.
If --yes IS set: print the summary but continue automatically.
Once the user confirms (or --yes was set), autoConfirm is already true (set from --yes at parse time). If the user confirmed interactively (without --yes), set autoConfirm = true now — forge sub-agents must receive --yes to avoid per-phase confirmation prompts that cannot be answered in a sub-agent context.
Step 4 — FORGE (loop)
4a. Pre-loop check
Record phaseTimestamps.forge.startedAt (only if not already set — forge may be resuming).
Read .codegraph/titan/sync.json → count total phases in executionOrder.
Read .codegraph/titan/titan-state.json → check execution.completedPhases (may not exist yet if forge hasn't started).
If .codegraph/titan/arch-snapshot.json does not exist:
Print: "NOTE: No arch-snapshot.json found. Architectural comparison in /titan-gate (Step 5.5) will be skipped for this run. To enable it, run '/titan-run --start-from sync' to re-capture the pre-forge snapshot."
4b. Forge loop
Set maxIterations = 20 (safety limit).
Set stallCount = 0, maxStalls = 2 (forge stalls are more serious — fewer retries).
previousCompletedPhases = execution.completedPhases (or [])
previousCompletedTargets = execution.completedTargets (or [])
previousFailedTargets = execution.failedTargets (or [])
iteration = 0
while iteration < maxIterations:
iteration += 1
# Run Pre-Agent Gate (G1-G4) — CRITICAL for forge since it commits
# Context: show recent commits so agent has context for V12 commit audit
Print: git log --oneline -5
# Record the HEAD sha before dispatching
headBefore = $(git rev-parse HEAD)
# Determine next phase
Read .codegraph/titan/titan-state.json
completedPhases = execution.completedPhases (or [])
totalPhases = len(sync.json.executionOrder)
if len(completedPhases) >= totalPhases → break
nextPhase = first phase number NOT in completedPhases
# Dispatch sub-agent
yesFlag = "--yes" if autoConfirm else ""
Agent → "Run /titan-forge --phase <nextPhase> <yesFlag>.
Read .claude/skills/titan-forge/SKILL.md and follow it exactly.
Skip worktree check and main sync — already handled.
For each target, the validation flow is:
1. Apply code change
2. Stage files
3. Diff review (Step 9 in forge) — verify diff matches gauntlet recommendation and sync plan intent
4. Run tests
5. Run /titan-gate — read .claude/skills/titan-gate/SKILL.md and follow it exactly.
Gate now includes semantic assertions (Step 5) and architectural snapshot comparison (Step 5.5).
The arch snapshot is at .codegraph/titan/arch-snapshot.json.
6. Commit on success, rollback on failure
Do NOT skip the diff review step — it catches intent drift before gate even runs."
# Post-agent checks
headAfter = $(git rev-parse HEAD)
# V11. Verify commits were actually made (unless all targets failed)
Read .codegraph/titan/titan-state.json
newCompletedPhases = execution.completedPhases (or [])
newCompletedTargets = execution.completedTargets (or [])
newFailedTargets = execution.failedTargets (or [])
# Count total targets processed (succeeded + failed) to distinguish true stalls from all-fail phases
newProcessedCount = len(newCompletedTargets) + len(newFailedTargets)
previousProcessedCount = len(previousCompletedTargets) + len(previousFailedTargets)
if newCompletedPhases == previousCompletedPhases and newProcessedCount == previousProcessedCount:
stallCount += 1
Print: "WARNING: Forge iteration <iteration> made no progress (stall <stallCount>/<maxStalls>)"
if stallCount >= maxStalls:
Stop: "Forge stalled on phase <nextPhase> for <maxStalls> consecutive iterations. Check titan-state.json → execution.failedTargets for details."
else:
stallCount = 0
previousCompletedPhases = newCompletedPhases
previousCompletedTargets = newCompletedTargets
previousFailedTargets = newFailedTargets
# V12. Commit audit — verify commits match expectations
if headAfter != headBefore:
# Get commits made by this agent
git log --oneline <headBefore>..<headAfter>
commitCount = number of commits
Print: "Forge phase <nextPhase>: <commitCount> commits, <completedCount> targets completed, <failedCount> targets failed"
else:
# No commits but phase may have completed (all targets failed/skipped)
Print: "Forge phase <nextPhase>: no commits (all targets failed or skipped)"
# V13. Test suite still green after forge commits (skip if no commits were made)
if headAfter != headBefore:
# Detect test command (same detection as gate Step 4):
testCmd = node -e "const p=require('./package.json');const s=p.scripts||{};const script=s.test?'test':s['test:ci']?'test:ci':null;if(!script){console.log('NO_TEST_SCRIPT');process.exit(0);}const fs=require('fs');const runner=fs.existsSync('yarn.lock')?'yarn':fs.existsSync('pnpm-lock.yaml')?'pnpm':fs.existsSync('bun.lockb')?'bun':'npm';console.log(runner+(script==='test'?' test':' run '+script));"
if testCmd == "NO_TEST_SCRIPT":
Print: "V13: No test script configured — skipping post-forge test run."
else:
Run: <testCmd> 2>&1
if tests fail:
Print: "WARNING: Test suite has failures after forge phase <nextPhase>. Auto-spawning regression-fix agent."
Print: "Failing tests: <list of failing test files>"
Print: "Commits from this phase: git log --oneline <headBefore>..<headAfter>"
# Run Pre-Agent Gate (G1-G4) and back up state
# Then dispatch fix agent:
Agent → "Investigate and fix test regressions introduced by the last forge phase.
Commits in scope: git log --oneline <headBefore>..<headAfter>
Failing tests: <list>
Steps:
1. Read the failing tests to understand what they expect
2. Identify which committed change broke them (git diff <headBefore>..<headAfter>)
3. Fix the root cause — prefer targeted fixes over reverts
4. If Rust source changed: rebuild the native addon before running tests
npx napi build --platform --release --manifest-path crates/codegraph-core/Cargo.toml --output-dir .
PLATFORM=$(node -e "const os=require('os');const arch=os.arch()==='arm64'?'arm64':'x64';console.log(os.platform()+'-'+arch)")
if [[ "$(uname)" == "Darwin" ]]; then codesign --sign - --force codegraph-core.node; fi
cp codegraph-core.node "node_modules/@optave/codegraph-${PLATFORM}/codegraph-core.node"
5. Verify with: <testCmd>
6. Commit fixes with: 'fix: correct regressions from forge phase <N>'
7. Do NOT stage: package-lock.json, vitest.config.worktree.ts, tests/benchmarks/resolution/fixtures/python/__pycache__/
Run all commands INLINE (not in background)."
# After fix agent returns, re-run tests:
Run: <testCmd> 2>&1
if tests still fail:
Print: "CRITICAL: Regression-fix agent could not resolve test failures after forge phase <nextPhase>. Manual intervention required."
Print: "Consider reverting: git revert <headBefore>..HEAD"
Stop.
else:
Print: "V13: Regressions resolved by fix agent. Continuing pipeline."
4c. Post-loop validation
V14. Final state consistency:
Read .codegraph/titan/titan-state.json:
execution.completedPhases should contain all phase numbers from sync.json → executionOrder
execution.commits should be an array (may be empty if all targets failed)
- Every commit SHA in
execution.commits should exist in git log:
git cat-file -t <sha>
If any SHA doesn't exist → WARN: "Commit recorded in state but not found in git history. State may be out of sync."
V15. Gate log consistency:
If .codegraph/titan/gate-log.ndjson exists:
- Count PASS vs FAIL entries
- Every FAIL entry with
"rolledBack": true should NOT have a corresponding commit in execution.commits
Print forge summary.
Record phaseTimestamps.forge.completedAt.
Step 4.5 — GRIND (loop)
Grind runs after forge to close the adoption loop. Forge extracts helpers; grind wires them into consumers and removes dead code. Without grind, the dead symbol count inflates with every forge phase.
Skip if: --start-from is parity or close, or titan-state.json → grind.completedPhases already covers all forge phases.
4.5a. Pre-loop check
Record phaseTimestamps.grind.startedAt (only if not already set — grind may be resuming).
Read .codegraph/titan/sync.json → count total phases in executionOrder.
Read .codegraph/titan/titan-state.json → check grind.completedPhases (may not exist yet if grind hasn't started).
4.5b. Grind loop
Set maxIterations = 20 (safety limit — same as forge).
Set stallCount = 0, maxStalls = 2.
previousGrindPhases = grind.completedPhases (or [])
iteration = 0
while iteration < maxIterations:
iteration += 1
# Run Pre-Agent Gate (G1-G4)
# Determine next forge phase to grind
Read .codegraph/titan/titan-state.json
grindCompleted = grind.completedPhases (or [])
forgePhases = execution.completedPhases (or [])
ungroundPhases = forgePhases.filter(p => !grindCompleted.includes(p))
if len(ungroundPhases) == 0 → break
nextPhase = ungroundPhases[0]
headBefore = $(git rev-parse HEAD)
yesFlag = "--yes" if autoConfirm else ""
Agent → "Run /titan-grind --phase <nextPhase> <yesFlag>.
Read .claude/skills/titan-grind/SKILL.md and follow it exactly.
Skip worktree check and main sync — already handled.
For each dead helper from forge phase <nextPhase>:
1. Classify: adopt / re-export / promote / false-positive / intentionally-private / remove
2. For adopt/re-export/promote: wire consumers, stage, run /titan-gate, commit
3. For remove: delete, stage, run /titan-gate, commit
4. Gate on dead-symbol delta at phase end"
# Post-agent checks
headAfter = $(git rev-parse HEAD)
Read .codegraph/titan/titan-state.json
newGrindPhases = grind.completedPhases (or [])
if newGrindPhases == previousGrindPhases:
stallCount += 1
Print: "WARNING: Grind iteration <iteration> made no progress (stall <stallCount>/<maxStalls>)"
if stallCount >= maxStalls:
Stop: "Grind stalled on phase <nextPhase>. Check titan-state.json → grind for details."
else:
stallCount = 0
previousGrindPhases = newGrindPhases
# V16. Commit audit
if headAfter != headBefore:
git log --oneline <headBefore>..<headAfter>
commitCount = number of commits
Print: "Grind phase <nextPhase>: <commitCount> adoption commits"
else:
Print: "Grind phase <nextPhase>: no adoptions needed (forge wired everything correctly)"
# V17. Test suite still green (same as forge V13)
if headAfter != headBefore:
testCmd = <same detection as forge V13>
if testCmd != "NO_TEST_SCRIPT":
Run: <testCmd> 2>&1
if tests fail:
Print: "WARNING: Test suite has failures after grind phase <nextPhase>. Auto-spawning regression-fix agent."
Print: "Failing tests: <list of failing test files>"
Print: "Commits from this phase: git log --oneline <headBefore>..<headAfter>"
# Run Pre-Agent Gate (G1-G4) and back up state
# Then dispatch fix agent:
Agent → "Investigate and fix test regressions introduced by the last grind phase.
Commits in scope: git log --oneline <headBefore>..<headAfter>
Failing tests: <list>
Steps:
1. Read the failing tests to understand what they expect
2. Identify which committed change broke them (git diff <headBefore>..<headAfter>)
3. Fix the root cause — prefer targeted fixes over reverts
4. If Rust source changed: rebuild the native addon before running tests
npx napi build --platform --release --manifest-path crates/codegraph-core/Cargo.toml --output-dir .
PLATFORM=$(node -e "const os=require('os');const arch=os.arch()==='arm64'?'arm64':'x64';console.log(os.platform()+'-'+arch)")
if [[ "$(uname)" == "Darwin" ]]; then codesign --sign - --force codegraph-core.node; fi
cp codegraph-core.node "node_modules/@optave/codegraph-${PLATFORM}/codegraph-core.node"
5. Verify with: <testCmd>
6. Commit fixes with: 'fix: correct regressions from grind phase <N>'
7. Do NOT stage: package-lock.json, vitest.config.worktree.ts, tests/benchmarks/resolution/fixtures/python/__pycache__/
Run all commands INLINE (not in background)."
# After fix agent returns, re-run tests:
Run: <testCmd> 2>&1
if tests still fail:
Print: "CRITICAL: Regression-fix agent could not resolve test failures after grind phase <nextPhase>. Manual intervention required."
Print: "Consider reverting: git revert <headBefore>..HEAD"
Stop.
else:
Print: "V17: Regressions resolved by fix agent. Continuing pipeline."
4.5c. Post-loop validation
V18. Dead-symbol delta:
Read grind.deadSymbolBaseline and grind.deadSymbolCurrent from titan-state.json.
- If delta > 10: WARN "Grind could not fully adopt forge's helpers. new dead symbols remain."
- Otherwise: Print summary.
V19. Grind coverage:
- Count forge phases processed vs total forge phases
- If < 100%: WARN with details
Print grind summary:
GRIND complete.
Dead symbols: <baseline> → <current> (delta: <+/-N>)
Adoptions: <N> helpers wired, <N> removed, <N> false positives logged
Phases ground: <N>/<M>
Record phaseTimestamps.grind.completedAt.
Step 4.6 — PARITY (conditional, repo-provided)
Some repos ship multiple implementations of the same logic that must stay in lockstep (e.g. a dual native/WASM engine, a client and server copy of a validator). Forge and grind edit code across the tree; this step verifies those edits didn't leave one implementation behind.
titan-run is repo-agnostic — never assume the target repo has engines, fixtures, or any parity surface. The contract: a repo opts in by shipping its own /parity skill at .claude/skills/parity/SKILL.md (wrapping whatever audit mechanism it uses internally). No skill → no parity phase.
4.6a. Detect the repo's parity mechanism
test -f .claude/skills/parity/SKILL.md && echo "PARITY SKILL FOUND" || echo "NO PARITY SKILL"
- NO PARITY SKILL → print
"PARITY skipped — repo provides no /parity skill." and continue to Step 5. Absence is normal for most repos; do not warn.
- PARITY SKILL FOUND → continue below.
Skip also if: --start-from is close, or the pipeline made no code changes this run (titan-state.json → execution.commits empty/absent AND no grind adoption commits) — unless --start-from parity was given explicitly, which always runs the audit.
4.6b. Record phase start
Record phaseTimestamps.parity.startedAt.
headBefore=$(git rev-parse HEAD)
4.6c. Run Pre-Agent Gate (G1-G4)
4.6d. Dispatch sub-agent
Agent → "Run /parity. Read .claude/skills/parity/SKILL.md and follow it exactly.
Skip worktree check — already handled.
Audit every surface the skill covers. Fix any divergence introduced by
recent commits at the root cause, commit the fixes, and re-verify until
the audit is clean. If a divergence pre-dates this run (verify via
git log on the relevant files), follow the skill's and repo's rules for
pre-existing findings (typically: file an issue, don't expand scope)."
4.6e. Post-phase validation
After the agent returns:
headAfter=$(git rev-parse HEAD)
git status --short → the working tree must be clean. The sub-agent commits its fixes; uncommitted changes mean it stopped mid-fix → stop and report.
- If the agent fixed divergences, run V16-style commit audit:
git log --oneline $headBefore..$headAfter and print the parity-fix commits.
- If the agent reports divergences introduced by THIS run that it could not fix → stop: "PARITY failed — this run introduced implementation drift. Fix before CLOSE or revert the offending commits." Pre-existing divergences filed as issues are not blockers; print the issue URLs.
Print: "PARITY complete: <clean | N divergences fixed | N pre-existing filed as issues>"
Record phaseTimestamps.parity.completedAt.
Step 5 — CLOSE (report + PRs)
After forge completes, dispatch /titan-close to produce the final report with before/after metrics and split commits into focused PRs.
5a. Run Pre-Agent Gate (G1-G4)
5a.1. Record phase start timestamp
Record phaseTimestamps.close.startedAt.
5b. Dispatch sub-agent
Agent → "Run /titan-close. Read .claude/skills/titan-close/SKILL.md and follow it exactly.
Skip worktree check and main sync — already handled."
5c. Post-phase validation
After the agent returns, verify:
.codegraph/titan/TITAN_REPORT.md or generated/titan/titan-report-*.md exists and has content (> 20 lines)
- Print: "CLOSE complete. Report: "
If the agent created PRs, print the PR URLs.
Commit hygiene check: If .claude/skills/ files were modified during this run (e.g., by a V13 regression-fix agent), they must NOT share a commit with .codegraph/titan/ artifacts. Check:
git status --short | grep -E "\.claude/skills/|generated/titan/|\.codegraph/titan/"
If both are dirty, commit them separately before proceeding to the retrospective.
Record phaseTimestamps.close.completedAt.
Step 6 — RETROSPECTIVE (automatic)
After CLOSE, run a brief retrospective on this pipeline execution and offer to apply findings as skill improvements.
6a. Collect run data
Read from disk (no sub-agent needed):
node -e "
const fs = require('fs');
const s = JSON.parse(fs.readFileSync('.codegraph/titan/titan-state.json','utf8'));
const ts = s.phaseTimestamps || {};
const phases = ['recon','gauntlet','sync','forge','grind','parity','close'];
for (const p of phases) {
if (ts[p]?.startedAt && ts[p]?.completedAt) {
const dur = ((new Date(ts[p].completedAt) - new Date(ts[p].startedAt))/60000).toFixed(1);
console.log(p + ': ' + dur + ' min');
}
}
const stalls = s.gauntletStalls || 0;
const forgeStalls = s.execution?.stallCount || 0;
const failed = (s.execution?.failedTargets || []).length;
const grindDelta = (s.grind?.deadSymbolCurrent || 0) - (s.grind?.deadSymbolBaseline || 0);
console.log('gauntlet stalls:', stalls, '| forge stalls:', forgeStalls, '| failed targets:', failed, '| grind delta:', grindDelta);
"
6b. Dispatch retrospective agent
Agent → "Analyze this Titan pipeline run and produce a retrospective.
Read:
- .codegraph/titan/titan-state.json (phase timestamps, stalls, failed targets)
- .codegraph/titan/gate-log.ndjson (PASS/WARN/FAIL verdicts per commit)
- .codegraph/titan/gauntlet-summary.json
- .codegraph/titan/issues.ndjson (bugs and anomalies logged during the run)
Produce a JSON file at .codegraph/titan/retrospective.json:
{
'wentWell': [string], // phases/outcomes that ran cleanly
'anomalies': [ // anything that required retries, fixes, or manual intervention
{ phase: string, description: string }
],
'skillRecs': [ // concrete skill improvements, highest-impact first
{ priority: 'P1'|'P2'|'P3', area: string, problem: string, fix: string }
]
}
Keep skillRecs to 3-5 items max. Focus on structural issues (things that would affect every run), not one-off incidents."
6c. Print retrospective summary
After the agent writes retrospective.json, read and print it:
node -e "
const fs = require('fs');
if (!fs.existsSync('.codegraph/titan/retrospective.json')) { console.log('retrospective.json not written — skipping summary'); process.exit(0); }
const r = JSON.parse(fs.readFileSync('.codegraph/titan/retrospective.json','utf8'));
console.log('\n=== Titan Retrospective ===');
console.log('\nWent well:');
(r.wentWell || []).forEach(w => console.log(' ✓', w));
if ((r.anomalies||[]).length) {
console.log('\nAnomalies:');
r.anomalies.forEach(a => console.log(' ⚠', a.phase + ':', a.description));
}
if ((r.skillRecs||[]).length) {
console.log('\nSkill improvement recommendations:');
r.skillRecs.forEach(rec => console.log(' ' + rec.priority + ' [' + rec.area + '] ' + rec.fix));
}
console.log('');
"
6d. Offer skill improvement
Ask the user:
Would you like me to apply these recommendations to improve the /titan-run skill? [y/n]
If --yes is set → apply automatically without asking.
If yes (or --yes):
Run Pre-Agent Gate (G1-G4), back up state, then:
Agent → "Improve .claude/skills/titan-run/SKILL.md based on these recommendations:
<paste skillRecs from retrospective.json>
Rules:
- Use the Edit tool — make targeted changes only, do not rewrite unrelated sections
- One logical change per edit call
- After editing, verify the file is internally consistent (read the changed sections)
- Commit with: 'fix(titan-run): <one-line summary of improvements applied>'
- Do NOT stage: package-lock.json, vitest.config.worktree.ts, .codegraph/titan/"
If no: Print "Retrospective saved to .codegraph/titan/retrospective.json — apply recommendations later by editing .claude/skills/titan-run/SKILL.md." and finish.
Error Handling
- Sub-agent returns error: Print the error, stop, and tell the user which phase failed and how to retry (e.g., "Run
/titan-run --start-from gauntlet").
- State file missing when expected: Stop with clear message about which prerequisite phase to run.
- State file corrupt (JSON parse error): Attempt restore from
.bak. If no backup → stop: "State file corrupted. Run /titan-reset and start over."
- NDJSON corrupt lines: Warn but continue — partial results are better than none. The corrupt lines are logged so the user knows which targets to re-audit.
- Merge conflict detected by pre-agent gate: Stop immediately with the conflicting files listed.
- Tests fail after forge/grind phase: Auto-spawn a regression-fix agent. If the fix agent resolves the failures, continue the pipeline. If it cannot, stop and print the failing commits so the user can revert.
- Parity audit fails on drift introduced by this run: Stop before CLOSE. Retry with
/titan-run --start-from parity after fixing or reverting.
- Validation failure (any V-check marked FAILED): Stop with details. Warn-level V-checks are logged but don't stop the pipeline.
Rules
- You are the orchestrator, not the executor. Never run codegraph commands, edit source files, or make commits yourself. Only spawn sub-agents and read state files. Exceptions (pure validation/snapshot, no code changes): the post-forge test run (V13), NDJSON integrity checks, the V3 baseline snapshot check (
codegraph snapshot list), and the pre-forge architectural snapshot capture (Step 3.5a) are run directly by the orchestrator.
- Run the Pre-Agent Gate (G1-G4) before EVERY sub-agent. No exceptions.
- One sub-agent at a time. Phases are sequential — recon before gauntlet, gauntlet before sync, sync before forge, forge before grind, grind before parity (when the repo provides one), parity before close.
- Fresh context per sub-agent. This is the whole point — each sub-agent gets a clean context window.
- Read AND validate state files after every sub-agent. Trust the on-disk state, not the sub-agent's text output — but verify the state is structurally sound.
- Back up state before every sub-agent. The
.bak file is your safety net against mid-write crashes.
- Mandatory pause before forge unless
--yes is set. Analysis is safe; code changes deserve human review.
- Stall detection is strict for forge (2 retries) and looser for gauntlet (3 retries) since gauntlet is more likely to hit context limits legitimately.
- Respect --start-from. Skip phases before the specified starting point, but verify their artifacts exist AND pass validation.
- Pass --yes through to forge if the user provided it, so forge skips its per-phase confirmation prompt. Within the orchestrator,
--yes also skips the pre-pipeline, forge checkpoint, and resume prompts.
Self-Improvement
This skill lives at .claude/skills/titan-run/SKILL.md. It improves itself automatically via Step 6 (RETROSPECTIVE) at the end of every run.
For manual edits: use the Edit tool for targeted changes. Never rewrite sections unrelated to the fix. Commit skill changes separately from pipeline artifacts (generated/titan/, .codegraph/titan/) — they must not share a commit.
When adding new phases: update meta.phases at the top, add the phase to the timestamp list in Step 0.3, add a V-check for the phase's key artifact, and add the phase to Step 0.5's pre-validation table.