Skip to content

Improve layer identity system; improve save workflow#222

Draft
C-Achard wants to merge 44 commits into
mainfrom
cy/refactor-layer-identity-and-save
Draft

Improve layer identity system; improve save workflow#222
C-Achard wants to merge 44 commits into
mainfrom
cy/refactor-layer-identity-and-save

Conversation

@C-Achard

@C-Achard C-Achard commented Jun 3, 2026

Copy link
Copy Markdown
Collaborator

Motivation

While the current system based around the layer manager works well, checks related to layer identity and specifications (e.g. for saving) are not properly centralized.
This makes tracking layer identities and capabilities more difficult, and offloads reponsibilities to downstream workflows (e.g. saving), which will lead to duplicated logic and already makes the plugin layer I/O harder to track.

In addition, the current save workflow does not properly account for deletions, which is now fixed using the new system of layer identity to ensure saving is done correctly per layer role in the plugin, as well as showing proper UI warnings when saving report deletions.

Goals

TODO

  • Implement layer identity/save specs system
  • Redo wiring throughout the plugin to use the identity system instead of local checks
  • Fix the saving behavior for deletions and update the UI accordingly
  • Machine layers integration
  • Introduce proper regression tests around new behavior
  • Test & review

C-Achard added 27 commits June 3, 2026 09:22
Add save-aware dataframe utilities and deletion-aware conflict detection. Introduces canonical_keypoint_columns_from_header, save_index_from_points_metadata, complete_df_for_save and merge_save_df to normalize headers, expand the save scope (inserting NaNs for deleted keypoints) and merge new saves with existing DLC files while preserving out-of-scope rows. keypoint_conflicts now supports include_deletions to report destructive deletions (old present, new absent) and combines overwrite+deletion checks. Integrate these behaviors into compute_overwrite_report_for_points_save and write_hdf, and update imports to include PointsMetadata and the new helpers.
Move _drop_likelihood_columns from core/io.py into core/dataframes.py as drop_likelihood_columns and update callers/tests to use the new location. Add _keypoint_group_levels and keypoint_deletions to detect destructive keypoint removals, refactor keypoint_conflicts to delegate deletion detection, and have compute_overwrite_report_for_points_save include deletion info. Extend build_overwrite_conflict_report and models (ConflictEntry, OverwriteConflictReport) to surface deleted_keypoints and n_deletions, prefer reporting deletions over overwrites when both occur, and update has_conflicts to account for deletions.
Refactor and enhance the overwrite-conflicts UI and report handling.

- Use OverwriteConflictReport from config.models (import updated).
- Add helpers to build plain-text details, HTML summary, and affected summary from reports (with backward compatibility to older report models).
- Make dialog content richer: enable rich-text summaries, selectable labels, configurable details label and confirm button text, and variable default button behavior for dangerous deletions.
- Compute dynamic dialog title and texts in maybe_confirm_overwrite (handle deletions specially), escape context strings, and rely on content-driven sizing.

