Problem
Workflows need a way to parameterize configuration across environments (dev/staging/prod) without duplicating the workflow definition. Currently, the same workflow targeting different environments requires either:
- Separate workflow files with duplicated step logic, or
- Manual
--input overrides on every mantle run invocation
Actual behavior: Each environment needs manual input overrides at run time or separate workflow files.
Expected behavior: A values file (or named environment) can be associated with a workflow run, supplying environment-specific inputs and env overrides — similar to Helm --values or Terraform .tfvars files.
Root Cause Analysis
The engine already supports a complete parameterization model via workflow inputs and env variables, both accessible in CEL expressions. What's missing is:
- A values-file loader — a mechanism to supply input overrides from a YAML file at run/plan time, rather than requiring every key to be passed as individual
--input flags.
- Named stored environments — a first-class resource (stored in the database like credentials) that associates a name (e.g.
production) with a set of input defaults and env overrides, enabling mantle run workflow --env production without a file on disk.
- CLI integration —
--values <file> and --env <name> flags on mantle run, mantle plan, and mantle apply.
The layered override order that needs to be enforced is:
named environment < values file < inline --input flags
TDD Fix Plan
Phase 1 — Values file (--values)
-
RED: Write a test that loading a well-formed values YAML file returns a Values struct with the declared inputs and env overrides.
GREEN: Add a Values type with Inputs map[string]any and Env map[string]string fields and a LoadValues(path string) (*Values, error) function in the workflow package.
-
RED: Write a test that LoadValues returns a descriptive error when the file is missing, malformed YAML, or contains unknown top-level keys.
GREEN: Add validation in LoadValues that checks for unknown keys and wraps file/YAML errors with context.
-
RED: Write a test that merging a values file with inline --input flags applies the correct precedence: inline flags override values-file inputs; values-file inputs override workflow-level default values.
GREEN: Add a MergeInputs(workflowDefaults, valuesInputs, inlineInputs map[string]any) map[string]any helper and wire it into the run command's input-assembly path.
-
RED: Write a test that env overrides from a values file are surfaced in the CEL env variable during step execution.
GREEN: Extend the Evaluator.SetConfigEnv call path (or add SetValuesEnv) to layer values-file env overrides on top of config env but below MANTLE_ENV_* OS vars.
-
RED: Write an integration test (testcontainers) that mantle run <workflow> --values prod.values.yaml executes steps with input and env values from the file.
GREEN: Wire --values flag into the run CLI command, load the values file, merge inputs and env, and pass them through to the engine.
Phase 2 — Named stored environments (--env)
-
RED: Write a test that creating an environment stores its name, inputs, and env overrides in the database scoped to a team, and that loading it by name retrieves the same data.
GREEN: Add an environments table (migration), an EnvironmentStore with Create, Get, List, and Delete, and a mantle env create subcommand that reads from a values file.
-
RED: Write a test that mantle run <workflow> --env staging resolves the staging environment from the store and applies its inputs/env with the same override precedence as a values file.
GREEN: Wire --env <name> into the run command, resolve the environment from the store, and reuse the same merge helper from Phase 1.
-
RED: Write a test that mantle plan <workflow> --env production reports the diff between the workflow's current applied state and what would be executed with the given environment's overrides.
GREEN: Thread the environment/values resolution through the plan command using the same merge helper.
REFACTOR: Extract the input-merge and env-layer logic into a shared resolution package so run, plan, and future commands share a single, tested override stack.
Acceptance Criteria
Problem
Workflows need a way to parameterize configuration across environments (dev/staging/prod) without duplicating the workflow definition. Currently, the same workflow targeting different environments requires either:
--inputoverrides on everymantle runinvocationActual behavior: Each environment needs manual input overrides at run time or separate workflow files.
Expected behavior: A values file (or named environment) can be associated with a workflow run, supplying environment-specific inputs and env overrides — similar to Helm
--valuesor Terraform.tfvarsfiles.Root Cause Analysis
The engine already supports a complete parameterization model via workflow
inputsandenvvariables, both accessible in CEL expressions. What's missing is:--inputflags.production) with a set of input defaults and env overrides, enablingmantle run workflow --env productionwithout a file on disk.--values <file>and--env <name>flags onmantle run,mantle plan, andmantle apply.The layered override order that needs to be enforced is:
named environment < values file < inline --input flagsTDD Fix Plan
Phase 1 — Values file (
--values)RED: Write a test that loading a well-formed values YAML file returns a
Valuesstruct with the declared inputs and env overrides.GREEN: Add a
Valuestype withInputs map[string]anyandEnv map[string]stringfields and aLoadValues(path string) (*Values, error)function in the workflow package.RED: Write a test that
LoadValuesreturns a descriptive error when the file is missing, malformed YAML, or contains unknown top-level keys.GREEN: Add validation in
LoadValuesthat checks for unknown keys and wraps file/YAML errors with context.RED: Write a test that merging a values file with inline
--inputflags applies the correct precedence: inline flags override values-file inputs; values-file inputs override workflow-leveldefaultvalues.GREEN: Add a
MergeInputs(workflowDefaults, valuesInputs, inlineInputs map[string]any) map[string]anyhelper and wire it into theruncommand's input-assembly path.RED: Write a test that
envoverrides from a values file are surfaced in the CELenvvariable during step execution.GREEN: Extend the
Evaluator.SetConfigEnvcall path (or addSetValuesEnv) to layer values-file env overrides on top of config env but belowMANTLE_ENV_*OS vars.RED: Write an integration test (testcontainers) that
mantle run <workflow> --values prod.values.yamlexecutes steps with input and env values from the file.GREEN: Wire
--valuesflag into therunCLI command, load the values file, merge inputs and env, and pass them through to the engine.Phase 2 — Named stored environments (
--env)RED: Write a test that creating an environment stores its name, inputs, and env overrides in the database scoped to a team, and that loading it by name retrieves the same data.
GREEN: Add an
environmentstable (migration), anEnvironmentStorewithCreate,Get,List, andDelete, and amantle env createsubcommand that reads from a values file.RED: Write a test that
mantle run <workflow> --env stagingresolves thestagingenvironment from the store and applies its inputs/env with the same override precedence as a values file.GREEN: Wire
--env <name>into theruncommand, resolve the environment from the store, and reuse the same merge helper from Phase 1.RED: Write a test that
mantle plan <workflow> --env productionreports the diff between the workflow's current applied state and what would be executed with the given environment's overrides.GREEN: Thread the environment/values resolution through the
plancommand using the same merge helper.REFACTOR: Extract the input-merge and env-layer logic into a shared
resolutionpackage sorun,plan, and future commands share a single, tested override stack.Acceptance Criteria
mantle run <workflow> --values <file>accepts a YAML values file withinputs:andenv:keysmantle env create <name> --from <file>stores a named environmentmantle run <workflow> --env <name>resolves a stored environmentMANTLE_ENV_*OS vars >--inputflags > values file / named env > workflowdefaultvalues > config env