Files
StarPunk/docs/decisions/ADR-062-timestamp-based-slug-format.md
Phil Skentelbery 9dcc5c5710 docs: v1.5.0 planning - ADR-062, release plan, and design docs
- ADR-062: Timestamp-based slug format (supersedes ADR-007)
- Updated v1.5.0 RELEASE.md with 6-phase plan
- Updated BACKLOG.md with deferred N+1 query locations
- Developer questions and architect responses for Phase 1

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-16 19:38:01 -07:00

7.0 KiB

ADR-062: Timestamp-Based Slug Format

Status

Accepted (Supersedes ADR-007)

Context

ADR-007 established a content-based slug generation algorithm that extracts the first 5 words from note content to create URL slugs. While this approach provides readable, SEO-friendly URLs, it has drawbacks for a personal note-taking system:

  1. Privacy Concerns: The slug reveals note content in the URL. Private thoughts or draft content become visible in URLs that may be shared or logged.

  2. SEO Irrelevance: StarPunk is designed for personal IndieWeb notes, not public blogs. Notes are typically short-form content (similar to tweets or status updates) where SEO optimization provides no meaningful benefit.

  3. Unpredictable Slugs: Users cannot predict what slug will be generated without examining their content carefully.

  4. Edge Case Handling: Content-based slugs require complex fallback logic for short content, unicode-only content, or special characters.

  5. Collision Complexity: Similar notes require random suffix generation (e.g., hello-world-a7c9), which undermines the readability goal.

The user has explicitly stated: "Notes don't need SEO."

Decision

Change the default slug format from content-based to timestamp-based:

New Default Format: YYYYMMDDHHMMSS

  • Example: 20251216143052
  • Compact, sortable, predictable
  • 14 characters total

Collision Handling: Sequential numeric suffix

  • First collision: 20251216143052-1
  • Second collision: 20251216143052-2
  • Simple, predictable, no randomness

Custom Slugs: Continue to support user-specified slugs via mp-slug Micropub property and the web UI custom slug field. When provided, custom slugs take precedence.

Algorithm Specification

def generate_slug(custom_slug: str = None, created_at: datetime = None) -> str:
    """Generate a URL-safe slug for a note.

    Args:
        custom_slug: User-provided custom slug (takes precedence if provided)
        created_at: Note creation timestamp (defaults to now)

    Returns:
        URL-safe slug string
    """
    if custom_slug:
        return sanitize_slug(custom_slug)

    # Default: timestamp-based
    timestamp = (created_at or datetime.now()).strftime("%Y%m%d%H%M%S")
    return ensure_unique_slug(timestamp)

def ensure_unique_slug(base_slug: str) -> str:
    """Ensure slug is unique, adding numeric suffix if needed."""
    if not slug_exists(base_slug):
        return base_slug

    suffix = 1
    while slug_exists(f"{base_slug}-{suffix}"):
        suffix += 1
    return f"{base_slug}-{suffix}"

Examples

Scenario Generated Slug
Normal note at 2:30:52 PM on Dec 16, 2025 20251216143052
Second note in same second 20251216143052-1
Third note in same second 20251216143052-2
Note with custom slug "my-custom-slug" my-custom-slug

Rationale

Timestamp Format (Score: 9/10)

Pros:

  • Privacy: URL reveals nothing about content
  • Predictability: User knows exactly what slug format to expect
  • Sortability: Chronological sorting by URL is possible
  • Simplicity: No complex word extraction or normalization
  • Collision Rarity: Same-second creation is rare; handled cleanly when it occurs
  • Compact: 14 characters vs potentially 50+ for content-based

Cons:

  • Not Memorable: 20251216143052 is harder to remember than hello-world
  • No SEO Value: Search engines prefer descriptive URLs

Why Cons Don't Matter:

  • StarPunk is for personal notes, not public SEO-optimized content
  • Notes are accessed via UI, feeds, or bookmarks, not memorized URLs
  • Users wanting memorable URLs can use custom slugs

Sequential Suffix (Score: 10/10)

Pros:

  • Deterministic: No randomness; same collision always gets same suffix
  • Simple: No cryptographic random generation needed
  • Readable: -1, -2 are clear and obvious
  • Debuggable: Easy to understand collision resolution

Cons:

  • Enumerable: Sequential numbers could be probed
  • Not a real security concern for note slugs

Comparison with ADR-007 Approach

Aspect ADR-007 (Content-Based) ADR-062 (Timestamp)
Privacy Reveals content Reveals only time
Complexity High (word extraction, normalization, unicode handling) Low (strftime)
SEO Good None
Predictability Low High
Collision handling Random suffix Sequential suffix
Fallback cases Many (short content, unicode, etc.) None
Code lines ~50 ~15

Consequences

Positive

  1. Simplified Code: Remove complex word extraction, unicode normalization, and multiple fallback paths
  2. Better Privacy: Note content never appears in URLs
  3. Predictable Output: Users always know what slug format to expect
  4. Fewer Edge Cases: No special handling for short content, unicode, or special characters
  5. Cleaner Collisions: Sequential suffixes are more intuitive than random strings

Negative

  1. Migration: Existing notes keep their content-based slugs (no migration needed)
  2. Not Human-Readable: URLs don't describe content
  3. No SEO: Search engines won't benefit from descriptive URLs

Mitigations

Human-Readable URLs:

  • Users wanting descriptive URLs can use custom slugs via mp-slug
  • The web UI custom slug field remains available
  • This is opt-in rather than default

SEO:

  • IndieWeb notes are typically not SEO targets
  • Content is in the page body where search engines can index it
  • Microformats2 markup provides semantic meaning

Backward Compatibility

  • Existing notes retain their slugs (no data migration)
  • New notes use timestamp format by default
  • Custom slug functionality unchanged
  • All existing URLs remain valid

Testing Requirements

  • Test default slug generation produces timestamp format
  • Test collision handling with sequential suffixes
  • Test custom slugs still take precedence
  • Test edge case: multiple notes in same second
  • Test reserved slug rejection still works
  • Verify existing tests for custom slugs pass

Implementation Notes

Changes required in:

  • starpunk/slug_utils.py: Update generate_slug() function
  • starpunk/notes.py: Remove content parameter from slug generation call
  • Tests: Update expected slug formats

Estimated effort: Small (1-2 hours implementation, 1 hour testing)

References

  • ADR-007: Slug Generation Algorithm (Superseded by this ADR)
  • ADR-035: Custom Slugs (Unchanged; complements this decision)
  • IndieWeb Permalink Best Practices: https://indieweb.org/permalink

Acceptance Criteria

  • Default slugs use YYYYMMDDHHMMSS format
  • Collision handling uses sequential suffix (-1, -2, etc.)
  • Custom slugs via mp-slug continue to work
  • Custom slugs via web UI continue to work
  • Reserved slug validation unchanged
  • Existing notes unaffected
  • All tests pass
  • Code complexity reduced

Approved: 2025-12-16 Architect: StarPunk Architect Agent Supersedes: ADR-007 (Slug Generation Algorithm)