diff --git a/docs/development/ui-ux/subject-memory-information.md b/docs/development/ui-ux/subject-memory-information.md
new file mode 100644
index 000000000..2a2ff687c
--- /dev/null
+++ b/docs/development/ui-ux/subject-memory-information.md
@@ -0,0 +1,133 @@
+# Subject / Memory information audit (T-692)
+
+## Decision and scope
+
+The previous Subjects index exposed `Store revision` alongside identity, state and
+update time; Subject detail led with technical metadata and raw surface states.
+These numbers require implementation knowledge before they help a user choose a
+Subject or understand what is available. The revised hierarchy starts with
+identity, connection, committed records and generated-context availability.
+
+This is a presentation change, not a change to storage, freshness comparisons,
+permissions, generation, or Worker history. T-688's ordinary Worker launch and
+T-689's independent Subject creation remain the actual operation paths. T-693
+owns editable user-managed behavior: that content must remain distinct from this
+page's context generated from committed Memories. A successful read or a current
+surface does not attest what is already in any running Worker's history.
+
+## Field classification and before/after priorities
+
+| Information | Classification and reason | Presentation decision |
+| --- | --- | --- |
+| Subject role and ID | Normal identification. Similar or identical roles need a stable identifier for selection and comparison. | Keep role as heading and the complete wrapping ID visible in index and detail. |
+| Subject active/retired state | Normal operation decision: whether a new Worker connection can use the Subject. | Replace raw `active` with `Available`; keep `Retired`. Detail explains connection eligibility, not an assertion that a Worker is running. |
+| Current Worker | Normal connection decision, derived from the existing Backend singleton projection. | Promote `Connected` / `Not connected` and display name. Do not infer ownership from store revision or claim all Workers received current Memory. Worker start remains the ordinary launch form. |
+| Committed-Memory presence | Normal reading decision, independent from surface generation or candidates. | Show `Available` / `None` only on the successful first bounded page. On later pages say `N on this page`; errors say `Unavailable`. Never infer counts from revision or scan all pages. |
+| List counts | Normal navigation context, not a store-wide statistic. | Label `N shown` and `more available`, preserving existing bounded cursor pagination. |
+| Resident availability | Normal context decision. A missing surface is not missing committed Memory. | `Ready to use`, `Ready, no context`, `Not generated`, `Needs refresh`, `Generation failed`, and request `Unavailable` are distinct. These describe the current generated store artifact, not content truth or Worker-history propagation. |
+| Resident body and generated time | Normal content/read freshness context. | Keep the shared Markdown renderer, generated date and exact snapshot reference count. Ready-empty explains that a valid snapshot has no body; stale/failed do not display a partial or old snapshot. |
+| Subject store revision | Diagnostic only: internal committed-Memory change number, not record count, schema version or quality score. | Remove it from the index's normal columns. Retain the exact value under detail's `Technical details`, with its meaning explained. Backend comparisons are untouched. |
+| Subject update time | Secondary metadata; not a context-injection or generation completion timestamp. | Label `Last changed`; on detail put it with Subject diagnostics rather than competing with the availability summary. |
+| Snapshot ID and built-from revision | Diagnostic correlation, distinct from per-Memory revisions. | Retain under `Surface sources and diagnostics`, labelled `Generated from Subject revision` and explained. |
+| Surface Memory references | Detailed audit/reachability. | Preserve reference IDs, immutable revision values and Memory links in the disclosure. |
+| Memory claim, kind, lifecycle state, excerpt and body | Normal content decision. Active, resolved and retracted are not interchangeable. | Retain list scanning and body reading. Keep the Markdown renderer and current/historical distinction. |
+| Individual Memory revision and history | Detailed content comparison, also useful for identifying a selected version. | Explicit `Memory revision` labels, separate from Subject counters. Preserve immutable history, selected revision links and revision pagination. |
+| Candidate provenance, evidence, origin, source refs and derivations | Detailed audit, required to understand why a record exists. | Retain IDs, ranges, summaries, excerpts, origins and derived-revision links. Counts say `shown` except where the API provides exact per-candidate totals. Preserve body and evidence continuation links. |
+| Raw field names / duplicated general revision terminology | Unnecessary in the normal hierarchy; multiple unrelated counters are misleading. | Replace bare revision labels and raw surface enums with purpose-specific labels. Do not delete API fields, stored information, diagnostics or their detailed access. |
+
+No diagnostic datum was removed from Subject detail, surface detail or Memory
+provenance/history. The index no longer shows a store revision, but the exact
+value remains one click away in Subject detail. No invented regeneration,
+editing or recovery action was added; `Needs refresh` does not mean an update job
+is running. Creation still creates only a Subject, and selection still uses the
+existing ordinary launch contract.
+
+## Candidate-only verification
+
+Candidates are not committed records and are not an input to this page. The
+Subject loader performs just three bounded reads: Subject, resident surface and
+`/memories?limit=100[&cursor=...]`. It does not read or count staging records.
+
+The existing real-store regression
+`subjektiv::tests::candidate_decisions_reject_conflicts_and_roll_back_partial_writes`
+stages a candidate and attempts an invalid apply. It asserts the candidate has no
+resolution and the committed Memory list remains empty. The component regression
+`candidate-only Subjects show no committed Memories, not an inferred candidate count`
+uses that read projection (store revision 0, ungenerated surface, empty successful
+committed list) and checks `None`, `No committed Memories yet.`, `Not connected`
+and `Not generated`, with no resident body. Thus a staged candidate does not turn
+into a committed-Memory count or a claim that context has been generated.
+
+The store test proves the persistence boundary; the component test proves the
+rendering of its public projection. These are separate boundary tests, **not** a
+live browser-to-Backend candidate-creation E2E. The existing `ungenerated` capture
+represents the same visible projection, but does not itself stage a candidate.
+
+## Visual review and validation evidence
+
+The restored implementation is in `fbb09e65` and `4cf6c130`; this report/test
+supplement does not change rendered markup, styling, copy or data contracts.
+
+- Scenarios: `tools/web-ux/scenarios/memory-subjects-{light,dark}.json`.
+- Before: `target/web-ux/t-692-before-{light,dark}` at `cc979874`.
+- After: `target/web-ux/t-692-final2-{light,dark}` at clean `4cf6c130`.
+- Comparisons: `target/web-ux/t-692-final2-{light,dark}-comparison`.
+- Browser interactions: `target/web-ux/t-692-browser-interactions`.
+- Persona: synthetic Workspace fixture; permitted creation and intentional
+ permission-rejection responses. No real account or private store data used.
+- Viewports: 1440, 768, 390 and 320px; light and dark themes.
+- States: connected/unconnected, active/retired, successful empty lists, ready
+ long Markdown, ready-empty, ungenerated, stale, failed, unavailable HTTP503,
+ create form and create permission error; expanded technical disclosures.
+- The takeover implementer personally inspected individual before/after images
+ for index at 768px, representative detail at desktop/mobile and Memory detail,
+ then ready-empty mobile, ungenerated narrow, stale desktop, failed mobile,
+ endpoint error at 320px, mobile surface sources, compact Subject diagnostics
+ and mobile permission error. Oversized contact sheets were rejected by the
+ image provider; individual viewport PNGs were inspected instead.
+- Findings/result: primary state groups replace counters without losing IDs or
+ document access; long IDs wrap, status is textual rather than color-only,
+ mobile groups stack, diagnostics remain readable, and error state regions stay
+ separate. No new visual defect identified. **Pass for the audited scope.**
+- Final bundles contain 64 captures each. `completed-with-errors` is expected:
+ intentional creation HTTP403, endpoint HTTP503/associated aborted requests,
+ and the deliberately failed surface's alert are retained as evidence. Healthy
+ capture states have empty error arrays; this is not a blanket claim that all
+ captures were error-free. Review-context diagnostics are empty.
+- The focused production-shell browser test proves main-owned vertical scrolling
+ with wheel/keyboard/touch, 320px/mobile shell behavior, no page-wide overflow,
+ local table/code scroll, Markdown safety, creation/launch separation, bounded
+ pagination and Memory/source/revision reachability.
+
+Independent review at source `4cf6c130` ran root `cargo check`,
+`cargo fmt --all -- --check`, diff hygiene, Web check (0 errors/warnings), 12
+Memory API tests, 452 full Web unit tests, 134 component tests (8 Memory tests),
+production build, the focused production-shell browser test (1), and Web-UX unit
+tests (16), all passing.
+
+The takeover implementer then validated this report/test supplement with:
+
+- `cargo test -p yoi-workspace-server --lib candidate_decisions_reject_conflicts_and_roll_back_partial_writes`: 1 passed.
+- Root `cargo check`, `cargo fmt --all -- --check`, and `git diff --check HEAD`: passed.
+- Targeted Memory component tests: 9 passed, including the added candidate-only projection test.
+- Web `deno task check`: 0 errors/warnings; `deno task test`: 452 passed;
+ `deno task test:component`: 135 passed.
+- Scoped `deno fmt --check` on the changed TypeScript test: passed.
+- JSON audit of both final2 bundles: 64 captures each, exact clean source
+ `4cf6c130`, and no errors outside the deliberate permission/error/failed routes.
+
+The supplement changes only a document and a test, so it does not require new
+rendered-UI captures. Rereview records and the final approved Ticket handoff link
+this report to the new exact source.
+
+Known non-blocking validation diagnostics: component runs emitted incidental
+localhost:3000 connection-refused messages while passing; the production build
+warned about a large chunk. A broader Web-UX typecheck fails in unchanged
+`tools/web-ux/src/processes.ts:44` on a `TextDecoderStream` generic incompatibility;
+this report does not claim that broad check passed.
+
+No live-Backend browser interactions, real Worker model-request propagation,
+regeneration, or server/runtime process E2E were performed. No Rust production
+crate changed in T-692; the supplemental store test exercises an existing
+contract without changing that contract. Backend counters, comparison logic,
+storage schema and feature behavior remain intact.
diff --git a/tools/web-ux/browser-tests/memory_document_fixture_server.ts b/tools/web-ux/browser-tests/memory_document_fixture_server.ts
index 3cd9cc001..b370ffe81 100644
--- a/tools/web-ux/browser-tests/memory_document_fixture_server.ts
+++ b/tools/web-ux/browser-tests/memory_document_fixture_server.ts
@@ -66,7 +66,13 @@ ${"detail-wide-code-".repeat(18)}
`;
const subjects = [
- subject(representativeSubjectId, "Release coordination", "active", 42),
+ subject(
+ representativeSubjectId,
+ "Release coordination",
+ "active",
+ 42,
+ connectedWorker("Release coordination Worker with a deliberately long display name"),
+ ),
subject(emptySubjectId, "Empty ready subject", "active", 0),
subject(staleSubjectId, "Stale surface subject", "active", 19),
subject(failedSubjectId, "Failed surface subject", "active", 8),
@@ -75,7 +81,37 @@ 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 connectedWorker(displayName: string) {
+ return {
+ runtime_id: "embedded-worker-runtime",
+ worker_id: "release-coordination-worker",
+ host_id: "fixture-host",
+ display_name: displayName,
+ label: displayName,
+ profile: "builtin:companion",
+ singleton_key: `subjektiv:${representativeSubjectId}`,
+ tags: [],
+ workspace: {
+ visibility: "workspace",
+ identity: workspaceId,
+ workspace_id: workspaceId,
+ },
+ state: "idle",
+ last_seen_at: "2026-01-02T03:04:05Z",
+ pinned: false,
+ retention_state: "retained",
+ implementation: { kind: "embedded", display_hint: "Fixture Worker" },
+ diagnostics: [],
+ };
+}
+
+function subject(
+ id: string,
+ role: string,
+ state: "active" | "retired",
+ storeRevision: number,
+ currentWorker?: ReturnType,
+) {
return {
id,
role,
@@ -83,6 +119,7 @@ function subject(id: string, role: string, state: "active" | "retired", storeRev
store_revision: storeRevision,
created_at: "2026-01-01T00:00:00Z",
updated_at: "2026-01-02T03:04:05Z",
+ ...(currentWorker ? { current_worker: currentWorker } : {}),
};
}
@@ -125,7 +162,7 @@ function memoriesFor(subjectId: string) {
1,
"working_assumption",
"retracted",
- "T-672 UNIQUE END MARKER",
+ "Memory list end marker for scroll validation",
),
];
}
@@ -235,7 +272,7 @@ function memoryDetail(memoryId: string, requestedRevision: number | null) {
evidence_id: "evidence-message-0001",
origin: { kind: "human_input", account_id: "account-fixture" },
evidence_kind: "message",
- label: "T-672 product direction",
+ label: "Memory information design source",
summary: "Bounded source reference retained with the committed revision.",
}],
source_refs_total: 1,
diff --git a/tools/web-ux/browser-tests/memory_document_shell_test.ts b/tools/web-ux/browser-tests/memory_document_shell_test.ts
index c0913082a..53b167168 100644
--- a/tools/web-ux/browser-tests/memory_document_shell_test.ts
+++ b/tools/web-ux/browser-tests/memory_document_shell_test.ts
@@ -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-692-browser-interactions",
);
async function freePort(): Promise {
@@ -51,7 +51,7 @@ async function markerIsInsideMain(page: Page): Promise {
const document = (globalThis as any).document;
const main = document.querySelector("main");
const marker = [...document.querySelectorAll("h3")].find((heading) =>
- heading.textContent === "T-672 UNIQUE END MARKER"
+ heading.textContent === "Memory list end marker for scroll validation"
);
if (!main || !marker) return false;
const mainRect = main.getBoundingClientRect();
@@ -262,7 +262,7 @@ Deno.test("production subject Memory and Worker launch shells preserve exact sco
assertEquals(await page.locator('a[href="https://example.com"]').count(), 1);
assertEquals(await page.locator('a[href^="javascript:"]').count(), 0);
assertEquals(
- await page.getByRole("article", { name: "Resident surface" }).locator("img, script")
+ await page.getByRole("article", { name: "Resident context" }).locator("img, script")
.count(),
0,
);
@@ -282,7 +282,7 @@ Deno.test("production subject Memory and Worker launch shells preserve exact sco
await page.locator("main").hover();
await page.mouse.wheel(0, 100_000);
} else if (scenario.label === "keyboard") {
- await page.getByText("Surface provenance", { exact: true }).focus();
+ await page.getByText("Surface sources and diagnostics", { exact: true }).focus();
await page.keyboard.press("End");
} else {
await swipeMainToEnd(page);
@@ -313,17 +313,17 @@ Deno.test("production subject Memory and Worker launch shells preserve exact sco
level: 1,
}).waitFor();
assertEquals(await detailPage.getByText("candidate-release-decision-0001").count(), 1);
- assertEquals(await detailPage.getByText("T-672 product direction").count(), 1);
+ assertEquals(await detailPage.getByText("Memory information design source").count(), 1);
assertEquals(await detailPage.getByText("memory-derived-source-0002").count(), 1);
assertEquals(await detailPage.getByRole("heading", { name: "Revision history" }).count(), 1);
- assertEquals(await detailPage.getByText("Revision 3", { exact: true }).count(), 1);
- assertEquals(await detailPage.getByText("Revision 2", { exact: true }).count(), 1);
- assertEquals(await detailPage.getByText("Revision 1", { exact: true }).count(), 0);
+ assertEquals(await detailPage.getByText("Memory revision 3", { exact: true }).count(), 1);
+ assertEquals(await detailPage.getByText("Memory revision 2", { exact: true }).count(), 1);
+ assertEquals(await detailPage.getByText("Memory revision 1", { exact: true }).count(), 0);
await detailPage.getByRole("navigation", { name: "Revision history pages" }).getByRole(
"link",
{ name: "Next page →" },
).click();
- await detailPage.getByText("Revision 1", { exact: true }).waitFor();
+ await detailPage.getByText("Memory revision 1", { exact: true }).waitFor();
assert(new URL(detailPage.url()).searchParams.has("revision_cursor"));
assertEquals(
await detailPage.getByRole("navigation", { name: "Revision history pages" }).getByRole(
@@ -352,6 +352,15 @@ Deno.test("production subject Memory and Worker launch shells preserve exact sco
const paginationPage = await paginationContext.newPage();
await paginationPage.goto(`${baseUrl}/w/${workspaceId}/memory`);
await paginationPage.getByRole("heading", { name: "Subjects", level: 1 }).waitFor();
+ assertEquals(
+ await paginationPage.getByText(
+ "Release coordination Worker with a deliberately long display name",
+ { exact: true },
+ ).count(),
+ 1,
+ );
+ assertEquals(await paginationPage.getByText("Store revision", { exact: true }).count(), 0);
+ assert((await paginationPage.getByText("Not connected", { exact: true }).count()) > 0);
assertEquals(await paginationPage.getByText("Subject on the next page").count(), 0);
await paginationPage.getByRole("navigation", { name: "Subject pages" }).getByRole(
"link",
@@ -451,10 +460,12 @@ Deno.test("production subject Memory and Worker launch shells preserve exact sco
await createPage.locator("code.subject-id", { hasText: "created-subject-0001" }).count(),
1,
);
- const currentWorker = createPage.locator(".subject-facts div").filter({
- hasText: "Current worker",
- });
- assert((await currentWorker.textContent())?.includes("None"));
+ const currentWorker = createPage.getByText("Worker connection", { exact: true }).locator(
+ "..",
+ );
+ assert((await currentWorker.textContent())?.includes("Not connected"));
+ await createPage.getByText("Resident context has not been generated.").waitFor();
+ await createPage.getByText("No committed Memories yet.").waitFor();
assertEquals(mutationPaths, [
`/api/w/${workspaceId}/subjektiv/subjects`,
`/api/w/${workspaceId}/subjektiv/subjects`,
@@ -472,21 +483,29 @@ Deno.test("production subject Memory and Worker launch shells preserve exact sco
const stateCases = [
{
id: "empty-subject",
- text: "Resident surface is ready and empty.",
+ text: "Resident context is current but empty.",
+ role: "status" as const,
+ },
+ {
+ id: "stale-subject",
+ text: "Resident context needs to be refreshed.",
role: "status" as const,
},
- { id: "stale-subject", text: "Resident surface is stale.", role: "status" as const },
{
id: "failed-subject",
- text: "Resident surface generation failed.",
+ text: "Resident context generation failed.",
role: "alert" as const,
},
{
id: "ungenerated-subject",
- text: "Resident surface has not been generated.",
+ text: "Resident context has not been generated.",
role: "status" as const,
},
- { id: "error-subject", text: "Resident surface unavailable.", role: "alert" as const },
+ {
+ id: "error-subject",
+ text: "Resident context status unavailable.",
+ role: "alert" as const,
+ },
];
for (const stateCase of stateCases) {
const page = await stateContext.newPage();
@@ -494,8 +513,14 @@ Deno.test("production subject Memory and Worker launch shells preserve exact sco
const state = page.getByRole(stateCase.role).filter({ hasText: stateCase.text });
await state.waitFor();
assert((await state.textContent())?.includes(stateCase.text));
+ if (stateCase.id === "stale-subject") {
+ assertEquals(await page.getByText("A resolved stale-surface lesson").count(), 1);
+ }
+ if (stateCase.id === "failed-subject") {
+ assertEquals(await page.getByText("A retracted failed-surface constraint").count(), 1);
+ }
if (stateCase.id !== "empty-subject") {
- assertEquals(await page.getByRole("article", { name: "Resident surface" }).count(), 0);
+ assertEquals(await page.getByRole("article", { name: "Resident context" }).count(), 0);
}
await assertNoPageWideOverflow(page);
await page.close();
diff --git a/tools/web-ux/scenarios/memory-subjects-dark.json b/tools/web-ux/scenarios/memory-subjects-dark.json
index 7fbdebfb0..bfb3de69b 100644
--- a/tools/web-ux/scenarios/memory-subjects-dark.json
+++ b/tools/web-ux/scenarios/memory-subjects-dark.json
@@ -122,9 +122,33 @@
"dataState": "Ready generated Markdown surface with long content, unsafe Markdown, wide table/code, provenance refs, and three lifecycle states.",
"ready": {
"kind": "selector",
- "selector": "[data-memory-view='subject'][data-memory-state='ready'] article[aria-label='Resident surface']"
+ "selector": "[data-memory-view='subject'][data-memory-state='ready'] article[aria-label='Resident context']"
},
- "capturePoints": [{ "id": "initial", "label": "Ready subject" }]
+ "capturePoints": [
+ { "id": "initial", "label": "Ready subject" },
+ {
+ "id": "subject-diagnostics",
+ "label": "Subject diagnostics",
+ "interaction": [
+ { "action": "click", "selector": "details.subject-technical-details summary" }
+ ],
+ "ready": {
+ "kind": "selector",
+ "selector": "details.subject-technical-details[open]"
+ }
+ },
+ {
+ "id": "surface-diagnostics",
+ "label": "Surface sources and diagnostics",
+ "interaction": [
+ { "action": "click", "selector": "details.surface-details summary" }
+ ],
+ "ready": {
+ "kind": "selector",
+ "selector": "details.surface-details[open]"
+ }
+ }
+ ]
},
{
"id": "detail",
@@ -150,6 +174,18 @@
},
"capturePoints": [{ "id": "initial", "label": "Ready empty" }]
},
+ {
+ "id": "ungenerated",
+ "label": "Ungenerated subject context",
+ "path": "/w/memory-review/memory/ungenerated-subject",
+ "goal": "Distinguish a Subject with no generated resident context from lost committed Memory.",
+ "dataState": "Ungenerated resident context and a successful empty committed Memory list.",
+ "ready": {
+ "kind": "selector",
+ "selector": "[data-memory-view='subject'][data-memory-state='ungenerated']"
+ },
+ "capturePoints": [{ "id": "initial", "label": "Not generated" }]
+ },
{
"id": "stale",
"label": "Stale subject surface",
diff --git a/tools/web-ux/scenarios/memory-subjects-light.json b/tools/web-ux/scenarios/memory-subjects-light.json
index 9ddf6f803..905f15372 100644
--- a/tools/web-ux/scenarios/memory-subjects-light.json
+++ b/tools/web-ux/scenarios/memory-subjects-light.json
@@ -122,9 +122,33 @@
"dataState": "Ready generated Markdown surface with long content, unsafe Markdown, wide table/code, provenance refs, and three lifecycle states.",
"ready": {
"kind": "selector",
- "selector": "[data-memory-view='subject'][data-memory-state='ready'] article[aria-label='Resident surface']"
+ "selector": "[data-memory-view='subject'][data-memory-state='ready'] article[aria-label='Resident context']"
},
- "capturePoints": [{ "id": "initial", "label": "Ready subject" }]
+ "capturePoints": [
+ { "id": "initial", "label": "Ready subject" },
+ {
+ "id": "subject-diagnostics",
+ "label": "Subject diagnostics",
+ "interaction": [
+ { "action": "click", "selector": "details.subject-technical-details summary" }
+ ],
+ "ready": {
+ "kind": "selector",
+ "selector": "details.subject-technical-details[open]"
+ }
+ },
+ {
+ "id": "surface-diagnostics",
+ "label": "Surface sources and diagnostics",
+ "interaction": [
+ { "action": "click", "selector": "details.surface-details summary" }
+ ],
+ "ready": {
+ "kind": "selector",
+ "selector": "details.surface-details[open]"
+ }
+ }
+ ]
},
{
"id": "detail",
@@ -150,6 +174,18 @@
},
"capturePoints": [{ "id": "initial", "label": "Ready empty" }]
},
+ {
+ "id": "ungenerated",
+ "label": "Ungenerated subject context",
+ "path": "/w/memory-review/memory/ungenerated-subject",
+ "goal": "Distinguish a Subject with no generated resident context from lost committed Memory.",
+ "dataState": "Ungenerated resident context and a successful empty committed Memory list.",
+ "ready": {
+ "kind": "selector",
+ "selector": "[data-memory-view='subject'][data-memory-state='ungenerated']"
+ },
+ "capturePoints": [{ "id": "initial", "label": "Not generated" }]
+ },
{
"id": "stale",
"label": "Stale subject surface",
diff --git a/web/workspace/src/lib/workspace/console/worker-console.ui.test.ts b/web/workspace/src/lib/workspace/console/worker-console.ui.test.ts
index 6194e31f5..10acf6efa 100644
--- a/web/workspace/src/lib/workspace/console/worker-console.ui.test.ts
+++ b/web/workspace/src/lib/workspace/console/worker-console.ui.test.ts
@@ -502,7 +502,9 @@ Deno.test("workspace Memory surfaces use read-only scoped memory APIs", async ()
subjectIndexLoad.includes('"/subjektiv/subjects"') &&
subjectIndexLoad.includes("parseSubjektivSubjectListResponse") &&
subjectIndexPage.includes(">Subjects") &&
- subjectIndexPage.includes("subject.store_revision") &&
+ subjectIndexPage.includes("Worker connection") &&
+ subjectIndexPage.includes("subject.current_worker") &&
+ !subjectIndexPage.includes("subject.store_revision") &&
subjectIndexPage.includes('data-memory-view="subjects"'),
"Memory product entry should list explicit typed subjects",
);
@@ -510,10 +512,13 @@ Deno.test("workspace Memory surfaces use read-only scoped memory APIs", async ()
subjectPageLoad.includes("`${subjectPath}/surface`") &&
subjectPageLoad.includes("`${subjectPath}/memories`") &&
subjectPage.includes("DocumentMarkdown from") &&
- subjectPage.includes("Resident surface") &&
+ subjectPage.includes("Resident context") &&
+ subjectPage.includes("Subject store revision") &&
subjectPage.includes("Current Memories") &&
subjectPage.includes("surface?.availability === 'ready'") &&
+ subjectPage.includes("surface?.availability === 'ungenerated'") &&
subjectPage.includes("surface?.availability === 'stale'") &&
+ subjectPage.includes("surface?.availability === 'failed'") &&
subjectPage.includes("data-surface-ready-empty") &&
!subjectPage.includes("overflow-y"),
"Subject page should combine strict resident-surface and current-Memory reads without a nested vertical scroller",
diff --git a/web/workspace/src/routes/w/[workspaceId]/memory/+page.svelte b/web/workspace/src/routes/w/[workspaceId]/memory/+page.svelte
index 9483ad3fc..2ba3dbdca 100644
--- a/web/workspace/src/routes/w/[workspaceId]/memory/+page.svelte
+++ b/web/workspace/src/routes/w/[workspaceId]/memory/+page.svelte
@@ -31,6 +31,10 @@
return cursor ? `${path}?cursor=${encodeURIComponent(cursor)}` : path;
}
+ function subjectStateLabel(state: SubjektivSubjectResponse['state']): string {
+ return state === 'active' ? 'Available' : 'Retired';
+ }
+
async function openCreateForm(): Promise {
createExpanded = true;
createError = null;
@@ -109,7 +113,7 @@