Skip to content

ship 2026-05-28: bb Apps subsystem (Phases 1–3 + follow-ups)#56

Merged
SawyerHood merged 13 commits into
mainfrom
sawyer/ship-2026-05-28
May 28, 2026
Merged

ship 2026-05-28: bb Apps subsystem (Phases 1–3 + follow-ups)#56
SawyerHood merged 13 commits into
mainfrom
sawyer/ship-2026-05-28

Conversation

@SawyerHood
Copy link
Copy Markdown
Collaborator

@SawyerHood SawyerHood commented May 28, 2026

What this ships

The bb Apps subsystem (three phases + follow-ups) lands on main. A thread now exposes one or more apps in its secondary panel instead of the legacy STATUS surface. The bundled status app replaces STATUS.html + STATUS-data/. Includes a new bb app CLI, a bb-styled blank scaffold (now also the default for fresh managers), a Create App… tile that prefills the composer, the migration guide for old threads, and hardening so a single malformed manifest can't take down the whole launcher.

Architecture report (local, not in repo): /Users/sawyerhood/.bb/thread-storage/thr_mpi7fmdbse/reports/apps-system-architecture.md.

Commits in this batch (12)

Commit What
8defe97b Apps Phase 1 — storage layout, daemon file serve + data watcher, server routes, window.bb bridge injection, bundled status app, bb app CLI. STATUS still in place.
03568757 Apps Phase 2 — app-tab kind in secondary panel, pinned status tab, "+" New Tab launcher with unified apps + files search, HTML-vs-Markdown entry branching. STATUS UI intact.
f3e7c92a Apps Phase 3 — full STATUS teardown (server routes, daemon status commands, frontend manager-status surface, all STATUS-specific tests). HOST_DAEMON_PROTOCOL_VERSION → 26. Prompt/guide rewrite: bb-guide-status-statebb-guide-app (with bb guide styling folded in).
3b414eef Migration guide at docs/migrating-status-to-apps.md.
220c7346 Flat URL canonicalization — dropped /assets/* URL segment (no back-compat alias), added /api/v1/* JSON-404 guard, distinguished app_not_provisioned from ENOENT.
251921fb Always-overlay seedseedManagerThreadStorage no longer early-returns after a user-template copy; bundled apps/status is always overlaid (user files win via flag:"wx").
5af2941c Create App… button in the New Tab launcher; prefills the composer via usePromptDraftStorage.
f7ff5581 New Tab launcher polish + prompt rework (no placeholders, cursor at end).
b5967795 bb app new --template blank scaffolds a bb-styled HTML surface (Inter + Fira Code + oklch tokens + dark mode + .panel) with the placeholder "Ask your agent to customize the status app how you please." Shared escapeHtmlText helper extracted into @bb/domain.
08bf1c86 New Tab visual pass — unified launcher tile rhythm + hidden redundant id captions.
2c9d2866 bb-styled generic default status app + scaffold dedup — replaced the hardcoded PR/worker default status with the generic bb-styled scaffold via a new shared buildBlankAppIndexHtml({ name }) in blank-app-scaffold.ts. bb app new --template blank, --template status, AND the bundled manager-template overlay all go through the same generator now.
4e69ce19 Resilience: single invalid manifest no longer takes down the whole apps list. List route logs + skips invalid manifests; detail/serve routes surface a new invalid_manifest 422 error code.

QA checklist

Apps core

  • Open the secondary panel → see pinned Status tab + "+" for New Tab.
  • Click + → New Tab launcher renders: search input, APPS section header, app rows, Create App tile at end.
  • App rows have uniform icon tiles (Pet / Status / Create App all the same size-9 frame; only the asset inside varies).
  • Redundant pet / status id captions hidden under app names; would re-appear if id ≠ slugified name.

Default status (NEW look)

  • Fresh manager → Status tab renders the bb-styled generic dashboard: header chip + "Status" title, single .panel with placeholder "Ask your agent to customize the status app how you please.", "Example tasks" sample section with two row shapes. NOT the old PRs / Workers / "Message the manager" UI.
  • Existing managers' Status tab still renders (this manager's per-task task_*.json shape is supported but intentionally drifted from the bundled state.json default).

Manager-template overlay

  • Manager with a user-authored ~/.bb/manager-templates/<name>/ template ALSO gets apps/status/ overlaid — add a custom file to your template dir, make a fresh manager, confirm BOTH the custom file AND apps/status/ are present.

URL contract

  • App URLs are flat: curl -i http://127.0.0.1:38886/api/v1/threads/<id>/apps/status/index.html → 200.
  • Nested asset URLs work: try a nested asset → 200.
  • Missing asset → JSON 404 (not the bb SPA shell HTML, not strict-MIME text/html).
  • Missing manifest → app_not_provisioned JSON error (not raw ENOENT).
  • Logo: GET /api/v1/threads/<id>/apps/pet/icon serves logo.svg when present.

Apps list resilience (NEW)

  • Drop a deliberately broken manifest in a thread (e.g. apps/broken/manifest.json with "icon": "NotAnIcon"). GET /threads/<id>/apps200, returns the valid apps, skips the broken one. Check server logs for the structured WARN.
  • Directly hit the broken app's URL: GET /threads/<id>/apps/broken/index.html422 with { code: "invalid_manifest", message: "..." } (NOT bb SPA shell, NOT raw 500).
  • Detail endpoint for the broken app also returns invalid_manifest 422.

window.bb bridge

  • HTML app: window.bb.appId + window.bb.data + window.bb.message present (gated by capabilities in manifest).
  • Markdown app (entry: "index.md", e.g. the Markdown Test I just dropped at apps/mdtest/): window.bb is NOT injected. Open /api/v1/threads/<id>/apps/mdtest/index.md (or the Markdown Test tab) and verify headings / code blocks / tables / task lists / nested blockquotes / footnotes / <details> all render.
  • window.bb.data.onChange("", cb) replays existing matches once on subscribe, then streams.
  • Browser window.bb.data.write(path, value) persists and broadcasts to other open viewers.
  • Agent direct-fs write to apps/<id>/data/state.json (atomic temp+rename) ALSO broadcasts — open the app, write the file from a shell, watch the UI update.
  • window.bb.message("text") posts a message to the parent thread.

bb app CLI

  • bb app list --self shows the seeded status app.
  • bb app new "Foo" --id foo --template blank --self scaffolds an app; serves at the flat URL; renders bb tokens + dark mode; placeholder "Ask your agent to customize the status app how you please." visible.
  • XSS-safe: bb app new '<script>alert(1)</script>' --id xss --template blank --self — open the served URL; verify <script> does NOT execute (escaped as &lt;script&gt;).
  • bb app new "Bar" --id bar --template status --self scaffolds the status-template variant (now identical to blank, just with icon: ListTodo in the manifest).
  • bb app open foo --self prints the URL.
  • bb app rm foo --self --yes removes the app from disk.

New Tab launcher / Create App… tile

  • Click Create App… → composer draft replaced with the prefilled template (cursor at end of text after What I want:).
  • If composer had a draft, browser confirm dialog appears first.
  • Cancel the confirm → existing draft text and any attachments preserved.
  • Accept the confirm → text replaced AND attachments cleared (regression check on setDraft vs old setText).
  • Composer focuses after click; cursor at end.
  • Secondary panel closes / returns to thread after the prefill so the composer is visible.
  • Keyboard navigation (arrows, Enter) in the launcher still works.
  • Visible focus ring on tiles.

Legacy STATUS gone

  • GET /api/v1/threads/<id>/status/... → 404 (route removed).
  • GET /api/v1/threads/<id>/status-data → 404 (route removed).
  • window.bbStatusState / window.bbThreadTell NOT present in any served app.
  • Old STATUS.html / STATUS.md / STATUS-data/ files in existing thread storages are simply ignored.

Migration guide

  • docs/migrating-status-to-apps.md reads cleanly and is accurate after the post-ship corrections (flat URL contract, assetsDir: "" Vite hint, etc.).

Protocol / desktop

  • Desktop app reconnects to host daemon cleanly (protocol version 26).
  • Auto-update flow still works.
  • No regressions in unrelated panels (Terminal, HTML preview, Logs viewer, etc.).

Quality / regressions

  • pnpm exec turbo run typecheck — green across the board.
  • pnpm exec turbo run test — green across the board (or any remaining failures are pre-existing sandbox-permission flakes, not regressions).
  • Light + dark mode both look right across the new New Tab UI.
  • Existing thread-detail flows unchanged (composition, drag, scroll).

Notes

  • Standing autonomy / merge style: per PREFERENCES.md, sawyer-next auto-merges on review APPROVE; this PR is merged to main with Rebase and merge (one commit per worker-batch).
  • The architecture report covers the design decisions and known limitations in depth — read that before sign-off if you want the why behind any of the choices.

🤖 Co-authored by Claude Opus 4.7 + bb manager thr_mpi7fmdbse.

SawyerHood and others added 12 commits May 28, 2026 14:31
…s + status app + bb app CLI

Per-thread Apps system foundation (no frontend yet): apps/<id>/ layout
(manifest.json + served assets/ + file-based data/ + logo.*), daemon-owned
asset serving + data/ watcher, server routes (entry/assets/icon/data CRUD/
message + WS relay on thread:<id>:app:<id>:data), injected window.bb bridge
(capability-gated, advisory for v1), default seeded `status` app, and
bb app new|list|open|rm. STATUS remains in place; teardown is Phase 3.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ch + app tabs

Frontend for the Apps system (additive; STATUS UI left intact for Phase 3):
- App tab kind + pinned `status` app tab in the secondary panel
- "+" launcher listing the thread's apps with resolved icons
- Unified app + file search (Apps + Files sections)
- App tab content branches on entry kind: HTML → iframe + injected window.bb,
  Markdown → static react-markdown
- Live data updates via window.bb.data.onChange (with replay/unsubscribe
  lifecycle guard), shared markdown asset URL transform, centralized STATUS_APP_ID

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Removes the legacy STATUS system end to end (no backwards compat) and makes
the Apps system the only path:
- Delete STATUS server routes/injection/status-data/status-version, the WS hub
  status method, the daemon status commands (HOST_DAEMON_PROTOCOL_VERSION→26),
  the frontend manager-status surface + useThreadStatusVersion, and all
  STATUS-specific tests.
- Rewrite manager/thread system prompts + instructions to the apps model
  (apps/status, data/state.json, window.bb, bb app CLI).
- bb-guide-status-state → bb-guide-app (bb guide app); fold bb guide styling
  into it; regenerate templates. New managers seed only the `status` app.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Guide for migrating an existing manager/thread off the removed STATUS surface
(STATUS.html/.md/STATUS folder + STATUS-data) to a `status` app: target layout,
manifest, state consolidation into data/state.json, the bbStatusState→window.bb
and bbThreadTell→window.bb.message API mapping, agent/maintainer direct-fs
writes, and cleanup. Points to `bb guide app` for the full reference.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
… on unmatched

Replace `/api/v1/threads/:id/apps/:appId/assets/*` with the canonical flat
`/api/v1/threads/:id/apps/:appId/*` wildcard → filesystem
`apps/<appId>/assets/<*>`. The `assets/` directory is an internal storage
detail; URLs no longer expose it. Nested paths preserved.

Add an `/api/v1/*` JSON-404 guard so unmatched API URLs return JSON 404
instead of falling through to the bb SPA shell HTML.

Improve the fresh-manager missing-manifest path to return `app_not_provisioned`
with a rebuild hint (root cause was stale binaries, not a real seeding bug).

Update default status template HTML, file-content-urls, migration guide,
bb guide app, tests, and regenerated templates to flat URLs only. No
back-compat alias kept — apps system isn't shipped yet.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
seedManagerThreadStorage previously early-returned after copying from
a user-authored manager template, skipping the bundled apps/status
fallback entirely. Fresh managers with any user template on disk would
404 on apps/status because the bundled manifest never got seeded.

Now the bundled set (apps/status/{manifest.json, assets/index.html,
data/state.json}) is always overlaid on top of any user-template copy,
regardless of which template is active. User-authored files still win
because they are copied first and the bundled overlay uses
writeFile(..., flag: "wx") which refuses to overwrite. Missing
non-default templates still warn but no longer skip the seed.

Tests refactored around expectBundledStatusAppSeeded() helper; added
"user-authored files win over the bundled overlay at the same path"
case asserting both behaviors.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Full-width button below the search input in the secondary-panel New
Tab. Clicking it prefills the thread composer's draft via
usePromptDraftStorage.setDraft (text + clears attachments — full
replacement, not just text), focuses the composer with the [NAME]
placeholder selected, and dismisses the New Tab panel.

Replacing a non-empty draft prompts a browser confirm; cancel
preserves the existing draft (text + attachments), accept replaces
both. Reuses the existing prompt-draft store; no parallel state.
Plumbs a stable textareaId from ThreadDetailPromptArea through
FollowUpPromptBox so the button can focus the composer reliably.

Prefill template references `bb guide app`, the apps/<id>/
{manifest.json, assets/index.html, data/state.json} layout, and the
window.bb.data / window.bb.message browser APIs.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Visual: bigger app rows with rounded tile-style icon container, name
+ id caption, uppercase tracking-wider section headers ("APPS",
"FILES"), token-driven hover/active/focus states that work in dark
mode. Create App moves from a full-width outline button below the
search to a dotted "+ Create App" tile at the end of the Apps list,
mirroring app rows with a "Describe an idea, the manager builds it"
hint line. The Apps section now always renders so the tile is
reachable immediately on open.

Prompt: prefilled template drops the [NAME] / [DESCRIBE…] placeholders
in favor of an end-of-text cursor landing. All apps-system context
lives at the top (manifest layout, window.bb usage), with a single
line pointing at `bb app new` for default styling, and a trailing
`What I want:` blank line where the user starts typing. Composer
cursor parks at end of text — no placeholder hunt.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The blank template now scaffolds a bb-native HTML surface instead of
a barebones one-line demo. New apps get the bb default styling head
verbatim from `bb guide styling` (Google Fonts links for Inter +
Fira Code, the full `:root` oklch token block, dark-mode overrides,
body / .panel defaults), with scaffold-only classes (header chip,
title, timestamp slot, section title, task-list rows, pills) layered
strictly after the guide block. Visual structure mirrors the status
task-list dashboard at a stripped-down plain-HTML scale, with a
placeholder line — "Ask your agent to customize the status app how
you please." — inviting the user to ask their agent to build on top.

Name interpolation goes through a shared escapeHtmlText helper now
extracted into @bb/domain and reused from the previous module-local
copies in apps/desktop (local-view, log-viewer). XSS test covers
`<script>`, `&`, `"`, and `'` in the app name.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Three small visual fixes to the secondary-panel New Tab launcher:

1. Unified the icon containers across Pet / Status / Create App tiles
   via shared LAUNCHER_TILE_ICON_CLASS constants. All three now share
   a size-9 raised frame with a size-5 glyph; the only intentional
   variant is the dashed inner border on Create App. The frame stays
   constant, only the asset varies.

2. Hide redundant id captions when the app id matches a slugified
   display name. `pet` / `status` captions disappear; when the id
   genuinely differs from the name, the caption renders in font-mono
   so it visibly reads as a developer identifier.

3. Drop the duplicate `title` tooltip on Create App (body hint stays)
   and the dead `disabled` prop on CreateAppTile.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The default status app shipped to fresh managers was hard-coded to a
PR/worker-review workflow (PULL REQUESTS + WORKERS + "Message the
manager" sections, a `pr` landing-mode chip). That assumed one
specific routine; a brand-new bb user has neither. Replace with a
bb-styled generic starting-point dashboard that matches the
`bb app new --template blank` output and invites customization:

  "Ask your agent to customize the status app how you please."

Mechanism: extract buildBlankAppIndexHtml({ name }) into a new
apps/server/src/services/threads/blank-app-scaffold.ts. Both
`bb app new --template blank` and `--template status` paths plus the
bundled manager-template overlay now generate index.html through one
generator (only the manifest icon differs). Delete the static
default-template/apps/status/{assets/index.html, data/state.json}
that pinned the old PR-review content.

User-authored manager templates still win over the bundled seed via
the existing flag:"wx" overlay semantics.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
A single invalid app manifest in a thread's apps/ directory used to
fail the entire GET /api/v1/threads/<id>/apps list response with 422,
taking down the whole secondary-panel launcher.

The list route now shares a single readAppManifest parse step with
the detail and serve routes; zod validation failures on an individual
manifest log a structured warning (appId, manifestPath, summarized
issue list at WARN level, full issues at debug) and that app is
skipped in the list response. Detail and serve routes still surface
the failure as a new invalid_manifest 422 error code (added to
@bb/server-contract errors) with a one-line message — full zod issues
stay in the server log, not on the wire.

Tests cover: partial-success list (valid + invalid → 200 with only
the valid summary + logger warned); detail returns invalid_manifest
422; serve returns invalid_manifest 422 (not the SPA shell
fallthrough).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@SawyerHood SawyerHood force-pushed the sawyer/ship-2026-05-28 branch from 4e69ce1 to 49ae445 Compare May 28, 2026 21:35
useCallback dep list had `promptDraft.getCurrent` / `promptDraft.setDraft`
broken out; react-hooks/exhaustive-deps wants `promptDraft` itself.
Replaced both with the parent object.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@SawyerHood SawyerHood merged commit 458b2a1 into main May 28, 2026
6 checks passed
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