# SAG MODULE IMPLEMENTATION PLAN ## Professional Sub-Agent Task Breakdown **Document Version:** 2.0 **Created:** 2026-01-30 **Module Location:** `/app/modules/sag/` **API Path:** `/api/v1/cases` **Database Prefix:** `sag_` --- ## EXECUTIVE SUMMARY The Sag (Case) Module is the **process backbone** of BMC Hub. It implements a universal entity model where tickets, tasks, and (eventually) orders are all represented as Cases differentiated by: - **Relations** (directional, transitive connections between cases) - **Tags** (workflow state and categorization) - **Attached modules** (billing, hardware, etc.) This plan provides a phased, task-based approach to complete the implementation with architectural constraints preserved. --- ## 1️⃣ CURRENT STATE ASSESSMENT ### ✅ What's Already Implemented #### Database Schema (COMPLETE) - ✅ `sag_sager` table (cases) - `/migrations/001_init.sql` - Binary status: `åben` / `lukket` (constraint enforced) - `template_key` as nullable field (creation-time only) - `deleted_at` for soft-deletes - Proper indexes on customer_id, status, ansvarlig_bruger_id - Auto-update trigger for `updated_at` - ✅ `sag_relationer` table (relations) - Directional: `kilde_sag_id` → `målsag_id` - `relationstype` for categorization - Soft-delete support - Constraint preventing self-relations - ✅ `sag_tags` table (tags) - `tag_navn` for categorization - `state` field: `open` / `closed` (constraint enforced) - `closed_at` timestamp - Soft-delete support - ✅ `sag_kontakter` table (case-contact links) - `/migrations/002_sag_contacts_relations.sql` - Many-to-many with roles - ✅ `sag_kunder` table (case-customer links) - Many-to-many with roles #### Backend API (MOSTLY COMPLETE) ✅ **Cases CRUD** - `router.py` lines 1-671 - `GET /api/v1/cases` - List with filters (status, tag, customer_id, ansvarlig) - `POST /api/v1/cases` - Create case - `GET /api/v1/cases/{id}` - Get case details - `PATCH /api/v1/cases/{id}` - Update case - `DELETE /api/v1/cases/{id}` - Soft-delete ✅ **Relations CRUD** - `GET /api/v1/cases/{id}/relations` - List relations with titles - `POST /api/v1/cases/{id}/relations` - Create relation - `DELETE /api/v1/cases/{id}/relations/{relation_id}` - Soft-delete ✅ **Tags CRUD** - `GET /api/v1/cases/{id}/tags` - List tags - `POST /api/v1/cases/{id}/tags` - Add tag (with state support) - `DELETE /api/v1/cases/{id}/tags/{tag_id}` - Soft-delete ✅ **Contact/Customer Links** - `POST /api/v1/cases/{id}/contacts` - Link contact - `GET /api/v1/cases/{id}/contacts` - List contacts - `DELETE /api/v1/cases/{id}/contacts/{contact_id}` - Remove contact - `POST /api/v1/cases/{id}/customers` - Link customer - `GET /api/v1/cases/{id}/customers` - List customers - `DELETE /api/v1/cases/{id}/customers/{customer_id}` - Remove customer ✅ **Search** - `GET /api/v1/search/cases` - Full-text search - `GET /api/v1/search/customers` - Customer search - `GET /api/v1/search/contacts` - Contact search #### Frontend Views (MOSTLY COMPLETE) ✅ **List View** - `frontend/views.py` - `/cases` and `/sag` routes (dual paths) - Filter by status, tag, customer_id - Fetches statuses and tags for filters ✅ **Detail View** - `/cases/{id}` and `/sag/{id}` routes - Shows case, tags, relations, customer - Fetches contacts and customers (partially implemented) ✅ **Create Form** - `/cases/new` and `/sag/new` routes - Template exists (`create.html`) ❌ **Edit Form** - Template exists (`edit.html`) but no backend route #### Templates (COMPLETE) ✅ All four templates exist: - `index.html` - Case list with Nordic Top design, filters, dark mode - `detail.html` - Case details with relations, tags, info sections - `create.html` - Create form with customer search, validation - `edit.html` - Edit form (needs backend route) ### 🔴 Architectural Violations Found #### VIOLATION 1: Duplicate API Paths **Issue:** Router has both `/sag/*` and `/cases/*` endpoints doing identical things. - Lines 14-157: `/sag` prefix endpoints - Lines 327-671: `/cases` prefix endpoints (duplicates) **Fix Required:** Remove `/sag/*` endpoints, keep only `/cases/*` (architectural standard). #### VIOLATION 2: Missing Tag State Management **Issue:** Tag closing endpoint missing. - `PATCH /api/v1/cases/{id}/tags/{tag_id}` should allow closing tags (set `state='closed'`, `closed_at=NOW()`) - Currently only delete (soft) exists **Fix Required:** Add tag state update endpoint. #### VIOLATION 3: Missing Edit Route **Issue:** `edit.html` template exists but no backend route to serve it. **Fix Required:** Add `GET /cases/{id}/edit` view route. ### ⚠️ What Needs Enhancement 1. **Tag State Workflow** - Endpoint to close tags (not delete) 2. **Edit View Route** - Backend route for edit form 3. **Relation Type Validation** - Should validate allowed relation types 4. **Bulk Operations** - Close multiple tags, archive cases 5. **Activity Log** - Track who changed what (future phase) 6. **Order Integration** - Link cases to orders (future phase) --- ## 2️⃣ PHASE OVERVIEW ### Phase 1: Database Schema Validation (30 min) - Verify all tables match architectural constraints - Add any missing indexes - Validate soft-delete coverage ### Phase 2: Backend API Enhancement (2-3 hours) - Remove duplicate `/sag/*` endpoints - Add tag state management endpoint - Add edit view route - Improve error handling - Add relation type validation ### Phase 3: Frontend Enhancement (2-3 hours) - Wire up edit form - Add tag closing UI - Improve relation visualization - Add bulk operations UI - Enhance mobile responsiveness ### Phase 4: Order Integration Planning (1 hour) - Define order-case relation types - Document order creation workflow - Plan order table schema - Define API contract ### Phase 5: Documentation (1 hour) - Update module README - Create API documentation - Document relation types - Create workflow examples ### Phase 6: QA & Testing (1-2 hours) - Unit tests for CRUD operations - Integration tests for relations - Frontend E2E tests - Soft-delete verification - Module disable/enable test **Total Estimate:** 8-12 hours --- ## 3️⃣ SUB-AGENT TASK BREAKDOWN --- ### PHASE 1: DATABASE SCHEMA VALIDATION #### DB-001: Schema Compliance Audit **Assigned:** DB-Agent **Estimated Time:** 30 minutes **Dependencies:** None **Description:** Audit all `sag_*` tables against architectural constraints. Verify: - Binary status constraint on `sag_sager` - Tag state constraint on `sag_tags` - Soft-delete columns present - No missing indexes - Trigger functions working **Inputs:** - `/migrations/001_init.sql` - `/migrations/002_sag_contacts_relations.sql` - Database connection **Outputs:** - Validation report (pass/fail for each constraint) - SQL script for any missing indexes/constraints **Done Criteria:** - All constraints match architectural rules - All soft-delete columns present - All indexes exist and performant --- ### PHASE 2: BACKEND API ENHANCEMENT #### BE-001: Remove Duplicate `/sag/*` Endpoints **Assigned:** Backend-Agent **Estimated Time:** 45 minutes **Dependencies:** None **Description:** Remove duplicate endpoints with `/sag` prefix (lines 14-330). Keep only `/cases` prefix endpoints. Update any internal references. **Inputs:** - `/app/modules/sag/backend/router.py` **Outputs:** - Modified `router.py` with only `/cases` endpoints - Updated imports if needed **Done Criteria:** - No `/sag` prefix endpoints remain - All functionality preserved under `/cases` - No broken internal references --- #### BE-002: Add Tag State Management Endpoint **Assigned:** Backend-Agent **Estimated Time:** 45 minutes **Dependencies:** BE-001 **Description:** Add `PATCH /api/v1/cases/{id}/tags/{tag_id}` endpoint to close tags without deleting. Should: - Accept `{"state": "closed"}` payload - Set `closed_at = NOW()` - NOT set `deleted_at` (tags are closed, not deleted) - Return updated tag **Inputs:** - `/app/modules/sag/backend/router.py` - `sag_tags` table schema **Outputs:** - New endpoint in `router.py` - Proper error handling **Done Criteria:** - Endpoint closes tag (state='closed', closed_at set) - Returns 404 if tag not found - Doesn't soft-delete - Logs action with emoji prefix --- #### BE-003: Add Edit View Route **Assigned:** Backend-Agent **Estimated Time:** 30 minutes **Dependencies:** BE-001 **Description:** Add `GET /cases/{id}/edit` route in `frontend/views.py` to serve the edit form. Should fetch case and render `edit.html` template. **Inputs:** - `/app/modules/sag/frontend/views.py` - `/app/modules/sag/templates/edit.html` **Outputs:** - New route in `views.py` - Template receives case data **Done Criteria:** - Route returns edit form - Form pre-populated with case data - Returns 404 if case not found --- #### BE-004: Add Relation Type Validation **Assigned:** Backend-Agent **Estimated Time:** 45 minutes **Dependencies:** BE-001 **Description:** Add validation for allowed relation types when creating relations. Define allowed types: - `derived` (spawned from) - `blocks` (prevents progress) - `executes` (performs work for) - `relates_to` (generic link) Reject other types with 400 error. **Inputs:** - `/app/modules/sag/backend/router.py` (relation creation endpoint) **Outputs:** - Updated `POST /api/v1/cases/{id}/relations` with validation - Error message listing allowed types **Done Criteria:** - Only allowed types accepted - 400 error with clear message for invalid types - Logged validation failures --- #### BE-005: Improve Error Handling **Assigned:** Backend-Agent **Estimated Time:** 45 minutes **Dependencies:** BE-001, BE-002, BE-003, BE-004 **Description:** Standardize error responses across all endpoints: - Use consistent HTTPException format - Include helpful error messages - Log all errors with context - Return proper status codes **Inputs:** - All endpoint functions in `router.py` and `views.py` **Outputs:** - Consistent error handling pattern - Improved logging **Done Criteria:** - All errors return proper status codes - Error messages are user-friendly - All errors logged with context - No uncaught exceptions --- ### PHASE 3: FRONTEND ENHANCEMENT #### FE-001: Wire Up Edit Form Backend Connection **Assigned:** Frontend-Agent **Estimated Time:** 45 minutes **Dependencies:** BE-003 **Description:** Complete the edit form JavaScript to: - Load case data into form - Submit to `PATCH /api/v1/cases/{id}` - Handle success/error responses - Redirect to detail view on success **Inputs:** - `/app/modules/sag/templates/edit.html` - Backend edit route - PATCH endpoint **Outputs:** - Functional edit form with submission - Success/error feedback - Redirect on success **Done Criteria:** - Form loads case data - Submission calls PATCH endpoint - Success redirects to `/cases/{id}` - Errors displayed to user --- #### FE-002: Add Tag Closing UI **Assigned:** Frontend-Agent **Estimated Time:** 60 minutes **Dependencies:** BE-002 **Description:** Add UI to close tags (not delete) in detail view: - Add "Close Tag" button next to each open tag - Call `PATCH /api/v1/cases/{id}/tags/{tag_id}` with `state='closed'` - Update UI to show closed state (greyed out, strikethrough) - Keep closed tags visible in UI **Inputs:** - `/app/modules/sag/templates/detail.html` - Tag closing endpoint **Outputs:** - Close button on each open tag - Visual distinction for closed tags - AJAX call to close endpoint **Done Criteria:** - Closed tags remain visible - Visual feedback on state change - No page reload needed - Error handling for failures --- #### FE-003: Improve Relation Visualization **Assigned:** Frontend-Agent **Estimated Time:** 90 minutes **Dependencies:** None **Description:** Enhance relation display in detail view: - Group relations by type (derived, blocks, executes, relates_to) - Show directionality clearly (this case → target vs. source → this case) - Add visual indicators (arrows, colors) - Make relation targets clickable links **Inputs:** - `/app/modules/sag/templates/detail.html` - Relation data from backend **Outputs:** - Updated relation section with grouping - Visual indicators for direction - Clickable links to related cases **Done Criteria:** - Relations grouped by type - Direction clear (outgoing vs incoming) - All targets are clickable - Mobile-responsive --- #### FE-004: Add Bulk Operations UI **Assigned:** Frontend-Agent **Estimated Time:** 90 minutes **Dependencies:** BE-002 **Description:** Add bulk operations to list view: - Checkboxes to select multiple cases - "Close Selected" button (set status='lukket') - "Archive Selected" button (soft-delete) - Confirmation dialogs **Inputs:** - `/app/modules/sag/templates/index.html` - Update/delete endpoints **Outputs:** - Selection UI in list view - Bulk action buttons - Confirmation dialogs **Done Criteria:** - Multiple cases selectable - Bulk close works - Bulk archive works (soft-delete) - Confirmation required - Success feedback shown --- #### FE-005: Enhance Mobile Responsiveness **Assigned:** Frontend-Agent **Estimated Time:** 60 minutes **Dependencies:** FE-001, FE-002, FE-003, FE-004 **Description:** Ensure all views work on mobile: - Stack filters vertically on small screens - Make tables scrollable or card-based - Touch-friendly buttons (min 44px) - Collapsible sections for detail view **Inputs:** - All template files **Outputs:** - Updated CSS with media queries - Mobile-optimized layouts **Done Criteria:** - All views usable on 375px width - No horizontal scroll needed - Touch targets large enough - Text readable without zoom --- ### PHASE 4: ORDER INTEGRATION PLANNING #### INT-001: Define Order-Case Relation Model **Assigned:** Integration-Agent **Estimated Time:** 30 minutes **Dependencies:** None **Description:** Document how orders integrate with cases: - Orders CAN be created independently - Orders MUST be linkable to one or more cases - When order created from case → create relation - Define relation type for order links (e.g., `fulfills`, `invoices_for`) **Inputs:** - Architectural constraints document - Current relation types **Outputs:** - Design document: `docs/ORDER_CASE_INTEGRATION.md` - Relation type definitions - Workflow diagrams **Done Criteria:** - Clear definition of order independence - Relation type defined - Creation workflow documented - No violation of "orders are not cases" rule --- #### INT-002: Plan Order Table Schema **Assigned:** Integration-Agent **Estimated Time:** 45 minutes **Dependencies:** INT-001 **Description:** Design order tables with case linking: - `orders` table (id, title, description, status, etc.) - `order_case_relations` table (order_id, case_id, relation_type) - NO embedding of case logic in orders - Soft-delete everywhere **Inputs:** - Order-case relation model (INT-001) - Existing `sag_*` schema **Outputs:** - SQL migration draft: `migrations/XXX_orders.sql` - Schema documentation **Done Criteria:** - Order table defined - Many-to-many relation to cases - No parent/child embedding - Soft-delete columns present --- #### INT-003: Define Order API Contract **Assigned:** Integration-Agent **Estimated Time:** 45 minutes **Dependencies:** INT-002 **Description:** Define order API endpoints: - `GET /api/v1/orders` - List orders - `POST /api/v1/orders` - Create order (optionally from case) - `POST /api/v1/orders/{id}/cases` - Link to case - `GET /api/v1/cases/{id}/orders` - Get orders for case **Inputs:** - Order schema (INT-002) - Case API patterns **Outputs:** - API specification document - Example requests/responses **Done Criteria:** - All CRUD operations defined - Case linking endpoints defined - Consistent with case API patterns - Examples provided --- ### PHASE 5: DOCUMENTATION #### DOC-001: Update Module README **Assigned:** Docs-Agent **Estimated Time:** 30 minutes **Dependencies:** BE-001 through BE-005 **Description:** Update `/app/modules/sag/README.md` with: - Current implementation status - All API endpoints with examples - Relation types and meanings - Tag workflow explanation - Architectural constraints summary **Inputs:** - Current implementation - Backend router - Templates **Outputs:** - Updated `README.md` **Done Criteria:** - All endpoints documented - Examples provided - Architectural rules explained - Easy to understand for new developers --- #### DOC-002: Create API Documentation **Assigned:** Docs-Agent **Estimated Time:** 45 minutes **Dependencies:** BE-001 through BE-005 **Description:** Create OpenAPI-style documentation for all endpoints: - Request/response schemas - Status codes - Error responses - Example curl commands **Inputs:** - Backend router - Pydantic models (if any) **Outputs:** - `docs/SAG_API_REFERENCE.md` **Done Criteria:** - All endpoints documented - Schemas defined - Examples provided - Status codes listed --- #### DOC-003: Document Relation Types **Assigned:** Docs-Agent **Estimated Time:** 30 minutes **Dependencies:** BE-004 **Description:** Create reference guide for relation types: - `derived` - Target case spawned from source - `blocks` - Source blocks target from progressing - `executes` - Target performs work for source - `relates_to` - Generic association Include use cases and examples. **Inputs:** - Relation type validation (BE-004) - Existing use patterns **Outputs:** - `docs/CASE_RELATION_TYPES.md` **Done Criteria:** - All types explained - Use cases provided - Examples given - Directionality clear --- #### DOC-004: Create Workflow Examples **Assigned:** Docs-Agent **Estimated Time:** 45 minutes **Dependencies:** DOC-001, DOC-002, DOC-003 **Description:** Document common workflows: 1. Customer calls → create support case → derive hardware order case → execute fulfillment case 2. Billing cycle → create invoice case → relate to completed support cases 3. Project → create project case → derive task cases → track via tags **Inputs:** - All documentation - Current implementation **Outputs:** - `docs/SAG_WORKFLOWS.md` **Done Criteria:** - At least 3 workflows documented - Step-by-step instructions - API calls shown - Relation chains visualized --- ### PHASE 6: QA & TESTING #### QA-001: Backend CRUD Tests **Assigned:** QA-Agent **Estimated Time:** 90 minutes **Dependencies:** BE-001 through BE-005 **Description:** Write integration tests for all CRUD operations: - Create case - Read case - Update case - Delete case (soft) - Verify deleted_at set correctly **Inputs:** - Backend router - Test database **Outputs:** - Test file: `tests/test_sag_crud.py` - At least 20 test cases **Done Criteria:** - All CRUD operations tested - Soft-delete verified - Error cases tested - All tests pass --- #### QA-002: Relation Tests **Assigned:** QA-Agent **Estimated Time:** 60 minutes **Dependencies:** BE-001, BE-004 **Description:** Test relation operations: - Create relation between cases - List relations (both directions) - Validate relation type validation - Delete relation (soft) - Prevent self-relations **Inputs:** - Relation endpoints - Test database **Outputs:** - Test file: `tests/test_sag_relations.py` - At least 10 test cases **Done Criteria:** - Create/delete tested - Type validation tested - Self-relation prevention tested - Directionality correct --- #### QA-003: Tag Workflow Tests **Assigned:** QA-Agent **Estimated Time:** 60 minutes **Dependencies:** BE-002 **Description:** Test tag lifecycle: - Add tag to case - Close tag (state='closed', closed_at set) - Verify closed tags not deleted - Delete tag (soft) - List tags (filter by state) **Inputs:** - Tag endpoints - Test database **Outputs:** - Test file: `tests/test_sag_tags.py` - At least 8 test cases **Done Criteria:** - Add/close/delete tested - State transitions verified - Soft-delete vs close distinction tested - All tests pass --- #### QA-004: Frontend E2E Tests **Assigned:** QA-Agent **Estimated Time:** 90 minutes **Dependencies:** FE-001 through FE-005 **Description:** Test frontend user flows: - Navigate to case list - Filter by status/tag - Create new case - View case details - Edit case - Add/close tag - Add relation - Delete case **Inputs:** - All frontend templates - Browser automation tool (Playwright/Selenium) **Outputs:** - Test file: `tests/test_sag_frontend.py` - At least 10 E2E scenarios **Done Criteria:** - All major flows tested - Form submissions work - Navigation works - Error handling tested --- #### QA-005: Module Disable/Enable Test **Assigned:** QA-Agent **Estimated Time:** 30 minutes **Dependencies:** QA-001, QA-002, QA-003, QA-004 **Description:** Test module deactivation: 1. Create test cases/relations/tags 2. Disable module (enabled=false in module.json) 3. Verify endpoints return 404 4. Verify database data intact 5. Re-enable module 6. Verify data still accessible **Inputs:** - Module loader - Test database **Outputs:** - Test file: `tests/test_sag_module_lifecycle.py` **Done Criteria:** - Disable prevents access - Data preserved - Re-enable restores access - No data loss --- ## 4️⃣ DEPENDENCY GRAPH ### Sequential Dependencies (Critical Path) ``` DB-001 (30m) ↓ BE-001 (45m) ← MUST complete before any backend work ↓ ├→ BE-002 (45m) ├→ BE-003 (30m) ├→ BE-004 (45m) └→ BE-005 (45m) ← Touches all other BE tasks ↓ ┌────┴────┐ ↓ ↓ FE-001 QA-001 (90m) (45m) ↓ ↓ QA-002 (60m) FE-002 ↓ (60m) QA-003 (60m) ↓ ↓ FE-003 QA-004 (90m) (90m) ↓ ↓ QA-005 (30m) FE-004 (90m) ↓ FE-005 (60m) ``` ### Parallel Opportunities **Wave 1 (after BE-001):** - BE-002, BE-003, BE-004 can run in parallel - Saves ~90 minutes **Wave 2 (after BE-005):** - FE-001, FE-003, QA-001, QA-002 can run in parallel - DOC-001, DOC-002, DOC-003 can run in parallel - Saves ~2 hours **Wave 3 (after FE-004):** - FE-005, QA-004, DOC-004 can run in parallel - Saves ~1 hour **Wave 4 (order integration):** - INT-001, INT-002, INT-003 can run in parallel (independent of other work) ### Critical Path Time If executed sequentially: **~12 hours** If parallelized optimally: **~6-7 hours** ### Where Orders Integrate **Order Integration is PARALLEL to Case Enhancement:** - INT-001, INT-002, INT-003 can start immediately - Do NOT block case module completion - Orders gain meaning when linked to cases via relations - No architectural changes to cases needed **Order-Case Interface:** ``` Cases Module (Complete) ↓ Orders Module (New) ↓ order_case_relations table ↓ Relation type: "fulfills" / "invoices_for" ``` --- ## 5️⃣ VALIDATION CHECKLIST ### Phase 1: Database Schema - [ ] Binary status constraint verified (`åben` / `lukket`) - [ ] Tag state constraint verified (`open` / `closed`) - [ ] All soft-delete columns present - [ ] All indexes exist and performant - [ ] Triggers working (updated_at auto-update) - [ ] No foreign key errors - [ ] Relation self-reference prevented ### Phase 2: Backend API - [ ] No duplicate `/sag/*` endpoints exist - [ ] Only `/cases` prefix used - [ ] Tag closing endpoint works (PATCH tags/{id}) - [ ] Edit view route serves form - [ ] Relation type validation enforces allowed types - [ ] All endpoints return consistent error format - [ ] All errors logged with context - [ ] Soft-deletes set `deleted_at`, not physical delete ### Phase 3: Frontend - [ ] Edit form loads case data - [ ] Edit form submits to PATCH endpoint - [ ] Tag closing UI doesn't delete tags - [ ] Closed tags remain visible (greyed out) - [ ] Relations grouped by type - [ ] Directionality clear (in/out) - [ ] Bulk operations work - [ ] All views mobile-responsive (375px) ### Phase 4: Order Integration - [ ] Order-case relation model documented - [ ] Orders can be created independently - [ ] Orders linkable to multiple cases - [ ] No case logic embedded in orders - [ ] Order table schema has soft-deletes - [ ] API contract consistent with case API ### Phase 5: Documentation - [ ] README updated with current state - [ ] All endpoints documented - [ ] Relation types explained - [ ] Workflows documented - [ ] Examples provided ### Phase 6: Testing - [ ] All CRUD operations tested - [ ] Relation creation/deletion tested - [ ] Tag state transitions tested - [ ] Frontend flows tested - [ ] Module disable/enable tested - [ ] Data preserved after disable - [ ] All tests pass --- ## ARCHITECTURAL CONSTRAINT RE-VALIDATION After each phase, validate these rules: ### Core Entity Rule - ✅ Only one entity: Case - ✅ Tickets/tasks are cases with different tags/relations - ⚠️ Orders are transactional objects, NOT cases (separate table) ### Orders Exception - ⚠️ Orders can be created independently (INT-002) - ⚠️ Orders linkable to cases via relations (INT-002) - ⚠️ No case logic in order workflow (INT-001) ### Template Usage - ✅ `template_key` only used at creation - ✅ No business logic depends on it ### Case Status - ✅ Binary only: `åben` / `lukket` - ✅ Workflow state via tags ### Tags - ✅ Represent work to be done - ✅ Have state: `open` / `closed` - ✅ Never deleted when completed (closed instead) - ✅ Closing = completion of responsibility ### Relations - ✅ First-class data (own table) - ✅ Directional (kilde → mål) - ✅ Transitive (chains allowed) - ✅ No parent/child duality in stored data - ✅ UI derives parent/child views ### Deletion Policy - ✅ All deletes are soft-deletes - ✅ `deleted_at` mandatory everywhere ### Modeling Rule - ✅ If you think you need a new table → use relations instead - ⚠️ Exception: Orders (per architectural rule) --- ## TEST SCENARIOS BY PHASE ### Phase 1: Database 1. Insert case with status='other' → constraint violation 2. Insert tag with state='pending' → constraint violation 3. Create relation with kilde_sag_id = målsag_id → constraint violation 4. Soft-delete case → deleted_at set, not physical delete 5. Query cases WHERE deleted_at IS NULL → deleted cases excluded ### Phase 2: Backend 1. Call `/sag/cases` → 404 (removed endpoint) 2. Call `/cases` → 200 with case list 3. PATCH tag state to 'closed' → closed_at set, deleted_at NULL 4. DELETE tag → deleted_at set, state unchanged 5. POST relation with type='invalid' → 400 error 6. POST relation with type='derived' → 201 created ### Phase 3: Frontend 1. Edit case form → loads current values 2. Submit edit form → case updated, redirect to detail 3. Click "Close Tag" → tag state='closed', remains visible, greyed 4. Add relation → dropdown shows allowed types 5. Bulk close cases → all selected closed 6. View on 375px screen → no horizontal scroll ### Phase 4: Order Integration 1. Create order without case → success 2. Link order to case → relation created 3. View case → shows linked orders 4. View order → shows linked cases 5. Delete case → order remains (independent) ### Phase 5: Documentation 1. Developer reads README → understands architecture 2. Developer reads API docs → can call endpoints 3. Developer reads relation types → understands usage 4. Developer reads workflows → can implement feature ### Phase 6: QA 1. All CRUD operations → success 2. Soft-delete case → data preserved 3. Module disabled → endpoints return 404 4. Module re-enabled → data accessible again 5. Tag closed → not deleted 6. Relation created → directionality correct --- ## ANTI-PATTERNS TO AVOID ### DO NOT: - ❌ Create `tickets` or `tasks` tables (use cases with tags) - ❌ Embed workflow logic in orders - ❌ Introduce parent/child trees outside relations - ❌ Add state machines (use tags) - ❌ Optimize prematurely (no Redis, no caching yet) - ❌ Physical delete anything (always soft-delete) - ❌ Skip validation (enforce constraints) - ❌ Duplicate endpoints (one path per resource) ### DO: - ✅ Use relations to express connections - ✅ Use tags to express workflow state - ✅ Keep orders independent but relatable - ✅ Soft-delete everything - ✅ Log all actions with emoji prefixes - ✅ Return user-friendly errors - ✅ Preserve data on module disable --- ## ROLLOUT PLAN ### Development Environment 1. Complete Phases 1-3 in dev 2. Test with test data 3. Verify module disable/enable ### Staging/QA 1. Run full QA suite (Phase 6) 2. Performance test with 10k+ cases 3. Load test endpoints 4. Verify soft-deletes work at scale ### Production 1. Run schema validation (DB-001) 2. Deploy backend changes (Phase 2) 3. Deploy frontend changes (Phase 3) 4. Monitor logs for errors 5. Verify no downtime 6. Roll back if issues (soft-deletes preserve data) ### Post-Deployment 1. Monitor case creation rate 2. Track relation usage patterns 3. Identify most-used tags 4. Plan order integration (Phase 4) --- ## SUCCESS CRITERIA The Sag Module implementation is COMPLETE when: ✅ **Database** - All constraints enforced - Soft-deletes everywhere - Indexes performant ✅ **Backend** - No duplicate endpoints - Tag closing works - Relations validated - Edit route exists - Errors consistent ✅ **Frontend** - Edit form works - Tag closing UI works - Relations visualized - Bulk operations work - Mobile responsive ✅ **Order Integration** - Model documented - Schema planned - API defined - No constraint violations ✅ **Documentation** - README complete - API documented - Relation types explained - Workflows documented ✅ **Testing** - All CRUD tested - Relations tested - Tags tested - Frontend tested - Module lifecycle tested ✅ **Architectural Compliance** - One entity: Case - Orders separate but linkable - Template key not used in logic - Status binary - Tags never deleted (closed) - Relations directional - Soft-deletes everywhere - No new tables (except orders) --- ## APPENDIX A: TASK TIME SUMMARY | Phase | Task Count | Sequential Time | Parallel Time | |-------|-----------|----------------|---------------| | Phase 1: Database | 1 | 30m | 30m | | Phase 2: Backend | 5 | 3h 30m | 1h 45m | | Phase 3: Frontend | 5 | 5h 45m | 2h 30m | | Phase 4: Orders | 3 | 2h | 30m (parallel) | | Phase 5: Docs | 4 | 2h 30m | 45m (parallel) | | Phase 6: QA | 5 | 4h 30m | 2h | | **TOTAL** | **23** | **18h 45m** | **~8h** | --- ## APPENDIX B: RELATION TYPE REFERENCE | Type | Direction | Meaning | Example | |------|-----------|---------|---------| | `derived` | A → B | B spawned from A | Support call → hardware order | | `blocks` | A → B | A prevents B | Missing part → installation task | | `executes` | A → B | A performs work for B | Packing task → customer order | | `relates_to` | A ↔ B | Generic association | Related support tickets | **Validation:** Only these four types allowed in `POST /api/v1/cases/{id}/relations` --- ## APPENDIX C: TAG STATE LIFECYCLE ``` Tag Created (state='open', closed_at=NULL) ↓ [Work happens] ↓ Tag Closed (state='closed', closed_at=NOW(), deleted_at=NULL) ↓ Tag Remains Visible (greyed out in UI) ↓ [Optional: Technical cleanup] ↓ Tag Soft-Deleted (deleted_at=NOW()) ``` **Key Rule:** Closing ≠ Deleting. Tags are closed when work is done, deleted only if added by mistake. --- ## APPENDIX D: ORDER-CASE INTEGRATION EXAMPLE **Scenario:** Customer needs new laptop ``` 1. Support Call (Case ID: 1001) - Type: Case - Tags: [support, hardware] - Status: åben 2. Create Hardware Order (Order ID: 5001) - Type: Order (separate table) - Items: [Laptop, Charger] - Status: pending 3. Link Order to Case - POST /api/v1/orders/5001/cases - Body: {"case_id": 1001, "relation_type": "fulfills"} - Creates entry in order_case_relations 4. Fulfillment Task (Case ID: 1002) - Type: Case - Tags: [fulfillment] - Status: åben - Relation: 1002 executes 1001 5. Query Cases for Order - GET /api/v1/orders/5001/cases - Returns: [Case 1001] 6. Query Orders for Case - GET /api/v1/cases/1001/orders - Returns: [Order 5001] ``` **Architecture Preserved:** - Order is NOT a case - Order is independent (can exist without case) - Order gains context through case relation - Cases remain the process backbone --- ## DOCUMENT CONTROL **Version History:** | Version | Date | Author | Changes | |---------|------|--------|---------| | 1.0 | 2026-01-30 | GitHub Copilot | Initial plan | | 2.0 | 2026-01-30 | GitHub Copilot | Sub-agent task breakdown added | **Approval:** - [ ] Architect Review - [ ] Technical Lead Review - [ ] Project Manager Approval **Next Review Date:** After Phase 2 completion --- **END OF IMPLEMENTATION PLAN**