Skip to content

Live Collab M2 - Automatically update annotation to newest changes #8648

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 77 commits into
base: master
Choose a base branch
from

Conversation

philippotto
Copy link
Member

@philippotto philippotto commented May 22, 2025

From changelog:

When you are viewing an annotation and another user changes that annotation, these changes will be automatically shown. For some changes (e.g., when adding a new annotation layer), you will still need to reload the page, but most of the time WEBKNOSSOS will update the annotation automatically.

Implementation details:

  • I extended the save saga so that it not only polls for the newest version, but also does try to incorporate newer version from the server, if they exist. I named this incorporation "update action application"
  • The update action application does not support all update actions yet (I skipped the ones that I deemed unimportant for this iteration). If an update action cannot be applied, the user is asked to reload the page (as before).
  • I refactored the tests and fixtures quite a bit to get the new tests up and running.

Limitations:

  • adding/removing layers will still require a reload
  • changes to the active mapping will still require a reload <-- this also impacts the very first brush as this will make the null-mapping locked
  • meshes aren't refreshed automatically

URL of deployed dev instance (used for testing):

  • https://___.webknossos.xyz

Steps to test:

  • open wk in two different windows (one incognito) and login as sample and sample2
  • create a new annotation as sample user and copy a sharing link
  • open the same annotation as sample2 (should be read only)
  • perform actions as sample and check that all changes are also shown for sample2
  • support actions include
    • skeleton related actions
    • volume brushing as well as changing the segment list
    • proofreading operations

TODOs:

  • proof of concept
  • skeleton
  • volume
  • proofreading
  • bounding boxes
  • write new tests
    • skeleton
    • proofreading
    • volume
  • final clean up

Issues:


(Please delete unneeded items, merge only when none are left open)

@philippotto philippotto self-assigned this May 22, 2025
@philippotto philippotto changed the base branch from master to annotation-user-state May 22, 2025 08:27
Copy link
Contributor

coderabbitai bot commented May 22, 2025

📝 Walkthrough

Walkthrough

This change implements real-time, live updates for annotation views in WEBKNOSSOS, allowing users to see annotation changes made by others without reloading the page. It introduces mechanisms for incrementally incorporating server-sent update actions into the client state for both skeleton and volume tracings, updates action/reducer logic, and refactors bounding box type usage throughout the codebase.

Changes

