The original adr-0046 commit was never run. Fixes: - generate/graph_constraint.py: import RegionSource (was the non-existent AdmissibilitySource). - tests/test_graph_constraint.py + demo_01: load pack "en_core_cognition_v1" (was "en", which is not a pack ID). - demo_03: read JsonlBufferSink.lines as a list attribute, not a method call. - demo_04 (exact_recall_scale): DROPPED. The construction used raw standard_normal vectors through unitize_versor and asserted cga_inner self-similarity is the population max. Cl(4,1) has 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 exact-recall claim's correct home is ADR-0045 (real vault path, properly constructed versors, N up to 100k = 100%). Doc/index updates: - ADR-0046 trimmed to three demos, with an explicit note on the dropped demo's geometric error and the cross-reference to ADR-0045. - ADR-0046 verification block updated with measured lane numbers (smoke 67 / cognition 121 / runtime 19 / algebra 132 / teaching 17 / packs 6; core eval cognition unchanged). - ADR-0046 cross-references ADR-0018 (intent_bridge source of the graph) and ADR-0022→ADR-0026 (AdmissibilityRegion contract). - docs/decisions/README.md: ADR-0046 added to the index and to a new "Pillar 1 → 2 → 3 coupling" section linking the graph constraint to the existing forward-semantic-control chain. - evals/industry_demos/__init__.py: invocation list trimmed to the three real entry points; removed the aspirational "core demo …" subcommands that were never wired. Verification on this branch: tests/test_graph_constraint.py 8 passed evals/industry_demos/demo_01..03 exit 0 each 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 100%, versor_closure 100%
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.