# Feature Specification: Public Grid Viewer + Live Selection (Vertical Slice) **Feature Branch**: `001-public-grid-viewer` **Created**: 2026-01-03 **Status**: Draft **Input**: User description: "Feature 1: Public Grid Viewer + Live Selection (no checkout). Vertical slice: public master image + pan/zoom + canvas-based grid, selection, server-side price prop, stub availability, upload preview modal (no payment)." ## Constitution Alignment (REQUIRED) This spec MUST comply with `constitution.md`. If not, document deviation + mitigation here. ### Mandatory Invariants - [ ] Server computes price; client is display-only - [ ] DB-level prevention of double booking - [ ] Webhooks + rendering pipeline are idempotent - [ ] Rendering occurs in queued job(s) ## Success Criteria (MUST) Define measurable outcomes for "done". - Performance: - [ ] Public page loads master image + metadata within ___ ms on ___ connection - Correctness: - [ ] No double booking possible under concurrency - [ ] Paid orders render exactly once (idempotent) - UX: - [ ] User can select cells, preview, and pay end-to-end - Security: - [ ] Upload rules enforced (type/size/dimensions), safe link validation ## Acceptance Scenarios (MUST) Write at least 3 end-to-end scenarios (Gherkin-style preferred). ### Scenario 1: Reserve → Pay → Render Given the public page is loaded with the master image and grid When a visitor drags a rectangular selection over the grid and opens the sidebar Then the UI shows the selected cell count and the server-provided `price_per_cell` and computes the total price (display only) ### Scenario 2: Concurrent selection conflict Given two visitors load the same public page When Visitor A selects a block of cells and Visitor B selects an overlapping block Then both visitors see availability overlays; selection is local (no booking) and the server availability API reports occupied cells when present (MVP: API returns empty set) ### Scenario 3: Webhook replay / retry Given this vertical slice does not include payment, webhook scenarios are out of scope for this feature; the spec requires the pipeline to be prepared for idempotent webhooks in later features. ## Scope (See above for file contents. You may not need to search or read the file again.) This vertical slice covers the public-facing viewer and live selection experience without creating orders or processing payments. It provides the UI and server endpoints required to compute prices, return current `master_version`, and (MVP) return an availability stub. It does NOT: create reservations in the database, process payments, run rendering Jobs, or persist uploaded images permanently. ## User Scenarios & Testing *(mandatory)* ### User Story 1 - Public viewer + Live selection (Priority: P1) Any visitor can open the public grid page, pan/zoom the master image, draw a rectangular selection (multi-cell), and see the live cell count and computed total price. The selection is client-only for now; no reservation or checkout is created. **Why this priority**: Validates UI complexity (grid, pan/zoom, selection) early and delivers immediate visible value. **Independent Test**: Load the public page, perform selection on desktop and mobile, verify cell count and price calculation match server-provided `price_per_cell`. **Acceptance Scenarios**: 1. **Given** the public master image is visible, **When** the user drags to select cells, **Then** the sidebar updates with cell count and total price. 2. **Given** mobile device, **When** user pinches/drag to select, **Then** selection precision is maintained and preview shows correctly. --- ### User Story 2 - Upload preview modal (Priority: P2) User can open a modal from the sidebar, select an image file and optional link, and see a client-side preview composited into the selection rectangle. The modal does not persist anything to the server in this slice. **Independent Test**: Select an image and link, verify preview scales/crops to selection proportion and displays correctly. --- ### User Story 3 - Availability overlay (Priority: P3) Server exposes a lightweight endpoint returning currently occupied cell coordinates. For the vertical slice this may return an empty list (stub) but the client must render any occupied cells in a distinct overlay. **Independent Test**: Call the availability endpoint and verify the overlay marks returned coordinates as unavailable. --- [Add more user stories as needed, each with an assigned priority] ### Edge Cases - Selection partially outside bounds: selection is clamped to image bounds and cell counts reflect clamped area. - Very large selection on mobile: UI prevents selections beyond a maximum configured cell count and shows a warning. ## Requirements *(mandatory)* ### Functional Requirements - **FR-001**: Public page MUST return `master_image_url` and `master_version`. - **FR-002**: Client MUST be able to request `price_per_cell` from the server; client computes `cell_count × price_per_cell` for display only. - **FR-003**: Client MUST render an interactive grid overlay and allow rectangle selection with pan/zoom support. - **FR-004**: Client MUST provide an upload + link modal with client-side preview scaled to selection. - **FR-005**: Server MUST expose an availability endpoint returning occupied cells (MVP: can return empty set). - **FR-006**: All endpoints MUST validate inputs and rate-limit sensitive endpoints. ### Key Entities *(include if feature involves data)* - **MasterImage**: `path`, `version` (for cache-busting) - **Availability**: list of `{cell_x, cell_y}` entries returned by endpoint ## Success Criteria *(mandatory)* ### Measurable Outcomes - **SC-001**: Public page loads master image and metadata within 2000ms on a 4G connection (approximate). - **SC-002**: Selection cell coordinates reported by client match server cell unit grid (pixel-accurate within 1 cell). - **SC-003**: Mobile pinch/drag interaction completes selection without lost events on iPhone 14 Pro viewport. - **SC-004**: Price calculation displays correctly using server-provided `price_per_cell`. ## Assumptions - `cell_size` (in pixels) is a server-configured value provided to the client. - Availability API may return empty set for MVP; production will enforce DB-level locks later. ## Out of Scope - Creating orders, reservations, payments, rendering jobs, or persisting uploaded images. ## Requirements *(mandatory)* ### Functional Requirements - **FR-001**: System MUST [specific capability, e.g., "allow users to create accounts"] - **FR-002**: System MUST [specific capability, e.g., "validate email addresses"] - **FR-003**: Users MUST be able to [key interaction, e.g., "reset their password"] - **FR-004**: System MUST [data requirement, e.g., "persist user preferences"] - **FR-005**: System MUST [behavior, e.g., "log all security events"] *Example of marking unclear requirements:* - **FR-006**: System MUST authenticate users via [NEEDS CLARIFICATION: auth method not specified - email/password, SSO, OAuth?] - **FR-007**: System MUST retain user data for [NEEDS CLARIFICATION: retention period not specified] ### Key Entities *(include if feature involves data)* - **[Entity 1]**: [What it represents, key attributes without implementation] - **[Entity 2]**: [What it represents, relationships to other entities] ## Success Criteria *(mandatory)* ### Measurable Outcomes - **SC-001**: [Measurable metric, e.g., "Users can complete account creation in under 2 minutes"] - **SC-002**: [Measurable metric, e.g., "System handles 1000 concurrent users without degradation"] - **SC-003**: [User satisfaction metric, e.g., "90% of users successfully complete primary task on first attempt"] - **SC-004**: [Business metric, e.g., "Reduce support tickets related to [X] by 50%"]