@orkestrel/rater 0.0.1 → 0.0.3
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/README.md +19 -27
- package/dist/src/core/index.cjs +238 -1292
- package/dist/src/core/index.cjs.map +1 -1
- package/dist/src/core/index.d.cts +469 -1092
- package/dist/src/core/index.d.ts +469 -1092
- package/dist/src/core/index.js +237 -1235
- package/dist/src/core/index.js.map +1 -1
- package/package.json +5 -5
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":["#definition","#engine","#evaluator","#total","#labels","#rateLines","#quantitative","#programs","#engine","#emitter","#total","#labels","#validate","#ensureAlive","#removeOne","#destroyed","#emitter","#manager","#engine","#evaluator","#ensureAlive","#rateOne","#rateBatch","#destroyed","#subject","#emitSubject","#groups","#aggregateDeterminations","#projections","#tallies","#gate","#reasonLogical"],"sources":["../../../src/core/constants.ts","../../../src/core/errors.ts","../../../src/core/validators.ts","../../../src/core/helpers.ts","../../../src/core/programs/Program.ts","../../../src/core/programs/ProgramManager.ts","../../../src/core/Rater.ts","../../../src/core/factories.ts"],"sourcesContent":["import type { Decision, Effect, Eligibility, Status } from './types.js'\n\n/** Default definition validation policy for `ProgramManager.add`. */\nexport const DEFAULT_RATER_VALIDATE = true\n\n/** Eligibility severity order, most severe first — the {@link combineEligibilities} scan order. */\nexport const ELIGIBILITY_PRECEDENCE: readonly Eligibility[] = Object.freeze([\n\t'ineligible',\n\t'referral',\n\t'eligible',\n])\n\n/** Status tally precedence order — least to most resolved. */\nexport const STATUS_PRECEDENCE: readonly Status[] = Object.freeze([\n\t'ineligible',\n\t'referral',\n\t'conditional',\n\t'unrated',\n\t'eligible',\n])\n\n/** The deterministic authority decision for each eligibility. */\nexport const ELIGIBILITY_DECISIONS: Readonly<Record<Eligibility, Decision>> = Object.freeze({\n\teligible: 'approved',\n\tineligible: 'denied',\n\treferral: 'submitted',\n})\n\n/** The direct eligibility impact of an applied determination, by its effect. */\nexport const EFFECT_ELIGIBILITIES: Readonly<Record<Effect, Eligibility | undefined>> =\n\tObject.freeze({\n\t\trestriction: 'ineligible',\n\t\treferral: 'referral',\n\t\tcondition: undefined,\n\t\tnotice: undefined,\n\t\tlimit: undefined,\n\t})\n\n/** The reserved working-subject key a batch's aggregate projection is written under. */\nexport const AGGREGATE_KEY = 'aggregate'\n\n/** The reserved working-subject key an authority's outcome projection is written under. */\nexport const OUTCOME_KEY = 'outcome'\n","import type { RaterErrorCode } from './types.js'\n\n/**\n * A coded programmer error thrown by the rating layer.\n *\n * @remarks\n * `DUPLICATE` — a program id collision on `ProgramManager.add`. `MISSING` — an\n * unknown authored line reference at compile time.\n * `DEFINITION` — a program definition failed `isProgramDefinition`. `MISMATCH` —\n * a rated subject is not a record, or uses a reserved working-subject key.\n * `DESTROYED` — use of a destroyed entity.\n */\nexport class RaterError extends Error {\n\treadonly code: RaterErrorCode\n\treadonly context?: Readonly<Record<string, unknown>>\n\n\tconstructor(code: RaterErrorCode, message: string, context?: Readonly<Record<string, unknown>>) {\n\t\tsuper(message)\n\t\tthis.name = 'RaterError'\n\t\tthis.code = code\n\t\tthis.context = context\n\t}\n}\n\n/** Narrow a caught value to a {@link RaterError}. */\nexport function isRaterError(value: unknown): value is RaterError {\n\treturn value instanceof RaterError\n}\n","import type { Guard } from '@orkestrel/contract'\nimport type {\n\tAggregateDefinition,\n\tDecision,\n\tEffect,\n\tEligibility,\n\tLineDefinition,\n\tNotice,\n\tPassDefinition,\n\tProgramDefinition,\n\tRuling,\n\tStage,\n\tStatus,\n} from './types.js'\nimport {\n\tarrayOf,\n\tisJSONValue,\n\tisRecord,\n\tisString,\n\tliteralOf,\n\trecordOf,\n\tunionOf,\n\twhereOf,\n} from '@orkestrel/contract'\nimport { isFieldPath, isLogicalDefinition, isQuantitativeDefinition } from '@orkestrel/reason'\n\n/** Determine whether a value is an {@link Eligibility} literal. */\nexport const isEligibility: Guard<Eligibility> = literalOf('eligible', 'ineligible', 'referral')\n\n/** Determine whether a value is a {@link Decision} literal. */\nexport const isDecision: Guard<Decision> = literalOf('approved', 'denied', 'submitted')\n\n/** Determine whether a value is a {@link Status} literal. */\nexport const isStatus: Guard<Status> = literalOf(\n\t'ineligible',\n\t'referral',\n\t'conditional',\n\t'unrated',\n\t'eligible',\n)\n\n/** Determine whether a value is an {@link Effect} literal. */\nexport const isEffect: Guard<Effect> = literalOf(\n\t'restriction',\n\t'referral',\n\t'condition',\n\t'notice',\n\t'limit',\n)\n\n/** Determine whether a value is a {@link Stage} literal. */\nexport const isStage: Guard<Stage> = literalOf('factor', 'group', 'total')\n\n/** Determine whether a value is an exact {@link Ruling} record. */\nexport function isRuling(value: unknown): value is Ruling {\n\treturn recordOf({ effect: isEffect, line: isString, message: isString }, ['line', 'message'])(\n\t\tvalue,\n\t)\n}\n\n/** Determine whether a value is an exact {@link Notice} record. */\nexport function isNotice(value: unknown): value is Notice {\n\treturn recordOf({ id: isString, message: isString, line: isString }, ['line'])(value)\n}\n\n/** Determine whether a value is an exact {@link PassDefinition} record. */\nexport function isPassDefinition(value: unknown): value is PassDefinition {\n\treturn recordOf(\n\t\t{ line: isString, definition: unionOf(isLogicalDefinition, isQuantitativeDefinition) },\n\t\t['line'],\n\t)(value)\n}\n\n/** Determine whether a value is an exact {@link LineDefinition} record. */\nexport function isLineDefinition(value: unknown): value is LineDefinition {\n\treturn recordOf(\n\t\t{\n\t\t\tid: isString,\n\t\t\tname: isString,\n\t\t\tdescription: isString,\n\t\t\trate: isQuantitativeDefinition,\n\t\t\tmetadata: isJSONValue,\n\t\t},\n\t\t['description', 'metadata'],\n\t)(value)\n}\n\n/** Determine whether a value is an exact {@link AggregateDefinition} record. */\nexport function isAggregateDefinition(value: unknown): value is AggregateDefinition {\n\treturn recordOf({ fields: arrayOf(isFieldPath), by: isFieldPath, gates: isLogicalDefinition }, [\n\t\t'by',\n\t\t'gates',\n\t])(value)\n}\n\n/** Determine whether a value is a rule-id-keyed {@link Ruling} record. */\nexport const isRulings: Guard<Readonly<Record<string, Ruling>>> = whereOf(\n\tisRecord,\n\t(record): record is Readonly<Record<string, Ruling>> => Object.values(record).every(isRuling),\n)\n\n/** Determine whether a value is an exact {@link ProgramDefinition} record. */\nexport function isProgramDefinition(value: unknown): value is ProgramDefinition {\n\treturn recordOf(\n\t\t{\n\t\t\tid: isString,\n\t\t\tname: isString,\n\t\t\tdescription: isString,\n\t\t\tpasses: arrayOf(isPassDefinition),\n\t\t\tlines: arrayOf(isLineDefinition),\n\t\t\trulings: isRulings,\n\t\t\tnotices: arrayOf(isNotice),\n\t\t\tauthority: isLogicalDefinition,\n\t\t\taggregate: isAggregateDefinition,\n\t\t\tmetadata: isJSONValue,\n\t\t},\n\t\t['description', 'passes', 'rulings', 'notices', 'authority', 'aggregate', 'metadata'],\n\t)(value)\n}\n","import type { FieldPath } from '@orkestrel/contract'\nimport type {\n\tCheck,\n\tCheckResult,\n\tEvaluatorInterface,\n\tExpression,\n\tFactor,\n\tFactorResult,\n\tGroupResult,\n\tLogicalDefinition,\n\tLogicalResult,\n\tQuantitativeDefinition,\n\tQuantitativeResult,\n\tRule,\n\tSubject,\n} from '@orkestrel/reason'\nimport type {\n\tAggregateGroup,\n\tDecision,\n\tDetermination,\n\tEligibility,\n\tLineDefinition,\n\tLineResult,\n\tNotice,\n\tPremise,\n\tProgramDefinition,\n\tProgramInterface,\n\tProgramResult,\n\tRuling,\n\tStage,\n\tStatus,\n\tStep,\n\tTally,\n\tWorksheet,\n\tWorksheetFactor,\n\tWorksheetGroup,\n} from './types.js'\nimport { isFiniteNumber, isRecord, resolveField } from '@orkestrel/contract'\nimport { extractAtoms, formatField, isBounds } from '@orkestrel/reason'\nimport { RaterError } from './errors.js'\nimport {\n\tAGGREGATE_KEY,\n\tEFFECT_ELIGIBILITIES,\n\tELIGIBILITY_DECISIONS,\n\tELIGIBILITY_PRECEDENCE,\n\tOUTCOME_KEY,\n\tSTATUS_PRECEDENCE,\n} from './constants.js'\n\n/**\n * Interpolate `{{dotted.path}}` tokens in a message template against a record.\n *\n * @remarks\n * Each token is split on `.` into a {@link FieldPath} array and resolved with\n * the contracts {@link resolveField} (a plain string field is ONE key, never\n * dot-split — the split here is the token-to-path bridge). A finite number\n * renders with `en-US` thousand grouping (`5010` → `5,010`); any other resolved\n * value String-coerces. An UNRESOLVED path (the resolved value is `undefined`)\n * renders as the empty string — the deterministic \"nothing to show\" rule.\n *\n * @param template - The message template carrying `{{dotted.path}}` tokens\n * @param record - The record tokens resolve against\n * @returns The template with every token replaced\n *\n * @example\n * ```ts\n * import { interpolateMessage } from '@orkestrel/rater'\n *\n * interpolateMessage('Limit is {{limit}}', { limit: 5010 }) // 'Limit is 5,010'\n * interpolateMessage('Missing {{gone}}', {}) // 'Missing '\n * ```\n */\nexport function interpolateMessage(\n\ttemplate: string,\n\trecord: Readonly<Record<string, unknown>>,\n): string {\n\treturn template.replace(/\\{\\{\\s*([^}]+?)\\s*\\}\\}/g, (_match, path: string) => {\n\t\tconst value = resolveField(record, path.split('.'))\n\t\tif (value === undefined) return ''\n\t\tif (isFiniteNumber(value)) return value.toLocaleString('en-US')\n\t\treturn String(value)\n\t})\n}\n\n/**\n * Describe a {@link Premise} comparison as a display-neutral verb phrase.\n *\n * @param comparison - The comparison to describe\n * @returns A display-neutral phrase\n *\n * @example\n * ```ts\n * import { describeComparison } from '@orkestrel/rater'\n *\n * describeComparison('above') // 'is more than'\n * ```\n */\nexport function describeComparison(comparison: NonNullable<Premise['comparison']>): string {\n\tswitch (comparison) {\n\t\tcase 'equals':\n\t\t\treturn 'is'\n\t\tcase 'not':\n\t\t\treturn 'is not'\n\t\tcase 'above':\n\t\t\treturn 'is more than'\n\t\tcase 'below':\n\t\t\treturn 'is less than'\n\t\tcase 'from':\n\t\t\treturn 'is at least'\n\t\tcase 'to':\n\t\t\treturn 'is at most'\n\t\tcase 'any':\n\t\t\treturn 'is any of'\n\t\tcase 'none':\n\t\t\treturn 'is none of'\n\t\tcase 'between':\n\t\t\treturn 'is between'\n\t\tcase 'outside':\n\t\t\treturn 'is outside'\n\t}\n}\n\n/**\n * Render a structured or scalar value display-neutrally.\n *\n * @remarks\n * An array renders as its elements, each `String`-coerced, joined with\n * `', '`. A Bounds-shaped record (a reason `Bounds` — the `between`/`outside`\n * comparison's expected value shape) renders its present sides joined with\n * the word `and`; an absent side is omitted. A finite number and everything\n * else fall back to plain `String()` coercion — a finite number is NOT\n * grouped here (that grouping is {@link interpolateMessage}'s own,\n * message-token-only behavior).\n *\n * @param value - The value to render\n * @returns A display-neutral rendering\n *\n * @example\n * ```ts\n * import { describeValue } from '@orkestrel/rater'\n *\n * describeValue([18, 25, 40]) // '18, 25, 40'\n * describeValue({ minimum: 18, maximum: 65 }) // '18 and 65'\n * describeValue(42) // '42'\n * ```\n */\nexport function describeValue(value: unknown): string {\n\tif (Array.isArray(value)) return value.map((entry) => String(entry)).join(', ')\n\tif (isBounds(value)) {\n\t\tconst sides: string[] = []\n\t\tif (value.minimum !== undefined) sides.push(String(value.minimum))\n\t\tif (value.maximum !== undefined) sides.push(String(value.maximum))\n\t\treturn sides.join(' and ')\n\t}\n\treturn String(value)\n}\n\n/**\n * Render one {@link Premise} into a display-neutral sentence.\n *\n * @param entry - The premise to render\n * @param labels - Optional field-to-label overrides, keyed by dot-joined field\n * @returns A display-neutral sentence\n *\n * @example\n * ```ts\n * import { describePremise } from '@orkestrel/rater'\n *\n * describePremise({ field: 'age', comparison: 'above', expected: 18, actual: 25, met: true })\n * // 'age is more than 18 ? met'\n * ```\n */\nexport function describePremise(entry: Premise, labels?: Readonly<Record<string, string>>): string {\n\tconst status = entry.met === undefined ? 'unknown' : entry.met ? 'met' : 'not met'\n\tif (entry.field === undefined || entry.comparison === undefined) {\n\t\treturn `${entry.description ?? 'Premise'} ? ${status}`\n\t}\n\tconst field = formatField(entry.field)\n\tconst label = labels?.[field] ?? entry.label ?? field\n\tconst expected = entry.expected === undefined ? '' : ` ${describeValue(entry.expected)}`\n\treturn `${label} ${describeComparison(entry.comparison)}${expected} ? ${status}`\n}\n\n/**\n * Build a {@link Premise} from an evaluated {@link Check}.\n *\n * @param check - The authored check\n * @param actual - The resolved subject value\n * @param met - Whether the check was met (absent when not yet evaluated)\n * @param labels - Optional field-to-label overrides, keyed by dot-joined field\n * @returns A fresh premise\n */\nexport function premiseCheck(\n\tcheck: Check,\n\tactual: unknown,\n\tmet: boolean | undefined,\n\tlabels?: Readonly<Record<string, string>>,\n): Premise {\n\tconst field = formatField(check.field)\n\treturn {\n\t\tfield: check.field,\n\t\t...(labels?.[field] === undefined ? {} : { label: labels[field] }),\n\t\tcomparison: check.operator,\n\t\texpected: check.value,\n\t\tactual,\n\t\t...(met === undefined ? {} : { met }),\n\t}\n}\n\n/**\n * Build premises from a quantitative factor's authored checks and evaluated\n * check results.\n *\n * @param checks - The factor's authored checks\n * @param results - The corresponding {@link CheckResult}s, in the same order\n * @param labels - Optional field-to-label overrides, keyed by dot-joined field\n * @returns A fresh list of premises, one per authored check\n */\nexport function checkPremises(\n\tchecks: readonly Check[] | undefined,\n\tresults: readonly CheckResult[] | undefined,\n\tlabels?: Readonly<Record<string, string>>,\n): readonly Premise[] {\n\treturn (checks ?? []).map((check, index) => {\n\t\tconst result = results?.[index]\n\t\treturn premiseCheck(check, result?.actual, result?.met, labels)\n\t})\n}\n\n/**\n * Describe a logical {@link Expression} tree without atom-specific evidence.\n *\n * @param expression - The expression to describe\n * @param labels - Optional field-to-label overrides, keyed by dot-joined field\n * @returns A display-neutral description\n */\nexport function describeExpression(\n\texpression: Expression,\n\tlabels?: Readonly<Record<string, string>>,\n): string {\n\tif (expression.form === 'atom') {\n\t\treturn describePremise(premiseCheck(expression.check, undefined, undefined, labels), labels)\n\t}\n\tconst descriptions = expression.operands.map((operand) => describeExpression(operand, labels))\n\treturn `${expression.operator} (${descriptions.join(', ')})`\n}\n\n/**\n * Build rich premises for one fired {@link Rule} by walking its premise atoms\n * and re-evaluating each against the working subject.\n *\n * @remarks\n * A reason {@link RuleResult} carries only booleans, so this is rater's own\n * premise-audit projection: each authored premise expression is flattened to\n * its atom leaves via {@link extractAtoms}, and each leaf's {@link Check} is\n * re-evaluated through the injected `evaluator`. A membership check (`any` /\n * `none`) over an EMPTY array value is content-free — a tautology or\n * contradiction that reads as \"is none of —\" in every surface — and is skipped.\n *\n * @param rule - The authored rule\n * @param working - The working subject to evaluate against\n * @param evaluator - The shared reason check evaluator\n * @param labels - Optional field-to-label overrides, keyed by dot-joined field\n * @returns A fresh, ordered list of rich premises\n */\nexport function logicalPremises(\n\trule: Rule,\n\tworking: Subject,\n\tevaluator: EvaluatorInterface,\n\tlabels?: Readonly<Record<string, string>>,\n): readonly Premise[] {\n\tconst output: Premise[] = []\n\tfor (const premise of rule.premises) {\n\t\tfor (const atom of extractAtoms(premise)) {\n\t\t\tconst { check } = atom\n\t\t\tif (\n\t\t\t\t(check.operator === 'any' || check.operator === 'none') &&\n\t\t\t\tArray.isArray(check.value) &&\n\t\t\t\tcheck.value.length === 0\n\t\t\t) {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tconst result = evaluator.evaluate(check, working)\n\t\t\toutput.push(premiseCheck(check, result.actual, result.met, labels))\n\t\t}\n\t}\n\treturn output\n}\n\n/**\n * Locate an authored {@link Rule} by id.\n *\n * @param definition - The logical definition to search\n * @param id - The rule id\n * @returns The matching rule, or `undefined`\n */\nexport function findRule(definition: LogicalDefinition, id: string): Rule | undefined {\n\treturn definition.rules.find((rule) => rule.id === id)\n}\n\n/**\n * Join one authored quantitative factor to its evaluated {@link FactorResult}.\n *\n * @param definition - The authored factor\n * @param results - The group's factor results\n * @param labels - Optional field-to-label overrides, keyed by dot-joined field\n * @returns A fresh worksheet factor\n */\nexport function worksheetFactor(\n\tdefinition: Factor,\n\tresults: readonly FactorResult[],\n\tlabels?: Readonly<Record<string, string>>,\n): WorksheetFactor {\n\tconst result = results.find((entry) => entry.id === definition.id)\n\treturn {\n\t\tid: definition.id,\n\t\tname: definition.name,\n\t\t...(definition.description === undefined ? {} : { description: definition.description }),\n\t\tapplied: result?.applied ?? false,\n\t\t...(result?.value === undefined ? {} : { value: result.value }),\n\t\tpremises: checkPremises(definition.checks, result?.checks, labels),\n\t}\n}\n\n/**\n * Join one authored quantitative group to its evaluated {@link GroupResult}.\n *\n * @param definition - The authored group\n * @param results - The definition's group results\n * @param labels - Optional field-to-label overrides, keyed by dot-joined field\n * @returns A fresh worksheet group\n */\nexport function worksheetGroup(\n\tdefinition: QuantitativeDefinition['groups'][number],\n\tresults: readonly GroupResult[],\n\tlabels?: Readonly<Record<string, string>>,\n): WorksheetGroup {\n\tconst result = results.find((entry) => entry.id === definition.id)\n\treturn {\n\t\tid: definition.id,\n\t\tname: definition.name,\n\t\t...(definition.description === undefined ? {} : { description: definition.description }),\n\t\tapplied: result?.applied ?? false,\n\t\tvalue: result?.value ?? 0,\n\t\tfactors: definition.factors.map((factor) =>\n\t\t\tworksheetFactor(factor, result?.factors ?? [], labels),\n\t\t),\n\t}\n}\n\n/**\n * Build one display-neutral {@link Step} row.\n *\n * @param stage - The step's stage\n * @param id - The stage's authored id, when it has one\n * @param name - The stage's authored name, when it has one\n * @param value - The stage's resolved value\n * @param expression - A display-neutral expression string\n * @returns A fresh step\n */\nexport function worksheetStep(\n\tstage: Stage,\n\tid: string | undefined,\n\tname: string | undefined,\n\tvalue: number,\n\texpression: string,\n): Step {\n\treturn {\n\t\tstage,\n\t\t...(id === undefined ? {} : { id }),\n\t\t...(name === undefined ? {} : { name }),\n\t\tvalue,\n\t\texpression,\n\t}\n}\n\n/**\n * Build the ordered {@link Step} rows for a resolved {@link Worksheet}.\n *\n * @param definition - The authored quantitative definition\n * @param result - The evaluated quantitative result\n * @param groups - The definition's already-joined worksheet groups\n * @returns A fresh, ordered list of steps: applied factors, then groups, then the total\n */\nexport function worksheetSteps(\n\tdefinition: QuantitativeDefinition,\n\tresult: QuantitativeResult,\n\tgroups: readonly WorksheetGroup[],\n): readonly Step[] {\n\tconst output: Step[] = []\n\tfor (const group of groups) {\n\t\tfor (const factor of group.factors) {\n\t\t\tif (factor.applied && factor.value !== undefined) {\n\t\t\t\toutput.push(\n\t\t\t\t\tworksheetStep(\n\t\t\t\t\t\t'factor',\n\t\t\t\t\t\tfactor.id,\n\t\t\t\t\t\tfactor.name,\n\t\t\t\t\t\tfactor.value,\n\t\t\t\t\t\t`${factor.id} = ${factor.value}`,\n\t\t\t\t\t),\n\t\t\t\t)\n\t\t\t}\n\t\t}\n\t\toutput.push(\n\t\t\tworksheetStep('group', group.id, group.name, group.value, `${group.id} = ${group.value}`),\n\t\t)\n\t}\n\toutput.push(\n\t\tworksheetStep(\n\t\t\t'total',\n\t\t\tdefinition.id,\n\t\t\tdefinition.name,\n\t\t\tresult.value,\n\t\t\t`${definition.aggregation} = ${result.value}`,\n\t\t),\n\t)\n\treturn output\n}\n\n/**\n * Join a {@link QuantitativeDefinition} and its {@link QuantitativeResult} into\n * a {@link Worksheet} — the rating audit trail.\n *\n * @param definition - The authored quantitative definition\n * @param result - The evaluated quantitative result\n * @param labels - Optional field-to-label overrides, keyed by dot-joined field\n * @returns A fresh worksheet\n */\nexport function resultsWorksheet(\n\tdefinition: QuantitativeDefinition,\n\tresult: QuantitativeResult,\n\tlabels?: Readonly<Record<string, string>>,\n): Worksheet {\n\tconst groups = definition.groups.map((group) => worksheetGroup(group, result.groups, labels))\n\treturn {\n\t\tid: definition.id,\n\t\tname: definition.name,\n\t\taggregation: definition.aggregation,\n\t\t...(definition.precision === undefined ? {} : { precision: definition.precision }),\n\t\tvalue: result.value,\n\t\tgroups,\n\t\tsteps: worksheetSteps(definition, result, groups),\n\t\ttrace: result.trace,\n\t\terrors: result.errors,\n\t\tsuccess: result.success,\n\t}\n}\n\n/**\n * Convert fired logical {@link RuleResult}s into line- or program-scoped\n * {@link Determination}s.\n *\n * @param definition - The authored logical definition\n * @param result - The evaluated logical result\n * @param rulings - Authored consequences, keyed by rule id\n * @param working - The working subject the rules ran against\n * @param line - The line id this pass is scoped to, when it is line-scoped\n * @param evaluator - The shared reason check evaluator\n * @param labels - Optional field-to-label overrides, keyed by dot-joined field\n * @returns A fresh list of determinations\n */\nexport function rulesToDeterminations(\n\tdefinition: LogicalDefinition,\n\tresult: LogicalResult,\n\trulings: Readonly<Record<string, Ruling>> | undefined,\n\tworking: Subject,\n\tline: string | undefined,\n\tevaluator: EvaluatorInterface,\n\tlabels?: Readonly<Record<string, string>>,\n): readonly Determination[] {\n\tconst output: Determination[] = []\n\tfor (const entry of result.rules) {\n\t\tconst authored = findRule(definition, entry.id)\n\t\tif (authored === undefined) continue\n\t\tconst routed = rulings?.[entry.id]\n\t\tif (!entry.applied && routed === undefined) continue\n\t\tconst resolved = routed?.line ?? line\n\t\toutput.push({\n\t\t\tid: entry.id,\n\t\t\teffect: routed?.effect ?? 'restriction',\n\t\t\tapplied: entry.applied,\n\t\t\t...(resolved === undefined ? {} : { line: resolved }),\n\t\t\t...(routed?.message === undefined\n\t\t\t\t? {}\n\t\t\t\t: { message: interpolateMessage(routed.message, working) }),\n\t\t\tpremises: logicalPremises(authored, working, evaluator, labels),\n\t\t})\n\t}\n\treturn output\n}\n\n/**\n * Convert an authority's fired {@link RuleResult}s into `limit`\n * {@link Determination}s.\n *\n * @param definition - The authority's logical definition\n * @param result - The evaluated logical result\n * @param rulings - Authored consequences, keyed by rule id\n * @param working - The working subject the authority ran against\n * @param evaluator - The shared reason check evaluator\n * @param labels - Optional field-to-label overrides, keyed by dot-joined field\n * @returns A fresh list of `limit` determinations\n */\nexport function authorityToDeterminations(\n\tdefinition: LogicalDefinition,\n\tresult: LogicalResult,\n\trulings: Readonly<Record<string, Ruling>> | undefined,\n\tworking: Subject,\n\tevaluator: EvaluatorInterface,\n\tlabels?: Readonly<Record<string, string>>,\n): readonly Determination[] {\n\tconst output: Determination[] = []\n\tfor (const entry of result.rules) {\n\t\tif (!entry.applied) continue\n\t\tconst authored = findRule(definition, entry.id)\n\t\tif (authored === undefined) continue\n\t\tconst routed = rulings?.[entry.id]\n\t\toutput.push({\n\t\t\tid: entry.id,\n\t\t\teffect: 'limit',\n\t\t\tapplied: true,\n\t\t\t...(routed?.line === undefined ? {} : { line: routed.line }),\n\t\t\t...(routed?.message === undefined\n\t\t\t\t? {}\n\t\t\t\t: { message: interpolateMessage(routed.message, working) }),\n\t\t\tpremises: logicalPremises(authored, working, evaluator, labels),\n\t\t})\n\t}\n\treturn output\n}\n\n/**\n * Convert authored {@link Notice}s into unconditionally-applied\n * `notice` {@link Determination}s.\n *\n * @param notices - The authored notices\n * @param working - The working subject to interpolate messages against\n * @returns A fresh list of notice determinations\n */\nexport function noticesToDeterminations(\n\tnotices: readonly Notice[] | undefined,\n\tworking: Readonly<Record<string, unknown>>,\n): readonly Determination[] {\n\treturn (notices ?? []).map((entry) => ({\n\t\tid: entry.id,\n\t\teffect: 'notice',\n\t\tapplied: true,\n\t\t...(entry.line === undefined ? {} : { line: entry.line }),\n\t\tmessage: interpolateMessage(entry.message, working),\n\t\tpremises: [],\n\t}))\n}\n\n/**\n * Keep only the determinations scoped to one line.\n *\n * @param determinations - The determinations to filter\n * @param id - The line id to keep\n * @returns The determinations whose `line` matches `id`\n */\nexport function filterLineDeterminations(\n\tdeterminations: readonly Determination[],\n\tid: string,\n): readonly Determination[] {\n\treturn determinations.filter((entry) => entry.line === id)\n}\n\n/**\n * Keep only program-scoped (line-unscoped) determinations.\n *\n * @param determinations - The determinations to filter\n * @returns The determinations with no `line`\n */\nexport function filterProgramDeterminations(\n\tdeterminations: readonly Determination[],\n): readonly Determination[] {\n\treturn determinations.filter((entry) => entry.line === undefined)\n}\n\n/**\n * Derive the eligibility impact of a set of determinations.\n *\n * @param determinations - The determinations to fold\n * @returns The most severe eligibility any APPLIED determination's effect carries\n *\n * @example\n * ```ts\n * import { deriveDeterminationEligibility } from '@orkestrel/rater'\n *\n * deriveDeterminationEligibility([{ id: 'r1', effect: 'restriction', applied: true, premises: [] }])\n * // 'ineligible'\n * ```\n */\nexport function deriveDeterminationEligibility(\n\tdeterminations: readonly Determination[],\n): Eligibility {\n\tconst impacts: Eligibility[] = ['eligible']\n\tfor (const entry of determinations) {\n\t\tconst eligibility = entry.applied ? EFFECT_ELIGIBILITIES[entry.effect] : undefined\n\t\tif (eligibility !== undefined) impacts.push(eligibility)\n\t}\n\treturn combineEligibilities(impacts)\n}\n\n/**\n * Return the most severe {@link Eligibility} in a list.\n *\n * @param eligibilities - The eligibilities to combine\n * @returns The most severe eligibility, or `'eligible'` for an empty list\n *\n * @example\n * ```ts\n * import { combineEligibilities } from '@orkestrel/rater'\n *\n * combineEligibilities(['eligible', 'referral']) // 'referral'\n * ```\n */\nexport function combineEligibilities(eligibilities: readonly Eligibility[]): Eligibility {\n\tfor (const eligibility of ELIGIBILITY_PRECEDENCE) {\n\t\tif (eligibilities.includes(eligibility)) return eligibility\n\t}\n\treturn 'eligible'\n}\n\n/**\n * Convert an {@link Eligibility} to its deterministic authority {@link Decision}.\n *\n * @param eligibility - The eligibility to convert\n * @returns The matching decision\n */\nexport function decideEligibility(eligibility: Eligibility): Decision {\n\treturn ELIGIBILITY_DECISIONS[eligibility]\n}\n\n/**\n * Derive the final {@link Status} from eligibility, determinations, and rated\n * line evidence.\n *\n * @param eligibility - The program's combined eligibility\n * @param determinations - Every determination in scope (program and line)\n * @param lines - The program's rated lines\n * @returns The derived status\n */\nexport function deriveStatus(\n\teligibility: Eligibility,\n\tdeterminations: readonly Determination[],\n\tlines: readonly LineResult[],\n): Status {\n\tif (eligibility === 'ineligible') return 'ineligible'\n\tif (eligibility === 'referral') return 'referral'\n\tif (determinations.some((entry) => entry.applied && entry.effect === 'condition'))\n\t\treturn 'conditional'\n\tif (lines.some((entry) => !(entry.worksheet?.success ?? false) || entry.amount === undefined))\n\t\treturn 'unrated'\n\treturn 'eligible'\n}\n\n/**\n * Build a rated {@link LineResult} from a line's evaluated\n * {@link QuantitativeResult}.\n *\n * @param definition - The authored line definition\n * @param result - The evaluated quantitative result\n * @param determinations - The line-scoped determinations\n * @param labels - Optional field-to-label overrides, keyed by dot-joined field\n * @returns A fresh line result\n */\nexport function ratedLine(\n\tdefinition: LineDefinition,\n\tresult: QuantitativeResult,\n\tdeterminations: readonly Determination[],\n\tlabels?: Readonly<Record<string, string>>,\n): LineResult {\n\treturn {\n\t\tid: definition.id,\n\t\tname: definition.name,\n\t\teligibility: deriveDeterminationEligibility(determinations),\n\t\t...(result.success ? { amount: result.value } : {}),\n\t\tworksheet: resultsWorksheet(definition.rate, result, labels),\n\t\tdeterminations,\n\t}\n}\n\n/**\n * Sum defined line amounts.\n *\n * @param lines - The rated lines\n * @returns The sum of every line's `amount`, or `undefined` when no line has one\n */\nexport function sumAmounts(lines: readonly LineResult[]): number | undefined {\n\tlet total = 0\n\tlet count = 0\n\tfor (const rated of lines) {\n\t\tif (rated.amount !== undefined) {\n\t\t\ttotal += rated.amount\n\t\t\tcount += 1\n\t\t}\n\t}\n\treturn count === 0 ? undefined : total\n}\n\n/**\n * Build an authority outcome projection from an assembled {@link ProgramResult}.\n *\n * @param result - The program result computed before authority runs\n * @returns A record shaped for the authority's `outcome` projection\n */\nexport function outcomeProjection(result: ProgramResult): Readonly<Record<string, unknown>> {\n\tconst lines: Record<string, unknown> = {}\n\tfor (const rated of result.lines) {\n\t\tif (rated.amount !== undefined) lines[rated.id] = rated.amount\n\t}\n\treturn { eligibility: result.eligibility, status: result.status, total: result.total, lines }\n}\n\n/**\n * Assemble a final {@link ProgramResult} from its rated parts.\n *\n * @param definition - The authored program definition\n * @param lines - The program's rated lines\n * @param determinations - The program-scoped determinations\n * @param derivations - The pass-level quantitative worksheets\n * @param total - The program's total amount, when the total handler produced one\n * @param decision - The authority-derived decision, when one was reached\n * @param trace - The accumulated reasoning trace\n * @param errors - The accumulated reasoning errors\n * @returns A fresh program result\n */\nexport function programResult(\n\tdefinition: ProgramDefinition,\n\tlines: readonly LineResult[],\n\tdeterminations: readonly Determination[],\n\tderivations: readonly Worksheet[],\n\ttotal: number | undefined,\n\tdecision: Decision | undefined,\n\ttrace: readonly string[],\n\terrors: readonly string[],\n): ProgramResult {\n\tconst scoped = deriveDeterminationEligibility(determinations)\n\tconst lineEligibilities = lines.map((entry) => entry.eligibility)\n\tconst eligibility = combineEligibilities([scoped, ...lineEligibilities])\n\tconst allDeterminations = [...determinations, ...lines.flatMap((entry) => entry.determinations)]\n\treturn {\n\t\tid: definition.id,\n\t\tname: definition.name,\n\t\teligibility,\n\t\tstatus: deriveStatus(eligibility, allDeterminations, lines),\n\t\t...(decision === undefined ? {} : { decision }),\n\t\tlines,\n\t\tdeterminations,\n\t\tderivations,\n\t\t...(total === undefined ? {} : { total }),\n\t\tsuccess: errors.length === 0 && lines.every((entry) => entry.worksheet?.success ?? true),\n\t\ttrace,\n\t\terrors,\n\t}\n}\n\n/**\n * Return authored line references (in passes, rulings, or notices) that name\n * no line on the program.\n *\n * @param definition - The program definition to check\n * @returns A fresh, deduped list of missing line ids\n */\nexport function findMissingLineReferences(definition: ProgramDefinition): readonly string[] {\n\tconst ids = new Set(definition.lines.map((entry) => entry.id))\n\tconst missing = new Set<string>()\n\tfor (const entry of definition.passes ?? []) {\n\t\tif (entry.line !== undefined && !ids.has(entry.line)) missing.add(entry.line)\n\t}\n\tfor (const entry of Object.values(definition.rulings ?? {})) {\n\t\tif (entry.line !== undefined && !ids.has(entry.line)) missing.add(entry.line)\n\t}\n\tfor (const entry of definition.notices ?? []) {\n\t\tif (entry.line !== undefined && !ids.has(entry.line)) missing.add(entry.line)\n\t}\n\treturn [...missing]\n}\n\n/** Determine whether a working record already carries a reserved rater key. */\nexport function hasReservedKey(subject: Readonly<Record<string, unknown>>): boolean {\n\treturn Object.hasOwn(subject, AGGREGATE_KEY) || Object.hasOwn(subject, OUTCOME_KEY)\n}\n\n/**\n * Locate a {@link ProgramDefinition}'s reserved-key collisions — a quantitative\n * pass whose id shadows {@link AGGREGATE_KEY} / {@link OUTCOME_KEY}, or a\n * logical pass's or the authority's fired rule conclusion whose asserted\n * field's FIRST path segment shadows one.\n *\n * @remarks\n * A quantitative pass writes its value onto the working subject under its own\n * definition id (`assignField`), silently clobbering the reserved aggregate or\n * outcome projection when the id collides. A fired rule's conclusion atoms\n * merge onto the working subject (`mergeSubjects`/`extractConclusions`) keyed\n * by `formatField(check.field)` — an atom whose field's first segment is a\n * reserved key is flagged here even when its FULL formatted key is a nested\n * form (`outcome.total`), since authoring a conclusion under a reserved\n * top-level segment is never intentional.\n *\n * @param definition - The program definition to scan\n * @returns A fresh, deduped list of offending pass or rule ids\n *\n * @example\n * ```ts\n * import { quantitativeDefinition } from '@orkestrel/reason'\n * import { findReservedCollisions, passDefinition, programDefinition } from '@orkestrel/rater'\n *\n * findReservedCollisions(\n * \tprogramDefinition('p1', 'P1', [], {\n * \t\tpasses: [passDefinition(quantitativeDefinition('aggregate', 'Aggregate', []))],\n * \t}),\n * ) // ['aggregate']\n * ```\n */\nexport function findReservedCollisions(definition: ProgramDefinition): readonly string[] {\n\tconst offending = new Set<string>()\n\tconst logicals: LogicalDefinition[] = []\n\tfor (const pass of definition.passes ?? []) {\n\t\tif (pass.definition.reasoning === 'quantitative') {\n\t\t\tif (pass.definition.id === AGGREGATE_KEY || pass.definition.id === OUTCOME_KEY) {\n\t\t\t\toffending.add(pass.definition.id)\n\t\t\t}\n\t\t} else if (pass.definition.reasoning === 'logical') {\n\t\t\tlogicals.push(pass.definition)\n\t\t}\n\t}\n\tif (definition.authority !== undefined) logicals.push(definition.authority)\n\tfor (const logical of logicals) {\n\t\tfor (const rule of logical.rules) {\n\t\t\tfor (const atom of extractAtoms(rule.conclusion)) {\n\t\t\t\tconst head = Array.isArray(atom.check.field) ? atom.check.field[0] : atom.check.field\n\t\t\t\tif (head === AGGREGATE_KEY || head === OUTCOME_KEY) offending.add(rule.id)\n\t\t\t}\n\t\t}\n\t}\n\treturn [...offending]\n}\n\n/**\n * Assert a value is a valid rater {@link Subject}, narrowing it in place.\n *\n * @param subject - The candidate subject to validate\n * @throws {@link RaterError} `'MISMATCH'` when the value is not a record, or\n * when it already carries a reserved rater working-subject key\n */\nexport function assertSubject(subject: unknown): asserts subject is Subject {\n\tif (!isRecord(subject)) throw new RaterError('MISMATCH', 'Subject must be a record')\n\tif (hasReservedKey(subject)) throw new RaterError('MISMATCH', 'Subject uses a reserved rater key')\n}\n\n/**\n * Sum aggregate fields across a batch of subjects.\n *\n * @param subjects - The batch of subjects\n * @param fields - The fields to sum\n * @returns A fresh record of dot-joined field to summed finite value\n */\nexport function aggregateSums(\n\tsubjects: readonly Subject[],\n\tfields: readonly FieldPath[],\n): Readonly<Record<string, number>> {\n\tconst sums: Record<string, number> = {}\n\tfor (const field of fields) sums[formatField(field)] = 0\n\tfor (const subject of subjects) {\n\t\tfor (const field of fields) {\n\t\t\tconst key = formatField(field)\n\t\t\tconst value = resolveField(subject, field)\n\t\t\tif (isFiniteNumber(value)) sums[key] = (sums[key] ?? 0) + value\n\t\t}\n\t}\n\treturn sums\n}\n\n/**\n * Partition a batch of subjects by a field, summing aggregate fields per\n * partition.\n *\n * @remarks\n * The partition key is the resolved `by` field, coerced with `String` —\n * `undefined` collapses to the empty string, so a subject missing the field\n * and a subject whose field is literally `''` land in the SAME partition,\n * and a numeric `1` collides with the string `'1'`.\n *\n * @param subjects - The batch of subjects\n * @param fields - The fields to sum within each partition\n * @param by - The partition key field; no partition is built when absent\n * @returns A fresh list of aggregate groups, or an empty list when `by` is absent\n */\nexport function aggregateGroups(\n\tsubjects: readonly Subject[],\n\tfields: readonly FieldPath[],\n\tby?: FieldPath,\n): readonly AggregateGroup[] {\n\tif (by === undefined) return []\n\tconst groups = new Map<string, Subject[]>()\n\tfor (const subject of subjects) {\n\t\tconst key = String(resolveField(subject, by) ?? '')\n\t\tconst entries = groups.get(key) ?? []\n\t\tentries.push(subject)\n\t\tgroups.set(key, entries)\n\t}\n\treturn [...groups.entries()].map(([key, entries]) => ({\n\t\tkey,\n\t\tcount: entries.length,\n\t\tsums: aggregateSums(entries, fields),\n\t}))\n}\n\n/**\n * Collect the deduped, ordered union of every program's aggregate fields.\n *\n * @param programs - The compiled programs to scan\n * @returns A fresh list of aggregate field paths, in first-seen order\n *\n * @example\n * ```ts\n * import { aggregateFields } from '@orkestrel/rater'\n *\n * aggregateFields(programs) // [['income'], ['assets']]\n * ```\n */\nexport function aggregateFields(programs: readonly ProgramInterface[]): readonly FieldPath[] {\n\tconst fields: FieldPath[] = []\n\tconst keys = new Set<string>()\n\tfor (const program of programs) {\n\t\tfor (const field of program.definition.aggregate?.fields ?? []) {\n\t\t\tconst key = formatField(field)\n\t\t\tif (!keys.has(key)) {\n\t\t\t\tkeys.add(key)\n\t\t\t\tfields.push(field)\n\t\t\t}\n\t\t}\n\t}\n\treturn fields\n}\n\n/**\n * Locate the {@link AggregateGroup} a subject belongs to.\n *\n * @remarks\n * The lookup key is the resolved `by` field, coerced with `String` —\n * `undefined` collapses to the empty string, matching {@link aggregateGroups}'\n * partition-key coercion (a missing key and an empty-string key collide, and\n * a numeric `1` collides with the string `'1'`).\n *\n * @param subject - The subject to key\n * @param groups - The candidate groups to search\n * @param by - The partition key field; no group is found when absent\n * @returns The subject's matching group, or `undefined`\n *\n * @example\n * ```ts\n * import { groupFor } from '@orkestrel/rater'\n *\n * groupFor({ region: 'east' }, [{ key: 'east', count: 1, sums: {} }], ['region'])\n * // { key: 'east', count: 1, sums: {} }\n * ```\n */\nexport function groupFor(\n\tsubject: Subject,\n\tgroups: readonly AggregateGroup[],\n\tby?: FieldPath,\n): AggregateGroup | undefined {\n\tif (by === undefined) return undefined\n\tconst key = String(resolveField(subject, by) ?? '')\n\treturn groups.find((entry) => entry.key === key)\n}\n\n/**\n * Build the batch aggregate working projection written under\n * {@link AGGREGATE_KEY}'s value.\n *\n * @param count - The batch (or partition) subject count\n * @param sums - The summed aggregate fields\n * @param group - The subject's own partition, when the aggregate is partitioned\n * @returns A fresh aggregate projection record\n */\nexport function aggregateProjection(\n\tcount: number,\n\tsums: Readonly<Record<string, number>>,\n\tgroup?: AggregateGroup,\n): Readonly<Record<string, unknown>> {\n\treturn { count, sums, ...(group === undefined ? {} : { group }) }\n}\n\n/**\n * Build the reserved-key record an aggregate gate definition runs against.\n *\n * @param count - The batch (or partition) subject count\n * @param sums - The summed aggregate fields\n * @param group - The subject's own partition, when the aggregate is partitioned\n * @returns A fresh record carrying the aggregate projection under {@link AGGREGATE_KEY}\n */\nexport function aggregateRecord(\n\tcount: number,\n\tsums: Readonly<Record<string, number>>,\n\tgroup?: AggregateGroup,\n): Readonly<Record<string, unknown>> {\n\treturn { [AGGREGATE_KEY]: aggregateProjection(count, sums, group) }\n}\n\n/**\n * Build zero sums for a set of fields.\n *\n * @param fields - The fields to zero\n * @returns A fresh record of dot-joined field to `0`\n */\nexport function emptySums(fields: readonly FieldPath[]): Readonly<Record<string, number>> {\n\tconst sums: Record<string, number> = {}\n\tfor (const field of fields) sums[formatField(field)] = 0\n\treturn sums\n}\n\n/**\n * Complete a partial status tally record with zero entries for every missing\n * {@link Status}.\n *\n * @param entries - The partial tally entries to complete\n * @returns A record with all five statuses present\n */\nexport function completeTallies(\n\tentries: Partial<Record<Status, Tally>>,\n): Readonly<Record<Status, Tally>> {\n\treturn {\n\t\tineligible: entries.ineligible ?? { count: 0, sums: {} },\n\t\treferral: entries.referral ?? { count: 0, sums: {} },\n\t\tconditional: entries.conditional ?? { count: 0, sums: {} },\n\t\tunrated: entries.unrated ?? { count: 0, sums: {} },\n\t\teligible: entries.eligible ?? { count: 0, sums: {} },\n\t}\n}\n\n/**\n * Build empty status tallies in precedence order.\n *\n * @param fields - The fields each tally's sums are zeroed for\n * @returns A fresh, complete tally record\n */\nexport function emptyTallies(fields: readonly FieldPath[]): Readonly<Record<Status, Tally>> {\n\tconst entries: Partial<Record<Status, Tally>> = {}\n\tfor (const status of STATUS_PRECEDENCE) entries[status] = { count: 0, sums: emptySums(fields) }\n\treturn completeTallies(entries)\n}\n\n/**\n * Add one subject's aggregate contribution to a status tally record.\n *\n * @param tallies - The tallies to update\n * @param status - The subject's derived status\n * @param subject - The subject to fold in\n * @param fields - The fields to sum\n * @returns A fresh, complete tally record with the subject folded in\n */\nexport function tallySubject(\n\ttallies: Readonly<Record<Status, Tally>>,\n\tstatus: Status,\n\tsubject: Subject,\n\tfields: readonly FieldPath[],\n): Readonly<Record<Status, Tally>> {\n\tconst updated: Partial<Record<Status, Tally>> = { ...tallies }\n\tconst current = tallies[status]\n\tconst sums: Record<string, number> = { ...current.sums }\n\tfor (const field of fields) {\n\t\tconst key = formatField(field)\n\t\tconst value = resolveField(subject, field)\n\t\tif (isFiniteNumber(value)) sums[key] = (sums[key] ?? 0) + value\n\t}\n\tupdated[status] = { count: current.count + 1, sums }\n\treturn completeTallies(updated)\n}\n","import type {\n\tEvaluatorInterface,\n\tQuantitativeDefinition,\n\tQuantitativeResult,\n\tReasonInterface,\n\tSubject,\n} from '@orkestrel/reason'\nimport type {\n\tDetermination,\n\tLineResult,\n\tProgramDefinition,\n\tProgramInterface,\n\tProgramOptions,\n\tProgramResult,\n\tWorksheet,\n} from '../types.js'\nimport {\n\tassignField,\n\tbuildErrorResult,\n\tcreateEvaluator,\n\textractConclusions,\n\tmergeSubjects,\n} from '@orkestrel/reason'\nimport { AGGREGATE_KEY, OUTCOME_KEY } from '../constants.js'\nimport { RaterError } from '../errors.js'\nimport {\n\tassertSubject,\n\tauthorityToDeterminations,\n\tdecideEligibility,\n\tderiveDeterminationEligibility,\n\tfilterLineDeterminations,\n\tfilterProgramDeterminations,\n\tfindMissingLineReferences,\n\tfindReservedCollisions,\n\tfindRule,\n\tnoticesToDeterminations,\n\toutcomeProjection,\n\tprogramResult,\n\tratedLine,\n\tresultsWorksheet,\n\trulesToDeterminations,\n} from '../helpers.js'\n\n/**\n * A compiled program — rates one subject at a time over an injected, shared\n * reasoning engine.\n *\n * @remarks\n * The engine is INJECTED and never owned, destroyed, or otherwise mutated by\n * the program: every actual evaluation (passes, lines, authority) delegates to\n * it. `rate` builds its working subject through copy-on-write overlays only —\n * the caller's `subject` is never mutated.\n */\nexport class Program implements ProgramInterface {\n\treadonly #definition: ProgramDefinition\n\treadonly #engine: ReasonInterface\n\treadonly #evaluator: EvaluatorInterface\n\treadonly #total: ProgramOptions['total']\n\treadonly #labels: Readonly<Record<string, string>> | undefined\n\n\tconstructor(definition: ProgramDefinition, reason: ReasonInterface, options?: ProgramOptions) {\n\t\tconst missing = findMissingLineReferences(definition)\n\t\tif (missing.length > 0) {\n\t\t\tthrow new RaterError('MISSING', `Unknown line reference: ${missing.join(', ')}`, {\n\t\t\t\tprogram: definition.id,\n\t\t\t})\n\t\t}\n\t\tconst collisions = findReservedCollisions(definition)\n\t\tif (collisions.length > 0) {\n\t\t\tthrow new RaterError('DEFINITION', `Reserved key collision: ${collisions.join(', ')}`, {\n\t\t\t\tprogram: definition.id,\n\t\t\t\tkeys: collisions,\n\t\t\t})\n\t\t}\n\t\tthis.#definition = definition\n\t\tthis.#engine = reason\n\t\tthis.#evaluator = createEvaluator()\n\t\tthis.#total = options?.total\n\t\tthis.#labels = options?.labels\n\t}\n\n\tget id(): string {\n\t\treturn this.#definition.id\n\t}\n\n\tget name(): string {\n\t\treturn this.#definition.name\n\t}\n\n\tget definition(): ProgramDefinition {\n\t\treturn this.#definition\n\t}\n\n\trate(subject: Subject, aggregate?: Readonly<Record<string, unknown>>): ProgramResult {\n\t\tassertSubject(subject)\n\t\tlet working: Subject =\n\t\t\taggregate === undefined ? subject : assignField(subject, AGGREGATE_KEY, aggregate)\n\t\tconst determinations: Determination[] = []\n\t\tconst derivations: Worksheet[] = []\n\t\tconst trace: string[] = []\n\t\tconst errors: string[] = []\n\n\t\tfor (const pass of this.#definition.passes ?? []) {\n\t\t\tif (pass.definition.reasoning === 'quantitative') {\n\t\t\t\tconst result = this.#engine.reason(working, pass.definition)\n\t\t\t\ttrace.push(...result.trace)\n\t\t\t\terrors.push(...result.errors)\n\t\t\t\tif (result.reasoning === 'quantitative') {\n\t\t\t\t\tworking = assignField(working, pass.definition.id, result.value)\n\t\t\t\t\tderivations.push(resultsWorksheet(pass.definition, result, this.#labels))\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tconst result = this.#engine.reason(working, pass.definition)\n\t\t\t\ttrace.push(...result.trace)\n\t\t\t\terrors.push(...result.errors)\n\t\t\t\tif (result.reasoning === 'logical') {\n\t\t\t\t\tdeterminations.push(\n\t\t\t\t\t\t...rulesToDeterminations(\n\t\t\t\t\t\t\tpass.definition,\n\t\t\t\t\t\t\tresult,\n\t\t\t\t\t\t\tthis.#definition.rulings,\n\t\t\t\t\t\t\tworking,\n\t\t\t\t\t\t\tpass.line,\n\t\t\t\t\t\t\tthis.#evaluator,\n\t\t\t\t\t\t\tthis.#labels,\n\t\t\t\t\t\t),\n\t\t\t\t\t)\n\t\t\t\t\tfor (const entry of result.rules) {\n\t\t\t\t\t\tif (!entry.applied) continue\n\t\t\t\t\t\tconst rule = findRule(pass.definition, entry.id)\n\t\t\t\t\t\tif (rule !== undefined)\n\t\t\t\t\t\t\tworking = mergeSubjects(working, extractConclusions(rule.conclusion))\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tdeterminations.push(...noticesToDeterminations(this.#definition.notices, working))\n\t\tconst scoped = filterProgramDeterminations(determinations)\n\t\tconst lines = this.#rateLines(working, scoped, determinations)\n\t\tconst total = this.#total?.(lines)\n\t\tconst base = programResult(\n\t\t\tthis.#definition,\n\t\t\tlines,\n\t\t\tscoped,\n\t\t\tderivations,\n\t\t\ttotal,\n\t\t\tundefined,\n\t\t\ttrace,\n\t\t\terrors,\n\t\t)\n\t\tif (this.#definition.authority === undefined) return base\n\n\t\tconst authorityWorking = assignField(working, OUTCOME_KEY, outcomeProjection(base))\n\t\tconst authorityResult = this.#engine.reason(authorityWorking, this.#definition.authority)\n\t\tif (authorityResult.reasoning !== 'logical') return base\n\t\tconst limits = authorityToDeterminations(\n\t\t\tthis.#definition.authority,\n\t\t\tauthorityResult,\n\t\t\tthis.#definition.rulings,\n\t\t\tauthorityWorking,\n\t\t\tthis.#evaluator,\n\t\t\tthis.#labels,\n\t\t)\n\t\tconst decision =\n\t\t\tlimits.length === 0 && authorityResult.errors.length === 0\n\t\t\t\t? decideEligibility(base.eligibility)\n\t\t\t\t: undefined\n\t\treturn programResult(\n\t\t\tthis.#definition,\n\t\t\tlines,\n\t\t\t[...scoped, ...limits],\n\t\t\tderivations,\n\t\t\ttotal,\n\t\t\tdecision,\n\t\t\t[...trace, ...authorityResult.trace],\n\t\t\t[...errors, ...authorityResult.errors],\n\t\t)\n\t}\n\n\t#rateLines(\n\t\tworking: Subject,\n\t\tcontext: readonly Determination[],\n\t\tdeterminations: readonly Determination[],\n\t): readonly LineResult[] {\n\t\tconst lines: LineResult[] = []\n\t\tfor (const entry of this.#definition.lines) {\n\t\t\tconst own = filterLineDeterminations(determinations, entry.id)\n\t\t\tconst line = ratedLine(entry, this.#quantitative(working, entry.rate), own, this.#labels)\n\t\t\tlines.push({\n\t\t\t\t...line,\n\t\t\t\teligibility: deriveDeterminationEligibility([...context, ...line.determinations]),\n\t\t\t})\n\t\t}\n\t\treturn lines\n\t}\n\n\t// Narrows the engine's tagged ReasonResult union down to a QuantitativeResult\n\t// for a definition the engine is guaranteed (by dispatch-by-reasoning) to\n\t// resolve as such — a defensive, total fallback rather than an assertion.\n\t#quantitative(subject: Subject, definition: QuantitativeDefinition): QuantitativeResult {\n\t\tconst result = this.#engine.reason(subject, definition)\n\t\tif (result.reasoning === 'quantitative') return result\n\t\tconst failure = buildErrorResult(\n\t\t\tdefinition,\n\t\t\t`Expected quantitative result, got ${result.reasoning}`,\n\t\t)\n\t\tconst errors = [...result.errors, ...failure.errors]\n\t\tif (failure.reasoning === 'quantitative') return { ...failure, errors }\n\t\treturn {\n\t\t\treasoning: 'quantitative',\n\t\t\tvalue: 0,\n\t\t\tgroups: [],\n\t\t\tcount: 0,\n\t\t\tsuccess: false,\n\t\t\ttrace: [],\n\t\t\terrors,\n\t\t}\n\t}\n}\n","import type { EmitterInterface } from '@orkestrel/emitter'\nimport type { ReasonInterface } from '@orkestrel/reason'\nimport type {\n\tProgramDefinition,\n\tProgramInterface,\n\tProgramManagerEventMap,\n\tProgramManagerInterface,\n\tProgramManagerOptions,\n\tProgramOptions,\n} from '../types.js'\nimport { Emitter } from '@orkestrel/emitter'\nimport { DEFAULT_RATER_VALIDATE } from '../constants.js'\nimport { RaterError } from '../errors.js'\nimport { isProgramDefinition } from '../validators.js'\nimport { Program } from './Program.js'\n\n/**\n * An ordered manager over compiled {@link ProgramInterface}s (AGENTS §9), built\n * over an injected, shared reasoning engine.\n *\n * @remarks\n * OWNS its `#programs` collection and its own {@link Emitter} over\n * {@link ProgramManagerEventMap}. `destroy()` is idempotent and tears the\n * emitter down LAST; every other call afterwards throws\n * {@link RaterError} `'DESTROYED'`.\n */\nexport class ProgramManager implements ProgramManagerInterface {\n\treadonly #programs = new Map<string, ProgramInterface>()\n\treadonly #engine: ReasonInterface\n\treadonly #emitter: Emitter<ProgramManagerEventMap>\n\treadonly #total: ProgramOptions['total']\n\treadonly #labels: Readonly<Record<string, string>> | undefined\n\treadonly #validate: boolean\n\t#destroyed = false\n\n\tconstructor(engine: ReasonInterface, options?: ProgramManagerOptions) {\n\t\tthis.#engine = engine\n\t\tthis.#emitter = new Emitter<ProgramManagerEventMap>({ on: options?.on, error: options?.error })\n\t\tthis.#total = options?.total\n\t\tthis.#labels = options?.labels\n\t\tthis.#validate = options?.validate ?? DEFAULT_RATER_VALIDATE\n\t}\n\n\tget emitter(): EmitterInterface<ProgramManagerEventMap> {\n\t\treturn this.#emitter\n\t}\n\n\tget size(): number {\n\t\tthis.#ensureAlive()\n\t\treturn this.#programs.size\n\t}\n\n\thas(id: string): boolean {\n\t\tthis.#ensureAlive()\n\t\treturn this.#programs.has(id)\n\t}\n\n\tprogram(id: string): ProgramInterface | undefined {\n\t\tthis.#ensureAlive()\n\t\treturn this.#programs.get(id)\n\t}\n\n\tprograms(): readonly ProgramInterface[] {\n\t\tthis.#ensureAlive()\n\t\treturn [...this.#programs.values()]\n\t}\n\n\tadd(definition: ProgramDefinition, options?: ProgramOptions): ProgramInterface {\n\t\tthis.#ensureAlive()\n\t\tif (this.#programs.has(definition.id)) {\n\t\t\tthrow new RaterError('DUPLICATE', `Program already exists: ${definition.id}`, {\n\t\t\t\tprogram: definition.id,\n\t\t\t})\n\t\t}\n\t\tconst id = definition.id\n\t\tif (this.#validate && !isProgramDefinition(definition)) {\n\t\t\tthrow new RaterError('DEFINITION', 'Program definition failed validation', { program: id })\n\t\t}\n\t\tconst program = new Program(definition, this.#engine, {\n\t\t\ttotal: options?.total ?? this.#total,\n\t\t\tlabels: options?.labels ?? this.#labels,\n\t\t})\n\t\tthis.#programs.set(program.id, program)\n\t\tthis.#emitter.emit('add', program.id)\n\t\treturn program\n\t}\n\n\tremove(ids: readonly string[]): boolean\n\tremove(id: string): boolean\n\tremove(): void\n\tremove(target?: string | readonly string[]): boolean | void {\n\t\tthis.#ensureAlive()\n\t\tif (target === undefined) {\n\t\t\tfor (const id of this.#programs.keys()) this.#emitter.emit('remove', id)\n\t\t\tthis.#programs.clear()\n\t\t\treturn\n\t\t}\n\t\tif (typeof target === 'string') return this.#removeOne(target)\n\t\tlet removed = true\n\t\tfor (const id of target) removed = this.#removeOne(id) && removed\n\t\treturn removed\n\t}\n\n\tdestroy(): void {\n\t\tif (this.#destroyed) return\n\t\tthis.#programs.clear()\n\t\tthis.#destroyed = true\n\t\tthis.#emitter.emit('destroy')\n\t\tthis.#emitter.destroy()\n\t}\n\n\t#removeOne(id: string): boolean {\n\t\tconst removed = this.#programs.delete(id)\n\t\tif (removed) this.#emitter.emit('remove', id)\n\t\treturn removed\n\t}\n\n\t#ensureAlive(): void {\n\t\tif (this.#destroyed) throw new RaterError('DESTROYED', 'ProgramManager has been destroyed')\n\t}\n}\n","import type { EmitterInterface } from '@orkestrel/emitter'\nimport type {\n\tEvaluatorInterface,\n\tLogicalDefinition,\n\tLogicalResult,\n\tReasonInterface,\n\tSubject,\n} from '@orkestrel/reason'\nimport type {\n\tAggregateGroup,\n\tAggregateResult,\n\tDetermination,\n\tProgramInterface,\n\tProgramManagerInterface,\n\tRaterEventMap,\n\tRaterInterface,\n\tRaterOptions,\n\tRuling,\n\tSubjectResult,\n\tTally,\n} from './types.js'\nimport { Emitter } from '@orkestrel/emitter'\nimport {\n\tbuildErrorResult,\n\tcreateEvaluator,\n\tcreateLogicalReasoner,\n\tcreateQuantitativeReasoner,\n\tcreateReason,\n} from '@orkestrel/reason'\nimport { isRecord } from '@orkestrel/contract'\nimport { RaterError } from './errors.js'\nimport {\n\taggregateFields,\n\taggregateGroups,\n\taggregateProjection,\n\taggregateRecord,\n\taggregateSums,\n\tassertSubject,\n\temptyTallies,\n\tgroupFor,\n\trulesToDeterminations,\n\ttallySubject,\n} from './helpers.js'\nimport { ProgramManager } from './programs/ProgramManager.js'\n\n/**\n * The rating orchestrator — owns the shared reasoning engine and an ordered\n * {@link ProgramManager}, and projects results into the rating domain\n * vocabulary.\n *\n * @remarks\n * Constructs ONE shared reason engine (quantitative + logical reasoners,\n * `bail: false`) and injects it into every compiled program — Rater performs\n * NO evaluation arithmetic of its own; it only orchestrates and projects. The\n * batch `rate` overload is declared FIRST (AGENTS §9.2) so an array subject\n * resolves to the batch form. `destroy()` tears down the manager, then the\n * engine, then the emitter LAST (AGENTS §13); afterwards every other method\n * throws {@link RaterError} `'DESTROYED'`.\n */\nexport class Rater implements RaterInterface {\n\treadonly #emitter: Emitter<RaterEventMap>\n\treadonly #manager: ProgramManager\n\treadonly #engine: ReasonInterface\n\treadonly #evaluator: EvaluatorInterface\n\t#destroyed = false\n\n\tconstructor(options?: RaterOptions) {\n\t\tthis.#emitter = new Emitter<RaterEventMap>({ on: options?.on, error: options?.error })\n\t\tthis.#engine = createReason({\n\t\t\treasoners: [createQuantitativeReasoner(), createLogicalReasoner()],\n\t\t\tbail: false,\n\t\t})\n\t\tthis.#evaluator = createEvaluator()\n\t\tthis.#manager = new ProgramManager(this.#engine, {\n\t\t\ttotal: options?.total,\n\t\t\tlabels: options?.labels,\n\t\t\tvalidate: options?.validate,\n\t\t})\n\t\tfor (const definition of options?.programs ?? []) this.#manager.add(definition)\n\t}\n\n\tget emitter(): EmitterInterface<RaterEventMap> {\n\t\treturn this.#emitter\n\t}\n\n\tget programs(): ProgramManagerInterface {\n\t\treturn this.#manager\n\t}\n\n\t// Array overload first (AGENTS §9.2) so a list resolves to the batch form.\n\trate(subjects: readonly Subject[]): AggregateResult\n\trate(subject: Subject): SubjectResult\n\trate(subject: Subject | readonly Subject[]): SubjectResult | AggregateResult {\n\t\tthis.#ensureAlive()\n\t\tif (isRecord(subject)) return this.#rateOne(subject)\n\t\treturn this.#rateBatch(subject)\n\t}\n\n\tdestroy(): void {\n\t\tif (this.#destroyed) return\n\t\tthis.#manager.destroy()\n\t\tthis.#engine.destroy()\n\t\tthis.#destroyed = true\n\t\tthis.#emitter.destroy()\n\t}\n\n\t#rateOne(subject: Subject): SubjectResult {\n\t\tassertSubject(subject)\n\t\tconst result = this.#subject(subject)\n\t\tthis.#emitSubject(result)\n\t\treturn result\n\t}\n\n\t#rateBatch(subjects: readonly Subject[]): AggregateResult {\n\t\tfor (const subject of subjects) assertSubject(subject)\n\t\tconst programs = this.#manager.programs()\n\t\tconst fields = aggregateFields(programs)\n\t\tconst sums = aggregateSums(subjects, fields)\n\t\t// Precompute each program's whole-batch sums and groups ONCE. The per-subject\n\t\t// projection pass, the group listing, and the aggregate-determination gates all\n\t\t// reuse these instead of re-summing the whole batch for every subject.\n\t\tconst programSums = new Map<string, Readonly<Record<string, number>>>()\n\t\tconst programGroups = new Map<string, readonly AggregateGroup[]>()\n\t\tfor (const program of programs) {\n\t\t\tconst aggregate = program.definition.aggregate\n\t\t\tif (aggregate === undefined) continue\n\t\t\tprogramSums.set(program.id, aggregateSums(subjects, aggregate.fields))\n\t\t\tprogramGroups.set(program.id, aggregateGroups(subjects, aggregate.fields, aggregate.by))\n\t\t}\n\t\tconst groups = this.#groups(programs, programGroups)\n\t\tconst determinations = this.#aggregateDeterminations(\n\t\t\tprograms,\n\t\t\tprogramSums,\n\t\t\tprogramGroups,\n\t\t\tsubjects.length,\n\t\t)\n\t\tconst subjectsResult = subjects.map((subject) =>\n\t\t\tthis.#subject(\n\t\t\t\tsubject,\n\t\t\t\tthis.#projections(subject, programs, programSums, programGroups, subjects.length),\n\t\t\t),\n\t\t)\n\t\tconst tallies = this.#tallies(subjects, subjectsResult, programs)\n\t\tconst result: AggregateResult = {\n\t\t\tsubjects: subjectsResult,\n\t\t\tdeterminations,\n\t\t\tgroups,\n\t\t\ttallies,\n\t\t\tcount: subjects.length,\n\t\t\tsums,\n\t\t}\n\t\tfor (const rated of subjectsResult) this.#emitSubject(rated)\n\t\tfor (const determination of determinations) {\n\t\t\tif (determination.applied) this.#emitter.emit('determine', determination)\n\t\t}\n\t\tthis.#emitter.emit('aggregate', result)\n\t\treturn result\n\t}\n\n\t#subject(\n\t\tsubject: Subject,\n\t\tprojections?: Readonly<Record<string, Readonly<Record<string, unknown>>>>,\n\t): SubjectResult {\n\t\tconst programs = this.#manager.programs().map((program) => {\n\t\t\tconst projection = projections?.[program.id]\n\t\t\treturn projection === undefined ? program.rate(subject) : program.rate(subject, projection)\n\t\t})\n\t\treturn { subject, programs }\n\t}\n\n\t#emitSubject(result: SubjectResult): void {\n\t\tfor (const program of result.programs) {\n\t\t\tconst determinations = [\n\t\t\t\t...program.determinations,\n\t\t\t\t...program.lines.flatMap((rated) => rated.determinations),\n\t\t\t]\n\t\t\tfor (const determination of determinations) {\n\t\t\t\tif (determination.applied) this.#emitter.emit('determine', determination)\n\t\t\t}\n\t\t\tif (program.decision !== undefined) this.#emitter.emit('decide', program.decision, program)\n\t\t}\n\t\tthis.#emitter.emit('rate', result)\n\t}\n\n\t#groups(\n\t\tprograms: readonly ProgramInterface[],\n\t\tprogramGroups: ReadonlyMap<string, readonly AggregateGroup[]>,\n\t): readonly AggregateGroup[] {\n\t\tconst output: AggregateGroup[] = []\n\t\tfor (const program of programs) {\n\t\t\tconst aggregate = program.definition.aggregate\n\t\t\tif (aggregate?.by !== undefined) output.push(...(programGroups.get(program.id) ?? []))\n\t\t}\n\t\treturn output\n\t}\n\n\t#projections(\n\t\tsubject: Subject,\n\t\tprograms: readonly ProgramInterface[],\n\t\tprogramSums: ReadonlyMap<string, Readonly<Record<string, number>>>,\n\t\tprogramGroups: ReadonlyMap<string, readonly AggregateGroup[]>,\n\t\tcount: number,\n\t): Readonly<Record<string, Readonly<Record<string, unknown>>>> {\n\t\tconst output: Record<string, Readonly<Record<string, unknown>>> = {}\n\t\tfor (const program of programs) {\n\t\t\tconst aggregate = program.definition.aggregate\n\t\t\tif (aggregate === undefined) continue\n\t\t\tconst sums = programSums.get(program.id)\n\t\t\tif (sums === undefined) continue\n\t\t\tconst group = groupFor(subject, programGroups.get(program.id) ?? [], aggregate.by)\n\t\t\toutput[program.id] = aggregateProjection(count, sums, group)\n\t\t}\n\t\treturn output\n\t}\n\n\t#aggregateDeterminations(\n\t\tprograms: readonly ProgramInterface[],\n\t\tprogramSums: ReadonlyMap<string, Readonly<Record<string, number>>>,\n\t\tprogramGroups: ReadonlyMap<string, readonly AggregateGroup[]>,\n\t\tcount: number,\n\t): readonly Determination[] {\n\t\tconst determinations: Determination[] = []\n\t\tfor (const program of programs) {\n\t\t\tconst aggregate = program.definition.aggregate\n\t\t\tif (aggregate?.gates === undefined) continue\n\t\t\tconst sums = programSums.get(program.id)\n\t\t\tif (sums === undefined) continue\n\t\t\tdeterminations.push(\n\t\t\t\t...this.#gate(aggregate.gates, aggregateRecord(count, sums), program.definition.rulings),\n\t\t\t)\n\t\t\tfor (const group of programGroups.get(program.id) ?? []) {\n\t\t\t\tdeterminations.push(\n\t\t\t\t\t...this.#gate(\n\t\t\t\t\t\taggregate.gates,\n\t\t\t\t\t\taggregateRecord(group.count, group.sums, group),\n\t\t\t\t\t\tprogram.definition.rulings,\n\t\t\t\t\t),\n\t\t\t\t)\n\t\t\t}\n\t\t}\n\t\treturn determinations\n\t}\n\n\t#gate(\n\t\tdefinition: LogicalDefinition,\n\t\trecord: Readonly<Record<string, unknown>>,\n\t\trulings: Readonly<Record<string, Ruling>> | undefined,\n\t): readonly Determination[] {\n\t\tconst result = this.#reasonLogical(record, definition)\n\t\treturn rulesToDeterminations(definition, result, rulings, record, undefined, this.#evaluator)\n\t}\n\n\t#tallies(\n\t\tsubjects: readonly Subject[],\n\t\tresults: readonly SubjectResult[],\n\t\tprograms: readonly ProgramInterface[],\n\t): Readonly<Record<string, Readonly<Record<string, Tally>>>> {\n\t\tconst output: Record<string, Readonly<Record<string, Tally>>> = {}\n\t\tfor (const program of programs) {\n\t\t\tlet tallies = emptyTallies(program.definition.aggregate?.fields ?? [])\n\t\t\tfor (let index = 0; index < results.length; index += 1) {\n\t\t\t\tconst subject = subjects[index]\n\t\t\t\tconst rated = results[index]?.programs.find((entry) => entry.id === program.id)\n\t\t\t\tif (subject !== undefined && rated !== undefined) {\n\t\t\t\t\ttallies = tallySubject(\n\t\t\t\t\t\ttallies,\n\t\t\t\t\t\trated.status,\n\t\t\t\t\t\tsubject,\n\t\t\t\t\t\tprogram.definition.aggregate?.fields ?? [],\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t}\n\t\t\toutput[program.id] = tallies\n\t\t}\n\t\treturn output\n\t}\n\n\t// Narrows the engine's tagged ReasonResult union down to a LogicalResult for\n\t// a definition the engine is guaranteed (by dispatch-by-reasoning) to resolve\n\t// as such — a defensive, total fallback rather than an assertion.\n\t#reasonLogical(subject: Subject, definition: LogicalDefinition): LogicalResult {\n\t\tconst result = this.#engine.reason(subject, definition)\n\t\tif (result.reasoning === 'logical') return result\n\t\tconst failure = buildErrorResult(definition, `Expected logical result, got ${result.reasoning}`)\n\t\tconst errors = [...result.errors, ...failure.errors]\n\t\tif (failure.reasoning === 'logical') return { ...failure, errors }\n\t\treturn {\n\t\t\treasoning: 'logical',\n\t\t\tconclusion: false,\n\t\t\trules: [],\n\t\t\tcount: 0,\n\t\t\tsuccess: false,\n\t\t\ttrace: [],\n\t\t\terrors,\n\t\t}\n\t}\n\n\t#ensureAlive(): void {\n\t\tif (this.#destroyed) throw new RaterError('DESTROYED', 'Rater has been destroyed')\n\t}\n}\n","import type { FieldPath } from '@orkestrel/contract'\nimport type { LogicalDefinition, QuantitativeDefinition, ReasonInterface } from '@orkestrel/reason'\nimport type {\n\tAggregateDefinition,\n\tEffect,\n\tLineDefinition,\n\tNotice,\n\tPassDefinition,\n\tProgramDefinition,\n\tProgramInterface,\n\tProgramManagerInterface,\n\tProgramManagerOptions,\n\tProgramOptions,\n\tRaterInterface,\n\tRaterOptions,\n\tRuling,\n} from './types.js'\nimport { isRecord } from '@orkestrel/contract'\nimport { Program } from './programs/Program.js'\nimport { ProgramManager } from './programs/ProgramManager.js'\nimport { Rater } from './Rater.js'\nimport { RaterError } from './errors.js'\nimport { isProgramDefinition } from './validators.js'\n\n/**\n * Create a rating orchestrator.\n *\n * @param options - Optional total handler, seed programs, labels, validation policy, and emitter hooks\n * @returns A {@link RaterInterface}\n *\n * @example\n * ```ts\n * import { createRater } from '@orkestrel/rater'\n *\n * const rater = createRater()\n * rater.destroy()\n * ```\n */\nexport function createRater(options?: RaterOptions): RaterInterface {\n\treturn new Rater(options)\n}\n\n/**\n * Create and validate one compiled program over a shared reasoning engine.\n *\n * @param definition - The authored program definition\n * @param reason - The shared reasoning engine, injected (never owned or destroyed by the program)\n * @param options - Optional total handler and labels\n * @returns A {@link ProgramInterface}\n * @throws {@link RaterError} `'DEFINITION'` when the definition fails {@link isProgramDefinition}\n *\n * @example\n * ```ts\n * import { createLogicalReasoner, createQuantitativeReasoner, createReason } from '@orkestrel/reason'\n * import { createProgram, programDefinition } from '@orkestrel/rater'\n *\n * const reason = createReason({ reasoners: [createQuantitativeReasoner(), createLogicalReasoner()], bail: false })\n * const program = createProgram(programDefinition('p1', 'Program', []), reason)\n * ```\n */\nexport function createProgram(\n\tdefinition: ProgramDefinition,\n\treason: ReasonInterface,\n\toptions?: ProgramOptions,\n): ProgramInterface {\n\tconst id = isRecord(definition) ? definition.id : undefined\n\tif (!isProgramDefinition(definition)) {\n\t\tthrow new RaterError('DEFINITION', 'Program definition failed validation', { program: id })\n\t}\n\treturn new Program(definition, reason, options)\n}\n\n/**\n * Create an ordered manager over compiled programs (AGENTS §9), built over an\n * injected, shared reasoning engine.\n *\n * @param reason - The shared reasoning engine, injected into every compiled program\n * @param options - Optional total handler, labels, validation policy, and emitter hooks\n * @returns A {@link ProgramManagerInterface}\n *\n * @example\n * ```ts\n * import { createLogicalReasoner, createQuantitativeReasoner, createReason } from '@orkestrel/reason'\n * import { createProgramManager, programDefinition } from '@orkestrel/rater'\n *\n * const reason = createReason({ reasoners: [createQuantitativeReasoner(), createLogicalReasoner()], bail: false })\n * const manager = createProgramManager(reason)\n * manager.add(programDefinition('p1', 'Program', []))\n * manager.destroy()\n * ```\n */\nexport function createProgramManager(\n\treason: ReasonInterface,\n\toptions?: ProgramManagerOptions,\n): ProgramManagerInterface {\n\treturn new ProgramManager(reason, options)\n}\n\n/**\n * Build a {@link ProgramDefinition}.\n *\n * @param id - The program id\n * @param name - The display name\n * @param lines - The program's rateable lines\n * @param overrides - Optional {@link ProgramDefinition} fields merged over the defaults\n * @returns A fresh program definition\n *\n * @example\n * ```ts\n * import { programDefinition } from '@orkestrel/rater'\n *\n * programDefinition('p1', 'Program', [])\n * ```\n */\nexport function programDefinition(\n\tid: string,\n\tname: string,\n\tlines: readonly LineDefinition[],\n\toverrides?: Partial<Omit<ProgramDefinition, 'id' | 'name' | 'lines'>>,\n): ProgramDefinition {\n\treturn { id, name, lines, ...overrides }\n}\n\n/**\n * Build a {@link LineDefinition}.\n *\n * @param id - The line id\n * @param name - The display name\n * @param rate - The line's quantitative rating definition\n * @param overrides - Optional {@link LineDefinition} fields merged over the defaults\n * @returns A fresh line definition\n *\n * @example\n * ```ts\n * import { quantitativeDefinition } from '@orkestrel/reason'\n * import { lineDefinition } from '@orkestrel/rater'\n *\n * lineDefinition('base', 'Base Amount', quantitativeDefinition('base', 'Base', []))\n * ```\n */\nexport function lineDefinition(\n\tid: string,\n\tname: string,\n\trate: QuantitativeDefinition,\n\toverrides?: Partial<Omit<LineDefinition, 'id' | 'name' | 'rate'>>,\n): LineDefinition {\n\treturn { id, name, rate, ...overrides }\n}\n\n/**\n * Build a {@link PassDefinition}.\n *\n * @param definition - The pass's logical or quantitative definition\n * @param line - The line id this pass is scoped to, when it is line-scoped\n * @returns A fresh pass definition\n *\n * @example\n * ```ts\n * import { quantitativeDefinition } from '@orkestrel/reason'\n * import { passDefinition } from '@orkestrel/rater'\n *\n * passDefinition(quantitativeDefinition('surcharge', 'Surcharge', []))\n * ```\n */\nexport function passDefinition(\n\tdefinition: LogicalDefinition | QuantitativeDefinition,\n\tline?: string,\n): PassDefinition {\n\treturn { definition, ...(line === undefined ? {} : { line }) }\n}\n\n/**\n * Build a {@link Ruling}.\n *\n * @param effect - The determination effect a fired rule routes to\n * @param line - The line id this ruling is scoped to, when it is line-scoped\n * @param message - An interpolated message template, when the ruling carries one\n * @returns A fresh ruling\n *\n * @example\n * ```ts\n * import { rulingDefinition } from '@orkestrel/rater'\n *\n * rulingDefinition('restriction', 'base', 'Amount exceeds {{limit}}')\n * ```\n */\nexport function rulingDefinition(effect: Effect, line?: string, message?: string): Ruling {\n\treturn {\n\t\teffect,\n\t\t...(line === undefined ? {} : { line }),\n\t\t...(message === undefined ? {} : { message }),\n\t}\n}\n\n/**\n * Build a {@link Notice}.\n *\n * @param id - The notice id\n * @param message - An interpolated message template\n * @param line - The line id this notice is scoped to, when it is line-scoped\n * @returns A fresh notice\n *\n * @example\n * ```ts\n * import { noticeDefinition } from '@orkestrel/rater'\n *\n * noticeDefinition('n1', 'Rated on {{date}}')\n * ```\n */\nexport function noticeDefinition(id: string, message: string, line?: string): Notice {\n\treturn { id, message, ...(line === undefined ? {} : { line }) }\n}\n\n/**\n * Build an {@link AggregateDefinition}.\n *\n * @param fields - The batch fields to sum\n * @param by - The partition key field, when the aggregate is partitioned\n * @param gates - The logical definition gating aggregate determinations, when present\n * @returns A fresh aggregate definition\n *\n * @example\n * ```ts\n * import { aggregateDefinition } from '@orkestrel/rater'\n *\n * aggregateDefinition(['amount'])\n * ```\n */\nexport function aggregateDefinition(\n\tfields: readonly FieldPath[],\n\tby?: FieldPath,\n\tgates?: LogicalDefinition,\n): AggregateDefinition {\n\treturn { fields, ...(by === undefined ? {} : { by }), ...(gates === undefined ? {} : { gates }) }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":["#emitter","#engine","#owned","#total","#labels","#ensureAlive","#normalize","#rateLine","#destroyed","#reasonQuantitative"],"sources":["../../../src/core/errors.ts","../../../src/core/validators.ts","../../../src/core/helpers.ts","../../../src/core/Rater.ts","../../../src/core/factories.ts"],"sourcesContent":["import type { RaterErrorCode } from './types.js'\n\n/**\n * A coded programmer error thrown by the rating layer.\n *\n * @remarks\n * `DEFINITION` — the `rate` input failed both the array-of-lines and rating\n * definition validation. `MISMATCH` — a rated subject is not a record.\n * `DESTROYED` — use of a destroyed entity.\n *\n * @example\n * ```ts\n * import { RaterError } from '@orkestrel/rater'\n *\n * throw new RaterError('MISMATCH', 'Subject must be a record')\n * ```\n */\nexport class RaterError extends Error {\n\treadonly code: RaterErrorCode\n\treadonly context?: Readonly<Record<string, unknown>>\n\n\tconstructor(code: RaterErrorCode, message: string, context?: Readonly<Record<string, unknown>>) {\n\t\tsuper(message)\n\t\tthis.name = 'RaterError'\n\t\tthis.code = code\n\t\tthis.context = context\n\t}\n}\n\n/**\n * Narrow a caught value to a {@link RaterError}.\n *\n * @param value - The caught value to test\n * @returns `true` when `value` is a `RaterError`\n *\n * @example\n * ```ts\n * import { isRaterError, RaterError } from '@orkestrel/rater'\n *\n * try {\n * \tthrow new RaterError('DESTROYED', 'Rater has been destroyed')\n * } catch (error) {\n * \tif (isRaterError(error)) error.code // 'DESTROYED'\n * }\n * ```\n */\nexport function isRaterError(value: unknown): value is RaterError {\n\treturn value instanceof RaterError\n}\n","import type { Guard } from '@orkestrel/contract'\nimport type { LineDefinition, RatingDefinition, Stage } from './types.js'\nimport { arrayOf, isJSONValue, isString, literalOf, recordOf } from '@orkestrel/contract'\nimport { isQuantitativeDefinition } from '@orkestrel/reason'\n\n/**\n * Determine whether a value is a {@link Stage} literal.\n *\n * @param value - The value to test\n * @returns `true` when `value` is `'factor'`, `'group'`, or `'total'`\n *\n * @example\n * ```ts\n * import { isStage } from '@orkestrel/rater'\n *\n * isStage('group') // true\n * isStage('step') // false\n * ```\n */\nexport const isStage: Guard<Stage> = literalOf('factor', 'group', 'total')\n\n/**\n * Determine whether a value is an exact {@link LineDefinition} record.\n *\n * @remarks\n * Total guard (AGENTS §14): adversarial input (cycles, hostile prototypes)\n * returns `false`, never throws. The record shape is EXACT — an extra key fails.\n *\n * @param value - The value to test\n * @returns `true` when `value` is a `LineDefinition`\n *\n * @example\n * ```ts\n * import { quantitativeDefinition } from '@orkestrel/reason'\n * import { isLineDefinition } from '@orkestrel/rater'\n *\n * isLineDefinition({ id: 'base', name: 'Base', rate: quantitativeDefinition('base', 'Base', []) }) // true\n * ```\n */\nexport function isLineDefinition(value: unknown): value is LineDefinition {\n\treturn recordOf(\n\t\t{\n\t\t\tid: isString,\n\t\t\tname: isString,\n\t\t\tdescription: isString,\n\t\t\trate: isQuantitativeDefinition,\n\t\t\tmetadata: isJSONValue,\n\t\t},\n\t\t['description', 'metadata'],\n\t)(value)\n}\n\n/**\n * Determine whether a value is an exact {@link RatingDefinition} record.\n *\n * @remarks\n * Total guard (AGENTS §14): adversarial input (cycles, hostile prototypes)\n * returns `false`, never throws. The record shape is EXACT — an extra key fails.\n *\n * @param value - The value to test\n * @returns `true` when `value` is a `RatingDefinition`\n *\n * @example\n * ```ts\n * import { isRatingDefinition } from '@orkestrel/rater'\n *\n * isRatingDefinition({ id: 'r1', name: 'Rating', lines: [] }) // true\n * ```\n */\nexport function isRatingDefinition(value: unknown): value is RatingDefinition {\n\treturn recordOf(\n\t\t{\n\t\t\tid: isString,\n\t\t\tname: isString,\n\t\t\tdescription: isString,\n\t\t\tlines: arrayOf(isLineDefinition),\n\t\t\tmetadata: isJSONValue,\n\t\t},\n\t\t['description', 'metadata'],\n\t)(value)\n}\n","import type {\n\tCheck,\n\tCheckResult,\n\tFactor,\n\tFactorResult,\n\tGroupResult,\n\tQuantitativeDefinition,\n\tQuantitativeResult,\n} from '@orkestrel/reason'\nimport type {\n\tEvidence,\n\tLineDefinition,\n\tLineResult,\n\tStage,\n\tStep,\n\tWorksheet,\n\tWorksheetFactor,\n\tWorksheetGroup,\n} from './types.js'\nimport { formatField } from '@orkestrel/reason'\n\n/**\n * Build an {@link Evidence} row from an evaluated {@link Check}.\n *\n * @param check - The authored check\n * @param actual - The resolved subject value\n * @param met - Whether the check was met (absent when not yet evaluated)\n * @param labels - Optional field-to-label overrides, keyed by dot-joined field\n * @returns A fresh evidence row\n *\n * @example\n * ```ts\n * import { check } from '@orkestrel/reason'\n * import { evidenceCheck } from '@orkestrel/rater'\n *\n * const entry = check('age', 'above', 18)\n * evidenceCheck(entry, 25, true) // { field: 'age', comparison: 'above', expected: 18, actual: 25, met: true }\n * ```\n */\nexport function evidenceCheck(\n\tcheck: Check,\n\tactual: unknown,\n\tmet: boolean | undefined,\n\tlabels?: Readonly<Record<string, string>>,\n): Evidence {\n\tconst field = formatField(check.field)\n\treturn {\n\t\tfield: check.field,\n\t\t...(labels?.[field] === undefined ? {} : { label: labels[field] }),\n\t\tcomparison: check.operator,\n\t\texpected: check.value,\n\t\tactual,\n\t\t...(met === undefined ? {} : { met }),\n\t}\n}\n\n/**\n * Build evidence rows from a quantitative factor's authored checks and\n * evaluated check results.\n *\n * @param checks - The factor's authored checks\n * @param results - The corresponding {@link CheckResult}s, in the same order\n * @param labels - Optional field-to-label overrides, keyed by dot-joined field\n * @returns A fresh list of evidence rows, one per authored check\n *\n * @example\n * ```ts\n * import { check } from '@orkestrel/reason'\n * import { checkEvidence } from '@orkestrel/rater'\n *\n * const checks = [check('age', 'above', 18)]\n * checkEvidence(checks, [{ field: 'age', met: true, actual: 25 }])\n * ```\n */\nexport function checkEvidence(\n\tchecks: readonly Check[] | undefined,\n\tresults: readonly CheckResult[] | undefined,\n\tlabels?: Readonly<Record<string, string>>,\n): readonly Evidence[] {\n\treturn (checks ?? []).map((check, index) => {\n\t\tconst result = results?.[index]\n\t\treturn evidenceCheck(check, result?.actual, result?.met, labels)\n\t})\n}\n\n/**\n * Join one authored quantitative factor to its evaluated {@link FactorResult}.\n *\n * @param definition - The authored factor\n * @param results - The group's factor results\n * @param labels - Optional field-to-label overrides, keyed by dot-joined field\n * @returns A fresh worksheet factor\n *\n * @example\n * ```ts\n * import { fieldFactor } from '@orkestrel/reason'\n * import { worksheetFactor } from '@orkestrel/rater'\n *\n * const factor = fieldFactor('seats', 'seats')\n * worksheetFactor(factor, [{ id: 'seats', applied: true, value: 10 }])\n * ```\n */\nexport function worksheetFactor(\n\tdefinition: Factor,\n\tresults: readonly FactorResult[],\n\tlabels?: Readonly<Record<string, string>>,\n): WorksheetFactor {\n\tconst result = results.find((entry) => entry.id === definition.id)\n\treturn {\n\t\tid: definition.id,\n\t\tname: definition.name,\n\t\t...(definition.description === undefined ? {} : { description: definition.description }),\n\t\tapplied: result?.applied ?? false,\n\t\t...(result?.value === undefined ? {} : { value: result.value }),\n\t\tevidence: checkEvidence(definition.checks, result?.checks, labels),\n\t}\n}\n\n/**\n * Join one authored quantitative group to its evaluated {@link GroupResult}.\n *\n * @param definition - The authored group\n * @param results - The definition's group results\n * @param labels - Optional field-to-label overrides, keyed by dot-joined field\n * @returns A fresh worksheet group\n *\n * @example\n * ```ts\n * import { factorGroup, staticFactor } from '@orkestrel/reason'\n * import { worksheetGroup } from '@orkestrel/rater'\n *\n * const group = factorGroup('charge', 'sum', [staticFactor('base', 100)])\n * worksheetGroup(group, [{ id: 'charge', applied: true, value: 100, factors: [] }])\n * ```\n */\nexport function worksheetGroup(\n\tdefinition: QuantitativeDefinition['groups'][number],\n\tresults: readonly GroupResult[],\n\tlabels?: Readonly<Record<string, string>>,\n): WorksheetGroup {\n\tconst result = results.find((entry) => entry.id === definition.id)\n\treturn {\n\t\tid: definition.id,\n\t\tname: definition.name,\n\t\t...(definition.description === undefined ? {} : { description: definition.description }),\n\t\tapplied: result?.applied ?? false,\n\t\tvalue: result?.value ?? 0,\n\t\tfactors: definition.factors.map((factor) =>\n\t\t\tworksheetFactor(factor, result?.factors ?? [], labels),\n\t\t),\n\t}\n}\n\n/**\n * Build one display-neutral {@link Step} row.\n *\n * @param stage - The step's stage\n * @param id - The stage's authored id, when it has one\n * @param name - The stage's authored name, when it has one\n * @param value - The stage's resolved value\n * @param expression - A display-neutral expression string\n * @returns A fresh step\n *\n * @example\n * ```ts\n * import { worksheetStep } from '@orkestrel/rater'\n *\n * worksheetStep('total', 'quote', 'Quote', 110, 'sum = 110')\n * // { stage: 'total', id: 'quote', name: 'Quote', value: 110, expression: 'sum = 110' }\n * ```\n */\nexport function worksheetStep(\n\tstage: Stage,\n\tid: string | undefined,\n\tname: string | undefined,\n\tvalue: number,\n\texpression: string,\n): Step {\n\treturn {\n\t\tstage,\n\t\t...(id === undefined ? {} : { id }),\n\t\t...(name === undefined ? {} : { name }),\n\t\tvalue,\n\t\texpression,\n\t}\n}\n\n/**\n * Build the ordered {@link Step} rows for a resolved {@link Worksheet}.\n *\n * @param definition - The authored quantitative definition\n * @param result - The evaluated quantitative result\n * @param groups - The definition's already-joined worksheet groups\n * @returns A fresh, ordered list of steps: applied factors, then groups, then the total\n *\n * @example\n * ```ts\n * import { createQuantitativeReasoner, createReason, factorGroup, quantitativeDefinition, staticFactor } from '@orkestrel/reason'\n * import { worksheetGroup, worksheetSteps } from '@orkestrel/rater'\n *\n * const definition = quantitativeDefinition('base', 'Base', [factorGroup('g', 'sum', [staticFactor('flat', 100)])])\n * const result = createReason({ reasoners: [createQuantitativeReasoner()] }).reason({}, definition)\n * if (result.reasoning === 'quantitative') {\n * \tworksheetSteps(definition, result, definition.groups.map((group) => worksheetGroup(group, result.groups)))\n * }\n * ```\n */\nexport function worksheetSteps(\n\tdefinition: QuantitativeDefinition,\n\tresult: QuantitativeResult,\n\tgroups: readonly WorksheetGroup[],\n): readonly Step[] {\n\tconst output: Step[] = []\n\tfor (const group of groups) {\n\t\tfor (const factor of group.factors) {\n\t\t\tif (factor.applied && factor.value !== undefined) {\n\t\t\t\toutput.push(\n\t\t\t\t\tworksheetStep(\n\t\t\t\t\t\t'factor',\n\t\t\t\t\t\tfactor.id,\n\t\t\t\t\t\tfactor.name,\n\t\t\t\t\t\tfactor.value,\n\t\t\t\t\t\t`${factor.id} = ${factor.value}`,\n\t\t\t\t\t),\n\t\t\t\t)\n\t\t\t}\n\t\t}\n\t\toutput.push(\n\t\t\tworksheetStep('group', group.id, group.name, group.value, `${group.id} = ${group.value}`),\n\t\t)\n\t}\n\toutput.push(\n\t\tworksheetStep(\n\t\t\t'total',\n\t\t\tdefinition.id,\n\t\t\tdefinition.name,\n\t\t\tresult.value,\n\t\t\t`${definition.aggregation} = ${result.value}`,\n\t\t),\n\t)\n\treturn output\n}\n\n/**\n * Join a {@link QuantitativeDefinition} and its {@link QuantitativeResult} into\n * a {@link Worksheet} — the rating audit trail.\n *\n * @param definition - The authored quantitative definition\n * @param result - The evaluated quantitative result\n * @param labels - Optional field-to-label overrides, keyed by dot-joined field\n * @returns A fresh worksheet\n *\n * @example\n * ```ts\n * import { createQuantitativeReasoner, createReason, factorGroup, quantitativeDefinition, staticFactor } from '@orkestrel/reason'\n * import { resultsWorksheet } from '@orkestrel/rater'\n *\n * const definition = quantitativeDefinition('base', 'Base', [factorGroup('g', 'sum', [staticFactor('flat', 100)])])\n * const result = createReason({ reasoners: [createQuantitativeReasoner()] }).reason({}, definition)\n * if (result.reasoning === 'quantitative') resultsWorksheet(definition, result) // the full audit trail\n * ```\n */\nexport function resultsWorksheet(\n\tdefinition: QuantitativeDefinition,\n\tresult: QuantitativeResult,\n\tlabels?: Readonly<Record<string, string>>,\n): Worksheet {\n\tconst groups = definition.groups.map((group) => worksheetGroup(group, result.groups, labels))\n\treturn {\n\t\tid: definition.id,\n\t\tname: definition.name,\n\t\taggregation: definition.aggregation,\n\t\t...(definition.precision === undefined ? {} : { precision: definition.precision }),\n\t\tvalue: result.value,\n\t\tgroups,\n\t\tsteps: worksheetSteps(definition, result, groups),\n\t\ttrace: result.trace,\n\t\terrors: result.errors,\n\t\tsuccess: result.success,\n\t}\n}\n\n/**\n * Build a rated {@link LineResult} from a line's evaluated\n * {@link QuantitativeResult}.\n *\n * @param definition - The authored line definition\n * @param result - The evaluated quantitative result\n * @param labels - Optional field-to-label overrides, keyed by dot-joined field\n * @returns A fresh line result\n *\n * @example\n * ```ts\n * import { createQuantitativeReasoner, createReason, quantitativeDefinition } from '@orkestrel/reason'\n * import { lineDefinition, ratedLine } from '@orkestrel/rater'\n *\n * const line = lineDefinition('base', 'Base', quantitativeDefinition('base', 'Base', []))\n * const result = createReason({ reasoners: [createQuantitativeReasoner()] }).reason({}, line.rate)\n * if (result.reasoning === 'quantitative') ratedLine(line, result) // amount present only when success\n * ```\n */\nexport function ratedLine(\n\tdefinition: LineDefinition,\n\tresult: QuantitativeResult,\n\tlabels?: Readonly<Record<string, string>>,\n): LineResult {\n\treturn {\n\t\tid: definition.id,\n\t\tname: definition.name,\n\t\t...(result.success ? { amount: result.value } : {}),\n\t\tworksheet: resultsWorksheet(definition.rate, result, labels),\n\t\tsuccess: result.success,\n\t}\n}\n\n/**\n * Sum defined line amounts.\n *\n * @param lines - The rated lines\n * @returns The sum of every line's `amount`, or `undefined` when no line has one\n *\n * @example\n * ```ts\n * import { createQuantitativeReasoner, createReason, factorGroup, quantitativeDefinition, staticFactor } from '@orkestrel/reason'\n * import { lineDefinition, ratedLine, sumAmounts } from '@orkestrel/rater'\n *\n * const rate = quantitativeDefinition('base', 'Base', [factorGroup('g', 'sum', [staticFactor('flat', 100)])])\n * const line = lineDefinition('base', 'Base', rate)\n * const result = createReason({ reasoners: [createQuantitativeReasoner()] }).reason({}, rate)\n * if (result.reasoning === 'quantitative') sumAmounts([ratedLine(line, result)]) // 100\n * ```\n */\nexport function sumAmounts(lines: readonly LineResult[]): number | undefined {\n\tlet total = 0\n\tlet count = 0\n\tfor (const rated of lines) {\n\t\tif (rated.amount !== undefined) {\n\t\t\ttotal += rated.amount\n\t\t\tcount += 1\n\t\t}\n\t}\n\treturn count === 0 ? undefined : total\n}\n","import type { EmitterInterface } from '@orkestrel/emitter'\nimport type {\n\tQuantitativeDefinition,\n\tQuantitativeResult,\n\tReasonInterface,\n\tSubject,\n} from '@orkestrel/reason'\nimport type {\n\tLineDefinition,\n\tLineResult,\n\tRaterEventMap,\n\tRaterInterface,\n\tRaterOptions,\n\tRatingDefinition,\n\tRatingResult,\n\tTotalHandler,\n} from './types.js'\nimport { Emitter } from '@orkestrel/emitter'\nimport { arrayOf, isArray } from '@orkestrel/contract'\nimport {\n\tbuildErrorResult,\n\tcreateQuantitativeReasoner,\n\tcreateReason,\n\tisSubject,\n} from '@orkestrel/reason'\nimport { RaterError } from './errors.js'\nimport { ratedLine, sumAmounts } from './helpers.js'\nimport { isLineDefinition, isRatingDefinition } from './validators.js'\n\n/**\n * The rating orchestrator — owns (or receives) the shared quantitative\n * reasoning engine and projects results into the rating domain vocabulary.\n *\n * @remarks\n * When no `engine` is injected, `Rater` builds and OWNS its own\n * quantitative-only engine (`bail: false`) — Rater performs NO evaluation\n * arithmetic of its own; it only orchestrates and projects. The\n * array-of-lines `rate` overload is declared FIRST (AGENTS §9.2). `destroy()`\n * destroys an OWNED engine, then the emitter LAST (AGENTS §13); an INJECTED\n * engine is never destroyed. Afterwards every other method throws\n * {@link RaterError} `'DESTROYED'`.\n *\n * @example\n * ```ts\n * import { Rater } from '@orkestrel/rater'\n *\n * const rater = new Rater()\n * rater.rate([], { id: 'subject-1' }) // { lines: [], success: true }\n * rater.destroy()\n * ```\n */\nexport class Rater implements RaterInterface {\n\treadonly #emitter: Emitter<RaterEventMap>\n\treadonly #engine: ReasonInterface\n\treadonly #owned: boolean\n\treadonly #total: TotalHandler | undefined\n\treadonly #labels: Readonly<Record<string, string>> | undefined\n\t#destroyed = false\n\n\tconstructor(options?: RaterOptions) {\n\t\tthis.#emitter = new Emitter<RaterEventMap>({ on: options?.on, error: options?.error })\n\t\tif (options?.engine === undefined) {\n\t\t\tthis.#engine = createReason({ reasoners: [createQuantitativeReasoner()], bail: false })\n\t\t\tthis.#owned = true\n\t\t} else {\n\t\t\tthis.#engine = options.engine\n\t\t\tthis.#owned = false\n\t\t}\n\t\tthis.#total = options?.total\n\t\tthis.#labels = options?.labels\n\t}\n\n\tget emitter(): EmitterInterface<RaterEventMap> {\n\t\treturn this.#emitter\n\t}\n\n\t// Array overload first (AGENTS §9.2) so a plain line list resolves to that form.\n\trate(lines: readonly LineDefinition[], subject: Subject): RatingResult\n\trate(definition: RatingDefinition, subject: Subject): RatingResult\n\trate(input: readonly LineDefinition[] | RatingDefinition, subject: Subject): RatingResult {\n\t\tthis.#ensureAlive()\n\t\tconst lines = this.#normalize(input)\n\t\tif (!isSubject(subject)) throw new RaterError('MISMATCH', 'Subject must be a record')\n\t\tconst results = lines.map((line) => this.#rateLine(line, subject))\n\t\tconst total = (this.#total ?? sumAmounts)(results)\n\t\tconst success = results.every((entry) => entry.success)\n\t\tconst result: RatingResult = {\n\t\t\tlines: results,\n\t\t\t...(total === undefined ? {} : { total }),\n\t\t\tsuccess,\n\t\t}\n\t\tthis.#emitter.emit('rate', subject, result)\n\t\treturn result\n\t}\n\n\tdestroy(): void {\n\t\tif (this.#destroyed) return\n\t\tthis.#destroyed = true\n\t\tif (this.#owned) this.#engine.destroy()\n\t\tthis.#emitter.destroy()\n\t}\n\n\t#normalize(input: readonly LineDefinition[] | RatingDefinition): readonly LineDefinition[] {\n\t\tif (arrayOf(isLineDefinition)(input)) return input\n\t\tif (isRatingDefinition(input)) return input.lines\n\t\tthrow new RaterError(\n\t\t\t'DEFINITION',\n\t\t\t'Rating input must be an array of line definitions or a rating definition',\n\t\t)\n\t}\n\n\t#rateLine(line: LineDefinition, subject: Subject): LineResult {\n\t\tconst result = this.#reasonQuantitative(subject, line.rate)\n\t\treturn ratedLine(line, result, this.#labels)\n\t}\n\n\t// Narrows the engine's tagged ReasonResult union down to a QuantitativeResult\n\t// for a definition the engine is guaranteed (by dispatch-by-reasoning, given\n\t// only the quantitative reasoner is ever registered) to resolve as such — a\n\t// defensive, total fallback rather than an assertion. A nonconforming\n\t// injected engine result missing (or with a non-array) `errors` contributes\n\t// no errors from that side rather than throwing.\n\t#reasonQuantitative(subject: Subject, definition: QuantitativeDefinition): QuantitativeResult {\n\t\tconst result = this.#engine.reason(subject, definition)\n\t\tif (result.reasoning === 'quantitative') return result\n\t\tconst failure = buildErrorResult(\n\t\t\tdefinition,\n\t\t\t`Expected quantitative result, got ${result.reasoning}`,\n\t\t)\n\t\tconst errors = [...(isArray<string>(result.errors) ? result.errors : []), ...failure.errors]\n\t\tif (failure.reasoning === 'quantitative') return { ...failure, errors }\n\t\treturn {\n\t\t\treasoning: 'quantitative',\n\t\t\tvalue: 0,\n\t\t\tgroups: [],\n\t\t\tcount: 0,\n\t\t\tsuccess: false,\n\t\t\ttrace: [],\n\t\t\terrors,\n\t\t}\n\t}\n\n\t#ensureAlive(): void {\n\t\tif (this.#destroyed) throw new RaterError('DESTROYED', 'Rater has been destroyed')\n\t}\n}\n","import type { QuantitativeDefinition } from '@orkestrel/reason'\nimport type { LineDefinition, RatingDefinition, RaterInterface, RaterOptions } from './types.js'\nimport { Rater } from './Rater.js'\n\n/**\n * Create a rating orchestrator.\n *\n * @param options - Optional total handler, labels, injected engine, and emitter hooks\n * @returns A {@link RaterInterface}\n *\n * @example\n * ```ts\n * import { createRater } from '@orkestrel/rater'\n *\n * const rater = createRater()\n * rater.destroy()\n * ```\n */\nexport function createRater(options?: RaterOptions): RaterInterface {\n\treturn new Rater(options)\n}\n\n/**\n * Build a {@link LineDefinition}.\n *\n * @param id - The line id\n * @param name - The display name\n * @param rate - The line's quantitative rating definition\n * @param overrides - Optional {@link LineDefinition} fields merged over the defaults\n * @returns A fresh line definition\n *\n * @example\n * ```ts\n * import { quantitativeDefinition } from '@orkestrel/reason'\n * import { lineDefinition } from '@orkestrel/rater'\n *\n * lineDefinition('base', 'Base Amount', quantitativeDefinition('base', 'Base', []))\n * ```\n */\nexport function lineDefinition(\n\tid: string,\n\tname: string,\n\trate: QuantitativeDefinition,\n\toverrides?: Partial<Omit<LineDefinition, 'id' | 'name' | 'rate'>>,\n): LineDefinition {\n\treturn { id, name, rate, ...overrides }\n}\n\n/**\n * Build a {@link RatingDefinition}.\n *\n * @param id - The rating id\n * @param name - The display name\n * @param lines - The rating's ordered lines\n * @param overrides - Optional {@link RatingDefinition} fields merged over the defaults\n * @returns A fresh rating definition\n *\n * @example\n * ```ts\n * import { lineDefinition, ratingDefinition } from '@orkestrel/rater'\n * import { quantitativeDefinition } from '@orkestrel/reason'\n *\n * ratingDefinition('r1', 'Rating', [\n * \tlineDefinition('base', 'Base Amount', quantitativeDefinition('base', 'Base', [])),\n * ])\n * ```\n */\nexport function ratingDefinition(\n\tid: string,\n\tname: string,\n\tlines: readonly LineDefinition[],\n\toverrides?: Partial<Omit<RatingDefinition, 'id' | 'name' | 'lines'>>,\n): RatingDefinition {\n\treturn { id, name, lines, ...overrides }\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@orkestrel/rater",
|
|
3
|
-
"version": "0.0.
|
|
4
|
-
"description": "A typed rating
|
|
3
|
+
"version": "0.0.3",
|
|
4
|
+
"description": "A typed quantitative rating layer: authored lines are rated against a subject to produce per-line amounts, worksheets, and a derived total. Part of the @orkestrel line.",
|
|
5
5
|
"keywords": [
|
|
6
|
-
"
|
|
7
|
-
"eligibility",
|
|
6
|
+
"quantitative",
|
|
8
7
|
"rater",
|
|
9
8
|
"rating",
|
|
10
9
|
"rules-engine",
|
|
11
|
-
"typescript"
|
|
10
|
+
"typescript",
|
|
11
|
+
"worksheet"
|
|
12
12
|
],
|
|
13
13
|
"homepage": "https://github.com/orkestrel/rater#readme",
|
|
14
14
|
"bugs": {
|