Skip to content

Add Vault & Inventory integration: DB schema, IPC, renderer UI, and tests#61

Open
hyperremix wants to merge 34 commits intomainfrom
codex/add-vaultcategories-and-vaultitems-tables-xn1z27
Open

Add Vault & Inventory integration: DB schema, IPC, renderer UI, and tests#61
hyperremix wants to merge 34 commits intomainfrom
codex/add-vaultcategories-and-vaultitems-tables-xn1z27

Conversation

@hyperremix
Copy link
Owner

Motivation

  • Provide an in-app Item Vault and unified inventory search so users can tag/vault items without touching game saves and see vault state alongside inventory snapshots.
  • Persist vaulted items, categories and many-to-many mappings in the app DB and reconcile vault state from parsed save-file snapshots.
  • Cover new behavior with backend and frontend tests to prevent regressions and validate reconciliation/fingerprint logic.

Description

  • Database: added Drizzle schemas and SQL schema additions for vault_items, vault_categories, and vault_item_categories, plus converters and Drizzle re-exports to support vault models and indexes/triggers in electron/database/*.
  • DB modules: implemented vault DB modules (electron/database/vault-items.ts, vault-categories.ts) and surfaced methods on GrailDatabase (add/update/remove/upsert/search/reconcile/mark-missing/set-present, category CRUD & mapping).
  • Save-file reconciliation: extended SaveFileMonitor to produce typed inventory snapshots, deterministic fingerprint creation, and a reconciliation pipeline that upserts fingerprints and calls per-scan reconciliation to mark present/missing items.
  • IPC & preload: added typed, defensive IPC handlers in electron/ipc-handlers/vaultHandlers.ts, exposed vault and inventory APIs on the preload surface (electron/preload.ts), and wired initializeVaultHandlers into electron/main.ts with access to the save-file monitor.
  • Renderer UI: added inventory browser and vault UIs plus integrations: src/components/inventory/CharacterInventoryBrowser.tsx, src/components/vault/ItemVault.tsx, updated ItemDetailsDialog to show vault status/tags and Vault/Unvault actions, updated TitleBar and router to include Inventory/Vault routes, and updated translations usage.
  • Tests & translations: added comprehensive tests for DB behavior and handlers (electron/database/vault-items.test.ts, electron/ipc-handlers/vaultHandlers.test.ts), save-file monitor reconciliation tests, and frontend component tests for CharacterInventoryBrowser, ItemVault, and updated ItemDetailsDialog.test.tsx; added translation keys in src/i18n/locales/en/common.json and consumed them via src/i18n/translations.ts.

Testing

  • Ran type checking with bun run typecheck and it completed successfully.
  • Ran formatting (bun run format), lint (bun run lint) and combined checks (bun run check) and they all passed.
  • Ran the full unit test suite bun run test:run (Vitest); all added and existing tests passed: Test Files 37 passed, Tests 651 passed.
  • Notes: attempted browser screenshot via Playwright against local Vite server failed (net::ERR_EMPTY_RESPONSE) and Electron could not run in this environment due to a missing native library (libatk-1.0.so.0), so visual/electron runtime verification was not possible here.

Codex Task

@greptile-apps
Copy link

greptile-apps bot commented Feb 11, 2026

Greptile Overview

Greptile Summary

This PR adds a first-class Vault + unified Inventory search feature across the stack:

  • DB layer: new vault_items, vault_categories, and vault_item_categories tables (SQL + Drizzle schemas), plus DB modules for vault CRUD/search and reconciliation.
  • Backend services: SaveFileMonitor now produces typed inventory snapshots with deterministic fingerprints and reconciles vault presence state based on each scan.
  • IPC/preload: new typed IPC handlers for vault & inventory endpoints exposed to the renderer.
  • Renderer: new Inventory and Vault views, updated item details to show vault status/tags, route + titlebar integrations, and translation key usage.
  • Tests: adds backend + IPC + renderer tests to cover vault behavior, snapshot/fingerprint logic, and UI integrations.

Key integration point: SaveFileMonitor populates inventory snapshots and calls GrailDatabase.upsertVaultItemByFingerprint + reconcileVaultItemsForScan so the vault DB stays in sync with the latest parsed save-file state, while renderer queries via IPC for both vault search and inventory snapshot browsing.

Confidence Score: 4/5

  • This PR looks safe to merge once the inventory characterId filtering bug is fixed.
  • Review found one deterministic functional issue in the new IPC inventory filtering path (characterId never populated, so the filter can’t match). The remaining changes are additive (new tables, handlers, UI, and tests) and appear coherent with existing patterns; schema and reconciliation logic look internally consistent based on spot-checking the key modules.
  • electron/ipc-handlers/vaultHandlers.ts and electron/services/saveFileMonitor.ts (ensure ParsedInventoryItem.characterId is populated or stop filtering on it).

Important Files Changed

Filename Overview
electron/database/schema.ts Extends SQL schema with vault tables/indexes/triggers; integrates into createSchema.
electron/database/vault-items.ts Implements vault item CRUD/search/reconcile logic; main risk area but no definitive runtime bug found in review.
electron/ipc-handlers/vaultHandlers.ts Adds vault and inventory IPC handlers with input sanitization; includes a deterministic bug where inventory characterId filtering can never match because snapshots don’t set item.characterId.
electron/services/saveFileMonitor.ts Adds inventory snapshot generation, fingerprinting, and vault reconciliation; overall coherent but ensure downstream consumers don’t expect characterId on items (currently never set).
src/components/inventory/CharacterInventoryBrowser.tsx Implements inventory browser UI consuming preload inventory APIs; no deterministic issues found in review.
src/components/vault/ItemVault.tsx Implements vault UI consuming vault IPC APIs; no deterministic issues found in review.

Sequence Diagram

sequenceDiagram
  participant R as Renderer
  participant P as Preload
  participant I as IPC(Main)
  participant S as SaveFileMonitor
  participant D as GrailDatabase
  participant V as Vault DB

  Note over S,D: On save scan (parseFiles)
  S->>S: parseSaveDirectory/parseAllSaveDirectories
  S->>S: createParsedInventoryItem + createFingerprint
  S->>D: upsertVaultItemByFingerprint(fingerprint, fields)
  D->>V: INSERT .. ON CONFLICT(fingerprint) DO UPDATE
  S->>D: reconcileVaultItemsForScan(presentFingerprints)
  D->>V: UPDATE is_present_in_latest_scan/last_seen_at

  Note over R,V: User searches Inventory + Vault
  R->>P: window.electron.inventory.searchAll(filter)
  P->>I: ipcRenderer.invoke('inventory:searchAll', filter)
  I->>S: getInventorySearchResult()
  I->>D: searchVaultItems(filter)
  D->>V: SELECT/COUNT vault_items (+ category EXISTS)
  I-->>P: {inventory, vault}
  P-->>R: {inventory, vault}
Loading

Copy link

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

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

48 files reviewed, 1 comment

Edit Code Review Agent Settings | Greptile

@hyperremix
Copy link
Owner Author

@greptile

@greptile-apps
Copy link

greptile-apps bot commented Feb 11, 2026

Greptile Overview

Greptile Summary

This PR implements a comprehensive vault and inventory system allowing users to track items from save files without modifying game saves. The implementation adds database schemas for vault storage, save-file reconciliation logic with deterministic fingerprints, IPC handlers with input validation, and polished UI components with drag-and-drop support.

Key Changes:

  • Database: Added 3 new tables (vault_items, vault_categories, vault_item_categories) with comprehensive indexes, triggers, and foreign key constraints
  • Save-file monitoring: Extended SaveFileMonitor to create typed inventory snapshots with deterministic fingerprinting and automatic reconciliation that marks items as present/missing
  • Backend: Implemented vault DB modules with CRUD operations, fingerprint-based upserts to prevent duplicates, and reconciliation pipeline
  • IPC layer: Added defensive handlers with input validation, sanitization, and combined inventory+vault search
  • UI: Created CharacterInventoryBrowser and ItemVault components with filtering, category management, drag-and-drop, and optimistic updates
  • Tests: Comprehensive coverage across backend (DB operations, IPC handlers, reconciliation) and frontend (component behavior, filtering, interactions)

Previous Issue Addressed:
The previous review identified that characterId was never set on ParsedInventoryItem, breaking character-based filtering. This has been fixed in SaveFileMonitor.processSingleFile() (lines 790-794), which now properly retrieves and assigns characterId from the database.

Testing:
All 651 tests pass including 37 test files. Type checking, formatting, and linting all pass.

Confidence Score: 5/5

  • This PR is safe to merge with high confidence
  • The implementation is well-architected with comprehensive test coverage (651 passing tests), proper error handling, input validation, and defensive programming. The previous character filtering issue has been resolved. All type checking, linting, and formatting checks pass.
  • No files require special attention

Important Files Changed

Filename Overview
electron/database/schema.ts Added 3 new vault tables (vault_categories, vault_items, vault_item_categories) with comprehensive indexes and triggers for timestamp management
electron/database/vault-items.ts Implemented comprehensive vault item CRUD operations with fingerprint-based upsert, reconciliation logic, and search with pagination
electron/services/saveFileMonitor.ts Extended to produce typed inventory snapshots with deterministic fingerprints, reconciliation pipeline, and characterId propagation (fixes previous issue)
electron/ipc-handlers/vaultHandlers.ts Implemented defensive IPC handlers with input validation, sanitization, and combined inventory+vault search functionality
electron/preload.ts Exposed vault and inventory APIs on preload surface with proper type safety
src/components/inventory/CharacterInventoryBrowser.tsx New inventory browser with filtering, drag-and-drop vault integration, optimistic UI updates, and character-based navigation
src/components/vault/ItemVault.tsx New vault management UI with search, filtering, category management, and drag-and-drop support
electron/database/vault-items.test.ts Comprehensive tests for vault item operations including upsert deduplication, reconciliation, and category filtering
electron/ipc-handlers/vaultHandlers.test.ts Tests for IPC handler validation, error handling, and combined search functionality
electron/types/grail.ts Added comprehensive type definitions for vault items, categories, inventory snapshots, and search filters

Sequence Diagram

sequenceDiagram
    participant UI as Renderer UI
    participant IPC as IPC Handlers
    participant DB as GrailDatabase
    participant Monitor as SaveFileMonitor
    participant FS as File System

    Note over Monitor,FS: Save File Scan & Reconciliation
    FS->>Monitor: File change detected
    Monitor->>Monitor: parseSave() - Parse D2S/SSS files
    Monitor->>Monitor: createParsedInventoryItem() - Create fingerprints
    Monitor->>Monitor: Create CharacterInventorySnapshot
    Monitor->>DB: upsertVaultItemByFingerprint() - Upsert each item
    Monitor->>DB: reconcileVaultItemsForScan() - Mark present/missing
    
    Note over UI,DB: User Searches Inventory
    UI->>IPC: inventory:searchAll(filter)
    IPC->>Monitor: getInventorySearchResult()
    Monitor-->>IPC: Return snapshots with ParsedInventoryItems
    IPC->>DB: searchVaultItems(filter)
    DB-->>IPC: Return VaultItemSearchResult
    IPC-->>UI: Combined inventory + vault results
    
    Note over UI,DB: User Vaults an Item
    UI->>IPC: vault:addItem(VaultItemUpsertInput)
    IPC->>IPC: validateVaultItemInput()
    IPC->>DB: addVaultItem()
    DB->>DB: Insert into vault_items table
    DB->>DB: setVaultItemCategories() if categoryIds provided
    DB-->>IPC: Return VaultItem
    IPC-->>UI: Return VaultItem
    
    Note over UI,DB: User Manages Categories
    UI->>IPC: vault:createCategory(input)
    IPC->>DB: addVaultCategory()
    DB-->>IPC: Return VaultCategory
    UI->>IPC: vault:updateItemTags(itemId, categoryIds)
    IPC->>DB: setVaultItemCategories()
    DB->>DB: Update vault_item_categories mapping
    DB-->>IPC: Success response
    
    Note over UI,DB: User Searches Vault
    UI->>IPC: vault:search(filter)
    IPC->>IPC: sanitizeFilter()
    IPC->>DB: searchVaultItems()
    DB->>DB: SQL query with filters & pagination
    DB->>DB: attachCategoryIds()
    DB-->>IPC: VaultItemSearchResult
    IPC-->>UI: Return paginated results
Loading

@hyperremix hyperremix force-pushed the codex/add-vaultcategories-and-vaultitems-tables-xn1z27 branch from d3c76b3 to 0f58ce2 Compare February 11, 2026 17:40
@codecov-commenter
Copy link

codecov-commenter commented Feb 11, 2026

@hyperremix
Copy link
Owner Author

@greptile

@greptile-apps
Copy link

greptile-apps bot commented Feb 11, 2026

Greptile Overview

Greptile Summary

This PR adds a comprehensive vault and inventory integration feature to the D2R Arcane Tracker. The implementation includes database schema additions for vault items/categories with proper indexing, a reconciliation pipeline that automatically tracks item presence across save file scans using deterministic fingerprints, defensive IPC handlers with input validation, and full-featured UI components for browsing inventory and managing vaulted items.

Key Changes:

  • Database layer: Added vault_items, vault_categories, and vault_item_categories tables with Drizzle schemas, comprehensive indexes, and CRUD operations
  • Save file monitoring: Extended to create typed ParsedInventoryItem snapshots with deterministic fingerprints and integrated vault reconciliation on each scan
  • IPC layer: Added 11 new handlers (vault:* and inventory:*) with defensive input validation and sanitization
  • UI components: New CharacterInventoryBrowser and ItemVault components with filtering, search, drag-and-drop, and category management
  • Testing: Added 651 passing tests including DB operations, IPC handlers, save file reconciliation, and component tests

Architecture:
The reconciliation pipeline works by creating deterministic fingerprints from item attributes (file type, character, location, quality, ethereal, sockets, name), upserting items by fingerprint on each scan, and marking items as present/missing based on scan results. This allows tracking items across game sessions without modifying save files.

Test Coverage:
All test suites pass (37 files, 651 tests). Coverage includes unit tests for DB operations (upsert deduplication, reconciliation logic), IPC handler validation, save file monitor snapshot creation, and component integration tests for inventory/vault UIs.

Confidence Score: 5/5

  • This PR is safe to merge with no critical issues found
  • Score reflects comprehensive implementation with excellent test coverage (651 passing tests), defensive input validation in IPC handlers, proper database schema design with indexes and foreign keys, clean separation of concerns across layers, and successful type checking/linting/formatting validation as documented in the PR description
  • No files require special attention

Important Files Changed

Filename Overview
electron/database/drizzle/schema/vaultItems.ts Added vault_items schema with fingerprint-based uniqueness, comprehensive indexing for queries, and foreign key relationships to characters and items
electron/database/vault-items.ts Implemented vault item DB operations: upsert by fingerprint, reconciliation logic for scan presence tracking, search with pagination and filtering
electron/services/saveFileMonitor.ts Extended save file parsing to create typed inventory snapshots with deterministic fingerprints, integrated vault reconciliation pipeline
electron/ipc-handlers/vaultHandlers.ts Added defensive IPC handlers for vault/inventory operations with input validation, sanitization, and typed error handling
src/components/inventory/CharacterInventoryBrowser.tsx New inventory browser component with filtering, vault status tracking, drag-and-drop to vault functionality
src/components/vault/ItemVault.tsx New vault UI with category management, tag assignments, search/filter, and drag-and-drop to unvault items
electron/database/vault-items.test.ts Comprehensive tests for vault DB operations: upsert deduplication, reconciliation, search filtering, category mappings
electron/types/grail.ts Added comprehensive TypeScript types for vault items, categories, inventory snapshots, filters, and reconciliation inputs

Sequence Diagram

sequenceDiagram
    participant User as Renderer UI
    participant IPC as IPC Handlers
    participant Monitor as SaveFileMonitor
    participant DB as GrailDatabase
    participant FS as File System

    User->>IPC: inventory:searchAll(filter)
    IPC->>Monitor: getInventorySearchResult()
    Monitor-->>IPC: CharacterInventorySnapshot[]
    IPC->>DB: searchVaultItems(filter)
    DB-->>IPC: VaultItemSearchResult
    IPC-->>User: {inventory, vault}

    FS->>Monitor: Save file modified
    Monitor->>Monitor: parseSave() → ParsedInventoryItem[]
    Monitor->>Monitor: createFingerprint(item)
    Monitor->>DB: upsertVaultItemByFingerprint()
    DB->>DB: INSERT ON CONFLICT UPDATE
    Monitor->>DB: reconcileVaultItemsForScan()
    DB->>DB: Mark present/missing items

    User->>IPC: vault:addItem(item)
    IPC->>DB: addVaultItem(item)
    DB-->>IPC: VaultItem
    IPC-->>User: VaultItem

    User->>IPC: vault:updateItemTags(id, categoryIds)
    IPC->>DB: setVaultItemCategories()
    DB->>DB: DELETE + INSERT mappings
    DB-->>IPC: success
    IPC-->>User: {success: true}
Loading

hyperremix and others added 21 commits February 28, 2026 11:04
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ule rebuilds

Force-parse save files when inventory snapshots are empty on startup,
use characterName as fallback key in character dropdown and filter,
and auto-rebuild better-sqlite3 for the correct runtime in dev/test scripts.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Extend vault items with grid position, dimensions, equipped slot, and
icon metadata columns. Add spatial layout classification for inventory,
stash, and equipped item rendering with overflow board support. Introduce
sprite icon resolution and board primitive components for the inventory
browser.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@hyperremix hyperremix force-pushed the codex/add-vaultcategories-and-vaultitems-tables-xn1z27 branch from d4935f4 to 75efea0 Compare February 28, 2026 10:07
@hyperremix
Copy link
Owner Author

@codex

Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

// Handle shared stash files (.d2i)
if (extension === '.d2i') {
// Parse the stash file header to extract hardcore status
let isHardcore = false;
try {

P1 Badge Handle .sss/.d2x in stash-specific parse path

This parser only treats .d2i as a shared-stash file and falls back to D2S header offsets for all other extensions, but the monitor also ingests .sss and .d2x; for those files this produces invalid character metadata (often unknown class) that is later written through character upserts and can violate the characters.character_class check, causing repeated batch-write failures whenever legacy stash files are present. The stash branch should include .sss/.d2x and return shared-stash metadata instead of D2S character metadata.

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +387 to +391
await removeItemFromSaveFile(
normalizedItem.sourceFilePath,
normalizedItem.sourceFileType,
parsedItem.id,
);

Choose a reason for hiding this comment

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

P1 Badge Persist vault record before removing source save item

The vault:addItem flow deletes the item from the save file before inserting/upserting it into vault_items, so any failure in grailDatabase.addVaultItem(...) (for example a transient SQLite write failure or a constraint error) leaves the item removed from the save and absent from the vault. That is a user-data-loss path during a normal vault action and should be avoided by reordering the operations (or adding rollback logic).

Useful? React with 👍 / 👎.

hyperremix and others added 10 commits March 11, 2026 09:01
- Add modernStashParser.ts: parses D2R v105 .d2i stash files, classifying
  items into shared/gems/materials/runes tabs with correct stack counts
- Extend constants105 magical_properties through index 381 so the d2s
  bit-reader no longer throws "Invalid Stat Id: 381" for stacked resource
  items (gems, runes, materials with stack count > 1)
- Add attribute-381 fallback in resolveStackCount for runes and other items
  whose stack count is encoded exclusively via magic attribute 381
- Add stashFormat.ts: low-level .d2i sector/metadata reader
- Integrate parseModernStash into saveFileMonitor for .d2i v105+ files
- Expose stashTabKind and stackCount through IPC / CharacterInventoryBrowser
- Fix vite.config.ts: pass d2sSourceAliases explicitly to the electron main
  vite config; vite-plugin-electron does not inherit root resolve.alias, so
  the @dschu012/d2s/lib/ → src/ redirect was absent from the main-process
  build, causing a CommonJS resolver failure at startup
- Add d2s-compat.d.ts ambient declarations for untyped d2s internals
- Add fixtures, unit tests for stashFormat, modernStashParser, updated
  saveFileMonitor/saveFileEditor/CharacterInventoryBrowser tests
- Bump vitest config with d2s source aliases matching vite.config.ts

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Resource-stash items (runes, gems, materials) were always showing
stackCount = 1 because the v105 conditional field in d2s/items.ts
was skipping the 8-bit quantity value with SkipBits(8) instead of
storing it. Diagnostic confirmed simple_item = 1 for runes/gems and
simple_item = 0 with empty magic_attrs for materials — both rely on
this field.

- Point @dschu012/d2s at file:../d2s (local clone with the fix applied)
- Delete lib/ from clone so Vite aliases to TS source, avoiding
  Rollup __exportStar static-analysis error on symlinked CJS modules
- Raise resolveStackCount upper bound from 99 to 511 (full 9-bit range)
- Add type declarations for IMagicAttribute and _readMagicProperties
- Fix tests: remove stackCount === 1 assertions that were testing the bug,
  update bound to <= 511, add simple-item quantity path coverage

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ls tab sections

- Fix socketed rune items showing raw item codes (r18, r26) instead of human-readable
  names (Ko Rune, Vex Rune) by looking up runeDisplayNameByCode[raw.type] first in
  getSocketedItemName(), since d2s encodes the item code in `type`, not `name`
- Fix Lo Rune (and all runes) failing to show their icon in socket tooltip entries by
  adding runeImageFilenameByCode[raw.type] as the first icon candidate, resolving in
  one IPC cache hit instead of ~11 sequential misses before reaching {name}_rune.png
- Add RunesTabSection, GemsTabSection, and MaterialsTabSection components with
  dedicated grouped layouts for rune/gem/material stash tabs
- Wire new tab sections into CharacterInventoryBrowser based on fallbackTabKind
- Add pointer-events-none to tooltip content to prevent hover capture issues

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…patch

Replace file:../d2s with viniciusrezende/d2s#feat/support-reign-of-warlock and
apply the one-line stack-count fix (SkipBits → ReadUInt8) as a managed bun patch
in patches/@dschu012+d2s.patch.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The waitFor sentinel was screen.getByText('Selected Item'), which is an
unconditional card title always present before data loads. On slower CI
machines (especially with coverage overhead) the async searchAll mock had
not yet resolved when getByTestId ran immediately after waitFor.

Replace the two-step pattern with a single
  await waitFor(() => screen.getByTestId('mercenary-board-merc-unknown'))
so the assertion only proceeds once the board is actually in the DOM.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@hyperremix
Copy link
Owner Author

@codex

Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: fe8ffe1897

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +419 to +423
if (parsedItem.id !== undefined) {
await removeItemFromSaveFile(
normalizedItem.sourceFilePath,
normalizedItem.sourceFileType,
parsedItem.id,

Choose a reason for hiding this comment

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

P1 Badge Handle id-less modern stash items in vault:addItem

When vault:addItem is used on a rune/gem/material from a v105 .d2i, rawItemJson often has no numeric id (the move-input validation in this file already documents that simple .d2i items are located by position instead). In that case this branch skips removeItemFromSaveFile but still inserts the vault row, so the item stays in the stash and also appears in the vault. That duplicates stackables for modern-stash users unless this falls back to the position-based removal path.

Useful? React with 👍 / 👎.

Comment on lines +424 to +428
);
}
}

return grailDatabase.addVaultItem(normalizedItem);

Choose a reason for hiding this comment

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

P1 Badge Persist the vault record before deleting the source item

This removes the item from the save file before calling grailDatabase.addVaultItem. If the DB write fails afterwards (for example, SQLite I/O/locking errors), the handler exits with the item already deleted from the character or stash and no vault record created. Because the file mutation is irreversible here, vaulting becomes destructive under ordinary persistence failures.

Useful? React with 👍 / 👎.

Comment on lines +1039 to +1043
const readOnly = false;

if (extension === '.d2i') {
try {
const metadata = readD2iMetadata(buffer);

Choose a reason for hiding this comment

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

P2 Badge Mark modern v105 stash snapshots as read-only

This hard-codes every parsed snapshot as writable, but the renderer only disables dragging when snapshot.readOnly is true (src/components/inventory/CharacterInventoryBrowser.tsx:3617). For modern .d2i resource tabs, the move backend only searches shared-page sectors (electron/services/saveFileEditor.ts:2018-2024), so dragging a rune/gem/material from tabs 5-7 exposes a path that cannot find the source item and fails at runtime. Setting readOnly for modern stash snapshots, or at least for their resource tabs, would prevent that broken interaction.

Useful? React with 👍 / 👎.

hyperremix and others added 2 commits March 19, 2026 20:39
…verwrite

- Add full targetOptions validation to vault:unvaultItem; guard against
  silent data loss when targetOptions is provided but vault item is missing
- Wrap rawItemJson parse in try/catch (vault:unvaultItem)
- Validate categoryIds elements in vault:updateItemTags
- Add quality assertion to validateVaultItemInput
- Add id assertion to validateCategoryInput (create path)
- Add categoryIds, characterId, and page upper-bound checks in sanitizeFilter
- Wrap tickReader parse block in try/finally so readingFiles is always reset
- Only update sourceCharacterName in setVaultItemsPresentInLatestScan when defined

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants