You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
move the remaining v0.5.0 changelog entries out of [Unreleased] and add a release invariant that fails if [Unreleased] is non-empty once the current package version has a dated section
update the public Python package docstring to match the copy-before-detach GIL safety contract, and add a drift guard against the stale zero-copy wording
remove the contradictory "stable / semver-covered" claim for Contingency / Projection while they remain behind experimental
soften turbovec provenance wording: courtesy/scaffold context only, with Project-Navi/ordvec as the active upstream and implementation history
tests/release_environment_settings.sh verified the GitHub crates-io and pypi environments: required reviewers are Fieldnote-Echo and toadkicker, self-review is blocked, the wait timer is 30 minutes, and deployment branches/tags are restricted to tag:v[0-9]*.[0-9]*.[0-9]*.
The registry trusted-publisher records for crates.io (ordvec, ordvec-manifest) and PyPI (ordvec, ordvec-manifest) still need to be verified in the registry owner/project settings before tagging.
• Finalize v0.5.0 changelog by moving entries out of [Unreleased] and enforcing emptiness.
• Align Python binding safety docs with copy-before-detach behavior and add drift guards.
• Clarify provenance language and remove “stable/semver-covered” claims for experimental APIs.
Diagram
sequenceDiagram
actor Maintainer
participant Invariants as "tests/release_publish_invariants.py"
participant Changelog as "CHANGELOG.md"
participant PyDoc as "ordvec-python/python/ordvec/__init__.py"
participant SafetyDoc as "docs/bindings-safety.md"
Maintainer->>Invariants: Run pre-tag release checks
Invariants->>Changelog: Require dated current version
Invariants->>Changelog: Fail if [Unreleased] non-empty
Invariants->>PyDoc: Require copy-before-detach contract
Invariants->>SafetyDoc: Require matching safety wording
Loading
High-Level Assessment
The following are alternative approaches to this PR:
1. Use a Markdown-aware changelog parser
➕ More robust than regex against heading/format variations
➕ Easier to extend with additional structural rules over time
➖ Adds dependency/complexity for a small invariant surface
➖ Still requires agreed conventions to be useful
2. Single-source the Python safety contract (shared snippet)
➕ Eliminates drift by construction (README/docstring rendered from one source)
➕ Reduces need for fragment-based tests
➖ Introduces a doc build step and templating/tooling
➖ Harder to keep packaging/simple source-tree viewing straightforward
Recommendation: Keep the current lightweight, dependency-free invariant checks: regex + fragment guards are appropriate for pre-tag gating and match the repo’s existing release-invariant style. If drift issues continue to recur, consider moving to a single-sourced safety-contract snippet as the next step.
Files changed (7) +128 / -54
Tests (1) +66 / -0
release_publish_invariants.pyAdd release invariants for changelog emptiness and doc drift+66/-0
Add release invariants for changelog emptiness and doc drift
• Adds a guard that fails publishing if the current version has a dated changelog section but [Unreleased] contains meaningful content. Adds a doc-sync check ensuring the Python package docstring and bindings safety doc reflect copy-before-detach behavior and rejects stale zero-copy threading fragments.
CHANGELOG.mdFinalize v0.5.0 section and empty-out [Unreleased]+19/-22
Finalize v0.5.0 section and empty-out [Unreleased]
• Moves remaining v0.5.0 entries out of the [Unreleased] area, adds an explicit “No unreleased changes” placeholder, and restores the dated 0.5.0 header in the right position. Updates older provenance wording to clarify turbovec as scaffold/lineage rather than an active upstream.
README.mdClarify provenance and upstream ownership+7/-5
Clarify provenance and upstream ownership
• Rewrites the provenance section to state Project-Navi/ordvec as the active upstream and governance source. Retains turbovec credit as historical scaffold and explicitly notes it is not a source fork.
README.mdAlign Python package provenance wording with repo README+7/-4
Align Python package provenance wording with repo README
• Updates provenance/licensing text to identify Project-Navi/ordvec as the active upstream. Downgrades turbovec phrasing to a courtesy/scaffold lineage note and clarifies it is not a fork.
__init__.pyUpdate Python docstring: copy-before-detach GIL safety contract+12/-7
Update Python docstring: copy-before-detach GIL safety contract
• Updates the top-level package docstring provenance text and replaces stale zero-copy/GIL wording. Documents that GIL-released calls copy NumPy inputs before detaching (race-free snapshot) and warns about temporary extra buffering and object-level scheduling constraints.
• Rewords the binding crate’s rustdoc provenance lines to match the new scaffold/lineage framing and Project-Navi upstream statement. No functional FFI changes.
lib.rsRust crate docs: provenance clarification and experimental API caveat+13/-14
Rust crate docs: provenance clarification and experimental API caveat
• Updates crate-level rustdoc to soften turbovec wording and emphasize this repository as the implementation history/upstream. Removes contradictory comments claiming experimental-gated APIs are already stable/semver-covered while they remain behind the feature gate.
1. Changelog parse fooled by ##✓ Resolved🐞 Bug☼ Reliability
Description
changelog_section_after_heading() ends the [Unreleased] section at the next line that starts
with ## , so a ## sequence inside an [Unreleased] code block/example can truncate scanning
and let non-empty unreleased content bypass the invariant. This weakens
check_unreleased_section_empty_for_dated_version() and can allow a release publish with leftover
[Unreleased] entries.
+def changelog_section_after_heading(changelog: str, heading: str) -> str:+ match = re.search(rf"^## {re.escape(heading)}\s*$", changelog, re.MULTILINE)+ if match is None:+ fail(f"CHANGELOG.md must contain a {heading} section")+ following = changelog[match.end() :]+ next_heading = re.search(r"^## ", following, re.MULTILINE)+ if next_heading is not None:+ return following[: next_heading.start()]+ return following
Relevance
⭐⭐⭐ High
Team repeatedly accepted hardening brittle parsing in release invariants (e.g., TOML parsing
robustness) in PR #180.
ⓘ Recommendations generated based on similar findings in past PRs
Evidence
The section slicing logic treats any ## line as the next section boundary, regardless of whether
it's a version heading, which makes the invariant vulnerable to valid Markdown content inside
[Unreleased].
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution
### Issue description
`changelog_section_after_heading()` currently finds the end of a section by searching for the next `^## ` line. This can incorrectly treat `## ` inside fenced code blocks or examples as a new top-level section heading, causing the `[Unreleased]` section slice to be truncated and potentially skipping real unreleased entries.
### Issue Context
This function is used to enforce that `[Unreleased]` is empty once the current version has a dated release section.
### Fix Focus Areas
- tests/release_publish_invariants.py[386-417]
### Implementation notes
- Restrict the “next heading” search to *release* headings only (e.g., `^## \[?\d+\.\d+\.\d+\]? - \d{4}-\d{2}-\d{2}` or `^## \[Unreleased\]`), rather than any `##`.
- Alternatively, parse line-by-line and ignore content inside fenced code blocks when looking for headings.
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
2. Safety invariant too permissive✓ Resolved🐞 Bug⚙ Maintainability
Description
check_python_binding_safety_docs_sync() searches required fragments across the entire
ordvec-python/python/ordvec/__init__.py file (not specifically the module docstring), so the
invariant can pass even if the docstring drifts as long as the phrases appear elsewhere. It also
checks forbidden fragments with a case-sensitive substring search, allowing stale zero-copy wording
to slip through with minor capitalization changes.
+def check_python_binding_safety_docs_sync() -> None:+ package_doc = read_text("ordvec-python/python/ordvec/__init__.py")+ safety_doc = read_text("docs/bindings-safety.md")+ package_doc_normalized = " ".join(package_doc.split()).lower()+ safety_doc_normalized = " ".join(safety_doc.split()).lower()++ required_fragments = (+ "copy NumPy inputs into Rust-owned buffers before detaching",+ "Large calls may temporarily require an additional input-sized buffer",+ )+ for fragment in required_fragments:+ if fragment.lower() not in package_doc_normalized:+ fail(f"ordvec-python/python/ordvec/__init__.py must document: {fragment}")++ safety_fragment = "copies NumPy input arrays into Rust-owned buffers"+ if safety_fragment.lower() not in safety_doc_normalized:+ fail("docs/bindings-safety.md must document Python copy-before-detach")++ forbidden_fragments = (+ "read in place",+ "not copied",+ "do not mutate an array from another thread",+ )+ for fragment in forbidden_fragments:+ if fragment in package_doc:+ fail(+ "ordvec-python/python/ordvec/__init__.py still contains stale "+ f"zero-copy threading wording: {fragment!r}"+ )
Relevance
⭐⭐ Medium
Mixed history: they harden invariants often (#180) but rejected more-robust parsing over simple
substring checks (#260).
ⓘ Recommendations generated based on similar findings in past PRs
Evidence
The invariant reads and normalizes the entire __init__.py file content for required fragments, and
performs a raw, case-sensitive substring check for forbidden fragments, so it can both (a) be
satisfied by non-docstring text and (b) miss forbidden wording with capitalization changes.
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution
### Issue description
The safety-doc drift guard is intended to keep the public Python package docstring aligned with `docs/bindings-safety.md`, but it currently:
1) scans the whole `__init__.py` file (comments/strings can satisfy it), and
2) performs case-sensitive matching for forbidden fragments.
### Issue Context
This check runs in the release publish invariant script and is meant to prevent stale “zero-copy / read in place” wording from reappearing.
### Fix Focus Areas
- tests/release_publish_invariants.py[478-507]
### Implementation notes
- Use `ast.parse()` + `ast.get_docstring()` to extract and validate only the module docstring.
- Normalize case/whitespace consistently for both required and forbidden fragment checks (e.g., compare in lowercase over a whitespace-normalized docstring).
- Keep `docs/bindings-safety.md` check as-is, but apply the same normalization approach.
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
[Unreleased]and add a release invariant that fails if[Unreleased]is non-empty once the current package version has a dated sectionContingency/Projectionwhile they remain behindexperimentalValidation
python3 tests/release_publish_invariants.pycargo fmt --checkpython3 -m py_compile tests/release_publish_invariants.py ordvec-python/python/ordvec/__init__.pygit diff --checkbash tests/release_environment_settings.shExternal Release Note
tests/release_environment_settings.shverified the GitHubcrates-ioandpypienvironments: required reviewers areFieldnote-Echoandtoadkicker, self-review is blocked, the wait timer is 30 minutes, and deployment branches/tags are restricted totag:v[0-9]*.[0-9]*.[0-9]*.The registry trusted-publisher records for crates.io (
ordvec,ordvec-manifest) and PyPI (ordvec,ordvec-manifest) still need to be verified in the registry owner/project settings before tagging.