Skip to content

Tell engines without MCP that the safeoutputs CLI is the only transport - #63493

Merged
pelikhan merged 6 commits into
mainfrom
copilot/fix-safe-outputs-prompt
Sep 26, 2026
Merged

pelikhan merged 6 commits into
mainfrom
copilot/fix-safe-outputs-prompt

Conversation

Copilot AI commented Sep 25, 2026 •

Copy link
Copy Markdown
Contributor

The safe-outputs prompt fragments unconditionally describe the safeoutputs CLI as an optional equivalent to calling safe-output tools directly. That framing is wrong for engines with EngineCapabilities.MCP: false (today, engine: pi), which have no tool-call interface at all — agents there look for a command literally named after the tool (type create_pull_request, compgen -c | grep …), find nothing, conclude the tool is unavailable, and finish without emitting any safe output.

Changes

  • Prompt fragments — safe_outputs_prompt.md and mcp_cli_tools_with_safeoutputs_prompt.md replace their hard-coded transport sentences with __GH_AW_SAFE_OUTPUTS_TRANSPORT__ / __GH_AW_SAFE_OUTPUTS_CLI_TRANSPORT__, resolved by the existing compile-time placeholder-substitution step.
  • pkg/workflow/safe_outputs_transport_prompt.go (new) — engineSupportsMCPToolCalls() resolves the engine from the registry and reads GetCapabilities().MCP (unknown/unset engines stay MCP-capable), plus the two wording variants per fragment. The MCP-false wording states the CLI is the only transport and pre-empts the failed discovery path.
  • Wiring — collectPromptSections (safe-outputs section) and buildMCPCLIPromptSection (safeoutputs variant only) attach the resolved text as section EnvVars.
  • Tests — composed-prompt assertions for pi vs. an MCP engine, and compiled-lock-file integration assertions that a pi workflow contains no "optional equivalent transport" framing and does carry the "ONLY transport" statement.
  • Lock files — recompiled; the 31 engine: pi workflows pick up the new wording, all others are unchanged in substance.

Rendered for engine: pi:

GH_AW_SAFE_OUTPUTS_TRANSPORT: 'This engine has no MCP tool-call interface, so the `safeoutputs` CLI on `PATH` is the ONLY way to invoke the tools listed in `<safe-output-tools>`: run `safeoutputs <tool_name> <json>` from bash … shell discovery such as `type create_pull_request` or `compgen -c` will never find one; always go through the `safeoutputs` binary.'

Not covered here

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix safe-outputs prompt for engine:pi tool calls Tell engines without MCP that the safeoutputs CLI is the only transport Sep 25, 2026
Copilot AI requested a review from pelikhan September 25, 2026 19:36
@pelikhan
pelikhan marked this pull request as ready for review September 25, 2026 19:45
Copilot AI balanced review requested due to automatic review settings September 25, 2026 19:45

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

Per-compiler behavior-defined engines such as Aider and DeepSeek Harness remain incorrectly classified as MCP-capable.

Get a fresh assessment by requesting another Copilot review.

Review effort: Balanced
Findings: 1 Medium severity

Open (1)
What changed in this PR

Adds engine-aware safe-output guidance so MCP-disabled engines use the safeoutputs CLI exclusively.

Changes:

  • Adds capability-based transport wording.
  • Wires prompt placeholders into compilation.
  • Adds tests and recompiles generated workflows.
File Description
pkg/​workflow/​safe_outputs_transport_prompt.go Resolves transport guidance from engine capabilities.
pkg/​workflow/​unified_prompt_step.go Injects safe-output transport wording.
pkg/​workflow/​mcp_cli_mount.go Injects CLI-specific transport wording.
pkg/​workflow/​prompt_validation_test.go Tests composed prompts for MCP and non-MCP engines.
pkg/​workflow/​pi_safe_outputs_transport_integration_test.go Tests compiled Pi and Copilot workflows.
actions/​setup/​md/​safe_outputs_prompt.md Adds transport placeholder.
actions/​setup/​md/​mcp_cli_tools_with_safeoutputs_prompt.md Adds CLI transport placeholder.
.github/​workflows/​*.lock.yml (298 files) Carries regenerated prompt environment substitutions.

💡 Add a code-review agent skill for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +37 to +39
engine, err := GetGlobalEngineRegistry().GetEngine(strings.ToLower(data.EngineConfig.ID))
if err != nil || engine == nil {
return true
@github-actions

github-actions Bot commented Sep 25, 2026 •

Copy link
Copy Markdown
Contributor

✅ PR Code Quality Reviewer completed the code quality review.

🔎 Code quality review by PR Code Quality Reviewer

@github-actions

github-actions Bot commented Sep 25, 2026 •

Copy link
Copy Markdown
Contributor

✅ Ponytail Reviewer completed successfully!

Cannot run ponytail review: /tmp/gh-aw/agent/pr-diff.patch is empty, so there are no changed lines to review.

Generated by Ponytail Reviewer for #63493

@github-actions

github-actions Bot commented Sep 25, 2026 •

Copy link
Copy Markdown
Contributor

🧠 Matt Pocock Skills Reviewer has completed the skills-based review. ✅

🧠 Reviewed using Matt Pocock's skills by Matt Pocock Skills Reviewer

@github-actions

github-actions Bot commented Sep 25, 2026 •

Copy link
Copy Markdown
Contributor

✅ Test Quality Sentinel completed test quality analysis.

Test Quality Sentinel skipped because pre-fetch PR data was unavailable: unable to fetch PR file list

🧪 Test quality analysis by Test Quality Sentinel

@github-actions

github-actions Bot commented Sep 25, 2026 •

Copy link
Copy Markdown
Contributor

✅ Design Decision Gate 🏗️ completed the design decision gate check. See the comment below for the result and any generated ADR draft.

🏗️ ADR gate enforced by Design Decision Gate 🏗️

@github-actions

Copy link
Copy Markdown
Contributor
🏗️ ADR required — draft added for PR #63493

I enforced the ADR gate for this PR because the prefetch summary shows 210 additions in default business-logic directories, which is above the 100-line threshold.

Evidence used

  • adr-prefetch-summary.json: requires_adr_by_default_volume: true, diff_available: false
  • pr.json: PR title/body describe an engine-capability-aware safe-output transport change for engine: pi
  • pr-files.json: changes span prompt fragments, workflow compilation code, tests, and many compiled lock files
  • PR body and linked issue #63239: no existing ADR link or ADR section was provided, but the implementation clearly introduces an architectural prompt-generation decision

Result

I added a draft ADR at:

  • docs/adr/63493-make-safeoutputs-cli-the-only-transport-for-pi.md

Inferred decision

  • Make safe-output transport instructions depend on engine MCP capability.
  • State explicitly that for engine: pi, the safeoutputs CLI is the only transport.

Next action

Please review and refine the draft ADR in this branch, especially the decision scope and trade-offs, before merging.

🏗️ ADR gate enforced by Design Decision Gate 🏗️ · pi · gpt54 · 18.5 AIC · ⌖ 7.49 AIC · ⊞ 10.2K · ◷
Comment /review to run again

@github-actions

Copy link
Copy Markdown
Contributor

Comment Memory

reviewed_at: 2026-09-25T19:56:52.935+00:00
review_event: REQUEST_CHANGES
top_themes:
  - global engine registry bypass leaves imported mcp false engines on broken safe-outputs wording
  - regression coverage only exercises built-in pi, not compiler-registered custom engines
files_reviewed:
  - actions/setup/md/safe_outputs_prompt.md
  - actions/setup/md/mcp_cli_tools_with_safeoutputs_prompt.md
  - pkg/workflow/safe_outputs_transport_prompt.go
  - pkg/workflow/mcp_cli_mount.go
  - pkg/workflow/unified_prompt_step.go
  - pkg/workflow/prompt_validation_test.go
  - pkg/workflow/pi_safe_outputs_transport_integration_test.go
comment_count: 1

Note

This comment is managed by comment memory.

It stores persistent context for this thread in the code block at the top of this comment.
Edit only the text inside the backtick fences; workflow metadata and the footer are regenerated automatically.

Learn more about comment memory

🔎 Code quality review by PR Code Quality Reviewer · copilot · gpt54 · 51.2 AIC · ⌖ 9.97 AIC · ⊞ 20.3K · ◷
Comment /review to run again

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Request changes

The fix is still incomplete because the new capability lookup uses the global engine registry. Imported behavior-defined engines with mcp false are registered on the compiler instance, so they still get the old optional transport wording instead of the required CLI-only wording.

🔎 Code quality review by PR Code Quality Reviewer · copilot · gpt54 · 51.2 AIC · ⌖ 9.97 AIC · ⊞ 20.3K
Comment /review to run again

func TestComposedPromptForNonMCPEngineStatesCLIIsOnlyTransport(t *testing.T) {
compiler := &Compiler{}
data := &WorkflowData{
EngineConfig: &EngineConfig{ID: "pi"},

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test only covers built-in pi, so imported behavior-defined engines with mcp false can still ship the wrong optional transport prompt; please add a regression for a compiler-registered custom non-MCP engine as well.

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Skills-Based Review 🧠

Applied /codebase-design on the new transport-wording module (safe_outputs_transport_prompt.go). The mechanism (registry-driven capability lookup, compile-time placeholder substitution) is sound and well-tested for the direct compilation path, but there's a real gap for engines registered on a per-compiler basis.

📋 Key Themes & Highlights

Key Themes

  • Registry scope mismatch: engineSupportsMCPToolCalls resolves engines via GetGlobalEngineRegistry(), but behavior-defined engines imported into a specific compiler instance (e.g. shared workflows declaring mcp: false) are registered on c.engineRegistry, not the global singleton. For those engines the lookup misses, falls back to true, and the exact "optional CLI" wording bug this PR fixes reappears for imported non-MCP engines. This was also flagged by a prior automated reviewer comment on the same line.

Positive Highlights

  • ✅ Clean separation: wording constants, capability lookup, and per-file rendering are each isolated and easy to follow.
  • ✅ Solid test coverage for the built-in pi engine path — both composed-prompt unit tests and compiled-lock-file integration tests assert the exact wording transitions in both directions (MCP-capable vs. not).
  • ✅ Good doc comment explaining why the distinction matters (agents hunting for a nonexistent named command).

Minor note

  • No .changeset/*.md entry found for this user-facing prompt-wording change; the project's other prompt/behavior fixes in .changeset/ suggest one may be expected here for the release notes.

@copilot please address the review comments above.

🧠 Reviewed using Matt Pocock's skills by Matt Pocock Skills Reviewer · copilot · sonnet50 · 83.6 AIC · ⌖ 14.6 AIC · ⊞ 10.1K
Comment /matt to run again

if data == nil || data.EngineConfig == nil || data.EngineConfig.ID == "" {
return true
}
engine, err := GetGlobalEngineRegistry().GetEngine(strings.ToLower(data.EngineConfig.ID))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[/codebase-design] engineSupportsMCPToolCalls looks up GetGlobalEngineRegistry() instead of the compiler-instance c.engineRegistry. Behavior-defined engines imported/registered per-compiler (e.g. shared workflows with mcp: false) are absent from the global singleton, so this falls back to true (MCP-capable) and silently re-introduces the exact bug this PR fixes for those engines.

💡 Suggested fix

Thread the compiler's registry through instead of relying on the global singleton, matching how other call sites (e.g. compiler_activation_job.go, compiler_jobs.go) use c.engineRegistry.GetEngine(...). This likely means passing the registry (or the resolved engine) into safeOutputsTransportText/safeOutputsCLITransportText rather than resolving it fresh from the global registry inside a WorkflowData-only helper.

func engineSupportsMCPToolCalls(registry *EngineRegistry, data *WorkflowData) bool {
    if data == nil || data.EngineConfig == nil || data.EngineConfig.ID == "" || registry == nil {
        return true
    }
    engine, err := registry.GetEngine(strings.ToLower(data.EngineConfig.ID))
    if err != nil || engine == nil {
        return true
    }
    return engine.GetCapabilities().MCP
}

@copilot please address this.

@pelikhan

Copy link
Copy Markdown
Collaborator

@copilot run pr-finisher skill

Copilot AI and others added 2 commits September 25, 2026 21:09
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>

Copilot AI commented Sep 25, 2026

Copy link
Copy Markdown
Contributor Author

@copilot run pr-finisher skill

Completed the pr-finisher pass. The compiler now resolves safe-output MCP capability through its active engine catalog, including runtime aliases, with global fallback for unresolved engines. Added regression coverage and regenerated the affected Aider and DeepSeek Harness lock files.

@pelikhan

Copy link
Copy Markdown
Collaborator

@copilot minimize overhead in compiled code

@gh-aw-bot

Copy link
Copy Markdown
Collaborator

pr-sous-chef
@copilot There is still forward progress needed on this PR.

Generated by 👨🍳 PR Sous Chef

Generated by 👨‍🍳 PR Sous Chef · pi · gpt54 · 44.4 AIC · ⌖ 8.72 AIC · ⊞ 9.6K · ◷
Comment /souschef to run again

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>

Copilot AI commented Sep 25, 2026

Copy link
Copy Markdown
Contributor Author

@copilot minimize overhead in compiled code

Implemented in 5668619. Safe-output transport wording now uses static prompt fragments, removing the repeated transport environment variables and their substitution entries from compiled workflows while preserving CLI-only selection for MCP-disabled engines.

Copilot AI commented Sep 25, 2026

Copy link
Copy Markdown
Contributor Author

pr-sous-chef
@copilot There is still forward progress needed on this PR.
...

The overhead reduction is in 5668619: static prompt fragments replace per-workflow transport substitutions. Refreshed local checks (make fmt, make lint, make test-unit, make test, and make recompile) all pass. The current GitHub Advanced Security check passed; the Copilot check is still in progress.

Copilot AI requested a review from gh-aw-bot September 25, 2026 22:20
@pelikhan
pelikhan merged commit fd68fc2 into main Sep 26, 2026
44 checks passed
@pelikhan
pelikhan deleted the copilot/fix-safe-outputs-prompt branch September 26, 2026 05:23
@github-actions

Copy link
Copy Markdown
Contributor

🎉 This pull request is included in a new release.

Release: v0.89.22

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

safe-outputs prompt tells engine:pi the CLI is an optional alternative to a direct tool call that doesn't exist for it (MCP: false)

4 participants