448 lines
44 KiB
Markdown
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. |
|