matelab-python-sdk 0.1.0a23__tar.gz → 0.2.0a1__tar.gz

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 (67) hide show
  1. {matelab_python_sdk-0.1.0a23 → matelab_python_sdk-0.2.0a1}/CHANGELOG.md +51 -0
  2. {matelab_python_sdk-0.1.0a23 → matelab_python_sdk-0.2.0a1}/CONTEXT.md +5 -0
  3. {matelab_python_sdk-0.1.0a23 → matelab_python_sdk-0.2.0a1}/PKG-INFO +126 -64
  4. {matelab_python_sdk-0.1.0a23 → matelab_python_sdk-0.2.0a1}/README.md +125 -63
  5. {matelab_python_sdk-0.1.0a23 → matelab_python_sdk-0.2.0a1}/contracts/matelab-integration-v1.lock.json +3 -3
  6. {matelab_python_sdk-0.1.0a23 → matelab_python_sdk-0.2.0a1}/contracts/matelab-integration-v1.openapi.yaml +647 -215
  7. {matelab_python_sdk-0.1.0a23 → matelab_python_sdk-0.2.0a1}/docs/agent-workflows.md +5 -2
  8. {matelab_python_sdk-0.1.0a23 → matelab_python_sdk-0.2.0a1}/docs/operation-coverage.yaml +58 -58
  9. {matelab_python_sdk-0.1.0a23 → matelab_python_sdk-0.2.0a1}/pyproject.toml +1 -1
  10. {matelab_python_sdk-0.1.0a23 → matelab_python_sdk-0.2.0a1}/scripts/generate_models.py +11 -3
  11. {matelab_python_sdk-0.1.0a23 → matelab_python_sdk-0.2.0a1}/src/matelab/__init__.py +2 -0
  12. {matelab_python_sdk-0.1.0a23 → matelab_python_sdk-0.2.0a1}/src/matelab/_generated/models.py +785 -210
  13. {matelab_python_sdk-0.1.0a23 → matelab_python_sdk-0.2.0a1}/src/matelab/_wire_validation.py +30 -2
  14. {matelab_python_sdk-0.1.0a23 → matelab_python_sdk-0.2.0a1}/src/matelab/domains/cloud_drive.py +1 -1
  15. {matelab_python_sdk-0.1.0a23 → matelab_python_sdk-0.2.0a1}/src/matelab/domains/groups.py +6 -2
  16. {matelab_python_sdk-0.1.0a23 → matelab_python_sdk-0.2.0a1}/src/matelab/domains/literature.py +12 -2
  17. {matelab_python_sdk-0.1.0a23 → matelab_python_sdk-0.2.0a1}/src/matelab/domains/notebooks.py +4 -0
  18. {matelab_python_sdk-0.1.0a23 → matelab_python_sdk-0.2.0a1}/src/matelab/domains/records.py +64 -26
  19. {matelab_python_sdk-0.1.0a23 → matelab_python_sdk-0.2.0a1}/src/matelab/domains/templates.py +1 -1
  20. {matelab_python_sdk-0.1.0a23 → matelab_python_sdk-0.2.0a1}/src/matelab/streaming.py +1 -0
  21. {matelab_python_sdk-0.1.0a23 → matelab_python_sdk-0.2.0a1}/tests/test_client.py +25 -0
  22. {matelab_python_sdk-0.1.0a23 → matelab_python_sdk-0.2.0a1}/tests/test_cloud_drive.py +10 -5
  23. {matelab_python_sdk-0.1.0a23 → matelab_python_sdk-0.2.0a1}/tests/test_comments_and_downloads.py +62 -1
  24. {matelab_python_sdk-0.1.0a23 → matelab_python_sdk-0.2.0a1}/tests/test_extended_records.py +32 -4
  25. {matelab_python_sdk-0.1.0a23 → matelab_python_sdk-0.2.0a1}/tests/test_generation.py +122 -12
  26. {matelab_python_sdk-0.1.0a23 → matelab_python_sdk-0.2.0a1}/tests/test_groups.py +4 -1
  27. {matelab_python_sdk-0.1.0a23 → matelab_python_sdk-0.2.0a1}/tests/test_notebook_lifecycle.py +33 -0
  28. {matelab_python_sdk-0.1.0a23 → matelab_python_sdk-0.2.0a1}/tests/test_record_lifecycle.py +1 -1
  29. {matelab_python_sdk-0.1.0a23 → matelab_python_sdk-0.2.0a1}/tests/test_record_mutations.py +119 -2
  30. {matelab_python_sdk-0.1.0a23 → matelab_python_sdk-0.2.0a1}/tests/test_records.py +75 -4
  31. {matelab_python_sdk-0.1.0a23 → matelab_python_sdk-0.2.0a1}/tests/test_template_lifecycle.py +31 -0
  32. {matelab_python_sdk-0.1.0a23 → matelab_python_sdk-0.2.0a1}/tests/test_transport.py +12 -15
  33. {matelab_python_sdk-0.1.0a23 → matelab_python_sdk-0.2.0a1}/tests/test_uploads_and_literature.py +42 -0
  34. {matelab_python_sdk-0.1.0a23 → matelab_python_sdk-0.2.0a1}/uv.lock +1 -1
  35. {matelab_python_sdk-0.1.0a23 → matelab_python_sdk-0.2.0a1}/.env.example +0 -0
  36. {matelab_python_sdk-0.1.0a23 → matelab_python_sdk-0.2.0a1}/.github/workflows/release.yml +0 -0
  37. {matelab_python_sdk-0.1.0a23 → matelab_python_sdk-0.2.0a1}/.gitignore +0 -0
  38. {matelab_python_sdk-0.1.0a23 → matelab_python_sdk-0.2.0a1}/AGENTS.md +0 -0
  39. {matelab_python_sdk-0.1.0a23 → matelab_python_sdk-0.2.0a1}/LICENSE +0 -0
  40. {matelab_python_sdk-0.1.0a23 → matelab_python_sdk-0.2.0a1}/NOTICE +0 -0
  41. {matelab_python_sdk-0.1.0a23 → matelab_python_sdk-0.2.0a1}/docs/agent-architecture.md +0 -0
  42. {matelab_python_sdk-0.1.0a23 → matelab_python_sdk-0.2.0a1}/scripts/check_installed_package.py +0 -0
  43. {matelab_python_sdk-0.1.0a23 → matelab_python_sdk-0.2.0a1}/scripts/check_release.py +0 -0
  44. {matelab_python_sdk-0.1.0a23 → matelab_python_sdk-0.2.0a1}/src/matelab/_generated/__init__.py +0 -0
  45. {matelab_python_sdk-0.1.0a23 → matelab_python_sdk-0.2.0a1}/src/matelab/_hashes.py +0 -0
  46. {matelab_python_sdk-0.1.0a23 → matelab_python_sdk-0.2.0a1}/src/matelab/_transport.py +0 -0
  47. {matelab_python_sdk-0.1.0a23 → matelab_python_sdk-0.2.0a1}/src/matelab/client.py +0 -0
  48. {matelab_python_sdk-0.1.0a23 → matelab_python_sdk-0.2.0a1}/src/matelab/domains/__init__.py +0 -0
  49. {matelab_python_sdk-0.1.0a23 → matelab_python_sdk-0.2.0a1}/src/matelab/domains/_attachment_content.py +0 -0
  50. {matelab_python_sdk-0.1.0a23 → matelab_python_sdk-0.2.0a1}/src/matelab/domains/uploads.py +0 -0
  51. {matelab_python_sdk-0.1.0a23 → matelab_python_sdk-0.2.0a1}/src/matelab/domains/users.py +0 -0
  52. {matelab_python_sdk-0.1.0a23 → matelab_python_sdk-0.2.0a1}/src/matelab/errors.py +0 -0
  53. {matelab_python_sdk-0.1.0a23 → matelab_python_sdk-0.2.0a1}/src/matelab/py.typed +0 -0
  54. {matelab_python_sdk-0.1.0a23 → matelab_python_sdk-0.2.0a1}/src/matelab/session.py +0 -0
  55. {matelab_python_sdk-0.1.0a23 → matelab_python_sdk-0.2.0a1}/tests/conftest.py +0 -0
  56. {matelab_python_sdk-0.1.0a23 → matelab_python_sdk-0.2.0a1}/tests/provider/test_provider_smoke.py +0 -0
  57. {matelab_python_sdk-0.1.0a23 → matelab_python_sdk-0.2.0a1}/tests/test_async_attachment_upload.py +0 -0
  58. {matelab_python_sdk-0.1.0a23 → matelab_python_sdk-0.2.0a1}/tests/test_consumer_smoke.py +0 -0
  59. {matelab_python_sdk-0.1.0a23 → matelab_python_sdk-0.2.0a1}/tests/test_errors.py +0 -0
  60. {matelab_python_sdk-0.1.0a23 → matelab_python_sdk-0.2.0a1}/tests/test_notebooks.py +0 -0
  61. {matelab_python_sdk-0.1.0a23 → matelab_python_sdk-0.2.0a1}/tests/test_operation_coverage.py +0 -0
  62. {matelab_python_sdk-0.1.0a23 → matelab_python_sdk-0.2.0a1}/tests/test_release.py +0 -0
  63. {matelab_python_sdk-0.1.0a23 → matelab_python_sdk-0.2.0a1}/tests/test_streaming.py +0 -0
  64. {matelab_python_sdk-0.1.0a23 → matelab_python_sdk-0.2.0a1}/tests/test_templates.py +0 -0
  65. {matelab_python_sdk-0.1.0a23 → matelab_python_sdk-0.2.0a1}/tests/test_users.py +0 -0
  66. {matelab_python_sdk-0.1.0a23 → matelab_python_sdk-0.2.0a1}/tests/typing/consumer.py +0 -0
  67. {matelab_python_sdk-0.1.0a23 → matelab_python_sdk-0.2.0a1}/tests/typing/pyproject.toml +0 -0
