eval-quality 1.2.0 → 1.4.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.
@@ -15,6 +15,8 @@ export type { RuntimeFaultCode } from '../core/schemas/faults.ts';
15
15
  export { RUNTIME_FAULT_CODES, RuntimeFault } from '../core/schemas/faults.ts';
16
16
  export type { EvaluatorRecommendation, Verdict, } from '../core/schemas/verdict.ts';
17
17
  export { EVALUATOR_RECOMMENDATIONS, VERDICTS, } from '../core/schemas/verdict.ts';
18
+ export type { QualificationFailure, QualificationFailureCode, QualificationResult, } from '../core/score/qualification.ts';
19
+ export { QUALIFICATION_FAILURES } from '../core/score/qualification.ts';
18
20
  export { compile } from './compile.ts';
19
21
  export type { Diagnostic, DiagnosticSink } from './diagnostics.ts';
20
22
  export type { PreflightFromObservationsOptions, RunPreflightOptions, } from './preflight.ts';
@@ -11,6 +11,7 @@ export { validateLineageChain } from '../core/lineage/chain.js';
11
11
  export { INTERCHANGE_ARTIFACT_KEYS } from '../core/schemas/artifact.js';
12
12
  export { RUNTIME_FAULT_CODES, RuntimeFault } from '../core/schemas/faults.js';
13
13
  export { EVALUATOR_RECOMMENDATIONS, VERDICTS, } from '../core/schemas/verdict.js';
14
+ export { QUALIFICATION_FAILURES } from '../core/score/qualification.js';
14
15
  export { compile } from './compile.js';
15
16
  export { preflightFromObservations, runPreflight } from './preflight.js';
16
17
  export { runScore } from './score.js';
@@ -8,6 +8,7 @@ import { Probe } from '../core/schemas/probe.ts';
8
8
  import { ScoringPolicy } from '../core/schemas/scoring-policy.ts';
9
9
  import { SealedRunRecord } from '../core/schemas/sealed-run-record.ts';
10
10
  import type { LadderResolution } from '../core/score/ladder.ts';
11
+ import type { QualificationResult } from '../core/score/qualification.ts';
11
12
  import { type CorpusPort } from '../ports/corpus-port.ts';
12
13
  export type RunScoreOptions = {
13
14
  readonly record: SealedRunRecord;
@@ -44,5 +45,16 @@ export type RunScoreResult = {
44
45
  */
45
46
  readonly artifact: EvidenceArtifact | null;
46
47
  readonly ladder: LadderResolution;
48
+ /**
49
+ * AD-9's gate over `options.probe`, carried out of `score` unchanged. A
50
+ * rejected probe resolves an oracle to `infrastructure-error` wherever no
51
+ * higher-precedence AD-33 row already resolved it, and any of those states
52
+ * lands the run on the Invalid rung, where `artifact` is `null` and there
53
+ * is no artifact field for the reason to travel in. A contract declaring no
54
+ * oracles resolves no outcome at all: that run mints an artifact carrying
55
+ * no trace of the rejection. `failures` is the reason in both cases, in the
56
+ * closed `QualificationFailureCode` vocabulary.
57
+ */
58
+ readonly qualification: QualificationResult;
47
59
  };
48
60
  export declare function runScore(options: RunScoreOptions): Promise<RunScoreResult>;
@@ -176,11 +176,19 @@ export async function runScore(options) {
176
176
  // identical gap.
177
177
  'none', false);
178
178
  if (scored.ladder.verdict === null) {
179
- return { artifact: null, ladder: scored.ladder };
179
+ return {
180
+ artifact: null,
181
+ ladder: scored.ladder,
182
+ qualification: scored.probeQualification,
183
+ };
180
184
  }
181
185
  const artifact = emit(scored, corpusDigest,
182
186
  // AD-11 names the same fixture digest `PreflightVerdict.fixtureDigest`
183
187
  // already carries: restated, never re-derived.
184
188
  preflightVerdict.fixtureDigest, record.evaluatorConfigurationDigest);
185
- return { artifact, ladder: scored.ladder };
189
+ return {
190
+ artifact,
191
+ ladder: scored.ladder,
192
+ qualification: scored.probeQualification,
193
+ };
186
194
  }
@@ -1,4 +1,4 @@
1
- import { type Diagnostic } from '../application/index.ts';
1
+ import { type Diagnostic, type QualificationFailure } from '../application/index.ts';
2
2
  /**
3
3
  * Delegates to `serializeArtifact`; the canonical bytes are not re-derived
4
4
  * here, so the text written to stdout is the text `digestArtifact` hashes.
@@ -14,6 +14,14 @@ export declare function renderDiagnostic(diagnostic: Diagnostic): string;
14
14
  * own code looks like from outside.
15
15
  */
16
16
  export declare function renderError(error: unknown): string;
17
+ /**
18
+ * One AD-9 qualification failure, in the same
19
+ * `<code>: <artifactPath>: <detail>` shape `renderError` uses. An unqualified
20
+ * probe is a domain outcome the ladder resolves to Invalid, so no
21
+ * `StructuralFailure` and no `RuntimeFault` carries it. It reads like one on
22
+ * stderr because the reader's question is the same.
23
+ */
24
+ export declare function renderQualificationFailure(failure: QualificationFailure): string;
17
25
  /**
18
26
  * AD-21's seven exit codes, one line each. The `--help` output and the README
19
27
  * table are this text, so the two cannot drift.
@@ -1,7 +1,8 @@
1
1
  /**
2
2
  * The four shapes the binary writes, and the exit-code table two documents
3
3
  * share. Every line the CLI emits is produced here, so a format change is one
4
- * file.
4
+ * file. Five renderers over the four shapes: a qualification failure and an
5
+ * error both print `<code>: <artifactPath>: <detail>`.
5
6
  */
6
7
  import { z } from 'zod';
7
8
  import { RuntimeFault, StructuralFailure, serializeArtifact, } from '../application/index.js';
@@ -81,6 +82,16 @@ export function renderError(error) {
81
82
  }
82
83
  return `${PREFIX}: ${String(error)}`;
83
84
  }
85
+ /**
86
+ * One AD-9 qualification failure, in the same
87
+ * `<code>: <artifactPath>: <detail>` shape `renderError` uses. An unqualified
88
+ * probe is a domain outcome the ladder resolves to Invalid, so no
89
+ * `StructuralFailure` and no `RuntimeFault` carries it. It reads like one on
90
+ * stderr because the reader's question is the same.
91
+ */
92
+ export function renderQualificationFailure(failure) {
93
+ return `${PREFIX}: ${failure.code}: ${failure.artifactPath}: ${failure.detail}`;
94
+ }
84
95
  /**
85
96
  * AD-21's seven exit codes, one line each. The `--help` output and the README
86
97
  * table are this text, so the two cannot drift.
package/dist/cli/run.js CHANGED
@@ -6,7 +6,7 @@
6
6
  * run in memory.
7
7
  */
8
8
  import { compile, preflightFromObservations, RuntimeFault, runScore, StructuralFailure, seal, } from '../application/index.js';
9
- import { EXIT_CODE_TABLE, renderArtifact, renderDiagnostic, renderError, renderUsage, } from './render.js';
9
+ import { EXIT_CODE_TABLE, renderArtifact, renderDiagnostic, renderError, renderQualificationFailure, renderUsage, } from './render.js';
10
10
  export const APPLICATION = {
11
11
  compile,
12
12
  seal,
@@ -316,6 +316,14 @@ async function runScoreCommand(invocation, environment, application, target) {
316
316
  port: corpusRoot === null ? undefined : environment.corpusPort(corpusRoot),
317
317
  signal: environment.signal,
318
318
  });
319
+ // A rejected probe resolves an oracle to `infrastructure-error` wherever no
320
+ // higher-precedence AD-33 row already resolved it, and no artifact field
321
+ // carries the reason on any rung. Written whatever the rung, since the gate
322
+ // also runs on a probe the ladder never invalidated, a contract declaring
323
+ // no oracles above all: the reasons are the same either way.
324
+ for (const failure of result.qualification.failures) {
325
+ environment.writeDiagnostic(renderQualificationFailure(failure));
326
+ }
319
327
  if (result.artifact !== null) {
320
328
  await emitArtifact(environment, result.artifact, 'score', target);
321
329
  }
