Commit Graph

13057 Commits

Author SHA1 Message Date
gsxdsm
ebe514c3e4 FN-8677: propagate task update lanes before conversion
Propagate cache-warmed workflow lanes through task updates so synchronous engine consumers support renamed boards.

- Add task lane cache and attach resolved lanes to task:updated metadata.
- Update scheduler, triage, and notification consumers to use carried lanes with bridge-safe fallbacks.
- Cover lane propagation and renamed-lane event behavior with core and engine tests.

Files changed:
 .changeset/fn-8677-manual-merge-hold-lanes.md      |   7 ++
 .changeset/task-updated-carries-lanes.md           |   7 ++
 ...orkflow-ir-readers-always-return-the-default.md |  22 +++++
 .../sync-workflow-ir-second-blocker.test.ts        |  43 +++-----
 .../core/src/__tests__/task-lane-cache.test.ts     |  30 ++++++
 .../task-updated-lanes-emit-surfaces.test.ts       |  92 ++++++++++++++++++
 .../__tests__/task-updated-lanes-payload.test.ts   |  42 ++++++++
 packages/core/src/index.ts                         |   1 +
 packages/core/src/store.ts                         |  36 ++++++-
 packages/core/src/task-lane-cache.ts               |  63 ++++++++++++
 .../core/src/task-store/archive-lifecycle-2.ts     |   3 +
 packages/core/src/task-store/moves.ts              |   1 +
 packages/core/src/task-store/task-artifacts-ops.ts |   1 +
 packages/core/src/task-store/task-update.ts        |   1 +
 packages/core/src/task-store/update-task-deps.ts   |   4 +-
 .../core/src/task-store/workflow-definitions.ts    |  71 +++++---------
 .../__tests__/scheduler-task-updated-lanes.test.ts | 108 +++++++++++++++++++++
 .../task-updated-lanes-bridge-compat.test.ts       |  94 ++++++++++++++++++
 ...task-updated-lanes-engine-emit-surfaces.test.ts | 101 +++++++++++++++++++
 .../src/__tests__/triage-pause-abort.test.ts       |  22 +++++
 .../src/__tests__/triage-planning-wake.test.ts     |  25 +++++
 .../notification-renamed-lifecycle-columns.test.ts |  84 +++++++++++++++-
 .../__tests__/task-wedge-notification.test.ts      |  19 ++++
 .../src/notification/notification-service.ts       |  56 ++++-------
 packages/engine/src/scheduler.ts                   |  62 +++---------
 packages/engine/src/triage.ts                      | 105 ++++++--------------
 scripts/lib/inert-sync-lane-baseline.json          |   5 +-
 27 files changed, 858 insertions(+), 247 deletions(-)

Fusion-Task-Id: FN-8677

Fusion-Task-Lineage: d8fef9db-0f88-4dfd-9813-be25e10e3588

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-08-01 01:15:10 -07:00
gsxdsm
5f12044168 FN-8652: add multiple provider credential instances
Enable operators to create, select, and remove named credentials for each supported provider.

- Add provider-auth instance discovery and credential mutation endpoints.
- Add dashboard authentication controls, status handling, and instance coverage.
- Document instance behavior and add a release changeset.

Files changed:
 .changeset/fn-8652-provider-credential-instances.md       |   7 +
 docs/secrets.md                                    |   2 +
 docs/settings-reference.md                         |   4 +
 packages/dashboard/app/api.ts                      |   1 +
 packages/dashboard/app/api/provider-status.ts      |  94 +++++-
 .../dashboard/app/components/SettingsModal.tsx     | 138 ++++-----
 .../AuthenticationSection.instances.test.tsx       |  75 +++++
 .../settings/sections/AuthenticationSection.css    |  13 +
 .../settings/sections/AuthenticationSection.tsx    | 199 +++++++-----
 .../dashboard/src/__tests__/routes-auth.test.ts    |  27 +-
 packages/dashboard/src/routes.ts                   |  12 +-
 .../dashboard/src/routes/register-auth-routes.ts   | 334 ++++++++++++++++++---
 .../src/__tests__/provider-auth-instances.test.ts  |  65 ++++
 packages/engine/src/provider-auth.ts               |  89 ++++++
 14 files changed, 845 insertions(+), 215 deletions(-)

Fusion-Task-Id: FN-8652

Fusion-Task-Lineage: 39568d58-7f57-4d17-97cb-1837323b3a94

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-08-01 01:07:59 -07:00
gsxdsm
df56790c9b FN-8676: fix Quick Add upward menu anchoring
Keep Quick Add portal menus attached to their triggers when they open upward.

- Position upward menus with bottom anchors rather than height-capped top offsets.
- Apply shared positioning to Quick Add pickers and the custom model dropdown.
- Add desktop and mobile regression coverage and a patch changeset.

Files changed:
 .changeset/quick-add-upward-menu-anchor.md         |  7 ++++
 .../app/components/CustomModelDropdown.tsx         | 18 ++++++--
 .../dashboard/app/components/QuickEntryBox.tsx     | 48 +++++++++++++++-------
 .../__tests__/CustomModelDropdown.test.tsx         | 40 +++++++++++++++---
 .../components/__tests__/QuickEntryBox.test.tsx    | 46 ++++++++++++++++++---
 .../app/utils/__tests__/fixedMenuPosition.test.ts  | 12 +++---
 packages/dashboard/app/utils/fixedMenuPosition.ts  | 22 ++++++----
 7 files changed, 151 insertions(+), 42 deletions(-)

Fusion-Task-Id: FN-8676

Fusion-Task-Lineage: e48b4b2c-9675-42c6-8712-83cb4a0ca3d8

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-08-01 01:00:57 -07:00
gsxdsm
1e83dcceec chore(release): v0.74.0-beta.6
Version bump via changesets.
2026-08-01 00:55:04 -07:00
gsxdsm
b244ecd9a0 fix(dashboard): refine queued badge icon alignment 2026-08-01 00:46:42 -07:00
gsxdsm
1cfedbb7c5 fix(dashboard): shift queued icon spacing left 2026-08-01 00:42:11 -07:00
gsxdsm
22da0f2341 fix(dashboard): balance queued badge icon spacing 2026-08-01 00:39:12 -07:00
gsxdsm
9b82ff29e1 fix: enforce active capacity and clarify queued cards
Refresh canonical live-task claims at serialized scheduler, planning, and merge admission boundaries, including workflow-step leases and reservation handoffs. Back off capacity-denied merges safely across abort and restart lifecycles. Render queued planning cards in the header badge family with compact reason-specific icons.
2026-08-01 00:32:25 -07:00
gsxdsm
cced31208e FN-8672: document observed suite-only flakes
Record first-sighting evidence for three suite-only flakes while preserving their substantial test coverage.

- Define the narrow first-sighting observed-register exception and second-sighting quarantine escalation.
- Add reproduction data for the core and engine PostgreSQL-adjacent flakes.
- Validate register metadata, paths, hierarchy segments, and escalation guidance.

