Context
Audited against current main:
9478ed314a9cd7528ed38025709c2873a1f65970
This is the merge of CMS-024M / PR #33.
CMS-024M already provides the persistence API:
pub enum PgnPositionSnapshotDeleteResult {
Deleted,
NotFound,
}
pub fn delete_pgn_position_snapshot(
path: &Path,
chapter_id: i32,
snapshot: &PgnPositionSnapshot,
) -> Result<PgnPositionSnapshotDeleteResult, String>
Its persisted identity remains:
chapter_id
+ initial_fen
+ main_line_uci
+ ply_index
The current ProjectTab already owns a contextual saved-PGN cache keyed by project path + active chapter, and pgn_position_snapshot_at(index) reads only from that cache.
main.rs was also audited: it special-cases only ProjectMessage::OpenPgnPosition(index); all other project messages already flow through ProjectTab::update(...). Therefore this task does not require a production change in src/main.rs.
Objective
Allow a user to delete one saved PGN position from the active chapter in ProjectTab, with an inline confirmation flow that is safe across project/chapter context changes.
Conceptual flow:
Saved PGN position
→ Delete
→ inline confirmation
→ Confirm delete / Cancel
→ delete_pgn_position_snapshot(...)
→ refresh contextual PGN cache
→ row disappears
No modal/dialog framework.
Safety rule: never confirm by visual index
The initial Delete click may originate from the current rendered index, but the destructive confirmation must not depend on that index still pointing to the same item.
Store an owned pending-delete value conceptually equivalent to:
pending delete =
PgnPositionsContext { path, chapter_id }
+ cloned PgnPositionSnapshot
Only one pending delete is needed at a time.
At confirm time, compare the pending context with the current active PGN context.
If they do not match:
- clear the pending delete;
- perform no persistence write;
- perform no deletion.
The persistence call must receive the cloned snapshot, never a UI index.
ProjectMessage additions
Add small ProjectTab-local messages conceptually equivalent to:
RequestDeletePgnPosition(usize)
ConfirmDeletePgnPosition
CancelDeletePgnPosition
Exact names may follow existing style, but keep the three-step semantics explicit.
Request Delete behavior
On the first Delete click:
- resolve the snapshot only through the current contextual
pgn_positions() cache;
- clone the snapshot and current
PgnPositionsContext into pending state;
- do not write SQLite;
- do not refresh the cache;
- do not remove the row locally;
- show the inline confirmation only for that pending snapshot.
If the cache is missing, stale, failed, or the index is out of range:
- fail closed;
- do not create pending state;
- do not write SQLite.
Cancel behavior
Cancel must:
- clear pending delete;
- perform no persistence write;
- perform no cache refresh;
- leave the row/cache unchanged.
Confirm behavior
Context mismatch
If current active PGN context != pending context:
- clear pending;
- do not call the delete API;
- do not delete anything.
Deleted
When the persistence API returns Deleted:
- clear pending;
- refresh
pgn_positions_cache using the existing explicit refresh boundary;
- show success feedback;
- the deleted row must disappear from the refreshed list.
NotFound
This may happen when the database changed externally while the UI cache was stale.
When the API returns NotFound:
- clear pending;
- refresh
pgn_positions_cache;
- show neutral "not found" feedback;
- recover to the current SQLite truth rather than pretending a local success.
Persistence error
When the delete API returns Err:
- clear pending;
- do not fabricate success;
- do not remove the row from the loaded cache;
- do not perform a speculative local cache mutation;
- show failure feedback including the underlying error text;
- fail closed.
A later normal contextual refresh may replace the cache if appropriate.
Lifecycle invalidation
Any successful operation that changes the active PGN project/chapter context must cancel pending delete.
At minimum:
- successful project creation;
- successful project open/switch;
- Close project;
- successful chapter creation when it activates the new chapter;
- successful chapter selection.
This prevents a confirmation created in one context from being applied to another.
UI
Keep confirmation inline inside ProjectTab.
Normal row:
Pending row:
Delete this saved PGN position?
[Confirm delete]
[Cancel]
Open must continue to behave exactly as it does now.
Do not add a generic modal/dialog system.
Translation keys
Add only task-specific keys to all six current translation bundles.
Suggested keys:
delete_saved_pgn_position
confirm_delete_saved_pgn_position
confirm_delete_saved_pgn_position_button
cancel_delete_saved_pgn_position
saved_pgn_position_deleted
saved_pgn_position_not_found
saved_pgn_position_delete_failed
Suggested English meanings:
Delete
Delete this saved PGN position?
Confirm delete
Cancel
Saved PGN position deleted
Saved PGN position not found
Could not delete saved PGN position
For the failure status, append the persistence error using the existing "<translated prefix>: {error}" style.
Extend the existing translation-key coverage test so every new key is non-empty for every supported language.
Scope
Expected modified files only:
src/project_tab.rs
translations/en-US/ocp.ftl
translations/es/ocp.ftl
translations/pt-BR/ocp.ftl
translations/fr/ocp.ftl
translations/cn/ocp.ftl
translations/nl/ocp.ftl
Do not modify
Do not modify:
src/project.rs
src/main.rs
src/pgn_tab.rs
src/pgn_review.rs
src/pgn_import.rs
src/schema.rs
project_migrations/
README.md
Cargo.toml
Cargo.lock
If implementation reveals a genuine need to touch one of these, stop and report it rather than broadening scope silently.
Schema and persistence
No schema or migration change.
Do not change CMS-024M's delete API or identity semantics.
PROJECT_SCHEMA_VERSION must remain 4.
No SQL should be added to ProjectTab; call delete_pgn_position_snapshot(...).
Tests
Add focused tests in the existing src/project_tab.rs test module.
At minimum cover:
Request creates pending without writing
- load a chapter with a saved PGN position;
- request Delete by current visual index;
- pending stores cloned snapshot + exact project/chapter context;
- SQLite row remains;
- loaded cache remains unchanged.
Cancel
- create pending;
- Cancel;
- pending becomes
None;
- SQLite row remains;
- cache remains unchanged.
Confirm Deleted
- save at least two positions;
- request one;
- confirm;
- only the requested persisted identity is deleted;
- pending is cleared;
- cache is refreshed;
- deleted row disappears;
- the other row remains.
Confirm NotFound with stale cache
- load the snapshot into
ProjectTab cache;
- remove that persisted snapshot externally through the CMS-024M API so the cache becomes stale;
- request Delete from the stale cache;
- confirm;
- receive
NotFound;
- refresh;
- cache reflects SQLite truth;
- pending is cleared.
Persistence error fails closed
Create a deterministic persistence failure after pending state exists.
Verify:
- no local success is fabricated;
- pending is cleared;
- loaded cache is not locally pruned;
- failure status contains the translated prefix and underlying error.
Invalid/stale request paths
Cover:
- out-of-range index;
- missing cache;
- mismatched/stale cache context;
- failed cache state.
None may create pending or write SQLite.
Context invalidation
Verify pending is cancelled across the relevant lifecycle boundaries:
- chapter selection;
- chapter creation/activation;
- project switch/open;
- project creation;
- Close project.
Render boundary
Preserve the existing invariant:
- render/content reads the contextual cache only;
- no SQLite read/write occurs merely because
pgn_positions_content() renders.
Existing Open behavior
Existing saved-position Open behavior and cached accessor semantics must remain green.
Acceptance criteria
Verification
Run:
git status
cargo fmt
cargo fmt --check
cargo clippy --all-targets --all-features -- -D warnings
cargo test
cargo check
cargo build
git diff --check
git diff --name-only
git diff -- src/project_tab.rs
git status
Before finishing, confirm the diff is limited to the seven scoped files.
Out of scope
Do not implement:
- bulk delete;
- delete-all;
- reorder;
- edit/update saved PGN positions;
- deleting from
PgnTab;
- generic modal/dialog infrastructure;
- schema changes;
- migration changes;
- persistence identity changes;
- generic editorial-material abstractions;
- unrelated refactors.
Context
Audited against current
main:9478ed314a9cd7528ed38025709c2873a1f65970This is the merge of CMS-024M / PR #33.
CMS-024M already provides the persistence API:
Its persisted identity remains:
The current
ProjectTabalready owns a contextual saved-PGN cache keyed by project path + active chapter, andpgn_position_snapshot_at(index)reads only from that cache.main.rswas also audited: it special-cases onlyProjectMessage::OpenPgnPosition(index); all other project messages already flow throughProjectTab::update(...). Therefore this task does not require a production change insrc/main.rs.Objective
Allow a user to delete one saved PGN position from the active chapter in
ProjectTab, with an inline confirmation flow that is safe across project/chapter context changes.Conceptual flow:
No modal/dialog framework.
Safety rule: never confirm by visual index
The initial Delete click may originate from the current rendered index, but the destructive confirmation must not depend on that index still pointing to the same item.
Store an owned pending-delete value conceptually equivalent to:
pending delete = PgnPositionsContext { path, chapter_id } + cloned PgnPositionSnapshotOnly one pending delete is needed at a time.
At confirm time, compare the pending context with the current active PGN context.
If they do not match:
The persistence call must receive the cloned snapshot, never a UI index.
ProjectMessage additions
Add small ProjectTab-local messages conceptually equivalent to:
Exact names may follow existing style, but keep the three-step semantics explicit.
Request Delete behavior
On the first Delete click:
pgn_positions()cache;PgnPositionsContextinto pending state;If the cache is missing, stale, failed, or the index is out of range:
Cancel behavior
Cancel must:
Confirm behavior
Context mismatch
If current active PGN context != pending context:
DeletedWhen the persistence API returns
Deleted:pgn_positions_cacheusing the existing explicit refresh boundary;NotFoundThis may happen when the database changed externally while the UI cache was stale.
When the API returns
NotFound:pgn_positions_cache;Persistence error
When the delete API returns
Err:A later normal contextual refresh may replace the cache if appropriate.
Lifecycle invalidation
Any successful operation that changes the active PGN project/chapter context must cancel pending delete.
At minimum:
This prevents a confirmation created in one context from being applied to another.
UI
Keep confirmation inline inside
ProjectTab.Normal row:
Pending row:
Openmust continue to behave exactly as it does now.Do not add a generic modal/dialog system.
Translation keys
Add only task-specific keys to all six current translation bundles.
Suggested keys:
Suggested English meanings:
For the failure status, append the persistence error using the existing
"<translated prefix>: {error}"style.Extend the existing translation-key coverage test so every new key is non-empty for every supported language.
Scope
Expected modified files only:
Do not modify
Do not modify:
If implementation reveals a genuine need to touch one of these, stop and report it rather than broadening scope silently.
Schema and persistence
No schema or migration change.
Do not change CMS-024M's delete API or identity semantics.
PROJECT_SCHEMA_VERSIONmust remain4.No SQL should be added to
ProjectTab; calldelete_pgn_position_snapshot(...).Tests
Add focused tests in the existing
src/project_tab.rstest module.At minimum cover:
Request creates pending without writing
Cancel
None;Confirm
DeletedConfirm
NotFoundwith stale cacheProjectTabcache;NotFound;Persistence error fails closed
Create a deterministic persistence failure after pending state exists.
Verify:
Invalid/stale request paths
Cover:
None may create pending or write SQLite.
Context invalidation
Verify pending is cancelled across the relevant lifecycle boundaries:
Render boundary
Preserve the existing invariant:
pgn_positions_content()renders.Existing Open behavior
Existing saved-position Open behavior and cached accessor semantics must remain green.
Acceptance criteria
ProjectTab.Deletedrefreshes the contextual cache and removes only the target row.NotFoundrefreshes stale cache to SQLite truth.ProjectTab.PROJECT_SCHEMA_VERSION == 4.Verification
Run:
Before finishing, confirm the diff is limited to the seven scoped files.
Out of scope
Do not implement:
PgnTab;