diff --git a/.changeset/clean-compound-pipeline.md b/.changeset/clean-compound-pipeline.md
index 104f7734e9..c0f2de9fc5 100644
--- a/.changeset/clean-compound-pipeline.md
+++ b/.changeset/clean-compound-pipeline.md
@@ -2,6 +2,6 @@
"@runfusion/fusion": minor
---
-summary: Support repeatable Compound Engineering cycles with project-scoped progress and artifact collections.
+summary: Guide repeatable Compound Engineering cycles from product grounding through reusable learnings.
category: feature
-dev: Adds collection-aware stages, safer session controls, explicit choice confirmation, and terminal Work progression.
+dev: Adds project-scoped sessions, collection-aware stages, Work quality gates, and terminal Compound progression.
diff --git a/docs/screenshots/compound-engineering/pipeline-desktop.png b/docs/screenshots/compound-engineering/pipeline-desktop.png
index 7791a07b2f..f13368fc77 100644
--- a/docs/screenshots/compound-engineering/pipeline-desktop.png
+++ b/docs/screenshots/compound-engineering/pipeline-desktop.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:a6c218824cbd21ec36305437e2adc5c810163ab25cd28fdee705bb12ddf48b93
-size 270173
+oid sha256:06d35e52a88966234de0aef50cc471a474a80422789cfaba5e3b8133f189e2d8
+size 251273
diff --git a/docs/screenshots/compound-engineering/pipeline-mobile.png b/docs/screenshots/compound-engineering/pipeline-mobile.png
index 668a8fc751..1eaa719035 100644
--- a/docs/screenshots/compound-engineering/pipeline-mobile.png
+++ b/docs/screenshots/compound-engineering/pipeline-mobile.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:9bbcbf6767d6982ebb4340498396dc1453eb2e7458c3f0877c20d7a4f5ece9d3
-size 95990
+oid sha256:5ddba7235bdf54679910eae60d9716ea631fec8ef5e0a8be651e197fca243225
+size 98300
diff --git a/plugins/fusion-plugin-compound-engineering/src/__tests__/stage-registry.test.ts b/plugins/fusion-plugin-compound-engineering/src/__tests__/stage-registry.test.ts
index 6545ad778a..3ee1c1ddd5 100644
--- a/plugins/fusion-plugin-compound-engineering/src/__tests__/stage-registry.test.ts
+++ b/plugins/fusion-plugin-compound-engineering/src/__tests__/stage-registry.test.ts
@@ -8,20 +8,21 @@ describe("compound engineering stage registry", () => {
const stageIds = listStages().map((stage) => stage.stageId);
const pipelineStageIds = listPipelineStages().map((stage) => stage.stageId);
- expect(stageIds.slice(0, 5)).toEqual(["strategy", "ideate", "brainstorm", "plan", "work"]);
+ expect(stageIds.slice(0, 6)).toEqual(["strategy", "ideate", "brainstorm", "plan", "work", "compound"]);
expect(stageIds.at(-1)).toBe("debug");
expect(stageIds.filter((stageId) => stageId === "debug")).toHaveLength(1);
expect(stageIds.indexOf("plan")).toBeLessThan(stageIds.indexOf("work"));
- expect(stageIds.indexOf("work")).toBeLessThan(stageIds.indexOf("debug"));
- expect(pipelineStageIds).toEqual(["strategy", "ideate", "brainstorm", "plan", "work"]);
+ expect(stageIds.indexOf("compound")).toBeLessThan(stageIds.indexOf("debug"));
+ expect(pipelineStageIds).toEqual(["brainstorm", "plan", "work", "compound"]);
});
- it("advances through every automatic transition and treats work as terminal", () => {
- expect(nextStageAfter("strategy")).toBe("ideate");
- expect(nextStageAfter("ideate")).toBe("brainstorm");
+ it("advances through delivery into compounding and treats compound as terminal", () => {
+ expect(nextStageAfter("strategy")).toBeUndefined();
+ expect(nextStageAfter("ideate")).toBeUndefined();
expect(nextStageAfter("brainstorm")).toBe("plan");
expect(nextStageAfter("plan")).toBe("work");
- expect(nextStageAfter("work")).toBeUndefined();
+ expect(nextStageAfter("work")).toBe("compound");
+ expect(nextStageAfter("compound")).toBeUndefined();
expect(nextStageAfter("debug")).toBeUndefined();
});
@@ -47,7 +48,7 @@ describe("compound engineering stage registry", () => {
expect(stage).toMatchObject({
stageId: "debug",
- order: 600,
+ order: 700,
skillId: "ce-debug",
artifactLocation: "docs/debug/",
artifactGlob: "docs/debug/**/*.md",
diff --git a/plugins/fusion-plugin-compound-engineering/src/__tests__/sync.test.ts b/plugins/fusion-plugin-compound-engineering/src/__tests__/sync.test.ts
index 89f0335f9d..d2eb74da90 100644
--- a/plugins/fusion-plugin-compound-engineering/src/__tests__/sync.test.ts
+++ b/plugins/fusion-plugin-compound-engineering/src/__tests__/sync.test.ts
@@ -183,21 +183,37 @@ describe("U8 reconciler (convergence + outbound)", () => {
expect(state.currentStage).toBe("work");
});
- it("completing work terminates the automatic pipeline without creating a debug task", async () => {
+ it("completing work advances to Compound without creating a debug task", async () => {
const { cePipelineId, task } = await landPipeline("work");
const before = await taskStore.listTasks();
await moveTo(task.id, "done");
const result = await reconcileCePipelines(ctx);
+ expect(result.advanced).toBe(1);
+ expect(result.tasksCreated).toBe(1);
+ expect(getCePipelineStore(ctx).getState(cePipelineId)).toMatchObject({
+ currentStage: "compound",
+ status: "awaiting_board",
+ });
+ expect(await taskStore.listTasks()).toHaveLength(before.length + 1);
+ expect(getCePipelineStore(ctx).listByPipeline(cePipelineId).some((link) => link.ceStageId === "debug")).toBe(false);
+ });
+
+ it("completing Compound closes the delivery loop without creating another task", async () => {
+ const { cePipelineId, task } = await landPipeline("compound");
+ const before = await taskStore.listTasks();
+
+ await moveTo(task.id, "done");
+ const result = await reconcileCePipelines(ctx);
+
expect(result.advanced).toBe(1);
expect(result.tasksCreated).toBe(0);
expect(getCePipelineStore(ctx).getState(cePipelineId)).toMatchObject({
- currentStage: "work",
+ currentStage: "compound",
status: "completed",
});
expect(await taskStore.listTasks()).toHaveLength(before.length);
- expect(getCePipelineStore(ctx).listByPipeline(cePipelineId).some((link) => link.ceStageId === "debug")).toBe(false);
});
it("outbound: advancing the pipeline propagates a NEW next-stage board task", async () => {
diff --git a/plugins/fusion-plugin-compound-engineering/src/artifacts/__tests__/discovery.test.ts b/plugins/fusion-plugin-compound-engineering/src/artifacts/__tests__/discovery.test.ts
index 9d6716683f..10bda3d56f 100644
--- a/plugins/fusion-plugin-compound-engineering/src/artifacts/__tests__/discovery.test.ts
+++ b/plugins/fusion-plugin-compound-engineering/src/artifacts/__tests__/discovery.test.ts
@@ -78,7 +78,7 @@ describe("discoverArtifacts", () => {
expect(byStage.plan.label).toBe("Plan");
expect(byStage.work.entries[0]).toMatchObject({ path: "docs/work/work.md" });
expect(byStage.debug.entries[0]).toMatchObject({ path: "docs/debug/debug.md" });
- expect(byStage.solution.entries).toHaveLength(1);
+ expect(byStage.compound.entries).toHaveLength(1);
// Every group present is flagged present.
expect(byStage.ideate.present).toBe(true);
expect(byStage.brainstorm.entries[0]).toMatchObject({ path: "docs/brainstorms/requirements.md" });
diff --git a/plugins/fusion-plugin-compound-engineering/src/artifacts/discovery.ts b/plugins/fusion-plugin-compound-engineering/src/artifacts/discovery.ts
index 175bb835b4..deb3a46a43 100644
--- a/plugins/fusion-plugin-compound-engineering/src/artifacts/discovery.ts
+++ b/plugins/fusion-plugin-compound-engineering/src/artifacts/discovery.ts
@@ -65,7 +65,7 @@ function stageLocations(): ConventionalLocation[] {
const path = definition.artifactLocation.replace(/\/$/, "");
return {
stage: definition.stageId,
- label: definition.label,
+ label: definition.stageId === "compound" ? "Learnings" : definition.label,
path,
kind: definition.artifactLocation.endsWith("/") ? "directory" : "file",
};
@@ -76,7 +76,6 @@ function conventionalLocations(): ConventionalLocation[] {
return [
...stageLocations(),
{ stage: "brainstorm", label: "Brainstorms", path: "docs/brainstorms", kind: "directory" },
- { stage: "solution", label: "Solutions", path: "docs/solutions", kind: "directory" },
{ stage: "concepts", label: "Concepts", path: "CONCEPTS.md", kind: "file" },
];
}
diff --git a/plugins/fusion-plugin-compound-engineering/src/dashboard/CompoundEngineeringView.css b/plugins/fusion-plugin-compound-engineering/src/dashboard/CompoundEngineeringView.css
index d95afc69a8..22523b2fa6 100644
--- a/plugins/fusion-plugin-compound-engineering/src/dashboard/CompoundEngineeringView.css
+++ b/plugins/fusion-plugin-compound-engineering/src/dashboard/CompoundEngineeringView.css
@@ -132,6 +132,11 @@ The shared ViewHeader sits directly above plugin content, so the body needs top
gap: calc(var(--space-sm) - var(--space-xs) / 2);
}
+/* Keep multi-run histories scannable; older artifacts remain available on demand. */
+.ce-group-more { border-top: 1px solid var(--border); padding-top: var(--space-xs); }
+.ce-group-more summary { color: var(--text-muted); cursor: pointer; font-size: 0.74rem; font-weight: 600; }
+.ce-group-more[open] summary { margin-bottom: var(--space-xs); color: var(--text); }
+
.ce-artifact {
display: flex;
align-items: center;
@@ -748,21 +753,30 @@ The pipeline overview must communicate progression and the next useful action at
.ce-pipeline-header h2 { margin: 0; font-size: 1rem; font-weight: 650; }
.ce-pipeline-header p { margin: calc(var(--space-xs) / 2) 0 0; color: var(--text-muted); font-size: 0.78rem; }
.ce-investigate { display: inline-flex; align-items: center; gap: var(--space-xs); flex: none; }
-.ce-stage-rail { display: grid; grid-template-columns: repeat(5, minmax(0, 1fr)); list-style: none; margin: 0; padding: 0; }
+.ce-pipeline-block { display: flex; flex-direction: column; gap: var(--space-xs); }
+.ce-pipeline-block-heading { display: flex; align-items: baseline; justify-content: space-between; gap: var(--space-md); }
+.ce-pipeline-block-heading h3 { margin: 0; font-size: 0.78rem; font-weight: 650; }
+.ce-pipeline-block-heading span { color: var(--text-dim); font-size: 0.7rem; }
+.ce-stage-rail { display: grid; grid-template-columns: repeat(auto-fit, minmax(12rem, 1fr)); list-style: none; margin: 0; padding: 0; }
+.ce-stage-rail[data-count="2"] { grid-template-columns: repeat(2, minmax(0, 1fr)); }
+.ce-stage-rail[data-count="4"] { grid-template-columns: repeat(4, minmax(0, 1fr)); }
.ce-stage-step { position: relative; min-width: 0; border-block: 1px solid var(--border); border-left: 1px solid var(--border); background: var(--surface); }
.ce-stage-step:first-child { border-radius: var(--radius-md) 0 0 var(--radius-md); }
.ce-stage-step:last-child { border-right: 1px solid var(--border); border-radius: 0 var(--radius-md) var(--radius-md) 0; }
.ce-stage-step::after { position: absolute; z-index: 2; top: calc(var(--space-lg) + 1px); right: calc(var(--space-xs) * -1); width: calc(var(--space-sm) - 2px); height: calc(var(--space-sm) - 2px); border-top: 1px solid var(--border); border-right: 1px solid var(--border); background: inherit; content: ""; transform: rotate(45deg); }
.ce-stage-step:last-child::after { display: none; }
-.ce-stage-main { display: flex; width: 100%; min-height: calc(var(--space-2xl) * 2.25); align-items: flex-start; gap: var(--space-sm); border: 0; background: transparent; color: inherit; cursor: pointer; padding: var(--space-md); text-align: left; }
+.ce-stage-main { display: flex; width: 100%; min-height: calc(var(--space-2xl) * 2.65); align-items: flex-start; gap: var(--space-sm); border: 0; background: transparent; color: inherit; cursor: pointer; padding: var(--space-md); text-align: left; }
.ce-stage-main:hover:not(:disabled), .ce-stage-main:focus-visible { background: var(--card-hover); }
.ce-stage-main:focus-visible { position: relative; z-index: 3; outline: 2px solid var(--color-info); outline-offset: -2px; }
.ce-stage-icon { display: grid; width: calc(var(--space-lg) + var(--space-xs)); height: calc(var(--space-lg) + var(--space-xs)); flex: none; place-items: center; border: 1px solid var(--border); border-radius: 50%; color: var(--text-muted); }
-.ce-stage-step[data-status="complete"] .ce-stage-icon { border-color: color-mix(in srgb, var(--color-success) 45%, var(--border)); color: var(--color-success); }
+.ce-stage-step[data-status="current"] .ce-stage-icon,
+.ce-stage-step[data-status="history"] .ce-stage-icon,
+.ce-stage-step[data-status="last-run-complete"] .ce-stage-icon { border-color: color-mix(in srgb, var(--color-success) 45%, var(--border)); color: var(--color-success); }
.ce-stage-step[data-status="active"] .ce-stage-icon, .ce-stage-step[data-status="needs-input"] .ce-stage-icon { border-color: var(--color-info); background: color-mix(in srgb, var(--color-info) 12%, var(--surface)); color: var(--color-info); }
.ce-stage-step[data-status="needs-input"] .ce-stage-status { color: var(--color-warning); }
.ce-stage-copy { display: flex; min-width: 0; flex-direction: column; gap: calc(var(--space-xs) / 2); }
.ce-stage-copy strong { overflow: hidden; font-size: 0.86rem; text-overflow: ellipsis; white-space: nowrap; }
+.ce-stage-purpose { color: var(--text-muted); font-size: 0.7rem; line-height: 1.25; }
.ce-stage-status { color: var(--text-muted); font-size: 0.7rem; }
.ce-stage-artifact { display: flex; width: calc(100% - var(--space-lg)); min-width: 0; align-items: center; gap: var(--space-xs); margin: 0 var(--space-sm) var(--space-sm); border: 0; background: transparent; color: var(--text-dim); cursor: pointer; font: inherit; font-size: 0.68rem; text-align: left; }
.ce-stage-artifact span { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
@@ -793,7 +807,8 @@ The pipeline overview must communicate progression and the next useful action at
@media (max-width: 768px) {
.ce-pipeline-header { align-items: center; }
- .ce-stage-rail { grid-template-columns: 1fr; }
+ .ce-stage-rail,
+ .ce-stage-rail[data-count] { grid-template-columns: 1fr; }
.ce-stage-step, .ce-stage-step:first-child, .ce-stage-step:last-child { border: 1px solid var(--border); border-bottom: 0; border-radius: 0; }
.ce-stage-step:first-child { border-radius: var(--radius-md) var(--radius-md) 0 0; }
.ce-stage-step:last-child { border-bottom: 1px solid var(--border); border-radius: 0 0 var(--radius-md) var(--radius-md); }
diff --git a/plugins/fusion-plugin-compound-engineering/src/dashboard/CompoundEngineeringView.tsx b/plugins/fusion-plugin-compound-engineering/src/dashboard/CompoundEngineeringView.tsx
index f4771f9ae8..dc1700ef52 100644
--- a/plugins/fusion-plugin-compound-engineering/src/dashboard/CompoundEngineeringView.tsx
+++ b/plugins/fusion-plugin-compound-engineering/src/dashboard/CompoundEngineeringView.tsx
@@ -170,15 +170,18 @@ function SessionsPanel({
}
function nextPipelineStageId(stageId: string): string | undefined {
+ if (stageId === "strategy" || stageId === "ideate") return "brainstorm";
const stages = listPipelineStages();
const index = stages.findIndex((stage) => stage.stageId === stageId);
return index >= 0 ? stages[index + 1]?.stageId : undefined;
}
-type RailStatus = "Not started" | "Active" | "Needs input" | "Complete";
+type RailStatus = "Not started" | "Active" | "Needs input" | "Last run complete" | "History" | "Current";
function artifactsForStage(stageId: string, groups: CeArtifactGroup[]): CeArtifactEntry[] {
- const entries = groups.find((group) => group.stage === stageId)?.entries ?? [];
+ const entries = groups.find((group) => group.stage === stageId)?.entries
+ ?? (stageId === "compound" ? groups.find((group) => group.stage === "solution")?.entries : undefined)
+ ?? [];
if (stageId === "brainstorm") {
const unifiedEntries = groups.find((group) => group.stage === "plan")?.entries ?? [];
return [...entries, ...unifiedEntries.filter((entry) => (
@@ -200,16 +203,87 @@ function railStatus(stageId: string, sessions: CeSession[], artifact?: CeArtifac
const latest = sessions.find((session) => session.stage === stageId);
if (latest?.status === "awaiting_input") return "Needs input";
if (latest?.status === "active" || latest?.status === "launching") return "Active";
- if (latest?.status === "completed" || artifact?.kind === "artifact") return "Complete";
+ if (latest?.status === "completed") return "Last run complete";
+ if (artifact?.kind === "artifact") return stageId === "strategy" ? "Current" : "History";
return "Not started";
}
+const STAGE_PURPOSE: Record Move from direction to delivered work. Plan deeply, ship carefully, and save what you learn.
No compound-engineering artifacts found yet. Compound Engineering tracks the documents your
- pipeline produces — strategy, ideation, brainstorms, plans, solutions, and concepts — as you
+ workflow produces — strategy, ideation, brainstorms, plans, work logs, learnings, and concepts — as you
move through each stage.
- Begin with a stage and its artifact will appear here, grouped and traceable.
+ Start with Brainstorm when the problem is still fuzzy, Plan when requirements are settled, or Debug for broken behavior.
+ {stages.map((stage) => {
+ const artifacts = artifactsForStage(stage.stageId, groups);
+ const artifact = artifacts[0];
+ const stageSessions = sessions.filter((session) => session.stage === stage.stageId);
+ const latestSession = stageSessions[0];
+ const status = railStatus(stage.stageId, sessions, artifact);
+ const Icon = resolveIcon(stage.icon);
+ return (
+
+ );
+}
+
/**
* FNXC:CompoundEngineeringUI 2026-07-10-12:00:
- * Operators need the five-stage compounding loop as the overview's primary navigation. Stages report repeatable run and artifact collections instead of implying one permanent completion; Strategy remains the upstream singleton anchor, while Debug remains a separate investigation action because it is not pipeline progression.
+ * Operators need the optional foundation plus four-stage delivery loop as the overview's primary navigation. Stages report repeatable run and artifact collections instead of implying one permanent completion; Strategy remains the upstream singleton anchor, while Debug remains a separate investigation action because it is not pipeline progression.
*
* FNXC:CompoundEngineeringUI 2026-07-10-23:40:
- * Compound Engineering follows the upstream repeatable loop: brainstorm requirements, plan implementation, work, review, compound, then repeat with better context. Brainstorm and Plan therefore expose collection counts and latest activity over multiple unified-plan files rather than a single artifact slot.
+ * Compound Engineering follows the upstream repeatable loop: brainstorm requirements, plan implementation, work through built-in simplification and review, compound the learning, then repeat with better context. Brainstorm and Plan therefore expose collection counts and latest activity over multiple unified-plan files rather than a single artifact slot.
+ *
+ * FNXC:CompoundEngineeringUI 2026-07-11-00:02:
+ * Strategy and Ideate are optional foundation work, not mandatory delivery gates. Visually separate them from the Brainstorm -> Plan -> Work -> Compound loop, and state that Work owns simplification and review so users do not repeat quality stages manually.
*/
function PipelineOverview({
groups,
@@ -226,14 +300,16 @@ function PipelineOverview({
onLaunch: (stage: CeStageDefinition) => void;
onOpenSession: (session: CeSession) => void;
}) {
- const stages = listPipelineStages();
+ const stages = listStages().filter((stage) => stage.stageId !== "debug");
+ const foundationStages = stages.filter((stage) => stage.stageId === "strategy" || stage.stageId === "ideate");
+ const deliveryStages = stages.filter((stage) => stage.stageId !== "strategy" && stage.stageId !== "ideate");
const debug = getStage("debug");
return (
Pipeline
- Compound Engineering
+
- {stages.map((stage) => {
- const artifacts = artifactsForStage(stage.stageId, groups);
- const artifact = artifacts[0];
- const stageSessions = sessions.filter((session) => session.stage === stage.stageId);
- const latestSession = stageSessions[0];
- const status = railStatus(stage.stageId, sessions, artifact);
- const Icon = resolveIcon(stage.icon);
- return (
-
+ Foundation
Optional groundingDelivery loop
Repeat for each meaningful changeStart your compounding pipeline