Skip to content

Commit a48d617

Browse files
committed
fix(release): address sdk lockstep review findings
1 parent 26cefa5 commit a48d617

3 files changed

Lines changed: 34 additions & 14 deletions

File tree

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,10 @@ GitHub Releases page; `0.8.0` is the new starting line.
1515

1616
## Unreleased
1717

18+
- **Release packaging keeps SDK/core pins in lockstep.** The SDK's `pythinker-core`
19+
dependency is now updated by release automation and checked by CI/release validation,
20+
preventing no-sources binary builds from resolving against a stale core pin.
21+
1822
## 0.29.0 (2026-06-01)
1923

2024
### What changed in this release

docs/superpowers/plans/2026-05-31-release-orchestration-p1-release-tool.md

Lines changed: 27 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,9 @@ There are no admin/secret/App actions in P1.
3030
- `tests/test_release_py.py` — unit tests for the pure functions of `scripts/release.py` (and the extended dep-check script).
3131

3232
**Modified**
33-
- `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`.
34-
- `.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`).
35-
- `.github/workflows/release-pythinker-cli.yml:57-60` — same new arg for the release-time dep-check call.
33+
- `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.
34+
- `.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).
35+
- `.github/workflows/release-pythinker-cli.yml:57-60` — same required args for the release-time dep-check call.
3636
- `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.
3737
- `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`).
3838
- `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.
9090
core = _write(tmp_path, "core.toml", '[project]\nname="pythinker-core"\nversion="1.1.1"\n')
9191
host = _write(tmp_path, "host.toml", '[project]\nname="pythinker-host"\nversion="1.0.0"\n')
9292
review = _write(tmp_path, "review.toml", '[project]\nname="pythinker-review"\nversion="0.1.0"\n')
93+
sdk = _write(
94+
tmp_path,
95+
"sdk.toml",
96+
'[project]\nname="pythinker-sdk"\nversion="1.1.0"\n'
97+
'dependencies=["pythinker-core==1.1.1"]\n',
98+
)
9399
result = _run_dep_check(
94100
"--root-pyproject", str(root),
95101
"--pythinker-core-pyproject", str(core),
96102
"--pythinker-host-pyproject", str(host),
97103
"--pythinker-review-pyproject", str(review),
104+
"--pythinker-sdk-pyproject", str(sdk),
98105
)
99106
assert result.returncode == 0, result.stderr
100107

@@ -110,11 +117,18 @@ There are no admin/secret/App actions in P1.
110117
core = _write(tmp_path, "core.toml", '[project]\nname="pythinker-core"\nversion="1.1.1"\n')
111118
host = _write(tmp_path, "host.toml", '[project]\nname="pythinker-host"\nversion="1.0.0"\n')
112119
review = _write(tmp_path, "review.toml", '[project]\nname="pythinker-review"\nversion="0.2.0"\n')
120+
sdk = _write(
121+
tmp_path,
122+
"sdk.toml",
123+
'[project]\nname="pythinker-sdk"\nversion="1.1.0"\n'
124+
'dependencies=["pythinker-core==1.1.1"]\n',
125+
)
113126
result = _run_dep_check(
114127
"--root-pyproject", str(root),
115128
"--pythinker-core-pyproject", str(core),
116129
"--pythinker-host-pyproject", str(host),
117130
"--pythinker-review-pyproject", str(review),
131+
"--pythinker-sdk-pyproject", str(sdk),
118132
)
119133
assert result.returncode == 1
120134
assert "pythinker-review version mismatch" in result.stderr
@@ -126,6 +140,7 @@ There are no admin/secret/App actions in P1.
126140

127141
```python
128142
parser.add_argument("--pythinker-review-pyproject", type=Path, required=True)
143+
parser.add_argument("--pythinker-sdk-pyproject", type=Path, required=True)
129144
```
130145

131146
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.
148163
```
149164

150165
6. - [ ] Run and see it pass. `uv run pytest tests/test_release_py.py -q``2 passed`.
151-
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:
166+
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:
152167

153168
```yaml
154169
uv run python scripts/check_pythinker_dependency_versions.py \
155170
--root-pyproject pyproject.toml \
156171
--pythinker-core-pyproject packages/pythinker-core/pyproject.toml \
157172
--pythinker-host-pyproject packages/pythinker-host/pyproject.toml \
158-
--pythinker-review-pyproject packages/pythinker-review/pyproject.toml
173+
--pythinker-review-pyproject packages/pythinker-review/pyproject.toml \
174+
--pythinker-sdk-pyproject sdks/pythinker-sdk/pyproject.toml
159175
```
160176

161-
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).
162-
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`.
177+
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).
178+
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`.
163179
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.
164180
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"`
165181

@@ -639,11 +655,12 @@ The git/gh/uv orchestration is genuine I/O and is verified by `--dry-run` + a re
639655
GATES = [
640656
["python", "scripts/check_version_tag.py", "--pyproject", "pyproject.toml",
641657
"--expected-version", "{target}"],
642-
["python", "scripts/check_pythinker_dependency_versions.py",
658+
["uv", "run", "python", "scripts/check_pythinker_dependency_versions.py",
643659
"--root-pyproject", "pyproject.toml",
644660
"--pythinker-core-pyproject", "packages/pythinker-core/pyproject.toml",
645661
"--pythinker-host-pyproject", "packages/pythinker-host/pyproject.toml",
646-
"--pythinker-review-pyproject", "packages/pythinker-review/pyproject.toml"],
662+
"--pythinker-review-pyproject", "packages/pythinker-review/pyproject.toml",
663+
"--pythinker-sdk-pyproject", "sdks/pythinker-sdk/pyproject.toml"],
647664
["uv", "sync", "--frozen", "--all-extras", "--all-packages"],
648665
["uv", "run", "pytest", "tests/test_version_lockstep.py", "-q"],
649666
]
@@ -1065,7 +1082,7 @@ Brew must NOT set `PYTHINKER_MANAGED`; it keeps its existing cellar path-sniff (
10651082
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.
10661083

10671084
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.
1068-
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`.
1085+
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`.
10691086
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`.
10701087
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)."`
10711088
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).

scripts/check_pythinker_dependency_versions.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -95,14 +95,13 @@ def main() -> int:
9595
else:
9696
sdk_deps = sdk_project.get("dependencies", [])
9797
if not isinstance(sdk_deps, list):
98-
errors.append(
99-
f"project.dependencies must be a list in {args.pythinker_sdk_pyproject}"
100-
)
98+
errors.append(f"project.dependencies must be a list in {args.pythinker_sdk_pyproject}")
10199
elif core_version := package_versions.get("pythinker-core"):
102100
sdk_core_pin = find_pinned_dependency(sdk_deps, "pythinker-core")
103101
if sdk_core_pin is None:
104102
errors.append(
105-
f"Missing pinned dependency for pythinker-core in {args.pythinker_sdk_pyproject}."
103+
"Missing pinned dependency for pythinker-core in "
104+
f"{args.pythinker_sdk_pyproject}."
106105
)
107106
elif sdk_core_pin != core_version:
108107
errors.append(

0 commit comments

Comments
 (0)