Commit Graph

19 Commits

Author SHA1 Message Date
gsxdsm
0106eee4ff feat(compound-engineering): live agent output, steering, and a real Q&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>
2026-06-03 14:17:40 -07:00
gsxdsm
7f8db9f892 feat(compound-engineering): manage and work across multiple CE sessions
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>
2026-06-03 13:47:02 -07:00
gsxdsm
8c170480dd fix(compound-engineering): break dashboard dependency cycle
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.
2026-06-03 07:59:20 -07:00
gsxdsm
1cf0521a0a fix(compound-engineering): validate parsed JSON shape in rowToSession
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'/'{}'.
2026-06-03 07:10:10 -07:00
gsxdsm
fb1f726f39 fix(compound-engineering): address code review feedback
- 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.
2026-06-02 23:38:43 -07:00
gsxdsm
504308422d docs: update CE transport docs for SSE push + projectId session identity 2026-06-02 21:29:59 -07:00
gsxdsm
b3c7601e79 feat(core,dashboard,compound-engineering): real plugin event push over 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.
2026-06-02 21:28:57 -07:00
gsxdsm
79e66e414f fix(compound-engineering): thread projectId through session answer/resume/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.
2026-06-02 21:12:20 -07:00
gsxdsm
f5be21178f feat(core,engine,compound-engineering): make CE sub-agent sessions load 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.
2026-06-02 20:55:01 -07:00
gsxdsm
08312bb59d fix(compound-engineering): correctness fixes from code review
- 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).
2026-06-02 20:42:44 -07:00
gsxdsm
061297b432 refactor(compound-engineering): consolidate duplication from simplify 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
2026-06-02 20:15:29 -07:00
gsxdsm
f6c8aeeb04 feat(compound-engineering): settings schema, getters, docs (U9)
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).
2026-06-02 19:58:35 -07:00
gsxdsm
4dd7509f53 feat(compound-engineering): CE-pipeline state model + bidirectional sync (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).
2026-06-02 19:50:43 -07:00
gsxdsm
b71e531735 feat(compound-engineering): work->board bridge with traceable tasks (U7)
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.
2026-06-02 19:42:14 -07:00
gsxdsm
f251195ca2 feat(compound-engineering): CE flow renderer, stage launcher, audit (U6)
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.
2026-06-02 19:34:01 -07:00
gsxdsm
8bb459702c feat(compound-engineering): artifact hub view, discovery, read routes (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).
2026-06-02 19:25:54 -07:00
gsxdsm
0189477d9d feat(compound-engineering): session orchestrator, persistence, resume (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.
2026-06-02 19:13:43 -07:00
gsxdsm
6d12ee3e9b feat(compound-engineering): bundle and install ce-* pipeline skills (U2)
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.
2026-06-02 18:59:41 -07:00
gsxdsm
3cf13dd25b feat(compound-engineering): scaffold bundled CE plugin shell (U1) 2026-06-02 18:29:37 -07:00