Skip to content

epic-5/story-1: Establish Maintainer Release and Parity Validation Workflow - #43

Closed
usmanabbas7 wants to merge 10 commits into
epic-4/story-5-deliver-advanced-documentation-and-migration-guidesfrom
epic-5/story-1-establish-maintainer-release-and-parity-validation-workflow
Closed

epic-5/story-1: Establish Maintainer Release and Parity Validation Workflow#43
usmanabbas7 wants to merge 10 commits into
epic-4/story-5-deliver-advanced-documentation-and-migration-guidesfrom
epic-5/story-1-establish-maintainer-release-and-parity-validation-workflow

Conversation

@usmanabbas7

Copy link
Copy Markdown
Collaborator

Story 5.1 — Establish Maintainer Release and Parity Validation Workflow

Part of sprint sprint/2026-04-06-convert-python-sdk. Stacked on epic-4/story-5 (#42#41#40#39#38 → epic-3 chain).

What was built (genuine BUILD story — the story file's stale "shipped on dev-branch" Codex DAR was verified false against the live branch and treated only as a blueprint)

  • CI pipeline .github/workflows/ci.yml (qs-02): Ruff lint → mypy --strict → 15-cell pytest matrix (Python 3.9–3.13 × ubuntu/macos/windows) → dependency-bounds check → towncrier-fragment gate (PR-only) → uv build. workflow_callable so release reuses it.
  • Release workflow .github/workflows/release.yml (qs-11): v* tag-triggered, reuses ci.yml as the gate, version/tag match check, towncrier build, uv build, OIDC Trusted Publishing via pypa/gh-action-pypi-publish (id-token: write, no long-lived tokens), GitHub Release with compiled notes via scripts/extract_release_notes.py.
  • Coverage gates (qs-03): 85% project floor + separate 95% evaluation/ floor, both fail-not-warn. Backfilled evaluation/ 89% → 97% via new tests/test_evaluation_internals.py (45 direct unit tests, no production code changes).
  • Dependency bounds (qs-09): ci/lower-bounds-overrides.txt (httpx lower-bound install + smoke; httpx is the only runtime dep).
  • Changelog (qs-10): towncrier configured in pyproject.toml (changes/ fragments, CHANGELOG.md marker), changes/README.md convention doc, PR fragment gate.
  • Maintainer surface: scripts/verify_release.py reproduces all gates locally; docs/release-process.md + README "Releasing" section.
  • Parity gate (F-017): wires the EXISTING Story 3.5 tests/parity/ infra + scripts/generate_parity_fixtures.py as a release-blocking job (cross-SDK-critical fields per Story 4.3). No duplication.

Quality

  • Tests: 687 → 732 (+45 evaluation-internals). Zero regressions.
  • scripts/verify_release.py runs green locally — all 7 gates pass (Ruff, mypy --strict 26→0, pytest+85% floor, evaluation/ ≥95%, parity suite, towncrier draft, uv build). Overall coverage 96.81%.
  • Readiness: PASS 9/10 round 1, 0 auto-delegations.
  • Code review: clean round 1 → review-passed-with-warnings. All src/ edits gate-traceable and behavior-preserving (mypy/ruff fixes only; no opportunistic refactors).
  • Beads: epic ai-driven-product-dev-08y2; tasks -kwat/-h0df/-sds5/-siy9/-d2r2/-dh6r — closed.

Warnings / follow-ups

  • CI YAML structure-reviewed only (actionlint unavailable locally, no GitHub runner). Both ci.yml/release.yml parse cleanly and match qs-02/qs-11 — to be confirmed on the first real CI run.
  • Out-of-scope, documented not performed (in docs/release-process.md): Task 4.2 (PyPI Trusted Publisher one-time manual setup on pypi.org) and Task 5.1 (push v0.0.1-test to a fork to verify the publish job is reached).

🤖 Generated with Claude Code

@usmanabbas7 usmanabbas7 self-assigned this Jun 8, 2026
@usmanabbas7
usmanabbas7 requested a review from clllaur June 8, 2026 12:59

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request establishes the maintainer release and parity-validation workflow, introducing release process documentation, CI quality gates (Ruff, mypy, coverage, and Towncrier), helper scripts for release verification and notes extraction, and direct unit tests to meet the 95% coverage floor on evaluation modules. Feedback from the reviewer suggests running development tools via the current Python interpreter in the release verification script to prevent environment mismatches, and simplifying redundant ternary operations and explicit boolean checks in the evaluation rules and segments logic to be more Pythonic.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread scripts/verify_release.py
Comment on lines +122 to +123
runner.run("Ruff lint", ["ruff", "check", "src", "tests", "scripts"])
runner.run("mypy --strict", ["mypy", "--strict"])

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Invoking ruff and mypy directly as global shell commands can fail if they are not in the system PATH, or it might run a globally installed version instead of the one pinned in the active virtual environment. Running them via sys.executable -m ensures they are executed using the current Python interpreter's environment.

Suggested change
runner.run("Ruff lint", ["ruff", "check", "src", "tests", "scripts"])
runner.run("mypy --strict", ["mypy", "--strict"])
runner.run("Ruff lint", [sys.executable, "-m", "ruff", "check", "src", "tests", "scripts"])
runner.run("mypy --strict", [sys.executable, "-m", "mypy", "--strict"])

Comment thread scripts/verify_release.py
Comment on lines +130 to +133
runner.run(
"towncrier draft",
["towncrier", "build", "--draft", "--version", args.version],
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Similarly, invoking towncrier directly as a global command can lead to environment mismatch or command-not-found errors. Running it via sys.executable -m towncrier ensures the correct environment-specific package is used.

Suggested change
runner.run(
"towncrier draft",
["towncrier", "build", "--draft", "--version", args.version],
)
runner.run(
"towncrier draft",
[sys.executable, "-m", "towncrier", "build", "--draft", "--version", args.version],
)

Comment thread src/convert_sdk/evaluation/segments.py Outdated
this_matched = True
else:
this_matched = is_rule_matched(segment_rule, rules)
this_matched = True if not rules else is_rule_matched(segment_rule, rules)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The ternary expression True if not rules else is_rule_matched(segment_rule, rules) is redundant. Simplifying it to a direct boolean expression is more Pythonic and readable.

Suggested change
this_matched = True if not rules else is_rule_matched(segment_rule, rules)
this_matched = not rules or is_rule_matched(segment_rule, rules)

if _process_rule_item(data, item) is True:
return True
return False
return any(_process_rule_item(data, item) is True for item in items)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Since _process_rule_item is annotated to return a bool, the explicit is True check is redundant. Simplifying it to a direct boolean evaluation is more idiomatic.

Suggested change
return any(_process_rule_item(data, item) is True for item in items)
return any(_process_rule_item(data, item) for item in items)

if _process_or_when(data, block) is not True:
return False
return True
return all(_process_or_when(data, block) is True for block in blocks)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Since _process_or_when returns a bool, the explicit is True check is redundant and can be simplified.

Suggested change
return all(_process_or_when(data, block) is True for block in blocks)
return all(_process_or_when(data, block) for block in blocks)

if _process_and(data, branch) is True:
return True
return False
return any(_process_and(data, branch) is True for branch in or_branches)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Since _process_and returns a bool, the explicit is True check is redundant and can be simplified.

Suggested change
return any(_process_and(data, branch) is True for branch in or_branches)
return any(_process_and(data, branch) for branch in or_branches)

@usmanabbas7
usmanabbas7 force-pushed the epic-4/story-5-deliver-advanced-documentation-and-migration-guides branch from 65e82a8 to 16fccf1 Compare June 14, 2026 17:09
@usmanabbas7
usmanabbas7 force-pushed the epic-5/story-1-establish-maintainer-release-and-parity-validation-workflow branch from 13f7556 to 87786af Compare June 14, 2026 17:13
@usmanabbas7
usmanabbas7 force-pushed the epic-4/story-5-deliver-advanced-documentation-and-migration-guides branch from 16fccf1 to cc3c404 Compare June 15, 2026 11:31
usmanabbas7 and others added 10 commits June 15, 2026 17:02
Beads: ai-driven-product-dev-kwat
Agent: fullstack-sdk-dev
Status: completed

Added [tool.ruff]/[tool.mypy]/[tool.coverage.*]/[tool.towncrier] config, pinned
pytest to 8.4.x, added pytest-cov/ruff/mypy/towncrier dev-deps. Bounded
gate-traceable src/ fixes: 26 mypy-strict errors -> 0 (generic type args,
no-any-return casts, __exit__ -> None on Transport protocol + impls, generic
_diagnose TypeVar). Ruff E/W/F/B/SIM/RUF green on src (line-length=100 matching
codebase; tests/scripts scoped per-file-ignores). 687 tests green, 0 regression.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Beads: ai-driven-product-dev-kwat

Ruff --fix applied F401 unused-import and SIM collapsible cleanups to test
files as part of standing up the lint gate. No behavior change; 687 tests green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Beads: ai-driven-product-dev-h0df
Agent: sdk-test-writer
Status: completed

45 direct unit tests for evaluation/ internal helpers (bucketing surrogate
pairs, experiences bucket-building + miss paths, features _cast_value/
_feature_change_for/_variable_types, rules comparators, segments id-skip
branches). evaluation/ coverage 89% -> 97% (clears qs-03 95% floor). No
production code changed. 687 -> 732 tests, 0 regression. overall cov 96.81%.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Beads: ai-driven-product-dev-sds5
Agent: fullstack-sdk-dev
Status: completed

CHANGELOG.md with towncrier marker, changes/README.md documenting the
fragment-per-PR convention + categories, and an initial issue-less fragment for
the release-workflow standup. 'towncrier build --draft --version 0.1.0' renders
a valid 0.1.0 draft. qs-10. 732 tests green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…bounds override

Beads: ai-driven-product-dev-siy9
Agent: fullstack-sdk-dev
Status: completed

scripts/verify_release.py runs all 7 gates (ruff, mypy strict, pytest+85% floor,
evaluation/ 95% floor, parity suite, towncrier draft, uv build) and exits 0
locally — the maintainer green bar. scripts/extract_release_notes.py pulls a
version section from CHANGELOG.md for gh release --notes-file (handles v-prefix).
ci/lower-bounds-overrides.txt pins httpx==0.28.0 (sole runtime dep). qs-09/qs-03.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Beads: ai-driven-product-dev-d2r2
Agent: fullstack-sdk-dev
Status: completed

ci.yml: lint -> type-check (fail-fast) -> 15-cell test matrix (3.9-3.13 x
{ubuntu,macos,windows}) with project 85% + evaluation 95% coverage floors,
bounds-check (lower httpx==0.28.0 / upper newest), PR-only towncrier-fragment
gate, build (wheel+sdist artifact). workflow_call-able. release.yml: v* tag
trigger, reuses ci.yml as gate, version-match, towncrier build --yes, uv build,
OIDC publish via pypa/gh-action-pypi-publish (id-token: write, no tokens),
GitHub Release with extracted notes (--prerelease for a/b/rc/dev). Both YAML
parse + structure verified against qs-02/qs-11 (actionlint unavailable locally).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Beads: ai-driven-product-dev-dh6r
Agent: fullstack-sdk-dev
Status: completed

docs/release-process.md: full maintainer release flow (gates table, coverage+
parity release gates, parity-fixture regeneration via generate_parity_fixtures,
dependency-bounds policy, towncrier discipline, cut-a-release steps, one-time
PyPI Trusted Publisher setup [Task 4.2 documented-not-performed], v0.0.1-test
fork verification [Task 5.1 documented-not-performed], troubleshooting). README
Development gates + Releasing section. docs/index.md cross-link. 732 tests green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The CI matrix introduced in this story (qs-02: 3.9-3.13 × {ubuntu,macos,windows})
exposed two pre-existing test-portability defects, red on all 5 Windows cells:

- tests/integration/test_queue_lifecycle.py: the generated subprocess script
  was written with the platform-default encoding. On Windows (cp1252) the
  em-dashes in its comments became byte 0x97, which the child interpreter
  cannot parse as UTF-8 source (SyntaxError: Non-UTF-8 code). Write the script,
  read the output file, and write the child's JSONL with explicit encoding="utf-8".
- tests/test_layering.py: the import-boundary allow-list uses POSIX-style paths
  but compared against str(path.relative_to(_SRC)), which yields backslash
  separators on Windows -> false-positive contract violation. Normalize with
  Path.as_posix() at both comparison/report sites.

Product code unchanged; the SDK was already portable. 732 tests pass locally.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Windows has no POSIX SIGTERM delivery to a Python handler: os.kill(pid,
SIGTERM) maps to TerminateProcess and terminates the process unconditionally,
so the registered graceful-flush handler never runs and the child exits
non-zero. SIGTERM-based graceful shutdown is a POSIX concept; the SDK's
Windows graceful-shutdown path is the atexit hook, which is exercised by
test_atexit_scenario_attempts_final_delivery (passing on all platforms).

Marks the SIGTERM scenario skipif(sys.platform == "win32"). This is the last
of the Windows-matrix failures surfaced by the qs-02 CI matrix; the prior
commit fixed the UTF-8-encoding and path-separator portability bugs.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… to UTF-8 hashing — F-063 propagation

The qs-03 coverage tests in tests/test_evaluation_internals.py exercised the
deleted `_utf16_code_units` helper and asserted UTF-16 surrogate-pair semantics.
Replaced the 3 affected tests with `murmurhash3_32` tests covering the same
BMP/ASCII, astral (4-byte UTF-8), and mixed-string branches against real-npm
oracle values (😀 U+1F600 @9999 = 689720682). All other internal-coverage tests
unchanged.

Gates green on this branch (ruff/mypy come online here): full suite 740 passed,
parity 131 passed, ruff clean, mypy --strict clean (43 files).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@usmanabbas7
usmanabbas7 force-pushed the epic-5/story-1-establish-maintainer-release-and-parity-validation-workflow branch from 87786af to 331edf0 Compare June 15, 2026 12:03
@usmanabbas7

Copy link
Copy Markdown
Collaborator Author

F-066 propagation (rebase onto remediated 3-3 — conflict resolved)

Rebased onto the remediated stack (3-3 f2d8916 latch → … → this branch). segments.py conflicted as expected: this branch's [REL-1] ruff auto-fix reformatted the old per-segment matcher into a ternary, which collides with the F-066 latch. Resolved by keeping the latch (the ruff cosmetic fix is moot — the latch code is already ruff-clean); segments.py is now byte-identical to remediated 3-3. The two F-066 latch parity tests are present and the branch's ruff/pyproject cleanups are preserved.

  • uv run pytest742 passed · latch tests pass · uv run ruff check clean · uv run mypy --strict clean (43 files)
  • CI gate: PASSED — Ruff, mypy --strict, changelog fragment, build, bounds-check, and the full py3.9–3.13 × {ubuntu,macos,windows} test matrix all green.

@abbaseya

Copy link
Copy Markdown
Collaborator

Superseded — all commits already in main (bc76b64). Closing without merge as part of post-sprint cleanup.

@abbaseya abbaseya closed this Jun 18, 2026
@abbaseya
abbaseya deleted the epic-5/story-1-establish-maintainer-release-and-parity-validation-workflow branch June 18, 2026 16:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants