FE-845: TUI chrome resolutions pass I#196
Conversation
PR SummaryLow Risk Overview Pi startup behavior changes: Adds Reviewed by Cursor Bugbot for commit 25dc094. Bugbot is set up for automated code reviews on this repo. Configure here. |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 2 potential issues.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit b4186e2. Configure here.
| return this.theme.fg('dim', `web-ui: ${sanitizeText(this.facts.sidecarUrl)}`); | ||
| } | ||
| return this.theme.fg('dim', 'Press ctrl+o to show full Brunch startup help and active surfaces.'); | ||
| } |
There was a problem hiding this comment.
Startup header expand unwired
Medium Severity
The new BrunchStartupHeader supports collapsed vs expanded rendering and tells users to use ctrl+o for “more”, but it never implements handleInput and the chrome extension does not register a shortcut to call setExpanded. With a live webSidecarUrl, the collapsed header also omits the expand hint, so session/spec detail shown only when expanded may be unreachable in normal TUI launches.
Additional Locations (1)
Reviewed by Cursor Bugbot for commit b4186e2. Configure here.
There was a problem hiding this comment.
Pull request overview
This PR updates the Brunch TUI launch path to (a) reduce Pi startup noise and (b) add visible “chrome” (header/footer/title) that surfaces Brunch identity plus the active web sidecar URL.
Changes:
- Thread web-sidecar URL + activation decision through the TUI launch context and into the chrome state, and adjust browser auto-open defaults under
BRUNCH_DEV. - Add chrome header/footer projections (including startup header rendering) and enable the chrome extension entrypoint in
src/.pi/settings.json. - Introduce a dev-only “TUI style lab” extension + component helpers, with dedicated unit tests.
Reviewed changes
Copilot reviewed 15 out of 15 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| src/app/brunch-tui.ts | Threads webSidecarUrl/activation decision into session runtime + chrome; scopes Pi env vars during interactive run; updates auto-open logic. |
| src/app/brunch-tui.test.ts | Extends boot tests for decision propagation, auto-open defaults under BRUNCH_DEV, and env scoping behavior. |
| src/.pi/settings.json | Enables the chrome extension entrypoint for direct Pi iteration. |
| src/.pi/extensions/tui-lab/index.ts | Adds dev-gated TUI style lab command + interactive overlay component. |
| src/.pi/extensions/README.md | Documents new chrome ownership rules and launch-facts plumbing. |
| src/.pi/extensions/chrome/index.ts | Adds startup header + sidecar URL projection into chrome; updates default extension entrypoint to register chrome. |
| src/.pi/components/tui-lab/style-palette.ts | Adds palette/badge helpers for style lab rendering. |
| src/.pi/components/tui-lab/segment-track.ts | Adds segment track helpers for cycling demo state in style lab. |
| src/.pi/components/tui-lab/index.ts | Re-exports style lab component helpers. |
| src/.pi/components/chrome-header.ts | Adds BrunchStartupHeader component that renders identity/help + optional sidecar URL. |
| src/.pi/tests/tui-lab-style.test.ts | Tests palette/badge rendering and extension registration gating. |
| src/.pi/tests/tui-lab-cycle.test.ts | Tests segment cycling helpers + demo component behavior. |
| src/.pi/tests/extension-registry.test.ts | Asserts chrome entrypoint remains enabled in .pi/settings.json. |
| src/.pi/tests/chrome.test.ts | Extends chrome projection tests for sidecar footer line + startup header behavior. |
| memory/SPEC.md | Updates D35-L/D69-L/D71-L language to reflect the new chrome + startup suppression direction. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| try { | ||
| if (options.dev) { | ||
| delete env.PI_OFFLINE; | ||
| } else { | ||
| applyBrunchOfflineDefault(env); | ||
| } | ||
| applyBrunchOfflineDefault(env); | ||
| await options.run(); | ||
| } finally { |
| @@ -443,19 +465,37 @@ describe('Brunch TUI boot', () => { | |||
| }), | |||
| await expect( | ||
| runWithScopedBrunchOfflineDefault({ | ||
| dev: true, | ||
| env: devEnv, | ||
| run: async () => { | ||
| expect(devEnv.PI_OFFLINE).toBeUndefined(); | ||
| expect(devEnv.PI_OFFLINE).toBe('1'); | ||
| }, | ||
| }), |
| export async function runWithScopedBrunchOfflineDefault(options: { | ||
| readonly dev: boolean; | ||
| readonly env?: { PI_OFFLINE?: string }; | ||
| readonly env?: { PI_OFFLINE?: string; PI_SKIP_VERSION_CHECK?: string }; | ||
| readonly run: () => Promise<void>; |
| return formatBrunchProductIdentity({ | ||
| logoLines: readBrunchAnsiLogo({ assetUrl: ASSET_DIR, truecolor: true }), | ||
| version: brunchVersion(), | ||
| theme: this.theme, | ||
| }); |
| - **D40-L — Runtime state is transcript-backed Brunch session-agent state, not hidden extension memory.** `src/session/runtime-state.ts` owns the transcript entry facts (`brunch.agent_runtime_state` schema, parser, and init/switch append helpers); `src/projections/session/runtime-state.ts` owns the pure reusable projection, `src/projections/session/runtime-policy.ts` owns operational-mode/role policy plus shared grade legality tables, and `src/projections/session/affordances.ts` owns the pure `(resolvedState, readinessGrade) → legal options + default-on-switch` derivation for goal/strategy/lens. The projection reconstructs agent posture from linear `brunch.agent_runtime_state` entries (`reason: "init" | "switch"`), last-writer-wins at turn preparation and over `session.runtimeState`; default/empty slots are explicit when no entry family exists. Runtime-state entries are Pi JSONL state-change facts, not assistant/user chat content: init and switch entries should render, when visible, as dim non-chat state rows analogous to Pi thinking/model-change rows, and must not enter LLM context as ordinary conversation. Its axes are `op_mode` (`elicit`, future `execute`) plus optional, AUTO-able objective axes `strategy`, `lens`, and `goal` (D25-L, D59-L). **Posture switches (durable `reason: "switch"` entries) are a user/system authority: the foreground agent never emits a posture switch.** The agent's only in-axis freedom is `AUTO` (per-turn implicit selection from the D58-L manifest); what it actually chose each turn is legible downstream via per-emission facet stamping (D25-L), not via runtime-state — so runtime-state is the *frame/constraints* while emitted facets carry the agent's per-turn choice. User-mutable axes are `op_mode`, `strategy`, and `lens`; `goal` is internal/grade-derived and not part of the user posture-change surface for now (D59-L). On a parent switch that invalidates a child axis, the child defaults to `AUTO`. The `source: "agent"` entry value is reserved — no current path emits it; it is parked for a future execute-mode orchestrator that might legitimately steer sub-postures. `session.runtimeState` also exposes shaped mention slots, world-update watermarks (latest graph LSN and optional git head, without raw transcript detail bags), and lifecycle facts when transcript-backed entries make them computable; this is a projection contract, not a mutable state table. The **foreground session agent** (`elicitor` now, future `executor`) is *derived* from `op_mode`, not stored; the other agent roles (`reviewer`, `reconciler`, future `scout`/`researcher`) are async sub-agent/side-chain workers (D29-L, D44-L) invoked out-of-band, never part of the session state machine. `op_mode` gates tool authority, applied by `src/.pi/extensions/runtime/index.ts` (current `elicit` policy denies side-effecting `bash`/`edit`/`write` plus user-shell interception) while `.pi` reuses session-owned entry definitions and projected policy. Prompt composition is a separate concern (D58-L). Depends on: D17-L, D23-L, D25-L, D39-L, D58-L, D59-L. Supersedes: mode-only vocabulary, extension-local mutable state as authority, storing the foreground role as independent session state, the "runtime bundle / role preset" as one knob deriving model/thinking/resources, and binding prompt-resource location to `src/.pi/context/`. | ||
| - **D34-L — Command containment separates visibility suppression from effect blocking.** Current Pi extension seams can hide unsupported slash suggestions with autocomplete wrapping and can cancel branch/session effects through lifecycle hooks, but they cannot strictly suppress exact interactive built-in commands before `InteractiveMode` dispatches them. Brunch-owned commands must use product-specific names and route writes through Brunch handlers/`CommandExecutor`; extension command collisions are not an override mechanism. Strict built-in command/keybinding policy is a Pi upstream/API ask, while POC safety relies on hiding generic affordances, blocking dangerous effects (`/fork`, `/clone`, `/tree`, raw session replacement), and failing fast on branched transcripts. Brunch's command-policy code should live in `src/.pi/extensions/commands/policy.ts`, merging branch/session-effect blocking with any product command allow/deny behavior instead of preserving a branch-only module. Depends on: D2-L, D24-L, A18-L. Supersedes: treating extension `input` handlers or command-name collisions as built-in command allowlisting. | ||
| - **D35-L — Dynamic TUI chrome is a Brunch projection wrapper over Pi UI primitives.** Downstream TUI affordances should call a Brunch-owned renderer (`renderBrunchChrome` or its successor) with one activated product-state value rather than scattering raw `ctx.ui.setHeader`, `setFooter`, `setWidget`, title, or working-indicator calls. The wrapper is stateless projection over canonical workspace/session/graph facts, including the discovered project name, selected spec, and real activated session id/label, while its TUI footer compositor may read Pi footer telemetry (`getGitBranch`, foreign `getExtensionStatuses`) at render time. Brunch chrome and startup dialog are project-first shell surfaces with selected-spec context: the project name labels the cwd container, the spec title labels the selected graph, and the session label distinguishes transcript instances. Brunch chrome does not publish a `brunch.chrome` status key; `ctx.ui.setStatus(key, text)` remains a lateral contribution channel for other extensions and future dynamic Brunch state. RPC clients should rely only on surfaces Pi actually emits for the wrapper (currently diagnostic widget/title, plus any future explicit status adapter) because header/footer/working-indicator are TUI-only in current Pi RPC mode. Session display names are product projections over Pi session metadata: every Brunch-created session should immediately receive a neutral workspace-global `Untitled Session N` `session_info` label, and later user/generated names may characterize the transcript without replacing spec identity or graph truth. Depends on: D2-L, D21-L, D34-L, A18-L. Supersedes: treating Pi UI methods as direct downstream affordance APIs, rendering placeholder session state such as `unbound` after a session is activated, consuming the status-key namespace for chrome's own static summary, using spec title as the default session label, or allowing two unchanged Brunch-created default names to collide in one cwd. | ||
| - **D35-L — Dynamic TUI chrome is a Brunch projection wrapper over Pi UI primitives.** Downstream TUI affordances should call a Brunch-owned renderer (`renderBrunchChrome` or its successor) with one activated product-state value rather than scattering raw `ctx.ui.setHeader`, `setFooter`, `setWidget`, title, or working-indicator calls. The wrapper is stateless projection over canonical workspace/session/graph facts, including the discovered project name, selected spec, real activated session id/label, launch activation kind for new-session startup headers, and app-supplied live sidecar URL when present, while its TUI footer compositor may read Pi footer telemetry (`getGitBranch`, foreign `getExtensionStatuses`) at render time. Brunch chrome and startup dialog are project-first shell surfaces with selected-spec context: the project name labels the cwd container, the spec title labels the selected graph, and the session label distinguishes transcript instances. New `newSpec` / `newSession` launches keep Pi `quietStartup` but install a Brunch-owned expandable header through the chrome wrapper; resume/open launches stay quiet. Brunch chrome does not publish a `brunch.chrome` status key; `ctx.ui.setStatus(key, text)` remains a lateral contribution channel for other extensions and future dynamic Brunch state. RPC clients should rely only on surfaces Pi actually emits for the wrapper (currently sidecar/widget-compatible string arrays and title, plus any future explicit status adapter) because header/footer/working-indicator are TUI-only in current Pi RPC mode. Session display names are product projections over Pi session metadata: every Brunch-created session should immediately receive a neutral workspace-global `Untitled Session N` `session_info` label, and later user/generated names may characterize the transcript without replacing spec identity or graph truth. Depends on: D2-L, D21-L, D34-L, A18-L. Supersedes: treating Pi UI methods as direct downstream affordance APIs, rendering placeholder session state such as `unbound` after a session is activated, consuming the status-key namespace for chrome's own static summary, using spec title as the default session label, or allowing two unchanged Brunch-created default names to collide in one cwd. |
b4186e2 to
25dc094
Compare



Summary