Skip to content

fix: shared-folder write and navigation correctness on web#603

Merged
FSM1 merged 67 commits into
mainfrom
feat/shared-write-navigation-correctness-web
Jul 10, 2026
Merged

fix: shared-folder write and navigation correctness on web#603
FSM1 merged 67 commits into
mainfrom
feat/shared-write-navigation-correctness-web

Conversation

@FSM1

@FSM1 FSM1 commented Jul 10, 2026

Copy link
Copy Markdown
Owner

Phase 73 — Shared Write/Navigation Correctness (Web)

Web-side correctness for shared-folder navigation and write capability. Seven success criteria (five roadmap + two folded-in cleanups), all in the useSharedNavigationActions.ts subsystem and the SDK seams it consumes.

What changed

  • SC1 — nested write-key retention. navStack entries now carry the derived writeKey (+ publishedNode), so a write into a deep shared subfolder succeeds after navigate-up / breadcrumb restore. Single-owner buffer discipline (clone-on-transfer, zero-at-terminal-owner) prevents use-after-free.
  • SC2 — stale-child-snapshot invalidation. On sharedFolder:updated, a suspended depth's frozen child snapshot is re-resolved via refreshSharedFolder on restore rather than restored by reference.
  • SC3 — floor-gated read facades. resolveFileMetadata, downloadFromIpns, and resolveNodeIdentity route through gatedResolveChild (ROT-07 anti-rollback floor) instead of raw resolvePublishedNode. resolveNodeIdentity now takes the full SealedChildRef so the gate sources generation/versionFloor from the parent mirror.
  • SC4 — WRITE-03 refresh-access, wired end-to-end. createAndPublishIpnsRecord maps the API's HTTP 410 IPNS_TOMBSTONED to a catchable { success: false, tombstoned: true } return; publishNodeFn surfaces it as CannotWriteUntilRefetchError; the shared-write hooks supply a real refreshWriteAccess (refreshCurrentDepthWriteKey) into runWithFailureUx. The rotation-ux e2e now drives the real classifier (no toast injection).
  • SC5 — drag-payload kind from resolved listing. SharedFolderRow classifies drag kind via isFileRefResolved against resolvedByIpnsName, not isFileRef on a bare SealedChildRef (always false post-kind-cache-removal).
  • SC6 (folded-in) — consolidation. navigateUp / navigateToBreadcrumb restore logic deduped into a single restoreToBreadcrumbIndex source of truth.
  • SC7 (folded-in) — dead-path removal. The dead resolveFolderIpnsPrivateKey / getShareKeys folder-IPNS write-share-key path removed; the SC1 derived-writeKey path is now the sole write-key source.

Gate results

Gate Result
Verify (gsd-verify-work) 7/7 truths VERIFIED
Secure (gsd-secure-phase) SECURED — 17/17 threats closed, 0 open
Crypto/privacy review Clean; 1 LOW defense-in-depth (logged), nits discarded
Built-in /security-review Clean — no high-confidence vulns; changes trend security-positive
Validate (gsd-validate-phase) COMPLIANT — 0 gaps
Simplify No changes needed (diff already consolidated)
SDK E2E 106/106 green
CodeRabbit CLI 1 MAJOR fixed, then 0 findings on re-run

Fixes applied during ship

  • fix(73): re-resolve IPNS on shared write-access refresh. CodeRabbit MAJOR — refreshCurrentDepthWriteKey reseeded from cached SDK state, so a co-writer whose write failed on a stale-but-not-revoked publishedNode could never recover on the "Refresh access" retry. Now mirrors restoreToBreadcrumbIndex: reseed, then additive refreshSharedFolder re-check (monotonicity-guarded no-op; try/catch-ignored so a genuinely revoked name still escalates to the terminal toast).
  • test(73): align WRITE-04 e2e (Test 20) to the new tombstone-publish contract. The SC4 throw-to-return-value change left a pre-existing e2e asserting the old throw; updated the publish half to assert { tombstoned: true, success: false } (resolve half still asserts a 410 throw, unchanged).

Triage tally

  • Fixed: 2 (CodeRabbit MAJOR refresh-access recovery; WRITE-04 e2e contract).
  • Todos logged: 1 — appended to 2026-07-06-68.2-coderabbit-hardening-backlog.md item 4: the publishedParent write-base resolve at client.ts:5674 is ungated (LOW defense-in-depth; mitigated by the in-memory seq-monotonicity guard + server CAS; proper floor-gating needs parent-mirror context absent at the share root). Deferred to a future SDK write-plane hardening pass.
  • Discarded: CodeRabbit's 3 .planning/ doc-metadata minors (two referencing nonexistent cross-phases); crypto review's 410-body-agnostic nit (availability-only) and rotationHighWater-conditional informational (pre-existing, web always wires it).

Notes

  • SDK E2E initially reported 3 failures (tee-republish A/B, ipns-publish-gate Test 20) with tee_key_state is empty. Root cause was a local-stack config gap, not a phase regression (Phase 73 touches zero TEE/API code): the API defaults TEE_WORKER_URL to http://localhost:3001, which on this host is mock-ipns-routing — the tee-worker is published on host :3002 (its container :3001 remapped due to the collision) and gates /public-key behind Authorization: Bearer $TEE_WORKER_SECRET. Re-pointing the API at :3002 with the matching secret seeded tee_key_state; all 3 then pass (Test 20 after the contract fix above).
  • Web-e2e specs (writable-shares, shared-folder-desync, rotation-ux) are behaviorally complete but main-push gated in CI.

🤖 Generated with Claude Code

https://claude.ai/code/session_012Z3zThQZPm6bVk2476WURL

Summary by CodeRabbit

  • New Features

    • Shared-folder navigation now preserves write access when moving up or restoring breadcrumbs.
    • Restored shared folders refresh their contents to show current changes.
    • Added clearer refresh-access and write-revoked feedback for stale or invalid write access.
    • Shared-file reads now reject unverified records for improved safety.
    • Drag-and-drop items are classified more accurately as files or folders.
  • Bug Fixes

    • Improved handling of tombstoned shared records and stale write envelopes.
    • Fixed shared writes after nested navigation restores.
    • Added regression coverage for shared navigation, writes, refresh flows, and tombstoned records.

FSM1 and others added 30 commits July 10, 2026 20:10
…eKeys cleanup

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012Z3zThQZPm6bVk2476WURL
Entire-Checkpoint: a93ee05e7fe4
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012Z3zThQZPm6bVk2476WURL
Entire-Checkpoint: c6308f50c0af
Entire-Checkpoint: ea7c82c76ac4
Entire-Checkpoint: 274ccc896d8f
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012Z3zThQZPm6bVk2476WURL
Entire-Checkpoint: 6412d1b18665
…9 dep

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012Z3zThQZPm6bVk2476WURL
Entire-Checkpoint: 887413e6d876
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012Z3zThQZPm6bVk2476WURL
Entire-Checkpoint: 1884ca182681
- RED case: 410 axios-shaped rejection asserts tombstoned:true, success:false
- regression case: non-410 (500) rejection must still rethrow unchanged
- return type cast locally until GREEN extends the declared type
- wrap ipnsControllerPublishRecord in try/catch, reusing resolveIpnsRecord's
  status-extraction idiom
- status===410 returns success:false, sequenceNumber:0n, tombstoned:true;
  any other status/error rethrows unchanged
- extends the declared return type with tombstoned?: boolean
- no change to the D-05 caller-owns-key contract; no key zeroing added
- SharedFolderRow now takes resolvedByIpnsName and uses isFileRefResolved
  instead of isFileRef on bare SealedChildRefs in handleDragStart
- SharedFileBrowser threads the already-computed resolvedByIpnsName map
  into the SharedFolderRow call site
- SUMMARY.md documents the createAndPublishIpnsRecord 410->tombstoned
  transport mapping and its self-check
Add descend-2/up-1/write fixme case to writable-shares.spec.ts (SC1) and
deeper-mutate-then-navigate-up fixme case to shared-folder-desync.spec.ts
(SC2), each reusing the analog test's existing multi-account harness. Both
are discovered by Playwright but not asserted until 73-07/73-09 remove the
fixme guard.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012Z3zThQZPm6bVk2476WURL
…flow

Convert the D-01/WRITE-03 direct-notification-injection case into a
test.fixme skeleton documenting the real classifier-driven flow (a
tombstoned co-writer publish surfacing Refresh access through
runWithFailureUx -> CannotWriteUntilRefetchError). Updated SCOPE NOTE cites
the enabling plans 73-02 (sdk-core 410->tombstoned), 73-05 (publishNodeFn
mapping), and 73-08 (useSharedWriteOps runWithFailureUx wiring), which
will remove the fixme guard and finalize assertions.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012Z3zThQZPm6bVk2476WURL
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012Z3zThQZPm6bVk2476WURL
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012Z3zThQZPm6bVk2476WURL
Delete resolveFolderIpnsPrivateKey and its getShareKeys param/call
sites in useSharedNavigationActions.ts, replacing them with a direct
zero-buffer assignment (byte-identical behavior, since the
fetchShareKeys stub always returns []). Remove the now-unused
getShareKeys callback and shareKeysCacheRef plumbing from
useSharedNavigation.ts. Real shared-write signing keys come from the
SDK write-body, never from this path (SC7).

