Skip to content

feat(cli): drive the fan-out goals engine from flynn goal --fanout#176

Merged
ion-alpha-dev merged 2 commits into
mainfrom
feat/goal-fanout
Jun 29, 2026
Merged

feat(cli): drive the fan-out goals engine from flynn goal --fanout#176
ion-alpha-dev merged 2 commits into
mainfrom
feat/goal-fanout

Conversation

@ion-alpha-dev

Copy link
Copy Markdown
Collaborator

What

Add flynn goal --fanout. With the flag, a one-shot run is assembled over the durable store with a driver.Router (per-goal model and loop) plus an orchestration.Spawner, and the spawn action is added to the run grant, so the model may delegate self-contained sub-goals to concurrent child agents. The root run and every child report onto one event stream, so the whole fan-out seals into a single record that flynn spine verify checks tier by tier. A child that names an Agent archetype is routed to that archetype's model through a credential-backed resolver. Without the flag a run is a single governed conversation, unchanged.

Two supporting changes make this work over the verifiable path: driver.Spec now carries the governance EventSink and the CompactionBudget (mapped by the default driver), so a Router-built loop records each admission onto the run's stream and compacts a long transcript exactly as the direct assembly did; and a spawned child's grant always includes the model call, so a child delegated only tool actions can still generate.

Why

The advanced orchestration the runtime already supports, per-goal model routing and nested delegation, was only reachable through the embeddable agent, which records onto an ephemeral log. The durable flynn goal path assembled a single executor with one model and no delegation, so a delegated run could not be sealed and verified. This exposes the full goals engine on the path that produces a signed, checkable record.

The child-grant fix is an independent correctness bug: narrowing a child's grant to exactly the requested tool actions dropped the implicit model.generate, so a child spawned with actions:["write"] was admitted for its tools but refused the model call and stalled. The model call is now added before the grant is narrowed against the parent, so it is still intersected with the parent and never widens authority.

How to verify

go build ./... && go vet ./driver/ ./orchestration/ ./cmd/flynn/ are clean.

go test ./driver/ ./orchestration/ ./cmd/flynn/ passes. The new end-to-end test TestFanoutRunSealsOneVerifiableRecord drives a fan-out run with a scripted model that delegates to a concurrent child, confirms a child goal owned by the parent was created, seals the run, verifies the single record, and confirms a one-byte tamper fails to verify. TestChildAlwaysGrantedModelCall covers the grant fix.

End to end against a live provider: flynn --fanout --model <provider:model> --no-learn --verify "<check>" goal "<objective that decomposes into sub-tasks>", then flynn spine verify <run-id> reports integrity: VERIFIED, governance: OK, and ground-truth: GROUNDED for the one record covering the parent and all children. Global flags precede the subcommand.

A fan-out narrowed a child goal's grant to exactly the tool actions the
delegation requested (or the bound Agent's capabilities), which dropped the
implicit `model.generate` action. A child spawned with `actions:["write"]`
was then admitted for its tools but refused the model call, so its loop
could not generate and the run stalled.

Add `withModelGenerate` so the model call is included in a child's requested
authority before it is narrowed against the parent. Because it is still
intersected with the parent's grant, it never widens authority: a child only
gets the model call if the parent holds it. This mirrors how the host grants
the root run its own model call.
The durable one-shot path assembled a single mission executor: one model, no
delegation. The Router (per-goal model and loop) and the delegation spawner
were only reachable through the embeddable agent, which records onto an
ephemeral log, so a delegated run could not be sealed and verified.

Add `flynn goal --fanout`. When set, the run is assembled over the durable
store with a `driver.Router` plus an `orchestration.Spawner`, and `spawn` is
added to the run grant, so the model may delegate self-contained sub-goals to
concurrent child agents. The root and every child report onto one event
stream, so the whole fan-out seals into a single record that `flynn spine
verify` checks. A child that names an Agent archetype is routed to that
archetype's model through a credential-backed resolver. Without the flag a run
is a single conversation, unchanged.

To make this work over the durable path, `driver.Spec` carries the governance
`EventSink` and the `CompactionBudget`, mapped by the default driver, so a
Router-built loop records each admission onto the run's stream and compacts a
long transcript exactly as the direct assembly did. The fan-out root runs
under a larger step budget because a parent spends a step per delegation and a
step per poll while it waits for its children to finish.

Covered by an end-to-end test: a fan-out run delegates to a concurrent child,
folds it in, and seals into one record that verifies and fails closed on a
single-byte tamper.
@ion-alpha-dev ion-alpha-dev merged commit f492b9e into main Jun 29, 2026
12 checks passed
@ion-alpha-dev ion-alpha-dev deleted the feat/goal-fanout branch June 29, 2026 21:27
@github-actions github-actions Bot locked and limited conversation to collaborators Jun 29, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant