matelab-python-sdk 0.1.0a18__tar.gz → 0.1.0a20__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 (65) hide show
  1. {matelab_python_sdk-0.1.0a18 → matelab_python_sdk-0.1.0a20}/CHANGELOG.md +26 -0
  2. {matelab_python_sdk-0.1.0a18 → matelab_python_sdk-0.1.0a20}/PKG-INFO +34 -32
  3. {matelab_python_sdk-0.1.0a18 → matelab_python_sdk-0.1.0a20}/README.md +33 -31
  4. {matelab_python_sdk-0.1.0a18 → matelab_python_sdk-0.1.0a20}/contracts/matelab-integration-v1.lock.json +3 -3
  5. {matelab_python_sdk-0.1.0a18 → matelab_python_sdk-0.1.0a20}/contracts/matelab-integration-v1.openapi.yaml +128 -67
  6. {matelab_python_sdk-0.1.0a18 → matelab_python_sdk-0.1.0a20}/docs/operation-coverage.yaml +2 -2
  7. {matelab_python_sdk-0.1.0a18 → matelab_python_sdk-0.1.0a20}/pyproject.toml +1 -1
  8. {matelab_python_sdk-0.1.0a18 → matelab_python_sdk-0.1.0a20}/src/matelab/_generated/models.py +166 -43
  9. {matelab_python_sdk-0.1.0a18 → matelab_python_sdk-0.1.0a20}/src/matelab/domains/records.py +8 -18
  10. {matelab_python_sdk-0.1.0a18 → matelab_python_sdk-0.1.0a20}/tests/test_async_attachment_upload.py +2 -1
  11. {matelab_python_sdk-0.1.0a18 → matelab_python_sdk-0.1.0a20}/tests/test_generation.py +155 -3
  12. {matelab_python_sdk-0.1.0a18 → matelab_python_sdk-0.1.0a20}/tests/test_record_mutations.py +20 -19
  13. {matelab_python_sdk-0.1.0a18 → matelab_python_sdk-0.1.0a20}/tests/test_records.py +103 -12
  14. {matelab_python_sdk-0.1.0a18 → matelab_python_sdk-0.1.0a20}/tests/test_transport.py +1 -1
  15. {matelab_python_sdk-0.1.0a18 → matelab_python_sdk-0.1.0a20}/tests/test_uploads_and_literature.py +2 -0
  16. {matelab_python_sdk-0.1.0a18 → matelab_python_sdk-0.1.0a20}/uv.lock +1 -1
  17. {matelab_python_sdk-0.1.0a18 → matelab_python_sdk-0.1.0a20}/.env.example +0 -0
  18. {matelab_python_sdk-0.1.0a18 → matelab_python_sdk-0.1.0a20}/.github/workflows/release.yml +0 -0
  19. {matelab_python_sdk-0.1.0a18 → matelab_python_sdk-0.1.0a20}/.gitignore +0 -0
  20. {matelab_python_sdk-0.1.0a18 → matelab_python_sdk-0.1.0a20}/AGENTS.md +0 -0
  21. {matelab_python_sdk-0.1.0a18 → matelab_python_sdk-0.1.0a20}/CONTEXT.md +0 -0
  22. {matelab_python_sdk-0.1.0a18 → matelab_python_sdk-0.1.0a20}/LICENSE +0 -0
  23. {matelab_python_sdk-0.1.0a18 → matelab_python_sdk-0.1.0a20}/NOTICE +0 -0
  24. {matelab_python_sdk-0.1.0a18 → matelab_python_sdk-0.1.0a20}/scripts/check_installed_package.py +0 -0
  25. {matelab_python_sdk-0.1.0a18 → matelab_python_sdk-0.1.0a20}/scripts/check_release.py +0 -0
  26. {matelab_python_sdk-0.1.0a18 → matelab_python_sdk-0.1.0a20}/scripts/generate_models.py +0 -0
  27. {matelab_python_sdk-0.1.0a18 → matelab_python_sdk-0.1.0a20}/src/matelab/__init__.py +0 -0
  28. {matelab_python_sdk-0.1.0a18 → matelab_python_sdk-0.1.0a20}/src/matelab/_generated/__init__.py +0 -0
  29. {matelab_python_sdk-0.1.0a18 → matelab_python_sdk-0.1.0a20}/src/matelab/_hashes.py +0 -0
  30. {matelab_python_sdk-0.1.0a18 → matelab_python_sdk-0.1.0a20}/src/matelab/_transport.py +0 -0
  31. {matelab_python_sdk-0.1.0a18 → matelab_python_sdk-0.1.0a20}/src/matelab/_wire_validation.py +0 -0
  32. {matelab_python_sdk-0.1.0a18 → matelab_python_sdk-0.1.0a20}/src/matelab/client.py +0 -0
  33. {matelab_python_sdk-0.1.0a18 → matelab_python_sdk-0.1.0a20}/src/matelab/domains/__init__.py +0 -0
  34. {matelab_python_sdk-0.1.0a18 → matelab_python_sdk-0.1.0a20}/src/matelab/domains/_attachment_content.py +0 -0
  35. {matelab_python_sdk-0.1.0a18 → matelab_python_sdk-0.1.0a20}/src/matelab/domains/cloud_drive.py +0 -0
  36. {matelab_python_sdk-0.1.0a18 → matelab_python_sdk-0.1.0a20}/src/matelab/domains/groups.py +0 -0
  37. {matelab_python_sdk-0.1.0a18 → matelab_python_sdk-0.1.0a20}/src/matelab/domains/literature.py +0 -0
  38. {matelab_python_sdk-0.1.0a18 → matelab_python_sdk-0.1.0a20}/src/matelab/domains/notebooks.py +0 -0
  39. {matelab_python_sdk-0.1.0a18 → matelab_python_sdk-0.1.0a20}/src/matelab/domains/templates.py +0 -0
  40. {matelab_python_sdk-0.1.0a18 → matelab_python_sdk-0.1.0a20}/src/matelab/domains/uploads.py +0 -0
  41. {matelab_python_sdk-0.1.0a18 → matelab_python_sdk-0.1.0a20}/src/matelab/domains/users.py +0 -0
  42. {matelab_python_sdk-0.1.0a18 → matelab_python_sdk-0.1.0a20}/src/matelab/errors.py +0 -0
  43. {matelab_python_sdk-0.1.0a18 → matelab_python_sdk-0.1.0a20}/src/matelab/py.typed +0 -0
  44. {matelab_python_sdk-0.1.0a18 → matelab_python_sdk-0.1.0a20}/src/matelab/session.py +0 -0
  45. {matelab_python_sdk-0.1.0a18 → matelab_python_sdk-0.1.0a20}/src/matelab/streaming.py +0 -0
  46. {matelab_python_sdk-0.1.0a18 → matelab_python_sdk-0.1.0a20}/tests/conftest.py +0 -0
  47. {matelab_python_sdk-0.1.0a18 → matelab_python_sdk-0.1.0a20}/tests/provider/test_provider_smoke.py +0 -0
  48. {matelab_python_sdk-0.1.0a18 → matelab_python_sdk-0.1.0a20}/tests/test_client.py +0 -0
  49. {matelab_python_sdk-0.1.0a18 → matelab_python_sdk-0.1.0a20}/tests/test_cloud_drive.py +0 -0
  50. {matelab_python_sdk-0.1.0a18 → matelab_python_sdk-0.1.0a20}/tests/test_comments_and_downloads.py +0 -0
  51. {matelab_python_sdk-0.1.0a18 → matelab_python_sdk-0.1.0a20}/tests/test_consumer_smoke.py +0 -0
  52. {matelab_python_sdk-0.1.0a18 → matelab_python_sdk-0.1.0a20}/tests/test_errors.py +0 -0
  53. {matelab_python_sdk-0.1.0a18 → matelab_python_sdk-0.1.0a20}/tests/test_extended_records.py +0 -0
  54. {matelab_python_sdk-0.1.0a18 → matelab_python_sdk-0.1.0a20}/tests/test_groups.py +0 -0
  55. {matelab_python_sdk-0.1.0a18 → matelab_python_sdk-0.1.0a20}/tests/test_notebook_lifecycle.py +0 -0
  56. {matelab_python_sdk-0.1.0a18 → matelab_python_sdk-0.1.0a20}/tests/test_notebooks.py +0 -0
  57. {matelab_python_sdk-0.1.0a18 → matelab_python_sdk-0.1.0a20}/tests/test_operation_coverage.py +0 -0
  58. {matelab_python_sdk-0.1.0a18 → matelab_python_sdk-0.1.0a20}/tests/test_record_lifecycle.py +0 -0
  59. {matelab_python_sdk-0.1.0a18 → matelab_python_sdk-0.1.0a20}/tests/test_release.py +0 -0
  60. {matelab_python_sdk-0.1.0a18 → matelab_python_sdk-0.1.0a20}/tests/test_streaming.py +0 -0
  61. {matelab_python_sdk-0.1.0a18 → matelab_python_sdk-0.1.0a20}/tests/test_template_lifecycle.py +0 -0
  62. {matelab_python_sdk-0.1.0a18 → matelab_python_sdk-0.1.0a20}/tests/test_templates.py +0 -0
  63. {matelab_python_sdk-0.1.0a18 → matelab_python_sdk-0.1.0a20}/tests/test_users.py +0 -0
  64. {matelab_python_sdk-0.1.0a18 → matelab_python_sdk-0.1.0a20}/tests/typing/consumer.py +0 -0
  65. {matelab_python_sdk-0.1.0a18 → matelab_python_sdk-0.1.0a20}/tests/typing/pyproject.toml +0 -0
@@ -3,6 +3,32 @@
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.1.0a20 - 2026-08-24
7
+
8
+ - Adopts immutable `matelab-spec v0.4.3` at commit `7965ac9803505ae35dc5ce36f52afc3c2c133c45` and OpenAPI
9
+ SHA-256 `6576812dd13f92ad58131f4edb8dc919d1b1fe0f19a695c2d25fbe6d13a8445b`, aligned with Provider evidence
10
+ `a3e6b961800f6b1ab666f0d3f6cd4c64c2a3ce27`.
11
+ - Breaking: tightens record readback to one canonical attachment object or `null` per table file cell, removing the
12
+ obsolete attachment-array compatibility path. Template decoding now accepts nullable JSON-code data and preserves
13
+ empty-string number/boolean cells alongside their scalar and null forms.
14
+ - Records PVD-046 while keeping `templates.save_content()` deliberately narrower than the Provider: the request remains
15
+ a canonical module list, and every table file cell remains one canonical attachment object or `null`, never an
16
+ arbitrary or multi-attachment array.
17
+
18
+ ## 0.1.0a19 - 2026-08-23
19
+
20
+ - Re-adopts the force-rewritten `matelab-spec v0.4.2` at commit
21
+ `1a4b77721d782fccd2b1f26bcc16a7d876347686` and OpenAPI SHA-256
22
+ `1e737421aac636356487122ea89e20d54de07c8b9c0651cf6970a7123fe3b72b`. The published `0.1.0a18` entry below remains
23
+ historical evidence for the former `v0.4.2` target at `a51483f`; it is not rewritten.
24
+ - Aligns with current Provider evidence: comment attachment multipart uses `file`; temporary-file and comment identifiers
25
+ allow 100 characters; code-4 `errs` accepts opaque array or object containers; and DOI extraction validates
26
+ `^10\.[0-9]+/\S+$` before transport.
27
+ - Breaking: removes `RecordValueAddition.path` and `RecordFilesAttachmentAppend.folder_path` because the current
28
+ Provider's add-value shape no longer accepts `path`; files-module append now targets only the module root. Search
29
+ excludes encrypted records, and callers of arbitrary template-shaped imports must omit unset file-valued keys because
30
+ the Provider rejects their explicit `null`.
31
+
6
32
  ## 0.1.0a18 - 2026-08-23
7
33
 
8
34
  - Adopts immutable `matelab-spec v0.4.2` at commit `a51483f910440fbbbc3693cc32566482064bb3ab` and OpenAPI
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: matelab-python-sdk
3
- Version: 0.1.0a18
3
+ Version: 0.1.0a20
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,10 +35,10 @@ 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.0a18`. `[project].version` in `pyproject.toml` is the sole SDK version source;
38
+ The current alpha is `0.1.0a20`. `[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.2` Contract Release. The sole release pin is
41
+ The SDK is pinned to the immutable `matelab-spec v0.4.3` Contract Release. The sole release pin is
42
42
  `contracts/matelab-integration-v1.lock.json`, which records
43
43
  the source tag, commit, OpenAPI path, local snapshot path, and SHA-256.
44
44
 
@@ -64,17 +64,15 @@ To test the same artifact a downstream Consumer will install, build and install
64
64
 
65
65
  ```bash
66
66
  uv build --no-build-isolation --out-dir dist/release
67
- python -m pip install dist/release/matelab_python_sdk-0.1.0a18-py3-none-any.whl
67
+ python -m pip install dist/release/matelab_python_sdk-0.1.0a20-py3-none-any.whl
68
68
  ```
69
69
 
70
70
  Do not infer Provider compatibility from the SDK version alone. A release is also bound to the Contract
71
71
  tag, commit, and checksum recorded below.
72
72
 
73
- The Contract and public SDK interface remain compatible with the stable Provider evidence at
74
- `59101f006e9fd6d6ce0b951dfd34695a7949b54f`. The latest reviewed Provider,
75
- `a3e6b961800f6b1ab666f0d3f6cd4c64c2a3ce27`, is incompatible with that evidence. Its operation-specific
76
- differences are documented alongside the affected interfaces below; do not treat a successful Contract adoption as
77
- proof that this newer Provider revision is interchangeable.
73
+ The Contract and public SDK interface track the current reviewed Provider at
74
+ `a3e6b961800f6b1ab666f0d3f6cd4c64c2a3ce27`. Provider limitations that remain relevant to integrations are documented
75
+ alongside the affected interfaces below.
78
76
 
79
77
  ## Design
80
78
 
@@ -123,8 +121,8 @@ success code it does not allow raises `MatelabProtocolError` rather than assigni
123
121
  `MatelabTransportError.status_code` remains available for HTTP failures. Provider response bodies, top-level `msg`,
124
122
  legacy `errmsg`, opaque `errs`, uncontracted debug fields, and request payloads are not attached to exceptions because
125
123
  Provider-authored diagnostics may contain stack traces, credentials, or submitted values.
126
- The pinned v0.4.2 Contract also declares optional code-4 `errs`, but PVD-041 and PCG-015 record incompatible observed
127
- item/container shapes and insufficient evidence for requiredness, cardinality, or exclusion from other codes. The SDK
124
+ The pinned v0.4.3 Contract also declares optional code-4 `errs`, but PVD-041 and PCG-015 record opaque array/object
125
+ containers and insufficient evidence for requiredness, cardinality, or exclusion from other codes. The SDK
128
126
  therefore keeps those diagnostics wire-only and does not expose `errs` as stable public metadata. A typed detail
129
127
  interface remains blocked until a future Contract release can define one from stronger Provider evidence. `retryable`
130
128
  means the identical SDK call is safe to replay without further interpretation. It is conservatively `False` for every
@@ -318,8 +316,8 @@ supports one file column. A locally rejected target does not spend the handle; o
318
316
  result cannot be retried through the same client.
319
317
 
320
318
  Files-module append and table/files replacement use the general record attachment intents and may be combined with
321
- other non-conflicting record changes. `RecordFilesAttachmentAppend` targets the module root by default; pass
322
- `folder_path=(...)` for an existing folder-title path:
319
+ other non-conflicting record changes. `RecordFilesAttachmentAppend` targets the module root; the current Provider does
320
+ not accept a files-folder path (PVD-044):
323
321
 
324
322
  ```python
325
323
  from matelab import RecordAttachmentReplacement, RecordFilesAttachmentAppend, RecordValueUpdate
@@ -350,7 +348,9 @@ await client.records.update(
350
348
 
351
349
  For a files occurrence, `caption=None` preserves the observed string caption (an observed null caption normalizes to
352
350
  the required empty string). Table replacement requires exactly one current attachment in the selected cell and does
353
- not accept a caption. Both forms require the replacement hash to differ from the current occurrence.
351
+ not accept a caption. Record reads likewise accept only one canonical attachment object or `null` in a table file cell;
352
+ an attachment array violates the pinned response contract. Both replacement forms require the replacement hash to
353
+ differ from the current occurrence.
354
354
 
355
355
  All record attachment intents reject handles from another notebook or authenticated uploader, duplicate use within one
356
356
  finalization request, and raw Provider attachment references. Row/index-based operations use an immediate SDK read and
@@ -502,9 +502,12 @@ await client.templates.save_content(template, modules)
502
502
  `TemplateModule.data_present` distinguishes a missing `data` property from an explicit null value, while
503
503
  `attributes` prevents additive canonical properties from being dropped during a read/save cycle. The SDK does not
504
504
  synthesize frontend editor UIDs, layout widths, rows, folders, option encoding, or a fixed set of display module
505
- types. The Provider supports only whole-content replacement and has no atomic patch or compare-and-swap operation
506
- (PCG-007), so
507
- a read-modify-save sequence can overwrite a concurrent change.
505
+ types. A JSON-language code module with style metadata may return its JSON string or explicit `null`; number and boolean
506
+ table cells preserve the Provider's empty string as distinct from scalar and `null` values. The Provider accepts some
507
+ non-canonical module roots and arbitrary table file arrays, but the SDK does not expose that behavior: saved content
508
+ must remain a canonical module list, and a table file cell must be one canonical attachment object or `null` (PVD-046).
509
+ The Provider supports only whole-content replacement and has no atomic patch or compare-and-swap operation (PCG-007),
510
+ so a read-modify-save sequence can overwrite a concurrent change.
508
511
 
509
512
  The market result retains the Provider's `total_count` and derives `has_more` without echoing the caller's page
510
513
  arguments; it does not claim a stable order or continuation token. Template reads remain experimental
@@ -544,7 +547,7 @@ relations = await client.records.relations(source)
544
547
  as `modules`; it is not the template-shaped `RecordImportItem.data` accepted by record import.
545
548
  `records.search()` maps each requested field name to a stored record path. A match exposes only Provider-returned
546
549
  entries in `values`, so key membership distinguishes an absent result from an explicit JSON `null`.
547
- The latest reviewed Provider excludes encrypted records from search (PVD-043); an empty search result therefore does
550
+ The current Provider excludes encrypted records from search (PVD-043); an empty search result therefore does
548
551
  not prove that no matching encrypted record exists.
549
552
 
550
553
  The Provider calls its notebook directory tree `subtype`; that name remains wire-only. The SDK exposes
@@ -595,9 +598,10 @@ UID. Import validates the complete batch with generated wire models but cannot m
595
598
  individual inputs or promise atomicity (PCG-008); it therefore returns only the tuple of database IDs actually
596
599
  reported by the Provider. `RecordImportItem.keywords` is a tuple; the adapter owns the Provider's
597
600
  legacy semicolon encoding. Folder paths are ordered existing folder titles from the notebook root; they do not create
598
- missing folders. The latest reviewed Provider rejects explicit-null table-file imports (PVD-045), so integrations
599
- targeting it must not interpret that rejection as an invalid SDK input. Delete means moving records into the recycle bin,
600
- not permanent deletion. Delete and restore return `None`; record mutations are not automatically retried.
601
+ missing folders. The current Provider rejects explicit-null table-file imports (PVD-045). Because the SDK accepts
602
+ arbitrary template-shaped data and cannot identify file columns without the template definition, integrations must omit
603
+ an unset file-valued key instead of sending it as `None`. Delete means moving records into the recycle bin, not permanent
604
+ deletion. Delete and restore return `None`; record mutations are not automatically retried.
601
605
 
602
606
  `records.copy()` accepts an optional `new_record_uid`; when omitted, the SDK generates one locally. The Provider
603
607
  returns only the copied row's database ID, so retaining that UID lets the SDK return a complete `RecordLocator`
@@ -614,9 +618,8 @@ returns the acknowledgement classification
614
618
  issue a post-write read. Database, active-browser, and unclassified acknowledgements remain distinct, and mutation
615
619
  retries stay disabled. Encrypted record content is outside the SDK's public read/update interface: the list-level
616
620
  `RecordSummary.encrypted` flag lets callers identify and skip it, while the Provider password field and frontend-only
617
- password lifecycle remain unexposed. The latest reviewed Provider rejects a valid files-folder append path
618
- (PVD-044); integrations targeting it must not reinterpret that rejection as invalid SDK input, and should treat a
619
- staged attachment as indeterminate once the write has started.
621
+ password lifecycle remain unexposed. Files-module attachment append is root-only because the current Provider rejects
622
+ folder-path segments (PVD-044). A staged attachment is indeterminate once the write has started.
620
623
 
621
624
  Relation addition reads both endpoints and checks their resolved data server before writing; this reduces PVD-019
622
625
  risk but is not an atomic Provider authorization guarantee. It does not list relations merely to inspect the
@@ -628,8 +631,7 @@ ignores target notebook identity (PVD-020); its mutation likewise performs autho
628
631
  `records.comments()`. Each returned `RecordRelationRef` carries only the relation row and declared target IDs.
629
632
  Deletion accepts the source `RecordLocator` and one of those observed refs.
630
633
 
631
- Comment upload follows the stable Provider's literal one-request `upload` field. The latest reviewed Provider renames
632
- that field (PVD-037), but the SDK retains the Contract-compatible `upload` encoding. Comment mutations return `None`
634
+ Comment upload follows the current Provider's literal one-request `file` field (PVD-037). Comment mutations return `None`
633
635
  after acknowledgement. Edit first verifies that the selected
634
636
  comment is currently observed and caller-owned because the Provider otherwise reports a false success for a missing or
635
637
  other-user ID (PVD-004). Delete sends the typed ref directly because the Provider mutation itself enforces record and
@@ -709,9 +711,9 @@ The Provider's static literature form definition and extraction deployment flag
709
711
  `extract_metadata()` directly and handle an unavailable or failing optional backend as a Provider error. Extraction
710
712
  returns a `PublicationMetadataCandidate`: unstored, rich publication evidence rather than metadata already attached to
711
713
  a literature item. Its structured authors are `PublicationAuthorCandidate` values. Extraction accepts a DOI, a
712
- completed staged PDF, or both; a supplied DOI takes precedence over the staged PDF. The latest reviewed Provider
713
- rejects DOI formats accepted by the stable evidence (PVD-042), so a rejection from that revision does not by itself
714
- make a DOI invalid for this SDK interface. Literature list
714
+ completed staged PDF, or both; a supplied DOI takes precedence over the staged PDF. A non-empty DOI must match
715
+ `^10\.[0-9]+/\S+$`; the generated request model rejects other values before sending them to the Provider (PVD-042).
716
+ Literature list
715
717
  results likewise exclude Provider-generated citation HTML and author-highlight fragments; Consumers receive the
716
718
  underlying citation fields instead of presentation markup. `LiteratureDetail.metadata` is `None` when invalid legacy
717
719
  JSON produces the Provider's empty-array fallback; otherwise it contains decoded stored metadata without arbitrary
@@ -915,6 +917,6 @@ uv run python scripts/check_release.py dist/release/*.whl dist/release/*.tar.gz
915
917
  ```
916
918
 
917
919
  Rebuilding the same commit with the same locked environment and `SOURCE_DATE_EPOCH` must produce
918
- byte-identical wheel and source distribution hashes. The release is bound to `matelab-spec v0.4.2`,
919
- commit `a51483f910440fbbbc3693cc32566482064bb3ab`, and OpenAPI SHA-256
920
- `4c92b96ba3dfe7122b30b4db2e2c8054c06018291c8c52c15482112b51a32920`.
920
+ byte-identical wheel and source distribution hashes. The release is bound to immutable `matelab-spec v0.4.3`, commit
921
+ `7965ac9803505ae35dc5ce36f52afc3c2c133c45`, and OpenAPI SHA-256
922
+ `6576812dd13f92ad58131f4edb8dc919d1b1fe0f19a695c2d25fbe6d13a8445b`.
@@ -7,10 +7,10 @@ This README describes the current SDK interface, integration semantics, examples
7
7
  [AGENTS.md](AGENTS.md) for durable maintenance rules, and `docs/operation-coverage.yaml` for the machine-readable
8
8
  operation inventory.
9
9
 
10
- The current alpha is `0.1.0a18`. `[project].version` in `pyproject.toml` is the sole SDK version source;
10
+ The current alpha is `0.1.0a20`. `[project].version` in `pyproject.toml` is the sole SDK version source;
11
11
  `uv.lock` only mirrors that source.
12
12
 
13
- The SDK is pinned to the immutable `matelab-spec v0.4.2` Contract Release. The sole release pin is
13
+ The SDK is pinned to the immutable `matelab-spec v0.4.3` Contract Release. The sole release pin is
14
14
  `contracts/matelab-integration-v1.lock.json`, which records
15
15
  the source tag, commit, OpenAPI path, local snapshot path, and SHA-256.
16
16
 
@@ -36,17 +36,15 @@ To test the same artifact a downstream Consumer will install, build and install
36
36
 
37
37
  ```bash
38
38
  uv build --no-build-isolation --out-dir dist/release
39
- python -m pip install dist/release/matelab_python_sdk-0.1.0a18-py3-none-any.whl
39
+ python -m pip install dist/release/matelab_python_sdk-0.1.0a20-py3-none-any.whl
40
40
  ```
41
41
 
42
42
  Do not infer Provider compatibility from the SDK version alone. A release is also bound to the Contract
43
43
  tag, commit, and checksum recorded below.
44
44
 
45
- The Contract and public SDK interface remain compatible with the stable Provider evidence at
46
- `59101f006e9fd6d6ce0b951dfd34695a7949b54f`. The latest reviewed Provider,
47
- `a3e6b961800f6b1ab666f0d3f6cd4c64c2a3ce27`, is incompatible with that evidence. Its operation-specific
48
- differences are documented alongside the affected interfaces below; do not treat a successful Contract adoption as
49
- proof that this newer Provider revision is interchangeable.
45
+ The Contract and public SDK interface track the current reviewed Provider at
46
+ `a3e6b961800f6b1ab666f0d3f6cd4c64c2a3ce27`. Provider limitations that remain relevant to integrations are documented
47
+ alongside the affected interfaces below.
50
48
 
51
49
  ## Design
52
50
 
@@ -95,8 +93,8 @@ success code it does not allow raises `MatelabProtocolError` rather than assigni
95
93
  `MatelabTransportError.status_code` remains available for HTTP failures. Provider response bodies, top-level `msg`,
96
94
  legacy `errmsg`, opaque `errs`, uncontracted debug fields, and request payloads are not attached to exceptions because
97
95
  Provider-authored diagnostics may contain stack traces, credentials, or submitted values.
98
- The pinned v0.4.2 Contract also declares optional code-4 `errs`, but PVD-041 and PCG-015 record incompatible observed
99
- item/container shapes and insufficient evidence for requiredness, cardinality, or exclusion from other codes. The SDK
96
+ The pinned v0.4.3 Contract also declares optional code-4 `errs`, but PVD-041 and PCG-015 record opaque array/object
97
+ containers and insufficient evidence for requiredness, cardinality, or exclusion from other codes. The SDK
100
98
  therefore keeps those diagnostics wire-only and does not expose `errs` as stable public metadata. A typed detail
101
99
  interface remains blocked until a future Contract release can define one from stronger Provider evidence. `retryable`
102
100
  means the identical SDK call is safe to replay without further interpretation. It is conservatively `False` for every
@@ -290,8 +288,8 @@ supports one file column. A locally rejected target does not spend the handle; o
290
288
  result cannot be retried through the same client.
291
289
 
292
290
  Files-module append and table/files replacement use the general record attachment intents and may be combined with
293
- other non-conflicting record changes. `RecordFilesAttachmentAppend` targets the module root by default; pass
294
- `folder_path=(...)` for an existing folder-title path:
291
+ other non-conflicting record changes. `RecordFilesAttachmentAppend` targets the module root; the current Provider does
292
+ not accept a files-folder path (PVD-044):
295
293
 
296
294
  ```python
297
295
  from matelab import RecordAttachmentReplacement, RecordFilesAttachmentAppend, RecordValueUpdate
@@ -322,7 +320,9 @@ await client.records.update(
322
320
 
323
321
  For a files occurrence, `caption=None` preserves the observed string caption (an observed null caption normalizes to
324
322
  the required empty string). Table replacement requires exactly one current attachment in the selected cell and does
325
- not accept a caption. Both forms require the replacement hash to differ from the current occurrence.
323
+ not accept a caption. Record reads likewise accept only one canonical attachment object or `null` in a table file cell;
324
+ an attachment array violates the pinned response contract. Both replacement forms require the replacement hash to
325
+ differ from the current occurrence.
326
326
 
327
327
  All record attachment intents reject handles from another notebook or authenticated uploader, duplicate use within one
328
328
  finalization request, and raw Provider attachment references. Row/index-based operations use an immediate SDK read and
@@ -474,9 +474,12 @@ await client.templates.save_content(template, modules)
474
474
  `TemplateModule.data_present` distinguishes a missing `data` property from an explicit null value, while
475
475
  `attributes` prevents additive canonical properties from being dropped during a read/save cycle. The SDK does not
476
476
  synthesize frontend editor UIDs, layout widths, rows, folders, option encoding, or a fixed set of display module
477
- types. The Provider supports only whole-content replacement and has no atomic patch or compare-and-swap operation
478
- (PCG-007), so
479
- a read-modify-save sequence can overwrite a concurrent change.
477
+ types. A JSON-language code module with style metadata may return its JSON string or explicit `null`; number and boolean
478
+ table cells preserve the Provider's empty string as distinct from scalar and `null` values. The Provider accepts some
479
+ non-canonical module roots and arbitrary table file arrays, but the SDK does not expose that behavior: saved content
480
+ must remain a canonical module list, and a table file cell must be one canonical attachment object or `null` (PVD-046).
481
+ The Provider supports only whole-content replacement and has no atomic patch or compare-and-swap operation (PCG-007),
482
+ so a read-modify-save sequence can overwrite a concurrent change.
480
483
 
481
484
  The market result retains the Provider's `total_count` and derives `has_more` without echoing the caller's page
482
485
  arguments; it does not claim a stable order or continuation token. Template reads remain experimental
@@ -516,7 +519,7 @@ relations = await client.records.relations(source)
516
519
  as `modules`; it is not the template-shaped `RecordImportItem.data` accepted by record import.
517
520
  `records.search()` maps each requested field name to a stored record path. A match exposes only Provider-returned
518
521
  entries in `values`, so key membership distinguishes an absent result from an explicit JSON `null`.
519
- The latest reviewed Provider excludes encrypted records from search (PVD-043); an empty search result therefore does
522
+ The current Provider excludes encrypted records from search (PVD-043); an empty search result therefore does
520
523
  not prove that no matching encrypted record exists.
521
524
 
522
525
  The Provider calls its notebook directory tree `subtype`; that name remains wire-only. The SDK exposes
@@ -567,9 +570,10 @@ UID. Import validates the complete batch with generated wire models but cannot m
567
570
  individual inputs or promise atomicity (PCG-008); it therefore returns only the tuple of database IDs actually
568
571
  reported by the Provider. `RecordImportItem.keywords` is a tuple; the adapter owns the Provider's
569
572
  legacy semicolon encoding. Folder paths are ordered existing folder titles from the notebook root; they do not create
570
- missing folders. The latest reviewed Provider rejects explicit-null table-file imports (PVD-045), so integrations
571
- targeting it must not interpret that rejection as an invalid SDK input. Delete means moving records into the recycle bin,
572
- not permanent deletion. Delete and restore return `None`; record mutations are not automatically retried.
573
+ missing folders. The current Provider rejects explicit-null table-file imports (PVD-045). Because the SDK accepts
574
+ arbitrary template-shaped data and cannot identify file columns without the template definition, integrations must omit
575
+ an unset file-valued key instead of sending it as `None`. Delete means moving records into the recycle bin, not permanent
576
+ deletion. Delete and restore return `None`; record mutations are not automatically retried.
573
577
 
574
578
  `records.copy()` accepts an optional `new_record_uid`; when omitted, the SDK generates one locally. The Provider
575
579
  returns only the copied row's database ID, so retaining that UID lets the SDK return a complete `RecordLocator`
@@ -586,9 +590,8 @@ returns the acknowledgement classification
586
590
  issue a post-write read. Database, active-browser, and unclassified acknowledgements remain distinct, and mutation
587
591
  retries stay disabled. Encrypted record content is outside the SDK's public read/update interface: the list-level
588
592
  `RecordSummary.encrypted` flag lets callers identify and skip it, while the Provider password field and frontend-only
589
- password lifecycle remain unexposed. The latest reviewed Provider rejects a valid files-folder append path
590
- (PVD-044); integrations targeting it must not reinterpret that rejection as invalid SDK input, and should treat a
591
- staged attachment as indeterminate once the write has started.
593
+ password lifecycle remain unexposed. Files-module attachment append is root-only because the current Provider rejects
594
+ folder-path segments (PVD-044). A staged attachment is indeterminate once the write has started.
592
595
 
593
596
  Relation addition reads both endpoints and checks their resolved data server before writing; this reduces PVD-019
594
597
  risk but is not an atomic Provider authorization guarantee. It does not list relations merely to inspect the
@@ -600,8 +603,7 @@ ignores target notebook identity (PVD-020); its mutation likewise performs autho
600
603
  `records.comments()`. Each returned `RecordRelationRef` carries only the relation row and declared target IDs.
601
604
  Deletion accepts the source `RecordLocator` and one of those observed refs.
602
605
 
603
- Comment upload follows the stable Provider's literal one-request `upload` field. The latest reviewed Provider renames
604
- that field (PVD-037), but the SDK retains the Contract-compatible `upload` encoding. Comment mutations return `None`
606
+ Comment upload follows the current Provider's literal one-request `file` field (PVD-037). Comment mutations return `None`
605
607
  after acknowledgement. Edit first verifies that the selected
606
608
  comment is currently observed and caller-owned because the Provider otherwise reports a false success for a missing or
607
609
  other-user ID (PVD-004). Delete sends the typed ref directly because the Provider mutation itself enforces record and
@@ -681,9 +683,9 @@ The Provider's static literature form definition and extraction deployment flag
681
683
  `extract_metadata()` directly and handle an unavailable or failing optional backend as a Provider error. Extraction
682
684
  returns a `PublicationMetadataCandidate`: unstored, rich publication evidence rather than metadata already attached to
683
685
  a literature item. Its structured authors are `PublicationAuthorCandidate` values. Extraction accepts a DOI, a
684
- completed staged PDF, or both; a supplied DOI takes precedence over the staged PDF. The latest reviewed Provider
685
- rejects DOI formats accepted by the stable evidence (PVD-042), so a rejection from that revision does not by itself
686
- make a DOI invalid for this SDK interface. Literature list
686
+ completed staged PDF, or both; a supplied DOI takes precedence over the staged PDF. A non-empty DOI must match
687
+ `^10\.[0-9]+/\S+$`; the generated request model rejects other values before sending them to the Provider (PVD-042).
688
+ Literature list
687
689
  results likewise exclude Provider-generated citation HTML and author-highlight fragments; Consumers receive the
688
690
  underlying citation fields instead of presentation markup. `LiteratureDetail.metadata` is `None` when invalid legacy
689
691
  JSON produces the Provider's empty-array fallback; otherwise it contains decoded stored metadata without arbitrary
@@ -887,6 +889,6 @@ uv run python scripts/check_release.py dist/release/*.whl dist/release/*.tar.gz
887
889
  ```
888
890
 
889
891
  Rebuilding the same commit with the same locked environment and `SOURCE_DATE_EPOCH` must produce
890
- byte-identical wheel and source distribution hashes. The release is bound to `matelab-spec v0.4.2`,
891
- commit `a51483f910440fbbbc3693cc32566482064bb3ab`, and OpenAPI SHA-256
892
- `4c92b96ba3dfe7122b30b4db2e2c8054c06018291c8c52c15482112b51a32920`.
892
+ byte-identical wheel and source distribution hashes. The release is bound to immutable `matelab-spec v0.4.3`, commit
893
+ `7965ac9803505ae35dc5ce36f52afc3c2c133c45`, and OpenAPI SHA-256
894
+ `6576812dd13f92ad58131f4edb8dc919d1b1fe0f19a695c2d25fbe6d13a8445b`.
@@ -2,12 +2,12 @@
2
2
  "schema_version": 1,
3
3
  "contract_line": "integration-v1",
4
4
  "release": {
5
- "tag": "v0.4.2",
6
- "commit": "a51483f910440fbbbc3693cc32566482064bb3ab"
5
+ "tag": "v0.4.3",
6
+ "commit": "7965ac9803505ae35dc5ce36f52afc3c2c133c45"
7
7
  },
8
8
  "openapi": {
9
9
  "source_path": "openapi/matelab-integration-v1.openapi.yaml",
10
10
  "snapshot": "matelab-integration-v1.openapi.yaml",
11
- "sha256": "4c92b96ba3dfe7122b30b4db2e2c8054c06018291c8c52c15482112b51a32920"
11
+ "sha256": "6576812dd13f92ad58131f4edb8dc919d1b1fe0f19a695c2d25fbe6d13a8445b"
12
12
  }
13
13
  }