@@ -3,6 +3,57 @@
3
3
  This file records release-to-release changes and migration impact. It is not the authority for the current interface;
4
4
  see [README.md](README.md) for current behavior and [AGENTS.md](AGENTS.md) for maintenance policy.
5
5
 
6
+ ## 0.2.0a1 - 2026-10-02
7
+
8
+ - Establishes the `0.x` versioning rule: incompatible public changes advance the SDK minor version; alpha/beta/RC
9
+ counters identify successive candidates for the same target version. SDK and Contract versions remain independent.
10
+ - Adopts immutable `matelab-spec v2026.10.1` at commit `9941e2d623a1c2633adb459b823d78d4e62d3802`
11
+ (annotated tag object `ae8098e1b5178b69706bddd8b70c1b4cfe3e09ba`) and OpenAPI SHA-256
12
+ `a07997128c1df17a03724c5af8e11b45a34166f9d6e15558e66e0a301f2e0813`, aligned with Provider evidence
13
+ `3f39d47b7ad43bed618b029a7d9cc51fab852eb6`. The Contract's calendar version identifies a Provider-specific
14
+ snapshot verified on an isolated Workerman instance with local data servers.
15
+ - Breaking: record JSON operations accept business code `0` only; Provider-internal forwarding code `10` is outside
16
+ the verified callable scope and raises `MatelabProtocolError` (PVD-049). Search aliases additionally reserve
17
+ `eln_id`, `title`, and `eln_name`; extraction paths reject numeric strings, negative indices and a numeric fourth
18
+ segment before HTTP. Generation retains these referenced scalar and positional JSON Schema constraints.
19
+ - Adds `Record.encrypted_modules` and public `EncryptedRecordModules`, preserving ciphertext and additive fields
20
+ without decryption. Encrypted reads have no plaintext modules or attachment refs. State-dependent updates reject
21
+ encrypted content locally; stateless module changes retain the Provider's pre-write password check. Unresolved
22
+ author descriptors map to `author_name=None` while remaining validated in the private wire layer (PVD-044).
23
+ - Restores `RecordFilesAttachmentAppend.folder_path` after the dedicated shape became valid within the top-level
24
+ update request. A non-empty path uses the staged-name finalizer, validates safe segments and requires an exclusive
25
+ mutation; reuse and ambiguous-finalization protections remain in effect (PVD-030). Inline base64 file input remains
26
+ outside the public write boundary (PVD-042).
27
+ - Breaking: `GroupSummary.ref` can be `None` for a dangling membership (PVD-048), and `CloudFile.modified_at` is now
28
+ a required string because stored null timestamps fail before a successful Provider row is returned (PVD-047).
29
+ Notebook reads accept non-null JSON type settings, but metadata updates reject settings or extraction requests that
30
+ cannot be preserved within the write Contract before mutation (PVD-046).
31
+ - Literature metadata extraction maps nested Provider errors to the usual safe SDK errors. Empty or opaque lists
32
+ raise `MatelabProtocolError` instead of being presented as publication candidates (PVD-045).
33
+ - Allows zero-byte comment attachment discovery/downloads and documents the 4097-byte cap on local open ranges
34
+ beginning at zero. Download metadata remains available for explicit continuation; no automatic replay is added.
35
+ - Retains the existing read-only missing table-attachment timestamp containment and the unexposed password,
36
+ signing, record-title-update and structured-error-detail capabilities.
37
+
38
+ ## 0.1.0a24 - 2026-09-26
39
+
40
+ - Adopts immutable `matelab-spec v0.5.0` at commit `4f2959472933da47d1ea3e275552884d51d4fdd9`
41
+ (annotated tag object `cc362a9a36601f5d07d6e90c2c6994e33c202d88`) and OpenAPI SHA-256
42
+ `dee14f76e24b2d7c3d97a6f30a9369ac093d615d132db85836ab86671eadcf18`, aligned with Provider source evidence
43
+ `acb0e12f2d62a8959ab9e9bddf38e34a67c19b48`. The release evidence covers an isolated Provider, not production.
44
+ - Breaking: `templates.save_content()` rejects invalid JSON in JSON code modules with non-null `style` before HTTP.
45
+ Generated write models and whole-request validation now enforce nested JSON content schemas. Readback continues to
46
+ preserve raw code strings (including empty or non-JSON text) and null; reading a module does not guarantee it can
47
+ be saved unchanged. JSON code without a style, or with null style, retains its existing write boundary.
48
+ - Regenerates the private application bootstrap fields and credential-login restriction to an omitted or empty
49
+ WeChat code. Public credential login already omits that input. Record module creation preserves explicit code
50
+ strings and leaves omitted/null initialization to the Provider, which now uses an empty string without synthesizing
51
+ an ECharts series object.
52
+ - Documents Chat SSO expiry against the user's mutable activity timestamp, single-attempt exchange, and lack of
53
+ concurrent one-time guarantees. Synchronizes the release's PVD/PCG renumbering and retired comment-upload issue.
54
+ - Keeps staged files-module append root-only because its dedicated schema and `UpdateRecordRequest.add[]` still
55
+ disagree about folder paths (now PVD-030). Retains the read-only missing table-attachment timestamp compatibility.
56
+
6
57
  ## 0.1.0a23 - 2026-09-04
