feat: v1.2.0-rc.1 - IndieWeb Features Release Candidate

Complete implementation of v1.2.0 "IndieWeb Features" release.

## Phase 1: Custom Slugs
- Optional custom slug field in note creation form
- Auto-sanitization (lowercase, hyphens only)
- Uniqueness validation with auto-numbering
- Read-only after creation to preserve permalinks
- Matches Micropub mp-slug behavior

## Phase 2: Author Discovery + Microformats2
- Automatic h-card discovery from IndieAuth identity URL
- 24-hour caching with graceful fallback
- Never blocks login (per ADR-061)
- Complete h-entry, h-card, h-feed markup
- All required Microformats2 properties
- rel-me links for identity verification
- Passes IndieWeb validation

## Phase 3: Media Upload
- Upload up to 4 images per note (JPEG, PNG, GIF, WebP)
- Automatic optimization with Pillow
  - Auto-resize to 2048px
  - EXIF orientation correction
  - 95% quality compression
- Social media-style layout (media top, text below)
- Optional captions for accessibility
- Integration with all feed formats (RSS, ATOM, JSON Feed)
- Date-organized storage with UUID filenames
- Immutable caching (1 year)

## Database Changes
- migrations/006_add_author_profile.sql - Author discovery cache
- migrations/007_add_media_support.sql - Media storage

## New Modules
- starpunk/author_discovery.py - h-card discovery and caching
- starpunk/media.py - Image upload, validation, optimization

## Documentation
- 4 new ADRs (056, 057, 058, 061)
- Complete design specifications
- Developer Q&A with 40+ questions answered
- 3 implementation reports
- 3 architect reviews (all approved)

## Testing
- 56 new tests for v1.2.0 features
- 842 total tests in suite
- All v1.2.0 feature tests passing

## Dependencies
- Added: mf2py (Microformats2 parser)
- Added: Pillow (image processing)

Version: 1.2.0-rc.1

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-11-28 15:02:20 -07:00
parent 83739ec2c6
commit dd822a35b5
40 changed files with 6929 additions and 15 deletions

View File

@@ -0,0 +1,303 @@
# v1.2.0 Developer Q&A
**Date**: 2025-11-28
**Architect**: StarPunk Architect Subagent
**Purpose**: Answer critical implementation questions for v1.2.0
## Custom Slugs Answers
**Q1: Validation pattern conflict - should we apply new lowercase validation to existing slugs?**
- **Answer:** Validate only new custom slugs, don't migrate existing slugs
- **Rationale:** Existing slugs work, no need to change them retroactively
- **Implementation:** In `validate_and_sanitize_custom_slug()`, apply lowercase enforcement only to new/edited slugs
**Q2: Form field readonly behavior - how should the slug field behave on edit forms?**
- **Answer:** Display as readonly input field with current value visible
- **Rationale:** Users need to see the current slug but understand it cannot be changed
- **Implementation:** Use `readonly` attribute, not `disabled` (disabled fields don't submit with form)
**Q3: Slug uniqueness validation - where should this happen?**
- **Answer:** Both client-side (for UX) and server-side (for security)
- **Rationale:** Client-side prevents unnecessary submissions, server-side is authoritative
- **Implementation:** Database unique constraint + Python validation in `validate_and_sanitize_custom_slug()`
## Media Upload Answers
**Q4: Media upload flow - how should upload and note association work?**
- **Answer:** Upload during note creation, associate via note_id after creation
- **Rationale:** Simpler than pre-upload with temporary IDs
- **Implementation:** Upload files in `create_note_submit()` after note is created, store associations in media table
**Q5: Storage directory structure - exact path format?**
- **Answer:** `data/media/YYYY/MM/filename-uuid.ext`
- **Rationale:** Date organization helps with backups and management
- **Implementation:** Use `os.makedirs(path, exist_ok=True)` to create directories as needed
**Q6: File naming convention - how to ensure uniqueness?**
- **Answer:** `{original_name_slug}-{uuid4()[:8]}.{extension}`
- **Rationale:** Preserves original name for SEO while ensuring uniqueness
- **Implementation:** Slugify original filename, append 8-char UUID, preserve extension
**Q7: MIME type validation - which types exactly?**
- **Answer:** Allow: image/jpeg, image/png, image/gif, image/webp. Reject all others
- **Rationale:** Common web formats only, no SVG (XSS risk)
- **Implementation:** Use python-magic for reliable MIME detection, not just file extension
**Q8: Upload size limits - what's reasonable?**
- **Answer:** 10MB per file, 40MB total per note (4 files × 10MB)
- **Rationale:** Sufficient for high-quality images without overwhelming storage
- **Implementation:** Check in both client-side JavaScript and server-side validation
**Q9: Database schema for media table - exact columns?**
- **Answer:** id, note_id, filename, mime_type, size_bytes, width, height, uploaded_at
- **Rationale:** Minimal but sufficient metadata for display and management
- **Implementation:** Use Pillow to extract image dimensions on upload
**Q10: Orphaned file cleanup - how to handle?**
- **Answer:** Keep orphaned files, add admin cleanup tool in future version
- **Rationale:** Data preservation is priority, cleanup can be manual for v1.2.0
- **Implementation:** Log orphaned files but don't auto-delete
**Q11: Upload progress indication - required for v1.2.0?**
- **Answer:** No, simple form submission is sufficient for v1.2.0
- **Rationale:** Keep it simple, can enhance in future version
- **Implementation:** Standard HTML form with enctype="multipart/form-data"
**Q12: Image display order - how to maintain?**
- **Answer:** Use upload sequence, store display_order in media table
- **Rationale:** Predictable and simple
- **Implementation:** Auto-increment display_order starting at 0
**Q13: Thumbnail generation - needed for v1.2.0?**
- **Answer:** No, use CSS for responsive sizing
- **Rationale:** Simplicity over optimization for v1
- **Implementation:** Use `max-width: 100%` and lazy loading
**Q14: Edit form media handling - can users remove media?**
- **Answer:** Yes, checkbox to mark for deletion
- **Rationale:** Essential editing capability
- **Implementation:** "Remove" checkboxes next to each image in edit form
**Q15: Media URL structure - exact format?**
- **Answer:** `/media/YYYY/MM/filename.ext` (matches storage path)
- **Rationale:** Clean URLs, date organization visible
- **Implementation:** Route in `starpunk/routes/public.py` using send_from_directory
## Author Discovery Answers
**Q16: Discovery failure handling - what if profile URL is unreachable?**
- **Answer:** Use defaults: name from IndieAuth me URL domain, no photo
- **Rationale:** Always provide something, never break
- **Implementation:** Try discovery, catch all exceptions, use defaults
**Q17: h-card parsing library - which one?**
- **Answer:** Use mf2py (already in requirements for Micropub)
- **Rationale:** Already a dependency, well-maintained
- **Implementation:** `import mf2py; result = mf2py.parse(url=profile_url)`
**Q18: Multiple h-cards on profile - which to use?**
- **Answer:** First h-card with url property matching the profile URL
- **Rationale:** Most specific match per IndieWeb convention
- **Implementation:** Loop through h-cards, check url property
**Q19: Discovery caching duration - how long?**
- **Answer:** 24 hours, with manual refresh button in admin
- **Rationale:** Balance between freshness and performance
- **Implementation:** Store discovered_at timestamp, check age
**Q20: Profile update mechanism - when to refresh?**
- **Answer:** On login + manual refresh button + 24hr expiry
- **Rationale:** Login is natural refresh point
- **Implementation:** Call discovery in auth callback
**Q21: Missing properties handling - what if no name/photo?**
- **Answer:** name = domain from URL, photo = None (no image)
- **Rationale:** Graceful degradation
- **Implementation:** Use get() with defaults on parsed properties
**Q22: Database schema for author_profile - exact columns?**
- **Answer:** me_url (PK), name, photo, url, discovered_at, raw_data (JSON)
- **Rationale:** Cache parsed data + raw for debugging
- **Implementation:** Single row table, upsert on discovery
## Microformats2 Answers
**Q23: h-card placement - where exactly in templates?**
- **Answer:** Only within h-entry author property (p-author h-card)
- **Rationale:** Correct semantic placement per spec
- **Implementation:** In note partial template, not standalone
**Q24: h-feed container - which pages need it?**
- **Answer:** Homepage (/) and any paginated list pages
- **Rationale:** Feed pages only, not single note pages
- **Implementation:** Wrap note list in div.h-feed with h1.p-name
**Q25: Optional properties - which to include?**
- **Answer:** Only what we have: author, name, url, published, content
- **Rationale:** Don't add empty properties
- **Implementation:** Use conditional template blocks
**Q26: Micropub compatibility - any changes needed?**
- **Answer:** No, Micropub already handles microformats correctly
- **Rationale:** Micropub creates data, templates display it
- **Implementation:** Ensure templates match Micropub's data model
## Feed Integration Answers
**Q27: RSS/Atom changes for media - how to include images?**
- **Answer:** Add as enclosures (RSS) and link rel="enclosure" (Atom)
- **Rationale:** Standard podcast/media pattern
- **Implementation:** Loop through note.media, add enclosure elements
**Q28: JSON Feed media handling - which property?**
- **Answer:** Use "attachments" array per JSON Feed 1.1 spec
- **Rationale:** Designed for exactly this use case
- **Implementation:** Create attachment objects with url, mime_type
**Q29: Feed caching - any changes needed?**
- **Answer:** No, existing cache logic is sufficient
- **Rationale:** Media URLs are stable once uploaded
- **Implementation:** No changes required
**Q30: Author in feeds - use discovered data?**
- **Answer:** Yes, use discovered name and photo in feed metadata
- **Rationale:** Consistency across all outputs
- **Implementation:** Pass author_profile to feed templates
## Database Migration Answers
**Q31: Migration naming convention - what number?**
- **Answer:** Use next sequential: 005_add_media_support.sql
- **Rationale:** Continue existing pattern
- **Implementation:** Check latest migration, increment
**Q32: Migration rollback - needed?**
- **Answer:** No, forward-only migrations per project convention
- **Rationale:** Simplicity, follows existing pattern
- **Implementation:** CREATE IF NOT EXISTS, never DROP
**Q33: Migration testing - how to verify?**
- **Answer:** Test on copy of production database
- **Rationale:** Real-world data is best test
- **Implementation:** Copy data/starpunk.db, run migration, verify
## Testing Strategy Answers
**Q34: Test data for media - what to use?**
- **Answer:** Generate 1x1 pixel PNG in tests, don't use real files
- **Rationale:** Minimal, fast, no binary files in repo
- **Implementation:** Use Pillow to generate test images in memory
**Q35: Author discovery mocking - how to test?**
- **Answer:** Mock HTTP responses with test h-card HTML
- **Rationale:** Deterministic, no external dependencies
- **Implementation:** Use responses library or unittest.mock
**Q36: Integration test priority - which are critical?**
- **Answer:** Upload → Display → Edit → Delete flow
- **Rationale:** Core user journey must work
- **Implementation:** Single test that exercises full lifecycle
## Error Handling Answers
**Q37: Upload failure recovery - how to handle?**
- **Answer:** Show error, preserve form data, allow retry
- **Rationale:** Don't lose user's work
- **Implementation:** Flash error, return to form with content preserved
**Q38: Discovery network timeout - how long to wait?**
- **Answer:** 5 second timeout for profile fetch
- **Rationale:** Balance between patience and responsiveness
- **Implementation:** Use requests timeout parameter
## Deployment Answers
**Q39: Media directory permissions - what's needed?**
- **Answer:** data/media/ needs write permission for app user
- **Rationale:** Same as existing data/ directory
- **Implementation:** Document in deployment guide, create in setup
**Q40: Upgrade path from v1.1.2 - any special steps?**
- **Answer:** Run migration, create media directory, restart app
- **Rationale:** Minimal disruption
- **Implementation:** Add to CHANGELOG upgrade notes
**Q41: Configuration changes - any new env vars?**
- **Answer:** No, all settings have sensible defaults
- **Rationale:** Maintain zero-config philosophy
- **Implementation:** Hardcode limits in code with constants
## Critical Path Decisions Summary
These are the key decisions to unblock implementation:
1. **Media upload flow**: Upload after note creation, associate via note_id
2. **Author discovery**: Use mf2py, cache for 24hrs, graceful fallbacks
3. **h-card parsing**: First h-card with matching URL property
4. **h-card placement**: Only within h-entry as p-author
5. **Migration strategy**: Sequential numbering (005), forward-only
## Implementation Order
Based on dependencies and complexity:
### Phase 1: Custom Slugs (2 hours)
- Simplest feature
- No database changes
- Template and validation only
### Phase 2: Author Discovery (4 hours)
- Build discovery module
- Add author_profile table
- Integrate with auth flow
- Update templates
### Phase 3: Media Upload (6 hours)
- Most complex feature
- Media table and migration
- Upload handling
- Template updates
- Storage management
## File Structure
Key files to create/modify:
### New Files
- `starpunk/discovery.py` - Author discovery module
- `starpunk/media.py` - Media handling module
- `migrations/005_add_media_support.sql` - Database changes
- `static/js/media-upload.js` - Optional enhancement
### Modified Files
- `templates/admin/new.html` - Add slug and media fields
- `templates/admin/edit.html` - Add slug (readonly) and media
- `templates/partials/note.html` - Add microformats markup
- `templates/public/index.html` - Add h-feed container
- `starpunk/routes/admin.py` - Handle slugs and uploads
- `starpunk/routes/auth.py` - Trigger discovery on login
- `starpunk/models/note.py` - Add media relationship
## Success Metrics
Implementation is complete when:
1. ✅ Custom slug can be specified on creation
2. ✅ Images can be uploaded and displayed
3. ✅ Author info is discovered from IndieAuth profile
4. ✅ IndieWebify.me validates h-feed and h-entry
5. ✅ All tests pass
6. ✅ No regressions in existing functionality
7. ✅ Media files are tracked in database
8. ✅ Errors are handled gracefully
## Final Notes
- Keep it simple - this is v1.2.0, not v2.0.0
- Data preservation over premature optimization
- When uncertain, choose the more explicit option
- Document any deviations from this guidance
---
This Q&A document serves as the authoritative implementation guide for v1.2.0. Any questions not covered here should follow the principle of maximum simplicity.