Repository navigation
Fix runner-label indentation in custom jobs - #70840
Conversation
Co-authored-by: dsyme <7204669+dsyme@users.noreply.github.com>
Align replacement mappings by their first key to avoid panics when runner selections change shape. Cover scalar, sequence, mapping, expression, quoted, folded, anchor, and alias forms across parsing, emission, and edits. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
|
/review |
|
🧠 Matt Pocock Skills Reviewer has completed the skills-based review. ✅
|
|
✅ Test Quality Sentinel completed test quality analysis. Test Quality SentinelOutcome: The agent completed and reported no action. A no-op output alone does not verify that all required work was completed or that publication was possible. Agent messages are omitted from this comment. The category uses structured run status, not interpretation of agent prose.
|
There was a problem hiding this comment.
🔵 Needs a closer look
The shared source-preserving YAML alignment changes warrant maintainer review beyond the targeted runner-label fix.
No open issues 🎉
What changed in this PR
Fixes runner-label serialization in gh-aw so custom and safe-output jobs preserve correct runner matching, addressing #70811.
Changes:
- Serializes fields with their values before applying job indentation.
- Standardizes source-node alignment during YAML emission.
- Adds indentation and round-trip regression coverage and documents runner-value preservation.
| File | Description |
|---|---|
pkg/workflow/safe_jobs_test.go |
Corrects expected label indentation. |
pkg/workflow/compiler_jobs_custom_fields_test.go |
Tests runner forms and compiled label preservation. |
pkg/workflow/compiler_custom_job_execution.go |
Fixes field serialization and indentation. |
pkg/workflow/aw/document_test.go |
Tests runner values across document round trips and edits. |
pkg/workflow/aw/doc.go |
Documents preserved runner forms. |
pkg/workflow/actions/workflow_test.go |
Tests runner emission, edits, and aliases. |
pkg/workflow/actions/source_sequence.go |
Uses shared node-column alignment. |
pkg/workflow/actions/source_merge.go |
Centralizes alignment for source-preserving merges. |
pkg/workflow/actions/doc.go |
Documents native runner values and expressions. |
Review effort: Max
There was a problem hiding this comment.
Verdict
No actionable blocking issues found in the changed lines. The serialization change correctly marshals the field name together with its value before applying indentation, and the added round-trip coverage exercises scalar, sequence, mapping, expression, anchor, and alias forms. Static review only; tests were not executed.
🔎 Code quality review by PR Code Quality Reviewer · copilot · gpt50 · 1.43 AIC · ⌖ 0.547 AIC · ⊞ 1.6K
Comment /review to run again
There was a problem hiding this comment.
Skills-Based Review 🧠
Applied /diagnosing-bugs and /tdd — approving. Root cause properly fixed with strong regression coverage.
📋 Analysis
Root cause fix, not a patch
The original bug was that formatIndentedYAMLField marshaled the runs-on value standalone, then prepended a fixed 6-space indent to every line including the first sequence item — misaligning subsequent items. The fix wraps the value in map[string]any{fieldName: value} before marshaling, letting go-yaml's own indentation engine produce consistent indentation across all lines. This addresses the actual cause rather than special-casing the symptom, and the same helper is shared by runs-on, strategy, concurrency, container, services, and environment, so all callers benefit.
Regression coverage
TestCompileCustomJobRunnerLabelsreproduces the exact bug report (#70811) end-to-end throughCompileWorkflowand asserts both the raw lock-file bytes and the round-tripped YAML structure.TestBuildCustomJobsRunsOnFormsgainedsingle label arrayandobject with labelscases that pin exact expected indentation.safe_jobs_test.goassertions were corrected to the now-proper indentation (previously encoded the buggy output).- The
actions/awpackages added exhaustiveRunsOnround-trip tests (scalars, quoted/expression forms, flow/block arrays, group/labels maps, anchors and aliases) covering parse → emit → reparse stability for every shape, which is excellent/tdd-style boundary coverage for a YAML-AST-sensitive area.
Verification
Ran the full affected test surface locally (pkg/workflow, pkg/workflow/actions, pkg/workflow/aw) plus go build ./... — all pass.
Minor observation (non-blocking)
The sourceNodeColumn extraction in source_merge.go/source_sequence.go is a clean, welcome dedup of previously duplicated column-finding logic — no action needed.
🧠 Reviewed using Matt Pocock's skills by Matt Pocock Skills Reviewer · copilot · sonnet50 · 58 AIC · ⌖ 15.2 AIC · ⊞ 10.2K
Comment /matt to run again
|
✅ Test Quality Sentinel completed test quality analysis. Test Quality SentinelOutcome: The agent completed and reported no action. A no-op output alone does not verify that all required work was completed or that publication was possible. Agent messages are omitted from this comment. The category uses structured run status, not interpretation of agent prose.
|
|
✅ Ponytail Reviewer completed successfully!
|
|
✅ PR Code Quality Reviewer completed the code quality review.
|
|
✅ Design Decision Gate 🏗️ completed the design decision gate check. See the comment below for the result and any generated ADR draft. Commit pushed:
|
|
🧠 Matt Pocock Skills Reviewer has completed the skills-based review. ✅
|
There was a problem hiding this comment.
Two test matrices provide exhaustive pairwise transitions without corresponding production branches; retain representation parsing and a small set of boundary edits.
net: -26 lines possible.
Generated by ✂️ Ponytail Reviewer for #70840 · codex · gpt56 · 10.4 AIC · ⌖ 5.34 AIC · ⊞ 14.8K
Comment /ponytail to run again
🏗️ Design Decision Gate — ADR RequiredThis PR makes significant changes to core business logic (289 new lines under 📄 Draft ADR committed:
📋 What to do next
Once the PR body links an ADR, this gate re-runs and checks that the implementation matches the decision. ❓ Why ADRs MatterADRs are a searchable, permanent record of why the codebase looks the way it does. Future contributors, including your future self, will thank you. 📋 Michael Nygard ADR Format ReferenceAn ADR is complete only if it has these four sections:
ADRs are stored in
|
There was a problem hiding this comment.
Verdict: comment
No actionable blocking issues found in the changed lines. The serialization fix correctly marshals the field together with its value before applying job indentation, and the source-column helper consistently handles nested mappings and sequences. The added round-trip and compiled-YAML coverage exercises scalar, sequence, mapping, expression, anchor, and alias forms. Static review only; tests were not run per review constraints.
🔎 Code quality review by PR Code Quality Reviewer · copilot · gpt50 · 5.01 AIC · ⌖ 0.532 AIC · ⊞ 1.6K
Comment /review to run again
There was a problem hiding this comment.
Skills-Based Review 🧠
Applied /diagnosing-bugs and /tdd (confirmed via pr-triage: change_type=bug_fix). This is a clean, well-tested root-cause fix — approving.
📋 Key Themes & Highlights
Root cause analysis
The bug (#70811) was that formatIndentedYAMLField marshaled the runs-on value alone (e.g. []any{...}) and then manually prefixed fieldName + ":" plus a fixed 6-space indent per line. Because the YAML encoder did not know the value was nested under a parent key, its own internal sequence-indentation math did not account for that extra nesting level, producing doubled dashes after the first list item. The fix wraps the value in map[string]any{fieldName: value} before marshaling, so goccy/go-yaml computes indentation for the actual nested structure, then re-indents only the continuation lines by a fixed 4 spaces. I reproduced both old and new behavior directly (yaml.MarshalWithOptions with/without wrapping) and confirmed the new approach yields correctly aligned sibling labels.
Verified correctness
- Ran
go test ./pkg/workflow/...— allRunsOn/CustomJobRunnerLabelstests pass, including the new 27 round-trip cases across scalars, flow/block arrays, group/labels mappings, expressions, and YAML anchors/aliases. - Confirmed the error path for unmarshalable values (
chan int) still fails correctly after wrapping. gofmt -landgo vet ./pkg/workflow/...are clean on the changed files.- The shared
sourceNodeColumn()helper insource_merge.gonicely deduplicates three near-identical column-calculation blocks that previously existed separately insource_merge.goandsource_sequence.go— good refactor hygiene alongside the bug fix.
Positive highlights
- ✅ Root cause fixed (serialization context), not just the symptom (manual line-prefixing)
- ✅ Extensive regression coverage at both the
actions.Workflowandaw.Documentlevels, including edit-and-reparse stability checks - ✅
safe_jobs_test.goexpectations updated consistently, sinceextractCustomJobRunsOn/formatIndentedYAMLFieldare shared between custom jobs and safe-output jobs - ✅ Doc comments (
doc.gofiles) updated to describe the preserved runner forms
No actionable issues found in the changed lines; this PR is ready to merge.
🧠 Reviewed using Matt Pocock's skills by Matt Pocock Skills Reviewer · copilot · sonnet50 · 87.7 AIC · ⌖ 15 AIC · ⊞ 10.2K
Comment /matt to run again
Keep every runs-on representation round trip and alias regression while testing eight representative edits per AST, including the block-list-to-mapping panic and each AW execution field kind. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Custom jobs with multiple
runs-onlabels compiled with extra indentation after the first label, causing incorrect runner matching and indefinitely queued jobs.The reported example now compiles as: