Phase 8 Validation Checklist
> Historical implementation snapshot, dated 2026-10-02 (notice added 2026-11-02). This checklist is historical implementation evidence. It is not a canonical architecture owner, is outside the governed documentation structure and authority map (see PRR-048 in the readiness register), and must not be read as the current status. Its "Ready for validation" status describes 2 October 2026 only; no manual validation, STAGING deployment or production deployment is recorded here as completed. "Principal" in this document is superseded terminology for User; persisted identifiers such as principal_id remain unchanged until a separately authorised migration (legacy identifiers). Current implementation evidence is in implementation status; unresolved readiness items are in the readiness register.
Objective: Verify all dashboard UI enhancements (drag-drop, resize, persistence) work correctly end-to-end.
Date: 2026-10-02 Status: Ready for validation
Features to Validate
1. Space-Making Drag-and-Drop ✓ Code Complete
- [ ] **Drag initiation**: Click and hold item → opacity drops to 0.4
- [ ] **Ghost preview**: Fixed-position clone follows cursor while dragging
- [ ] **Proposed position (top half)**: Cursor in top half of target → dragged item inserts before target
- [ ] **Proposed position (bottom half)**: Cursor in bottom half of target → dragged item inserts after target
- [ ] **Live repositioning**: Other items shift in real-time to make space (no additional markers)
- [ ] **Drop acceptance**: Release mouse → `isDropAccepted = true`, new order persisted to DB
- [ ] **Drag cancel**: Move cursor outside container → release → reverts to `originalOrder`
- [ ] **Persistence confirmation**: Browser DevTools Network tab shows POST to `/presentations/preference/order` with 200 OK
Test Scenario:
1. Navigate to /capabilities/[id]
2. Drag middle item down to bottom position
3. Watch other items shift to show proposed position
4. Release → item stays at bottom
5. Reload page → verify item still at bottom
6. Repeat with drag outside container (release) → item reverts to original position
2. Resize Handle (Width via Grid Span) ✓ Code Complete
- [ ] **Handle visibility**: Bottom-right corner gradient triangle visible on cards
- [ ] **Cursor change**: Hover over handle → cursor becomes `nwse-resize`
- [ ] **Drag right**: Increases grid column span (2→3→4→6)
- [ ] **Drag left**: Decreases grid column span (6→4→3→2)
- [ ] **Minimum span**: Never goes below 2 columns
- [ ] **Maximum span**: Never exceeds 6 columns
- [ ] **Persistence**: Resize width → reload → width persisted
- [ ] **Visual feedback**: Card shows `resizing` class with shadow while dragging
Test Scenario:
1. Hover over card's bottom-right corner → see gradient triangle
2. Click and drag right → span increases smoothly
3. Drag left → span decreases
4. Release → calls persistSpan()
5. Reload page → card maintains new span
3. Resize Handle (Height via Min-Height) ✓ Code Complete
- [ ] **Drag down**: Y-axis movement increases min-height
- [ ] **Drag up**: Y-axis movement decreases min-height
- [ ] **Minimum height**: Never goes below 12rem
- [ ] **Height calculation**: Converts pixel delta to rem units (÷16)
- [ ] **Persistence**: Resize height → reload → height persisted
- [ ] **Effective height**: Backend provides `effective_height` in rem from database
Test Scenario:
1. Click and drag bottom-right corner down → min-height increases
2. Drag up → min-height decreases (min 12rem)
3. Release → calls persistHeight()
4. Reload page → height remains at set size
5. Verify via F12 DevTools: <article> has style="min-height: XXrem"
4. Database Persistence (All Three Endpoints) ✓ Code Complete
Order Persistence
- [ ] **Endpoint**: `POST /presentations/preference/order`
- [ ] **Payload**: `{ order: [{type_id, order}, ...], capability_instance_id }`
- [ ] **Database**: Creates/updates `USER_PRESENTATION_PREFERENCE_TYPE` object
- [ ] **Field**: Stores in `constituent_preferences[].preferred_order`
- [ ] **Response**: 200 OK with `{status: 'persisted'}`
Span Persistence
- [ ] **Endpoint**: `POST /presentations/preference/span`
- [ ] **Payload**: `{ type_id, preferred_span, capability_instance_id }`
- [ ] **Database**: Updates `constituent_preferences[{type_id}].preferred_span`
- [ ] **Valid values**: 2, 3, 4, 6 only
- [ ] **Response**: 200 OK
Height Persistence
- [ ] **Endpoint**: `POST /presentations/preference/height`
- [ ] **Payload**: `{ type_id, preferred_height, capability_instance_id }`
- [ ] **Database**: Updates `constituent_preferences[{type_id}].preferred_height`
- [ ] **Valid values**: Integer ≥ 12 (rem units)
- [ ] **Response**: 200 OK
Database Validation:
-- Query stored preferences (staging database only)
SELECT id, value FROM objects
WHERE type_id = '8c4f6b8c-3d2e-4f10-8a11-223344556692'
AND business_id = '[business_id]'
LIMIT 1
Check that value.constituent_preferences contains:- `preferred_order` (0, 1, 2, ...)
- `preferred_span` (2, 3, 4, or 6)
- `preferred_height` (≥ 12)
5. Preference Service Loading ✓ Code Complete
- [ ] **Service**: `PresentationPreferenceService::enhanceConstituentsWithPreferences()`
- [ ] **Loads from**: `USER_PRESENTATION_PREFERENCE_TYPE` objects in database
- [ ] **Merges**: Default values (span=2, order=index, height=12) with stored preferences
- [ ] **Sort order**: Returns constituents sorted by `preferred_order`
- [ ] **Backwards compatible**: Missing preferences default to system defaults
Test Flow:
1. Resize and reorder items
2. Close browser (or navigate away)
3. Return to page
4. Verify: Order, span, and height all restored from database
6. Backend Calculation of Effective Height ✓ Code Complete
- [ ] **Service**: `PersistedCapabilityProjector::applyLayout()`
- [ ] **Calculation**: `effective_height = max(12, preferred_height)`
- [ ] **Template receives**: `effective_height` in rem units
- [ ] **Minimum enforced**: Never less than 12rem (at backend)
- [ ] **Dynamic rendering**: Template uses `min-height: <?= $effective_height ?>rem`
Verification:
// In capability.php template
style="min-height: <?= (int) ($constituent['effective_height'] ?? 12) ?>rem"
7. Template Dynamic Styling ✓ Code Complete
- [ ] **Span**: `style="grid-column: span <?= (int) ($constituent['effective_span'] ?? 2) ?>"`
- [ ] **Height**: `style="min-height: <?= (int) ($constituent['effective_height'] ?? 12) ?>rem"`
- [ ] **Position**: `position: relative` (for resize handle absolute positioning)
- [ ] **Draggable**: `draggable="true"` on article element
- [ ] **Data attributes**: `data-preferred-span`, `data-preferred-height` populated correctly
End-to-End Test Flow
Scenario A: Drag-Drop + Persistence + Reload
1. Load /capabilities/[id]
2. Note current order: [A, B, C, D, E]
3. Drag C to bottom → order becomes [A, B, D, E, C]
4. Verify Network tab: POST /presentations/preference/order → 200 OK
5. Reload page (F5)
6. Verify order is still [A, B, D, E, C]
7. Undo: Drag C back to original position → order [A, B, C, D, E]
8. Reload → confirm reverted
Scenario B: Resize Width + Persistence + Reload
1. Load /capabilities/[id]
2. Note item A span: 2 columns
3. Drag bottom-right corner right → span increases to 4
4. Verify Network tab: POST /presentations/preference/span → 200 OK
5. Reload page (F5)
6. Verify item A still spans 4 columns
7. Drag corner left → span decreases to 2
8. Reload → confirm back to 2
Scenario C: Resize Height + Persistence + Reload
1. Load /capabilities/[id]
2. Note item B min-height: 12rem
3. Drag bottom-right corner down → height increases to 24rem
4. Verify Network tab: POST /presentations/preference/height → 200 OK
5. Reload page (F5)
6. Verify item B still at 24rem height
7. Drag corner up → height decreases to 12rem
8. Reload → confirm back to 12rem
Scenario D: Cancel Drag (No Persistence)
1. Load /capabilities/[id]
2. Start dragging item A
3. Move to different position (A shows moving)
4. Move cursor outside container while still dragging
5. Release mouse
6. Verify: Item A reverts to original position
7. Verify Network tab: No POST request sent
Database State Validation (Staging Only)
NEVER run these queries against production. Use staging database only.
-- Check if preferences exist for current user/business
SELECT * FROM objects
WHERE type_id = '8c4f6b8c-3d2e-4f10-8a11-223344556692'
AND business_id = '[your_business_id]'
LIMIT 1;
-- Should see JSON like:
-- {
-- "principal_id": "...",
-- "business_id": "...",
-- "capability_instance_id": "...",
-- "constituent_preferences": [
-- {
-- "type_id": "...",
-- "preferred_order": 0,
-- "preferred_span": 4,
-- "preferred_height": 18
-- },
-- ...
-- ]
-- }
Browser Console Validation
Open DevTools Console (F12) and verify these logs:
On Page Load
Dashboard: Initializing (capability: [uuid])
Dashboard: Initialization complete
On Drag Start
Drag started on: [type_id]
During Drag Over (should log continuously)
Proposed position: dragged item shown at new location
On Drop
Persisting order: [array of type_id + order]
Order persistence response: 200
Order persisted: {status: "persisted"}
On Drag Cancel (outside container)
Drag cancelled - reverted to original order
Drag ended
On Resize
Resize started: [type_id] size: 2
Resizing: 3 columns, 18.0 rem height
Span persistence response: 200
Span persisted: {status: "persisted"}
Height persistence response: 200
Height persisted: {status: "persisted"}
Resize ended: [type_id] 3 columns
Known Constraints
1. Minimum Span: 2 columns (hard limit, never below) 2. Maximum Span: 6 columns (grid has exactly 6 columns on desktop) 3. Minimum Height: 12rem (approximately 192px) 4. Viewport Responsiveness:
- Desktop (≥1200px): 6 columns
- Tablet (721-1199px): 4 columns
- Mobile (≤720px): 2 columns
5. Grid Gap: 1rem (spacing between items remains constant)
Sign-Off Criteria
| Criterion | Met? | |-----------|------| | Drag-drop shows proposed position in real-time | [ ] | | Resizing works for both width and height | [ ] | | All persistence endpoints return 200 OK | [ ] | | Preferences survive page reload | [ ] | | Cancel drag reverts to original order | [ ] | | No console errors or warnings | [ ] | | Database stores all three preference types | [ ] | | Backend calculation matches frontend resize | [ ] | | Responsive grid maintains correct column count | [ ] | | All interactions persist to staging database | [ ] |
Deployment Readiness
Phase 8 is COMPLETE when:
- ✅ All code changes committed to `main`
- ✅ All validation checklist items pass
- ✅ No regressions in existing Phase 7 functionality
- ✅ Database schema supports all preference types
- ✅ Staging deployment tested and verified
- ✅ No breaking changes to user data model
Next Phase: Phase 9 (Relationship Instantiation) - deferred per project plan