From 207a4654d975140f5939c3f260da4eaa75e41ade Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 27 Aug 2026 11:06:25 +0000 Subject: [PATCH 1/6] Initial plan From ede7631e9370b003b9c31ea7250852acc2359d98 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 27 Aug 2026 11:19:13 +0000 Subject: [PATCH 2/6] fix: tighten design-decision-gate instructions to avoid runaway turns Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- .github/workflows/design-decision-gate.lock.yml | 2 +- .github/workflows/design-decision-gate.md | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/design-decision-gate.lock.yml b/.github/workflows/design-decision-gate.lock.yml index 7f00fef3c2e..686049978ba 100644 --- a/.github/workflows/design-decision-gate.lock.yml +++ b/.github/workflows/design-decision-gate.lock.yml @@ -1,4 +1,4 @@ -# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"7e7e7984d326866fa326cb765cc4d0213428fbd4424ec2c9de0ea7c5d0a91a78","body_hash":"1c9861a959a33041af262a50bb0a41a91543fd413e2b033d6d18a3aa26885e41","strict":true,"agent_id":"claude","agent_model":"claude-sonnet-4-6","engine_versions":{"claude":"2.1.245"}} +# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"7e7e7984d326866fa326cb765cc4d0213428fbd4424ec2c9de0ea7c5d0a91a78","body_hash":"717b361e38eb6e49e9046857379d72984007b23edf466ffb278a4b22b101496f","strict":true,"agent_id":"claude","agent_model":"claude-sonnet-4-6","engine_versions":{"claude":"2.1.245"}} # gh-aw-manifest: {"version":1,"secrets":["ANTHROPIC_API_KEY","COPILOT_GITHUB_TOKEN","GH_AW_CI_TRIGGER_TOKEN","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GH_AW_OTEL_GRAFANA_AUTHORIZATION","GH_AW_OTEL_GRAFANA_ENDPOINT","GH_AW_OTEL_SENTRY_AUTHORIZATION","GH_AW_OTEL_SENTRY_ENDPOINT","GITHUB_TOKEN"],"actions":[{"repo":"actions/cache/restore","sha":"55cc8345863c7cc4c66a329aec7e433d2d1c52a9","version":"v6.1.0"},{"repo":"actions/cache/save","sha":"55cc8345863c7cc4c66a329aec7e433d2d1c52a9","version":"v6.1.0"},{"repo":"actions/checkout","sha":"3d3c42e5aac5ba805825da76410c181273ba90b1","version":"v7.0.1"},{"repo":"actions/download-artifact","sha":"3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c","version":"v8.0.1"},{"repo":"actions/github-script","sha":"3a2844b7e9c422d3c10d287c895573f7108da1b3","version":"v9.0.0"},{"repo":"actions/setup-node","sha":"820762786026740c76f36085b0efc47a31fe5020","version":"v7.0.0"},{"repo":"actions/upload-artifact","sha":"043fb46d1a93c77aae656e7c1c64a875d1fc6a0a","version":"v7.0.1"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.28.7","digest":"sha256:40a1e30b1b8d70642d4292485146cd5af612730d7a6a2e12706ddd13df375059","pinned_image":"ghcr.io/github/gh-aw-firewall/agent:0.28.7@sha256:40a1e30b1b8d70642d4292485146cd5af612730d7a6a2e12706ddd13df375059"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.28.7","digest":"sha256:4f209dd4cbc74d47a6c7379956143de293429d1b1b2fb2647776cdcbf65836a1","pinned_image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.28.7@sha256:4f209dd4cbc74d47a6c7379956143de293429d1b1b2fb2647776cdcbf65836a1"},{"image":"ghcr.io/github/gh-aw-firewall/cli-proxy:0.28.7","digest":"sha256:ebc8758c9b085ca244234e3e3ee22300d150095f9bfe7312ca8a61c5acb34a78","pinned_image":"ghcr.io/github/gh-aw-firewall/cli-proxy:0.28.7@sha256:ebc8758c9b085ca244234e3e3ee22300d150095f9bfe7312ca8a61c5acb34a78"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.28.7","digest":"sha256:fb362a08d4d2f0da6c036e3f5d3b2fd87931e857fec3ca4a241cd2f2b61131f9","pinned_image":"ghcr.io/github/gh-aw-firewall/squid:0.28.7@sha256:fb362a08d4d2f0da6c036e3f5d3b2fd87931e857fec3ca4a241cd2f2b61131f9"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.4.10","digest":"sha256:08bb5fa417aed94b40a14e2b7b3ae457531a5f22b143a32fe58317139d9b8f42","pinned_image":"ghcr.io/github/gh-aw-mcpg:v0.4.10@sha256:08bb5fa417aed94b40a14e2b7b3ae457531a5f22b143a32fe58317139d9b8f42"},{"image":"ghcr.io/github/gh-aw-node","digest":"sha256:bac2192f6374d6262116399b34fc5e143d576f82719e90a18261cae7480f4d4e","pinned_image":"ghcr.io/github/gh-aw-node@sha256:bac2192f6374d6262116399b34fc5e143d576f82719e90a18261cae7480f4d4e"},{"image":"ghcr.io/github/github-mcp-server:v1.10.1","digest":"sha256:1817b57d43916532dc002bdc5f344d639bd9fb54a9148d42168458f7c3280567","pinned_image":"ghcr.io/github/github-mcp-server:v1.10.1@sha256:1817b57d43916532dc002bdc5f344d639bd9fb54a9148d42168458f7c3280567"}],"has_pull_request":true,"mcp_servers":[{"name":"safeoutputs","tools":["add_comment","missing_data","missing_tool","noop","push_to_pull_request_branch"]}]} # This file was automatically generated by gh-aw. DO NOT EDIT. To debug this workflow, load the skill at https://github.com/github/gh-aw/blob/main/debug.md # diff --git a/.github/workflows/design-decision-gate.md b/.github/workflows/design-decision-gate.md index e509f95a27d..e7a86416bb2 100644 --- a/.github/workflows/design-decision-gate.md +++ b/.github/workflows/design-decision-gate.md @@ -227,7 +227,7 @@ Stop and emit a safe output **immediately** when any of the following is true: - Missing `pr.diff` → `mcp__github__get_pull_request_diff` (only if `diff_available` is `true` in the summary; if `false`, the diff exceeds the 300-file API limit — use `pr-files.json` instead and do **not** call the diff API) - Missing `adr-prefetch-summary.json` → compute manually from PR files and labels 3. Do **not** perform broad exploration. Only fetch extra data if a required field is missing from pre-fetched files. -4. Do not use the `Agent` or `Task` tools, delegate work, run `git push`, or investigate safe-output tool availability or permissions. The required safe-output tools are available directly. +4. Do not use the `Agent`, `Task`, or `Skill` tools, delegate work, run `git add`/`git commit`/`git push`/`gh pr comment`, or investigate safe-output tool availability or permissions. The required safe-output tools are available directly. 5. Call only the minimum final safe outputs, then stop: use `noop` or `add-comment` for every non-draft outcome; when a draft ADR is generated, call `push-to-pull-request-branch` and then `add-comment`. 6. If you have enough evidence to decide, stop immediately. Do not gather optional data. @@ -344,7 +344,7 @@ Generate a draft ADR file using the imported `adr-writer` template. Fill the Mic mkdir -p ${{ github.workspace }}/docs/adr ``` -4. **Post the blocking comment.** Read the `adr-report-templates` skill and use `add-comment` with the **ADR Required** template. +4. **Post the blocking comment.** Use `add-comment` with the **ADR Required** template from the **ADR Report Templates** section below. ### ADR Found — Verify Implementation @@ -362,7 +362,7 @@ If an ADR **is** found (either in the PR body, on the PR branch, or in a linked 3. **Scope creep** — Significant architectural changes not covered by the ADR 4. **Full alignment** — Code faithfully implements the stated decision -3. **Report findings.** Read the `adr-report-templates` skill and use `add-comment` with the matching template: +3. **Report findings.** Use `add-comment` with the matching template from the **ADR Report Templates** section below: - **If the implementation MATCHES the ADR**: use the **ADR Verified** template. - **If there are DIVERGENCES**: use the **Implementation Diverges** template. @@ -375,7 +375,7 @@ If an ADR **is** found (either in the PR body, on the PR branch, or in a linked {"noop": {"message": "No action needed: [brief explanation of what was found and why no action was required]"}} ``` -## skill: `adr-report-templates` +## ADR Report Templates --- description: PR comment templates for the Design Decision Gate (ADR Required, ADR Verified, and Implementation Diverges). --- From 36a3c4ed4b125747eae50a9548b3e2ceca70924c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 27 Aug 2026 11:53:15 +0000 Subject: [PATCH 3/6] fix: restore ADR inline skill block and explicit terminator Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- .github/workflows/design-decision-gate.lock.yml | 2 +- .github/workflows/design-decision-gate.md | 11 ++++++----- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/.github/workflows/design-decision-gate.lock.yml b/.github/workflows/design-decision-gate.lock.yml index 686049978ba..5a2d4c187e8 100644 --- a/.github/workflows/design-decision-gate.lock.yml +++ b/.github/workflows/design-decision-gate.lock.yml @@ -1,4 +1,4 @@ -# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"7e7e7984d326866fa326cb765cc4d0213428fbd4424ec2c9de0ea7c5d0a91a78","body_hash":"717b361e38eb6e49e9046857379d72984007b23edf466ffb278a4b22b101496f","strict":true,"agent_id":"claude","agent_model":"claude-sonnet-4-6","engine_versions":{"claude":"2.1.245"}} +# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"7e7e7984d326866fa326cb765cc4d0213428fbd4424ec2c9de0ea7c5d0a91a78","body_hash":"7d29a9747dead226db68ef157b81153cf8bcfed9287b2246edc4612297eb3a07","strict":true,"agent_id":"claude","agent_model":"claude-sonnet-4-6","engine_versions":{"claude":"2.1.245"}} # gh-aw-manifest: {"version":1,"secrets":["ANTHROPIC_API_KEY","COPILOT_GITHUB_TOKEN","GH_AW_CI_TRIGGER_TOKEN","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GH_AW_OTEL_GRAFANA_AUTHORIZATION","GH_AW_OTEL_GRAFANA_ENDPOINT","GH_AW_OTEL_SENTRY_AUTHORIZATION","GH_AW_OTEL_SENTRY_ENDPOINT","GITHUB_TOKEN"],"actions":[{"repo":"actions/cache/restore","sha":"55cc8345863c7cc4c66a329aec7e433d2d1c52a9","version":"v6.1.0"},{"repo":"actions/cache/save","sha":"55cc8345863c7cc4c66a329aec7e433d2d1c52a9","version":"v6.1.0"},{"repo":"actions/checkout","sha":"3d3c42e5aac5ba805825da76410c181273ba90b1","version":"v7.0.1"},{"repo":"actions/download-artifact","sha":"3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c","version":"v8.0.1"},{"repo":"actions/github-script","sha":"3a2844b7e9c422d3c10d287c895573f7108da1b3","version":"v9.0.0"},{"repo":"actions/setup-node","sha":"820762786026740c76f36085b0efc47a31fe5020","version":"v7.0.0"},{"repo":"actions/upload-artifact","sha":"043fb46d1a93c77aae656e7c1c64a875d1fc6a0a","version":"v7.0.1"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.28.7","digest":"sha256:40a1e30b1b8d70642d4292485146cd5af612730d7a6a2e12706ddd13df375059","pinned_image":"ghcr.io/github/gh-aw-firewall/agent:0.28.7@sha256:40a1e30b1b8d70642d4292485146cd5af612730d7a6a2e12706ddd13df375059"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.28.7","digest":"sha256:4f209dd4cbc74d47a6c7379956143de293429d1b1b2fb2647776cdcbf65836a1","pinned_image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.28.7@sha256:4f209dd4cbc74d47a6c7379956143de293429d1b1b2fb2647776cdcbf65836a1"},{"image":"ghcr.io/github/gh-aw-firewall/cli-proxy:0.28.7","digest":"sha256:ebc8758c9b085ca244234e3e3ee22300d150095f9bfe7312ca8a61c5acb34a78","pinned_image":"ghcr.io/github/gh-aw-firewall/cli-proxy:0.28.7@sha256:ebc8758c9b085ca244234e3e3ee22300d150095f9bfe7312ca8a61c5acb34a78"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.28.7","digest":"sha256:fb362a08d4d2f0da6c036e3f5d3b2fd87931e857fec3ca4a241cd2f2b61131f9","pinned_image":"ghcr.io/github/gh-aw-firewall/squid:0.28.7@sha256:fb362a08d4d2f0da6c036e3f5d3b2fd87931e857fec3ca4a241cd2f2b61131f9"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.4.10","digest":"sha256:08bb5fa417aed94b40a14e2b7b3ae457531a5f22b143a32fe58317139d9b8f42","pinned_image":"ghcr.io/github/gh-aw-mcpg:v0.4.10@sha256:08bb5fa417aed94b40a14e2b7b3ae457531a5f22b143a32fe58317139d9b8f42"},{"image":"ghcr.io/github/gh-aw-node","digest":"sha256:bac2192f6374d6262116399b34fc5e143d576f82719e90a18261cae7480f4d4e","pinned_image":"ghcr.io/github/gh-aw-node@sha256:bac2192f6374d6262116399b34fc5e143d576f82719e90a18261cae7480f4d4e"},{"image":"ghcr.io/github/github-mcp-server:v1.10.1","digest":"sha256:1817b57d43916532dc002bdc5f344d639bd9fb54a9148d42168458f7c3280567","pinned_image":"ghcr.io/github/github-mcp-server:v1.10.1@sha256:1817b57d43916532dc002bdc5f344d639bd9fb54a9148d42168458f7c3280567"}],"has_pull_request":true,"mcp_servers":[{"name":"safeoutputs","tools":["add_comment","missing_data","missing_tool","noop","push_to_pull_request_branch"]}]} # This file was automatically generated by gh-aw. DO NOT EDIT. To debug this workflow, load the skill at https://github.com/github/gh-aw/blob/main/debug.md # diff --git a/.github/workflows/design-decision-gate.md b/.github/workflows/design-decision-gate.md index e7a86416bb2..2c5d671e910 100644 --- a/.github/workflows/design-decision-gate.md +++ b/.github/workflows/design-decision-gate.md @@ -227,7 +227,7 @@ Stop and emit a safe output **immediately** when any of the following is true: - Missing `pr.diff` → `mcp__github__get_pull_request_diff` (only if `diff_available` is `true` in the summary; if `false`, the diff exceeds the 300-file API limit — use `pr-files.json` instead and do **not** call the diff API) - Missing `adr-prefetch-summary.json` → compute manually from PR files and labels 3. Do **not** perform broad exploration. Only fetch extra data if a required field is missing from pre-fetched files. -4. Do not use the `Agent`, `Task`, or `Skill` tools, delegate work, run `git add`/`git commit`/`git push`/`gh pr comment`, or investigate safe-output tool availability or permissions. The required safe-output tools are available directly. +4. Do not use the `Agent` or `Task` tools, delegate work, run `git push`, or investigate safe-output tool availability or permissions. The required safe-output tools are available directly. 5. Call only the minimum final safe outputs, then stop: use `noop` or `add-comment` for every non-draft outcome; when a draft ADR is generated, call `push-to-pull-request-branch` and then `add-comment`. 6. If you have enough evidence to decide, stop immediately. Do not gather optional data. @@ -344,7 +344,7 @@ Generate a draft ADR file using the imported `adr-writer` template. Fill the Mic mkdir -p ${{ github.workspace }}/docs/adr ``` -4. **Post the blocking comment.** Use `add-comment` with the **ADR Required** template from the **ADR Report Templates** section below. +4. **Post the blocking comment.** Read the `adr-report-templates` skill and use `add-comment` with the **ADR Required** template. ### ADR Found — Verify Implementation @@ -362,7 +362,7 @@ If an ADR **is** found (either in the PR body, on the PR branch, or in a linked 3. **Scope creep** — Significant architectural changes not covered by the ADR 4. **Full alignment** — Code faithfully implements the stated decision -3. **Report findings.** Use `add-comment` with the matching template from the **ADR Report Templates** section below: +3. **Report findings.** Read the `adr-report-templates` skill and use `add-comment` with the matching template: - **If the implementation MATCHES the ADR**: use the **ADR Verified** template. - **If there are DIVERGENCES**: use the **Implementation Diverges** template. @@ -375,7 +375,7 @@ If an ADR **is** found (either in the PR body, on the PR branch, or in a linked {"noop": {"message": "No action needed: [brief explanation of what was found and why no action was required]"}} ``` -## ADR Report Templates +## skill: `adr-report-templates` --- description: PR comment templates for the Design Decision Gate (ADR Required, ADR Verified, and Implementation Diverges). --- @@ -471,4 +471,5 @@ Either: The ADR and implementation must be in sync before this PR can merge. -``` \ No newline at end of file +``` +## end skill: `adr-report-templates` \ No newline at end of file From a9f1d60f3abee8c5186dcaf58a8364383242fe73 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 27 Aug 2026 12:10:07 +0000 Subject: [PATCH 4/6] fix: keep inline skill EOF boundary implicit Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- .github/workflows/design-decision-gate.lock.yml | 2 +- .github/workflows/design-decision-gate.md | 3 +-- actions/setup/js/extract_inline_skills.cjs | 10 ++++++---- actions/setup/js/extract_inline_skills.test.cjs | 4 ++-- actions/setup/js/runtime_import.test.cjs | 14 ++++++-------- pkg/parser/inline_skill_extractor_test.go | 14 ++++++++++++++ 6 files changed, 30 insertions(+), 17 deletions(-) diff --git a/.github/workflows/design-decision-gate.lock.yml b/.github/workflows/design-decision-gate.lock.yml index 5a2d4c187e8..7f00fef3c2e 100644 --- a/.github/workflows/design-decision-gate.lock.yml +++ b/.github/workflows/design-decision-gate.lock.yml @@ -1,4 +1,4 @@ -# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"7e7e7984d326866fa326cb765cc4d0213428fbd4424ec2c9de0ea7c5d0a91a78","body_hash":"7d29a9747dead226db68ef157b81153cf8bcfed9287b2246edc4612297eb3a07","strict":true,"agent_id":"claude","agent_model":"claude-sonnet-4-6","engine_versions":{"claude":"2.1.245"}} +# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"7e7e7984d326866fa326cb765cc4d0213428fbd4424ec2c9de0ea7c5d0a91a78","body_hash":"1c9861a959a33041af262a50bb0a41a91543fd413e2b033d6d18a3aa26885e41","strict":true,"agent_id":"claude","agent_model":"claude-sonnet-4-6","engine_versions":{"claude":"2.1.245"}} # gh-aw-manifest: {"version":1,"secrets":["ANTHROPIC_API_KEY","COPILOT_GITHUB_TOKEN","GH_AW_CI_TRIGGER_TOKEN","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GH_AW_OTEL_GRAFANA_AUTHORIZATION","GH_AW_OTEL_GRAFANA_ENDPOINT","GH_AW_OTEL_SENTRY_AUTHORIZATION","GH_AW_OTEL_SENTRY_ENDPOINT","GITHUB_TOKEN"],"actions":[{"repo":"actions/cache/restore","sha":"55cc8345863c7cc4c66a329aec7e433d2d1c52a9","version":"v6.1.0"},{"repo":"actions/cache/save","sha":"55cc8345863c7cc4c66a329aec7e433d2d1c52a9","version":"v6.1.0"},{"repo":"actions/checkout","sha":"3d3c42e5aac5ba805825da76410c181273ba90b1","version":"v7.0.1"},{"repo":"actions/download-artifact","sha":"3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c","version":"v8.0.1"},{"repo":"actions/github-script","sha":"3a2844b7e9c422d3c10d287c895573f7108da1b3","version":"v9.0.0"},{"repo":"actions/setup-node","sha":"820762786026740c76f36085b0efc47a31fe5020","version":"v7.0.0"},{"repo":"actions/upload-artifact","sha":"043fb46d1a93c77aae656e7c1c64a875d1fc6a0a","version":"v7.0.1"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.28.7","digest":"sha256:40a1e30b1b8d70642d4292485146cd5af612730d7a6a2e12706ddd13df375059","pinned_image":"ghcr.io/github/gh-aw-firewall/agent:0.28.7@sha256:40a1e30b1b8d70642d4292485146cd5af612730d7a6a2e12706ddd13df375059"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.28.7","digest":"sha256:4f209dd4cbc74d47a6c7379956143de293429d1b1b2fb2647776cdcbf65836a1","pinned_image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.28.7@sha256:4f209dd4cbc74d47a6c7379956143de293429d1b1b2fb2647776cdcbf65836a1"},{"image":"ghcr.io/github/gh-aw-firewall/cli-proxy:0.28.7","digest":"sha256:ebc8758c9b085ca244234e3e3ee22300d150095f9bfe7312ca8a61c5acb34a78","pinned_image":"ghcr.io/github/gh-aw-firewall/cli-proxy:0.28.7@sha256:ebc8758c9b085ca244234e3e3ee22300d150095f9bfe7312ca8a61c5acb34a78"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.28.7","digest":"sha256:fb362a08d4d2f0da6c036e3f5d3b2fd87931e857fec3ca4a241cd2f2b61131f9","pinned_image":"ghcr.io/github/gh-aw-firewall/squid:0.28.7@sha256:fb362a08d4d2f0da6c036e3f5d3b2fd87931e857fec3ca4a241cd2f2b61131f9"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.4.10","digest":"sha256:08bb5fa417aed94b40a14e2b7b3ae457531a5f22b143a32fe58317139d9b8f42","pinned_image":"ghcr.io/github/gh-aw-mcpg:v0.4.10@sha256:08bb5fa417aed94b40a14e2b7b3ae457531a5f22b143a32fe58317139d9b8f42"},{"image":"ghcr.io/github/gh-aw-node","digest":"sha256:bac2192f6374d6262116399b34fc5e143d576f82719e90a18261cae7480f4d4e","pinned_image":"ghcr.io/github/gh-aw-node@sha256:bac2192f6374d6262116399b34fc5e143d576f82719e90a18261cae7480f4d4e"},{"image":"ghcr.io/github/github-mcp-server:v1.10.1","digest":"sha256:1817b57d43916532dc002bdc5f344d639bd9fb54a9148d42168458f7c3280567","pinned_image":"ghcr.io/github/github-mcp-server:v1.10.1@sha256:1817b57d43916532dc002bdc5f344d639bd9fb54a9148d42168458f7c3280567"}],"has_pull_request":true,"mcp_servers":[{"name":"safeoutputs","tools":["add_comment","missing_data","missing_tool","noop","push_to_pull_request_branch"]}]} # This file was automatically generated by gh-aw. DO NOT EDIT. To debug this workflow, load the skill at https://github.com/github/gh-aw/blob/main/debug.md # diff --git a/.github/workflows/design-decision-gate.md b/.github/workflows/design-decision-gate.md index 2c5d671e910..e509f95a27d 100644 --- a/.github/workflows/design-decision-gate.md +++ b/.github/workflows/design-decision-gate.md @@ -471,5 +471,4 @@ Either: The ADR and implementation must be in sync before this PR can merge. -``` -## end skill: `adr-report-templates` \ No newline at end of file +``` \ No newline at end of file diff --git a/actions/setup/js/extract_inline_skills.cjs b/actions/setup/js/extract_inline_skills.cjs index e432d2bcfbf..afd527d529b 100644 --- a/actions/setup/js/extract_inline_skills.cjs +++ b/actions/setup/js/extract_inline_skills.cjs @@ -229,9 +229,9 @@ function extractInlineSkills(content) { /** * Ensures every "## skill: `name`" start marker in content has a matching - * explicit "## end skill: `name`" marker, inserting one at the block's - * implicit boundary (next H2 heading or EOF) for any start marker that - * doesn't already have one. + * explicit "## end skill: `name`" marker when the implicit boundary is a + * following H2 heading. Blocks that naturally end at EOF keep their implicit + * EOF boundary and are not rewritten. * * This is intended for content that is about to be spliced into a larger * document (for example a runtime-imported file). Without it, a skill block @@ -280,7 +280,9 @@ function closeUnterminatedSkillMarkers(content) { if (matchedEnd === undefined) { const contentEnd = h2Positions.find(pos => pos >= lineEnd) ?? content.length; - insertions.push({ pos: contentEnd, name }); + if (contentEnd < content.length) { + insertions.push({ pos: contentEnd, name }); + } } } diff --git a/actions/setup/js/extract_inline_skills.test.cjs b/actions/setup/js/extract_inline_skills.test.cjs index 13eff8a0a8b..4f30a73e2d9 100644 --- a/actions/setup/js/extract_inline_skills.test.cjs +++ b/actions/setup/js/extract_inline_skills.test.cjs @@ -203,10 +203,10 @@ describe("extractInlineSkills", () => { describe("closeUnterminatedSkillMarkers", () => { const skillEndMarker = name => `## end skill: \`${name}\``; - it("adds an explicit end marker at EOF for an unterminated skill", () => { + it("keeps EOF-terminated skills implicit", () => { const content = ["Main.", "", skillMarker("reporting"), "Skill content."].join("\n"); - expect(closeUnterminatedSkillMarkers(content)).toBe(["Main.", "", skillMarker("reporting"), "Skill content.", "", skillEndMarker("reporting"), ""].join("\n")); + expect(closeUnterminatedSkillMarkers(content)).toBe(content); }); it("adds an explicit end marker before the next H2 boundary", () => { diff --git a/actions/setup/js/runtime_import.test.cjs b/actions/setup/js/runtime_import.test.cjs index 99ceb5fc467..2530eb20ec2 100644 --- a/actions/setup/js/runtime_import.test.cjs +++ b/actions/setup/js/runtime_import.test.cjs @@ -744,12 +744,11 @@ describe("runtime_import", () => { // Use ../../ to escape .github/workflows and go up to the temp directory await expect(processRuntimeImport("../../outside.md", !1, tempDir)).rejects.toThrow("Security: Path"); })); - it("should implicitly close an unterminated inline skill block so it cannot swallow spliced-in content", async () => { + it("should keep EOF-terminated inline skill blocks implicit", async () => { const content = "## skill: `reporting`\n\nGuidelines here.\n"; fs.writeFileSync(path.join(workflowsDir, "unterminated-skill.md"), content); const result = await processRuntimeImport("unterminated-skill.md", !1, tempDir); - expect(result).toContain("## skill: `reporting`"); - expect(result).toContain("## end skill: `reporting`"); + expect(result).toBe(content); }); it("should implicitly close an unterminated inline sub-agent block so it cannot swallow spliced-in content", async () => { const content = "## agent: `helper`\n\nAgent instructions.\n"; @@ -1122,14 +1121,13 @@ describe("runtime_import", () => { const result = await processRuntimeImports("First: {{#runtime-import test.txt:1-2}} Second: {{#runtime-import test.txt:4-5}}", tempDir); expect(result).toBe("First: Line 1\nLine 2 Second: Line 4\nLine 5"); }), - it("should not let an unterminated inline skill block from one runtime import swallow a subsequent runtime import's content", async () => { + it("should keep EOF-terminated skill boundaries implicit while preserving subsequent import content", async () => { fs.writeFileSync(path.join(workflowsDir, "reporting.md"), "## skill: `reporting`\n\nFormatting guidelines.\n"); fs.writeFileSync(path.join(workflowsDir, "otlp.md"), "## Telemetry\n\nOTLP guidance.\n"); const result = await processRuntimeImports("{{#runtime-import reporting.md}}\n{{#runtime-import otlp.md}}\nMain body content.", tempDir); - // The skill block must be explicitly closed before the next import's - // content, otherwise it would swallow "## Telemetry" and everything - // after it since there is no other H2 heading to stop at. - expect(result).toContain("## end skill: `reporting`"); + // The next import begins with an H2, so implicit skill termination + // must preserve following content without injecting an explicit end marker. + expect(result).not.toContain("## end skill: `reporting`"); expect(result).toContain("## Telemetry"); expect(result).toContain("Main body content."); }), diff --git a/pkg/parser/inline_skill_extractor_test.go b/pkg/parser/inline_skill_extractor_test.go index acfda60f6a2..7832e1f1a4f 100644 --- a/pkg/parser/inline_skill_extractor_test.go +++ b/pkg/parser/inline_skill_extractor_test.go @@ -109,6 +109,20 @@ func TestExtractInlineSkills_SkillWithoutFrontmatter(t *testing.T) { assert.Equal(t, "Just a prompt, no frontmatter.", skills[0].Content, "skill content should be the prompt") } +func TestExtractInlineSkills_ImplicitEOFBoundaryWithoutTrailingNewline(t *testing.T) { + // EOF should implicitly terminate the skill block even when the file has no + // trailing newline and no explicit end marker. + markdown := "Main.\n\n" + skillLine("reporting") + "\nEOF-terminated content." + + mainMarkdown, skills, err := ExtractInlineSkills(markdown) + + require.NoError(t, err, "implicit EOF boundary should parse without error") + require.Len(t, skills, 1) + assert.Equal(t, "reporting", skills[0].Name) + assert.Equal(t, "EOF-terminated content.", skills[0].Content) + assert.Equal(t, "Main.", mainMarkdown) +} + func TestExtractInlineSkills_SeparatorWithTrailingWhitespace(t *testing.T) { // Trailing whitespace after the closing backtick should be tolerated markdown := "Main.\n\n" + skillLine("padded") + " \nSkill content." From 4f1663ef7ac5b2fce44b76fc1e13f3d12819d270 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 27 Aug 2026 12:13:27 +0000 Subject: [PATCH 5/6] test: cover implicit EOF inline-skill parsing Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- actions/setup/js/extract_inline_skills.cjs | 10 ++++------ actions/setup/js/extract_inline_skills.test.cjs | 13 +++++++++++-- actions/setup/js/runtime_import.test.cjs | 17 +++++++++++------ 3 files changed, 26 insertions(+), 14 deletions(-) diff --git a/actions/setup/js/extract_inline_skills.cjs b/actions/setup/js/extract_inline_skills.cjs index afd527d529b..e432d2bcfbf 100644 --- a/actions/setup/js/extract_inline_skills.cjs +++ b/actions/setup/js/extract_inline_skills.cjs @@ -229,9 +229,9 @@ function extractInlineSkills(content) { /** * Ensures every "## skill: `name`" start marker in content has a matching - * explicit "## end skill: `name`" marker when the implicit boundary is a - * following H2 heading. Blocks that naturally end at EOF keep their implicit - * EOF boundary and are not rewritten. + * explicit "## end skill: `name`" marker, inserting one at the block's + * implicit boundary (next H2 heading or EOF) for any start marker that + * doesn't already have one. * * This is intended for content that is about to be spliced into a larger * document (for example a runtime-imported file). Without it, a skill block @@ -280,9 +280,7 @@ function closeUnterminatedSkillMarkers(content) { if (matchedEnd === undefined) { const contentEnd = h2Positions.find(pos => pos >= lineEnd) ?? content.length; - if (contentEnd < content.length) { - insertions.push({ pos: contentEnd, name }); - } + insertions.push({ pos: contentEnd, name }); } } diff --git a/actions/setup/js/extract_inline_skills.test.cjs b/actions/setup/js/extract_inline_skills.test.cjs index 4f30a73e2d9..e38466cd275 100644 --- a/actions/setup/js/extract_inline_skills.test.cjs +++ b/actions/setup/js/extract_inline_skills.test.cjs @@ -103,6 +103,15 @@ describe("extractInlineSkills", () => { expect(skills[0].name).toBe("only"); }); + it("uses implicit EOF boundary without explicit end marker", () => { + const content = ["Main.", "", skillMarker("reporting"), "EOF-terminated content."].join("\n"); + const { mainContent, skills } = extractInlineSkills(content); + expect(mainContent).toBe("Main."); + expect(skills).toHaveLength(1); + expect(skills[0].name).toBe("reporting"); + expect(skills[0].content).toBe("EOF-terminated content."); + }); + it("skill content is trimmed", () => { const content = "Main.\n\n" + skillMarker("a") + "\n\n\n Trimmed. \n\n"; const { skills } = extractInlineSkills(content); @@ -203,10 +212,10 @@ describe("extractInlineSkills", () => { describe("closeUnterminatedSkillMarkers", () => { const skillEndMarker = name => `## end skill: \`${name}\``; - it("keeps EOF-terminated skills implicit", () => { + it("adds an explicit end marker at EOF for an unterminated skill", () => { const content = ["Main.", "", skillMarker("reporting"), "Skill content."].join("\n"); - expect(closeUnterminatedSkillMarkers(content)).toBe(content); + expect(closeUnterminatedSkillMarkers(content)).toBe(["Main.", "", skillMarker("reporting"), "Skill content.", "", skillEndMarker("reporting"), ""].join("\n")); }); it("adds an explicit end marker before the next H2 boundary", () => { diff --git a/actions/setup/js/runtime_import.test.cjs b/actions/setup/js/runtime_import.test.cjs index 2530eb20ec2..c873fd3fcbd 100644 --- a/actions/setup/js/runtime_import.test.cjs +++ b/actions/setup/js/runtime_import.test.cjs @@ -20,6 +20,7 @@ const { extractAndReplacePlaceholders, generatePlaceholderName, } = require("./runtime_import.cjs"); +const { extractInlineSkills } = require("./extract_inline_skills.cjs"); describe("runtime_import", () => { let tempDir; let githubDir; @@ -744,11 +745,12 @@ describe("runtime_import", () => { // Use ../../ to escape .github/workflows and go up to the temp directory await expect(processRuntimeImport("../../outside.md", !1, tempDir)).rejects.toThrow("Security: Path"); })); - it("should keep EOF-terminated inline skill blocks implicit", async () => { + it("should implicitly close an unterminated inline skill block so it cannot swallow spliced-in content", async () => { const content = "## skill: `reporting`\n\nGuidelines here.\n"; fs.writeFileSync(path.join(workflowsDir, "unterminated-skill.md"), content); const result = await processRuntimeImport("unterminated-skill.md", !1, tempDir); - expect(result).toBe(content); + expect(result).toContain("## skill: `reporting`"); + expect(result).toContain("## end skill: `reporting`"); }); it("should implicitly close an unterminated inline sub-agent block so it cannot swallow spliced-in content", async () => { const content = "## agent: `helper`\n\nAgent instructions.\n"; @@ -1121,13 +1123,16 @@ describe("runtime_import", () => { const result = await processRuntimeImports("First: {{#runtime-import test.txt:1-2}} Second: {{#runtime-import test.txt:4-5}}", tempDir); expect(result).toBe("First: Line 1\nLine 2 Second: Line 4\nLine 5"); }), - it("should keep EOF-terminated skill boundaries implicit while preserving subsequent import content", async () => { + it("should not let an unterminated inline skill block from one runtime import swallow a subsequent runtime import's content", async () => { fs.writeFileSync(path.join(workflowsDir, "reporting.md"), "## skill: `reporting`\n\nFormatting guidelines.\n"); fs.writeFileSync(path.join(workflowsDir, "otlp.md"), "## Telemetry\n\nOTLP guidance.\n"); const result = await processRuntimeImports("{{#runtime-import reporting.md}}\n{{#runtime-import otlp.md}}\nMain body content.", tempDir); - // The next import begins with an H2, so implicit skill termination - // must preserve following content without injecting an explicit end marker. - expect(result).not.toContain("## end skill: `reporting`"); + expect(result).toContain("## end skill: `reporting`"); + const { mainContent, skills } = extractInlineSkills(result); + expect(skills).toHaveLength(1); + expect(skills[0].name).toBe("reporting"); + expect(skills[0].content).not.toContain("## Telemetry"); + expect(mainContent).toContain("## Telemetry"); expect(result).toContain("## Telemetry"); expect(result).toContain("Main body content."); }), From cd44c47ee6c767dfbc7e1cc7582ff4c683682af1 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 27 Aug 2026 20:04:21 +0000 Subject: [PATCH 6/6] test: prevent Getwd stub races Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- pkg/gitutil/gitutil_test.go | 1 - 1 file changed, 1 deletion(-) diff --git a/pkg/gitutil/gitutil_test.go b/pkg/gitutil/gitutil_test.go index 2c2a834b9dc..2dc1a532f2e 100644 --- a/pkg/gitutil/gitutil_test.go +++ b/pkg/gitutil/gitutil_test.go @@ -235,7 +235,6 @@ func TestExtractBaseRepo(t *testing.T) { } func TestGetwd(t *testing.T) { - t.Parallel() t.Run("returns the current working directory", func(t *testing.T) { t.Parallel() dir, err := Getwd()