Three phase plans for Wave 2 of the Nexus layout overhaul. Each plan
is self-contained with ownership boundaries, scope fence, file inventory,
implementation notes, acceptance criteria, and commit scheme — designed
for parallel subagent dispatch per MIGRATION-PLAN.md section 11.
Phase 9 (Assistant mode):
- Full-bleed chat at /assistant, no inner conversation list
- History slide-over (left) + Memory slide-over (right)
- Conversational home-state greeting replaces Dashboard
- ActionStrip with Promote/Attach/Memory/History
- New components: AssistantInputBar, ActionStrip, HistorySheet,
MemorySheet, AssistantHomeGreeting + useAssistantHomeStatus hook
- Owns: PersonalAssistant.tsx + components/assistant/**
Phase 10 (Studio mode):
- 8-card workshop grid replaces 7-tab ContentStudio
- ConvertPage folds in as 8th workshop (legacy /convert route kept)
- StudioPromptBar freeform input with keyword-based classifier
- Two-column workshop detail view (params left, preview right)
- Owns: ContentStudio.tsx + pages/StudioWorkshopDetail.tsx
+ components/studio/**
Phase 11 (Projects + Builder mode):
- ProjectCard with 72px Inter Black volt hero percentage
- 7-tab BuilderTabStrip (Overview/Issues/Agents/Gates/Costs/Activity/Org)
- Approvals -> Gates display-only rename
- OverviewTab with milestone checklist, origin chat card, activity
- Thin per-project wrappers reuse existing list components scoped
by projectId (escalate if not supported)
- useGateIndicator hook for the future Assistant dot notification
- Owns: Projects.tsx + ProjectDetail.tsx + components/projects/**
Ownership boundaries prevent parallel-dispatch file conflicts:
- App.tsx routing changes are controller-owned (post-Wave wiring)
- Each phase declares its file ownership and must not cross into others
- Existing list components reused as-is; escalate if not scope-compatible
- IconRail dot wiring for phase 11's gate indicator deferred to
post-Wave controller step
Dispatch pattern: three general-purpose subagents dispatched in parallel,
each handed the full plan text + the spec + ownership rules. Each
subagent implements its phase end-to-end with atomic commits per
logical unit. Controller reviews outputs after all three complete.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
357 lines
20 KiB
Markdown
357 lines
20 KiB
Markdown
# Nexus Phase 10 — Studio Mode Implementation Plan
|
||
|
||
> **For agentic workers:** REQUIRED SUB-SKILL: Use `superpowers:test-driven-development`. Commit atomically per logical unit.
|
||
|
||
**Goal:** Refactor ContentStudio from a 7-tab page into an **8-card workshop grid** that folds in the existing ConvertPage as the 8th workshop. Add a freeform prompt input at the bottom of the Studio home that routes to a workshop based on intent. Build a workshop detail layout (two columns: params left, preview right) for when a user selects a workshop.
|
||
|
||
**Source of truth:** `docs/specs/2026-04-11-nexus-layout-overhaul.md` **§6** (Mode 2 — Studio). Read §6.1–§6.5 before starting. DESIGN.md governs visuals.
|
||
|
||
**Branch:** `nexus/design-system-migration`. Commit directly.
|
||
|
||
---
|
||
|
||
## Ownership boundaries
|
||
|
||
**You may create or modify ONLY:**
|
||
|
||
| Path | Action |
|
||
|---|---|
|
||
| `ui/src/pages/ContentStudio.tsx` | Modify (major rewrite: from tabs to workshop grid) |
|
||
| `ui/src/pages/StudioWorkshopDetail.tsx` | Create (new routed view for a single workshop) |
|
||
| `ui/src/components/studio/**` | Create (new subdir for Phase 10 components) |
|
||
| `ui/src/pages/ConvertPage.tsx` | MAY read and reference, but DO NOT DELETE. Phase 16 handles dead-file deletion. Leave the file in place; it just stops being linked from the rail. |
|
||
|
||
**You MUST NOT touch:**
|
||
|
||
- `ui/src/App.tsx` — routing is controller-owned. If you need new sub-routes for workshops (e.g. `/content-studio/:workshopSlug`), report them in your final report; the controller wires them up after Wave 2.
|
||
- Existing content generator backends under `server/` or `packages/`. Phase 10 is purely a frontend IA reshape.
|
||
- Any file outside `ui/src/` except as noted.
|
||
- `ui/src/components/Layout.tsx`, `ui/src/components/frame/*` — Phase 8 territory, do not touch.
|
||
- Other phases' owned paths (`ui/src/pages/PersonalAssistant.tsx`, `ui/src/pages/Projects.tsx`, `ui/src/pages/ProjectDetail.tsx`, `ui/src/components/assistant/**`, `ui/src/components/projects/**`).
|
||
|
||
**Existing code you may reuse (read-only):**
|
||
|
||
- The 7 existing content-generator subcomponents that the current `ContentStudio.tsx` references for its tabs. Read `ContentStudio.tsx` first to find them. You'll reuse them as the body content of each workshop's detail view, unchanged.
|
||
- `ui/src/pages/ConvertPage.tsx` — you'll embed its main content (not the whole page shell) as the body of the Convert workshop detail view. Do not delete the file; the route path `/convert` still works in Phase 10 for backwards compatibility.
|
||
- `ui/src/components/frame/*` — Phase 8 patterns (test shape, semantic tokens, focus-visible styles, cn helper, slide-overs if you need any).
|
||
|
||
---
|
||
|
||
## Scope (strictly)
|
||
|
||
**In Phase 10:**
|
||
|
||
1. **Studio home rewrite** — replace the 7-tab layout in `ContentStudio.tsx` with a grid of **8 workshop cards**:
|
||
- `diagrams`, `icons`, `themes`, `wallpapers`, `documents`, `brand-kits`, `social`, `convert`
|
||
- 3-column grid on `>= 1024px`, 2-column on `>= 640px`, 1-column below
|
||
- Card: 1px charcoal border, 8px radius, transparent fill on idle, `bg-card` near-black on hover, 24px padding
|
||
- Title in Inter 700 uppercase 24px, subtitle in silver 14px, Lucide icon in volt top-right
|
||
2. **Convert workshop fold-in** — the 8th card routes to the existing ConvertPage body rendered inside a Studio workshop detail shell. The `/convert` top-level route must continue to work (because users might have it bookmarked) but the IconRail's Studio destination is the canonical path forward.
|
||
3. **Workshop detail view** at `StudioWorkshopDetail.tsx` — a new routed view that renders one workshop at a time:
|
||
- Two-column layout on `>= 1024px`: left = params/prompt input (40% width), right = preview (60% width)
|
||
- Mobile stacks to a single column
|
||
- Top of page shows `STUDIO / WORKSHOP-NAME` as the page title (the actual mode-breadcrumb is in TopStrip from Phase 8; this is an in-page heading)
|
||
- Action bar at the bottom of the right column: `Save`, `Export`, `Send to Assistant`
|
||
4. **StudioPromptBar** — freeform text input at the bottom of the Studio home. Placeholder: `Or just describe it: "I need a 1920×1080 wallpaper of …"`. Submits to a classifier (§6.5 of the spec) that maps intent to a workshop slug. If routing succeeds, navigate to `/content-studio/{workshopSlug}` with the prompt pre-filled. If routing fails, the prompt falls through to the Assistant as a new conversation message (see §Implementation notes).
|
||
5. **Workshop definitions** — a single source-of-truth data structure that both the grid and the detail view consume.
|
||
|
||
**NOT in Phase 10:**
|
||
|
||
- Any backend changes to content generators.
|
||
- The freeform prompt classifier itself — use a simple regex/keyword matching stub for Phase 10 (see §Implementation notes). A real LLM classifier is out of scope.
|
||
- Recipe integration (v1.8 planned; not part of Wave 2).
|
||
- Deleting `ConvertPage.tsx`. Phase 16 cleanup handles dead files.
|
||
- Editing `App.tsx` routes — report needed routes to the controller.
|
||
- Mobile polish beyond "grid collapses to 1 column" — Phase 15 handles mobile.
|
||
|
||
---
|
||
|
||
## File plan
|
||
|
||
### Create
|
||
|
||
| File | Responsibility | Est. lines |
|
||
|---|---|---|
|
||
| `ui/src/components/studio/workshops.ts` | Single source-of-truth data: array of 8 workshops with `slug`, `title`, `subtitle`, `icon`, `componentKey`. Exported for consumption by both the grid and the detail view. | ~80 |
|
||
| `ui/src/components/studio/workshops.test.ts` | Tests: all 8 workshops present, slugs unique, icons are valid Lucide imports, each `componentKey` maps to a real component | ~60 |
|
||
| `ui/src/components/studio/WorkshopCard.tsx` | Single card for the grid. Props: `workshop`, `onClick`. Renders title, subtitle, icon, hover state. | ~70 |
|
||
| `ui/src/components/studio/WorkshopCard.test.tsx` | Tests: renders workshop data, click fires callback, hover state applies correctly | ~90 |
|
||
| `ui/src/components/studio/WorkshopGrid.tsx` | Responsive grid of WorkshopCards. Takes `workshops` array and `onSelect` callback. | ~60 |
|
||
| `ui/src/components/studio/WorkshopGrid.test.tsx` | Tests: renders N cards, onSelect fires with correct workshop slug, responsive class application | ~80 |
|
||
| `ui/src/components/studio/StudioPromptBar.tsx` | Freeform text input + submit button. Calls `classifyIntent` helper and either navigates or calls `onFallbackToAssistant`. | ~80 |
|
||
| `ui/src/components/studio/StudioPromptBar.test.tsx` | Tests: renders input, classifier routing, fallback path | ~110 |
|
||
| `ui/src/components/studio/classifyIntent.ts` | Pure function. Given a prompt string, returns `{ slug: string; prefilledPrompt: string } \| null`. Keyword-matching only in Phase 10. | ~70 |
|
||
| `ui/src/components/studio/classifyIntent.test.ts` | Parameterized tests: "diagram of X" → diagrams, "wallpaper of Y" → wallpapers, "convert pdf to docx" → convert, "make me a logo" → brand-kits, "random chat" → null | ~80 |
|
||
| `ui/src/pages/StudioWorkshopDetail.tsx` | Routed view. Reads `:workshopSlug` from params, looks up in workshops data, renders two-column layout with the workshop's existing generator component on the right and a params/prompt input on the left. Action bar at the bottom: Save / Export / Send to Assistant. | ~180 |
|
||
| `ui/src/pages/StudioWorkshopDetail.test.tsx` | Tests: renders workshop by slug, 404 fallback for unknown slug, action bar renders | ~120 |
|
||
|
||
### Modify
|
||
|
||
| File | Change |
|
||
|---|---|
|
||
| `ui/src/pages/ContentStudio.tsx` | Replace the 7-tab layout with the new WorkshopGrid + StudioPromptBar. Remove tab state and tab container. Preserve any cross-cutting concerns (e.g. error boundaries, query client usage) that the existing page has. |
|
||
|
||
**Do not create or modify any other files.**
|
||
|
||
---
|
||
|
||
## Implementation notes
|
||
|
||
### Workshop data structure
|
||
|
||
```ts
|
||
// ui/src/components/studio/workshops.ts
|
||
import { Sparkles, Layers, Palette, Image, FileText, Award, Share2, Repeat } from "lucide-react";
|
||
import type { ComponentType } from "react";
|
||
|
||
export type WorkshopSlug =
|
||
| "diagrams" | "icons" | "themes" | "wallpapers"
|
||
| "documents" | "brand-kits" | "social" | "convert";
|
||
|
||
export interface WorkshopDefinition {
|
||
slug: WorkshopSlug;
|
||
title: string; // Inter 700 uppercase, e.g. "DIAGRAMS"
|
||
subtitle: string; // Inter 400 silver, single line
|
||
icon: ComponentType<{ className?: string }>; // Lucide icon
|
||
/**
|
||
* Key for looking up the generator component. The detail view maps
|
||
* this to an existing component from ContentStudio's current tab
|
||
* implementations. Phase 10 does NOT rewrite any generator internals.
|
||
*/
|
||
componentKey: string;
|
||
}
|
||
|
||
export const WORKSHOPS: WorkshopDefinition[] = [
|
||
{ slug: "diagrams", title: "DIAGRAMS", subtitle: "Mermaid → rendered SVG", icon: Sparkles, componentKey: "diagram-renderer" },
|
||
{ slug: "icons", title: "ICONS", subtitle: "SVG sets from description", icon: Layers, componentKey: "icon-renderer" },
|
||
{ slug: "themes", title: "THEMES", subtitle: "Color → full palette", icon: Palette, componentKey: "theme-renderer" },
|
||
{ slug: "wallpapers", title: "WALLPAPERS", subtitle: "Desktop, mobile, banners", icon: Image, componentKey: "wallpaper-renderer" },
|
||
{ slug: "documents", title: "DOCUMENTS", subtitle: "PDF reports, invoices", icon: FileText, componentKey: "document-renderer" },
|
||
{ slug: "brand-kits", title: "BRAND KITS", subtitle: "Full brand identity", icon: Award, componentKey: "brand-renderer" },
|
||
{ slug: "social", title: "SOCIAL", subtitle: "Platform-ready posts", icon: Share2, componentKey: "social-renderer" },
|
||
{ slug: "convert", title: "CONVERT", subtitle: "File format conversion", icon: Repeat, componentKey: "convert" },
|
||
];
|
||
```
|
||
|
||
Pick Lucide icons that fit. If a more appropriate icon exists for a slug, use it. The above is a sensible default.
|
||
|
||
### The `componentKey` → real component mapping
|
||
|
||
Phase 10 does NOT rewrite any generator. For each workshop, the detail view renders the existing generator component as-is. You must read the current `ContentStudio.tsx` to find those components. They'll be something like `<DiagramGeneratorTab />`, `<IconGeneratorTab />`, etc. Build a map:
|
||
|
||
```ts
|
||
// In StudioWorkshopDetail.tsx (or a small helper module within studio/)
|
||
const WORKSHOP_COMPONENTS: Record<string, ComponentType> = {
|
||
"diagram-renderer": DiagramGeneratorTab,
|
||
"icon-renderer": IconGeneratorTab,
|
||
// ... etc.
|
||
"convert": ConvertPageBody, // see Convert fold-in notes below
|
||
};
|
||
```
|
||
|
||
If a generator doesn't currently exist in `ContentStudio.tsx`'s tabs (e.g. if the current ContentStudio has only 6 tabs and we're adding Social as a future extension), render a `<WorkshopPlaceholder />` inline-component that shows `"Coming soon"` for that slug. Do NOT stub out a fake generator.
|
||
|
||
### Convert fold-in
|
||
|
||
`ui/src/pages/ConvertPage.tsx` currently exists and is routed via `/convert`. Phase 10 needs the Convert workshop body rendered inside the Studio workshop detail shell, but the existing `/convert` route MUST continue to work (backwards compat).
|
||
|
||
Strategy:
|
||
1. Extract the inner content of `ConvertPage.tsx` into a reusable component `ConvertPageBody`. Do this by **reading** `ConvertPage.tsx` and identifying the JSX that's the actual convert UI (the file uploader, format selector, conversion runner) vs. the page-level shell.
|
||
2. If `ConvertPage.tsx` is already structured so the inner content is a single child component, just import and reuse that component — no refactor needed.
|
||
3. If `ConvertPage.tsx` is a monolithic component and extracting `ConvertPageBody` requires a real refactor, take the smallest possible subset: wrap the whole `<ConvertPage />` in the workshop detail and accept that there's a small amount of redundant chrome. Flag as a concern.
|
||
4. Leave `ConvertPage.tsx` in place; the `/convert` route keeps rendering it directly.
|
||
|
||
### StudioPromptBar fallback to Assistant
|
||
|
||
When the classifier returns `null`, the prompt should fall through to the Assistant as a new conversation. The cleanest implementation is:
|
||
|
||
1. Navigate to `/<companyPrefix>/assistant?prompt=<urlencoded prompt>`.
|
||
2. The Assistant page (Phase 9's work) reads the `prompt` query param on mount and pre-fills the input.
|
||
|
||
**BUT:** Phase 9's `PersonalAssistant.tsx` rewrite may not include query-param pre-fill logic. Coordinate: add a note to your final report saying "StudioPromptBar fallback assumes Phase 9 will honor `?prompt=` query param on the Assistant route". Phase 9's plan should also have this noted; if it doesn't, the controller will coordinate.
|
||
|
||
For Phase 10's implementation, go ahead and navigate with the query param. If Phase 9 doesn't honor it yet, the worst case is the prompt is dropped and the user lands on an empty assistant — acceptable degradation.
|
||
|
||
### classifyIntent — Phase 10 rules
|
||
|
||
Simple keyword-based classifier. Pure function, no state, no async.
|
||
|
||
```ts
|
||
export function classifyIntent(prompt: string): { slug: WorkshopSlug; prefilledPrompt: string } | null {
|
||
const lower = prompt.toLowerCase().trim();
|
||
if (!lower) return null;
|
||
|
||
// Convert intent — "convert X to Y", "from X to Y", explicit formats
|
||
if (/\bconvert\b|from\s+\w+\s+to\s+\w+|\bpdf\b|\bdocx?\b|\bmd\b/i.test(lower)) {
|
||
return { slug: "convert", prefilledPrompt: prompt };
|
||
}
|
||
// Wallpaper intent
|
||
if (/wallpaper|background\s+image|desktop\s+bg/i.test(lower)) {
|
||
return { slug: "wallpapers", prefilledPrompt: prompt };
|
||
}
|
||
// Diagram intent
|
||
if (/diagram|flowchart|sequence|mermaid|architecture/i.test(lower)) {
|
||
return { slug: "diagrams", prefilledPrompt: prompt };
|
||
}
|
||
// Icon intent
|
||
if (/\bicon\b|icon\s+set|svg\s+icon/i.test(lower)) {
|
||
return { slug: "icons", prefilledPrompt: prompt };
|
||
}
|
||
// Theme intent
|
||
if (/\btheme\b|color\s+palette|palette/i.test(lower)) {
|
||
return { slug: "themes", prefilledPrompt: prompt };
|
||
}
|
||
// Document intent
|
||
if (/pdf|document|invoice|report|one-?pager/i.test(lower)) {
|
||
return { slug: "documents", prefilledPrompt: prompt };
|
||
}
|
||
// Brand intent
|
||
if (/brand|logo|identity|style\s+guide/i.test(lower)) {
|
||
return { slug: "brand-kits", prefilledPrompt: prompt };
|
||
}
|
||
// Social intent
|
||
if (/social|tweet|post|instagram|linkedin|twitter|x\.com/i.test(lower)) {
|
||
return { slug: "social", prefilledPrompt: prompt };
|
||
}
|
||
|
||
return null; // unclassified → fall through to Assistant
|
||
}
|
||
```
|
||
|
||
Test exhaustively with the parameterized test pattern from Phase 8's `ModeBreadcrumb.test.tsx`.
|
||
|
||
### ContentStudio.tsx target structure
|
||
|
||
```tsx
|
||
export function ContentStudio() {
|
||
const navigate = useNavigate();
|
||
const { selectedCompanyId, selectedCompany } = useCompany();
|
||
const prefix = selectedCompany?.issuePrefix ?? "";
|
||
|
||
const handleSelectWorkshop = (slug: WorkshopSlug) => {
|
||
navigate(`/${prefix}/content-studio/${slug}`);
|
||
};
|
||
|
||
const handleFallbackToAssistant = (prompt: string) => {
|
||
navigate(`/${prefix}/assistant?prompt=${encodeURIComponent(prompt)}`);
|
||
};
|
||
|
||
return (
|
||
<div className="mx-auto w-full max-w-[1200px] px-6 py-8">
|
||
<header className="mb-8">
|
||
<h1 className="text-[14px] font-semibold uppercase tracking-[0.1em] text-primary">STUDIO</h1>
|
||
<p className="mt-2 text-[14px] text-muted-foreground">
|
||
Eight workshops. Pick one or describe what you need.
|
||
</p>
|
||
</header>
|
||
|
||
<WorkshopGrid workshops={WORKSHOPS} onSelect={handleSelectWorkshop} />
|
||
|
||
<div className="mt-12">
|
||
<StudioPromptBar
|
||
onClassified={(slug, prefilled) => navigate(`/${prefix}/content-studio/${slug}?prompt=${encodeURIComponent(prefilled)}`)}
|
||
onFallbackToAssistant={handleFallbackToAssistant}
|
||
/>
|
||
</div>
|
||
</div>
|
||
);
|
||
}
|
||
```
|
||
|
||
Preserve any existing cross-cutting concerns (error boundaries, query client, auth checks) from the current ContentStudio.
|
||
|
||
### StudioWorkshopDetail routing
|
||
|
||
**You are NOT allowed to edit App.tsx.** You'll need sub-routes for Studio workshops, e.g. `/<prefix>/content-studio/:workshopSlug`. The existing App.tsx currently only has `/<prefix>/content-studio` without a sub-route param.
|
||
|
||
**Workaround for Phase 10:** render StudioWorkshopDetail as a child of ContentStudio, not as a separate route. ContentStudio checks for a `:workshopSlug` path segment via `useLocation()` (not `useParams()`, because the App.tsx routes don't define the param). If there's a slug, render StudioWorkshopDetail; otherwise render the grid.
|
||
|
||
```tsx
|
||
// ContentStudio.tsx pseudocode
|
||
export function ContentStudio() {
|
||
const location = useLocation();
|
||
const match = location.pathname.match(/\/content-studio\/([^/]+)/);
|
||
const workshopSlug = match?.[1];
|
||
|
||
if (workshopSlug) {
|
||
return <StudioWorkshopDetail slug={workshopSlug} />;
|
||
}
|
||
return <StudioHomeGrid ... />;
|
||
}
|
||
```
|
||
|
||
This is hacky but correct for Phase 10. Report to the controller: "New App.tsx routes needed: `content-studio/:workshopSlug`". The controller will replace the pathname-matching with a proper route param after Wave 2.
|
||
|
||
---
|
||
|
||
## Acceptance criteria
|
||
|
||
Phase 10 is complete when:
|
||
|
||
1. Loading `/NEX/content-studio` renders an 8-card grid with the new workshop definitions — no tab strip visible anywhere on the page.
|
||
2. Each card navigates to `/NEX/content-studio/{slug}` when clicked.
|
||
3. Loading `/NEX/content-studio/{slug}` renders the workshop detail view with the existing generator component on the right and a params panel on the left.
|
||
4. Loading `/NEX/content-studio/convert` renders the ConvertPage body inside the workshop detail shell.
|
||
5. Loading `/NEX/convert` (the legacy route) still works — backwards compat.
|
||
6. The StudioPromptBar at the bottom of the grid view accepts text input and routes correctly:
|
||
- `"diagram of the auth flow"` → navigates to `/NEX/content-studio/diagrams?prompt=...`
|
||
- `"convert this pdf to markdown"` → navigates to `/NEX/content-studio/convert?prompt=...`
|
||
- `"random musing"` → navigates to `/NEX/assistant?prompt=...` (fallback)
|
||
7. All new components have tests using the Phase 8 manual createRoot + act pattern.
|
||
8. `npx vitest run src/components/studio/ src/pages/StudioWorkshopDetail.test.tsx` passes.
|
||
9. `npx tsc --noEmit 2>&1 | grep -E "studio/|ContentStudio\.tsx|StudioWorkshopDetail\.tsx"` returns no errors.
|
||
10. The `/convert` top-level route still renders the ConvertPage.
|
||
11. No file outside the declared ownership is modified.
|
||
|
||
---
|
||
|
||
## Commit scheme
|
||
|
||
Suggested atomic commits (one per logical unit):
|
||
|
||
1. `feat(nexus): add WORKSHOPS data + classifyIntent helper (phase 10)` — workshops.ts, classifyIntent.ts, their tests
|
||
2. `feat(nexus): add WorkshopCard and WorkshopGrid components (phase 10)` — cards + grid + tests
|
||
3. `feat(nexus): add StudioPromptBar with intent routing (phase 10)` — prompt bar + tests
|
||
4. `feat(nexus): add StudioWorkshopDetail page (phase 10)` — detail view + tests
|
||
5. `refactor(nexus): rewire ContentStudio to workshop grid (phase 10)` — ContentStudio.tsx rewrite
|
||
|
||
Each commit must build and pass tests. Include `Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>` on every commit.
|
||
|
||
---
|
||
|
||
## Before you begin
|
||
|
||
Ask the controller if any of these are unclear:
|
||
|
||
- The component names and import paths of the current ContentStudio's 7 tabs (read the file first and report what's there)
|
||
- Whether `ConvertPage.tsx` is extractable or if you should wrap the whole thing
|
||
- Whether the new sub-routes the controller needs to add warrant a pause and App.tsx edit, or if the hacky path-matching workaround is acceptable
|
||
- Anything in §6 of the spec that reads as ambiguous
|
||
|
||
If you find that the current ContentStudio has fewer than 7 tabs (e.g. only 5 generators are actually built), report it — the spec assumes 7 existing + 1 new (convert). The workshop grid can still show 8 cards with placeholder content for missing generators.
|
||
|
||
---
|
||
|
||
## When you're in over your head
|
||
|
||
Escalate early if:
|
||
|
||
- ConvertPage body extraction breaks tests in the legacy `/convert` route
|
||
- The current ContentStudio has a global state or context that doesn't compose cleanly with the new grid → detail flow
|
||
- Any generator component has hardcoded dependencies on the tab layout you're removing
|
||
- classifyIntent is pulling in too many edge cases (stop at ~10 keyword patterns; more is Phase 14's classifier)
|
||
|
||
---
|
||
|
||
## Report format (final)
|
||
|
||
- **Status**
|
||
- **Commits produced**
|
||
- **Files created / modified**
|
||
- **Tests added and passing count**
|
||
- **Typecheck result** for Phase 10 files
|
||
- **Routing needs** for the controller to add to App.tsx
|
||
- **ConvertPage fold-in notes** — did you extract a body component or wrap the whole page?
|
||
- **Open concerns**
|
||
- **Deviations from the plan**
|
||
- **Self-review findings**
|