Skip to content

UI Formula view: create, review and approve Formula proposals (rebind, refresh baseline, enable review, model pin) without the CLI #255

Closed

Description

@DanielNetzer

Problem (v1: drive Forgecell from the UI, 2026-10-26)

Every Formula change is CLI-only (forgecell init …):

The UI's Formula view (#249) only shows the active Formula card, so the operator can't change how their Lab works without a terminal.

Fix

Make the Formula view a full, reviewed place to change the Formula, using exactly the engine's existing proposal functions:

  • Active Formula: name, version or short digest, the Atom list in plain words (intake → scope → coding → checks → [review] → gate → ship → document → learn), the coding agent and model, the reviewer, the required checks with human names, and when and by whom it was approved.
  • Proposals:
    • Create: buttons for the proposal kinds the engine offers today: Rebind to this build (shown when the running binary differs from the Formula's launcher), Refresh baseline from CI, Enable in-loop review (when no review Atom exists), and Pin a model (an input). Each creates the proposal through the same onboarding function the CLI uses, through a guarded POST.
    • Review: a pending proposal shows a readable diff: changed keys with old → new, plus the exact YAML diff behind "details". It also shows the engine's note, for example the reviewer resolution and the "same CLI reviews its own change" warning.
    • Decide: Approve is primary, bound to the exact proposal id and digest shown. Dismiss is secondary.
    • Refusals: shown with their reason (UI correctness from the first browser test: delivered lane, actionable refusals, consistent next action, stray null, job 404 #235 style), for example "a proposal is already pending" or "no reviewer binding".
  • In-flight safety: while any Molecule is in flight, the rebind and baseline buttons explain that approving changes future Molecules and that in-flight ones keep their snapshot. A launcher-only rebind offers the existing --rebase-formula for waiting Molecules where the engine allows it.
  • Guards and demo: the same write guards as the other UI writes (cookie, tab header, exact Origin, same-origin, 4 KiB JSON), and demo mode refuses all of it.

Done when

  • Tests (ui/onboarding, deterministic, fixture Labs):
    • each proposal kind created from the UI produces the same saved proposal bytes as the CLI path;
    • approve and dismiss go through the existing flow and bind the exact id and digest;
    • a stale or mismatched digest is refused;
    • a pending proposal blocks new ones, with the reason shown;
    • the readable diff for each kind;
    • the guards and demo refusal;
    • no raw YAML or paths outside "details".
  • Targeted checks: go test ./internal/ui/... ./internal/onboarding/....

UX rules for every redesign slice (from the 2026-10-26 UI review)

  • Follow the approved mockup in docs/design/ui/redesign-2026-10-26/ (its README lists the tokens and rules), with the assets in lab/internal/ui/web/assets/. Reuse the .btn primary, secondary and danger system and the glass panel from UI polish: composed Start Work controls, segmented lane bar, sharper glass (visual only) #241.
  • One primary action per card or row. Destructive actions (Retire, Stop Molecule, Stop loop) sit behind a disclosure and a confirmation that names the Molecule or loop. Never place them next to a primary action.
  • State in the URL: view, selected Molecule, filter and search (for example ?view=inbox, ?m=<id>, ?filter=…&q=…) via history.pushState, so Back, reload and copied links work. Navigation uses real <a href>.
  • Forms: real <form>s, so Enter submits. Inputs carry name, autocomplete="off" and an example placeholder ending in "…". A submit button stays enabled until the request starts, then shows progress. Errors appear inline, linked with aria-describedby, and focus moves to them.
  • No rebuild under the user: polling patches the DOM in place, keyed by id. Never rebuild a block that has focus or an input being typed in. Keep one persistent aria-live region and update its text only when it changes.
  • Readable values:
    • dates via Intl.DateTimeFormat plus relative time;
    • money via Intl.NumberFormat (one precision rule; small amounts never round to $0);
    • counts via Intl.PluralRules;
    • tabular-nums for numbers;
    • never raw phase codes, assay-… or mol-… ids, full digests or ISO timestamps in the main UI (short forms only, behind "details" with a copy button).
  • Plain language, second person, Title Case buttons: "Plan ready for your approval", "Coding · 18 min", "Ready to publish". Keep the primitives true: Lab, Molecule, Atom and Formula keep their names and meanings.
  • Accessibility and touch:
    • a skip link;
    • one <main>;
    • ordered headings;
    • visible :focus-visible everywhere;
    • targets at least 44 px on coarse pointers;
    • touch-action: manipulation;
    • reduced motion and forced colors honoured;
    • no horizontal page scroll at 400 px.

Review checklist (read before coding)

The adversarial review checks these every time. Recent slices failed review on all of them, so build them in from the start:

  • Bounds: sizes, counts, time and total work are bounded, and hitting a bound fails safe (incomplete or truncated, never silently accepted).
  • Filesystem safety: paths cannot traverse. Use Lstat and O_NOFOLLOW, and check symlinks and ownership. Watch for TOCTOU races on shared dirs (/tmp). Only ever delete Forgecell-owned, marked paths.
  • Untrusted input: issue text, repository files, model and tool output, and ledgers can be malformed, huge or hostile. Validate them, never interpolate them into HTML or a shell, and never let them trigger retries or approvals.
  • No local leaks: no absolute local paths, environment variables or credentials in output, logs, the UI or ledgers.
  • Contract changes: a changed adapter invocation bumps the protocol, and approved digests still bind exactly what was shown.
  • Tests: deterministic. Use no wall-clock budgets (use readiness markers or seams), write nothing into the user's real temp or home, and cover the failure path as well as the happy path.

Plan checks (for intake)

Leave every check's reviewedAcceptance and independentProvenance empty: those come only from a separate human review, never from analysis. Check category must be one of regression, candidate or setup. Use targeted package tests only as plain go test commands (for example go test ./internal/<pkg>/...); the protected view adds -json itself when protected deltas are declared. The Formula baseline already runs the full suite once, so don't add another full go test ./... check, and don't repeat its go vet or gofmt checks. A plan may hold at most 16 checks in total, counting the 5 Formula baseline checks, so prefer one combined targeted command over one check per package.

Added 2026-10-27 (from #217 triage)

  • Rebind to this build must work for dev builds. compareReleaseVersions compares dev commit hashes alphabetically, so a newer build can count as older and launcherUpgrade (formula.go) refuses. Order dev builds by the build's recorded commit time, or refuse with a plain reason plus an explicit "rebind anyway" that is still bound to the shown digest.

  • Tests: a newer dev build can be rebound, and an older one is refused with the reason.

  • Leave room in the Formula view for a later Suggestions section (UI-5: Lab UI — Formula suggestion review and Formula version history #202).

  • Keep base tests compiling (added 2026-10-27): the protected view runs the base _test.go files against your code. Never change the signature of an existing Go function, method or type that a base test file calls. Add a new one instead. A protected build failure cannot be declared as an expected delta.

  • After UI foundation: split app.js/style.css into per-view modules (no behaviour change) so v1 UI slices run in parallel #262: this slice owns its own web/js/<view>.js module(s) and web/css/<view>.css. Don't edit other views' modules; shared helpers go in core*.js.

Plan checks for this slice (2026-10-27)

  • The operator's visual or browser check is a manual step after delivery, not a plan check. Don't add a check for it.
  • Every plan check: category: regression or candidate, with reviewedAcceptance and independentProvenance left empty. The analysis must never assert independent acceptance.
  • Use one combined targeted go test check for the packages this slice changes.

Activity

  1. added this to the v1 milestone on Oct 26, 2026
  2. DanielNetzer commented on Oct 27, 2026

    @DanielNetzer
    OwnerAuthor

    Added to scope (from #217, triage 2026-10-27): Rebind to this build must work for dev builds. compareReleaseVersions compares dev commit hashes alphabetically, so a newer build can count as older, and launcherUpgrade (formula.go) refuses. Order dev builds by the build's recorded commit time (or refuse with a plain reason and an explicit 'rebind anyway' that is still bound to the shown digest). Also leave room in the Formula view for a later Suggestions section (#202).

  3. added 2 commits that reference this issue on Oct 27, 2026
  4. DanielNetzer commented on Oct 27, 2026

    @DanielNetzer
    OwnerAuthor

    Merged via #274.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    wave-7Wave 7: Lab UI redesign

    Projects

    No projects

      Milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions