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>
9.6 KiB
Phase 1 Implementation Report: Timestamp-Based Slugs
Date: 2025-12-17 Developer: StarPunk Fullstack Developer Agent Phase: v1.5.0 Phase 1 - Timestamp-Based Slugs Status: ✅ Complete
Summary
Implemented timestamp-based slug generation per ADR-062, replacing the content-based slug algorithm with a simpler, privacy-preserving timestamp format. All tests pass (892 total).
Changes Implemented
1. New Function: generate_timestamp_slug()
File: /home/phil/Projects/starpunk/starpunk/slug_utils.py
Created new function that generates slugs in YYYYMMDDHHMMSS format with sequential collision handling:
def generate_timestamp_slug(
created_at: datetime = None,
existing_slugs: Set[str] = None
) -> str:
"""Generate a timestamp-based slug with collision handling.
Per ADR-062: Default format is YYYYMMDDHHMMSS with sequential
suffix (-1, -2, etc.) for collisions.
"""
Key Features:
- Base format:
20251216143052(14 characters) - First collision:
20251216143052-1 - Second collision:
20251216143052-2 - Defaults to UTC now if no timestamp provided
- Handles empty existing_slugs set gracefully
2. Updated Note Creation
File: /home/phil/Projects/starpunk/starpunk/notes.py (lines 228-231)
Before:
else:
# Generate base slug from content
base_slug = generate_slug(content, created_at)
# Make unique if collision
slug = make_slug_unique(base_slug, existing_slugs)
# Validate final slug (defensive check)
if not validate_slug(slug):
raise InvalidNoteDataError("slug", slug, f"Generated slug is invalid: {slug}")
After:
else:
# Generate timestamp-based slug (ADR-062)
from starpunk.slug_utils import generate_timestamp_slug
slug = generate_timestamp_slug(created_at, existing_slugs)
Simplification: Removed 8 lines of code, including:
- Content-based slug generation call
- Separate uniqueness check
- Defensive validation check (timestamp slugs are valid by construction)
3. Updated Tests
Added: tests/test_timestamp_slugs.py
New comprehensive test file with 18 tests covering:
- Basic timestamp format validation
- Collision handling with sequential suffixes
- Edge cases (midnight, end of day, leap year, single-digit padding)
- Integration with note creation
- Custom slug compatibility verification
Updated: tests/test_custom_slugs.py
- Added
TIMESTAMP_SLUG_PATTERNconstant for validation - Updated
test_create_note_without_custom_slugto use pattern matching - Updated
test_empty_slug_uses_auto_generationto verify timestamp format - Updated
test_whitespace_only_slug_uses_auto_generationto accept both old and new timestamp formats
Note: Whitespace custom slugs go through sanitize_slug() which uses the older YYYYMMDD-HHMMSS format. This is acceptable as it only affects invalid custom slugs, not default generation.
Updated: tests/test_notes.py
- Updated
test_create_generates_unique_slugto test timestamp collision handling with fixed timestamps
4. Preserved Legacy Code
File: /home/phil/Projects/starpunk/starpunk/utils.py
The old generate_slug() function remains unchanged for backward compatibility and potential future use. This follows the architect's guidance in the response document.
Testing Results
Test Summary
892 passed, 1 warning in ~6 minutes
Test Count Change:
- Previous: 874 tests
- New: 892 tests (+18 from new test_timestamp_slugs.py file)
Key Tests Verified
-
Timestamp Format:
- ✅ Format matches
YYYYMMDDHHMMSSexactly - ✅ No hyphen between date and time components
- ✅ 14 characters total
- ✅ Format matches
-
Collision Handling:
- ✅ Base slug gets no suffix:
20251216143052 - ✅ First collision gets
-1:20251216143052-1 - ✅ Second collision gets
-2:20251216143052-2 - ✅ Sequential suffixes work up to 10+
- ✅ Base slug gets no suffix:
-
Edge Cases:
- ✅ Midnight timestamp:
20250101000000 - ✅ End of day:
20251231235959 - ✅ Leap year:
20240229123045 - ✅ Single-digit padding:
20250105090503
- ✅ Midnight timestamp:
-
Integration:
- ✅ Creating note without custom slug generates timestamp
- ✅ Multiple notes at same second get sequential suffixes
- ✅ Custom slugs via
mp-slugstill work unchanged - ✅ Web UI custom slug field still works
-
Compatibility:
- ✅ No collision with reserved slugs (timestamp = numeric, reserved = alphabetic)
- ✅ Existing notes unaffected (no migration needed)
Acceptance Criteria
Per docs/projectplan/v1.5.0/RELEASE.md Phase 1:
- ✅ Default slugs use
YYYYMMDDHHMMSSformat - ✅ Collision handling uses
-1,-2suffix (sequential) - ✅ Custom slugs via
mp-slugwork unchanged - ✅ Custom slugs via web UI work unchanged
- ✅ Existing notes unaffected
- ✅ ADR-062 referenced in code comments (in function docstring)
All acceptance criteria met.
Code Quality
Lines of Code Impact
- Added: 47 lines (new function + docstring)
- Removed: 8 lines (simplified note creation logic)
- Net: +39 lines in production code
- Tests: +200 lines (comprehensive new test file)
Complexity Reduction
- Removed content extraction logic
- Removed word normalization
- Removed multiple fallback paths
- Removed defensive validation check
- Simplified collision handling (sequential vs random)
Maintainability
- Single responsibility: timestamp generation
- Clear, predictable behavior
- No edge cases for content (unicode, short text, special chars)
- Easier to debug (no randomness in collision handling)
Privacy & Security
Privacy Improvements
- ✅ Note content no longer visible in URLs
- ✅ Timestamps reveal only creation time, not content
- ✅ No accidental information leakage through slugs
Security Considerations
- ✅ Timestamp slugs cannot collide with reserved slugs (different character sets)
- ✅ Sequential suffixes are deterministic but not a security concern
- ✅ No user-controlled input in default slug generation
Performance
Generation Speed
- Before: Extract words → normalize → check uniqueness → add random suffix
- After: Format timestamp → check uniqueness → add sequential suffix
Improvement: Timestamp formatting is O(1) vs content parsing which is O(n) where n = content length.
Database Queries
- No change (uniqueness check still requires one query)
Documentation References
All implementation decisions based on:
docs/decisions/ADR-062-timestamp-based-slug-format.mddocs/design/v1.5.0/2025-12-16-architect-responses.mddocs/projectplan/v1.5.0/RELEASE.md
Known Limitations
Two Timestamp Formats in System
The system now has two timestamp formats:
- Default slugs:
YYYYMMDDHHMMSS(ADR-062, no hyphen) - Custom slug fallback:
YYYYMMDD-HHMMSS(old format, with hyphen)
This occurs when:
- User provides custom slug that fails normalization (e.g., emoji, whitespace)
- System falls back to timestamp via
sanitize_slug()
Impact: Minimal. Both formats are valid, sortable, and private. The difference only affects edge cases of invalid custom slugs.
Future Consideration: Could unify formats in v1.6.0 by updating sanitize_slug() to use new format.
No Reserved Slug Check for Timestamp Slugs
Per architect's decision (Q4 in responses), timestamp slugs skip reserved slug validation because:
- Timestamp slugs are purely numeric
- Reserved slugs are alphabetic
- Collision is impossible by construction
This is a simplification, not a limitation.
Migration Notes
Existing Data
- No database migration required
- Existing notes keep their content-based slugs
- All existing URLs remain valid
- Old and new slug formats coexist naturally
Rollback Plan
If rollback is needed:
- Revert changes to
notes.py(restore old logic) - Remove
generate_timestamp_slug()function - Remove
test_timestamp_slugs.pyfile - Restore old test assertions
Next Steps
Per task instructions:
- ✅ Create this implementation report
- ⏳ Commit changes (pending)
- ⏳ Report back to architect for review
- ⏸️ Do NOT proceed to Phase 2 until review complete
Files Changed
Production Code
/home/phil/Projects/starpunk/starpunk/slug_utils.py- Addedgenerate_timestamp_slug()/home/phil/Projects/starpunk/starpunk/notes.py- Updated default slug generation
Tests
/home/phil/Projects/starpunk/tests/test_timestamp_slugs.py- New file (18 tests)/home/phil/Projects/starpunk/tests/test_custom_slugs.py- Updated 4 tests/home/phil/Projects/starpunk/tests/test_notes.py- Updated 1 test
Documentation
/home/phil/Projects/starpunk/docs/design/v1.5.0/2025-12-17-phase-1-implementation-report.md- This file
Total files modified: 6
Developer Notes
What Went Well
- Clear specifications in ADR-062 and architect responses
- Sequential suffix logic is simpler than random suffix
- Pattern matching in tests makes assertions flexible
- Implementation was straightforward with no surprises
Challenges Encountered
- Test import error: Used
starpunk.dbinstead ofstarpunk.database(fixed) - Two timestamp formats: Discovered old format in
sanitize_slug()(documented) - Test runtime: Full suite takes ~6 minutes (acceptable for CI)
Code Review Points
- Verify timestamp format consistency is acceptable
- Confirm sequential suffix behavior meets requirements
- Check if
generate_slug()in utils.py should be deprecated - Consider future unification of timestamp formats
Implementation Status: ✅ Ready for Architect Review