Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ linters:
desc: "core may not import the engine (layer inversion); see ARCHITECTURE.md"
- pkg: "github.com/ionalpha/flynn/skill"
desc: "core may not import the engine (layer inversion); see ARCHITECTURE.md"
- pkg: "github.com/ionalpha/flynn/spinesink"
- pkg: "github.com/ionalpha/flynn/internal/spinesink"
desc: "core may not import the engine (layer inversion); see ARCHITECTURE.md"
- pkg: "github.com/ionalpha/flynn/goal"
desc: "core may not import the engine (layer inversion); see ARCHITECTURE.md"
Expand Down
83 changes: 59 additions & 24 deletions ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ package is one plus the highest layer it imports.
| L1 primitives | `spine`, `hlc`, `ids`, `sandbox`, `bus`, `llm/anthropic`, `llm/openai` | Event-log port, hybrid logical clock, seeded id generator, isolation port, in-process event bus, concrete model adapters. |
| L2 core data | `state`, `resource`, `provider` | The host persistence boundary, the event-sourced resource store, the model-adapter registry. |
| L3 mechanisms | `dispatch`, `reconcile`, `jobs`, `memory`, `skill`, `chain` | The governance waist, the reconcile loop, the leased job queue, durable memory and skill stores, the verifiable-record encoder and signer over spine events. |
| L4 governance + domain | `capability`, `budget`, `spinesink`, `goal`, `learn`, `storage/sqlite` | Capability grants, the per-run spend ceiling, the dispatch-to-spine sink, the goal controller, the learning loop, the SQLite backend. |
| L4 governance + domain | `capability`, `budget`, `internal/spinesink`, `goal`, `learn`, `storage/sqlite` | Capability grants, the per-run spend ceiling, the dispatch-to-spine sink, the goal controller, the learning loop, the SQLite backend. |
| L5 orchestration | `mission`, `runtime` | The conversation executor, the wired-up runtime. |
| L6 composition | `tools`, `session` | The default toolset, the conversational session/stream front door. |
| L7 entry | `cmd/flynn`, root `agent` | The binary and the embedding facade. |
Expand All @@ -48,16 +48,38 @@ reach up into a domain package; that inversion is what turns a clean graph into
ball of mud. The direction is enforced by `depguard` (see Invariants), so the
table cannot silently rot.

### Why flat, not nested

Packages stay at the top level rather than nested under `infra/`, `agent/`, etc.
In Go a directory *is* a package and its import identity; nesting changes import
paths and (for a public module) breaks importers, but it does not change the
dependency graph. The graph is what matters, and we enforce it directly with the
layer rule above. So the layering lives in `depguard`, not in folders, and the
top level stays scannable. The two adapter families that genuinely cluster
(`llm/*`, `storage/*`) are the only nesting, because they are alternative
implementations of one port.
### Surface and structure are two different controls

Two separate questions get conflated in most repo layouts, and Flynn answers
them with two separate mechanisms:

- **Internal structure** (which package may import which) is governed by the
`depguard` layer rules above. Folders cannot express import *direction*, so
nesting packages under an `infra/`-style taxonomy would change import paths
without changing the graph. The layering lives in the linter, not in folders.
- **External API surface** (what the module promises importers) is governed by
`internal/`. A public Go module's every exported package is formally API;
`internal/` is the one language-level tool that removes a package from that
promise. It is not about visibility - the code stays world-readable on
GitHub - it only removes importability, i.e. the semver promise.

They are complementary, not alternatives. So the top level holds only the
surface a visitor or embedder should see: the root `agent` facade, the ports a
host implements or consumes, and the domain nouns of the product. The
machinery those are built from (parsers, guards, stores, probes, adapters)
lives under `internal/`, one level flat for the same scannability reason - no
`internal/infra/...` taxonomy. Within each band the packages stay flat, and
the only nesting is adapter families that are alternative implementations of
one port (`llm/*`, `storage/*`).

The mechanical criterion for what stays public: a package is top-level iff
(a) it appears in the exported signatures of a public package (checked with a
`go/types` audit over the declared public set - an internal type leaking
through a public signature compiles but is broken API), or (b) it is a port
third parties implement (`sandbox` backends, `state` stores, `llm` adapters,
`driver` loops, `inbox` sources), or (c) it is a standard's reference
implementation (`chain`, for Provetrail). Everything else is a mechanism and
goes under `internal/`.

## Ports (the interfaces that keep it swappable)

Expand Down Expand Up @@ -220,21 +242,27 @@ evidence.
## Stability tiers

Flynn is a public Go module, so every exported package is, in principle, a
promise. Until v1.0 the promise is deliberately scoped:
promise. The layout makes the tiers physical:

