Skip to content

feat: resolve the focus window's folders below the scope root - #945

Merged
FSM1 merged 3 commits into
mainfrom
feat/917-focus-window-folder-refresh
Aug 2, 2026
Merged

feat: resolve the focus window's folders below the scope root#945
FSM1 merged 3 commits into
mainfrom
feat/917-focus-window-folder-refresh

Conversation

@FSM1

@FSM1 FSM1 commented Aug 1, 2026

Copy link
Copy Markdown
Owner

Problem

Nothing resolved a folder record below the scope root on a device that did not author it, so such a device rendered every subtree empty — indefinitely.

  • spawn_resolve_tick_loop refreshed the vault root only; refresh_base_from_outcome to project_root lifts direct children only.
  • The only other writer into the base below the root was the drain's self-adopt — state this device published itself.
  • Command::SetFocus fell through to EngineError::Unimplemented, which apps/web/src/engine/snapshotStore.ts turns into a committed error state on every navigation.
  • ChildAdopter's single caller was read_content, which is file-only.
  • focus_set / FocusWindow / FocusTarget::Folder were pure planning with no caller.

blueprint/engine.md "Sync core" specifies both halves — the focus-window tick and the on-access refresh past the staleness threshold. Neither was wired.

Change

  • crates/engine/src/net/focus.rs (new)FolderRefresh resolves each focus folder's own record cache-first, passes the ChildAdopter gate on this device's floors, and merges the gate-passing body with project_folder. A gate rejection is fail-closed: last-known-good stands and no floor moves. The pass merges root-ward, so a parent that dropped a child unlinks it before the pass would project into it, and it reads no clock — the caller stamps.
  • resolve_child in net/child.rs — the Adopted / Current / NoUpdate + at-floor re-open walk, previously duplicated. Both read paths that descend below the scope root (a file's content read and this refresh) now share it, so neither can drift on which outcome is staleness and which is a fail-closed violation.
  • Command::SetFocus records the focus window and refreshes it immediately, but only for folders past the staleness threshold — navigation is the tick model's second trigger source, and a repeat visit renders state already held. Stamps record attempts, so a folder that never gate-passes cannot turn every navigation into a fresh endpoint fan-out; the poll leg still refreshes the window unconditionally, so recovery is automatic.
  • The resolve tick refreshes the same window every pass, after the root leg and before the drain, so the queue rebases onto the deepest state the pass reconciled.
  • focus_folders / focus_folders_due in sync/tick.rs — the window's folder targets below the scope root, and the threshold filter. The root rides the vault-pointer leg, never the child gate.
  • project_folder reports whether the merge moved the base, so re-merging the same body every tick does not repaint the host. Snapshot::link reports whether it established or raised a link, so the projection no longer predicts it; project_root and refresh_base_from_outcome forward the report.

No new crypto, no new seam, no TypeScript change — setFocus was already plumbed through crates/wasm and packages/client, and the web store already pulls on success.

Tests

crates/engine/tests/write_plane.rs, gated by Engine Tests (cargo test -p cipherbox-engine):

  • a_second_device_lists_below_the_scope_root_once_it_focuses_there — the facade half of engine: assert the two-device round trip on a create below the scope root #895's round trip. A cold second device lists the depth-2 child with device A's name and node id after setting focus on the depth-1 parent, having listed nothing there before.
  • a_planted_focus_record_never_renders — fail-closed on all three child-gate bindings the pass rests on. A legitimate concurrent record reconciles first, so the negatives cannot pass on a leg that never ran; then a foreign node id, a foreign scope, and a file body behind a folder ref are each planted at the focused name at a strictly newer sequence, and last-known-good is pinned through all three.
  • an_unreachable_record_plane_leaves_the_focused_folder_rendering — every endpoint failed, the folder keeps rendering off the cache rather than clearing.
  • navigation_re_resolves_a_folder_only_past_the_staleness_threshold — a repeat visit inside the threshold renders held state; the same navigation past it reconciles.

Plus crates/engine/tests/facade.rs::set_focus_records_a_window_with_nothing_to_resolve, sync/tick.rs unit tests for focus_folders and focus_folders_due, and sync/project.rs::project_root_reports_a_change_only_when_the_body_moves_the_base.

Revert check: stubbing focus_folders to return an empty list fails exactly the four new write_plane tests and nothing else.

Verification

All exit 0:

  • cargo fmt --all
  • cargo clippy --workspace --all-targets -- -D warnings — clean
  • cargo check --workspace --all-targets
  • cargo check -p cipherbox-wasm --target wasm32-unknown-unknown --all-targets
  • cargo test --workspace — 32 suites green, 0 failures
  • pnpm -r --if-present run typecheck — api, client, web all clean
  • pnpm -r --if-present run test — client, api 177, web 67 all pass
  • npx eslint . — clean

Review gates

/simplify, /security-review and /crypto-privacy-review were each run on git diff main...HEAD. Security and crypto returned no findings. The second commit folds in the real items from all three: the shared resolve_child walk, the attempt-stamped on-access damper, Snapshot::link's change report, the root-ward merge order, the extra fail-closed test coverage, and a comment-discipline pass.

One deferral: a child-gate trust violation on the focus leg is fail-closed but unsurfaced to the host, exactly as the root leg is. That is #796's scope; this PR widens what #796 has to cover from one record to the focus chain, and #796 has been updated to say so.

Closes #917

Summary by CodeRabbit

  • New Features

    • Focused folders now refresh automatically when stale and immediately after focus changes.
    • Deep-folder views update with newly available content while preserving existing state during refresh failures.
    • Snapshot updates are emitted when refreshed data changes.
  • Bug Fixes

    • Improved handling of unavailable or rejected child content.
    • Prevented invalid records from replacing valid folder data.
    • Avoided reporting changes when refreshed data is identical.

FSM1 added 2 commits August 1, 2026 11:26
The vault-pointer leg lifts the root's direct children only, and the drain
repaints just what this device published, so a device that did not author a
subtree rendered it empty below the scope root indefinitely. Command::SetFocus
fell through to Unimplemented, and focus_set/ChildAdopter had no caller that
descended.

SetFocus now records the focus window and refreshes it on access past the
staleness threshold; the resolve tick refreshes the same window every pass,
before the drain. Each folder resolves its own record cache-first, passes the
child gate on this device's floors, and merges through project_folder. A gate
rejection is fail-closed: last-known-good stands and no floor moves.

project_folder now reports whether the merge moved the base, so a re-merge of
the same body on every tick does not repaint the host.

Closes #917
…re-probes

Review follow-ups on the focus-window slice.

The Adopted / Current / NoUpdate + at-floor re-open walk is trust-critical and
was copied in two places; both read paths that descend below the scope root now
go through one resolve_child, so neither can drift on which outcome is staleness
and which is a fail-closed violation.

The on-access stamp now records attempts rather than merges: a folder that never
gate-passes no longer turns every navigation into a fresh endpoint fan-out, and
the poll leg still refreshes the window unconditionally so recovery is automatic.
The threshold filter itself moves next to focus_set as focus_folders_due, with
its own unit test.

Snapshot::link reports whether it established or raised a link, so project_folder
no longer predicts it. The focus pass merges root-ward, so a parent that dropped
a child unlinks it before the pass would project into it.

Tests: the planted-record case now covers all three child-gate bindings the pass
rests on -- foreign node id, foreign scope, and a file body behind a folder ref --
and an unreachable record plane is asserted to leave the focused folder rendering
off the cache rather than clearing it.
@coderabbitai

coderabbitai Bot commented Aug 1, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

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

Next review available in: 59 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 9f0b35f1-dedd-46b0-a98b-7f33a9a60b58

📥 Commits

Reviewing files that changed from the base of the PR and between 767d5bd and 7f7524b.

📒 Files selected for processing (1)
  • crates/engine/tests/write_plane.rs

Walkthrough

Changes

Focus-window refresh

Layer / File(s) Summary
Projection change detection
crates/engine/src/sync/model.rs, crates/engine/src/sync/project.rs, crates/engine/src/net/resolve.rs
Snapshot links and folder projections now report whether state changed. Repeated identical projections return false.
Focus-folder selection and refresh support
crates/engine/src/sync/tick.rs, crates/engine/src/net/child.rs, crates/engine/src/net/focus.rs, crates/engine/src/net/mod.rs, crates/engine/src/sync/mod.rs
The engine derives due focused folders, resolves child records through shared outcomes, and projects valid folder snapshots.
Engine refresh orchestration
crates/engine/src/facade.rs
The facade refreshes stale focused folders during ticks, after SetFocus, and on access. It emits SnapshotUpdated when projection changes.
Focus and cross-device validation
crates/engine/tests/facade.rs, crates/engine/tests/write_plane.rs
Tests cover deep-folder focus, invalid records, outages, refresh damping, and focus registration.

Estimated code review effort: 4 (Complex) | ~45 minutes

Possibly related issues

Possibly related PRs

🚥 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 clearly and concisely describes the main change: resolving folders below the scope root within the focus window.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
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 docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/917-focus-window-folder-refresh

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.

@FSM1
FSM1 marked this pull request as ready for review August 1, 2026 20:22

@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.

🧹 Nitpick comments (1)
crates/engine/tests/write_plane.rs (1)

1994-2029: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Consider reusing this helper in the existing depth-2 test.

deep_create_seen_by_a_second_device repeats the create-and-boot prologue of a_create_below_the_scope_root_is_adoptable_by_a_second_device at lines 1921-1947. That earlier test can call this helper and keep only its own gateway and adopter assertions. The change removes one copy of the fixture and keeps both tests on the same setup.

🤖 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 `@crates/engine/tests/write_plane.rs` around lines 1994 - 2029, The existing
depth-2 test should reuse the deep_create_seen_by_a_second_device fixture
instead of duplicating its create-and-boot setup. Replace that test’s repeated
prologue with the helper’s returned world, blocks, second engine, tasks, photos,
and deep node, while retaining only the test-specific gateway and adopter
assertions.
🤖 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.

Nitpick comments:
In `@crates/engine/tests/write_plane.rs`:
- Around line 1994-2029: The existing depth-2 test should reuse the
deep_create_seen_by_a_second_device fixture instead of duplicating its
create-and-boot setup. Replace that test’s repeated prologue with the helper’s
returned world, blocks, second engine, tasks, photos, and deep node, while
retaining only the test-specific gateway and adopter assertions.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 7548cd52-ce25-4544-a84f-dc75c8999c79

📥 Commits

Reviewing files that changed from the base of the PR and between afb3887 and 767d5bd.

📒 Files selected for processing (11)
  • crates/engine/src/facade.rs
  • crates/engine/src/net/child.rs
  • crates/engine/src/net/focus.rs
  • crates/engine/src/net/mod.rs
  • crates/engine/src/net/resolve.rs
  • crates/engine/src/sync/mod.rs
  • crates/engine/src/sync/model.rs
  • crates/engine/src/sync/project.rs
  • crates/engine/src/sync/tick.rs
  • crates/engine/tests/facade.rs
  • crates/engine/tests/write_plane.rs

@FSM1
FSM1 marked this pull request as draft August 1, 2026 21:09
…lves

The record-plane test duplicated the fixture's create-and-boot prologue.
Return the fixture as a named struct so it can also hand back device B's
seams, and destructure it at every call site.
@FSM1

FSM1 commented Aug 1, 2026

Copy link
Copy Markdown
Owner Author

Addressed the review-body nitpick. No inline threads were opened on this pass, so replying here.

crates/engine/tests/write_plane.rs 1994-2029 — reuse the fixture in the depth-2 test — accepted, with a correction.

The duplication is real: a_create_below_the_scope_root_is_adoptable_by_a_second_device repeated the fixture's create-and-boot prologue verbatim.

The suggestion as written would not have compiled, though. It proposed reusing "the helper's returned world, blocks, second engine, tasks, photos, and deep node", but that test also needs device B's own seams — bob.http, bob.floor_store, bob.record_store, bob.snapshot_cache — to build the ChildAdopter and call resolve. The fixture did not return the FakeDevice, and it cannot be re-derived at the call site: FakeWorld::device mints fresh device-local stores on every call, so a second world.device(b"alice-second-device") would hand back a cold floor store and cache that the booted engine never touched, silently changing what the test asserts.

Fixed in 7f7524b by returning a named DeepCreate struct rather than widening the 6-tuple to 7. The struct also keeps the call sites self-documenting — named fields plus .. in place of six positional _world, _blocks, _tasks_b placeholders.

The record-plane test's doc comment claimed "the facade has no descent below the scope root yet", which this PR makes false; it now cross-references its facade half instead.

Verified: cargo fmt --all --check, cargo clippy --workspace --all-targets -- -D warnings, cargo check --workspace --all-targets, cargo check -p cipherbox-wasm --target wasm32-unknown-unknown --all-targets, and cargo test --workspace all exit 0, with no test failures.

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.

engine: nothing resolves a folder record below the scope root on a device that did not author it

1 participant