feat(slugs): Implement timestamp-based slugs per ADR-062

Replaces content-based slug generation with timestamp format YYYYMMDDHHMMSS.
Simplifies slug generation and improves privacy by not exposing note content in URLs.

Changes:
- Add generate_timestamp_slug() to slug_utils.py
- Update notes.py to use timestamp slugs for default generation
- Sequential collision suffix (-1, -2) instead of random
- Custom slugs via mp-slug continue to work unchanged
- 892 tests passing (+18 new timestamp slug tests)

Per ADR-062 and v1.5.0 Phase 1 specification.

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

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-12-17 09:49:30 -07:00
parent 92e7bdd342
commit 3f1f82a749
6 changed files with 580 additions and 20 deletions

View File

@@ -133,15 +133,19 @@ class TestCreateNote:
assert note.updated_at == created_at
def test_create_generates_unique_slug(self, app, client):
"""Test slug uniqueness enforcement"""
"""Test slug uniqueness enforcement with timestamp slugs (ADR-062)"""
with app.app_context():
# Create two notes with identical content to force slug collision
note1 = create_note("# Same Title\n\nSame content for both")
note2 = create_note("# Same Title\n\nSame content for both")
from datetime import datetime
# Create two notes at the same timestamp to force slug collision
fixed_time = datetime(2025, 12, 16, 14, 30, 52)
note1 = create_note("First note", created_at=fixed_time)
note2 = create_note("Second note", created_at=fixed_time)
assert note1.slug != note2.slug
# Second slug should have random suffix added (4 chars + hyphen)
assert len(note2.slug) == len(note1.slug) + 5 # -xxxx suffix
# First note gets base timestamp slug
assert note1.slug == "20251216143052"
# Second note gets sequential suffix per ADR-062
assert note2.slug == "20251216143052-1"
def test_create_file_created(self, app, client):
"""Test that file is created on disk"""