Summary
Since 92e400a (#67439, "Add declarative JSON schema validation for memory"), the safe outputs MCP server crashes on startup for every workflow whose setup action is pinned to a main commit at or after that change. The safe outputs bundle that actions/setup/setup.sh stages is missing memory_schema_contract.cjs. The server exits during module loading, registers no tools, and the agent cannot emit safe outputs.
This regression does not depend on the engine or SDK mode. It happens in Start MCP Gateway, before the agent starts.
Impact
- Affects every workflow compiled against
main since 92e400a that configures safe outputs, including current main (9e12379).
- The setup step still reports success ("Successfully copied 138 safe-outputs files"), so the missing file only shows up when the gateway starts the server.
- Downstream effects:
- The agent's
safeoutputs CLI commands exit with code 127.
- Ingest agent output fails with "safeoutputs MCP gateway registered 0 tools during setup".
- The conclusion job reports an agent failure.
Root cause
#67439 added actions/setup/js/memory_schema_contract.cjs and made two existing modules require it: memory_custom_validation.cjs and ledger_store.cjs. Both modules are listed in SAFE_OUTPUTS_FILES in actions/setup/setup.sh, but memory_schema_contract.cjs was not added to that list.
The safe outputs server runs from ${RUNNER_TEMP}/gh-aw/safeoutputs, which contains only the files in that list (plus the entry point and tool JSON). So the relative require cannot be resolved there.
Require stack from the run logs:
safe_outputs_mcp_server.cjs → safe_outputs_handlers.cjs → memory_custom_validation.cjs → ./memory_schema_contract.cjs (missing)
ledger_store.cjs, which is also in the bundle, has the same unresolved require.
Evidence
Workflow runs
All three runs are in githubnext/gh-aw-routing-sandbox. Their setup action is pinned to github/gh-aw/actions/setup@9e123795f5e522f975e08c7effb62fe8a709f4f1:
All three runs fail in the same sequence:
- Start MCP Gateway: the gateway (v0.4.30) health check reports
github as running and safeoutputs with status error.
- Gateway backend stderr and
mcp-logs/safeoutputs.log: the server logs Error: Cannot find module './memory_schema_contract.cjs' with the require stack shown above. The gateway then logs MCP backend connection failed: server=safeoutputs.
- Mount MCP servers as CLIs:
tools/list returned 0 tools for 'safeoutputs' after 5 retries, then fails fast.
- Agent: the
safeoutputs commands exit with code 127.
- Ingest agent output: fails with "safeoutputs MCP gateway registered 0 tools during setup".
Run 38077047915 also has an unrelated agent-step failure: the SDK driver reports "No GitHub OAuth token or Copilot HMAC key provided". That failure is out of scope for this issue. In all three runs, the safe outputs crash happens before the agent starts.
CI
The existing guard in actions/setup/js/setup_sh_file_lists.test.cjs already detects this gap. The relevant test is "setup.sh SAFE_OUTPUTS_FILES › contains all transitive local dependencies".
- It fails on
main with expected [ 'memory_schema_contract.cjs' ] to deeply equal [] in these CJS runs:
- Run 38072203232 on the merge commit 92e400a
- Run 38074915262 on 1118de3
- On the PR head (74019c6), the CJS workflow run (38069547592) concluded
failure without running any jobs, so this guard never ran before merge.
Local reproduction
Using a tree at 9e12379:
- Staging only what
setup.sh places in the safe outputs directory (the SAFE_OUTPUTS_FILES list, the entry point, safe_outputs_tools.json/tools.json, and an empty config.json), then loading safe_outputs_handlers.cjs and ledger_store.cjs, fails with MODULE_NOT_FOUND for ./memory_schema_contract.cjs.
- Adding only
memory_schema_contract.cjs to the staged directory lets both modules load.
Other copy steps checked
I checked every file #67439 touched under actions/setup/js against every place that stages or loads those files:
MCP_SCRIPTS_FILES: all transitive local requires are covered, so there is no gap.
- Main actions directory (
${RUNNER_TEMP}/gh-aw/actions): setup.sh copies every non-test .cjs and .json file there, so memory_schema_contract.cjs is present. These consumers load from that directory and are unaffected:
push_repo_memory.cjs
validate_memory_step.cjs
- the drive-memory steps emitted by
pkg/workflow/drive_memory.go
- the ledger MCP server, which is launched from the actions directory
memory_schema_contract.cjs dependencies: the file has no local requires, so no other transitive files are needed.
memory_schema_contract.fixtures.json: only tests use it, so it does not need to be copied into the safe outputs directory.
SAFE_OUTPUTS_FILES is the only list that needs a change.
Proposed fix
Add memory_schema_contract.cjs to SAFE_OUTPUTS_FILES in actions/setup/setup.sh, next to memory_custom_validation.cjs.
No compiler changes are needed. Affected workflows pick up the fix after they are recompiled against a gh-aw ref that includes it.
Regression test
A static check for this class of bug already exists (the test named above). It caught this regression, but it never ran before merge. To prevent a repeat:
-
Add a runtime bundle-load test next to the existing static check.
- Stage the safe outputs directory the way
setup.sh does. Preferably, run setup.sh itself against a temporary RUNNER_TEMP, so the test exercises the real copy logic instead of a parsed copy of the array.
- In a fresh Node process, load every bundled
.cjs module from that isolated directory without starting the server.
- Fail on any
MODULE_NOT_FOUND.
On current main, this test fails with exactly the error above and passes once the file is added. Unlike the regex-based static check, it also catches local requires that are not written as a single string literal.
-
Run the check as a blocking job for every PR that touches actions/setup/js/** or actions/setup/setup.sh. Here the CJS workflow failed on the PR before running any jobs, and the PR merged anyway.
Acceptance criteria
- "contains all transitive local dependencies" in
setup_sh_file_lists.test.cjs passes on main.
- The new runtime bundle-load test fails without the fix and passes with it.
- A workflow compiled against the fixed ref shows
safeoutputs as running in the gateway health check and registers its tools.
Summary
Since 92e400a (#67439, "Add declarative JSON schema validation for memory"), the safe outputs MCP server crashes on startup for every workflow whose setup action is pinned to a
maincommit at or after that change. The safe outputs bundle thatactions/setup/setup.shstages is missingmemory_schema_contract.cjs. The server exits during module loading, registers no tools, and the agent cannot emit safe outputs.This regression does not depend on the engine or SDK mode. It happens in Start MCP Gateway, before the agent starts.
Impact
mainsince 92e400a that configures safe outputs, including currentmain(9e12379).safeoutputsCLI commands exit with code 127.Root cause
#67439 added
actions/setup/js/memory_schema_contract.cjsand made two existing modules require it:memory_custom_validation.cjsandledger_store.cjs. Both modules are listed inSAFE_OUTPUTS_FILESinactions/setup/setup.sh, butmemory_schema_contract.cjswas not added to that list.The safe outputs server runs from
${RUNNER_TEMP}/gh-aw/safeoutputs, which contains only the files in that list (plus the entry point and tool JSON). So the relative require cannot be resolved there.Require stack from the run logs:
safe_outputs_mcp_server.cjs→safe_outputs_handlers.cjs→memory_custom_validation.cjs→./memory_schema_contract.cjs(missing)ledger_store.cjs, which is also in the bundle, has the same unresolved require.Evidence
Workflow runs
All three runs are in githubnext/gh-aw-routing-sandbox. Their setup action is pinned to
github/gh-aw/actions/setup@9e123795f5e522f975e08c7effb62fe8a709f4f1:All three runs fail in the same sequence:
githubas running andsafeoutputswith statuserror.mcp-logs/safeoutputs.log: the server logsError: Cannot find module './memory_schema_contract.cjs'with the require stack shown above. The gateway then logsMCP backend connection failed: server=safeoutputs.tools/list returned 0 tools for 'safeoutputs'after 5 retries, then fails fast.safeoutputscommands exit with code 127.Run 38077047915 also has an unrelated agent-step failure: the SDK driver reports "No GitHub OAuth token or Copilot HMAC key provided". That failure is out of scope for this issue. In all three runs, the safe outputs crash happens before the agent starts.
CI
The existing guard in
actions/setup/js/setup_sh_file_lists.test.cjsalready detects this gap. The relevant test is "setup.sh SAFE_OUTPUTS_FILES › contains all transitive local dependencies".mainwithexpected [ 'memory_schema_contract.cjs' ] to deeply equal []in these CJS runs:failurewithout running any jobs, so this guard never ran before merge.Local reproduction
Using a tree at 9e12379:
setup.shplaces in the safe outputs directory (theSAFE_OUTPUTS_FILESlist, the entry point,safe_outputs_tools.json/tools.json, and an emptyconfig.json), then loadingsafe_outputs_handlers.cjsandledger_store.cjs, fails withMODULE_NOT_FOUNDfor./memory_schema_contract.cjs.memory_schema_contract.cjsto the staged directory lets both modules load.Other copy steps checked
I checked every file #67439 touched under
actions/setup/jsagainst every place that stages or loads those files:MCP_SCRIPTS_FILES: all transitive local requires are covered, so there is no gap.${RUNNER_TEMP}/gh-aw/actions):setup.shcopies every non-test.cjsand.jsonfile there, somemory_schema_contract.cjsis present. These consumers load from that directory and are unaffected:push_repo_memory.cjsvalidate_memory_step.cjspkg/workflow/drive_memory.gomemory_schema_contract.cjsdependencies: the file has no local requires, so no other transitive files are needed.memory_schema_contract.fixtures.json: only tests use it, so it does not need to be copied into the safe outputs directory.SAFE_OUTPUTS_FILESis the only list that needs a change.Proposed fix
Add
memory_schema_contract.cjstoSAFE_OUTPUTS_FILESinactions/setup/setup.sh, next tomemory_custom_validation.cjs.No compiler changes are needed. Affected workflows pick up the fix after they are recompiled against a gh-aw ref that includes it.
Regression test
A static check for this class of bug already exists (the test named above). It caught this regression, but it never ran before merge. To prevent a repeat:
Add a runtime bundle-load test next to the existing static check.
setup.shdoes. Preferably, runsetup.shitself against a temporaryRUNNER_TEMP, so the test exercises the real copy logic instead of a parsed copy of the array..cjsmodule from that isolated directory without starting the server.MODULE_NOT_FOUND.On current
main, this test fails with exactly the error above and passes once the file is added. Unlike the regex-based static check, it also catches local requires that are not written as a single string literal.Run the check as a blocking job for every PR that touches
actions/setup/js/**oractions/setup/setup.sh. Here the CJS workflow failed on the PR before running any jobs, and the PR merged anyway.Acceptance criteria
setup_sh_file_lists.test.cjspasses onmain.safeoutputsas running in the gateway health check and registers its tools.