Skip to content

fix: serialize array import-inputs as JSON instead of Go slice format [a b]#29084

Merged
pelikhan merged 5 commits intomainfrom
copilot/fix-import-inputs-stringified-go-slice
Apr 29, 2026
Merged

fix: serialize array import-inputs as JSON instead of Go slice format [a b]#29084
pelikhan merged 5 commits intomainfrom
copilot/fix-import-inputs-stringified-go-slice

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Apr 29, 2026

When a shared workflow's run: step references an array-typed import-input via ${{ github.aw.import-inputs.X }}, the compiled output produced Go's fmt.Sprint slice format ([a b]) instead of JSON (["a","b"]), breaking any consumer expecting valid JSON (e.g. jq --argjson).

Root cause

The substitution pipeline had three failure points:

  1. MapToStep (step-level env) — the primary bug. After substituteImportInputsInContent replaces the expression with ["a","b"] (valid JSON), YAML reparsing interprets it as a flow sequence ([]interface{}). When MapToStep later encodes the env map, non-string values fell through to fmt.Sprint(v)[a b].
  2. buildCustomJobs (job-level env) — non-string env values were silently dropped.
  3. marshalImportInputValue / substituteImportInputsInContent — no fallback for typed slices ([]string) that goccy/go-yaml may produce instead of []interface{}.

Changes

  • step_types.go: Replace fmt.Sprint(v) in MapToStep with a new marshalEnvValue() helper that JSON-encodes arrays and maps, with a reflect-based fallback for typed slices ([]string, etc.) produced by goccy/go-yaml.
  • compiler_jobs.go: Apply the same marshalEnvValue() at the job-level env path instead of ignoring non-string values.
  • expression_extraction.go / import_field_extractor.go: Add reflect-based typed-slice fallback to marshalImportInputValue and substituteImportInputsInContent for defense-in-depth.

Result

# Before
env:
  PKGS: "[microsoft/apm#main github/awesome-copilot/skills/foo]"  # breaks jq --argjson

