Docs / State model

State model

Runs, steps, attempts, artifacts, validations, gates.

Every Codencer run is a tree. The root is the run itself — one task spec, one or more attempts to satisfy it. Below the run sit steps, the discrete units of work the bridge dispatched. Each step has one or more attempts, because failure is part of the record. Each attempt produces artifacts — files changed, diffs, test output, logs. Validations run against artifacts. Gates evaluate validations. The state model is the thing every other vendor's architecture flattens away. Codencer keeps it, because it's the asset.

The six entities

Run

The top-level container for a task. One run per submission. Stored in runs. A run has a lifecycle status: pending, running, completed, failed, aborted. Status transitions are deterministic — only the daemon writes them, only through explicit state-machine paths.

Step

A unit of work inside a run. The planner decides which step is next; the bridge executes it. Stored in steps. A step has a goal (natural-language), a target executor, and a set of validators and gates. Steps belong to exactly one run.

Attempt

A single execution of a step. Stored in attempts. Multiple attempts per step is the norm, not the exception — the executor might fail, the validator might catch a bug, the gate might reject the diff. Each attempt has its own start time, end time, status, and artifact set. Failed attempts are not deleted. They stay in the record as evidence.

Artifact

A file produced by an attempt. Diffs, test output, logs, generated files. Stored in artifacts with a path on disk under artifacts/<run_id>/<attempt_id>/.... Artifacts are immutable once written. Re-running an attempt creates new artifacts under a new attempt id; the old ones remain.

Validation

A deterministic check run against the artifacts of an attempt. Validators are shell commands declared as part of the step — go test ./..., pytest, pnpm typecheck, golangci-lint run. Stored in validations with the command, exit code, stdout/stderr paths, and pass/fail. The validator runner does not interpret the command; it runs it and records the result.

Gate

A policy threshold evaluated against validations and artifacts. Examples: "all validations passed," "diff under 800 lines," "no new dependencies introduced," "no schema migration in the diff." Stored in gates. Gates are how the bridge decides whether to surface a step as complete or hold it for human review.

Append-only design

The state tables are append-only. The daemon writes; nothing rewrites. This is deliberate:

  • A failed attempt stays a failed attempt forever. The audit log shows the failure path, not just the success path.
  • A successful retry adds a new attempt node; it does not overwrite the previous failure.
  • A run that gets aborted shows its abort point. No silent rollback.

NOTE — friction: Abort remains best-effort. Codencer reports a successful abort only when the active step actually reaches cancelled. It does not claim universal hard-kill semantics. Inspect step state, result, validations, logs, gates, and artifacts after an abort instead of assuming a forced stop.

Append-only is what makes the run record auditable. If the schema permitted updates-in-place, the record would only show the last write — not the history.

Worktree isolation

Each attempt runs in its own git worktree, branched off the operator's repo root. Worktrees are cheap (git is optimized for them) and provide perfect isolation: parallel attempts cannot collide.

The flow:

  1. Attempt opens. Daemon creates a worktree for the attempt.
  2. The executor adapter operates on that worktree, not the operator's working directory.
  3. Artifacts are read from the worktree and copied to the attempt's artifact directory.
  4. The worktree is closed (kept by default for forensics; configurable retention).

The operator's working directory is never touched by an attempt. If three attempts ran in parallel against the same step, each has its own diff, its own test output, its own everything. The state model captures all three independently.

Cross-session continuity

The daemon persists state to SQLite. If the daemon restarts, the state survives. A run that was running when the daemon stopped is recovered to its last committed state — pending steps remain pending, in-flight attempts are marked aborted (not lost), and the planner can resume.

Cross-session memory at the planner side — the planner remembering what happened across days or weeks of work — is on the post-beta roadmap. The bridge's state model is the substrate for it.

What's deferred

NOTE — friction: A direct public API for listing action-log rows from cloud connectors does not exist yet at the v0.2.0-beta tag. Action-log depth is mainly a store and audit-truth improvement at this point. Use the existing cloud audit and event surfaces for operator visibility until a direct action-log listing route exists.

For the schema details and migration history, see the repo's docs/02_architecture.md and the SQL migrations under internal/.

State model · Codencer