File(s) / Group Change Summary
viewer/model/sagas/save_saga.ts, viewer/model/reducers/update_action_application/, viewer/model/actions/, viewer/model/reducers/* Introduce incremental update action incorporation for live annotation updates, add new action types and reducers for server-driven state updates, and update saga logic for conflict resolution and polling.
viewer/model/helpers/compaction/compact_update_actions.ts, viewer/model/sagas/update_actions.ts Update compaction logic to support previous/current tracing comparison, refactor action type names, and clarify action construction.
viewer/model_initialization.ts, viewer/store.ts, viewer/default_state.ts Refactor dataset preprocessing and initialization, introduce StoreDataset type, and mark datasets as preprocessed.
types/api_types.ts, types/bounding_box.ts, viewer/constants.ts, viewer/model/reducers/reducer_helpers.ts, viewer/model/bucket_data_handling/*, viewer/api/api_latest.ts, viewer/view/action-bar/download_modal_view.ts, viewer/view/right-border-tabs/bounding_box_tab.tsx, viewer/controller/scene_controller.ts, viewer/model/helpers/nml_helpers.ts, viewer/model/sagas/min_cut_saga.ts, viewer/model/sagas/volume/floodfill_saga.tsx, viewer/model/helpers/position_converter.ts, viewer/controller/combinations/bounding_box_handlers.ts Refactor and standardize bounding box and axis types, replace BoundingBoxType with BoundingBoxMinMaxType and related protobuf types throughout codebase.
viewer/model/actions/skeletontracing_actions.tsx, viewer/model/actions/skeletontracing_actions_with_effects.tsx, viewer/view/context_menu.tsx, viewer/controller/viewmodes/arbitrary_controller.tsx, viewer/controller/viewmodes/plane_controller.tsx, viewer/view/right-border-tabs/trees_tab/skeleton_tab_view.tsx Move user-interaction-based node/tree deletion logic to a new effects module, update imports accordingly.
viewer/model/sagas/proofread_saga.ts, viewer/model/sagas/mapping_saga.ts, viewer/model/actions/proofread_actions.ts, viewer/controller/combinations/tool_controls.ts Refactor proofreading and mapping sagas, update action creator naming, improve error logging, and centralize mapping updates.
viewer/view/right-border-tabs/dataset_info_tab_view.tsx, viewer/api/wk_dev.ts Add debug info component to display annotation version based on a new dev flag.
test/fixtures/, test/helpers/, test/reducers/, test/sagas/, test/libs/nml.spec.ts, test/schemas/dataset_view_configuration.spec.ts Update and extend test fixtures, helpers, and test suites for new update action flows, bounding box types, and server-driven updates.
tsconfig.json Consolidate TypeScript path aliases.
unreleased_changes/8648.md Add changelog entry describing live update feature.

Assessment against linked issues

Objective Addressed Explanation
Implement live annotation updates for read-only users, reflecting changes made by others in real time (#8664)

Assessment against linked issues: Out-of-scope changes

Code Change Explanation
Addition of debug info in DatasetInfoTabView and related dev flag (viewer/view/right-border-tabs/dataset_info_tab_view.tsx, viewer/api/wk_dev.ts) This UI debug addition is not strictly required for live update functionality, but is minor and related to developer experience.

Possibly related PRs

Suggested reviewers

  • MichaelBuessemeyer
  • fm3

Poem

🐇✨
A hop, a skip, a bounding box anew,
Live updates now shimmer in every view!
No more reloads, just real-time delight,
As annotations change in collaborative light.
Bounding boxes, actions, and sagas align—
The warren of code is now simply divine!
—Your friendly code rabbit

✨ Finishing Touches
  • 📝 Generate Docstrings

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
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@philippotto philippotto force-pushed the live-m2 branch 2 times, most recently from 0ff6084 to 1badc1d Compare May 28, 2025 08:05
@philippotto philippotto force-pushed the live-m2 branch 2 times, most recently from 1240cda to 4699f30 Compare June 10, 2025 12:35
Base automatically changed from annotation-user-state to master June 17, 2025 08:42
Copy link
Contributor

@MichaelBuessemeyer MichaelBuessemeyer left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And here is the missing feedback regarding the testing code.

Kudos for digging through all these. This seems pretty complicated and like tedious work. #fleißgesbienchen 🐝

Comment on lines 144 to 152
getEdgesForAgglomerateMinCut: vi.fn(
(
_tracingStoreUrl: string,
_tracingId: string,
_segmentsInfo: unknown,
): Promise<Array<MinCutTargetEdge>> => {
throw new Error("Not yet mocked.");
},
),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But why mock it then? Or is this still a todo for this pr?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I changed the content of the function to:

// This simply serves as a preparation so that specs can mock the function
// when needed. Without this stub, it's harder to mock this specific function
// later.
throw new Error("No test has mocked the return value yet here.");

I hope this makes it clearer.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (3)
frontend/javascripts/test/reducers/update_action_application/skeleton.spec.ts (2)

156-215: Complex but comprehensive test coverage.

This nested test structure provides thorough coverage by testing all combinations of before/after version indices with and without compaction. While complex, this approach ensures the diff re-application mechanism works correctly across all scenarios.

The complexity concern raised in past reviews is valid, but the comprehensive nature of this test is essential for validating the live collaboration feature's correctness.


278-280: Essential verification of action type coverage.

This ensures that each possible action type is included in the testing at least once, providing confidence in comprehensive test coverage.

frontend/javascripts/viewer/model/reducers/update_action_application/skeleton.ts (1)

178-191: Missing active tree/node handling in tree deletion.

The deleteTree case should also clear the active tree and node if the deleted tree was active, similar to how deleteNode handles active node deletion.

 case "deleteTree": {
   const { id } = ua.value;
   const skeleton = enforceSkeletonTracing(state.annotation);
   const updatedTrees = skeleton.trees.delete(id);
 
+  const newActiveTreeId = skeleton.activeTreeId === id ? null : skeleton.activeTreeId;
+  const newActiveNodeId = skeleton.activeTreeId === id ? null : skeleton.activeNodeId;
+
   return update(state, {
     annotation: {
       skeleton: {
         trees: { $set: updatedTrees },
         cachedMaxNodeId: { $set: getMaximumNodeId(updatedTrees) },
+        activeTreeId: { $set: newActiveTreeId },
+        activeNodeId: { $set: newActiveNodeId },
       },
     },
   });
 }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 26f02fb and 1662292.

📒 Files selected for processing (10)
  • frontend/javascripts/test/fixtures/hybridtracing_server_objects.ts (1 hunks)
  • frontend/javascripts/test/fixtures/skeletontracing_server_objects.ts (2 hunks)
  • frontend/javascripts/test/fixtures/tasktracing_server_objects.ts (3 hunks)
  • frontend/javascripts/test/helpers/apiHelpers.ts (10 hunks)
  • frontend/javascripts/test/reducers/update_action_application/skeleton.spec.ts (1 hunks)
  • frontend/javascripts/viewer/default_state.ts (4 hunks)
  • frontend/javascripts/viewer/model/reducers/skeletontracing_reducer.ts (8 hunks)
  • frontend/javascripts/viewer/model/reducers/update_action_application/skeleton.ts (1 hunks)
  • frontend/javascripts/viewer/model/reducers/update_action_application/volume.ts (1 hunks)
  • frontend/javascripts/viewer/model/reducers/volumetracing_reducer.ts (4 hunks)
🚧 Files skipped from review as they are similar to previous changes (5)
  • frontend/javascripts/viewer/model/reducers/skeletontracing_reducer.ts
  • frontend/javascripts/viewer/default_state.ts
  • frontend/javascripts/test/fixtures/hybridtracing_server_objects.ts
  • frontend/javascripts/viewer/model/reducers/volumetracing_reducer.ts
  • frontend/javascripts/test/helpers/apiHelpers.ts
🧰 Additional context used
🪛 GitHub Check: frontend-tests
frontend/javascripts/viewer/model/reducers/update_action_application/skeleton.ts

[failure] 350-350:
Property 'name' does not exist on type 'never'.

⏰ Context from checks skipped due to timeout of 90000ms (2)
  • GitHub Check: backend-tests
  • GitHub Check: build-smoketest-push
🔇 Additional comments (14)
frontend/javascripts/test/fixtures/skeletontracing_server_objects.ts (2)

7-7: Good practice: Centralizing view mode constants.

The import of ViewModeValues aligns with the broader refactoring effort to centralize view mode definitions across the codebase.


209-209: Verify that ViewModeValues contains the expected view modes.

The replacement of the hardcoded array with ViewModeValues is a good practice for maintainability. Please ensure that the constant contains the same values as the previous hardcoded array ["orthogonal", "oblique", "flight"].

#!/bin/bash
# Description: Verify that ViewModeValues contains the expected view modes
# Expected: ViewModeValues should contain ["orthogonal", "oblique", "flight"]

# Find the ViewModeValues constant definition
ast-grep --pattern 'export const ViewModeValues = $_'

# Also search for any ViewModeValues definition or usage
rg -A 3 "ViewModeValues"
frontend/javascripts/test/fixtures/tasktracing_server_objects.ts (3)

7-7: Consistent import pattern with other fixture files.

The import of ViewModeValues follows the same pattern as the skeleton tracing fixture, maintaining consistency across the test fixtures.


92-92: Good refactoring: Centralized constants for task type settings.

Replacing the hardcoded array with ViewModeValues in the task type settings improves maintainability and consistency.


150-150: Consistent usage of ViewModeValues across all settings.

The replacement of the hardcoded array in the top-level settings maintains consistency with the task type settings above and other fixture files.

frontend/javascripts/test/reducers/update_action_application/skeleton.spec.ts (3)

59-82: Excellent type safety approach for action coverage.

The actionNamesHelper dictionary ensures compile-time verification that all applicable skeleton update actions are tested. This is a robust approach that will catch missing test coverage when new action types are added.


247-276: Test logic for active tree deletion is correct.

This test properly verifies that both active tree and active node are cleared when the active tree is deleted, which is the expected behavior.


217-245: Let’s inspect how server-side deleteNode updates are applied and how the reducer handles client-side delete and active-node state:

#!/bin/bash
set -e

echo "----- Server-update deleteNode case in update_action_application/skeleton.ts -----"
sed -n '200,400p' frontend/javascripts/viewer/model/reducers/update_action_application/skeleton.ts

echo
echo "----- Client deleteNodeAction handling in skeletontracing_reducer.ts -----"
sed -n '720,820p' frontend/javascripts/viewer/model/reducers/skeletontracing_reducer.ts

echo
echo "----- setActiveNodeAction handling in skeletontracing_reducer.ts -----"
rg -n "setActiveNodeAction" -g "frontend/javascripts/viewer/model/reducers/skeletontracing_reducer.ts" -C 8
frontend/javascripts/viewer/model/reducers/update_action_application/skeleton.ts (2)

19-30: Clean function structure for batch action application.

The function correctly iterates through actions and applies them sequentially, maintaining immutability throughout the process.


38-57: Proper tree creation logic with unused parameter handling.

The destructuring correctly ignores server-specific parameters (updatedId, actionTracingId) and creates a new tree with the appropriate client-side structure.

frontend/javascripts/viewer/model/reducers/update_action_application/volume.ts (4)

17-31: Well-structured function signature and implementation.

The function properly accepts the reducer as a parameter and applies actions sequentially. The parameter ordering is consistent and logical.


46-58: Correct handling of segment creation and updates.

The destructuring properly separates server-specific fields and transforms anchorPosition to somePosition for client-side compatibility.


92-97: Appropriate handling of user-specific actions.

Correctly ignores user-specific update actions (expanded state and visibility) that don't need to be applied for other users in the collaborative environment.


98-107: Clean exhaustive type checking without unreachable code.

Unlike the skeleton implementation, this properly handles the default case without unreachable code issues.

…ng accessor; fix nulling activeTreeId when it the active tree deleted
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (4)
frontend/javascripts/test/reducers/update_action_application/skeleton.spec.ts (2)

156-215: Comprehensive test coverage for update action application.

The parameterized test approach with compaction modes and version ranges provides excellent coverage of the update action application mechanism. This ensures the system correctly handles both incremental updates and compacted update sequences.


217-279: Well-designed edge case tests for active item deletion.

These tests ensure that the active node and tree selections are properly cleared when the selected items are deleted, preventing invalid application state.

frontend/javascripts/viewer/model/reducers/update_action_application/skeleton.ts (2)

304-304: Fix incorrect active node ID update logic.

When deleting a node that is not the active node, the active node ID should remain unchanged, not be set to the deleted node's ID.

Apply this fix:

-      const newActiveNodeId = skeleton.activeNodeId === nodeId ? null : nodeId;
+      const newActiveNodeId = skeleton.activeNodeId === nodeId ? null : skeleton.activeNodeId;

347-355: Remove unreachable code and fix TypeScript error.

The code after the switch statement is unreachable and causes TypeScript errors. Move the error handling into the default case.

Apply this fix:

     default: {
       ua satisfies never;
+      throw new Error(`Unexpected update action: ${(ua as any).name}`);
     }
   }
-  ua satisfies never;
-
-  // Satisfy TS.
-  throw new Error("Reached unexpected part of function.");
 }
🧹 Nitpick comments (1)
frontend/javascripts/test/reducers/update_action_application/skeleton.spec.ts (1)

286-298: Consider removing or documenting the debug function.

The _debugLogTrees function appears to be unused debug code. Consider either removing it or adding a comment explaining when it should be used for debugging test failures.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1662292 and 7b3adab.

📒 Files selected for processing (2)
  • frontend/javascripts/test/reducers/update_action_application/skeleton.spec.ts (1 hunks)
  • frontend/javascripts/viewer/model/reducers/update_action_application/skeleton.ts (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (3)
  • GitHub Check: frontend-tests
  • GitHub Check: build-smoketest-push
  • GitHub Check: backend-tests
🔇 Additional comments (2)
frontend/javascripts/test/reducers/update_action_application/skeleton.spec.ts (1)

59-82: Excellent test coverage tracking mechanism!

Using a TypeScript Record type to ensure all ApplicableSkeletonUpdateAction types are covered is a clever approach. This guarantees compile-time verification that the test suite remains comprehensive as new action types are added.

frontend/javascripts/viewer/model/reducers/update_action_application/skeleton.ts (1)

19-30: Well-structured implementation of server update action application.

The function properly iterates through actions and maintains state immutability throughout the update process. The separation of concerns with applySingleAction makes the code maintainable and testable.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (3)
frontend/javascripts/viewer/model/reducers/update_action_application/skeleton.ts (2)

305-305: Critical bug in active node handling logic.

The logic incorrectly sets newActiveNodeId to the deleted node's ID when the deleted node is not the active one.

Apply this fix:

-      const newActiveNodeId = skeleton.activeNodeId === nodeId ? null : nodeId;
+      const newActiveNodeId = skeleton.activeNodeId === nodeId ? null : skeleton.activeNodeId;

352-355: Remove unreachable code causing TypeScript errors.

The code after the switch statement is unreachable and causes TypeScript compilation issues.

Remove the unreachable code:

   }
-  ua satisfies never;
-
-  // Satisfy TS.
-  throw new Error("Reached unexpected part of function.");
frontend/javascripts/viewer/model/sagas/save_saga.ts (1)

521-524: Restore production polling intervals before merge.

The polling intervals are still set to aggressive 1-second values for debugging, which will cause excessive server load in production.

-// todop: restore to 10, 60, 30 ?
-const VERSION_POLL_INTERVAL_COLLAB = 1 * 1000;
-const VERSION_POLL_INTERVAL_READ_ONLY = 1 * 1000;
-const VERSION_POLL_INTERVAL_SINGLE_EDITOR = 1 * 1000;
+const VERSION_POLL_INTERVAL_COLLAB = 10 * 1000;
+const VERSION_POLL_INTERVAL_READ_ONLY = 60 * 1000;
+const VERSION_POLL_INTERVAL_SINGLE_EDITOR = 30 * 1000;
🧹 Nitpick comments (2)
frontend/javascripts/viewer/model/reducers/update_action_application/skeleton.ts (1)

39-40: Document unused legacy properties.

Good practice to explicitly document why updatedId and actionTracingId are extracted but not used.

The comment on line 39 effectively explains this for updatedId, but consider making it more prominent:

-      // updatedId is part of the updateAction format but was never really used.
+      // updatedId and actionTracingId are part of the updateAction format but are unused here
+      // (kept for backwards compatibility with server update action format)
frontend/javascripts/viewer/model/sagas/save_saga.ts (1)

871-871: Consider structured logging for unsupported actions.

The console.log for unsupported actions could be enhanced for better debugging.

-          console.log("Cannot apply action", action.name);
+          console.warn("Cannot apply update action during incremental sync:", action.name);
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0928f6c and f22bc56.

📒 Files selected for processing (7)
  • frontend/javascripts/test/sagas/volumetracing/volumetracing_saga_integration_1.spec.ts (1 hunks)
  • frontend/javascripts/viewer/api/api_latest.ts (7 hunks)
  • frontend/javascripts/viewer/model/bucket_data_handling/data_cube.ts (6 hunks)
  • frontend/javascripts/viewer/model/reducers/update_action_application/skeleton.ts (1 hunks)
  • frontend/javascripts/viewer/model/sagas/proofread_saga.ts (11 hunks)
  • frontend/javascripts/viewer/model/sagas/save_saga.ts (11 hunks)
  • frontend/javascripts/viewer/view/version_list.tsx (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • frontend/javascripts/viewer/model/bucket_data_handling/data_cube.ts
  • frontend/javascripts/viewer/model/sagas/proofread_saga.ts
  • frontend/javascripts/viewer/api/api_latest.ts
⏰ Context from checks skipped due to timeout of 90000ms (2)
  • GitHub Check: build-smoketest-push
  • GitHub Check: backend-tests
🔇 Additional comments (5)
frontend/javascripts/test/sagas/volumetracing/volumetracing_saga_integration_1.spec.ts (1)

92-92: LGTM! Method rename improves semantic clarity.

The rename from collectAllBuckets() to removeAllBuckets() better describes the actual operation being performed and aligns with the broader refactoring effort across the codebase.

frontend/javascripts/viewer/view/version_list.tsx (1)

83-83: LGTM! Consistent method rename improves semantic clarity.

The rename from collectAllBuckets() to removeAllBuckets() is consistent with the broader refactoring effort and better describes the bucket clearing operation performed during version preview.

frontend/javascripts/viewer/model/reducers/update_action_application/skeleton.ts (1)

166-230: Well-implemented tree component movement logic.

The moveTreeComponent case correctly handles the complex operation of moving nodes and edges between trees, including proper separation of moved vs remaining elements and safe cloning of data structures.

frontend/javascripts/viewer/model/sagas/save_saga.ts (2)

686-884: Well-architected incremental update mechanism.

The tryToIncorporateActions function is well-structured with:

  • Clear separation of user-specific vs shared state updates
  • Proper delegation to appropriate reducers and sagas
  • Comprehensive action type coverage
  • Effective finalization pattern for bucket/mapping updates

The error handling gracefully falls back to user notification when incorporation fails.


596-617: Robust error handling for update action incorporation.

The try-catch around tryToIncorporateActions properly handles failures and falls back to user notification, preventing crashes while maintaining user experience.

@philippotto
Copy link
Member Author

@MichaelBuessemeyer thank you for your review! I think I addressed most of it (I already marked several comments as resolved but please have a look at the remaining ones if they can also be resolved in your opinion).
Do you want to do some testing next? afterwards, I'd do the final clean up (with removing logs etc).

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (2)
frontend/javascripts/viewer/model/sagas/save_saga.ts (2)

521-524: Temporary debugging intervals already addressed in past reviews.

The aggressive 1-second polling intervals with TODO comment have been previously flagged and acknowledged by the team. This is intentional for testing the live collaboration functionality.


680-681: Remove debug toast and TODO comment.

The debug error toast and TODO comment should be addressed before production.

🧹 Nitpick comments (1)
frontend/javascripts/viewer/model/sagas/save_saga.ts (1)

686-884: Consider refactoring the large tryToIncorporateActions function for better maintainability.

The tryToIncorporateActions function is quite large (~200 lines) and handles multiple responsibilities through a complex switch statement. This makes it harder to maintain, test, and reason about.

Consider refactoring by:

  1. Extracting action type handlers into separate functions (e.g., handleSkeletonActions, handleVolumeActions, handleProofreadingActions)
  2. Using a mapping object to route actions to their handlers
  3. Moving the finalization logic to a separate concern

Example refactor structure:

+const actionHandlers = {
+  skeleton: handleSkeletonActions,
+  volume: handleVolumeActions, 
+  proofreading: handleProofreadingActions,
+  ignored: handleIgnoredActions,
+};
+
+function* handleSkeletonActions(action, finalizers) {
+  // Handle skeleton-specific actions
+}
+
 export function* tryToIncorporateActions(newerActions) {
   // ... setup code ...
   for (const actionBatch of newerActions) {
     for (const action of actionBatch.value) {
-      switch (action.name) {
-        // ... 100+ lines of cases ...
-      }
+      const handler = getActionHandler(action.name);
+      yield* handler(action, finalizers);
     }
   }
 }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f22bc56 and a94a7a6.

📒 Files selected for processing (3)
  • frontend/javascripts/viewer/model/sagas/save_saga.ts (11 hunks)
  • frontend/javascripts/viewer/model_initialization.ts (8 hunks)
  • frontend/javascripts/viewer/store.ts (6 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • frontend/javascripts/viewer/store.ts
  • frontend/javascripts/viewer/model_initialization.ts
⏰ Context from checks skipped due to timeout of 90000ms (3)
  • GitHub Check: backend-tests
  • GitHub Check: build-smoketest-push
  • GitHub Check: frontend-tests
🔇 Additional comments (1)
frontend/javascripts/viewer/model/sagas/save_saga.ts (1)

508-510: LGTM - Appropriate parameter addition for compaction.

The addition of prevTracing parameter to compactUpdateActions is consistent with the function signature changes and enables better compaction logic by comparing previous and current states.

@MichaelBuessemeyer MichaelBuessemeyer self-requested a review June 25, 2025 12:20
Copy link
Contributor

@MichaelBuessemeyer MichaelBuessemeyer left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, here is my testing result. Sorry, I found some bugs and for some of them I cannot clearly say how to reproduce them.

  • Sadly, I managed to get into a error case in an annotation with already a skeleton and drawn segments visible. The error occured when I selected a new cell, drew in the YZ viewport a bit larger area and then hit save. After that the private tab getting the updates changed to: "You need to reload". Not sure how easy it is to reproduce this. The error was:
    image
  • I also got into this state when starting to do some node moving, activation and such. Afterwards, I did some flood fills which the server action applying does not seem to handle correctly currently.
    image
  • Applying skeleton group update apparently also has bugs:
    This is the original state
    image
    This the state of the annotation which was long opened: One can see that the groups were not created and trees were lost / are no longer rendered.
    image
    And here the state of the annotation which was loaded after Groups "Gourp 1" "Blue" and "Schmu" already existed. This shows that the groups wich existed before the annotation was opened are ok, but once a new group is created, this is not synced.
    image
  • Proofreading looked good. I did not manage to break anything there
  • Volume interactions with an agglomerate mapping active also seems to work

Comment on lines +350 to +355
}
}
ua satisfies never;

// Satisfy TS.
throw new Error("Reached unexpected part of function.");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The ua satisfies never; part is now also needed because TS is not smart enough / too pessimistic or so?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the throw-error part is because TS is not smart enough. satisfies-never is only there because I wanted to be extra sure that TS is really not smart enough ^^ in other words: TS agrees that ua satisfies never. so, the end of this function should not be reachable and TS should know this (from my understanding).

@philippotto
Copy link
Member Author

Ok, here is my testing result. Sorry, I found some bugs and for some of them I cannot clearly say how to reproduce them.

  • Sadly, I managed to get into a error case in an annotation with already a skeleton and drawn segments visible. The error occured when I selected a new cell, drew in the YZ viewport a bit larger area and then hit save. After that the private tab getting the updates changed to: "You need to reload". Not sure how easy it is to reproduce this. The error was:
    image
  • I also got into this state when starting to do some node moving, activation and such. Afterwards, I did some flood fills which the server action applying does not seem to handle correctly currently.
    image

Oops, there was a race condition when the frontend requested the newestVersion (number) from the backend and afterwards it fetched all newer versions (now, there might be more versions than before). I fixed this in my latest commit.

  • Applying skeleton group update apparently also has bugs:

I'll have a look.

@philippotto
Copy link
Member Author

I fixed the tree group bug, too. It was a "permission problem" because the annotation was opened in read-only mode and some check prevented the update actions from being applied. I adapted the tests to reflect the read-only aspect and fixed them. Please have a look at my latest commits 🙏

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (3)
frontend/javascripts/test/helpers/utils.ts (1)

19-29: Consider adding JSDoc documentation.

The helper function could benefit from JSDoc comments explaining its purpose and parameters, especially since it manipulates nested state properties.

+/**
+ * Updates the allowUpdate flag in the annotation restrictions of a WebknossosState.
+ * @param state The state to update
+ * @param value The new value for the allowUpdate flag
+ * @returns A new state with the updated allowUpdate flag
+ */
 function overrideAllowUpdateInState(state: WebknossosState, value: boolean) {
frontend/javascripts/test/reducers/update_action_application/skeleton.spec.ts (2)

157-218: Well-structured parameterized test but consider debugging complexity.

The nested parameterized test structure is comprehensive but may be challenging to debug if failures occur. The logic correctly tests update action application with and without compaction across different state version ranges.

Consider adding more descriptive test names or helper functions to make debugging easier if this test fails:

-        test.each(afterVersionIndices)("To v=%i", (afterVersionIndex: number) => {
+        test.each(afterVersionIndices)("To v=%i (%i actions)", (afterVersionIndex: number) => {
+          const actionCount = afterVersionIndex - beforeVersionIndex + 1;

294-306: Remove unused debug function.

This debug logging function appears to be leftover development code and should be removed to keep the test file clean.

-function _debugLogTrees(prefix: string, state: WebknossosState) {
-  const size = state.annotation.skeleton!.trees.getOrThrow(1).nodes.size();
-  console.log("logTrees. size", size);
-  for (const tree of state.annotation.skeleton!.trees.values()) {
-    console.log(
-      `${prefix}. tree.id=${tree.treeId}.`,
-      "edges: ",
-      Array.from(tree.edges.values().map((edge) => `${edge.source}-${edge.target}`)).join(", "),
-      "nodes: ",
-      Array.from(tree.nodes.values().map((n) => n.id)).join(", "),
-    );
-  }
-}
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 51e08f1 and 33bb355.

📒 Files selected for processing (4)
  • frontend/javascripts/test/helpers/utils.ts (1 hunks)
  • frontend/javascripts/test/reducers/update_action_application/skeleton.spec.ts (1 hunks)
  • frontend/javascripts/test/reducers/update_action_application/volume.spec.ts (1 hunks)
  • frontend/javascripts/viewer/model/reducers/skeletontracing_reducer.ts (8 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • frontend/javascripts/test/reducers/update_action_application/volume.spec.ts
  • frontend/javascripts/viewer/model/reducers/skeletontracing_reducer.ts
🧰 Additional context used
🧬 Code Graph Analysis (1)
frontend/javascripts/test/helpers/utils.ts (1)
frontend/javascripts/viewer/store.ts (1)
  • WebknossosState (568-588)
⏰ Context from checks skipped due to timeout of 90000ms (3)
  • GitHub Check: build-smoketest-push
  • GitHub Check: backend-tests
  • GitHub Check: frontend-tests
🔇 Additional comments (6)
frontend/javascripts/test/helpers/utils.ts (1)

4-17: LGTM! Well-designed utility function for testing.

The implementation correctly handles the read-only state transformation pattern and uses appropriate immutable update patterns with the immutability-helper library.

frontend/javascripts/test/reducers/update_action_application/skeleton.spec.ts (5)

32-51: LGTM! Proper test state initialization.

The initial state setup correctly configures the annotation restrictions and adds necessary data layers for testing.


60-83: Excellent type-safe test coverage mechanism!

This approach using a TypeScript record ensures that all ApplicableSkeletonUpdateAction types are covered in tests. If new action types are added, TypeScript will enforce adding them to this dictionary.


98-140: Comprehensive test action sequence covers complex scenarios.

The sequence effectively tests various skeleton operations including node/tree creation, deletion, merging, renaming, and user bounding box operations. This provides thorough coverage of the update action application logic.


206-212: Good use of the transformStateAsReadOnly utility.

The usage correctly simulates read-only state during update action application, which aligns with the live collaboration feature requirements.


288-291: Excellent test coverage validation.

The afterAll check ensures all action types have been exercised during testing, providing confidence in the test suite's completeness.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Milestone 2: Live Updates for read-only users
2 participants