Hide archived work_products from the visible surface

Retirement set `status = "archived"` but none of the downstream
readers honoured it. The snapshot pulled every row, the task-scoped
listing returned every row, the DocumentCenter rendered every row,
and `resolveWorkProductTarget` would happily hand a deleted file's
path to `shell.openPath`. The promise that archived rows "stop
surfacing" was only true inside the projector's own doc comment.

Pin the visibility gate at the three DB read paths:

  - `listSnapshot().workProducts` — the canonical renderer source —
    excludes archived;
  - `listWorkProductsForTask` (task API + thread view) excludes
    archived;
  - `listWorkProductsForCompany` excludes archived.

`resolveWorkProductTarget` now refuses to resolve an archived row, so
`openWorkProduct` fails with `WORK_PRODUCT_NOT_FOUND` instead of
routing the OS shell at a dead path. Archived rows stay in the table
as retirement tombstones — operators still have the audit trail;
they just no longer leak into the operator UI.
This commit is contained in:
ZacharyZhang-NY committed 2026-04-19 17:21:31 -04:00
1 parent 88dfe1a01f
commit f0c4a66cc1
2 files changed
+23 -9

No files matched your search

+5 -1
View File
@@ -129,8 +129,12 @@ export function buildSnapshotMethods(
goalId: r.goal_id, parentDocId: r.parent_doc_id, version: r.version, status: r.status,
tagsJson: r.tags_json, latestRevisionId: r.latest_revision_id ?? null, createdAt: r.created_at, updatedAt: r.updated_at,
})),
// Archived rows stay in the table as tombstones for audit but
// never surface as deliverables — the DocumentCenter and every
// downstream consumer read this snapshot, so the filter is
// pinned here as the canonical visibility gate.
workProducts: readAll<SqliteRow>(
`select id, company_id, project_id, task_id, workspace_id, type, provider, external_id, title, url, status, review_state, is_primary, summary, metadata_json, created_by_run_id, last_touched_by_run_id, created_at, updated_at from work_products${companyWhere} order by updated_at desc limit 500`,
`select id, company_id, project_id, task_id, workspace_id, type, provider, external_id, title, url, status, review_state, is_primary, summary, metadata_json, created_by_run_id, last_touched_by_run_id, created_at, updated_at from work_products${companyWhere}${companyWhere ? " and" : " where"} status <> 'archived' order by updated_at desc limit 500`,
...companyArgs,
).map((r) => ({
id: r.id, companyId: r.company_id, projectId: r.project_id, taskId: r.task_id,