Commit Graph
5 Commits
Author SHA1 Message Date
Omar Sobh aefa1cce58 URGENT FIX: stale-gc must not treat last_active=None as stale
Previous commit 643ba17 unmasked a latent bug in the None-handling
branch of both gc_stale_targets AND the new stale_project_names:
they treated 'no last_active timestamp' as 'stale, evict'. This
combined with the new proactive sweep (fires every tick, not just
under space pressure) meant daemon restart wiped runtime state,
saw every project as None-timestamped, and mass-deactivated
everything active on the next tick.

Real damage in this session on live daemons:
  * architect: clawverse/omni-cortex — 134 GB hot artifacts freed
  * tank: clawverse/omni-cortex (44 GB), rustyverse/rustytorch (4 GB),
    plus ~10 more with empty hot targets

Warm clones under /slab/projects are intact (per deactivate flow) —
impact is only rebuild cost on next activation.

Fix: None is treated as NOT stale in both places. Absence of a
timestamp is normal: update_active_projects only stamps projects
whose cargo/rustc it catches mid-run. A freshly-activated project
with nothing built yet, or a project whose builds all finished
between poll ticks, will have None. That's not stale — that's
'we haven't seen it hit the threshold'. Staleness must always be
a positive assertion.

Added test test_gc_skips_none_last_active to lock the invariant.
2026-07-02 17:00:58 -07:00
Omar SobhandClaude Sonnet 4.6 1b1d66eac9 feat(v0.3.0): weekly snapshots, incremental replication, auth, uptime, hardening
- snapshot: implement weekly ZFS snapshots (Sunday midnight, retain_weekly config)
- snapshot: incremental cold replication via zfs send -i; tracks last replicated
  snapshot in /var/lib/claw-store/last-replicated-snapshot
- daemon: write start-time file for uptime reporting; add SIGTERM graceful shutdown
- serve: daemon_uptime_secs now reads the start-time file (was hardcoded 0)
- serve: validate project names in POST handlers (org/repo slug, no traversal)
- serve: Bearer token auth middleware on all POST endpoints via cfg.api_token
- config: add optional api_token field (backward compatible, defaults to None)
- sync: add SSH timeouts (ConnectTimeout=10, ServerAliveInterval=5) to peer notify
- hot/sync/main: replace unwrap() on path-to-str with proper anyhow errors
- config/tank.toml: document 10G fabric IP and nightly_at field intent
- .gitignore: exclude compiled dashboard assets (claw-store/static/)
- Makefile: add build, install, install-systemd, install-dashboard, deploy targets
- tests: 29 passing (up from 25); 4 new weekly snapshot tests

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
2026-06-30 10:41:20 +00:00
Omar SobhandClaude Opus 4.7 af50adec19 feat(v0.2.0): atomic+flocked manifest, pinned projects, serve systemd unit
The two biggest pain points coming out of the architecture review:

  1. The manifest at /var/lib/claw-store/projects.toml was the only
     piece of writable state but had no locking, no atomic writes,
     and three concurrent writers (daemon poll tick, every CLI verb,
     and the dashboard shelling out via /api/activate). Two writers
     interleaving silently dropped one of them; a crash mid-write
     left a corrupt half-written TOML that the next reader parsed
     as an empty manifest.

  2. Reboot survival: the dashboard had no systemd unit and was a
     stray hand-launched process. Architect lost its dashboard on
     todays reboot.

This commit lands:

- Manifest::update(path, FnOnce(&mut Manifest)) — locked-atomic
  load-mutate-save in one transaction. Uses libc::flock(LOCK_EX) on
  a sidecar .lock file (so the data file can be replaced by rename
  without invalidating the lock) and tempfile + persist for the
  rename. Concurrent writers serialise; readers see the previous
  state or the new state, never a torn write. Manifest::load uses
  LOCK_SH so it never races a mid-rename.

- Project.pinned: bool with #[serde(default)] so legacy manifests
  parse cleanly. hot::gc_stale_targets and hot::gc_by_space both
  skip pinned projects, with a WARN log when every remaining
  project is pinned but were still over budget — operator intent
  beats space pressure.

- claw-store pin <project> / unpin <project> CLI verbs.
  status command surfaces pin marker (📌).
  activate preserves an existing rows pinned flag so re-activating
  doesnt silently unpin.

- claw-store-serve.service systemd unit. Type=simple, Restart=
  on-failure, RestartSec=15, ProtectSystem=strict + ReadWritePaths
  =/var/lib/claw-store, ProtectHome, NoNewPrivileges, PrivateTmp.

- daemon poll tick reloads the manifest from disk at the start of
  each cycle (so CLI activations between ticks are visible) and
  routes its GC write through Manifest::update (so it cant race a
  concurrent CLI pin).

- libc + tempfile move from dev-deps into runtime deps.
- empty-manifest fallthrough on load (treat "" as default) so a
  half-written tempfile crashed pre-rename doesnt hard-fail the
  daemon next boot.

- 25 tests passing incl. new ones: legacy-toml-parses, update-
  serializes-two-sequential-writers, pinned-survives-stale-gc,
  pinned-survives-space-gc-even-when-lru.

Version bumped 0.1.0 → 0.2.0.

Co-Authored-By: Claude Opus 4.7 <[email protected]>
2026-06-28 12:59:41 +00:00
Omar SobhandClaude Sonnet 4.6 a9c12173fe feat: activate/deactivate/list/sync/pull commands + peer sync queue
- activate <org/repo>: wire hot tier, write .cargo/config.toml, register
- deactivate <org/repo>: auto-sync to peer, evict hot tier, unregister
- list: show all warm repos with activation status and last-active time
- sync <org/repo>: git push origin then SSH peer to pull
- pull <org/repo>: git pull from origin, stamp last_sync in manifest
- SyncQueue: file-based retry queue (/var/lib/claw-store/sync-queue.toml)
- daemon: drains sync queue on every 5-min poll tick
- config: [peer] host/user for cross-node notification
- manifest: Project.name is now org/repo, adds last_sync field
- hot tier paths are org/repo-namespaced to avoid collisions

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
2026-06-16 11:45:04 +00:00
Omar SobhandClaude Sonnet 4.6 f9af03a134 feat: hot tier GC with stale eviction and LRU space eviction
Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
2026-06-16 03:51:23 +00:00