Skip to content

fix(cli): stop daemon before reset and show progress screen (#1494)#1514

Merged
Aaronontheweb merged 3 commits into
netclaw-dev:devfrom
Aaronontheweb:fix/win-init-reset-daemon-lock
Jun 29, 2026
Merged

fix(cli): stop daemon before reset and show progress screen (#1494)#1514
Aaronontheweb merged 3 commits into
netclaw-dev:devfrom
Aaronontheweb:fix/win-init-reset-daemon-lock

Conversation

@Aaronontheweb

Copy link
Copy Markdown
Collaborator

Problem

On Windows, netclaw init → Full reset fails with:

Reset failed: The process cannot access the file 'daemon-2026-06-26.log' because it is being used by another process.

The daemon netclawd holds the log file handle open, and Directory.Delete(path, recursive: true) aborts on the first locked file. The operator gets stuck on a confirmation screen with no way forward.

Fix

1. Stop the daemon before deletion

Inject DaemonManager into InitExistingInstallViewModel and call StopAsync('factory-reset') before any Directory.Delete() calls. This releases the file handles so Windows lets the deletion proceed.

Stop is best-effort — if the daemon isn't running or is externally managed (Docker container supervisor), it's silently ignored.

2. Show a progress screen with staggered checkmarks

Instead of confirming → instant failure, the operator now sees a dedicated progress screen:

┌─ Netclaw Setup ──────────────────────────────────────────────────┐
│                                                                  │
│   Resetting…                                                     │
│                                                                  │
│   Stopping daemon…                                               │
│   Deleting data…      ✓ Daemon stopped                           │
│                       ✓ Data purged                              │
│                       ✓ Purge complete                           │
│                                                                  │
│   [Ctrl+Q] Quit                                                  │
└──────────────────────────────────────────────────────────────────┘

Each step fades in as it completes — the current step gets a spinning dots glyph, completed steps get a green checkmark, pending steps are dimmed. After purge completes there's a brief 600ms pause so the user sees the ✓, then the wizard launches automatically.

Key properties:

  • Non-interactive: Once started, the operator can't interrupt the flow (Escape is a no-op during progress). Only Ctrl+Q exits.
  • Best-effort daemon stop: If StopAsync throws (not running, external supervisor), the delete still proceeds.
  • Same confirmation flow: The double-confirm screen is unchanged — operator still sees the warning and must click 'Yes' twice.

Files changed

  • src/Netclaw.Cli/Tui/InitExistingInstallViewModel.cs — Added DaemonManager dependency, Progress phase, RunResetAsync() lifecycle, and progress-state properties
  • src/Netclaw.Cli/Tui/InitExistingInstallPage.cs — Added BuildProgressScreen() with staggered checkmarks and spinner, updated key bindings for progress phase
  • src/Netclaw.Cli.Tests/Tui/InitExistingInstallViewModelTests.cs — Added FakeSupervisor inner class and wired it into the Create() factory to satisfy the new constructor signature

…dev#1494)

On Windows, `netclaw init` → Full reset fails because the daemon holds
the log file handle open, and `Directory.Delete(path, recursive: true)`
bails on the first locked file.

Fix:
- Inject `DaemonManager` into `InitExistingInstallViewModel` and call
  `StopAsync('factory-reset')` before any deletions to release file handles.
  Stop is best-effort — if the daemon isn't running or is externally managed
  (Docker container supervisor), it's silently ignored.

- Show a progress screen with staggered checkmarks so the operator sees
  what's happening: daemon stop → data deletion → purge complete, with a
  spinning dots glyph on the current step and green checkmarks for completed
  steps. After completion the wizard auto-launches.

Test updates:
- Added FakeSupervisor to InitExistingInstallViewModelTests to satisfy the
  new DaemonManager constructor parameter (matching HealthCheckStepViewModelTests
  pattern).
@Aaronontheweb Aaronontheweb added the tui Terminal UI (Termina) issues label Jun 27, 2026
@Aaronontheweb
Aaronontheweb merged commit 20301a3 into netclaw-dev:dev Jun 29, 2026
15 checks passed
@Aaronontheweb
Aaronontheweb deleted the fix/win-init-reset-daemon-lock branch June 29, 2026 19:06
Aaronontheweb added a commit that referenced this pull request Jun 30, 2026
… host crash (#1525)

#1494/#1514 moved the install reset onto a background Task.Run thread that publishes
progress by writing four ReactiveProperty instances (StatusMessage, CurrentProgressStep,
ProgressMessage, CanQuitProgress). In production those writes marshal onto the single
Termina render-loop thread, but an unbound view model (unit tests) runs them inline on the
background thread while the test thread subscribes/disposes the same property.

R3's ReactiveProperty is not thread-safe: its value-set walks the observer linked list
lock-free while Subscribe/Dispose mutate that list under a lock, so the two race and can
corrupt the list into a cycle -> infinite loop -> the reset Task never completes and the
xunit host is killed by the watchdog (flaky 'Test host process crashed', no stack trace,
Ubuntu-only; the same commit was seen both green and red). R3's documented remedy for
shared state written from multiple threads is SynchronizedReactiveProperty, which
serializes set/subscribe/dispose on one monitor (uncontended in production). The declared
type stays ReactiveProperty<T> so the page and all readers are unchanged.

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

Labels

tui Terminal UI (Termina) issues

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant