From c74cbe4b395958c6128b3b545e03587c502ee36e Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Thu, 18 Jun 2026 11:27:13 +0000
Subject: [PATCH 1/7] Initial plan


From 5817acfd8619dc6cc54e4d56ab6b5f9818019616 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Thu, 18 Jun 2026 11:40:07 +0000
Subject: [PATCH 2/7] chore: outline plan for update_issue MCP handler fix

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
---
 .github/workflows/daily-file-diet.lock.yml                 | 3 ++-
 .github/workflows/daily-testify-uber-super-expert.lock.yml | 3 ++-
 .github/workflows/go-fan.lock.yml                          | 3 ++-
 .github/workflows/spec-librarian.lock.yml                  | 3 ++-
 actions/setup-cli/install.sh                               | 2 +-
 5 files changed, 9 insertions(+), 5 deletions(-)

diff --git a/.github/workflows/daily-file-diet.lock.yml b/.github/workflows/daily-file-diet.lock.yml
index b9a00bfac1d..70c52032698 100644
--- a/.github/workflows/daily-file-diet.lock.yml
+++ b/.github/workflows/daily-file-diet.lock.yml
@@ -825,6 +825,7 @@ jobs:
         # --allow-tool github
         # --allow-tool safeoutputs
         # --allow-tool serena
+        # --allow-tool shell(awk)
         # --allow-tool shell(cat pkg/**/*.go)
         # --allow-tool shell(cat)
         # --allow-tool shell(date)
