Repository navigation
template.render_text reads the ambient environment instead of declared runtime variables #683
Description
Related: #680 (the documentation claim), #677 (method classification), #563
The defect
pkg/op/provider/template/provider.go:110:
var renderFuncs = template.FuncMap{
"Env": os.Getenv,
}Env reaches the ambient process environment. Ambient environment is not a declared input — it never passed
through the runtime-environment spec, so nothing records that a template depends on it and nothing can supply it
deliberately.
The consequence is a hermeticity break: the same plan renders differently on two machines, with the dependency
undeclared. Per 2.4-hermeticity-guarantees.md, a run is hermetic when its output is a function of its declared
inputs alone.
What it should do
template.render_text should consult variable values set into the runtime, the way make does — the
template reads what the environment declared, and only that. It is then deterministic by construction: it
consults only what the runtime environment declared.
The mechanism already exists:
op.VariableResolver—pkg/op/variable_resolver.go:48, withGet(name) (Variable, bool)at:85and
Variables() map[string]Variableat:96- Its sources are
application.Application's flags / config / overrides source maps
(pkg/application/application.go:29), plus a prefixed env source derived as
strings.ToUpper(Name) + "_"(pkg/application/application.go:39) — declared, not ambient RuntimeEnvironment.Applicationcarries them (pkg/op/runtime_environment.go:51)
So the fix routes Env through the resolver rather than os.Getenv. A prefixed env var remains reachable
because the resolver declares that source, which is the difference that matters.
What survives from the current rationale
The doc comment at provider.go:105 argues that Env must resolve at dispatch time, not plan time, because
graphs are transportable and plan-time resolution would embed environment values — potentially secrets — into
persisted graph documents. That reasoning is unaffected. Dispatch-time resolution stays; only the source
changes, from ambient to declared.
Naming
Env is the wrong name once the source is the variable resolver. Whether the template function becomes Var,
Variable, or keeps Env with corrected semantics is part of this issue.
Why it matters beyond one provider
template.render_text is currently the only method in the catalog whose hermeticity depends on the content of
an argument rather than its type — a template string either calls Env or does not. Fixing the source removes
that: every reachable value becomes a declared input, so the method can carry a determinism claim like any other.
It is the one confirmed hermeticity violation found in the 123-method survey of 2026-08-25.
Moved 2026-10-02: from epic #530 to feature #1023 (epic #441), the variables family, whose variable space is what
Envreads once this is fixed; ruled by the owner the same day. Lane 40 of #916, done in lane 38, PR C′.
Plan and design documents
| Kind | Document |
|---|---|
| Plan | Not yet opened: lane 38's plan, for #1023 |
| Design | docs/architecture/configuration.md § Variables — supplemental, docs/architecture/3.6-method-classification.md |
Activity
docs(op): the record catches up with what shipped
Providers claim what they guarantee, and a type-checked pass holds th…
David-Noble-at-work commentedon Oct 3, 2026
2026-10-03: the variable resolver's sources change. This issue names the resolver's sources as the application's flag, configuration and override maps plus a prefixed environment source. Ruled on 2026-10-02 while planning #927: settings and variables are orthogonal, and no setting enters the variable space. The resolver reads the variables family alone,
--variable NAME=value, then<PREFIX>_VARIABLE_<NAME>, then<program>.variable.<NAME>, under values the caller supplies (#1023, which this issue now sits under as lane 40). SoEnvreads exactly the variables a run declares, from those sources.docs/architecture/3.5.7-template-provider.mdstates it, and its status page records that the code is stillos.Getenv.docs: settings and variables are orthogonal, and the name-keyed famil…