From a3ee7cf578196b4837a732dcca759ee51d5b6fd0 Mon Sep 17 00:00:00 2001 From: Tomas Grosup Date: Tue, 5 May 2026 20:20:46 +0200 Subject: [PATCH 1/4] Add agentic state machine diagram generator workflow MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Weekly workflow that reads all .md agentic workflows in the repo, extracts triggers, labels, safe-outputs, handovers, and conditions, then renders a Mermaid diagram + tables in docs/state-machine.md. Outputs: - Summary table (workflow / trigger / reads / writes / key labels) - Mermaid stateDiagram showing full issue + PR lifecycle - Label dictionary (who applies, who reads, what it means) - Handover map (workflow A → workflow B, mechanism) Opens a PR to update the diagram. allowed-files: docs/** only. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../workflows/agentic-state-machine.lock.yml | 1224 +++++++++++++++++ .github/workflows/agentic-state-machine.md | 108 ++ 2 files changed, 1332 insertions(+) create mode 100644 .github/workflows/agentic-state-machine.lock.yml create mode 100644 .github/workflows/agentic-state-machine.md diff --git a/.github/workflows/agentic-state-machine.lock.yml b/.github/workflows/agentic-state-machine.lock.yml new file mode 100644 index 0000000000..6f6712c163 --- /dev/null +++ b/.github/workflows/agentic-state-machine.lock.yml @@ -0,0 +1,1224 @@ +# gh-aw-metadata: {"schema_version":"v3","frontmatter_hash":"fabdec898197657b7dfbf888255c19432c2a915b655d09a8cc01603baee4cf3b","compiler_version":"v0.68.3","strict":true,"agent_id":"copilot"} +# gh-aw-manifest: {"version":1,"secrets":["COPILOT_GITHUB_TOKEN","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/upload-artifact","sha":"043fb46d1a93c77aae656e7c1c64a875d1fc6a0a","version":"v7.0.1"},{"repo":"github/gh-aw-actions/setup","sha":"ba90f2186d7ad780ec640f364005fa24e797b360","version":"v0.68.3"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.25.20"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.25.20"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.25.20"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.2.19"},{"image":"ghcr.io/github/github-mcp-server:v0.32.0"},{"image":"node:lts-alpine"}]} +# ___ _ _ +# / _ \ | | (_) +# | |_| | __ _ ___ _ __ | |_ _ ___ +# | _ |/ _` |/ _ \ '_ \| __| |/ __| +# | | | | (_| | __/ | | | |_| | (__ +# \_| |_/\__, |\___|_| |_|\__|_|\___| +# __/ | +# _ _ |___/ +# | | | | / _| | +# | | | | ___ _ __ _ __| |_| | _____ ____ +# | |/\| |/ _ \ '__| |/ /| _| |/ _ \ \ /\ / / ___| +# \ /\ / (_) | | | | ( | | | | (_) \ V V /\__ \ +# \/ \/ \___/|_| |_|\_\|_| |_|\___/ \_/\_/ |___/ +# +# This file was automatically generated by gh-aw (v0.68.3). DO NOT EDIT. +# +# To update this file, edit the corresponding .md file and run: +# gh aw compile +# Not all edits will cause changes to this file. +# +# For more information: https://github.github.com/gh-aw/introduction/overview/ +# +# Reads all agentic workflow .md files in this repo, extracts the +# state machine (triggers, labels, actions, handovers between workflows), +# and renders a human-readable diagram in .github/workflows/docs/state-machine.md. +# Runs weekly. Updates the diagram via PR. +# +# Secrets used: +# - COPILOT_GITHUB_TOKEN +# - GH_AW_CI_TRIGGER_TOKEN +# - GH_AW_GITHUB_MCP_SERVER_TOKEN +# - GH_AW_GITHUB_TOKEN +# - GITHUB_TOKEN +# +# Custom actions used: +# - actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 +# - actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 +# - actions/github-script@373c709c69115d41ff229c7e5df9f8788daa9553 # v9 +# - actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 +# - github/gh-aw-actions/setup@ba90f2186d7ad780ec640f364005fa24e797b360 # v0.68.3 +# +# Container images used: +# - ghcr.io/github/gh-aw-firewall/agent:0.25.20 +# - ghcr.io/github/gh-aw-firewall/api-proxy:0.25.20 +# - ghcr.io/github/gh-aw-firewall/squid:0.25.20 +# - ghcr.io/github/gh-aw-mcpg:v0.2.19 +# - ghcr.io/github/github-mcp-server:v0.32.0 +# - node:lts-alpine + +name: "Agentic State Machine — Diagram Generator" +"on": + schedule: + - cron: "0 0 */7 * *" + # Friendly format: every 7d + workflow_dispatch: + inputs: + aw_context: + default: "" + description: Agent caller context (used internally by Agentic Workflows). + required: false + type: string + +permissions: {} + +concurrency: + group: "gh-aw-${{ github.workflow }}" + +run-name: "Agentic State Machine — Diagram Generator" + +jobs: + activation: + runs-on: ubuntu-slim + permissions: + actions: read + contents: read + outputs: + comment_id: "" + comment_repo: "" + lockdown_check_failed: ${{ steps.generate_aw_info.outputs.lockdown_check_failed == 'true' }} + model: ${{ steps.generate_aw_info.outputs.model }} + secret_verification_result: ${{ steps.validate-secret.outputs.verification_result }} + setup-trace-id: ${{ steps.setup.outputs.trace-id }} + stale_lock_file_failed: ${{ steps.check-lock-file.outputs.stale_lock_file_failed == 'true' }} + steps: + - name: Setup Scripts + id: setup + uses: github/gh-aw-actions/setup@ba90f2186d7ad780ec640f364005fa24e797b360 # v0.68.3 + with: + destination: ${{ runner.temp }}/gh-aw/actions + job-name: ${{ github.job }} + - name: Generate agentic run info + id: generate_aw_info + env: + GH_AW_INFO_ENGINE_ID: "copilot" + GH_AW_INFO_ENGINE_NAME: "GitHub Copilot CLI" + GH_AW_INFO_MODEL: ${{ vars.GH_AW_MODEL_AGENT_COPILOT || 'auto' }} + GH_AW_INFO_VERSION: "1.0.21" + GH_AW_INFO_AGENT_VERSION: "1.0.21" + GH_AW_INFO_CLI_VERSION: "v0.68.3" + GH_AW_INFO_WORKFLOW_NAME: "Agentic State Machine — Diagram Generator" + GH_AW_INFO_EXPERIMENTAL: "false" + GH_AW_INFO_SUPPORTS_TOOLS_ALLOWLIST: "true" + GH_AW_INFO_STAGED: "false" + GH_AW_INFO_ALLOWED_DOMAINS: '["defaults","github"]' + GH_AW_INFO_FIREWALL_ENABLED: "true" + GH_AW_INFO_AWF_VERSION: "v0.25.20" + GH_AW_INFO_AWMG_VERSION: "" + GH_AW_INFO_FIREWALL_TYPE: "squid" + GH_AW_COMPILED_STRICT: "true" + uses: actions/github-script@373c709c69115d41ff229c7e5df9f8788daa9553 # v9 + with: + script: | + const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io, getOctokit); + const { main } = require('${{ runner.temp }}/gh-aw/actions/generate_aw_info.cjs'); + await main(core, context); + - name: Validate COPILOT_GITHUB_TOKEN secret + id: validate-secret + run: bash "${RUNNER_TEMP}/gh-aw/actions/validate_multi_secret.sh" COPILOT_GITHUB_TOKEN 'GitHub Copilot CLI' https://github.github.com/gh-aw/reference/engines/#github-copilot-default + env: + COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }} + - name: Checkout .github and .agents folders + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false + sparse-checkout: | + .github + .agents + sparse-checkout-cone-mode: true + fetch-depth: 1 + - name: Check workflow lock file + id: check-lock-file + uses: actions/github-script@373c709c69115d41ff229c7e5df9f8788daa9553 # v9 + env: + GH_AW_WORKFLOW_FILE: "agentic-state-machine.lock.yml" + GH_AW_CONTEXT_WORKFLOW_REF: "${{ github.workflow_ref }}" + with: + script: | + const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io, getOctokit); + const { main } = require('${{ runner.temp }}/gh-aw/actions/check_workflow_timestamp_api.cjs'); + await main(); + - name: Check compile-agentic version + uses: actions/github-script@373c709c69115d41ff229c7e5df9f8788daa9553 # v9 + env: + GH_AW_COMPILED_VERSION: "v0.68.3" + with: + script: | + const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io, getOctokit); + const { main } = require('${{ runner.temp }}/gh-aw/actions/check_version_updates.cjs'); + await main(); + - name: Create prompt with built-in context + env: + GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt + GH_AW_SAFE_OUTPUTS: ${{ runner.temp }}/gh-aw/safeoutputs/outputs.jsonl + GH_AW_GITHUB_ACTOR: ${{ github.actor }} + GH_AW_GITHUB_EVENT_COMMENT_ID: ${{ github.event.comment.id }} + GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER: ${{ github.event.discussion.number }} + GH_AW_GITHUB_EVENT_ISSUE_NUMBER: ${{ github.event.issue.number }} + GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER: ${{ github.event.pull_request.number }} + GH_AW_GITHUB_REPOSITORY: ${{ github.repository }} + GH_AW_GITHUB_RUN_ID: ${{ github.run_id }} + GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }} + # poutine:ignore untrusted_checkout_exec + run: | + bash "${RUNNER_TEMP}/gh-aw/actions/create_prompt_first.sh" + { + cat << 'GH_AW_PROMPT_449d6f65728fd8bf_EOF' + + GH_AW_PROMPT_449d6f65728fd8bf_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_449d6f65728fd8bf_EOF' + + Tools: create_pull_request, missing_tool, missing_data, noop + GH_AW_PROMPT_449d6f65728fd8bf_EOF + cat "${RUNNER_TEMP}/gh-aw/prompts/safe_outputs_create_pull_request.md" + cat << 'GH_AW_PROMPT_449d6f65728fd8bf_EOF' + + + The following GitHub context information is available for this workflow: + {{#if __GH_AW_GITHUB_ACTOR__ }} + - **actor**: __GH_AW_GITHUB_ACTOR__ + {{/if}} + {{#if __GH_AW_GITHUB_REPOSITORY__ }} + - **repository**: __GH_AW_GITHUB_REPOSITORY__ + {{/if}} + {{#if __GH_AW_GITHUB_WORKSPACE__ }} + - **workspace**: __GH_AW_GITHUB_WORKSPACE__ + {{/if}} + {{#if __GH_AW_GITHUB_EVENT_ISSUE_NUMBER__ }} + - **issue-number**: #__GH_AW_GITHUB_EVENT_ISSUE_NUMBER__ + {{/if}} + {{#if __GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER__ }} + - **discussion-number**: #__GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER__ + {{/if}} + {{#if __GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER__ }} + - **pull-request-number**: #__GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER__ + {{/if}} + {{#if __GH_AW_GITHUB_EVENT_COMMENT_ID__ }} + - **comment-id**: __GH_AW_GITHUB_EVENT_COMMENT_ID__ + {{/if}} + {{#if __GH_AW_GITHUB_RUN_ID__ }} + - **workflow-run-id**: __GH_AW_GITHUB_RUN_ID__ + {{/if}} + + + GH_AW_PROMPT_449d6f65728fd8bf_EOF + cat "${RUNNER_TEMP}/gh-aw/prompts/github_mcp_tools_with_safeoutputs_prompt.md" + cat << 'GH_AW_PROMPT_449d6f65728fd8bf_EOF' + + {{#runtime-import .github/workflows/agentic-state-machine.md}} + GH_AW_PROMPT_449d6f65728fd8bf_EOF + } > "$GH_AW_PROMPT" + - name: Interpolate variables and render templates + uses: actions/github-script@373c709c69115d41ff229c7e5df9f8788daa9553 # v9 + env: + GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt + with: + script: | + const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io, getOctokit); + const { main } = require('${{ runner.temp }}/gh-aw/actions/interpolate_prompt.cjs'); + await main(); + - name: Substitute placeholders + uses: actions/github-script@373c709c69115d41ff229c7e5df9f8788daa9553 # v9 + env: + GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt + GH_AW_GITHUB_ACTOR: ${{ github.actor }} + GH_AW_GITHUB_EVENT_COMMENT_ID: ${{ github.event.comment.id }} + GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER: ${{ github.event.discussion.number }} + GH_AW_GITHUB_EVENT_ISSUE_NUMBER: ${{ github.event.issue.number }} + GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER: ${{ github.event.pull_request.number }} + GH_AW_GITHUB_REPOSITORY: ${{ github.repository }} + GH_AW_GITHUB_RUN_ID: ${{ github.run_id }} + GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }} + with: + script: | + const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io, getOctokit); + + const substitutePlaceholders = require('${{ runner.temp }}/gh-aw/actions/substitute_placeholders.cjs'); + + // Call the substitution function + return await substitutePlaceholders({ + file: process.env.GH_AW_PROMPT, + substitutions: { + GH_AW_GITHUB_ACTOR: process.env.GH_AW_GITHUB_ACTOR, + GH_AW_GITHUB_EVENT_COMMENT_ID: process.env.GH_AW_GITHUB_EVENT_COMMENT_ID, + GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER: process.env.GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER, + GH_AW_GITHUB_EVENT_ISSUE_NUMBER: process.env.GH_AW_GITHUB_EVENT_ISSUE_NUMBER, + GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER: process.env.GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER, + GH_AW_GITHUB_REPOSITORY: process.env.GH_AW_GITHUB_REPOSITORY, + GH_AW_GITHUB_RUN_ID: process.env.GH_AW_GITHUB_RUN_ID, + GH_AW_GITHUB_WORKSPACE: process.env.GH_AW_GITHUB_WORKSPACE + } + }); + - name: Validate prompt placeholders + env: + GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt + # poutine:ignore untrusted_checkout_exec + run: bash "${RUNNER_TEMP}/gh-aw/actions/validate_prompt_placeholders.sh" + - name: Print prompt + env: + GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt + # poutine:ignore untrusted_checkout_exec + run: bash "${RUNNER_TEMP}/gh-aw/actions/print_prompt_summary.sh" + - name: Upload activation artifact + if: success() + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: activation + path: | + /tmp/gh-aw/aw_info.json + /tmp/gh-aw/aw-prompts/prompt.txt + /tmp/gh-aw/github_rate_limits.jsonl + if-no-files-found: ignore + retention-days: 1 + + agent: + needs: activation + runs-on: ubuntu-latest + permissions: read-all + concurrency: + group: "gh-aw-copilot-${{ github.workflow }}" + env: + DEFAULT_BRANCH: ${{ github.event.repository.default_branch }} + GH_AW_ASSETS_ALLOWED_EXTS: "" + GH_AW_ASSETS_BRANCH: "" + GH_AW_ASSETS_MAX_SIZE_KB: 0 + GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs + GH_AW_WORKFLOW_ID_SANITIZED: agenticstatemachine + outputs: + agentic_engine_timeout: ${{ steps.detect-copilot-errors.outputs.agentic_engine_timeout || 'false' }} + checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }} + effective_tokens: ${{ steps.parse-mcp-gateway.outputs.effective_tokens }} + has_patch: ${{ steps.collect_output.outputs.has_patch }} + inference_access_error: ${{ steps.detect-copilot-errors.outputs.inference_access_error || 'false' }} + mcp_policy_error: ${{ steps.detect-copilot-errors.outputs.mcp_policy_error || 'false' }} + model: ${{ needs.activation.outputs.model }} + model_not_supported_error: ${{ steps.detect-copilot-errors.outputs.model_not_supported_error || 'false' }} + output: ${{ steps.collect_output.outputs.output }} + output_types: ${{ steps.collect_output.outputs.output_types }} + setup-trace-id: ${{ steps.setup.outputs.trace-id }} + steps: + - name: Setup Scripts + id: setup + uses: github/gh-aw-actions/setup@ba90f2186d7ad780ec640f364005fa24e797b360 # v0.68.3 + with: + destination: ${{ runner.temp }}/gh-aw/actions + job-name: ${{ github.job }} + trace-id: ${{ needs.activation.outputs.setup-trace-id }} + - name: Set runtime paths + id: set-runtime-paths + run: | + { + echo "GH_AW_SAFE_OUTPUTS=${RUNNER_TEMP}/gh-aw/safeoutputs/outputs.jsonl" + echo "GH_AW_SAFE_OUTPUTS_CONFIG_PATH=${RUNNER_TEMP}/gh-aw/safeoutputs/config.json" + echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${RUNNER_TEMP}/gh-aw/safeoutputs/tools.json" + } >> "$GITHUB_OUTPUT" + - name: Checkout repository + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false + - name: Create gh-aw temp directory + run: bash "${RUNNER_TEMP}/gh-aw/actions/create_gh_aw_tmp_dir.sh" + - name: Configure gh CLI for GitHub Enterprise + run: bash "${RUNNER_TEMP}/gh-aw/actions/configure_gh_for_ghe.sh" + env: + GH_TOKEN: ${{ github.token }} + - name: Configure Git credentials + env: + REPO_NAME: ${{ github.repository }} + SERVER_URL: ${{ github.server_url }} + GITHUB_TOKEN: ${{ github.token }} + run: | + git config --global user.email "github-actions[bot]@users.noreply.github.com" + git config --global user.name "github-actions[bot]" + git config --global am.keepcr true + # Re-authenticate git with GitHub token + SERVER_URL_STRIPPED="${SERVER_URL#https://}" + git remote set-url origin "https://x-access-token:${GITHUB_TOKEN}@${SERVER_URL_STRIPPED}/${REPO_NAME}.git" + echo "Git configured with standard GitHub Actions identity" + - name: Checkout PR branch + id: checkout-pr + if: | + github.event.pull_request || github.event.issue.pull_request + uses: actions/github-script@373c709c69115d41ff229c7e5df9f8788daa9553 # v9 + env: + GH_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} + with: + github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} + script: | + const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io, getOctokit); + const { main } = require('${{ runner.temp }}/gh-aw/actions/checkout_pr_branch.cjs'); + await main(); + - name: Install GitHub Copilot CLI + run: bash "${RUNNER_TEMP}/gh-aw/actions/install_copilot_cli.sh" 1.0.21 + env: + GH_HOST: github.com + - name: Install AWF binary + run: bash "${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh" v0.25.20 + - name: Parse integrity filter lists + id: parse-guard-vars + env: + GH_AW_BLOCKED_USERS_VAR: ${{ vars.GH_AW_GITHUB_BLOCKED_USERS || '' }} + GH_AW_TRUSTED_USERS_VAR: ${{ vars.GH_AW_GITHUB_TRUSTED_USERS || '' }} + GH_AW_APPROVAL_LABELS_VAR: ${{ vars.GH_AW_GITHUB_APPROVAL_LABELS || '' }} + run: bash "${RUNNER_TEMP}/gh-aw/actions/parse_guard_list.sh" + - name: Download container images + run: bash "${RUNNER_TEMP}/gh-aw/actions/download_docker_images.sh" ghcr.io/github/gh-aw-firewall/agent:0.25.20 ghcr.io/github/gh-aw-firewall/api-proxy:0.25.20 ghcr.io/github/gh-aw-firewall/squid:0.25.20 ghcr.io/github/gh-aw-mcpg:v0.2.19 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine + - name: Write Safe Outputs Config + run: | + 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_cf8f9bc3dcd2583f_EOF' + {"create_pull_request":{"allowed_files":[".github/workflows/docs/**"],"draft":false,"labels":["automation","NO_RELEASE_NOTES"],"max":1,"max_patch_size":1024,"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"],"protected_files_policy":"fallback-to-issue","protected_path_prefixes":[".github/",".agents/"],"title_prefix":"[Agentic State Machine] "},"create_report_incomplete_issue":{},"missing_data":{},"missing_tool":{},"noop":{"max":1,"report-as-issue":"false"},"report_incomplete":{}} + GH_AW_SAFE_OUTPUTS_CONFIG_cf8f9bc3dcd2583f_EOF + - name: Write Safe Outputs Tools + env: + GH_AW_TOOLS_META_JSON: | + { + "description_suffixes": { + "create_pull_request": " CONSTRAINTS: Maximum 1 pull request(s) can be created. Title will be prefixed with \"[Agentic State Machine] \". Labels [\"automation\" \"NO_RELEASE_NOTES\"] will be automatically added." + }, + "repo_params": {}, + "dynamic_tools": [] + } + GH_AW_VALIDATION_JSON: | + { + "create_pull_request": { + "defaultMax": 1, + "fields": { + "body": { + "required": true, + "type": "string", + "sanitize": true, + "maxLength": 65000 + }, + "branch": { + "required": true, + "type": "string", + "sanitize": true, + "maxLength": 256 + }, + "draft": { + "type": "boolean" + }, + "labels": { + "type": "array", + "itemType": "string", + "itemSanitize": true, + "itemMaxLength": 128 + }, + "repo": { + "type": "string", + "maxLength": 256 + }, + "title": { + "required": true, + "type": "string", + "sanitize": true, + "maxLength": 128 + } + } + }, + "missing_data": { + "defaultMax": 20, + "fields": { + "alternatives": { + "type": "string", + "sanitize": true, + "maxLength": 256 + }, + "context": { + "type": "string", + "sanitize": true, + "maxLength": 256 + }, + "data_type": { + "type": "string", + "sanitize": true, + "maxLength": 128 + }, + "reason": { + "type": "string", + "sanitize": true, + "maxLength": 256 + } + } + }, + "missing_tool": { + "defaultMax": 20, + "fields": { + "alternatives": { + "type": "string", + "sanitize": true, + "maxLength": 512 + }, + "reason": { + "required": true, + "type": "string", + "sanitize": true, + "maxLength": 256 + }, + "tool": { + "type": "string", + "sanitize": true, + "maxLength": 128 + } + } + }, + "noop": { + "defaultMax": 1, + "fields": { + "message": { + "required": true, + "type": "string", + "sanitize": true, + "maxLength": 65000 + } + } + }, + "report_incomplete": { + "defaultMax": 5, + "fields": { + "details": { + "type": "string", + "sanitize": true, + "maxLength": 65000 + }, + "reason": { + "required": true, + "type": "string", + "sanitize": true, + "maxLength": 1024 + } + } + } + } + uses: actions/github-script@373c709c69115d41ff229c7e5df9f8788daa9553 # v9 + with: + script: | + const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io, getOctokit); + const { main } = require('${{ runner.temp }}/gh-aw/actions/generate_safe_outputs_tools.cjs'); + await main(); + - name: Generate Safe Outputs MCP Server Config + id: safe-outputs-config + run: | + # Generate a secure random API key (360 bits of entropy, 40+ chars) + # Mask immediately to prevent timing vulnerabilities + API_KEY=$(openssl rand -base64 45 | tr -d '/+=') + echo "::add-mask::${API_KEY}" + + PORT=3001 + + # Set outputs for next steps + { + echo "safe_outputs_api_key=${API_KEY}" + echo "safe_outputs_port=${PORT}" + } >> "$GITHUB_OUTPUT" + + echo "Safe Outputs MCP server will run on port ${PORT}" + + - name: Start Safe Outputs MCP HTTP Server + id: safe-outputs-start + env: + DEBUG: '*' + GH_AW_SAFE_OUTPUTS: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS }} + GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-config.outputs.safe_outputs_port }} + GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-config.outputs.safe_outputs_api_key }} + GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ runner.temp }}/gh-aw/safeoutputs/tools.json + GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ runner.temp }}/gh-aw/safeoutputs/config.json + GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs + run: | + # Environment variables are set above to prevent template injection + export DEBUG + export GH_AW_SAFE_OUTPUTS + export GH_AW_SAFE_OUTPUTS_PORT + export GH_AW_SAFE_OUTPUTS_API_KEY + export GH_AW_SAFE_OUTPUTS_TOOLS_PATH + export GH_AW_SAFE_OUTPUTS_CONFIG_PATH + export GH_AW_MCP_LOG_DIR + + bash "${RUNNER_TEMP}/gh-aw/actions/start_safe_outputs_server.sh" + + - name: Start MCP Gateway + id: start-mcp-gateway + env: + GH_AW_SAFE_OUTPUTS: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS }} + GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-start.outputs.api_key }} + GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-start.outputs.port }} + GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} + run: | + set -eo pipefail + mkdir -p /tmp/gh-aw/mcp-config + + # Export gateway environment variables for MCP config and gateway script + export MCP_GATEWAY_PORT="80" + export MCP_GATEWAY_DOMAIN="host.docker.internal" + MCP_GATEWAY_API_KEY=$(openssl rand -base64 45 | tr -d '/+=') + echo "::add-mask::${MCP_GATEWAY_API_KEY}" + export MCP_GATEWAY_API_KEY + export MCP_GATEWAY_PAYLOAD_DIR="/tmp/gh-aw/mcp-payloads" + mkdir -p "${MCP_GATEWAY_PAYLOAD_DIR}" + export MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD="524288" + export DEBUG="*" + + export GH_AW_ENGINE="copilot" + export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -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.19' + + mkdir -p /home/runner/.copilot + cat << GH_AW_MCP_CONFIG_04fc9d7ce1d1c477_EOF | bash "${RUNNER_TEMP}/gh-aw/actions/start_mcp_gateway.sh" + { + "mcpServers": { + "github": { + "type": "stdio", + "container": "ghcr.io/github/github-mcp-server:v0.32.0", + "env": { + "GITHUB_HOST": "\${GITHUB_SERVER_URL}", + "GITHUB_PERSONAL_ACCESS_TOKEN": "\${GITHUB_MCP_SERVER_TOKEN}", + "GITHUB_READ_ONLY": "1", + "GITHUB_TOOLSETS": "repos" + }, + "guard-policies": { + "allow-only": { + "approval-labels": ${{ steps.parse-guard-vars.outputs.approval_labels }}, + "blocked-users": ${{ steps.parse-guard-vars.outputs.blocked_users }}, + "min-integrity": "none", + "repos": "all", + "trusted-users": ${{ steps.parse-guard-vars.outputs.trusted_users }} + } + } + }, + "safeoutputs": { + "type": "http", + "url": "http://host.docker.internal:$GH_AW_SAFE_OUTPUTS_PORT", + "headers": { + "Authorization": "\${GH_AW_SAFE_OUTPUTS_API_KEY}" + }, + "guard-policies": { + "write-sink": { + "accept": [ + "*" + ] + } + } + } + }, + "gateway": { + "port": $MCP_GATEWAY_PORT, + "domain": "${MCP_GATEWAY_DOMAIN}", + "apiKey": "${MCP_GATEWAY_API_KEY}", + "payloadDir": "${MCP_GATEWAY_PAYLOAD_DIR}" + } + } + GH_AW_MCP_CONFIG_04fc9d7ce1d1c477_EOF + - name: Download activation artifact + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + name: activation + path: /tmp/gh-aw + - name: Clean git credentials + continue-on-error: true + run: bash "${RUNNER_TEMP}/gh-aw/actions/clean_git_credentials.sh" + - name: Execute GitHub Copilot CLI + id: agentic_execution + # Copilot CLI tool arguments (sorted): + timeout-minutes: 15 + run: | + set -o pipefail + touch /tmp/gh-aw/agent-step-summary.md + (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" --env-all --exclude-env COPILOT_GITHUB_TOKEN --exclude-env GITHUB_MCP_SERVER_TOKEN --exclude-env MCP_GATEWAY_API_KEY --allow-domains '*.githubusercontent.com,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,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,docs.github.com,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.blog,github.com,github.githubassets.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,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.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 --image-tag 0.25.20 --skip-pull --enable-api-proxy \ + -- /bin/bash -c 'node ${RUNNER_TEMP}/gh-aw/actions/copilot_driver.cjs /usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --disable-builtin-mcps --no-ask-user --allow-all-tools --allow-all-paths --add-dir "${GITHUB_WORKSPACE}" --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log + env: + COPILOT_AGENT_RUNNER_TYPE: STANDALONE + COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }} + COPILOT_MODEL: ${{ vars.GH_AW_MODEL_AGENT_COPILOT || '' }} + GH_AW_MCP_CONFIG: /home/runner/.copilot/mcp-config.json + GH_AW_PHASE: agent + GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt + GH_AW_SAFE_OUTPUTS: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS }} + GH_AW_VERSION: v0.68.3 + GITHUB_API_URL: ${{ github.api_url }} + GITHUB_AW: true + GITHUB_HEAD_REF: ${{ github.head_ref }} + GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} + GITHUB_REF_NAME: ${{ github.ref_name }} + GITHUB_SERVER_URL: ${{ github.server_url }} + GITHUB_STEP_SUMMARY: /tmp/gh-aw/agent-step-summary.md + GITHUB_WORKSPACE: ${{ github.workspace }} + GIT_AUTHOR_EMAIL: github-actions[bot]@users.noreply.github.com + GIT_AUTHOR_NAME: github-actions[bot] + GIT_COMMITTER_EMAIL: github-actions[bot]@users.noreply.github.com + GIT_COMMITTER_NAME: github-actions[bot] + XDG_CONFIG_HOME: /home/runner + - name: Detect Copilot errors + id: detect-copilot-errors + if: always() + continue-on-error: true + run: node "${RUNNER_TEMP}/gh-aw/actions/detect_copilot_errors.cjs" + - name: Configure Git credentials + env: + REPO_NAME: ${{ github.repository }} + SERVER_URL: ${{ github.server_url }} + GITHUB_TOKEN: ${{ github.token }} + run: | + git config --global user.email "github-actions[bot]@users.noreply.github.com" + git config --global user.name "github-actions[bot]" + git config --global am.keepcr true + # Re-authenticate git with GitHub token + SERVER_URL_STRIPPED="${SERVER_URL#https://}" + git remote set-url origin "https://x-access-token:${GITHUB_TOKEN}@${SERVER_URL_STRIPPED}/${REPO_NAME}.git" + echo "Git configured with standard GitHub Actions identity" + - name: Copy Copilot session state files to logs + if: always() + continue-on-error: true + run: bash "${RUNNER_TEMP}/gh-aw/actions/copy_copilot_session_state.sh" + - name: Stop MCP Gateway + if: always() + continue-on-error: true + env: + MCP_GATEWAY_PORT: ${{ steps.start-mcp-gateway.outputs.gateway-port }} + MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }} + GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }} + run: | + bash "${RUNNER_TEMP}/gh-aw/actions/stop_mcp_gateway.sh" "$GATEWAY_PID" + - name: Redact secrets in logs + if: always() + uses: actions/github-script@373c709c69115d41ff229c7e5df9f8788daa9553 # v9 + with: + script: | + const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io, getOctokit); + const { main } = require('${{ runner.temp }}/gh-aw/actions/redact_secrets.cjs'); + await main(); + env: + GH_AW_SECRET_NAMES: 'COPILOT_GITHUB_TOKEN,GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN' + SECRET_COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }} + SECRET_GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }} + SECRET_GH_AW_GITHUB_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN }} + SECRET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - name: Append agent step summary + if: always() + run: bash "${RUNNER_TEMP}/gh-aw/actions/append_agent_step_summary.sh" + - name: Copy Safe Outputs + if: always() + env: + GH_AW_SAFE_OUTPUTS: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS }} + run: | + mkdir -p /tmp/gh-aw + cp "$GH_AW_SAFE_OUTPUTS" /tmp/gh-aw/safeoutputs.jsonl 2>/dev/null || true + - name: Ingest agent output + id: collect_output + if: always() + uses: actions/github-script@373c709c69115d41ff229c7e5df9f8788daa9553 # v9 + env: + GH_AW_SAFE_OUTPUTS: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS }} + GH_AW_ALLOWED_DOMAINS: "*.githubusercontent.com,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,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,docs.github.com,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.blog,github.com,github.githubassets.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,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.googleapis.com" + GITHUB_SERVER_URL: ${{ github.server_url }} + GITHUB_API_URL: ${{ github.api_url }} + with: + script: | + const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io, getOctokit); + const { main } = require('${{ runner.temp }}/gh-aw/actions/collect_ndjson_output.cjs'); + await main(); + - name: Parse agent logs for step summary + if: always() + uses: actions/github-script@373c709c69115d41ff229c7e5df9f8788daa9553 # v9 + env: + GH_AW_AGENT_OUTPUT: /tmp/gh-aw/sandbox/agent/logs/ + with: + script: | + const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io, getOctokit); + const { main } = require('${{ runner.temp }}/gh-aw/actions/parse_copilot_log.cjs'); + await main(); + - name: Parse MCP Gateway logs for step summary + if: always() + id: parse-mcp-gateway + uses: actions/github-script@373c709c69115d41ff229c7e5df9f8788daa9553 # v9 + with: + script: | + const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io, getOctokit); + const { main } = require('${{ runner.temp }}/gh-aw/actions/parse_mcp_gateway_log.cjs'); + await main(); + - name: Print firewall logs + if: always() + continue-on-error: true + env: + AWF_LOGS_DIR: /tmp/gh-aw/sandbox/firewall/logs + run: | + # Fix permissions on firewall logs so they can be uploaded as artifacts + # AWF runs with sudo, creating files owned by root + sudo chmod -R a+r /tmp/gh-aw/sandbox/firewall/logs 2>/dev/null || true + # Only run awf logs summary if awf command exists (it may not be installed if workflow failed before install step) + if command -v awf &> /dev/null; then + awf logs summary | tee -a "$GITHUB_STEP_SUMMARY" + else + echo 'AWF binary not installed, skipping firewall log summary' + fi + - name: Parse token usage for step summary + if: always() + continue-on-error: true + uses: actions/github-script@373c709c69115d41ff229c7e5df9f8788daa9553 # v9 + with: + script: | + const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io, getOctokit); + const { main } = require('${{ runner.temp }}/gh-aw/actions/parse_token_usage.cjs'); + await main(); + - name: Write agent output placeholder if missing + if: always() + run: | + if [ ! -f /tmp/gh-aw/agent_output.json ]; then + echo '{"items":[]}' > /tmp/gh-aw/agent_output.json + fi + - name: Upload agent artifacts + if: always() + continue-on-error: true + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: agent + path: | + /tmp/gh-aw/aw-prompts/prompt.txt + /tmp/gh-aw/sandbox/agent/logs/ + /tmp/gh-aw/redacted-urls.log + /tmp/gh-aw/mcp-logs/ + /tmp/gh-aw/proxy-logs/ + !/tmp/gh-aw/proxy-logs/proxy-tls/ + /tmp/gh-aw/agent_usage.json + /tmp/gh-aw/agent-stdio.log + /tmp/gh-aw/agent/ + /tmp/gh-aw/github_rate_limits.jsonl + /tmp/gh-aw/safeoutputs.jsonl + /tmp/gh-aw/agent_output.json + /tmp/gh-aw/aw-*.patch + /tmp/gh-aw/aw-*.bundle + /tmp/gh-aw/sandbox/firewall/logs/ + /tmp/gh-aw/sandbox/firewall/audit/ + if-no-files-found: ignore + + conclusion: + needs: + - activation + - agent + - detection + - safe_outputs + if: > + always() && (needs.agent.result != 'skipped' || needs.activation.outputs.lockdown_check_failed == 'true' || + needs.activation.outputs.stale_lock_file_failed == 'true') + runs-on: ubuntu-slim + permissions: + contents: write + issues: write + pull-requests: write + concurrency: + group: "gh-aw-conclusion-agentic-state-machine" + cancel-in-progress: false + outputs: + incomplete_count: ${{ steps.report_incomplete.outputs.incomplete_count }} + noop_message: ${{ steps.noop.outputs.noop_message }} + tools_reported: ${{ steps.missing_tool.outputs.tools_reported }} + total_count: ${{ steps.missing_tool.outputs.total_count }} + steps: + - name: Setup Scripts + id: setup + uses: github/gh-aw-actions/setup@ba90f2186d7ad780ec640f364005fa24e797b360 # v0.68.3 + with: + destination: ${{ runner.temp }}/gh-aw/actions + job-name: ${{ github.job }} + trace-id: ${{ needs.activation.outputs.setup-trace-id }} + - name: Download agent output artifact + id: download-agent-output + continue-on-error: true + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + name: agent + path: /tmp/gh-aw/ + - name: Setup agent output environment variable + id: setup-agent-output-env + if: steps.download-agent-output.outcome == 'success' + run: | + mkdir -p /tmp/gh-aw/ + find "/tmp/gh-aw/" -type f -print + echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/agent_output.json" >> "$GITHUB_OUTPUT" + - name: Process no-op messages + id: noop + uses: actions/github-script@373c709c69115d41ff229c7e5df9f8788daa9553 # v9 + env: + GH_AW_AGENT_OUTPUT: ${{ steps.setup-agent-output-env.outputs.GH_AW_AGENT_OUTPUT }} + GH_AW_NOOP_MAX: "1" + GH_AW_WORKFLOW_NAME: "Agentic State Machine — Diagram Generator" + GH_AW_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} + GH_AW_AGENT_CONCLUSION: ${{ needs.agent.result }} + GH_AW_NOOP_REPORT_AS_ISSUE: "false" + with: + github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} + script: | + const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io, getOctokit); + const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_noop_message.cjs'); + await main(); + - name: Log detection run + id: detection_runs + uses: actions/github-script@373c709c69115d41ff229c7e5df9f8788daa9553 # v9 + env: + GH_AW_AGENT_OUTPUT: ${{ steps.setup-agent-output-env.outputs.GH_AW_AGENT_OUTPUT }} + GH_AW_WORKFLOW_NAME: "Agentic State Machine — Diagram Generator" + GH_AW_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} + GH_AW_DETECTION_CONCLUSION: ${{ needs.detection.outputs.detection_conclusion }} + GH_AW_DETECTION_REASON: ${{ needs.detection.outputs.detection_reason }} + with: + github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} + script: | + const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io, getOctokit); + const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_detection_runs.cjs'); + await main(); + - name: Record missing tool + id: missing_tool + uses: actions/github-script@373c709c69115d41ff229c7e5df9f8788daa9553 # v9 + env: + GH_AW_AGENT_OUTPUT: ${{ steps.setup-agent-output-env.outputs.GH_AW_AGENT_OUTPUT }} + GH_AW_MISSING_TOOL_CREATE_ISSUE: "true" + GH_AW_WORKFLOW_NAME: "Agentic State Machine — Diagram Generator" + with: + github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} + script: | + const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io, getOctokit); + const { main } = require('${{ runner.temp }}/gh-aw/actions/missing_tool.cjs'); + await main(); + - name: Record incomplete + id: report_incomplete + uses: actions/github-script@373c709c69115d41ff229c7e5df9f8788daa9553 # v9 + env: + GH_AW_AGENT_OUTPUT: ${{ steps.setup-agent-output-env.outputs.GH_AW_AGENT_OUTPUT }} + GH_AW_REPORT_INCOMPLETE_CREATE_ISSUE: "true" + GH_AW_WORKFLOW_NAME: "Agentic State Machine — Diagram Generator" + with: + github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} + script: | + const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io, getOctokit); + const { main } = require('${{ runner.temp }}/gh-aw/actions/report_incomplete_handler.cjs'); + await main(); + - name: Handle agent failure + id: handle_agent_failure + if: always() + uses: actions/github-script@373c709c69115d41ff229c7e5df9f8788daa9553 # v9 + env: + GH_AW_AGENT_OUTPUT: ${{ steps.setup-agent-output-env.outputs.GH_AW_AGENT_OUTPUT }} + GH_AW_WORKFLOW_NAME: "Agentic State Machine — Diagram Generator" + GH_AW_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} + GH_AW_AGENT_CONCLUSION: ${{ needs.agent.result }} + GH_AW_WORKFLOW_ID: "agentic-state-machine" + GH_AW_ENGINE_ID: "copilot" + GH_AW_SECRET_VERIFICATION_RESULT: ${{ needs.activation.outputs.secret_verification_result }} + GH_AW_CHECKOUT_PR_SUCCESS: ${{ needs.agent.outputs.checkout_pr_success }} + GH_AW_INFERENCE_ACCESS_ERROR: ${{ needs.agent.outputs.inference_access_error }} + GH_AW_MCP_POLICY_ERROR: ${{ needs.agent.outputs.mcp_policy_error }} + GH_AW_AGENTIC_ENGINE_TIMEOUT: ${{ needs.agent.outputs.agentic_engine_timeout }} + GH_AW_MODEL_NOT_SUPPORTED_ERROR: ${{ needs.agent.outputs.model_not_supported_error }} + GH_AW_CODE_PUSH_FAILURE_ERRORS: ${{ needs.safe_outputs.outputs.code_push_failure_errors }} + GH_AW_CODE_PUSH_FAILURE_COUNT: ${{ needs.safe_outputs.outputs.code_push_failure_count }} + GH_AW_LOCKDOWN_CHECK_FAILED: ${{ needs.activation.outputs.lockdown_check_failed }} + GH_AW_STALE_LOCK_FILE_FAILED: ${{ needs.activation.outputs.stale_lock_file_failed }} + GH_AW_GROUP_REPORTS: "false" + GH_AW_FAILURE_REPORT_AS_ISSUE: "true" + GH_AW_TIMEOUT_MINUTES: "15" + with: + github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} + script: | + const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io, getOctokit); + const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_agent_failure.cjs'); + await main(); + + detection: + needs: + - activation + - agent + if: > + always() && needs.agent.result != 'skipped' && (needs.agent.outputs.output_types != '' || needs.agent.outputs.has_patch == 'true') + runs-on: ubuntu-latest + permissions: + contents: read + outputs: + detection_conclusion: ${{ steps.detection_conclusion.outputs.conclusion }} + detection_reason: ${{ steps.detection_conclusion.outputs.reason }} + detection_success: ${{ steps.detection_conclusion.outputs.success }} + steps: + - name: Setup Scripts + id: setup + uses: github/gh-aw-actions/setup@ba90f2186d7ad780ec640f364005fa24e797b360 # v0.68.3 + with: + destination: ${{ runner.temp }}/gh-aw/actions + job-name: ${{ github.job }} + trace-id: ${{ needs.activation.outputs.setup-trace-id }} + - name: Download agent output artifact + id: download-agent-output + continue-on-error: true + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + name: agent + path: /tmp/gh-aw/ + - name: Setup agent output environment variable + id: setup-agent-output-env + if: steps.download-agent-output.outcome == 'success' + run: | + mkdir -p /tmp/gh-aw/ + find "/tmp/gh-aw/" -type f -print + echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/agent_output.json" >> "$GITHUB_OUTPUT" + - name: Checkout repository for patch context + if: needs.agent.outputs.has_patch == 'true' + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false + # --- Threat Detection --- + - name: Clean stale firewall files from agent artifact + run: | + rm -rf /tmp/gh-aw/sandbox/firewall/logs + rm -rf /tmp/gh-aw/sandbox/firewall/audit + - name: Download container images + run: bash "${RUNNER_TEMP}/gh-aw/actions/download_docker_images.sh" ghcr.io/github/gh-aw-firewall/agent:0.25.20 ghcr.io/github/gh-aw-firewall/api-proxy:0.25.20 ghcr.io/github/gh-aw-firewall/squid:0.25.20 + - name: Check if detection needed + id: detection_guard + if: always() + env: + OUTPUT_TYPES: ${{ needs.agent.outputs.output_types }} + HAS_PATCH: ${{ needs.agent.outputs.has_patch }} + run: | + if [[ -n "$OUTPUT_TYPES" || "$HAS_PATCH" == "true" ]]; then + echo "run_detection=true" >> "$GITHUB_OUTPUT" + echo "Detection will run: output_types=$OUTPUT_TYPES, has_patch=$HAS_PATCH" + else + echo "run_detection=false" >> "$GITHUB_OUTPUT" + echo "Detection skipped: no agent outputs or patches to analyze" + fi + - name: Clear MCP configuration for detection + if: always() && steps.detection_guard.outputs.run_detection == 'true' + run: | + rm -f /tmp/gh-aw/mcp-config/mcp-servers.json + rm -f /home/runner/.copilot/mcp-config.json + rm -f "$GITHUB_WORKSPACE/.gemini/settings.json" + - name: Prepare threat detection files + if: always() && steps.detection_guard.outputs.run_detection == 'true' + run: | + mkdir -p /tmp/gh-aw/threat-detection/aw-prompts + cp /tmp/gh-aw/aw-prompts/prompt.txt /tmp/gh-aw/threat-detection/aw-prompts/prompt.txt 2>/dev/null || true + cp /tmp/gh-aw/agent_output.json /tmp/gh-aw/threat-detection/agent_output.json 2>/dev/null || true + for f in /tmp/gh-aw/aw-*.patch; do + [ -f "$f" ] && cp "$f" /tmp/gh-aw/threat-detection/ 2>/dev/null || true + done + for f in /tmp/gh-aw/aw-*.bundle; do + [ -f "$f" ] && cp "$f" /tmp/gh-aw/threat-detection/ 2>/dev/null || true + done + echo "Prepared threat detection files:" + ls -la /tmp/gh-aw/threat-detection/ 2>/dev/null || true + - name: Setup threat detection + if: always() && steps.detection_guard.outputs.run_detection == 'true' + uses: actions/github-script@373c709c69115d41ff229c7e5df9f8788daa9553 # v9 + env: + WORKFLOW_NAME: "Agentic State Machine — Diagram Generator" + WORKFLOW_DESCRIPTION: "Reads all agentic workflow .md files in this repo, extracts the\nstate machine (triggers, labels, actions, handovers between workflows),\nand renders a human-readable diagram in .github/workflows/docs/state-machine.md.\nRuns weekly. Updates the diagram via PR." + HAS_PATCH: ${{ needs.agent.outputs.has_patch }} + with: + script: | + const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io, getOctokit); + const { main } = require('${{ runner.temp }}/gh-aw/actions/setup_threat_detection.cjs'); + await main(); + - name: Ensure threat-detection directory and log + if: always() && steps.detection_guard.outputs.run_detection == 'true' + run: | + mkdir -p /tmp/gh-aw/threat-detection + touch /tmp/gh-aw/threat-detection/detection.log + - name: Install GitHub Copilot CLI + run: bash "${RUNNER_TEMP}/gh-aw/actions/install_copilot_cli.sh" 1.0.21 + env: + GH_HOST: github.com + - name: Install AWF binary + run: bash "${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh" v0.25.20 + - name: Execute GitHub Copilot CLI + if: always() && steps.detection_guard.outputs.run_detection == 'true' + id: detection_agentic_execution + # Copilot CLI tool arguments (sorted): + timeout-minutes: 20 + run: | + set -o pipefail + touch /tmp/gh-aw/agent-step-summary.md + (umask 177 && touch /tmp/gh-aw/threat-detection/detection.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" --env-all --exclude-env COPILOT_GITHUB_TOKEN --allow-domains api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,telemetry.enterprise.githubcopilot.com --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --audit-dir /tmp/gh-aw/sandbox/firewall/audit --enable-host-access --image-tag 0.25.20 --skip-pull --enable-api-proxy \ + -- /bin/bash -c 'node ${RUNNER_TEMP}/gh-aw/actions/copilot_driver.cjs /usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --disable-builtin-mcps --no-ask-user --allow-all-tools --add-dir "${GITHUB_WORKSPACE}" --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log + env: + COPILOT_AGENT_RUNNER_TYPE: STANDALONE + COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }} + COPILOT_MODEL: ${{ vars.GH_AW_MODEL_DETECTION_COPILOT || '' }} + GH_AW_PHASE: detection + GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt + GH_AW_VERSION: v0.68.3 + GITHUB_API_URL: ${{ github.api_url }} + GITHUB_AW: true + GITHUB_HEAD_REF: ${{ github.head_ref }} + GITHUB_REF_NAME: ${{ github.ref_name }} + GITHUB_SERVER_URL: ${{ github.server_url }} + GITHUB_STEP_SUMMARY: /tmp/gh-aw/agent-step-summary.md + GITHUB_WORKSPACE: ${{ github.workspace }} + GIT_AUTHOR_EMAIL: github-actions[bot]@users.noreply.github.com + GIT_AUTHOR_NAME: github-actions[bot] + GIT_COMMITTER_EMAIL: github-actions[bot]@users.noreply.github.com + GIT_COMMITTER_NAME: github-actions[bot] + XDG_CONFIG_HOME: /home/runner + - name: Upload threat detection log + if: always() && steps.detection_guard.outputs.run_detection == 'true' + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: detection + path: /tmp/gh-aw/threat-detection/detection.log + if-no-files-found: ignore + - name: Parse and conclude threat detection + id: detection_conclusion + if: always() + uses: actions/github-script@373c709c69115d41ff229c7e5df9f8788daa9553 # v9 + env: + RUN_DETECTION: ${{ steps.detection_guard.outputs.run_detection }} + GH_AW_DETECTION_CONTINUE_ON_ERROR: "true" + with: + script: | + const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io, getOctokit); + const { main } = require('${{ runner.temp }}/gh-aw/actions/parse_threat_detection_results.cjs'); + await main(); + + safe_outputs: + needs: + - activation + - agent + - detection + if: (!cancelled()) && needs.agent.result != 'skipped' && needs.detection.result == 'success' + runs-on: ubuntu-slim + permissions: + contents: write + issues: write + pull-requests: write + timeout-minutes: 15 + env: + GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/agentic-state-machine" + GH_AW_DETECTION_CONCLUSION: ${{ needs.detection.outputs.detection_conclusion }} + GH_AW_DETECTION_REASON: ${{ needs.detection.outputs.detection_reason }} + GH_AW_EFFECTIVE_TOKENS: ${{ needs.agent.outputs.effective_tokens }} + GH_AW_ENGINE_ID: "copilot" + GH_AW_ENGINE_MODEL: ${{ needs.agent.outputs.model }} + GH_AW_WORKFLOW_ID: "agentic-state-machine" + GH_AW_WORKFLOW_NAME: "Agentic State Machine — Diagram Generator" + outputs: + code_push_failure_count: ${{ steps.process_safe_outputs.outputs.code_push_failure_count }} + code_push_failure_errors: ${{ steps.process_safe_outputs.outputs.code_push_failure_errors }} + create_discussion_error_count: ${{ steps.process_safe_outputs.outputs.create_discussion_error_count }} + create_discussion_errors: ${{ steps.process_safe_outputs.outputs.create_discussion_errors }} + created_pr_number: ${{ steps.process_safe_outputs.outputs.created_pr_number }} + created_pr_url: ${{ steps.process_safe_outputs.outputs.created_pr_url }} + process_safe_outputs_processed_count: ${{ steps.process_safe_outputs.outputs.processed_count }} + process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} + steps: + - name: Setup Scripts + id: setup + uses: github/gh-aw-actions/setup@ba90f2186d7ad780ec640f364005fa24e797b360 # v0.68.3 + with: + destination: ${{ runner.temp }}/gh-aw/actions + job-name: ${{ github.job }} + trace-id: ${{ needs.activation.outputs.setup-trace-id }} + - name: Download agent output artifact + id: download-agent-output + continue-on-error: true + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + name: agent + path: /tmp/gh-aw/ + - name: Setup agent output environment variable + id: setup-agent-output-env + if: steps.download-agent-output.outcome == 'success' + run: | + mkdir -p /tmp/gh-aw/ + find "/tmp/gh-aw/" -type f -print + echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/agent_output.json" >> "$GITHUB_OUTPUT" + - name: Download patch artifact + continue-on-error: true + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + name: agent + path: /tmp/gh-aw/ + - name: Checkout repository + if: (!cancelled()) && needs.agent.result != 'skipped' && contains(needs.agent.outputs.output_types, 'create_pull_request') + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + ref: ${{ github.base_ref || github.event.pull_request.base.ref || github.ref_name || github.event.repository.default_branch }} + token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} + persist-credentials: false + fetch-depth: 1 + - name: Configure Git credentials + if: (!cancelled()) && needs.agent.result != 'skipped' && contains(needs.agent.outputs.output_types, 'create_pull_request') + env: + REPO_NAME: ${{ github.repository }} + SERVER_URL: ${{ github.server_url }} + GIT_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} + run: | + git config --global user.email "github-actions[bot]@users.noreply.github.com" + git config --global user.name "github-actions[bot]" + git config --global am.keepcr true + # Re-authenticate git with GitHub token + SERVER_URL_STRIPPED="${SERVER_URL#https://}" + git remote set-url origin "https://x-access-token:${GIT_TOKEN}@${SERVER_URL_STRIPPED}/${REPO_NAME}.git" + echo "Git configured with standard GitHub Actions identity" + - name: Configure GH_HOST for enterprise compatibility + id: ghes-host-config + shell: bash + run: | + # Derive GH_HOST from GITHUB_SERVER_URL so the gh CLI targets the correct + # GitHub instance (GHES/GHEC). On github.com this is a harmless no-op. + GH_HOST="${GITHUB_SERVER_URL#https://}" + GH_HOST="${GH_HOST#http://}" + echo "GH_HOST=${GH_HOST}" >> "$GITHUB_ENV" + - name: Process Safe Outputs + id: process_safe_outputs + uses: actions/github-script@373c709c69115d41ff229c7e5df9f8788daa9553 # v9 + env: + GH_AW_AGENT_OUTPUT: ${{ steps.setup-agent-output-env.outputs.GH_AW_AGENT_OUTPUT }} + GH_AW_ALLOWED_DOMAINS: "*.githubusercontent.com,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,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,docs.github.com,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.blog,github.com,github.githubassets.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,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.googleapis.com" + GITHUB_SERVER_URL: ${{ github.server_url }} + GITHUB_API_URL: ${{ github.api_url }} + GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"create_pull_request\":{\"allowed_files\":[\".github/workflows/docs/**\"],\"draft\":false,\"labels\":[\"automation\",\"NO_RELEASE_NOTES\"],\"max\":1,\"max_patch_size\":1024,\"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\",\"AGENTS.md\"],\"protected_files_policy\":\"fallback-to-issue\",\"protected_path_prefixes\":[\".github/\",\".agents/\"],\"title_prefix\":\"[Agentic State Machine] \"},\"create_report_incomplete_issue\":{},\"missing_data\":{},\"missing_tool\":{},\"noop\":{\"max\":1,\"report-as-issue\":\"false\"},\"report_incomplete\":{}}" + GH_AW_CI_TRIGGER_TOKEN: ${{ secrets.GH_AW_CI_TRIGGER_TOKEN }} + with: + github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} + script: | + const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io, getOctokit); + const { main } = require('${{ runner.temp }}/gh-aw/actions/safe_output_handler_manager.cjs'); + await main(); + - name: Upload Safe Outputs Items + if: always() + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: safe-outputs-items + path: | + /tmp/gh-aw/safe-output-items.jsonl + /tmp/gh-aw/temporary-id-map.json + if-no-files-found: ignore + diff --git a/.github/workflows/agentic-state-machine.md b/.github/workflows/agentic-state-machine.md new file mode 100644 index 0000000000..9a8fcc6335 --- /dev/null +++ b/.github/workflows/agentic-state-machine.md @@ -0,0 +1,108 @@ +--- +description: | + Reads all agentic workflow .md files in this repo, extracts the + state machine (triggers, labels, actions, handovers between workflows), + and renders a human-readable diagram in .github/workflows/docs/state-machine.md. + Runs weekly. Updates the diagram via PR. + +on: + schedule: every 7d + workflow_dispatch: + +timeout-minutes: 15 + +permissions: read-all + +network: + allowed: + - defaults + - github + +tools: + github: + toolsets: [repos] + min-integrity: none + bash: true + +safe-outputs: + noop: + report-as-issue: false + create-pull-request: + title-prefix: "[Agentic State Machine] " + labels: [automation, NO_RELEASE_NOTES] + draft: false + max: 1 + allowed-files: + - ".github/workflows/docs/**" + protected-files: fallback-to-issue +--- + +# Agentic State Machine — Diagram Generator + + +You read all agentic workflow `.md` files in this repo, extract the inherent state machine they collectively define, and render it as a Mermaid diagram in `.github/workflows/docs/state-machine.md`. + + + +This repo uses GitHub Agentic Workflows (gh-aw). Each `.md` file in `.github/workflows/` defines an agent with triggers, rules, safe-outputs, and interactions with GitHub objects (issues, PRs, labels, comments). Together, these workflows form an implicit state machine — but no single document shows the full picture. Humans need a diagram. + + + +1. Read ALL `.md` files in `.github/workflows/` (excluding this one and the `docs/` subfolder). +2. For each workflow, extract: + - **Triggers**: schedule, workflow_dispatch, slash_command, reaction, dispatch from other workflows + - **Inputs**: what GitHub objects it reads (issues, PRs, labels, comments, check runs) + - **Outputs / safe-outputs**: what it can write (labels, comments, PRs, issues, dispatches) + - **Label operations**: which labels it reads (as conditions), adds, or removes + - **Handovers**: does it dispatch another workflow? Does it create PRs that another workflow processes? + - **Conditions**: what filters determine which items it processes (label presence, author, fork status, draft status) +3. Also read `.github/tooling-check-repo-rules.md` if it exists — it may define additional label semantics. +4. Synthesize everything into one coherent state machine covering the full lifecycle of: + - **Issues**: opened → triaged → regression-tested → closed + - **PRs**: opened → scanned → labeled → maintained → merged + - **Labels**: which workflow applies them, what they gate, which workflow reads them + - **Handovers**: workflow A dispatches workflow B, or workflow A creates a PR that workflow B maintains + + + +1. List all `.md` files in `.github/workflows/` via bash. +2. Read each one. Extract the frontmatter (triggers, safe-outputs, tools) and the body (rules, process, label logic). +3. Build a mental model of the full state machine. +4. Write `.github/workflows/docs/state-machine.md` with: + + a. A **summary table** of all workflows: + | Workflow | Trigger | Reads | Writes | Key Labels | + + b. A **Mermaid diagram** showing the full lifecycle: + - Nodes = states (issue opened, PR opened, PR scanned, PR labeled, etc.) + - Edges = transitions (triggered by schedule, label added, dispatch, etc.) + - Label each edge with the workflow that performs it + - Show handovers between workflows clearly + + c. A **label dictionary** — every label used across all workflows: + | Label | Applied by | Read by | Meaning | + + d. A **handover map** — which workflows interact: + | From | To | Mechanism | + +5. Open a PR with the updated diagram via `create-pull-request`. + + + +The Mermaid diagram should look something like: + +```mermaid +stateDiagram-v2 + [*] --> IssueOpened: issue created + IssueOpened --> IssueTriage: repo-assist (schedule) + IssueTriage --> RegressionPR: repo-assist creates PR + + [*] --> PROpened: PR created (fork) + PROpened --> PRScanned: tooling-check (schedule) + PRScanned --> PRLabeled: labels applied + PRLabeled --> PRMaintained: labelops (schedule) + PRMaintained --> PRMerged: human merge +``` + +Adapt to the actual workflows found. Include ALL workflows, ALL labels, ALL transitions. + From e7c00be8b78e6d39878c3e973e6002ea29daf906 Mon Sep 17 00:00:00 2001 From: Tomas Grosup Date: Tue, 5 May 2026 21:14:25 +0200 Subject: [PATCH 2/4] Rewrite state machine generator with multi-diagram approach Applied: Mermaid stateDiagram-v2 best practices (composite states, choice nodes, direction LR, classDef styling, notes), Anthropic prompt guide (XML tags, role, concrete examples), Peli audit-workflows pattern. Key changes: - Multiple diagrams by dimension (issues, PRs, labels, handovers) instead of one giant unreadable diagram - Composite states for PR types (fork, non-fork, regression-test) - Change detection via source fingerprint comment - skip if unchanged - Diagram guidelines section teaching Mermaid techniques - Concrete example showing Issue Lifecycle structure - Screen-wide LR layout, max ~15 states per diagram - Label dictionary + handover map as tables (not diagrams) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../workflows/agentic-state-machine.lock.yml | 32 ++-- .github/workflows/agentic-state-machine.md | 140 ++++++++++++------ 2 files changed, 107 insertions(+), 65 deletions(-) diff --git a/.github/workflows/agentic-state-machine.lock.yml b/.github/workflows/agentic-state-machine.lock.yml index 6f6712c163..42fc05ae19 100644 --- a/.github/workflows/agentic-state-machine.lock.yml +++ b/.github/workflows/agentic-state-machine.lock.yml @@ -1,4 +1,4 @@ -# gh-aw-metadata: {"schema_version":"v3","frontmatter_hash":"fabdec898197657b7dfbf888255c19432c2a915b655d09a8cc01603baee4cf3b","compiler_version":"v0.68.3","strict":true,"agent_id":"copilot"} +# gh-aw-metadata: {"schema_version":"v3","frontmatter_hash":"bb4d98081026580c09afaf506092ca4cb80651e1cc9208091621122e1e5d70d2","compiler_version":"v0.68.3","strict":true,"agent_id":"copilot"} # gh-aw-manifest: {"version":1,"secrets":["COPILOT_GITHUB_TOKEN","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/upload-artifact","sha":"043fb46d1a93c77aae656e7c1c64a875d1fc6a0a","version":"v7.0.1"},{"repo":"github/gh-aw-actions/setup","sha":"ba90f2186d7ad780ec640f364005fa24e797b360","version":"v0.68.3"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.25.20"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.25.20"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.25.20"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.2.19"},{"image":"ghcr.io/github/github-mcp-server:v0.32.0"},{"image":"node:lts-alpine"}]} # ___ _ _ # / _ \ | | (_) @@ -24,8 +24,8 @@ # # Reads all agentic workflow .md files in this repo, extracts the # state machine (triggers, labels, actions, handovers between workflows), -# and renders a human-readable diagram in .github/workflows/docs/state-machine.md. -# Runs weekly. Updates the diagram via PR. +# and renders human-readable Mermaid diagrams in .github/workflows/docs/state-machine.md. +# Runs weekly. Only updates if workflow sources changed. Opens a PR. # # Secrets used: # - COPILOT_GITHUB_TOKEN @@ -168,19 +168,19 @@ jobs: run: | bash "${RUNNER_TEMP}/gh-aw/actions/create_prompt_first.sh" { - cat << 'GH_AW_PROMPT_449d6f65728fd8bf_EOF' + cat << 'GH_AW_PROMPT_e9d49456b580f49e_EOF' - GH_AW_PROMPT_449d6f65728fd8bf_EOF + GH_AW_PROMPT_e9d49456b580f49e_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_449d6f65728fd8bf_EOF' + cat << 'GH_AW_PROMPT_e9d49456b580f49e_EOF' Tools: create_pull_request, missing_tool, missing_data, noop - GH_AW_PROMPT_449d6f65728fd8bf_EOF + GH_AW_PROMPT_e9d49456b580f49e_EOF cat "${RUNNER_TEMP}/gh-aw/prompts/safe_outputs_create_pull_request.md" - cat << 'GH_AW_PROMPT_449d6f65728fd8bf_EOF' + cat << 'GH_AW_PROMPT_e9d49456b580f49e_EOF' The following GitHub context information is available for this workflow: @@ -210,12 +210,12 @@ jobs: {{/if}} - GH_AW_PROMPT_449d6f65728fd8bf_EOF + GH_AW_PROMPT_e9d49456b580f49e_EOF cat "${RUNNER_TEMP}/gh-aw/prompts/github_mcp_tools_with_safeoutputs_prompt.md" - cat << 'GH_AW_PROMPT_449d6f65728fd8bf_EOF' + cat << 'GH_AW_PROMPT_e9d49456b580f49e_EOF' {{#runtime-import .github/workflows/agentic-state-machine.md}} - GH_AW_PROMPT_449d6f65728fd8bf_EOF + GH_AW_PROMPT_e9d49456b580f49e_EOF } > "$GH_AW_PROMPT" - name: Interpolate variables and render templates uses: actions/github-script@373c709c69115d41ff229c7e5df9f8788daa9553 # v9 @@ -380,9 +380,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_cf8f9bc3dcd2583f_EOF' + cat > "${RUNNER_TEMP}/gh-aw/safeoutputs/config.json" << 'GH_AW_SAFE_OUTPUTS_CONFIG_688199fadd8517a4_EOF' {"create_pull_request":{"allowed_files":[".github/workflows/docs/**"],"draft":false,"labels":["automation","NO_RELEASE_NOTES"],"max":1,"max_patch_size":1024,"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"],"protected_files_policy":"fallback-to-issue","protected_path_prefixes":[".github/",".agents/"],"title_prefix":"[Agentic State Machine] "},"create_report_incomplete_issue":{},"missing_data":{},"missing_tool":{},"noop":{"max":1,"report-as-issue":"false"},"report_incomplete":{}} - GH_AW_SAFE_OUTPUTS_CONFIG_cf8f9bc3dcd2583f_EOF + GH_AW_SAFE_OUTPUTS_CONFIG_688199fadd8517a4_EOF - name: Write Safe Outputs Tools env: GH_AW_TOOLS_META_JSON: | @@ -578,7 +578,7 @@ jobs: export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -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.19' mkdir -p /home/runner/.copilot - cat << GH_AW_MCP_CONFIG_04fc9d7ce1d1c477_EOF | bash "${RUNNER_TEMP}/gh-aw/actions/start_mcp_gateway.sh" + cat << GH_AW_MCP_CONFIG_65224a7dc033dff5_EOF | bash "${RUNNER_TEMP}/gh-aw/actions/start_mcp_gateway.sh" { "mcpServers": { "github": { @@ -622,7 +622,7 @@ jobs: "payloadDir": "${MCP_GATEWAY_PAYLOAD_DIR}" } } - GH_AW_MCP_CONFIG_04fc9d7ce1d1c477_EOF + GH_AW_MCP_CONFIG_65224a7dc033dff5_EOF - name: Download activation artifact uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: @@ -1035,7 +1035,7 @@ jobs: uses: actions/github-script@373c709c69115d41ff229c7e5df9f8788daa9553 # v9 env: WORKFLOW_NAME: "Agentic State Machine — Diagram Generator" - WORKFLOW_DESCRIPTION: "Reads all agentic workflow .md files in this repo, extracts the\nstate machine (triggers, labels, actions, handovers between workflows),\nand renders a human-readable diagram in .github/workflows/docs/state-machine.md.\nRuns weekly. Updates the diagram via PR." + WORKFLOW_DESCRIPTION: "Reads all agentic workflow .md files in this repo, extracts the\nstate machine (triggers, labels, actions, handovers between workflows),\nand renders human-readable Mermaid diagrams in .github/workflows/docs/state-machine.md.\nRuns weekly. Only updates if workflow sources changed. Opens a PR." HAS_PATCH: ${{ needs.agent.outputs.has_patch }} with: script: | diff --git a/.github/workflows/agentic-state-machine.md b/.github/workflows/agentic-state-machine.md index 9a8fcc6335..2bbc694fed 100644 --- a/.github/workflows/agentic-state-machine.md +++ b/.github/workflows/agentic-state-machine.md @@ -2,8 +2,8 @@ description: | Reads all agentic workflow .md files in this repo, extracts the state machine (triggers, labels, actions, handovers between workflows), - and renders a human-readable diagram in .github/workflows/docs/state-machine.md. - Runs weekly. Updates the diagram via PR. + and renders human-readable Mermaid diagrams in .github/workflows/docs/state-machine.md. + Runs weekly. Only updates if workflow sources changed. Opens a PR. on: schedule: every 7d @@ -40,69 +40,111 @@ safe-outputs: # Agentic State Machine — Diagram Generator -You read all agentic workflow `.md` files in this repo, extract the inherent state machine they collectively define, and render it as a Mermaid diagram in `.github/workflows/docs/state-machine.md`. +You read all agentic workflow `.md` files in this repo, extract the state machine they collectively define, and render it as Mermaid diagrams in `.github/workflows/docs/state-machine.md`. You produce diagrams that a human can read on one screen and immediately understand. -This repo uses GitHub Agentic Workflows (gh-aw). Each `.md` file in `.github/workflows/` defines an agent with triggers, rules, safe-outputs, and interactions with GitHub objects (issues, PRs, labels, comments). Together, these workflows form an implicit state machine — but no single document shows the full picture. Humans need a diagram. +This repo uses GitHub Agentic Workflows (gh-aw). Each `.md` file in `.github/workflows/` defines an agent with triggers, rules, safe-outputs, and interactions with GitHub objects. Together they form an implicit state machine — but no single document shows the full picture. + +The output must be useful to a maintainer who has never read any workflow file. They should be able to look at the diagrams and understand: what happens to an issue after it's opened? What happens to a PR from a fork? Which workflows talk to each other? Which labels mean what? -1. Read ALL `.md` files in `.github/workflows/` (excluding this one and the `docs/` subfolder). -2. For each workflow, extract: - - **Triggers**: schedule, workflow_dispatch, slash_command, reaction, dispatch from other workflows - - **Inputs**: what GitHub objects it reads (issues, PRs, labels, comments, check runs) - - **Outputs / safe-outputs**: what it can write (labels, comments, PRs, issues, dispatches) - - **Label operations**: which labels it reads (as conditions), adds, or removes - - **Handovers**: does it dispatch another workflow? Does it create PRs that another workflow processes? - - **Conditions**: what filters determine which items it processes (label presence, author, fork status, draft status) -3. Also read `.github/tooling-check-repo-rules.md` if it exists — it may define additional label semantics. -4. Synthesize everything into one coherent state machine covering the full lifecycle of: - - **Issues**: opened → triaged → regression-tested → closed - - **PRs**: opened → scanned → labeled → maintained → merged - - **Labels**: which workflow applies them, what they gate, which workflow reads them - - **Handovers**: workflow A dispatches workflow B, or workflow A creates a PR that workflow B maintains +1. Read ALL `.md` files in `.github/workflows/` (skip this file and the `docs/` subfolder). +2. Also read `.github/tooling-check-repo-rules.md` if it exists. +3. Check if `.github/workflows/docs/state-machine.md` already exists. If it does, read it and compare the workflow source file list + their sizes against what's listed at the bottom of the existing file (a `` marker). If nothing changed, emit `noop` and exit — no update needed. +4. Produce clear, screen-wide diagrams. Split by dimension — do NOT cram everything into one diagram. -1. List all `.md` files in `.github/workflows/` via bash. -2. Read each one. Extract the frontmatter (triggers, safe-outputs, tools) and the body (rules, process, label logic). -3. Build a mental model of the full state machine. -4. Write `.github/workflows/docs/state-machine.md` with: - - a. A **summary table** of all workflows: - | Workflow | Trigger | Reads | Writes | Key Labels | - - b. A **Mermaid diagram** showing the full lifecycle: - - Nodes = states (issue opened, PR opened, PR scanned, PR labeled, etc.) - - Edges = transitions (triggered by schedule, label added, dispatch, etc.) - - Label each edge with the workflow that performs it - - Show handovers between workflows clearly - - c. A **label dictionary** — every label used across all workflows: - | Label | Applied by | Read by | Meaning | - - d. A **handover map** — which workflows interact: - | From | To | Mechanism | - -5. Open a PR with the updated diagram via `create-pull-request`. +1. List `.md` files in `.github/workflows/` via bash. Read each one. +2. For each workflow, extract: + - Triggers (schedule, workflow_dispatch, slash_command, reaction, dispatch-from-other) + - What it reads (issues, PRs, labels, comments, check runs, files) + - What it writes (safe-outputs: labels, comments, PRs, issues, dispatches) + - Label operations (which labels it checks as conditions, adds, or removes) + - Handovers (dispatches to other workflows, creates PRs that other workflows process) + - Filters (author, fork status, draft status, label presence, head SHA) +3. Write `.github/workflows/docs/state-machine.md` with these sections: + +### Section 1: Workflow Overview Table + +| Workflow | Trigger | Reads | Writes | Key Labels | + +### Section 2: Issue Lifecycle Diagram + +A `stateDiagram-v2` showing what happens to issues from creation to resolution. Use composite states for issue types (regression, feature, bug). Show which workflow handles each transition. + +### Section 3: PR Lifecycle Diagram + +A `stateDiagram-v2` with composite states for PR types: +- Fork PRs (scanned, labeled, maintained) +- Non-fork PRs (bypassed) +- Regression-test PRs (created by automation) + +Show: opened → scanned → labeled → CI-maintained → conflict-resolved → merged. Label each edge with the workflow that performs it. + +### Section 4: Label Dictionary + +| Label | Applied by | Read/checked by | Meaning | + +Group by: workflow labels, status labels, category labels. + +### Section 5: Handover Map + +| From workflow | To workflow | Mechanism | When | + +Show dispatch-workflow, PR-creation, label-gating relationships. + +### Section 6: Source Fingerprint + +At the bottom, emit an HTML comment listing the source files and their sizes: +``` + +``` +This is used by rule 3 to detect when re-generation is needed. + +4. Open a PR with the updated file via `create-pull-request`. + +Use Mermaid `stateDiagram-v2` for lifecycle diagrams. Key techniques: + +- **Composite states** for dimensions: `state "Fork PRs" as ForkPR { ... }` +- **Choice nodes** for decision points: `state check <>` +- **Notes** for context: `note right of PRScanned: tooling-check workflow` +- **Direction**: use `direction LR` for wide diagrams that fill the screen +- **Styling**: use `classDef` to color-code by workflow (e.g., blue = repo-assist, orange = labelops) +- **Keep it readable**: max ~15 states per diagram. If more, split into sub-diagrams. +- **Label edges** with the workflow name and trigger: `PROpened --> PRScanned: tooling-check (hourly)` + +For the label dictionary and handover map, use markdown tables — not diagrams. + + -The Mermaid diagram should look something like: +Example of a well-structured Issue Lifecycle diagram: ```mermaid stateDiagram-v2 - [*] --> IssueOpened: issue created - IssueOpened --> IssueTriage: repo-assist (schedule) - IssueTriage --> RegressionPR: repo-assist creates PR + direction LR + + [*] --> Opened: issue created + + state "Triage" as Triage { + Opened --> Classified: repo-assist (12h schedule) + Classified --> RegressionDetected: contains repro steps + Classified --> NeedsInfo: insufficient info + } + + state "Regression Path" as Regression { + RegressionDetected --> TestPRCreated: repo-assist creates PR + TestPRCreated --> TestPRGreen: CI passes + TestPRGreen --> FixPRCreated: regression-pr-shepherd + } - [*] --> PROpened: PR created (fork) - PROpened --> PRScanned: tooling-check (schedule) - PRScanned --> PRLabeled: labels applied - PRLabeled --> PRMaintained: labelops (schedule) - PRMaintained --> PRMerged: human merge + NeedsInfo --> [*]: closed (stale) + FixPRCreated --> [*]: fix merged, issue closed ``` -Adapt to the actual workflows found. Include ALL workflows, ALL labels, ALL transitions. +Adapt to the actual workflows found. This is just a structural example. From 54e9b3b6a5a224865b560c4bd0a7b5d48abccede Mon Sep 17 00:00:00 2001 From: Tomas Grosup Date: Tue, 5 May 2026 21:19:44 +0200 Subject: [PATCH 3/4] Add actors, CI, incremental updates to state machine generator MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Actors: 👤 contributor, 👤 maintainer, 🤖 agent:, ⚙️ CI, ⏰ scheduler Every diagram edge shows WHO performs the transition. CI is always present — PRs go through build/test, agents react to CI results. Never skip the CI step in diagrams. Incremental updates: read existing state-machine.md as starting point, update only sections affected by workflow changes. Minimal diffs. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .github/workflows/agentic-state-machine.md | 89 ++++++++++++++-------- 1 file changed, 56 insertions(+), 33 deletions(-) diff --git a/.github/workflows/agentic-state-machine.md b/.github/workflows/agentic-state-machine.md index 2bbc694fed..cd238f9227 100644 --- a/.github/workflows/agentic-state-machine.md +++ b/.github/workflows/agentic-state-machine.md @@ -52,8 +52,8 @@ The output must be useful to a maintainer who has never read any workflow file. 1. Read ALL `.md` files in `.github/workflows/` (skip this file and the `docs/` subfolder). 2. Also read `.github/tooling-check-repo-rules.md` if it exists. -3. Check if `.github/workflows/docs/state-machine.md` already exists. If it does, read it and compare the workflow source file list + their sizes against what's listed at the bottom of the existing file (a `` marker). If nothing changed, emit `noop` and exit — no update needed. -4. Produce clear, screen-wide diagrams. Split by dimension — do NOT cram everything into one diagram. +3. Check if `.github/workflows/docs/state-machine.md` already exists. If it does, read it and compare the workflow source file list + their sizes against the `` marker at the bottom. If nothing changed, emit `noop` and exit. If sources changed, read the existing file — use it as a starting point, update only the sections affected by the change. Produce a minimal diff. +4. Produce clear, screen-wide diagrams. Split by dimension. Every transition must show which actor performs it. @@ -65,38 +65,49 @@ The output must be useful to a maintainer who has never read any workflow file. - Label operations (which labels it checks as conditions, adds, or removes) - Handovers (dispatches to other workflows, creates PRs that other workflows process) - Filters (author, fork status, draft status, label presence, head SHA) -3. Write `.github/workflows/docs/state-machine.md` with these sections: +3. If `.github/workflows/docs/state-machine.md` already exists, read it. Use it as a starting point — update incrementally rather than rewriting from scratch. This keeps diffs small and reviewable. Only rewrite sections where the underlying workflow changed. +4. Write or update `.github/workflows/docs/state-machine.md` with these sections: ### Section 1: Workflow Overview Table | Workflow | Trigger | Reads | Writes | Key Labels | -### Section 2: Issue Lifecycle Diagram +### Section 2: Actors -A `stateDiagram-v2` showing what happens to issues from creation to resolution. Use composite states for issue types (regression, feature, bug). Show which workflow handles each transition. +Define the actors that appear in the diagrams. Every edge and node should make clear WHO does it: -### Section 3: PR Lifecycle Diagram +- **👤 Human contributor** — opens issues, creates fork PRs, pushes commits, writes PR descriptions +- **👤 Human maintainer** — reviews PRs, applies labels manually, merges, closes issues, runs workflow_dispatch +- **🤖 Agent: \** — each agentic workflow is a named agent (e.g., 🤖 repo-assist, 🤖 regression-pr-shepherd) +- **⚙️ CI** — GitHub Actions CI pipeline: runs builds, tests, produces check statuses (success/failure/pending). CI is not an agent but is a critical actor — it produces the signals that agents react to. +- **⏰ Scheduler** — cron trigger that initiates agent runs + +### Section 3: Issue Lifecycle Diagram + +A `stateDiagram-v2` showing what happens to issues from creation to resolution. Use composite states for issue types (regression, feature, bug). Show which actor handles each transition. Include CI check results where relevant. + +### Section 4: PR Lifecycle Diagram A `stateDiagram-v2` with composite states for PR types: -- Fork PRs (scanned, labeled, maintained) -- Non-fork PRs (bypassed) -- Regression-test PRs (created by automation) +- **Fork PRs** (scanned → labeled → CI runs → maintained → merged) +- **Non-fork PRs** (bypassed scan → CI runs → merged) +- **Regression-test PRs** (created by 🤖 repo-assist → CI validates → 🤖 regression-pr-shepherd maintains) -Show: opened → scanned → labeled → CI-maintained → conflict-resolved → merged. Label each edge with the workflow that performs it. +Show the full cycle including CI: opened → scanned → CI runs → CI passes/fails → agent reacts → human merges. Label each edge with the actor (👤/🤖/⚙️/⏰). -### Section 4: Label Dictionary +### Section 5: Label Dictionary -| Label | Applied by | Read/checked by | Meaning | +| Label | Applied by (actor) | Read/checked by (actor) | Meaning | -Group by: workflow labels, status labels, category labels. +Group by: scan labels, maintenance labels, status labels. -### Section 5: Handover Map +### Section 6: Handover Map -| From workflow | To workflow | Mechanism | When | +| From (actor) | To (actor) | Mechanism | When | -Show dispatch-workflow, PR-creation, label-gating relationships. +Include human → agent handovers (e.g., maintainer applies label → agent starts processing) and agent → agent handovers (dispatch-workflow, PR-creation, label-gating). -### Section 6: Source Fingerprint +### Section 7: Source Fingerprint At the bottom, emit an HTML comment listing the source files and their sizes: ``` @@ -112,39 +123,51 @@ Use Mermaid `stateDiagram-v2` for lifecycle diagrams. Key techniques: - **Composite states** for dimensions: `state "Fork PRs" as ForkPR { ... }` - **Choice nodes** for decision points: `state check <>` -- **Notes** for context: `note right of PRScanned: tooling-check workflow` +- **Notes** for context: `note right of PRScanned: 🤖 tooling-check` - **Direction**: use `direction LR` for wide diagrams that fill the screen -- **Styling**: use `classDef` to color-code by workflow (e.g., blue = repo-assist, orange = labelops) - **Keep it readable**: max ~15 states per diagram. If more, split into sub-diagrams. -- **Label edges** with the workflow name and trigger: `PROpened --> PRScanned: tooling-check (hourly)` +- **Actor prefixes on edges**: always show who performs the transition: + - `👤 contributor opens` / `👤 maintainer merges` / `👤 maintainer applies label` + - `🤖 repo-assist (12h)` / `🤖 tooling-check (1h)` / `🤖 regression-pr-shepherd (4h)` + - `⚙️ CI passes` / `⚙️ CI fails` + - `⏰ schedule triggers` +- **CI is always present**: every PR goes through CI. Show CI check results as states or transitions — never skip the build/test step. For the label dictionary and handover map, use markdown tables — not diagrams. -Example of a well-structured Issue Lifecycle diagram: +Example of a well-structured PR Lifecycle diagram with actors: ```mermaid stateDiagram-v2 direction LR - [*] --> Opened: issue created + [*] --> Opened: 👤 contributor pushes fork PR + + state "Tooling Scan" as Scan { + Opened --> Scanned: 🤖 tooling-check (⏰ 1h) + Scanned --> Labeled: 🤖 applies ⚠️ labels + Scanned --> Clean: 🤖 applies Scanned-Clean + } - state "Triage" as Triage { - Opened --> Classified: repo-assist (12h schedule) - Classified --> RegressionDetected: contains repro steps - Classified --> NeedsInfo: insufficient info + state "CI" as CI { + Labeled --> CIRunning: ⚙️ CI triggered by push + Clean --> CIRunning: ⚙️ CI triggered by push + CIRunning --> CIPassed: ⚙️ all checks green + CIRunning --> CIFailed: ⚙️ check failed } - state "Regression Path" as Regression { - RegressionDetected --> TestPRCreated: repo-assist creates PR - TestPRCreated --> TestPRGreen: CI passes - TestPRGreen --> FixPRCreated: regression-pr-shepherd + state "Maintenance" as Maint { + CIFailed --> CIFixed: 🤖 labelops (⏰ 3h) small fix + CIFailed --> Escalated: 🤖 labelops adds AI-needs-CI-fix-input + CIFailed --> FlakeDispatched: 🤖 labelops dispatches flake-fix } - NeedsInfo --> [*]: closed (stale) - FixPRCreated --> [*]: fix merged, issue closed + CIPassed --> Reviewed: 👤 maintainer reviews + CIFixed --> CIRunning: ⚙️ CI re-triggered + Reviewed --> [*]: 👤 maintainer merges ``` -Adapt to the actual workflows found. This is just a structural example. +Adapt to the actual workflows found. This is just a structural example showing actors. From abddbc1b08b7d24c7aef3606a77b635e56ad0d9b Mon Sep 17 00:00:00 2001 From: Tomas Grosup Date: Tue, 5 May 2026 21:30:52 +0200 Subject: [PATCH 4/4] =?UTF-8?q?Rewrite=20state=20machine=20generator:=2017?= =?UTF-8?q?3=20=E2=86=92=2075=20lines?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adversarial review by Opus 4.7 + 4.6 found 6 blocking issues. Generation test by Opus 4.7 produced excellent output but confirmed the prompt was overfit to current 3 workflows. Fixes applied: - Future-proof: agent discovers lifecycle groups from data, not hardcoded Issue/PR sections. Handles file/branch workflows (aw-auto-update) and meta-workflows (itself) without prompt changes. - Mermaid: cross-composite transitions go outside blocks (syntax fix). Removed broken example. No fake workflow names. - Fingerprint: sha256 content hash instead of file size. - Incremental: reads existing output, updates only changed sections. - Bloat: removed , actor definitions, filler sentences. Cut from 173 to 75 lines — same capability. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../workflows/agentic-state-machine.lock.yml | 33 ++-- .github/workflows/agentic-state-machine.md | 148 +++--------------- 2 files changed, 41 insertions(+), 140 deletions(-) diff --git a/.github/workflows/agentic-state-machine.lock.yml b/.github/workflows/agentic-state-machine.lock.yml index 42fc05ae19..ff10d1e01b 100644 --- a/.github/workflows/agentic-state-machine.lock.yml +++ b/.github/workflows/agentic-state-machine.lock.yml @@ -1,4 +1,4 @@ -# gh-aw-metadata: {"schema_version":"v3","frontmatter_hash":"bb4d98081026580c09afaf506092ca4cb80651e1cc9208091621122e1e5d70d2","compiler_version":"v0.68.3","strict":true,"agent_id":"copilot"} +# gh-aw-metadata: {"schema_version":"v3","frontmatter_hash":"32e6ff156c71bc9c95853126538286990bf9b4ed942be777a5ef01fe8918e1b3","compiler_version":"v0.68.3","strict":true,"agent_id":"copilot"} # gh-aw-manifest: {"version":1,"secrets":["COPILOT_GITHUB_TOKEN","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/upload-artifact","sha":"043fb46d1a93c77aae656e7c1c64a875d1fc6a0a","version":"v7.0.1"},{"repo":"github/gh-aw-actions/setup","sha":"ba90f2186d7ad780ec640f364005fa24e797b360","version":"v0.68.3"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.25.20"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.25.20"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.25.20"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.2.19"},{"image":"ghcr.io/github/github-mcp-server:v0.32.0"},{"image":"node:lts-alpine"}]} # ___ _ _ # / _ \ | | (_) @@ -23,9 +23,8 @@ # For more information: https://github.github.com/gh-aw/introduction/overview/ # # Reads all agentic workflow .md files in this repo, extracts the -# state machine (triggers, labels, actions, handovers between workflows), -# and renders human-readable Mermaid diagrams in .github/workflows/docs/state-machine.md. -# Runs weekly. Only updates if workflow sources changed. Opens a PR. +# state machine they define, renders Mermaid diagrams + tables in +# .github/workflows/docs/state-machine.md. Weekly. Opens PR if changed. # # Secrets used: # - COPILOT_GITHUB_TOKEN @@ -168,19 +167,19 @@ jobs: run: | bash "${RUNNER_TEMP}/gh-aw/actions/create_prompt_first.sh" { - cat << 'GH_AW_PROMPT_e9d49456b580f49e_EOF' + cat << 'GH_AW_PROMPT_c87665cf5d412c8c_EOF' - GH_AW_PROMPT_e9d49456b580f49e_EOF + GH_AW_PROMPT_c87665cf5d412c8c_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_e9d49456b580f49e_EOF' + cat << 'GH_AW_PROMPT_c87665cf5d412c8c_EOF' Tools: create_pull_request, missing_tool, missing_data, noop - GH_AW_PROMPT_e9d49456b580f49e_EOF + GH_AW_PROMPT_c87665cf5d412c8c_EOF cat "${RUNNER_TEMP}/gh-aw/prompts/safe_outputs_create_pull_request.md" - cat << 'GH_AW_PROMPT_e9d49456b580f49e_EOF' + cat << 'GH_AW_PROMPT_c87665cf5d412c8c_EOF' The following GitHub context information is available for this workflow: @@ -210,12 +209,12 @@ jobs: {{/if}} - GH_AW_PROMPT_e9d49456b580f49e_EOF + GH_AW_PROMPT_c87665cf5d412c8c_EOF cat "${RUNNER_TEMP}/gh-aw/prompts/github_mcp_tools_with_safeoutputs_prompt.md" - cat << 'GH_AW_PROMPT_e9d49456b580f49e_EOF' + cat << 'GH_AW_PROMPT_c87665cf5d412c8c_EOF' {{#runtime-import .github/workflows/agentic-state-machine.md}} - GH_AW_PROMPT_e9d49456b580f49e_EOF + GH_AW_PROMPT_c87665cf5d412c8c_EOF } > "$GH_AW_PROMPT" - name: Interpolate variables and render templates uses: actions/github-script@373c709c69115d41ff229c7e5df9f8788daa9553 # v9 @@ -380,9 +379,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_688199fadd8517a4_EOF' + cat > "${RUNNER_TEMP}/gh-aw/safeoutputs/config.json" << 'GH_AW_SAFE_OUTPUTS_CONFIG_0c922306854eb0cb_EOF' {"create_pull_request":{"allowed_files":[".github/workflows/docs/**"],"draft":false,"labels":["automation","NO_RELEASE_NOTES"],"max":1,"max_patch_size":1024,"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"],"protected_files_policy":"fallback-to-issue","protected_path_prefixes":[".github/",".agents/"],"title_prefix":"[Agentic State Machine] "},"create_report_incomplete_issue":{},"missing_data":{},"missing_tool":{},"noop":{"max":1,"report-as-issue":"false"},"report_incomplete":{}} - GH_AW_SAFE_OUTPUTS_CONFIG_688199fadd8517a4_EOF + GH_AW_SAFE_OUTPUTS_CONFIG_0c922306854eb0cb_EOF - name: Write Safe Outputs Tools env: GH_AW_TOOLS_META_JSON: | @@ -578,7 +577,7 @@ jobs: export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -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.19' mkdir -p /home/runner/.copilot - cat << GH_AW_MCP_CONFIG_65224a7dc033dff5_EOF | bash "${RUNNER_TEMP}/gh-aw/actions/start_mcp_gateway.sh" + cat << GH_AW_MCP_CONFIG_799dbde3eaa53aad_EOF | bash "${RUNNER_TEMP}/gh-aw/actions/start_mcp_gateway.sh" { "mcpServers": { "github": { @@ -622,7 +621,7 @@ jobs: "payloadDir": "${MCP_GATEWAY_PAYLOAD_DIR}" } } - GH_AW_MCP_CONFIG_65224a7dc033dff5_EOF + GH_AW_MCP_CONFIG_799dbde3eaa53aad_EOF - name: Download activation artifact uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: @@ -1035,7 +1034,7 @@ jobs: uses: actions/github-script@373c709c69115d41ff229c7e5df9f8788daa9553 # v9 env: WORKFLOW_NAME: "Agentic State Machine — Diagram Generator" - WORKFLOW_DESCRIPTION: "Reads all agentic workflow .md files in this repo, extracts the\nstate machine (triggers, labels, actions, handovers between workflows),\nand renders human-readable Mermaid diagrams in .github/workflows/docs/state-machine.md.\nRuns weekly. Only updates if workflow sources changed. Opens a PR." + WORKFLOW_DESCRIPTION: "Reads all agentic workflow .md files in this repo, extracts the\nstate machine they define, renders Mermaid diagrams + tables in\n.github/workflows/docs/state-machine.md. Weekly. Opens PR if changed." HAS_PATCH: ${{ needs.agent.outputs.has_patch }} with: script: | diff --git a/.github/workflows/agentic-state-machine.md b/.github/workflows/agentic-state-machine.md index cd238f9227..33596153e0 100644 --- a/.github/workflows/agentic-state-machine.md +++ b/.github/workflows/agentic-state-machine.md @@ -1,22 +1,18 @@ --- description: | Reads all agentic workflow .md files in this repo, extracts the - state machine (triggers, labels, actions, handovers between workflows), - and renders human-readable Mermaid diagrams in .github/workflows/docs/state-machine.md. - Runs weekly. Only updates if workflow sources changed. Opens a PR. + state machine they define, renders Mermaid diagrams + tables in + .github/workflows/docs/state-machine.md. Weekly. Opens PR if changed. on: schedule: every 7d workflow_dispatch: timeout-minutes: 15 - permissions: read-all network: - allowed: - - defaults - - github + allowed: [defaults, github] tools: github: @@ -32,142 +28,48 @@ safe-outputs: labels: [automation, NO_RELEASE_NOTES] draft: false max: 1 - allowed-files: - - ".github/workflows/docs/**" + allowed-files: [".github/workflows/docs/**"] protected-files: fallback-to-issue --- # Agentic State Machine — Diagram Generator -You read all agentic workflow `.md` files in this repo, extract the state machine they collectively define, and render it as Mermaid diagrams in `.github/workflows/docs/state-machine.md`. You produce diagrams that a human can read on one screen and immediately understand. +You read all agentic workflow `.md` files in `.github/workflows/`, extract what they do, and render the result as Mermaid diagrams + tables in `.github/workflows/docs/state-machine.md`. - -This repo uses GitHub Agentic Workflows (gh-aw). Each `.md` file in `.github/workflows/` defines an agent with triggers, rules, safe-outputs, and interactions with GitHub objects. Together they form an implicit state machine — but no single document shows the full picture. - -The output must be useful to a maintainer who has never read any workflow file. They should be able to look at the diagrams and understand: what happens to an issue after it's opened? What happens to a PR from a fork? Which workflows talk to each other? Which labels mean what? - - -1. Read ALL `.md` files in `.github/workflows/` (skip this file and the `docs/` subfolder). +1. Read ALL `.md` files in `.github/workflows/` except `docs/` and `agentic-state-machine.md` (this file). 2. Also read `.github/tooling-check-repo-rules.md` if it exists. -3. Check if `.github/workflows/docs/state-machine.md` already exists. If it does, read it and compare the workflow source file list + their sizes against the `` marker at the bottom. If nothing changed, emit `noop` and exit. If sources changed, read the existing file — use it as a starting point, update only the sections affected by the change. Produce a minimal diff. -4. Produce clear, screen-wide diagrams. Split by dimension. Every transition must show which actor performs it. +3. If `.github/workflows/docs/state-machine.md` exists, read it. Compare source hashes in the `` footer against current files (use `sha256sum`). If unchanged → `noop`. If changed → update incrementally, minimal diff. +4. Every transition edge must label its actor: 👤 human, 🤖 agent-name, ⚙️ CI, ⏰ scheduler. +5. Do not hardcode sections for "issues" or "PRs". Discover what lifecycle groups exist from the workflows themselves. A workflow that maintains files/branches is its own group. -1. List `.md` files in `.github/workflows/` via bash. Read each one. -2. For each workflow, extract: - - Triggers (schedule, workflow_dispatch, slash_command, reaction, dispatch-from-other) - - What it reads (issues, PRs, labels, comments, check runs, files) - - What it writes (safe-outputs: labels, comments, PRs, issues, dispatches) - - Label operations (which labels it checks as conditions, adds, or removes) - - Handovers (dispatches to other workflows, creates PRs that other workflows process) - - Filters (author, fork status, draft status, label presence, head SHA) -3. If `.github/workflows/docs/state-machine.md` already exists, read it. Use it as a starting point — update incrementally rather than rewriting from scratch. This keeps diffs small and reviewable. Only rewrite sections where the underlying workflow changed. -4. Write or update `.github/workflows/docs/state-machine.md` with these sections: - -### Section 1: Workflow Overview Table - -| Workflow | Trigger | Reads | Writes | Key Labels | - -### Section 2: Actors - -Define the actors that appear in the diagrams. Every edge and node should make clear WHO does it: - -- **👤 Human contributor** — opens issues, creates fork PRs, pushes commits, writes PR descriptions -- **👤 Human maintainer** — reviews PRs, applies labels manually, merges, closes issues, runs workflow_dispatch -- **🤖 Agent: \** — each agentic workflow is a named agent (e.g., 🤖 repo-assist, 🤖 regression-pr-shepherd) -- **⚙️ CI** — GitHub Actions CI pipeline: runs builds, tests, produces check statuses (success/failure/pending). CI is not an agent but is a critical actor — it produces the signals that agents react to. -- **⏰ Scheduler** — cron trigger that initiates agent runs - -### Section 3: Issue Lifecycle Diagram +1. `ls .github/workflows/*.md` — list source files. Read each. Compute `sha256sum` for fingerprint. +2. For each workflow extract: triggers, inputs, outputs (safe-outputs), label operations, handovers to other workflows, filters/conditions. +3. Group workflows by what they act on. Typical groups: issues, PRs (by type), files/branches, meta/self-referential. Let the data decide — do not force groups. +4. Write `.github/workflows/docs/state-machine.md` with: -A `stateDiagram-v2` showing what happens to issues from creation to resolution. Use composite states for issue types (regression, feature, bug). Show which actor handles each transition. Include CI check results where relevant. + **Workflow overview table** — one row per workflow: trigger, reads, writes, key labels. -### Section 4: PR Lifecycle Diagram + **One Mermaid `stateDiagram-v2` per lifecycle group** — `direction LR`, composite states for sub-types within a group, `<>` for decision points. Max ~15 states per diagram; split if larger. Include ⚙️ CI wherever a workflow reacts to check results. -A `stateDiagram-v2` with composite states for PR types: -- **Fork PRs** (scanned → labeled → CI runs → maintained → merged) -- **Non-fork PRs** (bypassed scan → CI runs → merged) -- **Regression-test PRs** (created by 🤖 repo-assist → CI validates → 🤖 regression-pr-shepherd maintains) + **Label dictionary** — every label: who applies, who reads, meaning. -Show the full cycle including CI: opened → scanned → CI runs → CI passes/fails → agent reacts → human merges. Label each edge with the actor (👤/🤖/⚙️/⏰). + **Handover map** — agent↔agent, human↔agent, scheduler→agent. One table. -### Section 5: Label Dictionary + **Footer**: `` -| Label | Applied by (actor) | Read/checked by (actor) | Meaning | - -Group by: scan labels, maintenance labels, status labels. - -### Section 6: Handover Map - -| From (actor) | To (actor) | Mechanism | When | - -Include human → agent handovers (e.g., maintainer applies label → agent starts processing) and agent → agent handovers (dispatch-workflow, PR-creation, label-gating). - -### Section 7: Source Fingerprint - -At the bottom, emit an HTML comment listing the source files and their sizes: -``` - -``` -This is used by rule 3 to detect when re-generation is needed. - -4. Open a PR with the updated file via `create-pull-request`. +5. Open PR via `create-pull-request`. -Use Mermaid `stateDiagram-v2` for lifecycle diagrams. Key techniques: - -- **Composite states** for dimensions: `state "Fork PRs" as ForkPR { ... }` -- **Choice nodes** for decision points: `state check <>` -- **Notes** for context: `note right of PRScanned: 🤖 tooling-check` -- **Direction**: use `direction LR` for wide diagrams that fill the screen -- **Keep it readable**: max ~15 states per diagram. If more, split into sub-diagrams. -- **Actor prefixes on edges**: always show who performs the transition: - - `👤 contributor opens` / `👤 maintainer merges` / `👤 maintainer applies label` - - `🤖 repo-assist (12h)` / `🤖 tooling-check (1h)` / `🤖 regression-pr-shepherd (4h)` - - `⚙️ CI passes` / `⚙️ CI fails` - - `⏰ schedule triggers` -- **CI is always present**: every PR goes through CI. Show CI check results as states or transitions — never skip the build/test step. - -For the label dictionary and handover map, use markdown tables — not diagrams. +- `stateDiagram-v2`, `direction LR` for wide screen layout. +- Composite states for sub-types: `state "Regression PRs" as RegPR { ... }` +- Cross-composite transitions go OUTSIDE the composite blocks (Mermaid limitation). +- `<>` for decision points, notes for context. +- Actor prefixes on every edge: `🤖 repo-assist (⏰ 12h)`, `⚙️ CI passes`, `👤 maintainer merges`. +- No placeholder/fake names in examples — agent discovers real names from source files. - - -Example of a well-structured PR Lifecycle diagram with actors: - -```mermaid -stateDiagram-v2 - direction LR - - [*] --> Opened: 👤 contributor pushes fork PR - - state "Tooling Scan" as Scan { - Opened --> Scanned: 🤖 tooling-check (⏰ 1h) - Scanned --> Labeled: 🤖 applies ⚠️ labels - Scanned --> Clean: 🤖 applies Scanned-Clean - } - - state "CI" as CI { - Labeled --> CIRunning: ⚙️ CI triggered by push - Clean --> CIRunning: ⚙️ CI triggered by push - CIRunning --> CIPassed: ⚙️ all checks green - CIRunning --> CIFailed: ⚙️ check failed - } - - state "Maintenance" as Maint { - CIFailed --> CIFixed: 🤖 labelops (⏰ 3h) small fix - CIFailed --> Escalated: 🤖 labelops adds AI-needs-CI-fix-input - CIFailed --> FlakeDispatched: 🤖 labelops dispatches flake-fix - } - - CIPassed --> Reviewed: 👤 maintainer reviews - CIFixed --> CIRunning: ⚙️ CI re-triggered - Reviewed --> [*]: 👤 maintainer merges -``` - -Adapt to the actual workflows found. This is just a structural example showing actors. -