# Product Roadmap And Status **Date:** 2026-03-13 **Purpose:** Canonical source for active roadmap, open gaps, and document ownership. ## Canonical Documents - Active product and refactor backlog: this file - Estimating system design and workbook mapping: [estimating-extension-design.md](/home/hartmut/Documents/Copilot/capakraken/docs/estimating-extension-design.md) - Dispo clean-slate import design and field mapping: [dispo-import-implementation.md](/home/hartmut/Documents/Copilot/capakraken/docs/dispo-import-implementation.md) - Dispo worker ticket pack and dependency breakdown: [dispo-import-implementation-tickets.md](/home/hartmut/Documents/Copilot/capakraken/docs/dispo-import-implementation-tickets.md) - Demand/assignment migration cutover and readiness policy: [demand-assignment-migration-cutover.md](/home/hartmut/Documents/Copilot/capakraken/docs/demand-assignment-migration-cutover.md) - Strategic longer-horizon architecture direction: [v2-architecture-proposal-2026-03-11.md](/home/hartmut/Documents/Copilot/capakraken/research/v2-architecture-proposal-2026-03-11.md) - Implementation history and decisions: [LEARNINGS.md](/home/hartmut/Documents/Copilot/capakraken/LEARNINGS.md) Older plans and reviews were left in place only as archive notes so active guidance is no longer split across stale task lists. ## Current Baseline The following items were proposed in older markdown files and are already implemented enough that they should not stay on the active backlog: - Argon2-based user creation and login compatibility - Manager-restricted notification creation - Resource detail surface - Timeline virtualization - Excel import and export support - Skill-matrix import flow, AI summary generation, and related resource metadata - View preference groundwork such as shared column/view preference types and hooks - Shared dynamic-field filter and blueprint-validation parity across project and resource APIs - Blueprints list parity with shared sort, selection, and view-preference behavior - Shared dashboard widget contracts, layout normalization/migration, and registry-driven rendering - Timeline drag+selection interaction stability: `FloatingActionBar` cleared synchronously when a project-bar or single-allocation drag begins, preventing committed multi-select state from bleeding into unrelated drag operations - SSE reconnect edge-case coverage: hide-during-pending-reconnect and first-ever-connection-failure paths locked in with dedicated regression tests - Scenario module unit regression: all four scenario modules (`scenario-shared`, `scenario-apply`, `scenario-baseline`, `scenario-simulation`) covered with dedicated test files (31 tests total) ## Active Workstreams | Workstream | Status | Why It Is Still Open | Recommended Next Step | |---|---|---|---| | Estimating system | `Complete` | Full CRUD, versioning, export, planning handoff, clone/template, rate cards per client, richer version comparison, scope-to-effort rule engine, experience multipliers & shoring ratios, weekly phasing (4Dispo grid), and structured commercial terms. | — | | Demand vs assignment split | `Complete` | Legacy `Allocation` table dropped. `legacyAllocationId` columns removed. Migration tooling deleted. Compatibility facades renamed to clean domain names (`updateAllocationEntry`, `deleteAllocationEntry`, `fillOpenDemand`, `loadAllocationEntry`). `isPlaceholder` is a derived read-model property. No legacy compatibility naming remains. | — | | Widget platform refactor | `Implemented` | Widget config typing, layout normalization, registry-driven rendering, and dashboard query extraction now live behind shared/application contracts instead of ad hoc router logic. | Keep future widgets on the same registry + application-use-case pattern. | | Package-level regression tests | `Expanded` | Shared schema validation (rate-card, allocation, estimate — 32 tests), engine vacation/recurrence (29 tests), staffing capacity-analyzer (12 tests), plus existing application and dashboard tests. Scenario module regression: 31 new unit tests across all four scenario modules (`scenario-shared`, `scenario-apply`, `scenario-baseline`, `scenario-simulation`). Timeline interaction: drag+selection conflict fix (FloatingActionBar cleared on project-bar and allocation drag start) + `FloatingActionBar` render regression suite. SSE stability: 2 additional edge-case tests for hide-during-reconnect and first-ever-connection-failure paths (10 tests total). | Continue adding API-level integration tests for remaining router procedures. | | Chargeability and resource planning (Dispo v2) | `Complete` | Plans 1-5 (schema, types, SAH engine, 5 API routers, 5 admin UIs, resource/project modal extensions), Phase A (live forecast report), Phase B (target comparison + drill-down grouping), Phase D (Excel/CSV export). Phase C (SAP actuals) removed from scope. | — | ## Prioritized Backlog ### 1. Estimating foundation Build the estimating bounded context first. It is the largest product gap and the most structurally separate from the existing planning workflow. Scope: - Prisma models for estimate aggregates - typed calculation engine in `packages/engine` - estimate wizard and estimate workspace UI - rate-card and resource snapshot handling - export serializers Current state: - completed enough to use as foundation: - estimating Prisma schema - shared estimate types and schemas - engine summary helper - application-layer estimate use-cases - API estimate router registration - estimates list route and first create wizard - completed enough to use as foundation: - estimate workspace detail route with tabbed read surfaces - working-draft editor for overview, assumptions, scope, and staffing - live resource-linked staffing rows with snapshot persistence on save - completed in the current iteration: - server-side demand-line normalization before metrics - persisted live-vs-manual rate override metadata on demand lines - read/write workspace visibility for manual overrides vs live resource snapshots - completed in the current iteration: - approved-version planning handoff into downstream allocations - estimate project snapshot persistence for approval and handoff context - completed in the current iteration: - format-specific export serializers with downloadable stored artifacts and workspace previews - completed in the current iteration: - estimate clone/template feature across all layers (shared schema, application use-case, API router, UI) - rate cards per client with admin management UI - richer version comparison engine: per-item scope diffs with field-level change detection, resource snapshot rate/location diffs, chapter-grouped subtotals sorted by cost impact, margin % delta - version comparison UI: scope item detail table, resource rate diff table, chapter subtotals section, margin summary card - scope-to-effort rule engine: admin CRUD for rule sets (per_frame/per_item/flat unit modes), estimate workspace preview + apply with replace/append mode, engine with 16 tests - experience multipliers & shoring ratios: hierarchical specificity matching (chapter/location/level), rate multipliers, shoring ratio + additional effort factor, admin CRUD, estimate workspace preview + apply, engine with 23 tests - weekly phasing (4Dispo grid): ISO week-based phasing with even/front-loaded/back-loaded patterns, chapter aggregation view, heat-map coloring, estimate workspace phasing tab, engine with 30 tests - structured commercial terms: pricing model (fixed/T&M/hybrid), contingency %, discount %, payment term days, warranty months, payment milestones with validation and amount computation, integrated into financials tab, engine with 19 tests ### 2. Refactor the planning core before v2 migration The best low-risk internal work is the refactor slice that reduces duplication without forcing a full relational migration yet. Scope: - shared dynamic-field validation/filter path - typed widget config and layout migration - package-level regression tests ### 3. Prepare the demand/assignment model split This should follow the refactor slice, not precede it. The current `Allocation` model is workable short-term but is the main domain-model constraint for v2 planning and estimating integration. Current incremental slice: - completed in the current iteration: - shared `DemandRequirement` and `Assignment` contracts - application adapter that splits mixed allocation rows into explicit read models - additive timeline/allocation API read endpoints and project-context exposure for the new split - migrate primary timeline and allocations UI surfaces onto explicit `demands` / `assignments` read models - add demand-aware allocations page visibility alongside assignment-first tables - completed in the current iteration: - manual create flows now write through `createDemandRequirement` / `createAssignment` instead of the mixed `allocation.create` payload where the UI already has an explicit intent - open-demand fill flows now route through a single compatibility-aware backend command so the UI no longer resolves linked demand records or branches between demand and placeholder fill mutations - completed in the current iteration: - centralize create-side legacy-allocation compatibility wiring in application use-cases so allocation create, explicit demand/assignment create, and timeline quick-assign share the same dual-write path - centralize split-record creation from compatibility allocations behind shared application helpers so demand-only, assignment-only, and estimate-handoff create facades no longer each hand-wire `legacyAllocationId` propagation - centralize the remaining compatibility-create orchestration behind a shared legacy-allocation bundle helper so demand-only, assignment-only, and combined compatibility creates no longer duplicate the "create legacy allocation, then materialize split rows" sequence - completed in the current iteration: - centralize estimate planning-handoff compatibility creation in shared application helpers so resource-backed and fallback-placeholder handoff lines reuse the same demand/assignment dual-write paths as the rest of planning - completed in the current iteration: - migrate the resource-detail surface off legacy mixed allocation reads onto assignment-only read models - completed in the current iteration: - align allocation report/export generation with assignment read-model semantics instead of direct placeholder filtering - completed in the current iteration: - migrate dashboard demand queries onto demand-requirement and assignment semantics, with legacy allocation and `staffingReqs` fallback during the compatibility period - centralize dashboard planning demand loading behind a shared split read-model helper so project demand rollups no longer hand-normalize mixed allocation rows and can derive project metadata from explicit demand/assignment relations during the compatibility window - completed in the current iteration: - migrate resource and dashboard utilization metrics onto assignment-first booking reads with legacy allocation fallback - completed in the current iteration: - migrate staffing suggestion, utilization-analysis, and capacity-window APIs off direct `resource.allocations` reads onto the shared assignment-first booking helper - completed in the current iteration: - migrate project detail compatibility reads and project cost rollups off direct `project.allocations` queries onto split read models and assignment-first booking reads with legacy fallback - completed in the current iteration: - remove unused legacy allocation hydration from `resource.getById` and move timeline budget-status calculations onto assignment-first booking reads with legacy fallback - completed in the current iteration: - keep timeline quick-assign, inline edit, and project-shift mutations in sync with linked demand/assignment rows during the compatibility period, while falling back to legacy allocation sync for intentionally dual-linked estimate handoff rows - completed in the current iteration: - keep legacy allocation delete flows in sync with linked demand/assignment rows and move allocation edit flows onto explicit demand/assignment update commands where linked rows already exist - collapse the main allocation edit modal onto the compatibility update facade so the frontend no longer resolves linked demand or assignment ids before submitting edits - completed in the current iteration: - centralize demand-requirement and assignment update compatibility sync in application use-cases so dedicated API procedures, legacy allocation edit facades, and bulk status updates share the same write path - completed in the current iteration: - centralize legacy-allocation link resolution in shared application code so allocation and timeline compatibility facades use the same exact-one-link vs dual-link decision rule - completed in the current iteration: - centralize compatibility-aware legacy allocation update routing in an application use-case so allocation edit, bulk status, timeline inline edit, and project-shift mutations share the same exact-one-link vs dual-link fallback behavior - completed in the current iteration: - move timeline `getEntriesView` and `getProjectContext` read paths to demand/assignment-first responses, while preserving legacy allocation fallback and compatibility ids for drag/edit flows - completed in the current iteration: - move timeline drag-context cross-project resource bookings onto the shared assignment-first booking helper, and allow that helper to serve unbounded resource-context reads while preserving legacy allocation fallback - completed in the current iteration: - update the project detail page to present explicit assignments and open demands instead of relying on the compatibility `project.allocations` view - completed in the current iteration: - route the legacy `allocation.list` and `timeline.getEntries` facades through the same demand/assignment-first read-model loaders as `listView` and `getEntriesView`, so compatibility endpoints no longer hand-shape raw allocation query results - completed in the current iteration: - centralize project-scoped planning reads behind a shared project/timeline loader so `project.getById`, `timeline.getProjectContext`, and project-shift preparation all build from the same demand/assignment-first source - add focused API regression coverage for the shared project-planning loader, including active-only filtering and explicit-plus-legacy fallback shaping - completed in the current iteration: - move dashboard overview budget utilization onto the shared assignment-first booking helper so linked legacy allocations no longer risk double-counting staffing cost in the top-level summary cards - completed in the current iteration: - route the legacy `allocation.fillPlaceholder` API facade through the same compatibility-aware open-demand fill command as `fillOpenDemandByAllocation`, while preserving the old response contract for callers still on the legacy endpoint - move project-shift resource booking windows onto the shared assignment-first booking helper so shift validation no longer hand-loads mixed allocation and assignment rows for cross-project overlap checks - completed in the current iteration: - rename primary allocation/timeline UX copy from placeholder language to open-demand and assignment language where the explicit demand/assignment flows already exist - completed in the current iteration: - route timeline project-shift preview/apply through a shared demand/assignment-first planning helper, updating explicit demand and assignment rows directly while preserving compatibility fallback for legacy-only rows - move the allocation modal form state to explicit demand-vs-assignment intent semantics while preserving the legacy allocation edit facade during the migration window - completed in the current iteration: - harden legacy allocation update, delete, batch delete, batch status, and timeline inline-edit facades so explicit `DemandRequirement` and `Assignment` ids without a backing legacy allocation row still work through the old endpoints - centralize allocation-facade id resolution in shared application helpers so legacy API surfaces can accept allocation, demand, or assignment ids without duplicating router-side branching - add regression coverage for explicit-id compatibility across application helpers, allocation router facades, and timeline inline editing - completed in the current iteration: - move explicit assignment availability validation onto the shared assignment-booking compatibility loader so conflict checks no longer hand-read mixed legacy allocation rows - add exclusion support for in-flight compatibility ids in shared booking reads, preventing linked legacy rows from being double-counted during assignment creation - completed in the current iteration: - move legacy resource-allocation availability validation onto the shared assignment-booking compatibility loader so old create flows also respect explicit assignment bookings during the migration window - add regression coverage proving legacy non-placeholder allocation creation now blocks against explicit assignment availability conflicts - completed in the current iteration: - move dashboard overview allocation counters off raw legacy `allocation.count()` so explicit demand/assignment rows without a backing legacy allocation still appear in top-level planning totals - keep dashboard budget utilization on assignment-first booking reads while aligning the visible overview counts with the split planning read model - completed in the current iteration: - move project-list and role usage counts onto a shared compatibility-aware planning-entry counter so linked legacy rows are deduplicated against explicit demand/assignment rows in user-facing badges and delete guards - keep existing `_count.allocations` response shapes intact while sourcing those values from the split planning model during the migration window - completed in the current iteration: - move estimate planning-handoff duplicate detection onto a shared compatibility-aware planning-entry counter so explicit demand/assignment-only handoff rows block reruns without hand-rolled legacy allocation bookkeeping - completed in the current iteration: - add an idempotent `packages/db` backfill + dry-run rehearsal utility for migrating legacy allocation rows into additive `DemandRequirement` / `Assignment` records without overwriting already-linked explicit rows - completed in the current iteration: - add a reconciliation audit utility for the demand/assignment persistence split so migration readiness can be measured by pending legacy backfills, explicit non-legacy rows, and legacy-data anomalies before any broader cutover - completed in the current iteration: - allow legacy open-demand fill compatibility commands to accept explicit `DemandRequirement` ids without a backing legacy allocation row - keep the legacy `allocation.fillPlaceholder` facade reload path compatible with split assignment/demand ids by reloading through the shared allocation-facade resolver - completed in the current iteration: - centralize linked demand-fill compatibility behind a shared explicit demand-fill helper so `fillDemandRequirement` and the legacy `fillPlaceholder` facade now reuse the same assignment-first transaction path - keep explicit demand fulfillment working even when a legacy placeholder link is stale or already missing, so additive split records remain operable during migration cleanup - completed in the current iteration: - stop explicit `DemandRequirement` and `Assignment` updates from broadly mirroring back into legacy `Allocation` rows, so split persistence is authoritative by default after backfill completion - keep the remaining legacy mirror writes scoped to true legacy-id compatibility updates, preserving old `allocation.update` behavior without forcing every explicit split edit through the legacy updater - add regression coverage proving live-linked explicit allocation-facade updates stay on explicit split semantics while legacy-only update branches still sync the compatibility row intentionally - completed in the current iteration: - move timeline inline-edit compatibility loading onto the shared allocation-facade resolver so it no longer hand-resolves allocation vs demand vs assignment ids - keep timeline inline cost recalculation resource loading separate from id-resolution logic, reducing another compatibility-only branch in the router - completed in the current iteration: - turn the legacy `allocation.create` facade into an intent-inferred compatibility path so open-demand creation no longer requires `isPlaceholder` when no resource is supplied, while resource-backed creates still route through assignment dual-write behavior - stop the allocation modal edit flow from sending the legacy `isPlaceholder` toggle back to the compatibility update facade, letting persisted demand-vs-assignment identity stay authoritative during the migration window - add router regression coverage for generic create compatibility on both demand and assignment paths so the remaining facade stays stable while newer explicit commands continue to replace it - completed in the current iteration: - centralize the generic allocation-create compatibility contract in shared schemas so omitted `isPlaceholder` now means "infer intent from resource presence" across callers instead of only inside the API router - move the low-level legacy `createAllocation` use-case onto the same inferred-demand rule so direct application callers and compatibility wrappers cannot drift from the router behavior - add application regression coverage proving omitted-flag open-demand creation still writes a placeholder-compatible legacy allocation row - completed in the current iteration: - harden the demand/assignment backfill rehearsal tooling with blocker detection for orphaned explicit legacy links, pending backfills, and invalid staffed legacy rows - add strict `--fail-on-blockers` and machine-readable `--json` modes to the DB backfill/audit scripts so migration rehearsal can gate automation safely - extend DB regression coverage for metadata-preserving backfill planning, staffed `headcount > 1` warnings, and orphaned legacy-link audit states - completed in the current iteration: - make explicit demand and assignment updates tolerate stale `legacyAllocationId` values when the backing `Allocation` row has already been cleaned up, so split-row edits no longer fail during the migration window - centralize backing-legacy-allocation existence checks across update and delete compatibility helpers instead of assuming every split row with a legacy link still has a live legacy record - add regression coverage for stale-link explicit update flows through the allocation compatibility facade - completed in the current iteration: - make shared allocation-facade id resolution fall back from missing legacy `Allocation` rows to split `DemandRequirement` or `Assignment` rows by `legacyAllocationId`, so stale compatibility ids continue to work after legacy-row cleanup - keep that fallback intentionally strict by refusing to guess when both a demand and an assignment still claim the same stale legacy id - add regression coverage for stale legacy-id update and delete flows through both the application compatibility helpers and the allocation router facade - completed in the current iteration: - self-heal missing `DemandRequirement` rows for legacy placeholders before compatibility fills run, so legacy placeholder ids no longer bypass split persistence when backfill has not happened yet - route `fillOpenDemandWithCompatibility` and the `allocation.fillPlaceholder` facade through explicit demand-fill semantics after that self-heal step, deleting the backing legacy placeholder row instead of mutating or cloning staffed legacy allocations - keep the low-level `fillPlaceholder` helper on the shared demand-fill compatibility transaction so allocation-shaped responses still remain available for intentional legacy-placeholder compatibility branches - completed in the current iteration: - add a canonical demand/assignment cutover document with explicit go/no-go criteria, signoff requirements, staged execution, and ambiguity policy - add a single readiness command that combines audit and dry-run signals, writes deterministic review artifacts, and fails fast when cutover blockers remain - add focused DB regression coverage for readiness report generation and deterministic artifact naming - completed in the current iteration: - rerun the demand/assignment readiness gate against the live workspace and confirm a clean `go` baseline with `0` pending demand backfills, `0` pending assignment backfills, and `0` orphaned legacy links - execute the first real `db:backfill:demand-assignment --apply` pass on shared data; it completed as a no-op because all `132` scanned legacy allocations were already represented by additive split rows - refresh the canonical cutover baseline and saved readiness artifacts so follow-up work can focus on compatibility-branch retirement and signoff rather than backlog speculation - completed in the current iteration: - make legacy allocation update compatibility self-heal unbackfilled rows by creating a `DemandRequirement` or `Assignment` on first legacy-only edit instead of leaving the row split-blind indefinitely - keep the old `allocation.update` and batch-status facade behavior intact while ensuring staffed legacy rows still run through assignment availability validation during that backfill-on-update path - add regression coverage for legacy-only placeholder and staffed update flows so split persistence is now exercised even before the bulk backfill runs - completed in the current iteration: - keep explicit `DemandRequirement` and `Assignment` deletes on explicit persistence semantics even when a backing legacy `Allocation` row still exists, instead of routing those deletes through the legacy-removal branch - remove the backing legacy allocation row as a cleanup side effect for those explicit deletes, while leaving surviving split rows addressable through stale compatibility ids during the migration window - add regression coverage for explicit delete paths with live legacy links so demand deletes detach assignments and assignment deletes no longer risk deleting sibling demand records - completed in the current iteration: - make explicit demand-fill flows sever a linked legacy placeholder instead of continuing to mutate or clone legacy `Allocation` rows after the split records already exist - keep legacy-placeholder-id fill compatibility intact, but route direct `DemandRequirement` fills onto assignment-first persistence with legacy cleanup as a side effect - add regression coverage for explicit legacy-linked fill flows through both the application use-cases and the allocation router facade - completed in the current iteration: - centralize compatibility id derivation in a shared helper so allocation read models, booking fallbacks, demand-fill compatibility, dashboard demand loading, and timeline consumers all preserve the same `legacyAllocationId ?? explicitId` rule - centralize stale-legacy fallback partitioning in the shared split-allocation read-model helper so timeline and allocation compatibility reads no longer duplicate the same fallback-legacy branching logic - add regression coverage for booking fallback compatibility ids and shared split-read fallback partitioning, keeping timeline/router cleanup pinned to application-level helpers - completed in the current iteration: - stop the explicit `createDemandRequirement` and `createAssignment` API routes from dual-writing legacy `Allocation` rows by default, so split persistence remains authoritative unless a caller intentionally uses a compatibility facade - move the legacy `allocation.create` facade itself onto split-authoritative demand/assignment creation, preserving intent inference and the allocation-shaped response contract without persisting new legacy `Allocation` rows - emit create/update allocation SSE events through the shared compatibility-id helper for explicit and legacy-linked split rows alike, and add router regression coverage for explicit create behavior without legacy dual writes - completed in the current iteration: - isolate the last legacy-placeholder fill mutation path behind a dedicated application helper so explicit `DemandRequirement` fills no longer carry inline `allocation.create` / `allocation.update` branching - centralize demand-fill progress updates behind a shared application helper so explicit demand fills and the legacy-placeholder branch now decrement or complete demand rows through the same path - remove the extra assignment reload from the single-headcount legacy-placeholder fill branch because the created assignment already carries the correct compatibility linkage and relations - retire the dedicated legacy-placeholder fill mutation branch entirely, so both explicit demand fills and `allocation.fillPlaceholder` now route through the same split-authoritative demand-fill command and legacy placeholder rows are always cleaned up as a side effect instead of being mutated in place - move explicit demand-fill legacy-row cleanup onto the shared delete-side compatibility helper so fill transactions no longer carry their own inline `allocation.delete` branch - isolate the create-side allocation compatibility paths behind dedicated legacy-create helpers so demand, assignment, and estimate-handoff compatibility writes no longer piggyback on the generic low-level create entry point - centralize direct legacy allocation table create/update/delete calls behind a shared record-store helper so compatibility-only write paths now share one low-level mutation surface - centralize direct legacy allocation compatibility reads behind the same shared record-store helper so placeholder self-heal, facade resolution, and legacy reload flows no longer hand-call `db.allocation.findUnique` - centralize true legacy-id mirror payload shaping in shared helpers so intentional `allocation.update` compatibility writes are clearly separated from split-authoritative demand/assignment updates - isolate the compatibility delete-side legacy removal path behind a dedicated helper so the allocation facade no longer carries inline raw `allocation.delete` branching - move delete-side backing-legacy existence tolerance into the shared existence helper so the compatibility delete path no longer carries its own `allocation.findUnique` shape shim - verify the isolated compatibility branches with focused application/API regression coverage and keep the remaining raw legacy allocation access constrained to the shared record-store helper plus the shared backing-existence helper - completed in the current iteration: - expose explicit split-row ids alongside compatibility ids in shared planning read models, with allocation-shaped entries carrying `entityId` and demand/assignment entries carrying the real explicit row id in `sourceAllocationId` - move timeline drag, inline edit, open-demand fill, project-panel edit/delete, allocation modal edit, and single-row allocation delete flows onto those explicit ids while preserving compatibility `id` values for rendering, SSE, and stale-legacy callers - completed in the current iteration: - keep the allocations table selection UX keyed to compatibility-facing display ids while routing batch status and batch delete payloads through explicit split-row ids where available - completed in the current iteration: - retired the true legacy-id delete surface by routing legacy allocation deletes through split-authoritative `deleteAssignment`/`deleteDemandRequirement` paths with lazy backfill via `ensureLegacyAllocationSplitPersistence` - retired the true legacy-id update mirror by routing legacy allocation updates through split-authoritative `updateAssignment`/`updateDemandRequirement` paths with lazy backfill - removed `isPlaceholder` from Prisma schema columns, router inputs, and create facade — demand vs. assignment intent is now derived from `resourceId` presence at read-model build time; the shared `Allocation` type still exposes `isPlaceholder` as a computed property for frontend consumption - removed legacy allocation reads from `loadAllocationReadModel` — now queries only demand/assignment tables, passing `allocations: []` - reversed `findAllocationFacadeEntry` resolution priority to be split-authoritative (demand/assignment first, legacy allocation last resort) - removed the `fillPlaceholder` legacy facade from the allocation router - cleaned up dead legacy compatibility helpers: `updateLegacyAllocationWithCompatibility`, `deleteLegacyAllocationWithCompatibility`, `syncLegacyAllocationUpdate`, `syncLegacyAllocationRemoval`, `toLegacyAllocationUpdateData`, `fillPlaceholder` application use-case, and their tests - moved `deleteBackingLegacyAllocationIfPresent` into the shared `legacy-allocation-record-store` helper - completed in the current iteration: - Stage 5: dropped the legacy `Allocation` table from the Prisma schema (132 rows removed) - removed `legacyAllocationId` columns and indexes from `DemandRequirement` and `Assignment` - deleted `getAllocationCompatibilityId` helper and replaced all ~20 call sites with direct entity `.id` access - deleted `getLegacyAllocationLinks` and removed legacy-link fallback from `findAllocationFacadeEntry` - simplified SSE event emissions to use entity IDs directly - deleted migration tooling: backfill, audit, readiness scripts and their tests - cleaned seed data: removed all `allocation.create` and `allocation.deleteMany` calls - renamed `FillPlaceholderModal` to `FillOpenDemandModal` - deleted `legacy-allocation-links.test.ts` and updated all remaining tests - completed in the current iteration: - retired compatibility facades by renaming to clean domain names: `updateAllocationEntry`, `deleteAllocationEntry`, `fillOpenDemand`, `loadAllocationEntry` - replaced all `AllocationFacade*` type prefixes with `AllocationEntry*` - removed `compatibilityId` and `source` fields from booking interfaces - renamed `demandRequirementByCompatibilityId` → `demandRequirementById`, `existingFacade` → `resolved` - updated test descriptions to remove "compatibility" references - **migration is fully complete — no legacy compatibility naming or debt remains** ## Delivery Sequence 1. Completed: shared dynamic-field parity, blueprint validation parity, Blueprints list parity, and typed dashboard layout/widget migration 2. Completed: estimate router procedures, estimates list route, and first wizard shell 3. Completed: demand/assignment split persistence — all reads and writes are split-authoritative, legacy compatibility branches retired 4. Complete: Dispo v2 — all phases delivered (Phase C/SAP actuals removed from scope). 5. Next: broader cross-package regression coverage 6. Then: broader cross-package regression coverage ### Dispo v2 Parallel Stream Runs independently of the demand/assignment workstream. Source analysis and detailed plans in `samples/Dispov2/plan-*.md`. | Plan | Scope | Depends On | Primary Files | |---|---|---|---| | Country/SAH/FTE | Country + MetroCity models, SAH calculator, Spain schedule, FTE scaling | - | `schema.prisma`, `packages/engine/src/sah/`, `packages/shared/src/types/country.ts` | | OrgUnit Hierarchy | 3-level self-ref OrgUnit tree (L5→L6→L7) | - | `schema.prisma`, `packages/api/src/router/org-unit.ts`, `apps/web/src/components/org-units/` | | Utilization Categories | UtilizationCategory model on Projects (Chg, BD, MD&I, M&O, PD&R, Absence) | - | `schema.prisma`, `packages/api/src/router/utilization-category.ts` | | Client/WBS | Self-ref Client tree (Master→Entity), project linking | - | `schema.prisma`, `packages/api/src/router/client.ts`, `apps/web/src/components/clients/` | | Resource Extensions | EID attributes, ManagementLevel, ResourceType, derivation rules | Plans 1-4 | `schema.prisma`, `Resource` model, `ResourceModal.tsx` | | Chargeability Report | Live reporting page, forecast from assignments + SAH, export | All above | `packages/engine/src/chargeability/`, `apps/web/src/app/(app)/reports/` | Serialization constraint: all plans add to `schema.prisma` — edits serialized, not parallel. ## Demand And Assignment Persistence Split This is now the highest-value planning-core change. The read side is already separated enough that the next step should be an additive persistence migration instead of more UI churn. ### Problem (resolved) The legacy `Allocation` table mixed two business concepts: demand (`isPlaceholder = true`) and assignment (`isPlaceholder = false`). That overload leaked into creation, fill, estimate handoff, validation, reporting, and planning logic. This has been fully resolved: the `Allocation` table has been dropped, and all reads/writes now go through first-class `DemandRequirement` and `Assignment` tables. `isPlaceholder` remains as a derived read-model property for frontend consumption. ### Target persistence model Additive target for the next schema slice: | New model | Core fields | Purpose | |---|---|---| | `DemandRequirement` | `id`, `projectId`, `roleId`, `role`, `startDate`, `endDate`, `hoursPerDay`, `percentage`, `headcount`, `status`, `metadata` | first-class planning demand | | `Assignment` | `id`, `projectId`, `resourceId`, `demandRequirementId?`, `startDate`, `endDate`, `hoursPerDay`, `percentage`, `dailyCostCents`, `status`, `metadata` | actual staffing assignment | | `AssignmentRevision` later | `assignmentId`, `changedAt`, `before`, `after`, `reason` | audit-friendly history after the base split lands | ### Field mapping from current `Allocation` | Current `Allocation` field | DemandRequirement target | Assignment target | Notes | |---|---|---|---| | `id` | new id on backfill | new id on backfill | keep old allocation id in compatibility metadata during migration | | `projectId` | `projectId` | `projectId` | direct | | `resourceId` | not stored except optional `suggestedResourceId` metadata | `resourceId` | nullable meaning is removed from the main model | | `startDate` / `endDate` | direct | direct | direct | | `hoursPerDay` | direct | direct | direct | | `percentage` | direct | direct | direct | | `role` / `roleId` | direct | copied from linked demand when useful | preserve text fallback until role normalization is done | | `isPlaceholder` | removed | removed | business state moves to model choice, not a flag | | `headcount` | direct | removed from assignment | one assignment row always means one staffing record | | `dailyCostCents` | derived or zero | direct | demand does not carry execution cost as a required source field | | `status` | direct | direct | keep enum initially for compatibility | | `metadata` | direct | direct | preserve `estimateHandoff`, recurrence, and migration provenance | ### Delivery phases 1. Phase A: additive schema Add Prisma models for `DemandRequirement` and `Assignment` without removing `Allocation`. Add nullable `demandRequirementId` on `Assignment` only if the implementation keeps the `Assignment` name; if the table name must stay `allocations` temporarily, keep a compatibility mapping documented in Prisma comments. 2. Phase B: dual-write application services Introduce `defineDemand`, `assignResource`, and `fillDemand` use cases in `packages/application`. New estimate handoff should create `DemandRequirement` rows first, then create `Assignment` rows only for lines that have a resolvable resource. 3. Phase C: backfill and compatibility reads — **Complete.** Backfill ran as a no-op (all rows already split). Backfill/audit/readiness tooling has been deleted. 4. Phase D: command/API cutover — **Complete.** `isPlaceholder` removed from create/update payloads. `fillPlaceholder` replaced by `fillOpenDemandByAllocation`. Compatibility facades renamed to clean domain names. 5. Phase E: legacy removal — **Complete.** Legacy `Allocation` table dropped. `legacyAllocationId` columns removed. All compatibility naming retired. ### Use cases to migrate first | Priority | Scope | Why | |---|---|---| | `P0` | estimate planning handoff | this is where planning demand now enters the operational domain | | `P0` | manual demand creation and fill flow | direct replacement for placeholder allocation creation/fill | | `P1` | timeline command paths | move/resize/edit semantics should stop branching on placeholder state | | `P1` | timeline command paths and remaining persistence-facing reads | remove the remaining placeholder-specific assumptions after the dashboard/report cleanup | | `P2` | legacy allocation CRUD compatibility | keep only as a migration facade | ### Acceptance criteria for the split - A demand can exist without inventing a fake allocation row. - Filling one seat of a multi-headcount demand does not mutate assignment semantics. - Approved estimate handoff writes demand first and assignment second. - Timeline and allocations pages render from explicit demand and assignment reads with no `isPlaceholder` branching in UI data loading. - Legacy `allocation.list` can remain temporarily, but new features must not require `isPlaceholder` input. - Migration can be run without losing existing allocation history or estimate handoff metadata. ### Parallel tickets | Ticket | Owner | Scope | |---|---|---| | `DAS-01` | `A1-architect` | finalize Prisma target schema, compatibility period, and cutover rules | | `DAS-02` | `C1-estimate-backend` | switch estimate planning handoff from placeholder allocations to demand-first writes | | `DAS-03` | `C1-estimate-backend` | add demand/assignment application services and additive API procedures | | `DAS-04` | `C2-estimate-frontend` | adapt wizard/workspace follow-up UIs and any demand-fill flows to new commands | | `DAS-05` | `T1-regression` | backfill tests, compatibility tests, and Docker smoke checks | ### Known residual semantic mismatches All previously listed mismatches have been resolved: - The legacy `Allocation` table has been dropped; persistence uses `DemandRequirement` and `Assignment` exclusively. - Compatibility facades have been renamed to clean domain names (`updateAllocationEntry`, `deleteAllocationEntry`, `fillOpenDemand`). - `isPlaceholder` is retained as a derived read-model property (not a stored column) for frontend consumption. ## Parallel Delivery Plan Use a single orchestrator and split roadmap execution into package-owned workstreams. Shared files should be merged only at integration checkpoints. | Agent | Scope | Primary Files / Packages | Deliverables | Notes | |---|---|---|---|---| | `A1-architect` | keep roadmap, contracts, and merge boundaries coherent | [docs/product-roadmap.md](/home/hartmut/Documents/Copilot/capakraken/docs/product-roadmap.md), [docs/estimating-extension-design.md](/home/hartmut/Documents/Copilot/capakraken/docs/estimating-extension-design.md), shared contract entry points | acceptance criteria, sequencing, shared-file coordination | should not implement feature code unless integration is blocked | | `C1-estimate-backend` | estimate domain, router, persistence, exports | `packages/api`, `packages/application`, `packages/engine`, `packages/db`, `packages/shared` | workspace read/write procedures, export serializers, version actions, metrics persistence | owns server-side behavior and cross-package type safety | | `C2-estimate-frontend` | estimates pages, wizard follow-up, workspace tabs | `apps/web/src/app/(app)/estimates`, `apps/web/src/components/estimates`, shared UI components | detail workspace, overview/assumptions/scope/rates tabs, iteration UX | should avoid editing backend contracts without handoff | | `T1-regression` | tests and runtime verification | `packages/*/test*`, `apps/web` verification paths, Docker runtime checks | regression tests, package typechecks, app smoke validation | runs after each integration checkpoint | | `R1-reviewer` | final integration and regression review | diff review across touched packages | findings, missing permissions, backward-compatibility risks | review after C1 and C2 merge | ### Active Parallel Slice Current target: execute the demand/assignment persistence split without blocking estimate usage that already works. | Ticket | Owner | Depends On | Scope | |---|---|---|---| | `DAS-01` Additive Prisma schema and compatibility rules | `A1-architect` + `C1-estimate-backend` | current read-model split | define `DemandRequirement` / `Assignment` tables, migration comments, and cutover constraints | | `DAS-02` Demand-first planning handoff | `C1-estimate-backend` | `DAS-01` | change approved estimate handoff to create demand first, assignment second | | `DAS-03` Demand/assignment command surface | `C1-estimate-backend` | `DAS-01` | add demand create/fill procedures and compatibility wrappers for legacy allocation flows | | `DAS-04` Frontend follow-up flows | `C2-estimate-frontend` | `DAS-02`, `DAS-03` | completed for the main allocation, timeline, and estimate handoff surfaces; continue only for residual legacy copy | | `DAS-05` Regression and migration net | `T1-regression` | `DAS-01` to `DAS-04` checkpoints | backfill tests, contract tests, Docker smoke validation, and migration rehearsal | ### Merge Boundaries - serialize edits to: - `packages/db/prisma/schema.prisma` - `packages/shared/src/types/allocation.ts` - `packages/shared/src/schemas/allocation.schema.ts` - `packages/api/src/router/index.ts` - `packages/api/src/router/allocation.ts` - `packages/application/src/use-cases/estimate/create-planning-handoff.ts` - allow parallel edits to: - demand/assignment backend packages vs estimate frontend app code - docs vs tests - dashboard/report query cleanup vs estimate workspace UI copy ## Document Ownership | Topic | Canonical File | Notes | |---|---|---| | Active backlog | [product-roadmap.md](/home/hartmut/Documents/Copilot/capakraken/docs/product-roadmap.md) | Update this instead of reopening old plan files. | | Estimating design and field mapping | [estimating-extension-design.md](/home/hartmut/Documents/Copilot/capakraken/docs/estimating-extension-design.md) | Holds workbook analysis, mapping, and implementation plan. | | Strategic architecture direction | [v2-architecture-proposal-2026-03-11.md](/home/hartmut/Documents/Copilot/capakraken/research/v2-architecture-proposal-2026-03-11.md) | Keep as strategy, not sprint backlog. | | Historical decisions | [LEARNINGS.md](/home/hartmut/Documents/Copilot/capakraken/LEARNINGS.md) | Append-only log. |