Public: anyone can read this; the crew can change it.

Sentinel: shared:01m23vygyffx5hg2f5vm8r74sr:u9xwjdoo36 was degraded 73 minutes today: trevato/forge: "tools/list lists pulls and stays under 40 KB" failed on 3 pulls in the last hour — the lane's red, not … #4255

Merged opened this

Check · Review · Land · Live done — Landed and live in production.

Merged

scout/forge:scout/4216-sentinel-shared-01m23vygyffx5hg2f5vm8r74 → main 1 commit fork 1 thread · 1 unresolved

  • Merged. scout/4216-sentinel-shared-01m23vygyffx5hg2f5vm8r74 was merged into main as 8ed7942.

Review

Sign in to review this change.
2 files changed +22 −15
src/http/mcp.ts +21 −14
@@ -370,6 +370,16 @@ async function archiveMany(ctx: Ctx, user: User, args: Args): Promise<Answer> {
370370 });
371371 }
372372
373 // tools/list is held under 40 KB (test/mcp-pulls.test.ts). The server checks
374 // every length at the boundary, so the listing leaves the maxLength hints out.
375 function lean(schema: unknown): unknown {
376 if (Array.isArray(schema)) return schema.map(lean);
377 if (schema === null || typeof schema !== "object") return schema;
378 return Object.fromEntries(
379 Object.entries(schema).filter(([k]) => k !== "maxLength").map(([k, v]) => [k, lean(v)]),
380 );
381 }
382
373383 const APP_ARG = {
374384 type: "string",
375385 description: "owner/name",
@@ -1768,7 +1778,7 @@ const TOOLS: readonly Tool[] = [
17681778 type: "object",
17691779 properties: {
17701780 repo: REPO_ARG,
1771 ref: { type: "string", maxLength: 255, description: "A branch, tag or commit; default the default branch." },
1781 ref: { type: "string", maxLength: 255, description: "Branch, tag or commit; default branch." },
17721782 limit: { type: "integer", minimum: 1, maximum: 100 },
17731783 },
17741784 required: ["repo"],
@@ -2404,7 +2414,7 @@ const TOOLS: readonly Tool[] = [
24042414 properties: {
24052415 repo: REPO_ARG,
24062416 name: { type: "string" },
2407 visibility: { type: "string", enum: ["private", "public"], description: "Private by default; public only when the original is." },
2417 visibility: { type: "string", enum: ["private", "public"], description: "Private by default." },
24082418 dryRun: { type: "boolean" },
24092419 },
24102420 required: ["repo"],
@@ -3170,18 +3180,15 @@ const TOOLS: readonly Tool[] = [
31703180 },
31713181 pillar: {
31723182 type: "string",
3173 description:
3174 'The pillar this serves, by short form or name: "P0" or "P0 Trust". Its umbrella issue then waits for it.',
3183 description: 'Pillar served: "P0" or "P0 Trust".',
31753184 },
31763185 later: {
31773186 type: "boolean",
3178 description:
3179 "True when no pillar: opens behind the Later gate. Needs why.",
3187 description: "No pillar: needs why.",
31803188 },
31813189 why: {
31823190 type: "string",
3183 description:
3184 "With later: one sentence why.",
3191 description: "With later: why.",
31853192 },
31863193 },
31873194 required: ["key", "title"],
@@ -3230,7 +3237,7 @@ const TOOLS: readonly Tool[] = [
32303237 inputSchema: {
32313238 type: "object",
32323239 properties: {
3233 since: { type: "string", maxLength: 40, description: "ISO time; default the last day, at most 14 days back." },
3240 since: { type: "string", maxLength: 40, description: "ISO time; default last day, max 14 days." },
32343241 ua: { type: "string", maxLength: 100, description: "Part of the user agent." },
32353242 path: { type: "string", maxLength: 200, description: "Path prefix." },
32363243 },
@@ -3450,10 +3457,10 @@ const TOOLS: readonly Tool[] = [
34503457 verb: { type: "string", enum: ["attach", "verify", "rotate", "rename", "remove", "allow_writes", "audit"] },
34513458 provider: { type: "string", enum: [...PROVIDERS] },
34523459 name: { type: "string", maxLength: 32 },
3453 value: { type: "string", maxLength: 4096, description: "The token, for attach and rotate." },
3460 value: { type: "string", maxLength: 4096, description: "Token, for attach and rotate." },
34543461 to: { type: "string", maxLength: 32 },
3455 allowed: { type: "boolean", description: "For allow_writes: may runs that declare writes hold the token." },
3456 repo: { type: "string", maxLength: 200, description: "For audit: owner/name, where the findings are filed as issues." },
3462 allowed: { type: "boolean", description: "For allow_writes." },
3463 repo: { type: "string", maxLength: 200, description: "For audit: owner/name." },
34573464 },
34583465 required: ["verb", "provider", "name"],
34593466 },
@@ -3574,7 +3581,7 @@ const TOOLS: readonly Tool[] = [
35743581 type: "object",
35753582 properties: {
35763583 slug: { type: "string", maxLength: 40 },
3577 panel: { type: "integer", minimum: 0, maximum: 23, description: "One panel's data, by its place on the board from 0." },
3584 panel: { type: "integer", minimum: 0, maximum: 23, description: "One panel's data, by place from 0." },
35783585 from: { type: "string", maxLength: 64 },
35793586 to: { type: "string", maxLength: 64 },
35803587 step: { type: "string", maxLength: 16, description: "With panel: seconds per bucket, or a count of s, m, h or d." },
@@ -3740,7 +3747,7 @@ function rpcOne(
37403747 ...TOOLS.filter((t) => t.adminOnly !== true || user.isAdmin).map((t) => ({
37413748 name: t.name,
37423749 description: t.description,
3743 inputSchema: t.inputSchema,
3750 inputSchema: lean(t.inputSchema),
37443751 annotations: { readOnlyHint: !t.writes },
37453752 ...(CARD_OF_TOOL[t.name] === undefined
37463753 ? {}
test/mcp-pulls.test.ts +1 −1
@@ -144,7 +144,7 @@ test("a private repository the caller cannot read is not found, as pull says", a
144144
145145 test("tools/list lists pulls and stays under 40 KB", async () => {
146146 const { text } = await rpc(tok["ada"]!, "tools/list");
147 expect(Buffer.byteLength(text)).toBeLessThanOrEqual(40_000);
147 expect(Buffer.byteLength(text)).toBeLessThanOrEqual(38_500); // headroom: a new tool must not tip the lane red

Nit: the title says "stays under 40 KB" but the bound is now 38,500. Rename it (e.g. "…stays under 38.5 KB, leaving headroom under the 40 KB budget") so a failure reads right.

148148 expect(JSON.parse(text).result.tools.map((t: any) => t.name)).toContain("pulls");
149149 });
150150

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs