Skip to content

Feat/embed daemon#43

Merged
enegalan merged 4 commits into
mainfrom
feat/embed-daemon
Jul 8, 2026
Merged

Feat/embed daemon#43
enegalan merged 4 commits into
mainfrom
feat/embed-daemon

Conversation

@enegalan

@enegalan enegalan commented Jul 8, 2026

Copy link
Copy Markdown
Owner

Summary by CodeRabbit

  • New Features
    • Added platform-specific build/dev and release commands for macOS, Linux, and Windows.
    • The desktop app now embeds and manages the daemon automatically, waiting for readiness before showing the UI.
    • Added a shared “open port” action across container screens.
  • Bug Fixes
    • Improved backend startup/shutdown timing, PATH/daemon readiness checks, and port takeover behavior.
    • Enhanced container parsing and runtime status logging; improved Lima Docker CLI proxying.
  • Chores
    • Updated changelog/versioning, packaging, ignore rules, and macOS release entitlements; added daemon parent watchdog.

enegalan added 2 commits July 8, 2026 01:48
- Added binaries to .gitignore to prevent tracking of build artifacts.
- Updated CHANGELOG to include new features and changes for version 0.7.0, highlighting cross-platform support, backend cross-compilation, and improvements in port conflict detection and UI build targets.
- Enhanced documentation in CLAUDE.md and ROADMAP.md to reflect the new daemon integration and installation plans.
- Updated Makefile to include new build targets for Linux and Windows.
- Bumped version to 0.7.0 in backend and UI configurations.
- Updated CHANGELOG to document changes for version 0.7.0, including fixes and enhancements.
- Modified Makefile to include new build targets for macOS, Linux, and Windows.
- Removed unnecessary entitlements from macOS release configuration to comply with app sandboxing requirements.
- Refactored container parsing logic to unify handling of labels and improve error handling in the backend.
- Enhanced UI logic to ensure proper state management during daemon startup.
@enegalan enegalan linked an issue Jul 8, 2026 that may be closed by this pull request
6 tasks
@coderabbitai

coderabbitai Bot commented Jul 8, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@enegalan, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 40 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 1cd89279-2a87-44d4-ba9f-0b02c9edf18c

📥 Commits

Reviewing files that changed from the base of the PR and between 700d4e7 and 3a54490.

📒 Files selected for processing (1)
  • backend/cmd/calf/watchdog_windows.go
📝 Walkthrough

Walkthrough

This PR ships v0.7.0: platform-specific build and release targets, embedded daemon startup and lifecycle handling, Lima Docker CLI proxying, shared UI port opening, a removed settings toggle, and nerdctl container parsing fixes.

Changes

Build & Release Infrastructure

Layer / File(s) Summary
Platform-specific build and release targets
Makefile
Adds ui-macos/linux/windows, dev-ui-macos/linux/windows, and release/release-macos/linux/windows targets, including macOS universal binary build, codesign, and bundle packaging.
CI cross-compilation and macOS entitlements
.github/workflows/ci.yml, ui/macos/Runner/Release.entitlements
CI now cross-compiles the backend for Linux/Windows; entitlements swap app-sandbox for network-server capability to support the release build.
Documentation of new build/dev targets
.cursor/rules/calf.mdc, CLAUDE.md, ROADMAP.md
Docs describe the new per-OS Make targets and the embedded daemon lifecycle.
Version bump, changelog and ignored binaries
backend/version/version.go, ui/pubspec.yaml, CHANGELOG.md, .gitignore
Bumps version to 0.7.0, adds a changelog entry, and ignores built backend binaries.

Daemon Embedding & Docker Runtime

Layer / File(s) Summary
Daemon startup, PATH setup, and shutdown
backend/cmd/calf/main.go, backend/cmd/calf/watchdog_unix.go, backend/cmd/calf/watchdog_windows.go
Sets PATH at startup, adds parent-process watchdogs, and refactors shutdown to use separate timeout contexts.
Port conflict detection and process cleanup
backend/cmd/calf/main.go
Reworks stale-port detection and cleanup with OS-specific PID lookup and process termination.
Lima status reporting and Log field
backend/internal/runtime/runtime.go, backend/internal/runtime/lima.go, backend/go.mod
Adds a Log field to Status and improves Lima status detection/logging with TCP dial fallback.
Docker CLI unix-socket/TCP proxy
backend/internal/runtime/lima.go, backend/internal/runtime/lima.yaml
Adds a Docker CLI socket/proxy over TCP 2375 via socat, routes nerdctl to sudo docker in the VM.
Flutter app spawns and monitors the daemon
ui/lib/main.dart
Spawns and stops the bundled daemon, polls /v1/status, and gates UI rendering on daemon readiness.

Estimated code review effort: 5 (Critical) | ~120 minutes

UI Port Opening Refactor & Settings Cleanup

Layer / File(s) Summary
Shared openPort helper
ui/lib/platform/open_url.dart
Adds a public openPort(int port) helper delegating to openExternalUrl.
Migrate screens to shared openPort
ui/lib/screens/compose_group_detail_screen.dart, ui/lib/screens/container_detail_screen.dart, ui/lib/screens/containers_screen.dart
Removes per-screen macOS-only _openPort implementations and switches call sites to the shared helper.
Remove start-at-login setting
ui/lib/app_shell.dart
Removes the _startAtLogin state and its settings row.

Estimated code review effort: 2 (Simple) | ~15 minutes

Container List Parsing Fix

Layer / File(s) Summary
Deduplicated parsing and label handling
backend/internal/runtime/nerdctl.go
Deduplicates containers by ID and adds parseLabels to robustly parse JSON-object or comma-separated label strings.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant main as main()
  participant App as _MainAppState
  participant Daemon as calf-daemon
  participant API as v1/status

  main->>Daemon: start daemon process
  main->>App: runApp()
  App->>App: initState() calls _waitForDaemon()
  loop poll until ready or timeout
    App->>API: GET /v1/status
    API-->>App: runtime.state
  end
  App->>App: setState(_daemonReady = true)
  App->>App: build() renders AppShell
  App->>Daemon: stop() on detached/dispose
Loading
sequenceDiagram
  participant Lima
  participant UnixSocket as DockerCLISocket
  participant Proxy as TCPProxy
  participant VM as LimaVM

  Lima->>Lima: Start()
  Lima->>UnixSocket: ensureDockerCLISocket()
  UnixSocket->>Proxy: serveTCPProxy() to 127.0.0.1:2375
  VM-->>Proxy: forwarded Docker socket via socat
  Lima->>Lima: Stop()
  Lima->>Proxy: removeDockerCLISocket()
  Proxy-->>UnixSocket: close listener/server, remove socket
Loading

Possibly related PRs

  • enegalan/calf#9: Both PRs modify backend/cmd/calf/main.go to change daemon/server startup and lifecycle wiring.
  • enegalan/calf#19: Continues the same Lima runtime and nerdctl parsing paths, including Docker proxying and container list handling.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title matches the main change: embedding and bundling the daemon into the app.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/embed-daemon

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 12

🧹 Nitpick comments (3)
backend/internal/runtime/nerdctl.go (2)

186-221: 🎯 Functional Correctness | 🔵 Trivial | 💤 Low value

parseLabels logic is sound; one edge case to consider.

The function correctly handles:

  • Empty/null labels → nil
  • JSON object form → map[string]string
  • Comma-separated string form → split on , then SplitN("=", 2)

One edge case: if a label value contains a comma (e.g., key=val,ue), the comma-separated parser will split it into two segments (key=val and ue), and ue (no =) is silently dropped. This is an inherent limitation of the comma-separated format and likely acceptable for nerdctl's output, but worth documenting with a brief comment if nerdctl can produce such values.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/internal/runtime/nerdctl.go` around lines 186 - 221, Add a brief
comment in parseLabels to document the limitation of the comma-separated parsing
path: values containing commas will be split and later fragments without '=' are
ignored. Keep the existing logic in parseLabels unchanged, but make the edge
case explicit near the strings.Split(s, ",") / SplitN handling so future readers
know this behavior is intentional and tied to nerdctl output.

