Architecture decisions
Gin records its architecture decisions as ADRs. This page summarises each one. Most were written for the first milestone and are proposed until the milestone's discovery results are approved; the status of each is given below.
The agents-not-humans principle applies to every other decision.
Gin is built for agents, Git for humans
Status: accepted (2026-11-06). A product principle.
Gin is built for agents and machines; Git is built for humans. Gin does not copy Git's human-facing features and carries checkpoints, the DAG of dependent work, structured metadata, provenance, evidence and comments instead.
- Git commits are not Gin nodes; Gin does not mirror or walk Git history.
- Gin stores no commit messages, authors, committer dates or porcelain conventions.
- A sync is one diff and one checkpoint in each direction; one sync record is the only link. Export refuses if the Git ref moved since the last sync.
- Git parity means cost, not features: no extra work by default; extra guarantees are opt-in.
- Work per sync is proportional to the change.
- Older docs on authored Git imports and per-commit Git mappings are superseded. See Git interoperability.
Consistency of publication on one gin serve
Status: proposed.
How a worker's checkpoint reaches the daemon, how concurrent writers are kept apart, and how events are ordered.
- One commit point per publication: exactly one SQLite transaction. No two-phase commit; atomicity comes from ordering and content addressing.
- Objects before metadata, always. Metadata never references an object that is not durable and verified.
- Every mutable pointer (channel heads, workspace epochs, write leases, Git export mappings) is updated by compare-and-swap. No blind writes.
- Every server-side workspace mutation is fenced by the holder's write lease and epoch.
- Every mutating request is idempotent; a retry returns the original outcome.
- Events are written in the same transaction (transactional outbox), ordered per stream and replayed from a cursor. There is no global order or lock.
Durability of savepoints and publication
Status: proposed. The default policy was later changed to cached writes; full durability is opt-in.
- The private workspace journal is an append-only journal: an edit is one unsynced append, a savepoint one append plus fsync.
- Daemon metadata is SQLite in WAL mode; every publication is staged (objects durable and verified first, then one atomic metadata transition).
- Any failed write or fsync poisons the store; a failed fsync is never retried and trusted.
- Recovery never trusts cached bytes: it rewrites what it recovered before accepting work.
- One open store per workspace, enforced by an exclusive lock that dies with its process.
- Default cached mode keeps acknowledgements across a process kill but may lose recent work on an OS crash; full mode (
GIN_DURABILITY=full) requires device barriers. See Getting started.
Evidence equivalence and strict exact-input reuse
Status: proposed.
What "the same inputs" means before a result may stand for a run that has not happened.
- Strict mode is the only reuse mode for now.
- A result is reusable only when its reuse key matches exactly, every component is known, and the test is cleared as hermetic. No partial match.
- The source-input closure is the whole effective tree; reuse happens only across identical tree identities.
- Declared, observed and verified inputs are separate records; none is treated as complete on its own word.
- Reuse is advisory for now: Gin reports "would reuse" and skips nothing.
- Reused evidence keeps its original trust and provenance and is labelled reused; a gate may demand a fresh run.
Integration and operations
Status: approved.
What runs where, who owns what, and how releases roll out and back.
- One service: a single
gin serveprocess on one Linux host, with SQLite metadata and the pack store embedded. Embedded-library mode runs the same core without a server. - One contract (
gin.v1), several adapters: gRPC, local IPC, CLI and MCP. Within a major version changes are additive only. - Ownership stays put: the orchestrator owns tasks, reviews and landing decisions; Toki owns build and test execution; Gin owns source state and evidence; the forge keeps final landing authority.
- Gin depends on no orchestrator schema and works with none present.
- Git stays the external boundary: Gin holds no mirror or forge credential.
- Rollback is switching the binary version and restarting; a binary refuses a store format newer than it understands. No persisted-format change lands without an upgrade path.
Threat boundaries and identity rules
Status: proposed.
- Not a sandbox. Gin versions what lands in a workspace; containing processes belongs to the executor or harness.
- Build scripts, shell commands, repository contents and compromised workers are untrusted to stated degrees. Gin never executes repository code, runs repository Git hooks or filters, or follows stored symlinks.
- Hostile paths, collisions, oversized objects and crafted objects are refused at ingestion, never normalised.
- Capabilities are scoped to repository, workspace and operation; a compromised worker can only do what its token allows, and its receipts never exceed executor-reported trust.
- Identities are BLAKE3-256 over one canonical encoding in a per-kind domain; non-canonical input is refused. Git SHAs are never Gin identities.
- An ID is a name, not a capability. Elapsed time alone never makes an object collectable.
Product hybrid directory manifests
Status: implementation decision.
How large directories are stored so that one edit stays cheap.
- Directories up to 256 entries are sorted leaves; larger ones use compressed raw-byte prefix indexes.
- This replaces an earlier hash-chunk layout that rewrote whole directories under crafted names.
- Updates touch only changed groups; diff skips shared branches.
- Exact source identity is kept for prefix-related and non-UTF-8 names.
- No constant-time or depth-independent claim is made: long name prefixes can increase work.
Runtime resource ownership
Status: design record for the daemon runtime.
How the daemon bounds memory, descriptors and resident owners.
- Bounded resident repositories and workspaces, evicted least-recently-used; active owners are never evicted.
- Callers waiting for an owner queue in FIFO order and withdraw cleanly when cancelled.
- One process-wide descriptor reservation pool; a request reserves its whole footprint before taking locks, so there are no partial-reservation deadlocks.
- A request too large to ever fit gets a clear error rather than an endless wait.
- No acknowledged state lives in disposable routing snapshots; missing or corrupt ones fall back to journal replay.