- Add fkJson to wire protocol with serialization/deserialization
- Validate JSON/JSONB on INSERT/UPDATE via std/json
- Send real column type metadata in wire protocol responses
- Update all 4 clients (Nim, Python, JS, Rust) for JSON and columnTypes
- Implement multi-column index parser (CREATE INDEX idx ON t(a, b))
- Add ciColumns to AST nkCreateIndex
- Build composite index keys as table.col1.col2 with val1|val2
- Support multi-column exact match in SELECT for AND chains
- Implement non-recursive CTE execution via ctx.cteTables materialization
- Add tkRecursive token and parse WITH RECURSIVE
- Fix test isolation: use temp dirs for JOIN, migration, and RLS tests
- All tests passing (0 failures)
Critical fixes:
- B-Tree splitChild: fixed out-of-bounds on child.children.len (mid+1..len → mid+1..<len)
- Wire protocol: all deserializeValue cases now properly assign to result
- Wire protocol: fkBool/fkInt8 now increment pos after reading
- MVCC: write-write conflict now detected for completed transactions
- MVCC: commit only marks visible versions as deleted (not concurrent uncommitted)
- Lexer: buffer overrun on backslash escape at end of input — added bounds check
- Bulgarian/Russian stemmers: fixed UTF-8 byte offsets for Cyrillic suffixes (2 bytes per char)
High fixes:
- WebSocket: added missing std/tables import
- Raft: added randomize() call for election timeouts
- MemTable.get: binary search O(log n) instead of linear scan O(n)
Medium fixes:
- Zerocopy: string length now written as 4-byte int32 (was single byte)
- Raft appendEntries: guard against nextIdx underflow
- Columnar: null tracking with nulls seq on ColumnPtr
- Removed dead code in lexer readIdent (redundant true/false checks)
- Updated Bulgarian stemming test to match corrected byte offsets
All 214 tests pass across 48 suites with 0 failures.