These changes improve clarity around keypoint deletions vs overwrites and make the dialog safer and more informative for users.
Import QPalette and add a helper to compute an inverted parent background color. Insert a thin separator line in OverwriteConflictsDialog using that color. Update conflict summary wording to be shorter/clearer, and adjust confirmation button and details label text/formatting (use italics for the details header). Minor UI copy and layout cleanups.
Refactor keypoint_deletions to use harmonize_keypoint_row_index and harmonize_keypoint_column_index. The old align_old_new flow was replaced by explicit reindexing of old/new frames to the new save scope (index and columns) so deletion detection is restricted to the current scope. Minor docstring/comment cleanup and formatting adjustments; logic for grouping by keypoint levels and computing deletions is unchanged.
Refactor tests in core/test_conflicts.py to exercise deletion conflict handling and dataframe completion logic. Introduces a simple _HeaderStub and updates _stub_validation_pipeline to return a completed_df and monkeypatch complete_df_for_save (and captures its inputs). Replaces references to dataframes_mod with conflicts_mod for functions under test (keypoint_conflicts, keypoint_deletions, build_overwrite_conflict_report) and updates expectations accordingly. Adds new tests to ensure deletion conflicts are passed to the report builder and to cover extracted-labels overwrite-report behavior and fallbacks.
Extend dataframe tests to cover complete_df_for_save, merge_save_df, keypoint_deletions, build_overwrite_conflict_report, and drop_likelihood_columns. New tests verify expansion of sparse napari data to full save scope, dropping of likelihood cols, merging behavior that preserves rows/columns outside save scope and respects intentional NaN deletions, detection/harmonization of deletions across basename vs deep-path indices, and conflict report counting of overwrites vs deletions.
Introduce utilities for tagging and reading Napari layer identity metadata for the DeepLabCut plugin. Adds enums (LayerRole, FrameLayerType, LayerSaveBehavior), metadata keys, and helpers to mark config-derived placeholder layers (tag_config_placeholder_metadata), get/set role and save behavior, and check NO_DELETIONS semantics. These utilities let the plugin distinguish layer origins and control save/merge behavior for annotations.
Introduce and use explicit layer identity metadata across readers and lifecycle manager. Readers now set FrameLayerType (IMAGES/VIDEO) and apply LayerRole.FRAMES via set_layer_role_metadata for frame layers; read_config tags config-placeholder metadata. The LayerLifecycleManager gains identity helpers (layer_role_from_metadata, dlc_meta_for_layer, is_dlc_session_image_layer), save-behavior helpers for Points layers, and updated is_config_placeholder_points_layer to prefer explicit identity metadata while keeping a legacy fallback and warning. Overall replaces ad-hoc string role checks with enum-backed, explicit metadata handling for more robust lifecycle behavior.
Introduce explicit tag_* helpers for layer metadata and update the reader to use them. _reader.py now calls tag_frames_metadata instead of set_layer_role_metadata/LayerRole.FRAMES. core/layer_lifecycle/identity.py was reorganized: the config-placeholder helper was moved/renamed placement, tag_frames_metadata was added, and a new tag_tracking_result_metadata helper was introduced. These changes centralize and clarify layer metadata tagging for frames, config-derived placeholders, and tracking results.
Introduce an allow_deletions: bool = True parameter to complete_df_for_save to control save semantics. When True (default) the function expands to the full editable DLC save scope using the header-derived canonical keypoint columns (dropping likelihoods) so missing keypoints become explicit NaN and can clear old saved values during merge. When False, the function preserves only explicitly present keypoint columns (df_copy.columns) and does not materialize NaNs for unmentioned bodyparts, allowing old on-disk values outside this layer's explicit scope to be preserved. Docstring and in-function branching updated to reflect the new behavior.
Use the shared layer-lifecycle helpers to tag tracking-result metadata and make the tracking kind dynamic based on tracker_name. build_tracking_result_metadata now calls tag_tracking_result_metadata and writes kind as "{tracker_name}-result". is_tracking_result_points_layer now recognizes the new LayerRole.TRACKING_RESULT tag and retains a legacy fallback that compares kind dynamically. Tests were updated/added to reflect these changes (use a tracker variable, verify role tagging, and ensure backward compatibility).
Propagate the layer manager's save-behavior decision into save preflight and writer so deletions can be disabled. compute_overwrite_report_for_points_save now reads save_behavior_disallows_deletions from metadata, passes allow_deletions into complete_df_for_save, and skips deletion conflict checks when deletions are disallowed. write_hdf likewise checks save behavior and forwards allow_deletions to complete_df_for_save. The save UI adds _apply_layer_save_identity_to_metadata (and DLC_SAVE_BEHAVIOR_KEY) to embed the chosen save behavior into layer metadata before preflight/write; numpy is imported to handle empty-point checks. Changes touch core/conflicts.py, core/io.py, and ui/ui_dialogs/save.py to align UI, preflight and write semantics.
In complete_df_for_save, avoid reindexing the DataFrame's rows when allow_deletions is False so that unmentioned images/bodyparts are not materialized as NaN. When deletions are allowed, keep the previous behavior of reindexing both index and columns; otherwise only reindex columns. This preserves sparse/layered annotation semantics and prevents creating implicit deletions. Added a test (test_complete_df_for_save_no_deletions_does_not_materialize_unmentioned_rows_or_bodyparts) to assert the expected behavior.
Convert the module-level _apply_layer_save_identity_to_metadata function into a method on PointsLayerSaveWorkflow so it can access self.layer_manager. Remove the old top-level definition and update the single-layer save path to call self._apply_layer_save_identity_to_metadata(...) instead of the standalone function. This keeps save-time metadata logic colocated with the workflow and ensures the manager's save behavior is applied consistently.
Adjust fake_complete_df_for_save in test_conflicts._stub_validation_pipeline to include the new allow_deletions=True parameter and record it in the seen dict. This keeps the test stub in sync with the updated function signature used by the code under test.
Split and clarify placeholder detection: add is_empty_points_layer and is_config_derived_points_layer, and rename the legacy predicate to is_empty_config_placeholder_points_layer. Update call sites in the layer lifecycle manager and save workflow to use the more specific checks (separating explicit config-derived identity from the legacy empty-layer heuristic) and replace direct emptiness checks. Also remove the previous warning log related to legacy fallback.
Introduce TRACKING_RESULT_TYPE = "tracking-result" and update code/tests to use this constant for the metadata "kind" value. build_tracking_result_metadata and is_tracking_result_points_layer now reference the constant instead of composing a per-tracker f-string, and unit tests were updated accordingly to assert the new constant. This centralizes the result kind value for consistency across tracking result layers.
Rename core/schemas.py to core/schemas/metadata_schemas.py and add core/schemas/__init__.py that re-exports metadata models and layer_identity helpers. Update imports in manager.py and save.py to import from core.schemas.layer_identity. This separates metadata schemas from layer identity logic and provides a centralized public import surface for schema-related symbols.
Rename the NO_DELETIONS save mode to PARTIAL_UPDATE and update codepaths to use the new semantics so config-derived/placeholder layers do not cause deletions on save. LayerLifecycleManager now returns PARTIAL_UPDATE for config-derived points and related predicates were updated. tag_config_placeholder_metadata now tags layers via set_layer_identity_metadata with PARTIAL_UPDATE, and a helper tag_dlc_annotation_metadata was added. Adjusted a comment in complete_df_for_save to match the new name. Exported PointsDataModel and KeypointPropertiesModel from the schemas package. In the UI save workflow, route saveable DLC points to the plugin writer (avoiding the generic QFileDialog for tracking result layers) and handle empty validated layers with an informational message. Added a unit test (test_save_layers_routes_saveable_points_to_dlc_writer) that monkeypatches dialogs and viewer.layers.save to ensure selected DLC points are saved via the napari-deeplabcut writer. Minor whitespace and refactor cleanups.
Adjust tests to reflect metadata schema path changes and new save semantics. Update mocks to use dlc_schemas.metadata_schemas and include the new allow_deletions flag in the conflict test stub. Rename and expand an e2e test to assert that config-derived (partial) Points layers use PARTIAL_UPDATE semantics and preserve unmentioned keypoints on direct save. Add a new e2e test that verifies a deletion-warning/confirmation is triggered when an existing saved keypoint is removed.
Rework Layer identity utilities: clarify enum docstrings and reorganize sections. Add helpers: get_effective_save_behavior_from_metadata, get_source_config_from_metadata, has_layer_role_metadata, clear_save_behavior_metadata, and predicates that use effective save behavior. Change config-placeholder handling to avoid implicitly setting PARTIAL_UPDATE (store resolved config path and pop save behavior instead) and add promote_config_placeholder_to_annotation_metadata for promoting placeholders. Add tag_tracking_result_metadata which tags tracking results and ensures stale save-behavior is not carried. Overall aims: clearer lifecycle semantics, prevent sticky PARTIAL_UPDATE from config placeholders, and provide utility functions for metadata inspection/manipulation.
Introduce explicit save ownership semantics (PLUGIN_MANAGED / NAPARI_MANAGED) and remove the legacy PARTIAL_UPDATE behavior. Update metadata helpers to compute effective save ownership and add validation helpers for plugin-managed DLC annotation saving. Replace legacy heuristics in LayerLifecycleManager: rename config-derived checks, add methods to detect merge targets and save-context availability, and implement automatic promotion of config-placeholder Points layers to DLC annotation metadata when sufficient save context is present. Wire promotions into sync/setup flows and adjust merge candidate filtering. Clean up exports in schemas.__init__ accordingly.
Refactor save/conflict/io logic to respect plugin-managed DLC annotation layers. Tag DLC annotation metadata on read, validate plugin-managed metadata on write, and always compute deletion conflicts during overwrite preflight. Update UI save workflow to detect plugin-owned DLC layers (by role and save behavior), block multi-layer generic saves when such layers are present, and provide an explanatory dialog. Adjust tests to expect napari-managed/config-placeholder distinctions and the new save behavior routing.
@C-Achard C-Achard self-assigned this Jun 3, 2026
@C-Achard C-Achard added enhancement New feature or request bug fix Fixes an issue or a bug labels Jun 3, 2026
@C-Achard C-Achard added the I/O Related to reading/writing in the plugin: h5, csv, videos, etc label Jun 3, 2026
C-Achard added 13 commits June 3, 2026 19:03
Adds a new test module covering LayerLifecycleManager behavior for Points layers. Tests verify default save behavior for generic points, plugin-managed behavior for DLC annotations, and the promotion flow for config-placeholder layers (both when frames are inserted before/after placeholders). Assertions cover metadata updates (role, save behavior, root/paths) and signal setup calls; validate_header is monkeypatched where needed. File: src/napari_deeplabcut/_tests/core/layer_manager/test_layer_id.py
Update import and usage to call validate_plugin_managed_dlc_annotation_metadata instead of validate_save_behavior_from_metadata in conflicts.py. Aligns code with the renamed/updated validation API for plugin-managed DLC annotation metadata.
Add comprehensive tests for LayerLifecycleManager related to DLC layer identity and save behavior. Introduce helper factories for creating Points layers with DLC headers and tag utilities; test config-placeholder promotion, annotation merging, tracking behavior, and save-policy resolution. Update conflict and writer tests to tag metadata as DLC annotations using tag_dlc_annotation_metadata and add a small helper to mark test metadata as plugin-managed. Also add/import relevant enums and result types used by the new tests.
Wrap the write_hdf call in write_hdf_napari_dlc with a try/except to log detailed diagnostic information (path, layer name, and metadata keys) using logger.exception, then re-raise the exception. This improves error visibility for HDF write failures while preserving the original error behavior.
Add prepare_points_layer_for_plugin_save to reconcile Points layers before routing to the DLC plugin save workflow. The method validates layer type/role, skips tracking results and empty config placeholders, promotes config placeholders by injecting image root/paths into layer.metadata, and defers to validate_header. Use this method across the manager and UI: allow plugin-managed DLC layers (including empty plugin-owned layers that may represent delete-all operations) to be routed to the plugin save flow, and show an informational message when selected config-placeholder layers lack save context. Remove duplicated metadata population/promotion logic and adjust checks to use is_config_placeholder_points_layer; update_save_history is now triggered earlier when root is present.
Import and use tag_dlc_annotation_metadata in tests so metadata is marked as plugin-managed. Updated test_write_routing.py and test_writer_promotion.py to tag metadata (added a small helper wrapper in test_write_routing.py) so writer routing and promotion tests exercise the plugin DLC annotation writer paths and avoid false negatives due to untagged metadata.
Refactor and harden the end-to-end test to ensure DLC plugin save/overwrite logic is used rather than generic napari save. The test was rewritten into explicit phases (promote config-derived placeholder to a plugin-managed annotation, save via plugin, reload, re-add config and run overwrite preflight). Added monkeypatches to fail on unexpected QMessageBox/QFileDialog paths, switched saves to keypoint_controls._save_layers_dialog, added assertions for LayerRole and LayerSaveBehavior, and added an overwrite confirmation check. Minor waits, imports and helper calls were adjusted and related assertions added in another test to validate plugin-managed state.
Update test_manager.py to replace a generic config-metadata assertion with explicit checks: confirm the layer is recognized as a tracking result and assert it is not a config metadata merge target. This clarifies the expected behavior for tracking layers in the manager tests.
Add an explicit check in PointsLayerSaveWorkflow to detect image paths that lie outside the folder being associated with a DLC project. If any unresolved paths are found the code now shows a QMessageBox listing up to five sample paths (and a count of additional ones), instructs the user to move/copy or remove those images, and cancels the save (returns None, True). Also update the maybe_confirm_dataset_path_rewrite call to pass n_unresolved=0 since unresolved paths are handled earlier.
Enhance the test overwrite confirmation helper to normalize numeric fields (n_overwrites/n_deletions/n_frames), capture entries tuple, and include both n_overwrites and n_deletions in the recorded call data. Tighten the "forbid" mode to treat deletions as destructive and fail accordingly. Update the e2e test to assert that the deleted keypoint ("bodypart1") appears in the report entries instead of relying only on a numeric check.
Update tests to reflect default layer save behavior and extend the dummy layer manager used in UI tests. Imported LayerSaveBehavior and changed an assertion to expect DLC_SAVE_BEHAVIOR_KEY == LayerSaveBehavior.NAPARI_MANAGED.value. Removed unused fake_store parameters from two layer-manager tests. Added stub methods to DummyLayerManager (prepare_points_layer_for_plugin_save, is_config_placeholder_points_layer, is_tracking_result_layer) and ensured managed_points_layers returns a tuple to satisfy test expectations.
Introduce helpers to inspect and assert plugin-visible point labels (_get_single_point_label_at_xy and _assert_points_layer_has_label_xy) for napari Points layers. Refactor test_projectless_folder_save_can_associate_with_config_and_coerce_paths_to_d to use these helpers, capture the actual saved label, strengthen waits and assertion messages, and verify that reopening the written H5 recovers the same label and normalized dataset path. Clarify test docstring/goals/non-goals and tighten metadata/preflight checks. Add a new E2E test (test_projectless_folder_save_refuses_unresolved_outside_paths) to ensure a project-association save is refused when any path cannot be rewritten to a canonical DLC row key.
Add handling for config-placeholder points layers: enrich placeholder metadata and abort saves for unsupported direct-video + placeholder cases (log, warn, return SaveOutcome(saved=False)). Improve unresolved-path warnings by converting numeric indexes back to original path strings and robustly stringifying items for display. Update tests to reflect new dialog fields and updated details/summary formatting (details label, confirm button text, n_deletions/n_images, and entry formatting). These changes clarify UX and prevent incorrect saves for unsupported scenarios.
Introduce a DummyDims test helper that records calls to set_current_step via a set_current_step_calls list. Attach a DummyDims instance to DummyViewer (self.dims) so tests can assert interactions with viewer dims without requiring the real napari Dims object.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR centralizes layer identity/save-ownership decisions into explicit metadata roles and uses that to fix and harden the DLC keypoints save workflow (including deletion-aware merges and clearer overwrite/deletion confirmation UX), addressing the “fails to save labels” / routing inconsistencies described in #219.

