From 7be2fb0f629d77d31658b23c10f59294171e9d62 Mon Sep 17 00:00:00 2001 From: Phil Skentelbery Date: Tue, 16 Dec 2025 19:04:43 -0700 Subject: [PATCH] docs: Add v1.5.0 "Trigger" release definition MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Focus: Cleanup, test coverage, and quality of life improvements Goals: - 90% test coverage target - MPO format test coverage (High backlog item) - Debug file storage cleanup (Medium backlog item) - Filename sanitization in debug path (Medium backlog item) - N+1 query pattern fix (Medium backlog item) - Atomic variant generation (Medium backlog item) - Default slug change to timestamp format (Medium backlog item) Backlog items marked as scheduled for v1.5.0. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- docs/projectplan/BACKLOG.md | 12 +- docs/projectplan/v1.5.0/RELEASE.md | 257 +++++++++++++++++++++++++++++ 2 files changed, 263 insertions(+), 6 deletions(-) create mode 100644 docs/projectplan/v1.5.0/RELEASE.md diff --git a/docs/projectplan/BACKLOG.md b/docs/projectplan/BACKLOG.md index 1b60f50..fc30fc4 100644 --- a/docs/projectplan/BACKLOG.md +++ b/docs/projectplan/BACKLOG.md @@ -40,7 +40,7 @@ ## High -### MPO Format Test Coverage +### MPO Format Test Coverage *(Scheduled: v1.5.0)* - **Description**: MPO conversion code exists but has no test coverage. MPO is advertised in the CHANGELOG but the handling is untested. - **Location**: `starpunk/media.py` lines 163-173 - **Source**: Developer Review (M1) @@ -60,7 +60,7 @@ ## Medium -### Debug File Storage Without Cleanup Mechanism +### Debug File Storage Without Cleanup Mechanism *(Scheduled: v1.5.0)* - **Description**: Failed uploads are saved to `data/debug/` directory for analysis, but there is no mechanism to clean up these files. This could consume significant disk space, especially if under attack. - **Location**: `starpunk/media.py` lines 133-137 - **Source**: Developer Review (M2), Architect Review (Issue 1.2.2) @@ -69,19 +69,19 @@ 2. Implement automatic cleanup (files older than 7 days) 3. Add disk space check or size limit (e.g., 100MB max) -### Filename Not Sanitized in Debug Path (Security) +### Filename Not Sanitized in Debug Path (Security) *(Scheduled: v1.5.0)* - **Description**: The original filename is used directly in the debug file path without sanitization, which could cause path traversal or special character issues. - **Location**: `starpunk/media.py` line 135 - **Source**: Architect Review (Issue 1.2.3) - **Approach**: Sanitize filename before use: `safe_filename = "".join(c for c in filename if c.isalnum() or c in "._-")[:50]` -### N+1 Query Pattern in Feed Generation +### N+1 Query Pattern in Feed Generation *(Scheduled: v1.5.0)* - **Description**: In `_get_cached_notes()`, media and tags are loaded per-note in separate queries. For 50 notes, this is 100 additional database queries, degrading performance. - **Location**: `starpunk/routes/public.py` lines 68-74 - **Source**: Architect Review (Issue 2.2.9) - **Approach**: Implement batch loading function `get_media_for_notes(note_ids: List[int])` using a single query with `WHERE note_id IN (...)`. -### Transaction Not Atomic in Variant Generation +### Transaction Not Atomic in Variant Generation *(Scheduled: v1.5.0)* - **Description**: Files are written to disk before database commit. If the database commit fails, orphaned files remain on disk. - **Location**: `starpunk/media.py` lines 404-440 - **Source**: Architect Review (Issue 2.2.6) @@ -93,7 +93,7 @@ - **Source**: Architect Review (Security Assessment) - **Approach**: Implement Flask-Limiter or similar rate limiting middleware for upload routes. -### Default Slug Change +### Default Slug Change *(Scheduled: v1.5.0)* - The default slug should be a date time stamp - YYYYMMDDHHMMSS - Edge case, if the slug would somehow be a duplicate append a "-x" e.g. -1 diff --git a/docs/projectplan/v1.5.0/RELEASE.md b/docs/projectplan/v1.5.0/RELEASE.md new file mode 100644 index 0000000..0ea172c --- /dev/null +++ b/docs/projectplan/v1.5.0/RELEASE.md @@ -0,0 +1,257 @@ +# StarPunk v1.5.0 Release + +**Status**: Planning +**Codename**: "Trigger" +**Focus**: Cleanup, Test Coverage, and Quality of Life Improvements + +## Overview + +This minor release focuses on technical debt reduction, improving test coverage to 90%, and addressing quality-of-life issues identified in previous release reviews. No new user-facing features are planned. + +## Goals + +1. **90% Test Coverage** - Increase overall test coverage from current baseline to 90% +2. **Address Backlog Technical Debt** - Resolve 6 specific backlog items +3. **Improve Code Quality** - Better error handling, atomicity, and performance + +## Features + +### 1. Test Coverage Target: 90% + +Increase overall test coverage to 90% minimum across all modules. + +#### Scope +- Identify modules with coverage below 90% +- Write missing unit tests for uncovered code paths +- Add integration tests for critical workflows +- Ensure edge cases and error paths are tested + +#### Acceptance Criteria +- `uv run pytest --cov=starpunk` reports ≥90% overall coverage +- No module below 85% coverage +- All new code in this release has 100% coverage + +--- + +### 2. MPO Format Test Coverage + +**Source**: Backlog - High Priority (Developer Review M1) + +Add test coverage for MPO (Multi-Picture Object) format handling. + +#### Current State +- MPO conversion code exists at `starpunk/media.py` lines 163-173 +- Advertised in CHANGELOG but untested + +#### Implementation +- Add `test_mpo_detection_and_conversion()` to `TestHEICSupport` class +- Create MPO test image using Pillow's MPO support +- Test primary image extraction +- Test conversion to JPEG + +#### Acceptance Criteria +- MPO detection tested +- MPO to JPEG conversion tested +- Edge cases (corrupted MPO, single-frame MPO) tested + +--- + +### 3. Debug File Storage Cleanup + +**Source**: Backlog - Medium Priority (Developer Review M2, Architect Review 1.2.2) + +Implement cleanup mechanism for failed upload debug files. + +#### Current State +- Failed uploads saved to `data/debug/` directory +- No cleanup mechanism exists +- Potential disk space exhaustion + +#### Implementation +1. Add `DEBUG_SAVE_FAILED_UPLOADS` config option (default: `false` in production) +2. Implement automatic cleanup for files older than 7 days +3. Add disk space check or size limit (100MB max for debug folder) +4. Cleanup runs on application startup and periodically + +#### Configuration +```python +DEBUG_SAVE_FAILED_UPLOADS = False # Enable only for debugging +DEBUG_FILE_MAX_AGE_DAYS = 7 # Auto-delete after 7 days +DEBUG_FILE_MAX_SIZE_MB = 100 # Maximum debug folder size +``` + +#### Acceptance Criteria +- Debug files disabled by default in production +- Old files automatically cleaned up when enabled +- Disk space protected with size limit +- Tests cover all cleanup scenarios + +--- + +### 4. Filename Sanitization in Debug Path + +**Source**: Backlog - Medium Priority (Architect Review 1.2.3) + +Sanitize filenames before use in debug file paths. + +#### Current State +- Original filename used directly at `starpunk/media.py` line 135 +- Path traversal or special character issues possible + +#### Implementation +```python +safe_filename = "".join(c for c in filename if c.isalnum() or c in "._-")[:50] +``` + +#### Acceptance Criteria +- Filenames sanitized before debug path construction +- Path traversal attempts neutralized +- Special characters removed +- Filename length limited +- Tests cover malicious filename patterns + +--- + +### 5. N+1 Query Pattern Fix + +**Source**: Backlog - Medium Priority (Architect Review 2.2.9) + +Eliminate N+1 query pattern in feed generation. + +#### Current State +- `_get_cached_notes()` loads media and tags per-note +- For 50 notes: 100 additional database queries +- Performance degrades with note count + +#### Implementation +1. Create `get_media_for_notes(note_ids: List[int])` batch function +2. Create `get_tags_for_notes(note_ids: List[int])` batch function +3. Use single query with `WHERE note_id IN (...)` +4. Update `_get_cached_notes()` to use batch loading + +#### Example +```python +def get_media_for_notes(note_ids: List[int]) -> Dict[int, List[dict]]: + """Batch load media for multiple notes.""" + # Single query: SELECT * FROM note_media WHERE note_id IN (?) + # Returns: {note_id: [media_list]} +``` + +#### Acceptance Criteria +- Feed generation uses batch queries +- Query count reduced from O(n) to O(1) for media/tags +- Performance improvement measurable in tests +- No change to API behavior + +--- + +### 6. Atomic Variant Generation + +**Source**: Backlog - Medium Priority (Architect Review 2.2.6) + +Make variant file generation atomic with database commits. + +#### Current State +- Files written to disk before database commit +- Database commit failure leaves orphaned files + +#### Implementation +1. Write variant files to temporary location first +2. Database insert in transaction +3. Move files to final location after successful commit +4. Cleanup temp files on any failure + +#### Flow +``` +1. Generate variants to temp directory +2. BEGIN TRANSACTION +3. INSERT media record +4. INSERT variant records +5. COMMIT +6. Move files from temp to final location +7. On failure: rollback DB, delete temp files +``` + +#### Acceptance Criteria +- No orphaned files on database failures +- No orphaned database records on file failures +- Atomic operation for all media saves +- Tests simulate failure scenarios + +--- + +### 7. Default Slug Format Change + +**Source**: Backlog - Medium Priority + +Change default slug format from content-based to timestamp-based. + +#### Current State +- Slugs generated from note content +- Can produce unwanted slugs from private content + +#### New Format +- Default: `YYYYMMDDHHMMSS` (e.g., `20251216143052`) +- Duplicate handling: append `-1`, `-2`, etc. +- Custom slugs still supported via `mp-slug` + +#### Implementation +1. Update `generate_slug()` function in `starpunk/slug_utils.py` +2. Generate timestamp-based slug by default +3. Check for duplicates and append suffix if needed +4. Preserve custom slug functionality + +#### Example +```python +def generate_slug(content: str = None, custom_slug: str = None) -> str: + if custom_slug: + return sanitize_slug(custom_slug) + # Default: timestamp-based + timestamp = datetime.now().strftime("%Y%m%d%H%M%S") + return ensure_unique_slug(timestamp) +``` + +#### Acceptance Criteria +- New notes use timestamp slugs by default +- Duplicate timestamps handled with suffix +- Custom `mp-slug` still works +- Existing notes unchanged +- Tests cover all slug generation scenarios + +--- + +## Out of Scope + +- New user-facing features +- UI changes +- New feed formats +- Micropub extensions +- Database schema changes (except for bug fixes) + +## Dependencies + +- v1.4.x complete +- No new external dependencies expected + +## Success Criteria + +1. ✅ Test coverage ≥90% overall +2. ✅ MPO format has test coverage +3. ✅ Debug file cleanup implemented and tested +4. ✅ Filename sanitization implemented and tested +5. ✅ N+1 query pattern eliminated +6. ✅ Variant generation is atomic +7. ✅ Default slugs are timestamp-based +8. ✅ All existing tests continue to pass +9. ✅ No regressions in functionality + +## Related Backlog Items + +After completion, the following backlog items should be marked complete or moved to "Recently Completed": + +- MPO Format Test Coverage (High) +- Debug File Storage Without Cleanup Mechanism (Medium) +- Filename Not Sanitized in Debug Path (Medium) +- N+1 Query Pattern in Feed Generation (Medium) +- Transaction Not Atomic in Variant Generation (Medium) +- Default Slug Change (Medium)