From db7d936c767ea7df52eebbb28acff22f64b7be9a Mon Sep 17 00:00:00 2001 From: Eli Powell Date: Sun, 12 Jul 2026 18:33:36 -0400 Subject: [PATCH] fix: honor authoritative room goal handoff --- .../deterministic/room_receptacle_analog.ts | 5 +++- .../test/room_receptacle_analog.test.ts | 26 ++++++++++++++++++- 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/apps/operator-backend/src/deterministic/room_receptacle_analog.ts b/apps/operator-backend/src/deterministic/room_receptacle_analog.ts index 9750779..514595b 100644 --- a/apps/operator-backend/src/deterministic/room_receptacle_analog.ts +++ b/apps/operator-backend/src/deterministic/room_receptacle_analog.ts @@ -10,7 +10,10 @@ function ensureRoomDesignGoal(req: ChatRequest, intent: AecTaskIntentV1, roomNum const objective = intent.evidence.user_text.trim() || `Lay out receptacles in Room ${roomNumber}.`; const active = getActiveGoalForSession(req.session_id); const sameObjective = !!active && active.objective.trim().toLocaleLowerCase() === objective.toLocaleLowerCase(); - const replaceableAutoGoal = !!active && active.created_by === "auto_goal:chat" && sameObjective && active.work_items.length === 0 && active.evidence_log.length === 0 && active.action_log.length === 0 && active.validation_log.length === 0; + const context = req.context && typeof req.context === "object" && !Array.isArray(req.context) ? req.context as Record : null; + const ui = context?.ui && typeof context.ui === "object" && !Array.isArray(context.ui) ? context.ui as Record : null; + const authoritative = typeof ui?.authoritative_user_text === "string" ? ui.authoritative_user_text.trim() : ""; + const replaceableAutoGoal = !!active && active.created_by === "auto_goal:chat" && active.related_session_id === req.session_id && (sameObjective || authoritative === objective) && active.work_items.length === 0 && active.evidence_log.length === 0 && active.action_log.length === 0 && active.validation_log.length === 0; if (active && !replaceableAutoGoal) return; setAgentGoal(req.session_id, { title: `Design receptacle layout in Room ${roomNumber}`, diff --git a/apps/operator-backend/test/room_receptacle_analog.test.ts b/apps/operator-backend/test/room_receptacle_analog.test.ts index 1e49b33..4551091 100644 --- a/apps/operator-backend/test/room_receptacle_analog.test.ts +++ b/apps/operator-backend/test/room_receptacle_analog.test.ts @@ -6,7 +6,7 @@ import test, { after, before } from "node:test"; import { OPERATOR_BACKEND_CONTRACT_VERSION, type ChatRequest, type ToolResult } from "../src/contracts.js"; import { __testOnlyVerifiedAnalogApplyReceipt, maybeRunDeterministicRoomReceptacleAnalog } from "../src/deterministic/room_receptacle_analog.js"; import { AEC_TASK_INTENT_V1_SCHEMA, type AecTaskIntentV1 } from "../src/aec_task_intent.js"; -import { getActiveGoalForSession } from "../src/goals/service.js"; +import { getActiveGoalForSession, setAgentGoal } from "../src/goals/service.js"; const previousWorkspace = process.env.OPERATOR_WORKSPACE_ROOT; let testWorkspace = ""; @@ -140,6 +140,30 @@ test("room design persists target, selected precedent, exact apply evidence, and assert.match(completed?.progress_summary ?? "", /Room 407 apply and native persistent readback passed/); }); +test("authoritative room request replaces only an empty model-expanded auto goal", () => { + const session = "room-authoritative-auto-goal"; + const prompt = "Layout receptacles in room 403."; + setAgentGoal(session, { + title: "Expanded room plan", + objective: "Inspect Room 403, place and circuit devices, save the model, and report all locations.", + success_criteria: ["Complete or block truthfully."], + created_by: "auto_goal:chat", + current_phase: "observe", + current_step: "preflight" + } as any); + const req = sessionRequest(session, prompt); + req.context = { ui: { authoritative_user_text: prompt } }; + const intent = layoutIntent(); + intent.evidence.user_text = prompt; + const response = maybeRunDeterministicRoomReceptacleAnalog(req, intent); + assert.deepEqual(response?.actions.map(action => action.path), ["/revit/plan-room-receptacles-from-analog"]); + const goal = getActiveGoalForSession(session); + assert.equal(goal?.objective, prompt); + assert.equal(goal?.work_budget?.mode, "room_receptacle_design"); + assert.equal(goal?.work_budget?.conversational_permission_loops, 0); + assert.equal(goal?.work_items.find(item => item.id === "target.inspect")?.scope?.room_number, "403"); +}); + test("completion receipt requires exact current-run ids plus type, room, host, position, and orientation evidence", () => { const valid = appliedReceipt([1700001], [{ familyType: "Duplex Receptacle|Standard", count: 1 }]); assert.ok(__testOnlyVerifiedAnalogApplyReceipt(valid));