Documentation Index
Fetch the complete documentation index at: https://mintlify.com/8BitTacoSupreme/flowstate/llms.txt
Use this file to discover all available pages before exploring further.
Every pipeline run reads from and writes to a single flowstate.json file in the project root. The file is fully described by Pydantic models in flowstate/state.py — all reads go through FlowStateModel.model_validate(), so the schema is enforced on every load. State is written after context generation and after every tool step, giving crash-resilient progress tracking. State files written by v0.1.0 are automatically migrated on first read.
FlowStateModel
The top-level model that maps to the root of flowstate.json:
class FlowStateModel(BaseModel):
version: str = "0.2.0"
created_at: datetime = Field(default_factory=lambda: datetime.now(UTC))
updated_at: datetime = Field(default_factory=lambda: datetime.now(UTC))
interview: InterviewAnswers = Field(default_factory=InterviewAnswers)
preferences: ProjectPreferences = Field(default_factory=ProjectPreferences)
tools: dict[str, ToolState] = Field(
default_factory=lambda: {
"research": ToolState(),
"strategy": ToolState(),
"gsd": ToolState(),
"discipline": ToolState(),
}
)
artifacts: dict[str, str] = Field(default_factory=dict)
context_files: list[str] = Field(default_factory=list)
| Field | Type | Description |
|---|
version | str | Schema version, currently "0.2.0" |
created_at | datetime | UTC timestamp when the file was first created |
updated_at | datetime | UTC timestamp updated by every save_state() call |
interview | InterviewAnswers | All user answers from the intake interview |
preferences | ProjectPreferences | Project-level settings (model, budget, dry-run, etc.) |
tools | dict[str, ToolState] | Per-tool status keyed by "research", "strategy", "gsd", "discipline" |
artifacts | dict[str, str] | Named artifact paths, e.g. {"research_report": "research/report.md"} |
context_files | list[str] | Relative paths of files written by write_context_files() |
Tracks the lifecycle of a single tool execution:
class ToolState(BaseModel):
status: ToolStatus = ToolStatus.READY
started_at: datetime | None = None
completed_at: datetime | None = None
error: str | None = None
artifacts: list[str] = Field(default_factory=list)
started_at is set when status transitions to RUNNING. completed_at is set on COMPLETED or BLOCKED. Both remain null in JSON until the tool runs.
class ToolStatus(StrEnum):
READY = "ready"
RUNNING = "running"
COMPLETED = "completed"
BLOCKED = "blocked"
The lifecycle transitions enforced by update_tool():
READY → RUNNING → COMPLETED
↘ BLOCKED
BLOCKED means the tool’s execute_fn() returned success=False. The pipeline continues with remaining steps; BLOCKED tools are not retried automatically.
InterviewAnswers
Stores all answers captured by the intake interview:
class InterviewAnswers(BaseModel):
research_focus: str = ""
core_problem: str = ""
ten_x_vision: str = ""
milestones: list[str] = Field(default_factory=list)
test_coverage: int = 80
architecture_pattern: str = ""
| Field | Default | Description |
|---|
research_focus | "" | Comma-separated topics for the research adapter |
core_problem | "" | The problem the project solves (used in strategy prompt) |
ten_x_vision | "" | The 10× improvement the project delivers |
milestones | [] | Ordered list of milestone strings for roadmap generation |
test_coverage | 80 | Target test coverage percentage |
architecture_pattern | "" | e.g. "event-driven", "CQRS", "monolith" |
ProjectPreferences
Project-level settings that control bridge behaviour and pipeline mode:
class ProjectPreferences(BaseModel):
project_name: str = ""
dry_run: bool = False
auto_branch_on_hardening: bool = True
model: str = ""
max_budget_usd: float | None = None
effort: str = ""
| Field | Default | Description |
|---|
project_name | "" | Used in PROJECT.md and CLAUDE.md headings |
dry_run | False | If True, all bridge calls are skipped and mock output is returned |
auto_branch_on_hardening | True | Reserved for future branch automation on hardening phases |
model | "" | Overrides the default Claude model for all bridge calls |
max_budget_usd | None | Optional spend cap passed to BridgeConfig |
effort | "" | Optional effort level passed to BridgeConfig |
State Functions
load_state(root) → FlowStateModel
Reads and validates flowstate.json from the project root. If the file does not exist, returns a fresh default FlowStateModel. Runs _migrate_state() before validation to handle v0.1.0 files:
def load_state(root: Path | None = None) -> FlowStateModel:
p = state_path(root)
if p.exists():
raw = json_mod.loads(p.read_text())
migrated = _migrate_state(raw)
return FlowStateModel.model_validate(migrated)
return FlowStateModel()
save_state(state, root) → Path
Sets updated_at to the current UTC time, serializes the model to JSON with 2-space indentation, and writes it to flowstate.json. Returns the path:
def save_state(state: FlowStateModel, root: Path | None = None) -> Path:
p = state_path(root)
state.updated_at = datetime.now(UTC)
p.write_text(state.model_dump_json(indent=2) + "\n")
return p
Mutates a ToolState entry in-place. All keyword arguments are optional:
def update_tool(
state: FlowStateModel,
tool: str,
*,
status: ToolStatus | None = None,
error: str | None = None,
artifact: str | None = None,
) -> None:
ts = state.tools[tool]
if status is not None:
ts.status = status
if status == ToolStatus.RUNNING:
ts.started_at = datetime.now(UTC)
elif status in (ToolStatus.COMPLETED, ToolStatus.BLOCKED):
ts.completed_at = datetime.now(UTC)
if error is not None:
ts.error = error
if artifact is not None:
ts.artifacts.append(artifact)
state_path(root) → Path
Returns the absolute path to flowstate.json for a given project root. Defaults to the current working directory:
def state_path(root: Path | None = None) -> Path:
return (root or Path.cwd()) / "flowstate.json"
State Migration: v0.1.0 → v0.2.0
_migrate_state() is called by load_state() on every read. It checks the version field and renames old tool keys to their current names:
_OLD_TOOL_KEYS = {
"autoresearch": "research",
"gstack": "strategy",
"superpowers": "discipline",
}
The migration also ensures context_files is present (it was not tracked in v0.1.0) and fills any missing tool keys with default ToolState values:
def _migrate_state(data: dict) -> dict:
version = data.get("version", "0.1.0")
if version >= "0.2.0":
return data
# rename old tool keys
...
data["version"] = "0.2.0"
if "context_files" not in data:
data["context_files"] = []
return data
Migration is non-destructive — the original file is overwritten only when save_state() is called after migration. Loading a v0.1.0 file and inspecting it without running the pipeline will not modify the file.
flowstate.json Schema Example
{
"version": "0.2.0",
"created_at": "2025-01-15T10:30:00Z",
"updated_at": "2025-01-15T10:35:42Z",
"interview": {
"research_focus": "event sourcing, CQRS",
"core_problem": "Teams lose context switching between planning tools",
"ten_x_vision": "Single command bootstraps a fully-planned, research-backed project",
"milestones": [
"Core pipeline with dry-run mode",
"Persistent memory with FTS5",
"Native GSD integration"
],
"test_coverage": 90,
"architecture_pattern": "event-driven"
},
"preferences": {
"project_name": "flowstate",
"dry_run": false,
"auto_branch_on_hardening": true,
"model": "",
"max_budget_usd": null,
"effort": ""
},
"tools": {
"research": {
"status": "completed",
"started_at": "2025-01-15T10:31:05Z",
"completed_at": "2025-01-15T10:32:44Z",
"error": null,
"artifacts": ["/home/user/project/research/report.md"]
},
"strategy": {
"status": "completed",
"started_at": "2025-01-15T10:32:45Z",
"completed_at": "2025-01-15T10:34:01Z",
"error": null,
"artifacts": ["/home/user/project/research/strategy.md"]
},
"gsd": {
"status": "completed",
"started_at": "2025-01-15T10:34:02Z",
"completed_at": "2025-01-15T10:34:03Z",
"error": null,
"artifacts": ["/home/user/project/.planning/ROADMAP.md"]
},
"discipline": {
"status": "completed",
"started_at": "2025-01-15T10:34:03Z",
"completed_at": "2025-01-15T10:34:03Z",
"error": null,
"artifacts": []
}
},
"artifacts": {
"research_report": "/home/user/project/research/report.md",
"strategy_report": "/home/user/project/research/strategy.md",
"roadmap": "/home/user/project/.planning/ROADMAP.md"
},
"context_files": [
".planning/PROJECT.md",
".planning/ROADMAP.md",
".planning/config.json",
".claude/CLAUDE.md",
"research/brief.md"
]
}
flowstate.json is gitignored by default because it contains absolute paths to your local filesystem. Do not commit it to version control — re-run flowstate init --skip-interview in a new clone to regenerate it.