You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
gh-aw v0.89.6 still emits an invalid MCP Gateway configuration for a static GitHub agent enclave when primary-agent GitHub tools are explicitly disabled with tools.github: false.
The recent fixes in #59816 and #59878 corrected the original static-enclave guard, startup, deferred-tool, and information-budget problems tracked in #59826. However, the newly generated static-enclave safeoutputs write-sink policy exposes a related dangling-reference bug: the compiler emits GH_AW_SINK_VISIBILITY from steps.determine-automatic-lockdown.outputs.visibility even though that step is deliberately not generated for this configuration.
At runtime the expression resolves to an empty string, producing:
"sink-visibility": ""
MCP Gateway v0.4.20 rejects the configuration before the agent or enclave starts.
There is no step with id: determine-automatic-lockdown in this workflow. The run confirms GH_AW_SINK_VISIBILITY is empty and fails in job agent, step Start MCP Gateway:
Location: /mcpServers/safeoutputs/guard-policies/write-sink/sink-visibility
Error: value must be one of 'public', 'private', 'internal'
Details: Invalid value - allowed values: public, private, internal
...
failed to load config: Configuration validation error (MCP Gateway version: v0.4.20)
...
Process completed with exit code 1
The agent command and enclave never start. The later detection, safe-output, and conclusion jobs succeed only as cleanup/fallback processing.
#48589 moved sink-visibility through GH_AW_SINK_VISIBILITY to avoid embedding a GitHub Actions expression inside the MCP config heredoc. This established the current relationship:
That was correct for workflows where the primary GitHub tool caused the lockdown step to be generated, but it left an assumption that every emitted sink-visibility value has that producer step.
#57973 added shared MCP Gateway support for enclave-only GitHub access, including a dedicated enclave identity and a synthesized GitHub backend when the primary agent has tools.github: false.
This introduced an important distinction:
GitHub is disabled for the primary agent.
A GitHub backend still needs to be rendered for the enclave identity.
Some later generation paths correctly preserve this distinction; the sink-visibility environment path currently does not.
#58880 added dynamic enclave repository policies and established dynamic-enclave guard/write-sink paths. Static enclaves initially remained uncovered in several related paths.
#59816 fixed the invalid empty GitHub server allow-only policy for enclave-only delegation. It added githubLockdownDetectionStepEnabled() as the authoritative predicate for whether determine-automatic-lockdown is generated and made static enclave-only GitHub server guards derive concrete values from the enclave declaration.
This correctly fixed the previous output:
{"allow-only":{"min-integrity":"","repos":""}}
The v0.89.6 output now correctly contains concrete values such as:
Added disclosure-budget prompt guidance and a runtime hint for bit-budget-exhausted.
These fixes are present in v0.89.6. In particular, the GitHub server guard in the failing workflow is valid. The new failure is specifically in the safeoutputs write-sink policy added for static enclaves.
Root cause
The relevant generation paths use different definitions of “GitHub enabled.”
githubLockdownDetectionStepEnabled(workflowData) correctly uses the original workflow state, including ExplicitlyDisabledTools, and returns false for tools.github: false. Therefore the lockdown step is not generated.
During MCP setup, toolsWithEnclaveGitHubIssues() synthesizes a non-false tools["github"] entry so that the enclave-scoped GitHub backend can still be rendered. This is necessary for enclave delegation, but that reconstructed tools map must not be treated as evidence that the primary-agent lockdown step exists.
collectMCPEnvironmentVariables() correctly gates GITHUB_MCP_GUARD_MIN_INTEGRITY and GITHUB_MCP_GUARD_REPOS on githubLockdownDetectionStepEnabled(workflowData). Its sink-visibility branch instead uses the reconstructed tools map:
For static enclave-only delegation, githubToolEnabledInTools is true because the backend was synthesized, while githubLockdownDetectionStepEnabled(workflowData) is false. This emits a reference to a nonexistent step.
writeSinkGuardPolicy() compounds the problem by unconditionally adding:
"sink-visibility": sinkVisibilityRuntimeExpr
for the new static-enclave write-sink policy.
Why existing tests missed it
TestCompileEnclaveOnlyGitHubToolsGuardPolicy covers this exact configuration and correctly asserts that:
Determine automatic lockdown mode is absent;
GITHUB_MCP_GUARD_MIN_INTEGRITY does not reference the missing step;
GITHUB_MCP_GUARD_REPOS does not reference the missing step;
${GH_AW_SINK_VISIBILITY} is present in the generated policy.
It does not make the equivalent assertion for the environment variable's producer:
The unit/golden tests therefore verify the consumer syntax but not that its referenced producer exists. Compile validation also passes because GitHub Actions accepts the missing-step expression and the invalid empty enum is only observed after runtime expansion by MCP Gateway.
Required fix
The compiler needs a single consistent rule for every value derived from determine-automatic-lockdown.
At minimum:
Gate GH_AW_SINK_VISIBILITY on the same authoritative githubLockdownDetectionStepEnabled(workflowData) predicate used for the other lockdown outputs.
Ensure static-enclave write-sink generation never emits sink-visibility unless it has a valid source.
Preserve the distinction between a GitHub backend rendered for an enclave and GitHub tools enabled for the primary agent.
Decide the intended secure behavior when a static enclave write-sink needs target-repository visibility but the normal lockdown step is absent:
generate a visibility-only/detection step for this case; or
derive a valid target visibility through another trusted runtime source; or
omit the field only if that preserves the intended DIFC policy and does not weaken private-to-public protections.
Simply substituting an empty value is invalid. Silently defaulting to a less restrictive visibility would be unsafe.
The disclosure-budget work in #59878 improved prompt guidance and runtime error reporting, but does not appear to implement the proposed compile-time rejection or warning for example response schemas whose cardinality cannot fit the enclave's sensitivity-derived bit budget. That preventive validation remains useful, but it is separate from this startup-blocking regression.
Acceptance criteria
The reproduction workflow compiles without a dangling step-output reference.
MCP Gateway v0.4.20 accepts the expanded configuration.
The primary agent has no GitHub access when tools.github: false.
The enclave identity retains its scoped GitHub repository access.
The safeoutputs write-sink policy remains narrow and enforces the correct target visibility.
The agent, enclave invocation, and safe-output path complete end to end in the demo repository.
A regression test fails on v0.89.6 behavior and passes with the fix.
Required test-first reproduction and validation
The fix must be developed with a red-green regression test, not only assertions added after implementation.
Before changing production code, add a test fixture using representative frontmatter equivalent to the real githubnext/gh-aw-enclave-demo-public workflow, including:
The test must first be run against the current compiler and demonstrated to fail for the observed gap: the generated workflow references steps.determine-automatic-lockdown.outputs.visibility even though no step with id: determine-automatic-lockdown exists, and the expanded MCP Gateway configuration contains an invalid empty sink-visibility. This failing result should be captured in the PR description or test commit history before the production fix is applied.
After implementing the fix, run the unchanged regression test and require it to prove all of the following:
the generated workflow contains no dangling steps.<id>.outputs.* references;
the actual MCP Gateway JSON produced after GitHub Actions and environment expansion validates against the pinned MCP Gateway schema/runtime;
sink-visibility is one of public, private, or internal when present;
the primary agent still has no GitHub access with tools.github: false;
the enclave identity retains only its declared repository access;
the static write-sink retains the narrow accepted secrecy labels;
the concrete GitHub server guard remains valid (min-integrity is non-empty and repos is an array);
A golden-string assertion alone is insufficient. The test must exercise or faithfully reproduce runtime variable expansion and pass the resulting configuration through MCP Gateway configuration validation. Finally, the corrected compiler/setup action must be tested end to end against the demo workflow so the fix is shown to close this regression without reopening any of the original gaps from #59826.
Summary
gh-aw v0.89.6still emits an invalid MCP Gateway configuration for a static GitHub agent enclave when primary-agent GitHub tools are explicitly disabled withtools.github: false.The recent fixes in #59816 and #59878 corrected the original static-enclave guard, startup, deferred-tool, and information-budget problems tracked in #59826. However, the newly generated static-enclave
safeoutputswrite-sink policy exposes a related dangling-reference bug: the compiler emitsGH_AW_SINK_VISIBILITYfromsteps.determine-automatic-lockdown.outputs.visibilityeven though that step is deliberately not generated for this configuration.At runtime the expression resolves to an empty string, producing:
MCP Gateway v0.4.20 rejects the configuration before the agent or enclave starts.
Confirmed reproduction
Repository: https://github.com/githubnext/gh-aw-enclave-demo-public
Source workflow: https://github.com/githubnext/gh-aw-enclave-demo-public/blob/060f47ae89fd93185090a34dfa2065d964fdebf4/.github/workflows/roadmap-triage-enclave.md
Generated workflow: https://github.com/githubnext/gh-aw-enclave-demo-public/blob/060f47ae89fd93185090a34dfa2065d964fdebf4/.github/workflows/roadmap-triage-enclave.lock.yml
Failed run: https://github.com/githubnext/gh-aw-enclave-demo-public/actions/runs/34650415396
The workflow was recompiled from current source with the verified
v0.89.6prerelease binary in githubnext/gh-aw-enclave-demo-public#43.Relevant source configuration:
The generated
Start MCP Gatewayenvironment contains:The generated safeoutputs policy contains:
There is no step with
id: determine-automatic-lockdownin this workflow. The run confirmsGH_AW_SINK_VISIBILITYis empty and fails in jobagent, stepStart MCP Gateway:The agent command and enclave never start. The later detection, safe-output, and conclusion jobs succeed only as cleanup/fallback processing.
Work completed so far
#48589 — sink visibility environment wiring
#48589 moved
sink-visibilitythroughGH_AW_SINK_VISIBILITYto avoid embedding a GitHub Actions expression inside the MCP config heredoc. This established the current relationship:That was correct for workflows where the primary GitHub tool caused the lockdown step to be generated, but it left an assumption that every emitted sink-visibility value has that producer step.
#57973 — enclave GitHub delegation
#57973 added shared MCP Gateway support for enclave-only GitHub access, including a dedicated enclave identity and a synthesized GitHub backend when the primary agent has
tools.github: false.This introduced an important distinction:
Some later generation paths correctly preserve this distinction; the sink-visibility environment path currently does not.
#58880 — dynamic enclave policies
#58880 added dynamic enclave repository policies and established dynamic-enclave guard/write-sink paths. Static enclaves initially remained uncovered in several related paths.
#59816 — static enclave GitHub server guard
#59816 fixed the invalid empty GitHub server
allow-onlypolicy for enclave-only delegation. It addedgithubLockdownDetectionStepEnabled()as the authoritative predicate for whetherdetermine-automatic-lockdownis generated and made static enclave-only GitHub server guards derive concrete values from the enclave declaration.This correctly fixed the previous output:
{"allow-only":{"min-integrity":"","repos":""}}The v0.89.6 output now correctly contains concrete values such as:
{"allow-only":{"min-integrity":"none","repos":["githubnext/gh-aw-enclave-demo-private"]}}However, the same predicate was not applied to
GH_AW_SINK_VISIBILITY.#59878 — static enclave startup fixes
#59878 addressed the remaining failures collected in #59826:
safeoutputswrite-sink policy.awf-enclaveCLI tool discovery after the backend registers.bit-budget-exhausted.These fixes are present in v0.89.6. In particular, the GitHub server guard in the failing workflow is valid. The new failure is specifically in the safeoutputs write-sink policy added for static enclaves.
Root cause
The relevant generation paths use different definitions of “GitHub enabled.”
githubLockdownDetectionStepEnabled(workflowData)correctly uses the original workflow state, includingExplicitlyDisabledTools, and returns false fortools.github: false. Therefore the lockdown step is not generated.During MCP setup,
toolsWithEnclaveGitHubIssues()synthesizes a non-falsetools["github"]entry so that the enclave-scoped GitHub backend can still be rendered. This is necessary for enclave delegation, but that reconstructed tools map must not be treated as evidence that the primary-agent lockdown step exists.collectMCPEnvironmentVariables()correctly gatesGITHUB_MCP_GUARD_MIN_INTEGRITYandGITHUB_MCP_GUARD_REPOSongithubLockdownDetectionStepEnabled(workflowData). Its sink-visibility branch instead uses the reconstructed tools map:For static enclave-only delegation,
githubToolEnabledInToolsis true because the backend was synthesized, whilegithubLockdownDetectionStepEnabled(workflowData)is false. This emits a reference to a nonexistent step.writeSinkGuardPolicy()compounds the problem by unconditionally adding:for the new static-enclave write-sink policy.
Why existing tests missed it
TestCompileEnclaveOnlyGitHubToolsGuardPolicycovers this exact configuration and correctly asserts that:Determine automatic lockdown modeis absent;GITHUB_MCP_GUARD_MIN_INTEGRITYdoes not reference the missing step;GITHUB_MCP_GUARD_REPOSdoes not reference the missing step;${GH_AW_SINK_VISIBILITY}is present in the generated policy.It does not make the equivalent assertion for the environment variable's producer:
The unit/golden tests therefore verify the consumer syntax but not that its referenced producer exists. Compile validation also passes because GitHub Actions accepts the missing-step expression and the invalid empty enum is only observed after runtime expansion by MCP Gateway.
Required fix
The compiler needs a single consistent rule for every value derived from
determine-automatic-lockdown.At minimum:
GH_AW_SINK_VISIBILITYon the same authoritativegithubLockdownDetectionStepEnabled(workflowData)predicate used for the other lockdown outputs.sink-visibilityunless it has a valid source.Simply substituting an empty value is invalid. Silently defaulting to a less restrictive visibility would be unsafe.
Regression coverage
Please add coverage that:
tools.github: false+ static GitHub enclave + safe outputs;min_integrity,repos, andvisibility) consistently;A general generated-workflow invariant would also help: every
steps.<id>.outputs.*reference should correspond to an emitted step ID in the same job.Additional remaining work from #59826
The disclosure-budget work in #59878 improved prompt guidance and runtime error reporting, but does not appear to implement the proposed compile-time rejection or warning for example response schemas whose cardinality cannot fit the enclave's sensitivity-derived bit budget. That preventive validation remains useful, but it is separate from this startup-blocking regression.
Acceptance criteria
tools.github: false.Required test-first reproduction and validation
The fix must be developed with a red-green regression test, not only assertions added after implementation.
Before changing production code, add a test fixture using representative frontmatter equivalent to the real
githubnext/gh-aw-enclave-demo-publicworkflow, including:The test must first be run against the current compiler and demonstrated to fail for the observed gap: the generated workflow references
steps.determine-automatic-lockdown.outputs.visibilityeven though no step withid: determine-automatic-lockdownexists, and the expanded MCP Gateway configuration contains an invalid emptysink-visibility. This failing result should be captured in the PR description or test commit history before the production fix is applied.After implementing the fix, run the unchanged regression test and require it to prove all of the following:
steps.<id>.outputs.*references;sink-visibilityis one ofpublic,private, orinternalwhen present;tools.github: false;min-integrityis non-empty andreposis an array);A golden-string assertion alone is insufficient. The test must exercise or faithfully reproduce runtime variable expansion and pass the resulting configuration through MCP Gateway configuration validation. Finally, the corrected compiler/setup action must be tested end to end against the demo workflow so the fix is shown to close this regression without reopening any of the original gaps from #59826.