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

119 lines
6.3 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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