Commit graph

197 commits

Author SHA1 Message Date
Bryan Helmkamp
999f2a11c3 refactor(fabro-web): stage 6.6 rewrite workflowData literal to v2 shape
The hardcoded sample workflow entries in `workflow-detail.tsx` still
embedded the legacy flat `RunSettings` shape (top-level `llm`, `vars`,
`sandbox`, `setup`) — a visible mismatch with what the server now
returns on `/api/v1/runs/:id/settings`.

Rewrites the four static literals (fix_build, implement, sync_drift,
expand) to mirror the v2 `SettingsFile` tree: `_version`, `run.goal`,
`run.inputs`, `run.model`, `run.sandbox`, `run.prepare.steps`,
`run.prepare.timeout`, etc. Duration and size fields now use the
human-readable forms (`"120s"`, `"8GB"`, `"10GB"`) per R83 / R84.

Adds a module-level doc comment pointing readers at the
`fabro_types::settings::SettingsFile` Rust type as the source of truth
for the shape. `RunSettings` stays as `Record<string, unknown>`, so
the literal typechecks without needing a formal type assertion on
each entry.

fabro-web `typecheck` / `test` / `build` stay green.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 17:18:34 -04:00
Bryan Helmkamp
78c57d585c refactor(api): stage 6.6 collapse settings DTOs to freeform v2 shape
Replaces the legacy flat `ServerSettings` / `RunSettings` schemas in
`docs/api-reference/fabro-api.yaml` and 20+ supporting nested type
schemas (LlmSettings, SandboxSettings, HookDefinition, WebSettings,
ApiSettings, GitSettings, McpServerEntry, etc.) with two simple
`type: object, additionalProperties: true` schemas that declare the
wire shape as the v2 `SettingsFile` tree with secret-bearing subtrees
dropped before serialization.

Regenerates the Rust progenitor and TypeScript Axios clients against
the new spec. The progenitor generates `RunSettings` / `ServerSettings`
as `#[serde(transparent)]` newtypes over `serde_json::Map<String,
Value>`; the openapi-generator emits `{ [key: string]: any; }` inlined
into the API method signatures and no longer exports named model
types.

Updates fabro-web to define local `type ServerSettings =
Record<string, unknown>` / `type RunSettings = Record<string,
unknown>` aliases since the generated client no longer exports them.
The UI only `JSON.stringify`s these payloads into a CollapsibleFile,
so the opaque shape is fine.

All 3,756 workspace tests remain green. The OpenAPI conformance test
`server_settings_keys_match_openapi_spec` still passes because
`compare_schema` short-circuits on pure-map schemas (no `properties`);
it becomes a no-op that will be removed entirely when Stage 6.3b
deletes the legacy flat `Settings` struct it still builds.

Unblocks the server handler + CLI migration in the next commits of
Stage 6.6.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 17:04:33 -04:00
Bryan Helmkamp
5003fb5c2e fix(fabro-web): restore local watch rebuilds
Replace the unsupported Bun.watch call in the SPA build script with
node:fs.watch so `bun run dev` keeps running in local development.
Add a regression test that verifies watch mode stays alive until
interrupted.
2026-04-08 15:27:27 -04:00
Bryan Helmkamp
8726065fd1 feat: embed fabro spa and align interruption semantics
Move the built web bundle into an embedded fabro-spa crate so Cargo and
release builds no longer depend on Bun at build time, and preserve the
local dev override path for fast UI iteration.

At the same time, rename interview and agent-level aborted flows to
interrupted, keep cancelled for run-level shutdown, and stop reporting
skipped answers as interruptions in the run event stream.
2026-04-08 14:43:44 -04:00
Bryan Helmkamp
4c1addbc5c fix(fabro-web): default unsaved theme selection to dark
Remove the system color-scheme fallback from the web UI theme boot path.
Fabro now uses a saved light/dark preference when present and otherwise
starts in dark mode by default. Add a regression test for the shared
theme selection helper and refresh the built web assets.
2026-04-08 12:10:34 -04:00
Bryan Helmkamp
31d80373d4 fix: session cookie decryption and add HTTP endpoint logging
Cookie auth was broken because parse_cookie_header used Cookie::parse
which does not percent-decode values. The cookie crate's private jar
percent-encodes on Set-Cookie but Cookie::parse leaves %2F/%3D intact,
making base64 decryption fail silently. Switch to Cookie::parse_encoded.

