Skip to content

[go-fan] Go Module Review: goccy/go-yamlΒ #55333

Description

@github-actions

🐹 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

  1. 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.
  2. 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

  1. 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.
  2. 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

  1. Fix UpdateWorkflowFrontmatter to call QuoteCronExpressions (small, low-risk, closes
    a real correctness gap on the mcp add path).
  2. Share DefaultMarshalOptions-equivalent formatting between pkg/parser and
    pkg/workflow so all frontmatter-rewrite paths are consistent.
  3. Prototype AST-based editing for gh aw mcp add's frontmatter mutation to stop
    silently discarding comments/key order.
  4. 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 Β· β—·

  • expires on Aug 25, 2026, 12:21 AM UTC-08:00

Metadata

Metadata

Labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions