diff --git a/CLAUDE.md b/CLAUDE.md
new file mode 100644
index 0000000..60829bc
--- /dev/null
+++ b/CLAUDE.md
@@ -0,0 +1,25 @@
+# reklamator Development Guidelines
+
+Auto-generated from all feature plans. Last updated: 2025-10-15
+
+## Active Technologies
+- Python 3.11+ + Flask (web framework), no CSS frameworks, no JavaScript libraries (001-build-an-application)
+
+## Project Structure
+```
+backend/
+frontend/
+tests/
+```
+
+## Commands
+cd src [ONLY COMMANDS FOR ACTIVE TECHNOLOGIES][ONLY COMMANDS FOR ACTIVE TECHNOLOGIES] pytest [ONLY COMMANDS FOR ACTIVE TECHNOLOGIES][ONLY COMMANDS FOR ACTIVE TECHNOLOGIES] ruff check .
+
+## Code Style
+Python 3.11+: Follow standard conventions
+
+## Recent Changes
+- 001-build-an-application: Added Python 3.11+ + Flask (web framework), no CSS frameworks, no JavaScript libraries
+
+
+
\ No newline at end of file
diff --git a/specs/001-build-an-application/contracts/admin_api.md b/specs/001-build-an-application/contracts/admin_api.md
new file mode 100644
index 0000000..7429f96
--- /dev/null
+++ b/specs/001-build-an-application/contracts/admin_api.md
@@ -0,0 +1,604 @@
+# Admin API Contract
+
+**Scope**: Product and user management endpoints (User Story P4)
+**Authentication**: Required (admin role only)
+
+---
+
+## GET /admin/products
+
+Display list of all registered products.
+
+### Request
+
+**Authentication**: Required (admin role)
+
+### Response
+
+**Success (200 OK)**:
+```html
+Content-Type: text/html
+
+
+
+
Product Management
+
+ Product Management
+
+ + Create New Product
+
+
+
+
+ ID
+ Name
+ Status
+ Submission URL
+ Target Language
+ Feedback Count
+ Assigned Owners
+ Actions
+
+
+
+
+ 001-acme-app
+ Acme Mobile App
+ Active
+ /submit/acme-app
+ English (en)
+ 127
+ 2 owners
+
+ Edit |
+ Archive
+
+
+
+
+
+
+
+```
+
+**Error (403 Forbidden)**: User is not an administrator
+```html
+Content-Type: text/html
+
+
+
+
+ Access Denied
+ Administrator privileges required.
+
+
+```
+
+### Functional Requirements Covered
+- FR-045: List all products
+- FR-054: Display product statistics
+
+---
+
+## GET /admin/products/new
+
+Display form to create a new product.
+
+### Request
+
+**Authentication**: Required (admin role)
+
+### Response
+
+**Success (200 OK)**:
+```html
+Content-Type: text/html
+
+
+
+
+ Create New Product
+
+
+
+```
+
+### Functional Requirements Covered
+- FR-045: Form to register new products
+- FR-047: Set preferred target language
+- FR-048: Assign product owners
+
+---
+
+## POST /admin/products
+
+Create a new product.
+
+### Request
+
+**Authentication**: Required (admin role)
+
+**Form Data**:
+- `id` (string, required): Unique product identifier (URL-safe, lowercase, hyphens allowed)
+- `name` (string, required): Display name (1-100 characters)
+- `description` (string, optional): Description (max 500 characters)
+- `target_language` (string, required): ISO 639-1 language code
+- `submission_url_slug` (string, required): URL-safe slug (unique)
+- `assigned_owner_ids` (string[], required): At least one product owner ID
+
+### Response
+
+**Success (302 Redirect)**:
+```http
+HTTP/1.1 302 Found
+Location: /admin/products
+```
+
+**Error (400 Bad Request)**: Validation failure
+```html
+Content-Type: text/html
+
+
+
+
+ Validation Error
+
+ Product ID must be unique
+ Product ID must be URL-safe (lowercase, hyphens only)
+ Submission URL slug must be unique
+ At least one product owner must be assigned
+ Target language must be valid ISO 639-1 code
+
+
+
+```
+
+### Side Effects
+
+1. **File System**:
+ - Creates `data/products/{product_id}/`
+ - Writes `data/products/{product_id}/config.yaml`
+ - Creates `data/products/{product_id}/feedback/` directory
+
+2. **Config File** (`config.yaml`):
+```yaml
+id: "001-acme-app"
+name: "Acme Mobile App"
+description: "Customer feedback for Acme's flagship mobile application"
+target_language: "en"
+submission_url_slug: "acme-app"
+created_date: "2025-10-15"
+status: "active"
+assigned_owner_ids:
+ - "owner-001"
+ - "owner-002"
+statistics:
+ total_feedback_count: 0
+ last_submission: null
+```
+
+### Functional Requirements Covered
+- FR-045: Register new products
+- FR-046: Unique product identifier
+- FR-047: Set target language
+- FR-048: Assign product owners
+- FR-049: Generate unique submission URL
+
+---
+
+## GET /admin/products/{product_id}/edit
+
+Display form to edit an existing product.
+
+### Request
+
+**Authentication**: Required (admin role)
+
+**Path Parameters**:
+- `product_id` (string, required): Product identifier
+
+### Response
+
+**Success (200 OK)**: Same form as create, pre-populated with existing values
+
+**Error (404 Not Found)**: Product does not exist
+
+### Functional Requirements Covered
+- FR-051: Update product details
+
+---
+
+## POST /admin/products/{product_id}
+
+Update an existing product.
+
+### Request
+
+**Authentication**: Required (admin role)
+
+**Path Parameters**:
+- `product_id` (string, required): Product identifier
+
+**Form Data**: Same as POST /admin/products (except `id` is immutable)
+
+### Response
+
+**Success (302 Redirect)**:
+```http
+HTTP/1.1 302 Found
+Location: /admin/products
+```
+
+**Error (400 Bad Request)**: Validation failure
+**Error (404 Not Found)**: Product does not exist
+
+### Side Effects
+
+- Updates `data/products/{product_id}/config.yaml`
+- Product `id` cannot be changed (immutable)
+- Changing `target_language` affects future feedback translations only (FR-048)
+
+### Functional Requirements Covered
+- FR-048: Update product owner assignments
+- FR-051: Update product details
+
+---
+
+## POST /admin/products/{product_id}/archive
+
+Archive a product (stop accepting new feedback).
+
+### Request
+
+**Authentication**: Required (admin role)
+
+**Path Parameters**:
+- `product_id` (string, required): Product identifier
+
+### Response
+
+**Success (302 Redirect)**:
+```http
+HTTP/1.1 302 Found
+Location: /admin/products
+```
+
+**Error (404 Not Found)**: Product does not exist
+
+### Side Effects
+
+- Updates `data/products/{product_id}/config.yaml`: Sets `status: "archived"`
+- Submission form (GET /submit/{product_slug}) returns 404 for archived products (FR-053)
+- Historical feedback preserved (FR-052)
+
+### Functional Requirements Covered
+- FR-052: Archive products without deleting feedback
+- FR-053: Prevent new submissions to archived products
+
+---
+
+## POST /admin/products/{product_id}/unarchive
+
+Reactivate an archived product.
+
+### Request
+
+**Authentication**: Required (admin role)
+
+**Path Parameters**:
+- `product_id` (string, required): Product identifier
+
+### Response
+
+**Success (302 Redirect)**:
+```http
+HTTP/1.1 302 Found
+Location: /admin/products
+```
+
+### Side Effects
+
+- Updates `data/products/{product_id}/config.yaml`: Sets `status: "active"`
+- Submission form becomes available again
+
+### Functional Requirements Covered
+- Allow reversing archive operation (not explicitly in FR but useful)
+
+---
+
+## GET /admin/users
+
+Display list of all users (product owners and admins).
+
+### Request
+
+**Authentication**: Required (admin role)
+
+### Response
+
+**Success (200 OK)**:
+```html
+Content-Type: text/html
+
+
+
+
+ User Management
+
+ + Create New User
+
+
+
+
+ ID
+ Email
+ Name
+ Role
+ Assigned Products
+ Last Login
+ Actions
+
+
+
+
+ owner-001
+ jane.smith@example.com
+ Jane Smith
+ Product Owner
+ 2 products
+ 2025-10-15 09:23
+
+ Edit |
+ Delete
+
+
+
+
+
+
+
+```
+
+### Functional Requirements Covered
+- User management interface (implied by FR-048: assigning owners)
+
+---
+
+## GET /admin/users/new
+
+Display form to create a new user.
+
+### Request
+
+**Authentication**: Required (admin role)
+
+### Response
+
+**Success (200 OK)**:
+```html
+Content-Type: text/html
+
+
+
+
+ Create New User
+
+ Email:
+
+
+
+ Name:
+
+
+
+ Password:
+
+
+
+ Role:
+
+ Product Owner
+ Administrator
+
+
+
+ Create User
+
+
+
+```
+
+---
+
+## POST /admin/users
+
+Create a new user.
+
+### Request
+
+**Authentication**: Required (admin role)
+
+**Form Data**:
+- `email` (string, required): Valid email address (unique)
+- `name` (string, required): Display name (1-100 characters)
+- `password` (string, required): Password (min 8 characters)
+- `role` (string, required): "product_owner" or "admin"
+
+### Response
+
+**Success (302 Redirect)**:
+```http
+HTTP/1.1 302 Found
+Location: /admin/users
+```
+
+**Error (400 Bad Request)**: Validation failure
+```html
+Content-Type: text/html
+
+
+
+
+ Validation Error
+
+ Email must be unique
+ Password must be at least 8 characters
+ Invalid role specified
+
+
+
+```
+
+### Side Effects
+
+- Appends new user to `data/users.yaml`
+- Password hashed with bcrypt (cost factor 12) before storage (FR-063)
+- Generates unique user ID (e.g., "owner-001", "admin-002")
+
+### Functional Requirements Covered
+- FR-063: Secure password storage (bcrypt)
+- User creation for product owner assignment
+
+---
+
+## GET /admin/users/{user_id}/edit
+
+Display form to edit an existing user.
+
+### Request
+
+**Authentication**: Required (admin role)
+
+**Path Parameters**:
+- `user_id` (string, required): User identifier
+
+### Response
+
+**Success (200 OK)**: Same form as create, pre-populated (except password field empty)
+
+**Error (404 Not Found)**: User does not exist
+
+---
+
+## POST /admin/users/{user_id}
+
+Update an existing user.
+
+### Request
+
+**Authentication**: Required (admin role)
+
+**Path Parameters**:
+- `user_id` (string, required): User identifier
+
+**Form Data**:
+- `email` (string, required): Valid email address
+- `name` (string, required): Display name
+- `password` (string, optional): New password (if changing)
+- `role` (string, required): "product_owner" or "admin"
+
+### Response
+
+**Success (302 Redirect)**:
+```http
+HTTP/1.1 302 Found
+Location: /admin/users
+```
+
+### Side Effects
+
+- Updates user entry in `data/users.yaml`
+- If password provided, re-hash with bcrypt
+- Email and role can be updated
+
+---
+
+## POST /admin/users/{user_id}/delete
+
+Delete a user.
+
+### Request
+
+**Authentication**: Required (admin role)
+
+**Path Parameters**:
+- `user_id` (string, required): User identifier
+
+### Response
+
+**Success (302 Redirect)**:
+```http
+HTTP/1.1 302 Found
+Location: /admin/users
+```
+
+**Error (400 Bad Request)**: Cannot delete self
+```html
+Content-Type: text/html
+
+
+
+
+ Cannot Delete
+ You cannot delete your own account.
+
+
+```
+
+### Side Effects
+
+- Removes user from `data/users.yaml`
+- User automatically unassigned from all products
+- Historical feedback metadata unchanged (no user tracking in feedback)
+
+---
+
+## Access Control
+
+All admin endpoints enforce:
+1. User must be authenticated (session cookie)
+2. User role must be "admin"
+3. Otherwise: 403 Forbidden response
+
+### Functional Requirements Covered
+- FR-045: Admin can register products
+- FR-048: Admin can assign product owners
+- FR-051: Admin can update products
+- FR-052: Admin can archive products
diff --git a/specs/001-build-an-application/contracts/dashboard_api.md b/specs/001-build-an-application/contracts/dashboard_api.md
new file mode 100644
index 0000000..5bde73d
--- /dev/null
+++ b/specs/001-build-an-application/contracts/dashboard_api.md
@@ -0,0 +1,420 @@
+# Dashboard API Contract
+
+**Scope**: Product owner dashboard endpoints (User Story P3)
+**Authentication**: Required (session-based via Flask-Login)
+
+---
+
+## GET /login
+
+Display login form for product owners and administrators.
+
+### Request
+
+**Headers**: None required
+
+**Query Parameters**:
+- `next` (string, optional): Redirect URL after successful login
+
+### Response
+
+**Success (200 OK)**:
+```html
+Content-Type: text/html
+
+
+
+ Login - Reklamator
+
+ Login
+
+
+
+ Login
+
+
+
+```
+
+**Already Authenticated (302 Redirect)**: Redirect to `/dashboard`
+
+---
+
+## POST /login
+
+Authenticate product owner or administrator.
+
+### Request
+
+**Headers**:
+- `Content-Type: application/x-www-form-urlencoded`
+
+**Form Data**:
+- `email` (string, required): User email
+- `password` (string, required): User password
+
+### Response
+
+**Success (302 Redirect)**:
+```http
+HTTP/1.1 302 Found
+Location: /dashboard
+Set-Cookie: session=...; HttpOnly; Secure; SameSite=Lax
+```
+
+**Error (401 Unauthorized)**:
+```html
+Content-Type: text/html
+
+
+
+
+ Login Failed
+ Invalid email or password.
+
+
+```
+
+### Functional Requirements Covered
+- FR-056: Authentication required for dashboard
+- FR-063: Password verification against bcrypt hash
+
+---
+
+## GET /logout
+
+Log out current user.
+
+### Request
+
+**Authentication**: Required (session cookie)
+
+### Response
+
+**Success (302 Redirect)**:
+```http
+HTTP/1.1 302 Found
+Location: /login
+Set-Cookie: session=deleted; expires=Thu, 01 Jan 1970 00:00:00 GMT
+```
+
+---
+
+## GET /dashboard
+
+Display product owner dashboard with feedback list.
+
+### Request
+
+**Authentication**: Required (session cookie)
+
+**Query Parameters**:
+- `page` (integer, optional, default=1): Page number for pagination
+- `category` (string, optional): Filter by category (idea/feature_request/bug/complaint)
+- `language` (string, optional): Filter by original language (ISO 639-1 code)
+- `status` (string, optional): Filter by status (analyzed/reviewed/in_progress/resolved/rejected)
+- `date_from` (string, optional): Filter by date range start (ISO 8601 date)
+- `date_to` (string, optional): Filter by date range end (ISO 8601 date)
+- `search` (string, optional): Keyword search across text/translation/summary
+
+### Response
+
+**Success (200 OK)**:
+```html
+Content-Type: text/html
+
+
+
+ Feedback Dashboard
+
+ Feedback Dashboard
+
+
+
+ Acme Mobile App (127 items)
+ Beta Service (43 items)
+
+
+
+
+
+ All Categories
+ Ideas
+ Feature Requests
+ Bugs
+ Complaints
+
+
+ All Statuses
+ Analyzed
+ Reviewed
+ In Progress
+ Resolved
+ Rejected
+
+
+ Filter
+
+
+
+
+
+
+ ID
+ Date
+ Category
+ Original Lang
+ Summary
+ Status
+ Attachments
+
+
+
+
+ a3f2c1d5
+ 2025-10-15 14:32
+ Bug
+ DE
+ User reports app crashes when uploading large files...
+ Reviewed
+ 2 files
+
+
+
+
+
+
+
+
+
+```
+
+**Error (401 Unauthorized)**: Not authenticated
+```http
+HTTP/1.1 302 Found
+Location: /login?next=/dashboard
+```
+
+**Error (403 Forbidden)**: User has no assigned products
+```html
+Content-Type: text/html
+
+
+
+
+ No Access
+ You are not assigned to any products.
+
+
+```
+
+### Behavior
+
+- Display only feedback for products assigned to current user (FR-033)
+- Admin users see all products
+- Default sort: newest first (FR-041)
+- Pagination: 50 items per page (SC-008: <3s for 1000 items)
+- Filters preserved in URL for sharing/bookmarking
+
+### Functional Requirements Covered
+- FR-032: Authenticated dashboard access
+- FR-033: Product owner access control
+- FR-034: Display all analysis results
+- FR-036-FR-041: Filtering and searching
+- FR-041: Reverse chronological order
+
+---
+
+## GET /feedback/{feedback_id}
+
+Display detailed view of a single feedback item.
+
+### Request
+
+**Authentication**: Required (session cookie)
+
+**Path Parameters**:
+- `feedback_id` (UUID, required): Feedback identifier
+
+### Response
+
+**Success (200 OK)**:
+```html
+Content-Type: text/html
+
+
+
+ Feedback Detail - a3f2c1d5
+
+ Feedback Detail
+
+
+
+ AI Analysis Summary
+ User reports that the app crashes when uploading large files. This appears to be a bug affecting the file upload module, preventing users from submitting documents over 5MB.
+
+ Original Text (German)
+ Die App stürzt ab, wenn ich versuche, große Dateien hochzuladen. Jedes Mal wenn ich eine PDF über 5MB hochlade, friert die App ein und schließt sich.
+
+ Translation (English)
+ The app crashes when I try to upload large files. Every time I upload a PDF over 5MB, the app freezes and closes.
+
+ Attachments
+
+
+
+```
+
+**Error (401 Unauthorized)**: Not authenticated
+```http
+HTTP/1.1 302 Found
+Location: /login?next=/feedback/{feedback_id}
+```
+
+**Error (403 Forbidden)**: User not authorized for this product
+```html
+Content-Type: text/html
+
+
+
+
+ Access Denied
+ You do not have permission to view this feedback.
+
+
+```
+
+**Error (404 Not Found)**: Feedback does not exist
+```html
+Content-Type: text/html
+
+
+
+
+ Feedback Not Found
+
+
+```
+
+### Functional Requirements Covered
+- FR-034: Display complete feedback details
+- FR-035: Links to download attachments
+- FR-042: Status indicators
+- FR-044: File attachments with icons/thumbnails
+
+---
+
+## POST /feedback/{feedback_id}/status
+
+Update the status of a feedback item.
+
+### Request
+
+**Authentication**: Required (session cookie)
+
+**Path Parameters**:
+- `feedback_id` (UUID, required): Feedback identifier
+
+**Form Data**:
+- `status` (string, required): New status (analyzed/reviewed/in_progress/resolved/rejected)
+
+### Response
+
+**Success (302 Redirect)**:
+```http
+HTTP/1.1 302 Found
+Location: /feedback/{feedback_id}
+```
+
+**Error (403 Forbidden)**: User not authorized for this product
+**Error (404 Not Found)**: Feedback does not exist
+
+### Side Effects
+
+- Updates `metadata.yaml`: `status` field
+- Preserves timestamp of status change
+
+### Functional Requirements Covered
+- FR-042: Mark feedback with status indicators
+- FR-043: Preserve status when filtering
+
+---
+
+## GET /feedback/{feedback_id}/attachment/{filename}
+
+Download or view an attached file.
+
+### Request
+
+**Authentication**: Required (session cookie)
+
+**Path Parameters**:
+- `feedback_id` (UUID, required): Feedback identifier
+- `filename` (string, required): Sanitized filename
+
+### Response
+
+**Success (200 OK)**: Image file
+```http
+HTTP/1.1 200 OK
+Content-Type: image/png
+Content-Disposition: inline; filename="screenshot.png"
+Content-Length: 245678
+
+[binary image data]
+```
+
+**Success (200 OK)**: Document file
+```http
+HTTP/1.1 200 OK
+Content-Type: application/pdf
+Content-Disposition: attachment; filename="report.pdf"
+Content-Length: 1234567
+
+[binary document data]
+```
+
+**Error (403 Forbidden)**: User not authorized for this product
+**Error (404 Not Found)**: File does not exist
+
+### Security
+
+- Files served via Flask route (not direct filesystem access per FR-058)
+- Access control enforced: User must have access to parent product
+- Path traversal prevention: Filename sanitized
+- MIME type from stored metadata (not client-provided)
+
+### Functional Requirements Covered
+- FR-035: Provide links to download/view attachments
+- FR-058: Prevent unauthorized file access
+
+---
+
+## Rate Limiting
+
+Dashboard endpoints are NOT rate limited (authenticated users only).
diff --git a/specs/001-build-an-application/contracts/submission_api.md b/specs/001-build-an-application/contracts/submission_api.md
new file mode 100644
index 0000000..903002d
--- /dev/null
+++ b/specs/001-build-an-application/contracts/submission_api.md
@@ -0,0 +1,219 @@
+# Submission API Contract
+
+**Scope**: Anonymous feedback submission endpoints (User Story P1)
+**Authentication**: None (anonymous access)
+
+---
+
+## GET /submit/{product_slug}
+
+Display the feedback submission form for a specific product.
+
+### Request
+
+**Path Parameters**:
+- `product_slug` (string, required): Product's URL-safe identifier
+
+**Headers**: None required
+
+**Query Parameters**: None
+
+### Response
+
+**Success (200 OK)**:
+```html
+Content-Type: text/html
+
+
+
+ Submit Feedback - {Product Name}
+
+ Submit Feedback for {Product Name}
+
+
+
+ Submit Feedback
+
+
+
+```
+
+**Error (404 Not Found)**: Product does not exist or is archived
+```html
+Content-Type: text/html
+
+
+
+
+ Product Not Found
+ The product you're looking for does not exist or is no longer accepting feedback.
+
+
+```
+
+### Functional Requirements Covered
+- FR-001: Public, unauthenticated submission form
+- FR-002: Text input up to 10,000 characters
+- FR-004: Up to 3 file attachments
+
+---
+
+## POST /submit/{product_slug}
+
+Submit anonymous feedback for a specific product.
+
+### Request
+
+**Path Parameters**:
+- `product_slug` (string, required): Product's URL-safe identifier
+
+**Headers**:
+- `Content-Type: multipart/form-data`
+
+**Form Data**:
+- `feedback_text` (string, optional): Feedback text (0-10,000 characters)
+- `attachments` (file[], optional): Up to 3 files, max 10MB each
+
+**Example**:
+```http
+POST /submit/acme-app HTTP/1.1
+Content-Type: multipart/form-data; boundary=----WebKitFormBoundary
+
+------WebKitFormBoundary
+Content-Disposition: form-data; name="feedback_text"
+
+The app crashes when uploading files over 5MB.
+------WebKitFormBoundary
+Content-Disposition: form-data; name="attachments"; filename="screenshot.png"
+Content-Type: image/png
+
+[binary data]
+------WebKitFormBoundary--
+```
+
+### Response
+
+**Success (200 OK)**:
+```html
+Content-Type: text/html
+
+
+
+
+ Thank You!
+ Your feedback has been submitted successfully.
+ Your feedback ID: {feedback_id}
+
+
+```
+
+**Error (400 Bad Request)**: Validation failure
+```html
+Content-Type: text/html
+
+
+
+
+ Submission Error
+
+ Feedback must contain text or at least one attachment
+ Maximum 3 attachments allowed
+ Text cannot exceed 10,000 characters
+ File size cannot exceed 10MB per file
+ Unsupported file type: {filename}
+
+
+
+```
+
+**Error (413 Payload Too Large)**: File size exceeds limit
+```html
+Content-Type: text/html
+HTTP/1.1 413 Payload Too Large
+
+
+
+
+ File Too Large
+ One or more files exceed the 10MB limit.
+
+
+```
+
+**Error (429 Too Many Requests)**: Rate limit exceeded
+```html
+Content-Type: text/html
+X-RateLimit-Limit: 10
+X-RateLimit-Remaining: 0
+X-RateLimit-Reset: 1697456789
+
+
+
+
+ Too Many Submissions
+ You have exceeded the submission limit of 10 per hour. Please try again later.
+
+
+```
+
+**Error (451 Unavailable For Legal Reasons)**: Malware detected
+```html
+Content-Type: text/html
+
+
+
+
+ Security Error
+ One or more files failed security scanning. Please ensure your files are safe and try again.
+
+
+```
+
+### Validation Rules
+
+1. **Text Validation** (FR-002, FR-003):
+ - Length: 0-10,000 characters
+ - Encoding: UTF-8, any language accepted
+ - Empty allowed if attachments present
+
+2. **Attachment Validation** (FR-004, FR-005, FR-006):
+ - Count: 0-3 files
+ - Size: Max 10MB per file
+ - Types: PDF, DOCX, TXT, JPG, PNG, GIF, WebP
+ - MIME type validation (server-side)
+
+3. **Submission Validation** (FR-011, FR-012):
+ - Must have text OR attachments (not both empty)
+
+4. **Security** (FR-059, FR-060):
+ - ClamAV virus scan before storage
+ - Filename sanitization (remove path traversal)
+ - MIME type validation
+
+5. **Rate Limiting** (FR-061):
+ - 10 submissions per hour per IP address
+
+### Functional Requirements Covered
+- FR-001 to FR-012: Complete submission flow
+- FR-055: No IP/session tracking stored
+- FR-059, FR-060: File security
+- FR-061: Rate limiting
+
+### Side Effects
+
+1. **File System**:
+ - Creates `data/products/{product_id}/feedback/{feedback_id}/`
+ - Writes `metadata.yaml`, `content.txt`
+ - Writes `attachments/{filename}` if files uploaded
+
+2. **Async Processing**:
+ - Triggers AI analysis background job
+ - Updates feedback status: `submitted` → `analyzing`
+
+---
+
+## POST /submit/{product_slug}/status/{feedback_id}
+
+**Note**: This endpoint is OUT OF SCOPE for MVP. Feedback submission is fire-and-forget. Users cannot track submission status anonymously.
+
+Future consideration: Anonymous status check via feedback ID (requires balancing anonymity with user experience).
diff --git a/specs/001-build-an-application/data-model.md b/specs/001-build-an-application/data-model.md
new file mode 100644
index 0000000..6dfcafa
--- /dev/null
+++ b/specs/001-build-an-application/data-model.md
@@ -0,0 +1,410 @@
+# Data Model: Anonymous Feedback Platform (Reklamator)
+
+**Branch**: `001-build-an-application` | **Date**: 2025-10-15
+
+This document defines the domain entities, their attributes, relationships, validation rules, and state transitions for the Reklamator application.
+
+## Entity Overview
+
+```
+Product (1) ----< (N) Feedback
+ | |
+ | |---< (N) Attachment
+ | |
+ | |---- (1) AnalysisResult
+ |
+ |----< (N) ProductOwner
+
+Administrator (manages all entities)
+```
+
+---
+
+## Entity Definitions
+
+### 1. Feedback Submission
+
+**Description**: Represents a single feedback item submitted by an anonymous user.
+
+**Storage Location**: `data/products/{product_id}/feedback/{feedback_id}/`
+
+#### Attributes
+
+| Field | Type | Required | Validation | Description |
+|-------|------|----------|------------|-------------|
+| `id` | UUID v4 | Yes | Auto-generated | Unique identifier |
+| `product_id` | String | Yes | Must reference existing product | Associated product identifier |
+| `original_text` | String | Yes* | 1-10,000 characters | Original feedback text (*empty if file-only submission) |
+| `original_language` | String (ISO 639-1) | No | 2-char code | Detected language (e.g., "en", "de", "ja") |
+| `submission_timestamp` | ISO 8601 DateTime | Yes | Auto-generated | When feedback was submitted (UTC) |
+| `status` | Enum | Yes | See Status enum below | Current processing/review status |
+| `category` | Enum | No | See Category enum below | AI-assigned category (null if analysis pending/failed) |
+| `attachment_count` | Integer | Yes | 0-3 | Number of attached files |
+
+#### Status Enum
+- `submitted` - Initial state after successful submission
+- `analyzing` - AI analysis in progress
+- `analysis_failed` - AI analysis encountered error
+- `analyzed` - AI analysis completed successfully
+- `reviewed` - Product owner has reviewed
+- `in_progress` - Product owner marked as being worked on
+- `resolved` - Product owner marked as resolved
+- `rejected` - Product owner marked as not actionable
+
+#### Category Enum (AI-assigned)
+- `idea` - New concept or suggestion
+- `feature_request` - Request for specific functionality
+- `bug` - Problem or defect report
+- `complaint` - Negative feedback about existing functionality
+
+#### Validation Rules
+- FR-002: `original_text` max length 10,000 characters
+- FR-011: At least one of (`original_text`, `attachment_count > 0`) must be true
+- FR-012: Cannot be empty (no text AND no attachments)
+- FR-027: `submission_timestamp` immutable after creation
+
+#### State Transitions
+```
+submitted → analyzing → analyzed → {reviewed, in_progress, resolved, rejected}
+ ↓
+ analysis_failed (terminal state until manual retry)
+```
+
+#### File Representation (metadata.yaml)
+```yaml
+id: "a3f2c1d5-8b4e-4f1a-9c2d-7e6f5a4b3c2d"
+product_id: "001-acme-app"
+original_language: "en"
+submission_timestamp: "2025-10-15T14:32:10Z"
+status: "analyzed"
+category: "bug"
+attachment_count: 2
+attachments:
+ - filename: "screenshot.png"
+ size_bytes: 245678
+ mime_type: "image/png"
+ - filename: "error_log.txt"
+ size_bytes: 1234
+ mime_type: "text/plain"
+```
+
+---
+
+### 2. Product/Service
+
+**Description**: Represents a product or service for which feedback can be collected.
+
+**Storage Location**: `data/products/{product_id}/config.yaml`
+
+#### Attributes
+
+| Field | Type | Required | Validation | Description |
+|-------|------|----------|------------|-------------|
+| `id` | String | Yes | Unique, URL-safe slug | Product identifier (e.g., "001-acme-app") |
+| `name` | String | Yes | 1-100 characters | Display name |
+| `description` | String | No | Max 500 characters | Product description |
+| `target_language` | String (ISO 639-1) | Yes | 2-char code | Preferred language for AI translations |
+| `submission_url_slug` | String | Yes | URL-safe, unique | URL path for submission form (e.g., "/submit/acme-app") |
+| `created_date` | ISO 8601 Date | Yes | Auto-generated | When product was registered |
+| `status` | Enum | Yes | "active" or "archived" | Current status |
+| `assigned_owner_ids` | List[String] | No | Must reference existing users | Product owner user IDs |
+
+#### Validation Rules
+- FR-046: `id` must be unique across all products
+- FR-047: `target_language` must be valid ISO 639-1 code
+- FR-049: `submission_url_slug` must be unique and URL-safe (alphanumeric + hyphens)
+- FR-053: Cannot accept new feedback if `status` is "archived"
+
+#### State Transitions
+```
+active ⇄ archived (bidirectional, admin only)
+```
+
+#### File Representation (config.yaml)
+```yaml
+id: "001-acme-app"
+name: "Acme Mobile App"
+description: "Customer feedback for Acme's flagship mobile application"
+target_language: "en"
+submission_url_slug: "acme-app"
+created_date: "2025-10-01"
+status: "active"
+assigned_owner_ids:
+ - "owner-001"
+ - "owner-002"
+statistics:
+ total_feedback_count: 127
+ last_submission: "2025-10-15T14:32:10Z"
+```
+
+---
+
+### 3. Analysis Result
+
+**Description**: Represents the AI-generated analysis of a feedback submission.
+
+**Storage Location**: `data/products/{product_id}/feedback/{feedback_id}/analysis.md`
+**Additional Storage**: Original text stored in `content.txt` for reference
+
+#### Attributes (Markdown Format)
+
+| Field | Type | Required | Description |
+|-------|------|----------|-------------|
+| `translated_text` | Markdown | Yes* | Feedback translated to target language (*if different from original) |
+| `summary` | Markdown | Yes | Concise 2-3 sentence summary in target language |
+| `detected_category` | Enum | Yes | Category assigned by AI (idea/feature_request/bug/complaint) |
+| `confidence_score` | Float (0.0-1.0) | Yes | AI confidence in categorization |
+| `analysis_timestamp` | ISO 8601 DateTime | Yes | When analysis completed |
+| `model_used` | String | Yes | AI model identifier (e.g., "claude-3-haiku-20240307") |
+| `error_message` | String | No | Error details if analysis failed |
+
+#### Validation Rules
+- FR-014: `summary` should be 2-3 sentences maximum
+- FR-015: `translated_text` required unless original language = target language
+- FR-016: Original text preserved in `content.txt` alongside analysis
+- FR-017: Detected language stored in feedback metadata.yaml `original_language`
+
+#### File Representation (analysis.md)
+```markdown
+# Feedback Analysis
+
+**Analyzed**: 2025-10-15T14:35:22Z
+**Model**: claude-3-haiku-20240307
+**Category**: bug (confidence: 0.92)
+**Original Language**: de → **Target Language**: en
+
+## Summary
+
+User reports that the app crashes when uploading large files. This appears to be a bug affecting the file upload module, preventing users from submitting documents over 5MB.
+
+## Translation
+
+**Original (German):**
+> Die App stürzt ab, wenn ich versuche, große Dateien hochzuladen. Jedes Mal wenn ich eine PDF über 5MB hochlade, friert die App ein und schließt sich.
+
+**Translated (English):**
+The app crashes when I try to upload large files. Every time I upload a PDF over 5MB, the app freezes and closes.
+
+## Attachments
+
+- screenshot.png (245 KB)
+- error_log.txt (1 KB)
+```
+
+---
+
+### 4. Attachment
+
+**Description**: Represents a file (document or image) uploaded with feedback.
+
+**Storage Location**: `data/products/{product_id}/feedback/{feedback_id}/attachments/{filename}`
+
+#### Attributes (stored in feedback metadata.yaml)
+
+| Field | Type | Required | Validation | Description |
+|-------|------|----------|------------|-------------|
+| `filename` | String | Yes | Sanitized, max 255 chars | Original filename (sanitized for safety) |
+| `size_bytes` | Integer | Yes | Max 10,485,760 (10MB) | File size in bytes |
+| `mime_type` | String | Yes | See allowed types | Validated MIME type |
+| `upload_timestamp` | ISO 8601 DateTime | Yes | Auto-generated | When file was uploaded |
+| `virus_scan_status` | Enum | Yes | "clean" or "infected" | ClamAV scan result |
+
+#### Allowed MIME Types
+- Documents: `application/pdf`, `application/vnd.openxmlformats-officedocument.wordprocessingml.document` (DOCX), `text/plain`
+- Images: `image/jpeg`, `image/png`, `image/gif`, `image/webp`
+
+#### Validation Rules
+- FR-004: Maximum 3 attachments per feedback
+- FR-006: Maximum 10MB per file
+- FR-026: Preserve original filename (sanitized)
+- FR-059: Sanitize filename to prevent directory traversal
+- FR-060: Must pass ClamAV virus scan before storage
+
+#### Security Sanitization
+- Remove directory traversal patterns: `../`, `..\\`, absolute paths
+- Replace unsafe characters: `<>:"|?*`
+- Limit filename length to 255 characters
+- If duplicate filename, append counter: `file.pdf` → `file_2.pdf`
+
+---
+
+### 5. Product Owner
+
+**Description**: Represents an authenticated user responsible for reviewing feedback for one or more products.
+
+**Storage Location**: `data/users.yaml`
+
+#### Attributes
+
+| Field | Type | Required | Validation | Description |
+|-------|------|----------|------------|-------------|
+| `id` | String | Yes | Unique | User identifier (e.g., "owner-001") |
+| `email` | String | Yes | Valid email, unique | Login email address |
+| `password_hash` | String (bcrypt) | Yes | bcrypt format | Hashed password (never store plaintext) |
+| `name` | String | Yes | 1-100 characters | Display name |
+| `role` | Enum | Yes | "product_owner" | User role (always "product_owner" for this entity) |
+| `assigned_product_ids` | List[String] | Yes | Must reference existing products | Products this owner can access |
+| `created_date` | ISO 8601 Date | Yes | Auto-generated | Account creation date |
+| `last_login` | ISO 8601 DateTime | No | Auto-updated | Last successful login |
+
+#### Validation Rules
+- FR-048: Can be assigned to multiple products
+- FR-056: Must authenticate to access dashboard
+- FR-063: Passwords hashed with bcrypt (cost factor 12)
+- FR-033: Can only view feedback for `assigned_product_ids`
+
+#### File Representation (users.yaml entry)
+```yaml
+users:
+ - id: "owner-001"
+ email: "jane.smith@example.com"
+ password_hash: "$2b$12$KIXxBt5H4vE2zT9vN8FqOe9Jx..."
+ name: "Jane Smith"
+ role: "product_owner"
+ assigned_product_ids:
+ - "001-acme-app"
+ - "002-beta-service"
+ created_date: "2025-09-15"
+ last_login: "2025-10-15T09:23:11Z"
+```
+
+---
+
+### 6. Administrator
+
+**Description**: Represents a privileged user who can register products, assign owners, and manage system configuration.
+
+**Storage Location**: `data/users.yaml` (same file as Product Owners)
+
+#### Attributes
+
+| Field | Type | Required | Validation | Description |
+|-------|------|----------|------------|-------------|
+| `id` | String | Yes | Unique | User identifier (e.g., "admin-001") |
+| `email` | String | Yes | Valid email, unique | Login email address |
+| `password_hash` | String (bcrypt) | Yes | bcrypt format | Hashed password |
+| `name` | String | Yes | 1-100 characters | Display name |
+| `role` | Enum | Yes | "admin" | User role (always "admin" for this entity) |
+| `assigned_product_ids` | List[String] | Yes | Empty list | Empty = access to all products |
+| `created_date` | ISO 8601 Date | Yes | Auto-generated | Account creation date |
+| `last_login` | ISO 8601 DateTime | No | Auto-updated | Last successful login |
+
+#### Validation Rules
+- Admin role grants full access regardless of `assigned_product_ids`
+- FR-045: Can create/modify/archive products
+- FR-048: Can assign/unassign product owners
+- Same authentication requirements as Product Owner (FR-056, FR-063)
+
+#### File Representation (users.yaml entry)
+```yaml
+users:
+ - id: "admin-001"
+ email: "admin@reklamator.local"
+ password_hash: "$2b$12$vL3Fx9..."
+ name: "System Administrator"
+ role: "admin"
+ assigned_product_ids: [] # Empty = all access
+ created_date: "2025-09-01"
+ last_login: "2025-10-15T10:45:33Z"
+```
+
+---
+
+## Domain Rules & Invariants
+
+### Cross-Entity Rules
+
+1. **Product-Feedback Relationship** (1:N)
+ - Every Feedback must reference exactly one valid Product
+ - Product can have zero or many Feedback items
+ - Archived products cannot receive new feedback (FR-053)
+
+2. **Feedback-Attachment Relationship** (1:N)
+ - Feedback can have 0-3 Attachments (FR-004, FR-010)
+ - Attachments cannot exist without parent Feedback (cascade delete)
+
+3. **Feedback-AnalysisResult Relationship** (1:1)
+ - Every analyzed Feedback has exactly one AnalysisResult
+ - AnalysisResult created asynchronously after Feedback submission
+ - Original content preserved even if analysis fails (FR-020)
+
+4. **Product-Owner Relationship** (N:M)
+ - Product can have 1 or more assigned Product Owners (FR-048)
+ - Product Owner can be assigned to multiple Products
+ - Admin users bypass assignment logic (implicit access to all)
+
+5. **Anonymity Constraint** (Global)
+ - No IP addresses stored in Feedback metadata (FR-055, SC-010)
+ - No session tracking for anonymous submissions
+ - Rate limiting uses IP for abuse prevention only (not persisted)
+
+### Deletion Rules
+
+- **Feedback Deletion**: Deletes metadata.yaml, content.txt, analysis.md, and all attachments/
+- **Product Archival**: Sets status to "archived", preserves all feedback (FR-052)
+- **Product Owner Removal**: Unassigns from products, does not delete feedback
+- **Cascade Protection**: Cannot delete Product with active feedback (archive instead)
+
+---
+
+## Storage Implementation Notes
+
+### Directory Structure Example
+
+```
+data/
+├── users.yaml # All users (admins + owners)
+└── products/
+ ├── 001-acme-app/
+ │ ├── config.yaml # Product metadata
+ │ └── feedback/
+ │ ├── a3f2c1d5-8b4e-.../
+ │ │ ├── metadata.yaml # Feedback + attachment metadata
+ │ │ ├── content.txt # Original feedback text
+ │ │ ├── analysis.md # AI analysis report
+ │ │ └── attachments/
+ │ │ ├── screenshot.png
+ │ │ └── error_log.txt
+ │ └── b7e1f3d2-4a9c-.../
+ │ ├── metadata.yaml
+ │ ├── content.txt
+ │ └── analysis.md # No attachments/ for this one
+ └── 002-beta-service/
+ ├── config.yaml
+ └── feedback/
+ └── ...
+```
+
+### File Format Standards
+
+- **YAML**: UTF-8 encoding, 2-space indentation, explicit type declarations
+- **Markdown**: CommonMark specification, UTF-8 encoding
+- **Text Files**: UTF-8 encoding with BOM handling
+
+### Indexing Strategy (Performance)
+
+For dashboard performance (SC-008, SC-014):
+- Cache product feedback counts in `config.yaml` statistics
+- Implement pagination (50 items per page)
+- Load metadata.yaml only, defer analysis.md loading until detail view
+- File modification times used for sorting (newest first per FR-041)
+
+---
+
+## Validation Summary by Functional Requirement
+
+| FR | Validation Location | Rule |
+|----|---------------------|------|
+| FR-002 | Feedback.original_text | Max 10,000 characters |
+| FR-004 | Feedback.attachment_count | 0-3 attachments |
+| FR-006 | Attachment.size_bytes | Max 10MB per file |
+| FR-011 | Feedback validation | At least text OR files required |
+| FR-012 | Feedback validation | Cannot be completely empty |
+| FR-046 | Product.id | Must be unique |
+| FR-048 | Product.assigned_owner_ids | 1+ owners required |
+| FR-053 | Product status check | Reject if archived |
+| FR-063 | User.password_hash | bcrypt with cost 12 |
+
+---
+
+**Next Steps**: Define API contracts in `/contracts/` directory
diff --git a/specs/001-build-an-application/plan.md b/specs/001-build-an-application/plan.md
new file mode 100644
index 0000000..74ab63e
--- /dev/null
+++ b/specs/001-build-an-application/plan.md
@@ -0,0 +1,188 @@
+# Implementation Plan: Anonymous Feedback Platform (Reklamator)
+
+**Branch**: `001-build-an-application` | **Date**: 2025-10-15 | **Spec**: [spec.md](./spec.md)
+**Input**: Feature specification from `/specs/001-build-an-application/spec.md`
+
+**Note**: This template is filled in by the `/speckit.plan` command. See `.specify/templates/commands/plan.md` for the execution workflow.
+
+## Summary
+
+Build a minimal web application using Flask that enables anonymous feedback submission with AI-powered analysis and translation. The system uses a file-based storage approach with folders for each submission, YAML metadata files, and markdown-formatted AI analysis reports. Product owners access analyzed feedback through an authenticated web dashboard. Design prioritizes simplicity and functionality over aesthetics - plain HTML without CSS frameworks or JavaScript libraries.
+
+## Technical Context
+
+**Language/Version**: Python 3.11+
+**Primary Dependencies**: Flask (web framework), no CSS frameworks, no JavaScript libraries
+**Storage**: File-based - folders per feedback item with YAML metadata and markdown reports
+**Testing**: pytest (contract and integration tests prioritized per constitution)
+**Target Platform**: Linux server (web application)
+**Project Type**: web (backend + frontend, but minimal frontend without frameworks)
+**AI Integration**: NEEDS CLARIFICATION - Claude API or pluggable AI provider interface
+**Performance Goals**: Handle 100 concurrent submissions, <3s dashboard load for 1000 items
+**Constraints**: <30s AI analysis time for 95% of submissions, complete anonymity (no IP/session tracking)
+**Scale/Scope**: MVP supports 100 products, 10,000 feedback items per product, 50+ languages
+**File Upload**: NEEDS CLARIFICATION - malware scanning approach, storage location strategy
+**Authentication**: NEEDS CLARIFICATION - session management approach for product owners
+**Rate Limiting**: NEEDS CLARIFICATION - implementation strategy for submission abuse prevention
+
+## Constitution Check
+
+*GATE: Must pass before Phase 0 research. Re-check after Phase 1 design.*
+
+### ✅ I. Specification-First Development
+**Status**: PASS
+Complete specification exists at `specs/001-build-an-application/spec.md` with prioritized user stories (P1-P4), 64 functional requirements with unique IDs (FR-001 to FR-064), measurable success criteria (SC-001 to SC-014), and comprehensive edge cases. All user stories are independently testable.
+
+### ✅ II. Test-First Discipline
+**Status**: PASS (Will be enforced during implementation)
+Plan includes pytest as testing framework. Implementation phase will follow mandatory workflow: write tests → verify failures → implement code → refactor. Contract and integration tests prioritized per constitution.
+
+### ✅ III. Independent User Stories
+**Status**: PASS
+Four user stories explicitly prioritized (P1: Anonymous Submission, P2: AI Analysis, P3: Dashboard, P4: Product Management). Each story is independently deliverable and testable. P1 can function standalone, P2 depends only on P1, P3 on P1+P2, P4 adds multi-product support.
+
+### ✅ IV. Simplicity & Justification
+**Status**: PASS
+Design explicitly minimizes complexity: plain HTML without CSS frameworks, no JavaScript libraries, file-based storage (no database), Flask for web framework (minimal dependencies). User input emphasizes "designed as simple as possible" and "functionality over design."
+
+**Potential Complexity Point**: File-based storage vs. database
+- **Decision**: File-based storage with folder-per-feedback structure
+- **Rationale**: Simpler deployment, no database setup/maintenance, natural fit for storing files+metadata together, sufficient for MVP scale (100 products × 10k items)
+- **Alternative Rejected**: PostgreSQL/SQLite - adds operational complexity, requires schema migrations, doesn't simplify file attachment handling
+
+### ✅ V. Documentation as Code
+**Status**: PASS
+Specification-driven workflow with all documentation in version control under `/specs/001-build-an-application/`. This plan will generate: research.md, data-model.md, contracts/, quickstart.md per constitution requirements.
+
+### Gate Result: ✅ PASS - Proceed to Phase 0 Research
+
+No constitutional violations detected. Complexity Tracking table remains empty.
+
+## Project Structure
+
+### Documentation (this feature)
+
+```
+specs/[###-feature]/
+├── plan.md # This file (/speckit.plan command output)
+├── research.md # Phase 0 output (/speckit.plan command)
+├── data-model.md # Phase 1 output (/speckit.plan command)
+├── quickstart.md # Phase 1 output (/speckit.plan command)
+├── contracts/ # Phase 1 output (/speckit.plan command)
+└── tasks.md # Phase 2 output (/speckit.tasks command - NOT created by /speckit.plan)
+```
+
+### Source Code (repository root)
+
+```
+reklamator/
+├── app/
+│ ├── __init__.py # Flask app factory
+│ ├── routes/
+│ │ ├── __init__.py
+│ │ ├── submission.py # Anonymous feedback submission endpoints
+│ │ ├── dashboard.py # Product owner dashboard endpoints
+│ │ └── admin.py # Product/owner management endpoints
+│ ├── services/
+│ │ ├── __init__.py
+│ │ ├── feedback_storage.py # File-based storage operations
+│ │ ├── ai_analyzer.py # AI analysis/translation interface
+│ │ └── auth.py # Session management
+│ ├── models/
+│ │ ├── __init__.py
+│ │ ├── feedback.py # Feedback domain model
+│ │ ├── product.py # Product domain model
+│ │ └── user.py # Product owner/admin model
+│ ├── templates/ # Plain HTML templates (Jinja2)
+│ │ ├── submission_form.html
+│ │ ├── dashboard.html
+│ │ ├── feedback_detail.html
+│ │ └── admin_products.html
+│ └── utils/
+│ ├── __init__.py
+│ ├── file_validator.py # File upload validation
+│ └── rate_limiter.py # Submission rate limiting
+│
+├── data/ # File-based storage root
+│ └── products/
+│ └── {product-id}/
+│ └── feedback/
+│ └── {feedback-id}/
+│ ├── metadata.yaml
+│ ├── analysis.md
+│ └── attachments/
+│
+├── tests/
+│ ├── contract/ # API contract tests
+│ │ ├── test_submission_api.py
+│ │ ├── test_dashboard_api.py
+│ │ └── test_admin_api.py
+│ ├── integration/ # User journey tests
+│ │ ├── test_feedback_submission_flow.py
+│ │ ├── test_ai_analysis_flow.py
+│ │ └── test_dashboard_access_flow.py
+│ └── unit/ # Optional unit tests for complex logic
+│ ├── test_feedback_storage.py
+│ └── test_file_validator.py
+│
+├── config/
+│ ├── development.py
+│ ├── production.py
+│ └── testing.py
+│
+├── requirements.txt
+├── pytest.ini
+└── run.py # Application entry point
+```
+
+**Structure Decision**: Selected web application structure (Option 2 variant) with backend-focused layout since frontend is minimal (plain HTML templates). Flask follows a single-project structure but organized by layers (routes/services/models). The `data/` directory implements the file-based storage requirement with nested folders per product and feedback item. Templates directory contains plain HTML served by Flask without separate frontend build process.
+
+## Complexity Tracking
+
+*Fill ONLY if Constitution Check has violations that must be justified*
+
+No violations detected. Table remains empty.
+
+---
+
+## Post-Design Constitution Re-Check
+
+*Re-evaluated after Phase 1 design completion*
+
+### ✅ I. Specification-First Development
+**Status**: PASS (unchanged)
+Design artifacts (research.md, data-model.md, contracts/, quickstart.md) generated from specification. No implementation code written yet.
+
+### ✅ II. Test-First Discipline
+**Status**: PASS (unchanged)
+API contracts define testable behaviors. Contract tests can be written before implementation. Quickstart guide includes test-first workflow examples.
+
+### ✅ III. Independent User Stories
+**Status**: PASS (unchanged)
+Data model and API contracts support independent implementation of P1→P2→P3→P4 stories. Each has clear endpoints and data structures.
+
+### ✅ IV. Simplicity & Justification
+**Status**: PASS (confirmed post-design)
+- File-based storage design confirmed (YAML + Markdown)
+- No database complexity introduced
+- Minimal dependencies: Flask + 7 small extensions
+- Plain HTML templates (no CSS frameworks, no JavaScript)
+- Direct file I/O (no ORM or abstraction layers)
+- Single-project structure (no microservices)
+
+**Design Review**: All research decisions favor simplicity. No new complexity introduced during Phase 1.
+
+### ✅ V. Documentation as Code
+**Status**: PASS (enhanced)
+Generated artifacts:
+- ✅ research.md (7 decision records)
+- ✅ data-model.md (6 entities fully specified)
+- ✅ contracts/ (3 API contract documents: submission, dashboard, admin)
+- ✅ quickstart.md (developer onboarding guide)
+- ✅ CLAUDE.md (agent context updated)
+
+All documentation version-controlled, linked to spec.md.
+
+### Final Gate Result: ✅ PASS - Ready for Phase 2 (Task Generation)
+
+No constitutional violations introduced during design phase. Proceed to `/speckit.tasks` command.
diff --git a/specs/001-build-an-application/quickstart.md b/specs/001-build-an-application/quickstart.md
new file mode 100644
index 0000000..0f68f7b
--- /dev/null
+++ b/specs/001-build-an-application/quickstart.md
@@ -0,0 +1,469 @@
+# Quickstart Guide: Reklamator Development
+
+**Branch**: `001-build-an-application` | **Date**: 2025-10-15
+
+This guide helps developers set up the Reklamator development environment and understand the project structure.
+
+---
+
+## Prerequisites
+
+- Python 3.11 or higher
+- ClamAV daemon (`clamd`) for malware scanning
+- Git
+- Virtual environment tool (venv)
+
+---
+
+## Initial Setup
+
+### 1. Clone Repository
+
+```bash
+git clone
+cd reklamator
+git checkout 001-build-an-application
+```
+
+### 2. Create Virtual Environment
+
+```bash
+python3 -m venv venv
+source venv/bin/activate # On Windows: venv\Scripts\activate
+```
+
+### 3. Install Dependencies
+
+```bash
+pip install -r requirements.txt
+```
+
+**Expected Core Dependencies**:
+- Flask 3.0+
+- Flask-Login (session management)
+- Flask-Limiter (rate limiting)
+- Flask-WTF (CSRF protection)
+- anthropic (Claude API client)
+- clamd (ClamAV integration)
+- bcrypt (password hashing)
+- PyYAML (configuration files)
+- pytest, pytest-flask (testing)
+
+### 4. Install and Configure ClamAV
+
+**Ubuntu/Debian**:
+```bash
+sudo apt-get update
+sudo apt-get install clamav clamav-daemon
+sudo systemctl start clamav-daemon
+sudo systemctl enable clamav-daemon
+```
+
+**macOS**:
+```bash
+brew install clamav
+brew services start clamav
+```
+
+**Verify ClamAV is running**:
+```bash
+clamdscan --version
+```
+
+### 5. Set Up Environment Variables
+
+Create `.env` file in project root:
+
+```bash
+# Flask Configuration
+FLASK_APP=run.py
+FLASK_ENV=development
+SECRET_KEY=your-secret-key-here-change-in-production
+
+# Claude API
+ANTHROPIC_API_KEY=your-claude-api-key-here
+
+# ClamAV
+CLAMD_SOCKET=/var/run/clamav/clamd.ctl # Adjust path for your system
+
+# File Storage
+DATA_DIR=./data
+
+# Rate Limiting
+RATE_LIMIT_ENABLED=true
+RATE_LIMIT_PER_HOUR=10
+```
+
+**Get Claude API Key**:
+1. Sign up at https://console.anthropic.com/
+2. Create an API key
+3. Add to `.env` file
+
+### 6. Initialize Data Directory
+
+```bash
+mkdir -p data/products
+```
+
+### 7. Create Initial Admin User
+
+Create `data/users.yaml`:
+
+```yaml
+users:
+ - id: "admin-001"
+ email: "admin@localhost"
+ password_hash: "$2b$12$KIXxBt5H4vE2zT9vN8FqOe9JxwLxPqz0q5kYv2Z3j4RQvN8FqOe9J" # Password: "admin123"
+ name: "Admin User"
+ role: "admin"
+ assigned_product_ids: []
+ created_date: "2025-10-15"
+ last_login: null
+```
+
+**Security Note**: Change the password immediately after first login!
+
+To generate a new password hash:
+```python
+import bcrypt
+password = "your-password-here"
+hash = bcrypt.hashpw(password.encode('utf-8'), bcrypt.gensalt(rounds=12))
+print(hash.decode('utf-8'))
+```
+
+---
+
+## Running the Application
+
+### Development Server
+
+```bash
+python run.py
+```
+
+Application will be available at: http://localhost:5000
+
+### Production Server (Gunicorn)
+
+```bash
+gunicorn -w 4 -b 0.0.0.0:8000 "app:create_app()"
+```
+
+---
+
+## Project Structure Overview
+
+```
+reklamator/
+├── app/ # Application code
+│ ├── __init__.py # Flask app factory
+│ ├── routes/ # HTTP endpoints
+│ │ ├── submission.py # Anonymous feedback submission
+│ │ ├── dashboard.py # Product owner dashboard
+│ │ └── admin.py # Admin interface
+│ ├── services/ # Business logic
+│ │ ├── feedback_storage.py # File-based storage operations
+│ │ ├── ai_analyzer.py # AI analysis/translation
+│ │ └── auth.py # Authentication
+│ ├── models/ # Domain models
+│ │ ├── feedback.py # Feedback entity
+│ │ ├── product.py # Product entity
+│ │ └── user.py # User entity
+│ ├── templates/ # HTML templates (Jinja2)
+│ └── utils/ # Utilities
+│ ├── file_validator.py # File upload validation
+│ └── rate_limiter.py # Rate limiting
+│
+├── data/ # File-based storage
+│ ├── users.yaml # User accounts
+│ └── products/ # Product-specific data
+│ └── {product-id}/
+│ ├── config.yaml # Product metadata
+│ └── feedback/
+│ └── {feedback-id}/
+│ ├── metadata.yaml
+│ ├── content.txt
+│ ├── analysis.md
+│ └── attachments/
+│
+├── tests/ # Test suite
+│ ├── contract/ # API contract tests
+│ ├── integration/ # User journey tests
+│ └── unit/ # Unit tests
+│
+├── config/ # Configuration files
+│ ├── development.py
+│ ├── production.py
+│ └── testing.py
+│
+├── specs/ # Feature specifications (this directory)
+├── requirements.txt
+├── pytest.ini
+├── .env # Environment variables (not in git)
+└── run.py # Application entry point
+```
+
+---
+
+## Common Development Tasks
+
+### Creating a Test Product
+
+1. Log in as admin: http://localhost:5000/login
+ - Email: `admin@localhost`
+ - Password: `admin123`
+
+2. Navigate to: http://localhost:5000/admin/products
+
+3. Click "Create New Product" and fill in:
+ - ID: `001-test-product`
+ - Name: `Test Product`
+ - Target Language: `en`
+ - Submission URL Slug: `test-product`
+ - Assign yourself as product owner
+
+4. Access submission form: http://localhost:5000/submit/test-product
+
+### Submitting Test Feedback
+
+1. Visit: http://localhost:5000/submit/test-product
+2. Enter feedback text
+3. Optionally attach files (max 3, max 10MB each)
+4. Submit
+
+Feedback will be processed asynchronously. Check the dashboard to view analysis results.
+
+### Viewing Feedback in Dashboard
+
+1. Log in: http://localhost:5000/login
+2. Dashboard: http://localhost:5000/dashboard
+3. Click on feedback item to view details
+
+### Running Tests
+
+**All tests**:
+```bash
+pytest
+```
+
+**Contract tests only**:
+```bash
+pytest tests/contract/
+```
+
+**Integration tests only**:
+```bash
+pytest tests/integration/
+```
+
+**With coverage**:
+```bash
+pytest --cov=app --cov-report=html
+```
+
+**Test-first workflow** (per constitution):
+1. Write test for new feature (should fail)
+2. Run test to verify failure
+3. Implement feature
+4. Run test to verify success
+5. Refactor if needed
+
+---
+
+## API Endpoints Reference
+
+### Anonymous Submission
+- `GET /submit/{product_slug}` - Submission form
+- `POST /submit/{product_slug}` - Submit feedback
+
+### Authentication
+- `GET /login` - Login form
+- `POST /login` - Authenticate
+- `GET /logout` - Log out
+
+### Dashboard (Product Owners)
+- `GET /dashboard` - Feedback list (with filters)
+- `GET /feedback/{feedback_id}` - Feedback detail
+- `POST /feedback/{feedback_id}/status` - Update status
+- `GET /feedback/{feedback_id}/attachment/{filename}` - Download attachment
+
+### Admin
+- `GET /admin/products` - List products
+- `GET /admin/products/new` - Create product form
+- `POST /admin/products` - Create product
+- `GET /admin/products/{id}/edit` - Edit product form
+- `POST /admin/products/{id}` - Update product
+- `POST /admin/products/{id}/archive` - Archive product
+- `GET /admin/users` - List users
+- `POST /admin/users` - Create user
+- `POST /admin/users/{id}` - Update user
+
+Full API contracts: See `/specs/001-build-an-application/contracts/`
+
+---
+
+## Configuration
+
+### Development Configuration (`config/development.py`)
+
+```python
+DEBUG = True
+TESTING = False
+SECRET_KEY = os.environ.get('SECRET_KEY', 'dev-secret-key')
+DATA_DIR = os.environ.get('DATA_DIR', './data')
+ANTHROPIC_API_KEY = os.environ.get('ANTHROPIC_API_KEY')
+CLAMD_SOCKET = os.environ.get('CLAMD_SOCKET', '/var/run/clamav/clamd.ctl')
+MAX_CONTENT_LENGTH = 10 * 1024 * 1024 # 10MB max upload
+RATE_LIMIT_ENABLED = True
+RATE_LIMIT_PER_HOUR = 10
+```
+
+### Production Configuration (`config/production.py`)
+
+```python
+DEBUG = False
+TESTING = False
+SECRET_KEY = os.environ.get('SECRET_KEY') # Required, no default
+DATA_DIR = os.environ.get('DATA_DIR', '/var/lib/reklamator/data')
+ANTHROPIC_API_KEY = os.environ.get('ANTHROPIC_API_KEY') # Required
+CLAMD_SOCKET = os.environ.get('CLAMD_SOCKET', '/var/run/clamav/clamd.ctl')
+MAX_CONTENT_LENGTH = 10 * 1024 * 1024
+RATE_LIMIT_ENABLED = True
+RATE_LIMIT_PER_HOUR = 10
+SESSION_COOKIE_SECURE = True # HTTPS only
+SESSION_COOKIE_HTTPONLY = True
+SESSION_COOKIE_SAMESITE = 'Lax'
+```
+
+---
+
+## Troubleshooting
+
+### ClamAV Connection Error
+
+**Error**: `pyclamd.ConnectionError: Could not connect to clamd`
+
+**Solution**:
+1. Verify ClamAV is running: `sudo systemctl status clamav-daemon`
+2. Check socket path: `ls /var/run/clamav/clamd.ctl`
+3. Update `CLAMD_SOCKET` in `.env` if needed
+4. Restart ClamAV: `sudo systemctl restart clamav-daemon`
+
+### Claude API Error
+
+**Error**: `anthropic.APIError: Invalid API key`
+
+**Solution**:
+1. Verify API key in `.env` file
+2. Check key is active at https://console.anthropic.com/
+3. Ensure no extra whitespace in key
+
+### File Upload Fails
+
+**Error**: `413 Payload Too Large`
+
+**Solution**:
+- Check file size (max 10MB per file)
+- Check total payload size (3 files + form data)
+- Verify `MAX_CONTENT_LENGTH` in config
+
+**Error**: `Unsupported file type`
+
+**Solution**:
+- Verify file extension: `.pdf`, `.docx`, `.txt`, `.jpg`, `.png`, `.gif`, `.webp`
+- Check MIME type matches extension
+
+### Rate Limit Exceeded
+
+**Error**: `429 Too Many Requests`
+
+**Solution**:
+- Wait 1 hour before retrying
+- For development, disable rate limiting: `RATE_LIMIT_ENABLED=false` in `.env`
+- Or increase limit: `RATE_LIMIT_PER_HOUR=100`
+
+---
+
+## Development Guidelines
+
+### Test-First Discipline (Constitutional Requirement)
+
+1. **Before implementing any feature**:
+ - Write contract/integration test
+ - Run test to verify it fails
+ - Implement feature
+ - Run test to verify success
+
+2. **Test organization**:
+ - Contract tests: Test API endpoints (HTTP requests/responses)
+ - Integration tests: Test user journeys (multi-step workflows)
+ - Unit tests: Test complex business logic in isolation
+
+3. **Example test-first workflow**:
+
+```python
+# Step 1: Write test (tests/contract/test_submission_api.py)
+def test_submit_feedback_with_text_only(client):
+ response = client.post('/submit/test-product', data={
+ 'feedback_text': 'This is test feedback'
+ })
+ assert response.status_code == 200
+ assert b'Thank You!' in response.data
+
+# Step 2: Run test (should FAIL - endpoint not implemented)
+# pytest tests/contract/test_submission_api.py::test_submit_feedback_with_text_only
+
+# Step 3: Implement feature (app/routes/submission.py)
+@bp.route('/submit/', methods=['POST'])
+def submit_feedback(product_slug):
+ # Implementation here
+ pass
+
+# Step 4: Run test again (should PASS)
+# pytest tests/contract/test_submission_api.py::test_submit_feedback_with_text_only
+```
+
+### Code Style
+
+- Follow PEP 8
+- Use type hints where helpful
+- Keep functions small and focused
+- Prefer clear names over comments
+- Run linting: `flake8 app/`
+- Run formatting: `black app/`
+
+### Git Workflow
+
+- Feature branch: `001-build-an-application` (already created)
+- Commit messages: Descriptive, imperative mood
+- Test before committing
+- Regular integration to main branch
+
+---
+
+## Next Steps
+
+1. **Set up environment** following steps above
+2. **Run tests** to verify setup: `pytest`
+3. **Start development server**: `python run.py`
+4. **Create test product** via admin interface
+5. **Submit test feedback** via submission form
+6. **Review implementation plan**: `/specs/001-build-an-application/plan.md`
+7. **Begin task implementation**: Wait for `/specs/001-build-an-application/tasks.md` (generated by `/speckit.tasks`)
+
+---
+
+## Resources
+
+- **Feature Specification**: `/specs/001-build-an-application/spec.md`
+- **Implementation Plan**: `/specs/001-build-an-application/plan.md`
+- **Research**: `/specs/001-build-an-application/research.md`
+- **Data Model**: `/specs/001-build-an-application/data-model.md`
+- **API Contracts**: `/specs/001-build-an-application/contracts/`
+- **Flask Documentation**: https://flask.palletsprojects.com/
+- **Claude API Documentation**: https://docs.anthropic.com/
+- **Pytest Documentation**: https://docs.pytest.org/
+
+---
+
+**Questions?** Refer to the specification documents or implementation plan for detailed requirements and design decisions.
diff --git a/specs/001-build-an-application/research.md b/specs/001-build-an-application/research.md
new file mode 100644
index 0000000..5c4559b
--- /dev/null
+++ b/specs/001-build-an-application/research.md
@@ -0,0 +1,366 @@
+# Research: Anonymous Feedback Platform (Reklamator)
+
+**Branch**: `001-build-an-application` | **Date**: 2025-10-15
+
+This document resolves all NEEDS CLARIFICATION items identified in the Technical Context section of plan.md.
+
+## 1. AI Integration Approach
+
+### Decision: Pluggable AI provider interface with Claude as default
+
+**Rationale**:
+- User input specifies "we use claude but it can be any other service as well"
+- Designing for extensibility aligns with good architectural practice
+- Enables future migration to different AI providers without major refactoring
+
+**Implementation Approach**:
+- Abstract base class `AIAnalyzer` defining interface: `analyze_feedback(text: str, target_lang: str) -> AnalysisResult`
+- Concrete implementation `ClaudeAnalyzer` using Anthropic API
+- Configuration-driven provider selection
+- API key management via environment variables
+
+**Alternatives Considered**:
+- **Hard-coded Claude API integration**: Simpler initially but violates user requirement for provider flexibility
+- **LangChain framework**: Adds significant dependency weight for simple translation/categorization task
+- **Multiple provider implementations from start**: Premature complexity - implement Claude first, abstract as needed
+
+**Claude API Specifics**:
+- Use `anthropic` Python SDK
+- Model: `claude-3-haiku-20240307` for cost-effective analysis (fast, sufficient for categorization/translation)
+- Prompt design: Single API call with structured output for category, summary, translation
+- Error handling: Retry logic for transient failures, graceful degradation for persistent errors
+
+**Research References**:
+- Anthropic API Documentation: https://docs.anthropic.com/
+- Python SDK: https://github.com/anthropics/anthropic-sdk-python
+
+---
+
+## 2. File Upload - Malware Scanning Approach
+
+### Decision: ClamAV integration via clamd for virus scanning
+
+**Rationale**:
+- FR-060 requires malware scanning before storage
+- ClamAV is open-source, widely used, actively maintained
+- `clamd` provides Python bindings for integration
+- Suitable for on-premise deployment matching file-based storage philosophy
+
+**Implementation Approach**:
+- Install ClamAV daemon (`clamd`) as system service
+- Use `clamd` Python library for scanning uploaded files
+- Scan files synchronously during upload before writing to disk
+- Reject files that fail virus scan with clear error message
+- Log scanning failures for security monitoring
+
+**Configuration**:
+- Maximum file size: 10MB per file (FR-006)
+- Allowed extensions: `.pdf`, `.docx`, `.txt`, `.jpg`, `.png`, `.gif`, `.webp`
+- MIME type validation in addition to extension checking
+- Temporary upload storage cleaned after scan (pass or fail)
+
+**Alternatives Considered**:
+- **Cloud-based scanning (VirusTotal API)**: Violates anonymity requirement (uploads data externally), adds latency
+- **No scanning**: Violates FR-060 security requirement
+- **Manual review**: Not scalable, delays feedback processing
+- **Python-based scanning (yara-python)**: More complex to configure, less comprehensive than ClamAV
+
+**Dependencies**:
+- `clamd` Python library
+- ClamAV daemon installed on server
+
+---
+
+## 3. File Upload - Storage Location Strategy
+
+### Decision: Local filesystem storage in `data/products/{product-id}/feedback/{feedback-id}/attachments/`
+
+**Rationale**:
+- Aligns with file-based storage architecture (no database)
+- User input specifies "folders foreach user input" and "files in a folder"
+- Simple to implement, backup, and inspect
+- No additional service dependencies
+- Sufficient for MVP scale (100 products × 10k items × 3 files × 10MB = ~30TB worst case)
+
+**Directory Structure**:
+```
+data/
+└── products/
+ └── {product-id}/ # e.g., "001-acme-app"
+ ├── config.yaml # Product metadata (name, target language, owners)
+ └── feedback/
+ └── {feedback-id}/ # UUID v4, e.g., "a3f2c1d5-..."
+ ├── metadata.yaml # Feedback metadata (timestamp, status, category, etc.)
+ ├── content.txt # Original feedback text
+ ├── analysis.md # AI-generated analysis report
+ └── attachments/
+ ├── original_filename_1.pdf
+ ├── original_filename_2.png
+ └── original_filename_3.jpg
+```
+
+**File Naming**:
+- Preserve original filenames to maintain user context
+- Sanitize filenames to prevent directory traversal (strip `../`, absolute paths, etc.)
+- Handle duplicate filenames by appending counter if needed
+
+**Alternatives Considered**:
+- **Cloud storage (S3/GCS)**: Adds external dependency, cost, complexity; overkill for MVP
+- **Flat directory per product**: Poor scalability, difficult to organize metadata
+- **Database with BLOB storage**: Contradicts file-based storage decision, adds DB complexity
+- **Content-addressed storage (hash-based filenames)**: Loses original filename context, complicates presentation
+
+**Backup Strategy** (out of scope for MVP but noted):
+- Simple filesystem backup via rsync/tar sufficient
+- Can upgrade to cloud sync if needed later
+
+---
+
+## 4. Authentication - Session Management Approach
+
+### Decision: Flask-Login with server-side sessions for product owners/admins
+
+**Rationale**:
+- Flask-Login is standard, well-tested session management for Flask
+- Server-side sessions prevent token tampering
+- Simple username/password authentication sufficient for MVP
+- FR-056, FR-063 require authentication and secure password storage
+
+**Implementation Approach**:
+- Use `Flask-Login` extension for session management
+- Store user credentials in simple YAML file (products/users.yaml) for MVP consistency with file-based approach
+- Hash passwords with `bcrypt` (FR-063)
+- Session cookies: `HttpOnly`, `Secure` (HTTPS only), `SameSite=Lax`
+- Session timeout: 24 hours of inactivity
+
+**User Model**:
+```yaml
+users:
+ - id: "admin-001"
+ email: "admin@example.com"
+ password_hash: "$2b$12$..."
+ role: "admin"
+ assigned_products: [] # Empty = all products access
+
+ - id: "owner-001"
+ email: "owner@example.com"
+ password_hash: "$2b$12$..."
+ role: "product_owner"
+ assigned_products: ["001-acme-app", "002-beta-service"]
+```
+
+**Access Control**:
+- Admins: Full access to all products, can manage products/owners
+- Product Owners: Read-only access to assigned products only (FR-033)
+- Anonymous users: Submission form access only (no authentication)
+
+**Alternatives Considered**:
+- **JWT tokens**: More complex, unnecessary for server-rendered HTML application
+- **OAuth/SAML**: Over-engineered for MVP, adds external identity provider dependency
+- **Database-backed sessions**: Contradicts file-based architecture
+- **No authentication**: Violates FR-056 requirement
+
+**Dependencies**:
+- `Flask-Login` extension
+- `bcrypt` for password hashing
+
+---
+
+## 5. Rate Limiting - Implementation Strategy
+
+### Decision: Flask-Limiter with IP-based rate limiting for submission endpoint
+
+**Rationale**:
+- FR-061 requires rate limiting (suggested: 10 submissions/hour/IP)
+- Flask-Limiter is standard, well-maintained Flask extension
+- IP-based limiting suitable for anonymous submissions
+- In-memory storage sufficient for MVP (single server deployment)
+
+**Implementation Approach**:
+- Use `Flask-Limiter` extension
+- Apply rate limit decorator to submission route: `@limiter.limit("10 per hour")`
+- Storage backend: In-memory (default) for MVP
+- Return HTTP 429 Too Many Requests with clear error message
+- Exempt authenticated admin users from rate limits (for testing)
+
+**Rate Limit Configuration**:
+- Anonymous submission: 10 requests per hour per IP address
+- Dashboard/admin routes: No rate limiting (authenticated users only)
+- Rate limit headers included in response: `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset`
+
+**Considerations**:
+- IP-based limiting can be circumvented via VPN/proxy but sufficient deterrent for casual abuse
+- Behind proxy/load balancer: Configure Flask-Limiter to read `X-Forwarded-For` header
+- Note: FR-055 requires no fingerprinting for identification - rate limiting is for abuse prevention only, not user tracking
+
+**Alternatives Considered**:
+- **CAPTCHA (hCaptcha/reCAPTCHA)**: Adds friction to user experience, contradicts "lower barriers" goal
+- **Redis-backed rate limiting**: Unnecessary complexity for single-server MVP
+- **No rate limiting**: Violates FR-061 requirement, leaves system vulnerable to abuse
+- **Token bucket per session**: Requires session tracking for anonymous users, violates anonymity
+
+**Dependencies**:
+- `Flask-Limiter` extension
+
+---
+
+## 6. Flask Best Practices for Simple HTML Applications
+
+### Decision: Server-side rendering with Jinja2 templates, no JavaScript
+
+**Rationale**:
+- User explicitly specifies "plain html using flask" and "does not use any css frameworks or javascript libraries"
+- Server-side rendering eliminates frontend build complexity
+- Jinja2 included with Flask, no additional dependencies
+- Forms use standard HTTP POST/GET, progressive enhancement approach
+
+**Template Approach**:
+- Minimal inline CSS for basic layout (no framework)
+- Semantic HTML5 for accessibility
+- Server-side form validation with error display
+- Standard browser form controls (no custom widgets)
+
+**Form Handling**:
+- POST requests for submissions
+- Server-side validation with error messages
+- Flash messages for user feedback
+- Redirect-after-POST pattern to prevent duplicate submissions
+
+**No JavaScript Requirement** (Edge Case from spec.md line 105):
+- "What happens when a user's browser doesn't support JavaScript"
+- Answer: Application works fully without JavaScript (no JS used)
+- File uploads work via standard HTML ` `
+
+**Best Practices Applied**:
+- Flask app factory pattern for testability
+- Blueprint organization for routes
+- Environment-based configuration
+- CSRF protection via Flask-WTF (even for simple forms)
+
+**Dependencies**:
+- Flask (includes Jinja2)
+- Flask-WTF for CSRF protection
+
+---
+
+## 7. Python Dependency Management
+
+### Decision: requirements.txt with pinned versions for reproducibility
+
+**Rationale**:
+- Simplest dependency management for Flask application
+- No need for Poetry/Pipenv complexity in MVP
+- Pin exact versions for reproducibility
+- Virtual environment assumed for isolation
+
+**Core Dependencies** (estimated):
+```
+Flask==3.0.0
+Flask-Login==0.6.3
+Flask-Limiter==3.5.0
+Flask-WTF==1.2.1
+anthropic==0.8.0
+clamd==1.0.2
+bcrypt==4.1.2
+PyYAML==6.0.1
+pytest==7.4.3
+pytest-flask==1.3.0
+```
+
+**Development Dependencies**:
+- pytest, pytest-flask for testing
+- black for code formatting
+- flake8 for linting
+
+---
+
+## Technology Stack Summary
+
+| Component | Technology | Rationale |
+|-----------|-----------|-----------|
+| **Web Framework** | Flask 3.0+ | Lightweight, simple, widely supported |
+| **Template Engine** | Jinja2 (built-in) | Server-side rendering, no JS needed |
+| **AI Provider** | Claude API (Anthropic) | User-specified, abstracted for future flexibility |
+| **Authentication** | Flask-Login + bcrypt | Standard session management, secure passwords |
+| **Rate Limiting** | Flask-Limiter | Prevent abuse, simple IP-based approach |
+| **Malware Scanning** | ClamAV + clamd | Open-source, reliable, on-premise |
+| **Storage** | Filesystem (YAML + Markdown) | Matches user requirements, simple, no DB |
+| **Testing** | pytest + pytest-flask | Industry standard, good Flask integration |
+| **Python Version** | 3.11+ | Modern, stable, good performance |
+| **Deployment** | Gunicorn (WSGI) | Production-ready Flask server |
+
+---
+
+## Non-Functional Requirements Research
+
+### Performance Considerations
+
+**Concurrent Submissions** (SC-012):
+- Target: 100 concurrent submissions without errors
+- Flask + Gunicorn with 4-8 worker processes should handle this
+- File I/O is bottleneck: consider async I/O if performance issues arise
+- AI analysis happens asynchronously (background task) to not block submission response
+
+**Dashboard Performance** (SC-008):
+- Target: Load 1000 items in <3 seconds
+- File-based approach: Index product feedback directories, cache counts
+- Implement pagination (50 items per page)
+- Use lazy loading for file attachments (links, not embedded content)
+
+**AI Analysis Time** (SC-007):
+- Target: <30 seconds for 95% of submissions
+- Claude Haiku model typically responds in 2-5 seconds for translation/categorization
+- Timeout: 45 seconds before marking as failed
+- Queue-based processing if needed (Python `queue` module or simple file-based queue)
+
+### Security Considerations
+
+**Anonymity Enforcement** (FR-055, SC-010):
+- Do NOT log IP addresses in feedback metadata
+- Rate limiting uses IP for abuse prevention only, not stored with feedback
+- No session cookies for anonymous submission
+- No analytics/tracking scripts
+
+**File Upload Security**:
+- Validate MIME types server-side (don't trust client)
+- ClamAV scanning before storage
+- Sanitize filenames to prevent directory traversal
+- Store outside web root, serve via Flask route with access control
+
+**HTTPS Requirement** (FR-064):
+- Deployment guide must specify reverse proxy (nginx) with TLS
+- Redirect HTTP to HTTPS
+- HSTS headers recommended
+
+---
+
+## Open Questions for Implementation Phase
+
+1. **Asynchronous AI Analysis**: Should analysis happen synchronously (user waits) or asynchronously (background job)?
+ - **Recommendation**: Asynchronous - return success immediately, process in background
+ - Implement simple file-based queue or use Python `threading` for MVP
+
+2. **Admin Bootstrap**: How is the first admin user created?
+ - **Recommendation**: CLI command or config file initialization script
+
+3. **Email Notifications**: Out of scope (line 255) but commonly requested
+ - **Recommendation**: Document as future enhancement, design hooks for extensibility
+
+4. **Logging Strategy**: Structured logs for operational monitoring?
+ - **Recommendation**: Python `logging` module, JSON format, separate file per environment
+
+---
+
+## Research Validation
+
+All NEEDS CLARIFICATION items from Technical Context have been resolved:
+
+| Item | Resolution | Document Section |
+|------|------------|------------------|
+| AI Integration | Pluggable interface, Claude as default | §1 |
+| File Upload - Malware Scanning | ClamAV + clamd | §2 |
+| File Upload - Storage Location | Filesystem: `data/products/.../feedback/.../attachments/` | §3 |
+| Authentication | Flask-Login + server-side sessions + bcrypt | §4 |
+| Rate Limiting | Flask-Limiter, 10/hour/IP | §5 |
+
+**Next Phase**: Proceed to Phase 1 (data-model.md, contracts, quickstart.md)