Skip to content

Commit 455e958

Browse files
authored
feat(registry): register the two scope-choice refusal codes (#29)
* feat(registry): register the two scope-choice refusal codes Add requested_option_unavailable and conflicting_requested_options as search refusals on the dataset_resubmit door (problem details, 422), in the registry and its packaged mirror. * fix(registry): define the scope-choice refusals as Search raises them Each definition covers every case dfo-apis refuses with it: an axis the part does not offer, an option the axis does not list or an unavailable one, and entries that conflict or rule out every option of an axis. The client contract gives both a fix_request row.
1 parent b2987de commit 455e958

5 files changed

Lines changed: 93 additions & 1 deletion

File tree

‎python/client-contract/CHANGELOG.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ All notable changes to the `dfo-spec-kit-client-contract` Python package will be
99
- Package scaffolding (uv, ruff, ty, pytest).
1010
- `served/openapi.json`, the composed dfo-apis app's OpenAPI document, and `served/SOURCE.json`, naming dfo-apis commit `d80df9f5fd598b7345255470932d202ee5d49942`. The document admits `timed_out` as a no_data reason and the optional `entity_refs` on each answer part, a list of `EntityRefs` with one entry per restatement entity row, the graph nodes that row binds, and a stored manifest warning states its counts and the refs it names (`CountedRefsModel`, `CountedEntityYearsModel`, `EntityYearsModel`). `NoDataOption` and `SentenceToken` carry `entity_choice`, the index into `NoData.ambiguous_entities` of the group an entity_ambiguous option settles or a slot token asks, so a client groups a place menu by it when two wordings share one text.
1111
- `strict-fields.json` and its schema: 29 members that stay required and non-nullable, each one a client reads, and the answer kinds, part statuses and operation statuses the clients branch on.
12-
- `actions.json` and its schema: the caller's next step for each of the 102 active refusal codes on `problem_details` or `no_data_reason`, with the gap kind of each no_data reason.
12+
- `actions.json` and its schema: the caller's next step for each of the 104 active refusal codes on `problem_details` or `no_data_reason`, with the gap kind of each no_data reason.
1313
- The status fallback for a refusal whose code has no row: 400 and 422 to `InvalidRequestError`, 401 to `AuthenticationError`, 403 to `PermissionDeniedError`, 404 and 410 to `NotFoundError`, 408 to `DfoTimeoutError`, 429 to `RateLimitError`, and any other 4xx or 5xx to `InvalidRequestError` or `ServerError`.
1414
- `scenario.schema.json`, the scenario record, and 124 scenarios: 34 recorded against the deployed dfo-apis dev app at `d80df9f5f`, and 90 synthetic, each derived from a recorded one by a one-line edit, with 21 binary bodies totalling 1,134,603 bytes and records and bodies together 13,680,926 bytes. `search-place-ambiguous-twice` pins two entity_ambiguous groups of one text, each option and slot token naming its group by `entity_choice`. The scenarios cover the several-part choice entries and the assumptions each identity entry echoes, a picked reading whose assumption Search states in its answer, the place choice through a no_data part's options, the choice refusals, a part without variable_text, an unknown status without a restatement, a shared label, a one-part option whose data_query holds two parts, the separate and operations echo, an unsupported part's placeholder, the timed_out, unsupported and unknown gaps, an unknown answer kind, the `SearchWarning` rule, the stacked dataset's `part` column, the frame layout, a strict fetch's `DataWarning`, a spec fetch that rebinds one of two entity roles and keeps the other, one that drops a role bound to null, a spec whose destination is a named group fetched with a window or with its origin rebound, whose sentence lookup posts the groups of the roles the build left bound, a twice cut-off build, a cut-off build retried after its Retry-After, a build refused 504 then 503 and one refused 503 then 504, a refused build, a build refused 501 `capability_unavailable`, a digest mismatch, a signed URL refused on both rounds, a relative files-route Location, an artifact url on a foreign origin, on the storage host and absolute on the API's origin, a manifest definition without identity or schema_version, a Search spec without identity or resolved, nested specs no rule reads, an empty parts list, a Search body that is not an object, a codeless 503 problem document that is not retryable, a 401, an identity resubmit answering another spec, a part whose status is not a string, a Search spec whose definition names another spec_id, a Search response with no JSON body, an option with no data_query, empty entities and facets, a Sunset header issued once, `x-dfo-warning` messages and a Deprecation and Sunset pair, an unreadable versions page, a mint and a spec fetch whose build answers 202 and is polled to done, a resumed operation, a rate-limited poll, 503 polls retried within the budget an answered poll restores, a 202 that reads running, polls whose `retry_after_ms` changes from one to the next, a 404 poll that raises, a failed operation posted again, a failed operation with no error, and a ref whose stored file expired, read through the file selector, rebuilt at the same version, at a newer one, or refused with the newest version read from the versions route: the first version it lists with no `base_content_version`, the newest plain build, so a derived version listed before it is skipped.
1515
- `client-codes.json` and its schema: the 23 codes a client sets on the errors it raises itself, each with its error class, retryable flag and condition, disjoint from `actions.json` and `registries/codes.json`, and the `client_codes()` and `client_codes_schema()` accessors. Every scenario error the client raises itself carries its code, and `refusal_without_code` names a refusal whose body carries no code.

‎python/client-contract/src/dfo_client_contract/actions.json‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,10 @@
4646
"code": "column_unknown",
4747
"action": "fix_request"
4848
},
49+
{
50+
"code": "conflicting_requested_options",
51+
"action": "fix_request"
52+
},
4953
{
5054
"code": "constraint_violation",
5155
"action": "fix_request"
@@ -292,6 +296,10 @@
292296
"code": "request_invalid",
293297
"action": "fix_request"
294298
},
299+
{
300+
"code": "requested_option_unavailable",
301+
"action": "fix_request"
302+
},
295303
{
296304
"code": "resolution_ambiguous",
297305
"action": "fix_request"

‎python/spec-kit/CHANGELOG.md‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,14 @@ and this package adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
220220
mirror: a 422 refusal on `search.resolve` for a `dataset_resubmit` whose
221221
identity names an open role, when the StatVars the definition elects declare
222222
no role on that axis, so the definition cannot give one series per member.
223+
- Codes `requested_option_unavailable` and `conflicting_requested_options` in
224+
`registries/codes.json` and its bundled mirror: 422 refusals on
225+
`search.resolve` for a `dataset_resubmit` that posts `scope_entries`.
226+
`requested_option_unavailable` covers an entry that names an axis the part
227+
does not offer, an option the axis does not list, or an unavailable option.
228+
`conflicting_requested_options` covers entries that cannot all apply: two
229+
options on one axis, an option that conflicts with an entry on another
230+
axis, or entries that rule out every option of an axis.
223231
- Optional top-level identity member `open_roles` in
224232
`dfo_spec_kit.identity`: a list of flow roles, from `origin` and
225233
`destination`, whose every member the request asks for, as distinct from an

‎python/spec-kit/src/dfo_spec_kit/registry/codes.json‎

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -475,6 +475,25 @@
475475
],
476476
"lifecycle": "active"
477477
},
478+
{
479+
"code": "conflicting_requested_options",
480+
"classification": "refusal",
481+
"definition": "The scope_entries posted for a part cannot all apply: two entries name different options on one axis, an option conflicts with an entry on another axis, or the entries rule out every option of an axis.",
482+
"owning_contract": "search",
483+
"owner": "platform",
484+
"introduced_in": "v1",
485+
"mappings": [
486+
{
487+
"endpoint": "search.resolve",
488+
"doors": [
489+
"dataset_resubmit"
490+
],
491+
"channel": "problem_details",
492+
"http_status": 422
493+
}
494+
],
495+
"lifecycle": "active"
496+
},
478497
{
479498
"code": "conjunction_cross_shape",
480499
"classification": "warning",
@@ -3313,6 +3332,25 @@
33133332
],
33143333
"lifecycle": "active"
33153334
},
3335+
{
3336+
"code": "requested_option_unavailable",
3337+
"classification": "refusal",
3338+
"definition": "A scope_entries entry names an axis the part does not offer, an option the axis does not list, or an option that is unavailable for this part.",
3339+
"owning_contract": "search",
3340+
"owner": "platform",
3341+
"introduced_in": "v1",
3342+
"mappings": [
3343+
{
3344+
"endpoint": "search.resolve",
3345+
"doors": [
3346+
"dataset_resubmit"
3347+
],
3348+
"channel": "problem_details",
3349+
"http_status": 422
3350+
}
3351+
],
3352+
"lifecycle": "active"
3353+
},
33163354
{
33173355
"code": "resolution_ambiguous",
33183356
"classification": "refusal",

‎registries/codes.json‎

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -475,6 +475,25 @@
475475
],
476476
"lifecycle": "active"
477477
},
478+
{
479+
"code": "conflicting_requested_options",
480+
"classification": "refusal",
481+
"definition": "The scope_entries posted for a part cannot all apply: two entries name different options on one axis, an option conflicts with an entry on another axis, or the entries rule out every option of an axis.",
482+
"owning_contract": "search",
483+
"owner": "platform",
484+
"introduced_in": "v1",
485+
"mappings": [
486+
{
487+
"endpoint": "search.resolve",
488+
"doors": [
489+
"dataset_resubmit"
490+
],
491+
"channel": "problem_details",
492+
"http_status": 422
493+
}
494+
],
495+
"lifecycle": "active"
496+
},
478497
{
479498
"code": "conjunction_cross_shape",
480499
"classification": "warning",
@@ -3313,6 +3332,25 @@
33133332
],
33143333
"lifecycle": "active"
33153334
},
3335+
{
3336+
"code": "requested_option_unavailable",
3337+
"classification": "refusal",
3338+
"definition": "A scope_entries entry names an axis the part does not offer, an option the axis does not list, or an option that is unavailable for this part.",
3339+
"owning_contract": "search",
3340+
"owner": "platform",
3341+
"introduced_in": "v1",
3342+
"mappings": [
3343+
{
3344+
"endpoint": "search.resolve",
3345+
"doors": [
3346+
"dataset_resubmit"
3347+
],
3348+
"channel": "problem_details",
3349+
"http_status": 422
3350+
}
3351+
],
3352+
"lifecycle": "active"
3353+
},
33163354
{
33173355
"code": "resolution_ambiguous",
33183356
"classification": "refusal",

0 commit comments

Comments
 (0)