7
58
 
8
59
  - Temporarily accepts `readRecord` table file attachments that contain valid `filename`, `hash`, and `size` fields but
@@ -85,6 +85,11 @@ A template-shaped value composed of JSON-compatible scalars and containers, with
85
85
  allowed wherever the template expects a file.
86
86
  _Avoid_: Arbitrary object, stored record module, Provider attachment reference
87
87
 
88
+ **Encrypted record modules**:
89
+ Stored ciphertext returned without decryption, separate from a record's available plaintext modules and attachments.
90
+ The encrypted descriptor is read evidence; it does not provide a decryption or password management interface.
91
+ _Avoid_: Empty record content, decrypted modules
92
+
88
93
  **Staged attachment**:
89
94
  An upload prepared in a selected notebook for one later record import or update; uploading does not choose the target
90
95
  record.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: matelab-python-sdk
3
- Version: 0.1.0a23
3
+ Version: 0.2.0a1
4
4
  Summary: Reusable async Python client for the Matelab Integration Contract
5
5
  Author-email: 朱天念 <zhutiannian@gmail.com>
6
6
  License-Expression: Apache-2.0
@@ -35,12 +35,14 @@ This README describes the current SDK interface, integration semantics, examples
35
35
  [AGENTS.md](AGENTS.md) for durable maintenance rules, and `docs/operation-coverage.yaml` for the machine-readable
36
36
  operation inventory.
37
37
 
38
- The current alpha is `0.1.0a23`. `[project].version` in `pyproject.toml` is the sole SDK version source;
38
+ The current alpha is `0.2.0a1`. `[project].version` in `pyproject.toml` is the sole SDK version source;
39
39
  `uv.lock` only mirrors that source.
40
40
 
41
- The SDK is pinned to the immutable `matelab-spec v0.4.5` Contract Release. The sole release pin is
42
- `contracts/matelab-integration-v1.lock.json`, which records
43
- the source tag, commit, OpenAPI path, local snapshot path, and SHA-256.
41
+ The SDK is pinned to the immutable `matelab-spec v2026.10.1` Contract Release. The sole pin,
42
+ `contracts/matelab-integration-v1.lock.json`, records peeled commit `9941e2d623a1c2633adb459b823d78d4e62d3802`,
43
+ source `openapi/matelab-integration-v1.openapi.yaml`, the local snapshot path, and SHA-256
44
+ `a07997128c1df17a03724c5af8e11b45a34166f9d6e15558e66e0a301f2e0813`. The annotated tag object is
45
+ `ae8098e1b5178b69706bddd8b70c1b4cfe3e09ba`.
44
46
 
45
47
  ## Installation
46
48
 
@@ -64,15 +66,32 @@ To test the same artifact a downstream Consumer will install, build and install
64
66
 
65
67
  ```bash
66
68
  uv build --no-build-isolation --out-dir dist/release
67
- python -m pip install dist/release/matelab_python_sdk-0.1.0a23-py3-none-any.whl
69
+ python -m pip install dist/release/matelab_python_sdk-0.2.0a1-py3-none-any.whl
68
70
  ```
69
71
 
70
72
  Do not infer Provider compatibility from the SDK version alone. A release is also bound to the Contract
71
73
  tag, commit, and checksum recorded below.
72
74
 
