diff --git a/.github/workflows/design-decision-gate.lock.yml b/.github/workflows/design-decision-gate.lock.yml index 6206dfd842..b8449c0694 100644 --- a/.github/workflows/design-decision-gate.lock.yml +++ b/.github/workflows/design-decision-gate.lock.yml @@ -1,4 +1,4 @@ -# gh-aw-metadata: {"schema_version":"v3","frontmatter_hash":"f239717c54cbcb18f7bf4c8a00a9e2d9a6e46305cd9d8b9560e381be34487bfd","strict":true,"agent_id":"claude"} +# gh-aw-metadata: {"schema_version":"v3","frontmatter_hash":"a3ab4dd7994b65ef3ef668efcb796e26693ca648c473159a167ef609f863e313","strict":true,"agent_id":"claude"} # gh-aw-manifest: {"version":1,"secrets":["ANTHROPIC_API_KEY","GH_AW_CI_TRIGGER_TOKEN","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GITHUB_TOKEN"],"actions":[{"repo":"actions/checkout","sha":"de0fac2e4500dabe0009e67214ff5f5447ce83dd","version":"v6.0.2"},{"repo":"actions/download-artifact","sha":"3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c","version":"v8.0.1"},{"repo":"actions/github-script","sha":"373c709c69115d41ff229c7e5df9f8788daa9553","version":"v9"},{"repo":"actions/setup-node","sha":"53b83947a5a98c8d113130e565377fae1a50d02f","version":"v6.3.0"},{"repo":"actions/upload-artifact","sha":"043fb46d1a93c77aae656e7c1c64a875d1fc6a0a","version":"v7.0.1"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.25.25"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.25.25"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.25.25"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.2.25"},{"image":"ghcr.io/github/github-mcp-server:v1.0.0"},{"image":"node:lts-alpine","digest":"sha256:01743339035a5c3c11a373cd7c83aeab6ed1457b55da6a69e014a95ac4e4700b","pinned_image":"node:lts-alpine@sha256:01743339035a5c3c11a373cd7c83aeab6ed1457b55da6a69e014a95ac4e4700b"}]} # ___ _ _ # / _ \ | | (_) @@ -203,23 +203,23 @@ jobs: run: | bash "${RUNNER_TEMP}/gh-aw/actions/create_prompt_first.sh" { - cat << 'GH_AW_PROMPT_7b71667809e4e0bc_EOF' + cat << 'GH_AW_PROMPT_75c0780fd80a3f10_EOF' - GH_AW_PROMPT_7b71667809e4e0bc_EOF + GH_AW_PROMPT_75c0780fd80a3f10_EOF cat "${RUNNER_TEMP}/gh-aw/prompts/xpia.md" cat "${RUNNER_TEMP}/gh-aw/prompts/temp_folder_prompt.md" cat "${RUNNER_TEMP}/gh-aw/prompts/markdown.md" cat "${RUNNER_TEMP}/gh-aw/prompts/safe_outputs_prompt.md" - cat << 'GH_AW_PROMPT_7b71667809e4e0bc_EOF' + cat << 'GH_AW_PROMPT_75c0780fd80a3f10_EOF' Tools: add_comment(max:2), push_to_pull_request_branch, missing_tool, missing_data, noop - GH_AW_PROMPT_7b71667809e4e0bc_EOF + GH_AW_PROMPT_75c0780fd80a3f10_EOF cat "${RUNNER_TEMP}/gh-aw/prompts/safe_outputs_push_to_pr_branch.md" - cat << 'GH_AW_PROMPT_7b71667809e4e0bc_EOF' + cat << 'GH_AW_PROMPT_75c0780fd80a3f10_EOF' - GH_AW_PROMPT_7b71667809e4e0bc_EOF + GH_AW_PROMPT_75c0780fd80a3f10_EOF cat "${RUNNER_TEMP}/gh-aw/prompts/mcp_cli_tools_prompt.md" - cat << 'GH_AW_PROMPT_7b71667809e4e0bc_EOF' + cat << 'GH_AW_PROMPT_75c0780fd80a3f10_EOF' The following GitHub context information is available for this workflow: {{#if __GH_AW_GITHUB_ACTOR__ }} @@ -248,14 +248,14 @@ jobs: {{/if}} - GH_AW_PROMPT_7b71667809e4e0bc_EOF + GH_AW_PROMPT_75c0780fd80a3f10_EOF cat "${RUNNER_TEMP}/gh-aw/prompts/github_mcp_tools_with_safeoutputs_prompt.md" - cat << 'GH_AW_PROMPT_7b71667809e4e0bc_EOF' + cat << 'GH_AW_PROMPT_75c0780fd80a3f10_EOF' {{#runtime-import .github/agents/adr-writer.agent.md}} {{#runtime-import .github/workflows/shared/reporting.md}} {{#runtime-import .github/workflows/design-decision-gate.md}} - GH_AW_PROMPT_7b71667809e4e0bc_EOF + GH_AW_PROMPT_75c0780fd80a3f10_EOF } > "$GH_AW_PROMPT" - name: Interpolate variables and render templates uses: actions/github-script@373c709c69115d41ff229c7e5df9f8788daa9553 # v9 @@ -402,6 +402,12 @@ jobs: run: bash "${RUNNER_TEMP}/gh-aw/actions/configure_gh_for_ghe.sh" env: GH_TOKEN: ${{ github.token }} + - env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + PR_NUMBER: ${{ github.event.pull_request.number || github.event.inputs.pr_number }} + name: Pre-fetch ADR gate PR context + run: "set -euo pipefail\n\nmkdir -p /tmp/gh-aw/agent\n\ngh pr view \"$PR_NUMBER\" \\\n --repo \"${{ github.repository }}\" \\\n --json number,title,body,labels,baseRefName,headRefName,author,url \\\n > /tmp/gh-aw/agent/pr.json\n\ngh pr diff \"$PR_NUMBER\" \\\n --repo \"${{ github.repository }}\" \\\n > /tmp/gh-aw/agent/pr.diff\n\ngh api --paginate \"repos/${{ github.repository }}/pulls/$PR_NUMBER/files?per_page=100\" \\\n --jq '.[]' | jq -s '.' > /tmp/gh-aw/agent/pr-files.json\n\nif [ -f \"${{ github.workspace }}/.design-gate.yml\" ]; then\n cp \"${{ github.workspace }}/.design-gate.yml\" /tmp/gh-aw/agent/design-gate-config.yml\n HAS_CUSTOM_CONFIG=true\nelse\n echo \"No .design-gate.yml found — using defaults\" > /tmp/gh-aw/agent/design-gate-config.yml\n HAS_CUSTOM_CONFIG=false\nfi\n\nBUSINESS_ADDITIONS_DEFAULT=$(jq '[.[] | select(.filename | test(\"^(src|lib|pkg|internal|app|core|domain|services|api)/\")) | .additions] | add // 0' /tmp/gh-aw/agent/pr-files.json)\nHAS_IMPLEMENTATION_LABEL=$(jq '[.labels[]?.name] | index(\"implementation\") != null' /tmp/gh-aw/agent/pr.json)\n\njq -n \\\n --argjson default_business_additions \"$BUSINESS_ADDITIONS_DEFAULT\" \\\n --argjson has_implementation_label \"$HAS_IMPLEMENTATION_LABEL\" \\\n --argjson has_custom_config \"$HAS_CUSTOM_CONFIG\" \\\n --arg pr_number \"$PR_NUMBER\" \\\n --arg threshold \"100\" \\\n '{\n pr_number: ($pr_number | tonumber),\n threshold: ($threshold | tonumber),\n has_custom_config: $has_custom_config,\n has_implementation_label: $has_implementation_label,\n default_business_additions: $default_business_additions,\n requires_adr_by_default_volume: ($default_business_additions > ($threshold | tonumber))\n }' > /tmp/gh-aw/agent/adr-prefetch-summary.json\n" + - name: Configure Git credentials env: REPO_NAME: ${{ github.repository }} @@ -455,9 +461,9 @@ jobs: mkdir -p "${RUNNER_TEMP}/gh-aw/safeoutputs" mkdir -p /tmp/gh-aw/safeoutputs mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs - cat > "${RUNNER_TEMP}/gh-aw/safeoutputs/config.json" << 'GH_AW_SAFE_OUTPUTS_CONFIG_a69af6efb55b93f1_EOF' + cat > "${RUNNER_TEMP}/gh-aw/safeoutputs/config.json" << 'GH_AW_SAFE_OUTPUTS_CONFIG_d2fb339d47be8824_EOF' {"add_comment":{"hide_older_comments":true,"max":2},"create_report_incomplete_issue":{},"missing_data":{},"missing_tool":{},"noop":{"max":1,"report-as-issue":"true"},"push_to_pull_request_branch":{"allowed_files":["docs/adr/**"],"commit_title_suffix":" [design-decision-gate]","if_no_changes":"warn","ignore_missing_branch_failure":true,"max_patch_size":1024,"patch_format":"bundle","protected_files":["package.json","bun.lockb","bunfig.toml","deno.json","deno.jsonc","deno.lock","global.json","NuGet.Config","Directory.Packages.props","mix.exs","mix.lock","go.mod","go.sum","stack.yaml","stack.yaml.lock","pom.xml","build.gradle","build.gradle.kts","settings.gradle","settings.gradle.kts","gradle.properties","package-lock.json","yarn.lock","pnpm-lock.yaml","npm-shrinkwrap.json","requirements.txt","Pipfile","Pipfile.lock","pyproject.toml","setup.py","setup.cfg","Gemfile","Gemfile.lock","uv.lock","CODEOWNERS","CLAUDE.md","AGENTS.md"],"protected_path_prefixes":[".github/",".agents/",".claude/"]},"report_incomplete":{}} - GH_AW_SAFE_OUTPUTS_CONFIG_a69af6efb55b93f1_EOF + GH_AW_SAFE_OUTPUTS_CONFIG_d2fb339d47be8824_EOF - name: Write Safe Outputs Tools env: GH_AW_TOOLS_META_JSON: | @@ -664,7 +670,7 @@ jobs: export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host --add-host host.docker.internal:127.0.0.1 --user '"${MCP_GATEWAY_UID}"':'"${MCP_GATEWAY_GID}"' --group-add '"${DOCKER_SOCK_GID}"' -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.2.25' GH_AW_NODE=$(which node 2>/dev/null || command -v node 2>/dev/null || echo node) - cat << GH_AW_MCP_CONFIG_58ce56625cad4b25_EOF | "$GH_AW_NODE" "${RUNNER_TEMP}/gh-aw/actions/start_mcp_gateway.cjs" + cat << GH_AW_MCP_CONFIG_130fcd198989fbc7_EOF | "$GH_AW_NODE" "${RUNNER_TEMP}/gh-aw/actions/start_mcp_gateway.cjs" { "mcpServers": { "github": { @@ -704,7 +710,7 @@ jobs: "payloadDir": "${MCP_GATEWAY_PAYLOAD_DIR}" } } - GH_AW_MCP_CONFIG_58ce56625cad4b25_EOF + GH_AW_MCP_CONFIG_130fcd198989fbc7_EOF - name: Mount MCP servers as CLIs id: mount-mcp-clis continue-on-error: true @@ -839,7 +845,7 @@ jobs: (umask 177 && touch /tmp/gh-aw/agent-stdio.log) # shellcheck disable=SC1003 sudo -E awf --container-workdir "${GITHUB_WORKSPACE}" --mount "${RUNNER_TEMP}/gh-aw:${RUNNER_TEMP}/gh-aw:ro" --mount "${RUNNER_TEMP}/gh-aw:/host${RUNNER_TEMP}/gh-aw:ro" --tty --env-all --exclude-env ANTHROPIC_API_KEY --exclude-env GITHUB_MCP_SERVER_TOKEN --exclude-env MCP_GATEWAY_API_KEY --allow-domains '*.githubusercontent.com,anthropic.com,api.anthropic.com,api.github.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,cdn.playwright.dev,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,playwright.download.prss.microsoft.com,ppa.launchpad.net,pypi.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,statsig.anthropic.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.googleapis.com' --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --audit-dir /tmp/gh-aw/sandbox/firewall/audit --enable-host-access --allow-host-ports 80,443,8080 --image-tag 0.25.25 --skip-pull --enable-api-proxy \ - -- /bin/bash -c 'export PATH="${RUNNER_TEMP}/gh-aw/mcp-cli/bin:$PATH" && export PATH="$(find /opt/hostedtoolcache -maxdepth 4 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH"; [ -n "$GOROOT" ] && export PATH="$GOROOT/bin:$PATH" || true && claude --print --no-chrome --mcp-config "${{ runner.temp }}/gh-aw/mcp-config/mcp-servers.json" --allowed-tools '\''Bash(cat),Bash(cat:*),Bash(date),Bash(echo),Bash(echo:*),Bash(find:*),Bash(git add:*),Bash(git branch:*),Bash(git checkout:*),Bash(git commit:*),Bash(git diff:*),Bash(git log:*),Bash(git merge:*),Bash(git rm:*),Bash(git show:*),Bash(git status),Bash(git switch:*),Bash(grep),Bash(grep:*),Bash(head),Bash(ls),Bash(ls:*),Bash(pwd),Bash(safeoutputs:*),Bash(sort),Bash(tail),Bash(uniq),Bash(wc),Bash(wc:*),Bash(yq),BashOutput,Edit,ExitPlanMode,Glob,Grep,KillBash,LS,MultiEdit,NotebookEdit,NotebookRead,Read,Task,TodoWrite,Write,mcp__github__download_workflow_run_artifact,mcp__github__get_code_scanning_alert,mcp__github__get_commit,mcp__github__get_dependabot_alert,mcp__github__get_discussion,mcp__github__get_discussion_comments,mcp__github__get_file_contents,mcp__github__get_job_logs,mcp__github__get_label,mcp__github__get_latest_release,mcp__github__get_me,mcp__github__get_notification_details,mcp__github__get_pull_request,mcp__github__get_pull_request_comments,mcp__github__get_pull_request_diff,mcp__github__get_pull_request_files,mcp__github__get_pull_request_review_comments,mcp__github__get_pull_request_reviews,mcp__github__get_pull_request_status,mcp__github__get_release_by_tag,mcp__github__get_secret_scanning_alert,mcp__github__get_tag,mcp__github__get_workflow_run,mcp__github__get_workflow_run_logs,mcp__github__get_workflow_run_usage,mcp__github__issue_read,mcp__github__list_branches,mcp__github__list_code_scanning_alerts,mcp__github__list_commits,mcp__github__list_dependabot_alerts,mcp__github__list_discussion_categories,mcp__github__list_discussions,mcp__github__list_issue_types,mcp__github__list_issues,mcp__github__list_label,mcp__github__list_notifications,mcp__github__list_pull_requests,mcp__github__list_releases,mcp__github__list_secret_scanning_alerts,mcp__github__list_starred_repositories,mcp__github__list_tags,mcp__github__list_workflow_jobs,mcp__github__list_workflow_run_artifacts,mcp__github__list_workflow_runs,mcp__github__list_workflows,mcp__github__pull_request_read,mcp__github__search_code,mcp__github__search_issues,mcp__github__search_orgs,mcp__github__search_pull_requests,mcp__github__search_repositories,mcp__github__search_users'\'' --debug-file /tmp/gh-aw/agent-stdio.log --verbose --permission-mode bypassPermissions --output-format stream-json "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"${GH_AW_MODEL_AGENT_CLAUDE:+ --model "$GH_AW_MODEL_AGENT_CLAUDE"}' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log + -- /bin/bash -c 'export PATH="${RUNNER_TEMP}/gh-aw/mcp-cli/bin:$PATH" && export PATH="$(find /opt/hostedtoolcache -maxdepth 4 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH"; [ -n "$GOROOT" ] && export PATH="$GOROOT/bin:$PATH" || true && claude --print --no-chrome --max-turns 5 --mcp-config "${{ runner.temp }}/gh-aw/mcp-config/mcp-servers.json" --allowed-tools '\''Bash(cat),Bash(cat:*),Bash(date),Bash(echo),Bash(echo:*),Bash(find:*),Bash(git add:*),Bash(git branch:*),Bash(git checkout:*),Bash(git commit:*),Bash(git diff:*),Bash(git log:*),Bash(git merge:*),Bash(git rm:*),Bash(git show:*),Bash(git status),Bash(git switch:*),Bash(grep),Bash(grep:*),Bash(head),Bash(ls),Bash(ls:*),Bash(pwd),Bash(safeoutputs:*),Bash(sort),Bash(tail),Bash(uniq),Bash(wc),Bash(wc:*),Bash(yq),BashOutput,Edit,ExitPlanMode,Glob,Grep,KillBash,LS,MultiEdit,NotebookEdit,NotebookRead,Read,Task,TodoWrite,Write,mcp__github__download_workflow_run_artifact,mcp__github__get_code_scanning_alert,mcp__github__get_commit,mcp__github__get_dependabot_alert,mcp__github__get_discussion,mcp__github__get_discussion_comments,mcp__github__get_file_contents,mcp__github__get_job_logs,mcp__github__get_label,mcp__github__get_latest_release,mcp__github__get_me,mcp__github__get_notification_details,mcp__github__get_pull_request,mcp__github__get_pull_request_comments,mcp__github__get_pull_request_diff,mcp__github__get_pull_request_files,mcp__github__get_pull_request_review_comments,mcp__github__get_pull_request_reviews,mcp__github__get_pull_request_status,mcp__github__get_release_by_tag,mcp__github__get_secret_scanning_alert,mcp__github__get_tag,mcp__github__get_workflow_run,mcp__github__get_workflow_run_logs,mcp__github__get_workflow_run_usage,mcp__github__issue_read,mcp__github__list_branches,mcp__github__list_code_scanning_alerts,mcp__github__list_commits,mcp__github__list_dependabot_alerts,mcp__github__list_discussion_categories,mcp__github__list_discussions,mcp__github__list_issue_types,mcp__github__list_issues,mcp__github__list_label,mcp__github__list_notifications,mcp__github__list_pull_requests,mcp__github__list_releases,mcp__github__list_secret_scanning_alerts,mcp__github__list_starred_repositories,mcp__github__list_tags,mcp__github__list_workflow_jobs,mcp__github__list_workflow_run_artifacts,mcp__github__list_workflow_runs,mcp__github__list_workflows,mcp__github__pull_request_read,mcp__github__search_code,mcp__github__search_issues,mcp__github__search_orgs,mcp__github__search_pull_requests,mcp__github__search_repositories,mcp__github__search_users'\'' --debug-file /tmp/gh-aw/agent-stdio.log --verbose --permission-mode bypassPermissions --output-format stream-json "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"${GH_AW_MODEL_AGENT_CLAUDE:+ --model "$GH_AW_MODEL_AGENT_CLAUDE"}' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log env: ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} BASH_DEFAULT_TIMEOUT_MS: 60000 @@ -847,6 +853,7 @@ jobs: DISABLE_BUG_COMMAND: 1 DISABLE_ERROR_REPORTING: 1 DISABLE_TELEMETRY: 1 + GH_AW_MAX_TURNS: 5 GH_AW_MCP_CONFIG: ${{ runner.temp }}/gh-aw/mcp-config/mcp-servers.json GH_AW_MODEL_AGENT_CLAUDE: ${{ vars.GH_AW_MODEL_AGENT_CLAUDE || '' }} GH_AW_PHASE: agent diff --git a/.github/workflows/design-decision-gate.md b/.github/workflows/design-decision-gate.md index 74c6abcf10..748b2bf935 100644 --- a/.github/workflows/design-decision-gate.md +++ b/.github/workflows/design-decision-gate.md @@ -13,7 +13,9 @@ permissions: contents: read pull-requests: read issues: read -engine: claude +engine: + id: claude + max-turns: 5 safe-outputs: add-comment: max: 2 @@ -49,6 +51,53 @@ tools: - "wc:*" - "find:*" - "echo:*" +steps: + - name: Pre-fetch ADR gate PR context + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + PR_NUMBER: ${{ github.event.pull_request.number || github.event.inputs.pr_number }} + run: | + set -euo pipefail + + mkdir -p /tmp/gh-aw/agent + + gh pr view "$PR_NUMBER" \ + --repo "${{ github.repository }}" \ + --json number,title,body,labels,baseRefName,headRefName,author,url \ + > /tmp/gh-aw/agent/pr.json + + gh pr diff "$PR_NUMBER" \ + --repo "${{ github.repository }}" \ + > /tmp/gh-aw/agent/pr.diff + + gh api --paginate "repos/${{ github.repository }}/pulls/$PR_NUMBER/files?per_page=100" \ + --jq '.[]' | jq -s '.' > /tmp/gh-aw/agent/pr-files.json + + if [ -f "${{ github.workspace }}/.design-gate.yml" ]; then + cp "${{ github.workspace }}/.design-gate.yml" /tmp/gh-aw/agent/design-gate-config.yml + HAS_CUSTOM_CONFIG=true + else + echo "No .design-gate.yml found — using defaults" > /tmp/gh-aw/agent/design-gate-config.yml + HAS_CUSTOM_CONFIG=false + fi + + BUSINESS_ADDITIONS_DEFAULT=$(jq '[.[] | select(.filename | test("^(src|lib|pkg|internal|app|core|domain|services|api)/")) | .additions] | add // 0' /tmp/gh-aw/agent/pr-files.json) + HAS_IMPLEMENTATION_LABEL=$(jq '[.labels[]?.name] | index("implementation") != null' /tmp/gh-aw/agent/pr.json) + + jq -n \ + --argjson default_business_additions "$BUSINESS_ADDITIONS_DEFAULT" \ + --argjson has_implementation_label "$HAS_IMPLEMENTATION_LABEL" \ + --argjson has_custom_config "$HAS_CUSTOM_CONFIG" \ + --arg pr_number "$PR_NUMBER" \ + --arg threshold "100" \ + '{ + pr_number: ($pr_number | tonumber), + threshold: ($threshold | tonumber), + has_custom_config: $has_custom_config, + has_implementation_label: $has_implementation_label, + default_business_additions: $default_business_additions, + requires_adr_by_default_volume: ($default_business_additions > ($threshold | tonumber)) + }' > /tmp/gh-aw/agent/adr-prefetch-summary.json features: mcp-cli: true --- @@ -57,29 +106,49 @@ features: You are the Design Decision Gate, an AI agent that enforces a culture of "decide explicitly before you build." Your mission is to ensure that significant implementation work in pull requests is backed by an Architecture Decision Record (ADR) before the PR can merge. -## Current Context +## Current Context and Operating Constraints - **Repository**: ${{ github.repository }} - **Pull Request**: #${{ github.event.pull_request.number || github.event.inputs.pr_number }} - **Event**: ${{ github.event_name }} - **Actor**: ${{ github.actor }} -- **Label Added**: (check PR labels via GitHub tools) +- **Hard Turn Budget**: 5 turns maximum (stop early when done) + +### Mandatory Efficiency Rules + +1. Start with pre-fetched files in `/tmp/gh-aw/agent/` before calling any GitHub tool: + - `pr.json` + - `pr-files.json` + - `pr.diff` + - `design-gate-config.yml` + - `adr-prefetch-summary.json` +2. Do **not** perform broad exploration. Only fetch extra data if a required field is missing from pre-fetched files. +3. Call exactly one final safe output action (`add-comment`, `push-to-pull-request-branch`, or `noop`) and then stop. +4. If you have enough evidence to decide, stop immediately. Do not gather optional data. ## Step 1: Determine if This PR Requires an ADR -First, decide whether this PR needs ADR enforcement. There are two trigger conditions: +Read the pre-fetched summary first: + +```bash +cat /tmp/gh-aw/agent/adr-prefetch-summary.json +``` + +Decide if this PR needs ADR enforcement using the following deterministic checks: ### Condition A: "implementation" Label -If the event is `labeled` (`${{ github.event_name }} == 'pull_request'` and the PR now has the "implementation" label), enforcement is **always required** — proceed to Step 2. You can verify the label is present by fetching the PR's current labels using GitHub tools. +If `has_implementation_label` is `true`, enforcement is **required** — proceed to Step 2. ### Condition B: Code Volume in Business Logic Directories -If the PR was opened or synchronized (not labeled), you must check if >100 lines of new code exist in core business logic directories. +If `has_custom_config` is `false` and `default_business_additions` is `> 100`, enforcement is **required** — proceed to Step 2. -**Load configuration** (if it exists): +Configuration snapshot is pre-fetched: ```bash -cat ${{ github.workspace }}/.design-gate.yml 2>/dev/null || echo "No .design-gate.yml found — using defaults" +cat /tmp/gh-aw/agent/design-gate-config.yml ``` +If `has_custom_config` is `true` and the config defines custom business directories or thresholds, recompute Condition B from `pr-files.json` using that config before deciding. Do not use `default_business_additions` for the final decision in that case. + Default business logic directories (used when `.design-gate.yml` is absent): - `src/` - `lib/` @@ -91,7 +160,7 @@ Default business logic directories (used when `.design-gate.yml` is absent): - `services/` - `api/` -Use the GitHub tools to get the PR files and count additions in business logic directories. If the total new lines of code in those directories is **≤ 100**, this PR does not need ADR enforcement. +If neither condition is true, this PR does not need ADR enforcement. In that case, call `noop`: @@ -99,17 +168,19 @@ In that case, call `noop`: {"noop": {"message": "No ADR enforcement needed: PR does not have the 'implementation' label and has ≤100 new lines of code in business logic directories."}} ``` -If **> 100 lines** of new code exist in business logic directories, continue to Step 2. +If ADR enforcement is required by either condition, continue to Step 2. ## Step 2: Fetch Pull Request Details -Use the GitHub tools to gather comprehensive PR information: +Use pre-fetched files first: -1. **Get the pull request** — title, body, author, base branch, labels -2. **Get the list of changed files** — file paths and line counts -3. **Get the PR diff** — to understand what design decisions the code is making +```bash +cat /tmp/gh-aw/agent/pr.json +cat /tmp/gh-aw/agent/pr-files.json +cat /tmp/gh-aw/agent/pr.diff +``` -Note the PR number: `${{ github.event.pull_request.number || github.event.inputs.pr_number }}` +Only if one of these files is missing required fields, make a targeted GitHub tool call for the missing field only. ## Step 3: Check for an Existing ADR @@ -158,11 +229,16 @@ Format the number with zero-padding to 4 digits (e.g., PR #42 becomes `0042`, PR ### Analyze the PR Diff and Generate a Draft ADR -Carefully read the PR diff and PR description. Identify: -- What **architectural or design decisions** is this code implicitly making? -- What **patterns, structures, or approaches** is it introducing? -- What **alternatives** could have been chosen instead? -- What **consequences** (positive and negative) does this decision carry? +Use this scoped question template before writing the ADR. Answer each item in 1–3 concise bullets: + +1. **Decision**: What single architectural decision is this PR making? +2. **Driver**: What concrete constraint or problem in this PR necessitates that decision? +3. **Alternatives**: What are the top 2 realistic alternatives visible from this diff? +4. **Consequences**: What are 2 positive and 2 negative consequences of the chosen decision? + +If any answer cannot be justified from `pr.json` + `pr-files.json` + `pr.diff`, state "Not inferable from current PR evidence" instead of speculating. + +If Question 1 (Decision) is not inferable from current PR evidence, call `missing_data` with a concise explanation of what is missing, then stop. Generate a draft ADR file following the **Michael Nygard template**: