From 20819230bfa7d78f6c694153fcdf73b376895c69 Mon Sep 17 00:00:00 2001 From: Nelson Spence Date: Fri, 19 Jun 2026 13:00:14 -0500 Subject: [PATCH 1/3] Fix v0.5 release gate polish Signed-off-by: Nelson Spence --- CHANGELOG.md | 13 ++- Makefile | 5 +- README.md | 50 ++++++---- benchmarks/beir-bench/src/main.rs | 5 +- benchmarks/beir/README.md | 6 +- benchmarks/beir/beir_plot.py | 2 +- benchmarks/beir/requirements.txt | 2 +- docs/INDEX_PROVENANCE.md | 14 ++- docs/PERSISTED_FORMAT.md | 18 +++- docs/compatibility-policy.md | 10 +- tests/release_signed_release_invariants.sh | 110 ++++++++++++--------- 11 files changed, 154 insertions(+), 81 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 69c32cc7..344fc25b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,7 +13,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 tooling** (introduced with the benchmark harness; none reach the published `ordvec` crate or the `ordvec` PyPI wheel). The `benchmarks/beir/requirements.txt` deps were unpinned, so OSV flagged each against its full historical CVE list; - they are now lower-bound-pinned at the first patched release (`requests>=2.32.4`, + they are now lower-bound-pinned at the first patched release (`requests>=2.33.0`, `hnswlib>=0.8.0`, `numpy>=1.26`, plus safe floors for the rest). `bincode` 1.x (RUSTSEC-2025-0141, *unmaintained* — not a vulnerability) enters only transitively via `hnsw_rs` in `benchmarks/beir-bench` and is absent from @@ -37,8 +37,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - **`RankQuantFastscan` is now a stable, public API** (previously re-exported `#[doc(hidden)]`), with `.ovfs` / `OVFS` persistence via `RankQuantFastscan::{write,load}` and a ninth `load_fastscan` cargo-fuzz - target. Metadata-probe support (`probe_index_metadata`) for `.ovfs` is - deferred to 0.8.0 (#233, #232). + target. Metadata-probe support (`probe_index_metadata`) and + `ordvec-manifest` v1 support for `.ovfs` are deferred to 0.8.0 (#233, #232); + bind `.ovfs` artifacts with caller-owned checksums or attestations when they + cross a trust boundary. ### Performance @@ -64,6 +66,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed +- **Clarified BEIR benchmark release claims.** The committed README figures use + the default method set and do not yet include the newer + `sign-rq2-threaded` probe row; the docs and plot generator now distinguish + 4096-byte HNSW float-vector storage from implementation-owned graph side + storage instead of treating the graph as zero. - **On-disk format magics renamed to `OV*`** (`OVR1` / `OVRQ` / `OVBM` / `OVSB`). The loaders still accept the legacy `TV*` magics, so every previously-written `.tvr` / `.tvrq` / `.tvbm` / `.tvsb` file continues to load diff --git a/Makefile b/Makefile index 4b2e1f22..716d748b 100644 --- a/Makefile +++ b/Makefile @@ -59,10 +59,13 @@ SCALE_SIZES := 1000 3000 10000 30000 100000 170000 # ── methods (all measured in the single Rust process) ───────────────────────── # flat exact inner product (== FAISS IndexFlatIP math), 4096 B/vec -# hnsw pure-Rust HNSW M=32 (Malkov–Yashunin), 4096 B/vec +# hnsw pure-Rust HNSW M=32 (Malkov–Yashunin), 4096 B/vec + graph # rq2/rq4 ordvec RankQuant b=2 / b=4 (256 / 512 B/vec) # bitmap-rq2 ordvec Bitmap → RankQuant b=2 (two-stage) # sign-rq2 ordvec SignBitmap → RankQuant b=2 (two-stage) +# The committed README figures use this default set; they intentionally do not +# include the newer sign-rq2-threaded probe row until the public artifacts are +# regenerated and reviewed together. BENCH_METHODS := flat,hnsw,rq2,rq4,bitmap-rq2,sign-rq2 # ── encoder (canonical: GGUF Q8_0 via llama-cpp-python / CUDA) ──────────────── diff --git a/README.md b/README.md index ff81c9ab..9cd6237e 100644 --- a/README.md +++ b/README.md @@ -34,9 +34,9 @@ append-friendly, and graph-optional. > **ordvec matches dense retrieval quality within BEIR qrel noise at 8–16× smaller > vector storage — with no training and no graph build — and sub-millisecond -> single-query retrieval on 171K Harrier embeddings. A threaded HNSW graph still -> wins highly-parallel batched serving; ordvec wins the lightweight -> compressed-substrate lane.** +> single-query retrieval on 171K Harrier embeddings. In the committed +> default-method run, a threaded HNSW graph still wins highly-parallel batched +> serving; ordvec wins the lightweight compressed-substrate lane.** On **trec-covid** (171,332 documents, the public [BEIR](https://github.com/beir-cellar/beir) benchmark) with **Harrier-Q8** 1024-d embeddings, ordvec's two-stage retrieval @@ -130,9 +130,12 @@ Two further paths, for callers who need them: type: an optional b=2 FastScan kernel (block-32 nibble/PQ-LUT, AVX-512 → AVX2 → scalar dispatch) for absolute-minimum stage-1 scan latency, at 2× the RankQuant b=2 footprint (`dim/2` bytes/doc) and 8-bit LUT scoring noise. It - persists to `.ovfs` (magic `OVFS`). Reach for it only when scan latency at - b=2 is the binding constraint; the headline retrieval surface is still - `RankQuant` / `Bitmap` / two-stage. + persists to `.ovfs` (magic `OVFS`) through direct + `RankQuantFastscan::{write,load}` calls. In v0.5.0, `.ovfs` is not yet part + of the `probe_index_metadata()` / `ordvec-manifest` v1 contract; bind it with + an application-owned digest or attestation if it crosses a trust boundary. + Reach for it only when scan latency at b=2 is the binding constraint; the + headline retrieval surface is still `RankQuant` / `Bitmap` / two-stage. - **`MultiBucketBitmap`** *(behind `--features experimental`)* — the multi-bucket bilinear-overlap probe behind the research-side decomposition; an algebraic scaffold, not the top-bucket theorem surface or a production @@ -326,6 +329,12 @@ thread count, no Python/FFI in the hot path: - **`hnsw`** — pure-Rust HNSW (`hnsw_rs`, M=32, ef_construction=200, ef_search=128) — the portable stand-in for the C++ hnswlib. +The committed figures use the default method set in `Makefile`. They do not +yet include the newer `sign-rq2-threaded` probe row. Read HNSW byte labels as +stored float-vector bytes plus an implementation-owned graph side structure; +the tables and plot generator now spell that caveat out rather than treating +the graph as zero. + Reproduce end-to-end (downloads the data, embeds, runs every method, renders the figures, and emits the summary tables transcribed below): @@ -347,12 +356,12 @@ run; regenerate your own with `make benchmark-beir`. | Dataset | Method | Bytes/vec | nDCG@10 | Δ vs flat (95% CI) | |---|---|--:|--:|---| | scifact (5,183) | `flat` (exact) | 4096 | 0.7551 | (baseline) | -| | `hnsw` M=32 | 4096 | 0.7554 | +0.0003 * | +| | `hnsw` M=32 | 4096 + graph | 0.7554 | +0.0003 * | | | **ordvec rq4** | **512** | **0.7549** | −0.0003 * | | | ordvec rq2 | 256 | 0.7471 | −0.0080 * | | | ordvec sign→rq2 | 384 | 0.7471 | −0.0080 * | | trec-covid (171,332) | `flat` (exact) | 4096 | 0.7574 | (baseline) | -| | `hnsw` M=32 | 4096 | 0.7555 | −0.0019 * | +| | `hnsw` M=32 | 4096 + graph | 0.7555 | −0.0019 * | | | ordvec rq2 | 256 | 0.7632 | +0.0057 * | | | **ordvec rq4** | **512** | **0.7636** | +0.0062 * | | | ordvec sign→rq2 | 384 | 0.7638 | +0.0064 * | @@ -386,6 +395,9 @@ field compresses; `hnsw` threads best: ![threaded throughput bars](https://raw.githubusercontent.com/Fieldnote-Echo/ordvec/main/benchmarks/beir/figures/bars_threaded.png) `hnsw` 4.8× vs `flat`, ordvec `bitmap→rq2` 3.7×, `rq2` 2.5×, `sign→rq2` 2.1×. +This committed chart uses the default `sign-rq2` row, not the newer +within-query-threaded `sign-rq2-threaded` probe row; regenerate public figures +before using that probe for release claims. In this default-method view, **HNSW wins this regime** — by a hair on threaded throughput. The honest ordvec-vs-HNSW tradeoff, all from this same run (trec-covid, 171,332 docs): @@ -458,17 +470,19 @@ clean-checkout kernel sanity check. ## Security: index-file trust -The on-disk formats (`.ovr` / `.ovrq` / `.ovbm` / `.ovsb` / `.ovfs`; legacy -`.tvr` / `.tvrq` / `.tvbm` / `.tvsb` files still load) carry **no built-in -checksum, MAC, or signature — by design.** The loaders validate *structure* -(magic, version, bounds, exact-length payload) but not *origin*: a -structurally valid file can still be untrusted. If an index file crosses a +The probe/manifest-covered on-disk formats (`.ovr` / `.ovrq` / `.ovbm` / +`.ovsb`; legacy `.tvr` / `.tvrq` / `.tvbm` / `.tvsb` files still load) carry +**no built-in checksum, MAC, or signature — by design.** The loaders validate +*structure* (magic, version, bounds, exact-length payload) but not *origin*: a +structurally valid file can still be untrusted. `RankQuantFastscan` also writes +and loads `.ovfs` directly, but in v0.5.0 that format is not yet covered by +`probe_index_metadata()` or `ordvec-manifest` v1. If an index file crosses a trust boundary (network transfer, shared storage), verify it before loading. -`ordvec-manifest` binds an index file to a JSON manifest by SHA-256, header -metadata, row identity, named auxiliary sidecars, and attestation shape checks. -It does not sign artifacts, manage keys, or decide deployment trust policy. No -in-format crypto is shipped because it would add key management the library -can't own. See +`ordvec-manifest` binds supported index files to a JSON manifest by SHA-256, +header metadata, row identity, named auxiliary sidecars, and attestation shape +checks. It does not sign artifacts, manage keys, or decide deployment trust +policy. No in-format crypto is shipped because it would add key management the +library can't own. See [`docs/PERSISTED_FORMAT.md`](https://github.com/Fieldnote-Echo/ordvec/blob/main/docs/PERSISTED_FORMAT.md), [`docs/INDEX_PROVENANCE.md`](https://github.com/Fieldnote-Echo/ordvec/blob/main/docs/INDEX_PROVENANCE.md), and [`THREAT_MODEL.md`](https://github.com/Fieldnote-Echo/ordvec/blob/main/THREAT_MODEL.md) diff --git a/benchmarks/beir-bench/src/main.rs b/benchmarks/beir-bench/src/main.rs index 2d52b5ea..0e1962e4 100644 --- a/benchmarks/beir-bench/src/main.rs +++ b/benchmarks/beir-bench/src/main.rs @@ -986,8 +986,9 @@ fn run_hnsw( let build_seconds = t0.elapsed().as_secs_f64(); eprintln!(" build done in {build_seconds:.2}s"); - // HNSW graph size is implementation-internal; report the stored-vector bytes - // (full float) as the index footprint, matching the dense baseline accounting. + // HNSW graph size is implementation-internal to hnsw_rs; the numeric field + // reports stored float-vector bytes only. Public docs label this as + // "4096 B + graph" so the side structure is not silently counted as zero. let bytes_per_vector = dim * 4; let index_total_mib = (n_docs * bytes_per_vector) as f64 / 1024.0 / 1024.0; let warmup = 5.min(n_queries); diff --git a/benchmarks/beir/README.md b/benchmarks/beir/README.md index 3f9887e4..99a25600 100644 --- a/benchmarks/beir/README.md +++ b/benchmarks/beir/README.md @@ -70,7 +70,7 @@ against the host CUDA toolkit (`CMAKE_ARGS="-DGGML_CUDA=on"`; override | Method | Bytes/vec | Description | |-------------------|----------:|-------------| | `flat` | 4096 | Exact inner product (== FAISS `IndexFlatIP` math), pure-Rust SIMD GEMM. **Baseline, not ground truth.** | -| `hnsw` | 4096 | Pure-Rust HNSW (`hnsw_rs`, M=32, ef=128) — portable stand-in for C++ hnswlib. | +| `hnsw` | 4096 + graph | Pure-Rust HNSW (`hnsw_rs`, M=32, ef=128) — portable stand-in for C++ hnswlib. The graph is implementation-owned side storage, not included in the 4096-byte float-vector payload. | | `rq2` | 256 | RankQuant 2 bits/dim, asymmetric float-query LUT scan. | | `rq4` | 512 | RankQuant 4 bits/dim, asymmetric float-query LUT scan. | | `bitmap-rq2` | 384 | Two-stage: Bitmap candidate-gen → RankQuant-2 rerank. | @@ -79,6 +79,10 @@ against the host CUDA toolkit (`CMAKE_ARGS="-DGGML_CUDA=on"`; override Thread/batch knobs (per `beir-bench`): `--threads N` pins query latency to a rayon pool of N threads (index build still uses all cores); `--max-docs M` sub-samples the corpus for the scaling sweep; `--batch` sets the matched batch. +The committed README figures use the default method set from the top-level +`Makefile`; they do not yet include the newer `sign-rq2-threaded` probe row. +Regenerate and review the public tables before using that probe for release +claims. ## Cache layout diff --git a/benchmarks/beir/beir_plot.py b/benchmarks/beir/beir_plot.py index e7719d78..74ddcf1e 100644 --- a/benchmarks/beir/beir_plot.py +++ b/benchmarks/beir/beir_plot.py @@ -36,7 +36,7 @@ # (slug, display label, colour). Order = legend / bar order. METHOD_STYLE: list[tuple[str, str, str]] = [ ("flat", "flat (exact IP, 4096 B)", "#444444"), - ("hnsw", "HNSW M=32 (4096 B)", "#1f77b4"), + ("hnsw", "HNSW M=32 (4096 B + graph)", "#1f77b4"), ("ordvec-rq4", "ordvec RankQuant b=4 (512 B)", "#2ca02c"), ("ordvec-rq2", "ordvec RankQuant b=2 (256 B)", "#17becf"), ("ordvec-bitmap-rq2", "ordvec Bitmap→rq2 (384 B)", "#ff7f0e"), diff --git a/benchmarks/beir/requirements.txt b/benchmarks/beir/requirements.txt index 34e85dd2..e6083edd 100644 --- a/benchmarks/beir/requirements.txt +++ b/benchmarks/beir/requirements.txt @@ -20,7 +20,7 @@ # --- core --- numpy>=1.26.0 scipy>=1.11.0 -requests>=2.32.4 # GHSA-9hjg-9r4m-mvj7 (.netrc leak) + all older requests CVEs +requests>=2.33.0 # GHSA-gc5v-m9x4-r6x2 + older requests CVEs tqdm>=4.66.3 # CVE-2024-34062 pandas>=2.2.0 tabulate>=0.9.0 diff --git a/docs/INDEX_PROVENANCE.md b/docs/INDEX_PROVENANCE.md index ac94b8d8..39719ede 100644 --- a/docs/INDEX_PROVENANCE.md +++ b/docs/INDEX_PROVENANCE.md @@ -1,8 +1,11 @@ # Index file provenance -`ordvec` persists indexes as `.ovr` / `.ovrq` / `.ovbm` / `.ovsb` files and -reloads them through `Rank::load`, `RankQuant::load`, `Bitmap::load`, and -`SignBitmap::load`. This note states exactly **what the loaders guarantee and +`ordvec` persists the manifest/probe-covered primitive indexes as `.ovr` / +`.ovrq` / `.ovbm` / `.ovsb` files and reloads them through `Rank::load`, +`RankQuant::load`, `Bitmap::load`, and `SignBitmap::load`. +`RankQuantFastscan` also writes and loads `.ovfs` through its direct API, but in +v0.5.0 `.ovfs` is not covered by `probe_index_metadata()` or +`ordvec-manifest` v1. This note states exactly **what the loaders guarantee and what they do not**, so you can decide whether an index file needs out-of-band verification before you load it. For the byte layout and versioning of the persisted formats themselves, see [`PERSISTED_FORMAT.md`](PERSISTED_FORMAT.md). @@ -98,6 +101,11 @@ The manifest verifier checks: least one subject SHA-256 matching the artifact when attestations are supplied. +The v1 verifier intentionally does not create or verify `.ovfs` FastScan +artifacts yet. If a `RankQuantFastscan` artifact crosses a trust boundary in +v0.5.0, bind the bytes with a caller-owned checksum, artifact-store control, or +attestation and load it directly only after that policy check succeeds. + Auxiliary artifacts are for application-owned sidecars such as metadata, secondary indexes, or stores that a caller intends to load together with the ordvec index. The verifier does not interpret those bytes; it only reports diff --git a/docs/PERSISTED_FORMAT.md b/docs/PERSISTED_FORMAT.md index 1d53282c..d41c9317 100644 --- a/docs/PERSISTED_FORMAT.md +++ b/docs/PERSISTED_FORMAT.md @@ -5,13 +5,21 @@ It covers the primitive index artifacts only: `.ovr`, `.ovrq`, `.ovbm`, and `.ovsb`. It does not define a database, transaction log, replication protocol, provenance system, checksum manifest, signature, or trust policy. +`RankQuantFastscan` can write and load `.ovfs` (magic `OVFS`) through its direct +API, but `.ovfs` is intentionally outside this v1 primitive-format, +`probe_index_metadata()`, and `ordvec-manifest` contract. Until metadata-probe +and manifest support are promoted, callers should treat `.ovfs` as a +specialized direct-load artifact and bind it with application-owned checksums or +attestations when it crosses a trust boundary. + All integer fields are little-endian. Each format has one fixed header followed by one contiguous payload. The payload must consume the rest of the file exactly; v1 files have no footer, reserved trailing bytes, or extension block. ## Compatibility Policy -The current on-disk format version is `1` for every persisted index family. +The current on-disk format version is `1` for every primitive family covered +here. Within the v1 contract: - loaders and `probe_index_metadata()` reject unknown magic, unsupported @@ -54,6 +62,11 @@ cache in their own manifests: `n_top`; - `file_size_bytes`: total observed file size. +In v0.5.0, `probe_index_metadata(path)` rejects `OVFS` with an unsupported +metadata-probe error rather than returning a partial descriptor. Load `.ovfs` +only through `RankQuantFastscan::load` unless and until the FastScan metadata +contract is promoted in a later minor release. + Example external segment entry: ```json @@ -209,5 +222,8 @@ payload invariants: - `Bitmap::load`: each row has exactly `n_top` bits set; - `SignBitmap::load`: no additional row invariant exists. +`RankQuantFastscan::load` has its own direct loader path for `.ovfs`; it is not +covered by this probe-versus-load contract in v0.5.0. + Loader success is the primitive binary-safety boundary. It is not a provenance or deployment-policy decision. diff --git a/docs/compatibility-policy.md b/docs/compatibility-policy.md index ab0edb01..cd0d7009 100644 --- a/docs/compatibility-policy.md +++ b/docs/compatibility-policy.md @@ -62,7 +62,10 @@ The `experimental` feature is a default-off research surface. Today it exposes `MultiBucketBitmap`; it is not patch-stable before 1.0. `RankQuantFastscan` is a stable, public (but specialized) type, covered by the -normal pre-1.0 compatibility policy above. `#[doc(hidden)]` exports such as +normal pre-1.0 compatibility policy above. Its direct `.ovfs` +`RankQuantFastscan::{write,load}` path is supported, but in v0.5.0 `.ovfs` is +not yet part of the primitive persisted-format, `probe_index_metadata()`, or +`ordvec-manifest` v1 contract. `#[doc(hidden)]` exports such as `search_asymmetric_byte_lut` are reachable for internal benchmarks and parity tests, but are not part of the stable default API. @@ -131,6 +134,11 @@ Legacy files using the old turbovec-era magics (`TVR1`, `TVRQ`, `TVBM`, `TVSB` and extensions `.tvr`, `.tvrq`, `.tvbm`, `.tvsb`) are still accepted by current loaders. Writers no longer emit those magics. +`RankQuantFastscan` writes and loads `.ovfs` / `OVFS` directly, but that +specialized format is not included in the primitive probe/manifest contract for +v0.5.0. Promoting `.ovfs` into `probe_index_metadata()` and `ordvec-manifest` +requires an explicit future compatibility review. + Patch releases should keep valid files from the same minor series loadable. Loader hardening may reject malformed files, forged sizes, trailing bytes, bad dimensions, unsupported bit widths, or files outside documented capacity diff --git a/tests/release_signed_release_invariants.sh b/tests/release_signed_release_invariants.sh index 7d3df87a..8daefee3 100755 --- a/tests/release_signed_release_invariants.sh +++ b/tests/release_signed_release_invariants.sh @@ -48,6 +48,16 @@ fail() { echo "::error::signed-release invariant violated: $*"; exit 1; } wf=".github/workflows/release.yml" [ -f "$wf" ] || fail "$wf: workflow file not found" +matches_regex() { + local text="$1" pattern="$2" + grep -qE -- "$pattern" <<<"$text" +} + +contains_literal() { + local text="$1" needle="$2" + grep -q -- "$needle" <<<"$text" +} + # Extract the body of a job (from ` :` to the next 2-space-indented job key). job_body() { local jobname="$1" start end @@ -64,12 +74,14 @@ job_needs() { local body escaped body="$(job_body "$jobname")" escaped="$(printf '%s' "$needed" | sed 's/[][\\.^$*+?{}|()]/\\&/g')" - printf '%s\n' "$body" | grep -qE "(^[[:space:]]+needs:[[:space:]]*\\[[^]]*(^|[^A-Za-z0-9_-])${escaped}([^A-Za-z0-9_-]|$)|^[[:space:]]+-[[:space:]]+${escaped}[[:space:]]*$)" + matches_regex "$body" "(^[[:space:]]+needs:[[:space:]]*\\[[^]]*(^|[^A-Za-z0-9_-])${escaped}([^A-Za-z0-9_-]|$)|^[[:space:]]+-[[:space:]]+${escaped}[[:space:]]*$)" } job_line() { - local jobname="$1" pattern="$2" - job_body "$jobname" | grep -nE "$pattern" | head -1 | cut -d: -f1 + local jobname="$1" pattern="$2" body line + body="$(job_body "$jobname")" + line="$(grep -nE -m 1 -- "$pattern" <<<"$body" || true)" + [ -n "$line" ] && printf '%s\n' "${line%%:*}" } require_job_line() { @@ -112,14 +124,14 @@ done body_draft="$(job_body release-assets-draft)" github_repo_env_re='^[[:space:]]+GH_REPO:[[:space:]]*"?\$\{\{[[:space:]]*github\.repository[[:space:]]*\}\}"?[[:space:]]*$' for ext in '\.crate' '\.whl' '\.tar\.gz' '\.sigstore\.json' '\.intoto\.jsonl'; do - printf '%s\n' "$body_draft" | grep -qE "dist/\*${ext}([^a-zA-Z]|$)" \ + matches_regex "$body_draft" "dist/\*${ext}([^a-zA-Z]|$)" \ || fail "release-assets-draft must \`gh release upload\` dist/*$(printf '%s' "$ext" | sed 's/\\//g')" done -printf '%s\n' "$body_draft" | grep -qE 'name:[[:space:]]*pypi-canonical-dist' \ +matches_regex "$body_draft" 'name:[[:space:]]*pypi-canonical-dist' \ || fail "release-assets-draft must upload canonical Python dist, not raw rebuilt wheel/sdist artifacts" job_downloads_artifact_to_path release-assets-draft dist-crate dist \ || fail "release-assets-draft must download the core dist-crate artifact into dist" -printf '%s\n' "$body_draft" | grep -qE "$github_repo_env_re" \ +matches_regex "$body_draft" "$github_repo_env_re" \ || fail "release-assets-draft must set \`GH_REPO: \${{ github.repository }}\` (no checkout, so gh release upload needs explicit repo context)" body_manifest_draft="$(job_body release-manifest-assets-draft)" @@ -139,17 +151,17 @@ job_downloads_artifact_to_path release-manifest-assets-draft sigstore-bundle-man || fail "release-manifest-assets-draft must download the manifest Sigstore bundle into dist" job_downloads_artifact_to_path release-manifest-assets-draft pypi-manifest-canonical-dist dist \ || fail "release-manifest-assets-draft must download the canonical manifest Python dist into dist" -printf '%s\n' "$body_manifest_draft" | grep -qE 'dist/\*\.crate([^a-zA-Z]|$)' \ +matches_regex "$body_manifest_draft" 'dist/\*\.crate([^a-zA-Z]|$)' \ || fail "release-manifest-assets-draft must upload dist/*.crate" -printf '%s\n' "$body_manifest_draft" | grep -qE 'dist/\*\.whl([^a-zA-Z]|$)' \ +matches_regex "$body_manifest_draft" 'dist/\*\.whl([^a-zA-Z]|$)' \ || fail "release-manifest-assets-draft must upload dist/*.whl" -printf '%s\n' "$body_manifest_draft" | grep -qE 'dist/\*\.tar\.gz([^a-zA-Z]|$)' \ +matches_regex "$body_manifest_draft" 'dist/\*\.tar\.gz([^a-zA-Z]|$)' \ || fail "release-manifest-assets-draft must upload dist/*.tar.gz" -printf '%s\n' "$body_manifest_draft" | grep -qE 'dist/\*\.sigstore\.json([^a-zA-Z]|$)' \ +matches_regex "$body_manifest_draft" 'dist/\*\.sigstore\.json([^a-zA-Z]|$)' \ || fail "release-manifest-assets-draft must upload dist/*.sigstore.json" -printf '%s\n' "$body_manifest_draft" | grep -qE 'dist/\*\.intoto\.jsonl([^a-zA-Z]|$)' \ +matches_regex "$body_manifest_draft" 'dist/\*\.intoto\.jsonl([^a-zA-Z]|$)' \ || fail "release-manifest-assets-draft must upload dist/*.intoto.jsonl" -printf '%s\n' "$body_manifest_draft" | grep -qE "$github_repo_env_re" \ +matches_regex "$body_manifest_draft" "$github_repo_env_re" \ || fail "release-manifest-assets-draft must set \`GH_REPO: \${{ github.repository }}\`" # ---------------------------------------------------------------------- @@ -157,10 +169,10 @@ printf '%s\n' "$body_manifest_draft" | grep -qE "$github_repo_env_re" \ # that; un-drafting here would re-introduce the public-release-before- # publish failure mode). # ---------------------------------------------------------------------- -if printf '%s\n' "$body_draft" | grep -qE 'gh release edit.*--draft=false'; then +if matches_regex "$body_draft" 'gh release edit.*--draft=false'; then fail "release-assets-draft must NOT un-draft the Release (un-drafting belongs in publish-github-release, after all registry publishes succeed)" fi -if printf '%s\n' "$body_manifest_draft" | grep -qE 'gh release edit.*--draft=false'; then +if matches_regex "$body_manifest_draft" 'gh release edit.*--draft=false'; then fail "release-manifest-assets-draft must NOT un-draft the Release (un-drafting belongs in publish-github-release, after all registry publishes succeed)" fi @@ -169,42 +181,42 @@ fi # (NOT a SHA — SLSA trust model requires the tag for its self-verification) # ---------------------------------------------------------------------- prov="$(job_body provenance)" -printf '%s\n' "$prov" | grep -qE 'uses:[[:space:]]*slsa-framework/slsa-github-generator/.+/generator_generic_slsa3\.yml@v[0-9]+\.[0-9]+\.[0-9]+' \ +matches_regex "$prov" 'uses:[[:space:]]*slsa-framework/slsa-github-generator/.+/generator_generic_slsa3\.yml@v[0-9]+\.[0-9]+\.[0-9]+' \ || fail "provenance must \`uses: slsa-framework/slsa-github-generator/.../generator_generic_slsa3.yml@vX.Y.Z\` (tag-pinned per SLSA trust model)" manifest_prov="$(job_body manifest-provenance)" -printf '%s\n' "$manifest_prov" | grep -qE 'uses:[[:space:]]*slsa-framework/slsa-github-generator/.+/generator_generic_slsa3\.yml@v[0-9]+\.[0-9]+\.[0-9]+' \ +matches_regex "$manifest_prov" 'uses:[[:space:]]*slsa-framework/slsa-github-generator/.+/generator_generic_slsa3\.yml@v[0-9]+\.[0-9]+\.[0-9]+' \ || fail "manifest-provenance must \`uses: slsa-framework/slsa-github-generator/.../generator_generic_slsa3.yml@vX.Y.Z\` (tag-pinned per SLSA trust model)" # ---------------------------------------------------------------------- # (5) provenance must have `upload-assets: false` — asset-staging jobs, not # SLSA generator workflows, own Release uploads. # ---------------------------------------------------------------------- -printf '%s\n' "$prov" | grep -qE '^[[:space:]]+upload-assets:[[:space:]]*false[[:space:]]*$' \ +matches_regex "$prov" '^[[:space:]]+upload-assets:[[:space:]]*false[[:space:]]*$' \ || fail "provenance must set \`upload-assets: false\` (release-assets-draft uploads the collected .intoto.jsonl from the workflow-artifact path)" -printf '%s\n' "$manifest_prov" | grep -qE '^[[:space:]]+upload-assets:[[:space:]]*false[[:space:]]*$' \ +matches_regex "$manifest_prov" '^[[:space:]]+upload-assets:[[:space:]]*false[[:space:]]*$' \ || fail "manifest-provenance must set \`upload-assets: false\` (release-manifest-assets-draft uploads the collected .intoto.jsonl from the workflow-artifact path)" # ---------------------------------------------------------------------- # (6) provenance-name MUST end in `.intoto.jsonl` — Scorecard's provenance # probe is a pure filename-suffix match. # ---------------------------------------------------------------------- -printf '%s\n' "$prov" | grep -qE '^[[:space:]]+provenance-name:.*\.intoto\.jsonl[[:space:]]*$' \ +matches_regex "$prov" '^[[:space:]]+provenance-name:.*\.intoto\.jsonl[[:space:]]*$' \ || fail "provenance must set \`provenance-name: .intoto.jsonl\` (Scorecard Signed-Releases provenance probe matches this suffix only)" -printf '%s\n' "$manifest_prov" | grep -qE '^[[:space:]]+provenance-name:.*ordvec-manifest-.*\.intoto\.jsonl[[:space:]]*$' \ +matches_regex "$manifest_prov" '^[[:space:]]+provenance-name:.*ordvec-manifest-.*\.intoto\.jsonl[[:space:]]*$' \ || fail "manifest-provenance must set \`provenance-name: ordvec-manifest-.intoto.jsonl\`" # ---------------------------------------------------------------------- # (7) attest job grants id-token: write + attestations: write # ---------------------------------------------------------------------- att="$(job_body attest)" -printf '%s\n' "$att" | grep -qE '^[[:space:]]+id-token:[[:space:]]*write' \ +matches_regex "$att" '^[[:space:]]+id-token:[[:space:]]*write' \ || fail "attest job must grant \`id-token: write\` (Sigstore OIDC signing cert)" -printf '%s\n' "$att" | grep -qE '^[[:space:]]+attestations:[[:space:]]*write' \ +matches_regex "$att" '^[[:space:]]+attestations:[[:space:]]*write' \ || fail "attest job must grant \`attestations: write\` (persist to the GitHub attestation store)" att_manifest="$(job_body attest-manifest)" -printf '%s\n' "$att_manifest" | grep -qE '^[[:space:]]+id-token:[[:space:]]*write' \ +matches_regex "$att_manifest" '^[[:space:]]+id-token:[[:space:]]*write' \ || fail "attest-manifest job must grant \`id-token: write\` (Sigstore OIDC signing cert)" -printf '%s\n' "$att_manifest" | grep -qE '^[[:space:]]+attestations:[[:space:]]*write' \ +matches_regex "$att_manifest" '^[[:space:]]+attestations:[[:space:]]*write' \ || fail "attest-manifest job must grant \`attestations: write\` (persist to the GitHub attestation store)" job_needs attest-manifest build-manifest-crate \ || fail "attest-manifest must \`needs: build-manifest-crate\`" @@ -229,9 +241,9 @@ job_downloads_artifact_to_path combine-manifest-hash pypi-manifest-canonical-dis build_manifest="$(job_body build-manifest-crate)" job_needs build-manifest-crate publish-crate \ || fail "build-manifest-crate must \`needs: publish-crate\` so lockstep ordvec exists on crates.io" -printf '%s\n' "$build_manifest" | grep -qE 'cargo[[:space:]]+package[[:space:]]+-p[[:space:]]+ordvec-manifest[[:space:]]+--locked([^[:alnum:]_-]|$)' \ +matches_regex "$build_manifest" 'cargo[[:space:]]+package[[:space:]]+-p[[:space:]]+ordvec-manifest[[:space:]]+--locked([^[:alnum:]_-]|$)' \ || fail "build-manifest-crate must package ordvec-manifest with Cargo registry verification" -if printf '%s\n' "$build_manifest" | grep -q -- '--no-verify'; then +if contains_literal "$build_manifest" '--no-verify'; then fail "build-manifest-crate must not use --no-verify after publish-crate" fi @@ -240,20 +252,20 @@ fi # ---------------------------------------------------------------------- for pub in publish-crate publish-pypi; do body="$(job_body "$pub")" - printf '%s\n' "$body" | grep -qE '^[[:space:]]+id-token:[[:space:]]*write' \ + matches_regex "$body" '^[[:space:]]+id-token:[[:space:]]*write' \ || fail "$pub must grant \`id-token: write\` (Trusted Publishing OIDC)" job_needs "$pub" release-assets-draft \ || fail "$pub must \`needs: release-assets-draft\` (gated by attest + provenance via the draft-assets edge)" done body="$(job_body publish-manifest-crate)" -printf '%s\n' "$body" | grep -qE '^[[:space:]]+id-token:[[:space:]]*write' \ +matches_regex "$body" '^[[:space:]]+id-token:[[:space:]]*write' \ || fail "publish-manifest-crate must grant \`id-token: write\` (Trusted Publishing OIDC)" job_needs publish-manifest-crate release-manifest-assets-draft \ || fail "publish-manifest-crate must \`needs: release-manifest-assets-draft\`" job_needs publish-manifest-crate publish-crate \ || fail "publish-manifest-crate must \`needs: publish-crate\`" body="$(job_body publish-manifest-pypi)" -printf '%s\n' "$body" | grep -qE '^[[:space:]]+id-token:[[:space:]]*write' \ +matches_regex "$body" '^[[:space:]]+id-token:[[:space:]]*write' \ || fail "publish-manifest-pypi must grant \`id-token: write\` (Trusted Publishing OIDC)" job_needs publish-manifest-pypi release-manifest-assets-draft \ || fail "publish-manifest-pypi must \`needs: release-manifest-assets-draft\`" @@ -276,17 +288,17 @@ job_needs publish-pypi publish-crate \ check_crate_publish_job() { local jobname="$1" package="$2" artifact="$3" body pre_line oidc_line publish_line post_line body="$(job_body "$jobname")" - printf '%s\n' "$body" | grep -qE 'uses:[[:space:]]*actions/download-artifact' \ + matches_regex "$body" 'uses:[[:space:]]*actions/download-artifact' \ || fail "$jobname must download the attested $artifact artifact (byte-identity gate)" - printf '%s\n' "$body" | grep -qE "name:[[:space:]]*${artifact}" \ + matches_regex "$body" "name:[[:space:]]*${artifact}" \ || fail "$jobname must download the artifact named \`$artifact\` (the attested .crate)" - printf '%s\n' "$body" | grep -qE "cargo[[:space:]]+package[[:space:]]+-p[[:space:]]+${package}[[:space:]]+--locked" \ + matches_regex "$body" "cargo[[:space:]]+package[[:space:]]+-p[[:space:]]+${package}[[:space:]]+--locked" \ || fail "$jobname must re-run \`cargo package -p $package --locked\` so it can sha256-compare to the attested .crate (pre-publish gate)" - printf '%s\n' "$body" | grep -qE "cargo[[:space:]]+publish[[:space:]]+-p[[:space:]]+${package}[[:space:]]+--locked" \ + matches_regex "$body" "cargo[[:space:]]+publish[[:space:]]+-p[[:space:]]+${package}[[:space:]]+--locked" \ || fail "$jobname must run \`cargo publish -p $package --locked\`" - printf '%s\n' "$body" | grep -qE 'sha256sum' \ + matches_regex "$body" 'sha256sum' \ || fail "$jobname must sha256sum-compare the repackaged .crate vs the attested .crate before publishing" - printf '%s\n' "$body" | grep -qE "crates\.io/api/v1/crates/${package}|static\.crates\.io/crates/${package}" \ + matches_regex "$body" "crates\.io/api/v1/crates/${package}|static\.crates\.io/crates/${package}" \ || fail "$jobname must download the just-published .crate from crates.io after \`cargo publish\` (post-publish byte-identity proof; pre-publish alone cannot inspect cargo publish's internal packaging)" pre_line="$(require_job_line "$jobname" '^[[:space:]]+- name:[[:space:]]*Verify byte-identity vs the attested \.crate' 'a pre-publish byte-identity verification step')" @@ -308,8 +320,8 @@ job_needs publish-manifest-crate publish-crate \ manifest_pre_line="$(require_job_line publish-manifest-crate '^[[:space:]]+- name:[[:space:]]*Verify byte-identity vs the attested \.crate' 'a manifest pre-publish byte-identity verification step')" manifest_dry_line="$(require_job_line publish-manifest-crate '^[[:space:]]+- name:[[:space:]]*Validate manifest publish dry-run' 'a manifest publish dry-run step')" manifest_oidc_line="$(require_job_line publish-manifest-crate '^[[:space:]]+- name:[[:space:]]*Mint a short-lived crates\.io credential' 'a manifest OIDC credential mint step')" -printf '%s\n' "$(job_body publish-manifest-crate)" \ - | awk '/cargo[[:space:]]+publish/ && /ordvec-manifest/ && /--dry-run/ && /--locked/ { found = 1 } END { exit found ? 0 : 1 }' \ +awk '/cargo[[:space:]]+publish/ && /ordvec-manifest/ && /--dry-run/ && /--locked/ { found = 1 } END { exit found ? 0 : 1 }' \ + <<<"$(job_body publish-manifest-crate)" \ || fail "publish-manifest-crate must dry-run \`cargo publish -p ordvec-manifest --dry-run --locked\` after byte-identity and before OIDC" [ "$manifest_pre_line" -lt "$manifest_dry_line" ] \ || fail "publish-manifest-crate must dry-run publish AFTER byte-identity verification" @@ -317,33 +329,33 @@ printf '%s\n' "$(job_body publish-manifest-crate)" \ || fail "publish-manifest-crate must dry-run publish BEFORE minting the crates.io OIDC credential" pcd="$(job_body pypi-canonical-dist)" -printf '%s\n' "$pcd" | grep -qE 'release_pypi_canonical_dist\.py canonicalize' \ +matches_regex "$pcd" 'release_pypi_canonical_dist\.py canonicalize' \ || fail "pypi-canonical-dist must canonicalize Python artifacts before attestation/release upload" -printf '%s\n' "$pcd" | grep -qE 'name:[[:space:]]*pypi-canonical-dist' \ +matches_regex "$pcd" 'name:[[:space:]]*pypi-canonical-dist' \ || fail "pypi-canonical-dist must upload the canonical Python dist artifact" pcd_manifest="$(job_body pypi-manifest-canonical-dist)" -printf '%s\n' "$pcd_manifest" | grep -qE 'release_pypi_canonical_dist\.py canonicalize' \ +matches_regex "$pcd_manifest" 'release_pypi_canonical_dist\.py canonicalize' \ || fail "pypi-manifest-canonical-dist must canonicalize manifest Python artifacts before attestation/release upload" -printf '%s\n' "$pcd_manifest" | grep -qE -- '--project[[:space:]]+ordvec-manifest' \ +matches_regex "$pcd_manifest" '--project[[:space:]]+ordvec-manifest' \ || fail "pypi-manifest-canonical-dist must canonicalize the ordvec-manifest PyPI project" -printf '%s\n' "$pcd_manifest" | grep -qE 'name:[[:space:]]*pypi-manifest-canonical-dist' \ +matches_regex "$pcd_manifest" 'name:[[:space:]]*pypi-manifest-canonical-dist' \ || fail "pypi-manifest-canonical-dist must upload the canonical manifest Python dist artifact" ppb="$(job_body publish-pypi)" job_needs publish-pypi pypi-canonical-dist \ || fail "publish-pypi must \`needs: pypi-canonical-dist\` (publish/verify exactly the canonical files)" -printf '%s\n' "$ppb" | grep -qE 'name:[[:space:]]*pypi-canonical-dist' \ +matches_regex "$ppb" 'name:[[:space:]]*pypi-canonical-dist' \ || fail "publish-pypi must consume pypi-canonical-dist, not raw rebuilt wheel/sdist artifacts" -printf '%s\n' "$ppb" | grep -qE 'release_pypi_canonical_dist\.py verify' \ +matches_regex "$ppb" 'release_pypi_canonical_dist\.py verify' \ || fail "publish-pypi must verify PyPI-served wheel/sdist hashes against canonical dist" mppb="$(job_body publish-manifest-pypi)" job_needs publish-manifest-pypi pypi-manifest-canonical-dist \ || fail "publish-manifest-pypi must \`needs: pypi-manifest-canonical-dist\` (publish/verify exactly the canonical manifest files)" -printf '%s\n' "$mppb" | grep -qE 'name:[[:space:]]*pypi-manifest-canonical-dist' \ +matches_regex "$mppb" 'name:[[:space:]]*pypi-manifest-canonical-dist' \ || fail "publish-manifest-pypi must consume pypi-manifest-canonical-dist, not raw rebuilt wheel/sdist artifacts" -printf '%s\n' "$mppb" | grep -qE 'release_pypi_canonical_dist\.py verify' \ +matches_regex "$mppb" 'release_pypi_canonical_dist\.py verify' \ || fail "publish-manifest-pypi must verify PyPI-served manifest wheel/sdist hashes against canonical dist" -printf '%s\n' "$mppb" | grep -qE -- '--project[[:space:]]+ordvec-manifest' \ +matches_regex "$mppb" '--project[[:space:]]+ordvec-manifest' \ || fail "publish-manifest-pypi must verify the ordvec-manifest PyPI project" grep -q 'pypi.org/pypi' tests/release_pypi_canonical_dist.py \ || fail "release_pypi_canonical_dist.py must query PyPI for served file hashes" @@ -356,9 +368,9 @@ for dep in publish-crate publish-manifest-crate publish-pypi publish-manifest-py || fail "publish-github-release must \`needs: $dep\` (un-draft only after all registry publishes succeed)" done unp="$(job_body publish-github-release)" -printf '%s\n' "$unp" | grep -qE 'gh release edit.*--draft=false' \ +matches_regex "$unp" 'gh release edit.*--draft=false' \ || fail "publish-github-release must \`gh release edit --draft=false\` (this is the sole un-draft point)" -printf '%s\n' "$unp" | grep -qE "$github_repo_env_re" \ +matches_regex "$unp" "$github_repo_env_re" \ || fail "publish-github-release must set \`GH_REPO: \${{ github.repository }}\` (no checkout, so gh release edit needs explicit repo context)" echo "OK: signed-release invariants hold." From 3fa82e51296af5b9ff1133bc53ac893b98a92b59 Mon Sep 17 00:00:00 2001 From: Nelson Spence Date: Fri, 19 Jun 2026 13:08:28 -0500 Subject: [PATCH 2/3] Update metadata for Project-Navi transfer Signed-off-by: Nelson Spence --- .github/CODEOWNERS | 2 +- .github/ISSUE_TEMPLATE/config.yml | 2 +- .github/ISSUE_TEMPLATE/feature_request.md | 2 +- .github/actions/setup-intel-sde/action.yml | 2 +- .github/workflows/ci.yml | 2 +- .github/workflows/release.yml | 20 +++++++----- CHANGELOG.md | 6 ++-- Cargo.toml | 4 +-- GOVERNANCE.md | 2 +- README.md | 38 +++++++++++----------- RELEASING.md | 25 ++++++++------ SECURITY.md | 2 +- cliff.toml | 2 +- docs/compatibility-policy.md | 4 +-- ordvec-go/go.mod | 2 +- ordvec-manifest-python/Cargo.toml | 2 +- ordvec-manifest-python/pyproject.toml | 6 ++-- ordvec-manifest/Cargo.toml | 4 +-- ordvec-manifest/README.md | 2 +- ordvec-python/Cargo.toml | 2 +- ordvec-python/README.md | 8 ++--- ordvec-python/pyproject.toml | 6 ++-- ordvec-python/src/lib.rs | 2 +- tests/release_environment_settings.sh | 2 +- tests/release_publish_invariants.py | 20 ++++++------ 25 files changed, 88 insertions(+), 81 deletions(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index f5827228..e376fad5 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1,4 +1,4 @@ -# Code owners for Fieldnote-Echo/ordvec +# Code owners for Project-Navi/ordvec # # Listed owners are automatically requested for review on pull requests that # touch matching paths. For GitHub to honour an entry the owner must have write diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index e9972c15..66c03c5d 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -1,5 +1,5 @@ blank_issues_enabled: false contact_links: - name: Questions & research discussion - url: https://github.com/Fieldnote-Echo/ordvec/discussions + url: https://github.com/Project-Navi/ordvec/discussions about: For usage questions, methodology, and paper-related discussion. diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md index 8b6e8cb5..61176145 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -12,7 +12,7 @@ What are you trying to do that ordvec doesn't support today? What you'd like to see. **Alternatives considered** -(See [docs/ALTERNATIVES_CONSIDERED.md](https://github.com/Fieldnote-Echo/ordvec/blob/main/docs/ALTERNATIVES_CONSIDERED.md) — +(See [docs/ALTERNATIVES_CONSIDERED.md](https://github.com/Project-Navi/ordvec/blob/main/docs/ALTERNATIVES_CONSIDERED.md) — some directions were already trialled and dropped.) **Scope** diff --git a/.github/actions/setup-intel-sde/action.yml b/.github/actions/setup-intel-sde/action.yml index 72063fe7..ecc7dc37 100644 --- a/.github/actions/setup-intel-sde/action.yml +++ b/.github/actions/setup-intel-sde/action.yml @@ -61,7 +61,7 @@ runs: archive_cache_dir="${HOME}/.cache/ordvec-intel-sde" cached_archive="${archive_cache_dir}/${version}-${sha256}.tar.xz" download_url="${url_base}/${version}.tar.xz" - curl_user_agent="ordvec-ci-intel-sde/${version} (https://github.com/Fieldnote-Echo/ordvec)" + curl_user_agent="ordvec-ci-intel-sde/${version} (https://github.com/Project-Navi/ordvec)" archive_ready=false mark_unavailable() { diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 755f4ecb..59574460 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -280,7 +280,7 @@ jobs: --max-time 60 \ --retry 3 \ --retry-all-errors \ - --user-agent "ordvec-ci/${core_version} (https://github.com/Fieldnote-Echo/ordvec)" \ + --user-agent "ordvec-ci/${core_version} (https://github.com/Project-Navi/ordvec)" \ --output /dev/null \ --write-out "%{http_code}" \ "https://crates.io/api/v1/crates/ordvec/${core_version}" || true)" diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 16d07b00..ce0499fd 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -72,12 +72,14 @@ # ONE-TIME SETUP before the first tag on this workflow (each fails CLOSED until # done, so landing this first is safe): # * crates.io: ordvec and ordvec-manifest > Settings > Trusted Publishing > -# edit each GitHub publisher -> workflow = `release.yml` (env stays -# `crates-io`). -# * PyPI: ordvec > publishing > edit the GitHub publisher -> workflow = -# `release.yml` (env stays `pypi`). +# edit each GitHub publisher -> owner = `Project-Navi`, repository = +# `ordvec`, workflow = `release.yml` (env stays `crates-io`). +# * PyPI: ordvec > publishing > edit the GitHub publisher -> owner = +# `Project-Navi`, repository = `ordvec`, workflow = `release.yml` +# (env stays `pypi`). # * PyPI: ordvec-manifest > publishing > add or edit the GitHub publisher -# -> workflow = `release.yml` (env stays `pypi`). +# -> owner = `Project-Navi`, repository = `ordvec`, workflow = +# `release.yml` (env stays `pypi`). # * GitHub Environments `crates-io` AND `pypi`: # - keep "Required reviewers" (the human publish gate); # - set "Deployment branches and tags" to **Selected branches and tags** @@ -1090,7 +1092,7 @@ jobs: METADATA_URL="https://crates.io/api/v1/crates/ordvec/${VERSION}" API_URL="https://crates.io/api/v1/crates/ordvec/${VERSION}/download" STATIC_URL="https://static.crates.io/crates/ordvec/ordvec-${VERSION}.crate" - CRATES_IO_USER_AGENT="ordvec-release-verify/${VERSION} (https://github.com/Fieldnote-Echo/ordvec)" + CRATES_IO_USER_AGENT="ordvec-release-verify/${VERSION} (https://github.com/Project-Navi/ordvec)" EXISTING="${RUNNER_TEMP}/existing-ordvec.crate" METADATA="${RUNNER_TEMP}/existing-ordvec-metadata.json" METADATA_STATUS_FILE="${RUNNER_TEMP}/existing-ordvec-metadata-status.txt" @@ -1223,7 +1225,7 @@ jobs: # CDN propagation can take a few seconds after publish — retry briefly. API_URL="https://crates.io/api/v1/crates/ordvec/${VERSION}/download" STATIC_URL="https://static.crates.io/crates/ordvec/ordvec-${VERSION}.crate" - CRATES_IO_USER_AGENT="ordvec-release-verify/${VERSION} (https://github.com/Fieldnote-Echo/ordvec)" + CRATES_IO_USER_AGENT="ordvec-release-verify/${VERSION} (https://github.com/Project-Navi/ordvec)" PUBLISHED="${RUNNER_TEMP}/published.crate" for i in 1 2 3 4 5 6 7 8 9 10 11 12; do rm -f "$PUBLISHED" @@ -1467,7 +1469,7 @@ jobs: METADATA_URL="https://crates.io/api/v1/crates/ordvec-manifest/${VERSION}" API_URL="https://crates.io/api/v1/crates/ordvec-manifest/${VERSION}/download" STATIC_URL="https://static.crates.io/crates/ordvec-manifest/ordvec-manifest-${VERSION}.crate" - CRATES_IO_USER_AGENT="ordvec-release-verify/${VERSION} (https://github.com/Fieldnote-Echo/ordvec)" + CRATES_IO_USER_AGENT="ordvec-release-verify/${VERSION} (https://github.com/Project-Navi/ordvec)" EXISTING="${RUNNER_TEMP}/existing-ordvec-manifest.crate" METADATA="${RUNNER_TEMP}/existing-ordvec-manifest-metadata.json" METADATA_STATUS_FILE="${RUNNER_TEMP}/existing-ordvec-manifest-metadata-status.txt" @@ -1585,7 +1587,7 @@ jobs: A_SHA=$(sha256sum "$ATTESTED" | cut -d' ' -f1) API_URL="https://crates.io/api/v1/crates/ordvec-manifest/${VERSION}/download" STATIC_URL="https://static.crates.io/crates/ordvec-manifest/ordvec-manifest-${VERSION}.crate" - CRATES_IO_USER_AGENT="ordvec-release-verify/${VERSION} (https://github.com/Fieldnote-Echo/ordvec)" + CRATES_IO_USER_AGENT="ordvec-release-verify/${VERSION} (https://github.com/Project-Navi/ordvec)" PUBLISHED="${RUNNER_TEMP}/published.crate" for i in 1 2 3 4 5 6 7 8 9 10 11 12; do rm -f "$PUBLISHED" diff --git a/CHANGELOG.md b/CHANGELOG.md index 344fc25b..3a055246 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -328,6 +328,6 @@ system dependencies** — no BLAS, no `ndarray`, no `faer`. AVX-512 intrinsics this crate relies on were stabilized. - Dual-licensed under **MIT OR Apache-2.0**. -[0.4.0]: https://github.com/Fieldnote-Echo/ordvec/compare/v0.3.0...v0.4.0 -[0.2.0]: https://github.com/Fieldnote-Echo/ordvec/compare/v0.1.0...v0.2.0 -[0.1.0]: https://github.com/Fieldnote-Echo/ordvec/releases/tag/v0.1.0 +[0.4.0]: https://github.com/Project-Navi/ordvec/compare/v0.3.0...v0.4.0 +[0.2.0]: https://github.com/Project-Navi/ordvec/compare/v0.1.0...v0.2.0 +[0.1.0]: https://github.com/Project-Navi/ordvec/releases/tag/v0.1.0 diff --git a/Cargo.toml b/Cargo.toml index 9f38180d..21b46252 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,8 +5,8 @@ edition = "2021" rust-version = "1.89" # AVX-512 intrinsics stabilized in 1.89.0; also clears the 1.87 floor from u64::is_multiple_of description = "Training-free ordinal & sign quantization for vector retrieval" license = "MIT OR Apache-2.0" -repository = "https://github.com/Fieldnote-Echo/ordvec" -homepage = "https://github.com/Fieldnote-Echo/ordvec" +repository = "https://github.com/Project-Navi/ordvec" +homepage = "https://github.com/Project-Navi/ordvec" documentation = "https://docs.rs/ordvec" readme = "README.md" keywords = ["vector-search", "quantization", "nearest-neighbor", "ann", "simd"] diff --git a/GOVERNANCE.md b/GOVERNANCE.md index 3d984d08..21b1c081 100644 --- a/GOVERNANCE.md +++ b/GOVERNANCE.md @@ -8,7 +8,7 @@ describes how it is run. - **Maintainer.** ordvec is currently maintained by Nelson Spence ([@Fieldnote-Echo](https://github.com/Fieldnote-Echo)), the project lead and final decision-maker on technical direction, releases, and scope. -- **Code owners.** Listed in [`.github/CODEOWNERS`](https://github.com/Fieldnote-Echo/ordvec/blob/main/.github/CODEOWNERS); they +- **Code owners.** Listed in [`.github/CODEOWNERS`](https://github.com/Project-Navi/ordvec/blob/main/.github/CODEOWNERS); they review and approve changes. ## Decision-making diff --git a/README.md b/README.md index 9cd6237e..ae16887d 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,11 @@ # ordvec -[![CI](https://github.com/Fieldnote-Echo/ordvec/actions/workflows/ci.yml/badge.svg)](https://github.com/Fieldnote-Echo/ordvec/actions/workflows/ci.yml) +[![CI](https://github.com/Project-Navi/ordvec/actions/workflows/ci.yml/badge.svg)](https://github.com/Project-Navi/ordvec/actions/workflows/ci.yml) [![License: MIT OR Apache-2.0](https://img.shields.io/badge/License-MIT%20OR%20Apache--2.0-blue.svg)](#license) [![MSRV](https://img.shields.io/badge/MSRV-1.89-blue.svg)](#minimum-supported-rust-version) -[![OpenSSF Scorecard](https://api.scorecard.dev/projects/github.com/Fieldnote-Echo/ordvec/badge)](https://scorecard.dev/viewer/?uri=github.com/Fieldnote-Echo/ordvec) +[![OpenSSF Scorecard](https://api.scorecard.dev/projects/github.com/Project-Navi/ordvec/badge)](https://scorecard.dev/viewer/?uri=github.com/Project-Navi/ordvec) [![OpenSSF Best Practices](https://www.bestpractices.dev/projects/12977/badge)](https://www.bestpractices.dev/projects/12977) -[![codecov](https://codecov.io/gh/Fieldnote-Echo/ordvec/graph/badge.svg)](https://codecov.io/gh/Fieldnote-Echo/ordvec) +[![codecov](https://codecov.io/gh/Project-Navi/ordvec/graph/badge.svg)](https://codecov.io/gh/Project-Navi/ordvec) [![Crates.io](https://img.shields.io/crates/v/ordvec.svg)](https://crates.io/crates/ordvec) [![docs.rs](https://docs.rs/ordvec/badge.svg)](https://docs.rs/ordvec) @@ -44,7 +44,7 @@ keeps a near-flat per-query cost as the corpus grows, while exact brute-force (`flat`, identical math to FAISS `IndexFlatIP`) is O(n) — so the speedup *widens* with scale: -![ordvec speedup over exact search grows with corpus size](https://raw.githubusercontent.com/Fieldnote-Echo/ordvec/main/benchmarks/beir/figures/scaling_curve.png) +![ordvec speedup over exact search grows with corpus size](https://raw.githubusercontent.com/Project-Navi/ordvec/main/benchmarks/beir/figures/scaling_curve.png) - **~100× faster, single query.** At 171K docs, single-query latency: exact `flat` 56 ms vs ordvec `Sign→rq2` **0.53 ms** — and the gap grows with the @@ -184,7 +184,7 @@ Details in [`docs/RANK_MODES.md`](docs/RANK_MODES.md). ordvec = "0.5" # Or, to track unreleased `main`, use a git dependency instead: -# ordvec = { git = "https://github.com/Fieldnote-Echo/ordvec" } +# ordvec = { git = "https://github.com/Project-Navi/ordvec" } ``` ```rust @@ -262,7 +262,7 @@ pip install ordvec ``` Wheels target CPython 3.10+ (abi3); to build from source instead, see -[`ordvec-python/`](https://github.com/Fieldnote-Echo/ordvec/tree/main/ordvec-python). +[`ordvec-python/`](https://github.com/Project-Navi/ordvec/tree/main/ordvec-python). The runtime dependency floor is `numpy>=2.2`. ### Threading / concurrency @@ -287,16 +287,16 @@ candidate slices passed to `Search` until the call returns. - **Design deep-dive & reproducible benchmark tables:** [`docs/RANK_MODES.md`](docs/RANK_MODES.md) - **Design alternatives evaluated and cut:** - [`docs/ALTERNATIVES_CONSIDERED.md`](https://github.com/Fieldnote-Echo/ordvec/blob/main/docs/ALTERNATIVES_CONSIDERED.md) + [`docs/ALTERNATIVES_CONSIDERED.md`](https://github.com/Project-Navi/ordvec/blob/main/docs/ALTERNATIVES_CONSIDERED.md) - **Index-file trust model:** - [`docs/INDEX_PROVENANCE.md`](https://github.com/Fieldnote-Echo/ordvec/blob/main/docs/INDEX_PROVENANCE.md), - [`docs/determinism.md`](https://github.com/Fieldnote-Echo/ordvec/blob/main/docs/determinism.md), - [`THREAT_MODEL.md`](https://github.com/Fieldnote-Echo/ordvec/blob/main/THREAT_MODEL.md) + [`docs/INDEX_PROVENANCE.md`](https://github.com/Project-Navi/ordvec/blob/main/docs/INDEX_PROVENANCE.md), + [`docs/determinism.md`](https://github.com/Project-Navi/ordvec/blob/main/docs/determinism.md), + [`THREAT_MODEL.md`](https://github.com/Project-Navi/ordvec/blob/main/THREAT_MODEL.md) - **Manifest verifier, C ABI, and Go wrapper:** `ordvec-manifest` is versioned and published in lockstep with the core crate through its own package gate; use the GitHub checkout for `ordvec-ffi/`, `ordvec-go/`, and - [`docs/c-api.md`](https://github.com/Fieldnote-Echo/ordvec/blob/main/docs/c-api.md). + [`docs/c-api.md`](https://github.com/Project-Navi/ordvec/blob/main/docs/c-api.md). - **Bindings safety and ownership contract:** [`docs/bindings-safety.md`](docs/bindings-safety.md) - **Artifact and platform matrix:** @@ -318,7 +318,7 @@ candidate slices passed to `Search` until the call returns. ### BEIR retrieval (public datasets, reproducible) A fully reproducible harness over standard [BEIR](https://github.com/beir-cellar/beir) -datasets lives in [`benchmarks/beir/`](https://github.com/Fieldnote-Echo/ordvec/tree/main/benchmarks/beir). It embeds the corpus +datasets lives in [`benchmarks/beir/`](https://github.com/Project-Navi/ordvec/tree/main/benchmarks/beir). It embeds the corpus with **Harrier-Q8** (GGUF `Q8_0` via `llama-cpp-python`, CUDA), then measures ordvec's methods against two references **in a single Rust process** so the latency comparison is genuinely apples-to-apples — same machine, batch, and @@ -379,7 +379,7 @@ views (trec-covid, 171,332 docs, 1024-d): **1. Single query (batch = 1, 1 thread)** — latency-sensitive serving, where `flat` cannot amortize its memory traffic: -![single-query latency bars](https://raw.githubusercontent.com/Fieldnote-Echo/ordvec/main/benchmarks/beir/figures/bars_single_thread.png) +![single-query latency bars](https://raw.githubusercontent.com/Project-Navi/ordvec/main/benchmarks/beir/figures/bars_single_thread.png) `flat` 56 ms → ordvec `sign→rq2` **0.53 ms (≈106×)**, `bitmap→rq2` 0.62 ms (≈91×), `hnsw` 1.5 ms (37×). The scaling curve [above](#benchmark-at-a-glance) is this @@ -392,7 +392,7 @@ narrowing the gap: ordvec `sign→rq2`/`bitmap→rq2` stay ≈8–9.5× ahead. **3. Many cores (batch = 32, 32 threads)** — everything parallelizes and the field compresses; `hnsw` threads best: -![threaded throughput bars](https://raw.githubusercontent.com/Fieldnote-Echo/ordvec/main/benchmarks/beir/figures/bars_threaded.png) +![threaded throughput bars](https://raw.githubusercontent.com/Project-Navi/ordvec/main/benchmarks/beir/figures/bars_threaded.png) `hnsw` 4.8× vs `flat`, ordvec `bitmap→rq2` 3.7×, `rq2` 2.5×, `sign→rq2` 2.1×. This committed chart uses the default `sign-rq2` row, not the newer @@ -483,9 +483,9 @@ header metadata, row identity, named auxiliary sidecars, and attestation shape checks. It does not sign artifacts, manage keys, or decide deployment trust policy. No in-format crypto is shipped because it would add key management the library can't own. See -[`docs/PERSISTED_FORMAT.md`](https://github.com/Fieldnote-Echo/ordvec/blob/main/docs/PERSISTED_FORMAT.md), -[`docs/INDEX_PROVENANCE.md`](https://github.com/Fieldnote-Echo/ordvec/blob/main/docs/INDEX_PROVENANCE.md), -and [`THREAT_MODEL.md`](https://github.com/Fieldnote-Echo/ordvec/blob/main/THREAT_MODEL.md) +[`docs/PERSISTED_FORMAT.md`](https://github.com/Project-Navi/ordvec/blob/main/docs/PERSISTED_FORMAT.md), +[`docs/INDEX_PROVENANCE.md`](https://github.com/Project-Navi/ordvec/blob/main/docs/INDEX_PROVENANCE.md), +and [`THREAT_MODEL.md`](https://github.com/Project-Navi/ordvec/blob/main/THREAT_MODEL.md) in the full repository. ## Provenance @@ -524,13 +524,13 @@ Collaboration we're actively seeking: and reporting the numbers. If that's your area, see -[GOVERNANCE.md](https://github.com/Fieldnote-Echo/ordvec/blob/main/GOVERNANCE.md) +[GOVERNANCE.md](https://github.com/Project-Navi/ordvec/blob/main/GOVERNANCE.md) and open an issue or a discussion. ## Contributing Contributions to the code, the docs, and the paper are all welcome — see -[CONTRIBUTING.md](https://github.com/Fieldnote-Echo/ordvec/blob/main/CONTRIBUTING.md). +[CONTRIBUTING.md](https://github.com/Project-Navi/ordvec/blob/main/CONTRIBUTING.md). ## Minimum supported Rust version diff --git a/RELEASING.md b/RELEASING.md index 8a6864e3..3f868c8d 100644 --- a/RELEASING.md +++ b/RELEASING.md @@ -107,20 +107,25 @@ the GitHub Release. ### Trusted-publisher configuration (one-time, in the registries) The crates.io and PyPI Trusted Publisher records must point at this workflow -filename. Until a record is updated, the corresponding gated publish fails -**closed** at the OIDC exchange (no risk of a bad publish; just a failed run). +filename and GitHub repository identity. After the GitHub repo transfer, the +registry-side owner must be `Project-Navi` and the repository must be `ordvec`. +Until a record is updated, the corresponding gated publish fails **closed** at +the OIDC exchange (no risk of a bad publish; just a failed run). - **crates.io** → `ordvec` → Settings → Trusted Publishing → GitHub publisher: - `workflow = release.yml`, `environment = crates-io`. + `owner = Project-Navi`, `repository = ordvec`, `workflow = release.yml`, + `environment = crates-io`. - **crates.io** → `ordvec-manifest` → Settings → Trusted Publishing → GitHub - publisher: `workflow = release.yml`, `environment = crates-io`. If crates.io - requires an initial owner bootstrap before a new crate's Trusted Publisher can - be configured, do that explicit maintainer-approved bootstrap before tagging. + publisher: `owner = Project-Navi`, `repository = ordvec`, + `workflow = release.yml`, `environment = crates-io`. If crates.io requires an + initial owner bootstrap before a new crate's Trusted Publisher can be + configured, do that explicit maintainer-approved bootstrap before tagging. - **PyPI** → `ordvec` → Publishing → GitHub publisher: `workflow = release.yml`, - `environment = pypi`, project URL `https://pypi.org/p/ordvec`. + `owner = Project-Navi`, `repository = ordvec`, `environment = pypi`, project + URL `https://pypi.org/p/ordvec`. - **PyPI** → `ordvec-manifest` → Publishing → GitHub publisher: - `workflow = release.yml`, `environment = pypi`, project URL - `https://pypi.org/p/ordvec-manifest`. + `workflow = release.yml`, `owner = Project-Navi`, `repository = ordvec`, + `environment = pypi`, project URL `https://pypi.org/p/ordvec-manifest`. ### Tag and branch protection @@ -244,7 +249,7 @@ filename. Until a record is updated, the corresponding gated publish fails at `GET https://pypi.org/integrity/ordvec/X.Y.Z//provenance`); - the GitHub Release page (`.crate`, wheels, sdist, `*.sigstore.json`, `*.intoto.jsonl` all present); - - `gh attestation verify -R Fieldnote-Echo/ordvec` on a downloaded + - `gh attestation verify -R Project-Navi/ordvec` on a downloaded artifact; - compare the observed release assets against [`docs/artifact-platform-matrix.md`](docs/artifact-platform-matrix.md); diff --git a/SECURITY.md b/SECURITY.md index 166b0a36..07a86144 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -11,7 +11,7 @@ Please report security issues **privately** — do not open a public issue. Use GitHub's private vulnerability reporting: **Security → Report a vulnerability** -(). +(). We aim to acknowledge reports within a few business days. diff --git a/cliff.toml b/cliff.toml index cf3f9282..2a74452d 100644 --- a/cliff.toml +++ b/cliff.toml @@ -45,7 +45,7 @@ filter_unconventional = true split_commits = false # Turn "(#NN)" PR references into links in the public release notes. commit_preprocessors = [ - { pattern = '\(#([0-9]+)\)', replace = "([#${1}](https://github.com/Fieldnote-Echo/ordvec/pull/${1}))" }, + { pattern = '\(#([0-9]+)\)', replace = "([#${1}](https://github.com/Project-Navi/ordvec/pull/${1}))" }, ] # Conventional Commit type -> Keep a Changelog section. Housekeeping and merge # commits are dropped so the notes stay user-facing. diff --git a/docs/compatibility-policy.md b/docs/compatibility-policy.md index cd0d7009..4efede6f 100644 --- a/docs/compatibility-policy.md +++ b/docs/compatibility-policy.md @@ -143,7 +143,7 @@ Patch releases should keep valid files from the same minor series loadable. Loader hardening may reject malformed files, forged sizes, trailing bytes, bad dimensions, unsupported bit widths, or files outside documented capacity limits. This bucket tracks the format-compatibility requirements from -[#118](https://github.com/Fieldnote-Echo/ordvec/issues/118). +[#118](https://github.com/Project-Navi/ordvec/issues/118). Minor releases may introduce new format versions or new sidecar conventions. When they do, release notes must say whether older files remain readable and @@ -155,7 +155,7 @@ This is a primitive file-format promise. It does not define an application database lifecycle, the consuming store schema, cache invalidation policy, manifest trust policy, or migration framework for downstream systems. Deployment-side provenance guidance lives in -[`INDEX_PROVENANCE.md`](https://github.com/Fieldnote-Echo/ordvec/blob/main/docs/INDEX_PROVENANCE.md). +[`INDEX_PROVENANCE.md`](https://github.com/Project-Navi/ordvec/blob/main/docs/INDEX_PROVENANCE.md). ## MSRV and Build Features diff --git a/ordvec-go/go.mod b/ordvec-go/go.mod index 1903981c..6b1e3e52 100644 --- a/ordvec-go/go.mod +++ b/ordvec-go/go.mod @@ -1,4 +1,4 @@ -module github.com/Fieldnote-Echo/ordvec/ordvec-go +module github.com/Project-Navi/ordvec/ordvec-go go 1.22 diff --git a/ordvec-manifest-python/Cargo.toml b/ordvec-manifest-python/Cargo.toml index 89663be0..6b70394c 100644 --- a/ordvec-manifest-python/Cargo.toml +++ b/ordvec-manifest-python/Cargo.toml @@ -5,7 +5,7 @@ edition = "2021" rust-version = "1.89" description = "Python bindings for ordvec-manifest index provenance verification" license = "MIT OR Apache-2.0" -repository = "https://github.com/Fieldnote-Echo/ordvec" +repository = "https://github.com/Project-Navi/ordvec" # Ships to PyPI as `ordvec-manifest` via maturin, never to crates.io. publish = false diff --git a/ordvec-manifest-python/pyproject.toml b/ordvec-manifest-python/pyproject.toml index fbea95dc..090b4b75 100644 --- a/ordvec-manifest-python/pyproject.toml +++ b/ordvec-manifest-python/pyproject.toml @@ -39,9 +39,9 @@ classifiers = [ ] [project.urls] -Homepage = "https://github.com/Fieldnote-Echo/ordvec" -Repository = "https://github.com/Fieldnote-Echo/ordvec" -Issues = "https://github.com/Fieldnote-Echo/ordvec/issues" +Homepage = "https://github.com/Project-Navi/ordvec" +Repository = "https://github.com/Project-Navi/ordvec" +Issues = "https://github.com/Project-Navi/ordvec/issues" [tool.maturin] manifest-path = "Cargo.toml" diff --git a/ordvec-manifest/Cargo.toml b/ordvec-manifest/Cargo.toml index 26154d58..cfe9eb2a 100644 --- a/ordvec-manifest/Cargo.toml +++ b/ordvec-manifest/Cargo.toml @@ -5,8 +5,8 @@ edition = "2021" rust-version = "1.89" license = "MIT OR Apache-2.0" description = "Manifest verifier for ordvec index provenance and sidecar artifacts" -repository = "https://github.com/Fieldnote-Echo/ordvec" -homepage = "https://github.com/Fieldnote-Echo/ordvec" +repository = "https://github.com/Project-Navi/ordvec" +homepage = "https://github.com/Project-Navi/ordvec" documentation = "https://docs.rs/ordvec-manifest" readme = "README.md" keywords = ["vector-search", "manifest", "provenance", "verification", "quantization"] diff --git a/ordvec-manifest/README.md b/ordvec-manifest/README.md index 5a651073..6ed4f229 100644 --- a/ordvec-manifest/README.md +++ b/ordvec-manifest/README.md @@ -220,7 +220,7 @@ ordvec invariant while leaving the caller's `u64` document IDs as caller-owned sidecar bytes. Do not encode the ID sidecar as `RowIdentity::Jsonl`: v1 JSONL row identity is UUID-oriented (`id_kind = "uuid"`), and generic row-map ID formats are intentionally deferred to -[#145](https://github.com/Fieldnote-Echo/ordvec/issues/145). The reserved +[#145](https://github.com/Project-Navi/ordvec/issues/145). The reserved `row_identity.db` metadata block is rejected in v1 because it is not byte-bound or path-checked. diff --git a/ordvec-python/Cargo.toml b/ordvec-python/Cargo.toml index 2f1f56b4..44ab6768 100644 --- a/ordvec-python/Cargo.toml +++ b/ordvec-python/Cargo.toml @@ -5,7 +5,7 @@ edition = "2021" rust-version = "1.89" # inherits ordvec's AVX-512 MSRV floor description = "Python bindings for ordvec — training-free ordinal & sign vector quantization" license = "MIT OR Apache-2.0" -repository = "https://github.com/Fieldnote-Echo/ordvec" +repository = "https://github.com/Project-Navi/ordvec" # Ships to PyPI as `ordvec` via maturin — never to crates.io. publish = false diff --git a/ordvec-python/README.md b/ordvec-python/README.md index 25c4fb34..4d39595e 100644 --- a/ordvec-python/README.md +++ b/ordvec-python/README.md @@ -1,6 +1,6 @@ # ordvec (Python) -Python bindings for [`ordvec`](https://github.com/Fieldnote-Echo/ordvec) — a +Python bindings for [`ordvec`](https://github.com/Project-Navi/ordvec) — a training-free **ordinal & sign** vector-quantization library for compressed nearest-neighbour retrieval over high-dimensional embeddings. Pure-Rust core, zero system dependencies; SIMD-accelerated at runtime (AVX-512 / AVX2 / scalar). @@ -68,7 +68,7 @@ The Python binding releases the GIL while Rust searches, scores, and mutates indexes. NumPy arrays passed to those methods are read in place while the call is active; do not mutate them from another thread until the method returns. The cross-language ownership and lifetime contract is maintained in -[`docs/bindings-safety.md`](https://github.com/Fieldnote-Echo/ordvec/blob/v0.5.0/docs/bindings-safety.md) +[`docs/bindings-safety.md`](https://github.com/Project-Navi/ordvec/blob/v0.5.0/docs/bindings-safety.md) for this release line. ## Type stubs @@ -86,6 +86,6 @@ into this standalone package. turbovec the origin project. Dual-licensed under either of -[MIT](https://github.com/Fieldnote-Echo/ordvec/blob/main/LICENSE-MIT) or -[Apache-2.0](https://github.com/Fieldnote-Echo/ordvec/blob/main/LICENSE-APACHE-2.0) +[MIT](https://github.com/Project-Navi/ordvec/blob/main/LICENSE-MIT) or +[Apache-2.0](https://github.com/Project-Navi/ordvec/blob/main/LICENSE-APACHE-2.0) at your option. diff --git a/ordvec-python/pyproject.toml b/ordvec-python/pyproject.toml index b7e2eb91..91d42e0d 100644 --- a/ordvec-python/pyproject.toml +++ b/ordvec-python/pyproject.toml @@ -46,9 +46,9 @@ dependencies = [ ] [project.urls] -Homepage = "https://github.com/Fieldnote-Echo/ordvec" -Repository = "https://github.com/Fieldnote-Echo/ordvec" -Issues = "https://github.com/Fieldnote-Echo/ordvec/issues" +Homepage = "https://github.com/Project-Navi/ordvec" +Repository = "https://github.com/Project-Navi/ordvec" +Issues = "https://github.com/Project-Navi/ordvec/issues" Formalization = "https://github.com/Fieldnote-Echo/ordvec-formalization" [tool.maturin] diff --git a/ordvec-python/src/lib.rs b/ordvec-python/src/lib.rs index 72825fa1..d910f0c1 100644 --- a/ordvec-python/src/lib.rs +++ b/ordvec-python/src/lib.rs @@ -1,4 +1,4 @@ -//! Python bindings for [`ordvec`](https://github.com/Fieldnote-Echo/ordvec) — the +//! Python bindings for [`ordvec`](https://github.com/Project-Navi/ordvec) — the //! training-free ordinal & sign vector-quantization crate. //! //! Exposes the four retrieval types under the OrdVec ontology — `Rank`, diff --git a/tests/release_environment_settings.sh b/tests/release_environment_settings.sh index dc627487..2e0a4bbd 100755 --- a/tests/release_environment_settings.sh +++ b/tests/release_environment_settings.sh @@ -6,7 +6,7 @@ # gh token that can read repository environment settings. set -euo pipefail -REPO="${REPO:-Fieldnote-Echo/ordvec}" +REPO="${REPO:-Project-Navi/ordvec}" EXPECTED_REVIEWER="${EXPECTED_REVIEWER:-Fieldnote-Echo}" EXPECTED_POLICY="${EXPECTED_POLICY:-v[0-9]*.[0-9]*.[0-9]*}" ENVIRONMENTS=(crates-io pypi) diff --git a/tests/release_publish_invariants.py b/tests/release_publish_invariants.py index a7b420ad..0f015523 100644 --- a/tests/release_publish_invariants.py +++ b/tests/release_publish_invariants.py @@ -490,8 +490,8 @@ def check_registry_metadata_parity() -> None: expected_crates = { "Cargo.toml": { "license": "MIT OR Apache-2.0", - "repository": "https://github.com/Fieldnote-Echo/ordvec", - "homepage": "https://github.com/Fieldnote-Echo/ordvec", + "repository": "https://github.com/Project-Navi/ordvec", + "homepage": "https://github.com/Project-Navi/ordvec", "documentation": "https://docs.rs/ordvec", "readme": "README.md", "keywords": ["vector-search", "quantization", "nearest-neighbor", "ann", "simd"], @@ -499,8 +499,8 @@ def check_registry_metadata_parity() -> None: }, "ordvec-manifest/Cargo.toml": { "license": "MIT OR Apache-2.0", - "repository": "https://github.com/Fieldnote-Echo/ordvec", - "homepage": "https://github.com/Fieldnote-Echo/ordvec", + "repository": "https://github.com/Project-Navi/ordvec", + "homepage": "https://github.com/Project-Navi/ordvec", "documentation": "https://docs.rs/ordvec-manifest", "readme": "README.md", "keywords": ["vector-search", "manifest", "provenance", "verification", "quantization"], @@ -580,9 +580,9 @@ def check_python_package_metadata() -> None: fail(f"ordvec-python/pyproject.toml: missing classifier {classifier!r}") urls = mapping(project.get("urls"), "ordvec-python/pyproject.toml: project.urls") for key, expected in { - "Homepage": "https://github.com/Fieldnote-Echo/ordvec", - "Repository": "https://github.com/Fieldnote-Echo/ordvec", - "Issues": "https://github.com/Fieldnote-Echo/ordvec/issues", + "Homepage": "https://github.com/Project-Navi/ordvec", + "Repository": "https://github.com/Project-Navi/ordvec", + "Issues": "https://github.com/Project-Navi/ordvec/issues", "Formalization": "https://github.com/Fieldnote-Echo/ordvec-formalization", }.items(): if urls.get(key) != expected: @@ -641,9 +641,9 @@ def check_python_package_metadata() -> None: "ordvec-manifest-python/pyproject.toml: project.urls", ) for key, expected in { - "Homepage": "https://github.com/Fieldnote-Echo/ordvec", - "Repository": "https://github.com/Fieldnote-Echo/ordvec", - "Issues": "https://github.com/Fieldnote-Echo/ordvec/issues", + "Homepage": "https://github.com/Project-Navi/ordvec", + "Repository": "https://github.com/Project-Navi/ordvec", + "Issues": "https://github.com/Project-Navi/ordvec/issues", }.items(): if manifest_urls.get(key) != expected: fail( From bede72df4ccf3f30cb8db62565fc63ebc0fd4677 Mon Sep 17 00:00:00 2001 From: Nelson Spence Date: Fri, 19 Jun 2026 13:11:01 -0500 Subject: [PATCH 3/3] Address release invariant review nits Signed-off-by: Nelson Spence --- tests/release_signed_release_invariants.sh | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/tests/release_signed_release_invariants.sh b/tests/release_signed_release_invariants.sh index 8daefee3..1e9bb790 100755 --- a/tests/release_signed_release_invariants.sh +++ b/tests/release_signed_release_invariants.sh @@ -43,7 +43,7 @@ # It is intentionally a structural lint on release.yml (greps the YAML), not a # runtime exercise of the pipeline — that's the fork dry-run's job. set -euo pipefail -fail() { echo "::error::signed-release invariant violated: $*"; exit 1; } +fail() { echo "::error::signed-release invariant violated: $*" >&2; exit 1; } wf=".github/workflows/release.yml" [ -f "$wf" ] || fail "$wf: workflow file not found" @@ -55,7 +55,7 @@ matches_regex() { contains_literal() { local text="$1" needle="$2" - grep -q -- "$needle" <<<"$text" + grep -Fq -- "$needle" <<<"$text" } # Extract the body of a job (from ` :` to the next 2-space-indented job key). @@ -81,7 +81,10 @@ job_line() { local jobname="$1" pattern="$2" body line body="$(job_body "$jobname")" line="$(grep -nE -m 1 -- "$pattern" <<<"$body" || true)" - [ -n "$line" ] && printf '%s\n' "${line%%:*}" + if [ -n "$line" ]; then + printf '%s\n' "${line%%:*}" + fi + return 0 } require_job_line() {