Root cause
Every sdd-* agent that imports shared/sdd-mcp-distillery.md compiles the Distillery MCP server into its lock with:
url: "${{ vars.DISTILLERY_MCP_URL }}"
When a consumer has not set DISTILLERY_MCP_URL, the expression resolves to the empty string at runtime. The MCP gateway validates its config before starting and rejects an empty url:
/mcpServers/distillery/url Error: '' is not valid 'uri'
Error: does not match pattern '^https?://.+'
The gateway exits 1, so the agent job fails before the agent runs. A consumer that does not run Distillery cannot run any sdd-* agent — the whole pipeline is dead at sdd-spec.
This contradicts the fragment's own outage contract, which already says:
if the store is unreachable, skip the pass and note the omission per the outage rule
The agent is built to tolerate an unreachable store, but it never gets the chance because the gateway dies on an unconfigured (not merely unreachable) store.
Reproduction
Fresh install pinned to main, no DISTILLERY_MCP_URL set, open a feature issue. sdd-spec → route/activation pass, agent job fails at "start MCP gateway" with the schema error above. Surfaced by the issue #89 E2E harness.
Fix
Fall the URL back to a non-routable placeholder when the variable is unset, so an unconfigured store degrades into the existing unreachable-store path instead of a hard gateway failure:
url: "${{ vars.DISTILLERY_MCP_URL || 'https://distillery.disabled.invalid/' }}"
.invalid is reserved (RFC 6761) and never resolves.
- It satisfies the gateway's
^https?://.+ format check, so the gateway starts.
- The first
distillery_search then fails unreachable, and the agent applies the documented outage rule and continues.
Applies to shared/sdd-mcp-distillery.md (covers sdd-spec, sdd-derive, sdd-triage-arch) and the equivalent line in .github/workflows/distillery-sync.md. Locks recompile from the fragment edit.
A configured consumer is unaffected: a non-empty DISTILLERY_MCP_URL always wins the ||.
Acceptance
- A
main-pinned install with no Distillery config runs sdd-spec past gateway startup; the agent completes and notes the Distillery pass was skipped.
- A configured consumer's runs are byte-identical in behavior (the fallback is never taken).
Root cause
Every
sdd-*agent that importsshared/sdd-mcp-distillery.mdcompiles the Distillery MCP server into its lock with:When a consumer has not set
DISTILLERY_MCP_URL, the expression resolves to the empty string at runtime. The MCP gateway validates its config before starting and rejects an emptyurl:The gateway exits 1, so the
agentjob fails before the agent runs. A consumer that does not run Distillery cannot run anysdd-*agent — the whole pipeline is dead atsdd-spec.This contradicts the fragment's own outage contract, which already says:
The agent is built to tolerate an unreachable store, but it never gets the chance because the gateway dies on an unconfigured (not merely unreachable) store.
Reproduction
Fresh install pinned to
main, noDISTILLERY_MCP_URLset, open a feature issue.sdd-spec→route/activationpass,agentjob fails at "start MCP gateway" with the schema error above. Surfaced by the issue #89 E2E harness.Fix
Fall the URL back to a non-routable placeholder when the variable is unset, so an unconfigured store degrades into the existing unreachable-store path instead of a hard gateway failure:
.invalidis reserved (RFC 6761) and never resolves.^https?://.+format check, so the gateway starts.distillery_searchthen fails unreachable, and the agent applies the documented outage rule and continues.Applies to
shared/sdd-mcp-distillery.md(coverssdd-spec,sdd-derive,sdd-triage-arch) and the equivalent line in.github/workflows/distillery-sync.md. Locks recompile from the fragment edit.A configured consumer is unaffected: a non-empty
DISTILLERY_MCP_URLalways wins the||.Acceptance
main-pinned install with no Distillery config runssdd-specpast gateway startup; the agent completes and notes the Distillery pass was skipped.