packed_spatial_index 0.26.0

Packed static spatial index (Hilbert R-tree) for 2D/3D AABBs — SIMD range, kNN, raycast, and spatial-join queries, with zero-copy and streaming serialization.
Documentation
# Agent guide

Operational notes for an AI agent (Codex/Claude) working in this repo.

## Commit conventions

- Subjects use **domain prefixes**, not conventional `feat:`/`fix:`. Pick the
  prefix from the [changelog taxonomy]RELEASING-AGENT.md#changelog-taxonomy
  (e.g. `search:`, `knn:`, `raycast:`, `server:`, `perf:`, `builder2d:`,
  `docs:`, `bench:`, `lint:`). The prefix decides the changelog group.
- Put benchmark tables and perf numbers in the commit **body**, never the
  subject — only the subject reaches release notes.
- Measure before committing a perf change: A/B with `git stash` on a quiet
  machine, keep it only on a stable win, revert dead ends.

## Workflow

- Work directly on `main`. This is a single-maintainer repo; do not open a
  feature branch unless a change is speculative or you were asked to.
- Commit each task as soon as it is done — one task, one commit, with the
  matching domain prefix. Do not batch unrelated changes across tasks and then
  split them afterwards (that is double work, and the prefix grouping gets
  muddy). Run `cargo fmt --check` + `cargo clippy` + `cargo test` for the
  touched crate(s) before committing; the `geo/` companion crate is formatted
  separately (`cargo fmt --all --manifest-path geo/Cargo.toml`).
- Never bump the version or touch `Cargo.lock` outside a `release:` commit.
  Feature/fix commits carry code only; a user-facing note may go under that
  crate's `## [Unreleased]`. The version bump (and the semver decision: minor
  for new public API, patch for a fix, major for a breaking change) happens in
  the release commit alone — see [`RELEASING-AGENT.md`]RELEASING-AGENT.md.
- Push only when the maintainer asks, or when preparing a release.

## Architecture discipline

The crate shares internal kernels (`TreeAccess` + `range` / `raycast` /
`neighbors::best_first`) so a traversal change lands in one place instead of
drifting across the owned / view / SIMD / f32 × 2D / 3D frontends. That is the
main maintenance win — reach for the shared kernel for cross-cutting algorithm
changes. But it cuts both ways, so:

- **Keep genuinely-different cases local.** Owned indexes iterate `&entries[..]`
  slices (LLVM autovectorizes the per-node test); byte views read per element.
  Do **not** route an owned hot path through the generic per-element kernel — it
  cost owned `visit` ~50–75% once before it was reverted to the local slice loop.
  A shared kernel that needs many special cases for ordinary behavior is a sign
  to stop sharing.
- **Measure owned paths specifically** when touching a shared kernel: the generic
  `TreeAccess` loop is free for views but not for slice-backed owned loops. Tests
  passing is **not** proof of no regression — A/B the hot path vs the prior commit.
- **File Boundary Rule:** extract for a separate reason to change or to remove
  real duplication; do not extract if a normal fix then has to touch four files
  instead of one. A 1000-line file can be fine.

## Releases

Releases are prepared **by hand** (no generator tool). The agent prepares the
version bump and changelog and shows the diff — the one stop in a release. After
the maintainer OKs the wording it carries on without asking again: commit, push,
wait for CI, start the publish workflow, and confirm the version reached
crates.io. The maintainer alone approves the `release` GitHub environment that
publishes, and no amount of asking brings that button forward.

Full step list and the changelog taxonomy: **[`RELEASING-AGENT.md`](RELEASING-AGENT.md)**.
The human-facing contract: [`RELEASING.md`](RELEASING.md).