Theoretical Enrichment & Tenability Evaluation Post-Processor¶
Overview¶
The Theoretical Enrichment and Tenability Evaluation post-processor (TheoreticalEnrichmentRunner) bridges the
domain-agnostic structural theory graph (\(\Phi_{\text{gen}}\)) produced by Phases 1–6 with formal structuralist
metatheory of science ([Stegmüller, 1976]; [Balzer et al., 1987]; [Schurz, 2024]).
It projects empirical observation clusters into theory-specific parametric spaces (\(\Phi_{\text{spec}}\)) and calculates continuous tenability optimization scores (\(TS_{\text{local}}\), \(TS_{\text{edge}}\)) using admissible blurs over uniform topological spaces.
Alignment with ADR 0015 & Code Implementation¶
This implementation realizes the architectural decisions laid out in ADR 0015, with several key engineering refinements:
| ADR 0015 Design Aspect | Code Implementation Detail | Rationale & Architectural Nuance |
|---|---|---|
| Pipeline Scope (ADR mentions Phases 1–4/1–5) | Executes after Phases 1–6 | The core pipeline evolved to include 6 distinct phases (Foundation, Entity Discovery, Global Relations, Consolidation, Entity Maturation, Argument Mining, Alignment Fusion, TheoryNet). Theoretical Enrichment operates on the complete graph. |
Input View (Phase4ArtifactsView) |
PhaseRunner[Phase4ArtifactsView] |
Phase4ArtifactsView filters THEORY_ATOM and THEORY_RELATION artifacts across the cumulative artifact collection, capturing atoms and relations created in Phase 4 and refined through Phases 5 and 6. |
| Theory Lenses (ADR used Neurology/Psychoanalysis examples) | 100% Domain-Agnostic Generic Extraction | ADR 0015 used neurology and psychoanalysis strictly as examples. In the codebase, all hardcoded domain projectors have been eliminated. Extraction is performed dynamically via LLMTheoryProjector (with measurement fast-path) and GenericTheoryProjector. |
| Empirical Clustering | Grouped by source_chunk_id |
In runner._identify_empirical_clusters(), observation units sharing a source chunk are partitioned into an EmpiricalCluster representing a localized Intended Application (\(I_k \subseteq M_{pp}\)). |
| Claimant Matching | TheoryRegistry.match_claimants() |
Multi-criteria heuristic: matches cluster measurements against required_dimensions, checks text for theory name/keywords, or inspects explicit claimant hypothesis IDs. Defaults to all theories if unassigned. |
| Core Law Evaluation | SafeFormulaEvaluator |
AST-based symbolic expression evaluator supporting abs, min, max, sqrt, and basic arithmetic without invoking Python's unsafe eval(). |
| Persistence | Dual Persistence | Emits TheoreticalEnrichmentArtifact envelopes and directly calls graph_store.upsert_argument_components and graph_store.upsert_relations to update Neo4j. |
Detailed Execution Workflow¶
flowchart TD
INPUT["Phase4ArtifactsView<br>(TheoryAtoms: Partition A & B, TheoryRelations)"]
subgraph Stage0 ["Stage 0: Dynamic Theory-Element Induction (Macro Scope)"]
CHECK{"Registry<br>Seeded?"}
CHECK -->|No & induce_theories = True| INDUCE["LLMTheoryInducer.induce_theories()<br>Discovers T = ⟨K, I⟩, M_pp, M_p, and Laws M"]
CHECK -->|Yes| REG["TheoryRegistry (Active Theories)"]
INDUCE --> REG
end
subgraph Step1 ["Step 1: Empirical Clustering (Intended Applications)"]
CLUSTER["_identify_empirical_clusters()<br>Group observations by source_chunk_id"]
MATCH["registry.match_claimants(cluster)<br>Identify claiming Theory-Elements"]
CLUSTER --> MATCH
end
subgraph Step2 ["Step 2: Domain-Specific Projection (Phi_spec)"]
PROJECT["projector.project() / aproject()<br>LLMTheoryProjector (Fast-path or LLM)<br>GenericTheoryProjector (Measurements + Text tokens)"]
MATCH --> PROJECT
end
subgraph Step3 ["Step 3: Local Tenability Calculation (TS_local)"]
EVAL_LAWS["SafeFormulaEvaluator<br>Compute deviation for each law L ∈ M"]
DELTA_STAR["delta* = max(deviations)<br>TS_local = max(0, 1 - delta* / max_blur)"]
PROJECT --> EVAL_LAWS --> DELTA_STAR
end
subgraph Step4 ["Step 4: Intertheoretical Consistency (TS_edge)"]
EDGE_SOLV["TenabilitySolver.solve_edge_tenability()<br>Evaluate CONSTRAINS, REDUCES_TO, COHERES_WITH edges"]
DELTA_STAR --> EDGE_SOLV
end
subgraph Step5 ["Step 5: Aggregation & Graph Persistence"]
AGG["TenabilitySolver.evaluate_theory_tenability()<br>TS_agg = w_local * TS_local + w_edge * avg(TS_edge)"]
ANOM["Flag anomalies if TS < threshold (default 0.5)"]
EMIT["Emit Telemetry Events & ArtifactEnvelopes"]
PERSIST[("Neo4j Upsert:<br>obs.parameters & obs.tenability<br>rel.tenability")]
EDGE_SOLV --> AGG --> ANOM --> EMIT --> PERSIST
end
INPUT --> CHECK
REG --> MATCH
Step-by-Step Breakdown¶
Stage 0: Dynamic Theory-Element Induction¶
If the TheoryRegistry is empty and config.induce_theories=True, LLMTheoryInducer inspects the macro graph:
- Partitions atoms into theoretical hypotheses (Partition \(A\)) and empirical observations (Partition \(B\)).
- Prompts the LLM with
TheoryInductionOutputschema to discover active theory elements without hardcoded domain knowledge. - Extracts required empirical dimensions (\(M_{pp}\)), latent parameters (\(M_p\)), and symbolic constraint expressions (\(M\)).
- Registers induced theories into
TheoryRegistry.
Step 1: Empirical Cluster Mapping¶
- Groups observation atoms sharing a
source_chunk_idinto anEmpiricalCluster. - Queries
TheoryRegistry.match_claimants(cluster)to determine which theories claim the cluster as an Intended Application. - Emits
TheoreticalClusterIdentifiedevent.
Step 2: Domain-Specific Projection (\(\Phi_{\text{spec}}\))¶
- Evaluates each cluster through each claiming theory's lens using
TheoryProjector:- Measurement Fast-Path: If structured measurements (
obs.measurements) directly match parameter names or required dimensions, parameters are computed instantly without LLM calls. - LLM Projection (
LLMTheoryProjector): Prompts the LLM to estimate latent parameters \(M_p \in [0.0, 1.0]\) based on cluster observations, measurements, and core laws. - Generic Fallback (
GenericTheoryProjector): Extracts parameter values by matching parameter tokens and numeric regex indicators in observation text.
- Measurement Fast-Path: If structured measurements (
- Emits
TheoreticalParametersProjectedevent.
Step 3: Local Tenability Calculation (\(TS_{\text{local}}\))¶
- Evaluates each core law \(L \in M\) against projected parameters via
SafeFormulaEvaluator. - Computes deviation \(\delta = \text{error} \ge 0.0\) and finds tightest blur \(\delta^* = \max (\delta_i)\).
- Derives local tenability score \(TS_{\text{local}} \in [0.0, 1.0]\).
- If \(TS_{\text{local}} < \text{threshold}\), records local anomaly.
Step 4: Global & Intertheoretical Consistency (\(TS_{\text{edge}}\))¶
- Iterates through cluster relation edges (
CONSTRAINS,REDUCES_TO,COHERES_WITH). - Compares projected parameters of source and target nodes to compute constraint blur \(\delta_C\).
- Calculates edge tenability \(TS_{\text{edge}} = \max (0.0, 1.0 - \delta_C)\).
- If \(TS_{\text{edge}} < \text{threshold}\), emits
TenabilityAnomalyDetected.
Step 5: Aggregation & Graph Persistence¶
- Aggregates local and edge tenability into
TenabilityResult:
- Emits
TenabilityEvaluationCompletedevent. - Updates
TheoreticalHypothesisnodes in the cluster with projected parameters and tenability attributes. - Updates relation edges with
tenability = ts_edge. - Emits
ArtifactEnvelope[TheoreticalEnrichmentArtifact]containing complete optimization metrics. - Persists enriched nodes and edges directly into Neo4j via
graph_store.upsert_argument_components()andgraph_store.upsert_relations().
Domain Telemetry Events¶
The post-processor emits four standardized events via pipeline.events:
from pipeline.post_processing.theoretical_enrichment.events import (
TheoreticalClusterIdentified,
TheoreticalParametersProjected,
TenabilityAnomalyDetected,
TenabilityEvaluationCompleted,
)
| Event | Attributes | Trigger |
|---|---|---|
TheoreticalClusterIdentified |
cluster_id, observation_count, claimant_theories |
When an empirical cluster is assembled and mapped to claiming theories. |
TheoreticalParametersProjected |
cluster_id, theory_id, parameters |
When theoretical parameters (\(M_p\)) are projected for a cluster. |
TenabilityAnomalyDetected |
element_id, theory_id, score, reason |
When a hypothesis or constraint edge fails the tenability threshold (\(TS < 0.5\)). |
TenabilityEvaluationCompleted |
theory_id, local_score, aggregated_score, is_tenable, tightest_blur |
When local and edge evaluations conclude for a Theory-Element. |
Configuration Reference¶
Post-processing behavior is configured via TheoreticalEnrichmentConfig under pipeline_config.theoretical_enrichment:
| Parameter | Type | Default | Description |
|---|---|---|---|
enabled |
bool |
True |
Whether to execute the theoretical enrichment post-processor. |
induce_theories |
bool |
True |
Whether to dynamically induce Theory-Elements via LLM if registry is unseeded. |
max_theories |
int |
5 |
Maximum number of Theory-Elements to induce from graph. |
tenability_threshold |
float |
0.5 |
Threshold below which nodes or edges are flagged as untenable anomalies. |
weight_local |
float |
0.5 |
Relative weight of local law adherence (\(TS_{\text{local}}\)) in aggregated score. |
weight_edge |
float |
0.5 |
Relative weight of constraint consistency (\(TS_{\text{edge}}\)) in aggregated score. |
decoding_strategy |
StructuredDecodingStrategy |
DIRECT |
LLM structured decoding strategy (DIRECT, JSON_SCHEMA, etc.). |
induction_prompts |
StructuredPromptBundle \| None |
None |
Custom prompt bundle overriding default theory induction prompt. |
projection_prompts |
StructuredPromptBundle \| None |
None |
Custom prompt bundle overriding default cluster projection prompt. |
Phase Contract¶
Inputs¶
Phase4ArtifactsView:theory_atoms: list[TheoryAtom]: Extracted observation units, empirical statements, and theoretical hypotheses.theory_relations: list[TheoryRelation]: Inter-component relations (EXPLAINS,SUPPORTS,CONSTRAINS,REDUCES_TO).
Outputs¶
ArtifactCollection: ContainsTheoreticalEnrichmentArtifactenvelopes with:cluster_id,theory_idprojected_parameters: dict[str, float]local_tenability: float,edge_tenabilities: dict[str, float],aggregated_tenability: floatadmissible_blur_delta: float,is_tenable: bool,anomalies: list[str]
- Graph Store (Neo4j):
- Updated
TheoreticalHypothesisnodes with.parametersand.tenability. - Updated
TheoryRelationedges with.tenability.
- Updated
Invariants¶
- Mathematical tenability scores \(TS \in [0.0, 1.0]\).
- Blur values \(\delta^* \ge 0.0\).
- Safe formula evaluation never invokes Python
eval(). - Missing parameters fallback gracefully to
0.5without failing pipeline execution.
Programmatic Usage Example¶
While dynamic LLM induction is enabled by default, custom theories can be registered explicitly:
from pipeline.post_processing.theoretical_enrichment import (
TheoreticalEnrichmentRunner,
TheoryElementDefinition,
TheoryLaw,
TheoryRegistry,
)
# 1. Define a core mathematical law (AST evaluated)
law = TheoryLaw(
law_id="conservation_law",
description="Conservation of energy parameter",
formula_expression="abs(KineticEnergy + PotentialEnergy - TotalEnergy)",
involved_parameters=["KineticEnergy", "PotentialEnergy", "TotalEnergy"],
)
# 2. Define the Theory-Element
mechanics = TheoryElementDefinition(
theory_id="classical_mechanics",
name="Classical Mechanics",
required_dimensions=["velocity", "height", "mass"],
parameter_names=["KineticEnergy", "PotentialEnergy", "TotalEnergy"],
laws=[law],
max_admissible_blur=1.0,
)
# 3. Initialize registry and runner
registry = TheoryRegistry(seed_theories=[mechanics])
runner = TheoreticalEnrichmentRunner(registry=registry)
Related Documentation¶
- Architecture Overview: Post-Processing System Architecture
- ADR: ADR 0015: Theoretical Enrichment & Tenability Evaluation
- Concepts: Epistemic Coherence: Tenability
- Reference: Theoretical Enrichment API Reference
- Code:
packages/episteme-pipeline/episteme_pipeline/post_processing/theoretical_enrichment/