Commit 73a2cc9
authored
docs: Add Valuation Service PRD - Account-as-a-Contract pattern (#742)
* docs: add Valuation Service PRD - Account-as-a-Contract pattern
Introduces the Valuation Strategy Orchestrator PRD that shifts value
computation from centralized price lists to account-level strategies.
Key innovations:
- Account-as-a-Contract: Each account defines how it accepts value
- Two-step resolution: Discovery (Account) + Execution (Valuation)
- Bi-temporal valuation replay with full audit trail
- CEL for stateless calculations, Starlark for complex orchestration
- Valuation Basis: Complete provenance for every value transformation
Architecture:
- valuation_assignments table extends CurrentAccount/InternalBankAccount
- Stateless Valuation Service executes Starlark/CEL strategies
- L1/L2 caching for performance (<10ms p99 target)
- Integration with Market Information service for rates
- Batch API for high-throughput settlement scenarios
Implementation organized into 9 parallel streams:
1. Account strategy assignments (P0, 5 points)
2. Valuation service foundation (P0, 8 points)
3. CEL runtime (P0, 5 points)
4. Starlark runtime (P1, 8 points)
5. Identity strategy (P0, 3 points)
6. Energy/commodity valuation (P1, 13 points)
7. Batch valuation API (P2, 5 points)
8. Integration with sagas (P1, 8 points)
9. Tenant self-service (P2, 13 points)
Related:
- ADR-0028: Starlark Saga Orchestration with CEL Valuation
- Universal Asset System PRD
- Market Information Management PRD
This enables tenant-specific pricing strategies without code deployment
and provides the computational integrity for multi-asset ledgers.
* docs: revise Valuation PRD - embedded library over standalone service
BREAKING ARCHITECTURAL CHANGE: Shift from standalone Valuation Service
to embedded library pattern within Account Services.
Key Changes:
- Rename: "Valuation Service" → "Account-Scoped Valuation Engine"
- Architecture: shared/pkg/valuation library (not new microservice)
- RPC: GetValuedAmount() on Account Services (not separate service)
- Network hops: 3 instead of 4 (25% latency reduction)
- Story points: 46 instead of 68 (32% reduction)
Why This Change:
- Domain alignment: Valuation is Account aggregate behavior
- Simpler operations: No new microservice to deploy/monitor
- Better performance: Eliminates extra network hop
- Matches existing patterns: Like shared/pkg/saga
Implementation Streams:
- Stream 1: Account Strategy Assignments (5 pts) - Both services
- Stream 2: Valuation Engine Library (10 pts)
- Stream 3: Reference Data Strategy Storage (5 pts)
- Stream 4: Current Account Integration (5 pts)
- Stream 5: Internal Bank Account Integration (5 pts)
- Stream 6: Saga Integration (5 pts)
- Stream 7: Energy/Commodity Strategies (8 pts)
- Stream 8: Performance Optimization (3 pts)
Applies to: Both CurrentAccountService and InternalBankAccountService
Version: 2.0 (Draft - Revised Architecture)
* docs: enhance Valuation PRD with Gemini feedback - safety guarantees
Incorporate Gemini's architectural review feedback to strengthen
the PRD with explicit safety guarantees and audit trail clarity.
Key Enhancements:
1. FR-3: Bi-Temporal Replay
- Explicitly document knowledge_at passed to market_data.get_price()
- Ensures saga replay sees exact same rates used historically
2. FR-4.1: Output Instrument Validation (NEW)
- "Chemical Signature" - full InstrumentAmount in response
- Activation-time check: strategy output matches assignment
- Prevents USD account returning GBP (dimension confusion)
3. FR-5: Valuation Basis Enhancement
- Per ADR-017: observation_ids stored in PositionEntry.attributes
- Permanent audit trail survives MarketInfo data purges
- 7-year audit without external service calls
4. FR-6: Event-Driven Cache Invalidation
- Kafka subscription to strategy.updated events
- Reduces staleness from 5min TTL to <100ms
- "Train Track Precision" for strategy changes
5. FR-7: Conservation of Dimension (NEW)
- Recursion prevention: Valuation is read-only operation
- No writes to Position Keeping during valuation
- Prevents "Infinite Asset Inflation" loops
6. Section 5.3: The "Passport Pattern" (NEW)
- Three-layer persistence model documented:
- Layer 1: Account Service (stateless / pure function)
- Layer 2: Saga Orchestrator (checkpoint in saga_step_results)
- Layer 3: Position Keeping (permanent in attributes JSONB)
- Basis travels like passport through system
- Guarantees audit trail at each layer
7. Stream 8: Performance Optimization
- Added task for event-driven invalidation implementation
- Success criteria: <100ms staleness for strategy updates
Why These Changes Matter:
- Statelessness guarantee: No writes = predictable TPS
- Audit integrity: Basis preserved at 3 independent layers
- Type safety: Full instrument identity prevents confusion
- Recursion safety: Read-only contract prevents loops
- Regulatory compliance: 7-year audit trail without dependencies
Reviewed-By: Gemini (architectural feedback)
Version: 2.0 → 2.1 (safety enhancements)
* docs: add atomic valuation with price lock (v2.2) - eliminate TOCTOU race
Major architectural enhancement based on identified "Tiered Valuation Drift"
race condition. Collapse valuation into transactional operations to guarantee
price integrity under concurrent load.
The "Subtle Bug" Identified:
State-dependent pricing (tiered rates, volume discounts) creates TOCTOU race:
TIME: T0 T1 T2
Saga A: GetValuedAmount(300 kWh) → £60 (tier 1, balance=0)
InitiateLien(300 kWh)
Saga B: GetValuedAmount(300 kWh) → £60 (WRONG - sees balance=0)
InitiateLien(300 kWh)
Result: Both charged at introductory rate. Lost revenue.
The Solution: Atomic Valuation-in-Lien
Valuation now happens ATOMICALLY within transactional operations:
1. InitiateLien (withdrawals):
- Queries ProjectedBalance (current + active liens)
- Executes valuation with projected state
- Stores BOTH reserved_quantity AND valued_amount (price lock)
- Returns lien with guaranteed price
2. ExecuteDeposit (inbound assets):
- Queries ProjectedBalance
- Executes valuation atomically
- Posts to Position Keeping with basis
3. GetValuedAmount (inquiry-only):
- Non-binding estimate for UX
- Returns is_estimate=true
- No state changes, no price guarantee
New Functional Requirements:
FR-1 Revision:
- Reframe GetValuedAmount as inquiry-only (non-binding)
- Add is_estimate flag to response
- Document use cases (mobile apps, dashboards)
FR-8 (NEW): Atomic Valuation in Lien Initiation
- InitiateLien accepts any InstrumentAmount (multi-asset)
- Response includes valued_amount + basis (price lock)
- Lien stores valuation at creation time
- ExecuteLien uses stored value (no recalculation)
- Idempotent retries return existing lien
FR-9 (NEW): Projected Balance for State-Dependent Valuation
- Position Keeping provides GetProjectedBalance RPC
- Formula: CurrentBalance + Sum(ActiveLiens)
- Valuation strategies query projected state
- Second lien sees first lien's reservation
- Eliminates tiered pricing drift
Technical Architecture Updates:
Section 5.1: Transactional Workflow (NEW)
- Primary flow shows valuation inside InitiateLien
- Mermaid diagram includes ProjectedBalance query
- 4 network hops (vs 3 for inquiry-only)
- Price lock guarantee documented
Section 5.1.1: Inquiry Workflow (NEW)
- Secondary flow for non-binding queries
- 3 network hops (no ProjectedBalance needed)
- WARNING about non-binding nature
Section 5.3: Passport Pattern Update
- Layer 1: Transactional writes vs inquiry stateless
- Liens store price lock in database
- Three-layer audit trail preserved
Implementation Streams Updated:
Stream 4 (Current Account): 5 → 8 points
- Add InitiateLien multi-asset support
- Add ExecuteDeposit atomic valuation
- Add ProjectedBalance client
- Concurrency tests for tiered pricing
Stream 5 (Internal Bank Account): 5 → 8 points
- Mirror Stream 4 changes
- Wholesale energy with price lock
Stream 6 (Saga Integration): 5 → 8 points
- Sagas use InitiateLien (no separate valuation)
- Extract valued_amount from lien response
- Deterministic replay from saga checkpoints
- Chaos tests (pod kills during valuation)
Stream 9 (NEW): Lien Schema Enhancement (3 points)
- Add valued_amount field to liens table
- Add valuation_basis JSONB field
- Idempotency returns existing lien
- Migration scripts for existing liens
Stream 10 (NEW): Position Keeping Projected Balance (5 points)
- Add GetProjectedBalance RPC
- Aggregate active liens
- Performance: <10ms p99
- Concurrency tests (10 parallel liens)
Testing Strategy Enhanced:
- Atomic valuation in liens
- Tiered pricing with concurrent liens (TOCTOU prevention)
- Saga deterministic replay
- Projected balance queries
- Chaos tests for price lock integrity
Complexity Impact:
- Story Points: 46 → 63 (+17 points, +37%)
- Buys: TOCTOU elimination, price lock, concurrent correctness
- Trade-off: Worth it - prevents silent revenue loss
Why This Change:
- Original PRD had race condition for tiered pricing
- Separate valuation + lien = TOCTOU vulnerability
- Atomic valuation + projected balance = financial correctness
- Liens become "price locks" not just quantity reservations
- BIAN-compliant extension of existing lien pattern
Version: 2.0 → 2.2 (Atomic Valuation with Price Lock)
Reviewed-By: Gemini (identified TOCTOU bug)
Reviewed-By: User (confirmed lien-based approach)
* docs: add security & resilience enhancements (v2.3) - Gemini PR feedback
Comprehensive refinements based on Gemini's PR review addressing security
enforcement, resilience patterns, fungibility-aware tiering, and audit quality
tracking.
1. ExecuteLien Constraint (Price Lock Integrity)
Added CRITICAL constraint: ExecuteLien MUST use stored valued_amount from lien
record. NO override parameters permitted.
Why: Allowing overrides defeats price lock guarantee and reintroduces TOCTOU
vulnerabilities.
Implementation check:
- Load lien from database
- Use lien.ValuedAmount (stored at InitiateLien time)
- FORBIDDEN: req.OverrideAmount parameter
Impact: Guarantees price lock is immutable from reservation to execution.
2. Read-Only Starlark VM (Security Enforcement)
FR-7 Enhanced: Valuation library MUST use separate builtin registry from saga
library, explicitly excluding all write-capable handlers:
Blocked handlers:
- position_keeping.initiate_log
- financial_accounting.post_entries
- current_account.execute_debit
- Any state-mutating operations
Implementation:
- shared/pkg/valuation/starlark_runtime.go uses newValuationBuiltins()
- Only includes: market_data, cel_eval, quantity (all read-only)
Verification:
- Stream 2 MUST include security test
- Strategy attempting write operations fails with "name not defined"
- VM-level enforcement (not just convention)
Why: Prevents recursive loops, unauthorized mutations, malicious strategies
triggering writes.
3. Graceful Stale Cache Policy (Resilience)
FR-6 Enhanced: If Reference Data unavailable and L1 cache cold, continue using
expired strategies for up to 1 hour grace period.
Behavior:
- Normal: 5-minute TTL
- Backend down: Extend to 1-hour stale use
- Log high-priority warning
- Metric: valuation_stale_cache_hits_total
Why: "Calculated with slightly old formula" > "System Down"
Allows ops team to restore Reference Data without blocking all transactions.
4. Bucket Filtering for Projected Balance (Fungibility-Aware Tiering)
FR-9 Enhanced: GetProjectedBalance MUST support bucket_id filtering.
Problem: Without filtering, liens for source:solar contaminate tier calculations
for source:grid.
Solution:
- Add bucket_id parameter to GetProjectedBalanceRequest
- Filter queries: WHERE bucket_id = 'kwh_solar'
- Separate tier thresholds per bucket
Implementation:
- Stream 10: Bucket-aware SQL aggregation
- Integration tests verify isolation (solar liens don't affect grid tiers)
Example:
- Tier for source:solar: GetProjectedBalance(KWH, bucket_id=kwh_solar)
- Tier for source:grid: GetProjectedBalance(KWH, bucket_id=kwh_grid)
Impact: Cross-contamination eliminated, fungibility boundaries preserved.
5. Market Data Quality Tracking (Audit & Reconciliation)
FR-5 Enhanced: ValuationBasis MUST include SourceTrustLevel for each market
data observation.
New message:
- MarketDataQuality repeated field
- Fields: observation_id, source_trust_level, instrument_code, value
Quality levels (per ADR-018):
- ESTIMATE (Quality 1): Forecast/projection
- COEFFICIENT (Quality 2): Model-derived
- ACTUAL (Quality 3): Metered/observed
- REVISED (Quality 4): Corrected after audit
Why: If valuation used ESTIMATE at T0, but ACTUAL arrives at T1, the Basis
provides proof of why original (now "wrong") amount was booked.
Use cases:
- Settlement: Explain provisional vs final amounts
- Reconciliation: Track estimate-to-actual adjustments
- Regulatory audit: Demonstrate best available info at transaction time
6. Naming Convention Note (BIAN Alignment)
FR-1 Design Note: Alternative naming consideration - EvaluateAmount vs
GetValuedAmount.
Rationale: "Evaluate" emphasizes computation/simulation (BIAN "Probe" pattern),
"Get" implies simple retrieval.
Current: GetValuedAmount (acceptable)
Alternative: EvaluateAmount (stronger BIAN alignment)
Status: Noted for implementation decision.
Implementation Stream Updates:
Stream 2 (Valuation Library):
- Add separate read-only builtin registry (CRITICAL)
- Add security verification test (CRITICAL)
- Add graceful stale cache policy
- Success: Security test passes (write attempts fail)
Stream 10 (Projected Balance):
- Add bucket_id filtering support (CRITICAL)
- Implement bucket-aware SQL aggregation
- Add isolation tests (solar vs grid liens)
- Success: Bucket filtering prevents cross-contamination
Why These Changes:
Security: VM-level enforcement prevents malicious strategies
Resilience: Graceful degradation > hard failures
Correctness: Bucket filtering preserves fungibility boundaries
Auditability: Quality tracking supports settlement/reconciliation
Integrity: ExecuteLien constraint protects price lock guarantee
Version: 2.2 → 2.3 (Security & Resilience Enhancements)
Reviewed-By: Gemini (PR feedback)
Story Points: 63 (unchanged - security/resilience built into existing streams)
* docs: add Reservation Ledger pattern & audit enhancements (v2.4) - Gemini feedback
Critical architectural refinement: Position Keeping owns reservation data to avoid
cross-service dependencies. Plus calculation path audit trail and canonical bucketing.
1. Reservation Ledger Pattern (CRITICAL ARCHITECTURE FIX)
Problem Identified by Gemini:
- Liens stored in CurrentAccount, but ProjectedBalance queries in Position Keeping
- Cross-service JOINs = performance nightmare
- Calling back to CurrentAccount = circular dependency
Solution:
- CurrentAccount calls position_keeping.RecordReservation() synchronously after lien creation
- Position Keeping maintains local `reservations` table
- GetProjectedBalance is now a simple local query (no cross-service calls)
- ReleaseReservation() called during ExecuteLien/TerminateLien
New RPCs in Position Keeping:
- RecordReservation(account_id, lien_id, amount, bucket_id)
- ReleaseReservation(lien_id, reason)
- GetProjectedBalance (uses local reservations table)
Schema:
- reservations table with (lien_id, account_id, instrument_code, bucket_id, reserved_amount, status)
- Indexed on (account_id, instrument_code, status, bucket_id)
Impact: Eliminates circular dependencies, enables O(1) projected balance queries.
2. Calculation Path Audit Trail
New field in ValuationBasis: calculation_path (repeated string)
- Breadcrumbs of decision branches taken during Starlark execution
- Strategies call record_path("tier_2_applied") at decision points
- Auditors see which parts of complex strategy executed without re-running logic
Use case: M+14 regulatory settlement audits need to understand WHY a specific
rate was applied without re-executing the entire strategy.
Example:
calculation_path: ["tier_2_applied", "premium_discount_applied"]
3. Canonical Bucketing Library (shared/pkg/bucketing)
Problem: bucket_id calculation must be identical across all services to prevent
"Bucket Drift" where same InstrumentAmount produces different bucket keys.
Solution:
- New shared library: shared/pkg/bucketing
- Single function: CalculateBucketID(InstrumentAmount) string
- Canonical key generation with sorted attributes
- Used by: CurrentAccount, InternalBankAccount, PositionKeeping, ReferenceData
Enforcement: Direct bucket_id string construction is FORBIDDEN.
4. Market Information Graceful Degradation
Extended FR-6: If Market Information is unavailable:
- Return last known good value from L1 cache
- OR use tenant-configured default rate
- Set degraded_mode: true in ValuationBasis
- Log high-priority warning
- Metric: valuation_degraded_mode_total
Rationale: "Calculated with fallback rate" > "System Down"
5. Implementation Stream Updates
Stream 4 (Current Account): 8 → 10 points
- Add RecordReservation call after lien creation
- Add ReleaseReservation call in ExecuteLien/TerminateLien
- Add record_path() builtin for calculation audit
- Add graceful degradation for Market Information
- Rollback test: If RecordReservation fails, delete lien
Stream 5 (Internal Bank Account): 8 → 10 points
- Mirror Stream 4 changes
Stream 10 (Position Keeping): 5 → 8 points
- Add reservations table and schema
- Add RecordReservation/ReleaseReservation RPCs
- GetProjectedBalance uses local reservations table
- No cross-service queries
Stream 11 (NEW): Shared Bucketing Library (2 points)
- Create shared/pkg/bucketing package
- Implement canonical CalculateBucketID()
- Unit tests for edge cases
- Audit all services for consistent usage
Total Story Points: 63 → 72 (+9 points, +14%)
Why These Changes:
Architecture:
- Reservation Ledger eliminates cross-service dependency nightmare
- Position Keeping can scale independently of Account Services
- Local queries enable O(1) projected balance performance
Audit:
- Calculation path enables regulatory compliance without strategy re-execution
- degraded_mode flag identifies transactions that may need reconciliation
Integrity:
- Canonical bucketing prevents subtle "Bucket Drift" bugs
- Same InstrumentAmount = same bucket_id across entire platform
Resilience:
- Market Information graceful degradation prevents cascading failures
- Transaction proceeds with fallback rate, flagged for review
Version: 2.3 → 2.4 (Reservation Ledger & Audit Enhancements)
Reviewed-By: Gemini (LGTM with PK Reservation RPC recommendation)
Story Points: 72 (11 streams)
* docs: PRD v2.5 - idempotency, drift detection, architecture guards
Incorporates Gemini's v2.4 feedback (7 items):
1. RecordReservation Idempotency (P0)
- lien_id is idempotency key
- Retry returns success without double-counting
2. Basis Drift Detection
- ExecuteLien emits VALUATION_STALE event if basis > 30 days old
- Configurable threshold for reconciliation flagging
3. Read-Only VM Architecture Enforcement (CRITICAL)
- Internal package isolation prevents import of write clients
- CI verification script fails if write imports added
4. Calculation Path Size Limit
- Max 20 entries to prevent JSON bloat
- Truncates silently with warning log
5. Platform Default Inheritance
- SYSTEM strategies for unconfigured tenants
- Motive/UN WFP scenarios work out-of-box
6. Degraded Mode in Ledger (CRITICAL)
- degraded_mode flag propagates to PositionEntry
- Enables audit query for degraded transactions
7. Output Instrument Validation (Runtime Type Check)
- VALUATION_OUTPUT_MISMATCH hard error on mismatch
Story points: 72 → 75 (Stream 2: +2, Stream 3: +1)
* docs: Address CodeRabbit inline comments (5 items)
1. Story point calculation error (CRITICAL)
- Fixed: 31 points → 75 points (+10% vs standalone, not 54% reduction)
- Added justification: TOCTOU safety, Reservation Ledger, resilience
2. CEL cost limits specification
- Added: 10,000 cost units max, 100ms timeout
- Cost accounting rules for each operation type
- Error response format (COST_LIMIT_EXCEEDED)
3. Starlark error handling example
- Added parameter validation
- Added market_data error handling
- Documented sandbox constraints and limits
4. Rollback failure scenario
- Handle DeleteLien failure during reservation rollback
- Emit metrics (liens.orphaned.total)
- Publish to dead-letter queue for async cleanup
5. Strategy versioning and migration path
- Version resolution rules (latest non-deprecated)
- Migration path across phases
- logic_hash change behavior
- Deprecated strategy rejection with bi-temporal exception
- Operational notifications (Slack alerts, P2/P3 events)
* docs: PRD v2.6 - Ghost Pricing prevention & lien immutability
Incorporates Gemini's v2.5 feedback:
1. Ghost Pricing Prevention (CRITICAL)
- GetValuedAmount and InitiateLien MUST share same private valuation function
- Prevents mobile app showing different price than transaction executes
- Added valuateInternal() pattern with test requirement
2. Lien Immutability Enforcement (Database Level)
- Option A: CockroachDB trigger to prevent valued_amount modification
- Option B: Application-level repository enforcement
- Audit requirement for security event logging
3. Calculation Path Truncation Warning (Saga Debugger)
- Added Warnings field to ValuationBasis
- CALCULATION_PATH_TRUNCATED warning visible to tenants
- Shows omitted steps count and details
Gemini approvals (no changes needed):
- Smart Account paradigm (FR-1)
- Atomic Valuation & Price Lock (FR-8)
- Reservation Ledger idempotency (FR-9)
- Architecture Enforcement Option A (FR-7)
- Passport Pattern audit (FR-5)
- Canonical Bucketing (Stream 11)
* docs: PRD v2.7 - Dimension-tagged buckets & Ghost Pricing regression
Incorporates Gemini's v2.6 feedback:
1. Ghost Pricing Regression Test (MANDATORY)
- 1,000 random inputs through GetValuedAmount and InitiateLien
- Assert identical to last decimal place
- Catches code path divergence between inquiry and transactional
2. Dimension Tag in bucket_id (Stream 11)
- Prefix: "monetary_", "commodity_", "compute_"
- Enables database CHECK constraint enforcement
- Example: monetary_gbp, commodity_kwh_source=solar
- Prevents MONETARY account accepting COMMODITY bucket
Gemini approvals confirmed:
- Embedded Authority model (valuation as Account behavior)
- Atomic Valuation & Price Lock (Gold Standard fix)
- Reservation Ledger fail-fast behavior
- Bi-temporal replay audit strategy
- Read-only VM enforcement
Story points: 75 → 76 (+1 for dimension tag in Stream 11)
* docs: PRD v2.8 - BIAN alignment assessment documentation
Adds Section 13: BIAN Alignment Assessment based on independent review.
Assessment: 85-90% BIAN compliance
Strong Alignments:
- Service Domain Mappings (5/5 excellent)
- Current Account Management
- Position Keeping
- Asset Valuation
- Public Reference Data Management
- Market Information Management
- Functional Patterns (FULFILL, TRACK, ANALYZE, CATALOG)
- Action Terms (Initiate, Execute, Retrieve, Terminate)
Documented Deviation:
- Embedded valuation library vs separate service domain
- Justified by: performance (3 vs 4 hops), domain modeling, operational simplicity
- BIAN-compatible: could expose as separate service if needed
Future Considerations:
- Terminology mapping table for enhanced BIAN alignment
- References to BIAN Semantic API Guide V8.1 and Service Landscape 12.0
No story point changes - documentation only.
* docs(prd): Update valuation PRD to v2.9 with full BIAN alignment
Implements 100% BIAN compliance with the following changes:
BIAN Terminology Adoption:
- GetValuedAmount → EvaluateAssetValuation (BIAN "Evaluate" action term)
- ValuationBasis → ValuationAnalysis (BIAN analysis record)
- Valuation Strategy → ValuationMethod (BIAN algorithmic method)
- valuation_assignments → valuation_features (Behavior Qualifier)
Control Record / Behavior Qualifier Pattern:
- AccountFulfillmentArrangement as Control Record (CR)
- ValuationFeature as Behavior Qualifier (BQ)
- PositionFeature as additional BQ
- New valuation_features table schema
Virtual Service Pattern:
- Documented embedded library as BIAN "Virtual Service"
- External consumers see standard BIAN interface
- Internal implementation uses zero-latency library calls
Stream Task Updates:
- Stream 1: Account Valuation Features (from Strategy Assignments)
- Stream 3: Reference Data ValuationMethod Storage
- Stream 7: Energy/Commodity ValuationMethods
- All code examples updated to use BIAN terminology
This achieves the 100% BIAN compliance recommended by Gemini review.
* docs(prd): Add multi-asset acceptance pattern and native instrument validation
Addresses PR feedback to make implicit patterns explicit:
Multi-Asset Acceptance Pattern:
- Documents that accounts can accept multiple input types
- Shows CO₂ account example with KWH, THERM, GBP, DIESEL inputs
- Explains resolution logic via (account_id, instrument_code) lookup
- Adds visual diagram of multi-input account pattern
Account Native Instrument Validation:
- New validation at feature assignment time
- Ensures method output matches account's native instrument
- Prevents CO₂ account from being assigned a GBP-outputting method
- Documents both validation layers (assignment-time + runtime)
* docs(prd): upgrade Asset Valuation PRD to v3.0 with Referenced Policies
Major architectural change: move from inline CEL expressions to
named, versioned Policy objects.
Key changes:
- Remove cel_eval(string, context) builtin from valuation library
- Add run_policy(policy_name, context) builtin for named policy invocation
- Add valuation_policies table schema in Reference Data
- Enforce "No-Inline-Math" constraint: Starlark is procedure, not formula
- Add Policy cost validation at creation time (reject > 10,000 units)
- Add Policy dry-run endpoint for testing before deployment
- Update Stream 2 tasks for policy architecture (12 → 15 points)
- Update Stream 3 tasks to include Policy storage (6 → 10 points)
- Update all example methods to use run_policy() instead of cel_eval()
- Add anti-pattern examples showing forbidden patterns
Benefits:
- Auditability: Every formula is a first-class, versioned object
- Testability: Policies can be unit-tested in isolation
- Governance: Finance teams can update Policies without touching Starlark
- AI Safety: AI generates procedure flow, not formula strings
---------
Co-authored-by: Ben Coombs <bjcoombs@users.noreply.github.com>1 parent e616cdc commit 73a2cc9
1 file changed
Lines changed: 3075 additions & 0 deletions
0 commit comments