# After
env:
  PKGS: '["microsoft/apm#main","github/awesome-copilot/skills/foo"]'  # valid JSON

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • https://api.github.com/graphql
    • Triggering command: /usr/bin/gh gh repo view --json owner,name --jq .owner.login + "/" + .name ache/go/1.25.8/x64/bin/go GOINSECURE GOMOD GOMODCACHE go estl�� -json GO111MODULE ache/go/1.25.8/x64/bin/go GOINSECURE GOMOD GOMODCACHE go (http block)
    • Triggering command: /usr/bin/gh gh repo view owner/repo 2429�� -json GO111MODULE ache/go/1.25.8/x64/bin/go GOINSECURE GOMOD GOMODCACHE N_/MhA652aEkSuR8NKQxPfr/feDwSjXSTest User env t3706903257/.github/workflows GO111MODULE ache/go/1.25.8/x64/bin/go GOINSECURE GOMOD GOMODCACHE go (http block)
    • Triggering command: /usr/bin/gh gh repo view owner/repo env -json GO111MODULE ache/go/1.25.8/x64/bin/go GOINSECURE GOMOD GOMODCACHE go env t3706903257 GO111MODULE .yml GOINSECURE GOMOD GOMODCACHE go (http block)
  • https://api.github.com/orgs/test-owner/actions/secrets
    • Triggering command: /usr/bin/gh gh api /orgs/test-owner/actions/secrets --jq .secrets[].name -c=4 -nolocalimports -importcfg /tmp/go-build3242920472/b430/importcfg -embedcfg /tmp/go-build3242920472/b430/embedcfg -pack env -json GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE go (http block)
  • https://api.github.com/repos/actions/ai-inference/git/ref/tags/v1
    • Triggering command: /usr/bin/gh gh api /repos/actions/ai-inference/git/ref/tags/v1 --jq [.object.sha, .object.type] | @tsv ns.json and pkg/workflow/data/action_pins.json..." assword=$GITHUB_TOKEN"; }; f get-ifaceassert x_amd64/compile http.https://gitbash (http block)
    • Triggering command: /usr/bin/gh gh api /repos/actions/ai-inference/git/ref/tags/v1 --jq [.object.sha, .object.type] | @tsv --show-toplevel 64/pkg/tool/linux_amd64/compile /usr/bin/git g_.a GO111MODULE ache/go/1.25.8/x--show-toplevel git rev-�� --show-toplevel go /usr/bin/git 5615-35048/test-git GO111MODULE tartedAt,updated--show-toplevel git (http block)
  • https://api.github.com/repos/actions/checkout/git/ref/tags/v3
    • Triggering command: /usr/bin/gh gh api /repos/actions/checkout/git/ref/tags/v3 --jq [.object.sha, .object.type] | @tsv /tmp/TestHashStability_SameInputSameOutput1963615577/001/stability-test.md go /usr/bin/git -json GO111MODULE 64/bin/go git remo�� add origin /usr/bin/git -json GO111MODULE 64/bin/go git (http block)
  • https://api.github.com/repos/actions/checkout/git/ref/tags/v5
    • Triggering command: /usr/bin/gh gh api /repos/actions/checkout/git/ref/tags/v5 --jq [.object.sha, .object.type] | @tsv i.go ntifiers.go 64/pkg/tool/linux_amd64/compile GOINSECURE GOMOD GOMODCACHE 64/pkg/tool/linux_amd64/compile env 2250223116 GO111MODULE 2920472/b398=> GOINSECURE es GOMODCACHE go (http block)
    • Triggering command: /usr/bin/gh gh api /repos/actions/checkout/git/ref/tags/v5 --jq [.object.sha, .object.type] | @tsv --show-toplevel ortcfg /usr/bin/git p/smoke_test_225tr GO111MODULE ache/go/1.25.8/x: git rev-�� --show-toplevel ache/go/1.25.8/x64/pkg/tool/linux_amd64/cgo /usr/bin/git 36504640/.githubgit GO111MODULE ache/go/1.25.8/x--show-toplevel git (http block)
    • Triggering command: /usr/bin/gh gh api /repos/actions/checkout/git/ref/tags/v5 --jq [.object.sha, .object.type] | @tsv --show-toplevel infocmp /usr/bin/git xterm-color -tests /tmp/go-build324--show-toplevel git rev-�� --show-toplevel /tmp/go-build3242920472/b435/repoutil.test /usr/bin/git -test.paniconexigit -test.v=true /usr/bin/git git (http block)
  • https://api.github.com/repos/actions/checkout/git/ref/tags/v6
    • Triggering command: /usr/bin/gh gh api /repos/actions/checkout/git/ref/tags/v6 --jq [.object.sha, .object.type] | @tsv L0nF/R3iUEqf5PFeb3NCkL0nF GO111MODULE 1/x64/bin/node GOINSECURE GOMOD GOMODCACHE go -has�� ithub/workflows/ai-moderator.md 2920472/b424/_testmain.go /opt/hostedtoolcache/go/1.25.8/x64/pkg/tool/linux_amd64/compile GOINSECURE GOMOD GOMODCACHE /opt/hostedtoolcache/go/1.25.8/x-buildtags (http block)
    • Triggering command: /usr/bin/gh gh api /repos/actions/checkout/git/ref/tags/v6 --jq [.object.sha, .object.type] | @tsv 5615-35048/test-2250223116 -trimpath /usr/bin/git -p main -lang=go1.25 git conf�� 900827173 test@example.com /usr/bin/git sions.md -c=4 -nolocalimports git (http block)
    • Triggering command: /usr/bin/gh gh api /repos/actions/checkout/git/ref/tags/v6 --jq [.object.sha, .object.type] | @tsv . 4410388/b439/impGO111MODULE e/git k/gh-aw/gh-aw/pkgit k/gh-aw/gh-aw/pkrev-parse 64/bin/go e/git rev-�� --show-toplevel /opt/hostedtoolcGOPROXY ache/node/24.14.1/x64/bin/node /tmp/go-build256git -trimpath 64/bin/go ache/node/24.14.1/x64/bin/node (http block)
  • https://api.github.com/repos/actions/github-script/git/ref/tags/v8
    • Triggering command: /usr/bin/gh gh api /repos/actions/github-script/git/ref/tags/v8 --jq [.object.sha, .object.type] | @tsv --show-toplevel ache/go/1.25.8/xconfig /usr/bin/git vaScript34147615git GO111MODULE 2920472/b419=> git rev-�� --show-toplevel go /usr/bin/git /v2.0.0 '/tmp/TestParseDrev-parse sv git (http block)
  • https://api.github.com/repos/actions/github-script/git/ref/tags/v9
    • Triggering command: /usr/bin/gh gh api /repos/actions/github-script/git/ref/tags/v9 --jq [.object.sha, .object.type] | @tsv GOSUMDB GOWORK 64/bin/go GOINSECURE GOMOD GOMODCACHE 4410388/b419/impGO111MODULE -c che/go-build/65/GOINSECURE GOPROXY 64/bin/go GOSUMDB GOWORK 64/bin/go /opt/hostedtoolcGO111MODULE (http block)
    • Triggering command: /usr/bin/gh gh api /repos/actions/github-script/git/ref/tags/v9 --jq [.object.sha, .object.type] | @tsv SjWz/pXpMyW3RlN5GOINSECURE GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE 4410388/b436/impGO111MODULE -c che/go-build/12/GOINSECURE GOPROXY 64/bin/go GOSUMDB GOWORK 64/bin/go /opt/hostedtoolcGOPROXY (http block)
    • Triggering command: /usr/bin/gh gh api /repos/actions/github-script/git/ref/tags/v9 --jq [.object.sha, .object.type] | @tsv -json GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE npx pret�� --check scripts/**/*.js 64/bin/go .prettierignore GOWORK 64/bin/go go (http block)
  • https://api.github.com/repos/actions/setup-go/git/ref/tags/v4
    • Triggering command: /usr/bin/gh gh api /repos/actions/setup-go/git/ref/tags/v4 --jq [.object.sha, .object.type] | @tsv --show-toplevel go 2920472/b471/vet.cfg mLsRemoteWithReagit mLsRemoteWithRearev-parse 64/bin/go infocmp -1 xterm-color go /tmp/go-build3242920472/b469/workflow.test report.md GO111MODULE 64/pkg/tool/linu--show-toplevel /tmp/go-build3242920472/b469/workflow.test (http block)
  • https://api.github.com/repos/actions/setup-node/git/ref/tags/v4
    • Triggering command: /usr/bin/gh gh api /repos/actions/setup-node/git/ref/tags/v4 --jq [.object.sha, .object.type] | @tsv /tmp/TestGuardPolicyTrustedUsersRequiresMinIntegrity2255808059/001 remote /usr/bin/git /tmp/go-build256git -trimpath 64/bin/go git -C /tmp/gh-aw-test-runs/20260429-115615-35048/test-1973928049/custom/workflows remote /usr/bin/git -json GO111MODULE 64/bin/go git (http block)
    • Triggering command: /usr/bin/gh gh api /repos/actions/setup-node/git/ref/tags/v4 --jq [.object.sha, .object.type] | @tsv --show-toplevel go /usr/bin/gh -json GO111MODULE 64/bin/go gh run download 3 /opt/hostedtoolcache/go/1.25.8/x64/pkg/tool/linux_amd64/vet test-logs/run-3 GO111MODULE 64/pkg/tool/linu--show-toplevel /opt/hostedtoolcache/go/1.25.8/x64/pkg/tool/linux_amd64/vet (http block)
  • https://api.github.com/repos/actions/setup-node/git/ref/tags/v6
    • Triggering command: /usr/bin/gh gh api /repos/actions/setup-node/git/ref/tags/v6 --jq [.object.sha, .object.type] | @tsv -json GO111MODULE 1/x64/bin/node GOINSECURE GOMOD GOMODCACHE go -has�� ithub/workflows/api-consumption-report.md GO111MODULE /opt/hostedtoolcache/go/1.25.8/x64/pkg/tool/linux_amd64/link 001' 001' GOMODCACHE /opt/hostedtoolcache/go/1.25.8/x-test.v=true (http block)
    • Triggering command: /usr/bin/gh gh api /repos/actions/setup-node/git/ref/tags/v6 --jq [.object.sha, .object.type] | @tsv ithub/workflows/artifacts-summary.md 2920472/b438/_testmain.go /opt/hostedtoolcache/go/1.25.8/x64/pkg/tool/linux_amd64/link -errorsas -ifaceassert -nilfunc /opt/hostedtoolcache/go/1.25.8/x64/pkg/tool/linux_amd64/link -o /tmp/go-build3242920472/b438/semverutil.test -importcfg /usr/bin/git -s -w -buildmode=exe git (http block)
    • Triggering command: /usr/bin/gh gh api /repos/actions/setup-node/git/ref/tags/v6 --jq [.object.sha, .object.type] | @tsv run l /usr/bin/git --detach **/*.cjs 64/bin/go git -C /tmp/TestGuardPolicyTrustedUsersRequiresMinIntegrity2255808059/001 config clusion,workflowName,createdAt,startedAt,updatedAt,event,headBranch,headSha,displayTitle remote.origin.urgit go 64/bin/go node (http block)
  • https://api.github.com/repos/actions/upload-artifact/git/ref/tags/v4
    • Triggering command: /usr/bin/gh gh api /repos/actions/upload-artifact/git/ref/tags/v4 --jq [.object.sha, .object.type] | @tsv /tmp/gh-aw-test-runs/20260429-115615-35048/test-1787475366/.github/workflows rev-parse /usr/bin/git log.showsignaturgit log 64/bin/go git -C /tmp/gh-aw-test-runs/20260429-115615-35048/test-1859363698 config 0"}} remote.origin.urgit GO111MODULE 64/bin/go /opt/hostedtoolcache/go/1.25.8/x64/pkg/tool/linux_amd64/compile (http block)
  • https://api.github.com/repos/github/gh-aw-actions/git/ref/tags/v0.1.2
    • Triggering command: /usr/bin/gh gh api /repos/github/gh-aw-actions/git/ref/tags/v0.1.2 --jq [.object.sha, .object.type] | @tsv --git-dir go /usr/bin/git md GO111MODULE 64/bin/go /usr/bin/git conf�� --get-regexp ^remote\..*\.gh-resolved$ /usr/bin/git -json GO111MODULE util.test git (http block)
  • https://api.github.com/repos/github/gh-aw-actions/git/ref/tags/v1.0.0
    • Triggering command: /usr/bin/gh gh api /repos/github/gh-aw-actions/git/ref/tags/v1.0.0 --jq [.object.sha, .object.type] | @tsv k/gh-aw/gh-aw/.github/workflows/agent-persona-extest-logs/run-5 rev-parse /usr/bin/git /tmp/go-build256git -trimpath 64/bin/go 2920472/b474/importcfg rev-�� k/gh-aw/gh-aw/scripts/lint_error_messages.go k/gh-aw/gh-aw/scripts/lint_error_messages_test.go om/org1/repo.git -json GO111MODULE 64/bin/go git (http block)
  • https://api.github.com/repos/github/gh-aw-actions/git/ref/tags/v1.2.3
    • Triggering command: /usr/bin/gh gh api /repos/github/gh-aw-actions/git/ref/tags/v1.2.3 --jq [.object.sha, .object.type] | @tsv k/gh-aw/gh-aw/.github/workflows/agent-performanctest-logs/run-1 remote 2920472/b465/vet.cfg /tmp/go-build256git -trimpath 64/bin/go infocmp -1 xterm-color (http block)
  • https://api.github.com/repos/github/gh-aw/actions/runs
    • Triggering command: /usr/bin/gh gh run list --json databaseId,number,url,status,conclusion,workflowName,createdAt,startedAt,updatedAt,event,headBranch,headSha,displayTitle --limit 100 --created >=2026-04-22 GOMOD GOMODCACHE go env -json GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE go (http block)
    • Triggering command: /usr/bin/gh gh run list --json databaseId,number,url,status,conclusion,workflowName,createdAt,startedAt,updatedAt,event,headBranch,headSha,displayTitle --limit 100 --created >=2026-03-30 GOMOD GOMODCACHE go env -json GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE go (http block)
    • Triggering command: /usr/bin/gh gh run list --json databaseId,number,url,status,conclusion,workflowName,createdAt,startedAt,updatedAt,event,headBranch,headSha,displayTitle --limit 100 --created >=2026-01-29 GOMOD GOMODCACHE go env -json GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE go (http block)
  • https://api.github.com/repos/github/gh-aw/actions/runs/1/artifacts
    • Triggering command: /usr/bin/gh gh api --paginate repos/{owner}/{repo}/actions/runs/1/artifacts --jq .artifacts[].name GO111MODULE 64/pkg/tool/linux_amd64/vet GOINSECURE GOMOD GOMODCACHE 64/pkg/tool/linux_amd64/vet env 1859363698 GO111MODULE k GOINSECURE GOMOD GOMODCACHE go (http block)
    • Triggering command: /usr/bin/gh gh run download 1 --dir test-logs/run-1 GO111MODULE ache/go/1.25.8/x64/bin/go GOINSECURE GOMOD GOMODCACHE go env -json GO111MODULE ache/go/1.25.8/x64/bin/go GOINSECURE GOMOD GOMODCACHE go (http block)
  • https://api.github.com/repos/github/gh-aw/actions/runs/12345/artifacts
    • Triggering command: /usr/bin/gh gh api --paginate repos/{owner}/{repo}/actions/runs/12345/artifacts --jq .artifacts[].name GO111MODULE 64/pkg/tool/linux_amd64/vet GOINSECURE GOMOD GOMODCACHE 64/pkg/tool/linux_amd64/vet env 1612701431 GO111MODULE ache/go/1.25.8/x64/bin/go GOINSECURE GOMOD GOMODCACHE go (http block)
    • Triggering command: /usr/bin/gh gh run download 12345 --dir test-logs/run-12345 GO111MODULE ntdrain.test GOINSECURE GOMOD GOMODCACHE ntdrain.test 2429�� 2888167393 GO111MODULE ache/go/1.25.8/x64/bin/go GOINSECURE GOMOD GOMODCACHE go (http block)
  • https://api.github.com/repos/github/gh-aw/actions/runs/12346/artifacts
    • Triggering command: /usr/bin/gh gh api --paginate repos/{owner}/{repo}/actions/runs/12346/artifacts --jq .artifacts[].name GO111MODULE 64/pkg/tool/linux_amd64/vet GOINSECURE GOMOD GOMODCACHE 64/pkg/tool/linux_amd64/vet env 12/001/test-simple-frontmatter.md GO111MODULE ache/go/1.25.8/x64/bin/go GOINSECURE GOMOD GOMODCACHE go (http block)
    • Triggering command: /usr/bin/gh gh run download 12346 --dir test-logs/run-12346 GO111MODULE 64/pkg/tool/linux_amd64/vet GOINSECURE GOMOD GOMODCACHE 64/pkg/tool/linu--json env -json GO111MODULE ache/go/1.25.8/x--created GOINSECURE GOMOD GOMODCACHE go (http block)
  • https://api.github.com/repos/github/gh-aw/actions/runs/2/artifacts
    • Triggering command: /usr/bin/gh gh api --paginate repos/{owner}/{repo}/actions/runs/2/artifacts --jq .artifacts[].name GO111MODULE 64/pkg/tool/linux_amd64/link GOINSECURE GOMOD GOMODCACHE 64/pkg/tool/linu^remote\..*\.gh-resolved$ env 1859363698 GO111MODULE x_amd64/compile GOINSECURE GOMOD GOMODCACHE x_amd64/compile (http block)
    • Triggering command: /usr/bin/gh gh run download 2 --dir test-logs/run-2 GO111MODULE ache/go/1.25.8/x64/bin/go GOINSECURE GOMOD GOMODCACHE go env 12/001/test-frontmatter-with-env-template-expressions.md GO111MODULE x_amd64/vet GOINSECURE GOMOD GOMODCACHE x_amd64/vet (http block)
  • https://api.github.com/repos/github/gh-aw/actions/runs/3/artifacts
    • Triggering command: /usr/bin/gh gh api --paginate repos/{owner}/{repo}/actions/runs/3/artifacts --jq .artifacts[].name GO111MODULE x_amd64/compile GOINSECURE GOMOD GOMODCACHE x_amd64/compile env -json GO111MODULE ache/go/1.25.8/x64/bin/go GOINSECURE GOMOD GOMODCACHE go (http block)
    • Triggering command: /usr/bin/gh gh run download 3 --dir test-logs/run-3 GO111MODULE 64/pkg/tool/linux_amd64/compile GOINSECURE GOMOD GOMODCACHE 64/pkg/tool/linux_amd64/compile env g_.a GO111MODULE ache/go/1.25.8/x64/bin/go GOINSECURE GOMOD GOMODCACHE go (http block)
  • https://api.github.com/repos/github/gh-aw/actions/runs/4/artifacts
    • Triggering command: /usr/bin/gh gh api --paginate repos/{owner}/{repo}/actions/runs/4/artifacts --jq .artifacts[].name GO111MODULE 64/pkg/tool/linux_amd64/compile GOINSECURE GOMOD GOMODCACHE 64/pkg/tool/linuTest User env 1859363698 GO111MODULE ache/go/1.25.8/x64/bin/go GOINSECURE GOMOD GOMODCACHE go (http block)
    • Triggering command: /usr/bin/gh gh run download 4 --dir test-logs/run-4 GO111MODULE ache/go/1.25.8/x64/bin/go GOINSECURE GOMOD GOMODCACHE go env 12/001/test-frontmatter-with-nested-objects.md GO111MODULE x_amd64/compile GOINSECURE GOMOD GOMODCACHE x_amd64/compile (http block)
  • https://api.github.com/repos/github/gh-aw/actions/runs/5/artifacts
    • Triggering command: /usr/bin/gh gh api --paginate repos/{owner}/{repo}/actions/runs/5/artifacts --jq .artifacts[].name GO111MODULE 64/pkg/tool/linux_amd64/vet GOINSECURE GOMOD GOMODCACHE 64/pkg/tool/linux_amd64/vet env 12/001/test-complex-frontmatter--errorsas GO111MODULE x_amd64/compile GOINSECURE GOMOD GOMODCACHE x_amd64/compile (http block)
    • Triggering command: /usr/bin/gh gh run download 5 --dir test-logs/run-5 GO111MODULE ache/go/1.25.8/x64/bin/go GOINSECURE GOMOD GOMODCACHE go env -json o ache/go/1.25.8/x64/bin/go GOINSECURE GOMOD GOMODCACHE go (http block)
  • https://api.github.com/repos/github/gh-aw/actions/workflows
    • Triggering command: /usr/bin/gh gh workflow list --json name,state,path --check **/*.cjs 64/bin/go **/*.json --ignore-path ../../../.pretti-bool go env -json GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE go (http block)
    • Triggering command: /usr/bin/gh gh run list --json databaseId,number,url,status,conclusion,workflowName,createdAt,startedAt,updatedAt,event,headBranch,headSha,displayTitle --workflow nonexistent-workflow-12345 --limit 100 GOMOD GOMODCACHE go _bra�� -json GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE go (http block)
    • Triggering command: /usr/bin/gh gh run list --json databaseId,number,url,status,conclusion,workflowName,createdAt,startedAt,updatedAt,event,headBranch,headSha,displayTitle --workflow nonexistent-workflow-12345 --limit 6 GOMOD GOMODCACHE 64/pkg/tool/linuremote.origin.url env -json GO111MODULE ache/go/1.25.8/x64/bin/go GOINSECURE GOMOD GOMODCACHE go (http block)
  • https://api.github.com/repos/github/gh-aw/contents/.github/workflows/shared/reporting.md
    • Triggering command: /tmp/go-build3242920472/b404/cli.test /tmp/go-build3242920472/b404/cli.test -test.testlogfile=/tmp/go-build3242920472/b404/testlog.txt -test.paniconexit0 -test.v=true -test.parallel=4 -test.timeout=10m0s -test.run=^Test -test.short=true GOINSECURE GOMOD GOMODCACHE node /opt�� prettier --check 64/bin/go **/*.ts **/*.json --ignore-path /opt/hostedtoolcGO111MODULE (http block)
  • https://api.github.com/repos/github/gh-aw/git/ref/tags/v0.47.4
    • Triggering command: /usr/bin/gh gh api /repos/github/gh-aw/git/ref/tags/v0.47.4 --jq [.object.sha, .object.type] | @tsv --show-toplevel go /usr/bin/git -json GO111MODULE ache/go/1.25.8/x--show-toplevel git rev-�� --show-toplevel ache/go/1.25.8/x64/pkg/tool/linux_amd64/compile /usr/bin/git /ref/tags/v9 GO111MODULE sv git (http block)
  • https://api.github.com/repos/github/gh-aw/git/ref/tags/v1.0.0
    • Triggering command: /usr/bin/gh gh api /repos/github/gh-aw/git/ref/tags/v1.0.0 --jq [.object.sha, .object.type] | @tsv 5615-35048/test-source-field-variant-86945895/.github/workflows GO111MODULE ache/go/1.25.8/x64/bin/go GOINSECURE GOMOD GOMODCACHE go env -json GO111MODULE bin/bash GOINSECURE GOMOD GOMODCACHE go (http block)
  • https://api.github.com/repos/github/gh-aw/git/ref/tags/v1.2.3
    • Triggering command: /usr/bin/gh gh api /repos/github/gh-aw/git/ref/tags/v1.2.3 --jq [.object.sha, .object.type] | @tsv -json GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE rRo2mzy/Er_eZaAbconfig env -json GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE go (http block)
  • https://api.github.com/repos/github/gh-aw/git/ref/tags/v2.0.0
    • Triggering command: /usr/bin/gh gh api /repos/github/gh-aw/git/ref/tags/v2.0.0 --jq [.object.sha, .object.type] | @tsv iant-86945895/.g--thin GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE _3VA1bELNvSY env Gitcustom_branch161946936/001' Gitcustom_branch161946936/001' 64/bin/go GOINSECURE GOMOD GOMODCACHE go (http block)
    • Triggering command: /usr/bin/gh gh api /repos/github/gh-aw/git/ref/tags/v2.0.0 --jq [.object.sha, .object.type] | @tsv -json GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE go env -json GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE go (http block)
    • Triggering command: /usr/bin/gh gh api /repos/github/gh-aw/git/ref/tags/v2.0.0 --jq [.object.sha, .object.type] | @tsv -json GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE go env lGitbranch_with_hyphen2495740474/001' lGitbranch_with_hyphen2495740474/001' 64/bin/go GOINSECURE GOMOD GOMODCACHE go (http block)
  • https://api.github.com/repos/github/gh-aw/git/ref/tags/v3.0.0
    • Triggering command: /usr/bin/gh gh api /repos/github/gh-aw/git/ref/tags/v3.0.0 --jq [.object.sha, .object.type] | @tsv -json GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE go env phen2495740474/001 phen2495740474/002/work 64/bin/go GOINSECURE GOMOD GOMODCACHE go (http block)
  • https://api.github.com/repos/nonexistent/action/git/ref/tags/v999.999.999
    • Triggering command: /usr/bin/gh gh api /repos/nonexistent/action/git/ref/tags/v999.999.999 --jq [.object.sha, .object.type] | @tsv 5615-35048/test-source-field-variant-86945895/.g--thin GO111MODULE ache/go/1.25.8/x64/bin/go GOINSECURE GOMOD GOMODCACHE hz/8-8vmLiYCmHH9yLNKNaz/ITCHFh6Rconfig env efaultBranchFromLsRemoteWithRealremote.origin.url efaultBranchFromLsRemoteWithRealGitcustom_branch161946936/001' ache/node/24.14.1/x64/bin/bash GOINSECURE GOMOD GOMODCACHE go (http block)
  • https://api.github.com/repos/nonexistent/repo/actions/runs/12345
    • Triggering command: /usr/bin/gh gh run view 12345 --repo nonexistent/repo --json status,conclusion GOINSECURE GOMOD GOMODCACHE go env 12/001/test-inlined-imports-enabled-with-env-template-expressions-in-body.md GO111MODULE sv GOINSECURE GOMOD GOMODCACHE go (http block)
  • https://api.github.com/repos/owner/repo/actions/workflows
    • Triggering command: /usr/bin/gh gh workflow list --json name,state,path --repo owner/repo 64/bin/go ettierignore GOWORK 64/bin/go go env -json GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE go (http block)
    • Triggering command: /usr/bin/gh gh workflow list --json name,state,path --repo owner/repo 64/bin/go .prettierignore GOWORK 64/bin/go go env -json GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE go (http block)
    • Triggering command: /usr/bin/gh gh workflow list --repo owner/repo --json name,path,state ortcfg.link GOINSECURE GOMOD GOMODCACHE FYD7TXdX-i1ql3kueA/opxEl9d6gxDxhremote.origin.url env -json GO111MODULE g_.a GOINSECURE GOMOD GOMODCACHE go (http block)
  • https://api.github.com/repos/test-owner/test-repo/actions/secrets
    • Triggering command: /usr/bin/gh gh api /repos/test-owner/test-repo/actions/secrets --jq .secrets[].name --check **/*.cjs 64/bin/go **/*.json --ignore-path ../../../.pretti-x go env -json GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE go (http block)
  • https://api.github.com/repos/test/repo
    • Triggering command: /usr/bin/gh gh api /repos/test/repo --jq .default_branch 12/001/test-frontmatter-with-nested-objects.md GO111MODULE x_amd64/compile GOINSECURE GOMOD GOMODCACHE x_amd64/compile env -json GO111MODULE ache/go/1.25.8/x64/bin/go GOINSECURE GOMOD GOMODCACHE go (http block)

If you need me to access, download, or install something from one of these locations, you can either:

Arrays from goccy/go-yaml were hitting fmt.Sprint() in MapToStep's env
handling, producing '[a b]' instead of valid JSON. Fix three locations:

1. step_types.go: Replace fmt.Sprint(v) with marshalEnvValue() for step
   env vars - handles []any, map[string]any, and typed slices via reflection.
2. compiler_jobs.go: Same fix for top-level job env vars.
3. expression_extraction.go: Add reflection fallback to marshalImportInputValue
   for typed slices (e.g. []string) from goccy/go-yaml.
4. import_field_extractor.go: Same reflection fallback in substituteImportInputsInContent.

Fixes: import-input arrays are now serialized as '["a","b"]' (valid JSON)
in both step env vars and markdown prompt text, enabling jq --argjson consumers.

Agent-Logs-Url: https://github.com/github/gh-aw/sessions/138f5a50-0079-4ed9-9b9b-fb4e4979153b

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix import-input arrays being stringified as Go-slice fix: serialize array import-inputs as JSON instead of Go slice format [a b] Apr 29, 2026
Copilot AI requested a review from pelikhan April 29, 2026 11:58
Comment thread pkg/parser/import_field_extractor.go Outdated
return string(b), true
}
case reflect.Map:
normalized := make(map[string]any, rv.Len())
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot make sure keys are sorted to keep serialization stable

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in c275ff4. All three reflect.Map branches now collect keys, sort them with sort.Strings, and iterate in sorted order when building the normalized map before marshaling.

@pelikhan pelikhan marked this pull request as ready for review April 29, 2026 11:59
Copilot AI review requested due to automatic review settings April 29, 2026 11:59
@github-actions
Copy link
Copy Markdown
Contributor

🧪 Test Quality Sentinel Report

Test Quality Score: 90/100

Excellent test quality

Metric Value
New/modified tests analyzed 2
✅ Design tests (behavioral contracts) 2 (100%)
⚠️ Implementation tests (low value) 0 (0%)
Tests with error/edge cases 2 (100%)
Duplicate test clusters 0
Test inflation detected ⚠️ Minor (expression_extraction_test.go: 62 lines added vs 26 in production, ratio 2.38:1)
🚨 Coding-guideline violations None

Test Classification Details

Test File Classification Issues Detected
TestMarshalImportInputValue pkg/workflow/expression_extraction_test.go ✅ Design None — 8 table rows covering scalars, typed slices, maps, empty inputs
TestImportWithArrayInputs pkg/workflow/imports_inputs_test.go ✅ Design None — end-to-end regression test on compiled lock file output

Test Analysis

TestMarshalImportInputValue

Table-driven test (8 rows) verifying the marshalImportInputValue helper serializes values correctly. Covers: string/int/bool scalars (pass-through), []any, []string (the exact goccy/go-yaml regression case), []int, map[string]any, and empty []string. All branches of the bug fix are exercised. High value — deleting this would allow the JSON serialization contract to regress silently.

TestImportWithArrayInputs

End-to-end regression test: writes real workflow markdown files, compiles via workflow.NewCompiler(), reads the emitted lock file, and asserts both the negative case (Go slice format [a b] must not appear) and the positive case (JSON array must appear). Also checks no unresolved expressions remain. Very high value — precisely guards against the reported bug.


Minor Observations

Test inflation in expression_extraction_test.go: 62 lines added in test vs 26 in expression_extraction.go (ratio ≈ 2.4:1). This is borderline, but the extra lines are justified — 8 table-driven test cases with inline struct literals are naturally verbose. No action required.

No assertion messages on some t.Error/t.Errorf calls, but the format strings already carry sufficient context (e.g. t.Errorf("marshalImportInputValue(%v) = %q, want %q", ...)). Acceptable.


Language Support

Tests analyzed:


Verdict

Check passed. 0% of new tests are implementation tests (threshold: 30%). Both tests directly guard the behavioral contract described in the PR: array-typed import-inputs must serialize as JSON, not Go slice format.


📖 Understanding Test Classifications

Design Tests (High Value) verify what the system does:

  • Assert on observable outputs, return values, or state changes
  • Cover error paths and boundary conditions
  • Would catch a behavioral regression if deleted
  • Remain valid even after internal refactoring

Implementation Tests (Low Value) verify how the system does it:

  • Assert on internal function calls (mocking internals)
  • Only test the happy path with typical inputs
  • Break during legitimate refactoring even when behavior is correct
  • Give false assurance: they pass even when the system is wrong

Goal: Shift toward tests that describe the system's behavioral contract — the promises it makes to its users and collaborators.

References: §25107570244

🧪 Test quality analysis by Test Quality Sentinel · ● 629.8K ·

Copy link
Copy Markdown
Contributor

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Test Quality Sentinel: 90/100. Test quality is excellent — 0% of new tests are implementation tests (threshold: 30%). Both tests are high-value behavioral contracts that directly guard the array JSON serialization fix.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Fixes incorrect serialization of array-typed import-inputs when substituted into workflow YAML/markdown so that consumers receive valid JSON (e.g., for jq --argjson) instead of Go’s slice string format ([a b]).

Changes:

  • Add marshalEnvValue() and use it when converting step/job env maps so arrays/maps are JSON-encoded instead of fmt.Sprint output.
  • Add reflection-based fallbacks to JSON-encode typed slices/maps produced by goccy/go-yaml during import-input substitution.
  • Add regression/unit tests covering array import-inputs JSON serialization.
Show a summary per file
File Description
pkg/workflow/step_types.go Introduces marshalEnvValue() and uses it for step-level env serialization to preserve JSON arrays/objects.
pkg/workflow/compiler_jobs.go Applies the same env value marshaling at the job level instead of dropping non-string env values.
pkg/workflow/expression_extraction.go Extends marshalImportInputValue to JSON-encode typed slices/maps via reflection.
pkg/parser/import_field_extractor.go Adds reflection fallback to JSON-encode typed slices/maps during raw-content substitution.
pkg/workflow/imports_inputs_test.go Adds end-to-end regression test ensuring array import-inputs end up as JSON in the compiled lock output.
pkg/workflow/expression_extraction_test.go Adds direct unit tests for marshalImportInputValue across scalar/slice/map types.

Copilot's findings

Tip

Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

  • Files reviewed: 6/6 changed files
  • Comments generated: 2

Comment on lines +344 to +348
default:
// Handle typed slices (e.g. []string) that goccy/go-yaml may produce
// instead of []any, and typed maps.
rv := reflect.ValueOf(v)
switch rv.Kind() {
Copy link

Copilot AI Apr 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

marshalImportInputValue now uses reflect.ValueOf(v) in the default branch. If the resolved import input exists but is explicitly null in YAML (so the map contains the key with a nil value), reflect.ValueOf(nil) yields an invalid Value and rv.Kind() will panic at runtime. Add a case nil: (or a if value == nil guard) to bypass the reflection path and preserve the previous non-panicking behavior.

Copilot uses AI. Check for mistakes.
@@ -804,6 +807,26 @@ func substituteImportInputsInContent(content string, inputs map[string]any) stri
if b, err := json.Marshal(v); err == nil {
return string(b), true
}
Copy link

Copilot AI Apr 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

substituteImportInputsInContent uses reflect.ValueOf(v) in the default branch. If an input key is present with a nil value (e.g. with: { packages: } in YAML), reflect.ValueOf(nil) produces an invalid Value and rv.Kind() will panic. Add an explicit nil check / case nil: before the reflection fallback so null inputs don’t crash compilation.

Suggested change
}
}
case nil:
// Preserve the existing scalar fallback behavior for null inputs,
// but avoid calling reflect.ValueOf(nil), which yields an invalid
// Value and would panic on Kind().

Copilot uses AI. Check for mistakes.
…-inputs

Documents the decision to serialize array/map-typed import-input values
as JSON in compiled env blocks, replacing the fmt.Sprint fallback.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@github-actions
Copy link
Copy Markdown
Contributor

Commit pushed: ccb42ed

🏗️ ADR gate enforced by Design Decision Gate 🏗️

@github-actions
Copy link
Copy Markdown
Contributor

🏗️ Design Decision Gate — ADR Required

This PR makes significant changes to core business logic (248 new lines in pkg/) but does not have a linked Architecture Decision Record (ADR).

AI has analyzed the PR diff and generated a draft ADR to help you get started:

📄 Draft ADR: docs/adr/29084-json-serialization-for-array-typed-import-inputs.md

The draft captures the key decision: serializing array/map-typed import-input values as JSON (instead of Go's fmt.Sprint output [a b]) across all three env-writing paths, with a reflection fallback for typed slices produced by goccy/go-yaml.

What to do next

  1. Review the draft ADR committed to your branch — it was generated from the PR diff
  2. Complete the missing sections — add context the AI couldn't infer, refine the decision rationale, and list real alternatives you considered
  3. Commit the finalized ADR to docs/adr/ on your branch
  4. Reference the ADR in this PR body by adding a line such as:

    ADR: ADR-29084: JSON Serialization for Array-Typed Import-Inputs

Once an ADR is linked in the PR body, this gate will re-run and verify the implementation matches the decision.

Why ADRs Matter

"AI made me procrastinate on key design decisions. Because refactoring was cheap, I could always say 'I'll deal with this later.' Deferring decisions corroded my ability to think clearly."

ADRs create a searchable, permanent record of why the codebase looks the way it does. Future contributors (and your future self) will thank you.


📋 Michael Nygard ADR Format Reference

An ADR must contain these four sections to be considered complete:

  • Context — What is the problem? What forces are at play?
  • Decision — What did you decide? Why?
  • Alternatives Considered — What else could have been done?
  • Consequences — What are the trade-offs (positive and negative)?

All ADRs are stored in docs/adr/ as Markdown files numbered by PR number (e.g., 29084-json-serialization-for-array-typed-import-inputs.md for PR #29084).

🔒 This PR cannot merge until an ADR is linked in the PR body.

🏗️ ADR gate enforced by Design Decision Gate 🏗️ · ● 151.7K ·

Adds explicit sort.Strings() before building the normalized map in all three
reflect.Map branches (marshalImportInputValue, substituteImportInputsInContent,
marshalEnvValue) to make serialization order deterministic and clearly
intentional, regardless of reflect.MapKeys() iteration order.

Agent-Logs-Url: https://github.com/github/gh-aw/sessions/76c36295-aae8-4e78-b37d-84593ed57e8a

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Copilot AI requested a review from pelikhan April 29, 2026 12:10
@pelikhan
Copy link
Copy Markdown
Collaborator

@copilot review all comments

Add explicit `case nil:` branches in marshalImportInputValue,
substituteImportInputsInContent, and marshalEnvValue so that a null
import-input value (e.g. `with: { packages: }` in YAML) does not
panic when reflect.ValueOf(nil) is called. Also adds a nil test case
to TestMarshalImportInputValue.

Agent-Logs-Url: https://github.com/github/gh-aw/sessions/873bdc30-1e53-4d1b-bc2b-ef1ba74ae5f3

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Copy link
Copy Markdown
Contributor Author

Copilot AI commented Apr 29, 2026

@copilot review all comments

Addressed in 87a2d82. Added case nil: guards in all three marshal helpers (marshalImportInputValue, substituteImportInputsInContent, marshalEnvValue) so a null import-input value doesn't reach reflect.ValueOf(nil) and panic. Also added a nil_value sub-case to TestMarshalImportInputValue.

@pelikhan pelikhan merged commit 7590969 into main Apr 29, 2026
@pelikhan pelikhan deleted the copilot/fix-import-inputs-stringified-go-slice branch April 29, 2026 12:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Import-input arrays are stringified as Go-slice [a b] instead of JSON, breaking jq --argjson consumers

3 participants