fetchShareKeys and resolveSharedRootWriteKey remain live and untouched.
…ownloadFromIpns

- New file-metadata-facade.test.ts proves resolveFileMetadata and
  downloadFromIpns must reject on a signatureVerified:false resolve
  (SC3, ROT-07 anti-rollback floor)
- Currently RED: both facades bypass the gate via raw resolvePublishedNode
- Includes null-resolve fail-closed cases (already GREEN, pre-existing
  not-found behavior)
…atedResolveChild

- Both facades now resolve the file's PublishedNode via gatedResolveChild
  (ROT-07 anti-rollback floor) instead of raw resolvePublishedNode
- A signatureVerified:false resolve now fails closed for both facades
  (SC3); file-metadata-facade.test.ts is GREEN
…eToBreadcrumb

Consolidate navigateUp and navigateToBreadcrumb's near-verbatim
restore/re-seed blocks into one restoreToBreadcrumbIndex helper.
navigateUp delegates with stack.length - 1; navigateToBreadcrumb
delegates with its crumbIndex. UI-behavior-neutral: restored fields,
breadcrumb slicing, isRootDepth writeKey re-derivation, and all
folderKey zeroing are unchanged (SC6). This is the sole landing spot
for SC1/SC2's follow-on fixes in plans 73-07/73-09.
…ugh gatedResolveChild

- Breaking signature change: resolveNodeIdentity(ipnsName: string) becomes
  resolveNodeIdentity(childRef: SealedChildRef), routed through
  gatedResolveChild (ROT-07 anti-rollback floor, SC3)
- Updated resolve-node-identity.test.ts to build a SealedChildRef fixture
  and added a signatureVerified:false fail-closed case
- Updated the one production caller, useSharedWriteOps resolveChildNodeId,
  to accept and pass the full SealedChildRef; deleteItemHandler call site
  updated atomically
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
FSM1 and others added 11 commits July 10, 2026 22:35
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012Z3zThQZPm6bVk2476WURL
Entire-Checkpoint: f2a4facf15ac
… todo

The 6 shared-write/navigation bug todos resolved by Phase 73 (SC1-SC5, SC7)
lacked resolves_phase:73 frontmatter so the auto-close skipped them; move
them to completed. The SC6 consolidate-shared-navigation todo is only
partially resolved (navigateUp/navigateToBreadcrumb consolidation done;
readSharedContent/resolveKinds/ShareDialog dedup remain) -- annotate and
keep open.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012Z3zThQZPm6bVk2476WURL
Entire-Checkpoint: 2a24dea80c52
Export parseRootGeneration/toSentShare/fetchAllSentShares from
share.service.ts and have ShareDialog import parseRootGeneration
instead of keeping a verbatim copy. The sent-shares pagination and
DTO mapping in ShareDialog stay inline: they diverge from
fetchAllSentShares/toSentShare (filtered-to-item subset, itemName
seeded from the local item, and a stricter truthy permission check)
so swapping them in would change behavior.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012Z3zThQZPm6bVk2476WURL
Entire-Checkpoint: 05b5d8dca796
Extract readSharedContent as a module-level helper shared by
downloadSharedFile and loadSharedFileContent: it builds the
client.downloadSharedFile request from an already-resolved share +
vault keypair and maps the revoked/behind-retry statuses to marker
errors carrying the exact existing user-visible message strings.
loadSharedFileContent lets the marker errors propagate (matching its
prior throw-based behavior byte for byte). downloadSharedFile catches
them and restores its exact setError-then-return control flow, so its
generic catch-all (logger.error + "Failed to download file") only
fires for genuinely unexpected errors, same as before.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012Z3zThQZPm6bVk2476WURL
Entire-Checkpoint: 9558c393adc0
Close 3 of 4 remaining sub-items (resolveFileIpnsKey mirror is moot,
ShareDialog parseRootGeneration deduped, readSharedContent extracted)
and document the back-out on the resolveKinds-then-project
consolidation: a full site-by-site read found 5 owned-plane and 4
shared-plane variants, each with a load-bearing guard/timing/
projection difference tied to a specific correctness requirement
(write-key zeroization, anti-rollback floors, D-05 error surfacing).
Unifying any of them would change observable behavior, so the item
stays open rather than forcing a risky consolidation.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012Z3zThQZPm6bVk2476WURL
Entire-Checkpoint: 26b0bdeb0784
Genuine duplication removed (readSharedContent, parseRootGeneration export,
dead resolveFolderIpnsPrivateKey); the 9-site resolveKinds-then-project
unification was evaluated and deliberately declined -- a family of
load-bearing-distinct nav guards, not a mechanical duplicate, with zero user
impact and moderate regression risk. Recorded the decision in the todo so it
is not reopened as a mechanical refactor.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012Z3zThQZPm6bVk2476WURL
Entire-Checkpoint: 62c19362319b
Rename the /security:review command to /crypto-privacy-review and its
security-reviewer agent to crypto-privacy-reviewer.

Wire two security passes into /ship-phase: a conditional
/crypto-privacy-review step (only when the phase diff touches
crypto/privacy code) and the built-in /security-review sweep.

Replace the binary fix-or-defer triage with a three-way rule
(fix / log-material-todo / discard) that defaults nits to discard,
adds a materiality bar and dedupe check for new todos, and reports a
fixed/logged/discarded tally to keep the todo backlog a real signal.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012Z3zThQZPm6bVk2476WURL
Entire-Checkpoint: c954b16d2b5e
refreshCurrentDepthWriteKey (the SC4 refreshWriteAccess supplier) reseeded
the current depth from cached SDK state, so a co-writer whose write failed on
a stale-but-not-revoked publishedNode reused the same stale envelope on retry
and could never recover. Mirror restoreToBreadcrumbIndex: after reseeding,
call refreshSharedFolder to re-check IPNS -- additive, guarded by the SDK
monotonicity no-op, and try/catch-ignored so a genuinely tombstoned name still
escalates to the terminal write-access-revoked toast.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012Z3zThQZPm6bVk2476WURL
Entire-Checkpoint: 1cc578595bc9
…hrow

Phase 73 SC4 changed createAndPublishIpnsRecord to map the API 410
IPNS_TOMBSTONED to a catchable { success: false, tombstoned: true } return
instead of throwing a raw AxiosError. Update the WRITE-04 e2e (Test 20) to
assert the new return-value contract on the publish half; the resolve half
still throws 410 unchanged.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012Z3zThQZPm6bVk2476WURL
Entire-Checkpoint: 7f19b37e018b
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012Z3zThQZPm6bVk2476WURL
Entire-Checkpoint: 25f1b7d462d6
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012Z3zThQZPm6bVk2476WURL
Entire-Checkpoint: a3532b545d21
@coderabbitai

coderabbitai Bot commented Jul 10, 2026

Copy link
Copy Markdown

Warning

Review limit reached

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

Next review available in: 41 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

Run ID: f11d4b73-9059-4c73-8bf3-e26ea86c39e1

📥 Commits

Reviewing files that changed from the base of the PR and between 60035ac and 52f0439.

📒 Files selected for processing (2)
  • .planning/todos/pending/2026-07-06-68.2-coderabbit-hardening-backlog.md
  • packages/sdk/src/client.ts

Walkthrough

Phase 73 adds shared-write and navigation correctness changes across the SDK and web app. It introduces tombstone signaling, gated read resolution, per-depth write-key restoration, refresh-access retry wiring, fresh navigation restores, resolved drag classification, and related tests and planning records.

Changes

Shared write/navigation correctness

