Skip to content

Extract shared buildInputSchema helper to deduplicate input-schema generation#25795

Merged
pelikhan merged 2 commits intomainfrom
copilot/fix-duplicate-code-input-schema
Apr 11, 2026
Merged

Extract shared buildInputSchema helper to deduplicate input-schema generation#25795
pelikhan merged 2 commits intomainfrom
copilot/fix-duplicate-code-input-schema

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Apr 11, 2026

Three MCP tool generators (generateCallWorkflowTool, generateDispatchWorkflowTool, generateDispatchRepositoryTool) each contained ~60 lines of nearly identical JSON Schema construction logic for mapping GitHub Actions input definitions to MCP tool inputSchema. A bug fix or type-mapping change required touching all three sites, with subtle behavioral drift already present (e.g., choice-type default handling existed only in dispatch_repository).

Changes

  • New pkg/workflow/build_input_schema.go — shared buildInputSchema(inputs, descriptionFn) that returns (properties, required). Accepts a callback for fallback descriptions since each caller uses different phrasing.
  • Replaced duplicated loops in all three generators with a 3-line call:
    properties, required := buildInputSchema(workflowInputs, func(inputName string) string {
        return fmt.Sprintf("Input parameter '%s' for workflow %s", inputName, workflowName)
    })
  • Normalized choice-default handling — the helper uses the most complete variant (from dispatch_repository) so choice inputs with defaults now behave consistently across all three generators.
  • 15 new tests in build_input_schema_test.go covering type mapping, default propagation, required tracking, invalid-def skipping, nil/empty inputs, fallback descriptions, and choice edge cases.

