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>
4.3 KiB
Data Model: Product Selection Landing Page
Feature: 002-product-list | Date: 2025-10-17
Overview
This feature reuses the existing Product entity from feature 001-build-an-application with a minor extension. No new entities are created.
Existing Entity: Product
Source: app/models/product.py (from feature 001)
Attributes (relevant to this feature):
| Attribute | Type | Required | Description |
|---|---|---|---|
product_id |
str | Yes | Unique identifier (e.g., "001-acme-app") |
name |
str | Yes | Display name for the product |
submission_url_slug |
str | Yes | URL segment for submission form |
status |
str | Yes | "active" or "archived" |
description |
str | No | Brief product description (optional) |
Storage: File-based YAML at data/products/{product-id}/config.yaml
Example:
product_id: "001-acme-app"
name: "Acme Application"
submission_url_slug: "acme-app"
status: "active"
description: "Enterprise resource planning system"
Model Extension
New Class Method: load_active()
Purpose: Load and return only active products with valid submission URLs, sorted for display.
Signature:
@classmethod
def load_active(cls) -> list[Product]:
"""Load all active products, sorted alphabetically by name then product_id."""
Returns: List of Product instances where:
status == 'active'submission_url_slugis present and valid- Sorted by:
(name.lower(), product_id)
Implementation Location: app/models/product.py
Usage Example:
from app.models.product import Product
# In route handler
products = Product.load_active()
# Returns sorted list of active products ready for display
Data Flow
Landing Page Load Sequence
- Request: User navigates to
/ - Route Handler:
app/routes/landing.py- Calls
Product.load_active()
- Calls
- Data Loading: Product model
- Reads all
data/products/*/config.yamlfiles - Filters:
status == 'active'ANDsubmission_url_slugexists - Sorts: By
(name.lower(), product_id)
- Reads all
- Response: Render template with product list
- Pass
productstotemplates/landing/index.html - Template loops over products, displays name/description/link
- Pass
Diagram
User → GET / → landing.py → Product.load_active() → [Product, Product, ...]
↓
Templates (Jinja2) → HTML Response → User
Validation Rules
Product Visibility (for landing page)
A product is visible on the landing page if and only if:
status == 'active'(FR-003)submission_url_slugis not None/empty (FR-015)
Products failing either condition are excluded from the list.
Sorting Rules (FR-009)
Products are sorted by:
- Primary:
name(case-insensitive alphabetical) - Secondary:
product_id(alphabetical, for ties)
Examples:
-
Input:
[{"name": "Zebra", "product_id": "001"}, {"name": "Apple", "product_id": "002"}] -
Output:
[{"name": "Apple", ...}, {"name": "Zebra", ...}] -
Input:
[{"name": "App", "product_id": "002"}, {"name": "App", "product_id": "001"}] -
Output:
[{"name": "App", "product_id": "001"}, {"name": "App", "product_id": "002"}]
No New Entities
This feature introduces no new entities. All data structures are reused from feature 001:
- Product entity (extended with
load_active()method only) - File-based YAML storage (unchanged)
- No database tables, no new data files
Testing Considerations
Test Data Requirements
For comprehensive testing, create products with:
- Various statuses: "active", "archived"
- With/without descriptions
- With/without valid
submission_url_slug - Duplicate names (to test secondary sort)
- Various name cases ("Apple", "apple", "APPLE")
Expected Behaviors
| Scenario | Expected Result |
|---|---|
Product with status: active |
Included in list |
Product with status: archived |
Excluded from list |
Product with missing submission_url_slug |
Excluded from list |
| Products with same name | Sorted by product_id |
| Empty product directory | Returns empty list |
Reference Tests: See tests/contract/test_landing_routes.py for data model validation tests.