Skip to content

fix: complete web kind discrimination and revive deferred test suites#611

Merged
FSM1 merged 41 commits into
mainfrom
feat/web-kind-discrimination-completion-and-deferred-test-revival
Jul 12, 2026
Merged

fix: complete web kind discrimination and revive deferred test suites#611
FSM1 merged 41 commits into
mainfrom
feat/web-kind-discrimination-completion-and-deferred-test-revival

Conversation

@FSM1

@FSM1 FSM1 commented Jul 12, 2026

Copy link
Copy Markdown
Owner

Phase 79 — Web Kind-Discrimination Completion and Deferred Test Revival

Routes the file-browser UI through the SDK-resolved ResolvedChild.kind projection, wires a real Created date, and revives (or explicitly retires) the four deferred describe.skip suites, driving TODO(phase 63/65) markers to zero.

What changed

  • Kind discrimination at every listing/dialog/drag site — folders-first sort, folder-only drop targets, kind-aware multi-select drag payload, and kind-aware rename/delete/share/move dialogs all derive file-vs-folder from ResolvedChild.kind via isFileRefResolved(item, resolvedByIpnsName) instead of a hardcoded 'folder' stub. Move cycle-guards (private + shared) now exclude only real folder-kind items.
  • Real Created dateResolvedChild.createdAt is now a mandatory field sourced from the already-unsealed Node envelope in resolveChildren; the Details panes render it (Number.isFinite-guarded) instead of the "unavailable (phase 63)" stub.
  • Folder-delete store cleanupcollectDescendantFolderIds BFS purges a deleted folder's descendant FolderNode entries (walk parentId links) so no stale entry survives the isLoaded fast path. Identity stays ipnsName-keyed — no Node.id re-key.
  • Deferred suitesfetchAndDecryptMetadata (load.test.ts) revived against the node/v3 contract; useSharedWriteOps move + batch-move suites revived; bin.test.ts nodeRef fixture populated and restoreFromBin un-skipped; updateFileMetadata CAS suite retired with rationale (current fn is single-shot; equivalent coverage already in file/file-node.test.ts).
  • InviteInfo.itemType dropped — no data source at the invite layer, no consumer.

Locked invariants respected

  • Folder identity stays keyed by ipnsName, never Node.id (a prior UUID re-key caused an orphaned-store bug, 68.1/68.2-09). The re-key TODO was deleted as a documented NON-CHANGE.
  • The live-API-gated integration.test.ts stays describe.skip (3 skipped).

Gates

  • Verify: PASS — all three success criteria met, zero TODO(phase 63/65) markers in product code (79-VERIFICATION.md).
  • Secure: SECURED — 11 threats, 0 open at/above block threshold (79-SECURITY.md).
  • Crypto/privacy review: skipped — no crypto/privacy-adjacent product-code changes (client-side kind projection + display metadata only).
  • Built-in security review: no findings (no unsafe sinks, injection, authz, or secret exposure introduced).
  • Validate (Nyquist): compliant, 0 gaps (79-VALIDATION.md).
  • Unit suites: sdk-core 395, sdk 411 (+3 intentional skips), core 203, web 67 — all green.
  • SDK E2E: 105/106 green — every IPNS publish/resolve and folder-resolve suite passes. The one failure is the TEE lease-renewer test, an infra gap (API TEE_WORKER_URL points at the mock on :3001, so the real worker on :3002 never renews — epoch-mismatch logged at boot), unrelated to this phase's domain. Accepted infra override.
  • CodeRabbit CLI: clean after fixes (0 findings on re-review).

Ship-time review fixes

CodeRabbit CLI (pre-ship) + the PR-level CodeRabbit/Greptile reviews, triaged and addressed:

  • fix(79): DetailsDialog listing-miss fallback used createdAt: 0, which the Number.isFinite guard rendered as "January 1, 1970" — switched to Number.NaN.
  • fix(79): the node decoder defaults a missing createdAt to 0, so a real epoch-0 node also rendered "1970" — both Details Created rows now guard on createdAt > 0 (Greptile P2).
  • fix(79): single-folder delete walked the store for descendants after the SDK delete while batch delete snapshotted before — single-delete now snapshots before too (Greptile P1).
  • fix(79): batch delete removed folders from the store only after the loop, stranding earlier successes on a mid-loop throw — now purges each folder's subtree inline after its own success.
  • fix(79): SharedMoveDialog's single-item title always read "Move Folder" — now reflects the moved item's real kind.
  • test(79): the folder-listing fixture had createdAt === modifiedAt, hiding a field-projection swap — threaded a distinct createdAt and assert the inequality.
  • doc clarity: folder-listing ResolvedChild comment (only size is optional); .planning/STATE.md status reconciled to complete; 79-07-SUMMARY.md createdAt-sentinel record updated.

Triage tally

Across both review stages: 7 code/test fixes + 2 doc reconciliations applied; 0 todos logged. All 4 PR review threads resolved (2 Greptile, 2 CodeRabbit).

Discarded (nits/false-positives/pre-existing): the CodeRabbit CLI "treat unresolved kind as a third state / fail-closed" cluster (79-02/04/05/06 plans) — the folder-safe map-miss default is intentional and documented, the server validates every move/drop, and the suggestions were internally inconsistent (fail-open for drops vs fail-closed for cycle-guards); the SharedMoveDialog "id-space mismatch" (false positive — enumerateSharedSubtree sets id, parentId, and ipnsName all to the ipnsName, verified in code); the bin.test.ts fileKey finding (out-of-domain pre-existing bin wire format; fixture matches current behavior, bin-operations E2E passes); and the .planning/ VALIDATION.md matrix-status nit.

Merge status

Base main advanced by one commit — #610 (Phase 76, fix: harden FUSE publish and TEE write paths) — after this branch forked. The PR therefore shows CONFLICTING, and GitHub cannot build a merge ref, so CI (ci.yml / PR Title Lint / PR Release Preview) has not run yet. The conflict is confined to the two shared planning-tracking files .planning/ROADMAP.md and .planning/STATE.md (bookkeeping only) — there is zero code conflict (Phase 76 touched FUSE/TEE; Phase 79 touches web/sdk). Resolving is a trivial rebase onto the new main, owned by the ship orchestrator; CI will run once rebased.

🤖 Generated with Claude Code

FSM1 added 30 commits July 11, 2026 22:45
8 plans (3 Wave-1 foundation/tests, 5 Wave-2 UI wiring) for web kind-discrimination completion + deferred test revival
- ResolvedChild.createdAt sourced from Node.createdAt in resolveChildren
- Same already-unsealed node, no new codec/seal call
- Add createdAt alongside modifiedAt in all ResolvedChild toEqual assertions
- Add explicit createdAt wiring assertion proving SC2 SDK foundation
- client.test.ts, client-shared-write.test.ts, folder-reresolve.test.ts
  needed no changes -- their Node fixtures already carried createdAt and
  none construct ResolvedChild literals directly
- ResolvedChild.createdAt threaded end-to-end in packages/sdk
- typecheck and full 411-test vitest suite green
- Return resolvedByIpnsName from useFileBrowserActions so
  FileBrowser.tsx and dialogs can read the real kind
- Replace the five hardcoded-folder itemType stubs fed into
  renameItem/moveItem/deleteItem/moveItems/deleteItems with
  isFileRefResolved lookups against resolvedByIpnsName
- Fix the shift-select range sort to use folders-first-then-alpha
  ordering via isFileRefResolved, matching FileList.tsx display order,
  removing the last deferred-phase marker in the file
Delete the stale TODO suggesting a re-key to Node.id for the folder ID
in useFolderNavigation.ts. Folder identity is intentionally keyed by
ipnsName across route params, store lookups, and SDK mutation calls --
a prior UUID-keying attempt caused an orphaned-store-entry bug
(68.1/68.2-09). This is a documentation-only change with zero
code-behavior modification.
grep-verified no consumer reads InviteInfo.itemType -- InviteLinkTab.tsx
(the sole caller of fetchInvitesForItem) never destructures or renders
it. There is also no ResolvedChild/parent listing in scope at the
invite layer to resolve a real kind from. Rather than keep a hardcoded
best-effort default with no data source, drop the field entirely and
remove the stale deferred-phase marker.
- Replace empty object-spread branch with a populated node/v3 file Node
  matching BinEntry.nodeRef, using the fixture shape from 79-PATTERNS.md
- Update the round-trip field assertion to check nodeRef identity/content
  fields now that the fixture is populated (fileKey is not asserted since
  the bin wire form only hex-encodes nodeReadKey, not nested content.fileKey)