All 187 workflow lock files recompile identically, confirming no schema output change for existing workflows.

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 /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 vce9/Iw7fHw9tzQVrev-parse 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 9516280/b229/_pkgit oYmy/n_pwg_VDfKQrev-parse 9516280/b229=> git (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 /usr/bin/unpigz /usr/bin/infocmp--show-toplevel git rev-�� --show-toplevel infocmp /usr/bin/git xterm-color git /usr/bin/git git (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 git /usr/bin/git go env -json GO111MODULE 64/bin/go 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 -json GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE go 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 --show-toplevel x_amd64/compile /opt/hostedtoolcache/node/24.14.1/x64/bin/node -json GO111MODULE 64/pkg/tool/linu--show-toplevel /opt/hostedtoolcache/node/24.14.1/x64/bin/node /tmp�� Value: ${{ github.actor }} 64/pkg/tool/linutest@example.com /usr/bin/git g_.a GO111MODULE 64/pkg/tool/linu--show-toplevel git (http block)
    • Triggering command: /usr/bin/gh gh api /repos/actions/ai-inference/git/ref/tags/v1 --jq .object.sha -ignore_readdir_race -lname /home/REDACTED/.cargo/bin/bash -exec touch -c bash --no�� --noprofile git /usr/bin/git --show-toplevel infocmp /usr/bin/git 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 --show-toplevel go /usr/bin/git 1032488834/001' 1032488834/001' 64/bin/go git rev-�� --show-toplevel l /usr/bin/git -json GO111MODULE 64/bin/go git (http block)
    • Triggering command: /usr/bin/gh gh api /repos/actions/checkout/git/ref/tags/v3 --jq .object.sha --show-toplevel git /usr/bin/git --show-toplevel x_amd64/compile /usr/bin/git git rev-�� --show-toplevel git /usr/bin/git --show-toplevel go (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 g_.a 7zDW0_L0m ache/go/1.25.8/x64/pkg/tool/linux_amd64/compile GOINSECURE GOMOD GOMODCACHE ache/go/1.25.8/x64/pkg/tool/linux_amd64/compile env JyY-sPRHm GO111MODULE ache/go/1.25.8/x64/pkg/tool/linux_amd64/compile GOINSECURE GOMOD 9516280/b007/sym--show-toplevel ache/go/1.25.8/x64/pkg/tool/linutest@example.com (http block)
    • Triggering command: /usr/bin/gh gh api /repos/actions/checkout/git/ref/tags/v5 --jq .object.sha --show-toplevel 64/pkg/tool/linuremote.origin.url /usr/bin/git ut3043525165/001git GO111MODULE 64/pkg/tool/linu--show-toplevel git rev-�� --show-toplevel 64/pkg/tool/linux_amd64/vet /usr/bin/git -json qbNVEaFt_ 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 --show-toplevel 64/pkg/tool/linux_amd64/link /usr/bin/git ipts.test pkg/mod/golang.orev-parse ortcfg.link git rev-�� --show-toplevel 9FX53sm1OTZ6jdpoJ_/CWrYu2czG7Ca7ylQP4Z8/vCNYLdc7D8RXanEmFBss /usr/bin/git runs/20260411-12git -trimpath g_.a 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 add origin /usr/bin/git go1.25.8 -c=4 -nolocalimports git rev-�� --show-toplevel /tmp/go-build1937493502/b455/_testmain.go /usr/bin/git -json o x_amd64/compile git (http block)
    • Triggering command: /usr/bin/gh gh api /repos/actions/checkout/git/ref/tags/v6 --jq .object.sha add other /usr/bin/git repo2493319662/0git GO111MODULE x_amd64/vet git rev-�� --show-toplevel x_amd64/vet /usr/bin/git -json GO111MODULE 64/pkg/tool/linu--show-toplevel git (http block)
    • Triggering command: /usr/bin/gh gh api /repos/actions/checkout/git/ref/tags/v6 --jq .object.sha --show-toplevel ache/go/1.25.8/x64/pkg/tool/linux_amd64/compile /usr/bin/git vaScript18188640git -trimpath 7493502/b418=> git rev-�� --show-toplevel /opt/hostedtoolcache/go/1.25.8/xrepos/{owner}/{repo}/actions/runs/3/artifacts /usr/bin/infocmp 75 pkg/mod/go.yaml.rev-parse /opt/hostedtoolc--show-toplevel infocmp (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 --show-toplevel 64/pkg/tool/linux_amd64/vet /usr/bin/git 9/001/test-frontgit O_2Lfc9jA ache/go/1.25.8/x322673d1822d3fcaaca5ec51ef5997ef6ffecf6a..HEAD git rev-�� --show-toplevel ache/go/1.25.8/x12345 /usr/bin/git ortcfg GO111MODULE .cfg git (http block)
    • Triggering command: /usr/bin/gh gh api /repos/actions/github-script/git/ref/tags/v8 --jq .object.sha --show-toplevel git /usr/bin/git --show-toplevel git /usr/bin/git git rev-�� --show-toplevel git /usr/bin/git --show-toplevel git /usr/bin/git 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 go1.25.8 -c=4 -nolocalimports -importcfg /tmp/go-build3429516280/b243/importcfg -pack /home/REDACTED/go/pkg/mod/golang.org/x/text@v0.35.0/internal/language/compact/compact.go env -json (http block)
    • Triggering command: /usr/bin/gh gh api /repos/actions/github-script/git/ref/tags/v9 --jq .object.sha -json GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE go env -json GO111MODULE x_amd64/compile GOINSECURE GOMOD GOMODCACHE x_amd64/compile (http block)
    • Triggering command: /usr/bin/gh gh api /repos/actions/github-script/git/ref/tags/v9 --jq .object.sha -json GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE go env -json GO111MODULE x_amd64/asm GOINSECURE GOMOD GOMODCACHE x_amd64/asm (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 --get remote.origin.url /usr/bin/git -json GO111MODULE 64/bin/go git rev-�� --show-toplevel go /opt/hostedtoolcache/node/24.14.1/x64/bin/node m/workflows @v1.1.3/cpu/cpuirev-parse 64/pkg/tool/linu--show-toplevel node (http block)
    • Triggering command: /usr/bin/gh gh api /repos/actions/setup-go/git/ref/tags/v4 --jq .object.sha --show-toplevel git /usr/bin/git --show-toplevel 7493502/b381/imprev-parse /usr/bin/git git rev-�� --show-toplevel git /opt/hostedtoolcache/node/24.14.1/x64/bin/bash --show-toplevel 7493502/b429/semrev-parse /usr/bin/git bash (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 remove origin /usr/bin/git workflows GO111MODULE x_amd64/compile git rev-�� --show-toplevel x_amd64/compile /usr/bin/git -json GO111MODULE 64/pkg/tool/linu--show-toplevel git (http block)
    • Triggering command: /usr/bin/gh gh api /repos/actions/setup-node/git/ref/tags/v4 --jq .object.sha --show-toplevel git /usr/bin/git --show-toplevel /opt/hostedtoolcrev-parse n-dir/node git rev-�� --show-toplevel git /home/REDACTED/go/bin/bash --show-toplevel 7493502/b422/parrev-parse /usr/bin/git bash (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 ithub/workflows/agent-performance-analyzer.md -buildtags /usr/lib/git-core/git -errorsas -ifaceassert -nilfunc /usr/lib/git-core/git unpa�� --pack_header=2,3 -q om/testorg/testrepo.git /tmp/go-build350git -trimpath 64/bin/go git (http block)
    • Triggering command: /usr/bin/gh gh api /repos/actions/upload-artifact/git/ref/tags/v4 --jq .object.sha --show-toplevel git /usr/bin/git --show-toplevel 64/pkg/tool/linurev-parse /usr/bin/git git rev-�� --show-toplevel git n-dir/bash --show-toplevel ache/go/1.25.8/xrev-parse /usr/bin/git git (http block)
  • https://api.github.com/repos/actions/upload-artifact/git/ref/tags/v7
    • Triggering command: /usr/bin/gh gh api /repos/actions/upload-artifact/git/ref/tags/v7 --jq .object.sha 9516280/b166/_pktest-logs/run-1 om/santhosh-tekuri/jsonschema/v6@v6.0.2/kind/kind.go ache/go/1.25.8/x64/pkg/tool/linux_amd64/compile GOINSECURE fips140/rsa GOMODCACHE ache/go/1.25.8/x64/pkg/tool/linuTest User env 9516280/b235/_pkg_.a NED5/7tbAQfilcefdiXaaNED5 9516280/b235=> GOINSECURE osh-tekuri/jsonsrev-parse GOMODCACHE 9gdENF7/XrFc0YzkOD3l6_X8Sjqe (http block)
    • Triggering command: /usr/bin/gh gh api /repos/actions/upload-artifact/git/ref/tags/v7 --jq .object.sha 9516280/b220/_pkg_.a i2Jk/kxQktkbJrdZm0O72i2Jk 64/pkg/tool/linux_amd64/vet GOINSECURE GOMOD GOMODCACHE 64/pkg/tool/linux_amd64/vet cat-�� 9516280/b051/importcfg -Eee/499QsILxkBjFfa_H-Eee /opt/hostedtoolcache/go/1.25.8/x64/pkg/tool/linux_amd64/compile GOSUMDB GOWORK 64/bin/go /opt/hostedtoolcache/go/1.25.8/x64/pkg/tool/linux_amd64/compile (http block)
    • Triggering command: /usr/bin/gh gh api /repos/actions/upload-artifact/git/ref/tags/v7 --jq .object.sha 9516280/b254/_pkg_.a NNuM/NZNs7zEf3uyY_7BzNNuM 1/x64/bin/node GOINSECURE /semver GOMODCACHE /opt/hostedtoolcache/go/1.25.8/x64/pkg/tool/linux_amd64/asm t-ha�� vaScript181886409/001/test-frontmatter-with-arrays.md -trimpath /opt/hostedtoolcache/go/1.25.8/x64/pkg/tool/linux_amd64/compile -I /tmp/go-build342rev-parse -I /opt/hostedtoolcache/go/1.25.8/xrepos/{owner}/{repo}/actions/runs/2/artifacts (http block)
  • https://api.github.com/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 -json GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE go /pre�� -json GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE go (http block)
    • Triggering command: /usr/bin/gh gh api /repos/astral-sh/setup-uv/git/ref/tags/eac588ad8def6316056a12d4907a9d4d84ff7a3b --jq .object.sha -json GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE go env re GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE go (http block)
  • https://api.github.com/repos/docker/build-push-action/git/ref/tags/v7
    • Triggering command: /usr/bin/gh gh api /repos/docker/build-push-action/git/ref/tags/v7 --jq .object.sha on' --ignore-path ../../../.pret.prettierignore GO111MODULE $name) { hasDiscussionsEnabled } } 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/docker/build-push-action/git/ref/tags/v7 --jq .object.sha on' --ignore-path ../../../.pret.prettierignore GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE go env -json GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE go (http block)
  • https://api.github.com/repos/docker/metadata-action/git/ref/tags/v6
    • Triggering command: /usr/bin/gh gh api /repos/docker/metadata-action/git/ref/tags/v6 --jq .object.sha on' --ignore-path ../../../.pret.prettierignore GO111MODULE r: $owner, name: $name) { hasDiscussionsEnabled } } GOINSECURE GOMOD GOMODCACHE go env -json GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE go (http block)
    • Triggering command: /usr/bin/gh gh api /repos/docker/metadata-action/git/ref/tags/v6 --jq .object.sha on' --ignore-patremote.origin.url GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE go env -json GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE go (http block)
  • https://api.github.com/repos/docker/setup-buildx-action/git/ref/tags/v4
    • Triggering command: /usr/bin/gh gh api /repos/docker/setup-buildx-action/git/ref/tags/v4 --jq .object.sha on' --ignore-pat-f 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/docker/setup-buildx-action/git/ref/tags/v4 --jq .object.sha on' --ignore-pat-f GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE go env to pkg/workflow/remote.origin.url GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE go (http block)
  • https://api.github.com/repos/github/gh-aw
    • Triggering command: /usr/bin/gh gh api /repos/github/gh-aw --jq .visibility -json GO111MODULE r: $owner, name: $name) { hasDiscussionsEnabled } } GOINSECURE GOMOD GOMODCACHE go env -json GO111MODULE de_modules/.bin/node GOINSECURE GOMOD GOMODCACHE go (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 /tmp/gh-aw-test-runs/20260411-120419-32160/test-107976138 resolved$ /usr/bin/git @{u} GO111MODULE x_amd64/link git rev-�� --git-dir x_amd64/link /usr/bin/git -json GO111MODULE 64/pkg/tool/linu--show-toplevel git (http block)
    • Triggering command: /usr/bin/gh gh api /repos/github/gh-aw-actions/git/ref/tags/v0.1.2 --jq .object.sha --show-toplevel git /usr/bin/git --show-toplevel /opt/hostedtoolcrev-parse /usr/bin/git git rev-�� --show-toplevel git /home/REDACTED/.config/composer/vendor/bin/bash --show-toplevel /opt/hostedtoolcrev-parse /usr/bin/git bash (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 /tmp/go-build1937493502/b397/cli.test -importcfg /usr/lib/git-core/git-receive-pack -s -w -buildmode=exe git-receive-pack /tmp�� :latest -extld=gcc /usr/bin/git -json GO111MODULE 64/bin/go git (http block)
    • Triggering command: /usr/bin/gh gh api /repos/github/gh-aw-actions/git/ref/tags/v1.0.0 --jq .object.sha --show-toplevel git /usr/bin/git --show-toplevel x_amd64/vet /usr/bin/git git rev-�� --show-toplevel git /usr/bin/git --show-toplevel ache/go/1.25.8/xrev-parse /usr/bin/git 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 -bool -buildtags ache/go/1.25.8/x64/pkg/tool/linux_amd64/compile -errorsas -ifaceassert -nilfunc ache/go/1.25.8/x64/pkg/tool/linux_amd64/compile push�� 7493502/b449/_pkg_.a my-default 7493502/b449=> -json (http block)
    • Triggering command: /usr/bin/gh gh api /repos/github/gh-aw-actions/git/ref/tags/v1.2.3 --jq .object.sha --show-toplevel git /usr/bin/git --show-toplevel go /usr/bin/git git rev-�� --show-toplevel git /usr/bin/git 4 -type d -namegit ache/go/1.25.8/xrev-parse /usr/bin/git git (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 l.go 64/pkg/tool/linux_amd64/compile GOINSECURE al_wasm.o 64/src/internal/--show-toplevel 64/pkg/tool/linux_amd64/compile env g_.a Y_7BzNNuM ache/go/1.25.8/x64/pkg/tool/linux_amd64/asm GOINSECURE GOMOD GOMODCACHE ache/go/1.25.8/x64/pkg/tool/linux_amd64/asm (http block)
    • Triggering command: /usr/bin/gh gh run download 1 --dir test-logs/run-1 ri/jsonschema/v6@v6.0.2/kind/kind.go 64/pkg/tool/linux_amd64/compile GOINSECURE GOMOD GOMODCACHE 64/pkg/tool/linuTest User env g_.a diXaaNED5 ache/go/1.25.8/x64/pkg/tool/linux_amd64/compile GOINSECURE chema/v6 GOMODCACHE ache/go/1.25.8/x64/pkg/tool/linux_amd64/compile (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 emplate/v3@v3.0.2/compile.go 64/pkg/tool/linux_amd64/compile GOINSECURE GOMOD abis 64/pkg/tool/linuTest User env g_.a KjIdi_zAe ache/go/1.25.8/x64/pkg/tool/linu--limit GOINSECURE GOMOD GOMODCACHE ache/go/1.25.8/xremote.origin.url (http block)
    • Triggering command: /usr/bin/gh gh run download 12345 --dir test-logs/run-12345 GO111MODULE 64/pkg/tool/linux_amd64/compile GOINSECURE essage abis 64/pkg/tool/linux_amd64/compile env g_.a ke8fejfLv x_amd64/compile GOINSECURE GOMOD GOMODCACHE x_amd64/compile (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/compile GOINSECURE GOMOD abis 64/pkg/tool/linux_amd64/compile 64/s�� g_.a sNGC5r73k 64/pkg/tool/linux_amd64/compile GOINSECURE GOMOD GOMODCACHE 64/pkg/tool/linuremote.origin.url (http block)
    • Triggering command: /usr/bin/gh gh run download 12346 --dir test-logs/run-12346 GO111MODULE 64/pkg/tool/linux_amd64/compile GOINSECURE GOMOD abis 64/pkg/tool/linux_amd64/compile 64/s�� g_.a GO111MODULE 64/pkg/tool/linux_amd64/vet GOINSECURE pproxy GOMODCACHE 64/pkg/tool/linux_amd64/vet (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 /semver/semver.go 64/pkg/tool/linux_amd64/compile GOINSECURE exbyte_wasm.o 64/src/internal/--show-toplevel 64/pkg/tool/linux_amd64/compile env 758231958 QuPWq4ACQ ache/go/1.25.8/x64/pkg/tool/linux_amd64/asm GOINSECURE erutil GOMODCACHE ache/go/1.25.8/x64/pkg/tool/linuremote.origin.url (http block)
    • Triggering command: /usr/bin/gh gh run download 2 --dir test-logs/run-2 rotocol/go-sdk@v-nolocalimports 64/pkg/tool/linu-importcfg GOINSECURE GOMOD abis 64/pkg/tool/linu/home/REDACTED/work/gh-aw/gh-aw/scripts/lint_error_messages_test.go env g_.a GO111MODULE ck GOINSECURE go-sdk/auth GOMODCACHE ache/go/1.25.8/x64/pkg/tool/linux_amd64/compile (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 4/apic.go 64/pkg/tool/linux_amd64/compile GOINSECURE GOMOD GOMODCACHE 64/pkg/tool/linux_amd64/compile env g_.a YfB4YDUdE x_amd64/vet GOINSECURE util GOMODCACHE x_amd64/vet (http block)
    • Triggering command: /usr/bin/gh gh run download 3 --dir test-logs/run-3 rotocol/go-sdk@v1.5.0/mcp/client.go 64/pkg/tool/linux_amd64/compile GOINSECURE (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 0/internal/language/compact/compact.go 64/pkg/tool/linux_amd64/compile GOINSECURE GOMOD GOMODCACHE 64/pkg/tool/linux_amd64/compile env 758231958 deRMpwyMD 64/pkg/tool/linux_amd64/compile GOINSECURE GOMOD GOMODCACHE 64/pkg/tool/linux_amd64/compile (http block)
    • Triggering command: /usr/bin/gh gh run download 4 --dir test-logs/run-4 X4Ap2OrxA 64/pkg/tool/linux_amd64/compile GOINSECURE util GOMODCACHE 64/pkg/tool/linux_amd64/compile env 3645584192/.github/workflows GO111MODULE ache/go/1.25.8/x64/pkg/tool/linux_amd64/asm GOINSECURE GOMOD GOMODCACHE ache/go/1.25.8/x64/pkg/tool/linux_amd64/asm (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 verutil.go 64/pkg/tool/linux_amd64/compile GOINSECURE cha8_stub.o 64/src/internal/--show-toplevel 64/pkg/tool/linux_amd64/compile env 758231958 64jHUho52 ache/go/1.25.8/x64/pkg/tool/linux_amd64/compile GOINSECURE age/compact GOMODCACHE ache/go/1.25.8/x64/pkg/tool/linux_amd64/compile (http block)
    • Triggering command: /usr/bin/gh gh run download 5 --dir test-logs/run-5 5.0/internal/doc.go 64/pkg/tool/linux_amd64/compile GOINSECURE b.o 64/src/internal/--show-toplevel 64/pkg/tool/linux_amd64/compile env 3645584192 1Yg0zHCmd ache/go/1.25.8/x64/pkg/tool/linux_amd64/compile GOINSECURE GOMOD GOMODCACHE ache/go/1.25.8/x64/pkg/tool/linu-test.v=true (http block)
  • https://api.github.com/repos/github/gh-aw/actions/workflows
    • Triggering command: /usr/bin/gh gh workflow list --json name,state,path go1.25.8 -c=4 -nolocalimports -importcfg /tmp/go-build1937493502/b415/importcfg -pack /tmp/go-build1937493502/b415/_testmain.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 x_amd64/asm env -json GO111MODULE x_amd64/compile GOINSECURE GOMOD GOMODCACHE x_amd64/compile (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/linuremote1 env g_.a m0O72i2Jk x_amd64/vet GOINSECURE GOMOD GOMODCACHE x_amd64/vet (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 --show-toplevel 64/pkg/tool/linutest@example.com /usr/bin/git t5smDhwOz GO111MODULE ache/go/1.25.8/x--show-toplevel git rev-�� --show-toplevel ache/go/1.25.8/x64/pkg/tool/linu-tests /usr/bin/git 9516280/b059/_pk/bin/sh om/goccy/go-yaml-c ache/go/1.25.8/xgit-upload-pack 'origin' git (http block)
    • Triggering command: /usr/bin/gh gh api /repos/github/gh-aw/git/ref/tags/v0.47.4 --jq .object.sha --show-toplevel git /usr/bin/git --show-toplevel git /usr/bin/git git rev-�� --show-toplevel git /usr/bin/git --show-toplevel git /usr/bin/git 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 edOutput3196856207/001 hOYFQ3cxW 64/pkg/tool/linux_amd64/vet GOINSECURE rm GOMODCACHE 64/pkg/tool/linux_amd64/vet env ortcfg BIoY6_isA ache/go/1.25.8/x64/pkg/tool/linux_amd64/compile GOINSECURE %H %ct %D (http block)
    • Triggering command: /usr/bin/gh gh api /repos/github/gh-aw/git/ref/tags/v1.0.0 --jq .object.sha --show-toplevel git /usr/bin/git GOMODCACHE go /opt/hostedtoolcs/^session\.save_handler=\(.*\)$/\1/p git rev-�� --show-toplevel node /usr/bin/git /tmp/TestHashConbash go /usr/bin/git git (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 -json GO111MODULE x_amd64/compile GOINSECURE GOMOD GOMODCACHE x_amd64/compile env -json GO111MODULE x_amd64/compile GOINSECURE GOMOD GOMODCACHE x_amd64/compile (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 -json GO111MODULE x_amd64/compile GOINSECURE GOMOD GOMODCACHE x_amd64/compile 3126�� -json oken.go x_amd64/compile 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 -json GO111MODULE x_amd64/asm GOINSECURE GOMOD GOMODCACHE x_amd64/asm env -json GO111MODULE x_amd64/compile 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 -json d/cpuid.go x_amd64/compile GOINSECURE GOMOD GOMODCACHE x_amd64/compile 3126�� -json sonschema/annotations.go x_amd64/compile GOINSECURE GOMOD GOMODCACHE x_amd64/compile (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 -json GO111MODULE x_amd64/compile GOINSECURE BoUGNsu/a381HaLIremote GOMODCACHE x_amd64/compile env -json l/errors/error.go x_amd64/compile Action pins syngit GOMOD GOMODCACHE x_amd64/compile (http block)
  • https://api.github.com/repos/githubnext/agentics/git/ref/tags/-
    • Triggering command: /usr/bin/gh gh api /repos/githubnext/agentics/git/ref/tags/- --jq .object.sha -json GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE go env -json GO111MODULE ode_modules/.bin/sh 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 edOutput3196856207/001 h00yucQ7c ntdrain.test GOINSECURE /bidi GOMODCACHE ntdrain.test 9374�� ortcfg JmzP4TwGo ache/go/1.25.8/x64/pkg/tool/linux_amd64/compile GOINSECURE %H %ct %D (http block)
    • Triggering command: /usr/bin/gh gh api /repos/nonexistent/action/git/ref/tags/v999.999.999 --jq .object.sha --show-toplevel git /usr/bin/git runs/20260411-12sed rev-parse /opt/hostedtoolcs/^session\.gc_maxlifetime=\(.*\)$/\1/p git rev-�� /v0.47.4 /opt/hostedtoolcache/go/1.25.8/x64/pkg/tool/linux_amd64/vet /usr/bin/git -bool (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 abis 64/pkg/tool/linux_amd64/vet 64/s�� til.go til_test.go x_amd64/compile GOINSECURE GOMOD GOMODCACHE x_amd64/compile (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 GOINSECURE GOMOD GOMODCACHE 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 -nolocalimports -importcfg /tmp/go-build1937493502/b412/importcfg -pack /tmp/go-build1937493502/b412/_testmain.go env -json GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE go (http block)
  • https://api.github.com/repos/owner/repo/contents/file.md
    • Triggering command: /tmp/go-build1937493502/b397/cli.test /tmp/go-build1937493502/b397/cli.test -test.testlogfile=/tmp/go-build1937493502/b397/testlog.txt -test.paniconexit0 -test.v=true -test.parallel=4 -test.timeout=10m0s -test.run=^Test -test.short=true **/*.json --ignore-path ../../../.pretti/tmp/go-build3429516280/b081/_pkg_.a go env -json GO111MODULE 64/bin/go 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 -json GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE go env -json th_meta.go 64/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:

…-schema generation

Extract the repeated input-schema construction logic from three generator
functions into a shared buildInputSchema helper in build_input_schema.go.

The helper consolidates the JSON Schema property/required generation from
workflow/tool input definitions, including type mapping (string, number,
boolean, choice, environment), choice enum/default handling, and required
field tracking.

Replaces duplicated loops in:
- generateCallWorkflowTool (safe_outputs_call_workflow.go)
- generateDispatchWorkflowTool (safe_outputs_dispatch.go)
- generateDispatchRepositoryTool (dispatch_repository.go)

Adds comprehensive tests for the shared helper covering all type mappings,
default propagation, required tracking, invalid input skipping, and edge
cases.

Fixes #685

Agent-Logs-Url: https://github.com/github/gh-aw/sessions/59ee6590-52dd-4f72-9a51-b2e0ef119be2

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Copilot AI changed the title [WIP] Refactor input-schema generation logic to eliminate duplication Extract shared buildInputSchema helper to deduplicate input-schema generation Apr 11, 2026
Copilot AI requested a review from pelikhan April 11, 2026 12:18
@pelikhan pelikhan marked this pull request as ready for review April 11, 2026 13:47
Copilot AI review requested due to automatic review settings April 11, 2026 13:47
@pelikhan pelikhan merged commit 11b45b8 into main Apr 11, 2026
54 checks passed
@pelikhan pelikhan deleted the copilot/fix-duplicate-code-input-schema branch April 11, 2026 13:47
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 deduplicates GitHub Actions input→MCP inputSchema generation by extracting a shared helper, and adds targeted unit tests to lock in behavior (including consistent choice-default handling).

Changes:

  • Added buildInputSchema(inputs, descriptionFn) helper to generate JSON Schema properties and required from Actions-style input definitions.
  • Replaced three duplicated schema-building loops with calls to the shared helper.
  • Added unit tests covering type mapping, defaults, required tracking, invalid defs, nil/empty inputs, fallback descriptions, and choice edge cases.
Show a summary per file
File Description
pkg/workflow/build_input_schema.go Introduces the shared helper for building properties + required.
pkg/workflow/build_input_schema_test.go Adds unit tests to validate schema-building behavior and edge cases.
pkg/workflow/safe_outputs_dispatch.go Uses the shared helper for workflow_dispatch tool schema generation.
pkg/workflow/safe_outputs_call_workflow.go Uses the shared helper for workflow_call tool schema generation.
pkg/workflow/dispatch_repository.go Uses the shared helper for dispatch_repository tool schema generation.

Copilot's findings

Tip

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

  • Files reviewed: 5/5 changed files
  • Comments generated: 1

Comment on lines +13 to +25
func buildInputSchema(inputs map[string]any, descriptionFn func(inputName string) string) (properties map[string]any, required []string) {
properties = make(map[string]any)
required = []string{}

for inputName, inputDef := range inputs {
inputDefMap, ok := inputDef.(map[string]any)
if !ok {
continue
}

inputType := "string"
inputDescription := descriptionFn(inputName)
inputRequired := false
Copy link

Copilot AI Apr 11, 2026

Choose a reason for hiding this comment

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

buildInputSchema unconditionally calls descriptionFn (line 24). If a future caller passes a nil callback, this will panic with a nil-pointer dereference. Consider handling a nil descriptionFn (e.g., default to an empty-string fallback) or explicitly documenting/panicking with a clearer message.

Copilot uses AI. Check for mistakes.
@github-actions
Copy link
Copy Markdown
Contributor

🧪 Test Quality Sentinel Report

Test Quality Score: 78/100

⚠️ Acceptable, with suggestions

Metric Value
New/modified tests analyzed 15
✅ Design tests (behavioral contracts) 15 (100%)
⚠️ Implementation tests (low value) 0 (0%)
Tests with error/edge cases 9 (60%)
Duplicate test clusters 0
Test inflation detected ✅ YES — 3.33:1 (263 test lines / 79 production lines)
🚨 Coding-guideline violations None

Test Classification Details

📋 All 15 tests (click to expand)
Test File Classification Issues Detected
TestBuildInputSchemaStringType pkg/workflow/build_input_schema_test.go:19 ✅ Design Happy path
TestBuildInputSchemaNumberType pkg/workflow/build_input_schema_test.go:38 ✅ Design Happy path
TestBuildInputSchemaBooleanType pkg/workflow/build_input_schema_test.go:55 ✅ Design Happy path
TestBuildInputSchemaChoiceType pkg/workflow/build_input_schema_test.go:71 ✅ Design Happy path
TestBuildInputSchemaChoiceWithDefault pkg/workflow/build_input_schema_test.go:89 ✅ Design Happy path
TestBuildInputSchemaEnvironmentType pkg/workflow/build_input_schema_test.go:111 ✅ Design Happy path
TestBuildInputSchemaDefaultPropagation pkg/workflow/build_input_schema_test.go:126 ✅ Design Happy path
TestBuildInputSchemaRequiredInputs pkg/workflow/build_input_schema_test.go:143 ✅ Design Edge case: required=false ✅
TestBuildInputSchemaRequiredSorting pkg/workflow/build_input_schema_test.go:159 ✅ Design Edge case: ordering stability ✅
TestBuildInputSchemaSkipsInvalidDefs pkg/workflow/build_input_schema_test.go:175 ✅ Design Edge cases: non-map / nil / scalar inputs ✅
TestBuildInputSchemaEmptyInputs pkg/workflow/build_input_schema_test.go:196 ✅ Design Edge case: empty map ✅
TestBuildInputSchemaNilInputs pkg/workflow/build_input_schema_test.go:203 ✅ Design Edge case: nil ✅
TestBuildInputSchemaFallbackDescription pkg/workflow/build_input_schema_test.go:210 ✅ Design Edge case: missing description ✅
TestBuildInputSchemaChoiceWithoutOptions pkg/workflow/build_input_schema_test.go:229 ✅ Design Edge case: choice with no options ✅
TestBuildInputSchemaUnknownType pkg/workflow/build_input_schema_test.go:249 ✅ Design Edge case: unknown type → string fallback ✅

Suggestions for Improvement

⚠️ Test Inflation (3.33:1 ratio)

build_input_schema_test.go adds 263 lines for 79 lines of production code (ratio: 3.33:1, threshold: 2:1). The score for proportional growth was zeroed out.

The inflation is largely caused by six single-type tests (TestBuildInputSchemaStringType, TestBuildInputSchemaNumberType, TestBuildInputSchemaBooleanType, TestBuildInputSchemaEnvironmentType, TestBuildInputSchemaChoiceType, TestBuildInputSchemaChoiceWithDefault) that each stand alone rather than being combined into one table-driven test. Consider consolidating them:

func TestBuildInputSchemaTypeMapping(t *testing.T) {
    tests := []struct {
        name        string
        inputType   string
        options     []any
        defaultVal  any
        expectType  string
        expectEnum  []any
    }{
        {name: "string", inputType: "string", expectType: "string"},
        {name: "number", inputType: "number", expectType: "number"},
        {name: "boolean", inputType: "boolean", expectType: "boolean"},
        {name: "environment", inputType: "environment", expectType: "string"},
        {name: "choice with options", inputType: "choice", options: []any{"a", "b"}, expectType: "string", expectEnum: []any{"a", "b"}},
        {name: "choice with default", inputType: "choice", options: []any{"a", "b"}, defaultVal: "a", expectType: "string"},
        {name: "unknown type defaults to string", inputType: "mystery", expectType: "string"},
    }
    // ...
}

i️ TestBuildInputSchemaRequiredSorting — Minor Clarity Issue

This test runs buildInputSchema 10 times, calls sort.Strings(required), and then asserts a fixed order. The 10-iteration loop does not meaningfully test for non-determinism (Go map iteration is random, but sort.Strings is always deterministic). The test effectively verifies that all three required fields are captured, which is valuable — but the iteration + sort framing is misleading. Consider renaming it to TestBuildInputSchemaRequiredFields and running it once, using assert.ElementsMatch to avoid depending on order:

assert.ElementsMatch(t, []string{"a_param", "m_param", "z_param"}, required,
    "all required inputs should be collected")

Language Support

Tests analyzed:

  • 🐹 Go (*_test.go): 15 tests — unit (//go:build !integration)
  • 🟨 JavaScript (*.test.cjs, *.test.js): 0 tests

Verdict

Check passed. 0% of new tests are implementation tests (threshold: 30%). All 15 tests enforce observable behavioral contracts of buildInputSchema. Score is capped to 78/100 due to the test inflation ratio (3.33:1 > 2:1 threshold), but this does not trigger a failure.


📖 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.

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

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: 78/100. Test quality is acceptable — 0% of new tests are implementation tests (threshold: 30%). All 15 tests in build_input_schema_test.go verify observable behavioral contracts. Minor suggestion: consolidate the six single-type-mapping tests into a table-driven test to reduce inflation (current ratio 3.33:1).

@github-actions
Copy link
Copy Markdown
Contributor

🏗️ Design Decision Gate — ADR Required

This PR makes significant changes to core business logic (353 new lines in pkg/workflow/, which exceeds the 100-line threshold) but was merged without a linked Architecture Decision Record (ADR).

AI has analyzed the PR diff and generated a draft ADR to document the design decision. Please add it to the repository:

📄 Draft ADR: docs/adr/0002-extract-shared-build-input-schema-helper.md

📋 Draft ADR Content — copy to docs/adr/0002-extract-shared-build-input-schema-helper.md
# ADR-0002: Extract Shared `buildInputSchema` Helper for GitHub Actions to MCP Tool Schema Conversion

**Date**: 2026-04-11
**Status**: Draft
**Deciders**: pelikhan, Copilot

---

## Part 1 — Narrative (Human-Friendly)

### Context

The `pkg/workflow` package contains three tool generators (`generateCallWorkflowTool`, `generateDispatchWorkflowTool`, `generateDispatchRepositoryTool`) that each expose GitHub Actions workflows as MCP tools. Each generator independently implemented a ~60-line loop to map GitHub Actions input definitions (type, description, default, required, options) into JSON Schema properties for the MCP `inputSchema`. This triplication meant bug fixes and type-mapping changes had to be applied at three separate sites. Behavioral drift had already materialized: choice-type default value handling (`prop["default"] = defaultVal`) existed only in `dispatch_repository` but was absent from the other two generators, producing inconsistent schema output across tool types.

### Decision

We will extract the duplicated input-mapping loop into a single shared function `buildInputSchema(inputs map[string]any, descriptionFn func(inputName string) string) (properties map[string]any, required []string)` located in `pkg/workflow/build_input_schema.go`. The function accepts a `descriptionFn` callback because each caller uses different fallback description phrasing when input definitions omit an explicit `description` field. We adopt the most complete implementation (from `dispatch_repository`) as the canonical version, which extends choice-type default propagation to all three generators and eliminates the existing behavioral divergence. Each of the three call sites is replaced with a 3-line call.

### Alternatives Considered

#### Alternative 1: Keep Duplication and Enforce Synchronization via Linting

Add a Go vet rule or developer-convention comments requiring callers to stay in sync with a designated "source of truth" implementation. This approach avoids refactoring risk but adds process overhead without preventing drift at the compiler level — the existing choice-default bug demonstrates that convention-based enforcement is insufficient. Any future type-mapping change would still require three edits with no automated guarantee of consistency.

#### Alternative 2: Test-Based Equivalence Checks

Add cross-generator tests that verify all three generators produce identical JSON Schema output for identical input definitions. This detects divergence after the fact but does not eliminate the maintenance burden; the same three-site edit requirement persists. It also requires test infrastructure to invoke all three generators with identical inputs and compare outputs, which is more complex than a shared helper with direct unit tests.

#### Alternative 3: Struct or Interface-Based Abstraction

Replace the function with a struct type (e.g., `InputSchemaBuilder`) carrying the description callback as a field, or define a `SchemaBuilder` interface. For a function with two inputs and two outputs, this would add boilerplate and indirection with no behavioral benefit. The functional approach is idiomatic Go for a stateless transformation and is easier to test in isolation.

### Consequences

#### Positive
- Single fix point for all GitHub Actions → JSON Schema type-mapping bugs; any future change requires one edit instead of three.
- Consistent behavior across all tool generator types: choice-type inputs with defaults now emit `"default"` in the JSON Schema property in all three contexts.
- 15 dedicated unit tests cover type mapping, default propagation, required tracking, invalid-def skipping, nil/empty inputs, fallback descriptions, and choice edge cases.
- All 187 existing workflow lock files recompile identically, confirming no schema regression for production workflows.

#### Negative
- New callers must supply a `descriptionFn` callback rather than relying on a default phrasing; this is a minor API surface addition compared to a zero-argument function.
- `buildInputSchema` is unexported (lowercase), so it is inaccessible outside the `workflow` package. If other packages later need the same logic, the function must be promoted to exported or moved to a shared package.

#### Neutral
- The normalization of choice-default behavior is a behavioral change for `generateCallWorkflowTool` and `generateDispatchWorkflowTool` — specifically a bug fix that makes them consistent with `generateDispatchRepositoryTool`.
- The function handles nil inputs gracefully (returns empty maps), which is a minor improvement over the previous per-caller behavior.

---

## Part 2 — Normative Specification (RFC 2119)

> The key words **MUST**, **MUST NOT**, **REQUIRED**, **SHALL**, **SHALL NOT**, **SHOULD**, **SHOULD NOT**, **RECOMMENDED**, **MAY**, and **OPTIONAL** in this section are to be interpreted as described in [RFC 2119]((www.rfceditor.org/redacted)

### Input Schema Construction

1. All tool generators in `pkg/workflow` that convert GitHub Actions input definitions to MCP `inputSchema` JSON Schema properties **MUST** use the shared `buildInputSchema` function.
2. Tool generators **MUST NOT** implement their own inline input-mapping loops that duplicate the logic in `buildInputSchema`.
3. Callers **MUST** supply a `descriptionFn` callback that returns a non-empty fallback description string for any input name; the callback **MUST NOT** return an empty string.
4. Callers **MAY** use different description phrasing per generator to reflect the tool type (e.g., including the workflow name in the fallback).

### Type Mapping

1. `buildInputSchema` **MUST** map the GitHub Actions input type `"number"` to the JSON Schema type `"number"`.
2. `buildInputSchema` **MUST** map the GitHub Actions input type `"boolean"` to the JSON Schema type `"boolean"`.
3. `buildInputSchema` **MUST** map the GitHub Actions input type `"choice"` to the JSON Schema type `"string"` with an `"enum"` field containing the `options` array when `options` is non-empty.
4. `buildInputSchema` **MUST** include the `"default"` field in the JSON Schema property for `"choice"` inputs when a default value is present in the input definition.
5. `buildInputSchema` **MUST** map the GitHub Actions input type `"environment"` and all unrecognized types to the JSON Schema type `"string"`.
6. `buildInputSchema` **MUST** include the `"default"` field in the JSON Schema property for all non-choice inputs when a default value is present in the input definition.

### Required Field Tracking

1. `buildInputSchema` **MUST** add an input name to the `required` return slice if and only if the input definition contains `"required": true`.
2. `buildInputSchema` **MUST** skip input definitions that are not of type `map[string]any`; such entries **MUST NOT** appear in the returned `properties` or `required`.
3. `buildInputSchema` **MUST** return an empty (non-nil) `required` slice when no inputs are required.

### Conformance

An implementation is considered conformant with this ADR if it satisfies all **MUST** and **MUST NOT** requirements above. Specifically: all tool generators in `pkg/workflow` invoke `buildInputSchema` for schema construction; no generator contains an inline reimplementation; and the function maps all supported GitHub Actions input types to their correct JSON Schema equivalents with default and required fields propagated consistently. Failure to meet any **MUST** or **MUST NOT** requirement constitutes non-conformance.

---

*This is a DRAFT ADR generated by the [Design Decision Gate](https://github.com/github/gh-aw/actions/runs/24283796640) workflow.*

What to do next

  1. Copy the draft ADR above into docs/adr/0002-extract-shared-build-input-schema-helper.md on the main branch
  2. Review and complete it — add any context the AI couldn't infer, refine the decision rationale, and verify the alternatives are accurate
  3. Change the status from Draft to Accepted once the team has reviewed it

Note: This PR was already merged when the gate ran. ADR enforcement is retroactive here — the ADR should be added to main directly via a follow-up PR or commit.

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 numbered Markdown files (e.g., 0002-extract-shared-build-input-schema-helper.md).

🔒 Generated by Design Decision Gate. This PR was merged before the gate ran — please add the ADR to main via a follow-up commit.

Note

🔒 Integrity filter blocked 1 item

The following item were blocked because they don't meet the GitHub integrity level.

To allow these resources, lower min-integrity in your GitHub frontmatter:

tools:
  github:
    min-integrity: approved  # merged | approved | unapproved | none

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

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.

This PR introduced >100 new lines in pkg/workflow/ without a linked Architecture Decision Record. A draft ADR (docs/adr/0002-extract-shared-build-input-schema-helper.md) has been generated from the diff and posted above. Please add it to main via a follow-up commit. The PR was already merged when the gate ran, so blocking is not possible retroactively.

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.

Duplicate code: input-schema generation logic repeated across workflow dispatch tool builders

3 participants