fix(docs): heal orphan work_products by falling back to observation workspace
Work-product rows written before the artifact projector tracked workspace_id end-to-end can lose the link through all three joins: workspace_id is NULL, task_id is NULL, and the row's run exists but its runs.workspace_id is also NULL. Those orphans were readable in the DocumentCenter (the listing doesn't need a workspace root) but could not be opened — resolveWorkProductTarget returned null and the toast now reports workspace_unresolved. Add a fourth resolution tier in resolveWorkProductTarget: if the three-step chain yields nothing, look up the company's observation workspace (primary first, then oldest) and check whether the external_id resolves to a real file inside that root. If it does, return that absolute path and the row opens. If the file isn't there, the error falls through with an extended detail line naming the observation root that was tried, so the operator sees exactly why the heal also failed. This is deterministic — exactly one fallback workspace — and side-effect-free on the read path: the row itself is left alone, no stealth write-back. A follow-up commit will stop the projector from writing NULL workspace_id in the first place. Typecheck and build:verify clean.
This commit is contained in:
1 parent
390f76fa76
commit
cfb8ba5dc1
1 file changed
+35
-10
@@ -1,4 +1,5 @@
|
||||
import { randomUUID } from "node:crypto";
|
||||
import { statSync } from "node:fs";
|
||||
import { isAbsolute, resolve as resolvePath, relative as relativePath } from "node:path";
|
||||
import type { DatabaseSync, SQLInputValue } from "node:sqlite";
|
||||
import type { ReadAll, Transaction } from "./helpers";
|
||||
@@ -379,19 +380,43 @@ export function buildDocumentMethods(
|
||||
row.task_id,
|
||||
row.last_touched_by_run_id ?? row.created_by_run_id,
|
||||
);
|
||||
if (!workspaceRoot) {
|
||||
return {
|
||||
kind: "error",
|
||||
reason: "workspace_unresolved",
|
||||
detail: `workspace_id=${row.workspace_id ?? "null"} task_id=${row.task_id ?? "null"} run_id=${row.last_touched_by_run_id ?? row.created_by_run_id ?? "null"} — none led to a workspace with a local_path`,
|
||||
};
|
||||
if (workspaceRoot) {
|
||||
const absolute = joinWorkspaceRelative(workspaceRoot, row.external_id);
|
||||
if (!absolute) {
|
||||
return { kind: "error", reason: "path_escape", detail: `external_id=${row.external_id} escapes workspace root ${workspaceRoot}` };
|
||||
}
|
||||
return { kind: "path", value: absolute };
|
||||
}
|
||||
|
||||
const absolute = joinWorkspaceRelative(workspaceRoot, row.external_id);
|
||||
if (!absolute) {
|
||||
return { kind: "error", reason: "path_escape", detail: `external_id=${row.external_id} escapes workspace root ${workspaceRoot}` };
|
||||
// Fourth-tier heal: orphan rows created before the projector tracked
|
||||
// workspace_id end-to-end lose the link through all three joins. If
|
||||
// the externalId resolves to a real file inside the company's
|
||||
// observation (primary/oldest) workspace, we treat that as the
|
||||
// canonical root for this row. No guessing across all workspaces —
|
||||
// just one deterministic fallback, and it only fires when the file
|
||||
// actually exists on disk so the shell never sees a phantom path.
|
||||
const observation = getDb()
|
||||
.prepare(
|
||||
"SELECT local_path as localPath FROM workspaces WHERE company_id = ? AND local_path IS NOT NULL ORDER BY is_primary DESC, created_at ASC LIMIT 1",
|
||||
)
|
||||
.get(companyId) as { localPath: string } | undefined;
|
||||
if (observation?.localPath) {
|
||||
const candidate = joinWorkspaceRelative(observation.localPath, row.external_id);
|
||||
if (candidate) {
|
||||
try {
|
||||
if (statSync(candidate).isFile()) {
|
||||
return { kind: "path", value: candidate };
|
||||
}
|
||||
} catch {
|
||||
// fall through to error below
|
||||
}
|
||||
}
|
||||
}
|
||||
return { kind: "path", value: absolute };
|
||||
return {
|
||||
kind: "error",
|
||||
reason: "workspace_unresolved",
|
||||
detail: `workspace_id=${row.workspace_id ?? "null"} task_id=${row.task_id ?? "null"} run_id=${row.last_touched_by_run_id ?? row.created_by_run_id ?? "null"} — none led to a workspace with a local_path, and ${row.external_id} is not present in the company's observation workspace${observation?.localPath ? ` (${observation.localPath})` : " (no observation workspace exists)"}`,
|
||||
};
|
||||
},
|
||||
|
||||
// Task-scoped visible listing. Archived rows stay in the table as
|
||||
|
||||
Reference in new issue
Block a user