tftsr-devops_investigation/TICKET-attachment-db-storage-recall.md
Shaun Arman 1b36ebfb3d
All checks were successful
Test / rust-fmt-check (pull_request) Successful in 1m35s
Test / frontend-tests (pull_request) Successful in 1m41s
Test / frontend-typecheck (pull_request) Successful in 1m43s
Test / rust-clippy (pull_request) Successful in 3m10s
Test / rust-tests (pull_request) Successful in 4m39s
PR Review Automation / review (pull_request) Successful in 4m58s
feat: attachment DB storage and cross-incident recall
Store compressed log content and raw image bytes in SQLite so attachments
are self-contained regardless of source file availability on disk.

DB (migrations 020-022):
- log_files.content_compressed BLOB — gzip-compressed extracted text
- image_attachments.image_data BLOB — raw image bytes
- Views v_log_files_with_issue and v_image_attachments_with_issue for
  cross-incident queries with joined issue title

Rust backend:
- compress_text / decompress_text helpers (flate2 rust_backend / miniz_oxide)
  with 100 MB decompression-bomb guard
- upload_log_file*, upload_log_file_by_content store content_compressed
- upload_image_attachment*, upload_paste_image store image_data
- New commands: get_log_file_content, list_all_log_files (analysis.rs)
- New commands: get_image_attachment_data, list_all_image_attachments (image.rs)
- All commands fall back to file_path for pre-migration records

Frontend:
- LogFileSummary, ImageAttachmentSummary types in tauriCommands.ts
- attachmentStore (Zustand) — loadAttachments, searchAttachments
- History page: Issues tab (existing) + Attachments tab (new)
  with log/image tables, search bar, View modals, lazy thumbnails

Tests: 227 Rust (+16 new), 103 frontend (+9 new), tsc clean, clippy clean

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
2026-05-31 17:55:47 -05:00

6.3 KiB
Raw Permalink Blame History

Ticket: Attachment DB Storage & Cross-Incident Recall

Branch: feature/attachment-db-storage-recall Base: master


Description

Log file and image attachment records previously stored only metadata and filesystem paths, making content volatile — if the source file moved or was deleted, the attachment record became orphaned. There was also no mechanism to search or recall attachments across incidents.

This feature:

  1. Stores gzip-compressed log text and raw image bytes directly in the database, making attachments fully self-contained and portable.
  2. Surfaces a new Attachments tab on the History page for cross-incident search and recall.
  3. Exposes content-retrieval commands so the AI chat context can reference log content from DB on demand, with no disk dependency.

Acceptance Criteria

  • Uploading a log file stores gzip-compressed text in log_files.content_compressed (BLOB)
  • Uploading an image stores raw bytes in image_attachments.image_data (BLOB)
  • get_log_file_content returns decompressed text from DB; falls back to disk for pre-migration records
  • get_image_attachment_data returns base64 data URL from DB; falls back to disk for pre-migration records
  • list_all_log_files returns cross-incident log summaries with joined issue title, supports search and issueId filter
  • list_all_image_attachments returns cross-incident image summaries with joined issue title, supports search and issueId filter
  • History page shows two tabs: Issues (existing, unchanged) and Attachments (new)
  • Attachments tab: Log Files section with filename, incident link, date, size, type badge, View button
  • Attachments tab: Images section with 48px thumbnail, filename, incident link, date, View button
  • "View" on log file → modal showing decompressed plain text
  • "View" on image → modal showing full-size image
  • Existing records with NULL content fall back to disk read — no breakage for pre-migration data
  • All new DB changes tracked via migrations 020022 with idempotency guarantees
  • Wiki documentation updated: IPC-Commands.md and Database.md

Work Implemented

Database (src-tauri/src/db/)

File Change
migrations.rs Migrations 020 (content_compressed BLOB), 021 (image_data BLOB), 022 (views v_log_files_with_issue + v_image_attachments_with_issue). Extended duplicate-column graceful handling for new ALTER TABLE migrations.
models.rs Added LogFileSummary and ImageAttachmentSummary structs for lightweight cross-incident list views (no BLOB fields — content stays out of IPC).

Rust Backend (src-tauri/src/commands/)

File Change
analysis.rs Private compress_text / decompress_text helpers (flate2/miniz_oxide — pure Rust, no system binary). Updated upload_log_file and upload_log_file_by_content INSERTs to store content_compressed. New commands: get_log_file_content, list_all_log_files.
image.rs Updated upload_image_attachment, upload_image_attachment_by_content, upload_paste_image INSERTs to store image_data. New commands: get_image_attachment_data, list_all_image_attachments.
lib.rs Registered all 4 new commands.

Dependencies (src-tauri/Cargo.toml)

  • Added flate2 = { version = "1", features = ["rust_backend"] } — pure-Rust gzip, portable cross-platform.

Frontend (src/)

File Change
lib/tauriCommands.ts Added LogFileSummary, ImageAttachmentSummary interfaces and 4 typed command wrappers.
stores/attachmentStore.ts New Zustand store: loadAttachments, searchAttachments, setSearchQuery.
pages/History/index.tsx Added tab bar; extracted IssuesTab (existing content, unchanged); added AttachmentsTab with log/image tables, search, View modals, and lazy ImageThumbnail component.

Documentation (docs/wiki/)

File Change
IPC-Commands.md Documented get_log_file_content, list_all_log_files, get_image_attachment_data, list_all_image_attachments with TypeScript signatures and interface shapes. Updated upload command notes.
Database.md Updated migration count (18 → 22). Documented migrations 020, 021, 022 with SQL, rationale, and usage notes.

Testing Needed

Automated (already passing)

Suite Count Status
Rust unit tests (cargo test) 226 All pass
Frontend unit tests (npm run test:run) 103 All pass
TypeScript type check (tsc --noEmit) Clean
Rust clippy (clippy -- -D warnings) Zero warnings
Rust format (fmt --check) Clean

New tests added:

  • test_compress_decompress_roundtrip, test_compress_large_text_is_smaller, test_decompress_invalid_bytes_returns_error (Rust, analysis.rs)
  • test_get_image_attachment_data_base64_format (Rust, image.rs)
  • test_020_log_content_compressed_column, test_021_image_data_column, test_022_attachment_views_exist, test_022_views_join_issue_title, test_020_021_idempotent (Rust, migrations.rs)
  • 9 attachment store tests (tests/unit/attachmentStore.test.ts)

Manual Smoke Testing Required

  1. Log upload → DB content storage

    • Create issue → upload .log file → inspect SQLite: SELECT id, LENGTH(content_compressed) FROM log_files — verify non-NULL non-zero value
  2. Content retrieval from DB

    • History → Attachments tab → Log Files → click "View" → confirm readable decompressed text appears in modal
  3. Fallback for pre-migration records

    • Manually UPDATE log_files SET content_compressed = NULL WHERE id = '<id>' → View should still load from disk path
  4. Image upload → DB byte storage

    • Upload image → SELECT id, LENGTH(image_data) FROM image_attachments — verify non-NULL
  5. Image display

    • History → Attachments tab → Images → thumbnails should render, View → full-size image modal
  6. Cross-incident search

    • Create 2+ issues with different log files → Attachments tab → search by partial filename → correct files appear
  7. Issue link navigation

    • Click incident title in Attachments tab → navigates to correct triage page
  8. Issue tab unchanged

    • Verify existing Issues tab retains all functionality (search, filter, sort, open, export buttons)