Implements the 4-phase documentation reorganization master plan. - Consolidation: Merged brief/, handoff/, planning/, and decisions/ into briefs/, handoffs/, plans/, and adr/ respectively (101 ADRs relocated) - Root Cleanup: Relocated HANDOFF-gpt55-*.md and key top-level docs (runtime_contracts.md, etc.) to canonical folders. Added superseded alerts. - Indices & Navigation: Created docs/README.md navigation document, docs/sessions/README.md index, docs/adr/README.md index - Note: Also includes prior commit adding ADR-0200+ corpus hygiene governance (ADR-0225, dependency map, backfilled cross-references)
6.3 KiB
ADR-0046 — PropositionGraph as Forward Admissibility Constraint
Status: Accepted
Date: 2026-05-18
Author: Shay
Context
The 2026-05-17 assessment identified the load-bearing structural gap in CORE:
The
PropositionGraphis currently a post-hoc structural wrapper over what the field already produced, not a forward constraint on what the field should produce. That's the seam — not a disconnection, but a directionality that limits how much the graph can steer generation rather than describe it.
The intent_bridge.py path (ADR-0018) builds a PropositionGraph from the
classified intent and the ArticulationPlan, then grounds it with recalled
words from the generation result. The graph is built after generate() has
already walked the manifold. The graph describes; it does not constrain.
generate() already accepts an AdmissibilityRegion (ADR-0022 through
ADR-0026). The region is computed from the vocabulary's admissibility
structure. What was missing was the coupling: convert the graph's named
node versors into an AdmissibilityRegion before calling generate().
Decision
Add generate/graph_constraint.py with one public entry point:
build_graph_constraint(
graph: PropositionGraph,
vocab,
*,
top_k: int = 8,
) -> AdmissibilityRegion
The region's allowed_indices is the union of the CGA top-k neighbourhoods
of every named surface in the graph, computed by exact cga_inner scan.
This converts the graph from a descriptor into a forward constraint:
geometry (CGA versor neighbourhood)
→ structure (PropositionGraph nodes)
→ propagation (AdmissibilityRegion fed to generate())
The chat/runtime.py hot path can now call:
graph = _build_graph_from_intent(intent, articulation)
region = build_graph_constraint(graph, vocab, top_k=8)
result = generate(field_state, vocab, persona, region=region, ...)
This is a drop-in — generate() already accepts region. The only new
code is the CGA neighbourhood computation in graph_constraint.py.
Consequences
What changes
- The generation walk is now shaped by the proposition's geometric meaning before any tokens are produced, not after.
- The
admissibility_tracein everyGenerationResultnow carries the graph root IDs as the region label — full traceability from surface token back to the intent node that constrained it. - The system satisfies the three-pillar coupling end-to-end: Pillar 1 (geometry, CGA algebra) → Pillar 2 (structure, typed graph) → Pillar 3 (propagation, constrained field walk).
What does not change
generate()API is unchanged.- Empty or fully OOV graphs return an unconstrained region — existing fallback contract is preserved.
- All existing tests pass unchanged.
versor_condition < 1e-6invariant is unaffected (the region filters candidates; it does not alter the rotor construction or field update).
Scope limits (documented)
top_k=8is an operational default. Pack authors who need tighter or looser constraints can override at call time.- The coupling between
chat/runtime.pyandbuild_graph_constraintis available but the hot-path wire-up is a follow-up ADR (wire when the intent bridge returns a non-empty graph on the main path). - The CGA neighbourhood is computed over the full vocab on each call (O(|vocab| × |nodes|)). At current pack sizes this is negligible; a cached neighbourhood index is a future optimisation if packs grow.
Industry Demo Suite
Three standalone demos in evals/industry_demos/ make falsifiable claims
no transformer-LLM wrapper can reproduce:
| Demo | Claim |
|---|---|
demo_01_forward_constraint |
Graph constrains walk via CGA geometry before any tokens are produced |
demo_02_geometry_drives_identity |
Identity pack swap changes manifold geometry, not just output text |
demo_03_deterministic_audit |
Three independent runtimes produce byte-identical audit records (architectural determinism) |
Each demo exits 0 on pass, 1 on fail, and prints structured JSON evidence.
The exact-recall-at-scale claim that previously sat under this ADR as a
fourth demo has been moved out. An earlier draft attempted to
demonstrate it via random standard_normal vectors run through
unitize_versor; that construction is not valid as a versor in Cl(4,1)
(mixed signature cga_inner is not self-maximising for arbitrary
unitized random vectors), and the demo failed at N=10 000 in exactly
the way the construction predicts. The correct home for that claim
remains ADR-0045,
which measures recall on the actual vault path with properly
constructed versors at N ∈ {100, 1k, 10k, 100k} = 100 %. Putting the
same claim behind a weaker construction here would have been honest
neither to the geometry nor to the existing measurement.
Cross-References
- ADR-0018 —
intent_bridge.pyoriginally builds thePropositionGraphfrom the classified intent and articulation plan; this ADR converts that graph into a forward constraint. - ADR-0022 through
ADR-0026 —
AdmissibilityRegioncontract thatgenerate()already accepts; this ADR provides a new source for that region (the graph) without changing the contract. - ADR-0045 — load-bearing exact-recall measurement; the canonical source for that claim (see note above).
Verification
tests/test_graph_constraint.py — 8 tests, all green
evals/industry_demos/demo_01..03.py — 3 demos, each exits 0
Lanes (all green on this branch):
core test --suite smoke 67 passed
core test --suite cognition 121 passed
core test --suite runtime 19 passed
core test --suite algebra 132 passed
core test --suite teaching 17 passed
core test --suite packs 6 passed
core eval cognition intent_accuracy=100% versor_closure_rate=100%
The non-negotiable field invariant (versor_condition(F) < 1e-6) is
unaffected: this ADR only narrows the candidate index set fed to
generate() — it does not touch versor construction, sandwich
application, or field update.