v7.3.0 — Forge
[7.3.0] Forge
Forge 7.3 is primarily a GUI release — focus-chat mode for the Electron app and the Watchfire version surfaced under the sidebar logo so you no longer have to dig through Settings to know what you're running — plus one daemon-side fix that closes the size-cap deferral v7.2.1 explicitly left open: ~/.watchfire/daemon.log is now bounded at ~1 GB total (500 MB active + one 500 MB backup) after a user's log grew to 300 GB. The version bump lands on version.json, gui/package.json, and gui/package-lock.json together so all shipped components advertise v7.3.0 even where their code is unchanged.
Added
- GUI focus-chat mode — collapse the center column so only the right panel (Chat / Branches / Logs) remains. Today's
ProjectViewis a three-region layout: the center column (Tasks / Definition / Insights / Secrets / Trash / Settings tabs), the right panel (Chat / Branches / Logs tabs, collapsible via the existingPanelRightClosetoggle), and the bottom integrated terminal. While chatting with the agent the center column is visual noise and steals horizontal space, but there was no way to hide it without also losing the agent terminal. NewchatFocusboolean ingui/src/renderer/src/views/ProjectView/ProjectView.tsxcollapses the center column entirely and lets the right panel take the full row via a conditionalflex-1/shrink-0 + style.widthswap on its container; the panel keeps all three tabs visible per the user's spec choice, so Branches and Logs remain reachable in focus mode. Toggle surfaces: (1) a newMaximize2/Minimize2lucide button in the project header, placed immediately before the existing right-panel toggle and tooltip'dFocus chat/Exit focus(button is conditionally rendered — when the right panel is closed there is no chat to focus on, so the button hides itself rather than rendering disabled); (2)onDoubleClickon the right-panel resize divider, which the user explicitly asked for as a secondary affordance. The existingusePanelResizedrag logic usesonMouseDown+ global mousemove listeners and does not interfere withonDoubleClick; in focus mode theonMouseDownhandler is detached so the 1px column reads as a click target rather than a drag handle, and the cursor flips fromcursor-col-resizetocursor-pointer. Two small but important interlocks on the existing right-panel toggle: entering focus mode while the right panel is closed also opens it (no chat to focus otherwise), and closing the right panel while focus is on also exits focus (keeps the two states consistent). State is per-project, persisted tolocalStorageunderwf-chat-focus-<projectId>with the sameuseState+useEffectpattern that already backswf-right-panel-openandwf-right-panel-width; a seconduseEffectre-hydrates the flag from localStorage onprojectIdchange so switching projects picks up each project's own focus state. The bottom terminal panel is independent of focus mode per the user's spec choice — whateverCmd+\`` set, focus mode does not touch. No new dependencies;Maximize2/Minimize2are already in lucide-react and thecn()helper fromlib/utils.ts` covers the conditional className swap. - GUI version under the sidebar logo. The Watchfire version was previously only reachable through Settings → About. New
useState+useEffectingui/src/renderer/src/components/Sidebar.tsxcalls the existingwindow.watchfire.getVersion()IPC (defined ingui/src/preload/index.ts:33, typed ingui/src/renderer/src/env.d.ts:25, backed bygui/src/main/ipc.tsreadingpackage.json) once on mount; the expanded logo block is restructured from a singleflex items-center gap-2row into aflex flex-colstack so a smallv{version}line can sit directly under the wordmark, aligned withpx-4and tightened with-mt-1. Styled withtext-[10px] text-[var(--wf-text-muted)]to match the sidebar's existing muted-metadata treatment. Collapsed sidebar (56 px wide) is intentionally left alone — there is no room forv7.3.0next to the 24 px logo without it looking cramped, and the value is still in Settings → About. Format matchesAboutSection.tsx:13:v{version}, not bare{version}.
Changed
- All shipped components carry the
7.3.0version label.version.json7.2.1 → 7.3.0 (codenameForgepreserved — same release line, no new theme name minted), which propagates to the daemon and CLI binaries through the existingMakefileldflags wiring (-X .../buildinfo.Version=$(VERSION)) the next timemake buildruns.gui/package.jsonandgui/package-lock.json7.2.1 → 7.3.0 so the Electron app'sapp.getVersion(), thewindow.watchfire.getVersion()IPC, and now the new sidebar version line all report 7.3.0.
Fixed
- Daemon log is now size-capped at ~1 GB (500 MB active + 1 backup). Closes the cap deferral v7.2.1 called out explicitly ("No size cap (rotate manually if it grows); a corrupt or
/dev/null-overridden file does not abort daemon startup.") — one user's~/.watchfire/daemon.loggrew to 300 GB on disk before they noticed. Newinternal/daemon/cmd/daemonlog.goholds a self-rotatingio.Writer(rotatingFileWriter) that wraps the file destination inside the existingio.MultiWriter(os.Stderr, …)chain inopenDaemonLog(also moved into the new file for testability; the call site atinternal/daemon/cmd/root.go:73is unchanged). Per-file cap is 500 MiB, one numbered backup is kept (daemon.log.1), no gzip — total disk budget ≈ 1 GiB. OnWrite, ifsize + len(p) > fileCapthe writer closes the active file, drops any stale backups numbered beyond the currentbackupsconstant (defensive cleanup if a future build lowers the count), shifts numbered backups down (zero-iteration loop in the 1-backup case), promotesdaemon.log→daemon.log.1(overwriting any prior.1), and opens a fresh active file in append mode. On startup, if an existingdaemon.logis already ≥ cap (the upgrade-from-oversized case — a user upgrading with a multi-GB log on disk), the constructor rotates immediately so the next write starts in a fresh file under cap. The writer holds its ownsync.Mutexfor defensive concurrency; the stdliblogpackage already serialises calls through its own mutex, so the writer's lock is uncontended in practice but covers any caller that bypasseslog.Output(test code, future direct writers). Errors during rotation propagate back throughWriteto the stdliblogpackage, which surfaces them onos.Stderr(still wired viaio.MultiWriter) and continues; the daemon never crashes because the log can't rotate. No third-party dep added: the codebase uses stdliblogexclusively across 162 call sites ininternal/daemon/, andgopkg.in/natefinch/lumberjack.v2isn't worth the import block for ~80 lines of behaviour, especially since its main feature beyond what we ship (gzipped backups) is the one we explicitly didn't want. Tests ininternal/daemon/cmd/daemonlog_test.gocover five cases: under-cap append (1 KB write into a 4 KB-cap writer leaves onlydaemon.logon disk), cap-crossing rotation (1000 B + 100 B into a 1024 B-cap writer leavesdaemon.log.1holding the first 1000 B and a freshdaemon.logholding the 100 B), upgrade-with-oversized-existing-file (pre-populated 2 KB at a 1 KB-cap writer rotates onNew…so the pre-populated bytes land in.1and the active file is empty), multi-rotation backup-count invariant (six forced rotations at 512 B cap leave onlydaemon.log+daemon.log.1on disk — no.2,.3,.4), and a 50-goroutine concurrent-write smoke (no panic, total bytes on disk ≤ 2 ×fileCap, each goroutine'sWritereturnednil).