ff58df22e4e7a6eafe8797f4d2f0807f0cd9d1f6
6956 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
ff58df22e4 |
chore(deps-dev): bump @vitest/coverage-v8 from 4.1.8 to 4.1.10 (#2445)
Bumps [@vitest/coverage-v8](https://github.com/vitest-dev/vitest/tree/HEAD/packages/coverage-v8) from 4.1.8 to 4.1.10. <details> <summary>Release notes</summary> <p><em>Sourced from <a href="https://github.com/vitest-dev/vitest/releases">@vitest/coverage-v8's releases</a>.</em></p> <blockquote> <h2>v4.1.10</h2> <h3> 🐞 Bug Fixes</h3> <ul> <li><strong>browser</strong>: Check fs access in builtin commands [backport to v4] - by <a href="https://github.com/hi-ogawa"><code>@hi-ogawa</code></a>, <strong>Hiroshi Ogawa</strong> and <strong>OpenCode (claude-opus-4-8)</strong> in <a href="https://redirect.github.com/vitest-dev/vitest/issues/10680">vitest-dev/vitest#10680</a> <a href="https://github.com/vitest-dev/vitest/commit/5c18dd267"><!-- raw HTML omitted -->(5c18d)<!-- raw HTML omitted --></a></li> <li><strong>vm</strong>: Fix external module resolve error with deps optimizer query for encoded URI [backport to v4] - by <a href="https://github.com/SveLil"><code>@SveLil</code></a> and <a href="https://github.com/hi-ogawa"><code>@hi-ogawa</code></a> in <a href="https://redirect.github.com/vitest-dev/vitest/issues/10661">vitest-dev/vitest#10661</a> <a href="https://github.com/vitest-dev/vitest/commit/bae52b511"><!-- raw HTML omitted -->(bae52)<!-- raw HTML omitted --></a></li> </ul> <h5> <a href="https://github.com/vitest-dev/vitest/compare/v4.1.9...v4.1.10">View changes on GitHub</a></h5> <h2>v4.1.9</h2> <h3>🐞 Bug Fixes</h3> <ul> <li>Fix <code>importOriginal</code> with optimizer and query import [backport to v4] - by <strong>Hiroshi Ogawa</strong>, <strong>David Harris</strong>, <strong>Codex</strong>and <strong>Vladimir</strong> in <a href="https://redirect.github.com/vitest-dev/vitest/issues/10546">vitest-dev/vitest#10546</a> <a href="https://github.com/vitest-dev/vitest/commit/a5180190c"><!-- raw HTML omitted -->(a5180)<!-- raw HTML omitted --></a></li> <li><strong>browser</strong>: <ul> <li>Wait for orchestrator readiness before resolving browser sessions [backport to v4] - by <strong>Vladimir</strong> and <strong>Séamus O'Connor</strong> in <a href="https://redirect.github.com/vitest-dev/vitest/issues/10555">vitest-dev/vitest#10555</a> <a href="https://github.com/vitest-dev/vitest/commit/7fb29651a"><!-- raw HTML omitted -->(7fb29)<!-- raw HTML omitted --></a></li> <li>Wait for iframe tester readiness before preparing [backport to v4] - by <strong>Vladimir</strong> and <strong>Séamus O'Connor</strong> in <a href="https://redirect.github.com/vitest-dev/vitest/issues/10497">vitest-dev/vitest#10497</a> and <a href="https://redirect.github.com/vitest-dev/vitest/issues/10556">vitest-dev/vitest#10556</a> <a href="https://github.com/vitest-dev/vitest/commit/fbc626c40"><!-- raw HTML omitted -->(fbc62)<!-- raw HTML omitted --></a></li> </ul> </li> <li><strong>mocker</strong>: <ul> <li>Hoist vi.mock() for vite-plus/test imports [backport to v4] - by <strong>Hiroshi Ogawa</strong>, <strong>LongYinan</strong>, <strong>Claude Opus 4.8</strong> and <strong>Vladimir</strong> in <a href="https://redirect.github.com/vitest-dev/vitest/issues/10548">vitest-dev/vitest#10548</a> <a href="https://github.com/vitest-dev/vitest/commit/2c9559c02"><!-- raw HTML omitted -->(2c955)<!-- raw HTML omitted --></a></li> </ul> </li> <li><strong>pool</strong>: <ul> <li>Prevent test run hang on worker crash [backport to v4] - by <strong>Ari Perkkiö</strong> and <strong>Jattioui Ismail</strong> in <a href="https://redirect.github.com/vitest-dev/vitest/issues/10543">vitest-dev/vitest#10543</a> and <a href="https://redirect.github.com/vitest-dev/vitest/issues/10564">vitest-dev/vitest#10564</a> <a href="https://github.com/vitest-dev/vitest/commit/934b0f587"><!-- raw HTML omitted -->(934b0)<!-- raw HTML omitted --></a></li> </ul> </li> </ul> <h5><a href="https://github.com/vitest-dev/vitest/compare/v4.1.8...v4.1.9">View changes on GitHub</a></h5> </blockquote> </details> <details> <summary>Commits</summary> <ul> <li><a href=" |
||
|
|
a7856e8a90 |
chore(deps): bump @xyflow/react from 12.11.0 to 12.11.2 (#2447)
Bumps [@xyflow/react](https://github.com/xyflow/xyflow/tree/HEAD/packages/react) from 12.11.0 to 12.11.2. <details> <summary>Release notes</summary> <p><em>Sourced from <a href="https://github.com/xyflow/xyflow/releases">@xyflow/react's releases</a>.</em></p> <blockquote> <h2><code>@xyflow/react</code><a href="https://github.com/12"><code>@12</code></a>.11.2</h2> <h3>Patch Changes</h3> <ul> <li> <p><a href="https://redirect.github.com/xyflow/xyflow/pull/5825">#5825</a> <a href=" |
||
|
|
710d56b2db |
U4 trim: delete the dependency-blocked-todo feature (unreachable in production) and revert 5a2de7d (#2477)
Stacked on #2474. Base is `feature/workflow-vocabulary-u4-dead-code` — do not merge before it. Deletes an **entire feature that has never executed in production**, and reverts `5a2de7d`, which only threaded resolved lifecycle columns through it. ## Reachability evidence — the whole basis for this ``` surfaceDependencyBlockedTodos ← in NEITHER sweep registry; no caller in └─ getDependencyBlockedTodoReporter() engine/dashboard/cli — only tests └─ engine/dependency-blocked-todo-reporter.ts ← sole caller of ↓ └─ core/computeDependencyBlockedTodoReport ``` self-healing owns two name-based sweep registries (`runStartupRecovery`, 58 entries; `runMaintenance`, 76). `surfaceDependencyBlockedTodos` is in **neither**, so nothing ever invoked the chain below it. Its four tests passed while proving nothing about production. ## Why delete rather than wire it up Wiring was the tempting option and is the riskier one. Switching on a 450-line path that has never run — whose tests therefore establish nothing about its behavior against real data — is a **behavior change with unquantified blast radius**. This program already refused exactly that move for the **pool-id sentinel**, a one-line change that would switch on dormant enforcement across every project. This is the same class of move at ~450× the size. Deleting is also the recoverable direction: git keeps the feature, and it can be resurrected deliberately — with tests that prove it *runs* — if dependency-blocked reporting is actually wanted. ## The settings keys go with it `dependencyBlockedTodoReportEnabled` defaulted `true` while driving nothing. A schema/API-visible switch that lies about what the system does is worse than no switch. (It had no dashboard UI field — the dashboard test allowlist already recorded it as *"no UI field"*.) Four sibling tuning keys are removed with it. ## Against my own earlier work `5a2de7d` threaded resolved lifecycle roles into `computeDependencyBlockedTodoReport` and its reporter, answering a review finding I confirmed as real. **The code was correct; the impact claim was not**, because the path never executes. Neither the reviewer nor I checked *reachability* before agreeing the defect mattered — only correctness. A correction is posted on that thread in #2470. **Scope limit on that admission:** the same finding also described *incorrect scheduler ordering*. That half runs through `buildUnblockWeightMap` in `task-priority.ts`, which is **live** and was already threading `terminalColumns` (B1, `434b385`). Scheduler ordering was never affected, before or after. ## What survives `blocker-fanout.ts` **stays** — it is live via `task-priority.ts`. Only the plural `holdColumns` option added by `5a2de7d` is reverted, since the deleted report was its sole consumer. `holdColumn` (singular, from B1) remains. ## Net **1,244 deletions / 5 insertions across 15 files** — ~450 production lines, ~684 test lines, 5 settings keys. ## Verification - `tsc --noEmit` clean in **core, engine, and dashboard-app**; `pnpm lint` clean - merge gate green (299 + 10 + 71) - self-healing suite: 411 passed, 1 **pre-existing** failure (`archiveStaleDoneTasks`) - dashboard settings-descriptions suite green - `settings-parity.test.ts` has one **pre-existing** failure (`agentToolOutputMaxChars` overlap) that fails identically with these changes stashed — unrelated to this deletion No changeset: `@fusion/core` and `@fusion/engine` are private. 🤖 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** * Added quiet-window backlog health diagnostics for stalled items in review, with repeat-alert suppression. * Added default thresholds for backlog-pressure alerts. * **Changes** * Removed dependency-blocked todo reporting and related alerts. * Removed the dependency-blocked todo enable/disable setting; remaining tuning options are no longer active. * Updated the workflow hold classification to use a single todo column. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
4158cf1ab7 |
Phase A: workflow-owned lifecycle foundation (U1, U2, U3) (#2467)
Phase A (Foundation) of
`docs/plans/2026-07-26-001-refactor-workflow-owned-lifecycle-plan.md`.
Three units, one commit each. No operator-visible behavior change.
## U1 — Lifecycle-column resolution seam
`resolveLifecycleColumns(ir)` returns `{ intake, hold, wip, review,
complete, archived }` — the first column carrying each trait,
`undefined` for a role no column carries.
`resolveTaskLifecycleColumns(store, taskId, cache?)` is the store-aware
form; the cache is caller-owned so a sweep reads one IR per workflow
rather than one per card.
A v1/column-less IR resolves to `undefined` for the **whole struct**
rather than a struct of undefined roles. A caller must be able to
distinguish "this workflow declares no hold column" (a real shape to
honor) from "no column vocabulary at all" (skip and log) — only the
second licenses conservative fallback.
Nothing consumes the seam yet; Phases B–D convert the ~207 hardcoded
column literals onto it.
## U2 — Delete the pre-cutover parity machinery (delete-only)
**`workflow-columns-settings.ts`** — `isWorkflowColumnsEnabled` had the
body `return true`. Six live call sites branched on it, so every
flag-OFF arm was dead code that read as a supported configuration.
Deleted; surviving side inlined at self-healing's transitionPending
sweep, the scheduler's per-column capacity diagnostic, merge-trait's
policy resolver, the board-workflows payload, two task-workflow routes,
and the CLI TUI's column enrichment.
**`workflow-parity.ts`** — asserted the default workflow's adjacency
*equals* the legacy `VALID_TRANSITIONS`. U11 deliberately breaks that
equality by merging Todo into Planning, so this is not a stale assertion
to update; it is a contract against the target state. Its emitter
(`workflow-parity-observer.ts`) is already a tombstone, so
`getWorkflowParitySummary` and `computeWorkflowColumnsGraduationReport`
aggregated run-audit rows nothing writes and had no caller outside
`TaskStore`. Both store methods go with it.
`flagEnabled` stays on the board-workflows **wire** as a constant `true`
— shipped dashboard clients still branch on it, and changing the
response shape is not a deletion. U10 retires the field once no client
reads it.
The `legacy-tombstones` ratchet is extended to both files plus seven
symbols, each with the reason it is gone.
### ⚠️ Finding: the third listed deletion was NOT dead
The plan also lists "the flag-off inline move path" in
`task-store/moves.ts`. It is **not** deleted, per U2's execution note
("any behavior change found while removing a branch means the branch was
not dead").
That path is gated on `isWorkflowColumnsCompatibilityFlagEnabled`
(`store.ts:38`) — a **different** function from the always-true public
helper. It reads the raw `experimentalFeatures.workflowColumns` setting,
which nothing in production sets (`settings-schema.ts:396` — "no default
flags are emitted"; zero non-test writers; the operator's own
`~/.fusion/settings.json` has no such key). So `useWorkflow` is false
for effectively every real project: the flag-OFF inline side effects are
the **live** default move path and the flag-ON `default-workflow-hooks`
path is the dead one. The code says so itself at `moves.ts:638`.
Deleting that branch would swap every project onto an untravelled code
path — a behavior change, not a deletion.
**Carry this into Phases B and C, stated plainly so the plan's error is
not repeated:**
> **The inline move path in `moves.ts` is LIVE.
`default-workflow-hooks.ts` (the trait-hook path) is DEAD.** KTD-6
asserted the inverse. Until the convergence unit lands, **nothing may
assume trait hooks run** — a guard, sweep, or subscriber written against
`applyDefaultWorkflowMoveEffects` would never fire in production and
would still pass its tests.
Convergence is **not** attempted here. It is its own unit (Phase A2)
with a proper equivalence proof, per operator decision.
### U3's emit point is on the LIVE path — the seam is not born dead
Worth stating explicitly because it is the failure mode that would make
every later subscriber silently never fire: the `TaskTransitioned` emit
is **not** inside the `if (useWorkflow)` branch. That block closes at
`moves.ts:1212`; the emit sits at `:1214`, beside the existing
`store.emit("task:moved", …)`, on the unconditional post-commit path. It
therefore fires on **both** the live inline path and the dead hooks
path, and the convergence unit inherits the obligation to keep it firing
on whichever path survives — same events, same order, same payloads.
The graph-side emitters (`NodeEntered`, `RunSuspended`) carry the same
risk from a different direction: the bus refuses an invalid payload
*silently* by design, so an emitter regression would stop the event with
no test failure. They are asserted end-to-end through the real bus —
"did a subscriber actually receive it", not "was emit called" — because
a spy passes on a refused payload. The `moveTaskInternalImpl` emit does
**not** yet have that end-to-end assertion against a real store move;
that proof belongs to the convergence unit, which has to build the
both-paths fixture anyway.
## U3 — Post-commit event seam with a transactional outbox
**The bus is not a queue, not a transaction participant, and not a
delivery guarantee.** Durable follow-on work uses the transactional
outbox — a `workflow_work_items` row written *inside* the transition
transaction (the shape `createCompletionHandoffWorkflowWork` already
uses). "Emit after commit, let a subscriber enqueue the work" has a
crash window where a process dies between commit and subscriber, leaving
no event *and* no work-item row, so required work is skipped permanently
with nothing to recover from. Post-commit subscribers therefore carry
only losable reactions.
Emission is consequently lossy and isolated by design: a throwing or
rejecting subscriber is caught and logged, cannot roll back the
transition, and cannot stop the others. Deliveries append to one serial
chain, so two transitions on a task deliver in commit order.
The ids/outcomes-only rule is **mechanised, not documented** —
run-audit's equivalent lives only in prose and has been violated
repeatedly. A payload carrying an object body or a prose string is
refused at the emit boundary and never reaches a subscriber or log sink.
It degrades rather than throws: the emitter is post-commit, so a shape
bug must not become a lifecycle failure.
Emit points: `TaskTransitioned` from the single post-commit point in
`moveTaskInternalImpl`; `NodeEntered` and `RunSuspended` from the graph
column boundary, the latter *after* the durable continuation is
persisted so an observed suspension implies a resumable run.
`registerWorkflowEventSubscribers` (engine) is empty on purpose —
U7/U8/U10 move real reactions onto it, each with the characterization
test proving the reaction was non-authoritative first.
## Verification
- `pnpm test:gate` — green (2/10, 16/299, 1/71).
- `pnpm lint`, `pnpm build`, `tsc --noEmit` on core and engine — green.
- U1: 20 tests in `workflow-lifecycle-traits.test.ts`, including the
fully-renamed-workflow case (fails if the resolver falls back to a
literal) and a shared-cache read-count assertion.
- U2: `legacy-tombstones.test.ts` green with the extended ratchet;
`board-workflows`, `merge-trait`, `workflow-graph-executor-parity`, and
move-hook suites green with no expectation edits.
- U3: 20 bus-invariant unit tests (isolation, ordering, the allowed-key
and required-key halves of the ids-only rule, lossiness) plus 3
end-to-end emitter-delivery tests; 5 outbox tests against a **real
PostgreSQL** work-item table (crash survival, rollback, at-least-once
redelivery on lease expiry, idempotent handler → one effect,
dropped-subscriber vs. durable work). A hand-written fake of the lease
predicate would only prove the fake redelivers.
**Not verified:** the `moveTaskInternalImpl` emit is confirmed on the
unconditional post-commit path by structure and by the surrounding
tests, but is *not* yet asserted end-to-end against a real store move on
both flag settings — that is Phase A2's fixture. The engine subscriber
registry ships empty by design, so no production subscriber exercises
the bus end-to-end yet. `settings-defaults.test.ts` has one pre-existing
failure on `main` (a logger-prefix mismatch in the
`mergeIntegrationWorktree=cwd-main` warning) — confirmed present on a
clean tree, unrelated to this branch.
🤖 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**
* Workflow lifecycle columns are now derived from workflow definitions,
supporting renamed and custom workflows.
* Added post-commit lifecycle events for task transitions, node entry,
and run suspend/resume with validated payloads.
* Follow-on processing for lifecycle emissions is now more robust
(rollback-safe, at-least-once delivery, idempotent handling).
* **Bug Fixes**
* Workflow board responses, task enrichment, and promotion no longer
depend on workflow-columns feature-flag gating.
* Subscriber failures no longer impact committed workflow transitions.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
|
||
|
|
8b039a543e |
fix(desktop): advance Pi runtime pin to 0.82.1 for packaging PR lane (#2465)
## Summary - Advance the matched Pi runtime pin (`pi-ai`, `pi-coding-agent`, `pi-agent-core`, `pi-tui`) from **0.82.0 → 0.82.1** so electron-builder's production-dependency walk accepts `pi-agent-core`'s `pi-ai@^0.82.1` requirement. - Fixes the Desktop packaging PR-lane failure: `Production dependency @earendil-works/pi-ai not found for package @earendil-works/pi-agent-core` (required `^0.82.1`). - Keep the workspace override guard; update pin-policy fixtures and CLI package-config expectations. - Tighten the advisory packaging step-order test so it asserts against the real `electron-builder --dir` step (not a missing release-only step name that previously passed via `indexOf === -1`). - Run `pnpm dedupe` so the packaging lane's lockfile dedupe early-warning is clean. ## Context #2439 pinned the full Pi closure at 0.82.0 and made recent main-based packaging runs green. This advances to the current upstream patch so deploy + electron-builder stay aligned with `pi-agent-core@0.82.1`'s declared dependency range. ## Test plan - [x] `node scripts/check-pi-versions-pinned.mjs` - [x] `node --test scripts/__tests__/check-pi-versions-pinned.test.mjs` - [x] `pnpm --filter @runfusion/fusion exec vitest run src/__tests__/package-config.test.ts` - [x] `pnpm --filter @fusion/desktop exec vitest run src/__tests__/release-workflow.test.ts` - [x] `pnpm dedupe --check` - [ ] GitHub: Desktop packaging (should run full packaging walk — lockfile/package.json touched) - [ ] GitHub: PR Checks (Lint, Typecheck, Build, Gate) |
||
|
|
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> |
||
|
|
0e3d2a2265 |
refactor: delete meta-task auto-archive and automated recovery follow-ups (#2461)
Deletes two pieces of automated "meta" machinery that filed and garbage-collected cards restating state already on the task that failed. Net **-1015 lines**. ## Why **Automated recovery follow-ups.** `createAutomatedFollowup` and its dedup engine (289 lines of signature matching, 1h recurrence rate-limiting, 24h supersedes windows) existed to file recovery cards for verification-cap and merge-conflict give-ups. In both cases the parent is *already* parked `failed` with a descriptive `error` and a log entry carrying the failing command, branch, and output — the card was a second copy of that. **Meta-task auto-archive.** The sweeps that garbage-collected those cards were worse than redundant: the regex classifier matched ordinary feature work, and its positional fallback bound cards to unrelated tasks, so **live work could be archived**. They are removed together, because the auto-archive sweeps only existed to clean up after the follow-up engine. ## What changed ### Deleted - `packages/engine/src/verification-followup-dedup.ts` in full — `createAutomatedFollowup`, `decideAutomatedFollowup`, `AutomatedFollowupKind`, `computeVerificationFailureSignature`, `extractFailingTestFiles`. - `findActiveRecoveryFollowUp` — dead code, defined and never called (`tsc` independently flagged it `6133 declared but its value is never read`). - The meta-task auto-archive sweeps `autoArchiveResolvedMetaTasks` / `autoArchiveStalledMetaTasks` and helpers `classifyMetaTask` / `resolveMetaTargetTaskId` / `computeMetaChainDepth` / `archiveMetaTask` / `evaluateMetaAutoArchiveGuards`, plus settings `metaTaskStallAutoCloseMs` and `metaTaskActiveExecutionGraceMs`. - Run-audit types `task:auto-archived-meta-resolved`, `task:auto-archived-meta-stalled`, `task:auto-archive-meta-resolved-skipped`, `task:auto-archive-meta-stalled-skipped`, `verification:followup-created`, `verification:followup-deduped`. The two signature helpers were **deleted rather than relocated** — once the three call sites went they were provably unreachable: `buildVerificationFailureSignature` had exactly one caller, and it was the only caller of `extractFailingTestFiles`. ### Call sites 1 and 2 — park kept, card dropped Verification-cap and merge-conflict give-ups keep their park, audit event, operator comment, and log entry. Site 1's `error` string was reworded off `"See follow-up task for investigation."` (no follow-up will exist) to carry the guidance itself. `autoResolveDisabled` was **kept** — it still drives the outer park guard and the `reason` string; only the inner branch that guarded card creation is gone. ### Call site 3 — autostash orphan, replaced not deleted This one is a genuine data-loss guard, so it keeps a durable trail. A `live`-classified orphan is a merger stash holding **real uncommitted work**, and unlike sites 1–2 there is no parked parent — the parent may already be `done` and merged, so nothing else on the board would ever mention the stash. The card is replaced by a `logEntry` **and** an `addTaskComment` on the parent, preserving every fact the old description carried: the sha, `record.label` (the handle `git stash` recovery needs), `record.detectedByTaskId`, and `sourcePhase`. New truthful run-audit event `task:autostash-orphan-live-detected` replaces the borrowed `verification:followup-*` name, with ids/outcomes-only metadata per AGENTS.md. ### Kept unchanged: the two real product features Eval follow-ups (`eval-followups.ts`) and PR-comment follow-ups (`pr-comment-handler.ts`) only borrowed the shared engine for its dedup pass. Both keep their exact behavior, column, priority, `sourceType`, and log lines, with dedup inlined as a `listTasks` scan on `suggestionId` / `prNumber` respectively. Both fail open (create) if the listing throws, matching the old engine. ## Test changes — read this one Two tests asserted the *deleted* engine's rate-limited `"[verification recurrence]"` logEntry. Those assertions were removed, **not loosened**: both tests still assert no duplicate card is created, and the eval test still asserts the existing id is reported back. No coverage of surviving behavior was weakened. The three `meta-*` test files were deleted along with the sweeps they covered. ## Verification ``` $ pnpm test:gate Test Files 2 passed (2) Tests 10 passed (10) # core Test Files 16 passed (16) Tests 299 passed (299) # engine-core Test Files 1 passed (1) Tests 70 passed (70) # ci-shape GATE_EXIT=0 $ pnpm --filter @fusion/engine --filter @fusion/core exec tsc --noEmit -p tsconfig.json TSC_EXIT=0 (no output) ``` Plus a file-scoped run over the touched surfaces (`eval-followups`, `pr-comment-handler`, `merger-autostash-orphan-surface`, `merger-autostash-cleanup`, `run-audit`, `run-audit-secret-taxonomy`, `project-engine`, `project-engine-manager`): **213/213 passed**. A repo-wide grep confirms no surviving references to any deleted symbol, module, or audit event. 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Bug Fixes** * Failed tasks now retain recovery and verification details directly on the original task instead of generating separate follow-up cards. * Live autostash issues now preserve stash information in task comments and activity logs. * Existing evaluation and pull-request follow-ups continue to be reused when appropriate. * **Changes** * Removed automatic archival of meta-tasks. * Removed obsolete meta-task timing settings. * **Documentation** * Updated architecture and settings documentation to reflect these workflow changes. <!-- end of auto-generated comment: release notes by coderabbit.ai --> Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
d19c82b33b |
test(dashboard): align modal and locale fixtures (#2460)
## Summary - query Task Detail modal assertions from the document after its FloatingWindow portal migration - sync secondary locale catalogs with the current English key structure ## Test plan - `pnpm --filter @fusion/dashboard exec vitest run app/components/__tests__/TaskDetailModal.attachments-and-tabs.test.tsx --silent=passed-only --reporter=dot` - `pnpm i18n:status` - `pnpm --filter @fusion/dashboard build` <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Documentation** * Added missing localization entries for workflow selection/help text, action-based report targeting, agent tool output limit hints, auto-update/restart messaging, and task refinement status labels across Spanish, French, Korean, Simplified Chinese, and Traditional Chinese. * **Tests** * Updated task detail modal tests to query the correct rendered document root so portal/modal content is asserted reliably. * Minor test formatting adjustments to keep assertions consistent without changing coverage. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
11887625f1 |
test(dashboard): align touch fixture with FloatingWindow (#2459)
## Summary - update the touch-resize browser fixture assertions for New Task's FloatingWindow migration - verify the shared nine touch targets, resize handle ID, and unified geometry persistence key ## Test plan - `corepack pnpm --filter @fusion/dashboard build` - `corepack pnpm --filter @fusion/dashboard exec vitest run --project dashboard-browser-touch --silent=passed-only --reporter=dot src/__tests__/task-modal-touch-resize-browser.test.ts` - `corepack pnpm check:changesets` <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Tests** * Updated tablet touch-resize coverage for task-detail to use the shared southeast resize handle and corrected expected hit-target counts. * Refreshed hit-target detection and drag target assertions after switching the test surface to the task-detail/floating window layout. * Updated persistence/geometry validations to compare the stored unified width/height against the resized panel dimensions. * Reworked desktop vs tablet assertions, including tighter overlay padding checks and standardized shared resize-handle sizing expectations. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
d6c917d726 |
feat(dashboard): add view and settings-section enumeration API (#2453)
## Summary Gives external integrations (command palettes, plugin launchers, alternate dashboard shells) a supported way to **discover the host UI** — instead of hardcoding the dashboard's view ids, labels and settings search terms and hand-syncing them on every release. This is the read-only metadata slice of the "constrained by a stable host context and API client" idea in `docs/proposals/2026-07-01-dashboard-theme-plugin-system.md`, and the follow-on to #2415 (theme tokens + overlay layering). Two additions, both inert unless called: | Endpoint | Returns | |---|---| | `GET /api/views` | Every registered built-in view id, in dashboard order — `id`, English `label`, plus optional i18n `labelKey`, legacy `aliases` and `internal` flag. | | `GET /api/settings/sections` | Selectable Settings sections — `id`, `label`, `labelKey`, `scope`, `group`, `keywords`, `searchableKeys`, `advanced`. | Both are read-only, return static project-independent metadata, take no project id, and are mounted inside `createApiRoutes` so they sit behind exactly the same `/api` authentication as every other dashboard route — no more, no less. ## What actually changed — one source of truth The endpoints are the small part. The core of the diff is **collapsing duplicated UI metadata into two shared registries that now drive both the dashboard UI and the API**: - `packages/dashboard/src/shared/dashboard-views.ts` — canonical view ids + English labels + i18n keys + legacy aliases. - `packages/dashboard/src/shared/settings-sections.ts` — canonical settings sections + scope/group/search metadata, with `group` and `advanced` derived from the list's own structure. `LeftSidebarNav`, `SettingsModal` and `useViewState` were rewritten to consume those registries instead of carrying their own copies (net **−230 lines** in `SettingsModal` alone). Edit the registry and the rendered UI and the API move together. ## Drift protection Being precise about what each test can and cannot catch, because "no-drift" claims are easy to overstate: - `left-sidebar-nav-registry-parity.test.tsx` — the one test that catches drift the registry does not already determine. It **renders** the sidebar with a recording `t()` spy and pins each entry's translation key and English fallback to the registry (the sidebar still hardcodes its keys). It also asserts the rendered destination count equals the enrolled id list, so a newly added sidebar view fails until it is enrolled. - `ui-metadata-sync.test.ts` — pins the Settings navigation list, advanced-visibility set, persisted view list, reset-key registry and both endpoint payloads to the registries. Since those consumers are now *derived* from the registries, these assertions mainly guard against a future consumer **re-hardcoding** its own copy. Two of them do stand on their own: each section's served `group` is pinned to the group header it actually renders under, and no published `labelKey` may resolve to a non-leaf i18n node. - `register-ui-metadata-routes.test.ts` — drives the real Express router and asserts each endpoint serves the registry payload verbatim, with no filtering or reshaping. - Exactly two **existing** tests are updated, both for the same reason: they asserted that `SettingsModal.tsx`'s *source text* contains a section literal that now lives in the registry. `VoiceInputSection.modal-visibility.test.tsx` now asserts Voice Input's Basic-mode contract against `SETTINGS_SECTION_METADATA`, and `mcp-documentation.test.ts` reads the registry for the two MCP section ids. No other existing test in the package changes. ## Design notes / decisions for review - **`GET /api/views` returns the full registry, not the live menu.** It includes flag-gated / experimental ids and `internal` (non-navigable) destinations; reachability depends on flags and plugins this endpoint does not evaluate. Documented as "known view ids", not "visible nav entries". - **`labelKey` is optional and best-effort; `label` is the guarantee.** A `labelKey` is published only where the dashboard itself renders that view's title through it. `graph` (labelled from a plugin manifest) and the internal `task-detail` carry none rather than advertise a key that resolves to nothing — and `task-detail` in particular must not point at `taskDetail.title`, which is an occupied i18n *namespace* whose lookup returns an object rather than falling through to a default. A guard test now enforces that. Separately, a few published keys (`nav.ideation`, `nav.importTasks`, `nav.automations`, `pr.view.title`) are the dashboard's real keys but aren't in the shipped catalogs yet because the host supplies their English inline; the docs say plainly that consumers must fall back to `label`. - **`keywords` / `searchableKeys` are explicitly non-contractual.** `searchableKeys` exposes the raw i18n translation-key strings backing a section's searchable copy; values, ordering and presence may change between releases. Documented as best-effort search hints, never stable identifiers. - **Migration is deliberately partial.** The desktop sidebar, Settings navigation and persisted view list now come from the registries; `Header.tsx` and the mobile More sheet still hardcode a few of the same labels. They can still drift from what `GET /api/views` reports; converting them is left to a follow-up so this diff stays reviewable. - **No project scoping, deliberately.** The proposal doc rightly pushes plugin traffic through a project-scoped client — these two endpoints are the exception that proves the rule: they return static registry metadata that is identical for every project, so threading a `projectId` would imply a scoping guarantee that does not exist here. They never touch `getScopedStore` / `TaskStore`. - **Two endpoints rather than one `/api/ui-metadata` envelope.** Views and Settings sections are independent registries with different consumers, and `/settings/sections` sits naturally beside the existing `/settings/*` routes. A consumer that only needs navigation doesn't pay for settings metadata. - **The registry extraction ships with the endpoints rather than as a separate PR.** The registries *are* the mechanism that keeps the API honest — split apart, the first half is a refactor with no observable effect and the second can't land without it. - **Placement:** `packages/dashboard/src/shared/` is a new directory, and these are the first *production* `app/ → src/` imports in the package (today the only one is in `ProviderIcon.test.tsx`). They sit under `src/` because `src/`'s tsconfig cannot import `app/`, so a module both sides consume has nowhere else to go; both registries are dependency-free data leaves, and `vite build` plus `check-no-node-only-core-imports-in-dashboard` confirm the client bundle is unaffected. The considered alternative was `packages/core/src` behind the `dashboard-browser-safe-core-modules.json` allowlist, where `mobile-nav-primary-items.ts` keeps a destination→labelKey table — these stayed out of `core` because they are dashboard-owned UI ids, and because the two tables describe different surfaces (core mirrors the mobile nav's `nav.skills`/`nav.settings`; this registry mirrors the desktop sidebar's `header.skillsView`/`header.settings`). - Ships a `@runfusion/fusion` **minor** changeset (`category: feature`). Happy to adjust any of the above — shape, placement, or dropping `searchableKeys` — if you'd rather it landed differently. ## Verification - Rebased onto `main@26dcccb7c`. Two conflicts, both resolved by absorbing upstream's work rather than reverting it: - `SettingsModal.tsx` — upstream's `voice-input` section (and the `FNXC:VoiceInput` decision comment explaining it stays out of the advanced-only set) moved into the registry. The registry's section list is byte-identical to `main`'s `SETTINGS_SECTIONS` (45/45 entries, all fields), and the registry-derived `ADVANCED_SETTINGS_SECTION_IDS` is identical to `main`'s hardcoded set (19/19, same order) — both verified mechanically, not by eye. Upstream's `RUNTIME_*` hide-uninstalled-runtimes sets are untouched. - `routes/README.md` — the `mount-sequence` list regenerated from `CREATE_API_ROUTES_REGISTRAR_MOUNT_SEQUENCE`, so `registerVoiceRoutes` and `registerUiMetadataRoutes` are both in place and the contract test passes. - `DASHBOARD_VIEWS` covers exactly `main`'s `BuiltInTaskView` union, aliases included, and `BUILT_IN_TASK_VIEWS` reproduces `main`'s 27-entry array in order (`devserver` still preceding `dev-server` for the migration path). - Every one of the 20 sidebar labels the refactor rewrote was checked to be byte-identical to `main`'s hardcoded fallback, and every `FNXC:` decision comment displaced by the move was accounted for — all 75 in `SettingsModal.tsx` and all 11 in `useViewState.ts` survive, relocated onto the registry entries they document. - The full `dashboard-app` + `dashboard-api` suites were run at this commit (**20,706 passing**) and again on unmodified `main@26dcccb7c`, and the failing-file sets compared: **every file that fails here also fails on `main`** — nothing regresses. The overlap is environment-driven (Postgres-backed `*.pg.test.ts`, tests needing built `dist` artifacts, and `SettingsModalNodeRouting.test.tsx`'s `No "fetchSystemInfo" export is defined on the "../../api" mock`), none of it touched by this change. - `tsc --noEmit` clean for both dashboard projects, `eslint` clean on every changed file, and `vite build` of the client bundle succeeds (the two pre-existing `@fusion-plugin-examples/claude-runtime` / `playwright-core` module-resolution errors reproduce on unmodified `main`). - Repo gate scripts pass: `check-changeset-format`, `check-routes-modular`, `check-no-node-only-core-imports-in-dashboard`, `check-no-cwd-relative-dashboard-test-reads`, `check-mock-completeness`. - The three new assertions were mutation-tested rather than assumed load-bearing: breaking the registry's `group` derivation, dropping an enrolled sidebar id, and re-pointing `task-detail` at the `taskDetail.title` namespace each make their test fail. - Local CodeRabbit review over two passes: 3 minor findings, all addressed (parity projection missing `group`; route tests asserting partial instead of exact payloads; the `labelKey` guard not covering the settings registry). <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added authenticated, read-only APIs for discovering dashboard views and selectable Settings sections. * Added dashboard view metadata, including labels, aliases, internal status, and translation keys. * Added Settings metadata with grouping, scope, advanced status, and search-related information. * Updated navigation and Settings UI labels to use shared metadata. * **Documentation** * Documented the new metadata endpoints and integration guidance. * **Bug Fixes** * Added safeguards and automated checks to keep UI navigation and API metadata synchronized. <!-- end of auto-generated comment: release notes by coderabbit.ai --> Co-authored-by: Claude <noreply@anthropic.com> |
||
|
|
3f33cb000f |
feat: per-origin workflow selection + feedback-derived refinement titles
Two task origins had no workflow picker in front of the operator and always inherited the project default: `fn task create` (CLI + the `fn_task_create` agent tool) and refinement tasks. Add a Project General setting for each, where blank/unset means "Selected workflow" (the operator's current Board lane, falling back to the project default) and a concrete id pins that origin. Because the Board lane lives in browser localStorage, non-browser callers could not resolve "Selected workflow" at all. `boardSelectedWorkflowId` mirrors the lane into project settings so they can. Note this makes the mirrored lane project-scoped: two operators on one project share it, last switch wins. The Board never reads it back, so the only effect is which workflow a newly created task inherits. Resolution is `TaskStore.resolveOriginWorkflowOverrideId(origin)`: pinned setting -> mirrored lane -> `undefined` to inherit each caller's existing default-workflow path unchanged. A deleted or fragment id degrades to inherit rather than throwing, so a stale settings value can never break task creation. An explicit `workflow_id` argument to `fn_task_create` still wins. Separately, a refinement is now titled by the operator's own feedback via the shared `deriveFallbackTaskTitle`, not `Refinement: <parent title>`. Ten refinements of one task previously rendered ten identical titles, so the board could not tell them apart while the text saying what each one asked for sat in the description. Provenance moves to a `Refines <id>` card chip alongside the existing detail-view parent link and dependency edge. Verified: merge gate (299 tests), lint, full build, and typecheck for core, CLI, and dashboard all pass. New coverage: origin resolution across both origins and the full precedence ladder, the two settings pickers, the board-lane mirror, refinement titling (including sibling distinctness), and the card chip. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
e4cb957b57 |
FN-8619: migrate modal geometry to FloatingWindow
Unify migrated modal geometry and responsive behavior under FloatingWindow. - Cover Agent Detail and GitHub Import floating-window interaction and persistence contracts. - Preserve unique Agent Detail labels, mouse-only backdrop dismissal, and tablet touch layouts. - Update the dashboard modal migration inventory. Files changed: docs/dashboard-modal-inventory.md | 6 +- .../dashboard/app/components/AgentDetailView.css | 49 +++++----- .../dashboard/app/components/AgentDetailView.tsx | 6 +- .../dashboard/app/components/FloatingWindow.css | 10 +- .../dashboard/app/components/TaskDetailModal.css | 15 +++ .../AgentDetailView.floating-window.test.tsx | 102 +++++++++++++++++++++ .../__tests__/GitHubImportModal.test.tsx | 58 ++++++++++++ ...etailModal.responsive-and-dependencies.test.tsx | 24 +++++ 8 files changed, 241 insertions(+), 29 deletions(-) Fusion-Task-Id: FN-8619 Fusion-Task-Lineage: efa95e41-b12e-4e5d-aeb2-65b43c2dd8e0 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
43160a7aae |
FN-8621: migrate complex modals to FloatingWindow
Unify complex dashboard modal presentation under persisted FloatingWindow geometry. - Migrate Create Room, Task Detail, Agent Detail, and GitHub Import modal presentations. - Preserve documented embedded and docked exceptions, dismissal behavior, and nested scrolling. - Add presentation-contract coverage and publish dashboard guidance and changesets. Files changed: ...n-8619-resize-persist-modals-floating-window.md | 7 ++ .changeset/fn-8621-create-room-floating-window.md | 7 ++ docs/dashboard-guide.md | 16 +++- docs/dashboard-modal-inventory.md | 12 +-- .../dashboard/app/components/AgentDetailView.css | 16 +--- .../dashboard/app/components/AgentDetailView.tsx | 102 ++++++++++++++++----- .../dashboard/app/components/CreateRoomModal.css | 19 +++- .../dashboard/app/components/CreateRoomModal.tsx | 57 ++++++++---- .../dashboard/app/components/FloatingWindow.css | 13 ++- .../dashboard/app/components/FloatingWindow.tsx | 15 +++ .../dashboard/app/components/GitHubImportModal.css | 11 +-- .../dashboard/app/components/GitHubImportModal.tsx | 40 ++++++-- .../dashboard/app/components/TaskDetailModal.css | 52 +---------- .../dashboard/app/components/TaskDetailModal.tsx | 58 ++++++------ .../__tests__/AgentDetailView.core.test.tsx | 2 +- .../AgentDetailView.mobile-scroll.test.tsx | 6 +- .../components/__tests__/CreateRoomModal.test.tsx | 62 +++++++++++-- .../components/__tests__/FloatingWindow.test.tsx | 1 + .../__tests__/GitHubImportModal.test.tsx | 8 +- ...etailModal.responsive-and-dependencies.test.tsx | 77 +++++++--------- .../__tests__/modal-presentation-contract.test.tsx | 74 +++++++++++++++ .../dashboard/app/hooks/useEmbeddedPresentation.ts | 2 +- .../dashboard/app/hooks/useModalResizePersist.ts | 5 + 23 files changed, 441 insertions(+), 221 deletions(-) Fusion-Task-Id: FN-8621 Fusion-Task-Lineage: 04b6f3fe-d527-4a21-a0cb-489eb20f5e91 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
7715a83e6f |
FN-8624: fix task detail activity right padding
Keep Task Detail activity insets symmetric while reserving overlay clearance only for first-row content. - Move Live/Feed overlay clearance from the activity container to first-row elements. - Preserve responsive overlay spacing and the full-width Interventions timeline. - Cover modal, pop-out, embedded, and responsive activity layouts with updated tests. - Add a patch changeset for the Task Detail padding fix. Files changed: .changeset/fn-8624-task-detail-inset.md | 7 ++++ .../dashboard/app/components/TaskDetailModal.css | 41 ++++++++++------------ .../TaskDetailModal.attachments-and-tabs.test.tsx | 6 ++-- .../__tests__/TaskDetailModal.css.test.ts | 21 ++++++----- ...etailModal.responsive-and-dependencies.test.tsx | 25 +++++++------ 5 files changed, 54 insertions(+), 46 deletions(-) Fusion-Task-Id: FN-8624 Fusion-Task-Lineage: 578cfe86-3021-421d-841b-2670efc10a5d Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
d3c1955866 |
FN-8625: remove heartbeat controls from agent org charts
Keep org charts read-only for heartbeat configuration while preserving controls on board and list surfaces. - Remove heartbeat toggle props, markup, and styling from org chart nodes - Cover both org chart layouts and board heartbeat availability with regression tests - Add a patch changeset for the dashboard behavior fix Files changed: .../fn-8625-remove-org-chart-heartbeat-toggle.md | 7 +++++++ packages/dashboard/app/components/AgentsView.css | 7 ------- packages/dashboard/app/components/AgentsView.tsx | 14 +++++--------- .../__tests__/AgentsView.orgchart.test.tsx | 20 ++++++++++++++++++++ .../app/components/__tests__/AgentsView.test.tsx | 20 +++++++++++--------- 5 files changed, 43 insertions(+), 25 deletions(-) Fusion-Task-Id: FN-8625 Fusion-Task-Lineage: 454c05e6-fe31-486b-975a-80ba5e8c518c Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
89b4621a93 |
FN-8607: migrate shared modals to FloatingWindow
Standardize agent, onboarding, and utility modal behavior on the shared FloatingWindow contract. - Migrate modal hosts to shared geometry, sheet, dismissal, and focus behavior. - Make FloatingWindow the sole owner of modal ARIA semantics and protect portal-safe dismissal surfaces. - Cover migrated modal contracts and document the responsive sheet requirements. Files changed: docs/dashboard-guide.md | 6 +- packages/dashboard/app/components/AgentGenerationModal.tsx | 8 +- packages/dashboard/app/components/AgentImportModal.tsx | 4 +- packages/dashboard/app/components/AgentListModal.tsx | 4 +- packages/dashboard/app/components/AgentOnboardingModal.tsx | 4 +- packages/dashboard/app/components/DockerNodeOnboardingModal.tsx | 4 +- packages/dashboard/app/components/ExperimentalAgentOnboardingModal.tsx | 4 +- packages/dashboard/app/components/FloatingWindow.css | 36 ++++-- packages/dashboard/app/components/FloatingWindow.tsx | 12 ++ packages/dashboard/app/components/MilestoneSliceInterviewModal.tsx | 2 +- packages/dashboard/app/components/NativeShellOnboardingModal.tsx | 2 +- packages/dashboard/app/components/SetupWizardModal.tsx | 3 +- packages/dashboard/app/components/SubtaskBreakdownModal.tsx | 2 +- packages/dashboard/app/components/__tests__/AgentModals.floatingWindow.test.tsx | 134 ++++++++++++++++++--- packages/dashboard/app/components/__tests__/OnboardingModals.floatingWindow.test.tsx | 128 +++++++++++++++++--- packages/dashboard/app/components/__tests__/UtilityModals.floatingWindow.test.tsx | 124 +++++++++++++++++-- packages/dashboard/app/components/__tests__/migratedModalFixtures.tsx | 59 ++++++--- packages/dashboard/app/components/__tests__/modalFloatingWindowContract.test.tsx | 20 ++- 18 files changed, 458 insertions(+), 98 deletions(-) Fusion-Task-Id: FN-8607 Fusion-Task-Lineage: 11ba6a9c-ce89-4358-83c4-a15ef15b6128 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
bdfa5186c4 |
FN-8622: capture Chromium CDP modal screenshots
Add assertion-guarded CDP evidence for migrated Agent List and Setup Wizard modal touch geometry. - Mount production modal surfaces in the resize browser fixture - Verify tablet resize and drag behavior plus true-phone sheet behavior - Store the captured phone and tablet screenshots as regression evidence - Extend mobile sheet styling to the migrated modal hosts Files changed: .../dashboard/app/components/FloatingWindow.css | 8 ++ .../app/task-modal-touch-resize-e2e-fixture.tsx | 17 +++- .../fn-8607/phone-fullscreen-sheet.png | Bin 0 -> 17823 bytes .../fn-8607/tablet-agent-list-after.png | Bin 0 -> 21489 bytes .../fn-8607/tablet-setup-wizard-after.png | Bin 0 -> 81964 bytes .../task-modal-touch-resize-browser.test.ts | 99 +++++++++++++++++++++ 6 files changed, 120 insertions(+), 4 deletions(-) Fusion-Task-Id: FN-8622 Fusion-Task-Lineage: 3fabd950-bac0-47f4-9649-08709db31e02 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
ba24a530ef |
FN-8620: migrate dashboard modals to FloatingWindow
Standardize dashboard modal geometry and interaction behavior through the shared FloatingWindow contract. - Migrate New Task, right-dock expansion, terminal, onboarding, and utility modals to FloatingWindow. - Expand shared window resizing, positioning, accessibility, and contract coverage. - Document the modal migration inventory and publish changesets. Files changed: .changeset/fn-8607-floating-modal-contract.md | 7 + .changeset/fn-8620-bespoke-geometry-modals.md | 7 + docs/dashboard-guide.md | 47 ++ docs/dashboard-modal-inventory.md | 6 +- .../app/components/AgentGenerationModal.tsx | 14 +- .../dashboard/app/components/AgentImportModal.tsx | 7 +- .../dashboard/app/components/AgentListModal.tsx | 8 +- .../app/components/AgentOnboardingModal.tsx | 6 +- .../app/components/DockerNodeOnboardingModal.tsx | 14 +- .../ExperimentalAgentOnboardingModal.tsx | 6 +- .../dashboard/app/components/FloatingWindow.css | 21 + .../dashboard/app/components/FloatingWindow.tsx | 62 ++- packages/dashboard/app/components/MailboxModal.tsx | 14 +- .../components/MilestoneSliceInterviewModal.tsx | 12 +- .../app/components/NativeShellOnboardingModal.tsx | 6 +- packages/dashboard/app/components/NewTaskModal.css | 196 +------ packages/dashboard/app/components/NewTaskModal.tsx | 593 +++++++-------------- packages/dashboard/app/components/RightDock.css | 84 +-- .../app/components/RightDockExpandModal.tsx | 333 ++---------- .../dashboard/app/components/SetupWizardModal.tsx | 20 +- .../app/components/SubtaskBreakdownModal.tsx | 6 +- .../dashboard/app/components/TerminalModal.css | 120 +---- .../dashboard/app/components/TerminalModal.tsx | 321 ++--------- .../__tests__/AgentModals.floatingWindow.test.tsx | 26 + .../components/__tests__/FloatingWindow.test.tsx | 27 +- .../app/components/__tests__/NewTaskModal.test.tsx | 66 +-- .../OnboardingModals.floatingWindow.test.tsx | 20 + .../app/components/__tests__/RightDock.test.tsx | 18 +- .../components/__tests__/TerminalModal.test.tsx | 61 +-- .../UtilityModals.floatingWindow.test.tsx | 18 + .../components/__tests__/migratedModalFixtures.tsx | 25 + .../__tests__/modalFloatingWindowContract.test.tsx | 30 ++ 32 files changed, 726 insertions(+), 1475 deletions(-) Fusion-Task-Id: FN-8620 Fusion-Task-Lineage: 2a912260-3292-4b13-bcf0-9de5a3df8ccd Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
256c64a7bd |
chore(release): v0.74.0-beta.5
Version bump via changesets. |
||
|
|
034827f251 |
FN-8623: restore CDP touch geometry test lane
Restore a dedicated Chromium CDP lane for dashboard touch-geometry coverage. - Add an opt-in touch-geometry test command and isolated Vitest project. - Keep the browser-dependent spec out of deep and quality backfill collection. - Document browser discovery, port, and single-collection requirements. Files changed: docs/testing.md | 10 ++- packages/dashboard/package.json | 1 + .../__tests__/dashboard-test-config-guard.test.ts | 71 +++++++++++++++++++++- .../task-modal-touch-resize-browser.test.ts | 5 ++ packages/dashboard/vitest.config.ts | 28 ++++++++- scripts/lib/test-inventory-spec.json | 3 +- 6 files changed, 114 insertions(+), 4 deletions(-) Fusion-Task-Id: FN-8623 Fusion-Task-Lineage: eafd7497-9302-49a4-8e9e-aa93c9f56a6f Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
73b2a32e2b |
test: backfill dashboard test mocks for new api and viewport exports
Concurrent dashboard work added runtime exports that hardcoded module mocks did not expose, so any suite rendering the affected component threw "No <export> is defined on the mock". Adds the missing `../api` exports (system-info probe, update install/restart, cloudflared, provider key and login helpers, git remotes/branches) and the `isTabletTouchViewport` viewport helper across the 26 suites that mock those modules. Verified: all 26 files pass (1012 tests). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
beebd270bd |
fix: make Queued to plan / Ready badges agree with the planning lane
TaskCard inferred "unplanned" from steps.length === 0 while triage's todo-discovery and the scheduler's dispatch filter both decide from PROMPT.md seed-ness, so the badges disagreed with the engine in both directions: a real spec that parsed to zero steps read as "Queued to plan" while the scheduler already treated it as a WIP-slot candidate, and a re-seeded card still carrying old steps read as "Ready" while triage was about to plan it. Either way the badge sent operators to the wrong cap. Adds the shared isTaskAwaitingPlanning predicate (replan park, missing spec, seed-vs-real content) used by both triage's discovery and a new best-effort `awaitingPlanning` enrichment on GET /api/tasks. TaskCard derives both badges from that one value — strict complements — and keeps the step count only as a fallback for SSE payloads and older servers. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
01b27b13f1 |
test(dashboard): add per-character typing coverage to uncovered field surfaces
Sweeping the other views for the FN-8606 typing bug found no further breakage, but it did expose why the bug shipped: almost all field coverage uses fireEvent.change, which sets a value in one shot on a node it already holds and never needs the input to stay mounted. A remount is invisible to it. Adds expectStableTyping (types character by character via userEvent, then asserts DOM node identity, accumulated value, and retained focus) and applies it to the uncovered surfaces: the FN-8606-migrated AddNode, ConnectNode, NodeDetail, Scripts, and WorkflowAddStep modals, plus the board's QuickEntryBox composer and SubtaskBreakdownModal title editing. All pass — this is a detection floor, not a fix. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
d193dcdbb0 |
chore(lint): ban React components declared inside another component
Adds fusion-react/no-nested-component-definitions, a custom rule in the house style of the existing detached-spawn guard. A component declared in render is a new element type every render, so React remounts its subtree on each parent update and destroys focus, scroll, and local state. This pattern shipped three times without review or tests catching it: FN-8606's ModalShell left Planning Mode and Settings untypable, and MailboxModal's ReplyContextExpandable collapsed expanded reply rows. Tests missed it because fireEvent.change sets a value without needing the node to stay mounted. The rule reports PascalCase functions (including memo()/forwardRef()-wrapped) that return JSX and are declared inside another JSX-returning function. Lowercase render helpers are deliberately allowed — they are the sanctioned fix. Escape hatch: // nested-component-allowlist: <reason>. Scoped to production .tsx, with a vitest guard for the rule itself. Hoists the two pre-existing violations (ProviderStatusBadge, GitHubStatusBadge in ModelOnboardingModal) to module scope so the rule lands clean at "error". Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
c21fb0d1d0 |
fix(dashboard): stop Mailbox reply-context rows collapsing on unrelated re-renders
ReplyContextExpandable was declared inside MailboxModal's render, making it a new element type on every render. Any parent update remounted the whole recursive reply thread, so expanding one reply row collapsed the others and discarded their DOM identity, focus, and scroll position. Hoist it to module scope and pass the parent's reply state and handlers through an explicit env prop so the element type stays stable. Adds a regression test that expands one row, expands a second, and asserts the first keeps both its node identity and aria-expanded state — same defect class as the FN-8606 ModalShell fix. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
7065d03895 |
fix(dashboard): keep Planning Mode and Settings typable after floating-window migration
FN-8606 declared the window shell as a component inside PlanningModeModal and SettingsModal render. A component declared in render is a new element type on every render, so React remounted the whole subtree on each keystroke, destroying the focused input: Planning Mode dropped everything after the first character and Settings text fields did the same. Replace ModalShell with a plain renderModalShell(children) call so the returned element types stay stable, and add a Planning regression test that types per-character across both the modal and embedded surfaces (fireEvent.change cannot observe this class of bug, which is why it shipped). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
0022621d22 |
chore(release): v0.74.0-beta.4
Version bump via changesets. |
||
|
|
2bb8537352 |
FN-8616: make agent tool-output limits configurable
Expose the shared agent tool-output budget as a scoped operator setting with an explicit no-limit option. - Resolve global and project output caps with a safe finite default and zero sentinel. - Propagate configured budgets through PI and plugin runtime tool wrappers. - Add settings controls, localized labels, documentation, and regression coverage. Files changed: .changeset/fn-8616-tool-output-budget-setting.md | 7 ++++ docs/agents.md | 4 +- docs/settings-reference.md | 1 + .../core/src/__tests__/tool-output-budget.test.ts | 23 ++++++++--- packages/core/src/index.gate.ts | 2 + packages/core/src/index.ts | 2 + packages/core/src/settings-schema.ts | 12 ++++++ packages/core/src/tool-output-budget.ts | 31 +++++++++++++-- packages/core/src/types/settings-scope.ts | 8 ++++ .../app/components/settings/save-split.ts | 1 + .../sections/GlobalGeneralSection.search.ts | 20 ++++++++++ .../settings/sections/GlobalGeneralSection.tsx | 26 ++++++++++++ ...lobalGeneralSection.tool-output-budget.test.tsx | 46 ++++++++++++++++++++++ .../settings-default-descriptions.test.tsx | 1 + .../src/__tests__/agent-session-helpers.test.ts | 20 ++++++++++ .../src/__tests__/runtime-resolution.test.ts | 15 +++++++ .../__tests__/tool-output-budget-wrapper.test.ts | 45 ++++++++++++++++----- packages/engine/src/agent-runtime.ts | 2 + packages/engine/src/agent-session-helpers.ts | 18 +++++++-- packages/engine/src/pi.ts | 29 ++++++++++---- packages/engine/src/runtime-resolution.ts | 10 ++++- packages/i18n/locales/en/app.json | 4 ++ packages/i18n/locales/es/app.json | 6 ++- packages/i18n/locales/fr/app.json | 6 ++- packages/i18n/locales/ko/app.json | 6 ++- packages/i18n/locales/zh-CN/app.json | 6 ++- packages/i18n/locales/zh-TW/app.json | 6 ++- packages/i18n/src/resources.d.ts | 4 ++ 28 files changed, 323 insertions(+), 38 deletions(-) Fusion-Task-Id: FN-8616 Fusion-Task-Lineage: 3ca99a61-d6ae-48ff-98d2-f14a153aa2b7 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
743dc5f46e |
FN-8612: remove tablet task modal padding
Keep task modals dense on tablets without reducing their touch resize targets. - Move the Task Detail drag target out of layout flow while preserving its 44px hit area. - Restore desktop-density New Task header and body padding on tablet resize surfaces. - Add CSS, unit, and browser coverage for tablet geometry and generic floating windows. Files changed: .changeset/fn-8612-tablet-modal-padding.md | 7 + docs/dashboard-guide.md | 8 +- .../task-detail-modal-tablet-width.test.ts | 8 + .../dashboard/app/components/FloatingWindow.css | 40 ++++- packages/dashboard/app/components/NewTaskModal.css | 14 ++ .../FloatingWindow.touch-geometry.test.tsx | 14 ++ .../app/components/__tests__/NewTaskModal.test.tsx | 8 + .../app/task-modal-touch-resize-e2e-fixture.tsx | 54 ++++++- .../task-modal-touch-resize-browser.test.ts | 177 ++++++++++++++++++++- packages/dashboard/vitest.config.ts | 6 + 10 files changed, 322 insertions(+), 14 deletions(-) Fusion-Task-Id: FN-8612 Fusion-Task-Lineage: fecd7c48-7b6c-434e-9002-f8f21241120c Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
65f6748e5b |
FN-8613: theme mailbox markdown links
Theme mailbox message anchors with dashboard tokens and verify link coverage. - Style markdown anchor default, visited, hover, and focus-visible states with design tokens - Cover inline, autolink, list, and table links while preserving file-path controls - Add a patch changeset for themed mailbox links Files changed: .changeset/theme-mailbox-markdown-links.md | 7 ++++ packages/dashboard/app/components/MailboxModal.css | 29 +++++++++++++ .../__tests__/MailboxMessageContent.test.tsx | 49 ++++++++++++++++++---- 3 files changed, 77 insertions(+), 8 deletions(-) Fusion-Task-Id: FN-8613 Fusion-Task-Lineage: a8c7e8bb-8238-4018-bf95-38193f908eb4 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
a6885b73f2 |
FN-8606: migrate core workflow modals to shared floating windows
Migrate dashboard dialogs to the shared movable and resizable FloatingWindow contract. - Move core, workflow, Git, planning, and automation modal surfaces to stable floating-window identities with persisted geometry. - Suspend geometry and floating controls for phone and short-viewport sheets, including Quick Chat. - Add accessibility wiring, tablet touch targets, migration tests, and operator documentation. Files changed: .changeset/fn-8606-floating-window-core-modals.md | 7 + docs/dashboard-guide.md | 4 + packages/dashboard/app/App.tsx | 8 +- .../dashboard/app/components/ActivityLogModal.tsx | 30 ++-- packages/dashboard/app/components/AddNodeModal.tsx | 8 +- .../dashboard/app/components/ChangesDiffModal.tsx | 35 +++-- .../dashboard/app/components/ConnectNodeModal.tsx | 9 +- .../dashboard/app/components/FloatingWindow.css | 71 ++++++++- .../dashboard/app/components/FloatingWindow.tsx | 27 +++- .../dashboard/app/components/GitManagerModal.tsx | 30 +++- .../dashboard/app/components/GroupTaskModal.tsx | 8 +- .../app/components/ModelOnboardingModal.tsx | 28 ++-- .../dashboard/app/components/NodeDetailModal.tsx | 9 +- .../dashboard/app/components/PlanningModeModal.css | 1 - .../dashboard/app/components/PlanningModeModal.tsx | 57 +++---- .../app/components/ScheduledTasksModal.tsx | 9 +- packages/dashboard/app/components/ScriptsModal.css | 1 - packages/dashboard/app/components/ScriptsModal.tsx | 32 ++-- .../dashboard/app/components/SettingsModal.css | 1 - .../dashboard/app/components/SettingsModal.tsx | 58 ++++--- .../app/components/WorkflowAddStepModal.css | 15 -- .../app/components/WorkflowAddStepModal.tsx | 46 +++--- .../components/__tests__/ActivityLogModal.test.tsx | 33 ++-- .../app/components/__tests__/AddNodeModal.test.tsx | 12 +- .../components/__tests__/ChangesDiffModal.test.tsx | 78 +++++----- .../components/__tests__/ConnectNodeModal.test.tsx | 7 + .../components/__tests__/FloatingWindow.test.tsx | 173 ++++++++++++++++++++- .../components/__tests__/GitManagerModal.test.tsx | 30 ++-- .../components/__tests__/GroupTaskModal.test.tsx | 9 ++ .../__tests__/ModelOnboardingModal.test.tsx | 18 ++- .../components/__tests__/NodeDetailModal.test.tsx | 9 ++ .../__tests__/PlanningModeModal.autosize.test.tsx | 30 ++-- .../__tests__/ScheduledTasksModal.test.tsx | 21 ++- .../app/components/__tests__/ScriptsModal.test.tsx | 15 +- .../__tests__/SettingsModal.mobileClose.test.tsx | 25 ++- .../__tests__/WorkflowAddStepModal.test.tsx | 7 + .../floatingWindowMigration.test-helpers.ts | 126 +++++++++++++++ 37 files changed, 828 insertions(+), 259 deletions(-) Fusion-Task-Id: FN-8606 Fusion-Task-Lineage: dab0df2d-73f4-4b0f-bec3-a45016310c91 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
93a403af67 |
fix(dashboard): import delete-attribution constants via browser-safe subpath
The client bundle aliases `@fusion/core` to the leaf `core/src/types.ts` to keep Node-only dependencies out of the browser, so a package-root import of `FUSION_CLIENT_HEADER`/`FUSION_DASHBOARD_UI_CLIENT` typechecked but failed `vite build`: "FUSION_CLIENT_HEADER" is not exported by "../core/src/types.ts" Follow the documented pattern instead of widening the root alias: declare a `./task-delete-attribution` subpath export, add the matching Vite alias ahead of the broader `@fusion/core` key (Vite matches in order), register the module in the browser-safe-core allowlist, and import the subpath from the client. `task-delete-attribution.ts` has no imports at all, so it is a safe leaf. `app/utils/detectContentLanguage.ts` already warned about exactly this trap; the miss was mine for verifying with typecheck, lint and test:gate but not `pnpm build`, which is one of the four checks CI blocks on. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
ab87d0d803 |
fix(api): return 404 for missing tasks, and make task deletions attributable
Three related fixes, all originating from a `[api:error] Request failed` log line showing a 500 on `GET /api/tasks/FN-8610/runtime-fallback`. 1. Missing/deleted tasks now return 404 instead of 500. `getTaskImpl` signalled a miss with a bare `Error`, and route catches only mapped errno `ENOENT` to 404 — a leftover from the file-backed storage era. In Postgres mode nothing sets an errno code, so every unknown/missing/soft-deleted/wrong-project read returned 500. Adds a typed `TaskNotFoundError` (message byte-identical) plus a shared `task-lookup-error` mapper applied across the task, session-diff, git/GitHub, workflow and file-workspace route registrars. The same bare throw existed on both archive-lifecycle delete paths, so `DELETE /tasks/:id` was affected too. 2. 5xx logs now carry the origin stack. `rethrowAsApiError` constructed a fresh `ApiError` from the message and discarded the original, so the `FNXC:ApiErrorDiagnostics` contract logged the rethrow site rather than the throw site — the reported log entry had no stack at all. Threads `cause` through the error factories and walks the chain (bounded, cycle-guarded). 3. Task deletions are attributable, and non-operator deletes notify. `task:deleted` audit rows recorded `agentId: "system"` for every HTTP delete, making an operator click indistinguishable from a script or an agent; the calling agent's task id was accepted by the store and then never persisted. Adds a `callerKind` union recorded in audit metadata, tags every delete call site, and stamps a self-reported `x-fusion-client` header from the dashboard client. When the caller is `agent-tool` or `api-unattributed`, a best-effort notice is sent to the operator mailbox; operator and engine deletes stay silent. `x-fusion-client` is attribution, not authentication — anything can send it. No delete-blocking, gating or permission logic is added here. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
827b145fa1 |
FN-8611: persist manual import translations
Persist manual GitHub and GitLab import translations across preview sessions. - Add durable translation cache reads and identity-aware translation requests. - Route manual translations through the translation budget with actionable API errors. - Restore cached previews automatically and cover API/UI behavior with tests. Files changed: .changeset/fn-8611-manual-import-translations.md | 7 ++ docs/dashboard-guide.md | 2 +- packages/dashboard/app/api/ai-text.ts | 52 ++++++-- packages/dashboard/app/api/legacy.ts | 2 + .../dashboard/app/components/GitHubImportModal.tsx | 14 ++- .../components/GitHubImportTranslateControls.tsx | 39 +++++- .../__tests__/GitHubImportModal.test.tsx | 42 +++++++ packages/dashboard/src/ai-translate.ts | 23 ++++ .../register-ai-text-assistant-routes.test.ts | 135 +++++++++++++++++---- .../routes/register-ai-text-assistant-routes.ts | 131 ++++++++++++++------ 10 files changed, 370 insertions(+), 77 deletions(-) Fusion-Task-Id: FN-8611 Fusion-Task-Lineage: 00a84f43-4469-4bc5-bbb6-8245d8d79781 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
47087342fc |
FN-8605: harden tablet touch window controls
Make shared floating dashboard windows reliably movable and resizable on tablet touch viewports. - Apply a tablet-aware 44px drag and resize hit-target contract to FloatingWindow. - Preserve phone full-screen sheets below 768px while retaining desktop-hybrid geometry. - Add geometry, browser touch, and viewport boundary coverage with captured visual evidence. - Document the shared modal touch contract and release the dashboard fix. Files changed: .changeset/fn-8605-floating-window-touch.md | 7 ++ docs/dashboard-guide.md | 5 + docs/testing.md | 2 +- .../dashboard/app/components/FloatingWindow.css | 116 ++++++++++++++---- .../dashboard/app/components/FloatingWindow.tsx | 56 ++++++++- .../components/__tests__/FloatingWindow.test.tsx | 80 +++--------- .../FloatingWindow.touch-geometry.test.tsx | 136 +++++++++++++++++++++ .../FloatingWindowStack.cross-type.test.tsx | 2 +- .../app/hooks/__tests__/useViewportMode.test.ts | 18 ++- packages/dashboard/app/hooks/useViewportMode.ts | 7 +- .../app/task-modal-touch-resize-e2e-fixture.tsx | 39 +++++- .../__screenshots__/fn-8605/phone-fullscreen.png | Bin 0 -> 11393 bytes .../e2e/__screenshots__/fn-8605/tablet-after.png | Bin 0 -> 14275 bytes .../e2e/__screenshots__/fn-8605/tablet-before.png | Bin 0 -> 14308 bytes .../task-modal-touch-resize-browser.test.ts | 83 +++++++++++++ 15 files changed, 453 insertions(+), 98 deletions(-) Fusion-Task-Id: FN-8605 Fusion-Task-Lineage: 993c7ed8-c3e9-4673-b0b7-5c50746991a7 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
fde3b76a8c |
FN-8602: enable tablet touch resizing for task modals
Enable touch-driven resizing for task modals on tablet viewports. - Add tablet touch resize handles and pointer interactions for task and new-task modals. - Preserve responsive modal sizing while keeping phone layouts fullscreen. - Add unit, browser, and visual regression coverage with updated documentation. Files changed: .changeset/fn-8602-tablet-touch-resize.md | 7 + docs/dashboard-guide.md | 6 +- docs/testing.md | 4 + packages/dashboard/app/components/NewTaskModal.css | 70 ++++++++++ packages/dashboard/app/components/NewTaskModal.tsx | 19 ++- .../dashboard/app/components/TaskDetailModal.css | 12 ++ .../dashboard/app/components/TaskDetailModal.tsx | 7 +- .../app/components/__tests__/NewTaskModal.test.tsx | 4 + ...etailModal.responsive-and-dependencies.test.tsx | 1 + .../hooks/__tests__/useModalResizePersist.test.tsx | 10 +- .../app/hooks/__tests__/useViewportMode.test.ts | 26 +++- .../dashboard/app/hooks/useModalResizePersist.ts | 26 +++- packages/dashboard/app/hooks/useViewportMode.ts | 13 ++ packages/dashboard/app/styles.css | 16 +++ .../app/task-modal-touch-resize-e2e-fixture.html | 5 + .../app/task-modal-touch-resize-e2e-fixture.tsx | 60 ++++++++ .../__screenshots__/fn-8602/phone-fullscreen.png | Bin 0 -> 43987 bytes .../e2e/__screenshots__/fn-8602/tablet-after.png | Bin 0 -> 52549 bytes .../e2e/__screenshots__/fn-8602/tablet-before.png | Bin 0 -> 11500 bytes .../task-modal-touch-resize-browser.test.ts | 154 +++++++++++++++++++++ 20 files changed, 424 insertions(+), 16 deletions(-) Fusion-Task-Id: FN-8602 Fusion-Task-Lineage: f07ce22f-f529-4be3-ba1b-04063154a1b0 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
cca13737b6 |
FN-8603: reduce steady-state diagnostic log noise
Route routine core, engine, and dashboard diagnostics through debug-gated shared loggers. - Demote steady-state diagnostic sites while preserving warnings and errors for actionable failures. - Add cross-package severity contracts and manifest coverage for demoted log sites. - Document logging severity guidance and add a patch changeset. Files changed: .changeset/fn-8603-log-severity.md | 7 ++ docs/diagnostics.md | 20 ++++-- .../__tests__/log-severity-spam-contract.test.ts | 71 ++++++++++++++++++ packages/core/src/activity-analytics.ts | 5 +- packages/core/src/ai-summarize.ts | 61 +++++++--------- packages/core/src/async-mission-store.ts | 5 +- packages/core/src/async-secrets-store.ts | 7 +- packages/core/src/central-core.ts | 17 ++--- packages/core/src/docker-provisioning.ts | 13 ++-- packages/core/src/index.ts | 1 + packages/core/src/master-key.ts | 9 ++- packages/core/src/memory-compaction.ts | 29 ++++---- packages/core/src/memory-insights.ts | 7 +- packages/core/src/migration-orchestrator.ts | 7 +- packages/core/src/mission-store.ts | 5 +- packages/core/src/node-discovery.ts | 7 +- packages/core/src/notification/dispatcher.ts | 9 ++- .../core/src/plugins/bundled-plugin-install.ts | 11 +-- packages/core/src/reflection-store.ts | 5 +- packages/core/src/secrets-store.ts | 7 +- packages/core/src/task-store/agent-logs.ts | 21 +++--- packages/core/src/task-store/async-events.ts | 5 +- packages/core/src/task-store/async-maintenance.ts | 7 +- packages/core/src/task-store/comments-ops.ts | 7 +- packages/core/src/task-store/task-mutation-ops.ts | 11 +-- packages/core/src/task-store/workflow-integrity.ts | 9 ++- packages/core/src/types/merge-policy.ts | 5 +- packages/core/src/usage-events.ts | 5 +- .../__tests__/log-severity-spam-contract.test.ts | 48 +++++++++++++ packages/dashboard/src/ai-refine.ts | 5 +- packages/dashboard/src/ai-session-diagnostics.ts | 10 +-- packages/dashboard/src/chat.ts | 8 ++- packages/dashboard/src/devserver-manager.ts | 9 ++- packages/dashboard/src/file-service.ts | 5 +- packages/dashboard/src/github-tracking-comments.ts | 7 +- .../dashboard/src/github-tracking-reconciler.ts | 5 +- packages/dashboard/src/github-tracking-state.ts | 5 +- packages/dashboard/src/gitlab-lifecycle.ts | 5 +- packages/dashboard/src/insights-routes.ts | 9 ++- packages/dashboard/src/issue-image-attachments.ts | 5 +- packages/dashboard/src/knowledge-index.ts | 5 +- packages/dashboard/src/plugin-routes.ts | 7 +- packages/dashboard/src/routes/board-workflows.ts | 5 +- packages/dashboard/src/routes/context.ts | 5 +- .../dashboard/src/routes/register-auth-routes.ts | 13 ++-- .../routes/register-docker-provisioning-routes.ts | 7 +- .../dashboard/src/routes/register-git-github.ts | 21 +++--- packages/dashboard/src/routes/register-gitlab.ts | 7 +- .../src/routes/register-session-diff-routes.ts | 9 ++- .../src/routes/register-settings-memory-routes.ts | 7 +- .../src/routes/register-setup-activity-routes.ts | 7 +- .../dashboard/src/routes/register-signal-routes.ts | 5 +- .../src/routes/register-task-workflow-routes.ts | 11 +-- packages/dashboard/src/runtime-logger.ts | 11 +-- packages/dashboard/src/server.ts | 7 +- packages/dashboard/src/sse.ts | 8 ++- packages/dashboard/src/terminal-service.ts | 34 ++++----- packages/dashboard/src/view-chunk-manifest.ts | 5 +- .../engine/src/__tests__/log-severity-manifest.ts | 83 ++++++++++++++++++++++ .../__tests__/log-severity-spam-contract.test.ts | 40 ++++++++++- .../src/__tests__/logger-debug-gating.test.ts | 7 +- packages/engine/src/goal-anchoring-audit.ts | 5 +- packages/engine/src/plugin-runner.ts | 44 ++++++------ packages/engine/src/pty-native.ts | 9 ++- .../engine/src/runtimes/child-process-worker.ts | 4 +- packages/engine/src/self-healing.ts | 12 ++-- packages/engine/src/worktree-hooks.ts | 10 ++- 67 files changed, 632 insertions(+), 250 deletions(-) Fusion-Task-Id: FN-8603 Fusion-Task-Lineage: 53901db6-1af2-4bd7-b5ea-49507e048ef2 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
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> |
||
|
|
f5163d8351 |
fix(dashboard): resync SSE state on reopen and bound the service-worker cache
Follow-up to
|
||
|
|
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> |
||
|
|
c76f276266 |
fix(dashboard): stop mobile board swipes jumping two columns at the edges
Columns are narrower than a phone viewport, so the first/last column's ideal centered scrollLeft is outside the reachable scroll range. isColumnCentered compared against that unreachable value, so an edge rest never read as centered and commitDirectionalPage took its origin at release (already moved onto the next column) instead of at gesture start — paging two columns. Clamp the centering target to the reachable range, and clamp the mid-transit origin against the gesture-start column so drag travel is never counted twice. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
9afd88dd9b |
FN-8599: fix task card size badge alignment
Keep the size badge aligned with the first task-card header row when status badges wrap. - Anchor desktop and mobile size chips to the header start with fixed tokenized heights - Cover size-badge geometry and paused/reviewing badge combinations - Add a patch changeset for the task-card alignment fix Files changed: .changeset/fn-8599-task-card-size-badge.md | 7 +++ packages/dashboard/app/components/TaskCard.css | 25 +++++++-- .../__tests__/TaskCard.badge-height.test.tsx | 13 ++++- .../__tests__/TaskCard.badge-wrap.test.tsx | 62 +++++++++++++++++++-- 4 files changed, 98 insertions(+), 9 deletions(-) Fusion-Task-Id: FN-8599 Fusion-Task-Lineage: 062716b6-58ca-4f5b-8741-50856e933192 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
af897d9e3c |
FN-8596: isolate cross-root plugin MCP discovery
Prevent cross-root MCP discovery from unloading active plugin runtimes. - Isolate discovery loader lifecycle and runtime-state persistence. - Preserve shared plugin owners when non-owner loader participants stop. - Cover core, dashboard, and engine cross-root discovery behavior. Files changed: .changeset/fn-8596-plugin-discovery-isolation.md | 7 ++ .../plugin-loader-lifecycle-scope.test.ts | 12 +++ .../plugin-mcp-servers-discovery-isolation.test.ts | 115 +++++++++++++++++++++ packages/core/src/plugin-loader.ts | 34 +++++- packages/core/src/plugin-mcp-servers.ts | 8 +- .../context-plugin-mcp-discovery-isolation.test.ts | 48 +++++++++ packages/dashboard/src/routes/context.ts | 39 ++++++- ...-runtime-plugin-mcp-discovery-isolation.test.ts | 69 +++++++++++++ packages/engine/src/runtimes/in-process-runtime.ts | 47 +++++++-- 9 files changed, 364 insertions(+), 15 deletions(-) Fusion-Task-Id: FN-8596 Fusion-Task-Lineage: 231e53b6-a9a3-4a65-9732-3dabe44da198 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
cfa84781d6 |
fix(engine): demote high-frequency TUI log spam to debug
Route steady-state chatter (maintenance batch, skill listings, activity heartbeats, stuck polls, SSE connect/disconnect, heartbeat timer skips, cron/routine de-dupe skips, hold-release capacity races) through FUSION_DEBUG so the operator log pane keeps real state changes and failures. |
||
|
|
8b9cf3d4e8 |
fix(dashboard): remove right-edge dead space on landscape-tablet task pop-ups
The `.floating-window__body` resize-handle clearance gutter (FN-8015) was carved out only for 769-1024px, so landscape iPads (1180-1366 CSS px) fell through to the desktop contract and kept it — content stopped ~17px short of the right edge while the left edge stayed flush. Gate the carve-out on the input device instead of viewport width: the gutter only protects resize hot zones a pointer can actually grab. `(pointer: coarse)` is primary-input only, so a touchscreen laptop on a trackpad still reports `fine` and keeps desktop clearance. Measured at 1180px: header inset went from 1px/17px to 1px/1px. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
351fba1d33 |
FN-8594: improve project overview mobile reflow
Make the project overview usable at small viewport widths while hiding its non-functional mobile task navigation. - Reflow overview filters, stats, cards, and skeletons for 480px and 380px breakpoints. - Hide the mobile navigation bar and clear its reserved height outside an active project task view. - Add regression coverage and document responsive dashboard behavior. Files changed: docs/dashboard-guide.md | 5 +- packages/dashboard/app/App.tsx | 8 +- .../mobile-feature-access-regression.test.tsx | 22 ++++- .../project-overview-small-screen.test.ts | 91 ++++++++++++++++++++ packages/dashboard/app/components/MobileNavBar.tsx | 19 ++++- packages/dashboard/app/components/ProjectCard.css | 58 +++++++++++++ .../dashboard/app/components/ProjectOverview.css | 99 ++++++++++++++++++++++ 7 files changed, 294 insertions(+), 8 deletions(-) Fusion-Task-Id: FN-8594 Fusion-Task-Lineage: 0999f6e4-ffe9-4ed6-a471-8e5ba3413029 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
99b80ad748 |
feat(dashboard): add opt-in auto-update and harden restart supervision
Add the `autoUpdateAndRestart` global setting (default off, Settings -> General next to Release channel). When enabled, the dashboard host installs available updates on the selected channel by itself and requests the supervised in-place restart. Supervised hosts only: without a parent to respawn, installing would leave a running process whose code no longer matches its own install. Fix two ways the restart affordance could silently do nothing: - The supervisor now stamps FUSION_SUPERVISOR_PID and supervision is only counted when that pid is the real parent. FUSION_RESTART_SUPERVISED is inherited by every process Fusion spawns, so `fn dashboard` launched from an agent terminal skipped its own supervisor while still advertising restart support -- a restart request then killed it for good. - Settings and the update banner probe /system/info on mount and treat capability as advisory: the button always issues the request and shows the server's actual refusal instead of sitting disabled after a failed probe. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
147398f2c8 |
FN-8595: add mobile project favorite controls
Group mobile project switcher favorites ahead of other projects. - Reuse the shared bookmark store for mobile project rows and toggles. - Add responsive favorite-control styling and coverage for grouping, empty states, and selection behavior. - Add a release changeset for the mobile favorite-project experience. Files changed: .changeset/mobile-project-favorites.md | 7 ++ packages/dashboard/app/components/Header.tsx | 120 +++++++++++++++------ .../dashboard/app/components/ProjectSelector.css | 40 +++++++ .../Header.mobile-project-favorites.test.tsx | 116 ++++++++++++++++++++ 4 files changed, 253 insertions(+), 30 deletions(-) Fusion-Task-Id: FN-8595 Fusion-Task-Lineage: 1f739ee9-1ff5-4b47-ac9e-7e4a5857f1a5 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |