feat: Compound Engineering plugin with interactive sessions, work bridge, and bidirectional sync#1343
Conversation
…(U4) 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).
Bundle pinned copies of 7 CE pipeline-stage skills (strategy, ideate, brainstorm, plan, work, code-review, compound) under src/skills/ and declare them via PluginSkillContribution. Empirical finding: the skills contribution alone does not make a SKILL.md resolvable in a session -- the engine ingests it as a name only. So onLoad runs an idempotent, isolation-guarded physical install into a plugin-local .fusion-ce-skills/ dir (never a global ~/.claude/skills), which the engine skill-resolver can then discover. Proven against the real loadSkills + resolveSessionSkills pipeline.
… (U5) Add ce_sessions schema (onSchemaInit), CeSessionStore (via ctx.taskStore.getDatabase()), and CeOrchestrator driving a stage's skill on the U4 interactive seam: streams thinking/text, persists questions as awaiting_input, writes the stage artifact on complete, and auto-saves + emits an observable event on interrupt/error (never silent loss). Lifecycle: launching/active/awaiting_input/completed/error/interrupted with resume and an interval-relative staleness rubric. Start/answer/resume/get-state routes. Streaming transport is client polling of the get-session-state route for v1: plugin routes have no native SSE and the loader emitEvent is a logging stub, so true server push needs a host publish-to-/api/events seam (tracked follow-up). Skill discovery is cwd+prompt-based; forwarding install paths through the seam is a tracked follow-up.
… (U3) Add allowlist-based artifact discovery over the conventional CE locations (STRATEGY.md, docs/ideation, docs/brainstorms, docs/plans, docs/solutions, CONCEPTS.md), grouped by stage, with error entries for unreadable artifacts and no reads outside the allowlist. Add list/read/preview routes (self-contained sandboxed HTML, data-section markers) and the primary CompoundEngineeringView with explicit empty/partial/error states, viewport-gated fetch, and a short-TTL cache. Bind the view via registerBundledPluginViews + dashboard workspace dep (the real binding; manifest componentPath is cosmetic).
Extend the stage registry with presentation metadata (icon/label/glob) so adding a stage stays data-only. Add CeFlow renderer for text/single_select/multi_select/ confirm questions over the U5 polling session routes, with a visibly-marked chat fallback (AE1) that still completes the stage. Wire the artifact-hub launcher to list and start registered stages. Add the skill-interaction audit test producing a measured rich-vs-chat coverage ratio (declared classification: 8/8=100% across brainstorm/ideate/plan), failing on unclassified interactions.
When the work stage completes with a derived task list, create Fusion tasks via ctx.taskStore.createTask tagged CE-originated (sourceType workflow_step + sourceMetadata marker) and record an authoritative ce_pipeline_links row per task (back-reference lives in the link table, not task-row JSON, per FN-5719). Tasks then run the normal lifecycle untouched. Zero derived tasks is a clean no-op. The link store is intentionally minimal for U8 to extend with the bidirectional pipeline-state machine.
…ync (U8) Add a ce_pipeline_state machine (currentStage/status) kept distinct from board-task ownership (task column) — separate tables, no shared column, per FN-5719. onTaskMoved/onTaskCompleted hooks do only an indexed lookup + enqueue and return well under the 5s budget; advancement happens in an on-demand reconciler sweep that re-derives correct pipeline state from board truth, so a dropped hook event still converges (no tight poll loop). Outbound CE-flow changes create the next-stage board task. Conflict policy: board authoritative for task state, CE flow authoritative for artifact/pipeline content (inbound read and outbound write target different rows, so they cannot contend). Host-scheduler note: no host timer wired; sweeps run on hook-drain and on the dashboard/route refresh surface (tracked for the host event-publish follow-up).
Add settingsSchema (default session provider/model, enabled stages, sync reconcile-on-hooks toggle, reconcile cadence hint) aligned across manifest.json and the runtime manifest, with typed getters. Wire the consumed getters: orchestrator passes defaultProvider/defaultModelId into sessions and rejects disabled stages; hooks gate their reconcile drain on reconcileOnHooks. Add the plugin README and docs/plugins/compound-engineering.md documenting the hub, interactive sessions, work bridge, and the sync ownership model. reconcileIntervalMinutes is exposed as an operator cadence hint but not yet consumed (no host scheduler; reconcile is on-demand by design).
… pass Quality cleanup across the 9-unit build (behavior-preserving, 89 tests green): - extract createCeTaskWithLink so the work bridge and reconciler share one provenance+link contract (prevents drift) - discovery list scan probes readability via accessSync instead of reading and discarding full file bytes - makeError helper replaces ~7 duplicated CeArtifactError literals - shared asString route helper; drop dead pipelineIds set; collapse a double pipeline-state write and a redundant link re-query in advance - resolveStageSkillCwd no longer takes params it ignores
- reconciler: a deleted current-stage board task no longer wedges the pipeline in 'running' forever (terminality computed over existing tasks only; all-deleted is a no-op, not a wedge) - session-store: a human-slow awaiting_input session is no longer misclassified stale (interval rubric applies only to in-flight active/launching turns) - stage-registry: pipeline progression uses an explicit order ordinal instead of registry insertion order, so out-of-order registration can't corrupt advancement - orchestrator.answer(): validate questionId before mutating state, so a stale id can't destroy the persisted currentQuestion recovery anchor - orchestrator.resume(): rehydrate a live interactive session by replaying persisted history (side effects suppressed) so a resumed session is actually answerable instead of dead-ending; honest interrupted+error fallback when no factory is available 95 tests (6 new regression tests, each confirmed failing pre-fix).
…ad their skills 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.
…sume/poll The session's owning store (and its live in-process handle) is selected per request by projectId. start() sent projectId but answer/resume/getSession did not, so any project-scoped session broke on the first answer (a different store resolved → session not found / no live handle). The client now captures the start projectId and reuses it on every subsequent call. Closes the multi-project session-identity residual.
…r SSE Replaces polling-only with a true server-push seam (any plugin benefits): - core: createRouteContext accepts an emitEvent override (default still logs) - dashboard: emitPluginCustomSseEvent forwards a plugin's ctx.emitEvent calls to connected /api/events clients as a project-scoped 'plugin:custom' event; the plugin route context's emitEvent is wired to it - dashboard: PluginDashboardViewContext gains subscribePluginEvents so views consume push via a host capability (no raw EventSource, no deep app import) - CE view subscribes its session to push and refetches on each event; polling stays as the fallback when push isn't wired or an event is missed Also: skill-reachability test installs into a temp dir (no repo-dir writes). Tests: dashboard sse +2 (plugin:custom relay + project scoping), plugin 99.
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughAdds a Compound Engineering plugin: engine/core interactive-session seam and adapter, dashboard SSE + subscribe seam and bundled view, plugin runtime (orchestrator, routes, stores, schema, artifact discovery), bundled skills, extensive tests, and docs. ChangesCompound Engineering Feature Slice
Sequence Diagram (high-level)sequenceDiagram
participant Dashboard
participant PluginRoutes
participant Orchestrator
participant Engine
participant SSE
Dashboard->>PluginRoutes: POST /sessions (start/answer/resume)
PluginRoutes->>Orchestrator: start/answer/resume
Orchestrator->>Engine: createInteractiveAiSession / prompt
Engine->>Orchestrator: interactive events (question/complete/error)
Orchestrator->>SSE: emitPluginCustomSseEvent(pluginId,event,payload,projectId)
SSE->>Dashboard: plugin:custom event delivered
Estimated code review effort Poem
✨ Finishing Touches🧪 Generate unit tests (beta)
|
There was a problem hiding this comment.
Actionable comments posted: 8
🧹 Nitpick comments (5)
packages/dashboard/app/plugins/registerBundledPluginViews.ts (1)
40-50: ⚡ Quick winConsider catching import failures for consistency.
The CLI plugin loaders (lines 52-82) wrap their imports in
try/catchto handle module-not-found and other import failures, butloadCompoundEngineeringViewonly handles missing exports. If the module fails to import (e.g., build error, missing dependency), users will see an unhandled promise rejection instead of the graceful "Bundled plugin view unavailable" fallback.♻️ Add try/catch wrapper
async function loadCompoundEngineeringView(): Promise<{ default: PluginViewComponent }> { const moduleId = "`@fusion-plugin-examples/compound-engineering/dashboard-view`"; const exportName = "CompoundEngineeringDashboardView"; - const mod = await import("`@fusion-plugin-examples/compound-engineering/dashboard-view`") as unknown as Record<string, ComponentType<{ context?: PluginDashboardViewContext }>>; - const component = mod[exportName]; - if (!component) { - console.warn(`[plugin-views] Missing export ${exportName} from ${moduleId}`); - return { default: createMissingPluginView(moduleId, exportName) }; + try { + const mod = await import("`@fusion-plugin-examples/compound-engineering/dashboard-view`") as unknown as Record<string, ComponentType<{ context?: PluginDashboardViewContext }>>; + const component = mod[exportName]; + if (!component) { + console.warn(`[plugin-views] Missing export ${exportName} from ${moduleId}`); + return { default: createMissingPluginView(moduleId, exportName) }; + } + return { default: component as PluginViewComponent }; + } catch { + return { default: createMissingPluginView(moduleId, exportName) }; } - return { default: component as PluginViewComponent }; }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/dashboard/app/plugins/registerBundledPluginViews.ts` around lines 40 - 50, The loader loadCompoundEngineeringView currently only checks for a missing export but doesn't catch import failures; wrap the dynamic import of moduleId in a try/catch (same pattern used by the CLI loaders) so any import error is caught, log a warning including the error and moduleId (use console.warn like the others), and return the fallback { default: createMissingPluginView(moduleId, exportName) } when an exception occurs; ensure you still keep the existing exportName check and final cast to PluginViewComponent when successful.plugins/fusion-plugin-compound-engineering/src/skills/ce-code-review/references/findings-schema.json (1)
102-139: 💤 Low valueNon-standard
_metasection in JSON Schema.The
_metasection (lines 102-139) is not part of the JSON Schema draft-07 specification. While valid JSON, strict schema validators or tooling expecting only standard keywords might warn or ignore this section.If this schema is primarily consumed by AI agents in prompts (as suggested by the subagent-template.md usage), the
_metadocumentation is valuable in-context. However, if the schema is also validated by standard JSON Schema tooling, consider:
- Moving
_metacontent to a separate documentation file referenced by the schema- Using standard JSON Schema
$commentordescriptionfields for documentation- Adding a note that
_metais documentation-only and should be ignored by validatorsIf the current approach is intentional for AI consumption, no change needed—just document that
_metais non-standard.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@plugins/fusion-plugin-compound-engineering/src/skills/ce-code-review/references/findings-schema.json` around lines 102 - 139, The _meta block is non-standard for JSON Schema; update the findings-schema to avoid validator warnings by either (A) extracting the entire "_meta" object into a separate documentation file referenced by your repo and removing it from the schema, (B) converting its human-facing entries (e.g., "confidence_anchors", "severity_definitions", "autofix_classes", "return_tiers", "owners") into standard JSON Schema documentation fields like "$comment" or top-level "description" strings, or (C) if you must keep it in-place for AI consumption, add a clear $comment or top-level note in the schema that "_meta" is documentation-only and must be ignored by validators; target the "_meta" symbol and the nested keys when making the change so the schema validates under draft-07 while preserving the human-readable guidance.plugins/fusion-plugin-compound-engineering/src/routes/artifact-routes.ts (1)
98-98: 💤 Low valueAdd defensive handling for
decodeURIComponentwhen decodingrequest.params.id.Both the
/artifacts/:idand/artifacts/:id/preview.htmlhandlers calldecodeURIComponent(request.params.id); it can throw on malformed percent-encoding, so wrap the decode intry/catchand return a 400/422 for invalid artifact IDs to avoid handler failures.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@plugins/fusion-plugin-compound-engineering/src/routes/artifact-routes.ts` at line 98, Wrap the decodeURIComponent call used to set id in both route handlers (the lines with const id = decodeURIComponent(request.params.id)) in a try/catch; if decodeURIComponent throws, return an HTTP 400 (or 422) error response from the handler (e.g., reply.code(400).send(...) or equivalent) with a clear message about an invalid artifact id instead of letting the exception bubble and crash the handler. Ensure you stop further processing when the ID is invalid.plugins/fusion-plugin-compound-engineering/src/skill-installation.ts (1)
139-141: 💤 Low valueMinor optimization opportunity: hoist targetRoot creation outside the loop.
mkdirSync(targetRoot, { recursive: true })is called inside the loop for each skill (line 139), resulting in 7 redundant calls for 7 skills. While this is harmless due to idempotency, it's inefficient.♻️ Proposed refactor
export function installBundledCeSkills( options: InstallBundledCeSkillsOptions = {}, ): InstallBundledCeSkillsResult { const targetRoot = options.targetRoot ? resolve(options.targetRoot) : resolveDefaultInstallTargetRoot(); assertPluginLocalTarget(targetRoot); const sourceRoot = options.sourceRoot ? resolve(options.sourceRoot) : resolveBundledSkillsRoot(); + // Create targetRoot once before processing skills + mkdirSync(targetRoot, { recursive: true }); + const results = COMPOUND_ENGINEERING_SKILLS.map<CeSkillInstallResult>((skill) => { const sourceDir = join(sourceRoot, skill.skillId); const targetDir = join(targetRoot, skill.skillId); try { assertValidSkillSource(skill.skillId, sourceDir); if (existsSync(targetDir)) { return { skillId: skill.skillId, sourceDir, targetDir, outcome: "skipped", reason: "existing install preserved" }; } - mkdirSync(targetRoot, { recursive: true }); cpSync(sourceDir, targetDir, { recursive: true }); return { skillId: skill.skillId, sourceDir, targetDir, outcome: "installed" }; } catch (error) {🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@plugins/fusion-plugin-compound-engineering/src/skill-installation.ts` around lines 139 - 141, The mkdirSync(targetRoot, { recursive: true }) call should be hoisted out of the per-skill loop to avoid redundant invocations; before iterating over skills, ensure the targetRoot directory is created once, then inside the loop keep the cpSync(sourceDir, targetDir, { recursive: true }) and the return/object construction (referencing skill.skillId, sourceDir, targetDir, outcome) unchanged so each skill is still copied and reported as "installed".plugins/fusion-plugin-compound-engineering/src/dashboard/hooks/useCeSession.ts (1)
177-199: 💤 Low valuePolling effect may restart unnecessarily when busy toggles.
The polling effect at lines 177-199 includes
busyin its dependency array (line 199). Whenbusybecomestrue(during answer/resume/start), the effect cleanup runs and clears the interval. Then whenbusybecomesfalse, the effect re-runs and starts a new interval—even if the session is already settled.While line 180 guards against polling settled sessions, the effect still cleans up and re-initializes the interval whenever
busychanges. This is benign (the guard prevents actual network calls), but slightly inefficient.Consider moving the
busycheck inside the interval callback instead of the effect dependencies to avoid restarting the interval:useEffect(() => { const id = sessionIdRef.current; if (!id || !status || SETTLED.has(status)) return; let cancelled = false; const timer = setInterval(() => { if (busy) return; // Check inside callback transport .get(id, projectIdRef.current) .then((next) => { if (!cancelled) apply(next); }) .catch((err: unknown) => { if (!cancelled && mounted.current) { setError(err instanceof Error ? err.message : String(err)); } }); }, pollIntervalMs); return () => { cancelled = true; clearInterval(timer); }; }, [status, transport, apply, pollIntervalMs]); // Remove busyHowever, the current implementation is correct and the performance impact is negligible. This is an optional micro-optimization.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@plugins/fusion-plugin-compound-engineering/src/dashboard/hooks/useCeSession.ts` around lines 177 - 199, The effect currently lists busy in its dependency array which causes the interval to tear down and restart whenever busy toggles; to avoid that, remove busy from the dependencies of the useEffect containing sessionIdRef/status/SETTLED/transport/apply/pollIntervalMs and instead check the busy flag inside the interval callback (i.e. early-return from the timer function if busy is true); keep the existing guards for id/status/SETTLED at effect start, keep cancelled/clearInterval cleanup, and ensure the dependencies remain [status, transport, apply, pollIntervalMs] so the interval isn't restarted on busy changes.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@plugins/fusion-plugin-compound-engineering/src/__tests__/_harness.ts`:
- Around line 62-74: The nextEvent implementation in makeScriptedSession can
index script[-1] when script is empty; update nextEvent (and its use of cursor
and script) to guard for an empty script by returning undefined (or an explicit
error) when script.length === 0, otherwise return script[Math.min(cursor,
script.length - 1)]; keep the function name makeScriptedSession and the cursor
logic but add the empty-array check inside the nextEvent vi.fn to avoid
accessing a negative index.
In
`@plugins/fusion-plugin-compound-engineering/src/dashboard/hooks/__tests__/useCeSession.test.tsx`:
- Line 84: Replace the real timeout await new Promise((r) => setTimeout(r, 20))
in the test with Jest fake timers: call jest.useFakeTimers() before the action
that schedules the timeout, replace the await with jest.advanceTimersByTime(20)
(or jest.runOnlyPendingTimers()/jest.runAllTimers() as appropriate) to
fast-forward timers, then await any pending microtasks (e.g., await
Promise.resolve()) and finally restore timers with jest.useRealTimers(); update
the test that exercises useCeSession to use this pattern so it becomes
deterministic and fast.
- Line 109: Replace the real timeout used by the expression await new
Promise((r) => setTimeout(r, 40)); with Jest fake timers: enable fake timers at
the start of the test (jest.useFakeTimers()), then advance time (e.g.,
jest.advanceTimersByTime(40) or jest.runOnlyPendingTimers()) in place of the
await Promise, and finally flush pending microtasks if needed (await
Promise.resolve() or wrap the advance in act(...) for React) and restore real
timers after the test (jest.useRealTimers()); update the test setup/teardown
accordingly so no real setTimeout is used.
In `@plugins/fusion-plugin-compound-engineering/src/routes/session-routes.ts`:
- Around line 119-130: The handler in the GET "/sessions" route currently uses
an unsafe cast `status as never` when calling getCeSessionStore(ctx).list(),
bypassing TypeScript checks; replace this by validating query.status against the
store's allowed status values (e.g., an array or union like
"active"|"completed"|"interrupted") using a small type guard or lookup
(isValidStatus), then pass the validated/typed value or undefined to
getCeSessionStore(ctx).list(); if the value is invalid either set status =
undefined or return a 400 error—do not use `as never`. Ensure you update the
handler where (req as RouteRequest).query.status is read and remove the cast so
list() receives a properly typed status.
In `@plugins/fusion-plugin-compound-engineering/src/session/session-store.ts`:
- Around line 80-81: In rowToSession, wrap JSON.parse calls for
row.currentQuestion and row.conversationHistory in try/catch so corrupted JSON
doesn't throw; on parse failure log the error with context (include row
id/session id), set currentQuestion to null and conversationHistory to an empty
array or mark the session as corrupted (e.g., add a corrupted flag) and return
the session object safely. Ensure the catch targets both fields independently
(so one bad field doesn't null out the other) and reference rowToSession,
currentQuestion, and conversationHistory when implementing the fixes.
In
`@plugins/fusion-plugin-compound-engineering/src/skills/ce-code-review/references/persona-catalog.md`:
- Line 3: The summary line claiming "14 reviewer personas" is incorrect; update
the opening sentence that contains the phrase "14 reviewer personas" in
persona-catalog.md to reflect the actual counts (4 always-on personas + 2 CE
always-on agents + 7 conditional personas + 2 stack-specific personas + 1 CE
conditional agent = 16 total) or clarify whether "personas" excludes "agents"
and then adjust the number accordingly so the total matches the listed items;
ensure the revised line uses the same wording context as the original to keep
intent clear.
In
`@plugins/fusion-plugin-compound-engineering/src/skills/ce-code-review/references/subagent-template.md`:
- Line 24: The comment points out a path mismatch: update the reference to the
artifact write location that currently mentions ".context/" so it matches the
concrete artifact path used elsewhere. Replace the `.context/` reference with
the actual output path
`/tmp/compound-engineering/ce-code-review/{run_id}/{reviewer_name}.json`
wherever the incorrect phrase appears (e.g., the sentence mentioning ".context/"
near the analysis guidance) so all mentions (including the one at line 136)
consistently use the
`/tmp/compound-engineering/ce-code-review/{run_id}/{reviewer_name}.json` path.
In
`@plugins/fusion-plugin-compound-engineering/src/skills/ce-plan/references/synthesis-summary.md`:
- Line 131: Several fenced code blocks in synthesis-summary.md are unlabeled and
trigger the MD040 lint; open synthesis-summary.md and add a language tag (e.g.,
```text or ```json as appropriate) to each unlabeled code fence mentioned (the
fence at the shown diff plus the other unlabeled blocks referenced at 199, 246,
259, 298, 311) so every triple-backtick block has a language identifier; ensure
the tag matches the content type (use `text` for plain examples) and save to
clear the MD040 lint errors.
---
Nitpick comments:
In `@packages/dashboard/app/plugins/registerBundledPluginViews.ts`:
- Around line 40-50: The loader loadCompoundEngineeringView currently only
checks for a missing export but doesn't catch import failures; wrap the dynamic
import of moduleId in a try/catch (same pattern used by the CLI loaders) so any
import error is caught, log a warning including the error and moduleId (use
console.warn like the others), and return the fallback { default:
createMissingPluginView(moduleId, exportName) } when an exception occurs; ensure
you still keep the existing exportName check and final cast to
PluginViewComponent when successful.
In
`@plugins/fusion-plugin-compound-engineering/src/dashboard/hooks/useCeSession.ts`:
- Around line 177-199: The effect currently lists busy in its dependency array
which causes the interval to tear down and restart whenever busy toggles; to
avoid that, remove busy from the dependencies of the useEffect containing
sessionIdRef/status/SETTLED/transport/apply/pollIntervalMs and instead check the
busy flag inside the interval callback (i.e. early-return from the timer
function if busy is true); keep the existing guards for id/status/SETTLED at
effect start, keep cancelled/clearInterval cleanup, and ensure the dependencies
remain [status, transport, apply, pollIntervalMs] so the interval isn't
restarted on busy changes.
In `@plugins/fusion-plugin-compound-engineering/src/routes/artifact-routes.ts`:
- Line 98: Wrap the decodeURIComponent call used to set id in both route
handlers (the lines with const id = decodeURIComponent(request.params.id)) in a
try/catch; if decodeURIComponent throws, return an HTTP 400 (or 422) error
response from the handler (e.g., reply.code(400).send(...) or equivalent) with a
clear message about an invalid artifact id instead of letting the exception
bubble and crash the handler. Ensure you stop further processing when the ID is
invalid.
In `@plugins/fusion-plugin-compound-engineering/src/skill-installation.ts`:
- Around line 139-141: The mkdirSync(targetRoot, { recursive: true }) call
should be hoisted out of the per-skill loop to avoid redundant invocations;
before iterating over skills, ensure the targetRoot directory is created once,
then inside the loop keep the cpSync(sourceDir, targetDir, { recursive: true })
and the return/object construction (referencing skill.skillId, sourceDir,
targetDir, outcome) unchanged so each skill is still copied and reported as
"installed".
In
`@plugins/fusion-plugin-compound-engineering/src/skills/ce-code-review/references/findings-schema.json`:
- Around line 102-139: The _meta block is non-standard for JSON Schema; update
the findings-schema to avoid validator warnings by either (A) extracting the
entire "_meta" object into a separate documentation file referenced by your repo
and removing it from the schema, (B) converting its human-facing entries (e.g.,
"confidence_anchors", "severity_definitions", "autofix_classes", "return_tiers",
"owners") into standard JSON Schema documentation fields like "$comment" or
top-level "description" strings, or (C) if you must keep it in-place for AI
consumption, add a clear $comment or top-level note in the schema that "_meta"
is documentation-only and must be ignored by validators; target the "_meta"
symbol and the nested keys when making the change so the schema validates under
draft-07 while preserving the human-readable guidance.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: fbcf9f56-0556-4b80-bc4e-12f9d4663cc6
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (115)
.changeset/compound-engineering-plugin-scaffold.mddocs/plans/2026-06-02-001-feat-compound-engineering-plugin-plan.mddocs/plugins/compound-engineering.mdpackages/cli/src/plugins/__tests__/bundled-plugin-install.test.tspackages/cli/src/plugins/bundled-plugin-install.tspackages/core/src/__tests__/interactive-ai-session-seam.test.tspackages/core/src/ai-engine-loader.tspackages/core/src/index.tspackages/core/src/plugin-loader.tspackages/core/src/plugin-types.tspackages/dashboard/app/App.tsxpackages/dashboard/app/plugins/registerBundledPluginViews.tspackages/dashboard/app/plugins/types.tspackages/dashboard/package.jsonpackages/dashboard/src/__tests__/sse.test.tspackages/dashboard/src/plugin-routes.tspackages/dashboard/src/sse.tspackages/engine/src/__tests__/compound-engineering-skill-resolution.test.tspackages/engine/src/__tests__/interactive-ai-session.test.tspackages/engine/src/__tests__/plugin-runner.test.tspackages/engine/src/index.tspackages/engine/src/interactive-ai-session.tspackages/engine/src/pi.tsplugins/fusion-plugin-compound-engineering/.gitignoreplugins/fusion-plugin-compound-engineering/README.mdplugins/fusion-plugin-compound-engineering/manifest.jsonplugins/fusion-plugin-compound-engineering/package.jsonplugins/fusion-plugin-compound-engineering/src/__tests__/_harness.tsplugins/fusion-plugin-compound-engineering/src/__tests__/manifest.test.tsplugins/fusion-plugin-compound-engineering/src/__tests__/orchestrator-flow.test.tsplugins/fusion-plugin-compound-engineering/src/__tests__/orchestrator-interrupt-resume.test.tsplugins/fusion-plugin-compound-engineering/src/__tests__/session-routes.test.tsplugins/fusion-plugin-compound-engineering/src/__tests__/session-store.test.tsplugins/fusion-plugin-compound-engineering/src/__tests__/settings.test.tsplugins/fusion-plugin-compound-engineering/src/__tests__/skill-installation.test.tsplugins/fusion-plugin-compound-engineering/src/__tests__/skill-interaction-audit.test.tsplugins/fusion-plugin-compound-engineering/src/__tests__/skill-reachability.test.tsplugins/fusion-plugin-compound-engineering/src/__tests__/skill-wiring.test.tsplugins/fusion-plugin-compound-engineering/src/__tests__/sync.test.tsplugins/fusion-plugin-compound-engineering/src/__tests__/work-bridge.test.tsplugins/fusion-plugin-compound-engineering/src/artifacts/__tests__/discovery.test.tsplugins/fusion-plugin-compound-engineering/src/artifacts/discovery.tsplugins/fusion-plugin-compound-engineering/src/dashboard-view.tsxplugins/fusion-plugin-compound-engineering/src/dashboard/CeFlow.tsxplugins/fusion-plugin-compound-engineering/src/dashboard/CompoundEngineeringView.cssplugins/fusion-plugin-compound-engineering/src/dashboard/CompoundEngineeringView.tsxplugins/fusion-plugin-compound-engineering/src/dashboard/__tests__/CeFlow.test.tsxplugins/fusion-plugin-compound-engineering/src/dashboard/__tests__/CompoundEngineeringView.test.tsxplugins/fusion-plugin-compound-engineering/src/dashboard/__tests__/StageLauncher.test.tsxplugins/fusion-plugin-compound-engineering/src/dashboard/ce-question-support.tsplugins/fusion-plugin-compound-engineering/src/dashboard/hooks/__tests__/useCeSession.test.tsxplugins/fusion-plugin-compound-engineering/src/dashboard/hooks/api.tsplugins/fusion-plugin-compound-engineering/src/dashboard/hooks/useArtifacts.tsplugins/fusion-plugin-compound-engineering/src/dashboard/hooks/useCeSession.tsplugins/fusion-plugin-compound-engineering/src/dashboard/hooks/useViewportMode.tsplugins/fusion-plugin-compound-engineering/src/dashboard/test-setup.tsplugins/fusion-plugin-compound-engineering/src/index.tsplugins/fusion-plugin-compound-engineering/src/routes/__tests__/artifact-routes.test.tsplugins/fusion-plugin-compound-engineering/src/routes/artifact-routes.tsplugins/fusion-plugin-compound-engineering/src/routes/route-helpers.tsplugins/fusion-plugin-compound-engineering/src/routes/session-routes.tsplugins/fusion-plugin-compound-engineering/src/schema.tsplugins/fusion-plugin-compound-engineering/src/session/orchestrator.tsplugins/fusion-plugin-compound-engineering/src/session/session-store.tsplugins/fusion-plugin-compound-engineering/src/session/stage-registry.tsplugins/fusion-plugin-compound-engineering/src/settings.tsplugins/fusion-plugin-compound-engineering/src/skill-installation.tsplugins/fusion-plugin-compound-engineering/src/skills.tsplugins/fusion-plugin-compound-engineering/src/skills/ce-brainstorm/SKILL.mdplugins/fusion-plugin-compound-engineering/src/skills/ce-brainstorm/references/brainstorm-sections.mdplugins/fusion-plugin-compound-engineering/src/skills/ce-brainstorm/references/handoff.mdplugins/fusion-plugin-compound-engineering/src/skills/ce-brainstorm/references/html-rendering.mdplugins/fusion-plugin-compound-engineering/src/skills/ce-brainstorm/references/markdown-rendering.mdplugins/fusion-plugin-compound-engineering/src/skills/ce-brainstorm/references/synthesis-summary.mdplugins/fusion-plugin-compound-engineering/src/skills/ce-brainstorm/references/universal-brainstorming.mdplugins/fusion-plugin-compound-engineering/src/skills/ce-code-review/SKILL.mdplugins/fusion-plugin-compound-engineering/src/skills/ce-code-review/references/bulk-preview.mdplugins/fusion-plugin-compound-engineering/src/skills/ce-code-review/references/diff-scope.mdplugins/fusion-plugin-compound-engineering/src/skills/ce-code-review/references/findings-schema.jsonplugins/fusion-plugin-compound-engineering/src/skills/ce-code-review/references/persona-catalog.mdplugins/fusion-plugin-compound-engineering/src/skills/ce-code-review/references/review-output-template.mdplugins/fusion-plugin-compound-engineering/src/skills/ce-code-review/references/subagent-template.mdplugins/fusion-plugin-compound-engineering/src/skills/ce-code-review/references/tracker-defer.mdplugins/fusion-plugin-compound-engineering/src/skills/ce-code-review/references/validator-template.mdplugins/fusion-plugin-compound-engineering/src/skills/ce-code-review/references/walkthrough.mdplugins/fusion-plugin-compound-engineering/src/skills/ce-compound/SKILL.mdplugins/fusion-plugin-compound-engineering/src/skills/ce-compound/assets/resolution-template.mdplugins/fusion-plugin-compound-engineering/src/skills/ce-compound/references/concepts-vocabulary.mdplugins/fusion-plugin-compound-engineering/src/skills/ce-compound/references/schema.yamlplugins/fusion-plugin-compound-engineering/src/skills/ce-compound/references/yaml-schema.mdplugins/fusion-plugin-compound-engineering/src/skills/ce-compound/scripts/validate-frontmatter.pyplugins/fusion-plugin-compound-engineering/src/skills/ce-ideate/SKILL.mdplugins/fusion-plugin-compound-engineering/src/skills/ce-ideate/references/post-ideation-workflow.mdplugins/fusion-plugin-compound-engineering/src/skills/ce-ideate/references/universal-ideation.mdplugins/fusion-plugin-compound-engineering/src/skills/ce-ideate/references/web-research-cache.mdplugins/fusion-plugin-compound-engineering/src/skills/ce-plan/SKILL.mdplugins/fusion-plugin-compound-engineering/src/skills/ce-plan/references/deepening-workflow.mdplugins/fusion-plugin-compound-engineering/src/skills/ce-plan/references/html-rendering.mdplugins/fusion-plugin-compound-engineering/src/skills/ce-plan/references/markdown-rendering.mdplugins/fusion-plugin-compound-engineering/src/skills/ce-plan/references/plan-handoff.mdplugins/fusion-plugin-compound-engineering/src/skills/ce-plan/references/plan-sections.mdplugins/fusion-plugin-compound-engineering/src/skills/ce-plan/references/synthesis-summary.mdplugins/fusion-plugin-compound-engineering/src/skills/ce-plan/references/universal-planning.mdplugins/fusion-plugin-compound-engineering/src/skills/ce-strategy/SKILL.mdplugins/fusion-plugin-compound-engineering/src/skills/ce-strategy/references/interview.mdplugins/fusion-plugin-compound-engineering/src/skills/ce-strategy/references/strategy-template.mdplugins/fusion-plugin-compound-engineering/src/skills/ce-work/SKILL.mdplugins/fusion-plugin-compound-engineering/src/skills/ce-work/references/shipping-workflow.mdplugins/fusion-plugin-compound-engineering/src/skills/ce-work/references/tracker-defer.mdplugins/fusion-plugin-compound-engineering/src/sync/ce-task.tsplugins/fusion-plugin-compound-engineering/src/sync/pipeline-store.tsplugins/fusion-plugin-compound-engineering/src/sync/reconciler.tsplugins/fusion-plugin-compound-engineering/tsconfig.jsonplugins/fusion-plugin-compound-engineering/vitest.config.tspnpm-workspace.yaml
| @@ -0,0 +1,63 @@ | |||
| # Persona Catalog | |||
|
|
|||
| 14 reviewer personas organized into always-on, cross-cutting conditional, and stack-specific conditional layers, plus CE-specific agents. The orchestrator uses this catalog to select which reviewers to spawn for each review. | |||
There was a problem hiding this comment.
Count mismatch: "14 reviewer personas" doesn't match actual count.
Line 3 claims "14 reviewer personas" but the document lists:
- 4 always-on personas (lines 12-17)
- 2 CE always-on agents (lines 22-24)
- 7 conditional personas (lines 29-37)
- 2 stack-specific personas (lines 45-46)
- 1 CE conditional agent (line 54)
Total: 16 reviewers/agents, not 14.
If "personas" excludes "agents," the count would be 13 (4 + 7 + 2), still not 14. Consider updating line 3 to reflect the accurate total or clarifying the counting methodology.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In
`@plugins/fusion-plugin-compound-engineering/src/skills/ce-code-review/references/persona-catalog.md`
at line 3, The summary line claiming "14 reviewer personas" is incorrect; update
the opening sentence that contains the phrase "14 reviewer personas" in
persona-catalog.md to reflect the actual counts (4 always-on personas + 2 CE
always-on agents + 7 conditional personas + 2 stack-specific personas + 1 CE
conditional agent = 16 total) or clarify whether "personas" excludes "agents"
and then adjust the number accordingly so the total matches the listed items;
ensure the revised line uses the same wording context as the original to keep
intent clear.
| You produce up to two outputs depending on whether a run ID was provided: | ||
|
|
||
| 1. **Artifact file (when run ID is present).** If a Run ID appears in <review-context> below, WRITE your full analysis (all schema fields, including why_it_matters, evidence, and suggested_fix) as JSON to: | ||
| /tmp/compound-engineering/ce-code-review/{run_id}/{reviewer_name}.json |
There was a problem hiding this comment.
Path inconsistency: artifact write location mismatch.
Line 24 specifies the artifact write path as:
/tmp/compound-engineering/ce-code-review/{run_id}/{reviewer_name}.json
But line 136 states:
The one permitted exception is writing your full analysis to the `.context/` artifact path
These paths don't match. The .context/ reference appears incorrect, as:
- Line 24's
/tmp/compound-engineering/...is the concrete path used tracker-defer.md(lines 97, 102) also references/tmp/compound-engineering/ce-code-review/<run-id>/
Fix: Update line 136 to reference the correct path: /tmp/compound-engineering/ce-code-review/{run_id}/{reviewer_name}.json instead of the generic .context/ artifact path.
Also applies to: 136-136
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In
`@plugins/fusion-plugin-compound-engineering/src/skills/ce-code-review/references/subagent-template.md`
at line 24, The comment points out a path mismatch: update the reference to the
artifact write location that currently mentions ".context/" so it matches the
concrete artifact path used elsewhere. Replace the `.context/` reference with
the actual output path
`/tmp/compound-engineering/ce-code-review/{run_id}/{reviewer_name}.json`
wherever the incorrect phrase appears (e.g., the sentence mentioning ".context/"
near the analysis guidance) so all mentions (including the one at line 136)
consistently use the
`/tmp/compound-engineering/ce-code-review/{run_id}/{reviewer_name}.json` path.
|
|
||
| When auto-proceed applies (Lightweight + zero call-outs), emit a one-line announcement and continue: | ||
|
|
||
| ``` |
There was a problem hiding this comment.
Add info strings to the fenced examples.
These unlabeled code fences trip MD040, so the docs lint will stay red until each block is marked with a language like text.
Also applies to: 199-199, 246-246, 259-259, 298-298, 311-311
🧰 Tools
🪛 markdownlint-cli2 (0.22.1)
[warning] 131-131: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In
`@plugins/fusion-plugin-compound-engineering/src/skills/ce-plan/references/synthesis-summary.md`
at line 131, Several fenced code blocks in synthesis-summary.md are unlabeled
and trigger the MD040 lint; open synthesis-summary.md and add a language tag
(e.g., ```text or ```json as appropriate) to each unlabeled code fence mentioned
(the fence at the shown diff plus the other unlabeled blocks referenced at 199,
246, 259, 298, 311) so every triple-backtick block has a language identifier;
ensure the tag matches the content type (use `text` for plain examples) and save
to clear the MD040 lint errors.
- session-store: rowToSession parses JSON columns via a safeParse helper, so a corrupted currentQuestion/conversationHistory column degrades to null/[] instead of throwing and crashing reads of an otherwise-valid row (+regression test) - session-routes: validate the ?status= list filter against CE_SESSION_STATUSES (asCeSessionStatus) instead of casting an arbitrary query string with 'as never' - _harness: makeScriptedSession throws on an empty script rather than yielding undefined, surfacing test mistakes loudly - useCeSession tests: use fake timers (advanceTimersByTimeAsync) instead of real setTimeout waits for deterministic poll-interval assertions Skipped: 3 doc nits in src/skills/ce-*/references/** — those are pinned upstream ce-* skill copies (KTD5 vendored snapshot), not this repo's content.
|
Thanks for the review — addressed in Fixed (code/robustness):
Skipped (with rationale): the three docs nits in |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@plugins/fusion-plugin-compound-engineering/src/session/session-store.ts`:
- Around line 85-104: The parsed JSON may be syntactically valid but
semantically wrong (e.g., 'null' or '{}' instead of an array/object), so update
the deserialization to validate shapes and fall back when invalid: either extend
safeParse<T> to accept a validator function (e.g., isArray,
isPlanningQuestionOrNull) and return fallback when validator(parsed) is false,
or perform explicit checks inside rowToSession after parsing (validate
conversationHistory is an array and currentQuestion is null or a valid
PlanningQuestion) and replace invalid values with [] / null; reference safeParse
and rowToSession when making the change so existing callers keep correct
behavior.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: 9c22465d-3b52-4398-86cf-b3135c3430c2
📒 Files selected for processing (5)
plugins/fusion-plugin-compound-engineering/src/__tests__/_harness.tsplugins/fusion-plugin-compound-engineering/src/__tests__/session-store.test.tsplugins/fusion-plugin-compound-engineering/src/dashboard/hooks/__tests__/useCeSession.test.tsxplugins/fusion-plugin-compound-engineering/src/routes/session-routes.tsplugins/fusion-plugin-compound-engineering/src/session/session-store.ts
🚧 Files skipped from review as they are similar to previous changes (3)
- plugins/fusion-plugin-compound-engineering/src/routes/session-routes.ts
- plugins/fusion-plugin-compound-engineering/src/dashboard/hooks/tests/useCeSession.test.tsx
- plugins/fusion-plugin-compound-engineering/src/tests/_harness.ts
safeParse() previously only caught JSON syntax errors, so a semantically-wrong
but valid column ('null', '{}', a string) would rehydrate a non-array
conversationHistory that later crashed appendHistory's spread (and a bogus
currentQuestion). safeParse now takes a shape validator and falls back to []/null
on invalid shapes too. + regression test covering 'null'/'{}'.
Document why PluginSkillContribution.skillFiles silently fails to load bundled skills in live sessions (resolver only filters disk-discovered skills) and the physical-install + additionalSkillPaths-forwarding fix, as a searchable docs/solutions/ learning. Surface docs/solutions/ in AGENTS.md so agents discover it.
A new workspace-acyclicity invariant on main (run via the PR merge) flagged compound-engineering -> @fusion/dashboard -> compound-engineering: the plugin is listed in @fusion/dashboard's deps (for view loading) AND declared @fusion/dashboard as a runtime dependency, which the cycle check (deps+devDeps) and the 'bundled plugins must not depend on host packages' check both reject. The plugin's only @fusion/dashboard use is the type-only PluginDashboardViewContext import. Drop the @fusion/dashboard dependency and resolve that type via an ambient dashboard-interop.d.ts + tsconfig paths mapping (the fusion-plugin-dependency-graph interop pattern). Breaks the cycle; the host passes the real context at runtime.
…ompile plugin source With @fusion/dashboard removed from the CE plugin's deps (cycle fix), a literal import() of its dashboard-view made the dashboard typecheck the plugin source and fail to resolve the plugin's type-only @fusion/dashboard import. Use the moduleId-variable + @vite-ignore pattern (as cli-printing-press does) so tsc treats it as dynamic; Vite still resolves it at runtime.
The store/orchestrator were already multi-session (independent rows + live
handles per session); this surfaces it end to end:
- Sessions panel in the dashboard view: lists every session with stage,
status badge ("needs your input" for awaiting_input), and last activity;
stays visible while a flow is open so switching is one click. Closing a
flow returns to the overview without stopping the session.
- useCeSession.open(): adopt an existing session (pins its projectId for
answer/resume/poll); useCeSessions list hook with push-event refresh and
poll fallback while any session is mid-turn.
- DELETE /sessions/:id + orchestrator.discard(): dispose the live handle
before deleting the row (pipeline-link rows kept for task provenance);
Discard affordance on settled sessions.
- Tests: cross-session independence through one orchestrator, store delete,
route list/delete, hook open/list/remove/push/poll, view panel
open/switch/discard. 116 tests green; plugin + dashboard tsc clean.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…into gsxdsm/compound
…&A surface
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>
… session vocabulary ce-compound learning from the live-output/steering work: push-channel- alongside-pull-contract, void-safe detached turns, inactivity watchdog, transient liveActivity + persisted trace, replay suppression. Also adds a "Compound Engineering sessions" cluster to CONCEPTS.md and refreshes the plugin reference doc's Sessions section with the new transport behaviors. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
What & why
Adds
fusion-plugin-compound-engineering— a bundled Fusion plugin that brings the Compound Engineering flow into the dashboard: an artifact hub, interactive in-dashboardce-*sessions that reuse the real bundled skills, a work→board bridge, and event-driven bidirectional board↔pipeline sync. It runs alongside Fusion's native pipeline.Implements the full 9-unit plan in
docs/plans/2026-06-02-001-feat-compound-engineering-plugin-plan.md.Two reusable host capabilities (any plugin benefits)
The load-bearing pieces are foundational seams added to
@fusion/core+@fusion/engine+@fusion/dashboard, not just plugin code:ctx.createInteractiveAiSessionexposes the agent's question/await-input protocol (reimplemented in an engine seam modeled onplanning.ts; injected on route contexts only, likecreateAiSession). CarriesrequestedSkillNames/additionalSkillPathsso a plugin can load a bundled skill into a live session.ctx.emitEventcalls are forwarded to connected/api/eventsclients as project-scopedplugin:customevents, consumed by views via a newsubscribePluginEventsview-context capability (no rawEventSource).How it's built
ce-*pipeline skillsQuality
questionIdguard, and a resume→answer dead-end (now rehydrates).projectIdthreading; true SSE push (polling kept as fallback).Testing
@fusion/core,@fusion/engine,@fusion/dashboard,@fusion/clibuild + typecheck clean; dashboard SSE/plugin-route suites green;clibundled-plugin test green.DefaultResourceLoaderdiscoveringce-plan(and filtering outce-work) viaadditionalSkillPaths— the exact pathcreateFnAgentfeeds.Known residuals
Post-Deploy Monitoring & Validation
docs/artifacts and launching a stage starts an interactive session that streams a question.plugin:customevents on/api/events; sessions advance question→answer→complete; the work stage creates CE-tagged board tasks that run the normal lifecycle.started;[plugin:compound-engineering] skill install failedin logs; sessions stuckactive(push + poll both failing); board moves not advancing the pipeline (reconciler).🤖 Generated with Claude Code
Summary by CodeRabbit
New Features
Settings
Documentation