@cat-factory/contracts 0.289.0 → 0.291.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 (39) hide show
  1. package/dist/binary-capabilities.d.ts +165 -21
  2. package/dist/binary-capabilities.d.ts.map +1 -1
  3. package/dist/binary-capabilities.js +265 -49
  4. package/dist/binary-capabilities.js.map +1 -1
  5. package/dist/binary-generators.d.ts +50 -0
  6. package/dist/binary-generators.d.ts.map +1 -1
  7. package/dist/binary-generators.js +25 -1
  8. package/dist/binary-generators.js.map +1 -1
  9. package/dist/debug-api.d.ts +2 -2
  10. package/dist/entities.d.ts +23 -7
  11. package/dist/entities.d.ts.map +1 -1
  12. package/dist/entities.js +18 -5
  13. package/dist/entities.js.map +1 -1
  14. package/dist/execution.d.ts +2 -2
  15. package/dist/pr-report.d.ts +45 -10
  16. package/dist/pr-report.d.ts.map +1 -1
  17. package/dist/pr-report.js +26 -8
  18. package/dist/pr-report.js.map +1 -1
  19. package/dist/routes/agent-runs.d.ts +2 -2
  20. package/dist/routes/board.d.ts +13 -13
  21. package/dist/routes/bug-hunt.d.ts +4 -4
  22. package/dist/routes/debug-api.d.ts +1 -1
  23. package/dist/routes/execution.d.ts +11 -11
  24. package/dist/routes/human-review.d.ts +1 -1
  25. package/dist/routes/human-test.d.ts +5 -5
  26. package/dist/routes/initiative.d.ts +1 -1
  27. package/dist/routes/public-evidence.d.ts +1 -0
  28. package/dist/routes/public-evidence.d.ts.map +1 -1
  29. package/dist/routes/tasks.d.ts +3 -3
  30. package/dist/routes/visual-confirm.d.ts +3 -3
  31. package/dist/routes/workspaces.d.ts +34 -6
  32. package/dist/routes/workspaces.d.ts.map +1 -1
  33. package/dist/snapshot.d.ts +17 -3
  34. package/dist/snapshot.d.ts.map +1 -1
  35. package/dist/tool-servers.d.ts +9 -7
  36. package/dist/tool-servers.d.ts.map +1 -1
  37. package/dist/tool-servers.js +8 -5
  38. package/dist/tool-servers.js.map +1 -1
  39. package/package.json +1 -1
@@ -23,27 +23,25 @@ import * as v from 'valibot';
23
23
  * two producers of one modality apart without unlocking an option would be the refused
24
24
  * discriminator wearing a new name.
25
25
  *
26
- * **A capability says the request can CARRY the value, never which values are accepted.** It is
27
- * the answer to "does asking this reach a parameter at all", and it is deliberately not the
28
- * answer to "will this endpoint take 4x, or 7:3, or 96x96". Declaring the accepted VALUES beside
29
- * each member is the per-integration table the design record refuses, one axis over: it goes
30
- * stale in the deployment's repo while the vendor changes it in its own, and the version that
31
- * looks harmless is the one that lies. An endpoint that upscales at a ratio it fixes itself has
32
- * no factor to enumerate, so `upscale: [2]` would not be a narrower declaration of the truth, it
33
- * would be a fabricated one, and a step asking for 4x would be admitted against it and served an
34
- * enlargement at an unknown multiple. That is exactly the silent wrong artifact this axis exists
35
- * to prevent, arriving through the axis.
36
- *
37
- * So an endpoint that accepts an option only at values it fixes does one of two things, and
38
- * never a third. If a COARSER member already means "a set it rounds to" it declares that one
39
- * ({@link 'aspect-ratio'} is the worked example, and it is why the ratio and size members are
40
- * separate). If there is none, it declares nothing here and says what it does in a definition's
41
- * `guidance`: the step is then refused an option the endpoint cannot answer, which is a visible
42
- * refusal naming the capability rather than an artifact that checks out everywhere and is wrong
43
- * where it counts. A refusal that reads as too strict is the
44
- * correct half of that trade, and the honest reading of it is usually that the option was the
45
- * wrong way to state the requirement (a step wanting a specific larger deliverable is asking for
46
- * {@link 'exact-size'}, not for a multiple of something it never measured).
26
+ * **A capability says the request can CARRY the value; {@link binaryGeneratorAcceptsSchema} says
27
+ * which values are accepted.** The two questions are separate because they fail differently, and
28
+ * collapsing them is what produced both halves of the problem the value axis was added to fix.
29
+ * This member answers only "does asking this reach a parameter at all". An endpoint that has no
30
+ * such parameter declares NOTHING here, however close its behaviour looks: Recraft's
31
+ * `crispUpscale` enlarges at a ratio it fixes itself and takes no factor, so declaring `upscale`
32
+ * for it would admit a step asking for 4x and hand it an enlargement at an unknown multiple. No
33
+ * accepted-value list repairs that, because `upscale: [2]` is not a narrower statement of the
34
+ * truth, it is a fabricated one. The endpoint says what it does in a definition's `guidance` and
35
+ * the step is refused an option the endpoint genuinely cannot answer, which is a visible refusal
36
+ * naming the capability rather than an artifact that checks out everywhere and is wrong where it
37
+ * counts. The honest reading of such a refusal is usually that the option was the wrong way to
38
+ * state the requirement: a step wanting a specific larger deliverable is asking for
39
+ * {@link 'exact-size'}, not for a multiple of something it never measured.
40
+ *
41
+ * An endpoint that DOES take the parameter, but only at values from a closed set, declares the
42
+ * capability and states the set. That is not the per-integration table the design record refuses:
43
+ * a set of accepted values partitions exactly, the same way `mediaTypes` does, and stating it
44
+ * makes an existing ask checkable without unlocking anything new to ask for.
47
45
  *
48
46
  * Anything that does not clear the bar stays prose in `description` / `guidance`, which is where
49
47
  * "good at pixel art", "expensive above 2K" and "rate limited to 5/min" belong.
@@ -172,6 +170,21 @@ export declare const binaryOutputSizeSchema: v.ObjectSchema<{
172
170
  readonly height: v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 1, undefined>, v.MaxValueAction<number, 16384, undefined>]>;
