Files
StarPunk/docs/design/v1.2.0/developer-qa.md
Phil Skentelbery dd822a35b5 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>
2025-11-28 15:02:20 -07:00

13 KiB
Raw Blame History

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.