Skip to content

The variables family: --variable NAME=value, <PREFIX>_VARIABLE_<NAME> and <program>.variable, read only by the variable resolver #1023

Open

Description

@David-Noble-at-work

The runtime variable space has no surface of its own. Variables are declared by reference (plan.variable(...)),
gathered from a graph's nodes, and bound when a run starts by pkg/op's VariableResolver, but nothing lets a user
introduce one. The resolver makes up for it by reading settings: the flag map, the configuration map, and the
environment under the program's prefix plus the bare name. Two bugs are this gap seen from either end: writ's template
variables come only from the configuration file (#975), and a template's Env reads the process environment (#683).

Ruled 2026-10-02

The owner, planning lane 8 of #916 (#927):

  • "settings are different than variables in the context of a RuntimeEnvironment. variables are declared by reference
    and bound at execution time. settings are orthogonal."
  • "Until we have specific use cases with a syntax for introducing settings (as i've now defined settings) they will
    not be entered into the runtime variable space. Bindings are entered into the runtime variable space using a syntax
    similar to what make uses."
  • "this seems like a big hole that was meant to be filled exclusively by the VariableResolver. It is the logical
    place. the VariableResolver needs access to settings. it's domain is the variable space. It should not need to do
    more than resolve the set of variables defined by the current application. ... I propose --variable NAME=value. And
    we must be sensitive to the fact that these things may represent complex types; any types that can be serialized:
    primitives and structs. anything that might be passed to a provider method."
  • The family is named as segments are (--segment, WRIT_SEGMENT_<NAME>, writ.segments): --variable NAME=value,
    <PREFIX>_VARIABLE_<NAME> and <program>.variables. Ruled "yes".
  • No file form for a long value until a use case asks for one: a string on the command line and in the environment,
    JSON for a structure, YAML in the file. Ruled "(a)".
  • "our shipping apps (lore, star, and writ) and devlore-test are the apps that should carry variables. none other."
  • The work splits, ruled "(c)": lane 8 (The environment prefix is doubled: WRIT_WRIT_VERBOSE works and WRIT_VERBOSE does not; the variable is the prefix plus the bare key, for every program #927, PR C) moves the resolver's environment lookup to
    <PREFIX>_VARIABLE_<NAME> and drops its lookups of settings; this feature adds the surfaces.

Amended 2026-10-02: the file key is singular, <program>.variable. Asked whether scopes, segments and variables should take singular or plural keys, the owner: "yes, singular for all three": writ.scope, writ.segment (#1024) and <program>.variable. Under #927's rule, writ.variable.<NAME> reads WRIT_VARIABLE_<NAME>: the file key and the environment variable are one setting chain.

Acceptance

  • --variable NAME=value, repeatable, on lore, star, writ and devlore-test, the programs on the shared root, and
    on no other program.
  • The variable resolver is the family's only reader. It binds the variables a graph references from the family's
    three layers (the flag, then <PREFIX>_VARIABLE_<NAME>, then <program>.variable in the configuration file),
    beneath values the run's caller supplies and above declared defaults. It reads no other setting.
  • A value reaches its slot as anything a provider method takes. A string from the command line or the environment
    decodes through envValue (primitives, durations, file modes, TextUnmarshaler, then JSON), and every value then
    projects through op.Convert (structs by their json or yaml tags, slices, maps, registered resources).
  • <program>.variable replaces writ.vars; writ's templates read their variables from the variable space, and
    names keep their case (writ's template variables come only from the configuration file: writ.vars is read as a whole map, which the environment never reaches, and no flag sets one #975).
  • A template's Env reads the variable space, not the process environment, and its name is settled (template.render_text reads the ambient environment instead of declared runtime variables #683).
  • devlore-test's fixtures set variables through the family.
  • configuration.md § Variables, 10-command-line-interface.md, 2.1-typed-slots.md,
    2.5-lifecycle-pipeline-construction.md and the templates guide describe the family.

Schedule

#916 lane 38, PR C′, after lane 22 (#1009, the one configuration file), lane 24 (#1010, the shared root's flags as
global settings, from which --variable is the exception), and lane 8's part in PR C. #975 and #683 are lanes 39 and
40, done in lane 38, and #1024 (writ.scope and writ.segment) is lane 41, done in lane 38 too.

Plan and design documents

Kind Document
Plan Not yet opened: written when lane 38 opens, in PR C′'s worktree
Design docs/architecture/configuration.md § Variables — supplemental

Epic: #441

Activity

  1. changed the title [-]The variables family: --variable NAME=value, <PREFIX>_VARIABLE_<NAME> and <program>.variables, read only by the variable resolver[/-] [+]The variables family: --variable NAME=value, <PREFIX>_VARIABLE_<NAME> and <program>.variable, read only by the variable resolver[/+] on Oct 3, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions