audit: packed-versioned-bundle cycle close

Cycle-close tidy for the packed-versioned-index-bundle cycle (issues #3 + #4,
both shipped and closed). Architect drift review over d83b10f..HEAD against the
spec + glossary (no paths.design_ledger / project CLAUDE.md configured, so spec
+ glossary are the only references; the regression gate is empty —
commands.regression: [] — so architect was the sole cycle-close gate).

Architect verdict: drift_found, minor — no spec contract violated. Format,
header schema, on-disk layout, and the load_packed_or_store validation order
(n_rows → model → corpus hash) match the spec exactly; both #3 and #4
acceptance criteria are test-evidenced; no glossary term drift; the
IndexStatus / degraded orthogonality is honored.

Resolutions:
- [medium] Missing positional-alignment test. The spec § Testing strategy
  promised a "row i == store.get(entries[i].text)" guard, and it had not
  shipped — the load-bearing invariant that licenses dropping the per-row
  SHA256 key (row i = corpus entry i) was unprotected. FIXED: added
  tests/pack_cli_tests.rs::cli_packed_rows_are_in_corpus_order, which packs a
  store with order-revealing orthogonal vectors via the CLI, reads the packed
  file back, and asserts the rows follow corpus-load order (swapped order
  fails the test).
- [low] Stale IndexStatus doc comments still said "structurally valid/invalid"
  though the variants now also carry semantic model/corpus mismatch. FIXED:
  refreshed the Ok / Mismatch comments in src/model.rs.
- [low] The `index --pack` writer logic lives inline in main rather than in the
  packed module, leaving its header construction outside library test reach.
  CARRY-ON: filed as backlog issue #5 (idea/debt) — behaviour is correct and
  CLI-test-covered; the refactor is a self-contained testability improvement,
  not forced into this tidy.
- [note] No design ledger / project CLAUDE.md configured; drift is measurable
  only against spec + glossary. Recorded as an infrastructure observation.

Regression: no scripts configured (no-op). Full suite green (68 tests, +1 for
the alignment guard). cycle packed-versioned-bundle tidy (drift-clean).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-01 01:06:05 +02:00
parent 79de4e1404
commit 27e6a67388
2 changed files with 36 additions and 2 deletions
+2 -2
View File
@@ -82,10 +82,10 @@ pub enum Mode { Lexical, Hybrid }
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Default)]
pub enum IndexStatus {
Ok, // packed file present and structurally valid
Ok, // packed file present and matches the corpus + model
#[default]
Absent, // no usable index — intentional lexical-only
Mismatch(String), // packed file present but structurally invalid; reason carried
Mismatch(String), // packed file present but structurally or semantically invalid; reason carried
}
impl std::fmt::Display for IndexStatus {
+34
View File
@@ -120,3 +120,37 @@ fn cli_packed_file_loads_back_as_ok_index() {
assert!(vector.is_some(), "packed file written by the CLI must yield an index");
assert_eq!(status, IndexStatus::Ok);
}
/// Positional alignment (spec § Testing strategy): the packed matrix stores one
/// row per corpus entry in corpus-load order, so row `i` is the embedding of
/// `entries[i].text`. The two entries carry order-revealing orthogonal vectors;
/// if packing did not preserve corpus order the rows would come back swapped.
#[test]
fn cli_packed_rows_are_in_corpus_order() {
use alpha_id::packed::read_packed;
let dir = tempfile::tempdir().unwrap();
let corpus = dir.path().join("corpus.txt");
// alpha is line 1 (corpus entry 0), beta is line 2 (corpus entry 1).
std::fs::write(&corpus, format!("{}\n{}\n", corpus_line("alpha"), corpus_line("beta"))).unwrap();
let index_dir = dir.path().join("index");
std::fs::create_dir_all(&index_dir).unwrap();
let store = alpha_id::embed::EmbeddingStore::open(index_dir.to_str().unwrap(), "test/model").unwrap();
store.put("alpha", &[1.0f32, 0.0]).unwrap();
store.put("beta", &[0.0f32, 1.0]).unwrap();
let config = write_config(dir.path(), &corpus, &index_dir);
let out = Command::new(env!("CARGO_BIN_EXE_alpha-id"))
.args(["--config", config.to_str().unwrap(), "index", "--pack"])
.output()
.unwrap();
assert!(out.status.success(), "stderr: {}", String::from_utf8_lossy(&out.stderr));
let (_header, rows) = read_packed(&index_dir.join("packed.test_model.bin")).unwrap();
// Row i is the embedding of corpus entry i: alpha→[1,0] at row 0, beta→[0,1] at row 1.
assert_eq!(
rows,
vec![vec![1.0f32, 0.0], vec![0.0f32, 1.0]],
"packed rows must follow corpus-load order"
);
}