- **Stable surface** (the embedding contract): the root `agent` facade and the
ports a host implements or consumes, `state`, `observe`, `llm`, `capability`,
`fault`, `tools`. Keep these small and guarded; breaking them is a major-version
event.
- **Engine** (`goal`, `mission`, `reconcile`, `resource`, `dispatch`, `session`,
`runtime`, `learn`, ...): importable and visible, but **unstable while pre-1.0**.
These churn as the orchestration graph and router land. Power users may import
them with that understanding.

Pre-1.0 Go semver already permits breaking changes, so the engine stays
refactorable while the top level stays visible. The decision to physically move
the engine under `internal/` is deferred to the v1.0 cut, when this tier list
says exactly what must lock.
`fault`, `tools`, `sandbox`, `secret`, `spine`, `clock`, `ids`, `provider`,
`storage/sqlite`, and `chain` (the Provetrail reference implementation, which
external verifiers must be able to import). Keep these small and guarded;
breaking them is a major-version event.
- **Domain surface** (`goal`, `mission`, `reconcile`, `resource`, `dispatch`,
`session`, `runtime`, `learn`, `budget`, `memory`, `skill`, `extension`,
`controlplane`, `orchestration`, ...): importable and visible, but **unstable
while pre-1.0**. These churn as the orchestration graph and router land.
Power users may import them with that understanding.
- **Mechanisms** (`internal/...`): not importable, no promise at all. Parsers,
guards, stores, probes, and adapters the surfaces are built from.

Pre-1.0 Go semver already permits breaking changes, so the domain surface stays
refactorable while remaining visible. The mechanism band moved under
`internal/` pre-launch, while the module had no external importers - the
cheapest such a move can ever be - so godoc shows exactly the curated surface
and nothing below it ever enters the compatibility promise.

## Concurrency and lifecycle

