Skip to content

Bundles: capability-gated one-click package stacks (both shells)#146

Merged
msitarzewski merged 9 commits into
mainfrom
feat/bundles
Jul 14, 2026
Merged

Bundles: capability-gated one-click package stacks (both shells)#146
msitarzewski merged 9 commits into
mainfrom
feat/bundles

Conversation

@msitarzewski

Copy link
Copy Markdown
Owner

Bundles — capability-gated one-click package stacks

Adds a Bundles section: curated one-click Homebrew stacks with post-install setup guidance, capability-gated by a zero-install system profile (RAM/arch/GPU/disk) so an 8 GB Mac isn't told to install a local-LLM stack it can't run. Both shells (Tauri + native Swift), data-contract parity. Recipes are validated JSON anyone can PR against a published contract.

The 9 bundles (every token brew info-verified, zero third-party taps)

Bundle Packages
Local LLMs ollama · open-webui
Image Generation draw-things · comfy
Graphics & Design inkscape · gimp · krita
Media Toolkit ffmpeg · yt-dlp · mpv · handbrake
Web Dev Starter node · pnpm · git · gh
Local Databases postgresql@16 · redis · tableplus · dbeaver-community
Agentic Web Dev opencode · zed · node · pnpm · git · caddy · orbstack
LAMP Stack httpd · mysql · php
LEMP Stack nginx · mariadb · php

UI

  • List + Details pane matching Library/Trending (master list left, right-side Details pane; no auto-select, ✕/section-switch closes). Tauri = shell-level resizable aside; native = stock .inspector.
  • Intent paragraph (description) under each tagline; readiness shown as a single header pill + a color-coded reason callout only when Marginal/Not-recommended.
  • Clickable inline package descriptions (lazy catalog/brew-info, cached).
  • Per-package Install — "Not installed" is an inline Install action (single package, streamed like "Install all", keeps the pane open); brew-native setup steps run in-app, external commands are copy-only.

