Three items from the post-assessment stabilization slice:
1. field/state.py: restore frozen=True, slots=True
slots=True closes __dict__ on FieldState instances, preventing
incidental attribute injection that frozen=True alone does not block.
The holonomy field works cleanly with slots because ndarray | None
is a valid slotted field type in Python 3.12.
2. README.md: correct vocab/ layer description
Was: 'Word-to-versor manifold, edge rotors'
Now: 'Surface-token manifold points; indexed access for algebraic
transition construction'
Edge rotors are constructed by algebra/, not stored in vocab/.
This exact confusion caused vocab.edge_rotor() drift in earlier work.
3. AGENTS.md: add language-pack checksum rule
Manifest checksums MUST be computed by reading back the bytes
written to disk (Path(f).read_bytes()), never from in-memory strings
before serialization. Unicode-escaped JSON on disk != Python str.
Three surgical import changes. No behavior change. No new semantics.
Backend decides Rust vs pure-Python transparently.
- field/propagate.py: versor_apply <- algebra.backend
- vocab/manifold.py: cga_inner <- algebra.backend
- vault/store.py: recall loop replaced with vault_recall() from
algebra.backend; public result shape preserved ({versor, score,
metadata, index}). null_project stays on algebra.cga (not the
recall hot path). store() and reproject() unchanged.
Rust path for vault_recall uses Rayon parallel scan and releases
the GIL. Python fallback is sequential and behaviorally identical.
No batching introduced; that is Commit 3+.
- field/state.py: FieldState is now frozen+slotted; constructor copies and
enforces float32 shape (32,); advance() updated to pass raw arrays.
np.ndarray inside frozen dataclass is ref-frozen — copy() at construction
is the explicit contract boundary.
- generate/result.py: NEW — GenerationResult frozen dataclass carrying
tokens + final_state. Async variant yields tokens and exposes final_state
on completion.
- generate/stream.py: generate() now returns GenerationResult, not list[str].
vocab.edge_rotor() call replaced with:
A = vocab.get_versor_at(current.node)
B = vocab.get_versor_at(word_idx)
V = word_transition_rotor(A, B)
agenerate() updated to yield tokens and surface final_state.
- vocab/manifold.py: added get_versor_at(idx) and get_word_at(idx) indexed
accessors. VocabManifold stores points; algebra constructs operators.
normalize_to_versor() call-site in docstring clarified: callers must call
unitize_versor() (algebra construction primitive) before add(), not
normalize_to_versor() directly.
- algebra/versor.py: unitize_versor() added as the explicit construction-time
primitive. normalize_to_versor() kept but marked internal/gate-only.
Distinction encoded in docstrings and __all__.
- persona/motor.py + ingest/gate.py: SessionContext.respond() is not yet in
the repo as a separate file; gate.py docstring updated to reflect the
three-tier normalization doctrine:
unitize_versor() — algebra construction only
inject() — gate, once per raw input
normalization — forbidden in propagate/generate/vault recall