eval-quality 0.1.0 → 0.3.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 +142 -67
- package/corpus/dev/README.md +20 -13
- package/corpus/dev/compile-seal-example/brief.json +1 -1
- package/corpus/dev/compile-seal-example/contract.json +1 -1
- package/corpus/dev/contracts/absent-collection-locations.json +1 -1
- package/corpus/dev/contracts/absent-sibling-groups.json +1 -1
- package/corpus/dev/contracts/absent-success-indicator.json +1 -1
- package/corpus/dev/contracts/empty-channel-roles.json +1 -1
- package/corpus/dev/contracts/empty-collection-locations.json +1 -1
- package/corpus/dev/contracts/empty-request-shapes.json +1 -1
- package/corpus/dev/contracts/empty-sibling-groups.json +1 -1
- package/corpus/dev/contracts/fragment-selection.json +1 -0
- package/corpus/dev/contracts/no-collection-quantifier.json +1 -1
- package/corpus/dev/contracts/no-operation-inventory.json +1 -1
- package/corpus/dev/contracts/no-read-back-relation.json +1 -1
- package/corpus/dev/contracts/no-state-change-marker.json +1 -1
- package/corpus/dev/contracts/no-type-violating-step.json +1 -1
- package/corpus/dev/contracts/per-key-split-oracles.json +1 -1
- package/corpus/dev/contracts/review-corpus.json +1 -0
- package/corpus/dev/contracts/satisfied-declarations.json +1 -1
- package/corpus/dev/contracts/single-required-response-key.json +1 -1
- package/corpus/dev/contracts/split-indicator-oracle.json +1 -1
- package/corpus/dev/contracts/unaddressed-parameter-sibling.json +1 -1
- package/corpus/dev/contracts/unnamed-reference-set.json +1 -1
- package/corpus/dev/contracts/wrong-cardinality-form.json +1 -1
- package/corpus/dev/index.json +1 -1
- package/dist/application/index.d.ts +2 -0
- package/dist/application/index.js +1 -0
- package/dist/application/score.d.ts +48 -0
- package/dist/application/score.js +186 -0
- package/dist/cli/arguments.d.ts +7 -3
- package/dist/cli/arguments.js +73 -5
- package/dist/cli/exit-codes.d.ts +6 -2
- package/dist/cli/exit-codes.js +8 -9
- package/dist/cli/main.js +9 -0
- package/dist/cli/render.d.ts +3 -6
- package/dist/cli/render.js +57 -5
- package/dist/cli/run.d.ts +13 -3
- package/dist/cli/run.js +136 -16
- package/dist/core/canonical/canonicalize.d.ts +12 -0
- package/dist/core/canonical/canonicalize.js +18 -4
- package/dist/core/compile/bindings.d.ts +89 -0
- package/dist/core/compile/bindings.js +334 -0
- package/dist/core/compile/compile.d.ts +17 -0
- package/dist/core/compile/compile.js +37 -1
- package/dist/core/compile/excluded-content.d.ts +11 -0
- package/dist/core/compile/excluded-content.js +42 -0
- package/dist/core/compile/expression-legality.d.ts +49 -0
- package/dist/core/compile/expression-legality.js +158 -38
- package/dist/core/compile/forbidden-inputs.d.ts +8 -1
- package/dist/core/compile/forbidden-inputs.js +16 -3
- package/dist/core/compile/interface-inventory.d.ts +57 -2
- package/dist/core/compile/interface-inventory.js +119 -13
- package/dist/core/compile/reachability.d.ts +28 -1
- package/dist/core/compile/reachability.js +167 -35
- package/dist/core/compile/schema-version.d.ts +2 -0
- package/dist/core/compile/schema-version.js +25 -0
- package/dist/core/compile/sensitivity-witness.d.ts +31 -12
- package/dist/core/compile/sensitivity-witness.js +110 -23
- package/dist/core/compile/step-reference.d.ts +2 -0
- package/dist/core/compile/step-reference.js +49 -0
- package/dist/core/coverage/operations.d.ts +62 -0
- package/dist/core/coverage/operations.js +57 -0
- package/dist/core/coverage/relevance.d.ts +4 -2
- package/dist/core/coverage/relevance.js +22 -23
- package/dist/core/coverage/satisfaction.d.ts +2 -2
- package/dist/core/coverage/satisfaction.js +73 -48
- package/dist/core/declared-inputs.d.ts +83 -4
- package/dist/core/declared-inputs.js +105 -8
- package/dist/core/emit/emit.d.ts +9 -0
- package/dist/core/emit/emit.js +214 -0
- package/dist/core/emit/private-artifact-digest.d.ts +11 -0
- package/dist/core/emit/private-artifact-digest.js +31 -0
- package/dist/core/evaluate/evidence-resolution.d.ts +10 -3
- package/dist/core/evaluate/evidence-resolution.js +74 -11
- package/dist/core/evaluate/operators.d.ts +22 -0
- package/dist/core/evaluate/operators.js +17 -1
- package/dist/core/evaluate/resolution.d.ts +14 -3
- package/dist/core/evaluate/resolution.js +63 -3
- package/dist/core/excluded-content.d.ts +65 -0
- package/dist/core/excluded-content.js +113 -0
- package/dist/core/failure-codes.d.ts +2 -2
- package/dist/core/failure-codes.js +7 -2
- package/dist/core/ingest/conditions.d.ts +201 -0
- package/dist/core/ingest/conditions.js +48 -0
- package/dist/core/ingest/index.d.ts +13 -0
- package/dist/core/ingest/index.js +11 -0
- package/dist/core/ingest/ingest.d.ts +68 -0
- package/dist/core/ingest/ingest.js +318 -0
- package/dist/core/lineage/stage-table.d.ts +21 -4
- package/dist/core/lineage/stage-table.js +35 -3
- package/dist/core/preflight/plan.d.ts +7 -5
- package/dist/core/preflight/plan.js +75 -32
- package/dist/core/preflight/projection.d.ts +6 -3
- package/dist/core/preflight/projection.js +22 -2
- package/dist/core/preflight/reduce.js +47 -7
- package/dist/core/preflight/witness-evidence.d.ts +5 -5
- package/dist/core/preflight/witness-evidence.js +73 -20
- package/dist/core/schemas/artifact.d.ts +1049 -30
- package/dist/core/schemas/constraint-ledger.js +51 -0
- package/dist/core/schemas/defect-signature.d.ts +695 -0
- package/dist/core/schemas/defect-signature.js +175 -0
- package/dist/core/schemas/eval-contract.d.ts +550 -12
- package/dist/core/schemas/eval-contract.js +32 -2
- package/dist/core/schemas/evidence-artifact.d.ts +109 -19
- package/dist/core/schemas/evidence-artifact.js +64 -8
- package/dist/core/schemas/interface.d.ts +741 -13
- package/dist/core/schemas/interface.js +120 -7
- package/dist/core/schemas/isolation-manifest.js +16 -9
- package/dist/core/schemas/plan.d.ts +329 -3
- package/dist/core/schemas/plan.js +89 -3
- package/dist/core/schemas/pointer.d.ts +47 -1
- package/dist/core/schemas/pointer.js +89 -8
- package/dist/core/schemas/port-messages.d.ts +194 -10
- package/dist/core/schemas/port-messages.js +73 -6
- package/dist/core/schemas/probe-body.d.ts +18 -0
- package/dist/core/schemas/probe-body.js +13 -0
- package/dist/core/schemas/probe-qualification.d.ts +168 -0
- package/dist/core/schemas/probe-qualification.js +106 -0
- package/dist/core/schemas/probe.d.ts +431 -0
- package/dist/core/schemas/probe.js +6 -2
- package/dist/core/schemas/scoring-policy.d.ts +1 -0
- package/dist/core/schemas/scoring-policy.js +6 -1
- package/dist/core/schemas/sealed-evaluator-brief.d.ts +1 -0
- package/dist/core/schemas/sealed-evaluator-brief.js +4 -1
- package/dist/core/schemas/sealed-run-record.d.ts +161 -12
- package/dist/core/schemas/sealed-run-record.js +92 -15
- package/dist/core/schemas/sensitivity-witness.d.ts +108 -6
- package/dist/core/schemas/sensitivity-witness.js +61 -5
- package/dist/core/score/binding-order.d.ts +31 -0
- package/dist/core/score/binding-order.js +88 -0
- package/dist/core/score/bindings.d.ts +93 -0
- package/dist/core/score/bindings.js +309 -0
- package/dist/core/score/ladder-table.d.ts +23 -0
- package/dist/core/score/ladder-table.js +109 -0
- package/dist/core/score/ladder.d.ts +229 -0
- package/dist/core/score/ladder.js +464 -0
- package/dist/core/score/mode-agreement.d.ts +30 -0
- package/dist/core/score/mode-agreement.js +16 -0
- package/dist/core/score/outcome-table.d.ts +17 -0
- package/dist/core/score/outcome-table.js +172 -0
- package/dist/core/score/outcome.d.ts +466 -0
- package/dist/core/score/outcome.js +562 -0
- package/dist/core/score/qualification.d.ts +83 -0
- package/dist/core/score/qualification.js +645 -0
- package/dist/core/score/quotation.d.ts +56 -0
- package/dist/core/score/quotation.js +226 -0
- package/dist/core/score/reduce-trials.d.ts +80 -0
- package/dist/core/score/reduce-trials.js +90 -0
- package/dist/core/score/score.d.ts +72 -0
- package/dist/core/score/score.js +607 -0
- package/dist/core/score/selection.d.ts +80 -0
- package/dist/core/score/selection.js +98 -0
- package/dist/core/score/strength.d.ts +58 -0
- package/dist/core/score/strength.js +227 -0
- package/dist/core/score/witness.d.ts +138 -0
- package/dist/core/score/witness.js +320 -0
- package/dist/core/seal/derived-reference.d.ts +2 -9
- package/dist/core/seal/derived-reference.js +293 -46
- package/dist/core/seal/plan-index.d.ts +18 -3
- package/dist/core/seal/plan-index.js +44 -8
- package/dist/core/seal/seal.js +8 -3
- package/dist/core/stage-contracts.d.ts +62 -2
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/ports/environment-probe-port.d.ts +60 -4
- package/dist/testing/index.d.ts +2 -0
- package/dist/testing/index.js +1 -0
- package/dist/testing/probe-conformance.d.ts +29 -0
- package/dist/testing/probe-conformance.js +34 -5
- package/package.json +12 -3
- package/schemas/eval-contract.schema.json +1787 -797
- package/schemas/evidence-artifact.schema.json +126 -9
- package/schemas/isolation-manifest.schema.json +17 -10
- package/schemas/probe.schema.json +636 -5
- package/schemas/rubric.schema.json +1 -1
- package/schemas/scoring-policy.schema.json +8 -1
- package/schemas/sealed-evaluator-brief.schema.json +11 -1
- package/schemas/sealed-run-record.schema.json +314 -40
|
@@ -3,8 +3,9 @@
|
|
|
3
3
|
* that turns an `Expression` into one `CheckResolutionValue`. Leaf operators,
|
|
4
4
|
* `covers-by-key` included, live in `operators.ts`. Operand resolution, every
|
|
5
5
|
* pointer form including the bound-element `@/` form, is injected;
|
|
6
|
-
* `evidence-resolution.ts` supplies it, and `ResolveOperand
|
|
7
|
-
* `PointerDenotesCollection` are the consumer-side
|
|
6
|
+
* `evidence-resolution.ts` supplies it, and `ResolveOperand`,
|
|
7
|
+
* `PointerDenotesCollection`, and `ReferenceSetKeys` are the consumer-side
|
|
8
|
+
* contract it satisfies.
|
|
8
9
|
*/
|
|
9
10
|
import type { CheckResolutionValue } from '../schemas/evidence-artifact.ts';
|
|
10
11
|
import type { Expression, Operand } from '../schemas/expression.ts';
|
|
@@ -31,10 +32,20 @@ export type ResolveOperand = (operand: Operand, boundElement: ResolvedValue, art
|
|
|
31
32
|
* soft-delete agreement pair (AD-4's own worked example).
|
|
32
33
|
*/
|
|
33
34
|
export type PointerDenotesCollection = (pointer: string) => boolean;
|
|
35
|
+
/**
|
|
36
|
+
* The `keys` each declared reference set names, by identifier.
|
|
37
|
+
* `set-membership`'s set position reads the single declared key off each
|
|
38
|
+
* member (`reference-set.ts`), and the members map the injected
|
|
39
|
+
* `ResolveOperand` closes over has already discarded the keys, so they travel
|
|
40
|
+
* separately. Plain data, because `PreflightPlan` carries this through to
|
|
41
|
+
* its reducer and is compared by value, which a closure fails.
|
|
42
|
+
* `evidence-resolution.ts` builds it from a contract.
|
|
43
|
+
*/
|
|
44
|
+
export type ReferenceSetKeys = Readonly<Record<string, readonly string[]>>;
|
|
34
45
|
/**
|
|
35
46
|
* The public entry point. Walks `expression` and produces one
|
|
36
47
|
* `CheckResolutionValue`, exactly what `Outcome.checkResolution` needs,
|
|
37
48
|
* unmodified. `boundElement` starts `ABSENT` at the root, where no quantifier
|
|
38
49
|
* has bound anything yet.
|
|
39
50
|
*/
|
|
40
|
-
export declare function resolveCheck(expression: Expression, resolveOperand: ResolveOperand, pointerDenotesCollection: PointerDenotesCollection, regexMatchStepBudget: number, artifactPath: string): CheckResolutionValue;
|
|
51
|
+
export declare function resolveCheck(expression: Expression, resolveOperand: ResolveOperand, pointerDenotesCollection: PointerDenotesCollection, referenceSetKeys: ReferenceSetKeys, regexMatchStepBudget: number, artifactPath: string): CheckResolutionValue;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { absence, containment, countTolerance, coversByKey, deepEquality, equality, existence, ordering, regexMatch, setMembership, shape, } from './operators.js';
|
|
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
4
|
* AD-4's one closed introduction condition, checked per operand and applied
|
|
@@ -242,10 +242,69 @@ function resolveRegexNode(expression, boundElement, ctx) {
|
|
|
242
242
|
const { pattern } = expression;
|
|
243
243
|
return resolveSingleOperand(operand, boundElement, ctx, (resolved) => regexMatch(resolved, pattern, ctx.regexMatchStepBudget, ctx.artifactPath));
|
|
244
244
|
}
|
|
245
|
+
/**
|
|
246
|
+
* Reads the single declared key off each member of a `{ referenceSet }` set
|
|
247
|
+
* operand. `reference-set.ts` declares members as objects so one declaration
|
|
248
|
+
* serves both operators, and states that "a `set-membership` operand against
|
|
249
|
+
* the same set reads the single named key". This is where that reading
|
|
250
|
+
* happens, and without it AD-20 rule 6's injection form can never answer
|
|
251
|
+
* `true`: the value operand resolves to a scalar, and `setMembership` compares
|
|
252
|
+
* whole members by digest, so a scalar against an object is always `false`.
|
|
253
|
+
*
|
|
254
|
+
* Scoped to this one operator position on purpose. `covers-by-key` projects
|
|
255
|
+
* both sides itself on `expectedKey` and `actualKey`, and `containment`
|
|
256
|
+
* matches whole members, so both want the members unprojected. The injected
|
|
257
|
+
* `ResolveOperand` is handed one operand at a time and cannot see which
|
|
258
|
+
* operator position it is filling, which is why the projection lives at the
|
|
259
|
+
* resolution site.
|
|
260
|
+
*
|
|
261
|
+
* The option turned down: an explicit `memberKey` field on `set-membership`
|
|
262
|
+
* mirroring `covers-by-key`'s `expectedKey`. It is symmetric with its sibling
|
|
263
|
+
* and it carries a multi-key set, at the price of a grammar change to a
|
|
264
|
+
* published artifact, an eval-contract `schemaVersion` bump, and
|
|
265
|
+
* published-schema drift, for a case no shipped contract has. Revisit it the
|
|
266
|
+
* first time a real contract wants a multi-key set in this position.
|
|
267
|
+
*
|
|
268
|
+
* Both throws are unreachable for a compiled contract:
|
|
269
|
+
* `checkOperandLegality` rejects a multi-key reference set in this position,
|
|
270
|
+
* and a member missing the declared key, under
|
|
271
|
+
* `malformed-operator-expression`. They throw, because a set this function
|
|
272
|
+
* cannot project carries no membership answer to give.
|
|
273
|
+
*/
|
|
274
|
+
function projectSetOperand(resolvedSet, setOperand, ctx) {
|
|
275
|
+
if (!('referenceSet' in setOperand))
|
|
276
|
+
return resolvedSet;
|
|
277
|
+
const { referenceSet } = setOperand;
|
|
278
|
+
// `Object.hasOwn`, the same prototype-chain guard `makeResolveOperand`
|
|
279
|
+
// applies to its own maps: `Identifier`'s charset admits `constructor`.
|
|
280
|
+
const keys = Object.hasOwn(ctx.referenceSetKeys, referenceSet)
|
|
281
|
+
? ctx.referenceSetKeys[referenceSet]
|
|
282
|
+
: undefined;
|
|
283
|
+
// No declared keys means the contract declares no such reference set,
|
|
284
|
+
// which compilation rejects under `unresolved-reference-set`; a non-array
|
|
285
|
+
// is the array guard below reporting whatever the resolver returned. Both
|
|
286
|
+
// pass through so exactly one guard speaks for each.
|
|
287
|
+
if (keys === undefined || !Array.isArray(resolvedSet))
|
|
288
|
+
return resolvedSet;
|
|
289
|
+
if (keys.length !== 1) {
|
|
290
|
+
throw new Error(`set-membership's set-operand projection: referenceSet "${referenceSet}" declares ${keys.length} keys, and this position reads exactly one. A multi-key set here is malformed-operator-expression, which compilation rejects.`);
|
|
291
|
+
}
|
|
292
|
+
const key = keys[0];
|
|
293
|
+
return resolvedSet.map((member) => {
|
|
294
|
+
const keyValue = keyValueOf(member, key);
|
|
295
|
+
if (keyValue === ABSENT) {
|
|
296
|
+
throw new Error(`set-membership's set-operand projection: a member of referenceSet "${referenceSet}" carries no own property "${key}", the key it declares. That is malformed-operator-expression, which compilation rejects.`);
|
|
297
|
+
}
|
|
298
|
+
return keyValue;
|
|
299
|
+
});
|
|
300
|
+
}
|
|
245
301
|
function resolveSetMembershipNode(expression, boundElement, ctx) {
|
|
246
302
|
const [valueOperand, setOperand] = expression.operands;
|
|
247
303
|
const value = ctx.resolveOperand(valueOperand, boundElement, ctx.artifactPath);
|
|
248
|
-
|
|
304
|
+
// Projected before the empty-collection interception and the array guard
|
|
305
|
+
// below, so both read the one value the operator will see. Projection
|
|
306
|
+
// preserves length and array-ness, so neither answer moves.
|
|
307
|
+
const resolvedSet = projectSetOperand(ctx.resolveOperand(setOperand, boundElement, ctx.artifactPath), setOperand, ctx);
|
|
249
308
|
if (anyOperandEmpty([
|
|
250
309
|
{ operand: valueOperand, resolved: value },
|
|
251
310
|
{ operand: setOperand, resolved: resolvedSet },
|
|
@@ -326,10 +385,11 @@ function resolveNode(expression, boundElement, ctx) {
|
|
|
326
385
|
* unmodified. `boundElement` starts `ABSENT` at the root, where no quantifier
|
|
327
386
|
* has bound anything yet.
|
|
328
387
|
*/
|
|
329
|
-
export function resolveCheck(expression, resolveOperand, pointerDenotesCollection, regexMatchStepBudget, artifactPath) {
|
|
388
|
+
export function resolveCheck(expression, resolveOperand, pointerDenotesCollection, referenceSetKeys, regexMatchStepBudget, artifactPath) {
|
|
330
389
|
return resolveNode(expression, ABSENT, {
|
|
331
390
|
resolveOperand,
|
|
332
391
|
pointerDenotesCollection,
|
|
392
|
+
referenceSetKeys,
|
|
333
393
|
regexMatchStepBudget,
|
|
334
394
|
artifactPath,
|
|
335
395
|
});
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AD-18's excluded categories, as patterns that read a value rather than a name.
|
|
3
|
+
*
|
|
4
|
+
* AD-18 excludes "credentials, tokens, real names, email addresses, account
|
|
5
|
+
* identifiers, and transaction content" from every artifact this package
|
|
6
|
+
* produces or publishes, and binds "published examples and test fixtures as
|
|
7
|
+
* strictly as real runs". Until this module the whole prohibition was carried by
|
|
8
|
+
* one test that scans `corpus/dev`, so a contract carrying a live-shaped token
|
|
9
|
+
* compiled clean and the token travelled into the sealed brief.
|
|
10
|
+
*
|
|
11
|
+
* Only the value-shaped half of that scan ships here, and the split is the point
|
|
12
|
+
* rather than an economy. AD-18's own reading of the rule for the header channel
|
|
13
|
+
* is that a declaration "names the header and its type and never carries a
|
|
14
|
+
* credential value", so a name is what an author is supposed to write: an
|
|
15
|
+
* operation declaring a `password` body key or an `Authorization` header is a
|
|
16
|
+
* contract for an authentication API, and a gate matching `\bpassword\b` would
|
|
17
|
+
* reject exactly the systems this package exists to evaluate. The patterns below
|
|
18
|
+
* therefore match only strings whose shape is decidable without knowing what
|
|
19
|
+
* field they sit in: a PEM header, a token with an issuer prefix, an address, a
|
|
20
|
+
* checkable account number, a card-shaped digit run. The name-shaped patterns
|
|
21
|
+
* stay in `tests/architecture/dev-corpus.test.ts`, where review rather than a
|
|
22
|
+
* compiler decides.
|
|
23
|
+
*
|
|
24
|
+
* Each pattern carries a string it must fire on, and a test asserts every one
|
|
25
|
+
* still does. A pattern that has rotted into a regex matching nothing is worse
|
|
26
|
+
* than no pattern: it reports clean forever.
|
|
27
|
+
*/
|
|
28
|
+
/** AD-18's six categories, in the rule's own order. */
|
|
29
|
+
export declare const EXCLUDED_CATEGORIES: readonly ['credentials', 'tokens', 'real names', 'email addresses', 'account identifiers', 'transaction content'];
|
|
30
|
+
export type ExcludedCategory = (typeof EXCLUDED_CATEGORIES)[number];
|
|
31
|
+
export type ExcludedContentPattern = {
|
|
32
|
+
readonly category: ExcludedCategory;
|
|
33
|
+
readonly regex: RegExp;
|
|
34
|
+
/** a string the pattern must match, so a rotted pattern fails its own test. */
|
|
35
|
+
readonly fires: string;
|
|
36
|
+
};
|
|
37
|
+
/**
|
|
38
|
+
* `real names` has no member here and never will: a personal name has no surface
|
|
39
|
+
* form, so every pattern that reaches one reads the field that carries it, which
|
|
40
|
+
* is a name-shaped test. The category stays in the tuple above because AD-18
|
|
41
|
+
* names six and dropping one from the vocabulary would make the gate look total.
|
|
42
|
+
*/
|
|
43
|
+
export declare const EXCLUDED_VALUE_PATTERNS: readonly ExcludedContentPattern[];
|
|
44
|
+
export type ExcludedContentHit = {
|
|
45
|
+
readonly category: ExcludedCategory;
|
|
46
|
+
/** the artifact-rooted path of the string that matched. */
|
|
47
|
+
readonly path: string;
|
|
48
|
+
/** the matched run, which is short by construction and is what a caller redacts. */
|
|
49
|
+
readonly match: string;
|
|
50
|
+
};
|
|
51
|
+
/**
|
|
52
|
+
* Every string anywhere inside one value, against every value-shaped pattern.
|
|
53
|
+
*
|
|
54
|
+
* Object keys are walked as paths and never scanned as content: a key is a name
|
|
55
|
+
* by construction, which is the same line the pattern set is drawn on. The walk
|
|
56
|
+
* is total over JSON and stops at nothing, so a token nested under a body
|
|
57
|
+
* example twelve levels down is found; the first matching category per string
|
|
58
|
+
* wins, since one string reported six times says nothing more than once.
|
|
59
|
+
*
|
|
60
|
+
* Order is the walk's order, which for an object is its own key order. A caller
|
|
61
|
+
* that needs a stable report over two spellings of the same object sorts what
|
|
62
|
+
* comes back; the compile gate below reads only the first hit and never the
|
|
63
|
+
* order.
|
|
64
|
+
*/
|
|
65
|
+
export declare function scanExcludedContent(value: unknown, path: string): readonly ExcludedContentHit[];
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AD-18's excluded categories, as patterns that read a value rather than a name.
|
|
3
|
+
*
|
|
4
|
+
* AD-18 excludes "credentials, tokens, real names, email addresses, account
|
|
5
|
+
* identifiers, and transaction content" from every artifact this package
|
|
6
|
+
* produces or publishes, and binds "published examples and test fixtures as
|
|
7
|
+
* strictly as real runs". Until this module the whole prohibition was carried by
|
|
8
|
+
* one test that scans `corpus/dev`, so a contract carrying a live-shaped token
|
|
9
|
+
* compiled clean and the token travelled into the sealed brief.
|
|
10
|
+
*
|
|
11
|
+
* Only the value-shaped half of that scan ships here, and the split is the point
|
|
12
|
+
* rather than an economy. AD-18's own reading of the rule for the header channel
|
|
13
|
+
* is that a declaration "names the header and its type and never carries a
|
|
14
|
+
* credential value", so a name is what an author is supposed to write: an
|
|
15
|
+
* operation declaring a `password` body key or an `Authorization` header is a
|
|
16
|
+
* contract for an authentication API, and a gate matching `\bpassword\b` would
|
|
17
|
+
* reject exactly the systems this package exists to evaluate. The patterns below
|
|
18
|
+
* therefore match only strings whose shape is decidable without knowing what
|
|
19
|
+
* field they sit in: a PEM header, a token with an issuer prefix, an address, a
|
|
20
|
+
* checkable account number, a card-shaped digit run. The name-shaped patterns
|
|
21
|
+
* stay in `tests/architecture/dev-corpus.test.ts`, where review rather than a
|
|
22
|
+
* compiler decides.
|
|
23
|
+
*
|
|
24
|
+
* Each pattern carries a string it must fire on, and a test asserts every one
|
|
25
|
+
* still does. A pattern that has rotted into a regex matching nothing is worse
|
|
26
|
+
* than no pattern: it reports clean forever.
|
|
27
|
+
*/
|
|
28
|
+
/** AD-18's six categories, in the rule's own order. */
|
|
29
|
+
export const EXCLUDED_CATEGORIES = [
|
|
30
|
+
'credentials',
|
|
31
|
+
'tokens',
|
|
32
|
+
'real names',
|
|
33
|
+
'email addresses',
|
|
34
|
+
'account identifiers',
|
|
35
|
+
'transaction content',
|
|
36
|
+
];
|
|
37
|
+
/**
|
|
38
|
+
* `real names` has no member here and never will: a personal name has no surface
|
|
39
|
+
* form, so every pattern that reaches one reads the field that carries it, which
|
|
40
|
+
* is a name-shaped test. The category stays in the tuple above because AD-18
|
|
41
|
+
* names six and dropping one from the vocabulary would make the gate look total.
|
|
42
|
+
*/
|
|
43
|
+
export const EXCLUDED_VALUE_PATTERNS = [
|
|
44
|
+
{
|
|
45
|
+
category: 'credentials',
|
|
46
|
+
regex: /-----BEGIN [A-Z ]*PRIVATE KEY-----/,
|
|
47
|
+
fires: '-----BEGIN RSA PRIVATE KEY-----',
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
category: 'tokens',
|
|
51
|
+
regex: /(?<![A-Za-z])(?:[Bb]earer\s+[\w.~+/-]{16,}|eyJ[\w-]{8,}\.[\w-]{8,}|sk-[A-Za-z0-9]{16,}|gh[pousr]_[A-Za-z0-9]{16,}|AKIA[0-9A-Z]{16})/,
|
|
52
|
+
fires: 'Authorization: Bearer abcdefghijklmnopqrstuv',
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
category: 'email addresses',
|
|
56
|
+
regex: /[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}/,
|
|
57
|
+
fires: '{"contact":"ada@example.com"}',
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
// Case-sensitive: an IBAN is upper case, and a case-insensitive form
|
|
61
|
+
// matches a run inside a lower-case hex digest.
|
|
62
|
+
category: 'account identifiers',
|
|
63
|
+
regex: /\b[A-Z]{2}\d{2}[A-Z0-9]{11,30}\b|\b\d{3}-\d{2}-\d{4}\b/,
|
|
64
|
+
fires: 'GB29NWBK60161331926819',
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
category: 'transaction content',
|
|
68
|
+
regex: /(?<![0-9A-Za-z])\d{4}(?:[ -]\d{4}){3}(?![0-9A-Za-z])/,
|
|
69
|
+
fires: 'charged 4111 1111 1111 1111',
|
|
70
|
+
},
|
|
71
|
+
];
|
|
72
|
+
const step = (path, key) => typeof key === 'number' ? `${path}[${key}]` : `${path}.${key}`;
|
|
73
|
+
/**
|
|
74
|
+
* Every string anywhere inside one value, against every value-shaped pattern.
|
|
75
|
+
*
|
|
76
|
+
* Object keys are walked as paths and never scanned as content: a key is a name
|
|
77
|
+
* by construction, which is the same line the pattern set is drawn on. The walk
|
|
78
|
+
* is total over JSON and stops at nothing, so a token nested under a body
|
|
79
|
+
* example twelve levels down is found; the first matching category per string
|
|
80
|
+
* wins, since one string reported six times says nothing more than once.
|
|
81
|
+
*
|
|
82
|
+
* Order is the walk's order, which for an object is its own key order. A caller
|
|
83
|
+
* that needs a stable report over two spellings of the same object sorts what
|
|
84
|
+
* comes back; the compile gate below reads only the first hit and never the
|
|
85
|
+
* order.
|
|
86
|
+
*/
|
|
87
|
+
export function scanExcludedContent(value, path) {
|
|
88
|
+
const hits = [];
|
|
89
|
+
const visit = (node, at) => {
|
|
90
|
+
if (typeof node === 'string') {
|
|
91
|
+
for (const { category, regex } of EXCLUDED_VALUE_PATTERNS) {
|
|
92
|
+
const match = regex.exec(node);
|
|
93
|
+
if (match === null)
|
|
94
|
+
continue;
|
|
95
|
+
hits.push({ category, path: at, match: match[0] });
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
if (Array.isArray(node)) {
|
|
101
|
+
node.forEach((element, index) => {
|
|
102
|
+
visit(element, step(at, index));
|
|
103
|
+
});
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
if (node === null || typeof node !== 'object')
|
|
107
|
+
return;
|
|
108
|
+
for (const [key, child] of Object.entries(node))
|
|
109
|
+
visit(child, step(at, key));
|
|
110
|
+
};
|
|
111
|
+
visit(value, path);
|
|
112
|
+
return hits;
|
|
113
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
/** AD-5's twenty-
|
|
2
|
-
export declare const FAILURE_CODES: readonly ['missing-requirement-linkage', 'no-observable-success-criterion', 'unreachable-check-evidence', 'malformed-operator-expression', 'quantifier-over-non-collection', 'quantifier-nesting-exceeded', 'unresolved-reference-set', 'duplicate-operation-signature', 'undeclared-mandatory-input', 'oracle-missing-channel', 'direction-check-misaligned', 'unsupported-interface-kind', 'nested-temporal-clause', 'plan-exceeds-scripting-bound', 'rubric-scores-reasoning-prose', 'rubric-unanchored', 'rubric-evidence-unreachable', 'forbidden-input-floor-incomplete', 'scoped-reference-resolves-forbidden', 'waiver-incomplete', 'brief-exceeds-scripting-bound'];
|
|
1
|
+
/** AD-5's twenty-six compile-time failure codes, as data, in the table's order. */
|
|
2
|
+
export declare const FAILURE_CODES: readonly ['missing-requirement-linkage', 'no-observable-success-criterion', 'unreachable-check-evidence', 'malformed-operator-expression', 'quantifier-over-non-collection', 'quantifier-nesting-exceeded', 'unresolved-reference-set', 'duplicate-operation-signature', 'undeclared-mandatory-input', 'oracle-missing-channel', 'direction-check-misaligned', 'unsupported-interface-kind', 'nested-temporal-clause', 'plan-exceeds-scripting-bound', 'binding-cycle', 'captured-channel-undeclared', 'rubric-scores-reasoning-prose', 'rubric-unanchored', 'rubric-evidence-unreachable', 'forbidden-input-floor-incomplete', 'scoped-reference-resolves-forbidden', 'waiver-incomplete', 'brief-exceeds-scripting-bound', 'unresolved-artifact-reference', 'irreducible-step-reference', 'excluded-content-in-declaration'];
|
|
3
3
|
export type FailureCode = (typeof FAILURE_CODES)[number];
|
|
4
4
|
/**
|
|
5
5
|
* AD-5's compile-time failure registry: thrown when a structural error fails
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
/** AD-5's twenty-
|
|
1
|
+
/** AD-5's twenty-six compile-time failure codes, as data, in the table's order. */
|
|
2
2
|
// This lives in `core/`, since it's a plain data tuple and `core/schemas/`
|
|
3
3
|
// is reserved for Zod definitions. The compiler that emits these codes lives
|
|
4
4
|
// in `core/compile/`, one directory over.
|
|
5
5
|
//
|
|
6
6
|
// `scripts/check-ad5-registry.ts` checks this tuple against AD-5's own
|
|
7
7
|
// registry table under `npm run validate`; the tuple's own invariants
|
|
8
|
-
// (twenty-
|
|
8
|
+
// (twenty-six members, unique, kebab-case) are locked in
|
|
9
9
|
// tests/schemas/failure-codes.test.ts.
|
|
10
10
|
export const FAILURE_CODES = [
|
|
11
11
|
'missing-requirement-linkage',
|
|
@@ -22,6 +22,8 @@ export const FAILURE_CODES = [
|
|
|
22
22
|
'unsupported-interface-kind',
|
|
23
23
|
'nested-temporal-clause',
|
|
24
24
|
'plan-exceeds-scripting-bound',
|
|
25
|
+
'binding-cycle',
|
|
26
|
+
'captured-channel-undeclared',
|
|
25
27
|
'rubric-scores-reasoning-prose',
|
|
26
28
|
'rubric-unanchored',
|
|
27
29
|
'rubric-evidence-unreachable',
|
|
@@ -29,6 +31,9 @@ export const FAILURE_CODES = [
|
|
|
29
31
|
'scoped-reference-resolves-forbidden',
|
|
30
32
|
'waiver-incomplete',
|
|
31
33
|
'brief-exceeds-scripting-bound',
|
|
34
|
+
'unresolved-artifact-reference',
|
|
35
|
+
'irreducible-step-reference',
|
|
36
|
+
'excluded-content-in-declaration',
|
|
32
37
|
];
|
|
33
38
|
/**
|
|
34
39
|
* AD-5's compile-time failure registry: thrown when a structural error fails
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The eleven conditions `core/ingest` detects, and the ladder input each one
|
|
3
|
+
* feeds.
|
|
4
|
+
*
|
|
5
|
+
* Each condition is a cross-artifact rule a shipped schema names `core/ingest`
|
|
6
|
+
* as the enforcement point for, and none of them is a parse failure: the
|
|
7
|
+
* artifacts arrive already parsed, and a rule two conforming artifacts break
|
|
8
|
+
* between them is a finding about the run rather than a rejection of its bytes.
|
|
9
|
+
*
|
|
10
|
+
* The kinds are a runtime tuple first and the union draws from it, the idiom
|
|
11
|
+
* `RUNTIME_FAULT_CODES`, `FORBIDDEN_INPUT_FLOOR`, and `ORACLE_DISPOSITIONS` are
|
|
12
|
+
* written in. A union's `kind` literals are erased at compile time, so a drift
|
|
13
|
+
* check over a bare union could only compare the mapping below against itself or
|
|
14
|
+
* against a hand-written second list, which is the drift it exists to catch.
|
|
15
|
+
*
|
|
16
|
+
* `ladder.ts` is imported type-only on purpose. `isolatedModules` without
|
|
17
|
+
* `verbatimModuleSyntax` compiles a value import used only in type position
|
|
18
|
+
* silently, which would put the whole ladder module on this module's runtime
|
|
19
|
+
* load path; `preflight/reduce.ts` imports `ReduceStage` the same way.
|
|
20
|
+
*/
|
|
21
|
+
import type { ForbiddenInput } from '../schemas/eval-contract.ts';
|
|
22
|
+
import type { EvidenceIntegrityInputs, OutcomeStateInputs } from '../score/ladder.ts';
|
|
23
|
+
import type { UnwitnessedQuotation } from '../score/quotation.ts';
|
|
24
|
+
/**
|
|
25
|
+
* The eleven kinds, in the order `ingest` records them. The two isolation-family
|
|
26
|
+
* kinds and the two evaluator-configuration ones are sequenced here; the order
|
|
27
|
+
* of the values inside one `isolation-manifest-violation` is the manifest's own
|
|
28
|
+
* array order and belongs to the stage, not to this tuple.
|
|
29
|
+
*/
|
|
30
|
+
export declare const INGEST_CONDITION_KINDS: readonly ['duplicate-record-identifier', 'dangling-citation', 'dangling-disposition-citation', 'unwitnessed-quotation', 'isolation-manifest-absent', 'isolation-manifest-violation', 'forbidden-input-not-withheld', 'cross-artifact-disagreement', 'evaluator-configuration-absent', 'evaluator-configuration-digest-mismatch', 'judge-result-unscored'];
|
|
31
|
+
export type IngestConditionKind = (typeof INGEST_CONDITION_KINDS)[number];
|
|
32
|
+
/**
|
|
33
|
+
* The three fields AD-32 requires the sealed run record and the isolation
|
|
34
|
+
* manifest to agree on, in the order a disagreement is reported.
|
|
35
|
+
*
|
|
36
|
+
* `conditionArm` sits on both artifacts and is deliberately excluded: both
|
|
37
|
+
* schemas describe it as "an opaque caller label with no product semantics, per
|
|
38
|
+
* AD-24", so making a mismatch invalidate would mint a normative rule with no
|
|
39
|
+
* AD behind it and would be the one invalidating condition in the design with
|
|
40
|
+
* nothing downstream reading it.
|
|
41
|
+
*/
|
|
42
|
+
export declare const AGREEMENT_FIELDS: readonly ['runId', 'contractDigest', 'evaluatorConfigurationDigest'];
|
|
43
|
+
export type AgreementField = (typeof AGREEMENT_FIELDS)[number];
|
|
44
|
+
/**
|
|
45
|
+
* An identifier the record uses twice where everything downstream addresses by
|
|
46
|
+
* it once.
|
|
47
|
+
*
|
|
48
|
+
* `SealedRunRecord` refines exactly one array for uniqueness, and it refines it
|
|
49
|
+
* on `sequence` rather than on an identifier, so all three of `observationId`,
|
|
50
|
+
* `findingId`, and `oracleId` may repeat in a record that parses. Nothing
|
|
51
|
+
* downstream can then tell the pair apart: `verdictBasis` names a finding by its
|
|
52
|
+
* identifier, `FindingConfidence` is keyed by it, `resolveOutcome` reads one
|
|
53
|
+
* disposition per oracle, and `auditQuotation` indexes observations into a `Map`
|
|
54
|
+
* keyed on `observationId`, which is last-write-wins. Recorded rather than
|
|
55
|
+
* repaired, because choosing which of two entries an identifier means is not a
|
|
56
|
+
* choice this stage can make.
|
|
57
|
+
*
|
|
58
|
+
* The observation case is the one with teeth. A repeated `observationId` decides
|
|
59
|
+
* which body a quotation is checked against, so it changes whether a defect
|
|
60
|
+
* finding's evidence is witnessed at all, which is the AD-32
|
|
61
|
+
* declared-versus-observed property this stage exists to check. The other two
|
|
62
|
+
* change an order; this one changes an answer.
|
|
63
|
+
*
|
|
64
|
+
* It is also what makes the product's `findings` and `dispositions` order total,
|
|
65
|
+
* and `unwitnessedQuotations` a function of the record's content, in every
|
|
66
|
+
* record that does not carry this condition.
|
|
67
|
+
*/
|
|
68
|
+
export type DuplicateRecordIdentifier = {
|
|
69
|
+
readonly kind: Extract<IngestConditionKind, 'duplicate-record-identifier'>;
|
|
70
|
+
readonly subject: 'observation' | 'finding' | 'oracle-disposition' | 'judge-result';
|
|
71
|
+
readonly identifier: string;
|
|
72
|
+
readonly occurrences: number;
|
|
73
|
+
};
|
|
74
|
+
/**
|
|
75
|
+
* A finding citing an observation identifier the record does not declare. One
|
|
76
|
+
* per finding, carrying every identifier that resolved to nothing, so a
|
|
77
|
+
* consumer never has to re-derive which half of the citation was bad.
|
|
78
|
+
*/
|
|
79
|
+
export type DanglingCitation = {
|
|
80
|
+
readonly kind: Extract<IngestConditionKind, 'dangling-citation'>;
|
|
81
|
+
readonly findingId: string;
|
|
82
|
+
readonly unresolvedObservationIds: readonly string[];
|
|
83
|
+
};
|
|
84
|
+
/**
|
|
85
|
+
* A disposition citing an observation identifier the record does not declare.
|
|
86
|
+
*
|
|
87
|
+
* The record's second citation site, and the one nothing else reads. AD-33 wants
|
|
88
|
+
* "every disposition citing supporting observations, and an unsupported
|
|
89
|
+
* disposition invalidating cross-artifact agreement rather than being believed";
|
|
90
|
+
* `resolveOutcome`'s `unsupported-disposition` covers the empty list only, so a
|
|
91
|
+
* `violated` disposition corroborated by a citation to nothing would otherwise
|
|
92
|
+
* be believed. Ingest is the only stage holding the dispositions and the
|
|
93
|
+
* observations at once.
|
|
94
|
+
*/
|
|
95
|
+
export type DanglingDispositionCitation = {
|
|
96
|
+
readonly kind: Extract<IngestConditionKind, 'dangling-disposition-citation'>;
|
|
97
|
+
readonly oracleId: string;
|
|
98
|
+
readonly unresolvedObservationIds: readonly string[];
|
|
99
|
+
};
|
|
100
|
+
/**
|
|
101
|
+
* The quotation audit's own return value, unchanged. `auditQuotation` already
|
|
102
|
+
* shapes the payload its consumer declares, so re-projecting it here would be a
|
|
103
|
+
* second spelling of one result.
|
|
104
|
+
*/
|
|
105
|
+
export type UnwitnessedQuotationCondition = {
|
|
106
|
+
readonly kind: Extract<IngestConditionKind, 'unwitnessed-quotation'>;
|
|
107
|
+
readonly quotations: readonly UnwitnessedQuotation[];
|
|
108
|
+
};
|
|
109
|
+
/**
|
|
110
|
+
* AD-16's absent manifest. No payload: there is no artifact to describe, which
|
|
111
|
+
* is why no schema can carry this rule and why the manifest's own `.meta` hands
|
|
112
|
+
* the violating case to `core/ingest`.
|
|
113
|
+
*/
|
|
114
|
+
export type IsolationManifestAbsent = {
|
|
115
|
+
readonly kind: Extract<IngestConditionKind, 'isolation-manifest-absent'>;
|
|
116
|
+
};
|
|
117
|
+
/**
|
|
118
|
+
* A declared violation, an observed mount, network target, or tool call outside
|
|
119
|
+
* its allowlist, or any combination. One condition rather than four, each
|
|
120
|
+
* exceeded allowlist carrying its own offending values in the manifest's array
|
|
121
|
+
* order, so a consumer reads values rather than parsing them back out of a
|
|
122
|
+
* joined summary.
|
|
123
|
+
*/
|
|
124
|
+
export type IsolationManifestViolation = {
|
|
125
|
+
readonly kind: Extract<IngestConditionKind, 'isolation-manifest-violation'>;
|
|
126
|
+
readonly violation: string | null;
|
|
127
|
+
readonly mountsOutsideAllowlist: readonly string[];
|
|
128
|
+
readonly networkTargetsOutsideAllowlist: readonly string[];
|
|
129
|
+
readonly toolCallsOutsideAllowlist: readonly string[];
|
|
130
|
+
};
|
|
131
|
+
/**
|
|
132
|
+
* AD-16's first clause: a prohibited input the manifest accounts for with
|
|
133
|
+
* `withheld: false`. Named in `FORBIDDEN_INPUT_FLOOR` order, since that list is
|
|
134
|
+
* the floor's one home and the manifest's key order is the caller's.
|
|
135
|
+
*/
|
|
136
|
+
export type ForbiddenInputNotWithheld = {
|
|
137
|
+
readonly kind: Extract<IngestConditionKind, 'forbidden-input-not-withheld'>;
|
|
138
|
+
readonly inputs: readonly ForbiddenInput[];
|
|
139
|
+
};
|
|
140
|
+
/**
|
|
141
|
+
* One field the two artifacts disagree on, carrying both values. Per field
|
|
142
|
+
* rather than per record: a run whose two artifacts disagree about which
|
|
143
|
+
* configuration produced it is a different finding from one that disagrees
|
|
144
|
+
* about which run it is.
|
|
145
|
+
*/
|
|
146
|
+
export type CrossArtifactDisagreement = {
|
|
147
|
+
readonly kind: Extract<IngestConditionKind, 'cross-artifact-disagreement'>;
|
|
148
|
+
readonly field: AgreementField;
|
|
149
|
+
readonly recordValue: string;
|
|
150
|
+
readonly manifestValue: string;
|
|
151
|
+
};
|
|
152
|
+
/**
|
|
153
|
+
* AD-24's absent evaluator configuration. No payload, for the same reason
|
|
154
|
+
* `IsolationManifestAbsent` carries none: there is no artifact to describe.
|
|
155
|
+
*/
|
|
156
|
+
export type EvaluatorConfigurationAbsent = {
|
|
157
|
+
readonly kind: Extract<IngestConditionKind, 'evaluator-configuration-absent'>;
|
|
158
|
+
};
|
|
159
|
+
/**
|
|
160
|
+
* The digest the record declares for the evaluator configuration against the
|
|
161
|
+
* one that artifact actually produces.
|
|
162
|
+
*
|
|
163
|
+
* The operand is the record's declaration. A disagreement between the record's
|
|
164
|
+
* and the manifest's declarations is already `cross-artifact-disagreement`, and
|
|
165
|
+
* what this condition adds is the half AD-32 calls the trust boundary: both
|
|
166
|
+
* declarations are caller-attested, and only the recomputation reads the
|
|
167
|
+
* artifact.
|
|
168
|
+
*/
|
|
169
|
+
export type EvaluatorConfigurationDigestMismatch = {
|
|
170
|
+
readonly kind: Extract<IngestConditionKind, 'evaluator-configuration-digest-mismatch'>;
|
|
171
|
+
readonly declaredDigest: string;
|
|
172
|
+
readonly computedDigest: string;
|
|
173
|
+
};
|
|
174
|
+
/**
|
|
175
|
+
* AD-17's record-decidable half: a judge result carrying `score: null`. The
|
|
176
|
+
* other half, that the criterion is one the cited rubric declares, needs the
|
|
177
|
+
* rubric, which no stage row declares as an input.
|
|
178
|
+
*/
|
|
179
|
+
export type JudgeResultUnscored = {
|
|
180
|
+
readonly kind: Extract<IngestConditionKind, 'judge-result-unscored'>;
|
|
181
|
+
readonly rubricId: string;
|
|
182
|
+
readonly criterionId: string;
|
|
183
|
+
};
|
|
184
|
+
export type IngestCondition = DuplicateRecordIdentifier | DanglingCitation | DanglingDispositionCitation | UnwitnessedQuotationCondition | IsolationManifestAbsent | IsolationManifestViolation | ForbiddenInputNotWithheld | CrossArtifactDisagreement | EvaluatorConfigurationAbsent | EvaluatorConfigurationDigestMismatch | JudgeResultUnscored;
|
|
185
|
+
/**
|
|
186
|
+
* The ladder field a condition feeds. Every one of the eleven kinds now
|
|
187
|
+
* carries a rung, so the union below names all ten `EvidenceIntegrityInputs`
|
|
188
|
+
* fields that carry an ingest condition and `null` is dropped entirely: a
|
|
189
|
+
* kind with no rung is a compile error now, not a documented gap.
|
|
190
|
+
*
|
|
191
|
+
* Ten pairs built with `Extract` rather than a bare `keyof` product: the
|
|
192
|
+
* product would also pull in fields like `internallyInconsistent`, whose row
|
|
193
|
+
* is FAIL and whose guard reads "internally inconsistent under AD-17" rather
|
|
194
|
+
* than reading an `IngestCondition`, and a bare key would lose which of the
|
|
195
|
+
* two input types a member came from. Renaming a field in `ladder.ts`
|
|
196
|
+
* collapses its branch here to `never`, so the mapping below stops compiling
|
|
197
|
+
* instead of pointing at a field that no longer exists.
|
|
198
|
+
*/
|
|
199
|
+
export type LadderTarget = Extract<keyof EvidenceIntegrityInputs, 'isolationViolation'> | Extract<keyof OutcomeStateInputs, 'unwitnessedQuotations'> | Extract<keyof EvidenceIntegrityInputs, 'duplicateRecordIdentifiers'> | Extract<keyof EvidenceIntegrityInputs, 'danglingCitations'> | Extract<keyof EvidenceIntegrityInputs, 'danglingDispositionCitations'> | Extract<keyof EvidenceIntegrityInputs, 'forbiddenInputsNotWithheld'> | Extract<keyof EvidenceIntegrityInputs, 'crossArtifactDisagreements'> | Extract<keyof EvidenceIntegrityInputs, 'evaluatorConfigurationAbsent'> | Extract<keyof EvidenceIntegrityInputs, 'evaluatorConfigurationDigestMismatches'> | Extract<keyof EvidenceIntegrityInputs, 'judgeResultsUnscored'>;
|
|
200
|
+
/** Total over the kinds tuple: a new kind fails to compile until it declares its rung. */
|
|
201
|
+
export declare const LADDER_TARGETS: Record<IngestConditionKind, LadderTarget>;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The eleven kinds, in the order `ingest` records them. The two isolation-family
|
|
3
|
+
* kinds and the two evaluator-configuration ones are sequenced here; the order
|
|
4
|
+
* of the values inside one `isolation-manifest-violation` is the manifest's own
|
|
5
|
+
* array order and belongs to the stage, not to this tuple.
|
|
6
|
+
*/
|
|
7
|
+
export const INGEST_CONDITION_KINDS = [
|
|
8
|
+
'duplicate-record-identifier',
|
|
9
|
+
'dangling-citation',
|
|
10
|
+
'dangling-disposition-citation',
|
|
11
|
+
'unwitnessed-quotation',
|
|
12
|
+
'isolation-manifest-absent',
|
|
13
|
+
'isolation-manifest-violation',
|
|
14
|
+
'forbidden-input-not-withheld',
|
|
15
|
+
'cross-artifact-disagreement',
|
|
16
|
+
'evaluator-configuration-absent',
|
|
17
|
+
'evaluator-configuration-digest-mismatch',
|
|
18
|
+
'judge-result-unscored',
|
|
19
|
+
];
|
|
20
|
+
/**
|
|
21
|
+
* The three fields AD-32 requires the sealed run record and the isolation
|
|
22
|
+
* manifest to agree on, in the order a disagreement is reported.
|
|
23
|
+
*
|
|
24
|
+
* `conditionArm` sits on both artifacts and is deliberately excluded: both
|
|
25
|
+
* schemas describe it as "an opaque caller label with no product semantics, per
|
|
26
|
+
* AD-24", so making a mismatch invalidate would mint a normative rule with no
|
|
27
|
+
* AD behind it and would be the one invalidating condition in the design with
|
|
28
|
+
* nothing downstream reading it.
|
|
29
|
+
*/
|
|
30
|
+
export const AGREEMENT_FIELDS = [
|
|
31
|
+
'runId',
|
|
32
|
+
'contractDigest',
|
|
33
|
+
'evaluatorConfigurationDigest',
|
|
34
|
+
];
|
|
35
|
+
/** Total over the kinds tuple: a new kind fails to compile until it declares its rung. */
|
|
36
|
+
export const LADDER_TARGETS = {
|
|
37
|
+
'duplicate-record-identifier': 'duplicateRecordIdentifiers',
|
|
38
|
+
'dangling-citation': 'danglingCitations',
|
|
39
|
+
'dangling-disposition-citation': 'danglingDispositionCitations',
|
|
40
|
+
'unwitnessed-quotation': 'unwitnessedQuotations',
|
|
41
|
+
'isolation-manifest-absent': 'isolationViolation',
|
|
42
|
+
'isolation-manifest-violation': 'isolationViolation',
|
|
43
|
+
'forbidden-input-not-withheld': 'forbiddenInputsNotWithheld',
|
|
44
|
+
'cross-artifact-disagreement': 'crossArtifactDisagreements',
|
|
45
|
+
'evaluator-configuration-absent': 'evaluatorConfigurationAbsent',
|
|
46
|
+
'evaluator-configuration-digest-mismatch': 'evaluatorConfigurationDigestMismatches',
|
|
47
|
+
'judge-result-unscored': 'judgeResultsUnscored',
|
|
48
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The ingest stage's surface for the rest of `core/`: the stage itself, the
|
|
3
|
+
* product it owns, the condition vocabulary, and the ladder input each
|
|
4
|
+
* condition feeds.
|
|
5
|
+
*
|
|
6
|
+
* Nothing here reaches `src/index.ts` or `src/application/index.ts`.
|
|
7
|
+
* `validated-observations` is an internal stage product AD-24 exempts from
|
|
8
|
+
* publication, so it has no schema, no registry entry, and no published type.
|
|
9
|
+
*/
|
|
10
|
+
export type { AgreementField, CrossArtifactDisagreement, DanglingCitation, DanglingDispositionCitation, DuplicateRecordIdentifier, EvaluatorConfigurationAbsent, EvaluatorConfigurationDigestMismatch, ForbiddenInputNotWithheld, IngestCondition, IngestConditionKind, IsolationManifestAbsent, IsolationManifestViolation, JudgeResultUnscored, LadderTarget, UnwitnessedQuotationCondition, } from './conditions.ts';
|
|
11
|
+
export { AGREEMENT_FIELDS, INGEST_CONDITION_KINDS, LADDER_TARGETS, } from './conditions.ts';
|
|
12
|
+
export type { ValidatedObservations } from './ingest.ts';
|
|
13
|
+
export { ingest } from './ingest.ts';
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The ingest stage's surface for the rest of `core/`: the stage itself, the
|
|
3
|
+
* product it owns, the condition vocabulary, and the ladder input each
|
|
4
|
+
* condition feeds.
|
|
5
|
+
*
|
|
6
|
+
* Nothing here reaches `src/index.ts` or `src/application/index.ts`.
|
|
7
|
+
* `validated-observations` is an internal stage product AD-24 exempts from
|
|
8
|
+
* publication, so it has no schema, no registry entry, and no published type.
|
|
9
|
+
*/
|
|
10
|
+
export { AGREEMENT_FIELDS, INGEST_CONDITION_KINDS, LADDER_TARGETS, } from './conditions.js';
|
|
11
|
+
export { ingest } from './ingest.js';
|