Expand Down Expand Up @@ -262,7 +290,8 @@ is part of CI).
- **`resource`** event-sourced state of record; **`spine`** the raw event log;
**`storage/sqlite`** the durable backend; **`state`** the host boundary.
- **`dispatch`** the governance waist; **`capability`** grants; **`budget`** the
per-run spend ceiling; **`spinesink`** routes dispatched actions onto the spine.
per-run spend ceiling; **`internal/spinesink`** routes dispatched actions onto
the spine.
- **`chain`** the verifiable-record layer: canonical CBOR encoding, the RFC 9162
Merkle log, COSE signing, sealed run records, and the verifier behind
`flynn spine verify`.
Expand All @@ -273,3 +302,9 @@ is part of CI).
- **`clock`**, **`ids`**, **`hlc`** the determinism ports; **`fault`** the error
taxonomy; **`observe`** logging/tracing; **`bus`** the in-process event bus;
**`jobs`** the leased job queue.
- **`internal/...`** the mechanism band: model plumbing (`inference`, `gguf`,
`gbnf`, `modelformat`, `modeltrust`, `huggingface`, `hardware`, `acquire`),
network and credential guards (`bindguard`, `vault`, `credential`), the
extension machinery (`integrations`, `catalog`, `flow`, `ops`, `service`,
`playbook`, `dependency`, `fetch`, `source`), and the rest of the plumbing.
Not importable; see Stability tiers.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -364,13 +364,13 @@ skill/, memory/ durable skill and memory stores
llm/, provider/ the model port and concrete adapters
tools/ the default agentic toolset
sandbox/ the isolation boundary for command execution
integrations/ data-driven integration and plugin engine
clock/, ids/, hlc/ determinism: time source, sortable ids, write ordering
fault/ typed, classified error model
runtime/ wires controller, worker, store, and bus together
session/ conversational front door and event stream
storage/sqlite/ the durable SQLite backend
internal/ build and runtime internals
internal/ the mechanism band: model plumbing, guards, the
integration/extension engine, and other non-API machinery
```

See [ARCHITECTURE.md](ARCHITECTURE.md) for the full layer map, the ports a host
Expand Down
2 changes: 1 addition & 1 deletion agent.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,12 @@ import (
"strings"
"time"

"github.com/ionalpha/flynn/archetype"
"github.com/ionalpha/flynn/brakes"
"github.com/ionalpha/flynn/bus"
"github.com/ionalpha/flynn/capability"
"github.com/ionalpha/flynn/driver"
"github.com/ionalpha/flynn/goal"
"github.com/ionalpha/flynn/internal/archetype"
"github.com/ionalpha/flynn/jobs"
"github.com/ionalpha/flynn/llm"
"github.com/ionalpha/flynn/mission"
Expand Down
2 changes: 1 addition & 1 deletion agent_agentkind_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import (
"testing"
"time"

"github.com/ionalpha/flynn/archetype"
"github.com/ionalpha/flynn/internal/archetype"
"github.com/ionalpha/flynn/llm/llmtest"
"github.com/ionalpha/flynn/resource"
)
Expand Down
2 changes: 1 addition & 1 deletion cmd/flynn/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ import (

"golang.org/x/term"

"github.com/ionalpha/flynn/internal/vault"
"github.com/ionalpha/flynn/provider"
"github.com/ionalpha/flynn/secret"
"github.com/ionalpha/flynn/vault"
)

// runAuth implements the `flynn auth` command group: managing the credentials the
Expand Down
10 changes: 5 additions & 5 deletions cmd/flynn/catalog.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,15 @@ import (
"sort"
"strings"

"github.com/ionalpha/flynn/credential"
"github.com/ionalpha/flynn/extension"
"github.com/ionalpha/flynn/extension/catalog"
"github.com/ionalpha/flynn/integrations"
"github.com/ionalpha/flynn/internal/credential"
"github.com/ionalpha/flynn/internal/integrations"
"github.com/ionalpha/flynn/internal/ops"
"github.com/ionalpha/flynn/internal/service"
"github.com/ionalpha/flynn/internal/vault"
"github.com/ionalpha/flynn/mission"
"github.com/ionalpha/flynn/ops"
"github.com/ionalpha/flynn/resource"
"github.com/ionalpha/flynn/service"
"github.com/ionalpha/flynn/vault"
)

// runIntegrations implements `flynn integrations <subcommand>`: the catalog of
Expand Down
2 changes: 1 addition & 1 deletion cmd/flynn/consent.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import (
"io"
"strings"

"github.com/ionalpha/flynn/inference/modelsource"
"github.com/ionalpha/flynn/internal/inference/modelsource"
)

// requireConsent is the no-footgun gate: before a risky model run proceeds, the user must
Expand Down
2 changes: 1 addition & 1 deletion cmd/flynn/consent_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import (
"strings"
"testing"

"github.com/ionalpha/flynn/inference/modelsource"
"github.com/ionalpha/flynn/internal/inference/modelsource"
"github.com/ionalpha/flynn/sandbox"
)

Expand Down
4 changes: 2 additions & 2 deletions cmd/flynn/credentials.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ import (
"os"
"strings"

"github.com/ionalpha/flynn/credential"
"github.com/ionalpha/flynn/internal/credential"
"github.com/ionalpha/flynn/internal/vault"
"github.com/ionalpha/flynn/secret"
"github.com/ionalpha/flynn/vault"
)

// openCredentialStore opens the durable store and returns a credential facade over
Expand Down
4 changes: 2 additions & 2 deletions cmd/flynn/credentials_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@ import (
"errors"
"testing"

"github.com/ionalpha/flynn/credential"
"github.com/ionalpha/flynn/internal/credential"
"github.com/ionalpha/flynn/internal/vault"
"github.com/ionalpha/flynn/resource"
"github.com/ionalpha/flynn/secret"
"github.com/ionalpha/flynn/vault"
)

// memKeyring is an in-memory vault.Keyring for tests, so the vault uses its
Expand Down
4 changes: 2 additions & 2 deletions cmd/flynn/deploy.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,10 @@ import (

"github.com/ionalpha/flynn/clock"
"github.com/ionalpha/flynn/extension"
"github.com/ionalpha/flynn/internal/ops"
"github.com/ionalpha/flynn/internal/service"
"github.com/ionalpha/flynn/mission"
"github.com/ionalpha/flynn/ops"
"github.com/ionalpha/flynn/resource"
"github.com/ionalpha/flynn/service"
)

// runDeploy implements `flynn deploy <extension> [flags] [json-input]`. It runs a
Expand Down
4 changes: 2 additions & 2 deletions cmd/flynn/deps.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ import (
"os"
"text/tabwriter"

"github.com/ionalpha/flynn/dependency"
"github.com/ionalpha/flynn/fetch"
"github.com/ionalpha/flynn/internal/dependency"
"github.com/ionalpha/flynn/internal/fetch"
"github.com/ionalpha/flynn/sandbox"
)

Expand Down
10 changes: 5 additions & 5 deletions cmd/flynn/get.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,16 @@ import (
"strings"
"text/tabwriter"

"github.com/ionalpha/flynn/archetype"
"github.com/ionalpha/flynn/controlplane"
"github.com/ionalpha/flynn/credential"
"github.com/ionalpha/flynn/goal"
"github.com/ionalpha/flynn/inbox"
"github.com/ionalpha/flynn/instance"
"github.com/ionalpha/flynn/internal/archetype"
"github.com/ionalpha/flynn/internal/credential"
"github.com/ionalpha/flynn/internal/instance"
"github.com/ionalpha/flynn/internal/profilestore"
"github.com/ionalpha/flynn/internal/service"
"github.com/ionalpha/flynn/internal/version"
"github.com/ionalpha/flynn/profilestore"
"github.com/ionalpha/flynn/resource"
"github.com/ionalpha/flynn/service"
)

// cpKind binds a resource kind to how the read surface displays it.
Expand Down
4 changes: 2 additions & 2 deletions cmd/flynn/get_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ import (
"strings"
"testing"

"github.com/ionalpha/flynn/archetype"
"github.com/ionalpha/flynn/controlplane"
"github.com/ionalpha/flynn/instance"
"github.com/ionalpha/flynn/internal/archetype"
"github.com/ionalpha/flynn/internal/instance"
"github.com/ionalpha/flynn/resource"
)

Expand Down
2 changes: 1 addition & 1 deletion cmd/flynn/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,12 @@ import (
"strings"

"github.com/ionalpha/flynn/harness"
"github.com/ionalpha/flynn/internal/vault"
"github.com/ionalpha/flynn/internal/version"
"github.com/ionalpha/flynn/learn"
"github.com/ionalpha/flynn/llm"
"github.com/ionalpha/flynn/provider"
"github.com/ionalpha/flynn/secret"
"github.com/ionalpha/flynn/vault"
)

// dataDirCommands are the subcommands whose only state is the data directory.
Expand Down
8 changes: 4 additions & 4 deletions cmd/flynn/modelbless.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,10 @@ import (
"sort"
"strings"

"github.com/ionalpha/flynn/catalog"
"github.com/ionalpha/flynn/fetch"
"github.com/ionalpha/flynn/huggingface"
"github.com/ionalpha/flynn/inference/modelsource"
"github.com/ionalpha/flynn/internal/catalog"
"github.com/ionalpha/flynn/internal/fetch"
"github.com/ionalpha/flynn/internal/huggingface"
"github.com/ionalpha/flynn/internal/inference/modelsource"
)

// runModelBless implements `flynn models bless <hf:owner/repo>`: resolve a Hugging Face
Expand Down
4 changes: 2 additions & 2 deletions cmd/flynn/modelbless_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ package main
import (
"testing"

"github.com/ionalpha/flynn/catalog"
"github.com/ionalpha/flynn/huggingface"
"github.com/ionalpha/flynn/internal/catalog"
"github.com/ionalpha/flynn/internal/huggingface"
)

func TestSelectServeFilesKeepsWeightsDropsDocs(t *testing.T) {
Expand Down
4 changes: 2 additions & 2 deletions cmd/flynn/modelcheck.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ import (
gruntime "runtime"
"strings"

"github.com/ionalpha/flynn/inference"
"github.com/ionalpha/flynn/inference/provision"
"github.com/ionalpha/flynn/internal/inference"
"github.com/ionalpha/flynn/internal/inference/provision"
"github.com/ionalpha/flynn/sandbox"
)

Expand Down
8 changes: 4 additions & 4 deletions cmd/flynn/modelcmds.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,12 @@ import (
"strings"
"time"

"github.com/ionalpha/flynn/catalog"
"github.com/ionalpha/flynn/harness"
"github.com/ionalpha/flynn/inference/modelsource"
"github.com/ionalpha/flynn/internal/catalog"
"github.com/ionalpha/flynn/internal/inference/modelsource"
"github.com/ionalpha/flynn/internal/profilestore"
"github.com/ionalpha/flynn/internal/reliability"
"github.com/ionalpha/flynn/llm"
"github.com/ionalpha/flynn/profilestore"
"github.com/ionalpha/flynn/reliability"
"github.com/ionalpha/flynn/resource"
)

Expand Down
6 changes: 3 additions & 3 deletions cmd/flynn/modelfetch.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@ import (
"path/filepath"
"strings"

"github.com/ionalpha/flynn/catalog"
"github.com/ionalpha/flynn/fetch"
"github.com/ionalpha/flynn/inference/launch"
"github.com/ionalpha/flynn/internal/catalog"
"github.com/ionalpha/flynn/internal/fetch"
"github.com/ionalpha/flynn/internal/inference/launch"
)

// runModelFetch implements `flynn models fetch <id>`: download a catalog model's
Expand Down
2 changes: 1 addition & 1 deletion cmd/flynn/modelfetch_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import (
"strings"
"testing"

"github.com/ionalpha/flynn/catalog"
"github.com/ionalpha/flynn/internal/catalog"
)

func TestRunModelFetchBranches(t *testing.T) {
Expand Down
2 changes: 1 addition & 1 deletion cmd/flynn/modelgate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import (
"bytes"
"testing"

"github.com/ionalpha/flynn/inference/modelsource"
"github.com/ionalpha/flynn/internal/inference/modelsource"
"github.com/ionalpha/flynn/sandbox"
)

Expand Down
Loading
Loading