eval-quality 0.2.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 (125) hide show
  1. package/README.md +3 -3
  2. package/corpus/dev/README.md +3 -2
  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/cli/render.d.ts +2 -5
  28. package/dist/cli/render.js +51 -1
  29. package/dist/core/compile/bindings.d.ts +26 -7
  30. package/dist/core/compile/bindings.js +49 -32
  31. package/dist/core/compile/compile.d.ts +12 -0
  32. package/dist/core/compile/compile.js +28 -1
  33. package/dist/core/compile/excluded-content.d.ts +11 -0
  34. package/dist/core/compile/excluded-content.js +42 -0
  35. package/dist/core/compile/expression-legality.d.ts +2 -2
  36. package/dist/core/compile/expression-legality.js +58 -12
  37. package/dist/core/compile/interface-inventory.d.ts +34 -1
  38. package/dist/core/compile/interface-inventory.js +88 -11
  39. package/dist/core/compile/reachability.d.ts +12 -2
  40. package/dist/core/compile/reachability.js +123 -36
  41. package/dist/core/compile/schema-version.d.ts +2 -0
  42. package/dist/core/compile/schema-version.js +25 -0
  43. package/dist/core/compile/sensitivity-witness.d.ts +31 -12
  44. package/dist/core/compile/sensitivity-witness.js +110 -23
  45. package/dist/core/compile/step-reference.d.ts +2 -0
  46. package/dist/core/compile/step-reference.js +49 -0
  47. package/dist/core/coverage/operations.d.ts +62 -0
  48. package/dist/core/coverage/operations.js +57 -0
  49. package/dist/core/coverage/relevance.d.ts +4 -2
  50. package/dist/core/coverage/relevance.js +22 -23
  51. package/dist/core/coverage/satisfaction.d.ts +2 -2
  52. package/dist/core/coverage/satisfaction.js +73 -48
  53. package/dist/core/declared-inputs.d.ts +83 -4
  54. package/dist/core/declared-inputs.js +105 -8
  55. package/dist/core/evaluate/evidence-resolution.d.ts +9 -12
  56. package/dist/core/evaluate/evidence-resolution.js +70 -10
  57. package/dist/core/evaluate/operators.d.ts +22 -0
  58. package/dist/core/evaluate/operators.js +17 -1
  59. package/dist/core/evaluate/resolution.d.ts +14 -3
  60. package/dist/core/evaluate/resolution.js +63 -3
  61. package/dist/core/excluded-content.d.ts +65 -0
  62. package/dist/core/excluded-content.js +113 -0
  63. package/dist/core/failure-codes.d.ts +2 -2
  64. package/dist/core/failure-codes.js +5 -2
  65. package/dist/core/ingest/conditions.d.ts +1 -1
  66. package/dist/core/ingest/ingest.js +8 -0
  67. package/dist/core/preflight/plan.d.ts +7 -5
  68. package/dist/core/preflight/plan.js +75 -32
  69. package/dist/core/preflight/projection.d.ts +6 -3
  70. package/dist/core/preflight/projection.js +22 -2
  71. package/dist/core/preflight/reduce.js +47 -7
  72. package/dist/core/preflight/witness-evidence.d.ts +5 -5
  73. package/dist/core/preflight/witness-evidence.js +63 -20
  74. package/dist/core/schemas/artifact.d.ts +683 -35
  75. package/dist/core/schemas/constraint-ledger.js +11 -0
  76. package/dist/core/schemas/defect-signature.d.ts +438 -16
  77. package/dist/core/schemas/defect-signature.js +58 -10
  78. package/dist/core/schemas/eval-contract.d.ts +517 -12
  79. package/dist/core/schemas/eval-contract.js +18 -2
  80. package/dist/core/schemas/evidence-artifact.d.ts +14 -4
  81. package/dist/core/schemas/evidence-artifact.js +1 -1
  82. package/dist/core/schemas/interface.d.ts +741 -13
  83. package/dist/core/schemas/interface.js +120 -7
  84. package/dist/core/schemas/isolation-manifest.js +16 -9
  85. package/dist/core/schemas/plan.d.ts +217 -3
  86. package/dist/core/schemas/plan.js +22 -1
  87. package/dist/core/schemas/pointer.d.ts +47 -1
  88. package/dist/core/schemas/pointer.js +89 -8
  89. package/dist/core/schemas/port-messages.d.ts +194 -10
  90. package/dist/core/schemas/port-messages.js +73 -6
  91. package/dist/core/schemas/probe-body.d.ts +18 -0
  92. package/dist/core/schemas/probe-body.js +13 -0
  93. package/dist/core/schemas/probe.d.ts +122 -3
  94. package/dist/core/schemas/probe.js +1 -1
  95. package/dist/core/schemas/sealed-run-record.d.ts +131 -9
  96. package/dist/core/schemas/sealed-run-record.js +56 -11
  97. package/dist/core/schemas/sensitivity-witness.d.ts +108 -6
  98. package/dist/core/schemas/sensitivity-witness.js +61 -5
  99. package/dist/core/score/bindings.d.ts +2 -2
  100. package/dist/core/score/bindings.js +25 -13
  101. package/dist/core/score/qualification.d.ts +5 -5
  102. package/dist/core/score/qualification.js +67 -37
  103. package/dist/core/score/quotation.d.ts +1 -24
  104. package/dist/core/score/quotation.js +29 -4
  105. package/dist/core/score/score.js +28 -2
  106. package/dist/core/score/strength.d.ts +9 -0
  107. package/dist/core/score/strength.js +35 -1
  108. package/dist/core/score/witness.d.ts +21 -0
  109. package/dist/core/score/witness.js +9 -4
  110. package/dist/core/seal/derived-reference.d.ts +2 -9
  111. package/dist/core/seal/derived-reference.js +62 -23
  112. package/dist/core/seal/plan-index.d.ts +18 -3
  113. package/dist/core/seal/plan-index.js +44 -8
  114. package/dist/index.d.ts +1 -1
  115. package/dist/index.js +1 -1
  116. package/dist/ports/environment-probe-port.d.ts +60 -4
  117. package/dist/testing/probe-conformance.d.ts +29 -0
  118. package/dist/testing/probe-conformance.js +34 -5
  119. package/package.json +1 -1
  120. package/schemas/eval-contract.schema.json +1356 -464
  121. package/schemas/evidence-artifact.schema.json +59 -29
  122. package/schemas/isolation-manifest.schema.json +17 -10
  123. package/schemas/probe.schema.json +197 -71
  124. package/schemas/rubric.schema.json +1 -1
  125. package/schemas/sealed-run-record.schema.json +296 -39
