# Monitor and Poll

Check status of a SF project, handle blockers, track costs, and decide next actions.

## Checking Project State

The `query` command is your primary monitoring tool. It's instant (~50ms), costs nothing (no LLM), and returns the full project snapshot.

```bash
cd /path/to/project
sf headless query
```

### Key fields to inspect

```bash
# Overall status
sf headless query | jq '{
  phase: .state.phase,
  milestone: .state.activeMilestone.id,
  slice: .state.activeSlice.id,
  task: .state.activeTask.id,
  progress: .state.progress,
  cost: .cost.total
}'

# What should happen next
sf headless query | jq '.next'
# Returns: { "action": "dispatch", "unitType": "execute-task", "unitId": "M001/S01/T01" }

# Is it done?
sf headless query | jq '.state.phase'
# "complete" = done, "blocked" = needs you, anything else = in progress
```

### Phase meanings

| Phase | Meaning | Your action |
|-------|---------|-------------|
| `pre-planning` | Milestone exists, no slices planned yet | Run `auto` or `next` |
| `needs-discussion` | Ambiguities need resolution | Supply answers or run with defaults |
| `discussing` | Discussion in progress | Wait |
| `researching` | Codebase/library research | Wait |
| `planning` | Creating task plans | Wait |
| `executing` | Writing code | Wait |
| `verifying` | Checking must-haves | Wait |
| `summarizing` | Recording what happened | Wait |
| `advancing` | Moving to next task/slice | Wait |
| `evaluating-gates` | Quality checks before execution | Wait or run `next` |
| `validating-milestone` | Final milestone checks | Wait |
| `completing-milestone` | Archiving and cleanup | Wait |
| `complete` | Done | Verify deliverables |
| `blocked` | Needs human input | Handle blocker (see below) |
| `paused` | Explicitly paused | Resume with `auto` |

## Handling Blockers

When exit code is `10` or phase is `blocked`:

```bash
# 1. Understand the blocker
sf headless query | jq '{phase: .state.phase, blockers: .state.blockers, nextAction: .state.nextAction}'

# 2. Option A: Steer around it
sf headless steer "Skip the database dependency, use in-memory storage instead"

# 3. Option B: Supply pre-built answers
cat > fix.json << 'EOF'
{
  "questions": { "blocked_question_id": "workaround_option" },
  "defaults": { "strategy": "first_option" }
}
EOF
sf headless --answers fix.json auto

# 4. Option C: Force a specific phase
sf headless dispatch replan

# 5. Option D: Escalate to user
echo "SF build blocked. Phase: $(sf headless query | jq -r '.state.phase')"
echo "Manual intervention required."
```

## Cost Tracking

```bash
# Current cumulative cost
sf headless query | jq '.cost.total'

# Per-worker breakdown
sf headless query | jq '.cost.workers'

# After a step (from HeadlessJsonResult)
RESULT=$(sf headless --output-format json next 2>/dev/null)
echo "$RESULT" | jq '.cost'
```

### Budget enforcement pattern

```bash
MAX_BUDGET=15.00

check_budget() {
  TOTAL=$(sf headless query | jq -r '.cost.total')
  OVER=$(echo "$TOTAL > $MAX_BUDGET" | bc -l)
  if [ "$OVER" = "1" ]; then
    echo "Budget exceeded: \$$TOTAL > \$$MAX_BUDGET"
    sf headless stop
    return 1
  fi
  return 0
}
```

## Poll-and-React Loop

For agents that need to periodically check on a build:

```bash
cd /path/to/project

poll_project() {
  STATE=$(sf headless query 2>/dev/null)
  if [ -z "$STATE" ]; then
    echo "NO_PROJECT"
    return
  fi

  PHASE=$(echo "$STATE" | jq -r '.state.phase')
  COST=$(echo "$STATE" | jq -r '.cost.total')
  PROGRESS=$(echo "$STATE" | jq -r '"\(.state.progress.milestones.done)/\(.state.progress.milestones.total) milestones, \(.state.progress.tasks.done)/\(.state.progress.tasks.total) tasks"')

  case "$PHASE" in
    complete)
      echo "COMPLETE cost=\$$COST progress=$PROGRESS"
      ;;
    blocked)
      BLOCKER=$(echo "$STATE" | jq -r '.state.nextAction // "unknown"')
      echo "BLOCKED reason=$BLOCKER cost=\$$COST"
      ;;
    *)
      NEXT=$(echo "$STATE" | jq -r '.next.action // "none"')
      echo "IN_PROGRESS phase=$PHASE next=$NEXT cost=\$$COST progress=$PROGRESS"
      ;;
  esac
}
```