Files changed:
 AGENTS.md                                          |  4 ++
 .../suite-only-flakes-observed-register.md         | 74 ++++++++++++++++++++++
 docs/testing.md                                    |  4 ++
 scripts/__tests__/observed-flake-register.test.mjs | 61 ++++++++++++++++++
 4 files changed, 143 insertions(+)

Fusion-Task-Id: FN-8672

Fusion-Task-Lineage: b52c74fb-aa7b-49e3-9f1d-a2c8c577f9c7

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-08-01 00:21:14 -07:00
gsxdsm
dd569395e8 FN-8671: isolate triage admission state in tests
Prevent leaked singleton admission state from affecting later triage polling tests.

- Add test-only coordinator reset and inspection seams for all admission categories.
- Stop tracked triage processors before clearing shared reservation and pre-held-slot state.
- Cover teardown behavior and document singleton-state isolation guidance.

Files changed:
 docs/testing.md                                   |  6 ++
 packages/engine/src/__tests__/concurrency.test.ts | 56 ++++++++++++++++
 packages/engine/src/__tests__/triage.test.ts      | 82 ++++++++++++++++++++---
 packages/engine/src/concurrency.ts                | 30 +++++++++
 4 files changed, 166 insertions(+), 8 deletions(-)

Fusion-Task-Id: FN-8671

Fusion-Task-Lineage: 41217db4-1fd6-45d6-a846-c57fc3c7052e

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-08-01 00:14:32 -07:00
gsxdsm
3b680cce04 FN-8675: align task-card size badges
Align direct size chips with adjacent task-card header badges.

- Center size badges on the shared header row across desktop and responsive layouts.
- Add coverage for size badge alignment, dimensions, and responsive CSS rules.
- Add a patch changeset for the dashboard alignment fix.

Files changed:
 .changeset/fn-8675-size-badge-alignment.md         |  7 +++
 packages/dashboard/app/components/TaskCard.css     |  8 +++
 .../__tests__/TaskCard.badge-height.test.tsx       | 68 ++++++++++++++++++++++
 .../__tests__/TaskCard.badge-wrap.test.tsx         | 10 ++++
 4 files changed, 93 insertions(+)

Fusion-Task-Id: FN-8675

Fusion-Task-Lineage: bf2eefb8-dbc2-40dd-a009-800f883146a8

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-08-01 00:10:04 -07:00
gsxdsm
ec55889605 FN-8674: add plan approval banner actions
Place plan approval controls alongside the task approval message for faster operator action.

- Add approve and reject controls to the plan-approval banner.
- Reuse existing approval handlers and add coverage for banner and footer actions.
- Add responsive banner action styling and a patch changeset.

Files changed:
 .changeset/plan-approval-banner-actions.md         |  7 +++
 .../dashboard/app/components/TaskDetailModal.css   | 14 +++++
 .../dashboard/app/components/TaskDetailModal.tsx   | 19 ++++++-
 .../TaskDetailModal.definition-actions.test.tsx    | 59 +++++++++++++++++-----
 4 files changed, 85 insertions(+), 14 deletions(-)

Fusion-Task-Id: FN-8674

Fusion-Task-Lineage: 89c521c2-535d-4492-8b39-6e1517693218

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-31 23:53:38 -07:00
gsxdsm
713e9320b0 fix(engine): enforce capacity for workflow continuations
Route durable planning and review continuations through shared project admission, retain reservations through execution, and prevent duplicate continuations from releasing another run's slot.
2026-07-31 23:33:47 -07:00
gsxdsm
7334cffebd FN-8660: add credential instance selection persistence
Persist optional credential-instance selections across model configuration without changing runtime credential behavior.

- Add credential instance IDs to global, project, task, preset, and workflow IR model lanes.
- Preserve selected instance IDs through model resolution and task persistence.
- Add PostgreSQL migration coverage, unit tests, documentation, and a minor changeset.

Files changed:
 .../fn-8660-credential-instance-selection.md       |  7 ++
 docs/settings-reference.md                         | 17 +++++
 .../core/src/__tests__/model-resolution.test.ts    | 37 ++++++++++
 .../credential-instance-selection.pg.test.ts       | 81 +++++++++++++++++++++
 .../postgres/settings-persistence.pg.test.ts       | 83 ++++++++++++++++++++++
 .../src/__tests__/workflow-ir-settings.test.ts     | 66 +++++++++++++++++
 packages/core/src/builtin-workflow-settings.ts     | 41 +++++++++++
 packages/core/src/model-resolution.ts              | 57 ++++++++++++++-
 .../0039_fn_8660_credential_instance_selection.sql |  9 +++
 packages/core/src/postgres/schema-applier.ts       | 14 +++-
 packages/core/src/postgres/schema/project.ts       |  4 ++
 packages/core/src/settings-schema.ts               | 25 +++++++
 packages/core/src/store.ts                         |  2 +-
 .../core/src/task-store/archive-lifecycle-2.ts     |  8 +++
 .../core/src/task-store/branch-and-pr-entities.ts  |  2 +-
 packages/core/src/task-store/persistence.ts        |  8 +++
 packages/core/src/task-store/serialization.ts      |  6 ++
 packages/core/src/task-store/settings-ops.ts       | 30 ++++++++
 packages/core/src/task-store/task-creation.ts      | 18 ++++-
 packages/core/src/task-store/task-mutation-ops.ts  |  6 +-
 packages/core/src/task-store/task-row-mappers.ts   |  6 +-
 packages/core/src/task-store/task-update.ts        | 24 +++++++
 packages/core/src/types/archive-planning.ts        |  5 ++
 packages/core/src/types/settings-scope.ts          | 40 +++++++++++
 packages/core/src/types/task-core.ts               | 30 ++++++++
 packages/core/src/types/workflow-steps.ts          |  9 +++
 packages/core/src/workflow-ir.ts                   | 18 +++++
 packages/core/src/workflow-settings.ts             | 10 +++
 28 files changed, 650 insertions(+), 13 deletions(-)

Fusion-Task-Id: FN-8660

Fusion-Task-Lineage: a3f625eb-018c-4084-954e-488b1d37691e

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-31 23:08:51 -07:00
gsxdsm
f5a776d923 FN-8667: align fast-mode badges with task status
Keep fast-mode metadata badges in the task-card header's shared wrapping context.

- Make the meta badge wrapper layout-transparent so fast, priority, and oversight badges wrap independently with status.
- Prevent fast and priority chips from shrinking and update desktop and mobile layout coverage.
- Add a patch changeset for aligned task-card badges.

Files changed:
 .changeset/task-card-fast-mode-badge-wrap.md       |  7 ++++
 packages/dashboard/app/components/TaskCard.css     | 16 ++++----
 .../__tests__/TaskCard.badge-wrap.test.tsx         | 46 ++++++++++++++++++++++
 .../app/components/__tests__/TaskCard.test.tsx     |  6 +--
 4 files changed, 64 insertions(+), 11 deletions(-)

