From 2bbaeea53aca353959d1ca62b084a7c8baa10c4b Mon Sep 17 00:00:00 2001 From: Matthew Leibowitz Date: Thu, 21 May 2026 04:21:22 +0200 Subject: [PATCH 1/4] Move extraction to regenerate-stubs job and add artifact audit trail - Extract placeholders mechanically in the gen job (Windows) instead of pre-agent-steps, making it an immutable artifact baseline - Upload extracted JSON as 'docs-extracted' artifact (7-day retention) - Pre-agent-steps now downloads pre-extracted JSON instead of running extract - Copy original JSON to /tmp/gh-aw/agent/docs-work-original/ for auto-upload - Post-step copies final JSON to /tmp/gh-aw/agent/docs-work-final/ - This gives full audit trail: diff original vs final = exactly what the agent changed Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .github/workflows/auto-api-docs-writer.md | 28 ++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/.github/workflows/auto-api-docs-writer.md b/.github/workflows/auto-api-docs-writer.md index e205eec..8d57e8e 100644 --- a/.github/workflows/auto-api-docs-writer.md +++ b/.github/workflows/auto-api-docs-writer.md @@ -81,12 +81,23 @@ jobs: run: dotnet cake --target=docs-download-output - name: Regenerate API docs run: dotnet cake --target=update-docs + - name: Extract placeholders and manifest + shell: pwsh + run: | + New-Item -ItemType Directory -Path output/docs-work -Force | Out-Null + & .agents/skills/api-docs/scripts/docs-tool.ps1 extract docs/SkiaSharpAPI/ -Output output/docs-work/ - name: Upload regenerated docs uses: actions/upload-artifact@v4 with: name: docs-regenerated path: docs/SkiaSharpAPI/ retention-days: 1 + - name: Upload extracted JSON (immutable baseline) + uses: actions/upload-artifact@v4 + with: + name: docs-extracted + path: output/docs-work/ + retention-days: 7 # -- Checkout ---------------------------------------------------------- # Primary: this docs repo only. SkiaSharp is cloned in pre-agent-steps. @@ -133,6 +144,12 @@ pre-agent-steps: name: docs-regenerated path: SkiaSharpAPI/ + - name: Download pre-extracted JSON + uses: actions/download-artifact@v4 + with: + name: docs-extracted + path: output/docs-work/ + - name: Clone SkiaSharp (shallow, with submodules) env: SKIASHARP_BRANCH: ${{ inputs.skiasharp_branch || 'main' }} @@ -144,15 +161,20 @@ pre-agent-steps: ln -sfn "$(pwd)/SkiaSharpAPI" skiasharp/docs/SkiaSharpAPI cd skiasharp && dotnet tool restore - - name: Extract placeholders and manifest + - name: Save original JSON to agent artifact cache run: | - mkdir -p output/docs-work - pwsh skiasharp/.agents/skills/api-docs/scripts/docs-tool.ps1 extract SkiaSharpAPI/ -Output output/docs-work/ + mkdir -p /tmp/gh-aw/agent/docs-work-original + cp -r output/docs-work/* /tmp/gh-aw/agent/docs-work-original/ # -- Post-agent steps (host) ------------------------------------------ # Format docs AFTER the agent merges JSON→XML. Runs on host outside the # sandbox so it has full access to the SkiaSharp cake scripts. post-steps: + - name: Save final JSON to agent artifact cache + run: | + mkdir -p /tmp/gh-aw/agent/docs-work-final + cp -r output/docs-work/* /tmp/gh-aw/agent/docs-work-final/ + - name: Format docs run: cd skiasharp && dotnet cake --target=docs-format-docs --- From abb99d045a0137a905a8ff46761ad05eee17d329 Mon Sep 17 00:00:00 2001 From: Matthew Leibowitz Date: Thu, 21 May 2026 04:25:43 +0200 Subject: [PATCH 2/4] Regenerate lock file after workflow changes Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../workflows/auto-api-docs-writer.lock.yml | 56 +++++++++++++------ 1 file changed, 38 insertions(+), 18 deletions(-) diff --git a/.github/workflows/auto-api-docs-writer.lock.yml b/.github/workflows/auto-api-docs-writer.lock.yml index b463f1e..86b486b 100644 --- a/.github/workflows/auto-api-docs-writer.lock.yml +++ b/.github/workflows/auto-api-docs-writer.lock.yml @@ -1,4 +1,4 @@ -# gh-aw-metadata: {"schema_version":"v3","frontmatter_hash":"b01557de21e89fbab66a67c6c4bf090641da579e4df264707ddb91f151015f10","compiler_version":"v0.71.5","strict":true,"agent_id":"copilot"} +# gh-aw-metadata: {"schema_version":"v3","frontmatter_hash":"ee0b83f9557935df28356851297da8f9275b8282c2b09d4c34fea266d3602e16","compiler_version":"v0.71.5","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/cache","sha":"0057852bfaa89a56745cba8c7296529d2fc39830","version":"v4"},{"repo":"actions/checkout","sha":"34e114876b0b11c390a56381ad16ebd13914f8d5","version":"v4"},{"repo":"actions/checkout","sha":"de0fac2e4500dabe0009e67214ff5f5447ce83dd","version":"v6.0.2"},{"repo":"actions/download-artifact","sha":"3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c","version":"v8.0.1"},{"repo":"actions/download-artifact","sha":"d3f86a106a0bac45b974a628896c90dbdf5c8093","version":"v4"},{"repo":"actions/github-script","sha":"3a2844b7e9c422d3c10d287c895573f7108da1b3","version":"v9.0.0"},{"repo":"actions/setup-dotnet","sha":"67a3573c9a986a3f9c594539f4ab511d57bb3ce9","version":"v4"},{"repo":"actions/setup-node","sha":"48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e","version":"v6.4.0"},{"repo":"actions/upload-artifact","sha":"043fb46d1a93c77aae656e7c1c64a875d1fc6a0a","version":"v7.0.1"},{"repo":"actions/upload-artifact","sha":"ea165f8d65b6e75b540449e92b4886f43607fa02","version":"v4"},{"repo":"github/gh-aw-actions/setup","sha":"b8068426813005612b960b5ab0b8bd2c27142323","version":"v0.71.5"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.25.40","digest":"sha256:14ff567e8d9d4c2fbc5e55c973488381c71d7e0fdbe72d30ee7b8a738fd86504","pinned_image":"ghcr.io/github/gh-aw-firewall/agent:0.25.40@sha256:14ff567e8d9d4c2fbc5e55c973488381c71d7e0fdbe72d30ee7b8a738fd86504"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.25.40","digest":"sha256:2883ca3e5ae9f330cafdd9345bfd4ae17fc8da36c96d4c9a1f76e922b4c45280","pinned_image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.25.40@sha256:2883ca3e5ae9f330cafdd9345bfd4ae17fc8da36c96d4c9a1f76e922b4c45280"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.25.40","digest":"sha256:b084f4a2c771f584ee68084ced52fa6b3245197a1889645d817462d307d3ac51","pinned_image":"ghcr.io/github/gh-aw-firewall/squid:0.25.40@sha256:b084f4a2c771f584ee68084ced52fa6b3245197a1889645d817462d307d3ac51"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.3.6","digest":"sha256:2bb8eef86006a4c5963c55616a9c51c32f27bfdecb023b8aa6f91f6718d9171c","pinned_image":"ghcr.io/github/gh-aw-mcpg:v0.3.6@sha256:2bb8eef86006a4c5963c55616a9c51c32f27bfdecb023b8aa6f91f6718d9171c"},{"image":"ghcr.io/github/github-mcp-server:v1.0.3","digest":"sha256:2ac27ef03461ef2b877031b838a7d1fd7f12b12d4ace7796d8cad91446d55959","pinned_image":"ghcr.io/github/github-mcp-server:v1.0.3@sha256:2ac27ef03461ef2b877031b838a7d1fd7f12b12d4ace7796d8cad91446d55959"},{"image":"node:lts-alpine","digest":"sha256:d1b3b4da11eefd5941e7f0b9cf17783fc99d9c6fc34884a665f40a06dbdfc94f","pinned_image":"node:lts-alpine@sha256:d1b3b4da11eefd5941e7f0b9cf17783fc99d9c6fc34884a665f40a06dbdfc94f"}]} # ___ _ _ # / _ \ | | (_) @@ -219,23 +219,23 @@ jobs: run: | bash "${RUNNER_TEMP}/gh-aw/actions/create_prompt_first.sh" { - cat << 'GH_AW_PROMPT_f13c438bc17fdea5_EOF' + cat << 'GH_AW_PROMPT_e5660c6c744a78d6_EOF' - GH_AW_PROMPT_f13c438bc17fdea5_EOF + GH_AW_PROMPT_e5660c6c744a78d6_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_f13c438bc17fdea5_EOF' + cat << 'GH_AW_PROMPT_e5660c6c744a78d6_EOF' Tools: create_pull_request, missing_tool, missing_data, noop - GH_AW_PROMPT_f13c438bc17fdea5_EOF + GH_AW_PROMPT_e5660c6c744a78d6_EOF cat "${RUNNER_TEMP}/gh-aw/prompts/safe_outputs_create_pull_request.md" - cat << 'GH_AW_PROMPT_f13c438bc17fdea5_EOF' + cat << 'GH_AW_PROMPT_e5660c6c744a78d6_EOF' - GH_AW_PROMPT_f13c438bc17fdea5_EOF + GH_AW_PROMPT_e5660c6c744a78d6_EOF cat "${RUNNER_TEMP}/gh-aw/prompts/mcp_cli_tools_prompt.md" - cat << 'GH_AW_PROMPT_f13c438bc17fdea5_EOF' + cat << 'GH_AW_PROMPT_e5660c6c744a78d6_EOF' The following GitHub context information is available for this workflow: {{#if __GH_AW_GITHUB_ACTOR__ }} @@ -267,12 +267,12 @@ jobs: - **Note**: If a branch you need is not in the list above and is not listed as an additional fetched ref, it has NOT been checked out. For private repositories you cannot fetch it without proper authentication. If the branch is required and not available, exit with an error and ask the user to add it to the `fetch:` option of the `checkout:` configuration (e.g., `fetch: ["refs/pulls/open/*"]` for all open PR refs, or `fetch: ["main", "feature/my-branch"]` for specific branches). - GH_AW_PROMPT_f13c438bc17fdea5_EOF + GH_AW_PROMPT_e5660c6c744a78d6_EOF cat "${RUNNER_TEMP}/gh-aw/prompts/github_mcp_tools_with_safeoutputs_prompt.md" - cat << 'GH_AW_PROMPT_f13c438bc17fdea5_EOF' + cat << 'GH_AW_PROMPT_e5660c6c744a78d6_EOF' {{#runtime-import .github/workflows/auto-api-docs-writer.md}} - GH_AW_PROMPT_f13c438bc17fdea5_EOF + GH_AW_PROMPT_e5660c6c744a78d6_EOF } > "$GH_AW_PROMPT" - name: Interpolate variables and render templates uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 @@ -459,14 +459,19 @@ jobs: with: name: docs-regenerated path: SkiaSharpAPI/ + - name: Download pre-extracted JSON + uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4 + with: + name: docs-extracted + path: output/docs-work/ - env: SKIASHARP_BRANCH: ${{ inputs.skiasharp_branch || 'main' }} name: Clone SkiaSharp (shallow, with submodules) run: "git clone --depth 1 --branch \"$SKIASHARP_BRANCH\" \\\n --recurse-submodules --shallow-submodules \\\n https://github.com/mono/SkiaSharp.git skiasharp\nmkdir -p skiasharp/docs\nln -sfn \"$(pwd)/SkiaSharpAPI\" skiasharp/docs/SkiaSharpAPI\ncd skiasharp && dotnet tool restore\n" - - name: Extract placeholders and manifest + - name: Save original JSON to agent artifact cache run: |- - mkdir -p output/docs-work - pwsh skiasharp/.agents/skills/api-docs/scripts/docs-tool.ps1 extract SkiaSharpAPI/ -Output output/docs-work/ + mkdir -p /tmp/gh-aw/agent/docs-work-original + cp -r output/docs-work/* /tmp/gh-aw/agent/docs-work-original/ - name: Download container images run: bash "${RUNNER_TEMP}/gh-aw/actions/download_docker_images.sh" ghcr.io/github/gh-aw-firewall/agent:0.25.40@sha256:14ff567e8d9d4c2fbc5e55c973488381c71d7e0fdbe72d30ee7b8a738fd86504 ghcr.io/github/gh-aw-firewall/api-proxy:0.25.40@sha256:2883ca3e5ae9f330cafdd9345bfd4ae17fc8da36c96d4c9a1f76e922b4c45280 ghcr.io/github/gh-aw-firewall/squid:0.25.40@sha256:b084f4a2c771f584ee68084ced52fa6b3245197a1889645d817462d307d3ac51 ghcr.io/github/gh-aw-mcpg:v0.3.6@sha256:2bb8eef86006a4c5963c55616a9c51c32f27bfdecb023b8aa6f91f6718d9171c ghcr.io/github/github-mcp-server:v1.0.3@sha256:2ac27ef03461ef2b877031b838a7d1fd7f12b12d4ace7796d8cad91446d55959 node:lts-alpine@sha256:d1b3b4da11eefd5941e7f0b9cf17783fc99d9c6fc34884a665f40a06dbdfc94f @@ -475,9 +480,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_cb3252e91cdaec4f_EOF' + cat > "${RUNNER_TEMP}/gh-aw/safeoutputs/config.json" << 'GH_AW_SAFE_OUTPUTS_CONFIG_c2d4d133de8c2ed7_EOF' {"create_pull_request":{"base_branch":"main","draft":false,"max":1,"max_patch_files":100,"max_patch_size":1024,"preserve_branch_name":true,"protect_top_level_dot_folders":true,"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","DESIGN.md","README.md","CONTRIBUTING.md","CHANGELOG.md","SECURITY.md","CODE_OF_CONDUCT.md","AGENTS.md","CLAUDE.md","GEMINI.md"],"recreate_ref":true},"create_report_incomplete_issue":{},"missing_data":{},"missing_tool":{},"noop":{"max":1,"report-as-issue":"true"},"report_incomplete":{}} - GH_AW_SAFE_OUTPUTS_CONFIG_cb3252e91cdaec4f_EOF + GH_AW_SAFE_OUTPUTS_CONFIG_c2d4d133de8c2ed7_EOF - name: Generate Safe Outputs Tools env: GH_AW_TOOLS_META_JSON: | @@ -683,7 +688,7 @@ jobs: mkdir -p /home/runner/.copilot GH_AW_NODE=$(which node 2>/dev/null || command -v node 2>/dev/null || echo node) - cat << GH_AW_MCP_CONFIG_f43305c2a999d47d_EOF | "$GH_AW_NODE" "${RUNNER_TEMP}/gh-aw/actions/start_mcp_gateway.cjs" + cat << GH_AW_MCP_CONFIG_25debe57faeea757_EOF | "$GH_AW_NODE" "${RUNNER_TEMP}/gh-aw/actions/start_mcp_gateway.cjs" { "mcpServers": { "github": { @@ -731,7 +736,7 @@ jobs: "payloadDir": "${MCP_GATEWAY_PAYLOAD_DIR}" } } - GH_AW_MCP_CONFIG_f43305c2a999d47d_EOF + GH_AW_MCP_CONFIG_25debe57faeea757_EOF - name: Mount MCP servers as CLIs id: mount-mcp-clis continue-on-error: true @@ -924,6 +929,10 @@ jobs: if [ ! -f /tmp/gh-aw/agent_output.json ]; then echo '{"items":[]}' > /tmp/gh-aw/agent_output.json fi + - name: Save final JSON to agent artifact cache + run: | + mkdir -p /tmp/gh-aw/agent/docs-work-final + cp -r output/docs-work/* /tmp/gh-aw/agent/docs-work-final/ - name: Format docs run: cd skiasharp && dotnet cake --target=docs-format-docs @@ -1388,12 +1397,23 @@ jobs: run: dotnet cake --target=docs-download-output - name: Regenerate API docs run: dotnet cake --target=update-docs + - name: Extract placeholders and manifest + run: | + New-Item -ItemType Directory -Path output/docs-work -Force | Out-Null + & .agents/skills/api-docs/scripts/docs-tool.ps1 extract docs/SkiaSharpAPI/ -Output output/docs-work/ + shell: pwsh - name: Upload regenerated docs uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 with: name: docs-regenerated path: docs/SkiaSharpAPI/ retention-days: 1 + - name: Upload extracted JSON (immutable baseline) + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 + with: + name: docs-extracted + path: output/docs-work/ + retention-days: 7 safe_outputs: needs: From 45608f233e7b841bf27a34dead75dd38a1f5cc00 Mon Sep 17 00:00:00 2001 From: Matthew Leibowitz Date: Thu, 21 May 2026 18:56:40 +0200 Subject: [PATCH 3/4] Add completion gate and read_agent wait rules to prevent session termination MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The orchestrator in run 2 launched the writer agent, said 'waiting', and ended its turn with no tool call — causing the runtime to terminate the session. The writer did complete (2.1M tokens consumed) but the orchestrator never read its result. Fix: explicit rules requiring read_agent(wait=true) in the same turn as agent launch, plus a completion gate that prevents exiting without a safe output call. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .github/workflows/auto-api-docs-writer.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/auto-api-docs-writer.md b/.github/workflows/auto-api-docs-writer.md index 8d57e8e..1e1b8a7 100644 --- a/.github/workflows/auto-api-docs-writer.md +++ b/.github/workflows/auto-api-docs-writer.md @@ -230,6 +230,8 @@ If there are no documentation changes after merging, call the `noop` tool instea - **Phase 6 MUST run.** If you skip the merge, no PR is created and the entire run is wasted. - **Do NOT run `docs-format-docs`** — formatting runs automatically as a post-step. - **Budget awareness:** After the writer completes and reviewers report, fix CRITICAL issues and proceed to merge+PR immediately. Do not re-run reviewers unless absolutely necessary. +- **NEVER end a turn without a tool call while waiting for agents.** When you launch a background agent and need its result, you MUST call `read_agent` with `wait: true` in the SAME turn. Do NOT just say "waiting" in text and end your turn — the session will terminate. The pattern is: launch agent → immediately call `read_agent(agent_id, wait=true)` → process result → continue. +- **COMPLETION GATE:** Your session is NOT complete until you have called `create_pull_request` or `noop`. If you reach a point where you think you're done but haven't called either, something went wrong — retrace your steps and complete the remaining phases. ## Path differences from SKILL.md From 716704d4314380c7ac5b5800512438187585e324 Mon Sep 17 00:00:00 2001 From: Matthew Leibowitz Date: Thu, 21 May 2026 19:59:40 +0200 Subject: [PATCH 4/4] Strengthen multi-agent wait rules and add budget fallback MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Run 5 reproduced the run 2 failure: orchestrator launched 3 reviewer agents, said 'waiting', ended its turn with no active tool call, and the session terminated (13m 44s). The writer phase DID use read_agent correctly (our previous fix worked) but the parallel reviewers triggered the same pattern. Fixes: - Explicit multi-agent pattern: call read_agent sequentially for each agent - FORBIDDEN pattern documented explicitly (launch → text → end turn) - Budget fallback: skip Phase 5 if past 10 minutes without merge - Stronger wording: session WILL terminate (not 'may') Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .github/workflows/auto-api-docs-writer.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/.github/workflows/auto-api-docs-writer.md b/.github/workflows/auto-api-docs-writer.md index 1e1b8a7..ab56fd5 100644 --- a/.github/workflows/auto-api-docs-writer.md +++ b/.github/workflows/auto-api-docs-writer.md @@ -229,8 +229,11 @@ If there are no documentation changes after merging, call the `noop` tool instea - **Do NOT edit XML files directly** — edit only the JSON files in `output/docs-work/`. - **Phase 6 MUST run.** If you skip the merge, no PR is created and the entire run is wasted. - **Do NOT run `docs-format-docs`** — formatting runs automatically as a post-step. -- **Budget awareness:** After the writer completes and reviewers report, fix CRITICAL issues and proceed to merge+PR immediately. Do not re-run reviewers unless absolutely necessary. -- **NEVER end a turn without a tool call while waiting for agents.** When you launch a background agent and need its result, you MUST call `read_agent` with `wait: true` in the SAME turn. Do NOT just say "waiting" in text and end your turn — the session will terminate. The pattern is: launch agent → immediately call `read_agent(agent_id, wait=true)` → process result → continue. +- **Budget awareness:** After the writer completes and reviewers report, fix CRITICAL issues and proceed to merge+PR immediately. Do not re-run reviewers unless absolutely necessary. **If you're past 10 minutes and haven't merged yet, skip Phase 5 (review) entirely and go straight to Phase 6 (merge) + PR. A PR without review is better than no PR.** +- **NEVER end a turn without a tool call while waiting for agents.** When you launch a background agent, you MUST call `read_agent` with `wait: true` in the SAME response. Do NOT output text saying "waiting" and end your turn — the session WILL terminate and all work is lost. + - **Single agent:** `task(background)` + `read_agent(id, wait=true)` in the same response. + - **Multiple agents:** Launch all agents, then call `read_agent` for THE FIRST ONE with `wait: true`. When it returns, call `read_agent` for the next, and so on. You MUST have an active `read_agent` call at all times until all agents complete. + - **FORBIDDEN pattern:** Launching agents → saying "Waiting for them to complete" → ending turn. This KILLS the session. - **COMPLETION GATE:** Your session is NOT complete until you have called `create_pull_request` or `noop`. If you reach a point where you think you're done but haven't called either, something went wrong — retrace your steps and complete the remaining phases. ## Path differences from SKILL.md