Relationships
#2036 Document env.* namespace for model-instance globalArguments (host-portable config)
Opened by webframp · 9/6/2026· Shipped 9/14/2026
Problem
The env.* CEL namespace works in model instance globalArguments but is undocumented in the model expression reference. This is the model-instance sibling of the datastore-layer gap fixed in #1801 — same shape ("a committed config file needs a value that varies per host/operator"), one layer up.
Concretely: a model definition YAML is committed to git, but a globalArguments value often must vary per host (e.g. an absolute filesystem path that differs between Linux /home/[REDACTED]/... and macOS /Users/[REDACTED]/...). Today the only portable option is to hardcode one host's absolute path, which breaks the moment the repo is used from another machine.
What actually works (but isn't documented)
${{ env.VAR }} in globalArguments resolves correctly at method-run time:
globalArguments:
repo_root: ${{ env.HOME }}/src/webframp/swamp-extensions # portable, no new var
# or an explicit var:
# repo_root: ${{ env.EXT_MAINT_REPO_ROOT }}Verified on swamp 20260904.230512.0:
- Resolves at run time, not evaluate time — consistent with the documented
behavior that
vault.*/env.*are "left untouched during expansion — they resolve at their own runtime/execution stage." ${{ env.HOME }}+ literal suffix produces a fully host-portable path with zero new environment variables — works on both macOS and Linux.- Missing var fails loudly and precisely, which is good:
error: No such key: EXT_MAINT_REPO_ROOT path: globalArguments.repo_root envVar: EXT_MAINT_REPO_ROOT
The documentation gap
references/model/references/expressions.md — the "Reference Types" table lists
inputs, model, file, self, data (and vault in prose) but has no
mention of env. The only place env.* is documented is the workflow
reference (references/workflow/references/expressions-and-foreach.md, "Environment
Variables" section). A user working with model instances has no signal the
namespace exists there, so relying on it feels like depending on undocumented
behavior.
Requested changes
- Document
env.*in the model expression reference — add it to the Reference Types table alongsideinputs/self/data, with a host-portable path example (${{ env.HOME }}/...) and a note that it resolves at method-run time (not atmodel evaluate). - Call out the cross-host portability pattern explicitly in the model /
model createdocs: prefer${{ env.* }}over hardcoded absolute paths in committedglobalArguments. - (Optional) Validation / discoverability improvements:
- Surface required env vars at create/evaluate time so an operator learns
up front which host vars a model instance depends on (today
model evaluateandmodel getshow the literal${{ env... }}, so the resolved value and the dependency aren't inspectable until a method runs). - Consider a
model validatesignal that flags committed absolute paths inglobalArgumentsas a portability smell, nudging towardenv.*.
- Surface required env vars at create/evaluate time so an operator learns
up front which host vars a model instance depends on (today
Relationship to #1801
#1801 (shipped) added env/interpolation support at the datastore layer,
which had a genuine bootstrap blocker (datastore initializes before vault
resolution). Model instances have no such blocker — they run well after the
environment is available, and env.* already resolves for them. So this is
narrower: primarily a documentation coverage issue (make env a documented,
first-class option for model-instance config), plus optional
validation/discoverability niceties to help operators keep committed configs
host-portable.
Shipped
Click a lifecycle step above to view its details.