History

fix(mcp): clarify launch admission failures

Julius Marminge committed 2 months ago

Commit 561d3d51 parent f9307fe3 files · +58 −9Browse files

Unified · Split

3 files
  1. apps/server/src/git/GitWorkflowService.test.ts +39 −1
  2. apps/server/src/mcp/OrchestratorMcpService.ts +2 −2
  3. apps/server/src/mcp/OrchestratorMcpToolkit.integration.test.ts +17 −6
apps/server/src/git/GitWorkflowService.test.ts+39−1
@@ -1,14 +1,52 @@
11 import { assert, describe, expect, it, vi } from "@effect/vitest";
2+import * as DateTime from "effect/DateTime";
23 import * as Effect from "effect/Effect";
34 import * as Layer from "effect/Layer";
5+import * as Option from "effect/Option";
46
57 import { GitCommandError, VcsRepositoryDetectionError } from "@t3tools/contracts";
68
79 import * as GitManager from "./GitManager.ts";
810 import * as GitWorkflowService from "./GitWorkflowService.ts";
911 import * as GitVcsDriver from "../vcs/GitVcsDriver.ts";
12+import * as VcsDriver from "../vcs/VcsDriver.ts";
1013 import * as VcsDriverRegistry from "../vcs/VcsDriverRegistry.ts";
1114
15+const unusedVcsDriver = VcsDriver.VcsDriver.of({
16+ capabilities: {
17+ kind: "git",
18+ supportsWorktrees: true,
19+ supportsBookmarks: false,
20+ supportsAtomicSnapshot: false,
21+ supportsPushDefaultRemote: true,
22+ ignoreClassifier: "native",
23+ },
24+ execute: () => Effect.die("unused VCS driver execute"),
25+ detectRepository: () => Effect.die("unused VCS repository detection"),
26+ isInsideWorkTree: () => Effect.die("unused VCS worktree lookup"),
27+ listWorkspaceFiles: () => Effect.die("unused VCS workspace listing"),
28+ listRemotes: () => Effect.die("unused VCS remote listing"),
29+ filterIgnoredPaths: () => Effect.die("unused VCS ignore classification"),
30+ initRepository: () => Effect.die("unused VCS repository initialization"),
31+});
32+
33+function gitHandle(cwd: string): VcsDriverRegistry.VcsDriverHandle {
34+ return {
35+ kind: "git",
36+ repository: {
37+ kind: "git",
38+ rootPath: cwd,
39+ metadataPath: `${cwd}/.git`,
40+ freshness: {
41+ source: "live-local",
42+ observedAt: DateTime.makeUnsafe("2026-08-30T00:00:00.000Z"),
43+ expiresAt: Option.none(),
44+ },
45+ },
46+ driver: unusedVcsDriver,
47+ };
48+}
49+
1250 function makeLayer(input: {
1351 readonly detect: VcsDriverRegistry.VcsDriverRegistry["Service"]["detect"];
1452 readonly resolve?: VcsDriverRegistry.VcsDriverRegistry["Service"]["resolve"];
@@ -221,7 +259,7 @@ describe("GitWorkflowService", () => { · 206 unmodified lines
221259 Effect.provide(
222260 makeLayer({
223261 detect: () => Effect.succeed(null),
224− resolve: () => Effect.succeed({ kind: "git" } as VcsDriverRegistry.VcsDriverHandle),
262+ resolve: ({ cwd }) => Effect.succeed(gitHandle(cwd)),
225263 execute: (input) => Effect.fail(input.cwd === "/detached" ? detached : corrupt),
226264 }),
227265 ),
apps/server/src/mcp/OrchestratorMcpService.ts+2−2
@@ -202,13 +202,13 @@ function threadLaunchPolicyFailure( · 201 unmodified lines
202202 if (isRuntimeModeCeilingError(cause)) {
203203 return failure(
204204 "runtime_mode_escalation_denied",
205− `Child runtime mode ${cause.targetMode} is broader than the caller ceiling ${cause.capturedCallerMode}.`,
205+ `Child runtime mode ${cause.targetMode} exceeds the caller ceiling (captured ${cause.capturedCallerMode}; current ${cause.currentCallerMode}).`,
206206 );
207207 }
208208 if (isInteractionModeCeilingError(cause)) {
209209 return failure(
210210 "interaction_mode_escalation_denied",
211− `Child interaction mode ${cause.targetMode} is broader than the caller ceiling ${cause.capturedCallerMode}.`,
211+ `Child interaction mode ${cause.targetMode} exceeds the caller ceiling (captured ${cause.capturedCallerMode}; current ${cause.currentCallerMode}).`,
212212 );
213213 }
214214 return null;
apps/server/src/mcp/OrchestratorMcpToolkit.integration.test.ts+17−6
@@ -636,12 +636,19 @@ describe("orchestrator MCP toolkit", () => { · 635 unmodified lines
636636 ...actual,
637637 withProjectCreationAdmission: (input, effect) =>
638638 actual.withProjectCreationAdmission(input, (receipt) =>
639− input.commandId === deletionRaceCommandId && Option.isNone(receipt)
640− ? Deferred.succeed(creationAdmissionEntered, undefined).pipe(
641− Effect.andThen(Deferred.await(allowCreationAdmission)),
642− Effect.andThen(effect(receipt)),
643− )
644− : effect(receipt),
639+ Effect.gen(function* () {
640+ if (input.commandId === deletionRaceCommandId && Option.isNone(receipt)) {
641+ yield* Deferred.succeed(creationAdmissionEntered, undefined);
642+ yield* Deferred.await(allowCreationAdmission);
643+ }
644+ const gate = launchReceiptReadGates.get(input.commandId);
645+ if (gate !== undefined) {
646+ yield* Deferred.succeed(gate.entered, undefined);
647+ yield* Deferred.await(gate.release);
648+ launchReceiptReadGates.delete(input.commandId);
649+ }
650+ return yield* effect(receipt);
651+ }),
645652 ),
646653 });
647654 }),
@@ -2287,6 +2294,8 @@ describe("orchestrator MCP toolkit", () => { · 1639 unmodified lines
22872294 const preCreateCall = yield* Fiber.join(preCreateFiber);
22882295 expect(preCreateCall.structuredContent).toMatchObject({
22892296 code: "interaction_mode_escalation_denied",
2297+ message:
2298+ "Child interaction mode default exceeds the caller ceiling (captured default; current plan).",
22902299 });
22912300 const preCreateThreadId = ThreadId.make(
22922301 `thread:mcp:mcp-provider-session-parent:${preCreateKey}:0`,
@@ -2339,6 +2348,8 @@ describe("orchestrator MCP toolkit", () => { · 46 unmodified lines
23392348 const betweenCreateAndMessageCall = yield* Fiber.join(betweenCreateAndMessageFiber);
23402349 expect(betweenCreateAndMessageCall.structuredContent).toMatchObject({
23412350 code: "runtime_mode_escalation_denied",
2351+ message:
2352+ "Child runtime mode full-access exceeds the caller ceiling (captured full-access; current approval-required).",
23422353 });
23432354 const partialProjection = yield* orchestrator.getThreadProjection(
23442355 betweenCreateAndMessageThreadId,

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t