Type system
Types are Agent Flow's contract layer: an expect type compiles into a JSON
Schema enforced at runtime; field access, projections and return values are
all checked at compile time.
Type expressions
TypeRef := PrimaryTypeRef Suffix*
PrimaryTypeRef := 'member' '<' Id '>' | Id
Suffix := '[' RangeBounds? ']' // a suffix makes it an array
RangeBounds := NUMBER '..' NUMBER // closed interval
Examples: text, path[], WorkItem[1..6], path[][], and
member<engineering>[]. Optional fields use field?: Type. Reading one
produces internal T?, which must be resolved with value ?? fallback;
null is the explicit empty value.
Type kinds
| Type | Syntax | JSON Schema | Notes |
|---|---|---|---|
| text | text | string | |
| number | number | number | |
| bool | bool | boolean | |
| path | path | string | structurally text; semantically for permissions / write scopes |
| duration | duration | number (ms) | number <: duration both ways |
| object | type Name { ... } | object | named field set with types and optionality |
| array | T[], T[min..max] | array (optional minItems/maxItems) | bounds enforced at runtime only; static assignability ignores them |
| optional field/value | field?: T / expr ?? fallback | omitted from required | reads cannot be used directly as T |
| member | member<Team> | string + x-flow-member annotation | team member id; membership validated by the Runtime when used in calls |
| verification | Verification (builtin) | {passed, failed, checks} | only producible by verify |
| external | undeclared named type | anything (accepted) | only allowed at workflow input/output/context boundaries; info diagnostic |
Name resolution order
- Primitive type names (
text/number/bool/path/duration) - Builtin
Verification typedeclarations in this workflow (order-independent, forward refs ok)- At input/output/context boundaries, an external type; elsewhere
unknown-type
Undeclared types in expect, stage returns, or fields are errors, so contracts
cannot silently degenerate.
Assignability
Rules for S <: T:
any(context) and external types: assignable both ways with anything- Primitives: same name;
number <: durationandduration <: number member<T> <: text;member<T> <: member<T>only for the same alias- Arrays: element assignability (min/maxItems not compared — bounds are runtime Ajv business)
- Objects are structural:
S <: Tiff every required field of T exists in S with assignable type; optional fields may be absent; extra fields are fine (runtime validation is Schema-based withadditionalProperties: false) Verification<S_checks> <: Verification<T_checks>: checks assignable- Recursive types are rejected:
recursive-type
Common patterns
Bounds as business constraints
type Plan {
summary: text
work: WorkItem[1..6] // compiled into the Schema: Ajv enforces 1..6
}
Bounds are runtime semantics (static checks ignore them), so they
complement require ... in 1..6: bounds gate bad data at the Schema; the
require states a workflow assertion with a readable message and a
require.failed event.
member<Team>: people as types
use team "engineering-team" as engineering
type WorkItem {
id: text
owner: member<engineering> // value must be a member id of that team
writes: path[]
}
member<engineering> compiles to
{"type":"string","x-flow-member":"engineering"}. When used as the argument
of team.member(expr), the Runtime validates membership — assigning work to
a person who actually exists on the team is a runtime guarantee.
Structural object assignability
A parallel's anonymous object result can be returned directly to a structurally identical named type:
stage evidence -> Evidence { // type Evidence { style: Findings ... }
let found = parallel {
style = agent(styler) { /*...*/ expect Findings }
accuracy = agent(facter) { /*...*/ expect Findings }
}
return found // {style: Findings, accuracy: Findings} <: Evidence
}
JSON Schema mapping
| Flow type | JSON Schema |
|---|---|
text / path | {"type":"string"} |
number / duration | {"type":"number"} |
bool | {"type":"boolean"} |
member<T> | {"type":"string","x-flow-member":"<teamId>"} |
T[] | {"type":"array","items":<T>} |
T[min..max] | the above + minItems/maxItems |
| object (named, nested) | properties + required + additionalProperties:false, nested named types inlined |
Verification / external / any | {} (accepts anything) |
expect Verification is forbidden (expect-invalid-type): verification
verdicts must be computed by the Runtime, never produced by an agent — see
verify.
Next steps
- agent() calls — how expect becomes a runtime contract
- Language reference