Live preset references
Allocating an existing preset stores its ID and never copies its cells. This applies to admin baselines, agent downline caps, player creation, and later player-layer edits. Creator edits take effect on the next limit resolution for every allocation referencing the preset. Same-currency selection, ownership, and visibility checks remain in place.
A player still has admin, master-agent, and agent layers. Default removes the selected local layer (or agent cap pointer), leaving the parent cascade active. Existing enforcement is preserved: a player admin override replaces the admin baseline; player MA/agent overrides stack with the cascade and the tightest cap binds. Custom limits still store their own cells. Switching a shared preset to custom never overwrites the shared library preset, even if the caller owns it.
Storage and reads
- Agent allocations use
adminBaselinePresetId/downlineCapPresetIddirectly. - Player preset layers use
source = 'preset'andsourcePresetId, with no local cells. Custom layers retain local cells and have no source preset ID. - Enforcement and visibility collect player preset references with the agent-chain references and fetch cells in bulk. A cached agent chain never contains player-specific references.
- Profile and all-layer reads return current referenced cells. Archived presets remain valid for existing references; they cannot be newly selected.
- Preset edits invalidate display visibility. Chain and visibility cache namespaces move to
v2so pre-migration cached pointers/values are not reused.
Migration and deployment
Migration: backend/prisma/migrations/20260908120000_live_preset_references/migration.sql.
This is a coordinated deployment, not a rolling deployment: old code reads copied player cells and must not run after those cells are removed.
- Back up the affected tables and pause traffic; stop every backend API and worker process that reads/writes exposure settings.
- Run the normal Prisma migration deployment (
npx prisma migrate deployfrombackend, against the intended database). - If validation fails, the data transaction rolls back and reports up to 20 offending agent/set IDs. Resolve missing, nested/internal, ambiguous, or wrong-currency source mappings before retrying. Use the normal Prisma failed-migration recovery procedure after investigation. Do not guess the source from matching cell values.
- Start all backend instances with this release, then resume traffic. New cache namespaces avoid stale pre-migration values.
- Verify a referenced preset edit affects a downline player, and Default restores parent inheritance.
The migration repoints agent copies (including traceable legacy admin baseline copies), removes copied player preset cells, removes legacy inherited snapshot layers, and clears stale source IDs on custom layers. It retains internal preset records/cells for audit. Archived original presets remain valid references. Custom caps without a source stay custom. Untraceable internal admin baselines and wrong-currency player references move to the active platform Low preset in their target currency. If USD is affected and has no Low preset, the migration creates Low (USD) from the active platform INR Low policy before assigning it.
Do not roll back application code alone after conversion: restore the backed-up exposure data with the old release, or roll forward. The original snapshot conversion script and exported helper are retained but explicitly refuse execution. Its legacy script body is unreachable and intentionally retained pending approval to remove it.
The migration SQL is idempotent. It validates all mappings before mutations and takes table write locks for the transaction. Its duration depends on table sizes and must be measured on a staging copy; no production data has been migrated as part of this change.
Performance and impact
- Allocation: O(1) storage per selected preset instead of O(C) copied cells.
- Resolution: O(D + C) time/space for hierarchy depth D and fetched cells C. The chain lookup remains cached. Existing preset/set grain indexes support the bulk reads; no new index is required.
- Enforcement adds no per-layer queries: player preset references share the existing bulk preset-cell query. Profile reads add one indexed preset-cell query when showing a reference. Millisecond latency has not been benchmarked.
- Backend changes cover allocation, reads, enforcement/display resolution, and migration. Frontend API shapes and controls are unchanged; only outdated comments were updated. Auxiliary services are unaffected.
Validation
Run from backend:
npm run db:generate
npm run typecheck
npm run typecheck:tests
npm run test:unit -- src/domain/exposureLimits
npm run test:unit -- --maxWorkers=1 src/routes/__tests__/exposureLimits.routes.test.ts src/routes/__tests__/adminAgentCapRoutes.test.ts src/routes/__tests__/adminExposurePresets.errorResponse.test.ts src/routes/__tests__/credit-limit-negative-balance.test.ts src/routes/__tests__/agentPnlReportRange.test.ts
The migration was also exercised in an isolated PostgreSQL runtime (PGlite), covering repointing, copied-cell removal, custom preservation, Default, archived sources, retained audit rows, reruns, and transaction rollback on missing/wrong-currency/internal source references. A full staging rehearsal remains a deployment requirement.
Validation results: 146 domain tests and 74 route tests passed; application and test TypeScript checks passed. Parallel HTTP suites produced intermittent unexpected responses in this environment; the route suites passed together with one worker.