Skip to content

feat(demo): 'it's agents all the way down' scripted animation + Library as a toggleable panel#4327

Closed
vibegui wants to merge 34 commits into
mainfrom
vibegui/turtles-demo
Closed

feat(demo): 'it's agents all the way down' scripted animation + Library as a toggleable panel#4327
vibegui wants to merge 34 commits into
mainfrom
vibegui/turtles-demo

Conversation

@vibegui

@vibegui vibegui commented Jul 5, 2026

Copy link
Copy Markdown
Contributor

What is this contribution about?

A scripted, full-screen product animation of the "path = context" navigation model from #4326 — plus a real UX fix that fell out of building it (Library as a toggleable panel instead of a route).

1. /demo/turtles — one animation instead of two demos

Replaces the two previous demo scenarios with a single continuous walkthrough of the simplified world, built on the demo-mode Director/ghost-cursor machinery (rebased onto current main) and rendered with the real chat pipeline and the real shell's design vocabulary (bg-sidebar cream, card-shadow float cards, the home hero + capybara + corner art, the composer's Tools/scope/model chrome, the real deco logo):

  1. Home — your deco. Topbar breadcrumb shows just the logo. The real home hero + composer, with a vertical "Your orgs" rail where each org is a reporting agent: card with status dot + three metric squares (stat with ▲ delta, single-hue 12-point sparkline, and a watched number). Org agents also list directly under AGENTS in the sidebar. "Good morning — what needs me today?" fans out ask_org_pilot to every org in parallel and streams a "While you were away" digest table with a Needs you column; the rail updates live.
  2. Hop into an org. Click the org card → breadcrumb grows to deco / Vela Store → same product one level down: agents sidebar, chat with the org's pilot, live storefront preview. Ship a task; watch the storefront change.
  3. Zoom into an agent. deco / Vela Store / Storefront Bot — fix LCP; the watched LCP square back on the home rail later ticks 1.9s → 1.2s.
  4. Share sits right beside the breadcrumb and mints the MCP URL for exactly that scope (…/api/you/mcp, …/api/vela/mcp, …/api/vela/mcp/vela-bot) with client chips (Claude Code / Cursor / WhatsApp).
  5. Logo → home. The rail already reflects the shipped work. End card: "It's agents all the way down."

The composer's scope chip changes with the breadcrumb (Decopilot → Vela Pilot → Storefront Bot) — breadcrumb = chat scope, made visible.