73
- The Contract and public SDK interface track the current reviewed Provider at
74
- `59eb05b233e5ddd023a60ccb5e5c5f2ae22a70f0`. Provider limitations that remain relevant to integrations are documented
75
- alongside the affected interfaces below.
75
+ The Contract uses Provider source evidence from `3f39d47b7ad43bed618b029a7d9cc51fab852eb6`; v2026.10.1 validation ran
76
+ against an isolated test instance and does not establish production behavior. The corresponding Front Consumer evidence
77
+ is `0c985f5b6da8e8ef4627ca7065cea85e408ae981`; it informs cross-Consumer capability review but does not define the SDK
78
+ interface. Provider limitations that remain relevant to integrations are documented alongside the affected interfaces
79
+ below.
80
+
81
+ Contract releases now use calendar versions to identify Provider-specific snapshots. This snapshot covers the
82
+ Workerman runtime with the `localhost` data-server branch for ELN records and attachments; remote data-server
83
+ forwarding is outside its verified callable scope (PVD-049).
84
+
85
+ ## Versioning
86
+
87
+ SDK versions express compatibility of the public Python interface and behavior. Python prerelease spelling follows
88
+ PEP 440: `0.2.0a1` is the first alpha candidate for `0.2.0`. During `0.x` development, incompatible changes advance
89
+ the minor version. From `1.0.0`, compatible fixes advance patch, compatible additions advance minor, and incompatible
90
+ changes advance major. Successive candidates for the same target version use `aN`, `bN`, or `rcN`.
91
+
92
+ Contract calendar versions identify Provider wire snapshots independently. Each SDK release records its exact
93
+ Contract tag, commit and checksum in the lock; multiple SDK fixes can use the same Contract release. Adopting a new
94
+ Contract changes the SDK version according to its public impact. See [CHANGELOG.md](CHANGELOG.md) for migration details.
76
95
 
77
96
  ## Design
78
97
 
@@ -107,7 +126,7 @@ category:
107
126
  | `MatelabUsageError` | `VALIDATION` | No | The call cannot be represented safely; correct its arguments. |
108
127
  | `MatelabProviderError` | `BUSINESS`, `VALIDATION`, or `UPSTREAM` | No | The Provider rejected the request; record `provider_code` and route recovery by `category`; `provider_message` is an SDK-owned safe summary. |
109
128
  | `MatelabTransportError` | `UPSTREAM` | No | The HTTP exchange failed; `status_code` is present for HTTP failures, and a mutation outcome may be unknown. |
110
- | `MatelabProtocolError` | `UPSTREAM` | No | The response violates the pinned Contract; treat it as Provider drift or an SDK defect. |
129
+ | `MatelabProtocolError` | `UPSTREAM` | No | The response violates the pinned Contract or cannot supply the structured result required by the SDK operation; inspect the Provider result or SDK boundary. |
111
130
 
112
131
  For `MatelabProviderError`, the SDK maps Provider wire code `2` to `BUSINESS`, `4` to `VALIDATION`, and `3` or an
113
132
  unknown code to `UPSTREAM`. `provider_code` is diagnostic metadata, not a stable routing identifier, and integrations
@@ -116,12 +135,12 @@ selected from the code mapping; they do not reproduce Provider-authored text. Au
116
135
  `MatelabAuthenticationError`; code `5`
117
136
  triggers at most one refresh and replay only for authenticated operations that explicitly enable
118
137
  `retry_on_access_expired`, otherwise codes `1` and `5` raise `MatelabAuthenticationError` directly.
119
- Codes `0` and `10` remain operation-specific successes selected by the pinned Contract; an operation that receives a
120
- success code it does not allow raises `MatelabProtocolError` rather than assigning an error category.
138
+ Record operations accept business code `0`. Provider-internal forwarding code `10` is outside the pinned
139
+ callable Contract and raises `MatelabProtocolError` rather than assigning an error category.
121
140
  `MatelabTransportError.status_code` remains available for HTTP failures. Provider response bodies, top-level `msg`,
122
141
  legacy `errmsg`, opaque `errs`, uncontracted debug fields, and request payloads are not attached to exceptions because
123
142
  Provider-authored diagnostics may contain stack traces, credentials, or submitted values.
124
- The pinned v0.4.5 Contract also declares optional code-4 `errs`, but PVD-041 and PCG-015 record opaque array/object
143
+ The pinned v2026.10.1 Contract also declares optional code-4 `errs`, but PVD-029 and PCG-010 record opaque array/object
125
144
  containers and insufficient evidence for requiredness, cardinality, or exclusion from other codes. The SDK
126
145
  therefore keeps those diagnostics wire-only and does not expose `errs` as stable public metadata. A typed detail
127
146
  interface remains blocked until a future Contract release can define one from stronger Provider evidence. `retryable`
@@ -315,11 +334,10 @@ These three operations use the Provider's staged-name finalizer and must be the
315
334
  supports one file column. A locally rejected target does not spend the handle; once transport starts, an ambiguous
316
335
  result cannot be retried through the same client.
317
336
 
318
- Files-module append and table/files replacement use the general record attachment intents and may be combined with
319
- other non-conflicting record changes. `RecordFilesAttachmentAppend` currently targets the module root. Although
320
- v0.4.5 records Provider evidence for safe folder paths, its top-level `UpdateRecordRequest.add[]` schema still excludes
321
- the `path` property declared by the dedicated staged-name append shape; the SDK therefore does not expose folder paths
322
- until that wire-schema conflict is resolved (PVD-044):
337
+ Files-module append at the module root and table/files replacement may be combined with other non-conflicting record
338
+ changes. `RecordFilesAttachmentAppend.folder_path` optionally selects folders within the files module. A non-empty
339
+ path uses the staged-name finalizer and must be the only mutation in its update request; the SDK validates every
340
+ segment before writing to avoid the Provider's malformed-path fallback to the module root (PVD-030):
323
341
 
324
342
  ```python
325
343
  from matelab import RecordAttachmentReplacement, RecordFilesAttachmentAppend, RecordValueUpdate
@@ -333,6 +351,20 @@ await client.records.update(
333
351
  )
334
352
  ```
335
353
 
354
+ To append within a folder, use a fresh upload in a separate update:
355
+
356
+ ```python
357
+ await client.records.update(
358
+ source,
359
+ (RecordFilesAttachmentAppend(module="Files", attachment=folder_uploaded, folder_path=("Evidence", "Raw Data")),),
360
+ )
361
+ ```
362
+
363
+ For `RecordModuleCreation(module_type="code", language="json", style="echarts")`, omitted, null, and empty `data` all
364
+ produce an empty string; explicit `"0"` stays `"0"`. Send the desired initial chart configuration as a JSON code string.
365
+ Richtext/code module creation defaults missing `data` to `""`; form/table/files creation ignores it and initializes
366
+ content as `[]`.
367
+
336
368
  A replacement starts with an occurrence returned by `records.read()`; applications must not fabricate a
337
369
  `RecordAttachmentRef`. The same `RecordAttachmentReplacement` covers both table-file and files-module occurrences:
338
370
 
