Skip to content

Commit dde50d1

Browse files
committed
feat(contract): migrate every remote MCP tool contract to @loopover/contract
Completes #9518. All seven remote-server categories -- admin, maintainer, review, branch, discovery, utility and agent -- now take their input and output schemas from the contract package instead of ~120 declarations local to src/mcp/server.ts. The remote server is the second of the three to be migrated to completion, after the AMS miner (#9542). A handful of inputs deliberately stay server-side, each for a stated reason: schemas carrying a .transform() or .default() (a caller must not be able to assert its own branch eligibility into its own score), one input shared verbatim with a REST route, and three bounded by constants a zod-only leaf package cannot import. Their outputs migrate either way. Two fixes fall out of the move: - The changed-file and validation-entry schemas behind loopover_run_local_scorer and loopover_preflight_local_diff, and both nested schemas behind loopover_suggest_boundary_tests, lost their .strict() in an earlier batch of this migration. That strictness is the no-upload boundary: without it a caller that smuggles a patch/content field gets a silently-stripped call instead of a rejected one, and believes the upload succeeded. Restored, with regression tests on both surfaces. - PLAN_STEP_STATUSES said 'in_progress' where both real surfaces -- the remote plan DAG and the miner's plan store -- say 'running'. Nothing consumed it yet, so nothing broke, but the first consumer would have rejected every running step the store has ever persisted. Corrected, aliased from the miner contract so there is one vocabulary, and pinned by a test. The REST<->MCP parity guards now read the contract rather than server-local declarations that no longer exist. Tool descriptions are relocated verbatim.
1 parent 4b266b2 commit dde50d1

19 files changed

Lines changed: 1074 additions & 526 deletions

packages/loopover-contract/src/enums.ts

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,18 @@ export type ProposeActionClass = (typeof PROPOSE_ACTION_CLASSES)[number];
5050
export const TEST_FRAMEWORKS = ["vitest", "jest", "pytest", "go-test", "rspec", "cargo-test"] as const;
5151
export type TestFramework = (typeof TEST_FRAMEWORKS)[number];
5252

53-
/** Lifecycle states of a plan step in the stateless plan-DAG tools. */
54-
export const PLAN_STEP_STATUSES = ["pending", "in_progress", "completed", "skipped", "failed"] as const;
53+
/**
54+
* Lifecycle states of a plan step.
55+
*
56+
* One vocabulary, deliberately: the remote server's stateless plan-DAG tools (loopover_build_plan /
57+
* plan_status / record_step_result) and the miner's own plan store hold the same steps at different
58+
* points in their life, and a step written by one and read by the other must round-trip.
59+
*
60+
* This list said `in_progress` where both real surfaces say `running` until #9518. Nothing consumed
61+
* it yet, so nothing broke -- but the first consumer would have rejected every running step the
62+
* plan store has ever persisted.
63+
*/
64+
export const PLAN_STEP_STATUSES = ["pending", "running", "completed", "failed", "skipped"] as const;
5565
export type PlanStepStatus = (typeof PLAN_STEP_STATUSES)[number];
5666

5767
/** Verdicts the pre-start feasibility surfaces return. */

packages/loopover-contract/src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ export {
2323
} from "./tool-definition.js";
2424

2525
export * from "./enums.js";
26-
export { PREFLIGHT_LIMITS, PREDICT_GATE_MAX_CHANGED_PATHS, PREDICT_GATE_MAX_CHANGED_PATH_CHARS } from "./limits.js";
26+
export { PREFLIGHT_LIMITS, PREDICT_GATE_MAX_CHANGED_PATHS, PREDICT_GATE_MAX_CHANGED_PATH_CHARS, WRITE_TOOL_LIMITS, SCENARIO_LIMITS } from "./limits.js";
2727
export { ownerRepoInput, ownerRepoPullInput, freshnessFields, toolErrorFields } from "./shared.js";
2828
export { TOOL_CONTRACTS, listToolDefinitions, getToolContract } from "./tools/index.js";
2929
export {

packages/loopover-contract/src/limits.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,3 +40,30 @@ export const PREDICT_GATE_MAX_CHANGED_PATHS = 500;
4040
* servers accepts today, which is the one thing an input schema may never do.
4141
*/
4242
export const PREDICT_GATE_MAX_CHANGED_PATH_CHARS = 400;
43+
44+
/**
45+
* Bounds on the local-execution write-spec tools' free text (#780).
46+
*
47+
* These tools never perform a write -- they return a spec the caller runs with its own credentials
48+
* -- so the bounds are about keeping a spec small enough to be reviewable before it is executed,
49+
* not about protecting a database column. `bodyChars` matches GitHub's own 65536-character comment
50+
* ceiling with headroom for the wrapper the spec builder adds.
51+
*/
52+
export const WRITE_TOOL_LIMITS = {
53+
titleChars: 400,
54+
bodyChars: 60_000,
55+
branchChars: 255,
56+
targetFiles: 50,
57+
} as const;
58+
59+
/**
60+
* Repo and branch identifier bounds.
61+
*
62+
* Restated from src/scenarios/input-model.ts for the same reason PREFLIGHT_LIMITS is restated from
63+
* the engine -- this package is a zod-only leaf and cannot import the Worker's `src/` -- and pinned
64+
* against it by a meta-test so the two cannot drift.
65+
*/
66+
export const SCENARIO_LIMITS = {
67+
repoFullNameChars: 200,
68+
branchRefChars: 200,
69+
} as const;

0 commit comments

Comments
 (0)