Fusion-Task-Id: FN-8667

Fusion-Task-Lineage: 34bdb410-8d8a-4b1f-8bc4-105ae324874e

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-31 22:51:07 -07:00
gsxdsm
ee77a8d3fa FN-8659: preserve custom task specification sections
Preserve custom planning sections while reinjecting the original task description.

- Align unmarked original-description bodies before selecting a terminator.
- Anchor inserted descriptions before custom H2 sections and retain safe fallback behavior.
- Add regression coverage, workflow documentation, and a patch changeset.

Files changed:
 ...fn-8659-original-description-custom-sections.md |   7 ++
 docs/workflow-steps.md                             |   2 +
 .../__tests__/original-description-policy.test.ts  | 114 ++++++++++++++++-
 packages/core/src/original-description-policy.ts   | 135 ++++++++++++++++++---
 4 files changed, 235 insertions(+), 23 deletions(-)

Fusion-Task-Id: FN-8659
Fusion-Task-Lineage: 7f047a31-3750-4545-b743-bfac9546c55b
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-31 22:38:52 -07:00
gsxdsm
5596d915ab FN-8647: quarantine flaky Kimi K3 catalog test
Quarantine the timing-sensitive Kimi K3 SDK catalog test without changing timeout budgets.

- Reuse the native model registry once per test file.
- Add the observed CI timeout to the dashboard quarantine ledger and config.
- Document validation and timeout-budget preservation requirements.

Files changed:
 docs/testing.md                                    |  8 ++++++++
 ...ister-model-routes-kimi-k3-supplemental.test.ts | 23 ++++++++++++++++++++--
 packages/dashboard/vitest.config.ts                |  8 ++++++++
 scripts/lib/test-quarantine.json                   |  5 +++++
 4 files changed, 42 insertions(+), 2 deletions(-)

Fusion-Task-Id: FN-8647

Fusion-Task-Lineage: 31e79677-d923-4003-a8e8-082159334e65

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-31 22:32:18 -07:00
gsxdsm
897cce9d94 FN-8656: resolve scheduler lanes for renamed holds
Resolve scheduler lane lookup against workflow-defined hold and terminal columns.

- Use asynchronous workflow lane resolution after the synchronous event prologue
- Preserve legacy lane fallback and recognize all terminal workflow columns
- Update scheduler regression coverage, sync-lane guardrails, and release notes

Files changed:
 .changeset/fn-8656-scheduler-renamed-hold-lanes.md |   7 ++
 .../sync-workflow-ir-callsite-allowlist.test.ts    |  10 +-
 .../scheduler-renamed-hold-events.test.ts          |  20 ++--
 ...ow-scheduler-parked-columns-live-e2e.pg.test.ts |  13 ++-
 ...-sync-role-conversion-inert-live-e2e.pg.test.ts |   8 +-
 packages/engine/src/scheduler.ts                   | 121 +++++++++------------
 scripts/check-inert-sync-lane-conversions.mjs      |   5 +
 scripts/lib/inert-sync-lane-baseline.json          |   3 +-
 8 files changed, 92 insertions(+), 95 deletions(-)

Fusion-Task-Id: FN-8656

Fusion-Task-Lineage: 389a95a1-289f-4dde-86b3-1e450f8d43db

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-31 22:19:32 -07:00
gsxdsm
dcf1b921a3 fix(engine): close active-slot reservation handoff gaps 2026-07-31 22:10:14 -07:00
gsxdsm
012729cf2b chore: tighten lifecycle-column census baseline after slot-accounting fix
The active-worktree slot-accounting fix removed two deliberate scheduler
literals (done/archived: 3 -> 2); re-record so the ratchet follows the count
down.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-31 22:07:50 -07:00
gsxdsm
5a19d1da6e fix: count only actively running tasks against worktree capacity
Retained directories on queued, paused, blocked, or terminal tasks no longer
consume scheduler slots. Agent concurrency and worktree capacity now count the
same canonical live-task population through one project admission ceiling
(resolveActiveTaskCapacityLimit) with an atomic reserveIfAvailable claim, so
planning, execute, and merge lanes cannot each observe and claim the final
worktree slot independently.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-31 22:07:05 -07:00
gsxdsm
031d0f3e0b FN-8665: align task-card size badge heights
Align size badges with the shared task-card badge geometry across responsive layouts.

- Remove fixed size-chip heights so shared badge styling determines dimensions.
- Cover desktop and wrapped badge geometry for S, M, and L task sizes.
- Add a patch changeset for the visual consistency fix.

Files changed:
 .changeset/fn-8665-size-badge-height.md            |  7 +++++
 packages/dashboard/app/components/TaskCard.css     | 14 +++------
 .../__tests__/TaskCard.badge-height.test.tsx       | 33 ++++++++++++++++++++--
 .../__tests__/TaskCard.badge-wrap.test.tsx         | 18 ++++++------
 4 files changed, 50 insertions(+), 22 deletions(-)

Fusion-Task-Id: FN-8665
Fusion-Task-Lineage: e54e76d5-ba33-49d5-ac05-25c0cc4183db
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-31 22:03:15 -07:00
gsxdsm
182e3fdbe8 FN-8651: add named provider credential instances
Add provider-instance identity and auth.json storage for multiple credentials per provider.

- Export provider-instance key parsing, validation, and reserved metadata helpers.
- Resolve, list, mutate, and select named credential instances atomically in auth storage.
- Preserve legacy bare-key hydration and document the auth.json instance contract.
- Cover provider instance parsing, storage behavior, and concurrent writes.

Files changed:
 .changeset/fn-8651-provider-instances.md           |   7 +
 docs/secrets.md                                    |   8 +
 .../src/__tests__/oauth-credential-interop.test.ts |  16 ++
 packages/core/src/index.ts                         |  16 ++
 packages/core/src/oauth-credential-interop.ts      |  15 +-
 packages/core/src/provider-instance.test.ts        |  24 +++
 packages/core/src/provider-instance.ts             |  65 +++++++
 .../src/__tests__/auth-storage-concurrency.test.ts |  13 ++
 .../src/__tests__/auth-storage-instances.test.ts   |  75 ++++++++
 packages/engine/src/auth-storage.ts                | 214 +++++++++++++--------
 10 files changed, 367 insertions(+), 86 deletions(-)

Fusion-Task-Id: FN-8651

Fusion-Task-Lineage: cfabe496-60f9-4166-a09f-87ea2028cfd9

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-31 21:56:30 -07:00
gsxdsm
e080bca464 fix: persist awaitingApprovalReason through updateTask
The field was defined in persistence and serialization, the executor's Plan
Review replan-cap park wrote it, the triage manual gate null-cleared it, and
the dashboard special-cases it (isReviewBudgetExhaustedApproval badge + detail
explanation) — but updateTask's field-by-field merge never applied the key, so
every writer silently dropped it. FN-8647's 15-cycle non-converging Plan Review
loop therefore parked with a generic 'needs approval' and no hint it was a cap
escalation.

