Part II: The Substrate CHAPTER 3 OF 16 ⏱️ 14 min read

The Execution Substrate: Worktrees, Gates, and Two-Phase Commit

Mark Gantlett
Mark Gantlett
Principal Systems Architect
Nomos Mascot
⚡ AI AUGMENTED Tier 1 Frontier Reasoning + On-Premise RTX 4080 Silicon
🗓️ Created: August 2026 🔄 Last Updated: September 2026 100% Compiler-Verified

The Execution Substrate: Worktrees, Gates, and Two-Phase Commit#

A coding agent that can emit diffs is not an engineering system. An engineering system is a machine that can refuse a transition.

Nomos treats the shared trunk as a single-writer resource under contention. The LLM is a bounded heuristic subroutine. It does not own the working tree, does not hold the branch lock, and does not declare success. The compiled Go runtime owns those privileges, and it exercises them through four mechanisms that form the execution substrate:

  1. Ephemeral worktrees — physical isolation of mutation.
  2. Pre-commit trunk locks — physical exclusion of mutation on protected refs.
  3. Proof gates — non-negotiable, exit-code-enforced predicates on AST, imports, and tests.
  4. Two-Phase Commit with Ed25519 receipts — atomic publication plus an auditable, tree-independent proof object on refs/notes/agent.

None of this makes the model smarter. It makes incorrect state transitions mechanically impossible.

Execution Is a Concurrency Problem#

The failure modes of 2024–2026 “vibe coding” were not primarily model failures. They were harness failures:

  • Concurrent writers on main with no isolation boundary.
  • “Done” declared in natural language while tests were red, skipped, or never run.
  • Commits that mixed diagnostic scaffolding with production source.
  • No durable, independently verifiable record of what was proven at release time.

Those are the same class of bugs distributed systems already named: lost updates, dirty reads, non-atomic commit, and missing write-ahead proof. The substrate therefore borrows the oldest reliable tools in that literature—isolation, exclusion, prepare/commit, signed receipts—and binds them to git’s object model rather than to a chat log.

The lifecycle the engine actually runs is not a prompt. It is a state machine:

TRIAGE → SPEC → PLAN → EDIT → REVIEW → SYNC → LEARN

EDIT is allowed only inside a worktree. REVIEW is allowed only as gate evaluation. SYNC is allowed only as 2PC. The model is invoked with an ephemeral capability token and a step budget; when the budget or a gate fails, the transition does not occur.

stateDiagram-v2
    [*] --> TRIAGE
    TRIAGE --> SPEC: issue pinned
    SPEC --> PLAN: contract accepted
    PLAN --> EDIT: worktree scaffolded
    EDIT --> REVIEW: patch staged
    REVIEW --> EDIT: gate exit ≠ 0 / snap-back
    REVIEW --> SYNC: all gates exit 0
    SYNC --> LEARN: 2PC commit + receipt
    SYNC --> EDIT: 2PC abort
    LEARN --> [*]

Hermetic Worktrees#

Git already provides the isolation primitive: linked working trees sharing an object store, each with its own index and HEAD. Nomos uses that primitive as a sandbox, not as a convenience.

Convention:

worktrees/<repo>-<task>

<task> is a substrate-issued identifier (not a model-invented branch name). The engine creates the worktree, binds it to the task’s capability token, and destroys it on commit or abort. The trunk working copy is never the EDIT cwd.

Scaffold is a single, engine-owned sequence:

git fetch origin
git worktree add -B agent/<task> worktrees/<repo>-<task> origin/<trunk>
# cwd for all subsequent agent syscalls:
#   worktrees/<repo>-<task>

Invariants the runtime enforces before any LLM-issued edit tool is enabled:

Invariant Enforcement
CWD is a linked worktree git rev-parse --is-inside-work-tree and git worktree list --porcelain must name the task path
HEAD is agent/<task> Symbolic ref match; detached HEAD on trunk is rejected
Worktree is not the primary checkout Path inequality against the repository’s main worktree
Index starts clean relative to the pinned base git status --porcelain empty after scaffold
Capability token is bound to this path CapBAC resource = worktree path + task id; other paths return EPERM

The model never receives a shell whose cwd is the trunk. File tools (replace_file_content, structured AST edits) resolve paths against the worktree root. Attempts to write ../ out of the worktree fail at the membrane, not as a polite request in the system prompt.

Teardown is equally mechanical:

git worktree remove --force worktrees/<repo>-<task>
git branch -D agent/<task>          # if 2PC aborted
# object store retains blobs until ordinary git gc

Diagnostic reproducers, scratch tests, and failed hypotheses die with the worktree. They are not an argument for lingering on main.

This is the Axiom of Ephemeral Sandboxing in operational form:

Scaffold → sandboxed TDD loop → DoD verification → atomic merge & teardown.

The worktree is the unit of mutation. The trunk is the unit of publication. They are not the same directory.

Pre-Commit Trunk Locks#

Isolation without exclusion is theater. A worktree that cannot reach main is useless if another process—human, agent, or CI—can still write main from the primary checkout while 2PC is in flight, or if an agent can cd to the primary tree and commit directly.

Nomos therefore treats protected refs as locked by construction. The lock has two layers: a hook that cannot be talked out of, and a prepare-phase lease used by 2PC.

Layer 1: Physical hook#

A pre-commit (and pre-push) hook is installed in the primary repository and inherited by worktrees via core.hooksPath. The hook does not inspect commit messages for obedience. It inspects refs:

var protected = map[string]struct{}{
    "refs/heads/main":      {},
    "refs/heads/develop":   {},
    "refs/heads/substrate": {},
}

func denyTrunkMutation(ref string, cwd string, inWorktree bool) error {
    if _, ok := protected[ref]; !ok {
        return nil
    }
    // Trunk commits are legal only from the 2PC commit agent,
    // holding a live prepare lease, executing outside EDIT.
    if os.Getenv("NOMOS_2PC_LEASE") == "" || inWorktree {
        return fmt.Errorf("exit 1: trunk mutation forbidden (ref=%s cwd=%s)", ref, cwd)
    }
    return verifyLease(os.Getenv("NOMOS_2PC_LEASE"), ref)
}

If the hook exits 1, git aborts the commit. There is no retry prompt to the model. The state machine remains in EDIT or transitions to snap-back.

NOMOS_2PC_LEASE is not a string the LLM can forge in a commit message. It is set in the engine’s environment for the duration of phase 2, derived from a lease object on disk (see below), and cleared on abort.

Layer 2: Exclusive prepare lease#

Hooks prevent accidental and agentic trunk writes. They do not serialize two successful 2PC attempts. For that, the substrate takes an exclusive lease on the protected ref before phase 2:

$GIT_COMMON_DIR/nomos/locks/<ref-as-filename>

The lease is a small, fsync’d record:

type TrunkLease struct {
    Ref       string    `json:"ref"`
    Task      string    `json:"task"`
    Worktree  string    `json:"worktree"`
    BaseSHA   string    `json:"base_sha"`   // trunk tip at prepare
    TreeSHA   string    `json:"tree_sha"`   // proposed tree
    Holder    string    `json:"holder"`     // engine instance id
    Expires   time.Time `json:"expires"`
    Nonce     [16]byte  `json:"nonce"`
}

Acquisition is O_EXCL create (or flock on a well-known lockfile) plus a compare of BaseSHA to git rev-parse <ref>. If the tip moved, prepare fails. Stale leases expire; the holder is the engine, not the model.

The combination is mundane and sufficient: the agent cannot commit to trunk, and two engines cannot commit to trunk at once.

Gates: Non-Verbal Proof#

REVIEW is not a conversation about quality. It is a vector of predicates, each a process with an exit code. The engine runs them against the worktree. Any non-zero exit blocks SYNC.

The model is not asked whether the code works. The toolchain is asked.

Ground-truth gates (the minimum closed set; adapters may add language-specific predicates):

Gate Predicate Failure
Compile Native toolchain exit 0 Transition blocked
Tests 100% of the invoked suite green; reproducer (if any) now exits 0 Transition blocked
Cyclomatic complexity Per-function complexity < 15 (AST) Transition blocked
Docstring density Documented public surface ≥ 10% (AST) Transition blocked
Import boundaries No illegal edges; no cycles across declared packages Transition blocked
Worktree hygiene No untracked diagnostic files scheduled for trunk Transition blocked

These are the same idea as cargo clippy failing the build, or terraform plan disagreeing with apply: the substrate’s opinion is the exit code. Negotiation is not an input.

A sketch of the gate runner:

type Gate struct {
    Name string
    Run  func(ctx context.Context, wt Worktree) error // nil iff exit 0
}

func Review(ctx context.Context, wt Worktree, gates []Gate) error {
    for _, g := range gates {
        if err := g.Run(ctx, wt); err != nil {
            return fmt.Errorf("gate %s: %w", g.Name, err)
        }
    }
    return nil
}

