Skip to content

Commit 73a2cc9

Browse files
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

File tree

0 commit comments

Comments
 (0)