fix(projector): stop writing null workspace_id on work_products
Two sites in upsertFilesystemArtifact leaked null workspace_ids into the work_products table and produced the orphan rows the read path now has to heal: 1. Create path unconditionally forwarded ctx.workspaceId, which was null whenever a run was created without a workspace binding (historically cognitive runs, or runs that lost their link through the observationPath regression fixed in1f2f977). 2. Update path passed {workspaceId: ctx.workspaceId} as part of the patch. updateWorkProduct treats any key present in patch as "write this value", so a run touching a previously-well-linked row with ctx.workspaceId=null would clobber the existing workspace_id back to null — actively destroying data. Resolve both at the projector layer: - Add workspacesMod.findWorkspaceIdByLocalPath(companyId, localPath) as a reverse-lookup. workspacePath is the argument the projector is already walking; it came from some workspace row's local_path, so finding that row by path is deterministic. - Compute resolvedWorkspaceId = ctx.workspaceId ?? reverseLookup(...) once, and use it for both the identity lookup (findWorkProductByExternalId) and the subsequent write. - On the update path, only include workspaceId in the patch when resolvedWorkspaceId is non-null. Null now means "no info — leave the existing column alone" instead of "clobber to null". Together with the read-path heal incfb8ba5this closes the loop: new files never enter the table with a null workspace_id if we can possibly resolve it, old files keep their links, and truly unlinkable rows still fall back to the observation workspace on open. Typecheck and build:verify clean.
This commit is contained in:
1 parent
cfb8ba5dc1
commit
9fba8dd6c3
2 files changed
+35
-5
No files matched your search
@@ -51,6 +51,24 @@ export function buildWorkspaceMethods(
|
||||
return row ? { ...row, isPrimary: toBoolean(row.isPrimary) } : null;
|
||||
},
|
||||
|
||||
/**
|
||||
* Reverse-lookup a workspace's id by its local_path within a
|
||||
* company. Used by the artifact projector to heal a run context
|
||||
* whose workspaceId was never bound but whose workspacePath is
|
||||
* still known — we look up the row that owns that path so the
|
||||
* resulting work_product carries a real foreign key instead of
|
||||
* another null that downstream readers would have to heal again.
|
||||
* Returns null when the path does not match any workspace (the
|
||||
* caller proceeds without a workspace_id, which is then handled
|
||||
* defensively by updateWorkProduct).
|
||||
*/
|
||||
findWorkspaceIdByLocalPath(companyId: string, localPath: string): string | null {
|
||||
const row = getDb()
|
||||
.prepare("select id from workspaces where company_id = ? and local_path = ? limit 1")
|
||||
.get(companyId, localPath) as { id: string } | undefined;
|
||||
return row?.id ?? null;
|
||||
},
|
||||
|
||||
getProjectPrimaryWorkspace(projectId: string | null | undefined, companyId: string): WorkspaceRecord | null {
|
||||
if (!projectId) return null;
|
||||
const row = getDb()
|
||||
|
||||
Reference in new issue
Block a user