@rulvar/core 1.236.0 → 1.237.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -1679,6 +1679,46 @@ interface TerminalEnvelope {
1679
1679
  /** Agents admitted over the run's lifetime, resume seed included. */
1680
1680
  agentsSpawned: number;
1681
1681
  /**
1682
+ * Whether the artifact this terminal carries passed the declared
1683
+ * finish contract (RV2506), mirrored onto the envelope since RV3304:
1684
+ * the 2026-08-12 comparison run settled ok/complete over a retained
1685
+ * contradiction, and neither the HTTP response nor the persisted
1686
+ * rebuild could say whether anything ever judged the deliverable.
1687
+ * Absent when no contract judged anything; absence means NOT
1688
+ * RECORDED, never "accepted".
1689
+ */
1690
+ deliverableAccepted?: boolean;
1691
+ /**
1692
+ * Whether this terminal carries a deliverable to read at all
1693
+ * (RV2506); same mirror and posture. Distinct from
1694
+ * `deliverableAccepted`: an unjudged artifact still EXISTS, and a
1695
+ * run with no artifact still has a completion claim.
1696
+ */
1697
+ resultAvailable?: boolean;
1698
+ /**
1699
+ * The journal seq of the decision entry recording the acceptance of
1700
+ * the artifact this terminal carries (RV2506); same mirror, absent
1701
+ * unless the acceptance actually rendered. Read it with
1702
+ * `rulvar inspect` to see WHICH validators accepted WHICH hash.
1703
+ */
1704
+ acceptedArtifactRef?: number;
1705
+ /**
1706
+ * The claim consistency pass meta, detached (RV3304): `judgedStage`,
1707
+ * `judgedHash`, the coverage grade and the `findings` count, so the
1708
+ * surface a consumer gates on says WHAT was semantically verified,
1709
+ * over WHICH document, and what the judge found, without reaching
1710
+ * into the workflow value. Mutating this copy never touches the
1711
+ * outcome the engine owns.
1712
+ */
1713
+ claimConsistencyMeta?: Record<string, unknown>;
1714
+ /**
1715
+ * The host declared config identity the run was started under
1716
+ * (RV3210), echoed here since RV3304 so a decision consumer binds
1717
+ * the verdict above to the configuration that produced it without a
1718
+ * second read of the run record. Absent when the run declared none.
1719
+ */
1720
+ configFingerprint?: string;
1721
+ /**
1682
1722
  * Where THIS copy of the envelope was assembled (RV1209). Absent, the
1683
1723
  * historical byte contract, means the settlement chokepoint built it
1684
1724
  * from the live outcome, so every field above is the run's own
@@ -10361,7 +10401,12 @@ interface OrchestrateClaimConsistency {
10361
10401
  * explicitly (a ConfigError without that synthesis, the
10362
10402
  * contradictions precedent), and non-empty findings block the
10363
10403
  * `skipWhenDraftValid` gate: a draft contradicting its own pool
10364
- * never earns the skip. 'fail' fails the run typed with
10404
+ * never earns the skip. The carry can only ride a prompt that still
10405
+ * lies ahead, so it binds the pass that runs BEFORE the synthesis:
10406
+ * under `stage: 'both'` the draft pass carries and the final pass
10407
+ * reports, and `stage: 'final'` with 'carry' is a ConfigError at
10408
+ * intake, because a posture that reads as a gate must not quietly
10409
+ * behave as 'report'. 'fail' fails the run typed with
10365
10410
  * `data.source` 'orchestrator_claim_consistency' BEFORE any
10366
10411
  * synthesis dispatch; the judge itself has already been paid, which
10367
10412
  * is the honest minimum for a semantic verdict. A judge that does
@@ -10585,6 +10630,18 @@ interface OrchestrateClaimConsistencyMeta {
10585
10630
  */
10586
10631
  judgeDeclined?: true;
10587
10632
  /**
10633
+ * How many judged contradictions the pass FOUND on the judged
10634
+ * document, present exactly when the judge settled ok (RV3304): `0`
10635
+ * is a clean verdict, a positive count is a disagreement that stayed
10636
+ * wherever the posture did not stop the run. The findings themselves
10637
+ * ride `claimContradictions` beside this meta on the acceptance
10638
+ * envelope, but the meta travels ALONE onto RunOutcome, the
10639
+ * journaled run settle, and the terminal envelope, and the
10640
+ * 2026-08-12 comparison run settled ok/complete over a retained
10641
+ * finding no terminal surface could count.
10642
+ */
10643
+ findings?: number;
10644
+ /**
10588
10645
  * The one field a consumer reads INSTEAD of inferring semantic
10589
10646
  * health from an empty findings array (RV1702):
10590
10647
  * {@link claimCoverageOf} over this meta, so `completion:
@@ -12155,7 +12212,7 @@ interface RunHandle<R> {
12155
12212
  //#endregion
12156
12213
  //#region src/engine/terminal-envelope.d.ts
12157
12214
  /** The outcome facts the assembler reads; a structural subset of RunOutcome. */
12158
- type TerminalOutcomeFacts = Pick<RunOutcome<unknown>, "status" | "error" | "completion"> & {
12215
+ type TerminalOutcomeFacts = Pick<RunOutcome<unknown>, "status" | "error" | "completion" | "deliverableAccepted" | "resultAvailable" | "acceptedArtifactRef" | "claimConsistencyMeta"> & {
12159
12216
  usage: RunOutcome<unknown>["usage"];
12160
12217
  cost: Pick<RunOutcome<unknown>["cost"], "totalUsd" | "grossUsd" | "byModel"> & {
12161
12218
  usageApprox?: boolean;
@@ -12183,7 +12240,8 @@ declare function terminalEnvelopeOf(input: {
12183
12240
  settlement?: {
12184
12241
  settledReason?: "superseded";
12185
12242
  };
12186
- provenance?: "journal";
12243
+ provenance?: "journal"; /** The run's declared config identity (RV3210), echoed onto the envelope (RV3304). */
12244
+ configFingerprint?: string;
12187
12245
  }): TerminalEnvelope;
12188
12246
  //#endregion
12189
12247
  //#region src/l0/decision-chain.d.ts
@@ -13075,6 +13133,18 @@ declare function lastRunSettle(entries: readonly JournalEntry[]): {
13075
13133
  * recorded" rather than as a claim.
13076
13134
  */
13077
13135
  rejectedFinishCandidates?: RejectedFinishCandidate[];
13136
+ /**
13137
+ * The semantic outcome the settle recorded (RV3304), read back
13138
+ * the same defensive way: the acceptance verdict, the
13139
+ * deliverable presence, the acceptance ref and the judge meta,
13140
+ * so a restarted reader recovers the facts a live consumer
13141
+ * gated on. Absent on journals written before the lift carried
13142
+ * them; absence means NOT RECORDED, never a verdict.
13143
+ */
13144
+ deliverableAccepted?: boolean;
13145
+ resultAvailable?: boolean;
13146
+ acceptedArtifactRef?: number;
13147
+ claimConsistencyMeta?: Record<string, unknown>;
13078
13148
  } | undefined;
13079
13149
  /**
13080
13150
  * Whether a terminal figure counts THIS segment's work or the whole
@@ -14055,6 +14125,17 @@ interface StatementReconciliation {
14055
14125
  componentToleranceUsd: number;
14056
14126
  verdict: "match" | "divergence" | "partial-coverage" | "no-overlap";
14057
14127
  /**
14128
+ * How much of the MATCHED statement claims money (RV3306):
14129
+ * 'complete' when every matched export row (requests mode) or every
14130
+ * component line (categories mode) carries a dollar claim, a row
14131
+ * total or a component split; 'partial' when some do; 'none' when
14132
+ * the statement matched on identity and usage alone, or matched
14133
+ * nothing. Kept apart from row coverage on purpose: coverage says
14134
+ * the records line up, this says whether the provider actually
14135
+ * stated dollars over them.
14136
+ */
14137
+ dollarCoverage: "complete" | "partial" | "none";
14138
+ /**
14058
14139
  * The settlement-grade composite, first class (RV1006): true exactly
14059
14140
  * when the verdict is 'match' AND coverage is complete AND no row's
14060
14141
  * usage is unknown AND no model went unpriced. A 'match' alone is
@@ -14063,9 +14144,22 @@ interface StatementReconciliation {
14063
14144
  * consumer must not assemble this predicate by hand. The last two
14064
14145
  * conditions overlap today's verdict semantics deliberately: the
14065
14146
  * predicate states the full contract so it cannot drift apart from
14066
- * a future verdict refinement.
14147
+ * a future verdict refinement. Note what it does NOT require: a
14148
+ * dollar claim. A usage-only export that matches on identity and
14149
+ * tokens reads `settleable: true`; gate MONETARY closure on
14150
+ * `monetarySettleable` below.
14067
14151
  */
14068
14152
  settleable: boolean;
14153
+ /**
14154
+ * The MONETARY settlement predicate (RV3306): `settleable` AND
14155
+ * complete dollar coverage. `settleable` answers "do the records
14156
+ * agree"; this answers "may money close against this statement".
14157
+ * The 2026-08-12 audit named the difference on this exact module: a
14158
+ * usage-only request export settled 'match' without one dollar of
14159
+ * provider evidence, and a finance pipeline gating on `settleable`
14160
+ * alone would have closed money against it.
14161
+ */
14162
+ monetarySettleable: boolean;
14069
14163
  }
14070
14164
  /**
14071
14165
  * Reconciles the invoice against a normalized provider export. Pure and
package/dist/index.js CHANGED
@@ -1327,6 +1327,20 @@ function detachedError(error) {
1327
1327
  }
1328
1328
  }
1329
1329
  /**
1330
+ * A detached copy of the judge meta (RV3304), the `detachedError`
1331
+ * posture: Json shaped by construction, so a structured clone
1332
+ * reproduces it exactly, and a host that smuggled something exotic
1333
+ * past the type falls back to a shallow copy rather than throwing at
1334
+ * the settlement chokepoint.
1335
+ */
1336
+ function detachedMeta(meta) {
1337
+ try {
1338
+ return structuredClone(meta);
1339
+ } catch {
1340
+ return { ...meta };
1341
+ }
1342
+ }
1343
+ /**
1330
1344
  * Assembles one terminal envelope (RV1105). `settlement` present means
1331
1345
  * nothing durable records the terminal: `settled` reads false, and the
1332
1346
  * optional `settledReason: 'superseded'` names the fenced-out segment
@@ -1356,6 +1370,11 @@ function terminalEnvelopeOf(input) {
1356
1370
  };
1357
1371
  if (outcome.error !== void 0) envelope.error = detachedError(outcome.error);
1358
1372
  if (outcome.completion !== void 0) envelope.completion = outcome.completion;
1373
+ if (outcome.deliverableAccepted !== void 0) envelope.deliverableAccepted = outcome.deliverableAccepted;
1374
+ if (outcome.resultAvailable !== void 0) envelope.resultAvailable = outcome.resultAvailable;
1375
+ if (outcome.acceptedArtifactRef !== void 0) envelope.acceptedArtifactRef = outcome.acceptedArtifactRef;
1376
+ if (outcome.claimConsistencyMeta !== void 0) envelope.claimConsistencyMeta = detachedMeta(outcome.claimConsistencyMeta);
1377
+ if (input.configFingerprint !== void 0) envelope.configFingerprint = input.configFingerprint;
1359
1378
  if (outcome.cost.wireRequests !== void 0) envelope.wireRequests = outcome.cost.wireRequests;
1360
1379
  if (input.settlement?.settledReason !== void 0) envelope.settledReason = input.settlement.settledReason;
1361
1380
  if (input.provenance !== void 0) envelope.provenance = input.provenance;
@@ -8806,17 +8825,36 @@ function lastRunSettle(entries) {
8806
8825
  if (value?.decisionType === "run_settle" && typeof value.runStatus === "string" && RUN_STATUSES.has(value.runStatus)) {
8807
8826
  const completion = value.completion;
8808
8827
  const rejected = readRejectedFinishCandidates(value.rejectedFinishCandidates);
8828
+ const judgeMeta = readClaimConsistencyMeta(value.claimConsistencyMeta);
8809
8829
  return {
8810
8830
  runStatus: value.runStatus,
8811
8831
  seq: entry.seq,
8812
8832
  ...typeof value.outputHash === "string" ? { outputHash: value.outputHash } : {},
8813
8833
  ...completion === "complete" || completion === "partial" || completion === "rejected" ? { completion } : {},
8814
- ...rejected === void 0 ? {} : { rejectedFinishCandidates: rejected }
8834
+ ...rejected === void 0 ? {} : { rejectedFinishCandidates: rejected },
8835
+ ...typeof value.deliverableAccepted === "boolean" ? { deliverableAccepted: value.deliverableAccepted } : {},
8836
+ ...typeof value.resultAvailable === "boolean" ? { resultAvailable: value.resultAvailable } : {},
8837
+ ...typeof value.acceptedArtifactRef === "number" && Number.isSafeInteger(value.acceptedArtifactRef) && value.acceptedArtifactRef >= 0 ? { acceptedArtifactRef: value.acceptedArtifactRef } : {},
8838
+ ...judgeMeta === void 0 ? {} : { claimConsistencyMeta: judgeMeta }
8815
8839
  };
8816
8840
  }
8817
8841
  }
8818
8842
  }
8819
8843
  /**
8844
+ * The judge meta of a persisted settle, or `undefined` (RV3304). The
8845
+ * whole object drops unless its load bearing fields are shaped as the
8846
+ * live producer writes them (`judgeInvoked`, the coverage grade, the
8847
+ * judged stage and hash): a partially shaped meta read as a verdict
8848
+ * would claim semantic ground the journal does not hold, the same
8849
+ * posture as `readRejectedFinishCandidates`.
8850
+ */
8851
+ function readClaimConsistencyMeta(raw) {
8852
+ if (typeof raw !== "object" || raw === null || Array.isArray(raw)) return;
8853
+ const meta = raw;
8854
+ if (typeof meta.judgeInvoked !== "boolean" || typeof meta.coverage !== "string" || meta.judgedStage !== "draft" && meta.judgedStage !== "final" || typeof meta.judgedHash !== "string") return;
8855
+ return { ...raw };
8856
+ }
8857
+ /**
8820
8858
  * The rejected finish candidates of a persisted settle, or `undefined`
8821
8859
  * (RV2605). The WHOLE list drops on any malformed row, the same posture
8822
8860
  * the live lift takes (RV2507): a partial history read as complete
@@ -9286,6 +9324,21 @@ function reduceInvocationTable(events) {
9286
9324
  */
9287
9325
  const CLAIM_JUDGE_LABEL = "claim-consistency-judge";
9288
9326
  /**
9327
+ * Whether a synthesize span's label names a claim-consistency judge
9328
+ * invocation: the exact {@link CLAIM_JUDGE_LABEL}, or a suffixed
9329
+ * variant of it (the final pass dispatches under
9330
+ * `claim-consistency-judge-final` since RV2509 so the two passes of
9331
+ * `stage: 'both'` stay separable). BOTH reducers must classify through
9332
+ * this one predicate (RV3302): the live fold compared the label for
9333
+ * exact equality while the journal fold accepted the suffix, and the
9334
+ * 2026-08-12 comparison run reported semanticJudgeMs 0 with the whole
9335
+ * 272923 ms window read as final composition on the live surface
9336
+ * while the journal fold correctly split 224864 against 48059.
9337
+ */
9338
+ function isClaimJudgeLabel(label) {
9339
+ return label === "claim-consistency-judge" || (label?.startsWith(`claim-consistency-judge-`) ?? false);
9340
+ }
9341
+ /**
9289
9342
  * The label the final synthesis (composition) invocation dispatches
9290
9343
  * under (RV2901). The engine labelling its OWN dispatches is what lets
9291
9344
  * `criticalPathFromJournal` split the synthesize bucket offline: the
@@ -9366,7 +9419,7 @@ function reduceCriticalPath(events) {
9366
9419
  if (started === void 0) break;
9367
9420
  if (started.role === "synthesize") {
9368
9421
  const wall = Math.max(0, at - started.at);
9369
- const judge = started.label === CLAIM_JUDGE_LABEL;
9422
+ const judge = isClaimJudgeLabel(started.label);
9370
9423
  synthesisMs += wall;
9371
9424
  if (judge) semanticJudgeMs += wall;
9372
9425
  else finalCompositionMs += wall;
@@ -9510,7 +9563,7 @@ function criticalPathFromJournal(entries) {
9510
9563
  continue;
9511
9564
  }
9512
9565
  labelledSynthesis = true;
9513
- if (label === "claim-consistency-judge" || label.startsWith(`claim-consistency-judge-`)) semanticJudgeMs += wall;
9566
+ if (isClaimJudgeLabel(label)) semanticJudgeMs += wall;
9514
9567
  else finalCompositionMs += wall;
9515
9568
  }
9516
9569
  const segments = logicalRunTelemetry(ordered).segments;
@@ -16111,6 +16164,7 @@ function reconcileStatement(invoice, statement, options) {
16111
16164
  let statementComponents;
16112
16165
  let matchedStatementRows = 0;
16113
16166
  let matchedUsdRows = 0;
16167
+ let matchedDollarRows = 0;
16114
16168
  let tokenMismatches = 0;
16115
16169
  let partialOverlap = false;
16116
16170
  const tokenMismatchSample = [];
@@ -16215,6 +16269,7 @@ function reconcileStatement(invoice, statement, options) {
16215
16269
  for (const row of statement.rows) {
16216
16270
  if (!matchedStatement.has(row.responseId)) continue;
16217
16271
  matchedStatementRows += 1;
16272
+ if (row.usd !== void 0 || row.componentsUsd !== void 0) matchedDollarRows += 1;
16218
16273
  if (row.usd !== void 0) {
16219
16274
  matchedUsdRows += 1;
16220
16275
  totalSeen = true;
@@ -16320,6 +16375,10 @@ function reconcileStatement(invoice, statement, options) {
16320
16375
  else if (matchedRows === 0 && !partialOverlap) verdict = "no-overlap";
16321
16376
  else if (!coverageComplete) verdict = "partial-coverage";
16322
16377
  else verdict = "match";
16378
+ const dollarClaims = statement.kind === "requests" ? matchedDollarRows : components.filter((line) => line.statementUsd !== void 0).length;
16379
+ const dollarSlots = statement.kind === "requests" ? matchedStatementRows : components.length;
16380
+ const dollarCoverage = dollarSlots > 0 && dollarClaims === dollarSlots ? "complete" : dollarClaims > 0 ? "partial" : "none";
16381
+ const settleable = verdict === "match" && coverageComplete && usageUnknownRows === 0 && unpricedModels.size === 0;
16323
16382
  return {
16324
16383
  mode: statement.kind,
16325
16384
  coverage: {
@@ -16345,7 +16404,9 @@ function reconcileStatement(invoice, statement, options) {
16345
16404
  usageUnknownRows,
16346
16405
  componentToleranceUsd,
16347
16406
  verdict,
16348
- settleable: verdict === "match" && coverageComplete && usageUnknownRows === 0 && unpricedModels.size === 0
16407
+ dollarCoverage,
16408
+ settleable,
16409
+ monetarySettleable: settleable && dollarCoverage === "complete"
16349
16410
  };
16350
16411
  }
16351
16412
  /** A cell that is absent by export convention: missing, null, or ''. */
@@ -16587,10 +16648,15 @@ function persistedTerminalEnvelope(input) {
16587
16648
  outcome: {
16588
16649
  status: settle.runStatus,
16589
16650
  ...settle.completion === void 0 ? {} : { completion: settle.completion },
16651
+ ...settle.deliverableAccepted === void 0 ? {} : { deliverableAccepted: settle.deliverableAccepted },
16652
+ ...settle.resultAvailable === void 0 ? {} : { resultAvailable: settle.resultAvailable },
16653
+ ...settle.acceptedArtifactRef === void 0 ? {} : { acceptedArtifactRef: settle.acceptedArtifactRef },
16654
+ ...settle.claimConsistencyMeta === void 0 ? {} : { claimConsistencyMeta: settle.claimConsistencyMeta },
16590
16655
  usage: ledger.usage,
16591
16656
  cost: costReportFromJournal(input.entries, input.priceUsd)
16592
16657
  },
16593
16658
  agentsSpawned: ledger.agentsSpawned,
16659
+ ...input.meta?.configFingerprint === void 0 ? {} : { configFingerprint: input.meta.configFingerprint },
16594
16660
  provenance: "journal"
16595
16661
  })
16596
16662
  };
@@ -22549,6 +22615,7 @@ function validateOrchestrateOptions(opts) {
22549
22615
  const stage = consistency.stage ?? "draft";
22550
22616
  if (stage !== "draft" && stage !== "final" && stage !== "both") throw new ConfigError("orchestrate claimConsistency.stage must be 'draft', 'final' or 'both'; got " + JSON.stringify(consistency.stage));
22551
22617
  if (stage !== "draft" && opts.synthesis === void 0) throw new ConfigError(`orchestrate claimConsistency.stage '${stage}' requires synthesis: without the post-fan-in invocation the coordination draft IS the final artifact, and the default 'draft' already judges it`);
22618
+ if (stage === "final" && onFound === "carry") throw new ConfigError("orchestrate claimConsistency.onFound 'carry' cannot pair with stage 'final': the final pass runs after the synthesis, so there is no prompt left to carry the findings into; use 'report' or 'fail', or keep a carried draft pass with stage 'both'");
22552
22619
  if (consistency.pattern !== void 0) {
22553
22620
  if (typeof consistency.pattern !== "string") throw new ConfigError(`orchestrate claimConsistency.pattern must be a string; got ${typeof consistency.pattern}`);
22554
22621
  let probe;
@@ -24848,7 +24915,10 @@ function makeOrchestratorWorkflow(goal, opts) {
24848
24915
  reason
24849
24916
  }));
24850
24917
  claimFindingsFound = findings;
24851
- claimConsistencyMeta = finishMeta({ judgeInvoked: true });
24918
+ claimConsistencyMeta = finishMeta({
24919
+ judgeInvoked: true,
24920
+ findings: findings.length
24921
+ });
24852
24922
  announce();
24853
24923
  if (onFound !== "fail" || findings.length === 0) return;
24854
24924
  throw new FailRunError(`the claim-consistency judge found ${String(findings.length)} contradiction${findings.length === 1 ? "" : "s"} between the draft and the settled child pool: ` + findings.map((finding) => `${finding.anchor} (${finding.reason})`).join("; "), { data: {
@@ -28419,6 +28489,7 @@ function createEngine(options) {
28419
28489
  workflow: wf.name,
28420
28490
  outcome: outcomeFacts,
28421
28491
  agentsSpawned: budget.spent().agentsSpawned,
28492
+ ...configFingerprint === void 0 ? {} : { configFingerprint },
28422
28493
  ...settlementFailure !== void 0 ? { settlement: {} } : supersededBy !== void 0 ? { settlement: { settledReason: "superseded" } } : {}
28423
28494
  });
28424
28495
  const outcome = {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rulvar/core",
3
- "version": "1.236.0",
3
+ "version": "1.237.0",
4
4
  "description": "Rulvar core: L0 contracts, journal kernel, ctx primitives, agent runtime, model router, tool system, dynamic orchestrator, InMemory and JSONL stores, event stream.",
5
5
  "type": "module",
6
6
  "license": "Apache-2.0",