/projects/:slug), its own KPI strip, its own workflow sidebar, its own conversation canvas, and its own ⌘K search. This is the surface long-running products like cachepool are driven from — every prompt, every file, every deploy belongs to one project rather than living as an isolated workflow in the global list.
Concretely: a user lands on /projects, sees a card grid of all their projects with status, repo, branch, and last-edit timestamps, and opens one to a workspace that shows the project’s full life — recent workflows, live activity, generated files, cost/time/tokens spent — all in one place. Selecting a workflow inside a project switches the canvas to a wider conversation lane (~1100px composer, vs. ~500px in the legacy three-panel Workbench) with the agent’s scenario, graph, and live deployment preview rendered above the threads.
Refinement (2026-05-31): heterogeneity-aware command-bar console
The/projects/:slug console was reworked from a three-band poster header + single-card feed into a denser, professional layout that treats the project as one abstraction above workflows — a long-running shared context hosting concurrent, heterogeneous workflows from different people (e.g. one person’s build alongside another’s QA pass). What it now enables:
- A single ~56px command bar carries every fact exactly once. The old triple-redundancy — a synthesised hero sentence, an
idlechip, and a six-tile KPI grid all repeating the same workflow/prompt counts — is gone. Counts live only in one inline stat ribbon (N workflows · C/T prompts). - The who × what-kind mix is legible at a glance. The command bar shows a contributor avatar stack (real faces via the team-override avatar) + active-kind chips (Build / QA / Research / Docs / Review), with running kinds pulsing. A single-author/single-kind project collapses this to a quiet
"Azat · Build"so a focused project never looks busy; a mixed project expands and the board auto-groups by kind. - The canvas fills space at 0 / 1 / many workflows. A responsive card grid replaces the single-card feed (no more empty Mars-background void at one workflow); a 0-workflow Launchpad offers an inline composer instead of a lone button in a void.
- Each card/row answers who+what+state in one glance via a fixed four-corner identity stamp: kind tile (top-left), status pill (top-right), author avatar (bottom-left), prompt numbers + progress underline (bottom-right). Three reserved colour zones — kind = scenario tone, status = status colour, author = avatar ring — so no element ever carries two meanings.
needs_attentionis now surfaced on/projects(a completed-with-warnings job no longer reads as a clean “completed”), via a shared status module the rail/board/changelog/mix all consume.
Refinement (2026-06-11): collapsible Workflows panel + latest prompt per row
The project dashboard’s Workflows section (apps/frontend/app/(authenticated)/projects/dashboard/project-workflows-list.tsx/projects/dashboard/project-workflows-list.tsx)) now:
- Collapses to its header, collapsed by default — the dashboard leads with the deployment card; the header keeps the workflow count and the New-workflow action either way, and a caret toggle (rotates on expand) reveals the search box + rows.
- Shows the latest user prompt in each row, truncated in muted text next to the (often auto-named) workflow title — so a row like
workflow:wf-188also tells you what was last asked of it.
WorkflowStatusResponse gains latest_prompt: Option<String> — the newest prompt_threads.prompt_text per workflow, filled only by the list query (GET /v1/workflows) via a correlated subquery; single-workflow endpoints leave it null since they fetch full prompt threads separately. Paths: mother-ai/src/models.rs, mother-ai/src/routes.rs (fetch_workflows_from_postgres), zod schema in apps/frontend/lib/schemas.ts. Old clients are unaffected (skip_serializing_if on null; .passthrough() schema).
Refinement (2026-06-11): dashboard header — repo link for kind=new projects, Share, actions menu
The dashboard tab bar (apps/frontend/app/(authenticated)/projects/dashboard/project-dashboard.tsx/projects/dashboard/project-dashboard.tsx)) gains:
- GitHub Repository link now shows for
kind=newprojects. Root cause: the worker auto-provisions the GitHub repo for new projects but never backfillsprojects.repo_url(deliberately — feeding it back asmetadata.project.repoon follow-up prompts would flip the worker’s explicit-override path and disable repo auto-provisioning /github_full_nameresolution, which Vercel self-heal depends on). Fix: mother-ai now lifts the public remote from the worker’s push audit log (job_runs.audit_log.project_push.remote_url, falling back toproject_initial_push.remote_url) intometadata.project.repo_urlin both the single-workflow status query andGET /v1/workflows(compose_workflow_metadatainmother-ai/src/routes.rs— the list endpoint now runs the same overlay, which also makesproduction_urlavailable to list consumers). The dashboard readsproject.repoUrl ?? deployment.repoUrl(the.gitsuffix is trimmed for the browser link). - Share button (ShareNetwork icon) — copies the production URL (or the console URL pre-deploy) to the clipboard with a 1.5s “Copied” check flash.
- Actions menu — a 3-dot trigger (radix
@radix-ui/react-dropdown-menu, same pattern as the workflow rail rows) with a single Delete item.Visual affordance only; deletion is not wired up yet.Superseded same-day: Rename and Delete are both live — see the project-lifecycle refinement below.
Refinement (2026-06-11): project lifecycle — guaranteed binding, rename, delete, deployment duration
What it enables:- ”+ New workflow” can no longer silently attach to a nonexistent project. Workflow submissions carrying
metadata.project.slugare validated against theprojectstable at enqueue time and the client’s project snapshot (name/kind/base_branch/repo) is overwritten from the authoritative row — so the binding the/projectsconsole relies on is server-guaranteed, not client trust. - Rename a project from the dashboard actions menu. Display name only — the slug (and the GitHub repo / Vercel project / workspace naming derived from it) is immutable. The server backfills
metadata.project.nameon every bound workflow in the same transaction, so legacy projects whose names were derived from the first prompt can finally be fixed, everywhere at once. - Delete a project (typed-name confirmation) — removes the project, all of its workflows, prompts, and run history from Cerebrum. The GitHub repository, Vercel project, and any provisioned database are deliberately not touched; the dialog says so.
- The deployment card’s Duration field is real — workflow wall-clock (
completed_at − created_at) of the deployment workflow, formatted via the sharedformatDuration; an em dash while queued/running.
- mother-ai (
mother-ai/src/routes.rs,mother-ai/src/models.rs):validate_and_stamp_project_bindingruns insubmit_chat,create_workflow, andsubmit_command_intent(notappend_prompt, which inherits stored workflow metadata). Unknown slug → 404.project.repois only stamped whenprojects.repo_urlis set (kind=existing) so kind=new repo auto-provisioning stays intact; a workflow-pinneddesign_system_idbeats the project default. Without a Postgres DSN (dev mode) validation is skipped.PATCH /v1/projects/:slug(UpdateProjectRequest { name }) — renames + backfillsworkflow_runs.metadata.project.nameviajsonb_set, transactionally, without bumpingworkflow_runs.updated_at(protectsupdated_at desckeyset cursors).DELETE /v1/projects/:slug— one transaction: lock project row (for update, so rename/delete races serialize), deletejob_runsby both linkage paths (no FK), deleteworkflow_runs(prompt_threadscascade via FK), delete the project row. Returns 200 +{deleted, slug, workflows_deleted}(the BFF proxy re-serializes JSON bodies, so no 204). Redis queue/status keys self-expire via TTL.WorkflowStatusResponse.completed_at: Option<DateTime<Utc>>— read fromworkflow_runs.completed_atin both the list and single-workflow Postgres queries; null on Redis fallback paths. Additive (skip_serializing_if).
- worker (
worker/worker/storage/postgres_repo.py): new expression indexidx_workflow_runs_project_slugon(metadata->'project'->>'slug')— serves the project-scoped list, the rename backfill, and the delete cascade. Idempotent, added inensure_schema(). - Frontend BFF:
PATCH/DELETEhandlers on apps/frontend/app/api/projects/[slug]/route.ts;PATCHjoins the proxy method union inapps/frontend/app/api/mother-ai/_shared.ts. - Frontend client/UI:
updateProject/deleteProjectinapps/frontend/lib/mother-ai-client.ts;completed_atin the zod schema (apps/frontend/lib/schemas.ts); newrename-project-dialog.tsx+delete-project-dialog.tsxunderapps/frontend/app/(authenticated)/projects/dashboard//projects/dashboard/); the dashboard’s actions menu wires both, andprojects-workbench.tsxdrops back to the projects rail after a delete.deriveDeploymentcomputesdurationMsconsumed bydeployment-card.tsx.
metadata.project.slug that has no projects row now returns 404 instead of queueing. Workflows without project metadata are untouched. Delete is Cerebrum-data-only; a job already running for a deleted workflow finishes and re-upserts a ghost workflow row carrying the dead slug (visible in the global /workflows list, 404 under the project) — accepted under the data-only scope. Rename is safe mid-flight: the worker’s metadata upsert merges stored-wins (excluded.metadata || stored), so a completing job carrying the old name cannot clobber the backfill.
Tests: cargo test in mother-ai/ (5 new unit tests on the stamp function: identity refresh, repo None/Some, pass-through, design-system pinning — 23 total green); worker suite worker/.venv/bin/python -m pytest tests/ -v stays 0-fail; tsc --noEmit + biome clean in apps/frontend.
What’s changed
Backend (mother-ai)
GET /v1/projects/:slug— single-project lookup by collision-free slug. ReturnsProjectRecord(id, name, slug, kind, repo_url, base_branch, created_by, timestamps). Path:mother-ai/src/routes.rs.GET /v1/workflows?project_slug=<slug>— new optional filter on the existing list endpoint. Applies to both page rows andtotal_count, using ametadata->'project'->>'slug'JSON path filter at the SQL layer. The handler’s optional-clause builder was refactored to a running param-index counter so the next filter we add slots in without re-indexing$Npositions.ListWorkflowsQuery.project_slug: Option<String>added tomother-ai/src/models.rs.
Frontend BFF
GET /api/projects/[slug]proxies the new mother-ai endpoint throughrequireApiUser(apps/frontend/app/api/projects/[slug]/route.ts).GET /api/mother-ai/workflowsnow forwardsproject_slugwhen present (apps/frontend/app/api/mother-ai/workflows/route.ts).
Frontend client
getProject(slug)and a newprojectSlugargument onlistWorkflows()inapps/frontend/lib/mother-ai-client.ts.useWorkflowSession(workflowId)hook inapps/frontend/lib/workflow/use-workflow-session.tslifts the workflow status + prompts + active-job SSE + scenario-derived state out ofWorkflowPanelsso both the legacy Workbench and the new console share one source of truth. (A prior commit had already updatedWorkflowPanelsto import this hook without committing the file itself — this change unbreaks HEAD.)- “Projects” nav entry added between Workbench and Atlas in
apps/frontend/components/layout/app-header.tsx.
Frontend route surface
All underapps/frontend/app/(authenticated)/projects//projects/):
page.tsx—/projectsindex, max-w-[1680px] frosted-card layout with a 1–3 column responsive project grid.projects-list-view.tsx— list view + create-new modal (POST /api/projects).[slug]/page.tsx+[slug]/project-detail-view.tsx— the per-project shell. Command bar on top, collapsible sidebar (workflows + activity) on the left, mode-driven canvas on the right (launchpad / board / detail), ⌘K palette and new-workflow dialog overlaid. Holds the lifted status-filter state (shared rail + board), the boardgroupBystate (localStoragecerebrum:projects:board-groupby, with an auto-vs-explicit default), the mixfocusstate, and ESC-to-deselect.[slug]/project-header.tsx— the ~56px command bar: identity +ProjectMixSummary+ one stat ribbon (the only place counts live) + a quiet Setup popover (repo/branch real; db/scenario as mutedconfigure →placeholders, never coloured fake status) + ⌘K + the one canonical+ WorkflowCTA. Replaces the old hero sentence +LastActivityBadge+ six-tile KPI grid.[slug]/project-mix-summary.tsx— the command-bar who × what-kind roll-up: contributor avatar stack (max 4 ++N) + active-kind chips (running kinds pulse) + a liveness token; collapses to inline"Azat · Build"when calm; clicking an avatar/chip sets a soft board focus (filter-only). Exports theMixFocustype.[slug]/use-project-workflows.ts— shareduseInfiniteQueryhook +aggregateProjectStats+ newaggregateProjectMix(contributors / kinds /isHeterogeneous, derived purely from loaded pages, no extra fetch). Sidebar, command bar, board, activity strip, and ⌘K palette all consume the same React Query cache key (["project-workflows", slug]).[slug]/project-workflow-sidebar.tsx— compact nav rail. Now prepends a per-row identity stamp (kind glyph + author avatar — author is no longer constant within a project), consumes the shared status module (soneeds_attentionshows), lifts its status filter up to the shell, and owns its single header (with the⊕new-workflow and‹collapse controls) — the redundant stackedPROJECTband above it is removed.[slug]/project-changelog.tsx— the Activity strip (kept visible in the sidebar footer): an actor-led, verb-led chronological feed —Azat · Build · completed · "…"— last 12 state changes, using the same avatar + kind vocabulary as the cards. No counts (orthogonal to the mix summary).[slug]/project-workflow-board.tsx— the no-selection canvas (replacesworkflow-showcase-feed.tsx): every loaded workflow as a four-corner identity card in a responsive grid (grid-cols-1 lg:grid-cols-2 3xl:grid-cols-3), segmented in place by Kind / Author / Status / None via aGroup:control, over the one shared cursor (oneLoad moresentinel re-buckets all sections — no per-lane pagination). Keeps the showcase card body +useQueries6-card preview-fetch cap.[slug]/project-launchpad.tsx— the 0-workflow canvas: an inlineComposerTextareawired to the samesubmitChat({ project })path the dialog uses (routes through ScenarioOrchestrator), instead of a lone button in a void.lib/scenario-kind.ts—bucketKind(scenario_id)→ Build/QA/Research/Docs/Review/Ops/Other (+KIND_ORDER,kindVisual), the label-side sibling ofscenarioIcon. The kind axis of project heterogeneity.lib/workflow-status.ts— shared status vocabulary (effectiveStatuswithneeds_attention,statusColor/statusDot/statusLabel/statusToFilter,STATUS_FILTERS,relativeTime) lifted fromJobsListPaneland consumed by the /projects surfaces;/workflowskeeps its own copies untouched (no regression on the reference page).[slug]/selected-workflow-canvas.tsx— when a workflow is selected, mounts (in order): slim header,StatusTicker,WorkflowSummaryTile(cost/tokens/time/files/build), reusedScenarioHeader, reusedAgentGraphCard,ArtifactPreview(iframe ofmetadata.project.production_urlwhen set, dashed placeholder otherwise), collapsibleFilesGeneratedlist, and the existingJobContextPanel(threads + composer). Per-status radial-gradient hue on the canvas.[slug]/new-workflow-dialog.tsx— submits viasubmitChat({ project: {…} })so the resulting workflow is stamped withmetadata.project.slugand surfaces immediately in the project’s own list.[slug]/project-cmd-k.tsx— project-scoped ⌘K palette. Arrow-key navigation, fuzzy search on objective + workflow id, ”+ Workflow” fallback when nothing matches.
Impact scope
- Affected subsystems: mother-ai (Rust HTTP layer), Next.js BFF + UI. Worker unchanged. Auth/permissions inherit the existing
requireApiUser+ non-admincreated_byscoping. - Backwards compatibility: the new
project_slugquery param is optional; callers that don’t pass it see exactly the same behaviour as before. Existing workflows that already carrymetadata.projecton the worker side become filterable immediately — no backfill required. - Defaults:
/projectsis visible to every signed-in user. Workflows created via the new ”+ Workflow” button are stamped with the project metadata automatically. The ⌘K palette only activates when focus is on a/projects/:slugpage. - Auto-runs: none. No new cron jobs or background workers; the page polls via React Query with the same active/idle intervals the Workbench uses (2s while anything is running, 6s idle).
- Known stubs (slots reserved, follow-ups tracked elsewhere):
db/scenarioin the Setup popover — quietconfigure →placeholders (no backing field yet); wire to DB provisioning / scenario live-edit when those land. No longer rendered as coloured fake status in the header.- Composer attachment dropdown items remain
console.infostubs. - Pinned/featured workflows not yet implemented.
- The mix summary and aggregates summarise loaded pages only (one shared cursor); a large project under-reports kinds/people until more pages load — the
+Nchip signals there may be more. No separate count endpoint. - A fuller extraction of
WorkflowRow/WorkflowListinto shared components (soJobsListPanelconsumes them too) is a deliberate follow-up — out of scope here to avoid regressing/workflows.
Tests
- Rust:
cd mother-ai && cargo check— passes. No new unit tests added; the new SQL filter is exercised end-to-end via the FE. - TypeScript:
cd apps/frontend && npx --no-install tsc --noEmit— passes. - Biome:
cd apps/frontend && pnpm run lint(ornpx --no-install biome check) — passes. - Build:
cd apps/frontend && pnpm build— passes (exit 0). - Manual verification path:
cd apps/frontend && pnpm dev- Visit
/projects, click ”+ New project”, create one (slugconsole-testfor example). - Open
/projects/console-testwith no workflows → the Launchpad composer (not an empty void); submit a prompt and confirm it appears in the rail + board. - With ≥1 workflow → the board card fills its column (no Mars void); confirm the command bar’s stat ribbon is the only place counts appear (no hero sentence / KPI grid). Hit ⌘K to confirm the palette opens.
- Heterogeneity: with workflows from two people / two kinds, confirm the command bar shows a contributor avatar stack + kind chips and the board auto-groups by Kind; a single-author/single-kind project collapses the summary to
"Azat · Build"and shows a flat grid. Switch theGroup:control (Kind/Author/Status/None) and click an avatar/kind chip to focus-filter. - Select a workflow → the canvas swaps to the detail view (summary tile / scenario card / agent graph / artifact preview / files / composer); the rail + command bar persist; the selected rail row gets the cyan border; press ESC to return to the board. Confirm a completed-with-warnings job renders as
needs attentionon the rail. - Visit
/workflowsand confirm the Agent Workbench list is unchanged (the shared status module is consumed only by /projects).
- No pre-existing test regressions introduced by this UI refinement.