CS-10622: Reimplement boxel realm track command#4657
Draft
FadhlanR wants to merge 2 commits intocs-10623-reimplement-boxel-realm-watch-commandfrom
Draft
CS-10622: Reimplement boxel realm track command#4657FadhlanR wants to merge 2 commits intocs-10623-reimplement-boxel-realm-watch-commandfrom
boxel realm track command#4657FadhlanR wants to merge 2 commits intocs-10623-reimplement-boxel-realm-watch-commandfrom
Conversation
Rename watch-lock.ts to sync-lock.ts and parameterize by LockKind = 'watch' | 'track'. acquireSyncLock refuses if either the same kind is held by a live process or the *other* kind is — running `boxel realm track` and `boxel realm watch` against the same dir would otherwise create a push/pull loop (track pushes -> server changes -> watch pulls -> mtime moves -> track pushes again). A stale same-kind lock is overwritten as before; a stale other-kind lock is left alone (its owner reclaims it on next run). Watch updated to call acquireSyncLock(localDir, 'watch', realmUrl). Two new tests in realm-watch.test.ts cover the cross-guard: - refuses when a live track lock exists at the same localDir - ignores a stale track lock from a dead pid and proceeds Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Port the legacy `boxel track` command from the standalone boxel-cli repo into the monorepo as `boxel realm track`. Track is the write-side counterpart to `boxel realm watch`: it watches the local filesystem via fs.watch + 2s polling, debounces edits, hash-gates noop saves, creates checkpoints in .boxel-history, and with --push batch-uploads add/update changes to the realm via /_atomic. Push semantics mirror RealmPusher's: addPaths discriminates op:add vs op:update by manifest membership so the atomic endpoint can surface 409 from concurrent creates instead of silently overwriting peer changes. Modules (.gts/.ts/.js) are sorted before instances (.json) within the atomic doc so definitions land before instances at write time. Files whose push fails transiently are retained in pending for the next cycle. Deletions on --push are deferred: - `op: 'remove'` is defined in runtime-common/atomic-document.ts but not implemented server-side (filterAtomicOperations strips no-data ops, the handler iterates only add/update). - Per-file DELETE is out of scope for this PR. Track records deletions in the local checkpoint and logs a verbose skip on push. Filed as follow-up. Track's lock (.boxel-track.lock) interlocks with watch's via the generalized sync-lock module landed in the previous commit. Integration tests cover 15 scenarios across local-only behavior, --push, and lock orchestration. See docs/cs-10622-... for the full plan; the doc will be removed in the cleanup commit before merge. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Port
boxel realm trackfrom the standalonecardstack/boxel-cliinto the monorepo asboxel realm track. Track is the write-side counterpart toboxel realm watch(PR #4554, CS-10623): it watches the local filesystem via fs.watch + 2s polling, debounces edits, hash-gates noop saves, creates checkpoints in.boxel-history/, and with--pushbatch-uploads add/update changes to the realm via/_atomic.Marquee workflow: collaborate on a card while teammates view changes in the web UI.
What's in this PR
RealmTracker extends RealmSyncBase— fs.watch + 2s polling, debounced flush, min-interval gate, hash-gated noop suppression, retry-on-fail for transient push failures.boxel realm track <local-dir> <realm-url>with-d/--debounce,-i/--interval,-q/--quiet,-p/--push,-v/--verbose,--realm-secret-seed.sync-lock.ts(replaceswatch-lock.ts) withLockKind = 'watch' | 'track'and bidirectional cross-guard. Track refuses if.boxel-watch.lockis held by a live PID; watch refuses if.boxel-track.lockis. Prevents the push/pull loop on the same dir.RealmPusher's:addPathsdiscriminates op:add vs op:update by manifest membership so the atomic endpoint surfaces 409 from concurrent creates. Modules (.gts/.ts/.js) are sorted before instances (.json) inside the atomic doc.--push, and lock orchestration.Out of scope (deferred)
Atomic deletions on
--pushop: 'remove'is defined inruntime-common/atomic-document.ts:6but not implemented server-side:filterAtomicOperations(atomic-document.ts:35) strips no-data ops, and the handler atrealm.ts:1498only iterates add/update. Implementing it requires non-trivial changes toruntime-common/realm.ts(validation, dispatch, indexing/invalidation hooks) and is its own ticket.Track records deletions in the local checkpoint and logs a verbose skip on push. The deferred-deletion behavior matches the legacy
track.tsTODO.boxel realm stop(CS-10624)Track lays down
.boxel-track.lockso a futurerealm stopcan use it as a discovery source.Linear
CS-10622
Plan doc
See
docs/cs-10622-boxel-realm-track-plan.md(added in this branch; will be removed in the cleanup commit before merge — same convention as CS-10623).Depends on
PR #4554 (CS-10623) — this branch is based on
cs-10623-...so the diff shows only the track delta. Once #4554 merges, this PR rebases ontomain.Test plan
pnpm --filter @cardstack/boxel-cli build— succeeds (219KB bundle).pnpm --filter @cardstack/boxel-cli lint— clean.pnpm --filter @cardstack/boxel-cli exec tsc --noEmit— clean.vitest run tests/integration/realm-track.test.ts— 15/15 passing (add/modify/delete/burst/min-interval/hash-gate/push add+update/.gts-first/skip-deletions/no-manifest/409-retain/lock-self/lock-cross/stale-lock-overwrite/abort-flush). Full integration suite also runs the new realm-watch cross-guard cases (refuses-when-track-live, ignores-stale-track-lock).boxel realm track --helpdocuments-i,-d,-q,-p,-v,--realm-secret-seed.boxel realm sync ./scratch <staging-url>(manifest established).boxel realm track ./scratch <staging-url> --push -v..gtsand matching.json; confirm local checkpoint, atomic POST visible in verbose output with.gtsop listed first, manifest hash updated.boxel realm watch <staging-url> ./scratchrefuses with.boxel-track.lockconflict.deletedand verbose log showsSkipping delete on push (deferred). Remote file remains.Follow-up tickets to file
op: 'remove'so track andrealm push --deletecan use a single atomic doc for delete operations.🤖 Generated with Claude Code