## Resuming Work

If a build was interrupted or you need to continue:

```bash
cd /path/to/project

# Check current state
sf headless query | jq '.state.phase'

# Resume from where it left off
sf headless --output-format json auto 2>/dev/null

# Or resume a specific session
sf headless --resume "$SESSION_ID" --output-format json auto 2>/dev/null
```

## Supervised Mode

Use `--supervised` when you want SF to ask you questions interactively rather than auto-answering or blocking. SF writes UI requests to stdout as JSONL; you respond via stdin.

**When to use it:** You're the orchestrator running in a loop and want to intercept SF's questions yourself instead of pre-supplying an answers file.

```bash
# Launch in supervised mode — SF will write extension_ui_request events to stdout
# and wait for your response on stdin before continuing
sf headless --supervised --json auto 2>/dev/null | while IFS= read -r line; do
  TYPE=$(echo "$line" | jq -r '.type')

  if [ "$TYPE" = "extension_ui_request" ]; then
    # SF is asking a question — inspect it and respond
    TITLE=$(echo "$line" | jq -r '.title // .message // "?"')
    OPTIONS=$(echo "$line" | jq -r '.options[]?.label // empty' | head -5)
    echo "SF asks: $TITLE" >&2
    echo "Options: $OPTIONS" >&2

    # Send your answer back on stdin (the option label or value)
    echo "first_option"   # replace with your selection logic
  fi
done
```

`--response-timeout N` (default 30000ms) controls how long SF waits for your response before treating it as a timeout. If you don't respond in time, SF blocks with exit code 10.

**Simpler alternative:** If you just want to pre-answer known questions without interactive handling, use `--answers <file>` instead — see `references/answer-injection.md`.

## Crash Recovery

When SF exits unexpectedly (crash, OOM, signal) or `.sf/` state looks corrupted:

```bash
cd /path/to/project

# 1. Check if the project directory is intact
ls .sf/ 2>/dev/null || { echo "No .sf/ — project state lost, start fresh"; exit 1; }

# 2. Run doctor — detects and auto-fixes common state corruption
sf headless doctor

# 3. Check what state SF thinks it's in
sf headless query | jq '{phase: .state.phase, next: .next}'

# 4. If query fails (state unreadable), inspect STATE.md directly
cat .sf/STATE.md 2>/dev/null

# 5. Resume from current state
sf headless --output-format json auto 2>/dev/null

# 6. If a specific session was interrupted, resume by session ID
sf headless --resume "$SESSION_ID" --output-format json auto 2>/dev/null
```

**Common crash scenarios:**

| Symptom | Cause | Fix |
|---------|-------|-----|
| `query` returns empty / parse error | `.sf/STATE.md` corrupted | Run `sf headless doctor` |
| Phase stuck at `advancing` | Slice summary write interrupted | Run `sf headless next` to retry |
| Phase stuck at `completing-milestone` | Milestone archive write interrupted | Run `sf headless dispatch complete` |
| Zombie `.sf/` lock file | Previous process killed mid-write | Run `sf headless doctor` |
| `exit 1` with no JSON output | SF itself crashed (OOM, signal) | Check system logs; resume with `--resume` |

If `doctor` can't recover the state, the safest path is to read `.sf/milestones/*/ROADMAP.md` to see what completed, then start a new milestone for remaining work.

## Reading Build Artifacts

After completion, inspect what SF produced:

```bash
cd /path/to/project

# Project summary
cat .sf/PROJECT.md

# What was decided
cat .sf/DECISIONS.md

# Requirements and their validation status
cat .sf/REQUIREMENTS.md

# Milestone summary
cat .sf/milestones/M001-*/M001-*-SUMMARY.md 2>/dev/null

# Git history (SF commits per-slice)
git log --oneline
```