@@ -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": [
@@ -1340,7 +1608,7 @@
1340
1608
  "fixtureReset"
1341
1609
  ],
1342
1610
  "additionalProperties": false,
1343
- "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.",
1344
1612
  "$defs": {
1345
1613
  "Expression": {
1346
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.",
@@ -1893,8 +2161,8 @@
1893
2161
  "anyOf": [
1894
2162
  {
1895
2163
  "type": "string",
1896
- "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])*)*)$",
1897
- "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."
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."
1898
2166
  },
1899
2167
  {
1900
2168
  "type": "string",
@@ -2065,7 +2333,7 @@
2065
2333
  },
2066
2334
  "evidence": {
2067
2335
  "type": "string",
2068
- "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])*)*)$",
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])*)*)$",
2069
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."
2070
2338
  }
2071
2339
  },
@@ -2088,6 +2356,630 @@
2088
2356
  "additionalProperties": false,
2089
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."
2090
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."
2547
+ },
2548
+ "body": {
2549
+ "type": "object",
2550
+ "properties": {
2551
+ "requiredKeys": {
2552
+ "type": "array",
2553
+ "items": {
2554
+ "type": "string",
2555
+ "minLength": 1
2556
+ }
2557
+ },
2558
+ "permittedKeys": {
2559
+ "type": "array",
2560
+ "items": {
2561
+ "type": "string",
2562
+ "minLength": 1
2563
+ },
2564
+ "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."
2565
+ },
2566
+ "types": {
2567
+ "type": "object",
2568
+ "propertyNames": {
2569
+ "type": "string",
2570
+ "minLength": 1
2571
+ },
2572
+ "additionalProperties": {
2573
+ "anyOf": [
2574
+ {
2575
+ "type": "string",
2576
+ "enum": [
2577
+ "string",
2578
+ "number",
2579
+ "boolean",
2580
+ "object",
2581
+ "array",
2582
+ "null"
2583
+ ]
2584
+ },
2585
+ {
2586
+ "type": "null"
2587
+ }
2588
+ ]
2589
+ },
2590
+ "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\"."
2591
+ }
2592
+ },
2593
+ "required": [
2594
+ "requiredKeys",
2595
+ "permittedKeys",
2596
+ "types"
2597
+ ],
2598
+ "additionalProperties": false
2599
+ }
2600
+ },
2601
+ "required": [
2602
+ "path",
2603
+ "query",
2604
+ "header",
2605
+ "body"
2606
+ ],
2607
+ "additionalProperties": false
2608
+ },
2609
+ "responseDescriptor": {
2610
+ "type": "object",
2611
+ "properties": {
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."
2619
+ },
2620
+ "permittedKeys": {
2621
+ "type": "array",
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": [
2655
+ {
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."
2659
+ },
2660
+ {
2661
+ "type": "null"
2662
+ }
2663
+ ],
2664
+ "description": "AD-20 rule 1 relevance reads this as its first conjunct, so `null` must stay representable."
2665
+ },
2666
+ "channelRoles": {
2667
+ "anyOf": [
2668
+ {
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"
2687
+ }
2688
+ ],
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."
2690
+ },
2691
+ "collectionLocations": {
2692
+ "anyOf": [
2693
+ {
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
+ }
2785
+ },
2786
+ {
2787
+ "type": "null"
2788
+ }
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."
2791
+ }
2792
+ },
2793
+ "required": [
2794
+ "requiredKeys",
2795
+ "permittedKeys",
2796
+ "types",
2797
+ "successIndicator",
2798
+ "channelRoles",
2799
+ "collectionLocations"
2800
+ ],
2801
+ "additionalProperties": false
2802
+ },
2803
+ "volatilePointers": {
2804
+ "type": "array",
2805
+ "items": {
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."
2809
+ }
2810
+ },
2811
+ "sensitivityWitness": {
2812
+ "anyOf": [
2813
+ {
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."
2820
+ },
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
2949
+ }
2950
+ },
2951
+ "relation": {
2952
+ "$ref": "#/$defs/Expression"
2953
+ }
2954
+ },
2955
+ "required": [
2956
+ "witnessId",
2957
+ "channel",
2958
+ "legs",
2959
+ "relation"
2960
+ ],
2961
+ "additionalProperties": false
2962
+ },
2963
+ {
2964
+ "type": "null"
2965
+ }
2966
+ ],
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."
2968
+ }
2969
+ },
2970
+ "required": [
2971
+ "operationId",
2972
+ "method",
2973
+ "pathTemplate",
2974
+ "stateChangeMarker",
2975
+ "requestShape",
2976
+ "responseDescriptor",
2977
+ "volatilePointers",
2978
+ "sensitivityWitness"
2979
+ ],
2980
+ "additionalProperties": false,
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."
2982
+ },
2091
2983
  "WitnessInputs": {
2092
2984
  "type": "object",
2093
2985
  "properties": {
@@ -2206,8 +3098,8 @@
2206
3098
  "properties": {
2207
3099
  "captured": {
2208
3100
  "type": "string",
2209
- "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])*)*)$",
2210
- "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."
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."
2211
3103
  }
2212
3104
  },
2213
3105
  "required": [