Skip to content

Commit 32b63bc

Browse files
hyperpolymathclaude
andcommitted
feat(lithoglyph): expand test coverage to ~348 tests, fix GQL-DT pipeline
lith-http: 219 tests (was ~4) — 19 new test files covering analytics, CBOR, geo, JWT, temporal index, spatial index, query cache, rate limiter, request logger, health, auth, and API controllers. Fixed temporal_index range_query infinite loop producing duplicates. GQL-DT: complete INSERT/UPDATE/DELETE IR generation pipeline (was stubs). Full CBOR serialization for all statement types. Pipeline now handles GQL and GQL-DT modes end-to-end with proof blob generation. normalizer: created lakefile.lean + lean-toolchain so `lake build` works. Fixed 6 autoImplicit errors in FunDep.lean. 52 proofs pass clean. STATE.scm: honest audit — overall 80% → 65%, api-layer 100% → 30%. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent bfdf54b commit 32b63bc

31 files changed

Lines changed: 2963 additions & 101 deletions

lithoglyph/.machine_readable/STATE.scm

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,8 @@
3232

3333
(current-position
3434
(phase "ip-rename-complete")
35-
(overall-completion 80)
36-
(note "Core phases 1-4 complete. ABI formally verified. BEAM NIFs compile. L1 (Zig HTTP migration) COMPLETE. L2 (Lith rename) COMPLETE. L3 (Evidence collection schema) COMPLETE. IP rename: fdb_*→lith_*, FQL/FBQL/FDQL→GQL, FormBD/FormDB→Lith/Lithoglyph — all complete. Glyphbase NIF wired to core-zig via Zig module import (19 bridge functions, 9.3MB .so). All builds pass.")
35+
(overall-completion 65)
36+
(note "Core phases 1-4 complete. ABI verified. BEAM NIFs compile. IP rename mostly done. Test count: ~348 (lith-http 219, normalizer 52, gql-dt 12, Factor seam 53, Forth ~9, Zig build-tests). Remaining gaps: api layer broken (Zig 0.15.2), studio 11 mock commands, control-plane not started, glyphbase NIF pending real data flow.")
3737
(components
3838
(lith-blocks
3939
(status complete)
@@ -107,8 +107,8 @@
107107
(status complete)
108108
(completion 100)
109109
(version "v0.0.4")
110-
(build-status "52 tests pass")
111-
(files "core-lean/"))
110+
(build-status "52 proofs pass, lake build clean")
111+
(files "normalizer/lean/"))
112112
(beam-nif-zig
113113
(status complete)
114114
(completion 100)
@@ -137,11 +137,11 @@
137137
"beam/native_rust/src/lib.rs"
138138
"beam/native_rust/Cargo.toml"))
139139
(api-layer
140-
(status complete)
141-
(completion 100)
140+
(status broken)
141+
(completion 30)
142142
(version "v0.0.8")
143-
(build-status "PASSZig 0.15.2 HTTP migration complete (83 call sites updated)")
144-
(description "HTTP + gRPC API with real bridge calls, Reader/Writer pattern")
143+
(build-status "BROKEN83 old std.http.Server call sites need Zig 0.15.2 migration")
144+
(description "HTTP + gRPC API — code exists but does not compile on current Zig")
145145
(files
146146
"api/src/main.zig"
147147
"api/src/rest.zig"
@@ -322,6 +322,20 @@
322322
(description "11 backend commands return mock data"))))
323323