- Remove the phase 65 deferred marker
…tract

REVIVE decision. Read the current load.ts fetchAndDecryptMetadata: it composes
fetchFromIpfs then JSON.parse then unsealNode, with no D-13 error-wrapping
try-catch (no CID-in-message typed Error exists in the current implementation).
The skipped suite mocked the retired decryptFolderMetadata export and asserted
a wrapped-error contract that no longer exists.

Rewrote the suite to mock unsealNode instead, asserting the actual current
behavior: malformed JSON rejects with the raw parse error before unsealNode
is reached, a wrong-key rejection propagates unsealNode's own error unchanged,
and the happy path returns unsealNode's Node unchanged. Zero .skip, zero
deferred marker.
…ionale

RETIRE decision. Read the current updateFileMetadata (packages/sdk-core/src/file/index.ts:433):
it is single-shot (rebuilds and republishes the Node directly at fileSequenceNumber+1n, no
expectedSequenceNumber CAS, no 409 retry, no remote-merge), matching shared-write.ts's
updateSharedFile. The quarantined describe.skip mocked retired @cipherbox/core exports
(encryptFileMetadata/decryptFileMetadata) and exercised a CAS-retry/conflict-merge loop
the current function does not have; un-skipping verbatim would not compile against the
current contract.

Discovered this is NOT a coverage gap (contrary to the plan's working assumption):
packages/sdk-core/src/__tests__/file/file-node.test.ts already has a live,
non-skipped 'updateFileMetadata' describe block (Phase 68.1-07) that exercises the
CURRENT single-shot contract end-to-end against the real @cipherbox/core codec —
sequenceNumber threading, nodeId/generation/originalCreatedAt preservation (verified
via a full seal-publish-unseal round-trip), and version-capping via capVersions. The
write-side rollback-guard coverage T-79-03 flags is already satisfied there.

Removed the dead vi.mock scaffolding and unused imports that only served the retired
suite; kept the still-valid mergeVersions suite (and its makeVersion fixture helper)
untouched. Zero .skip, zero deferred marker, zero @ts-expect-error/as-any casts.
Records the REVIVE decision for load.test.ts and the RETIRE decision for
file.test.ts's updateFileMetadata CAS+conflict suite, with the read-first
evidence and rationale for each, per the plan's decision-recording requirement.
- sortItems now sorts folders before files via isFileRefResolved, with
  in-progress UploadVirtualEntry rows short-circuited to file kind
- onDrop and onExternalFileDrop are re-enabled per row, gated on
  isFileRefResolved so only folder rows accept a drop
- toResolvedChildView synthetic fallback now carries createdAt: 0
- FileList passes resolvedByIpnsName down to FileListItem
- removes phase 63 deferred markers in this file
- add resolvedByIpnsName map prop to FileListItem
- multi-select drag branch now derives each item's real per-item kind
  via isFileRefResolved instead of a hardcoded 'folder' stub
- single-item drag branch uses the already-resolved isFolder value
- removes phase 63 deferred markers in this file
- sortItems now sorts folders-first via isFileRefResolved, then localeCompare
- thread resolvedByIpnsName into both SharedMoveDialog render sites
- remove stale phase 63 deferred markers
- movedFolderIds now filters moved items via isFileRefResolved so a file
  cannot disable destinations in the cannot-move-into-own-subtree guard
- add resolvedByIpnsName prop, fed by SharedFileBrowser
- remove stale phase 63 deferred markers
FileBrowser resolves each dialog subject's real kind via isFileRefResolved and passes it into RenameDialog/ConfirmDialog (kind-aware title + delete message), ShareDialog (new kind prop gating the folder slash suffix), and both MoveDialog renders (new resolvedByIpnsName prop). MoveDialog's cannot-move-into-own-subtree cycle guard now filters to folder-kind items so a file cannot impose a phantom cycle constraint. Removes all TODO(phase 63) markers from the three files.
Replace the 'unavailable (phase 63)' Created-row stub in FileDetails and FolderDetails with the same Number.isFinite guard used for the Modified row, rendering formatDate(item.createdAt) from the now-mandatory ResolvedChild.createdAt (Plan 01). Removes the stale FolderDetails read-chain TODO(phase 63) marker.
…dChild

The still-loading/listing-miss fallback ResolvedChild literal gains createdAt: 0 next to the existing modifiedAt: 0 so it typechecks under the now-mandatory ResolvedChild.createdAt (Plan 01). A real ResolvedChild from the listing flows through unchanged with its true createdAt.
removeFolder only drops the single keyed entry, orphaning already-loaded descendant FolderNodes when a parent folder is deleted (a stale entry could be hit by useFolderNavigation's isLoaded fast path). Adds collectDescendantFolderIds, a BFS over the store's parentId links, and applies it at both the single-delete and batch-delete sites. Identity stays ipnsName-keyed — parentId walk only, no Node.id re-key. Removes both TODO(phase 63) markers.
Un-skip the moveItemHandler and batchMoveItemsHandler describe blocks (REQ-2/REQ-6) and add createdAt to the ResolvedChild fixture so the file typechecks under the now-mandatory field (Plan 01). Two revived assertions were stale vs the live signature and are corrected (not re-skipped): the vaultKeypair-absent case surfaces 'Not authenticated' (the live message), and the empty-batch case is a vacuous success that still calls clearSelection (no early-return guard). Removes all TODO(phase 65) markers and the two inline phase-65 annotations.
FSM1 added 7 commits July 12, 2026 01:25
…ry field

makeResolvedChild (useSyncPolling.test.ts) and makeChild (folder.store.test.ts) gain createdAt so both files typecheck under the now-mandatory ResolvedChild.createdAt (Plan 01). folder.store.test.ts was not in the plan's files_modified but carries the same compile break; folding its one-line fixture fix here brings apps/web tsc -b fully green (it was the 4th of the 4 pre-existing errors).
…listing test

DetailsDialog's listing-miss fallback set createdAt: 0, which the Details
Created row rendered as 'January 1, 1970' (Number.isFinite(0) is true) instead
of the intended dim '—'. Use Number.NaN so the guard shows the placeholder.

folder-listing.test.ts fixed createdAt === modifiedAt, so a modifiedAt to
createdAt projection swap in resolveChildren went undetected. Thread a distinct
createdAt through buildChildFixture and assert createdAt !== modifiedAt.
@coderabbitai

coderabbitai Bot commented Jul 12, 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: 30 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: 3c313ff0-ad28-4d85-a908-01e95d8cdc51

📥 Commits

Reviewing files that changed from the base of the PR and between 7099845 and 7056b32.

📒 Files selected for processing (8)
  • .planning/ROADMAP.md
  • .planning/STATE.md
  • .planning/phases/79-web-kind-discrimination-completion-and-deferred-test-revival/79-07-SUMMARY.md
  • apps/web/src/components/file-browser/SharedMoveDialog.tsx
  • apps/web/src/components/file-browser/details/FileDetails.tsx
  • apps/web/src/components/file-browser/details/FolderDetails.tsx
  • apps/web/src/hooks/useFolderMutations.ts
  • packages/sdk/src/folder-listing.ts

Walkthrough

Phase 79 completes resolved file/folder kind handling across the web browser, adds ResolvedChild.createdAt, cleans deleted folder descendants from the store, revives or retires deferred tests, and updates phase documentation and verification records.

Changes

Phase 79 tracking and implementation

Layer / File(s) Summary
SDK metadata contract
packages/sdk/src/folder-listing.ts, packages/sdk/src/__tests__/folder-listing.test.ts
ResolvedChild.createdAt is populated from Node.createdAt and covered by listing and event assertions.
Kind-aware browser interactions
apps/web/src/components/file-browser/*
Sorting, drag payloads, drop targets, dialogs, sharing labels, and move cycle guards use resolvedByIpnsName and isFileRefResolved.
Metadata display and store cleanup
apps/web/src/components/file-browser/details/*, apps/web/src/components/file-browser/DetailsDialog.tsx, apps/web/src/hooks/useFolderMutations.ts
Created dates use finite-value guards, missing dates use sentinels, and folder deletion removes loaded descendants.
Deferred test updates
packages/core/src/__tests__/bin.test.ts, packages/sdk-core/src/**/__tests__/*, apps/web/src/hooks/__tests__/*
Deferred suites are revived or retired, fixtures include createdAt, and expectations match current contracts.
Phase records
.planning/ROADMAP.md, .planning/STATE.md, .planning/phases/79-*/**
Phase 79 is marked complete with eight executed plans and supporting research, security, validation, and verification records.

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

Possibly related PRs

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

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed Docstring coverage is 88.46% 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.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: completing web kind discrimination and reviving deferred test suites.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/web-kind-discrimination-completion-and-deferred-test-revival

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.

@greptile-apps

greptile-apps Bot commented Jul 12, 2026

Copy link
Copy Markdown

Greptile Summary

This PR completes web file-kind handling and restores deferred test coverage. The main changes are:

  • Uses resolved child kind data for file-browser sorting, drag/drop, and dialogs.
  • Adds Created date data to resolved folder listings and details panes.
  • Cleans deleted folder subtrees from the web folder store.
  • Revives or retires deferred test suites with updated fixtures.

Confidence Score: 5/5

This looks safe to merge.

  • No blocking issues found in the changed code.

Important Files Changed

Filename Overview
apps/web/src/hooks/useFolderMutations.ts Snapshots the loaded folder tree before deletion and removes deleted folder subtrees from the store after successful deletes.
packages/sdk/src/folder-listing.ts Adds mandatory Created date data to resolved child listings from the resolved node envelope.
apps/web/src/components/file-browser/DetailsDialog.tsx Uses a non-date sentinel for missing Created date data in the details fallback path.
apps/web/src/components/file-browser/details/FileDetails.tsx Renders file Created dates only when the value is finite and greater than zero.
apps/web/src/components/file-browser/details/FolderDetails.tsx Renders folder Created dates only when the value is finite and greater than zero.

Reviews (3): Last reviewed commit: "Merge branch 'main' into feat/web-kind-d..." | Re-trigger Greptile

Comment thread apps/web/src/hooks/useFolderMutations.ts Outdated
Comment thread packages/sdk/src/folder-listing.ts

@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: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
apps/web/src/components/file-browser/SharedMoveDialog.tsx (1)

168-174: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Use the moved item’s kind in the single-item title.

SharedFileBrowser can open this dialog for files, but the title remains Move Folder. Since the new cycle guard explicitly supports file moves, this produces incorrect user-facing text for single-file moves.

-  const title = isBatchMode ? `Move ${items!.length} items` : 'Move Folder';
+  const title = isBatchMode
+    ? `Move ${items!.length} items`
+    : item && isFileRefResolved(item, resolvedByIpnsName)
+      ? 'Move File'
+      : 'Move Folder';
🤖 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 `@apps/web/src/components/file-browser/SharedMoveDialog.tsx` around lines 168 -
174, Update the single-item title logic in SharedMoveDialog’s title declaration
to use the moved item’s kind, displaying the appropriate file or folder wording
instead of always “Move Folder.” Preserve the existing batch title and label
behavior, including the fallback when no item is available.
apps/web/src/hooks/useFolderMutations.ts (1)

416-464: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Clean up deleted folders as each batch item succeeds. handleDeleteItems defers store removal until the loop finishes, so a later throw skips cleanup entirely. The SDK event only removes the deleted folder itself, which can leave already-deleted folder subtrees with stale descendants in useFolderStore until the next refresh. Move subtree removal inline after each successful delete, or add compensating cleanup in catch.

🤖 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 `@apps/web/src/hooks/useFolderMutations.ts` around lines 416 - 464, Update
handleDeleteItems so each successful deleteWithBehavior call immediately removes
the corresponding folder and its collected descendants from useFolderStore,
rather than deferring all cleanup until the loop completes. Preserve cleanup for
file deletions and ensure earlier successful folder deletions remain removed if
a later batch item throws.
🧹 Nitpick comments (1)
packages/sdk/src/folder-listing.ts (1)

8-9: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Doc comment misgroups mandatory fields with "optional".

createdAt and modifiedAt are non-optional on ResolvedChild (only size is ?), but the phrasing lists them right after "optional", reading as if all three are optional.

✏️ Suggested wording fix
- * optional file size, createdAt, modifiedAt, and the child's own IPNS
- * sequence.
+ * an optional file size, plus the mandatory createdAt, modifiedAt, and the
+ * child's own IPNS sequence.
🤖 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 `@packages/sdk/src/folder-listing.ts` around lines 8 - 9, Update the doc
comment for ResolvedChild to state that only the file size is optional, while
createdAt, modifiedAt, and the child’s own IPNS sequence are mandatory fields.
🤖 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/79-web-kind-discrimination-completion-and-deferred-test-revival/79-07-SUMMARY.md:
- Around line 34-37: Update the createdAt sentinel decision entry in the summary
to record Number.NaN instead of 0, and state that this value causes
DetailsDialog to render the placeholder for missing details. Remove the outdated
epoch-rendering claim while preserving the surrounding context about the
existing modifiedAt behavior.

In @.planning/STATE.md:
- Around line 5-11: Update the status field in the phase metadata to complete so
it matches the completed Phase 79 state and the last_activity_desc indicating
all plans were executed and verified.

---

Outside diff comments:
In `@apps/web/src/components/file-browser/SharedMoveDialog.tsx`:
- Around line 168-174: Update the single-item title logic in SharedMoveDialog’s
title declaration to use the moved item’s kind, displaying the appropriate file
or folder wording instead of always “Move Folder.” Preserve the existing batch
title and label behavior, including the fallback when no item is available.

In `@apps/web/src/hooks/useFolderMutations.ts`:
- Around line 416-464: Update handleDeleteItems so each successful
deleteWithBehavior call immediately removes the corresponding folder and its
collected descendants from useFolderStore, rather than deferring all cleanup
until the loop completes. Preserve cleanup for file deletions and ensure earlier
successful folder deletions remain removed if a later batch item throws.

---

Nitpick comments:
In `@packages/sdk/src/folder-listing.ts`:
- Around line 8-9: Update the doc comment for ResolvedChild to state that only
the file size is optional, while createdAt, modifiedAt, and the child’s own IPNS
sequence are mandatory fields.
🪄 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: a6e7701c-8b9f-4bdc-8e9e-2a293f9f9b79

📥 Commits

Reviewing files that changed from the base of the PR and between 02efe51 and 7099845.

📒 Files selected for processing (46)
  • .planning/ROADMAP.md
  • .planning/STATE.md
  • .planning/phases/79-web-kind-discrimination-completion-and-deferred-test-revival/79-01-PLAN.md
  • .planning/phases/79-web-kind-discrimination-completion-and-deferred-test-revival/79-01-SUMMARY.md
  • .planning/phases/79-web-kind-discrimination-completion-and-deferred-test-revival/79-02-PLAN.md
  • .planning/phases/79-web-kind-discrimination-completion-and-deferred-test-revival/79-02-SUMMARY.md
  • .planning/phases/79-web-kind-discrimination-completion-and-deferred-test-revival/79-03-PLAN.md
  • .planning/phases/79-web-kind-discrimination-completion-and-deferred-test-revival/79-03-SUMMARY.md
  • .planning/phases/79-web-kind-discrimination-completion-and-deferred-test-revival/79-04-PLAN.md
  • .planning/phases/79-web-kind-discrimination-completion-and-deferred-test-revival/79-04-SUMMARY.md
  • .planning/phases/79-web-kind-discrimination-completion-and-deferred-test-revival/79-05-PLAN.md
  • .planning/phases/79-web-kind-discrimination-completion-and-deferred-test-revival/79-05-SUMMARY.md
  • .planning/phases/79-web-kind-discrimination-completion-and-deferred-test-revival/79-06-PLAN.md
  • .planning/phases/79-web-kind-discrimination-completion-and-deferred-test-revival/79-06-SUMMARY.md
  • .planning/phases/79-web-kind-discrimination-completion-and-deferred-test-revival/79-07-PLAN.md
  • .planning/phases/79-web-kind-discrimination-completion-and-deferred-test-revival/79-07-SUMMARY.md
  • .planning/phases/79-web-kind-discrimination-completion-and-deferred-test-revival/79-08-PLAN.md
  • .planning/phases/79-web-kind-discrimination-completion-and-deferred-test-revival/79-08-SUMMARY.md
  • .planning/phases/79-web-kind-discrimination-completion-and-deferred-test-revival/79-LEARNINGS.md
  • .planning/phases/79-web-kind-discrimination-completion-and-deferred-test-revival/79-PATTERNS.md
  • .planning/phases/79-web-kind-discrimination-completion-and-deferred-test-revival/79-RESEARCH.md
  • .planning/phases/79-web-kind-discrimination-completion-and-deferred-test-revival/79-SECURITY.md
  • .planning/phases/79-web-kind-discrimination-completion-and-deferred-test-revival/79-VALIDATION.md
  • .planning/phases/79-web-kind-discrimination-completion-and-deferred-test-revival/79-VERIFICATION.md
  • apps/web/src/components/file-browser/DetailsDialog.tsx
  • apps/web/src/components/file-browser/FileBrowser.tsx
  • apps/web/src/components/file-browser/FileList.tsx
  • apps/web/src/components/file-browser/FileListItem.tsx
  • apps/web/src/components/file-browser/MoveDialog.tsx
  • apps/web/src/components/file-browser/ShareDialog.tsx
  • apps/web/src/components/file-browser/SharedFileBrowser.tsx
  • apps/web/src/components/file-browser/SharedMoveDialog.tsx
  • apps/web/src/components/file-browser/details/FileDetails.tsx
  • apps/web/src/components/file-browser/details/FolderDetails.tsx
  • apps/web/src/components/file-browser/useFileBrowserActions.ts
  • apps/web/src/hooks/__tests__/useSharedWriteOps.test.ts
  • apps/web/src/hooks/__tests__/useSyncPolling.test.ts
  • apps/web/src/hooks/useFolderMutations.ts
  • apps/web/src/hooks/useFolderNavigation.ts
  • apps/web/src/services/invite.service.ts
  • apps/web/src/stores/__tests__/folder.store.test.ts
  • packages/core/src/__tests__/bin.test.ts
  • packages/sdk-core/src/__tests__/file.test.ts
  • packages/sdk-core/src/folder/__tests__/load.test.ts
  • packages/sdk/src/__tests__/folder-listing.test.ts
  • packages/sdk/src/folder-listing.ts

Comment thread .planning/STATE.md
FSM1 added 4 commits July 12, 2026 03:21
…ed date

Single-folder delete walked the store for descendants AFTER the SDK delete,
while batch delete snapshots before; store events during deleteWithBehavior
could prune descendants first, leaving stale entries. Snapshot the loaded
subtree before the delete in the single path too (mirrors batch).

The node decoder defaults a missing createdAt to 0 (Number(obj.createdAt ?? 0)),
which the Details Created row rendered as 'January 1, 1970' since Number.isFinite(0)
is true. Guard both detail panes on createdAt > 0 so a real epoch-0 node renders
the dim placeholder.
…es by kind

handleDeleteItems removed folders from the store only after the whole loop, so
a mid-loop throw stranded already-deleted folders as stale entries. Purge each
folder's subtree from the store immediately after its own successful delete.

SharedMoveDialog's single-item title always read 'Move Folder' even though
shared file moves are supported; it now reflects the moved item's real kind.

Clarify the folder-listing doc comment: only file size is optional; createdAt,
modifiedAt, and sequence are mandatory.
…deferred-test-revival

# Conflicts:
#	.planning/STATE.md
@github-actions github-actions Bot added release:sdk:feat Minor version bump (new feature) for sdk release:web:feat Minor version bump (new feature) for web labels Jul 12, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Release Preview

Package Bump Label Source
sdk minor release:sdk:feat Direct (feat commit)
web minor release:web:feat Direct (feat commit)

@codecov

codecov Bot commented Jul 12, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 70.12%. Comparing base (02efe51) to head (7056b32).

Additional details and impacted files

Impacted file tree graph

@@           Coverage Diff           @@
##             main     #611   +/-   ##
=======================================
  Coverage   70.12%   70.12%           
=======================================
  Files         159      159           
  Lines       15322    15323    +1     
  Branches     1950     1950           
=======================================
+ Hits        10744    10745    +1     
  Misses       4324     4324           
  Partials      254      254           
Flag Coverage Δ
api 84.62% <100.00%> (+<0.01%) ⬆️
api-client 84.62% <100.00%> (+<0.01%) ⬆️
core 84.62% <100.00%> (+<0.01%) ⬆️
crypto 84.62% <100.00%> (+<0.01%) ⬆️
sdk 84.62% <100.00%> (+<0.01%) ⬆️
sdk-core 84.62% <100.00%> (+<0.01%) ⬆️

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

Files with missing lines Coverage Δ
packages/sdk/src/folder-listing.ts 100.00% <100.00%> (ø)
🚀 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.

@FSM1 FSM1 merged commit fcf1596 into main Jul 12, 2026
31 checks passed
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:web:feat Minor version bump (new feature) for web

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant