eval-quality 0.1.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.
Files changed (230) hide show
  1. package/LICENSE +190 -0
  2. package/README.md +355 -0
  3. package/corpus/dev/README.md +36 -0
  4. package/corpus/dev/compile-seal-example/brief.json +1 -0
  5. package/corpus/dev/compile-seal-example/contract.json +1 -0
  6. package/corpus/dev/contracts/absent-collection-locations.json +1 -0
  7. package/corpus/dev/contracts/absent-sibling-groups.json +1 -0
  8. package/corpus/dev/contracts/absent-success-indicator.json +1 -0
  9. package/corpus/dev/contracts/empty-channel-roles.json +1 -0
  10. package/corpus/dev/contracts/empty-collection-locations.json +1 -0
  11. package/corpus/dev/contracts/empty-request-shapes.json +1 -0
  12. package/corpus/dev/contracts/empty-sibling-groups.json +1 -0
  13. package/corpus/dev/contracts/no-collection-quantifier.json +1 -0
  14. package/corpus/dev/contracts/no-operation-inventory.json +1 -0
  15. package/corpus/dev/contracts/no-read-back-relation.json +1 -0
  16. package/corpus/dev/contracts/no-state-change-marker.json +1 -0
  17. package/corpus/dev/contracts/no-type-violating-step.json +1 -0
  18. package/corpus/dev/contracts/per-key-split-oracles.json +1 -0
  19. package/corpus/dev/contracts/satisfied-declarations.json +1 -0
  20. package/corpus/dev/contracts/single-required-response-key.json +1 -0
  21. package/corpus/dev/contracts/split-indicator-oracle.json +1 -0
  22. package/corpus/dev/contracts/unaddressed-parameter-sibling.json +1 -0
  23. package/corpus/dev/contracts/unnamed-reference-set.json +1 -0
  24. package/corpus/dev/contracts/wrong-cardinality-form.json +1 -0
  25. package/corpus/dev/index.json +1 -0
  26. package/dist/adapters/index.d.ts +12 -0
  27. package/dist/adapters/index.js +3 -0
  28. package/dist/adapters/local-corpus-adapter.d.ts +7 -0
  29. package/dist/adapters/local-corpus-adapter.js +78 -0
  30. package/dist/adapters/node-file-system-adapter.d.ts +7 -0
  31. package/dist/adapters/node-file-system-adapter.js +39 -0
  32. package/dist/adapters/port-boundary.d.ts +18 -0
  33. package/dist/adapters/port-boundary.js +83 -0
  34. package/dist/adapters/system-clock-adapter.d.ts +10 -0
  35. package/dist/adapters/system-clock-adapter.js +18 -0
  36. package/dist/application/compile.d.ts +4 -0
  37. package/dist/application/compile.js +24 -0
  38. package/dist/application/diagnostics.d.ts +22 -0
  39. package/dist/application/diagnostics.js +18 -0
  40. package/dist/application/index.d.ts +23 -0
  41. package/dist/application/index.js +17 -0
  42. package/dist/application/invoke-port.d.ts +2 -0
  43. package/dist/application/invoke-port.js +45 -0
  44. package/dist/application/preflight.d.ts +27 -0
  45. package/dist/application/preflight.js +131 -0
  46. package/dist/application/seal.d.ts +4 -0
  47. package/dist/application/seal.js +28 -0
  48. package/dist/application/serialize.d.ts +6 -0
  49. package/dist/application/serialize.js +9 -0
  50. package/dist/cli/arguments.d.ts +28 -0
  51. package/dist/cli/arguments.js +167 -0
  52. package/dist/cli/exit-codes.d.ts +36 -0
  53. package/dist/cli/exit-codes.js +39 -0
  54. package/dist/cli/main.d.ts +2 -0
  55. package/dist/cli/main.js +143 -0
  56. package/dist/cli/render.d.ts +26 -0
  57. package/dist/cli/render.js +53 -0
  58. package/dist/cli/run.d.ts +41 -0
  59. package/dist/cli/run.js +203 -0
  60. package/dist/core/canonical/canonicalize.d.ts +1 -0
  61. package/dist/core/canonical/canonicalize.js +113 -0
  62. package/dist/core/canonical/digest.d.ts +7 -0
  63. package/dist/core/canonical/digest.js +81 -0
  64. package/dist/core/canonical/scan-json.d.ts +1 -0
  65. package/dist/core/canonical/scan-json.js +298 -0
  66. package/dist/core/canonical/value-domain.d.ts +4 -0
  67. package/dist/core/canonical/value-domain.js +113 -0
  68. package/dist/core/compile/compile.d.ts +24 -0
  69. package/dist/core/compile/compile.js +67 -0
  70. package/dist/core/compile/declarations.d.ts +5 -0
  71. package/dist/core/compile/declarations.js +22 -0
  72. package/dist/core/compile/expression-legality.d.ts +14 -0
  73. package/dist/core/compile/expression-legality.js +346 -0
  74. package/dist/core/compile/forbidden-inputs.d.ts +5 -0
  75. package/dist/core/compile/forbidden-inputs.js +19 -0
  76. package/dist/core/compile/interface-inventory.d.ts +10 -0
  77. package/dist/core/compile/interface-inventory.js +55 -0
  78. package/dist/core/compile/oracle-alignment.d.ts +14 -0
  79. package/dist/core/compile/oracle-alignment.js +93 -0
  80. package/dist/core/compile/reachability.d.ts +17 -0
  81. package/dist/core/compile/reachability.js +177 -0
  82. package/dist/core/compile/rubrics.d.ts +55 -0
  83. package/dist/core/compile/rubrics.js +198 -0
  84. package/dist/core/compile/scripting-bound.d.ts +5 -0
  85. package/dist/core/compile/scripting-bound.js +151 -0
  86. package/dist/core/compile/sensitivity-witness.d.ts +73 -0
  87. package/dist/core/compile/sensitivity-witness.js +232 -0
  88. package/dist/core/compile/waivers.d.ts +3 -0
  89. package/dist/core/compile/waivers.js +21 -0
  90. package/dist/core/coverage/coverage.d.ts +18 -0
  91. package/dist/core/coverage/coverage.js +47 -0
  92. package/dist/core/coverage/relevance.d.ts +75 -0
  93. package/dist/core/coverage/relevance.js +186 -0
  94. package/dist/core/coverage/rules.d.ts +7 -0
  95. package/dist/core/coverage/rules.js +18 -0
  96. package/dist/core/coverage/satisfaction.d.ts +107 -0
  97. package/dist/core/coverage/satisfaction.js +415 -0
  98. package/dist/core/coverage/table.d.ts +26 -0
  99. package/dist/core/coverage/table.js +222 -0
  100. package/dist/core/declared-inputs.d.ts +16 -0
  101. package/dist/core/declared-inputs.js +18 -0
  102. package/dist/core/evaluate/evidence-resolution.d.ts +54 -0
  103. package/dist/core/evaluate/evidence-resolution.js +153 -0
  104. package/dist/core/evaluate/operators.d.ts +87 -0
  105. package/dist/core/evaluate/operators.js +350 -0
  106. package/dist/core/evaluate/resolution.d.ts +40 -0
  107. package/dist/core/evaluate/resolution.js +336 -0
  108. package/dist/core/evaluate/resolved-value.d.ts +9 -0
  109. package/dist/core/evaluate/resolved-value.js +6 -0
  110. package/dist/core/failure-codes.d.ts +18 -0
  111. package/dist/core/failure-codes.js +50 -0
  112. package/dist/core/lineage/chain.d.ts +49 -0
  113. package/dist/core/lineage/chain.js +210 -0
  114. package/dist/core/lineage/freeze.d.ts +12 -0
  115. package/dist/core/lineage/freeze.js +35 -0
  116. package/dist/core/lineage/stage-table.d.ts +43 -0
  117. package/dist/core/lineage/stage-table.js +106 -0
  118. package/dist/core/preflight/plan.d.ts +67 -0
  119. package/dist/core/preflight/plan.js +313 -0
  120. package/dist/core/preflight/projection.d.ts +40 -0
  121. package/dist/core/preflight/projection.js +96 -0
  122. package/dist/core/preflight/reduce.d.ts +8 -0
  123. package/dist/core/preflight/reduce.js +180 -0
  124. package/dist/core/preflight/witness-evidence.d.ts +42 -0
  125. package/dist/core/preflight/witness-evidence.js +88 -0
  126. package/dist/core/probe/target-policy.d.ts +63 -0
  127. package/dist/core/probe/target-policy.js +328 -0
  128. package/dist/core/schemas/artifact-reference.d.ts +19 -0
  129. package/dist/core/schemas/artifact-reference.js +30 -0
  130. package/dist/core/schemas/artifact.d.ts +1153 -0
  131. package/dist/core/schemas/artifact.js +84 -0
  132. package/dist/core/schemas/constraint-ledger.d.ts +57 -0
  133. package/dist/core/schemas/constraint-ledger.js +143 -0
  134. package/dist/core/schemas/eval-contract.d.ts +402 -0
  135. package/dist/core/schemas/eval-contract.js +147 -0
  136. package/dist/core/schemas/evaluator-configuration.d.ts +37 -0
  137. package/dist/core/schemas/evaluator-configuration.js +43 -0
  138. package/dist/core/schemas/evidence-artifact.d.ts +471 -0
  139. package/dist/core/schemas/evidence-artifact.js +232 -0
  140. package/dist/core/schemas/expression.d.ts +197 -0
  141. package/dist/core/schemas/expression.js +284 -0
  142. package/dist/core/schemas/faults.d.ts +9 -0
  143. package/dist/core/schemas/faults.js +32 -0
  144. package/dist/core/schemas/interface.d.ts +417 -0
  145. package/dist/core/schemas/interface.js +115 -0
  146. package/dist/core/schemas/isolation-manifest.d.ts +136 -0
  147. package/dist/core/schemas/isolation-manifest.js +92 -0
  148. package/dist/core/schemas/lineage.d.ts +21 -0
  149. package/dist/core/schemas/lineage.js +28 -0
  150. package/dist/core/schemas/oracle.d.ts +72 -0
  151. package/dist/core/schemas/oracle.js +35 -0
  152. package/dist/core/schemas/plan.d.ts +115 -0
  153. package/dist/core/schemas/plan.js +58 -0
  154. package/dist/core/schemas/pointer.d.ts +63 -0
  155. package/dist/core/schemas/pointer.js +94 -0
  156. package/dist/core/schemas/port-messages.d.ts +103 -0
  157. package/dist/core/schemas/port-messages.js +90 -0
  158. package/dist/core/schemas/preflight-verdict.d.ts +60 -0
  159. package/dist/core/schemas/preflight-verdict.js +38 -0
  160. package/dist/core/schemas/primitives.d.ts +78 -0
  161. package/dist/core/schemas/primitives.js +121 -0
  162. package/dist/core/schemas/private-artifact-manifest.d.ts +56 -0
  163. package/dist/core/schemas/private-artifact-manifest.js +46 -0
  164. package/dist/core/schemas/probe-body.d.ts +25 -0
  165. package/dist/core/schemas/probe-body.js +23 -0
  166. package/dist/core/schemas/probe-policy.d.ts +76 -0
  167. package/dist/core/schemas/probe-policy.js +33 -0
  168. package/dist/core/schemas/probe.d.ts +189 -0
  169. package/dist/core/schemas/probe.js +82 -0
  170. package/dist/core/schemas/publish.d.ts +30 -0
  171. package/dist/core/schemas/publish.js +135 -0
  172. package/dist/core/schemas/reference-set.d.ts +18 -0
  173. package/dist/core/schemas/reference-set.js +27 -0
  174. package/dist/core/schemas/rubric.d.ts +62 -0
  175. package/dist/core/schemas/rubric.js +68 -0
  176. package/dist/core/schemas/scoring-policy.d.ts +28 -0
  177. package/dist/core/schemas/scoring-policy.js +51 -0
  178. package/dist/core/schemas/sealed-evaluator-brief.d.ts +80 -0
  179. package/dist/core/schemas/sealed-evaluator-brief.js +59 -0
  180. package/dist/core/schemas/sealed-run-record.d.ts +382 -0
  181. package/dist/core/schemas/sealed-run-record.js +215 -0
  182. package/dist/core/schemas/sensitivity-witness.d.ts +138 -0
  183. package/dist/core/schemas/sensitivity-witness.js +86 -0
  184. package/dist/core/schemas/verdict.d.ts +25 -0
  185. package/dist/core/schemas/verdict.js +20 -0
  186. package/dist/core/schemas/waiver.d.ts +17 -0
  187. package/dist/core/schemas/waiver.js +24 -0
  188. package/dist/core/seal/derived-reference.d.ts +36 -0
  189. package/dist/core/seal/derived-reference.js +383 -0
  190. package/dist/core/seal/direction-prose.d.ts +13 -0
  191. package/dist/core/seal/direction-prose.js +120 -0
  192. package/dist/core/seal/plan-index.d.ts +63 -0
  193. package/dist/core/seal/plan-index.js +155 -0
  194. package/dist/core/seal/scripting-audit.d.ts +15 -0
  195. package/dist/core/seal/scripting-audit.js +51 -0
  196. package/dist/core/seal/seal.d.ts +18 -0
  197. package/dist/core/seal/seal.js +155 -0
  198. package/dist/core/stage-contracts.d.ts +30 -0
  199. package/dist/core/stage-contracts.js +1 -0
  200. package/dist/index.d.ts +15 -0
  201. package/dist/index.js +22 -0
  202. package/dist/ports/clock-port.d.ts +13 -0
  203. package/dist/ports/clock-port.js +7 -0
  204. package/dist/ports/corpus-port.d.ts +16 -0
  205. package/dist/ports/corpus-port.js +7 -0
  206. package/dist/ports/environment-probe-port.d.ts +76 -0
  207. package/dist/ports/environment-probe-port.js +32 -0
  208. package/dist/ports/file-system-port.d.ts +27 -0
  209. package/dist/ports/file-system-port.js +11 -0
  210. package/dist/ports/port.d.ts +33 -0
  211. package/dist/ports/port.js +14 -0
  212. package/dist/testing/conformance.d.ts +88 -0
  213. package/dist/testing/conformance.js +281 -0
  214. package/dist/testing/index.d.ts +26 -0
  215. package/dist/testing/index.js +18 -0
  216. package/dist/testing/probe-conformance.d.ts +45 -0
  217. package/dist/testing/probe-conformance.js +186 -0
  218. package/package.json +130 -0
  219. package/schemas/artifact-reference.schema.json +64 -0
  220. package/schemas/eval-contract.schema.json +2145 -0
  221. package/schemas/evaluator-configuration.schema.json +199 -0
  222. package/schemas/evidence-artifact.schema.json +1229 -0
  223. package/schemas/isolation-manifest.schema.json +394 -0
  224. package/schemas/preflight-verdict.schema.json +114 -0
  225. package/schemas/private-artifact-manifest.schema.json +103 -0
  226. package/schemas/probe.schema.json +1151 -0
  227. package/schemas/rubric.schema.json +148 -0
  228. package/schemas/scoring-policy.schema.json +90 -0
  229. package/schemas/sealed-evaluator-brief.schema.json +268 -0
  230. package/schemas/sealed-run-record.schema.json +875 -0