Layer / File(s) Summary
IPNS tombstone and gated SDK resolution
packages/sdk-core/..., packages/sdk/src/client.ts, packages/sdk/src/__tests__/*
HTTP 410 publish responses now return tombstoned: true; non-410 errors rethrow. SDK read facades use gated child resolution, and shared-folder refresh adopts a freshly resolved published envelope.
Navigation write-key restoration
apps/web/src/hooks/useSharedNavigation*.ts
Navigation entries retain write keys and published nodes, restore through one helper, zero discarded key buffers, refresh restored folders, and remove obsolete folder-IPNS key plumbing.
Shared-write retry UX
apps/web/src/hooks/useSharedWriteOps.ts, apps/web/src/hooks/useSharedNavigation.ts
Shared mutations use runWithFailureUx with a live refresh supplier, and child identity resolution receives full sealed references.
Shared UI projection and helpers
apps/web/src/components/file-browser/*, apps/web/src/services/share.service.ts
Drag payload kinds use resolved listing metadata, and shared service helpers are exported for reuse.
Regression coverage and phase records
tests/web-e2e/tests/*, .planning/...
End-to-end cases cover write-after-restore, fresh children after remote mutation, and tombstone-driven refresh UX; Phase 73 planning and verification records are updated.

Estimated code review effort: 5 (Critical) | ~120 minutes

Possibly related PRs

  • FSM1/cipher-box#448: Provides the IPNS signature verification data used by the new gated read paths.
  • FSM1/cipher-box#587: Introduces the failure-UX classifier contract used by shared-write retry wiring.
  • FSM1/cipher-box#589: Provides the resolved-listing basis for shared drag-item classification.

Suggested labels: release:web:fix, release:sdk-core:fix, release:sdk:fix

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 66.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title is concise and accurately summarizes the PR’s main web shared-folder write/navigation correctness changes.
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 unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/shared-write-navigation-correctness-web

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.

@github-actions github-actions Bot added release:sdk-core:feat Minor version bump (new feature) for sdk-core release:web:feat Minor version bump (new feature) for web release:sdk:feat Minor version bump (new feature) for sdk release:tee-worker:fix Patch version bump (bug fix) for tee-worker labels Jul 10, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Release Preview

Package Bump Label Source
sdk minor release:sdk:feat Direct (feat commit)
sdk-core minor release:sdk-core:feat Direct (feat commit)
tee-worker patch release:tee-worker:fix Cascade (sdk-core minor)
web minor release:web:feat Direct (feat commit)

Cascade Details

  • sdk-core minor -> tee-worker patch (direct dependency)

@greptile-apps

greptile-apps Bot commented Jul 10, 2026

Copy link
Copy Markdown

Greptile Summary

This PR updates shared-folder write and navigation behavior on the web. The main changes are:

  • Shared navigation stack entries now retain write state for restored depths.
  • Restored shared folders refresh their listing after navigation.
  • SDK read facades now use the shared-child gate.
  • Tombstoned publish responses now feed the refresh-access UX.
  • Shared-row drag payloads now use resolved item kind data.

Confidence Score: 4/5

This should be fixed before merging.

  • A pending descent can still overwrite restored shared-folder state after the user navigates back.
  • A restored write key can still be zeroed through the refresh-access path and break later restores.

apps/web/src/hooks/useSharedNavigationActions.ts

Important Files Changed

Filename Overview
apps/web/src/hooks/useSharedNavigationActions.ts Adds shared-folder restore, write-key retention, and refresh-access wiring, but the async navigation and key ownership paths can still leave write state incorrect.
apps/web/src/hooks/useSharedWriteOps.ts Wires shared write operations through refresh-access handling and passes full child refs for identity resolution.
packages/sdk/src/client.ts Routes shared read facades through gated child resolution and maps tombstoned publish results for write refresh handling.
packages/sdk-core/src/ipns/index.ts Maps publish HTTP 410 responses to a structured tombstoned result while preserving other error paths.
apps/web/src/components/file-browser/SharedFolderRow.tsx Uses resolved listing data to classify shared drag payload items.

Comments Outside Diff (1)

  1. apps/web/src/hooks/useSharedNavigationActions.ts, line 445-458 (link)

    P1 Stale descent can reseed

    This continuation can still run after the user has already navigated back to a parent. If resolveSharedSubfolderWriteKey is pending and the user clicks [..] or a breadcrumb, the restore path puts the UI back on the parent, but this resumed descent then writes the child key and reseeds sharedFolderTree with the child ipnsName, writeKey, and publishedNode. The next shared write reads from sharedFolderTree, so it can publish with child state while the UI is showing the restored parent. Add a navigation token, current-depth check, or cancellation guard before assigning currentWriteKeyRef and reseeding.

Reviews (2): Last reviewed commit: "docs(73): log in-flight descent race fol..." | Re-trigger Greptile

Comment thread apps/web/src/hooks/useSharedNavigationActions.ts
Comment thread apps/web/src/hooks/useSharedNavigationActions.ts
@codecov

codecov Bot commented Jul 10, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 60.00000% with 6 lines in your changes missing coverage. Please review.
✅ Project coverage is 84.71%. Comparing base (c21f896) to head (52f0439).

Files with missing lines Patch % Lines
packages/sdk/src/client.ts 60.00% 5 Missing and 1 partial ⚠️

❌ Your patch check has failed because the patch coverage (60.00%) is below the target coverage (80.00%). You can increase the patch coverage or adjust the target coverage.

Additional details and impacted files

Impacted file tree graph

@@             Coverage Diff             @@
##             main     #603       +/-   ##
===========================================
+ Coverage   70.05%   84.71%   +14.65%     
===========================================
  Files         157      121       -36     
  Lines       15392    11185     -4207     
  Branches     1941     1952       +11     
===========================================
- Hits        10783     9475     -1308     
+ Misses       4357     1456     -2901     
- Partials      252      254        +2     
Flag Coverage Δ
api 84.71% <60.00%> (+0.07%) ⬆️
api-client 84.71% <60.00%> (+0.07%) ⬆️
core 84.71% <60.00%> (+0.07%) ⬆️
crypto 84.71% <60.00%> (+0.07%) ⬆️
desktop ?
sdk 84.71% <60.00%> (+0.07%) ⬆️
sdk-core 84.71% <60.00%> (+0.07%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
packages/sdk/src/client.ts 83.91% <60.00%> (+0.26%) ⬆️

... and 36 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

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

Actionable comments posted: 6

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

Inline comments:
In @.planning/phases/73-shared-write-navigation-correctness-web/73-05-PLAN.md:
- Around line 64-66: Update the <verify> section’s <automated> command to run
the SDK typecheck and tests as independent required checks, without chaining
them with ||. Ensure both commands execute and the overall verification fails if
either check fails, while keeping the verification status accurate.

In
@.planning/phases/73-shared-write-navigation-correctness-web/73-VALIDATION.md:
- Around line 45-55: Update the validation matrix in 73-VALIDATION.md so SC3
references the correct existing test file, resolve-node-identity.test.ts, and
aligns its status with the later verification results. Reconcile all other SC
statuses with the documented green checks, or explicitly label the matrix as a
pre-execution snapshot; keep the commands and statuses consistent with the
plan_check/source_grounding verification sections.

In @.planning/STATE.md:
- Around line 5-17: Reconcile the workflow metadata in STATE.md so phase,
status, current phase, and plan counts consistently reflect the actual state,
removing contradictory completion/execution/not-started values. Update the
corresponding metadata in the additionally affected phase/plan state entries,
including 27-36, and ensure totals, completed counts, percentages, and
stopped_at/last activity fields agree.

In `@apps/web/src/hooks/useSharedNavigationActions.ts`:
- Line 268: Restore the actual shared-folder IPNS private key in the
shared-write state instead of initializing `ipnsPrivateKey` with a zero-filled
Uint8Array. In the relevant shared navigation action, resolve and assign the key
from the existing resolver/source used by the SDK contract; if the field is no
longer supported, remove it consistently from the contract and consumers.

In `@packages/sdk-core/src/__tests__/ipns.test.ts`:
- Around line 104-132: Update the tombstoned-record test for
createAndPublishIpnsRecord to assert result.sequenceNumber equals 0n in addition
to success and tombstoned. In the SC4(a) non-410 rejection test, assert the
rejected value is the exact same error object by using rejects.toBe(error),
while retaining coverage that the 500 error is rethrown unchanged.

In `@packages/sdk/src/client.ts`:
- Around line 5668-5680: Guard the adoption in the shared-folder refresh path so
`result` and `parentResolved` are only combined when their sequence numbers are
consistent. Before calling `adoptSharedFolderResult`, compare
`parentResolved.published`’s sequence number with `result.sequenceNumber` using
the same strict `<=` validation as `doReresolveFolderInPlace`; only pass
`publishedParent` when valid, otherwise omit it or avoid adopting the mismatched
result.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 2eea7fcf-5cb8-48d4-8ee5-4252894c9f35

📥 Commits

Reviewing files that changed from the base of the PR and between c21f896 and 60035ac.

⛔ Files ignored due to path filters (3)
  • .claude/agents/crypto-privacy-reviewer.md is excluded by !.claude/agents/**
  • .claude/commands/crypto-privacy-review.md is excluded by !.claude/commands/**
  • .claude/commands/ship-phase.md is excluded by !.claude/commands/**
📒 Files selected for processing (56)
  • .planning/PROJECT.md
  • .planning/ROADMAP.md
  • .planning/STATE.md
  • .planning/phases/73-shared-write-navigation-correctness-web/73-01-PLAN.md
  • .planning/phases/73-shared-write-navigation-correctness-web/73-01-SUMMARY.md
  • .planning/phases/73-shared-write-navigation-correctness-web/73-02-PLAN.md
  • .planning/phases/73-shared-write-navigation-correctness-web/73-02-SUMMARY.md
  • .planning/phases/73-shared-write-navigation-correctness-web/73-03-PLAN.md
  • .planning/phases/73-shared-write-navigation-correctness-web/73-03-SUMMARY.md
  • .planning/phases/73-shared-write-navigation-correctness-web/73-04-PLAN.md
  • .planning/phases/73-shared-write-navigation-correctness-web/73-04-SUMMARY.md
  • .planning/phases/73-shared-write-navigation-correctness-web/73-05-PLAN.md
  • .planning/phases/73-shared-write-navigation-correctness-web/73-05-SUMMARY.md
  • .planning/phases/73-shared-write-navigation-correctness-web/73-06-PLAN.md
  • .planning/phases/73-shared-write-navigation-correctness-web/73-06-SUMMARY.md
  • .planning/phases/73-shared-write-navigation-correctness-web/73-07-PLAN.md
  • .planning/phases/73-shared-write-navigation-correctness-web/73-07-SUMMARY.md
  • .planning/phases/73-shared-write-navigation-correctness-web/73-08-PLAN.md
  • .planning/phases/73-shared-write-navigation-correctness-web/73-08-SUMMARY.md
  • .planning/phases/73-shared-write-navigation-correctness-web/73-09-PLAN.md
  • .planning/phases/73-shared-write-navigation-correctness-web/73-09-SUMMARY.md
  • .planning/phases/73-shared-write-navigation-correctness-web/73-CONTEXT.md
  • .planning/phases/73-shared-write-navigation-correctness-web/73-LEARNINGS.md
  • .planning/phases/73-shared-write-navigation-correctness-web/73-PATTERNS.md
  • .planning/phases/73-shared-write-navigation-correctness-web/73-RESEARCH.md
  • .planning/phases/73-shared-write-navigation-correctness-web/73-SECURITY.md
  • .planning/phases/73-shared-write-navigation-correctness-web/73-VALIDATION.md
  • .planning/phases/73-shared-write-navigation-correctness-web/73-VERIFICATION.md
  • .planning/todos/completed/2026-07-02-write03-refresh-access-path-has-no-live-trigger.md
  • .planning/todos/completed/2026-07-03-consolidate-web-shared-navigation-dup.md
  • .planning/todos/completed/2026-07-04-nested-shared-write-key-lost-on-up-breadcrumb-restore.md
  • .planning/todos/completed/2026-07-04-remove-dead-getsharekeys-folder-ipns-path.md
  • .planning/todos/completed/2026-07-04-shared-nav-stack-stale-children-snapshot.md
  • .planning/todos/completed/2026-07-06-gate-non-listing-read-facades.md
  • .planning/todos/completed/2026-07-06-sharedfolderrow-drag-kind-classification.md
  • .planning/todos/pending/2026-07-03-consolidate-web-shared-navigation-dup.md
  • .planning/todos/pending/2026-07-06-68.2-coderabbit-hardening-backlog.md
  • apps/web/src/components/file-browser/ShareDialog.tsx
  • apps/web/src/components/file-browser/SharedFileBrowser.tsx
  • apps/web/src/components/file-browser/SharedFolderRow.tsx
  • apps/web/src/hooks/__tests__/useSharedWriteOps.test.ts
  • apps/web/src/hooks/shared-folder-projection.ts
  • apps/web/src/hooks/useSharedNavigation.ts
  • apps/web/src/hooks/useSharedNavigationActions.ts
  • apps/web/src/hooks/useSharedWriteOps.ts
  • apps/web/src/services/share.service.ts
  • packages/sdk-core/src/__tests__/ipns.test.ts
  • packages/sdk-core/src/ipns/index.ts
  • packages/sdk/src/__tests__/client-shared-write.test.ts
  • packages/sdk/src/__tests__/file-metadata-facade.test.ts
  • packages/sdk/src/__tests__/resolve-node-identity.test.ts
  • packages/sdk/src/client.ts
  • tests/sdk-e2e/src/suites/ipns-publish-gate.test.ts
  • tests/web-e2e/tests/rotation-ux.spec.ts
  • tests/web-e2e/tests/shared-folder-desync.spec.ts
  • tests/web-e2e/tests/writable-shares.spec.ts
💤 Files with no reviewable changes (1)
  • .planning/todos/pending/2026-07-03-consolidate-web-shared-navigation-dup.md

Comment thread .planning/STATE.md
Comment thread apps/web/src/hooks/useSharedNavigationActions.ts
Comment thread packages/sdk-core/src/__tests__/ipns.test.ts
Comment thread packages/sdk/src/client.ts
FSM1 and others added 2 commits July 11, 2026 00:34
refreshSharedFolder does two independent resolves of the same ipnsName
(loadFolderMetadata result vs a separate resolvePublishedNode). If they race
and diverge, the adopted publishedNode could be older than the just-adopted
children/sequence, reintroducing the stale-envelope-drops-WriteChildRefs bug
the publishedParent seed targets. Adopt publishedParent only when its sequence
is at least as fresh as the result (mirrors doReresolveFolderInPlace's
owned-folder guard); otherwise omit and let the next refresh reseed.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012Z3zThQZPm6bVk2476WURL
Entire-Checkpoint: 139e4c0ac75d
…note

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012Z3zThQZPm6bVk2476WURL
Entire-Checkpoint: 6b6d135e9fce
Comment thread apps/web/src/hooks/useSharedNavigationActions.ts
@FSM1 FSM1 merged commit bd8c1e0 into main Jul 10, 2026
30 checks passed
@FSM1 FSM1 deleted the feat/shared-write-navigation-correctness-web branch July 10, 2026 22:44
FSM1 added a commit that referenced this pull request Jul 10, 2026
* docs(todos): triage M4 pending todos — retire resolved/NA, merge recovery, annotate 68.2 backlog

Retire to completed/ with resolution notes:
- web-e2e-flaky-cascade-abort — resolved by PR #593 (file-level parallelism)
- rotation-hardening-followups-from-pr-review — all 6 closed by Phase 70 + 70.1
- add-permanent-delete-confirmation-dialog — ConfirmDialog gates delete (D-02
  per-mode wording caveat noted)
- shared-write-base-aware-merge-parity — NOT APPLICABLE: shared-write.ts has no
  CAS-409 merge path (strict publishOrThrow), so the resurrection vector cannot occur

Merge duplicate recovery-v3 todos: fold
2026-06-29-recovery-html-vault-v3-migration into the fuller
2026-07-03-port-recovery-tool-to-v3-vault-format (supersedes + vault-v3-blob.json note).

Annotate 68.2 CodeRabbit backlog after re-verifying against HEAD/#603:
mark item 1 RESOLVED (72-06 listingCache.delete), item 4 RESOLVED (73-05, ROT-07
residual deferred), item 7 PARTIAL (generation assertion done; fail-closed +
selectEncryptionMode still open); 8 items remain open.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012Z3zThQZPm6bVk2476WURL
Entire-Checkpoint: 22aff8d89af6

* docs(roadmap): scope M4 v2.0 closeout phases 74-78 and map pending todos

Add five closeout phases to the active v2.0 milestone (Phases 61-78) with goals,
success criteria, dependencies, and per-phase source-todo lists:

- Phase 74: Rust/FUSE Rotation-Revocation Soundness (3 todos)
- Phase 75: Cross-Language IPNS/Node-Codec Verification Parity (4 todos)
- Phase 76: FUSE Durability + TEE Write-Path Hardening (4 todos)
- Phase 77: Crypto Hygiene + Terminology Canonicalization (12 todos)
- Phase 78: Recovery Tool v3 + Web UX + CI Guards (5 todos)

Stamp resolves_phase on all 28 M4 closeout todos; the 10 non-M4 todos
(M5 features, infra/ops, tech-debt, parked FUSE-T) are left unstamped.

Retire confirm-no-legacy-v1-v2-vault-blobs (WON'T DO — user: no migration, all
legacy vaults deprecated; the deserializeVaultBlobV3 hard-cut stands as the
D-05 guard).

Update ROADMAP milestone header/checklist and STATE roadmap-evolution + phase
count (16 -> 21).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012Z3zThQZPm6bVk2476WURL
Entire-Checkpoint: 07289de753be

* docs(roadmap): add Phase 79 for web kind-discrimination completion + deferred test revival

Triaging the 83 TODO(phase 63/65) markers found ~40 that are not stale: the web
listing UI never got routed through ResolvedChild.kind (added in 68.2), so
folders-first sort, drag-and-drop, and kind-aware dialog labels never returned
after the Phase 62 cutover, createdAt is still stubbed in details panes, and four
test suites remain describe.skip'd.

- Add Phase 79 to the active v2.0 milestone (now Phases 61-79) with grouped scope,
  success criteria, and the full still-valid marker inventory
- Add consolidating pending todo (resolves_phase: 79)
- Update ROADMAP header/checklist and STATE roadmap-evolution (phase count -> 22)

The 43 stale markers (63/65 shipped, code migrated) are removed separately as a
pure comment cleanup on a code branch.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012Z3zThQZPm6bVk2476WURL
Entire-Checkpoint: ab67c91ff365

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

release:sdk:feat Minor version bump (new feature) for sdk release:sdk-core:feat Minor version bump (new feature) for sdk-core release:tee-worker:fix Patch version bump (bug fix) for tee-worker release:web:feat Minor version bump (new feature) for web

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant