Repository navigation
feat: refresh the viewport by itself when the store changes - #401
Conversation
The placement quaternion composes into the member body mesh (the section frame is `R_placement . (axis x up/roll)`; the roll applies about the member axis first), so the stored mesh is the detailed outline in the composed frame and the scene snapshot carries the translation alone. Quantities and the analytic dimensions are invariant under the rotation; a hostile quaternion is refused by code (`INVALID_ARGUMENT` / `NON_FINITE_NUMBER`), never a crash. `update-element` gains the `--orientation-xyzw` patch (validated and fingerprinted, replayable), the CLI grows the matching flag, and the viewport draws inclined and rolled beams from the same snapshot shape as before. The UI's default-workspace helpers (`source::reload` growth, `default_workspace_at`) come with the viewport test suites that use them: those suites grew the placement coverage and share the files. Co-Authored-By: Claude Code <[email protected]>
…sed image dependency Review follow-up. `patch_plate` accepted a non-identity orientation patch, but a committed rotated plate refuses every later read (plate-inspect, view-snapshot, view-export and pick all build the scene, which raises UNSUPPORTED_GEOMETRY) - the write path must not admit a state the read paths reject. A plate now refuses a non-identity orientation patch with UNSUPPORTED_GEOMETRY, mirroring `create_plate`; the identity stays the documented reset, the unit test pins the refusal, and the model doc comment no longer promises the patch for a plate. Also drops the `image` dependency from beamcraft-ui: nothing references it (the icon PNG decode goes through `eframe::icon_data` in theme.rs, unchanged from master), so the crate and its Cargo.lock entry go. Co-Authored-By: Claude Code <[email protected]>
The window watches the store's revisions directory and reloads on a head change another process committed (the CLI or an agent), so the view follows the store without the R key; a reload whose framing changed fits the camera, so new content comes into view. A missing revisions directory is "no signal yet", not an error, and a watcher that cannot start only loses the automatic reload - the R key keeps working either way. Co-Authored-By: Claude Code <[email protected]>
Resolves the add/add collision on the two skill docs: upstream received them through the #242 squash in their pre-#156 wording, this branch carries the post-#156 wording, which is kept. Co-Authored-By: Claude Code <[email protected]>
axisrow
left a comment
There was a problem hiding this comment.
PR #401 - viewport self-refresh on store change - CHANGES REQUESTED
One required change, inline: the commit carries two files from the still-unmerged #242. On the base master there is no skills/ tree at all - skills/beamcraft-model/reference/view.md and skills/open/SKILL.md exist only on the #242 branch, and here they arrive as a partial copy (the reference file without its SKILL.md, the open skill without the plugin manifests). If this merges first, master gains orphan docs for a plugin it does not have; if #242 merges first, the same lines conflict across the two branches. Drop both files from this PR: the auto-refresh sentence belongs in #242 (or a tiny docs PR after both merge), and the apps/beamcraft-ui/README.md note - the only doc that exists on master - already covers the feature here.
The watcher itself reviewed clean:
revisions_stampis a directory mtime read; a missing revisions directory is None (no signal, no error), and the first commit into an empty store moves None to Some - both pinned by the new test.spawn_watchpolls a stat every 500 ms on one unnamed-workspace thread, exits when the receiver drops, and a failed start only costs the automatic reload -Rkeeps working; the right ladder rung versus a notify dependency.- The signal feeds the same
reloadpath as the R key, so the existing debounce (reload_queued) and the framing-driven refit apply unchanged; extra queued messages collapsing into one refresh is the wanted behaviour. - Stamp granularity and GC: a history compact that prunes old revision files also moves the stamp and fires one harmless reload of an unchanged head.
- README note matches the behaviour.
With the two files dropped, this is an approve.
axisrow
left a comment
There was a problem hiding this comment.
PR #401 - viewport self-refresh on store change (round 2) - APPROVED
The round-1 finding is resolved: the branch merged upstream master, so skills/beamcraft-model/reference/view.md and skills/open/SKILL.md now come from the base (#242 is merged there) and the PR delta on them is only the two auto-refresh sentences. No orphan files, nothing duplicated, and the merge dropped nothing from the feature (the watcher files are byte-identical to the round-1 head, which reviewed clean: stamp semantics, polling thread lifecycle, shared reload path with its debounce and framing-driven refit, GC-triggered stamp moves firing one harmless refresh).
No change requested. Process note, body only: the branch still carries the unmerged #243 as its first commits, as the description discloses, and upstream master has since moved (#241); after #243 merges, the promised rebase collapses this diff to the feature plus the two doc sentences.
axisrow
left a comment
There was a problem hiding this comment.
PR #401 - viewport self-refresh on store change - APPROVED (re-request, unchanged head)
The head is the same commit (5d6eafd) this reviewer approved minutes ago (GitHub review 5643274150): the watcher feature, its test, the README note and the two skill-doc sentences, with the upstream merge bringing #242 into the base. No new commits, no new evidence, verdict unchanged: approved, no change requested.
Upstream merged the placement rotation (#243) and the nextest profile (#241), so the placement commit this branch carried collapses into master and the branch's own diff is the auto-refresh feature alone. The app.rs add/add resolves to this branch's side: the same placement changes plus the watcher. Co-Authored-By: Claude Code <[email protected]>
axisrow
left a comment
There was a problem hiding this comment.
Reviewed d959e9d against a17e694 (4 files: apps/beamcraft-ui/src/app.rs plus the three doc files). Approving — no changes requested.
The design is right for the job: the watcher polls the revisions/ directory stamp every 500 ms instead of pulling in a platform FS-event dependency, and a commit is exactly one new hard-linked record file appearing in that directory (crates/host/src/revision.rs), so a moved directory mtime is a faithful "the head may have moved" signal. A missing directory resolving to None is treated as no signal, so an empty store stays calm until the first commit creates it.
Verified:
store_changedroutes through the sameself.reload()as theRkey, so an in-flight reload queues viareload_queued(app.rs:707) and the framing-change refit comes from the existingneeds_refitpath inpoll_reload.- The thread unwraps nothing and exits on its own when the window closes (
tx.senderrors once the receiver drops); a watcher that cannot start degrades to the manualRonly, as documented. - A stamp move from
.tmp-*crash leftovers or from the UI committing to its own store costs at most one extra reload thatsource::reloadresolves toRefreshed::Unchanged(snapshot-token dedupe), so the camera is kept. - The new test matches the repo fixture convention (pid + nanos in the temp name) and pins both documented behaviors: no directory = no signal, a new record file moves the stamp.
Two non-blocking observations, both fine as is:
- A commit landing in the tiny window between the initial load and the watcher first stamp read is missed until the next commit (or
R); closing it would mean capturingseenon the main thread before spawning, not worth the coupling. - Only one queued message is consumed per frame, so a burst of commits costs one extra reload on the next frame — converges, harmless.
The doc updates (ui README, skills view/open) match the implemented behavior, including the honest "accepted by the person looking at it" caveat.
Blogbotana
left a comment
There was a problem hiding this comment.
Please address these two issues before merging:
-
[P2] Detect the committed head rather than relying solely on directory mtime.
revisions_stamp()compares onlymetadata.modified(). Filesystems with coarse timestamp resolution can give successive directory changes the same timestamp, so a completed commit can remain invisible to the viewport until another change or a manual reload. Please compare the committed head identity/sequence (or another reliable revision token). The new test also assumes that a 10 ms sleep changes the timestamp; please add coverage that does not depend on timestamp precision and verifies that a changed head triggers a reload. -
[P2] Stop the watcher when its owning model/window is dropped. The worker discovers that its receiver has been dropped only when
tx.send()fails, and it sends only after the stamp changes. If the old store remains unchanged, the detached thread keeps waking and checking the filesystem indefinitely. Repeated model opens can accumulate these workers. Please add an explicit shutdown mechanism tied to the owner's lifetime, and test that the watcher exits even when no filesystem change occurs.
The existing asynchronous reload path is reused appropriately, and CI is green at d959e9d21b06d77894b1a1b7c9bc1427060090ad. These requests concern reliable change detection and watcher lifetime; I have not run the native UI locally.
…he window Review follow-up. The stamp trusted the revisions directory's mtime, which coarse-timestamp filesystems can keep identical across two commits; the token is now the directory's entry count plus the newest record file name, so every commit is visible. The watcher also lives only as long as the window: it waits on a shutdown channel instead of sleeping, and dropping the handle ends the thread even if the store never changes again, so repeated model opens no longer accumulate threads. Co-Authored-By: Claude Code <[email protected]>
Summary
The viewport refreshes by itself when the store changes: the window watches the store's
revisions directory and reloads on a head change another process committed (the CLI or an
agent), so the view follows the store without the
Rkey. A reload whose framing changed fitsthe camera, so new content comes into view. A missing revisions directory is "no signal yet",
not an error, and a watcher that cannot start only loses the automatic reload.
Earlier this branch was stacked on #243; that PR has since merged, so this PR's diff is now the
auto-refresh feature alone (4 files).
Tests
cargo fmt --check,cargo clippy --workspace --all-targets -- -D warningsand thebeamcraft-uisuite pass locally on this head.Notes
view,open) that describe thewindow's behavior.
🤖 Generated with Claude Code