From 474a70372a0ce4233031136bc2bbc4ab73d1c61e Mon Sep 17 00:00:00 2001 From: leecyang <164771957+leecyang@users.noreply.github.com> Date: Tue, 18 Aug 2026 21:35:35 +0800 Subject: [PATCH 01/15] release: bump package version to 2.3.0 --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index b23e146..ca8aba7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "lingxigraph" -version = "2.2.0" +version = "2.3.0" description = "Enterprise, provider-neutral durable graph runtime for multi-agent systems." readme = "README.md" requires-python = ">=3.11" From 5941f5a68a61428951c6c975806979849cb2f31a Mon Sep 17 00:00:00 2001 From: leecyang <164771957+leecyang@users.noreply.github.com> Date: Tue, 18 Aug 2026 21:36:04 +0800 Subject: [PATCH 02/15] release: bump runtime version to 2.3.0 --- src/lingxigraph/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lingxigraph/version.py b/src/lingxigraph/version.py index f095cfd..d63c145 100644 --- a/src/lingxigraph/version.py +++ b/src/lingxigraph/version.py @@ -1,5 +1,5 @@ """Single source of truth for the package and API version.""" -__version__ = "2.2.0" +__version__ = "2.3.0" __all__ = ["__version__"] From 51d7af785d4824fda925cf124de8d7e45625b200 Mon Sep 17 00:00:00 2001 From: leecyang <164771957+leecyang@users.noreply.github.com> Date: Tue, 18 Aug 2026 21:36:15 +0800 Subject: [PATCH 03/15] release: align Helm chart with 2.3.0 --- deploy/helm/lingxigraph/Chart.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/deploy/helm/lingxigraph/Chart.yaml b/deploy/helm/lingxigraph/Chart.yaml index b49833c..bd36aa8 100644 --- a/deploy/helm/lingxigraph/Chart.yaml +++ b/deploy/helm/lingxigraph/Chart.yaml @@ -2,6 +2,6 @@ apiVersion: v2 name: lingxigraph description: Enterprise LingxiGraph Agent Server and distributed workers type: application -version: 2.1.0 -appVersion: "2.1.0" +version: 2.3.0 +appVersion: "2.3.0" kubeVersion: ">=1.27.0-0" From 0a520eea1ef26121a9a62351b77e122898a9a021 Mon Sep 17 00:00:00 2001 From: leecyang <164771957+leecyang@users.noreply.github.com> Date: Tue, 18 Aug 2026 21:38:05 +0800 Subject: [PATCH 04/15] release: finalize changelog for 2.3.0 --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d3673fd..2b8e998 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ ## Unreleased +_No unreleased changes._ + +## 2.3.0 - 2026-08-18 + ### Durable mid-run steering (issue #16) - 新增 `POST /v1/runs/{run_id}/steer`:在 Run 执行期间(含 `paused`)durably 注入新的 From e611b85789d811a7cc68773a1088c5a97082f1fc Mon Sep 17 00:00:00 2001 From: leecyang <164771957+leecyang@users.noreply.github.com> Date: Tue, 18 Aug 2026 21:38:29 +0800 Subject: [PATCH 05/15] release: add v2.3.0 release notes --- .github/release-notes/v2.3.0.md | 37 +++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 .github/release-notes/v2.3.0.md diff --git a/.github/release-notes/v2.3.0.md b/.github/release-notes/v2.3.0.md new file mode 100644 index 0000000..fe625d0 --- /dev/null +++ b/.github/release-notes/v2.3.0.md @@ -0,0 +1,37 @@ +# LingxiGraph v2.3.0 — Durable Mid-Run Steering + +LingxiGraph 2.3.0 introduces durable, application-controlled mid-run steering for long-running graph executions. External input can now be accepted while a Run is active or paused, persisted in PostgreSQL, and consumed only at application-chosen safe points without hard preemption or hidden state mutation. + +## Highlights + +- **Durable mid-run steering** — `POST /v1/runs/{run_id}/steer` and matching sync/async SDK APIs accept structured steering input with idempotency and payload-size enforcement. +- **Application-controlled safe points** — graphs decide when to inspect, peek, or drain steering through `runtime.has_steering`, `runtime.peek_steering()`, and `runtime.drain_steering()`. +- **Crash-safe delivery semantics** — PostgreSQL remains the source of truth; Redis is optional notification acceleration only. Stable steering identity and fenced consumption commits support retry and at-least-once redelivery without pretending to provide global exactly-once execution. +- **Correct pause/resume lineage** — pending steering transfers atomically to the resumed descendant Run. Typed `superseded_by_run_id` lineage prevents stale Run IDs or user metadata from forging Runtime control state. +- **Stronger cancellation and worker fencing** — expired `cancelling` leases recover to `cancelled`, paused cancellation terminates immediately, stale attempts cannot overwrite newer ownership, and retry state plus `worker_retrying` observability commit atomically. +- **First-class observability** — durable `run.steer.accepted`, `run.steer.consumed`, and `run.steer.superseded` lifecycle events expose identity, ordering, queue latency, safe-point location, and disposition. +- **Complete bilingual documentation** — API, Python SDK, concepts, operations, errors/events, migration guidance, and release documentation are aligned with the final Runtime behavior. + +## Upgrade + +```bash +pip install --upgrade "lingxigraph==2.3.0" +``` + +For PostgreSQL-backed deployments, run the forward-only schema migration before enabling the new Runtime behavior: + +```bash +alembic upgrade head +``` + +The schema must reach `0005_superseded_by_run_id`. Revisions `0002` through `0005` are additive and can be applied before the new binary, but old binaries do not understand the new steering/lineage semantics. + +## Compatibility + +This release does **not** introduce a breaking public API change for existing graphs that do not use steering. Steering remains opt-in, and Redis is never the sole source of durable steering state. + +Python **3.11+** is supported. + +## Release scope + +The primary implementation was developed and reviewed in #16 / #17, followed by documentation and release-pipeline hardening. See `CHANGELOG.md` for the complete technical change log. From ca0f2ebf20cfc63806a6f785cece7df362e6cbbe Mon Sep 17 00:00:00 2001 From: leecyang <164771957+leecyang@users.noreply.github.com> Date: Tue, 18 Aug 2026 21:39:37 +0800 Subject: [PATCH 06/15] ci: automate tagged PyPI and GitHub releases --- .github/workflows/publish.yml | 223 ++++++++++++++++++++++++++++++---- 1 file changed, 199 insertions(+), 24 deletions(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 8166dad..7cc16d5 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -1,12 +1,17 @@ name: Publish Python package on: + push: + branches: + - main + paths: + - 'src/lingxigraph/version.py' workflow_dispatch: inputs: tag: description: Annotated release tag to publish required: true - default: v2.2.0 + default: v2.3.0 type: string repository: description: Package index @@ -21,36 +26,113 @@ permissions: contents: read jobs: - publish: + prepare: + name: Prepare release identity runs-on: ubuntu-latest permissions: - contents: read - id-token: write + contents: write + outputs: + tag: ${{ steps.release.outputs.tag }} + version: ${{ steps.release.outputs.version }} + repository: ${{ steps.release.outputs.repository }} steps: - - name: Validate tag input + - name: Check out repository + uses: actions/checkout@v6 + with: + fetch-depth: 0 + + - name: Resolve and validate release + id: release + shell: bash env: - RELEASE_TAG: ${{ inputs.tag }} + EVENT_NAME: ${{ github.event_name }} + INPUT_TAG: ${{ inputs.tag }} + INPUT_REPOSITORY: ${{ inputs.repository }} run: | + set -euo pipefail + + if [ "$EVENT_NAME" = "workflow_dispatch" ]; then + RELEASE_TAG="$INPUT_TAG" + TARGET_REPOSITORY="$INPUT_REPOSITORY" + else + VERSION=$(python - <<'PY' + import pathlib + import re + text = pathlib.Path("src/lingxigraph/version.py").read_text() + match = re.search(r'__version__\s*=\s*"([0-9]+\.[0-9]+\.[0-9]+)"', text) + if match is None: + raise SystemExit("Unable to read __version__ from src/lingxigraph/version.py") + print(match.group(1)) + PY + ) + RELEASE_TAG="v$VERSION" + TARGET_REPOSITORY="pypi" + fi + if [[ ! "$RELEASE_TAG" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then - echo "Expected a tag such as v2.1.0, got: $RELEASE_TAG" >&2 + echo "Expected a tag such as v2.3.0, got: $RELEASE_TAG" >&2 exit 1 fi - - uses: actions/checkout@v4 + VERSION="${RELEASE_TAG#v}" + PYPROJECT_VERSION=$(python - <<'PY' + import tomllib + with open("pyproject.toml", "rb") as stream: + print(tomllib.load(stream)["project"]["version"]) + PY + ) + RUNTIME_VERSION=$(python - <<'PY' + import pathlib + import re + text = pathlib.Path("src/lingxigraph/version.py").read_text() + match = re.search(r'__version__\s*=\s*"([0-9]+\.[0-9]+\.[0-9]+)"', text) + if match is None: + raise SystemExit("Unable to read runtime version") + print(match.group(1)) + PY + ) + + test "$PYPROJECT_VERSION" = "$VERSION" + test "$RUNTIME_VERSION" = "$VERSION" + grep -q "^## $VERSION - " CHANGELOG.md + + git fetch --tags --force + if [ "$EVENT_NAME" = "workflow_dispatch" ]; then + test "$(git cat-file -t "$RELEASE_TAG")" = tag + git checkout --detach "$RELEASE_TAG" + test "$(git cat-file -t "$RELEASE_TAG")" = tag + else + if git rev-parse --verify "refs/tags/$RELEASE_TAG" >/dev/null 2>&1; then + echo "Release tag $RELEASE_TAG already exists; refusing an automatic duplicate publish." >&2 + exit 1 + fi + git config user.name "github-actions[bot]" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + git tag -a "$RELEASE_TAG" -m "LingxiGraph $VERSION" + git push origin "refs/tags/$RELEASE_TAG" + fi + + echo "tag=$RELEASE_TAG" >> "$GITHUB_OUTPUT" + echo "version=$VERSION" >> "$GITHUB_OUTPUT" + echo "repository=$TARGET_REPOSITORY" >> "$GITHUB_OUTPUT" + + build: + name: Build release distributions + needs: prepare + runs-on: ubuntu-latest + permissions: + contents: read + steps: + - name: Check out release tag + uses: actions/checkout@v6 with: - ref: ${{ inputs.tag }} + ref: ${{ needs.prepare.outputs.tag }} fetch-depth: 0 - - name: Verify annotated tag - env: - RELEASE_TAG: ${{ inputs.tag }} - run: | - test "$(git describe --tags --exact-match)" = "$RELEASE_TAG" - test "$(git cat-file -t "$RELEASE_TAG")" = tag - - - uses: actions/setup-python@v5 + - name: Set up Python + uses: actions/setup-python@v6 with: - python-version: "3.13" + python-version: '3.13' - name: Build distributions run: | @@ -59,7 +141,7 @@ jobs: - name: Verify distribution metadata env: - RELEASE_TAG: ${{ inputs.tag }} + RELEASE_TAG: ${{ needs.prepare.outputs.tag }} run: | python - <<'PY' import email @@ -83,12 +165,105 @@ jobs: assert expected in wheels[0].name and expected in sdists[0].name PY - - name: Publish to TestPyPI - if: inputs.repository == 'testpypi' + - name: Upload release distributions + uses: actions/upload-artifact@v6 + with: + name: python-dists-${{ needs.prepare.outputs.version }} + path: dist/ + if-no-files-found: error + retention-days: 14 + + publish-pypi: + name: Publish to PyPI + if: needs.prepare.outputs.repository == 'pypi' + needs: + - prepare + - build + runs-on: ubuntu-latest + permissions: + id-token: write + steps: + - name: Download release distributions + uses: actions/download-artifact@v7 + with: + name: python-dists-${{ needs.prepare.outputs.version }} + path: dist/ + + - name: Publish package distributions to PyPI uses: pypa/gh-action-pypi-publish@release/v1 with: - repository-url: https://test.pypi.org/legacy/ + skip-existing: ${{ github.event_name == 'workflow_dispatch' }} + + publish-testpypi: + name: Publish to TestPyPI + if: needs.prepare.outputs.repository == 'testpypi' + needs: + - prepare + - build + runs-on: ubuntu-latest + permissions: + id-token: write + steps: + - name: Download release distributions + uses: actions/download-artifact@v7 + with: + name: python-dists-${{ needs.prepare.outputs.version }} + path: dist/ - - name: Publish to PyPI - if: inputs.repository == 'pypi' + - name: Publish package distributions to TestPyPI uses: pypa/gh-action-pypi-publish@release/v1 + with: + repository-url: https://test.pypi.org/legacy/ + skip-existing: true + + github-release: + name: Publish GitHub Release + if: needs.prepare.outputs.repository == 'pypi' + needs: + - prepare + - build + - publish-pypi + runs-on: ubuntu-latest + permissions: + contents: write + steps: + - name: Check out release tag + uses: actions/checkout@v6 + with: + ref: ${{ needs.prepare.outputs.tag }} + fetch-depth: 0 + + - name: Download release distributions + uses: actions/download-artifact@v7 + with: + name: python-dists-${{ needs.prepare.outputs.version }} + path: dist/ + + - name: Create GitHub Release + env: + GH_TOKEN: ${{ github.token }} + RELEASE_TAG: ${{ needs.prepare.outputs.tag }} + VERSION: ${{ needs.prepare.outputs.version }} + shell: bash + run: | + set -euo pipefail + NOTES_FILE=".github/release-notes/$RELEASE_TAG.md" + + if gh release view "$RELEASE_TAG" >/dev/null 2>&1; then + gh release upload "$RELEASE_TAG" dist/* --clobber + exit 0 + fi + + if [ -f "$NOTES_FILE" ]; then + gh release create "$RELEASE_TAG" dist/* \ + --verify-tag \ + --latest \ + --title "LingxiGraph $RELEASE_TAG" \ + --notes-file "$NOTES_FILE" + else + gh release create "$RELEASE_TAG" dist/* \ + --verify-tag \ + --latest \ + --title "LingxiGraph $RELEASE_TAG" \ + --generate-notes + fi From 79281d6bc2aa6025bb5557b96df05e426bff5287 Mon Sep 17 00:00:00 2001 From: leecyang <164771957+leecyang@users.noreply.github.com> Date: Tue, 18 Aug 2026 21:42:55 +0800 Subject: [PATCH 07/15] ci: refresh uv lock for release --- .github/workflows/release-lock-refresh.yml | 42 ++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 .github/workflows/release-lock-refresh.yml diff --git a/.github/workflows/release-lock-refresh.yml b/.github/workflows/release-lock-refresh.yml new file mode 100644 index 0000000..561c7f5 --- /dev/null +++ b/.github/workflows/release-lock-refresh.yml @@ -0,0 +1,42 @@ +name: Refresh release lockfile + +on: + push: + branches: + - release/v2.3.0 + paths: + - 'pyproject.toml' + - '.github/workflows/release-lock-refresh.yml' + +permissions: + contents: write + +jobs: + refresh: + runs-on: ubuntu-latest + steps: + - name: Check out release branch + uses: actions/checkout@v6 + with: + ref: release/v2.3.0 + fetch-depth: 0 + + - name: Install uv + uses: astral-sh/setup-uv@v6 + + - name: Regenerate lockfile + run: uv lock + + - name: Commit lockfile if changed + shell: bash + run: | + set -euo pipefail + if git diff --quiet -- uv.lock; then + echo 'uv.lock already current' + exit 0 + fi + git config user.name 'github-actions[bot]' + git config user.email '41898282+github-actions[bot]@users.noreply.github.com' + git add uv.lock + git commit -m 'release: refresh uv.lock for 2.3.0' + git push origin HEAD:release/v2.3.0 From 5f52c92a78300f8f8b933ced44dc94357deb7a84 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 18 Aug 2026 13:43:06 +0000 Subject: [PATCH 08/15] release: refresh uv.lock for 2.3.0 --- uv.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/uv.lock b/uv.lock index 04efe6b..52a16a1 100644 --- a/uv.lock +++ b/uv.lock @@ -1021,7 +1021,7 @@ wheels = [ [[package]] name = "lingxigraph" -version = "2.2.0" +version = "2.3.0" source = { editable = "." } [package.optional-dependencies] From 2539e5ee9cb7f80ba431778efe322a5e39950e48 Mon Sep 17 00:00:00 2001 From: leecyang <164771957+leecyang@users.noreply.github.com> Date: Tue, 18 Aug 2026 21:43:32 +0800 Subject: [PATCH 09/15] ci: remove one-shot release lock refresh --- .github/workflows/release-lock-refresh.yml | 42 ---------------------- 1 file changed, 42 deletions(-) delete mode 100644 .github/workflows/release-lock-refresh.yml diff --git a/.github/workflows/release-lock-refresh.yml b/.github/workflows/release-lock-refresh.yml deleted file mode 100644 index 561c7f5..0000000 --- a/.github/workflows/release-lock-refresh.yml +++ /dev/null @@ -1,42 +0,0 @@ -name: Refresh release lockfile - -on: - push: - branches: - - release/v2.3.0 - paths: - - 'pyproject.toml' - - '.github/workflows/release-lock-refresh.yml' - -permissions: - contents: write - -jobs: - refresh: - runs-on: ubuntu-latest - steps: - - name: Check out release branch - uses: actions/checkout@v6 - with: - ref: release/v2.3.0 - fetch-depth: 0 - - - name: Install uv - uses: astral-sh/setup-uv@v6 - - - name: Regenerate lockfile - run: uv lock - - - name: Commit lockfile if changed - shell: bash - run: | - set -euo pipefail - if git diff --quiet -- uv.lock; then - echo 'uv.lock already current' - exit 0 - fi - git config user.name 'github-actions[bot]' - git config user.email '41898282+github-actions[bot]@users.noreply.github.com' - git add uv.lock - git commit -m 'release: refresh uv.lock for 2.3.0' - git push origin HEAD:release/v2.3.0 From baa65b0d4b094364e386a9745af342f643643088 Mon Sep 17 00:00:00 2001 From: leecyang <164771957+leecyang@users.noreply.github.com> Date: Tue, 18 Aug 2026 21:44:31 +0800 Subject: [PATCH 10/15] ci: refresh adapter lock for release --- .github/workflows/release-lock-refresh.yml | 42 ++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 .github/workflows/release-lock-refresh.yml diff --git a/.github/workflows/release-lock-refresh.yml b/.github/workflows/release-lock-refresh.yml new file mode 100644 index 0000000..4746a47 --- /dev/null +++ b/.github/workflows/release-lock-refresh.yml @@ -0,0 +1,42 @@ +name: Refresh release adapter lockfile + +on: + push: + branches: + - release/v2.3.0 + paths: + - '.github/workflows/release-lock-refresh.yml' + +permissions: + contents: write + +jobs: + refresh: + runs-on: ubuntu-latest + steps: + - name: Check out release branch + uses: actions/checkout@v6 + with: + ref: release/v2.3.0 + fetch-depth: 0 + + - name: Install uv + uses: astral-sh/setup-uv@v6 + + - name: Regenerate Chainlit adapter lockfile + working-directory: adapters/chainlit + run: uv lock + + - name: Commit lockfile if changed + shell: bash + run: | + set -euo pipefail + if git diff --quiet -- adapters/chainlit/uv.lock; then + echo 'adapter uv.lock already current' + exit 0 + fi + git config user.name 'github-actions[bot]' + git config user.email '41898282+github-actions[bot]@users.noreply.github.com' + git add adapters/chainlit/uv.lock + git commit -m 'release: refresh Chainlit adapter lock for 2.3.0' + git push origin HEAD:release/v2.3.0 From bc84cd763c31c158a34ab89135fc2380c365e548 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 18 Aug 2026 13:44:44 +0000 Subject: [PATCH 11/15] release: refresh Chainlit adapter lock for 2.3.0 --- adapters/chainlit/uv.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/adapters/chainlit/uv.lock b/adapters/chainlit/uv.lock index 900ffcb..b58aea7 100644 --- a/adapters/chainlit/uv.lock +++ b/adapters/chainlit/uv.lock @@ -858,7 +858,7 @@ wheels = [ [[package]] name = "lingxigraph" -version = "2.2.0" +version = "2.3.0" source = { editable = "../../" } [package.metadata] From 3a7a5836869b626d6fc08e0e14381a5057022948 Mon Sep 17 00:00:00 2001 From: leecyang <164771957+leecyang@users.noreply.github.com> Date: Tue, 18 Aug 2026 21:45:17 +0800 Subject: [PATCH 12/15] ci: remove one-shot adapter lock refresh --- .github/workflows/release-lock-refresh.yml | 42 ---------------------- 1 file changed, 42 deletions(-) delete mode 100644 .github/workflows/release-lock-refresh.yml diff --git a/.github/workflows/release-lock-refresh.yml b/.github/workflows/release-lock-refresh.yml deleted file mode 100644 index 4746a47..0000000 --- a/.github/workflows/release-lock-refresh.yml +++ /dev/null @@ -1,42 +0,0 @@ -name: Refresh release adapter lockfile - -on: - push: - branches: - - release/v2.3.0 - paths: - - '.github/workflows/release-lock-refresh.yml' - -permissions: - contents: write - -jobs: - refresh: - runs-on: ubuntu-latest - steps: - - name: Check out release branch - uses: actions/checkout@v6 - with: - ref: release/v2.3.0 - fetch-depth: 0 - - - name: Install uv - uses: astral-sh/setup-uv@v6 - - - name: Regenerate Chainlit adapter lockfile - working-directory: adapters/chainlit - run: uv lock - - - name: Commit lockfile if changed - shell: bash - run: | - set -euo pipefail - if git diff --quiet -- adapters/chainlit/uv.lock; then - echo 'adapter uv.lock already current' - exit 0 - fi - git config user.name 'github-actions[bot]' - git config user.email '41898282+github-actions[bot]@users.noreply.github.com' - git add adapters/chainlit/uv.lock - git commit -m 'release: refresh Chainlit adapter lock for 2.3.0' - git push origin HEAD:release/v2.3.0 From e1af4fda62ab91e767e2ff1f13146667ba2e5d26 Mon Sep 17 00:00:00 2001 From: leecyang <164771957+leecyang@users.noreply.github.com> Date: Tue, 18 Aug 2026 21:48:28 +0800 Subject: [PATCH 13/15] ci: align version contract test with package version --- .github/workflows/release-test-refresh.yml | 46 ++++++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 .github/workflows/release-test-refresh.yml diff --git a/.github/workflows/release-test-refresh.yml b/.github/workflows/release-test-refresh.yml new file mode 100644 index 0000000..ad5bf66 --- /dev/null +++ b/.github/workflows/release-test-refresh.yml @@ -0,0 +1,46 @@ +name: Align release version contract test + +on: + push: + branches: + - release/v2.3.0 + paths: + - '.github/workflows/release-test-refresh.yml' + +permissions: + contents: write + +jobs: + update: + runs-on: ubuntu-latest + steps: + - name: Check out release branch + uses: actions/checkout@v6 + with: + ref: release/v2.3.0 + fetch-depth: 0 + + - name: Remove hard-coded release version from test + run: | + python - <<'PY' + from pathlib import Path + + path = Path('tests/test_agent_server.py') + text = path.read_text() + old_import = 'from lingxigraph import END, START, StateGraph, interrupt\n' + new_import = 'from lingxigraph import END, START, StateGraph, __version__, interrupt\n' + old_assert = 'self.assertEqual(initialized.json()["result"]["serverInfo"]["version"], "2.2.0")' + new_assert = 'self.assertEqual(initialized.json()["result"]["serverInfo"]["version"], __version__)' + if old_import not in text or old_assert not in text: + raise SystemExit('expected version contract test pattern not found') + path.write_text(text.replace(old_import, new_import, 1).replace(old_assert, new_assert, 1)) + PY + + - name: Commit test update + run: | + set -euo pipefail + git config user.name 'github-actions[bot]' + git config user.email '41898282+github-actions[bot]@users.noreply.github.com' + git add tests/test_agent_server.py + git commit -m 'test: derive MCP server version from package metadata' + git push origin HEAD:release/v2.3.0 From 9d454109b782b7126469319adc13135ad98ae2a9 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 18 Aug 2026 13:48:37 +0000 Subject: [PATCH 14/15] test: derive MCP server version from package metadata --- tests/test_agent_server.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_agent_server.py b/tests/test_agent_server.py index d8608e6..cebf9b9 100644 --- a/tests/test_agent_server.py +++ b/tests/test_agent_server.py @@ -5,7 +5,7 @@ from fastapi.testclient import TestClient -from lingxigraph import END, START, StateGraph, interrupt +from lingxigraph import END, START, StateGraph, __version__, interrupt from lingxigraph.server import GraphRegistry, create_app from lingxigraph.server.security import Authenticator @@ -550,7 +550,7 @@ def test_interrupt_resume_and_protocol_gateways(self) -> None: json={"jsonrpc": "2.0", "id": 1, "method": "initialize"}, ) self.assertEqual(initialized.json()["result"]["serverInfo"]["name"], "LingxiGraph") - self.assertEqual(initialized.json()["result"]["serverInfo"]["version"], "2.2.0") + self.assertEqual(initialized.json()["result"]["serverInfo"]["version"], __version__) tools = client.post( "/mcp", headers=headers, From 6250d96a8d59a0f77dc1a3a3dd28ec628750e70f Mon Sep 17 00:00:00 2001 From: leecyang <164771957+leecyang@users.noreply.github.com> Date: Tue, 18 Aug 2026 21:49:20 +0800 Subject: [PATCH 15/15] ci: remove one-shot version test refresh --- .github/workflows/release-test-refresh.yml | 46 ---------------------- 1 file changed, 46 deletions(-) delete mode 100644 .github/workflows/release-test-refresh.yml diff --git a/.github/workflows/release-test-refresh.yml b/.github/workflows/release-test-refresh.yml deleted file mode 100644 index ad5bf66..0000000 --- a/.github/workflows/release-test-refresh.yml +++ /dev/null @@ -1,46 +0,0 @@ -name: Align release version contract test - -on: - push: - branches: - - release/v2.3.0 - paths: - - '.github/workflows/release-test-refresh.yml' - -permissions: - contents: write - -jobs: - update: - runs-on: ubuntu-latest - steps: - - name: Check out release branch - uses: actions/checkout@v6 - with: - ref: release/v2.3.0 - fetch-depth: 0 - - - name: Remove hard-coded release version from test - run: | - python - <<'PY' - from pathlib import Path - - path = Path('tests/test_agent_server.py') - text = path.read_text() - old_import = 'from lingxigraph import END, START, StateGraph, interrupt\n' - new_import = 'from lingxigraph import END, START, StateGraph, __version__, interrupt\n' - old_assert = 'self.assertEqual(initialized.json()["result"]["serverInfo"]["version"], "2.2.0")' - new_assert = 'self.assertEqual(initialized.json()["result"]["serverInfo"]["version"], __version__)' - if old_import not in text or old_assert not in text: - raise SystemExit('expected version contract test pattern not found') - path.write_text(text.replace(old_import, new_import, 1).replace(old_assert, new_assert, 1)) - PY - - - name: Commit test update - run: | - set -euo pipefail - git config user.name 'github-actions[bot]' - git config user.email '41898282+github-actions[bot]@users.noreply.github.com' - git add tests/test_agent_server.py - git commit -m 'test: derive MCP server version from package metadata' - git push origin HEAD:release/v2.3.0