Skip to content

Harden v0.6.0 release gates and first-run surfaces#299

Merged
Navi Bot (project-navi-bot) merged 9 commits into
codex/manifest-content-address-hardeningfrom
codex/release-hardening-v0.6.0
Jul 23, 2026
Merged

Harden v0.6.0 release gates and first-run surfaces#299
Navi Bot (project-navi-bot) merged 9 commits into
codex/manifest-content-address-hardeningfrom
codex/release-hardening-v0.6.0

Conversation

@Fieldnote-Echo

@Fieldnote-Echo Fieldnote-Echo commented Jul 19, 2026

Copy link
Copy Markdown
Member

Summary

  • wait up to 30 minutes for the latest exact-SHA main push runs across the six release-gated workflows
  • fail immediately on terminal non-success or a moving main, including a final post-query head check
  • block draft-release creation and every artifact-build front door directly behind the CI gate
  • audit the complete locked workspace graph on PRs and the weekly advisory scan
  • update anyhow 1.0.102 to 1.0.103 for RUSTSEC-2026-0190 and declare the actual transitive Zlib license
  • pin every internal path dependency to the lockstep 0.6.0 requirement and enforce future version-sync updates
  • align current README, compatibility, provenance, manifest-schema, and threat-model copy with v0.6.0
  • replace degenerate first examples with deterministic checked Rust and Python quickstarts, persistence/reopen, and visible output
  • add end-to-end first runs for both manifest packages plus complete ordvec-manifest CLI help
  • pin package-specific Python documentation and changelog links and add release invariants for first-run surface drift

Closes #272.

Stack

This is stacked on #298, which is stacked on #297. Merge in that order, then retarget this PR to main. Preserve the signed ancestry with merge commits; squash or rebase merging an earlier PR requires restacking and re-signing downstream commits.

Validation

  • release publish invariants: 18 tests pass
  • signed-release invariants: pass
  • actionlint v1.7.12: pass
  • cargo-deny full locked workspace/all-features: pass
  • cargo audit: no vulnerability after the documented dev-only bincode ignore
  • ordvec all-features: 352 pass, 4 ignored
  • ordvec-manifest all-features: 122 pass; docs.rs quickstart doctest passes
  • ordvec-ffi: 12 pass after CI-equivalent build
  • locally built v0.6.0 wheels: ordvec Python 505 pass; ordvec-manifest Python 7 pass
  • Rust and Python first runs: exact top document 0, score 0.396; persistence and two-stage follow-on pass
  • CLI create/verify first run: exact manifest path followed by verified
  • all-workspace all-target/all-feature clippy: clean
  • independent launch red-team: SHIP after narrowing current-contract invariants so historical migration text remains allowed

The full cargo test workspace command is not a valid local aggregate for the PyO3 cdylib members because it tries to link binding test harnesses without libpython; the package-separated Rust lanes plus wheel-installed Python suites above pass.

External pre-tag blocker

The repository now reports the live environment drift honestly. Do not tag v0.6.0 until tests/release_environment_settings.sh passes for both crates-io and pypi: restore toadkicker as an eligible reviewer, enable prevent self-review, and restore the 30-minute wait timer. The release-tag patterns are already correct.

@codecov

codecov Bot commented Jul 19, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@Fieldnote-Echo
Nelson Spence (Fieldnote-Echo) force-pushed the codex/manifest-content-address-hardening branch from 108fa63 to 8219786 Compare July 19, 2026 15:03
@Fieldnote-Echo Nelson Spence (Fieldnote-Echo) changed the title Harden v0.6 release gates and workspace advisory coverage Harden v0.6 release gates and first-run surfaces Jul 19, 2026
@Fieldnote-Echo Nelson Spence (Fieldnote-Echo) changed the title Harden v0.6 release gates and first-run surfaces Harden v0.6.0 release gates and first-run surfaces Jul 19, 2026
@Fieldnote-Echo

Copy link
Copy Markdown
Member Author

/agentic_review

@qodo-code-review

Copy link
Copy Markdown

PR Summary by Qodo

Harden v0.6.0 release CI gate and deterministic first-run docs

✨ Enhancement 📝 Documentation 🧪 Tests ⚙️ Configuration changes 🕐 40+ Minutes

Grey Divider

AI Description

• Make release.yml fail-closed by polling exact-SHA main push runs for up to 30 minutes.
• Block draft releases and all build/artifact jobs behind the CI gate.
• Expand cargo-deny to audit the full locked workspace graph on PRs and cron.
• Replace first-run docs with deterministic Rust/Python quickstarts and enforce via invariants
 tests.