Also:
- Add tower-http TraceLayer for request/response logging (DEBUG for
  requests, INFO for responses with status and latency)
- Add structured tracing to all web_auth handlers per logging strategy
- Replace eprintln debug calls with tracing::warn
- Update GitHub App manifest homepage URL to https://fabro.sh

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 11:46:30 -04:00
Bryan Helmkamp
5e2d125cd0 fix: GitHub App setup flow — nullable webhook_secret, double POST, stale restart copy, wrong port
- Change webhook_secret to Option<String> in GitHubManifestConversion since
  GitHub's API returns null when no webhook URL is configured
- Use useRef guard to prevent React StrictMode from firing the one-time
  manifest conversion POST twice
- Remove fake "restart required" flow — server reads auth config lazily so
  no restart is needed after setup
- Derive web.url and api.base_url from the request Origin header instead of
  hardcoding port 3000
- Add error logging for manifest conversion parse failures

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 08:34:39 -04:00
Bryan Helmkamp
da88a47595 feat: wire web UI to real server with demo mode toggle
Server changes:
- Add /boards/runs to demo routes (delegates to list_runs)
- Fix demo get_run_status to return StoreRunSummary shape matching OpenAPI spec
- Enrich real /boards/runs to return RunListItem shape with board column mapping
  (Running->working, Paused->pending, Completed->merge; others excluded)
- Update existing tests that asserted old RunStatusResponse fields from /boards/runs