Changes:

  • Introduces a layer identity schema (LayerRole, LayerSaveBehavior) and wires readers/lifecycle manager/save workflow to use it for consistent routing.
  • Makes saves deletion-aware by completing the save dataframe to the editable scope and merging in a way that preserves intentional NaN deletions; updates conflict reporting/UI to surface deletions distinctly.
  • Adds/updates regression + E2E tests for routing, project association path coercion, and deletion confirmation behavior.

Reviewed changes

Copilot reviewed 26 out of 27 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
src/napari_deeplabcut/ui/ui_dialogs/save.py Routes saves using the lifecycle manager’s identity decisions; blocks generic multi-layer saves for plugin-managed annotation layers; improves project-association failure UX.
src/napari_deeplabcut/ui/dialogs.py Enhances overwrite confirmation dialog to distinguish overwrites vs deletions and improves formatting/safety.
src/napari_deeplabcut/tracking/core/data.py Tags tracking-result layers with explicit role/save behavior and keeps a legacy fallback.
src/napari_deeplabcut/core/schemas/metadata_schemas.py Adds pydantic validation models for writer inputs/attributes.
src/napari_deeplabcut/core/schemas/layer_identity.py New identity system: roles, save behaviors, metadata accessors/mutators, and writer safety validation.
src/napari_deeplabcut/core/schemas/init.py Exposes the new schema utilities/models via the core.schemas package API.
src/napari_deeplabcut/core/layer_lifecycle/manager.py Uses identity metadata to classify layers, promote config placeholders when context exists, and compute save ownership.
src/napari_deeplabcut/core/io.py Tags config/annotation layers on read; enforces writer gating; switches to deletion-aware dataframe completion/merge helpers.
src/napari_deeplabcut/core/dataframes.py Adds deletion-aware save completion (complete_df_for_save), save-scope merge (merge_save_df), and deletion detection/reporting.
src/napari_deeplabcut/core/conflicts.py Runs overwrite + deletion preflight using completed save-scope dataframes and enforces identity gating.
src/napari_deeplabcut/config/models.py Extends conflict report model to include deletions and updated has_conflicts semantics.
src/napari_deeplabcut/_writer.py Adds exception logging context around the writer call.
src/napari_deeplabcut/_reader.py Tags frame layers with identity metadata via tag_frames_metadata.
src/napari_deeplabcut/_tests/ui/test_save.py Updates dummy layer manager surface for new save-routing calls.
src/napari_deeplabcut/_tests/ui/test_dialogs.py Updates dialog tests for new deletion-aware dialog arguments and formatting.
src/napari_deeplabcut/_tests/tracking/test_data.py Tests tracking identity tagging and the new TRACKING_RESULT_TYPE.
src/napari_deeplabcut/_tests/test_writer.py Ensures tests tag metadata as plugin-managed DLC annotations for the writer gate.
src/napari_deeplabcut/_tests/test_widgets.py Adds a widget-level routing regression test that fails if generic save dialog is shown.
src/napari_deeplabcut/_tests/e2e/test_routing_and_provenance.py Extends E2E save/readback coverage; adds refusal test for unresolved outside paths.
src/napari_deeplabcut/_tests/e2e/test_overwrite_and_merge.py Adds end-to-end regression coverage for config/annotation routing and deletion confirmation.
src/napari_deeplabcut/_tests/e2e/conftest.py Extends overwrite-confirm fixture to track deletions and treat them as destructive confirmations.
src/napari_deeplabcut/_tests/core/test_writer_promotion.py Tags promotion test metadata for the new writer eligibility gate.
src/napari_deeplabcut/_tests/core/test_dataframes.py Adds extensive unit coverage for save completion, merge semantics, deletions, and reporting.
src/napari_deeplabcut/_tests/core/test_conflicts.py Updates conflict-preflight tests for deletion plumbing and the new completion step.
src/napari_deeplabcut/_tests/core/layer_manager/test_manager.py Adds identity/promotion/save-behavior tests for the lifecycle manager.
src/napari_deeplabcut/_tests/core/layer_manager/test_layer_id.py Adds dedicated lifecycle manager identity tests (roles/save behavior/promotion).
src/napari_deeplabcut/_tests/core/io/test_write_routing.py Updates write-routing tests to satisfy the new writer eligibility gate and dataframe helper extraction.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/napari_deeplabcut/core/schemas/layer_identity.py Outdated
Comment thread src/napari_deeplabcut/_tests/core/io/test_write_routing.py Outdated
Comment on lines +384 to +388
return [
layer
for layer in layers
if isinstance(layer, Points) and self.layer_manager.prepare_points_layer_for_plugin_save(layer)
]
@C-Achard C-Achard linked an issue Jun 4, 2026 that may be closed by this pull request
2 tasks
C-Achard added 2 commits June 4, 2026 14:09
Update test helper to return the dict produced by tag_dlc_annotation_metadata (fixing its type/signature). Clarify and reword comments in layer_identity about config-placeholder behavior so it isn't described as a sticky save behavior. Remove an outdated/misleading comment block in complete_df_for_save (no functional changes to the save logic). These changes align tests and comments with actual behavior and reduce confusion.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 26 out of 27 changed files in this pull request and generated 1 comment.

