Skip to content

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 appropriate

Identity

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.jsonl

Each 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/sqlite to 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.

Forge is local-first. The docs are part of the product engineering system.