Files
CapaKraken/docs/product-roadmap.md
T

448 lines
44 KiB
Markdown

# 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/planarchy/docs/estimating-extension-design.md)
- Dispo clean-slate import design and field mapping: [dispo-import-implementation.md](/home/hartmut/Documents/Copilot/planarchy/docs/dispo-import-implementation.md)
- Dispo worker ticket pack and dependency breakdown: [dispo-import-implementation-tickets.md](/home/hartmut/Documents/Copilot/planarchy/docs/dispo-import-implementation-tickets.md)
- Demand/assignment migration cutover and readiness policy: [demand-assignment-migration-cutover.md](/home/hartmut/Documents/Copilot/planarchy/docs/demand-assignment-migration-cutover.md)
- Strategic longer-horizon architecture direction: [v2-architecture-proposal-2026-03-11.md](/home/hartmut/Documents/Copilot/planarchy/research/v2-architecture-proposal-2026-03-11.md)
- Implementation history and decisions: [LEARNINGS.md](/home/hartmut/Documents/Copilot/planarchy/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
## Active Workstreams
| Workstream | Status | Why It Is Still Open | Recommended Next Step |
|---|---|---|---|
| Estimating system | `In progress` | Full CRUD, versioning, export, planning handoff, clone/template, rate cards per client, and richer version comparison (scope item diffs, resource snapshot diffs, chapter subtotals, margin %). Remaining gaps: scope-to-effort rule engine, experience multipliers, weekly phasing, structured commercial terms. | Scope-to-effort rule engine for auto-expanding scope items into demand lines per discipline. |
| 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. | 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
### 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/planarchy/docs/product-roadmap.md), [docs/estimating-extension-design.md](/home/hartmut/Documents/Copilot/planarchy/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/planarchy/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/planarchy/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/planarchy/research/v2-architecture-proposal-2026-03-11.md) | Keep as strategy, not sprint backlog. |
| Historical decisions | [LEARNINGS.md](/home/hartmut/Documents/Copilot/planarchy/LEARNINGS.md) | Append-only log. |