Skip to content

template.render_text reads the ambient environment instead of declared runtime variables #683

Open

Description

@David-Noble-at-work

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, with Get(name) (Variable, bool) at :85 and
    Variables() map[string]Variable at :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.Application carries 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 Env reads 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

Epic: #441 · Feature: #1023

Activity

  1. David-Noble-at-work commented on Oct 3, 2026

    @David-Noble-at-work
    ContributorAuthor

    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). So Env reads exactly the variables a run declares, from those sources. docs/architecture/3.5.7-template-provider.md states it, and its status page records that the code is still os.Getenv.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

Area:ArchitectureSystem architecture and designEpic:UnifiedConfigurationUnified configurationPriority:P2Scheduled, not urgentSeverity:HighA supported path is broken with no workaroundbugSomething isn't working

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions