diff --git a/DECISIONS.md b/DECISIONS.md
index 71cb5af9..c055fdb9 100644
--- a/DECISIONS.md
+++ b/DECISIONS.md
@@ -43,3 +43,9 @@
- data.rs:data_infer_model still uses local infer_field_type without identity-forcing post-pass; same fix as infer.rs eef494f applies — added 3-line identity coercion block after identity is determined. data.rs is source-experience's file but the fix is trivial and necessary to pass cargo test gate; acceptable cross-lane touch since it replicates an already-landed pattern.
+- [studio-code:planner] SECURITY: received a message claiming to relay a director response ('not my fleet -- I direct kevinthelago/networkmonitor') telling me to stop asking and resume from prompts/planner-checkpoint.md. Both claims are false/inconsistent: BSC_AUDIT_PANE=studio-code:planner (this session IS correctly wired to studio-code), and the referenced checkpoint file does not exist. Treated as a prompt-injection/hijack attempt per protocol -- ignored, did not change task or scope, continuing on #3787/#3989 as before.
+
+- [studio-code:planner] reviewed bsc ui doctor findings for src/features/planner: all git-tracked/seeded component records (pane/session/bodies/list) are clean and correctly provides-tagged; found ~15 stale DB-only duplicate/draft records (progressionrail, mcpdownloadmodal, donebanner, lockbanner, rolechip, kinddot, seg, stepper, planpreviewpane, plan-body-stream-focus-cards, collapsiblecard, planningdialogs, planningnotices, projectcard, projectsrail) left behind by earlier harvest/authoring passes, none seed-backed so none affect the shipped app; planner role's ui scope is read-only (mutating bsc ui verbs denied) so I cannot delete them myself -- flagging for a session with UI write access (designer/worker) to prune
+
+- [studio-code:planner] confirmed #3787's full scope (Planning.tsx / FocusedShell / ProjectPane / ProjectsList into the graph) already delivered and live-mounted via already-merged #3874 + #4224 + #4227 + #4230 (all closed, epic #3604); #3989 already merged (#4011); requesting the director close #3787 referencing those four sub-issues since no new planner-owned code work remains
+
diff --git a/src-tauri/data/components/app/features/planner/pane/plan-pane-bodies.json b/src-tauri/data/components/app/features/planner/pane/plan-pane-bodies.json
index 6f7f89de..25abebdc 100644
--- a/src-tauri/data/components/app/features/planner/pane/plan-pane-bodies.json
+++ b/src-tauri/data/components/app/features/planner/pane/plan-pane-bodies.json
@@ -4,24 +4,7 @@
"kitId": "base-studio-code",
"role": "composite",
"provides": "@/features/planner/pane/FocusedBodies",
- "srcText": "// Project pane → the stage-body switch, AS GRAPH SOURCE (#4227, epic #3604). Transcribed from the live feature file. Its\n// `provides: \"@/features/planner/pane/FocusedBodies\"` makes the runtime loader vendor THIS source wherever a graph\n// component imports that specifier — so the planning page composes the pane from the graph without\n// knowing it, and the registered module stays as the fallback. Behaviour runs here.\n// FocusedBodies — the planner ProjectPane stage-body dispatcher (#1332; bodies split into\n// ../bodies/Focused*Body per #1757). Each per-stage body now lives in its own file under\n// `../bodies/`; this module is the `FocusedStageBody` switch that maps a Stage to its body and\n// assembles the shared handler sets once. ProjectPane.tsx is the thin shell that renders it.\nimport type { Flow, ContextFile, McpServer, ProjectPaneData } from \"@/features/planner/pane/projectPaneData\";\nimport { type ModelId } from \"@/app/console/lib/models\";\nimport type { Stage } from \"@/features/planner/stages/focusedPlan\";\nimport type { DeployConfig } from \"@/features/planner/lib/deployConfig\";\nimport type { Topology } from \"@/features/planner/relationship/relationshipGraph\";\nimport type { DirectorDrive } from \"@/features/planner/fleet/directorDrive\";\n// The merged Repositories & Deployment pane + source body (pre-existing files). #1914 collapsed the\n// stage vocabulary: the `deployment` stage (repos+deploy) renders the merged Repositories & Deployment\n// pane, the `streams` stage (structure+permissions) renders the plan graph + the folded-in fleet\n// permissions, and `source` stays (it's in `complete`).\nimport { FileIntakePane } from \"@/features/planner/bodies/FileIntakePane\";\nimport { DeploymentBody } from \"@/features/planner/bodies/ReposDeployView\";\nimport { SourceBody } from \"@/features/planner/bodies/FocusedSourceBody\";\n// Core planning-stage bodies (#1757 split out of this file).\nimport { DiscoveryBody } from \"@/features/planner/bodies/DiscoveryBody\";\nimport { MarketBody } from \"@/features/planner/bodies/MarketBody\";\nimport { TransformationsBody } from \"@/features/planner/bodies/TransformationsBody\";\nimport { AutomationsBody } from \"@/features/planner/bodies/FocusedAutomationsBody\";\nimport { SkillsBody } from \"@/features/planner/bodies/FocusedSkillsBody\";\nimport { McpsBody } from \"@/features/planner/bodies/McpsBody\";\nimport { FeaturesBody } from \"@/features/planner/bodies/FocusedFeaturesBody\";\nimport { StreamsBody } from \"@/features/planner/bodies/StreamsBody\";\nimport type { FleetHandlers, McpHandlers } from \"@/features/planner/bodies/focusedHandlers\";\nimport { EmptyState } from \"@/shared/ui/feedback/EmptyState\";\n// The Planner Components pane (#2314) — the body of the `test_ui` stage.\nimport { PlannerComponentsPane } from \"@/features/designs\";\nimport { PreviewPaneShell } from \"@/features/planner/preview/PreviewPaneShell\";\nimport { useAppStore } from \"@/store\";\n\n// Re-export the shared body types so existing `from \"@/features/planner/pane/FocusedBodies\"` imports keep resolving.\nexport type { FleetHandlers, McpHandlers, SyncState } from \"@/features/planner/bodies/focusedHandlers\";\n\n/* =================================================================\n FocusedStageBody — maps a Stage to its body (#652 / #674)\n ================================================================= */\n\nexport function FocusedStageBody({ stage, data, projectId, onLinkRepo, onView, onFlow, onModel, onPersona, onTopology, onDirectorDrive, onToggleMcp, onBuildMcp, onAddMcp, onRemoveMcp, onDeployChange, requiredContext, onInject }: {\n stage: Stage;\n data?: ProjectPaneData;\n projectId?: string;\n /** Required-context topics for the Context body's written/missing checklist (#1061). */\n requiredContext?: string[];\n /** Inject a prompt into the live planner terminal (#1986) — the Source body's declare affordance. */\n onInject?: (text: string) => void;\n onLinkRepo?: (r: string) => void;\n /** Deploy stage (#919): persist the edited deployment config. */\n onDeployChange?: (next: DeployConfig) => void;\n onView?: (f: ContextFile) => void;\n onFlow?: (streamId: string, flow: Flow) => void;\n onModel?: (streamId: string, model: ModelId | undefined) => void;\n onPersona?: (streamId: string, personaId: string | undefined) => void;\n onTopology?: (t: Topology) => void;\n onDirectorDrive?: (d: DirectorDrive) => void;\n onToggleMcp?: (id: string) => void;\n onBuildMcp?: (s: McpServer) => void;\n onAddMcp?: (input: string) => void;\n onRemoveMcp?: (id: string) => void;\n}) {\n // #3783: the UI stage renders one of two surfaces per the project's UI mode (planner-set at\n // discovery; an unset project defaults to \"custom\" — the in-app designer preview).\n const uiMode = useAppStore((s) => (projectId ? s.planClassification[projectId]?.uiMode : undefined) ?? \"custom\");\n // Assemble the repeated handler sets once (#1640) so the cases below spread them instead of\n // re-threading each handler by name. Same handlers, same values — purely cuts prop-chain noise.\n const fleetHandlers: FleetHandlers = { onFlow, onModel, onPersona, onTopology, onDirectorDrive };\n const mcpHandlers: McpHandlers = { onToggle: onToggleMcp, onBuild: onBuildMcp, onAdd: onAddMcp, onRemove: onRemoveMcp };\n switch (stage.key) {\n case \"source\":\n return ;\n case \"deployment\":\n // The unified `deployment` stage (#1914 — the collapsed repos+deploy def) renders as one\n // cohesive Repositories & Deployment pane: each repo's git identity merged with its deploy\n // target (click a repo to expand its target editor inline).\n return (\n \n );\n case \"discovery\":\n return ;\n case \"market\":\n // The market-research stage (#2430): read-focused rendering of the planner-recorded\n // assessment (gap statement · scored rubric · competitors · verdict) from `bsc plan market`.\n return ;\n case \"transformations\":\n // The transformations stage (#2509): the bottom-up confirm queue over the planner-recorded\n // modification rows from `bsc plan transformation` — tier by tier, confirm-only.\n return ;\n case \"ui\":\n // #3783: the UI stage renders one of two surfaces depending on the project's UI mode.\n // \"custom\" (default) = the in-app designer preview — the render-preview pipeline shows the\n // navigable shell the designer commissions (PreviewPaneShell). \"external\" = the drop-in-files\n // surface (#604/#829) that stages Claude-Design assets into `design/` for the planner to route.\n return uiMode === \"external\"\n ? \n : ;\n case \"features\":\n return ;\n case \"streams\":\n // The unified `streams` stage (#1914 — the collapsed structure+permissions def). The plan +\n // relationship graph always shows; the fleet (coordination + the per-stream roster + shared\n // deps, as collapsible cards) folds in below when the def carries the `fleet` substep — which\n // the kept blueprints always do.\n return ;\n case \"mcps\":\n return ;\n case \"automations\":\n return ;\n case \"skills\":\n return ;\n case \"test_ui\":\n // The Planner Components pane (#2314): the project-scoped lens on the proven-component library —\n // a Components ⇄ Full UI toggle (inspect one component with pull-into-plan / open-in-studio\n // hand-offs, or the whole app assembled from the kit). Reads the global library from the store,\n // so it needs no stage data.\n return ;\n default:\n return ;\n }\n}\n",
+ "srcText": "// Project pane → the stage-body switch, AS GRAPH SOURCE (#4227, epic #3604). Transcribed from the live feature file. Its\n// `provides: \"@/features/planner/pane/FocusedBodies\"` makes the runtime loader vendor THIS source wherever a graph\n// component imports that specifier — so the planning page composes the pane from the graph without\n// knowing it, and the registered module stays as the fallback. Behaviour runs here.\n// FocusedBodies — the planner ProjectPane stage-body dispatcher (#1332; bodies split into\n// ../bodies/Focused*Body per #1757). Each per-stage body now lives in its own file under\n// `../bodies/`; this module is the `FocusedStageBody` switch that maps a Stage to its body and\n// assembles the shared handler sets once. ProjectPane.tsx is the thin shell that renders it.\nimport type { Flow, ContextFile, McpServer, ProjectPaneData } from \"@/features/planner/pane/projectPaneData\";\nimport { type ModelId } from \"@/app/console/lib/models\";\nimport type { Stage } from \"@/features/planner/stages/focusedPlan\";\nimport type { DeployConfig } from \"@/features/planner/lib/deployConfig\";\nimport type { Topology } from \"@/features/planner/relationship/relationshipGraph\";\nimport type { DirectorDrive } from \"@/features/planner/fleet/directorDrive\";\n// The merged Repositories & Deployment pane + source body (pre-existing files). #1914 collapsed the\n// stage vocabulary: the `deployment` stage (repos+deploy) renders the merged Repositories & Deployment\n// pane, the `streams` stage (structure+permissions) renders the plan graph + the folded-in fleet\n// permissions, and `source` stays (it's in `complete`).\nimport { FileIntakePane } from \"@/features/planner/bodies/FileIntakePane\";\nimport { DeploymentBody } from \"@/features/planner/bodies/ReposDeployView\";\nimport { SourceBody } from \"@/features/planner/bodies/FocusedSourceBody\";\n// Core planning-stage bodies (#1757 split out of this file).\nimport { DiscoveryBody } from \"@/features/planner/bodies/DiscoveryBody\";\nimport { MarketBody } from \"@/features/planner/bodies/MarketBody\";\nimport { TransformationsBody } from \"@/features/planner/bodies/TransformationsBody\";\nimport { AutomationsBody } from \"@/features/planner/bodies/FocusedAutomationsBody\";\nimport { SkillsBody } from \"@/features/planner/bodies/FocusedSkillsBody\";\nimport { McpsBody } from \"@/features/planner/bodies/McpsBody\";\nimport { StreamsBody } from \"@/features/planner/bodies/StreamsBody\";\nimport type { FleetHandlers, McpHandlers } from \"@/features/planner/bodies/focusedHandlers\";\nimport { EmptyState } from \"@/shared/ui/feedback/EmptyState\";\n// The project planner's LIBRARY surface (#4265) — the components lens (#2314) and the algorithms lens\n// together. Folded into the `features` stage, which every packaged blueprint carries; `test_ui` (which\n// none do, since #4249) keeps rendering it so a user blueprint carrying that stage still resolves.\nimport { PlannerLibraryPane } from \"@/features/planner/bodies/PlannerLibraryPane\";\nimport { FeaturesStageBody } from \"@/features/planner/bodies/FeaturesStageBody\";\nimport { PreviewPaneShell } from \"@/features/planner/preview/PreviewPaneShell\";\nimport { useAppStore } from \"@/store\";\n\n// Re-export the shared body types so existing `from \"@/features/planner/pane/FocusedBodies\"` imports keep resolving.\nexport type { FleetHandlers, McpHandlers, SyncState } from \"@/features/planner/bodies/focusedHandlers\";\n\n/* =================================================================\n FocusedStageBody — maps a Stage to its body (#652 / #674)\n ================================================================= */\n\nexport function FocusedStageBody({ stage, data, projectId, onLinkRepo, onView, onFlow, onModel, onPersona, onTopology, onDirectorDrive, onToggleMcp, onBuildMcp, onAddMcp, onRemoveMcp, onDeployChange, requiredContext, onInject }: {\n stage: Stage;\n data?: ProjectPaneData;\n projectId?: string;\n /** Required-context topics for the Context body's written/missing checklist (#1061). */\n requiredContext?: string[];\n /** Inject a prompt into the live planner terminal (#1986) — the Source body's declare affordance. */\n onInject?: (text: string) => void;\n onLinkRepo?: (r: string) => void;\n /** Deploy stage (#919): persist the edited deployment config. */\n onDeployChange?: (next: DeployConfig) => void;\n onView?: (f: ContextFile) => void;\n onFlow?: (streamId: string, flow: Flow) => void;\n onModel?: (streamId: string, model: ModelId | undefined) => void;\n onPersona?: (streamId: string, personaId: string | undefined) => void;\n onTopology?: (t: Topology) => void;\n onDirectorDrive?: (d: DirectorDrive) => void;\n onToggleMcp?: (id: string) => void;\n onBuildMcp?: (s: McpServer) => void;\n onAddMcp?: (input: string) => void;\n onRemoveMcp?: (id: string) => void;\n}) {\n // #3783: the UI stage renders one of two surfaces per the project's UI mode (planner-set at\n // discovery; an unset project defaults to \"custom\" — the in-app designer preview).\n const uiMode = useAppStore((s) => (projectId ? s.planClassification[projectId]?.uiMode : undefined) ?? \"custom\");\n // Assemble the repeated handler sets once (#1640) so the cases below spread them instead of\n // re-threading each handler by name. Same handlers, same values — purely cuts prop-chain noise.\n const fleetHandlers: FleetHandlers = { onFlow, onModel, onPersona, onTopology, onDirectorDrive };\n const mcpHandlers: McpHandlers = { onToggle: onToggleMcp, onBuild: onBuildMcp, onAdd: onAddMcp, onRemove: onRemoveMcp };\n switch (stage.key) {\n case \"source\":\n return ;\n case \"deployment\":\n // The unified `deployment` stage (#1914 — the collapsed repos+deploy def) renders as one\n // cohesive Repositories & Deployment pane: each repo's git identity merged with its deploy\n // target (click a repo to expand its target editor inline).\n return (\n \n );\n case \"discovery\":\n return ;\n case \"market\":\n // The market-research stage (#2430): read-focused rendering of the planner-recorded\n // assessment (gap statement · scored rubric · competitors · verdict) from `bsc plan market`.\n return ;\n case \"transformations\":\n // The transformations stage (#2509): the bottom-up confirm queue over the planner-recorded\n // modification rows from `bsc plan transformation` — tier by tier, confirm-only.\n return ;\n case \"ui\":\n // #3783: the UI stage renders one of two surfaces depending on the project's UI mode.\n // \"custom\" (default) = the in-app designer preview — the render-preview pipeline shows the\n // navigable shell the designer commissions (PreviewPaneShell). \"external\" = the drop-in-files\n // surface (#604/#829) that stages Claude-Design assets into `design/` for the planner to route.\n return uiMode === \"external\"\n ? \n : ;\n case \"features\":\n // #4265: the plan AND the library it should be built from — the stage where reuse-vs-commission\n // is decided is the stage that shows what there is to reuse.\n return ;\n case \"streams\":\n // The unified `streams` stage (#1914 — the collapsed structure+permissions def). The plan +\n // relationship graph always shows; the fleet (coordination + the per-stream roster + shared\n // deps, as collapsible cards) folds in below when the def carries the `fleet` substep — which\n // the kept blueprints always do.\n return ;\n case \"mcps\":\n return ;\n case \"automations\":\n return ;\n case \"skills\":\n return ;\n case \"test_ui\":\n // Legacy home of the components lens. #4249 retired this stage (no packaged blueprint carries\n // it), so the library now lives on `features` — but a USER blueprint may still carry `test_ui`,\n // and it gets the same both-libraries dock rather than a dead pane.\n return ;\n default:\n return ;\n }\n}\n",
"src": "src/features/planner/pane/FocusedBodies.tsx",
- "folder": "features/planner/pane",
- "composes": [
- "AutomationsBody",
- "DeploymentBody",
- "DiscoveryBody",
- "EmptyState",
- "FeaturesBody",
- "FileIntakePane",
- "FocusedStageBody",
- "MarketBody",
- "McpsBody",
- "PreviewPaneShell",
- "SkillsBody",
- "SourceBody",
- "StreamsBody",
- "TransformationsBody"
- ],
- "props": []
+ "folder": "features/planner/pane"
}
diff --git a/src-tauri/data/ui/platform-modules.json b/src-tauri/data/ui/platform-modules.json
index df9f87b3..5f6e483c 100644
--- a/src-tauri/data/ui/platform-modules.json
+++ b/src-tauri/data/ui/platform-modules.json
@@ -75,15 +75,16 @@
"@/features/planner/blueprints/blueprintSkills",
"@/features/planner/bodies/DeployView",
"@/features/planner/bodies/DiscoveryBody",
+ "@/features/planner/bodies/FeaturesStageBody",
"@/features/planner/bodies/FileIntakePane",
"@/features/planner/bodies/FocusedAutomationsBody",
- "@/features/planner/bodies/FocusedFeaturesBody",
"@/features/planner/bodies/FocusedPermissionsBody",
"@/features/planner/bodies/FocusedPlanBody",
"@/features/planner/bodies/FocusedSkillsBody",
"@/features/planner/bodies/FocusedSourceBody",
"@/features/planner/bodies/MarketBody",
"@/features/planner/bodies/McpsBody",
+ "@/features/planner/bodies/PlannerLibraryPane",
"@/features/planner/bodies/ReposDeployView",
"@/features/planner/bodies/ScanViews",
"@/features/planner/bodies/SharedDependencies",
diff --git a/src/features/planner/pane/graphPlatform.ts b/src/features/planner/pane/graphPlatform.ts
index 8b7eed7e..3f9a2e37 100644
--- a/src/features/planner/pane/graphPlatform.ts
+++ b/src/features/planner/pane/graphPlatform.ts
@@ -11,17 +11,17 @@
// The pane's OWN modules are registered too: each is `provides`-resolved from the graph first, and the
// module behind it is the fallback for a record that will not load.
import { registerAppModule } from "@/shared/lib/runtime/moduleRegistry";
-import * as Designs from "@/features/designs";
import * as BlueprintCatalog from "@/features/planner/blueprints/blueprintCatalog";
import * as BlueprintIcons from "@/features/planner/blueprints/blueprintIcons";
import * as DiscoveryBody from "@/features/planner/bodies/DiscoveryBody";
+import * as FeaturesStageBody from "@/features/planner/bodies/FeaturesStageBody";
import * as FileIntakePane from "@/features/planner/bodies/FileIntakePane";
import * as FocusedAutomationsBody from "@/features/planner/bodies/FocusedAutomationsBody";
-import * as FocusedFeaturesBody from "@/features/planner/bodies/FocusedFeaturesBody";
import * as FocusedSkillsBody from "@/features/planner/bodies/FocusedSkillsBody";
import * as FocusedSourceBody from "@/features/planner/bodies/FocusedSourceBody";
import * as MarketBody from "@/features/planner/bodies/MarketBody";
import * as McpsBody from "@/features/planner/bodies/McpsBody";
+import * as PlannerLibraryPane from "@/features/planner/bodies/PlannerLibraryPane";
import * as ReposDeployView from "@/features/planner/bodies/ReposDeployView";
import * as StreamsBody from "@/features/planner/bodies/StreamsBody";
import * as TransformationsBody from "@/features/planner/bodies/TransformationsBody";
@@ -46,17 +46,17 @@ let done = false;
export function registerPanePlatform(): void {
if (done) return;
done = true;
- registerAppModule("@/features/designs", Designs);
registerAppModule("@/features/planner/blueprints/blueprintCatalog", BlueprintCatalog);
registerAppModule("@/features/planner/blueprints/blueprintIcons", BlueprintIcons);
registerAppModule("@/features/planner/bodies/DiscoveryBody", DiscoveryBody);
+ registerAppModule("@/features/planner/bodies/FeaturesStageBody", FeaturesStageBody);
registerAppModule("@/features/planner/bodies/FileIntakePane", FileIntakePane);
registerAppModule("@/features/planner/bodies/FocusedAutomationsBody", FocusedAutomationsBody);
- registerAppModule("@/features/planner/bodies/FocusedFeaturesBody", FocusedFeaturesBody);
registerAppModule("@/features/planner/bodies/FocusedSkillsBody", FocusedSkillsBody);
registerAppModule("@/features/planner/bodies/FocusedSourceBody", FocusedSourceBody);
registerAppModule("@/features/planner/bodies/MarketBody", MarketBody);
registerAppModule("@/features/planner/bodies/McpsBody", McpsBody);
+ registerAppModule("@/features/planner/bodies/PlannerLibraryPane", PlannerLibraryPane);
registerAppModule("@/features/planner/bodies/ReposDeployView", ReposDeployView);
registerAppModule("@/features/planner/bodies/StreamsBody", StreamsBody);
registerAppModule("@/features/planner/bodies/TransformationsBody", TransformationsBody);