import { mkdirSync, writeFileSync } from "node:fs"; import { mkdtemp, mkdir, rm, writeFile, unlink } from "node:fs/promises"; import { dirname, join } from "node:path"; import { tmpdir } from "node:os"; import { afterEach, describe, expect, it } from "vitest"; import { AppDatabase } from "../../src/main/database"; import { reconcileWorkspaceArtifacts, type ArtifactProjectorRunContext, } from "../../src/main/agent-runtime/artifact-projector"; /** * Regression coverage for the `work_products` retirement + visibility * contract. The projector stamps `status = 'archived'` when a file * disappears; every reader (renderer snapshot, task list, open target * resolver, deliverable signal) must honour that tombstone so the * DocumentCenter stops surfacing removed files and the review gate * stops counting deleted artifacts as produced output. */ const tempDirs: string[] = []; async function createTempDir(prefix: string): Promise { const dir = await mkdtemp(join(tmpdir(), prefix)); tempDirs.push(dir); return dir; } afterEach(async () => { await Promise.all(tempDirs.splice(0).map((dir) => rm(dir, { recursive: true, force: true }))); }); interface Harness { db: AppDatabase; companyId: string; workspaceId: string; workspacePath: string; agentId: string; taskId: string; runId: string; } async function createHarness(): Promise { const profileDir = await createTempDir("agentcompany-workproducts-"); const workspacePath = await createTempDir("agentcompany-workspace-"); const db = new AppDatabase(profileDir); db.init(); db.core.connectors.updateConnectorHealth({ id: "openai-codex", status: "ready", authState: "authenticated", version: "test", lastError: null, }); const companyId = db.core.company.saveCompany({ name: "Work Products Co", description: "Tests archived visibility, bulk retirement, deliverable signal", status: "active", }); const workspaceId = db.core.workspaces.saveWorkspace({ companyId, name: "Primary", localPath: workspacePath, repoUrl: "", repoRef: "", isPrimary: true, }); const agentId = db.core.agents.saveAgent({ companyId, name: "Artifact Engineer", role: "Engineer", title: "Artifact Engineer", status: "active", reportsTo: null, connectorId: "openai-codex", workspaceId, model: null, capabilities: "Writes files", budgetMonthlyUsd: 100, }); const taskId = db.core.tasks.saveTask({ companyId, title: "Produce artifact", description: "", assigneeAgentId: agentId, workspaceId, priority: "medium", status: "in_progress", }); const run = db.core.runs.createRun({ companyId, taskId, agentId, workspaceId, connectorId: "openai-codex", }); return { db, companyId, workspaceId, workspacePath, agentId, taskId, runId: run.id, }; } function registerArtifact(harness: Harness, relativePath: string): string { const absPath = join(harness.workspacePath, relativePath); mkdirSync(dirname(absPath), { recursive: true }); writeFileSync(absPath, "tracked artifact", "utf8"); return harness.db.core.documents.createWorkProduct({ companyId: harness.companyId, taskId: harness.taskId, workspaceId: harness.workspaceId, type: "file", provider: "filesystem", externalId: relativePath, title: relativePath, url: null, createdByRunId: harness.runId, }); } describe("work_products archived visibility", () => { it("excludes archived rows from the renderer snapshot", async () => { const harness = await createHarness(); try { const activeId = registerArtifact(harness, "docs/active.md"); const archivedId = registerArtifact(harness, "docs/retired.md"); harness.db.core.documents.updateWorkProduct(archivedId, harness.companyId, { status: "archived", lastTouchedByRunId: harness.runId, }); const snapshot = harness.db.projection.snapshot.listSnapshot(); const visibleIds = snapshot.workProducts.map((wp) => wp.id); expect(visibleIds).toContain(activeId); expect(visibleIds).not.toContain(archivedId); } finally { harness.db.close(); } }); it("excludes archived rows from the task-scoped listing", async () => { const harness = await createHarness(); try { const activeId = registerArtifact(harness, "src/feature.ts"); const archivedId = registerArtifact(harness, "src/removed.ts"); harness.db.core.documents.updateWorkProduct(archivedId, harness.companyId, { status: "archived", lastTouchedByRunId: harness.runId, }); const rows = harness.db.core.documents.listWorkProductsForTask(harness.taskId, harness.companyId); const ids = rows.map((r) => r.id); expect(ids).toContain(activeId); expect(ids).not.toContain(archivedId); } finally { harness.db.close(); } }); it("refuses to resolve an archived work product open target", async () => { const harness = await createHarness(); try { const id = registerArtifact(harness, "notes/ghost.md"); expect( harness.db.core.documents.resolveWorkProductTarget(id, harness.companyId), ).not.toBeNull(); harness.db.core.documents.updateWorkProduct(id, harness.companyId, { status: "archived", lastTouchedByRunId: harness.runId, }); expect( harness.db.core.documents.resolveWorkProductTarget(id, harness.companyId), ).toMatchObject({ kind: "error", reason: "archived" }); } finally { harness.db.close(); } }); it("resolves workspace-prefixed filesystem rows through another company workspace when the bound root misses", async () => { const harness = await createHarness(); try { const parentPath = await createTempDir("agentcompany-parent-workspace-"); const projectPath = join(parentPath, "booking-app"); await mkdir(projectPath, { recursive: true }); await writeFile(join(projectPath, "AGENTS.md"), "instructions", "utf8"); harness.db.core.workspaces.saveWorkspace({ companyId: harness.companyId, name: "Parent", localPath: parentPath, repoUrl: "", repoRef: "", isPrimary: false, }); const id = harness.db.core.documents.createWorkProduct({ companyId: harness.companyId, taskId: harness.taskId, workspaceId: harness.workspaceId, type: "file", provider: "filesystem", externalId: "booking-app/AGENTS.md", title: "AGENTS.md", url: null, createdByRunId: harness.runId, }); expect( harness.db.core.documents.resolveWorkProductTarget(id, harness.companyId), ).toEqual({ kind: "path", value: join(projectPath, "AGENTS.md") }); } finally { harness.db.close(); } }); it("reports missing filesystem rows when no company workspace contains the file", async () => { const harness = await createHarness(); try { const id = registerArtifact(harness, "docs/missing.md"); await unlink(join(harness.workspacePath, "docs/missing.md")); expect( harness.db.core.documents.resolveWorkProductTarget(id, harness.companyId), ).toMatchObject({ kind: "error", reason: "path_missing" }); } finally { harness.db.close(); } }); }); describe("work_products bulk retirement", () => { it("registers changed artifacts from non-exclusive observation workspaces", async () => { const harness = await createHarness(); try { const ctx: ArtifactProjectorRunContext = { runId: harness.runId, agentId: harness.agentId, companyId: harness.companyId, taskId: harness.taskId, workspaceId: null, workspacePath: harness.workspacePath, workspaceMode: "none", workspaceBaseline: { mtimeByRelPath: new Map(), capturedAt: Date.now(), truncated: false, }, }; await mkdir(join(harness.workspacePath, "reports"), { recursive: true }); await writeFile(join(harness.workspacePath, "reports", "non-exclusive.md"), "observed", "utf8"); const outcomes = reconcileWorkspaceArtifacts(harness.db, ctx); expect(outcomes).toEqual([ expect.objectContaining({ kind: "inserted", relativePath: "reports/non-exclusive.md", }), ]); expect( harness.db.projection.snapshot.listSnapshot().workProducts.some( (entry) => entry.externalId === "reports/non-exclusive.md", ), ).toBe(true); } finally { harness.db.close(); } }); it("registers every changed artifact past the previous 50-path budget", async () => { const harness = await createHarness(); try { const paths: string[] = []; await mkdir(join(harness.workspacePath, "bulk"), { recursive: true }); for (let i = 0; i < 120; i += 1) { const rel = `bulk/new-${i}.md`; paths.push(rel); await writeFile(join(harness.workspacePath, rel), `artifact ${i}`, "utf8"); } const outcomes = reconcileWorkspaceArtifacts(harness.db, { runId: harness.runId, agentId: harness.agentId, companyId: harness.companyId, taskId: harness.taskId, workspaceId: null, workspacePath: harness.workspacePath, workspaceMode: "none", workspaceBaseline: { mtimeByRelPath: new Map(), capturedAt: Date.now(), truncated: false, }, }); expect(outcomes.filter((o) => o.kind === "inserted")).toHaveLength(120); const visible = new Set( harness.db.projection.snapshot.listSnapshot().workProducts.map((entry) => entry.externalId), ); for (const path of paths) { expect(visible.has(path)).toBe(true); } } finally { harness.db.close(); } }); it("retires deleted artifacts from non-exclusive observation workspaces", async () => { const harness = await createHarness(); try { const relPath = "reports/non-exclusive-retired.md"; const workProductId = registerArtifact(harness, relPath); await unlink(join(harness.workspacePath, relPath)); const outcomes = reconcileWorkspaceArtifacts(harness.db, { runId: harness.runId, agentId: harness.agentId, companyId: harness.companyId, taskId: harness.taskId, workspaceId: null, workspacePath: harness.workspacePath, workspaceMode: "none", workspaceBaseline: { mtimeByRelPath: new Map([[relPath, Date.now()]]), capturedAt: Date.now(), truncated: false, }, }); expect(outcomes).toEqual([ expect.objectContaining({ kind: "retired", relativePath: relPath, }), ]); expect( harness.db.core.documents.resolveWorkProductTarget(workProductId, harness.companyId), ).toMatchObject({ kind: "error", reason: "archived" }); } finally { harness.db.close(); } }); it("archives every deletion past the previous 50-path budget", async () => { const harness = await createHarness(); try { const ids: string[] = []; const paths: string[] = []; for (let i = 0; i < 120; i += 1) { const rel = `build/asset-${i}.bin`; paths.push(rel); ids.push(registerArtifact(harness, rel)); } const archived = harness.db.core.documents.archiveWorkProductsByExternalPaths({ companyId: harness.companyId, workspaceId: harness.workspaceId, provider: "filesystem", paths, lastTouchedByRunId: harness.runId, }); expect(archived.length).toBe(120); const archivedIdSet = new Set(archived.map((r) => r.id)); for (const id of ids) { expect(archivedIdSet.has(id)).toBe(true); } const visible = harness.db.projection.snapshot.listSnapshot().workProducts; expect(visible.length).toBe(0); } finally { harness.db.close(); } }); it("skips rows that are already archived so repeated sweeps stay idempotent", async () => { const harness = await createHarness(); try { const id = registerArtifact(harness, "tmp/scratch.log"); harness.db.core.documents.archiveWorkProductsByExternalPaths({ companyId: harness.companyId, workspaceId: harness.workspaceId, provider: "filesystem", paths: ["tmp/scratch.log"], lastTouchedByRunId: harness.runId, }); const second = harness.db.core.documents.archiveWorkProductsByExternalPaths({ companyId: harness.companyId, workspaceId: harness.workspaceId, provider: "filesystem", paths: ["tmp/scratch.log"], lastTouchedByRunId: harness.runId, }); expect(second).toHaveLength(0); expect( harness.db.core.documents.resolveWorkProductTarget(id, harness.companyId), ).toMatchObject({ kind: "error", reason: "archived" }); } finally { harness.db.close(); } }); it("archives rows via the reconcile sweep when the baseline detects a missing file", async () => { const harness = await createHarness(); try { await mkdir(join(harness.workspacePath, "reports"), { recursive: true }); const relPath = "reports/summary.md"; const absPath = join(harness.workspacePath, relPath); await writeFile(absPath, "initial content", "utf8"); const workProductId = registerArtifact(harness, relPath); expect( harness.db.core.documents.resolveWorkProductTarget(workProductId, harness.companyId), ).not.toBeNull(); const baseline = { mtimeByRelPath: new Map([[relPath, Date.now() / 1000]]), capturedAt: Date.now(), truncated: false, }; await unlink(absPath); const ctx: ArtifactProjectorRunContext = { runId: harness.runId, agentId: harness.agentId, companyId: harness.companyId, taskId: harness.taskId, workspaceId: harness.workspaceId, workspacePath: harness.workspacePath, workspaceMode: "exclusive", workspaceBaseline: baseline, }; const outcomes = reconcileWorkspaceArtifacts(harness.db, ctx); const retired = outcomes.filter((o) => o.kind === "retired"); expect(retired.map((o) => (o as { relativePath: string }).relativePath)).toContain(relPath); expect( harness.db.core.documents.resolveWorkProductTarget(workProductId, harness.companyId), ).toMatchObject({ kind: "error", reason: "archived" }); } finally { harness.db.close(); } }); }); describe("work_products deliverable signal", () => { it("does not count an archived row as a produced deliverable", async () => { const harness = await createHarness(); try { const id = registerArtifact(harness, "deliverables/final.md"); const beforeArchive = harness.db.core.knowledge.collectRunDeliverableSignals( harness.runId, harness.companyId, harness.taskId, ); expect(beforeArchive.hasWorkProduct).toBe(true); harness.db.core.documents.updateWorkProduct(id, harness.companyId, { status: "archived", lastTouchedByRunId: harness.runId, }); const afterArchive = harness.db.core.knowledge.collectRunDeliverableSignals( harness.runId, harness.companyId, harness.taskId, ); expect(afterArchive.hasWorkProduct).toBe(false); } finally { harness.db.close(); } }); });