Skip to content

feat: Compound Engineering plugin with interactive sessions, work bridge, and bidirectional sync#1343

Merged
gsxdsm merged 26 commits into
mainfrom
gsxdsm/compound
Jun 3, 2026
Merged

feat: Compound Engineering plugin with interactive sessions, work bridge, and bidirectional sync#1343
gsxdsm merged 26 commits into
mainfrom
gsxdsm/compound

Conversation

@gsxdsm

@gsxdsm gsxdsm commented Jun 3, 2026

Copy link
Copy Markdown
Collaborator

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-dashboard ce-* 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:

  • Interactive agent sessions for plugin routesctx.createInteractiveAiSession exposes the agent's question/await-input protocol (reimplemented in an engine seam modeled on planning.ts; injected on route contexts only, like createAiSession). Carries requestedSkillNames / additionalSkillPaths so a plugin can load a bundled skill into a live session.
  • Real plugin event push over SSE — a plugin's ctx.emitEvent calls are forwarded to connected /api/events clients as project-scoped plugin:custom events, consumed by views via a new subscribePluginEvents view-context capability (no raw EventSource).

How it's built

Unit Summary
U1 Plugin scaffold, bundled-plugin registration
U4 Interactive AI-session host seam (the gating spike)
U2 Bundle + isolation-safe install of 7 ce-* pipeline skills
U5 Session orchestrator: turn loop, persistence, rehydrating resume, no-silent-loss
U3 Artifact hub: discovery, read/preview routes, dashboard view with empty/partial/error states
U6 CE flow renderer (rich + degraded chat fallback), stage launcher, skill-interaction audit
U7 Work→board bridge: CE-tagged, traceable tasks (FN-5719 link records)
U8 CE-pipeline state machine + bidirectional sync (separate ownership, reconciler converges dropped events)
U9 Settings schema, README, docs

Quality

  • Simplify pass (4 angles) consolidated cross-unit duplication built by isolated agents.
  • Code review (correctness angles) traced the riskiest seams clean (DI wiring, no hook-loop, view binding) and found 5 bugs, all fixed: deleted-task pipeline wedge, human-slow staleness misclassification, registry-order advancement fragility, missing questionId guard, and a resume→answer dead-end (now rehydrates).
  • Residuals fixed: session-identity projectId threading; true SSE push (polling kept as fallback).

Testing

  • Plugin: 99 tests (node + jsdom). @fusion/core, @fusion/engine, @fusion/dashboard, @fusion/cli build + typecheck clean; dashboard SSE/plugin-route suites green; cli bundled-plugin test green.
  • New skill-loading is proven by a real DefaultResourceLoader discovering ce-plan (and filtering out ce-work) via additionalSkillPaths — the exact path createFnAgent feeds.
  • Every code-review fix and residual fix ships with a regression test confirmed failing pre-fix.

Known residuals

  • Live-model end-to-end is not automated — a real agent run needs a live model + auth, which can't run in CI. The seam→agent→skill chain is proven by unit/integration tests at both ends + the real loader; only the final "talk to a real model" hop is unautomatable. Recommend a manual smoke test of one stage (e.g. brainstorm) post-merge.

Post-Deploy Monitoring & Validation

  • Validate: open the Compound Engineering dashboard view; confirm the artifact hub lists existing docs/ artifacts and launching a stage starts an interactive session that streams a question.
  • Healthy signals: plugin:custom events on /api/events; sessions advance question→answer→complete; the work stage creates CE-tagged board tasks that run the normal lifecycle.
  • Failure signals / triggers: plugin fails to reach started; [plugin:compound-engineering] skill install failed in logs; sessions stuck active (push + poll both failing); board moves not advancing the pipeline (reconciler).
  • Rollback: disable the plugin in Settings → Plugins (the host seams are additive and inert when unused).
  • Window/owner: first 24h post-merge, PR author.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Compound Engineering plugin: dashboard artifact hub, stage launcher, CeFlow interactive sessions, rich question UI with chat fallback, work→board bridge creating linked tasks, and bidirectional pipeline↔board synchronization with reconciliation.
    • Live interactive multi-turn AI sessions for plugins and dashboard SSE-based plugin event subscriptions (subscribePluginEvents).
  • Settings

    • Plugin settings to choose session provider/model, enabled stages, and reconciliation behavior (hooks vs scheduled).
  • Documentation

    • Full docs, skill specs, operator guides, and settings reference for the Compound Engineering plugin.

