# Gap Card — S05 Artifact Center > Execution model: **in-place incremental refactor** of `apps/web` + targeted backend extensions. > Mockups are directional; PRD acceptance criteria win over pixels (PRD §24). > Sources: PRD §12.5 + §16.6, blueprint p.32 (`_blueprint_extracted.txt:306-317, 525, 547`), > mockup `screen_05_artifact_center.png`, baseline inventories under `docs/ux-refactor/_inventory/`, > backend-map `sections/04-feature-map.md`. --- ## 1. Screen & purpose The **Artifact Center** is the **outcome layer**: it organizes *outputs* (documents, presentations, spreadsheets, dashboards, research, code, media, designs, other) as first-class **relational objects** — not file attachments. PRD §12.5 purpose verbatim: "Organize outcomes, not just attachments." The headline acceptance criterion (PRD line 532): **searching a topic (e.g. "Germany GTM") returns all relevant outcome objects plus related memories, sessions, tasks, agents, and people — not just files.** Mockup (directional): left filter rail (type / status / workspace / tag facets), a center **artifact table** (icon, title, type, workspace, status badge, updated, owner) with a top search bar + view toggle + pagination, and a right **detail panel** (preview thumbnail, metadata, related items, actions). This is a **data-heavy table+detail screen**, the same shape the blueprint flags as acceptable in light variant (`_blueprint_extracted.txt:484`). The defining difference from today's Files app: an artifact is an **outcome with relations** (`relatedMemoryIds / relatedSessionIds / relatedTaskIds / relatedAgentIds`, `generatedByAgentId`, `status: draft|final|shared|generated`), addressable by a stable `id`, spanning **all storage backends and all formats**. Today's Files app exposes only a raw filesystem tree scoped to one workspace + one storage tab. --- ## 2. Required states (PRD / Blueprint) PRD §12.5 functional requirements: - Artifact **categories** (= `ArtifactKind`, PRD §15.2 line 950): `document | presentation | spreadsheet | dashboard | research | code | media | design | other`. - **Topic search returns artifacts + related memories/sessions/tasks/agents/people** (the §16.6 `GET /api/artifacts/search-related?q=` contract). - **Detail panel**: preview, metadata, workspace, creator, updated time, access, status, tags, related items, actions. - **Actions**: open, share, duplicate, move, delete, relate to workspace/memory/session/task. State model — PRD §14.1 global states (every major screen): Loading, Empty, Populated, Error, Offline/local-only, Syncing, Permission denied, Partial data, Approval required. Plus the **artifact-specific states** (PRD §14 line not enumerated but blueprint `:312-314, 458`): **Draft, Final, Shared, Generated, External-missing (source unavailable), Permission denied.** So the concrete states to build: 1. Loading (skeleton table + skeleton detail). 2. Empty ("no artifacts yet" — first-run / no outputs produced). 3. Populated (table + facets + detail). 4. Error (fetch failed). 5. Offline/local-only (sidecar unreachable — mirror FilesApp offline banner pattern). 6. Permission denied (team-scoped artifact the user can't view). 7. Per-row status badges: Draft / Final / Shared / Generated. 8. Source-unavailable (artifact row whose backing file/url is missing — show broken-link affordance). --- ## 3. Current state in repo (disposition: **create-new** for the screen; **keep-promote** the substrates) **There is NO Artifact entity, type, route, or component anywhere.** Grep-confirmed: - No `Artifact` type in `apps/web/src/lib/types.ts` (the only `Artifact` hit in `apps/web/src` is `components/os/apps/memory/EvolutionTab.tsx`, referring to evolution `artifacts_json` — unrelated). - No `artifacts` table in `packages/hive-mind-core/src/mind/schema.ts`. - No `/api/artifacts/*` routes (grep over `packages/server/src/local/routes/*.ts`: 0 matches). - Confirmed by `_inventory/substrate-types.md:259-266` ("**NO backing entity anywhere** … the single largest entity gap") and `_inventory/backend-routes.md:476-484` (all 6 §16.6 rows PARTIAL/MISSING). **The closest current surface is the Files app** (the current-component hint), which is **NOT an artifact center** — disposition for it is **keep-as-is, do not retrofit**: - `apps/web/src/components/os/apps/FilesAppTabs.tsx` — P16 three-tab (Virtual/Local/Team) wrapper that remounts `FilesApp` per `storageType`. Storage-location switcher, not an outcome browser. - `apps/web/src/components/os/apps/FilesApp.tsx` (735 LOC) — full file-manager: tree + list/grid + preview + upload + rename/move/copy/delete + bulk ops + properties dialog + inline `VersionHistory`. It is **path/workspace/storage-scoped** (`adapter.listFiles(workspaceId, currentPath)`), has no cross-workspace aggregation, no type/status/relation model, no facet filtering by outcome kind. - Sub-components `components/os/files/{FileTree,FilePreview,FileActions,FileUploadZone,SyntaxPreview, WorkspaceRail}.tsx` — operate on `FileEntry` (`lib/types.ts:42-50`: `name/path/type/size/mimeType/ modifiedAt/createdAt`), a raw FS entry, not an outcome object. **Three existing backend substrates the new Artifact layer must aggregate over (reuse, do not duplicate):** 1. **Workspace file registry** — `GET /api/workspaces/:id/files` (`workspaces.ts:594-606`) returns `readFileRegistry(dataDir, id)` of `FileRegistryEntry { name, type, summary, sizeBytes, ingestedAt }` (`routes/ingest.ts:125-130`). This is an **ingest log**, newest-first — closest thing to a per-workspace "produced/ingested things" list, but no id, no status, no relations. 2. **Document version registry** — `GET /api/workspaces/:id/documents` + `/documents/:name/versions` (`routes/documents.ts`, JSON at `~/.waggle/workspaces/{id}/documents.json`, shapes `TrackedDocument`/`DocumentVersion`). Gives versioning + size + createdAt keyed by name; already surfaced in FilesApp's `VersionHistory` (`FilesApp.tsx:27-54`). No type/status/relations. 3. **Workspace storage files** — `GET /api/workspaces/:id/storage/files` + `/storage/read|write|delete` (`workspaces.ts:885+`) — the actual byte store for virtual/local/team. **Verdict:** the screen is **create-new** (`ArtifactCenter` is in PRD §20.3 "Create" list, line 1289). The backend is **a thin net-new aggregation/normalization layer over the three existing stores** — no new data store required (`_inventory/backend-routes.md:604-607`). --- ## 4. Frontend work **New top-level app (dock id `artifacts`).** Register in `Desktop.tsx` `appConfig` + `renderAppContent` switch, add `AppId` `'artifacts'` in `lib/dock-tiers.ts`, and a Work-bucket dock entry (the IA maps Artifacts to the **Work** layer — `_inventory/frontend.md:360`). Do **not** route — this is a windowed single-route desktop; opening is by `AppId` via `openApp` (`useWindowManager`). Components to **create** (keep files small, ~200-400 LOC each per repo file-org rule): - `components/os/apps/ArtifactCenterApp.tsx` — shell: search bar + view toggle + facet rail + table + detail panel + pagination. Owns query/filter/selection state. Mirrors the FilesApp three-pane layout idiom (rail / main / detail) so it feels native. - `components/os/artifacts/ArtifactTable.tsx` (or `ArtifactRow.tsx` — blueprint names `ArtifactRow`, `_blueprint_extracted.txt:487`) — list rows with icon/title/type/workspace/status badge/updated/owner. - `components/os/artifacts/ArtifactFacetRail.tsx` — type/status/workspace/tag facet filters (left rail in mockup). - `components/os/artifacts/ArtifactDetailPanel.tsx` — preview + metadata + related-items list + actions. - `components/os/artifacts/ArtifactRelatedList.tsx` — renders related memories/sessions/tasks/agents; clicking a related item should raise the relevant window via the existing `waggle:open-app` CustomEvent (and/or `onContextRail` like FilesApp does, `FilesApp.tsx:206-208`). **Reuse targets (do not rebuild):** - Status/confidence badges, `Skeleton`, `Table`, view-toggle, `Pagination`, `HoverCard` — all exist in `components/ui/*` (shadcn set, `_inventory/frontend.md:337-342`). - Offline banner pattern + retry — copy from `FilesApp.tsx:384-389`. - File preview for an artifact's backing file — reuse `components/os/files/FilePreview.tsx`. - Empty-state idiom — `FilesApp.tsx:470-477`. - Detail-panel metadata layout idiom — FilesApp Properties dialog (`FilesApp.tsx:667-729`). - ContextRail for "show full context of this artifact" — `overlays/ContextRail.tsx` already exists (extend `ContextRailTarget` with an `'artifact'` variant). **New hook + adapter methods:** - `hooks/useArtifacts.ts` — `{ artifacts, filters, setFilter, selected, select, search, refresh, create, patch, remove, share }`; reads/writes through the adapter. Follow the `useMemory` shape (`hooks/useMemory.ts`). - Extend `lib/adapter.ts` (the single sidecar gateway, ~1930 LOC — new §16 methods land here per `_inventory/frontend.md:380`) with: `getArtifacts`, `getArtifact`, `createArtifact`, `patchArtifact`, `deleteArtifact`, `searchRelatedArtifacts`, `shareArtifact`. **Props/state notes:** `ArtifactCenterApp` takes `{ workspaces?, activeWorkspaceId?, onSelectWorkspace?, onContextRail? }` (same cross-workspace pattern FilesApp uses). It is **cross-workspace by default** (the whole point vs FilesApp) — workspace becomes a *facet*, not a hard scope. --- ## 5. Backend work (PRD §16.6) > No new data store. Every endpoint is a **net-new aggregation/normalization route** over the existing > file registry + document versions + workspace storage. New file: > `packages/server/src/local/routes/artifacts.ts`, registered in `local/index.ts`. The Artifact `id` > can be a stable composite of `workspaceId + source-store + name/path` (or a registry-assigned id if a > lightweight `artifacts.json` index is added per workspace, mirroring `documents.json`). | PRD §16.6 endpoint | Status | Plan (EXTEND vs NET-NEW) + substrate | |---|---|---| | `GET /api/artifacts` | **PARTIAL → NET-NEW route** | No `/api/artifacts` domain. NET-NEW `GET /api/artifacts` in `artifacts.ts` that **fans out over `WorkspaceManager.list()`** and, per workspace, normalizes (a) file registry `GET /api/workspaces/:id/files` (`workspaces.ts:594`, `FileRegistryEntry`), (b) document versions `GET /api/workspaces/:id/documents` (`documents.ts`), into a unified `Artifact[]`. Supports `?workspaceId=&type=&status=&tag=&q=` facet filters. Cross-workspace = the differentiator. | | `POST /api/artifacts` | **PARTIAL → NET-NEW route (thin)** | Closest writes that already persist bytes: `POST /api/ingest` (`ingest.ts`), `POST /api/workspaces/:id/files/upload` (`files.ts`), `POST /api/workspaces/:id/documents` (`documents.ts`), `POST /api/workspaces/:id/storage/write` (`workspaces.ts`). NET-NEW `POST /api/artifacts` records artifact metadata (kind/title/status/tags/relations + `generatedByAgentId`) in a per-workspace `artifacts.json` index and (optionally) writes the backing file via the storage route. | | `GET /api/artifacts/:id` | **MISSING → NET-NEW** | Resolve composite id → normalized `Artifact` with relations + preview metadata. Reuse `documents.ts` version lookup for `relatedVersions`. | | `PATCH /api/artifacts/:id` | **MISSING → NET-NEW** | Update title/status/tags/relations in the `artifacts.json` index (move = re-point `workspaceId`/`storagePath`; reuse `files/move`). | | `DELETE /api/artifacts/:id` | **PARTIAL → NET-NEW route** | Closest: `POST /api/workspaces/:id/files/delete` (`files.ts`), `DELETE /api/workspaces/:id/storage/delete` (`workspaces.ts`). NET-NEW `DELETE /api/artifacts/:id` removes the index entry and (optionally) the backing file via those. | | `GET /api/artifacts/search-related?q=` | **MISSING → NET-NEW (the headline endpoint)** | Federated search: query the normalized artifact index **plus** `GET /api/memory/search` (`memory.ts`), session search `GET /api/workspaces/:wid/sessions/search` (`sessions.ts`), tasks `GET /api/tasks` (`tasks.ts`), and fleet/agents (`GET /api/agents/active`/`/api/fleet`), returning grouped `{ artifacts, memories, sessions, tasks, agents }`. Internally can lean on existing FTS (`memory_frames_fts`, marketplace FTS5, wiki search). Delivers PRD line 532 acceptance. | | `POST /api/artifacts/:id/share` (blueprint `:525`) | **MISSING → NET-NEW** | Blueprint adds a `/share` action not in PRD §16.6 list. Maps to the missing `POST /api/share` (`_inventory/backend-routes.md:551`) + team scope. Defer to the Team phase (see §7); gate behind TEAMS tier like `/api/team/*`. | **Substrate touched:** workspace file registry (`ingest.ts` `FileRegistryEntry`), document versions (`documents.ts` JSON), workspace storage (`workspaces.ts` storage routes), memory FTS (`memory_frames_fts`), sessions JSONL, tasks store, fleet/orchestrator. **No `.mind` migration required** for a metadata-first implementation: artifact metadata + relations live in a per-workspace `artifacts.json` index (same pattern as `documents.json`). If artifacts must later be queryable in SQL alongside frames, a future additive `artifacts` table in `schema.ts` follows the established idempotent ADD-pattern (`mind/db.ts:116-124`) — flag, not now. **Governance note:** if agent-generated artifacts (`generatedByAgentId`) need an audit trail, reuse `InstallAuditStore`/`emitAuditEvent` (`workspaces.ts:631` already emits `workspace_update`) rather than a parallel log. --- ## 6. Shared types needed (PRD §15.2 / §15.6 vs `lib/types.ts`) **Net-new, none exist today** (`_inventory/substrate-types.md:226, 259-266`): - `ArtifactKind` union (PRD §15.2 line 950) — add to `lib/types.ts` (and `packages/shared/src/types.ts` if the sidecar route also imports it, to keep one contract). - `ArtifactStatus = 'draft' | 'final' | 'shared' | 'generated'` (from blueprint states `:312`, PRD §14 artifact states). Note blueprint also implies `external-missing`/`source-unavailable` — model as a derived flag, not a status value. - `Artifact` interface — PRD §15.6 (lines 1039-1056) ∪ blueprint `:547`: `id, title, kind, workspaceId, teamId?, createdBy, generatedByAgentId?, source, status, mimeType?, storagePath?, previewUrl?, summary?, tags[], relatedMemoryIds[], relatedSessionIds[], relatedTaskIds[], relatedAgentIds[], createdAt, updatedAt`. - `RelatedSearchResult` — the grouped `{ artifacts, memories, sessions, tasks, agents }` envelope for `search-related`. **Reconcile, don't fork:** define `Artifact`/`ArtifactKind`/`ArtifactStatus` **once** (shared package preferred) so the sidecar route and the frontend hook share the contract — avoid the existing FE↔BE `MemoryFrame` drift the inventory flags (`_inventory/substrate-types.md:246-247`). Optionally add `relatedArtifactIds[]` to the memory side later (PRD §15.4) so the relation is bidirectional. --- ## 7. Dependencies (screens / phases first) - **PRD Phase 2 — Work layer** (this screen's home; Memory Center is its sibling, Sprint 4). The `search-related` federated endpoint is the binding dependency on Memory (FTS) + Sessions + Tasks. - **AppShell / dock IA (Phase 0/1)** must exist first so `artifacts` registers as a Work-bucket dock entry (consolidate on `AppId`, retire stale `AppView`). - **Command Center (Ctrl+K) (Phase 1)** should index artifacts (`_blueprint_extracted.txt:515` — command index unifies artifacts) — soft dependency; Artifact Center can ship before Ctrl+K wires it in. - **`WorkspaceConfigV2` `type`/`status` fields** (S-workspace cards) help facet labels but are not blocking. - **Team Workspace / RBAC (Phase 5)** gates `POST /api/artifacts/:id/share` + permission-denied state. Ship Artifact Center personal-scoped first; share/team-scope is a follow-on. - **Agent run → artifact linkage** (PRD Journey 7, §15.6 `generatedByAgentId`) depends on the agent runtime writing artifact records — a downstream integration, not a blocker for the read/browse screen. --- ## 8. Effort: **L** Net-new full-stack surface: 5-6 new frontend components + a new hook + 7 new adapter methods, **plus** a net-new backend aggregation domain (`artifacts.ts`, ~6 routes) that must normalize **three** existing stores into one entity and a **federated** search across memory/sessions/tasks/agents. No new DB and heavy component/route reuse keep it out of XL, but the cross-workspace aggregation + the `search-related` federation + a brand-new shared `Artifact` contract make it clearly more than M. --- ## 9. Open questions 1. **Artifact identity & index.** Synthesize `id` as a composite (`workspaceId:store:name`), or add a per-workspace `artifacts.json` index (mirroring `documents.json`) that assigns stable ids and holds status/tags/relations? (PATCH/relations effectively require the latter.) → maps to PRD Open Q6 (storage: workspace FS vs virtual store vs external refs, PRD line 1421). 2. **What counts as an artifact in v1?** Only explicit outputs (generated docs/decks/etc.), or every ingested file in the registry? The registry mixes ingested inputs with produced outputs — need a classification rule for `kind`/`status`. 3. **`search-related` scope/cost.** Federating memory FTS + sessions + tasks + agents per query — cap per-source result counts and run async result groups (PRD §24 perf mitigation), or a single indexed provider? (Mirrors the Command Center search concern.) 4. **Share semantics (blueprint `/share` vs PRD `/api/share`).** Is `POST /api/artifacts/:id/share` in-scope for the first Artifact Center cut, or deferred entirely to the Team phase? 5. **Preview generation.** `previewUrl`/thumbnails — generate server-side, reuse `FilePreview` on-demand client-side, or skip thumbnails in v1 (icon + on-click preview only)? 6. **Bidirectional relations.** Do we add `relatedArtifactIds[]` to memory frames now (PRD §15.4) or keep relations one-directional (artifact → others) in v1?