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.
Files changed (179) hide show
  1. package/README.md +142 -67
  2. package/corpus/dev/README.md +20 -13
  3. package/corpus/dev/compile-seal-example/brief.json +1 -1
  4. package/corpus/dev/compile-seal-example/contract.json +1 -1
  5. package/corpus/dev/contracts/absent-collection-locations.json +1 -1
  6. package/corpus/dev/contracts/absent-sibling-groups.json +1 -1
  7. package/corpus/dev/contracts/absent-success-indicator.json +1 -1
  8. package/corpus/dev/contracts/empty-channel-roles.json +1 -1
  9. package/corpus/dev/contracts/empty-collection-locations.json +1 -1
  10. package/corpus/dev/contracts/empty-request-shapes.json +1 -1
  11. package/corpus/dev/contracts/empty-sibling-groups.json +1 -1
  12. package/corpus/dev/contracts/fragment-selection.json +1 -0
  13. package/corpus/dev/contracts/no-collection-quantifier.json +1 -1
  14. package/corpus/dev/contracts/no-operation-inventory.json +1 -1
  15. package/corpus/dev/contracts/no-read-back-relation.json +1 -1
  16. package/corpus/dev/contracts/no-state-change-marker.json +1 -1
  17. package/corpus/dev/contracts/no-type-violating-step.json +1 -1
  18. package/corpus/dev/contracts/per-key-split-oracles.json +1 -1
  19. package/corpus/dev/contracts/review-corpus.json +1 -0
  20. package/corpus/dev/contracts/satisfied-declarations.json +1 -1
  21. package/corpus/dev/contracts/single-required-response-key.json +1 -1
  22. package/corpus/dev/contracts/split-indicator-oracle.json +1 -1
  23. package/corpus/dev/contracts/unaddressed-parameter-sibling.json +1 -1
  24. package/corpus/dev/contracts/unnamed-reference-set.json +1 -1
  25. package/corpus/dev/contracts/wrong-cardinality-form.json +1 -1
  26. package/corpus/dev/index.json +1 -1
  27. package/dist/application/index.d.ts +2 -0
  28. package/dist/application/index.js +1 -0
  29. package/dist/application/score.d.ts +48 -0
  30. package/dist/application/score.js +186 -0
  31. package/dist/cli/arguments.d.ts +7 -3
  32. package/dist/cli/arguments.js +73 -5
  33. package/dist/cli/exit-codes.d.ts +6 -2
  34. package/dist/cli/exit-codes.js +8 -9
  35. package/dist/cli/main.js +9 -0
  36. package/dist/cli/render.d.ts +3 -6
  37. package/dist/cli/render.js +57 -5
  38. package/dist/cli/run.d.ts +13 -3
  39. package/dist/cli/run.js +136 -16
  40. package/dist/core/canonical/canonicalize.d.ts +12 -0
  41. package/dist/core/canonical/canonicalize.js +18 -4
  42. package/dist/core/compile/bindings.d.ts +89 -0
  43. package/dist/core/compile/bindings.js +334 -0
  44. package/dist/core/compile/compile.d.ts +17 -0
  45. package/dist/core/compile/compile.js +37 -1
  46. package/dist/core/compile/excluded-content.d.ts +11 -0
  47. package/dist/core/compile/excluded-content.js +42 -0
  48. package/dist/core/compile/expression-legality.d.ts +49 -0
  49. package/dist/core/compile/expression-legality.js +158 -38
  50. package/dist/core/compile/forbidden-inputs.d.ts +8 -1
  51. package/dist/core/compile/forbidden-inputs.js +16 -3
  52. package/dist/core/compile/interface-inventory.d.ts +57 -2
  53. package/dist/core/compile/interface-inventory.js +119 -13
  54. package/dist/core/compile/reachability.d.ts +28 -1
  55. package/dist/core/compile/reachability.js +167 -35
  56. package/dist/core/compile/schema-version.d.ts +2 -0
  57. package/dist/core/compile/schema-version.js +25 -0
  58. package/dist/core/compile/sensitivity-witness.d.ts +31 -12
  59. package/dist/core/compile/sensitivity-witness.js +110 -23
  60. package/dist/core/compile/step-reference.d.ts +2 -0
  61. package/dist/core/compile/step-reference.js +49 -0
  62. package/dist/core/coverage/operations.d.ts +62 -0
  63. package/dist/core/coverage/operations.js +57 -0
  64. package/dist/core/coverage/relevance.d.ts +4 -2
  65. package/dist/core/coverage/relevance.js +22 -23
  66. package/dist/core/coverage/satisfaction.d.ts +2 -2
  67. package/dist/core/coverage/satisfaction.js +73 -48
  68. package/dist/core/declared-inputs.d.ts +83 -4
  69. package/dist/core/declared-inputs.js +105 -8
  70. package/dist/core/emit/emit.d.ts +9 -0
  71. package/dist/core/emit/emit.js +214 -0
  72. package/dist/core/emit/private-artifact-digest.d.ts +11 -0
  73. package/dist/core/emit/private-artifact-digest.js +31 -0
  74. package/dist/core/evaluate/evidence-resolution.d.ts +10 -3
  75. package/dist/core/evaluate/evidence-resolution.js +74 -11
  76. package/dist/core/evaluate/operators.d.ts +22 -0
  77. package/dist/core/evaluate/operators.js +17 -1
  78. package/dist/core/evaluate/resolution.d.ts +14 -3
  79. package/dist/core/evaluate/resolution.js +63 -3
  80. package/dist/core/excluded-content.d.ts +65 -0
  81. package/dist/core/excluded-content.js +113 -0
  82. package/dist/core/failure-codes.d.ts +2 -2
  83. package/dist/core/failure-codes.js +7 -2
  84. package/dist/core/ingest/conditions.d.ts +201 -0
  85. package/dist/core/ingest/conditions.js +48 -0
  86. package/dist/core/ingest/index.d.ts +13 -0
  87. package/dist/core/ingest/index.js +11 -0
  88. package/dist/core/ingest/ingest.d.ts +68 -0
  89. package/dist/core/ingest/ingest.js +318 -0
  90. package/dist/core/lineage/stage-table.d.ts +21 -4
  91. package/dist/core/lineage/stage-table.js +35 -3
  92. package/dist/core/preflight/plan.d.ts +7 -5
  93. package/dist/core/preflight/plan.js +75 -32
  94. package/dist/core/preflight/projection.d.ts +6 -3
  95. package/dist/core/preflight/projection.js +22 -2
  96. package/dist/core/preflight/reduce.js +47 -7
  97. package/dist/core/preflight/witness-evidence.d.ts +5 -5
  98. package/dist/core/preflight/witness-evidence.js +73 -20
  99. package/dist/core/schemas/artifact.d.ts +1049 -30
  100. package/dist/core/schemas/constraint-ledger.js +51 -0
  101. package/dist/core/schemas/defect-signature.d.ts +695 -0
  102. package/dist/core/schemas/defect-signature.js +175 -0
  103. package/dist/core/schemas/eval-contract.d.ts +550 -12
  104. package/dist/core/schemas/eval-contract.js +32 -2
  105. package/dist/core/schemas/evidence-artifact.d.ts +109 -19
  106. package/dist/core/schemas/evidence-artifact.js +64 -8
  107. package/dist/core/schemas/interface.d.ts +741 -13
  108. package/dist/core/schemas/interface.js +120 -7
  109. package/dist/core/schemas/isolation-manifest.js +16 -9
  110. package/dist/core/schemas/plan.d.ts +329 -3
  111. package/dist/core/schemas/plan.js +89 -3
  112. package/dist/core/schemas/pointer.d.ts +47 -1
  113. package/dist/core/schemas/pointer.js +89 -8
  114. package/dist/core/schemas/port-messages.d.ts +194 -10
  115. package/dist/core/schemas/port-messages.js +73 -6
  116. package/dist/core/schemas/probe-body.d.ts +18 -0
  117. package/dist/core/schemas/probe-body.js +13 -0
  118. package/dist/core/schemas/probe-qualification.d.ts +168 -0
  119. package/dist/core/schemas/probe-qualification.js +106 -0
  120. package/dist/core/schemas/probe.d.ts +431 -0
  121. package/dist/core/schemas/probe.js +6 -2
  122. package/dist/core/schemas/scoring-policy.d.ts +1 -0
  123. package/dist/core/schemas/scoring-policy.js +6 -1
  124. package/dist/core/schemas/sealed-evaluator-brief.d.ts +1 -0
  125. package/dist/core/schemas/sealed-evaluator-brief.js +4 -1
  126. package/dist/core/schemas/sealed-run-record.d.ts +161 -12
  127. package/dist/core/schemas/sealed-run-record.js +92 -15
  128. package/dist/core/schemas/sensitivity-witness.d.ts +108 -6
  129. package/dist/core/schemas/sensitivity-witness.js +61 -5
  130. package/dist/core/score/binding-order.d.ts +31 -0
  131. package/dist/core/score/binding-order.js +88 -0
  132. package/dist/core/score/bindings.d.ts +93 -0
  133. package/dist/core/score/bindings.js +309 -0
  134. package/dist/core/score/ladder-table.d.ts +23 -0
  135. package/dist/core/score/ladder-table.js +109 -0
  136. package/dist/core/score/ladder.d.ts +229 -0
  137. package/dist/core/score/ladder.js +464 -0
  138. package/dist/core/score/mode-agreement.d.ts +30 -0
  139. package/dist/core/score/mode-agreement.js +16 -0
  140. package/dist/core/score/outcome-table.d.ts +17 -0
  141. package/dist/core/score/outcome-table.js +172 -0
  142. package/dist/core/score/outcome.d.ts +466 -0
  143. package/dist/core/score/outcome.js +562 -0
  144. package/dist/core/score/qualification.d.ts +83 -0
  145. package/dist/core/score/qualification.js +645 -0
  146. package/dist/core/score/quotation.d.ts +56 -0
  147. package/dist/core/score/quotation.js +226 -0
  148. package/dist/core/score/reduce-trials.d.ts +80 -0
  149. package/dist/core/score/reduce-trials.js +90 -0
  150. package/dist/core/score/score.d.ts +72 -0
  151. package/dist/core/score/score.js +607 -0
  152. package/dist/core/score/selection.d.ts +80 -0
  153. package/dist/core/score/selection.js +98 -0
  154. package/dist/core/score/strength.d.ts +58 -0
  155. package/dist/core/score/strength.js +227 -0
  156. package/dist/core/score/witness.d.ts +138 -0
  157. package/dist/core/score/witness.js +320 -0
  158. package/dist/core/seal/derived-reference.d.ts +2 -9
  159. package/dist/core/seal/derived-reference.js +293 -46
  160. package/dist/core/seal/plan-index.d.ts +18 -3
  161. package/dist/core/seal/plan-index.js +44 -8
  162. package/dist/core/seal/seal.js +8 -3
  163. package/dist/core/stage-contracts.d.ts +62 -2
  164. package/dist/index.d.ts +1 -1
  165. package/dist/index.js +1 -1
  166. package/dist/ports/environment-probe-port.d.ts +60 -4
  167. package/dist/testing/index.d.ts +2 -0
  168. package/dist/testing/index.js +1 -0
  169. package/dist/testing/probe-conformance.d.ts +29 -0
  170. package/dist/testing/probe-conformance.js +34 -5
  171. package/package.json +12 -3
  172. package/schemas/eval-contract.schema.json +1787 -797
  173. package/schemas/evidence-artifact.schema.json +126 -9
  174. package/schemas/isolation-manifest.schema.json +17 -10
  175. package/schemas/probe.schema.json +636 -5
  176. package/schemas/rubric.schema.json +1 -1
  177. package/schemas/scoring-policy.schema.json +8 -1
  178. package/schemas/sealed-evaluator-brief.schema.json +11 -1
  179. package/schemas/sealed-run-record.schema.json +314 -40
@@ -161,8 +161,8 @@
161
161
  "type": "array",
162
162
  "items": {
163
163
  "type": "string",
164
- "pattern": "^\\/interactions\\/[a-z0-9]+(?:-[a-z0-9]+)*\\/(?:(?:response-body|response-headers|stdout|stderr)(?:\\/(?:[^/~]|~[01])*)*|(?:response-status|exit-code)|call-inputs\\/(?:path|query|header|body)(?:\\/(?:[^/~]|~[01])*)*)$",
165
- "description": "AD-26 interaction-rooted pointer: \"/interactions/{stepId}/\" followed by one channel of the closed vocabulary. `call-inputs` takes one of the four transport channels as its next segment; `response-status` and `exit-code` take no tail. Syntax only: whether the step exists and whether the evidence is reachable are compile-time checks, not schema checks."
164
+ "pattern": "^\\/interactions\\/[a-z0-9]+(?:-[a-z0-9]+)*\\/(?:(?:response-body|response-headers|stdout|stderr)(?:\\/(?:[^/~]|~[01])*)*|(?:response-status|exit-code)|call-inputs\\/(?:path|query|header|body|argument|option|environment|stdin)(?:\\/(?:[^/~]|~[01])*)*|artifact\\/[a-z0-9]+(?:-[a-z0-9]+)*(?:\\/(?:[^/~]|~[01])*)*)$",
165
+ "description": "AD-26 interaction-rooted pointer: \"/interactions/{stepId}/\" followed by one channel of the closed vocabulary. `call-inputs` takes one input channel as its next segment, one of the four transport channels or one of the four command channels; `artifact` takes the identifier of a file the operation declares it writes; `response-status` and `exit-code` take no tail. Syntax only: whether the step exists and whether the evidence is reachable are compile-time checks, not schema checks."
166
166
  },
167
167
  "description": "Interaction-rooted pointers only. AD-3 computes containment after quantifier substitution, so a target is always fully rooted and the bound-element spelling never appears here."
168
168
  },