On failure the engine does not “tell the agent to try harder” as a side channel. It:

  1. Records the failing gate name, stdout/stderr hash, and tree SHA in the task log.
  2. Restores the worktree to the last known good index state (snap-back).
  3. Leaves the FSM in EDIT with a decremented step budget.
  4. Re-invokes the model only as a heuristic to propose a new patch.

If the budget hits zero, the worktree is removed and the task fails. Unfinished work is not merged because a context window filled up.

This is Cognitive Inversion applied to verification: the Go runtime drives the loop; the LLM is a stochastic ALU that may be called again, or not.

Two-Phase Commit#

A green worktree is not a release. A release is a transaction over three resources that must commit or abort together:

  • the trunk ref,
  • the signed receipt,
  • the lock/lease.

Git’s commit + merge is not a transaction over those three. Nomos therefore wraps publication in classical 2PC, with the engine as coordinator and the git refs as participants.

Participants#

Participant Prepare Commit Abort
Worktree / proposed tree Gates green; tree SHA frozen Merge or fast-forward trunk to frozen tree git worktree remove; delete agent/<task>
Trunk lease O_EXCL lease; BaseSHA still tip Lease released after ref update Lease released
Notes (refs/notes/agent) Receipt bytes signed; object written but not ref-tipped as published Notes ref updated to include receipt for the release commit Unsigned/prepared blob left unreachable

Phase 1 — Prepare#

Coordinator algorithm:

  1. Review(worktree) — all gates exit 0. Freeze TreeSHA = git write-tree.
  2. Read TrunkSHA = git rev-parse <trunk>. If TrunkSHA ≠ the SHA the worktree was based on, rebase-or-fail policy applies. Default: fail prepare (no silent rebase of an already-gated tree).
  3. Acquire TrunkLease with BaseSHA = TrunkSHA, TreeSHA frozen.
  4. Build the receipt (next section), sign with the engine’s Ed25519 key, write the blob to the object store. Do not yet move refs/notes/agent to a published state the rest of the world treats as final.
  5. Record a prepare log: {task, tree, base, lease nonce, receipt sha}.

If any step fails, abort: drop lease, do not move trunk, do not publish notes.

Prepare is idempotent for a given task: repeating it with a dirty tree or a moved tip fails closed.

Phase 2 — Commit#

Only the coordinator holding a live lease, with NOMOS_2PC_LEASE in environment, may run:

# still inside engine, not inside EDIT tools
git -C <primary> merge --ff-only agent/<task>
# or: git update-ref refs/heads/<trunk> <new-commit> <BaseSHA>

git notes --ref=agent add -f -F <receipt.json> <release-commit>
git update-ref refs/notes/agent <new-notes-commit> <old-notes-commit>

release lease
git worktree remove worktrees/<repo>-<task>

--ff-only (or update-ref with the expected old value) is the git encoding of “the world did not move.” If the compare-and-swap fails, this is an abort, not a retry from the model.

Abort#

Abort is the default. Any of: gate failure after a retry budget, lease expiry, tip movement, signature failure, notes CAS failure.

release lease if held
git worktree remove --force worktrees/<repo>-<task>
git update-ref -d refs/heads/agent/<task>
# trunk unchanged; notes unpublished

There is no “partial merge with a comment.” Partial publication is the bug 2PC exists to prevent.

sequenceDiagram
    participant E as Engine (coordinator)
    participant W as Worktree
    participant G as Gates
    participant L as Trunk lease
    participant T as refs/heads/trunk
    participant N as refs/notes/agent

    E->>W: freeze TreeSHA
    E->>G: Review()
    G-->>E: all exit 0
    E->>L: O_EXCL acquire (BaseSHA)
    L-->>E: lease
    E->>E: sign Ed25519 receipt
    alt commit
        E->>T: CAS update-ref / ff-only merge
        E->>N: notes add receipt
        E->>L: release
        E->>W: remove worktree
    else abort
        E->>L: release
        E->>W: remove worktree
        Note over T,N: unchanged
    end

The protocol is deliberately boring. Boring is the point. Agents invent; publication must not.

Ed25519 Receipts on Git Notes#

A green CI log in a chat transcript is not evidence. Evidence is a signed statement bound to content-addressed objects, stored where git already knows how to replicate it, without mutating the source tree.

Nomos stores that statement as a Git note on refs/notes/agent (the machine subconscious; human contracts remain Markdown in the Intent plane). Notes are first-class refs. They travel with fetch/push of that ref. They do not change TreeSHA of the code.

Receipt payload#

type ReleaseReceipt struct {
    Version    int      `json:"v"`              // 1
    Task       string   `json:"task"`
    Repo       string   `json:"repo"`           // origin URL or id
    Trunk      string   `json:"trunk"`          // e.g. refs/heads/main
    BaseSHA    string   `json:"base_sha"`
    CommitSHA  string   `json:"commit_sha"`     // release commit
    TreeSHA    string   `json:"tree_sha"`
    Gates      []GateResult `json:"gates"`
    TestTrace  string   `json:"test_trace_sha256"`
    Engine     string   `json:"engine"`         // nomos binary version + goos/goarch
    IssuedAt   int64    `json:"iat"`
    KeyID      string   `json:"key_id"`         // fingerprint of Ed25519 public key
    // Signature is over the canonical encoding of all fields above.
    Sig        []byte   `json:"sig"`            // Ed25519
}

type GateResult struct {
    Name     string `json:"name"`
    Exit     int    `json:"exit"`
    TraceSHA string `json:"trace_sha256"`
}

Canonicalization is deterministic (sorted keys, no insignificant whitespace). The signature is Ed25519 over that byte string. The private key lives with the engine host, not with the model and not in the repository.

Verification is a pure function:

func VerifyReceipt(r ReleaseReceipt, pub ed25519.PublicKey) error {
    if !ed25519.Verify(pub, canonical(r), r.Sig) {
        return errBadSig
    }
    if r.TreeSHA != gitTree(r.CommitSHA) {
        return errTreeMismatch
    }
    for _, g := range r.Gates {
        if g.Exit != 0 {
            return errGateNotGreen
        }
    }
    return nil
}

A replica that fetches refs/notes/agent can answer, without trusting the LLM or the operator’s memory: this commit was published by a substrate engine holding this key, from this base, with these gate traces. That is the audit object. Chat is not.

Notes are append-mostly. Force-updating a note on a commit is allowed only by the same 2PC path (the -f in git notes add -f is for idempotent recommit of the same receipt, not for rewriting history after the fact). Downstream policy may treat a notes ref as FF-only.

Why notes, not files in-tree#

In-tree receipts would couple proof to the source DAG, invite merge conflicts on every release, and tempt models to edit the proof. Notes keep the proof in the GitBrain: high-density, machine-oriented, invisible to language toolchains. The Intent plane (Markdown specs) remains human-readable. The two are transduced; they are not mixed.

Comparative Matrix: Cargo, Terraform, Substrate#

Cargo made a social rule (“don’t ship unsound Rust”) into a compile-time lock. Terraform made a social rule (“don’t mutate cloud state ad hoc”) into a plan/apply lock. Nomos makes a social rule (“don’t let the agent commit because it said it was done”) into a worktree/gate/2PC lock.

The domains differ. The shape does not: a deterministic engine owns state transitions; plugins extend; the core refuses to become a general-purpose script runner.

Dimension Cargo (Rust) Terraform (HashiCorp) Substrate (Nomos)
Primary domain Crate compilation, feature resolution, dependency integrity Infrastructure lifecycle, remote resource graphs Agent SDLC: isolated mutation, proof, publication
State that must not tear Cargo.lock + target artifacts vs. source Remote objects vs. terraform.tfstate Trunk ref vs. worktree tree vs. signed receipt
Core invariant If it compiles and clippy/test agree, type and memory contracts hold Divergence is computed in plan and applied only then Mutations live in transient worktrees; trunk moves only after gates + 2PC
Lock Package graph lockfile; advisory file locks on target dir State lock (remote backend) during plan/apply Pre-commit denial on protected refs + exclusive prepare lease
Execution loop checktestbuild planapply PLAN (read-only) → EDIT (worktree) → REVIEW (gates) → SYNC (2PC)
Proof object Compiler diagnostics; lockfile hashes Plan file; state serial Ed25519 receipt on refs/notes/agent
Failure closed form non-zero exit; no binary no apply; state unchanged snap-back; lease drop; trunk unchanged
What the core must not do Provision cloud, write product copy, own deployment topology Author application logic, commit to git on behalf of an app Embed company CD, CMS, or cloud credentials in the engine binary
Extensibility cargo-<plugin> binaries Providers and modules Agent adapters (nomos-code, …) and declarative workspace hooks
Untrusted component Build scripts (heavily constrained) Provider plugins (protocol-bounded) The LLM (CapBAC-bounded heuristic)

