@actuarial-ts/compliance 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/bundle.js ADDED
@@ -0,0 +1,214 @@
1
+ /**
2
+ * Reproducibility bundle: canonical JSON serialization + a tiny integrity
3
+ * hash, so an analysis can state "these results came from exactly these
4
+ * inputs, parameters, and SDK versions" and a re-run can be byte-verified.
5
+ *
6
+ * Ground truth:
7
+ * - `canonicalJson` is the package's single equality oracle: object keys are
8
+ * sorted recursively, arrays keep their order, numbers render via
9
+ * `String(n)` except -0 which normalizes to "0" (so 0 and -0 never differ),
10
+ * and anything JSON cannot faithfully represent (undefined, functions,
11
+ * NaN/Infinity, bigint, symbol, non-plain objects such as Date/Map/Set,
12
+ * circular references) THROWS with the offending path instead of being
13
+ * silently dropped or coerced the way JSON.stringify would.
14
+ * - `fnv1a64` is an integrity aid, NOT a security control (see its doc block).
15
+ * - Timestamps are caller-supplied ISO strings; this module never reads a
16
+ * clock, so identical inputs yield byte-identical bundles.
17
+ * - Browser-safe: no node builtins (TextEncoder is a web-standard global).
18
+ *
19
+ * Error style for the whole package: `ComplianceError` with a registered
20
+ * machine code, mirroring core's `ReservingError` idiom. It lives here (not
21
+ * in its own module) because bundle.ts is the package's dependency-root
22
+ * module — ledger.ts already imports `canonicalJson` from it. Core's
23
+ * `RESERVING_ERROR_CODES` registry is a closed contract enforced against
24
+ * core's own source, and compliance failures (missing rationale, unsupported
25
+ * bundle values) are not reserving-input errors, so this package does not
26
+ * reuse `ReservingError`.
27
+ *
28
+ * These utilities are designed to support the actuary's compliance with
29
+ * ASOP No. 41 (documentation and reproducibility of the analysis);
30
+ * responsibility for compliance remains with the credentialed actuary.
31
+ */
32
+ /**
33
+ * Every machine-readable code a ComplianceError can carry, across all modules
34
+ * of this package. Add the code here when introducing a new throw.
35
+ */
36
+ export const COMPLIANCE_ERROR_CODES = [
37
+ "BAD_BUNDLE",
38
+ "BAD_CDF",
39
+ "MISSING_RATIONALE",
40
+ "UNSUPPORTED_VALUE",
41
+ ];
42
+ /** Thrown for invalid compliance input (unrepresentable bundle payloads, judgment without rationale, non-positive CDFs). */
43
+ export class ComplianceError extends Error {
44
+ code;
45
+ constructor(code, message) {
46
+ super(message);
47
+ this.name = "ComplianceError";
48
+ this.code = code;
49
+ }
50
+ }
51
+ function isPlainObject(value) {
52
+ if (value === null || typeof value !== "object" || Array.isArray(value))
53
+ return false;
54
+ const proto = Object.getPrototypeOf(value);
55
+ return proto === Object.prototype || proto === null;
56
+ }
57
+ function canonicalize(value, path, seen) {
58
+ if (value === null)
59
+ return "null";
60
+ switch (typeof value) {
61
+ case "string":
62
+ return JSON.stringify(value);
63
+ case "boolean":
64
+ return value ? "true" : "false";
65
+ case "number": {
66
+ if (!Number.isFinite(value)) {
67
+ throw new ComplianceError("UNSUPPORTED_VALUE", `non-finite number (${String(value)}) at ${path}`);
68
+ }
69
+ return Object.is(value, -0) ? "0" : String(value);
70
+ }
71
+ case "undefined":
72
+ throw new ComplianceError("UNSUPPORTED_VALUE", `undefined at ${path}`);
73
+ case "function":
74
+ throw new ComplianceError("UNSUPPORTED_VALUE", `function at ${path}`);
75
+ case "bigint":
76
+ case "symbol":
77
+ throw new ComplianceError("UNSUPPORTED_VALUE", `${typeof value} at ${path}`);
78
+ case "object":
79
+ break;
80
+ }
81
+ const obj = value;
82
+ if (seen.has(obj)) {
83
+ throw new ComplianceError("UNSUPPORTED_VALUE", `circular reference at ${path}`);
84
+ }
85
+ seen.add(obj);
86
+ let out;
87
+ if (Array.isArray(obj)) {
88
+ const parts = [];
89
+ for (let i = 0; i < obj.length; i++) {
90
+ parts.push(canonicalize(obj[i], `${path}[${i}]`, seen));
91
+ }
92
+ out = `[${parts.join(",")}]`;
93
+ }
94
+ else if (isPlainObject(obj)) {
95
+ const keys = Object.keys(obj).sort();
96
+ const parts = [];
97
+ for (const key of keys) {
98
+ parts.push(`${JSON.stringify(key)}:${canonicalize(obj[key], `${path}.${key}`, seen)}`);
99
+ }
100
+ out = `{${parts.join(",")}}`;
101
+ }
102
+ else {
103
+ const name = obj.constructor?.name ?? "unknown";
104
+ throw new ComplianceError("UNSUPPORTED_VALUE", `non-plain object (${name}) at ${path}; only plain objects, arrays, and JSON primitives are canonicalizable`);
105
+ }
106
+ seen.delete(obj);
107
+ return out;
108
+ }
109
+ /**
110
+ * Deterministic JSON serialization: sorted object keys (recursively), arrays
111
+ * in order, no whitespace, -0 normalized to "0". Two structurally equal
112
+ * values always produce the same string regardless of key insertion order.
113
+ * Throws ComplianceError("UNSUPPORTED_VALUE") — with the offending path, e.g.
114
+ * "$.rows[2].ultimate" — for any value JSON cannot faithfully represent.
115
+ */
116
+ export function canonicalJson(value) {
117
+ return canonicalize(value, "$", new Set());
118
+ }
119
+ const FNV_OFFSET_BASIS = 0xcbf29ce484222325n;
120
+ const FNV_PRIME = 0x100000001b3n;
121
+ const MASK_64 = 0xffffffffffffffffn;
122
+ /**
123
+ * FNV-1a 64-bit hash over the UTF-8 bytes of `text`, returned as a 16-hex-char
124
+ * string.
125
+ *
126
+ * This is an INTEGRITY AID for detecting accidental divergence between a
127
+ * bundle and a re-run. It is NOT a security control: FNV-1a is not collision
128
+ * resistant and offers no protection against deliberate tampering. Anyone
129
+ * needing tamper evidence must sign or cryptographically hash the payload
130
+ * (which is exactly why the full canonical payload — not just the hash — is
131
+ * stored on the bundle).
132
+ */
133
+ export function fnv1a64(text) {
134
+ const bytes = new TextEncoder().encode(text);
135
+ let hash = FNV_OFFSET_BASIS;
136
+ for (const byte of bytes) {
137
+ hash ^= BigInt(byte);
138
+ hash = (hash * FNV_PRIME) & MASK_64;
139
+ }
140
+ return hash.toString(16).padStart(16, "0");
141
+ }
142
+ /**
143
+ * Packages an analysis run into a reproducibility bundle. The payload is
144
+ * canonical, so two runs with structurally equal inputs produce byte-identical
145
+ * payloads (and therefore identical hashes) regardless of key insertion order.
146
+ * `seeds` is included only when provided (undefined would be unrepresentable).
147
+ */
148
+ export function createBundle(input) {
149
+ const body = {
150
+ createdAt: input.createdAt,
151
+ inputs: input.inputs,
152
+ parameters: input.parameters,
153
+ results: input.results,
154
+ sdkVersions: input.sdkVersions,
155
+ };
156
+ if (input.seeds !== undefined)
157
+ body["seeds"] = input.seeds;
158
+ const payload = canonicalJson(body);
159
+ return { payload, hash: fnv1a64(payload) };
160
+ }
161
+ /** First differing path between two canonicalizable structures, or null when equal. */
162
+ function firstDifference(stored, rerun, path) {
163
+ if (isPlainObject(stored) && isPlainObject(rerun)) {
164
+ const keys = [...new Set([...Object.keys(stored), ...Object.keys(rerun)])].sort();
165
+ for (const key of keys) {
166
+ const keyPath = `${path}.${key}`;
167
+ if (!(key in stored) || !(key in rerun))
168
+ return keyPath;
169
+ const diff = firstDifference(stored[key], rerun[key], keyPath);
170
+ if (diff !== null)
171
+ return diff;
172
+ }
173
+ return null;
174
+ }
175
+ if (Array.isArray(stored) && Array.isArray(rerun)) {
176
+ const shared = Math.min(stored.length, rerun.length);
177
+ for (let i = 0; i < shared; i++) {
178
+ const diff = firstDifference(stored[i], rerun[i], `${path}[${i}]`);
179
+ if (diff !== null)
180
+ return diff;
181
+ }
182
+ return stored.length === rerun.length ? null : `${path}[${shared}]`;
183
+ }
184
+ return canonicalJson(stored) === canonicalJson(rerun) ? null : path;
185
+ }
186
+ /**
187
+ * Verifies a re-run against a bundle: canonicalizes `rerunResults` and
188
+ * byte-compares it with the bundle's stored `results` segment. On mismatch,
189
+ * reports the FIRST differing path (see VerifyBundleResult.mismatchPath).
190
+ *
191
+ * Throws ComplianceError("BAD_BUNDLE") when the payload is not a valid bundle
192
+ * body, and propagates ComplianceError("UNSUPPORTED_VALUE") when
193
+ * `rerunResults` itself is not canonicalizable — a result that cannot be
194
+ * serialized cannot be verified.
195
+ */
196
+ export function verifyBundle(bundle, rerunResults) {
197
+ let stored;
198
+ try {
199
+ stored = JSON.parse(bundle.payload);
200
+ }
201
+ catch {
202
+ throw new ComplianceError("BAD_BUNDLE", "bundle payload is not valid JSON");
203
+ }
204
+ if (!isPlainObject(stored) || !("results" in stored)) {
205
+ throw new ComplianceError("BAD_BUNDLE", 'bundle payload has no "results" segment');
206
+ }
207
+ const storedResults = stored["results"];
208
+ const rerunCanonical = canonicalJson(rerunResults);
209
+ const storedCanonical = canonicalJson(storedResults);
210
+ if (rerunCanonical === storedCanonical)
211
+ return { reproduced: true };
212
+ return { reproduced: false, mismatchPath: firstDifference(storedResults, rerunResults, "$") ?? "$" };
213
+ }
214
+ //# sourceMappingURL=bundle.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bundle.js","sourceRoot":"","sources":["../src/bundle.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,76 @@
1
+ import type { EstimateMetadata } from "./metadata.js";
2
+ import type { AssumptionLedger, ChangedAssumptions } from "./ledger.js";
3
+ import { type MethodId } from "./modelCards.js";
4
+ /**
5
+ * ASOP No. 41 (Actuarial Communications) disclosure generator.
6
+ *
7
+ * ASOP 41 requires disclosure of the methods, procedures, assumptions, and
8
+ * data underlying the findings with enough clarity that another qualified
9
+ * actuary could objectively appraise the reasonableness of the work. The
10
+ * SDK already knows every method invoked, every parameter, and (through the
11
+ * assumption ledger) which values were machine defaults versus human or
12
+ * agent judgment — so it can render that appendix instead of the actuary
13
+ * reconstructing it by hand.
14
+ *
15
+ * DETERMINISM CONTRACT: identical DisclosureInput yields byte-identical
16
+ * markdown. No clock reads, no randomness — `generatedAt` is an input.
17
+ *
18
+ * The generated document is a DRAFT SUPPORT DOCUMENT for the responsible
19
+ * actuary to review, edit, and adopt; generating it is not compliance.
20
+ */
21
+ export interface MethodResultSummary {
22
+ ultimate?: number;
23
+ ibnr?: number;
24
+ unpaid?: number;
25
+ standardError?: number;
26
+ }
27
+ export interface MethodUse {
28
+ methodId: MethodId;
29
+ /** e.g. "paid", "incurred", "capped layer". */
30
+ basisLabel?: string;
31
+ /** The parameters the method actually ran with (JSON-serializable). */
32
+ parameters?: unknown;
33
+ resultSummary?: MethodResultSummary;
34
+ }
35
+ /**
36
+ * Structural stand-in for @actuarial-ts/data's DataReviewReport (no package
37
+ * dependency; anything with this shape renders).
38
+ */
39
+ export interface DataReviewLike {
40
+ checks: {
41
+ id: string;
42
+ description: string;
43
+ status: string;
44
+ details: string[];
45
+ }[];
46
+ summary: {
47
+ pass: number;
48
+ warning: number;
49
+ fail: number;
50
+ notEvaluated?: number;
51
+ };
52
+ }
53
+ export interface PriorComparison {
54
+ priorLabel?: string;
55
+ changes: ChangedAssumptions;
56
+ priorReserve?: number;
57
+ currentReserve?: number;
58
+ }
59
+ export interface DisclosureInput {
60
+ title?: string;
61
+ /** Identification of the responsible actuary (ASOP 41 section 3.1.4). */
62
+ preparedBy?: string;
63
+ metadata: EstimateMetadata;
64
+ methods: MethodUse[];
65
+ ledger?: AssumptionLedger;
66
+ dataReview?: DataReviewLike;
67
+ priorComparison?: PriorComparison;
68
+ reliances?: string[];
69
+ limitations?: string[];
70
+ sdkVersion: string;
71
+ /** Caller-supplied ISO timestamp (determinism: the generator never reads a clock). */
72
+ generatedAt: string;
73
+ }
74
+ /** Renders the ASOP 41-oriented methods-assumptions-and-data appendix. */
75
+ export declare function generateDisclosure(input: DisclosureInput): string;
76
+ //# sourceMappingURL=disclosure.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"disclosure.d.ts","sourceRoot":"","sources":["../src/disclosure.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AACtD,OAAO,KAAK,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AACxE,OAAO,EAAe,KAAK,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAG7D;;;;;;;;;;;;;;;;GAgBG;AAEH,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,SAAS;IACxB,QAAQ,EAAE,QAAQ,CAAC;IACnB,+CAA+C;IAC/C,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,uEAAuE;IACvE,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,aAAa,CAAC,EAAE,mBAAmB,CAAC;CACrC;AAED;;;GAGG;AACH,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,EAAE,CAAA;KAAE,EAAE,CAAC;IACjF,OAAO,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,YAAY,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;CACjF;AAED,MAAM,WAAW,eAAe;IAC9B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,kBAAkB,CAAC;IAC5B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,eAAe;IAC9B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,yEAAyE;IACzE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,gBAAgB,CAAC;IAC3B,OAAO,EAAE,SAAS,EAAE,CAAC;IACrB,MAAM,CAAC,EAAE,gBAAgB,CAAC;IAC1B,UAAU,CAAC,EAAE,cAAc,CAAC;IAC5B,eAAe,CAAC,EAAE,eAAe,CAAC;IAClC,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,UAAU,EAAE,MAAM,CAAC;IACnB,sFAAsF;IACtF,WAAW,EAAE,MAAM,CAAC;CACrB;AAkDD,0EAA0E;AAC1E,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,eAAe,GAAG,MAAM,CA+KjE"}
@@ -0,0 +1,204 @@
1
+ import { MODEL_CARDS } from "./modelCards.js";
2
+ import { canonicalJson, fnv1a64 } from "./bundle.js";
3
+ const fmtNumber = (v) => Number.isInteger(v) ? v.toLocaleString("en-US") : v.toLocaleString("en-US", { maximumFractionDigits: 2 });
4
+ function describeMeasure(m) {
5
+ switch (m.kind) {
6
+ case "central-estimate":
7
+ return "central estimate (actuarial central estimate / expected value)";
8
+ case "high-estimate":
9
+ return "high estimate";
10
+ case "low-estimate":
11
+ return "low estimate";
12
+ case "specified-percentile":
13
+ return `specified percentile (${m.percentile ?? "unstated"})`;
14
+ case "range":
15
+ return "range of reasonable estimates";
16
+ }
17
+ }
18
+ function describeBasis(b) {
19
+ const gross = {
20
+ gross: "gross of reinsurance",
21
+ "net-of-reinsurance": "net of reinsurance",
22
+ "net-of-salvage-subro": "net of salvage and subrogation",
23
+ "net-all": "net of reinsurance, salvage, and subrogation",
24
+ }[b.grossNet];
25
+ const lae = {
26
+ "excluding-lae": "excluding loss adjustment expenses",
27
+ "including-all-lae": "including all loss adjustment expenses",
28
+ "dcc-only": "including defense and cost containment (DCC) expenses only",
29
+ "aao-only": "including adjusting and other (A&O) expenses only",
30
+ }[b.laeTreatment];
31
+ return `${gross}, ${lae}`;
32
+ }
33
+ function mdTable(header, rows) {
34
+ const out = [];
35
+ out.push(`| ${header.join(" | ")} |`);
36
+ out.push(`|${header.map(() => "---").join("|")}|`);
37
+ for (const r of rows)
38
+ out.push(`| ${r.join(" | ")} |`);
39
+ return out;
40
+ }
41
+ function parameterLines(parameters) {
42
+ if (parameters === undefined || parameters === null)
43
+ return "(engine defaults)";
44
+ const json = canonicalJson(parameters);
45
+ return json.length > 400 ? `${json.slice(0, 400)}… (truncated; full value in the reproducibility bundle)` : json;
46
+ }
47
+ /** Renders the ASOP 41-oriented methods-assumptions-and-data appendix. */
48
+ export function generateDisclosure(input) {
49
+ const L = [];
50
+ const m = input.metadata;
51
+ L.push(`# ${input.title ?? "Actuarial analysis: methods, assumptions, and data disclosure"}`);
52
+ L.push("");
53
+ L.push("> Draft support document generated by the actuarial-ts SDK for the responsible actuary's review and adoption. It is designed to support disclosures under ASOP Nos. 41, 43, 23, and 56; responsibility for the actuarial communication and for compliance with the ASOPs remains with the credentialed actuary.");
54
+ L.push("");
55
+ // 1. Identification, purpose, users, measure.
56
+ L.push("## 1. Identification and intended purpose");
57
+ L.push("");
58
+ if (input.preparedBy)
59
+ L.push(`- **Responsible actuary / preparer:** ${input.preparedBy}`);
60
+ L.push(`- **Intended purpose:** ${m.intendedPurpose}`);
61
+ if (m.intendedUsers && m.intendedUsers.length > 0) {
62
+ L.push(`- **Intended users:** ${m.intendedUsers.join("; ")}`);
63
+ }
64
+ L.push(`- **Intended measure:** ${describeMeasure(m.intendedMeasure)}`);
65
+ L.push(`- **Generated:** ${input.generatedAt} — actuarial-ts SDK v${input.sdkVersion}`);
66
+ L.push("");
67
+ // 2. Scope, dates, basis.
68
+ L.push("## 2. Scope, dates, and basis");
69
+ L.push("");
70
+ L.push(`- **Accounting date:** ${m.accountingDate}`);
71
+ L.push(`- **Valuation date:** ${m.valuationDate}`);
72
+ if (m.reviewDate)
73
+ L.push(`- **Review date:** ${m.reviewDate}`);
74
+ L.push(`- **Basis:** ${describeBasis(m.basis)}`);
75
+ if (m.currency)
76
+ L.push(`- **Currency:** ${m.currency}`);
77
+ if (m.scopeNotes)
78
+ L.push(`- **Scope notes:** ${m.scopeNotes}`);
79
+ L.push("");
80
+ // 3. Data review (ASOP 23).
81
+ L.push("## 3. Data and data review (ASOP No. 23)");
82
+ L.push("");
83
+ if (input.dataReview) {
84
+ const r = input.dataReview;
85
+ const notEvaluated = r.summary.notEvaluated ?? 0;
86
+ L.push(`A data review was performed: ${r.checks.length} checks (${r.summary.pass} pass, ${r.summary.warning} warning, ${r.summary.fail} fail${notEvaluated > 0 ? `, ${notEvaluated} not evaluated` : ""}). The checks performed, and their findings, were:`);
87
+ L.push("");
88
+ L.push(...mdTable(["Check", "Description", "Status", "Findings"], r.checks.map((c) => [
89
+ c.id,
90
+ c.description,
91
+ c.status.toUpperCase(),
92
+ c.details.length === 0 ? "none" : c.details.join("<br>"),
93
+ ])));
94
+ }
95
+ else {
96
+ L.push("No automated data review report was attached. ASOP No. 23 requires the actuary to review data for reasonableness, consistency, and completeness, or to disclose that no such review was performed and the limitations that imposes.");
97
+ }
98
+ L.push("");
99
+ // 4. Methods and models (ASOP 56).
100
+ L.push("## 4. Methods and models (ASOP No. 56)");
101
+ L.push("");
102
+ for (const use of input.methods) {
103
+ const card = MODEL_CARDS[use.methodId];
104
+ L.push(`### ${card.title}${use.basisLabel ? ` — ${use.basisLabel}` : ""}`);
105
+ L.push("");
106
+ L.push(`- **Intended use:** ${card.intendedUse}`);
107
+ L.push(`- **Specification:** ${card.specification}`);
108
+ L.push(`- **Key assumptions:** ${card.keyAssumptions.join(" ")}`);
109
+ L.push(`- **Known weaknesses:** ${card.weaknesses.join(" ")}`);
110
+ L.push(`- **Primary sensitivities:** ${card.sensitivities.join(" ")}`);
111
+ L.push(`- **Literature:** ${card.literature.join("; ")}`);
112
+ L.push(`- **Parameters this analysis ran with:** \`${parameterLines(use.parameters)}\``);
113
+ if (use.resultSummary) {
114
+ const s = use.resultSummary;
115
+ const bits = [];
116
+ if (s.ultimate !== undefined)
117
+ bits.push(`ultimate ${fmtNumber(s.ultimate)}`);
118
+ if (s.ibnr !== undefined)
119
+ bits.push(`IBNR ${fmtNumber(s.ibnr)}`);
120
+ if (s.unpaid !== undefined)
121
+ bits.push(`unpaid ${fmtNumber(s.unpaid)}`);
122
+ if (s.standardError !== undefined)
123
+ bits.push(`standard error ${fmtNumber(s.standardError)}`);
124
+ if (bits.length > 0)
125
+ L.push(`- **Indicated:** ${bits.join(", ")}`);
126
+ }
127
+ L.push("");
128
+ }
129
+ // 5. Assumptions and judgments.
130
+ L.push("## 5. Assumptions and judgments");
131
+ L.push("");
132
+ if (input.ledger && input.ledger.entries.length > 0) {
133
+ L.push("Every recorded assumption, distinguishing machine defaults from human or agent judgment (judgment entries carry their rationale and source):");
134
+ L.push("");
135
+ L.push(...mdTable(["#", "When", "Actor", "Assumption", "Value", "Rationale / source"], input.ledger.entries.map((e) => [
136
+ String(e.seq),
137
+ e.timestamp,
138
+ e.actor,
139
+ e.field,
140
+ `\`${canonicalJson(e.value)}\``,
141
+ [e.rationale, e.source ? `(source: ${e.source})` : ""].filter(Boolean).join(" ") || "—",
142
+ ])));
143
+ }
144
+ else {
145
+ L.push("No assumption ledger was attached; assumptions are visible only through Section 4's parameters.");
146
+ }
147
+ L.push("");
148
+ // 6. Changes from the prior analysis.
149
+ L.push("## 6. Changes from the prior analysis");
150
+ L.push("");
151
+ if (input.priorComparison) {
152
+ const pc = input.priorComparison;
153
+ if (pc.priorLabel)
154
+ L.push(`Prior analysis: ${pc.priorLabel}.`);
155
+ if (pc.priorReserve !== undefined && pc.currentReserve !== undefined) {
156
+ L.push(`Carried indication moved from ${fmtNumber(pc.priorReserve)} to ${fmtNumber(pc.currentReserve)} (${fmtNumber(pc.currentReserve - pc.priorReserve)}).`);
157
+ }
158
+ const { added, removed, changed } = pc.changes;
159
+ if (added.length === 0 && removed.length === 0 && changed.length === 0) {
160
+ L.push("No assumption or method changes were recorded against the prior analysis.");
161
+ }
162
+ else {
163
+ if (changed.length > 0) {
164
+ L.push("");
165
+ L.push(...mdTable(["Assumption", "Prior", "Current"], changed.map((c) => [c.field, `\`${canonicalJson(c.priorValue)}\``, `\`${canonicalJson(c.currentValue)}\``])));
166
+ }
167
+ if (added.length > 0)
168
+ L.push(`- **New assumptions:** ${added.join(", ")}`);
169
+ if (removed.length > 0)
170
+ L.push(`- **Removed assumptions:** ${removed.join(", ")}`);
171
+ }
172
+ }
173
+ else {
174
+ L.push("No prior-analysis comparison was attached.");
175
+ }
176
+ L.push("");
177
+ // 7. Reliances and limitations.
178
+ L.push("## 7. Reliances and limitations");
179
+ L.push("");
180
+ if (input.reliances && input.reliances.length > 0) {
181
+ for (const r of input.reliances)
182
+ L.push(`- **Reliance:** ${r}`);
183
+ }
184
+ else {
185
+ L.push("- No reliances on data or analyses supplied by others were recorded.");
186
+ }
187
+ if (input.limitations && input.limitations.length > 0) {
188
+ for (const lim of input.limitations)
189
+ L.push(`- **Limitation:** ${lim}`);
190
+ }
191
+ L.push("");
192
+ // 8. Reproducibility.
193
+ L.push("## 8. Reproducibility");
194
+ L.push("");
195
+ const inputHash = fnv1a64(canonicalJson({
196
+ metadata: input.metadata,
197
+ methods: input.methods,
198
+ ledger: input.ledger ?? null,
199
+ }));
200
+ L.push(`This disclosure derives deterministically from its inputs (integrity tag \`${inputHash}\`, actuarial-ts v${input.sdkVersion}). Rerunning the same inputs on the same SDK version reproduces the analysis and this document byte for byte; pair with a reproducibility bundle for auditor/examiner requests under ASOP No. 21.`);
201
+ L.push("");
202
+ return L.join("\n");
203
+ }
204
+ //# sourceMappingURL=disclosure.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"disclosure.js","sourceRoot":"","sources":["../src/disclosure.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ export * from "./metadata.js";
2
+ export * from "./ledger.js";
3
+ export * from "./modelCards.js";
4
+ export * from "./disclosure.js";
5
+ export * from "./bundle.js";
6
+ export * from "./ave.js";
7
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,eAAe,CAAC;AAC9B,cAAc,aAAa,CAAC;AAC5B,cAAc,iBAAiB,CAAC;AAChC,cAAc,iBAAiB,CAAC;AAChC,cAAc,aAAa,CAAC;AAC5B,cAAc,UAAU,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,7 @@
1
+ export * from "./metadata.js";
2
+ export * from "./ledger.js";
3
+ export * from "./modelCards.js";
4
+ export * from "./disclosure.js";
5
+ export * from "./bundle.js";
6
+ export * from "./ave.js";
7
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,eAAe,CAAC;AAC9B,cAAc,aAAa,CAAC;AAC5B,cAAc,iBAAiB,CAAC;AAChC,cAAc,iBAAiB,CAAC;AAChC,cAAc,aAAa,CAAC;AAC5B,cAAc,UAAU,CAAC"}
@@ -0,0 +1,84 @@
1
+ /**
2
+ * Assumption ledger: an append-only record of every assumption an analysis
3
+ * used, separating machine defaults from actuarial/agent judgment, feeding
4
+ * the ASOP 41 assumptions-and-judgments and changes-from-prior disclosures.
5
+ *
6
+ * Ground truth:
7
+ * - The ledger is IMMUTABLE: `recordAssumption` returns a NEW frozen ledger;
8
+ * the input ledger, its entries array, and every recorded entry are frozen.
9
+ * Nothing is ever edited or deleted — a corrected assumption is a new entry
10
+ * for the same field, and the LATEST entry per field is the live value.
11
+ * - `seq` is assigned by the ledger (entries.length + 1), never by the caller.
12
+ * - `rationale` is REQUIRED (throws MISSING_RATIONALE) whenever
13
+ * actor !== "default": undocumented judgment is exactly what the ledger
14
+ * exists to prevent. Machine defaults need no rationale — the method's
15
+ * model card documents them.
16
+ * - `changedAssumptions` diffs the LATEST entry per field on each side and
17
+ * compares values by canonical JSON, so key insertion order never causes a
18
+ * false "changed". Output field lists are lexicographically sorted for
19
+ * deterministic disclosure rendering.
20
+ * - Timestamps are caller-supplied ISO strings (purity: no clock reads).
21
+ *
22
+ * These utilities are designed to support the actuary's compliance with
23
+ * ASOP No. 41; responsibility for compliance remains with the credentialed
24
+ * actuary.
25
+ */
26
+ /** Who set the assumption: the machine default, a credentialed actuary, or an AI agent. */
27
+ export type AssumptionActor = "default" | "actuary" | "agent";
28
+ /** JSON-representable assumption value (the ledger stores data, not behavior). */
29
+ export type JsonValue = string | number | boolean | null | JsonValue[] | {
30
+ [key: string]: JsonValue;
31
+ };
32
+ export interface AssumptionEntry {
33
+ /** 1-based position in the ledger; assigned by recordAssumption, never by the caller. */
34
+ seq: number;
35
+ /** Caller-supplied ISO timestamp (purity: no clock reads). */
36
+ timestamp: string;
37
+ actor: AssumptionActor;
38
+ /** Dotted path identifying the assumption, e.g. "chainLadder.tailFactor". */
39
+ field: string;
40
+ value: JsonValue;
41
+ /** The value this entry superseded, when the caller knows it. */
42
+ previousValue?: JsonValue;
43
+ /** Where the value came from (e.g. "Friedland Table 15", "rate filing 2024-07"). */
44
+ source?: string;
45
+ /** Why the value was chosen. REQUIRED when actor !== "default". */
46
+ rationale?: string;
47
+ }
48
+ /** A not-yet-recorded entry: everything but the ledger-assigned seq. */
49
+ export type NewAssumptionEntry = Omit<AssumptionEntry, "seq">;
50
+ export interface AssumptionLedger {
51
+ readonly entries: readonly AssumptionEntry[];
52
+ }
53
+ /** An empty, frozen ledger. */
54
+ export declare function createLedger(): AssumptionLedger;
55
+ /**
56
+ * Appends an entry, returning a NEW frozen ledger; the input ledger is
57
+ * untouched. Assigns seq = entries.length + 1. Throws
58
+ * ComplianceError("MISSING_RATIONALE") when actor !== "default" and rationale
59
+ * is missing or blank.
60
+ */
61
+ export declare function recordAssumption(ledger: AssumptionLedger, entry: NewAssumptionEntry): AssumptionLedger;
62
+ /** Entries that represent judgment (actor !== "default"), in ledger order. */
63
+ export declare function judgmentEntries(ledger: AssumptionLedger): AssumptionEntry[];
64
+ export interface AssumptionValueChange {
65
+ field: string;
66
+ priorValue: JsonValue;
67
+ currentValue: JsonValue;
68
+ }
69
+ /** The ASOP 41 change-disclosure diff between two analyses' ledgers. */
70
+ export interface ChangedAssumptions {
71
+ /** Fields set in current but never set in prior. */
72
+ added: string[];
73
+ /** Fields set in prior but never set in current. */
74
+ removed: string[];
75
+ /** Fields set on both sides whose latest values differ (canonical-JSON inequality). */
76
+ changed: AssumptionValueChange[];
77
+ }
78
+ /**
79
+ * Diffs the LATEST entry per field between a prior analysis's ledger and the
80
+ * current one. Values compare by canonical JSON (key order never matters).
81
+ * All output lists are sorted by field for deterministic rendering.
82
+ */
83
+ export declare function changedAssumptions(prior: AssumptionLedger, current: AssumptionLedger): ChangedAssumptions;
84
+ //# sourceMappingURL=ledger.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ledger.d.ts","sourceRoot":"","sources":["../src/ledger.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAIH,2FAA2F;AAC3F,MAAM,MAAM,eAAe,GAAG,SAAS,GAAG,SAAS,GAAG,OAAO,CAAC;AAE9D,kFAAkF;AAClF,MAAM,MAAM,SAAS,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,IAAI,GAAG,SAAS,EAAE,GAAG;IAAE,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,CAAA;CAAE,CAAC;AAEtG,MAAM,WAAW,eAAe;IAC9B,yFAAyF;IACzF,GAAG,EAAE,MAAM,CAAC;IACZ,8DAA8D;IAC9D,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,eAAe,CAAC;IACvB,6EAA6E;IAC7E,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,SAAS,CAAC;IACjB,iEAAiE;IACjE,aAAa,CAAC,EAAE,SAAS,CAAC;IAC1B,oFAAoF;IACpF,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,mEAAmE;IACnE,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,wEAAwE;AACxE,MAAM,MAAM,kBAAkB,GAAG,IAAI,CAAC,eAAe,EAAE,KAAK,CAAC,CAAC;AAE9D,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,OAAO,EAAE,SAAS,eAAe,EAAE,CAAC;CAC9C;AAED,+BAA+B;AAC/B,wBAAgB,YAAY,IAAI,gBAAgB,CAE/C;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,gBAAgB,EAAE,KAAK,EAAE,kBAAkB,GAAG,gBAAgB,CAStG;AAED,8EAA8E;AAC9E,wBAAgB,eAAe,CAAC,MAAM,EAAE,gBAAgB,GAAG,eAAe,EAAE,CAE3E;AAED,MAAM,WAAW,qBAAqB;IACpC,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,SAAS,CAAC;IACtB,YAAY,EAAE,SAAS,CAAC;CACzB;AAED,wEAAwE;AACxE,MAAM,WAAW,kBAAkB;IACjC,oDAAoD;IACpD,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,oDAAoD;IACpD,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,uFAAuF;IACvF,OAAO,EAAE,qBAAqB,EAAE,CAAC;CAClC;AASD;;;;GAIG;AACH,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,gBAAgB,EACvB,OAAO,EAAE,gBAAgB,GACxB,kBAAkB,CAuBpB"}