Files
StarPunk/docs/design/v1.2.0/2025-11-28-v1.2.0-phase1-custom-slugs.md
Phil Skentelbery f10d0679da feat(tags): Add database schema and tags module (v1.3.0 Phase 1)
Implements tag/category system backend following microformats2 p-category specification.

Database changes:
- Migration 008: Add tags and note_tags tables
- Normalized tag storage (case-insensitive lookup, display name preserved)
- Indexes for performance

New module:
- starpunk/tags.py: Tag management functions
  - normalize_tag: Normalize tag strings
  - get_or_create_tag: Get or create tag records
  - add_tags_to_note: Associate tags with notes (replaces existing)
  - get_note_tags: Retrieve note tags (alphabetically ordered)
  - get_tag_by_name: Lookup tag by normalized name
  - get_notes_by_tag: Get all notes with specific tag
  - parse_tag_input: Parse comma-separated tag input

Model updates:
- Note.tags property (lazy-loaded, prefer pre-loading in routes)
- Note.to_dict() add include_tags parameter

CRUD updates:
- create_note() accepts tags parameter
- update_note() accepts tags parameter (None = no change, [] = remove all)

Micropub integration:
- Pass tags to create_note() (tags already extracted by extract_tags())
- Return tags in q=source response

Per design doc: docs/design/v1.3.0/microformats-tags-design.md

Generated with Claude Code

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-10 11:24:23 -07:00

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

  1. 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
  2. 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
  3. Route Handler Updates (starpunk/routes/admin.py)

    • Updated create_note_submit() to accept custom_slug form parameter
    • Passes custom slug to create_note() function
    • Uses existing slug validation from slug_utils.py
  4. 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 field
  • templates/admin/edit.html - Added read-only slug display
  • starpunk/routes/admin.py - Updated route handler
  • CHANGELOG.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 had custom_slug parameter
  • starpunk/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 + disabled prevents 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

  1. User fills in content
  2. (Optional) User enters custom slug
  3. System auto-sanitizes slug (lowercase, remove invalid chars)
  4. System checks uniqueness, adds suffix if needed
  5. Note created with custom or auto-generated slug
  6. Success message shows final slug

Creating a Note Without Custom Slug

  1. User fills in content
  2. User leaves slug field blank
  3. System auto-generates slug from first 5 words
  4. System checks uniqueness, adds suffix if needed
  5. Note created with auto-generated slug

Editing a Note

  1. User opens edit form
  2. Slug shown as disabled field
  3. User can see but not change slug
  4. 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