6d10683dbded88a9d01f92c2ee68ec442084e82d
1083 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
eb874f3da3 |
convert(cli/commands/task.ts): triage guard 1 → 0 (+ a live main regression in the lifecycle E2E release path) (#2627)
**Batched push, gate open.** One conversion; the two E2E commits are held back and the reason is below — it is the more important half of this PR body. ## Conversion | File | triage column comparisons before | after | |---|---|---| | `packages/cli/src/commands/task.ts` | **1** | **0** | `pnpm test:gate` green, `pnpm lint` clean. All four non-terminal columns rendered the **same** glyph, so the four id comparisons were only ever asking "is this column terminal?". Naming `triage` made it a lifecycle-vocabulary site for no behavioural reason — the merged Planning column dropped that id, so the comparison silently stopped matching while the output stayed correct **by accident** (the fallthrough gave it the same glyph). Behaviour-identical **only** because the loop iterates the legacy `COLUMNS` constant (`types/board.ts:27` — exactly the six ids), so `col` can never be a custom id. Stated because the forms **diverge** outside that set: the old chain fell through to the terminal glyph for an unrecognised id, the new form returns the non-terminal one. If this ever iterates workflow-resolved columns that difference becomes live, and the right answer is a trait lookup, not this. **Deeper bug deliberately untouched, for U12:** because the loop iterates the legacy enum, a card in a workflow-renamed column **is not rendered at all**. That is R8's surface change, far bigger than this glyph. *(Note: this file is not on the 45-guard list, so it will not move your count. Flagging so the numbers reconcile.)* --- ## ⚠️ Live regression on origin/main — the lifecycle E2E release path While rebasing to push, the flagship lifecycle E2E went red. **I verified it on `origin/main` alone, with none of my commits: 2 failed / 18 passed.** ``` scenario 1 — DEFAULT vocabulary → AssertionError: expected [] to include 'FN-E2E-1' (r.sweep.released is EMPTY) scenario 2 — RENAMED vocabulary → audit trail differential broken: renamed produced [{end},{review}], default produced [] ``` Both are pre-existing tests I have never touched. **The capacity release sweep is releasing nothing.** **Likely cause, from reading rather than bisecting** — so treat it as a lead, not a verdict: `moves.ts:1059` now resolves a capacity pool id and enforces `enforcePooledColumnCapacity` **inside the move transaction** (#2488 "bind the in-transaction capacity gate", made user-visible by #2499 "make the capacity gate actually bind for real projects"). The E2E drives with `settings = { experimentalFeatures: { workflowGraphExecutor: true } }` — **no `maxConcurrent`** — while the fixture's wip column declares `{ trait: "wip", config: { limitSetting: "maxConcurrent", countPending: true } }`. If the resolved limit is finite and the pooled count meets it, the hold→wip move is rejected on capacity and the sweep correctly reports nothing released. If that is right, it is a **test-harness/production interaction, not a product break** — but it means the program's primary end-to-end evidence for the capacity boundary is currently inert on main, which matters for completion criterion 3. It needs the capacity worker's eyes, since #2488/#2499 are theirs and I would be guessing at the intended pool/limit contract. ## Why my two E2E commits are held They add scenario 3 (merged intake+hold board) and scenario 6 (REVISE → rework), both of which **depend on the same release leg**. On current main they fail for main's reason, taking the file from 2 failures to 4. Pushing them would add red to the count you are tracking and obscure whose regression it is. Both are complete, mutation-attributed, and green against the commit I wrote them on: | Scenario | Proves | Mutation that fails it | |---|---|---| | 3 — merged intake+hold | capacity release works from a dual-role column | `isHeldTask` treating intake/hold as exclusive → exactly its 2 tests | | 6 — REVISE → rework | `InReview → InProgress` on renamed *and* merged boards | disabling rework re-entry → exactly its 2 tests | They go out in the next batch the moment the release path is green. ## Also not shipped, twice attempted, deleted both times Safeguard 2 (`autoMerge:false` terminal-until-human) still has **no** graph-level E2E. Attempt 1 passed and then survived mutating `merge-gate` to ignore `task.autoMerge` — the card was parking on the review column's `merge-blocker` trait, not the gate. Attempt 2 removed that trait to isolate the gate, and then the *control* case parked too, so the flag still was not the discriminator. A fixture that can isolate it needs a merge path mirroring the builtin (`merge-gate → merge node → end`) rather than a direct edge to `end` — a real redesign, not a speculative edit. The enforcement that actually holds today is `allowInReviewMergeProcessing` in `project-engine` (unit-mutation verified, NEW=9; gated via #2526). 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
d5f1ce7abd |
U11 [writes]: stop CREATING cards into a column the workflow no longer declares (9 -> 0, engine+cli) (#2603)
**Taking: `engine/triage.ts`, `engine/pr-comment-handler.ts`, `engine/eval-followups.ts`, `cli/commands/task.ts`, `cli/extension.ts`** (write class — no collision with the comparison backlog). ## A class the census does not count The 48-guard work list tracks `=== "triage"` **comparisons**. These are `column: "triage"` **writes** — and post-#2515 every one creates a card directly into the state STALL 3 was about, except **manufactured continuously** rather than left behind by the upgrade. ## Why they bite `createTaskImpl` resolves the column as: ```ts column: input.column || options?.resolvedEntryColumn || fallbackIntakeColumn || "triage" ``` `input.column` **wins**, so an explicit `column: "triage"` overrides the workflow's resolved intake column entirely. `store-create-intake-column.test.ts` already pins that a create with **no** column lands in the default workflow's intake (now `todo`) — these callers opted out of it. The sharpest is `triage.ts`'s `fn_task_create` agent tool: it passed `workflowId: params.workflow_id` **and** `column: "triage"` in the same call. The caller chose a workflow and the column ignored it — a Coding (Ideas) create landed in `triage` instead of `ideas`. ## Counts **Comparison guards: unchanged by this PR.** This is the write class; conflating the two would misreport convergence toward the zero bar. | file | `column: "triage"` writes before | after | |---|---:|---:| | `packages/engine/src/triage.ts` | 1 | **0** | | `packages/engine/src/pr-comment-handler.ts` | 1 | **0** | | `packages/engine/src/eval-followups.ts` | 1 | **0** | | `packages/cli/src/commands/task.ts` | 3 | **0** | | `packages/cli/src/extension.ts` | 3 | **0** | | **total** | **9** | **0** | ## A test that pinned the defect `pr-comment-handler.test.ts` asserted `column: "triage"` in the createTask call — so it would have **failed the fix and passed the bug**. Rewritten to assert the invariant (the caller passes no column, so the workflow's intake wins) plus an explicit `Object.hasOwn(arg, "column") === false`, which is what actually catches a reintroduction. ## Interaction with #2591 My merged #2591 rescues these cards once created — they sit on a legacy planner id their workflow doesn't declare and are still in planning stage. So this isn't a *visible* stall today; the rescue absorbs it. **That's the reason to fix it rather than leave it:** a self-healing path silently absorbing a steady stream of malformed creates is exactly how the underlying defect stays invisible. ## Deliberately not touched - `{ id: "start", kind: "start", column: "triage" }` in the builtin coding / PR / lead-generation IRs — workflow-internal **node declarations** for workflows that still legitimately declare a `triage` column, not lifecycle writes. - Left for their owners: `core/task-store/project-store-ops.ts:210`, `core/task-store/update-task-deps.ts:111` (main worker), `dashboard/src/routes/register-gitlab.ts:108` (u12). Same defect, same one-line shape. ## Verification - 304 engine/CLI tests green across the affected suites - merge gate green (482 + 132 + 10), engine + CLI tsc clean, lint clean No changeset: `@fusion/engine` and `@fusion/core` are private; the CLI change is a bug fix with no user-facing API change — happy to add one if you'd rather it appear in release notes. 🤖 Generated with [Claude Code](https://claude.com/claude-code) |
||
|
|
9a8fc409ff |
fix: persist manual task pauses (#2536)
## Summary - persist an explicit `userPaused` latch when operators pause tasks through CLI, MCP, dashboard task routes, or mission stop - keep automatic/internal pauses distinct (`userPaused` remains false unless explicitly requested) - clear the latch on unpause - route the flag through in-memory and PostgreSQL task stores - add contract coverage across core, CLI, MCP, dashboard task routes, and mission stop ## Why A manually paused task could lose the reason for its pause across dashboard/runtime restart. Startup recovery then treated it like an internally interrupted task and reclaimed it, restarting automation against the operator’s intent. Manual pauses must survive restart and remain non-runnable until explicitly unpaused. ## Verification - core pause durability tests: 2 passed - CLI task/extension tests: 150 passed; PostgreSQL integration lane remains active in CI - dashboard route tests: 261 passed - `@fusion/core`, `@runfusion/fusion`, and `@fusion/dashboard` typechecks passed - full workspace build passed with pnpm 10.33.0 - changeset validation and `git diff --check` passed - live aggregate runtime verification also confirmed `paused=true,userPaused=true` survived a normal dashboard restart with zero active tasks <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **Bug Fixes** - Manual task pauses now persist across application restarts and recovery. - Pauses initiated via the CLI, dashboard, MCP tools, and mission stop controls are recorded as explicit user actions. - Automatically paused tasks remain eligible for recovery. - Unpausing clears the durable manual-pause state. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
3badc244a7 |
U12 part 2: bind the three U5 reconciliation guards — USER-VISIBLE (and one path that couldn't run under PostgreSQL at all) (#2512)
## U12 part 2 — the three U5 reconciliation guards now actually fire
USER-VISIBLE. Taken on standing authority; here is exactly what changed
for operators.
All three read the RAW `experimentalFeatures.workflowColumns` key via
`store.workflowColumnsFlagOn()`. Nothing in production writes it, so all
three have been inert since the workflow-columns cutover.
| Guard | Before (every real project) | After |
|---|---|---|
| Workflow edit removing an **occupied** column | Save succeeded; cards
left in a column the workflow no longer declares | Save fails with
`OccupiedColumnsError` unless `rehomeTo` is supplied |
| Workflow **delete** | Occupant capture returned `[]`; cards sat in the
deleted workflow's columns until the next engine start | Cards move to
the default workflow's entry column as part of the delete |
| Workflow **switch** | Never reconciled; the `reconciliation` field in
the declared return type was never populated | Card in an undeclared
column moves to the resolved target; a declared column is preserved |
Both consumers already handle the new outcomes and needed no change:
`register-workflow-routes.ts` maps `OccupiedColumnsError` to a
structured 409 carrying per-column occupant counts, and
`fn_workflow_update` returns a retryable structured result. The
dashboard editor's `rehomeTo` retry flow becomes reachable for the first
time. I only updated two stale "flag-ON" comments there — that code was
correct all along and simply never fired.
### What an operator actually sees (USER-VISIBLE — read this bit)
Four changes to what the board and the API do. Nothing here is silent.
1. **Editing a workflow to remove a column that has cards in it now
FAILS.** Previously the save succeeded and the cards were left in a
column their workflow no longer declared. The dashboard shows the
existing 409 with per-column occupant counts and prompts for a re-home
target; retrying with `rehomeTo` moves the cards and saves. Removing an
EMPTY column is unaffected.
2. **Deleting a workflow moves its cards immediately** to the default
workflow's entry column, instead of leaving them until the next engine
start.
3. **Switching a task's workflow moves the card** when the new workflow
does not declare its current column. A card whose column IS declared
stays exactly where it is. The API response now carries the
`reconciliation` summary it always promised.
4. **A switch whose re-home would be REJECTED is now refused before
anything is written.** If the destination column is at its WIP limit,
the switch fails with a structured 409 (`workflow-switch-rehome-failed`)
naming the task, both columns and the reason — and **nothing changes**:
the task keeps its current workflow AND its current column. Retry after
making room. Previously this combination committed the selection and
then silently reported a move that never happened, leaving selection and
column disagreeing.
**Can a torn card still happen? Yes, in one narrow case, and here is how
you recover.** If the destination fills in the window between the
pre-flight and the move, the selection is already committed and the card
ends up in a column its new workflow does not declare. That case is not
silent: it writes a `task:workflow-switch-torn` run-audit row, and the
error carries `selectionCommitted: true` with both columns. Recovery:
make room in the destination and move the card there, or switch the task
back — and if neither happens, the R7 startup sweep
`reconcileUndeclaredTaskColumns` re-homes it on the next engine start.
The card is never lost; it is visible in a lane the board may not draw
until one of those runs.
The one thing to watch after merge: (1) converts a previously-silent
success into a visible failure, so an operator mid-edit on a busy
workflow will start seeing a 409 they never saw before. That is the
point — the alternative was stranding their cards — but it is the change
most likely to generate a "this used to work" report.
### The thing that made this more than a gate removal
Un-gating the switch guard surfaced that
`selectTaskWorkflowAndReconcileImpl` read the task through
`store.readTaskFromDb` — the **synchronous SQLite** reader, which throws
under PostgreSQL:
```
TaskStore.db: SQLite Database is not available in backend mode
```
The flag returned before that line, so the gate was hiding a path that
**could not execute at all in the production backend**, not merely a
disabled feature. Ported to the async `readTaskRow`. Found by the new
tests, not by reading the code.
### Review round 2 (both findings real, both fixed)
**Torn write with no alarm — fixed by ORDERING, not by a louder
message.** My first attempt only made the error loud, which left the
torn state intact. The real fix is that the deterministic rejection
cause (destination at its WIP limit) is now checked BEFORE
`selectTaskWorkflow` commits, by resolving the target IR straight from
`workflowId` instead of through the task's selection. Nothing commits on
that path.
For the residual race the failure is loud AND recorded: `rehomeOccupant`
now returns `{ moved, error? }` (additive; sweep callers ignore it), the
switch writes a `task:workflow-switch-torn` run-audit row, and throws
`WorkflowSwitchRehomeFailedError` with `committed: true`. Consumers
translate it: the dashboard route returns a structured 409 with
`selectionCommitted`, and `fn_task_set_workflow` returns the same fields
— no more generic "something went wrong".
**Fabricated column for a deleted task.** My first fix fell back to
`fromColumn` when the final read found no row, so a task soft-deleted
mid-switch was reported as having its old column *preserved*. Absent now
reads as absent (the optional `reconciliation` is omitted). Extracted as
the pure `buildSwitchReconciliation` seam because the window is not
reachable through the public call — `selectTaskWorkflow` rejects an
already-deleted task up front — so it is a genuine race, and I test the
decision directly rather than asserting it from reading the code.
### Revert-proof, measured
New `workflow-reconciliation-production-shape.pg.test.ts` — 6 cases,
with the flag **never written**, which is the configuration every real
project has. Each flip reverted individually:
- re-gate the edit guard → **2 failures** (OccupiedColumnsError case;
rehomeTo re-home case)
- re-gate the delete capture → **1 failure** (card stays in
`custom-hold`)
- restore the switch early return → **2 failures** (`reconciliation`
undefined; card does not move)
- all three in place → **6/6 green**
Round-2 fixes, also measured:
- restore the `fromColumn` fallback → the "row is gone" case fails
(reports `preserved: true` for a deleted task)
- drop the `!outcome.moved` throw → the capacity-blocked case fails
(resolves instead of raising)
- **move the capacity pre-flight back AFTER the commit → the case fails
on the SELECTION assertion** (expected `WF-002`, received `WF-001`),
i.e. it proves the ordering, not the wording
The pre-existing coverage in `workflow-authoritative-reads.pg.test.ts`
reached the occupied-column guard by **writing the flag ON itself** —
same pattern as the ListView/Board suites in part 1. Its flag write is
removed; it now runs in the production shape.
### Where I nearly got this wrong
My first revert harness was buggy and I briefly concluded the delete
re-home was **redundant** — I had probed the stored column and seen
`triage` with what I thought was the flip reverted. It wasn't.
`workflow-ops.ts` contains two identical `const occupantTaskIds = await
store.listWorkflowOccupantTaskIds(id, false)` lines (field-reconcile
block, delete path), so my first-match edit reverted the wrong one.
Re-run anchored on surrounding context, the delete case fails as
predicted. Recorded in the test header as a caution. I also chased and
**refuted** a scarier hypothesis along the way — that an unrelated
`updateTask` coerces a custom column back to `triage`. It does not; the
column survives.
### Deliberately NOT in this PR
The v1-IR rollback-compat persistence (`downgradeIrToV1IfPure`) on the
workflow UPDATE path. It shared the same `flagOn` variable, which is how
it surfaced: **one flag read was feeding two unrelated decisions, so the
flag has more decision sites than call sites** — my earlier 9-site
inventory undercounted. It chooses the stored *shape* of the graph
rather than gating a guard, so it is a persistence-format change with a
different blast radius. It now reads the flag explicitly, behaviour
unchanged, for a follow-up.
The `moves.ts` group remains U2b's.
### Verification
`pnpm test:gate` (307 + 10 + 71), `pnpm lint`, `pnpm verify:fast` (17
steps), both typechecks green. Full `packages/core` PostgreSQL suite:
**1042 passed, 3 failed** — `central-archive-secrets.test.ts`
(log-prefix assertion) and
`workflow-settings-project-identity.pg.test.ts` (×2, project-id
resolution). I confirmed the identical 3 failures on a stashed clean
tree: pre-existing, unrelated. No Fusion instance booted.
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **Bug Fixes**
* Workflow edits now prevent removal of occupied columns unless cards
are moved to a specified destination.
* Cards are automatically re-homed when workflows are deleted or
switched.
* Workflow switches now check destination capacity before committing and
provide clear conflict details when re-homing fails.
* Reconciliation results now indicate whether cards were moved or
preserved.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
||
|
|
74d6513fae |
chore(deps): bump actions/upload-artifact from 4 to 7 (#2444)
Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 4 to 7. <details> <summary>Release notes</summary> <p><em>Sourced from <a href="https://github.com/actions/upload-artifact/releases">actions/upload-artifact's releases</a>.</em></p> <blockquote> <h2>v7.0.0</h2> <h2>v7 What's new</h2> <h3>Direct Uploads</h3> <p>Adds support for uploading single files directly (unzipped). Callers can set the new <code>archive</code> parameter to <code>false</code> to skip zipping the file during upload. Right now, we only support single files. The action will fail if the glob passed resolves to multiple files. The <code>name</code> parameter is also ignored with this setting. Instead, the name of the artifact will be the name of the uploaded file.</p> <h3>ESM</h3> <p>To support new versions of the <code>@actions/*</code> packages, we've upgraded the package to ESM.</p> <h2>What's Changed</h2> <ul> <li>Add proxy integration test by <a href="https://github.com/Link"><code>@Link</code></a>- in <a href="https://redirect.github.com/actions/upload-artifact/pull/754">actions/upload-artifact#754</a></li> <li>Upgrade the module to ESM and bump dependencies by <a href="https://github.com/danwkennedy"><code>@danwkennedy</code></a> in <a href="https://redirect.github.com/actions/upload-artifact/pull/762">actions/upload-artifact#762</a></li> <li>Support direct file uploads by <a href="https://github.com/danwkennedy"><code>@danwkennedy</code></a> in <a href="https://redirect.github.com/actions/upload-artifact/pull/764">actions/upload-artifact#764</a></li> </ul> <h2>New Contributors</h2> <ul> <li><a href="https://github.com/Link"><code>@Link</code></a>- made their first contribution in <a href="https://redirect.github.com/actions/upload-artifact/pull/754">actions/upload-artifact#754</a></li> </ul> <p><strong>Full Changelog</strong>: <a href="https://github.com/actions/upload-artifact/compare/v6...v7.0.0">https://github.com/actions/upload-artifact/compare/v6...v7.0.0</a></p> <h2>v6.0.0</h2> <h2>v6 - What's new</h2> <blockquote> <p>[!IMPORTANT] actions/upload-artifact@v6 now runs on Node.js 24 (<code>runs.using: node24</code>) and requires a minimum Actions Runner version of 2.327.1. If you are using self-hosted runners, ensure they are updated before upgrading.</p> </blockquote> <h3>Node.js 24</h3> <p>This release updates the runtime to Node.js 24. v5 had preliminary support for Node.js 24, however this action was by default still running on Node.js 20. Now this action by default will run on Node.js 24.</p> <h2>What's Changed</h2> <ul> <li>Upload Artifact Node 24 support by <a href="https://github.com/salmanmkc"><code>@salmanmkc</code></a> in <a href="https://redirect.github.com/actions/upload-artifact/pull/719">actions/upload-artifact#719</a></li> <li>fix: update <code>@actions/artifact</code> for Node.js 24 punycode deprecation by <a href="https://github.com/salmanmkc"><code>@salmanmkc</code></a> in <a href="https://redirect.github.com/actions/upload-artifact/pull/744">actions/upload-artifact#744</a></li> <li>prepare release v6.0.0 for Node.js 24 support by <a href="https://github.com/salmanmkc"><code>@salmanmkc</code></a> in <a href="https://redirect.github.com/actions/upload-artifact/pull/745">actions/upload-artifact#745</a></li> </ul> <p><strong>Full Changelog</strong>: <a href="https://github.com/actions/upload-artifact/compare/v5.0.0...v6.0.0">https://github.com/actions/upload-artifact/compare/v5.0.0...v6.0.0</a></p> <h2>v5.0.0</h2> <h2>What's Changed</h2> <p><strong>BREAKING CHANGE:</strong> this update supports Node <code>v24.x</code>. This is not a breaking change per-se but we're treating it as such.</p> <ul> <li>Update README.md by <a href="https://github.com/GhadimiR"><code>@GhadimiR</code></a> in <a href="https://redirect.github.com/actions/upload-artifact/pull/681">actions/upload-artifact#681</a></li> <li>Update README.md by <a href="https://github.com/nebuk89"><code>@nebuk89</code></a> in <a href="https://redirect.github.com/actions/upload-artifact/pull/712">actions/upload-artifact#712</a></li> <li>Readme: spell out the first use of GHES by <a href="https://github.com/danwkennedy"><code>@danwkennedy</code></a> in <a href="https://redirect.github.com/actions/upload-artifact/pull/727">actions/upload-artifact#727</a></li> <li>Update GHES guidance to include reference to Node 20 version by <a href="https://github.com/patrikpolyak"><code>@patrikpolyak</code></a> in <a href="https://redirect.github.com/actions/upload-artifact/pull/725">actions/upload-artifact#725</a></li> <li>Bump <code>@actions/artifact</code> to <code>v4.0.0</code></li> <li>Prepare <code>v5.0.0</code> by <a href="https://github.com/danwkennedy"><code>@danwkennedy</code></a> in <a href="https://redirect.github.com/actions/upload-artifact/pull/734">actions/upload-artifact#734</a></li> </ul> <!-- raw HTML omitted --> </blockquote> <p>... (truncated)</p> </details> <details> <summary>Commits</summary> <ul> <li><a href=" |
||
|
|
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) |
||
|
|
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> |
||
|
|
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> |
||
|
|
106c61e6ee |
fix(agent-tools): close the fn_delegate_task Deny bypass and the store's window clamp
Follow-up to
|
||
|
|
13a2b2a9da |
fix(agent-tools): hide fn_task_create under Deny and widen the dedupe window
Operator report: with project policy "Ephemeral agent follow-up tasks = Deny", an executing agent filed ten follow-up tasks — five parallel fn_task_create calls it reported as timed out, then five sequential retries. Two defects: 1. Deny was advisory. fn_task_create was registered for every session and only refused inside execute(), so the model still saw the tool, planned around it, and retried it. The pi extension's isEphemeralCallerAgent also failed OPEN whenever the caller id did not resolve to an agent row — which is the normal shape of an ephemeral task-worker — so on that lane Deny was a no-op. 2. The deterministic content-fingerprint duplicate window was 60s, which only covered concurrent in-flight creates. A retry two minutes later saw nothing and filed a second task. Fixes: isAgentTaskCreateToolAvailable() withholds the tool from ephemeral sessions under Deny in both engine lanes (outer execution session, per-step workflow session); isEphemeralCallerAgent fails closed on an unresolvable caller id; the fingerprint window goes 60s -> 10m (clamp ceiling 5m -> 1h). upon_validation keeps the tool, and permanent-agent and human/chat callers are unaffected. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
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> |
||
|
|
2ea5ac206d |
fix(cli-tests,dashboard): align quiet-mode result spies and voiceInput allowlist
CLI JSON/create success lines write via result() (raw stdout) so quiet mode cannot drop machine-readable output; capture that seam in research/update/task tests instead of console.log. Allowlist nested voiceInput settings for the FN-7505 default-description guard and ship locale keys for Voice Input UI. |
||
|
|
b31bee03a8 |
FN-8576: add global quiet flag to CLI
Add a global quiet mode that suppresses informational CLI stdout without hiding requested results. - Parse --quiet/-q and FUSION_QUIET with command and output exemptions. - Route result output and interactive prompts around the reversible stdout gate. - Document the flag and cover quiet output, prompts, and argument parsing. Files changed: .changeset/fn-8576-cli-quiet-flag.md | 7 ++ docs/cli-reference.md | 8 ++ packages/cli/src/__tests__/bin.test.ts | 15 +++ packages/cli/src/__tests__/cli-quiet-mode.test.ts | 63 +++++++++++++ .../__tests__/cli-quiet-prompt-surfaces.test.ts | 33 +++++++ packages/cli/src/bin.ts | 39 ++++++-- packages/cli/src/commands/experiment-finalize.ts | 3 +- packages/cli/src/commands/git.ts | 17 ++-- packages/cli/src/commands/goals.ts | 4 +- packages/cli/src/commands/mission.ts | 8 +- packages/cli/src/commands/node.ts | 4 +- packages/cli/src/commands/onboard.ts | 4 +- packages/cli/src/commands/org-import.ts | 5 +- packages/cli/src/commands/plugin.ts | 14 ++- packages/cli/src/commands/port-prompt.ts | 4 +- packages/cli/src/commands/project.ts | 6 +- packages/cli/src/commands/research.ts | 3 +- packages/cli/src/commands/task.ts | 101 +++++++++++--------- packages/cli/src/commands/update.ts | 3 +- packages/cli/src/commands/workflow.ts | 13 +-- packages/cli/src/output.ts | 104 +++++++++++++++++++++ packages/cli/src/project-resolver.ts | 20 ++-- 22 files changed, 388 insertions(+), 90 deletions(-) Fusion-Task-Id: FN-8576 Fusion-Task-Lineage: 2493d6d5-bbd9-4fc2-b903-950457ef30b0 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
e3dba364d1 |
FN-8564: update bundled Pi runtime to 0.82.0
Update Fusion's matched Pi dependencies and compatibility coverage for version 0.82.0. - Pin Pi AI and coding-agent packages to the exact 0.82.0 release pair. - Refresh provider catalog, supplemental model, auth storage, and Droid thinking coverage. - Add the published CLI patch changeset. Files changed: .changeset/fn-8564-pi-082.md | 7 + packages/cli/package.json | 4 +- packages/cli/src/__tests__/package-config.test.ts | 2 +- packages/core/package.json | 2 +- packages/dashboard/package.json | 2 +- ...ister-model-routes-kimi-k3-supplemental.test.ts | 6 +- packages/engine/package.json | 4 +- .../src/__tests__/provider-registration.test.ts | 4 +- packages/engine/src/auth-storage.ts | 11 +- packages/engine/src/pi.ts | 6 + packages/pi-claude-cli/package.json | 8 +- .../src/thinking-config.ts | 10 +- pnpm-lock.yaml | 176 +++++++++++---------- pnpm-workspace.yaml | 6 +- .../__tests__/check-pi-versions-pinned.test.mjs | 8 +- 15 files changed, 142 insertions(+), 114 deletions(-) Fusion-Task-Id: FN-8564 Fusion-Task-Lineage: 543c5e17-4cb2-446f-9a1c-ec7ec8b8117a Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
749167cbed |
fix(engine-controls): surface a paused engine instead of showing it as idle
A paused engine with nothing running derived executorState "idle", so the footer badge was indistinguishable from a healthy engine waiting for work. That is exactly the state a pause settles into once in-flight tasks drain: triage and planning stall while the board keeps moving, with the pause only visible by opening the Engine Control menu. Pause state now dominates run state; the adjacent counters still report throughput. In the CLI TUI, the global `t` (Git view) branch returned before the Utilities dispatch in the same key handler, so the advertised "[t] Toggle Engine Pause" was unreachable dead UI. It now yields when Utilities owns input, and because the shortcut can stop the board, pausing takes a second `t` within 5s while resuming stays single-press. Tests assert the invariant across the whole state matrix (both pause flags x 0/1/5 running), not just the zero-running repro, plus the TUI routing, the two-press pause, single-press resume, and re-arm behavior. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
ab41554980 |
fix(cli-tests): load task.js once for lock-retry suite under real timers
Full Suite shard 4 timed out runTaskShow lock-exhaustion at the default 5s budget (run 30096660913): each case re-imported the heavy task.js graph under fake timers, so cold CI workers spent the whole budget on transform and left unhandled store.getTask / process.exit races after the timeout. Import task.js once per cached-store describe under real timers via a mutable store holder, and enable fake timers only around the backoff body. Exhaustion cases now finish in ~1ms locally while keeping the shipped entry path. |
||
|
|
e17151cba5 |
fix(cli-tests): align CLI suite with inbox-mail help, beta versions, and planning-session store
Stale-test drift, no product changes: FN-8424 inbox reply help text and fn message inbox --user routing, beta-track prerelease suffixes in version regexes (0.73.0-beta.N), FN-8399 onMigrationProgress in createTaskStoreForBackend, #2400 workflow-docs heading, and the durable planning-session store mocks for fn task plan (0412113de/fdd120232). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
0412113de7 |
fix(dashboard,cli): un-dead-end deleted plan tasks; harden fn task plan per review
Reported bug (screenshot): deleting the task created from a plan left the
session permanently stuck on PLANNING_CREATED_TASK_MISSING — Retry create
replayed the same 409 forever. A linked task absent from the
include-archived scan (task-row authority; a successful scan proves
deletion, not a flaky read) now clears the stale linkage and creates a
fresh task, in both the create-task route and createTaskFromPlanSession;
a still-listed-but-unreadable task keeps failing closed.
Multi-agent review of
|
||
|
|
fdd1202328 |
feat(cli): claim-aware multi-task planning parity for fn task plan
Closes the P1 agent-native gap from the multi-task review: the CLI and fn_task_plan pi tool created tasks via a raw store.createTask with no proposalClaimId — no idempotency, no session linkage, and tasks outside the epoch sequence, so a later dashboard Proceed would duplicate them. - New shared createTaskFromPlanSession in @fusion/dashboard/planning: the agent-surface twin of POST /planning/create-task (epoch-derived claim key, claim/finalize/reconcile/release CAS lifecycle with the 30s stale-lease takeover, formatPlanningPlanMd task shape, plan/original- description documents, validate-on-create, generating guard). - runTaskPlan creates through it (making the FN-7734 retry wrapper genuinely safe), prints the session id, and offers an interactive keep-refining loop that creates further tasks from the evolved plan. - fn task plan --resume <sessionId> / fn_task_plan resumeSessionId reopen an existing session — even a validated one whose task exists — and the no-question resume regenerates the interview via a refine turn, which rotates the creation epoch server-side. Tests: CLI suite pins claim-aware creation, the continue prompt, and the resume flow; dashboard suite pins createTaskFromPlanSession idempotent replay and epoch-aware second creation. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
11fab37379 |
feat(ci): incremental caching for the PR merge-gate jobs (Gate, Build, Typecheck) (#2421)
## Summary
Every PR's blocking checks were dominated by redundant full rebuilds,
not by tests. Measured on recent runs: the Gate job spent ~6 of its ~7.5
min on a cold `pnpm build` (the exact-key dist cache missed on virtually
every PR) for ~45s of actual boot smoke + gate tests; Build ran ~8 min
and Typecheck ~4 min, both fully cold every time. Expected end state
once the warm job has run on main: all four blocking checks in roughly
2–4 min wall-clock.
### Gate job
- New `gate-dist-*` cache namespace with `restore-keys`, additionally
caching `.fusion/cache/plugin-build-cache.json` (build-workspace's
per-package content-hash skip cache) and `packages/cli/dist`. The
always-run `pnpm build` reconciles a near-match restore by content hash
and rebuilds only the packages the PR touched. This is safe *because*
the gate builds after restoring — the shard jobs' "no restore-keys" rule
(FN-4232/FN-4605 stale-dist incidents) still stands there, since they
consume dist without building.
- `FUSION_CLI_FULL_PACKAGE=0` on the gate build: skips the multi-minute
CLI desktop/plugins/DTS packaging tail nothing in the gate consumes
(same shape `pnpm verify:fast` proves locally). Full CLI packaging
coverage stays blocking in the Build job.
### Build job
- Restore-only tap (`actions/cache/restore`) of the same warmed cache.
Restore-only because this job runs FULL CLI packaging (`CI=true`) and
saving that shape would swap the cache's canonical fast-CLI contents out
from under the Gate job. Its distinctive coverage is preserved:
`ensureFullPackageCliPlanned` force-plans the CLI in full mode
regardless of cache state.
### Typecheck job
- Caches per-package tsc incremental buildinfo — self-validating (tsc
hashes every input against it and re-checks whatever changed), so
`restore-keys` is correctness-neutral by construction.
- **Fixes a real incrementality bug:** `tsconfig.json` and
`tsconfig.app.json` in the dashboard both inherited
`${configDir}/dist/.tsbuildinfo` from `tsconfig.base.json`, so the two
typecheck programs clobbered each other's buildinfo and re-checked the
full program every run — incremental typechecking never worked for the
dashboard, in CI or locally. `tsconfig.app.json` now writes
`dist/.tsbuildinfo-app`. Measured: dashboard typecheck 44s cold → 5.6s
warm.
### Warm job (full-suite.yml, push to main)
- New `warm-gate-build-cache` job saves both caches from main on every
push. Caches saved on a PR merge ref are invisible to other PRs, so
without this every PR's *first* run would still build/check cold.
## Guardrails
`ci-workflow.test.ts` pins the coupled invariants so they can't drift
apart silently:
- restore-keys requires the reconciling `pnpm build` after restore,
before boot smoke
- the mtime-defeating seed step stays exact-hit-only
- byte-identical cache path lists between the Gate/Build/warm blocks
(actions/cache versions caches by path list — a drifted list makes
caches mutually invisible)
- Build stays restore-only and must NOT opt out of full CLI packaging
- Typecheck cache shape + the distinct dashboard app buildinfo path
## Notes
- First PR runs after this lands still build cold until the warm job has
run once on main.
- No changeset: CI config + test-only per AGENTS.md.
## Verification
- `ci-workflow.test.ts` + `package-config.test.ts`: 106 tests pass
- Dashboard typecheck run twice locally: 44s cold → 5.6s warm, both
`.tsbuildinfo` and `.tsbuildinfo-app` written, exit 0
- Cache block path/key parity verified programmatically across both
workflow files
🤖 Generated with [Claude Code](https://claude.com/claude-code)
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **Performance**
* Improved CI build and type-check performance through incremental
caching.
* Added cache warming from the main branch to speed up pull request
checks.
* Enabled faster CLI packaging during gate validation while retaining
full packaging coverage elsewhere.
* **Bug Fixes**
* Prevented dashboard TypeScript build information from being
overwritten, preserving incremental type-checking reliability.
* **Tests**
* Added coverage to verify CI cache behavior, build ordering, cache
paths, and packaging modes.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
---------
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
|
||
|
|
e5caea542a |
FN-8544: gate mission remediation behind autopilot
Keep mission validation report-only until an operator explicitly enables autopilot. - Gate validator-created remediation features and task dispatch behind mission autopilot. - Audit attributed status and autopilot transitions atomically across mission stores. - Expose mission autonomy controls and document the opt-in lifecycle. Files changed: .changeset/fn-8544-mission-autonomy-audit.md | 7 ++ docs/missions.md | 10 +- packages/cli/src/extension.ts | 26 ++++- .../__tests__/postgres/mission-store.pg.test.ts | 27 +++++ packages/core/src/async-mission-store.ts | 70 +++++++++++-- packages/core/src/index.gate.ts | 3 + packages/core/src/index.ts | 3 + packages/core/src/mission-store.ts | 113 +++++++++++++-------- packages/core/src/mission-types.ts | 22 ++++ .../dashboard/app/components/MissionManager.tsx | 1 + packages/dashboard/src/mission-routes.ts | 25 +++-- .../src/__tests__/agent-mission-tools.test.ts | 17 +++- .../src/__tests__/mission-execution-loop.test.ts | 21 ++++ packages/engine/src/agent-heartbeat.ts | 4 +- packages/engine/src/agent-tools.ts | 30 +++++- packages/engine/src/executor.ts | 5 +- packages/engine/src/mission-autopilot.ts | 51 +++++----- packages/engine/src/mission-execution-loop.ts | 49 ++++++--- packages/engine/src/triage.ts | 5 +- 19 files changed, 377 insertions(+), 112 deletions(-) Fusion-Task-Id: FN-8544 Fusion-Task-Lineage: 23a69923-5a19-407e-9fe2-8973c166ee9a Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
0d355f371c |
FN-8547: add local OpenAI-compatible provider onboarding
Add guided onboarding for OpenAI-compatible local model providers. - Add custom/local provider prompts and atomic models registry updates. - Preserve registry fields, validate endpoint configuration, and support Qwen thinking compatibility. - Document setup and cover registry persistence with onboarding tests. Files changed: .changeset/fn-8547-local-provider-onboarding.md | 7 + docs/cli-reference.md | 13 ++ docs/settings-reference.md | 35 ++++- packages/cli/src/commands/__tests__/onboard.test.ts | 55 ++++++- packages/cli/src/commands/onboard.ts | 161 ++++++++++++++++++++- 5 files changed, 261 insertions(+), 10 deletions(-) Fusion-Task-Id: FN-8547 Fusion-Task-Lineage: 740221de-fce8-43ae-a095-13b8abf1904a Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
961edf2145 |
fix(dashboard): mount plugin routes without engine
Keep plugin-defined APIs reachable in UI-only dashboards by sourcing routes from the plugin loader when no engine runner exists. |
||
|
|
2f014f5c0c |
fix(cli): install agent-browser across Windows, Linux, and macOS (#2405)
## Summary Fusion npm installs now expose a working `agent-browser` command on Windows, Linux, and macOS. Fusion publishes a top-level shim backed by the exact pinned native package, preserving the existing Browser Verification probe contract on every supported desktop OS. ## Validation - Added a packed consumer-install matrix for `ubuntu-latest`, `macos-latest`, and `windows-latest`. - Each platform executes npm's generated shim, verifies `agent-browser 0.26.0`, and asserts the matching native binary is installed. - Confirmed the packed Fusion manifest, installed dependency, and command output all retain the exact declared version pin. - Passed 101 focused CLI workflow/package tests, full workspace lint and typecheck, strict changeset validation, and a real macOS ARM64 packed-install smoke. ## Post-Deploy Monitoring & Validation - Search task logs for `agent-browser not found on PATH`, `Missing native executable`, and Browser Verification availability warnings. - Healthy signal: npm installs on Windows, Linux, and macOS resolve `agent-browser --version` without missing-shim or native-payload errors. - Failure signal: command resolution errors, version-pin mismatches, missing native payloads, or increased browser-verification fast-bails. - Validation window: first release cycle after publish; owner: Fusion maintainers. - Mitigation: revert the dependency and top-level shim if install compatibility regresses. --- [](https://github.com/EveryInc/compound-engineering-plugin) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added cross-platform `agent-browser` installation through Fusion CLI. * Exposed an `agent-browser` command for Windows, macOS, and Linux. * Pinned the included `agent-browser` version to ensure consistent installations. * **Bug Fixes** * Improved reliability of platform-specific executable selection and command setup across supported operating systems. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
241a5c94ea |
chore: bump @earendil-works/pi to 0.81.1 (#2399)
## Summary - Bump `@earendil-works/pi-ai` and `@earendil-works/pi-coding-agent` from **0.80.10 → 0.81.1** (exact matched pins). - Update `pnpm-workspace.yaml` overrides so floating `*` consumers (`droid-cli`, `pi-llama-cpp`, runtime plugins) stay on the same ModelRuntime surface. - Refresh pin-guard tests, package-config assertions, and FNXC notes for the new pin. ## What's new in pi 0.81.x - Qwen Token Plan providers - Expanded usage accounting (tools/compaction/branch summaries) - Resilient compaction retries + retry lifecycle events - Full provider-extension registration API - Built-in llama.cpp router management - Provider/catalog fixes (Bedrock env credentials, OpenAI Responses early-stream retry, Codex 272K defaults, extension stream-fallback restore) ## Test plan - [x] `scripts/check-pi-versions-pinned` (4/4) - [x] Typecheck: core, engine, dashboard, cli, pi-claude-cli - [x] `package-config.test.ts` (35) - [x] `provider-registration.test.ts` (14) - [x] `auth-storage-concurrency` + `model-registry-refresh` (15) - [x] `register-model-routes-kimi-k3-supplemental` (1) - [ ] CI gate green - [ ] Spot-check Anthropic OAuth + API key session - [ ] Spot-check openai-codex model picker / supplemental models <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Updates** * Updated the bundled Pi runtime to version 0.81.1. * Added support for newer models and providers, including Qwen Token Plan. * Improved usage accounting and session reliability. * Strengthened compaction retry handling and provider catalog accuracy. * Added support for the expanded maximum thinking level. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
396090fc03 |
fix(startup): bound model registry refresh so dashboard cannot hang
Post-extension modelRegistry.refresh() had no timeout, so a hung remote catalog fetch left the TUI on "Loading extensions…" forever. Use a shared 15s-bounded refresh across dashboard/serve/daemon and related registration paths. |
||
|
|
5f0502e166 |
FN-8452: reject invalid update flags and announce beta releases
Make update commands fail clearly on invalid arguments while helping stable users discover newer beta releases. - Parse update and upgrade options strictly, rejecting unknown, duplicate, and malformed flags before running an update - Show a live-registry beta availability notice for stable human-readable output without affecting JSON or cached results - Add CLI coverage, beta bootstrap documentation, and a patch changeset Files changed: .changeset/fn-8452-update-unknown-flags.md | 7 + RELEASING.md | 2 +- docs/cli-reference.md | 4 + docs/getting-started.md | 2 + packages/cli/src/__tests__/bin-update-args.test.ts | 75 ++++++++++ packages/cli/src/bin.ts | 25 +--- packages/cli/src/commands/__tests__/update.test.ts | 44 ++++++ packages/cli/src/commands/update.ts | 162 +++++++++++++++++++-- 8 files changed, 285 insertions(+), 36 deletions(-) Fusion-Task-Id: FN-8452 Fusion-Task-Lineage: b29a1ce5-5a40-40ec-ac18-07107fa18344 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
3962222863 |
FN-8424: route CLI chat replies through inbox mail
Route agent replies to the correct CLI or dashboard mailbox with bounded polling deadlines. - add reply-parent routing validation and CLI/dashboard inbox selection - preserve named mailbox conversations while handling per-message reply deadlines - document chat and inbox interfaces and cover deadline and routing regressions Files changed: .changeset/fn-8424-cli-chat-reply-routing.md | 7 + docs/agents.md | 20 +- docs/cli-reference.md | 29 +- packages/cli/src/bin.ts | 15 +- packages/cli/src/commands/__tests__/chat.test.ts | 262 +++++++++--------- .../cli/src/commands/__tests__/message.test.ts | 12 + packages/cli/src/commands/chat.ts | 293 ++++++++++++--------- packages/cli/src/commands/message.ts | 18 +- ...tools-send-message-recipient-validation.test.ts | 86 +++++- packages/engine/src/agent-heartbeat-prompts.ts | 8 +- packages/engine/src/agent-tools.ts | 71 +++-- 11 files changed, 523 insertions(+), 298 deletions(-) Fusion-Task-Id: FN-8424 Fusion-Task-Lineage: 28d0ef88-717e-4f39-8880-64d2fef94706 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
9db0ffc1f9 |
FN-8425: route CLI chat through agent inbox
Route CLI chat messages through durable, named agent mailbox conversations. - Add conversation IDs and parsing for CLI chat sessions. - Filter CLI chat history and replies by mailbox conversation identity. - Surface conversation IDs to agents and document the inbox-based transport. Files changed: .changeset/fn-8425-cli-chat-conversation.md | 7 + docs/agents.md | 11 +- docs/cli-reference.md | 15 +- packages/cli/src/__tests__/bin-chat-args.test.ts | 34 +++++ packages/cli/src/bin.ts | 45 ++---- packages/cli/src/commands/__tests__/chat.test.ts | 162 +++++++++++++++++++++ packages/cli/src/commands/chat.ts | 132 +++++++++++++++-- packages/core/src/types/messages.ts | 6 + .../__tests__/agent-tools-read-messages.test.ts | 48 ++++++ packages/engine/src/agent-tools.ts | 10 +- 10 files changed, 418 insertions(+), 52 deletions(-) Fusion-Task-Id: FN-8425 Fusion-Task-Lineage: 5091f49f-1f12-4ff7-8d21-48f008cf984e Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
81abe53957 |
FN-8430: simplify TUI log timestamps
Keep dashboard TUI log rows focused on useful message content. - Show capture times as HH:MM:SS across list, expanded, and copied log output. - Strip leading detailed upstream timestamps from list and clipboard text while preserving raw expanded messages. - Add regression coverage and a patch changeset. Files changed: .changeset/fn-8430-tui-log-timestamps.md | 7 ++ .../commands/dashboard-tui/__tests__/app.test.tsx | 93 +++++++++++++++++++++- packages/cli/src/commands/dashboard-tui/app.tsx | 33 ++++++-- 3 files changed, 125 insertions(+), 8 deletions(-) Fusion-Task-Id: FN-8430 Fusion-Task-Lineage: b656dfa1-3c16-4a6f-8ba8-a936fdfdd42d Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
4f0d89e106 |
FN-8419: safeguard project partition reconciliation
Safely reconcile fallback and registered project partitions during dashboard startup. - Merge duplicate partition rows with fallback data taking precedence. - Validate unique indexes and foreign-key dependencies before rekeying. - Bind safely after failed promotion and stop non-retryable dashboard failures. - Add PostgreSQL reconciliation and supervisor coverage. Files changed: .changeset/fn-8419-rekey-partition-merge.md | 7 + packages/cli/src/bin.ts | 5 +- .../commands/__tests__/dashboard-supervise.test.ts | 16 +- packages/cli/src/commands/dashboard.ts | 41 ++- .../src/__tests__/postgres/schema-applier.test.ts | 178 +++++++++++- packages/core/src/async-secrets-store.ts | 9 +- packages/core/src/index.ts | 8 +- packages/core/src/postgres-errors.ts | 9 + packages/core/src/postgres/index.ts | 5 + packages/core/src/postgres/migration-stamping.ts | 318 ++++++++++++++++----- packages/core/src/postgres/startup-factory.ts | 49 +++- packages/core/src/process-supervisor.ts | 3 + packages/core/src/task-store/async-persistence.ts | 7 +- 13 files changed, 546 insertions(+), 109 deletions(-) Fusion-Task-Id: FN-8419 Fusion-Task-Lineage: bfc54e40-a31e-4b61-b6ae-01eb147efde1 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
ddd1316644 |
FN-8413: add ACP SDK to published CLI dependencies
Ensure packed Fusion installs the ACP SDK required by the vendored Claude CLI extension. - Declare the compatible ACP SDK version in the CLI runtime dependencies. - Guard published-manifest dependency coverage and clean-install resolution with tests. - Add a patch changeset for the published package fix. Files changed: .changeset/fn-8413-pi-claude-cli-acp-sdk-dep.md | 7 ++ packages/cli/package.json | 1 + packages/cli/src/__tests__/package-config.test.ts | 26 +++++++ .../pi-claude-cli-publish-resolve.smoke.test.ts | 81 ++++++++++++++++++++++ pnpm-lock.yaml | 3 + 5 files changed, 118 insertions(+) Fusion-Task-Id: FN-8413 Fusion-Task-Lineage: c3cb8cc1-0be8-4196-b43e-eacd5837105d Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
5c67b19cb2 |
FN-8394: rescue deterministic quarantined tests
Restore reliable test coverage and delete quarantined tests that could not be rescued. - Replace process- and database-dependent tests with bounded dependency seams - Restore stabilized CLI, dashboard, and plugin test coverage - Remove unrescuable bundle and merge-worktree test suites and clear the quarantine ledger Files changed: packages/cli/src/__tests__/bundle-output.test.ts | 519 ------------ .../src/commands/__tests__/task-lock-retry.test.ts | 10 + packages/cli/vitest.config.ts | 8 - .../TaskDetailModal.tab-persistence.test.tsx | 2 +- .../__tests__/TaskDetailModal.test-helpers.ts | 7 + .../src/__tests__/dev-server-process.test.ts | 391 ++++----- packages/dashboard/src/dev-server-process.ts | 22 +- packages/dashboard/vitest.config.ts | 21 +- .../merge-reuse-task-worktree.slow.test.ts | 876 --------------------- packages/engine/vitest.config.ts | 7 - .../src/__tests__/process-lifecycle.test.ts | 21 +- .../fusion-plugin-grok-runtime/vitest.config.ts | 2 - .../src/__tests__/async-quality-store.pg.test.ts | 148 +++- plugins/fusion-plugin-quality/vitest.config.ts | 3 +- scripts/lib/test-quarantine.json | 43 +- 15 files changed, 323 insertions(+), 1757 deletions(-) Fusion-Task-Id: FN-8394 Fusion-Task-Lineage: e949b33e-b8d5-4f73-a002-e550b97ee125 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
e4a032d9d9 |
FN-8399: expose incomplete migration status on dashboard
Expose durable SQLite-to-PostgreSQL migration state through dashboard health and banners. - Read per-project running and failed migration markers from PostgreSQL - Surface degraded migration state in health endpoints and dashboard banners - Preserve migration context across CLI and runtime startup paths - Document the recovery workflow and add a patch changeset Files changed: .changeset/FN-8399-migration-status-dashboard.md | 7 +++ docs/storage.md | 5 ++ packages/cli/src/commands/daemon.ts | 19 +++++++ packages/cli/src/commands/desktop.ts | 6 +++ packages/cli/src/commands/serve.ts | 20 +++++++- packages/core/src/index.ts | 2 + packages/core/src/postgres/index.ts | 2 + packages/core/src/postgres/sqlite-migrator.ts | 43 ++++++++++++++++ packages/dashboard/app/api/health.ts | 7 ++- .../app/components/dashboard/DashboardBanners.tsx | 18 ++++++- .../dashboard/__tests__/DashboardBanners.test.tsx | 12 ++++- .../__tests__/dashboard-postgres-health.test.ts | 45 +++++++++++++++++ .../dashboard/src/dashboard-postgres-health.ts | 58 ++++++++++++++++++++++ packages/dashboard/src/server.ts | 27 ++++++++-- packages/engine/src/project-engine-manager.ts | 4 ++ packages/engine/src/project-runtime.ts | 9 +++- packages/engine/src/runtimes/in-process-runtime.ts | 1 + 17 files changed, 275 insertions(+), 10 deletions(-) Fusion-Task-Id: FN-8399 Fusion-Task-Lineage: e196d6c4-ea9a-48ba-bedc-9e6fa44c33d3 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
3e376b98ba |
FN-8369: centralize GitHub issue import deduplication
Use provenance-first deduplication consistently across dashboard, CLI, and extension GitHub imports. - Prioritize persisted sourceIssue and legacy metadata over editable descriptions. - Reuse the dashboard deduplication helper for CLI and extension imports. - Prevent duplicate issue creation within a dashboard batch import. Files changed: .changeset/fn-8369-github-import-dedup.md | 7 ++++++ packages/cli/src/__tests__/extension.test.ts | 27 ++++++++++++++++++++++ packages/cli/src/commands/__tests__/task.test.ts | 20 ++++++++++------ packages/cli/src/extension.ts | 25 +++++++++++--------- packages/dashboard/src/__tests__/github.test.ts | 26 ++++++++++++++++----- packages/dashboard/src/__tests__/routes-github.test.ts | 22 ++++++++++++++++++ packages/dashboard/src/github.ts | 27 +++++++++++++--------- 7 files changed, 119 insertions(+), 35 deletions(-) Fusion-Task-Id: FN-8369 Fusion-Task-Lineage: 8eb6d19d-bd0e-487a-9f11-8945d744b7df Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
b2c784b3f8 |
FN-8381: remove flaky dist-barrel test
Remove the repeatedly quarantined extension dist-barrel test while retaining source-level listing coverage. - Delete the CPU-bound dist-barrel regression test after its fourth quarantine cycle. - Remove its quarantine exclusion and ledger entry. - Document retained source-level formatting and truncation coverage. Files changed: .../src/__tests__/extension-dist-barrel.test.ts | 204 --------------------- packages/cli/src/__tests__/extension.test.ts | 4 +- packages/cli/vitest.config.ts | 6 +- scripts/lib/test-quarantine.json | 5 - 4 files changed, 6 insertions(+), 213 deletions(-) Fusion-Task-Id: FN-8381 Fusion-Task-Lineage: ba6e61e1-fba1-4308-9d9d-1d3f387aa5e9 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
954ebe816a |
FN-8362: isolate agent lifecycle pauses and backlog claims
Keep agent lifecycle transitions independent from task pause state and make automatic backlog pickup executor-first. - Prevent agent stop, sleep, resume, and heartbeat recovery from changing assigned task pause state - Limit engineer automatic backlog pickup to explicit opt-in while preserving explicit routing - Document CLI and extension lifecycle behavior and cover the boundaries with regression tests Files changed: .../cli/skill/fusion/references/extension-tools.md | 4 +-- .../skill/fusion/references/fusion-capabilities.md | 4 +-- packages/cli/src/__tests__/extension.test.ts | 13 +++++++ packages/cli/src/commands/__tests__/agent.test.ts | 27 ++++++++++++-- packages/cli/src/commands/agent.ts | 4 +++ packages/cli/src/extension.ts | 8 +++-- .../core/src/__tests__/agent-role-policy.test.ts | 7 +++- packages/core/src/agent-role-policy.ts | 7 ++++ .../src/__tests__/heartbeat-executor.test.ts | 35 +++++++++--------- packages/engine/src/__tests__/self-healing.test.ts | 30 ++++++++++++++++ packages/engine/src/agent-heartbeat.ts | 42 ++++++++++++++++------ packages/engine/src/self-healing.ts | 12 +++++++ 12 files changed, 157 insertions(+), 36 deletions(-) Fusion-Task-Id: FN-8362 Fusion-Task-Lineage: a1fb37f3-f95e-4eb7-9fbf-bdc0ab5d5f9e Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
2302fb8a3d |
feat: add beta/stable release tracks with switchable update channel (#2345)
## Summary Fusion can now ship on two release tracks. Betas are cut from `main` as `vX.Y.Z-beta.N` (npm dist-tag `beta`, GitHub prerelease), stable releases are promoted to a long-lived `release` branch and published to `latest`, and users pick their track with the new `updateChannel` global setting — via **Settings → General → Release channel** or `fn update --channel <stable|beta>`. Previously everything was single-track: every publish landed on `latest` and every update surface could only see it. | | beta | stable | |---|---|---| | Cut from | `main` | `release` branch | | Version | `X.Y.Z-beta.N` (changesets pre-mode) | `X.Y.Z` | | npm dist-tag | `beta` | `latest` | | GitHub Release | prerelease | latest | | Homebrew tap / X draft | skipped | bumped / printed | ## How releasing works now `pnpm release` prompts for the channel and **defaults to beta**, so day-to-day releases are betas; stable is always an explicit choice. Choosing stable from `main` triggers assisted promotion: the script proposes the newest beta tag reachable from HEAD, verifies `release` fast-forwards to it, then runs the whole stable release inside a temporary git worktree on `release` — the primary checkout never leaves `main`. Changesets pre-mode preserves changeset files across betas, so the promoted stable release aggregates every changeset since the last stable into one clean changelog entry. ## Design decisions - **Every publish path names an explicit `--tag`.** A beta accidentally landing on `latest` is the one unrecoverable failure of a dual-track scheme, so nothing relies on npm's implicit default (`release.mjs`, `version.yml`). - **Beta channel resolves to semver-max of `latest` and `beta`**, so beta users are offered each promoted stable once it overtakes their prerelease. Switching beta → stable never downgrades; `fn update --channel stable --force` is the explicit escape hatch. - **One comparator instead of three.** CLI, dashboard, and desktop each had their own `isRemoteNewer` that ignored prerelease identifiers — `0.73.0-beta.2`, `-beta.3`, and `0.73.0` all compared equal, which breaks the moment any beta exists. They now share full SemVer-precedence helpers (`compareVersions`, `resolveUpdateTargetVersion`) from `@fusion/core`. - **Installs pin exact versions** (`@runfusion/fusion@0.73.0-beta.2`), never a dist-tag, so an install can't silently land on the wrong track. - **Desktop channels via electron-updater manifests.** Beta tags build desktop artifacts with `publish.channel=beta` (emitting `beta*.yml`); the app sets `channel`/`allowPrerelease` from the shared setting, re-read on every manual check. - **Update caches are channel-stamped** — a cache written for one channel is never served to the other, so switching tracks takes effect on the next check instead of after TTL. ## Test plan - New unit coverage: SemVer precedence + channel resolution in `@fusion/core` (30), channel behavior of the dashboard update check (28, incl. 9 new) and `fn update` (16, incl. 8 new: persist `--channel`, no-downgrade, `--force`, cache channel mismatch). - `pnpm verify:fast` green (scoped typecheck, builds, CLI build, boot smoke); desktop + settings-section suites green. - `release.mjs` dry-run matrix exercised by hand: channel prompt (default/override/invalid), branch preflights per channel, assisted-promotion target selection, fast-forward guard against a diverged `release` branch, and bootstrap when no `release` branch exists. - Not exercised live: an end-to-end publish (needs TTY authorization + real npm publish). First real run is the first `pnpm release --channel beta`. --- [](https://github.com/EveryInc/compound-engineering-plugin)  <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added beta and stable release channels across CLI, dashboard, and desktop updates. * Users can select a channel via Settings or `fn update --channel <stable|beta>` (stored as a global default). * Desktop beta releases now generate beta update manifests and publish as prereleases. * **Documentation** * Expanded release-track, settings, and CLI references to explain channel semantics and workflows. * **Bug Fixes** * Updates now pin the resolved version per channel, improve version comparison, and prevent unintended cross-channel downgrades unless `--force` is used. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
aa401123ea |
fix: make Windows updates and CE personas reliable (#2340)
## Summary Windows installs with slow native dependencies now get five minutes to finish, and a real timeout is reported as an actionable terminal retry instead of a wall of preceding npm deprecation warnings. Registry `ETIMEDOUT` errors keep their network diagnosis, including after the legacy-bin `--force` retry. Compound Engineering personas are now included in the published CLI bundle, with complete source-to-staged coverage for all persona definitions and a clear startup error if the bundled assets are missing or empty. The PostgreSQL statement visible in the report was validated by the existing real-Postgres schema reapply test. Its actual `caused by` detail was truncated, so this PR deliberately makes no speculative database change. ## Validation - Dashboard updater tests: 22 passed - CLI updater tests: 16 passed - CE persona installer tests: 7 passed - Published bundle persona assertion: passed against every source persona - CLI and CE plugin typechecks: passed - Changed production/config lint and strict changeset validation: passed - Real PostgreSQL schema reapply integration test: passed --- [](https://github.com/EveryInc/compound-engineering-plugin) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Bug Fixes** * Windows CLI and dashboard updates now allow up to five minutes for installation and restore Compound Engineering agent personas during npm installs. * Update failures now surface clearer, terminal timeout guidance (while preserving specific network connection diagnostics) and avoid misleading “deprecated”/generic timeout text. * Persona assets are reliably included in plugin builds and bunded persona installation now errors clearly when definitions are missing or empty. * **Tests** * Expanded update and bundling coverage for the new 5-minute timeout and error-handling scenarios. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
4cc8002c97 |
FN-8341: rework planning interviews for reactive validation
Make planning interviews user-controlled and context-aware from validation through task creation. - Replace fixed-depth and deepening checkpoints with reactive follow-up questions - Add explicit planning validation and require it before creating or breaking down work - Expose validation through the API, CLI, routes, types, documentation, and release notes - Update localized copy and PlanningModeModal mocks and assertions for the revised workflow Files changed: .changeset/fn-8341-deepening-checkpoint-removal.md | 7 + .changeset/fn-8341-planning-reactive-backend.md | 7 + docs/dashboard-guide.md | 8 +- packages/cli/src/commands/task.ts | 12 +- packages/core/src/index.gate.ts | 2 +- packages/core/src/index.ts | 2 +- packages/core/src/types.ts | 38 +- packages/dashboard/app/api/legacy.ts | 15 +- .../dashboard/app/components/PlanningModeModal.tsx | 192 +----- .../__tests__/PlanningModeModal.autosize.test.tsx | 4 + .../__tests__/PlanningModeModal.initial.test.tsx | 50 +- .../PlanningModeModal.planning-flow.test.tsx | 442 +------------ .../__tests__/PlanningModeModal.test-helpers.ts | 1 + .../PlanningModeModal.ui-interactions.test.tsx | 3 + .../__tests__/planning-infinite-interview.test.ts | 221 +++++++ packages/dashboard/src/planning.ts | 711 ++++++--------------- .../src/routes/register-planning-subtask-routes.ts | 64 +- packages/i18n/locales/en/app.json | 1 - packages/i18n/locales/es/app.json | 1 - packages/i18n/locales/fr/app.json | 1 - packages/i18n/locales/ko/app.json | 1 - packages/i18n/locales/zh-CN/app.json | 1 - packages/i18n/locales/zh-TW/app.json | 1 - 23 files changed, 536 insertions(+), 1249 deletions(-) Fusion-Task-Id: FN-8341 Fusion-Task-Lineage: 102f88af-2675-43f5-8e08-5403a0e17da8 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
9bc0eb6943 |
fix(FN-8277): prevent replayed duplicate follow-up tasks (#2319)
## Summary Repeated review and executor sessions could replay a follow-up creation step and produce another live task whenever the wording changed. In the incident behind this fix, 21 creation calls for three intended follow-ups left 18 duplicate tasks. Agent-created tasks now retain their parent and agent provenance across step sessions, heartbeats, and the published CLI surface. Same-parent paraphrases converge on the existing task through a serialized pre-check and a database-backed intent claim, while distinct sibling actions remain separate. Candidate lookup is parent-indexed, uniqueness failures abort creation, and canonical reuse no longer emits misleading creation audit events or workflow claims. Related: FN-8277 ## Validation - Core duplicate guard and intake: 24 tests passed - Engine task creation and heartbeat: 154 tests passed - CLI extension: 66 tests passed, 95 skipped - Core, engine, and CLI typechecks passed - Core, engine, and CLI builds passed before rebase; the rebase was conflict-free - Scoped lint, strict changeset validation, and diff checks passed <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Bug Fixes** * Prevented retried agent steps from creating duplicate follow-up tasks. * Improved parent-scoped deduplication for paraphrased follow-ups while preserving distinct sibling actions. * Preserved parent-task and agent context for created follow-ups. * Concurrent follow-up requests are now serialized/deduplicated so duplicates link to the existing task instead of showing as newly created. * Updated agent follow-up/heartbeat activity so reused follow-ups no longer appear in run results as fresh creations. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
baa1baff9f |
FN-8283: add secret-scrubbed organization bundle CLI
Add portable, secret-scrubbed organization export and import workflows. - Assemble agents, raw skills, routines, automations, and settings into versioned bundles. - Add org-export and org-import CLI commands with dry-run and collision controls. - Preserve existing skills by default or materialize suffixed destinations, with CLI coverage. Files changed: .changeset/fn-8283-org-bundle.md | 7 + docs/cli-reference.md | 9 ++ docs/secrets.md | 10 ++ packages/cli/src/__tests__/bin.test.ts | 17 ++ packages/cli/src/bin.ts | 26 ++- .../cli/src/commands/__tests__/org-export.test.ts | 30 ++++ .../cli/src/commands/__tests__/org-import.test.ts | 31 ++++ packages/cli/src/commands/org-export.ts | 19 +++ packages/cli/src/commands/org-import.ts | 18 +++ packages/core/src/__tests__/org-bundle.test.ts | 69 ++++++++ packages/core/src/index.ts | 17 ++ packages/core/src/org-bundle.ts | 176 +++++++++++++++++++++ 12 files changed, 428 insertions(+), 1 deletion(-) Fusion-Task-Id: FN-8283 Fusion-Task-Lineage: 93877746-8e57-4c79-bdbe-8e4ec7a2efe6 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
8d1620ea23 |
FN-8294: expose action-gated Mission hierarchy tools
Expose project-scoped Mission hierarchy operations to engine agents and eligible dashboard chat sessions. - Add Mission, milestone, slice, and feature tool definitions backed by MissionStore - Classify hierarchy mutations for action and permanent-agent approval gates - Wire gated tool access through triage, executor, heartbeat, CLI, and chat lanes - Cover tool availability, mutation gating, and chat integration with tests and documentation Files changed: .changeset/fn-8294-mission-engine-tools.md | 7 ++ docs/missions.md | 8 ++ packages/cli/src/extension.ts | 2 +- .../dashboard/src/__tests__/chat-manager.test.ts | 48 +++++++++ packages/dashboard/src/__tests__/chat.test.ts | 1 + packages/dashboard/src/chat.ts | 113 ++++++++++++++++++++- .../src/__tests__/agent-mission-tools.test.ts | 43 ++++++++ packages/engine/src/__tests__/triage.test.ts | 34 ++++++- packages/engine/src/agent-heartbeat.ts | 4 +- packages/engine/src/agent-tools.ts | 64 ++++++++++++ packages/engine/src/executor.ts | 2 + packages/engine/src/gating-classifications.ts | 11 ++ packages/engine/src/index.ts | 17 ++++ packages/engine/src/triage.ts | 111 ++++++++++++++++++++ 14 files changed, 457 insertions(+), 8 deletions(-) Fusion-Task-Id: FN-8294 Fusion-Task-Lineage: ab0f248b-8a38-40e3-b297-79f9dbe18075 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
afb2ed0650 |
FN-8271: restore quarantined CLI tests under shard load
Restore affected CLI and engine tests by removing load-amplifying fixture work and synchronizing fake-timer recovery. - Replace the dist-barrel PostgreSQL fixture with an injected in-memory task store. - Move mission and goal tool coverage to the shared PostgreSQL harness and complete plugin-store mocks. - Return rescued CLI and heartbeat tests to default lanes and clear their quarantine records. Files changed: .../src/__tests__/extension-dist-barrel.test.ts | 90 ++++++++-------------- .../__tests__/extension-mission-goal-tools.test.ts | 27 ++++--- packages/cli/src/commands/__tests__/plugin.test.ts | 11 +++ packages/cli/vitest.config.ts | 21 +---- .../src/__tests__/heartbeat-error-recovery.test.ts | 33 ++++---- packages/engine/vitest.config.ts | 7 +- scripts/lib/test-quarantine.json | 78 +------------------ 7 files changed, 84 insertions(+), 183 deletions(-) Fusion-Task-Id: FN-8271 Fusion-Task-Lineage: 212a3ec7-db6b-4e80-97c3-1c704822cf60 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
7c23771433 |
FN-8265: add task follow-up proposal creation
Enable configured ephemeral workers to propose and create follow-up tasks from mailbox messages. - Add persisted task-proposal claim state, migrations, and async messaging APIs. - Register task-proposal creation routes, SSE events, agent tool support, and CLI integration. - Add mailbox creation controls, settings, documentation, localization, and regression coverage. Files changed: .changeset/fn-8265-task-follow-up-policy.md | 7 ++ docs/dashboard-guide.md | 2 +- docs/settings-reference.md | 12 ++- packages/cli/src/extension.ts | 34 ++++--- .../src/__tests__/postgres/sqlite-migrator.test.ts | 14 ++- .../postgres/task-proposal-claim.pg.test.ts | 51 +++++++++++ packages/core/src/async-message-store.ts | 39 ++++++++ packages/core/src/index.gate.ts | 4 +- packages/core/src/index.ts | 4 +- packages/core/src/message-store.ts | 46 ++++++++++ .../core/src/postgres/migrations/0000_initial.sql | 2 + .../migrations/0020_task_proposal_claim.sql | 4 + packages/core/src/postgres/schema-applier.ts | 13 ++- packages/core/src/postgres/schema/project.ts | 3 + packages/core/src/settings-schema.ts | 19 +++- packages/core/src/task-store/async-persistence.ts | 2 +- packages/core/src/task-store/persistence.ts | 4 +- packages/core/src/task-store/serialization.ts | 1 + packages/core/src/task-store/task-creation.ts | 51 +++++++++++ packages/core/src/task-store/task-row-mappers.ts | 2 +- packages/core/src/types.ts | 56 +++++++++++- packages/dashboard/app/api/legacy.ts | 5 + packages/dashboard/app/components/MailboxModal.tsx | 4 + .../app/components/MailboxTaskProposal.css | 3 + .../app/components/MailboxTaskProposal.tsx | 33 +++++++ packages/dashboard/app/components/MailboxView.tsx | 4 + .../__tests__/MailboxTaskProposal.test.tsx | 43 +++++++++ .../app/components/settings/section-keys.ts | 2 +- .../settings/sections/GeneralSection.search.ts | 13 ++- .../settings/sections/GeneralSection.tsx | 22 +++-- .../settings-default-descriptions.test.tsx | 4 +- .../routes/__tests__/task-proposal-routes.test.ts | 99 ++++++++++++++++++++ .../src/routes/register-messaging-scripts.ts | 101 +++++++++++++++++++++ packages/dashboard/src/sse.ts | 7 ++ packages/engine/src/agent-tools.ts | 30 ++++-- packages/engine/src/executor.ts | 9 +- packages/engine/src/step-session-executor.ts | 8 +- packages/i18n/locales/en/app.json | 5 + 38 files changed, 696 insertions(+), 66 deletions(-) Fusion-Task-Id: FN-8265 Fusion-Task-Lineage: 4e864a2f-3485-4a54-8be7-1699b5479a94 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
0bf496ee7b |
FN-8268: resolve Claude runtime aliases in Vitest
Ensure CLI Vitest runs resolve plugin runtime sources without built distribution artifacts. - Alias the Claude runtime source entry point for CLI tests. - Preserve focused execution of explicitly requested quarantined tests. - Extend workspace-resolution coverage for runtime provider aliases and absent dist outputs. Files changed: packages/cli/src/__tests__/vitest-workspace-resolution.test.ts | 53 ++++++++++++++++------ packages/cli/vitest.config.ts | 23 +++++++++- 2 files changed, 62 insertions(+), 14 deletions(-) Fusion-Task-Id: FN-8268 Fusion-Task-Lineage: c6e27f58-0de6-49ac-aa90-05a9829c0ad6 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
0b6c4cd4ca |
feat(dashboard,desktop): show live database-migration progress during boot
The one-time SQLite→PostgreSQL migration runs inside createTaskStoreForBackend before any HTTP server listens, so browsers saw "connection refused" and open tabs failed silently for minutes. Now: - CLI: a temporary holding server binds the dashboard port for the boot window, serving an auto-reloading "Database migration in progress" page and an /api/health payload with status "migrating" + structured progress; the port is handed off (awaited) to the real app.listen(). - Dashboard SPA: already-open tabs render the new MigrationInProgressBanner from the 15s health poll when status is "migrating". - Desktop: LocalRuntimeManager publishes migration progress on DesktopRuntimeStatus via the new core onMigrationProgress option; DesktopLaunchGate shows the live label and extends its 30s startup timeout while progress advances (2min stall cap), in both boot and first-run flows. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
9cafa045df |
fix(pr-merge): resolve repo from the project checkout, not process.cwd(), in PR-mode auto-merge (#2281)
## Summary In a centrally-installed, multi-project Fusion server (one process serving several repos, `process.cwd()` = the install dir, not any repo), every task under `mergeStrategy: "pull-request"` fails at the auto-merge stage with: ``` Could not determine repository. Specify owner/repo in params or run from a git repository with a GitHub remote. ``` PR creation from the dashboard and status polling work; only the engine's automatic PR path fails. This is the **non-workspace sibling of #1924** (FN-7610 routed workspace-mode tasks to direct merge but does not cover regular multi-project tasks) and the completion of #1797/FN-7133 (which fixed only the `getPrMergeStatus` arguments). ## Root cause `GitHubClient.resolveRepo()` (`packages/dashboard/src/github.ts`) falls back to a cwd-less `getCurrentRepo()` — i.e. `git remote get-url origin` in `process.cwd()` — whenever a PR method is called without explicit `owner`/`repo`. The engine merge path already resolves the correct repo from the per-project cwd (`prRepo = getCurrentRepo(cwd)`, FN-7133) but only threaded it into `getPrMergeStatus`. Every other GitHub call omitted it: - `processPullRequestMergeTask`: `findPrForBranch` / `createPr` / `mergePr` on both the per-task and shared-branch-group paths - `createGroupPrCallback` (group-PR promotion): `findPrForBranch` / `createPr` - `createPrNodeGithubOps` (`pr-create`/`pr-merge` workflow nodes): cwd-less `getCurrentRepo()` persisted `entity.repo` as `""` (poisoning the downstream `splitRepoSlug` consumers), and the git push/`createPr`/`mergePr` ran against `process.cwd()` - the engine's review-response run (`buildRespondCallback`): `respondOps.getCwd` collapses to `process.cwd()` because no CLI composition site wires `getTaskWorktree`, so its git ops and response agent ran outside the project repo In a central install the fallback throws; worse, if `process.cwd()` happens to be inside some *other* git repo, it silently targets the **wrong repository**. ## What changed - `fix(pr-merge): thread repo identity into PR auto-merge GitHub calls` — widens the CLI-local `GitHubOperations` interface (optional `owner`/`repo`, already accepted by `GitHubClient`'s `FindPrParams`/`CreatePrParams`/`MergePrParams`) and passes `prRepo` at all six call sites in `processPullRequestMergeTask`. - `fix(pr-merge): resolve group-PR repo from project cwd in createGroupPrCallback` — resolves via `getCurrentRepo(cwd)` from the callback input (same T4 pattern as `syncGroupPrCallback`) with a loud failure instead of a silent wrong-repo fallback. - `fix(pr-merge): resolve PR-node repo from task worktree instead of process cwd` — `resolvePrSource` resolves from `task.worktree`, git ops run in `getTaskWorktree(...) ?? task.worktree ?? process.cwd()`, and `createPr`/`mergePr` pass `owner`/`repo` parsed from `entity.repo`. - `fix(pr-merge): resolve review-response run cwd from the task worktree` — the engine owns the store, so `buildRespondCallback` prefers the task's recorded `worktree` for the response run's git ops + agent, keeping `ops.getCwd` as the single-project fallback (defensive against structural `PrNodeStore`s without `getTask`). - Changeset (`@runfusion/fusion` patch, structured body) included. Deliberately **not** done: a constructor-scoped default repo on `GitHubClient` — one client instance is shared across all projects in a central install (`serve.ts`/`daemon.ts`/`dashboard.ts`), so per-call `owner`/`repo` is the only correct scope. ## Testing - New regression tests simulate the central-install topology (`getCurrentRepo` mocked as `(cwd?) => cwd ? repo : null`, exactly the failing environment) and drive the merge flow end-to-end on the per-task path, the shared-branch-group path, `createGroupPrCallback`, and all three `createPrNodeGithubOps` ops, asserting every GitHub call carries explicit `owner`/`repo` (45 tests in `packages/cli/src/commands/__tests__/task-lifecycle.test.ts`, all green). - `packages/engine/src/__tests__/pr-respond-cwd-resolution.test.ts` covers the respond-run cwd: worktree preferred, `ops.getCwd` fallback when the task has no worktree, when the lookup fails, and when a structural store has no `getTask`. - Existing exact-argument assertions were extended to the new call contract (no assertions weakened or removed). - `pnpm lint`, `pnpm typecheck`, and `pnpm build` green locally; `pnpm test:gate`'s engine-core suite green (294/294) — its PostgreSQL-backend lane needs local PG credentials this environment lacks, so that lane defers to CI. `pnpm verify:fast` (scoped typecheck/build + CLI build + boot smoke) also passes. ## Repro 1. Install the CLI centrally; run the server from a dir that is not a git repo, serving ≥1 project with a GitHub `origin` and `mergeStrategy: "pull-request"`. 2. Run a task to completion and let it reach the merge stage. 3. Before this fix: the auto-merger throws `Could not determine repository …` (tasks with a persisted PR poll fine but never merge). Merging the same task from the Pull Requests tab succeeds, because the dashboard route resolves the repo explicitly (`parseBadgeUrl(...) ?? getCurrentRepo(rootDir)`). Full analysis: https://github.com/Tchori-Labs/Fusion/issues/4 --- Developed with Claude (co-authored on all commits). https://claude.ai/code/session_01ChEa8SHFYNAzjCdFbwFMfh <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **Bug Fixes** - Resolved pull request auto-merge failures in centrally installed, multi-project deployments. - Ensured explicit repository context (`owner/repo`) is used for pull request lookup, creation, and merging throughout the merge workflow. - Improved pull request response handling to prefer the task worktree for working-directory resolution, with safe error behavior when task details are unavailable. - **Tests** - Expanded coverage for multi-repository merge workflows and worktree-based repository/cwd resolution in PR response handling. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Claude <noreply@anthropic.com> |
||
|
|
adb3eb3a37 |
FN-8224: add Claude ACP runtime support
Add a bundled Claude Code ACP runtime with model discovery and CLI distribution integration. - Add the Claude ACP runtime plugin, bridge, tool forwarding, and tests. - Register Claude model discovery and cached picker support in the dashboard. - Stage the plugin in CLI and desktop packaging with its pinned dependency. - Add workspace, lockfile, and release metadata. Files changed: .changeset/fn-8224-claude-acp-runtime.md | 7 + packages/cli/package.json | 1 + packages/cli/src/__tests__/bundle-output.test.ts | 32 +- .../cli/src/plugins/staged-bundled-plugin-ids.ts | 1 + packages/cli/tsup.config.ts | 40 +- .../core/src/plugins/bundled-plugin-install.ts | 2 + packages/dashboard/package.json | 3 +- packages/dashboard/src/claude-model-cache.ts | 185 +++++++ packages/dashboard/src/routes.ts | 1 + .../dashboard/src/routes/register-model-routes.ts | 11 + packages/dashboard/src/runtime-provider-probes.ts | 15 + packages/dashboard/vitest.config.ts | 4 + packages/desktop/scripts/workspace-tools.ts | 1 + plugins/fusion-plugin-claude-runtime/README.md | 7 + plugins/fusion-plugin-claude-runtime/manifest.json | 1 + plugins/fusion-plugin-claude-runtime/package.json | 1 + .../src/__tests__/cli-spawn.test.ts | 3 + .../src/__tests__/index.test.ts | 3 + .../src/__tests__/provider.test.ts | 8 + .../src/__tests__/runtime-adapter.test.ts | 7 + .../src/__tests__/tool-bridge.test.ts | 79 +++ .../src/acp-settings.ts | 21 + .../src/acp/VENDORED.md | 30 ++ .../src/acp/cli-spawn.ts | 188 +++++++ .../src/acp/control-handler.ts | 302 ++++++++++++ .../src/acp/event-bridge.ts | 308 ++++++++++++ .../src/acp/fs-capabilities.ts | 263 ++++++++++ .../fusion-plugin-claude-runtime/src/acp/index.ts | 16 + .../src/acp/path-jail.ts | 229 +++++++++ .../src/acp/process-manager.ts | 177 +++++++ .../src/acp/prompt-builder.ts | 87 ++++ .../src/acp/provider.ts | 542 +++++++++++++++++++++ .../src/acp/runtime-adapter.ts | 190 ++++++++ .../src/acp/sanitize.ts | 81 +++ .../src/acp/tool-mapping.ts | 47 ++ .../fusion-plugin-claude-runtime/src/acp/types.ts | 189 +++++++ .../fusion-plugin-claude-runtime/src/cli-spawn.ts | 31 ++ plugins/fusion-plugin-claude-runtime/src/index.ts | 95 ++++ .../src/mcp-forwarding.ts | 114 +++++ .../src/mcp-schema-server.cjs | 155 ++++++ plugins/fusion-plugin-claude-runtime/src/probe.ts | 8 + .../fusion-plugin-claude-runtime/src/provider.ts | 8 + .../src/runtime-adapter.ts | 424 ++++++++++++++++ .../src/skill-loader.ts | 290 +++++++++++ .../src/tool-bridge.ts | 264 ++++++++++ plugins/fusion-plugin-claude-runtime/src/types.ts | 142 ++++++ plugins/fusion-plugin-claude-runtime/tsconfig.json | 10 + .../fusion-plugin-claude-runtime/vitest.config.ts | 22 + pnpm-lock.yaml | 131 ++++- pnpm-workspace.yaml | 1 + 50 files changed, 4753 insertions(+), 24 deletions(-) Fusion-Task-Id: FN-8224 Fusion-Task-Lineage: 4e626913-32bb-4baa-a142-816c7f4ee7c4 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |