diff --git a/python/client-contract/CHANGELOG.md b/python/client-contract/CHANGELOG.md index ea1159e..cfd2adc 100644 --- a/python/client-contract/CHANGELOG.md +++ b/python/client-contract/CHANGELOG.md @@ -9,7 +9,7 @@ All notable changes to the `dfo-spec-kit-client-contract` Python package will be - Package scaffolding (uv, ruff, ty, pytest). - `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. - `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. -- `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. +- `actions.json` and its schema: the caller's next step for each of the 106 active refusal codes on `problem_details` or `no_data_reason`, with the gap kind of each no_data reason. - 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`. - `scenario.schema.json`, the scenario record, and 126 scenarios: 34 recorded against the deployed dfo-apis dev app at `d80df9f5f`, and 92 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,695,441 bytes. `search-place-ambiguous-twice` pins two entity_ambiguous groups of one text, each option and slot token naming its group by `entity_choice`, and `choose-refused-label-in-two-groups` pins that a label both groups offer matches two options, so a choice there names its option by index. 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 retryable refusal whose Retry-After passes the 60-second cap, 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. - `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. diff --git a/python/client-contract/src/dfo_client_contract/actions.json b/python/client-contract/src/dfo_client_contract/actions.json index cb28717..66a24f5 100644 --- a/python/client-contract/src/dfo_client_contract/actions.json +++ b/python/client-contract/src/dfo_client_contract/actions.json @@ -463,6 +463,14 @@ "code": "shape_mismatch", "action": "fix_request" }, + { + "code": "share_id_taken", + "action": "fix_request" + }, + { + "code": "share_import_not_permitted", + "action": "request_scope" + }, { "code": "share_not_replaceable", "action": "fix_request" diff --git a/python/spec-kit/CHANGELOG.md b/python/spec-kit/CHANGELOG.md index 70e3735..58f5965 100644 --- a/python/spec-kit/CHANGELOG.md +++ b/python/spec-kit/CHANGELOG.md @@ -8,6 +8,14 @@ and this package adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added +- Codes `share_id_taken` (409) and `share_import_not_permitted` (403) on + `presentation.shares` in `registries/codes.json` and its bundled mirror, + for the share import route. `share_id_taken` covers an id that holds + another share, or the same share stored with a different write-token + digest. `share_import_not_permitted` covers a caller that did not send the + import principal, and a deployment with no import principal set. Neither is + retryable. The client contract gives them the `fix_request` and + `request_scope` actions. - `dfo_spec_kit.units` reads a unit ref into one typed value: `Money` (`currency`, `prices`, `base_year`, `chained`), `UnpricedMoney` (`currency`), `Percent`, `Count` or `Other` (`ref`). A ref that leaves diff --git a/python/spec-kit/src/dfo_spec_kit/registry/codes.json b/python/spec-kit/src/dfo_spec_kit/registry/codes.json index 6ec8b03..9e0ae18 100644 --- a/python/spec-kit/src/dfo_spec_kit/registry/codes.json +++ b/python/spec-kit/src/dfo_spec_kit/registry/codes.json @@ -4554,6 +4554,38 @@ ], "lifecycle": "active" }, + { + "code": "share_id_taken", + "classification": "refusal", + "definition": "An import names a share id that holds another share, or the same share stored with a different write-token digest.", + "owning_contract": "presentation", + "owner": "platform", + "introduced_in": "v1", + "mappings": [ + { + "endpoint": "presentation.shares", + "channel": "problem_details", + "http_status": 409 + } + ], + "lifecycle": "active" + }, + { + "code": "share_import_not_permitted", + "classification": "refusal", + "definition": "The caller of a share import did not send the import principal, or no import principal is set on this deployment.", + "owning_contract": "presentation", + "owner": "platform", + "introduced_in": "v1", + "mappings": [ + { + "endpoint": "presentation.shares", + "channel": "problem_details", + "http_status": 403 + } + ], + "lifecycle": "active" + }, { "code": "share_not_replaceable", "classification": "refusal", diff --git a/registries/codes.json b/registries/codes.json index 6ec8b03..9e0ae18 100644 --- a/registries/codes.json +++ b/registries/codes.json @@ -4554,6 +4554,38 @@ ], "lifecycle": "active" }, + { + "code": "share_id_taken", + "classification": "refusal", + "definition": "An import names a share id that holds another share, or the same share stored with a different write-token digest.", + "owning_contract": "presentation", + "owner": "platform", + "introduced_in": "v1", + "mappings": [ + { + "endpoint": "presentation.shares", + "channel": "problem_details", + "http_status": 409 + } + ], + "lifecycle": "active" + }, + { + "code": "share_import_not_permitted", + "classification": "refusal", + "definition": "The caller of a share import did not send the import principal, or no import principal is set on this deployment.", + "owning_contract": "presentation", + "owner": "platform", + "introduced_in": "v1", + "mappings": [ + { + "endpoint": "presentation.shares", + "channel": "problem_details", + "http_status": 403 + } + ], + "lifecycle": "active" + }, { "code": "share_not_replaceable", "classification": "refusal",