Generalizes the foreach-only bounded-rework mechanism to the top-level
graph walk so the PR review loop (await-review -> pr-respond -> rework
back) is a legal bounded cycle: a kind:rework back-edge to a stacked node
returns a ReworkSignal the loop-head re-runs up to maxReworkCycles, then
routes outcome:rework-exhausted. Non-rework cycles still throw 'Cycle
detected' (safety preserved); foreach rework unchanged (shared core
constants). Adds createAutoMergeGateHandler (live entity re-fetch +
isPrEntityAutoMergeReady -> auto-on/auto-off). Pins R14: graph-executed
PR tasks merge through pr-merge, never the legacy queue. 122 graph tests
green.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Implements the fix-or-disagree agent loop behind pr-respond: batched
one-run-per-cycle over actionable threads (filtering resolved/outdated/
self/bot), with prompt-injection delimiting of untrusted comment bodies,
viewer-authenticated anti-spoof markers, a pre-push secret scan, fast-
forward-only push (no force-push path) with non-ff abort+re-batch, reply+
resolve, commit-last thread-state persistence with marker+SHA crash
recovery (R15), and an iteration cap on responseRounds (R8). GitHub/git/
agent ops injected; engine stays dashboard-import-free. Adds GraphQL
getPrReviewThreadsDetailed + getViewerLogin to the client. 23 tests.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Adds PrReconciler — a per-repo, self-owned polling loop (started from the
runtime layer in project-engine.ts, NOT the scheduler) that ETag-probes
GitHub, deep-fetches on change, persists mirror state, clears unverified
on first reconcile, and fires releaseHeldTaskByEvent(github:pr-<event>)
for transitions (changes-requested/approved/conflict/conflict-cleared/
merged/closed). Drops terminal entities; persists an audit event on error.
GitHub ops injected via PrReconcileGithubOps at the 3 CLI sites; engine
never imports the dashboard client. scheduler.ts stays PR-free (R20),
pinned by a regression test. 8 tests.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Adds the three first-class PR workflow node kinds and their handlers via
createPrNodeHandlers(deps), registered in createDefaultNodeHandlers
(fail-closed pr-nodes-unwired when absent). GitHub ops are injected as
callbacks (PrNodeGithubOps) at all three CLI sites (daemon/serve/dashboard)
so the engine never imports the dashboard client (FN-3049). pr-create
routes open/failed as outcomes; pr-merge passes expectedHeadOid and never
writes 'merged' (reconcile corroborates); pr-respond delegates to an
injected respond callback (U5 fills the body). 10 tests.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Mount CliChatSurface in ChatView for cli-backed chat sessions (sessions carrying
cliExecutorAdapterId): the message-pane + composer region is delegated to the
surface (transcript/raw-terminal toggle for hybrid/native adapters, terminal-only
for the generic adapter), while regular sessions keep the standard composer. The
existing message list and composer JSX are captured once as render thunks and
passed through, so there is no parallel message/composer UI.
Add a narrow telemetry seam: TelemetryHub gains an optional onEvent tap (also
settable post-construction via setEventListener) invoked with each sanitized
event after routing — best-effort, a throwing listener never breaks ingest. This
is the seam the CliChatSessionRunner uses to build the durable transcript from
the same sanitized events the hook route already feeds the hub, without the hub
becoming a general subscriber bus.
Fix the stale @fusion/engine vi.mocks across dashboard tests: object-literal
mocks that fully replace the module now also return listCliAdapterDescriptors
(added by U15's cli-agent-settings route, evaluated at module load). Mocks that
spread importOriginal/importActual already pick it up.
Tests: new ChatView.cli-mount.test.tsx (cli session → CliChatSurface, regular
session → normal composer, generic → terminal-only); telemetry-hub onEvent tap
coverage. chat-attachment-routes, chat-cli-sessions, cli-agent-hooks-route,
ChatView.cli-toggle all green.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add `cli-agent` as a task-execute executor kind. A workflow node with
`config.executor === "cli-agent"` drives an engine-owned CLI coding agent
through the execute step via the new cli-agent/task-session.ts orchestration:
spawn in the worktree, issue the hook token + write hook scripts, inject the
prompt after readiness, subscribe to the state machine, and resolve on a
positive completion signal (R20 gating). Config is snapshotted at launch; the
PTY is reaped (completed) at the in-review handoff.
Executor seam: runGraphCustomNode gains a cli-agent branch delegating to
runCliAgentNode; the hard-cancel/abort path (awaitAbortInFlightTaskWork +
abortAllInFlight) claims and SIGKILLs the CLI session as a first-class surface,
marking it killed (never resume-eligible). Re-entry kills any prior live
session and launches fresh; follow-up resumes the recorded native session id
when supported. A PTY-pool ceiling surfaces as a typed task value, not a stall.
Node-config typing extended minimally (WorkflowNodeExecutorKind /
WorkflowNodeExecutorConfig in @fusion/core).
Tests: cli-agent/__tests__/task-session.test.ts (12) and
__tests__/cli-agent-executor.test.ts (9) cover AE1/AE5, hard cancel, re-entry,
follow-up, config snapshot, ceiling, and the generic-tier confirm-advance path,
using scripted adapters + a mock PTY seam. Engine typecheck clean; full
src/cli-agent suite + new tests green (144 passing).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Importing a catalog ("company") agent assigns role "custom", which the
scheduler never auto-assigns mission/queue work to. Combined with a
model/provider that rejects the "developer" system role, this surfaced as
an invisible, repeating failure loop (GitHub #1261).
- pi.ts: treat an unsupported message-role rejection as a model-selection
error so a configured fallback model is tried once (single-swap guarded)
before the task is marked failed.
- mission-autopilot.ts: block a mission feature immediately on an
operator-actionable failure instead of burning the retry budget
re-running the same cryptic error.
- mission-routes.ts: preflight mission start — when ephemeral agents are
disabled and no eligible executor exists, fail fast with an actionable
message instead of queueing tasks forever.
- agent import route + AgentImportModal: warn when only custom-role agents
are imported and no executor exists.
- agent-assignment.ts: extract shared listEligibleExecutorAgents helper.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Main independently landed the same pausedReason persistence fix (column in
the task INSERT/upsert, TaskRow type, rowToTask mapping) with the column
ordered between paused and userPaused; adopt main's ordering and drop this
branch's duplicate entries so the values array stays aligned. Union the
dashboard package.json deps (main's compound-engineering plugin alongside
this branch's sorted pi-coding-agent/cli-printing-press/cursor-runtime).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Both sides bootstrapped CONCEPTS.md — kept main's broader structure (Missions,
Merge lifecycle, CE sessions) and folded the branch-group vocabulary into its
Merge-lifecycle cluster (enriched Shared branch group + Self-healing sweep;
added Branch assignment mode, Landed, Group promotion). Union-merged core
index/store imports; took main's AGENTS.md discoverability wording. Also adds
execFile to the two child_process test mocks that only stubbed exec (the
coordinator's argv-based git calls now require it).
Agent-native parity: users can list workflows and select one for a task in
the dashboard, so agents should be able to as well. Adds two task-session
tools — fn_workflow_list (read: built-ins + user definitions) and
fn_workflow_select (assign a workflow to a task, defaulting to the current
one) — wired into the executor's customTools and exported from the engine
index. Covered by unit tests against mock stores.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Users can now watch everything the agent does while a CE stage works, steer
it mid-stage, and read the whole conversation as a proper chat surface.
Live output:
- New host capability: CreateInteractiveAiSessionOptions.onProgress — the
engine adapter streams thinking/text deltas + tool start/end markers from
the pi agent hooks (any plugin can use this).
- Orchestrator buffers per-session live activity (merged deltas, discrete
tool lines, capped), emits throttled progress events over SSE, and
GET /sessions/:id attaches it as liveActivity for the polling fallback.
- Routes detach turn execution: start/answer/resume return immediately
(status active) and clients converge via push/poll — the turn is watchable
instead of hidden inside a blocking POST.
- Turn timeout is now INACTIVITY-based: an actively-working long turn is
never killed; a quiet one interrupts with its working trace preserved.
- On settle the trace persists into history as a condensed record.
Steering:
- Stage protocol: responses may be a direct answer, {value, comment}
(answer + guidance), or {feedback} (guidance without answering); the
system prompt instructs agents to treat steering as first-class input.
- CeFlow: guidance textarea alongside selectable questions — attach to the
clicked answer, or "Send guidance" on its own.
Q&A UI:
- Transcript no longer hides control records: past questions/answers render
as chat bubbles (option ids → labels), steering turns marked, working
traces as collapsible "Agent work" blocks, completion marker.
- Live working pane (pulse + streaming thinking/tool lines) while a turn runs.
Tests: 130 plugin tests green (14 new: live buffer/flush ordering, inactivity
watchdog survives active work, detached convergence, steering payload shapes,
transcript rendering, live pane). Engine seam tests green; plugin/core/
engine/dashboard tsc clean. Core full suite OOMs locally (known orchestrator-
shell issue) — covered by CI shards.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Review residuals #3/#4/#6/#10: per-group in-process promotion lock (concurrent
route+auto promotion could double-create PRs), finalized-but-PR-less groups can
be repaired by re-promotion without re-merging, auto-promotion failures emit
merge:branch-group-promotion-failed instead of silent swallow, exported
reconcileBranchGroupPr for out-of-band merged reconciliation, and the merger
sync block drops its (store as any) casts (TaskStore already carries the
methods).
Loads a task's selected workflow, runs the graph with injected legacy seams
(execute/review/merge) and a custom-node runner, and maps the terminal outcome
to completed/failed/fell-back. Any interpreter-level error falls back so the
caller can run the legacy pipeline — a task is never stranded. Covered with
fake seams: lifecycle ordering, failure routing, gate blocking, fallback
reasons, diagnostics isolation. Includes the interpreter-cutover plan doc.
Push the single group PR's body (member checklist, x/N landed) on each member
landing via an injected SyncGroupPrFn — new updatePr/closePr GitHubClient
helpers (gh CLI + API parity); refreshPrInBackground is task-scoped/wrong
direction and intentionally not reused. Sync failures are non-fatal+retryable;
out-of-band closed/merged PRs reconcile prState instead of erroring. New
POST /branch-groups/:id/abandon closes the PR best-effort and marks the group
abandoned. Also fixes the U5-introduced stub-context regression in the U4
dashboard bridge test (missing options).
Group promotion in PR mode previously flipped prState to 'open' without ever
calling GitHub — prNumber/prUrl were never populated. Add an injected
CreateGroupPrFn (mirrors the processPullRequestMerge seam, no engine→dashboard
import): coordinator creates-or-reuses exactly one PR per group, persists
prNumber/prUrl/prState, and leaves state untouched on GitHub failure so
re-promotion retries. Idempotent via persisted prNumber +
getBranchGroupByBranchName. Wired at all three CLI engine-construction sites
(daemon/dashboard/serve).
Closes the U2/U5 skill-discovery carry-forward so the plugin's interactive ce-*
sessions actually load the stage's bundled skill in a live agent (not just in
scripted-fake tests).
Root cause: createFnAgent built its DefaultResourceLoader without forwarding any
skill-discovery path, and the interactive seam options couldn't carry one. The
loader's skillsOverride only *filters* skills already discovered from cwd's
standard roots, so the plugin-local .fusion-ce-skills/<id>/SKILL.md was never
discoverable.
Fix (end-to-end):
- AgentOptions.additionalSkillPaths forwarded into DefaultResourceLoader
- CreateInteractiveAiSessionOptions gains requestedSkillNames + additionalSkillPaths
- the interactive engine adapter forwards them to createFnAgent (skills +
additionalSkillPaths)
- the orchestrator runs the session with cwd on the real project root and hands
it [stage.skillId] + the install root
Proven: a real DefaultResourceLoader with additionalSkillPaths discovers ce-plan
and filters out ce-work; the orchestrator passes the right id/path/cwd. Plugin 96,
engine 136, core 99 tests green.
Expose createInteractiveAiSession on route/loader PluginContext alongside the
one-shot createAiSession. The prompt->parse->retry->pause->resume loop is
reimplemented in an engine seam modeled on planning.ts (no engine await-input
primitive exists). Reuses PlanningQuestion/PlanningResponse; stays generic with
no plugin-specific concepts. Injected on route contexts only; tool/runtime
contexts omit it (parity with createAiSession).
Add the Phase 2 workflow-graph interpreter scaffold and builtin coding IR wiring while keeping execution behavior unchanged by default.
- add BUILTIN_CODING_WORKFLOW_IR and builder in @fusion/core with coverage tests
- export new coding workflow IR APIs from core index
- add WorkflowGraphExecutor scaffold in @fusion/engine plus parity-focused test coverage
- document the flagged-off interpreter scaffold, parity gate, and v1 IR gap reconciliation in workflow docs
- include a changeset for published @runfusion/fusion
Files changed:
.../fn-5766-workflow-graph-executor-scaffold.md | 11 +++
docs/workflow-steps.md | 27 +++++++
.../__tests__/builtin-coding-workflow-ir.test.ts | 34 +++++++++
packages/core/src/builtin-coding-workflow-ir.ts | 60 ++++++++++++++++
packages/core/src/index.ts | 4 ++
.../workflow-graph-executor-parity.test.ts | 32 +++++++++
packages/engine/src/index.ts | 7 ++
packages/engine/src/workflow-graph-executor.ts | 82 ++++++++++++++++++++++
8 files changed, 257 insertions(+)
Fusion-Task-Id: FN-5766
Fusion-Task-Lineage: 4bbef713-a415-4d42-a20b-9ae4f3235419