@@ -436,7 +468,8 @@ and `deletePersonalLiteratureItem`.
436
468
  Every other implemented operation is explicitly `experimental`; the exact per-operation list and its
437
469
  PVD/PCG references live in
438
470
  `docs/operation-coverage.yaml`. `readApplicationBootstrap` is intentionally unexposed because it returns the Provider
439
- application environment and menu model, while SDK identity is sourced from token issuance. `readLiteratureCreateTemplate`
471
+ application environment and menu model, including required `data_express_enable` and `data_express_text` fields, while
472
+ SDK identity is sourced from token issuance. `readLiteratureCreateTemplate`
440
473
  is intentionally unexposed because it returns a frontend form definition and an optional deployment flag rather than
441
474
  a resource needed by the SDK interface. There are no `planned` operations. Experimental support means the SDK
442
475
  validates and exposes the pinned Contract while preserving limitations such as unstable ordering/pagination,
@@ -450,6 +483,13 @@ automatically retried, and the returned token set is stored in the same in-memor
450
483
  session = await client.exchange_chat_sso_code(code="chat-sanitizedcode123", key="sanitized-shared-key")
451
484
  ```
452
485
 
486
+ The code suffix must be non-empty and the shared key must be non-blank. The Provider rejects a code after the current
487
+ user's `active_datetime` plus 300 seconds; that timestamp can change through other user activity, so Consumers must not
488
+ derive a deadline from the code or automatically retry an uncertain exchange. A sequential replay is rejected, but the
489
+ Provider's read-then-clear implementation does not guarantee that concurrent exchanges have only one winner.
490
+ Ordinary credential login does not expose the non-empty WeChat binding code branch, which can reassign another account's
491
+ OpenID (PVD-032).
492
+
453
493
  Group and user discovery expose sharing identities without inventing Provider pagination:
454
494
 
455
495
  ```python
@@ -460,9 +500,11 @@ targets = await client.users.search("Example Researcher", global_scope=False)
460
500
  `groups.list()` returns the `GroupSummary` tuple directly. Ordering remains Provider-unspecified and is documented
461
501
  rather than repeated as a constant result field. The Provider's members for an unstable first group remain wire-only;
462
502
  `users.search()` is the public recipient-discovery interface. These two discovery interfaces are experimental because
463
- group order is unstable and user search is unpaged, unordered and not field-minimized (PVD-006, PVD-029).
503
+ group order is unstable and user search is unpaged, unordered and not field-minimized (PVD-017, PVD-025).
464
504
  `GroupSummary.notebook_creation_available` expresses whether the group can currently host notebook creation without
465
505
  exposing the Provider's data-server routing value.
506
+ An observed dangling group membership has `GroupSummary.ref=None` and no usable group identity. If such a membership
507
+ is selected as the Provider's first group, the whole request can fail with a Provider error (PVD-048).
466
508
 
467
509
  Notebook create and direct sharing use the Provider acknowledgement without an automatic follow-up read:
468
510
 
@@ -485,9 +527,12 @@ Consequently, update performs one list pre-read followed by the write. This pres
485
527
  Provider offers no compare-and-swap or atomic read-modify-write operation. Call `list()` or `shares()` explicitly when
486
528
  the application needs current state. `NotebookPermissions` normalizes the Provider's stored and effective masks and
487
529
  is reused by record pages instead of maintaining a second nearly identical permission model. Direct shares always
488
- have implicit read access even when every optional permission is false (PVD-010).
530
+ have implicit read access even when every optional permission is false (PVD-037).
489
531
  `provider_signing_allowed` only represents Provider policy, not an SDK signing operation. Share-list order remains
490
532
  unspecified.
533
+ Stored notebook type configuration may decode to any non-null JSON value, but metadata writes accept arrays only.
534
+ If the stored configuration or display extraction requests cannot be preserved within the write Contract, update
535
+ raises `MatelabProtocolError` before sending a mutation instead of silently replacing them (PVD-046).
491
536
 
492
537
  Template discovery keeps a template database identity separate from direct-share, market-acquisition, and group
493
538
  relation identities:
@@ -510,17 +555,19 @@ await client.templates.save_content(template, modules)
510
555
  `TemplateModule.data_present` distinguishes a missing `data` property from an explicit null value, while
511
556
  `attributes` prevents additive canonical properties from being dropped during a read/save cycle. The SDK does not
512
557
  synthesize frontend editor UIDs, layout widths, rows, folders, option encoding, or a fixed set of display module
513
- types. A JSON-language code module with style metadata may return its JSON string or explicit `null`; number and boolean
514
- table cells preserve the Provider's empty string as distinct from scalar and `null` values. The Provider accepts some
558
+ types. A JSON-language code module's `data` remains code text and may be empty, null, or invalid JSON;
559
+ `language=json` does not imply that Consumers can parse it. When template content contains a code module with
560
+ `language=json` and non-null `style`, its code text must be valid JSON to save. Number and boolean table cells preserve
561
+ the Provider's empty string as distinct from scalar and `null` values. The Provider accepts some
515
562
  non-canonical module roots and arbitrary table file arrays, but the SDK does not expose that behavior: saved content
516
- must remain a canonical module list, and a table file cell must be one canonical attachment object or `null` (PVD-046).
517
- The Provider supports only whole-content replacement and has no atomic patch or compare-and-swap operation (PCG-007),
563
+ must remain a canonical module list, and a table file cell must be one canonical attachment object or `null` (PVD-013).
564
+ The Provider supports only whole-content replacement and has no atomic patch or compare-and-swap operation (PCG-008),
518
565
  so a read-modify-save sequence can overwrite a concurrent change.
519
566
 
520
567
  The market result retains the Provider's `total_count` and derives `has_more` without echoing the caller's page
521
568
  arguments; it does not claim a stable order or continuation token. Template reads remain experimental
522
569
  because Provider discovery ordering/pagination and historical `images` compatibility are not fully stable
523
- (PCG-003, PCG-009, PVD-013, PVD-022).
570
+ (PCG-011, PCG-012, PVD-038, PVD-022).
524
571
 
525
572
  `templates.list()` returns one tuple. `TemplateSummary.scope` is limited to `owned`, `shared`, `group`, and `public`;
526
573
  Provider marketplace acquisitions and system defaults are both public templates. An acquired public template carries
@@ -535,7 +582,7 @@ transaction. Creation returns the new `TemplateRef`; metadata replacement and ot
535
582
  the Provider supplies no new resource representation. `update()` therefore requires the complete title, summary, and