Merge contract, pinned by tests with a measured revert proof (3/4 fail
pre-fix): set persists, explicit null clears, a status write that leaves
awaiting-approval without addressing the reason auto-clears it so an approved
or replanned card cannot carry a stale escalation reason into its next park,
and unrelated updates leave it untouched.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-31 21:42:24 -07:00
gsxdsm
744c01f7fb FN-8657: extract move-target literal counter
Make the move-target literal ratchet directly testable without changing its scan behavior.

- Export the AST-based legacy destination counter for fixture testing
- Cover literal destinations, private moves, and deliberate exemptions

Files changed:
 .../__tests__/check-move-target-literals.test.mjs  | 30 ++++++++-
 scripts/check-move-target-literals.mjs             | 77 +++++++++-------------
 2 files changed, 60 insertions(+), 47 deletions(-)

Fusion-Task-Id: FN-8657

Fusion-Task-Lineage: 5b1f9ac4-6026-4baf-b42c-f41ab0562679

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-31 21:34:25 -07:00
gsxdsm
44e607e1a7 refactor: rename tablet marker to floating-window--tablet-viewport, add shell naming maps
The right-padding bug recurred three times because 'Task Detail modal' names ONE
surface with THREE shells and fixes kept landing in the wrong file. Disambiguate:

- Rename the just-introduced .floating-window--tablet marker to
  .floating-window--tablet-viewport and document the naming contract next to it:
  --tablet-viewport = viewport MODE classifies tablet (touch or not, styling
  surface); --touch-geometry = tablet AND touch (enlarged 44px targets only).
- Add SHELL NAMING MAP breadcrumbs at the top of TaskDetailModal.css and
  TerminalModal.css pointing inset/padding fixes at the FloatingWindow shell
  that tablet popups and floating terminals actually render through.

Comment wording deliberately avoids dot-prefixed class tokens because
FloatingWindow.test.tsx scans raw CSS (comments included) with selector regexes.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-31 21:25:40 -07:00
gsxdsm
a3c0501822 fix(engine): unblock retained worktree dispatch
Allow queued tasks to reuse worktrees they already hold when the durable worktree ledger is at or above capacity. Preserve independent agent and semaphore limits, and avoid releasing a worktree slot that a rejected transfer never acquired.
2026-07-31 21:21:28 -07:00
gsxdsm
71576d9536 fix: even out tablet popup right inset; drag terminal from whole toolbar
Third recurrence of the Task Detail right-padding bug (FN-8630/FN-8634): those
fixes only covered the .modal-overlay shells, while every tablet task popup and
floating terminal renders through FloatingWindow, whose shared body carries
FN-8015's margin-inline-end scrollbar gutter — a 16px right border with a 0px
left one. Tablet-mode windows (.floating-window--tablet, keyed on viewport MODE
so non-touch tablet widths match too) now zero the gutter; touch never grabs
scrollbar thumbs, so the desktop hot-zone conflict FN-8015 solves cannot occur.
GitHub-import's detail panel, which used the gutter as its right inset,
compensates locally. Desktop keeps the FN-8015 contract.

Also per operator request: the floating terminal is draggable from the empty
strip space behind the tabs and anywhere in the top toolbar, not only the
FN-8633 grip. Tab presses keep stopPropagation (scoped to .terminal-tab) so
they never start a window drag; the tablet floating header supersedes the
pan-x contract with touch-action: none (an overflowing strip is replaced by
the mobile-tabs dropdown, so no visible strip pans horizontally).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-31 20:54:48 -07:00
gsxdsm
b9612de748 FN-8646: fix dashboard task enrichment validation
Repair deterministic dashboard test regressions and reject whitespace-only enrichment targets.

- Normalize task IDs before enabling and submitting research enrichment.
- Update research, task-detail CSS, and chat visibility test fixtures.
- Add a patch changeset for the dashboard behavior fix.

Files changed:
 .changeset/fn-8646-dashboard-test-repairs.md         |  7 +++++++
 .../app/components/ResearchTaskActionModal.tsx       | 12 ++++++++++--
 .../app/components/__tests__/ResearchView.test.tsx   | 20 ++++++++++++++++----
 .../components/__tests__/TaskDetailModal.css.test.ts | 11 +++++++++--
 .../dashboard/app/hooks/__tests__/useChat.test.ts    |  9 ++++++++-
 5 files changed, 50 insertions(+), 9 deletions(-)

Fusion-Task-Id: FN-8646
Fusion-Task-Lineage: 033f4fa4-b096-4129-b82b-8fa7078dd6b2
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-31 20:39:37 -07:00
gsxdsm
7e4e892fce fix(dashboard): one queued badge family — map raw 'queued', fold queued-to-plan into the status badge
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-31 20:33:34 -07:00
gsxdsm
577dcb6c0c FN-8643: correct TaskWedgeNotifications FNXC timestamp
Correct the TaskWedgeNotifications migration-baseline stamp and remove its obsolete future-date allowance.

- Set the baseline FNXC annotation to its intended non-future timestamp.
- Remove the resolved migration from the future-date baseline allowlist.

Files changed:
 packages/core/src/postgres/migrations/0000_initial.sql | 2 +-
 scripts/lib/fnxc-future-dates-baseline.json            | 1 -
 2 files changed, 1 insertion(+), 2 deletions(-)

Fusion-Task-Id: FN-8643

Fusion-Task-Lineage: 0f27e9e6-0581-4e38-8005-e93f94ad4f78

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-31 19:32:55 -07:00
gsxdsm
408e558f54 FN-8648: audit sync workflow resolver test fixtures
Audit workflow resolver test fixtures so they reflect authoritative async resolution and document intentional sync coverage.

- Remove redundant synchronous resolver fixtures from async workflow tests.
- Preserve and document the scheduler fixture that exposes the known synchronous listener defect.
- Clarify the deliberate default-workflow sync contrast and strengthen recovery fixture intent.

Files changed:
 .../executor-planner-lanes-resolved.test.ts        | 45 +++++-----------------
 .../src/__tests__/planner-lane-resolution.test.ts  | 11 +++---
 .../planner-lanes-async-resolution.test.ts         |  9 ++++-
 .../recover-approved-intake-post-u11.test.ts       | 11 +++---
 .../scheduler-renamed-hold-events.test.ts          |  9 +++++
 .../__tests__/triage-release-renamed-hold.test.ts  |  6 ++-
 .../triage-undeclared-column-rescue.test.ts        | 11 +++---
 packages/engine/src/__tests__/triage.test.ts       | 23 +++++------
 8 files changed, 56 insertions(+), 69 deletions(-)

Fusion-Task-Id: FN-8648

