<objective> Prove TOOL-05 end to end: outcome-equal results through MCP and ACP against one dispatcher, a static no-tool-logic guard over all three surfaces, and taxonomy coverage for the phase.
Purpose: Deliver P5-D12 and P5-D13, the phase's contract proof, depending only on the two Wave 2 surfaces. Output: Two surface test files plus the taxonomy extension. </objective>
<execution_context> @~/.claude/gsd-core/workflows/execute-plan.md @~/.claude/gsd-core/templates/summary.md </execution_context>
<context> @.planning/PROJECT.md @.planning/ROADMAP.md @.planning/phases/05-omp-plugin-mcp-and-acp-surfaces/05-CONTEXT.md </context>
<tasks>
<task type="tdd"> <name>Task 1: MCP-versus-ACP outcome equivalence on one dispatcher (P5-D13)</name> <files>test/surfaces/equivalence.test.ts</files> <read_first> - src/mcp/server.ts (from plan 05-03; the MCP half: envelope shape to strip at the test edge) - src/acp/agent.ts (from plan 05-04; the ACP half: realm/tool envelope shape to strip at the test edge) - src/tools/dispatch.ts lines 77-89 (ToolOutcome: the done-versus-refused shape compared outcome-equal) - test/support/fake-tools.ts lines 153-200 (makeHarness: the ONE dispatcher both surfaces drive) - test/tools/dispatch.test.ts (call-order style proving both surfaces reach the same pipeline) </read_first> <behavior> - Test (TOOL-05): the same ToolCall for each of the six tools through the MCP server and through the ACP adapter against one harness dispatcher returns outcome-equal ToolOutcome payloads after envelope stripping - Test (TOOL-05): one refusal case (a schema-invalid call and a required-mode non-active write) is outcome-equal across both surfaces with the same catalogue code - Test titles cite TOOL-05 </behavior> <action>Write the failing tests first per behavior, then implement test/surfaces/equivalence.test.ts per P5-D13: build one makeHarness dispatcher, wire both surfaces to it over in-memory streams, send the identical ToolCall through each surface, strip only the transport envelope (JSON-RPC ids on the MCP half, realm/tool wrappers on the ACP half) at the test edge without editing either surface, and deep-equal the ToolOutcome payloads. Cover all six tools plus the two refusal cases. A mismatch fails with both payloads shown. (Implements P5-D13; P5-D11 holds because neither surface is edited.)</action> <acceptance_criteria> - test/surfaces/equivalence.test.ts asserts outcome-equal payloads for all six tools plus both refusal cases across the two surfaces - Envelope stripping lives in the test only; src/mcp/ and src/acp/ are untouched </acceptance_criteria> <verify> <automated>npx tsc -p tsconfig.json && node --test dist/test/surfaces/equivalence.test.js</automated> </verify> <done>Equivalence green for six tools plus refusals on one dispatcher.</done> </task>
<task type="auto"> <name>Task 2: No-tool-logic static guard plus taxonomy coverage (P5-D12)</name> <files>test/surfaces/no-logic.test.ts, test/quality/suite-taxonomy.test.ts</files> <read_first> - test/tools/no-bypass.test.ts lines 17-60 (GUARDED_SYMBOLS, SHARED_RUNTIME_MODULES, FILE_FUNCTIONS, violation-collector style to copy) - test/tools/no-bypass.test.ts lines 100-140 (title-cite style for structural assertions) - src/mcp/server.ts and src/acp/agent.ts and src/omp/plugin.ts (the three surfaces the guard scans; read to list their legitimate imports) - test/quality/suite-taxonomy.test.ts lines 12-24 and 222-241 (CATEGORY_DIRS, title-count style, and the Phase 4 ID check to mirror) </read_first> <action>Implement test/surfaces/no-logic.test.ts per P5-D12 by copying the no-bypass.test.ts grep-source method: assert the D-99 symbols (buildExecArgv, lockedWrite, readFileCapped, listDir, spawnOpenShellRunner) are referenced only from their allowlisted modules, that no file under src/mcp/, src/acp/, or src/omp/ references any of them or any dispatcher-internal symbol the surfaces must not touch, that those surface files call the dispatcher only through ToolDispatcher.call, and that spawn( still appears in src/ only in src/runtime/session-execution.ts. Then extend test/quality/suite-taxonomy.test.ts in place: add the realm, mcp, acp, omp, surfaces, and docs category entries and a PHASE_5_REQUIREMENT_IDS check (TOOL-05, OMP-01 to OMP-05) mirroring the Phase 4 check, keeping earlier phase lists intact. Test titles cite TOOL-05. (Implements P5-D12 and the QUAL-02 traceability row.)</action> <acceptance_criteria> - The guard fails on a planted tool-internal reference in any surface and passes on the real tree - The taxonomy cites all six Phase 5 IDs from named test files with earlier phases intact </acceptance_criteria> <verify> <automated>npx tsc -p tsconfig.json && node --test dist/test/surfaces/no-logic.test.js dist/test/quality/suite-taxonomy.test.js</automated> </verify> <done>Guard and taxonomy green with surfaces proven logic-free.</done> </task>
</tasks>
<threat_model>
Trust Boundaries
| Boundary | Description |
|---|---|
| MCP envelope -> comparison edge | Transport framing crosses into the comparison; only the ToolOutcome payload is compared, never ids or wrappers |
| source tree -> static guard | Source text crosses into assertions; the guard reads files but executes nothing |
STRIDE Threat Register
| Threat ID | Category | Component | Severity | Disposition | Mitigation Plan |
|---|---|---|---|---|---|
| T-05-12 | T (Tampering) | src/mcp/ plus src/acp/ plus src/omp/ | high | mitigate | The static guard proves no surface references tool internals or spawns processes, so tool logic cannot hide outside the dispatcher pipeline (P5-D12); the guard self-tests with a planted violation |
| T-05-13 | R (Repudiation) | test/surfaces/equivalence.test.ts | low | accept | Equivalence compares outcome payloads only after envelope stripping at the test edge; rationale: envelope shape is covered by the 05-03 and 05-04 tests, and stripping code never ships |
| </threat_model> |
<verification> npx tsc -p tsconfig.json && node --test dist/test/surfaces/equivalence.test.js dist/test/surfaces/no-logic.test.js dist/test/quality/suite-taxonomy.test.js; grep the surface test files for TOOL-05 in test titles. </verification>
<success_criteria> Same-call outcome equivalence for six tools plus refusals, a self-testing no-logic guard over all three surfaces, and taxonomy coverage for all six Phase 5 IDs. </success_criteria>
Artifacts this phase produces (contribution of this plan)
test/surfaces/equivalence.test.ts(new): TOOL-05 outcome-equal comparison across MCP and ACP on one dispatcher (P5-D13).test/surfaces/no-logic.test.ts(new): P5-D12 static guard over src/mcp/, src/acp/, src/omp/ in the D-99 style.test/quality/suite-taxonomy.test.ts(extended): Phase 5 category entries and ID citation check.
<output> Create .planning/phases/05-omp-plugin-mcp-and-acp-surfaces/05-06-SUMMARY.md when done </output>