173
171
  }, undefined>;
174
172
  export type BinaryOutputSize = v.InferOutput<typeof binaryOutputSizeSchema>;
173
+ /**
174
+ * An aspect ratio, `W:H`.
175
+ *
176
+ * Named rather than inlined into the one option that used to be its only reader, because an
177
+ * integration now declares the ratios it ACCEPTS ({@link binaryGeneratorAcceptsSchema}) and the
178
+ * two have to be the same rule: a declaration validated more loosely than the request could hold
179
+ * a value no step is able to spell, so the endpoint's own list would silently accept nothing.
180
+ */
181
+ export declare const binaryAspectRatioSchema: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.TrimAction, v.MaxLengthAction<string, 16, undefined>, v.RegexAction<string, "must be an aspect ratio of the form W:H">]>;
182
+ /**
183
+ * How many times the integration's native size to render at. Named for the reason
184
+ * {@link binaryAspectRatioSchema} is: the accepted-factor list and the step's own field are one
185
+ * rule read from two places.
186
+ */
187
+ export declare const binaryUpscaleFactorSchema: v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 2, undefined>, v.MaxValueAction<number, 8, undefined>]>;
175
188
  /**
176
189
  * The per-step GENERATION OPTIONS: the parameters a step wants every generation to carry, each
177
190
  * gated by the capability that makes it answerable ({@link BINARY_OPTION_CAPABILITIES}).
@@ -412,4 +425,135 @@ export declare function binaryCapabilityProviders(required: readonly BinaryGener
412
425
  capability: BinaryGeneratorCapability;
413
426
  generatorIds: string[];
414
427
  }[];
428
+ /**
429
+ * The values an integration ACCEPTS for the generation options whose domain is enumerable.
430
+ *
431
+ * A sibling of `capabilities` rather than a change to it, so a definition that declares only tags
432
+ * keeps working unchanged and absent goes on meaning "only the coarse facts are known".
433
+ *
434
+ * **Only the options with a CLOSED domain appear here, and each entry is a SET.** A range is
435
+ * deliberately unrepresentable: `min`/`max`/`step`/`multiple-of` is a constraint language, and
436
+ * the moment one exists it grows to cover Flux's "any pair up to 4 MP in multiples of 32", which
437
+ * is the `resolutionRange` discriminator the design record refuses. An endpoint with a genuine
438
+ * range declares the capability, states no set, and puts its limits in `guidance`: its steps are
439
+ * then judged exactly as they were before this field existed.
440
+ *
441
+ * **An empty list is refused rather than read as "accepts nothing".** Absent is the one spelling
442
+ * of "not stated", so a `[]` that fell out of a filter would otherwise refuse every value the
443
+ * endpoint has, silently, on a declaration whose author meant to say nothing at all.
444
+ */
445
+ export declare const binaryGeneratorAcceptsSchema: v.ObjectSchema<{
446
+ /** The aspect ratios it takes, when it takes a closed set of them. Compared in LOWEST TERMS. */
447
+ readonly aspectRatios: v.OptionalSchema<v.SchemaWithPipe<readonly [v.ArraySchema<v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.TrimAction, v.MaxLengthAction<string, 16, undefined>, v.RegexAction<string, "must be an aspect ratio of the form W:H">]>, undefined>, v.MinLengthAction<string[], 1, undefined>, v.MaxLengthAction<string[], 64, undefined>]>, undefined>;
448
+ /** The exact `WxH` pairs it renders, when its size parameter is a closed list. */
449
+ readonly outputSizes: v.OptionalSchema<v.SchemaWithPipe<readonly [v.ArraySchema<v.ObjectSchema<{
450
+ readonly width: v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 1, undefined>, v.MaxValueAction<number, 16384, undefined>]>;
451
+ readonly height: v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 1, undefined>, v.MaxValueAction<number, 16384, undefined>]>;
452
+ }, undefined>, undefined>, v.MinLengthAction<{
453
+ width: number;
454
+ height: number;
455
+ }[], 1, undefined>, v.MaxLengthAction<{
456
+ width: number;
457
+ height: number;
458
+ }[], 64, undefined>]>, undefined>;
459
+ /** The upscale factors it takes, when it takes a closed set of them. */
460
+ readonly upscaleFactors: v.OptionalSchema<v.SchemaWithPipe<readonly [v.ArraySchema<v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 2, undefined>, v.MaxValueAction<number, 8, undefined>]>, undefined>, v.MinLengthAction<number[], 1, undefined>, v.MaxLengthAction<number[], 8, undefined>]>, undefined>;
461
+ }, undefined>;
462
+ export type BinaryGeneratorAccepts = v.InferOutput<typeof binaryGeneratorAcceptsSchema>;
463
+ /** A generation option whose accepted values an integration can enumerate. */
464
+ export type BinaryValueOption = 'aspectRatio' | 'outputSize' | 'upscale';
465
+ /** A value a step asks for that no selected integration accepts. Refuses the run. */
466
+ export interface BinaryUnacceptedValue {
467
+ option: BinaryValueOption;
468
+ /** What the step asks for, in the spelling a message states back. */
469
+ requested: string;
470
+ /** What the integrations that DID state a set accept, deduplicated, in selection order. */
471
+ accepted: string[];
472
+ }
473
+ /**
474
+ * A value some selected integration accepts and another has ENUMERATED AWAY: the step can be
475
+ * served, by a subset of what it selected, and the rest will quietly deliver something else.
476
+ */
477
+ export interface BinaryPartiallyAcceptedValue {
478
+ option: BinaryValueOption;
479
+ /** What the step asks for, in the spelling a message states back. */
480
+ requested: string;
481
+ /** Ids of the integrations whose stated set EXCLUDES it, in selection order. */
482
+ refusedBy: string[];
483
+ }
484
+ /** How a step's requested option VALUES stand against what its selected integrations accept. */
485
+ export interface BinaryValueCoverage {
486
+ /** Requested values every integration that stated a set excludes, and none left open. Refuses. */
487
+ unaccepted: BinaryUnacceptedValue[];
488
+ /**
489
+ * Requested values a stated set contains and ANOTHER stated set excludes, naming the
490
+ * integrations that exclude them. The step is servable, so this is advisory.
491
+ */
492
+ partial: BinaryPartiallyAcceptedValue[];
493
+ /**
494
+ * Requested values no stated set contains, where another integration declaring the capability
495
+ * stated NO set, so the value may still be served and nothing may say otherwise. Advisory.
496
+ */
497
+ unverifiable: BinaryValueOption[];
498
+ }
499
+ /**
500
+ * Judge the VALUES a step asks for against the sets its selected integrations accept.
501
+ *
502
+ * Judged per option and PER DECLARER, over the integrations that declare the gating capability
503
+ * (one that declares none of it is already the coarse axis's answer, and counting it here would
504
+ * report one fault twice). Each declarer is in one of three states, and the disposition is a
505
+ * function of the whole set rather than of the first agreeable member:
506
+ *
507
+ * - **Nobody stated a set.** SILENT. This is the state every registration is in until somebody
508
+ * audits an endpoint, and it is exactly what the platform knew before this field existed. An
509
+ * advisory that fired here would ride nearly every step with an aspect ratio, which is how a
510
+ * line stops being read.
511
+ * - **Every declarer that stated a set contains the value.** Covered, and a declarer that stated
512
+ * nothing beside them stays the pre-field unknown rather than a finding.
513
+ * - **Some stated set contains it and another stated set EXCLUDES it.** PARTIAL. The step is
514
+ * servable and is not refused, for the reason {@link binaryCapabilityCoverage} treats one
515
+ * declarer as covering a capability: which integration renders which artifact is the agent's
516
+ * call, not the platform's. But an excluding set is a DEFINITE fact about a definite endpoint,
517
+ * so it is named rather than absorbed. Absorbing it is the exact silent crop this axis exists
518
+ * to prevent, and it inverted the reporting: the LESS informed selection (a declarer that
519
+ * stated nothing) raised an advisory, so declaring an accurate second set bought silence.
520
+ * - **No stated set contains it, and some declarer stated none.** UNVERIFIABLE. One integration
521
+ * refuses the value and another has not said, so the step may well be served: reported, never
522
+ * refused, the same disposition {@link binaryCapabilityCoverage} gives its own third state.
523
+ * - **No stated set contains it, and every declarer stated one.** UNACCEPTED, and this is the
524
+ * refusal the axis exists for: every endpoint that could serve the option has enumerated what
525
+ * it takes, and this is not among them.
526
+ *
527
+ * Takes the step's OPTIONS rather than a pre-derived requirement list, unlike its neighbours,
528
+ * because the judgement needs the value and not only the capability it implies. It takes each
529
+ * integration's `id` for the same reason {@link binaryCapabilityProviders} does: a partial
530
+ * finding whose remedy is routing is unusable without naming who to route around.
531
+ */
532
+ export declare function binaryValueCoverage(options: BinaryGenerationOptions | undefined, selected: readonly {
533
+ id: string;
534
+ capabilities?: readonly BinaryGeneratorCapability[];
535
+ accepts?: BinaryGeneratorAccepts;
536
+ }[]): BinaryValueCoverage;
537
+ /**
538
+ * The value sets a definition states for options its capabilities do NOT declare.
539
+ *
540
+ * A definition saying which aspect ratios it accepts while declaring no `aspect-ratio` has stated
541
+ * two contradicting facts about one endpoint, and every reader believes a different half:
542
+ * {@link binaryValueCoverage} judges only over the capability's declarers and never sees the set,
543
+ * the agent's brief renders the set as fact beside the integration's formats, and admission
544
+ * refuses every step asking for the option at all. So the accurate half is unreachable and the
545
+ * step is refused for lacking a capability the same registration was documenting.
546
+ *
547
+ * Derived from the same table the coverage rule folds over, so an option added to
548
+ * {@link BinaryValueOption} is checked here with no second edit. Lives beside that table rather
549
+ * than in the boot validator for the reason the table is private: a caller re-deriving which
550
+ * capability gates which set is the drift this returns instead.
551
+ */
552
+ export declare function binaryAcceptsWithoutCapability(declaration: {
553
+ capabilities?: readonly BinaryGeneratorCapability[];
554
+ accepts?: BinaryGeneratorAccepts;
555
+ }): {
556
+ option: BinaryValueOption;
557
+ capability: BinaryGeneratorCapability;
558
+ }[];
415
559
  //# sourceMappingURL=binary-capabilities.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"binary-capabilities.d.ts","sourceRoot":"","sources":["../src/binary-capabilities.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"binary-capabilities.d.ts","sourceRoot":"","sources":["../src/binary-capabilities.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,SAAS,CAAA;AAuB5B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8CG;AACH,eAAO,MAAM,+BAA+B,mOA2G1C,CAAA;AACF,MAAM,MAAM,yBAAyB,GAAG,CAAC,CAAC,WAAW,CAAC,OAAO,+BAA+B,CAAC,CAAA;AAI7F;;;;;;;;;GASG;AACH,wBAAgB,2BAA2B,CAAC,KAAK,EAAE,MAAM,GAAG,KAAK,IAAI,yBAAyB,CAE7F;AAED;;;;;;;;;GASG;AACH,eAAO,MAAM,oBAAoB;IAC/B,yFAAyF;;IAEzF;;;;;OAKG;;IAIH,gFAAgF;;aAEhF,CAAA;AACF,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,WAAW,CAAC,OAAO,oBAAoB,CAAC,CAAA;AAEvE;;;;;GAKG;AACH,eAAO,MAAM,0BAA0B;;;;;aAuBrC,CAAA;AACF,MAAM,MAAM,oBAAoB,GAAG,CAAC,CAAC,WAAW,CAAC,OAAO,0BAA0B,CAAC,CAAA;AAEnF;;;;;;GAMG;AACH,eAAO,MAAM,uBAAuB;IAClC;;;OAGG;;IAEH,sDAAsD;;IAEtD;;;OAGG;;QAlEH,yFAAyF;;QAEzF;;;;;WAKG;;QAIH,gFAAgF;;;IAyDhF,kFAAkF;;QApElF,yFAAyF;;QAEzF;;;;;WAKG;;QAIH,gFAAgF;;;aA2DhF,CAAA;AACF,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,WAAW,CAAC,OAAO,uBAAuB,CAAC,CAAA;AAE7E;;;;;;;;;GASG;AACH,eAAO,MAAM,uBAAuB,QAAQ,CAAA;AAE5C;;;;;;;;;GASG;AACH,eAAO,MAAM,sBAAsB;;;aAGjC,CAAA;AACF,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,WAAW,CAAC,OAAO,sBAAsB,CAAC,CAAA;AAE3E;;;;;;;GAOG;AACH,eAAO,MAAM,uBAAuB,kLAKnC,CAAA;AAED;;;;GAIG;AACH,eAAO,MAAM,yBAAyB,4KAKrC,CAAA;AAED;;;;;;;;;;;;GAYG;AACH;;;;;GAKG;AACH,eAAO,MAAM,6BAA6B;IACxC;;;OAGG;;;;;;;;;;;;IAEH;;;OAGG;;QApGH;;;WAGG;;QAEH,sDAAsD;;QAEtD;;;WAGG;;YAlEH,yFAAyF;;YAEzF;;;;;eAKG;;YAIH,gFAAgF;;;QAyDhF,kFAAkF;;YApElF,yFAAyF;;YAEzF;;;;;eAKG;;YAIH,gFAAgF;;;;IAmJhF,iDAAiD;;IAEjD;;;;;;OAMG;;IAEH,kDAAkD;;IAElD;;;;;;;;OAQG;;;;;IAEH,iFAAiF;;IAEjF,yFAAyF;;IAEzF,2DAA2D;;aAE3D,CAAA;AAEF;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,WAAW,uBAAuB;IACtC,eAAe,CAAC,EAAE,oBAAoB,EAAE,GAAG,SAAS,CAAA;IACpD,IAAI,CAAC,EAAE,iBAAiB,GAAG,SAAS,CAAA;IACpC,cAAc,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IACnC,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IACzB,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IAChC,UAAU,CAAC,EAAE,gBAAgB,GAAG,SAAS,CAAA;IACzC,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IAC5B,qBAAqB,CAAC,EAAE,IAAI,GAAG,SAAS,CAAA;IACxC,QAAQ,CAAC,EAAE,IAAI,GAAG,SAAS,CAAA;CAC5B;AAED;;;;GAIG;AACH,eAAO,MAAM,6BAA6B,EAAE,CAAC,CAAC,aAAa,CAAC,OAAO,EAAE,uBAAuB,CAC7D,CAAA;AAE/B;;;;;;;;GAQG;AACH,eAAO,MAAM,0BAA0B,EAAE,MAAM,CAC7C,MAAM,uBAAuB,EAC7B,SAAS,yBAAyB,EAAE,CAWrC,CAAA;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,0BAA0B,CACxC,OAAO,EAAE,uBAAuB,GAAG,SAAS,GAC3C,yBAAyB,EAAE,CAiB7B;AAED;;;;GAIG;AACH,MAAM,MAAM,2BAA2B,GAAG,aAAa,GAAG,SAAS,CAAA;AAEnE;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,4BAA4B,CAC1C,OAAO,EAAE,uBAAuB,GAAG,SAAS,GAC3C,2BAA2B,EAAE,CAM/B;AAED,+FAA+F;AAC/F,MAAM,WAAW,wBAAwB;IACvC;iDAC6C;IAC7C,SAAS,EAAE,yBAAyB,EAAE,CAAA;IACtC;;qBAEiB;IACjB,YAAY,EAAE,yBAAyB,EAAE,CAAA;CAC1C;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,wBAAwB,CACtC,QAAQ,EAAE,SAAS,yBAAyB,EAAE,EAC9C,QAAQ,EAAE,SAAS;IAAE,YAAY,CAAC,EAAE,SAAS,yBAAyB,EAAE,CAAA;CAAE,EAAE,GAC3E,wBAAwB,CAa1B;AAED;;;;;;;;;GASG;AACH,wBAAgB,yBAAyB,CACvC,QAAQ,EAAE,SAAS,yBAAyB,EAAE,EAC9C,QAAQ,EAAE,SAAS;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,YAAY,CAAC,EAAE,SAAS,yBAAyB,EAAE,CAAA;CAAE,EAAE,GACvF;IAAE,UAAU,EAAE,yBAAyB,CAAC;IAAC,YAAY,EAAE,MAAM,EAAE,CAAA;CAAE,EAAE,CASrE;AAoBD;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,4BAA4B;IACvC,gGAAgG;;IAIhG,kFAAkF;;;;;;;;;;;IAElF,wEAAwE;;aAIxE,CAAA;AACF,MAAM,MAAM,sBAAsB,GAAG,CAAC,CAAC,WAAW,CAAC,OAAO,4BAA4B,CAAC,CAAA;AAEvF,8EAA8E;AAC9E,MAAM,MAAM,iBAAiB,GAAG,aAAa,GAAG,YAAY,GAAG,SAAS,CAAA;AAgExE,qFAAqF;AACrF,MAAM,WAAW,qBAAqB;IACpC,MAAM,EAAE,iBAAiB,CAAA;IACzB,qEAAqE;IACrE,SAAS,EAAE,MAAM,CAAA;IACjB,2FAA2F;IAC3F,QAAQ,EAAE,MAAM,EAAE,CAAA;CACnB;AAED;;;GAGG;AACH,MAAM,WAAW,4BAA4B;IAC3C,MAAM,EAAE,iBAAiB,CAAA;IACzB,qEAAqE;IACrE,SAAS,EAAE,MAAM,CAAA;IACjB,gFAAgF;IAChF,SAAS,EAAE,MAAM,EAAE,CAAA;CACpB;AAED,gGAAgG;AAChG,MAAM,WAAW,mBAAmB;IAClC,kGAAkG;IAClG,UAAU,EAAE,qBAAqB,EAAE,CAAA;IACnC;;;OAGG;IACH,OAAO,EAAE,4BAA4B,EAAE,CAAA;IACvC;;;OAGG;IACH,YAAY,EAAE,iBAAiB,EAAE,CAAA;CAClC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,uBAAuB,GAAG,SAAS,EAC5C,QAAQ,EAAE,SAAS;IACjB,EAAE,EAAE,MAAM,CAAA;IACV,YAAY,CAAC,EAAE,SAAS,yBAAyB,EAAE,CAAA;IACnD,OAAO,CAAC,EAAE,sBAAsB,CAAA;CACjC,EAAE,GACF,mBAAmB,CAsCrB;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,8BAA8B,CAAC,WAAW,EAAE;IAC1D,YAAY,CAAC,EAAE,SAAS,yBAAyB,EAAE,CAAA;IACnD,OAAO,CAAC,EAAE,sBAAsB,CAAA;CACjC,GAAG;IAAE,MAAM,EAAE,iBAAiB,CAAC;IAAC,UAAU,EAAE,yBAAyB,CAAA;CAAE,EAAE,CAWzE"}
