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>
13 KiB
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
readonlyattribute, notdisabled(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.pyusing 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:
- Media upload flow: Upload after note creation, associate via note_id
- Author discovery: Use mf2py, cache for 24hrs, graceful fallbacks
- h-card parsing: First h-card with matching URL property
- h-card placement: Only within h-entry as p-author
- 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 modulestarpunk/media.py- Media handling modulemigrations/005_add_media_support.sql- Database changesstatic/js/media-upload.js- Optional enhancement
Modified Files
templates/admin/new.html- Add slug and media fieldstemplates/admin/edit.html- Add slug (readonly) and mediatemplates/partials/note.html- Add microformats markuptemplates/public/index.html- Add h-feed containerstarpunk/routes/admin.py- Handle slugs and uploadsstarpunk/routes/auth.py- Trigger discovery on loginstarpunk/models/note.py- Add media relationship
Success Metrics
Implementation is complete when:
- ✅ Custom slug can be specified on creation
- ✅ Images can be uploaded and displayed
- ✅ Author info is discovered from IndieAuth profile
- ✅ IndieWebify.me validates h-feed and h-entry
- ✅ All tests pass
- ✅ No regressions in existing functionality
- ✅ Media files are tracked in database
- ✅ 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.