Skip to content

Commit b11ada1

Browse files
test(memory): desktop+390 browser proof; fix stale posture and cross-agent reads
The @core p5-memory browser spec proves the BF Memory plant room end to end with console-error, pageerror, requestfailed, and 400+ capture plus recorded sensitive-mutation bodies: one lamp by stable id, the global plant landing with honest per-assistant posture, exact posture and Staff-card drill-ins, the honest unconfigured panel, every drill-in section with the card-to-Reasoning-to-citation chain, exact runtime defaults save and source sync bodies, the lane-policy save as one full-routing write through the shared authority, elevator relanding, the eleventh-shortcut byte-for-byte full-canvas refusal with all ten earlier shortcuts genuinely pinned, config-only pin with honest repeat, live hide/show sync, door execution, reload persistence, the disabled and restored door truth, the MP room mark with an elementFromPoint no-cover proof, per-section 390px containment, and zero horizontal overflow. Thirteen screenshots were captured and individually reviewed. The proof surfaced two real defects, both fixed test-first: Memory's posture presented a stale routing snapshot after a Front Desk save (the controller now reloads when the shared authority's routing config changes), and a derived agent-selection flip could pair the new agent with the previous agent's card, atom, graph, turn, or citation ids in detail requests (every visible list is now gated on its payload's own agent identity). The mock gateway gained faithful lanes/status and memory sources/sync endpoints derived from the live runtime config. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 parent 4349cad commit b11ada1

4 files changed

Lines changed: 1039 additions & 12 deletions

File tree

apps/mission-control/e2e/mockGateway.mjs

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2887,6 +2887,61 @@ function getAgentMemoryStatusPayload(agent) {
28872887
return cloneSeed(lane.status);
28882888
}
28892889

2890+
/**
2891+
* Lane runtime statuses derive from the live runtime routing config so the
2892+
* browser proof exercises the same identity boundaries as production: only
2893+
* enabled assignments from enabled humans produce lanes, and only for the
2894+
* exact requested agent.
2895+
*/
2896+
function buildAgentMemoryLaneStatuses(agent) {
2897+
const routing = runtimeConfig.routing;
2898+
if (!routing.enabled) {
2899+
return [];
2900+
}
2901+
const enabledHumans = new Set(
2902+
routing.human_identities
2903+
.filter((human) => human.enabled)
2904+
.map((human) => human.human_identity_id)
2905+
);
2906+
const bound = Boolean(
2907+
agent.memory_binding?.enabled && getAgentMemoryLane(agent.agent_id)
2908+
);
2909+
return routing.assistant_assignments
2910+
.filter(
2911+
(assignment) =>
2912+
assignment.enabled &&
2913+
assignment.assistant_agent_id === agent.agent_id &&
2914+
enabledHumans.has(assignment.human_identity_id)
2915+
)
2916+
.map((assignment) => {
2917+
const policy =
2918+
routing.lane_memory_policies.find(
2919+
(item) =>
2920+
item.human_identity_id === assignment.human_identity_id &&
2921+
item.assistant_agent_id === agent.agent_id
2922+
) ?? null;
2923+
const configured = policy?.memory_mode ?? "inherit_runtime";
2924+
const effective =
2925+
configured === "inherit_runtime" ? "mno_with_local_sources" : configured;
2926+
return {
2927+
human_identity_id: assignment.human_identity_id,
2928+
assistant_agent_id: agent.agent_id,
2929+
lane_id:
2930+
policy?.lane_id ??
2931+
`lane-${assignment.human_identity_id}-${agent.agent_id}`,
2932+
configured_memory_mode: configured,
2933+
effective_memory_mode: effective,
2934+
source: bound ? "managed_lane" : "memory_off",
2935+
status: bound ? "available" : "unconfigured",
2936+
detail: bound
2937+
? "Lane runtime is live against the assistant's memory binding."
2938+
: "This assistant has no memory binding, so the lane runs without shared memory.",
2939+
local_memory_sources: policy ? [...policy.local_memory_sources] : [],
2940+
orchestration: null,
2941+
};
2942+
});
2943+
}
2944+
28902945
function sendAgentMemoryJson(res, agentId, data) {
28912946
const lane = getAgentMemoryLane(agentId);
28922947
const bindingId =
@@ -4034,6 +4089,61 @@ async function routeRequest(req, res) {
40344089
return;
40354090
}
40364091

4092+
const agentMemoryLaneStatusMatch = requestUrl.pathname.match(
4093+
/^\/api\/v1\/agents\/([^/]+)\/memory\/lanes\/status$/
4094+
);
4095+
if (req.method === "GET" && agentMemoryLaneStatusMatch) {
4096+
const agentId = decodeURIComponent(agentMemoryLaneStatusMatch[1]);
4097+
const agent = agents.find((item) => item.agent_id === agentId);
4098+
if (!agent) {
4099+
sendJson(res, 404, { error: "agent not found" });
4100+
return;
4101+
}
4102+
sendJson(res, 200, { items: buildAgentMemoryLaneStatuses(agent) });
4103+
return;
4104+
}
4105+
4106+
if (
4107+
req.method === "POST" &&
4108+
requestUrl.pathname === "/api/v1/memory/sources/sync"
4109+
) {
4110+
const payload = await readJson(req);
4111+
const humanId =
4112+
typeof payload.human_identity_id === "string"
4113+
? payload.human_identity_id
4114+
: "";
4115+
const assistantId =
4116+
typeof payload.assistant_agent_id === "string"
4117+
? payload.assistant_agent_id
4118+
: "";
4119+
let sources = Array.isArray(payload.sources) ? payload.sources : null;
4120+
if (!sources) {
4121+
if (humanId && assistantId) {
4122+
const policy = runtimeConfig.routing.lane_memory_policies.find(
4123+
(item) =>
4124+
item.human_identity_id === humanId &&
4125+
item.assistant_agent_id === assistantId
4126+
);
4127+
sources =
4128+
policy && policy.local_memory_sources.length > 0
4129+
? policy.local_memory_sources
4130+
: runtimeConfig.memory.memory_md_sources;
4131+
} else {
4132+
sources = runtimeConfig.memory.memory_md_sources;
4133+
}
4134+
}
4135+
const syncedAt = Date.now();
4136+
const items = sources.map((sourcePath, index) => ({
4137+
source_path: sourcePath,
4138+
note_id: `memory-note-${index + 1}`,
4139+
status: "synced",
4140+
detail: null,
4141+
synced_at: syncedAt,
4142+
}));
4143+
sendJson(res, 200, { items, synced: items.length, failed: 0 });
4144+
return;
4145+
}
4146+
40374147
const agentMemoryCardsMatch = requestUrl.pathname.match(
40384148
/^\/api\/v1\/agents\/([^/]+)\/memory\/cards$/
40394149
);

0 commit comments

Comments
 (0)