You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
# ADR-56983: Add Typed `on.stop-after` and Runtime Expressions
2
+
3
+
**Date**: 2026-08-29
4
+
**Status**: Draft
5
+
**Deciders**: pelikhan, adr-writer agent
6
+
7
+
---
8
+
9
+
### Context
10
+
11
+
This pull request changes workflow frontmatter handling in `pkg/workflow` so `on.stop-after` is no longer interpreted only through the dynamic `On` map and can also accept GitHub Actions expressions such as `${{ inputs.stop-after }}`. The PR description identifies a drift risk between typed config, parser behavior, and documentation because `stop-after` was documented and consumed at runtime but had no dedicated typed field. The existing compile-time stop-after resolution logic also rejected expression-based values even when those values should be deferred to workflow runtime. Because this PR adds more than 100 lines in business-logic directories and changes parser/compiler behavior, the underlying design decision should be recorded explicitly.
12
+
13
+
### Decision
14
+
15
+
We will add a typed `OnStopAfter` field to `FrontmatterConfig`, centralize `on.stop-after` extraction in a shared parser helper, and treat GitHub Actions expressions for `stop-after` as runtime-resolved values that pass through compilation unchanged. We chose this approach to eliminate schema/parser/docs drift, keep typed and untyped frontmatter access paths consistent, and allow parameterized stop times without forcing compile-time parsing of runtime expressions. Literal relative and absolute stop-after values will continue to be resolved using the existing compiler behavior.
16
+
17
+
### Alternatives Considered
18
+
19
+
#### Alternative 1: Keep `stop-after` Dynamic-Only in `on` Map
20
+
21
+
Continue reading `on.stop-after` only from `map[string]any` and leave typed config without a dedicated field.
22
+
23
+
This was considered because it would require the fewest structural changes to frontmatter parsing. It was not chosen because the PR evidence shows this has already created typed-schema and documentation drift risk, and separate access paths make it easier for parser behavior to diverge over time.
24
+
25
+
#### Alternative 2: Require All `stop-after` Values to Be Compile-Time Literals
26
+
27
+
Preserve the existing behavior that parses every `stop-after` value as a relative delta or absolute timestamp during compilation.
28
+
29
+
This was considered because compile-time normalization gives early validation and a single resolved representation in generated workflows. It was not chosen because GitHub Actions expressions are legitimate runtime inputs for workflow dispatch and should not be rejected merely because they cannot be resolved at compile time.
30
+
31
+
### Consequences
32
+
33
+
#### Positive
34
+
- Typed frontmatter now exposes `on.stop-after` explicitly, reducing drift between config structs, parser behavior, schema text, and documentation.
35
+
- A shared parsing helper makes typed population and runtime extraction use the same interpretation logic, lowering the risk of inconsistent behavior.
36
+
- Workflows can accept expression-based `stop-after` values such as `${{ inputs.stop-after }}`, enabling runtime parameterization for dispatch inputs.
37
+
38
+
#### Negative
39
+
- Stop-after handling now has two execution modes: compile-time resolution for literals and runtime passthrough for expressions, which increases conceptual complexity.
40
+
- Expression-based values defer some validation until workflow runtime, so certain user errors will no longer be caught during compilation.
41
+
- Adding another typed frontmatter field increases the maintenance surface of `FrontmatterConfig` and its parsing/tests.
42
+
43
+
#### Neutral
44
+
- Existing literal `stop-after` formats remain supported; this change extends accepted inputs rather than replacing them.
45
+
- The implementation requires coordinated updates across parser code, schema descriptions, generated docs, and tests.
46
+
- Runtime workflow semantics change only for expression inputs; absolute and relative literal values continue through the established resolution path.
47
+
48
+
---
49
+
50
+
*ADR created by [adr-writer agent]. Review and finalize before changing status from Draft to Accepted.*
Copy file name to clipboardExpand all lines: pkg/parser/schemas/main_workflow_schema.json
+1-1Lines changed: 1 addition & 1 deletion
Original file line number
Diff line number
Diff line change
@@ -1933,7 +1933,7 @@
1933
1933
},
1934
1934
"stop-after": {
1935
1935
"type": "string",
1936
-
"description": "Time when workflow should stop running. Supports multiple formats: absolute dates (YYYY-MM-DD HH:MM:SS, June 1 2025, 1st June 2025, 06/01/2025, etc.) or relative time deltas (+25h, +3d, +1d12h30m). Maximum values for time deltas: 12mo, 52w, 365d, 8760h (365 days). Note: Minute unit 'm' is not allowed for stop-after; minimum unit is hours 'h'."
1936
+
"description": "Time when workflow should stop running. Supports multiple formats: absolute dates (YYYY-MM-DD HH:MM:SS, June 1 2025, 1st June 2025, 06/01/2025, etc.), relative time deltas (+25h, +3d, +1d12h30m), or a GitHub Actions expression (e.g. ${{ inputs.stop-after }}) resolved at workflow runtime. Maximum values for time deltas: 12mo, 52w, 365d, 8760h (365 days). Note: Minute unit 'm' is not allowed for stop-after; minimum unit is hours 'h'."
Copy file name to clipboardExpand all lines: pkg/workflow/frontmatter_types.go
+1Lines changed: 1 addition & 0 deletions
Original file line number
Diff line number
Diff line change
@@ -390,6 +390,7 @@ type FrontmatterConfig struct {
390
390
// Event and trigger configuration
391
391
Onmap[string]any`json:"on,omitempty"`// Complex trigger config with many variants (too dynamic to type)
392
392
OnNeeds []string`json:"-"`// New typed field extracted from on.needs (not in JSON to avoid conflict)
393
+
OnStopAfterstring`json:"-"`// Typed field extracted from on.stop-after (not in JSON to avoid conflict). Accepts a relative delta ("+25h"), an absolute timestamp, or a GitHub Actions expression (e.g. "${{ inputs.stop-after }}").
393
394
Permissionsmap[string]any`json:"permissions,omitempty"`// Deprecated: use PermissionsTyped (can be string or map)
0 commit comments