Introduces a persisted planner-overseer intervention timeline surfaced in the task-detail Planner Oversight cluster, recording stage, reason, action taken, outcome, attempt count/limit, and source links for each intervention.
- Add core `PlannerInterventionEntry` type plus `recordPlannerIntervention`/`getPlannerInterventionTimeline` helpers that persist entries via the run-audit store under the `overseer:intervention` mutation
- Add `PlannerInterventionTimeline` dashboard component rendering the timeline (stage/reason/action/outcome/attempts/links) with associated styles
- Wire the new API route/legacy handler and TaskDetailModal integration to expose and render the timeline
- Add unit tests for the core helpers and the new UI component
- Add changeset for the new minor feature and update architecture/dashboard-guide docs
Files changed:
$(cat /tmp/diffstat_7519.txt)
Fusion-Task-Id: FN-7519
Fusion-Task-Lineage: 3c4fcda3-9eb2-46d3-b142-b0c7d6334cd0
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
Fixes splitSettingsSave so the five global GitLab keys are diffed only against the scoped global initial values, not the project-merged effective values, so a real global edit is no longer dropped when it happens to match a project override.
- Add GLOBAL_GITLAB_SCOPED_ONLY_KEYS (gitlabEnabled, gitlabInstanceUrl, gitlabApiBaseUrl, gitlabAuthToken, gitlabAuthTokenType) that never fall back to merged initialValues when computing the save diff
- Update splitSettingsSave to treat these keys as scoped-global-only, treating a missing scoped-global initial as undefined rather than falling back to the merged value
- Add regression tests in settings-save-split.test.ts covering the scoped-vs-merged diff behavior
- Add regression tests in SettingsModal.general.test.tsx covering the save flow end-to-end
- Add changeset fn-7535-global-gitlab-setting-save.md (patch, fix)
Files changed:
.changeset/fn-7535-global-gitlab-setting-save.md | 7 +++
.../app/__tests__/settings-save-split.test.ts | 62 ++++++++++++++++++++++
.../__tests__/SettingsModal.general.test.tsx | 36 +++++++++++++
.../app/components/settings/save-split.ts | 26 ++++++++-
4 files changed, 129 insertions(+), 2 deletions(-)
Fusion-Task-Id: FN-7535
Fusion-Task-Lineage: f9cd212e-a691-4cd9-8946-48c59537d583
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
Route window resize/orientationchange/scroll through the same opening-viewport guard used by visualViewport so a same-gesture echo repositions the Activity menu instead of closing it, fixing the dropdown not showing on mobile.
- Add handleGuardedViewportChange shared by resize/orientationchange/visualViewport listeners so an opening-gesture echo only repositions the menu instead of closing it
- Add handleScrollChange to treat scrolls originating inside the .detail-tabs horizontal tab strip as benign (reposition-only), since window scroll listens with capture and would otherwise see nested scroller scrolls as a close signal
- Add regression tests covering mobile Activity dropdown open/scroll/resize behavior
- Add changeset for the fix
- Update dashboard guide docs
Files changed:
.changeset/fn-7536-activity-dropdown-mobile.md | 7 +
docs/dashboard-guide.md | 1 +
.../dashboard/app/components/TaskDetailModal.tsx | 38 +++--
.../TaskDetailModal.task-activity-chat.test.tsx | 161 +++++++++++++++++++++
4 files changed, 196 insertions(+), 11 deletions(-)
Fusion-Task-Id: FN-7536
Fusion-Task-Lineage: c8a3b11e-9bd2-4a61-b49e-038c93cfe4d2
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
Branch-group completion no longer silently drops archived-but-unlanded members, which previously let genuinely-incomplete groups be flagged complete and promoted.
- listTasksByBranchGroup now scans with includeArchived:true so archived members stay counted in the group's total instead of dropping out silently
- ArchivedTaskEntry gains a persisted mergeDetails snapshot so an archived member that had already landed is still distinguished from one that never landed
- store.ts archival paths (task->archive projection) now carry mergeDetails through so isBranchGroupMemberLanded keeps working post-archival
- Added regression coverage in branch-group-store.test.ts and group-merge-coordinator.test.ts for archived-landed and archived-unlanded gating
- Added changeset documenting the fix as a patch-level bug fix
Files changed:
.changeset/fn-7534-branch-group-archived-member.md | 7 +
docs/dashboard-guide.md | 2 +
packages/core/src/__tests__/branch-group-store.test.ts | 77 +++++++++++
packages/core/src/store.ts | 30 ++++-
packages/core/src/types.ts | 11 ++
packages/engine/src/__tests__/group-merge-coordinator.test.ts | 148 ++++++++++++++++++++-
6 files changed, 273 insertions(+), 2 deletions(-)
Fusion-Task-Id: FN-7534
Fusion-Task-Lineage: 510af857-ce08-49a0-a2a1-41b3ad473804
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
Adds two display-only badges to TaskCard so operators can see planner oversight configuration and live overseer activity at a glance.
- Resolve and display the workflow's effective `plannerOversightLevel` (observe/steer/autonomous) per task, with a module-level cache keyed by (projectId, workflowId) and in-flight de-duplication to avoid redundant `/api/workflows/:id/setting-values` fetches across cards sharing a workflow.
- Gate oversight badge rendering until the workflow-effective value resolves (or a per-task override is known) so the very first render never shows a guessed schema-default badge.
- Derive a card-local "active overseer state" indicator (Executor/Reviewer/Merger/Pull request/Workflow gate) that mirrors the engine's `resolveWatchedStage` precedence using only fields already present on the Task payload (column, paused, pausedReason, prInfo, reviewState, workflowTransitionNotification), since the real in-memory monitor state has no persisted/API surface today.
- Add corresponding CSS badge modifiers and update dashboard-guide.md documentation.
- Add TaskCard.oversight.test.tsx covering the new badges and update existing TaskCard tests/badge-wrap test for the new markup.
Files changed:
docs/dashboard-guide.md | 4 +
packages/dashboard/app/components/TaskCard.css | 106 +++++-
packages/dashboard/app/components/TaskCard.tsx | 369 +++++++++++++++++++-
.../__tests__/TaskCard.badge-wrap.test.tsx | 1 +
.../__tests__/TaskCard.oversight.test.tsx | 383 +++++++++++++++++++++
.../app/components/__tests__/TaskCard.test.tsx | 32 +-
6 files changed, 889 insertions(+), 6 deletions(-)
Fusion-Task-Id: FN-7516
Fusion-Task-Lineage: ce909409-862b-4697-a19e-d6735a3b572d
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
## The bug
`GET /api/command-center/tokens` documents and accepts `groupBy=task`,
but the `task` dimension was never actually wired through the
aggregator. Four enumeration sites all stopped at `model | provider |
node | agent`:
- `TokenGroupBy` (core `token-analytics.ts`) did not include `"task"`.
- `groupKeyFor()` had no `case "task"`.
- `VALID_GROUP_BY` (dashboard route) rejected the value, so
`resolveGroupBy()` returned `undefined`.
- `groupAttributes()` (core `otel-metrics.ts`) emitted no attribute for
it.
Net effect: a caller asking for a per-task rollup silently fell back to
**ungrouped grand totals** — the per-task breakdown returned zero
groups, even though every task carries `tokenUsage*` and the data was
right there.
## The fix
Thread `"task"` through all four sites (5 functional lines + doc/test):
- Add `"task"` to the `TokenGroupBy` union — this makes the two `switch`
statements **compiler-exhaustive**, so `tsc` forces the two new cases
(no silent gaps).
- `groupKeyFor`: task rows group by their task id; chat rows have no
task and return `null`, mirroring the existing `node` case.
- `groupAttributes`: emit `task.id` for OTLP export, matching `node.id`
/ `agent.id`.
- `VALID_GROUP_BY`: accept `"task"`.
No schema or migration change — the task id is already on the row.
## Verification
- `pnpm --filter @fusion/core typecheck` and `@fusion/dashboard
typecheck` — clean.
- Extended the existing `groups by provider, node, agent` core test with
a `groupBy: "task"` assertion (two tasks → two groups keyed by task id,
100 / 200 tokens). Full suites green: **core 25/25**, **dashboard
325/325**.
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **New Features**
* Added task-based token analytics grouping, alongside existing grouping
options.
* Analytics views and metrics now include task-level breakdowns when
available.
* **Bug Fixes**
* Improved grouping behavior so task totals are reported correctly in
analytics results.
* **Documentation**
* Updated supported analytics options to reflect task grouping in
endpoint and metric descriptions.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
## What
Adds a new built-in workflow **Coding (Ideas)** — a capture-first
variant of the default coding pipeline that puts a manual **Ideas (no
AI)** intake in front of a merged **Todo** (planner + capacity) column.
The board becomes five stages, each staffed by a distinct role:
```
Ideas (no AI) → Todo (planner) → In-progress (coder) → In-review (reviewer) → Done
```
## Why
The current board parks un-worked cards in a passive **Todo** column
where no agent is active. Operators asked for a way to (1) capture ideas
without the engine auto-planning them, and (2) collapse the triage/todo
split so every visible column has an agent working it — planning now
happens *in* Todo. A "Ready" badge distinguishes planned cards waiting
for a capacity slot from freshly promoted unplanned ones.
## How it works
1. **Create** a task against Coding (Ideas) → it lands in **Ideas**
(`autoTriage:false` intake). The triage service ignores it — no AI runs.
2. **Start** (button on the card, or drag) moves it to **Todo**. The
triage poll discovers the unplanned card (bootstrap-stub PROMPT.md) and
plans it in place.
3. While planning the card shows **Planning**; once the spec is written
it shows **Ready** and waits for an in-progress slot under the normal
capacity hold.
4. From Todo onward the graph is identical to the default Coding
workflow (stepwise execution → optional code review → merge).
## Engine changes
| Surface | Change |
|---|---|
| `createTask` (`store.ts`) | Lands cards in the workflow's intake
column (`resolvedEntryColumn`) instead of hardcoding `"triage"`. Default
workflow is byte-identical (intake resolves to `"triage"`).
Bootstrap-prompt check generalized to all pre-planning columns. |
| Triage poll (`triage.ts`) | Also discovers unplanned `todo` tasks
(bootstrap-stub prompt); `finalizeApprovedTask` skips the redundant
triage→todo move for in-place planning; planning-concurrency counter
covers both columns. |
| Scheduler (`scheduler.ts`) | Skips `todo` tasks with
`status:"planning"` or a bootstrap-stub prompt so unplanned cards are
never dispatched. |
| TaskCard (`TaskCard.tsx`) | **Start** button on ideas cards; **Ready**
badge on planned todo tasks. |
| Board (`board-workflows.ts`) | `ideas` column label. |
All engine changes are **gated** — they only affect workflows whose
intake is not `"triage"`, so the default Coding workflow and every
existing built-in are byte-identical in behavior.
## Tests
- `builtin-coding-ideas-workflow-ir.test.ts` *(new)* — column set,
intake trait (`autoTriage:false`), merged todo traits, node re-homing
(start→ideas, planning→todo), optional-group defaults, round-trip.
- `store-create-intake-column.test.ts` *(new)* — createTask lands in
`ideas` for explicit + default selection, `triage` for the default
workflow, writes a bootstrap prompt.
- Updated `builtin-workflows.test.ts` catalog-order assertion for the
new entry.
## Verification
- Typecheck: core ✓ engine ✓ dashboard ✓
- Lint ✓ · Changeset format ✓ (`minor`)
- Merge gate (`test:gate`): 321 engine-core + 63 CI-shape ✓
- Regression suites: triage (39), concurrency (165),
movement/migration/hooks (259), builtin workflows (65), store-create
(54) — all green
- `verify:fast`: workspace build + CLI build + boot smoke (`fn --help` +
real `/api/health`) ✓
## Changeset
`.changeset/fn-coding-ideas-workflow.md` — `@runfusion/fusion: minor`
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **New Features**
* Added a new “Coding (Ideas)” builtin workflow with an Ideas intake
stage and merged planning flow.
* Updated task cards to support a **Start** action and show a **Ready**
badge for qualifying planning-stage tasks.
* **Bug Fixes**
* Tasks created for the Ideas workflow now persist into the correct
entry column and get the right prompt bootstrapping.
* Scheduler and triage avoid promoting/releasing unplanned todo tasks
that still contain the bootstrap prompt stub, and stale planning is
cleaned up across the merged intake flow.
* **Tests**
* Added coverage for the new builtin workflow IR and create-task intake
wiring.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
## The bug
Per-task token usage (and the cost figures derived from it) is
**silently dropped the moment a task is archived**. A task that burned
millions of tokens shows `0` — or nothing — everywhere once it leaves
the live board.
Root cause is a field whitelist plus a missing type field:
- `TaskStore.taskToArchiveEntry()` (`packages/core/src/store.ts`)
constructs the archived record from an **explicit property whitelist**.
It copies `modelId` / `modelProvider` / `planningModelId` / … but never
`task.tokenUsage`.
- `ArchivedTaskEntry` (`packages/core/src/types.ts`) has no `tokenUsage`
field, so even a stray copy would be dropped by the type.
At archival time the task is DB-hydrated and still carries `tokenUsage`,
and the live `tasks` row (with its `tokenUsage*` columns) is then
deleted — so the whitelist is the only place the data survives or dies.
Result: `archive.db` (`archived_tasks.taskJson`) never contains token
stats. Any tool that reports token/cost usage can only ever see the
small live working set, never the hundreds of finished tasks.
## The fix
Thread `tokenUsage` through the archive round-trip (5 lines, all
pass-through of the already-typed `TaskTokenUsage`):
- `ArchivedTaskEntry` gains an optional `tokenUsage?: TaskTokenUsage`
field.
- `taskToArchiveEntry()` copies `tokenUsage: task.tokenUsage` (write
path).
- `archiveEntryToTask()` and `unarchiveTask()` copy `tokenUsage:
entry.tokenUsage` (both restore paths), so restored tasks keep their
history too.
Because the archived entry is serialized into `taskJson`, no DB
migration/column is needed — the counts land in the existing JSON blob
and read back via `json_extract(taskJson, '$.tokenUsage.totalTokens')`
(and the `inputTokens` / `outputTokens` / `cachedTokens` /
`cacheWriteTokens` breakdown).
## Verification
Applied the equivalent change to the bundled `dist/bin.js` on a live
install and archived a 9.9M-token task into an isolated copy of the
store. The full breakdown survived into `archive.db`:
```
inputTokens=119 outputTokens=26100 cachedTokens=9637483 cacheWriteTokens=233674
totalTokens=9897376 modelId=claude-sonnet-4-6 (+ per-model split intact)
```
Without the change the same archival leaves `tokenUsage` absent from the
entry.
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **Bug Fixes**
* Archived tasks now keep token usage details when saved and restored,
so task history remains accurate across archive flows.
* Restored archived tasks now display the same usage accounting they had
before being archived.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
- Derive the Start button target from the workflow's ordered move columns
instead of hard-coding 'todo', so a manual-intake workflow whose first
working stage has a different id transitions correctly. Falls back to
'todo' when column metadata is unavailable.
- Gate bootstrap prompt to entry column/triage only, not every non-execution
column, so direct createTask({column:'todo'}) keeps generateSpecifiedPrompt.
- Guard the workflow-column hold-release dispatch path (reserveSlot) against
planning-status and bootstrap-stub todo tasks, matching the legacy filter.
- Extend clearStaleSpecifyingStatuses startup sweep to the todo column so a
restarted in-place planning task does not hold a maxTriageConcurrent slot.
- Gate the Start button on the intake column flag instead of the literal
'ideas' id, so any manual-intake workflow gets the affordance.
- Add regression test: direct todo create must not get a bootstrap stub.
Restore the stable extension suite by quarantining only the dist-barrel recompilation case.
- Move the built @fusion/core dist-barrel extension test into its own file.
- Re-admit extension.test.ts while keeping the isolated dist-barrel file quarantined.
- Update the quarantine ledger and velocity baseline to reflect the narrowed quarantine.
Files changed:
docs/test-velocity-baseline.md | 10 +-
.../src/__tests__/extension-dist-barrel.test.ts | 230 +++++++++++++++++++++
packages/cli/src/__tests__/extension.test.ts | 103 +--------
packages/cli/vitest.config.ts | 5 +-
scripts/lib/test-quarantine.json | 4 +-
5 files changed, 247 insertions(+), 105 deletions(-)
Fusion-Task-Id: FN-7530
Fusion-Task-Lineage: 7b07540f-689b-4133-b590-a39427095397
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
Update the App test to assert the open desktop search panel state after rerender.\n\n- Avoid re-clicking the desktop search toggle after the non-mobile panel stays open across rerender.\n- Assert the branch-filter selects directly while the search panel remains rendered.\n\nFiles changed:\n packages/dashboard/app/components/__tests__/App.test.tsx | 9 +++++++--\n 1 file changed, 7 insertions(+), 2 deletions(-)
Fusion-Task-Id: FN-7529
Fusion-Task-Lineage: 1495f872-d262-4ea4-83f2-5a53ecf7a202
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
The command-center token endpoint accepts groupBy=task, but the
dimension was never wired through: TokenGroupBy did not include "task",
groupKeyFor had no task case, VALID_GROUP_BY rejected it, and
groupAttributes emitted no attribute. As a result groupBy=task silently
fell back to ungrouped totals — the per-task rollup returned zero groups
even though tasks.tokenUsage* is populated per task.
Thread "task" through all four enumeration sites (the union type makes
the two switches compiler-exhaustive). Task rows group by task id; chat
rows have no task and return null, mirroring the existing node case.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
taskToArchiveEntry() builds the archived record from an explicit field
whitelist that omits task.tokenUsage, and ArchivedTaskEntry has no
tokenUsage field — so a task's input/output/cache token (and derived cost)
accounting is silently dropped the moment it is swept out of the live
tasks table into archive.db. Every archived task loses its token stats.
Add tokenUsage to the ArchivedTaskEntry type and thread it through both the
archive write path (taskToArchiveEntry) and the two restore paths
(archiveEntryToTask, unarchiveTask) so the counts round-trip intact.
Require generated task specs to summarize the requested before-to-after transformation near the top.
- Add a Before → After Transformation section to standard and fast planning prompt templates.\n- Document the new task definition section and cover it with prompt regression tests.\n- Add a patch changeset for the published Fusion package.\n\nFiles changed:\n .changeset/fn-7499-before-after-transformation.md | 7 +++++++\n docs/task-management.md | 1 +\n packages/core/src/__tests__/agent-prompts.test.ts | 17 +++++++++++++++\n packages/core/src/agent-prompts.ts | 25 +++++++++++++++++++----\n packages/engine/src/__tests__/triage.test.ts | 20 ++++++++++++++++++\n 5 files changed, 66 insertions(+), 4 deletions(-)
Fusion-Task-Id: FN-7499
Fusion-Task-Lineage: d049b5d6-a4bc-40dd-831d-04a11f9dc2cf
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
Show triage task cards when Plan Review is actively running.
- Render the existing progress affordance for triage cards only when unified progress has an active item.
- Keep enabled-but-idle workflow steps hidden to avoid false active indicators and empty progress shells.
- Cover running, idle, and empty triage progress states in TaskCard tests.
- Add a patch changeset for the published Fusion package.
Files changed:
.../FN-7492-task-card-plan-review-progress.md | 7 +++
packages/dashboard/app/components/TaskCard.tsx | 7 ++-
.../app/components/__tests__/TaskCard.test.tsx | 73 ++++++++++++++++++++++
3 files changed, 86 insertions(+), 1 deletion(-)
Fusion-Task-Id: FN-7492
Fusion-Task-Lineage: 582aea40-8a14-4571-9a7c-e770bc2ab1fb
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>