fabro/apps/fabro-web/app/components
Bryan Helmkamp bc0bda73a6
Some checks are pending
Rust / Clippy (push) Waiting to run
Rust / Format (push) Waiting to run
TypeScript / Build (push) Waiting to run
Rust / Generated Docs (push) Waiting to run
Rust / Test (Linux) (push) Waiting to run
Rust / Test (macOS) (push) Waiting to run
TypeScript / Typecheck (push) Waiting to run
TypeScript / Test (push) Waiting to run
feat(web): add server-managed Environments CRUD settings UI (#462)
## What

Adds a CRUD interface for **server-managed Environments** at
`/settings/environments`, driven by the `/api/v1/environments` REST API
(list / create / retrieve / replace / delete), and reshapes how built-in
environments are provisioned and protected.

The page lives in the **Workflows** settings nav section (also
introduced in this branch), positioned before Variables.

## Why

The Environments REST API shipped (#453) but had no UI — environments
could only be managed via the API/CLI. This gives operators a web UI
alongside Variables and Secrets, and along the way tightens the model:
environments are seeded at install time (not silently re-created on
every boot), and the `default` fallback is an ordinary, deletable
environment.

## Web UI

**Pages & component**
- `settings-environments.tsx` — list view: provider badge,
image/resource summary, row actions (Edit/Delete). **"New environment"
is a dropdown** of the enabled sandbox providers; the chosen provider is
fixed for the environment's lifetime.
- `settings-environments-new.tsx` / `settings-environments-edit.tsx` —
create/edit flows; create reads the provider from a query param.
- `environment-form.tsx` — shared form, reorganized:
- **General** panel (merged identity + image): id, and an **image-source
selector** (Image reference *vs* inline Dockerfile) that shows,
requires, and sends only the selected, mutually-exclusive source.
- **Resources**: CPU / memory / disk as **range sliders** (CPU 1–8,
memory 1–16 GB, disk 1–20 GB), each always writing a concrete value.
  - **Environment variables** key/value editor.
- **Advanced** progressive-disclosure section holding **Network** (a
single "Block all network access" toggle — allow-all vs block) and
**Lifecycle** (preserve / stop-on-terminal / auto-stop). Opens by
default when any advanced value is non-default.
- The in-form **provider control and the Labels editor were removed** —
labels remain API-managed and are round-tripped untouched so UI edits
never clear them.

**Data layer**: `environmentsApi` client, `queryKeys.environments`,
`useEnvironments` / `useEnvironment` SWR hooks.

**Nav & routing**: "Environments" item in the Workflows section before
Variables; routes registered in `router.tsx`.

## Backend: seed at install, deletable `default`

- **Seeding moved to install time.** The server no longer seeds
built-ins on startup; `EnvironmentStore::load_or_seed` → `load`
(load-only). A new public `seed_environments(dir)` (idempotent,
preserves operator edits) is called by both the web installer and the
CLI installer. An uninstalled instance therefore has no managed
environments, and a run selecting an absent environment fails explicitly
(`unknown environment: default`) rather than resurrecting a built-in.
- **`default` is no longer protected.** The delete guard and the
`Protected` error variant are gone; deleting `default` succeeds (204)
and removes the run fallback on purpose — forcing an explicit choice.
`local` is unchanged (reserved, in-memory).
- **`volumes` removed** from environment settings across the OpenAPI
spec, generated Rust + TS clients, config layers,
sandbox/server/workflow plumbing, docs, and tests.

## API contract details honored
- Edit sends the environment `revision` as `If-Match`; 409 conflicts
surface a "changed since you opened it" message.
- The REST API accepts inline Dockerfiles only — the form never sends a
Dockerfile path.

## Verification
- Rust: `cargo build` (touched crates) ✅, `cargo nextest -p
fabro-environment` 21/21 ✅, server env unit + `tests/it` integration 2/2
+ 15/15 ✅, `clippy` (nightly, touched crates, all targets) clean ✅, `fmt
--check` clean ✅. Full `--workspace` suite not run here — worth a CI
pass.
- Web: `bun run typecheck` ✅, `bun run build` ✅,
`environment-form.test.ts` 5/5 ✅. Web suite: 512 pass / 1 unrelated
pre-existing `RunDetail` failure.
- **Not visually verified in-browser** — the local app is login-gated
and automated loads redirect to `/login`; rendering of the form, the
New-environment dropdown, and `default` delete should be confirmed in a
logged-in session.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: fabro-sh-0530[bot] <281434857+fabro-sh-0530[bot]@users.noreply.github.com>
Co-authored-by: Fabro <noreply@fabro.sh>
Co-authored-by: Release Repro <release-repro@example.com>
2026-06-13 08:44:38 -04:00
..
chats Introduce approved effect hooks and migrate direct useEffect calls (#425) 2026-05-27 10:37:29 -04:00
playground feat: chat-driven workflow builder at /playground (#450) 2026-06-09 11:24:56 -04:00
runs-list feat(web): add server-managed Environments CRUD settings UI (#462) 2026-06-13 08:44:38 -04:00
stage-renderers refactor(web): improve React Doctor score (#405) 2026-05-25 22:41:37 -04:00
auth-layout.tsx refactor(web): improve React Doctor score (#405) 2026-05-25 22:41:37 -04:00
automation-form.tsx Remove top-level automation enabled master gate (#456) 2026-05-29 22:18:14 -04:00
collapsible-file.tsx refactor(web): drop light mode, go dark-only 2026-04-20 09:27:08 -04:00
editable-run-title.test.tsx feat(web): inline-edit run titles in the run header 2026-05-09 11:54:41 -04:00
editable-run-title.tsx refactor(web): improve React Doctor score (#405) 2026-05-25 22:41:37 -04:00
environment-form.test.ts feat(web): add server-managed Environments CRUD settings UI (#462) 2026-06-13 08:44:38 -04:00
environment-form.tsx feat(web): add server-managed Environments CRUD settings UI (#462) 2026-06-13 08:44:38 -04:00
event-debug-helpers.tsx refactor(web): improve React Doctor score (#405) 2026-05-25 22:41:37 -04:00
event-debug.tsx Introduce approved effect hooks and migrate direct useEffect calls (#425) 2026-05-27 10:37:29 -04:00
floating-tooltip.tsx Introduce approved effect hooks and migrate direct useEffect calls (#425) 2026-05-27 10:37:29 -04:00
graph-toolbar-constants.ts refactor(web): improve React Doctor score (#405) 2026-05-25 22:41:37 -04:00
graph-toolbar.tsx refactor(web): improve React Doctor score (#405) 2026-05-25 22:41:37 -04:00
icons.tsx feat(runs): link stored pull requests 2026-05-08 13:17:08 -07:00
inline-markdown.test.tsx feat(web): render inline Markdown in run titles 2026-05-08 18:30:51 -07:00
inline-markdown.tsx refactor(web): improve React Doctor score (#405) 2026-05-25 22:41:37 -04:00
interview-dock.test.tsx refactor(web): improve React Doctor score (#405) 2026-05-25 22:41:37 -04:00
interview-dock.tsx refactor(web): improve React Doctor score (#405) 2026-05-25 22:41:37 -04:00
interview-label.ts refactor(web): improve React Doctor score (#405) 2026-05-25 22:41:37 -04:00
pull-request-chip.test.tsx feat(pr): support GitHub pull request associations (#270) 2026-05-16 12:47:27 -04:00
pull-request-chip.tsx feat(pr): support GitHub pull request associations (#270) 2026-05-16 12:47:27 -04:00
run-summary-panel.test.tsx feat(web): add server-managed Environments CRUD settings UI (#462) 2026-06-13 08:44:38 -04:00
run-summary-panel.tsx feat(web): add server-managed Environments CRUD settings UI (#462) 2026-06-13 08:44:38 -04:00
run-waterfall.tsx Introduce approved effect hooks and migrate direct useEffect calls (#425) 2026-05-27 10:37:29 -04:00
settings-panel.tsx refactor(web): improve React Doctor score (#405) 2026-05-25 22:41:37 -04:00
size-chip.tsx feat(web): add sortable Size column to runs list 2026-05-25 14:07:17 -04:00
stage-context.tsx feat(web): add Context tab to the stage detail view (#340) 2026-05-21 20:19:55 -04:00
stage-insights-sidebar.test.tsx refactor: rationalize server secret scopes (vault-only for optional int… (#401) 2026-05-25 17:26:01 -04:00
stage-insights-sidebar.tsx refactor(web): improve React Doctor score (#405) 2026-05-25 22:41:37 -04:00
stage-popover-summary.ts refactor(web): improve React Doctor score (#405) 2026-05-25 22:41:37 -04:00
stage-popover.test.tsx refactor(web): improve React Doctor score (#405) 2026-05-25 22:41:37 -04:00
stage-popover.tsx refactor(web): improve React Doctor score (#405) 2026-05-25 22:41:37 -04:00
stage-sidebar.test.tsx Surface reasoning_effort + speed in stage badge end-to-end (#363) 2026-05-23 13:14:55 -04:00
stage-sidebar.tsx refactor(web): improve React Doctor score (#405) 2026-05-25 22:41:37 -04:00
state.tsx feat(web): add "Test models" sweep button on settings/models (#410) 2026-05-25 22:42:16 -04:00
steer-bar.tsx refactor(web): improve React Doctor score (#405) 2026-05-25 22:41:37 -04:00
terminal-view-helpers.ts Model run sandbox lifecycle explicitly (#431) 2026-05-27 12:48:56 -04:00
terminal-view.test.ts Model run sandbox lifecycle explicitly (#431) 2026-05-27 12:48:56 -04:00
terminal-view.tsx Model run sandbox lifecycle explicitly (#431) 2026-05-27 12:48:56 -04:00
toast.test.tsx Introduce approved effect hooks and migrate direct useEffect calls (#425) 2026-05-27 10:37:29 -04:00
toast.tsx feat(web): theme toasts to match the app 2026-05-29 08:44:48 -04:00
ui.tsx Replace DIY overlay primitives with Radix UI + Sonner (#424) 2026-05-26 23:03:06 -04:00