aedee4b8231bf050c3240a00ab6645ede5d87ee9
173 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
9eae6b9bc5 |
fix(auth): a provider's first-ever login silently saved nothing
Operator could not log in to Anthropic or Codex on a fresh container: every
attempt ended "Login did not complete. Please try again.", while the same
providers worked flawlessly on their long-lived native install.
FusionAuthStorage.modify() is the seam pi persists a COMPLETED LOGIN through
(Models.login -> credentials.modify(provider.id, ...) in pi-ai models.js:198).
It resolved its write target with `creating: false` and returned before invoking
the callback whenever the provider had no credential row yet:
const target = this.resolveWriteTarget(provider, current, false);
if (!target || !this.credential(target, current)) return { changed: false };
So a first login completed its browser flow, exchanged the code, took and
released the lock file, wrote NOTHING, and resolved as success — leaving the
dashboard poll to see authenticated:false and report the generic failure.
It reproduces only on a store with no existing row, which is why it looked
environment-specific: an install that has logged in before takes the same path
as a refresh over an existing row and is fine, while every new container, new
machine, or wiped ~/.fusion can never complete a first login for ANY provider.
Evidence from the operator's container: flow ended with err=None (pi resolved,
no error), nothing logged, auth.json still {}, the agent directory's mtime
bumped when the lock was taken and released while auth.json itself never
changed, and an API-key write — which goes through set(), not modify() — landed
immediately.
modify() now creates when absent and updates when present; a callback returning
undefined still writes nothing, so pi's refresh-bails-out behaviour is unchanged.
auth-storage-instances.test.ts asserted the old behaviour, grouping modify() with
remove/logout/removeInstance as "non-creating". The removal guarantees are kept;
the modify() assertion is inverted, because it encoded the defect.
Also surfaces the server's own loginError through a new describeLoginFailure()
helper instead of the generic sentence, so an OAuth state mismatch reads as the
stale-tab instruction it is. Writing its test caught a bad regex of mine:
`code.*expired` matched "OpenAI Codex ... token_expired", a different failure.
Verified: the new first-login test fails against the old `creating: false` and
passes with the fix; 86 engine auth tests, 238 dashboard auth/dialog tests, and
pnpm test:gate all pass.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
2eae0b2507 |
feat: remove stuck-task tagging from the dashboard; fix liveness-ratchet scan path
Removes the dashboard's stuck-task tagging per operator request: the Stuck card/status badges, stuck row styling, the footer Stuck segment and stuckTaskCount stat, utils/taskStuck.ts, the isStuck agent-activity gate, and the taskStuckTimeoutMs prop plumbing (App -> Board/Lane/Column/ WorktreeGroup/MainContent -> TaskCard/ListView/ExecutorStatusBar). Stuck-task tests are deleted or reconciled. The taskStuckTimeoutMs setting and the engine's recovery sweeps (including the stuck-killed status) are unchanged — the setting is engine-side only now. Also repoints the FN-6756 liveness-gate ratchet's facade scans at executor/task-executor-session-facades.ts, where the wave20 extraction moved hasLiveSessionSurface/clearPhantomExecutorBinding (the two pre-existing red tests on main). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
9a9e591b72 |
FN-9115: clarify and expand agent skill selection
Clarify automatic skill availability while making forced-reading selections easier to manage. - Replace the skill picker with a searchable multi-select checkbox list and explicit loading, error, empty, and unavailable states. - Label forced, disabled, and undiscovered skills consistently across agent detail and list views, including a clear None state. - Update localized guidance, documentation, regression coverage, and the published package changeset. Files changed: .changeset/fn-9115-skills-ui.md | 7 + docs/dashboard-guide.md | 4 + .../agent-detail-settings-theme-styling.test.ts | 3 +- .../dashboard/app/components/AgentDetailView.css | 7 +- .../dashboard/app/components/AgentDetailView.tsx | 35 ++- packages/dashboard/app/components/AgentsView.css | 4 + packages/dashboard/app/components/AgentsView.tsx | 13 +- .../dashboard/app/components/NewAgentDialog.tsx | 5 +- .../dashboard/app/components/SkillMultiselect.css | 258 +++------------------ .../dashboard/app/components/SkillMultiselect.tsx | 178 +++++--------- .../AgentDetailView.mobile-scroll.test.tsx | 4 +- .../AgentDetailView.skills-procedure.test.tsx | 59 +++-- .../app/components/__tests__/AgentsView.test.tsx | 19 +- .../components/__tests__/SkillMultiselect.test.tsx | 81 ++++--- .../__tests__/useDiscoveredSkillsCache.test.ts | 16 ++ .../app/hooks/useDiscoveredSkillsCache.ts | 13 +- .../app/utils/__tests__/agentSkills.test.ts | 28 +++ packages/dashboard/app/utils/agentSkills.ts | 69 +++++- packages/i18n/locales/en/app.json | 21 +- packages/i18n/locales/es/app.json | 21 +- packages/i18n/locales/fr/app.json | 21 +- packages/i18n/locales/ko/app.json | 21 +- packages/i18n/locales/pt-BR/app.json | 21 +- packages/i18n/locales/zh-CN/app.json | 21 +- packages/i18n/locales/zh-TW/app.json | 21 +- 25 files changed, 493 insertions(+), 457 deletions(-) Fusion-Task-Id: FN-9115 Fusion-Task-Lineage: 18229b87-d0dc-41e7-9333-2d12df852486 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
7ed1c39a67 |
FN-9102: fix active runtime segment accounting
Prevent reopened tasks from double-counting closed execution segments while preserving pre-execution planning time. - Clear the live execution anchor whenever a WIP segment is banked. - Clamp legacy poisoned runtime values with separate execution and combined-work wall-clock ceilings. - Cover cards, detail statistics, core totals, and planner metrics across WIP round trips and historical rows. - Add a patch changeset for the published Fusion package. Files changed: .changeset/fn-9102-task-runtime-double-count.md | 7 ++ .../src/__tests__/reopen-semantics-by-role.test.ts | 95 ++++++++++++++-------- packages/core/src/tasks/task-timing.ts | 20 ++++- .../core/src/workflows/default-workflow-hooks.ts | 14 +++- .../app/components/__tests__/TaskCard.test.tsx | 27 ++++++ .../__tests__/TaskTokenStatsPanel.test.tsx | 27 ++++++ .../app/utils/__tests__/taskTiming.test.ts | 40 +++++++++ packages/dashboard/app/utils/taskTiming.ts | 28 +++++-- .../__tests__/task-planner-chat-metrics.test.ts | 28 +++++++ .../dashboard/src/task-planner-chat-metrics.ts | 22 ++++- 10 files changed, 260 insertions(+), 48 deletions(-) Fusion-Task-Id: FN-9102 Fusion-Task-Lineage: 3b42e81f-4e11-4232-80f6-052f51b83f1c Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
284feeaf11 |
FN-9092: prevent model menus from dismissing host dialogs
Keep portaled model-menu interaction from propagating to host dialog dismissal. - Mark model-menu portal surfaces and stop their pointer events from reaching dialog backdrops. - Apply the dismissal guard across agent, chat, floating-window, and model-selection dialogs. - Close enabled overlays on valid touch taps while suppressing duplicate compatibility mouse closes. - Add desktop and mobile regression coverage for portal-host dismissal behavior. Files changed: .../fn-9092-model-filter-dialog-dismissal.md | 7 + .../ui-bugs/portaled-model-menu-host-dismissal.md | 19 +++ packages/dashboard/app/components/AgentsView.tsx | 6 + .../app/components/ChatThinkingLevelControl.tsx | 17 ++- packages/dashboard/app/components/ChatView.tsx | 38 +++-- .../app/components/CustomModelDropdown.tsx | 13 +- .../dashboard/app/components/FloatingWindow.tsx | 17 +-- .../app/components/ModelSelectionModal.tsx | 17 +-- .../dashboard/app/components/NewAgentDialog.tsx | 12 +- .../dashboard/app/components/QuickEntryBox.tsx | 5 +- .../ChatThinkingLevelControl.portal.test.tsx | 9 ++ .../__tests__/CustomModelDropdown.test.tsx | 24 ++- .../__tests__/ModelSelectionModal.test.tsx | 3 +- .../NewAgentDialog.portal-dismissal.test.tsx | 77 ++++++++++ .../components/__tests__/NewAgentDialog.test.tsx | 11 ++ .../model-menu-filter-host-dismissal.test.tsx | 163 +++++++++++++++++++++ .../app/hooks/__tests__/useOverlayDismiss.test.tsx | 7 +- packages/dashboard/app/hooks/useOverlayDismiss.ts | 26 +++- .../app/utils/__tests__/portalSurfaces.test.ts | 31 ++++ packages/dashboard/app/utils/portalSurfaces.ts | 31 ++++ 20 files changed, 469 insertions(+), 64 deletions(-) Fusion-Task-Id: FN-9092 Fusion-Task-Lineage: a0d0b84d-2efe-4a67-9485-77c08f7cc57e Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
0188d9427b |
FN-9044: group workspace tasks by acquired worktrees
Show workspace-mode task cards in their own worktree group instead of Unassigned. - Classify workspace worktree groups with stable identifiers and repository counts. - Render localized workspace headers and appropriate group icons. - Cover workspace grouping across utilities, columns, lanes, and group headers. - Document the board behavior and add a patch changeset. Files changed: .changeset/fn-9044-workspace-worktree-grouping.md | 7 ++ docs/dashboard-guide.md | 2 +- packages/dashboard/app/components/Column.tsx | 4 +- .../dashboard/app/components/WorktreeGroup.tsx | 20 ++++-- .../app/components/__tests__/Column.test.tsx | 32 ++++++++- .../app/components/__tests__/Lane.test.tsx | 24 ++++++- .../__tests__/worktree-grouping-workspace.test.tsx | 66 +++++++++++++++++ .../app/utils/__tests__/worktreeGrouping.test.ts | 82 ++++++++++++++++++++++ packages/dashboard/app/utils/worktreeGrouping.ts | 37 ++++++++-- packages/i18n/locales/en/app.json | 4 +- packages/i18n/src/resources.d.ts | 4 +- 11 files changed, 267 insertions(+), 15 deletions(-) Fusion-Task-Id: FN-9044 Fusion-Task-Lineage: d342d4fb-9fab-4efd-813f-2a3091d7c414 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
f6518b4275 |
FN-9029: keep release-gate verdicts transient
Keep release-gate verdicts restricted to fresh task-list responses. - Strip release-gate data from SSE and non-board task paths. - Reject stale initial verdicts before rendering promote controls. - Cover server persistence, SSE payload, and client freshness boundaries. Files changed: .../dashboard/app/hooks/__tests__/useTasks.test.ts | 115 +++++++++++++++++++++ packages/dashboard/app/hooks/useTasks.ts | 80 +++++++++----- .../app/utils/__tests__/releaseGate.test.ts | 9 ++ .../routes-tasks-release-gate-transient.test.ts | 103 ++++++++++++++++++ .../src/__tests__/sse-task-deleted-payload.test.ts | 10 +- packages/dashboard/src/sse.ts | 26 +++-- 6 files changed, 305 insertions(+), 38 deletions(-) Fusion-Task-Id: FN-9029 Fusion-Task-Lineage: cf764f3e-c6f5-4fa1-b608-ebb8d547cfdd Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
a6ce7f89ef |
FN-8987: expose release-gate verdicts for Promote visibility
Expose server-evaluated release-gate state so Promote controls accurately reflect approval readiness. - Attach transient release-gate verdicts to task API responses. - Preserve only fresh REST verdicts across task snapshots and render them in task cards. - Align hold-release gating and document the Promote-state contract. Files changed: .changeset/fn-8987-release-gate-verdict.md | 7 + docs/dashboard-guide.md | 4 + docs/solutions/workflow-learnings/project-union-versus-per-task-lanes.md | 1 + docs/workflow-steps.md | 4 + packages/core/src/index.gate.ts | 2 +- packages/core/src/index.ts | 2 +- packages/core/src/types.ts | 2 + packages/core/src/types/task/task-core.ts | 20 +++ packages/dashboard/app/components/TaskCard.tsx | 3 +- packages/dashboard/app/components/__tests__/TaskCard.test.tsx | 31 ++++ packages/dashboard/app/hooks/__tests__/useTasks.test.ts | 26 ++++ packages/dashboard/app/hooks/useTasks.ts | 135 +++++++++++++++-- packages/dashboard/app/utils/__tests__/releaseGate.contract.test.ts | 29 ++++ packages/dashboard/app/utils/__tests__/releaseGate.test.ts | 49 ++++++ packages/dashboard/app/utils/releaseGate.ts | 26 ++++ packages/dashboard/app/utils/reviewBudgetApproval.ts | 9 ++ packages/dashboard/src/routes/register-task-workflow-routes.ts | 20 ++- packages/engine/src/__tests__/release-gate-verdict.test.ts | 40 +++++ packages/engine/src/execution/hold-release.ts | 166 +++++++++------------ packages/engine/src/index.ts | 3 + 20 files changed, 469 insertions(+), 110 deletions(-) Fusion-Task-Id: FN-8987 Fusion-Task-Lineage: 6f1742bc-2b2b-4a32-9be5-92160335d90d Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
967e98194e |
FN-8950: hide Promote until plan gates clear
Prevent task-card promotion shortcuts when plan review or approval holds remain. - Mirror default-on plan-review gate and approval-hold predicates in dashboard helpers. - Suppress Promote across planning, review, and approval states. - Add regression coverage and a patch changeset. Files changed: .changeset/fn-8950-promote-plan-gate.md | 7 ++ packages/dashboard/app/components/TaskCard.tsx | 29 +++++-- .../__tests__/TaskCard.cost-badge.test.tsx | 2 + .../__tests__/TaskCard.footer-wrap.test.tsx | 2 + .../app/components/__tests__/TaskCard.test.tsx | 96 ++++++++++++++-------- .../utils/__tests__/reviewBudgetApproval.test.ts | 77 +++++++++++++++++ .../dashboard/app/utils/reviewBudgetApproval.ts | 43 ++++++++++ 7 files changed, 218 insertions(+), 38 deletions(-) Fusion-Task-Id: FN-8950 Fusion-Task-Lineage: c587b3b4-03b4-4cd2-9588-826706788eb7 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
a09e0cb87f |
feat(i18n): add Português (Brasil) (pt-BR) locale (#3347)
## Summary Adds **Português (Brasil)** (`pt-BR`) as a supported locale: - Selectable as **Translation target language** (project settings) and as the dashboard / terminal UI language. - Full machine-drafted catalogs (`app`, `cli`, `common`), disclosed in `packages/i18n/locales/TRANSLATION_STATUS.md` following the pattern #1352 established — reviewed for glossary/register consistency (0.18% untranslated, matching only keys that are empty in `en`), but native-speaker corrections are welcome. - Brazilian Portuguese content-language detection (accent-stripped stopword list — the scorer strips diacritics before matching, so accented entries never match; `com`/`mais` deliberately omitted to avoid bare-domain `.com` and French collisions, with regression tests for both directions). - `pt`/`pt-PT` browser and environment locales resolve to `pt-BR` on all three detection paths (`FALLBACK_LNG` routing plus a `pt` branch in `normalizeToSupportedLocale`, mirroring the existing `zh` handling). - `README.pt-BR.md` + switcher links in all READMEs, docs updates (`settings-reference`, `cli-reference`, `i18n-contributing`, `--lang` help text), changeset (`minor`). Drive-by fixes bundled: `TRANSLATION_STATUS.md` was missing the `ko` row; the LanguageSelector endonym test was missing `한국어`; `docs/i18n-contributing.md` now names the two compile-enforced display maps (`LOCALE_LABELS`, `localeDisplayName`) a new locale must update; the `--lang` CLI help text no longer drifts from its validator. ## Test plan - `pnpm i18n:status` (key parity gate) green; catalogs are `i18n:sync`-idempotent. - Updated/extended suites: core `locale-settings`, i18n `config`/`parity`/`db-banner-catalog`/`i18n-gate-coverage`, dashboard `useLanguage`/`LanguageSelector`/`GeneralSection.importTranslate`/`detectContentLanguage` (incl. new pt-BR detection + bare-domain regression tests), CLI `settings`. - `pnpm verify:fast` (typecheck, build, boot smoke), `pnpm lint`, `pnpm check:changesets`, and the bounded `pnpm test` lane all green locally (the three `test:pg-gate` files fail locally only for lack of a Postgres instance; they fail identically on clean `main`). <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added Brazilian Portuguese (Português (Brasil)) across the dashboard, terminal interface, settings, and translation tools. * Added Portuguese translations for common interface and CLI content. * Added automatic Portuguese language detection, locale normalization, and fallback support. * Added a Portuguese (Brazil) README with product, setup, and usage documentation. * **Documentation** * Updated language selectors, CLI references, settings documentation, and translation guidance. * Added Portuguese README links to translated documentation. * Added French to the documented dashboard language options. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: gsxdsm <gsxdsm@users.noreply.github.com> |
||
|
|
ec52a9131a |
FN-8859: add native roadmap embeds to chat
Enable roadmap structures to open, preview, and embed directly from chat and dashboard views. - Register roadmap views as native structures with chat-reference support. - Add composer previews, in-place dashboard embedding, and drag-safe interactions. - Document the native-structure plugin contract and add coverage across supported surfaces. Files changed: .changeset/fn-8859-roadmap-native-structure-embeds.md | 7 + docs/PLUGIN_AUTHORING.md | 1 + docs/dashboard-guide.md | 6 +- packages/dashboard/app/components/MessageComposer.tsx | 16 +- packages/dashboard/app/components/__tests__/MessageComposer.test.tsx | 102 ++++++++++- packages/dashboard/app/components/__tests__/overflowViewRegistry.test.tsx | 68 +++++++- packages/dashboard/app/components/dashboard/MainContent.tsx | 22 ++- packages/dashboard/app/components/dashboard/__tests__/MainContent.native-structure-roadmap.test.tsx | 188 +++++++++++++++++++++ packages/dashboard/app/components/nativeStructureChatRef.ts | 4 +- packages/dashboard/app/components/overflowViewRegistry.tsx | 34 ++-- packages/dashboard/app/plugins/types.ts | 10 +- packages/dashboard/app/utils/__tests__/nativeStructureDrag.test.ts | 46 +++-- packages/dashboard/app/utils/nativeStructureDrag.ts | 18 +- plugins/fusion-plugin-roadmap/src/dashboard-view.tsx | 7 +- plugins/fusion-plugin-roadmap/src/dashboard/RoadmapsView.tsx | 22 ++- plugins/fusion-plugin-roadmap/src/dashboard/__tests__/RoadmapsView.test.tsx | 51 ++++++ 16 files changed, 560 insertions(+), 42 deletions(-) Fusion-Task-Id: FN-8859 Fusion-Task-Lineage: 99a7292b-2a06-4325-8131-ca74431214e3 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
001bd2b97a |
FN-8814: prevent stale planning badges during code review
Keep planning and review lifecycle badges unambiguous across board and list views. - Classify active non-planning optional review gates for badge precedence. - Suppress stale Planning badges and empty list placeholders during Code Review. - Cover card, desktop list, mobile list, and workflow helper badge states. Files changed: packages/dashboard/app/components/ListView.tsx | 32 ++++++-- packages/dashboard/app/components/TaskCard.tsx | 24 +++++- .../app/components/__tests__/ListView.test.tsx | 56 +++++++++++++ .../app/components/__tests__/TaskCard.test.tsx | 66 +++++++++++++++ .../app/utils/__tests__/taskProgress.test.ts | 96 +++++++++++++++++++++- packages/dashboard/app/utils/taskProgress.ts | 34 ++++++-- 6 files changed, 292 insertions(+), 16 deletions(-) Fusion-Task-Id: FN-8814 Fusion-Task-Lineage: d3a9f087-02ea-4657-aa9e-30a537458cb9 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
fa3df0f6bd |
FN-8798: synchronize planning status across task views
Keep board, list, and task detail planning indicators consistent with fresh lifecycle state. - preserve authoritative task snapshots across SSE and detail refreshes - render transient replan planning only for fresh, unpaused planner activity - pass global pause state to every task-detail host and cover status convergence Files changed: packages/dashboard/app/App.tsx | 2 + packages/dashboard/app/components/AppModals.tsx | 3 + packages/dashboard/app/components/ListView.tsx | 11 +- packages/dashboard/app/components/TaskCard.tsx | 7 +- .../dashboard/app/components/TaskDetailModal.tsx | 27 ++- .../app/components/__tests__/AppModals.test.tsx | 201 +++++++++++++++++++-- .../app/components/__tests__/ListView.test.tsx | 31 ++++ .../app/components/__tests__/TaskCard.test.tsx | 17 ++ .../app/components/dashboard/MainContent.tsx | 1 + .../__tests__/useTasks-hydration-freshness.test.ts | 58 +++++- .../dashboard/app/hooks/__tests__/useTasks.test.ts | 29 ++- packages/dashboard/app/hooks/useTasks.ts | 51 +++++- .../utils/__tests__/taskStatusBadgeLabel.test.ts | 22 ++- .../dashboard/app/utils/taskStatusBadgeLabel.ts | 36 ++++ 14 files changed, 453 insertions(+), 43 deletions(-) Fusion-Task-Id: FN-8798 Fusion-Task-Lineage: 7defae4e-fb39-4d2c-943a-ef6c1a164bfc Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
bf173dad7c |
FN-8696: keep reverted tasks out of Done
Keep reverted tasks out of completed views and give every task-detail host a revision recovery path. - Exclude reverted tasks from complete columns and group them in dedicated recovery lists. - Provide Delete and Revise actions in cards, lists, docks, modals, and popped-out details. - Add localized copy, documentation, regression coverage, and a patch changeset. Files changed: .changeset/fn-8696-reverted-task-resolution.md | 7 ++++ docs/dashboard-guide.md | 4 ++ packages/dashboard/app/App.tsx | 2 + packages/dashboard/app/components/AppModals.tsx | 2 + packages/dashboard/app/components/Board.tsx | 42 +++++++++++++++++++- packages/dashboard/app/components/DockTaskList.tsx | 21 +++++++++- packages/dashboard/app/components/ListView.tsx | 18 +++++++++ packages/dashboard/app/components/TaskCard.css | 4 ++ packages/dashboard/app/components/TaskCard.tsx | 9 +++++ .../dashboard/app/components/TaskDetailModal.tsx | 18 ++++++++- .../app/components/__tests__/Board.test.tsx | 46 ++++++++++++++++++++++ .../app/components/__tests__/DockTaskList.test.tsx | 29 +++++++++++++- .../app/components/dashboard/MainContent.tsx | 4 ++ .../app/components/overflowViewRegistry.tsx | 3 ++ .../app/components/useRightDockController.tsx | 9 +++++ .../app/utils/__tests__/taskRevert.test.ts | 16 +++++++- packages/dashboard/app/utils/taskRevert.ts | 18 +++++++++ packages/i18n/locales/en/app.json | 3 ++ 18 files changed, 249 insertions(+), 6 deletions(-) Fusion-Task-Id: FN-8696 Fusion-Task-Lineage: 7c9550fb-ef8e-4422-8ec1-0de1665413a3 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
7dbcff139c |
fix: board lane counts and card glow never exceed live running agents
Operators summing the lane-header executing counts saw 10 active cards under a 9-slot concurrency cap and read it as a capacity breach. The header unioned the shared Running predicate with the card activity-chrome predicate, which glowed needs-replan parks (FN-8494) and fresh planner-log windows (FN-8300) that hold no concurrency slot. isTaskAgentActive's positive arm now delegates to the shared isRunningAgentTask predicate (footer/admission truth) and Column headers count only that predicate, so glow and counts are a strict subset of the live-agent population. Idle replans render the existing "Queued to revise" waiting label instead of activity chrome. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
df56790c9b |
FN-8676: fix Quick Add upward menu anchoring
Keep Quick Add portal menus attached to their triggers when they open upward. - Position upward menus with bottom anchors rather than height-capped top offsets. - Apply shared positioning to Quick Add pickers and the custom model dropdown. - Add desktop and mobile regression coverage and a patch changeset. Files changed: .changeset/quick-add-upward-menu-anchor.md | 7 ++++ .../app/components/CustomModelDropdown.tsx | 18 ++++++-- .../dashboard/app/components/QuickEntryBox.tsx | 48 +++++++++++++++------- .../__tests__/CustomModelDropdown.test.tsx | 40 +++++++++++++++--- .../components/__tests__/QuickEntryBox.test.tsx | 46 ++++++++++++++++++--- .../app/utils/__tests__/fixedMenuPosition.test.ts | 12 +++--- packages/dashboard/app/utils/fixedMenuPosition.ts | 22 ++++++---- 7 files changed, 151 insertions(+), 42 deletions(-) Fusion-Task-Id: FN-8676 Fusion-Task-Lineage: e48b4b2c-9675-42c6-8712-83cb4a0ca3d8 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
475bb2d641 |
FN-8637: restrict Quick Add Start to manual-intake workflows
Restrict Quick Add Start eligibility to verified manual intake lanes. - Require the server-derived manualIntake flag instead of hold alone. - Preserve Coding Ideas routing while hiding Start for Coding's merged planning lane. - Cover desktop and mobile eligibility behavior and document the updated rule. - Add a patch changeset for the corrected workflow gating. Files changed: .changeset/fn-8637-quick-add-start-manual-intake.md | 7 ++++ docs/dashboard-guide.md | 2 +- packages/dashboard/app/components/QuickEntryBox.tsx | 21 ++++++------ packages/dashboard/app/components/__tests__/Column.test.tsx | 20 ++++++++--- packages/dashboard/app/components/__tests__/ListView.test.tsx | 40 ++++++++++++++++++---- packages/dashboard/app/components/__tests__/QuickEntryBox.test.tsx | 21 +++++++++--- packages/dashboard/app/utils/__tests__/quickAddStart.test.ts | 38 ++++++++++++++++++-- packages/dashboard/app/utils/quickAddStart.ts | 9 ++++- 8 files changed, 128 insertions(+), 30 deletions(-) Fusion-Task-Id: FN-8637 Fusion-Task-Lineage: 9652d7d8-f954-49e8-9a76-a2421654baae Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
a8dae03fdb |
fleet(dashboard): taskRevert 2 → 0 — the recorded blocker named the wrong variable (#3129)
The largest remaining census cluster. Deferred twice, with a blocker that turns out to be false **in the same component where its counter-example already lives**. ## What the earlier notes got right `detailColumnFlags` describes the **modal's own task**, and the column classified here belongs to a **neighbour**. Supplying it would answer *"is this neighbour finished?"* with a different row's traits — wrong on data, not merely stale on vocabulary. That reasoning stands and I kept it. An earlier pass also converted this, left the parameter unsupplied, and **reverted it** — correctly. An unsupplied optional parameter is strictly worse than the literal: the guard is gone, the census counts a conversion, and the behaviour is the legacy fallback forever. That rule is why the wiring ships in this same commit. ## What the conclusion got wrong > "A correct conversion needs per-**neighbour** flags — which the modal does not have and should not fetch mid-render." `columnFlagsByTaskId` is a per-task map. It is **already a prop** of `TaskDetailModal` (declared :367, destructured :727), and the call site at :992 sits **below** that destructure. And `TaskDetailModal` already uses it exactly this way, for the near-duplicate canonical: ```ts columnFlagsByTaskId?.get(nearDuplicateCanonical.id) ``` …under a note observing that *its* blocker had been *"asserted from the shape of the problem rather than tested against what was in scope."* Same assertion, one function over. So the supplier the earlier note went looking for exists, is per-neighbour, and needs no fetch. ## What it fixes This lookup skips **finished** candidates so a done/archived prior undo attempt never renders as an active "Undo task" link. On a board that renames those lanes it matched neither — a finished undo task kept rendering as open, which is precisely the stale affordance the function's own header says it exists to prevent. ## Census | | before | after | |---|---|---| | `taskRevert.ts` | 2 | **0** | | repo backlog | 17 | **15** | ## Measured - 4 new cases; `taskRevert.test.ts` **11/11 pass**. - **MUTATION**: restoring the literal pair fails the renamed case. - **The negative is load-bearing.** The map is fail-soft, so a candidate it does not cover must still be treated as **open**, not skipped. A conversion that skipped unknown candidates would *hide live undo links* — failing in the direction nobody reports. - A **control** pins that an unwired caller (no flags at all) still skips the legacy ids, so the optional parameter cannot regress default boards. - `TaskDetailModal` suites — **31 files / 664 tests pass**. - `tsc --noEmit -p tsconfig.app.json` clean; census `--strict`, `check-lane-wiring`, `check-fnxc-future-dates` clean. ## Pattern worth noting This is the fourth deferral this session whose stated blocker had dissolved or misidentified itself, and the second where the counter-example was already in the same file. The common shape: a note records *why* something is blocked, is accurate when written, and is never re-checked — so the block outlives its cause. Re-reading them cost minutes each and returned two real conversions. Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
109204c590 |
fix: the query class — three sweeps that never ran on a renamed board (#2818)
Three sweeps that **never ran at all** on a renamed board, plus the shared answer the rest of the class needs. Consolidated from three handoff branches so the helper appears once. #2811 merged, so this is my only open PR. `#2800` measured this class and shipped evidence deliberately without conversions: `listTasks({ column: "<literal>" })` filters in the store, so on a renamed board the read returns an **empty array** and the sweep it feeds does nothing. The census scores the comparison *inside* the loop, never the query above it. ## What was broken | file | census count | what actually happened on a renamed board | |---|---|---| | `backlog-pressure-reporter.ts` | **0** | both reads empty, ratio computed as 0/0 — **the alert never fired**, on a board that may be under exactly the pressure it reports | | `stale-task-reporter.ts` | **0** | both reads empty — **no stale-task signal ever raised**, where work is most likely sitting unnoticed | | `restart-recovery-coordinator.ts` | flagged | sweep never ran — **an engine restart left interrupted tasks stuck with no requeue** | Two of the three have a census count of **zero**. They contain no lifecycle comparison at all, so they have never appeared in the backlog, in a per-file list, or in any "N → 0" claim — and were completely inert. **A file at zero is not evidence of anything.** ## The shared answer, and what it is not Every existing resolver answers a **per-task** question. A query has no task in hand, so it needs the project-level one: every column any workflow declares for a role, unioned with the legacy ids so a board mid-rename still finds rows under the old ones. The set is never empty, so a caller cannot accidentally query nothing. The header states what it is **not**: answering a per-card question from the union would mark a card as review because some *other* workflow calls its column review — the flat-set mistake this program has made four times. ## The finding that generalises: the query is rarely the whole defect `stale-task-reporter` **still reported zero after the query was fixed** — `getTaskAgeStalenessSignal` defaults to the legacy pair, so a card the query now returned was refused inside the signal. Converting only the query would have looked like a fix and changed nothing. That is a caveat on #2800's approach, offered as refinement rather than correction: **asserting the query ARGUMENT is right when pinning a known defect** (the outcome is 0 either way) **and insufficient when proving a fix**, because the outcome is the only thing that distinguishes a real conversion from a deeper one. All three conversions here assert outcomes. `restart-recovery` had three layers — query, a redundant re-assertion (deleted; a test pins the `paused` guard it did contribute), and a move destination that was **already** resolved but whose warning comment was stale. A stale warning is its own hazard: it told the next reader a defect existed where none did. ## Verification - helper **8 passed** · three reporter/coordinator suites **29 passed** - `pnpm test:gate` **161 / 13 / 487 / 71** · lint clean · `--strict` exits 0 · four `tsc` targets clean - each conversion revert-proven independently; the failing case is named in each test header ## Two mistakes worth recording **The helper's own test caught a bug in it.** My first draft wrapped the definition loop in one `try`, and `parseWorkflowIr` **validates** rather than parses — one malformed row would have returned legacy-only lanes for *every* workflow, indistinguishable from the bug it exists to fix. Now isolated per definition. **I clobbered the core barrel** by taking `index.ts` wholesale from a handoff branch, dropping two exports `main` had added since; three packages stopped compiling. Taking a file from another branch takes its whole contents, including what is now stale — for a barrel that is nearly always wrong. Re-applied as a single edit on top of `main`. ## Not included `self-healing.ts`'s 49 — actively owned and mid-conversion; an outside refactor there produces conflicting halves of one sweep. `project-engine.ts` (7) and `executor.ts` (2) need their own read of what each sweep does with the rows, which these three are the argument for. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
e84e9d7f60 |
fix: the caller audit — five unwired parameters, five defects in their callers (#2803)
Seven fixes that were sitting on separate handoff branches with no owner while `main` moved. Consolidated, rebased onto current `main`, and verified **together** rather than only per-branch. The individual branches remain if a subset is preferred. This is the same consolidation that got `batch-core` and #2787 adopted. **Close it if it breaks queue policy** — the branch keeps the work safe either way. ## Where these came from #2787's review found an optional parameter whose production caller never passed it. That is a class, so I ran it against everything I had landed and found five more. **All five turned out to have their real defect in the CALLER, not the parameter** — in four of them the parameter was unreachable: | unwired parameter | what was actually wrong | |---|---| | `blocker-fanout.escalationColumns` | the hold default made the count zero — **no bottleneck warning was emitted at all** | | analytics `columnFlagsByName` | routes never built a map — **0 in-progress / 0 in-review beside correct cost totals** | | `isLegacyAutoMergeStampCandidate` | the read **queried a column a renamed board does not have**, so the backfill iterated nothing | | `rankAssignedTasksForWakeDelta` | `getTasksByAssignedAgent`'s `excludeArchived` used the literal — **archived cards returned as open work** | | `duplicate-intake.columnFlagsByColumnId` | intake could **archive or soft-delete a newly created task** as a duplicate of finished work | The heuristic worth keeping: **an optional parameter no production caller fills is a marker pointing at an unexamined caller.** The census cannot see any of these five — every gate is a `Set`/array literal or a query filter, i.e. a definition rather than a comparison. ## Also included - **`executor.ts`** — the stale-spec guard did the exact thing its own comment forbids: on a renamed board it ran on a LIVE task and pulled it out of execution into replan. `activeMergeStatuses` protected merging cards *by accident*, which is why the symptom looked arbitrary. - **`register-project-routes.ts`** — project health reported **0 active tasks**; its list also still contained `triage`, dead since U11. - **`dashboard/app/utils/taskTiming.ts`** — a **second copy** of `getTotalAgentActiveMs`. Core's was converted; the card chip imports this one, so the census counted the site as done while the rendered number stayed keyed on `"in-progress"`. ## Verification Verified as a set: `pnpm test:gate` **161 / 13 / 487 / 71** · core suites **15 passed** · engine **7** · dashboard **12** · four `tsc` targets clean · lint clean · census `--strict` exits 0. Each fix is revert-proven individually; the specific case that fails is named in each test header. ## Two honesty notes **Three guards here are structural, not behavioural, and say so in their headers.** `sanitizeAgentTaskLinks` is a closure inside `createApiRoutes`; the analytics aggregators need a live `AsyncDataLayer`; the stale-spec guard sits deep inside `execute()`. Each ratchet fails on revert — verified — but none is an end-to-end proof, and the headers state which half they cover. **One of my behavioural test sets would have lied.** The intake-dedup cases drive `findSameAgentDuplicates` directly; I removed the wiring to measure the revert and **they stayed green**, because they pin the predicate and not the caller. That is the exact illusion this audit was chasing, reproduced in my own file. The forward now has its own structural check. ## Deliberately not included `worktree-pool.ts:1205` — it **fails safe** (a missed match protects a branch from cleanup rather than deleting it) and sits in the merger's branch-reaping path where the opposite error destroys work. That deserves its owner's judgement, not a drive-by conversion. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
642a4fa264 |
consolidate/u12 — U12 consolidation: 4 live defects, the AST ratchet fail-closed, and the moves.ts flag scoped (#2647)
One branch, one PR, per the consolidation directive. Contents file-by-file below. **Supersedes #2625** (its overlapping conversions landed via U11's #2624/#2626/#2636; only the parts nobody else did are folded here). **#2630 and #2639 stay open** — both green with zero threads, per rule 3. ## Four live defects, each measured **1. Every planning card renders an actions menu.** `TaskContextMenu.tsx` still had `shouldShowActionsMenu: task.column !== "triage"` on main *after* the rest of that file was converted. Since #2515 removed the id, the condition is TRUE for every card, so the suppression stopped applying anywhere — including on cards whose menu is empty, the orphaned click target the Surface Enumeration rule exists to catch. Found **twice independently**: by reading the guard, and again by the invariance test below, which failed on main with `shouldShowActionsMenu` true on one lineage and false on another. That is the argument for an invariance property over per-site conversion — the file had already been converted "2 → 1" and the survivor was the live one. **2. Worktree upcoming-work list empty on renamed boards.** `groupByWorktree` filtered `t.column === "todo"`. On the default board the id and the role coincide so every existing test passed; renamed, it matched nothing and a whole panel read as idle. **3. Hold-lane FIFO ordering lost on renamed boards.** `sortTasksForDisplayColumn` gated priority-then-FIFO on `column === "todo"`, degrading to the generic id-ordered sort elsewhere. Cards simply appear in the wrong order, silently. **4. The AST ratchet still failed open** — fourth time in that file, third found by review. `receiverName` understood only one-level property access and bare identifiers, so `task["column"]`, `metadataColumn(entry, "to")`, ternaries, `(task!.column)` and backtick literals were dropped. **Measured on main: `in-progress` 196 → 197, `in-review` 211 → 213** — three real guards nobody counted, including `metadataColumn(entry, "to") === "in-review"` in `reliability-metrics.ts`. Now walks wrappers, resolves calls to the callee name, and emits a `<SyntaxKind>` **sentinel** for anything unnameable: counted *and* trips the classification guard, so a human judges it instead of it vanishing. ## Per-file guard counts | file | before | after | |---|---:|---:| | `app/components/TaskContextMenu.tsx` | 1 | **0** | | `app/utils/worktreeGrouping.ts` | 1 | **0** | | `app/components/taskSorting.ts` | 1 | **0** | The other dashboard files I had converted reached 0 via U11's PRs; where our work overlapped I took theirs during the rebase, including two places where theirs was **stronger** than mine — they deleted Column's unreachable quick-create arm outright (with fixtures migrated) where I had converted it, and they verified the same `isPreExecutionHoldColumn` degraded-set asymmetry I did, independently. ## Flip precondition: the moves.ts flag is scoped, not flipped `move-target-declared-census.test.ts` answers precondition 2 with measurement. 41 engine `moveTask` calls have literal targets — `todo` 27, `in-progress` 7, `done` 6, `archived` 1 — and **all four are declared by the default lineage**, so the default board is not the exposure. `triage` appears only in a comment noting `replan-target.ts` used to hardcode it. My own grep had said `todo=29`; the AST says 27, because grep counts comments. The exposure is **custom** lineages: 20 of the 41 carry no `recoveryRehome` and would reject with unknown-column post-flip; 21 are exempt via the #1411 carve-out, which makes that carve-out load-bearing. I did not flip the flag. It is six seams, not the `789`/`837` pair every summary including mine described, and seam 2 turns on *new refusals* rather than swapping equivalent implementations — a green suite says nothing about that. #2639 pins the blast radius. ## Tests - `column-role-id-invariance.test.tsx` — hold traits fixed, vary only the column id across MERGED / LEGACY / RENAMED; every decision must agree. Drives the real consumers, so a component keeping an inline comparison fails it. Includes a unanimous-and-**false** case so it can't be satisfied by a predicate hardwired to true. **This is the test that caught defect 1 on main.** - `worktreeGrouping.test.ts` — includes two cards both in a column named `staging`, one hold and one not, asserting opposite answers. That assertion is impossible under a board-wide column-id set, which is why hold resolution is keyed per task via `getEffectiveTaskWorkflowId` (#2625 review). - `taskSorting.test.ts` — discriminates on the **tiebreak**, not priority: both branches sort by priority, so my first version passed for the wrong reason. Equal-priority cards whose `createdAt` order disagrees with their id order. - `no-hardcoded-lifecycle-columns.test.ts` — 16 detector cases: 11 shapes counted, 4 legitimate ignored, one asserting the sentinel path. Revert checks, all run: menu suppression → diff names the field; worktree → `expected [] to include 'FN-50'`; sort → `FN-2, FN-9` instead of `FN-9, FN-2`; ratchet → the 3 recovered guards disappear. ## One site that should never be converted `MissionControlPanel.tsx:46` — `{ id: "triage", match: (c) => c === "triage" || c === "signal" || c === "backlog" }` is a deliberate name-similarity heuristic for the SDLC funnel; it matches synonyms and folds unknown columns into an "other" bucket so custom columns still contribute. Converting it changes what the funnel displays. Like the `live-agent-count` fallbacks, it belongs in a documented floor — **the ratchet's target is that floor, not zero.** `DocumentsView.tsx:73` is convertible but the file has no column flags at all, so a real fix means plumbing board-workflow metadata into a view that doesn't fetch it — its own unit of work. ## Verification `pnpm lint` clean. `pnpm test:gate` green (10 / 482 / 71). `tsc -p packages/dashboard/tsconfig.app.json` and `packages/core/tsconfig.json` clean. Core ratchet + seam suites 24/24. Dashboard target suites 37/38 — the one failure is the pre-existing `"Back to In Progress"` label casing, confirmed identical on the base. --- ## Added after the initial push **5. `TaskCard` lost inline editing on renamed boards; `TaskDetailModal` kept it.** Still live on main: the modal resolved field editability from traits in U10/R8, the card used a hardcoded `{triage, todo}` set with **no trait path at all** — even though `taskColumnFlags` was already in scope. On a renamed board the title was editable in the modal and the pencil was missing from the card. Body moved unchanged into `isFieldEditableColumnRole` so the two surfaces cannot drift again. The veto traits are the substance: a column can legally carry `hold` **and** a WIP or review trait, and a plain `intake || hold` check would let an operator rewrite a description while a session executes against it. Coverage gap **measured, not assumed**: mutating `canEdit` back to the hardcoded set left `TaskCard*` at the same failure count as the unmutated run — nothing caught it. The four render cases assert the real `aria-label`; that mutation now fails with `Unable to find an accessible element ... name 'Edit task'`. **6. The ratchet's target is a documented FLOOR, not zero** — and this changes the completion bar. Zero is not reachable, and chasing it means breaking working code. Two categories are permanent, now protected as positive assertions so a future sweep cannot "finish the job" by deleting them: - `MissionControlPanel.tsx`'s `FUNNEL_STAGES` is a deliberate **name-similarity** heuristic — it matches `signal`, `backlog`, `to-do`, `ready`, `shipped` and folds unrecognised columns into an "other" bucket so a custom board still contributes counts. It is not asking whether a column has the intake trait; it buckets arbitrary column *names* for display. Asserted on the **synonym list**, because the synonyms are what prove it is name matching — if they disappear the site has changed character and the exemption stops applying. - `live-agent-count.ts`'s no-flags arm is reachable (a remote store is deliberately given an empty flag map; a card in an undeclared column has no flags at all) and deleting the literal makes such a card match **no** arm, so the queued total silently under-reports a stranded card. A count with an undocumented floor invites someone to drive it to zero. **Not done, and why:** `DocumentsView.tsx:73` is convertible but that file has no column flags anywhere, so a real fix means plumbing board-workflow metadata into a view that does not fetch it — its own unit of work, not something to smuggle into a conversion. **Re-verified after these commits:** `pnpm lint` clean, `pnpm test:gate` green (10 / 482 / 71), `tsc` clean on core and `tsconfig.app.json`, core ratchet suite 26/26, `columnRoles` 10/10, `TaskCard.test.tsx` 384/386 (the 2 are pre-existing CSS assertions). `TaskDetail*` is 130 failed / 551 passed **both with and without** this change — verified by stashing, so pre-existing and unrelated. --- ## Flag resolution: preconditions 1 and 2 are now DISCHARGED. Precondition 3 is blocked, and by evidence. **Precondition 1 — the side-effect equivalence proof — done.** `moves-flag-equivalence.test.ts` runs the same journey under both flag states against live PG and diffs the persisted row. **Result: identical** — whole-row equality across 128 fields plus an equal timing shape, over `todo → in-progress → in-review → todo → in-progress`. That test was **wrong twice** before it meant anything, and both times it was passing: 1. **It proved nothing.** `experimentalFeatures` is **global-only**, and `moves.ts` reads `getSettingsFast()`, which filters global-only keys out of the project layer. My `updateSettings` write was silently discarded, `useWorkflow` was false in *both* runs, and the "proof" compared the legacy path against itself. Found by stamping the flag-ON branch and observing the test still passed. Now written via `updateGlobalSettings`, and the helper **asserts the flag took effect** before the journey runs. 2. **The journey was forward-only**, so it never reached the reopen hook's field resets (`status`, `error`, `blockedBy`, pause clearing) — a mutation there passed. Extended with a backward move and a re-entry. Mutation-verified after both fixes: stamping seam 3, and diverging the reopen hook, each fail the comparison. **Precondition 2 — done, and its answer is a blocker.** The census says the default board is safe: all 41 literal engine move targets are declared by the default lineage. But **20 of those 41 carry no `recoveryRehome`**, so on a custom lineage that does not declare `todo` / `in-progress` / `done`, seam 2 would start rejecting them with unknown-column. That is a user-facing break on custom boards, not a theoretical one, and it is not fixed by the equivalence proof — seam 2 adds *new refusals* rather than swapping implementations. **So the flip is one step away, and the step is not mine to take alone:** those 20 call sites need to resolve their target from the task's workflow (or justify `recoveryRehome`), and they live across engine lanes in `moves.ts` caller territory — U2b/MAIN. Flipping before that trades a dormant flag for broken custom boards. What remains for precondition 3 once those land: flip both readers **atomically** (`moves.ts` + `workflow-task-create-ops.ts`, since the latter computes the preflight the former consumes), delete the flag-OFF branch with its guards, and drop the settings key. --- ## CORRECTION: seam 2 is not a blocker. My earlier claim was wrong. I stated in #2639 and above that "with the flag off there is **no** target-column validation on the move path", so flipping would introduce new refusals. **That is not what happens.** Reproduced against live PG: the identical custom-lineage move rejects with the flag **OFF** as well — ``` Error: Invalid transition: 'backlog' -> 'todo'. Valid targets: building ``` Transition validation is already in force on the flag-OFF path. So for the shape in question — an engine move to a column the task's own workflow does not declare — **the move already fails today**, and seam 2 introduces no new break for it. The 20 census sites lacking `recoveryRehome` are broken on a custom lineage *now*, not broken by the flip. I found this because the discriminator I added to prove "the flag is the cause" failed. Had I written the test to my assumption it would have passed and the false claim would have shipped — the same way the equivalence test passed while proving nothing until I tried to make it fail. **Revised precondition status:** | precondition | status | |---|---| | 1 — side-effect equivalence | **discharged** — identical rows, mutation-verified both directions | | 2 — seam-2 exposure census | **discharged, and it is not a blocker** — the rejection predates the flag | | 3 — flip both readers atomically, delete the flag-OFF branch, drop the settings key | **the remaining work** | So the flip is no longer gated on fixing 20 engine call sites. What it is still gated on is precondition 3 being done atomically across `moves.ts` and `workflow-task-create-ops.ts` (the latter computes the preflight the former consumes), which is `moves.ts` caller territory. Three cases now cover seam 2: the flag-ON rejection, the flag-OFF rejection (asserting the error *message*, so a change in which guard rejects stays visible rather than reading as agreement), and the #1411 `recoveryRehome` carve-out succeeding — pinning why that carve-out is load-bearing and must not be tidied away. --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
bad35775a1 |
Drift 3/4: Task Detail intake affordances from traits — the UI half of the #2571 approve/reject stall (4→3) (#2577)
## Drift conversion 3 of 4 — Task Detail, the UI half of the #2571 stall **Stacks on #2566.** Merge order: #2558 → #2566 → this. (#2571 is the P0 and is independent — merge it first regardless.) ### Convergence number Live-code `column === / !== "todo" | "triage"` in `TaskDetailModal.tsx`: **4 → 3** All three survivors are the documented no-metadata fallback, same shape as TaskCard and ListView: `workflowMoveMetadata` is `null` until the detail payload resolves, and a bare trait read would drop these controls during that window. ### This is the UI half of the P0 `isAwaitingApproval` and the standalone Delete button were both gated on `task.column === "triage"`. On the merged lineage (#2515) that is false for every card, so a task parked `awaiting-approval` **loses its Approve/Reject controls in the one surface that shows them**. #2571 fixes the routes that *reject* those actions. This fixes the UI that stops *offering* them. Either half alone leaves the operator stuck — one with buttons that 400, the other with no buttons at all. ### Three conversions | site | was | now | |---|---|---| | `isAwaitingApproval` + standalone Delete | `column === "triage"` | resolved column's `intake` | | `requiresExecutionModeReplan` | `todo \|\| in-progress` | `hold \|\| countsTowardWip` | | move-progress prompt | source column ids | **target** column's flags | The replan rule is "this card may already hold a plan or a live execution context" — which the traits state directly; `todo`/`in-progress` was the Default workflow's spelling of it. The move prompt is the mistake I made first in TaskCard, where its regression test caught that the site tests the move **destination**, not the card. Carried the lesson here rather than repeating it. ### Tested through a pure seam, and why `requiresExecutionModeReplanForTest` is exported so the rule can be asserted as a function of (column id, flags). Asserting it through the modal means booting async detail loading to observe one boolean — and an earlier DOM-level attempt at exactly this class of assertion (in #2566, ListView) **passed with the conversion reverted**, because the text it matched also appears in a column header. I am not repeating that. A seam discriminates; that DOM test did not. Revert-proof: restore `column === "todo" || column === "in-progress"` and the merged-column case fails, because that column is `intake + hold` and carries no `countsTowardWip`. The suite also pins that the rule still **narrows** (a complete lane needs no replan) and that the legacy fallback is unchanged when flags are absent. ### Verification `pnpm test:gate` (414 + 10 + 71), `pnpm lint`, dashboard typecheck green. **No new failures**: `TaskDetailModal.rendering.test.tsx` reports the same 28 pre-existing failures with and without this change, diffed by test *name* against a stashed clean tree. ### Drift set status | file | before | after | PR | |---|---|---|---| | `TaskCard.tsx` | 8 | 3 | #2558 | | `ListView.tsx` | 5 | 3 | #2566 | | `taskActivity.ts` (found underneath) | 1 | 1 | #2566 | | `TaskDetailModal.tsx` | 4 | 3 | this | | `register-task-workflow-routes.ts` | 10 | 11 | #2571 (P0, widened on purpose) | Survivors are no-metadata fallbacks except the routes, where the guards deliberately accept resolved-intake **or** `triage` so a P0 fix cannot reject anything previously allowed. Those retire together once the legacy id is gone board-wide. --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
99c9f14ee0 |
feat: run Plan Review in the planning lane with a Plan Review badge (#2462)
## What Plan Review, planning, and the replan loop move from the implementation column into the **planning lane** (`todo`), so a task under specification never holds a WIP slot. The card crosses into `in-progress` exactly once, at `parse`, released by the scheduler. Operators also finally see a **Plan Review** badge while the gate runs — it was previously invisible on the default workflow. ## The part that made it possible Moving the node is ten lines. It was attempted three times and reverted each time, because a graph run with no durable continuation replayed from `start` and dragged an in-progress card *backward* out of the WIP column, firing `abort-on-exit` and stranding it in a pre-WIP column with no releaser. So this PR adds the graph **entry contract** — `resolveColumnResumeNode`: | Card is in | Resumes at | |---|---| | `triage` | `start` | | `todo` | `plan` | | `in-progress` | `parse` — never re-plans, never moves backward | | `in-review` | first review node — gates are not skipped | `ir.columns` is ordered and that order is the lifecycle order; rework and failure edges are excluded so the entry point is always the main path. The proof it's the right fix: **`executor-task-done-invariant` passes unmodified** after failing every previous attempt. ## Also in here - **Release gate narrowed twice.** `isUnplannedForExecution` applies its pre-release plan-review gate only when the node's column equals the card's column *and* the group is enabled for the task. The enablement check fixes a real deadlock — a task with Plan Review toggled off was held forever waiting for evidence nothing would ever write. - **Badge cleanup.** Gate badge reads "Plan Review" instead of the ambiguous "Reviewing" and no longer hides behind a lane restriction; the status badge stops duplicating it; `planning` renders as "Planning" instead of the raw engine token. - **Coding (Ideas)** renames its planner column to "Planning" (id `todo` unchanged) and loses its private planning-node re-home — the graph it clones is already plan-in-place. - **New sweep** `reconcileUndeclaredTaskColumns` re-homes a row whose column its workflow no longer declares. Written for a follow-up, kept because it makes any column edit survivable. ## Test changes Scheduler and release fixtures now model a card whose Plan Review passed — the state every real card is in when the capacity sweep sees it. A held unreviewed card is the gate working, and that path stays owned by `pre-release-plan-review.test.ts`. New `workflow-graph-entry-contract.test.ts` covers the invariant at every lifecycle position, plus the gap-column and remediation-node cases. ## Verification Gate 299 + 70 + 10, dashboard badge suites 672, engine workflow/entry/executor suites 147, core 122. Lint and typecheck clean. Full engine suite sits at the pre-existing baseline (notifier / plugin-runner / notification-service, untouched by this). ## Follow-up Removing the Todo column entirely is a separate ~207-site lifecycle-vocabulary refactor — planned in `docs/plans/2026-07-26-001-refactor-workflow-owned-lifecycle-plan.md` (companion docs PR). 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Plan Review now runs in the Planning lane before implementation begins. * Cards resume from their current workflow column without replaying earlier steps. * Added automatic recovery for cards stranded in outdated workflow columns. * **Improvements** * Renamed the Coding (Ideas) planner column to “Planning.” * Refined Plan Review gating to respect enabled settings and the card’s current column. * Updated planning and Plan Review badges for clearer, consistent labels across cards and lists. * **Bug Fixes** * Improved workflow transitions and release behavior around planning, review, and execution. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
0d40bc41d1 |
fix(dashboard): stop badging the merge-blocker in-review stall
A pre-merge check reporting a blocker is the ordinary in-review resting state rather than an exceptional one, so badging it marked routine cards abnormal. Operator-requested removal. Suppression is expressed as a code list next to the existing no-worktree-no-merge-confirmed entry, so both surfaces that gate on shouldShowInReviewStallBadge -- the card header badge and the Task Detail diagnostic block -- drop it from one place. The previous carve-out only suppressed merge-blocker while isActiveMergeStatus(task.status) held; that is gone, and the test row that used to expect a badge for status undefined now asserts the suppression is unconditional. task.inReviewStall keeps being computed and stored -- only the affordance is withheld -- so the Review tab, run-audit, and self-healing are unaffected. No dead CSS: the shared .in-review-stall classes still serve the remaining codes and no --merge-blocker rule existed. The card test asserts no empty badge shell is left behind. Two TaskDetailModal cases used merge-blocker only as a fixture for the diagnostics row and jump-to-activity-entry behavior; repointed at transient-merge-status-no-owner so they still guard what they were written for. Note for follow-up: this badge was the board's only signal for a card blocked on a failed pre-merge step. self-healing's needsOperatorBypass comment already flags that such cards "sit silently" behind a generic badge; with the badge gone they show nothing at all on the board. Verified: tsc -p tsconfig.app.json clean, pnpm lint clean, 693 tests passing across the 6 affected suites. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
f26cbedf4f |
fix(dashboard): close the code-review findings on the mobile tab-discard work
An 11-reviewer pass over f157bf7460..f5163d8351 found defects in the mobile tab-discard change set itself. This fixes them. Silent data loss (the recurring defect class): - AgentDetailView reconnect refetched limit:100 and replaced wholesale, so 380 displayed lines vanished with no "Load older" and no indicator; it now reconciles through the shared logStreamReconcile helper. - useActivityLog.loadMore past the cap discarded the page it had just fetched while advancing the cursor and leaving hasMore true, so the feed silently stopped paginating behind a live-looking button. - useAgentLogs: loadMore and resyncFromServer had no mutual exclusion, a no-overlap resync discarded explicitly paged-back history, a resync outliving the reconnect delay left an unmarked gap, and the live-tail trim could evict the gap marker itself. - useLiveTranscript's resync overwrote live entries that raced the refetch. The premise itself was not fully delivered: - useProjects, useNodes, and useMeshState never called clearInterval, so they polled the whole time the tab was hidden. useProjects is mounted for the entire session, so the page never went idle -- the primary mechanism this work depends on. All three now use the shared visibility gate. - sse-bus fired onReconnect twice per reconnect cycle and fanned out ~28 subscribers in one tick, against a ~6-connection-per-origin cap on a waking radio. The successful open is now the single authority, and the fan-out uses the same exported stagger primitive as the polling path rather than a second copy of the slot formula. - A channel first subscribed during the hidden window opened a live EventSource and keepalive; suspension is now a module-level condition openChannel consults, and a channel opened inside the grace window re-arms it. Credentials and correctness: - The service worker persisted every GET /api/* to durable Cache Storage, including /api/settings with daemonToken, githubAuthToken, gitlabAuthToken and ntfyAccessToken in plaintext, with no exclusion and no purge path -- "Clear all cached data" only walked localStorage. Now gated, bounded, and genuinely purgeable. - useTasks cleared its own snapshot when the mount revalidation failed on a waking radio, so the board blanked and the next restore was empty too. Suspension-class failures no longer destroy the cache. - A single-row SSE update reset lastFetchTimeMs to now while an hours-old hydrated snapshot was on screen, re-marking every in-progress card stuck. - ListView's "Select all visible tasks" acted on the full filtered set while only 50 rows rendered, so a bulk delete reached rows the operator could not see. Column's search window reset keyed on a boolean, so refining a query kept the expanded window. Tests that could not fail: - App.test.tsx mocked TerminalModal as isOpen ? <div/> : null, making the unmount-on-close invariant unobservable; MockEventSource kept its listeners after close(), so cases passed with their onReconnect handlers deleted. - The SSE resync ratchet scanned only hooks/, exempting ~13 component call sites -- the exact regression it exists to prevent. - MissionControlPanel's bespoke poll and the xterm scrollback constants and WebGL disposal had no coverage at all. Verified: tsc -p tsconfig.app.json clean, pnpm lint clean, pnpm check:changesets clean, 877 tests passing across 36 scoped files. Known unrelated red: MailboxView.test.tsx's FN-8407 CSS guard fails at HEAD too -- this diff adds no @media rule and no .mailbox-view--mobile selector, the only two things that assertion inspects. Left alone deliberately. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
f157bf7460 |
fix(dashboard): harden visibility suspension, log caps, and mobile board UX
Suspend poll/SSE work when the tab is hidden, cap log buffers, restore board scroll more reliably, and improve list windowing/live tickers with related tests and a mobile-tab retention changeset. |
||
|
|
d4aa79b66c |
FN-8598: preserve legacy task cost badges
Restore cost badges for tasks with valid legacy token totals. - Preserve usage records when optional timestamps and cache-write totals are absent - Use task creation time to satisfy legacy usage timestamp requirements - Cover card badge rendering, unpriced mixed usage, and mobile visibility - Add a patch changeset for the restored badge behavior Files changed: .changeset/fn-8598-cost-badge-fix.md | 7 + .../task-token-usage-serialization.test.ts | 45 +++++++ packages/core/src/task-store/serialization.ts | 16 ++- .../__tests__/TaskCard.cost-badge.test.tsx | 146 +++++++++++++++++++++ .../app/utils/__tests__/taskTokenCost.test.ts | 11 ++ 5 files changed, 221 insertions(+), 4 deletions(-) Fusion-Task-Id: FN-8598 Fusion-Task-Lineage: 83fb4051-8e0f-4ee9-9f00-0e4d5cb8661e Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
10ebd0e025 |
fix(dashboard): keep quick-entry portal menus attached when space is tight
Extract shared fixed-menu positioning so deps/agent/node/priority pickers clamp max-height without detaching from the trigger when free space is shorter than the preferred dropdown height. |
||
|
|
fd9e4b2a30 |
FN-8509: create Coding Ideas Start tasks in Todo
Coding Ideas Start now atomically creates tasks in the validated Todo column. - Resolve Todo only from captured, visible Coding Ideas workflow metadata. - Preserve explicit Start destinations through Board and List create hosts without a follow-up move. - Add regression coverage, documentation, and a patch changeset. Files changed: .changeset/fn-8509-coding-ideas-start-todo.md | 7 +++++ docs/dashboard-guide.md | 4 +-- packages/dashboard/app/components/Column.tsx | 8 +++++- packages/dashboard/app/components/ListView.tsx | 5 ++++ .../dashboard/app/components/QuickEntryBox.tsx | 17 +++++++----- .../app/components/__tests__/Column.test.tsx | 16 ++++++++++- .../app/components/__tests__/ListView.test.tsx | 31 +++++++++++++++++++++- .../components/__tests__/QuickEntryBox.test.tsx | 31 ++++++++++------------ .../app/utils/__tests__/quickAddStart.test.ts | 25 ++++++++++++++++- packages/dashboard/app/utils/quickAddStart.ts | 21 ++++++++++++++- 10 files changed, 134 insertions(+), 31 deletions(-) Fusion-Task-Id: FN-8509 Fusion-Task-Lineage: 951599a0-f277-4499-aaea-90371ddeda72 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
c469f90b12 |
FN-8500: add Xiaomi provider icons
Show Xiaomi branding for direct provider names and MiMo model labels. - Add an accessible, tokenized Xiaomi provider mark - Infer boundary-safe MiMo labels through the shared provider icon key - Reuse shared inference in usage cards and cover Xiaomi mappings - Add a patch changeset for Xiaomi provider branding Files changed: .changeset/fn-8500-xiaomi-provider-icon.md | 7 +++ packages/dashboard/app/components/ProviderIcon.tsx | 24 +++++++++ .../dashboard/app/components/UsageIndicator.tsx | 58 +++------------------- .../app/components/__tests__/ProviderIcon.test.tsx | 48 ++++++++++++++++++ .../components/__tests__/UsageIndicator.test.tsx | 17 +++++++ packages/dashboard/app/styles.css | 1 + .../app/utils/__tests__/providerIconKey.test.ts | 9 ++++ packages/dashboard/app/utils/providerIconKey.ts | 8 +++ 8 files changed, 120 insertions(+), 52 deletions(-) Fusion-Task-Id: FN-8500 Fusion-Task-Lineage: 1784a180-cf41-4666-a59e-10aa24f47b72 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
da616e1cf9 |
FN-8498: add Quick Add Start menu
Add gesture-only Quick Add Start promotion for eligible hold-based workflows. - Open a Start menu with right-click or touch/pen long-press while preserving ordinary Save and Enter behavior. - Validate and snapshot workflow routing before creating, then move matching tasks through host callbacks. - Cover workflow guards, promotion outcomes, and Board/List callback wiring. Files changed: .changeset/fn-8498-quick-add-start-menu.md | 7 ++ docs/dashboard-guide.md | 4 + packages/dashboard/app/components/Column.tsx | 1 + packages/dashboard/app/components/ListView.tsx | 1 + .../dashboard/app/components/QuickEntryBox.css | 30 +++++ .../dashboard/app/components/QuickEntryBox.tsx | 115 ++++++++++++++++- .../app/components/__tests__/Column.test.tsx | 10 +- .../app/components/__tests__/ListView.test.tsx | 12 ++ .../components/__tests__/QuickEntryBox.test.tsx | 137 +++++++++++++++++++++ .../app/utils/__tests__/quickAddStart.test.ts | 38 ++++++ packages/dashboard/app/utils/quickAddStart.ts | 51 ++++++++ 11 files changed, 400 insertions(+), 6 deletions(-) Fusion-Task-Id: FN-8498 Fusion-Task-Lineage: 30d8e7d4-59f1-4c4f-9224-1a9d8c27141b Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
b6135f4bd5 |
FN-8494: keep task cards active while replanning
Keep task-card activity chrome visible throughout durable and fresh replanning states. - Treat needs-replan tasks as agent-active on triage and todo lanes without changing lock policy. - Cover board, list, mobile, pause, and freshness behaviors with regression tests. - Add a patch changeset for the replanning activity indicator. Files changed: .changeset/fn-8494-replan-active-glow.md | 7 +++++ .../app/components/__tests__/ListView.test.tsx | 4 +++ .../app/components/__tests__/TaskCard.test.tsx | 31 ++++++++++++++++++++++ .../app/utils/__tests__/taskActivity.test.ts | 23 ++++++++++++++++ packages/dashboard/app/utils/taskActivity.ts | 7 ++++- 5 files changed, 71 insertions(+), 1 deletion(-) Fusion-Task-Id: FN-8494 Fusion-Task-Lineage: a910a22a-cff7-423f-82ee-359830beb104 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
395f1364d6 |
FN-8493: show revising status badges
Rename the needs-replan badge to Revising across dashboard task views. - Map needs-replan status badges to the Revising translation. - Update English resources and generated resource types. - Cover board cards and desktop/mobile list rows with regression tests. - Add a patch changeset for the operator-facing copy fix. Files changed: .changeset/fn-8493-revising-status-badge.md | 7 ++++++ .../app/components/__tests__/ListView.test.tsx | 25 ++++++++++++++++++++++ .../app/components/__tests__/TaskCard.test.tsx | 13 +++++++++++ .../utils/__tests__/taskStatusBadgeLabel.test.ts | 6 ++++-- .../dashboard/app/utils/taskStatusBadgeLabel.ts | 8 +++---- packages/i18n/locales/en/app.json | 4 ++-- packages/i18n/src/resources.d.ts | 4 ++-- 7 files changed, 57 insertions(+), 10 deletions(-) Fusion-Task-Id: FN-8493 Fusion-Task-Lineage: 569d271b-48f9-42f4-83f8-4e9f86fdab68 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
69bd64d950 |
FN-8475: show planning status badges in Todo
Restore truthful planning status badges for Coding Ideas tasks across Todo views. - Replace column-based planning badge suppression with real-status detection - Render planning badges in board cards and desktop/mobile list rows - Add regression coverage and a patch changeset Files changed: .changeset/fn-8475-planning-status-badge.md | 7 ++++ packages/dashboard/app/components/ListView.tsx | 11 +++--- packages/dashboard/app/components/TaskCard.tsx | 7 ++-- .../app/components/__tests__/ListView.test.tsx | 40 ++++++++++++++-------- .../app/components/__tests__/TaskCard.test.tsx | 10 +++--- .../utils/__tests__/taskStatusBadgeLabel.test.ts | 33 +++++++++--------- .../dashboard/app/utils/taskStatusBadgeLabel.ts | 16 ++++----- 7 files changed, 70 insertions(+), 54 deletions(-) Fusion-Task-Id: FN-8475 Fusion-Task-Lineage: 98bff84e-bee7-4e3c-b7b2-e0a24709576b Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
8dc66bf3dd |
fix(dashboard): badge lane-owned gates and keep WIP progress clean
Suppress Ready while Plan Review is active, show only implementation steps on In-progress progress, and surface Plan Review / Code Review / Browser Verification as header badges on their lanes instead of progress bullets. |
||
|
|
1d4e8afa7b |
FN-8444: include planning time in task metrics
Track active planning time alongside execution time for costs, analytics, and task displays. - Persist planning timing state across task lifecycle transitions and recovery - Include planning activity in token cost, analytics, and dashboard timing displays - Add PostgreSQL migration support using the configured migration directory Files changed: .changeset/fn-8444-planning-time-cost.md | 7 +++ docs/dashboard-guide.md | 3 ++ docs/task-management.md | 5 ++ packages/core/src/index.ts | 1 + .../migrations/0029_planning_active_timing.sql | 3 ++ packages/core/src/postgres/schema-applier.ts | 14 ++++- packages/core/src/postgres/schema/project.ts | 2 + packages/core/src/productivity-analytics.ts | 29 +++++----- packages/core/src/store.ts | 2 +- .../core/src/task-store/archive-lifecycle-2.ts | 2 + packages/core/src/task-store/moves.ts | 7 +++ packages/core/src/task-store/persistence.ts | 4 ++ packages/core/src/task-store/remaining-ops-2.ts | 2 +- packages/core/src/task-store/serialization.ts | 7 +++ packages/core/src/task-store/task-row-mappers.ts | 2 +- packages/core/src/task-store/task-update.ts | 10 ++++ packages/core/src/task-timing.ts | 35 ++++++++++++ packages/core/src/types.ts | 12 +++++ packages/dashboard/app/components/TaskCard.tsx | 13 ++--- .../app/components/TaskTokenStatsPanel.tsx | 6 ++- .../app/components/__tests__/TaskCard.test.tsx | 17 ++++++ .../app/utils/__tests__/taskTiming.test.ts | 9 +++- packages/dashboard/app/utils/taskTiming.ts | 14 +++++ packages/dashboard/app/utils/taskTokenCost.ts | 2 + .../dashboard/src/task-planner-chat-metrics.ts | 14 ++++- packages/engine/src/__tests__/self-healing.test.ts | 61 +++++++++++++++++++++ packages/engine/src/executor.ts | 50 +++++++++++++++++ packages/engine/src/runtimes/in-process-runtime.ts | 3 ++ packages/engine/src/self-healing.ts | 62 ++++++++++++++++++++++ packages/engine/src/triage.ts | 10 ++++ packages/i18n/locales/en/app.json | 2 +- packages/i18n/locales/es/app.json | 2 +- packages/i18n/locales/fr/app.json | 2 +- packages/i18n/locales/ko/app.json | 2 +- packages/i18n/locales/zh-CN/app.json | 2 +- packages/i18n/locales/zh-TW/app.json | 2 +- 36 files changed, 384 insertions(+), 36 deletions(-) Fusion-Task-Id: FN-8444 Fusion-Task-Lineage: 0178e0a7-3018-4ef4-be9b-6de5f964fb58 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
8c01cc7c7a |
FN-8437: restore Planning Mode sessions after navigation
Persist active Planning interviews across embedded navigation while honoring explicit dismissal. - Store the active Planning session per project and restore it through the existing session loader. - Clear the saved active session for dismissals and terminal session actions. - Cover leave-and-return, completed-away, fresh-plan, and dismissed-error flows; document the behavior. - Add a patch changeset for the Planning session recovery fix. Files changed: .changeset/fn-8437-planning-leave-return-restore.md | 7 ++ docs/dashboard-guide.md | 2 +- packages/dashboard/app/components/PlanningModeModal.tsx | 51 ++++++++++++- packages/dashboard/app/components/__tests__/PlanningModeModal.planning-flow.test.tsx | 86 ++++++++++++++++++++++ packages/dashboard/app/hooks/__tests__/modalPersistence.test.ts | 18 +++++ packages/dashboard/app/hooks/modalPersistence.ts | 17 +++++ packages/dashboard/app/utils/__tests__/projectStorage.test.ts | 3 +- packages/dashboard/app/utils/projectStorage.ts | 1 + 8 files changed, 180 insertions(+), 5 deletions(-) Fusion-Task-Id: FN-8437 Fusion-Task-Lineage: 892aacb6-139e-4487-a40c-1bedd51237ea Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
c279b8cefc |
FN-8347: add report capture regression coverage
Expand dashboard report tests across screenshot capture and activity-trace egress. - Cover screenshot capture pending, failure, opt-out, and mobile modal behavior - Verify bounded activity traces and recursive privacy scrubbing across report routes - Exercise screenshot artifact references through draft, duplicate, and filing paths Files changed: .../app/components/__tests__/ReportModal.test.tsx | 46 ++++++++++++ .../app/utils/__tests__/activity-trace.test.ts | 32 +++++++++ .../src/__tests__/report-pipeline.test.ts | 83 +++++++++++++++++++++- .../dashboard/src/__tests__/report-routes.test.ts | 35 ++++++++- .../dashboard/src/__tests__/report-scrub.test.ts | 19 ++++- 5 files changed, 208 insertions(+), 7 deletions(-) Fusion-Task-Id: FN-8347 Fusion-Task-Lineage: 5323fdcb-01c3-48aa-beb9-ecd4651e981a Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
b2a7425c76 |
refactor(cutover 3/3): dashboard + changesets — IR-driven lifecycle cutover (#2335)
Completes the IR-driven lifecycle cutover: **the workflow IR becomes the
single source of truth for task lifecycle.** Node column assignments
move cards at runtime, every lifecycle predicate re-keys on column
traits instead of literal column ids, and the graph exclusively owns
review gates.
Plan (the spec for this work):
[`docs/plans/2026-07-18-001-refactor-ir-driven-lifecycle-cutover-plan.md`](docs/plans/2026-07-18-001-refactor-ir-driven-lifecycle-cutover-plan.md)
## What changed
**IR as runtime authority (R1, R2).** Graph traversal crossing a node
column boundary moves the card through the store's trait-hook `moveTask`
path, attributed `workflowMoveSource: "workflow-graph"` and emitting
`task:column-transition`. This replaces the executor's hardcoded
`moveTask(id, "in-review")` merge boundary and its handoff-invariant
allowlist. Scheduler, hold/release, self-healing, merger and
finalization now key on column traits
(`intake`/`hold`/`wip`/`merge-blocker`/`human-review`/`merge`/`complete`/`archived`/`timing`/`abort-on-exit`/`reset-on-entry`/`stall-detection`),
with rebound targets resolved per KTD-10.
**Single ownership of review gates (R4, R5).** Triage's out-of-graph
Plan Review gate is deleted; the graph is the sole author. `pending`
step results are CAS-claimed leases with owner and staleness floor
(KTD-4), so a crash/restart re-entry can no longer dispatch a second
reviewer and silently discard the losing verdict.
**Graph ownership is unconditional (R9).** The legacy execute fallback
is gone: `maybeExecuteWorkflowGraph` is now `executeWorkflowGraph`
returning `void`, `graphCompletion` is a required parameter, and a store
that cannot resolve a workflow fails closed rather than silently running
nothing. Also deleted, with a tombstone ratchet: `fn_review_step` and
its RETHINK/session-rewind machinery, `workflow-cutover.ts`,
`workflow-authoritative-driver.ts`, `workflow-parity-observer.ts`, and
the `graphCompletionInterceptors` map.
**`reviewLevel` becomes a creation-time preset (R6)** writing
`enabledWorkflowSteps`, with zero runtime reads.
**Upgrade path (R10).** Migration 0026 adds the durable per-node-entry
IR pin (KTD-3) and the one-time adoption stamp (KTD-8);
`planLegacyAdoption` is the single shared decision run by both the
startup sweep and the store-open reconcile, so pre-cutover rows are
adopted instead of freezing. A stale-binary guard refuses to open a
database migrated by a newer binary.
**Operator surfaces (R2, R11).** Four places still closed the column
set: the dashboard coerced every ingested task's column through the
legacy six-id enum (a card in a custom `Merging` column rendered in
**Triage**), `POST /tasks/:id/move` answered 400 for any
workflow-defined column, retry/reset/re-engage/unassign/spec-revise used
hardcoded move targets, and GitHub issue open/closed mapping
literal-compared `done`/`archived`. All now resolve from the task's
workflow by trait, each with a legacy fallback so `builtin:coding` is
byte-identical.
## Evidence
`builtin:coding` keeps its column ids and observable behavior
byte-compatible (R8, KTD-7), pinned by a characterization oracle. A new
**6-column benchmark acceptance suite** drives a user-authored workflow
— `Ideas → Todo → In-progress → In-review → Merging → Done` — asserting
the ordered transition trail, single-mover at the hold→wip seam (KTD-2),
column-role purity (R12), bounded review cycles from workflow config,
and park-in-place on failure (R3). The same fixture is proven
**editor-buildable** through the real save-validation path, plus
negative cases.
Verified locally on this branch, post-rebase:
- `pnpm test:gate` — green (engine-core 294/294, pg-gate 126/126,
ci-workflow 63/63)
- characterization oracle 59/59, tombstones 5/5, 6-column benchmark
11/11
- `tsc --noEmit` clean for `@fusion/core`, `@fusion/engine`,
`@fusion/dashboard` (both `tsconfig.json` and `tsconfig.app.json`)
## Known reds
- **`executor-task-done-invariant` → "moves a cleanly completed task to
in-review via the merge-node boundary"** — red on this branch. A
real-Postgres test whose graph re-entry rebounds the card to
`in-progress` after `execute()` returns. Not in the merge gate, so it
does not gate CI. Honest status: I could **not** verify it green on
pristine `main` — running main's tests in this worktree reuses built
artifacts and produced obviously polluted results, so I am not claiming
"pre-existing". It needs its own look.
- **`html2canvas` / FN-8309 — fixed here by deleting dead code.**
`packages/dashboard/app/utils/capture-screenshot.ts` imported
`html2canvas`, which is not a dependency of `@fusion/dashboard` and is
**not in `pnpm-lock.yaml` at all**, so it had never compiled in CI. The
file had **zero importers**. Main never caught it because PR Checks runs
only on pull requests (main's last PR Checks run was in June) while
main's own pushes run just the non-blocking Full Suite — so the required
**Typecheck** check was failing on *every* PR against main, including
this one. Inherited from `88b0db0f4` (FN-8309). **To restore when the
feature lands its dependency properly:** `git checkout
|
||
|
|
e30f6d851f |
FN-8293: add native structure drag-and-draft mail composition
Mail composers can attach native structures by drag-and-drop and generate report narratives with AI. - Add a shared native-structure drag protocol and attachable previews across Mail source views. - Add a disposable compose-chat panel that drafts narratives around attached embeds. - Cover drag behavior and compose-chat session lifecycle, and document the Mail interaction. Files changed: .changeset/fn-8293-mail-drag-compose-chat.md | 7 ++ docs/dashboard-guide.md | 3 + .../dashboard/app/components/ComposeChatPanel.css | 33 ++++++ .../dashboard/app/components/ComposeChatPanel.tsx | 117 +++++++++++++++++++++ packages/dashboard/app/components/EvalsView.tsx | 3 +- packages/dashboard/app/components/GoalsView.tsx | 6 ++ packages/dashboard/app/components/InsightsView.tsx | 8 +- .../dashboard/app/components/MessageComposer.css | 22 ++++ .../dashboard/app/components/MessageComposer.tsx | 92 ++++++++++++++-- .../dashboard/app/components/MissionManager.tsx | 11 +- .../components/__tests__/ComposeChatPanel.test.tsx | 102 ++++++++++++++++++ .../components/__tests__/MessageComposer.test.tsx | 25 +++++ .../utils/__tests__/nativeStructureDrag.test.ts | 56 ++++++++++ .../dashboard/app/utils/nativeStructureDrag.ts | 54 ++++++++++ 14 files changed, 525 insertions(+), 14 deletions(-) Fusion-Task-Id: FN-8293 Fusion-Task-Lineage: 9864e76c-2a0d-41f6-b7f2-de8dd63710b5 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
927f2baf7d |
FN-8354: add Oh My Pi provider logo
Render the official Oh My Pi mark throughout provider selection and analytics surfaces. - Add a tokenized OMP SVG icon and provider aliases. - Infer OMP-tagged model identifiers without misattributing bare models. - Cover OMP icon rendering and inference with dashboard tests. Files changed: packages/dashboard/app/components/ProviderIcon.tsx | 26 +++++++++++++++++++++- .../app/components/__tests__/ProviderIcon.test.tsx | 23 +++++++++++++++++++ packages/dashboard/app/styles.css | 1 + .../app/utils/__tests__/providerIconKey.test.ts | 8 +++++++ packages/dashboard/app/utils/providerIconKey.ts | 9 ++++++++ 5 files changed, 66 insertions(+), 1 deletion(-) Fusion-Task-Id: FN-8354 Fusion-Task-Lineage: 71d5638c-b41e-4960-9391-c7a441d04f51 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
7e7c3c999b |
FN-8317: add reviewed report screenshots and activity traces
Capture optional report context while preserving scrubbed text-first filing.\n\n- Add browser screenshot capture, review, validation, and compensating upload cleanup.\n- Include bounded activity traces in report drafts and filing flows.\n- Preserve roadmap deduplication and document report privacy behavior.\n\nFiles changed:\n .changeset/fn-8317-report-context.md | 7 + docs/dashboard-guide.md | 4 +- packages/dashboard/app/api/report.ts | 15 +- packages/dashboard/app/components/ReportModal.css | 5 +- packages/dashboard/app/components/ReportModal.tsx | 52 +++---- .../app/components/__tests__/ReportModal.test.tsx | 14 ++ packages/dashboard/app/hooks/useViewState.ts | 7 + .../app/utils/__tests__/report-capture.test.ts | 17 +++ packages/dashboard/app/utils/report-capture.ts | 47 +++++++ packages/dashboard/package.json | 1 - .../src/__tests__/report-pipeline.test.ts | 100 +++++++++++++- .../dashboard/src/__tests__/report-routes.test.ts | 17 +++ .../dashboard/src/__tests__/report-scrub.test.ts | 8 ++ packages/dashboard/src/github.ts | 107 +++++++++++++++ packages/dashboard/src/report-pipeline.ts | 152 ++++++++++++++++----- packages/dashboard/src/report-scrub.ts | 8 ++ .../dashboard/src/routes/register-report-routes.ts | 103 ++++++-------- pnpm-lock.yaml | 129 +++++++++-------- 18 files changed, 590 insertions(+), 203 deletions(-) Fusion-Task-Id: FN-8317 Fusion-Task-Lineage: 9d200cee-cf69-4827-9e1e-f7789adf09e0 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
88b0db0f4d |
FN-8309: add report screenshot and activity capture
Add opt-in local screenshot attachments and scrubbed activity traces to reports. - Capture, preview, confirm, and locally retain report screenshots without GitHub egress. - Include bounded activity traces in draft and filing context with server-side validation. - Add report pipeline coverage, documentation, and release metadata. Files changed: .changeset/fn-8309-report-capture.md | 7 ++ docs/dashboard-guide.md | 2 + packages/dashboard/app/App.tsx | 10 ++ packages/dashboard/app/api/report.ts | 16 ++- packages/dashboard/app/components/ReportModal.css | 3 + packages/dashboard/app/components/ReportModal.tsx | 45 ++++++- .../app/utils/__tests__/activity-trace.test.ts | 19 +++ packages/dashboard/app/utils/activity-trace.ts | 37 ++++++ packages/dashboard/app/utils/capture-screenshot.ts | 22 ++++ packages/dashboard/package.json | 5 +- .../src/__tests__/report-pipeline.test.ts | 13 +++ packages/dashboard/src/report-pipeline.ts | 29 ++++- packages/dashboard/src/routes.ts | 2 +- .../dashboard/src/routes/register-report-routes.ts | 85 +++++++++++++- packages/dashboard/src/routes/types.ts | 4 +- pnpm-lock.yaml | 129 ++++++++++----------- 16 files changed, 341 insertions(+), 87 deletions(-) Fusion-Task-Id: FN-8309 Fusion-Task-Lineage: db9c14a6-1721-4e81-8891-cfd301473cd0 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
d5db5fd58d |
FN-8304: detect foreign issue-form content for translation
Recognize foreign GitHub and GitLab issue-form prose so import translation remains available. - Remove issue-form scaffolding before content-language scoring. - Identify Czech as unsupported foreign Latin content and offer translation. - Cover automatic and manual translation paths with issue-form fixtures. - Document the expanded import translation behavior and add a patch changeset. Files changed: .changeset/fn-8304-issue-form-translation.md | 7 ++ docs/dashboard-guide.md | 2 +- docs/settings-reference.md | 2 +- packages/core/src/detect-content-language.ts | 66 ++++++++++-- .../__tests__/GitHubImportAutoTranslate.test.tsx | 42 +++++++- .../utils/__tests__/detectContentLanguage.test.ts | 120 +++++++++++++++++++++ .../src/__tests__/import-translate-service.test.ts | 56 ++++++++++ 7 files changed, 284 insertions(+), 11 deletions(-) Fusion-Task-Id: FN-8304 Fusion-Task-Lineage: ef6ca70c-aa67-40ff-a23a-077940b5a5f0 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
9debeaa951 |
FN-8300: show planner activity on status-null cards
Keep planning cards visibly active while fresh planner logs precede authoritative task status updates. - Track bounded client-only planner activity from fresh triage log events - Render matching pulsing Planning badges in board and list card views - Cover transient activity, authoritative clearing, and inactive edge cases Files changed: docs/dashboard-guide.md | 2 + packages/core/src/types.ts | 6 +++ packages/dashboard/app/components/ListView.tsx | 26 ++++++++--- packages/dashboard/app/components/TaskCard.tsx | 20 +++++--- .../app/components/__tests__/ListView.test.tsx | 38 +++++++++++++++ .../app/components/__tests__/TaskCard.test.tsx | 19 +++++++- .../dashboard/app/hooks/__tests__/useTasks.test.ts | 54 ++++++++++++++++++++++ packages/dashboard/app/hooks/useTasks.ts | 44 +++++++++++++----- .../app/utils/__tests__/taskActivity.test.ts | 22 ++++++++- packages/dashboard/app/utils/taskActivity.ts | 15 +++++- 10 files changed, 219 insertions(+), 27 deletions(-) Fusion-Task-Id: FN-8300 Fusion-Task-Lineage: e12f1277-5628-45a1-b731-54310027540e Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
68ce5c31da |
fix: harden onboarding/git/desktop batch per multi-agent review findings
- uninstaller: taskkill only the first, digits-only postmaster.pid line (the for /f loop ran taskkill on the port/epoch lines — potential unrelated-process kill) - git-missing dialogs use new ConfirmOptions.alwaysAsk so global skip-confirmations cannot silently pick an unseen choice - Windows quit prompt: embedded-local runtimes only, skipped during OS session end (sync dialog blocked Windows shutdown) - 'leave it running' detaches the embedded lifecycle (disarms its process shutdown hook) so Electron exit cannot kill the postmaster the operator chose to keep (new detachKeepingEmbedded) - wizard: ref-based double-submit guard around the async git preflight - clone route: ENOENT invalidate-and-retry matching runGitCommand - openExternalUrl: drop the async window.open fallback (always popup-blocked); log bridge failures instead - DirectoryPicker: close the panel when listing the created folder fails so Select cannot re-commit the parent - git status probe bounded to two spawns (PATH + first candidate) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
7bf83bb458 |
fix(desktop,dashboard): open OAuth URLs via activation-free IPC on desktop
window.open after awaiting /auth/login can outlive Chromium's transient user activation and get silently popup-blocked in the desktop app — the OpenAI Codex flow (method select + localhost callback server) crossed that threshold while Anthropic's faster flow usually didn't, so Codex login never opened the browser. Add a shell:openExternal IPC bridge (http/https only), expose it in preload, and route every dashboard auth-URL open through an openExternalUrl helper that falls back to window.open on the web. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
9152d8fa61 |
FN-8229: remove background AI tasks pill
Replace the redundant footer AI session control with progress and action visibility in the session notification banner. - Delete the BackgroundTasksIndicator component, styles, footer wiring, and obsolete translations. - Surface non-planning generating and retained error sessions through the notification banner while leaving Planning in its dedicated view. - Update session lifecycle, localization, documentation, and regression coverage. Files changed: .../fn-8229-remove-ai-background-tasks-pill.md | 7 + docs/dashboard-guide.md | 4 +- packages/dashboard/README.md | 1 - packages/dashboard/app/App.tsx | 28 +-- .../app/__tests__/css-hygiene-scan.test.ts | 2 - .../dashboard-component-color-tokenization.test.ts | 1 - packages/dashboard/app/api/legacy.ts | 2 +- .../app/components/BackgroundTasksIndicator.css | 130 ------------- .../app/components/BackgroundTasksIndicator.tsx | 210 --------------------- .../dashboard/app/components/ExecutorStatusBar.css | 3 - .../dashboard/app/components/ExecutorStatusBar.tsx | 30 +-- .../app/components/SessionNotificationBanner.css | 10 + .../app/components/SessionNotificationBanner.tsx | 52 ++++- .../app/components/SubtaskBreakdownModal.tsx | 7 +- .../app/components/__tests__/App.test.tsx | 11 +- .../__tests__/ExecutorStatusBar.test.tsx | 1 - .../__tests__/SessionNotificationBanner.test.tsx | 88 ++++----- .../components/__tests__/utility-mobile.test.tsx | 191 +------------------ .../app/components/dashboard/DashboardBanners.tsx | 8 +- .../dashboard/__tests__/DashboardBanners.test.tsx | 30 ++- .../hooks/__tests__/useBackgroundSessions.test.ts | 21 ++- .../dashboard/app/hooks/useBackgroundSessions.ts | 21 ++- .../app/utils/__tests__/appLifecycle.test.ts | 61 ++---- packages/dashboard/app/utils/appLifecycle.ts | 16 +- packages/dashboard/src/routes.ts | 2 +- packages/i18n/locales/en/app.json | 33 +--- packages/i18n/locales/es/app.json | 44 ++--- packages/i18n/locales/fr/app.json | 44 ++--- packages/i18n/locales/ko/app.json | 44 ++--- packages/i18n/locales/zh-CN/app.json | 44 ++--- packages/i18n/locales/zh-TW/app.json | 44 ++--- packages/i18n/src/resources.d.ts | 48 ++--- 32 files changed, 316 insertions(+), 922 deletions(-) Fusion-Task-Id: FN-8229 Fusion-Task-Lineage: a232fb04-08ff-4f0a-a440-e94545243e61 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
7f175b036b |
FN-8195: label replan task status
Display the needs-replan task state with a clear localized operator-facing label. - Map needs-replan status badges to Replan centrally. - Add English i18n resources and generated resource typings. - Cover the status-label mapping and add a patch changeset. Files changed: .changeset/fn-8195-replan-badge.md | 7 +++++++ .../dashboard/app/utils/__tests__/taskStatusBadgeLabel.test.ts | 4 ++++ packages/dashboard/app/utils/taskStatusBadgeLabel.ts | 8 ++++++++ packages/i18n/locales/en/app.json | 2 ++ packages/i18n/src/resources.d.ts | 2 ++ 5 files changed, 23 insertions(+) Fusion-Task-Id: FN-8195 Fusion-Task-Lineage: 7ad115b8-6405-426e-9201-bfa1258fff5e Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
79b05c62ea |
FN-8190: apply heartbeat multipliers to agent health
Align dashboard health labels with project-resolved heartbeat cadence. - Load the project heartbeat multiplier in agent list and detail views. - Apply the effective cadence once when determining stale agents. - Cover long-cadence and multiplier boundary health states. Files changed: .../dashboard/app/components/AgentDetailView.tsx | 19 ++++++++++++-- .../dashboard/app/components/AgentListModal.tsx | 19 ++++++++++++-- packages/dashboard/app/components/AgentsView.tsx | 2 +- .../__tests__/AgentDetailView.test-helpers.ts | 3 +++ .../components/__tests__/AgentListModal.test.tsx | 27 ++++++++++++++++++++ .../app/utils/__tests__/agentHealth.test.tsx | 29 ++++++++++++++++++++++ packages/dashboard/app/utils/agentHealth.tsx | 25 ++++++++++++++++--- 7 files changed, 115 insertions(+), 9 deletions(-) Fusion-Task-Id: FN-8190 Fusion-Task-Lineage: 1efcdc19-503a-4893-b277-f65e900a399b Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |