From 26cefa50bbc58e93a5ee32349cefd2222a68d9dc Mon Sep 17 00:00:00 2001 From: mohamed-elkholy95 Date: Tue, 2 Jun 2026 00:22:05 -0400 Subject: [PATCH 1/2] fix(release): keep sdk core pin in lockstep --- .github/workflows/ci-pythinker-cli.yml | 3 +- .github/workflows/release-pythinker-cli.yml | 3 +- .../check_pythinker_dependency_versions.py | 25 +++++++++ scripts/release.py | 4 ++ sdks/pythinker-sdk/pyproject.toml | 2 +- tests/test_release_py.py | 51 +++++++++++++++++++ tests/test_version_lockstep.py | 22 ++++++-- 7 files changed, 103 insertions(+), 7 deletions(-) diff --git a/.github/workflows/ci-pythinker-cli.yml b/.github/workflows/ci-pythinker-cli.yml index 82a2d247..d6bbac8a 100644 --- a/.github/workflows/ci-pythinker-cli.yml +++ b/.github/workflows/ci-pythinker-cli.yml @@ -261,7 +261,8 @@ jobs: --root-pyproject pyproject.toml \ --pythinker-core-pyproject packages/pythinker-core/pyproject.toml \ --pythinker-host-pyproject packages/pythinker-host/pyproject.toml \ - --pythinker-review-pyproject packages/pythinker-review/pyproject.toml + --pythinker-review-pyproject packages/pythinker-review/pyproject.toml \ + --pythinker-sdk-pyproject sdks/pythinker-sdk/pyproject.toml - name: Check pythinker-code version alignment if: steps.version.outputs.bump == 'true' diff --git a/.github/workflows/release-pythinker-cli.yml b/.github/workflows/release-pythinker-cli.yml index 177d93ac..46b218cd 100644 --- a/.github/workflows/release-pythinker-cli.yml +++ b/.github/workflows/release-pythinker-cli.yml @@ -67,7 +67,8 @@ jobs: --root-pyproject pyproject.toml \ --pythinker-core-pyproject packages/pythinker-core/pyproject.toml \ --pythinker-host-pyproject packages/pythinker-host/pyproject.toml \ - --pythinker-review-pyproject packages/pythinker-review/pyproject.toml + --pythinker-review-pyproject packages/pythinker-review/pyproject.toml \ + --pythinker-sdk-pyproject sdks/pythinker-sdk/pyproject.toml # Hard release gate: every PyPI release must ship a matching README + # CHANGELOG update. README.md must contain a "What's New in " diff --git a/scripts/check_pythinker_dependency_versions.py b/scripts/check_pythinker_dependency_versions.py index 44604957..2f2af57f 100644 --- a/scripts/check_pythinker_dependency_versions.py +++ b/scripts/check_pythinker_dependency_versions.py @@ -46,6 +46,7 @@ def main() -> int: parser.add_argument("--pythinker-core-pyproject", type=Path, required=True) parser.add_argument("--pythinker-host-pyproject", type=Path, required=True) parser.add_argument("--pythinker-review-pyproject", type=Path, required=True) + parser.add_argument("--pythinker-sdk-pyproject", type=Path, required=True) args = parser.parse_args() try: @@ -63,6 +64,7 @@ def main() -> int: return 1 errors: list[str] = [] + package_versions: dict[str, str] = {} for name, pyproject_path in ( ("pythinker-core", args.pythinker_core_pyproject), ("pythinker-host", args.pythinker_host_pyproject), @@ -73,6 +75,7 @@ def main() -> int: except ValueError as exc: errors.append(str(exc)) continue + package_versions[name] = package_version pinned_version = find_pinned_dependency(deps, name) if pinned_version is None: @@ -85,6 +88,28 @@ def main() -> int: f"but {pyproject_path} has {package_version}." ) + try: + sdk_project = load_project_table(args.pythinker_sdk_pyproject) + except ValueError as exc: + errors.append(str(exc)) + else: + sdk_deps = sdk_project.get("dependencies", []) + if not isinstance(sdk_deps, list): + errors.append( + f"project.dependencies must be a list in {args.pythinker_sdk_pyproject}" + ) + elif core_version := package_versions.get("pythinker-core"): + sdk_core_pin = find_pinned_dependency(sdk_deps, "pythinker-core") + if sdk_core_pin is None: + errors.append( + f"Missing pinned dependency for pythinker-core in {args.pythinker_sdk_pyproject}." + ) + elif sdk_core_pin != core_version: + errors.append( + f"pythinker-sdk core dependency mismatch: sdk depends on {sdk_core_pin}, " + f"but {args.pythinker_core_pyproject} has {core_version}." + ) + if errors: for error in errors: print(f"error: {error}", file=sys.stderr) diff --git a/scripts/release.py b/scripts/release.py index ed9f9654..dc8133cc 100644 --- a/scripts/release.py +++ b/scripts/release.py @@ -27,6 +27,7 @@ CORE_PYPROJECT = REPO_ROOT / "packages" / "pythinker-core" / "pyproject.toml" HOST_PYPROJECT = REPO_ROOT / "packages" / "pythinker-host" / "pyproject.toml" REVIEW_PYPROJECT = REPO_ROOT / "packages" / "pythinker-review" / "pyproject.toml" +SDK_PYPROJECT = REPO_ROOT / "sdks" / "pythinker-sdk" / "pyproject.toml" # Single source for the three hand-authored changelog files. validate() asserts # the `## Unreleased` anchor in ALL of them before any write, and rewrite() @@ -206,6 +207,7 @@ def rewrite(target: str, *, bump_core: str | None, bump_host: str | None) -> Non if bump_core: set_root_version(CORE_PYPROJECT, bump_core) set_dependency_pin(ROOT_PYPROJECT, "pythinker-core", bump_core) + set_dependency_pin(SDK_PYPROJECT, "pythinker-core", bump_core) if bump_host: set_root_version(HOST_PYPROJECT, bump_host) set_dependency_pin(ROOT_PYPROJECT, "pythinker-host", bump_host) @@ -247,6 +249,8 @@ def rewrite(target: str, *, bump_core: str | None, bump_host: str | None) -> Non "packages/pythinker-host/pyproject.toml", "--pythinker-review-pyproject", "packages/pythinker-review/pyproject.toml", + "--pythinker-sdk-pyproject", + "sdks/pythinker-sdk/pyproject.toml", ], ["uv", "sync", "--frozen", "--all-extras", "--all-packages"], ["uv", "run", "pytest", "tests/test_version_lockstep.py", "-q"], diff --git a/sdks/pythinker-sdk/pyproject.toml b/sdks/pythinker-sdk/pyproject.toml index 2e45e8dc..7a9d0cb5 100644 --- a/sdks/pythinker-sdk/pyproject.toml +++ b/sdks/pythinker-sdk/pyproject.toml @@ -19,7 +19,7 @@ classifiers = [ "Topic :: Software Development :: Libraries :: Python Modules", "Topic :: Scientific/Engineering :: Artificial Intelligence", ] -dependencies = ["pythinker-core==1.1.1", "mcp>=1.23,<2", "httpx>=0.28.1"] +dependencies = ["pythinker-core==1.2.0", "mcp>=1.23,<2", "httpx>=0.28.1"] [project.urls] Homepage = "https://github.com/Pythoughts-labs/pythinker-code" diff --git a/tests/test_release_py.py b/tests/test_release_py.py index df9760ff..574e2a1a 100644 --- a/tests/test_release_py.py +++ b/tests/test_release_py.py @@ -44,6 +44,12 @@ def test_dep_check_passes_when_review_pin_matches(tmp_path: Path) -> None: review = _write( tmp_path, "review.toml", '[project]\nname="pythinker-review"\nversion="0.1.0"\n' ) + sdk = _write( + tmp_path, + "sdk.toml", + '[project]\nname="pythinker-sdk"\nversion="1.1.0"\n' + 'dependencies=["pythinker-core==1.1.1"]\n', + ) result = _run_dep_check( "--root-pyproject", str(root), @@ -53,6 +59,8 @@ def test_dep_check_passes_when_review_pin_matches(tmp_path: Path) -> None: str(host), "--pythinker-review-pyproject", str(review), + "--pythinker-sdk-pyproject", + str(sdk), ) assert result.returncode == 0, result.stderr @@ -70,6 +78,12 @@ def test_dep_check_fails_when_review_pin_drifts(tmp_path: Path) -> None: review = _write( tmp_path, "review.toml", '[project]\nname="pythinker-review"\nversion="0.2.0"\n' ) + sdk = _write( + tmp_path, + "sdk.toml", + '[project]\nname="pythinker-sdk"\nversion="1.1.0"\n' + 'dependencies=["pythinker-core==1.1.1"]\n', + ) result = _run_dep_check( "--root-pyproject", str(root), @@ -79,11 +93,48 @@ def test_dep_check_fails_when_review_pin_drifts(tmp_path: Path) -> None: str(host), "--pythinker-review-pyproject", str(review), + "--pythinker-sdk-pyproject", + str(sdk), ) assert result.returncode == 1 assert "pythinker-review version mismatch" in result.stderr +def test_dep_check_fails_when_sdk_core_pin_drifts(tmp_path: Path) -> None: + root = _write( + tmp_path, + "root.toml", + '[project]\nname="pythinker-code"\nversion="0.29.0"\n' + 'dependencies=["pythinker-core[contrib]==1.2.0","pythinker-host==1.0.0",' + '"pythinker-review==0.1.0"]\n', + ) + core = _write(tmp_path, "core.toml", '[project]\nname="pythinker-core"\nversion="1.2.0"\n') + host = _write(tmp_path, "host.toml", '[project]\nname="pythinker-host"\nversion="1.0.0"\n') + review = _write( + tmp_path, "review.toml", '[project]\nname="pythinker-review"\nversion="0.1.0"\n' + ) + sdk = _write( + tmp_path, + "sdk.toml", + '[project]\nname="pythinker-sdk"\nversion="1.1.0"\n' + 'dependencies=["pythinker-core==1.1.1"]\n', + ) + result = _run_dep_check( + "--root-pyproject", + str(root), + "--pythinker-core-pyproject", + str(core), + "--pythinker-host-pyproject", + str(host), + "--pythinker-review-pyproject", + str(review), + "--pythinker-sdk-pyproject", + str(sdk), + ) + assert result.returncode == 1 + assert "pythinker-sdk core dependency mismatch" in result.stderr + + def test_parse_semver_accepts_xyz() -> None: assert release_tool.parse_semver("0.28.0") == (0, 28, 0) diff --git a/tests/test_version_lockstep.py b/tests/test_version_lockstep.py index f0c3050c..60592398 100644 --- a/tests/test_version_lockstep.py +++ b/tests/test_version_lockstep.py @@ -13,13 +13,17 @@ def _version(rel: str) -> str: return tomllib.load(fh)["project"]["version"] -def _root_deps() -> list[str]: - with (REPO_ROOT / "pyproject.toml").open("rb") as fh: +def _deps(rel: str) -> list[str]: + with (REPO_ROOT / rel).open("rb") as fh: return tomllib.load(fh)["project"]["dependencies"] -def _pin(name: str) -> str: - for dep in _root_deps(): +def _root_deps() -> list[str]: + return _deps("pyproject.toml") + + +def _pin_in(deps: list[str], name: str) -> str: + for dep in deps: head = dep.split("==", 1) if len(head) == 2 and head[0].split("[")[0] == name: return head[1].split(";")[0].strip() @@ -39,10 +43,20 @@ def test_subpackage_pins_match_versions() -> None: assert _pin("pythinker-review") == _version("packages/pythinker-review/pyproject.toml") +def _pin(name: str) -> str: + return _pin_in(_root_deps(), name) + + def test_review_is_frozen_at_0_1_0() -> None: assert _pin("pythinker-review") == "0.1.0" +def test_sdk_core_pin_matches_core_version() -> None: + assert _pin_in(_deps("sdks/pythinker-sdk/pyproject.toml"), "pythinker-core") == _version( + "packages/pythinker-core/pyproject.toml" + ) + + def test_readme_heading_and_pip_snippet() -> None: readme = (REPO_ROOT / "README.md").read_text(encoding="utf-8") assert f"What's New in {VERSION}" in readme From a48d61792db0991ea2b7893d232beaf13976a473 Mon Sep 17 00:00:00 2001 From: mohamed-elkholy95 Date: Tue, 2 Jun 2026 00:37:15 -0400 Subject: [PATCH 2/2] fix(release): address sdk lockstep review findings --- CHANGELOG.md | 4 ++ ...1-release-orchestration-p1-release-tool.md | 37 ++++++++++++++----- .../check_pythinker_dependency_versions.py | 7 ++-- 3 files changed, 34 insertions(+), 14 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7d40b02e..778cefd1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,10 @@ GitHub Releases page; `0.8.0` is the new starting line. ## Unreleased +- **Release packaging keeps SDK/core pins in lockstep.** The SDK's `pythinker-core` + dependency is now updated by release automation and checked by CI/release validation, + preventing no-sources binary builds from resolving against a stale core pin. + ## 0.29.0 (2026-06-01) ### What changed in this release diff --git a/docs/superpowers/plans/2026-05-31-release-orchestration-p1-release-tool.md b/docs/superpowers/plans/2026-05-31-release-orchestration-p1-release-tool.md index b749eecc..a29daceb 100644 --- a/docs/superpowers/plans/2026-05-31-release-orchestration-p1-release-tool.md +++ b/docs/superpowers/plans/2026-05-31-release-orchestration-p1-release-tool.md @@ -30,9 +30,9 @@ There are no admin/secret/App actions in P1. - `tests/test_release_py.py` — unit tests for the pure functions of `scripts/release.py` (and the extended dep-check script). **Modified** -- `scripts/check_pythinker_dependency_versions.py` — add a `--pythinker-review-pyproject` arg + a third `("pythinker-review", ...)` tuple so the `pythinker-review==0.1.0` pin must match `packages/pythinker-review`. -- `.github/workflows/ci-pythinker-cli.yml:253-256` — pass `--pythinker-review-pyproject` to the dep-check call (or argparse fails CI red, since the new arg is `required=True`). -- `.github/workflows/release-pythinker-cli.yml:57-60` — same new arg for the release-time dep-check call. +- `scripts/check_pythinker_dependency_versions.py` — add required dep-check args for `pythinker-review` and SDK/core lockstep so `pythinker-review==0.1.0` and the SDK `pythinker-core` pin must match their package versions. +- `.github/workflows/ci-pythinker-cli.yml:253-256` — pass the required review and SDK pyproject args to the dep-check call (or argparse fails CI red). +- `.github/workflows/release-pythinker-cli.yml:57-60` — same required args for the release-time dep-check call. - `src/pythinker_code/ui/shell/update.py` — `MANAGED_CHANNEL_MARKER` constant (after `NATIVE_INSTALLER_MARKER:61`); `PYTHINKER_MANAGED` env read at the top of `_detect_upgrade_command()` (line 95); a managed-channel branch in `_update_prompt_text()` (line 615) so the rendered "Update method" is a real channel-native hint; a managed-channel early-return in `do_update()` (after the detection at line 1215, before the readiness gate at line 1216) so a managed install neither mis-fires the PyPI readiness check nor tries to exec the marker. Brew path left unchanged. - `tests/ui_and_conv/test_shell_update.py` — add the brew-unchanged + `PYTHINKER_MANAGED` regression tests (this is the file that actually imports `update`; `tests/test_release_update_pipeline.py` is workflow-text only and does NOT import `update`). - `tests/test_release_update_pipeline.py` — add a test asserting `changelog-entry-required.yml` skips on both the `chore(release)*` title (line 54) and the `release/*` head branch (line 57) — the skip-contract that `release.py.open_pr()` depends on. (Workflow-text file, the correct home for this assertion.) @@ -90,11 +90,18 @@ There are no admin/secret/App actions in P1. core = _write(tmp_path, "core.toml", '[project]\nname="pythinker-core"\nversion="1.1.1"\n') host = _write(tmp_path, "host.toml", '[project]\nname="pythinker-host"\nversion="1.0.0"\n') review = _write(tmp_path, "review.toml", '[project]\nname="pythinker-review"\nversion="0.1.0"\n') + sdk = _write( + tmp_path, + "sdk.toml", + '[project]\nname="pythinker-sdk"\nversion="1.1.0"\n' + 'dependencies=["pythinker-core==1.1.1"]\n', + ) result = _run_dep_check( "--root-pyproject", str(root), "--pythinker-core-pyproject", str(core), "--pythinker-host-pyproject", str(host), "--pythinker-review-pyproject", str(review), + "--pythinker-sdk-pyproject", str(sdk), ) assert result.returncode == 0, result.stderr @@ -110,11 +117,18 @@ There are no admin/secret/App actions in P1. core = _write(tmp_path, "core.toml", '[project]\nname="pythinker-core"\nversion="1.1.1"\n') host = _write(tmp_path, "host.toml", '[project]\nname="pythinker-host"\nversion="1.0.0"\n') review = _write(tmp_path, "review.toml", '[project]\nname="pythinker-review"\nversion="0.2.0"\n') + sdk = _write( + tmp_path, + "sdk.toml", + '[project]\nname="pythinker-sdk"\nversion="1.1.0"\n' + 'dependencies=["pythinker-core==1.1.1"]\n', + ) result = _run_dep_check( "--root-pyproject", str(root), "--pythinker-core-pyproject", str(core), "--pythinker-host-pyproject", str(host), "--pythinker-review-pyproject", str(review), + "--pythinker-sdk-pyproject", str(sdk), ) assert result.returncode == 1 assert "pythinker-review version mismatch" in result.stderr @@ -126,6 +140,7 @@ There are no admin/secret/App actions in P1. ```python parser.add_argument("--pythinker-review-pyproject", type=Path, required=True) + parser.add_argument("--pythinker-sdk-pyproject", type=Path, required=True) ``` 5. - [ ] Add the third tuple. Change the loop header (lines 65-68) from: @@ -148,18 +163,19 @@ There are no admin/secret/App actions in P1. ``` 6. - [ ] Run and see it pass. `uv run pytest tests/test_release_py.py -q` → `2 passed`. -7. - [ ] Update the CI caller. In `.github/workflows/ci-pythinker-cli.yml`, change the existing dependency-check block to use the project-managed launcher and include review: +7. - [ ] Update the CI caller. In `.github/workflows/ci-pythinker-cli.yml`, change the existing dependency-check block to use the project-managed launcher and include review and SDK paths: ```yaml uv run python scripts/check_pythinker_dependency_versions.py \ --root-pyproject pyproject.toml \ --pythinker-core-pyproject packages/pythinker-core/pyproject.toml \ --pythinker-host-pyproject packages/pythinker-host/pyproject.toml \ - --pythinker-review-pyproject packages/pythinker-review/pyproject.toml + --pythinker-review-pyproject packages/pythinker-review/pyproject.toml \ + --pythinker-sdk-pyproject sdks/pythinker-sdk/pyproject.toml ``` -8. - [ ] Update the release caller. In `.github/workflows/release-pythinker-cli.yml`, apply the identical change to the block at lines 57-60 (same trailing-`\` addition on the host line + the new review line). -9. - [ ] Sanity-check the real workspace passes. `uv run python scripts/check_pythinker_dependency_versions.py --root-pyproject pyproject.toml --pythinker-core-pyproject packages/pythinker-core/pyproject.toml --pythinker-host-pyproject packages/pythinker-host/pyproject.toml --pythinker-review-pyproject packages/pythinker-review/pyproject.toml` → `ok: pythinker-code dependencies match workspace package versions`. +8. - [ ] Update the release caller. In `.github/workflows/release-pythinker-cli.yml`, apply the identical change to the block at lines 57-60 (same trailing-`\` addition on the host and review lines + the new SDK line). +9. - [ ] Sanity-check the real workspace passes. `uv run python scripts/check_pythinker_dependency_versions.py --root-pyproject pyproject.toml --pythinker-core-pyproject packages/pythinker-core/pyproject.toml --pythinker-host-pyproject packages/pythinker-host/pyproject.toml --pythinker-review-pyproject packages/pythinker-review/pyproject.toml --pythinker-sdk-pyproject sdks/pythinker-sdk/pyproject.toml` → `ok: pythinker-code dependencies match workspace package versions`. 10. - [ ] Lint the workflows. `uvx actionlint .github/workflows/ci-pythinker-cli.yml .github/workflows/release-pythinker-cli.yml` (if `actionlint` is unavailable, fall back to `uv run python -c "import yaml,sys; [yaml.safe_load(open(f)) for f in sys.argv[1:]]" .github/workflows/ci-pythinker-cli.yml .github/workflows/release-pythinker-cli.yml`) → no output / exit 0. 11. - [ ] Commit. `git add scripts/check_pythinker_dependency_versions.py tests/test_release_py.py .github/workflows/ci-pythinker-cli.yml .github/workflows/release-pythinker-cli.yml && git commit -m "feat(release): enforce pythinker-review pin in dependency check"` @@ -639,11 +655,12 @@ The git/gh/uv orchestration is genuine I/O and is verified by `--dry-run` + a re GATES = [ ["python", "scripts/check_version_tag.py", "--pyproject", "pyproject.toml", "--expected-version", "{target}"], - ["python", "scripts/check_pythinker_dependency_versions.py", + ["uv", "run", "python", "scripts/check_pythinker_dependency_versions.py", "--root-pyproject", "pyproject.toml", "--pythinker-core-pyproject", "packages/pythinker-core/pyproject.toml", "--pythinker-host-pyproject", "packages/pythinker-host/pyproject.toml", - "--pythinker-review-pyproject", "packages/pythinker-review/pyproject.toml"], + "--pythinker-review-pyproject", "packages/pythinker-review/pyproject.toml", + "--pythinker-sdk-pyproject", "sdks/pythinker-sdk/pyproject.toml"], ["uv", "sync", "--frozen", "--all-extras", "--all-packages"], ["uv", "run", "pytest", "tests/test_version_lockstep.py", "-q"], ] @@ -1065,7 +1082,7 @@ Brew must NOT set `PYTHINKER_MANAGED`; it keeps its existing cellar path-sniff ( Because every task committed to the single `p1/release-tool` branch (Task 1 onward), the required dep-check arg and both workflow-caller edits are atomic in one PR — there is no cherry-pick or stacked-PR reconciliation to do. 1. - [ ] Confirm the full local gate set is green before pushing. `uv run pytest tests/test_release_py.py tests/test_version_lockstep.py tests/ui_and_conv/test_shell_update.py tests/test_release_update_pipeline.py -q` → all pass; `uv run ruff check scripts/release.py tests/test_release_py.py tests/test_version_lockstep.py && uv run ruff format --check scripts/release.py tests/test_release_py.py tests/test_version_lockstep.py` → exit 0; `uv run pyright src/pythinker_code/ui/shell/update.py` → 0 errors. -2. - [ ] Confirm the workspace version checks pass exactly as CI will run them: `uv run python scripts/check_pythinker_dependency_versions.py --root-pyproject pyproject.toml --pythinker-core-pyproject packages/pythinker-core/pyproject.toml --pythinker-host-pyproject packages/pythinker-host/pyproject.toml --pythinker-review-pyproject packages/pythinker-review/pyproject.toml` → `ok: pythinker-code dependencies match workspace package versions`. +2. - [ ] Confirm the workspace version checks pass exactly as CI will run them: `uv run python scripts/check_pythinker_dependency_versions.py --root-pyproject pyproject.toml --pythinker-core-pyproject packages/pythinker-core/pyproject.toml --pythinker-host-pyproject packages/pythinker-host/pyproject.toml --pythinker-review-pyproject packages/pythinker-review/pyproject.toml --pythinker-sdk-pyproject sdks/pythinker-sdk/pyproject.toml` → `ok: pythinker-code dependencies match workspace package versions`. 3. - [ ] Confirm the branch history is one coherent stack. `git log --oneline -8 p1/release-tool` shows the dep-check, release.py (validation/rewrites/promotion/asset/orchestration), lockstep, skip-contract, updater, and SKILL commits all on `p1/release-tool`. Push: `git push -u origin p1/release-tool`. 4. - [ ] Open the PR. `gh pr create --base main --head p1/release-tool --title "feat(release): release.py + version lockstep SSOT (P1)" --body "Adds scripts/release.py (4-phase SSOT release orchestrator), tests/test_version_lockstep.py (every-PR version guard), the pythinker-review dependency-check tuple (with both CI callers updated atomically), the changelog-workflow skip-contract assertion, and the PYTHINKER_MANAGED updater hook with a brew-unchanged regression test and a managed-channel rendered hint. No new agent runtime deps (C3)."` 5. - [ ] Wait for CI and CodeRabbit. Confirm required checks (`check`, `test`, `changelog`, `release-validate` as applicable) pass and the `CodeRabbit` commit status on the PR head SHA is `success` (C2) before merging. Read CodeRabbit's "Actionable comments" and resolve or surface them — do not merge past unresolved findings. Per the project CLAUDE.md / MEMORY note, reject a CodeRabbit camelCase-for-Python finding if one appears (false positive; codebase is snake_case). diff --git a/scripts/check_pythinker_dependency_versions.py b/scripts/check_pythinker_dependency_versions.py index 2f2af57f..cbdaa6c3 100644 --- a/scripts/check_pythinker_dependency_versions.py +++ b/scripts/check_pythinker_dependency_versions.py @@ -95,14 +95,13 @@ def main() -> int: else: sdk_deps = sdk_project.get("dependencies", []) if not isinstance(sdk_deps, list): - errors.append( - f"project.dependencies must be a list in {args.pythinker_sdk_pyproject}" - ) + errors.append(f"project.dependencies must be a list in {args.pythinker_sdk_pyproject}") elif core_version := package_versions.get("pythinker-core"): sdk_core_pin = find_pinned_dependency(sdk_deps, "pythinker-core") if sdk_core_pin is None: errors.append( - f"Missing pinned dependency for pythinker-core in {args.pythinker_sdk_pyproject}." + "Missing pinned dependency for pythinker-core in " + f"{args.pythinker_sdk_pyproject}." ) elif sdk_core_pin != core_version: errors.append(