Documentation
¶
Overview ¶
Package ir provides stable IR types (the contract), structural validation, and the definition digest.
Index ¶
- Constants
- Variables
- func CallWorkflowParentPath(callPath string) string
- func ChildPath(parent, keyword string, idx int, branch string) string
- func ContainerPath(name, field string) string
- func FirstRuntimeComposePath(wf *Workflow) (string, bool)
- func HasErrors(ds []Diagnostic) bool
- func LastStepID(body NodeList) (string, bool)
- func MapImageTargetOwners(wf *Workflow) map[string][]string
- func MapImageTargets(wf *Workflow) map[string]bool
- func MapProductShape(mapPath string) bool
- func NodeSubtreeDigest(n Node) (string, error)
- func OutputFilesByStepID(wf *Workflow) map[string]OutputFiles
- func PathFor(parent, keyword, stepID string, index int) string
- func SingleMapBodyShape(staticPath string) (mapPath, suffix string, ok bool)
- func WalkNodes(list NodeList, parent string, visit func(n Node, path string))
- type AgentRole
- type AgentStep
- type ArtifactContract
- type ArtifactExports
- type CallStep
- type CodeStep
- type Compose
- type ComposeValidationError
- type Container
- type Diagnostic
- type Duration
- type Expr
- type Gate
- type If
- type JSONSchema
- type LoadedAsset
- type LoadedAssetFile
- type LoadedDefinition
- func (ld *LoadedDefinition) ComputeDigest() (string, error)
- func (ld *LoadedDefinition) Module(id string) (*LoadedModule, bool)
- func (ld *LoadedDefinition) Root() *LoadedModule
- func (ld *LoadedDefinition) WalkImportEdges(fn func(LoadedImportEdge) error) error
- func (ld *LoadedDefinition) WalkModules(fn func(*LoadedModule) error) error
- type LoadedImportEdge
- type LoadedModule
- type Loop
- type Map
- type Node
- type NodeList
- type OutputFile
- type OutputFiles
- type Parallel
- type Prune
- type PruneKeep
- type Ratio
- type RawConfig
- type React
- type Reduce
- type Resources
- type RetryPolicy
- type Severity
- type SignalStep
- type SkillCorpus
- type Skip
- type StepSkillRouting
- type Template
- type TemplateValue
- type Tool
- type ToolImpl
- type Try
- type Workflow
- func (w *Workflow) ComputeDigest(composeFiles map[string][]byte, assets map[string]LoadedAsset) (string, error)
- func (w *Workflow) RoleByName(name string) (AgentRole, bool)
- func (w *Workflow) SetDigest(composeFiles map[string][]byte, assets map[string]LoadedAsset) (string, error)
- func (w *Workflow) StructuralDigest(composeFiles map[string][]byte, assets map[string]LoadedAsset) (string, error)
- type WorkflowInputFiles
Constants ¶
const CallWorkflowSegment = "workflow"
const DigestScheme = "awf-d1:sha256:"
DigestScheme is the self-describing content-address prefix shared by workflow digests and state blob refs. Bump (awf-d2) only on a deliberate canonicalization change; state.blobRefPrefix and container/docker.snapshotBlobScheme are pinned equal to this by tests.
Variables ¶
QuorumVerdictFields is the fixed typed-output shape a quorum reducer commits. It is the SINGLE source of truth for the {passed, votes, agree} verdict contract: this validator binds downstream refs against it, and engine's runQuorumReduce must produce EXACTLY these keys — pinned by a cross-package engine test (TestQuorumReduceOutputMatchesVerdictFields) so the producer (engine/reduce.go) and the validator can never silently drift. Exported only for that test.
Functions ¶
func CallWorkflowParentPath ¶
func ChildPath ¶
ChildPath returns the path of a control node's named child block — the convention is
<parent>.<keyword>[idx].<branch>
e.g. `if[1].then`, `loop[0].body`, `gate[2].generate`, `try[0].catch`. Centralizes the branch-label / path-join convention so every validation pass (validateStructural, validateRefs, validateSchema, indexProducers) addresses the same nested node identically. Without this helper, a typo in any one walker's `path+".then"` literal would silently produce a divergent path for the same node in different passes.
func ContainerPath ¶
ContainerPath builds a dotted path for container metadata, e.g.:
ContainerPath("lab", "image") → "containers.lab.image"
ContainerPath("lab", "") → "containers.lab"
Used by the structural and compose passes for container-attached diagnostics.
func FirstRuntimeComposePath ¶
FirstRuntimeComposePath returns the first static path of a runtime compose block, or ("", false) when the workflow does not use runtime compose.
func HasErrors ¶
func HasErrors(ds []Diagnostic) bool
HasErrors reports whether ds contains at least one Diagnostic of Severity Error. Used by the CLI (slice 1.6) to decide the exit code: zero only when HasErrors returns false.
func LastStepID ¶
LastStepID returns the id of the body's LAST node when that node is a code/agent step, plus a present flag. The prune controller reads the score from the committed step output at ItemPath(mapPath, N) + "." + id, so the engine needs the id of the score-producing step; the validator already guarantees (AWF5008) the last node is a step with a numeric score field. Returns ("", false) for an empty body or a control-flow terminal node — mirrors lastStepSchema's walk so engine + validator agree on which node produces the score.
func MapImageTargetOwners ¶
func MapImageTargets ¶
MapImageTargets returns the bare container name of every `map` whose `image:` supplies a runtime per-element image (P6a). Such a container may declare resources alone — its image arrives per-element at dispatch. The runtime's capability guard (cli) also uses this to detect a runtime-image workflow. Recurses through every step-bearing control kind.
func MapProductShape ¶
MapProductShape reports whether a map at static mapPath is in the same v1 single-map envelope that aggregate refs can resolve: exactly one map boundary, no gate or loop multiplicity.
func NodeSubtreeDigest ¶
NodeSubtreeDigest is the content hash of a single node's definition subtree (RFC-8785/JCS canonical, scheme-prefixed). Reused as one input to the WS-6b node_key. Node.MarshalJSON already emits the canonical key-presence shape. Exported so engine.Commit can compute the key without reaching into ir internals.
func OutputFilesByStepID ¶
func OutputFilesByStepID(wf *Workflow) map[string]OutputFiles
OutputFilesByStepID indexes every code/agent step's and reduced map product's output_files by id in ONE graph walk. Shared by input_files validation (ir/validate_input_files.go) and engine resolution (engine.resolveInputFiles) so neither walks per-ref.
func PathFor ¶
PathFor computes the static IR path of a node for use in a Diagnostic. parent is the path of the enclosing node (empty at the top level); keyword is the control-node keyword (e.g. "if", "loop", "gate") and stepID is the step's id — exactly one of {keyword, stepID} is non-empty (a step has an id; a control node has a keyword); index is the node's position in its parent's children list (used only for control nodes — steps are addressed by id).
If both are set (a caller bug), stepID takes precedence; the keyword is ignored. The "?[index]" fallback below covers the neither-set case.
The runtime addressing function (engine/path, Phase 2) extends this with iter/attempt suffixes for in-flight nodes (e.g. `loop[0].body.iter-3`, `gate[0].attempt-2.generate`). The static prefix the validator emits matches the runtime form — `loop[0].body` is the same string in both, just without the per-iteration suffix.
Per AWF standard §8: "step nodes by id; control nodes positionally — try[0].catch, if[1].then, loop[0].body.iter-3, gate[0].attempt-2.generate, parallel[2], map[0].item-3."
func SingleMapBodyShape ¶
SingleMapBodyShape reports whether staticPath is the v1 aggregation shape: exactly one map[ boundary, no gate[ or loop[ anywhere, the map segment followed by "body". Returns the map's static path ("...map[N]") and the producer's suffix after ".body.". The single owner of this grammar predicate (engine uses it too).
Suffix contract: suffix has NO leading dot (it is the path segments after ".body.", joined by "."); callers compose it as ItemPath(mapPath, N) + "." + suffix.
func WalkNodes ¶
WalkNodes is the single pre-order traversal of a NodeList. It calls visit once per node — in slice order, parent before children — with the node's OWN static IR path, then recurses into every child branch using ir.PathFor / ir.ChildPath, centralizing the branch-label convention AND the Parallel quirk (children are addressed under the bare parallel[i] path, no branch label) so the family of index/collect walkers can never drift from the runtime addressing in engine/path.go. Slice order is load-bearing: indexProducers / StepPathIndex are last-write-wins on (validator-rejected) duplicate ids, so the order must match the old walkers' `for i, n := range`.
visit is called for every node EXCEPT *Skip — skip carries no step/map identity and no addressable child, and is absent from the §8 addressing grammar, so none of the index/collect callers act on it (byte-identical to the walkers this replaces). Leaf-only callers (step indexers) no-op on control kinds via their type switch; control-emitting callers (the map indexers) act on the kind they care about. All per-caller state (accumulator maps, *collector) is captured by the closure, never threaded through this signature — that minimalism is what lets the pure builders share one shape. Walkers that carry traversal-direction state (walkRefs's evaluateAllowed) or do node-anchored validation on most kinds (walkStructural) are deliberately NOT built on this; see their definitions.
ir.Node is a sealed sum type (the unexported isNode marker), so default is unreachable for valid input; it panics — like go/ast.Walk and the existing cli/runtimes.go walkAgentRefsNodes — so a future node kind added without updating this switch fails loudly instead of being silently dropped. This is forward-compat documentation, not a coverage guarantee; the static guarantee (gochecksumtype + //sumtype:decl on ir.Node) is a separate, out-of-scope call.
Types ¶
type AgentRole ¶
type AgentRole struct {
Uses string `json:"uses"`
Model string `json:"model,omitempty"`
SystemPrompt string `json:"system_prompt,omitempty"`
With RawConfig `json:"with,omitempty"`
}
AgentRole is one named, reusable agent configuration in the top-level `agents:` map. It binds an EXISTING adapter (Uses) plus opaque defaults the base adapter consumes: Model and SystemPrompt are convenience fields that the run-start resolver folds into the role's With map as opaque keys (the base adapter — e.g. claude — already reads with["model"]/with["system_prompt"]). With carries arbitrary opaque base-adapter config (e.g. mcp_servers — the memory MCP handle). AWF never interprets a With key; the named adapter validates it.
A role does NOT carry a typed-output schema: the typed-output contract is the STEP's own output_schema (engine/agent_step.go sources inv.OutputSchema from the AgentStep, never from with:), and the with: overlay seam cannot reach it (matching design spec §3.3 — a role is adapter + model + system_prompt + with: only).
type AgentStep ¶
type AgentStep struct {
ID string `json:"id"`
Container string `json:"container,omitempty"`
Uses string `json:"uses"`
With RawConfig `json:"with,omitempty"`
Continues string `json:"continues,omitempty"` // id of the prior agent turn to continue (engine-owned thread)
OutputSchema *JSONSchema `json:"output_schema,omitempty"`
OutputFiles OutputFiles `json:"output_files,omitempty"`
// OutputArtifact, valid ONLY on a containerless agent step, serializes the
// step's validated typed Output as canonical JSON and publishes it as the
// named artifact step.<id>.files.<name>. Mutually exclusive with
// output_files; requires output_schema. The dispatcher produces the bytes
// (a containerless step has no filesystem to capture from).
OutputArtifact string `json:"output_artifact,omitempty"`
Skills *StepSkillRouting `json:"skills,omitempty"`
// InputFiles maps an in-container destination path → a static artifact or asset
// reference (step.<id>.files.<name> or asset.<id>). The engine resolves each to
// a CAS blob from a prior named output_files commit or run-start asset snapshot
// and stages the bytes via Backend.CopyTo BEFORE this step runs. Static, not a
// {{ }} template (AWF3007). Requires a container (rejected on containerless
// agent steps at runtime).
InputFiles map[string]string `json:"input_files,omitempty"`
Timeout *Duration `json:"timeout,omitempty"`
IdempotencyKey *Template `json:"idempotency_key,omitempty"`
Retry *RetryPolicy `json:"retry,omitempty"`
}
type ArtifactContract ¶
type ArtifactContract struct {
Format string
Schema *JSONSchema
SchemaRef string
}
func ContractFromOutputFile ¶
func ContractFromOutputFile(of OutputFile) ArtifactContract
type ArtifactExports ¶
type CodeStep ¶
type CodeStep struct {
ID string `json:"id"`
Container string `json:"container,omitempty"`
Run string `json:"run"`
Timeout *Duration `json:"timeout,omitempty"`
OutputSchema *JSONSchema `json:"output_schema,omitempty"`
OutputFiles OutputFiles `json:"output_files,omitempty"`
// InputFiles maps an in-container destination path → a static artifact or asset
// reference (step.<id>.files.<name> or asset.<id>). The engine resolves each to
// a CAS blob from a prior named output_files commit or run-start asset snapshot
// and stages the bytes via Backend.CopyTo BEFORE this step runs. Static, not a
// {{ }} template (AWF3007). Requires a container (rejected on containerless
// agent steps at runtime).
InputFiles map[string]string `json:"input_files,omitempty"`
IdempotencyKey *Template `json:"idempotency_key,omitempty"`
Retry *RetryPolicy `json:"retry,omitempty"`
}
type Compose ¶
type Compose struct {
As string `json:"as"`
From string `json:"from"`
Service Template `json:"service"`
Body NodeList `json:"body"`
}
func (*Compose) MarshalJSON ¶
type ComposeValidationError ¶
func ValidateComposeBytes ¶
func ValidateComposeBytes(filename string, bytes []byte, requiredServices ...string) []ComposeValidationError
type Container ¶
type Container struct {
Image string `json:"image,omitempty"`
Compose string `json:"compose,omitempty"`
Service string `json:"service,omitempty"`
Snapshot string `json:"snapshot,omitempty"`
Cmd []string `json:"cmd,omitempty"`
Keepalive *bool `json:"keepalive,omitempty"`
Resources *Resources `json:"resources,omitempty"`
}
Container is backed by exactly one of Image or Compose (structural validation per the standard §3).
type Diagnostic ¶
type Diagnostic struct {
Severity Severity `json:"severity"`
Source string `json:"source,omitempty"`
Path string `json:"path"`
Code string `json:"code"`
Message string `json:"message"`
}
Diagnostic is one validation finding. Path is the static IR path (see ir/path.go) where the issue lives — empty for top-level / definition-wide findings. Code is a stable enum from the catalog below; never renumbered (the catalog is an API for --output json consumers).
Slice 1.4 collects all diagnostics in a single pass (no fail-fast). Slice 1.6's CLI computes the exit code via HasErrors.
func Validate ¶
func Validate(ld *LoadedDefinition) []Diagnostic
Validate is the AWF validator entry point. It walks ld in five passes (one per rule family) and returns every Diagnostic found — collect-all, no fail-fast. The returned slice is stable-sorted by (Code, Path, Message) so golden-file tests over the diagnostic stream are stable regardless of map-iteration order.
Passes (each lives in its own validate_*.go file):
- structural (AWF1xxx) — §4 step shape, §5 control-flow field requirements, §3 container shape, parallel/map distinct-container rule
- agents (AWF1033/4) — top-level agents: role-definition shape (non-empty uses:, role name not in the <vendor>/<name> adapter-ref form) and every uses: ref resolving to a declared role OR a syntactically-valid base ref
- skills (AWF1040-45, AWF3010-11) — native skill corpus declarations, loaded directory layout, and agent-step routing shape/staging constraints
- calls (AWF1046-51) — imported workflow call targets, input contracts, workflow outputs, and workflow artifact exports
- reduce (AWF1035, AWF5006, AWF1009) — map reduce: fan-in shape (exactly one of run:/quorum:; quorum needs over:; a run: reducer needs a resolvable container:) and quorum/over aggregation scope (over: names a real body field; min_success and reduce:{quorum} are mutually exclusive)
- prune (AWF1037, AWF5008) — map prune: frontier shape (a `score` field + exactly one of keep: top(<k>) / stop_when:) and score-field binding (score names a numeric field in the body's last step's output_schema)
- misplaced_with (AWF1061) — reserved step-level key names nested inside an agent step's with: block (silently ignored by the engine; advisory warning)
- refs (AWF3001/2) — output_schema-iff-referenced cross-walk via the template package (Slots → ParseRef per Template; ParseExpr → References per Expr)
- input_files (AWF3007) — every input_files value is a static step.<id>.files.<name> ref naming a prior in-scope step's NAMED output_files artifact (dst absolute + clean)
- output_files (AWF3009, AWF3014) — named output_files contract metadata shape and schema_ref assets; AWF3014 is the containerless output_artifact (typed-output → artifact) rule set
- schema (AWF2001/2) — JSON Schema well-formedness + §7 floor (warning, agents only)
- compose (AWF3003/4/5) — compose-go/v2 parse + digest-pinning of every inner image
Validate performs no filesystem I/O of its own — all reads happened in loader.Load before the LoadedDefinition was constructed. (compose-go/v2 is configured with SkipExtends, SkipInclude, and SkipResolveEnvironment so its transitive file-following directives don't reopen a file-read primitive.)
Validate(nil) returns one Error diagnostic (AWF1003) so the slice-1.6 CLI can surface a nil LoadedDefinition gracefully rather than panicking.
func (Diagnostic) String ¶
func (d Diagnostic) String() string
String renders a Diagnostic in human-readable form for the default CLI output. Format:
error AWF1005 at graph[0].run: image is a tag, not a digest
The JSON projection (`awf validate --output json`) marshals the struct via its lowercase json tags (severity/source/path/code/message), so consumers like `jq '.diagnostics[].code'` read all-lowercase keys consistent with the validateResult envelope around them.
type Duration ¶
Duration marshals to integer nanoseconds (digest stability); parses int ns or a Go duration string.
func (*Duration) MarshalJSON ¶
func (*Duration) UnmarshalJSON ¶
type Expr ¶
type Expr string
Expr is an unparsed condition expression (if.cond / loop.until / gate.until).
type Gate ¶
type Gate struct {
Generate NodeList `json:"generate"`
Evaluate NodeList `json:"evaluate"`
Until Expr `json:"until"`
MaxAttempts int `json:"max_attempts"`
}
func (*Gate) MarshalJSON ¶
type If ¶
type If struct {
Cond Expr `json:"cond"`
Then NodeList `json:"then"`
Else NodeList `json:"else,omitempty"`
}
func (*If) MarshalJSON ¶
Seven object-valued control nodes share the same wrap pattern.
type JSONSchema ¶
JSONSchema is a JSON Schema document, preserved as decoded JSON for canonicalization + validation. A defined map type is sufficient — json.Marshal sorts map keys alphabetically (digest-stable), and json.Unmarshal decodes any object directly. No custom marshalers needed (verified).
func MapCompactProducer ¶
func MapCompactProducer(m *Map) (suffix string, schema *JSONSchema, ok bool)
MapCompactProducer reports the final typed body-step product for a map without reduce:. Named compact map products expose that final step's typed outputs as a compact array for downstream map over: expressions.
type LoadedAsset ¶
type LoadedAsset struct {
ID string `json:"id"`
DeclaredPath string `json:"declared_path"`
IsDir bool `json:"is_dir"`
Files []LoadedAssetFile `json:"files"`
}
type LoadedAssetFile ¶
func (LoadedAssetFile) SHA256 ¶
func (f LoadedAssetFile) SHA256() string
func (LoadedAssetFile) Size ¶
func (f LoadedAssetFile) Size() int64
Size and SHA256 are derived from Bytes — never stored, so they cannot drift from the content. The durable manifest (RunStartedAssetFile), the digest, and skillroute all derive from Bytes too.
type LoadedDefinition ¶
type LoadedDefinition struct {
// Workflow is the parsed IR. Non-nil if Load succeeded.
Workflow *Workflow
// WorkflowPath is the absolute path of the workflow file (for error attribution).
WorkflowPath string
// ComposeFiles maps each container's cleaned workflow-relative compose path
// (e.g. "lab/compose.yml") to the raw bytes the loader read. Forward-slashed regardless
// of OS so the digest input is portable.
ComposeFiles map[string][]byte
// Assets maps each top-level asset id to the run-start snapshot loaded from disk.
Assets map[string]LoadedAsset
// Modules maps logical module ids to loaded workflow modules. The root module id is "".
Modules map[string]*LoadedModule
// ImportEdges records resolved parent -> child import relationships.
ImportEdges []LoadedImportEdge
}
LoadedDefinition is the loader's output and the validator's input — the parsed Workflow plus the raw bytes of every referenced compose file, keyed by the cleaned workflow-relative forward-slash path. That key form is what ir/digest.go consumes for the spec §E compose-fold (length-prefixed (path, sha256(bytes)) entries, sorted by path, folded into the workflow's content digest).
Lives in the `ir` package because validation — also in `ir` per docs/runtime-design.md — consumes it. Putting LoadedDefinition in `loader` would create a cycle (loader already imports ir for Workflow/Container/Node).
func (*LoadedDefinition) ComputeDigest ¶
func (ld *LoadedDefinition) ComputeDigest() (string, error)
ComputeDigest returns the content digest of the fully loaded definition. For a root-only definition it delegates exactly to Workflow.ComputeDigest to preserve the single-workflow digest contract. When imports are present, it folds the root workflow digest, each imported module's workflow digest, and the import edges into one deterministic loaded-definition digest.
func (*LoadedDefinition) Module ¶
func (ld *LoadedDefinition) Module(id string) (*LoadedModule, bool)
func (*LoadedDefinition) Root ¶
func (ld *LoadedDefinition) Root() *LoadedModule
func (*LoadedDefinition) WalkImportEdges ¶
func (ld *LoadedDefinition) WalkImportEdges(fn func(LoadedImportEdge) error) error
func (*LoadedDefinition) WalkModules ¶
func (ld *LoadedDefinition) WalkModules(fn func(*LoadedModule) error) error
type LoadedImportEdge ¶
type LoadedModule ¶
type Loop ¶
type Loop struct {
Until *Expr `json:"until,omitempty"`
MaxIters *int `json:"max_iters,omitempty"`
Body NodeList `json:"body"`
}
func (*Loop) MarshalJSON ¶
type Map ¶
type Map struct {
ID string `json:"id,omitempty"`
Over Expr `json:"over"`
As string `json:"as"`
Container string `json:"container"`
Image Template `json:"image,omitempty"`
Concurrency int `json:"concurrency"`
MinSuccess *Ratio `json:"min_success,omitempty"`
Body NodeList `json:"body"`
Reduce *Reduce `json:"reduce,omitempty"`
// Prune is the optional frontier clause (SP5, spec §3.2b). nil for a plain
// result-blind map. Folds into the digest via omitempty.
Prune *Prune `json:"prune,omitempty"`
}
func (*Map) MarshalJSON ¶
type Node ¶
type Node interface {
// contains filtered or unexported methods
}
Node is one of the thirteen node kinds (4 step + 9 control). Discriminated by key-presence at the wire level (the AWF standard's surface), so adding/removing a kind touches FOUR places in this package — keep them in sync:
- The type declaration and isNode() method in this file (node.go).
- The MarshalJSON method in node_marshal.go (either via `wrap` for object-valued kinds, or a custom shape for Skip / Parallel per the standard).
- The factory entry in `controlKeys` or `stepKeys` in node_unmarshal.go.
- The case in `unmarshalControl` in node_unmarshal.go (control kinds only; step kinds use a flat `json.Unmarshal` into the value the factory returned).
TestNodeRegistryExhaustive guards (1)/(3)/(4); the tag-reflection test (tags_test.go) guards json tags on every exported field. Adding a node kind without all four edits surfaces as a test failure (or, in the worst case, as the "unknown control key" runtime error).
type NodeList ¶
type NodeList []Node
NodeList is the type of every node-bearing field. A bare []Node cannot unmarshal (interface elements), so all such fields use this named type whose UnmarshalJSON (in node_unmarshal.go) dispatches each element by key-presence. Marshaling works through each element's own MarshalJSON, so no custom marshaler is needed for the slice itself.
func (*NodeList) UnmarshalJSON ¶
UnmarshalJSON dispatches each raw element through unmarshalNode (key-presence union). NodeList is the type of every node-bearing field; see node.go.
type OutputFile ¶
type OutputFile struct {
Name string
Path string
Format string
Schema *JSONSchema
SchemaRef string
}
OutputFile is one captured artifact. Name is the handle name for the NAMED (map) wire form; empty for the bare-list form (capture-only, unreferenceable). Never struct-marshaled (OutputFiles has a custom MarshalJSON) → no json tags, not in ir/tags_test.go's irTypes().
type OutputFiles ¶
type OutputFiles []OutputFile
OutputFiles unmarshals from EITHER a bare list (["/out/a"] → unnamed) OR a name→path map ({"report":"/out/r.md"} → named, referenceable as step.<id>.files.<name>). MarshalJSON re-emits the original shape so bare-list workflows stay byte-identical (digest-stable).
func (OutputFiles) MarshalJSON ¶
func (o OutputFiles) MarshalJSON() ([]byte, error)
func (OutputFiles) PathForName ¶
func (o OutputFiles) PathForName(name string) (string, bool)
PathForName returns the declared container path for a named artifact (ok=false if no named entry matches). Used by the engine resolver to map step.<id>.files.<name> → the key in NodeResult.Files.
func (OutputFiles) Paths ¶
func (o OutputFiles) Paths() []string
Paths returns the in-container paths to capture (Backend.CaptureFiles), in declaration order. nil for empty. Unchanged capture semantics.
func (*OutputFiles) UnmarshalJSON ¶
func (o *OutputFiles) UnmarshalJSON(b []byte) error
type Parallel ¶
type Parallel struct {
// Children is carried by Parallel's custom (un)marshalers as the wrapper's array value
// (`{"parallel":[<node>,...]}` per the standard §5.4). json:"-" documents that fact and
// keeps the tag-reflection test happy.
Children NodeList `json:"-"`
}
func (*Parallel) MarshalJSON ¶
Parallel marshals to the standard's array-value form: {"parallel":[<node>,...]} (§5.4).
type Prune ¶
type Prune struct {
// Score is the name of the NUMERIC field the body's last step declares in
// its output_schema. The engine reads it from the committed item's typed
// outputs (never parses text). Required (AWF1037 / AWF5008).
Score string `json:"score"`
// Keep is the "keep the top k scorers" policy. Wire form: keep: top(<k>).
// Mutually exclusive with StopWhen (AWF1037).
Keep *PruneKeep `json:"keep,omitempty"`
// StopWhen is a bounded boolean expression over `best.score` (the running
// best). Once true, all still-running/queued items are pruned. Mutually
// exclusive with Keep (AWF1037). Evaluated via template.EvalBoolString.
StopWhen string `json:"stop_when,omitempty"`
}
Prune is the optional frontier clause on a map (spec §3.2b). It reads a typed numeric `score` field per item as items commit and cancels in-flight/queued losers. Exactly one of Keep / StopWhen is set (AWF1037).
The whole struct folds into the workflow definition digest via the Map field's omitempty pointer — a nil Prune keeps pre-SP5 digests byte-identical; a non-nil Prune is pinned, so resume against a changed policy is a hard drift error (the existing pinning guard).
type PruneKeep ¶
type PruneKeep struct {
K int
}
PruneKeep is the parsed top(k) policy. Custom (un)marshal so the wire form is the string "top(<k>)" while the in-memory form is a typed K — mirroring how Skip/Parallel carry a non-object scalar through custom (un)marshalers. NOTE: K has no json tag (the custom marshaler owns the wire form), so PruneKeep is deliberately NOT in ir/tags_test.go's irTypes() list (Task 2 Step 5).
func ParsePruneKeep ¶
ParsePruneKeep parses "top(<k>)" into a PruneKeep with a positive K. Trims surrounding and inner whitespace. Any other shape (wrong head, non-int, k <= 0, unbalanced parens) is an error — the validator surfaces it as AWF1037.
func (PruneKeep) MarshalJSON ¶
MarshalJSON re-emits the canonical "top(<k>)" wire form (digest-stable).
func (*PruneKeep) UnmarshalJSON ¶
UnmarshalJSON parses the wire string "top(<k>)" into K (AWF1037 on bad shape).
type Ratio ¶
Ratio is map.min_success: a count (e.g. 3) or a fraction (e.g. 0.8). It is a type ALIAS to json.Number, not a defined type — a defined type over json.Number loses the special numeric-token decoding (verified during planning), so `min_success: 3` would fail to unmarshal.
Consumption (engine `map` fan-in, runtime-design.md §5): call .Int64() or .Float64() to interpret; the consumer must check which form. Strictness (rejecting a JSON-string form like "0.8" that json.Number also accepts) is the validator's job — see runtime-design.md §4.
type RawConfig ¶
RawConfig is opaque agent config (spec §4.2). The core never reads its keys; the adapter validates it.
type React ¶
type React struct {
ID string `json:"id,omitempty"`
With RawConfig `json:"with,omitempty"`
Prompt string `json:"prompt"`
Tools []string `json:"tools"`
MaxTurns int `json:"max_turns,omitempty"`
OutputSchema *JSONSchema `json:"output_schema,omitempty"`
}
React is the model+tools+loop node (P3 A3). A control-style wrapper of the Map class: its runtime path is react[N] (keyword[index], NOT its id); the id is for output addressing only ({{ <id>.* }} / awf outputs --step <id>) via producer registration. tools is required and non-empty; with is the awf/llm config minus prompt (the engine owns the messages array). See spec §3.2.
func (*React) MarshalJSON ¶
type Reduce ¶
type Reduce struct {
Quorum *Ratio `json:"quorum,omitempty"`
Over string `json:"over,omitempty"`
Run string `json:"run,omitempty"`
Container string `json:"container,omitempty"`
OutputSchema *JSONSchema `json:"output_schema,omitempty"`
OutputFiles OutputFiles `json:"output_files,omitempty"`
}
Reduce is the fan-in clause on a Map (C2a). (Parallel is deferred — its bare-array wire-form has no slot for a sibling reduce: key; see SP2 Task 8.) Exactly one of Quorum or Run is set (validator AWF1035). The reduced output REPLACES the node's per-item array output (engine/scope.go aggregateMapOutputs prefers the node-path NodeResult when Reduce != nil).
- Quorum form: the node succeeds iff at least Quorum branches produced a true Over field. Quorum reuses Ratio (= json.Number) — an int count or a (0,1] fraction — exactly like Map.MinSuccess, which it generalizes.
- Run form: an author shell reducer. The engine stages every branch's named output_files artifact + a canonical-JSON manifest of all branch typed outputs into Container via Backend.CopyTo (the SP1 artifact channel), then runs Run and commits OutputSchema/OutputFiles at the node path.
type RetryPolicy ¶
type RetryPolicy struct {
Attempts int `json:"attempts,omitempty"`
Backoff string `json:"backoff,omitempty"`
Initial *Duration `json:"initial,omitempty"`
Max *Duration `json:"max,omitempty"`
NonRetryableExitCodes []int `json:"non_retryable_exit_codes,omitempty"`
}
RetryPolicy mirrors the spec §6 default shape.
type Severity ¶
type Severity int
Severity classifies a Diagnostic. Only Error contributes to the non-zero exit code that `awf validate` will return (slice 1.6); warnings inform but don't fail the run.
func (Severity) MarshalJSON ¶
MarshalJSON emits the human-readable severity name ("error" / "warning") rather than the bare iota int. The CLI's `--output json` output is the primary consumer; opaque integers would be unusable for CI / IDE / dashboard tooling that reads the diagnostic stream.
func (*Severity) UnmarshalJSON ¶
UnmarshalJSON is the inverse of MarshalJSON; lets tooling round-trip the diagnostic stream without needing a parallel string-to-int mapping.
type SignalStep ¶
type SignalStep struct {
ID string `json:"id"`
Await string `json:"await"`
// Where is an optional bounded-boolean clause selecting WHICH buffered signal
// of name Await to consume. `{{ … }}` slots substitute from the surrounding
// engine scope (e.g. {{ hyp.id }}); bare identifiers resolve against the
// delivered signal's JSON payload (e.g. candidate_id == payload.candidate_id).
// Empty → earliest-first per name (unchanged). Folds into the digest (omitempty
// keeps where-less workflows byte-identical). Reuses the bounded boolean
// evaluator — no arithmetic (spec §3.5 / §8 C5).
Where string `json:"where,omitempty"`
Timeout *Duration `json:"timeout,omitempty"`
OutputSchema *JSONSchema `json:"output_schema,omitempty"`
}
type SkillCorpus ¶
type Skip ¶
type Skip struct {
// Reason is carried by Skip's custom (un)marshalers as the wrapper's string value
// (`{"skip":"<reason>"}` per the standard §5.6). json:"-" documents that fact and
// keeps the tag-reflection test happy.
Reason string `json:"-"`
}
func (*Skip) MarshalJSON ¶
Skip marshals to the standard's string-value form: {"skip":"<reason>"} (§5.6).
type StepSkillRouting ¶
type Template ¶
type Template string
Template is an unparsed templated string (e.g. "{{ input.cve_id }}:pr"). Parsed in the template package.
type TemplateValue ¶
type TemplateValue = json.RawMessage
type Tool ¶
type Tool struct {
Description string `json:"description"`
InputSchema *JSONSchema `json:"input_schema,omitempty"`
Impl ToolImpl `json:"impl"`
}
Tool is one entry of the top-level tools: map (P3 A4). The map KEY is the tool name (sent to the model); Tool is the value. A tool is offered to a react: step's model, which calls it by name.
type ToolImpl ¶
type ToolImpl struct {
Run string `json:"run,omitempty"`
Container string `json:"container,omitempty"`
Timeout *Duration `json:"timeout,omitempty"`
OutputFiles OutputFiles `json:"output_files,omitempty"`
InputFiles map[string]string `json:"input_files,omitempty"`
Retry *RetryPolicy `json:"retry,omitempty"`
}
ToolImpl is the executable body of a tool — a run: step that names a containers:-declared container. It is a DEDICATED id-less type, NOT a reused CodeStep: CodeStep.ID is `json:"id"` WITHOUT omitempty, so embedding a CodeStep would serialize an empty "id":"" into the JCS workflow digest. At execution time the engine synthesizes a real CodeStep from these fields (the reduce.go pattern). run:-ONLY — ir.CodeStep has no Exec field, so an exec: form cannot be synthesized into a CodeStep (rev #20). All fields are omitempty so an absent field never enters the digest.
type Try ¶
type Try struct {
Do NodeList `json:"do"`
Catch NodeList `json:"catch,omitempty"`
Finally NodeList `json:"finally,omitempty"`
}
func (*Try) MarshalJSON ¶
type Workflow ¶
type Workflow struct {
ID string `json:"workflow"`
Version int `json:"version"`
Input *JSONSchema `json:"input,omitempty"`
InputFiles WorkflowInputFiles `json:"input_files,omitempty"`
Env []string `json:"env,omitempty"`
Assets map[string]string `json:"assets,omitempty"`
Skills map[string]SkillCorpus `json:"skills,omitempty"`
Imports map[string]string `json:"imports,omitempty"`
Agents map[string]AgentRole `json:"agents,omitempty"`
Containers map[string]Container `json:"containers"`
OutputSchema *JSONSchema `json:"output_schema,omitempty"`
Outputs map[string]TemplateValue `json:"outputs,omitempty"`
ArtifactExports ArtifactExports `json:"output_files,omitempty"`
// Tools is the top-level tools: map (P3 A4) — tool name → definition. Offered
// to react: steps. Folds into the digest automatically (whole-workflow JCS).
Tools map[string]Tool `json:"tools,omitempty"`
Graph NodeList `json:"graph"`
Digest string `json:"-"`
}
Workflow is the top-level definition. The Digest field is populated by run-start (via (*Workflow).SetDigest) or compared on resume; it is excluded from its own hash input (`json:"-"`). After json.Unmarshal, Digest is empty until set.
func (*Workflow) ComputeDigest ¶
func (w *Workflow) ComputeDigest(composeFiles map[string][]byte, assets map[string]LoadedAsset) (string, error)
ComputeDigest returns the self-describing content digest of the workflow, folding in the sha256 of each referenced compose file (keyed by cleaned, workflow-relative path supplied by the loader) and each loaded asset file. Pure: does not modify w. The Digest field is excluded (`json:"-"`). composeFiles/assets are nil if none. See (*Workflow).SetDigest for the in-place variant.
func (*Workflow) RoleByName ¶
RoleByName returns the declared role (ok=false if none). The single accessor the validator and the run-start resolver share so role lookup lives in one place.
func (*Workflow) SetDigest ¶
func (w *Workflow) SetDigest(composeFiles map[string][]byte, assets map[string]LoadedAsset) (string, error)
SetDigest computes the workflow's content digest via ComputeDigest and stores it in w.Digest. Returns the computed value for convenience. Convenient for run-start / loader callers that want both the value and the field populated; ComputeDigest itself remains pure.
func (*Workflow) StructuralDigest ¶
func (w *Workflow) StructuralDigest(composeFiles map[string][]byte, assets map[string]LoadedAsset) (string, error)
StructuralDigest returns a self-describing content digest of the workflow's skeleton: the same compose-file and asset hashes as ComputeDigest, PLUS a hash over every node's (path, type) pair — but NOT the node body (Run text, With params, etc.). Two workflows with identical structure but different node bodies produce the same StructuralDigest and different ComputeDigest values. This is used as the WS-6b guard: resume rejects a workflow whose node set or topology changed since the original run, while tolerating body-only edits.
Implementation note: the workflow-level JSON (Digest excluded) and the compose/asset tail are hashed EXACTLY as ComputeDigest does — the only difference is that the node graph is replaced by its skeleton walk (path + reflect type) instead of being part of the JSON. The compose/asset tail code is duplicated (not refactored) to guarantee ComputeDigest's byte output is unchanged; see the TestGoldenDigest golden test.
v1 caveat: only the root workflow's structure is covered. Imported modules are not walked (T6b will extend this when per-module structural guards land).
type WorkflowInputFiles ¶
type WorkflowInputFiles map[string]ArtifactContract
func (WorkflowInputFiles) MarshalJSON ¶
func (w WorkflowInputFiles) MarshalJSON() ([]byte, error)
func (*WorkflowInputFiles) UnmarshalJSON ¶
func (w *WorkflowInputFiles) UnmarshalJSON(b []byte) error
Source Files
¶
- agents.go
- diagnostic.go
- digest.go
- loaded.go
- map_products.go
- node.go
- node_marshal.go
- node_unmarshal.go
- output_files.go
- path.go
- prune.go
- reduce.go
- runtime_compose.go
- tool.go
- types.go
- validate.go
- validate_agents.go
- validate_compose.go
- validate_continues.go
- validate_input_files.go
- validate_misplaced_with.go
- validate_output_files.go
- validate_prune.go
- validate_reduce.go
- validate_refs.go
- validate_schema.go
- validate_skills.go
- validate_structural.go
- validate_tools.go
- walk.go