37-51: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Remove the unused nerdctlLine struct. ParseContainerLines now uses an inline struct instead, so this type only adds drift and confusion.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/internal/runtime/nerdctl.go` around lines 37 - 51, Remove the unused
nerdctlLine struct from the runtime parsing code, since ParseContainerLines now
relies on an inline struct and this standalone type is no longer referenced.
Clean up the type definition in nerdctl.go and make sure any remaining parsing
logic continues to use the inline struct or another shared type only if it is
actually needed elsewhere.

Source: Learnings

.cursor/rules/calf.mdc (1)

147-147: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

calf.mdc is drifting from CLAUDE.md — sync the main.go and main.dart descriptions.

The Makefile entry (line 147) and build commands (lines 255-266) match CLAUDE.md, but two other updated sections in CLAUDE.md were not mirrored here:

  1. Line 54CLAUDE.md updated the main.go tree entry to "...stale-port takeover (cross-platform: lsof/Unix, netstat/Windows)"; calf.mdc still has the old shorter description.
  2. Line 212CLAUDE.md expanded the main.dart reference to cover _startDaemon(), /v1/status polling, daemon kill-on-close, and macOS PATH injection; calf.mdc still has the old theme-only description.
  3. Line 12 (CLAUDE.md) — new paragraph about the embedded daemon binary in the .app bundle has no equivalent in calf.mdc's Project section.

Based on learnings, CLAUDE.md and .cursor/rules/calf.mdc should be kept equivalent.

Also applies to: 255-266, 295-295

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.cursor/rules/calf.mdc at line 147, Sync calf.mdc with CLAUDE.md by updating
the outdated documentation entries for main.go and main.dart to match the newer
descriptions. Specifically, adjust the main.go tree entry in the Project section
to mention stale-port takeover with cross-platform lsof/Unix and netstat/Windows
behavior, and expand the main.dart reference to include _startDaemon(),
/v1/status polling, daemon kill-on-close, and macOS PATH injection. Also add the
missing Project section note about the embedded daemon binary inside the .app
bundle so calf.mdc stays equivalent to CLAUDE.md.

Source: Learnings

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@backend/cmd/calf/main.go`:
- Around line 219-222: The listener scan in main should not use a loose
strings.Contains(line, target) check because it can match the wrong socket
entry. Update the matching logic in the loop that processes strings.Split(out,
"\n") so it anchors the port check to the local-address field parsed from
fields, rather than any substring in the full line, and keep the LISTENING
filter tied to the same entry. Use the existing target construction and the
fields slice in this block to ensure the PID lookup only selects the exact port
listener.
- Around line 64-80: The parent-death watchdog in the anonymous goroutine using
os.Getppid() only works on Unix, so it will not detect Flutter parent exit on
Windows. Update the watchdog logic in main.go to use a Windows-specific
parent-exit mechanism when running on Windows, such as tracking a parent process
handle or job object, while keeping the existing Unix path intact. Make the
platform-specific check explicit around the current ticker loop and stop() call
so the daemon shuts down reliably across OSes.

In `@backend/internal/runtime/lima.go`:
- Around line 158-162: The status response is leaking the daemon’s full PATH
through handleStatus by copying os.Getenv("PATH") into status.Log. Keep the
detailed PATH only in the existing limaLogger.Warn call, and change the
user-facing status.Log message to a sanitized limactl-not-found error that does
not include internal environment values. Use the handleStatus flow and the
limactl lookup block as the place to update the returned status payload.
- Around line 739-780: The docker proxy lifecycle in Lima is racing because
serveTCPProxy and removeDockerCLISocket access dockerProxy and proxyListener
from different goroutines without synchronization. Protect all reads and writes
to both fields with l.mu, build the http.Server before starting Serve, and
ensure Stop/removeDockerCLISocket cannot observe or leave behind a stale server
or listener reference.