@@ -0,0 +1,155 @@
1
+ import { SCALAR_CHANNELS, TAIL_BEARING_CHANNELS, TRANSPORT_CHANNELS, TRANSPORT_ROOTED_CHANNEL, } from '../schemas/pointer.js';
2
+ import { IDENTIFIER_CHARSET_SOURCE } from '../schemas/primitives.js';
3
+ // An RFC 6901 reference token, respelled here because pointer.ts keeps its
4
+ // own TOKEN/TAIL fragments private. IDENTIFIER_CHARSET_SOURCE and the channel
5
+ // partition below are project vocabulary, so those are imported rather than
6
+ // respelled.
7
+ const TOKEN_SOURCE = '(?:[^/~]|~[01])*';
8
+ const TAIL_SOURCE = `(?:/${TOKEN_SOURCE})*`;
9
+ const alternation = (members) => members.join('|');
10
+ // Mirrors `pointer.ts`'s own `INTERACTION_POINTER_PATTERN` three-branch
11
+ // partition rather than a flatter grammar: a flatter version once silently
12
+ // accepted `/interactions/poll/response-status/oops` (a schema reject) by
13
+ // discarding the bogus trailing segment instead of rejecting the pointer.
14
+ // Named capture groups replace positional indices, avoiding a fragile
15
+ // dependency on `IDENTIFIER_CHARSET_SOURCE` staying free of its own capturing
16
+ // groups.
17
+ const EVIDENCE_TARGET_PATTERN = new RegExp(`^/interactions/(?<stepId>${IDENTIFIER_CHARSET_SOURCE})/(?:(?<tailBearingChannel>${alternation(TAIL_BEARING_CHANNELS)})(?<tailBearingTail>${TAIL_SOURCE})|(?<scalarChannel>${alternation(SCALAR_CHANNELS)})|${TRANSPORT_ROOTED_CHANNEL}/(?<transportChannel>${alternation(TRANSPORT_CHANNELS)})(?<callInputsTail>${TAIL_SOURCE}))$`);
18
+ const isEvidenceChannel = (value) => TAIL_BEARING_CHANNELS.includes(value) ||
19
+ SCALAR_CHANNELS.includes(value);
20
+ const isTransportChannel = (value) => TRANSPORT_CHANNELS.includes(value);
21
+ /**
22
+ * Exported so `core/evaluate/evidence-resolution.ts` decodes pointer tails
23
+ * with these same two functions, avoiding a second private copy (the drift
24
+ * `IDENTIFIER_CHARSET_SOURCE`'s own precedent warns against).
25
+ */
26
+ export const decodeToken = (token) => token.replace(/~1/g, '/').replace(/~0/g, '~');
27
+ export const decodeTail = (tailSource) => tailSource === '' ? [] : tailSource.slice(1).split('/').map(decodeToken);
28
+ /**
29
+ * Parses one `InteractionPointer` string (`INTERACTION_POINTER_PATTERN` in
30
+ * `pointer.ts`) into its step id, channel, transport channel, and tail, using
31
+ * the schema's own channel partition so this accepts exactly what
32
+ * `InteractionPointer.safeParse` accepts. A should-never-happen precondition
33
+ * violation throws `TypeError`, per `digest.ts`'s precedent.
34
+ */
35
+ export function parseEvidenceTarget(pointer) {
36
+ const groups = EVIDENCE_TARGET_PATTERN.exec(pointer)?.groups;
37
+ if (groups === undefined || groups.stepId === undefined) {
38
+ throw new TypeError(`not an interaction-rooted evidence pointer of a recognized channel: ${pointer}`);
39
+ }
40
+ const stepId = groups.stepId;
41
+ if (groups.scalarChannel !== undefined) {
42
+ if (!isEvidenceChannel(groups.scalarChannel)) {
43
+ // Unreachable: SCALAR_CHANNELS is exactly what this group can match.
44
+ throw new TypeError(`unrecognized evidence channel in pointer: ${pointer}`);
45
+ }
46
+ return {
47
+ stepId,
48
+ channel: groups.scalarChannel,
49
+ transportChannel: null,
50
+ tail: [],
51
+ };
52
+ }
53
+ if (groups.tailBearingChannel !== undefined) {
54
+ if (!isEvidenceChannel(groups.tailBearingChannel)) {
55
+ // Unreachable: TAIL_BEARING_CHANNELS is exactly what this group can match.
56
+ throw new TypeError(`unrecognized evidence channel in pointer: ${pointer}`);
57
+ }
58
+ return {
59
+ stepId,
60
+ channel: groups.tailBearingChannel,
61
+ transportChannel: null,
62
+ tail: decodeTail(groups.tailBearingTail ?? ''),
63
+ };
64
+ }
65
+ if (groups.transportChannel !== undefined) {
66
+ if (!isTransportChannel(groups.transportChannel)) {
67
+ // Unreachable: TRANSPORT_CHANNELS is exactly what this group can match.
68
+ throw new TypeError(`call-inputs evidence target names no transport channel: ${pointer}`);
69
+ }
70
+ return {
71
+ stepId,
72
+ channel: 'call-inputs',
73
+ transportChannel: groups.transportChannel,
74
+ tail: decodeTail(groups.callInputsTail ?? ''),
75
+ };
76
+ }
77
+ // Unreachable: the pattern's three branches are exhaustive once stepId matched.
78
+ throw new TypeError(`not an interaction-rooted evidence pointer of a recognized channel: ${pointer}`);
79
+ }
80
+ /**
81
+ * Builds the index once over the whole plan and interface set. Neither
82
+ * schema enforces `stepId`/`operationId` uniqueness. Strict callers keep the
83
+ * default throw instead of resolving by array order. Standalone structural
84
+ * checks can select `unresolved`, which removes every ambiguous identifier
85
+ * from lookup while preserving all unambiguous entries.
86
+ */
87
+ export function buildPlanIndex(interactionPlan, permittedInterfaces, options = {}) {
88
+ const duplicateIds = options.duplicateIds ?? 'throw';
89
+ const steps = new Map();
90
+ const duplicateStepIds = new Set();
91
+ const stepsByOperation = new Map();
92
+ for (const step of interactionPlan) {
93
+ if (steps.has(step.stepId) || duplicateStepIds.has(step.stepId)) {
94
+ if (duplicateIds === 'throw') {
95
+ throw new TypeError(`duplicate interaction step id: ${step.stepId}`);
96
+ }
97
+ steps.delete(step.stepId);
98
+ duplicateStepIds.add(step.stepId);
99
+ }
100
+ else {
101
+ steps.set(step.stepId, step);
102
+ }
103
+ const group = stepsByOperation.get(step.operationId);
104
+ if (group === undefined) {
105
+ stepsByOperation.set(step.operationId, [step]);
106
+ }
107
+ else {
108
+ group.push(step);
109
+ }
110
+ }
111
+ const operations = new Map();
112
+ const duplicateOperationIds = new Set();
113
+ for (const iface of permittedInterfaces) {
114
+ for (const operation of iface.operations) {
115
+ if (operations.has(operation.operationId) ||
116
+ duplicateOperationIds.has(operation.operationId)) {
117
+ if (duplicateIds === 'throw') {
118
+ throw new TypeError(`duplicate operation id across permitted interfaces: ${operation.operationId}`);
119
+ }
120
+ operations.delete(operation.operationId);
121
+ duplicateOperationIds.add(operation.operationId);
122
+ }
123
+ else {
124
+ operations.set(operation.operationId, operation);
125
+ }
126
+ }
127
+ }
128
+ return {
129
+ stepOf: (stepId) => steps.get(stepId),
130
+ operationOf: (operationId) => operations.get(operationId),
131
+ stepsUsing: (operationId) => stepsByOperation.get(operationId) ?? [],
132
+ };
133
+ }
134
+ /**
135
+ * Resolves a step id through the index or throws. Split from `stepOf` so the
136
+ * index itself stays a plain lookup (`| undefined`, per
137
+ * `noUncheckedIndexedAccess`) while callers get one function instead of
138
+ * repeating the `undefined` check. A precondition violation, not a
139
+ * `RuntimeFault`.
140
+ */
141
+ export function resolveStep(index, stepId) {
142
+ const step = index.stepOf(stepId);
143
+ if (step === undefined) {
144
+ throw new TypeError(`evidence target names a step the interaction plan does not declare: ${stepId}`);
145
+ }
146
+ return step;
147
+ }
148
+ /** Resolves an operation id through the index or throws. See `resolveStep`. */
149
+ export function resolveOperation(index, operationId) {
150
+ const operation = index.operationOf(operationId);
151
+ if (operation === undefined) {
152
+ throw new TypeError(`step names an operation the permitted interfaces do not declare: ${operationId}`);
153
+ }
154
+ return operation;
155
+ }
@@ -0,0 +1,15 @@
1
+ import type { SealedEvaluatorBrief } from '../schemas/sealed-evaluator-brief.ts';
2
+ /**
3
+ * `probeStepBound: null` skips the audit (the codebase's null-means-absent
4
+ * convention); a declared `0` is legal and strict, permitting no marker.
5
+ *
6
+ * Checked per direction, never summed across the brief, so an unrelated
7
+ * direction's own count never tightens an already-declared bound.
8
+ *
9
+ * Scoped to `brief.directions[].text` only, leaving `behaviors`,
10
+ * `scopedResources`, and `safetyLimits` unscanned: AD-16 runs this audit
11
+ * after generation over what `seal` generated, and AD-38 names AD-3's
12
+ * generated direction as that output. The other three are carried through
13
+ * from the contract.
14
+ */
15
+ export declare function auditBriefScripting(brief: SealedEvaluatorBrief): void;
@@ -0,0 +1,51 @@
1
+ /**
2
+ * The post-generation scripting audit (AD-16, `brief-exceeds-scripting-bound`).
3
+ * The declaration-side graph predicate over the interaction plan
4
+ * (`core/compile/scripting-bound.ts`) can't see free text an author wrote
5
+ * directly onto a direction's `scope`/`negativeDomain`. This audit is the
6
+ * other half: it counts sequencing/transition vocabulary in each direction's
7
+ * rendered `text`. The templates in `direction-prose.ts` compose no ordering
8
+ * word of their own, so a match arrives from the author's `scope` or
9
+ * `negativeDomain`, or from an `operationId` that `derived-reference.ts`
10
+ * humanizes into one (`next-page` renders "the next page endpoint").
11
+ */
12
+ import { StructuralFailure } from '../failure-codes.js';
13
+ const AD5_CODE = 'brief-exceeds-scripting-bound';
14
+ // Matches the ordering vocabulary forbidden in generator-composed text,
15
+ // minus bare ordinals (bare "first" also appears as a data-position
16
+ // adjective in accepted author prose), plus a numbered- or lettered-list
17
+ // marker (`1.`, `2)`, `a)`). `afterward`/`subsequent` match their inflected
18
+ // forms too, since a bare-word boundary can't catch one word inside another
19
+ // and each inflection is a real evasion path.
20
+ //
21
+ // The list-marker branch anchors to actual list context (string start,
22
+ // after a newline, or after a sentence-ending mark) rather than any
23
+ // mid-sentence digit-punctuation-space. Its trailing boundary rejects a
24
+ // following digit rather than requiring whitespace, so "1.Send the request"
25
+ // still opens a list item while "12.5" reads as a decimal fraction. The
26
+ // marker is an unbounded digit run or a single letter, so `a)`/`b)` count
27
+ // alongside `1.`/`2)`.
28
+ const SEQUENCE_MARKER_PATTERN = /\b(?:then|before|after|subsequent(?:ly)?|next|finally|afterward(?:s)?)\b|(?<=^\s*|\n\s*|[.!?]\s*)(?:\d+|[a-z])[.)](?!\d)/gi;
29
+ /**
30
+ * `probeStepBound: null` skips the audit (the codebase's null-means-absent
31
+ * convention); a declared `0` is legal and strict, permitting no marker.
32
+ *
33
+ * Checked per direction, never summed across the brief, so an unrelated
34
+ * direction's own count never tightens an already-declared bound.
35
+ *
36
+ * Scoped to `brief.directions[].text` only, leaving `behaviors`,
37
+ * `scopedResources`, and `safetyLimits` unscanned: AD-16 runs this audit
38
+ * after generation over what `seal` generated, and AD-38 names AD-3's
39
+ * generated direction as that output. The other three are carried through
40
+ * from the contract.
41
+ */
42
+ export function auditBriefScripting(brief) {
43
+ if (brief.probeStepBound === null)
44
+ return;
45
+ for (const direction of brief.directions) {
46
+ const count = direction.text.match(SEQUENCE_MARKER_PATTERN)?.length ?? 0;
47
+ if (count > brief.probeStepBound) {
48
+ throw new StructuralFailure(AD5_CODE, `SealedEvaluatorBrief.directions[oracleId=${direction.oracleId}].text`, `${count} enumerated-probe-step marker(s) exceed the declared bound of ${brief.probeStepBound}`);
49
+ }
50
+ }
51
+ }
@@ -0,0 +1,18 @@
1
+ import type { EvalContract } from '../schemas/eval-contract.ts';
2
+ import { SealedEvaluatorBrief } from '../schemas/sealed-evaluator-brief.ts';
3
+ export declare function seal(contract: EvalContract): SealedEvaluatorBrief;
4
+ /**
5
+ * The runtime backstop behind AD-16's exclusion guarantee. TypeScript's
6
+ * compile-time excess-property check protects only `seal()`'s current
7
+ * return literal; a future non-literal construction path (an `as` cast, a
8
+ * spread, an incremental build) would otherwise have no backstop. Reads only
9
+ * the `safeParse` result shape, so `core/schemas` stays the sole Zod
10
+ * boundary and a rejection becomes a `TypeError` (this file's
11
+ * precondition-violation convention) with no `ZodError` import to test
12
+ * against.
13
+ *
14
+ * Scoped to `seal()`, the one function in `core/` minting a package-boundary
15
+ * artifact today. Exported so a regression test can drive the rejection path
16
+ * directly.
17
+ */
18
+ export declare function validateAssembledBrief(brief: unknown): SealedEvaluatorBrief;
@@ -0,0 +1,155 @@
1
+ /**
2
+ * `seal`: walks a compiled `EvalContract`'s oracles to assemble the
3
+ * `SealedEvaluatorBrief` AD-16 describes, making its isolation boundary
4
+ * structural rather than conventional. Pure per AD-1/AD-2: no filesystem,
5
+ * network, clock, randomness, model call, or evaluator execution. Reuses
6
+ * `buildPlanIndex`/`renderDirectionText` for the generated prose and
7
+ * `digestArtifact` for `contractDigest`; never reimplements rendering or
8
+ * hashing locally. Before returning, `validateAssembledBrief` gives AD-16's
9
+ * exclusion guarantee a runtime backstop; see that function for why.
10
+ */
11
+ import { digestArtifact } from '../canonical/digest.js';
12
+ import { freezeArtifact } from '../lineage/freeze.js';
13
+ import { SealedEvaluatorBrief } from '../schemas/sealed-evaluator-brief.js';
14
+ import { renderDirectionText } from './direction-prose.js';
15
+ import { buildPlanIndex } from './plan-index.js';
16
+ // `seal` digests the contract whole, so one stable label serves every call
17
+ // regardless of which contract is being sealed.
18
+ const CONTRACT_ARTIFACT_PATH = 'EvalContract';
19
+ /**
20
+ * Sorts by a natural identifying string key, throwing `TypeError` on a
21
+ * duplicate key rather than emitting an order-dependent result. The schema
22
+ * enforces no uniqueness on `oracle.id`, `permittedInterface.logicalId`, or
23
+ * `scopedResource.reference`, so a duplicate is a real possibility and
24
+ * sort-stability alone would otherwise silently break byte-identity under
25
+ * contract-step reordering. This is the same precondition-violation
26
+ * convention `plan-index.ts`'s duplicate-`stepId`/`operationId` guard uses.
27
+ */
28
+ function sortedByKey(items, keyOf, keyLabel) {
29
+ const seen = new Set();
30
+ for (const item of items) {
31
+ const key = keyOf(item);
32
+ if (seen.has(key)) {
33
+ throw new TypeError(`duplicate ${keyLabel} in sealed brief input: ${key}`);
34
+ }
35
+ seen.add(key);
36
+ }
37
+ return [...items].sort((a, b) => {
38
+ const keyA = keyOf(a);
39
+ const keyB = keyOf(b);
40
+ return keyA < keyB ? -1 : keyA > keyB ? 1 : 0;
41
+ });
42
+ }
43
+ export function seal(contract) {
44
+ const index = buildPlanIndex(contract.interactionPlan, contract.permittedInterfaces);
45
+ // A `null` direction reaching here is a precondition violation: a
46
+ // compiled contract's oracles must carry a non-null direction by the
47
+ // time `seal` reads them. Throw, never filter or skip.
48
+ const directions = contract.oracles.map((oracle) => {
49
+ if (oracle.direction === null) {
50
+ throw new TypeError(`oracle ${oracle.id} reached seal() with a null direction`);
51
+ }
52
+ return {
53
+ oracleId: oracle.id,
54
+ text: renderDirectionText(oracle.direction, index),
55
+ };
56
+ });
57
+ // A per-element projection, not a carry-through: `PermittedInterface` has
58
+ // an `operations` field `BriefInterface` does not, so shipping it here
59
+ // would hand the evaluator the action inventory AD-39 keeps from it.
60
+ const permittedInterfaces = contract.permittedInterfaces.map((iface) => ({
61
+ logicalId: iface.logicalId,
62
+ kind: iface.kind,
63
+ }));
64
+ // `null` on the contract means "no scoped resources declared"; `seal`
65
+ // always resolves an answer, so it carries through as `[]`, matching the
66
+ // brief field's non-nullable schema.
67
+ const scopedResources = contract.scopedResources ?? [];
68
+ // Explicitly typed, not just inferred, so TypeScript's excess-property
69
+ // check still runs against this literal: assigning an object literal to
70
+ // an explicitly-typed binding triggers the same check as returning it
71
+ // from a typed function.
72
+ const brief = {
73
+ // `seal` owns these two fields per AD-24/AD-29: this call mints a
74
+ // fresh artifact, never a revision of a prior brief. `seal` is pure
75
+ // and stateless with no "prior brief" argument (AD-12), so the only
76
+ // honest artifact is a lineage root: `parentDigest` null,
77
+ // `revisionCount` 0, independent of the contract's own lineage.
78
+ // `schemaVersion` is the brief schema's current version.
79
+ schemaVersion: 1,
80
+ parentDigest: null,
81
+ revisionCount: 0,
82
+ // A plain digest of the literal input: two differently-ordered
83
+ // contracts necessarily digest differently, by construction. This
84
+ // field sits outside the byte-identical-under-reordering guarantee the
85
+ // rest of the brief carries, since it tracks the literal contract that
86
+ // was sealed.
87
+ contractDigest: digestArtifact(contract, CONTRACT_ARTIFACT_PATH),
88
+ // Carried through in contract order, unsorted: its own schema doc
89
+ // calls it "carried through unchanged," unlike the arrays below.
90
+ // Copied rather than aliased, like every field here, since the brief
91
+ // must not keep sharing structure with the input, or a caller
92
+ // mutating `contract.behaviors` after `seal()` returns would
93
+ // silently mutate the "sealed" brief too.
94
+ behaviors: [...contract.behaviors],
95
+ directions: sortedByKey(directions, (d) => d.oracleId, 'oracleId'),
96
+ permittedInterfaces: sortedByKey(permittedInterfaces, (i) => i.logicalId, 'permittedInterfaces logicalId'),
97
+ scopedResources: sortedByKey(scopedResources, (r) => r.reference, 'scopedResources reference'),
98
+ // Copied rather than aliased, for the same reason as `behaviors` above.
99
+ budgets: { ...contract.budgets },
100
+ // Sorted lexicographically; sort-key duplicates are impossible here
101
+ // since equal strings are interchangeable, so no duplicate guard.
102
+ safetyLimits: [...contract.safetyLimits].sort(),
103
+ probeStepBound: contract.probeStepBound,
104
+ };
105
+ // The parse returns Zod's own deep clone, so the freeze lands on the
106
+ // brief the caller receives and leaves the contract it was built from alone.
107
+ return freezeArtifact(validateAssembledBrief(brief));
108
+ }
109
+ /**
110
+ * The runtime backstop behind AD-16's exclusion guarantee. TypeScript's
111
+ * compile-time excess-property check protects only `seal()`'s current
112
+ * return literal; a future non-literal construction path (an `as` cast, a
113
+ * spread, an incremental build) would otherwise have no backstop. Reads only
114
+ * the `safeParse` result shape, so `core/schemas` stays the sole Zod
115
+ * boundary and a rejection becomes a `TypeError` (this file's
116
+ * precondition-violation convention) with no `ZodError` import to test
117
+ * against.
118
+ *
119
+ * Scoped to `seal()`, the one function in `core/` minting a package-boundary
120
+ * artifact today. Exported so a regression test can drive the rejection path
121
+ * directly.
122
+ */
123
+ export function validateAssembledBrief(brief) {
124
+ const result = SealedEvaluatorBrief.safeParse(brief);
125
+ if (result.success)
126
+ return result.data;
127
+ const issueCount = result.error.issues.length;
128
+ const firstIssue = result.error.issues[0];
129
+ const firstPath = firstIssue ? dotPath(firstIssue.path) : '';
130
+ throw new TypeError(`seal() assembled a brief that failed SealedEvaluatorBrief validation: ${issueCount} issue${issueCount === 1 ? '' : 's'}, first at "${firstPath === '' ? '(root)' : firstPath}"`, { cause: result.error });
131
+ }
132
+ /**
133
+ * Formats a Zod issue path for the message above, mirroring zod's own
134
+ * `core.toDotPath` branch by branch without importing it: a word-shaped
135
+ * segment joins with `.`; a numeric, symbol, or non-identifier segment is
136
+ * bracketed. The mirror holds even at the edges, so a segment that is
137
+ * word-shaped but starts with a digit stays unbracketed here exactly as zod
138
+ * leaves it.
139
+ */
140
+ function dotPath(path) {
141
+ const segments = [];
142
+ for (const segment of path) {
143
+ if (typeof segment === 'number')
144
+ segments.push(`[${segment}]`);
145
+ else if (typeof segment === 'symbol' || /[^\w$]/.test(segment)) {
146
+ segments.push(`[${JSON.stringify(String(segment))}]`);
147
+ }
148
+ else {
149
+ if (segments.length > 0)
150
+ segments.push('.');
151
+ segments.push(segment);
152
+ }
153
+ }
154
+ return segments.join('');
155
+ }
@@ -0,0 +1,30 @@
1
+ /**
2
+ * AD-34's stage-shape vocabulary. Two conformance types pin the exact shapes
3
+ * of `compile` and `seal`; two generic types record the conditional
4
+ * plan/reduce pair, which `preflight` implements.
5
+ *
6
+ * AD-24's six-stage input/output/owner/lineage table lives in
7
+ * `core/lineage/stage-table.ts`. This file holds the stage *shapes* TypeScript
8
+ * checks an implementation against; that one holds the *table* AD-24 fixes.
9
+ * Neither imports the other.
10
+ */
11
+ import type { EvalContract } from './schemas/eval-contract.ts';
12
+ import type { SealedEvaluatorBrief } from './schemas/sealed-evaluator-brief.ts';
13
+ /** The core compile stage's one runtime option. Core behavior never depends on an implicit configuration source (AD-1), so this is required; `application/compile.ts` is the only caller that supplies a default. */
14
+ export type CompileOptions = {
15
+ readonly strict: boolean;
16
+ };
17
+ /** `core/compile/compile.ts`'s exact conformance shape. */
18
+ export type CompileStage = (contract: EvalContract, options: CompileOptions) => EvalContract;
19
+ /** `core/seal/seal.ts`'s exact conformance shape. */
20
+ export type SealStage = (contract: EvalContract) => SealedEvaluatorBrief;
21
+ /**
22
+ * A stage's pure planning half: artifacts in, a request description out.
23
+ * AD-34 makes this pair conditional on a stage needing external observation.
24
+ * Compile and seal need none, so neither implements this; pre-flight does, and
25
+ * `core/preflight/plan.ts` and `core/preflight/reduce.ts` are the pair that
26
+ * satisfies these two types.
27
+ */
28
+ export type PlanStage<InputArtifact, RequestDescription> = (input: InputArtifact) => RequestDescription;
29
+ /** A stage's pure reducing half: the plan plus the observations a port produced, reduced to the next artifact. */
30
+ export type ReduceStage<RequestDescription, Observation, OutputArtifact> = (plan: RequestDescription, observations: Observation) => OutputArtifact;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,15 @@
1
+ export * from './application/index.ts';
2
+ export type { ArtifactReference } from './core/schemas/artifact-reference.ts';
3
+ export type { EvalContract } from './core/schemas/eval-contract.ts';
4
+ export type { EvaluatorConfiguration } from './core/schemas/evaluator-configuration.ts';
5
+ export type { EvidenceArtifact } from './core/schemas/evidence-artifact.ts';
6
+ export type { IsolationManifest } from './core/schemas/isolation-manifest.ts';
7
+ export type { PreflightCheck, PreflightVerdict, } from './core/schemas/preflight-verdict.ts';
8
+ export type { PrivateArtifactManifest } from './core/schemas/private-artifact-manifest.ts';
9
+ export type { Probe } from './core/schemas/probe.ts';
10
+ export type { Rubric } from './core/schemas/rubric.ts';
11
+ export type { ScoringPolicy } from './core/schemas/scoring-policy.ts';
12
+ export type { SealedEvaluatorBrief } from './core/schemas/sealed-evaluator-brief.ts';
13
+ export type { SealedRunRecord } from './core/schemas/sealed-run-record.ts';
14
+ export type { FixtureReset, ManifestationWitness, SensitivityWitness, SensitivityWitnessLeg, WitnessChannel, WitnessInputs, } from './core/schemas/sensitivity-witness.ts';
15
+ export declare const VERSION = "0.1.0";
package/dist/index.js ADDED
@@ -0,0 +1,22 @@
1
+ // eval-quality: compile disciplined eval contracts and score known-defect detection.
2
+ //
3
+ // Barrel export. Modules land here along the artifact pipeline:
4
+ // compile: validate Behavioral Evaluation Contracts and discipline coverage
5
+ // seal: emit deterministic evaluator briefs
6
+ // ingest: validate caller-produced run records and isolation manifests
7
+ // preflight: compile the contract's witnesses into probe requests, then
8
+ // reduce the observations the environment-probe port returned
9
+ // score: derive per-oracle outcomes and contract-strength vectors
10
+ // emit: produce versioned evidence artifacts
11
+ //
12
+ // The package executes nothing: it never runs an agent, judge, or system under test.
13
+ //
14
+ // The dependency matrix grants this file two edges and no others: `root ->
15
+ // application` and `root -> core-schemas`. So the layer barrel
16
+ // `application/index.ts` is how everything else under `core/` is reached,
17
+ // while the artifact types below come straight off `core/schemas` on the
18
+ // second edge. The port vocabulary stays at the `eval-quality/conformance`
19
+ // subpath, where AD-37 puts the conformance definition an adapter author
20
+ // reads; the reference adapters stay at `eval-quality/adapters`.
21
+ export * from './application/index.js';
22
+ export const VERSION = '0.1.0';
@@ -0,0 +1,13 @@
1
+ /** AD-1 forbids a clock read under `core/`, so a timestamp arrives through this port. */
2
+ import { ClockReadRequest, ClockReadResponse } from '../core/schemas/port-messages.ts';
3
+ import type { PortMethod } from './port.ts';
4
+ export type ClockPort = {
5
+ readonly read: PortMethod<ClockReadRequest, ClockReadResponse>;
6
+ };
7
+ /** the boundary parsers `application/` and the conformance suite validate with. */
8
+ export declare const clockReadParsers: {
9
+ readonly request: import("zod").ZodObject<{}, import("zod/v4/core").$strict>;
10
+ readonly response: import("zod").ZodObject<{
11
+ now: import("zod").ZodISODateTime;
12
+ }, import("zod/v4/core").$strict>;
13
+ };
@@ -0,0 +1,7 @@
1
+ /** AD-1 forbids a clock read under `core/`, so a timestamp arrives through this port. */
2
+ import { ClockReadRequest, ClockReadResponse, } from '../core/schemas/port-messages.js';
3
+ /** the boundary parsers `application/` and the conformance suite validate with. */
4
+ export const clockReadParsers = {
5
+ request: ClockReadRequest,
6
+ response: ClockReadResponse,
7
+ };
@@ -0,0 +1,16 @@
1
+ /** AD-8's corpus-provider port: an opaque reference in, resolved bytes out. */
2
+ import { CorpusResolveRequest, CorpusResolveResponse } from '../core/schemas/port-messages.ts';
3
+ import type { PortMethod } from './port.ts';
4
+ export type CorpusPort = {
5
+ readonly resolve: PortMethod<CorpusResolveRequest, CorpusResolveResponse>;
6
+ };
7
+ /** the boundary parsers `application/` and the conformance suite validate with. */
8
+ export declare const corpusResolveParsers: {
9
+ readonly request: import("zod").ZodObject<{
10
+ privateRef: import("zod").ZodString;
11
+ }, import("zod/v4/core").$strict>;
12
+ readonly response: import("zod").ZodObject<{
13
+ privateRef: import("zod").ZodString;
14
+ bytes: import("zod").ZodCustom<Uint8Array<ArrayBuffer>, Uint8Array<ArrayBuffer>>;
15
+ }, import("zod/v4/core").$strict>;
16
+ };
@@ -0,0 +1,7 @@
1
+ /** AD-8's corpus-provider port: an opaque reference in, resolved bytes out. */
2
+ import { CorpusResolveRequest, CorpusResolveResponse, } from '../core/schemas/port-messages.js';
3
+ /** the boundary parsers `application/` and the conformance suite validate with. */
4
+ export const corpusResolveParsers = {
5
+ request: CorpusResolveRequest,
6
+ response: CorpusResolveResponse,
7
+ };
@@ -0,0 +1,76 @@
1
+ /**
2
+ * AD-35's environment-probe port: a logical interface identifier in, one
3
+ * observation out. The request never names a URL, host, or port. Mapping the
4
+ * identifier to an authorized target is the adapter's job, from configuration
5
+ * outside the contract.
6
+ *
7
+ * Four rules an implementation MUST follow. Prose here because `ports/` holds
8
+ * no logic; `src/testing/probe-conformance.ts` is the executable half.
9
+ *
10
+ * 1. Apply the policy before any network call, and again to every redirect
11
+ * target.
12
+ * 2. Issue the request against the address the policy validated
13
+ * (`canonicalAddress`), keep the original host in the `Host` header, and
14
+ * verify TLS against that host. Never re-resolve a hostname after
15
+ * validation: re-resolving between the check and the connection is the
16
+ * classic way past an allowlist like this one. Where a host resolves to
17
+ * several addresses, validate each and connect only to a validated one.
18
+ * 3. A policy denial throws `forbidden-target`; a cap throws
19
+ * `budget-exhausted`; an abort throws `aborted`; a transport failure throws
20
+ * `port-failure`.
21
+ * 4. Every response the server returns is an observation, at any status. A 4xx
22
+ * or 5xx resolves to a schema-valid `ProbeObservation`, since AD-10's
23
+ * "every declared seeded fault being observed to fire" reads the status as
24
+ * payload. Throw on a non-2xx and a seeded fault goes invisible, which
25
+ * makes the whole pre-flight vacuous.
26
+ */
27
+ import { ProbeObservation, ProbeRequest } from '../core/schemas/port-messages.ts';
28
+ import type { PortMethod } from './port.ts';
29
+ export type EnvironmentProbePort = {
30
+ readonly probe: PortMethod<ProbeRequest, ProbeObservation>;
31
+ };
32
+ /** the boundary parsers `application/` and the conformance suite validate with. */
33
+ export declare const probeParsers: {
34
+ readonly request: import("zod").ZodObject<{
35
+ probeId: import("zod").ZodString;
36
+ interfaceId: import("zod").ZodString;
37
+ operationId: import("zod").ZodString;
38
+ method: import("zod").ZodEnum<{
39
+ DELETE: "DELETE";
40
+ GET: "GET";
41
+ HEAD: "HEAD";
42
+ OPTIONS: "OPTIONS";
43
+ PATCH: "PATCH";
44
+ POST: "POST";
45
+ PUT: "PUT";
46
+ }>;
47
+ pathTemplate: import("zod").ZodString;
48
+ channels: import("zod").ZodObject<{
49
+ path: import("zod").ZodRecord<import("zod").ZodString, import("zod").ZodType<import("../core/schemas/primitives.ts").JsonValue, unknown, import("zod/v4/core").$ZodTypeInternals<import("../core/schemas/primitives.ts").JsonValue, unknown>>>;
50
+ query: import("zod").ZodRecord<import("zod").ZodString, import("zod").ZodType<import("../core/schemas/primitives.ts").JsonValue, unknown, import("zod/v4/core").$ZodTypeInternals<import("../core/schemas/primitives.ts").JsonValue, unknown>>>;
51
+ header: import("zod").ZodRecord<import("zod").ZodString, import("zod").ZodString>;
52
+ body: import("zod").ZodDiscriminatedUnion<[import("zod").ZodObject<{
53
+ kind: import("zod").ZodLiteral<"json">;
54
+ value: import("zod").ZodType<import("../core/schemas/primitives.ts").JsonValue, unknown, import("zod/v4/core").$ZodTypeInternals<import("../core/schemas/primitives.ts").JsonValue, unknown>>;
55
+ }, import("zod/v4/core").$strict>, import("zod").ZodObject<{
56
+ kind: import("zod").ZodLiteral<"absent">;
57
+ }, import("zod/v4/core").$strict>], "kind">;
58
+ }, import("zod/v4/core").$strict>;
59
+ }, import("zod/v4/core").$strict>;
60
+ readonly response: import("zod").ZodObject<{
61
+ probeId: import("zod").ZodString;
62
+ interfaceId: import("zod").ZodString;
63
+ operationId: import("zod").ZodString;
64
+ status: import("zod").ZodInt;
65
+ headers: import("zod").ZodRecord<import("zod").ZodString, import("zod").ZodString>;
66
+ body: import("zod").ZodDiscriminatedUnion<[import("zod").ZodObject<{
67
+ kind: import("zod").ZodLiteral<"json">;
68
+ value: import("zod").ZodType<import("../core/schemas/primitives.ts").JsonValue, unknown, import("zod/v4/core").$ZodTypeInternals<import("../core/schemas/primitives.ts").JsonValue, unknown>>;
69
+ }, import("zod/v4/core").$strict>, import("zod").ZodObject<{
70
+ kind: import("zod").ZodLiteral<"text">;
71
+ value: import("zod").ZodString;
72
+ }, import("zod/v4/core").$strict>, import("zod").ZodObject<{
73
+ kind: import("zod").ZodLiteral<"absent">;
74
+ }, import("zod/v4/core").$strict>], "kind">;
75
+ }, import("zod/v4/core").$strict>;
76
+ };
@@ -0,0 +1,32 @@
1
+ /**
2
+ * AD-35's environment-probe port: a logical interface identifier in, one
3
+ * observation out. The request never names a URL, host, or port. Mapping the
4
+ * identifier to an authorized target is the adapter's job, from configuration
5
+ * outside the contract.
6
+ *
7
+ * Four rules an implementation MUST follow. Prose here because `ports/` holds
8
+ * no logic; `src/testing/probe-conformance.ts` is the executable half.
9
+ *
10
+ * 1. Apply the policy before any network call, and again to every redirect
11
+ * target.
12
+ * 2. Issue the request against the address the policy validated
13
+ * (`canonicalAddress`), keep the original host in the `Host` header, and
14
+ * verify TLS against that host. Never re-resolve a hostname after
15
+ * validation: re-resolving between the check and the connection is the
16
+ * classic way past an allowlist like this one. Where a host resolves to
17
+ * several addresses, validate each and connect only to a validated one.
18
+ * 3. A policy denial throws `forbidden-target`; a cap throws
19
+ * `budget-exhausted`; an abort throws `aborted`; a transport failure throws
20
+ * `port-failure`.
21
+ * 4. Every response the server returns is an observation, at any status. A 4xx
22
+ * or 5xx resolves to a schema-valid `ProbeObservation`, since AD-10's
23
+ * "every declared seeded fault being observed to fire" reads the status as
24
+ * payload. Throw on a non-2xx and a seeded fault goes invisible, which
25
+ * makes the whole pre-flight vacuous.
26
+ */
27
+ import { ProbeObservation, ProbeRequest, } from '../core/schemas/port-messages.js';
28
+ /** the boundary parsers `application/` and the conformance suite validate with. */
29
+ export const probeParsers = {
30
+ request: ProbeRequest,
31
+ response: ProbeObservation,
32
+ };