@@ -362,162 +362,414 @@
362
362
  "permittedInterfaces": {
363
363
  "type": "array",
364
364
  "items": {
365
- "type": "object",
366
- "properties": {
367
- "logicalId": {
368
- "type": "string",
369
- "pattern": "^[a-z0-9]+(?:-[a-z0-9]+)*$",
370
- "description": "AD-35: a logical identifier for the interface, never a URL, host, or port. Mapping it to a target is the caller's, outside the contract."
371
- },
372
- "kind": {
373
- "type": "string",
374
- "enum": [
375
- "api",
376
- "web",
377
- "cli",
378
- "mcp"
365
+ "oneOf": [
366
+ {
367
+ "type": "object",
368
+ "properties": {
369
+ "logicalId": {
370
+ "type": "string",
371
+ "pattern": "^[a-z0-9]+(?:-[a-z0-9]+)*$",
372
+ "description": "AD-35: a logical identifier for the interface, never a URL, host, or port. Mapping it to a target is the caller's, outside the contract."
373
+ },
374
+ "kind": {
375
+ "type": "string",
376
+ "const": "api"
377
+ },
378
+ "operations": {
379
+ "type": "array",
380
+ "items": {
381
+ "$ref": "#/$defs/Operation"
382
+ },
383
+ "description": "No uniqueness constraint: two operations colliding on their transport identity after parameter-name erasure is `duplicate-operation-signature`, a coded compile-time error, and a schema that deduped them would delete it."
384
+ }
385
+ },
386
+ "required": [
387
+ "logicalId",
388
+ "kind",
389
+ "operations"
379
390
  ],
380
- "description": "All four kinds are admitted so `unsupported-interface-kind` stays fireable. v0 supports `api`; the other three fail compilation under that code rather than failing to parse."
391
+ "additionalProperties": false
381
392
  },
382
- "operations": {
383
- "type": "array",
384
- "items": {
385
- "type": "object",
386
- "properties": {
387
- "operationId": {
388
- "type": "string",
389
- "pattern": "^[a-z0-9]+(?:-[a-z0-9]+)*$",
390
- "description": "A kebab-case slug. Excludes \"/\" and \"~\" so an identifier can be embedded in an interaction-rooted pointer without escaping."
391
- },
392
- "method": {
393
- "type": "string",
394
- "enum": [
395
- "GET",
396
- "HEAD",
397
- "POST",
398
- "PUT",
399
- "PATCH",
400
- "DELETE",
401
- "OPTIONS"
402
- ]
403
- },
404
- "pathTemplate": {
405
- "type": "string",
406
- "pattern": "^(?:\\/(?:[^/{}:]|\\{[A-Za-z0-9_-]+\\})*)+$",
407
- "description": "A path template whose parameters are spelled `{name}` in braces. The `:name` spelling is rejected: AD-40 resolves a defect signature by comparing method and path template, and that comparison is not implementable against an unstated syntax."
393
+ {
394
+ "type": "object",
395
+ "properties": {
396
+ "logicalId": {
397
+ "type": "string",
398
+ "pattern": "^[a-z0-9]+(?:-[a-z0-9]+)*$",
399
+ "description": "AD-35: a logical identifier for the interface, never a URL, host, or port. Mapping it to a target is the caller's, outside the contract."
400
+ },
401
+ "kind": {
402
+ "type": "string",
403
+ "const": "web"
404
+ },
405
+ "operations": {
406
+ "type": "array",
407
+ "items": {
408
+ "$ref": "#/$defs/Operation"
408
409
  },
409
- "stateChangeMarker": {
410
- "type": "boolean",
411
- "description": "AD-19: whether the operation is intended to change state. AD-20 rule 7 relevance reads it, and AD-10 selects the sensitivity channel by it. Both values are legal and neither is a default."
410
+ "description": "No uniqueness constraint: two operations colliding on their transport identity after parameter-name erasure is `duplicate-operation-signature`, a coded compile-time error, and a schema that deduped them would delete it."
411
+ }
412
+ },
413
+ "required": [
414
+ "logicalId",
415
+ "kind",
416
+ "operations"
417
+ ],
418
+ "additionalProperties": false
419
+ },
420
+ {
421
+ "type": "object",
422
+ "properties": {
423
+ "logicalId": {
424
+ "type": "string",
425
+ "pattern": "^[a-z0-9]+(?:-[a-z0-9]+)*$",
426
+ "description": "AD-35: a logical identifier for the interface, never a URL, host, or port. Mapping it to a target is the caller's, outside the contract."
427
+ },
428
+ "kind": {
429
+ "type": "string",
430
+ "const": "mcp"
431
+ },
432
+ "operations": {
433
+ "type": "array",
434
+ "items": {
435
+ "$ref": "#/$defs/Operation"
412
436
  },
413
- "requestShape": {
437
+ "description": "No uniqueness constraint: two operations colliding on their transport identity after parameter-name erasure is `duplicate-operation-signature`, a coded compile-time error, and a schema that deduped them would delete it."
438
+ }
439
+ },
440
+ "required": [
441
+ "logicalId",
442
+ "kind",
443
+ "operations"
444
+ ],
445
+ "additionalProperties": false
446
+ },
447
+ {
448
+ "type": "object",
449
+ "properties": {
450
+ "logicalId": {
451
+ "type": "string",
452
+ "pattern": "^[a-z0-9]+(?:-[a-z0-9]+)*$",
453
+ "description": "AD-35: a logical identifier for the interface, never a URL, host, or port. Mapping it to a target is the caller's, outside the contract."
454
+ },
455
+ "kind": {
456
+ "type": "string",
457
+ "const": "cli"
458
+ },
459
+ "operations": {
460
+ "type": "array",
461
+ "items": {
414
462
  "type": "object",
415
463
  "properties": {
416
- "path": {
464
+ "operationId": {
465
+ "type": "string",
466
+ "pattern": "^[a-z0-9]+(?:-[a-z0-9]+)*$",
467
+ "description": "A kebab-case slug. Excludes \"/\" and \"~\" so an identifier can be embedded in an interaction-rooted pointer without escaping."
468
+ },
469
+ "invocation": {
417
470
  "type": "object",
418
471
  "properties": {
419
- "requiredKeys": {
420
- "type": "array",
421
- "items": {
422
- "type": "string",
423
- "minLength": 1
424
- }
472
+ "executable": {
473
+ "type": "string",
474
+ "pattern": "^[a-z0-9]+(?:-[a-z0-9]+)*$",
475
+ "description": "A logical executable name, never a filesystem path, a URL, a host, or a port (AD-35). The caller maps it to an authorized target through configuration outside the contract."
425
476
  },
426
- "permittedKeys": {
477
+ "subcommandPath": {
427
478
  "type": "array",
428
479
  "items": {
429
480
  "type": "string",
430
- "minLength": 1
431
- },
432
- "description": "The closed set of keys the shape admits. Deliberately not refined to be a superset of `requiredKeys`: no AD-5 code names that contradiction, so it joins the cross-field rules left to the compiler in v0. There is no spelling for \"these keys are required and extras are unconstrained\" \u2014 AD-4 calls the descriptor closed, so an open mode would be a different grammar."
433
- },
434
- "types": {
435
- "type": "object",
436
- "propertyNames": {
437
- "type": "string",
438
- "minLength": 1
481
+ "pattern": "^[a-z0-9]+(?:-[a-z0-9]+)*$",
482
+ "description": "A kebab-case slug. Excludes \"/\" and \"~\" so an identifier can be embedded in an interaction-rooted pointer without escaping."
439
483
  },
440
- "additionalProperties": {
441
- "anyOf": [
442
- {
443
- "type": "string",
444
- "enum": [
445
- "string",
446
- "number",
447
- "boolean",
448
- "object",
449
- "array",
450
- "null"
451
- ]
452
- },
453
- {
454
- "type": "null"
455
- }
456
- ]
457
- },
458
- "description": "Caller-keyed by plain key name, never by pointer: the descriptor-relative pointer spelling does not apply to these keys and must not be extended to them by analogy. A missing key means \"not declared\"; an explicit `null` value means \"declared, type not stated\"."
484
+ "description": "The subcommand segments after the executable, outermost first. Empty is legal and means the executable is invoked with no subcommand."
459
485
  }
460
486
  },
461
487
  "required": [
462
- "requiredKeys",
463
- "permittedKeys",
464
- "types"
488
+ "executable",
489
+ "subcommandPath"
465
490
  ],
466
491
  "additionalProperties": false
467
492
  },
468
- "query": {
493
+ "stateChangeMarker": {
494
+ "type": "boolean",
495
+ "description": "AD-19: whether the operation is intended to change state. AD-20 rule 7 relevance reads it, and AD-10 selects the sensitivity channel by it. Both values are legal and neither is a default."
496
+ },
497
+ "requestShape": {
469
498
  "type": "object",
470
499
  "properties": {
471
- "requiredKeys": {
472
- "type": "array",
473
- "items": {
474
- "type": "string",
475
- "minLength": 1
476
- }
477
- },
478
- "permittedKeys": {
479
- "type": "array",
480
- "items": {
481
- "type": "string",
482
- "minLength": 1
500
+ "argument": {
501
+ "type": "object",
502
+ "properties": {
503
+ "requiredKeys": {
504
+ "type": "array",
505
+ "items": {
506
+ "type": "string",
507
+ "minLength": 1
508
+ }
509
+ },
510
+ "permittedKeys": {
511
+ "type": "array",
512
+ "items": {
513
+ "type": "string",
514
+ "minLength": 1
515
+ },
516
+ "description": "The closed set of keys the shape admits. Deliberately not refined to be a superset of `requiredKeys`: no AD-5 code names that contradiction, so it joins the cross-field rules left to the compiler in v0. There is no spelling for \"these keys are required and extras are unconstrained\" \u2014 AD-4 calls the descriptor closed, so an open mode would be a different grammar."
517
+ },
518
+ "types": {
519
+ "type": "object",
520
+ "propertyNames": {
521
+ "type": "string",
522
+ "minLength": 1
523
+ },
524
+ "additionalProperties": {
525
+ "anyOf": [
526
+ {
527
+ "type": "string",
528
+ "enum": [
529
+ "string",
530
+ "number",
531
+ "boolean",
532
+ "object",
533
+ "array",
534
+ "null"
535
+ ]
536
+ },
537
+ {
538
+ "type": "null"
539
+ }
540
+ ]
541
+ },
542
+ "description": "Caller-keyed by plain key name, never by pointer: the descriptor-relative pointer spelling does not apply to these keys and must not be extended to them by analogy. A missing key means \"not declared\"; an explicit `null` value means \"declared, type not stated\"."
543
+ }
483
544
  },
484
- "description": "The closed set of keys the shape admits. Deliberately not refined to be a superset of `requiredKeys`: no AD-5 code names that contradiction, so it joins the cross-field rules left to the compiler in v0. There is no spelling for \"these keys are required and extras are unconstrained\" \u2014 AD-4 calls the descriptor closed, so an open mode would be a different grammar."
545
+ "required": [
546
+ "requiredKeys",
547
+ "permittedKeys",
548
+ "types"
549
+ ],
550
+ "additionalProperties": false,
551
+ "description": "Positional arguments, keyed by the name the contract gives each position. The key is the author's own label; position is not encoded here, because no AD-31 predicate reads argument order."
485
552
  },
486
- "types": {
553
+ "option": {
487
554
  "type": "object",
488
- "propertyNames": {
489
- "type": "string",
490
- "minLength": 1
555
+ "properties": {
556
+ "requiredKeys": {
557
+ "type": "array",
558
+ "items": {
559
+ "type": "string",
560
+ "minLength": 1
561
+ }
562
+ },
563
+ "permittedKeys": {
564
+ "type": "array",
565
+ "items": {
566
+ "type": "string",
567
+ "minLength": 1
568
+ },
569
+ "description": "The closed set of keys the shape admits. Deliberately not refined to be a superset of `requiredKeys`: no AD-5 code names that contradiction, so it joins the cross-field rules left to the compiler in v0. There is no spelling for \"these keys are required and extras are unconstrained\" \u2014 AD-4 calls the descriptor closed, so an open mode would be a different grammar."
570
+ },
571
+ "types": {
572
+ "type": "object",
573
+ "propertyNames": {
574
+ "type": "string",
575
+ "minLength": 1
576
+ },
577
+ "additionalProperties": {
578
+ "anyOf": [
579
+ {
580
+ "type": "string",
581
+ "enum": [
582
+ "string",
583
+ "number",
584
+ "boolean",
585
+ "object",
586
+ "array",
587
+ "null"
588
+ ]
589
+ },
590
+ {
591
+ "type": "null"
592
+ }
593
+ ]
594
+ },
595
+ "description": "Caller-keyed by plain key name, never by pointer: the descriptor-relative pointer spelling does not apply to these keys and must not be extended to them by analogy. A missing key means \"not declared\"; an explicit `null` value means \"declared, type not stated\"."
596
+ }
491
597
  },
492
- "additionalProperties": {
493
- "anyOf": [
494
- {
598
+ "required": [
599
+ "requiredKeys",
600
+ "permittedKeys",
601
+ "types"
602
+ ],
603
+ "additionalProperties": false
604
+ },
605
+ "environment": {
606
+ "type": "object",
607
+ "properties": {
608
+ "requiredKeys": {
609
+ "type": "array",
610
+ "items": {
495
611
  "type": "string",
496
- "enum": [
497
- "string",
498
- "number",
499
- "boolean",
500
- "object",
501
- "array",
502
- "null"
612
+ "minLength": 1
613
+ }
614
+ },
615
+ "permittedKeys": {
616
+ "type": "array",
617
+ "items": {
618
+ "type": "string",
619
+ "minLength": 1
620
+ },
621
+ "description": "The closed set of keys the shape admits. Deliberately not refined to be a superset of `requiredKeys`: no AD-5 code names that contradiction, so it joins the cross-field rules left to the compiler in v0. There is no spelling for \"these keys are required and extras are unconstrained\" \u2014 AD-4 calls the descriptor closed, so an open mode would be a different grammar."
622
+ },
623
+ "types": {
624
+ "type": "object",
625
+ "propertyNames": {
626
+ "type": "string",
627
+ "minLength": 1
628
+ },
629
+ "additionalProperties": {
630
+ "anyOf": [
631
+ {
632
+ "type": "string",
633
+ "enum": [
634
+ "string",
635
+ "number",
636
+ "boolean",
637
+ "object",
638
+ "array",
639
+ "null"
640
+ ]
641
+ },
642
+ {
643
+ "type": "null"
644
+ }
503
645
  ]
504
646
  },
505
- {
506
- "type": "null"
647
+ "description": "Caller-keyed by plain key name, never by pointer: the descriptor-relative pointer spelling does not apply to these keys and must not be extended to them by analogy. A missing key means \"not declared\"; an explicit `null` value means \"declared, type not stated\"."
648
+ }
649
+ },
650
+ "required": [
651
+ "requiredKeys",
652
+ "permittedKeys",
653
+ "types"
654
+ ],
655
+ "additionalProperties": false,
656
+ "description": "AD-18: an environment channel declaration names a variable and its type and never carries a credential value. The same rule the header channel carries, for the channel that plays the same role off an HTTP interface."
657
+ },
658
+ "stdin": {
659
+ "type": "object",
660
+ "properties": {
661
+ "requiredKeys": {
662
+ "type": "array",
663
+ "items": {
664
+ "type": "string",
665
+ "minLength": 1
507
666
  }
508
- ]
667
+ },
668
+ "permittedKeys": {
669
+ "type": "array",
670
+ "items": {
671
+ "type": "string",
672
+ "minLength": 1
673
+ },
674
+ "description": "The closed set of keys the shape admits. Deliberately not refined to be a superset of `requiredKeys`: no AD-5 code names that contradiction, so it joins the cross-field rules left to the compiler in v0. There is no spelling for \"these keys are required and extras are unconstrained\" \u2014 AD-4 calls the descriptor closed, so an open mode would be a different grammar."
675
+ },
676
+ "types": {
677
+ "type": "object",
678
+ "propertyNames": {
679
+ "type": "string",
680
+ "minLength": 1
681
+ },
682
+ "additionalProperties": {
683
+ "anyOf": [
684
+ {
685
+ "type": "string",
686
+ "enum": [
687
+ "string",
688
+ "number",
689
+ "boolean",
690
+ "object",
691
+ "array",
692
+ "null"
693
+ ]
694
+ },
695
+ {
696
+ "type": "null"
697
+ }
698
+ ]
699
+ },
700
+ "description": "Caller-keyed by plain key name, never by pointer: the descriptor-relative pointer spelling does not apply to these keys and must not be extended to them by analogy. A missing key means \"not declared\"; an explicit `null` value means \"declared, type not stated\"."
701
+ }
509
702
  },
510
- "description": "Caller-keyed by plain key name, never by pointer: the descriptor-relative pointer spelling does not apply to these keys and must not be extended to them by analogy. A missing key means \"not declared\"; an explicit `null` value means \"declared, type not stated\"."
703
+ "required": [
704
+ "requiredKeys",
705
+ "permittedKeys",
706
+ "types"
707
+ ],
708
+ "additionalProperties": false
511
709
  }
512
710
  },
513
711
  "required": [
514
- "requiredKeys",
515
- "permittedKeys",
516
- "types"
712
+ "argument",
713
+ "option",
714
+ "environment",
715
+ "stdin"
517
716
  ],
518
717
  "additionalProperties": false
519
718
  },
520
- "header": {
719
+ "artifacts": {
720
+ "type": "array",
721
+ "items": {
722
+ "type": "string",
723
+ "pattern": "^[a-z0-9]+(?:-[a-z0-9]+)*$",
724
+ "description": "A kebab-case slug. Excludes \"/\" and \"~\" so an identifier can be embedded in an interaction-rooted pointer without escaping."
725
+ },
726
+ "description": "The files the operation writes, as bare declared identifiers with existence semantics only. No descriptor and no keys of their own: an artifact pointer's identifier segment resolves against this list, and structure comes from the operation's one response descriptor when `descriptorChannel` nominates that artifact. A pointer naming an identifier absent here fails compilation under `unresolved-artifact-reference`."
727
+ },
728
+ "descriptorChannel": {
729
+ "oneOf": [
730
+ {
731
+ "type": "object",
732
+ "properties": {
733
+ "kind": {
734
+ "type": "string",
735
+ "const": "stream"
736
+ },
737
+ "channel": {
738
+ "type": "string",
739
+ "enum": [
740
+ "stdout",
741
+ "stderr"
742
+ ]
743
+ }
744
+ },
745
+ "required": [
746
+ "kind",
747
+ "channel"
748
+ ],
749
+ "additionalProperties": false
750
+ },
751
+ {
752
+ "type": "object",
753
+ "properties": {
754
+ "kind": {
755
+ "type": "string",
756
+ "const": "artifact"
757
+ },
758
+ "artifactId": {
759
+ "type": "string",
760
+ "pattern": "^[a-z0-9]+(?:-[a-z0-9]+)*$",
761
+ "description": "One of the identifiers this operation declares in `artifacts`. A name absent from that list fails compilation under `unresolved-artifact-reference` rather than resolving absent, because a dangling declaration is an authoring fault the compiler can see."
762
+ }
763
+ },
764
+ "required": [
765
+ "kind",
766
+ "artifactId"
767
+ ],
768
+ "additionalProperties": false
769
+ }
770
+ ]
771
+ },
772
+ "responseDescriptor": {
521
773
  "type": "object",
522
774
  "properties": {
523
775
  "requiredKeys": {
@@ -525,15 +777,15 @@
525
777
  "items": {
526
778
  "type": "string",
527
779
  "minLength": 1
528
- }
780
+ },
781
+ "description": "No minimum of two anywhere in this shape: AD-20 rule 2 relevance is \"the descriptor declares more than one pointer\", so a one-pointer descriptor must parse in order to be the irrelevant case."
529
782
  },
530
783
  "permittedKeys": {
531
784
  "type": "array",
532
785
  "items": {
533
786
  "type": "string",
534
787
  "minLength": 1
535
- },
536
- "description": "The closed set of keys the shape admits. Deliberately not refined to be a superset of `requiredKeys`: no AD-5 code names that contradiction, so it joins the cross-field rules left to the compiler in v0. There is no spelling for \"these keys are required and extras are unconstrained\" \u2014 AD-4 calls the descriptor closed, so an open mode would be a different grammar."
788
+ }
537
789
  },
538
790
  "types": {
539
791
  "type": "object",
@@ -559,363 +811,351 @@
559
811
  }
560
812
  ]
561
813
  },
562
- "description": "Caller-keyed by plain key name, never by pointer: the descriptor-relative pointer spelling does not apply to these keys and must not be extended to them by analogy. A missing key means \"not declared\"; an explicit `null` value means \"declared, type not stated\"."
563
- }
564
- },
565
- "required": [
566
- "requiredKeys",
567
- "permittedKeys",
568
- "types"
569
- ],
570
- "additionalProperties": false,
571
- "description": "AD-18: a header channel declaration names a header and its type and never carries a credential value."
572
- },
573
- "body": {
574
- "type": "object",
575
- "properties": {
576
- "requiredKeys": {
577
- "type": "array",
578
- "items": {
579
- "type": "string",
580
- "minLength": 1
581
- }
814
+ "description": "Caller-keyed by plain key name, never by pointer: this is the shape where that trap bites, since `requiredKeys` sits beside a pointer-keyed `channelRoles` and a pointer-valued `successIndicator`, and the descriptor-relative spelling must not be extended here by analogy. A missing key means \"not declared\"; an explicit `null` value means \"declared, type not stated\", which is AD-31's enumerated indeterminate descriptor state and the shape `quantifier-over-non-collection` reads."
582
815
  },
583
- "permittedKeys": {
584
- "type": "array",
585
- "items": {
586
- "type": "string",
587
- "minLength": 1
588
- },
589
- "description": "The closed set of keys the shape admits. Deliberately not refined to be a superset of `requiredKeys`: no AD-5 code names that contradiction, so it joins the cross-field rules left to the compiler in v0. There is no spelling for \"these keys are required and extras are unconstrained\" \u2014 AD-4 calls the descriptor closed, so an open mode would be a different grammar."
816
+ "successIndicator": {
817
+ "anyOf": [
818
+ {
819
+ "type": "string",
820
+ "pattern": "^(?:\\/(?:[^/~]|~[01])*)*$",
821
+ "description": "A plain RFC 6901 pointer into one operation's response descriptor. It resolves through the operation an interaction step names, never through the interaction root. A request or response shape's descriptor keys are plain key names rather than pointers, so this spelling does not apply there. The empty string is admitted and carries RFC 6901's own meaning, the whole document: as a nominated success indicator it says success is visible in the response taken as a whole rather than at any one key, and as a channel-role key it assigns a role to the whole body."
822
+ },
823
+ {
824
+ "type": "null"
825
+ }
826
+ ],
827
+ "description": "AD-20 rule 1 relevance reads this as its first conjunct, so `null` must stay representable."
590
828
  },
591
- "types": {
592
- "type": "object",
593
- "propertyNames": {
594
- "type": "string",
595
- "minLength": 1
596
- },
597
- "additionalProperties": {
598
- "anyOf": [
599
- {
829
+ "channelRoles": {
830
+ "anyOf": [
831
+ {
832
+ "type": "object",
833
+ "propertyNames": {
834
+ "type": "string",
835
+ "pattern": "^(?:\\/(?:[^/~]|~[01])*)*$",
836
+ "description": "A plain RFC 6901 pointer into one operation's response descriptor. It resolves through the operation an interaction step names, never through the interaction root. A request or response shape's descriptor keys are plain key names rather than pointers, so this spelling does not apply there. The empty string is admitted and carries RFC 6901's own meaning, the whole document: as a nominated success indicator it says success is visible in the response taken as a whole rather than at any one key, and as a channel-role key it assigns a role to the whole body."
837
+ },
838
+ "additionalProperties": {
600
839
  "type": "string",
601
840
  "enum": [
602
- "string",
603
- "number",
604
- "boolean",
605
- "object",
606
- "array",
607
- "null"
841
+ "success-indicator",
842
+ "diagnostic",
843
+ "payload",
844
+ "collection"
608
845
  ]
609
- },
610
- {
611
- "type": "null"
612
846
  }
613
- ]
614
- },
615
- "description": "Caller-keyed by plain key name, never by pointer: the descriptor-relative pointer spelling does not apply to these keys and must not be extended to them by analogy. A missing key means \"not declared\"; an explicit `null` value means \"declared, type not stated\"."
847
+ },
848
+ {
849
+ "type": "null"
850
+ }
851
+ ],
852
+ "description": "Caller-keyed, and expected to be partial: a missing key means \"no role declared for that pointer\". AD-31 grades absent and explicitly empty differently, so `null`, `{}`, and a populated map are three distinct answers. Deliberately not refined to require a role for every descriptor key: no AD-5 code names that rule, the AD-31 predicates degrade gracefully on partial roles, and a refinement buys nothing an export can carry."
853
+ },
854
+ "collectionLocations": {
855
+ "anyOf": [
856
+ {
857
+ "type": "array",
858
+ "items": {
859
+ "type": "object",
860
+ "properties": {
861
+ "pointer": {
862
+ "type": "string",
863
+ "pattern": "^(?:\\/(?:[^/~]|~[01])*)*$",
864
+ "description": "A plain RFC 6901 pointer into one operation's response descriptor. It resolves through the operation an interaction step names, never through the interaction root. A request or response shape's descriptor keys are plain key names rather than pointers, so this spelling does not apply there. The empty string is admitted and carries RFC 6901's own meaning, the whole document: as a nominated success indicator it says success is visible in the response taken as a whole rather than at any one key, and as a channel-role key it assigns a role to the whole body."
865
+ },
866
+ "expectedCardinality": {
867
+ "oneOf": [
868
+ {
869
+ "type": "object",
870
+ "properties": {
871
+ "mode": {
872
+ "type": "string",
873
+ "const": "exact"
874
+ },
875
+ "count": {
876
+ "type": "integer",
877
+ "minimum": 0,
878
+ "maximum": 9007199254740991
879
+ }
880
+ },
881
+ "required": [
882
+ "mode",
883
+ "count"
884
+ ],
885
+ "additionalProperties": false
886
+ },
887
+ {
888
+ "type": "object",
889
+ "properties": {
890
+ "mode": {
891
+ "type": "string",
892
+ "const": "at-most"
893
+ },
894
+ "max": {
895
+ "type": "integer",
896
+ "minimum": 0,
897
+ "maximum": 9007199254740991
898
+ }
899
+ },
900
+ "required": [
901
+ "mode",
902
+ "max"
903
+ ],
904
+ "additionalProperties": false
905
+ },
906
+ {
907
+ "type": "object",
908
+ "properties": {
909
+ "mode": {
910
+ "type": "string",
911
+ "const": "page-bounded"
912
+ },
913
+ "max": {
914
+ "type": "integer",
915
+ "minimum": 0,
916
+ "maximum": 9007199254740991
917
+ }
918
+ },
919
+ "required": [
920
+ "mode",
921
+ "max"
922
+ ],
923
+ "additionalProperties": false
924
+ }
925
+ ]
926
+ },
927
+ "referenceSet": {
928
+ "anyOf": [
929
+ {
930
+ "type": "string",
931
+ "pattern": "^[a-z0-9]+(?:-[a-z0-9]+)*$",
932
+ "description": "A kebab-case slug. Excludes \"/\" and \"~\" so an identifier can be embedded in an interaction-rooted pointer without escaping."
933
+ },
934
+ {
935
+ "type": "null"
936
+ }
937
+ ],
938
+ "description": "AD-20 rule 6 is relevant when a declared collection location names a reference set, so `null` is the shape that makes the rule irrelevant and must stay representable."
939
+ }
940
+ },
941
+ "required": [
942
+ "pointer",
943
+ "expectedCardinality",
944
+ "referenceSet"
945
+ ],
946
+ "additionalProperties": false
947
+ }
948
+ },
949
+ {
950
+ "type": "null"
951
+ }
952
+ ],
953
+ "description": "AD-20 rule 4 relevance reads this, and AD-31 grades an absent declaration and an explicit empty one differently, so `null` and `[]` are distinct answers."
616
954
  }
617
955
  },
618
956
  "required": [
619
957
  "requiredKeys",
620
958
  "permittedKeys",
621
- "types"
959
+ "types",
960
+ "successIndicator",
961
+ "channelRoles",
962
+ "collectionLocations"
622
963
  ],
623
964
  "additionalProperties": false
624
- }
625
- },
626
- "required": [
627
- "path",
628
- "query",
629
- "header",
630
- "body"
631
- ],
632
- "additionalProperties": false
633
- },
634
- "responseDescriptor": {
635
- "type": "object",
636
- "properties": {
637
- "requiredKeys": {
638
- "type": "array",
639
- "items": {
640
- "type": "string",
641
- "minLength": 1
642
- },
643
- "description": "No minimum of two anywhere in this shape: AD-20 rule 2 relevance is \"the descriptor declares more than one pointer\", so a one-pointer descriptor must parse in order to be the irrelevant case."
644
965
  },
645
- "permittedKeys": {
966
+ "volatilePointers": {
646
967
  "type": "array",
647
968
  "items": {
648
969
  "type": "string",
649
- "minLength": 1
970
+ "pattern": "^(?:\\/(?:[^/~]|~[01])*)*$",
971
+ "description": "A plain RFC 6901 pointer into one operation's response descriptor. It resolves through the operation an interaction step names, never through the interaction root. A request or response shape's descriptor keys are plain key names rather than pointers, so this spelling does not apply there. The empty string is admitted and carries RFC 6901's own meaning, the whole document: as a nominated success indicator it says success is visible in the response taken as a whole rather than at any one key, and as a channel-role key it assigns a role to the whole body."
650
972
  }
651
973
  },
652
- "types": {
653
- "type": "object",
654
- "propertyNames": {
655
- "type": "string",
656
- "minLength": 1
657
- },
658
- "additionalProperties": {
659
- "anyOf": [
660
- {
661
- "type": "string",
662
- "enum": [
663
- "string",
664
- "number",
665
- "boolean",
666
- "object",
667
- "array",
668
- "null"
669
- ]
670
- },
671
- {
672
- "type": "null"
673
- }
674
- ]
675
- },
676
- "description": "Caller-keyed by plain key name, never by pointer: this is the shape where that trap bites, since `requiredKeys` sits beside a pointer-keyed `channelRoles` and a pointer-valued `successIndicator`, and the descriptor-relative spelling must not be extended here by analogy. A missing key means \"not declared\"; an explicit `null` value means \"declared, type not stated\", which is AD-31's enumerated indeterminate descriptor state and the shape `quantifier-over-non-collection` reads."
677
- },
678
- "successIndicator": {
679
- "anyOf": [
680
- {
681
- "type": "string",
682
- "pattern": "^(?:\\/(?:[^/~]|~[01])*)*$",
683
- "description": "A plain RFC 6901 pointer into one operation's response descriptor. It resolves through the operation an interaction step names, never through the interaction root. A request or response shape's descriptor keys are plain key names rather than pointers, so this spelling does not apply there. The empty string is admitted and carries RFC 6901's own meaning, the whole document: as a nominated success indicator it says success is visible in the response taken as a whole rather than at any one key, and as a channel-role key it assigns a role to the whole body."
684
- },
685
- {
686
- "type": "null"
687
- }
688
- ],
689
- "description": "AD-20 rule 1 relevance reads this as its first conjunct, so `null` must stay representable."
690
- },
691
- "channelRoles": {
974
+ "sensitivityWitness": {
692
975
  "anyOf": [
693
976
  {
694
977
  "type": "object",
695
- "propertyNames": {
696
- "type": "string",
697
- "pattern": "^(?:\\/(?:[^/~]|~[01])*)*$",
698
- "description": "A plain RFC 6901 pointer into one operation's response descriptor. It resolves through the operation an interaction step names, never through the interaction root. A request or response shape's descriptor keys are plain key names rather than pointers, so this spelling does not apply there. The empty string is admitted and carries RFC 6901's own meaning, the whole document: as a nominated success indicator it says success is visible in the response taken as a whole rather than at any one key, and as a channel-role key it assigns a role to the whole body."
699
- },
700
- "additionalProperties": {
701
- "type": "string",
702
- "enum": [
703
- "success-indicator",
704
- "diagnostic",
705
- "payload",
706
- "collection"
707
- ]
708
- }
709
- },
710
- {
711
- "type": "null"
712
- }
713
- ],
714
- "description": "Caller-keyed, and expected to be partial: a missing key means \"no role declared for that pointer\". AD-31 grades absent and explicitly empty differently, so `null`, `{}`, and a populated map are three distinct answers. Deliberately not refined to require a role for every descriptor key: no AD-5 code names that rule, the AD-31 predicates degrade gracefully on partial roles, and a refinement buys nothing an export can carry."
715
- },
716
- "collectionLocations": {
717
- "anyOf": [
718
- {
719
- "type": "array",
720
- "items": {
721
- "type": "object",
722
- "properties": {
723
- "pointer": {
724
- "type": "string",
725
- "pattern": "^(?:\\/(?:[^/~]|~[01])*)*$",
726
- "description": "A plain RFC 6901 pointer into one operation's response descriptor. It resolves through the operation an interaction step names, never through the interaction root. A request or response shape's descriptor keys are plain key names rather than pointers, so this spelling does not apply there. The empty string is admitted and carries RFC 6901's own meaning, the whole document: as a nominated success indicator it says success is visible in the response taken as a whole rather than at any one key, and as a channel-role key it assigns a role to the whole body."
727
- },
728
- "expectedCardinality": {
729
- "oneOf": [
730
- {
731
- "type": "object",
732
- "properties": {
733
- "mode": {
734
- "type": "string",
735
- "const": "exact"
736
- },
737
- "count": {
738
- "type": "integer",
739
- "minimum": 0,
740
- "maximum": 9007199254740991
741
- }
742
- },
743
- "required": [
744
- "mode",
745
- "count"
746
- ],
747
- "additionalProperties": false
748
- },
749
- {
750
- "type": "object",
751
- "properties": {
752
- "mode": {
753
- "type": "string",
754
- "const": "at-most"
755
- },
756
- "max": {
757
- "type": "integer",
758
- "minimum": 0,
759
- "maximum": 9007199254740991
760
- }
761
- },
762
- "required": [
763
- "mode",
764
- "max"
765
- ],
766
- "additionalProperties": false
767
- },
768
- {
769
- "type": "object",
770
- "properties": {
771
- "mode": {
772
- "type": "string",
773
- "const": "page-bounded"
774
- },
775
- "max": {
776
- "type": "integer",
777
- "minimum": 0,
778
- "maximum": 9007199254740991
779
- }
780
- },
781
- "required": [
782
- "mode",
783
- "max"
784
- ],
785
- "additionalProperties": false
786
- }
787
- ]
788
- },
789
- "referenceSet": {
790
- "anyOf": [
791
- {
978
+ "properties": {
979
+ "witnessId": {
980
+ "type": "string",
981
+ "pattern": "^[a-z0-9]+(?:-[a-z0-9]+)*$",
982
+ "description": "A kebab-case slug. Excludes \"/\" and \"~\" so an identifier can be embedded in an interaction-rooted pointer without escaping."
983
+ },
984
+ "channel": {
985
+ "type": "string",
986
+ "enum": [
987
+ "path",
988
+ "query",
989
+ "body",
990
+ "argument",
991
+ "option",
992
+ "environment",
993
+ "stdin"
994
+ ]
995
+ },
996
+ "legs": {
997
+ "minItems": 2,
998
+ "maxItems": 2,
999
+ "type": "array",
1000
+ "items": {
1001
+ "type": "object",
1002
+ "properties": {
1003
+ "legId": {
792
1004
  "type": "string",
793
1005
  "pattern": "^[a-z0-9]+(?:-[a-z0-9]+)*$",
794
1006
  "description": "A kebab-case slug. Excludes \"/\" and \"~\" so an identifier can be embedded in an interaction-rooted pointer without escaping."
795
1007
  },
796
- {
797
- "type": "null"
1008
+ "inputs": {
1009
+ "anyOf": [
1010
+ {
1011
+ "$ref": "#/$defs/WitnessInputs"
1012
+ },
1013
+ {
1014
+ "type": "object",
1015
+ "properties": {
1016
+ "argument": {
1017
+ "type": "object",
1018
+ "propertyNames": {
1019
+ "type": "string"
1020
+ },
1021
+ "additionalProperties": {
1022
+ "$ref": "#/$defs/JsonValue"
1023
+ }
1024
+ },
1025
+ "option": {
1026
+ "type": "object",
1027
+ "propertyNames": {
1028
+ "type": "string"
1029
+ },
1030
+ "additionalProperties": {
1031
+ "$ref": "#/$defs/JsonValue"
1032
+ }
1033
+ },
1034
+ "environment": {
1035
+ "type": "object",
1036
+ "propertyNames": {
1037
+ "type": "string",
1038
+ "minLength": 1
1039
+ },
1040
+ "additionalProperties": {
1041
+ "type": "string"
1042
+ }
1043
+ },
1044
+ "stdin": {
1045
+ "oneOf": [
1046
+ {
1047
+ "type": "object",
1048
+ "properties": {
1049
+ "kind": {
1050
+ "type": "string",
1051
+ "const": "json"
1052
+ },
1053
+ "value": {
1054
+ "$ref": "#/$defs/JsonValue"
1055
+ }
1056
+ },
1057
+ "required": [
1058
+ "kind",
1059
+ "value"
1060
+ ],
1061
+ "additionalProperties": false
1062
+ },
1063
+ {
1064
+ "type": "object",
1065
+ "properties": {
1066
+ "kind": {
1067
+ "type": "string",
1068
+ "const": "text"
1069
+ },
1070
+ "value": {
1071
+ "type": "string"
1072
+ }
1073
+ },
1074
+ "required": [
1075
+ "kind",
1076
+ "value"
1077
+ ],
1078
+ "additionalProperties": false
1079
+ },
1080
+ {
1081
+ "type": "object",
1082
+ "properties": {
1083
+ "kind": {
1084
+ "type": "string",
1085
+ "const": "absent"
1086
+ }
1087
+ },
1088
+ "required": [
1089
+ "kind"
1090
+ ],
1091
+ "additionalProperties": false
1092
+ }
1093
+ ]
1094
+ }
1095
+ },
1096
+ "required": [
1097
+ "argument",
1098
+ "option",
1099
+ "environment",
1100
+ "stdin"
1101
+ ],
1102
+ "additionalProperties": false
1103
+ }
1104
+ ]
798
1105
  }
1106
+ },
1107
+ "required": [
1108
+ "legId",
1109
+ "inputs"
799
1110
  ],
800
- "description": "AD-20 rule 6 is relevant when a declared collection location names a reference set, so `null` is the shape that makes the rule irrelevant and must stay representable."
1111
+ "additionalProperties": false
801
1112
  }
802
1113
  },
803
- "required": [
804
- "pointer",
805
- "expectedCardinality",
806
- "referenceSet"
807
- ],
808
- "additionalProperties": false
809
- }
1114
+ "relation": {
1115
+ "$ref": "#/$defs/Expression"
1116
+ }
1117
+ },
1118
+ "required": [
1119
+ "witnessId",
1120
+ "channel",
1121
+ "legs",
1122
+ "relation"
1123
+ ],
1124
+ "additionalProperties": false
810
1125
  },
811
1126
  {
812
1127
  "type": "null"
813
1128
  }
814
1129
  ],
815
- "description": "AD-20 rule 4 relevance reads this, and AD-31 grades an absent declaration and an explicit empty one differently, so `null` and `[]` are distinct answers."
1130
+ "description": "AD-10, mandatory per declared operation rather than per interface, on the api operation's own terms. `null` is legal only for an operation declaring no keys in any request channel, which for a command means no argument, no option, no environment variable, and no standard input. An input-bearing operation declaring `null` fails a strict compilation under `undeclared-mandatory-input`."
816
1131
  }
817
1132
  },
818
1133
  "required": [
819
- "requiredKeys",
820
- "permittedKeys",
821
- "types",
822
- "successIndicator",
823
- "channelRoles",
824
- "collectionLocations"
1134
+ "operationId",
1135
+ "invocation",
1136
+ "stateChangeMarker",
1137
+ "requestShape",
1138
+ "artifacts",
1139
+ "descriptorChannel",
1140
+ "responseDescriptor",
1141
+ "volatilePointers",
1142
+ "sensitivityWitness"
825
1143
  ],
826
1144
  "additionalProperties": false
827
1145
  },
828
- "volatilePointers": {
829
- "type": "array",
830
- "items": {
831
- "type": "string",
832
- "pattern": "^(?:\\/(?:[^/~]|~[01])*)*$",
833
- "description": "A plain RFC 6901 pointer into one operation's response descriptor. It resolves through the operation an interaction step names, never through the interaction root. A request or response shape's descriptor keys are plain key names rather than pointers, so this spelling does not apply there. The empty string is admitted and carries RFC 6901's own meaning, the whole document: as a nominated success indicator it says success is visible in the response taken as a whole rather than at any one key, and as a channel-role key it assigns a role to the whole body."
834
- }
835
- },
836
- "sensitivityWitness": {
837
- "anyOf": [
838
- {
839
- "type": "object",
840
- "properties": {
841
- "witnessId": {
842
- "type": "string",
843
- "pattern": "^[a-z0-9]+(?:-[a-z0-9]+)*$",
844
- "description": "A kebab-case slug. Excludes \"/\" and \"~\" so an identifier can be embedded in an interaction-rooted pointer without escaping."
845
- },
846
- "channel": {
847
- "type": "string",
848
- "enum": [
849
- "path",
850
- "query",
851
- "body"
852
- ]
853
- },
854
- "legs": {
855
- "minItems": 2,
856
- "maxItems": 2,
857
- "type": "array",
858
- "items": {
859
- "type": "object",
860
- "properties": {
861
- "legId": {
862
- "type": "string",
863
- "pattern": "^[a-z0-9]+(?:-[a-z0-9]+)*$",
864
- "description": "A kebab-case slug. Excludes \"/\" and \"~\" so an identifier can be embedded in an interaction-rooted pointer without escaping."
865
- },
866
- "inputs": {
867
- "$ref": "#/$defs/WitnessInputs"
868
- }
869
- },
870
- "required": [
871
- "legId",
872
- "inputs"
873
- ],
874
- "additionalProperties": false
875
- }
876
- },
877
- "relation": {
878
- "$ref": "#/$defs/Expression"
879
- }
880
- },
881
- "required": [
882
- "witnessId",
883
- "channel",
884
- "legs",
885
- "relation"
886
- ],
887
- "additionalProperties": false
888
- },
889
- {
890
- "type": "null"
891
- }
892
- ],
893
- "description": "AD-10, mandatory per declared operation rather than per interface. `null` is legal only for an operation declaring no keys in any request channel; AD-10 exempts that operation and requires the exemption to be recorded, which pre-flight does as an `exempt` check. An input-bearing operation declaring `null` fails a strict compilation under `undeclared-mandatory-input`, alongside the other declaration-completeness check that code already gates."
894
- }
895
- },
896
- "required": [
897
- "operationId",
898
- "method",
899
- "pathTemplate",
900
- "stateChangeMarker",
901
- "requestShape",
902
- "responseDescriptor",
903
- "volatilePointers",
904
- "sensitivityWitness"
905
- ],
906
- "additionalProperties": false
1146
+ "description": "No uniqueness constraint: two operations colliding on their transport identity after parameter-name erasure is `duplicate-operation-signature`, a coded compile-time error, and a schema that deduped them would delete it."
1147
+ }
907
1148
  },
908
- "description": "No uniqueness constraint: two operations colliding on method plus path template after parameter-name erasure is `duplicate-operation-signature`, a coded compile-time error, and a schema that deduped them would delete it."
1149
+ "required": [
1150
+ "logicalId",
1151
+ "kind",
1152
+ "operations"
1153
+ ],
1154
+ "additionalProperties": false
909
1155
  }
910
- },
911
- "required": [
912
- "logicalId",
913
- "kind",
914
- "operations"
915
- ],
916
- "additionalProperties": false
1156
+ ]
917
1157
  },
918
- "description": "AD-35: logical identifiers only. No entry here is ever a URL, a host, or a port."
1158
+ "description": "AD-35: logical identifiers only. No entry here is ever a URL, a host, or a port. Discriminated on `kind`, which makes this field the eval contract's `schemaVersion` 3 -> 4 BREAKING bump under AD-11, whose rule is that \"adding an optional field is a `schemaVersion` bump recorded in the field's own description; removing or retyping is breaking\". The `api`, `web`, and `mcp` branches carry the shipped operation shape unchanged, so a version-3 contract's own bytes still parse on them; the `cli` branch carries a command operation, which declares a logical invocation instead of a method and a path template, four command input channels instead of the four transport channels, a nominated output channel for its one response descriptor, and the files it writes."
919
1159
  },
920
1160
  "referenceSets": {
921
1161
  "anyOf": [
@@ -1034,28 +1274,56 @@
1034
1274
  "description": "A kebab-case slug. Excludes \"/\" and \"~\" so an identifier can be embedded in an interaction-rooted pointer without escaping."
1035
1275
  },
1036
1276
  "inputBinding": {
1037
- "type": "object",
1038
- "properties": {
1039
- "path": {
1040
- "$ref": "#/$defs/InputBindingChannel"
1041
- },
1042
- "query": {
1043
- "$ref": "#/$defs/InputBindingChannel"
1044
- },
1045
- "header": {
1046
- "$ref": "#/$defs/InputBindingChannel"
1277
+ "anyOf": [
1278
+ {
1279
+ "type": "object",
1280
+ "properties": {
1281
+ "path": {
1282
+ "$ref": "#/$defs/InputBindingChannel"
1283
+ },
1284
+ "query": {
1285
+ "$ref": "#/$defs/InputBindingChannel"
1286
+ },
1287
+ "header": {
1288
+ "$ref": "#/$defs/InputBindingChannel"
1289
+ },
1290
+ "body": {
1291
+ "$ref": "#/$defs/InputBindingChannel"
1292
+ }
1293
+ },
1294
+ "required": [
1295
+ "path",
1296
+ "query",
1297
+ "header",
1298
+ "body"
1299
+ ],
1300
+ "additionalProperties": false
1047
1301
  },
1048
- "body": {
1049
- "$ref": "#/$defs/InputBindingChannel"
1302
+ {
1303
+ "type": "object",
1304
+ "properties": {
1305
+ "argument": {
1306
+ "$ref": "#/$defs/InputBindingChannel"
1307
+ },
1308
+ "option": {
1309
+ "$ref": "#/$defs/InputBindingChannel"
1310
+ },
1311
+ "environment": {
1312
+ "$ref": "#/$defs/InputBindingChannel"
1313
+ },
1314
+ "stdin": {
1315
+ "$ref": "#/$defs/InputBindingChannel"
1316
+ }
1317
+ },
1318
+ "required": [
1319
+ "argument",
1320
+ "option",
1321
+ "environment",
1322
+ "stdin"
1323
+ ],
1324
+ "additionalProperties": false
1050
1325
  }
1051
- },
1052
- "required": [
1053
- "path",
1054
- "query",
1055
- "header",
1056
- "body"
1057
- ],
1058
- "additionalProperties": false
1326
+ ]
1059
1327
  },
1060
1328
  "after": {
1061
1329
  "anyOf": [
@@ -1069,13 +1337,23 @@
1069
1337
  }
1070
1338
  ],
1071
1339
  "description": "The temporal clause: the identifier of an earlier step, or `null`. That the named step carries no clause of its own is AD-39's one-level bound, which fires `nested-temporal-clause` at compile time and is deliberately not enforced here, so the code keeps a shape to fire on."
1340
+ },
1341
+ "cardinality": {
1342
+ "type": "string",
1343
+ "enum": [
1344
+ "exactly-one",
1345
+ "at-most-one",
1346
+ "any"
1347
+ ],
1348
+ "description": "AD-39's declared selector cardinality (owed item 2): `exactly-one` or `at-most-one` when the step expects a single matching observation, `any` when several are legitimate. Several matches under `exactly-one`/`at-most-one` is the named ambiguity condition; a reference selection function returns it as data. Required rather than optional, which makes this a BREAKING `schemaVersion` bump under AD-11: adding an optional field is additive, and this field is not optional."
1072
1349
  }
1073
1350
  },
1074
1351
  "required": [
1075
1352
  "stepId",
1076
1353
  "operationId",
1077
1354
  "inputBinding",
1078
- "after"
1355
+ "after",
1356
+ "cardinality"
1079
1357
  ],
1080
1358
  "additionalProperties": false
1081
1359
  },
@@ -1148,23 +1426,83 @@
1148
1426
  "type": "null"
1149
1427
  }
1150
1428
  ]
1151
- }
1152
- },
1153
- "required": [
1154
- "setup",
1155
- "cleanup"
1156
- ],
1157
- "additionalProperties": false
1158
- },
1159
- "budgets": {
1160
- "type": "object",
1161
- "properties": {
1162
- "maxToolCalls": {
1163
- "type": "integer",
1164
- "minimum": 0,
1165
- "maximum": 9007199254740991
1166
1429
  },
1167
- "maxWallClockMinutes": {
1430
+ "principals": {
1431
+ "anyOf": [
1432
+ {
1433
+ "type": "object",
1434
+ "propertyNames": {
1435
+ "type": "string",
1436
+ "pattern": "^[a-z0-9]+(?:-[a-z0-9]+)*$",
1437
+ "description": "A kebab-case slug. Excludes \"/\" and \"~\" so an identifier can be embedded in an interaction-rooted pointer without escaping."
1438
+ },
1439
+ "additionalProperties": {
1440
+ "type": "object",
1441
+ "properties": {
1442
+ "kind": {
1443
+ "type": "string",
1444
+ "minLength": 1
1445
+ }
1446
+ },
1447
+ "required": [
1448
+ "kind"
1449
+ ],
1450
+ "additionalProperties": false
1451
+ }
1452
+ },
1453
+ {
1454
+ "type": "null"
1455
+ }
1456
+ ],
1457
+ "description": "Owed item 3: the principals a `{ principal }` input binding may name. Caller-keyed by name, which makes a duplicate declaration unrepresentable, and valueless, since AD-19 forbids a credential value in a declaration and AD-18 keeps the name an opaque label. `null`, `{}`, and a populated map are three distinct answers, on `referenceSets`' own reasoning. A binding naming a key absent here still parses; it fires `undeclared-mandatory-input` at compile time in strict mode, because a cross-subtree constraint reaching from `interactionPlan` into this field cannot survive the export. Required, not optional, which makes this and `resources` below together the eval contract's `schemaVersion` 2 -> 3 BREAKING bump under AD-11, whose rule is that \"adding an optional field is a `schemaVersion` bump recorded in the field's own description; removing or retyping is breaking\". `null` is the legal spelling for a contract declaring none, but `TestData` is a `strictObject` and the key itself must be present, so no version-2 contract parses."
1458
+ },
1459
+ "resources": {
1460
+ "anyOf": [
1461
+ {
1462
+ "type": "object",
1463
+ "propertyNames": {
1464
+ "type": "string",
1465
+ "pattern": "^[a-z0-9]+(?:-[a-z0-9]+)*$",
1466
+ "description": "A kebab-case slug. Excludes \"/\" and \"~\" so an identifier can be embedded in an interaction-rooted pointer without escaping."
1467
+ },
1468
+ "additionalProperties": {
1469
+ "type": "object",
1470
+ "properties": {
1471
+ "kind": {
1472
+ "type": "string",
1473
+ "minLength": 1
1474
+ }
1475
+ },
1476
+ "required": [
1477
+ "kind"
1478
+ ],
1479
+ "additionalProperties": false
1480
+ }
1481
+ },
1482
+ {
1483
+ "type": "null"
1484
+ }
1485
+ ],
1486
+ "description": "Test-data resources, declared the same way as `principals` above. Any entry fires `scoped-reference-resolves-forbidden` at compile time exactly as a `scopedResources` entry does: a resource the contract points the evaluator at is a scoped reference wherever it is written down (AD-16). Required on the same terms as `principals`, and the other half of the eval contract's `schemaVersion` 2 -> 3 BREAKING bump under AD-11: `null` is the empty spelling and the key is mandatory."
1487
+ }
1488
+ },
1489
+ "required": [
1490
+ "setup",
1491
+ "cleanup",
1492
+ "principals",
1493
+ "resources"
1494
+ ],
1495
+ "additionalProperties": false
1496
+ },
1497
+ "budgets": {
1498
+ "type": "object",
1499
+ "properties": {
1500
+ "maxToolCalls": {
1501
+ "type": "integer",
1502
+ "minimum": 0,
1503
+ "maximum": 9007199254740991
1504
+ },
1505
+ "maxWallClockMinutes": {
1168
1506
  "type": "integer",
1169
1507
  "minimum": 0,
1170
1508
  "maximum": 9007199254740991
@@ -1270,7 +1608,7 @@
1270
1608
  "fixtureReset"
1271
1609
  ],
1272
1610
  "additionalProperties": false,
1273
- "description": "The Eval Contract. Succeeds the prior-art `eval-contract` schema per AD-24. It carries every declaration AD-19 requires so that AD-31's fourteen relevance and satisfaction predicates are decidable from declarations alone. AD-10's sensitivity witnesses arrived in this version, on each operation, as the additive `schemaVersion` bump AD-11 requires; the bump is recorded in each new field's own description, since no reader in this version declares an expected version constant to compare against.",
1611
+ "description": "The Eval Contract. Succeeds the prior-art `eval-contract` schema per AD-24. It carries every declaration AD-19 requires so that AD-31's fourteen relevance and satisfaction predicates are decidable from declarations alone. AD-10's sensitivity witnesses arrived on each operation as an additive `schemaVersion` bump; version 4 opens `permittedInterfaces` to a second interface kind, so a contract may describe a system under test that runs behind a command, and widens the interaction pointer's accepted language with the four command input channels and the `artifact` channel. The widening retypes nothing on its own, since every pointer legal under version 3 is still legal; it carries the same version because it ships in the same release as the retypes. Each bump is recorded in its own field's description, and `compile` compares the stamp against `EVAL_CONTRACT_SCHEMA_VERSION` and throws AD-28's `schema-version-mismatch` on anything else.",
1274
1612
  "$defs": {
1275
1613
  "Expression": {
1276
1614
  "description": "A `check` expression tree over AD-4's closed vocabulary, discriminated on `op`. Exports as a `oneOf` of sixteen branches, each identified by its `op` const; a constraint ledger entry names a branch by that const rather than by position.",
@@ -1596,11 +1934,618 @@
1596
1934
  "$ref": "#/$defs/Operand"
1597
1935
  }
1598
1936
  ],
1599
- "description": "One operand, legally a pointer. Every position admits the full operand union on purpose. AD-26 makes a reference-set operand outside its three legal positions \u2014 `covers-by-key`'s expected operand, and the set operand of `set-membership` or `containment` \u2014 fail compilation under `malformed-operator-expression`, which only stays fireable if the other positions admit it. Compilation enforces the legality stated here; the schema does not narrow it.",
1600
- "minItems": 1,
1601
- "items": false
1937
+ "description": "One operand, legally a pointer. Every position admits the full operand union on purpose. AD-26 makes a reference-set operand outside its three legal positions \u2014 `covers-by-key`'s expected operand, and the set operand of `set-membership` or `containment` \u2014 fail compilation under `malformed-operator-expression`, which only stays fireable if the other positions admit it. Compilation enforces the legality stated here; the schema does not narrow it.",
1938
+ "minItems": 1,
1939
+ "items": false
1940
+ },
1941
+ "descriptor": {
1942
+ "type": "object",
1943
+ "properties": {
1944
+ "requiredKeys": {
1945
+ "type": "array",
1946
+ "items": {
1947
+ "type": "string",
1948
+ "minLength": 1
1949
+ }
1950
+ },
1951
+ "permittedKeys": {
1952
+ "type": "array",
1953
+ "items": {
1954
+ "type": "string",
1955
+ "minLength": 1
1956
+ },
1957
+ "description": "The closed set of keys the shape admits. Deliberately not refined to be a superset of `requiredKeys`: no AD-5 code names that contradiction, so it joins the cross-field rules left to the compiler in v0. There is no spelling for \"these keys are required and extras are unconstrained\" \u2014 AD-4 calls the descriptor closed, so an open mode would be a different grammar."
1958
+ },
1959
+ "types": {
1960
+ "type": "object",
1961
+ "propertyNames": {
1962
+ "type": "string",
1963
+ "minLength": 1
1964
+ },
1965
+ "additionalProperties": {
1966
+ "anyOf": [
1967
+ {
1968
+ "type": "string",
1969
+ "enum": [
1970
+ "string",
1971
+ "number",
1972
+ "boolean",
1973
+ "object",
1974
+ "array",
1975
+ "null"
1976
+ ]
1977
+ },
1978
+ {
1979
+ "type": "null"
1980
+ }
1981
+ ]
1982
+ },
1983
+ "description": "Caller-keyed by plain key name, never by pointer: the descriptor-relative pointer spelling does not apply to these keys and must not be extended to them by analogy. A missing key means \"not declared\"; an explicit `null` value means \"declared, type not stated\"."
1984
+ }
1985
+ },
1986
+ "required": [
1987
+ "requiredKeys",
1988
+ "permittedKeys",
1989
+ "types"
1990
+ ],
1991
+ "additionalProperties": false,
1992
+ "description": "AD-4: a closed descriptor, never an embedded JSON Schema. Typing it as a named field is what makes that structural, and it is a deliberate divergence from the Gate C fixture's `{ \"literal\": { \u2026 } }` spelling, where any JSON could sit."
1993
+ }
1994
+ },
1995
+ "required": [
1996
+ "op",
1997
+ "operands",
1998
+ "descriptor"
1999
+ ],
2000
+ "additionalProperties": false
2001
+ },
2002
+ {
2003
+ "type": "object",
2004
+ "properties": {
2005
+ "op": {
2006
+ "type": "string",
2007
+ "const": "covers-by-key"
2008
+ },
2009
+ "operands": {
2010
+ "type": "array",
2011
+ "prefixItems": [
2012
+ {
2013
+ "$ref": "#/$defs/Operand"
2014
+ },
2015
+ {
2016
+ "$ref": "#/$defs/Operand"
2017
+ }
2018
+ ],
2019
+ "description": "Two operands: `expected` is legally a reference set, and `actual` is legally a pointer to a collection. Every position admits the full operand union on purpose. AD-26 makes a reference-set operand outside its three legal positions \u2014 `covers-by-key`'s expected operand, and the set operand of `set-membership` or `containment` \u2014 fail compilation under `malformed-operator-expression`, which only stays fireable if the other positions admit it. Compilation enforces the legality stated here; the schema does not narrow it.",
2020
+ "minItems": 2,
2021
+ "items": false
2022
+ },
2023
+ "expectedKey": {
2024
+ "type": "string",
2025
+ "minLength": 1
2026
+ },
2027
+ "actualKey": {
2028
+ "type": "string",
2029
+ "minLength": 1
2030
+ }
2031
+ },
2032
+ "required": [
2033
+ "op",
2034
+ "operands",
2035
+ "expectedKey",
2036
+ "actualKey"
2037
+ ],
2038
+ "additionalProperties": false
2039
+ },
2040
+ {
2041
+ "type": "object",
2042
+ "properties": {
2043
+ "op": {
2044
+ "type": "string",
2045
+ "const": "not"
2046
+ },
2047
+ "operands": {
2048
+ "type": "array",
2049
+ "prefixItems": [
2050
+ {
2051
+ "$ref": "#/$defs/Expression"
2052
+ }
2053
+ ],
2054
+ "description": "One nested expression rather than an operand. A connective composes the tree; it never addresses evidence itself.",
2055
+ "minItems": 1,
2056
+ "items": false
2057
+ }
2058
+ },
2059
+ "required": [
2060
+ "op",
2061
+ "operands"
2062
+ ],
2063
+ "additionalProperties": false
2064
+ },
2065
+ {
2066
+ "type": "object",
2067
+ "properties": {
2068
+ "op": {
2069
+ "type": "string",
2070
+ "const": "all"
2071
+ },
2072
+ "operands": {
2073
+ "minItems": 2,
2074
+ "type": "array",
2075
+ "items": {
2076
+ "$ref": "#/$defs/Expression"
2077
+ },
2078
+ "description": "Two or more nested expressions rather than operands. The minimum is settled here because AD-4 leaves connective arity unstated: zero operands would certify vacuously and one is the identity."
2079
+ }
2080
+ },
2081
+ "required": [
2082
+ "op",
2083
+ "operands"
2084
+ ],
2085
+ "additionalProperties": false
2086
+ },
2087
+ {
2088
+ "type": "object",
2089
+ "properties": {
2090
+ "op": {
2091
+ "type": "string",
2092
+ "const": "any"
2093
+ },
2094
+ "operands": {
2095
+ "minItems": 2,
2096
+ "type": "array",
2097
+ "items": {
2098
+ "$ref": "#/$defs/Expression"
2099
+ },
2100
+ "description": "Two or more nested expressions rather than operands, for the same reason `all` carries the same minimum."
2101
+ }
2102
+ },
2103
+ "required": [
2104
+ "op",
2105
+ "operands"
2106
+ ],
2107
+ "additionalProperties": false
2108
+ },
2109
+ {
2110
+ "type": "object",
2111
+ "properties": {
2112
+ "op": {
2113
+ "type": "string",
2114
+ "const": "for-all"
2115
+ },
2116
+ "collection": {
2117
+ "$ref": "#/$defs/Operand"
2118
+ },
2119
+ "predicate": {
2120
+ "$ref": "#/$defs/Expression"
2121
+ }
2122
+ },
2123
+ "required": [
2124
+ "op",
2125
+ "collection",
2126
+ "predicate"
2127
+ ],
2128
+ "additionalProperties": false,
2129
+ "description": "`collection` is legally a pointer to a collection, and `predicate` is the expression each bound element is tested against, addressed by the `@/` pointer spelling. A quantifier inside the predicate is structurally admitted so `quantifier-nesting-exceeded` keeps a shape to fire on. Every position admits the full operand union on purpose. AD-26 makes a reference-set operand outside its three legal positions \u2014 `covers-by-key`'s expected operand, and the set operand of `set-membership` or `containment` \u2014 fail compilation under `malformed-operator-expression`, which only stays fireable if the other positions admit it. Compilation enforces the legality stated here; the schema does not narrow it."
2130
+ },
2131
+ {
2132
+ "type": "object",
2133
+ "properties": {
2134
+ "op": {
2135
+ "type": "string",
2136
+ "const": "for-any"
2137
+ },
2138
+ "collection": {
2139
+ "$ref": "#/$defs/Operand"
2140
+ },
2141
+ "predicate": {
2142
+ "$ref": "#/$defs/Expression"
2143
+ }
2144
+ },
2145
+ "required": [
2146
+ "op",
2147
+ "collection",
2148
+ "predicate"
2149
+ ],
2150
+ "additionalProperties": false,
2151
+ "description": "`collection` is legally a pointer to a collection, and `predicate` is the expression each bound element is tested against, addressed by the `@/` pointer spelling. Every position admits the full operand union on purpose. AD-26 makes a reference-set operand outside its three legal positions \u2014 `covers-by-key`'s expected operand, and the set operand of `set-membership` or `containment` \u2014 fail compilation under `malformed-operator-expression`, which only stays fireable if the other positions admit it. Compilation enforces the legality stated here; the schema does not narrow it."
2152
+ }
2153
+ ]
2154
+ },
2155
+ "Operand": {
2156
+ "anyOf": [
2157
+ {
2158
+ "type": "object",
2159
+ "properties": {
2160
+ "pointer": {
2161
+ "anyOf": [
2162
+ {
2163
+ "type": "string",
2164
+ "pattern": "^\\/interactions\\/[a-z0-9]+(?:-[a-z0-9]+)*\\/(?:(?:response-body|response-headers|stdout|stderr)(?:\\/(?:[^/~]|~[01])*)*|(?:response-status|exit-code)|call-inputs\\/(?:path|query|header|body|argument|option|environment|stdin)(?:\\/(?:[^/~]|~[01])*)*|artifact\\/[a-z0-9]+(?:-[a-z0-9]+)*(?:\\/(?:[^/~]|~[01])*)*)$",
2165
+ "description": "AD-26 interaction-rooted pointer: \"/interactions/{stepId}/\" followed by one channel of the closed vocabulary. `call-inputs` takes one input channel as its next segment, one of the four transport channels or one of the four command channels; `artifact` takes the identifier of a file the operation declares it writes; `response-status` and `exit-code` take no tail. Syntax only: whether the step exists and whether the evidence is reachable are compile-time checks, not schema checks."
2166
+ },
2167
+ {
2168
+ "type": "string",
2169
+ "pattern": "^@(?:\\/(?:[^/~]|~[01])*)+$",
2170
+ "description": "AD-26 bound-element pointer: \"@/\" plus an RFC 6901 tail, addressing the element a quantifier binds. Bare \"@/\" addresses the element itself. That it appears only inside a quantifier is a compile-time check, not a schema check."
2171
+ }
2172
+ ]
2173
+ }
2174
+ },
2175
+ "required": [
2176
+ "pointer"
2177
+ ],
2178
+ "additionalProperties": false
2179
+ },
2180
+ {
2181
+ "type": "object",
2182
+ "properties": {
2183
+ "literal": {
2184
+ "$ref": "#/$defs/JsonValue"
2185
+ }
2186
+ },
2187
+ "required": [
2188
+ "literal"
2189
+ ],
2190
+ "additionalProperties": false
2191
+ },
2192
+ {
2193
+ "type": "object",
2194
+ "properties": {
2195
+ "referenceSet": {
2196
+ "type": "string",
2197
+ "pattern": "^[a-z0-9]+(?:-[a-z0-9]+)*$",
2198
+ "description": "A plain identifier. Deliberately not an enum of the contract's declared reference sets and deliberately not refined against them: an identifier the contract does not declare is `unresolved-reference-set`, a coded compile-time error, and a schema that rejected it would convert that code into an anonymous parse fault."
2199
+ }
2200
+ },
2201
+ "required": [
2202
+ "referenceSet"
2203
+ ],
2204
+ "additionalProperties": false
2205
+ }
2206
+ ],
2207
+ "description": "One of the three operand forms, each a single-keyed object. Named so the published schema carries it once as a shared definition rather than inlining it at seventeen use sites. Which forms are legal in which position is declared on each operator's own `operands`, never narrowed here."
2208
+ },
2209
+ "JsonValue": {
2210
+ "description": "AD-36 value domain. Every number is a finite IEEE 754 double-precision value, and every integer lies in the safe-integer range; a larger integer, and any value needing exact decimal semantics such as money, is carried as a string in its own declared format. JSON Schema cannot express finiteness, so this restriction is stated rather than encoded: producers are told the rule here instead of discovering it through a digest mismatch, and the canonical scanner rejects a violating value before any parse.",
2211
+ "anyOf": [
2212
+ {
2213
+ "type": "string"
2214
+ },
2215
+ {
2216
+ "type": "number"
2217
+ },
2218
+ {
2219
+ "type": "boolean"
2220
+ },
2221
+ {
2222
+ "type": "null"
2223
+ },
2224
+ {
2225
+ "type": "array",
2226
+ "items": {
2227
+ "$ref": "#/$defs/JsonValue"
2228
+ }
2229
+ },
2230
+ {
2231
+ "type": "object",
2232
+ "propertyNames": {
2233
+ "type": "string"
2234
+ },
2235
+ "additionalProperties": {
2236
+ "$ref": "#/$defs/JsonValue"
2237
+ }
2238
+ }
2239
+ ]
2240
+ },
2241
+ "RubricBody": {
2242
+ "type": "object",
2243
+ "properties": {
2244
+ "id": {
2245
+ "type": "string",
2246
+ "pattern": "^R-[0-9]{3,}$"
2247
+ },
2248
+ "scaleLevels": {
2249
+ "anyOf": [
2250
+ {
2251
+ "type": "array",
2252
+ "items": {
2253
+ "type": "object",
2254
+ "properties": {
2255
+ "level": {
2256
+ "type": "integer",
2257
+ "minimum": -9007199254740991,
2258
+ "maximum": 9007199254740991,
2259
+ "description": "The ordinal this level sits at. A repeated ordinal is the one half the compiler closes, failing under `rubric-unanchored`, since two levels at one ordinal make the ordinal address two anchors. Magnitude, sign, ordering, and contiguity stay free everywhere, because a scale running -2 to 2 or 1, 3, 5 is an ordinary authoring choice."
2260
+ },
2261
+ "anchor": {
2262
+ "type": "string",
2263
+ "description": "The observable condition that anchors this level. AD-22 calls an unanchored scale a compile-time failure under `rubric-unanchored`, so the anchor is a declared field rather than a convention."
2264
+ }
2265
+ },
2266
+ "required": [
2267
+ "level",
2268
+ "anchor"
2269
+ ],
2270
+ "additionalProperties": false
2271
+ }
2272
+ },
2273
+ {
2274
+ "type": "null"
2275
+ }
2276
+ ],
2277
+ "description": "`null` and `[]` are both the unanchored shape `rubric-unanchored` fires on, and both must parse."
2278
+ },
2279
+ "failureModePenalties": {
2280
+ "anyOf": [
2281
+ {
2282
+ "type": "array",
2283
+ "items": {
2284
+ "type": "object",
2285
+ "properties": {
2286
+ "name": {
2287
+ "type": "string",
2288
+ "minLength": 1
2289
+ },
2290
+ "description": {
2291
+ "type": "string",
2292
+ "description": "AD-22 requires named failure-mode penalties and states no magnitude. None is invented here: a penalty weight would be a scoring semantic no module below `score` has authority to mint, and AD-7 keeps weighted composites out of the reported result. AD-22's compile checks ship without one, so a penalty carries a name and a description and nothing else."
2293
+ }
2294
+ },
2295
+ "required": [
2296
+ "name",
2297
+ "description"
2298
+ ],
2299
+ "additionalProperties": false
2300
+ }
2301
+ },
2302
+ {
2303
+ "type": "null"
2304
+ }
2305
+ ],
2306
+ "description": "Missing named penalties is `rubric-unanchored`, not a parse failure."
2307
+ },
2308
+ "maxLength": {
2309
+ "anyOf": [
2310
+ {
2311
+ "type": "integer",
2312
+ "minimum": 1,
2313
+ "maximum": 9007199254740991
2314
+ },
2315
+ {
2316
+ "type": "null"
2317
+ }
2318
+ ],
2319
+ "description": "An unbounded length is `null`, which is one of the shapes `rubric-unanchored` fires on."
2320
+ },
2321
+ "criteria": {
2322
+ "type": "array",
2323
+ "items": {
2324
+ "type": "object",
2325
+ "properties": {
2326
+ "id": {
2327
+ "type": "string",
2328
+ "pattern": "^RC-[0-9]{3,}$"
2329
+ },
2330
+ "text": {
2331
+ "type": "string",
2332
+ "description": "A criterion whose wording matches the compiler's closed stated-reasoning vocabulary fails compilation under `rubric-scores-reasoning-prose`, so such a criterion must stay representable here. A blank text parses for the same reason and fails under `rubric-unanchored`."
2333
+ },
2334
+ "evidence": {
2335
+ "type": "string",
2336
+ "pattern": "^\\/interactions\\/[a-z0-9]+(?:-[a-z0-9]+)*\\/(?:(?:response-body|response-headers|stdout|stderr)(?:\\/(?:[^/~]|~[01])*)*|(?:response-status|exit-code)|call-inputs\\/(?:path|query|header|body|argument|option|environment|stdin)(?:\\/(?:[^/~]|~[01])*)*|artifact\\/[a-z0-9]+(?:-[a-z0-9]+)*(?:\\/(?:[^/~]|~[01])*)*)$",
2337
+ "description": "Where the criterion is answerable from. A pointer that resolves nowhere is `rubric-evidence-unreachable`, a compile-time code, and is deliberately not a schema rejection."
2338
+ }
2339
+ },
2340
+ "required": [
2341
+ "id",
2342
+ "text",
2343
+ "evidence"
2344
+ ],
2345
+ "additionalProperties": false
2346
+ }
2347
+ }
2348
+ },
2349
+ "required": [
2350
+ "id",
2351
+ "scaleLevels",
2352
+ "failureModePenalties",
2353
+ "maxLength",
2354
+ "criteria"
2355
+ ],
2356
+ "additionalProperties": false,
2357
+ "description": "The embeddable rubric body. Named so the shared body has a stable `$defs` key distinct from the published `Rubric` artifact, which is this body plus `schemaVersion` and AD-29 lineage; without the name the two collide in the published-schema drift check under a generated positional name."
2358
+ },
2359
+ "Operation": {
2360
+ "type": "object",
2361
+ "properties": {
2362
+ "operationId": {
2363
+ "type": "string",
2364
+ "pattern": "^[a-z0-9]+(?:-[a-z0-9]+)*$",
2365
+ "description": "A kebab-case slug. Excludes \"/\" and \"~\" so an identifier can be embedded in an interaction-rooted pointer without escaping."
2366
+ },
2367
+ "method": {
2368
+ "type": "string",
2369
+ "enum": [
2370
+ "GET",
2371
+ "HEAD",
2372
+ "POST",
2373
+ "PUT",
2374
+ "PATCH",
2375
+ "DELETE",
2376
+ "OPTIONS"
2377
+ ]
2378
+ },
2379
+ "pathTemplate": {
2380
+ "type": "string",
2381
+ "pattern": "^(?:\\/(?:[^/{}:]|\\{[A-Za-z0-9_-]+\\})*)+$",
2382
+ "description": "A path template whose parameters are spelled `{name}` in braces. The `:name` spelling is rejected: AD-40 resolves a defect signature by comparing method and path template, and that comparison is not implementable against an unstated syntax."
2383
+ },
2384
+ "stateChangeMarker": {
2385
+ "type": "boolean",
2386
+ "description": "AD-19: whether the operation is intended to change state. AD-20 rule 7 relevance reads it, and AD-10 selects the sensitivity channel by it. Both values are legal and neither is a default."
2387
+ },
2388
+ "requestShape": {
2389
+ "type": "object",
2390
+ "properties": {
2391
+ "path": {
2392
+ "type": "object",
2393
+ "properties": {
2394
+ "requiredKeys": {
2395
+ "type": "array",
2396
+ "items": {
2397
+ "type": "string",
2398
+ "minLength": 1
2399
+ }
2400
+ },
2401
+ "permittedKeys": {
2402
+ "type": "array",
2403
+ "items": {
2404
+ "type": "string",
2405
+ "minLength": 1
2406
+ },
2407
+ "description": "The closed set of keys the shape admits. Deliberately not refined to be a superset of `requiredKeys`: no AD-5 code names that contradiction, so it joins the cross-field rules left to the compiler in v0. There is no spelling for \"these keys are required and extras are unconstrained\" \u2014 AD-4 calls the descriptor closed, so an open mode would be a different grammar."
2408
+ },
2409
+ "types": {
2410
+ "type": "object",
2411
+ "propertyNames": {
2412
+ "type": "string",
2413
+ "minLength": 1
2414
+ },
2415
+ "additionalProperties": {
2416
+ "anyOf": [
2417
+ {
2418
+ "type": "string",
2419
+ "enum": [
2420
+ "string",
2421
+ "number",
2422
+ "boolean",
2423
+ "object",
2424
+ "array",
2425
+ "null"
2426
+ ]
2427
+ },
2428
+ {
2429
+ "type": "null"
2430
+ }
2431
+ ]
2432
+ },
2433
+ "description": "Caller-keyed by plain key name, never by pointer: the descriptor-relative pointer spelling does not apply to these keys and must not be extended to them by analogy. A missing key means \"not declared\"; an explicit `null` value means \"declared, type not stated\"."
2434
+ }
2435
+ },
2436
+ "required": [
2437
+ "requiredKeys",
2438
+ "permittedKeys",
2439
+ "types"
2440
+ ],
2441
+ "additionalProperties": false
2442
+ },
2443
+ "query": {
2444
+ "type": "object",
2445
+ "properties": {
2446
+ "requiredKeys": {
2447
+ "type": "array",
2448
+ "items": {
2449
+ "type": "string",
2450
+ "minLength": 1
2451
+ }
2452
+ },
2453
+ "permittedKeys": {
2454
+ "type": "array",
2455
+ "items": {
2456
+ "type": "string",
2457
+ "minLength": 1
2458
+ },
2459
+ "description": "The closed set of keys the shape admits. Deliberately not refined to be a superset of `requiredKeys`: no AD-5 code names that contradiction, so it joins the cross-field rules left to the compiler in v0. There is no spelling for \"these keys are required and extras are unconstrained\" \u2014 AD-4 calls the descriptor closed, so an open mode would be a different grammar."
2460
+ },
2461
+ "types": {
2462
+ "type": "object",
2463
+ "propertyNames": {
2464
+ "type": "string",
2465
+ "minLength": 1
2466
+ },
2467
+ "additionalProperties": {
2468
+ "anyOf": [
2469
+ {
2470
+ "type": "string",
2471
+ "enum": [
2472
+ "string",
2473
+ "number",
2474
+ "boolean",
2475
+ "object",
2476
+ "array",
2477
+ "null"
2478
+ ]
2479
+ },
2480
+ {
2481
+ "type": "null"
2482
+ }
2483
+ ]
2484
+ },
2485
+ "description": "Caller-keyed by plain key name, never by pointer: the descriptor-relative pointer spelling does not apply to these keys and must not be extended to them by analogy. A missing key means \"not declared\"; an explicit `null` value means \"declared, type not stated\"."
2486
+ }
2487
+ },
2488
+ "required": [
2489
+ "requiredKeys",
2490
+ "permittedKeys",
2491
+ "types"
2492
+ ],
2493
+ "additionalProperties": false
2494
+ },
2495
+ "header": {
2496
+ "type": "object",
2497
+ "properties": {
2498
+ "requiredKeys": {
2499
+ "type": "array",
2500
+ "items": {
2501
+ "type": "string",
2502
+ "minLength": 1
2503
+ }
2504
+ },
2505
+ "permittedKeys": {
2506
+ "type": "array",
2507
+ "items": {
2508
+ "type": "string",
2509
+ "minLength": 1
2510
+ },
2511
+ "description": "The closed set of keys the shape admits. Deliberately not refined to be a superset of `requiredKeys`: no AD-5 code names that contradiction, so it joins the cross-field rules left to the compiler in v0. There is no spelling for \"these keys are required and extras are unconstrained\" \u2014 AD-4 calls the descriptor closed, so an open mode would be a different grammar."
2512
+ },
2513
+ "types": {
2514
+ "type": "object",
2515
+ "propertyNames": {
2516
+ "type": "string",
2517
+ "minLength": 1
2518
+ },
2519
+ "additionalProperties": {
2520
+ "anyOf": [
2521
+ {
2522
+ "type": "string",
2523
+ "enum": [
2524
+ "string",
2525
+ "number",
2526
+ "boolean",
2527
+ "object",
2528
+ "array",
2529
+ "null"
2530
+ ]
2531
+ },
2532
+ {
2533
+ "type": "null"
2534
+ }
2535
+ ]
2536
+ },
2537
+ "description": "Caller-keyed by plain key name, never by pointer: the descriptor-relative pointer spelling does not apply to these keys and must not be extended to them by analogy. A missing key means \"not declared\"; an explicit `null` value means \"declared, type not stated\"."
2538
+ }
2539
+ },
2540
+ "required": [
2541
+ "requiredKeys",
2542
+ "permittedKeys",
2543
+ "types"
2544
+ ],
2545
+ "additionalProperties": false,
2546
+ "description": "AD-18: a header channel declaration names a header and its type and never carries a credential value."
1602
2547
  },
1603
- "descriptor": {
2548
+ "body": {
1604
2549
  "type": "object",
1605
2550
  "properties": {
1606
2551
  "requiredKeys": {
@@ -1650,373 +2595,390 @@
1650
2595
  "permittedKeys",
1651
2596
  "types"
1652
2597
  ],
1653
- "additionalProperties": false,
1654
- "description": "AD-4: a closed descriptor, never an embedded JSON Schema. Typing it as a named field is what makes that structural, and it is a deliberate divergence from the Gate C fixture's `{ \"literal\": { \u2026 } }` spelling, where any JSON could sit."
2598
+ "additionalProperties": false
1655
2599
  }
1656
2600
  },
1657
2601
  "required": [
1658
- "op",
1659
- "operands",
1660
- "descriptor"
2602
+ "path",
2603
+ "query",
2604
+ "header",
2605
+ "body"
1661
2606
  ],
1662
2607
  "additionalProperties": false
1663
2608
  },
1664
- {
2609
+ "responseDescriptor": {
1665
2610
  "type": "object",
1666
2611
  "properties": {
1667
- "op": {
1668
- "type": "string",
1669
- "const": "covers-by-key"
2612
+ "requiredKeys": {
2613
+ "type": "array",
2614
+ "items": {
2615
+ "type": "string",
2616
+ "minLength": 1
2617
+ },
2618
+ "description": "No minimum of two anywhere in this shape: AD-20 rule 2 relevance is \"the descriptor declares more than one pointer\", so a one-pointer descriptor must parse in order to be the irrelevant case."
1670
2619
  },
1671
- "operands": {
2620
+ "permittedKeys": {
1672
2621
  "type": "array",
1673
- "prefixItems": [
2622
+ "items": {
2623
+ "type": "string",
2624
+ "minLength": 1
2625
+ }
2626
+ },
2627
+ "types": {
2628
+ "type": "object",
2629
+ "propertyNames": {
2630
+ "type": "string",
2631
+ "minLength": 1
2632
+ },
2633
+ "additionalProperties": {
2634
+ "anyOf": [
2635
+ {
2636
+ "type": "string",
2637
+ "enum": [
2638
+ "string",
2639
+ "number",
2640
+ "boolean",
2641
+ "object",
2642
+ "array",
2643
+ "null"
2644
+ ]
2645
+ },
2646
+ {
2647
+ "type": "null"
2648
+ }
2649
+ ]
2650
+ },
2651
+ "description": "Caller-keyed by plain key name, never by pointer: this is the shape where that trap bites, since `requiredKeys` sits beside a pointer-keyed `channelRoles` and a pointer-valued `successIndicator`, and the descriptor-relative spelling must not be extended here by analogy. A missing key means \"not declared\"; an explicit `null` value means \"declared, type not stated\", which is AD-31's enumerated indeterminate descriptor state and the shape `quantifier-over-non-collection` reads."
2652
+ },
2653
+ "successIndicator": {
2654
+ "anyOf": [
1674
2655
  {
1675
- "$ref": "#/$defs/Operand"
2656
+ "type": "string",
2657
+ "pattern": "^(?:\\/(?:[^/~]|~[01])*)*$",
2658
+ "description": "A plain RFC 6901 pointer into one operation's response descriptor. It resolves through the operation an interaction step names, never through the interaction root. A request or response shape's descriptor keys are plain key names rather than pointers, so this spelling does not apply there. The empty string is admitted and carries RFC 6901's own meaning, the whole document: as a nominated success indicator it says success is visible in the response taken as a whole rather than at any one key, and as a channel-role key it assigns a role to the whole body."
1676
2659
  },
1677
2660
  {
1678
- "$ref": "#/$defs/Operand"
2661
+ "type": "null"
1679
2662
  }
1680
2663
  ],
1681
- "description": "Two operands: `expected` is legally a reference set, and `actual` is legally a pointer to a collection. Every position admits the full operand union on purpose. AD-26 makes a reference-set operand outside its three legal positions \u2014 `covers-by-key`'s expected operand, and the set operand of `set-membership` or `containment` \u2014 fail compilation under `malformed-operator-expression`, which only stays fireable if the other positions admit it. Compilation enforces the legality stated here; the schema does not narrow it.",
1682
- "minItems": 2,
1683
- "items": false
1684
- },
1685
- "expectedKey": {
1686
- "type": "string",
1687
- "minLength": 1
1688
- },
1689
- "actualKey": {
1690
- "type": "string",
1691
- "minLength": 1
1692
- }
1693
- },
1694
- "required": [
1695
- "op",
1696
- "operands",
1697
- "expectedKey",
1698
- "actualKey"
1699
- ],
1700
- "additionalProperties": false
1701
- },
1702
- {
1703
- "type": "object",
1704
- "properties": {
1705
- "op": {
1706
- "type": "string",
1707
- "const": "not"
2664
+ "description": "AD-20 rule 1 relevance reads this as its first conjunct, so `null` must stay representable."
1708
2665
  },
1709
- "operands": {
1710
- "type": "array",
1711
- "prefixItems": [
2666
+ "channelRoles": {
2667
+ "anyOf": [
1712
2668
  {
1713
- "$ref": "#/$defs/Expression"
2669
+ "type": "object",
2670
+ "propertyNames": {
2671
+ "type": "string",
2672
+ "pattern": "^(?:\\/(?:[^/~]|~[01])*)*$",
2673
+ "description": "A plain RFC 6901 pointer into one operation's response descriptor. It resolves through the operation an interaction step names, never through the interaction root. A request or response shape's descriptor keys are plain key names rather than pointers, so this spelling does not apply there. The empty string is admitted and carries RFC 6901's own meaning, the whole document: as a nominated success indicator it says success is visible in the response taken as a whole rather than at any one key, and as a channel-role key it assigns a role to the whole body."
2674
+ },
2675
+ "additionalProperties": {
2676
+ "type": "string",
2677
+ "enum": [
2678
+ "success-indicator",
2679
+ "diagnostic",
2680
+ "payload",
2681
+ "collection"
2682
+ ]
2683
+ }
2684
+ },
2685
+ {
2686
+ "type": "null"
1714
2687
  }
1715
2688
  ],
1716
- "description": "One nested expression rather than an operand. A connective composes the tree; it never addresses evidence itself.",
1717
- "minItems": 1,
1718
- "items": false
1719
- }
1720
- },
1721
- "required": [
1722
- "op",
1723
- "operands"
1724
- ],
1725
- "additionalProperties": false
1726
- },
1727
- {
1728
- "type": "object",
1729
- "properties": {
1730
- "op": {
1731
- "type": "string",
1732
- "const": "all"
1733
- },
1734
- "operands": {
1735
- "minItems": 2,
1736
- "type": "array",
1737
- "items": {
1738
- "$ref": "#/$defs/Expression"
1739
- },
1740
- "description": "Two or more nested expressions rather than operands. The minimum is settled here because AD-4 leaves connective arity unstated: zero operands would certify vacuously and one is the identity."
1741
- }
1742
- },
1743
- "required": [
1744
- "op",
1745
- "operands"
1746
- ],
1747
- "additionalProperties": false
1748
- },
1749
- {
1750
- "type": "object",
1751
- "properties": {
1752
- "op": {
1753
- "type": "string",
1754
- "const": "any"
1755
- },
1756
- "operands": {
1757
- "minItems": 2,
1758
- "type": "array",
1759
- "items": {
1760
- "$ref": "#/$defs/Expression"
1761
- },
1762
- "description": "Two or more nested expressions rather than operands, for the same reason `all` carries the same minimum."
1763
- }
1764
- },
1765
- "required": [
1766
- "op",
1767
- "operands"
1768
- ],
1769
- "additionalProperties": false
1770
- },
1771
- {
1772
- "type": "object",
1773
- "properties": {
1774
- "op": {
1775
- "type": "string",
1776
- "const": "for-all"
1777
- },
1778
- "collection": {
1779
- "$ref": "#/$defs/Operand"
2689
+ "description": "Caller-keyed, and expected to be partial: a missing key means \"no role declared for that pointer\". AD-31 grades absent and explicitly empty differently, so `null`, `{}`, and a populated map are three distinct answers. Deliberately not refined to require a role for every descriptor key: no AD-5 code names that rule, the AD-31 predicates degrade gracefully on partial roles, and a refinement buys nothing an export can carry."
1780
2690
  },
1781
- "predicate": {
1782
- "$ref": "#/$defs/Expression"
1783
- }
1784
- },
1785
- "required": [
1786
- "op",
1787
- "collection",
1788
- "predicate"
1789
- ],
1790
- "additionalProperties": false,
1791
- "description": "`collection` is legally a pointer to a collection, and `predicate` is the expression each bound element is tested against, addressed by the `@/` pointer spelling. A quantifier inside the predicate is structurally admitted so `quantifier-nesting-exceeded` keeps a shape to fire on. Every position admits the full operand union on purpose. AD-26 makes a reference-set operand outside its three legal positions \u2014 `covers-by-key`'s expected operand, and the set operand of `set-membership` or `containment` \u2014 fail compilation under `malformed-operator-expression`, which only stays fireable if the other positions admit it. Compilation enforces the legality stated here; the schema does not narrow it."
1792
- },
1793
- {
1794
- "type": "object",
1795
- "properties": {
1796
- "op": {
1797
- "type": "string",
1798
- "const": "for-any"
1799
- },
1800
- "collection": {
1801
- "$ref": "#/$defs/Operand"
1802
- },
1803
- "predicate": {
1804
- "$ref": "#/$defs/Expression"
1805
- }
1806
- },
1807
- "required": [
1808
- "op",
1809
- "collection",
1810
- "predicate"
1811
- ],
1812
- "additionalProperties": false,
1813
- "description": "`collection` is legally a pointer to a collection, and `predicate` is the expression each bound element is tested against, addressed by the `@/` pointer spelling. Every position admits the full operand union on purpose. AD-26 makes a reference-set operand outside its three legal positions \u2014 `covers-by-key`'s expected operand, and the set operand of `set-membership` or `containment` \u2014 fail compilation under `malformed-operator-expression`, which only stays fireable if the other positions admit it. Compilation enforces the legality stated here; the schema does not narrow it."
1814
- }
1815
- ]
1816
- },
1817
- "Operand": {
1818
- "anyOf": [
1819
- {
1820
- "type": "object",
1821
- "properties": {
1822
- "pointer": {
2691
+ "collectionLocations": {
1823
2692
  "anyOf": [
1824
2693
  {
1825
- "type": "string",
1826
- "pattern": "^\\/interactions\\/[a-z0-9]+(?:-[a-z0-9]+)*\\/(?:(?:response-body|response-headers|stdout|stderr)(?:\\/(?:[^/~]|~[01])*)*|(?:response-status|exit-code)|call-inputs\\/(?:path|query|header|body)(?:\\/(?:[^/~]|~[01])*)*)$",
1827
- "description": "AD-26 interaction-rooted pointer: \"/interactions/{stepId}/\" followed by one channel of the closed vocabulary. `call-inputs` takes one of the four transport channels as its next segment; `response-status` and `exit-code` take no tail. Syntax only: whether the step exists and whether the evidence is reachable are compile-time checks, not schema checks."
2694
+ "type": "array",
2695
+ "items": {
2696
+ "type": "object",
2697
+ "properties": {
2698
+ "pointer": {
2699
+ "type": "string",
2700
+ "pattern": "^(?:\\/(?:[^/~]|~[01])*)*$",
2701
+ "description": "A plain RFC 6901 pointer into one operation's response descriptor. It resolves through the operation an interaction step names, never through the interaction root. A request or response shape's descriptor keys are plain key names rather than pointers, so this spelling does not apply there. The empty string is admitted and carries RFC 6901's own meaning, the whole document: as a nominated success indicator it says success is visible in the response taken as a whole rather than at any one key, and as a channel-role key it assigns a role to the whole body."
2702
+ },
2703
+ "expectedCardinality": {
2704
+ "oneOf": [
2705
+ {
2706
+ "type": "object",
2707
+ "properties": {
2708
+ "mode": {
2709
+ "type": "string",
2710
+ "const": "exact"
2711
+ },
2712
+ "count": {
2713
+ "type": "integer",
2714
+ "minimum": 0,
2715
+ "maximum": 9007199254740991
2716
+ }
2717
+ },
2718
+ "required": [
2719
+ "mode",
2720
+ "count"
2721
+ ],
2722
+ "additionalProperties": false
2723
+ },
2724
+ {
2725
+ "type": "object",
2726
+ "properties": {
2727
+ "mode": {
2728
+ "type": "string",
2729
+ "const": "at-most"
2730
+ },
2731
+ "max": {
2732
+ "type": "integer",
2733
+ "minimum": 0,
2734
+ "maximum": 9007199254740991
2735
+ }
2736
+ },
2737
+ "required": [
2738
+ "mode",
2739
+ "max"
2740
+ ],
2741
+ "additionalProperties": false
2742
+ },
2743
+ {
2744
+ "type": "object",
2745
+ "properties": {
2746
+ "mode": {
2747
+ "type": "string",
2748
+ "const": "page-bounded"
2749
+ },
2750
+ "max": {
2751
+ "type": "integer",
2752
+ "minimum": 0,
2753
+ "maximum": 9007199254740991
2754
+ }
2755
+ },
2756
+ "required": [
2757
+ "mode",
2758
+ "max"
2759
+ ],
2760
+ "additionalProperties": false
2761
+ }
2762
+ ]
2763
+ },
2764
+ "referenceSet": {
2765
+ "anyOf": [
2766
+ {
2767
+ "type": "string",
2768
+ "pattern": "^[a-z0-9]+(?:-[a-z0-9]+)*$",
2769
+ "description": "A kebab-case slug. Excludes \"/\" and \"~\" so an identifier can be embedded in an interaction-rooted pointer without escaping."
2770
+ },
2771
+ {
2772
+ "type": "null"
2773
+ }
2774
+ ],
2775
+ "description": "AD-20 rule 6 is relevant when a declared collection location names a reference set, so `null` is the shape that makes the rule irrelevant and must stay representable."
2776
+ }
2777
+ },
2778
+ "required": [
2779
+ "pointer",
2780
+ "expectedCardinality",
2781
+ "referenceSet"
2782
+ ],
2783
+ "additionalProperties": false
2784
+ }
1828
2785
  },
1829
2786
  {
1830
- "type": "string",
1831
- "pattern": "^@(?:\\/(?:[^/~]|~[01])*)+$",
1832
- "description": "AD-26 bound-element pointer: \"@/\" plus an RFC 6901 tail, addressing the element a quantifier binds. Bare \"@/\" addresses the element itself. That it appears only inside a quantifier is a compile-time check, not a schema check."
2787
+ "type": "null"
1833
2788
  }
1834
- ]
1835
- }
1836
- },
1837
- "required": [
1838
- "pointer"
1839
- ],
1840
- "additionalProperties": false
1841
- },
1842
- {
1843
- "type": "object",
1844
- "properties": {
1845
- "literal": {
1846
- "$ref": "#/$defs/JsonValue"
1847
- }
1848
- },
1849
- "required": [
1850
- "literal"
1851
- ],
1852
- "additionalProperties": false
1853
- },
1854
- {
1855
- "type": "object",
1856
- "properties": {
1857
- "referenceSet": {
1858
- "type": "string",
1859
- "pattern": "^[a-z0-9]+(?:-[a-z0-9]+)*$",
1860
- "description": "A plain identifier. Deliberately not an enum of the contract's declared reference sets and deliberately not refined against them: an identifier the contract does not declare is `unresolved-reference-set`, a coded compile-time error, and a schema that rejected it would convert that code into an anonymous parse fault."
2789
+ ],
2790
+ "description": "AD-20 rule 4 relevance reads this, and AD-31 grades an absent declaration and an explicit empty one differently, so `null` and `[]` are distinct answers."
1861
2791
  }
1862
2792
  },
1863
2793
  "required": [
1864
- "referenceSet"
2794
+ "requiredKeys",
2795
+ "permittedKeys",
2796
+ "types",
2797
+ "successIndicator",
2798
+ "channelRoles",
2799
+ "collectionLocations"
1865
2800
  ],
1866
2801
  "additionalProperties": false
1867
- }
1868
- ],
1869
- "description": "One of the three operand forms, each a single-keyed object. Named so the published schema carries it once as a shared definition rather than inlining it at seventeen use sites. Which forms are legal in which position is declared on each operator's own `operands`, never narrowed here."
1870
- },
1871
- "JsonValue": {
1872
- "description": "AD-36 value domain. Every number is a finite IEEE 754 double-precision value, and every integer lies in the safe-integer range; a larger integer, and any value needing exact decimal semantics such as money, is carried as a string in its own declared format. JSON Schema cannot express finiteness, so this restriction is stated rather than encoded: producers are told the rule here instead of discovering it through a digest mismatch, and the canonical scanner rejects a violating value before any parse.",
1873
- "anyOf": [
1874
- {
1875
- "type": "string"
1876
- },
1877
- {
1878
- "type": "number"
1879
- },
1880
- {
1881
- "type": "boolean"
1882
2802
  },
1883
- {
1884
- "type": "null"
1885
- },
1886
- {
2803
+ "volatilePointers": {
1887
2804
  "type": "array",
1888
2805
  "items": {
1889
- "$ref": "#/$defs/JsonValue"
1890
- }
1891
- },
1892
- {
1893
- "type": "object",
1894
- "propertyNames": {
1895
- "type": "string"
1896
- },
1897
- "additionalProperties": {
1898
- "$ref": "#/$defs/JsonValue"
2806
+ "type": "string",
2807
+ "pattern": "^(?:\\/(?:[^/~]|~[01])*)*$",
2808
+ "description": "A plain RFC 6901 pointer into one operation's response descriptor. It resolves through the operation an interaction step names, never through the interaction root. A request or response shape's descriptor keys are plain key names rather than pointers, so this spelling does not apply there. The empty string is admitted and carries RFC 6901's own meaning, the whole document: as a nominated success indicator it says success is visible in the response taken as a whole rather than at any one key, and as a channel-role key it assigns a role to the whole body."
1899
2809
  }
1900
- }
1901
- ]
1902
- },
1903
- "RubricBody": {
1904
- "type": "object",
1905
- "properties": {
1906
- "id": {
1907
- "type": "string",
1908
- "pattern": "^R-[0-9]{3,}$"
1909
2810
  },
1910
- "scaleLevels": {
2811
+ "sensitivityWitness": {
1911
2812
  "anyOf": [
1912
2813
  {
1913
- "type": "array",
1914
- "items": {
1915
- "type": "object",
1916
- "properties": {
1917
- "level": {
1918
- "type": "integer",
1919
- "minimum": -9007199254740991,
1920
- "maximum": 9007199254740991,
1921
- "description": "The ordinal this level sits at. A repeated ordinal is the one half the compiler closes, failing under `rubric-unanchored`, since two levels at one ordinal make the ordinal address two anchors. Magnitude, sign, ordering, and contiguity stay free everywhere, because a scale running -2 to 2 or 1, 3, 5 is an ordinary authoring choice."
1922
- },
1923
- "anchor": {
1924
- "type": "string",
1925
- "description": "The observable condition that anchors this level. AD-22 calls an unanchored scale a compile-time failure under `rubric-unanchored`, so the anchor is a declared field rather than a convention."
1926
- }
2814
+ "type": "object",
2815
+ "properties": {
2816
+ "witnessId": {
2817
+ "type": "string",
2818
+ "pattern": "^[a-z0-9]+(?:-[a-z0-9]+)*$",
2819
+ "description": "A kebab-case slug. Excludes \"/\" and \"~\" so an identifier can be embedded in an interaction-rooted pointer without escaping."
1927
2820
  },
1928
- "required": [
1929
- "level",
1930
- "anchor"
1931
- ],
1932
- "additionalProperties": false
1933
- }
1934
- },
1935
- {
1936
- "type": "null"
1937
- }
1938
- ],
1939
- "description": "`null` and `[]` are both the unanchored shape `rubric-unanchored` fires on, and both must parse."
1940
- },
1941
- "failureModePenalties": {
1942
- "anyOf": [
1943
- {
1944
- "type": "array",
1945
- "items": {
1946
- "type": "object",
1947
- "properties": {
1948
- "name": {
1949
- "type": "string",
1950
- "minLength": 1
1951
- },
1952
- "description": {
1953
- "type": "string",
1954
- "description": "AD-22 requires named failure-mode penalties and states no magnitude. None is invented here: a penalty weight would be a scoring semantic no module below `score` has authority to mint, and AD-7 keeps weighted composites out of the reported result. AD-22's compile checks ship without one, so a penalty carries a name and a description and nothing else."
2821
+ "channel": {
2822
+ "type": "string",
2823
+ "enum": [
2824
+ "path",
2825
+ "query",
2826
+ "body",
2827
+ "argument",
2828
+ "option",
2829
+ "environment",
2830
+ "stdin"
2831
+ ]
2832
+ },
2833
+ "legs": {
2834
+ "minItems": 2,
2835
+ "maxItems": 2,
2836
+ "type": "array",
2837
+ "items": {
2838
+ "type": "object",
2839
+ "properties": {
2840
+ "legId": {
2841
+ "type": "string",
2842
+ "pattern": "^[a-z0-9]+(?:-[a-z0-9]+)*$",
2843
+ "description": "A kebab-case slug. Excludes \"/\" and \"~\" so an identifier can be embedded in an interaction-rooted pointer without escaping."
2844
+ },
2845
+ "inputs": {
2846
+ "anyOf": [
2847
+ {
2848
+ "$ref": "#/$defs/WitnessInputs"
2849
+ },
2850
+ {
2851
+ "type": "object",
2852
+ "properties": {
2853
+ "argument": {
2854
+ "type": "object",
2855
+ "propertyNames": {
2856
+ "type": "string"
2857
+ },
2858
+ "additionalProperties": {
2859
+ "$ref": "#/$defs/JsonValue"
2860
+ }
2861
+ },
2862
+ "option": {
2863
+ "type": "object",
2864
+ "propertyNames": {
2865
+ "type": "string"
2866
+ },
2867
+ "additionalProperties": {
2868
+ "$ref": "#/$defs/JsonValue"
2869
+ }
2870
+ },
2871
+ "environment": {
2872
+ "type": "object",
2873
+ "propertyNames": {
2874
+ "type": "string",
2875
+ "minLength": 1
2876
+ },
2877
+ "additionalProperties": {
2878
+ "type": "string"
2879
+ }
2880
+ },
2881
+ "stdin": {
2882
+ "oneOf": [
2883
+ {
2884
+ "type": "object",
2885
+ "properties": {
2886
+ "kind": {
2887
+ "type": "string",
2888
+ "const": "json"
2889
+ },
2890
+ "value": {
2891
+ "$ref": "#/$defs/JsonValue"
2892
+ }
2893
+ },
2894
+ "required": [
2895
+ "kind",
2896
+ "value"
2897
+ ],
2898
+ "additionalProperties": false
2899
+ },
2900
+ {
2901
+ "type": "object",
2902
+ "properties": {
2903
+ "kind": {
2904
+ "type": "string",
2905
+ "const": "text"
2906
+ },
2907
+ "value": {
2908
+ "type": "string"
2909
+ }
2910
+ },
2911
+ "required": [
2912
+ "kind",
2913
+ "value"
2914
+ ],
2915
+ "additionalProperties": false
2916
+ },
2917
+ {
2918
+ "type": "object",
2919
+ "properties": {
2920
+ "kind": {
2921
+ "type": "string",
2922
+ "const": "absent"
2923
+ }
2924
+ },
2925
+ "required": [
2926
+ "kind"
2927
+ ],
2928
+ "additionalProperties": false
2929
+ }
2930
+ ]
2931
+ }
2932
+ },
2933
+ "required": [
2934
+ "argument",
2935
+ "option",
2936
+ "environment",
2937
+ "stdin"
2938
+ ],
2939
+ "additionalProperties": false
2940
+ }
2941
+ ]
2942
+ }
2943
+ },
2944
+ "required": [
2945
+ "legId",
2946
+ "inputs"
2947
+ ],
2948
+ "additionalProperties": false
1955
2949
  }
1956
2950
  },
1957
- "required": [
1958
- "name",
1959
- "description"
1960
- ],
1961
- "additionalProperties": false
1962
- }
1963
- },
1964
- {
1965
- "type": "null"
1966
- }
1967
- ],
1968
- "description": "Missing named penalties is `rubric-unanchored`, not a parse failure."
1969
- },
1970
- "maxLength": {
1971
- "anyOf": [
1972
- {
1973
- "type": "integer",
1974
- "minimum": 1,
1975
- "maximum": 9007199254740991
2951
+ "relation": {
2952
+ "$ref": "#/$defs/Expression"
2953
+ }
2954
+ },
2955
+ "required": [
2956
+ "witnessId",
2957
+ "channel",
2958
+ "legs",
2959
+ "relation"
2960
+ ],
2961
+ "additionalProperties": false
1976
2962
  },
1977
2963
  {
1978
2964
  "type": "null"
1979
2965
  }
1980
2966
  ],
1981
- "description": "An unbounded length is `null`, which is one of the shapes `rubric-unanchored` fires on."
1982
- },
1983
- "criteria": {
1984
- "type": "array",
1985
- "items": {
1986
- "type": "object",
1987
- "properties": {
1988
- "id": {
1989
- "type": "string",
1990
- "pattern": "^RC-[0-9]{3,}$"
1991
- },
1992
- "text": {
1993
- "type": "string",
1994
- "description": "A criterion whose wording matches the compiler's closed stated-reasoning vocabulary fails compilation under `rubric-scores-reasoning-prose`, so such a criterion must stay representable here. A blank text parses for the same reason and fails under `rubric-unanchored`."
1995
- },
1996
- "evidence": {
1997
- "type": "string",
1998
- "pattern": "^\\/interactions\\/[a-z0-9]+(?:-[a-z0-9]+)*\\/(?:(?:response-body|response-headers|stdout|stderr)(?:\\/(?:[^/~]|~[01])*)*|(?:response-status|exit-code)|call-inputs\\/(?:path|query|header|body)(?:\\/(?:[^/~]|~[01])*)*)$",
1999
- "description": "Where the criterion is answerable from. A pointer that resolves nowhere is `rubric-evidence-unreachable`, a compile-time code, and is deliberately not a schema rejection."
2000
- }
2001
- },
2002
- "required": [
2003
- "id",
2004
- "text",
2005
- "evidence"
2006
- ],
2007
- "additionalProperties": false
2008
- }
2967
+ "description": "AD-10, mandatory per declared operation rather than per interface. `null` is legal only for an operation declaring no keys in any request channel; AD-10 exempts that operation and requires the exemption to be recorded, which pre-flight does as an `exempt` check. An input-bearing operation declaring `null` fails a strict compilation under `undeclared-mandatory-input`, alongside the other declaration-completeness check that code already gates."
2009
2968
  }
2010
2969
  },
2011
2970
  "required": [
2012
- "id",
2013
- "scaleLevels",
2014
- "failureModePenalties",
2015
- "maxLength",
2016
- "criteria"
2971
+ "operationId",
2972
+ "method",
2973
+ "pathTemplate",
2974
+ "stateChangeMarker",
2975
+ "requestShape",
2976
+ "responseDescriptor",
2977
+ "volatilePointers",
2978
+ "sensitivityWitness"
2017
2979
  ],
2018
2980
  "additionalProperties": false,
2019
- "description": "The embeddable rubric body. Named so the shared body has a stable `$defs` key distinct from the published `Rubric` artifact, which is this body plus `schemaVersion` and AD-29 lineage; without the name the two collide in the published-schema drift check under a generated positional name."
2981
+ "description": "AD-19's per-operation declaration inventory for an interface that speaks HTTP. Carried by the `api`, `web`, and `mcp` branches alike, so the export names it once instead of inlining three copies that could drift apart."
2020
2982
  },
2021
2983
  "WitnessInputs": {
2022
2984
  "type": "object",
@@ -2130,6 +3092,34 @@
2130
3092
  "matcher"
2131
3093
  ],
2132
3094
  "additionalProperties": false
3095
+ },
3096
+ {
3097
+ "type": "object",
3098
+ "properties": {
3099
+ "captured": {
3100
+ "type": "string",
3101
+ "pattern": "^\\/interactions\\/[a-z0-9]+(?:-[a-z0-9]+)*\\/(?:(?:response-body|response-headers|stdout|stderr)(?:\\/(?:[^/~]|~[01])*)*|(?:response-status|exit-code)|call-inputs\\/(?:path|query|header|body|argument|option|environment|stdin)(?:\\/(?:[^/~]|~[01])*)*|artifact\\/[a-z0-9]+(?:-[a-z0-9]+)*(?:\\/(?:[^/~]|~[01])*)*)$",
3102
+ "description": "AD-26 interaction-rooted pointer: \"/interactions/{stepId}/\" followed by one channel of the closed vocabulary. `call-inputs` takes one input channel as its next segment, one of the four transport channels or one of the four command channels; `artifact` takes the identifier of a file the operation declares it writes; `response-status` and `exit-code` take no tail. Syntax only: whether the step exists and whether the evidence is reachable are compile-time checks, not schema checks."
3103
+ }
3104
+ },
3105
+ "required": [
3106
+ "captured"
3107
+ ],
3108
+ "additionalProperties": false
3109
+ },
3110
+ {
3111
+ "type": "object",
3112
+ "properties": {
3113
+ "principal": {
3114
+ "type": "string",
3115
+ "pattern": "^[a-z0-9]+(?:-[a-z0-9]+)*$",
3116
+ "description": "A kebab-case slug. Excludes \"/\" and \"~\" so an identifier can be embedded in an interaction-rooted pointer without escaping."
3117
+ }
3118
+ },
3119
+ "required": [
3120
+ "principal"
3121
+ ],
3122
+ "additionalProperties": false
2133
3123
  }
2134
3124
  ]
2135
3125
  }