Documentation
¶
Overview ¶
Package deploy assembles a deployment from a resource+agent document: the Document DTO (resources forming a dependency DAG, agents binding them) and the Builder, in three phases.
- Builder.Plan is pure. It resolves every kind to a factory, validates that every dependency a document supplies is declared, checks the DAG is acyclic, and checks that every dependency edge is type-correct — a resource factory's build type (or a container's item type, or an external's injected type) must satisfy the consumer's resource.Requirement. Every problem is reported at once, and nothing is constructed, so a configuration error is diagnosed without side effects.
- Plan.Build constructs resources in topological order. Dependency types are already proven, so the only conversions left are the typed reads inside factories and Resolve/Collect at the call site. On failure every value built so far is closed in reverse order.
- Builder.Wire attaches observers and hooks, binds agents and hands the assembled deployment to resources implementing Binder.
Builder.Build is Plan plus Build, and Builder.Deploy adds Wire. A caller that wants a configuration preflight calls Plan directly — it needs a registry containing every kind the document names.
Two opt-outs make an edge check conditional: a factory that instantiates Factory[any], and a dependency declared with Requirement.Type == any. Both are for test doubles and plugin hosts that forward a value they do not model; such a contract is then enforced where the value is read.
Document and DAG shape errors (a malformed entry, a cycle) are reported one at a time, because the later checks need a resolved order; every dependency edge is reported together.
The package owns only two concrete resource kinds: the secret stores it assembles first (so their scheme can serve every other settings subtree) and nothing else. Everything kind-specific arrives through the registry.
Index ¶
- func BindAgent(ctx context.Context, reg *resource.Registry, result *Result, ...) (*agent.Agent, error)
- func Collect[T any](result *Result) []T
- func LoadLayers(ctx context.Context, layers []Layer) (Document, Provenance, error)
- func Resolve[T any](result *Result, name string) (T, error)
- type Binder
- type Builder
- type BuilderOption
- func WithExternalResources(externals []ExternalResource) BuilderOption
- func WithLoader(loader *resource.Loader) BuilderOption
- func WithResolver(resolver *resource.ReferenceResolver) BuilderOption
- func WithSecretCacheCapacity(n int) BuilderOption
- func WithSecretCacheTTL(ttl time.Duration) BuilderOption
- type Document
- type External
- type ExternalResource
- type Layer
- type LayerRef
- type Named
- type Plan
- type Provenance
- type Result
- func (r *Result) Agent(name string) (*agent.Agent, bool)
- func (r *Result) AgentNames() []string
- func (r *Result) Close() error
- func (r *Result) Detach(names ...string)
- func (r *Result) Names() []string
- func (r *Result) Resolver() *resource.ReferenceResolver
- func (r *Result) Secrets() *resource.SecretResolver
- func (r *Result) Value(name string) (any, bool)
- type View
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func BindAgent ¶ added in v0.1.11
func BindAgent( ctx context.Context, reg *resource.Registry, result *Result, loader *resource.Loader, resolver *resource.ReferenceResolver, secrets *resource.SecretResolver, name string, def agent.Definition, ) (*agent.Agent, error)
BindAgent assembles one agent.Agent from its Definition: the engine from the registry (kind = EngineRef.Kind / Impl), then each hook under "hook.<slot>", wiring resource.Wireable hooks before recording them on the agent. It never mutates result (result.agents stays untouched), so callers decide where the assembled agent is recorded — deployment build records it on the Result, while core/runtime registers it in the live agent registry.
On failure, every component already constructed (engine and any wired hooks) is closed in reverse order so a partial assembly never leaks attached subscriptions or other resources.
func Collect ¶ added in v0.9.0
Collect returns every built value that is a T, in construction order. It is the typed form of "walk the deployment and keep what implements X", used by post-build attachment passes that do not care which resource it was.
func LoadLayers ¶
LoadLayers loads every layer with its own loader (file refs resolve against that layer's BaseDir), merges them in ascending priority order, and returns the merged document plus provenance.
func Resolve ¶ added in v0.9.0
Resolve returns the built value registered under name, narrowed to T.
It is the typed read internal wiring should use: the caller names the type it expects, so a mismatch is reported once, here, instead of being re-asserted at every call site. Result.Value stays for host-level discovery, where the resource name comes from configuration and no type is known at compile time. Resolve is a free function because Go has no generic methods.
Types ¶
type Binder ¶ added in v0.9.0
Binder is implemented by resource values that need the fully assembled deployment after every resource and agent has been wired: backends registering into hosts, directories snapshotting the agent set.
Binder lives in deploy — the package that calls it — rather than in core/resource, because its argument is the assembly layer's view and only this package can name it. That keeps core/resource free of agent and deployment concepts.
type Builder ¶
type Builder struct {
// contains filtered or unexported fields
}
Builder constructs a deployment's resources from a Document using an explicit resource.Registry. The registry is owned by the caller; Builder never touches global state.
func NewBuilder ¶
func NewBuilder(registry *resource.Registry, opts ...BuilderOption) *Builder
NewBuilder returns a Builder over registry. A nil registry yields an empty one.
func (*Builder) Build ¶
Build validates the document, resolves the resource DAG, and constructs every resource in dependency order. On failure, values already built are closed in reverse construction order (rollback).
Build is Builder.Plan plus Plan.Build: a caller that wants to report configuration errors before constructing anything (a preflight, a CLI --check, a config validator) calls Plan directly.
func (*Builder) Deploy ¶
Deploy is the convenience entry point: Build, then Wire. On wire failure every built value is closed before returning.
func (*Builder) Plan ¶ added in v0.9.0
Plan validates doc against the builder's registry without constructing anything.
Every problem is reported at once, joined into one error, because a deployment document is authored as a whole: fixing one dep at a time is the slow path for both humans and models.
func (*Builder) Wire ¶
Wire runs the post-build wiring phase: resource values implementing resource.Wireable attach themselves (observers to buses, hooks to streams), then every agent is bound from its engine and hooks, and finally values implementing Binder receive the assembled deployment (agents included). Wire never participates in the construction DAG, so observed values never depend on their observers.
type BuilderOption ¶
type BuilderOption func(*Builder)
BuilderOption configures a Builder.
func WithExternalResources ¶ added in v0.2.6
func WithExternalResources(externals []ExternalResource) BuilderOption
WithExternalResources configures the builder with caller-owned dependency values. Values are copied by reference: deploy never closes or mutates them. Multiple calls accumulate; duplicates are rejected at Build time.
func WithLoader ¶
func WithLoader(loader *resource.Loader) BuilderOption
WithLoader supplies the deployment-level loader used to materialize settings subtrees that are {"file":…} / {"embed":…} references, and passed to factories for their own source resolution.
func WithResolver ¶ added in v0.2.0
func WithResolver(resolver *resource.ReferenceResolver) BuilderOption
WithResolver adds custom schemes to the expansion used for inline ${scheme:ref} strings in every settings subtree (resources, agent engine, agent hooks) before a factory decodes them. The custom schemes are merged on top of the all-open default resolver (env + home, plus base when the loader declares a base directory); a custom scheme with the same name overrides the built-in one.
func WithSecretCacheCapacity ¶ added in v0.8.0
func WithSecretCacheCapacity(n int) BuilderOption
WithSecretCacheCapacity bounds how many secret names the lazy-lookup cache holds at once. Zero keeps the default (see resource.WithCacheCapacity); raise it for a deployment that resolves more distinct names than that within one TTL window, so they are not re-fetched from the backend.
func WithSecretCacheTTL ¶ added in v0.2.0
func WithSecretCacheTTL(ttl time.Duration) BuilderOption
WithSecretCacheTTL sets how long lazy secret lookups reuse a cached value before hitting the backend again. Zero keeps the default (one minute); a negative value disables caching.
type Document ¶
type Document struct {
Version string `json:"version,omitempty"`
Resources resource.Resources `json:"resources,omitempty"`
Agents map[string]agent.Definition `json:"agents,omitempty"`
// Runtime is an application-runtime-owned configuration subtree.
// Parse preserves it verbatim so the runtime layer can decode it
// strictly without weakening strictness for the deployment schema.
Runtime *resource.Opaque `json:"runtime,omitempty"`
}
Document is the top-level deployment DTO: a versioned resource area (the DAG) plus an agent area binding resources.
func Parse ¶
Parse decodes one deployment document from YAML or JSON into a Document. It applies the shared strict semantics of core/utils: JSON is detected by the Kubernetes rule (first non-whitespace byte is an open brace), YAML is converted strictly, unknown fields are rejected, and a trailing document is an error. After decoding, Parse validates the document semantically (version, resources, agents).
type External ¶ added in v0.2.6
type External struct {
// Name is the resource-ref name used in document deps, e.g. "db".
Name string `json:"name"`
// Contract is the author's declared contract name, e.g. "db.Pool". It is
// document-facing: it travels with the deployment model so a reviewer can
// read what an injected value is meant to be. The check the assembly
// layer actually enforces is [ExternalResource.Type], derived from the
// injected Go value, so a stale contract string can never admit a
// mismatched value.
Contract string `json:"contract"`
}
External declares one caller-owned dependency supplied to the deployment builder. Externals are visible to dependency resolution but are never constructed, wired, or closed by deploy.
type ExternalResource ¶ added in v0.2.6
ExternalResource pairs one External declaration with the caller-owned value that satisfies it, plus the value's Go type.
Type is recorded at injection time and is what the plan checks a consuming factory's resource.Requirement against. It is the value's concrete type, so injecting a *tool.Registry satisfies a dependency that requires the tool.Source interface.
func NewExternal ¶ added in v0.9.0
func NewExternal(name, contract string, value any) ExternalResource
NewExternal records one caller-owned dependency together with its Go type. Injection sites use this constructor rather than a struct literal so an external can never enter the builder without a type to check against.
func (ExternalResource) Validate ¶ added in v0.2.6
func (e ExternalResource) Validate() error
Validate checks the declaration and rejects nil values.
type Layer ¶
Layer is one configuration layer. Layers are merged in ascending Priority order, so a higher priority layer overrides a lower one. The lowest-priority layer must be a complete document (with version); later layers may be partial.
type Named ¶ added in v0.9.0
Named is one built value together with the resource name it was registered under.
type Plan ¶ added in v0.9.0
type Plan struct {
// contains filtered or unexported fields
}
Plan is a deployment document checked against a registry: every kind resolves to a factory, every dependency a document supplies is declared by that factory, every required dependency is present, the resource graph is acyclic, and every dependency edge is type-correct.
Nothing is constructed and no side effect is performed. Plan is therefore the place a configuration error is reported — one run lists every bad edge instead of the first — and the place a validator (a CLI preflight, the flowcraft-config skill) can run without touching the network, the filesystem or a sandbox.
type Provenance ¶
type Provenance struct {
Resources map[string]LayerRef `json:"resources"`
Agents map[string]LayerRef `json:"agents"`
}
Provenance records which layer provided each top-level resource and agent in the merged document: the highest-priority layer that declared the key.
type Result ¶
type Result struct {
// contains filtered or unexported fields
}
Result owns the built resource values in construction order. The caller closes it (or the runtime layer closes it) when done.
func (*Result) AgentNames ¶
AgentNames returns the sorted bound agent names.
func (*Result) Close ¶
Close closes every io.Closer resource value in reverse construction order, then closes every bound agent (engine and lifecycle hooks).
func (*Result) Detach ¶ added in v0.1.14
Detach marks resource names as caller-owned: Result.Close will not close them, and the caller takes over their lifecycle. It is used by the runtime to reject a reload whose event bus factory returned the current generation's bus without letting the aborted result close a bus another generation still uses.
func (*Result) Resolver ¶ added in v0.4.10
func (r *Result) Resolver() *resource.ReferenceResolver
Resolver returns the effective settings reference resolver assembled by Build. Callers that construct values after the build (for example a runtime registering dynamic agents) pass it back into BindAgent so dynamic settings expand exactly like deployment-time settings.
func (*Result) Secrets ¶ added in v0.4.10
func (r *Result) Secrets() *resource.SecretResolver
Secrets returns the effective secret resolver assembled by Build, or nil when the deployment declared no secret stores.
func (*Result) Value ¶
Value returns the built resource registered under name, or false.
It is the dynamic accessor for host-level discovery, where the resource name comes from configuration and the caller cannot name the type at compile time (core/runtime.Runtime.Resource and delegation/hostwrap both need that). Core wiring uses Resolve and Collect, which name the type.
type View ¶ added in v0.9.0
type View interface {
// Agent returns the assembled agent registered under name.
Agent(name string) (*agent.Agent, bool)
// AgentNames returns the sorted bound agent names.
AgentNames() []string
// Names returns the sorted built resource names.
Names() []string
}
View is the read-only view of an assembled deployment handed to resource values after every resource and agent exists.
It is deliberately narrow: a resource that needs to look something up after assembly declares exactly what it reads, and deploy hands it this view instead of the mutable Result. Consumers narrow it structurally — a package that only needs agent lookup declares its own two-method interface and the view satisfies it.