@rulvar/core 1.110.0 → 1.111.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
@@ -824,6 +824,15 @@ interface EntryBillingFold {
824
824
  * semantics. False folds the aggregate slices, the historical basis.
825
825
  */
826
826
  fullyAttributed: boolean;
827
+ /**
828
+ * The models this fold priced per call: record sums equal slice sums
829
+ * counter for counter under the symmetric per-model key (RV604).
830
+ * Published so a row builder can honor the same decision (RV703): a
831
+ * covered model's rows are exactly its records, so no per-slice
832
+ * remainder may be fabricated for it; recomputing coverage elsewhere
833
+ * is how the phantom-remainder skew was born.
834
+ */
835
+ coveredModels: ReadonlySet<ModelRef>;
827
836
  }
828
837
  /**
829
838
  * The billing fold over one terminal entry (RV504), shared by the
package/dist/index.js CHANGED
@@ -2532,7 +2532,8 @@ function priceEntryBilling(entry, priceUsd) {
2532
2532
  servedBy,
2533
2533
  usage
2534
2534
  })),
2535
- fullyAttributed
2535
+ fullyAttributed,
2536
+ coveredModels: covered
2536
2537
  };
2537
2538
  }
2538
2539
  /**
@@ -8542,16 +8543,22 @@ function costReportFromJournal(entries, priceUsd) {
8542
8543
  * and per-row `allocatedUsd` is the additive column whose flat sum
8543
8544
  * reproduces `totalUsd` exactly in every case.
8544
8545
  *
8545
- * Coverage is loss-free by construction: an entry whose records do not
8546
- * cover its usage total (a resume restored from a checkpoint written
8547
- * before the ledger shipped) contributes an `unattributed` remainder
8548
- * row, and an entry with no records at all (written before the ledger
8549
- * shipped, or a fully replayed invocation) contributes one
8550
- * `unattributed` row per usage slice. Missing provider ids are marked,
8551
- * never dropped: a finished call without one reconciles as
8552
- * `missing-provider-id`, a failed or severed call without one as
8553
- * `unconfirmed` (the provider may or may not have billed it; there is
8554
- * no id to match).
8546
+ * Coverage is loss-free by construction: a model whose records do not
8547
+ * cover its usage (a resume restored from a checkpoint written before
8548
+ * the ledger shipped) contributes an `unattributed` remainder row per
8549
+ * slice, and an entry with no records at all (written before the
8550
+ * ledger shipped, or a fully replayed invocation) contributes one
8551
+ * `unattributed` row per usage slice. A COVERED model contributes no
8552
+ * remainder rows at all (RV703): its rows are exactly its records, the
8553
+ * same per-model decision the billing fold makes, so a role mismatch
8554
+ * between records and slices (the schema-extract default splits one
8555
+ * model's usage by role while the record carries one role, or none)
8556
+ * can no longer fabricate a phantom row that breaks the
8557
+ * `rowUsdNonAdditive: false` promise and siphons allocation from the
8558
+ * real call. Missing provider ids are marked, never dropped: a
8559
+ * finished call without one reconciles as `missing-provider-id`, a
8560
+ * failed or severed call without one as `unconfirmed` (the provider
8561
+ * may or may not have billed it; there is no id to match).
8555
8562
  *
8556
8563
  * Pricing happens at fold time from the table you pass, exactly like
8557
8564
  * CostReport. For historical stability against price-table updates,
@@ -8577,6 +8584,12 @@ const USAGE_FIELDS = [
8577
8584
  * model: the whole-entry remainder was published under `entry.servedBy`,
8578
8585
  * so a slice with no records left its allocation pool rowless and the
8579
8586
  * dust pass moved its dollars onto another model's row.
8587
+ *
8588
+ * Consulted only for UNCOVERED models (RV703): coverage is a per-model
8589
+ * decision, so a covered model's slices never reach this arithmetic.
8590
+ * The per-role subtraction here against the per-model coverage key was
8591
+ * exactly the mismatch that fabricated a phantom remainder whenever a
8592
+ * covered model's record roles differed from its slice roles.
8580
8593
  */
8581
8594
  function sliceRemainder(slice, records) {
8582
8595
  const remainder = {
@@ -8686,7 +8699,8 @@ function invoiceFromJournal(entries, priceUsd, options) {
8686
8699
  let everyEntryFullyAttributed = true;
8687
8700
  for (const entry of entries) {
8688
8701
  if (entry.status === "running" || entry.usage === void 0) continue;
8689
- if (!priceEntryBilling(entry, priceUsd).fullyAttributed) everyEntryFullyAttributed = false;
8702
+ const billing = priceEntryBilling(entry, priceUsd);
8703
+ if (!billing.fullyAttributed) everyEntryFullyAttributed = false;
8690
8704
  const abandoned = entry.kind !== "resolution" && entry.kind !== "abandon" && abandonFold.isAbandoned(entry.ref ?? entry.seq);
8691
8705
  const base = {
8692
8706
  entrySeq: entry.seq,
@@ -8737,6 +8751,7 @@ function invoiceFromJournal(entries, priceUsd, options) {
8737
8751
  }
8738
8752
  let remainderOrdinal = records.length + 1;
8739
8753
  for (const slice of entryUsageSlices(entry)) {
8754
+ if (billing.coveredModels.has(slice.servedBy)) continue;
8740
8755
  const remainder = sliceRemainder(slice, records);
8741
8756
  if (remainder === void 0) continue;
8742
8757
  const usd = rowUsd(priceUsd, slice.servedBy, remainder, entry.seq);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rulvar/core",
3
- "version": "1.110.0",
3
+ "version": "1.111.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",