basecradle 0.2.0__tar.gz → 0.3.0__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 (44) hide show
  1. {basecradle-0.2.0 → basecradle-0.3.0}/CHANGELOG.md +21 -0
  2. {basecradle-0.2.0 → basecradle-0.3.0}/CLAUDE.md +47 -0
  3. {basecradle-0.2.0 → basecradle-0.3.0}/PKG-INFO +1 -1
  4. {basecradle-0.2.0 → basecradle-0.3.0}/src/basecradle/__init__.py +4 -0
  5. {basecradle-0.2.0 → basecradle-0.3.0}/src/basecradle/_dashboard.py +27 -3
  6. basecradle-0.3.0/src/basecradle/_version.py +1 -0
  7. {basecradle-0.2.0 → basecradle-0.3.0}/tests/conftest.py +7 -1
  8. {basecradle-0.2.0 → basecradle-0.3.0}/tests/test_dashboard.py +28 -1
  9. basecradle-0.2.0/src/basecradle/_version.py +0 -1
  10. {basecradle-0.2.0 → basecradle-0.3.0}/.github/workflows/ci.yml +0 -0
  11. {basecradle-0.2.0 → basecradle-0.3.0}/.github/workflows/release.yml +0 -0
  12. {basecradle-0.2.0 → basecradle-0.3.0}/.gitignore +0 -0
  13. {basecradle-0.2.0 → basecradle-0.3.0}/.python-version +0 -0
  14. {basecradle-0.2.0 → basecradle-0.3.0}/LICENSE +0 -0
  15. {basecradle-0.2.0 → basecradle-0.3.0}/README.md +0 -0
  16. {basecradle-0.2.0 → basecradle-0.3.0}/pyproject.toml +0 -0
  17. {basecradle-0.2.0 → basecradle-0.3.0}/src/basecradle/_client.py +0 -0
  18. {basecradle-0.2.0 → basecradle-0.3.0}/src/basecradle/_exceptions.py +0 -0
  19. {basecradle-0.2.0 → basecradle-0.3.0}/src/basecradle/_items.py +0 -0
  20. {basecradle-0.2.0 → basecradle-0.3.0}/src/basecradle/_models.py +0 -0
  21. {basecradle-0.2.0 → basecradle-0.3.0}/src/basecradle/_pagination.py +0 -0
  22. {basecradle-0.2.0 → basecradle-0.3.0}/src/basecradle/_sessions.py +0 -0
  23. {basecradle-0.2.0 → basecradle-0.3.0}/src/basecradle/_timelines.py +0 -0
  24. {basecradle-0.2.0 → basecradle-0.3.0}/src/basecradle/_users.py +0 -0
  25. {basecradle-0.2.0 → basecradle-0.3.0}/src/basecradle/_webhooks.py +0 -0
  26. {basecradle-0.2.0 → basecradle-0.3.0}/src/basecradle/py.typed +0 -0
  27. {basecradle-0.2.0 → basecradle-0.3.0}/tests/__init__.py +0 -0
  28. {basecradle-0.2.0 → basecradle-0.3.0}/tests/test_async_client.py +0 -0
  29. {basecradle-0.2.0 → basecradle-0.3.0}/tests/test_async_resources.py +0 -0
  30. {basecradle-0.2.0 → basecradle-0.3.0}/tests/test_client.py +0 -0
  31. {basecradle-0.2.0 → basecradle-0.3.0}/tests/test_drift_guard.py +0 -0
  32. {basecradle-0.2.0 → basecradle-0.3.0}/tests/test_errors.py +0 -0
  33. {basecradle-0.2.0 → basecradle-0.3.0}/tests/test_items.py +0 -0
  34. {basecradle-0.2.0 → basecradle-0.3.0}/tests/test_login.py +0 -0
  35. {basecradle-0.2.0 → basecradle-0.3.0}/tests/test_models.py +0 -0
  36. {basecradle-0.2.0 → basecradle-0.3.0}/tests/test_pagination.py +0 -0
  37. {basecradle-0.2.0 → basecradle-0.3.0}/tests/test_parity.py +0 -0
  38. {basecradle-0.2.0 → basecradle-0.3.0}/tests/test_readme.py +0 -0
  39. {basecradle-0.2.0 → basecradle-0.3.0}/tests/test_sessions.py +0 -0
  40. {basecradle-0.2.0 → basecradle-0.3.0}/tests/test_timelines.py +0 -0
  41. {basecradle-0.2.0 → basecradle-0.3.0}/tests/test_users.py +0 -0
  42. {basecradle-0.2.0 → basecradle-0.3.0}/tests/test_version.py +0 -0
  43. {basecradle-0.2.0 → basecradle-0.3.0}/tests/test_webhooks.py +0 -0
  44. {basecradle-0.2.0 → basecradle-0.3.0}/uv.lock +0 -0
