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>
6.9 KiB
v1.2.0 Phase 1: Custom Slugs - Implementation Report
Date: 2025-11-28 Developer: StarPunk Fullstack Developer Subagent Phase: v1.2.0 Phase 1 of 3 Status: Complete
Summary
Implemented custom slug input field in the web UI note creation form, allowing users to specify custom slugs when creating notes. This brings the web UI to feature parity with the Micropub API's mp-slug property.
Implementation Overview
What Was Implemented
-
Custom Slug Input Field (templates/admin/new.html)
- Added optional text input field for custom slugs
- HTML5 pattern validation for client-side guidance
- Helpful placeholder and helper text
- Positioned between content field and publish checkbox
-
Read-Only Slug Display (templates/admin/edit.html)
- Shows current slug as disabled input field
- Includes explanation that slugs cannot be changed
- Preserves permalink integrity
-
Route Handler Updates (starpunk/routes/admin.py)
- Updated
create_note_submit()to acceptcustom_slugform parameter - Passes custom slug to
create_note()function - Uses existing slug validation from
slug_utils.py
- Updated
-
Comprehensive Test Suite (tests/test_custom_slugs.py)
- 30 tests covering all aspects of custom slug functionality
- Tests validation, sanitization, uniqueness, web UI, and edge cases
- Verifies consistency with Micropub behavior
Technical Details
Backend Integration
The implementation leverages existing infrastructure:
- Slug validation: Uses
slug_utils.validate_and_sanitize_custom_slug() - Slug sanitization: Auto-converts to lowercase, removes invalid characters
- Uniqueness checking: Handled by existing
make_slug_unique_with_suffix() - Error handling: Graceful fallbacks for reserved slugs, hierarchical paths, emoji
Frontend Behavior
New Note Form:
<input type="text"
id="custom_slug"
name="custom_slug"
pattern="[a-z0-9-]+"
placeholder="leave-blank-for-auto-generation">
Edit Note Form:
<input type="text"
id="slug"
value="{{ note.slug }}"
readonly
disabled>
Validation Rules
Per slug_utils.py:
- Lowercase letters only
- Numbers allowed
- Hyphens allowed (not consecutive, not leading/trailing)
- Max length: 200 characters
- Reserved slugs: api, admin, auth, feed, static, etc.
Error Handling
- Hierarchical paths (e.g., "path/to/note"): Rejected with error message
- Reserved slugs: Auto-suffixed (e.g., "api" becomes "api-note")
- Invalid characters: Sanitized to valid format
- Duplicates: Auto-suffixed with sequential number (e.g., "slug-2")
- Unicode/emoji: Falls back to timestamp-based slug
Test Results
All 30 tests passing:
tests/test_custom_slugs.py::TestCustomSlugValidation (15 tests)
tests/test_custom_slugs.py::TestCustomSlugWebUI (9 tests)
tests/test_custom_slugs.py::TestCustomSlugMatchesMicropub (2 tests)
tests/test_custom_slugs.py::TestCustomSlugEdgeCases (4 tests)
Test Coverage
Validation Tests:
- Lowercase conversion
- Invalid character sanitization
- Consecutive hyphen removal
- Leading/trailing hyphen trimming
- Unicode normalization
- Reserved slug detection
- Hierarchical path rejection
Web UI Tests:
- Custom slug creation
- Auto-generation fallback
- Uppercase conversion
- Invalid character handling
- Duplicate slug handling
- Reserved slug handling
- Hierarchical path error
- Read-only display in edit form
- Field presence in new form
Micropub Consistency Tests:
- Same validation rules
- Same sanitization behavior
Edge Case Tests:
- Empty slug
- Whitespace-only slug
- Emoji slug (timestamp fallback)
- Unicode slug normalization
Files Modified
Modified Files
templates/admin/new.html- Added custom slug input fieldtemplates/admin/edit.html- Added read-only slug displaystarpunk/routes/admin.py- Updated route handlerCHANGELOG.md- Added entry for v1.2.0 Phase 1
New Files
tests/test_custom_slugs.py- Comprehensive test suite (30 tests)docs/reports/2025-11-28-v1.2.0-phase1-custom-slugs.md- This report
Unchanged Files (Used)
starpunk/notes.py- Already hadcustom_slugparameterstarpunk/slug_utils.py- Already had validation functions
Design Decisions
Why Read-Only in Edit Form?
Per developer Q&A Q2 and Q7:
- Changing slugs breaks permalinks
- Users need to see current slug
- Using
readonly+disabledprevents form submission - Clear explanatory text prevents confusion
Why Same Validation as Micropub?
Per developer Q&A Q39:
- Consistency across all note creation methods
- Users shouldn't get different results from web UI vs API
- Reusing existing validation reduces bugs
Why Auto-Sanitize Instead of Reject?
Per developer Q&A Q3 and slug_utils design:
- Better user experience (helpful vs. frustrating)
- Follows "be liberal in what you accept" principle
- Timestamp fallback ensures notes are never rejected
- Matches Micropub behavior (Q8: never fail requests)
User Experience
Creating a Note with Custom Slug
- User fills in content
- (Optional) User enters custom slug
- System auto-sanitizes slug (lowercase, remove invalid chars)
- System checks uniqueness, adds suffix if needed
- Note created with custom or auto-generated slug
- Success message shows final slug
Creating a Note Without Custom Slug
- User fills in content
- User leaves slug field blank
- System auto-generates slug from first 5 words
- System checks uniqueness, adds suffix if needed
- Note created with auto-generated slug
Editing a Note
- User opens edit form
- Slug shown as disabled field
- User can see but not change slug
- Helper text explains why
Compliance with Requirements
✅ Custom slug field in note creation form
✅ Field is optional (auto-generate if empty)
✅ Field is read-only on edit (prevent permalink breaks)
✅ Validate slug format: ^[a-z0-9-]+$
✅ Auto-sanitize input (convert to lowercase, replace invalid chars)
✅ Check uniqueness before saving
✅ Show helpful error messages
✅ Tests passing
✅ CHANGELOG updated
✅ Implementation report created
Next Steps
This completes Phase 1 of v1.2.0. The remaining phases are:
Phase 2: Author Discovery + Microformats2 (4 hours)
- Implement h-card discovery from IndieAuth profile
- Add author_profile database table
- Update templates with microformats2 markup
- Integrate discovery with auth flow
Phase 3: Media Upload (6 hours)
- Add media upload to note creation form
- Implement media handling and storage
- Add media database table and migration
- Update templates to display media
- Add media management in edit form
Notes
- Implementation took approximately 2 hours as estimated
- No blockers encountered
- All existing tests continue to pass
- No breaking changes to existing functionality
- Ready for architect review
Implementation Status: ✅ Complete Tests Status: ✅ All Passing (30/30) Documentation Status: ✅ Complete