From e59d3d60e7bb8f802f38da8f47d831aa7335e8e1 Mon Sep 17 00:00:00 2001 From: gsxdsm Date: Mon, 22 Jun 2026 19:18:49 -0700 Subject: [PATCH] FN-6820: document artifact registry workflows Document the user-facing artifact registry workflow from agent tools through dashboard discovery and storage behavior. - Describe agent artifact registration, listing, viewing, and mailbox notification semantics. - Expand the Artifacts dashboard workflow with gallery counts, previews, task links, and loading/error/empty states. - Clarify artifact registry storage, hydration scope, retention, and concept terminology. Files changed: CONCEPTS.md | 2 +- docs/agents.md | 17 ++++++++++++++--- docs/dashboard-guide.md | 16 ++++++++++++---- docs/storage.md | 15 +++++++++++---- 4 files changed, 38 insertions(+), 12 deletions(-) Fusion-Task-Id: FN-6820 Fusion-Task-Lineage: 1d3be0f3-fb4a-4d7e-9470-2110435e83a4 --- CONCEPTS.md | 2 +- docs/agents.md | 17 ++++++++++++++--- docs/dashboard-guide.md | 16 ++++++++++++---- docs/storage.md | 15 +++++++++++---- 4 files changed, 38 insertions(+), 12 deletions(-) diff --git a/CONCEPTS.md b/CONCEPTS.md index a090f749d5..882cfe99c5 100644 --- a/CONCEPTS.md +++ b/CONCEPTS.md @@ -245,7 +245,7 @@ A persisted crash-safe marker (`tasks.transitionPending`) written in the same tr One runtime expansion of a `foreach` template subgraph, bound to a single planned step (`Task.steps[i]`). Identity is deterministic — `#:` — so resume reconstructs the full instance set from the pinned step count without persisting the expansion itself. Each instance carries its own run-state (current node, rework count, baseline/checkpoint, and in worktree mode its branch and integration status) in its own persisted run-state table. The step count is pinned at expansion; a later disagreement with the live step list is a `pin-mismatch` failure, never a silent re-expansion. An instance's lifecycle writes flow through `store.updateStep` so `Task.steps[]` stays the physical projection sink for every existing consumer. ### Artifact -A persisted registry entry produced by agents, workflows, or tasks for reusable deliverables and intermediate products. Artifacts have a type (`document`, `image`, `video`, `audio`, or `other`), author attribution, optional task linkage, metadata such as MIME type/size, and either inline text `content` or a `uri`/path reference for externally stored media. +A persisted registry entry produced by agents, dashboard chat, workflows, or tasks for reusable deliverables and intermediate products. Artifacts have a type (`document`, `image`, `video`, `audio`, or `other`), author attribution, optional task linkage, metadata such as MIME type/size, and either inline text `content` or a `uri`/path reference for stored media. The artifact registry stores metadata for cross-agent discovery, while the dashboard surfaces task-linked and task-less entries in the Artifacts view's **Artifacts** gallery. ### parse-steps A workflow graph node that reads a declared Artifact and runs a registry parser to write the canonical step list (`Task.steps[]`) — the only graph-side writer of steps. Built-in parsers are `step-headings` (the `### Step N:` convention, extracted byte-identically from the legacy regex, including the `(depends: N,M)` annotation) and `json-steps`; plugins contribute parsers under `plugin::`. Parsing failures fail closed to a routable `outcome:parse-error` rather than crashing. A parse-steps node must dominate (precede on all paths) any `foreach(source:"task-steps")`, and running one after a foreach has already expanded trips pin protection (an audited failure) so re-plan loops cannot desynchronize an expanded region. diff --git a/docs/agents.md b/docs/agents.md index 94b99a1937..9b0a73533f 100644 --- a/docs/agents.md +++ b/docs/agents.md @@ -33,7 +33,18 @@ fn chat [message…] [--once] [--non-interactive] [--poll-ms ] - In dashboard model-loop chat (main chat, QuickChat, and room responders), typing `/skill:{name}` requests that skill for the current AI session and strips the slash token from the prompt sent to the model. The requested skill is still subject to the normal enabled/disabled execution-skill filters; CLI-agent-backed PTY chat keeps raw terminal input semantics and does not interpret this command. - Dashboard chat and planning sessions with a scoped task store expose `fn_task_document_write` and `fn_task_document_read`; because neither lane has an ambient task, both tools require an explicit `task_id`. - Agent workflow-routing tools follow an intent boundary: agents may select or change a task workflow only when the user explicitly requested that workflow or when the agent created the task. Executors must not call `fn_workflow_select` to reroute the task they are executing unless the task instructions or a user steering comment explicitly asks for the workflow change. -- Executor, heartbeat, and dashboard chat sessions expose artifact registry tools: `fn_artifact_register` publishes document/image/video/audio/other artifacts with inline `content` or a `uri`, `fn_artifact_list` discovers artifacts across agents/tasks with filters, and `fn_artifact_view` reads metadata plus inline content or URI references. Each successful registration sends a best-effort `system` → dashboard user inbox notification with artifact metadata; notification failures are logged but do not fail the registration. Planning sessions intentionally exclude artifact tools until they can thread the existing `MessageStore` dependency. +- Executor, heartbeat, and dashboard chat sessions expose artifact registry tools so agents can publish and inspect multi-type deliverables without relying on the dashboard gallery. Planning sessions intentionally exclude artifact tools until they can thread the existing `MessageStore` dependency. + +### Artifact registry tools + +Artifact tools operate on the shared artifact registry, so artifacts are visible across agents and tasks when the caller has the artifact ID or can discover it through filters. + +- `fn_artifact_register` registers a `document`, `image`, `video`, `audio`, or `other` artifact with `title`, optional `description`, optional `mimeType`, optional inline text `content`, optional `uri`/path reference, and optional `taskId`. Tool callers should provide either inline `content` or a `uri`/path reference for media stored elsewhere. Executor/heartbeat sessions infer the registering agent as `authorId`; dashboard chat uses the `dashboard-chat` author and requires `task_id` because chat has no ambient task. +- `fn_artifact_list` lists artifacts across agents and tasks with optional `type`, `authorId`, `taskId`, `search`, `limit`, and `offset` filters. Dashboard chat's scoped variant requires `task_id` and otherwise supports `type`, `authorId`, `search`, `limit`, and `offset` for that task. +- `fn_artifact_view` fetches one artifact by `id`, returning registry metadata plus inline `content` when present or the stored `uri`/path reference for media artifacts. +- Successful registration emits a best-effort `system` → `user` inbox notification to `DASHBOARD_USER_ID` with `artifactId`, `artifactType`, `title`, `authorId`, and optional `taskId` metadata. Notification delivery failures are logged and must never fail or roll back the artifact registration. + +For the user-facing gallery and notification UX, see [Artifacts View](./dashboard-guide.md#artifacts-view) and [Mailbox View](./dashboard-guide.md#mailbox-view). For storage layout and hydration semantics, see [Artifact registry](./storage.md#artifact-registry-fn-6777). ### Flags @@ -134,13 +145,13 @@ V1 runtime action categories: The engine classifies tool calls by behavior (not namespace alone): -- `file_write_delete`: built-in `write` / `edit`, plus persistent write helpers like `fn_task_document_write`, `fn_memory_append`, `fn_task_attach` +- `file_write_delete`: built-in `write` / `edit`, plus direct filesystem attach helpers like `fn_task_attach`; low-risk coordination/registration writes such as `fn_task_document_write` and `fn_artifact_register` are handled by the coordination-exempt/read-only allow-lists below rather than this category - `command_execution`: built-in `bash` when not classified as mutating git - `git_write`: mutating git shell commands run via `bash` - `network_api`: external/network-facing tools (for example `fn_research_run`, `fn_research_cancel`, `fn_research_retry`, `fn_web_fetch`) - `task_agent_mutation`: task/agent mutation tools (for example `fn_update_agent_config`, `fn_task_pause`, `fn_spawn_agent`; action-gate task-import/create tools like `fn_task_create`, `fn_delegate_task`, `fn_task_import_github`, and `fn_task_import_github_issue` use this category in action-gate evaluation) - Dashboard permission editors now show per-category example tools sourced from `AGENT_PERMISSION_POLICY_CATEGORY_TOOL_EXAMPLES` in `@fusion/core`, plus a read-only exempt-tools panel for coordination/messaging bypass tools. -- `none`: positively recognized read-only tools (`read`, `grep`, `find`, `ls`, list/show/get-style `fn_*` tools, plus permanent-agent coordination/task-creation helpers like `fn_task_create`, `fn_delegate_task`, `fn_task_import_github`, and `fn_task_import_github_issue`) +- `none`: positively recognized read-only tools (`read`, `grep`, `find`, `ls`, list/show/get-style `fn_*` tools, plus permanent-agent coordination/task-creation helpers like `fn_task_create`, `fn_delegate_task`, `fn_task_import_github`, and `fn_task_import_github_issue`). Artifact tools mirror `fn_task_document_write` in the shipped allow-lists: `fn_artifact_register`, `fn_artifact_list`, and `fn_artifact_view` are present in `READONLY_FN_TOOLS` and `COORDINATION_EXEMPT_TOOLS`, so registration is treated as coordination/registry publication instead of a broad mutation approval. `bash` git-write heuristic in v1: diff --git a/docs/dashboard-guide.md b/docs/dashboard-guide.md index efe056095d..8e8d294e24 100644 --- a/docs/dashboard-guide.md +++ b/docs/dashboard-guide.md @@ -397,6 +397,7 @@ Mailbox view shows inbox/outbox communication threads and unread state. - Inbox renders one row per message (no sender-based collapsing) - clicking a message in the Mail tab opens the task detail pane with full message content and conversation context - reply rows in the mailbox modal can expand inline to show the replied-to message context for easier thread reading +- when an agent or dashboard chat session registers an artifact with `fn_artifact_register`, Fusion sends a best-effort `system` → user inbox message announcing the new artifact (for example, `New image artifact registered: `) with metadata for `artifactId`, `artifactType`, `title`, `authorId`, and optional `taskId`; notification delivery is informational and never blocks or rolls back the artifact registration - mailbox now includes an **Approvals** tab with pending and history filters (`approved` / `denied` / `completed`), approval detail context, and inline approve/deny actions for pending requests - in the **Agents** tab, the agent selector now includes **All agents**, which shows one combined agent-to-agent stream (with sender + recipient labels); selecting a specific agent still shows Inbox/Outbox subtabs - mailbox entry points now show unread/pending indicators: the desktop/tablet Header mailbox toggle shows a pending-approval dot first or an unread dot when unread mail exists without pending approvals, the mobile bottom-nav Mailbox tab carries the mobile badges/dots, and the compact Header actions overflow keeps a Mailbox entry only when the mobile bottom nav is disabled @@ -552,21 +553,28 @@ For per-run aggregation, `GET /api/agents/:id/runs/:runId/cited-goals` returns ` ## Artifacts View -Artifacts view aggregates task documents, project markdown files, and registered artifacts. +Artifacts view aggregates project markdown files, task documents, and registered artifacts. The dashboard title is **Artifacts**; the internal tab bar keeps the shipped **Project Files**, **Task Documents**, and **Artifacts** labels. Features: - Group task documents by task ID (with revision history metadata) - Search documents across tasks - Open project markdown files with inline preview -- Browse the **Artifacts** tab for media registered by agents, users, or the system across tasks +- Browse the **Artifacts** tab for registry media registered by any agent, dashboard chat/user action, or system tool across tasks +- Use the tab-count badges to see the current counts for Project Files, Task Documents, and Artifacts; the Artifacts badge reflects the loaded `GET /api/artifacts` result set, including active search filters - Use the responsive media gallery to scan thumbnail-first image and video cards with consistent framing, while audio, document, and generic artifacts remain readable cards in the same grid - Expand image and video artifact thumbnails into a full-size lightbox; dismiss it with the close button, backdrop click, or Escape while non-previewable artifact cards keep their normal controls and links -- Preview artifact images inline, play video and audio with native controls, read document previews, and open generic artifacts through their media URL -- Jump directly from a document group or artifact card to the owning task detail modal when a task is linked; inside task detail, the **Artifacts** tab shows that task's documents and registered media artifacts together +- Preview artifact images inline, play video and audio with native controls, read document previews from inline content/description, and open generic `other` artifacts through their media URL (`GET /api/artifacts/:id/media`) +- Read artifact metadata on each card: type badge (`Image`, `Video`, `Audio`, `Document`, or `Other`), title, optional description/content preview, author ID, timestamp, and linked task title/ID when present +- Use **Open task** on an artifact card to jump back to the originating task when the artifact has a `taskId`; inside task detail, the **Artifacts** tab shows that task's documents and registered media artifacts together +- Loading state: the Artifacts tab shows `Loading artifacts…` while the first artifact list request is pending and no artifact results are loaded +- Empty states: with no search query it shows `No artifacts yet.` plus the hint that artifacts are created by agents, users, and system tools; with a search query it shows `No artifacts match "<query>".` +- Error state: a failed artifact list request uses the shared `Failed to load artifacts: <error>` panel with a **Retry** action that re-runs the artifact fetch - Toggle between raw text and rendered markdown using the **Markdown/Plain** button - Highlight text in raw or rendered project-file previews, choose **Add comment**, and send the file path, selected snippet, and your comment to the **New Task** dialog +Agent registrations also surface through the [Mailbox View](#mailbox-view): successful `fn_artifact_register` calls send a best-effort system inbox notification so users can discover new media even before opening the gallery. + ![Artifacts view](./screenshots/documents-view.png) ## Reports View diff --git a/docs/storage.md b/docs/storage.md index 81ad4f925c..4d062b9955 100644 --- a/docs/storage.md +++ b/docs/storage.md @@ -65,11 +65,15 @@ ### Artifact registry (FN-6777) -- `artifacts` is the first-class metadata registry for generated or uploaded task artifacts. Rows store title/description, media type, author identity, optional task linkage, metadata JSON, textual content, a relative URI, and size; binary bytes are not stored in SQLite. +- `artifacts` is the first-class metadata registry for generated or uploaded task artifacts. Rows store ID, `type` (`document`, `image`, `video`, `audio`, or `other`), title/description, MIME type, size, author identity/type, optional task linkage, metadata JSON, textual `content`, a relative `uri`, and timestamps; binary bytes are not stored in SQLite. - `TaskStore.registerArtifact()` writes task-scoped binary payloads under `<rootDir>/.fusion/tasks/{ID}/artifacts/` and task-less registry payloads under `<rootDir>/.fusion/artifacts/`, then records a relative `artifacts/<file>` URI in SQLite. If the DB insert fails after a binary write, the store removes the orphaned file before surfacing the error. +- Inline text/document artifacts may store `content` directly in SQLite and therefore have no media file. The dashboard media route streams `GET /api/artifacts/:id/media` from disk when `uri` is present, or returns inline `content` with the persisted MIME type when no `uri` exists. - `getArtifact(id)` returns metadata by ID, `getArtifacts(taskId)` returns active-task artifacts newest-first, and `listArtifacts(...)` is the cross-agent query path with type/author/task/search filters and pagination. List reads hide artifacts whose parent task is soft-deleted while preserving task-less artifacts. - Task-linked artifact registration requires an active, non-archived task. Archived tasks are read-only for artifact writes; soft-deleted or missing tasks are rejected. -- Worktree DB hydration copies artifact metadata so isolated agents can query the registry shape locally, but binary payload files remain in the source project storage. +- Retention follows the existing task lifecycle rather than a separate artifact policy: soft-deleted parent tasks keep artifact rows/files for forensics but normal live-reader APIs hide them; hard deletion from the active `tasks` table cascades artifact metadata through the `taskId` foreign key, and archive cleanup removes the task directory that contains task-scoped artifact binaries. Task-less artifacts live under `<rootDir>/.fusion/artifacts/` and are not tied to task archival cleanup. +- Worktree DB hydration copies task-scoped artifact metadata for the current task/dependency graph alongside task rows and `task_documents`. It intentionally does not copy binary payload files, and it intentionally excludes task-less registry artifacts because dependency hydration is scoped to the active task graph. + +Agent-facing registration tools are documented in [Artifact registry tools](./agents.md#artifact-registry-tools), and the dashboard browsing surface is documented in [Artifacts View](./dashboard-guide.md#artifacts-view). ### Task-ID integrity detection @@ -426,6 +430,7 @@ The `tasks.cumulativeActiveMs` and `tasks.executionCompletedAt` columns are the | `secrets` | Encrypted secret KV rows (`key` unique) with raw BLOB `value_ciphertext` + per-row random `nonce` (AES-256-GCM), per-secret `access_policy` CHECK (`auto`/`prompt`/`deny`), env-materialization metadata (`env_exportable`, `env_export_key`), and read-audit fields (`last_read_at`, `last_read_by`). Plaintext is never written to the database. | | `task_documents` | Task-scoped document metadata/content keyed by `(taskId, key)` with current revision pointer. | | `task_document_revisions` | Immutable revision history for task documents (content snapshots by revision). | +| `artifacts` | Artifact registry metadata for inline text and on-disk media artifacts. Stores type/title/description, MIME type/size, author identity, optional task linkage, metadata JSON, inline `content`, relative `uri`, and timestamps; binary media bytes live under task or registry `artifacts/` directories instead of SQLite. | | `__meta` | Schema version + monotonic `lastModified` change detector, plus one-time bootstrap metadata such as `bootstrappedAt` and `projectIdentity`. | | `goals` | Strategic intent records (`title`, optional `description`, `status`, timestamps) that can outlive mission timelines. | | `mission_goals` | Many-to-many join between missions and goals with composite PK `(missionId, goalId)`, `createdAt`, and cascade-delete foreign keys to both parents. | @@ -627,7 +632,8 @@ Fusion now auto-hydrates the worktree DB during executor startup at three points Hydration copies only: - current task row, - transitive dependency task rows (BFS, depth cap 5, max 50 unique task IDs), -- `task_documents` rows for that same task-id set. +- `task_documents` rows for that same task-id set, +- task-scoped `artifacts` metadata rows for that same task-id set. Implementation uses in-process SQLite streaming (`DatabaseSync`), source-side `SELECT`, destination-side `INSERT OR REPLACE` inside a destination transaction. Column lists are built from source/destination schema intersection (`PRAGMA table_info`), so schema drift degrades gracefully (dropped columns are logged once, and defaults apply on destination-only columns). @@ -636,12 +642,13 @@ Example shape of the destination write: ```sql INSERT OR REPLACE INTO tasks (<shared-columns...>) VALUES (<placeholders...>); INSERT OR REPLACE INTO task_documents (<shared-columns...>) VALUES (<placeholders...>); +INSERT OR REPLACE INTO artifacts (<shared-columns...>) VALUES (<placeholders...>); ``` Expected executor log entry on success: ```text -Hydrated worktree DB: 4 tasks, 12 task_documents +Hydrated worktree DB: 4 tasks, 12 task_documents, 3 artifacts ``` A concrete recovered failure mode now covered by tests: when a worktree directory exists but its local `.fusion/` scratch state is missing, opening `DatabaseSync(<worktree>/.fusion/fusion.db)` can fail with `unable to open database file`. Hydration now performs destination bootstrap (`mkdir -p .fusion` + schema init) and retries the destination open once before degrading.