Theme
Data Model Direction
This is a design contract, not the final migration. We intentionally add columns/tables as the course reaches them rather than creating a giant speculative schema on day one.
Storage responsibilities
text
Git code truth / revisions / diffs
SQLite queryable Forge domain state
append-only JSONL raw provider protocol evidence
filesystem artifacts large outputs/attachments when appropriateIdentity
Use opaque stable IDs (ULID/UUID style). Human names and titles are never identity and should not determine stable worktree/session paths.
Initial relational model
projects
- id
- name
- repo_path
- default_branch
- trust_status
- created_at / updated_at
features
- id
- project_id
- title
- description
- created_at / updated_at
tasks
- id
- project_id
- feature_id nullable
- title
- description/goal
- status
- execution_mode
- involvement_mode
- preferred_agent_instance_id nullable
- preferred_strategy_id nullable
- created_at / updated_at
acceptance_criteria
- id
- task_id
- position
- text
- created_at
task_dependencies
- task_id
- depends_on_task_id
- primary key on pair
Write-time validation prevents self-dependency and cycles. Dependency satisfaction is computed from referenced Task states.
Plans
plans
- id
- project_id
- scope (
feature|task_execution) - feature_id nullable
- task_id nullable
- status
- current_revision_id nullable
- approved_revision_id nullable
- created_at / updated_at
Exactly one scope target should apply.
plan_revisions
- id
- plan_id
- revision_number
- content_markdown
- created_by_kind
- created_at
Immutable.
plan_reviews / plan_review_comments
Bind to plan_revision_id, never merely to plan_id.
plan_decompositions
Optional explicit record of an approved revision being proposed/confirmed into Tasks so decomposition history is inspectable and idempotent.
Agent configuration
agent_drivers
Usually seeded/system-defined integration families.
agent_instances
- id
- driver_id
- display_name
- configuration metadata/reference
- enabled
Secrets should not be casually stored in plaintext SQLite if the underlying agent already owns authentication. Forge normally references local agent configuration/installation rather than reimplementing auth.
adapter_strategies
- id
- driver_id
- name
- version/implementation identifier
Capabilities may be code-declared with a persisted snapshot on each Run rather than dynamically normalized into many tables.
Agent execution
agent_runs
- id
- task_id
- agent_instance_id
- strategy_id
- execution_environment_id
- status
- stopped_reason nullable
- delivery_error nullable
- external_session_id nullable
- capability_snapshot JSON (versioned)
- policy_snapshot JSON (versioned)
- started_at / stopped_at
agent_turns
- id
- run_id
- sequence
- status
- input/reference
- started_at / completed_at
agent_events
Queryable normalized events only; not every token delta.
- id
- run_id
- turn_id nullable
- sequence
- type
- evidence_kind (
fact|claim) - raw_ref NOT NULL
- payload JSON (versioned)
- occurred_at / recorded_at
Raw logs
Path example:
text
$XDG_DATA_HOME/forge/runs/<run-id>/raw.jsonlEach line is framed by Forge so the file itself is evolvable:
json
{
"formatVersion": 1,
"sequence": 42,
"receivedAt": "...",
"source": "claude-code/stream-json",
"payload": {}
}A partial/corrupt final line after power loss should be recoverable without making the entire application unstartable.
Execution environment and worktree
execution_environments
We may not need a generic table immediately. A Run records which environment implementation/configuration it used.
worktrees
- id
- project_id
- task_id
- path
- branch_name
- base_revision
- status
- created_at / removed_at nullable
Path is stable and ID-based.
Durable side effects
operations
- id
- kind
- aggregate_type
- aggregate_id
- idempotency_key UNIQUE where appropriate
- status
- attempt
- payload JSON (versioned)
- result JSON (versioned) nullable
- error_code nullable
- error_details nullable
- requested_at
- started_at nullable
- finished_at nullable
Do not stuff arbitrary exception strings into the domain model; operation diagnostics can be richer while the application maps them to actionable domain errors.
Decisions
decisions
- id
- task_id
- run_id / turn_id nullable
- reason
- prompt/context
- recommendation_option_id nullable until options persisted transactionally
- blocking
- deadline nullable
- created_at
decision_options
- id
- decision_id
- position
- label
- description
decision_resolutions
- decision_id
- selected_option_id nullable when freeform is allowed
- response text nullable
- resolution_kind (
human|default) - resolved_at
Candidate revisions, Review, Verification
Candidate revision can be an explicit record if useful, or revision identity can initially live on the relevant Review/Task projection. Prefer explicit records once multiple candidate attempts need richer history.
reviews
- id
- task_id
- revision
- reviewer_kind
- status
- created_at / completed_at
review_comments
- review_id
- path/hunk/line anchor when applicable
- body
- status/resolution later if needed
verification_definitions
Project-scoped commands/configuration.
verification_runs
- id
- task_id
- definition_id
- revision
- status
- exit/result details
- started_at / completed_at
Activity
activity_items is a query/read model for the human-readable feed. It references raw/normalized evidence and preserves fact versus claim. Do not let this projection become source truth for Task transitions.
SQLite operational defaults
Starting direction for the local database:
- foreign keys on;
- WAL journal mode;
- synchronous NORMAL unless measured requirements justify otherwise;
- busy timeout around 5s as a starting point;
- migrations embedded/versioned;
modernc.org/sqliteto keep the Go core cgo-free.
Apply connection-scoped pragmas through the driver/DSN mechanism rather than assuming one db.Exec(PRAGMA...) configures every pooled connection.
Migration discipline
- Migration files are immutable once released.
- Each migration applies transactionally with its migration record when SQLite permits the operations involved.
- Keep fixtures for old database versions and migrate them in tests.
- Version JSON payload schemas that survive releases.
- Backward compatibility for persisted history matters just as much as current RPC compatibility.