Also carries the sidebar thread-UX reorg (#4230: Team threads / My threads / Agents as collapsible peer sections), cherry-picked so both stories land together.

2. Library: a toggleable panel, not a route (real app change)

The Library sidebar item was a full route swap that destroyed your chat context. Now:

  • Toolbar toggle like Chat (folder icon) revealing a right-side panel on any org-shell screen; open state in localStorage, browse state in the URL (?path= — added to the chat route's search schema so it survives there). /$org/files stays for deep links; mobile gets a full-screen sheet.
  • Panel preview takeover: the panel caps at ~560px, so clicking a file makes the preview fill the entire panel instead of a squeezed browse|preview split. The full /files route keeps the side-by-side split where there's real horizontal space.
  • The panel's close button renders inside the Library's own header row (title · refresh · upload · close) — one consistent row, no floating control intercepting clicks. The Library card top-aligns with the chat card.

Screenshots/Demonstration

Run /demo/turtles for the animation. Frames verified headlessly at every beat (home rail with metric squares, digest table, org hop, agent zoom, share popover, end card) with zero console errors.

How to Test

  1. bun run dev, open http://localhost:4000/demo/turtles — the ~90s animation plays and loops via the end card.
  2. In the real app: the sidebar no longer has Library; the toolbar folder icon toggles the Library panel over any screen. Click a file → preview fills the panel; close (X in the header row or the file pill in the toolbar) → back to browse. /$org/files still renders the full split view.

bun run check 0 errors · bun run lint 0 errors · bun run fmt applied · bun test unaffected (the 12 pre-existing env failures in mcp-oauth.test.ts / sidebar-release-card.test.tsx fail identically on main).

Migration Notes

  • The two previous demo scenarios (/demo/storefront, /demo/agents) are replaced by /demo/turtles.
  • No database migrations.

Related: #4326 (the RFC this animates), #4230 (sidebar reorg, included), #3927 (demo mode, rebased + included).

🤖 Generated with Claude Code


Summary by cubic

Adds a public, full‑screen scripted demo mode with a /demo chooser and the “turtles” scenario, plus a new toolbar breadcrumb (deco › team › agent) that moves org switching up and turns / into a cross‑team “MY deco” home. Library becomes a toggleable panel you can open anywhere.

  • New Features

    • Demo Mode: /demo index and /demo/turtles play a scripted walkthrough using the real chat via DemoChatStreamContext, with a ghost cursor, narrated captions (15 Maya1 clips, on by default), and an end card (Replay/Get started). Inline cards render via a tiny part‑renderer registry (work plan, PR, daily digest, CTA). Per‑agent MCP apps (Ops dashboard with a compact SLO card, Sales, Tasks kanban, Settings → Members with invite) show in a preview pane with an app switcher; chat/preview split is 40/60.
    • Shell: adds a toolbar breadcrumb (deco › team › agent) with a popover org switcher and Share beside it that mints scope‑exact MCP URLs (agent, team, or all teams up from the crumb).
    • Home: / becomes “MY deco” — your threads across all teams, sorted by what needs you next, with a compact account menu. Each card shows agent, status, last activity, and a content‑grounded “what’s happening here” line (fanned out per team via self‑MCP; cached). New empty‑state composer lets you start a thread in any team from home; it hands off to /$org/$taskId?autosend=true and sends your message on arrival.
    • Sidebar: collapsible Team threads (read‑only, “See all”), My threads, and a flat, pinnable Agents list; thread previews on home and inside teams; teammate rows are non‑archivable.
    • Library: toolbar folder toggle opens a right‑side panel on any team screen; open state in localStorage and browse state in ?path=; preview takes over the narrow panel; proper aria/close in the Library header; mobile uses a sheet. /$org/files stays for deep links.
    • Polish: fixes blank chat on team landing, extends Share popover hold, and prevents long reasoning text from widening the chat column (minmax(0,·) grid tracks).
  • Migration

    • Replaces /demo/storefront and /demo/agents with /demo/turtles (now listed at /demo).
    • / is now the MY‑deco home (no auto‑redirect to last team); switch teams from the toolbar breadcrumb.
    • Removes Library from the sidebar; use the toolbar toggle instead.
    • No database changes.

Written for commit 4a0cfe3. Summary will update on new commits.

Review in cubic


👋 @baby (Rafael Crespo) — continue here: the onboarding scenario

This PR has the day-30 demo (/demo/turtles — morning brief, org hop, per-agent MCP apps, kanban, settings-as-agent, narration). Per our data-room meeting, the day-zero onboarding demo is yours — a second scenario on this same branch, so both play from /demo.

Point your Claude at this PR and paste this prompt:

Check out branch vibegui/turtles-demo in decocms/studio. Read apps/mesh/src/web/demo/scenarios/turtles.tsx end-to-end first — it's the reference for everything: the Director/Track screenplay primitives (say, caption, tool, showCard, setSender, setPreview), the real-UX chrome (bg-sidebar shell, card-shadow float cards, the composer clone), the HTML "MCP app" builders (orgDashboard, storeOpsApp, tasksKanban, settingsApp), and the narration map in demo/voiceover.tsx.

Create apps/mesh/src/web/demo/scenarios/day-zero.tsx — the ONBOARDING story we aligned on: deferred setup. Beats: (1) new user connects GitHub + site URL + Analytics (the "setup person" role — this is the filter, ask it upfront); (2) the diagnostic runs visibly — agents fan out over the site/code; (3) the org ASSEMBLES itself on screen: dashboard fills with real findings, kanban pre-populates with proposed tasks ("parece que tive 6 reuniões com uma agência"); (4) "sua org tá pronta" moment — WhatsApp/email ping; (5) invite the brand person (Michelle) — she gets agents already set up, never sees GitHub. Register it in demo/scenarios/index.ts (add to SCENARIOS — /demo lists both). Reuse the turtles builders/components wherever possible; add narration lines to the VO map (I'll record the clips after).

Rules: demo-only changes (nothing under the real app), bun run fmt + bun run --cwd apps/mesh check must pass, verify by screenshotting http://localhost:4000/demo/day-zero with the dev server running (bun run dev). Commit style: feat(demo): … on this branch.

Everything's mocked and self-contained — no backend, no risk. Bring your dashboard HTML from the Baby-sandbox; the orgDashboard() builder in turtles.tsx is already an adaptation of it, so lift whatever's missing.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

8 issues found and verified against the latest diff

You’re at about 95% of the monthly reviewed-line limit. You may want to disable incremental reviews to conserve quota. Reviews will continue until that limit is exceeded. If you need help avoiding interruptions, please contact contact@cubic.dev.

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="apps/mesh/src/web/layouts/org-shell-layout/index.tsx">

<violation number="1" location="apps/mesh/src/web/layouts/org-shell-layout/index.tsx:146">
P2: The LibraryPanel can render alongside the full `/$org/files` route content when the panel was previously left open. Since `libraryOpen` is persisted in localStorage, visiting `/$org/files` (via deep link or navigation) while the panel state is `true` shows two Library instances reading/writing the same `?path=` and `?preview=` URL params. Consider either closing the panel when the route is already `/$org/files`, or checking the current route to skip the panel render on that route.</violation>
</file>

<file name="apps/mesh/src/web/components/sidebar/task-groups/agent-row.tsx">

<violation number="1" location="apps/mesh/src/web/components/sidebar/task-groups/agent-row.tsx:60">
P2: Keyboard reordering can stop working in expanded agent rows because the row-level `onKeyDown` replaces dnd-kit’s sortable key handler. Composing `sortable.listeners.onKeyDown` before the open-on-Enter/Space logic keeps drag keyboard support and avoids regressing accessibility.</violation>
</file>

<file name="apps/mesh/src/web/demo/runner.ts">

<violation number="1" location="apps/mesh/src/web/demo/runner.ts:27">
P1: Unexpected errors in scenario scripts silently cause a hang. The bare `catch{}` block swallows all errors, not just abort rejections. When a non-abort error (e.g., a TypeError in scenario code) is thrown inside `scenario.run()`, the catch falls through to `markEnded()` and then `awaitReplay()`, which waits indefinitely for a replay click that will never come in a broken state — the function's promise never resolves.</violation>
</file>

<file name="apps/mesh/src/web/demo/ghost-cursor.tsx">

<violation number="1" location="apps/mesh/src/web/demo/ghost-cursor.tsx:36">
P3: The cursor SVG and click ripple are purely decorative demo elements that should be hidden from assistive technology. Add `aria-hidden="true"` to each decorative element so screen readers skip them during the animation.</violation>
</file>

<file name="apps/mesh/src/web/components/sidebar/task-groups/team-threads-section.tsx">

<violation number="1" location="apps/mesh/src/web/components/sidebar/task-groups/team-threads-section.tsx:78">
P2: Users on touch/mobile can miss navigation to the full Threads monitor when team thread count is small, because the only fallback button is hidden behind `hasOverflow`. Consider rendering a visible in-list "See all" action even without overflow (or making the header action always visible on touch).</violation>
</file>

<file name="apps/mesh/src/web/demo/stage.tsx">

<violation number="1" location="apps/mesh/src/web/demo/stage.tsx:104">
P2: An empty `scenarios` input currently crashes the demo stage during render, even though the runner is written to no-op for empty arrays. This comes from the non-null assertion on `pinnedScenarios[0]` before reading `active.Stage`; adding a local null guard keeps behavior consistent and avoids a hard crash.</violation>
</file>

<file name="apps/mesh/src/web/demo/demo-chat-stream.tsx">

<violation number="1" location="apps/mesh/src/web/demo/demo-chat-stream.tsx:31">
P2: Chat message rendering can do extra work during the scripted animation because the provider `value` gets a new object identity on every parent render. Memoizing the context value by `state` keeps consumer re-renders tied to actual chat-store updates instead of unrelated shell updates.</violation>
</file>

<file name="apps/mesh/src/web/components/sidebar/task-groups/sidebar-section-header.tsx">

<violation number="1" location="apps/mesh/src/web/components/sidebar/task-groups/sidebar-section-header.tsx:28">
P2: Both buttons are missing the `focus-visible:ring-2 focus-visible:ring-ring/50` class that every other interactive element in the sidebar uses. Without it, keyboard users see no visible focus indicator — the outline is removed and no ring replaces it, breaking the established accessibility contract in this directory.</violation>
</file>

Tip: cubic can generate docs of your entire codebase and keep them up to date. Try it here.

Re-trigger cubic

// Let the layout mount before the first beat writes to its stores.
await director.wait(300);
await scenario.run(director);
} catch {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: Unexpected errors in scenario scripts silently cause a hang. The bare catch{} block swallows all errors, not just abort rejections. When a non-abort error (e.g., a TypeError in scenario code) is thrown inside scenario.run(), the catch falls through to markEnded() and then awaitReplay(), which waits indefinitely for a replay click that will never come in a broken state — the function's promise never resolves.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/mesh/src/web/demo/runner.ts, line 27:

<comment>Unexpected errors in scenario scripts silently cause a hang. The bare `catch{}` block swallows all errors, not just abort rejections. When a non-abort error (e.g., a TypeError in scenario code) is thrown inside `scenario.run()`, the catch falls through to `markEnded()` and then `awaitReplay()`, which waits indefinitely for a replay click that will never come in a broken state — the function's promise never resolves.</comment>

<file context>
@@ -0,0 +1,34 @@
+      // Let the layout mount before the first beat writes to its stores.
+      await director.wait(300);
+      await scenario.run(director);
+    } catch {
+      if (signal.aborted) return;
+    }
</file context>

</div>
</SidebarInset>
{!isMobile && (
<LibraryPanel

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: The LibraryPanel can render alongside the full /$org/files route content when the panel was previously left open. Since libraryOpen is persisted in localStorage, visiting /$org/files (via deep link or navigation) while the panel state is true shows two Library instances reading/writing the same ?path= and ?preview= URL params. Consider either closing the panel when the route is already /$org/files, or checking the current route to skip the panel render on that route.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/mesh/src/web/layouts/org-shell-layout/index.tsx, line 146:

<comment>The LibraryPanel can render alongside the full `/$org/files` route content when the panel was previously left open. Since `libraryOpen` is persisted in localStorage, visiting `/$org/files` (via deep link or navigation) while the panel state is `true` shows two Library instances reading/writing the same `?path=` and `?preview=` URL params. Consider either closing the panel when the route is already `/$org/files`, or checking the current route to skip the panel render on that route.</comment>

<file context>
@@ -125,7 +142,19 @@ export default function OrgShellLayout() {
                   </div>
                 </SidebarInset>
+                {!isMobile && (
+                  <LibraryPanel
+                    open={libraryOpen}
+                    onClose={() => setLibraryOpen(false)}
</file context>

role="button"
tabIndex={0}
onClick={() => onOpen(virtualMcpId)}
onKeyDown={(e) => {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Keyboard reordering can stop working in expanded agent rows because the row-level onKeyDown replaces dnd-kit’s sortable key handler. Composing sortable.listeners.onKeyDown before the open-on-Enter/Space logic keeps drag keyboard support and avoids regressing accessibility.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/mesh/src/web/components/sidebar/task-groups/agent-row.tsx, line 60:

<comment>Keyboard reordering can stop working in expanded agent rows because the row-level `onKeyDown` replaces dnd-kit’s sortable key handler. Composing `sortable.listeners.onKeyDown` before the open-on-Enter/Space logic keeps drag keyboard support and avoids regressing accessibility.</comment>

<file context>
@@ -0,0 +1,116 @@
+      role="button"
+      tabIndex={0}
+      onClick={() => onOpen(virtualMcpId)}
+      onKeyDown={(e) => {
+        if (e.target !== e.currentTarget) return;
+        if (e.key === "Enter" || e.key === " ") {
</file context>

showAgentIcon
/>
))}
{hasOverflow && (

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Users on touch/mobile can miss navigation to the full Threads monitor when team thread count is small, because the only fallback button is hidden behind hasOverflow. Consider rendering a visible in-list "See all" action even without overflow (or making the header action always visible on touch).

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/mesh/src/web/components/sidebar/task-groups/team-threads-section.tsx, line 78:

<comment>Users on touch/mobile can miss navigation to the full Threads monitor when team thread count is small, because the only fallback button is hidden behind `hasOverflow`. Consider rendering a visible in-list "See all" action even without overflow (or making the header action always visible on touch).</comment>

<file context>
@@ -0,0 +1,92 @@
+              showAgentIcon
+            />
+          ))}
+          {hasOverflow && (
+            <button
+              type="button"
</file context>

// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);

const active = pinnedScenarios[activeIndex] ?? pinnedScenarios[0]!;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: An empty scenarios input currently crashes the demo stage during render, even though the runner is written to no-op for empty arrays. This comes from the non-null assertion on pinnedScenarios[0] before reading active.Stage; adding a local null guard keeps behavior consistent and avoids a hard crash.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/mesh/src/web/demo/stage.tsx, line 104:

<comment>An empty `scenarios` input currently crashes the demo stage during render, even though the runner is written to no-op for empty arrays. This comes from the non-null assertion on `pinnedScenarios[0]` before reading `active.Stage`; adding a local null guard keeps behavior consistent and avoids a hard crash.</comment>

<file context>
@@ -0,0 +1,121 @@
+    // eslint-disable-next-line react-hooks/exhaustive-deps
+  }, []);
+
+  const active = pinnedScenarios[activeIndex] ?? pinnedScenarios[0]!;
+  const ActiveStage = active.Stage;
+
</file context>

}: PropsWithChildren<{ store: Store<DemoChatState> }>) {
const state = useSyncExternalStore(store.subscribe, store.get, store.get);

const value: ChatStreamContextValue = {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Chat message rendering can do extra work during the scripted animation because the provider value gets a new object identity on every parent render. Memoizing the context value by state keeps consumer re-renders tied to actual chat-store updates instead of unrelated shell updates.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/mesh/src/web/demo/demo-chat-stream.tsx, line 31:

<comment>Chat message rendering can do extra work during the scripted animation because the provider `value` gets a new object identity on every parent render. Memoizing the context value by `state` keeps consumer re-renders tied to actual chat-store updates instead of unrelated shell updates.</comment>

<file context>
@@ -0,0 +1,56 @@
+}: PropsWithChildren<{ store: Store<DemoChatState> }>) {
+  const state = useSyncExternalStore(store.subscribe, store.get, store.get);
+
+  const value: ChatStreamContextValue = {
+    messages: state.messages,
+    status: state.status,
</file context>

type="button"
aria-expanded={open}
onClick={onToggle}
className="flex flex-1 min-w-0 items-center gap-1 text-[10px] font-medium uppercase tracking-wider text-muted-foreground/80 hover:text-foreground transition-colors focus-visible:outline-none"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Both buttons are missing the focus-visible:ring-2 focus-visible:ring-ring/50 class that every other interactive element in the sidebar uses. Without it, keyboard users see no visible focus indicator — the outline is removed and no ring replaces it, breaking the established accessibility contract in this directory.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/mesh/src/web/components/sidebar/task-groups/sidebar-section-header.tsx, line 28:

<comment>Both buttons are missing the `focus-visible:ring-2 focus-visible:ring-ring/50` class that every other interactive element in the sidebar uses. Without it, keyboard users see no visible focus indicator — the outline is removed and no ring replaces it, breaking the established accessibility contract in this directory.</comment>

<file context>
@@ -0,0 +1,52 @@
+        type="button"
+        aria-expanded={open}
+        onClick={onToggle}
+        className="flex flex-1 min-w-0 items-center gap-1 text-[10px] font-medium uppercase tracking-wider text-muted-foreground/80 hover:text-foreground transition-colors focus-visible:outline-none"
+      >
+        {open ? (
</file context>

transition: "transform 200ms ease-out, opacity 200ms ease-out",
}}
/>
{/* pointer */}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P3: The cursor SVG and click ripple are purely decorative demo elements that should be hidden from assistive technology. Add aria-hidden="true" to each decorative element so screen readers skip them during the animation.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/mesh/src/web/demo/ghost-cursor.tsx, line 36:

<comment>The cursor SVG and click ripple are purely decorative demo elements that should be hidden from assistive technology. Add `aria-hidden="true"` to each decorative element so screen readers skip them during the animation.</comment>

<file context>
@@ -0,0 +1,57 @@
+          transition: "transform 200ms ease-out, opacity 200ms ease-out",
+        }}
+      />
+      {/* pointer */}
+      <svg
+        width="22"
</file context>

vibegui and others added 28 commits July 11, 2026 14:19
Add a public, fully-mocked walkthrough that drives the REAL Studio chat
components from a recorded stream — no backend, MCP transport, or auth.

Framework (apps/mesh/src/web/demo/):
- Director: a non-React class that owns all timing via @decocms/std sleep
  and mutates external Stores; components subscribe via useSyncExternalStore,
  so no useEffect is needed to animate. Screenplays read as a storyboard
  (await d.user/stream/think/tool/parallel/endTurn) and support multiple
  parallel chat tracks plus org switching, previews, a ghost cursor, and a
  typed terminal.
- Chat seam: DemoChatStreamProvider supplies a scripted ChatStreamContextValue
  (via an exported DemoChatStreamContext) so the real renderers paint it
  identically to a live stream.
- DemoProviders: mock ProjectContext + a network-free QueryClient.

Two demos, each on its own URL (/demo chooser, /demo/<id>), light mode:
- /demo/storefront — business user drops in a URL and gets an accurate
  perf+SEO diagnosis scorecard, a fix plan, parallel fixes, and a re-audit.
- /demo/agents — Studio as a web Conductor: ghost cursor opens the connect-
  desktop modal, a mocked iTerm runs `bunx decocms link` then minimizes to
  "connected", then work runs across two orgs in parallel (chat + live
  preview) while switching context.

The autoplay runner is guarded by a per-stores singleton with deferred
teardown so the dev runtime's spurious effect re-runs can't restart a
scenario mid-play.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Reworked the scripted demos for rhythm and a real ending.

- Pacing: deliberate beats with anticipation captions, reading holds after
  big content (diagnosis / result tables), staggered parallel fixes, and
  smoother typing speeds.
- Transitions: caption fade/slide, org-workspace crossfade on switch, preview
  iframe fade-in on change, and cursor-driven org switching (the ghost cursor
  glides to and clicks the org tab) instead of hard cuts.
- Ending: the demo no longer loops silently — after a full play-through it
  shows an end card ("Get started free" / "Watch again"). The runner plays
  once, then awaits a replay signal.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Storefront demo:
- The agent now proposes a real WORK PLAN inline in the chat — a sprint of
  task cards — and waits for the viewer to approve (ghost cursor clicks
  "Approve & start"). Tasks then tick live; the first deliverable is a
  pull-request card (checks → merge), then deploy + re-audit.
- Ends with a "get this every morning" card to connect Slack / Teams for a
  daily automated audit.
- Inline cards render via a tiny part-renderer registry
  (chat/message/parts/extra-part-renderers.ts) the demo registers into — the
  core chat renderer stays demo-agnostic (empty registry in normal use).

Agents demo:
- Replaced the top org tabs with a real Studio-style sidebar of agents. You
  switch agents from the sidebar; background work keeps running and a
  notification dot appears on an agent's icon when its task finishes.

Fix: card outputs are replaced immutably on each update so the compiler-memoized
cards actually re-render (in-place mutation was silently dropped).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…crumb, your-deco home, share-a-scope

One continuous scripted walkthrough of the simplified navigation model:
path = context. Breadcrumb (deco > org > agent) is the only nav and the
chat's scope selector; org cards report as agents; every level has a
Share that mints the MCP URL for exactly that scope.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…re beside breadcrumb

The stage now copies the real shell's vocabulary: bg-sidebar cream body,
card-shadow float cards, the home hero + capybara + corner art, the
composer's Tools/scope/model chrome, colored tile avatars, real deco
logo. Share moved from far-right to right beside the breadcrumb (it
shares THAT scope). Library panel: give the floating card its cream
backdrop + top breathing room so its rounded top isn't clipped.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Home goes back to the vertical right rail — each org card now carries
three metric squares (stat + delta, single-hue 12-point sparkline,
watched number that updates live when work ships). Org agents also list
directly under AGENTS in the home sidebar. Sparkline hues are the 700
steps of each org's hue, validated >=3:1 on the light surface.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Wider canvas, bigger presence: larger metric values, taller squares,
roomier cards. The orgs are the home's co-protagonist.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Home sidebar carries example my-threads; the org sidebar gets the full
Team threads / My threads / Agents structure with teammate attribution
— you can see what teammates are working on in each org, and the demo
points at it. The morning brief now ends with an inline 'Vela Store
needs you' card whose button navigates into the org, landing in a
thread where the pilot has already asked the question — you answer and
work proceeds. New generic showCard primitive on the Director.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…th status chips; breadcrumb icons per level

The guiding caption moves to the top (was lost over the composer),
grows, gains backdrop blur and a violet tint — it reads as the
narrator, not a product component. The org CTA card gets a real
app-UI body: status chips (Assets approved ✓ / QA passed ✓ / Hero not
shipped) beside the navigate button. Every breadcrumb level now
carries its own icon tile, not just the deco logo.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ecomes a sales question

Nobody asks their teammate to 'keep LCP under 1.5s' — and the site
shouldn't ship slow in the first place. Vela's pilot now audits before
deploying, catches the regression the new hero introduced, subtasks
Storefront Bot to fix it, then continues to deploy — the user only says
'ship it'. The agent-level zoom is now Store Ops with a question a
human actually asks: how were yesterday's sales? (orders/revenue/AOV
recap, drop converting at 2.1x).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…s, explorable end state

Every assistant turn now carries its speaker (avatar tile/logo + name)
via Track.setSender. Settings becomes what the model says it should be:
an AGENT — always the bottom one in / and /{org} — whose screens are
MCP apps rendered in the preview panel ('Make Rafa an admin' → tool
call → the Members app updates). No special settings screen: chat +
preview, everywhere. The end card is dismissible into explore mode —
crumbs, agent rows, org cards and Share are genuinely clickable on the
final state, with floating Replay/Get-started pills.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… affordances, narration trial, cadence fixes

Entering an org lands on its OPERATIONS dashboard (KPIs with anomaly
flags, diagnosed anomalies, agent-proposed opportunities, review queue
— after Rafael's mocks), not a site preview. Store Ops gets a sales
app; the pilot's follow-ups land on a tasks kanban; Settings' Members
app gains an invite box and 'Invite your team' pins to every sidebar.
The CTA card now lands WITH the digest (no dead wait). Cadence fixes:
the team-threads beat moves to after the ship (was an urgency-killing
detour, and its display:contents target sent the ghost cursor to the
top-left corner); Share moves to Store Ops (take THIS agent to
WhatsApp); explore-mode clicks now set the right per-agent preview —
fixes 'settings shows nothing'. Trial narration: two caption clips
(macOS say, Samantha Enhanced) with a mute toggle on the caption row.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…y default

The captions are now the narrator's script: 17 lines that lead the tale
(present tense, written for the ear) instead of labeling UI. Every line
has a Zoe (Premium) clip keyed by exact caption text. Narration is ON
by default — the first pointer/keydown anywhere unlocks autoplay and
re-speaks the line on screen; the toggle mutes.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The show now opens with a start card — 'Play with narration' / 'Watch
muted'. That click is the browser's audio unlock, so the FIRST line
narrates (autoplay policy made cold-start narration impossible). Esc
toggles a pausable clock: every Director/Track wait runs on chunked
unpaused-time sleep (~150ms response), the in-flight clip pauses with
it, and a pill says how to resume. Start/mute choices survive replays.
Clips regenerated at 176wpm (186 was too fast).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ts pill changes

Audited all 17 captions against clip durations: added a dashboard beat
so line 5 completes, extended the two settings holds. Line 4 is now
'One brief, three orgs — and exactly one thing that actually needs you.'

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…o fit

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
caption() is now duration-aware: it awaits the previous line's clip
window (from an exported VO_MS table) before replacing it, so no
narration is ever cropped — regardless of voice or rate. All visible
'org(s)' become 'team(s)' (narration, rail label, digest header, helper
text, end card). Clips regenerated with Isha @160wpm.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…2wpm

- Captions now behave like subtitles: auto-clear when their narration
  window ends (no more idle pills over long tool sequences) and a fixed
  700ms metronome gap before the next line. Verified with a
  MutationObserver run: 10/16 transitions at ~250ms pill-free, none over
  ~2.5s.
- Script pass: "your deco Studio", proactive morning line, pilot→agent
  everywhere, "team context" framing, positive triage line, closing
  "same intelligence, only the context changes".
- Every agent is a LOOP over goals: new minified SLO card (SLI, target,
  error budget, trend) in the Store Ops app; caption 11 says it.
- All 17 clips regenerated at Isha @182wpm; VO_MS from afinfo.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Replaces macOS `say` clips with Maya1 (Apache 2.0) generated on-device:
LM on MPS, sampling + SNAC decode on CPU (works around two PyTorch MPS
bugs). Voice designed by description; all 17 clips whisper-verified
word-for-word. VO_MS from sample-accurate wav lengths; final beat
extended so markEnded() no longer crops the closing line.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Line 5 loses "nothing to rebuild"; line 15 ("Setup is one person's
  job") removed entirely; logo line now "back home — to your agents".
- vo-09 re-taken (5.6s -> 4.1s, the slow read), vo-11 re-taken without
  the mid-phrase pause (and whisper-verified "loop", not "looped").
- Clips renumbered to vo-01..16; all takes whisper-verified; cadence
  re-measured end to end (every clip plays fully, ~250ms metronome).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Voice picked from a 3-way bake-off (Maya US female/male, Chatterbox).
"No settings screens anywhere" line removed; clips renumbered
vo-01..15, all whisper-verified (one slurred take re-rolled), beats
retimed to the shorter reads, cadence re-measured end to end.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- PreviewFrame gains an app switcher (top right): the current screen is
  explicitly ONE MCP app of this agent among many. The kanban beat now
  CLICKS the Tasks chip; chips are clickable in explore mode too.
- Kanban restyled to brand: column wells, white cards, brand chips,
  agent/human avatar rows.
- Org/agent level split is chat 40 / preview 60.
- Lines 1-2 re-taken cheerful (viewer-picked take B); line 1 is now
  "This is deco Studio — the home for your AI agents!"

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Plain `2fr` tracks have min-width:auto — the reasoning row's long
single line stretched the chat column over the preview.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- New line 6: "Top left is your context" — the breadcrumb gets a violet
  spotlight while it plays; the crumb is named as scope + agent selector.
- Share arc rebuilt: agent scope goes to "Claude Code, Codex, or
  anywhere"; a new beat walks UP the breadcrumb and shares the whole
  team as one MCP; back home, a new beat connects Claude to your deco
  ("all your teams at once"). Codex added to client chips.
- Kanban rebuilt with Studio's real tokens (warm neutrals, 6px radius,
  ring-shadow cards) and story-connected tasks: the Winter Drop ship
  sits in "Done · today", follow-ups mirror the sidebar team threads.
- Script is 18 lines; 4 new clips generated + whisper-verified (one
  "Cloud Code" mispronunciation re-rolled); cadence verified end to end.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
vibegui and others added 5 commits July 11, 2026 14:22
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Main added editQueuedMessage / removeLocalMessage / isSendInFlight to the
live chat-stream contract; the scripted demo stream is read-only, so stub
them (no queued-edit, no local removal, never in flight).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Introduce `deco / <org> / <agent>` breadcrumb in the org-shell toolbar:
- **deco** — logo, links to `/` (the cross-org MY-deco home, next commit).
- **org**  — opens the org switcher popover (reuses the org list UI, now
  extracted from account-popover into a shared `header/org-switcher`).
- **agent**— the active agent, resolved from `?virtualmcpid`, inside a thread.

The sidebar-footer AccountPopover keeps account/settings/logout; org
switching now lives "up" in the breadcrumb. Replaces the bare Toolbar.LogoLink
in the org shell (LogoLink still used by settings + sidebar logo header).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The root route is now the center of your work: every thread you own across
every org, as cards sorted by what needs you next (requires_action →
in_progress → … → done, then recency). No auto-redirect into your last org —
that's a click away via the breadcrumb or by opening any card.

- `useMyThreads` fans out COLLECTION_THREADS_LIST { created_by: "me" } per org
  over each org's self-MCP client (reused from cache), merges + sorts client
  side. Agents resolved per-org via COLLECTION_VIRTUAL_MCP_LIST so cards show
  the agent without org context. No new backend tool, no migration.
- Org-less shell (own header + trimmed AccountMenu; no ProjectContextProvider).
- Org filter chips, per-org error isolation, loading skeletons, empty state.
- homeRoute moves under rootRoute; only zero-org users bounce to /onboarding.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Each card now reads "what's happening here" from the thread's actual latest
activity, not just its status:
- Instant heuristic: the manager-oriented status verb (kept as a prefix).
- Content summary: the latest agent message (or in-flight tool), pulled from
  COLLECTION_THREAD_MESSAGES_LIST over the org's self-MCP, cached per
  (threadId, updated_at). Fetched only for active threads so completed work
  doesn't fan out a message read.

useThreadAnalysis is written with an explicit LLM-upgrade seam: swap the
extractive summarizeMessages for a THREAD_ANALYZE backend tool (LLM_DO_GENERATE
+ a model from AI_PROVIDERS_LIST_MODELS) and the hook shape/caching are unchanged.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@vibegui vibegui force-pushed the vibegui/turtles-demo branch from f00259e to 8c41cca Compare July 11, 2026 19:15
…deco

When you have no threads, MY deco now shows a chatbox instead of a dead-end.
Pick an org (chips when you're in several), type, submit → it creates a fresh
thread in that org and sends your message.

Reuses the app's new-thread handoff verbatim: write the message to
sessionStorage keyed by (locator, taskId) via writeStoredAutosend, then
navigate to /$org/$taskId?autosend=true. The destination route's useEnsureTask
creates the thread and the autosend consumer fires the first message — so no
org-scoped context or ThreadManager is needed on the org-less home. Top filter
chips now hide until there's something to filter.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@vibegui

vibegui commented Jul 12, 2026

Copy link
Copy Markdown
Contributor Author

Superseding this with focused PRs (breadcrumb in #4473). Closing the demo PR.

@vibegui vibegui closed this Jul 12, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant