Files
Reklamator/specs/002-product-list/research.md
T
gurixandClaude 0f71ba969f Add implementation plan for product selection landing page (Feature 002)
Completed planning phases 0 and 1 for simple landing page feature.

## Plan Overview:

**Approach**: Minimal addition to existing Flask app - reuse Product model,
add one route, one template. No new dependencies or complexity.

**Constitution Check**:  All 5 principles satisfied
- Specification-first development (spec.md complete)
- Test-first discipline (TDD workflow defined)
- Independent user stories (3 stories, all independently testable)
- Simplicity (reuses existing Flask/Jinja2/Product architecture)
- Documentation as code (all artifacts in specs/002-product-list/)

## Artifacts Created:

### Phase 0: Research (research.md)
- Reuses infrastructure from feature 001 (Flask, Jinja2, file storage)
- Single new decision: Product.load_active() method for filtering/sorting
- Performance analysis: <100ms for 100 products (well under 1s target)

### Phase 1: Design & Contracts
- **data-model.md**: Documents Product model extension (load_active method)
- **contracts/landing-page.yaml**: OpenAPI contract for GET / route
- **quickstart.md**: Developer implementation guide with:
  - Step-by-step implementation checklist
  - Code snippets for route, template, tests
  - TDD workflow (write tests → verify fail → implement → pass)
  - Manual verification checklist

### Agent Context
- Updated CLAUDE.md with feature technologies (no new tech added)

## Implementation Summary:

**New Files** (to be created):
- app/routes/landing.py - Landing page route handler
- app/templates/landing/index.html - Product list template
- tests/contract/test_landing_routes.py - Contract tests (6 scenarios)
- tests/integration/test_landing_flow.py - User journey test

**Modified Files**:
- app/models/product.py - Add load_active() class method
- app/__init__.py - Register landing blueprint

## Key Technical Decisions:

1. **Filtering**: status=='active' AND submission_url_slug exists
2. **Sorting**: Alphabetical by name (case-insensitive), then product_id
3. **Empty State**: "No products are currently accepting feedback" message
4. **XSS Prevention**: Jinja2 auto-escaping (no manual escaping needed)
5. **Performance**: File I/O sufficient (<1s for 100 products, no caching)

## Next Steps:

1. Run /speckit.tasks to generate tasks.md
2. Run /speckit.implement to execute TDD workflow
3. Verify all tests pass
4. Manual verification checklist
5. Create pull request

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-17 14:26:47 +02:00

3.8 KiB

Research: Product Selection Landing Page

Branch: 002-product-list | Date: 2025-10-17

This document addresses technical decisions for the product selection landing page feature. Most infrastructure decisions were resolved in feature 001-build-an-application and are reused here.

Existing Infrastructure (from 001-build-an-application)

The following technical decisions from feature 001 are reused without modification:

  • Flask 3.0+ with Jinja2: Server-side rendering, no JavaScript
  • File-based storage: Product configs in data/products/*/config.yaml
  • Product model: Existing app/models/product.py with load methods
  • Template patterns: Minimal HTML/CSS, Jinja2 auto-escaping for XSS prevention
  • Routing: Flask route decorators, blueprint organization
  • Testing: pytest + pytest-flask for contract and integration tests

Reference: See /home/markus/workspace/reklamator/specs/001-build-an-application/research.md for full details.


New Technical Decision: Product List Retrieval & Sorting

Decision: Extend Product model with load_active() class method

Rationale:

  • Centralizes "active products only" filtering logic
  • Enables reuse if other features need active product lists
  • Encapsulates sorting algorithm in one place
  • Follows existing Product model pattern (e.g., load_all(), load_by_id())

Implementation:

# app/models/product.py - add class method
@classmethod
def load_active(cls):
    """Load all active products, sorted alphabetically by name then product_id.

    Returns:
        list[Product]: Active products with valid submission_url_slug, sorted by:
                       1. name (case-insensitive alphabetical)
                       2. product_id (alphabetical) as tiebreaker

    Products with missing/invalid submission_url_slug are excluded.
    """
    all_products = cls.load_all()
    active = [p for p in all_products
              if p.status == 'active' and p.submission_url_slug]
    return sorted(active, key=lambda p: (p.name.lower(), p.product_id))

Sorting Algorithm:

  • Primary sort: Product name (case-insensitive) - ensures alphabetical display
  • Secondary sort: Product ID - provides stable ordering when names are identical
  • Uses Python's built-in sorted() with tuple key for multi-level sorting

Performance Analysis:

  • File I/O for 100 products: ~10-50ms (depends on disk speed)
  • In-memory sorting: <1ms for 100 items
  • Total expected latency: <100ms (well under 1-second SC-002 target)
  • No caching needed for MVP (file reads are sufficiently fast)

Filtering Logic:

  • status == 'active': Per FR-003, only show active products
  • submission_url_slug: Per FR-015, skip products with missing/invalid slugs
  • Combined with and operator: both conditions must be true

Alternatives Considered:

  1. Sort in route handler: Simpler but violates DRY if multiple routes need sorted product lists
  2. Database query with ORDER BY: Contradicts file-based architecture decision from 001
  3. Pre-sorted cache: Premature optimization - file reads are fast enough for 100 products
  4. Client-side sorting with JavaScript: Violates no-JavaScript constraint from spec

Edge Cases Handled:

  • No active products → Returns empty list (handled in template)
  • Missing submission_url_slug → Product excluded from list (per FR-015)
  • Identical product names → Sorted by product_id as tiebreaker
  • Case-insensitive sorting → "Apple" and "apple" sort together

Validation

All technical decisions align with:

  • FR-002: Retrieves from file-based storage
  • FR-003: Filters for active status
  • FR-009: Sorts alphabetically with tiebreaker
  • FR-015: Skips invalid submission_url_slug
  • SC-002: <1 second load time for 100 products

Next Phase: Proceed to Phase 1 (data-model.md, contracts, quickstart.md)