@@ -43,27 +43,25 @@ import * as v from 'valibot';
43
43
  * two producers of one modality apart without unlocking an option would be the refused
44
44
  * discriminator wearing a new name.
45
45
  *
46
- * **A capability says the request can CARRY the value, never which values are accepted.** It is
47
- * the answer to "does asking this reach a parameter at all", and it is deliberately not the
48
- * answer to "will this endpoint take 4x, or 7:3, or 96x96". Declaring the accepted VALUES beside
49
- * each member is the per-integration table the design record refuses, one axis over: it goes
50
- * stale in the deployment's repo while the vendor changes it in its own, and the version that
51
- * looks harmless is the one that lies. An endpoint that upscales at a ratio it fixes itself has
52
- * no factor to enumerate, so `upscale: [2]` would not be a narrower declaration of the truth, it
53
- * would be a fabricated one, and a step asking for 4x would be admitted against it and served an
54
- * enlargement at an unknown multiple. That is exactly the silent wrong artifact this axis exists
55
- * to prevent, arriving through the axis.
56
- *
57
- * So an endpoint that accepts an option only at values it fixes does one of two things, and
58
- * never a third. If a COARSER member already means "a set it rounds to" it declares that one
59
- * ({@link 'aspect-ratio'} is the worked example, and it is why the ratio and size members are
60
- * separate). If there is none, it declares nothing here and says what it does in a definition's
61
- * `guidance`: the step is then refused an option the endpoint cannot answer, which is a visible
62
- * refusal naming the capability rather than an artifact that checks out everywhere and is wrong
63
- * where it counts. A refusal that reads as too strict is the
64
- * correct half of that trade, and the honest reading of it is usually that the option was the
65
- * wrong way to state the requirement (a step wanting a specific larger deliverable is asking for
66
- * {@link 'exact-size'}, not for a multiple of something it never measured).
46
+ * **A capability says the request can CARRY the value; {@link binaryGeneratorAcceptsSchema} says
47
+ * which values are accepted.** The two questions are separate because they fail differently, and
48
+ * collapsing them is what produced both halves of the problem the value axis was added to fix.
49
+ * This member answers only "does asking this reach a parameter at all". An endpoint that has no
50
+ * such parameter declares NOTHING here, however close its behaviour looks: Recraft's
51
+ * `crispUpscale` enlarges at a ratio it fixes itself and takes no factor, so declaring `upscale`
52
+ * for it would admit a step asking for 4x and hand it an enlargement at an unknown multiple. No
53
+ * accepted-value list repairs that, because `upscale: [2]` is not a narrower statement of the
54
+ * truth, it is a fabricated one. The endpoint says what it does in a definition's `guidance` and
55
+ * the step is refused an option the endpoint genuinely cannot answer, which is a visible refusal
56
+ * naming the capability rather than an artifact that checks out everywhere and is wrong where it
57
+ * counts. The honest reading of such a refusal is usually that the option was the wrong way to
58
+ * state the requirement: a step wanting a specific larger deliverable is asking for
59
+ * {@link 'exact-size'}, not for a multiple of something it never measured.
60
+ *
61
+ * An endpoint that DOES take the parameter, but only at values from a closed set, declares the
62
+ * capability and states the set. That is not the per-integration table the design record refuses:
63
+ * a set of accepted values partitions exactly, the same way `mediaTypes` does, and stating it
64
+ * makes an existing ask checkable without unlocking anything new to ask for.
67
65
  *
68
66
  * Anything that does not clear the bar stays prose in `description` / `guidance`, which is where
69
67
  * "good at pixel art", "expensive above 2K" and "rate limited to 5/min" belong.
@@ -102,30 +100,25 @@ export const binaryGeneratorCapabilitySchema = v.picklist([
102
100
  */
103
101
  'seed',
104
102
  /**
105
- * Accepts an explicit aspect SHAPE: a ratio (`16:9`), or a fixed set of sizes it rounds to.
106
- * Unlocks `generation.aspectRatio`.
103
+ * The request carries an explicit aspect SHAPE: a ratio (`16:9`), or a bucket it maps a shape
104
+ * onto (`image_size: square | portrait | landscape`, `resolution: 1k | 2k`). Unlocks
105
+ * `generation.aspectRatio`.
107
106
  *
108
107
  * Deliberately NOT "a ratio or an output size", which is what this member said while it was
109
- * the only one on the axis. A bucketed API (`image_size: square | portrait | landscape`,
110
- * `resolution: 1k | 2k`) honours a ratio exactly and an exact pixel size not at all, so one
111
- * member covering both made the two integrations that CAN be handed dimensions
112
- * indistinguishable from the two that cannot — see {@link 'exact-size'}.
108
+ * the only one on the axis. A bucketed API honours a ratio exactly and an exact pixel size not
109
+ * at all, so one member covering both made the integrations that CAN be handed dimensions
110
+ * indistinguishable from the ones that cannot: see {@link 'exact-size'}.
113
111
  *
114
- * **A closed LIST of exact `WxH` values is this member, not that one**, and it is the boundary
115
- * case worth stating because it looks like the other one: an endpoint whose `size` parameter
116
- * takes `1024x1024 | 1365x1024 | …` is handed pixel dimensions and still ROUNDS, so a step
117
- * needing 96x96 gets an 11x downscale from it. What separates the two members is not whether
118
- * the request carries numbers, it is whether the endpoint can be asked for an ARBITRARY pair
119
- * and render it. An enumerated set cannot, so it declares this and states the set in
120
- * a definition's `guidance`.
112
+ * The line between the two members is WHAT THE REQUEST CARRIES, not how free the answer is. A
113
+ * shape goes here even when the endpoint accepts only a closed list of ratios; dimensions go
114
+ * on `exact-size` even when it accepts only a closed list of those. Which values each takes is
115
+ * {@link binaryGeneratorAcceptsSchema}'s `aspectRatios`, so an endpoint offering ten ratios is
116
+ * a step's `7:3` refused by name rather than admitted and quietly cropped.
121
117
  */
122
118
  'aspect-ratio',
123
119
  /**
124
- * Accepts ARBITRARY output dimensions in pixels: a width and a height it renders at natively,
125
- * rather than a shape it rounds to. Unlocks `generation.outputSize`.
126
- *
127
- * "Arbitrary" is the whole test, and it is what an endpoint offering a closed list of exact
128
- * `WxH` values fails: see {@link 'aspect-ratio'}, which is where such an endpoint belongs.
120
+ * The request carries output DIMENSIONS in pixels: a width and a height, rather than a shape
121
+ * the endpoint maps onto a size of its own choosing. Unlocks `generation.outputSize`.
129
122
  *
130
123
  * Its own member rather than a refinement of `aspect-ratio`, because the two partition
131
124
  * differently and the difference is the whole deliverable for anything rendered to a fixed
@@ -135,21 +128,28 @@ export const binaryGeneratorCapabilitySchema = v.picklist([
135
128
  * format is covered, the upload succeeded). The consumer that rejects it is the game, weeks
136
129
  * later.
137
130
  *
131
+ * **An endpoint whose `size` parameter takes a closed list of `WxH` values declares THIS
132
+ * member and states the list**, which is the boundary case worth spelling out because it moved.
133
+ * While `accepts` did not exist such an endpoint had to declare `aspect-ratio`, on the ground
134
+ * that it rounds: that classification described a size-taking API as shape-taking, and it left
135
+ * a step needing 96x96 admitted against an endpoint whose nearest listed shape is 1024x1024.
136
+ * Now the capability answers the durable question (can it be handed dimensions at all) and
137
+ * `accepts.outputSizes` answers the vendor-specific one.
138
+ *
138
139
  * The vocabulary stays FLAT: an API taking width and height can honour any ratio, so it
139
140
  * declares BOTH members rather than this one implying the other. That is the same shape
140
- * `reference-image` / `multi-reference` already has, and it is chosen for the same reason
141
+ * `reference-image` / `multi-reference` already has, and it is chosen for the same reason:
141
142
  * an implication table is an ordering over a picklist that every future member would then
142
143
  * have to be placed in, to save a deployment one word in its own registration, and the cost
143
144
  * of forgetting that word is a loud refusal naming the capability rather than a silent
144
145
  * mis-render.
145
146
  *
146
- * What this does NOT do is state which sizes an endpoint supports. Flux caps at 4 MP and
147
- * wants multiples of 32; Retro Diffusion's range moves with the style. A per-integration size
148
- * TABLE is refused on the design record's own grounds (it is the `resolutionRange`
149
- * discriminator wearing a new name, and it goes stale in this repo while the vendor changes
150
- * it in theirs). This member answers only the question that is a durable fact about the
151
- * endpoint's REQUEST SHAPE: can it be handed dimensions at all. The rest is the integration's
152
- * own `guidance`, where a sentence can say what the limits are.
147
+ * An endpoint that renders any pair it is handed, inside limits no list can enumerate (Flux
148
+ * caps at 4 MP and wants multiples of 32; Retro Diffusion's range moves with the style),
149
+ * declares this member and states NO sizes. A per-integration size RANGE stays refused on the
150
+ * design record's own grounds: it is the `resolutionRange` discriminator wearing a new name,
151
+ * and `min`/`max`/`step`/`multiple-of` is a constraint language rather than a fact. The limits
152
+ * stay in the integration's own `guidance`, where a sentence can say what they are.
153
153
  */
154
154
  'exact-size',
155
155
  /**
@@ -287,6 +287,21 @@ export const binaryOutputSizeSchema = v.object({
287
287
  width: v.pipe(v.number(), v.integer(), v.minValue(1), v.maxValue(MAX_BINARY_PIXEL_EXTENT)),
288
288
  height: v.pipe(v.number(), v.integer(), v.minValue(1), v.maxValue(MAX_BINARY_PIXEL_EXTENT)),
289
289
  });
290
+ /**
291
+ * An aspect ratio, `W:H`.
292
+ *
293
+ * Named rather than inlined into the one option that used to be its only reader, because an
294
+ * integration now declares the ratios it ACCEPTS ({@link binaryGeneratorAcceptsSchema}) and the
295
+ * two have to be the same rule: a declaration validated more loosely than the request could hold
296
+ * a value no step is able to spell, so the endpoint's own list would silently accept nothing.
297
+ */
298
+ export const binaryAspectRatioSchema = v.pipe(v.string(), v.trim(), v.maxLength(16), v.regex(/^[1-9][0-9]{0,3}:[1-9][0-9]{0,3}$/, 'must be an aspect ratio of the form W:H'));
299
+ /**
300
+ * How many times the integration's native size to render at. Named for the reason
301
+ * {@link binaryAspectRatioSchema} is: the accepted-factor list and the step's own field are one
302
+ * rule read from two places.
303
+ */
304
+ export const binaryUpscaleFactorSchema = v.pipe(v.number(), v.integer(), v.minValue(2), v.maxValue(8));
290
305
  /**
291
306
  * The per-step GENERATION OPTIONS: the parameters a step wants every generation to carry, each
292
307
  * gated by the capability that makes it answerable ({@link BINARY_OPTION_CAPABILITIES}).
@@ -328,7 +343,7 @@ export const binaryGenerationOptionsObject = v.object({
328
343
  */
329
344
  seed: v.optional(v.pipe(v.number(), v.integer(), v.minValue(0), v.maxValue(4294967295))),
330
345
  /** `16:9`, `1:1`, `3:2`. Needs `aspect-ratio`. */
331
- aspectRatio: v.optional(v.pipe(v.string(), v.trim(), v.maxLength(16), v.regex(/^[1-9][0-9]{0,3}:[1-9][0-9]{0,3}$/, 'must be an aspect ratio of the form W:H'))),
346
+ aspectRatio: v.optional(binaryAspectRatioSchema),
332
347
  /**
333
348
  * Exact pixel dimensions every artifact must be delivered at. Needs `exact-size`.
334
349
  *
@@ -340,7 +355,7 @@ export const binaryGenerationOptionsObject = v.object({
340
355
  */
341
356
  outputSize: v.optional(binaryOutputSizeSchema),
342
357
  /** Render at this multiple of the integration's native size. Needs `upscale`. */
343
- upscale: v.optional(v.pipe(v.number(), v.integer(), v.minValue(2), v.maxValue(8))),
358
+ upscale: v.optional(binaryUpscaleFactorSchema),
344
359
  /** Deliver an alpha channel rather than a background. Needs `transparent-background`. */
345
360
  transparentBackground: v.optional(v.literal(true)),
346
361
  /** Deliver a seamlessly tiling image. Needs `tileable`. */
@@ -495,4 +510,205 @@ export function binaryCapabilityProviders(required, selected) {
495
510
  }
496
511
  return out;
497
512
  }
513
+ // ---------------------------------------------------------------------------
514
+ // The VALUE axis: not "can this endpoint be asked", which is the capability above, but "will it
515
+ // take the value this step is asking for".
516
+ //
517
+ // The capability axis is a yes/no, and for several real endpoints the honest answer is "yes, at
518
+ // one of these". Grok Imagine and Nano Banana take an aspect ratio from a closed picklist while
519
+ // Flux and Retro Diffusion honour any ratio at all, and all four declare `aspect-ratio`, so a
520
+ // step asking for `7:3` is admitted against every one of them and served by two. Nothing reports
521
+ // the crop: the modality is covered, the format is covered, the upload succeeded. That is the
522
+ // silent wrong artifact the capability axis exists to prevent, arriving through the capability
523
+ // axis, and no wording of a yes/no fixes it.
524
+ //
525
+ // What the platform will NOT do is negotiate. A "closest supported value" rule turns a refusal
526
+ // into a substitution chosen by whoever wrote the fallback, which is the failure this axis is
527
+ // about. A step either asks for something the endpoint takes or it is told, by name, that it
528
+ // does not.
529
+ // ---------------------------------------------------------------------------
530
+ /**
531
+ * The values an integration ACCEPTS for the generation options whose domain is enumerable.
532
+ *
533
+ * A sibling of `capabilities` rather than a change to it, so a definition that declares only tags
534
+ * keeps working unchanged and absent goes on meaning "only the coarse facts are known".
535
+ *
536
+ * **Only the options with a CLOSED domain appear here, and each entry is a SET.** A range is
537
+ * deliberately unrepresentable: `min`/`max`/`step`/`multiple-of` is a constraint language, and
538
+ * the moment one exists it grows to cover Flux's "any pair up to 4 MP in multiples of 32", which
539
+ * is the `resolutionRange` discriminator the design record refuses. An endpoint with a genuine
540
+ * range declares the capability, states no set, and puts its limits in `guidance`: its steps are
541
+ * then judged exactly as they were before this field existed.
542
+ *
543
+ * **An empty list is refused rather than read as "accepts nothing".** Absent is the one spelling
544
+ * of "not stated", so a `[]` that fell out of a filter would otherwise refuse every value the
545
+ * endpoint has, silently, on a declaration whose author meant to say nothing at all.
546
+ */
547
+ export const binaryGeneratorAcceptsSchema = v.object({
548
+ /** The aspect ratios it takes, when it takes a closed set of them. Compared in LOWEST TERMS. */
549
+ aspectRatios: v.optional(v.pipe(v.array(binaryAspectRatioSchema), v.minLength(1), v.maxLength(64))),
550
+ /** The exact `WxH` pairs it renders, when its size parameter is a closed list. */
551
+ outputSizes: v.optional(v.pipe(v.array(binaryOutputSizeSchema), v.minLength(1), v.maxLength(64))),
552
+ /** The upscale factors it takes, when it takes a closed set of them. */
553
+ upscaleFactors: v.optional(v.pipe(v.array(binaryUpscaleFactorSchema), v.minLength(1), v.maxLength(8))),
554
+ });
555
+ /**
556
+ * One value option's three parts, as the ONE table every reader folds over: which capability
557
+ * gates it, how to read the step's request, and how to read an integration's accepted set.
558
+ *
559
+ * A `Record` over the union, so a member added to {@link BinaryValueOption} fails the typecheck
560
+ * here rather than shipping as an option nothing checks. The capability is restated rather than
561
+ * read out of {@link BINARY_OPTION_CAPABILITIES}, whose values are LISTS ("any of these", which
562
+ * is what `edit` and `referenceImages` need); `binary-capabilities.test.ts` pins that each entry
563
+ * here names exactly the one capability that table maps its option to, so the two cannot drift.
564
+ *
565
+ * Values are compared as STRINGS, in the spelling each renders to below, because that is also
566
+ * what a refusal has to say back to a human. The one normalisation is the ratio's: `1920:1080`
567
+ * and `16:9` are the same shape asked for twice, and refusing the first against an endpoint that
568
+ * declared the second would be a false refusal over spelling.
569
+ */
570
+ const BINARY_VALUE_OPTIONS = {
571
+ aspectRatio: {
572
+ capability: 'aspect-ratio',
573
+ requested: (options) => options.aspectRatio ? reduceAspectRatio(options.aspectRatio) : undefined,
574
+ accepted: (accepts) => accepts.aspectRatios?.map(reduceAspectRatio),
575
+ },
576
+ outputSize: {
577
+ capability: 'exact-size',
578
+ requested: (options) => (options.outputSize ? formatOutputSize(options.outputSize) : undefined),
579
+ accepted: (accepts) => accepts.outputSizes?.map(formatOutputSize),
580
+ },
581
+ upscale: {
582
+ capability: 'upscale',
583
+ requested: (options) => (options.upscale === undefined ? undefined : String(options.upscale)),
584
+ accepted: (accepts) => accepts.upscaleFactors?.map(String),
585
+ },
586
+ };
587
+ /** `WxH`, the spelling the brief and every refusal already use for a size. */
588
+ function formatOutputSize(size) {
589
+ return `${size.width}x${size.height}`;
590
+ }
591
+ /**
592
+ * An aspect ratio in lowest terms, so `1920:1080` and `16:9` compare equal.
593
+ *
594
+ * Total by construction: the string has already passed {@link binaryAspectRatioSchema} at both
595
+ * ends (a step's field and an integration's declaration), and a value that somehow has not is
596
+ * returned unchanged rather than dropped, which compares as itself and cannot silently match.
597
+ */
598
+ function reduceAspectRatio(ratio) {
599
+ const match = /^([1-9][0-9]{0,3}):([1-9][0-9]{0,3})$/.exec(ratio.trim());
600
+ if (!match)
601
+ return ratio;
602
+ let a = Number(match[1]);
603
+ let b = Number(match[2]);
604
+ while (b !== 0)
605
+ [a, b] = [b, a % b];
606
+ return `${Number(match[1]) / a}:${Number(match[2]) / a}`;
607
+ }
608
+ /**
609
+ * Judge the VALUES a step asks for against the sets its selected integrations accept.
610
+ *
611
+ * Judged per option and PER DECLARER, over the integrations that declare the gating capability
612
+ * (one that declares none of it is already the coarse axis's answer, and counting it here would
613
+ * report one fault twice). Each declarer is in one of three states, and the disposition is a
614
+ * function of the whole set rather than of the first agreeable member:
615
+ *
616
+ * - **Nobody stated a set.** SILENT. This is the state every registration is in until somebody
617
+ * audits an endpoint, and it is exactly what the platform knew before this field existed. An
618
+ * advisory that fired here would ride nearly every step with an aspect ratio, which is how a
619
+ * line stops being read.
620
+ * - **Every declarer that stated a set contains the value.** Covered, and a declarer that stated
621
+ * nothing beside them stays the pre-field unknown rather than a finding.
622
+ * - **Some stated set contains it and another stated set EXCLUDES it.** PARTIAL. The step is
623
+ * servable and is not refused, for the reason {@link binaryCapabilityCoverage} treats one
624
+ * declarer as covering a capability: which integration renders which artifact is the agent's
625
+ * call, not the platform's. But an excluding set is a DEFINITE fact about a definite endpoint,
626
+ * so it is named rather than absorbed. Absorbing it is the exact silent crop this axis exists
627
+ * to prevent, and it inverted the reporting: the LESS informed selection (a declarer that
628
+ * stated nothing) raised an advisory, so declaring an accurate second set bought silence.
629
+ * - **No stated set contains it, and some declarer stated none.** UNVERIFIABLE. One integration
630
+ * refuses the value and another has not said, so the step may well be served: reported, never
631
+ * refused, the same disposition {@link binaryCapabilityCoverage} gives its own third state.
632
+ * - **No stated set contains it, and every declarer stated one.** UNACCEPTED, and this is the
633
+ * refusal the axis exists for: every endpoint that could serve the option has enumerated what
634
+ * it takes, and this is not among them.
635
+ *
636
+ * Takes the step's OPTIONS rather than a pre-derived requirement list, unlike its neighbours,
637
+ * because the judgement needs the value and not only the capability it implies. It takes each
638
+ * integration's `id` for the same reason {@link binaryCapabilityProviders} does: a partial
639
+ * finding whose remedy is routing is unusable without naming who to route around.
640
+ */
641
+ export function binaryValueCoverage(options, selected) {
642
+ const unaccepted = [];
643
+ const partial = [];
644
+ const unverifiable = [];
645
+ if (!options)
646
+ return { unaccepted, partial, unverifiable };
647
+ for (const [key, entry] of Object.entries(BINARY_VALUE_OPTIONS)) {
648
+ const option = key;
649
+ const requested = entry.requested(options);
650
+ if (requested === undefined)
651
+ continue;
652
+ const declarers = selected.filter((generator) => (generator.capabilities ?? []).includes(entry.capability));
653
+ const stated = [];
654
+ const refusedBy = [];
655
+ let accepting = 0;
656
+ let silent = false;
657
+ for (const generator of declarers) {
658
+ const values = generator.accepts ? entry.accepted(generator.accepts) : undefined;
659
+ if (!values) {
660
+ silent = true;
661
+ continue;
662
+ }
663
+ stated.push(values);
664
+ if (values.includes(requested))
665
+ accepting += 1;
666
+ else
667
+ refusedBy.push(generator.id);
668
+ }
669
+ if (stated.length === 0)
670
+ continue;
671
+ if (accepting > 0) {
672
+ if (refusedBy.length > 0)
673
+ partial.push({ option, requested, refusedBy });
674
+ continue;
675
+ }
676
+ if (silent) {
677
+ unverifiable.push(option);
678
+ continue;
679
+ }
680
+ unaccepted.push({ option, requested, accepted: [...new Set(stated.flat())] });
681
+ }
682
+ return { unaccepted, partial, unverifiable };
683
+ }
684
+ /**
685
+ * The value sets a definition states for options its capabilities do NOT declare.
686
+ *
687
+ * A definition saying which aspect ratios it accepts while declaring no `aspect-ratio` has stated
688
+ * two contradicting facts about one endpoint, and every reader believes a different half:
689
+ * {@link binaryValueCoverage} judges only over the capability's declarers and never sees the set,
690
+ * the agent's brief renders the set as fact beside the integration's formats, and admission
691
+ * refuses every step asking for the option at all. So the accurate half is unreachable and the
692
+ * step is refused for lacking a capability the same registration was documenting.
693
+ *
694
+ * Derived from the same table the coverage rule folds over, so an option added to
695
+ * {@link BinaryValueOption} is checked here with no second edit. Lives beside that table rather
696
+ * than in the boot validator for the reason the table is private: a caller re-deriving which
697
+ * capability gates which set is the drift this returns instead.
698
+ */
699
+ export function binaryAcceptsWithoutCapability(declaration) {
700
+ const accepts = declaration.accepts;
701
+ if (!accepts)
702
+ return [];
703
+ const declared = declaration.capabilities ?? [];
704
+ const missing = [];
705
+ for (const [key, entry] of Object.entries(BINARY_VALUE_OPTIONS)) {
706
+ if (!entry.accepted(accepts))
707
+ continue;
708
+ if (declared.includes(entry.capability))
709
+ continue;
710
+ missing.push({ option: key, capability: entry.capability });
711
+ }
712
+ return missing;
713
+ }
498
714
  //# sourceMappingURL=binary-capabilities.js.map