diff --git a/src/core-mutation-tools.ts b/src/core-mutation-tools.ts index 4ed4dee96..439d26d03 100644 --- a/src/core-mutation-tools.ts +++ b/src/core-mutation-tools.ts @@ -524,14 +524,14 @@ export function registerCoreMutationSessionTools( }, ); - if (options.recoveryOwnerClientId && inspectWriterDomain) { + if (inspectWriterDomain) { registerAppTool( server, "core_mutation_session_recover_orphaned_process", { title: "Recover orphaned Core PROCESS writer", description: - "Owner-only administrative recovery for one exact ACTIVE Core session whose original caller identity is unavailable and whose PROCESS writer is OUTCOME_UNKNOWN. Revalidates exact physical Git evidence, refuses live writers, clears only the PROCESS writer pin, preserves the original actor/session/worktree, and grants no retry, Candidate, mutation, completion, integration, merge, or release authority.", + "Owner-only administrative recovery for one exact ACTIVE Core session whose original caller identity is unavailable and whose PROCESS writer is OUTCOME_UNKNOWN. The tool remains projected even when owner recovery is disabled so the MCP catalog/schema stays stable; invocation fails closed unless the exact owner client is configured and authenticated. Revalidates exact physical Git evidence, refuses live writers, clears only the PROCESS writer pin, preserves the original actor/session/worktree, and grants no retry, Candidate, mutation, completion, integration, merge, or release authority.", inputSchema: { workspaceId: z.string(), sessionId: z.string(), diff --git a/src/server.test.ts b/src/server.test.ts index 275702c4d..95f367328 100644 --- a/src/server.test.ts +++ b/src/server.test.ts @@ -1974,15 +1974,23 @@ test("direct_candidate_execution_evidence tool produces valid evidence through h assert.doesNotThrow(() => validateDirectCandidateExecutionEvidence(content)); }); -test("Core orphan PROCESS recovery tool is opt-in and exact-owner-client fenced", async (t) => { +test("Core orphan PROCESS recovery tool keeps a stable catalog and exact-owner-client fence", async (t) => { const disabled = await fixture(t, { coreMutation: true }); const disabledTools = await disabled.client.listTools(); - assert.equal(disabledTools.tools.some((tool) => tool.name === "core_mutation_session_recover_orphaned_process"), false); + assert.equal(disabledTools.tools.some((tool) => tool.name === "core_mutation_session_recover_orphaned_process"), true); + assert.throws( + () => assertCoreMutationRecoveryOwnerClient({}, undefined), + /CORE_MUTATION_RECOVERY_DISABLED/, + ); const ownerClientId = "devspace-core-recovery-owner"; const enabled = await fixture(t, { coreMutation: true, coreMutationRecoveryOwnerClientId: ownerClientId }); const enabledTools = await enabled.client.listTools(); assert.equal(enabledTools.tools.some((tool) => tool.name === "core_mutation_session_recover_orphaned_process"), true); + assert.deepEqual( + disabledTools.tools.map((tool) => tool.name).sort(), + enabledTools.tools.map((tool) => tool.name).sort(), + ); assert.throws( () => assertCoreMutationRecoveryOwnerClient({}, ownerClientId),