Skip to content

v7.3.0 — Forge

Choose a tag to compare

@github-actions github-actions released this 29 May 14:06

[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 ProjectView is a three-region layout: the center column (Tasks / Definition / Insights / Secrets / Trash / Settings tabs), the right panel (Chat / Branches / Logs tabs, collapsible via the existing PanelRightClose toggle), 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. New chatFocus boolean in gui/src/renderer/src/views/ProjectView/ProjectView.tsx collapses the center column entirely and lets the right panel take the full row via a conditional flex-1 / shrink-0 + style.width swap 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 new Maximize2/Minimize2 lucide button in the project header, placed immediately before the existing right-panel toggle and tooltip'd Focus 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) onDoubleClick on the right-panel resize divider, which the user explicitly asked for as a secondary affordance. The existing usePanelResize drag logic uses onMouseDown + global mousemove listeners and does not interfere with onDoubleClick; in focus mode the onMouseDown handler is detached so the 1px column reads as a click target rather than a drag handle, and the cursor flips from cursor-col-resize to cursor-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 to localStorage under wf-chat-focus-<projectId> with the same useState + useEffect pattern that already backs wf-right-panel-open and wf-right-panel-width; a second useEffect re-hydrates the flag from localStorage on projectId change 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 — whatever Cmd+\`` 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 + useEffect in gui/src/renderer/src/components/Sidebar.tsx calls the existing window.watchfire.getVersion() IPC (defined in gui/src/preload/index.ts:33, typed in gui/src/renderer/src/env.d.ts:25, backed by gui/src/main/ipc.ts reading package.json) once on mount; the expanded logo block is restructured from a single flex items-center gap-2 row into a flex flex-col stack so a small v{version} line can sit directly under the wordmark, aligned with px-4 and tightened with -mt-1. Styled with text-[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 for v7.3.0 next to the 24 px logo without it looking cramped, and the value is still in Settings → About. Format matches AboutSection.tsx:13: v{version}, not bare {version}.

Changed

  • All shipped components carry the 7.3.0 version label. version.json 7.2.1 → 7.3.0 (codename Forge preserved — same release line, no new theme name minted), which propagates to the daemon and CLI binaries through the existing Makefile ldflags wiring (-X .../buildinfo.Version=$(VERSION)) the next time make build runs. gui/package.json and gui/package-lock.json 7.2.1 → 7.3.0 so the Electron app's app.getVersion(), the window.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.log grew to 300 GB on disk before they noticed. New internal/daemon/cmd/daemonlog.go holds a self-rotating io.Writer (rotatingFileWriter) that wraps the file destination inside the existing io.MultiWriter(os.Stderr, …) chain in openDaemonLog (also moved into the new file for testability; the call site at internal/daemon/cmd/root.go:73 is unchanged). Per-file cap is 500 MiB, one numbered backup is kept (daemon.log.1), no gzip — total disk budget ≈ 1 GiB. On Write, if size + len(p) > fileCap the writer closes the active file, drops any stale backups numbered beyond the current backups constant (defensive cleanup if a future build lowers the count), shifts numbered backups down (zero-iteration loop in the 1-backup case), promotes daemon.logdaemon.log.1 (overwriting any prior .1), and opens a fresh active file in append mode. On startup, if an existing daemon.log is 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 own sync.Mutex for defensive concurrency; the stdlib log package already serialises calls through its own mutex, so the writer's lock is uncontended in practice but covers any caller that bypasses log.Output (test code, future direct writers). Errors during rotation propagate back through Write to the stdlib log package, which surfaces them on os.Stderr (still wired via io.MultiWriter) and continues; the daemon never crashes because the log can't rotate. No third-party dep added: the codebase uses stdlib log exclusively across 162 call sites in internal/daemon/, and gopkg.in/natefinch/lumberjack.v2 isn'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 in internal/daemon/cmd/daemonlog_test.go cover five cases: under-cap append (1 KB write into a 4 KB-cap writer leaves only daemon.log on disk), cap-crossing rotation (1000 B + 100 B into a 1024 B-cap writer leaves daemon.log.1 holding the first 1000 B and a fresh daemon.log holding the 100 B), upgrade-with-oversized-existing-file (pre-populated 2 KB at a 1 KB-cap writer rotates on New… so the pre-populated bytes land in .1 and the active file is empty), multi-rotation backup-count invariant (six forced rotations at 512 B cap leave only daemon.log + daemon.log.1 on disk — no .2, .3, .4), and a 50-goroutine concurrent-write smoke (no panic, total bytes on disk ≤ 2 × fileCap, each goroutine's Write returned nil).