# V1.1.0 Implementation Decisions - Architectural Guidance ## Overview This document provides definitive architectural decisions for all 29 questions raised during v1.1.0 implementation planning. Each decision is final and actionable. --- ## RSS Feed Fix Decisions ### Q1: No Bug Exists - Action Required? **Decision**: Add a regression test and close as "working as intended" **Rationale**: Since the RSS feed is already correctly ordered (newest first), we should document this as the intended behavior and prevent future regressions. **Implementation**: 1. Add test case: `test_feed_order_newest_first()` in `tests/test_feed.py` 2. Add comment above line 96 in `feed.py`: `# Notes are already DESC ordered from database` 3. Close the issue with note: "Verified feed order is correct (newest first)" ### Q2: Line 96 Loop - Keep As-Is? **Decision**: Keep the current implementation unchanged **Rationale**: The `for note in notes[:limit]:` loop is correct because notes are already sorted DESC by created_at from the database query. **Implementation**: No code change needed. Add clarifying comment if not already present. --- ## Migration System Redesign (ADR-033) ### Q3: INITIAL_SCHEMA_SQL Storage Location **Decision**: Store in `starpunk/database.py` as a module-level constant **Rationale**: Keeps schema definitions close to database initialization code. **Implementation**: ```python # In starpunk/database.py, after imports: INITIAL_SCHEMA_SQL = """ -- V1.0.0 Schema - DO NOT MODIFY -- All changes must go in migration files [... original schema from v1.0.0 ...] """ ``` ### Q4: Existing SCHEMA_SQL Variable **Decision**: Keep both with clear naming **Implementation**: 1. Rename current `SCHEMA_SQL` to `INITIAL_SCHEMA_SQL` 2. Add new variable `CURRENT_SCHEMA_SQL` that will be built from initial + migrations 3. Document the purpose of each in comments ### Q5: Modify init_db() Detection **Decision**: Yes, modify `init_db()` to detect fresh install **Implementation**: ```python def init_db(app=None): """Initialize database with proper schema""" conn = get_db_connection() # Check if this is a fresh install cursor = conn.execute("SELECT name FROM sqlite_master WHERE type='table' AND name='migrations'") is_fresh = cursor.fetchone() is None if is_fresh: # Fresh install: use initial schema conn.executescript(INITIAL_SCHEMA_SQL) conn.execute("INSERT INTO migrations (version, applied_at) VALUES ('initial', CURRENT_TIMESTAMP)") # Apply any pending migrations apply_pending_migrations(conn) ``` ### Q6: Users Upgrading from v1.0.1 **Decision**: Automatic migration on application start **Rationale**: Zero-downtime upgrade with automatic schema updates. **Implementation**: 1. Application detects current version via migrations table 2. Applies only new migrations (005+) 3. No manual intervention required 4. Add startup log: "Database migrated to v1.1.0" ### Q7: Existing Migrations 001-004 **Decision**: Leave existing migrations unchanged **Rationale**: These are historical records and changing them would break existing deployments. **Implementation**: Do not modify files. They remain for upgrade path from older versions. ### Q8: Testing Both Paths **Decision**: Create two separate test scenarios **Implementation**: ```python # tests/test_migrations.py def test_fresh_install(): """Test database creation from scratch""" # Start with no database # Run init_db() # Verify all tables exist with correct schema def test_upgrade_from_v1_0_1(): """Test upgrade path""" # Create database with v1.0.1 schema # Add sample data # Run init_db() # Verify migrations applied # Verify data preserved ``` --- ## Full-Text Search (ADR-034) ### Q9: Title Source **Decision**: Extract title from first line of markdown content **Rationale**: Notes table doesn't have a title column. Follow existing pattern where title is derived from content. **Implementation**: ```sql -- Use SQL to extract first line as title substr(content, 1, instr(content || char(10), char(10)) - 1) as title ``` ### Q10: Trigger Implementation **Decision**: Use SQL expression to extract title, not a custom function **Rationale**: Simpler, no UDF required, portable across SQLite versions. **Implementation**: ```sql CREATE TRIGGER notes_fts_insert AFTER INSERT ON notes BEGIN INSERT INTO notes_fts (rowid, slug, title, content) SELECT NEW.id, NEW.slug, substr(content, 1, min(60, ifnull(nullif(instr(content, char(10)), 0) - 1, length(content)))), content FROM note_files WHERE file_path = NEW.file_path; END; ``` ### Q11: Migration 005 Scope **Decision**: Yes, create everything in one migration **Rationale**: Atomic operation ensures consistency. **Implementation in `migrations/005_add_full_text_search.sql`: 1. Create FTS5 virtual table 2. Create all three triggers (INSERT, UPDATE, DELETE) 3. Build initial index from existing notes 4. All in single transaction ### Q12: Search Endpoint URL **Decision**: `/api/search` **Rationale**: Consistent with existing API pattern, RESTful design. **Implementation**: Register route in `app.py` or API blueprint. ### Q13: Template Files Needing Modification **Decision**: Modify `base.html` for search box, create new `search.html` for results **Implementation**: - `templates/base.html`: Add search form in navigation - `templates/search.html`: New template for search results page - `templates/partials/search-result.html`: Result item component ### Q14: Search Filtering by Authentication **Decision**: Yes, filter by published status **Implementation**: ```python if not is_authenticated(): query += " AND published = 1" ``` ### Q15: FTS5 Unavailable Handling **Decision**: Disable search gracefully with warning **Rationale**: Better UX than failing to start. **Implementation**: ```python def check_fts5_support(): try: conn.execute("CREATE VIRTUAL TABLE test_fts USING fts5(content)") conn.execute("DROP TABLE test_fts") return True except sqlite3.OperationalError: app.logger.warning("FTS5 not available - search disabled") return False ``` --- ## Custom Slugs (ADR-035) ### Q16: mp-slug Extraction Location **Decision**: In `handle_create()` function after properties normalization **Implementation**: ```python def handle_create(request: Request) -> dict: properties = normalize_properties(request) # Extract custom slug if provided custom_slug = properties.get('mp-slug', [None])[0] # Continue with note creation... ``` ### Q17: Slug Validation Functions Location **Decision**: Create new module `starpunk/slug_utils.py` **Rationale**: Slug handling is complex enough to warrant its own module. **Implementation**: New file with functions: `validate_slug()`, `sanitize_slug()`, `ensure_unique_slug()` ### Q18: RESERVED_SLUGS Storage **Decision**: Module constant in `slug_utils.py` **Implementation**: ```python # starpunk/slug_utils.py RESERVED_SLUGS = frozenset([ 'api', 'admin', 'auth', 'feed', 'static', 'login', 'logout', 'settings', 'micropub' ]) ``` ### Q19: Conflict Resolution Strategy **Decision**: Use sequential numbers (-2, -3, etc.) **Rationale**: Predictable, easier to debug, standard practice. **Implementation**: ```python def make_unique_slug(base_slug: str, max_attempts: int = 99) -> str: for i in range(2, max_attempts + 2): candidate = f"{base_slug}-{i}" if not slug_exists(candidate): return candidate raise ValueError(f"Could not create unique slug after {max_attempts} attempts") ``` ### Q20: Hierarchical Slugs Support **Decision**: No, defer to v1.2.0 **Rationale**: Adds routing complexity, not essential for v1.1.0. **Implementation**: Validate slugs don't contain `/`. Add to roadmap for v1.2.0. ### Q21: Existing Slug Field Sufficient? **Decision**: Yes, current schema is sufficient **Rationale**: `slug TEXT UNIQUE NOT NULL` already enforces uniqueness. **Implementation**: No migration needed. ### Q22: Micropub Error Format **Decision**: Follow Micropub spec exactly **Implementation**: ```python return jsonify({ "error": "invalid_request", "error_description": f"Invalid slug format: {reason}" }), 400 ``` --- ## General Implementation Decisions ### Q23: Implementation Sequence **Decision**: Follow sequence but document design for all components first **Rationale**: Design clarity prevents rework. **Implementation**: 1. Day 1: Document all component designs 2. Days 2-4: Implement in sequence 3. Day 5: Integration testing ### Q24: Branching Strategy **Decision**: Single feature branch: `feature/v1.1.0` **Rationale**: Components are interdependent, easier to test together. **Implementation**: ```bash git checkout -b feature/v1.1.0 # All work happens here # PR to main when complete ``` ### Q25: Test Writing Strategy **Decision**: Write tests immediately after each component **Rationale**: Ensures each component works before moving on. **Implementation**: 1. Implement feature 2. Write tests 3. Verify tests pass 4. Move to next component ### Q26: Version Bump Timing **Decision**: Bump version in final commit before merge **Rationale**: Version represents released code, not development code. **Implementation**: 1. Complete all features 2. Update `__version__` to "1.1.0" 3. Update CHANGELOG.md 4. Commit: "chore: bump version to 1.1.0" ### Q27: New Migration Numbering **Decision**: Continue sequential: 005, 006, etc. **Implementation**: - `005_add_full_text_search.sql` - `006_add_custom_slug_support.sql` (if needed) ### Q28: Progress Documentation **Decision**: Daily updates in `/docs/reports/v1.1.0-progress.md` **Implementation**: ```markdown # V1.1.0 Implementation Progress ## Day 1 - [Date] ### Completed - [ ] Task 1 - [ ] Task 2 ### Blockers - None ### Notes - Implementation detail... ``` ### Q29: Backwards Compatibility Verification **Decision**: Test suite with v1.0.1 data **Implementation**: 1. Create test database with v1.0.1 schema 2. Add sample data 3. Run upgrade 4. Verify all existing features work 5. Verify API compatibility --- ## Developer Observations - Responses ### Migration System Complexity **Response**: Allocate extra 2 hours. Better to overdeliver than rush. ### FTS5 Title Extraction **Response**: Correct - index full content only in v1.1.0. Title extraction is display concern. ### Search UI Template Review **Response**: Keep minimal - search box in nav, simple results page. No JavaScript. ### Testing Time Optimistic **Response**: Add 2 hours buffer for testing. Quality over speed. ### Slug Validation Security **Response**: Yes, add fuzzing tests for slug validation. Security is non-negotiable. ### Performance Benchmarking **Response**: Defer to v1.2.0. Focus on correctness in v1.1.0. --- ## Implementation Checklist Order 1. **Day 1 - Design & Setup** - [ ] Create feature branch - [ ] Write component designs - [ ] Set up test fixtures 2. **Day 2 - Migration System** - [ ] Implement INITIAL_SCHEMA_SQL - [ ] Refactor init_db() - [ ] Write migration tests - [ ] Test both paths 3. **Day 3 - Full-Text Search** - [ ] Create migration 005 - [ ] Implement search endpoint - [ ] Add search UI - [ ] Write search tests 4. **Day 4 - Custom Slugs** - [ ] Create slug_utils.py - [ ] Modify micropub.py - [ ] Add validation - [ ] Write slug tests 5. **Day 5 - Integration** - [ ] Full system testing - [ ] Update documentation - [ ] Bump version - [ ] Create PR --- ## Risk Mitigations 1. **Database Corruption**: Test migrations on copy first 2. **Search Performance**: Limit results to 100 maximum 3. **Slug Conflicts**: Clear error messages for users 4. **Upgrade Failures**: Provide rollback instructions 5. **FTS5 Missing**: Graceful degradation --- ## Success Criteria - [ ] All existing tests pass - [ ] New tests for all features - [ ] No breaking changes to API - [ ] Documentation updated - [ ] Performance acceptable (<100ms responses) - [ ] Security review passed - [ ] Backwards compatible with v1.0.1 data --- ## Notes - This document represents final architectural decisions - Any deviations require ADR and approval - Focus on simplicity and correctness - When in doubt, defer complexity to v1.2.0