Runtime and State
The runtime is the SQLite-backed RuntimeStore. This section explains how the runtime root is resolved, what data lives there, how gates are persisted, and why direct mutation is forbidden.
Canonical runtime root
The runtime root is the directory containing goal-state.sqlite3 plus the auxiliary directories (next-actions, runs, sessions, logs, locks).
Default location for local dev:
D:\Projects\ASF50-Platform\ASF50\scripts\goal-runner\local\runtime
This is the location used by ASF50_DOCS_PORTAL_001. For production deployments, the runtime root should be moved outside the repository so that runtime state is never Git-tracked.
Path resolution
The orchestrator resolves the runtime root in this order:
- The
--runtime-rootCLI argument. - The
ASF50_RUNTIME_ROOTenvironment variable. - The repository's
local/runtimesubdirectory. - The
scripts/goal-runner/local/runtimesubdirectory (legacy fallback).
goal-doctor.ps1 detects split-brain runtime (where two of these disagree) and fails closed.
Repo-local runtime defaults may be non-canonical
scripts/goal-runner/local/runtime is a legacy local-dev location. Production deployments must:
- Move the runtime DB to a separate non-canonical directory.
- Set
ASF50_RUNTIME_ROOTin the operator's environment. - Confirm via
goal-doctor.ps1.
If you see the runtime DB inside the repo on a production deployment, that is a configuration bug. Open a repair goal.
What RuntimeStore contains
RuntimeStore persists:
goals,phases,tasks— declared state.next_actions— computed dispatch records.owner_gates— gate state with scope/source/approver.evidence_records— Git-tracked evidence pointers with SHA-256.worktrees,pull_requests,agent_sessions,runs— execution bookkeeping.task_dependencies— task graph edges.state_transitions— transition history.repair_attempts,findings— bounded repair and audit findings.ci_runs,commits,processes,quota_events,role_runs,locks— derived bookkeeping.schema_migrations— schema version table.
How gates are persisted
ScopedOwnerGateManager (via owner_gate_manager.py and goal_gate_cli.py) persists gates in the owner_gates table:
gate_id TEXT PRIMARY KEY,
goal_id TEXT NOT NULL,
scope TEXT,
reason TEXT,
status TEXT NOT NULL,
action TEXT,
source TEXT,
payload TEXT,
approver TEXT,
note TEXT,
evidence_path TEXT,
created_at TEXT NOT NULL,
decided_at TEXT,
dispatches_used INTEGER NOT NULL DEFAULT 0,
max_dispatches INTEGER NOT NULL DEFAULT 1,
last_dispatched_run_id TEXT,
reservation_run_id TEXT
Status values: OPEN, APPROVED, REJECTED, CONSUMED.
How runs/tasks/worktrees/CI/evidence relate
Each run is bound to a task and a worktree. Each PR is bound to a goal. Each CI run is bound to a goal. Each evidence record is bound to a goal and a SHA-256 of the evidence file.
Why direct SQLite mutation is forbidden
- Auditability — every state change must be recorded by a function with a receipt.
- Schema versioning — the schema is migrated by
RuntimeStore.migrate(). Direct writes may bypass migration expectations. - Reconciliation — the next materialization overwrites hand-mutated rows back to the canonical value.
If you need to fix a state bug, file a repair goal (goal_type: REPAIR_GOAL).
Backup and recovery
While ASF50 is idle:
sqlite3 goal-state.sqlite3 ".backup backup-$(date +%Y%m%d).sqlite3"
Recovery:
cp backup-20260919.sqlite3 goal-state.sqlite3
# then run reconciliation
python -m goal_registry.reconcile_all
A canonical-state-only recovery is also possible by deleting goal-state.sqlite3 and re-materializing every goal — the registry rebuilds the runtime from the Git-tracked contracts.
Continue with RuntimeStore →.