@rulvar/evals 1.243.0 → 1.244.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
@@ -849,9 +849,14 @@ declare function runFaultInjection(options?: RunFaultInjectionOptions): Promise<
849
849
  * figure whose premise contradicts the declared input (2,000 slots
850
850
  * computed from a 30 minute window where the input declares a 20
851
851
  * minute burst), and 'cost-basis' prints a locally estimated total as
852
- * the provider's bill.
852
+ * the provider's bill. The fourth comparison experiment named the
853
+ * decisive one (RV3909): 'stale-doctrine-echo' is a draft echoing a
854
+ * DOCUMENTED doctrine while the pool holds the diverging source fact,
855
+ * both sides cited; the experiment's answer echoed the retired
856
+ * budget-immutability wording from a guide six weeks stale, and no
857
+ * judge could flag it because the pool never carried the source side.
853
858
  */
854
- type ClaimCorpusClass = "live-fact" | "package-identity" | "inverted-default" | "numeric-range" | "negation" | "bounded-coverage" | "modality-overclaim" | "scope-ambiguity" | "bound-conflation" | "derived-premise" | "cost-basis";
859
+ type ClaimCorpusClass = "live-fact" | "package-identity" | "inverted-default" | "numeric-range" | "negation" | "bounded-coverage" | "modality-overclaim" | "scope-ambiguity" | "bound-conflation" | "derived-premise" | "cost-basis" | "stale-doctrine-echo";
855
860
  /** One adversarial case: a draft, its contradicting evidence, and the mechanical expectations. */
856
861
  interface ClaimCorpusCase {
857
862
  id: string;
package/dist/index.js CHANGED
@@ -3950,12 +3950,13 @@ const sectionalRepairRound = {
3950
3950
  const { compositions, judges } = tailSpans(entries);
3951
3951
  const verdictRows = entries.filter((entry) => entry.kind === "decision" && entry.value?.decisionType === "orchestrator_finish_validation").map((entry) => entry.value);
3952
3952
  const roundPrompts = adapter.calls.filter((call) => call.label === "final-composition").map((call) => call.prompt);
3953
- const sectionalPrompt = roundPrompts.length === 2 && roundPrompts[0]?.includes("SECTIONAL ROUND") === false && roundPrompts[1]?.includes("SECTIONAL ROUND") === true && roundPrompts[1]?.includes("\"## Verdict\"") === true;
3953
+ const sectionalPrompt = roundPrompts.length === 2 && roundPrompts[0]?.includes("SECTIONAL ROUND") === false && roundPrompts[1]?.includes("SECTIONAL ROUND") === true && roundPrompts[1]?.includes("\"## Verdict\"") === true && roundPrompts[0]?.includes("retained evidence discipline") === false && roundPrompts[1]?.includes("retained evidence discipline") === true;
3954
3954
  const byteIdentity = value?.result === SECTIONAL_SPLICED && String(value.result).startsWith(SECTIONAL_PREFIX);
3955
+ const lineage = value?.claimConsistencyMeta?.passes === 2 && value.claimConsistencyMeta.firstPassFindings === 1 && value.claimConsistencyMeta.semanticRepairRounds === 1;
3955
3956
  return {
3956
3957
  observation: {
3957
- matched: outcome.status === "ok" && byteIdentity && value.claimConsistencyMeta?.findings === 0 && verdictRows.map((row) => row.verdict).join(",") === "accepted,accepted" && verdictRows.map((row) => String(row.repairsUsed)).join(",") === "0,0" && sectionalPrompt && compositions.length === 2 && judges.length === 2,
3958
- detail: `run '${outcome.status}' shipped ${value?.result === SECTIONAL_SPLICED ? "the spliced whole" : "an unexpected document"}; byte identity=${String(byteIdentity)}; sectional prompt=${String(sectionalPrompt)}; verdicts [${verdictRows.map((row) => row.verdict).join(",")}]; ${String(compositions.length)} composition(s), ${String(judges.length)} final judge pass(es)`
3958
+ matched: outcome.status === "ok" && byteIdentity && value.claimConsistencyMeta?.findings === 0 && lineage && verdictRows.map((row) => row.verdict).join(",") === "accepted,accepted" && verdictRows.map((row) => String(row.repairsUsed)).join(",") === "0,0" && sectionalPrompt && compositions.length === 2 && judges.length === 2,
3959
+ detail: `run '${outcome.status}' shipped ${value?.result === SECTIONAL_SPLICED ? "the spliced whole" : "an unexpected document"}; byte identity=${String(byteIdentity)}; sectional prompt=${String(sectionalPrompt)}; lineage=${String(lineage)}; verdicts [${verdictRows.map((row) => row.verdict).join(",")}]; ${String(compositions.length)} composition(s), ${String(judges.length)} final judge pass(es)`
3959
3960
  },
3960
3961
  artifacts: [jsonArtifact("outcome.json", {
3961
3962
  status: outcome.status,
@@ -4146,10 +4147,11 @@ const SCENARIOS = [
4146
4147
  const judgeSawPatched = adapter.calls.filter((call) => call.label === "claim-consistency-judge-final").map((call) => call.prompt)[0]?.includes(`(run ${PATCH_RUN_ID})`) === true;
4147
4148
  const roundPrompts = adapter.calls.filter((call) => call.label === "final-composition").map((call) => call.prompt);
4148
4149
  const lessonCarried = roundPrompts.length === 2 && roundPrompts[0]?.includes("HOST VALIDATION LESSONS") === false && roundPrompts[1]?.includes("HOST VALIDATION LESSONS") === true && roundPrompts[1]?.includes("evidence-grade") === true;
4150
+ const aggregate = value?.deterministicPatches?.decisions === 1 && typeof value.deterministicPatches.patches === "number" && value.deterministicPatches.patches >= 1 && /^[0-9a-f]{64}$/u.test(String(value.deterministicPatches.lastAfterHash)) && value.deterministicPatches.lastAfterHash !== value.deterministicPatches.lastBeforeHash;
4149
4151
  return {
4150
4152
  observation: {
4151
- matched: outcome.status === "ok" && value?.result === TAIL_FINAL_CLEAN && value.claimConsistencyMeta?.findings === 0 && verdicts === "accepted,accepted" && pools === "0,0" && patch?.outcome === "accepted" && patch.healed?.map((healed) => healed.name).join(",") === "evidence-grade" && judgeSawPatched && lessonCarried && compositions.length === 2 && judges.length === 2,
4152
- detail: `run '${outcome.status}' shipped ${value?.result === TAIL_FINAL_CLEAN ? "the clean round document" : "an unexpected document"}; verdicts [${verdicts}], repairsUsed [${pools}] (no wire, no pool spent); patch outcome='${String(patch?.outcome)}'; judge saw patched=${String(judgeSawPatched)}; lesson carried=${String(lessonCarried)}; ${String(compositions.length)} composition(s), ${String(judges.length)} final judge pass(es)`
4153
+ matched: outcome.status === "ok" && value?.result === TAIL_FINAL_CLEAN && value.claimConsistencyMeta?.findings === 0 && aggregate && verdicts === "accepted,accepted" && pools === "0,0" && patch?.outcome === "accepted" && patch.healed?.map((healed) => healed.name).join(",") === "evidence-grade" && judgeSawPatched && lessonCarried && compositions.length === 2 && judges.length === 2,
4154
+ detail: `run '${outcome.status}' shipped ${value?.result === TAIL_FINAL_CLEAN ? "the clean round document" : "an unexpected document"}; verdicts [${verdicts}], repairsUsed [${pools}] (no wire, no pool spent); patch outcome='${String(patch?.outcome)}'; envelope aggregate=${String(aggregate)}; judge saw patched=${String(judgeSawPatched)}; lesson carried=${String(lessonCarried)}; ${String(compositions.length)} composition(s), ${String(judges.length)} final judge pass(es)`
4153
4155
  },
4154
4156
  artifacts: [jsonArtifact("outcome.json", {
4155
4157
  status: outcome.status,
@@ -4159,7 +4161,106 @@ const SCENARIOS = [
4159
4161
  };
4160
4162
  }
4161
4163
  },
4162
- claimJudgeDeadArmedRefusal
4164
+ claimJudgeDeadArmedRefusal,
4165
+ {
4166
+ name: "acceptance-reserve-refusal",
4167
+ doctrine: "budget.acceptanceReserve 'require' refuses the run typed BEFORE the first wire when the declared acceptance tail (synthesis hold + judge passes + mechanical repair + round composition + working room) does not fit the effective cap, and the refusal journals every term; the same config under the default warn posture dispatches",
4168
+ async run() {
4169
+ const store = new InMemoryStore();
4170
+ const adapter = new FakeAdapter({ agents: { "*": "never dispatched" } });
4171
+ const outcome = await createEngine({
4172
+ adapters: [adapter],
4173
+ stores: { journal: store },
4174
+ defaults: { routing: {
4175
+ loop: FAKE_MODEL_REF,
4176
+ orchestrate: FAKE_MODEL_REF,
4177
+ synthesize: FAKE_MODEL_REF,
4178
+ extract: FAKE_MODEL_REF
4179
+ } }
4180
+ }).run(makeOrchestratorWorkflow("audit the executor", {
4181
+ synthesis: { estCost: .15 },
4182
+ claimConsistency: {
4183
+ stage: "final",
4184
+ onFound: "repair",
4185
+ judge: { estCost: .2 }
4186
+ },
4187
+ finishValidation: {
4188
+ validators: [{
4189
+ name: "anything",
4190
+ validate: () => ({ ok: true })
4191
+ }],
4192
+ estRepairCostUsd: .1
4193
+ },
4194
+ budget: {
4195
+ synthesisReserveUsd: 1,
4196
+ acceptanceReserve: "require"
4197
+ }
4198
+ }), void 0, {
4199
+ runId: "fault-acceptance-reserve",
4200
+ budgetUsd: 10
4201
+ }).result;
4202
+ const refusal = (await store.load("fault-acceptance-reserve")).find((entry) => entry.value?.decisionType === "acceptance_reserve_refused");
4203
+ const terms = refusal?.value;
4204
+ return {
4205
+ observation: {
4206
+ matched: outcome.status === "error" && (outcome.error?.message ?? "").includes("acceptanceReserve 'require'") && adapter.calls.length === 0 && terms !== void 0 && Math.abs((terms.requiredUsd ?? 0) - 2.15) < 1e-9 && Math.abs((terms.effectiveCapUsd ?? 0) - 2) < 1e-9 && terms.judgePasses === 2,
4207
+ detail: `run '${outcome.status}'; dispatches=${String(adapter.calls.length)}; refusal decision=${String(refusal !== void 0)} (required ${String(terms?.requiredUsd)} vs cap ${String(terms?.effectiveCapUsd)}, ` + `judge passes ${String(terms?.judgePasses)}); ${outcome.error?.message ?? ""}`.slice(0, 400)
4208
+ },
4209
+ artifacts: [jsonArtifact("refusal.json", {
4210
+ error: outcome.error ?? null,
4211
+ decision: refusal?.value ?? null
4212
+ })]
4213
+ };
4214
+ }
4215
+ },
4216
+ {
4217
+ name: "budget-policy-immutable",
4218
+ doctrine: "budgetPolicy 'immutable-lifetime' refuses a resume-time ceiling override typed before ownership, raise and lower alike, with zero provider dispatches and zero durable mutations; a bare resume of the same run stays a pure replay",
4219
+ async run() {
4220
+ const store = new InMemoryStore();
4221
+ const seeded = await createEngine({
4222
+ adapters: [new FakeAdapter({ agents: { "*": "answered" } })],
4223
+ stores: { journal: store },
4224
+ defaults: ROUTING
4225
+ }).run(echoWorkflow, void 0, {
4226
+ runId: "fault-budget-policy",
4227
+ budgetUsd: 1,
4228
+ budgetPolicy: "immutable-lifetime"
4229
+ }).result;
4230
+ const entriesBefore = (await store.load("fault-budget-policy")).length;
4231
+ const overrideAdapter = new FakeAdapter({ agents: { "*": "never dispatched" } });
4232
+ let refusalText = "the override was not refused";
4233
+ let refusedTyped = false;
4234
+ try {
4235
+ await createEngine({
4236
+ adapters: [overrideAdapter],
4237
+ stores: { journal: store },
4238
+ defaults: ROUTING
4239
+ }).resume("fault-budget-policy", echoWorkflow, { run: { budgetUsd: 2 } }).result;
4240
+ } catch (thrown) {
4241
+ refusalText = errorText(thrown);
4242
+ refusedTyped = thrown instanceof ConfigError && refusalText.includes("immutable-lifetime");
4243
+ }
4244
+ const entriesAfterRefusal = (await store.load("fault-budget-policy")).length;
4245
+ const metaAfterRefusal = await store.getMeta("fault-budget-policy");
4246
+ const bare = await createEngine({
4247
+ adapters: [new FakeAdapter({ agents: { "*": "never dispatched either" } })],
4248
+ stores: { journal: store },
4249
+ defaults: ROUTING
4250
+ }).resume("fault-budget-policy", echoWorkflow).result;
4251
+ return {
4252
+ observation: {
4253
+ matched: seeded.status === "ok" && refusedTyped && overrideAdapter.calls.length === 0 && entriesAfterRefusal === entriesBefore && metaAfterRefusal?.budgetUsd === 1 && metaAfterRefusal.budgetPolicy === "immutable-lifetime" && bare.status === "ok",
4254
+ detail: `seed '${seeded.status}'; refusal typed=${String(refusedTyped)} (${refusalText.slice(0, 160)}); override dispatches=${String(overrideAdapter.calls.length)}; entries ${String(entriesBefore)} -> ${String(entriesAfterRefusal)}; meta budgetUsd=${String(metaAfterRefusal?.budgetUsd)} policy=${String(metaAfterRefusal?.budgetPolicy)}; bare resume '${bare.status}'`
4255
+ },
4256
+ artifacts: [jsonArtifact("refusal.json", {
4257
+ refusalText,
4258
+ entriesBefore,
4259
+ entriesAfterRefusal
4260
+ }), jsonArtifact("meta.json", metaAfterRefusal ?? null)]
4261
+ };
4262
+ }
4263
+ }
4163
4264
  ];
4164
4265
  /** The scenario names in run order. */
4165
4266
  const FAULT_SCENARIO_NAMES = SCENARIOS.map((scenario) => scenario.name);
@@ -4377,6 +4478,21 @@ const CLAIM_CORPUS = [
4377
4478
  },
4378
4479
  runFactTerms: ["provider bill", "charged and settled"],
4379
4480
  expect: { minRunFactPairs: 1 }
4481
+ },
4482
+ {
4483
+ id: "stale-doctrine-echo-budget-override",
4484
+ class: "stale-doctrine-echo",
4485
+ draft: "The run budget ceiling is documented as immutable after start: resume accepts no budget parameter, so an exhausted run can never continue under a raised ceiling (docs/guide/budgets.md:43; packages/core/src/engine/engine.ts:588).",
4486
+ pool: [{
4487
+ nodeId: "agent:3",
4488
+ text: "The budgets guide historically taught \"immutable after start\" (docs/guide/budgets.md:43), but ResumeOptions.run raises budgetUsd and maxInFlightExposureUsd at resume, validated and journaled as a run_budget_override decision with a typed floor at the settled spend (packages/core/src/engine/engine.ts:588-604); the doctrine in force is immutability WITHIN a segment."
4489
+ }],
4490
+ critical: ["packages/core/src/engine/engine.ts"],
4491
+ expect: {
4492
+ minPairs: 1,
4493
+ anchors: ["packages/core/src/engine/engine.ts:588"],
4494
+ coverage: "full"
4495
+ }
4380
4496
  }
4381
4497
  ];
4382
4498
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rulvar/evals",
3
- "version": "1.243.0",
3
+ "version": "1.244.0",
4
4
  "description": "Rulvar evals: eval cases, golden outputs, rubric and judge graders, matrix sweeps, canary fingerprint.",
5
5
  "type": "module",
6
6
  "license": "Apache-2.0",
@@ -22,11 +22,11 @@
22
22
  "access": "public"
23
23
  },
24
24
  "dependencies": {
25
- "@rulvar/core": "1.243.0",
26
- "@rulvar/openai": "1.243.0",
27
- "@rulvar/testing": "1.243.0",
28
- "@rulvar/anthropic": "1.243.0",
29
- "@rulvar/plan": "1.243.0"
25
+ "@rulvar/anthropic": "1.244.0",
26
+ "@rulvar/openai": "1.244.0",
27
+ "@rulvar/core": "1.244.0",
28
+ "@rulvar/plan": "1.244.0",
29
+ "@rulvar/testing": "1.244.0"
30
30
  },
31
31
  "devDependencies": {
32
32
  "@types/node": "^22.20.1",