@@ -59,11 +59,15 @@ export declare function regexMatch(value: ResolvedValue, pattern: string, matchS
59
59
  */
60
60
  export declare function ordering(collection: ResolvedValue, key: string, order: 'ascending' | 'descending', _artifactPath: string): boolean;
61
61
  /**
62
- * An empty array is a legitimate zero count, never special-cased; the
63
- * resolver in `resolution.ts` intercepts before this runs on a genuinely
64
- * empty collection. The allowed deviation is compared unrounded: `actual` is
65
- * an integer, so `<=` against a fractional deviation is already exact, and
66
- * rounding either direction would move the declared boundary.
62
+ * An empty array is a legitimate zero count, never special-cased, and this
63
+ * function is called with one. `count-tolerance` reads the collection's
64
+ * cardinality, so `resolution.ts` marks it `total` over a collection observed
65
+ * to be present and empty and hands the `[]` straight through; the guard below
66
+ * is load-bearing on `collection.length === 0`. A collection-typed pointer
67
+ * that resolved `absent` is still intercepted there and never reaches here.
68
+ * The allowed deviation is compared unrounded: `actual` is an integer, so `<=`
69
+ * against a fractional deviation is already exact, and rounding either
70
+ * direction would move the declared boundary.
67
71
  */
68
72
  export declare function countTolerance(collection: ResolvedValue, expected: number, tolerance: number, relative: boolean, _artifactPath: string): boolean;
69
73
  /**
@@ -249,11 +249,15 @@ export function ordering(collection, key, order, _artifactPath) {
249
249
  return true;
250
250
  }
251
251
  /**
252
- * An empty array is a legitimate zero count, never special-cased; the
253
- * resolver in `resolution.ts` intercepts before this runs on a genuinely
254
- * empty collection. The allowed deviation is compared unrounded: `actual` is
255
- * an integer, so `<=` against a fractional deviation is already exact, and
256
- * rounding either direction would move the declared boundary.
252
+ * An empty array is a legitimate zero count, never special-cased, and this
253
+ * function is called with one. `count-tolerance` reads the collection's
254
+ * cardinality, so `resolution.ts` marks it `total` over a collection observed
255
+ * to be present and empty and hands the `[]` straight through; the guard below
256
+ * is load-bearing on `collection.length === 0`. A collection-typed pointer
257
+ * that resolved `absent` is still intercepted there and never reaches here.
258
+ * The allowed deviation is compared unrounded: `actual` is an integer, so `<=`
259
+ * against a fractional deviation is already exact, and rounding either
260
+ * direction would move the declared boundary.
257
261
  */
258
262
  export function countTolerance(collection, expected, tolerance, relative, _artifactPath) {
259
263
  if (collection === ABSENT || !Array.isArray(collection))
@@ -1,21 +1,28 @@
1
1
  import { absence, containment, countTolerance, coversByKey, deepEquality, equality, existence, keyValueOf, ordering, regexMatch, setMembership, shape, } from './operators.js';
2
2
  import { ABSENT } from './resolved-value.js';
3
3
  /**
4
- * AD-4's one closed introduction condition, checked per operand and applied
5
- * uniformly: the resolved value and its operand are the only inputs.
4
+ * AD-4's one closed introduction condition, checked per operand. Two inputs
5
+ * decide it: what the operand resolved to, and whether the operator can answer
6
+ * from an empty collection.
6
7
  *
7
- * Two permanent consequences of that uniform reading, the same class as a
8
- * `{ literal: [] }` operand tripping it: a `count-tolerance` node asserting
9
- * `expected: 0` over a genuinely empty collection can never resolve `true`,
10
- * because this interception fires first; and `existence` over a pointer
11
- * resolving to a present-but-empty array resolves `insufficient-evidence`, not
12
- * `true`, even though `existence` only asks about presence.
8
+ * An operand that resolved to a present, empty array reached an evidence
9
+ * channel that worked and had nothing in it. That stops a `needs-a-member`
10
+ * operator, which would otherwise report a vacuous truth over no elements.
11
+ * A `total` operator has its answer: the cardinality is zero, the value is
12
+ * present. AD-4's own rule keeps a detected defect a detection, and that is
13
+ * what makes the second case a resolution.
14
+ *
15
+ * The `absent` branch does not vary. A collection-typed pointer that did not
16
+ * resolve is the missing page AD-4 folds into this condition to close the
17
+ * soft-delete fail-open, and no operator gets to read a missing collection as
18
+ * an empty one.
13
19
  */
14
- function operandDenotesEmptyCollection(resolved, operand, pointerDenotesCollection) {
15
- if (Array.isArray(resolved) && resolved.length === 0)
16
- return true;
17
- if (resolved !== ABSENT)
18
- return false;
20
+ function operandDenotesEmptyCollection(resolved, operand, pointerDenotesCollection, totality) {
21
+ if (resolved !== ABSENT) {
22
+ if (totality === 'total')
23
+ return false;
24
+ return Array.isArray(resolved) && resolved.length === 0;
25
+ }
19
26
  // Only a `{ pointer }` operand can carry a declared collection type.
20
27
  // `{ literal }` never resolves ABSENT, and an ABSENT `{ referenceSet }`
21
28
  // means `unresolved-reference-set` slipped past compilation, which this
@@ -38,8 +45,8 @@ function booleanResult(result) {
38
45
  }
39
46
  // Checked across every operand before any operator runs, so the interception
40
47
  // replaces a leaf's own two-valued answer outright.
41
- function anyOperandEmpty(pairs, pointerDenotesCollection) {
42
- return pairs.some(({ operand, resolved }) => operandDenotesEmptyCollection(resolved, operand, pointerDenotesCollection));
48
+ function anyOperandEmpty(pairs, pointerDenotesCollection, totality) {
49
+ return pairs.some(({ operand, resolved }) => operandDenotesEmptyCollection(resolved, operand, pointerDenotesCollection, totality));
43
50
  }
44
51
  /** `not(insufficient-evidence)` is terminal under both polarities (AD-4). */
45
52
  function notOf(child) {
@@ -107,10 +114,12 @@ function resolveQuantifier(op, collectionOperand, predicate, boundElement, ctx)
107
114
  };
108
115
  }
109
116
  // Shared by the six single-operand leaves: resolve, intercept on the
110
- // empty-collection condition, otherwise hand the value to the operator.
111
- function resolveSingleOperand(operand, boundElement, ctx, evaluate) {
117
+ // empty-collection condition, otherwise hand the value to the operator. Each
118
+ // caller declares its own totality, since that is the one thing the six
119
+ // disagree on.
120
+ function resolveSingleOperand(operand, boundElement, ctx, totality, evaluate) {
112
121
  const resolved = ctx.resolveOperand(operand, boundElement, ctx.artifactPath);
113
- if (anyOperandEmpty([{ operand, resolved }], ctx.pointerDenotesCollection)) {
122
+ if (anyOperandEmpty([{ operand, resolved }], ctx.pointerDenotesCollection, totality)) {
114
123
  return emptyCollectionResult();
115
124
  }
116
125
  return booleanResult(evaluate(resolved));
@@ -124,7 +133,7 @@ function resolveEqualityLike(operands, evaluate, boundElement, ctx) {
124
133
  if (anyOperandEmpty([
125
134
  { operand: aOperand, resolved: a },
126
135
  { operand: bOperand, resolved: b },
127
- ], ctx.pointerDenotesCollection)) {
136
+ ], ctx.pointerDenotesCollection, 'needs-a-member')) {
128
137
  return emptyCollectionResult();
129
138
  }
130
139
  return booleanResult(evaluate(a, b, ctx.artifactPath));
@@ -212,7 +221,7 @@ function resolveContainmentNode(expression, boundElement, ctx) {
212
221
  if (anyOperandEmpty([
213
222
  { operand: containerOperand, resolved: container },
214
223
  { operand: candidateOperand, resolved: candidate },
215
- ], ctx.pointerDenotesCollection)) {
224
+ ], ctx.pointerDenotesCollection, 'needs-a-member')) {
216
225
  return emptyCollectionResult();
217
226
  }
218
227
  // The array-narrowing guard applies only to a `{ referenceSet }` candidate.
@@ -231,16 +240,16 @@ function resolveContainmentNode(expression, boundElement, ctx) {
231
240
  }
232
241
  function resolveExistenceNode(expression, boundElement, ctx) {
233
242
  const [operand] = expression.operands;
234
- return resolveSingleOperand(operand, boundElement, ctx, (resolved) => existence(resolved, ctx.artifactPath));
243
+ return resolveSingleOperand(operand, boundElement, ctx, 'total', (resolved) => existence(resolved, ctx.artifactPath));
235
244
  }
236
245
  function resolveAbsenceNode(expression, boundElement, ctx) {
237
246
  const [operand] = expression.operands;
238
- return resolveSingleOperand(operand, boundElement, ctx, (resolved) => absence(resolved, ctx.artifactPath));
247
+ return resolveSingleOperand(operand, boundElement, ctx, 'total', (resolved) => absence(resolved, ctx.artifactPath));
239
248
  }
240
249
  function resolveRegexNode(expression, boundElement, ctx) {
241
250
  const [operand] = expression.operands;
242
251
  const { pattern } = expression;
243
- return resolveSingleOperand(operand, boundElement, ctx, (resolved) => regexMatch(resolved, pattern, ctx.regexMatchStepBudget, ctx.artifactPath));
252
+ return resolveSingleOperand(operand, boundElement, ctx, 'needs-a-member', (resolved) => regexMatch(resolved, pattern, ctx.regexMatchStepBudget, ctx.artifactPath));
244
253
  }
245
254
  /**
246
255
  * Reads the single declared key off each member of a `{ referenceSet }` set
@@ -308,7 +317,7 @@ function resolveSetMembershipNode(expression, boundElement, ctx) {
308
317
  if (anyOperandEmpty([
309
318
  { operand: valueOperand, resolved: value },
310
319
  { operand: setOperand, resolved: resolvedSet },
311
- ], ctx.pointerDenotesCollection)) {
320
+ ], ctx.pointerDenotesCollection, 'needs-a-member')) {
312
321
  return emptyCollectionResult();
313
322
  }
314
323
  // The set position needs the `JsonValue[]` the schema already guarantees
@@ -326,17 +335,17 @@ function resolveSetMembershipNode(expression, boundElement, ctx) {
326
335
  function resolveOrderingNode(expression, boundElement, ctx) {
327
336
  const [operand] = expression.operands;
328
337
  const { key, order } = expression;
329
- return resolveSingleOperand(operand, boundElement, ctx, (resolved) => ordering(resolved, key, order, ctx.artifactPath));
338
+ return resolveSingleOperand(operand, boundElement, ctx, 'needs-a-member', (resolved) => ordering(resolved, key, order, ctx.artifactPath));
330
339
  }
331
340
  function resolveCountToleranceNode(expression, boundElement, ctx) {
332
341
  const [operand] = expression.operands;
333
342
  const { expected, tolerance, relative } = expression;
334
- return resolveSingleOperand(operand, boundElement, ctx, (resolved) => countTolerance(resolved, expected, tolerance, relative, ctx.artifactPath));
343
+ return resolveSingleOperand(operand, boundElement, ctx, 'total', (resolved) => countTolerance(resolved, expected, tolerance, relative, ctx.artifactPath));
335
344
  }
336
345
  function resolveShapeNode(expression, boundElement, ctx) {
337
346
  const [operand] = expression.operands;
338
347
  const { descriptor } = expression;
339
- return resolveSingleOperand(operand, boundElement, ctx, (resolved) => shape(resolved, descriptor, ctx.artifactPath));
348
+ return resolveSingleOperand(operand, boundElement, ctx, 'needs-a-member', (resolved) => shape(resolved, descriptor, ctx.artifactPath));
340
349
  }
341
350
  // One entry per `Expression['op']`; the mapped type below fails to compile if
342
351
  // an op is missing or misassigned, since each key demands the handler typed
@@ -65,6 +65,62 @@ const sameFixtureState = (left, right) => {
65
65
  return (digestArtifact(state(left), PREFLIGHT_ARTIFACT_PATH) ===
66
66
  digestArtifact(state(right), PREFLIGHT_ARTIFACT_PATH));
67
67
  };
68
+ /**
69
+ * The canonical digest of one value, or `null` where the value holds something
70
+ * RFC 8785 cannot serialise. `JsonValue` admits an integer outside the safe
71
+ * range and a lone surrogate, and a 64-bit identifier in a query parameter is
72
+ * ordinary, so this is reachable from a contract that parses. A verdict is what
73
+ * this stage owes its caller, and `null` compares equal to nothing, so a value
74
+ * that cannot be digested leaves the two sides distinguishable and the check
75
+ * still reads the leg.
76
+ */
77
+ const digestOrNull = (value) => {
78
+ try {
79
+ return digestArtifact(value, PREFLIGHT_ARTIFACT_PATH);
80
+ }
81
+ catch (error) {
82
+ if (error instanceof RuntimeFault)
83
+ return null;
84
+ throw error;
85
+ }
86
+ };
87
+ /**
88
+ * Whether two legs issued one request and received one answer, which makes them
89
+ * one probe under two labels. A manifestation witness firing on such a leg is
90
+ * the fault leg's own manifestation read a second time, and it establishes
91
+ * nothing about where the defect is scoped.
92
+ *
93
+ * Both halves are required. Answers alone would drop AD-10's own worked example,
94
+ * two distinct nonexistent identifiers both returning 404: those legs ask
95
+ * different questions and are exactly the legs this check exists to read.
96
+ * Requests alone are what the plan can see, and identical requests can still be
97
+ * answered differently, which is why the comparison lives here where the
98
+ * answers are in hand.
99
+ *
100
+ * The answer half compares the evidence, which is everything a relation can
101
+ * address: two legs with equal evidence resolve one relation to one value. It
102
+ * carries AD-11's projected body, so a field the operation declares volatile is
103
+ * already out of it and a server-minted identifier stops being a difference,
104
+ * which is what makes the same request to a mutating operation comparable at
105
+ * all. The raw observation is the wrong side of this comparison for that exact
106
+ * reason: two writes to one collection differ on a minted id by design, and
107
+ * reading that as a difference puts the false failure this check just lost back
108
+ * one stage over.
109
+ *
110
+ * The correlation identifiers are neutralised on both sides, since they are the
111
+ * leg id and differ by construction. A digest that comes back `null` matches
112
+ * nothing, so a pair that cannot be compared stays a pair the check reads.
113
+ */
114
+ const answeredAlike = (left, right) => {
115
+ const request = (state) => digestOrNull({ ...state.leg.request, probeId: '' });
116
+ const answer = (state) => digestOrNull({ ...state.evidence, observationId: '' });
117
+ const leftRequest = request(left);
118
+ const leftAnswer = answer(left);
119
+ return (leftRequest !== null &&
120
+ leftAnswer !== null &&
121
+ leftRequest === request(right) &&
122
+ leftAnswer === answer(right));
123
+ };
68
124
  /**
69
125
  * Resolves a manifestation witness against one leg. Returns `null` when that
70
126
  * leg produced no observation, which the two seeded-fault rows read
@@ -175,7 +231,36 @@ export const reducePreflight = (plan, { observations }) => {
175
231
  }
176
232
  case 'seeded-faults-scoped': {
177
233
  const { witness, defectId } = planned;
234
+ const fault = states.get(witness.legId);
235
+ // The legs that answered a different question than the fault leg's, and
236
+ // the legs dropped for answering the same one.
237
+ const examined = [];
238
+ const dropped = [];
178
239
  for (const legId of planned.cleanLegIds) {
240
+ const state = states.get(legId);
241
+ if (state !== undefined &&
242
+ fault !== undefined &&
243
+ answeredAlike(state, fault)) {
244
+ dropped.push(legId);
245
+ continue;
246
+ }
247
+ examined.push(legId);
248
+ }
249
+ // Emptiness is tested on what survived the drop. A check over no clean
250
+ // leg examined nothing, and a check that examined nothing has
251
+ // established nothing, which is the rule the `input-sensitivity` row
252
+ // above already runs on. Satisfied here would certify scoping from zero
253
+ // evidence on the three contracts least able to afford it: one whose
254
+ // defect names the only leg its operation has, one whose every other leg
255
+ // repeats the fault leg's probe, and one where the plan named legs and
256
+ // the drop took all of them. The note says which.
257
+ if (examined.length === 0) {
258
+ const named = dropped.map((legId) => `"${legId}"`).join(', ');
259
+ return check(planned.kind, witness.operationId, 'failed', dropped.length === 0
260
+ ? `${defectId}: the operation has no leg besides the fault leg, so nothing here establishes that the defect is scoped to it`
261
+ : `${defectId}: every other leg of the operation issued the fault leg's own request and received its answer (${named}), so nothing here establishes that the defect is scoped to it`);
262
+ }
263
+ for (const legId of examined) {
179
264
  const resolved = resolveAgainst(witness, states.get(legId), plan, PREFLIGHT_ARTIFACT_PATH);
180
265
  if (resolved === 'true')
181
266
  return check(planned.kind, witness.operationId, 'failed', `${defectId}: the manifestation witness fires on clean leg "${legId}"`);
@@ -828,7 +828,7 @@ export declare const INTERCHANGE_ARTIFACTS: {
828
828
  legId: z.ZodString;
829
829
  interfaceId: z.ZodString;
830
830
  operationId: z.ZodString;
831
- inputs: z.ZodObject<{
831
+ inputs: z.ZodUnion<readonly [z.ZodObject<{
832
832
  path: z.ZodType<import("./primitives.ts").JsonObject, unknown, z.core.$ZodTypeInternals<import("./primitives.ts").JsonObject, unknown>>;
833
833
  query: z.ZodType<import("./primitives.ts").JsonObject, unknown, z.core.$ZodTypeInternals<import("./primitives.ts").JsonObject, unknown>>;
834
834
  header: z.ZodRecord<z.ZodString, z.ZodString>;
@@ -838,7 +838,20 @@ export declare const INTERCHANGE_ARTIFACTS: {
838
838
  }, z.core.$strict>, z.ZodObject<{
839
839
  kind: z.ZodLiteral<"absent">;
840
840
  }, z.core.$strict>], "kind">;
841
- }, z.core.$strict>;
841
+ }, z.core.$strict>, z.ZodObject<{
842
+ argument: z.ZodType<import("./primitives.ts").JsonObject, unknown, z.core.$ZodTypeInternals<import("./primitives.ts").JsonObject, unknown>>;
843
+ option: z.ZodType<import("./primitives.ts").JsonObject, unknown, z.core.$ZodTypeInternals<import("./primitives.ts").JsonObject, unknown>>;
844
+ environment: z.ZodRecord<z.ZodString, z.ZodString>;
845
+ stdin: z.ZodDiscriminatedUnion<[z.ZodObject<{
846
+ kind: z.ZodLiteral<"json">;
847
+ value: z.ZodType<import("./primitives.ts").JsonValue, unknown, z.core.$ZodTypeInternals<import("./primitives.ts").JsonValue, unknown>>;
848
+ }, z.core.$strict>, z.ZodObject<{
849
+ kind: z.ZodLiteral<"text">;
850
+ value: z.ZodString;
851
+ }, z.core.$strict>, z.ZodObject<{
852
+ kind: z.ZodLiteral<"absent">;
853
+ }, z.core.$strict>], "kind">;
854
+ }, z.core.$strict>]>;
842
855
  }, z.core.$strict>>;
843
856
  }, z.core.$strict>;
844
857
  readonly priorArt: 'eval-contract';
@@ -1404,7 +1417,7 @@ export declare const INTERCHANGE_ARTIFACTS: {
1404
1417
  legId: z.ZodString;
1405
1418
  interfaceId: z.ZodString;
1406
1419
  operationId: z.ZodString;
1407
- inputs: z.ZodObject<{
1420
+ inputs: z.ZodUnion<readonly [z.ZodObject<{
1408
1421
  path: z.ZodType<import("./primitives.ts").JsonObject, unknown, z.core.$ZodTypeInternals<import("./primitives.ts").JsonObject, unknown>>;
1409
1422
  query: z.ZodType<import("./primitives.ts").JsonObject, unknown, z.core.$ZodTypeInternals<import("./primitives.ts").JsonObject, unknown>>;
1410
1423
  header: z.ZodRecord<z.ZodString, z.ZodString>;
@@ -1414,7 +1427,20 @@ export declare const INTERCHANGE_ARTIFACTS: {
1414
1427
  }, z.core.$strict>, z.ZodObject<{
1415
1428
  kind: z.ZodLiteral<"absent">;
1416
1429
  }, z.core.$strict>], "kind">;
1417
- }, z.core.$strict>;
1430
+ }, z.core.$strict>, z.ZodObject<{
1431
+ argument: z.ZodType<import("./primitives.ts").JsonObject, unknown, z.core.$ZodTypeInternals<import("./primitives.ts").JsonObject, unknown>>;
1432
+ option: z.ZodType<import("./primitives.ts").JsonObject, unknown, z.core.$ZodTypeInternals<import("./primitives.ts").JsonObject, unknown>>;
1433
+ environment: z.ZodRecord<z.ZodString, z.ZodString>;
1434
+ stdin: z.ZodDiscriminatedUnion<[z.ZodObject<{
1435
+ kind: z.ZodLiteral<"json">;
1436
+ value: z.ZodType<import("./primitives.ts").JsonValue, unknown, z.core.$ZodTypeInternals<import("./primitives.ts").JsonValue, unknown>>;
1437
+ }, z.core.$strict>, z.ZodObject<{
1438
+ kind: z.ZodLiteral<"text">;
1439
+ value: z.ZodString;
1440
+ }, z.core.$strict>, z.ZodObject<{
1441
+ kind: z.ZodLiteral<"absent">;
1442
+ }, z.core.$strict>], "kind">;
1443
+ }, z.core.$strict>]>;
1418
1444
  relation: z.ZodType<import("./expression.ts").Expression, unknown, z.core.$ZodTypeInternals<import("./expression.ts").Expression, unknown>>;
1419
1445
  }, z.core.$strict>>;
1420
1446
  }, z.core.$strict>>;
@@ -1587,7 +1613,7 @@ export declare const INTERCHANGE_ARTIFACTS: {
1587
1613
  legId: z.ZodString;
1588
1614
  interfaceId: z.ZodString;
1589
1615
  operationId: z.ZodString;
1590
- inputs: z.ZodObject<{
1616
+ inputs: z.ZodUnion<readonly [z.ZodObject<{
1591
1617
  path: z.ZodType<import("./primitives.ts").JsonObject, unknown, z.core.$ZodTypeInternals<import("./primitives.ts").JsonObject, unknown>>;
1592
1618
  query: z.ZodType<import("./primitives.ts").JsonObject, unknown, z.core.$ZodTypeInternals<import("./primitives.ts").JsonObject, unknown>>;
1593
1619
  header: z.ZodRecord<z.ZodString, z.ZodString>;
@@ -1597,7 +1623,20 @@ export declare const INTERCHANGE_ARTIFACTS: {
1597
1623
  }, z.core.$strict>, z.ZodObject<{
1598
1624
  kind: z.ZodLiteral<"absent">;
1599
1625
  }, z.core.$strict>], "kind">;
1600
- }, z.core.$strict>;
1626
+ }, z.core.$strict>, z.ZodObject<{
1627
+ argument: z.ZodType<import("./primitives.ts").JsonObject, unknown, z.core.$ZodTypeInternals<import("./primitives.ts").JsonObject, unknown>>;
1628
+ option: z.ZodType<import("./primitives.ts").JsonObject, unknown, z.core.$ZodTypeInternals<import("./primitives.ts").JsonObject, unknown>>;
1629
+ environment: z.ZodRecord<z.ZodString, z.ZodString>;
1630
+ stdin: z.ZodDiscriminatedUnion<[z.ZodObject<{
1631
+ kind: z.ZodLiteral<"json">;
1632
+ value: z.ZodType<import("./primitives.ts").JsonValue, unknown, z.core.$ZodTypeInternals<import("./primitives.ts").JsonValue, unknown>>;
1633
+ }, z.core.$strict>, z.ZodObject<{
1634
+ kind: z.ZodLiteral<"text">;
1635
+ value: z.ZodString;
1636
+ }, z.core.$strict>, z.ZodObject<{
1637
+ kind: z.ZodLiteral<"absent">;
1638
+ }, z.core.$strict>], "kind">;
1639
+ }, z.core.$strict>]>;
1601
1640
  relation: z.ZodType<import("./expression.ts").Expression, unknown, z.core.$ZodTypeInternals<import("./expression.ts").Expression, unknown>>;
1602
1641
  }, z.core.$strict>>;
1603
1642
  }, z.core.$strict>>;
@@ -924,7 +924,7 @@ export declare const EvalContract: z.ZodObject<{
924
924
  legId: z.ZodString;
925
925
  interfaceId: z.ZodString;
926
926
  operationId: z.ZodString;
927
- inputs: z.ZodObject<{
927
+ inputs: z.ZodUnion<readonly [z.ZodObject<{
928
928
  path: z.ZodType<import("./primitives.ts").JsonObject, unknown, z.core.$ZodTypeInternals<import("./primitives.ts").JsonObject, unknown>>;
929
929
  query: z.ZodType<import("./primitives.ts").JsonObject, unknown, z.core.$ZodTypeInternals<import("./primitives.ts").JsonObject, unknown>>;
930
930
  header: z.ZodRecord<z.ZodString, z.ZodString>;
@@ -934,7 +934,20 @@ export declare const EvalContract: z.ZodObject<{
934
934
  }, z.core.$strict>, z.ZodObject<{
935
935
  kind: z.ZodLiteral<"absent">;
936
936
  }, z.core.$strict>], "kind">;
937
- }, z.core.$strict>;
937
+ }, z.core.$strict>, z.ZodObject<{
938
+ argument: z.ZodType<import("./primitives.ts").JsonObject, unknown, z.core.$ZodTypeInternals<import("./primitives.ts").JsonObject, unknown>>;
939
+ option: z.ZodType<import("./primitives.ts").JsonObject, unknown, z.core.$ZodTypeInternals<import("./primitives.ts").JsonObject, unknown>>;
940
+ environment: z.ZodRecord<z.ZodString, z.ZodString>;
941
+ stdin: z.ZodDiscriminatedUnion<[z.ZodObject<{
942
+ kind: z.ZodLiteral<"json">;
943
+ value: z.ZodType<import("./primitives.ts").JsonValue, unknown, z.core.$ZodTypeInternals<import("./primitives.ts").JsonValue, unknown>>;
944
+ }, z.core.$strict>, z.ZodObject<{
945
+ kind: z.ZodLiteral<"text">;
946
+ value: z.ZodString;
947
+ }, z.core.$strict>, z.ZodObject<{
948
+ kind: z.ZodLiteral<"absent">;
949
+ }, z.core.$strict>], "kind">;
950
+ }, z.core.$strict>]>;
938
951
  }, z.core.$strict>>;
939
952
  }, z.core.$strict>;
940
953
  export type EvalContract = z.infer<typeof EvalContract>;
@@ -42,7 +42,7 @@ export declare const Defect: z.ZodObject<{
42
42
  legId: z.ZodString;
43
43
  interfaceId: z.ZodString;
44
44
  operationId: z.ZodString;
45
- inputs: z.ZodObject<{
45
+ inputs: z.ZodUnion<readonly [z.ZodObject<{
46
46
  path: z.ZodType<import("./primitives.ts").JsonObject, unknown, z.core.$ZodTypeInternals<import("./primitives.ts").JsonObject, unknown>>;
47
47
  query: z.ZodType<import("./primitives.ts").JsonObject, unknown, z.core.$ZodTypeInternals<import("./primitives.ts").JsonObject, unknown>>;
48
48
  header: z.ZodRecord<z.ZodString, z.ZodString>;
@@ -52,7 +52,20 @@ export declare const Defect: z.ZodObject<{
52
52
  }, z.core.$strict>, z.ZodObject<{
53
53
  kind: z.ZodLiteral<"absent">;
54
54
  }, z.core.$strict>], "kind">;
55
- }, z.core.$strict>;
55
+ }, z.core.$strict>, z.ZodObject<{
56
+ argument: z.ZodType<import("./primitives.ts").JsonObject, unknown, z.core.$ZodTypeInternals<import("./primitives.ts").JsonObject, unknown>>;
57
+ option: z.ZodType<import("./primitives.ts").JsonObject, unknown, z.core.$ZodTypeInternals<import("./primitives.ts").JsonObject, unknown>>;
58
+ environment: z.ZodRecord<z.ZodString, z.ZodString>;
59
+ stdin: z.ZodDiscriminatedUnion<[z.ZodObject<{
60
+ kind: z.ZodLiteral<"json">;
61
+ value: z.ZodType<import("./primitives.ts").JsonValue, unknown, z.core.$ZodTypeInternals<import("./primitives.ts").JsonValue, unknown>>;
62
+ }, z.core.$strict>, z.ZodObject<{
63
+ kind: z.ZodLiteral<"text">;
64
+ value: z.ZodString;
65
+ }, z.core.$strict>, z.ZodObject<{
66
+ kind: z.ZodLiteral<"absent">;
67
+ }, z.core.$strict>], "kind">;
68
+ }, z.core.$strict>]>;
56
69
  relation: z.ZodType<import("./expression.ts").Expression, unknown, z.core.$ZodTypeInternals<import("./expression.ts").Expression, unknown>>;
57
70
  }, z.core.$strict>>;
58
71
  }, z.core.$strict>;
@@ -234,7 +247,7 @@ export declare const Probe: z.ZodDiscriminatedUnion<[z.ZodObject<{
234
247
  legId: z.ZodString;
235
248
  interfaceId: z.ZodString;
236
249
  operationId: z.ZodString;
237
- inputs: z.ZodObject<{
250
+ inputs: z.ZodUnion<readonly [z.ZodObject<{
238
251
  path: z.ZodType<import("./primitives.ts").JsonObject, unknown, z.core.$ZodTypeInternals<import("./primitives.ts").JsonObject, unknown>>;
239
252
  query: z.ZodType<import("./primitives.ts").JsonObject, unknown, z.core.$ZodTypeInternals<import("./primitives.ts").JsonObject, unknown>>;
240
253
  header: z.ZodRecord<z.ZodString, z.ZodString>;
@@ -244,7 +257,20 @@ export declare const Probe: z.ZodDiscriminatedUnion<[z.ZodObject<{
244
257
  }, z.core.$strict>, z.ZodObject<{
245
258
  kind: z.ZodLiteral<"absent">;
246
259
  }, z.core.$strict>], "kind">;
247
- }, z.core.$strict>;
260
+ }, z.core.$strict>, z.ZodObject<{
261
+ argument: z.ZodType<import("./primitives.ts").JsonObject, unknown, z.core.$ZodTypeInternals<import("./primitives.ts").JsonObject, unknown>>;
262
+ option: z.ZodType<import("./primitives.ts").JsonObject, unknown, z.core.$ZodTypeInternals<import("./primitives.ts").JsonObject, unknown>>;
263
+ environment: z.ZodRecord<z.ZodString, z.ZodString>;
264
+ stdin: z.ZodDiscriminatedUnion<[z.ZodObject<{
265
+ kind: z.ZodLiteral<"json">;
266
+ value: z.ZodType<import("./primitives.ts").JsonValue, unknown, z.core.$ZodTypeInternals<import("./primitives.ts").JsonValue, unknown>>;
267
+ }, z.core.$strict>, z.ZodObject<{
268
+ kind: z.ZodLiteral<"text">;
269
+ value: z.ZodString;
270
+ }, z.core.$strict>, z.ZodObject<{
271
+ kind: z.ZodLiteral<"absent">;
272
+ }, z.core.$strict>], "kind">;
273
+ }, z.core.$strict>]>;
248
274
  relation: z.ZodType<import("./expression.ts").Expression, unknown, z.core.$ZodTypeInternals<import("./expression.ts").Expression, unknown>>;
249
275
  }, z.core.$strict>>;
250
276
  }, z.core.$strict>>;
@@ -417,7 +443,7 @@ export declare const Probe: z.ZodDiscriminatedUnion<[z.ZodObject<{
417
443
  legId: z.ZodString;
418
444
  interfaceId: z.ZodString;
419
445
  operationId: z.ZodString;
420
- inputs: z.ZodObject<{
446
+ inputs: z.ZodUnion<readonly [z.ZodObject<{
421
447
  path: z.ZodType<import("./primitives.ts").JsonObject, unknown, z.core.$ZodTypeInternals<import("./primitives.ts").JsonObject, unknown>>;
422
448
  query: z.ZodType<import("./primitives.ts").JsonObject, unknown, z.core.$ZodTypeInternals<import("./primitives.ts").JsonObject, unknown>>;
423
449
  header: z.ZodRecord<z.ZodString, z.ZodString>;
@@ -427,7 +453,20 @@ export declare const Probe: z.ZodDiscriminatedUnion<[z.ZodObject<{
427
453
  }, z.core.$strict>, z.ZodObject<{
428
454
  kind: z.ZodLiteral<"absent">;
429
455
  }, z.core.$strict>], "kind">;
430
- }, z.core.$strict>;
456
+ }, z.core.$strict>, z.ZodObject<{
457
+ argument: z.ZodType<import("./primitives.ts").JsonObject, unknown, z.core.$ZodTypeInternals<import("./primitives.ts").JsonObject, unknown>>;
458
+ option: z.ZodType<import("./primitives.ts").JsonObject, unknown, z.core.$ZodTypeInternals<import("./primitives.ts").JsonObject, unknown>>;
459
+ environment: z.ZodRecord<z.ZodString, z.ZodString>;
460
+ stdin: z.ZodDiscriminatedUnion<[z.ZodObject<{
461
+ kind: z.ZodLiteral<"json">;
462
+ value: z.ZodType<import("./primitives.ts").JsonValue, unknown, z.core.$ZodTypeInternals<import("./primitives.ts").JsonValue, unknown>>;
463
+ }, z.core.$strict>, z.ZodObject<{
464
+ kind: z.ZodLiteral<"text">;
465
+ value: z.ZodString;
466
+ }, z.core.$strict>, z.ZodObject<{
467
+ kind: z.ZodLiteral<"absent">;
468
+ }, z.core.$strict>], "kind">;
469
+ }, z.core.$strict>]>;
431
470
  relation: z.ZodType<import("./expression.ts").Expression, unknown, z.core.$ZodTypeInternals<import("./expression.ts").Expression, unknown>>;
432
471
  }, z.core.$strict>>;
433
472
  }, z.core.$strict>>;
@@ -197,12 +197,23 @@ export type SensitivityWitness = z.infer<typeof SensitivityWitness>;
197
197
  * A different mechanism from AD-40's DEFECT SIGNATURE, which matches a
198
198
  * scoring-side finding against an observation. This one never enters a score; it
199
199
  * makes "every declared seeded fault observed to fire" decidable at pre-flight.
200
+ *
201
+ * `inputs` is the same union a sensitivity leg takes. It was `ApiWitnessInputs`
202
+ * alone, which made a seeded defect against a command-line system under test
203
+ * unrepresentable in both directions: command channels failed the `Probe`
204
+ * parse, and transport channels reached `requestOf` and threw
205
+ * `undeclared-mandatory-input` for supplying transport channels to an operation
206
+ * that runs behind a command. A `null` witness parses, so the only way through
207
+ * was to declare the defect unobservable, which pre-flight records as a failed
208
+ * `seeded-fault-fired` check. Every `defect` and `zero-action` probe against a
209
+ * command was therefore unscoreable. 0.3.0 widened the contract side and left
210
+ * this one and `FixtureReset` behind.
200
211
  */
201
212
  export declare const ManifestationWitness: z.ZodObject<{
202
213
  legId: z.ZodString;
203
214
  interfaceId: z.ZodString;
204
215
  operationId: z.ZodString;
205
- inputs: z.ZodObject<{
216
+ inputs: z.ZodUnion<readonly [z.ZodObject<{
206
217
  path: z.ZodType<import("./primitives.ts").JsonObject, unknown, z.core.$ZodTypeInternals<import("./primitives.ts").JsonObject, unknown>>;
207
218
  query: z.ZodType<import("./primitives.ts").JsonObject, unknown, z.core.$ZodTypeInternals<import("./primitives.ts").JsonObject, unknown>>;
208
219
  header: z.ZodRecord<z.ZodString, z.ZodString>;
@@ -212,7 +223,20 @@ export declare const ManifestationWitness: z.ZodObject<{
212
223
  }, z.core.$strict>, z.ZodObject<{
213
224
  kind: z.ZodLiteral<"absent">;
214
225
  }, z.core.$strict>], "kind">;
215
- }, z.core.$strict>;
226
+ }, z.core.$strict>, z.ZodObject<{
227
+ argument: z.ZodType<import("./primitives.ts").JsonObject, unknown, z.core.$ZodTypeInternals<import("./primitives.ts").JsonObject, unknown>>;
228
+ option: z.ZodType<import("./primitives.ts").JsonObject, unknown, z.core.$ZodTypeInternals<import("./primitives.ts").JsonObject, unknown>>;
229
+ environment: z.ZodRecord<z.ZodString, z.ZodString>;
230
+ stdin: z.ZodDiscriminatedUnion<[z.ZodObject<{
231
+ kind: z.ZodLiteral<"json">;
232
+ value: z.ZodType<import("./primitives.ts").JsonValue, unknown, z.core.$ZodTypeInternals<import("./primitives.ts").JsonValue, unknown>>;
233
+ }, z.core.$strict>, z.ZodObject<{
234
+ kind: z.ZodLiteral<"text">;
235
+ value: z.ZodString;
236
+ }, z.core.$strict>, z.ZodObject<{
237
+ kind: z.ZodLiteral<"absent">;
238
+ }, z.core.$strict>], "kind">;
239
+ }, z.core.$strict>]>;
216
240
  relation: z.ZodType<Expression, unknown, z.core.$ZodTypeInternals<Expression, unknown>>;
217
241
  }, z.core.$strict>;
218
242
  export type ManifestationWitness = z.infer<typeof ManifestationWitness>;
@@ -225,7 +249,7 @@ export declare const FixtureReset: z.ZodObject<{
225
249
  legId: z.ZodString;
226
250
  interfaceId: z.ZodString;
227
251
  operationId: z.ZodString;
228
- inputs: z.ZodObject<{
252
+ inputs: z.ZodUnion<readonly [z.ZodObject<{
229
253
  path: z.ZodType<import("./primitives.ts").JsonObject, unknown, z.core.$ZodTypeInternals<import("./primitives.ts").JsonObject, unknown>>;
230
254
  query: z.ZodType<import("./primitives.ts").JsonObject, unknown, z.core.$ZodTypeInternals<import("./primitives.ts").JsonObject, unknown>>;
231
255
  header: z.ZodRecord<z.ZodString, z.ZodString>;
@@ -235,6 +259,19 @@ export declare const FixtureReset: z.ZodObject<{
235
259
  }, z.core.$strict>, z.ZodObject<{
236
260
  kind: z.ZodLiteral<"absent">;
237
261
  }, z.core.$strict>], "kind">;
238
- }, z.core.$strict>;
262
+ }, z.core.$strict>, z.ZodObject<{
263
+ argument: z.ZodType<import("./primitives.ts").JsonObject, unknown, z.core.$ZodTypeInternals<import("./primitives.ts").JsonObject, unknown>>;
264
+ option: z.ZodType<import("./primitives.ts").JsonObject, unknown, z.core.$ZodTypeInternals<import("./primitives.ts").JsonObject, unknown>>;
265
+ environment: z.ZodRecord<z.ZodString, z.ZodString>;
266
+ stdin: z.ZodDiscriminatedUnion<[z.ZodObject<{
267
+ kind: z.ZodLiteral<"json">;
268
+ value: z.ZodType<import("./primitives.ts").JsonValue, unknown, z.core.$ZodTypeInternals<import("./primitives.ts").JsonValue, unknown>>;
269
+ }, z.core.$strict>, z.ZodObject<{
270
+ kind: z.ZodLiteral<"text">;
271
+ value: z.ZodString;
272
+ }, z.core.$strict>, z.ZodObject<{
273
+ kind: z.ZodLiteral<"absent">;
274
+ }, z.core.$strict>], "kind">;
275
+ }, z.core.$strict>]>;
239
276
  }, z.core.$strict>;
240
277
  export type FixtureReset = z.infer<typeof FixtureReset>;
@@ -121,12 +121,23 @@ export const SensitivityWitness = z.strictObject({
121
121
  * A different mechanism from AD-40's DEFECT SIGNATURE, which matches a
122
122
  * scoring-side finding against an observation. This one never enters a score; it
123
123
  * makes "every declared seeded fault observed to fire" decidable at pre-flight.
124
+ *
125
+ * `inputs` is the same union a sensitivity leg takes. It was `ApiWitnessInputs`
126
+ * alone, which made a seeded defect against a command-line system under test
127
+ * unrepresentable in both directions: command channels failed the `Probe`
128
+ * parse, and transport channels reached `requestOf` and threw
129
+ * `undeclared-mandatory-input` for supplying transport channels to an operation
130
+ * that runs behind a command. A `null` witness parses, so the only way through
131
+ * was to declare the defect unobservable, which pre-flight records as a failed
132
+ * `seeded-fault-fired` check. Every `defect` and `zero-action` probe against a
133
+ * command was therefore unscoreable. 0.3.0 widened the contract side and left
134
+ * this one and `FixtureReset` behind.
124
135
  */
125
136
  export const ManifestationWitness = z.strictObject({
126
137
  legId: Identifier,
127
138
  interfaceId: Identifier,
128
139
  operationId: Identifier,
129
- inputs: ApiWitnessInputs,
140
+ inputs: WitnessInputs,
130
141
  relation: Expression,
131
142
  });
132
143
  /**
@@ -138,5 +149,5 @@ export const FixtureReset = z.strictObject({
138
149
  legId: Identifier,
139
150
  interfaceId: Identifier,
140
151
  operationId: Identifier,
141
- inputs: ApiWitnessInputs,
152
+ inputs: WitnessInputs,
142
153
  });
@@ -35,7 +35,7 @@ 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';
38
- import { type SealedProbeSet } from './qualification.ts';
38
+ import { type QualificationResult, type SealedProbeSet } from './qualification.ts';
39
39
  import { type TrialSetResult } from './reduce-trials.ts';
40
40
  /**
41
41
  * `score`'s owned product: the assessment/ladder pairing AD-24 names, "the
@@ -56,6 +56,13 @@ export type ScoredOutcomesAndVerdict = {
56
56
  readonly policy: ScoringPolicy;
57
57
  readonly probe: Probe;
58
58
  readonly sealedProbes: SealedProbeSet;
59
+ /**
60
+ * The one probe's own qualification result, lifted out of `sealedProbes`
61
+ * so a caller reading a run's product holds the closed reason set that
62
+ * decided it. `emit` mints no field from this: AD-9's reasons stay off the
63
+ * `EvidenceArtifact` and travel the return path.
64
+ */
65
+ readonly probeQualification: QualificationResult;
59
66
  /** this probe's own AD-7 trial-set fold, keyed by `emit` under `probe.probeId` to build the strength vector. */
60
67
  readonly trialSetResult: TrialSetResult;
61
68
  /** the full `EvidenceArtifact.outcomes` shape, a parallel array to `ScoredOutcome[]` above: `ScoredOutcome` carries `resolution` but not `disposition` or the raw `CheckResolution` tree this shape needs, so the two are not reconstructible from one another. */
@@ -37,7 +37,7 @@ import { buildPlanIndex } from '../seal/plan-index.js';
37
37
  import { resolveCapturedBindings, selectWithBindings } from './bindings.js';
38
38
  import { resolveContractVerdict, resolveProductionVerdict } from './ladder.js';
39
39
  import { FINDING_BUCKETS, resolveOutcome, uncitedDefectFindingGaps, uncitedFindingIds, } from './outcome.js';
40
- import { resolveHomeOperation, sealProbeSet, } from './qualification.js';
40
+ import { qualifyProbe, resolveHomeOperation, sealProbeSet, } from './qualification.js';
41
41
  import { reduceTrialSet, TRIAL_VOTE_STATES, } from './reduce-trials.js';
42
42
  import { mapFindings, matchProbeWitness } from './witness.js';
43
43
  /** A mutable copy of the two record-shaped arrays every witness/finding function this stage calls wants, since `ValidatedObservations`' own arrays are `readonly`. */
@@ -245,8 +245,17 @@ export const score = (contract, trials, probe, preflightVerdict, policy, waiver,
245
245
  ? null
246
246
  : resolveHomeOperation(candidate.defectSignature, contract.permittedInterfaces);
247
247
  const sealedProbes = sealProbeSet([probe], homeOperationOf);
248
- const qualifiedEntry = sealedProbes.admitted[0] ?? sealedProbes.rejected[0];
249
- const probeQualified = qualifiedEntry === undefined ? false : qualifiedEntry.result.qualified;
248
+ // `sealProbeSet` over a one-probe array puts that probe in exactly one
249
+ // bucket, so the third branch is unreachable. It re-runs the gate rather
250
+ // than composing a result here: `qualifyProbe` holds
251
+ // `qualified === (failures.length === 0)`, and a hand-built
252
+ // `{ qualified: false, failures: [] }` would hand a consumer a rejection
253
+ // with no code to route on, which is the silent state this field removes.
254
+ // The gate is pure, so a second call over the same probe answers the same.
255
+ const probeQualification = sealedProbes.admitted[0]?.result ??
256
+ sealedProbes.rejected[0]?.result ??
257
+ qualifyProbe(probe, homeOperationOf(probe));
258
+ const probeQualified = probeQualification.qualified;
250
259
  const signedProbe = signedProbeOf(probe);
251
260
  const designatedOracleId = designatedOracleIdOf(probe, contract);
252
261
  const probeSigned = !probe.expectedClean && probe.defectSignature !== null;
@@ -578,6 +587,7 @@ export const score = (contract, trials, probe, preflightVerdict, policy, waiver,
578
587
  policy,
579
588
  probe,
580
589
  sealedProbes,
590
+ probeQualification,
581
591
  trialSetResult: reduced,
582
592
  outcomes,
583
593
  uncitedFindings,
@@ -600,6 +610,7 @@ export const score = (contract, trials, probe, preflightVerdict, policy, waiver,
600
610
  policy,
601
611
  probe,
602
612
  sealedProbes,
613
+ probeQualification,
603
614
  trialSetResult: reduced,
604
615
  outcomes,
605
616
  uncitedFindings,
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 = "1.2.0";
15
+ export declare const VERSION = "1.4.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 = '1.2.0';
22
+ export const VERSION = '1.4.0';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eval-quality",
3
- "version": "1.2.0",
3
+ "version": "1.4.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",
@@ -1566,7 +1566,102 @@
1566
1566
  "description": "A kebab-case slug. Excludes \"/\" and \"~\" so an identifier can be embedded in an interaction-rooted pointer without escaping."
1567
1567
  },
1568
1568
  "inputs": {
1569
- "$ref": "#/$defs/WitnessInputs"
1569
+ "anyOf": [
1570
+ {
1571
+ "$ref": "#/$defs/WitnessInputs"
1572
+ },
1573
+ {
1574
+ "type": "object",
1575
+ "properties": {
1576
+ "argument": {
1577
+ "type": "object",
1578
+ "propertyNames": {
1579
+ "type": "string"
1580
+ },
1581
+ "additionalProperties": {
1582
+ "$ref": "#/$defs/JsonValue"
1583
+ }
1584
+ },
1585
+ "option": {
1586
+ "type": "object",
1587
+ "propertyNames": {
1588
+ "type": "string"
1589
+ },
1590
+ "additionalProperties": {
1591
+ "$ref": "#/$defs/JsonValue"
1592
+ }
1593
+ },
1594
+ "environment": {
1595
+ "type": "object",
1596
+ "propertyNames": {
1597
+ "type": "string",
1598
+ "minLength": 1
1599
+ },
1600
+ "additionalProperties": {
1601
+ "type": "string"
1602
+ }
1603
+ },
1604
+ "stdin": {
1605
+ "oneOf": [
1606
+ {
1607
+ "type": "object",
1608
+ "properties": {
1609
+ "kind": {
1610
+ "type": "string",
1611
+ "const": "json"
1612
+ },
1613
+ "value": {
1614
+ "$ref": "#/$defs/JsonValue"
1615
+ }
1616
+ },
1617
+ "required": [
1618
+ "kind",
1619
+ "value"
1620
+ ],
1621
+ "additionalProperties": false
1622
+ },
1623
+ {
1624
+ "type": "object",
1625
+ "properties": {
1626
+ "kind": {
1627
+ "type": "string",
1628
+ "const": "text"
1629
+ },
1630
+ "value": {
1631
+ "type": "string"
1632
+ }
1633
+ },
1634
+ "required": [
1635
+ "kind",
1636
+ "value"
1637
+ ],
1638
+ "additionalProperties": false
1639
+ },
1640
+ {
1641
+ "type": "object",
1642
+ "properties": {
1643
+ "kind": {
1644
+ "type": "string",
1645
+ "const": "absent"
1646
+ }
1647
+ },
1648
+ "required": [
1649
+ "kind"
1650
+ ],
1651
+ "additionalProperties": false
1652
+ }
1653
+ ]
1654
+ }
1655
+ },
1656
+ "required": [
1657
+ "argument",
1658
+ "option",
1659
+ "environment",
1660
+ "stdin"
1661
+ ],
1662
+ "additionalProperties": false
1663
+ }
1664
+ ]
1570
1665
  }
1571
1666
  },
1572
1667
  "required": [
@@ -308,7 +308,102 @@
308
308
  "description": "A kebab-case slug. Excludes \"/\" and \"~\" so an identifier can be embedded in an interaction-rooted pointer without escaping."
309
309
  },
310
310
  "inputs": {
311
- "$ref": "#/$defs/WitnessInputs"
311
+ "anyOf": [
312
+ {
313
+ "$ref": "#/$defs/WitnessInputs"
314
+ },
315
+ {
316
+ "type": "object",
317
+ "properties": {
318
+ "argument": {
319
+ "type": "object",
320
+ "propertyNames": {
321
+ "type": "string"
322
+ },
323
+ "additionalProperties": {
324
+ "$ref": "#/$defs/JsonValue"
325
+ }
326
+ },
327
+ "option": {
328
+ "type": "object",
329
+ "propertyNames": {
330
+ "type": "string"
331
+ },
332
+ "additionalProperties": {
333
+ "$ref": "#/$defs/JsonValue"
334
+ }
335
+ },
336
+ "environment": {
337
+ "type": "object",
338
+ "propertyNames": {
339
+ "type": "string",
340
+ "minLength": 1
341
+ },
342
+ "additionalProperties": {
343
+ "type": "string"
344
+ }
345
+ },
346
+ "stdin": {
347
+ "oneOf": [
348
+ {
349
+ "type": "object",
350
+ "properties": {
351
+ "kind": {
352
+ "type": "string",
353
+ "const": "json"
354
+ },
355
+ "value": {
356
+ "$ref": "#/$defs/JsonValue"
357
+ }
358
+ },
359
+ "required": [
360
+ "kind",
361
+ "value"
362
+ ],
363
+ "additionalProperties": false
364
+ },
365
+ {
366
+ "type": "object",
367
+ "properties": {
368
+ "kind": {
369
+ "type": "string",
370
+ "const": "text"
371
+ },
372
+ "value": {
373
+ "type": "string"
374
+ }
375
+ },
376
+ "required": [
377
+ "kind",
378
+ "value"
379
+ ],
380
+ "additionalProperties": false
381
+ },
382
+ {
383
+ "type": "object",
384
+ "properties": {
385
+ "kind": {
386
+ "type": "string",
387
+ "const": "absent"
388
+ }
389
+ },
390
+ "required": [
391
+ "kind"
392
+ ],
393
+ "additionalProperties": false
394
+ }
395
+ ]
396
+ }
397
+ },
398
+ "required": [
399
+ "argument",
400
+ "option",
401
+ "environment",
402
+ "stdin"
403
+ ],
404
+ "additionalProperties": false
405
+ }
406
+ ]
312
407
  },
313
408
  "relation": {
314
409
  "$ref": "#/$defs/Expression"
@@ -668,7 +763,102 @@
668
763
  "description": "A kebab-case slug. Excludes \"/\" and \"~\" so an identifier can be embedded in an interaction-rooted pointer without escaping."
669
764
  },
670
765
  "inputs": {
671
- "$ref": "#/$defs/WitnessInputs"
766
+ "anyOf": [
767
+ {
768
+ "$ref": "#/$defs/WitnessInputs"
769
+ },
770
+ {
771
+ "type": "object",
772
+ "properties": {
773
+ "argument": {
774
+ "type": "object",
775
+ "propertyNames": {
776
+ "type": "string"
777
+ },
778
+ "additionalProperties": {
779
+ "$ref": "#/$defs/JsonValue"
780
+ }
781
+ },
782
+ "option": {
783
+ "type": "object",
784
+ "propertyNames": {
785
+ "type": "string"
786
+ },
787
+ "additionalProperties": {
788
+ "$ref": "#/$defs/JsonValue"
789
+ }
790
+ },
791
+ "environment": {
792
+ "type": "object",
793
+ "propertyNames": {
794
+ "type": "string",
795
+ "minLength": 1
796
+ },
797
+ "additionalProperties": {
798
+ "type": "string"
799
+ }
800
+ },
801
+ "stdin": {
802
+ "oneOf": [
803
+ {
804
+ "type": "object",
805
+ "properties": {
806
+ "kind": {
807
+ "type": "string",
808
+ "const": "json"
809
+ },
810
+ "value": {
811
+ "$ref": "#/$defs/JsonValue"
812
+ }
813
+ },
814
+ "required": [
815
+ "kind",
816
+ "value"
817
+ ],
818
+ "additionalProperties": false
819
+ },
820
+ {
821
+ "type": "object",
822
+ "properties": {
823
+ "kind": {
824
+ "type": "string",
825
+ "const": "text"
826
+ },
827
+ "value": {
828
+ "type": "string"
829
+ }
830
+ },
831
+ "required": [
832
+ "kind",
833
+ "value"
834
+ ],
835
+ "additionalProperties": false
836
+ },
837
+ {
838
+ "type": "object",
839
+ "properties": {
840
+ "kind": {
841
+ "type": "string",
842
+ "const": "absent"
843
+ }
844
+ },
845
+ "required": [
846
+ "kind"
847
+ ],
848
+ "additionalProperties": false
849
+ }
850
+ ]
851
+ }
852
+ },
853
+ "required": [
854
+ "argument",
855
+ "option",
856
+ "environment",
857
+ "stdin"
858
+ ],
859
+ "additionalProperties": false
860
+ }
861
+ ]
672
862
  },
673
863
  "relation": {
674
864
  "$ref": "#/$defs/Expression"