Comment thread src/napari_deeplabcut/tracking/core/data.py
Convert LayerLifecycleManager.is_tracking_result_layer to a @staticmethod and delegate several DummyLayerManager predicates to the real LayerLifecycleManager (is_config_placeholder_points_layer, is_empty_points_layer, is_tracking_result_layer, save_behavior_for_points_layer). Update unit tests to import LayerLifecycleManager and tag_config_placeholder_metadata, refine dummy behavior for prepare/validate methods, and add a new test to ensure config-placeholder point layers are not prepared/promoted or shown a QFileDialog during multi-layer save (asserting an informational message is displayed instead). These changes make tests rely on the authoritative production predicates and prevent unintended save flows for config-placeholder layers.

@C-Achard C-Achard Jun 5, 2026

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Note to self:

Caution

This is missing machine labels, which are also a special identity & save case. There is no reason not to include them here.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Moving AnnotationKind would be a good minimal first step, but closer integration might help

@C-Achard C-Achard changed the title Improve layer identity system; fix and improve save workflow Improve layer identity system; improve save workflow Jun 15, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug fix Fixes an issue or a bug enhancement New feature or request I/O Related to reading/writing in the plugin: h5, csv, videos, etc

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Deleting a label then saving fails: the label reappears upon reloading

2 participants