import { z } from "zod"; import { reviewSettlementAdjustmentRequestSchema } from "./reviewSettlementAdjustmentContracts.js"; import { reviewDevelopmentAdjustmentRequestSchema } from "./reviewDevelopmentAdjustmentContracts.js"; import { parseReviewBoundary } from "./reviewBoundary.js"; import { recipeDevelopmentProjectionRequestSchema } from "./recipeDevelopmentContracts.js"; import { recipeExposureProjectionRequestSchema } from "./recipeExposureContracts.js"; import { boundedGroupedDevelopmentRequestSchema, boundedGroupedDevelopmentResultSchema } from "./boundedGroupedHistory.js"; import { reviewDevelopmentTransformRequestSchema, reviewDevelopmentAccumulationRequestSchema } from "./reviewDevelopmentContracts.js"; import { REVIEW_DEVELOPMENT_CONTRACT_VERSION, REVIEW_RECONCILIATION_LIMITS, validateReserveBalanceInput, validateReserveReconciliationRequest, type ReserveBalanceInput, type ReserveReconciliationRequest, REVIEW_RESERVE_CONTROL_LIMIT, validateReserveControlInput, createDevelopmentReserveBalance, type ReserveControlInput, REVIEW_SELECTION_LIMITS, validateEstimateSelectionRequest, type EstimateSelectionRequest, DEVELOPMENT_INPUT_CONTRACT_VERSION, isDiagnosticToken, isDiagnosticPlainRecord, normalizeDevelopmentInput, validateNormalizedDevelopmentInput, type NormalizedDevelopmentInput, ANALYSIS_REFERENCE_KINDS, REVIEW_SEMANTICS_CONTRACT_VERSION, validateAnalysisReference, validateQuantitySemantics, validateAnalysisContext, checkAnalysisCompatibility, isRealIsoDate, type AnalysisReference, type QuantitySemantics, type AnalysisContext, type AnalysisCompatibilityResult, validateDevelopmentCalendar, remapDevelopmentAges, accumulateDevelopmentIntervals, aggregateDevelopmentOrigins, type DevelopmentCalendarInput, type DevelopmentAgeRemapping, type DevelopmentOriginAggregation, SCALAR_ASSUMPTION_KINDS, validateReviewCountDefinition, type ReviewCountDefinition, ASSUMPTION_CONTRACT_VERSION, validateAssumptionSnapshot, createAssumptionSnapshot, validateAnalysisSnapshot, bindAnalysisSemantics, type AssumptionSnapshot, type AnalysisSnapshot, type JsonValue, validateAlignedExposure, validateExpectedLossRequest, calibrateBfPrior, type AlignedExposureInput, type ExpectedLossRequest, type BfPriorCalibrationResult, REVIEW_MODEL_LIMITS, REVIEW_ODP_LIMITS, REVIEW_CLARK_LIMITS, REVIEW_COMPARISON_LIMITS, validateModelComparisonRequest, type ModelComparisonRequest, validateModelRequest, validateModelDevelopmentSource, projectBoundedGroupedDevelopment, type BoundedGroupedProjectionRequest, type BoundedGroupedProjection, type ModelRequest, type ModelDevelopmentSource, } from "@actuarial-ts/core"; const token = z.string().refine(isDiagnosticToken, "Expected a valid nonempty identifier"); const gridFields = { kind: z.enum(["paid", "incurred", "caseReserve", "reportedCount", "openCount", "closedCount", "closedWithPayCount"]), origins: z.array(token).min(1), ages: z.array(z.number().int().safe().nonnegative()), values: z.array(z.array(z.number().finite().nullable())), }; /** Structural schemas; the parsers below additionally validate semantic consistency. */ export const developmentGridInputSchema = z.object(gridFields).strict(); export const normalizedDevelopmentInputSchema = z.object({ ...gridFields, ages: z.array(z.number().int().safe().positive()), contractVersion: z.literal(DEVELOPMENT_INPUT_CONTRACT_VERSION), originBoundary: z.object({ kind: z.literal("structural-origin-zero"), ageMonths: z.literal(0), value: z.literal(0) }).strict(), boundaryEvidence: z.array(z.object({ origin: token, source: z.enum(["implicit", "explicit-zero", "explicit-null"]), sourceValue: z.literal(0).nullable() }).strict()), coverage: z.object({ originsWithObservations: z.array(token), originsWithoutObservations: z.array(token), observedCells: z.number().int().safe().nonnegative() }).strict(), calculationIdentity: token, sourceIdentity: token, }).strict(); /** Imports a cumulative grid, preserving its mandatory zero boundary and source evidence. */ export function parseDevelopmentInput(input: unknown): NormalizedDevelopmentInput { return normalizeDevelopmentInput(parseReviewBoundary(developmentGridInputSchema, input)); } /** Revalidates serialized content and identities; does not authenticate its source. */ export function parseNormalizedDevelopmentInput(input: unknown): NormalizedDevelopmentInput { return validateNormalizedDevelopmentInput(parseReviewBoundary(normalizedDevelopmentInputSchema, input)); } export const analysisReferenceSchema = z.object({ kind: z.enum(ANALYSIS_REFERENCE_KINDS), id: token, version: token, contentIdentity: token }).strict(); const countAttributes = z.record(z.string().transform(key => `:${key}`), z.union([z.string(), z.number().finite(), z.boolean(), z.null()])).transform(record => Object.fromEntries(Object.entries(record).map(([key, value]) => [key.slice(1), value]))); export const reviewCountDefinitionSchema = z.object({ reference: analysisReferenceSchema, definition: z.object({ id: token, displayName: token, subject: z.enum(["claim", "claimant", "policy", "occurrence", "other", "unknown"]), unit: token, description: token, attributes: countAttributes.optional() }).strict() }).strict(); export function parseReviewCountDefinition(input: unknown): ReviewCountDefinition { return validateReviewCountDefinition(parseReviewBoundary(reviewCountDefinitionSchema, input)); } /** Generic JSON content is preflight-checked by every parser before Zod accesses it. */ export const analysisSnapshotSchema = z.object({ reference: analysisReferenceSchema, content: z.custom(() => true) }).strict(); // Preserve prototype-like unit names across Zod 3 record construction. const factors = z.record(z.string().transform((key) => `:${key}`), z.number().int().min(-32).max(32)) .transform((record) => Object.fromEntries(Object.entries(record).map(([key, value]) => [key.slice(1), value]))); export const quantitySemanticsSchema = z.object({ role: token, unitFactors: factors, scale: z.number().positive().finite(), development: z.enum(["cumulative", "incremental", "point-in-time", "unknown", "ultimate"]), aggregation: z.enum(["sum", "ratio-of-sums", "none"]), denominator: analysisReferenceSchema.nullable(), }).strict(); const date = z.string().refine(isRealIsoDate, "Expected a real ISO calendar date"); const contextReference = z.union([analysisReferenceSchema, z.literal("not-applicable"), z.null()]); export const analysisContextSchema = z.object({ contractVersion: z.literal(REVIEW_SEMANTICS_CONTRACT_VERSION), id: token, version: token, populations: z.object({ claims: contextReference, financial: contextReference, reporting: contextReference, exposure: contextReference }).strict(), financialBasis: contextReference, originBasis: z.enum(["accident", "report", "policy"]), comparisonClock: z.enum(["common-evaluation-date", "since-period-start", "since-period-end", "matched-accident-cohorts"]), origins: z.array(z.object({ id: token, startDate: date, endDate: date }).strict()).min(1), valuationDate: date, knowledgeDate: date, accountingDate: date.nullable(), sourceRevisions: z.array(analysisReferenceSchema), adjustments: z.array(analysisReferenceSchema), }).strict(); const operand = z.object({ quantity: quantitySemanticsSchema, context: analysisContextSchema }).strict(); export const analysisCompatibilityRequestSchema = z.object({ operation: z.enum(["comparison", "selection", "aggregation", "reconciliation"]), left: operand, right: operand, contrasts: z.array(z.enum(["quantity", "units", "denominator", "population", "financial-basis", "valuation", "knowledge", "origins", "adjustments"])).optional(), populationDeclaration: z.object({ relationship: z.enum(["same", "disjoint", "overlapping"]), rationale: token }).strict().optional(), unknownBasisDeclaration: z.object({ relationship: z.literal("same"), rationale: token }).strict().optional(), aggregationAxis: z.enum(["population", "origin", "interval", "valuation"]).optional(), }).strict(); export function parseAnalysisReference(input: unknown): AnalysisReference { return validateAnalysisReference(parseReviewBoundary(analysisReferenceSchema, input)); } export function parseQuantitySemantics(input: unknown): QuantitySemantics { return validateQuantitySemantics(parseReviewBoundary(quantitySemanticsSchema, input)); } export function parseAnalysisContext(input: unknown): AnalysisContext { return validateAnalysisContext(parseReviewBoundary(analysisContextSchema, input)); } export function parseAnalysisCompatibility(input: unknown): AnalysisCompatibilityResult { return checkAnalysisCompatibility(parseReviewBoundary(analysisCompatibilityRequestSchema, input)); } const originPeriodSchema = z.object({ id: token, startDate: date, endDate: date }).strict(); export const developmentCalendarInputSchema = z.object({ development: normalizedDevelopmentInputSchema, origins: z.array(originPeriodSchema), ageConvention: z.enum(["calendar-period-end", "elapsed-months"]), valuations: z.array(z.array(date.nullable())), }).strict(); export const developmentAgeMappingSchema = z.object({ sourceConvention: z.enum(["period-end-index", "different-time-anchor"]), ages: z.array(z.object({ source: z.number().int().safe().nonnegative(), targetMonths: z.number().int().safe().nonnegative() }).strict()), rationale: token, }).strict(); export const developmentIntervalViewSchema = z.object({ kind: z.literal("development-interval-view"), measure: gridFields.kind, origins: gridFields.origins, intervals: z.array(z.object({ fromAgeMonths: z.number().int().safe().nonnegative(), throughAgeMonths: z.number().int().safe().positive() }).strict()), values: gridFields.values, sourceIdentity: token, opening: z.discriminatedUnion("kind", [ z.object({ kind: z.literal("structural-origin-zero") }).strict(), z.object({ kind: z.literal("declared-opening"), ageMonths: z.number().int().safe().positive(), values: z.array(z.number().finite().nullable()), rationale: token }).strict(), ]), }).strict(); export const developmentOriginAggregationRequestSchema = z.object({ source: developmentCalendarInputSchema, quantity: quantitySemanticsSchema, targets: z.array(z.object({ origin: originPeriodSchema, members: z.array(token), observations: z.array(z.object({ ageMonths: z.number().int().safe().positive(), valuationDate: date }).strict()) }).strict()), partialPeriodPolicy: z.enum(["reject", "retain-partial"]), }).strict(); export function parseDevelopmentCalendar(input: unknown): DevelopmentCalendarInput { return validateDevelopmentCalendar(parseReviewBoundary(developmentCalendarInputSchema, input)); } export function parseRemappedDevelopmentInput(input: unknown): DevelopmentAgeRemapping { const parsed = parseReviewBoundary(z.object({ source: developmentGridInputSchema, mapping: developmentAgeMappingSchema }).strict(), input); return remapDevelopmentAges(parsed.source, parsed.mapping); } /** Imports contiguous interval data and reconstructs only supported cumulative cells. */ export function parseDevelopmentIntervals(input: unknown): NormalizedDevelopmentInput { return accumulateDevelopmentIntervals(parseReviewBoundary(developmentIntervalViewSchema, input)); } export function parseDevelopmentOriginAggregation(input: unknown): DevelopmentOriginAggregation { return aggregateDevelopmentOrigins(parseReviewBoundary(developmentOriginAggregationRequestSchema, input)); } export const reviewAssumptionValueSchema = z.discriminatedUnion("kind", [ z.object({ kind: z.enum(SCALAR_ASSUMPTION_KINDS.filter(kind => kind !== "expected-ultimate-count") as [Exclude, ...Exclude[]]), quantity: quantitySemanticsSchema, defaultValue: z.number().finite().nullable(), byOrigin: z.array(z.object({ origin: token, value: z.number().finite() }).strict()) }).strict(), z.object({ kind: z.literal("expected-ultimate-count"), quantity: quantitySemanticsSchema, countDefinition: reviewCountDefinitionSchema, defaultValue: z.number().finite().nullable(), byOrigin: z.array(z.object({ origin: token, value: z.number().finite() }).strict()) }).strict(), z.object({ kind: z.literal("development-pattern"), ages: z.array(z.number().int().safe().positive()), factors: z.array(z.number().positive().finite().nullable()), tailFactor: z.number().positive().finite().nullable(), intentReference: analysisReferenceSchema.nullable() }).strict(), z.object({ kind: z.literal("case-outstanding-pattern"), ages: z.array(z.number().int().safe().positive()), caseRunoffRatios: z.array(z.number().finite().nonnegative()), paidOnPriorCaseRatios: z.array(z.number().finite()), tailPaidOnCase: z.number().finite().nonnegative() }).strict(), z.object({ kind: z.literal("fisher-lange-disposal-pattern"), ages: z.array(z.number().int().safe().positive()), disposalRates: z.array(z.number().finite().nonnegative()) }).strict(), ]); export const reviewAssumptionProvenanceSchema = z.discriminatedUnion("kind", [ z.object({ kind: z.literal("external"), sources: z.array(analysisReferenceSchema) }).strict(), z.object({ kind: z.literal("manual"), actorId: token, sources: z.array(analysisReferenceSchema) }).strict(), z.object({ kind: z.enum(["estimated-current-data", "estimated-separate-data"]), methodId: token, calibrationRun: analysisReferenceSchema, data: z.array(analysisReferenceSchema), assumptions: z.array(analysisReferenceSchema) }).strict(), ]); export const reviewAssumptionDefinitionSchema = z.object({ contractVersion: z.literal(ASSUMPTION_CONTRACT_VERSION), id: token, version: token, value: reviewAssumptionValueSchema, applicability: z.object({ origins: z.array(token).nullable(), effectiveFrom: date, effectiveThrough: date.nullable() }).strict(), knownDate: date, provenance: reviewAssumptionProvenanceSchema, rationale: token, adoption: z.object({ actor: z.enum(["actuary", "agent", "caller"]), actorId: token, adoptedDate: date, rationale: token }).strict().nullable(), supersedes: analysisReferenceSchema.nullable(), }).strict(); export const assumptionSnapshotSchema = reviewAssumptionDefinitionSchema.extend({ reference: analysisReferenceSchema }).strict(); export function parseAssumptionSnapshot(input: unknown): AssumptionSnapshot { return validateAssumptionSnapshot(parseReviewBoundary(assumptionSnapshotSchema, input)); } export function parseReviewAssumptionDefinition(input: unknown): AssumptionSnapshot { const { contractVersion: _version, ...definition } = parseReviewBoundary(reviewAssumptionDefinitionSchema, input); return createAssumptionSnapshot(definition); } export function parseAnalysisSnapshot(input: unknown): AnalysisSnapshot { return validateAnalysisSnapshot(parseReviewBoundary(analysisSnapshotSchema, input)); } export function parseBoundAnalysisSemantics(input: unknown): ReturnType { return bindAnalysisSemantics(parseReviewBoundary(z.object({ context: analysisContextSchema, quantities: z.array(quantitySemanticsSchema), snapshots: z.array(analysisSnapshotSchema) }).strict(), input)); } const attributes = z.record(z.string().transform((key) => `:${key}`), z.union([z.string(), z.number().finite(), z.boolean(), z.null()])) .transform((record) => Object.fromEntries(Object.entries(record).map(([key, value]) => [key.slice(1), value]))); export const alignedExposureInputSchema = z.object({ preparation: z.object({ kind: z.literal("recipe-exposure"), request: recipeExposureProjectionRequestSchema }).strict().optional(), basis: z.object({ id: token, displayName: token, basis: z.enum(["earned", "written", "in-force", "other", "unknown"]), unit: token, description: token, sourceDescription: token.optional(), attributes: attributes.optional() }).strict(), basisReference: analysisReferenceSchema, quantity: quantitySemanticsSchema, population: analysisReferenceSchema, sourceRevisions: z.array(analysisReferenceSchema), adjustments: z.array(analysisReferenceSchema), missingPolicy: z.literal("unavailable"), rows: z.array(z.object({ origin: token, period: originPeriodSchema, value: z.number().finite().nullable(), complete: z.boolean(), valuationDate: date.nullable(), knownDate: date }).strict()), }).strict(); const expectedLossAdjustment = z.discriminatedUnion("kind", [z.object({ kind: z.literal("none") }).strict(), z.object({ kind: z.literal("factor"), value: z.number().finite().positive(), reference: analysisReferenceSchema }).strict(), z.object({ kind: z.literal("unavailable"), reason: token, reference: analysisReferenceSchema }).strict()]); const expectedLossRow = z.object({ origin: token, emergence: z.object({ ageMonths: z.number().int().safe().nonnegative(), latestValue: z.number().finite().nullable(), development: z.object({ kind: z.enum(["cdf", "proportion"]), value: z.number().finite().nullable() }).strict() }).strict().nullable(), lossAdjustment: expectedLossAdjustment, exposureAdjustment: expectedLossAdjustment, }).strict(); const expectedLossBase = { id: token, context: analysisContextSchema, lossQuantity: quantitySemanticsSchema, exposure: alignedExposureInputSchema.nullable(), rows: z.array(expectedLossRow), missingPolicy: z.enum(["retain-unavailable", "reject-incomplete"]) }; export const expectedLossRequestSchema = z.discriminatedUnion("method", [ z.object({ ...expectedLossBase, method: z.enum(["bornhuetterFerguson", "expectedClaims"]), assumption: assumptionSnapshotSchema }).strict(), z.object({ ...expectedLossBase, method: z.literal("capeCod"), calibration: z.object({ decay: z.number().finite().min(0).max(1), distance: z.literal("declared-origin-row-steps") }).strict() }).strict(), ]); export const bfPriorCalibrationRequestSchema = z.object({ id: token, context: analysisContextSchema, ultimateQuantity: quantitySemanticsSchema, exposure: alignedExposureInputSchema, modelReference: analysisReferenceSchema, modelAssumptions: z.array(analysisReferenceSchema), estimates: z.array(z.object({ origin: token, ultimate: z.number().finite().nullable(), developedProportion: z.number().finite().nonnegative().nullable() }).strict()), selection: z.object({ origins: z.array(token), maturityThreshold: z.number().finite().nonnegative(), fallback: z.enum(["none", "all-eligible-in-window"]) }).strict(), sourceRelationship: z.enum(["current-data", "separate-data"]), assumption: reviewAssumptionDefinitionSchema.omit({ contractVersion: true, value: true, provenance: true }), }).strict(); export function parseAlignedExposure(input: unknown): AlignedExposureInput { return validateAlignedExposure(parseReviewBoundary(alignedExposureInputSchema, input)); } export function parseExpectedLossRequest(input: unknown): ExpectedLossRequest { return validateExpectedLossRequest(parseReviewBoundary(expectedLossRequestSchema, input)); } export function calibrateBfPriorFromInput(input: unknown): BfPriorCalibrationResult { return calibrateBfPrior(parseReviewBoundary(bfPriorCalibrationRequestSchema, input)); } export const boundedGroupedProjectionRequestSchema: z.ZodType = z.object({ id: token, version: token, grouping: boundedGroupedDevelopmentRequestSchema, grouped: boundedGroupedDevelopmentResultSchema, measureId: token, valuationDate: date, accountingDate: date.nullable(), sourceReplay: z.object({ reference: analysisReferenceSchema, sourceArtifacts: z.array(z.object({ id: token, sha256: z.string().regex(/^[a-f0-9]{64}$/) }).strict()).min(1).max(256), mappedRecordSha256: z.string().regex(/^[a-f0-9]{64}$/), revisionView: z.enum(["restated", "as-known"]), }).strict(), meaning: z.object({ quantity: quantitySemanticsSchema, financialBasis: analysisSnapshotSchema, actorId: token, knownDate: date, rationale: token }).strict(), }).strict(); export function parseBoundedGroupedProjectionRequest(input: unknown): BoundedGroupedProjectionRequest { return parseReviewBoundary(boundedGroupedProjectionRequestSchema, input); } export function projectBoundedGroupedDevelopmentFromInput(input: unknown): BoundedGroupedProjection { return projectBoundedGroupedDevelopment(parseBoundedGroupedProjectionRequest(input)); } export const modelDevelopmentSourceSchema = z.object({ calendar: developmentCalendarInputSchema, knownDate: date, reference: analysisReferenceSchema, preparation: z.object({ kind: z.literal("recipe-development"), request: recipeDevelopmentProjectionRequestSchema }).strict().optional(), grouped: z.object({ kind: z.literal("bounded-grouped-development"), request: boundedGroupedProjectionRequestSchema }).strict().optional(), transformation: z.object({ kind: z.literal("review-development-transform"), contractVersion: z.literal(REVIEW_DEVELOPMENT_CONTRACT_VERSION), sdkVersion: token, request: reviewDevelopmentTransformRequestSchema }).strict().optional(), accumulation: z.object({ kind: z.literal("review-development-accumulation"), contractVersion: z.literal(REVIEW_DEVELOPMENT_CONTRACT_VERSION), sdkVersion: token, request: reviewDevelopmentAccumulationRequestSchema }).strict().optional(), adjustment: z.object({ kind: z.literal("review-development-adjustment"), contractVersion: z.literal("actuarial-review-development-adjustment/1"), sdkVersion: token, request: z.union([reviewDevelopmentAdjustmentRequestSchema, reviewSettlementAdjustmentRequestSchema]) }).strict().optional() }).strict(); export const modelFactorPolicySchema = z.discriminatedUnion("kind", [ z.object({ kind: z.literal("reject-incomplete") }).strict(), z.object({ kind: z.literal("identity-for-missing"), rationale: token }).strict(), ]); export const modelExposureAdjustmentsSchema = z.discriminatedUnion("kind", [ z.object({ kind: z.literal("none") }).strict(), z.object({ kind: z.literal("by-origin"), rows: z.array(z.object({ origin: token, loss: expectedLossAdjustment, exposure: expectedLossAdjustment }).strict()) }).strict(), ]); const modelCommon = { id: token, context: analysisContextSchema, source: modelDevelopmentSourceSchema.nullable(), snapshots: z.array(analysisSnapshotSchema), policies: z.object({ missingResults: z.enum(["retain-unavailable", "reject-incomplete"]), futureObservations: z.enum(["reject", "truncate"]), factors: modelFactorPolicySchema }).strict(), limits: z.object({ maximumOrigins: z.number().int().positive().max(REVIEW_MODEL_LIMITS.maximumOrigins), maximumCells: z.number().int().positive().max(REVIEW_MODEL_LIMITS.maximumCells) }).strict(), }; const modelBase = { ...modelCommon, lossQuantity: quantitySemanticsSchema }; const expectedModelOptions = { prior: assumptionSnapshotSchema, exposure: alignedExposureInputSchema.nullable(), adjustments: modelExposureAdjustmentsSchema }; export const modelVariancePolicySchema = z.enum(["allow-documented-fallback", "require-estimate-or-mack-extrapolation"]); export const modelMackTailUncertaintySchema = z.discriminatedUnion("kind", [ z.object({ kind: z.literal("none") }).strict(), z.object({ kind: z.literal("extrapolate"), rationale: token }).strict(), z.object({ kind: z.literal("mack-1999"), standardError: z.number().finite().nonnegative(), sigma: z.number().finite().nonnegative(), rationale: token }).strict(), ]); export const modelClarkOptionsSchema = z.object({ curve: z.enum(["loglogistic", "weibull"]), target: z.discriminatedUnion("kind", [z.object({ kind: z.literal("ultimate"), rationale: token }).strict(), z.object({ kind: z.literal("development-age"), months: z.number().int().safe().positive(), rationale: token }).strict()]), negativeIncrements: z.enum(["reject", "allow-qualified"]), varianceClamping: z.enum(["retain-qualified", "uncertainty-unavailable"]), executionLimits: z.object({ maximumParameters: z.number().int().positive().max(REVIEW_CLARK_LIMITS.maximumParameters), maximumLikelihoodCellEvaluations: z.number().int().positive().max(REVIEW_CLARK_LIMITS.maximumLikelihoodCellEvaluations) }).strict(), }).strict(); export const modelMunichLastColumnSigmaSchema = z.discriminatedUnion("kind", [ z.object({ kind: z.literal("mack-extrapolation"), rationale: token }).strict(), z.object({ kind: z.literal("selected"), paid: z.number().finite().nonnegative(), incurred: z.number().finite().nonnegative(), actorId: token, knownDate: date, rationale: token }).strict(), ]); const modelRequestVariants: [z.AnyZodObject, z.AnyZodObject, ...z.AnyZodObject[]] = [ z.object({ ...modelBase, method: z.literal("frequencySeverity"), options: z.object({ counts: z.object({ source: modelDevelopmentSourceSchema, context: analysisContextSchema, quantity: quantitySemanticsSchema, countDefinition: reviewCountDefinitionSchema }).strict(), countDevelopment: assumptionSnapshotSchema, severityDevelopment: assumptionSnapshotSchema, relationship: z.object({ relationship: z.literal("same-covered-claims"), rationale: token, evidence: z.array(analysisReferenceSchema) }).strict(), maturity: z.discriminatedUnion("kind", [z.object({ kind: z.literal("require-current-pairs") }).strict(), z.object({ kind: z.literal("native-latest-qualified"), rationale: token }).strict()]), }).strict() }).strict(), z.object({ ...modelBase, method: z.literal("chainLadder"), options: z.object({ development: assumptionSnapshotSchema }).strict() }).strict(), z.object({ ...modelBase, method: z.literal("munichChainLadder"), options: z.object({ counterpart: z.object({ source: modelDevelopmentSourceSchema, quantity: quantitySemanticsSchema }).strict(), observationStructure: z.literal("require-identical-mask"), lastColumnSigma: modelMunichLastColumnSigmaSchema, tail: z.literal("none"), }).strict() }).strict(), z.object({ ...modelBase, method: z.literal("caseOutstanding"), options: z.object({ case: z.object({ source: modelDevelopmentSourceSchema, quantity: quantitySemanticsSchema }).strict(), pattern: assumptionSnapshotSchema, maturity: z.literal("latest-joint-observation-qualified"), negativeCase: z.enum(["reject", "allow-qualified"]), }).strict() }).strict(), z.object({ ...modelBase, method: z.literal("fisherLange"), options: z.object({ closedCounts: z.object({ source: modelDevelopmentSourceSchema, context: analysisContextSchema, quantity: quantitySemanticsSchema, countDefinition: reviewCountDefinitionSchema }).strict(), ultimateCounts: assumptionSnapshotSchema, disposalPattern: assumptionSnapshotSchema, severityTrend: assumptionSnapshotSchema, relationship: z.object({ relationship: z.literal("same-covered-claims"), rationale: token, evidence: z.array(analysisReferenceSchema) }).strict(), maturity: z.literal("latest-joint-observation-qualified"), negativePaidIncrements: z.enum(["reject", "allow-qualified"]), disposalTotal: z.object({ kind: z.literal("require-unit-sum"), tolerance: z.number().finite().min(0).max(0.01) }).strict(), }).strict() }).strict(), z.object({ ...modelCommon, method: z.literal("salvageSubro"), recoveryQuantity: quantitySemanticsSchema, options: z.object({ development: assumptionSnapshotSchema, signConvention: z.literal("positive-recovery-reduces-loss"), negativeCumulativeRecoveries: z.enum(["reject", "allow-qualified"]), cumulativeDecreases: z.enum(["reject", "allow-qualified"]), recoveryComponentIds: z.array(token).min(1), }).strict() }).strict(), z.object({ ...modelBase, method: z.enum(["bornhuetterFerguson", "benktander"]), options: z.object({ ...expectedModelOptions, development: assumptionSnapshotSchema.nullable() }).strict() }).strict(), z.object({ ...modelBase, method: z.literal("expectedClaims"), options: z.object(expectedModelOptions).strict() }).strict(), z.object({ ...modelBase, method: z.literal("capeCod"), options: z.object({ development: assumptionSnapshotSchema.nullable(), exposure: alignedExposureInputSchema, adjustments: modelExposureAdjustmentsSchema, decay: z.number().finite().min(0).max(1), distance: z.literal("declared-origin-row-steps") }).strict() }).strict(), z.object({ ...modelBase, method: z.literal("mack"), options: z.object({ projection: z.enum(["volume-weighted", "selected"]), development: assumptionSnapshotSchema.nullable(), tail: assumptionSnapshotSchema.nullable(), tailUncertainty: modelMackTailUncertaintySchema, variancePolicy: modelVariancePolicySchema }).strict() }).strict(), z.object({ ...modelBase, method: z.literal("merzWuthrich"), options: z.object({ tail: z.literal("none"), variancePolicy: modelVariancePolicySchema }).strict() }).strict(), z.object({ ...modelBase, method: z.literal("clarkLdf"), options: modelClarkOptionsSchema }).strict(), z.object({ ...modelBase, method: z.literal("clarkCapeCod"), options: modelClarkOptionsSchema.extend({ exposure: alignedExposureInputSchema, exposureProportionalityRationale: token }).strict() }).strict(), z.object({ ...modelBase, method: z.literal("odpBootstrap"), options: z.object({ tail: z.literal("none"), nSims: z.number().int().safe().min(100), seed: z.number().int().min(0).max(0xffffffff), biasAdjust: z.boolean(), processVariance: z.boolean(), negativeIncrements: z.enum(["reject", "allow-qualified"]), simulationFallback: z.enum(["retain-qualified", "uncertainty-unavailable"]), executionLimits: z.object({ maximumSimulations: z.number().int().positive().max(REVIEW_ODP_LIMITS.maximumSimulations), maximumSimulationCells: z.number().int().positive().max(REVIEW_ODP_LIMITS.maximumSimulationCells) }).strict(), }).strict() }).strict(), ]; /** Explicit boundary type keeps the generated declaration compact as model variants grow. */ export const modelRequestSchema: z.ZodType & { readonly options: readonly z.AnyZodObject[] } = z.discriminatedUnion("method", modelRequestVariants) as unknown as z.ZodType & { readonly options: readonly z.AnyZodObject[] }; export function parseModelDevelopmentSource(input: unknown): ModelDevelopmentSource { return validateModelDevelopmentSource(parseReviewBoundary(modelDevelopmentSourceSchema, input)); } export function parseModelRequest(input: unknown): ModelRequest { return validateModelRequest(parseReviewBoundary(modelRequestSchema, input)); } export const modelComparisonCandidateSchema = z.object({ id: token, result: analysisSnapshotSchema, seriesKey: z.enum(["ultimate", "unpaid", "additional-incurred-development", "loss-through-development-age", "future-paid-through-development-age", "future-incurred-development-through-development-age", "ultimate-recoveries", "future-recoveries"]), }).strict(); export const modelComparisonRequestSchema = z.object({ id: token, version: token, purpose: z.enum(["method-comparison", "declared-contrast"]), rationale: token, quantity: quantitySemanticsSchema, target: z.discriminatedUnion("kind", [z.object({ kind: z.literal("ultimate") }).strict(), z.object({ kind: z.literal("development-age"), months: z.number().int().safe().positive() }).strict()]), origins: z.array(originPeriodSchema).min(1), join: z.enum(["exact-universe", "matched-subset"]), scales: z.enum(["require-identical", "convert-to-target"]), candidates: z.array(modelComparisonCandidateSchema).min(2), declarations: z.array(z.object({ left: token, right: token, population: z.object({ relationship: z.enum(["same", "disjoint", "overlapping"]), rationale: token }).strict().nullable(), contrasts: z.array(z.object({ dimension: z.enum(["population", "financial-basis", "valuation", "knowledge", "origins", "adjustments"]), rationale: token, bridge: analysisSnapshotSchema }).strict()), }).strict()), limits: z.object({ maximumCandidates: z.number().int().positive().max(REVIEW_COMPARISON_LIMITS.maximumCandidates), maximumOrigins: z.number().int().positive().max(REVIEW_COMPARISON_LIMITS.maximumOrigins), maximumSnapshotCharacters: z.number().int().positive().max(REVIEW_COMPARISON_LIMITS.maximumSnapshotCharacters) }).strict(), }).strict(); /** Checks snapshot integrity and projection semantics; this is not model replay. */ export function parseModelComparisonRequest(input: unknown): ModelComparisonRequest { return validateModelComparisonRequest(parseReviewBoundary(modelComparisonRequestSchema, input)); } export const estimateSelectionTargetSchema = z.discriminatedUnion("kind", [ z.object({ kind: z.literal("ultimate") }).strict(), z.object({ kind: z.literal("development-age"), months: z.number().int().safe().positive() }).strict(), z.object({ kind: z.literal("quantity-defined") }).strict(), ]); // Prefix keys during Zod 3 construction, then restore safe own properties. // Otherwise __proto__ would silently disappear and broaden applicability. const selectionDimensions = z.record(token.transform((key) => `:${key}`), token) .transform((record) => Object.fromEntries(Object.entries(record).map(([key, value]) => [key.slice(1), value]))); const selectionDimensionPredicates = z.record(token.transform((key) => `:${key}`), z.array(token).min(1)) .transform((record) => Object.fromEntries(Object.entries(record).map(([key, value]) => [key.slice(1), value]))); const selectionMaturity = z.number().int().safe().nonnegative().nullable(); export const declaredEstimateInputSchema = z.object({ id: token, version: token, quantity: quantitySemanticsSchema, target: estimateSelectionTargetSchema, context: analysisContextSchema, dimensions: selectionDimensions, knownDate: date, rationale: token, evidence: z.array(analysisReferenceSchema).min(1), snapshots: z.array(analysisSnapshotSchema), rows: z.array(z.discriminatedUnion("status", [ z.object({ origin: token, maturityMonths: selectionMaturity, status: z.literal("available"), value: z.number().finite(), reason: z.null() }).strict(), z.object({ origin: token, maturityMonths: selectionMaturity, status: z.literal("unavailable"), value: z.null(), reason: token }).strict(), ])), }).strict(); export const estimateSelectionCandidateSchema = z.discriminatedUnion("kind", [ z.object({ kind: z.literal("model"), id: token, model: modelComparisonCandidateSchema, dimensions: selectionDimensions }).strict(), z.object({ kind: z.literal("calculated"), id: token, calculation: z.discriminatedUnion("kind", [z.object({ kind: z.literal("ilf-application"), result: z.custom>(isDiagnosticPlainRecord) }).strict(), z.object({ kind: z.literal("ulae-reserve"), result: z.custom>(isDiagnosticPlainRecord) }).strict(), z.object({ kind: z.literal("discounted-unpaid"), result: z.custom>(isDiagnosticPlainRecord) }).strict(), z.object({ kind: z.literal("net-of-recoveries"), result: z.custom>(isDiagnosticPlainRecord), series: z.enum(["net-ultimate", "net-unpaid"]) }).strict()]), dimensions: selectionDimensions }).strict(), z.object({ kind: z.literal("declared"), id: token, snapshot: analysisSnapshotSchema }).strict(), ]); export const estimateSelectionCoordinateSchema = z.object({ id: token, origin: token, maturityMonths: selectionMaturity, dimensions: selectionDimensions, context: analysisContextSchema }).strict(); const selectionRuleBase = { id: token, usage: z.enum(["primary", "fallback"]), priority: z.number().int().safe(), knownDate: date, rationale: token, applicability: z.object({ coordinates: z.array(token).min(1).nullable(), origins: z.array(token).min(1).nullable(), dimensions: selectionDimensionPredicates, maturity: z.object({ minimum: selectionMaturity, maximum: selectionMaturity }).strict().nullable() }).strict(), }; export const estimateSelectionRuleSchema = z.discriminatedUnion("kind", [ z.object({ ...selectionRuleBase, kind: z.literal("weighted"), weights: z.array(z.object({ candidate: token, weight: z.number().finite().min(0).max(1) }).strict()), weightPolicy: z.discriminatedUnion("kind", [z.object({ kind: z.literal("require-unit-sum"), tolerance: z.number().finite().min(0).max(1e-8) }).strict(), z.object({ kind: z.literal("normalize"), rationale: token }).strict()]) }).strict(), z.object({ ...selectionRuleBase, kind: z.literal("manual"), value: z.number().finite(), actorId: token, evidence: z.array(analysisReferenceSchema).min(1) }).strict(), ]); export const estimateSelectionRequestSchema = z.object({ id: token, version: token, rationale: token, quantity: quantitySemanticsSchema, target: estimateSelectionTargetSchema, coordinates: z.array(estimateSelectionCoordinateSchema).min(1), candidates: z.array(estimateSelectionCandidateSchema).min(1), snapshots: z.array(analysisSnapshotSchema), rules: z.array(estimateSelectionRuleSchema).min(1), fallbacks: z.array(z.object({ id: token, fromRule: token, toRule: token, trigger: z.literal("positive-weight-result-unavailable"), missingCandidates: z.array(token).min(1).nullable(), rationale: token }).strict()), scales: z.enum(["require-identical", "convert-to-target"]), compatibility: z.array(z.object({ candidate: token, coordinate: token, population: z.object({ relationship: z.literal("same"), rationale: token }).strict().nullable(), unknownBasis: z.object({ relationship: z.literal("same"), rationale: token }).strict().nullable() }).strict()), aggregation: z.discriminatedUnion("kind", [z.object({ kind: z.literal("none") }).strict(), z.object({ kind: z.literal("sum"), disjointPopulations: z.array(z.object({ left: token, right: token, rationale: token }).strict()) }).strict()]), limits: z.object({ maximumCandidates: z.number().int().positive().max(REVIEW_SELECTION_LIMITS.maximumCandidates), maximumCoordinates: z.number().int().positive().max(REVIEW_SELECTION_LIMITS.maximumCoordinates), maximumRules: z.number().int().positive().max(REVIEW_SELECTION_LIMITS.maximumRules), maximumCompatibilityChecks: z.number().int().positive().max(REVIEW_SELECTION_LIMITS.maximumCompatibilityChecks), maximumSnapshotCharacters: z.number().int().positive().max(REVIEW_SELECTION_LIMITS.maximumSnapshotCharacters) }).strict(), }).strict(); export function parseEstimateSelectionRequest(input: unknown): EstimateSelectionRequest { return validateEstimateSelectionRequest(parseReviewBoundary(estimateSelectionRequestSchema, input) as unknown as EstimateSelectionRequest); } const reserveBalanceKind = z.enum(["paid", "case", "reportedIncurred", "bookedUnpaid"]); const reconciliationEvidence = { knownDate: date, rationale: token, evidence: z.array(analysisReferenceSchema).min(1) }; export const reserveBalanceInputSchema = z.object({ id: token, version: token, kind: reserveBalanceKind, quantity: quantitySemanticsSchema, context: analysisContextSchema, dimensions: selectionDimensions, maturityMonths: selectionMaturity, value: z.number().finite().nullable(), reason: token.nullable(), ...reconciliationEvidence, snapshots: z.array(analysisSnapshotSchema), projection: z.discriminatedUnion("kind", [ z.object({ kind: z.literal("development-cell"), source: modelDevelopmentSourceSchema, futureObservations: z.enum(["reject", "exclude"]) }).strict(), z.object({ kind: z.literal("net-recoveries-result"), result: z.custom>(isDiagnosticPlainRecord), origin: token }).strict(), ]).optional(), }).strict(); export const developmentReserveBalanceInputSchema = z.object({ id: token, version: token, quantity: quantitySemanticsSchema, context: analysisContextSchema, dimensions: selectionDimensions, rationale: token, source: modelDevelopmentSourceSchema, snapshots: z.array(analysisSnapshotSchema), futureObservations: z.enum(["reject", "exclude"]) }).strict(); export const reserveControlInputSchema = z.object({ id: token, version: token, kind: reserveBalanceKind, quantity: quantitySemanticsSchema, coverage: z.array(z.object({ coordinate: token, context: analysisContextSchema, dimensions: selectionDimensions }).strict()).min(1).max(REVIEW_RECONCILIATION_LIMITS.maximumCoordinates), value: z.number().finite().nullable(), reason: token.nullable(), ...reconciliationEvidence, snapshots: z.array(analysisSnapshotSchema), }).strict(); export const reserveReconciliationRequestSchema = z.object({ id: token, version: token, rationale: token, selection: analysisSnapshotSchema, accountingDate: date, knowledgeDate: date, snapshots: z.array(analysisSnapshotSchema), entries: z.array(z.object({ coordinate: token, paid: analysisReferenceSchema.nullable(), case: analysisReferenceSchema.nullable(), reportedIncurred: analysisReferenceSchema.nullable(), bookedUnpaid: analysisReferenceSchema.nullable(), dateBridge: z.object({ kind: z.literal("hold-selected-ultimate"), fromValuationDate: date, fromAccountingDate: date.nullable(), toAccountingDate: date, ...reconciliationEvidence }).strict().nullable(), componentIdentity: z.object({ kind: z.literal("paid-plus-case-equals-reported"), includedComponents: z.array(token).min(1), absoluteTolerance: z.number().finite().nonnegative(), ...reconciliationEvidence }).strict().nullable(), populations: z.array(z.object({ balance: reserveBalanceKind, relationship: z.literal("same"), rationale: token }).strict()), }).strict()).min(1), controls: z.array(z.object({ id: token, source: analysisSnapshotSchema, absoluteTolerance: z.number().finite().nonnegative(), populations: z.array(z.object({ coordinate: token, relationship: z.literal("same"), rationale: token }).strict()), disjointPopulations: z.array(z.object({ left: token, right: token, rationale: token }).strict()), }).strict()).max(REVIEW_RESERVE_CONTROL_LIMIT).optional(), scales: z.enum(["require-identical", "convert-to-selection"]), aggregation: z.enum(["none", "selection-universe"]), limits: z.object({ maximumCoordinates: z.number().int().positive().max(REVIEW_RECONCILIATION_LIMITS.maximumCoordinates), maximumCompatibilityChecks: z.number().int().positive().max(REVIEW_RECONCILIATION_LIMITS.maximumCompatibilityChecks), maximumSnapshotCharacters: z.number().int().positive().max(REVIEW_RECONCILIATION_LIMITS.maximumSnapshotCharacters) }).strict(), }).strict(); export function parseReserveBalanceInput(input: unknown): ReserveBalanceInput { return validateReserveBalanceInput(parseReviewBoundary(reserveBalanceInputSchema, input) as unknown as ReserveBalanceInput); } export function parseReserveControlInput(input: unknown): ReserveControlInput { return validateReserveControlInput(parseReviewBoundary(reserveControlInputSchema, input)); } export function createDevelopmentReserveBalanceFromInput(input: unknown): AnalysisSnapshot { return createDevelopmentReserveBalance(parseReviewBoundary(developmentReserveBalanceInputSchema, input)); } export function parseReserveReconciliationRequest(input: unknown): ReserveReconciliationRequest { return validateReserveReconciliationRequest(parseReviewBoundary(reserveReconciliationRequestSchema, input)); }