fix: shared-folder write and navigation correctness on web#603
Conversation
…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: 0b163c625969
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>
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
|
Warning Review limit reached
Next review available in: 41 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the 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 configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (2)
WalkthroughPhase 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. ChangesShared write/navigation correctness
Estimated code review effort: 5 (Critical) | ~120 minutes Possibly related PRs
Suggested labels: 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
Release Preview
Cascade Details
|
Greptile SummaryThis PR updates shared-folder write and navigation behavior on the web. The main changes are:
Confidence Score: 4/5This should be fixed before merging.
apps/web/src/hooks/useSharedNavigationActions.ts Important Files Changed
|
Codecov Report❌ Patch coverage is
❌ 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@@ 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
Flags with carried forward coverage won't be shown. Click here to find out more.
🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
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
⛔ Files ignored due to path filters (3)
.claude/agents/crypto-privacy-reviewer.mdis excluded by!.claude/agents/**.claude/commands/crypto-privacy-review.mdis excluded by!.claude/commands/**.claude/commands/ship-phase.mdis 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.mdapps/web/src/components/file-browser/ShareDialog.tsxapps/web/src/components/file-browser/SharedFileBrowser.tsxapps/web/src/components/file-browser/SharedFolderRow.tsxapps/web/src/hooks/__tests__/useSharedWriteOps.test.tsapps/web/src/hooks/shared-folder-projection.tsapps/web/src/hooks/useSharedNavigation.tsapps/web/src/hooks/useSharedNavigationActions.tsapps/web/src/hooks/useSharedWriteOps.tsapps/web/src/services/share.service.tspackages/sdk-core/src/__tests__/ipns.test.tspackages/sdk-core/src/ipns/index.tspackages/sdk/src/__tests__/client-shared-write.test.tspackages/sdk/src/__tests__/file-metadata-facade.test.tspackages/sdk/src/__tests__/resolve-node-identity.test.tspackages/sdk/src/client.tstests/sdk-e2e/src/suites/ipns-publish-gate.test.tstests/web-e2e/tests/rotation-ux.spec.tstests/web-e2e/tests/shared-folder-desync.spec.tstests/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
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
* 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>
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.tssubsystem and the SDK seams it consumes.What changed
navStackentries now carry the derivedwriteKey(+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.sharedFolder:updated, a suspended depth's frozen child snapshot is re-resolved viarefreshSharedFolderon restore rather than restored by reference.resolveFileMetadata,downloadFromIpns, andresolveNodeIdentityroute throughgatedResolveChild(ROT-07 anti-rollback floor) instead of rawresolvePublishedNode.resolveNodeIdentitynow takes the fullSealedChildRefso the gate sourcesgeneration/versionFloorfrom the parent mirror.createAndPublishIpnsRecordmaps the API's HTTP 410IPNS_TOMBSTONEDto a catchable{ success: false, tombstoned: true }return;publishNodeFnsurfaces it asCannotWriteUntilRefetchError; the shared-write hooks supply a realrefreshWriteAccess(refreshCurrentDepthWriteKey) intorunWithFailureUx. Therotation-uxe2e now drives the real classifier (no toast injection).SharedFolderRowclassifies drag kind viaisFileRefResolvedagainstresolvedByIpnsName, notisFileRefon a bareSealedChildRef(alwaysfalsepost-kind-cache-removal).navigateUp/navigateToBreadcrumbrestore logic deduped into a singlerestoreToBreadcrumbIndexsource of truth.resolveFolderIpnsPrivateKey/getShareKeysfolder-IPNS write-share-key path removed; the SC1 derived-writeKey path is now the sole write-key source.Gate results
gsd-verify-work)gsd-secure-phase)/security-reviewgsd-validate-phase)Fixes applied during ship
fix(73): re-resolve IPNS on shared write-access refresh. CodeRabbit MAJOR —refreshCurrentDepthWriteKeyreseeded from cached SDK state, so a co-writer whose write failed on a stale-but-not-revokedpublishedNodecould never recover on the "Refresh access" retry. Now mirrorsrestoreToBreadcrumbIndex: reseed, then additiverefreshSharedFolderre-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
2026-07-06-68.2-coderabbit-hardening-backlog.mditem 4: thepublishedParentwrite-base resolve atclient.ts:5674is 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..planning/doc-metadata minors (two referencing nonexistent cross-phases); crypto review's 410-body-agnostic nit (availability-only) androtationHighWater-conditional informational (pre-existing, web always wires it).Notes
tee-republishA/B,ipns-publish-gateTest 20) withtee_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 defaultsTEE_WORKER_URLtohttp://localhost:3001, which on this host ismock-ipns-routing— the tee-worker is published on host:3002(its container:3001remapped due to the collision) and gates/public-keybehindAuthorization: Bearer $TEE_WORKER_SECRET. Re-pointing the API at:3002with the matching secret seededtee_key_state; all 3 then pass (Test 20 after the contract fix above).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
Bug Fixes