Lithoglyph as a self-normalizing database: automatic functional dependency discovery, type-encoded normal forms, and proof-carrying schema evolution.
A self-normalizing database automatically:
-
Discovers functional dependencies from data patterns
-
Encodes dependencies in the type system (via GQL-dt)
-
Proves schema satisfies target normal form
-
Proposes refactorings with equivalence proofs
-
Narrates every decision in human/agent-readable form
-
Applies transformations with full reversibility
This is not a fantasy. The pieces exist:
-
FD discovery algorithms (TANE, DFD, FDHits) are mature
-
GQL-dt provides dependent types for encoding constraints
-
Lithoglyph’s journal guarantees reversibility
-
Lithoglyph’s narrative philosophy demands explainability
| Algorithm | Approach | Source |
|---|---|---|
TANE |
Lattice traversal, partition refinement |
Huhtala et al., 1999 |
DFD |
Depth-first lattice search, sample-based |
Abedjan et al., 2014 |
FDHits |
Hitting set enumeration, hybrid validation |
ACM SIGMOD 2024 |
AutoNormalize |
Python library using DFD |
Alteryx, open source |
Flach & Savnik (1999) established ML approaches:
-
Top-down: Generate hypotheses, test against data
-
Bottom-up: Inspect data, infer what dependencies hold
Modern approaches use neural networks for approximate FD discovery.
┌─────────────────────────────────────────────────────────────┐
│ Form.Normalizer (new component) │
├─────────────────────────────────────────────────────────────┤
│ OBSERVE: Ingest data, discover FDs via DFD/TANE/ML │
├─────────────────────────────────────────────────────────────┤
│ ENCODE: Express discovered FDs as dependent types │
├─────────────────────────────────────────────────────────────┤
│ PROVE: Verify schema satisfies target normal form │
├─────────────────────────────────────────────────────────────┤
│ PROPOSE: Generate schema refactoring + equivalence proof │
├─────────────────────────────────────────────────────────────┤
│ NARRATE: Explain in human/agent-readable form │
├─────────────────────────────────────────────────────────────┤
│ APPLY: Transform via Form.Model with full reversibility │
└─────────────────────────────────────────────────────────────┘ ┌─────────────────────┐
│ Form.Normalizer │
│ (FD discovery + │
│ type encoding) │
└──────────┬──────────┘
│
┌───────────────────────┼───────────────────────┐
▼ ▼ ▼
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ GQL-dt │ │ GQL │ │ Form.Model │
│ (Lean 4) │ │ (Factor) │ │ (Forth) │
│ Type-check │ │ Runtime │ │ Apply │
│ proofs │ │ queries │ │ transforms │
└─────────────┘ └─────────────┘ └─────────────┘
│ │ │
└───────────────────────┼───────────────────────┘
▼
┌─────────────────────┐
│ Form.Blocks │
│ (Journal + │
│ Reversibility) │
└─────────────────────┘A functional dependency X → Y states that X uniquely determines Y.
/-- A functional dependency between attribute sets -/
structure FunDep (Schema : Type) where
determinant : List Attribute
dependent : List Attribute
/-- Proof that the FD holds in a given relation -/
structure FDHolds (fd : FunDep Schema) (r : Relation Schema) : Prop where
proof : ∀ t1 t2 : Tuple Schema,
(∀ a ∈ fd.determinant, t1.get a = t2.get a) →
(∀ a ∈ fd.dependent, t1.get a = t2.get a)/-- First Normal Form: All attributes are atomic -/
def FirstNormalForm (S : Schema) : Prop :=
∀ attr ∈ S.attributes, attr.type.isAtomic
/-- Second Normal Form: 1NF + no partial dependencies on candidate keys -/
def SecondNormalForm (S : Schema) (fds : List (FunDep S)) : Prop :=
FirstNormalForm S ∧
∀ fd ∈ fds,
¬fd.determinant.isProperSubsetOf S.candidateKey →
fd.dependent ⊆ S.primeAttributes
/-- Third Normal Form: 2NF + no transitive dependencies -/
def ThirdNormalForm (S : Schema) (fds : List (FunDep S)) : Prop :=
SecondNormalForm S fds ∧
∀ fd ∈ fds,
fd.determinant.isSuperkey ∨ fd.dependent ⊆ S.primeAttributes
/-- Boyce-Codd Normal Form: Every determinant is a superkey -/
def BCNF (S : Schema) (fds : List (FunDep S)) : Prop :=
∀ fd ∈ fds, fd.determinant.isSuperkey/-- A normalization step with proof of equivalence -/
structure NormalizationStep where
source : Schema
target : Schema
transform : Relation source → Relation target
inverse : Relation target → Relation source
equivalence : ∀ r, inverse (transform r) = r
/-- Apply normalization with proof preservation -/
def normalize (S : Schema) (fds : List (FunDep S))
(target : NormalForm) : NormalizationStep :=
-- Returns transformation + proof that:
-- 1. Target schema is in requested normal form
-- 2. Transformation is lossless (has inverse)
-- 3. All FDs are preserved or documented as lost! GQL extension for FD discovery
DISCOVER DEPENDENCIES
FROM collection
SAMPLE 10000 ! Sample size for large collections
CONFIDENCE 0.95 ! Statistical confidence threshold
ALGORITHM dfd ! dfd | tane | fdhits | ml
! Returns discovered FDs with confidence scores
! Result is a narrative artefact, journaledDiscovered FDs are encoded as GQL-dt types:
Discovered: {employee_id} → {name, department, salary}
Confidence: 0.998
Encoded as:
FunDep EmployeeSchema
{ determinant := ["employee_id"]
, dependent := ["name", "department", "salary"]
, confidence := 0.998
, discovered_at := JournalEntry#4521
, sample_size := 10000
}/-- Attempt to prove schema is in target normal form -/
theorem schema_is_3NF : ThirdNormalForm EmployeeSchema discoveredFDs := by
-- Automated proof search
-- If fails, generates counterexample + suggestionIf schema violates target normal form:
NORMALIZATION PROPOSAL
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Current Schema: employees
Attributes: employee_id, name, department, dept_location, salary
Violation: Transitive dependency
employee_id → department → dept_location
Proposed Split:
1. employees(employee_id, name, department, salary)
2. departments(department, dept_location)
Proof of Equivalence: [Link to Lean proof]
- Lossless join guaranteed
- All FDs preserved in decomposition
Reversibility:
- Forward: SPLIT employees INTO employees, departments ON department
- Inverse: JOIN employees, departments ON department
- Journal entry: Will create #4522
Narrative:
"The employees collection contains a transitive dependency where
department determines dept_location. This means if a department's
location changes, we'd need to update every employee record in that
department (update anomaly). Splitting into separate collections
ensures each fact is stored exactly once."
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━! Apply with explicit consent + journaling
APPLY NORMALIZATION proposal-4521
WITH ROLLBACK POINT "pre-3nf-normalization"
NARRATIVE "Normalizing to 3NF per proposal #4521"
! Journal records:
! - The transformation applied
! - The inverse transformation
! - The equivalence proof reference
! - The narrative explanationEvery normalization decision produces a narrative:
FUNCTIONAL DEPENDENCY DISCOVERY
Collection: employees
Date: 2026-01-11
Sample: 10,000 of 1,234,567 records
Discovered Dependencies:
1. {employee_id} → {name, department, salary} [conf: 0.999]
"Each employee ID uniquely identifies their name, department, and salary."
2. {department} → {dept_location} [conf: 0.997]
"Each department has exactly one location."
3. {project_id, employee_id} → {hours_worked} [conf: 0.995]
"Each employee's work on a project has unique hours recorded."
Approximate Dependencies (may indicate data quality issues):
4. {name} → {employee_id} [conf: 0.89]
"Names are mostly unique but 11% have duplicates."
Recommendation: Investigate—may be data entry issues.
Provenance: Journal entries #4515-#4518SCHEMA EVOLUTION: NORMALIZATION TO 3NF
Date: 2026-01-11
Requested by: Agent/system-optimizer
Approved by: human/jane-dba
BEFORE:
employees(employee_id, name, department, dept_location, salary)
Normal form: 2NF (violates 3NF due to transitive dependency)
TRANSFORMATION:
Split on transitive dependency: department → dept_location
AFTER:
employees(employee_id, name, department, salary) [3NF ✓]
departments(department, dept_location) [3NF ✓]
WHY:
The transitive dependency means department locations are stored
redundantly—once per employee in that department. This creates:
- Update anomaly: Changing a dept location requires N updates
- Insertion anomaly: Can't record a new dept without an employee
- Deletion anomaly: Deleting last employee loses dept location
PROOF:
Equivalence verified by Lean 4 proof: proofs/normalize-4521.lean
Lossless join: ✓ (department is common attribute)
Dependency preservation: ✓ (all FDs map to exactly one new table)
REVERSIBILITY:
Command: ROLLBACK TO "pre-3nf-normalization"
Journal: #4522 (forward), #4523 (inverse stored)(normalization-config
(discovery
(enabled true)
(algorithm "dfd")
(sample-size 10000)
(confidence-threshold 0.95)
(schedule "on-significant-insert") ; or "hourly" | "daily" | "manual"
(significant-insert-threshold 1000))
(target-normal-form "3NF") ; "1NF" | "2NF" | "3NF" | "BCNF" | "4NF" | "5NF"
(auto-apply false) ; If true, apply without human approval
(narrative
(verbosity "detailed") ; "minimal" | "standard" | "detailed"
(include-proofs true)
(format "asciidoc")))| Normal Form | Supported | Notes |
|---|---|---|
1NF |
Full |
Atomic value checking is straightforward; GQL-dt can encode atomicity in types |
2NF |
Full |
Partial dependencies on candidate keys discovered via FD algorithms |
3NF |
Full |
Transitive dependencies discovered via FD algorithms; most common target |
BCNF |
Full |
Checking "every determinant is a superkey" is direct FD analysis |
4NF |
Partial |
Requires multi-valued dependency (MVD) discovery—harder than FD but possible with extended algorithms |
5NF (PJNF) |
Limited |
Requires join dependency discovery; computationally expensive, often requires domain hints |
6NF |
Theoretical |
Decomposition to irreducible relations; rarely practical, included for completeness |
DKNF |
Out of Scope |
Domain-Key Normal Form requires all constraints expressible as domain/key consequences; too abstract for automatic discovery |
Most real-world databases target 3NF or BCNF:
-
3NF eliminates transitive dependencies while preserving all FDs
-
BCNF is stricter but may lose some FDs in decomposition
-
4NF and beyond address multi-valued and join dependencies, which are:
-
Rarer in practice
-
Harder to discover automatically
-
Often require domain expertise to identify
-
Lithoglyph will fully support 1NF through BCNF with automatic discovery and proof generation.
For 4NF and 5NF, Lithoglyph can:
-
Accept manually specified MVDs and JDs
-
Verify normalization against provided dependencies
-
Generate proofs for human-specified decompositions
/-- A multi-valued dependency X →→ Y -/
structure MVD (Schema : Type) where
determinant : List Attribute
dependent : List Attribute
-- X →→ Y means: for any two tuples with same X values,
-- we can swap their Y values and both results are in the relation
/-- Fourth Normal Form: BCNF + no non-trivial MVDs -/
def FourthNormalForm (S : Schema) (fds : List (FunDep S)) (mvds : List (MVD S)) : Prop :=
BCNF S fds ∧
∀ mvd ∈ mvds, mvd.isTrivial ∨ mvd.determinant.isSuperkey| ID | Question | Acceptance Criteria |
|---|---|---|
Q-NORM-001 |
Which FD discovery algorithm should be the default? |
Benchmark on representative datasets; choose based on accuracy/speed tradeoff |
Q-NORM-002 |
How to handle approximate FDs (confidence < 1.0)? |
Define policy for treating near-FDs; document data quality implications |
Q-NORM-003 |
Should denormalization be supported with the same rigor? |
If yes: define DenormalizationStep type with equivalent proofs |
Q-NORM-004 |
How to integrate with GQL-dt’s existing proof system? |
Define interface between Lean 4 proofs and Form.Normalizer |
Q-NORM-005 |
What happens when normalization would break existing queries? |
Define query rewriting strategy or migration period |
-
Implement DFD algorithm in Forth (truth core) or Factor (runtime)
-
Create
DISCOVER DEPENDENCIESGQL command -
Journal all discoveries as narrative artefacts
-
Confidence scoring and approximate FD detection
-
Encode FunDep structure in Lean 4
-
Implement normal form predicates (1NF through 5NF)
-
Create proof tactics for common normalization scenarios
-
Integrate with Form.Bridge for bidirectional FFI
-
Implement decomposition algorithms (for 2NF, 3NF, BCNF)
-
Generate equivalence proofs automatically
-
Create narrative templates
-
Build approval workflow
| Invariant | How Self-Normalization Respects It |
|---|---|
Truth Ownership |
FD discovery reads from blocks; transformations go through Form.Model |
Journal-First |
Every discovery, proposal, and transformation is journaled before committed |
Reversibility |
Every normalization has a proven inverse; denormalization always possible |
Renderability |
Narratives are human/agent-readable; proofs are inspectable |
Provenance |
Every FD traced to sample data; every transformation traced to proposal |
Constraints as Ethics |
Normalization explains why the change improves data integrity |
-
README - Lithoglyph overview
-
ARCHITECTURE - System architecture
-
GQL-dt - Dependent types for GQL
-
GQL Dependent Types - Type system specification