Some checks are pending
Bidi Control Character Guard / bidi-control-guard (push) Waiting to run
Circular Dependency Check / Check for new circular dependencies (push) Waiting to run
Citus Migration Smoke / Combined migrations on single-node Citus (push) Waiting to run
E2E Fresh Install Tests / fresh-install-e2e (push) Waiting to run
ext-v2 guardrails / Run ext-v2 guard and ESLint (push) Waiting to run
Integration Tests / Check for relevant changes (push) Waiting to run
Integration Tests / ${{ (github.event_name == 'schedule' || github.event.inputs.suite == 'full') && 'Full integration suite' || 'Tier-1 integration subset' }} (push) Blocked by required conditions
Mobile checks / Mobile lint + typecheck (push) Waiting to run
Mobile checks / Mobile unit tests (push) Waiting to run
Mobile checks / Mobile dependency audit (report) (push) Waiting to run
Mobile checks / Mobile reproducibility checks (push) Waiting to run
Secrets guard (env backups) / Ensure no tracked env backup files (push) Waiting to run
Temporal Readiness / fast-readiness (push) Waiting to run
Temporal Readiness / docker-parity (push) Waiting to run
TypeScript Type Check / Nx affected typecheck (push) Waiting to run
Unit Tests / Skipped-test budget (push) Waiting to run
Unit Tests / Nx affected unit tests (push) Waiting to run
Unit Tests / Server unit coverage (informational) (push) Waiting to run
Validate Tenant Management Schema / Check for relevant changes (push) Waiting to run
Validate Tenant Management Schema / Validate Tenant Management Schema (push) Blocked by required conditions
EE Workflows Build Guard / ee-workflows-build-guard (push) Waiting to run
Excluded: .git, node_modules, secrets/, compose.env, assemblyscript tgz Source: /opt/alga-psa on psa.joliet.tech
143 lines
8.9 KiB
Markdown
143 lines
8.9 KiB
Markdown
# PRD — Template Runtime Normalization Completion
|
|
|
|
- Slug: `template-runtime-normalization-completion`
|
|
- Date: `2026-03-20`
|
|
- Status: Draft
|
|
|
|
## Summary
|
|
|
|
Finish the remaining template/runtime cleanup after the service-driven invoicing cutover so instantiated contracts no longer depend on `template_contract_id` fallback semantics in client actions, runtime loaders, legacy scripts, or API/service adapters.
|
|
|
|
This plan is intentionally narrower than the earlier normalization umbrella in [2026-03-16-contract-template-normalization](/Users/roberisaacs/alga-psa.worktrees/feature/client-owned-contracts-simplification/ee/docs/plans/2026-03-16-contract-template-normalization/PRD.md). Core billing fallback joins have already been removed in the service-driven invoicing cutover. What remains is the tail of mixed-model behavior around contract application, contract-line cloning/setup, provenance exposure, and legacy decoupling utilities.
|
|
|
|
## Problem
|
|
|
|
The codebase is now in a partially normalized state:
|
|
|
|
- live recurring billing no longer needs template fallback joins in its critical execution paths
|
|
- contract-cadence materialization now reads instantiated contracts directly
|
|
- several static guards and post-drop migrations are already complete
|
|
|
|
but a smaller set of runtime paths still behaves as if `template_contract_id` can stand in for live contract identity or as if template/runtime resources are interchangeable.
|
|
|
|
Concrete remaining examples:
|
|
|
|
- `packages/clients/src/actions/clientContractActions.ts` still resolves template source with `clientContract.template_contract_id ?? clientContract.contract_id`
|
|
- `packages/clients/src/actions/clientContractLineActions.ts` still uses mixed template/runtime lookup semantics in line-add flows
|
|
- `server/src/lib/api/services/ContractLineService.ts` still carries template-source fallback reads
|
|
- `server/scripts/contract-template-decoupling.ts` still preserves hybrid fallback semantics
|
|
- several runtime DTO/model surfaces still expose `template_contract_id` without a clear provenance-only contract
|
|
|
|
This leaves three real problems:
|
|
|
|
1. runtime behavior is still inconsistent across modules
|
|
2. provenance and live identity are still blurred for engineers and callers
|
|
3. final schema cleanup remains blocked because the codebase has not fully converged on what `template_contract_id` is allowed to mean
|
|
|
|
## Goals
|
|
|
|
- Remove the remaining behaviorally active `template_contract_id` fallback paths from runtime code.
|
|
- Make any retained `template_contract_id` usage explicitly provenance-only and read-only.
|
|
- Make template-source-dependent operations resolve authoring source explicitly instead of silently falling back from runtime IDs.
|
|
- Tighten tests and static guards so mixed template/runtime fallback patterns cannot be reintroduced accidentally.
|
|
- Leave a clear, reviewable end state for whether `template_contract_id` remains as metadata or becomes removable.
|
|
|
|
## Non-goals
|
|
|
|
- Reworking service-period-driven recurring invoicing behavior already covered by the March 18 cutover plan.
|
|
- Redesigning the contract template UX or authoring model.
|
|
- Changing billing semantics, discounts, taxes, or renewals beyond removing leftover fallback behavior.
|
|
- Forcing immediate column removal if the final decision is to keep `template_contract_id` as provenance metadata for now.
|
|
- Rewriting historical invoices or rebuilding template migration history.
|
|
|
|
## Users and Primary Flows
|
|
|
|
- Billing/ops engineer
|
|
1. Instantiates a contract from a template.
|
|
2. Applies/configures lines on the instantiated contract.
|
|
3. Bills the client from runtime contract state only.
|
|
|
|
- Support/debugging engineer
|
|
1. Inspects a live contract or client assignment.
|
|
2. Can tell whether a template reference is provenance metadata or a live dependency.
|
|
3. Can reason about failures without guessing whether a fallback path exists.
|
|
|
|
- Backend engineer
|
|
1. Changes contract or client-contract code.
|
|
2. Gets clear type/test/static-guard feedback if a new fallback path is introduced.
|
|
|
|
## UX / UI Notes
|
|
|
|
- This plan is mostly backend/model cleanup.
|
|
- Any UI that surfaces `template_contract_id`-derived information must treat it as provenance text only, not as a second valid runtime lookup path.
|
|
- Runtime contract detail screens must not silently widen “contract” lookups to template-shaped resources.
|
|
|
|
## Requirements
|
|
|
|
### Functional Requirements
|
|
|
|
1. Inventory every remaining runtime use of `template_contract_id` in the active packages touched by contract application, line cloning, runtime loaders, API services, and maintenance scripts.
|
|
2. Classify each remaining use as one of:
|
|
- allowed provenance-only read
|
|
- forbidden runtime fallback
|
|
- obsolete legacy cleanup
|
|
3. `applyContractToClient(...)` and equivalent contract-application flows must stop deriving template source with `template_contract_id ?? contract_id`.
|
|
4. Contract-line add/clone/setup flows must resolve source template data explicitly and fail closed when the required authoring source cannot be identified.
|
|
5. Runtime client-contract and contract-line DTO/model surfaces must stop implying that `template_contract_id` is a live runtime key.
|
|
6. API/service-layer contract-line mutation flows must not backfill, infer, or rely on template fallback semantics beyond explicit provenance reads.
|
|
7. Contract/template decoupling scripts must be retired or rewritten so they no longer preserve hybrid fallback behavior.
|
|
8. Static guards must forbid known fallback patterns in the relevant runtime packages and scripts.
|
|
9. DB-backed integration tests must prove that post-normalization contract application and line configuration operate without template fallback joins or mixed runtime IDs.
|
|
10. The final plan output must state whether `client_contracts.template_contract_id` remains as provenance-only metadata or is now ready for a future drop migration.
|
|
|
|
### Non-functional Requirements
|
|
|
|
- Failures should be explicit. If a flow requires template provenance and it is missing, the system should error clearly rather than silently reinterpreting `contract_id`.
|
|
- The resulting code should be easier to reason about than the current partially normalized state.
|
|
- Static source guards should cover the packages that actually own these remaining runtime paths.
|
|
|
|
## Data / API / Integrations
|
|
|
|
- Clarify the contract for `client_contracts.template_contract_id`:
|
|
- allowed as provenance-only read metadata
|
|
- not allowed as live runtime contract identity
|
|
- Align shared interfaces/schemas that still expose `template_contract_id` so callers understand its provenance-only meaning.
|
|
- Review whether any API/service flows still expect a mixed template/contract ID space and split them if necessary.
|
|
- Rewrite or retire `server/scripts/contract-template-decoupling.ts` so it does not backfill or depend on hybrid semantics.
|
|
|
|
## Security / Permissions
|
|
|
|
- No new user-facing permissions are required.
|
|
- Existing contract/contract-line mutation permissions remain unchanged.
|
|
- Cleanup must not widen template visibility through runtime contract APIs.
|
|
|
|
## Rollout / Migration
|
|
|
|
- This is a cleanup/completion plan after the service-driven cutover, not a major feature flag rollout.
|
|
- Implementation should proceed by:
|
|
1. classifying remaining references
|
|
2. removing forbidden runtime behavior
|
|
3. keeping or narrowing provenance-only reads explicitly
|
|
4. updating tests and static guards
|
|
- If the final decision is to retain `template_contract_id` temporarily, the plan must leave the codebase ready for a later drop migration without behavioral dependence on the column.
|
|
|
|
## Risks
|
|
|
|
- Some contract-application flows may still need template provenance to locate authoring-side data. If that provenance is absent for old rows, removing fallback can turn a silent compatibility path into an explicit failure.
|
|
- Interfaces that expose `template_contract_id` may have downstream callers relying on old assumptions.
|
|
- Legacy scripts may still be used operationally even if their semantics are outdated.
|
|
|
|
## Open Questions
|
|
|
|
- Should `client_contracts.template_contract_id` remain as provenance-only metadata for renewals/draft-resume/debug flows, or is the codebase ready to deprecate it entirely after this cleanup?
|
|
- For flows that still need authoring-source context, should the source template ID be required data, or should the flow be redesigned to avoid re-reading authoring tables entirely?
|
|
- Do any remaining UI/detail surfaces still need explicit provenance labels once runtime fallback behavior is removed?
|
|
|
|
## Acceptance Criteria
|
|
|
|
- No live runtime contract application, line-clone/setup, or billing-adjacent flow derives template source with `template_contract_id ?? contract_id`.
|
|
- Remaining `template_contract_id` references are either provenance-only reads, tests/fixtures, or migration history.
|
|
- Legacy scripts no longer preserve hybrid template/runtime fallback behavior.
|
|
- Shared interfaces and runtime loaders no longer blur provenance metadata with live runtime identity.
|
|
- Focused DB-backed integration tests and static guards prove the remaining fallback patterns are gone from the targeted packages.
|