Diagram

graph TD
  tag(("Release tag")) --> gate["release.yml: require-ci-green"] --> build["Draft release + artifacts"]
  gate --> api["GitHub API polls"] --> runs["Exact-SHA push runs (6 wfs)"]
  pr(("PR / weekly cron")) --> ci["ci.yml + audit.yml"] --> deny["cargo-deny: workspace/all-features/locked"]
  ci --> inv["Release invariants tests"]
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Use GitHub Checks/Status APIs instead of workflow-run polling
  • ➕ Could reduce custom parsing of workflow runs and simplify the gate logic.
  • ➕ Aligns with how branch protection consumes status/check contexts.
  • ➖ Tag-triggered releases still need an explicit mapping from tag SHA to required checks; status contexts can be incomplete/non-uniform across workflows.
  • ➖ Less direct control over handling Actions visibility delays vs terminal failures (the original release: wait for merged-main CI to settle before tag release jobs #272 problem).
2. Trigger release only after required workflows complete (workflow_run fan-in)
  • ➕ Eliminates polling loops and timeouts; release becomes event-driven.
  • ➕ Naturally runs only after CI completion/visibility.
  • ➖ More complex wiring across multiple workflows and events, with higher risk of misconfiguration for tags vs main pushes.
  • ➖ Harder to ensure the release is tied to protected main HEAD and not an outdated SHA without additional checks.

Recommendation: Keep the current polling-based require-ci-green approach: it is explicit, fail-closed, and directly addresses Actions run-visibility delays while still failing immediately on terminal non-success or a moving main. The added invariants tests meaningfully reduce future drift/regressions, which is the key risk of custom gating logic.

Files changed (31) +847 / -166

Enhancement (1) +92 / -2
main.rsImprove CLI help text and add tests enforcing help coverage +92/-2

Improve CLI help text and add tests enforcing help coverage

• Expands clap help/option descriptions across commands (hash/inspect/verify/create/sqlite) and updates the top-level about text. Adds a unit test that asserts help contains expected command and safety-option descriptions.

ordvec-manifest/src/main.rs

Tests (3) +329 / -16
test_rank_quant.pyAdd end-to-end test matching README quickstart behavior +36/-1

Add end-to-end test matching README quickstart behavior

• Adds a deterministic test that asserts the README quickstart’s top result/score, validates persistence/reopen, and exercises a minimal Bitmap + subset rerank path.

ordvec-python/tests/test_rank_quant.py

release_publish_invariants.pyEnforce release-gate polling semantics and first-run/docs/version invariants +284/-15

Enforce release-gate polling semantics and first-run/docs/version invariants

• Extends invariants to (1) enforce lockstep internal dependency versions across multiple workspace members, (2) ensure threat-model status date matches the changelog release date, (3) pin release-line markers (v0.6.0) and schema-major markers (v2) across docs, (4) validate first-run docs ordering and determinism, (5) require cargo-deny workspace/all-features/locked in CI and audit, and (6) assert require-ci-green bounded polling behavior and artifact-gating structure.

tests/release_publish_invariants.py

release_signed_release_invariants.shAssert artifact jobs depend directly on require-ci-green gate +9/-0

Assert artifact jobs depend directly on require-ci-green gate

• Adds a signed-release invariant that ensures notes and all build jobs 'needs: require-ci-green', preventing future DAG refactors from starting release artifact work before CI has settled green.

tests/release_signed_release_invariants.sh

Documentation (16) +330 / -116
PULL_REQUEST_TEMPLATE.mdRequire locked workspace cargo-deny invocation in PR checklist +1/-1

Require locked workspace cargo-deny invocation in PR checklist

• Updates the contributor checklist to use 'cargo deny --workspace --all-features --locked check' so reviewers validate the full dependency graph and committed lockfile.

.github/PULL_REQUEST_TEMPLATE.md

CHANGELOG.mdDocument v0.6.0 first-run and release gate hardening changes +21/-0

Document v0.6.0 first-run and release gate hardening changes

• Adds changelog entries describing deterministic first-run surfaces across Rust/Python/CLI/manifest packages and the tightened release and supply-chain CI policies.

CHANGELOG.md

CONTRIBUTING.mdAlign local verification docs with full locked workspace cargo-deny +1/-1

Align local verification docs with full locked workspace cargo-deny

• Updates the recommended local command to use 'cargo deny --workspace --all-features --locked check' for consistent supply-chain validation.

CONTRIBUTING.md

README.mdAdd deterministic quickstarts and reframe first-run entrypoints +85/-40

Add deterministic quickstarts and reframe first-run entrypoints

• Introduces a surface-selection table and runnable deterministic Rust/Python quickstarts that print visible output and demonstrate persistence/reopen. Cleans up outdated v0.5.0 wording to v0.6.0 and removes placeholder/TBD copy.

README.md

RELEASING.mdDocument new bounded polling CI gate and version-sync steps +16/-6

Document new bounded polling CI gate and version-sync steps

• Expands release docs to describe the 30-minute, 30-second-interval polling behavior and fail-closed semantics. Clarifies that internal path dependencies must be lockstep-bumped during release prep.

RELEASING.md

THREAT_MODEL.mdUpdate supply-chain section with live environment audit blocker +31/-24

Update supply-chain section with live environment audit blocker

• Updates threat-model status date and revises THREAT-SUPPLY-001 to reflect the current environment drift as a release blocker until audited settings are restored.

THREAT_MODEL.md

INDEX_PROVENANCE.mdAlign provenance docs to v0.6.0 / manifest v2 contract wording +5/-5

Align provenance docs to v0.6.0 / manifest v2 contract wording

• Updates references from v0.5.0/v1 to v0.6.0/v2 for '.ovfs' non-coverage and manifest verifier behavior.

docs/INDEX_PROVENANCE.md

PERSISTED_FORMAT.mdUpdate persisted-format contract markers to v0.6.0 +2/-2

Update persisted-format contract markers to v0.6.0

• Rewrites v0.5.0 references to v0.6.0 in the '.ovfs' probe/loader contract notes to match the current release line.

docs/PERSISTED_FORMAT.md

artifact-platform-matrix.mdCorrect ordvec-manifest default feature policy documentation +1/-1

Correct ordvec-manifest default feature policy documentation

• Updates the matrix row to reflect that 'ordvec-manifest' defaults to the 'cli' feature and that library consumers can disable defaults.

docs/artifact-platform-matrix.md

compatibility-policy.mdBump compatibility-policy references to manifest v2 and v0.6.0 +4/-4

Bump compatibility-policy references to manifest v2 and v0.6.0

• Updates schema/manifest version references (v1→v2) and release-line markers (v0.5.0→v0.6.0) to keep policy docs consistent with the current contract.

docs/compatibility-policy.md

quickstart.rsAdd runnable Rust quickstart example with deterministic output +20/-0

Add runnable Rust quickstart example with deterministic output

• Adds a tiny example program that builds a small index, queries it, asserts the top result, and prints a visible score for first-run verification.

examples/quickstart.rs

README.mdReplace minimal verify snippet with end-to-end create+verify quickstart +26/-9

Replace minimal verify snippet with end-to-end create+verify quickstart

• Adds a deterministic Python example that writes an ordvec index, creates a manifest, verifies it, and prints a visible success result. Clarifies sidecar-binding guidance.

ordvec-manifest-python/README.md

README.mdAdd first verified index walkthrough using CLI create/verify +38/-0

Add first verified index walkthrough using CLI create/verify

• Introduces an end-to-end quickstart that creates a tiny index via Python, then uses the CLI to create and verify the manifest with visible output.

ordvec-manifest/README.md

lib.rsAdd crate-level doctest showing verify_for_load usage +19/-0

Add crate-level doctest showing verify_for_load usage

• Adds a no-run documentation example demonstrating 'verify_for_load' and highlighting the intended immediate-load safety pattern after verification.

ordvec-manifest/src/lib.rs

README.mdAdd deterministic Python quickstart and persistence/two-stage examples +48/-15

Add deterministic Python quickstart and persistence/two-stage examples

• Replaces random-data examples with deterministic snippets that print a stable score, demonstrate persistence/reopen, and show a minimal two-stage subset rerank setup. Updates release-line doc links to v0.6.0.

ordvec-python/README.md

lib.rsReplace crate docs example with small deterministic quickstart +12/-8

Replace crate docs example with small deterministic quickstart

• Updates the top-level Rust doc example to use a tiny deterministic corpus and query, avoiding large placeholder allocations while still demonstrating 'RankQuant' usage.

src/lib.rs

Other (11) +96 / -32
audit.ymlRun cargo-deny across full locked workspace in scheduled advisory scan +6/-3

Run cargo-deny across full locked workspace in scheduled advisory scan

• Expands the weekly/cron audit job to run 'cargo-deny check' with '--workspace --all-features --locked', while keeping the scan scoped to advisories via 'command-arguments: advisories'.

.github/workflows/audit.yml

ci.ymlHarden PR cargo-deny job to use workspace/all-features/locked +1/-0

Harden PR cargo-deny job to use workspace/all-features/locked

• Adds '--workspace --all-features --locked' arguments to the cargo-deny CI job to prevent feature- or member-scoped blind spots during PR gating.

.github/workflows/ci.yml

release.ymlFail-closed release gate with bounded polling and artifact blocking +75/-22

Fail-closed release gate with bounded polling and artifact blocking

• Reworks 'require-ci-green' to poll exact-SHA main push runs for six workflows up to 30 minutes, failing immediately on terminal non-success or main moving (with a final HEAD re-check). Updates job dependencies so notes and all artifact builds require the gate.

.github/workflows/release.yml

Cargo.lockBump anyhow to 1.0.103 +2/-2

Bump anyhow to 1.0.103

• Updates the lockfile for 'anyhow' from 1.0.102 to 1.0.103 (including checksum change), consistent with the security advisory remediation described in the PR.

Cargo.lock

Cargo.tomlPin internal ordvec dependency to lockstep 0.6.0 requirement +1/-1

Pin internal ordvec dependency to lockstep 0.6.0 requirement

• Changes the workspace path dependency to include 'version = "0.6.0"' to enforce release-line synchronization.

benchmarks/beir-bench/Cargo.toml

deny.tomlDocument/allow Zlib and update cargo-deny invocation guidance +4/-1

Document/allow Zlib and update cargo-deny invocation guidance

• Updates the suggested cargo-deny command to include workspace/all-features/locked. Declares Zlib in the allowed license list with a rationale for its transitive presence.

deny.toml

Cargo.tomlPin ordvec-ffi core dependency to version 0.6.0 +1/-1

Pin ordvec-ffi core dependency to version 0.6.0

• Adds an explicit version requirement alongside the path dependency to enforce lockstep internal versioning.

ordvec-ffi/Cargo.toml

Cargo.tomlPin Python manifest core dependency to version 0.6.0 +1/-1

Pin Python manifest core dependency to version 0.6.0

• Adds 'version = "0.6.0"' to the path dependency on 'ordvec-manifest' (as 'ordvec_manifest_core') to enforce lockstep releases.

ordvec-manifest-python/Cargo.toml

pyproject.tomlAdd pinned Documentation and Changelog URLs for PyPI metadata +2/-0

Add pinned Documentation and Changelog URLs for PyPI metadata

• Adds 'Documentation' pointing to the v0.6.0-tagged README and a 'Changelog' link to main for the Python manifest package metadata.

ordvec-manifest-python/pyproject.toml

Cargo.tomlPin ordvec-python core dependency to version 0.6.0 +1/-1

Pin ordvec-python core dependency to version 0.6.0

• Adds 'version = "0.6.0"' for the path dependency 'ordvec_core' to enforce lockstep internal versioning.

ordvec-python/Cargo.toml

pyproject.tomlAdd pinned Documentation and Changelog URLs for PyPI metadata +2/-0

Add pinned Documentation and Changelog URLs for PyPI metadata

• Adds 'Documentation' pointing to the v0.6.0-tagged README and a 'Changelog' link to main for the ordvec Python package metadata.

ordvec-python/pyproject.toml

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 305d469e39

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "Codex (@codex) review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "Codex (@codex) address that feedback".

Comment thread .github/workflows/audit.yml
@qodo-code-review

qodo-code-review Bot commented Jul 22, 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. Release gate fails slow ✓ Resolved 🐞 Bug ☼ Reliability
Description
require-ci-green treats any gh api failure when querying workflow runs as “waiting”, so
permanent errors (e.g., wrong workflow name, missing permissions) will burn the full 30-minute
timeout instead of failing fast with a clear cause.
Code

.github/workflows/release.yml[R196-204]

+              api_error="$(mktemp)"
+              if ! runs_json="$(gh api \
+                "repos/${REPO}/actions/workflows/${wf}/runs?head_sha=${SHA}&branch=main&event=push&per_page=100" \
+                2>"$api_error")"; then
+                echo "waiting for ${wf}: API query failed: $(<"$api_error")"
+                rm -f "$api_error"
+                all_green=false
+                continue
+              fi
Relevance

⭐⭐⭐ High

Team often distinguishes transient API failures vs real errors and fails fast/closed (e.g., curl
error handling in #203/#204).

PR-#203
PR-#204
PR-#177

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The workflow explicitly catches all gh api non-zero exits and converts them into all_green=false
+ continue, which guarantees retry-until-deadline even for non-transient errors.

.github/workflows/release.yml[191-205]

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 release gate’s polling loop retries on *any* `gh api` error. This is good for transient GitHub Actions visibility delays, but it also turns permanent problems (404/403/401/422) into a 30-minute timeout and produces a misleading “waiting” message.

### Issue Context
This gate is a front door to release artifact creation; it should remain fail-closed, but should also fail quickly and diagnostically when the underlying issue is permanent.

### Fix Focus Areas
- .github/workflows/release.yml[191-206]

### Proposed fix
- Classify `gh api` failures:
 - If stderr contains an HTTP status indicating permanent failure (401/403/404/422), emit `::error::` and exit immediately.
 - Otherwise (5xx, transient network, rate limit), keep the current retry-with-deadline behavior.
- Optionally use `gh api --include` (or equivalent) to capture status codes more reliably than string matching.

This preserves the intended polling for eventual consistency while avoiding 30-minute “timeouts” for configuration mistakes.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. Doc invariant slice fail-open ✓ Resolved 🐞 Bug ≡ Correctness
Description
check_first_run_docs() slices using text.find("RankQuant::new") without checking for -1, so if
the marker string is missing/renamed the slice can become empty/wrong and the invariant may silently
stop enforcing the “no 10_000 placeholder corpus” rule.
Code

tests/release_publish_invariants.py[R973-976]

+    crate_docs = read_text("src/lib.rs")
+    for path, text in (("README.md", readme), ("src/lib.rs", crate_docs)):
+        if "10_000" in text[text.find("RankQuant::new") : text.find("RankQuant::new") + 800]:
+            fail(f"{path}: first example must not allocate a 10,000-row placeholder corpus")
Relevance

⭐⭐⭐ High

Repo repeatedly hardens invariants against brittle parsing/sentinel cases (many accepted robustness
guards in #180/#181/#266).

PR-#180
PR-#181
PR-#266

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The invariant currently relies on find() results as slice indices, but find() returns -1 when
absent; that does not raise and can yield an empty/wrong slice, so the check can stop checking the
intended region.

tests/release_publish_invariants.py[955-976]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`check_first_run_docs()` uses `text.find("RankQuant::new")` directly as slice bounds. If the substring isn’t found, `find()` returns `-1` and Python slicing won’t error; it can produce an empty or unintended slice, causing the `"10_000"` check to become ineffective.

### Issue Context
This check is part of release invariants; it should fail closed when its structural anchor is missing so the intended “no 10,000-row placeholder example” policy can’t be bypassed by a doc refactor.

### Fix Focus Areas
- tests/release_publish_invariants.py[955-976]

### Proposed fix
- Compute the position once.
- If the marker is missing (`pos < 0`), call `fail(...)` with a clear message.
- Slice from `pos:pos+800` only after the guard.

Example sketch:
```py
pos = text.find("RankQuant::new")
if pos < 0:
   fail(f"{path}: expected RankQuant::new in first-run example")
window = text[pos:pos+800]
if "10_000" in window:
   fail(...)
```

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Qodo Logo

Comment thread tests/release_publish_invariants.py
Comment thread .github/workflows/release.yml
@project-navi-bot
Navi Bot (project-navi-bot) merged commit 6d4ef60 into codex/manifest-content-address-hardening Jul 23, 2026
32 checks passed
@project-navi-bot
Navi Bot (project-navi-bot) deleted the codex/release-hardening-v0.6.0 branch July 23, 2026 18:55
Navi Bot (project-navi-bot) pushed a commit that referenced this pull request Jul 23, 2026
* fix(manifest): harden canonical writes for v0.6.0

* fix(manifest): close canonical write races

* fix(manifest): canonicalize equivalent JSON numbers

* test(manifest): make inode swap deterministic

* fix(manifest): normalize typed signed zero

* Harden v0.6.0 release gates and first-run surfaces (#299)

* ci: harden v0.6 release gates

* docs: polish v0.6.0 first-run surfaces

* fix(release): fail closed across audit gaps

* fix(ci): configure fuzz advisory manifest once
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