import { reviewPremiumAdjustmentRequestSchema, adjustedModelPriorCalibrationRequestSchema } from "./reviewPremiumAdjustmentContracts.js"; import { reviewSettlementAdjustmentRequestSchema } from "./reviewSettlementAdjustmentContracts.js"; import { declaredReviewOriginSeriesRequestSchema, modelReviewOriginSeriesRequestSchema } from "./reviewOriginSeriesContracts.js"; import { reviewTrendFitRequestSchema, reviewTrendSelectionRequestSchema, reviewTrendApplicationRequestSchema } from "./reviewTrendContracts.js"; import { reviewOnLevelRequestSchema } from "./reviewOnLevelContracts.js"; import { reviewLayerApplicationRequestSchema, reviewIlfCalibrationRequestSchema, reviewIlfApplicationRequestSchema } from "./reviewCappingIlfContracts.js"; import { reviewUlaeCalibrationRequestSchema, reviewUlaeReserveRequestSchema } from "./reviewUlaeContracts.js"; import { nominalCashflowSourceDefinitionSchema, reviewNominalCashflowRequestSchema } from "./reviewNominalCashflowContracts.js"; import { reviewDiscountingRequestSchema } from "./reviewDiscountingContracts.js"; import { reviewNetRecoveriesRequestSchema } from "./reviewNetRecoveriesContracts.js"; import { z } from "zod"; import { AnalysisReviewError, DiagnosticValidationError, assessEligibility, isDiagnosticPlainRecord, isDiagnosticToken, listCapabilities as listCoreCapabilities, snapshotDiagnosticJson, type AnalysisCapability, type AnalysisEligibility, type AnalysisReviewFinding } from "@actuarial-ts/core"; import { parseReviewBoundary } from "./reviewBoundary.js"; import { modelRequestSchema, modelComparisonRequestSchema, estimateSelectionRequestSchema, reserveReconciliationRequestSchema } from "./reviewContracts.js"; import { reviewPlanSchema } from "./reviewPlanContracts.js"; import { reviewSensitivityRequestSchema } from "./reviewSensitivityContracts.js"; import { reviewSensitivityGridRequestSchema } from "./reviewSensitivityGridContracts.js"; import { developmentSelectionRequestSchema, tailFittingRequestSchema } from "./developmentSelectionContracts.js"; import { historicalInformationSetRequestSchema } from "./historicalInformationContracts.js"; import { modelDiagnosticSchemaForRun } from "./modelDiagnosticsContracts.js"; import { reviewDevelopmentTransformRequestSchema, reviewDevelopmentViewRequestSchema, reviewDevelopmentAccumulationRequestSchema } from "./reviewDevelopmentContracts.js"; import { dataOperationCapabilities, dataOperationSchema, assessDataOperationEligibility } from "./dataCapabilities.js"; import { modelForecastRequestSchema, modelForecastScoringRequestSchema } from "./modelForecastContracts.js"; import { modelPriorCalibrationRequestSchema } from "./modelCalibrationContracts.js"; import { reviewDevelopmentAdjustmentRequestSchema } from "./reviewDevelopmentAdjustmentContracts.js"; /** Composes pure-core discovery with boundaries implemented by this package; IDs stay unique. */ export function listCapabilities(): readonly AnalysisCapability[] { const owned = dataOperationCapabilities(), byId = new Map(owned.map((capability) => [capability.id, capability])); const core = listCoreCapabilities(); return snapshotDiagnosticJson([...core.map((capability) => byId.get(capability.id) ?? capability), ...owned.filter((capability) => !core.some((item) => item.id === capability.id))]); } // These are the same boundary schema instances used by the public execution parsers. const operations: Readonly> = { "netOfRecoveries": reviewNetRecoveriesRequestSchema, "discountUnpaid": reviewDiscountingRequestSchema, "cashflows.nominal": reviewNominalCashflowRequestSchema, "cashflows.nominal.source": nominalCashflowSourceDefinitionSchema, "ulae": reviewUlaeCalibrationRequestSchema, "ulae.reserve": reviewUlaeReserveRequestSchema, "cappingIlf.layer": reviewLayerApplicationRequestSchema, "cappingIlf": reviewIlfCalibrationRequestSchema, "cappingIlf.application": reviewIlfApplicationRequestSchema, "onLevel": reviewOnLevelRequestSchema, "premium.composition": reviewPremiumAdjustmentRequestSchema, "review.adjusted-prior-calibration": adjustedModelPriorCalibrationRequestSchema, "trend": reviewTrendFitRequestSchema, "trend.selection": reviewTrendSelectionRequestSchema, "trend.application": reviewTrendApplicationRequestSchema, "series.declared": declaredReviewOriginSeriesRequestSchema, "series.model": modelReviewOriginSeriesRequestSchema, "tailFitting": tailFittingRequestSchema, "berquistCaseAdequacy": reviewDevelopmentAdjustmentRequestSchema, "berquistSettlement": reviewSettlementAdjustmentRequestSchema, "review.forecast": modelForecastRequestSchema, "review.forecast-score": modelForecastScoringRequestSchema, "review.prior-calibration": modelPriorCalibrationRequestSchema, "development.transform": reviewDevelopmentTransformRequestSchema, "development.view": reviewDevelopmentViewRequestSchema, "development.accumulation": reviewDevelopmentAccumulationRequestSchema, "review.plan": reviewPlanSchema, "review.sensitivity": reviewSensitivityRequestSchema, "review.sensitivity-grid": reviewSensitivityGridRequestSchema, "development.selection": developmentSelectionRequestSchema, "review.comparison": modelComparisonRequestSchema, "review.selection": estimateSelectionRequestSchema, "review.reconciliation": reserveReconciliationRequestSchema, "review.historical-information": historicalInformationSetRequestSchema, }; const diagnostic = modelDiagnosticSchemaForRun(z.object({ request: modelRequestSchema }).passthrough()); /** Actual Zod boundary for an executable capability; null means contextual support is not advertised. */ export function getCapabilityInputSchema(capabilityId: string): z.ZodType | null { if (!isDiagnosticToken(capabilityId)) return null; const owned = dataOperationSchema(capabilityId); if (owned) return owned; if (Object.hasOwn(operations, capabilityId)) return operations[capabilityId]!; const capability = listCapabilities().find((item) => item.id === capabilityId); if (!capability?.connected.available) return null; if (capability.connected.entryPoint === "runModelDiagnostics") return diagnostic; if (capability.connected.entryPoint === "runModel") return modelRequestSchema; return null; } const request = z.object({ capabilityId: z.string().refine(isDiagnosticToken), input: z.unknown().refine((value) => value !== undefined, "An input payload is required") }).strict(); /** The catalog envelope selects a native payload boundary; it must not impose * an unrelated ordinary-JSON budget on every operation's opaque input first. * Inspect own descriptors before reading any field. The selected native/data * parser still validates the complete payload under its own resource contract. */ function parseCapabilityRequest(input: unknown) { const invalid = () => { throw new AnalysisReviewError([{ code: "INVALID_REVIEW_INPUT", path: "$", message: "A capability request requires enumerable own data fields", remedies: [] }]); }; if (!isDiagnosticPlainRecord(input)) return invalid(); const metadata: Record = Object.create(null); let payload: unknown; for (const name of Reflect.ownKeys(input)) { const field = Object.getOwnPropertyDescriptor(input, name); if (typeof name !== "string" || !field?.enumerable || !("value" in field)) return invalid(); if (name === "input") payload = field.value; metadata[name] = name === "input" && field.value !== undefined ? null : field.value; } const value = parseReviewBoundary(request, metadata); return { ...value, input: payload }; } function pathInRequest(path: string, payload: boolean): string { const normalized = path.startsWith("$") ? path : path.startsWith("input") ? `$${path.slice(5)}` : path; if (!payload || normalized === "$.input" || normalized.startsWith("$.input.") || normalized.startsWith("$.input[") || normalized.startsWith("$.capabilityId")) return normalized; return `$.input${normalized.startsWith("$") ? normalized.slice(1) : `[${JSON.stringify(normalized)}]`}`; } function locate(finding: AnalysisReviewFinding, payload: boolean): AnalysisReviewFinding { return { ...finding, path: pathInRequest(finding.path, payload) }; } /** Shared JSON/Zod/core preflight; reserve-model fitting is deferred, retained calculations may replay. */ export function assessEligibilityFromInput(input: unknown): AnalysisEligibility { let id: string | null = null, payload = false; try { const value = parseCapabilityRequest(input); id = value.capabilityId; const schema = getCapabilityInputSchema(id); payload = true; const local = assessDataOperationEligibility(id, value.input); if (local) return snapshotDiagnosticJson({ ...local, findings: local.findings.map((finding) => locate(finding, true)) }); if (schema) parseReviewBoundary(schema, value.input); const assessed = assessEligibility({ capabilityId: value.capabilityId, input: value.input }); return snapshotDiagnosticJson({ ...assessed, findings: assessed.findings.map((finding) => locate(finding, true)) }); } catch (error) { if (!(error instanceof AnalysisReviewError) && !(error instanceof DiagnosticValidationError)) throw error; const findings: readonly AnalysisReviewFinding[] = error instanceof AnalysisReviewError ? error.findings : error.issues.map((issue) => ({ code: issue.code, path: issue.path, message: issue.message, remedies: [] })); const unsupported = new Set(["RESOURCE_LIMIT_EXCEEDED", "OBSERVED_DIAGNOSTIC_QUANTITY_UNSUPPORTED", "PREPARATION_MEASURE_UNSUPPORTED", "PREPARATION_FINANCIAL_BASIS_UNREPRESENTABLE", "EXPOSURE_STRUCTURE_UNSUPPORTED", "PREPARATION_REPLAY_VERSION_UNSUPPORTED", "unsupported-operation", "resource-limit"]); return snapshotDiagnosticJson({ method: id, status: findings.some((finding) => unsupported.has(finding.code)) ? "unsupported" : "requires-input", coverage: { eligibleOrigins: [], unavailableOrigins: [] }, assurance: "structural-eligibility-only", findings: findings.map((finding) => ({ ...locate(finding, payload), remedies: finding.remedies.length ? finding.remedies : ["Use the capability's actual input schema and provide the identified field or evidence explicitly"], })) }); } }