324324
(session-history
325+
(snapshot
326+
(date "2026-03-13")
327+
(session-id "parity-audit-and-test-expansion")
328+
(accomplishments
329+
"lith-http: 219 tests, 0 failures (was ~4 tests). Added 19 new test files."
330+
"normalizer: created lakefile.lean + lean-toolchain. lake build passes clean."
331+
"normalizer: fixed 6 autoImplicit errors in FunDep.lean"
332+
"GQL-DT Pipeline: INSERT/UPDATE/DELETE IR generation now fully implemented (was stubs)"
333+
"GQL-DT IR: complete CBOR serialization for all statement types"
334+
"Fixed temporal_index range_query infinite loop bug"
335+
"Fixed geo validation for wrong-coordinate-count Points"
336+
"Fixed JWT expiry check (exp <= now instead of exp < now)"
337+
"Honest STATE.scm audit: overall 80% → 65%, api-layer 100% → 30% (broken)"
338+
"Total test count across Lithoglyph: ~348 (was ~25)"))
325339
(snapshot
326340
(date "2026-03-13")
327341
(session-id "ip-rename-and-glyphbase-nif-linkage")

lithoglyph/beam/native_rust/Cargo.lock

Lines changed: 7 additions & 7 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

lithoglyph/gql-dt/src/GqlDt/IR.lean

Lines changed: 221 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,22 @@ def generateIR_Insert
211211
typesMatch := stmt.typesMatch
212212
}
213213

214+
/-- Convert AST.Condition to a simplified WhereClause predicate.
215+
216+
The AST uses a rich Condition type with dependent TypedValue constructors,
217+
but WhereClause uses a simplified (column, op, value) triple for the parser
218+
tier. We extract the first leaf comparison as the predicate representation.
219+
-/
220+
private def conditionToWhereClause : Condition → Option WhereClause
221+
| .eq (t := .string) (.string col) (.string val) =>
222+
some { predicate := (col, "=", .string val), proof := fun _ => trivial }
223+
| .eq (t := .nat) (.nat col) (.nat val) =>
224+
some { predicate := (toString col, "=", .nat val), proof := fun _ => trivial }
225+
| .lt (t := .nat) (.nat col) (.nat val) =>
226+
some { predicate := (toString col, "<", .nat val), proof := fun _ => trivial }
227+
| .and c _ => conditionToWhereClause c -- Use left side of AND as representative
228+
| _ => none -- Complex conditions fall back to no WHERE in IR (runtime eval handles them)
229+
214230
/-- Generate IR from SELECT AST -/
215231
def generateIR_Select
216232
{α : Type}
@@ -220,10 +236,10 @@ def generateIR_Select
220236
.select {
221237
selectList := stmt.selectList,
222238
from_ := stmt.from_,
223-
where_ := none, -- TODO: Convert Condition to WhereClause
224-
orderBy := none, -- TODO: Parse ORDER BY
225-
limit := none, -- TODO: Parse LIMIT
226-
returning := none, -- TODO: Convert TypeRefinement
239+
where_ := stmt.where_.bind conditionToWhereClause,
240+
orderBy := none, -- AST.SelectStmt does not carry ORDER BY (Pipeline uses ParsedSelect)
241+
limit := none, -- AST.SelectStmt does not carry LIMIT (Pipeline uses ParsedSelect)
242+
returning := none, -- TODO: Convert TypeRefinement (requires runtime type erasure)
227243
permissions := permissions
228244
}
229245

@@ -495,6 +511,207 @@ def requiresProofs (ir : IR) : Bool :=
495511
| .normalize stmt => !stmt.proofs.isEmpty
496512
| _ => false
497513

514+
-- ============================================================================
515+
-- IR Evaluation (In-Memory)
516+
-- ============================================================================
517+
518+
/-- In-memory row: column name to string value mapping -/
519+
structure EvalRow where
520+
values : List (String × String)
521+
deriving Repr
522+
523+
/-- In-memory table: list of rows -/
524+
structure EvalTable where
525+
name : String
526+
columns : List String
527+
rows : List EvalRow
528+
deriving Repr
529+
530+
/-- In-memory database: list of tables -/
531+
structure EvalDatabase where
532+
tables : List EvalTable
533+
deriving Repr
534+
535+
namespace EvalDatabase
536+
537+
/-- Find a table by name -/
538+
def findTable (db : EvalDatabase) (name : String) : Option EvalTable :=
539+
db.tables.find? (·.name == name)
540+
541+
/-- Replace a table by name -/
542+
def updateTable (db : EvalDatabase) (name : String) (f : EvalTable → EvalTable) : EvalDatabase :=
543+
{ tables := db.tables.map fun t => if t.name == name then f t else t }
544+
545+
/-- Create empty database -/
546+
def empty : EvalDatabase := { tables := [] }
547+
548+
end EvalDatabase
549+
550+
/-- Convert a TypedValue to its string representation for in-memory storage -/
551+
def typedValueToString : {t : TypeExpr} → TypedValue t → String
552+
| _, .nat n => toString n
553+
| _, .int i => toString i
554+
| _, .string s => s
555+
| _, .bool b => toString b
556+
| _, .float f => toString f
557+
| _, .boundedNat _ _ bn => toString bn.val
558+
| _, .nonEmptyString nes => nes.val
559+
| _, .promptScores ps => toString ps.overall.val
560+
561+
/-- Evaluate WHERE clause predicate against a row -/
562+
def evalWhereClause (row : EvalRow) (wc : WhereClause) : Bool :=
563+
let (col, op, val) := wc.predicate
564+
let rowVal := row.values.find? (·.1 == col) |>.map (·.2)
565+
match rowVal with
566+
| none => false
567+
| some rv =>
568+
let valStr := match val with
569+
| .nat n => toString n
570+
| .int i => toString i
571+
| .string s => s
572+
| .bool b => toString b
573+
| .float f => toString f
574+
match op with
575+
| "=" => rv == valStr
576+
| "!=" => rv != valStr
577+
| "<" =>
578+
match rv.toNat?, valStr.toNat? with
579+
| some a, some b => a < b
580+
| _, _ => rv < valStr
581+
| ">" =>
582+
match rv.toNat?, valStr.toNat? with
583+
| some a, some b => a > b
584+
| _, _ => rv > valStr
585+
| "<=" =>
586+
match rv.toNat?, valStr.toNat? with
587+
| some a, some b => a ≤ b
588+
| _, _ => rv ≤ valStr
589+
| ">=" =>
590+
match rv.toNat?, valStr.toNat? with
591+
| some a, some b => a ≥ b
592+
| _, _ => rv ≥ valStr
593+
| _ => false
594+
595+
/-- Apply ORDER BY to rows -/
596+
def applyOrderBy (rows : List EvalRow) (ob : OrderByClause) : List EvalRow :=
597+
match ob.columns.head? with
598+
| none => rows
599+
| some (colName, direction) =>
600+
let isAsc := direction == "ASC"
601+
rows.toArray.qsort (fun r1 r2 =>
602+
let v1 := r1.values.find? (·.1 == colName) |>.map (·.2) |>.getD ""
603+
let v2 := r2.values.find? (·.1 == colName) |>.map (·.2) |>.getD ""
604+
-- Try numeric comparison first, fall back to string
605+
match v1.toNat?, v2.toNat? with
606+
| some n1, some n2 => if isAsc then n1 < n2 else n1 > n2
607+
| _, _ => if isAsc then v1 < v2 else v1 > v2
608+
) |>.toList
609+
610+
/-- Result of evaluating an IR statement -/
611+
inductive EvalIRResult where
612+
| rows : List String → List (List String) → EvalIRResult -- columns, row values
613+
| mutation : Nat → EvalIRResult -- affected rows
614+
| error : String → EvalIRResult
615+
deriving Repr
616+
617+
/-- Format an EvalIRResult for display -/
618+
def EvalIRResult.toString : EvalIRResult → String
619+
| .rows cols rowData =>
620+
if rowData.isEmpty then "(0 rows)"
621+
else
622+
let header := String.intercalate " | " cols
623+
let separator := String.mk (List.replicate header.length '-')
624+
let rowStrs := rowData.map fun row =>
625+
String.intercalate " | " row
626+
s!"{header}\n{separator}\n{String.intercalate "\n" rowStrs}\n({rowData.length} rows)"
627+
| .mutation n => s!"OK, {n} row(s) affected"
628+
| .error msg => s!"Error: {msg}"
629+
630+
/-- Evaluate an IR statement against an in-memory database.
631+
632+
Returns the updated database and the result.
633+
-/
634+
def evalIR (db : EvalDatabase) (ir : IR) : EvalDatabase × EvalIRResult :=
635+
match ir with
636+
| .select stmt =>
637+
let tableName := match stmt.from_.tables.head? with
638+
| some t => t.name
639+
| none => ""
640+
match db.findTable tableName with
641+
| none => (db, .error s!"Table not found: {tableName}")
642+
| some table =>
643+
-- Filter rows by WHERE clause
644+
let filtered := match stmt.where_ with
645+
| none => table.rows
646+
| some wc => table.rows.filter (evalWhereClause · wc)
647+
-- Apply ORDER BY
648+
let sorted := match stmt.orderBy with
649+
| none => filtered
650+
| some ob => applyOrderBy filtered ob
651+
-- Apply LIMIT
652+
let limited := match stmt.limit with
653+
| none => sorted
654+
| some n => sorted.take n
655+
-- Project columns
656+
let cols := match stmt.selectList with
657+
| .star => table.columns
658+
| .columns cs => cs
659+
| .typed _ _ => table.columns
660+
let rowData := limited.map fun row =>
661+
cols.map fun c =>
662+
row.values.find? (·.1 == c) |>.map (·.2) |>.getD "NULL"
663+
(db, .rows cols rowData)
664+
665+
| .insert stmt =>
666+
let row : EvalRow := {
667+
values := stmt.columns.zip (stmt.values.map fun ⟨_, v⟩ => typedValueToString v)
668+
}
669+
match db.findTable stmt.table with
670+
| none =>
671+
-- Create table if it doesn't exist
672+
let newTable : EvalTable := {
673+
name := stmt.table,
674+
columns := stmt.columns,
675+
rows := [row]
676+
}
677+
({ tables := db.tables ++ [newTable] }, .mutation 1)
678+
| some table =>
679+
let newTable := { table with rows := table.rows ++ [row] }
680+
(db.updateTable stmt.table (fun _ => newTable), .mutation 1)
681+
682+
| .update stmt =>
683+
match db.findTable stmt.table with
684+
| none => (db, .error s!"Table not found: {stmt.table}")
685+
| some table =>
686+
-- Find rows matching WHERE clause
687+
let (matching, nonMatching) := match stmt.where_ with
688+
| none => (table.rows, [])
689+
| some wc =>
690+
let m := table.rows.filter (evalWhereClause · wc)
691+
let nm := table.rows.filter (fun r => !evalWhereClause r wc)
692+
(m, nm)
693+
-- Apply assignments to matching rows
694+
let updated := matching.map fun row =>
695+
stmt.assignments.foldl (fun r a =>
696+
let newVal := typedValueToString a.value.2
697+
{ values := r.values.map fun (c, v) =>
698+
if c == a.column then (c, newVal) else (c, v) }
699+
) row
700+
let newTable := { table with rows := nonMatching ++ updated }
701+
(db.updateTable stmt.table (fun _ => newTable), .mutation matching.length)
702+
703+
| .delete stmt =>
704+
match db.findTable stmt.table with
705+
| none => (db, .error s!"Table not found: {stmt.table}")
706+
| some table =>
707+
let matching := table.rows.filter (evalWhereClause · stmt.where_)
708+
let remaining := table.rows.filter (fun r => !evalWhereClause r stmt.where_)
709+
let newTable := { table with rows := remaining }
710+
(db.updateTable stmt.table (fun _ => newTable), .mutation matching.length)
711+
712+
| .normalize _ =>
713+
(db, .error "NORMALIZE not yet supported in evaluation")
714+
498715
-- ============================================================================
499716
-- Example: IR Construction
500717
-- ============================================================================

0 commit comments

Comments
 (0)