@@ -6,6 +6,26 @@ The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and
6
6
  project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). The API the
7
7
  SDK wraps is unversioned and additive-only, so SDK minor versions track API additions.
8
8
 
9
+ ## [0.3.0] - 2026-06-03
10
+
11
+ Tracks the platform's Dashboard documentation reshape
12
+ ([basecradle/basecradle#256](https://github.com/basecradle/basecradle/pull/256)): the
13
+ never-populated `sdk` slot is gone, replaced by per-language `sdks` objects and a
14
+ `changelog` pointer.
15
+
16
+ ### Added
17
+
18
+ - **`me.documentation.changelog`** — the platform changelog URL.
19
+ - **`me.documentation.sdks`** — the official SDKs, typed and keyed by language:
20
+ `sdks.python.repository` and `sdks.python.package`. New languages and new per-SDK
21
+ pointers are additive.
22
+
23
+ ### Removed
24
+
25
+ - **`me.documentation.sdk`** — the placeholder that only ever returned `None`. The
26
+ platform removed it from the wire; reading it now raises the standard
27
+ "API did not return" `AttributeError`.
28
+
9
29
  ## [0.2.0] - 2026-06-03
10
30
 
11
31
  The async release: the same SDK for async code, on one shared core.
@@ -51,5 +71,6 @@ The first release: complete coverage of the BaseCradle API, for humans and AI pe
51
71
  - **The spec drift-guard** — CI fails if the live API ever has endpoints this SDK doesn't
52
72
  cover.
53
73
 
74
+ [0.3.0]: https://github.com/basecradle/basecradle-python/releases/tag/v0.3.0
54
75
  [0.2.0]: https://github.com/basecradle/basecradle-python/releases/tag/v0.2.0
55
76
  [0.1.0]: https://github.com/basecradle/basecradle-python/releases/tag/v0.1.0
@@ -91,6 +91,24 @@ Runtime dependencies: `httpx`. That's the list. Every addition is argued in a PR
91
91
  - **Versioning**: semver, `0.x` until the platform owner declares 1.0. The API is additive-only, so SDK minor versions track API additions.
92
92
  - **Public package name**: `basecradle` on PyPI. Publishing is via PyPI **Trusted Publishing** (GitHub Actions OIDC — no stored credentials), on git tag.
93
93
 
94
+ ## Releasing
95
+
96
+ The pipeline is `.github/workflows/release.yml`: pushing a `v*` tag → build → TestPyPI rehearsal → human approval → PyPI, all via OIDC Trusted Publishing (zero stored credentials). The workflow filename and the environment names (`testpypi`, `pypi`) are **contractual** — they match the Trusted Publisher registrations on PyPI/TestPyPI; renaming any of them breaks the trust relationship. The `pypi` environment requires the project owner's approval.
97
+
98
+ The procedure, in order:
99
+
100
+ 1. **Release PR**: bump `src/basecradle/_version.py` from `X.Y.Z.dev0` to `X.Y.Z` and add the `CHANGELOG.md` entry (Keep a Changelog format). Merge on green CI. Do **not** put `Closes #N` on release PRs — see step 6.
101
+ 2. **Tag**: on main after the merge — `git tag vX.Y.Z && git push origin vX.Y.Z`. This triggers the release workflow.
102
+ 3. **Verify the rehearsal**: the TestPyPI publish is automatic. In a clean venv:
103
+ `pip install -i https://test.pypi.org/simple/ --extra-index-url https://pypi.org/simple/ basecradle==X.Y.Z`
104
+ The extra index is required (httpx lives on real PyPI, not TestPyPI). Expect a minute or two of index-propagation lag — retry, don't panic.
105
+ 4. **The human gate**: the workflow waits on the `pypi` environment. Announce this unmissably (see the human-action-gate convention above); only the project owner can approve.
106
+ 5. **Verify the release**: clean venv, `pip install basecradle==X.Y.Z`, check import + `__version__` + both clients construct, and that https://pypi.org/project/basecradle/ renders. (The PyPI JSON API caches — pip resolving the new version is the real test.)
107
+ 6. **Close the release issue manually** with the verification record. Release issues never auto-close via a merged PR: an issue that closed before the publish was verified would lie.
108
+ 7. **Post-release version bump**: the first PR of the next cycle bumps `_version.py` to the next minor `.dev0` (after `0.2.0` ships, main becomes `0.3.0.dev0`) so dev builds are always distinguishable from releases.
109
+
110
+ Versioning facts: `_version.py` is the single source of truth (hatchling reads it; `pyproject.toml` declares `dynamic = ["version"]`). Local editable installs cache metadata — after editing the version, run `uv sync --reinstall-package basecradle` or the version-wiring test fails (that failure is the test doing its job).
111
+
94
112
  ## Where to Start
95
113
 
96
114
  The build is fully mapped in this repo's **GitHub Issues** — each issue is one PR-sized unit with its design details and steps, in dependency order. Start at the lowest open issue number, plan-first for anything non-trivial, and work through them in order unless an issue says otherwise.
@@ -99,6 +117,35 @@ The build is fully mapped in this repo's **GitHub Issues** — each issue is one
99
117
  gh issue list --repo basecradle/basecradle-python --state open
100
118
  ```
101
119
 
120
+ ## Cross-Repo Handoffs
121
+
122
+ BaseCradle is built across multiple repositories — the private Rails core, the public SDKs, and future ecosystem repos — each worked on by its own Claude Code sessions. Sessions cannot reach across repos; the human (Drawk) is the relay between them. This procedure makes that relay lossless and identical in every direction. It is ecosystem-wide: every BaseCradle repo carries this same section in its CLAUDE.md (see "Propagating this procedure"), so both ends of any handoff follow the same rules.
123
+
124
+ ### Sending work to another repo
125
+
126
+ When work in this repo creates work in another BaseCradle repo (a wire-shape change an SDK must mirror, a bug discovered in another repo's code, a feature needing a counterpart):
127
+
128
+ 1. **File the issue(s) on the target repo.** The issue is the complete, self-sufficient spec: the trigger (what changed here, with PR links), what the target repo must do, ordering/timing constraints ("release only after the platform deploys"), and the definition of done. Write it for a reader with zero context from the conversation that produced it.
129
+ 2. **Compose the handoff prompt and present it to Drawk in one copy-pasteable code block, immediately after filing.** Drawk pastes it verbatim into a Claude Code session running in the target repo. Structure, in order:
130
+ - Opening line: `Cross-repo handoff: work <issue URL>` — the receiving session recognizes a handoff by this line.
131
+ - The trigger in one or two lines, with links.
132
+ - Cross-repo state the receiving session cannot discover on its own: what is deployed, what is verified on production, what is blocked on what.
133
+ - What "done" looks like, including whether a return handoff is required.
134
+ 3. **The issue is the spec; the prompt is the pointer.** Never put a requirement only in the prompt — prompts are ephemeral, issues persist. If prompt and issue disagree, the issue wins, and the issue gets corrected.
135
+
136
+ ### Receiving work from another repo
137
+
138
+ When Drawk pastes a prompt beginning `Cross-repo handoff:`:
139
+
140
+ 1. Read the referenced issue(s) in full before acting — the issue is the spec.
141
+ 2. Execute under **this** repo's conventions (its own CLAUDE.md, workflow, tests). The sending repo's conventions do not transfer.
142
+ 3. Respect the issue's ordering constraints (e.g., verify a dependency has deployed before releasing).
143
+ 4. When done, report completion to Drawk: what shipped, version numbers, links. If the issue requires a return handoff (the sending repo is blocked on this work), compose one per "Sending work to another repo."
144
+
145
+ ### Propagating this procedure
146
+
147
+ Every BaseCradle ecosystem repo carries this same "Cross-Repo Handoffs" section in its CLAUDE.md, copied verbatim (it is written repo-agnostically so no adaptation is needed). When handing off to a repo whose CLAUDE.md lacks the section — always true for a brand-new repo — the handoff prompt's definition of done includes adding it, copied from this repo's CLAUDE.md by file-system path (the same mechanism public repos use to reference `constitution.md`).
148
+
102
149
  ## Development Commands
103
150
 
104
151
  ```bash
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: basecradle
3
- Version: 0.2.0
3
+ Version: 0.3.0
4
4
  Summary: The official Python SDK for BaseCradle — a communications platform where humans and AI are equal peers.
5
5
  Project-URL: Homepage, https://basecradle.com
6
6
  Project-URL: Documentation, https://basecradle.com/docs/api
@@ -10,6 +10,8 @@ from basecradle._dashboard import (
10
10
  DashboardDocumentation,
11
11
  DashboardEnvironment,
12
12
  DashboardInteraction,
13
+ DashboardSdk,
14
+ DashboardSdks,
13
15
  DashboardTimelines,
14
16
  )
15
17
  from basecradle._exceptions import (
@@ -90,6 +92,8 @@ __all__ = [
90
92
  "DashboardDocumentation",
91
93
  "DashboardEnvironment",
92
94
  "DashboardInteraction",
95
+ "DashboardSdk",
96
+ "DashboardSdks",
93
97
  "DashboardTimelines",
94
98
  "Item",
95
99
  "Message",
@@ -1,6 +1,6 @@
1
1
  """The Dashboard — the one place any peer lands to orient and navigate.
2
2
 
3
- Five sections, mirroring ``GET /users/dashboard`` exactly: identity (``you``),
3
+ Five sections, mirroring ``GET /users/dashboard`` exactly: identity,
4
4
  environment, interaction, account, and documentation.
5
5
  """
6
6
 
@@ -15,6 +15,8 @@ __all__ = [
15
15
  "DashboardDocumentation",
16
16
  "DashboardEnvironment",
17
17
  "DashboardInteraction",
18
+ "DashboardSdk",
19
+ "DashboardSdks",
18
20
  "DashboardTimelines",
19
21
  ]
20
22
 
@@ -53,14 +55,36 @@ class DashboardAccount(ApiObject):
53
55
  change_password_url: str
54
56
 
55
57
 
58
+ class DashboardSdk(ApiObject):
59
+ """One official SDK: where its code lives and where to install it from.
60
+
61
+ Per-SDK pointers are additive — fields the platform adds after this release are
62
+ readable immediately (``ApiObject`` reads the wire).
63
+ """
64
+
65
+ repository: str
66
+ package: str
67
+
68
+
69
+ class DashboardSdks(ApiObject):
70
+ """The official SDKs, keyed by language.
71
+
72
+ Languages the platform adds after this release are readable immediately as untyped
73
+ objects; typed attributes are added here as each SDK ships.
74
+ """
75
+
76
+ python: DashboardSdk
77
+
78
+
56
79
  class DashboardDocumentation(ApiObject):
57
- """The guides — prose, machine contract, interactive reference, and (in time) the SDK."""
80
+ """The guides — prose, machine contract, interactive reference, changelog, and the SDKs."""
58
81
 
59
82
  user_guide: str
60
83
  api: str
84
+ changelog: str
61
85
  openapi: str
62
86
  reference: str
63
- sdk: str | None
87
+ sdks: DashboardSdks
64
88
 
65
89
 
66
90
  class Dashboard(ApiObject):
@@ -0,0 +1 @@
1
+ __version__ = "0.3.0"
@@ -287,9 +287,15 @@ DASHBOARD_RESPONSE = {
287
287
  "documentation": {
288
288
  "user_guide": "https://basecradle.com/docs/user_guide.md",
289
289
  "api": "https://basecradle.com/docs/api.md",
290
+ "changelog": "https://basecradle.com/docs/changelog.md",
290
291
  "openapi": "https://basecradle.com/docs/api.yaml",
291
292
  "reference": "https://basecradle.com/docs/api/reference",
292
- "sdk": None,
293
+ "sdks": {
294
+ "python": {
295
+ "repository": "https://github.com/basecradle/basecradle-python",
296
+ "package": "https://pypi.org/project/basecradle/",
297
+ },
298
+ },
293
299
  },
294
300
  }
295
301
 
@@ -8,6 +8,8 @@ from basecradle import (
8
8
  DashboardDocumentation,
9
9
  DashboardEnvironment,
10
10
  DashboardInteraction,
11
+ DashboardSdk,
12
+ DashboardSdks,
11
13
  DashboardTimelines,
12
14
  UnauthorizedError,
13
15
  User,
@@ -66,7 +68,32 @@ class TestMe:
66
68
  assert me.interaction.timelines.url == "https://basecradle.com/timelines.json"
67
69
  assert me.account.sessions_url == "https://basecradle.com/users/sessions.json"
68
70
  assert me.documentation.openapi == "https://basecradle.com/docs/api.yaml"
69
- assert me.documentation.sdk is None
71
+ assert me.documentation.changelog == "https://basecradle.com/docs/changelog.md"
72
+
73
+ def test_documentation_sdks_are_typed_by_language(self, bc, api):
74
+ """The official SDKs, keyed by language, each entry an object of per-SDK pointers.
75
+
76
+ Pins the platform shape from core PR #256 — a regression there fails here.
77
+ """
78
+ api.get("/users/dashboard").respond(200, json=DASHBOARD_RESPONSE)
79
+
80
+ sdks = bc.me.documentation.sdks
81
+
82
+ assert isinstance(sdks, DashboardSdks)
83
+ assert isinstance(sdks.python, DashboardSdk)
84
+ assert sdks.python.repository == "https://github.com/basecradle/basecradle-python"
85
+ assert sdks.python.package == "https://pypi.org/project/basecradle/"
86
+
87
+ def test_the_removed_sdk_slot_raises_the_standard_attribute_error(self, bc, api):
88
+ """Core PR #256 removed `sdk` (the never-populated placeholder) from the wire.
89
+
90
+ Reading it gets the standard "API did not return" AttributeError listing the
91
+ real fields — never a silent None.
92
+ """
93
+ api.get("/users/dashboard").respond(200, json=DASHBOARD_RESPONSE)
94
+
95
+ with pytest.raises(AttributeError, match="did not return 'sdk'"):
96
+ bc.me.documentation.sdk
70
97
 
71
98
  def test_me_is_fetched_fresh_on_every_access(self, bc, api):
72
99
  """Decided in issue #4: no caching — bc.me is the live answer to "who am I?"."""
@@ -1 +0,0 @@
1
- __version__ = "0.2.0"
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes