Commit graph

54 commits

Author SHA1 Message Date
7d2bf4e7fa
phase 2: attach-forever sharding — zero merge cost
Each ingest shard owns its own SQLite file. N processes write in
true parallel (no WAL writer-lock contention). Cross-shard reads
attach all shards as UNION ALL views over the standard tables, so
existing query code (`SELECT * FROM documents`) works unchanged.

CLI:
  aborist ingest --shards-dir DIR --shard 0/4    # write to DIR/000.db
  aborist --shards-dir DIR stats                  # read across all shards
  aborist --shards-dir DIR analyze                #   ditto
  aborist --shards-dir DIR verify -n 20           #   Merkle round-trips
                                                  #   on a sample drawn
                                                  #   from all shards

Makefile:
  SHARDS_DIR=~/.aborist/shards
  ingest-cur-attached SHARDS=4   ingest-old-attached SHARDS=4
  stats-shards                   cross-shard stats

Implementation lives in store.connect_query():
  - opens a fresh in-memory connection
  - ATTACH DATABASE per shard file under aliases sh000, sh001, ...
  - CREATE TEMP VIEW <table> AS UNION ALL SELECT * FROM <alias>.<table>
    for the eight shardable tables (documents, chunks, merkle_nodes,
    edges, derivations, providence_cache, audit_events, falsifications)
  - exposes a _shards temp table for tools that want the shard list

Benchmark (20k cur, 4 cores):
  Phase 1a serial            55.3 s
  Phase 1b shared-WAL        43.1 s
  Phase 2 attached-forever   40.1 s    (no WAL contention)

The 7% gap vs Phase 1b is bz2-decompression duplication: each shard
decodes the full dump independently. Architectural wins are bigger
than that number — write isolation, per-shard crash recovery,
audit chain stays per-shard checkable, shards can migrate to other
disks, new shards are pure append (never rewrite existing files).

Merge cost remains: zero. `ls $SHARDS_DIR/*.db` is the registry.
2026-04-27 11:17:17 -04:00
d0857fb43d
phase 1b: --shard rank/total + parallel Makefile targets
Workers each parse the full SQL stream but only emit rows whose index
satisfies (idx % total == rank). N processes cooperate against a
single shared SQLite file; WAL+synchronous=NORMAL handles writer
contention via BEGIN IMMEDIATE serialization.

Implementation chose OS-level processes over in-process mp.Queue: the
gain ceiling is identical (writer is the bottleneck either way) and
the surface area is one new flag plus stride-filter logic instead of
a queue-based pipeline with picklable workers.

Makefile gains:
  ingest-cur-parallel SHARDS=4
  ingest-old-parallel SHARDS=4

Benchmark on 20k cur articles:
  serial            55.3 s   361 docs/s   1.0x CPU usage
  4-way parallel    43.1 s   464 docs/s   ~3.0x CPU usage   1.28x wall

Both runs landed 19,999 unique documents — correctness verified.

The write-lock is now the bottleneck; further gains require Phase 2
attach-forever sharding (each shard owns its own SQLite file, queries
fan out via ATTACH DATABASE, zero merge cost).
2026-04-27 11:10:25 -04:00
a056acfb5b
prepare full Wikipedia 2003-05-16 ingest: cur + old (revisions)
The 2003-05-16 archive ships three files:
  20030516_cur_tablesql.bz2   82 MB  current snapshot (single revision/page)
  old_tablesqlbz2.1          640 MiB \
  old_tablesqlbz2.2          252 MiB / split halves of old (full revision
                                       history). Concatenate before bzcat.

Generalize the parser:
  WikipediaSqlDump(table='cur'|'old')  — shared statement parser, single
                                         column-position contract for the
                                         first 4 fields (id/ns/title/text)
  WikipediaCurDump  — back-compat wrapper, table='cur'
  WikipediaOldDump  — new, table='old'; old has no is_redirect, every
                      revision is real

Old rows surface old_id and old_timestamp via Document.extra so a
downstream pass can sort revisions chronologically before re-ingesting
through the supersedes-edge path.

Makefile gains:
  fetch-cur / fetch-old / fetch (both)
  ingest-cur / ingest-old / ingest (cur default)
  WP_OLD target concatenates the two split parts
CLI ingest --source now accepts wikipedia_cur or wikipedia_old.

Smoke (real dump): 5 revisions of "AtlasShrugged/Companies" yielded
correctly with old_id=2..10, timestamps from January 2002.
2026-04-27 08:10:42 -04:00
856b3116d7
phase 0 explore: aborist core + sources + distill + evict
A content-addressed, Merkle-committed document store implementing the
runtime spec from Merkle Providence Reverse RAG (April 2026 whitepaper)
and Merkle-AGI v9.8 admissibility ledger. Ports proxy.unturf.com Go
merkle conventions to Python: non-commutative HashCombine with 0x03
prefix, explicit IsLeft per sibling, self-duplicate odd elements.

What's in:

- merkle.py — proof generation/verification, JSON serialization
- store.py — v9.8 SQLite schema: 8-dim providence_cache key,
  falsification_state, append-only audit chain, surface/core kind,
  hot/warm/cold tier, derivations, edges
- ingest.py — Source -> normalize -> chunk -> merkle -> upsert,
  idempotent on document_root collision
- search/ — SearchBackend ABC with explicit AuditMode (STRICT/HYBRID/
  VISUAL), FTS5 backend returning VISUAL hits
- sources/ — wikipedia.py (streaming bz2/MySQL extended-INSERT parser
  for 2003-era cur dumps); html_page.py (selectolax + httpx, robots.txt
  honored automatically)
- distill/ — Distiller ABC + first-sentence-v1 stub. Runner generates
  per-contributing-chunk Merkle proofs binding cores back to source
  document_root.
- evict.py — hot->cold demote (NULLs content, drops FTS row, retains
  leaf_hash). rehydrate() refetches via source pipeline; matching root
  restores content, mismatching root marks providence stale and writes
  rehydrate_drift event. Cores never evict.
- cli.py — ingest / search / verify / stats / distill / evict /
  rehydrate
- 31 tests covering merkle round-trip, ingest+audit, chunker version
  binding, html parse, distillation proof verification, evict+
  rehydrate including drift detection.

Smoke: 503 Wikipedia 2003-05-16 + 3 fox-owned HTML pages ingested,
478 cores produced (24 surface->core merkle dedups), 7 chunks evicted
to cold and round-tripped via rehydrate, 987 audit events chained 0
breaks.
2026-04-27 07:53:18 -04:00