Skip to content

Tighten v0.5.0 pre-tag release docs#266

Merged
Navi Bot (project-navi-bot) merged 2 commits into
mainfrom
codex/pretag-release-cleanup
Jun 20, 2026
Merged

Tighten v0.5.0 pre-tag release docs#266
Navi Bot (project-navi-bot) merged 2 commits into
mainfrom
codex/pretag-release-cleanup

Conversation

@Fieldnote-Echo

Copy link
Copy Markdown
Member

Summary

  • 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

Validation

  • python3 tests/release_publish_invariants.py
  • cargo fmt --check
  • python3 -m py_compile tests/release_publish_invariants.py ordvec-python/python/ordvec/__init__.py
  • git diff --check
  • bash tests/release_environment_settings.sh

External Release Note

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.

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@qodo-code-review

Copy link
Copy Markdown

PR Summary by Qodo

Tighten v0.5.0 pre-tag release docs and invariants
📝 Documentation 🧪 Tests 🕐 20-40 Minutes

Grey Divider

Description

• 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.

tests/release_publish_invariants.py

Documentation (6) +62 / -54
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.

CHANGELOG.md

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.md

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.

ordvec-python/README.md

__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.

ordvec-python/python/ordvec/init.py

lib.rsRust-side binding rustdoc provenance wording cleanup +4/-2

Rust-side binding rustdoc provenance wording cleanup

• Rewords the binding crate’s rustdoc provenance lines to match the new scaffold/lineage framing and Project-Navi upstream statement. No functional FFI changes.

ordvec-python/src/lib.rs

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.

src/lib.rs

@codecov

codecov Bot commented Jun 20, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@qodo-code-review

qodo-code-review Bot commented Jun 20, 2026

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (0) 📘 Rule violations (0) 📎 Requirement gaps (0) 🎨 UX issues (0) 🔗 Cross-repo conflicts (0) 📜 Skill insights (0)

Grey Divider


Remediation recommended

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.
Code

tests/release_publish_invariants.py[R386-394]

+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.

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].

tests/release_publish_invariants.py[386-394]
PR-#180

Agent prompt
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.
Code

tests/release_publish_invariants.py[R478-506]

+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).

PR-#180
PR-#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.

tests/release_publish_invariants.py[479-506]
ordvec-python/python/ordvec/init.py[1-61]
docs/bindings-safety.md[12-30]

Agent prompt
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


Grey Divider

Qodo Logo

@project-navi-bot Navi Bot (project-navi-bot) merged commit 1cda266 into main Jun 20, 2026
53 of 54 checks passed
@project-navi-bot Navi Bot (project-navi-bot) deleted the codex/pretag-release-cleanup branch June 20, 2026 01:22
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