Architecture

What Infinia Harness emits, and why each piece is shaped the way it is. Everything here describes v0.7.0.

The compile model #

The input is one YAML file. The output is a native harness per target — not a runtime, not a wrapper process. Nothing of Infinia Harness stays resident while your fleet runs; the agents your CLI loads are ordinary files it already knows how to read.

                        ┌──────────────────────────────────────────────┐
                        │                 fleet.yaml                   │
                        │  pattern · agents[] · skills[] · handoff graph│
                        │  capabilities · loops · schedule · defaults   │
                        └───────────────────────┬──────────────────────┘
                                                │
                                    ┌───────────▼───────────┐
                                    │   validate (schema +  │
                                    │   design-smell lint)  │
                                    └───────────┬───────────┘
                                                │
              ┌─────────────────────┬───────────┴──────────┬─────────────────────┐
              ▼                     ▼                      ▼                     ▼
     ┌────────────────┐   ┌──────────────────┐   ┌──────────────────┐  ┌──────────────────┐
     │  Claude Code   │   │     opencode     │   │      goose       │  │     _fleet/      │
     │ agents/*.md    │   │ agents/*.md      │   │ recipes/*.yaml   │  │ handoffs/        │
     │ skills/SKILL.md│   │ commands/ skills/│   │ AGENTS.md        │  │ LEDGER.md        │
     │ CLAUDE.md      │   │ AGENTS.md        │   │ sub-recipes      │  │ runs/  knowledge/│
     │ SubagentStop   │   │ permission.task  │   │ response.        │  │                  │
     │ hook (gate)    │   │ maps (gate)      │   │ json_schema(gate)│  │ portable protocol│
     └────────────────┘   └──────────────────┘   └──────────────────┘  └──────────────────┘
              └─────────────────────┴──────────────────────┴─────────────────────┘
                                    one fleet, three vocabularies,
                                       one shared file protocol

The three CLIs differ in vocabulary, not in concept. Each has a named specialist with a prompt, a scoped permission model, an attached methodology, and a way to pass work on. The compiler targets that intersection and lets each adapter spend the differences.

The spec #

A minimal fleet is short. Every key below has a default:

fleet:
  name: review-fleet
  domain: "reviewing pull requests in a Rust service"
  pattern: generate-verify

defaults:
  capabilities: { read: true }

agents:
  - name: change-analyst
    role: "Reads a diff and states what it actually changes"
    goal: "A change brief with every touched subsystem named"
    capabilities: { read: true }
    handoff:
      to: [risk-reviewer]
      artifact: change-brief.md
      criteria:
        - "Every changed file is accounted for"
        - "Behavioural changes are separated from refactors"

  - name: risk-reviewer
    role: "Adversarially checks the brief against the diff"
    goal: "A ranked risk list with file:line evidence"
    capabilities: { read: true, run: true }
    handoff:
      to: []

Patterns #

The pattern is chosen from the shape of the data flow, not from taste. It sets the default handoff topology and the orchestrator's prompt.

PatternShapeReach for it when
pipeline Sequential stages, each consuming the previous handoff Work has a natural order — analyse, then build, then review
fanout Independent parallel workers, then one merger Research sweeps and audits, where breadth beats depth
generate-verify Producers paired with adversarial checkers Output correctness matters more than throughput
supervisor A coordinating lead plus specialists; the lead owns state The work list is discovered as you go, not known upfront
expert-pool A router plus on-demand specialists Requests are heterogeneous and only some experts apply

Capabilities #

Agents declare intent, not tool names. Each adapter maps the same five booleans onto its own permission model, so least privilege survives the port instead of being re-derived by hand for every tool.

DeclaredClaude Codeopencodegoose
readtools: allowlisttools: booleans and permission: allow/deny mapsextensions, plus a stated read-only constraint
edit
run
web
spawn
goose has no tool-level sandbox

There is no permission surface to compile a capability into, so the constraint is emitted as an explicit statement in the recipe rather than an enforced boundary. That difference is stated in the generated output rather than papered over — a capability that is advisory on one target and enforced on two should not read identically on all three.

Cost control

model tiers (smart/fast/cheap) are intents. They bind to nothing unless you supply a defaults.claudeModels / opencodeModels / gooseModels map — because a pinned model spawns that model even when the user deliberately started a cheaper session, and fails outright where it is not on their plan. effort and turns give you cost control that works on an inherited model: they compile to Claude Code effort/maxTurns, opencode variant/steps, and goose settings.max_turns.

The handover protocol #

Every edge in the fleet graph carries an artifact and acceptance criteria. The protocol is file-based because files are the only channel all three tools share — which also makes fleet behaviour auditable after the fact and resumable after a crash.

The message is the doorbell; the file is the payload. Where a target has native agent-to-agent messaging, it is layered on top as a wake-up — never as the transport. A protocol that depends on messaging cannot be replayed, and cannot be read by a human deciding whether an agent actually did its job.

_fleet/
├── shared/                    committed — what the team has learned (PR-gated)
│   ├── knowledge/             promoted, reviewed notes
│   ├── evolution/             proposals + decisions.jsonl
│   └── CHANGELOG.md
└── local/                     gitignored — what my current run is doing
    ├── LEDGER.md              my task rows: task, owner, depends-on, status, artifact
    ├── handoffs/              one file per completed edge
    ├── runs/                  telemetry: gate verdicts, retries, corrections
    └── scripts/               the gate + event-log hooks

Each handoff file carries a real brief — objective, output format, sources and tools, boundaries, and failed approaches — and consumers are told to cite it rather than restate it. Every paraphrase between producer and consumer loses detail, and the loss compounds down a chain.

Enforced gates #

Where a target can check something deterministically, the compiler emits the check instead of an instruction to remember it.

TargetMechanismWhat it enforces
Claude Code SubagentStop hook running validate-handoff.sh An agent cannot finish until its handoff file exists and carries every required section
opencode permission.task maps compiled from the handoff graph An agent with no edge to another simply cannot see it in the task tool
goose response.json_schema The handoff summary is validated against its declared shape at runtime
Project hooks need a trusted workspace

Claude Code does not run project-level hooks until you accept its "trust this folder" dialog. Until you do, the handover gate is silently skipped and the fleet degrades to advisory instructions. This is the single most common reason a generated harness behaves less strictly than expected.

Workspace tiers #

Splitting the workspace stops a fleet's accumulated learning from living on one laptop, while stopping run logs from causing merge conflicts.

TierWhereHolds
user-global~/.claude/skills/Methodology you reuse on every project
project-shared_fleet/shared/ (committed)What the team has learned — promoted by PR
project-local_fleet/local/ (gitignored)What your current run is doing
grid enterpriseRelataDB cortexWhat everyone is doing and knows right now

The promotion ladder is one-directional: nothing becomes reviewed team knowledge except by landing in _fleet/shared/ through a pull request. The grid tier does not shortcut it.

Self-evolution #

A generated harness records what its runs did, checks itself deterministically, and can propose its own improvements for review — with one model call in the whole system, and gates that never depend on it.

observe ──► evaluate ──► mutate ──► validate ──► promote
   │            │           │           │            │
   │            │           │           │            └─ proposal lands on a branch;
   │            │           │           │               waits for `evolve --review`
   │            │           │           └─ qa + eval must both pass — deterministic
   │            │           └─ THE ONE MODEL CALL, and it may only touch
   │            │              artifacts that evolution itself generated
   │            └─ trigger discrimination against a held-out corpus,
   │               staged 3/10/full, measured noise floor, paired baseline
   └─ run telemetry the handover gate was already computing and throwing away
CommandWhat it does
fleetsmith qaSpec gate, per-target compile, handoff graph checked against compiled output, capability leaks, loop bounds, drift
fleetsmith evalWhether skills actually fire, on prompts that should and should not trigger them
fleetsmith evolveProposes changes on a branch; rollback is git revert fleet-gen/<n>
Why provenance is load-bearing

Every agent and skill carries origin: human|evolved, and the loop may only modify what it generated. The validator, QA battery, eval corpus and tests are hard-coded off limits — enforced in-process and in CI, because a control living inside the agent's own runtime is reachable by inputs that influence the agent. The Darwin Gödel Machine, scored by a function counting marker tokens, deleted the markers rather than fix the behaviour.

The Intelligence Grid enterprise #

Git makes team knowledge shareable, but only as fast as the last merged PR. That leaves four asks unanswered: agents cannot see each other's in-flight work, there is no semantic recall across the team mid-task, organisational context has no ingestion path, and there is no central governance. The grid is a fourth tier that answers them — and it is a projection, never an authority.

  developer A laptop                          developer B laptop
┌─────────────────────────┐                 ┌─────────────────────────┐
│ fleet agents            │                 │ fleet agents            │
│   │ read/write (files)  │                 │                         │
│ _fleet/local/           │                 │ _fleet/local/           │
│   LEDGER.md  handoffs/  │                 │   LEDGER.md  handoffs/  │
│   grid/peers/B/  ◄──────┼── of B's work   │   grid/peers/A/  ◄──────┼── read-only
│         ▲               │                 │         ▲               │   projection
│  fleetsmith grid sync   │                 │  fleetsmith grid sync   │
└───┬─────┴───────────────┘                 └───┬─────┴───────────────┘
    │ push: /ingest (typed rows)                │
    │ pull: SSE /graph/changes + cursor         │
    ▼                                           ▼
┌──────────────────────────────────────────────────────────────────┐
│                RelataDB cortex (customer-run, BYOL)              │
│  grid ontology: FleetTask · ActorPresence · HandoffPointer ·     │
│                 RunEventSummary                                  │
│  memory verbs:  MemoryItem · ProcedureMemory · DecisionRecord    │
│  org knowledge: OrgDocument (meetings, calls, discussions)       │
│  governance:    purpose registry · ACL · audit chain · AS OF     │
└──────────────────────────────────────────────────────────────────┘
        ▲                                   ▲
  fleetsmith grid import              cortex console (board, audit,
  (meeting notes, transcripts)        approvals, equip, tokens)

Agents never call the grid. They read files — the same files they would read with no cortex at all, plus read-only peer projections under _fleet/local/grid/peers/<actor>/ and one GRID.md rollup. That keeps the design identical across all three targets and makes degradation trivial: an unreachable cortex just means stale files.

Load-bearing decisions #

Each was forced by evidence. Reversing one means re-checking its source.

DecisionWhy
Task and presence state go through /ingest as our own ontology types, not the memory verbs /memory/* writes bypass governed_upsert, so they never reach the changefeed. Only /ingest rows get WAL, ACL and fan-out
Sync is an SSE wake-up plus cursor reconciliation, never /watch/stream /watch/stream is a stateless per-request poll whose registry is lost on restart. The SSE buffer is 1000 with gap notices — so SSE is only ever the doorbell and the cursor pull is the payload
Single-writer rows: every row keyed by actor The engine has no CRDTs and no client-facing merge. Keying by actor makes concurrent-write conflicts impossible by construction
Rows carry pointers and digests, never handoff bodies or file contents Server-side conditional-ACL enforcement is not yet wired into every call site; client-side redaction and minimal payloads are the defence until it is
One org = one tenant Sub-tenant namespaces are stored but not enforced by the engine. Never build isolation on namespaces
Deterministic overlap checks; model judgment only inside existing agent turns Project invariant since v0.5: deterministic checks carry every gate. The grid adds zero model calls

Degradation #

Every enterprise capability names the core answer it falls back to. Nothing is enterprise-only.

CapabilityDegrades to
Cross-developer overlapsGit file-intersection across local branches — grid overlaps --git-only needs no cortex and no network at all
Org knowledgeFiltering committed _fleet/shared/knowledge/ frontmatter directly
Semantic recallToken overlap over the same committed notes
AuditLocal run events from _fleet/local/runs/
Silent success, loud warning

No configuration produces plain OSS behaviour with no output at all. An unreachable or license-expired cortex produces exactly one warning, stale-marked grid files, and runs that still complete. A cortex problem is never a run failure.

The ee/ boundary #

Core is MIT with one runtime dependency and is complete on its own — the file backend and git are the supported open-source answer, forever. Everything enterprise ships as AGPL-3.0-only in a separate package.

Four rules, each enforced in CI rather than documented and hoped for:

  1. No core file may import from ee/.
  2. Every enterprise source file carries its SPDX header.
  3. No RelataDB binary or source is vendored anywhere.
  4. Every enterprise capability names its degraded core answer.

Core loads the enterprise package fail-soft, by ordinary module resolution. Removing it leaves core byte-identical to open-source behaviour — a property the test suite checks rather than asserts.

RelataDB is bring-your-own-license

The cortex engine is never shipped, vendored or patched here. You run your own instance; Infinia Harness speaks REST to it. The installer can start one for you in Docker, but the image and its license remain the vendor's.