Read the middle column as a warning, not a compliment. Terraform’s original sin in the hands of agents is that apply is still a privileged mutation if the plan was produced by a stochastic process with no hermetic boundary. Cargo’s lesson is the useful one: make the lock a file the toolchain understands, and make the toolchain the only writer.

Nomos’s lock is not Cargo.lock. It is the tuple (worktree, lease, receipt). Losing any one of the three is an abort.

Mapping onto the Four-Plane Topology#

The execution substrate is the Substrate plane. It must not absorb the others.

Plane Role in this chapter
Intent Markdown specs and DoD contracts. Read in SPEC/PLAN. Never executed.
Substrate Go engine: worktrees, hooks, gates, 2PC, keys. Single writer of trunk.
Membrane May display receipt verification and task state. Must not hold the Ed25519 private key or the lease.
Infra NixOS/daemons may host the engine and replicate refs/notes/agent. Must not be compiled into the core as a cloud provider.

SSoT transduction still applies: the human-readable description of these mechanics lives in Intent; this chapter is that description. Byte-parity projection to public membranes is a later concern. The runtime does not read this Markdown to decide whether a hook fires.

Swarm split follows the same cut. Tier 1 (orchestrator) may sign off PLAN and invoke SYNC. Tier 2 workers receive a worktree path and a token that cannot satisfy NOMOS_2PC_LEASE. Workers edit and run tests. They do not publish.

Failure Modes the Substrate Is Allowed to Have#

A honest substrate names what it does not solve.

  • Semantic correctness beyond gates. Complexity < 15 and a green suite do not imply the spec was right. Spec error is an Intent-plane failure, caught—if at all—by humans or by later tasks, not by 2PC.
  • Rebase policy. Default fail-on-moved-tip is conservative. An automatic rebase would require re-running every gate on the new tree; that is a new prepare, not a continuation.
  • Key management. Ed25519 receipts are only as strong as engine-key hygiene. Compromised keys forge history of proof, not of trees; trees remain content-addressed. Rotation is an ops problem, not an LLM problem.
  • Notes replication. If refs/notes/agent is not fetched, replicas see code without receipts. Absence of a note is not a signature of absence; policy must define whether unsigned trunk commits are admissible (default: no, for agent-published commits).
  • Hook bypass. git commit --no-verify exists. So does writing objects with git hash-object. The substrate assumes control of the engine host and of CI that verifies receipts on the notes ref. It does not assume a hostile superuser on the same box.

What it does solve is the failure mode that actually dominated agentic coding: the model and the trunk sharing a working directory, with success defined in prose.

Invariants (Closed Form)#

  1. No EDIT syscall whose resolved path is outside worktrees/<repo>-<task>.
  2. No commit to a protected ref unless a live 2PC lease is in the engine environment.
  3. No SYNC unless every registered gate returned exit 0 against the frozen tree.
  4. Trunk CAS, notes publication, and lease release succeed together or not at all.
  5. Every published agent commit has a verifiable Ed25519 receipt on refs/notes/agent whose TreeSHA matches the commit’s tree.
  6. Worktrees are removed on commit and on abort. They are not a long-lived branch strategy.
  7. The LLM cannot set NOMOS_2PC_LEASE, cannot sign receipts, and cannot disable hooks.

If an implementation violates any one of these, it is not this substrate. It is a chat wrapper with extra steps.

The next chapter concerns how Intent contracts are compiled into the predicates these gates evaluate. The mechanics above do not care what the predicates mean—only that they are processes, that they return 0 or 1, and that 1 is the end of the transaction.

Mark Gantlett
Mark Gantlett
Founder, SophiaLabs & Principal Systems Architect
Architect of Nomos & Dual-Core Systems

This handbook is human-directed and AI-augmented, authored to eliminate the non-deterministic guessing of modern software engineering through compiled Go runtimes and machine-enforced Definition of Done gates.

1. Human Architecture
Mark Gantlett
System vision, architectural synthesis, and first-principles governance.
2. Tiered AI Augmentation
Sophia AI Stack
Frontier agentic orchestration paired with private on-premise RTX 4080 silicon.
3. Cognitive Inversion
Nomos Substrate
Go runtime as the core loop calling LLMs as bounded heuristic functions with AST gates.
Sophia AI • Live Architecture Chat