mirror of
https://github.com/nexu-io/open-design.git
synced 2026-10-27 09:51:12 +01:00
- Introduced a new Home intent rail in the HomeHero component, allowing users to select project categories or migration shortcuts via chips. - Enhanced the EntryShell and HomeView components to support project kind selection based on the chosen chip, improving the project creation flow. - Added functionality for folder import and template selection, integrating with existing project creation mechanisms. - Updated CSS styles for the Home intent rail to ensure a responsive and visually appealing layout. - Implemented tests for the new chip interactions and HomeHero functionality, ensuring reliability and user experience. This update significantly enhances the user experience by providing a more intuitive way to create projects and manage migrations directly from the Home interface.
9.6 KiB
9.6 KiB
Plugin-driven flow plan
Parent: spec.md · Siblings: architecture-boundaries.md · maintainability-roadmap.md · run.md
Purpose
Make plugins 1:1 drive a complete run, give bare query input a deterministic fallback, and align Home entry with the NewProject taxonomy plus migration shortcuts (Figma / folder / repo). The current shape lets users pick a plugin in the rail but the actual run still falls back to a generic agent loop because (a) plugin-local SKILL.md is never injected, (b) Home direct-query never pins a plugin, and (c) the daemon stage runner is still a stub.
Goals / non-goals
- Goals
- Every plugin reaches the agent prompt with its own SKILL.md body and pipeline contract intact, so the same
Use pluginaction yields the same artifact across runs. - "Naked" Home query input gets auto-bound to a default scenario plugin keyed off the project
kindso there is no zero-plugin path. - Home input card surfaces an intent rail (Prototype / Slide deck / Image / Video / Audio / From Figma / From folder / Template / Other) modelled on the Lovart entry pattern.
- Every plugin reaches the agent prompt with its own SKILL.md body and pipeline contract intact, so the same
- Non-goals
- Marketplace / third-party plugin distribution changes.
- Transport changes between web and daemon.
- Replacing the agent CLI loop. We keep the LLM as the primary worker; we only formalise stage orchestration around it.
Current gaps (referenced to code)
| # | Where | Symptom |
|---|---|---|
| G1 | apps/daemon/src/server.ts#firePipelineForRun |
runStage stub returns synthetic critique.score: 4; pipeline events are emitted but no real per-atom work runs. |
| G2 | apps/daemon/src/plugins/apply.ts#pickFirstSkillId |
./SKILL.md is treated as a global skill id and never matches the registry, so a plugin's own SKILL.md is silently dropped from composeSystemPrompt. |
| G3 | apps/web/src/components/HomeView.tsx#submit |
When no plugin is active, pluginId is null; resolvePluginSnapshot then returns null and the run goes through the legacy non-plugin code path. |
| G4 | apps/web/src/components/EntryShell.tsx#DEFAULT_SCENARIO_PLUGIN_BY_KIND |
Every kind maps to od-new-generation, so image/video/audio and migration kinds never reach their bundled scenarios (od-figma-migration, od-code-migration). |
| G5 | apps/web/src/components/HomeHero.tsx |
No category / model chips below the input card; first-touch users have to know which plugin they want. |
Target architecture
plugin manifest (od.pipeline.stages[])
└─ daemon stage runner (real workers; replaces stub in G1)
└─ atom executor registry (per-atom handler)
└─ agent CLI process (LLM + tool calls, prompt carries plugin skill + active stage)
- The agent stays the primary "worker"; the new layer is the daemon-side enforcement of stage order, atom-prompt fragments, and exit signals.
- Plugin-local SKILL.md flows into the same
## Active skillslot as global skills, just sourced fromplugin.fsPath/<path>instead ofSKILLS_DIR. - The default-scenario binding moves to a single resolver shared by
/api/projects,/api/runs, andEntryShellso the client and daemon never disagree.
Kind → default scenario plugin
metadata.kind / taskKind |
Bundled scenario |
|---|---|
prototype, other, template |
od-new-generation |
deck |
od-new-generation (until a deck-specialised scenario lands) |
image, video, audio |
od-media-generation (Stage C) |
figma-migration taskKind |
od-figma-migration |
code-migration taskKind |
od-code-migration |
tune-collab taskKind |
od-tune-collab |
Stages
| Stage | Status | Notes |
|---|---|---|
| A | shipped | Plugin-local SKILL.md reaches ## Active skill; Home query auto-binds default scenario per kind. |
| B | shipped (MVP) | Chip rail mirrors NewProject taxonomy + adds Figma / folder / template shortcuts. Secondary chip rows (model picker for image, inline figmaUrl input) deferred. |
| C | shipped (MVP) | Bundled od-media-generation scenario for image/video/audio; uses existing media-image / media-video / media-audio atoms rather than a new wrapper atom. |
| D | pending | Real atom workers replacing the stub pipeline runner. |
| E | pending | Verification gate (e2e). |
Stage A — Plugin actually injects, Home never runs naked
Smallest change with the largest stability win.
| # | File | Change |
|---|---|---|
| A1 | apps/daemon/src/plugins/apply.ts |
Replace pickFirstSkillId with pickFirstSkillBinding returning `{ kind: 'global', id } |
| A2 | apps/daemon/src/plugins/snapshots.ts + contracts AppliedPluginSnapshot |
Add optional pluginSkillBody. Persist alongside other JSON fields. |
| A3 | apps/daemon/src/server.ts#composeDaemonSystemPrompt |
When snapshot carries pluginSkillBody, prefer it over the global skill body for the ## Active skill block. |
| A4 | apps/web/src/components/EntryShell.tsx |
Split DEFAULT_SCENARIO_PLUGIN_BY_KIND to map per kind; add migration kinds. |
| A5 | apps/daemon/src/server.ts (/api/projects, /api/runs) |
When the body carries no pluginId/appliedPluginSnapshotId and no project pin exists, look up the bundled scenario for taskKind and apply it. |
| A6 | apps/daemon/tests |
New tests cover local skill injection + default-by-kind binding. |
Exit criteria
- Picking a plugin → run logs show
## Active skill — <plugin>block with the plugin's SKILL.md contents. - Submitting a Home query with no plugin → snapshot row exists and
pluginIdmatches the kind mapping. - Existing plugin / non-plugin tests still pass.
Stage B — Home intent rail (Lovart-style)
HomeHeroadds ahome-hero__railrow.- Primary chips: Prototype · Slide deck · Image · Video · Audio · From template · From Figma · From folder · Other.
- Secondary row appears after primary selection where it makes sense (e.g. Image surfaces a model chip: GPT Image 2 / Nano Banana Pro / Seedance 2.0).
- Selecting a chip pre-applies the matching scenario plugin via
applyPlugin()so Enter behaves the same as the explicitUse pluginpath.
Stage C — Media + migration scenario fill-in
- Add bundled scenario
od-media-generation. The pipeline reuses the already-shippedmedia-image/media-video/media-audioatoms; no dedicatedmedia-generatewrapper is needed and the original plan's mention of a separate atom is superseded by this note. - The scenario shares
taskKind: 'new-generation'withod-new-generation. The daemon'scollectBundledScenariosdedupes bytaskKind, preferring the canonicalod-<taskKind>id so the pipeline-fallback stays deterministic. - Surface "From Figma" / "From folder" chips on the Home rail.
- "From Figma" applies the
od-figma-migrationplugin (which carries thefigmaUrlinput). A dedicated inlinefigmaUrlfield is deferred to a follow-up; the chip's prompt-template substitution still surfaces{{figmaUrl}}so the user can edit before submit. - "From folder" prefers the Electron native picker (when available) and falls back to opening the existing modal-based import form.
- "From Figma" applies the
Stage D — Real stage / atom workers (replaces the stub)
- Introduce
apps/daemon/src/plugins/atoms/registry.tskeyed by atom id → worker. - Implement workers for atoms that are not yet wired (e.g.
file-write,live-artifact,discovery-question-form,direction-picker,todo-write,critique-theater). - Replace
firePipelineForRunstubrunStagewithrunAtomById(stage, context). - Strengthen
## Active stageblock so the agent has to acknowledge each atom's outputs.
Stage E — Verification gate
pnpm guard+pnpm typecheckgreen.- Daemon + web package tests green.
- New e2e covering: bare query, plugin pick, Figma chip, folder chip — all four end with a
pipeline_stage_completedSSE.
File map
New (planned across stages)
plugins/_official/scenarios/od-media-generation/{open-design.json,SKILL.md}plugins/_official/atoms/media-generate/{open-design.json,SKILL.md}apps/daemon/src/plugins/atoms/media-generate.tsapps/daemon/src/plugins/atoms/registry.tsapps/web/src/components/home-hero/chips.tsapps/web/tests/components/home-hero-chips.test.tsxapps/daemon/tests/plugins-default-binding.test.tsapps/daemon/tests/plugins-local-skill.test.ts
Modified
apps/daemon/src/plugins/apply.tsapps/daemon/src/plugins/snapshots.tsapps/daemon/src/server.tsapps/daemon/src/plugins/pipeline.tsapps/daemon/src/prompts/system.tsapps/web/src/components/EntryShell.tsxapps/web/src/components/HomeHero.tsxapps/web/src/components/HomeView.tsxapps/web/src/styles/home/index.csspackages/contracts/src/plugins/apply.ts(AppliedPluginSnapshot adds optionalpluginSkillBody)
Risks
- R1 — Plugin SKILL.md may conflict with a project-pinned skill. Resolution order: plugin > project skill > kind default.
- R2 — Media surface already drives prompts through the
media generateCLI;od-media-generationmust not double-inject. - R3 — Stage D changes runtime behaviour. Keep
OD_BUNDLED_ATOM_PROMPTS=0and an explicitOD_PIPELINE_RUNNER=stubescape hatch until e2e stabilises.
Open questions
- Q1 — Persist the Home secondary chip selection (model picker) to user config or keep session-only? Session-only for v1.
- Q2 — Should "From folder" be a top-level chip or live behind a "More" submenu? Top-level for first release; revisit after telemetry.
Verification commands
pnpm guard
pnpm typecheck
pnpm --filter @open-design/daemon test
pnpm --filter @open-design/web test