In `@backend/internal/runtime/lima.yaml`:
- Around line 86-104: The docker-tcp-proxy service currently listens on all
guest interfaces via the Docker proxy setup in the lima.yaml systemd unit, which
exposes the Docker API more broadly than needed. Update the ExecStart for
docker-tcp-proxy to bind the socat TCP listener to loopback only so it serves
just the 127.0.0.1 forward, keeping the existing docker-tcp-proxy service and
portForwards configuration otherwise unchanged.

In `@CHANGELOG.md`:
- Around line 12-33: Rewrite the affected bullets in CHANGELOG.md to describe
user-visible behavior only, removing internal implementation terms like
httputil.ReverseProxy, lsof, netstat, limactl, nerdctl, docker, and any
protocol/tooling jargon. Keep the existing symbols/sections (Cross-platform
support, Changed, Fixed) but restate each item in plain user-facing language
that explains what improved for Linux, macOS, Windows, build/release, port
conflict detection, PATH setup, UI opening, daemon discovery, startup logging,
entitlements, container parsing, streaming/exec, and startup spinner behavior.

In `@CLAUDE.md`:
- Line 12: The bundle path description is incorrect: the daemon is not
`Contents/MacOS/calf` but `Contents/MacOS/calf-daemon`. Update the documentation
text in CLAUDE.md to reference the daemon binary by its actual bundled name, and
keep `Contents/MacOS/calf` reserved for the main Flutter executable so the
launch/cleanup description matches the packaging behavior.

In `@Makefile`:
- Around line 51-54: The release-linux target is building the daemon with the
host OS instead of a Linux target, so update the backend build step in
release-linux to explicitly set GOOS=linux and GOARCH=amd64, matching the
release-windows pattern. Keep the existing ui-linux dependency and copy/remove
steps, but ensure the calf-daemon binary produced by the backend build is always
a Linux binary before it is placed into the ui/build/linux/x64/release/bundle
bundle.

In `@ui/lib/main.dart`:
- Line 194: The try/catch in the daemon startup flow is swallowing all failures
in `main.dart`, which hides real bugs behind the expected “daemon not up yet”
path. Update the catch around the connection check to handle only the known
connection/timeout cases explicitly, and use the surrounding startup logic to
log or surface any unexpected exceptions instead of discarding them. Keep the
fix localized to the code path that probes the daemon so the expected retry
behavior stays intact while bad responses and JSON/parsing failures are visible.
- Around line 59-69: Update _findDaemon so it also resolves the Windows daemon
name fallback by checking for calf-daemon.exe in addition to calf-daemon, using
Platform.resolvedExecutable and the existing daemonPath logic. Remove or revise
the macOS .app bundle comment unless _findDaemon actually performs a
bundle-specific lookup, so the comments match the behavior in this helper.
- Around line 48-53: The daemon restart logic in the _daemonProcess.exitCode
callback can still schedule _startDaemon after shutdown and can also loop
endlessly on repeated startup failures. Add a shutdown guard in the widget/state
that is checked before calling Future.delayed and _startDaemon, and clear or
cancel any pending restart when dispose() or detached runs. Also update the
restart path around _startDaemon to use bounded retries with backoff and a stop
condition so failure states surface instead of continuously respawning the
daemon.
- Line 165: The startup status probe in main.dart is hardcoding the daemon URL
instead of using the shared configured base address. Update the status check in
the startup flow to build the endpoint from the same base URL source used by the
daemon listen address/config, so changes to the port or host stay in sync;
locate the probe near the url constant in main.dart and replace the duplicated
literal with the shared base URL path for v1/status.

---

Nitpick comments:
In @.cursor/rules/calf.mdc:
- Line 147: Sync calf.mdc with CLAUDE.md by updating the outdated documentation
entries for main.go and main.dart to match the newer descriptions. Specifically,
adjust the main.go tree entry in the Project section to mention stale-port
takeover with cross-platform lsof/Unix and netstat/Windows behavior, and expand
the main.dart reference to include _startDaemon(), /v1/status polling, daemon
kill-on-close, and macOS PATH injection. Also add the missing Project section
note about the embedded daemon binary inside the .app bundle so calf.mdc stays
equivalent to CLAUDE.md.

