schema: spec-driven # Project context - shown to AI when creating artifacts # This provides essential context about the Headroom project techstack: | ## Backend (Laravel API) - **Framework:** Laravel 12 (latest) with PHP 8.4 - **Database:** PostgreSQL (latest, Alpine container) - **Caching:** Redis (latest, Alpine container) - query + response caching - **Authentication:** JWT (tymon/jwt-auth package) - **API Design:** REST with Laravel API Resources - **API Documentation:** Laravel Scribe (auto-generates SwaggerUI) - **Testing:** PHPUnit (unit) + Pest (feature) - **Code Style:** PSR-12, Laravel conventions - **Container:** Docker (port 3000) ## Frontend (SvelteKit) - **Framework:** SvelteKit (latest) with Svelte 5 - **Styling:** Tailwind CSS 4 + DaisyUI 5 - **Icons:** Lucide Svelte (modern icon library) - **Charts:** Recharts - **Tables:** TanStack Table (React Table for Svelte) - **Forms:** Superforms + Zod + SvelteKit Form Actions - **State Management:** Svelte stores (minimal - UI state only) - **HTTP Client:** Native fetch (no Axios) - **Testing:** Vitest (unit) + Playwright (E2E) - **Container:** Docker (port 5173) ## Infrastructure - **Local Dev:** Docker Compose with code-mounted volumes (hot reload) - **Reverse Proxy:** Nginx Proxy Manager (existing) - **Database Volume:** Mounted to ./data/postgres - **Cache Volume:** Mounted to ./data/redis - **Secrets:** .env files (all environments) conventions: | ## Code Style Standards ### Backend (Laravel) - Follow PSR-12 coding standards - Use Laravel Pint for linting - Use PHPStan (level 5+) for static analysis - Use Laravel conventions for naming (camelCase methods, snake_case DB columns) - Use API Resources for consistent JSON responses - Use Form Requests for validation - Use Policies for authorization - Use UUIDs for primary keys (prevents ID enumeration) ### Frontend (SvelteKit) - Use Prettier for formatting - Use ESLint with Svelte plugin - Use TypeScript strict mode - Use SvelteKit file-based routing conventions - Use $lib alias for imports from src/lib - Use Zod schemas for validation (shared between frontend and API contracts) ## Git Conventions - **Branch naming:** `feature/opsx-` for OpenSpec changes - **Commit format:** ``` [Type] Brief description (50 chars max) Detailed explanation (optional, 72 char wrap) Refs: openspec/changes/ ``` - **Types:** feat, fix, refactor, test, docs, chore - **Granular commits:** One fix = one commit ## API Conventions - RESTful endpoints with standard HTTP verbs - Response format: `{ "data": {}, "meta": {}, "links": {} }` - Error format: `{ "message": "...", "errors": {} }` - Cache keys pattern: `allocations:month:{YYYY-MM}`, `reports:forecast:{from}:{to}:{hash}` - TTL: 1 hour (allocations), 15 min (reports), 24 hours (master data) development_strategy: approach: "Spec-Driven Development (SDD) + Test-Driven Development (TDD) Hybrid" description: | Every capability follows a 4-phase cycle: ## Phase 1: SPEC → TEST (Red Phase) - Read scenarios from specs//spec.md - Write E2E tests (Playwright) - mark as test.fixme() - Write API tests (Pest) - mark as ->todo() - Write unit tests (Pest/Vitest) - mark as ->todo() or test.skip() - Write component tests (Vitest) - mark as test.skip() - Commit: "test(): Add pending tests for all scenarios" ## Phase 2: IMPLEMENT (Green Phase) - Remove skip/todo marker from one test - Write MINIMAL code to make it pass - Run test suite (npm run test, php artisan test, npx playwright test) - Commit when green: "feat(): Implement " - Repeat for all scenarios ## Phase 3: REFACTOR (Clean Phase) - Review for code smells, duplication, performance - Refactor with confidence (tests guard against regression) - Run full test suite - Commit: "refactor(): " ## Phase 4: DOCUMENT - Generate API docs: php artisan scribe:generate - Verify all tests pass - Commit: "docs(): Update API documentation" test_granularity: "Every spec scenario gets a test" test_organization: "Mirror current structure" test_types: - E2E: Playwright tests for critical user journeys - API: Pest Feature tests for all endpoints - Unit: Pest/Vitest for internal methods and business logic - Component: Vitest + Testing Library for Svelte components pending_markers: php: "->todo()" playwright: "test.fixme()" vitest: "test.skip()" test_naming: format: "Descriptive - mirror spec scenario intent" examples: - "authenticates user with valid credentials and issues JWT tokens" - "returns 401 when credentials are invalid" - "rotates refresh token on each refresh request" coverage_target: ">70%" regression_strategy: "Every test is a regression test - run full suite on every PR" scripts: backend: test: "pest" "test:unit": "pest --filter=Unit" "test:feature": "pest --filter=Feature" "test:coverage": "pest --coverage --min=70" "test:todo": "pest --filter=todo" lint: "pint" "lint:fix": "pint --fix" analyse: "phpstan analyse --level=5" docs: "scribe:generate" frontend: test: "vitest run" "test:watch": "vitest" "test:ui": "vitest --ui" "test:e2e": "playwright test" "test:e2e:ui": "playwright test --ui" "test:all": "npm run test && npm run test:e2e" lint: "eslint ." "lint:fix": "eslint . --fix" format: "prettier --check ." "format:fix": "prettier --write ." rules: # Project-level standing instructions (from decision-log.md) all_changes: - "Every change must follow SDD+TDD: specs → pending tests → implementation → refactor" - Every spec scenario must have corresponding tests (E2E, API, Unit, Component) - Pending tests must be committed before implementation (red phase) - Changes must end with code review for style, standards, and security - Verification (/opsx-verify) must check for uncovered code (code not tested) - Commits must be granular (one scenario = one commit) - Code coverage must be >70% (enforced in /opsx-verify) - All tests must pass before merge - Zero linting errors (Laravel Pint, ESLint, Prettier) - API documentation must be up-to-date (Scribe generation) # Documentation Standards documentation: - API documentation (Scribe annotations) is MANDATORY for all controllers - Every endpoint must have @group, @authenticated (if protected), @response annotations - Update docs/ when making significant decisions (decision-log.md, architecture.md) - Document new dependencies in both config.yaml techstack AND design.md - UI decisions go in decision-log.md → "UI Layout Decisions" section - Architecture changes go in architecture.md → relevant section # Workflow Loops workflow: - "Follow capability-based workflow: Test → Implement → Refactor → Document" - Do NOT skip phases - each phase has a specific commit - Run full test suite after EACH implementation commit - Fix failing tests before moving to next scenario - API documentation (Scribe) is generated in Phase 4 (Document) - Loop through scenarios one at a time (not all at once) # UI Standards (70% data-dense, 30% utilitarian) ui_standards: - Use Lucide Svelte for ALL icons (no inline SVGs, no other icon libraries) - DaisyUI-first approach - use DaisyUI components before building custom - "Sidebar pattern: Collapsible (expanded ↔ collapsed ↔ hidden)" - Global month selector in top bar (affects all views) - Light mode default, dark mode available via toggle - "Table density: Use table-compact for data-heavy views" - "Reference apps: Obsidian (minimal chrome) + Jira (hierarchical sidebar)" # Component Patterns component_patterns: - "layout: AppLayout.svelte -> Main wrapper with sidebar + content area" - "layout: Sidebar.svelte -> Collapsible navigation with sections" - "layout: TopBar.svelte -> Breadcrumbs, month selector, user menu" - "layout: Breadcrumbs.svelte -> Auto-generated from route" - "layout: PageHeader.svelte -> Page title + action buttons slot" - "state: layoutStore -> sidebarState ('expanded'|'collapsed'|'hidden'), theme" - "state: periodStore -> selectedMonth (global YYYY-MM format)" - "state: Persist user preferences to localStorage" - "navigation: Sections -> PLANNING, REPORTS, ADMIN" - "navigation: ADMIN section visible only to superuser role" - "navigation: Active route highlighting required" # Accessibility Requirements accessibility: - "Keyboard navigation: Tab through sidebar, Enter/Space to activate" - Escape to close mobile drawer - "Cmd/Ctrl + \ to toggle sidebar (desktop)" - "ARIA: aria-expanded on sidebar toggle, aria-current='page' on active nav" - Focus trap in mobile drawer - Focus restored on drawer close - All form inputs must have associated labels # Responsive Design responsive: - "≥1280px (xl): Sidebar expanded by default, manual toggle" - "1024-1279px (lg): Sidebar collapsed by default, manual toggle" - "768-1023px (md): Sidebar hidden, hamburger menu (drawer overlay)" - "<768px (sm): Sidebar hidden, hamburger menu (drawer overlay)" - "Mobile drawer: Full-height overlay with backdrop" - Close drawer on route change (mobile) # State Management Patterns state_management: - Use Svelte stores for UI state only (not business data) - Business data comes from API (no client-side caching beyond DaisyUI) - "Stores: auth, layout, period" - "localStorage keys: headroom_access_token, headroom_refresh_token, headroom_sidebar_state, headroom_theme" - Store files go in src/lib/stores/ proposal: - Include clear Goals and Non-Goals sections - Reference the 4 personas (Superuser, Manager, Developer, Top Brass) - Align with monthly capacity planning workflow - Include data validation rules for any new entities specs: - Document all validation rules explicitly - Include RBAC permissions for each operation - Define error scenarios and expected responses - Reference existing data model (team_members, projects, allocations, actuals) - Use YYYY-MM format for month references - Each scenario must be testable (clear GIVEN/WHEN/THEN) design: - Include database schema changes (migrations needed) - Define API endpoints with request/response examples - Specify caching strategy (keys, TTL, invalidation rules) - Include UI/UX considerations for SvelteKit + DaisyUI - Document any new dependencies - Document test approach for each capability tasks: - Organize by capability (not by layer) - "Each capability has 4 phases: Test (Red) → Implement (Green) → Refactor → Document" - Break implementation into individual scenarios - Include explicit test tasks (write pending, enable one by one) - Include API documentation updates as tasks - Order capabilities by dependency and business priority # Domain knowledge context: | ## Project Overview Headroom is a resource planning and capacity management tool for engineering managers. It replaces error-prone spreadsheets with structured capacity planning, resource allocation, and utilization tracking. ## Core Workflow (Monthly Cycle) 1. **Capacity Planning** - Define team availability (holidays, PTO, working days) 2. **Project Setup** - Track projects through lifecycle with approved estimates 3. **Resource Allocation** - Allocate hours per person per project per month 4. **Actuals Tracking** - Log actual hours worked and compare to planned ## Personas & Permissions - **Superuser:** Full access (setup, config, all projects, all teams) - **Manager:** Create/edit own projects, allocate own team, view all projects read-only - **Developer:** View own allocations, log own hours, view assigned projects - **Top Brass:** View all reports read-only (forecasts, utilization, costs) ## Key Business Rules - Availability: 0 (unavailable), 0.5 (half day), 1.0 (full day) - Project allocation indicators: GREEN (100% ±5%), YELLOW (<95%), RED (>105%) - Monthly aggregate for actuals (not daily) - Untracked resource for external team time (not billed) - Validation: Cannot allocate to "Done" or "Cancelled" projects ## Data Model Summary - team_members: id (UUID), name, role_id, hourly_rate, active - projects: id (UUID), code, title, status_id, type_id, approved_estimate, forecasted_effort (JSON) - allocations: id (UUID), project_id, team_member_id, month (YYYY-MM), allocated_hours - actuals: id (UUID), project_id, team_member_id, month (YYYY-MM), hours_logged - roles, project_statuses, project_types: Master data tables - holidays, ptos: Calendar data ## Deferred to Phase 2 - Real-time notifications (WebSocket) - PDF/CSV exports - Background jobs (Laravel Queue) - Audit logging - Multi-tenancy - Time-tracking tool integration