πΉ Go Fan Report: goccy/go-yaml
Module Overview
github.com/goccy/go-yaml (v1.19.2) is gh-aw's sole YAML engine β every frontmatter
parse, workflow import merge, and .lock.yml render goes through it.
Current Usage in gh-aw
- Files: 103 files import it directly, concentrated in
pkg/workflow (compiler/YAML
formatting), pkg/parser (frontmatter extraction, imports, error translation), and
pkg/cli (frontmatter mutation commands).
- Key APIs used:
yaml.Unmarshal/yaml.Marshal (the vast majority of call sites, all
into map[string]any), yaml.MarshalWithOptions + DefaultMarshalOptions
(pkg/workflow/yaml_options.go), yaml.MapSlice/yaml.MapItem for GitHub-Actions field
ordering (pkg/workflow/yaml.go), yaml.FormatError for translated parser errors
(pkg/parser/yaml_error.go), and one call to yaml.DisallowUnknownField()
(pkg/cli/env_command.go:286).
- Not used: the AST layer (
ast.File/parser.ParseBytes), yaml.CommentMap,
yaml.Path/PathString, and the generic RegisterCustomMarshaler[T]/
CustomUnmarshaler[T] hooks β everything goes through map[string]any.
Research Findings
Live upstream research was unavailable this run β see details
gh api calls returned 401 Bad Credentials (GH_TOKEN reported invalid by
gh auth status), and WebFetch/WebSearch permissions could not be granted in this
sandbox. Because of that, Step 3 of the standard methodology (release notes, changelog,
recent features from the module's own repository) could not be completed live β the
findings below come entirely from reading gh-aw's own code and its comments describing
goccy's behavior, cross-referenced against call sites.
Suggested follow-up: check why GH_TOKEN was invalid for this run β it silently
degrades every future Go Fan review's upstream research step, not just this one.
Improvement Opportunities
π Quick Wins
UpdateWorkflowFrontmatter (pkg/parser/workflow_update.go:47) skips a correctness
fix applied everywhere else. It calls plain yaml.Marshal(frontmatter) and never
calls parser.QuoteCronExpressions afterward. Five other call sites that marshal
frontmatter YAML (pkg/workflow/frontmatter_extraction_yaml.go:103,
pkg/workflow/tools.go:205,299, pkg/workflow/trigger_parser.go:932,
pkg/cli/update_actions_content_refs.go:119) all post-process the marshaled output
with QuoteCronExpressions to re-quote cron strings goccy emits unquoted (e.g.
cron: 0 14 * * 1-5 instead of cron: "0 14 * * 1-5"). UpdateWorkflowFrontmatter is
what backs gh aw mcp add (pkg/cli/mcp_add.go:295) β running mcp add on a workflow
with a schedule:/cron: trigger can leave the cron expression unquoted, unlike every
other frontmatter-rewrite path. Fix: call QuoteCronExpressions on the marshaled
result before ReconstructWorkflowFile.
- Same function also drops the compiler's YAML formatting conventions β 2-space
indent and literal-style multiline strings (DefaultMarshalOptions in
pkg/workflow/yaml_options.go) aren't applied, because pkg/parser can't import
pkg/workflow (the reverse import already exists, so importing back would cycle).
Workflows edited via mcp add get differently-formatted YAML than ones produced by
gh aw compile. Fix: move an equivalent []yaml.EncodeOption into pkg/parser (or
a shared lower-level package) so both paths use the same house style.
β¨ Feature Opportunities
- Comment/order preservation on frontmatter rewrites. Any
Unmarshal β mutate β
Marshal round-trip through map[string]any permanently discards user-authored
comments and re-sorts keys, since comments/order live on the YAML AST, not on decoded
values. goccy/go-yaml's AST layer (parser.ParseBytes β ast.File) exists precisely to
allow targeted node edits without disturbing the rest of a document. gh aw mcp add is
the concrete, user-facing case: it only needs to touch one field (tools.<id>) in an
otherwise hand-authored file, so it's a good first candidate to prototype AST-based
editing on.
- Consider making
yaml.DisallowUnknownField() opt-in more broadly (or a --strict
flag), not just in pkg/cli/env_command.go β typo'd frontmatter keys currently pass
silently through every other Unmarshal call and are only caught later (if at all) by
JSON-Schema validation.
π Best Practice Alignment
pkg/parser/yaml_error.go's yaml.FormatError + translation-table pattern is already a
good model for user-facing YAML error messages β worth reusing as a precedent rather
than reinventing elsewhere.
pkg/parser/schema_compiler.go:normalizeForJSONSchema is a well-documented, deliberate
workaround for two real goccy decode quirks (int64/uint64 instead of float64;
typed []string/typed maps instead of []any/map[string]any), and deliberately avoids
a json.Marshal/Unmarshal roundtrip for performance. No changes needed.
- The hand-rolled
yaml.MapSlice-based ordering system (OrderMapFields,
MarshalWithFieldOrder) is the correct way to get GitHub-Actions-conventional field
order out of this library β not a case of reinventing something goccy already offers.
Recommendations
- Fix
UpdateWorkflowFrontmatter to call QuoteCronExpressions (small, low-risk, closes
a real correctness gap on the mcp add path).
- Share
DefaultMarshalOptions-equivalent formatting between pkg/parser and
pkg/workflow so all frontmatter-rewrite paths are consistent.
- Prototype AST-based editing for
gh aw mcp add's frontmatter mutation to stop
silently discarding comments/key order.
- Investigate the invalid
GH_TOKEN for gh api reads so future Go Fan reviews can
research upstream live again.
Next Steps
Pick up recommendation 1 first (smallest, highest-confidence fix); 2 and 3 are larger and
worth their own design discussion before implementation.
Module summary saved to: scratchpad/mods/goccy-go-yaml.md
Generated by πΉ Go Fan Β· claude Β· agent Β· 150.8 AIC Β· β 4.8 AIC Β· β 8.2K Β· β·
πΉ Go Fan Report: goccy/go-yaml
Module Overview
github.com/goccy/go-yaml(v1.19.2) is gh-aw's sole YAML engine β every frontmatterparse, workflow import merge, and
.lock.ymlrender goes through it.Current Usage in gh-aw
pkg/workflow(compiler/YAMLformatting),
pkg/parser(frontmatter extraction, imports, error translation), andpkg/cli(frontmatter mutation commands).yaml.Unmarshal/yaml.Marshal(the vast majority of call sites, allinto
map[string]any),yaml.MarshalWithOptions+DefaultMarshalOptions(
pkg/workflow/yaml_options.go),yaml.MapSlice/yaml.MapItemfor GitHub-Actions fieldordering (
pkg/workflow/yaml.go),yaml.FormatErrorfor translated parser errors(
pkg/parser/yaml_error.go), and one call toyaml.DisallowUnknownField()(
pkg/cli/env_command.go:286).ast.File/parser.ParseBytes),yaml.CommentMap,yaml.Path/PathString, and the genericRegisterCustomMarshaler[T]/CustomUnmarshaler[T]hooks β everything goes throughmap[string]any.Research Findings
Live upstream research was unavailable this run β see details
gh apicalls returned401 Bad Credentials(GH_TOKENreported invalid bygh auth status), andWebFetch/WebSearchpermissions could not be granted in thissandbox. Because of that, Step 3 of the standard methodology (release notes, changelog,
recent features from the module's own repository) could not be completed live β the
findings below come entirely from reading gh-aw's own code and its comments describing
goccy's behavior, cross-referenced against call sites.
Suggested follow-up: check why
GH_TOKENwas invalid for this run β it silentlydegrades every future Go Fan review's upstream research step, not just this one.
Improvement Opportunities
π Quick Wins
UpdateWorkflowFrontmatter(pkg/parser/workflow_update.go:47) skips a correctnessfix applied everywhere else. It calls plain
yaml.Marshal(frontmatter)and nevercalls
parser.QuoteCronExpressionsafterward. Five other call sites that marshalfrontmatter YAML (
pkg/workflow/frontmatter_extraction_yaml.go:103,pkg/workflow/tools.go:205,299,pkg/workflow/trigger_parser.go:932,pkg/cli/update_actions_content_refs.go:119) all post-process the marshaled outputwith
QuoteCronExpressionsto re-quote cron strings goccy emits unquoted (e.g.cron: 0 14 * * 1-5instead ofcron: "0 14 * * 1-5").UpdateWorkflowFrontmatteriswhat backs
gh aw mcp add(pkg/cli/mcp_add.go:295) β runningmcp addon a workflowwith a
schedule:/cron:trigger can leave the cron expression unquoted, unlike everyother frontmatter-rewrite path. Fix: call
QuoteCronExpressionson the marshaledresult before
ReconstructWorkflowFile.indent and literal-style multiline strings (
DefaultMarshalOptionsinpkg/workflow/yaml_options.go) aren't applied, becausepkg/parsercan't importpkg/workflow(the reverse import already exists, so importing back would cycle).Workflows edited via
mcp addget differently-formatted YAML than ones produced bygh aw compile. Fix: move an equivalent[]yaml.EncodeOptionintopkg/parser(ora shared lower-level package) so both paths use the same house style.
β¨ Feature Opportunities
Unmarshalβ mutate βMarshalround-trip throughmap[string]anypermanently discards user-authoredcomments and re-sorts keys, since comments/order live on the YAML AST, not on decoded
values. goccy/go-yaml's AST layer (
parser.ParseBytesβast.File) exists precisely toallow targeted node edits without disturbing the rest of a document.
gh aw mcp addisthe concrete, user-facing case: it only needs to touch one field (
tools.<id>) in anotherwise hand-authored file, so it's a good first candidate to prototype AST-based
editing on.
yaml.DisallowUnknownField()opt-in more broadly (or a--strictflag), not just in
pkg/cli/env_command.goβ typo'd frontmatter keys currently passsilently through every other
Unmarshalcall and are only caught later (if at all) byJSON-Schema validation.
π Best Practice Alignment
pkg/parser/yaml_error.go'syaml.FormatError+ translation-table pattern is already agood model for user-facing YAML error messages β worth reusing as a precedent rather
than reinventing elsewhere.
pkg/parser/schema_compiler.go:normalizeForJSONSchemais a well-documented, deliberateworkaround for two real goccy decode quirks (
int64/uint64instead offloat64;typed
[]string/typed maps instead of[]any/map[string]any), and deliberately avoidsa
json.Marshal/Unmarshalroundtrip for performance. No changes needed.yaml.MapSlice-based ordering system (OrderMapFields,MarshalWithFieldOrder) is the correct way to get GitHub-Actions-conventional fieldorder out of this library β not a case of reinventing something goccy already offers.
Recommendations
UpdateWorkflowFrontmatterto callQuoteCronExpressions(small, low-risk, closesa real correctness gap on the
mcp addpath).DefaultMarshalOptions-equivalent formatting betweenpkg/parserandpkg/workflowso all frontmatter-rewrite paths are consistent.gh aw mcp add's frontmatter mutation to stopsilently discarding comments/key order.
GH_TOKENforgh apireads so future Go Fan reviews canresearch upstream live again.
Next Steps
Pick up recommendation 1 first (smallest, highest-confidence fix); 2 and 3 are larger and
worth their own design discussion before implementation.
Module summary saved to:
scratchpad/mods/goccy-go-yaml.md