gsxdsm added 16 commits June 2, 2026 18:29
…(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.
@coderabbitai

coderabbitai Bot commented Jun 3, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It 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 reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds 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.

Changes

Compound Engineering Feature Slice

Layer / File(s) Summary
Engine interactive-session seam & adapter
packages/engine/*, packages/core/*, packages/cli/*
Adds interactive session types and factory, agent-output parsing/repair helpers, interactive session adapter, engine->core wiring and exported types, agent options (additionalSkillPaths), and seam tests validating parsing, retry, and DI behavior.
Dashboard SSE, view, hooks & tests
packages/dashboard/..., plugins/fusion-plugin-compound-engineering/src/dashboard/...
Adds plugin-custom SSE emitter/listener, App-level subscribePluginEvents, subscribe capability in dashboard view context, bundled dashboard view loader, CE view components (CompoundEngineeringView, CeFlow), hooks (useArtifacts, useCeSession, useViewportMode), API client, styles, and corresponding RTL/Vitest tests.
Compound Engineering plugin runtime, routes, storage, skills, docs & tests
plugins/fusion-plugin-compound-engineering/*
New plugin package: manifest/package.json/README, plugin export wiring, onLoad skill installation, settings/stage-registry, session store + schema, orchestrator (multi-turn lifecycle, timeout/recover, work→board bridge), artifact discovery/read, session & artifact routes, skill installation helpers, bundled ce-* skills and many reference docs, and extensive unit/integration tests.

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
Loading

Estimated code review effort
🎯 4 (Complex) | ⏱️ ~60 minutes

Poem

A rabbit taps the merge with pride,
Bundled skills now hop inside.
Sessions speak in turns that flow,
SSE winds help updates go.
Boards and pipelines sync in tune—
Artifacts bloom like buds in June.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch gsxdsm/compound

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 8

🧹 Nitpick comments (5)
packages/dashboard/app/plugins/registerBundledPluginViews.ts (1)

40-50: ⚡ Quick win

Consider catching import failures for consistency.

The CLI plugin loaders (lines 52-82) wrap their imports in try/catch to handle module-not-found and other import failures, but loadCompoundEngineeringView only 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 value

Non-standard _meta section in JSON Schema.

The _meta section (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 _meta documentation is valuable in-context. However, if the schema is also validated by standard JSON Schema tooling, consider:

  1. Moving _meta content to a separate documentation file referenced by the schema
  2. Using standard JSON Schema $comment or description fields for documentation
  3. Adding a note that _meta is documentation-only and should be ignored by validators

If the current approach is intentional for AI consumption, no change needed—just document that _meta is 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 value

Add defensive handling for decodeURIComponent when decoding request.params.id.

Both the /artifacts/:id and /artifacts/:id/preview.html handlers call decodeURIComponent(request.params.id); it can throw on malformed percent-encoding, so wrap the decode in try/catch and 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 value

Minor 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 value

Polling effect may restart unnecessarily when busy toggles.

The polling effect at lines 177-199 includes busy in its dependency array (line 199). When busy becomes true (during answer/resume/start), the effect cleanup runs and clears the interval. Then when busy becomes false, 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 busy changes. This is benign (the guard prevents actual network calls), but slightly inefficient.

Consider moving the busy check 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 busy

However, 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

📥 Commits

Reviewing files that changed from the base of the PR and between 6c7ed1e and 5043084.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (115)
  • .changeset/compound-engineering-plugin-scaffold.md
  • docs/plans/2026-06-02-001-feat-compound-engineering-plugin-plan.md
  • docs/plugins/compound-engineering.md
  • packages/cli/src/plugins/__tests__/bundled-plugin-install.test.ts
  • packages/cli/src/plugins/bundled-plugin-install.ts
  • packages/core/src/__tests__/interactive-ai-session-seam.test.ts
  • packages/core/src/ai-engine-loader.ts
  • packages/core/src/index.ts
  • packages/core/src/plugin-loader.ts
  • packages/core/src/plugin-types.ts
  • packages/dashboard/app/App.tsx
  • packages/dashboard/app/plugins/registerBundledPluginViews.ts
  • packages/dashboard/app/plugins/types.ts
  • packages/dashboard/package.json
  • packages/dashboard/src/__tests__/sse.test.ts
  • packages/dashboard/src/plugin-routes.ts
  • packages/dashboard/src/sse.ts
  • packages/engine/src/__tests__/compound-engineering-skill-resolution.test.ts
  • packages/engine/src/__tests__/interactive-ai-session.test.ts
  • packages/engine/src/__tests__/plugin-runner.test.ts
  • packages/engine/src/index.ts
  • packages/engine/src/interactive-ai-session.ts
  • packages/engine/src/pi.ts
  • plugins/fusion-plugin-compound-engineering/.gitignore
  • plugins/fusion-plugin-compound-engineering/README.md
  • plugins/fusion-plugin-compound-engineering/manifest.json
  • plugins/fusion-plugin-compound-engineering/package.json
  • plugins/fusion-plugin-compound-engineering/src/__tests__/_harness.ts
  • plugins/fusion-plugin-compound-engineering/src/__tests__/manifest.test.ts
  • plugins/fusion-plugin-compound-engineering/src/__tests__/orchestrator-flow.test.ts
  • plugins/fusion-plugin-compound-engineering/src/__tests__/orchestrator-interrupt-resume.test.ts
  • plugins/fusion-plugin-compound-engineering/src/__tests__/session-routes.test.ts
  • plugins/fusion-plugin-compound-engineering/src/__tests__/session-store.test.ts
  • plugins/fusion-plugin-compound-engineering/src/__tests__/settings.test.ts
  • plugins/fusion-plugin-compound-engineering/src/__tests__/skill-installation.test.ts
  • plugins/fusion-plugin-compound-engineering/src/__tests__/skill-interaction-audit.test.ts
  • plugins/fusion-plugin-compound-engineering/src/__tests__/skill-reachability.test.ts
  • plugins/fusion-plugin-compound-engineering/src/__tests__/skill-wiring.test.ts
  • plugins/fusion-plugin-compound-engineering/src/__tests__/sync.test.ts
  • plugins/fusion-plugin-compound-engineering/src/__tests__/work-bridge.test.ts
  • plugins/fusion-plugin-compound-engineering/src/artifacts/__tests__/discovery.test.ts
  • plugins/fusion-plugin-compound-engineering/src/artifacts/discovery.ts
  • plugins/fusion-plugin-compound-engineering/src/dashboard-view.tsx
  • plugins/fusion-plugin-compound-engineering/src/dashboard/CeFlow.tsx
  • plugins/fusion-plugin-compound-engineering/src/dashboard/CompoundEngineeringView.css
  • plugins/fusion-plugin-compound-engineering/src/dashboard/CompoundEngineeringView.tsx
  • plugins/fusion-plugin-compound-engineering/src/dashboard/__tests__/CeFlow.test.tsx
  • plugins/fusion-plugin-compound-engineering/src/dashboard/__tests__/CompoundEngineeringView.test.tsx
  • plugins/fusion-plugin-compound-engineering/src/dashboard/__tests__/StageLauncher.test.tsx
  • plugins/fusion-plugin-compound-engineering/src/dashboard/ce-question-support.ts
  • plugins/fusion-plugin-compound-engineering/src/dashboard/hooks/__tests__/useCeSession.test.tsx
  • plugins/fusion-plugin-compound-engineering/src/dashboard/hooks/api.ts
  • plugins/fusion-plugin-compound-engineering/src/dashboard/hooks/useArtifacts.ts
  • plugins/fusion-plugin-compound-engineering/src/dashboard/hooks/useCeSession.ts
  • plugins/fusion-plugin-compound-engineering/src/dashboard/hooks/useViewportMode.ts
  • plugins/fusion-plugin-compound-engineering/src/dashboard/test-setup.ts
  • plugins/fusion-plugin-compound-engineering/src/index.ts
  • plugins/fusion-plugin-compound-engineering/src/routes/__tests__/artifact-routes.test.ts
  • plugins/fusion-plugin-compound-engineering/src/routes/artifact-routes.ts
  • plugins/fusion-plugin-compound-engineering/src/routes/route-helpers.ts
  • plugins/fusion-plugin-compound-engineering/src/routes/session-routes.ts
  • plugins/fusion-plugin-compound-engineering/src/schema.ts
  • plugins/fusion-plugin-compound-engineering/src/session/orchestrator.ts
  • plugins/fusion-plugin-compound-engineering/src/session/session-store.ts
  • plugins/fusion-plugin-compound-engineering/src/session/stage-registry.ts
  • plugins/fusion-plugin-compound-engineering/src/settings.ts
  • plugins/fusion-plugin-compound-engineering/src/skill-installation.ts
  • plugins/fusion-plugin-compound-engineering/src/skills.ts
  • plugins/fusion-plugin-compound-engineering/src/skills/ce-brainstorm/SKILL.md
  • plugins/fusion-plugin-compound-engineering/src/skills/ce-brainstorm/references/brainstorm-sections.md
  • plugins/fusion-plugin-compound-engineering/src/skills/ce-brainstorm/references/handoff.md
  • plugins/fusion-plugin-compound-engineering/src/skills/ce-brainstorm/references/html-rendering.md
  • plugins/fusion-plugin-compound-engineering/src/skills/ce-brainstorm/references/markdown-rendering.md
  • plugins/fusion-plugin-compound-engineering/src/skills/ce-brainstorm/references/synthesis-summary.md
  • plugins/fusion-plugin-compound-engineering/src/skills/ce-brainstorm/references/universal-brainstorming.md
  • plugins/fusion-plugin-compound-engineering/src/skills/ce-code-review/SKILL.md
  • plugins/fusion-plugin-compound-engineering/src/skills/ce-code-review/references/bulk-preview.md
  • plugins/fusion-plugin-compound-engineering/src/skills/ce-code-review/references/diff-scope.md
  • plugins/fusion-plugin-compound-engineering/src/skills/ce-code-review/references/findings-schema.json
  • plugins/fusion-plugin-compound-engineering/src/skills/ce-code-review/references/persona-catalog.md
  • plugins/fusion-plugin-compound-engineering/src/skills/ce-code-review/references/review-output-template.md
  • plugins/fusion-plugin-compound-engineering/src/skills/ce-code-review/references/subagent-template.md
  • plugins/fusion-plugin-compound-engineering/src/skills/ce-code-review/references/tracker-defer.md
  • plugins/fusion-plugin-compound-engineering/src/skills/ce-code-review/references/validator-template.md
  • plugins/fusion-plugin-compound-engineering/src/skills/ce-code-review/references/walkthrough.md
  • plugins/fusion-plugin-compound-engineering/src/skills/ce-compound/SKILL.md
  • plugins/fusion-plugin-compound-engineering/src/skills/ce-compound/assets/resolution-template.md
  • plugins/fusion-plugin-compound-engineering/src/skills/ce-compound/references/concepts-vocabulary.md
  • plugins/fusion-plugin-compound-engineering/src/skills/ce-compound/references/schema.yaml
  • plugins/fusion-plugin-compound-engineering/src/skills/ce-compound/references/yaml-schema.md
  • plugins/fusion-plugin-compound-engineering/src/skills/ce-compound/scripts/validate-frontmatter.py
  • plugins/fusion-plugin-compound-engineering/src/skills/ce-ideate/SKILL.md
  • plugins/fusion-plugin-compound-engineering/src/skills/ce-ideate/references/post-ideation-workflow.md
  • plugins/fusion-plugin-compound-engineering/src/skills/ce-ideate/references/universal-ideation.md
  • plugins/fusion-plugin-compound-engineering/src/skills/ce-ideate/references/web-research-cache.md
  • plugins/fusion-plugin-compound-engineering/src/skills/ce-plan/SKILL.md
  • plugins/fusion-plugin-compound-engineering/src/skills/ce-plan/references/deepening-workflow.md
  • plugins/fusion-plugin-compound-engineering/src/skills/ce-plan/references/html-rendering.md
  • plugins/fusion-plugin-compound-engineering/src/skills/ce-plan/references/markdown-rendering.md
  • plugins/fusion-plugin-compound-engineering/src/skills/ce-plan/references/plan-handoff.md
  • plugins/fusion-plugin-compound-engineering/src/skills/ce-plan/references/plan-sections.md
  • plugins/fusion-plugin-compound-engineering/src/skills/ce-plan/references/synthesis-summary.md
  • plugins/fusion-plugin-compound-engineering/src/skills/ce-plan/references/universal-planning.md
  • plugins/fusion-plugin-compound-engineering/src/skills/ce-strategy/SKILL.md
  • plugins/fusion-plugin-compound-engineering/src/skills/ce-strategy/references/interview.md
  • plugins/fusion-plugin-compound-engineering/src/skills/ce-strategy/references/strategy-template.md
  • plugins/fusion-plugin-compound-engineering/src/skills/ce-work/SKILL.md
  • plugins/fusion-plugin-compound-engineering/src/skills/ce-work/references/shipping-workflow.md
  • plugins/fusion-plugin-compound-engineering/src/skills/ce-work/references/tracker-defer.md
  • plugins/fusion-plugin-compound-engineering/src/sync/ce-task.ts
  • plugins/fusion-plugin-compound-engineering/src/sync/pipeline-store.ts
  • plugins/fusion-plugin-compound-engineering/src/sync/reconciler.ts
  • plugins/fusion-plugin-compound-engineering/tsconfig.json
  • plugins/fusion-plugin-compound-engineering/vitest.config.ts
  • pnpm-workspace.yaml

Comment thread plugins/fusion-plugin-compound-engineering/src/session/session-store.ts Outdated
@@ -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.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

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

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

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:

  1. Line 24's /tmp/compound-engineering/... is the concrete path used
  2. 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:

```

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

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.
@gsxdsm

gsxdsm commented Jun 3, 2026

Copy link
Copy Markdown
Collaborator Author

Thanks for the review — addressed in fb1f726f3:

Fixed (code/robustness):

  • session-store.ts JSON.parserowToSession now parses via a safeParse helper; a corrupted currentQuestion/conversationHistory column degrades to null/[] instead of throwing and crashing reads of an otherwise-valid row. + regression test.
  • session-routes.ts as never — the ?status= list filter is validated against CE_SESSION_STATUSES via a new asCeSessionStatus() guard (invalid strings → undefined), no more type-safety bypass. + validation test.
  • _harness.ts empty scriptmakeScriptedSession now throws on an empty script rather than returning undefined, so the mistake surfaces loudly.
  • useCeSession.test.tsx real timers — both poll tests now use vi.useFakeTimers() + advanceTimersByTimeAsync, with an afterEach restoring real timers.

Skipped (with rationale): the three docs nits in src/skills/ce-code-review/** and src/skills/ce-plan/** (persona count, artifact-path inconsistency, MD040 fenced-code language) are in pinned upstream ce-* skill copies — a vendored snapshot bundled per the plan's KTD5. Editing them would diverge our copy from upstream; they aren't this repo's content to correct, and repo CI Lint passes on them as-is. They should be fixed upstream in the compound-engineering plugin.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

📥 Commits

Reviewing files that changed from the base of the PR and between 5043084 and fb1f726.

📒 Files selected for processing (5)
  • plugins/fusion-plugin-compound-engineering/src/__tests__/_harness.ts
  • plugins/fusion-plugin-compound-engineering/src/__tests__/session-store.test.ts
  • plugins/fusion-plugin-compound-engineering/src/dashboard/hooks/__tests__/useCeSession.test.tsx
  • plugins/fusion-plugin-compound-engineering/src/routes/session-routes.ts
  • plugins/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

Comment thread plugins/fusion-plugin-compound-engineering/src/session/session-store.ts Outdated
gsxdsm and others added 9 commits June 3, 2026 07:10
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>
…&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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant