fix(computers): reserve lifecycle traffic capacity during workspace setup

Route project setup and file operations through ordinary SSH admission, retaining reserved control capacity for owned-port inspection and lifecycle operations.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
DottaandPaperclip committed 2026-10-10 12:19:06 -05:00
1 parent d3fc9db89e
commit d4047f2fef
2 files changed
+38 -2

No files matched your search

@@ -19,6 +19,11 @@ import { afterEach, describe, it, expect, vi } from "vitest";
import { boatBackend, createBoatTransportAdmission, desktopReadinessProgram, processProgram } from "./boat.js";
import { buildGitAuthInvocation } from "../../../services/git-credentials.js";
import type { ComputerRecord } from "../domain/ledger.js";
const sshFactory = vi.hoisted(() => vi.fn());
vi.mock("@paperclipai/adapter-utils/ssh", async importOriginal => ({
...await importOriginal<typeof import("@paperclipai/adapter-utils/ssh")>(),
createSshCommandManagedRuntimeRunner: sshFactory,
}));
const record: ComputerRecord = {
id: "computer",
companyId: "company",
@@ -607,6 +612,34 @@ describe("Boat SSH admission", () => {
await Promise.all(jobs);
expect(runner.execute).toHaveBeenCalledTimes(12);
});
it("classifies checkout and file operations below reserved lifecycle capacity", async () => {
const id = randomUUID();
const scoped = { ...record, id, providerId: `bx_${id}` };
const completions: Array<() => void> = [];
const execute = vi.fn(async (_input: { stdin?: string }) => new Promise<typeof result>(resolve => {
completions.push(() => resolve({ ...result, stdout: "{}" }));
}));
sshFactory.mockReturnValue({ execute });
const backend = boatBackend(async () => "fixture-key", vi.fn(async () => json({
hostKey: "ssh-ed25519 AAAA", sshEndpoint: "fixture.invalid:2222",
})));
const jobs = Array.from({ length: 8 }, (_, index) => backend.remote(scoped, {
action: index % 2 ? "workspace" : "list", root: "/home/user/paperclip/company/project",
}));
await vi.waitFor(() => expect(execute).toHaveBeenCalledTimes(6));
jobs.push(backend.retire(scoped, { id: "owner", generation: 1, kind: "runner", phase: "retiring", port: 43127,
deadline: null, absoluteDeadline: null, process: null }));
jobs.push(backend.remote(scoped, { action: "owned-port", port: 5173, ownerId: "owner" }));
await vi.waitFor(() => expect(execute).toHaveBeenCalledTimes(8));
const actions = execute.mock.calls.map(([input]) => JSON.parse(input.stdin!).action);
expect(actions.filter(action => action === "workspace" || action === "list")).toHaveLength(6);
expect(actions).toContain("retire"); expect(actions).toContain("owned-port");
completions.splice(0).forEach(finish => finish());
await vi.waitFor(() => expect(execute).toHaveBeenCalledTimes(10));
completions.splice(0).forEach(finish => finish());
await Promise.all(jobs);
sshFactory.mockReset();
});
it("expires queued work without starting it and preserves the original remaining timeout", async () => {
vi.useFakeTimers();
try {
@@ -342,9 +342,10 @@ export function boatBackend(
record: ComputerRecord,
code: string,
payload: unknown,
control = true,
) {
const result = await (
await runner(record, { control: true })
await runner(record, { control })
).execute({
command: "python3",
args: ["-c", code],
@@ -550,7 +551,9 @@ print('{}')
return { url: url.toString(), secretHeaders: { Cookie: cookie } };
},
async remote(record, input) {
return execute(record, remoteProgram, input);
// Git setup and file I/O may wait on locks or transfer substantial data.
// Keep lifecycle capacity reserved; only the short owned-port probe uses it.
return execute(record, remoteProgram, input, input.action === "owned-port");
},
};
}