fix(backend): robustness pass — spawn leak, timeouts, workspace fail-loud, 409 order, respond guard
Audit findings 4.1-4.6. - spawnFor: a rejected configure/start no longer leaks a registered runtime with a live Pi child (identity-checked teardown + rethrow); every later start used to hit "session runtime already active". - ThtRunner.run: default 60s timeout on every tht child (SIGKILL backstop), 120s for DWH-touching calls (sql preview/export, search pack); a dropped VPN mid-call no longer wedges the HTTP request forever. - configArg: a NAMED workspace whose yaml is missing now throws instead of silently falling back to the default config (operations were silently targeting the wrong workspace). - resume: the finalized/archived 409 is evaluated BEFORE the alreadyActive fast-path — the manifest is the truth even with a lingering runtime. - ollamaEnsure: exit-0 with non-JSON stdout is a failed check, not ok:true. - SessionBridge.respond: only the response matching the pending descriptor is forwarded to Pi; stale/duplicate submissions return 409 instead of being sent with the current gate's RPC id. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -1349,7 +1349,7 @@ test("POST resume tears down a created runtime when bridge binding fails", async
|
||||
expect(delivered).toEqual(["before", "post-failure probe"]);
|
||||
});
|
||||
|
||||
test("POST /sessions/:id/response inoltra al bridge (no error)", async () => {
|
||||
test("POST /sessions/:id/response senza gate pendente risponde 409 (risposta stantia)", async () => {
|
||||
const app = buildApp(loadConfig({ THT_HARNESS_DIR: "../harness" }), {
|
||||
thtRunner: {
|
||||
ollamaEnsure: async () => ({ ok: true }),
|
||||
@@ -1361,9 +1361,11 @@ test("POST /sessions/:id/response inoltra al bridge (no error)", async () => {
|
||||
spawnFn: () => nodeSpawn("node", [FAKE, SCRIPT]) as any,
|
||||
});
|
||||
await app.inject({ method: "POST", url: "/sessions", payload: { question: "q" } });
|
||||
// The fake Pi never emitted a ui_request: the bridge has no pending descriptor, so a
|
||||
// response (stale UI, double submit) must be rejected instead of forwarded to Pi.
|
||||
const res = await app.inject({ method: "POST", url: "/sessions/s1/response",
|
||||
payload: { ui_response: { id: "u1", choices: ["a"] } } });
|
||||
expect(res.statusCode).toBe(204);
|
||||
expect(res.statusCode).toBe(409);
|
||||
});
|
||||
|
||||
test("POST /sessions/:id/rename calls setName", async () => {
|
||||
|
||||
Reference in New Issue
Block a user