- Add docs/decisions/README.md: ADR format guide and index - Add docs/decisions/ADR-0001-vocab-layer-invariants.md - Add docs/decisions/ADR-0002-ingest-layer-design.md - Add docs/decisions/ADR-0003-coordinate-system-dissolution.md - Add docs/decisions/ADR-0004-rotor-as-operator-not-property.md - Add docs/decisions/SESSION-2026-05-12.md: full timestamped session log
2.8 KiB
ADR-0004: Rotor as Operator, Not Vocabulary Property
Date: 2026-05-12
Status: Accepted
Commit: bd423e4
Implements: ADR-0003
Context
VocabManifold in the initial core implementation included an edge_rotor()
method that computed the rotor between two stored word-versors:
def edge_rotor(self, from_idx: int, to_idx: int) -> np.ndarray:
A = self._versors[from_idx]
B = self._versors[to_idx]
R = geometric_product(B, reverse(A))
R[0] += 1.0
return normalize_to_versor(R)
This method is mathematically correct but architecturally misplaced. Storing it
on VocabManifold means the vocabulary is carrying operator construction logic —
implying that the relationship between two words is a property of the vocabulary
rather than a property of a transformation being applied in the field.
Decision
- Remove
edge_rotor()fromVocabManifold. - Create
algebra/rotor.pywithword_transition_rotor(A, B)as a free function. - Export
word_transition_rotorfromalgebra/__init__.py.
VocabManifold contract is now strictly: store word-versor pairs, support
relational lookup by CGA inner product. Nothing else.
Rationale
A rotor between two words is not a property of those words in isolation — it is a description of a transformation being applied at a moment in the field. Storing it on the vocabulary conflates the map (what words exist and where they are) with the territory (what operations are being performed on the field state during generation or propagation).
Serves Field-State: operators live in algebra/; relational structure
lives in field/; the vocabulary is a lookup structure, not an operator store.
Serves Dual-Correction: the forward operator (field propagation) and its
corrective counterpart (rotor application / coherence restoration) should both
originate in algebra/, not be scattered across layers that don’t own them.
Consequences
- Cleaner dependency graph:
vocab/now imports fromalgebra/for algebraic primitives only (grade-norm check). It never constructs operators. - Clear callsite semantics:
algebra.word_transition_rotor(A, B)at a callsite infield/orgenerate/is self-documenting: an operator is being constructed here, by the layer that owns operators. - Forbidden: Any method on
VocabManifoldthat constructs a rotor, versor product, or transformation. Vocabulary is read-only geometry.
Alternatives Considered
- Keep
edge_rotor()as a convenience method with a deprecation warning: Rejected. Convenience methods that violate layer contracts tend to be the ones that get used. Remove cleanly. - Move to a separate
VocabOpsclass invocab/: Rejected. The operators don’t belong invocab/regardless of what class they live in. The layer boundary is the constraint, not the class boundary.