Web UI changes:
- Add DemoModeProvider context and useDemoMode hook
- Hide Workflows/Insights nav items in production mode via getVisibleNavigation
- Change run-detail loader to use /runs/{id} directly instead of searching /boards/runs
- Add mapRunSummaryToRunItem for mapping server response to UI shape
- Add Graph tab, hide Stages tab in production mode, always hide Files tab
- Make run-overview and run-graph loaders resilient to 501 via apiJsonOrNull
- Add isNotImplemented and apiJsonOrNull helpers to api.ts

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 04:46:08 -04:00
Bryan Helmkamp
4dc31192b8 refactor(scratch): remove stale scratch file refs
Drop scratch-only compatibility paths and legacy test scaffolding now that
SlateDB-backed state is authoritative. This removes scratch file fallbacks,
updates docs and UI labels, and moves tests onto durable store-backed helpers.
2026-04-07 20:40:06 -04:00
Bryan Helmkamp
d65a4c5f1a Merge remote-tracking branch 'origin/main'
# Conflicts:
#	apps/fabro-web/app/routes/run-overview.tsx
#	apps/fabro-web/app/routes/workflow-detail.tsx
#	apps/fabro-web/app/routes/workflows.tsx
#	lib/crates/fabro-workflow/src/lifecycle/artifact.rs
#	lib/crates/fabro-workflow/src/pipeline/finalize.rs
2026-04-07 15:24:07 -04:00
Bryan Helmkamp
5670be152f fix(web): use local workflow response types
The workflow routes were importing types that do not exist in the generated
OpenAPI client. Define the workflow endpoint response shapes locally so the
web app typechecks against the actual server responses.
2026-04-07 14:48:19 -04:00
Bryan Helmkamp
6ca2833e77 refactor(billing): unify the LLM billing domain
Replace the overlapping usage and cost model with canonical billing
primitives centered on ModelRef, ModelHandle, TokenCounts, and
BilledModelUsage. This also renames the public API and web surface from
usage to billing, removes compatibility aliases, and normalizes provider
usage adapters onto the shared billing vocabulary.
2026-04-07 14:33:35 -04:00
Bryan Helmkamp
17e59992d5 chore: remove unused verification, retros, and sessions endpoints
These endpoints had zero CLI callers and served only the web UI demo.
Verification and retros were `not_implemented` stubs in real mode;
sessions had an in-memory implementation but no CLI usage. Removing
them shrinks the API surface and eliminates ~9,000 lines of dead code.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 17:52:51 -04:00
Bryan Helmkamp
c9c9cd01bd chore: remove skill install command and GET /runs/{id}/verification endpoint
Dead feature cleanup: `skill install` was hidden/experimental and never
graduated; the run verification endpoint was only implemented in demo mode.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 16:38:03 -04:00
Bryan Helmkamp
0366501727 js 2026-04-05 10:19:51 -04:00
Bryan Helmkamp
bf2b6ba701 refactor(cli): route run state through the shared server daemon
Move durable run access and execution control onto the server-backed client,
canonicalize run APIs under /api/v1/runs, and switch CLI integration tests
to a shared test daemon/storage model with shared-state-safe assertions.
2026-04-05 00:15:33 -04:00
Bryan Helmkamp
96359f95cb Fix login route collision after SPA cutover
Move the browser sign-in page off the backend /auth namespace so direct
navigation works, update Rust and frontend redirects to /login, and add
tests that lock the split between SPA login UI and backend OAuth endpoints.
2026-04-01 21:55:30 -07:00
Bryan Helmkamp
b56b82d34b Cut over Fabro web app to a server-backed SPA
Replace the old React Router SSR setup with a static SPA build served by
fabro-server, move setup and GitHub auth handling into Rust, and update the
default local web URL and stale Arc-era references to match the Fabro name.
2026-04-01 21:36:01 -07:00
Bryan Helmkamp
55df05fe43 Prefix API routes with /api/v1 2026-03-30 16:17:06 -04:00
Bryan Helmkamp
defcf9c746 Clean up resolved settings interfaces 2026-03-29 13:47:07 -04:00
Bryan Helmkamp
d45b584ccd Update Discord invite link to new community server
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 09:46:29 -04:00
Bryan Helmkamp
412ef1c432 Add Linux ARM64 (aarch64-unknown-linux-gnu) release target
Add aarch64-unknown-linux-gnu as a third release platform using GitHub's
native ARM64 runner. Updates the release workflow matrix, install script
architecture detection, and CLI upgrade platform detection.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-20 10:33:46 -04:00
Bryan Helmkamp
f10f3c6f62 Add Remotion video app with Fabro brand intro animation
Sets up apps/remotion with a 5-second 1080p intro video featuring the
Fabro symbol, logotype, and tagline animated over the brand navy background.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-19 11:03:19 -04:00
Bryan Helmkamp
b9f5b9a83b Add Triggers and Scheduled Workflows to roadmap
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 17:43:05 -04:00
Bryan Helmkamp
706f563847 Align hero CTA with bottom CTA on marketing homepage
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 09:32:00 -04:00
Bryan Helmkamp
13befea8fa Check in .vercel project link for marketing site
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-17 08:39:01 -04:00
Bryan Helmkamp
7f7328b311 Update marketing roadmap: add new items, rename retros, remove Slack
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-17 08:31:05 -04:00
Bryan Helmkamp
30513cca2d tweak website copy 2026-03-17 08:15:26 -04:00
Bryan Helmkamp
75edf192b0 Remove Showcase and Releases links from marketing homepage footer
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 23:53:40 -04:00
Bryan Helmkamp
c06010b8cc Remove Showcase and Releases links from marketing footer
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 23:48:08 -04:00
Bryan Helmkamp
b539d6a368 blog styling 2026-03-16 23:46:23 -04:00
brynary-fabro[bot]
320e2dbe55 Refactor: Collapse pull_request fields on RunConfig into Option<PullRequestConfig> (#21)
This refactoring collapses four flat `pull_request_*` fields on
`RunConfig` (`pull_request_enabled`, `pull_request_draft`,
`pull_request_auto_merge`, `pull_request_merge_strategy`) into a single
`pull_request: Option<PullRequestConfig>` field. This better represents
the natural tree structure of the configuration: `None` means PR
creation is disabled, while `Some(config)` carries all PR settings
directly.

The PR creation logic in `run.rs` is restructured to use `if let
Some(ref pr_cfg) = config.pull_request` instead of checking a boolean
flag, and construction is simplified to filter out disabled configs at
build time via `.filter(|p| p.enabled).cloned()`. All test constructors
across `engine.rs`, `integration.rs`, `daytona_integration.rs`,
`server.rs`, and `manager_loop.rs` are updated from two lines
(`pull_request_enabled: false, pull_request_draft: false/true`) to a
single `pull_request: None`.

### Fabro Details

<details>
<summary>Ran 10 stages in 20m 32s for $6.62</summary>

| Stage | Duration | Cost | Retries |
|---|---|---|---|
| start | 0s | – | 0 |
| toolchain | 0s | – | 0 |
| preflight_compile | 0s | – | 0 |
| preflight_lint | 0s | – | 0 |
| implement | 0s | $1.71 | 0 |
| simplify_opus | 0s | $1.63 | 0 |
| simplify_gemini | 0s | $1.74 | 0 |
| simplify_gpt | 0s | $1.55 | 0 |
| verify | 0s | – | 0 |
| fmt | 0s | – | 0 |
| **Total** | **20m 32s** | **$6.62** | **0** |

</details>

<details>
<summary>Ran <code>ImplementAndSimplify.fabro</code> (13 nodes and 16
edges)</summary>

```dot
digraph ImplementAndSimplify {
    graph [
        goal="Implement and simplify",
        model_stylesheet="
            * { backend: api; model: claude-opus-4-6;}
        "
    ]
    rankdir=LR

    start [shape=Mdiamond, label="Start"]
    exit  [shape=Msquare, label="Exit"]

    toolchain         [label="Toolchain", shape=parallelogram, script="command -v cargo >/dev/null || { curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y && sudo ln -sf $HOME/.cargo/bin/* /usr/local/bin/; }; cargo --version 2>&1", max_retries=0]
    preflight_compile [label="Preflight Compile", shape=parallelogram, script="cargo check -q --workspace 2>&1", max_retries=0]
    preflight_lint    [label="Preflight Lint", shape=parallelogram, script="cargo clippy -q --workspace -- -D warnings 2>&1", max_retries=0]
    fix_lints         [label="Fix Lints", prompt="The preflight lint step failed. Read the build output from context and fix all clippy lint warnings.", max_visits=3]
    implement         [label="Implement", prompt="Read the plan file referenced in the goal and implement every step. Make all the code changes described in the plan. Use red/green TDD."]
    simplify_opus     [label="Simplify (Opus)", prompt="@prompts/simplify.md"]
    simplify_gemini   [label="Simplify (Gemini)", prompt="@prompts/simplify.md", model="gemini-3.1-pro-preview-customtools"]
    simplify_gpt      [label="Simplify (GPT-54)", prompt="@prompts/simplify.md", model="gpt-54"]
    verify            [label="Verify", shape=parallelogram, script="cargo clippy -q --workspace -- -D warnings 2>&1 && cargo nextest run --cargo-quiet --workspace --status-level fail 2>&1", goal_gate=true, retry_target="fixup"]
    fixup             [label="Fixup", prompt="The verify step failed. Read the build output from context and fix all clippy lint warnings and test failures.", max_visits=3]
    fmt               [label="Format", shape=parallelogram, script="cargo fmt --all 2>&1", goal_gate=true, max_retries=0]

    start -> toolchain
    toolchain -> preflight_compile [condition="outcome=success"]
    toolchain -> exit
    preflight_compile -> preflight_lint [condition="outcome=success"]
    preflight_compile -> exit
    preflight_lint -> implement [condition="outcome=success"]
    preflight_lint -> fix_lints
    fix_lints -> preflight_lint
    implement -> simplify_opus -> simplify_gemini -> simplify_gpt -> verify
    verify -> fmt   [condition="outcome=success"]
    verify -> fixup
    fixup -> verify
    fmt -> exit
}

```

</details>

⚒️ Generated with [Fabro](https://fabro.sh)

---------

Co-authored-by: Fabro <noreply@fabro.sh>
Co-authored-by: Bryan Helmkamp <bryan@brynary.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 21:55:41 -04:00
Bryan Helmkamp
971a6f2451 Remove AI slop from introducing-fabro blog post
Replace "leveraging" buzzword and "This is the key insight:" trope.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 21:38:14 -04:00
Bryan Helmkamp
493fe4b328 Hide Showcase from homepage navigation
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 21:34:18 -04:00
Bryan Helmkamp
18677fcae9 Update marketing site fonts to Razor Geometric treatment
Switch from Space Grotesk / DM Sans / JetBrains Mono to
Outfit / Lexend / Fira Code for a tighter, sharper aesthetic.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 21:22:14 -04:00
Bryan Helmkamp
c4b1dbe194 Rewrite "Introducing Fabro" blog post with founder voice
Expanded from a 1-min stub to a full introductory post with
problem framing, workflow graph example, model stylesheet syntax,
verification gates, checkpoint/resume, and install CTA.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 21:21:24 -04:00
Bryan Helmkamp
b6317a057a Hide Showcase from marketing site navigation
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 21:19:01 -04:00
Bryan Helmkamp
ae4aa1e04c Showcase: render workflow graphs as SVGs, add nav, fix prompt
- Render DOT workflow definitions as visual SVG diagrams at build time
  using @viz-js/viz, replacing raw code blocks on show pages and
  placeholder first-letter thumbnails on index cards
- Collapse models/skills/languages into a compact metadata strip on
  show pages instead of separate boxed sections
- Fix prompt expand/collapse to use a single DOM element with max-height
  animation instead of duplicating the text in two swapped containers
- Add prev/next navigation links at the bottom of show pages
- Extract duplicated langIcons data into shared src/lib/langIcons.ts
- Use varied reveal animation types (reveal-scale, reveal-left)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 20:55:31 -04:00
Bryan Helmkamp
359306569a Update roadmap and standardize on Graphviz terminology
Roadmap: replace placeholder items with current shipped/building/planned
features. Use real dates for sorting instead of manual sortOrder. Fix
UTC timezone rendering for date display.

Terminology: replace all standalone "DOT" references with "Graphviz" or
"Graphviz DOT" across docs, marketing, README, AGENTS.md, and OpenAPI
spec. Changelogs left unchanged as historical records.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 20:01:33 -04:00
Bryan Helmkamp
b06eeb1859 Update /discord redirect to use invite link
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 19:50:29 -04:00
Bryan Helmkamp
48717239e7 Add Showcase section to marketing site
Gallery of workflow recipes with index grid and detail pages.
Three sample entries: PR Review Bot, Test Generator, Docs Sync.
Add Roadmap link to shared Nav component and homepage nav.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 19:11:05 -04:00
Bryan Helmkamp
3bd2a43499 Roadmap: replace inline shipped descriptions with hover tooltip
Saves vertical space by showing only date + title in shipped rows,
with an info icon that reveals the description on hover.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 18:27:58 -04:00
Bryan Helmkamp
38ec5d2846 Add blog and redesign blog/roadmap pages
- Add blog collection (content config, prose styles, introducing-fabro post)
- Add Blog link to homepage nav and footer
- Make Layout description prop dynamic for per-page meta/OG tags
- Extract shared Nav, Footer, PageScripts components from duplicated markup
- Blog index: compact header, featured card for latest post, row list for older posts
- Blog post: clean reading surface (no grid/noise overlay), reading time, better header
- Roadmap: improved card contrast with tinted backgrounds, conditional section rendering

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 18:26:10 -04:00
Bryan Helmkamp
26d0a6a562 Add tabbed install widget (Claude/Codex/Bash) with copy buttons, show hero screenshot on load
Replace single curl command with a tabbed install widget defaulting to Claude,
with Codex and Bash alternatives. Each tab has a copy-to-clipboard button.
Remove scroll-reveal animation from hero screenshot so it's visible immediately.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 13:56:42 -04:00
Bryan Helmkamp
0cc02c294d Move install files to apps/marketing/public, symlink from repo root
Vercel deploys only the apps/marketing/ subtree, so the real files
need to live there. Repo root now symlinks into marketing/public.
Also add .vercel to gitignore.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 13:23:58 -04:00
Bryan Helmkamp
5fad662651 Temporarily hide Roadmap from nav while content is being written
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 12:51:55 -04:00
Bryan Helmkamp
4d93fa55df Improve marketing site mobile layout: reduce hero top spacing and hide curl command
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 12:46:22 -04:00
Bryan Helmkamp
5d2f4c0828 Add llms.txt and additional meta tags for marketing site
- Add llms.txt with structured overview of Fabro docs for LLM consumption
- Add canonical link, application-name, apple-mobile-web-app-title
- Add twitter:image dimensions
- Make og:url dynamic per page

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 12:30:16 -04:00
Bryan Helmkamp
7b452a0783 Add OG image and meta tags for link sharing, remove comma from tagline
- Add 1200x630 branded OG image matching Mintlify docs card style
- Add Open Graph and Twitter Card meta tags to Layout.astro
- Save og-image-template.html for easy regeneration
- Remove comma from "open source, dark software factory" everywhere

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 12:24:58 -04:00
Bryan Helmkamp
582fe2b0c4 Fix marketing site mobile layout: responsive nav and content overflow
Nav links, icons, and CTA button overflowed the viewport on mobile.
Text in Workflow-as-Code and Multi-model sections was clipped because
wide SVG/pre children caused CSS grid blowout (min-width: auto default).

- Add hamburger menu for mobile nav on both pages (hidden md:, toggle JS)
- Add overflow-x-hidden to html/body/main to prevent horizontal scroll
- Add .grid > * { min-width: 0 } to prevent grid children from expanding
  beyond their container

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 12:23:35 -04:00