536
583
  keyword set rather than implying a partial patch. `update_usage()` generates the Provider's required fresh hidden
537
584
  correlation value internally. Usage attachments remain unsupported until their staging and binding lifecycle is
538
- contracted safely (PVD-026). Marketplace revision limitations remain explicit through `content_version`,
585
+ contracted safely (PVD-003). Marketplace revision limitations remain explicit through `content_version`,
539
586
  `published_content_version`, and `market_review_reason` (PVD-021).
540
587
 
541
588
  Extended record reads stay behind the same `records` interface:
@@ -555,7 +602,10 @@ relations = await client.records.relations(source)
555
602
  as `modules`; it is not the template-shaped `RecordImportItem.data` accepted by record import.
556
603
  `records.search()` maps each requested field name to a stored record path. A match exposes only Provider-returned
557
604
  entries in `values`, so key membership distinguishes an absent result from an explicit JSON `null`.
558
- The current Provider excludes encrypted records from search (PVD-043); an empty search result therefore does
605
+ Extraction aliases cannot use `id`, `uid`, `eln_id`, `title`, or `eln_name`, which would overwrite result metadata
606
+ (PVD-043). Paths contain one to four segments: the first three accept non-empty, non-numeric names or non-negative
607
+ integer indices; a fourth segment must be a non-numeric property name. Numeric strings are rejected before HTTP.
608
+ The current Provider excludes encrypted records from search (PVD-041); an empty search result therefore does
559
609
  not prove that no matching encrypted record exists.
560
610
 
561
611
  The Provider calls its notebook directory tree `subtype`; that name remains wire-only. The SDK exposes
@@ -565,7 +615,7 @@ The Provider calls its notebook directory tree `subtype`; that name remains wire
565
615
  positive folder ID for one folder.
566
616
 
567
617
  `records.page` fixes the legacy request to `page_size=0&default=1`, preventing the known owner-preference writes
568
- described by PVD-039; its total is derived internally from the Provider's complete matching ID list. The result keeps
618
+ described by PVD-028; its total is derived internally from the Provider's complete matching ID list. The result keeps
569
619
  semantic `NotebookPermissions` directly, without a shallow context model or duplicated notebook/owner/template
570
620
  display metadata. Effective request pagination, the UI selection list and Provider keyword-filter echo are not exposed.
571
621
  Its `search` argument, shared with `records.list()`, maps the Provider's wire-level `content` filter without exposing
@@ -578,8 +628,7 @@ Public summaries keep source notebook attribution but omit the Provider's unused
578
628
  email; their `notebook_title` follows the same naming used by export and search results. Relation refs retain only the
579
629
  stored relation row and declared target IDs; nullable JOIN previews and resolved target identity stay wire-only.
580
630
  Search and relation order remain unspecified, and no continuation token is invented. Export and search return tuples
581
- directly; Provider code `10` only reports transparent data-server forwarding and does not create a second public result
582
- shape.
631
+ directly. Remote data-server forwarding code `10` is rejected because only local data-server results are covered.
583
632
  `DeletedRecordPage` does not repeat its input notebook.
584
633
 
585
634
  Record creation keeps blank creation and structured import as separate capabilities:
@@ -603,10 +652,10 @@ imported = await client.records.import_dataset(
603
652
 
604
653
  Blank-record creation returns `None` because the Provider returns no record identity, even when the caller supplies a
605
654
  UID. Import validates the complete batch with generated wire models but cannot map returned database IDs to
606
- individual inputs or promise atomicity (PCG-008); it therefore returns only the tuple of database IDs actually
655
+ individual inputs or promise atomicity (PCG-009); it therefore returns only the tuple of database IDs actually
607
656
  reported by the Provider. `RecordImportItem.keywords` is a tuple; the adapter owns the Provider's
608
657
  legacy semicolon encoding. Folder paths are ordered existing folder titles from the notebook root; they do not create
609
- missing folders. The current Provider rejects explicit-null table-file imports (PVD-045). Because the SDK accepts
658
+ missing folders. The current Provider rejects explicit-null table-file imports (PVD-031). Because the SDK accepts
610
659
  arbitrary template-shaped data and cannot identify file columns without the template definition, integrations must omit
611
660
  an unset file-valued key instead of sending it as `None`. Delete means moving records into the recycle bin, not permanent
612
661
  deletion. Delete and restore return `None`; record mutations are not automatically retried.
@@ -618,19 +667,24 @@ without a readback request. Omitting `target_notebook` copies within the source
618
667
  Record patching exposes a deliberately narrower capability than the raw Provider operation. Scalar/module changes
619
668
  cannot smuggle Provider-native attachment strings; staged attachments use separate form-removal, table-replacement,
620
669
  files append/replace/remove, and rich-text types. Unsafe form replacement and table-file removal are absent, while a
621
- files/images removal is rejected when the observed module contains the same hash more than once (PVD-014 through
622
- PVD-016). The SDK reads current content only when a change needs attachment type, occurrence, row, or index state;
670
+ files/images removal is rejected when the observed module contains the same hash more than once (PVD-010, PVD-018,
671
+ and PVD-019). The SDK reads current content only when a change needs attachment type, occurrence, row, or index state;
623
672
  ordinary module creation and deletion rely on the Provider's own canonical update validation. `records.update()`
624
673
  returns the acknowledgement classification
625
674
  `"persisted"`, `"pending"`, or `"unknown"`; it does not
626
675
  issue a post-write read. Database, active-browser, and unclassified acknowledgements remain distinct, and mutation
627
- retries stay disabled. Encrypted record content is outside the SDK's public read/update interface: the list-level
628
- `RecordSummary.encrypted` flag lets callers identify and skip it, while the Provider password field and frontend-only
629
- password lifecycle remain unexposed. Files-module attachment append remains root-only because the pinned top-level
630
- update request schema does not admit the dedicated staged-name append shape's folder `path`; malformed path containers
631
- may also silently append at root according to PVD-044. A staged attachment is indeterminate once the write has started.
632
-
633
- Relation addition reads both endpoints and checks their resolved data server before writing; this reduces PVD-019
676
+ retries stay disabled. Business code `0` alone does not distinguish persisted data from a browser collaboration
677
+ broadcast; the classification uses the observed Provider acknowledgement and supplies no revision or atomicity
678
+ guarantee. Current collaboration peers must be browsers viewing the same notebook and record.
679
+ Encrypted reads return `Record.encrypted_modules` as an `EncryptedRecordModules` containing the ciphertext,
680
+ algorithm and additive fields; `modules` and `attachments` are empty because no plaintext is available. Plaintext
681
+ reads have `encrypted_modules=None`. State-dependent updates reject encrypted content before mutation, while
682
+ ordinary module changes rely on the Provider's password validation before any write. The password and decryption
683
+ lifecycle remain unexposed. The list-level `RecordSummary.encrypted` flag remains available for discovery.
684
+ Unresolved author lookup descriptors remain wire-only and map to `author_name=None` on records and comments (PVD-044).
685
+ A staged attachment is indeterminate once the write has started.
686
+
687
+ Relation addition reads both endpoints and checks their resolved data server before writing; this reduces PVD-009
634
688
  risk but is not an atomic Provider authorization guarantee. It does not list relations merely to inspect the
635
689
  caller-relative `editable` display flag: the mutation endpoint performs the source permission check. Relation
636
690
  deletion lists current relations only to refuse an observed cross-notebook target-ID collision because the Provider
@@ -640,12 +694,12 @@ ignores target notebook identity (PVD-020); its mutation likewise performs autho
640
694
  `records.comments()`. Each returned `RecordRelationRef` carries only the relation row and declared target IDs.
641
695
  Deletion accepts the source `RecordLocator` and one of those observed refs.
642
696
 
643
- Comment upload follows the current Provider's literal one-request `file` field (PVD-037). Comment mutations return `None`
697
+ Comment upload follows the current Provider's literal one-request `file` field. Comment mutations return `None`
644
698
  after acknowledgement. Edit first verifies that the selected
645
699
  comment is currently observed and caller-owned because the Provider otherwise reports a false success for a missing or
646
700
  other-user ID (PVD-004). Delete sends the typed ref directly because the Provider mutation itself enforces record and
647
701
  caller ownership. Neither operation performs a post-write read. Staged comment attachments have no Contract abort
648
- operation, and binding remains affected by PVD-026. Comment create/update use the same `body` vocabulary as the
702
+ operation, and binding remains affected by PVD-003. Comment create/update use the same `body` vocabulary as the
649
703
  returned `RecordComment.body`; the SDK does not perform rich-text display normalization, while the Provider's
650
704
  documented surrounding-whitespace trim still applies when saving.
651
705
  `RecordCommentRef` carries its `RecordLocator`, so update and delete accept the ref rather than an entire comment or a
@@ -667,9 +721,12 @@ async with await client.records.download_comment_attachment(
667
721
  ```
668
722
 
669
723
  `DownloadStream` exposes status, content type, length, range, and disposition metadata without buffering the
670
- complete file. Streams are not automatically replayed. `ByteRange` deliberately rejects `bytes=0-0` (PVD-002).
724
+ complete file. Streams are not automatically replayed. `ByteRange` deliberately rejects `bytes=0-0` (PVD-015).
725
+ For local files, `ByteRange.from_start(0)` returns at most the first 4097 bytes. Inspect `content_range` and request
726
+ the next range starting at its end plus one, use a positive explicit end, or omit `byte_range` for the full file.
727
+ Zero-byte comment attachments can be discovered and downloaded without a range.
671
728
  Comment attachment refs contain only file identity. Pass the parent comment ref returned by the same read when
672
- downloading; current Providers do not verify that association (PVD-038).
729
+ downloading; current Providers do not verify that association (PVD-006).
673
730
 
674
731
  Cross-domain staging keeps resumable state and completed-file identity separate:
675
732
 
@@ -691,7 +748,7 @@ Provider URL and upload-session identity needed by internal binding and cleanup
691
748
  splice either into content. The result does not claim that a later literature/cloud operation checks the uploader or
692
749
  consumes the file exactly once. `uploads.abort()` accepts either an active `StagedUploadSession` or a completed
693
750
  `StagedFile` and exposes the Provider's legacy code-2 cancellation signal as a `None`-returning compensating cleanup
694
- that is not independently verified (PVD-028).
751
+ that is not independently verified (PVD-024).
695
752
  Staging mutations are never automatically retried.
696
753
 
697
754
  Literature identities distinguish the personal library from shared libraries:
@@ -721,7 +778,11 @@ The Provider's static literature form definition and extraction deployment flag
721
778
  returns a `PublicationMetadataCandidate`: unstored, rich publication evidence rather than metadata already attached to
722
779
  a literature item. Its structured authors are `PublicationAuthorCandidate` values. Extraction accepts a DOI, a
723
780
  completed staged PDF, or both; a supplied DOI takes precedence over the staged PDF. A non-empty DOI must match
724
- `^10\.[0-9]+/\S+$`; the generated request model rejects other values before sending them to the Provider (PVD-042).
781
+ `^10\.[0-9]+/\S+$`; the generated request model rejects other values before sending them to the Provider (PVD-040).
782
+ The Provider can wrap an error inside an outer successful extraction response. The SDK maps that nested error to
783
+ its usual safe Provider/authentication error. Empty or opaque backend lists raise `MatelabProtocolError` because they
784
+ provide no structured `PublicationMetadataCandidate`; an outer success code does not promise extracted metadata
785
+ (PVD-045).
725
786
  Literature list
726
787
  results likewise exclude Provider-generated citation HTML and author-highlight fragments; Consumers receive the
727
788
  underlying citation fields instead of presentation markup. `LiteratureDetail.metadata` is `None` when invalid legacy
@@ -746,22 +807,22 @@ Create returns `None` and never guesses the new item from list position because
746
807
  canonical update preflights the raw item response and refuses to drop top-level or nested source/hidden fields. Passing
747
808
  `allow_source_metadata_loss=True` explicitly skips that preflight and sends the canonical replacement directly. The
748
809
  default read/check/write protection is best effort rather than atomic because the Provider offers no revision or
749
- compare-and-swap condition; this Provider-specific behavior remains internal to the update operation (PVD-027).
810
+ compare-and-swap condition; this Provider-specific behavior remains internal to the update operation (PVD-012).
750
811
  PDF replace/delete are separate acknowledged mutations and are not presented as atomic with metadata.
751
812
  Permanent personal deletion is named `permanently_delete`, returns `None`, and is non-recoverable. Sharing requires
752
813
  list-observed item summaries, user-search summaries and a
753
- valid caller identity, then returns `None` because the Provider supplies no per-recipient IDs (PVD-012, PVD-036).
814
+ valid caller identity, then returns `None` because the Provider supplies no per-recipient IDs (PVD-008, PVD-027).
754
815
 
755
816
  Literature comments use explicit `create_comment()` and `update_comment()` intents. Creation first reads the current
756
817
  detail and rejects when the caller already owns a comment; callers then use that comment's `LiteratureCommentRef` with
757
818
  `update_comment()`. The ref contains only the Provider comment-row identity. Update and delete send that identity
758
819
  directly because the Provider mutation verifies current ownership and library permission. The creation preflight
759
820
  maintains the Integration Contract's one-caller-comment invariant for ordinary SDK use, but is not atomic because the
760
- Provider has no matching uniqueness constraint (PVD-035). When sharing with `copy_owner_comment=True`, multiple
821
+ Provider has no matching uniqueness constraint (PVD-026). When sharing with `copy_owner_comment=True`, multiple
761
822
  caller-owned comments remain an explicit ambiguity because the Provider would otherwise choose one implicitly. A
762
823
  staged attachment can replace one `matelab-staged-file` marker; raw temporary URLs are rejected. These checks prevent
763
- common misuse but do not repair the Provider's cross-user UID lookup (PVD-026). Shared-library reads and writes remain
764
- experimental because the Provider permission JOIN is not scoped to the current user (PVD-011); successful SDK calls
824
+ common misuse but do not repair the Provider's cross-user UID lookup (PVD-003). Shared-library reads and writes remain
825
+ experimental because the Provider permission JOIN is not scoped to the current user (PVD-007); successful SDK calls
765
826
  must not be treated as independent authorization proof. Literature PDF downloads reuse `DownloadStream` and the
766
827
  stable `ByteRange` subset.
767
828
 
@@ -777,16 +838,16 @@ await client.cloud_drive.bind_staged_file(staged, target=folder)
777
838
  snapshot and quota usage; pagination applies only to `files`, while request page arguments are not echoed. Personal-root
778
839
  metadata and permissions are synthetic/fixed for this Contract line and remain wire-only. File rows omit the constant
779
840
  caller owner fields and do not invent a folder location that the Provider fails to return for root-wide searches.
780
- Listings likewise omit search/order/direction request echoes. Ordering has no stable ID tie-breaker (PCG-003, PVD-013).
781
- Cloud files expose the nullable stored modification timestamp, not the Provider's duplicate display-formatted date and
782
- time strings.
841
+ Listings likewise omit search/order/direction request echoes. Ordering has no stable ID tie-breaker (PCG-011, PVD-038).
842
+ `CloudFile.modified_at` contains the stored modification timestamp as a non-null string. A stored null causes a
843
+ Provider error before a successful file row can be returned (PVD-047). Display-formatted date and time remain wire-only.
783
844
 
784
845
  Folder create returns a `CloudFolderRef` built from the Provider ID. Other folder and file mutations return `None`
785
846
  after acknowledgement and do not automatically list the drive. Staged finalize accepts a completed `StagedFile`;
786
- the Provider binds by temporary row ID without checking its owner (PVD-031), and finalize atomicity/idempotency remain
847
+ the Provider binds by temporary row ID without checking its owner (PVD-002), and finalize atomicity/idempotency remain
787
848
  absent. Batch move and permanent delete do not claim Provider per-item results or atomicity. Permanent
788
849
  deletion is named `permanently_delete_files` and is non-recoverable. Cloud downloads
789
- resolve bytes from the final file identity and reuse `DownloadStream`, thumbnail/preview choices and the PVD-002-safe
850
+ resolve bytes from the final file identity and reuse `DownloadStream`, thumbnail/preview choices and the PVD-015-safe
790
851
  range subset. Cloud mutations are not automatically retried.
791
852
 
792
853
  Owned, shared, and public catalog notebooks use one `NotebookRef`, distinguished by its
@@ -813,8 +874,9 @@ names or groups, so those fields are `None`. Public summary and creation time re
813
874
  Contract, while the unified model uses the wider owned/shared nullable types.
814
875
 
815
876
  Provider `types`, `display`, and `requests` fields configure its notebook-list UI rather than notebook domain data.
816
- `types` is a list of user-defined classification tags used by the Provider frontend for client-side filtering. These
817
- fields are therefore absent from `NotebookSummary` and from create/update inputs; the SDK handles their update-time
877
+ `types` normally holds classification tags used by the Provider frontend for client-side filtering; legacy stored JSON
878
+ can have another non-null shape. These fields are absent from `NotebookSummary` and from create/update inputs;
879
+ the SDK handles their update-time
818
880
  preservation only as an internal Provider compatibility detail.
819
881
 
820
882
  Template render metadata is also excluded from template summaries and record reads. A `Record` has one
@@ -822,7 +884,7 @@ Template render metadata is also excluded from template summaries and record rea
822
884
  notebook title/keywords, Provider data-server routing, or the frontend PDF-capability flag. `Record.version` identifies
823
885
  historical content and is `None` for the current content. `Record.versions` contains refs usable for historical reads;
824
886
  Provider lock and signature projections remain wire-only. Relation creation still checks returned data servers
825
- internally to mitigate PVD-019.
887
+ internally to mitigate PVD-009.
826
888
 
827
889
  Provider record-finalization and additional-signature mutations are intentionally outside the pinned Contract and SDK
828
890
  interface. They require the account password to unlock a private key, combine version creation with signing, provide no
@@ -926,6 +988,6 @@ uv run python scripts/check_release.py dist/release/*.whl dist/release/*.tar.gz
926
988
  ```
927
989
 
928
990
  Rebuilding the same commit with the same locked environment and `SOURCE_DATE_EPOCH` must produce
929
- byte-identical wheel and source distribution hashes. The release is bound to immutable `matelab-spec v0.4.5`, commit
930
- `6afac7ff151680130e6e4bc6d95782589b97f4e7`, and OpenAPI SHA-256
931
- `31a8d03b4366e2345dfccdbbaaaccf6754b2711428afa4295d778d121988460d`.
991
+ byte-identical wheel and source distribution hashes. The SDK Contract pin is immutable `matelab-spec v2026.10.1`, commit
992
+ `9941e2d623a1c2633adb459b823d78d4e62d3802`, and OpenAPI SHA-256
993
+ `a07997128c1df17a03724c5af8e11b45a34166f9d6e15558e66e0a301f2e0813`.