Add persistent Subject behavior context
This commit is contained in:
@@ -66,7 +66,14 @@ ${"detail-wide-code-".repeat(18)}
|
||||
<script>alert("not rendered")</script>`;
|
||||
|
||||
const subjects = [
|
||||
subject(representativeSubjectId, "Release coordination", "active", 42),
|
||||
subject(
|
||||
representativeSubjectId,
|
||||
"Release coordination",
|
||||
"active",
|
||||
42,
|
||||
"Prefer explicit evidence over assumptions.\nAsk before irreversible actions, and state uncertainty plainly.",
|
||||
3,
|
||||
),
|
||||
subject(emptySubjectId, "Empty ready subject", "active", 0),
|
||||
subject(staleSubjectId, "Stale surface subject", "active", 19),
|
||||
subject(failedSubjectId, "Failed surface subject", "active", 8),
|
||||
@@ -75,10 +82,19 @@ const subjects = [
|
||||
];
|
||||
const pagedSubject = subject(pagedSubjectId, "Subject on the next page", "active", 1);
|
||||
|
||||
function subject(id: string, role: string, state: "active" | "retired", storeRevision: number) {
|
||||
function subject(
|
||||
id: string,
|
||||
role: string,
|
||||
state: "active" | "retired",
|
||||
storeRevision: number,
|
||||
behaviorMd = "",
|
||||
behaviorRevision = 0,
|
||||
) {
|
||||
return {
|
||||
id,
|
||||
role,
|
||||
behavior_md: behaviorMd,
|
||||
behavior_revision: behaviorRevision,
|
||||
state,
|
||||
store_revision: storeRevision,
|
||||
created_at: "2026-01-01T00:00:00Z",
|
||||
@@ -403,7 +419,10 @@ Deno.serve({ hostname: "127.0.0.1", port }, async (request) => {
|
||||
const subjectsPath = `/api/w/${workspaceId}/subjektiv/subjects`;
|
||||
if (url.pathname === subjectsPath) {
|
||||
if (request.method === "POST") {
|
||||
const payload = await request.json().catch(() => null) as { role?: unknown } | null;
|
||||
const payload = await request.json().catch(() => null) as {
|
||||
role?: unknown;
|
||||
behavior_md?: unknown;
|
||||
} | null;
|
||||
subjectCreateRequests.push(payload);
|
||||
if (!payload || typeof payload.role !== "string" || payload.role.trim().length === 0) {
|
||||
return json({ error: "Bad Request", message: "subject role must not be empty" }, 400);
|
||||
@@ -411,9 +430,12 @@ Deno.serve({ hostname: "127.0.0.1", port }, async (request) => {
|
||||
if (payload.role === "Permission denied") {
|
||||
return json({ error: "Forbidden", message: "workspace permission denied" }, 403);
|
||||
}
|
||||
if (typeof payload.behavior_md !== "string") {
|
||||
return json({ error: "Bad Request", message: "subject behavior must be a string" }, 400);
|
||||
}
|
||||
createdSubjectCount += 1;
|
||||
const id = `created-subject-${String(createdSubjectCount).padStart(4, "0")}`;
|
||||
const created = subject(id, payload.role, "active", 0);
|
||||
const created = subject(id, payload.role, "active", 0, payload.behavior_md);
|
||||
createdSubjectIds.add(id);
|
||||
subjects.unshift(created);
|
||||
return json(created, 201);
|
||||
@@ -442,6 +464,27 @@ Deno.serve({ hostname: "127.0.0.1", port }, async (request) => {
|
||||
const foundSubject = subjects.find((item) => item.id === subjectId);
|
||||
if (!foundSubject) return json({}, 404);
|
||||
|
||||
if (parts.length === 2 && parts[1] === "behavior" && request.method === "PATCH") {
|
||||
const payload = await request.json().catch(() => null) as {
|
||||
expected_behavior_revision?: unknown;
|
||||
behavior_md?: unknown;
|
||||
} | null;
|
||||
if (
|
||||
!payload ||
|
||||
typeof payload.expected_behavior_revision !== "number" ||
|
||||
typeof payload.behavior_md !== "string"
|
||||
) {
|
||||
return json({ error: "Bad Request", message: "invalid behavior update" }, 400);
|
||||
}
|
||||
if (payload.expected_behavior_revision !== foundSubject.behavior_revision) {
|
||||
return json({ error: "Conflict", message: "subject behavior revision conflict" }, 409);
|
||||
}
|
||||
foundSubject.behavior_md = payload.behavior_md;
|
||||
foundSubject.behavior_revision += 1;
|
||||
foundSubject.updated_at = "2026-01-03T04:05:06Z";
|
||||
return json(foundSubject);
|
||||
}
|
||||
if (request.method !== "GET") return new Response("method not allowed", { status: 405 });
|
||||
if (parts.length === 1) return json(foundSubject);
|
||||
if (parts.length === 2 && parts[1] === "surface") return json(surfaceFor(subjectId));
|
||||
if (parts.length === 2 && parts[1] === "memories") {
|
||||
|
||||
@@ -16,7 +16,7 @@ const memoryId =
|
||||
const subjectPageCursor = "fixture-subject-page-2";
|
||||
const createInteractionArtifacts = join(
|
||||
repositoryRoot,
|
||||
"target/web-ux/t-689-browser-interactions",
|
||||
"target/web-ux/t-693-subject-behavior",
|
||||
);
|
||||
|
||||
async function freePort(): Promise<number> {
|
||||
@@ -111,6 +111,7 @@ async function openSubjectPage(
|
||||
|
||||
await hideSidebar.click();
|
||||
await showSidebar.waitFor();
|
||||
await page.waitForTimeout(250);
|
||||
assertEquals(await showSidebar.getAttribute("aria-expanded"), "false");
|
||||
assertEquals(
|
||||
await page.locator("main").evaluate((main) => (main as unknown as { inert: boolean }).inert),
|
||||
@@ -217,6 +218,13 @@ Deno.test("production subject Memory and Worker launch shells preserve exact sco
|
||||
});
|
||||
page.on("pageerror", (error) => errors.push(String(error)));
|
||||
await openSubjectPage(page, baseUrl, scenario.mobile);
|
||||
assertEquals(
|
||||
await page.getByRole("article", { name: "User-managed Subject behavior" }).count(),
|
||||
1,
|
||||
);
|
||||
await page.screenshot({
|
||||
path: join(createInteractionArtifacts, `subject-${scenario.label}.png`),
|
||||
});
|
||||
|
||||
const initial = await mainScrollState(page);
|
||||
assert(initial.scrollHeight > initial.clientHeight);
|
||||
@@ -425,7 +433,9 @@ Deno.test("production subject Memory and Worker launch shells preserve exact sco
|
||||
const createPage = await createContext.newPage();
|
||||
const mutationPaths: string[] = [];
|
||||
createPage.on("request", (request) => {
|
||||
if (request.method() === "POST") mutationPaths.push(new URL(request.url()).pathname);
|
||||
if (["POST", "PATCH"].includes(request.method())) {
|
||||
mutationPaths.push(new URL(request.url()).pathname);
|
||||
}
|
||||
});
|
||||
await createPage.goto(`${baseUrl}/w/${workspaceId}/memory?cursor=${subjectPageCursor}`);
|
||||
await createPage.getByText("Subject on the next page").waitFor();
|
||||
@@ -444,9 +454,21 @@ Deno.test("production subject Memory and Worker launch shells preserve exact sco
|
||||
await createPage.getByRole("button", { name: "Cancel" }).click();
|
||||
await createPage.getByRole("button", { name: "New Subject" }).click();
|
||||
await createPage.getByRole("textbox", { name: "Role" }).fill(" Release steward ");
|
||||
await createPage.getByRole("textbox", { name: /Behavior/ }).fill(
|
||||
"Challenge unsupported assumptions before release.",
|
||||
);
|
||||
await createPage.getByRole("button", { name: "Create Subject" }).click();
|
||||
await createPage.waitForURL("**/memory/created-subject-0001");
|
||||
await createPage.getByRole("heading", { name: "Release steward", level: 1 }).waitFor();
|
||||
assertEquals(
|
||||
await createPage.getByRole("article", { name: "User-managed Subject behavior" }).count(),
|
||||
1,
|
||||
);
|
||||
assert(
|
||||
(await createPage.getByRole("article", {
|
||||
name: "User-managed Subject behavior",
|
||||
}).textContent())?.includes("Challenge unsupported assumptions before release."),
|
||||
);
|
||||
assertEquals(
|
||||
await createPage.locator("code.subject-id", { hasText: "created-subject-0001" }).count(),
|
||||
1,
|
||||
@@ -455,15 +477,40 @@ Deno.test("production subject Memory and Worker launch shells preserve exact sco
|
||||
hasText: "Current worker",
|
||||
});
|
||||
assert((await currentWorker.textContent())?.includes("None"));
|
||||
await createPage.getByRole("button", { name: "Edit" }).click();
|
||||
const behaviorEditor = createPage.getByRole("textbox", { name: "Subject behavior" });
|
||||
await behaviorEditor.fill(
|
||||
"Challenge unsupported assumptions before release.\nName the evidence and any remaining uncertainty.",
|
||||
);
|
||||
await createPage.screenshot({
|
||||
path: join(createInteractionArtifacts, "behavior-edit-desktop-light.png"),
|
||||
});
|
||||
await createPage.getByRole("button", { name: "Save behavior" }).click();
|
||||
await createPage.getByText(/Behavior storage confirmed at revision 1/).waitFor();
|
||||
assert(
|
||||
(await createPage.getByRole("status").filter({
|
||||
hasText: "This page does not confirm application",
|
||||
}).textContent())?.includes("does not confirm application"),
|
||||
);
|
||||
await createPage.screenshot({
|
||||
path: join(createInteractionArtifacts, "behavior-saved-desktop-light.png"),
|
||||
});
|
||||
assertEquals(mutationPaths, [
|
||||
`/api/w/${workspaceId}/subjektiv/subjects`,
|
||||
`/api/w/${workspaceId}/subjektiv/subjects`,
|
||||
`/api/w/${workspaceId}/subjektiv/subjects/created-subject-0001/behavior`,
|
||||
]);
|
||||
const createEvidence = await (await fetch(`${baseUrl}/fixture/subject-create-requests`))
|
||||
.json();
|
||||
assertEquals(createEvidence, {
|
||||
count: 2,
|
||||
requests: [{ role: "Permission denied" }, { role: " Release steward " }],
|
||||
requests: [
|
||||
{ role: "Permission denied", behavior_md: "" },
|
||||
{
|
||||
role: " Release steward ",
|
||||
behavior_md: "Challenge unsupported assumptions before release.",
|
||||
},
|
||||
],
|
||||
});
|
||||
await assertNoPageWideOverflow(createPage);
|
||||
await createContext.close();
|
||||
|
||||
Reference in New Issue
Block a user