singularity-forge/gsd-orchestrator/workflows/monitor-and-poll.md
ace-pm b29c12d5e5 refactor(native): rename gsd_parser.rs to forge_parser.rs
Final rebrand: rename remaining Rust source file to complete the gsd → forge
transition. All parser references already use forge_parser after earlier commits.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 14:58:21 +02:00

4.7 KiB

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.

cd /path/to/project
sf headless query

Key fields to inspect

# 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:

# 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

# 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

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:

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:

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

Reading Build Artifacts

After completion, inspect what SF produced:

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