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
@@ -608,29 +608,41 @@ function upsertFilesystemArtifact(
|
||||
const title = lastSlash >= 0 ? relativePath.slice(lastSlash + 1) : relativePath;
|
||||
|
||||
const provider = "filesystem";
|
||||
// Reverse-resolve workspaceId from workspacePath when ctx didn't
|
||||
// carry it. The run may have been created before workspace_id was
|
||||
// tracked end-to-end, but the projector only ever walks a workspace
|
||||
// whose local_path is known — that path uniquely identifies the
|
||||
// workspace row, so it is safe to persist the foreign key instead
|
||||
// of writing another orphan.
|
||||
const resolvedWorkspaceId =
|
||||
ctx.workspaceId ?? db.workspacesMod.findWorkspaceIdByLocalPath(ctx.companyId, workspacePath);
|
||||
// Identity is scoped to (company, workspace, provider, externalId) so two
|
||||
// workspaces with the same relative path do not collapse onto one row.
|
||||
// Every touch refreshes `last_touched_by_run_id` so the deliverable
|
||||
// signal, the DocumentCenter preview, and the run-provenance readers
|
||||
// all see the latest run that wrote the file.
|
||||
const existing = db.documentsMod.findWorkProductByExternalId(ctx.companyId, provider, relativePath, ctx.workspaceId);
|
||||
const existing = db.documentsMod.findWorkProductByExternalId(ctx.companyId, provider, relativePath, resolvedWorkspaceId);
|
||||
if (existing) {
|
||||
// `status: "active"` flips a previously-archived row (earlier run
|
||||
// retired the path, this run recreated it) back into the visible
|
||||
// DocumentCenter set. `lastTouchedByRunId` keeps the provenance
|
||||
// timeline pointed at the run that just wrote the bytes.
|
||||
db.documentsMod.updateWorkProduct(existing.id, ctx.companyId, {
|
||||
// Only include workspaceId in the patch when we actually have one —
|
||||
// otherwise the UPDATE would clobber a previously-resolved
|
||||
// workspace_id back to null (the original source of orphan rows).
|
||||
const patch: Parameters<typeof db.documentsMod.updateWorkProduct>[2] = {
|
||||
title,
|
||||
status: "active",
|
||||
lastTouchedByRunId: ctx.runId,
|
||||
workspaceId: ctx.workspaceId,
|
||||
});
|
||||
};
|
||||
if (resolvedWorkspaceId) patch.workspaceId = resolvedWorkspaceId;
|
||||
db.documentsMod.updateWorkProduct(existing.id, ctx.companyId, patch);
|
||||
return { kind: "updated", workProductId: existing.id, relativePath };
|
||||
}
|
||||
const workProductId = db.documentsMod.createWorkProduct({
|
||||
companyId: ctx.companyId,
|
||||
taskId: ctx.taskId,
|
||||
workspaceId: ctx.workspaceId,
|
||||
workspaceId: resolvedWorkspaceId,
|
||||
type: "file",
|
||||
provider,
|
||||
externalId: relativePath,
|
||||
|
||||
Reference in new issue
Block a user