Merge branch 'main' into feature/readme-mobile
This commit is contained in:
37
.changeset/README.md
Normal file
37
.changeset/README.md
Normal file
@@ -0,0 +1,37 @@
|
||||
# Changeset Format Guide
|
||||
|
||||
Each changeset file in this directory describes one user-facing change for release notes.
|
||||
|
||||
## Required body format
|
||||
|
||||
```
|
||||
---
|
||||
"@runfusion/fusion": minor
|
||||
---
|
||||
|
||||
summary: Add a Command Center productivity control for LOC backfills.
|
||||
category: feature
|
||||
dev: Uses the new `fn_backfill_loc` tool; settings key `commandCenter.locBackfill`.
|
||||
```
|
||||
|
||||
## Fields
|
||||
|
||||
| Field | Required | Description |
|
||||
|-------|----------|-------------|
|
||||
| `summary` | Yes | One line, user-facing, max 120 chars. Describe what changed for the operator. |
|
||||
| `category` | Yes | One of: `feature`, `fix`, `breaking`, `security`, `performance`, `internal`. |
|
||||
| `dev` | No | Developer or migration detail. Preserved in per-package CHANGELOGs but excluded from distilled release notes. |
|
||||
|
||||
## Audience
|
||||
|
||||
The `summary` is the only content that appears in end-user release notes by default. Write for Fusion operators — describe behavior, fixes, and what changed. Avoid internal class names, file paths, and implementation detail.
|
||||
|
||||
## Bump types
|
||||
|
||||
- `patch` — bug fixes, internal changes
|
||||
- `minor` — new features, CLI additions, tools
|
||||
- `major` — breaking changes
|
||||
|
||||
## Validation
|
||||
|
||||
Run `pnpm check:changesets` to validate. The linter runs in the PR-check gate and `test:gate`. Legacy freeform changesets pass with a warning during the transition period.
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"@runfusion/fusion": patch
|
||||
---
|
||||
|
||||
Auto-continue the agent session after an engine-internal pause/resume abort instead of re-queueing the task to todo. When the engine tears down in-flight work (hard-cancel) and the workflow graph run ends with the task back in `todo`, the executor now retries the agent session in place — bounded by the existing graph-resume retry budget with backoff, falling back to a benign re-queue only after retries are exhausted. Before re-dispatching, it re-checks the task at fire time and aborts the auto-continue if the task was paused, moved, or deleted during the backoff window, so genuine user/global/task pauses are never resumed against the operator's intent. The transient reclassification clears any stale `failed` status and emits an `Auto-recovered:` log so no spurious failure notification fires.
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"@runfusion/fusion": patch
|
||||
---
|
||||
|
||||
Fix a false "engine not running" banner when another fusion process on the same machine already owns the engine. The dashboard's health check only counted engines this process started, so a second launch (e.g. `pnpm dev dashboard` alongside an already-running `fusion`) that was correctly refused the per-machine engine singleton lock reported the engine as unavailable — even though one was running. The `ProjectEngineManager` now tracks engines owned by another process (detected via `EngineAlreadyRunningError` from the singleton lock) and exposes `hasRunningEngine()`, which the dashboard health endpoint uses so the banner reflects machine-level truth. Reconciliation still retries so this process takes over if the other exits, and the "refusing to start" log is emitted once per project instead of on every reconciliation tick.
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"@runfusion/fusion": minor
|
||||
---
|
||||
|
||||
Add the `factory-mono` dashboard color theme, a monochrome Factory variant with red accents and neutralized glow effects.
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"@runfusion/fusion": patch
|
||||
---
|
||||
|
||||
Keep Fusion verification progress moving by making targeted script tests honor file arguments, reaping verification subprocess groups after clean exits, and preventing the line-count audit from blocking `pnpm test`. The changed-test runner now caps reverse-dependent fan-out so a foundational-package edit no longer expands into a whole-workspace run, and the executor/verification guidance now directs agents to scope verification to changed files rather than running the full workspace test suite.
|
||||
@@ -1,7 +0,0 @@
|
||||
---
|
||||
"@runfusion/fusion": patch
|
||||
---
|
||||
|
||||
Fix anthropic-compatible custom providers failing with "No API provider registered for api: anthropic".
|
||||
|
||||
`resolveCustomProviderApiType` mapped the `anthropic-compatible` provider type to the api key `"anthropic"`, but pi-ai registers the Anthropic Messages API under `"anthropic-messages"`. Any custom provider configured as `anthropic-compatible` (self-hosted Claude proxy, gateway, etc.) therefore selected a model whose `api` did not match a registered provider and threw at stream time. Mapped it to `"anthropic-messages"` and added a regression assertion alongside the existing openai-compatible / openai-responses coverage.
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"@runfusion/fusion": patch
|
||||
---
|
||||
|
||||
Clear the stale `failed` status when a pause/resume abort is reclassified as a benign todo re-queue, so the task no longer surfaces as failed on the board and the deferred failure notification is suppressed. Previously a pause-abort parked `status:"failed"` on an earlier non-todo observation stayed dispatchable (the scheduler filters on column+paused, not status), re-entered the benign-todo branch, and was logged benign while the row stayed failed — firing a contradictory failure alert during global pause when self-healing recovery was suppressed. The clear path also emits an `Auto-recovered:`-prefixed log so the notification service proactively cancels the pending failure timer instead of relying only on the fire-time re-check.
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"@runfusion/fusion": patch
|
||||
---
|
||||
|
||||
Make the compound-engineering built-in workflow actually load skills and run the full CE flow. Previously the workflow named CE skills at each node but the graph-node execution path (`runGraphCustomNode`) never loaded them: the named skill was only injected as prompt text, the plugin-injected `FUSION_CE_*` runtime env never reached the step session, and `fn_spawn_agent` was never registered for workflow steps, so persona fan-out and skill loading silently no-op'd. Now skill-executor graph steps thread the injected env, load the named skill (discovery + selection via `additionalSkillPaths`), register the spawn tool in coding mode, and receive an engine-injected Fusion workflow-step conventions preamble (await-input for questions, `FUSION_HEADLESS` degrade path, persona fan-out via `systemPromptOverride`). Adds an explicit `unattended` opt-in for `FUSION_HEADLESS`, reconciles the preamble with the gate verdict-JSON contract, and carries `skillName` through the `WorkflowStep` round-trip.
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"@runfusion/fusion": patch
|
||||
---
|
||||
|
||||
Fix the persistent non-blocking Full Suite failure caused by the Compound Engineering plugin's `dist-freshness.test.ts`. The test reads the plugin's compiled `dist/settings.js` and `dist/session/orchestrator.js`, but the plugin had no `pretest` build and was absent from `ensure-test-artifacts.mjs`, so on a fresh checkout `dist/` did not exist and the freshness guard threw "dist/ is missing — run pnpm build first". Register the plugin's required artifacts in `ensure-test-artifacts.mjs` and add a `pretest` hook that builds them, matching the other bundled plugins.
|
||||
@@ -1,9 +0,0 @@
|
||||
---
|
||||
"@runfusion/fusion": patch
|
||||
---
|
||||
|
||||
Fix the Droid runtime model discovery spawning a runaway storm of leaked `droid` processes.
|
||||
|
||||
`discoverDroidModels` invoked `droid models --json` / `droid model list --json`, but the droid CLI has no such commands — an unknown subcommand is parsed as a *prompt*, so each call launched a full agent session (a persistent `droid exec --stream-jsonrpc` backend) that never exited. The promise never settled and the process leaked; because the dashboard re-loads the droid extension on every chat-send, these piled up into dozens of orphaned `droid` processes.
|
||||
|
||||
Discovery now reads the catalog from `droid exec --help` (which lists `Available Models:` + `Custom Models:` and exits cleanly), parsed via the new `parseDroidModelsFromHelp` helper. A SIGKILL-on-timeout guard (`DROID_MODEL_DISCOVERY_TIMEOUT_MS`) ensures any wedged spawn is killed and the promise always settles, so a single discovery call can never leak a process again. Verified end-to-end against the real binary (46 models incl. custom, 0 leaked processes).
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"@runfusion/fusion": patch
|
||||
---
|
||||
|
||||
Show plugin-contributed skills (e.g. compound-engineering `ce-*`) in the workflow editor. The dashboard's discovered-skills catalog was built only from the disk-scanning package manager, so plugin skills — which the engine materializes for executor sessions separately — never appeared, and built-in workflow nodes that reference them (like `builtin:compound-engineering`) showed "— select skill —" / unresolved. The skills adapter now merges plugin skill contributions into the discovered list (deduped by bare name), and the editor's node summary + skill dropdown match namespaced skillNames (`compound-engineering:ce-work`) against the catalog's two-segment names (`ce-work/SKILL.md`) via a shared bare-name normalizer.
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"@runfusion/fusion": patch
|
||||
---
|
||||
|
||||
Fix loading spinners that didn't spin across the dashboard. Many loading states (Settings, task tabs, agents, documents, plugins, model pickers, command center, and more) rendered bare "Loading…" text with no spinner — and a couple rendered an unstyled `loading-spinner` div that never showed anything. Added a shared `<LoadingSpinner>` component (self-contained animated SVG, no `lucide-react` dependency so it survives partial test mocks) and adopted it across ~45 loading placeholders so every loading state now shows a consistent animated spinner.
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"@runfusion/fusion": patch
|
||||
---
|
||||
|
||||
Fix macOS system memory usage reporting by deriving host memory used from OS-available memory instead of raw `os.freemem()` pages.
|
||||
@@ -1,10 +0,0 @@
|
||||
---
|
||||
"@runfusion/fusion": patch
|
||||
---
|
||||
|
||||
Fix worktree-creation failures (and the `Workflow graph terminated with failure at node 'execute'` they surface as) caused by leaked orphan worktree directories.
|
||||
|
||||
A directory under `.worktrees/` that survives with a *dangling* `.git` pointer — present on disk, but the `.git/worktrees/<name>` admin entry it references is gone — is invisible to `git worktree list` and untouched by `git worktree prune`, yet collides with a freshly generated worktree name. When the executor then tries to clean up the "conflict", `git worktree remove --force` fails with `is not a working tree` and the whole `execute` node fails after 3 attempts.
|
||||
|
||||
- **On-demand recovery (`executor.ts`):** the FN-4813 stale-conflict recovery now also treats `is not a working tree` and `ENOENT` (not just `validation failed, cannot remove working tree`) as "no live worktree at this path" — it prunes any admin entry, force-removes the leftover directory, and proceeds with fresh worktree creation instead of failing.
|
||||
- **Leak prevention (`worktree-pool.ts`):** `reapOrphanWorktrees` previously skipped any dir on the mere *presence* of a `.git` file ("may be partially registered"), contradicting its own documented invariant. It now resolves the `.git` pointer and only skips when the gitdir target actually exists; a dangling pointer is reaped like any other half-initialized orphan, so these directories no longer accumulate across runs.
|
||||
@@ -1,9 +0,0 @@
|
||||
---
|
||||
"@runfusion/fusion": patch
|
||||
---
|
||||
|
||||
Fix the global pause/resume failure mode that stalled the board: a pause-abort that left a task back in `todo` was parked `status:"failed"` ("operator action required") and leaked its in-memory worktree slot, producing an instant re-fail retry storm and concurrency-starving the whole queue.
|
||||
|
||||
- Root cause: `handleGraphFailure` now treats a pause-abort that has re-queued a task to `todo` as benign (FN-6782) — it no longer parks it failed, clears the `pausedAborted` marker so the next dispatch starts clean, and releases the leaked worktree slot.
|
||||
- Auto-recovery: a new `recoverPausedAbortFailures` self-healing sweep clears any pause-abort park (`status:"failed"` with "operator action required") still on the board and requeues it for normal scheduling, so the board self-heals without operator intervention.
|
||||
- Defense-in-depth: a new `reapLeakedConcurrencySlots` self-healing sweep reclaims any in-memory worktree slot whose holder is no longer in-progress (the "in todo yet still a `maxWorktrees` holder" leak), gated by the executor's live-session refusal so it can never pull a worktree out from under a running agent. This recovers a leaked slot from any future/unknown path without an engine restart.
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"@runfusion/fusion": patch
|
||||
---
|
||||
|
||||
Stop edits to `scripts/lib/test-quarantine.json` from forcing `pnpm test` into gate mode. The quarantine list is runtime data, not executable test infra; tripping the shared-infra catch-all dropped affected-package coverage, so a dev's real changes went untested whenever they also touched the quarantine list. Quarantine edits now stay in changed mode and run the affected packages.
|
||||
@@ -1,9 +0,0 @@
|
||||
---
|
||||
"@fusion/core": patch
|
||||
---
|
||||
|
||||
Fix `reconcileOrphanedTaskDirs` silently resurrecting long-deleted tasks onto the live board after a restart ("all task IDs reset / starting over").
|
||||
|
||||
The sweep re-imports `.fusion/tasks/<id>/` directories that have no DB row, to recover heartbeat-created dirs that race store init or rows lost to a recent DB corruption. But it didn't distinguish a genuinely-recent orphan from an ancient deleted-task dir that merely lingered on disk. Modern deletes leave a soft-delete tombstone (caught by `taskIdExistsAnywhere`), but legacy hard-deletes left no tombstone — so a months-old `task.json` with no DB row was re-imported as a live task, surfacing old low-numbered IDs (FN-001, FN-002, …) at the top of the board.
|
||||
|
||||
Reconcile now gates recovery on a recency window (`task.json` modified within the last 7 days). Older orphan dirs are skipped with reason `stale-orphan-dir-beyond-recency-window` and left for explicit recovery (unarchive/restore) or directory cleanup, while heartbeat-race and recent-corruption recovery still work.
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"@runfusion/fusion": patch
|
||||
---
|
||||
|
||||
Fix the task detail chat always showing "No agent is working on this task" for in-progress tasks. The active-session check required a persistent `assignedAgentId`/`checkedOutBy`, but in the default ephemeral-agents mode the scheduler never sets those fields, so an actively-executing task always read as idle. An assignment is now sufficient-but-not-necessary: a non-blocked, non-`queued` in-progress task counts as a live agent session on its own (`queued` stays assignment-gated, in-review is unchanged).
|
||||
@@ -1,11 +0,0 @@
|
||||
---
|
||||
"@runfusion/fusion": patch
|
||||
"@fusion/core": patch
|
||||
---
|
||||
|
||||
Harden the orphan-worktree and stale-task-dir cleanup fixes (code-review follow-up).
|
||||
|
||||
- **executor.ts (P0):** the stale-conflict recovery's `rm(worktreePath, { recursive, force })` had no bounds check. `worktreePath` can originate from a git worktree admin entry that resolves outside `.worktrees/`, so an out-of-bounds or symlinked path could be force-removed. The recovery now refuses unless the path is inside the configured worktrees dir, is not a symlink (checked via `realpathSync`), is not a registered git worktree, and is not actively owned — and it re-verifies liveness inside the catch rather than trusting the error string. It also excludes `spawn` failures (e.g. `spawn git ENOENT` when git is missing) so a missing-binary error is no longer misread as a successful stale-path cleanup.
|
||||
- **worktree-pool.ts:** `resolveGitdirPointer` is replaced by `dotGitPointerIsDangling`, which reaps **only** when a `.git` link's gitdir target is confirmed missing. A real `.git` directory, an unparseable pointer, or any read/stat failure now returns "not dangling" (conservative) so a transient read error on a live worktree's `.git` can't cause a force-remove. Removes the `string | "directory" | null` sentinel union.
|
||||
- **core store.ts:** the `reconcileOrphanedTaskDirs` recency window is now bypassed when the live task table is empty (the corruption/restore case — surviving `task.json` files keep old mtimes), and when a corrupt `fusion.db` was auto-recovered on startup, so `.recover` row loss is not stranded by the gate. Adds an `ignoreRecencyWindow` option for explicit callers.
|
||||
- Tests for all of the above: executor recovery + out-of-bounds refusal, unparseable `.git` skip, recency boundary, empty-DB/forced bypass.
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"@runfusion/fusion": patch
|
||||
---
|
||||
|
||||
Track real plugin activation events and surface project-scoped Command Center plugin activation analytics instead of placeholder ecosystem counts.
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"@runfusion/fusion": minor
|
||||
---
|
||||
|
||||
Add the `fn_agent_set_instructions` extension tool so managing agents can update direct or indirect reports' inline or file-backed instructions with org-hierarchy authorization.
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"@runfusion/fusion": patch
|
||||
---
|
||||
|
||||
Move the Command Center Overview SDLC throughput funnel to the bottom of the tab and broaden hand-rolled chart primitive colors to cycle through existing semantic theme tokens.
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"@runfusion/fusion": minor
|
||||
---
|
||||
|
||||
Add a Command Center GitHub resolved-issues detail list and expose the resolved issue rows in the GitHub analytics endpoint payload and CSV export.
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"@runfusion/fusion": patch
|
||||
---
|
||||
|
||||
Fix Command Center Activity trend charts so mixed-unit agent/activity series stay visually legible instead of being flattened by high-volume message counts.
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"@runfusion/fusion": patch
|
||||
---
|
||||
|
||||
Recover in-progress tasks wedged behind stale in-memory executor bindings by clearing the phantom binding and requeueing with progress and worktree preserved.
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"@runfusion/fusion": patch
|
||||
---
|
||||
|
||||
Fix terminal shortcut focus preservation so on-screen Ctrl combinations emit control bytes reliably on touch and pointer devices while keeping physical Ctrl behavior intact.
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"@runfusion/fusion": minor
|
||||
---
|
||||
|
||||
Add the `xhigh` reasoning effort level to model settings and task/agent selectors. Claude CLI adapters pass the value through to runtime mapping, where non-Opus models use `high` effort and Opus models use `max` effort.
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"@runfusion/fusion": patch
|
||||
---
|
||||
|
||||
Rebaseline the dashboard i18n lint guardrail by excluding non-shipping tests and stories, suppressing technical token categories, localizing plugin missing-view copy, and tracking remaining source-copy deferrals with narrow follow-up tasks.
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"@runfusion/fusion": minor
|
||||
---
|
||||
|
||||
Add a built-in lead-generation workflow with custom lead columns, fields, and stage prompts.
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"@runfusion/fusion": minor
|
||||
---
|
||||
|
||||
Add a built-in Design workflow that gates UI-heavy work with a design/UX review before standard review and merge.
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"@runfusion/fusion": minor
|
||||
---
|
||||
|
||||
Add a built-in Marketing workflow with content-specific columns and prompts for brief, drafting, editorial review, and publishing.
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"@runfusion/fusion": patch
|
||||
---
|
||||
|
||||
Fix mobile bottom tab navigation icon spacing so every tab uses an equal-width column across optional tabs, badges, and status dots.
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"@runfusion/fusion": patch
|
||||
---
|
||||
|
||||
Make the Agents view sidebar wider by default on tablet and resizable with per-project persistence on non-mobile layouts.
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"@runfusion/fusion": patch
|
||||
---
|
||||
|
||||
Localize remaining plugin, agent, mission, node, research, document, activity, and miscellaneous dashboard strings and remove their i18n lint deferrals.
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"@runfusion/fusion": patch
|
||||
---
|
||||
|
||||
Localized the dashboard workflow/task/setup/PR component cluster and removed the obsolete i18n lint deferrals for those files so the hardcoded-string guardrail scans them again.
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"@runfusion/fusion": patch
|
||||
---
|
||||
|
||||
Keep settings section dashboard copy covered by i18n lint by removing the settings/sections deferral and regenerating i18n resource types.
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"@runfusion/fusion": patch
|
||||
---
|
||||
|
||||
Eliminate the legacy board flash before workflow lanes load by caching per-project board workflow metadata and showing a neutral skeleton while metadata resolves.
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"@runfusion/fusion": minor
|
||||
---
|
||||
|
||||
Add a core artifact registry data model and store APIs for persisted artifact metadata with on-disk binary storage.
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"@runfusion/fusion": minor
|
||||
---
|
||||
|
||||
Add dashboard artifact registry read APIs, client helpers, and a Documents-view Artifacts media gallery for images, videos, audio, documents, and generic artifacts.
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"@runfusion/fusion": patch
|
||||
---
|
||||
|
||||
Keep Command Center inline next to Agents across desktop and tablet header widths instead of moving it into the More views overflow menu.
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"@runfusion/fusion": patch
|
||||
---
|
||||
|
||||
Repair task-store startup and self-healing consistency by non-destructively re-importing orphaned live `.fusion/tasks/{ID}/task.json` records into the SQLite task index while preserving soft-deleted, archived, and tombstoned IDs.
|
||||
@@ -1,7 +0,0 @@
|
||||
---
|
||||
"@runfusion/fusion": patch
|
||||
---
|
||||
|
||||
Fix dependency gating so workflow-graph and workflow-authoritative executor dispatches re-check unmet task dependencies before running, requeueing blocked work with `blockedBy` instead of allowing it to advance to review.
|
||||
|
||||
Add self-healing reconciliation for already-advanced `in-review` tasks with unmet dependencies, including the `task:reconcile-in-review-unmet-dependencies` run-audit event and guarded no-action companion.
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"@runfusion/fusion": patch
|
||||
---
|
||||
|
||||
Recover benign in-review pause/resume abort parks without requiring operator intervention while preserving hard-cancel, pause, and terminal merge safeguards.
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"@runfusion/fusion": patch
|
||||
---
|
||||
|
||||
Harden in-review dependency drift reconciliation so guard-held or failed rebounds emit no-action audit evidence instead of silently wedging dependent tasks.
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"@runfusion/fusion": patch
|
||||
---
|
||||
|
||||
Fix mobile bottom navigation icon alignment so unread indicators use a centered token-sized icon slot without visually skewing tab spacing.
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"@runfusion/fusion": patch
|
||||
---
|
||||
|
||||
Prevent bundled Droid and Claude CLI auth/presence probes from surfacing unhandled promise rejections when `spawn` throws synchronously, such as when test guards block real AI CLI auth commands. These probes now resolve as unavailable/unauthenticated instead of rejecting from fire-and-forget validation paths.
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"@runfusion/fusion": minor
|
||||
---
|
||||
|
||||
Add a Shadcn Custom dashboard theme with persisted, sanitized design-token color picker overrides across Settings and Command Center theme selectors.
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"@runfusion/fusion": patch
|
||||
---
|
||||
|
||||
Fix the experimental left sidebar Settings button so it remains clear of the fixed executor status footer, and keep project-selector fallback labels readable when translations are incomplete.
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"@runfusion/fusion": patch
|
||||
---
|
||||
|
||||
Stop triage and planning prompts from auto-selecting alternate workflows based on task type; agents now preserve the project default workflow unless the user explicitly requests a specific workflow.
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"@runfusion/fusion": patch
|
||||
---
|
||||
|
||||
Await CLI extension cached TaskStore shutdown so deferred filesystem writes and SQLite handles drain before fixture or process cleanup.
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"@runfusion/fusion": patch
|
||||
---
|
||||
|
||||
Bump the internal @earendil-works pi SDK family from ^0.79.1 to ^0.79.9 for the CLI, dashboard, and engine packages.
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"@runfusion/fusion": patch
|
||||
---
|
||||
|
||||
Fix dashboard toast text colors so Shadcn dark-mode success, info, and error notifications remain readable against their themed backgrounds.
|
||||
@@ -1,7 +0,0 @@
|
||||
---
|
||||
"@runfusion/fusion": minor
|
||||
---
|
||||
|
||||
Add pricing entries for OpenAI Codex models used through the `openai-codex` provider, so Command Center token analytics can estimate costs for Codex runs instead of showing them as unavailable.
|
||||
|
||||
This is marked minor because it expands the set of priced models surfaced by the published CLI/dashboard without changing existing pricing behavior.
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"@runfusion/fusion": minor
|
||||
---
|
||||
|
||||
Allow users to manually pause and unpause agent-assigned tasks from the dashboard task detail view and API.
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"@runfusion/fusion": patch
|
||||
---
|
||||
|
||||
Ensure bundled Droid CLI provider startup registers without waiting for local `droid` probes and harden binary probes so missing, guarded, or hanging spawns resolve to unavailable sentinels instead of delaying engine boot.
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"@runfusion/fusion": minor
|
||||
---
|
||||
|
||||
Move Stash Recovery into the Git Manager Recovery tab and remove the standalone top-level Stash Recovery view from dashboard navigation.
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"@runfusion/fusion": minor
|
||||
---
|
||||
|
||||
Move desktop toolbar tools into the right sidebar tools rail. The right dock now hosts Activity, Activity Log, Import from GitHub, Git Manager, Files, and Automation, and no longer duplicates left-sidebar content views.
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"@runfusion/fusion": patch
|
||||
---
|
||||
|
||||
Move Planning Mode into the dashboard sidebar as a first-class embedded view while removing the desktop toolbar affordance.
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"@runfusion/fusion": minor
|
||||
---
|
||||
|
||||
Move the dashboard terminal launcher to the footer executor status bar and add docked plus floating resizable terminal modes on desktop/tablet while preserving mobile fullscreen terminal behavior.
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"@runfusion/fusion": patch
|
||||
---
|
||||
|
||||
Update the built-in compound-engineering workflow so its Review stage runs the `compound-engineering:ce-code-review` skill directly. The redundant generic reviewer seam node was removed, leaving the CE code-review gate as the sole review stage.
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"@runfusion/fusion": minor
|
||||
---
|
||||
|
||||
Make the dashboard right dock persistent by default with an in-dock collapse toggle, and remove duplicate Header right-dock toggle behavior.
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"@runfusion/fusion": patch
|
||||
---
|
||||
|
||||
Tasks created from a selected non-default workflow lane now appear on that lane immediately instead of vanishing until the board-workflows metadata refetch catches up.
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"@runfusion/fusion": patch
|
||||
---
|
||||
|
||||
Built-in compound-engineering workflow prompts now explicitly call out the `/ce-` skill slash command at each stage.
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"@runfusion/fusion": patch
|
||||
---
|
||||
|
||||
Improve bundled non-coding workflow prompts so marketing, lead-generation, and design runs produce structured deliverables, with content and design preview artifacts persisted for review.
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"@runfusion/fusion": minor
|
||||
---
|
||||
|
||||
Rename the task detail Documents tab to Artifacts and add a task-scoped media artifact gallery alongside existing task documents.
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"@runfusion/fusion": patch
|
||||
---
|
||||
|
||||
Hide the dashboard AI subtask-breakdown quick-add button behind the default-off `subtaskBreakdown` experimental feature flag.
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"@runfusion/fusion": patch
|
||||
---
|
||||
|
||||
Prevent the bundled Droid CLI extension from starting local `droid` probes during server boot; validation now runs only when a Droid stream is actually used while existing probe paths remain non-interactive and timeout-bounded.
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"@runfusion/fusion": patch
|
||||
---
|
||||
|
||||
Revalidate dashboard service-worker assets before falling back to cache so rebuilt tabs cannot stay on stale bundles and render a blank page.
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"@runfusion/fusion": minor
|
||||
---
|
||||
|
||||
Add a Command Center System node selector so local and registered remote node telemetry can be inspected from the dashboard.
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"@runfusion/fusion": minor
|
||||
---
|
||||
|
||||
Add estimated human hours saved to Command Center Productivity analytics, UI stats, and CSV exports.
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"@runfusion/fusion": patch
|
||||
---
|
||||
|
||||
Raise the minimum agent heartbeat staleness floor from 5 to 10 minutes. Agents go silent during long-running but legitimate work (notably a verification step running a multi-minute test command, where the agent is blocked awaiting the command and cannot tick/heartbeat). The 5-minute floor could misread such a busy agent as dead and reclaim its in-progress task mid-run; 10 minutes gives long operations room before the liveness gate acts.
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"@runfusion/fusion": minor
|
||||
---
|
||||
|
||||
Start the AI engine by default in `pnpm local`, keep dashboard `--dev` engine-on unless `--no-engine` is passed, start desktop local runtimes with engines, and show dashboard instructions when Fusion is launched without an engine.
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"@runfusion/fusion": minor
|
||||
---
|
||||
|
||||
Sync workflow setting values across nodes in settings push, pull, receive, and status flows.
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"@runfusion/fusion": patch
|
||||
---
|
||||
|
||||
Carry the selected workflow lane through Planning Mode and Subtask Breakdown task creation so saved tasks appear on the active workflow instead of falling back to the main board.
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"@runfusion/fusion": patch
|
||||
---
|
||||
|
||||
Preserve task progress when a single-session run is hard-cancelled mid-execution. When the engine aborted in-flight work and bounced the task back to `todo`, the single-session teardown cleared the task `branch` and re-queued without `preserveResumeState` — resetting every step to `pending` and dropping the pointer to commits already on the task branch, so the next dispatch re-planned from Step 0 and the committed work was stranded (observed as a task that "lost all progress" and got stuck). The teardown now keeps the branch and moves with `preserveResumeState` whenever the task has resumable step progress, matching the step-session and pause-park paths, so execution resumes onto the existing branch from the first incomplete step. The worktree is still removed to free its concurrency slot — only the durable pointers (branch + step state) are kept.
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"@runfusion/fusion": minor
|
||||
---
|
||||
|
||||
Close the validator reaper→slice deadlock and harden every validation re-drive site for the new behavioral-verification posture. A reaped, task-less "done" feature (left in `loopState="validating"`/`needs_fix`+`error`) is now re-driven by recovery to a terminal pass/fail/inconclusive verdict instead of livelocking the slice, milestone, and mission. Adds an adversarial reliability suite enumerating every re-drive entry point (normal `processTaskOutcome`, each `recoverActiveMissions` branch, and the stale-run reaper) and asserting source-tree git-cleanliness, zero duplicate Fix Features, a terminal verdict, and no `error`-state deadlock. Documents the non-mutating verification run, the first-class `inconclusive` verdict, and the adversarial default-to-fail posture across `docs/missions.md`, `docs/missions-completion-contract.md`, and `CONCEPTS.md`.
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"@runfusion/fusion": minor
|
||||
---
|
||||
|
||||
Add Shadcn color-variant dashboard themes for blue, green, red, purple, pink, orange, yellow, mono, and black variants.
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"@runfusion/fusion": minor
|
||||
---
|
||||
|
||||
Add a Shadcn dashboard color theme with zinc neutral tokens, sans-serif typography, 1px borders, subtle flat shadows, and solid primary buttons.
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"@runfusion/fusion": minor
|
||||
---
|
||||
|
||||
Add the `shadcn-gray-blue` dashboard color theme with slate blue-gray surfaces and a muted slate-blue accent.
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"@runfusion/fusion": minor
|
||||
---
|
||||
|
||||
Add a Shadcn Gray dashboard color theme with a fully neutral zinc-gray accent.
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"@runfusion/fusion": minor
|
||||
---
|
||||
|
||||
Add Shadcn Mono Red/Blue/Green/Purple/Pink/Orange/Yellow dashboard color themes and migrate legacy `shadcn-mono` selections to `shadcn-mono-red`.
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"@runfusion/fusion": patch
|
||||
---
|
||||
|
||||
Smooth the mobile Quick Chat fullscreen sheet during Android soft-keyboard viewport resizing while preserving synchronous iOS visualViewport alignment.
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"@runfusion/fusion": minor
|
||||
---
|
||||
|
||||
Add Command Center Productivity task-duration analytics, dashboard stat cards, and CSV export rows for completed-task active execution time.
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"@runfusion/fusion": patch
|
||||
---
|
||||
|
||||
Reset a task's stuck-kill streak on genuine forward progress. `stuckKillCount` was a lifetime counter — incremented by self-healing on each stuck-kill and cleared only by a manual retry — so a long, genuinely-progressing task could be terminalized by accumulation toward the stuck-kill budget. It now resets when a step reaches a terminal forward status (done/skipped), so only consecutive no-progress stalls count toward the budget.
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"@runfusion/fusion": minor
|
||||
---
|
||||
|
||||
Add workflow optional steps: workflows can declare optional step templates that tasks toggle on/off per task, with a workflow-level default. The built-in coding and stepwise-coding workflows expose agent browser verification as an optional step (the stepwise workflow gains a pre-merge workflow-step seam so enabled steps actually run). Optional steps are authorable in the node editor, preserved across node-editor saves, and selectable from a steps dropdown in both the quick-add card and the full New Task modal.
|
||||
3
.github/workflows/pr-checks.yml
vendored
3
.github/workflows/pr-checks.yml
vendored
@@ -44,6 +44,9 @@ jobs:
|
||||
- name: Lint
|
||||
run: pnpm lint
|
||||
|
||||
- name: Changeset format
|
||||
run: pnpm check:changesets
|
||||
|
||||
typecheck:
|
||||
name: Typecheck
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
26
.github/workflows/release.yml
vendored
26
.github/workflows/release.yml
vendored
@@ -392,10 +392,34 @@ jobs:
|
||||
|
||||
# Only create the release if at least one artifact exists. A failed build leg
|
||||
# yields a partial release rather than none; a total wipeout fails loudly.
|
||||
# FNXC:Changelog 2026-06-24-17:45:
|
||||
# Use the curated root CHANGELOG notes for the GitHub Release body instead
|
||||
# of GitHub's auto-generated notes. Extracts the version section and passes
|
||||
# it via --notes-file so the release body matches the distilled CHANGELOG.
|
||||
- name: Extract release notes from CHANGELOG
|
||||
if: ${{ steps.collect.outputs.count != '0' }}
|
||||
id: notes
|
||||
run: |
|
||||
VERSION="${GITHUB_REF#refs/tags/v}"
|
||||
NOTES=$(node -e "
|
||||
const fs = require('fs');
|
||||
const content = fs.readFileSync('CHANGELOG.md', 'utf8');
|
||||
const lines = content.split(/\r?\n/);
|
||||
const header = '## ' + '${VERSION}';
|
||||
const start = lines.findIndex(l => l.trim() === header);
|
||||
if (start === -1) { console.log('Release v${VERSION}'); process.exit(0); }
|
||||
let end = lines.length;
|
||||
for (let i = start + 1; i < lines.length; i++) {
|
||||
if (lines[i].startsWith('## ')) { end = i; break; }
|
||||
}
|
||||
console.log(lines.slice(start + 1, end).join('\n').trim() || 'Release v${VERSION}');
|
||||
")
|
||||
echo "$NOTES" > /tmp/release-notes.md
|
||||
|
||||
- name: Create GitHub Release
|
||||
if: ${{ steps.collect.outputs.count != '0' }}
|
||||
uses: softprops/action-gh-release@v2
|
||||
with:
|
||||
generate_release_notes: true
|
||||
body_path: /tmp/release-notes.md
|
||||
fail_on_unmatched_files: true
|
||||
files: release-files/*
|
||||
|
||||
26
AGENTS.md
26
AGENTS.md
@@ -44,6 +44,27 @@ Bump types:
|
||||
|
||||
Do **NOT** create changesets for AGENTS.md/README/internal docs, CI config, or behavior-preserving refactors. `@fusion/core`, `@fusion/dashboard`, and `@fusion/engine` are private.
|
||||
|
||||
#### Changeset body format (required)
|
||||
|
||||
Each changeset body must use labeled fields — not freeform paragraphs. The `summary` is the only content that appears in end-user release notes. The audience is Fusion operators, not developers reading internals.
|
||||
|
||||
```markdown
|
||||
---
|
||||
"@runfusion/fusion": minor
|
||||
---
|
||||
|
||||
summary: Add a Command Center productivity control for LOC backfills.
|
||||
category: feature
|
||||
dev: Uses the new `fn_backfill_loc` tool; settings key `commandCenter.locBackfill`.
|
||||
```
|
||||
|
||||
Fields:
|
||||
- `summary` (required) — one line, user-facing, max 120 chars. Describe what changed for the operator, not implementation detail.
|
||||
- `category` (required) — one of: `feature`, `fix`, `breaking`, `security`, `performance`, `internal`.
|
||||
- `dev` (optional) — developer/migration detail. Preserved in per-package CHANGELOGs but excluded from distilled release notes.
|
||||
|
||||
A linter (`pnpm check:changesets`) validates this format and runs in the PR-check gate. Legacy freeform changesets pass with a warning during the transition period; use `--strict` to fail on legacy format.
|
||||
|
||||
### Releasing
|
||||
|
||||
Use only:
|
||||
@@ -195,6 +216,9 @@ Scoped exception (FN-5819): shared-branch-group members (`branchContext.assignme
|
||||
- FN-6783: task-store open and self-healing housekeeping emit `task:reconcile-orphaned-task-dir` when they non-destructively re-import a valid live `.fusion/tasks/{ID}/task.json` directory that has no task row anywhere, preserving soft-deleted/archived/tombstoned IDs.
|
||||
- FN-6782/FN-6796: self-healing emits `task:auto-recover-paused-abort-park` when it clears a benign pause-abort operator park, requeueing safe `todo`/`in-progress` rows or preserving a clean auto-merge-eligible `in-review` row for review progression.
|
||||
- FN-6793/FN-6797: self-healing emits `task:reconcile-in-review-unmet-dependencies` when it rebounds an `in-review` task whose declared dependencies are still unmet, and `task:reconcile-in-review-unmet-dependencies-no-action` when pause/user-pause, `autoMerge:false`, live execution/checkout proof, or a failed rebound mutation blocks that backward move.
|
||||
- Workspace (Phase D U1): self-healing emits `task:reconcile-workspace-partial-land` when it re-enqueues a partial/zero-landed workspace task's per-repo land (or parks it `failed` when a sub-repo's `fusion/<id>` branch is gone with no `landedSha`), and `task:reconcile-workspace-partial-land-no-action` when `autoMerge:false`, user-pause, or a live sub-repo worktree (workspace-aware liveness) blocks that backward move.
|
||||
- Workspace (Phase D U1): self-healing emits `task:reclaim-phantom-workspace-land-lease` when it clears a leaked `workspace-repo-land` lease whose owning task is terminal/dead and older than the FN-6736 staleness floor (a live merging owner is left untouched).
|
||||
- Workspace (Phase D U1): self-healing emits `task:reconcile-orphaned-workspace-worktree` when it removes a done/dead workspace task's recorded per-repo worktree from its stored `worktreePath` (guarded by `isPathActive`; no temp-root walk).
|
||||
|
||||
|
||||
## Reference docs (deeper detail)
|
||||
@@ -245,6 +269,8 @@ Keep this AGENTS inventory in sync with App lazy imports, AppModals lazy modal i
|
||||
- `PiExtensionsManager`
|
||||
- `AgentDetailView`
|
||||
|
||||
Note: the embedded main-content views Workflows (`_WorkflowEditorView`), Import Tasks (`_ImportTasksView`), Automations (`_AutomationsView`), and Settings (`_SettingsView`) in App.tsx are `_`-prefixed lazy splits that reuse already-documented chunks. They are intentionally excluded from the curated list above and from the count; `lazy-loaded-views-docs.test.ts` filters out `_`-prefixed lazy consts (`extractAppLazyViews`), so do not add them as bullets.
|
||||
|
||||
## FNXC_LOG comments:
|
||||
- Please whenever you're working on a codebase. I want you to add comments describing the date of the change (must be in this format yyyy-MM-dd-hh:mm) and describing the requirements or the change in requirements that made you implement certain functionality.
|
||||
- I want you to write FNXC:Area-of-product in front of all your comments so they can be grepped.
|
||||
|
||||
496
CHANGELOG.md
496
CHANGELOG.md
@@ -2,6 +2,452 @@
|
||||
|
||||
User-facing release notes aggregated across all packages. This file is auto-synced from each `packages/*/CHANGELOG.md` by `scripts/release.mjs` — do not edit by hand.
|
||||
|
||||
## 0.47.0
|
||||
|
||||
### New
|
||||
|
||||
- Structured changeset format with AI-distilled release notes for cleaner, user-facing changelogs.
|
||||
|
||||
### Internal
|
||||
|
||||
- Saved agent tool-output details now default off to reduce persisted log payloads, while timeline rows remain logged and detailed tool arguments/results stay available via the global `persistAgentToolOutput: true` opt-in.
|
||||
- Harden the workspace per-repo land loop against partial-failure races. A lost `landedSha` DB write after a sub-repo's integration ref already advanced no longer silently continues — it escalates to a retryable partial-land error, and the landed predicate now recognizes an already-landed repo via its `Fusion-Task-Id` trailer on retry, so a re-run never produces a second squash commit. The land lease is now taskId-aware across registry kinds: a merging task can no longer clobber an executing task's acquire lease on a shared sub-repo (any foreign-task holder is treated as contention), and the active-session registry rejects foreign-task overwrites instead of silently clobbering. The transient `merging` status is always reset before any throw escapes the land loop (no stuck-`merging` leak), and finalize re-reads the latest task and no longer swallows the merge-details persist failure (no finalizing on a stale row).
|
||||
- Address Phase C workspace merge-loop review feedback. A sub-repo recognized as already-landed via the `Fusion-Task-Id` trailer fallback (when its `landedSha` persist was lost) now resolves and re-records a concrete `landedSha`, so finalize no longer drops it and mis-reports a fully-landed workspace task as a no-op (`mergeConfirmed:false`). A manual merge that hits sub-repo land-lease contention now surfaces the busy error to the user without consuming the persisted `mergeRetries` quota (matching the auto path's separate busy counter). The partial-land retry persists the incremented retry count before arming the backoff timer — a failed write now fails closed instead of looping without consuming budget — and clears the stale busy-contention counter when a real partial land supersedes transient busy failures. The CLI and dashboard merge doors use the shared `isWorkspaceTask` predicate instead of re-inlining the workspace check, and integration-branch shell interpolation in base-commit capture uses POSIX single-quote escaping.
|
||||
- Fix narrow right-sidebar Dev Server preview overlap by replacing the inline preview with an accessible modal launcher when the dock is very narrow, while keeping inline preview for full-page, mobile viewport, and expanded pop-out hosts.
|
||||
- Fix ntfy test notifications to honor unsaved Settings form config so users can enable ntfy, enter a valid topic/server/token, and send a test notification before saving.
|
||||
- Close task detail dialogs and embedded task-detail hosts immediately after delete confirmations complete, while delete requests continue reporting success or error toasts asynchronously.
|
||||
- Stack task-detail Chat agent headers above output blocks in the List View split-pane detail pane while preserving full-width desktop chat layout.
|
||||
- Merger unification (master-plan U0): `runAiMerge` (the FN-5633 clean-room AI merge path) is now the **sole** merge path. The engine dispatch, the `fn task merge` CLI command, and the UI-only (`--no-engine`) dashboard merge all route through `runAiMerge`; the legacy `aiMergeTask` pipeline is soft-deprecated (body retained, `@deprecated`). The `merger.mode` setting is now **inert and deprecated** — the type and field are retained as published surface, but the `"deterministic"` value no longer selects a different pipeline; observing it logs a one-time deprecation warning and proceeds via the unified AI merge path. A new shared `assertNotWorkspaceTaskMerge` guard rejects workspace-mode tasks (populated `workspaceWorktrees`) at every merge entry point with a clear error until per-repo merge support (master-plan U6) lands.
|
||||
- Fix multiworkspace tasks failing to complete. `task.workspaceWorktrees` is now durably persisted (it previously had no SQLite column, so `fn_acquire_repo_worktree`'s write was dropped on every persist and `fn_task_done` always reported "acquired no sub-repo worktrees"). Concurrent workspace tasks no longer collide on the shared browse-root active-session path — each task gets a task-scoped session key, so a second workspace task no longer fails with "active-session path … is held by …".
|
||||
- **Breaking:** the `WorkflowOptionalStep` type, previously exported from `@runfusion/fusion`, is removed — any consumer importing it must migrate to `optional-group` nodes / `ResolvedWorkflowOptionalStep`.
|
||||
- Add `X-Session-Id` and `X-Session-Affinity` request headers to all LLM chat completion requests. These let LLM gateways sticky-route consecutive requests from the same conversation to the same backend, and let observability tools (Langfuse, Arize, etc.) group the otherwise-stateless API calls of a session into a single multi-turn trace. Both headers carry the same stable identifier — the task id when available (stable across pause/resume), otherwise the pi session id. (#1675)
|
||||
- Workflow editor: add a Help section to the node detail pane. Every node now documents what it does, how to configure it, and its inputs/outputs/edges — including the engine-managed merge-lifecycle nodes (auto-merge gate, branch-group member integration, branch-group promotion, PR and recovery nodes), which are surfaced read-only with an "Engine-managed" badge.
|
||||
- Workflow editor: optional steps are now graph-native. A new `optional-group` container node (foreach/loop-style) holds a subgraph the executor runs once when the group is enabled for a task (per-task `enabledWorkflowSteps` + workflow `defaultOn`) and bypasses when disabled. All seven built-in add-ons (documentation-review, qa-check, security-audit, performance-review, accessibility-check, browser-verification, frontend-ux-design) are insertable from the node-editor palette as a node or wrapped in an optional-group. The built-in coding and stepwise-coding workflows now express `browser-verification` as an optional-group. Optional-group enable resolution correctly handles id collisions with add-on template ids, so a group's enable state is not silently bypassed during task creation/update. (The legacy declaration-based optional-steps model is retired in a sibling changeset; only the `workflow-step` seam infrastructure removal remains a follow-up.)
|
||||
- Workspace tasks no longer render blank in the dashboard. Task cards and the task
|
||||
- Add workspace mode: open a folder of git repositories as a single Fusion
|
||||
- Workspace mode (Phase A / U2): harden per-repo worktree acquisition. Each sub-repo worktree now gets the task identity guard installed (single-repo parity), a per-repo base commit SHA captured local-first against that sub-repo's resolved integration branch (shared `integrationBranch` override stripped so each repo falls through to its own `origin/HEAD`), and same-sub-repo acquisition exclusivity registered in the path-keyed active-session registry. Re-acquiring an already-acquired `(taskId, repo)` is idempotent, and acquisition failures surface an error plus an audit event instead of silently stalling.
|
||||
- Workspace mode (Phase C U3): serialize concurrent same-sub-repo lands with a per-repo file-scope lease. When two workspace tasks try to land onto the SAME sub-repo's local integration ref at the same time, the merge phase now registers the sub-repo's absolute path in the path-keyed active-session registry under a distinct `workspace-repo-land` kind before each land and releases it in a `finally` (on land success or failure — no stuck lock). A second task contending for the same sub-repo fast-fails with a retryable `WorkspaceRepoLandBusyError`, which the existing partial-land auto-retry-then-park dispatch handles (consume a `mergeRetry`, re-enqueue with backoff, then operator-park). Disjoint sub-repos lease different paths and never serialize against each other. The lease prevents clean-room ai-merge worktree collisions; ref correctness is already guaranteed by `advanceIntegrationBranchRef`'s CAS (concurrent-advance → rebuild).
|
||||
- Workspace mode Phase A (U1): executor session scoping. In workspace mode the executor now skips the root worktree acquisition and every rootDir git preflight (base-commit capture, contamination, worktree-liveness), runs the agent session rooted at the browse-only workspace root, and tracks acquired sub-repo worktrees as a per-task set. Single-repo tasks are unchanged (one-element set, byte-for-byte preflight parity).
|
||||
- Workspace mode (Phase B, U1): per-repo post-session change capture, contamination detection, and worktree-invariant verification. In workspace mode the executor now loops `task.workspaceWorktrees`, reusing `captureModifiedFiles` per sub-repo (diffing each against its own `baseCommitSha`, with a merge-base fallback when undefined) to aggregate repo-prefixed `task.modifiedFiles` and surface per-repo contamination, and un-stubs `verifyWorktreeInvariants` to assert each acquired worktree's git toplevel and `fusion/<id>` branch. Single-repo behavior is unchanged.
|
||||
- Workspace mode (Phase B, U2): per-repo review at both review entry points plus per-repo `fn_task_done` completion + scope-leak verification. In workspace mode both review call sites (the in-session `fn_review_step` tool and the step-inversion review seam) now loop the single-cwd `reviewStep` once per acquired sub-repo (cwd = each repo's worktree) and aggregate the repo-tagged verdicts as a conjunction — the task is reviewed only when every sub-repo approves, and the first failing sub-repo's verdict (with repo-tagged findings) drives the existing verdict→edge mapping. `fn_task_done` now verifies worktree invariants per acquired repo and iterates the scope-leak guard per sub-repo (cwd = repo worktree, repo `baseCommitSha`), blocking completion on any sub-repo carrying off-scope changes and naming the repo. Adds a minimal shared repo-prefix-derivation helper (`workspace-paths.ts`). Single-repo behavior is unchanged.
|
||||
- Workspace mode Phase C (U1): per-repo merge loop. Extract `landOneRepo` from the
|
||||
- Workspace mode Phase C (U2): per-repo landed predicate, finalize-once, and idempotent
|
||||
- Workspace mode Phase D (U1): workspace-aware self-healing. The existing merging-status reconcilers no longer mis-finalize a partial-landed workspace task (recoverInterruptedMergingTasks now clears the transient `merging` status and re-enqueues the idempotent per-repo land instead of running the single-commit finalize over the non-git workspace root), and recoverMergeableReviewTasks now admits workspace tasks (task.worktree is null). Adds three reconcilers: partial-land recovery (re-enqueue via enqueueMerge, FORK-A unrecoverable → park failed; guarded by autoMerge:false + user-pause + workspace-aware liveness), phantom `workspace-repo-land` lease reclaim (new `entriesByKind` registry seam), and per-repo worktree cleanup from stored paths (no temp walk). New run-audit events: `task:reconcile-workspace-partial-land`(`-no-action`), `task:reclaim-phantom-workspace-land-lease`, `task:reconcile-orphaned-workspace-worktree`.
|
||||
|
||||
## 0.46.0
|
||||
|
||||
### @fusion/dashboard
|
||||
|
||||
#### Patch Changes
|
||||
|
||||
- @fusion/core@0.46.0
|
||||
- @fusion/engine@0.46.0
|
||||
- @fusion/i18n@0.39.9
|
||||
- @fusion-plugin-examples/cli-printing-press@0.1.26
|
||||
- @fusion-plugin-examples/compound-engineering@0.1.9
|
||||
- @fusion-plugin-examples/dependency-graph@0.1.40
|
||||
- @fusion-plugin-examples/roadmap@0.1.28
|
||||
- @fusion-plugin-examples/cursor-runtime@0.1.28
|
||||
- @fusion-plugin-examples/droid-runtime@0.1.35
|
||||
- @fusion-plugin-examples/hermes-runtime@0.2.59
|
||||
- @fusion-plugin-examples/openclaw-runtime@0.2.59
|
||||
- @fusion-plugin-examples/paperclip-runtime@0.2.59
|
||||
|
||||
### @fusion/desktop
|
||||
|
||||
#### Patch Changes
|
||||
|
||||
- @fusion/core@0.46.0
|
||||
- @fusion/dashboard@0.46.0
|
||||
- @fusion/engine@0.46.0
|
||||
|
||||
### @fusion/engine
|
||||
|
||||
#### Patch Changes
|
||||
|
||||
- @fusion/core@0.46.0
|
||||
- @fusion/pi-claude-cli@0.46.0
|
||||
|
||||
### @fusion/plugin-sdk
|
||||
|
||||
#### Patch Changes
|
||||
|
||||
- @fusion/core@0.46.0
|
||||
|
||||
### @runfusion/fusion
|
||||
|
||||
#### Minor Changes
|
||||
|
||||
- 41f3b04: Add a Command Center Productivity control for previewing and applying historical LOC backfills from the dashboard.
|
||||
- efb94c8: Add editable global model pricing overrides, a one-click LiteLLM pricing refresh, and override-aware Command Center cost estimates.
|
||||
|
||||
#### Patch Changes
|
||||
|
||||
- f6e9deb: Stop Planning Mode from automatically focusing the initial text entry when it opens, preventing mobile keyboards from appearing until the user explicitly focuses the textarea.
|
||||
- 466cf9c: Dispose completed spawned child agent sessions so execution memory is released promptly after `fn_spawn_agent` children finish, keep artifact registry listing metadata-only so large inline artifacts are not loaded during agent execution, bound structured tool-result log previews before serialization, reduce dashboard SSE keepalive churn, and keep the dashboard TUI performance timeline drained during long-running execution.
|
||||
- d06e316: Fix Command Center Recharts line and pie graphs rendering blank when their cards initially report unusable responsive dimensions.
|
||||
- a670f5c: Restore core task lifecycle compatibility for workflow-column transitions, deferred title summarization fixtures, workflow IR rollback persistence, and capacity-aware task movement.
|
||||
- fe536b2: Fix stale durable agent task assignments for tasks parked behind file-scope lease queues, including Reports Health Check rendering and self-healing reconciliation.
|
||||
- 736ec6d: Fix mobile mailbox message selection so stale deep links no longer override the user's selected message.
|
||||
- 945f0f1: Pass project fallback model settings into triage spec reviewer sessions so global default overrides are honored during review.
|
||||
|
||||
### runfusion.ai
|
||||
|
||||
#### Patch Changes
|
||||
|
||||
- Updated dependencies [f6e9deb]
|
||||
- Updated dependencies [466cf9c]
|
||||
- Updated dependencies [d06e316]
|
||||
- Updated dependencies [a670f5c]
|
||||
- Updated dependencies [fe536b2]
|
||||
- Updated dependencies [736ec6d]
|
||||
- Updated dependencies [945f0f1]
|
||||
- Updated dependencies [41f3b04]
|
||||
- Updated dependencies [efb94c8]
|
||||
- @runfusion/fusion@0.46.0
|
||||
|
||||
## 0.45.0
|
||||
|
||||
### @fusion/core
|
||||
|
||||
#### Patch Changes
|
||||
|
||||
- 26ebb92: Fix `reconcileOrphanedTaskDirs` silently resurrecting long-deleted tasks onto the live board after a restart ("all task IDs reset / starting over").
|
||||
|
||||
The sweep re-imports `.fusion/tasks/<id>/` directories that have no DB row, to recover heartbeat-created dirs that race store init or rows lost to a recent DB corruption. But it didn't distinguish a genuinely-recent orphan from an ancient deleted-task dir that merely lingered on disk. Modern deletes leave a soft-delete tombstone (caught by `taskIdExistsAnywhere`), but legacy hard-deletes left no tombstone — so a months-old `task.json` with no DB row was re-imported as a live task, surfacing old low-numbered IDs (FN-001, FN-002, …) at the top of the board.
|
||||
|
||||
Reconcile now gates recovery on a recency window (`task.json` modified within the last 7 days). Older orphan dirs are skipped with reason `stale-orphan-dir-beyond-recency-window` and left for explicit recovery (unarchive/restore) or directory cleanup, while heartbeat-race and recent-corruption recovery still work.
|
||||
|
||||
- 7e7eb62: Harden the orphan-worktree and stale-task-dir cleanup fixes (code-review follow-up).
|
||||
|
||||
- **executor.ts (P0):** the stale-conflict recovery's `rm(worktreePath, { recursive, force })` had no bounds check. `worktreePath` can originate from a git worktree admin entry that resolves outside `.worktrees/`, so an out-of-bounds or symlinked path could be force-removed. The recovery now refuses unless the path is inside the configured worktrees dir, is not a symlink (checked via `realpathSync`), is not a registered git worktree, and is not actively owned — and it re-verifies liveness inside the catch rather than trusting the error string. It also excludes `spawn` failures (e.g. `spawn git ENOENT` when git is missing) so a missing-binary error is no longer misread as a successful stale-path cleanup.
|
||||
- **worktree-pool.ts:** `resolveGitdirPointer` is replaced by `dotGitPointerIsDangling`, which reaps **only** when a `.git` link's gitdir target is confirmed missing. A real `.git` directory, an unparseable pointer, or any read/stat failure now returns "not dangling" (conservative) so a transient read error on a live worktree's `.git` can't cause a force-remove. Removes the `string | "directory" | null` sentinel union.
|
||||
- **core store.ts:** the `reconcileOrphanedTaskDirs` recency window is now bypassed when the live task table is empty (the corruption/restore case — surviving `task.json` files keep old mtimes), and when a corrupt `fusion.db` was auto-recovered on startup, so `.recover` row loss is not stranded by the gate. Adds an `ignoreRecencyWindow` option for explicit callers.
|
||||
- Tests for all of the above: executor recovery + out-of-bounds refusal, unparseable `.git` skip, recency boundary, empty-DB/forced bypass.
|
||||
|
||||
### @fusion/dashboard
|
||||
|
||||
#### Patch Changes
|
||||
|
||||
- Updated dependencies [26ebb92]
|
||||
- Updated dependencies [7e7eb62]
|
||||
- @fusion/core@0.45.0
|
||||
- @fusion/engine@0.45.0
|
||||
- @fusion/i18n@0.39.8
|
||||
- @fusion-plugin-examples/cli-printing-press@0.1.25
|
||||
- @fusion-plugin-examples/compound-engineering@0.1.8
|
||||
- @fusion-plugin-examples/dependency-graph@0.1.39
|
||||
- @fusion-plugin-examples/roadmap@0.1.27
|
||||
- @fusion-plugin-examples/cursor-runtime@0.1.27
|
||||
- @fusion-plugin-examples/droid-runtime@0.1.34
|
||||
- @fusion-plugin-examples/hermes-runtime@0.2.58
|
||||
- @fusion-plugin-examples/openclaw-runtime@0.2.58
|
||||
- @fusion-plugin-examples/paperclip-runtime@0.2.58
|
||||
|
||||
### @fusion/desktop
|
||||
|
||||
#### Patch Changes
|
||||
|
||||
- Updated dependencies [26ebb92]
|
||||
- Updated dependencies [7e7eb62]
|
||||
- @fusion/core@0.45.0
|
||||
- @fusion/dashboard@0.45.0
|
||||
- @fusion/engine@0.45.0
|
||||
|
||||
### @fusion/engine
|
||||
|
||||
#### Patch Changes
|
||||
|
||||
- Updated dependencies [26ebb92]
|
||||
- Updated dependencies [7e7eb62]
|
||||
- @fusion/core@0.45.0
|
||||
- @fusion/pi-claude-cli@0.45.0
|
||||
|
||||
### @fusion/plugin-sdk
|
||||
|
||||
#### Patch Changes
|
||||
|
||||
- Updated dependencies [26ebb92]
|
||||
- Updated dependencies [7e7eb62]
|
||||
- @fusion/core@0.45.0
|
||||
|
||||
### @runfusion/fusion
|
||||
|
||||
#### Minor Changes
|
||||
|
||||
- 26e5514: Add the `factory-mono` dashboard color theme, a monochrome Factory variant with red accents and neutralized glow effects.
|
||||
- 130fea2: Ask first-run users whether to create an optional first persistent agent after project registration, with CEO as the default template, skip support, and no duplicate GitHub star prompt.
|
||||
- 70cce18: Add the `fn_agent_set_instructions` extension tool so managing agents can update direct or indirect reports' inline or file-backed instructions with org-hierarchy authorization.
|
||||
- 8dd9697: Add an operator-triggered Command Center Productivity LOC backfill API and client for historical commit-association diff stats.
|
||||
- f13aaa1: Add a Command Center GitHub resolved-issues detail list and expose the resolved issue rows in the GitHub analytics endpoint payload and CSV export.
|
||||
- c158dda: Add the `xhigh` reasoning effort level to model settings and task/agent selectors. Claude CLI adapters pass the value through to runtime mapping, where non-Opus models use `high` effort and Opus models use `max` effort.
|
||||
- 52924ba: Add a built-in lead-generation workflow with custom lead columns, fields, and stage prompts.
|
||||
- 7f3e942: Add a built-in Design workflow that gates UI-heavy work with a design/UX review before standard review and merge.
|
||||
- 281ce35: Add a built-in Marketing workflow with content-specific columns and prompts for brief, drafting, editorial review, and publishing.
|
||||
- fbce59b: Add a core artifact registry data model and store APIs for persisted artifact metadata with on-disk binary storage.
|
||||
- af06170: Add `fn_artifact_register`, `fn_artifact_list`, and `fn_artifact_view` agent tools for publishing and discovering multi-type artifacts, with best-effort dashboard user inbox notifications on registration.
|
||||
- ef48895: Add dashboard artifact registry read APIs, client helpers, and a Documents-view Artifacts media gallery for images, videos, audio, documents, and generic artifacts.
|
||||
- 58f7588: Add a Shadcn Custom dashboard theme with persisted, sanitized design-token color picker overrides across Settings and Command Center theme selectors.
|
||||
- f80a785: Add pricing entries for OpenAI Codex models used through the `openai-codex` provider, so Command Center token analytics can estimate costs for Codex runs instead of showing them as unavailable.
|
||||
|
||||
This is marked minor because it expands the set of priced models surfaced by the published CLI/dashboard without changing existing pricing behavior.
|
||||
|
||||
- 09acfbb: Allow users to manually pause and unpause agent-assigned tasks from the dashboard task detail view and API.
|
||||
- 4fec139: Move Stash Recovery into the Git Manager Recovery tab and remove the standalone top-level Stash Recovery view from dashboard navigation.
|
||||
- 5b33da9: Move desktop toolbar tools into the right sidebar tools rail. The right dock now hosts Activity, Activity Log, Import from GitHub, Git Manager, Files, and Automation, and no longer duplicates left-sidebar content views.
|
||||
- 7034b55: Move the dashboard terminal launcher to the footer executor status bar and add docked plus floating resizable terminal modes on desktop/tablet while preserving mobile fullscreen terminal behavior.
|
||||
- a913881: Make the dashboard right dock persistent by default with an in-dock collapse toggle, and remove duplicate Header right-dock toggle behavior.
|
||||
- 7fd14eb: Rename the task detail Documents tab to Artifacts and add a task-scoped media artifact gallery alongside existing task documents.
|
||||
- 496167c: Polish dashboard navigation, floating modal, file browser, chat footer, agent role, insights, and list-view action surfaces for a more consistent responsive UI.
|
||||
- eb3477a: Add a Command Center System node selector so local and registered remote node telemetry can be inspected from the dashboard.
|
||||
- 59d3eee: Add estimated human hours saved to Command Center Productivity analytics, UI stats, and CSV exports.
|
||||
- 2dc36d9: Import Tasks PR preview now shows the full comment thread and per-check status (with success/failure/pending indicators) for the selected pull request, fetched on selection and cached per PR. The body still renders immediately while checks and comments stream in.
|
||||
- 7ef3817: Start the AI engine by default in `pnpm local`, keep dashboard `--dev` engine-on unless `--no-engine` is passed, start desktop local runtimes with engines, and show dashboard instructions when Fusion is launched without an engine.
|
||||
- 7ddf58d: Sync workflow setting values across nodes in settings push, pull, receive, and status flows.
|
||||
- 8640a74: The shared markdown renderer (GitHub PR/issue bodies + comments, mailbox, chat) now renders embedded raw HTML and mermaid diagrams. Raw HTML (`<details>`/`<summary>`, `<kbd>`, `<sub>`, tables) renders as real elements via `rehype-raw`, with `rehype-sanitize` stripping XSS (script/style/iframe, event handlers, `javascript:` URLs) since these bodies come from GitHub; HTML comments (`<!-- -->`) are dropped. Fenced ```mermaid blocks render as actual diagrams via a lazy-loaded `mermaid` import (kept out of the main bundle, loaded only when a diagram is present), falling back to the raw code block on parse error and following the dashboard theme.
|
||||
- 4fd8d44: Polish dashboard navigation and app chrome, add responsive chat/file/modal behavior, refine roadmaps, missions, task details, workflow defaults, theme defaults, and sidebar/header styling.
|
||||
- 91180fb: Close the validator reaper→slice deadlock and harden every validation re-drive site for the new behavioral-verification posture. A reaped, task-less "done" feature (left in `loopState="validating"`/`needs_fix`+`error`) is now re-driven by recovery to a terminal pass/fail/inconclusive verdict instead of livelocking the slice, milestone, and mission. Adds an adversarial reliability suite enumerating every re-drive entry point (normal `processTaskOutcome`, each `recoverActiveMissions` branch, and the stale-run reaper) and asserting source-tree git-cleanliness, zero duplicate Fix Features, a terminal verdict, and no `error`-state deadlock. Documents the non-mutating verification run, the first-class `inconclusive` verdict, and the adversarial default-to-fail posture across `docs/missions.md`, `docs/missions-completion-contract.md`, and `CONCEPTS.md`.
|
||||
- da5fea6: Add Shadcn color-variant dashboard themes for blue, green, red, purple, pink, orange, yellow, mono, and black variants.
|
||||
- e19f7c2: Add a Shadcn dashboard color theme with zinc neutral tokens, sans-serif typography, 1px borders, subtle flat shadows, and solid primary buttons.
|
||||
- b20a25c: Add the `shadcn-gray-blue` dashboard color theme with slate blue-gray surfaces and a muted slate-blue accent.
|
||||
- 4672203: Add a Shadcn Gray dashboard color theme with a fully neutral zinc-gray accent.
|
||||
- 12aae94: Add Shadcn Mono Red/Blue/Green/Purple/Pink/Orange/Yellow dashboard color themes and migrate legacy `shadcn-mono` selections to `shadcn-mono-red`.
|
||||
- dc0064b: Dashboard navigation and panel redesign (desktop/tablet; mobile unchanged):
|
||||
|
||||
- **Right sidebar**: a single show/hide toggle now lives in the top header (replacing the tablet overflow menu); the dock is hidden when closed and no longer keeps a persistent icon rail or in-dock collapse button. Its tools (Files — now the default/first tab, Activity, Activity Log, Git Manager) render inline inside the dock instead of opening popup modals. Files opens inline with a pop-out to the resizable file modal. The embedded Git Manager adapts to its width (compact horizontal tab strip in the dock, full two-pane in the wide pop-out). The dependency graph no longer appears in the dock.
|
||||
- **Left sidebar**: New Task button matches the item-highlight box; footer spacing between Collapse and Settings; divider before the secondary section removed with uniform row spacing. New main-content destinations — Workflows, Import Tasks (GitHub import, with the GitHub mark), and Automations (two-pane, Command Center styling) — render in the main panel instead of as modals.
|
||||
- **Embedded views**: Planning Mode embeds without modal chrome (no header/close/shadow), fills the full content area, and renders correctly on mobile; the board WorkflowSwitcher is available in Planning. Dev Server header matches Command Center. Insights header wraps so actions don't overlap. List view's left pane can be dragged much narrower with two-line title wrapping.
|
||||
- **Other**: the docked terminal no longer blurs or blocks the page behind it; the footer Terminal button renders as plain text like the running-state trigger; the workflow selector matches the project selector's styling, height, and font size; the Automations screen uses theme color tokens.
|
||||
|
||||
- 5697d2c: Skills view detail pane: render SKILL.md as Markdown (GFM + sanitized HTML + mermaid), compact the referenced-files area while showing all files, and make each file clickable to view its content with a "Back to SKILL.md" affordance. Adds a `GET /api/skills/:id/file` endpoint for per-file content.
|
||||
- 5117944: Add Command Center Productivity task-duration analytics, dashboard stat cards, and CSV export rows for completed-task active execution time.
|
||||
- d4e91d4: Add workflow optional steps: workflows can declare optional step templates that tasks toggle on/off per task, with a workflow-level default. The built-in coding and stepwise-coding workflows expose agent browser verification as an optional step (the stepwise workflow gains a pre-merge workflow-step seam so enabled steps actually run). Optional steps are authorable in the node editor, preserved across node-editor saves, and selectable from a steps dropdown in both the quick-add card and the full New Task modal.
|
||||
|
||||
#### Patch Changes
|
||||
|
||||
- c8a82e7: Auto-continue the agent session after an engine-internal pause/resume abort instead of re-queueing the task to todo. When the engine tears down in-flight work (hard-cancel) and the workflow graph run ends with the task back in `todo`, the executor now retries the agent session in place — bounded by the existing graph-resume retry budget with backoff, falling back to a benign re-queue only after retries are exhausted. Before re-dispatching, it re-checks the task at fire time and aborts the auto-continue if the task was paused, moved, or deleted during the backoff window, so genuine user/global/task pauses are never resumed against the operator's intent. The transient reclassification clears any stale `failed` status and emits an `Auto-recovered:` log so no spurious failure notification fires.
|
||||
- ee9c8ab: Align dashboard view chrome and inner-pane spacing across Chat, Mailbox, Workflows, Artifacts-adjacent controls, Goals, and Compound Engineering.
|
||||
- ce6c0fb: Polish dashboard view chrome: align Dashboard, Import Tasks, Automations, Chat, and docked Files editor controls with the shared view header and toolbar styling.
|
||||
- 7635ba8: Fix a false "engine not running" banner when another fusion process on the same machine already owns the engine. The dashboard's health check only counted engines this process started, so a second launch (e.g. `pnpm dev dashboard` alongside an already-running `fusion`) that was correctly refused the per-machine engine singleton lock reported the engine as unavailable — even though one was running. The `ProjectEngineManager` now tracks engines owned by another process (detected via `EngineAlreadyRunningError` from the singleton lock) and exposes `hasRunningEngine()`, which the dashboard health endpoint uses so the banner reflects machine-level truth. Reconciliation still retries so this process takes over if the other exits, and the "refusing to start" log is emitted once per project instead of on every reconciliation tick.
|
||||
- ce90cc9: Keep Fusion verification progress moving by making targeted script tests honor file arguments, reaping verification subprocess groups after clean exits, and preventing the line-count audit from blocking `pnpm test`. The changed-test runner now caps reverse-dependent fan-out so a foundational-package edit no longer expands into a whole-workspace run, and the executor/verification guidance now directs agents to scope verification to changed files rather than running the full workspace test suite.
|
||||
- 5a422b0: Fix anthropic-compatible custom providers failing with "No API provider registered for api: anthropic".
|
||||
|
||||
`resolveCustomProviderApiType` mapped the `anthropic-compatible` provider type to the api key `"anthropic"`, but pi-ai registers the Anthropic Messages API under `"anthropic-messages"`. Any custom provider configured as `anthropic-compatible` (self-hosted Claude proxy, gateway, etc.) therefore selected a model whose `api` did not match a registered provider and threw at stream time. Mapped it to `"anthropic-messages"` and added a regression assertion alongside the existing openai-compatible / openai-responses coverage.
|
||||
|
||||
- 2d32760: Clear the stale `failed` status when a pause/resume abort is reclassified as a benign todo re-queue, so the task no longer surfaces as failed on the board and the deferred failure notification is suppressed. Previously a pause-abort parked `status:"failed"` on an earlier non-todo observation stayed dispatchable (the scheduler filters on column+paused, not status), re-entered the benign-todo branch, and was logged benign while the row stayed failed — firing a contradictory failure alert during global pause when self-healing recovery was suppressed. The clear path also emits an `Auto-recovered:`-prefixed log so the notification service proactively cancels the pending failure timer instead of relying only on the fire-time re-check.
|
||||
- b564ee0: Make the compound-engineering built-in workflow actually load skills and run the full CE flow. Previously the workflow named CE skills at each node but the graph-node execution path (`runGraphCustomNode`) never loaded them: the named skill was only injected as prompt text, the plugin-injected `FUSION_CE_*` runtime env never reached the step session, and `fn_spawn_agent` was never registered for workflow steps, so persona fan-out and skill loading silently no-op'd. Now skill-executor graph steps thread the injected env, load the named skill (discovery + selection via `additionalSkillPaths`), register the spawn tool in coding mode, and receive an engine-injected Fusion workflow-step conventions preamble (await-input for questions, `FUSION_HEADLESS` degrade path, persona fan-out via `systemPromptOverride`). Adds an explicit `unattended` opt-in for `FUSION_HEADLESS`, reconciles the preamble with the gate verdict-JSON contract, and carries `skillName` through the `WorkflowStep` round-trip.
|
||||
- 8b5b9a7: Fix the persistent non-blocking Full Suite failure caused by the Compound Engineering plugin's `dist-freshness.test.ts`. The test reads the plugin's compiled `dist/settings.js` and `dist/session/orchestrator.js`, but the plugin had no `pretest` build and was absent from `ensure-test-artifacts.mjs`, so on a fresh checkout `dist/` did not exist and the freshness guard threw "dist/ is missing — run pnpm build first". Register the plugin's required artifacts in `ensure-test-artifacts.mjs` and add a `pretest` hook that builds them, matching the other bundled plugins.
|
||||
- 68c4053: Fix the Droid runtime model discovery spawning a runaway storm of leaked `droid` processes.
|
||||
|
||||
`discoverDroidModels` invoked `droid models --json` / `droid model list --json`, but the droid CLI has no such commands — an unknown subcommand is parsed as a _prompt_, so each call launched a full agent session (a persistent `droid exec --stream-jsonrpc` backend) that never exited. The promise never settled and the process leaked; because the dashboard re-loads the droid extension on every chat-send, these piled up into dozens of orphaned `droid` processes.
|
||||
|
||||
Discovery now reads the catalog from `droid exec --help` (which lists `Available Models:` + `Custom Models:` and exits cleanly), parsed via the new `parseDroidModelsFromHelp` helper. A SIGKILL-on-timeout guard (`DROID_MODEL_DISCOVERY_TIMEOUT_MS`) ensures any wedged spawn is killed and the promise always settles, so a single discovery call can never leak a process again. Verified end-to-end against the real binary (46 models incl. custom, 0 leaked processes).
|
||||
|
||||
- 9101705: Show plugin-contributed skills (e.g. compound-engineering `ce-*`) in the workflow editor. The dashboard's discovered-skills catalog was built only from the disk-scanning package manager, so plugin skills — which the engine materializes for executor sessions separately — never appeared, and built-in workflow nodes that reference them (like `builtin:compound-engineering`) showed "— select skill —" / unresolved. The skills adapter now merges plugin skill contributions into the discovered list (deduped by bare name), and the editor's node summary + skill dropdown match namespaced skillNames (`compound-engineering:ce-work`) against the catalog's two-segment names (`ce-work/SKILL.md`) via a shared bare-name normalizer.
|
||||
- 3b61ac3: Fix loading spinners that didn't spin across the dashboard. Many loading states (Settings, task tabs, agents, documents, plugins, model pickers, command center, and more) rendered bare "Loading…" text with no spinner — and a couple rendered an unstyled `loading-spinner` div that never showed anything. Added a shared `<LoadingSpinner>` component (self-contained animated SVG, no `lucide-react` dependency so it survives partial test mocks) and adopted it across ~45 loading placeholders so every loading state now shows a consistent animated spinner.
|
||||
- d99246c: Fix macOS system memory usage reporting by deriving host memory used from OS-available memory instead of raw `os.freemem()` pages.
|
||||
- 438cd75: Fix worktree-creation failures (and the `Workflow graph terminated with failure at node 'execute'` they surface as) caused by leaked orphan worktree directories.
|
||||
|
||||
A directory under `.worktrees/` that survives with a _dangling_ `.git` pointer — present on disk, but the `.git/worktrees/<name>` admin entry it references is gone — is invisible to `git worktree list` and untouched by `git worktree prune`, yet collides with a freshly generated worktree name. When the executor then tries to clean up the "conflict", `git worktree remove --force` fails with `is not a working tree` and the whole `execute` node fails after 3 attempts.
|
||||
|
||||
- **On-demand recovery (`executor.ts`):** the FN-4813 stale-conflict recovery now also treats `is not a working tree` and `ENOENT` (not just `validation failed, cannot remove working tree`) as "no live worktree at this path" — it prunes any admin entry, force-removes the leftover directory, and proceeds with fresh worktree creation instead of failing.
|
||||
- **Leak prevention (`worktree-pool.ts`):** `reapOrphanWorktrees` previously skipped any dir on the mere _presence_ of a `.git` file ("may be partially registered"), contradicting its own documented invariant. It now resolves the `.git` pointer and only skips when the gitdir target actually exists; a dangling pointer is reaped like any other half-initialized orphan, so these directories no longer accumulate across runs.
|
||||
|
||||
- 9643563: Fix the global pause/resume failure mode that stalled the board: a pause-abort that left a task back in `todo` was parked `status:"failed"` ("operator action required") and leaked its in-memory worktree slot, producing an instant re-fail retry storm and concurrency-starving the whole queue.
|
||||
|
||||
- Root cause: `handleGraphFailure` now treats a pause-abort that has re-queued a task to `todo` as benign (FN-6782) — it no longer parks it failed, clears the `pausedAborted` marker so the next dispatch starts clean, and releases the leaked worktree slot.
|
||||
- Auto-recovery: a new `recoverPausedAbortFailures` self-healing sweep clears any pause-abort park (`status:"failed"` with "operator action required") still on the board and requeues it for normal scheduling, so the board self-heals without operator intervention.
|
||||
- Defense-in-depth: a new `reapLeakedConcurrencySlots` self-healing sweep reclaims any in-memory worktree slot whose holder is no longer in-progress (the "in todo yet still a `maxWorktrees` holder" leak), gated by the executor's live-session refusal so it can never pull a worktree out from under a running agent. This recovers a leaked slot from any future/unknown path without an engine restart.
|
||||
|
||||
- 24ff124: Stop edits to `scripts/lib/test-quarantine.json` from forcing `pnpm test` into gate mode. The quarantine list is runtime data, not executable test infra; tripping the shared-infra catch-all dropped affected-package coverage, so a dev's real changes went untested whenever they also touched the quarantine list. Quarantine edits now stay in changed mode and run the affected packages.
|
||||
- a2342ca: Fix the task detail chat always showing "No agent is working on this task" for in-progress tasks. The active-session check required a persistent `assignedAgentId`/`checkedOutBy`, but in the default ephemeral-agents mode the scheduler never sets those fields, so an actively-executing task always read as idle. An assignment is now sufficient-but-not-necessary: a non-blocked, non-`queued` in-progress task counts as a live agent session on its own (`queued` stays assignment-gated, in-review is unchanged).
|
||||
- 7e7eb62: Harden the orphan-worktree and stale-task-dir cleanup fixes (code-review follow-up).
|
||||
|
||||
- **executor.ts (P0):** the stale-conflict recovery's `rm(worktreePath, { recursive, force })` had no bounds check. `worktreePath` can originate from a git worktree admin entry that resolves outside `.worktrees/`, so an out-of-bounds or symlinked path could be force-removed. The recovery now refuses unless the path is inside the configured worktrees dir, is not a symlink (checked via `realpathSync`), is not a registered git worktree, and is not actively owned — and it re-verifies liveness inside the catch rather than trusting the error string. It also excludes `spawn` failures (e.g. `spawn git ENOENT` when git is missing) so a missing-binary error is no longer misread as a successful stale-path cleanup.
|
||||
- **worktree-pool.ts:** `resolveGitdirPointer` is replaced by `dotGitPointerIsDangling`, which reaps **only** when a `.git` link's gitdir target is confirmed missing. A real `.git` directory, an unparseable pointer, or any read/stat failure now returns "not dangling" (conservative) so a transient read error on a live worktree's `.git` can't cause a force-remove. Removes the `string | "directory" | null` sentinel union.
|
||||
- **core store.ts:** the `reconcileOrphanedTaskDirs` recency window is now bypassed when the live task table is empty (the corruption/restore case — surviving `task.json` files keep old mtimes), and when a corrupt `fusion.db` was auto-recovered on startup, so `.recover` row loss is not stranded by the gate. Adds an `ignoreRecencyWindow` option for explicit callers.
|
||||
- Tests for all of the above: executor recovery + out-of-bounds refusal, unparseable `.git` skip, recency boundary, empty-DB/forced bypass.
|
||||
|
||||
- 87f18f8: Track real plugin activation events and surface project-scoped Command Center plugin activation analytics instead of placeholder ecosystem counts.
|
||||
- ee72c94: Move the Command Center Overview SDLC throughput funnel to the bottom of the tab and broaden hand-rolled chart primitive colors to cycle through existing semantic theme tokens.
|
||||
- 8f052c6: Fix Command Center Activity trend charts so mixed-unit agent/activity series stay visually legible instead of being flattened by high-volume message counts.
|
||||
- df139ec: Recover in-progress tasks wedged behind stale in-memory executor bindings by clearing the phantom binding and requeueing with progress and worktree preserved.
|
||||
- e6f6111: Fix terminal shortcut focus preservation so on-screen Ctrl combinations emit control bytes reliably on touch and pointer devices while keeping physical Ctrl behavior intact.
|
||||
- d4d7623: Rebaseline the dashboard i18n lint guardrail by excluding non-shipping tests and stories, suppressing technical token categories, localizing plugin missing-view copy, and tracking remaining source-copy deferrals with narrow follow-up tasks.
|
||||
- 98720f3: Fix mobile bottom tab navigation icon spacing so every tab uses an equal-width column across optional tabs, badges, and status dots.
|
||||
- c4f34ce: Make the Agents view sidebar wider by default on tablet and resizable with per-project persistence on non-mobile layouts.
|
||||
- b760fa0: Localize remaining plugin, agent, mission, node, research, document, activity, and miscellaneous dashboard strings and remove their i18n lint deferrals.
|
||||
- bdf95f8: Localized the dashboard workflow/task/setup/PR component cluster and removed the obsolete i18n lint deferrals for those files so the hardcoded-string guardrail scans them again.
|
||||
- eca96fb: Keep settings section dashboard copy covered by i18n lint by removing the settings/sections deferral and regenerating i18n resource types.
|
||||
- c808177: Eliminate the legacy board flash before workflow lanes load by caching per-project board workflow metadata and showing a neutral skeleton while metadata resolves.
|
||||
- 0c0fda1: Keep Command Center inline next to Agents across desktop and tablet header widths instead of moving it into the More views overflow menu.
|
||||
- c32c925: Repair task-store startup and self-healing consistency by non-destructively re-importing orphaned live `.fusion/tasks/{ID}/task.json` records into the SQLite task index while preserving soft-deleted, archived, and tombstoned IDs.
|
||||
- d2fc70a: Fix dependency gating so workflow-graph and workflow-authoritative executor dispatches re-check unmet task dependencies before running, requeueing blocked work with `blockedBy` instead of allowing it to advance to review.
|
||||
|
||||
Add self-healing reconciliation for already-advanced `in-review` tasks with unmet dependencies, including the `task:reconcile-in-review-unmet-dependencies` run-audit event and guarded no-action companion.
|
||||
|
||||
- 08d1f09: Recover benign in-review pause/resume abort parks without requiring operator intervention while preserving hard-cancel, pause, and terminal merge safeguards.
|
||||
- 61ff17a: Harden in-review dependency drift reconciliation so guard-held or failed rebounds emit no-action audit evidence instead of silently wedging dependent tasks.
|
||||
- 26bd85d: Fix mobile bottom navigation icon alignment so unread indicators use a centered token-sized icon slot without visually skewing tab spacing.
|
||||
- 37c4cfa: Prevent bundled Droid and Claude CLI auth/presence probes from surfacing unhandled promise rejections when `spawn` throws synchronously, such as when test guards block real AI CLI auth commands. These probes now resolve as unavailable/unauthenticated instead of rejecting from fire-and-forget validation paths.
|
||||
- 185ff70: Fix the experimental left sidebar Settings button so it remains clear of the fixed executor status footer, and keep project-selector fallback labels readable when translations are incomplete.
|
||||
- c7b56a5: Stop triage and planning prompts from auto-selecting alternate workflows based on task type; agents now preserve the project default workflow unless the user explicitly requests a specific workflow.
|
||||
- c18e827: Await CLI extension cached TaskStore shutdown so deferred filesystem writes and SQLite handles drain before fixture or process cleanup.
|
||||
- 8c478ad: Fix stale board entries after dependency-driven task re-specification moves by syncing the watched task cache after `updateTaskDependencies` writes and defensively deduplicating `listTasks` rows so active task rows win over archived snapshots.
|
||||
- 47ba99a: Bump the internal @earendil-works pi SDK family from ^0.79.1 to ^0.79.9 for the CLI, dashboard, and engine packages.
|
||||
- 24c1c02: Fix dashboard toast text colors so Shadcn dark-mode success, info, and error notifications remain readable against their themed backgrounds.
|
||||
- 1f23a2e: Ensure bundled Droid CLI provider startup registers without waiting for local `droid` probes and harden binary probes so missing, guarded, or hanging spawns resolve to unavailable sentinels instead of delaying engine boot.
|
||||
- 15d427b: Move Planning Mode into the dashboard sidebar as a first-class embedded view while removing the desktop toolbar affordance.
|
||||
- 91971b6: Update the built-in compound-engineering workflow so its Review stage runs the `compound-engineering:ce-code-review` skill directly. The redundant generic reviewer seam node was removed, leaving the CE code-review gate as the sole review stage.
|
||||
- c4c8961: Tasks created from a selected non-default workflow lane now appear on that lane immediately instead of vanishing until the board-workflows metadata refetch catches up.
|
||||
- 4342172: Built-in compound-engineering workflow prompts now explicitly call out the `/ce-` skill slash command at each stage.
|
||||
- bb663a4: Improve bundled non-coding workflow prompts so marketing, lead-generation, and design runs produce structured deliverables, with content and design preview artifacts persisted for review.
|
||||
- f4d2fa2: Hide the dashboard AI subtask-breakdown quick-add button behind the default-off `subtaskBreakdown` experimental feature flag.
|
||||
- 5191e1f: Prevent the bundled Droid CLI extension from starting local `droid` probes during server boot; validation now runs only when a Droid stream is actually used while existing probe paths remain non-interactive and timeout-bounded.
|
||||
- 4879996: Restyle the workflow switcher trigger and dropdown to visually match the project selector.
|
||||
- ec1d29e: Prevent task worktree acquisition from returning the project repository root by enforcing a non-root postcondition across resume, pooled, and fresh checkout paths.
|
||||
- c229a15: Tighten agent workflow-routing prompt policy so triage and executor agents must not move a task's workflow unless the user explicitly requested it or the agent created that task. Executor prompts now include an explicit `fn_workflow_select` guardrail while preserving workflow selection for tasks agents create.
|
||||
- 849b40d: Keep workflow IR and effective-settings resolution usable when project identity lookup fails, falling back to declaration defaults instead of propagating the identity error.
|
||||
- 9218613: Fix auto-merge lifecycle finalization so successful squash commits reliably leave tasks done, clear transient auto-merge state, and preserve actionable failure state when lifecycle updates fail.
|
||||
- 6e563b9: Revalidate dashboard service-worker assets before falling back to cache so rebuilt tabs cannot stay on stale bundles and render a blank page.
|
||||
- a1cac3a: Replace the compact Quick Chat implementation with the full Chat modal launcher and configurable footer/FAB/off setting, move the file browser into the shared floating-window shell with a compact New menu and consistent narrow editor toolbar, fit the dependency graph after layout settles, and align chat/mailbox/task-detail expansion plus header/theme polish.
|
||||
- 67281fe: Fix Import from GitHub remote detection in multi-project dashboards by passing the active `projectId` to the `/api/git/remotes` lookup. The dialog now lists configured GitHub remotes instead of showing "No GitHub remotes detected" when the backend requires project scope.
|
||||
- a147a98: Prevent global settings updates from overwriting an existing unreadable settings file with defaults, and use provider/CPU icons in task chat agent headers.
|
||||
- e788537: Raise the minimum agent heartbeat staleness floor from 5 to 10 minutes. Agents go silent during long-running but legitimate work (notably a verification step running a multi-minute test command, where the agent is blocked awaiting the command and cannot tick/heartbeat). The 5-minute floor could misread such a busy agent as dead and reclaim its in-progress task mid-run; 10 minutes gives long operations room before the liveness gate acts.
|
||||
- 4ed84be: Polish mobile workflow header alignment, task chat provider icons, modal overlay chrome, and shadcn font consistency.
|
||||
- a6685b7: Check for duplicate tasks from the New Task dialog and show duplicate descriptions in the warning modal.
|
||||
- f0fbc59: Update first-run onboarding to include an optional first-agent step and clearer temporary-agent task guidance.
|
||||
- 36b8950: Carry the selected workflow lane through Planning Mode and Subtask Breakdown task creation so saved tasks appear on the active workflow instead of falling back to the main board.
|
||||
- 93017a3: Preserve task progress when a single-session run is hard-cancelled mid-execution. When the engine aborted in-flight work and bounced the task back to `todo`, the single-session teardown cleared the task `branch` and re-queued without `preserveResumeState` — resetting every step to `pending` and dropping the pointer to commits already on the task branch, so the next dispatch re-planned from Step 0 and the committed work was stranded (observed as a task that "lost all progress" and got stuck). The teardown now keeps the branch and moves with `preserveResumeState` whenever the task has resumable step progress, matching the step-session and pause-park paths, so execution resumes onto the existing branch from the first incomplete step. The worktree is still removed to free its concurrency slot — only the durable pointers (branch + step state) are kept.
|
||||
- 192a2f2: Preserve unrelated global settings when saving Settings sections, and graduate Chat Rooms, Goals, Memory, Insights, Skills, and Todo to default-on dashboard surfaces.
|
||||
- 2e3b965: Smooth the mobile Quick Chat fullscreen sheet during Android soft-keyboard viewport resizing while preserving synchronous iOS visualViewport alignment.
|
||||
- b9b9447: Reset a task's stuck-kill streak on genuine forward progress. `stuckKillCount` was a lifetime counter — incremented by self-healing on each stuck-kill and cleared only by a manual retry — so a long, genuinely-progressing task could be terminalized by accumulation toward the stuck-kill budget. It now resets when a step reaches a terminal forward status (done/skipped), so only consecutive no-progress stalls count toward the budget.
|
||||
- 192a2f2: Open task-card files changed actions in the inline task detail Changes tab instead of the task modal.
|
||||
- 5e55d9c: Show provider icons in task detail chat for default-backed executor, reviewer, planner, and merger models.
|
||||
- 19be91c: Floating modals (the reusable FloatingWindow, the right-dock pop-out, the floating terminal, and the floating New Task dialog) now share a single z-index stack, so tapping any of them brings it to the front above all the others regardless of type.
|
||||
- 65c4dc5: Graduate workflow columns and the workflow graph executor to the default runtime path.
|
||||
|
||||
Upgrade notes: stale persisted `experimentalFeatures.workflowColumns` and `experimentalFeatures.workflowGraphExecutor` values are ignored by the engine, so prior installs keep dispatching tasks through the workflow runtime after upgrade. `workflowInterpreterDualObserve` remains an internal diagnostic and defaults off.
|
||||
|
||||
If an upgraded project appears stalled, treat `todo` tasks with unmet dependencies, `paused`/`userPaused`, active checkout leases, unavailable assigned nodes, or file-scope overlap as intentionally parked. Eligible `todo` tasks without those blockers should be picked up by the workflow scheduler; eligible `in-progress` rows without a live executor are recovered through the normal orphan-resume/self-healing path. The old Experimental toggles are no longer a rollback switch; use a source rollback/downgrade to the previous release if the workflow runtime itself must be reverted.
|
||||
|
||||
### runfusion.ai
|
||||
|
||||
#### Patch Changes
|
||||
|
||||
- Updated dependencies [c8a82e7]
|
||||
- Updated dependencies [ee9c8ab]
|
||||
- Updated dependencies [ce6c0fb]
|
||||
- Updated dependencies [7635ba8]
|
||||
- Updated dependencies [26e5514]
|
||||
- Updated dependencies [ce90cc9]
|
||||
- Updated dependencies [130fea2]
|
||||
- Updated dependencies [5a422b0]
|
||||
- Updated dependencies [2d32760]
|
||||
- Updated dependencies [b564ee0]
|
||||
- Updated dependencies [8b5b9a7]
|
||||
- Updated dependencies [68c4053]
|
||||
- Updated dependencies [9101705]
|
||||
- Updated dependencies [3b61ac3]
|
||||
- Updated dependencies [d99246c]
|
||||
- Updated dependencies [438cd75]
|
||||
- Updated dependencies [9643563]
|
||||
- Updated dependencies [24ff124]
|
||||
- Updated dependencies [a2342ca]
|
||||
- Updated dependencies [7e7eb62]
|
||||
- Updated dependencies [87f18f8]
|
||||
- Updated dependencies [70cce18]
|
||||
- Updated dependencies [8dd9697]
|
||||
- Updated dependencies [ee72c94]
|
||||
- Updated dependencies [f13aaa1]
|
||||
- Updated dependencies [8f052c6]
|
||||
- Updated dependencies [df139ec]
|
||||
- Updated dependencies [e6f6111]
|
||||
- Updated dependencies [c158dda]
|
||||
- Updated dependencies [d4d7623]
|
||||
- Updated dependencies [52924ba]
|
||||
- Updated dependencies [7f3e942]
|
||||
- Updated dependencies [281ce35]
|
||||
- Updated dependencies [98720f3]
|
||||
- Updated dependencies [c4f34ce]
|
||||
- Updated dependencies [b760fa0]
|
||||
- Updated dependencies [bdf95f8]
|
||||
- Updated dependencies [eca96fb]
|
||||
- Updated dependencies [c808177]
|
||||
- Updated dependencies [fbce59b]
|
||||
- Updated dependencies [af06170]
|
||||
- Updated dependencies [ef48895]
|
||||
- Updated dependencies [0c0fda1]
|
||||
- Updated dependencies [c32c925]
|
||||
- Updated dependencies [d2fc70a]
|
||||
- Updated dependencies [08d1f09]
|
||||
- Updated dependencies [61ff17a]
|
||||
- Updated dependencies [26bd85d]
|
||||
- Updated dependencies [37c4cfa]
|
||||
- Updated dependencies [58f7588]
|
||||
- Updated dependencies [185ff70]
|
||||
- Updated dependencies [c7b56a5]
|
||||
- Updated dependencies [c18e827]
|
||||
- Updated dependencies [8c478ad]
|
||||
- Updated dependencies [47ba99a]
|
||||
- Updated dependencies [24c1c02]
|
||||
- Updated dependencies [f80a785]
|
||||
- Updated dependencies [09acfbb]
|
||||
- Updated dependencies [1f23a2e]
|
||||
- Updated dependencies [4fec139]
|
||||
- Updated dependencies [5b33da9]
|
||||
- Updated dependencies [15d427b]
|
||||
- Updated dependencies [7034b55]
|
||||
- Updated dependencies [91971b6]
|
||||
- Updated dependencies [a913881]
|
||||
- Updated dependencies [c4c8961]
|
||||
- Updated dependencies [4342172]
|
||||
- Updated dependencies [bb663a4]
|
||||
- Updated dependencies [7fd14eb]
|
||||
- Updated dependencies [f4d2fa2]
|
||||
- Updated dependencies [5191e1f]
|
||||
- Updated dependencies [4879996]
|
||||
- Updated dependencies [ec1d29e]
|
||||
- Updated dependencies [c229a15]
|
||||
- Updated dependencies [849b40d]
|
||||
- Updated dependencies [9218613]
|
||||
- Updated dependencies [6e563b9]
|
||||
- Updated dependencies [496167c]
|
||||
- Updated dependencies [a1cac3a]
|
||||
- Updated dependencies [eb3477a]
|
||||
- Updated dependencies [59d3eee]
|
||||
- Updated dependencies [2dc36d9]
|
||||
- Updated dependencies [67281fe]
|
||||
- Updated dependencies [a147a98]
|
||||
- Updated dependencies [e788537]
|
||||
- Updated dependencies [7ef3817]
|
||||
- Updated dependencies [7ddf58d]
|
||||
- Updated dependencies [8640a74]
|
||||
- Updated dependencies [4ed84be]
|
||||
- Updated dependencies [a6685b7]
|
||||
- Updated dependencies [f0fbc59]
|
||||
- Updated dependencies [36b8950]
|
||||
- Updated dependencies [4fd8d44]
|
||||
- Updated dependencies [93017a3]
|
||||
- Updated dependencies [91180fb]
|
||||
- Updated dependencies [192a2f2]
|
||||
- Updated dependencies [da5fea6]
|
||||
- Updated dependencies [e19f7c2]
|
||||
- Updated dependencies [b20a25c]
|
||||
- Updated dependencies [4672203]
|
||||
- Updated dependencies [12aae94]
|
||||
- Updated dependencies [dc0064b]
|
||||
- Updated dependencies [5697d2c]
|
||||
- Updated dependencies [2e3b965]
|
||||
- Updated dependencies [5117944]
|
||||
- Updated dependencies [b9b9447]
|
||||
- Updated dependencies [192a2f2]
|
||||
- Updated dependencies [5e55d9c]
|
||||
- Updated dependencies [19be91c]
|
||||
- Updated dependencies [d4e91d4]
|
||||
- Updated dependencies [65c4dc5]
|
||||
- @runfusion/fusion@0.45.0
|
||||
|
||||
## 0.44.0
|
||||
|
||||
### @fusion/dashboard
|
||||
@@ -9322,6 +9768,32 @@ for reference.
|
||||
- Updated dependencies [a2ed6d0]
|
||||
- @runfusion/fusion@0.1.0
|
||||
|
||||
## 0.39.10
|
||||
|
||||
### @fusion/i18n
|
||||
|
||||
#### Patch Changes
|
||||
|
||||
- @fusion/core@0.47.0
|
||||
|
||||
## 0.39.9
|
||||
|
||||
### @fusion/i18n
|
||||
|
||||
#### Patch Changes
|
||||
|
||||
- @fusion/core@0.46.0
|
||||
|
||||
## 0.39.8
|
||||
|
||||
### @fusion/i18n
|
||||
|
||||
#### Patch Changes
|
||||
|
||||
- Updated dependencies [26ebb92]
|
||||
- Updated dependencies [7e7eb62]
|
||||
- @fusion/core@0.45.0
|
||||
|
||||
## 0.39.7
|
||||
|
||||
### @fusion/i18n
|
||||
@@ -9378,6 +9850,30 @@ for reference.
|
||||
|
||||
- @fusion/core@0.40.0
|
||||
|
||||
## 0.11.36
|
||||
|
||||
### @fusion/droid-cli
|
||||
|
||||
#### Patch Changes
|
||||
|
||||
- @fusion-plugin-examples/droid-runtime@0.1.36
|
||||
|
||||
## 0.11.35
|
||||
|
||||
### @fusion/droid-cli
|
||||
|
||||
#### Patch Changes
|
||||
|
||||
- @fusion-plugin-examples/droid-runtime@0.1.35
|
||||
|
||||
## 0.11.34
|
||||
|
||||
### @fusion/droid-cli
|
||||
|
||||
#### Patch Changes
|
||||
|
||||
- @fusion-plugin-examples/droid-runtime@0.1.34
|
||||
|
||||
## 0.11.33
|
||||
|
||||
### @fusion/droid-cli
|
||||
|
||||
22
CONCEPTS.md
22
CONCEPTS.md
@@ -54,6 +54,17 @@ A Feature auto-generated from a failed Validator Run to carry the remediation wo
|
||||
### Project
|
||||
A registered workspace that Fusion can operate on: it has a canonical local path, project-scoped settings and data, and must be backed by a usable Git work tree before task execution can create worktrees from it.
|
||||
|
||||
A **workspace** is a special Project variant where the registered path is not
|
||||
itself a Git repository, but contains multiple Git repositories as direct
|
||||
sub-directories. Fusion discovers sub-repos at init time and records them in
|
||||
`.fusion/workspace.json`. In workspace mode, task execution does not require a
|
||||
single root-level worktree; instead, the agent acquires per-repo worktrees
|
||||
on demand via `fn_acquire_repo_worktree`.
|
||||
|
||||
Workspace-task merges are **non-atomic**: each sub-repo lands on its own local
|
||||
integration ref independently, so a partial-land window (some sub-repos merged,
|
||||
others not) is possible mid-task — this state is local and operator-resettable.
|
||||
|
||||
### Project Identity
|
||||
The durable identity a registered Project carries locally so it can be reattached to the central registry after central state is lost or rebuilt, preserving rows keyed by the same project id instead of minting a replacement.
|
||||
|
||||
@@ -85,6 +96,9 @@ The authoritative task lifecycle runtime. It resolves a Task to workflow IR, wal
|
||||
### Engine Singleton Lock
|
||||
A per-machine mutual-exclusion guard ensuring only one fusion process runs the engine for a given project, combining a lockfile in the project's `.fusion/` directory with a per-project loopback socket. Failure to acquire it (`EngineAlreadyRunningError`) is **positive proof an engine is already running** for that project elsewhere on the machine — not an error to swallow and not "no engine." A process refused the lock keeps that as a fact: it reports the engine as available (so UI surfaces don't claim it's down) while reconciliation keeps retrying, so it takes over if the current owner exits.
|
||||
|
||||
### Active-session lease
|
||||
A path-keyed, in-memory claim that a given worktree path is held by a specific Task's running session (executor, step, workflow-step, AI-merge, or a workspace sub-repo acquire/land). It serves two jobs at once: mutual exclusion (a second Task may not register a path already held by a different Task — the foreign-task guard) and liveness (self-healing treats a held path as proof the Task is actively running and must not be rebounded). The key is the path, so the registry is only as correct as the path chosen: a path uniquely owned by one Task gives real exclusivity, but a path shared across Tasks (e.g. a workspace's browse-only root) must be made Task-scoped before registration or the guard will reject every concurrent sibling. Re-registration by the same Task is idempotent; cleanup must unregister the exact key that was registered.
|
||||
|
||||
### ACP Ask Path
|
||||
A one-turn read-only model ask routed through the ACP runtime rather than a CLI print mode. The runner accumulates streamed prose, may recover a trailing JSON object for structured seams, and treats abnormal ACP stop reasons as incomplete answers for validator use.
|
||||
|
||||
@@ -244,12 +258,20 @@ A persisted crash-safe marker (`tasks.transitionPending`) written in the same tr
|
||||
### Step instance
|
||||
One runtime expansion of a `foreach` template subgraph, bound to a single planned step (`Task.steps[i]`). Identity is deterministic — `<foreachNodeId>#<stepIndex>:<templateNodeId>` — 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, 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:<pluginId>:<parserId>`. 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.
|
||||
|
||||
### Custom task field
|
||||
A workflow-declared, typed task field (`string | text | number | boolean | enum | multi-enum | date | url`, with enum options and render hints) whose values live in `tasks.customFields`, keyed by field id. The task model is thereby recast as core fields (title, description) + standard metadata + these workflow-defined fields. Writes pass through a single store authority (`updateTaskCustomFields`) that validates each value against the resolving workflow's schema and returns typed rejections (offending `fieldId` + `code`); agents write them via `fn_task_update`'s `custom_fields` patch. Editing a workflow's fields or switching a task's workflow orphans (never destroys) values for removed or type-incompatible ids — orphans are retained and surfaced under a detail disclosure, excluded from cards. Same id means the same field within a project; there is no cross-workflow shared field namespace.
|
||||
|
||||
### Optional step group
|
||||
A workflow graph container node (alongside `foreach`/`loop`) whose template subgraph runs once when a task has enabled it and is bypassed otherwise — the graph-native way to make a step optional per task. Enablement is a per-task toggle set seeded from the group's workflow-level default; the group's own node id is the toggle key. It replaces the earlier execution-inert *declaration* model (a separate optional-step list run through a hidden seam), so optional steps are now real, placeable nodes rather than an out-of-graph facet.
|
||||
|
||||
Single pass — no iteration or rework inside the template (this is what distinguishes it from `foreach`/`loop`). Because the toggle key is the node id, renaming or recreating a group resets its per-task enablement; and because that id may deliberately equal a built-in step-template id, the per-task enable set must keep group ids identity-stable rather than round-tripping them through legacy step-template materialization (which would remap the key and silently bypass the group).
|
||||
|
||||
## Persistence & migrations
|
||||
|
||||
### Schema-Version Sweep
|
||||
|
||||
20
README.md
20
README.md
@@ -313,14 +313,18 @@ A built-in mailbox for delegation, clarification, and hand-offs. Agents file tri
|
||||
|
||||
The full board, Command Center, missions, agents, and chat travel with you — native **iOS** and **Android** apps (Capacitor) plus an installable PWA. Start a run on your laptop, steer it from your phone.
|
||||
|
||||
<div align="center">
|
||||
<img src="./demo/assets/mobile-board.png" alt="Fusion mobile: board" width="180" />
|
||||
<img src="./demo/assets/mobile-command-center.png" alt="Fusion mobile: Command Center" width="180" />
|
||||
<img src="./demo/assets/mobile-missions.png" alt="Fusion mobile: missions" width="180" />
|
||||
<img src="./demo/assets/mobile-agents.png" alt="Fusion mobile: agents" width="180" />
|
||||
<img src="./demo/assets/mobile-chat.png" alt="Fusion mobile: agent chat" width="180" />
|
||||
<img src="./demo/assets/mobile-chat-list.png" alt="Fusion mobile: chat list" width="180" />
|
||||
</div>
|
||||
<table>
|
||||
<tr>
|
||||
<td width="33%"><img src="./demo/assets/mobile-board.png" alt="Fusion mobile: board" /></td>
|
||||
<td width="33%"><img src="./demo/assets/mobile-command-center.png" alt="Fusion mobile: Command Center" /></td>
|
||||
<td width="33%"><img src="./demo/assets/mobile-missions.png" alt="Fusion mobile: missions" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="33%"><img src="./demo/assets/mobile-agents.png" alt="Fusion mobile: agents" /></td>
|
||||
<td width="33%"><img src="./demo/assets/mobile-chat.png" alt="Fusion mobile: agent chat" /></td>
|
||||
<td width="33%"><img src="./demo/assets/mobile-chat-list.png" alt="Fusion mobile: chat list" /></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<sub>See [MOBILE.md](./MOBILE.md) for the Capacitor + PWA workflow.</sub>
|
||||
|
||||
|
||||
20
RELEASING.md
20
RELEASING.md
@@ -20,7 +20,24 @@ This will prompt you to:
|
||||
- Choose the semver bump type (patch, minor, major)
|
||||
- Write a summary of the change
|
||||
|
||||
A markdown file will be created in the `.changeset/` directory. Commit this file along with your code changes.
|
||||
Then edit the created changeset file to use the structured body format:
|
||||
|
||||
```markdown
|
||||
---
|
||||
"@runfusion/fusion": minor
|
||||
---
|
||||
|
||||
summary: Add a Command Center productivity control for LOC backfills.
|
||||
category: feature
|
||||
dev: Uses the new `fn_backfill_loc` tool; settings key `commandCenter.locBackfill`.
|
||||
```
|
||||
|
||||
Fields:
|
||||
- `summary` (required) — one line, user-facing, max 120 chars.
|
||||
- `category` (required) — one of: `feature`, `fix`, `breaking`, `security`, `performance`, `internal`.
|
||||
- `dev` (optional) — developer/migration detail.
|
||||
|
||||
A markdown file will be created in the `.changeset/` directory. Commit this file along with your code changes. Validate with `pnpm check:changesets`.
|
||||
|
||||
### 2. Version PR is created automatically
|
||||
|
||||
@@ -29,6 +46,7 @@ When changesets are merged to `main`, the `version.yml` workflow automatically o
|
||||
- Consumes all pending changeset files
|
||||
- Bumps package versions according to the changeset declarations
|
||||
- Generates/updates `CHANGELOG.md` files for affected packages
|
||||
- Distills the version's changeset summaries into grouped, end-user-facing release notes in the root `CHANGELOG.md`
|
||||
|
||||
### 3. Merge the Version PR to release
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ For a full walkthrough (installation, onboarding, first task, and daily workflow
|
||||
| Guide | Description |
|
||||
|---|---|
|
||||
| [Getting Started](./getting-started.md) | Installation, first-run, first task, and daily workflow basics |
|
||||
| [Dashboard Guide](./dashboard-guide.md) | Board/list views, chat, workflow selection/editor, terminal, git manager, files, planning, and UI tools |
|
||||
| [Dashboard Guide](./dashboard-guide.md) | Board/list views, left/right sidebar navigation, Artifacts, Import Tasks, chat, workflow selection/editor, terminal, git manager, files, planning, and UI tools |
|
||||
| [CLI Reference](./cli-reference.md) | Complete `fn` command reference with subcommands, flags, and examples |
|
||||
| [Remote Access](./remote-access.md) | Operator runbook for Tailscale/Cloudflare setup, tokenized login links, security caveats, and troubleshooting |
|
||||
| [Native Shell Connection Guide](./native-shell.md) | Canonical mobile/desktop shell onboarding, profile management, QR/manual setup, and remote handoff behavior |
|
||||
|
||||
@@ -4,6 +4,11 @@
|
||||
|
||||
Fusion uses multiple agent roles for planning, execution, review, and merge workflows.
|
||||
|
||||
<!--
|
||||
FNXC:WorkflowRouting 2026-06-22-12:00:
|
||||
Agent-facing docs must preserve the workflow movement boundary: agents can assign workflows when the user explicitly asked or when creating a task, while executors cannot reroute the task under execution on their own initiative.
|
||||
-->
|
||||
|
||||
## CLI session actions
|
||||
|
||||
The dashboard's CLI session banner uses authenticated `POST /api/cli-sessions/:id/*` routes for task-bound CLI sessions. `POST /api/cli-sessions/:id/relaunch` is project-scoped, rejects sessions that do not have a `taskId`, records a relaunch intent, and lets the engine listener clear resume linkage before moving the owning task back to `todo` for a fresh executor launch. This route backs the `resume-exhausted` banner's **Relaunch fresh** action; when a session summary has no `cliSessionId`, the client does not call the route.
|
||||
@@ -27,6 +32,19 @@ fn chat <agent-id> [message…] [--once] [--non-interactive] [--poll-ms <n>]
|
||||
- Agent-acting session lanes share the same skill-injection contract as executor sessions: executor, merger, triage, reviewer, heartbeat, step-session, dashboard chat/room responders, CLI agent execution, planning, mission interview, milestone/slice interview, agent-onboarding interview, workflow design, memory dreams/insight extraction, and scheduled cron automation all request agent/fallback skills plus enabled plugin-contributed skills when a plugin runner is available. Utility-only lanes that only summarize/extract/generate JSON (title/PR summaries, memory compaction, subtask breakdown, text refinement, agent generation, PR metadata generation, evaluator/research synthesis, and similar one-shot helpers) intentionally stay exempt to avoid loading skills where no agent-style tool loop can use them.
|
||||
- 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 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
|
||||
|
||||
@@ -127,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:
|
||||
|
||||
@@ -692,6 +710,14 @@ Before clicking **Create**, the final review step remains editable for identity/
|
||||
|
||||
The final `createAgent(...)` call always uses the latest values from these step-2 controls.
|
||||
|
||||
### First-run setup first agent
|
||||
|
||||
After first-project registration, first-run setup asks whether to create a first persistent agent before entering the dashboard. The CEO preset is selected by default because this first agent is framed as an optional coordinator that can help create tasks and keep direction across sessions.
|
||||
|
||||
Users can choose a preset, create the project agent, or skip it and create agents later from the Agents view. Agents are optional for task work: Fusion still starts temporary agents to plan, code, review, and merge tasks.
|
||||
|
||||
When `experimentalFeatures.agentOnboarding` is enabled, first-run setup also offers the same draft-first **AI Interview** path used by the New Agent dialog. Applying the interview draft updates the setup preview, but persistence remains explicit through **Create Agent**.
|
||||
|
||||
### Experimental planning-style onboarding
|
||||
|
||||
The **New Agent** dialog is the canonical launch point for agent creation.
|
||||
|
||||
@@ -602,6 +602,7 @@ See [Memory Plugin Contract](./memory-plugin-contract.md) for the full plan.
|
||||
- **Executor**: `TaskExecutor` (`executor.ts`) implements tasks in worktrees
|
||||
- **Reviewer**: `reviewStep()` (`reviewer.ts`) performs plan/code/spec reviews
|
||||
- **Merger**: `aiMergeTask()` (`merger.ts`) merges approved work
|
||||
- **Task-detail chat / steering comments**: `TaskStore.addSteeringComment()` writes chat steering text to both `task.comments` and `task.steeringComments`. The executor still uses `steeringComments` for live in-session injection, while next-prompt agent lanes read canonical user-authored `task.comments`: planning/spec generation, spec review, plan/code reviewers, standard merger prompts, and clean-room AI merge + merge-review prompts all surface recent user comments through the shared `agent-user-comments.ts` formatter.
|
||||
|
||||
#### Reviewer verdict recovery contract (FN-4092)
|
||||
- Reviewer verdicts are `APPROVE`, `REVISE`, `RETHINK`, or `UNAVAILABLE`.
|
||||
@@ -681,6 +682,7 @@ Runtime action-gate flow (v1):
|
||||
- `recoverGhostReviewTasks()` is a fallback only for idle, non-terminal `in-review` states. Terminal/actionable states (notably `status: "failed"`) are preserved and **not** auto-kicked back to `todo`.
|
||||
- `recoverPausedAbortFailures()` clears executor pause/resume abort parks only when the durable row is safe to recover. `todo`/`in-progress` rows are requeued for normal scheduling, while clean `in-review` rows (completed steps, not paused/user-paused/executing, auto-merge eligible, no confirmed or terminal merge evidence) have `status`/`error` cleared in place so review progression can continue. User hard-cancel, global/user pause, `autoMerge:false`, terminal merge, and live-execution guards remain operator-actionable. Successful recovery emits `task:auto-recover-paused-abort-park` with `preservedInReview` metadata.
|
||||
- `reattach-orphaned-assigned-executions` is a forward-resume safety net for durable-agent assignments. During startup recovery and periodic maintenance, after orphaned-agent and stale-heartbeat-run repairs, self-healing finds `in-progress` tasks with an `assignedAgentId` whose agent has no active heartbeat run and no active executor session after the orphan grace window. It re-dispatches in place via `executor.resumeTaskForAgent(agentId)` (the same seam used by clean `HeartbeatMonitor.onRunCompleted` and guarded by executor double-execution checks), emits `task:reattach-orphaned-execution`, and never moves the task backward. This complements engine-start `executor.resumeOrphaned()` and leaves unassigned/role-based execution recovery to the existing startup/limbo/stuck-task paths.
|
||||
- Durable `Agent.taskId` is a running assignment for parked `todo`/`triage` task rows only when the agent has live proof: a fresh active heartbeat run or an executor-active/tracked heartbeat signal. Scheduler overlap requeues, task move sync, self-healing, and Reports Health Check share this invariant: stale durable links are cleared or rendered as stale while `status: "queued"` and `overlapBlockedBy` remain on the task row so file-scope lease blocking is not weakened.
|
||||
- Mission validation has a dedicated stale-run reaper: startup recovery and Batch 2 maintenance call `reapStaleMissionValidatorRuns()` when wired by the runtime, using `VALIDATOR_RUN_STALE_MAX_AGE_MS` (currently 6 hours). The sweep terminates ownerless `mission_validator_runs.status='running'` rows as `error`, writes the reap reason into `summary`, leaves `lastValidatorRunId` pointing at the now-terminal run, and emits run-audit telemetry with `mutationType: "mission:validator-run-reaped"` plus `runId`/`featureId`/`missionId`/`triggerType`/`elapsedMs` metadata. Active mission features move to `loopState="needs_fix"` + `lastValidatorStatus="error"` unless their parent mission is already `complete`/`archived`.
|
||||
|
||||
#### Stuck-loop exhaustion terminal contract
|
||||
@@ -860,9 +862,10 @@ Operator setup + troubleshooting guide: **[Remote Access runbook](./remote-acces
|
||||
Key server capabilities:
|
||||
- REST APIs for tasks, git, GitHub, agents, missions, planning, automations/routines, settings
|
||||
- System stats snapshot and vitest process controls APIs (`GET /api/system-stats`, `POST /api/kill-vitest`) exposing dashboard process/system telemetry (including app CPU percentage and host memory rendered as numeric values, radial gauges, and trend sparklines in the Command Center System area), task/agent aggregates, and manual vitest process termination. Host-memory usage is derived from shared OS-available memory (`process.availableMemory()` with an unreliable `freemem` fallback) rather than raw free pages so macOS inactive/cache memory is not counted as used.
|
||||
- Command Center analytics APIs (`GET /api/command-center/tokens`, `/tools`, `/activity`, `/productivity`, `/team`, `/github`, `/signals`, `/plugin-activations`, `/live`) are project-scoped dashboard routes. `/productivity` reads Lines changed from nullable `task_commit_associations.additions`/`deletions` merge-time diff stats, derives estimated `hoursSaved` from that LOC via the exported `HUMAN_LINES_PER_HOUR` rate, and keeps the unavailable sentinel for both fields when no in-range association has stats. Its `taskDuration` payload aggregates done tasks whose `executionCompletedAt` falls in the selected range, using positive `tasks.cumulativeActiveMs` values for completed count, average, median, p90, and total active execution time; missing qualifying durations remain unavailable rather than zero. `/signals` aggregates real local `incidents` rows for total/open/resolved counts, MTTR, and source/severity/status breakdowns and returns honest empty/unavailable sentinels instead of synthetic signal volume. `/plugin-activations` aggregates persisted plugin/extension load events for the selected range and returns unavailable when no rows exist instead of treating missing history as zero activations.
|
||||
- Command Center analytics APIs (`GET /api/command-center/tokens`, `/tools`, `/activity`, `/productivity`, `/team`, `/github`, `/signals`, `/plugin-activations`, `/live`) are project-scoped dashboard routes. `/productivity` reads Lines changed from nullable `task_commit_associations.additions`/`deletions` merge-time or backfilled diff stats, derives estimated `hoursSaved` from that LOC via the exported `HUMAN_LINES_PER_HOUR` rate, and keeps the unavailable sentinel for both fields when no in-range association has stats. `POST /api/command-center/productivity/backfill-loc` is the explicit operator-triggered, dry-run-defaulting local-git backfill for historical NULL stats; it is not run during dashboard rendering or analytics reads. Its `taskDuration` payload aggregates done tasks whose `executionCompletedAt` falls in the selected range, using positive `tasks.cumulativeActiveMs` values for completed count, average, median, p90, and total active execution time; missing qualifying durations remain unavailable rather than zero. `/signals` aggregates real local `incidents` rows for total/open/resolved counts, MTTR, and source/severity/status breakdowns and returns honest empty/unavailable sentinels instead of synthetic signal volume. `/plugin-activations` aggregates persisted plugin/extension load events for the selected range and returns unavailable when no rows exist instead of treating missing history as zero activations.
|
||||
<!-- FNXC:CommandCenter 2026-06-21-00:00: Maintainers need the pricing contract in architecture docs: MODEL_PRICING is hand-maintained, pricingAsOf changes with every rate edit, provider coverage includes OpenAI/Codex/Anthropic/Gemini, and Command Center never guesses or persists costs. -->
|
||||
- Model pricing & cost estimation: Command Center token cost is derived at read time by `packages/core/src/model-pricing.ts`, not fetched from providers at runtime and not persisted as billing truth. Maintainers update the hand-maintained `MODEL_PRICING` table in that file; keys are lowercased `${provider}:${model}` with a bare `:model` fallback for callers that only know the model id. Each entry stores USD per 1M tokens for input, output, cache-read, and cache-write plus a `source` citation. Bump `pricingAsOf` in the same change as any rate edit, because the dashboard surfaces it as the **prices as of** date and marks entries low-confidence after `PRICING_STALE_AFTER_MS` (approximately 180 days / two quarters) relative to that date. Unknown models resolve to `unavailable` rather than a guessed price. The table is curated from provider pricing pages for Anthropic, OpenAI including explicit `openai-codex:*` Codex ids, and Google Gemini; keep provider/model additions in that curated map rather than adding runtime pricing fetches.
|
||||
<!-- FNXC:CommandCenter 2026-06-22-00:00: FN-6876 made pricing operator-editable: user/global overrides and LiteLLM one-click refreshes must take precedence over the built-in table while remaining estimates, not persisted billing truth. -->
|
||||
- Model pricing & cost estimation: Command Center token cost is derived at read time by `packages/core/src/model-pricing.ts` and is not persisted as billing truth. Maintainers still update the built-in `MODEL_PRICING` fallback table in that file; keys are lowercased `${provider}:${model}` with a bare `:model` fallback for callers that only know the model id. Each entry stores USD per 1M tokens for input, output, cache-read, and cache-write plus a `source` citation. Bump `pricingAsOf` in the same change as any built-in rate edit, because the dashboard surfaces it as the **prices as of** date and marks entries low-confidence after `PRICING_STALE_AFTER_MS` (approximately 180 days / two quarters) relative to that date. Global `modelPricingOverrides` from Settings take precedence over built-ins using the same exact-key then bare-model lookup order; `POST /api/command-center/pricing/fetch` is the only dashboard network path and fetches LiteLLM's model pricing JSON on explicit user action, parses it through the pure core parser, persists the resulting overrides with fetched metadata, and leaves the prior overrides intact on fetch/parse failure. Unknown models resolve to `unavailable` rather than a guessed price.
|
||||
- Remote access APIs (`/api/remote/*`) for provider config, activation, tunnel lifecycle, status, token issuance, authenticated URL generation, and QR payload generation
|
||||
- Operational runbook (prereqs/security/troubleshooting): [`docs/remote-access.md`](./remote-access.md)
|
||||
- `/api/remote/tunnel/start`, `/api/remote/tunnel/stop`, and `/api/remote/tunnel/kill-external` cover tunnel lifecycle and external funnel cleanup.
|
||||
@@ -1089,6 +1092,7 @@ The run-audit system records every mutation performed by the engine across four
|
||||
- **Database / `task:no-commits-finalize-blocked-incomplete-steps`** — emitted by no-op finalize lanes when a `noCommitsExpected` task has no net branch changes but incomplete/skipped steps outweigh done steps. Metadata includes `{ reason, doneCount, incompleteCount, lane, classification?, baseRef? }`; the accompanying task log explains that the task was demoted to `todo` with progress preserved instead of finalized as done.
|
||||
- **Database / `task:orphan-detected-no-action`** — emitted by `recoverOrphanedExecutions` (FN-5337) when row metadata looks orphaned after grace windows; annotation-only event with no lifecycle mutation (`in-progress` task stays put).
|
||||
- **Database / `task:reattach-orphaned-execution`** — emitted by `reattachOrphanedAssignedExecutions` (FN-6336) when self-healing re-dispatches an idle assigned `in-progress` task forward via `executor.resumeTaskForAgent(agentId)` after proving the assigned agent has no active heartbeat run or active execution.
|
||||
- **Database / `task:reconcile-stale-agent-assignment`** — emitted when self-healing or heartbeat reconciliation clears stale durable `Agent.taskId`/`state` for a task parked in `todo`/`triage` without live execution proof. Metadata includes `{ agentId, taskId, taskColumn, agentState, status, blockedBy, overlapBlockedBy, hadFreshRun, hadActiveExecution, reason }`; task queue/lease fields are preserved.
|
||||
- **Database / `task:soft-delete-column-reconciled`** — emitted by `reconcileSoftDeletedColumnDrift` (FN-5566, re-land FN-5446) when a soft-deleted row (`deletedAt IS NOT NULL`) is found with legacy `column != 'archived'`; rewrites only `column` (no resurrection), with metadata `{ previousColumn }`.
|
||||
- **Database / `session:runtime-resolved`** — emitted once per `createResolvedAgentSession` call with metadata `{ sessionPurpose, runtimeId, wasConfigured, provider, modelId, mockProviderActive, testModeActive, runtimeHint? }` for per-lane runtime/provider attribution.
|
||||
- **Database / `task:reconcile-dependency-blocking-lease`** — emitted by `reconcileDependencyBlockingLeases()` (FN-6292) when self-healing rebounds an `in-progress` holder to `todo` because an unmet dependency is blocked by the holder's stale file-scope lease. Metadata includes the dependency ID, blocked-by marker, and unmet dependency list.
|
||||
@@ -1467,7 +1471,7 @@ Dashboard session-diff route registration (`packages/dashboard/src/routes/regist
|
||||
- `legacy` = recovered via legacy task-id/subject matching
|
||||
- `ambiguous` = manual reconciliation where historical task-id attribution could be misleading
|
||||
|
||||
Commit associations also carry optional `additions`/`deletions` shortstat counts captured by merge paths. These nullable fields are the Command Center Productivity LOC source: analytics sum additions + deletions only when at least one in-range row has stats, derive estimated human hours saved as `round((additions + deletions) / HUMAN_LINES_PER_HOUR, 1)`, and preserve the `—` unavailable sentinel for both LOC and hours saved when all matching rows are `NULL` so unknown historical data is never rendered as `0`. The hours-saved field is a conservative estimate, not exact time tracking.
|
||||
Commit associations also carry optional `additions`/`deletions` shortstat counts captured by merge paths or filled later by the explicit `POST /api/command-center/productivity/backfill-loc` operator backfill. These nullable fields are the Command Center Productivity LOC source: analytics sum additions + deletions only when at least one in-range row has stats, derive estimated human hours saved as `round((additions + deletions) / HUMAN_LINES_PER_HOUR, 1)`, and preserve the `—` unavailable sentinel for both LOC and hours saved when all matching rows are `NULL` so unknown historical data is never rendered as `0`. The backfill only touches rows where both columns are `NULL`; malformed SHAs and commit objects unavailable in the local repo stay `NULL`, so partial historical coverage remains visible until a real local git object supplies stats. The hours-saved field is a conservative estimate, not exact time tracking.
|
||||
|
||||
Command Center Productivity task-duration stats use task rows, not commit rows: done tasks completed in range (`executionCompletedAt`) contribute when `cumulativeActiveMs > 0`. The aggregator computes completed count plus average, median, p90, and total active execution milliseconds; if no qualifying task exists, the duration metrics use the same unavailable `—` contract instead of reporting `0`.
|
||||
|
||||
@@ -1679,7 +1683,7 @@ The GitHub tracking state listener now attaches to every registered project stor
|
||||
- New global surfacing adds `merger:autostashOrphans` TaskStore events, engine helpers (`listAutostashOrphans`, `getAutostashDiff`, `applyAutostashBySha`, `dropAutostashBySha`), and dashboard API endpoints under `/api/stash-recovery/*`.
|
||||
- `merger:autostashOrphans` records now include provenance fields (`sourcePhase`, `detectedByTaskId`, `detectedAt`) so operators can attribute leftovers to the merge phase and surfacing task/session.
|
||||
- `ProjectEngine` consumes the orphan event stream and auto-creates deduplicated `sourceType: "recovery"` follow-up tasks for live leftovers, so repeated detections do not spam the board.
|
||||
- Dashboard operators can inspect orphan counts, review diffs, apply stashes, and explicitly drop entries with confirmation.
|
||||
- Dashboard operators inspect orphan counts, review diffs, apply stashes, and explicitly drop entries with confirmation from **Git Manager → Recovery**; the recovery controls are part of Git Manager rather than a standalone top-level dashboard view.
|
||||
- Decision: recovery stays user-gated. Auto-apply was rejected because clean-tree checks are racy, stash placement is ambiguous after source task merge, and apply conflicts can produce hard-to-untangle state. `sweepAutostashOrphans` continues to auto-drop only subsumed entries while preserving live developer work.
|
||||
|
||||
#### Automated follow-up dedup (FN-5232)
|
||||
|
||||
@@ -231,7 +231,7 @@ Use the default lane for normal local iteration before PRs. Run `test:deep` when
|
||||
Fusion uses Changesets + version PR workflow.
|
||||
|
||||
- See [RELEASING.md](../RELEASING.md) for release flow details.
|
||||
- For published package behavior changes, include a changeset.
|
||||
- For published package behavior changes, include a changeset using the structured body format (`summary`, `category`, optional `dev` fields). See the changeset format guide in [`.changeset/README.md`](../.changeset/README.md).
|
||||
|
||||
## Code Signing
|
||||
|
||||
|
||||
@@ -6,6 +6,9 @@
|
||||
FNXC:CustomWorkflowReliability 2026-06-17-05:41:
|
||||
Goal G-MPW67VQR-0001-97S3 needs an end-to-end reliability acceptance map for the custom workflow system so authoring, selection, execution, recovery, and restart behavior can be verified by measurable criteria instead of ad hoc spot checks.
|
||||
This artifact distinguishes MVP/blocking requirements from nice-to-have enhancements and keeps implementation out of scope: confirmed gaps become focused follow-up tasks rather than product-code changes in this documentation task.
|
||||
|
||||
FNXC:WorkflowRouting 2026-06-22-12:00:
|
||||
Workflow selection acceptance must distinguish operator intent and task creator ownership from executor opportunism. Agents can assign workflows when the user asked or when creating the task; executors cannot reroute the task under execution unless instructed.
|
||||
-->
|
||||
|
||||
## Purpose
|
||||
@@ -49,9 +52,9 @@ Use this document to write engineering tasks, QA plans, and release checks. It i
|
||||
|
||||
### 3. Select a workflow for a task, board, or mission-derived feature task
|
||||
|
||||
- **Actor / need:** An operator or triage agent needs to route work through the intended workflow at task creation or before execution, including tasks that originate from mission features.
|
||||
- **Actor / need:** An operator or task-creating agent needs to route work through the intended workflow at task creation or before execution, including tasks that originate from mission features.
|
||||
- **Trigger:** Use the dashboard task/board workflow selector, task detail **Workflow** tab, `fn_workflow_select`, `workflow_id` on `fn_task_create` / delegation tools, or mission feature triage/linking surfaces such as `fn_feature_link_task` where the created/linked task carries a workflow selection.
|
||||
- **Expected happy path + lifecycle transitions + feedback:** Unselected tasks resolve to `builtin:coding`; explicitly selected workflows persist on the task before scheduler pickup; newly created tasks enter the normal planning/todo path for their selected workflow; mission goal provenance remains derived through the mission/feature hierarchy rather than copied onto the task row. The UI shows the selected workflow and offers **Edit workflow** in the task workflow context.
|
||||
- **Expected happy path + lifecycle transitions + feedback:** Unselected tasks resolve to `builtin:coding`; explicitly selected workflows persist on the task before scheduler pickup; agents select/change workflows only when the user explicitly requested the workflow or when they created the task; executors do not reroute the task under execution unless instructed by the user; newly created tasks enter the normal planning/todo path for their selected workflow; mission goal provenance remains derived through the mission/feature hierarchy rather than copied onto the task row. The UI shows the selected workflow and offers **Edit workflow** in the task workflow context.
|
||||
- **Failure / recovery expectation:** A missing or corrupt explicit custom workflow fails closed as a workflow-resolution failure instead of silently falling back to `builtin:coding`. Invalid workflow IDs supplied through tools reject with a clear validation error. Mission links must preserve their own linked-task guards; deleting mission hierarchy cannot silently drop live linked tasks.
|
||||
- **Measurable success signal:** The task record/tool output shows the selected workflow ID; task detail shows the workflow context; runtime starts with the selected workflow; workflow-resolution failures park the task with an explicit error rather than executing the wrong workflow.
|
||||
- **Priority:** MVP/blocking for per-task selection and fail-closed resolution; nice-to-have for first-class mission-feature workflow defaults if not already supported by a triage entry point.
|
||||
|
||||
@@ -23,25 +23,52 @@ Task Detail modal opens from onboarding, activity log, and task-to-task navigati
|
||||
|
||||
**Left Sidebar Navigation** is enabled by default for desktop/tablet project screens, moving project navigation out of the Header and into a persistent left sidebar. To opt out, open **Settings → Experimental Features** and turn **Left Sidebar Navigation** off (`leftSidebarNav: false`).
|
||||
|
||||
When enabled on desktop or tablet project screens, the sidebar starts with a centered **New Task** button that opens the existing New Task dialog from any project screen. Expanded mode shows the plus icon and **New Task** label; collapsed rail mode keeps the centered icon-only button accessible through its label/title. Below that action, the sidebar contains the primary destinations (Board, List, Agents, Command Center, Missions, Chat, Artifacts, Mailbox, and plugin primary views), selected auxiliary destinations as regular entries (Research, Insights, Skills, Memory, Secrets, Stash Recovery, Evals, Goals, Todos, Dev Server, and plugin overflow views when their flags/plugins are enabled), and a footer with the collapse toggle directly above the Settings button. The Header retains the Fusion brand and project selector, keeps its non-navigation controls, and hides the view-toggle row and **More views** trigger so there is only one canonical primary navigation surface.
|
||||
<!-- FNXC:DashboardDocs 2026-06-22-00:00: The dashboard navigation docs must mirror the post-reshuffle source of truth: the left sidebar owns primary content views plus Workflows, Import Tasks, and Automations, while the right dock owns only inline tool panels. -->
|
||||
<!-- FNXC:DashboardNavigationDocs 2026-06-22-09:30: FN-6897 synced the user-facing navigation guide after the sidebar/dock reshuffle. Desktop/tablet navigation is split between left-sidebar main-content destinations, a persistent far-right tools dock, and the footer-launched Terminal; stale Header overflow, duplicate dock/sidebar, and standalone Stash Recovery affordances must not be documented as current behavior. -->
|
||||
|
||||
When enabled on desktop or tablet project screens, the sidebar starts with a centered **New Task** button that opens the existing New Task dialog from any project screen. Expanded mode shows the plus icon and **New Task** label; collapsed rail mode keeps the centered icon-only button accessible through its label/title. Below that action, the sidebar contains primary destinations (**Board**, **List**, **Agents** when enabled, **Command Center**, **Planning**, **Missions**, **Chat**, **Artifacts**, **Mailbox**, and plugin primary views) followed by secondary destinations (**Workflows**, **Import Tasks**, **Automations**, optional **Evals**, **Goals**, **Research**, **Insights**, **Skills**, **Memory**, **Dev Server**, and plugin overflow views when their flags/plugins are enabled). The footer contains the sidebar collapse toggle directly above **Settings**.
|
||||
|
||||
Use the desktop/tablet sidebar this way:
|
||||
|
||||
1. Select **New Task** at the top of the sidebar.
|
||||
Expected outcome: the existing New Task dialog opens from any project screen, including advanced options such as priority, execution mode, workflow/model routing, and GitHub tracking.
|
||||
2. Select a primary destination such as **Board**, **Command Center**, **Planning**, or **Artifacts**.
|
||||
Expected outcome: the selected view renders in the main content region and the sidebar item receives the active highlight.
|
||||
3. Select **Workflows**, **Import Tasks**, or **Automations** from the secondary section.
|
||||
Expected outcome: each surface opens as an embedded main-content view. **Import Tasks** is the GitHub import surface.
|
||||
4. Use the footer **Collapse** control or drag the right-edge resize handle.
|
||||
Expected outcome: the sidebar switches between labeled and icon-only rail modes or persists the resized width in browser `localStorage` (`fusion:left-sidebar-collapsed` and `fusion:left-sidebar-width`) for the next reload.
|
||||
|
||||
While the sidebar is active on desktop/tablet project screens, Board and List workflow controls move into the Header slot that replaces the hidden view toggle. Board and List share one workflow dropdown: each workflow row includes an inline edit action, and a persistent **New workflow** action remains at the bottom of the dropdown while the workflow list scrolls. The standalone workflow row above the board/list content is removed in this mode. When the flag is off, outside project screens, or on mobile, workflow controls remain inline with the same consolidated dropdown.
|
||||
|
||||
The footer collapse toggle uses the same row styling as other sidebar items: expanded mode shows a **Collapse** label, while collapsed rail mode hides the label and keeps the icon-only button accessible through its label/title. The active nav-item highlight and the resize-handle hover/focus accent track the active color theme's `--accent` token across all themes, so shadcn, forest, ocean, and other themes no longer show a fixed blue selected state. The expanded width can still be resized from the right-edge separator. Collapsed state and expanded width are saved in browser `localStorage` (`fusion:left-sidebar-collapsed` and `fusion:left-sidebar-width`) and restored on reload.
|
||||
The active nav-item highlight and the resize-handle hover/focus accent track the active color theme's `--accent` token across all themes, so shadcn, forest, ocean, and other themes no longer show a fixed blue selected state. The Header retains the Fusion brand and project selector, keeps non-navigation controls, and hides duplicate desktop view-toggle entries while the sidebar is active.
|
||||
|
||||
On mobile viewports (`<=768px`), the sidebar is not rendered even when the default-on setting is enabled. The existing bottom `MobileNavBar` remains the navigation surface.
|
||||
On mobile viewports (`<=768px`), the sidebar is not rendered even when the default-on setting is enabled. The existing bottom `MobileNavBar` remains the navigation surface, with mobile-only More-sheet entries for compact tools such as Git Manager, Terminal, Files, and **Import from GitHub**.
|
||||
|
||||
## Right Dock (experimental, default on)
|
||||
|
||||
The **Right Dock Panel** experiment is enabled by default. To disable it, open **Settings → Experimental Features** and turn off **Right Dock Panel**.
|
||||
|
||||
When enabled on desktop or tablet project screens, the dock is visible by default as the persistent far-right sidebar in the project content row. Its in-dock collapse toggle (`right-dock-collapse-toggle`) replaces the former Header right-panel toggle, so there is no duplicate Header control when Left Sidebar Navigation is active or when the Header view-toggle row is visible.
|
||||
When enabled on desktop or tablet project screens, the right dock is a persistent far-right tools sidebar in the project content row. Use the in-dock collapse control to switch between the full tool panel and the compact far-right rail; the selected tool, expanded/collapsed state, width, and expanded modal size persist across reloads.
|
||||
|
||||
The dock toolbar has exactly six destinations: **Activity**, **Activity Log**, **Import from GitHub**, **Git Manager**, **Files**, and **Automation**. The launcher tools reuse the same handlers as the former desktop Header toolbar buttons; **Files** remains the inline dock view, opens by default, and is the fallback when browser storage points at a removed dock key. Inline dock views have an expand button that opens the same view in a resizable modal, while action-only tools launch directly and are not expandable. Dock collapsed state, width, and expanded modal size persist across reloads.
|
||||
The dock toolbar has built-in inline tool panels for **Activity**, **Activity Log**, **Git Manager**, **Files**, and project tool launchers such as **Import from GitHub** / **Import Tasks** workflow entry points and **Automation** actions when available. **Activity**, **Activity Log**, **Git Manager**, and **Files** render in embedded mode inside the dock instead of opening fixed popup overlays; **Files** opens by default and is the fallback when browser storage points at a removed dock key. Inline dock views have an expand button that opens the same view in a resizable modal for more room. Plugin overflow views may add additional right-dock tool tabs, except plugin destinations that explicitly belong in the left sidebar.
|
||||
|
||||
Content views such as Artifacts, Research, Insights, Skills, Memory, Secrets, Evals, Goals, Todos, and Dev Server live in the left sidebar (or compact mobile navigation) rather than the right dock. The six tool buttons are no longer duplicated in the desktop top Header toolbar, and the former desktop overflow trigger is removed when it would otherwise be empty.
|
||||
Use the desktop/tablet right dock this way:
|
||||
|
||||
On mobile viewports, the Right Dock never renders. The compact Header overflow and bottom `MobileNavBar` keep their existing behavior even when the experiment is enabled.
|
||||
1. Open a project screen with **Right Dock Panel** enabled.
|
||||
Expected outcome: the dock appears on the far right with **Files** selected unless a valid previous dock view is stored.
|
||||
2. Select **Activity**, **Activity Log**, **Git Manager**, **Files**, or another available tool in the dock toolbar.
|
||||
Expected outcome: the selected tool renders inline inside the dock body and the toolbar tab becomes active.
|
||||
3. Drag the dock's left-edge resize handle, or focus the separator and use the arrow keys.
|
||||
Expected outcome: the dock width changes within its min/max bounds and is saved for future reloads.
|
||||
4. Select the dock expand action.
|
||||
Expected outcome: the same inline tool opens in a resizable modal while the dock remains the source navigation surface.
|
||||
5. Use the dock's own collapse control.
|
||||
Expected outcome: the far-right surface switches between full panel and compact rail without creating duplicate left-sidebar destinations; mobile viewports never render the right dock.
|
||||
|
||||
Content views such as Artifacts, Research, Insights, Skills, Memory, Evals, Goals, Dev Server, **Workflows**, **Import Tasks**, and **Automations** live in the left sidebar (or compact mobile navigation) rather than the right dock. On desktop/tablet, GitHub import lives under **Import Tasks**; mobile keeps compact GitHub import entries in the More surfaces.
|
||||
|
||||
On mobile viewports, the Right Dock never renders. The compact Header actions and bottom `MobileNavBar` keep their existing mobile behavior even when the experiment is enabled.
|
||||
|
||||
## Deep Links
|
||||
|
||||
@@ -111,12 +138,36 @@ Features:
|
||||
|
||||

|
||||
|
||||
## Import Tasks (GitHub import)
|
||||
|
||||
**Import Tasks** is the desktop/tablet sidebar destination for importing GitHub issues and pull requests onto the board. It embeds the GitHub import surface in the main content region; the same component can still appear as a modal from compact mobile paths.
|
||||
|
||||
Use Import Tasks on desktop/tablet:
|
||||
|
||||
1. Select **Import Tasks** in the left sidebar.
|
||||
Expected outcome: the GitHub import surface opens in the main content region with GitHub issue and pull request tabs.
|
||||
2. Choose or enter a repository (`owner/repo`). If Git remotes are detected, use the remote selector.
|
||||
Expected outcome: Fusion loads import candidates for the selected repository and shows repository/load state feedback.
|
||||
3. Stay on **Issues** or switch to **Pull Requests**, then optionally enter issue label filters before loading results.
|
||||
Expected outcome: the list pane shows matching open issues or pull requests and marks entries that already exist on the board.
|
||||
4. Select an issue or pull request row.
|
||||
Expected outcome: the preview pane shows its title, source link, body excerpt/content, labels or PR metadata, and import availability.
|
||||
5. Select the import action.
|
||||
Expected outcome: Fusion creates a task (or review task for a pull request) on the board and preserves GitHub provenance/tracking metadata.
|
||||
|
||||
Use GitHub import on mobile:
|
||||
|
||||
1. Open the compact Header actions or bottom **More** sheet and select **Import from GitHub**.
|
||||
Expected outcome: the same import workflow opens in the mobile modal layout.
|
||||
2. Choose the repository, issue/PR tab, candidate row, and import action.
|
||||
Expected outcome: Fusion creates the board task with the same GitHub provenance/tracking metadata as the desktop/tablet **Import Tasks** view.
|
||||
|
||||
## Graph View
|
||||
|
||||
Graph view visualizes task dependencies as an interactive node/edge map.
|
||||
|
||||
Navigation:
|
||||
- Desktop: **Header → More views → Graph**
|
||||
- Desktop/tablet: left sidebar or applicable plugin/content navigation entry for **Graph** when the dependency graph surface is enabled
|
||||
- Mobile: **MobileNavBar → More → Graph**
|
||||
|
||||
Behavior:
|
||||
@@ -143,7 +194,7 @@ The workflow editor opens as a full-screen modal editor for inspecting built-ins
|
||||
Navigation:
|
||||
- Open a task or board surface that shows the workflow selector, then choose **Manage…**.
|
||||
- From the Board or List workflow dropdown, use the inline edit button on a workflow row to open that workflow directly, or use the persistent **New workflow** footer to create a workflow without leaving the dropdown. The same dropdown previews each workflow's Todo / In Progress / Done task counts inline before switching.
|
||||
- Use the global **Workflow** / **Workflows** entry point from desktop header, compact header overflow, or mobile **More** navigation to browse definitions.
|
||||
- Use **Workflows** in the desktop/tablet left sidebar, compact mobile actions, or mobile **More** navigation to browse definitions.
|
||||
- From Settings moved-setting stubs, choose **Open workflow settings** to jump to the default workflow's settings values.
|
||||
|
||||
Behavior:
|
||||
@@ -162,6 +213,8 @@ Behavior:
|
||||
|
||||
Custom Providers live in **Settings → Authentication → Custom Providers**, inside the **Advanced: Custom Providers** disclosure. Use this section to add user-defined model providers that speak an OpenAI-compatible API, the OpenAI Responses API, an Anthropic-compatible API, or Google Generative AI. After a provider is saved with models, those models become selectable in model dropdowns, including **Settings → Project Models** lanes and workflow model lanes.
|
||||
|
||||
Settings → Global Models also includes **Model pricing overrides** for Command Center estimates. Add or edit rows with lowercased `provider:model` keys (or bare `:model` fallback keys), USD-per-1M token prices for input/output/cache read/cache write, and optional source text. **Fetch LiteLLM pricing** performs an explicit one-click refresh from LiteLLM's published model pricing JSON, replaces the override table only after a successful parse, and records the fetched timestamp/source; failed fetches keep the existing overrides.
|
||||
|
||||
Supported **API type** values match the dropdown in the form:
|
||||
|
||||
- **OpenAI-compatible**
|
||||
@@ -225,7 +278,7 @@ For the stored settings shape, see [`customProviders` in the Settings Reference]
|
||||
|
||||
## Planning Mode
|
||||
|
||||
Planning Mode now includes branch controls on the summary screen before you create a task.
|
||||
Planning is a desktop/tablet left-sidebar main-content destination after **Command Center**. It opens the planning-session list and composer in the main content region; mobile continues to use the compact planning entry points. Planning Mode now includes branch controls on the summary screen before you create a task.
|
||||
|
||||
- **Branch strategy** options mirror Subtask Breakdown semantics:
|
||||
- `Use project/default branch`
|
||||
@@ -240,6 +293,10 @@ These values are sent with the Planning Mode create-task request as `branchSelec
|
||||
|
||||
When inline quick-create, Planning Mode, or Subtask Breakdown is opened from a workflow-filtered board/list lane, the create request also carries that active workflow selection. Quick-created tasks appear on the selected workflow lane immediately while board-workflows metadata refreshes, and planning saves, planning breakdown saves, and subtask-breakdown saves create their tasks directly on the selected workflow lane instead of briefly landing on the default board.
|
||||
|
||||
The **New Task** dialog's workflow selector also defaults to the current or last selected Board/List workflow lane for the current project. If no valid lane has been selected, or the remembered lane was deleted, the selector falls back to the project default workflow and task creation omits an explicit `workflowId`.
|
||||
|
||||
Quick entry, inline quick-create, and the full **New Task** dialog all check for similar active tasks before creating. When possible duplicates exist, the warning lists each match by task description (falling back to title, then “No description”) and lets you open an existing task, cancel, or create anyway with the duplicates acknowledged.
|
||||
|
||||
Completed single-task planning sessions remain in the Planning Mode history after you create the task, and selecting one restores the completed summary instead of restarting the composer. History rows are deduplicated by session id even if the initial load and live session updates arrive out of order, and deleting a history entry now waits for the server delete to persist (failures keep the row visible and surface an error instead of silently disappearing until refresh).
|
||||
|
||||
## New Task Modal Branch Strategy
|
||||
@@ -259,7 +316,7 @@ Rules:
|
||||
- `Merge target / base branch` stays optional for all modes and uses the same branch-dropdown + `Custom…` fallback behavior as Planning Mode.
|
||||
- In **More options → Model Configuration**, **Auto-merge** is a per-task override with three states: **Default** (follow project setting), **Enabled**, or **Disabled**.
|
||||
|
||||
The dialog also exposes the board quick-add AI handoffs: **Plan** opens Planning Mode with the current description, and **Subtask** opens Subtask Breakdown with the current description when **Settings → Experimental Features → Subtask Breakdown** is enabled. The Subtask handoff is hidden by default; visible handoff buttons remain disabled until the description has content, matching the quick-add row behavior.
|
||||
The dialog also exposes the board quick-add AI handoffs: **Plan** opens Planning Mode with the current description, and **Subtask** opens Subtask Breakdown with the current description when **Settings → Experimental Features → Subtask Breakdown** is enabled. The Subtask handoff is hidden by default; visible handoff buttons remain disabled until the description has content, matching the quick-add row behavior. **Execution mode** is available in the New Task dialog as well as quick entry, so users can choose Fast or standard execution before creating a task from either surface.
|
||||
|
||||
## Chat View
|
||||
|
||||
@@ -352,12 +409,13 @@ 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: <title>`) 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
|
||||
- approval lifecycle SSE events (`approval:requested`, `approval:updated`, `approval:decided`) trigger mailbox approvals refresh without manual reload
|
||||
- when a task newly enters `awaiting-approval`, the app shows a persistent approval banner above project content with an **Open Mailbox** CTA; dismissals are remembered per approval item until that item advances or a different one arrives
|
||||
- when a task first transitions into `done`, the dashboard shows a one-time **Enjoying Fusion?** GitHub star prompt in the project view; clicking **Star on GitHub** or dismissing the card marks it shown in browser `localStorage`, so it does not reappear on reload or later task completions
|
||||
- when a task first transitions into `done`, the dashboard shows a one-time **Enjoying Fusion?** GitHub star prompt in the project view after first-run setup is closed; clicking **Star on GitHub** or dismissing the card marks it shown in browser `localStorage`, so it does not reappear on reload or later task completions. The setup wizard does not add a second star prompt.
|
||||
- Visible message history/threading is driven by explicit `message.metadata.replyTo.messageId` links
|
||||
- Separate top-level messages from the same sender remain independent in the inbox and detail pane
|
||||
|
||||
@@ -365,14 +423,32 @@ Mailbox view shows inbox/outbox communication threads and unread state.
|
||||
|
||||
## Interactive Terminal
|
||||
|
||||
Fusion embeds a terminal using xterm.js.
|
||||
Fusion embeds a terminal using xterm.js. Desktop and tablet use the footer status bar as the terminal launcher; mobile keeps the full-screen terminal path.
|
||||
|
||||
Use the terminal on desktop/tablet:
|
||||
|
||||
1. Select the **Terminal** button in the footer executor status bar.
|
||||
Expected outcome: the terminal opens as a bottom-docked panel with the active shell session and a draggable top resize handle.
|
||||
2. Drag the top edge of the docked panel.
|
||||
Expected outcome: the panel height changes within its viewport-safe bounds and persists per project.
|
||||
3. Select **Pop out** from the terminal header.
|
||||
Expected outcome: the terminal switches to a floating window that can be dragged and freely resized; size, position, and display mode are saved per project.
|
||||
4. Select **Dock** in the floating terminal.
|
||||
Expected outcome: the terminal returns to the bottom docked panel using the saved docked height.
|
||||
5. Select the scripts chevron beside the footer **Terminal** button.
|
||||
Expected outcome: the quick scripts menu opens without toggling the terminal; choosing a script runs it in the terminal, and the menu footer opens script management.
|
||||
|
||||
Use the terminal on mobile:
|
||||
|
||||
1. Open the bottom navigation **More** sheet and select **Terminal**.
|
||||
Expected outcome: the terminal opens as a full-screen, keyboard-aware modal rather than the desktop/tablet docked or floating surface.
|
||||
2. Use the mobile terminal controls and close the modal when finished.
|
||||
Expected outcome: terminal sessions reconnect/recover normally without desktop dock state affecting the mobile layout.
|
||||
|
||||
Features:
|
||||
|
||||
- Multiple terminal tabs
|
||||
- PTY-backed shell sessions
|
||||
- On desktop and tablet, the terminal opens from the footer executor status bar as a bottom-docked panel with a draggable top resize handle; use **Pop out** to switch to a draggable, freely resizable floating terminal, then **Dock** to return it to the footer panel.
|
||||
- Mobile keeps the terminal as a full-screen modal with the existing keyboard-aware layout instead of the docked or floating desktop/tablet modes.
|
||||
- Ctrl/Cmd+C copies the current terminal selection, while plain Ctrl+C with no selection still sends SIGINT
|
||||
- Ctrl/Cmd+V pastes clipboard text into the active terminal session
|
||||
- The Shortcuts panel includes Ctrl/Alt helpers, ESC/Tab, common shell shortcuts, and Up/Down/Left/Right arrow buttons that send standard ANSI cursor sequences for keyboard-less shell history and line editing
|
||||
@@ -387,7 +463,18 @@ Features:
|
||||
|
||||
## Git Manager
|
||||
|
||||
Git manager centralizes repo operations in the dashboard.
|
||||
Git Manager centralizes repo operations in the dashboard. On desktop/tablet it is available as an embedded right-dock panel and can expand into a resizable modal; on mobile it opens from the compact More surfaces.
|
||||
|
||||
Use Git Manager:
|
||||
|
||||
1. On desktop/tablet, open the right dock and select **Git Manager**.
|
||||
Expected outcome: Git Manager renders inline in the right dock with its section tabs and repository status.
|
||||
2. Select the dock expand action if you need more room.
|
||||
Expected outcome: the same Git Manager surface opens in a resizable modal without changing the selected dock tool.
|
||||
3. On mobile, open the compact Header overflow or bottom **More** sheet and select **Git Manager**.
|
||||
Expected outcome: Git Manager opens in the mobile modal layout with the section tabs restored as a horizontal scrolling strip.
|
||||
4. Select **Status**, **Changes**, **Commits**, **Branches**, **Worktrees**, **Stashes**, **Recovery**, or **Remotes**.
|
||||
Expected outcome: the corresponding section panel replaces the previous section while preserving the same Git Manager session.
|
||||
|
||||
Features:
|
||||
|
||||
@@ -398,10 +485,10 @@ Features:
|
||||
- One-click **Sync** action in Remotes (`git pull --rebase` followed by push; it stops and surfaces an error instead of pushing when the pull conflicts or fails)
|
||||
- Remote editing controls
|
||||
- Stash inspection (view stat + patch) before apply/pop/drop actions
|
||||
- Stash Recovery tab for orphaned merger-autostashes; orphan counts appear on Git Manager entry points instead of a standalone Stash Recovery view
|
||||
- **Recovery** tab for orphaned merger-autostashes; orphan counts appear on Git Manager entry points
|
||||
- Remotes tab keeps "Recent commits on {remote}" in sync immediately after successful push/pull actions
|
||||
|
||||

|
||||

|
||||
|
||||
## Merge Advance Notice
|
||||
|
||||
@@ -478,19 +565,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
|
||||
- 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
|
||||
- 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 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.
|
||||
|
||||

|
||||
|
||||
## Reports View
|
||||
@@ -498,7 +594,7 @@ Features:
|
||||
Reports View is available when the **Reports** plugin is installed and enabled.
|
||||
|
||||
Navigation:
|
||||
- Desktop: **Header → More views → Reports**
|
||||
- Desktop/tablet: left sidebar plugin/content entry for **Reports** when the Reports plugin is installed and enabled
|
||||
- Mobile: **More** sheet → **Reports**
|
||||
|
||||
Features:
|
||||
@@ -529,8 +625,7 @@ Todo View is an experimental full-height dashboard surface for managing per-proj
|
||||
> Available when `experimentalFeatures.todoView` is enabled.
|
||||
|
||||
Navigation:
|
||||
- Desktop/tablet with Left Sidebar Navigation enabled: **Left sidebar → Todos**
|
||||
- Desktop/tablet with the left-sidebar opt-out layout: **Header → More views → Todos**
|
||||
- Desktop/tablet: **Left sidebar → Todos** when the Todo view is enabled
|
||||
- Mobile: **More** sheet → **Todos**
|
||||
|
||||
For full behavior, API contracts, and storage details, use the canonical [Todo View guide](./todo-view.md).
|
||||
@@ -553,9 +648,9 @@ Features:
|
||||
- Graceful unavailable/setup messaging when research backend capability is disabled or not configured
|
||||
|
||||
Navigation:
|
||||
- Desktop: **Header → More views** overflow menu
|
||||
- Desktop/tablet: **Left sidebar → Research** when the Research view is enabled
|
||||
- Mobile: **More** sheet in `MobileNavBar`
|
||||
- Research is intentionally not shown in the primary board/list/agents/missions/chat toggle row
|
||||
- Research is intentionally separate from the primary Board/List workflow controls
|
||||
|
||||
For the full research workflow, provider setup, CLI commands, API reference, and agent integration, see the canonical [Research guide](./research.md).
|
||||
|
||||
@@ -593,6 +688,8 @@ Navigation:
|
||||
Features:
|
||||
- Switch between **List**, **Board**, and **Org chart** layouts
|
||||
- Filter by role/state, include/exclude system agents, and inspect health/status
|
||||
- Agent list cards show the configured **Model** or plugin **Runtime** for each agent, falling back to **Auto** when no override is set
|
||||
- First-run setup asks whether to create an optional project agent after project registration. The default template is **CEO**; users can choose another preset, use the AI interview when `experimentalFeatures.agentOnboarding` is enabled, or skip it. Fusion can still build tasks without an agent by starting temporary agents to plan, code, review, and merge task work.
|
||||
- Start, pause, stop, and trigger agent runs from the view and from detail panels
|
||||
- In **Agent detail**, use the kebab **Bulk agent actions** button in the header utility cluster (next to **Refresh** and **Close**) to run project-wide lifecycle transitions for non-ephemeral agents in the current project — **Pause All Agents** targets agents in the `active` or `running` state, while **Resume All Agents** targets agents in the `paused` state only
|
||||
- Bulk menu items stay disabled when nothing is eligible and show an inline hint (`Loading eligible agents...`, `No active agents eligible`, `No paused agents eligible`, or the current eligible count such as `Pause 2 active/running agents`)
|
||||
@@ -614,7 +711,7 @@ Roadmaps view manages roadmap hierarchies (roadmaps, milestones, features) and p
|
||||
> Hidden when a plugin replaces Roadmaps navigation.
|
||||
|
||||
Navigation:
|
||||
- Desktop: **Header → More views → Roadmaps**
|
||||
- Desktop/tablet: left sidebar plugin/content entry for **Roadmaps** when the Roadmap plugin is enabled
|
||||
- Mobile: **More** sheet (or promoted to a top tab when eligible based on mobile nav slot rules)
|
||||
|
||||
Features:
|
||||
@@ -665,7 +762,7 @@ Evals view is a dedicated dashboard surface for reviewing scheduled task-evaluat
|
||||
> Available when `experimentalFeatures.evalsView` is enabled.
|
||||
|
||||
Navigation:
|
||||
- Desktop: **Header → More views → Evals**
|
||||
- Desktop/tablet: **Left sidebar → Evals** when evaluations are enabled
|
||||
- Mobile: **More** sheet → **Evals**
|
||||
|
||||
Features:
|
||||
@@ -681,7 +778,7 @@ Insights view surfaces categorized project insights and lets you turn findings i
|
||||
> Available when `experimentalFeatures.insights` is enabled.
|
||||
|
||||
Navigation:
|
||||
- Desktop: **Header → More views → Insights**
|
||||
- Desktop/tablet: **Left sidebar → Insights** when insights are enabled
|
||||
- Mobile: **More** sheet → **Insights**
|
||||
|
||||
Features:
|
||||
@@ -705,10 +802,11 @@ Features:
|
||||
- **Overview controls dashboard** sits at the top of the Overview landing surface on desktop and mobile. It includes AI engine stop/start backed by `globalPause`, live scheduler status from executor stats, range sliders for `maxConcurrent`, `maxTriageConcurrent`, and `maxWorktrees` that persist through `/api/settings`, and a compact theme dropdown with the same color-chip swatches and Shadcn variant list as Settings → Appearance. These controls reuse existing APIs and App-level theme setters; they do not add a new backend route or second theme owner.
|
||||
- **Overview** summarizes token usage/cost, autonomy, active nodes, sessions, agent runs, tasks done, model breadth, and real open signals, and includes the SDLC throughput funnel for the selected range at the bottom of the Overview content in loading, error, empty, and populated states. Its token total and Live activity snapshot token metric refresh on a bounded live cadence and animate number changes while preserving reduced-motion preferences. The sessions card uses the selected-range `ActivityAnalytics.sessions` value already loaded for the overview. The Live activity snapshot also shows the current board-state count for tasks in progress, independent of the selected analytics date range. Overview includes a graph-rich software-factory snapshot with the existing tokens-by-model bar, tool-category bar, real recharts token-share pie, and the daily activity multi-series line chart placed before the daily activity sparkline/trend so the richer line graph sits higher in the chart grid. These reuse the already-loaded tokens, tools, activity, and signals analytics; the signals count comes from `/api/command-center/signals` and renders unavailable (`—`) while the incidents-backed response is loading or unavailable. The chart reveal/glow accents are decorative and disabled when reduced-motion preferences are active. The SDLC completion rate is shown as a radial gauge and is calculated as cohort conversion from in-range triage entrants, so the rate is capped at 100% even when older tasks finish during the range.
|
||||
<!-- FNXC:CommandCenter 2026-06-21-00:00: Command Center cost must read as an estimated, derived value from recorded token counts and the hand-maintained model pricing map; it is never persisted, and the UI must surface prices-as-of, stale low-confidence, and unavailable unknown-model states instead of implying billing truth. -->
|
||||
- **Tokens** breaks down token totals, estimated cost, tasks, and per-model usage. Per-model and per-provider breakdowns use the task's analytics-only actually-used model snapshot when available, so usage from settings-resolved runs appears under the real runtime model instead of `(unknown)` without changing future model resolution; estimated cost uses the same snapshot-first, legacy-fallback model identity so those resolved runs price normally when the model is in the pricing table. Estimated cost is derived at read time from recorded token counts multiplied by a hand-maintained per-model pricing table; it is not persisted, so historical rows stay tied to current maintained prices instead of stale stored billing truth. The Tokens area shows a **prices as of** date for that table, marks pricing older than the staleness threshold as low-confidence, and shows cost unavailable for models with no pricing entry rather than guessing a price. It includes the existing token-usage-over-time chart, an additive recharts multi-series line graph, and a token-share pie backed by the same grouped token analytics; use the granularity control to switch the time-series request between hourly, daily, and weekly buckets. The token total and charts poll on a bounded cadence, keep the previous data visible during refresh, animate decorative count/bar transitions, and disable those animations for reduced-motion users.
|
||||
<!-- FNXC:CommandCenter 2026-06-22-00:00: FN-6876 requires user-maintained/LiteLLM-fetched pricing overrides to feed Tokens and Team estimates immediately without implying provider billing reconciliation. -->
|
||||
- **Tokens** breaks down token totals, estimated cost, tasks, and per-model usage. Per-model and per-provider breakdowns use the task's analytics-only actually-used model snapshot when available, so usage from settings-resolved runs appears under the real runtime model instead of `(unknown)` without changing future model resolution; estimated cost uses the same snapshot-first, legacy-fallback model identity so those resolved runs price normally when the model is in the pricing table. Estimated cost is derived at read time from recorded token counts multiplied by the effective per-model pricing table: Settings → Global Models pricing overrides win first, then the built-in fallback table is used. It is not persisted, so historical rows stay tied to current maintained prices instead of stale stored billing truth. The Tokens area shows a **prices as of** date/source for the effective table, marks pricing older than the staleness threshold as low-confidence, and shows cost unavailable for models with no pricing entry rather than guessing a price. It includes the existing token-usage-over-time chart, an additive recharts multi-series line graph, and a token-share pie backed by the same grouped token analytics; use the granularity control to switch the time-series request between hourly, daily, and weekly buckets. The token total and charts poll on a bounded cadence, keep the previous data visible during refresh, animate decorative count/bar transitions, and disable those animations for reduced-motion users.
|
||||
- **Tools** shows autonomy ratio, tool-call volume, intervention counts, sessions, and tool categories. The area keeps the existing category bar and adds a recharts category-share pie from `ToolAnalytics.byCategory`. There is intentionally no tools line chart yet because `ToolAnalytics` does not expose a per-day tool trend; the dashboard does not fabricate one or call a new endpoint.
|
||||
- **Activity** tracks sessions, messages, active nodes, active agents, agent heartbeat runs, and stickiness. Agent-run sheets show total, active, completed, and failed runs for the selected range, and the Agent runs/day sparkline trends runs by `agentRuns.startedAt`. The area keeps the existing live animated line charts for messages/day, active agents/day, active nodes/day, and combined throughput/day (`messages + active agents + active nodes`), and adds a recharts multi-series line graph for messages, active agents, and agent runs plus an agent-run outcome pie from the existing `agentRuns` split. These charts reuse the existing activity analytics endpoint, refresh on a bounded 15-second cadence while mounted, keep the previous data visible during refreshes, and disable decorative draw-on motion for reduced-motion users.
|
||||
- **Productivity** separates outcome counters (commits and pull requests), task-duration stats, and volume proxies such as modified files, lines changed, and files by language. The task-duration block counts done tasks completed in the selected range and shows average, median, p90, and total active execution time from `cumulativeActiveMs`; when no qualifying duration data exists, duration values render the unavailable `—` sentinel rather than `0`. It keeps the files-by-language bar and adds a language-share pie from `ProductivityAnalytics.byLanguage`. There is intentionally no productivity line chart because the current productivity response has no per-day throughput or completion time series; no new endpoint is called.
|
||||
- **Productivity** separates outcome counters (commits and pull requests), task-duration stats, and volume proxies such as modified files, lines changed, and files by language. The task-duration block counts done tasks completed in the selected range and shows average, median, p90, and total active execution time from `cumulativeActiveMs`; when no qualifying duration data exists, duration values render the unavailable `—` sentinel rather than `0`. The Lines changed card includes **Preview LOC backfill**, an explicit operator control for historical commit-association diff stats. Preview runs the project-scoped backfill in dry-run mode by default and reports scanned rows, distinct commits, updated rows, skipped unavailable commits, and skipped invalid SHAs without writing; **Apply backfill** appears after a preview and requires danger confirmation before persisting additions/deletions to `task_commit_associations`, then renders the same counts as an applied report. It keeps the files-by-language bar and adds a language-share pie from `ProductivityAnalytics.byLanguage`. There is intentionally no productivity line chart because the current productivity response has no per-day throughput or completion time series; no new endpoint is called.
|
||||
- **Team** shows the read-only agent org chart, heartbeat pause/resume backed by the existing `enginePaused` setting, a per-agent analytics table, tokens-by-agent and tasks-done-by-agent charts, and a real token-share pie from the same per-agent token totals. The org chart is styled by Command Center's Team CSS, not lazy Agents view CSS, auto-switches to a horizontal top-down tree when the container is wide enough using the same breakpoint resolver as the full Agents view, and otherwise keeps the vertical nested list inside the taller scrollable org-chart container. The org-chart scroll container supports mouse click-and-drag panning while touch devices keep native scrolling. Parent agents draw connector lines to child agents in both horizontal and vertical Team layouts across desktop and mobile breakpoints. Org nodes show only agent names so role/title description/meta text does not clutter Team operations. Metrics come only from the project-scoped `tasks` and `agents` tables: token totals and estimated cost are summed from the `tokenUsage*` columns by `assignedAgentId`, files changed counts parsed `tasks.modifiedFiles` paths, tasks done counts `column = 'done'` moves in the selected range, and in-progress / in-review values reflect current task columns. Agent name, role, and live state come from the `agents` table; deleted-agent task history falls back to the raw agent id instead of crashing. The tab uses `/api/command-center/team`, adds no schema, never calls GitHub, and intentionally leaves per-agent issues filed/fixed to FN-6653. Team has no per-day analytics series today, so it intentionally does not render a line chart or fabricate a trend. Decorative chart reveal motion uses duration tokens and is disabled for reduced-motion users.
|
||||
- **Ecosystem** shows active model breadth, per-model task activity, and real plugin activations for the selected range. Plugin activation counts come from project-scoped plugin/extension load events via `/api/command-center/plugin-activations`; if no activation rows exist in range, the metric renders unavailable (`—`) rather than fabricating zero. The tab still reuses the tokens analytics endpoint grouped by model, adds a task-share-by-model pie from `TokenAnalytics.groups`, and renders a tokens/tasks trend line when `TokenAnalytics.series` buckets are present; if series buckets are absent, no synthetic trend is shown.
|
||||
<!-- FNXC:CommandCenter 2026-06-21-07:07: FN-6722 requires the GitHub area to expose a resolved-issue detail list from local task-store analytics only, with exact close timestamps flagged when reconciliation populated `sourceIssueClosedAt` and approximation called out otherwise. -->
|
||||
@@ -759,24 +857,31 @@ Dev Server view manages detected dev server commands, preview URLs, and live log
|
||||
> Available when `experimentalFeatures.devServerView` is enabled (`devServer` is treated as a legacy alias).
|
||||
|
||||
Navigation:
|
||||
- Desktop: **Header → More views → Dev Server**
|
||||
- Desktop/tablet: **Left sidebar → Dev Server** when the Dev Server view is enabled
|
||||
- Mobile: **More** sheet → **Dev Server**
|
||||
|
||||
Features:
|
||||
- Detect candidate dev server commands and choose which command/session to run
|
||||
- Pick an executing task to run the dev server against that task's worktree and preview its in-progress work; the selected task's descriptor is shown so you know what you're previewing.
|
||||
- Start, stop, and restart the current server session
|
||||
- Manage preview URLs with embedded preview and **Open in new tab** fallback
|
||||
- Tail live logs, load older history, and refresh session status
|
||||
- When Dev Server is hosted in a very narrow right sidebar, open the preview from the compact **Open preview** launcher; the modal keeps preview actions available while configuration and logs stay usable in the sidebar.
|
||||
|
||||
<!-- FNXC:DevServerDocs 2026-06-23-00:00: The narrow right-sidebar Dev Server host must describe the preview modal launcher so users do not expect the preview iframe to remain inline when the dock is too constrained for logs and preview together. -->
|
||||
|
||||
For module-level behavior and API surfaces, see [Dev Server modules](./dev-server-modules.md).
|
||||
|
||||
## Stash Recovery in Git Manager
|
||||
|
||||
Stash Recovery helps recover orphaned merger autostashes (`fusion-merger-autostash:*`) left behind when merge restore could not fully complete. It now lives as the **Recovery** tab in **Git Manager**; the former standalone top-level Stash Recovery view is removed from desktop and mobile navigation.
|
||||
Stash Recovery helps recover orphaned merger autostashes (`fusion-merger-autostash:*`) left behind when merge restore could not fully complete. It now lives as the **Recovery** tab in **Git Manager** and is reached through Git Manager on desktop/tablet and mobile.
|
||||
|
||||
Navigation:
|
||||
- Desktop: **Git Manager → Recovery**
|
||||
- Mobile: **More** sheet → **Git Manager → Recovery**
|
||||
|
||||
1. On desktop/tablet, open the right dock, select **Git Manager**, then select **Recovery**.
|
||||
Expected outcome: the Recovery section opens inside the embedded Git Manager panel; expanding Git Manager keeps the same section available in the modal.
|
||||
2. On mobile, open the **More** sheet, select **Git Manager**, then select **Recovery** from the horizontal section-tab strip.
|
||||
Expected outcome: the Recovery section opens in the mobile Git Manager modal with the tab strip still scrollable.
|
||||
|
||||
Features:
|
||||
- Lists orphaned stash entries grouped by source task ID (or **Unknown source** when unavailable)
|
||||
@@ -816,11 +921,13 @@ Inspect task definition, logs, review feedback, comments, artifacts, workflow ou
|
||||
|
||||
- Editable tasks with descriptions show **Summarize as title** beside the read-mode title; it asks AI to generate a concise title from the description and saves it without opening the edit form.
|
||||
- The **Chat** tab includes an expand/collapse control that lets the transcript and composer fill the task-detail modal, then restores the normal header, tabs, and action footer when collapsed.
|
||||
- Task-detail Chat messages are persisted as user comments/steering guidance and surfaced to every relevant agent lane: live executor sessions receive steering injection, while planner, reviewer (spec/plan/code), and merger agents (standard and clean-room AI merge/review) receive the latest user comments in their next prompt/pass.
|
||||
- The priority chip in task metadata is an inline picker: you can change priority directly without entering full edit mode.
|
||||
- Execution mode has a read-mode inline lightning-bolt toggle for Fast mode on/off without opening the full edit form.
|
||||
- These two metadata controls share matched sizing/alignment in read mode (including mobile wrapping) so they behave like a single polished control group.
|
||||
- Task metadata keeps priority, execution mode, provenance, optional PR context, and compact `Created` / `Updated` timestamps in one wrapping row across desktop and mobile widths; recent timestamps render as relative time (`just now`, `Xm`, `Xh`, `Xd`) and older values switch to short month/day dates.
|
||||
- The **Actions** menu exposes **Pause** / **Unpause** for eligible non-terminal tasks, including tasks assigned to agents. If a task was paused by an agent, the **Paused by agent** note is informational; users can still unpause it manually from the same menu.
|
||||
- After delete confirmations are complete, Task Detail closes immediately while the delete request finishes in the background; success and error outcomes still appear as toasts.
|
||||
- Eligible existing tasks (triage, todo, in-progress, in-review) expose a **GitHub tracking** section directly in Task Detail, even when tracking is currently disabled.
|
||||
- The GitHub tracking section now defaults to a compact summary row; use the disclosure arrow to expand linked-issue details plus tracking edit controls.
|
||||
- Backstop reconciliation runs every 15 minutes to close tracked GitHub issues for soft-deleted and archived tasks even after restart; the sweep is paginated so large archive backlogs are eventually drained.
|
||||
@@ -831,7 +938,7 @@ Inspect task definition, logs, review feedback, comments, artifacts, workflow ou
|
||||
- The **Create Pull Request** modal now offers in-app remediation for every blocking preflight check. If `branchOnRemote` is false, use **Push branch to remote** and Fusion will publish `fusion/<task-id-lower>` to `origin` and refresh preflight. If `conflictsWithBase` is true, use **Resolve conflicts with AI** and Fusion will use an AI coding agent to resolve merge markers on the task branch, commit and push real merge changes, or report success without an empty commit when the selected base is already merged; preflight then refreshes so normal PR creation can continue once all checks pass.
|
||||
- The modal shell renders immediately: preflight checks and PR options load independently of AI-generated title/body metadata, so slow AI suggestions no longer block base-branch selection, diagnostics, or manual PR authoring.
|
||||
- AI title/body generation is bounded to 60 seconds and is canceled if the dialog request disconnects; on timeout/cancel, Fusion falls back to deterministic task-based PR title/body content instead of leaving the spinner stuck forever.
|
||||
- The **Artifacts** tab combines task documents written by agents or users with task-scoped registered media artifacts. Images preview inline, video and audio use native controls, document artifacts show text previews, and generic artifacts open through their media URL.
|
||||
- The **Artifacts** tab combines task documents written by agents or users with task-scoped registered media artifacts. The gallery uses thumbnail-first image/video cards, image and video previews can expand into a dismissible full-size lightbox, video and audio use native controls, document artifacts show text previews, and generic artifacts open through their media URL.
|
||||
- The **Review** tab is separate from **Comments**: Review shows actionable PR/reviewer feedback and same-task revision controls, while Comments remains the general collaboration thread.
|
||||
- **Request revision** in Review resumes work on the same task ID (no refinement task): `in-progress` tasks get steering injection, while `in-review` tasks are moved back to `in-progress` for the same branch/worktree revision pass.
|
||||
- Review supports a manual **Refresh** action in-place: PR mode pulls latest GitHub review state/decision, while direct mode rehydrates reviewer-agent feedback from persisted task data (no GitHub call).
|
||||
@@ -1356,10 +1463,9 @@ Manage project and global secrets directly inside **Settings → Project → Sec
|
||||
|
||||
### Lazy-Loaded Heavy Views
|
||||
|
||||
These 22 views are lazy-loaded via `React.lazy()` with `<Suspense fallback={null}>`. `prefetchLazyViews()` warms App-level chunks once on mount via `requestIdleCallback`; AppModals lazy modal imports (`SettingsModal`, `WorkflowNodeEditor`, `SetupWizardModal`) are part of the same inventory. **Do not make these eager.**
|
||||
These 20 views are lazy-loaded via `React.lazy()` with `<Suspense fallback={null}>`. `prefetchLazyViews()` warms App-level chunks once on mount via `requestIdleCallback`; AppModals lazy modal imports (`SettingsModal`, `WorkflowNodeEditor`, `SetupWizardModal`) are part of the same inventory. **Do not make these eager.** The user-facing **Artifacts** section is still implemented by the `DocumentsView` component name.
|
||||
|
||||
- `AgentsView`
|
||||
- `NodesView`
|
||||
- `ChatView`
|
||||
- `MemoryView`
|
||||
- `DevServerView`
|
||||
@@ -1372,7 +1478,6 @@ These 22 views are lazy-loaded via `React.lazy()` with `<Suspense fallback={null
|
||||
- `EvalsView`
|
||||
- `TodoView`
|
||||
- `GoalsView`
|
||||
- `StashRecoveryView`
|
||||
- `PullRequestView`
|
||||
- `SetupWizardModal`
|
||||
- `SettingsModal`
|
||||
@@ -1381,6 +1486,8 @@ These 22 views are lazy-loaded via `React.lazy()` with `<Suspense fallback={null
|
||||
- `PiExtensionsManager`
|
||||
- `AgentDetailView`
|
||||
|
||||
Embedded Workflows (`_WorkflowEditorView`), Import Tasks (`_ImportTasksView`), Automations (`_AutomationsView`), and Settings (`_SettingsView`) reuse existing lazy chunks and are intentionally excluded from the curated count by the underscore-prefixed App const convention.
|
||||
|
||||
When adding or removing entries, update `packages/dashboard/app/__tests__/lazy-loaded-views-docs.test.ts` (expected set + count).
|
||||
|
||||
### CSS testing
|
||||
|
||||
@@ -112,9 +112,9 @@ Create tasks from the board or CLI.
|
||||
2. Press Enter.
|
||||
3. Task appears in **Planning** and the planning agent generates `PROMPT.md`.
|
||||
|
||||
### Option B: Plan Mode (Board)
|
||||
### Option B: Planning
|
||||
|
||||
Use the 💡 button to open AI planning mode:
|
||||
Open **Planning** from the left sidebar on desktop/tablet, or use the board/New Task **Plan** action to send your draft into AI planning mode:
|
||||
|
||||
- Fusion asks clarifying questions
|
||||
- Produces a structured summary
|
||||
|
||||
@@ -0,0 +1,626 @@
|
||||
---
|
||||
title: "feat: Optional-group container nodes + add-ons as insertable subgraphs"
|
||||
status: active
|
||||
date: 2026-06-21
|
||||
type: feat
|
||||
plan_id: 2026-06-21-002-feat-workflow-optional-group-subgraphs
|
||||
---
|
||||
|
||||
# feat: Optional-group container nodes + add-ons as insertable subgraphs
|
||||
|
||||
## Summary
|
||||
|
||||
Today "optional steps" are **execution-inert declarations**: a workflow lists `optionalSteps:
|
||||
[{ templateId, defaultOn }]`, the create/edit UI seeds a per-task `enabledWorkflowSteps` set, and a
|
||||
single hidden `workflow-step` seam node runs every enabled step *after* the graph finishes. Nothing about
|
||||
optionality is visible in the graph, and the steps cannot be placed, ordered, or composed.
|
||||
|
||||
This plan makes optionality **graph-native**. A new `optional-group` container node — modeled on the
|
||||
existing `foreach`/`loop` container nodes — holds a `template:{ nodes, edges }` subgraph. The graph
|
||||
executor runs that subgraph **once** when the group is enabled for the task and **passes through
|
||||
(skips)** it when disabled. Enable state reuses the existing per-task `enabledWorkflowSteps` facet plus a
|
||||
workflow-level `defaultOn`, keyed by the group. Separately, every prior pre-workflow **add-on** (the seven
|
||||
`WORKFLOW_STEP_TEMPLATES`: documentation-review, qa-check, security-audit, performance-review,
|
||||
accessibility-check, browser-verification, frontend-ux-design) becomes **insertable from the editor's
|
||||
template palette as a node subgraph**, and can be inserted already wrapped in an `optional-group` so an
|
||||
author can drop in "Security Audit (optional)" in one action.
|
||||
|
||||
Finally, the plan **replaces** the declaration-based system per the confirmed scope decision: the built-in
|
||||
**coding** and **stepwise-coding** workflows migrate `browser-verification` onto an `optional-group`, and
|
||||
the now-dead `WorkflowOptionalStep` / `optionalSteps` declaration, `resolveWorkflowOptionalSteps` source,
|
||||
the `workflow-step` seam node, and its compiler seam-anchor are retired — without breaking the create/edit
|
||||
toggle surfaces, which re-point to the new source.
|
||||
|
||||
**Plan depth:** Deep. Cross-cutting across core IR + validation, the engine graph executor, per-task
|
||||
persistence/seeding, the visual node editor, the built-in workflows, and a behavior-affecting removal of
|
||||
the legacy execution path.
|
||||
|
||||
---
|
||||
|
||||
## Problem Frame
|
||||
|
||||
The declaration model has three structural limits this plan removes:
|
||||
|
||||
- **Invisible & unplaceable.** `optionalSteps` never appears in the graph
|
||||
(`packages/core/src/workflow-ir-types.ts:314-339`, marked "Execution-inert; the graph executor ignores
|
||||
this facet"). All enabled steps run in one lump at the `workflow-step` seam
|
||||
(`packages/engine/src/executor.ts` `runWorkflowSteps`, gated on `enabledWorkflowSteps`), so an author
|
||||
cannot put an optional step *between* two graph nodes, order multiple optional steps, or branch on one.
|
||||
- **Add-ons are flat, not composable.** A `WorkflowStepTemplate`
|
||||
(`packages/core/src/types.ts:868-899`) is a flat prompt/script config. The seven built-in add-ons appear
|
||||
in the editor palette today only as **single "Built-in steps"** entries
|
||||
(`WorkflowNodeEditor.tsx:1002` `stepEntries`), not as subgraphs you can compose or gate.
|
||||
- **Two ways to express "run this sometimes."** The graph already has real conditional routing
|
||||
(`shouldTraverseEdge` in `packages/engine/src/workflow-graph-executor.ts:657`, edge `condition` of
|
||||
`success`/`failure`/`outcome:<x>`) and container nodes (`foreach`/`loop`), yet optionality lives in a
|
||||
parallel, execution-inert declaration channel. Converging optionality onto the graph removes the split.
|
||||
|
||||
The graph already provides every seam this needs: container nodes compile/execute via a `template`
|
||||
subgraph (`WorkflowForeachConfig`/`WorkflowLoopConfig`, `workflow-ir-types.ts:129-165`), the executor
|
||||
dispatches them in `runNodeAndTraverse` (`workflow-graph-executor.ts:431-485`), the editor renders them as
|
||||
React Flow group nodes with `parentId` children (`workflow-flow-mapping.ts:46-82,316-403,431-550`), and the
|
||||
palette can already insert multi-node subgraphs via `insertFragment` (`WorkflowNodeEditor.tsx:1425-1430`).
|
||||
The work is to add one new container kind that branches on a per-task toggle, project the add-on catalog
|
||||
into the palette as subgraphs, and migrate the built-ins off the legacy path.
|
||||
|
||||
---
|
||||
|
||||
## Requirements
|
||||
|
||||
- **R1 — Optional-group container kind.** Add an `optional-group` node kind to the IR carrying a
|
||||
`template:{ nodes, edges }` subgraph, mirroring `WorkflowForeachConfig`. Parse + validate it.
|
||||
- **R2 — Run-or-bypass execution.** The graph executor runs the group's template **once** when the group
|
||||
is enabled for the task and **passes through** (skips the subgraph, continues to the group's children)
|
||||
when disabled. No rework budget; a single pass.
|
||||
- **R3 — Enable state reuses the per-task facet.** Whether a group runs is driven by the existing per-task
|
||||
`enabledWorkflowSteps` set plus a workflow-level `defaultOn` on the group, keyed by the group's stable
|
||||
id. New tasks seed their enabled set from each group's `defaultOn` at creation.
|
||||
- **R4 — Author optional groups in the node editor.** An author can add an `optional-group` container,
|
||||
name it, set `defaultOn`, and place nodes inside it — reusing the foreach/loop group UX. The node type is
|
||||
registered so it renders (not as `react-flow__node-default`).
|
||||
- **R5 — Every add-on is an insertable subgraph.** All seven `WORKFLOW_STEP_TEMPLATES` add-ons are
|
||||
insertable from the editor palette as a node subgraph, and offered with an "insert as optional group"
|
||||
variant that drops the add-on wrapped in an `optional-group` (seeded `defaultOn`).
|
||||
- **R6 — Built-ins migrated, behavior preserved.** The coding and stepwise-coding built-ins express
|
||||
`browser-verification` as an `optional-group` (default OFF). A task with it enabled runs the step; a task
|
||||
with it disabled does not — proven by an execution-level (not traversal-only) test.
|
||||
- **R7 — Legacy path retired without surface breakage.** The `WorkflowOptionalStep`/`optionalSteps`
|
||||
declaration, `resolveWorkflowOptionalSteps` as the toggle source, the `workflow-step` seam node, and its
|
||||
compiler seam-anchor are removed. The create/edit toggle surfaces (inline card, New Task modal, Workflow
|
||||
tab, steps dropdown) keep working by resolving their toggle list from `optional-group` nodes instead.
|
||||
- **R8 — Validation invariants hold.** Optional-group templates are validated by walking the subgraph
|
||||
(children are not in `ir.nodes`): all template nodes reachable, no illegal (non-rework) cycles, no seam
|
||||
nodes inside a group. Graphs with no optional groups serialize byte-identically (R9 of prior art).
|
||||
- **R9 — Additive serialization.** A workflow with no optional groups round-trips through the node editor
|
||||
byte-identically; `optional-group` introduces no new top-level IR keys (it is just a node kind).
|
||||
|
||||
---
|
||||
|
||||
## High-Level Technical Design
|
||||
|
||||
### Execution: container branches on the per-task toggle
|
||||
|
||||
The new dispatch slots into `runNodeAndTraverse` beside `foreach`/`loop`. Enabled → run the template
|
||||
sub-walk once (reuse the loop/foreach template-walk machinery, no rework budget); disabled → return success
|
||||
and traverse the group's children, skipping the body entirely.
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
Prev["upstream node"] --> OG{{"optional-group node\n(id, defaultOn, template)"}}
|
||||
OG -->|"enabled = task.enabledWorkflowSteps.includes(group.id)"| CHK{enabled?}
|
||||
CHK -->|yes| RUN["run template sub-walk ONCE\n(template.nodes/edges, single pass)"]
|
||||
CHK -->|no| SKIP["pass through\noutcome=success, value=bypassed"]
|
||||
RUN --> CHILD["traverseChildren(OG, result)"]
|
||||
SKIP --> CHILD
|
||||
CHILD --> Next["downstream node"]
|
||||
```
|
||||
|
||||
Key boundary: the **decision** (run vs skip) is read from per-task state at the trigger seam, exactly like
|
||||
the existing per-task auto-merge override — so it must be consulted wherever the run is gated, not only in
|
||||
the executor branch (see Risks R-2). The **body** is an ordinary subgraph the executor already knows how to
|
||||
walk.
|
||||
|
||||
### Authoring + add-on projection: catalog → palette → graph
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
CAT["WORKFLOW_STEP_TEMPLATES\n(7 add-ons: flat prompt/script config)"]
|
||||
CAT -->|"project to subgraph entry"| PAL["editor template palette\n(Built-in steps → subgraph entries)"]
|
||||
PAL -->|"insert as node"| N["prompt/script node\n(add-on config)"]
|
||||
PAL -->|"insert as optional group"| OGW["optional-group{ template:[ add-on node ], defaultOn }"]
|
||||
N --> CANVAS["canvas IR"]
|
||||
OGW --> CANVAS
|
||||
CANVAS -->|"flowToIr / irToFlow\n(group children via parentId)"| IR["WorkflowIrV2"]
|
||||
```
|
||||
|
||||
The add-on→subgraph projection reuses the existing `insertFragment` subgraph-insertion path
|
||||
(`WorkflowNodeEditor.tsx:1425`), which already remaps ids and rewires internal edges — so "insert as
|
||||
optional group" is a wrap-then-insert, not a new insertion engine.
|
||||
|
||||
---
|
||||
|
||||
## Key Technical Decisions
|
||||
|
||||
- **KTD-1 — `optional-group` is a container node mirroring `WorkflowForeachConfig`, not a bypass edge.**
|
||||
Per the confirmed scope decision, optionality is encapsulated in a container (foreach/loop style) holding
|
||||
a `template:{ nodes, edges }`, rather than inline nodes plus an explicit bypass edge. This reuses the
|
||||
entire group-node toolchain (IR config, validation, React Flow `parentId` children, `insertFragment`),
|
||||
and the "skip" is the container passing through rather than a visible routed edge.
|
||||
|
||||
- **KTD-2 — Enable state reuses `enabledWorkflowSteps`, keyed by the group's stable node id.** No new
|
||||
persistence. The per-task `tasks.enabledWorkflowSteps` column (`db.ts:324`,
|
||||
`store.ts:424,2140`) holds the ids of enabled groups; `defaultOn` on the group seeds it at task creation
|
||||
via the existing materialization path. Keying on the **node id** (stable for built-ins and preserved
|
||||
across editor round-trips, like foreach/loop ids) means renaming/recreating a group resets its per-task
|
||||
state — acceptable and identical to today's `templateId` keying.
|
||||
|
||||
- **KTD-3 — Single pass, no rework budget.** Unlike `foreach` (per-step) and `loop` (bounded repeat), an
|
||||
optional-group runs its template exactly once when enabled. Reuse the loop/foreach template-walk helper
|
||||
but disable rework/iteration. Rework edges are **forbidden inside** an optional-group template (validation
|
||||
rejects them) to keep the single-pass guarantee unambiguous.
|
||||
|
||||
- **KTD-4 — Re-point the toggle resolver, don't keep two sources.** `resolveWorkflowOptionalSteps`
|
||||
(`workflow-optional-steps.ts`) currently maps `ir.optionalSteps` → display metadata for the create/edit
|
||||
UI. Replace its source with a scan of `optional-group` nodes (id, group name, `defaultOn`), preserving its
|
||||
output shape (`ResolvedWorkflowOptionalStep[]`) so the inline card, New Task modal, Workflow tab, and
|
||||
steps dropdown keep consuming it unchanged. This is what lets R7 retire the declaration without breaking
|
||||
the four toggle surfaces.
|
||||
|
||||
- **KTD-5 — Add-ons stay flat configs; the palette projects them to subgraphs at insert time.** Do **not**
|
||||
rewrite `WorkflowStepTemplate` into a nodes+edges shape. A template projects to a single `prompt`/`script`
|
||||
node (carrying its `prompt`/`scriptName`/`toolMode`/`gateMode`/`phase`/model), and the "optional" variant
|
||||
wraps that node in an `optional-group`. Keeping the catalog flat avoids migrating plugin-contributed
|
||||
templates and keeps the resolver/seeding logic simple.
|
||||
|
||||
- **KTD-6 — `workflow-step` seam removal is a compiler change, not just an IR edit.** `workflow-step` is a
|
||||
registered seam anchor in the compiler's canonical pipeline
|
||||
(`workflow-compiler.ts:45,170` `planning → execute → workflow-step → review → merge`). Retiring it
|
||||
requires removing it from `SEAM_NAMES`/`expectedSeamOrder` and updating the built-in IRs + their
|
||||
byte-identity parity oracles together, or the compiler will reject (or mis-order) the migrated graphs.
|
||||
|
||||
---
|
||||
|
||||
## Implementation Units
|
||||
|
||||
Grouped into three phases: **A — core/engine** (the construct runs), **B — editor** (authoring + add-on
|
||||
palette), **C — migration/cleanup** (built-ins on the new model, legacy path retired).
|
||||
|
||||
### Phase A — Core construct + execution
|
||||
|
||||
### U1. `optional-group` IR type, parse, and validation
|
||||
|
||||
**Goal:** Introduce the `optional-group` node kind and its `template` config, and validate it by walking
|
||||
the subgraph (R1, R8).
|
||||
|
||||
**Requirements:** R1, R8, R9
|
||||
|
||||
**Dependencies:** none
|
||||
|
||||
**Files:**
|
||||
- `packages/core/src/workflow-ir-types.ts` (modify — add `"optional-group"` to `WorkflowIrNodeKind`; add
|
||||
`WorkflowOptionalGroupConfig { defaultOn?: boolean; template: { nodes; edges } }` mirroring
|
||||
`WorkflowForeachConfig` at `:129`)
|
||||
- `packages/core/src/workflow-ir.ts` (modify — `validateV2` calls a new `validateOptionalGroup(node, ...)`;
|
||||
extend cycle/reachability/seam walks to descend into the template subgraph)
|
||||
- `packages/core/src/__tests__/workflow-ir.test.ts` (modify/create — validation cases)
|
||||
|
||||
**Approach:**
|
||||
- Add the kind + config type. The group's stable enable key is its node `id` (KTD-2); `defaultOn` lives on
|
||||
the node `config`.
|
||||
- In validation, treat the template like a foreach template: its nodes are **not** in `ir.nodes`, so every
|
||||
reader/validator must walk the subgraph explicitly (learning: per-entity blast-radius `:37`). Validate:
|
||||
all template nodes reachable from the template's entry; endpoints reference template-local nodes; **no
|
||||
rework edges** inside (KTD-3); **no seam nodes** inside (mirror `validateParallelism`'s seam-in-branch
|
||||
rule, `workflow-ir.ts:190+`).
|
||||
- `parseWorkflowIr` descends into optional-group templates the same way it clamps foreach/loop configs.
|
||||
|
||||
**Patterns to follow:** `WorkflowForeachConfig` type + `validateV2`/foreach template validation in
|
||||
`workflow-ir.ts`; the seam-in-branch check in `validateParallelism`.
|
||||
|
||||
**Test scenarios:**
|
||||
- A v2 IR with one `optional-group` (valid template) parses and validates.
|
||||
- A template referencing an undefined template-local node throws `WorkflowIrError`.
|
||||
- A rework edge inside an optional-group template is rejected.
|
||||
- A seam node (e.g. `merge-gate`) inside an optional-group template is rejected.
|
||||
- An unreachable template node is rejected.
|
||||
- A graph with **no** optional-group serializes/parses byte-identically (R9).
|
||||
|
||||
**Verification:** `pnpm --filter @fusion/core test workflow-ir` green; no change to graphs without optional
|
||||
groups.
|
||||
|
||||
---
|
||||
|
||||
### U2. Executor: run-once-or-bypass dispatch for `optional-group`
|
||||
|
||||
**Goal:** Make the graph executor run an enabled group's template once and pass through a disabled group
|
||||
(R2), reading enable state from per-task `enabledWorkflowSteps` (R3).
|
||||
|
||||
**Requirements:** R2, R3
|
||||
|
||||
**Dependencies:** U1
|
||||
|
||||
**Files:**
|
||||
- `packages/engine/src/workflow-graph-executor.ts` (modify — add an `optional-group` branch in
|
||||
`runNodeAndTraverse` at `:389-485`, beside `foreach`/`loop`)
|
||||
- `packages/engine/src/workflow-graph-loop.ts` or a shared helper (modify/extract — reuse the
|
||||
single-template-walk without iteration/rework for the enabled path)
|
||||
- `packages/engine/src/__tests__/workflow-graph-optional-group.test.ts` (create — execution-level tests)
|
||||
|
||||
**Approach:**
|
||||
- Resolve `enabled = currentTask.enabledWorkflowSteps?.includes(node.id) ?? false`. **Read the freshest
|
||||
task state** at the seam (the executor already re-reads the task in `runWorkflowSteps`); ensure
|
||||
`enabledWorkflowSteps` is in whatever projection the gate reads (learning: per-task override slim-SELECT
|
||||
trap `:105`).
|
||||
- Enabled → walk `node.config.template` once via the extracted helper, threading the same
|
||||
`runTemplateNode`/`shouldTraverseEdge` deps the foreach/loop handlers pass; collect `visitedNodeIds`;
|
||||
set `context[node:id:outcome]`. Disabled → `return await traverseChildren(node, { outcome: "success",
|
||||
value: "optional-group-bypassed" })`.
|
||||
- Namespaced child ids: reuse the foreach instance-id scheme defensively — parse candidate-style and
|
||||
validate the template node exists, not just the container (learning `:56`).
|
||||
|
||||
**Execution note:** Start with the failing **two-task divergence** execution test (below) — it is the
|
||||
contract that guards the dead-toggle failure mode.
|
||||
|
||||
**Test scenarios:**
|
||||
- **Two-task divergence (critical):** two tasks identical except `enabledWorkflowSteps` — the one including
|
||||
the group id records the template's node execution; the sibling records none and reaches the same
|
||||
downstream node. (Shape from per-task-auto-merge learning `:106`.)
|
||||
- Enabled group runs its template exactly **once** (not per-step, not looped) even when the graph has a
|
||||
foreach elsewhere.
|
||||
- Disabled group is byte-inert: downstream context/outcome identical to a graph with the group removed
|
||||
(kill-switch inertness, learning `:55`).
|
||||
- A template node failure inside an enabled group surfaces as the group's outcome and routes the group's
|
||||
`failure`/`outcome:` edges.
|
||||
|
||||
**Verification:** new engine test green; existing graph-executor tests unaffected.
|
||||
|
||||
---
|
||||
|
||||
### U3. Per-task enable resolution from optional-group nodes + `defaultOn` seeding
|
||||
|
||||
**Goal:** Re-point the per-task toggle source from `ir.optionalSteps` to `optional-group` nodes and seed
|
||||
new tasks' enabled set from each group's `defaultOn` (R3, R7-prep).
|
||||
|
||||
**Requirements:** R3, R7
|
||||
|
||||
**Dependencies:** U1
|
||||
|
||||
**Files:**
|
||||
- `packages/core/src/workflow-optional-steps.ts` (modify — `resolveWorkflowOptionalSteps` scans
|
||||
`optional-group` nodes instead of `ir.optionalSteps`, preserving `ResolvedWorkflowOptionalStep[]` output)
|
||||
- `packages/core/src/` task-creation/materialization path that seeds `enabledWorkflowSteps` from `defaultOn`
|
||||
(modify — seed from optional-group `defaultOn`; today's `materializeDefaultWorkflowSteps`, see
|
||||
`types.ts:2668-2675`)
|
||||
- `packages/core/src/__tests__/workflow-optional-steps.test.ts` (modify — group-sourced resolution)
|
||||
|
||||
**Approach:**
|
||||
- Resolver: walk `ir` (v2) nodes, collect `optional-group` nodes → `{ id, name (from config), defaultOn }`.
|
||||
Keep output shape identical so the four UI surfaces (inline card, modal, Workflow tab, dropdown) need no
|
||||
change beyond what U7/U-editor already covers. Unknown/stale ids in `enabledWorkflowSteps` are ignored
|
||||
(defensive, as today).
|
||||
- Seeding: at task creation, the enabled set is the ids of optional-group nodes whose effective
|
||||
`defaultOn` is true — mirroring the prior `optionalStep.defaultOn ?? false` precedence.
|
||||
- Grep every reader of `enabledWorkflowSteps`/`optionalSteps` and confirm each now resolves from groups
|
||||
(learning: consult the override at every trigger seam `:100,:102`).
|
||||
|
||||
**Test scenarios:**
|
||||
- `resolveWorkflowOptionalSteps` over a workflow with two optional-group nodes returns both, with names and
|
||||
`defaultOn` from node config.
|
||||
- A new task created against that workflow seeds `enabledWorkflowSteps` to exactly the `defaultOn: true`
|
||||
group ids.
|
||||
- A workflow with no optional-group nodes resolves to `[]` and seeds an empty set.
|
||||
- Stale id in `enabledWorkflowSteps` (group since removed) does not crash resolution or execution.
|
||||
|
||||
**Verification:** core optional-steps tests green; creation seeding covered.
|
||||
|
||||
---
|
||||
|
||||
### Phase B — Editor authoring + add-on palette
|
||||
|
||||
### U4. Render & author the `optional-group` container in the node editor
|
||||
|
||||
**Goal:** Let an author add, name, configure (`defaultOn`), and fill an `optional-group` container,
|
||||
reusing the foreach/loop group UX, with the node type registered so it renders (R4).
|
||||
|
||||
**Requirements:** R4
|
||||
|
||||
**Dependencies:** U1 (kind exists)
|
||||
|
||||
**Files:**
|
||||
- `packages/dashboard/app/components/nodes/WorkflowNodeTypes.tsx` (modify — add `optional-group` to the
|
||||
node-type registry + icon, render as a group container like `foreach`/`loop`)
|
||||
- `packages/dashboard/app/components/workflow-flow-mapping.ts` (modify — treat `optional-group` as a group
|
||||
kind everywhere foreach/loop are special-cased: `groupTemplateConfigOf` `:266`, group child
|
||||
reassembly `:441-480`, intra-template edge handling `:517`, group delete `:611,:636`,
|
||||
condition-editability `:663`)
|
||||
- `packages/dashboard/app/components/WorkflowNodeEditor.tsx` (modify — inspector controls: group name +
|
||||
`defaultOn` toggle; help entry)
|
||||
- `packages/dashboard/app/components/nodes/node-help.ts` (modify — add an `optional-group` help entry)
|
||||
- `packages/dashboard/app/components/__tests__/workflow-flow-mapping.test.ts` (modify — round-trip group
|
||||
children)
|
||||
- `packages/dashboard/app/components/__tests__/WorkflowNodeEditor.test.tsx` (modify — add/name/toggle/fill)
|
||||
|
||||
**Approach:**
|
||||
- Mirror the `foreach`/`loop` group node: a React Flow `type: "group"` with `parentId` children using the
|
||||
existing `foreachChildFlowId` namespacing (`workflow-flow-mapping.ts:75`). `irToFlow` renders the
|
||||
template as children; `flowToIr` reassembles it (the `:454,:480` kind checks gain `optional-group`).
|
||||
- Inspector: a `defaultOn` checkbox (labeled, focus ring) and the group name; the body is authored by
|
||||
dropping nodes inside, identical to foreach.
|
||||
- **Register the node type** in `WorkflowNodeTypes.tsx` — an unregistered kind renders as
|
||||
`react-flow__node-default` with missing children (learning: worktree bundle `:24`). Verify against a
|
||||
fresh `FUSION_CLIENT_DIR` bundle, non-4040 port.
|
||||
|
||||
**Test scenarios:**
|
||||
- Adding an optional-group, dropping a prompt node inside, and saving yields IR with an `optional-group`
|
||||
node whose `template.nodes` contains the inner node (round-trip).
|
||||
- Toggling `defaultOn` marks the editor dirty and persists on save.
|
||||
- Deleting the group removes its `parentId` children (no orphans) — mirrors the foreach delete test.
|
||||
- The node renders with its registered type (not `react-flow__node-default`) — asserted via node-type
|
||||
registry presence.
|
||||
|
||||
**Verification:** mapping + editor tests green; real-browser check that the container renders, accepts
|
||||
child nodes, and the `defaultOn` toggle works (fresh worktree bundle; verify on a mobile viewport per
|
||||
Risks R-4).
|
||||
|
||||
---
|
||||
|
||||
### U5. Add-ons as insertable subgraphs (plus "insert as optional group")
|
||||
|
||||
**Goal:** Make all seven `WORKFLOW_STEP_TEMPLATES` add-ons insertable from the palette as node subgraphs,
|
||||
each also offerable wrapped in an `optional-group` (R5).
|
||||
|
||||
**Requirements:** R5, KTD-5
|
||||
|
||||
**Dependencies:** U4 (optional-group authoring exists)
|
||||
|
||||
**Files:**
|
||||
- `packages/dashboard/app/components/WorkflowNodeEditor.tsx` (modify — the "Built-in steps" palette
|
||||
section `:1002,:2741`, the existing `stepTemplateToNode()` projector `:249-275`, and the
|
||||
`handleInsertStepTemplate`/`handleInsertFragment` handlers `:1425-1453`: add an "insert as optional
|
||||
group" variant per add-on)
|
||||
- `packages/dashboard/app/components/workflow-flow-mapping.ts` (modify — `insertFragment` `:1110-1219`
|
||||
already expands subgraphs incl. group `parentId` children; add the wrap-in-`optional-group` helper that
|
||||
builds the fragment IR from a projected add-on node)
|
||||
- `packages/dashboard/app/components/__tests__/WorkflowNodeEditor.test.tsx` (modify — insert-as-node and
|
||||
insert-as-optional-group for an add-on)
|
||||
|
||||
**Approach:**
|
||||
- Project each add-on to a node using the **existing `stepTemplateToNode()`** (`WorkflowNodeEditor.tsx:
|
||||
249-275`), which already maps a `WorkflowStepTemplate` → a `prompt`/`script` node carrying its
|
||||
`prompt`/`scriptName`/`toolMode`/`gateMode`/model (KTD-5). "Insert as node" is today's behavior.
|
||||
- "Insert as optional group" wraps that projected node in an `optional-group{ template:{ nodes:[node],
|
||||
edges:[] }, defaultOn }` (seeded from the template's `defaultOn`) and inserts it through the existing
|
||||
`insertFragment` path (`workflow-flow-mapping.ts:1110-1219`), which already remaps ids, rewires internal
|
||||
edges, and expands group `parentId` children — so no new insertion engine is needed.
|
||||
- Surface both variants in the palette's existing "Built-in steps" group; keep `data-testid` conventions.
|
||||
|
||||
**Test scenarios:**
|
||||
- Each of the seven add-ons appears in the palette and inserts a node carrying its template config.
|
||||
- "Insert as optional group" for `security-audit` yields an `optional-group` whose `template` holds the
|
||||
security-audit node and whose `defaultOn` matches the template default.
|
||||
- Inserting an add-on subgraph remaps ids so two insertions of the same add-on do not collide.
|
||||
- A plugin-contributed template (if present) still inserts as a node (catalog stays flat, KTD-5).
|
||||
|
||||
**Verification:** editor tests green; real-browser insert of an add-on and an optional-group-wrapped add-on,
|
||||
then save → reopen round-trip.
|
||||
|
||||
---
|
||||
|
||||
### Phase C — Migrate built-ins, retire the legacy path
|
||||
|
||||
### U6. Migrate coding + stepwise-coding built-ins to `optional-group` browser-verification
|
||||
|
||||
**Goal:** Express `browser-verification` as an `optional-group` (default OFF) in both built-ins, preserving
|
||||
runtime behavior, and update parity oracles (R6).
|
||||
|
||||
**Requirements:** R6, R8
|
||||
|
||||
**Dependencies:** U2, U3 (execution + seeding), U1 (kind)
|
||||
|
||||
**Files:**
|
||||
- `packages/core/src/builtin-coding-workflow-ir.ts` (modify — replace `optionalSteps:[{templateId:
|
||||
"browser-verification"}]` `:123` + the `workflow-step` seam node `:69` with a `browser-verification`
|
||||
`optional-group` on the pre-merge path)
|
||||
- `packages/core/src/builtin-stepwise-coding-workflow-ir.ts` (modify — add the `browser-verification`
|
||||
optional-group on the pre-merge path; stepwise had no `workflow-step` seam at all)
|
||||
- `packages/core/src/__tests__/` built-in IR snapshot/parity fixtures (modify — update byte-identity
|
||||
oracles deliberately)
|
||||
- `packages/engine/src/__tests__/` stepwise/coding execution parity test (modify — enabled-runs/disabled-
|
||||
skips at the built-in level)
|
||||
|
||||
**Approach:**
|
||||
- Place the optional-group on the success path where `workflow-step` sat (pre-merge, after execute/steps,
|
||||
before review), so an enabled task runs browser-verification pre-merge exactly as before.
|
||||
- The stepwise IR is a documented byte-identity parity oracle — adding the construct shifts its snapshot;
|
||||
update the fixture deliberately and confirm the group runs **once** post-foreach, not per step-instance
|
||||
(learning `:55`, prior plan R-5).
|
||||
|
||||
**Test scenarios:**
|
||||
- `resolveWorkflowOptionalSteps(BUILTIN_CODING_WORKFLOW_IR)` returns one `browser-verification` group,
|
||||
`defaultOn: false`; same for stepwise.
|
||||
- Execution: a coding task with the group enabled runs browser-verification pre-merge; disabled does not
|
||||
(two-task divergence at the built-in level).
|
||||
- Stepwise: same divergence; the group runs once after the foreach completes.
|
||||
- Both built-ins parse and pass `validateV2`.
|
||||
|
||||
**Verification:** core + engine built-in tests green; parity oracles updated and passing.
|
||||
|
||||
---
|
||||
|
||||
### U7. Retire the declaration-based optional-steps path
|
||||
|
||||
**Goal:** Remove the now-dead `WorkflowOptionalStep`/`optionalSteps` declaration, the `workflow-step` seam
|
||||
node + handler, and its compiler seam-anchor, keeping the four toggle surfaces working via U3's resolver
|
||||
(R7).
|
||||
|
||||
**Requirements:** R7
|
||||
|
||||
**Dependencies:** U3 (resolver re-pointed), U6 (built-ins migrated — nothing still declares `optionalSteps`)
|
||||
|
||||
**Files:**
|
||||
- `packages/core/src/workflow-ir-types.ts` (modify — remove `WorkflowOptionalStep` + `WorkflowIrV2.
|
||||
optionalSteps`)
|
||||
- `packages/core/src/workflow-compiler.ts` (modify — remove `workflow-step` from `SEAM_NAMES` `:45` and
|
||||
`expectedSeamOrder` `:170`)
|
||||
- `packages/engine/src/executor.ts` (modify — remove `runWorkflowSteps` + the `workflow-step` seam
|
||||
dispatch, now unreachable)
|
||||
- `packages/dashboard/app/components/workflow-flow-mapping.ts` (modify — drop `optionalSteps` threading in
|
||||
`flowToIr`/`optionalStepsOf` if present from the prior plan)
|
||||
- `packages/core/src/__tests__/`, `packages/engine/src/__tests__/` (modify — delete/replace tests asserting
|
||||
the legacy path; keep the resolver/seeding tests now backed by groups)
|
||||
|
||||
**Approach:**
|
||||
- This is a **behavior-affecting removal** — follow the codebase's Surface Enumeration discipline (see the
|
||||
section below). Enumerate every reader of `optionalSteps`/`workflow-step`/`runWorkflowSteps` and confirm
|
||||
each is migrated or removed; do not leave a mock-masked dead path (learning: branch-group dead-wiring).
|
||||
- Removing the seam anchor changes the compiler's accepted pipeline — confirm no remaining built-in or
|
||||
fragment references `workflow-step`, then drop it from both anchor lists together with the built-in edits
|
||||
from U6.
|
||||
|
||||
**Test scenarios:**
|
||||
- Grep proves zero remaining references to `optionalSteps`, `WorkflowOptionalStep`, `workflow-step` seam,
|
||||
and `runWorkflowSteps` in non-test source.
|
||||
- The compiler accepts the migrated built-ins with `workflow-step` removed from the seam order.
|
||||
- The four toggle surfaces (inline card, New Task modal, Workflow tab, steps dropdown) still render and
|
||||
submit `enabledWorkflowSteps` — now sourced from optional-group nodes (regression).
|
||||
- A pre-existing workflow JSON that still carries `optionalSteps` (legacy persisted) does not crash parse —
|
||||
the key is ignored, not fatal (back-compat decision: tolerate-and-drop). *(Confirm this stance in review;
|
||||
alternative is a one-time parse upgrade.)*
|
||||
|
||||
**Verification:** full core + engine + dashboard suites green; `pnpm lint`, `pnpm typecheck`, `pnpm build`,
|
||||
`pnpm test:gate` pass.
|
||||
|
||||
---
|
||||
|
||||
## Surface Enumeration
|
||||
|
||||
Behavior-affecting change (new execution construct + removal of the legacy path) and a UI-affordance change
|
||||
(new node kind + retired toggle source), so per AGENTS.md ("Fix the Invariant, Not the Repro", FN-5893) the
|
||||
surfaces are enumerated:
|
||||
|
||||
- **Workflow providers/graphs:** built-in **coding** and **stepwise-coding** (both migrated, U6); any
|
||||
fragment or user workflow that declared `optionalSteps` (tolerated-and-dropped, U7).
|
||||
- **Execution states:** group **enabled** (runs once), **disabled** (passes through), **stale id** in
|
||||
`enabledWorkflowSteps` (ignored), **template failure** (routes group failure edge).
|
||||
- **Editor breakpoints:** desktop and **mobile** node editor — container render, child placement,
|
||||
`defaultOn` toggle, palette insert (both variants).
|
||||
- **Per-task toggle surfaces:** inline quick-create card, New Task modal, task-detail Workflow tab, steps
|
||||
dropdown — all re-sourced from optional-group nodes (U3/U7).
|
||||
- **Validation:** subgraph walked (not just `ir.nodes`); no rework/seam inside a group; graphs without
|
||||
optional groups byte-identical.
|
||||
- **Compiler:** seam-anchor list with `workflow-step` removed; canonical pipeline still valid for migrated
|
||||
built-ins.
|
||||
|
||||
## Symptom Verification
|
||||
|
||||
- **Original symptom:** optionality is invisible and unplaceable — enabled steps run in one lump at a
|
||||
hidden seam, and add-ons cannot be composed or gated in the graph.
|
||||
- **Exact reproduction:** build/inspect the coding workflow; `browser-verification` appears only as an
|
||||
`optionalSteps` declaration, runs at the `workflow-step` seam, and is absent from the graph; add-ons
|
||||
appear in the palette only as flat single steps.
|
||||
- **Assertion it is gone:** an enabled optional-group runs its placed template once at its graph position
|
||||
and a disabled one is inert (two-task divergence execution test, U2/U6); every add-on inserts as a
|
||||
node/optional-group subgraph (U5); no `workflow-step`/`optionalSteps` path remains (U7 greps).
|
||||
|
||||
---
|
||||
|
||||
## Scope Boundaries
|
||||
|
||||
**In scope:**
|
||||
- `optional-group` IR kind + validation (U1), executor run/bypass (U2), per-task resolution + seeding (U3).
|
||||
- Editor container authoring + node-type registration (U4); add-ons as insertable subgraphs incl.
|
||||
optional-group wrapping (U5).
|
||||
- Built-in coding + stepwise migration (U6); retiring the declaration/seam/compiler-anchor path (U7).
|
||||
|
||||
**Already built (reuse, not rebuilt):**
|
||||
- Container-node toolchain: `foreach`/`loop` config, group rendering, `parentId` children,
|
||||
`insertFragment` subgraph insertion.
|
||||
- Per-task `enabledWorkflowSteps` column, create-surface toggles, the four toggle UIs, and
|
||||
`resolveWorkflowOptionalSteps`'s output shape (source re-pointed in U3).
|
||||
- Step→node projection (`workflow-steps-to-ir.ts`) reused to project add-ons (U5).
|
||||
|
||||
### Delivered cohort (this PR) vs. Deferred
|
||||
This PR delivers **U1–U6 plus U7a** (10 commits). U7a retired the legacy declaration *model*: the core
|
||||
`WorkflowOptionalStep` type + `WorkflowIrV2.optionalSteps` field + `validateOptionalSteps`, and the editor's
|
||||
declaration **authoring** surface (`WorkflowOptionalStepsPanel`, `optionalStepsOf`, the `flowToIr`
|
||||
`optionalSteps` threading). A code-review pass also fixed a P1 (the optional-group toggle-id collision in
|
||||
enable resolution) — captured in the commit history and in
|
||||
`docs/solutions/logic-errors/optional-group-toggle-id-remapped-by-step-materializer.md`. The per-task toggle
|
||||
surfaces (`WorkflowOptionalStepsDropdown`, inline card, modal, Workflow tab) stayed — they consume the
|
||||
distinct `ResolvedWorkflowOptionalStep`.
|
||||
|
||||
- **Deferred: the `workflow-step` seam infrastructure removal.** What remains of "full U7" is excising the
|
||||
`workflow-step` seam itself — a shared `WorkflowSeam` union member woven through ~9 engine runtime files
|
||||
(`runtime-primitives`, `step-session-executor`, `workflow-node-handlers`, `active-session-registry`,
|
||||
`workflow-graph-task-runner`, `executor.runWorkflowSteps`, the compiler seam-anchor). It is now orphaned
|
||||
(no built-in graph reaches it) but inert; excising it is its own focused refactor with its own blast radius.
|
||||
- **Nested/conditional groups** (an optional-group inside a split/foreach, or gated by a workflow field
|
||||
rather than the per-task toggle) — single-level, per-task-toggle only for now.
|
||||
- **Plugin-contributed add-ons as optional-group presets** beyond inserting them as flat nodes.
|
||||
- The prior plan's deferred **unified per-task workflow-facet override** abstraction (optional steps +
|
||||
auto-merge + column-agent overrides) — strong `/ce-compound` candidate once this lands.
|
||||
|
||||
**Out of scope:**
|
||||
- New persistence/migrations. Reuses `tasks.enabledWorkflowSteps`; `optional-group` is just a node kind.
|
||||
- Rewriting `WorkflowStepTemplate` into a nodes+edges shape (KTD-5 keeps it flat).
|
||||
- Changing unrelated execution (merge lifecycle, branch groups, PR nodes).
|
||||
|
||||
---
|
||||
|
||||
## Risks & Dependencies
|
||||
|
||||
- **R-1 — Subgraph-walking validation/readers miss template children.** Optional-group template nodes are
|
||||
not in `ir.nodes`; any validator, reachability pass, or id parser that only sees top-level nodes will be
|
||||
wrong. *Mitigation:* walk the subgraph explicitly and parse namespaced child ids candidate-style,
|
||||
validating the template node exists, not just the container.
|
||||
(`docs/solutions/architecture-patterns/per-entity-execution-principal-override-blast-radius.md:37,:56`)
|
||||
- **R-2 — Per-task enable consulted only in the executor branch.** If skip-vs-run is read only where the
|
||||
group executes and not at every gate/trigger that decides whether to run it, the toggle silently no-ops;
|
||||
a slim SELECT omitting `enabledWorkflowSteps` reads `undefined`. *Mitigation:* grep every gate; ensure the
|
||||
column is selected; ship the two-task divergence test.
|
||||
(`docs/solutions/logic-errors/per-task-auto-merge-override-ignored-by-trigger-gates.md:100,:102,:105,:106`)
|
||||
- **R-3 — Seam-anchor / parity-oracle drift on built-in migration.** `workflow-step` is a compiler seam
|
||||
anchor and the stepwise IR is a byte-identity oracle; removing the seam and adding the construct shifts
|
||||
snapshots and can break compile-order validation. *Mitigation:* edit built-ins, seam-anchor lists, and
|
||||
parity fixtures together (KTD-6); confirm the group runs once post-foreach.
|
||||
(`docs/solutions/architecture-patterns/workflow-native-runtime-primitives.md:117`; prior plan R-5)
|
||||
- **R-4 — Editor node-type registration + mobile.** An unregistered `optional-group` renders as
|
||||
`react-flow__node-default` with missing children, looking like a source bug; per-task toggle controls have
|
||||
a real-browser-only mobile failure history. *Mitigation:* register the node type and verify against a
|
||||
fresh `FUSION_CLIENT_DIR` worktree bundle on a non-4040 port; real-browser-check the `defaultOn` toggle on
|
||||
a mobile viewport.
|
||||
(`docs/solutions/developer-experience/browser-testing-dashboard-from-worktree-safely.md:24,:44`;
|
||||
`docs/solutions/ui-bugs/mobile-auto-merge-toggle-document-scroll-blank.md:32,:53`)
|
||||
- **R-5 — Mock-masked dead wiring on removal.** Retiring the legacy path risks a green suite over a feature
|
||||
whose new path is never actually exercised (the branch-group failure class). *Mitigation:* execution-level
|
||||
(not traversal-only) tests for enabled-runs/disabled-skips at both the construct and built-in levels;
|
||||
grep-prove the old path is gone.
|
||||
(`docs/solutions/integration-issues/branch-group-single-pr-synthetic-id-dead-wiring.md`)
|
||||
|
||||
---
|
||||
|
||||
## Sources & Research
|
||||
|
||||
- **Execution seam:** `packages/engine/src/workflow-graph-executor.ts:389-485` (`runNodeAndTraverse`
|
||||
foreach/loop dispatch), `:657` (`shouldTraverseEdge`); `workflow-graph-foreach.ts`,
|
||||
`workflow-graph-loop.ts` (template sub-walk to reuse).
|
||||
- **IR + validation:** `packages/core/src/workflow-ir-types.ts:41-165` (node/edge/container types),
|
||||
`:314-339` (legacy `optionalSteps`); `packages/core/src/workflow-ir.ts:190+` (seam-in-branch),
|
||||
`:1218-1298` (`validateV2`, cycles/endpoints), `:1327` (`parseWorkflowIr`).
|
||||
- **Per-task facet:** `packages/core/src/types.ts:2421,2663-2680` (`enabledWorkflowSteps`, `workflowId`
|
||||
precedence), `db.ts:324`, `store.ts:424,2140`; `workflow-optional-steps.ts` (resolver to re-point);
|
||||
`executor.ts` `runWorkflowSteps` (seam consumer to retire).
|
||||
- **Add-on catalog:** `packages/core/src/types.ts:868-899` (`WorkflowStepTemplate`), `:902-1150`
|
||||
(seven `WORKFLOW_STEP_TEMPLATES`: documentation-review, qa-check, security-audit, performance-review,
|
||||
accessibility-check, browser-verification, frontend-ux-design); `WorkflowNodeEditor.tsx:249-275`
|
||||
(`stepTemplateToNode` add-on→node projector to reuse).
|
||||
- **Editor:** `packages/dashboard/app/components/workflow-flow-mapping.ts:46-82,266-269,316-403,431-550`
|
||||
(group children, `groupTemplateConfigOf`), `:1110-1219` (`insertFragment` subgraph insertion);
|
||||
`WorkflowNodeEditor.tsx:985-1010` (palette: fragments/steps/plugins, sourced from
|
||||
`/api/workflow-step-templates`), `:1425-1453` (insert handlers); `nodes/WorkflowNodeTypes.tsx` (node-type
|
||||
registry to extend).
|
||||
- **Compiler:** `packages/core/src/workflow-compiler.ts:45,165-196` (seam anchors + canonical order).
|
||||
- **Built-ins:** `packages/core/src/builtin-coding-workflow-ir.ts:69,123`,
|
||||
`builtin-stepwise-coding-workflow-ir.ts:132`.
|
||||
- **Prior art:** `docs/plans/2026-06-20-001-feat-workflow-optional-steps-node-editor-modal-plan.md` (the
|
||||
declaration-based system this replaces); institutional learnings cited inline under Risks.
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user