eval-quality 2.0.0 → 3.0.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/README.md CHANGED
@@ -179,7 +179,7 @@ import spec from 'eval-quality/schemas/eval-contract.schema.json' with { type: '
179
179
 
180
180
  The import attribute is required: ESM on Node 22 and 24 both throw `ERR_IMPORT_ATTRIBUTE_MISSING` without it. The development corpus ships the same way, at `eval-quality/corpus/dev/`, so you can read twenty-four real contracts and one compiled-and-sealed pair without cloning this repository.
181
181
 
182
- Version 1.0 is out and the published surface is stable: a breaking change to a command, an export, or a schema is a major version bump. `compile` refuses a contract whose `schemaVersion` differs from the one this build reads, so check the stamp on anything you did not author against this version. `CHANGELOG.md` records what each release breaks.
182
+ Version 1.0 is out and the published surface is stable: a breaking change to a command, an export, or a schema is a major version bump. `compile` refuses a contract whose `schemaVersion` differs from the one this build reads, and `preflight` and `score` refuse a probe the same way, so check the stamp on anything you did not author against this version. `CHANGELOG.md` records what each release breaks.
183
183
 
184
184
  ## Relationship with BMad and TEA
185
185
 
@@ -55,7 +55,12 @@ export function compile(contract, options) {
55
55
  // First, and before any check reads a declaration. AD-11 makes an unequal
56
56
  // stamp a rejection rather than a degraded read, and every check below is
57
57
  // written against this version's field shapes.
58
- checkSchemaVersion(contract.schemaVersion, EVAL_CONTRACT_SCHEMA_VERSION, 'EvalContract.schemaVersion');
58
+ checkSchemaVersion({
59
+ stamped: contract.schemaVersion,
60
+ accepted: EVAL_CONTRACT_SCHEMA_VERSION,
61
+ artifactPath: 'EvalContract.schemaVersion',
62
+ consequence: 'since its stale version would travel into the scoring version',
63
+ });
59
64
  checkRequirementLinkage(contract);
60
65
  checkObservableSuccessCriterion(contract);
61
66
  // Ahead of reachability, because a pointer naming an artifact nothing
@@ -1,2 +1,15 @@
1
- /** `schema-version-mismatch`: the stamp is not the version this build reads. */
2
- export declare function checkSchemaVersion(stamped: number, accepted: number, artifactPath: string): void;
1
+ /**
2
+ * `schema-version-mismatch`: the stamp is not the version this build reads.
3
+ *
4
+ * An options object rather than four positional arguments. `artifactPath` and
5
+ * `consequence` are both free-form strings, and transposed they typecheck: the
6
+ * fault's `artifactPath`, which callers read programmatically, would then hold
7
+ * a sentence.
8
+ */
9
+ export declare function checkSchemaVersion(options: {
10
+ readonly stamped: number;
11
+ readonly accepted: number;
12
+ readonly artifactPath: string;
13
+ /** Why an unequal stamp is a rejection for this artifact, as a clause. */
14
+ readonly consequence: string;
15
+ }): void;
@@ -17,9 +17,17 @@
17
17
  * happened to notice first.
18
18
  */
19
19
  import { RuntimeFault } from '../schemas/faults.js';
20
- /** `schema-version-mismatch`: the stamp is not the version this build reads. */
21
- export function checkSchemaVersion(stamped, accepted, artifactPath) {
20
+ /**
21
+ * `schema-version-mismatch`: the stamp is not the version this build reads.
22
+ *
23
+ * An options object rather than four positional arguments. `artifactPath` and
24
+ * `consequence` are both free-form strings, and transposed they typecheck: the
25
+ * fault's `artifactPath`, which callers read programmatically, would then hold
26
+ * a sentence.
27
+ */
28
+ export function checkSchemaVersion(options) {
29
+ const { stamped, accepted, artifactPath, consequence } = options;
22
30
  if (stamped === accepted)
23
31
  return;
24
- throw new RuntimeFault('schema-version-mismatch', artifactPath, `carries "schemaVersion" ${stamped} where this build reads ${accepted}; a contract written for another version is not read leniently, since its stale version would travel into the scoring version (AD-11)`);
32
+ throw new RuntimeFault('schema-version-mismatch', artifactPath, `carries "schemaVersion" ${stamped} where this build reads ${accepted}; an artifact written for another version is not read leniently, ${consequence} (AD-11)`);
25
33
  }
@@ -9,11 +9,13 @@
9
9
  * identifier up in at reduce time.
10
10
  */
11
11
  import { isSupportedInterfaceKind, SUPPORTED_KINDS_CLAUSE, } from '../compile/interface-inventory.js';
12
+ import { checkSchemaVersion } from '../compile/schema-version.js';
12
13
  import { checkInputsAgainstShape, isApiWitnessInputs, isMcpWitnessInputs, } from '../compile/sensitivity-witness.js';
13
14
  import { declaresNoRequiredKeys, isCommandOperation, isMcpOperation, } from '../declared-inputs.js';
14
15
  import { referenceSetKeysOf } from '../evaluate/evidence-resolution.js';
15
16
  import { StructuralFailure } from '../failure-codes.js';
16
17
  import { operationsOf } from '../schemas/interface.js';
18
+ import { PROBE_SCHEMA_VERSION, PROBE_SCHEMA_VERSION_CONSEQUENCE, } from '../schemas/probe.js';
17
19
  import { referenceSetMembers } from './witness-evidence.js';
18
20
  const requestOf = (legId, interfaceId, operation, inputs) => {
19
21
  // NFR9's correlation by identifier: the port echoes these back, and they
@@ -202,6 +204,19 @@ const selectControl = (contract, permittedInterfaces) => {
202
204
  */
203
205
  export const planPreflight = (input) => {
204
206
  const { contract, probes, runId } = input;
207
+ // First, and before any check reads a probe. AD-11 makes an unequal stamp a
208
+ // rejection rather than a degraded read, and every rule below is written
209
+ // against this version's field shapes. `compile` performs the same check for
210
+ // the contract; this is the probe's reader, since a probe reaches no other
211
+ // core stage on the pre-flight path.
212
+ for (const probe of probes) {
213
+ checkSchemaVersion({
214
+ stamped: probe.schemaVersion,
215
+ accepted: PROBE_SCHEMA_VERSION,
216
+ artifactPath: `Probe[probeId=${probe.probeId}].schemaVersion`,
217
+ consequence: PROBE_SCHEMA_VERSION_CONSEQUENCE,
218
+ });
219
+ }
205
220
  for (const iface of contract.permittedInterfaces) {
206
221
  // Already thrown at compile; asserted again because the plan is
207
222
  // reachable from a caller who assembled a contract by hand. Reads the
@@ -71,6 +71,47 @@ export declare const Defect: z.ZodObject<{
71
71
  relation: z.ZodType<import("./expression.ts").Expression, unknown, z.core.$ZodTypeInternals<import("./expression.ts").Expression, unknown>>;
72
72
  }, z.core.$strict>>;
73
73
  }, z.core.$strict>;
74
+ /**
75
+ * The probe's current schema version. `EVAL_CONTRACT_SCHEMA_VERSION` is the
76
+ * same thing for the eval contract.
77
+ *
78
+ * It exists for the reason that one does: `lineage.ts` keeps the field a plain
79
+ * integer so a stale artifact fails as AD-28's `schema-version-mismatch` rather
80
+ * than as an anonymous parse error, which puts the comparison on the reader.
81
+ * `compile` is that reader for a contract. A probe has two in this pipeline and
82
+ * both compare against this constant: `planPreflight` before it plans a leg,
83
+ * and `score` before it seals the probe. An unequal stamp leaves by the fault
84
+ * path rather than being read leniently.
85
+ *
86
+ * A third reader exists for a caller outside this package.
87
+ * `validateLineageChain` takes an `acceptedSchemaVersion` and raises the same
88
+ * code over a presented chain, and `Probe` carries lineage, so a caller who
89
+ * presents one gets the comparison there. It words the fault its own way, which
90
+ * is why the two spellings of `schema-version-mismatch` in this tree are not a
91
+ * drift.
92
+ *
93
+ * It is also the single place the number is written. The committed
94
+ * worked-example chains build their probes from it and `check:doc-claims` reads
95
+ * it to hold the published sentence that names it, where the number was a
96
+ * literal in three places that could disagree in silence.
97
+ */
98
+ export declare const PROBE_SCHEMA_VERSION = 5;
99
+ /**
100
+ * Why a stale probe stamp is a rejection, in the words the fault carries.
101
+ *
102
+ * One string for both readers. Two copies of a sentence this long disagree in
103
+ * silence exactly as the number did before `PROBE_SCHEMA_VERSION` existed, and
104
+ * it lives here because this is where a version bump is one edit.
105
+ *
106
+ * It splits the shapes by branch, because a clean control carries neither a
107
+ * `defectSignature` nor a manifestation witness: the signature is declared on
108
+ * the seeded branch below and a witness hangs off a `Defect`, which that branch
109
+ * bounds at zero. What every probe carries is the qualification record, whose
110
+ * arrival on both branches is what made the 1 to 2 bump breaking. A clean
111
+ * control is the probe a reader is most likely to meet this message with, so
112
+ * naming a field it does not have would be the wrong half to lead with.
113
+ */
114
+ export declare const PROBE_SCHEMA_VERSION_CONSEQUENCE: string;
74
115
  /**
75
116
  * The prior art's `expectedClean` conditional, re-expressed as a discriminated
76
117
  * union per AD-13 (a boolean literal discriminator parses on this pin,
@@ -48,6 +48,49 @@ const probeCommonFields = {
48
48
  rationale: z.string().min(1),
49
49
  qualification: ProbeQualification.describe('AD-9\'s qualification record: which of the five routes earned this probe its ground truth, and the evidence that route demands. Required on every branch and on every class, canaries included, because AD-9 closes with "an unqualified probe cannot enter a sealed set" and spells a route for all five kinds. That the route is compatible with this probe\'s class and `expectedClean` flag is a cross-field rule the export cannot carry; the corpus qualification gate enforces it and returns a reason code. Required, not optional, which with `defectSignature` below makes the probe\'s `schemaVersion` 1 -> 2 BREAKING bump under AD-11, whose rule is that "adding an optional field is a `schemaVersion` bump recorded in the field\'s own description; removing or retyping is breaking". This field is on both branches, so it alone is what stops every version-1 probe from parsing.'),
50
50
  };
51
+ /**
52
+ * The probe's current schema version. `EVAL_CONTRACT_SCHEMA_VERSION` is the
53
+ * same thing for the eval contract.
54
+ *
55
+ * It exists for the reason that one does: `lineage.ts` keeps the field a plain
56
+ * integer so a stale artifact fails as AD-28's `schema-version-mismatch` rather
57
+ * than as an anonymous parse error, which puts the comparison on the reader.
58
+ * `compile` is that reader for a contract. A probe has two in this pipeline and
59
+ * both compare against this constant: `planPreflight` before it plans a leg,
60
+ * and `score` before it seals the probe. An unequal stamp leaves by the fault
61
+ * path rather than being read leniently.
62
+ *
63
+ * A third reader exists for a caller outside this package.
64
+ * `validateLineageChain` takes an `acceptedSchemaVersion` and raises the same
65
+ * code over a presented chain, and `Probe` carries lineage, so a caller who
66
+ * presents one gets the comparison there. It words the fault its own way, which
67
+ * is why the two spellings of `schema-version-mismatch` in this tree are not a
68
+ * drift.
69
+ *
70
+ * It is also the single place the number is written. The committed
71
+ * worked-example chains build their probes from it and `check:doc-claims` reads
72
+ * it to hold the published sentence that names it, where the number was a
73
+ * literal in three places that could disagree in silence.
74
+ */
75
+ export const PROBE_SCHEMA_VERSION = 5;
76
+ /**
77
+ * Why a stale probe stamp is a rejection, in the words the fault carries.
78
+ *
79
+ * One string for both readers. Two copies of a sentence this long disagree in
80
+ * silence exactly as the number did before `PROBE_SCHEMA_VERSION` existed, and
81
+ * it lives here because this is where a version bump is one edit.
82
+ *
83
+ * It splits the shapes by branch, because a clean control carries neither a
84
+ * `defectSignature` nor a manifestation witness: the signature is declared on
85
+ * the seeded branch below and a witness hangs off a `Defect`, which that branch
86
+ * bounds at zero. What every probe carries is the qualification record, whose
87
+ * arrival on both branches is what made the 1 to 2 bump breaking. A clean
88
+ * control is the probe a reader is most likely to meet this message with, so
89
+ * naming a field it does not have would be the wrong half to lead with.
90
+ */
91
+ export const PROBE_SCHEMA_VERSION_CONSEQUENCE = 'since the stamp says which shapes the probe was authored against: the ' +
92
+ 'qualification record on every probe, and the witness legs and the defect ' +
93
+ 'signature grammar on a seeded one';
51
94
  /**
52
95
  * The prior art's `expectedClean` conditional, re-expressed as a discriminated
53
96
  * union per AD-13 (a boolean literal discriminator parses on this pin,
@@ -31,7 +31,7 @@
31
31
  import type { ValidatedObservations } from '../ingest/ingest.ts';
32
32
  import { type EvalContract } from '../schemas/eval-contract.ts';
33
33
  import type { Outcome } from '../schemas/evidence-artifact.ts';
34
- import type { Probe } from '../schemas/probe.ts';
34
+ import { type Probe } from '../schemas/probe.ts';
35
35
  import type { ScoringPolicy } from '../schemas/scoring-policy.ts';
36
36
  import type { ScoreStage } from '../stage-contracts.ts';
37
37
  import type { ContractAssessment, LadderResolution, ProductionAssessment } from './ladder.ts';
@@ -29,10 +29,12 @@
29
29
  * rather than a new one.
30
30
  */
31
31
  import { walkExpression } from '../compile/expression-legality.js';
32
+ import { checkSchemaVersion } from '../compile/schema-version.js';
32
33
  import { evaluateCoverage } from '../coverage/coverage.js';
33
34
  import { makePointerDenotesCollection, makeResolveOperand, referenceSetKeysOf, } from '../evaluate/evidence-resolution.js';
34
35
  import { resolveCheck } from '../evaluate/resolution.js';
35
36
  import { SEVERITY_LEVELS, } from '../schemas/eval-contract.js';
37
+ import { PROBE_SCHEMA_VERSION, PROBE_SCHEMA_VERSION_CONSEQUENCE, } from '../schemas/probe.js';
36
38
  import { buildPlanIndex } from '../seal/plan-index.js';
37
39
  import { resolveCapturedBindings, selectWithBindings } from './bindings.js';
38
40
  import { resolveContractVerdict, resolveProductionVerdict } from './ladder.js';
@@ -236,6 +238,27 @@ const INVALIDATING_OUTCOME_STATES = new Set(TRIAL_VOTE_STATES.invalidating);
236
238
  * each arrives named and explicit rather than a hardcoded literal.
237
239
  */
238
240
  export const score = (contract, trials, probe, preflightVerdict, policy, waiver, evaluationFault) => {
241
+ // First, and before the probe is sealed. AD-11 makes an unequal stamp a
242
+ // rejection rather than a degraded read. `compile` performs the same check
243
+ // for the contract.
244
+ //
245
+ // A throw rather than a reported outcome, against this stage's usual rule
246
+ // that a rejected probe is a domain result the ladder carries. Two reasons,
247
+ // and the second is the one that decides it. `qualifyProbe` returns a code
248
+ // from a closed vocabulary keyed to this version's field shapes, so a
249
+ // foreign probe's reason code is uninterpretable rather than merely
250
+ // unreliable. And the reporting path drops the fact: an
251
+ // `unqualified-probe-in-sealed-set` condition lands the run on the Invalid
252
+ // rung, where `RunScoreResult.artifact` is null, so there is no field for
253
+ // the reason to travel in. The fault carries an artifact path naming the
254
+ // probe and both numbers, which is the only path that tells a caller which
255
+ // probe to restamp.
256
+ checkSchemaVersion({
257
+ stamped: probe.schemaVersion,
258
+ accepted: PROBE_SCHEMA_VERSION,
259
+ artifactPath: `Probe[probeId=${probe.probeId}].schemaVersion`,
260
+ consequence: PROBE_SCHEMA_VERSION_CONSEQUENCE,
261
+ });
239
262
  // Probe sealing: once per run, never per trial, since qualification reads
240
263
  // the probe and the contract's operation inventory alone. `probeQualified`
241
264
  // reads whichever bucket the probe actually lands in -- never a throw on
package/dist/index.d.ts CHANGED
@@ -12,4 +12,4 @@ export type { ScoringPolicy } from './core/schemas/scoring-policy.ts';
12
12
  export type { SealedEvaluatorBrief } from './core/schemas/sealed-evaluator-brief.ts';
13
13
  export type { SealedRunRecord } from './core/schemas/sealed-run-record.ts';
14
14
  export type { FixtureReset, ManifestationWitness, SensitivityWitness, SensitivityWitnessLeg, WitnessChannel, WitnessInputs, } from './core/schemas/sensitivity-witness.ts';
15
- export declare const VERSION = "2.0.0";
15
+ export declare const VERSION = "3.0.0";
package/dist/index.js CHANGED
@@ -19,4 +19,4 @@
19
19
  // subpath, where AD-37 puts the conformance definition an adapter author
20
20
  // reads; the reference adapters stay at `eval-quality/adapters`.
21
21
  export * from './application/index.js';
22
- export const VERSION = '2.0.0';
22
+ export const VERSION = '3.0.0';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eval-quality",
3
- "version": "2.0.0",
3
+ "version": "3.0.0",
4
4
  "description": "Compile disciplined Behavioral Evaluation Contracts and score their ability to catch known defects.",
5
5
  "author": "Murat Ozcan",
6
6
  "license": "Apache-2.0",