Action Parameter Contract
Purpose
This document is the design record for how action parameters, defaults and "not applicable" values work across CausalIQ packages. It exists so that a workflow keeps working as parameters and defaults change, and so that users never have to distinguish "use the default" from "this parameter does not apply".
The Contract
- A parameter is omitted when the user wants the declared default, and omitted when the parameter does not apply to that action in that configuration. The two cases are indistinguishable to the user.
- Every declared default is a value the user could have supplied, and the framework supplies it before the action runs. A default is therefore defined once, in the action's input specification.
- A parameter that does not apply is absent from the step and from the
metadata. It is never recorded as
null. - The
parametersmetadata block records the effective value of every parameter that applied, expressed in that parameter's own vocabulary. Resolved values (variant,hyperparameters) stay inparameters; a state that has an explicit token (no-randomise) is recorded as that token.
Declaration
Inputs are declared with causaliq_core.ActionInput:
NO_DEFAULT # sentinel: the parameter has no default
ActionInput(
name="randomise",
description="Randomisation options",
required=False,
default="no-randomise",
type_hint="list[str] or str",
choices=("no-randomise", "var_order", "var_best"),
)
defaultis either a concrete value orNO_DEFAULT.default=Noneis rejected:Nonecannot distinguish "no default" from "the default is null", and it is not a value a user should write.choicesis the closed set of values the parameter may take, orNonewhen any value of the declared type is accepted. For a list-valued parameter the choices enumerate the values each element may take, in addition to any scalar "off" token.- An enumerated default must appear in
choices. An open-domain default (for exampletimeout=60) is inherently a legal value.
Three Kinds of Parameter
| Kind | Declaration | Framework behaviour | Recorded value |
|---|---|---|---|
Enumerated with a default (randomise) |
default plus choices |
injects the default when omitted | the effective token or list |
Open domain with a default (timeout) |
default, no choices |
injects the default when omitted | the effective value |
Conditional (seed, reference, trace) |
required=False, NO_DEFAULT |
injects nothing | omitted when not applicable |
Runtime Resolution
causaliq_core.resolve_action_parameters(provider, action, supplied)
produces the effective parameters:
- a supplied value wins;
- a parameter supplied as
Nonemarks a matrix dimension that is not applicable: it is dropped and does not receive a default; - an omitted parameter takes its declared default when it applies to the action;
- applicability comes from the provider's
action_parametersmapping when it declares one, otherwise every declared input applies.
causaliq-workflow calls the resolver in both the execution and the
validation path, so a workflow validates and runs with the same effective
parameters.
Metadata
The parameters block follows the same rule as the action:
- explicit values are recorded as supplied;
- defaulted values are recorded as the declared default;
- parameters that do not apply are absent.
No value in parameters is ever null. Because defaults are recorded as
values, a filter can address them directly, for example
learn_graph.randomise == 'no-randomise'.
Tokens
A token expresses a state that has no other natural value:
| Token | Meaning |
|---|---|
no-randomise |
apply no randomisation (the randomise default) |
all |
use every row (the sample_size default) |
impute |
infer variable types from the data (the variable_types default) |
Tokens are ordinary values: they can be supplied explicitly, stored in a workflow matrix, compared in a filter, and passed to a called workflow.
Passing Defaults Through Sub-workflows
A caller can pass a token through a {{placeholder}} so that a child
reaches the same state as omitting the parameter:
matrix:
randomise: [no-randomise, var_best]
steps:
- name: Learn
uses: ./modules/learn-graphs.yml
with:
randomise: "{{randomise}}"
Forwarding a parameter only when the caller sets it is not supported yet; that requires workflow-level default declaration.
Enforcement
CausalIQActionProvider.__init_subclass__ checks every declared input when
the provider class is defined:
- a specification key must match its declared
name; - a required input must not declare a default;
- a default must not be
None; choices, when declared, must be non-empty, must not containNone, and must contain the default.
A failing provider is surfaced by the workflow registry as a discovery error rather than an import crash. Each package also carries a test that asserts the invariant, so CI catches drift before release.
See Also
- Architecture Overview - core package structure
causaliq-workflow/docs/architecture/metadata_structure.md- the parameters and result blocks of a cache entrycausaliq-workflow/docs/userguide/core_concepts.md- workflow parameters, matrix values and template variablescausaliq-workflow/docs/userguide/common_parameters.md- parameters shared across actions