Skip to content

Release v0.4.8

Choose a tag to compare

@github-actions github-actions released this 01 Mar 19:14
· 46 commits to main since this release
1cae20c

What's Changed

PR #25: feat: automatic context handoff on budget exhaustion (GH-22)

Author: @moneypennyrasener


Summary

Implements automatic context handoff when a Claude Code session exhausts its --max-budget-usd limit mid-stage, and fixes the existing max-turns handoff to preserve conversation context via --continue.

Two changes per the spec in issue #22:

1. Budget-based handoff (new feature)

When a stage hits its budget cap, fastflow now:

  1. Detects subtype: "error_max_budget_usd" from --output-format json
  2. Runs claude --continue -p "<handoff prompt>" on the same session so it has full context to write .fastflow/handoff.md
  3. Starts a fresh session that reads the handoff and resumes the original task
  4. Loops up to MaxResumptions times if the fresh session also runs out of budget

2. Fix max-turns handoff to use --continue

The existing runHandoffCycle was starting a new session for create_handoff, losing all conversation context. Both budget and max-turns handoffs now use --continue for the create_handoff step.

Changes

internal/config/

  • Config.Defaults.MaxBudgetUsd — global fallback budget limit
  • Stage.MaxBudgetUsd — per-stage override (pointer, so explicit 0 overrides global)
  • Config.EffectiveBudget(stage) — resolution helper (stage wins over default)
  • Validation: rejects negative budgets at both levels
  • New tests: config_test.go, validate_test.go

internal/templates/files/orchestrator.json

  • Adds defaults block with maxBudgetUsd: 0 and _maxBudgetNote documenting recommended values
  • Sets plan stage to maxBudgetUsd: 10.00 as a working example

internal/runner/claude.go

  • ClaudeInvoker.MaxBudgetUsd: when > 0, switches CLI to --output-format json + --max-budget-usd; non-budget runs keep --print streaming unchanged
  • claudeJSONResult struct + subtype constants (success / error_max_budget_usd / error_max_turns)
  • InvokeResult gains HitBudgetCap, SessionID, TotalCostUsd
  • runWithJSONParsing(): captures and parses structured Claude CLI JSON output
  • InvokeContinue(): runs claude --continue for handoff creation step

internal/runner/runner.go

  • executeStage: sets invoker.MaxBudgetUsd from config, adds budget exhaustion loop calling runBudgetHandoffCycle
  • runBudgetHandoffCycle: two-step handoff (continue → fresh session)

internal/runner/stream.go

  • Captures session_id and is_error from stream-json result events
  • Populates HitBudgetCap, HitMaxTurns, SessionID on InvokeResult for verbose mode

Test plan

  • go test ./internal/... — all tests pass
  • TestEffectiveBudget_* — stage override, default fallback, zero override, no budget
  • TestValidate_*Budget* — negative stage budget, negative default budget, valid budgets
  • TestLoadConfigWithBudget — round-trip JSON load with budget fields
  • TestClaudeJSONResultParsing — success, budget exhausted, max turns subtypes
  • TestTemplateContainsBudgetConfig — orchestrator.json template has required fields
  • End-to-end: run a stage with a low maxBudgetUsd to trigger handoff (requires live Claude CLI)

Notes

  • When maxBudgetUsd is not configured (default 0), behaviour is identical to before — no overhead, no JSON output-format switch.
  • The _maxBudgetNote field in orchestrator.json is a documentation comment field (ignored by the config loader).
  • Handoff file is cleaned up after a successful budget handoff cycle.

Closes #22


Full Changelog: v0.4.7...v0.4.8

What's Changed

Full Changelog: v0.4.7...v0.4.8