In `@backend/internal/runtime/nerdctl.go`:
- Around line 186-221: Add a brief comment in parseLabels to document the
limitation of the comma-separated parsing path: values containing commas will be
split and later fragments without '=' are ignored. Keep the existing logic in
parseLabels unchanged, but make the edge case explicit near the strings.Split(s,
",") / SplitN handling so future readers know this behavior is intentional and
tied to nerdctl output.
- Around line 37-51: Remove the unused nerdctlLine struct from the runtime
parsing code, since ParseContainerLines now relies on an inline struct and this
standalone type is no longer referenced. Clean up the type definition in
nerdctl.go and make sure any remaining parsing logic continues to use the inline
struct or another shared type only if it is actually needed elsewhere.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: a8491d9a-c40a-4a32-851d-e49610522c33

📥 Commits

Reviewing files that changed from the base of the PR and between 4573167 and a181d83.

📒 Files selected for processing (24)
  • .cursor/rules/calf.mdc
  • .github/workflows/ci.yml
  • .gitignore
  • CHANGELOG.md
  • CLAUDE.md
  • Makefile
  • ROADMAP.md
  • backend/api.test
  • backend/buildstore.test
  • backend/cmd/calf/main.go
  • backend/internal/runtime/lima.go
  • backend/internal/runtime/lima.yaml
  • backend/internal/runtime/nerdctl.go
  • backend/internal/runtime/runtime.go
  • backend/runtime.test
  • backend/version/version.go
  • ui/lib/app_shell.dart
  • ui/lib/main.dart
  • ui/lib/platform/open_url.dart
  • ui/lib/screens/compose_group_detail_screen.dart
  • ui/lib/screens/container_detail_screen.dart
  • ui/lib/screens/containers_screen.dart
  • ui/macos/Runner/Release.entitlements
  • ui/pubspec.yaml
💤 Files with no reviewable changes (1)
  • ui/lib/app_shell.dart

Comment thread backend/cmd/calf/main.go Outdated
Comment on lines +64 to +80
go func() {
ticker := time.NewTicker(5 * time.Second)
defer ticker.Stop()
for {
select {
case <-ctx.Done():
return
case <-ticker.C:
if os.Getppid() == 1 {
logger.Warn("parent process died, shutting down")
stop()
return
}
}
}
}()

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🌐 Web query:

Windows detect parent process exit Go child process orphan job object

💡 Result:

To detect the termination of a parent process or prevent orphaned child processes in Go on Windows, you can utilize the Windows API via the syscall or golang.org/x/sys/windows packages. To detect if a parent process has exited, the most reliable approach is to obtain a process handle for the parent and wait for it to signal. Since OpenProcess can succeed even if a process has exited (if other handles exist), you must correctly handle synchronization: 1. Obtain a handle to the parent process with SYNCHRONIZE access: h, err:= windows.OpenProcess(windows.SYNCHRONIZE, false, uint32(ppid)) 2. Use WaitForSingleObject to wait on that handle: windows.WaitForSingleObject(h, windows.INFINITE) 3. For an authoritative check that the process has actually terminated rather than just being in a zombie-like state, verify the exit code is no longer STILL_ACTIVE (259): var exitCode uint32 windows.GetExitCodeProcess(h, &exitCode) To prevent orphan processes, you should use Job Objects. Job Objects allow you to group processes and enforce termination policies. Specifically, the JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE flag causes all processes associated with the job to be automatically terminated when the last handle to the job object is closed. Steps to implement this in Go: 1. Create a job object: Use windows.CreateJobObject(nil, nil). 2. Configure the termination policy: Use windows.SetInformationJobObject with the JOBOBJECT_EXTENDED_LIMIT_INFORMATION structure, setting the BasicLimitInformation.LimitFlags field to JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE. 3. Assign child processes to the job: Use windows.AssignProcessToJobObject after starting the child process. By grouping child processes into such a job object, you ensure that if your parent Go process crashes or exits, the OS will automatically clean up the orphaned child processes [1][2]. Top results: [3][1][2]