Contract & contributions

  • One JSON file per recipe (recipes/*.json) against recipe.schema.json, a dependency-free validator (scripts/validate-recipes.mjs, brew resolution + tap-trust + link + description checks), deterministic bundles.json (3 synced copies), CI on recipe PRs, and CONTRIBUTING-bundles.md. Community can add Rails/Django/etc. without a maintainer code change.

Testing

Recipes 9/9 valid · native swift build + 195 tests · Rust bundle tests 9/9 (incl. a description frontend-round-trip guard) · Tauri svelte-check 0 / vitest 57.

Notes for reviewers

  • Tauri serves bundles through the Rust backend, so recipe fields must also live on the Rust Bundle struct (src-tauri/src/types.rs) or serde drops them frontend-bound — guarded by a test.
  • Live-refresh client ships fail-soft (no-op until <host>/bundles/bundles.json is served — a release-publish step).
  • BREWBROWSER_FAKE_RAM_GB is a debug-only readiness override; committed recipe data is real.

🤖 Generated with Claude Code

https://claude.ai/code/session_01X9iMFjHE21ePTjcbHpTXt6

msitarzewski and others added 9 commits July 12, 2026 19:22
…, baselines, M1–M5)

memory-bank/releases/0.7.0/: release tracker + the full Bundles plan — the
contributor recipe-contract, July-2026 capability-baselines (verified links),
and five milestone build docs (m1 capability engine → m5 live-refresh +
contributions). Wired into toc.md.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01X9iMFjHE21ePTjcbHpTXt6
… shells)

Zero-install system profile (RAM/arch/chip/cores/gpu/free-disk/os) + a pure
readiness(requires, capabilityNotes, profile) -> {verdict, reason} in Rust/TS
AND Swift. Both implementations are verified against ONE shared parity fixture
(test-fixtures/readiness-cases.json) that pins exact verdicts + reason strings,
so the two can't drift. BREWBROWSER_FAKE_RAM_GB override for testing gated states.

Tauri: src-tauri/src/system/profile.rs + system_profile command; src/lib/util/
readiness.ts + vitest (10/10). Native: SystemProfile.swift + BundleReadiness.swift
+ Swift Testing (10/10). cargo profile 3, svelte-check 0, swift build clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01X9iMFjHE21ePTjcbHpTXt6
…alidator

The contributor-facing half of Bundles: recipes/recipe.schema.json + one JSON
file per recipe (local-llm, image-gen, graphics, media, web-dev, databases) with
verified tokens/taps (all homebrew/core|cask) and July-2026 capability requires.
scripts/validate-recipes.mjs enforces the contract (structure + `brew info`
resolution + tap-trust rule + external-command rule + link reachability) and
concatenates passing recipes into bundles.json. 6/6 valid incl. --strict-links.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01X9iMFjHE21ePTjcbHpTXt6
…th shells)

The Bundles app feature, in parity across Tauri and native:

- M2 loaders: tolerant Bundle parsers (Rust serde / Swift Codable — the native
  model is `BrewBundle` to avoid shadowing Foundation.Bundle), each embedding
  its own bundles.json copy (Tauri include_str! / native Bundle.module).
- M3 browse + install: a "Bundles" nav section (⌘7), a card grid with
  capability ReadinessPill (Ready/Marginal/Not-recommended), a detail view
  with live per-package installed state, caveats, links, and "Install all".
  Install streams into Activity as per-kind groups (brew rejects interleaved
  --formula/--cask, verified via --dry-run on both shells).
- M4 setup guidance: a post-install checklist — service/open/reveal run in-app;
  external `command` steps are copy-only ("you run this"), never executed.

Readiness is computed client-side against the M1 SystemProfile; a fake-RAM
override surfaces gated states on high-RAM machines.

Tauri cargo 681, svelte-check 0, vitest 53. Native 187 tests. All green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01X9iMFjHE21ePTjcbHpTXt6
- .github/workflows/validate-recipes.yml: runs the validator (--strict-links,
  brew resolution) on every PR touching recipes/, and diff-checks all three
  bundles.json copies are in sync.
- CONTRIBUTING-bundles.md: the contributor onramp (copy a recipe, validate
  locally, PR — CI decides mergeability, no app code change).
- validate-recipes.mjs now writes all three bundles.json copies (root +
  each app's bundled copy) deterministically so they can't drift.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01X9iMFjHE21ePTjcbHpTXt6
…erred

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01X9iMFjHE21ePTjcbHpTXt6
Opt-in refresh of the bundle list from the public first-party host
(brew-browser.zerologic.com/bundles/bundles.json — same host + pattern as
live enrichment/trending, distinct /bundles/ path), so new/updated recipes
arrive without an app update.

- Native BundleLiveService (mirrors EnrichmentLiveService) + Tauri
  bundles_live command (mirrors enrichment/live.rs); reuses the M2 tolerant
  Bundle decode. Fail-soft: schemaVersion > supported OR malformed → keep
  the bundled copy; an empty payload can never wipe the shipped 6.
- Shared settings.json key `liveBundlesEnabled` added to BOTH shells
  (native SettingsDTO + Rust Settings, camelCase-matched); gated on the
  opt-in AND network (offline off), NOT AI-gated. New Settings→Network
  toggle in each shell.
- Store wiring is non-blocking (fire-and-forget) so a slow/absent endpoint
  never delays the cards; replaces only on non-empty success.

Client is unit-tested (schema-skew fail-soft, empty-guard, opt-in gating);
the live 200 path activates once the host serves the endpoint. Tauri cargo
686, svelte 0, vitest. Native 192 tests.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01X9iMFjHE21ePTjcbHpTXt6
…endpoint remains

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01X9iMFjHE21ePTjcbHpTXt6
…install

Move Bundles from a card grid + modal to the app's canonical master-list +
right-side Details pane (matches Library/Trending: no auto-select, pane
closed on entry, close on section switch). Tauri = shell-level resizable
aside reusing detailPaneWidth; native = the stock .inspector. Both shells.

Recipes 6 -> 9 (every token brew-info-verified, zero third-party taps):
add Agentic Web Dev (opencode/zed/node/pnpm/git/caddy/orbstack), LAMP
(httpd/mysql/php), LEMP (nginx/mariadb/php); expand Image Gen (+draw-things),
Media (+handbrake), Web Dev Starter (+git/gh), Local Databases
(+tableplus/dbeaver-community). opencode is now first-class in homebrew/core.

- New `description` intent field in the recipe contract (schema + validator +
  contract/CONTRIBUTING docs), rendered under the tagline. Added to the Rust
  Bundle struct (src-tauri/src/types.rs) so it survives the frontend
  round-trip that serves bundles through Rust; regression test added.
- Readiness dedup: the header pill is the sole verdict; the body shows a
  color-coded reason callout only for marginal/blocked (kills "Ready / Ready.").
- Clickable inline package descriptions (lazy catalog/brew-info lookup, cached,
  multiple-open).
- Per-package Install action for not-installed packages (reuses the streamed
  install path, keeps the pane open, row flips on success).
- Bundle icons: add server (LAMP/LEMP) + agentic (fixes a Tauri parity gap).
- Bundle-count guards bumped to 9 (native + Rust).

Gate: recipes 9/9 - native swift build + 195 tests - Rust bundle tests 9/9 -
Tauri svelte-check 0 / vitest 57.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01X9iMFjHE21ePTjcbHpTXt6
@msitarzewski
msitarzewski merged commit a23375c into main Jul 14, 2026
1 check passed
msitarzewski added a commit to cseelye/brew-browser that referenced this pull request Jul 14, 2026
Reconcile with the Bundles feature (msitarzewski#146), which merged first and added a
second ResizeHandle usage (the bundle-detail pane). This PR generalizes
ResizeHandle (width -> size, adds orientation), so the new bundle handle in
src/routes/+page.svelte is updated to the renamed size/defaultSize props.
Everything else auto-merged cleanly.

Gate on the merged tree: svelte-check 0 errors, vitest 57, native swift build
+ 199 tests (incl. the new ActivityDrawer sizing tests). Thanks @cseelye for
the clean, parity-respecting resizable Activity console.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01X9iMFjHE21ePTjcbHpTXt6
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