muse-code-sdk 1.4.11__tar.gz → 1.4.12__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.
- {muse_code_sdk-1.4.11/src/muse_code_sdk.egg-info → muse_code_sdk-1.4.12}/PKG-INFO +5 -3
- {muse_code_sdk-1.4.11 → muse_code_sdk-1.4.12}/README.md +3 -2
- {muse_code_sdk-1.4.11 → muse_code_sdk-1.4.12}/pyproject.toml +9 -8
- {muse_code_sdk-1.4.11 → muse_code_sdk-1.4.12}/src/muse_code/__init__.py +2 -2
- {muse_code_sdk-1.4.11 → muse_code_sdk-1.4.12}/src/muse_code/facade/approval.py +5 -0
- {muse_code_sdk-1.4.11 → muse_code_sdk-1.4.12/src/muse_code_sdk.egg-info}/PKG-INFO +5 -3
- muse_code_sdk-1.4.12/src/muse_code_sdk.egg-info/requires.txt +2 -0
- {muse_code_sdk-1.4.11 → muse_code_sdk-1.4.12}/tests/test_facade_approval.py +47 -0
- {muse_code_sdk-1.4.11 → muse_code_sdk-1.4.12}/tests/test_manifests.py +7 -3
- {muse_code_sdk-1.4.11 → muse_code_sdk-1.4.12}/tests/test_publish_shape.py +84 -14
- {muse_code_sdk-1.4.11 → muse_code_sdk-1.4.12}/tests/test_recovery_observation.py +12 -0
- muse_code_sdk-1.4.11/src/muse_code_sdk.egg-info/requires.txt +0 -1
- {muse_code_sdk-1.4.11 → muse_code_sdk-1.4.12}/LICENSE +0 -0
- {muse_code_sdk-1.4.11 → muse_code_sdk-1.4.12}/setup.cfg +0 -0
- {muse_code_sdk-1.4.11 → muse_code_sdk-1.4.12}/src/muse_code/connection/__init__.py +0 -0
- {muse_code_sdk-1.4.11 → muse_code_sdk-1.4.12}/src/muse_code/connection/connection.py +0 -0
- {muse_code_sdk-1.4.11 → muse_code_sdk-1.4.12}/src/muse_code/connection/discovery.py +0 -0
- {muse_code_sdk-1.4.11 → muse_code_sdk-1.4.12}/src/muse_code/connection/spawn.py +0 -0
- {muse_code_sdk-1.4.11 → muse_code_sdk-1.4.12}/src/muse_code/connection/validation.py +0 -0
- {muse_code_sdk-1.4.11 → muse_code_sdk-1.4.12}/src/muse_code/errors.py +0 -0
- {muse_code_sdk-1.4.11 → muse_code_sdk-1.4.12}/src/muse_code/facade/__init__.py +0 -0
- {muse_code_sdk-1.4.11 → muse_code_sdk-1.4.12}/src/muse_code/facade/client.py +0 -0
- {muse_code_sdk-1.4.11 → muse_code_sdk-1.4.12}/src/muse_code/facade/discarded.py +0 -0
- {muse_code_sdk-1.4.11 → muse_code_sdk-1.4.12}/src/muse_code/facade/gap_fill.py +0 -0
- {muse_code_sdk-1.4.11 → muse_code_sdk-1.4.12}/src/muse_code/facade/host_death.py +0 -0
- {muse_code_sdk-1.4.11 → muse_code_sdk-1.4.12}/src/muse_code/facade/host_start.py +0 -0
- {muse_code_sdk-1.4.11 → muse_code_sdk-1.4.12}/src/muse_code/facade/session.py +0 -0
- {muse_code_sdk-1.4.11 → muse_code_sdk-1.4.12}/src/muse_code/facade/turn_handle.py +0 -0
- {muse_code_sdk-1.4.11 → muse_code_sdk-1.4.12}/src/muse_code/facade/turn_submit.py +0 -0
- {muse_code_sdk-1.4.11 → muse_code_sdk-1.4.12}/src/muse_code/fold/__init__.py +0 -0
- {muse_code_sdk-1.4.11 → muse_code_sdk-1.4.12}/src/muse_code/fold/item_store.py +0 -0
- {muse_code_sdk-1.4.11 → muse_code_sdk-1.4.12}/src/muse_code/fold/session_fold.py +0 -0
- {muse_code_sdk-1.4.11 → muse_code_sdk-1.4.12}/src/muse_code/fold/state_store.py +0 -0
- {muse_code_sdk-1.4.11 → muse_code_sdk-1.4.12}/src/muse_code/pending/__init__.py +0 -0
- {muse_code_sdk-1.4.11 → muse_code_sdk-1.4.12}/src/muse_code/pending/pending_command_set.py +0 -0
- {muse_code_sdk-1.4.11 → muse_code_sdk-1.4.12}/src/muse_code/py.typed +0 -0
- {muse_code_sdk-1.4.11 → muse_code_sdk-1.4.12}/src/muse_code/replay.py +0 -0
- {muse_code_sdk-1.4.11 → muse_code_sdk-1.4.12}/src/muse_code/sync_facade.py +0 -0
- {muse_code_sdk-1.4.11 → muse_code_sdk-1.4.12}/src/muse_code_sdk.egg-info/SOURCES.txt +0 -0
- {muse_code_sdk-1.4.11 → muse_code_sdk-1.4.12}/src/muse_code_sdk.egg-info/dependency_links.txt +0 -0
- {muse_code_sdk-1.4.11 → muse_code_sdk-1.4.12}/src/muse_code_sdk.egg-info/top_level.txt +0 -0
- {muse_code_sdk-1.4.11 → muse_code_sdk-1.4.12}/tests/test_asyncio_cancellation.py +0 -0
- {muse_code_sdk-1.4.11 → muse_code_sdk-1.4.12}/tests/test_compatibility_row.py +0 -0
- {muse_code_sdk-1.4.11 → muse_code_sdk-1.4.12}/tests/test_connection.py +0 -0
- {muse_code_sdk-1.4.11 → muse_code_sdk-1.4.12}/tests/test_connection_error_arms.py +0 -0
- {muse_code_sdk-1.4.11 → muse_code_sdk-1.4.12}/tests/test_docs_generation.py +0 -0
- {muse_code_sdk-1.4.11 → muse_code_sdk-1.4.12}/tests/test_external_audience.py +0 -0
- {muse_code_sdk-1.4.11 → muse_code_sdk-1.4.12}/tests/test_facade_approval_resend.py +0 -0
- {muse_code_sdk-1.4.11 → muse_code_sdk-1.4.12}/tests/test_facade_gap_fill.py +0 -0
- {muse_code_sdk-1.4.11 → muse_code_sdk-1.4.12}/tests/test_facade_open_same_chunk.py +0 -0
- {muse_code_sdk-1.4.11 → muse_code_sdk-1.4.12}/tests/test_facade_persistent_approval.py +0 -0
- {muse_code_sdk-1.4.11 → muse_code_sdk-1.4.12}/tests/test_facade_required_selection.py +0 -0
- {muse_code_sdk-1.4.11 → muse_code_sdk-1.4.12}/tests/test_facade_turn.py +0 -0
- {muse_code_sdk-1.4.11 → muse_code_sdk-1.4.12}/tests/test_facade_user_input_resend.py +0 -0
- {muse_code_sdk-1.4.11 → muse_code_sdk-1.4.12}/tests/test_host_discovery.py +0 -0
- {muse_code_sdk-1.4.11 → muse_code_sdk-1.4.12}/tests/test_item_store.py +0 -0
- {muse_code_sdk-1.4.11 → muse_code_sdk-1.4.12}/tests/test_muse_serve_child.py +0 -0
- {muse_code_sdk-1.4.11 → muse_code_sdk-1.4.12}/tests/test_pending_command_corpus.py +0 -0
- {muse_code_sdk-1.4.11 → muse_code_sdk-1.4.12}/tests/test_pending_command_set.py +0 -0
- {muse_code_sdk-1.4.11 → muse_code_sdk-1.4.12}/tests/test_pending_command_wire_binding.py +0 -0
- {muse_code_sdk-1.4.11 → muse_code_sdk-1.4.12}/tests/test_permission_state_wire_types.py +0 -0
- {muse_code_sdk-1.4.11 → muse_code_sdk-1.4.12}/tests/test_public_mirror_text.py +0 -0
- {muse_code_sdk-1.4.11 → muse_code_sdk-1.4.12}/tests/test_session_fold.py +0 -0
- {muse_code_sdk-1.4.11 → muse_code_sdk-1.4.12}/tests/test_source_closure.py +0 -0
- {muse_code_sdk-1.4.11 → muse_code_sdk-1.4.12}/tests/test_spawn_handshake.py +0 -0
- {muse_code_sdk-1.4.11 → muse_code_sdk-1.4.12}/tests/test_spawn_host_lost.py +0 -0
- {muse_code_sdk-1.4.11 → muse_code_sdk-1.4.12}/tests/test_state_store.py +0 -0
- {muse_code_sdk-1.4.11 → muse_code_sdk-1.4.12}/tests/test_sync_facade.py +0 -0
- {muse_code_sdk-1.4.11 → muse_code_sdk-1.4.12}/tests/test_tolerance_corpus.py +0 -0
- {muse_code_sdk-1.4.11 → muse_code_sdk-1.4.12}/tests/test_transcript_replay.py +0 -0
- {muse_code_sdk-1.4.11 → muse_code_sdk-1.4.12}/tests/test_typing_gates.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: muse-code-sdk
|
|
3
|
-
Version: 1.4.
|
|
3
|
+
Version: 1.4.12
|
|
4
4
|
Summary: Muse Code SDK for MSP: asyncio-first facade over the generated wire types (muse-code-msp), with a thin sync wrapper.
|
|
5
5
|
License-Expression: MIT
|
|
6
6
|
Project-URL: Homepage, https://github.com/meta-models/muse-code-sdk
|
|
@@ -15,6 +15,7 @@ Requires-Python: >=3.10
|
|
|
15
15
|
Description-Content-Type: text/markdown
|
|
16
16
|
License-File: LICENSE
|
|
17
17
|
Requires-Dist: pydantic==2.13.5
|
|
18
|
+
Requires-Dist: muse-code-msp==1.4.12
|
|
18
19
|
Dynamic: license-file
|
|
19
20
|
|
|
20
21
|
# `muse-code-sdk`
|
|
@@ -42,13 +43,14 @@ Status: developer preview. Experimental protocol surfaces may change.
|
|
|
42
43
|
|
|
43
44
|
The generated MSP wire types live in the companion package
|
|
44
45
|
[`muse-code-msp`](https://pypi.org/project/muse-code-msp/) and are never
|
|
45
|
-
restated here
|
|
46
|
+
restated here; it is installed with this package, pinned to the same version.
|
|
47
|
+
One further runtime dependency: `pydantic`, exact-pinned.
|
|
46
48
|
Python 3.10+. Fully typed (`py.typed`).
|
|
47
49
|
|
|
48
50
|
## Install
|
|
49
51
|
|
|
50
52
|
```sh
|
|
51
|
-
pip install muse-code-sdk
|
|
53
|
+
pip install muse-code-sdk
|
|
52
54
|
```
|
|
53
55
|
|
|
54
56
|
You also need the Muse Code CLI: a `muse` binary on `PATH`, or an explicit
|
|
@@ -23,13 +23,14 @@ Status: developer preview. Experimental protocol surfaces may change.
|
|
|
23
23
|
|
|
24
24
|
The generated MSP wire types live in the companion package
|
|
25
25
|
[`muse-code-msp`](https://pypi.org/project/muse-code-msp/) and are never
|
|
26
|
-
restated here
|
|
26
|
+
restated here; it is installed with this package, pinned to the same version.
|
|
27
|
+
One further runtime dependency: `pydantic`, exact-pinned.
|
|
27
28
|
Python 3.10+. Fully typed (`py.typed`).
|
|
28
29
|
|
|
29
30
|
## Install
|
|
30
31
|
|
|
31
32
|
```sh
|
|
32
|
-
pip install muse-code-sdk
|
|
33
|
+
pip install muse-code-sdk
|
|
33
34
|
```
|
|
34
35
|
|
|
35
36
|
You also need the Muse Code CLI: a `muse` binary on `PATH`, or an explicit
|
|
@@ -5,19 +5,20 @@ build-backend = "setuptools.build_meta"
|
|
|
5
5
|
|
|
6
6
|
[project]
|
|
7
7
|
name = "muse-code-sdk"
|
|
8
|
-
version = "1.4.
|
|
8
|
+
version = "1.4.12"
|
|
9
9
|
description = "Muse Code SDK for MSP: asyncio-first facade over the generated wire types (muse-code-msp), with a thin sync wrapper."
|
|
10
10
|
readme = "README.md"
|
|
11
11
|
requires-python = ">=3.10"
|
|
12
12
|
license = "MIT"
|
|
13
13
|
license-files = ["LICENSE"]
|
|
14
|
-
#
|
|
15
|
-
# supply-chain exception
|
|
16
|
-
#
|
|
17
|
-
#
|
|
18
|
-
#
|
|
19
|
-
#
|
|
20
|
-
|
|
14
|
+
# Two runtime dependencies, both exact-pinned. pydantic is the deliberate,
|
|
15
|
+
# ruled supply-chain exception; bumps are deliberate reviewed PRs.
|
|
16
|
+
# muse-code-msp is the companion wire-types package generated and released
|
|
17
|
+
# from the same tree, so its pin is this package's own version: a plain
|
|
18
|
+
# `pip install muse-code-sdk` must import, and the release bump moves this
|
|
19
|
+
# package's version and its muse-code-msp pin together; pydantic bumps stay
|
|
20
|
+
# reviewed PRs (test_publish_shape pins the pairing).
|
|
21
|
+
dependencies = ["pydantic==2.13.5", "muse-code-msp==1.4.12"]
|
|
21
22
|
classifiers = [
|
|
22
23
|
# Alpha states the withheld promise: publication is permitted, but the
|
|
23
24
|
# stability promise and conformance claim stay withheld — experimental
|
|
@@ -80,7 +80,7 @@ __all__ = [
|
|
|
80
80
|
]
|
|
81
81
|
|
|
82
82
|
EXPECTED_SCHEMA_FINGERPRINT: Final[str] = (
|
|
83
|
-
"sha256:
|
|
83
|
+
"sha256:cfdb6d2525601b9bac76584ccb0610ab3a5539b5e4f51141f079b99b39fcb4e0"
|
|
84
84
|
)
|
|
85
85
|
"""The stable schema-bundle fingerprint this SDK was built against.
|
|
86
86
|
|
|
@@ -92,7 +92,7 @@ Python SDK reds this lane instead of drifting silently.
|
|
|
92
92
|
# The lockstep rule: the release train bumps pyproject.toml, the
|
|
93
93
|
# distribution-metadata authority; this constant is aligned at publication
|
|
94
94
|
# time.
|
|
95
|
-
__version__: Final[str] = "1.4.
|
|
95
|
+
__version__: Final[str] = "1.4.12"
|
|
96
96
|
|
|
97
97
|
# The facade is imported last so the constants above are already bound: the
|
|
98
98
|
# spawn path reads ``EXPECTED_SCHEMA_FINGERPRINT`` (lazily, at initialize
|
|
@@ -61,6 +61,7 @@ _REFRESH_FROM_UPDATE = frozenset(
|
|
|
61
61
|
_REFRESH_FROM_REQUEST = frozenset(
|
|
62
62
|
{
|
|
63
63
|
"approvalId",
|
|
64
|
+
"cuaCardFacts",
|
|
64
65
|
"itemId",
|
|
65
66
|
"judgeEscalated",
|
|
66
67
|
"protectedWrite",
|
|
@@ -297,6 +298,10 @@ class ApprovalRouter:
|
|
|
297
298
|
# Omitted rather than nulled when absent, like ``feedback`` below.
|
|
298
299
|
if "subagentOrigin" in params:
|
|
299
300
|
refreshed["subagentOrigin"] = params["subagentOrigin"]
|
|
301
|
+
# A Computer Use card's facts belong to the call, so they stay with
|
|
302
|
+
# the retained request across stages.
|
|
303
|
+
if "cuaCardFacts" in requested:
|
|
304
|
+
refreshed["cuaCardFacts"] = requested["cuaCardFacts"]
|
|
300
305
|
acked = self._in_flight.get(requested["approvalId"])
|
|
301
306
|
if acked is not None:
|
|
302
307
|
return self._after_ack(acked, refreshed)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: muse-code-sdk
|
|
3
|
-
Version: 1.4.
|
|
3
|
+
Version: 1.4.12
|
|
4
4
|
Summary: Muse Code SDK for MSP: asyncio-first facade over the generated wire types (muse-code-msp), with a thin sync wrapper.
|
|
5
5
|
License-Expression: MIT
|
|
6
6
|
Project-URL: Homepage, https://github.com/meta-models/muse-code-sdk
|
|
@@ -15,6 +15,7 @@ Requires-Python: >=3.10
|
|
|
15
15
|
Description-Content-Type: text/markdown
|
|
16
16
|
License-File: LICENSE
|
|
17
17
|
Requires-Dist: pydantic==2.13.5
|
|
18
|
+
Requires-Dist: muse-code-msp==1.4.12
|
|
18
19
|
Dynamic: license-file
|
|
19
20
|
|
|
20
21
|
# `muse-code-sdk`
|
|
@@ -42,13 +43,14 @@ Status: developer preview. Experimental protocol surfaces may change.
|
|
|
42
43
|
|
|
43
44
|
The generated MSP wire types live in the companion package
|
|
44
45
|
[`muse-code-msp`](https://pypi.org/project/muse-code-msp/) and are never
|
|
45
|
-
restated here
|
|
46
|
+
restated here; it is installed with this package, pinned to the same version.
|
|
47
|
+
One further runtime dependency: `pydantic`, exact-pinned.
|
|
46
48
|
Python 3.10+. Fully typed (`py.typed`).
|
|
47
49
|
|
|
48
50
|
## Install
|
|
49
51
|
|
|
50
52
|
```sh
|
|
51
|
-
pip install muse-code-sdk
|
|
53
|
+
pip install muse-code-sdk
|
|
52
54
|
```
|
|
53
55
|
|
|
54
56
|
You also need the Muse Code CLI: a `muse` binary on `PATH`, or an explicit
|
|
@@ -687,6 +687,53 @@ async def test_an_approval_updated_that_advances_the_requirement_re_asks_the_han
|
|
|
687
687
|
assert session.fold.pending_approvals() == []
|
|
688
688
|
|
|
689
689
|
|
|
690
|
+
CUA_CARD_FACTS: Params = {
|
|
691
|
+
"page": {"host": "shop.example.com", "hostAscii": "shop.example.com"},
|
|
692
|
+
"steps": [
|
|
693
|
+
{
|
|
694
|
+
"step": 1,
|
|
695
|
+
"kind": "verb:buy",
|
|
696
|
+
"action": "click",
|
|
697
|
+
"effect": "may buy or pay with your account",
|
|
698
|
+
"roleWord": "button",
|
|
699
|
+
"label": "Place order",
|
|
700
|
+
}
|
|
701
|
+
],
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
|
|
705
|
+
@pytest.mark.asyncio
|
|
706
|
+
@pytest.mark.parametrize("facts", [CUA_CARD_FACTS, None])
|
|
707
|
+
async def test_a_stage_advance_keeps_the_requests_cua_card_facts(facts: Any) -> None:
|
|
708
|
+
# `cuaCardFacts` belongs to the call and `approval/updated` doesn't carry
|
|
709
|
+
# it, so the refreshed stage-2 request keeps the original request's facts,
|
|
710
|
+
# and a request without facts stays without the key.
|
|
711
|
+
transport, session, minted = wired()
|
|
712
|
+
seen: List[Params] = []
|
|
713
|
+
|
|
714
|
+
def handler(request: Any) -> ApprovalDecisionInput:
|
|
715
|
+
seen.append(dict(request))
|
|
716
|
+
return ApprovalDecisionInput(choice_id="allow_once")
|
|
717
|
+
|
|
718
|
+
session.on_approval(handler)
|
|
719
|
+
overrides: Params = {} if facts is None else {"cuaCardFacts": facts}
|
|
720
|
+
first = session.apply(approval_requested(**overrides))
|
|
721
|
+
await wait_for_writes(transport, 1)
|
|
722
|
+
answer(transport, 0, {**decide_result("mint-0"), "terminal": False})
|
|
723
|
+
await settled_io(first.io)
|
|
724
|
+
|
|
725
|
+
updated = session.apply(stage_resolved(2, CHOICES))
|
|
726
|
+
await wait_for_writes(transport, 2)
|
|
727
|
+
assert minted == ["mint-0", "mint-1"]
|
|
728
|
+
assert len(seen) == 2
|
|
729
|
+
if facts is None:
|
|
730
|
+
assert "cuaCardFacts" not in seen[1]
|
|
731
|
+
else:
|
|
732
|
+
assert seen[1]["cuaCardFacts"] == facts
|
|
733
|
+
answer(transport, 1, decide_result("mint-1"))
|
|
734
|
+
await settled_io(updated.io)
|
|
735
|
+
|
|
736
|
+
|
|
690
737
|
@pytest.mark.asyncio
|
|
691
738
|
async def test_an_approval_updated_naming_the_decided_stage_asks_nothing() -> None:
|
|
692
739
|
# The per-stage latch: an update that still names the stage this client
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
The governing rule: the two package manifests carry the chartered shape
|
|
4
4
|
(names, floor, wheel purity, license, ``py.typed``), the dependency posture
|
|
5
5
|
holds (zero runtime deps in ``muse_code_msp``; exactly one exact-pinned
|
|
6
|
-
pydantic in ``muse_code``; every dev tool exact-pinned in the committed
|
|
6
|
+
pydantic plus ``muse-code-msp`` at the facade's own version in ``muse_code``; every dev tool exact-pinned in the committed
|
|
7
7
|
lock), and the schema-fingerprint pin binds to the stable bundle manifest so
|
|
8
8
|
a schema advance that forgets the Python SDK reds this lane.
|
|
9
9
|
"""
|
|
@@ -66,13 +66,17 @@ def test_facade_package_manifest_shape() -> None:
|
|
|
66
66
|
assert project["name"] == "muse-code-sdk"
|
|
67
67
|
assert project["requires-python"] == ">=3.10"
|
|
68
68
|
deps = project.get("dependencies", [])
|
|
69
|
-
assert len(deps) ==
|
|
69
|
+
assert len(deps) == 2, (
|
|
70
70
|
f"muse_code declares {len(deps)} runtime dependencies; the ruled "
|
|
71
|
-
"grant is exactly
|
|
71
|
+
"grant is exactly two: pydantic (exact pin) and the companion "
|
|
72
|
+
"wire-types package at the facade's own version"
|
|
72
73
|
)
|
|
73
74
|
assert re.fullmatch(r"pydantic==\d+(\.\d+)+", deps[0]), (
|
|
74
75
|
f"the pydantic dependency must be an exact pin, got: {deps[0]!r}"
|
|
75
76
|
)
|
|
77
|
+
assert deps[1] == f"muse-code-msp=={project['version']}", (
|
|
78
|
+
f"the wire-types dependency must be pinned to the facade's own version, got: {deps[1]!r}"
|
|
79
|
+
)
|
|
76
80
|
assert "license" in project
|
|
77
81
|
assert (SDK_DIR / "src" / "muse_code" / "py.typed").is_file()
|
|
78
82
|
|
|
@@ -16,6 +16,7 @@ regressed package.
|
|
|
16
16
|
from __future__ import annotations
|
|
17
17
|
|
|
18
18
|
import json
|
|
19
|
+
import os
|
|
19
20
|
import re
|
|
20
21
|
import subprocess
|
|
21
22
|
import sys
|
|
@@ -181,28 +182,39 @@ def test_the_classifiers_state_the_posture() -> None:
|
|
|
181
182
|
)
|
|
182
183
|
|
|
183
184
|
|
|
184
|
-
def
|
|
185
|
-
# The
|
|
186
|
-
#
|
|
187
|
-
#
|
|
188
|
-
#
|
|
189
|
-
#
|
|
190
|
-
#
|
|
191
|
-
#
|
|
192
|
-
#
|
|
193
|
-
|
|
194
|
-
|
|
185
|
+
def test_the_wire_types_are_a_same_version_pin_of_the_facade() -> None:
|
|
186
|
+
# The facade imports the generated wire types at import time, so a plain
|
|
187
|
+
# `pip install muse-code-sdk` must bring them along: muse-code-msp is the
|
|
188
|
+
# facade's second runtime dependency, exact-pinned to the facade's OWN
|
|
189
|
+
# version (both packages are versioned and released from the same tree,
|
|
190
|
+
# wire types first). The release bump rewrites the facade's version and
|
|
191
|
+
# its muse-code-msp pin together (pydantic bumps stay reviewed PRs); a
|
|
192
|
+
# drift between them, or a range, is a packaging defect. The wire-types
|
|
193
|
+
# package itself still declares no runtime dependency.
|
|
194
|
+
sdk = _pyproject(SDK_DIR)["project"]
|
|
195
|
+
deps = sdk.get("dependencies", [])
|
|
196
|
+
msp_pins = [d for d in deps if d.startswith("muse-code-msp")]
|
|
197
|
+
assert msp_pins == [f"muse-code-msp=={sdk['version']}"], (
|
|
198
|
+
f"muse-code-msp must be pinned to the facade's own version {sdk['version']!r}, got {msp_pins!r}"
|
|
199
|
+
)
|
|
200
|
+
assert sdk["version"] == _pyproject(MSP_DIR)["project"]["version"]
|
|
201
|
+
assert [d for d in deps if not d.startswith("muse-code-msp")] == [
|
|
202
|
+
d for d in deps if d.startswith("pydantic==")
|
|
203
|
+
], "besides the wire types, pydantic (exact pin) is the only runtime dependency"
|
|
195
204
|
assert _pyproject(MSP_DIR)["project"].get("dependencies", []) == []
|
|
196
205
|
|
|
197
206
|
|
|
198
207
|
def _run_gate(
|
|
199
|
-
*args: str, env: dict[str, str] | None = None
|
|
208
|
+
*args: str, env: dict[str, str] | None = None, script: Path = GATE_SCRIPT
|
|
200
209
|
) -> subprocess.CompletedProcess[str]:
|
|
201
210
|
# The env is built from scratch — GITHUB_ACTIONS and the OIDC variables
|
|
202
211
|
# are absent unless a test passes them — so the refusal arms below see
|
|
203
|
-
# exactly the context they assert, wherever the suite itself runs.
|
|
212
|
+
# exactly the context they assert, wherever the suite itself runs. Every
|
|
213
|
+
# gate run, the checked-in script or a copied layout's, goes through here,
|
|
214
|
+
# so each carries the same wall (review 5581715933: an uncapped child wait
|
|
215
|
+
# would hang the lane instead of failing a named test).
|
|
204
216
|
return subprocess.run(
|
|
205
|
-
[str(
|
|
217
|
+
[str(script), *args],
|
|
206
218
|
capture_output=True,
|
|
207
219
|
text=True,
|
|
208
220
|
timeout=GATE_WALL_SECONDS,
|
|
@@ -361,6 +373,64 @@ def test_the_publish_path_cannot_ship_a_wheel_without_its_row(tmp_path: Path) ->
|
|
|
361
373
|
assert "do not match the derived rows" in result.stderr
|
|
362
374
|
|
|
363
375
|
|
|
376
|
+
def _copied_gate_layout(tmp_path: Path) -> Path:
|
|
377
|
+
# The gate reads its repo root from its own location, so a copy of the
|
|
378
|
+
# script plus exactly the files its manifest-identity gates read makes a
|
|
379
|
+
# seedable tree; build-only mode notes a missing anchor and tolerates a
|
|
380
|
+
# non-git tree, so the run reaches the dependency-posture gate.
|
|
381
|
+
for source in (
|
|
382
|
+
"scripts/publish-sdk-pypi.sh",
|
|
383
|
+
"scripts/check-public-mirror-text.py",
|
|
384
|
+
"clients/msp-py/pyproject.toml",
|
|
385
|
+
"clients/msp-py/LICENSE",
|
|
386
|
+
"clients/msp-py/README.md",
|
|
387
|
+
"clients/sdk-py/pyproject.toml",
|
|
388
|
+
"clients/sdk-py/LICENSE",
|
|
389
|
+
"clients/sdk-py/README.md",
|
|
390
|
+
"schema/msp/stable/manifest.json",
|
|
391
|
+
):
|
|
392
|
+
target = tmp_path / source
|
|
393
|
+
target.parent.mkdir(parents=True, exist_ok=True)
|
|
394
|
+
target.write_bytes((PROJECT_ROOT / source).read_bytes())
|
|
395
|
+
script = tmp_path / "scripts" / "publish-sdk-pypi.sh"
|
|
396
|
+
script.chmod(0o755)
|
|
397
|
+
return script
|
|
398
|
+
|
|
399
|
+
|
|
400
|
+
def _run_copied_gate(script: Path) -> subprocess.CompletedProcess[str]:
|
|
401
|
+
return _run_gate("--build-only", env={"HOME": os.environ.get("HOME", "/tmp")}, script=script)
|
|
402
|
+
|
|
403
|
+
|
|
404
|
+
def test_the_dependency_gate_refuses_a_drifted_or_missing_wire_types_pin(tmp_path: Path) -> None:
|
|
405
|
+
# The same-version rule: the wire-types pin must equal the facade's own
|
|
406
|
+
# version. The committed tree can never carry a drift (the pairing test
|
|
407
|
+
# above), so each fault is seeded into a copied layout and the gate must
|
|
408
|
+
# die naming the facade's own version, not pass to the build.
|
|
409
|
+
manifest = PROJECT_ROOT / "clients" / "sdk-py" / "pyproject.toml"
|
|
410
|
+
own = _pyproject(SDK_DIR)["project"]["version"]
|
|
411
|
+
for label, seeded in (
|
|
412
|
+
("drifted", manifest.read_text().replace('"muse-code-msp==', '"muse-code-msp==1.0.0-never-', 1)),
|
|
413
|
+
("missing", re.sub(r',\s*"muse-code-msp==[^"]+"', "", manifest.read_text(), count=1)),
|
|
414
|
+
# A valid-shape pin at the WRONG version: the shape regex accepts it, only the ==own-version half refuses
|
|
415
|
+
# it (review 5580868376 P1: without this seed a gate trimmed to the shape check stays green).
|
|
416
|
+
("stale", manifest.read_text().replace(f'"muse-code-msp=={own}"', '"muse-code-msp==0.0.1"', 1)),
|
|
417
|
+
):
|
|
418
|
+
root = tmp_path / label
|
|
419
|
+
script = _copied_gate_layout(root)
|
|
420
|
+
copy = root / "clients" / "sdk-py" / "pyproject.toml"
|
|
421
|
+
assert seeded != manifest.read_text(), f"{label}: the fault must change the manifest"
|
|
422
|
+
copy.write_text(seeded)
|
|
423
|
+
result = _run_copied_gate(script)
|
|
424
|
+
assert result.returncode != 0, f"{label}: the gate must refuse:\n{result.stdout}\n{result.stderr}"
|
|
425
|
+
assert "the facade's own version" in (result.stdout + result.stderr), (
|
|
426
|
+
f"{label}: the refusal must name the same-version rule:\n{result.stdout}\n{result.stderr}"
|
|
427
|
+
)
|
|
428
|
+
if label == "stale":
|
|
429
|
+
assert "muse-code-msp==0.0.1" in (result.stdout + result.stderr), (
|
|
430
|
+
f"{label}: the refusal must quote the stale pin it found:\n{result.stdout}\n{result.stderr}"
|
|
431
|
+
)
|
|
432
|
+
|
|
433
|
+
|
|
364
434
|
def test_a_pre_release_version_refuses_the_row_derivation(tmp_path: Path) -> None:
|
|
365
435
|
# The plain-X.Y.Z refusal, proven under its fault: setuptools would
|
|
366
436
|
# normalize a PEP 440 suffix in the built wheel's filename, so the
|
|
@@ -151,6 +151,18 @@ async def test_absent_malformed_or_unbound_observation_is_unknown(diagnose, obse
|
|
|
151
151
|
@pytest.mark.asyncio
|
|
152
152
|
@pytest.mark.parametrize("delta, support, expected", [
|
|
153
153
|
({"effectiveMode": "on", "effectiveSource": "request", "qualification": "qualified"}, "recognized", "qualified"),
|
|
154
|
+
pytest.param(
|
|
155
|
+
{"effectiveMode": "on", "effectiveSource": "retained", "qualification": "qualified"},
|
|
156
|
+
"recognized", "qualified", id="retained-source",
|
|
157
|
+
),
|
|
158
|
+
pytest.param(
|
|
159
|
+
{"effectiveMode": "off", "effectiveSource": "hostPolicy", "qualification": "notRequired"},
|
|
160
|
+
"recognized", "unknown", id="host-policy-source",
|
|
161
|
+
),
|
|
162
|
+
pytest.param(
|
|
163
|
+
{"effectiveMode": "on", "effectiveSource": "request", "qualification": "unknown"},
|
|
164
|
+
"recognized", "unknown", id="unknown-qualification",
|
|
165
|
+
),
|
|
154
166
|
pytest.param(
|
|
155
167
|
{"effectiveMode": "on", "effectiveSource": "request", "qualification": "future-value"},
|
|
156
168
|
"unknown", "unknown", id="future-qualification",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
pydantic==2.13.5
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{muse_code_sdk-1.4.11 → muse_code_sdk-1.4.12}/src/muse_code_sdk.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|