@@ -901,7 +902,7 @@ jobs:
           fi
           # shellcheck disable=SC1003
           sudo -E awf --config "${RUNNER_TEMP}/gh-aw/awf-config.json" --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" ${GH_AW_TOOL_CACHE_MOUNT:+--mount "$GH_AW_TOOL_CACHE_MOUNT"} ${GH_AW_DOCKER_HOST:+--docker-host "$GH_AW_DOCKER_HOST"} ${GH_AW_DOCKER_HOST_PATH_PREFIX_ARGS} --env-all --exclude-env COPILOT_GITHUB_TOKEN --exclude-env GH_TOKEN --exclude-env GITHUB_MCP_SERVER_TOKEN --exclude-env MCP_GATEWAY_API_KEY --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --audit-dir /tmp/gh-aw/sandbox/firewall/audit --enable-host-access --allow-host-ports 80,443,8080 --skip-pull --difc-proxy-host host.docker.internal:18443 --difc-proxy-ca-cert /tmp/gh-aw/difc-proxy-tls/ca.crt \
-            -- /bin/bash -c 'set +o histexpand; export PATH="${RUNNER_TEMP}/gh-aw/mcp-cli/bin:$PATH" && GH_AW_TOOL_CACHE="${RUNNER_TOOL_CACHE:-/opt/hostedtoolcache}"; export PATH="$(find "$GH_AW_TOOL_CACHE" /opt/hostedtoolcache /home/runner/work/_tool -maxdepth 5 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH"; [ -n "$GOROOT" ] && export PATH="$GOROOT/bin:$PATH" || true && GH_AW_NODE_EXEC="${GH_AW_NODE_BIN:-}"; if [ -z "$GH_AW_NODE_EXEC" ] || [ ! -x "$GH_AW_NODE_EXEC" ]; then GH_AW_NODE_EXEC="$(command -v node 2>/dev/null || true)"; fi; if [ -z "$GH_AW_NODE_EXEC" ]; then echo "node runtime missing on this runner — check runtimes.node in workflow YAML" >&2; exit 127; fi; GH_AW_NPM_GLOBAL_ROOT="$(npm root -g 2>/dev/null || true)"; if [ -n "$GH_AW_NPM_GLOBAL_ROOT" ]; then export NODE_PATH="${GH_AW_NPM_GLOBAL_ROOT}${NODE_PATH:+:${NODE_PATH}}"; fi; "$GH_AW_NODE_EXEC" ${RUNNER_TEMP}/gh-aw/actions/copilot_harness.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 --agent developer.instructions --allow-tool github --allow-tool safeoutputs --allow-tool serena --allow-tool '\''shell(cat pkg/**/*.go)'\'' --allow-tool '\''shell(cat)'\'' --allow-tool '\''shell(date)'\'' --allow-tool '\''shell(echo)'\'' --allow-tool '\''shell(find pkg -name "*.go" ! -name "*_test.go" -type f -exec wc -l {} \; | sort -rn)'\'' --allow-tool '\''shell(find pkg -name "*.go" ! -name "*_test.go" -type f)'\'' --allow-tool '\''shell(find pkg -type f -name "*.go" ! -name "*_test.go")'\'' --allow-tool '\''shell(find pkg/ -maxdepth 1 -ls)'\'' --allow-tool '\''shell(find pkg/workflow/ -maxdepth 1 -ls)'\'' --allow-tool '\''shell(gh:*)'\'' --allow-tool '\''shell(grep -r "func " pkg --include="*.go")'\'' --allow-tool '\''shell(grep)'\'' --allow-tool '\''shell(head -n * pkg/**/*.go)'\'' --allow-tool '\''shell(head)'\'' --allow-tool '\''shell(ls)'\'' --allow-tool '\''shell(printf)'\'' --allow-tool '\''shell(pwd)'\'' --allow-tool '\''shell(safeoutputs:*)'\'' --allow-tool '\''shell(serena:*)'\'' --allow-tool '\''shell(sort)'\'' --allow-tool '\''shell(tail)'\'' --allow-tool '\''shell(uniq)'\'' --allow-tool '\''shell(wc -l pkg/**/*.go)'\'' --allow-tool '\''shell(wc)'\'' --allow-tool '\''shell(yq)'\'' --allow-tool write --allow-all-paths --add-dir "${GITHUB_WORKSPACE}" --prompt-file /tmp/gh-aw/aw-prompts/prompt.txt' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log
+            -- /bin/bash -c 'set +o histexpand; export PATH="${RUNNER_TEMP}/gh-aw/mcp-cli/bin:$PATH" && GH_AW_TOOL_CACHE="${RUNNER_TOOL_CACHE:-/opt/hostedtoolcache}"; export PATH="$(find "$GH_AW_TOOL_CACHE" /opt/hostedtoolcache /home/runner/work/_tool -maxdepth 5 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH"; [ -n "$GOROOT" ] && export PATH="$GOROOT/bin:$PATH" || true && GH_AW_NODE_EXEC="${GH_AW_NODE_BIN:-}"; if [ -z "$GH_AW_NODE_EXEC" ] || [ ! -x "$GH_AW_NODE_EXEC" ]; then GH_AW_NODE_EXEC="$(command -v node 2>/dev/null || true)"; fi; if [ -z "$GH_AW_NODE_EXEC" ]; then echo "node runtime missing on this runner — check runtimes.node in workflow YAML" >&2; exit 127; fi; GH_AW_NPM_GLOBAL_ROOT="$(npm root -g 2>/dev/null || true)"; if [ -n "$GH_AW_NPM_GLOBAL_ROOT" ]; then export NODE_PATH="${GH_AW_NPM_GLOBAL_ROOT}${NODE_PATH:+:${NODE_PATH}}"; fi; "$GH_AW_NODE_EXEC" ${RUNNER_TEMP}/gh-aw/actions/copilot_harness.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 --agent developer.instructions --allow-tool github --allow-tool safeoutputs --allow-tool serena --allow-tool '\''shell(awk)'\'' --allow-tool '\''shell(cat pkg/**/*.go)'\'' --allow-tool '\''shell(cat)'\'' --allow-tool '\''shell(date)'\'' --allow-tool '\''shell(echo)'\'' --allow-tool '\''shell(find pkg -name "*.go" ! -name "*_test.go" -type f -exec wc -l {} \; | sort -rn)'\'' --allow-tool '\''shell(find pkg -name "*.go" ! -name "*_test.go" -type f)'\'' --allow-tool '\''shell(find pkg -type f -name "*.go" ! -name "*_test.go")'\'' --allow-tool '\''shell(find pkg/ -maxdepth 1 -ls)'\'' --allow-tool '\''shell(find pkg/workflow/ -maxdepth 1 -ls)'\'' --allow-tool '\''shell(gh:*)'\'' --allow-tool '\''shell(grep -r "func " pkg --include="*.go")'\'' --allow-tool '\''shell(grep)'\'' --allow-tool '\''shell(head -n * pkg/**/*.go)'\'' --allow-tool '\''shell(head)'\'' --allow-tool '\''shell(ls)'\'' --allow-tool '\''shell(printf)'\'' --allow-tool '\''shell(pwd)'\'' --allow-tool '\''shell(safeoutputs:*)'\'' --allow-tool '\''shell(serena:*)'\'' --allow-tool '\''shell(sort)'\'' --allow-tool '\''shell(tail)'\'' --allow-tool '\''shell(uniq)'\'' --allow-tool '\''shell(wc -l pkg/**/*.go)'\'' --allow-tool '\''shell(wc)'\'' --allow-tool '\''shell(yq)'\'' --allow-tool write --allow-all-paths --add-dir "${GITHUB_WORKSPACE}" --prompt-file /tmp/gh-aw/aw-prompts/prompt.txt' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log
         env:
           AWF_REFLECT_ENABLED: 1
           COPILOT_AGENT_RUNNER_TYPE: STANDALONE
diff --git a/.github/workflows/daily-testify-uber-super-expert.lock.yml b/.github/workflows/daily-testify-uber-super-expert.lock.yml
index 7563eb927a8..eed47f927bd 100644
--- a/.github/workflows/daily-testify-uber-super-expert.lock.yml
+++ b/.github/workflows/daily-testify-uber-super-expert.lock.yml
@@ -851,6 +851,7 @@ jobs:
         # --allow-tool github
         # --allow-tool safeoutputs
         # --allow-tool serena
+        # --allow-tool shell(awk)
         # --allow-tool shell(cat **/*_test.go)
         # --allow-tool shell(cat pkg/**/*.go)
         # --allow-tool shell(cat)
@@ -940,7 +941,7 @@ jobs:
           COPILOT_MODEL: ${{ vars.GH_AW_MODEL_AGENT_COPILOT || vars.GH_AW_DEFAULT_MODEL_COPILOT || 'claude-sonnet-4.6' }}
           COPILOT_SDK_URI: http://127.0.0.1:3002
           GH_AW_COPILOT_SDK_DRIVER: 1
-          GH_AW_COPILOT_SDK_SERVER_ARGS: '["--headless","--no-auto-update","--port","3002","--add-dir","/tmp/gh-aw/","--log-level","all","--log-dir","/tmp/gh-aw/sandbox/agent/logs/","--disable-builtin-mcps","--no-ask-user","--allow-tool","github","--allow-tool","safeoutputs","--allow-tool","serena","--allow-tool","shell(cat **/*_test.go)","--allow-tool","shell(cat pkg/**/*.go)","--allow-tool","shell(cat)","--allow-tool","shell(date)","--allow-tool","shell(echo)","--allow-tool","shell(find . -name \"*_test.go\" -type f)","--allow-tool","shell(find pkg -name \"*.go\" ! -name \"*_test.go\" -type f)","--allow-tool","shell(find pkg -type f -name \"*.go\" ! -name \"*_test.go\")","--allow-tool","shell(find pkg/ -maxdepth 1 -ls)","--allow-tool","shell(find pkg/workflow/ -maxdepth 1 -ls)","--allow-tool","shell(gh:*)","--allow-tool","shell(go test -v ./...)","--allow-tool","shell(grep -r \"func \" pkg --include=\"*.go\")","--allow-tool","shell(grep -r \"func Test\" . --include=\"*_test.go\")","--allow-tool","shell(grep)","--allow-tool","shell(head -n * pkg/**/*.go)","--allow-tool","shell(head)","--allow-tool","shell(ls)","--allow-tool","shell(printf)","--allow-tool","shell(pwd)","--allow-tool","shell(safeoutputs:*)","--allow-tool","shell(serena:*)","--allow-tool","shell(sort)","--allow-tool","shell(tail)","--allow-tool","shell(uniq)","--allow-tool","shell(wc -l **/*_test.go)","--allow-tool","shell(wc -l pkg/**/*.go)","--allow-tool","shell(wc)","--allow-tool","shell(yq)","--allow-tool","write","--allow-all-paths"]'
+          GH_AW_COPILOT_SDK_SERVER_ARGS: '["--headless","--no-auto-update","--port","3002","--add-dir","/tmp/gh-aw/","--log-level","all","--log-dir","/tmp/gh-aw/sandbox/agent/logs/","--disable-builtin-mcps","--no-ask-user","--allow-tool","github","--allow-tool","safeoutputs","--allow-tool","serena","--allow-tool","shell(awk)","--allow-tool","shell(cat **/*_test.go)","--allow-tool","shell(cat pkg/**/*.go)","--allow-tool","shell(cat)","--allow-tool","shell(date)","--allow-tool","shell(echo)","--allow-tool","shell(find . -name \"*_test.go\" -type f)","--allow-tool","shell(find pkg -name \"*.go\" ! -name \"*_test.go\" -type f)","--allow-tool","shell(find pkg -type f -name \"*.go\" ! -name \"*_test.go\")","--allow-tool","shell(find pkg/ -maxdepth 1 -ls)","--allow-tool","shell(find pkg/workflow/ -maxdepth 1 -ls)","--allow-tool","shell(gh:*)","--allow-tool","shell(go test -v ./...)","--allow-tool","shell(grep -r \"func \" pkg --include=\"*.go\")","--allow-tool","shell(grep -r \"func Test\" . --include=\"*_test.go\")","--allow-tool","shell(grep)","--allow-tool","shell(head -n * pkg/**/*.go)","--allow-tool","shell(head)","--allow-tool","shell(ls)","--allow-tool","shell(printf)","--allow-tool","shell(pwd)","--allow-tool","shell(safeoutputs:*)","--allow-tool","shell(serena:*)","--allow-tool","shell(sort)","--allow-tool","shell(tail)","--allow-tool","shell(uniq)","--allow-tool","shell(wc -l **/*_test.go)","--allow-tool","shell(wc -l pkg/**/*.go)","--allow-tool","shell(wc)","--allow-tool","shell(yq)","--allow-tool","write","--allow-all-paths"]'
           GH_AW_MAX_AI_CREDITS: ${{ vars.GH_AW_DEFAULT_MAX_AI_CREDITS || '1000' }}
           GH_AW_MAX_TOOL_DENIALS: 5
           GH_AW_MAX_TURNS: ${{ vars.GH_AW_DEFAULT_MAX_TURNS || '' }}
diff --git a/.github/workflows/go-fan.lock.yml b/.github/workflows/go-fan.lock.yml
index 4a86548f579..9454bff69f8 100644
--- a/.github/workflows/go-fan.lock.yml
+++ b/.github/workflows/go-fan.lock.yml
@@ -844,6 +844,7 @@ jobs:
       - name: Execute Claude Code CLI
         id: agentic_execution
         # Allowed tools (sorted):
+        # - Bash(awk)
         # - Bash(cat /tmp/gh-aw/cache-memory/)
         # - Bash(cat > /tmp/gh-aw/cache-memory/)
         # - Bash(cat go.mod)
@@ -1001,7 +1002,7 @@ jobs:
           fi
           # shellcheck disable=SC1003
           sudo -E awf --config "${RUNNER_TEMP}/gh-aw/awf-config.json" --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" ${GH_AW_TOOL_CACHE_MOUNT:+--mount "$GH_AW_TOOL_CACHE_MOUNT"} ${GH_AW_DOCKER_HOST:+--docker-host "$GH_AW_DOCKER_HOST"} ${GH_AW_DOCKER_HOST_PATH_PREFIX_ARGS} --tty --env-all --exclude-env ANTHROPIC_API_KEY --exclude-env GH_TOKEN --exclude-env GITHUB_MCP_SERVER_TOKEN --exclude-env MCP_GATEWAY_API_KEY --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --audit-dir /tmp/gh-aw/sandbox/firewall/audit --enable-host-access --allow-host-ports 80,443,8080 --skip-pull --difc-proxy-host host.docker.internal:18443 --difc-proxy-ca-cert /tmp/gh-aw/difc-proxy-tls/ca.crt \
-            -- /bin/bash -c 'set +o histexpand; export PATH="${RUNNER_TEMP}/gh-aw/mcp-cli/bin:$PATH" && GH_AW_TOOL_CACHE="${RUNNER_TOOL_CACHE:-/opt/hostedtoolcache}"; export PATH="$(find "$GH_AW_TOOL_CACHE" /opt/hostedtoolcache /home/runner/work/_tool -maxdepth 5 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH"; [ -n "$GOROOT" ] && export PATH="$GOROOT/bin:$PATH" || true && GH_AW_NODE_EXEC="${GH_AW_NODE_BIN:-}"; if [ -z "$GH_AW_NODE_EXEC" ] || [ ! -x "$GH_AW_NODE_EXEC" ]; then GH_AW_NODE_EXEC="$(command -v node 2>/dev/null || true)"; fi; if [ -z "$GH_AW_NODE_EXEC" ]; then echo "node runtime missing on this runner — check runtimes.node in workflow YAML" >&2; exit 127; fi; GH_AW_NPM_GLOBAL_ROOT="$(npm root -g 2>/dev/null || true)"; if [ -n "$GH_AW_NPM_GLOBAL_ROOT" ]; then export NODE_PATH="${GH_AW_NPM_GLOBAL_ROOT}${NODE_PATH:+:${NODE_PATH}}"; fi; "$GH_AW_NODE_EXEC" ${RUNNER_TEMP}/gh-aw/actions/claude_harness.cjs claude --print --no-chrome --allowed-tools '\''Bash(cat /tmp/gh-aw/cache-memory/),Bash(cat > /tmp/gh-aw/cache-memory/),Bash(cat go.mod),Bash(cat go.sum),Bash(cat pkg/**/*.go),Bash(cat scratchpad/mods/*),Bash(cat),Bash(date),Bash(echo),Bash(find pkg -name "*.go" ! -name "*_test.go" -type f),Bash(find pkg -name "*.go"),Bash(find pkg -type f -name "*.go" ! -name "*_test.go"),Bash(find pkg/ -maxdepth 1 -ls),Bash(find pkg/workflow/ -maxdepth 1 -ls),Bash(find scratchpad/mods/ -maxdepth 1 -ls),Bash(gh:*),Bash(go list -m all),Bash(grep -r "func " pkg --include="*.go"),Bash(grep -r "import" --include="*.go"),Bash(grep),Bash(head -n * pkg/**/*.go),Bash(head),Bash(ls),Bash(mkdir -p /tmp/gh-aw/cache-memory/),Bash(mv /tmp/gh-aw/cache-memory/),Bash(printf),Bash(pwd),Bash(safeoutputs:*),Bash(serena:*),Bash(sort),Bash(tail),Bash(uniq),Bash(wc -l pkg/**/*.go),Bash(wc),Bash(yq),BashOutput,Edit,Edit(/tmp/*),Edit(/tmp/gh-aw/agent/*),Edit(/tmp/gh-aw/cache-memory/*),ExitPlanMode,Glob,Grep,KillBash,LS,MultiEdit,MultiEdit(/tmp/*),MultiEdit(/tmp/gh-aw/agent/*),MultiEdit(/tmp/gh-aw/cache-memory/*),NotebookEdit,NotebookRead,Read,Read(/tmp/*),Read(/tmp/gh-aw/agent/*),Read(/tmp/gh-aw/cache-memory/*),Task,TodoWrite,Write,Write(/tmp/*),Write(/tmp/gh-aw/agent/*),Write(/tmp/gh-aw/cache-memory/*),mcp__github__download_workflow_run_artifact,mcp__github__get_code_scanning_alert,mcp__github__get_commit,mcp__github__get_dependabot_alert,mcp__github__get_discussion,mcp__github__get_discussion_comments,mcp__github__get_file_contents,mcp__github__get_job_logs,mcp__github__get_label,mcp__github__get_latest_release,mcp__github__get_me,mcp__github__get_notification_details,mcp__github__get_pull_request,mcp__github__get_pull_request_comments,mcp__github__get_pull_request_diff,mcp__github__get_pull_request_files,mcp__github__get_pull_request_review_comments,mcp__github__get_pull_request_reviews,mcp__github__get_pull_request_status,mcp__github__get_release_by_tag,mcp__github__get_secret_scanning_alert,mcp__github__get_tag,mcp__github__get_workflow_run,mcp__github__get_workflow_run_logs,mcp__github__get_workflow_run_usage,mcp__github__issue_read,mcp__github__list_branches,mcp__github__list_code_scanning_alerts,mcp__github__list_commits,mcp__github__list_dependabot_alerts,mcp__github__list_discussion_categories,mcp__github__list_discussions,mcp__github__list_issue_types,mcp__github__list_issues,mcp__github__list_label,mcp__github__list_notifications,mcp__github__list_pull_requests,mcp__github__list_releases,mcp__github__list_secret_scanning_alerts,mcp__github__list_starred_repositories,mcp__github__list_tags,mcp__github__list_workflow_jobs,mcp__github__list_workflow_run_artifacts,mcp__github__list_workflow_runs,mcp__github__list_workflows,mcp__github__pull_request_read,mcp__github__search_code,mcp__github__search_issues,mcp__github__search_orgs,mcp__github__search_pull_requests,mcp__github__search_repositories,mcp__github__search_users,mcp__safeoutputs,mcp__serena'\'' --debug-file /tmp/gh-aw/agent-stdio.log --verbose --permission-mode acceptEdits --output-format stream-json --mcp-config "${RUNNER_TEMP}/gh-aw/mcp-config/mcp-servers.json" --prompt-file /tmp/gh-aw/aw-prompts/prompt.txt${GH_AW_MODEL_AGENT_CLAUDE:+ --model "$GH_AW_MODEL_AGENT_CLAUDE"}' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log
+            -- /bin/bash -c 'set +o histexpand; export PATH="${RUNNER_TEMP}/gh-aw/mcp-cli/bin:$PATH" && GH_AW_TOOL_CACHE="${RUNNER_TOOL_CACHE:-/opt/hostedtoolcache}"; export PATH="$(find "$GH_AW_TOOL_CACHE" /opt/hostedtoolcache /home/runner/work/_tool -maxdepth 5 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH"; [ -n "$GOROOT" ] && export PATH="$GOROOT/bin:$PATH" || true && GH_AW_NODE_EXEC="${GH_AW_NODE_BIN:-}"; if [ -z "$GH_AW_NODE_EXEC" ] || [ ! -x "$GH_AW_NODE_EXEC" ]; then GH_AW_NODE_EXEC="$(command -v node 2>/dev/null || true)"; fi; if [ -z "$GH_AW_NODE_EXEC" ]; then echo "node runtime missing on this runner — check runtimes.node in workflow YAML" >&2; exit 127; fi; GH_AW_NPM_GLOBAL_ROOT="$(npm root -g 2>/dev/null || true)"; if [ -n "$GH_AW_NPM_GLOBAL_ROOT" ]; then export NODE_PATH="${GH_AW_NPM_GLOBAL_ROOT}${NODE_PATH:+:${NODE_PATH}}"; fi; "$GH_AW_NODE_EXEC" ${RUNNER_TEMP}/gh-aw/actions/claude_harness.cjs claude --print --no-chrome --allowed-tools '\''Bash(awk),Bash(cat /tmp/gh-aw/cache-memory/),Bash(cat > /tmp/gh-aw/cache-memory/),Bash(cat go.mod),Bash(cat go.sum),Bash(cat pkg/**/*.go),Bash(cat scratchpad/mods/*),Bash(cat),Bash(date),Bash(echo),Bash(find pkg -name "*.go" ! -name "*_test.go" -type f),Bash(find pkg -name "*.go"),Bash(find pkg -type f -name "*.go" ! -name "*_test.go"),Bash(find pkg/ -maxdepth 1 -ls),Bash(find pkg/workflow/ -maxdepth 1 -ls),Bash(find scratchpad/mods/ -maxdepth 1 -ls),Bash(gh:*),Bash(go list -m all),Bash(grep -r "func " pkg --include="*.go"),Bash(grep -r "import" --include="*.go"),Bash(grep),Bash(head -n * pkg/**/*.go),Bash(head),Bash(ls),Bash(mkdir -p /tmp/gh-aw/cache-memory/),Bash(mv /tmp/gh-aw/cache-memory/),Bash(printf),Bash(pwd),Bash(safeoutputs:*),Bash(serena:*),Bash(sort),Bash(tail),Bash(uniq),Bash(wc -l pkg/**/*.go),Bash(wc),Bash(yq),BashOutput,Edit,Edit(/tmp/*),Edit(/tmp/gh-aw/agent/*),Edit(/tmp/gh-aw/cache-memory/*),ExitPlanMode,Glob,Grep,KillBash,LS,MultiEdit,MultiEdit(/tmp/*),MultiEdit(/tmp/gh-aw/agent/*),MultiEdit(/tmp/gh-aw/cache-memory/*),NotebookEdit,NotebookRead,Read,Read(/tmp/*),Read(/tmp/gh-aw/agent/*),Read(/tmp/gh-aw/cache-memory/*),Task,TodoWrite,Write,Write(/tmp/*),Write(/tmp/gh-aw/agent/*),Write(/tmp/gh-aw/cache-memory/*),mcp__github__download_workflow_run_artifact,mcp__github__get_code_scanning_alert,mcp__github__get_commit,mcp__github__get_dependabot_alert,mcp__github__get_discussion,mcp__github__get_discussion_comments,mcp__github__get_file_contents,mcp__github__get_job_logs,mcp__github__get_label,mcp__github__get_latest_release,mcp__github__get_me,mcp__github__get_notification_details,mcp__github__get_pull_request,mcp__github__get_pull_request_comments,mcp__github__get_pull_request_diff,mcp__github__get_pull_request_files,mcp__github__get_pull_request_review_comments,mcp__github__get_pull_request_reviews,mcp__github__get_pull_request_status,mcp__github__get_release_by_tag,mcp__github__get_secret_scanning_alert,mcp__github__get_tag,mcp__github__get_workflow_run,mcp__github__get_workflow_run_logs,mcp__github__get_workflow_run_usage,mcp__github__issue_read,mcp__github__list_branches,mcp__github__list_code_scanning_alerts,mcp__github__list_commits,mcp__github__list_dependabot_alerts,mcp__github__list_discussion_categories,mcp__github__list_discussions,mcp__github__list_issue_types,mcp__github__list_issues,mcp__github__list_label,mcp__github__list_notifications,mcp__github__list_pull_requests,mcp__github__list_releases,mcp__github__list_secret_scanning_alerts,mcp__github__list_starred_repositories,mcp__github__list_tags,mcp__github__list_workflow_jobs,mcp__github__list_workflow_run_artifacts,mcp__github__list_workflow_runs,mcp__github__list_workflows,mcp__github__pull_request_read,mcp__github__search_code,mcp__github__search_issues,mcp__github__search_orgs,mcp__github__search_pull_requests,mcp__github__search_repositories,mcp__github__search_users,mcp__safeoutputs,mcp__serena'\'' --debug-file /tmp/gh-aw/agent-stdio.log --verbose --permission-mode acceptEdits --output-format stream-json --mcp-config "${RUNNER_TEMP}/gh-aw/mcp-config/mcp-servers.json" --prompt-file /tmp/gh-aw/aw-prompts/prompt.txt${GH_AW_MODEL_AGENT_CLAUDE:+ --model "$GH_AW_MODEL_AGENT_CLAUDE"}' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log
         env:
           ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
           BASH_DEFAULT_TIMEOUT_MS: 60000
diff --git a/.github/workflows/spec-librarian.lock.yml b/.github/workflows/spec-librarian.lock.yml
index 8cd0579a224..a08617b5fb9 100644
--- a/.github/workflows/spec-librarian.lock.yml
+++ b/.github/workflows/spec-librarian.lock.yml
@@ -825,6 +825,7 @@ jobs:
         # --allow-tool github
         # --allow-tool safeoutputs
         # --allow-tool serena
+        # --allow-tool shell(awk)
         # --allow-tool shell(cat pkg/**/*.go)
         # --allow-tool shell(cat pkg/*/*.go)
         # --allow-tool shell(cat pkg/*/README.md)
@@ -916,7 +917,7 @@ jobs:
           fi
           # shellcheck disable=SC1003
           sudo -E awf --config "${RUNNER_TEMP}/gh-aw/awf-config.json" --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" ${GH_AW_TOOL_CACHE_MOUNT:+--mount "$GH_AW_TOOL_CACHE_MOUNT"} ${GH_AW_DOCKER_HOST:+--docker-host "$GH_AW_DOCKER_HOST"} ${GH_AW_DOCKER_HOST_PATH_PREFIX_ARGS} --env-all --exclude-env COPILOT_GITHUB_TOKEN --exclude-env GH_TOKEN --exclude-env GITHUB_MCP_SERVER_TOKEN --exclude-env MCP_GATEWAY_API_KEY --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --audit-dir /tmp/gh-aw/sandbox/firewall/audit --enable-host-access --allow-host-ports 80,443,8080 --skip-pull --difc-proxy-host host.docker.internal:18443 --difc-proxy-ca-cert /tmp/gh-aw/difc-proxy-tls/ca.crt \
-            -- /bin/bash -c 'set +o histexpand; export PATH="${RUNNER_TEMP}/gh-aw/mcp-cli/bin:$PATH" && GH_AW_TOOL_CACHE="${RUNNER_TOOL_CACHE:-/opt/hostedtoolcache}"; export PATH="$(find "$GH_AW_TOOL_CACHE" /opt/hostedtoolcache /home/runner/work/_tool -maxdepth 5 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH"; [ -n "$GOROOT" ] && export PATH="$GOROOT/bin:$PATH" || true && GH_AW_NODE_EXEC="${GH_AW_NODE_BIN:-}"; if [ -z "$GH_AW_NODE_EXEC" ] || [ ! -x "$GH_AW_NODE_EXEC" ]; then GH_AW_NODE_EXEC="$(command -v node 2>/dev/null || true)"; fi; if [ -z "$GH_AW_NODE_EXEC" ]; then echo "node runtime missing on this runner — check runtimes.node in workflow YAML" >&2; exit 127; fi; GH_AW_NPM_GLOBAL_ROOT="$(npm root -g 2>/dev/null || true)"; if [ -n "$GH_AW_NPM_GLOBAL_ROOT" ]; then export NODE_PATH="${GH_AW_NPM_GLOBAL_ROOT}${NODE_PATH:+:${NODE_PATH}}"; fi; "$GH_AW_NODE_EXEC" ${RUNNER_TEMP}/gh-aw/actions/copilot_harness.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-tool github --allow-tool safeoutputs --allow-tool serena --allow-tool '\''shell(cat pkg/**/*.go)'\'' --allow-tool '\''shell(cat pkg/*/*.go)'\'' --allow-tool '\''shell(cat pkg/*/README.md)'\'' --allow-tool '\''shell(cat)'\'' --allow-tool '\''shell(date)'\'' --allow-tool '\''shell(echo)'\'' --allow-tool '\''shell(find pkg -maxdepth 1 -type d)'\'' --allow-tool '\''shell(find pkg -name "*.go" ! -name "*_test.go" -type f)'\'' --allow-tool '\''shell(find pkg -name "README.md" -type f)'\'' --allow-tool '\''shell(find pkg -type f -name "*.go" ! -name "*_test.go")'\'' --allow-tool '\''shell(find pkg/ -maxdepth 1 -ls)'\'' --allow-tool '\''shell(find pkg/* -maxdepth 0 -type d)'\'' --allow-tool '\''shell(find pkg/workflow/ -maxdepth 1 -ls)'\'' --allow-tool '\''shell(gh:*)'\'' --allow-tool '\''shell(git log --oneline --since="30 days ago" -- pkg/*)'\'' --allow-tool '\''shell(git log --oneline --since="7 days ago" -- pkg/*/README.md)'\'' --allow-tool '\''shell(git log -1 --format=%H -- pkg/*)'\'' --allow-tool '\''shell(grep -r "func " pkg --include="*.go")'\'' --allow-tool '\''shell(grep -rn "const [A-Z]" pkg --include="*.go")'\'' --allow-tool '\''shell(grep -rn "func [A-Z]" pkg --include="*.go")'\'' --allow-tool '\''shell(grep -rn "import " pkg --include="*.go")'\'' --allow-tool '\''shell(grep -rn "package " pkg --include="*.go")'\'' --allow-tool '\''shell(grep -rn "type [A-Z]" pkg --include="*.go")'\'' --allow-tool '\''shell(grep)'\'' --allow-tool '\''shell(head -n * pkg/**/*.go)'\'' --allow-tool '\''shell(head -n * pkg/*/*.go)'\'' --allow-tool '\''shell(head)'\'' --allow-tool '\''shell(ls)'\'' --allow-tool '\''shell(printf)'\'' --allow-tool '\''shell(pwd)'\'' --allow-tool '\''shell(safeoutputs:*)'\'' --allow-tool '\''shell(serena:*)'\'' --allow-tool '\''shell(sort)'\'' --allow-tool '\''shell(tail)'\'' --allow-tool '\''shell(uniq)'\'' --allow-tool '\''shell(wc -l pkg/**/*.go)'\'' --allow-tool '\''shell(wc -l pkg/*/*.go)'\'' --allow-tool '\''shell(wc -l pkg/*/README.md)'\'' --allow-tool '\''shell(wc)'\'' --allow-tool '\''shell(yq)'\'' --allow-tool write --allow-all-paths --add-dir "${GITHUB_WORKSPACE}" --prompt-file /tmp/gh-aw/aw-prompts/prompt.txt' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log
+            -- /bin/bash -c 'set +o histexpand; export PATH="${RUNNER_TEMP}/gh-aw/mcp-cli/bin:$PATH" && GH_AW_TOOL_CACHE="${RUNNER_TOOL_CACHE:-/opt/hostedtoolcache}"; export PATH="$(find "$GH_AW_TOOL_CACHE" /opt/hostedtoolcache /home/runner/work/_tool -maxdepth 5 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH"; [ -n "$GOROOT" ] && export PATH="$GOROOT/bin:$PATH" || true && GH_AW_NODE_EXEC="${GH_AW_NODE_BIN:-}"; if [ -z "$GH_AW_NODE_EXEC" ] || [ ! -x "$GH_AW_NODE_EXEC" ]; then GH_AW_NODE_EXEC="$(command -v node 2>/dev/null || true)"; fi; if [ -z "$GH_AW_NODE_EXEC" ]; then echo "node runtime missing on this runner — check runtimes.node in workflow YAML" >&2; exit 127; fi; GH_AW_NPM_GLOBAL_ROOT="$(npm root -g 2>/dev/null || true)"; if [ -n "$GH_AW_NPM_GLOBAL_ROOT" ]; then export NODE_PATH="${GH_AW_NPM_GLOBAL_ROOT}${NODE_PATH:+:${NODE_PATH}}"; fi; "$GH_AW_NODE_EXEC" ${RUNNER_TEMP}/gh-aw/actions/copilot_harness.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-tool github --allow-tool safeoutputs --allow-tool serena --allow-tool '\''shell(awk)'\'' --allow-tool '\''shell(cat pkg/**/*.go)'\'' --allow-tool '\''shell(cat pkg/*/*.go)'\'' --allow-tool '\''shell(cat pkg/*/README.md)'\'' --allow-tool '\''shell(cat)'\'' --allow-tool '\''shell(date)'\'' --allow-tool '\''shell(echo)'\'' --allow-tool '\''shell(find pkg -maxdepth 1 -type d)'\'' --allow-tool '\''shell(find pkg -name "*.go" ! -name "*_test.go" -type f)'\'' --allow-tool '\''shell(find pkg -name "README.md" -type f)'\'' --allow-tool '\''shell(find pkg -type f -name "*.go" ! -name "*_test.go")'\'' --allow-tool '\''shell(find pkg/ -maxdepth 1 -ls)'\'' --allow-tool '\''shell(find pkg/* -maxdepth 0 -type d)'\'' --allow-tool '\''shell(find pkg/workflow/ -maxdepth 1 -ls)'\'' --allow-tool '\''shell(gh:*)'\'' --allow-tool '\''shell(git log --oneline --since="30 days ago" -- pkg/*)'\'' --allow-tool '\''shell(git log --oneline --since="7 days ago" -- pkg/*/README.md)'\'' --allow-tool '\''shell(git log -1 --format=%H -- pkg/*)'\'' --allow-tool '\''shell(grep -r "func " pkg --include="*.go")'\'' --allow-tool '\''shell(grep -rn "const [A-Z]" pkg --include="*.go")'\'' --allow-tool '\''shell(grep -rn "func [A-Z]" pkg --include="*.go")'\'' --allow-tool '\''shell(grep -rn "import " pkg --include="*.go")'\'' --allow-tool '\''shell(grep -rn "package " pkg --include="*.go")'\'' --allow-tool '\''shell(grep -rn "type [A-Z]" pkg --include="*.go")'\'' --allow-tool '\''shell(grep)'\'' --allow-tool '\''shell(head -n * pkg/**/*.go)'\'' --allow-tool '\''shell(head -n * pkg/*/*.go)'\'' --allow-tool '\''shell(head)'\'' --allow-tool '\''shell(ls)'\'' --allow-tool '\''shell(printf)'\'' --allow-tool '\''shell(pwd)'\'' --allow-tool '\''shell(safeoutputs:*)'\'' --allow-tool '\''shell(serena:*)'\'' --allow-tool '\''shell(sort)'\'' --allow-tool '\''shell(tail)'\'' --allow-tool '\''shell(uniq)'\'' --allow-tool '\''shell(wc -l pkg/**/*.go)'\'' --allow-tool '\''shell(wc -l pkg/*/*.go)'\'' --allow-tool '\''shell(wc -l pkg/*/README.md)'\'' --allow-tool '\''shell(wc)'\'' --allow-tool '\''shell(yq)'\'' --allow-tool write --allow-all-paths --add-dir "${GITHUB_WORKSPACE}" --prompt-file /tmp/gh-aw/aw-prompts/prompt.txt' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log
         env:
           AWF_REFLECT_ENABLED: 1
           COPILOT_AGENT_RUNNER_TYPE: STANDALONE
diff --git a/actions/setup-cli/install.sh b/actions/setup-cli/install.sh
index c7a5ed2ffed..1c565c9e893 100755
--- a/actions/setup-cli/install.sh
+++ b/actions/setup-cli/install.sh
@@ -1,7 +1,7 @@
 #!/bin/bash
 set +o histexpand
 
-# Kept in sync with actions/setup-cli/install.sh — edit this file, then copy to that path.
+# Script sync note: install-gh-aw.sh is canonical. actions/setup-cli/install.sh is copied from install-gh-aw.sh.
 
 # Script to download and install gh-aw binary for the current OS and architecture
 # Supports: Linux, macOS (Darwin), FreeBSD, Windows (Git Bash/MSYS/Cygwin)

From 457d957d569b70f103bad47f547f0329cddcfdad Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Thu, 18 Jun 2026 11:45:15 +0000
Subject: [PATCH 3/7] fix: treat update_issue target:triggering outside issue
 context as soft skip

- Add MCP-level validation in updateIssueHandler to reject update_issue
  when target is triggering (or unset) and there's no issue context
- Belt-and-suspenders: patch update_handler_factory to propagate
  shouldFail:false from resolveTarget as skipped:true result, preventing
  any existing NDJSON entries from failing the Process Safe Outputs step
- Add tests for both MCP handler validation and factory soft-skip logic

Fixes #40017

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
---
 actions/setup/js/safe_outputs_handlers.cjs    | 41 ++++++++++
 .../setup/js/safe_outputs_handlers.test.cjs   | 82 +++++++++++++++++++
 .../setup/js/safe_outputs_tools_loader.cjs    |  1 +
 actions/setup/js/update_handler_factory.cjs   | 13 ++-
 .../setup/js/update_handler_factory.test.cjs  | 46 +++++++++++
 5 files changed, 182 insertions(+), 1 deletion(-)

diff --git a/actions/setup/js/safe_outputs_handlers.cjs b/actions/setup/js/safe_outputs_handlers.cjs
index 0449d0f3351..eabbdbc835a 100644
--- a/actions/setup/js/safe_outputs_handlers.cjs
+++ b/actions/setup/js/safe_outputs_handlers.cjs
@@ -25,6 +25,7 @@ const { sanitizeTitle, applyTitlePrefix } = require("./sanitize_title.cjs");
 const { parseDeduplicateByTitle, normalizeTitleForDedup, findDuplicateByTitle } = require("./issue_title_dedup.cjs");
 const { validateCreatePullRequestIntent, validatePushToPullRequestBranchIntent, validateCreateIssueIntent, validateAddCommentIntent } = require("./intent_probe.cjs");
 const { globPatternToRegex } = require("./glob_pattern_helpers.cjs");
+const { resolveInvocationContext } = require("./invocation_context_helpers.cjs");
 /**
  * Read and parse a JSON file.
  * @param {string} filePath
@@ -1857,6 +1858,45 @@ function createHandlers(server, appendSafeOutput, config = {}) {
     };
   };
 
+  /**
+   * Handler for update_issue tool
+   * Spec cross-reference: Safe Output Outcome Evaluation §update_issue.
+   * Per Safe Outputs Specification MCE1: Enforces context constraints during tool invocation
+   * to provide immediate feedback to the LLM before recording to NDJSON.
+   * Rejects `target: triggering` (the default) when the workflow has no issue context
+   * (e.g. on schedule or push events), so the agent receives an actionable error
+   * instead of a downstream Process Safe Outputs failure.
+   */
+  const updateIssueHandler = args => {
+    const updateIssueConfig = getSafeOutputsToolConfig(config, "update_issue");
+    const effectiveTarget = updateIssueConfig.target || "triggering";
+
+    if (effectiveTarget === "triggering") {
+      let invocationContext;
+      try {
+        invocationContext = resolveInvocationContext(context);
+      } catch {
+        // If context resolution fails fall through and let the downstream handler deal with it.
+        return defaultHandler("update_issue")(args || {});
+      }
+      const effectiveEventName = invocationContext?.eventName || context.eventName;
+      const effectivePayload = invocationContext?.eventPayload || context.payload;
+      const isIssueCommentOnPR = effectiveEventName === "issue_comment" && Boolean(effectivePayload?.issue?.pull_request);
+      const isIssueContext = effectiveEventName === "issues" || (effectiveEventName === "issue_comment" && !isIssueCommentOnPR);
+
+      if (!isIssueContext) {
+        return buildIntentErrorResponse(
+          `update_issue requires an issue context but the workflow is running on a "${effectiveEventName}" event. ` +
+            `The update-issue handler uses target: triggering which only applies when an issue triggered the workflow. ` +
+            `To report results from this workflow, use create_discussion or create_issue instead. ` +
+            `If you need to update a specific issue, the workflow must configure update-issue: target: '*' and you must supply issue_number.`
+        );
+      }
+    }
+
+    return defaultHandler("update_issue")(args || {});
+  };
+
   /**
    * Handler for update_pull_request tool
    * Spec cross-reference: Safe Output Outcome Evaluation §update_pull_request.
@@ -1888,6 +1928,7 @@ function createHandlers(server, appendSafeOutput, config = {}) {
     addCommentHandler,
     createPullRequestReviewCommentHandler,
     submitPullRequestReviewHandler,
+    updateIssueHandler,
     updatePullRequestHandler,
   };
 }
diff --git a/actions/setup/js/safe_outputs_handlers.test.cjs b/actions/setup/js/safe_outputs_handlers.test.cjs
index 27375fac868..f7f22b54a54 100644
--- a/actions/setup/js/safe_outputs_handlers.test.cjs
+++ b/actions/setup/js/safe_outputs_handlers.test.cjs
@@ -2318,6 +2318,88 @@ describe("safe_outputs_handlers", () => {
       }
     });
   });
+
+  describe("updateIssueHandler", () => {
+    it("should return intent error when target is triggering (default) and not in issue context", () => {
+      // global.context has eventName: "push" (not an issue context)
+      const result = handlers.updateIssueHandler({ body: "Updated body" });
+      expect(result.isError).toBe(true);
+      const responseData = JSON.parse(result.content[0].text);
+      expect(responseData.result).toBe("error");
+      expect(responseData.error).toContain("update_issue");
+      expect(responseData.error).toContain('"push"');
+      expect(mockAppendSafeOutput).not.toHaveBeenCalled();
+    });
+
+    it("should return intent error on schedule event with default target", () => {
+      const savedContext = global.context;
+      global.context = { ...global.context, eventName: "schedule", payload: {} };
+      try {
+        const result = handlers.updateIssueHandler({ body: "Report" });
+        expect(result.isError).toBe(true);
+        const responseData = JSON.parse(result.content[0].text);
+        expect(responseData.result).toBe("error");
+        expect(responseData.error).toContain('"schedule"');
+        expect(responseData.error).toContain("create_discussion");
+        expect(mockAppendSafeOutput).not.toHaveBeenCalled();
+      } finally {
+        global.context = savedContext;
+      }
+    });
+
+    it("should write entry and return success when in issue context with default target", () => {
+      const savedContext = global.context;
+      global.context = { ...global.context, eventName: "issues", payload: { issue: { number: 42 } } };
+      try {
+        const result = handlers.updateIssueHandler({ body: "Updated body" });
+        expect(result.isError).toBeUndefined();
+        const responseData = JSON.parse(result.content[0].text);
+        expect(responseData.result).toBe("success");
+        expect(mockAppendSafeOutput).toHaveBeenCalledWith(expect.objectContaining({ type: "update_issue", body: "Updated body" }));
+      } finally {
+        global.context = savedContext;
+      }
+    });
+
+    it("should write entry and return success when target is '*' regardless of non-issue context", () => {
+      // global.context has eventName: "push" (not an issue context) but target is '*'
+      const wildcardHandlers = createHandlers(mockServer, mockAppendSafeOutput, {
+        "update-issue": { target: "*" },
+      });
+      const result = wildcardHandlers.updateIssueHandler({ issue_number: 42, body: "Updated body" });
+      expect(result.isError).toBeUndefined();
+      const responseData = JSON.parse(result.content[0].text);
+      expect(responseData.result).toBe("success");
+      expect(mockAppendSafeOutput).toHaveBeenCalledWith(expect.objectContaining({ type: "update_issue", issue_number: 42, body: "Updated body" }));
+    });
+
+    it("should write entry and return success on workflow_dispatch with issue aw_context", () => {
+      const savedContext = global.context;
+      global.context = {
+        ...global.context,
+        eventName: "workflow_dispatch",
+        payload: {
+          inputs: {
+            aw_context: JSON.stringify({
+              event_type: "issue_comment",
+              item_type: "issue",
+              item_number: 99,
+              repo: "test-owner/test-repo",
+            }),
+          },
+        },
+      };
+      try {
+        const result = handlers.updateIssueHandler({ body: "Issue update from dispatch" });
+        expect(result.isError).toBeUndefined();
+        const responseData = JSON.parse(result.content[0].text);
+        expect(responseData.result).toBe("success");
+        expect(mockAppendSafeOutput).toHaveBeenCalledWith(expect.objectContaining({ type: "update_issue" }));
+      } finally {
+        global.context = savedContext;
+      }
+    });
+  });
 });
 
 describe("hasUpdatePullRequestFields", () => {
diff --git a/actions/setup/js/safe_outputs_tools_loader.cjs b/actions/setup/js/safe_outputs_tools_loader.cjs
index 76d9fcc9805..0781349bb3c 100644
--- a/actions/setup/js/safe_outputs_tools_loader.cjs
+++ b/actions/setup/js/safe_outputs_tools_loader.cjs
@@ -130,6 +130,7 @@ function attachHandlers(tools, handlers, logger) {
     add_comment: handlers.addCommentHandler,
     create_pull_request_review_comment: handlers.createPullRequestReviewCommentHandler,
     submit_pull_request_review: handlers.submitPullRequestReviewHandler,
+    update_issue: handlers.updateIssueHandler,
     update_pull_request: handlers.updatePullRequestHandler,
   };
 
diff --git a/actions/setup/js/update_handler_factory.cjs b/actions/setup/js/update_handler_factory.cjs
index 35cac8684c0..e635a4650e0 100644
--- a/actions/setup/js/update_handler_factory.cjs
+++ b/actions/setup/js/update_handler_factory.cjs
@@ -72,7 +72,7 @@ function createStandardResolveNumber(config) {
     });
 
     if (!targetResult.success) {
-      return { success: false, error: targetResult.error };
+      return { success: false, shouldFail: targetResult.shouldFail, error: targetResult.error };
     }
 
     return { success: true, number: targetResult.number };
@@ -195,6 +195,17 @@ function createUpdateHandlerFactory(handlerConfig) {
       const itemNumberResult = resolveItemNumber(item, updateTarget, effectiveContext, resolvedTemporaryIds);
 
       if (!itemNumberResult.success) {
+        // shouldFail:false means the target cannot be resolved in this context but it is not
+        // an error (e.g. target:triggering on a schedule run has no triggering issue).
+        // Treat it as a soft skip so it does not count toward fatal failures.
+        if (itemNumberResult.shouldFail === false) {
+          core.info(itemNumberResult.error);
+          return {
+            success: false,
+            skipped: true,
+            error: itemNumberResult.error,
+          };
+        }
         core.warning(itemNumberResult.error);
         return {
           success: false,
diff --git a/actions/setup/js/update_handler_factory.test.cjs b/actions/setup/js/update_handler_factory.test.cjs
index 0b3c55517c6..e59ef8944ff 100644
--- a/actions/setup/js/update_handler_factory.test.cjs
+++ b/actions/setup/js/update_handler_factory.test.cjs
@@ -481,6 +481,23 @@ describe("update_handler_factory.cjs", () => {
       expect(result.error).toBeDefined();
     });
 
+    it("should propagate shouldFail:false when not in issue context (schedule event)", async () => {
+      const resolveNumber = factoryModule.createStandardResolveNumber({
+        itemType: "update_issue",
+        itemNumberField: "issue_number",
+        supportsPR: false,
+        supportsIssue: true,
+      });
+
+      // schedule event — no issue context, resolveTarget returns shouldFail:false
+      const scheduleContext = { ...mockContext, eventName: "schedule", payload: {} };
+      const result = resolveNumber({}, "triggering", scheduleContext);
+
+      expect(result.success).toBe(false);
+      expect(result.shouldFail).toBe(false);
+      expect(result.error).toContain("issue context");
+    });
+
     it("should resolve temporary ID in issue_number field", async () => {
       const resolveNumber = factoryModule.createStandardResolveNumber({
         itemType: "update_issue",
@@ -884,6 +901,35 @@ describe("update_handler_factory.cjs", () => {
       expect(mockExecuteUpdate).not.toHaveBeenCalled();
     });
 
+    it("should return skipped result when resolveItemNumber returns shouldFail:false", async () => {
+      const mockExecuteUpdate = vi.fn();
+
+      const handlerFactory = factoryModule.createUpdateHandlerFactory({
+        itemType: "update_issue",
+        itemTypeName: "issue",
+        supportsPR: false,
+        resolveItemNumber: vi.fn().mockReturnValue({
+          success: false,
+          shouldFail: false,
+          error: 'Target is "triggering" but not running in issue context, skipping update_issue',
+        }),
+        buildUpdateData: vi.fn(),
+        executeUpdate: mockExecuteUpdate,
+        formatSuccessResult: vi.fn(),
+      });
+
+      const handler = await handlerFactory({});
+      const result = await handler({ body: "Report body" });
+
+      expect(result.success).toBe(false);
+      expect(result.skipped).toBe(true);
+      expect(result.error).toContain("issue context");
+      expect(mockExecuteUpdate).not.toHaveBeenCalled();
+      // Should be logged as info (not error/warning)
+      expect(mockCore.info).toHaveBeenCalledWith(expect.stringContaining("issue context"));
+      expect(mockCore.warning).not.toHaveBeenCalledWith(expect.stringContaining("issue context"));
+    });
+
     it("should call itemFilter and skip update when filter returns a result", async () => {
       const mockExecuteUpdate = vi.fn();
       const mockItemFilter = vi.fn().mockResolvedValue({

From 2c0a5d18ef9991c7cac8400e27019daaf3c988a9 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Thu, 18 Jun 2026 12:22:36 +0000
Subject: [PATCH 4/7] fix: add MCP-level target:triggering context validation
 for add_comment and update_pull_request

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
---
 actions/setup/js/safe_outputs_handlers.cjs    |  61 ++++
 .../setup/js/safe_outputs_handlers.test.cjs   | 331 ++++++++++++++----
 actions/setup/setup.sh                        |   1 +
 3 files changed, 331 insertions(+), 62 deletions(-)

diff --git a/actions/setup/js/safe_outputs_handlers.cjs b/actions/setup/js/safe_outputs_handlers.cjs
index eabbdbc835a..5b5e21f8ade 100644
--- a/actions/setup/js/safe_outputs_handlers.cjs
+++ b/actions/setup/js/safe_outputs_handlers.cjs
@@ -1640,6 +1640,38 @@ function createHandlers(server, appendSafeOutput, config = {}) {
       );
     }
 
+    // Reject target:triggering early when no explicit item number and no issue/PR/discussion context.
+    // Per Safe Outputs Specification MCE1: provides actionable feedback before writing to NDJSON.
+    // Mirrors update_issue validation; explicit item_number bypasses this check because the
+    // downstream handler resolves explicit numbers before falling back to triggering context.
+    const effectiveAddCommentTarget = addCommentConfig.target || "triggering";
+    const hasExplicitItemNumber = args?.item_number != null || args?.issue_number != null || args?.["pr-number"] != null;
+    if (effectiveAddCommentTarget === "triggering" && !hasExplicitItemNumber) {
+      let addCommentInvocationContext;
+      try {
+        addCommentInvocationContext = resolveInvocationContext(context);
+      } catch {
+        // Context resolution failed; skip validation and let downstream handle gracefully
+      }
+      if (addCommentInvocationContext !== undefined) {
+        const effectiveEventName = addCommentInvocationContext?.eventName || context.eventName;
+        const effectivePayload = addCommentInvocationContext?.eventPayload || context.payload;
+        const prEventNames = new Set(["pull_request", "pull_request_target", "pull_request_review", "pull_request_review_comment"]);
+        const isIssueCommentOnPR = effectiveEventName === "issue_comment" && Boolean(effectivePayload?.issue?.pull_request);
+        const isIssueContext = effectiveEventName === "issues" || (effectiveEventName === "issue_comment" && !isIssueCommentOnPR);
+        const isPRContext = prEventNames.has(effectiveEventName) || isIssueCommentOnPR;
+        const isDiscussionContext = effectiveEventName === "discussion" || effectiveEventName === "discussion_comment";
+        if (!isIssueContext && !isPRContext && !isDiscussionContext) {
+          return buildIntentErrorResponse(
+            `add_comment requires an issue, pull request, or discussion context but the workflow is running on a "${effectiveEventName}" event. ` +
+              `The add-comment handler uses target: triggering which only applies when an issue, pull request, or discussion triggered the workflow. ` +
+              `To report results from this workflow, use create_discussion or create_issue instead. ` +
+              `If you need to comment on a specific item, provide an explicit item_number.`
+          );
+        }
+      }
+    }
+
     // Build the entry with a temporary_id
     const entry = { ...(args || {}), type: "add_comment" };
     const wildcardTargetValidationError = validateWildcardTargetRequirement(entry);
@@ -1904,6 +1936,9 @@ function createHandlers(server, appendSafeOutput, config = {}) {
    * to provide immediate feedback to the LLM before recording to NDJSON.
    * Uses hasUpdatePullRequestFields to validate that at least one of 'title', 'body',
    * or 'update_branch' is provided before recording to NDJSON.
+   * Rejects `target: triggering` (the default) when the workflow has no pull request context
+   * (e.g. on schedule or push events), so the agent receives an actionable error
+   * instead of a downstream Process Safe Outputs failure.
    */
   const updatePullRequestHandler = args => {
     if (!hasUpdatePullRequestFields(args)) {
@@ -1913,6 +1948,32 @@ function createHandlers(server, appendSafeOutput, config = {}) {
       };
     }
 
+    const updatePRConfig = getSafeOutputsToolConfig(config, "update_pull_request");
+    const effectivePRTarget = updatePRConfig.target || "triggering";
+    if (effectivePRTarget === "triggering") {
+      let invocationContext;
+      try {
+        invocationContext = resolveInvocationContext(context);
+      } catch {
+        // If context resolution fails fall through and let the downstream handler deal with it.
+        return defaultHandler("update_pull_request")(args || {});
+      }
+      const effectiveEventName = invocationContext?.eventName || context.eventName;
+      const effectivePayload = invocationContext?.eventPayload || context.payload;
+      const prEventNames = new Set(["pull_request", "pull_request_target", "pull_request_review", "pull_request_review_comment"]);
+      const isIssueCommentOnPR = effectiveEventName === "issue_comment" && Boolean(effectivePayload?.issue?.pull_request);
+      const isPRContext = prEventNames.has(effectiveEventName) || isIssueCommentOnPR;
+
+      if (!isPRContext) {
+        return buildIntentErrorResponse(
+          `update_pull_request requires a pull request context but the workflow is running on a "${effectiveEventName}" event. ` +
+            `The update-pull-request handler uses target: triggering which only applies when a pull request triggered the workflow. ` +
+            `To report results from this workflow, use create_discussion or create_issue instead. ` +
+            `If you need to update a specific pull request, the workflow must configure update-pull-request: target: '*' and you must supply pull_request_number.`
+        );
+      }
+    }
+
     return defaultHandler("update_pull_request")(args || {});
   };
 
diff --git a/actions/setup/js/safe_outputs_handlers.test.cjs b/actions/setup/js/safe_outputs_handlers.test.cjs
index f7f22b54a54..d238e1aefc6 100644
--- a/actions/setup/js/safe_outputs_handlers.test.cjs
+++ b/actions/setup/js/safe_outputs_handlers.test.cjs
@@ -1672,41 +1672,65 @@ describe("safe_outputs_handlers", () => {
 
   describe("addCommentHandler", () => {
     it("should auto-generate a temporary_id when not provided", () => {
-      const result = handlers.addCommentHandler({ body: "Valid comment body" });
+      const savedContext = global.context;
+      global.context = { ...global.context, eventName: "issues", payload: { issue: { number: 5 } } };
+      try {
+        const result = handlers.addCommentHandler({ body: "Valid comment body" });
 
-      expect(result).toHaveProperty("content");
-      const responseData = JSON.parse(result.content[0].text);
-      expect(responseData.result).toBe("success");
-      expect(responseData.temporary_id).toBeDefined();
-      expect(responseData.temporary_id).toMatch(/^aw_[A-Za-z0-9]{3,12}$/);
+        expect(result).toHaveProperty("content");
+        const responseData = JSON.parse(result.content[0].text);
+        expect(responseData.result).toBe("success");
+        expect(responseData.temporary_id).toBeDefined();
+        expect(responseData.temporary_id).toMatch(/^aw_[A-Za-z0-9]{3,12}$/);
+      } finally {
+        global.context = savedContext;
+      }
     });
 
     it("should use the provided temporary_id when given", () => {
-      const result = handlers.addCommentHandler({ body: "Valid comment body", temporary_id: "aw_abc123" });
+      const savedContext = global.context;
+      global.context = { ...global.context, eventName: "issues", payload: { issue: { number: 5 } } };
+      try {
+        const result = handlers.addCommentHandler({ body: "Valid comment body", temporary_id: "aw_abc123" });
 
-      expect(result).toHaveProperty("content");
-      const responseData = JSON.parse(result.content[0].text);
-      expect(responseData.result).toBe("success");
-      expect(responseData.temporary_id).toBe("aw_abc123");
+        expect(result).toHaveProperty("content");
+        const responseData = JSON.parse(result.content[0].text);
+        expect(responseData.result).toBe("success");
+        expect(responseData.temporary_id).toBe("aw_abc123");
+      } finally {
+        global.context = savedContext;
+      }
     });
 
     it("should return comment reference using temporary_id", () => {
-      const result = handlers.addCommentHandler({ body: "Valid comment body" });
+      const savedContext = global.context;
+      global.context = { ...global.context, eventName: "issues", payload: { issue: { number: 5 } } };
+      try {
+        const result = handlers.addCommentHandler({ body: "Valid comment body" });
 
-      const responseData = JSON.parse(result.content[0].text);
-      expect(responseData.comment).toBe(`#${responseData.temporary_id}`);
+        const responseData = JSON.parse(result.content[0].text);
+        expect(responseData.comment).toBe(`#${responseData.temporary_id}`);
+      } finally {
+        global.context = savedContext;
+      }
     });
 
     it("should record the temporary_id in the NDJSON entry", () => {
-      handlers.addCommentHandler({ body: "Valid comment body", temporary_id: "aw_test01" });
+      const savedContext = global.context;
+      global.context = { ...global.context, eventName: "issues", payload: { issue: { number: 5 } } };
+      try {
+        handlers.addCommentHandler({ body: "Valid comment body", temporary_id: "aw_test01" });
 
-      expect(mockAppendSafeOutput).toHaveBeenCalledWith(
-        expect.objectContaining({
-          type: "add_comment",
-          body: "Valid comment body",
-          temporary_id: "aw_test01",
-        })
-      );
+        expect(mockAppendSafeOutput).toHaveBeenCalledWith(
+          expect.objectContaining({
+            type: "add_comment",
+            body: "Valid comment body",
+            temporary_id: "aw_test01",
+          })
+        );
+      } finally {
+        global.context = savedContext;
+      }
     });
 
     it("should throw validation error for oversized comment body", () => {
@@ -1716,14 +1740,20 @@ describe("safe_outputs_handlers", () => {
     });
 
     it("should reject obvious exploratory placeholder comments before recording them", () => {
-      const result = handlers.addCommentHandler({ body: "test" });
+      const savedContext = global.context;
+      global.context = { ...global.context, eventName: "issues", payload: { issue: { number: 5 } } };
+      try {
+        const result = handlers.addCommentHandler({ body: "test" });
 
-      expect(result.isError).toBe(true);
-      const responseData = JSON.parse(result.content[0].text);
-      expect(responseData.result).toBe("error");
-      expect(responseData.error).toContain("Refusing to record an exploratory comment");
-      expect(responseData.error).toContain("noop or report_incomplete");
-      expect(mockAppendSafeOutput).not.toHaveBeenCalled();
+        expect(result.isError).toBe(true);
+        const responseData = JSON.parse(result.content[0].text);
+        expect(responseData.result).toBe("error");
+        expect(responseData.error).toContain("Refusing to record an exploratory comment");
+        expect(responseData.error).toContain("noop or report_incomplete");
+        expect(mockAppendSafeOutput).not.toHaveBeenCalled();
+      } finally {
+        global.context = savedContext;
+      }
     });
 
     it("should require explicit item_number when add_comment target is '*'", () => {
@@ -1744,6 +1774,7 @@ describe("safe_outputs_handlers", () => {
 
     it("should refuse reply_to_id when discussions are not enabled in config", () => {
       // Default handlers have no discussions: true in config
+      // Discussion check precedes context check so this error surfaces regardless of event context
       const result = handlers.addCommentHandler({
         body: "Reply to a discussion thread",
         reply_to_id: "DC_kwDOABcD1M4AaBbC",
@@ -1759,24 +1790,110 @@ describe("safe_outputs_handlers", () => {
     });
 
     it("should allow reply_to_id when discussions are enabled in config", () => {
-      const discussionHandlers = createHandlers(mockServer, mockAppendSafeOutput, {
-        "add-comment": { enabled: true, discussions: true },
-      });
+      const savedContext = global.context;
+      global.context = { ...global.context, eventName: "discussion", payload: { discussion: { number: 3 } } };
+      try {
+        const discussionHandlers = createHandlers(mockServer, mockAppendSafeOutput, {
+          "add-comment": { enabled: true, discussions: true },
+        });
 
-      const result = discussionHandlers.addCommentHandler({
-        body: "Reply to a discussion thread with real content that is not a test placeholder",
-        reply_to_id: "DC_kwDOABcD1M4AaBbC",
-      });
+        const result = discussionHandlers.addCommentHandler({
+          body: "Reply to a discussion thread with real content that is not a test placeholder",
+          reply_to_id: "DC_kwDOABcD1M4AaBbC",
+        });
+
+        expect(result.isError).toBeUndefined();
+        const responseData = JSON.parse(result.content[0].text);
+        expect(responseData.result).toBe("success");
+        expect(mockAppendSafeOutput).toHaveBeenCalledWith(
+          expect.objectContaining({
+            type: "add_comment",
+            reply_to_id: "DC_kwDOABcD1M4AaBbC",
+          })
+        );
+      } finally {
+        global.context = savedContext;
+      }
+    });
+
+    it("should return intent error when target is triggering (default) and not in issue/PR/discussion context", () => {
+      // global.context has eventName: "push" (no issue/PR/discussion context)
+      const result = handlers.addCommentHandler({ body: "A real comment body that is substantive" });
+      expect(result.isError).toBe(true);
+      const responseData = JSON.parse(result.content[0].text);
+      expect(responseData.result).toBe("error");
+      expect(responseData.error).toContain("add_comment");
+      expect(responseData.error).toContain('"push"');
+      expect(mockAppendSafeOutput).not.toHaveBeenCalled();
+    });
+
+    it("should return intent error on schedule event with default target", () => {
+      const savedContext = global.context;
+      global.context = { ...global.context, eventName: "schedule", payload: {} };
+      try {
+        const result = handlers.addCommentHandler({ body: "A real comment body that is substantive" });
+        expect(result.isError).toBe(true);
+        const responseData = JSON.parse(result.content[0].text);
+        expect(responseData.result).toBe("error");
+        expect(responseData.error).toContain('"schedule"');
+        expect(responseData.error).toContain("create_discussion");
+        expect(mockAppendSafeOutput).not.toHaveBeenCalled();
+      } finally {
+        global.context = savedContext;
+      }
+    });
+
+    it("should write entry when target is triggering and in PR context", () => {
+      const savedContext = global.context;
+      global.context = { ...global.context, eventName: "pull_request", payload: { pull_request: { number: 7 } } };
+      try {
+        const result = handlers.addCommentHandler({ body: "A real comment body for this pull request" });
+        expect(result.isError).toBeUndefined();
+        const responseData = JSON.parse(result.content[0].text);
+        expect(responseData.result).toBe("success");
+        expect(mockAppendSafeOutput).toHaveBeenCalledWith(expect.objectContaining({ type: "add_comment" }));
+      } finally {
+        global.context = savedContext;
+      }
+    });
 
+    it("should write entry when explicit item_number bypasses context check in non-issue/PR event", () => {
+      // push context but explicit item_number provided — downstream handles it directly
+      const result = handlers.addCommentHandler({
+        body: "A real comment body that is substantive enough",
+        item_number: 42,
+      });
       expect(result.isError).toBeUndefined();
       const responseData = JSON.parse(result.content[0].text);
       expect(responseData.result).toBe("success");
-      expect(mockAppendSafeOutput).toHaveBeenCalledWith(
-        expect.objectContaining({
-          type: "add_comment",
-          reply_to_id: "DC_kwDOABcD1M4AaBbC",
-        })
-      );
+      expect(mockAppendSafeOutput).toHaveBeenCalledWith(expect.objectContaining({ type: "add_comment", item_number: 42 }));
+    });
+
+    it("should write entry on workflow_dispatch with issue aw_context", () => {
+      const savedContext = global.context;
+      global.context = {
+        ...global.context,
+        eventName: "workflow_dispatch",
+        payload: {
+          inputs: {
+            aw_context: JSON.stringify({
+              event_type: "issue_comment",
+              item_type: "issue",
+              item_number: 99,
+              repo: "test-owner/test-repo",
+            }),
+          },
+        },
+      };
+      try {
+        const result = handlers.addCommentHandler({ body: "Comment from dispatch with real content" });
+        expect(result.isError).toBeUndefined();
+        const responseData = JSON.parse(result.content[0].text);
+        expect(responseData.result).toBe("success");
+        expect(mockAppendSafeOutput).toHaveBeenCalledWith(expect.objectContaining({ type: "add_comment" }));
+      } finally {
+        global.context = savedContext;
+      }
     });
   });
 
@@ -2276,35 +2393,59 @@ describe("safe_outputs_handlers", () => {
     });
 
     it("should write entry and return success when title is provided", () => {
-      const result = handlers.updatePullRequestHandler({ title: "New Title" });
-      expect(result).toHaveProperty("content");
-      const data = JSON.parse(result.content[0].text);
-      expect(data.result).toBe("success");
-      expect(mockAppendSafeOutput).toHaveBeenCalledWith(expect.objectContaining({ type: "update_pull_request", title: "New Title" }));
+      const savedContext = global.context;
+      global.context = { ...global.context, eventName: "pull_request", payload: { pull_request: { number: 7 } } };
+      try {
+        const result = handlers.updatePullRequestHandler({ title: "New Title" });
+        expect(result).toHaveProperty("content");
+        const data = JSON.parse(result.content[0].text);
+        expect(data.result).toBe("success");
+        expect(mockAppendSafeOutput).toHaveBeenCalledWith(expect.objectContaining({ type: "update_pull_request", title: "New Title" }));
+      } finally {
+        global.context = savedContext;
+      }
     });
 
     it("should write entry and return success when body is provided", () => {
-      const result = handlers.updatePullRequestHandler({ body: "Updated body" });
-      expect(result).toHaveProperty("content");
-      const data = JSON.parse(result.content[0].text);
-      expect(data.result).toBe("success");
-      expect(mockAppendSafeOutput).toHaveBeenCalledWith(expect.objectContaining({ type: "update_pull_request", body: "Updated body" }));
+      const savedContext = global.context;
+      global.context = { ...global.context, eventName: "pull_request", payload: { pull_request: { number: 7 } } };
+      try {
+        const result = handlers.updatePullRequestHandler({ body: "Updated body" });
+        expect(result).toHaveProperty("content");
+        const data = JSON.parse(result.content[0].text);
+        expect(data.result).toBe("success");
+        expect(mockAppendSafeOutput).toHaveBeenCalledWith(expect.objectContaining({ type: "update_pull_request", body: "Updated body" }));
+      } finally {
+        global.context = savedContext;
+      }
     });
 
     it("should write entry and return success when update_branch is true", () => {
-      const result = handlers.updatePullRequestHandler({ update_branch: true });
-      expect(result).toHaveProperty("content");
-      const data = JSON.parse(result.content[0].text);
-      expect(data.result).toBe("success");
-      expect(mockAppendSafeOutput).toHaveBeenCalledWith(expect.objectContaining({ type: "update_pull_request", update_branch: true }));
+      const savedContext = global.context;
+      global.context = { ...global.context, eventName: "pull_request", payload: { pull_request: { number: 7 } } };
+      try {
+        const result = handlers.updatePullRequestHandler({ update_branch: true });
+        expect(result).toHaveProperty("content");
+        const data = JSON.parse(result.content[0].text);
+        expect(data.result).toBe("success");
+        expect(mockAppendSafeOutput).toHaveBeenCalledWith(expect.objectContaining({ type: "update_pull_request", update_branch: true }));
+      } finally {
+        global.context = savedContext;
+      }
     });
 
     it("should write entry and return success when both title and body are provided", () => {
-      const result = handlers.updatePullRequestHandler({ title: "New Title", body: "New body" });
-      expect(result).toHaveProperty("content");
-      const data = JSON.parse(result.content[0].text);
-      expect(data.result).toBe("success");
-      expect(mockAppendSafeOutput).toHaveBeenCalledWith(expect.objectContaining({ type: "update_pull_request", title: "New Title", body: "New body" }));
+      const savedContext = global.context;
+      global.context = { ...global.context, eventName: "pull_request", payload: { pull_request: { number: 7 } } };
+      try {
+        const result = handlers.updatePullRequestHandler({ title: "New Title", body: "New body" });
+        expect(result).toHaveProperty("content");
+        const data = JSON.parse(result.content[0].text);
+        expect(data.result).toBe("success");
+        expect(mockAppendSafeOutput).toHaveBeenCalledWith(expect.objectContaining({ type: "update_pull_request", title: "New Title", body: "New body" }));
+      } finally {
+        global.context = savedContext;
+      }
     });
 
     it("error message should mention all required fields", () => {
@@ -2317,6 +2458,72 @@ describe("safe_outputs_handlers", () => {
         expect(err.message).toContain("'update_branch'");
       }
     });
+
+    it("should return intent error when target is triggering (default) and not in PR context", () => {
+      // global.context has eventName: "push" (not a PR context)
+      const result = handlers.updatePullRequestHandler({ title: "Update title" });
+      expect(result.isError).toBe(true);
+      const responseData = JSON.parse(result.content[0].text);
+      expect(responseData.result).toBe("error");
+      expect(responseData.error).toContain("update_pull_request");
+      expect(responseData.error).toContain('"push"');
+      expect(mockAppendSafeOutput).not.toHaveBeenCalled();
+    });
+
+    it("should return intent error on schedule event with default target", () => {
+      const savedContext = global.context;
+      global.context = { ...global.context, eventName: "schedule", payload: {} };
+      try {
+        const result = handlers.updatePullRequestHandler({ body: "Report" });
+        expect(result.isError).toBe(true);
+        const responseData = JSON.parse(result.content[0].text);
+        expect(responseData.result).toBe("error");
+        expect(responseData.error).toContain('"schedule"');
+        expect(responseData.error).toContain("create_discussion");
+        expect(mockAppendSafeOutput).not.toHaveBeenCalled();
+      } finally {
+        global.context = savedContext;
+      }
+    });
+
+    it("should write entry and return success when target is '*' regardless of non-PR context", () => {
+      // global.context has eventName: "push" (not a PR context) but target is '*'
+      const wildcardHandlers = createHandlers(mockServer, mockAppendSafeOutput, {
+        "update-pull-request": { target: "*" },
+      });
+      const result = wildcardHandlers.updatePullRequestHandler({ pull_request_number: 7, title: "New Title" });
+      expect(result.isError).toBeUndefined();
+      const responseData = JSON.parse(result.content[0].text);
+      expect(responseData.result).toBe("success");
+      expect(mockAppendSafeOutput).toHaveBeenCalledWith(expect.objectContaining({ type: "update_pull_request", pull_request_number: 7, title: "New Title" }));
+    });
+
+    it("should write entry and return success on workflow_dispatch with PR aw_context", () => {
+      const savedContext = global.context;
+      global.context = {
+        ...global.context,
+        eventName: "workflow_dispatch",
+        payload: {
+          inputs: {
+            aw_context: JSON.stringify({
+              event_type: "pull_request",
+              item_type: "pull_request",
+              item_number: 7,
+              repo: "test-owner/test-repo",
+            }),
+          },
+        },
+      };
+      try {
+        const result = handlers.updatePullRequestHandler({ title: "PR update from dispatch" });
+        expect(result.isError).toBeUndefined();
+        const responseData = JSON.parse(result.content[0].text);
+        expect(responseData.result).toBe("success");
+        expect(mockAppendSafeOutput).toHaveBeenCalledWith(expect.objectContaining({ type: "update_pull_request" }));
+      } finally {
+        global.context = savedContext;
+      }
+    });
   });
 
   describe("updateIssueHandler", () => {
diff --git a/actions/setup/setup.sh b/actions/setup/setup.sh
index 30dd6c7bd78..514d15b3bb9 100755
--- a/actions/setup/setup.sh
+++ b/actions/setup/setup.sh
@@ -336,6 +336,7 @@ SAFE_OUTPUTS_FILES=(
   "levenshtein_distance.cjs"
   "markdown_code_region_balancer.cjs"
   "temporary_id.cjs"
+  "invocation_context_helpers.cjs"
 )
 
 SAFE_OUTPUTS_COUNT=0

From be30ef56f8ca695284e23e8d87f8a8532843ebc9 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Thu, 18 Jun 2026 12:25:37 +0000
Subject: [PATCH 5/7] fix: improve test isolation and rename invocationContext
 for consistency

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
---
 actions/setup/js/safe_outputs_handlers.cjs    | 10 ++---
 .../setup/js/safe_outputs_handlers.test.cjs   | 42 ++++++++++++-------
 2 files changed, 31 insertions(+), 21 deletions(-)

diff --git a/actions/setup/js/safe_outputs_handlers.cjs b/actions/setup/js/safe_outputs_handlers.cjs
index 5b5e21f8ade..f8bca8e9636 100644
--- a/actions/setup/js/safe_outputs_handlers.cjs
+++ b/actions/setup/js/safe_outputs_handlers.cjs
@@ -1647,15 +1647,15 @@ function createHandlers(server, appendSafeOutput, config = {}) {
     const effectiveAddCommentTarget = addCommentConfig.target || "triggering";
     const hasExplicitItemNumber = args?.item_number != null || args?.issue_number != null || args?.["pr-number"] != null;
     if (effectiveAddCommentTarget === "triggering" && !hasExplicitItemNumber) {
-      let addCommentInvocationContext;
+      let invocationContext;
       try {
-        addCommentInvocationContext = resolveInvocationContext(context);
+        invocationContext = resolveInvocationContext(context);
       } catch {
         // Context resolution failed; skip validation and let downstream handle gracefully
       }
-      if (addCommentInvocationContext !== undefined) {
-        const effectiveEventName = addCommentInvocationContext?.eventName || context.eventName;
-        const effectivePayload = addCommentInvocationContext?.eventPayload || context.payload;
+      if (invocationContext !== undefined) {
+        const effectiveEventName = invocationContext?.eventName || context.eventName;
+        const effectivePayload = invocationContext?.eventPayload || context.payload;
         const prEventNames = new Set(["pull_request", "pull_request_target", "pull_request_review", "pull_request_review_comment"]);
         const isIssueCommentOnPR = effectiveEventName === "issue_comment" && Boolean(effectivePayload?.issue?.pull_request);
         const isIssueContext = effectiveEventName === "issues" || (effectiveEventName === "issue_comment" && !isIssueCommentOnPR);
diff --git a/actions/setup/js/safe_outputs_handlers.test.cjs b/actions/setup/js/safe_outputs_handlers.test.cjs
index d238e1aefc6..3a9684d47dd 100644
--- a/actions/setup/js/safe_outputs_handlers.test.cjs
+++ b/actions/setup/js/safe_outputs_handlers.test.cjs
@@ -1817,14 +1817,19 @@ describe("safe_outputs_handlers", () => {
     });
 
     it("should return intent error when target is triggering (default) and not in issue/PR/discussion context", () => {
-      // global.context has eventName: "push" (no issue/PR/discussion context)
-      const result = handlers.addCommentHandler({ body: "A real comment body that is substantive" });
-      expect(result.isError).toBe(true);
-      const responseData = JSON.parse(result.content[0].text);
-      expect(responseData.result).toBe("error");
-      expect(responseData.error).toContain("add_comment");
-      expect(responseData.error).toContain('"push"');
-      expect(mockAppendSafeOutput).not.toHaveBeenCalled();
+      const savedContext = global.context;
+      global.context = { ...global.context, eventName: "push", payload: {} };
+      try {
+        const result = handlers.addCommentHandler({ body: "A real comment body that is substantive" });
+        expect(result.isError).toBe(true);
+        const responseData = JSON.parse(result.content[0].text);
+        expect(responseData.result).toBe("error");
+        expect(responseData.error).toContain("add_comment");
+        expect(responseData.error).toContain('"push"');
+        expect(mockAppendSafeOutput).not.toHaveBeenCalled();
+      } finally {
+        global.context = savedContext;
+      }
     });
 
     it("should return intent error on schedule event with default target", () => {
@@ -2460,14 +2465,19 @@ describe("safe_outputs_handlers", () => {
     });
 
     it("should return intent error when target is triggering (default) and not in PR context", () => {
-      // global.context has eventName: "push" (not a PR context)
-      const result = handlers.updatePullRequestHandler({ title: "Update title" });
-      expect(result.isError).toBe(true);
-      const responseData = JSON.parse(result.content[0].text);
-      expect(responseData.result).toBe("error");
-      expect(responseData.error).toContain("update_pull_request");
-      expect(responseData.error).toContain('"push"');
-      expect(mockAppendSafeOutput).not.toHaveBeenCalled();
+      const savedContext = global.context;
+      global.context = { ...global.context, eventName: "push", payload: {} };
+      try {
+        const result = handlers.updatePullRequestHandler({ title: "Update title" });
+        expect(result.isError).toBe(true);
+        const responseData = JSON.parse(result.content[0].text);
+        expect(responseData.result).toBe("error");
+        expect(responseData.error).toContain("update_pull_request");
+        expect(responseData.error).toContain('"push"');
+        expect(mockAppendSafeOutput).not.toHaveBeenCalled();
+      } finally {
+        global.context = savedContext;
+      }
     });
 
     it("should return intent error on schedule event with default target", () => {

From 132323ff65fafe55c7a753369a427fc86f420400 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Thu, 18 Jun 2026 13:29:40 +0000
Subject: [PATCH 6/7] fix: address github-actions review feedback on
 safe_outputs_handlers

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
---
 actions/setup/js/safe_outputs_handlers.cjs    |  60 +++++---
 .../setup/js/safe_outputs_handlers.test.cjs   | 135 ++++++++++++++++--
 2 files changed, 168 insertions(+), 27 deletions(-)

diff --git a/actions/setup/js/safe_outputs_handlers.cjs b/actions/setup/js/safe_outputs_handlers.cjs
index f8bca8e9636..29e0b31d5c4 100644
--- a/actions/setup/js/safe_outputs_handlers.cjs
+++ b/actions/setup/js/safe_outputs_handlers.cjs
@@ -26,6 +26,23 @@ const { parseDeduplicateByTitle, normalizeTitleForDedup, findDuplicateByTitle }
 const { validateCreatePullRequestIntent, validatePushToPullRequestBranchIntent, validateCreateIssueIntent, validateAddCommentIntent } = require("./intent_probe.cjs");
 const { globPatternToRegex } = require("./glob_pattern_helpers.cjs");
 const { resolveInvocationContext } = require("./invocation_context_helpers.cjs");
+
+/** PR event names used for target:triggering context validation across all safe-output handlers. */
+const PR_EVENT_NAMES = new Set(["pull_request", "pull_request_target", "pull_request_review", "pull_request_review_comment"]);
+
+/**
+ * Resolve effective event name and payload from an invocation context,
+ * falling back to the raw GitHub Actions context.
+ * @param {ReturnType<typeof resolveInvocationContext> | null | undefined} invocationContext
+ * @param {any} rawContext
+ */
+function resolveEffectiveContext(invocationContext, rawContext) {
+  return {
+    effectiveEventName: invocationContext?.eventName || rawContext.eventName,
+    effectivePayload: invocationContext?.eventPayload || rawContext.payload,
+  };
+}
+
 /**
  * Read and parse a JSON file.
  * @param {string} filePath
@@ -1647,19 +1664,21 @@ function createHandlers(server, appendSafeOutput, config = {}) {
     const effectiveAddCommentTarget = addCommentConfig.target || "triggering";
     const hasExplicitItemNumber = args?.item_number != null || args?.issue_number != null || args?.["pr-number"] != null;
     if (effectiveAddCommentTarget === "triggering" && !hasExplicitItemNumber) {
-      let invocationContext;
+      let invocationContext = null;
       try {
         invocationContext = resolveInvocationContext(context);
-      } catch {
-        // Context resolution failed; skip validation and let downstream handle gracefully
+      } catch (err) {
+        // A validation error (e.g. disallowed target_repo / SEC-005) is a real failure — surface it.
+        if (err?.message?.startsWith(ERR_VALIDATION)) {
+          return buildIntentErrorResponse(err.message);
+        }
+        // Unexpected structural error: skip validation and let downstream handle gracefully.
       }
-      if (invocationContext !== undefined) {
-        const effectiveEventName = invocationContext?.eventName || context.eventName;
-        const effectivePayload = invocationContext?.eventPayload || context.payload;
-        const prEventNames = new Set(["pull_request", "pull_request_target", "pull_request_review", "pull_request_review_comment"]);
+      if (invocationContext != null) {
+        const { effectiveEventName, effectivePayload } = resolveEffectiveContext(invocationContext, context);
         const isIssueCommentOnPR = effectiveEventName === "issue_comment" && Boolean(effectivePayload?.issue?.pull_request);
         const isIssueContext = effectiveEventName === "issues" || (effectiveEventName === "issue_comment" && !isIssueCommentOnPR);
-        const isPRContext = prEventNames.has(effectiveEventName) || isIssueCommentOnPR;
+        const isPRContext = PR_EVENT_NAMES.has(effectiveEventName) || isIssueCommentOnPR;
         const isDiscussionContext = effectiveEventName === "discussion" || effectiveEventName === "discussion_comment";
         if (!isIssueContext && !isPRContext && !isDiscussionContext) {
           return buildIntentErrorResponse(
@@ -1907,12 +1926,15 @@ function createHandlers(server, appendSafeOutput, config = {}) {
       let invocationContext;
       try {
         invocationContext = resolveInvocationContext(context);
-      } catch {
-        // If context resolution fails fall through and let the downstream handler deal with it.
+      } catch (err) {
+        // A validation error (e.g. disallowed target_repo / SEC-005) is a real failure — surface it.
+        if (err?.message?.startsWith(ERR_VALIDATION)) {
+          return buildIntentErrorResponse(err.message);
+        }
+        // Unexpected structural error: fall through and let the downstream handler deal with it.
         return defaultHandler("update_issue")(args || {});
       }
-      const effectiveEventName = invocationContext?.eventName || context.eventName;
-      const effectivePayload = invocationContext?.eventPayload || context.payload;
+      const { effectiveEventName, effectivePayload } = resolveEffectiveContext(invocationContext, context);
       const isIssueCommentOnPR = effectiveEventName === "issue_comment" && Boolean(effectivePayload?.issue?.pull_request);
       const isIssueContext = effectiveEventName === "issues" || (effectiveEventName === "issue_comment" && !isIssueCommentOnPR);
 
@@ -1954,15 +1976,17 @@ function createHandlers(server, appendSafeOutput, config = {}) {
       let invocationContext;
       try {
         invocationContext = resolveInvocationContext(context);
-      } catch {
-        // If context resolution fails fall through and let the downstream handler deal with it.
+      } catch (err) {
+        // A validation error (e.g. disallowed target_repo / SEC-005) is a real failure — surface it.
+        if (err?.message?.startsWith(ERR_VALIDATION)) {
+          return buildIntentErrorResponse(err.message);
+        }
+        // Unexpected structural error: fall through and let the downstream handler deal with it.
         return defaultHandler("update_pull_request")(args || {});
       }
-      const effectiveEventName = invocationContext?.eventName || context.eventName;
-      const effectivePayload = invocationContext?.eventPayload || context.payload;
-      const prEventNames = new Set(["pull_request", "pull_request_target", "pull_request_review", "pull_request_review_comment"]);
+      const { effectiveEventName, effectivePayload } = resolveEffectiveContext(invocationContext, context);
       const isIssueCommentOnPR = effectiveEventName === "issue_comment" && Boolean(effectivePayload?.issue?.pull_request);
-      const isPRContext = prEventNames.has(effectiveEventName) || isIssueCommentOnPR;
+      const isPRContext = PR_EVENT_NAMES.has(effectiveEventName) || isIssueCommentOnPR;
 
       if (!isPRContext) {
         return buildIntentErrorResponse(
diff --git a/actions/setup/js/safe_outputs_handlers.test.cjs b/actions/setup/js/safe_outputs_handlers.test.cjs
index 3a9684d47dd..934b02a10b2 100644
--- a/actions/setup/js/safe_outputs_handlers.test.cjs
+++ b/actions/setup/js/safe_outputs_handlers.test.cjs
@@ -1862,16 +1862,39 @@ describe("safe_outputs_handlers", () => {
       }
     });
 
+    it("should write entry when issue_comment fires on a PR (valid PR context for add_comment)", () => {
+      const savedContext = global.context;
+      global.context = {
+        ...global.context,
+        eventName: "issue_comment",
+        payload: { issue: { number: 7, pull_request: { url: "https://api.github.com/repos/test-owner/test-repo/pulls/7" } } },
+      };
+      try {
+        const result = handlers.addCommentHandler({ body: "A real comment body for this PR comment thread" });
+        expect(result.isError).toBeUndefined();
+        const responseData = JSON.parse(result.content[0].text);
+        expect(responseData.result).toBe("success");
+        expect(mockAppendSafeOutput).toHaveBeenCalledWith(expect.objectContaining({ type: "add_comment" }));
+      } finally {
+        global.context = savedContext;
+      }
+    });
+
     it("should write entry when explicit item_number bypasses context check in non-issue/PR event", () => {
-      // push context but explicit item_number provided — downstream handles it directly
-      const result = handlers.addCommentHandler({
-        body: "A real comment body that is substantive enough",
-        item_number: 42,
-      });
-      expect(result.isError).toBeUndefined();
-      const responseData = JSON.parse(result.content[0].text);
-      expect(responseData.result).toBe("success");
-      expect(mockAppendSafeOutput).toHaveBeenCalledWith(expect.objectContaining({ type: "add_comment", item_number: 42 }));
+      const savedContext = global.context;
+      global.context = { ...global.context, eventName: "push", payload: {} };
+      try {
+        const result = handlers.addCommentHandler({
+          body: "A real comment body that is substantive enough",
+          item_number: 42,
+        });
+        expect(result.isError).toBeUndefined();
+        const responseData = JSON.parse(result.content[0].text);
+        expect(responseData.result).toBe("success");
+        expect(mockAppendSafeOutput).toHaveBeenCalledWith(expect.objectContaining({ type: "add_comment", item_number: 42 }));
+      } finally {
+        global.context = savedContext;
+      }
     });
 
     it("should write entry on workflow_dispatch with issue aw_context", () => {
@@ -1900,6 +1923,25 @@ describe("safe_outputs_handlers", () => {
         global.context = savedContext;
       }
     });
+
+    it("should return intent error on workflow_dispatch with no event_name override", () => {
+      const savedContext = global.context;
+      global.context = {
+        ...global.context,
+        eventName: "workflow_dispatch",
+        payload: { inputs: {} }, // no event_name, no aw_context
+      };
+      try {
+        const result = handlers.addCommentHandler({ body: "A real comment body that is substantive" });
+        expect(result.isError).toBe(true);
+        const responseData = JSON.parse(result.content[0].text);
+        expect(responseData.result).toBe("error");
+        expect(responseData.error).toContain('"workflow_dispatch"');
+        expect(mockAppendSafeOutput).not.toHaveBeenCalled();
+      } finally {
+        global.context = savedContext;
+      }
+    });
   });
 
   describe("createIssueHandler", () => {
@@ -2534,6 +2576,43 @@ describe("safe_outputs_handlers", () => {
         global.context = savedContext;
       }
     });
+
+    it("should return intent error on workflow_dispatch with no event_name override", () => {
+      const savedContext = global.context;
+      global.context = {
+        ...global.context,
+        eventName: "workflow_dispatch",
+        payload: { inputs: {} }, // no event_name, no aw_context
+      };
+      try {
+        const result = handlers.updatePullRequestHandler({ title: "No context title" });
+        expect(result.isError).toBe(true);
+        const responseData = JSON.parse(result.content[0].text);
+        expect(responseData.result).toBe("error");
+        expect(responseData.error).toContain('"workflow_dispatch"');
+        expect(mockAppendSafeOutput).not.toHaveBeenCalled();
+      } finally {
+        global.context = savedContext;
+      }
+    });
+
+    it("should write entry when issue_comment fires on a PR (PR context for update_pull_request)", () => {
+      const savedContext = global.context;
+      global.context = {
+        ...global.context,
+        eventName: "issue_comment",
+        payload: { issue: { number: 7, pull_request: { url: "https://api.github.com/repos/test-owner/test-repo/pulls/7" } } },
+      };
+      try {
+        const result = handlers.updatePullRequestHandler({ title: "PR update from issue_comment" });
+        expect(result.isError).toBeUndefined();
+        const responseData = JSON.parse(result.content[0].text);
+        expect(responseData.result).toBe("success");
+        expect(mockAppendSafeOutput).toHaveBeenCalledWith(expect.objectContaining({ type: "update_pull_request" }));
+      } finally {
+        global.context = savedContext;
+      }
+    });
   });
 
   describe("updateIssueHandler", () => {
@@ -2616,6 +2695,44 @@ describe("safe_outputs_handlers", () => {
         global.context = savedContext;
       }
     });
+
+    it("should return intent error for issue_comment on a PR (not issue context)", () => {
+      const savedContext = global.context;
+      global.context = {
+        ...global.context,
+        eventName: "issue_comment",
+        payload: { issue: { number: 7, pull_request: { url: "https://api.github.com/repos/test-owner/test-repo/pulls/7" } } },
+      };
+      try {
+        const result = handlers.updateIssueHandler({ body: "Update body" });
+        expect(result.isError).toBe(true);
+        const data = JSON.parse(result.content[0].text);
+        expect(data.result).toBe("error");
+        expect(data.error).toContain("issue context");
+        expect(mockAppendSafeOutput).not.toHaveBeenCalled();
+      } finally {
+        global.context = savedContext;
+      }
+    });
+
+    it("should return intent error on workflow_dispatch with no event_name override", () => {
+      const savedContext = global.context;
+      global.context = {
+        ...global.context,
+        eventName: "workflow_dispatch",
+        payload: { inputs: {} }, // no event_name, no aw_context
+      };
+      try {
+        const result = handlers.updateIssueHandler({ body: "Issue update no context" });
+        expect(result.isError).toBe(true);
+        const responseData = JSON.parse(result.content[0].text);
+        expect(responseData.result).toBe("error");
+        expect(responseData.error).toContain('"workflow_dispatch"');
+        expect(mockAppendSafeOutput).not.toHaveBeenCalled();
+      } finally {
+        global.context = savedContext;
+      }
+    });
   });
 });
 

From 18fb30eb375459e5970bcec20435201aa3c30e28 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Thu, 18 Jun 2026 13:33:26 +0000
Subject: [PATCH 7/7] fix: unify null-guard pattern across all three
 triggering-context handlers

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
---
 actions/setup/js/safe_outputs_handlers.cjs | 58 +++++++++++-----------
 1 file changed, 30 insertions(+), 28 deletions(-)

diff --git a/actions/setup/js/safe_outputs_handlers.cjs b/actions/setup/js/safe_outputs_handlers.cjs
index 29e0b31d5c4..846041ef455 100644
--- a/actions/setup/js/safe_outputs_handlers.cjs
+++ b/actions/setup/js/safe_outputs_handlers.cjs
@@ -1923,7 +1923,7 @@ function createHandlers(server, appendSafeOutput, config = {}) {
     const effectiveTarget = updateIssueConfig.target || "triggering";
 
     if (effectiveTarget === "triggering") {
-      let invocationContext;
+      let invocationContext = null;
       try {
         invocationContext = resolveInvocationContext(context);
       } catch (err) {
@@ -1931,20 +1931,21 @@ function createHandlers(server, appendSafeOutput, config = {}) {
         if (err?.message?.startsWith(ERR_VALIDATION)) {
           return buildIntentErrorResponse(err.message);
         }
-        // Unexpected structural error: fall through and let the downstream handler deal with it.
-        return defaultHandler("update_issue")(args || {});
+        // Unexpected structural error: skip validation and let downstream handle gracefully.
       }
-      const { effectiveEventName, effectivePayload } = resolveEffectiveContext(invocationContext, context);
-      const isIssueCommentOnPR = effectiveEventName === "issue_comment" && Boolean(effectivePayload?.issue?.pull_request);
-      const isIssueContext = effectiveEventName === "issues" || (effectiveEventName === "issue_comment" && !isIssueCommentOnPR);
-
-      if (!isIssueContext) {
-        return buildIntentErrorResponse(
-          `update_issue requires an issue context but the workflow is running on a "${effectiveEventName}" event. ` +
-            `The update-issue handler uses target: triggering which only applies when an issue triggered the workflow. ` +
-            `To report results from this workflow, use create_discussion or create_issue instead. ` +
-            `If you need to update a specific issue, the workflow must configure update-issue: target: '*' and you must supply issue_number.`
-        );
+      if (invocationContext != null) {
+        const { effectiveEventName, effectivePayload } = resolveEffectiveContext(invocationContext, context);
+        const isIssueCommentOnPR = effectiveEventName === "issue_comment" && Boolean(effectivePayload?.issue?.pull_request);
+        const isIssueContext = effectiveEventName === "issues" || (effectiveEventName === "issue_comment" && !isIssueCommentOnPR);
+
+        if (!isIssueContext) {
+          return buildIntentErrorResponse(
+            `update_issue requires an issue context but the workflow is running on a "${effectiveEventName}" event. ` +
+              `The update-issue handler uses target: triggering which only applies when an issue triggered the workflow. ` +
+              `To report results from this workflow, use create_discussion or create_issue instead. ` +
+              `If you need to update a specific issue, the workflow must configure update-issue: target: '*' and you must supply issue_number.`
+          );
+        }
       }
     }
 
@@ -1973,7 +1974,7 @@ function createHandlers(server, appendSafeOutput, config = {}) {
     const updatePRConfig = getSafeOutputsToolConfig(config, "update_pull_request");
     const effectivePRTarget = updatePRConfig.target || "triggering";
     if (effectivePRTarget === "triggering") {
-      let invocationContext;
+      let invocationContext = null;
       try {
         invocationContext = resolveInvocationContext(context);
       } catch (err) {
@@ -1981,20 +1982,21 @@ function createHandlers(server, appendSafeOutput, config = {}) {
         if (err?.message?.startsWith(ERR_VALIDATION)) {
           return buildIntentErrorResponse(err.message);
         }
-        // Unexpected structural error: fall through and let the downstream handler deal with it.
-        return defaultHandler("update_pull_request")(args || {});
+        // Unexpected structural error: skip validation and let downstream handle gracefully.
       }
-      const { effectiveEventName, effectivePayload } = resolveEffectiveContext(invocationContext, context);
-      const isIssueCommentOnPR = effectiveEventName === "issue_comment" && Boolean(effectivePayload?.issue?.pull_request);
-      const isPRContext = PR_EVENT_NAMES.has(effectiveEventName) || isIssueCommentOnPR;
-
-      if (!isPRContext) {
-        return buildIntentErrorResponse(
-          `update_pull_request requires a pull request context but the workflow is running on a "${effectiveEventName}" event. ` +
-            `The update-pull-request handler uses target: triggering which only applies when a pull request triggered the workflow. ` +
-            `To report results from this workflow, use create_discussion or create_issue instead. ` +
-            `If you need to update a specific pull request, the workflow must configure update-pull-request: target: '*' and you must supply pull_request_number.`
-        );
+      if (invocationContext != null) {
+        const { effectiveEventName, effectivePayload } = resolveEffectiveContext(invocationContext, context);
+        const isIssueCommentOnPR = effectiveEventName === "issue_comment" && Boolean(effectivePayload?.issue?.pull_request);
+        const isPRContext = PR_EVENT_NAMES.has(effectiveEventName) || isIssueCommentOnPR;
+
+        if (!isPRContext) {
+          return buildIntentErrorResponse(
+            `update_pull_request requires a pull request context but the workflow is running on a "${effectiveEventName}" event. ` +
+              `The update-pull-request handler uses target: triggering which only applies when a pull request triggered the workflow. ` +
+              `To report results from this workflow, use create_discussion or create_issue instead. ` +
+              `If you need to update a specific pull request, the workflow must configure update-pull-request: target: '*' and you must supply pull_request_number.`
+          );
+        }
       }
     }
 
