Skip to content
2 min read · 499 words

@nhtio/adk/batteries/orchestration/templates

Template validation and instantiation for orchestration.

Remarks

A template is a consumer-defined plan shape — written in TypeScript, registered with createOrchestration at construction, and therefore versioned with the consuming application. It needs no store seeding and can be validated once at boot rather than once per instantiation, so a misconfigured deployment fails at startup with a named issue rather than at the first use months later.

Why templates exist at all is the small-model story: a small model working on a forty-node plan does far better filling in five declared parameters than authoring forty nodes. The template is the static part of that bargain — the consumer writes the shape, the model fills the holes.

A template is not a plan. It has no lifecycle, no digest, no run, and cannot be approved. Only its instantiations are plans, which is what keeps "one plan id, at most one run" intact when the same template is instantiated fifty times: each instantiation is an independent plan with its own id and digest.

The two exported functions are the whole surface:

  • validateTemplate — runs at construction. Every issue it returns is decidable and total because a registered template is immutable: the graph cannot change after the check, so the answer cannot go stale. The most important check is the laundering rule (below).
  • instantiateTemplate — validates the arguments a model offers against the declared params, mints a fresh plan, substitutes every hole, and appends the graph as authored ops.

The laundering rule

A ParamRef reaching a call node's args is refused unless a node on every route to that call declares the corresponding field in declassifies. This is the taint story made static: a substituted parameter value is like entry input — untrusted — and it may reach a reason prompt but not a call node's args unless a node on every path to the call has declassified it.

The check lives over the template, not over an instantiation, and that is the whole reason it can be total: it runs at construction, once, over a graph that is immutable from that moment.

The narrower invariant, stated honestly: a template cannot launder its own parameters. It does not claim that a substituted value's template origin is tracked through arbitrary later edits — nothing in a freely-mutable graph can track that. Once instantiated, the result is an ordinary editable plan and a substituted value is an ordinary literal; a later set_node_field routing that literal into a call arg is exactly as visible as in any hand-authored plan, which is to say: it is in the operator's rendered prose, and the operator approves it. That is the honest boundary, and it is the same one every hand-authored plan already has.

Functions

FunctionDescription
instantiateTemplateInstantiate a template into a fresh, ordinary editable plan.
validateTemplateValidate a template against the invocable allowlist, once, at construction.