feat(workflows): expose {{ context.run_id }} template variable (#2664)
* feat(workflows): expose `{{ context.run_id }}` template variable
Closes #2590.
Surfaces the engine-assigned run id (the same 8-character hex
string Spec Kit prints as `Run ID:` at the end of
`workflow run`) as a workflow template variable so YAML
authors can reference it from shell `run:`, command
`input.args:`, switch `expression:`, and any other field that
already evaluates `{{ ... }}` templates.
### Why
The run id is the natural join key between a Spec Kit workflow
run and downstream artifacts, telemetry, or per-run scratch
state. Today the operator sees it in stdout but workflows
themselves cannot reference it — there was no way to stamp a
log line, name a scratch directory, or tag an artifact with
the same id Spec Kit assigned.
The three motivating use cases from the issue:
1. Telemetry / observability — stamp logs and events with the
run id so external systems can join workflow runs to
downstream artifacts.
2. Per-run scratch / isolation — interactive operator commands
that need their own state directory under
`/tmp/run-<id>/`.
3. Run-id in artifact metadata — stable join key from artifact
back to the producing run.
### Implementation
`StepContext.run_id` is already populated by `WorkflowEngine`
in both `execute()` and `resume()`. The only gap was the
template namespace builder.
`_build_namespace` (in `workflows/expressions.py`) now adds a
`context` key alongside the existing `inputs`, `steps`,
`item`, and `fan_in` namespaces:
```python
ns["context"] = {"run_id": run_id}
```
The value is always present (even outside a run) and falls
back to an empty string when no run is active. Workflows
referencing `{{ context.run_id }}` therefore never error — a
hard requirement from the issue's acceptance criteria for
dry-run, validation, and ad-hoc evaluator usage.
### Default behaviour preserved
Workflows that do not reference `{{ context.run_id }}` are
byte-equivalent to before this change. The `context`
namespace is added unconditionally to keep template
resolution branch-free, but its presence has no observable
effect when nothing references it.
### Tests
`TestExpressions` (unit-level) gains three tests:
- `test_context_run_id_resolves` — direct lookup against a
`StepContext(run_id=...)`.
- `test_context_run_id_defaults_to_empty_when_unset` —
graceful default outside a run context.
- `test_context_run_id_string_interpolation` — mixed
template (e.g. `"RUN_ID={{ context.run_id }}"`).
`TestContextRunId` (end-to-end) covers the three step types
the acceptance criteria called out:
- `test_shell_run_resolves_run_id` — `run:` field
substitution, verified via captured stdout.
- `test_command_input_args_resolves_run_id` — `input.args:`
resolution, captured in step output even when CLI dispatch
is unavailable (the artifact-metadata use case).
- `test_switch_expression_matches_on_run_id` — switch
matches against the resolved value, proving the run id is a
first-class value in the expression engine, not just an
interpolation token.
- `test_workflow_without_context_reference_unchanged` —
locks the byte-equivalent default required by the issue.
### Docs
`workflows/README.md` gains a "Runtime Context" subsection
under "Expressions" documenting the new namespace and the
three canonical use patterns (telemetry, per-run scratch,
artifact metadata).
* test(workflows): drop inline double-quotes in run_id shell tests
`test_shell_run_resolves_run_id` and
`test_switch_expression_matches_on_run_id` used
`run: 'echo "RUN_ID={{ context.run_id }}"'` with inner double-quotes
around the echo argument. Bash/sh strips those quotes before invoking
echo, but cmd.exe (used on Windows when `shell=True`) treats them
as literal characters and emits `"RUN_ID=abc12345"` — failing the
exact-match assertion. Linux passed; all three Windows-latest matrix
entries failed with `assert '"RUN_ID=abc12345"' == 'RUN_ID=abc12345'`.
Resolve by dropping the inner double-quotes (the value has no spaces
or shell metacharacters) and wrapping the YAML scalar in plain
double-quotes the same way other shell-step tests in this file do
(e.g. `run: "echo b-saw-..."`). Behaviour-equivalent on POSIX,
portable to cmd.exe. H
Huy Do committed
c6afe4cde12a4d417e699b3fc7d602d9ece8d759
Parent: 66884db
Committed by GitHub <noreply@github.com>
on 5/27/2026, 6:00:58 PM