Citations:


Parent-death watchdog is Unix-only. os.Getppid() == 1 depends on Unix re-parenting to PID 1, so this goroutine never fires on Windows and the daemon can be orphaned when the Flutter parent exits. Add a Windows-specific parent-exit mechanism, such as a parent process handle or job object.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/cmd/calf/main.go` around lines 64 - 80, The parent-death watchdog in
the anonymous goroutine using os.Getppid() only works on Unix, so it will not
detect Flutter parent exit on Windows. Update the watchdog logic in main.go to
use a Windows-specific parent-exit mechanism when running on Windows, such as
tracking a parent process handle or job object, while keeping the existing Unix
path intact. Make the platform-specific check explicit around the current ticker
loop and stop() call so the daemon shuts down reliably across OSes.

Comment thread backend/cmd/calf/main.go Outdated
Comment thread backend/internal/runtime/lima.go
Comment thread backend/internal/runtime/lima.go
Comment thread backend/internal/runtime/lima.yaml
Comment thread Makefile
Comment thread ui/lib/main.dart
Comment thread ui/lib/main.dart
Comment thread ui/lib/main.dart Outdated
Comment thread ui/lib/main.dart Outdated
…nhancements

- Revised CHANGELOG to reflect new features and improvements for version 0.7.0, including cross-platform support and automated builds.
- Updated CLAUDE.md to clarify the daemon's integration within the Flutter app bundle.
- Modified Makefile to ensure proper cross-compilation for Linux and Windows targets.
- Enhanced backend logic for port conflict detection and improved error handling during daemon startup.
- Implemented a watchdog mechanism for monitoring the parent process across platforms.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@backend/cmd/calf/watchdog_windows.go`:
- Around line 20-31: The goroutine in watchdog_windows.go leaks and can race
with CloseHandle because it waits forever in WaitForSingleObject(handle,
windows.INFINITE) without observing ctx. Update the watchdog logic in the
shutdown watcher function to use a finite-timeout polling loop that periodically
calls WaitForSingleObject with a short timeout, checks ctx.Done() between polls,
and exits cleanly on cancellation. Keep the existing stop() behavior when the
parent process is detected, but make the inner goroutine context-stoppable so it
cannot outlive the outer function.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: fc9319fc-1d9d-4f5a-ba34-6db348dffb88

📥 Commits

Reviewing files that changed from the base of the PR and between a181d83 and 700d4e7.

⛔ Files ignored due to path filters (1)
  • backend/go.sum is excluded by !**/*.sum
📒 Files selected for processing (12)
  • .cursor/rules/calf.mdc
  • CHANGELOG.md
  • CLAUDE.md
  • Makefile
  • backend/cmd/calf/main.go
  • backend/cmd/calf/watchdog_unix.go
  • backend/cmd/calf/watchdog_windows.go
  • backend/go.mod
  • backend/internal/runtime/lima.go
  • backend/internal/runtime/lima.yaml
  • backend/internal/runtime/nerdctl.go
  • ui/lib/main.dart
✅ Files skipped from review due to trivial changes (1)
  • CHANGELOG.md
🚧 Files skipped from review as they are similar to previous changes (6)
  • backend/internal/runtime/nerdctl.go
  • backend/internal/runtime/lima.yaml
  • backend/internal/runtime/lima.go
  • Makefile
  • backend/cmd/calf/main.go
  • ui/lib/main.dart

Comment thread backend/cmd/calf/watchdog_windows.go
…and wait group

- Enhanced the watchdog mechanism in `watchdog_windows.go` to utilize a goroutine and sync.WaitGroup for better management of the parent process monitoring.
- Implemented a polling mechanism to check the parent process status, allowing for graceful shutdown when the parent process exits.
- Improved error handling and logging for the parent process wait operation.
@enegalan enegalan merged commit 2b62780 into main Jul 8, 2026
3 checks passed
@enegalan enegalan deleted the feat/embed-daemon branch July 8, 2026 02:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Embed Go daemon inside Flutter .app bundle

1 participant