Fusion-Task-Lineage: c75fe9ca-30a3-467e-9268-f8d6d95c49e7

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-31 19:26:23 -07:00
gsxdsm
b65bc0bbb4 FN-8650: preserve Grok CLI authentication status
Keep unavailable Grok CLI billing data distinct from authentication expiry.

- Preserve billing outcomes for usage windows, missing data, HTTP responses, and transport failures.
- Mark CLI authentication expired only after observed 401 or 403 responses.
- Cover unmeterable authenticated accounts in usage and indicator tests.
- Add a patch changeset for the corrected indicator behavior.

Files changed:
 .changeset/fn-8650-grok-cli-auth-expired.md        |  7 ++
 .../components/__tests__/UsageIndicator.test.tsx   | 16 +++++
 packages/dashboard/src/__tests__/usage.test.ts     | 79 ++++++++++++++++++++--
 packages/dashboard/src/usage.ts                    | 57 ++++++++++++----
 4 files changed, 139 insertions(+), 20 deletions(-)

Fusion-Task-Id: FN-8650

Fusion-Task-Lineage: f18cceb6-1092-4073-b540-9c7a5434fbcd

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-31 19:20:16 -07:00
gsxdsm
7bdeaa8b0c fix(engine): the new worktree ledgers count terminal lanes by NAME — renamed boards stall (#3296)
## Census

**Before: `COLUMN guards (the backlog): 2`, `--strict` RED. After:
`BACKLOG ZERO`, all five gates green.**

Two commits from last night's `maxWorktrees` rollout copied the same
holder ledger, both with literals:

| commit | file | gate |
|---|---|---|
| `374956ef23` | `triage.ts` | planning admission |
| `6c7467a78d` | `executor.ts` | `fn_spawn_agent` |

```ts
t.column !== "done" && t.column !== "archived"
```

## What it costs

Both exclude terminal lanes because a finished card's worktree is
**cleanup-owned, not capacity**. On a renamed board neither literal
matches, so every finished card keeps counting as a live holder. The
count only grows, the gate reaches zero room on a board with free slots,
and planning admission is withheld forever / every spawn is refused.

That is the **mirror** of the breach these commits fixed, and strictly
worse: 8 planners on a 4-slot board is visible; a permanent stall is
silent. The recorded reason even names the worktree budget, which the
operator then checks and finds has room.

## The conversion

`resolveProjectColumnsForRoles(store, ["complete", "archived"])` —
project-level, because the ledger spans the whole board with no single
task to resolve against. Matches triage's existing use in
`sweepStalePlanningStatuses` and executor's at the wip gates.
Legacy-seeded, so a default board still excludes exactly `done` and
`archived` — byte-identical there.

## Both conversions were UNCOVERED when written

Measured with #3214's blinding procedure **before** writing tests:
reverting either to the literals left **all 19 tests in the capacity
suites green**. Nothing in the tree could tell the conversion from what
it replaced — which is how the literals got there in the first place.

Each now has a renamed-board case that fails when blinded:

```
triage    converted 2 passed  |  BLINDED 1 failed | 1 passed  |  restored 2 passed
executor  converted 8 passed  |  BLINDED 1 failed | 7 passed  |  restored 8 passed
```

## The pairing earned itself immediately

Both new cases assert an **absence** (no throttle / no refusal), so each
is paired with a positive proving the gate still fires on the same
renamed board when a card genuinely holds the last worktree.

That caught a real defect in my own fixture: the candidate scan resolves
each task's **own workflow selection**, not `listWorkflowDefinitions`,
so my first version fell back to the default board where `drafting`
isn't a hold lane. No card was eligible, nothing throttled, and the
absence assertion **passed for the wrong reason**. The positive failed
and exposed it. Recorded at the fixture so the next reader doesn't
reintroduce it.

## Verification

```
42 tests across 6 capacity suites             pass
check-fnxc-future-dates                       green
check-inert-sync-lane-conversions             green
check-lane-wiring                             green
check-sql-column-literals                     green
census --strict                               green   (BACKLOG ZERO restored)
```

No changeset: internal engine fix, no published-package surface change.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-31 19:16:07 -07:00
gsxdsm
dfe050e8d4 FN-8640: add FNXC stamp anomaly advisory
Add a non-blocking census for implausible future-dated FNXC stamps.

- Classify tolerated future stamps by timezone plausibility and report notable anomalies.
- Add injectable gate seams and coverage for advisory, report, baseline, and discovery behavior.
- Document the advisory and preserve read-only check-mode baseline handling.

Files changed:
 docs/testing.md                                    |   4 +
 scripts/__tests__/check-fnxc-future-dates.test.mjs | 185 ++++++++
 scripts/check-fnxc-future-dates.mjs                | 495 +++++++++++----------
 3 files changed, 439 insertions(+), 245 deletions(-)

Fusion-Task-Id: FN-8640

Fusion-Task-Lineage: 7e57feb0-95e2-46b9-a1cf-9bd93c40d8e0

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-31 19:03:08 -07:00
gsxdsm
69bc9fc7ab fix(engine): near-duplicate check runs before planning for imported issues
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-31 18:53:38 -07:00
gsxdsm
8d54c5e049 fix(gate): the SQL-literal check WROTE to the tree it was checking — last of three (#3292)
Completes the family. `check-fnxc-future-dates` was #3287,
`lifecycle-column-census` is #3289, and this is the third and last gate
that rewrote its baseline during a plain check.

## Reproduction

```
inflated one allowance by 6, ran the gate with NO flags
  rc=0
  entry RESET to 1        ← the check modified the tree it was checking
```

## Why it matters

The tightening is right in substance — an allowance nobody spends is a
hole a literal can be regrown into. Performing it as a **side effect of
checking** handed every worker a byte-identical uncommitted diff they
had not authored, which they then reasonably committed.

Measured across the family: nine PRs chased three defects on
2026-07-31/08-01, two of them (#3283/#3285, five minutes apart, `+0/-1`
each) deleting the **same line neither author wrote**.

#3289 states the class best — *a check that writes turns every reader
into an author*. Two of us separately mis-attributed a gate-written
baseline to our own work while debugging something else.

## Measured, all four directions

| scenario | result |
|---|---|
| plain run, stale baseline | `rc=0`, reports `allowed 7, now 1`;
**inflation survived** — read-only |
| a new SQL literal added | **`rc=1`**, names `__sql_probe.ts` —
regression detection intact |
| `--update-baseline` | `rc=0`, entry written |
| clean tree, plain run | `rc=0`, **zero files dirty** |

Row 2 is the one worth checking: a read-only change to a gate is
worthless if it also stops catching the thing it exists for. The rise
path is untouched.

`census --strict` 0, `check-fnxc-future-dates` 0, eslint clean.

## Correcting my own delay

I measured this defect family on #3267 and then **declined to fix two of
the three**, reasoning that the census *"deliberately fails on a drop"*
so the port might be unsafe. That was wrong: it tightened and exited
`0`, exactly as its own test asserts — *"TIGHTENS on a drop and exits 0,
so somebody else's merge cannot redden the gate."* I had read the
`--exact` contract and attributed it to the default path.

The caution cost hours and prevented nothing. #3289 was written by
someone else in the meantime; this finishes what I should have finished
then.
2026-07-31 18:52:12 -07:00
gsxdsm
26fdb67505 docs(solutions): the audit table had one axis — add the one that missed three defects (#3293)
## What

My blind-spot table in #3251 audited **one axis**. Adds the one that
missed three defects. Docs only.

That table records what each of the five lifecycle ratchets can and
cannot **see**. I probed that carefully — several spellings per tool —
and then wrote *"nothing found; sound"* for two of them.

Within a day, three of those same tools turned out to share a completely
different defect: **they wrote to the tree they were checking**,
auto-tightening their own baseline during a plain check run.

| gate | wrote during a check | fixed by |
|---|---|---|
| `check-fnxc-future-dates` | yes | #3287 |
| `lifecycle-column-census` | yes, under `--strict` | #3289 |
| `check-sql-column-literals` | yes | #3292 |

**No number of detection probes could have surfaced that.** The table
asserted one property carefully and said nothing about the other *while
reading as comprehensive* — which is precisely the failure it documents
in the tools it audits.

## The rule it adds

1. **What can it see?** — probe each spelling of the thing it claims to
catch.
2. **Can it fail at all?** — invoke it as `package.json` does; a
report-only run exits 0 forever (#3255).
3. **Does it write?** — `git status --porcelain` before and after, on a
clean tree.

With the trap on the third spelled out: these gates write only when a
tightening is **available**, so a clean tree after a run proves the
*trigger* is absent, not that the tool is read-only. Inflate a baseline
entry first, then run it. I hit exactly this while reviewing #3292 — ran
all three gates on main, saw a clean tree, and had to stop myself
concluding the SQL gate was fine.

## Why the pattern, not the people

Three tools converged on write-during-check independently. That argues
the design is **attractive**, not that three authors were careless: the
tightening is correct, the write saves a step, and the message even
tells you to commit it. It only becomes a defect at the moment a second
person runs the same gate — which is invisible from inside any one of
them.

What it cost, measured: #3283 and #3285 are the same `+0/-1`, five
minutes apart, by two authors, **neither of whom wrote that line**.

```
lint clean; fnxc-future-dates clean
```
2026-07-31 18:52:00 -07:00
gsxdsm
c19bec3752 fix(fnxc): two scheduler stamps to their authoring commits' real times (10 days and 1 day off) (#3294)
Last loose thread from the 2026-07-31 stamp-repointing wave. Two comment
lines.

```
line 1027  ConcurrencyAdmission      2026-07-31-09:00  ->  2026-07-21-22:30   (eef5eb751e)
line 1471  WorkflowLifecycleColumns  2026-07-31-05:00  ->  2026-07-30-20:55   (109204c590)
```

## Not a revert — neither value was ever right

| stamp | originally | after #3280 | authoring commit (UTC) |
|---|---|---|---|
| `ConcurrencyAdmission` | `2026-08-06-09:00` (16 days ahead) |
`2026-07-31-09:00` (10 days late) | **2026-07-21 22:30** |
| `WorkflowLifecycleColumns` | `2026-08-01-05:00` (~1.5 days ahead) |
`2026-07-31-05:00` (~1 day late) | **2026-07-30 20:55** |

Both were written **ahead of their own commits** to begin with. Three
lanes then repointed stamps to turn `main` green (#3261, #3269, #3280),
moving the **date** back a day while keeping the clock time — which
converts an hours-off stamp into a days-off one in the opposite
direction. #3282 reverted the batch it owned; these two were outside its
scope.

So restoring the originals would be wrong too. The defensible values are
the authoring commits' UTC timestamps, per the `date -u` rule #3281
settled.

## Why now

`scheduler.ts` is a hot file. This survived two successive claimants — I
flagged it on #3262 and again on #3288 rather than opening a conflicting
PR, and said I'd take it once the file was unclaimed.
`check-file-claimed` now reports UNCLAIMED, so here it is.

## Scope

The gate is **green either way** — #3277 fixed the comparison, so
nothing is blocked by this. It is purely about the FNXC trail recording
when the work actually happened, which is the only reason the trail
exists. A stamp that satisfies a check while misstating the date by ten
days is worse than no stamp.

## Verification

```
check-fnxc-future-dates           green
check-inert-sync-lane-conversions green
check-lane-wiring                 green
check-sql-column-literals         green
census --strict                   green
```

Diff is two comment lines — no executable change. No changeset.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-31 18:51:50 -07:00
gsxdsm
6c7467a78d fix(engine): spawned children gate on maxWorktrees too — the spawn note promised both dimensions
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-31 18:43:37 -07:00
gsxdsm
374956ef23 fix(engine): planning admission respects maxWorktrees — the merge-drain freeze was masking the missing gate
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-31 18:39:40 -07:00
gsxdsm
5efe97c0ae docs(solutions): your environment is a variable in every measurement you report (#3291)
Extends the doc from #3255/#3273 with the failure that cost the most in
a single session: **one stale install produced five wrong reports on one
issue** (#3264).

## What happened

A `node_modules` that had drifted from the lockfile — `jsdom@29.0.1`
installed, `29.1.1` pinned — generated failures that existed on no CI
machine and no other checkout. They were not subtle: deterministic,
reproducible on demand, with plausible stack traces and real-looking
assertion diffs.

Each round of triage got **more precise about the wrong data**:

| round | claim | why it was wrong |
| --- | --- | --- |
| 1 | "4 deterministic failures" | measured in a 4-file batch, called it
isolation |
| 2 | "3 deterministic, 2 order-dependent" | isolated correctly, but a
race is not deterministic |
| 3 | "TaskCard is broken" | stale jsdom; the CSS assertion was correct
|
| 4 | "no contamination" | true of four app files; published unqualified
|
| 5 | "quarantine these two" | never read the failure text — both were
timeouts |

The through-line is not carelessness about the code. **The environment
was never treated as part of the claim**, so no amount of care about the
analysis could recover it.

## The checks, in the order they cost the most

```bash
pnpm install --frozen-lockfile   # node_modules is not evidence until it matches the lockfile
<run the file ALONE, 3+ times>   # isolation and repetition answer different questions
<read the failure TEXT>          # a timeout and an assertion failure need opposite responses
uptime                           # a loaded box manufactures timeouts that mean nothing
```

## Why the load check earned its place

Two tests "failing" in a full-suite run were `Test timed out in 15000ms`
on a box at **load average 9.7 with 84 users**. Under AGENTS.md's
quarantine-on-sight rule that reads as a flake to quarantine — and the
ledger's **14-day deletion ratchet would have made the lost coverage
permanent**.

The rule presumes the failure is a property of the test, not of the
machine. A wall-clock budget crossed under local contention is evidence
about the hardware. I was one comment away from deleting healthy
coverage on that basis.

## The tell

A finding is environment-derived when it is **local, recent, and
unshared**: nobody else has reported it, CI is green, and it appeared
without a commit that could explain it. Any two of those should stop a
report before it is written. All three applied here, and the report went
out anyway — five times.

## Verification

Docs only; no code paths change. `fnxc-future-dates`,
`lifecycle-columns`, `quarantine-ledger` exit 0. No changeset — internal
docs are excluded.

Context: the one finding in #3264 that survived all five rounds is #3286
(merged), and it survived because it was verified by **reverting the
product change** rather than by trusting a red — 3/3/2 failures without
the fix, 27/27 across four runs with it.
2026-07-31 18:38:56 -07:00
gsxdsm
c0ddadd746 FN-8634: balance Task Detail and terminal right padding
Balance perceived right-edge spacing across Task Detail and terminal shells.

- Move content padding outside scrollbar-owning Task Detail surfaces.
- Account for injected terminal viewport scrollbar tracks across modal variants.
- Add layout regression coverage and a patch changeset.

Files changed:
 .changeset/fn-8634-task-detail-right-padding.md    |   7 +
 .../__tests__/detail-body-mobile-overflow.test.ts  |   8 +-
 .../__tests__/task-detail-inset-symmetry.test.ts   | 271 +++++++++++----------
 .../dashboard/app/components/SessionTerminal.css   |  16 +-
 .../dashboard/app/components/SessionTerminal.tsx   |  14 +-
 .../dashboard/app/components/TaskDetailModal.css   |  49 ++--
 .../dashboard/app/components/TaskDetailModal.tsx   |  48 ++--
 .../dashboard/app/components/TerminalModal.css     |  11 +-
 .../components/__tests__/TaskChangesTab.test.tsx   |  23 +-
 ...etailModal.responsive-and-dependencies.test.tsx |  16 +-
 10 files changed, 273 insertions(+), 190 deletions(-)

Fusion-Task-Id: FN-8634

Fusion-Task-Lineage: 58cce580-5da0-47cf-a325-20b3707dbeef

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-31 18:34:58 -07:00
gsxdsm
42a2699b9a test(scheduler): pin the worktree-capacity arithmetic — both failure directions (#3288)
## What

Pins the **worktree-capacity arithmetic** — the gap #3262 measured,
named, and explicitly left for someone to claim. Two commits: a
behaviour-preserving seam extraction, then the test.

#3262's own scope note:

> blinding this predicate to `false` leaves all 22 scheduler suites
green (365 tests). The capacity logic it feeds has no behavioural
coverage at all.

## Two live defects, opposite directions

Both came out of these few lines:

- **UNDER-COUNT admits work over the cap.** `maxWorktrees=4`, four
planning sessions each holding a worktree, and a replan dispatch
admitted as the **fifth** — the ledger counted WIP cards only and never
learned to count planners.
- **OVER-COUNT self-deadlocks.** A planned Ready card *retains* its
planning worktree for execution reuse, so counting it as a holder blocks
its own release: `2 wip + 3 idle-held = 5/4`, and the first unpause
released 2 of 4 slots' worth of work.

**Both are pinned, and the asymmetry is why.** Under-counting breaks the
cap and lets real work over it; over-counting only starves dispatch. A
test covering the "safe" direction alone would leave the expensive one
open.

## Mutation-tested — all four caught

| mutation | result |
|---|---|
| drop the terminal exclusion | 1 failed / 7 passed |
| count WIP cards twice | 2 failed / 6 passed |
| count cards holding no worktree | 1 failed / 7 passed |
| drop the self-slot subtraction | 1 failed / 7 passed |

```
clean:            8 passed (8)
scheduler suite:  16 files / 151 tests passed  (behaviour preserved by the extraction)
typecheck, lint:  clean
```

## Scope, stated rather than implied

The terminal predicate is **injected**, not resolved here. Which lanes
are terminal is #3262's test; resolving it in this file would make it
fail for that reason instead of this one. This pins the **set
arithmetic** — who is excluded, and how the total is formed.

Still not covered, and I am not claiming otherwise: the *stateful* half
of the ledger — the `+= 1` on dispatch and the `Math.max(0, … - 1)` on
failure inside `schedule()`'s loop. Extracting that would mean
restructuring dispatch itself, which is a different change from this
one.

## Process note

I claimed this on #3262 **before** starting rather than after, because
`scheduler.ts` is the hottest file in the tree and I produced three
duplicate PRs earlier tonight by picking up small shared-surface work
someone else already had in flight. Announcing first cost one comment;
the duplicates cost three PRs and two closes.


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Bug Fixes**
* Improved scheduler capacity calculations for tasks that retain
existing worktrees.
  * Prevented WIP tasks from being counted twice.
  * Excluded completed and worktree-less tasks from reserved capacity.
* Corrected candidate capacity calculations when no worktree capacity is
reserved.

* **Tests**
* Added coverage for worktree reservation totals and candidate reuse
scenarios.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-07-31 18:33:41 -07:00
gsxdsm
65af9fd694 fix(census): the census WROTE to the tree it was checking — same defect as #3287 (#3289)
## What

**Port of #3287 to the sibling tool.** `lifecycle-column-census.mjs
--strict` called `writeBaseline()` during a plain **check**, so running
the gate modified the tree it was checking.

```
clean:  0 files dirty
$ node scripts/lifecycle-column-census.mjs --strict        # no --update-baseline
  rc=0
after:  M scripts/lib/lifecycle-column-census-baseline.json
```

## Why it matters — measured by #3287, reproduced here

#3287 established what this costs: every worker who runs the gate
receives a **byte-identical uncommitted diff they did not author**, and
reasonably commits it. #3283 and #3285 are the same `+0/-1`, five
minutes apart, by two different authors, **neither of whom wrote that
line** — the gate wrote it in both checkouts.

I hit this one the same way, which is the part worth recording: I saw a
modified baseline on my own branch and started reasoning about where
*my* change had touched it. It had not. A check that writes turns every
reader into an author.

The tightening is right in substance, and this tool's `COMMIT IT`
message made the diff *explained* rather than mysterious — better than
fnxc's was. **Neither addresses the mechanism.**

## The shape, matching #3287

Still computed, still reported loudly, written only under an explicit
`--update-baseline` (which has its own path above and is untouched):

```
lifecycle-column-census --strict: baseline CAN BE TIGHTENED — the tree has fewer guards than it allowed
  packages/engine/src/scheduler.ts: allows 1, tree has 0

Not written. Record it deliberately, so the diff has one author:

  node scripts/lifecycle-column-census.mjs --strict --update-baseline
```

**A plain run stays green rather than failing.** Guard counts drop when
someone *else's* merge removes a literal, so failing on a tightening
would redden main on a change the author never made. Report, don't
enforce — same reasoning #3287 gives for stamps aging into the past.

## Measured, all three directions

| scenario | result |
|---|---|
| plain `--strict`, stale baseline | reports + hint; **tree clean**
(was: 1 file dirty) |
| `--strict --update-baseline` | writes, rc=0 |
| a new guard added | **rc=1** — regression detection intact |

```
lint clean
```

## Note

Claimed on #3287 before starting, since it is that author's fix and they
may have had the port in flight. The two differences from the fnxc case
are noted there: this one fires under `--strict` rather than a bare run
(but `--strict` is what `package.json` and CI invoke, so it is the
common path), and its message was already loud.
2026-07-31 18:33:30 -07:00
gsxdsm
ad15113872 test(dashboard): pin the typed-title regression MY archived-lane fix caused (#3290)
#3286 fixed a real user-facing regression and **shipped no test**, so
nothing stops it returning. The regression was mine.

## The bug

#3215 (mine) added `isArchivedColumn` to an effect's dependency list to
keep the task fetch honest. That effect **also owned four `setState`
calls**, and `isArchivedColumn` is a `useMemo` over
`useBoardWorkflows()` — which revalidates asynchronously.

Every revalidation re-ran the reset over whatever the operator had
typed. A title entered before the workflows settled silently reverted to
`Research: <heading>`, and the task was created with a title nobody
wrote.

## Why my own four tests could not see it

Every existing case in this file asserts the **filtered task list** —
render, await `fetchTasks`, read the datalist. **None types into the
form.**

I tested what I added and not what I touched. That is why the regression
belongs in this file rather than a new one: the gap is this file's.

## The case

Renders with `boardWorkflows: null` — the state when an operator opens
the modal and starts typing — types a title with per-character
`userEvent`, then rerenders with a resolved workflow set (a **new object
identity**, which is the entire mechanism) and asserts the typed text
survived.

Two details that each cost a cycle, recorded at the site:

- **`fetchTasks` is not awaited.** It runs only in enrich mode, while
the title field exists only in create mode — so the reset effect, not
the fetch, is under test. My first version waited on it and failed for
the wrong reason.
- **`userEvent.type`, not `fireEvent.change`.** The documented failure
is state overwritten between renders; a single synthetic change event
can land after the reset and mask it.

## Measured both directions, on main `6834ba35bd`

| state | result |
|---|---|
| fixed main | **5 passed** |
| dependency re-added to the reset effect (my bug) | **1 failed / 4
passed** — and only that case |

The second row is the point: it fails on precisely the mutation that
recreates the defect, and leaves the four archived-lane cases green — so
it pins the regression without duplicating what is already covered.

`eslint` clean, `check-fnxc-future-dates` 0. Test-only.

## Note on provenance

Getting this measurement took three attempts: a `git checkout` of the PR
branch silently failed (stderr suppressed), so I twice ran against the
wrong tree and nearly concluded the fix did not work. HEAD and
dirty-count are printed beside every number above for that reason.
2026-07-31 18:33:19 -07:00
gsxdsm
6834ba35bd fix(engine): hung-pass watchdogs on scheduler, merge queue, and continuation drain
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-31 18:19:17 -07:00
gsxdsm
210a89d74f fix(dashboard): an operator's typed task title was wiped when board workflows resolved (#3286)
Found by chasing the deterministic half of #3264 (dashboard red on
`main`). **The tests were right; the product is broken.**

## The bug

Open **Create Task** from a research finding, type a title before the
board workflows settle, and the field silently reverts to the derived
default `Research: <heading>`. The task is then created with a title the
operator did not write. `description`, `priority` and `taskId` reset the
same way.

`ResearchTaskActionModal` reset those four fields in the same effect
that fetched the task list, and that effect's dependency list carried
`isArchivedColumn`:

```ts
const isArchivedColumn = useMemo(() => { … }, [boardWorkflows]);   // useBoardWorkflows() — async
useEffect(() => {
  setTitle(`Research: ${finding.heading || run.title}`);           // ← re-runs on every revalidation
  …
}, [open, mode, projectId, finding.heading, preview, run.title, isArchivedColumn]);
```

`useBoardWorkflows` resolves and revalidates asynchronously, so the
memo's identity changes and the reset re-runs over whatever the operator
has typed.

Introduced by #3215, which correctly added the archived-column filter
but hung its dependency on an effect that also owns form state. Same
class as the documented
`docs/solutions/ui-bugs/skill-autocomplete-highlight-reset-on-swr-revalidation.md`.

## The fix

Split into two effects: the reset depends only on what it derives from;
the fetch keeps `isArchivedColumn`. No behaviour change to the archived
filter — #3215's guard is untouched.

## Verification, both directions

The three standing `ResearchView` tests fail without this and pass with
it:

```
isArchivedColumn back on the reset effect:  3 failed | 24 passed (27)
as committed:                               27 passed (27)
```

## What I tried and removed, because it matters

I wrote a dedicated invariant test (per "fix the invariant, not the
repro") asserting that *all* typed fields survive a revalidation. **I
deleted it, because it did not work.**

- First draft used `mockImplementationOnce` to defer
`fetchBoardWorkflows`. `ResearchView` resolves board workflows on mount,
so that once-implementation was consumed before the modal opened.
Reverting the product fix left the test **green** — it proved nothing.
- Second draft deferred *every* call. `beforeEach` uses
`vi.clearAllMocks()`, which clears calls but **not implementations**, so
the deferral leaked into later tests and left `fetchBoardWorkflows`
permanently pending — masking two of the three genuine failures. The
revert then showed `1 failed` instead of `3`, i.e. my test was hiding
real bugs.

Rather than ship a regression test that cannot regress, I removed it.
The three existing tests already fail without the fix, which is real
coverage; a broader invariant test needs a modal-level harness that
resets implementations between cases, and that is worth doing properly
rather than badly here.

## Scope

Also in #3264: `TaskCard.badge-wrap` (1 deterministic failure, unrelated
— CSS/layout), and `useChat` / `WorkflowNodeEditor` /
`PlanningModeModal`, which pass standalone and are cross-file
contamination, not product bugs. Untouched here; the issue has the
per-file matrix.

No changeset — `@fusion/dashboard` is private.

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **Bug Fixes**
* Preserved form entries during workflow revalidation in the research
task modal.
* Limited task selection to active workflow columns when enriching
findings.
  * Prevented outdated task results from replacing newer selections.
* Improved loading and task-list behavior when source findings or modal
state changes.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-31 18:17:52 -07:00
gsxdsm
00769fad7c fix(engine): run merges outside the admission drain — every merge froze planning admission
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-31 18:13:32 -07:00
gsxdsm
e51ebff381 fix(engine): hung-poll watchdog — triage admission recovers loudly instead of dying silently
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-31 18:08:50 -07:00