7-phase methodology for reverse-engineering codebases and producing clean-room design documents under new constraints. Core workflow: Phase 1: Clone & map repository structure Phase 2: Find key architectural files (sort by line count) Phase 3: Map architecture with Mermaid diagrams Phase 3b: Extract implicit storage/provider contracts (DAO design) Phase 4: Inventory feature surface Phase 5: Write clean-room specification (no code contamination) Phase 6: Break constraints (local-first, privacy-first redesign) Phase 7: Post-delivery QA (link integrity, diagram rendering) Includes a full worked example of the interface extraction pattern (references/interface-extraction-pattern.md) using the cashew thought-graph library — demonstrating all five steps from philosophy reading through two-provider proof concept. Signed-off-by: Jasper <magnus@groktop.us>
5.7 KiB
Interface Extraction — Worked Example
This reference demonstrates the Phase 3b interface extraction pattern using the cashew thought-graph library (MIT license) as the target codebase. The goal was to extract the implicit storage contract to design a provider-agnostic DAO interface enabling swappable backends (sqlite-vec, KuzuDB, DuckDB+vss, etc.).
This is a teaching example — the methodology, not the specific project, is what generalizes.
The Five Steps
Step 1 — Philosophy First
Read PHILOSOPHY.md, DESIGN.md, and README.md before touching code. The cashew repo's philosophy revealed a critical constraint: "dumb graph, smart reasoning layer" — edges carry no type labels, node types are descriptive hints for the LLM only, not load-bearing for graph engine operations. This constraint must be baked into the contract. The Edge dataclass has no type or label field.
Step 2 — Catalog Every Storage Operation
Read all core modules and extracted every distinct database operation:
| Module | Operations | Current Implementation |
|---|---|---|
| db.py | Node CRUD, connection mgmt, schema migration | Thin sqlite3 wrappers, schema in constants |
| embeddings.py | KNN search, dual-write, dim-mismatch, novelty check | vec0 fast path + numpy O(N) fallback + text Jaccard fallback |
| sleep.py | Cross-link candidates, dedup clusters, GC, core promotion | sklearn pairwise matrix, Bron-Kerbosch, random sampling via SQL |
| retrieval.py | Embedding search → BFS walk → hybrid score | vec0 query + recursive CTEs + manual scoring |
| traversal.py | Trace derivation paths, audit | Recursive CTEs, UNION BFS, DFS in Python |
| session.py | Context assembly, access tracking, node creation | db.py primitives + timestamp bumps |
| graph_utils.py | Load embeddings for batch operations | Full embeddings table → numpy array |
Step 3 — Identify Boundary Leaks (Workarounds)
Four workarounds signaled the boundary was in the wrong place:
-
Dual-write vector storage — every embed writes to two tables (embeddings BLOB + vec_embeddings virtual table), with dim-mismatch detection and a three-layer fallback chain (vec0 → numpy → Jaccard). This is the cost of sqlite-vec not supporting native HNSW.
-
O(N²) pairwise matrix in application memory — the sleep cycle loads all embeddings into sklearn for cosine similarity, hitting ~160GB for 200K 1024-dim nodes. A native HNSW provider would do this incrementally.
-
Recursive CTEs in Python strings — graph traversal is hand-rolled via raw SQL recursive CTEs that a native graph DB would do with a single
MATCHclause. -
Full-table load into numpy — cross-link candidate discovery loads the entire embeddings table into memory instead of incremental approximate queries.
Key lesson: These are not bugs. They are signals telling you where the architectural boundary is currently leaking. Each one is a candidate to push behind the contract.
Step 4 — Design the Contract
Produce a Python ABC (Abstract Base Class) with ~30 methods across 8 domains:
StorageProviderbase class withinitialize()/close()lifecycle- Node CRUD:
create_node,get_node,update_node,delete_node,scan_nodes,count_nodes - Edge CRUD (dumb graph — no edge types):
create_edge,get_neighbors,delete_incident_edges,redirect_edges - Vector KNN:
set_embedding,get_embedding,find_similar,find_similar_by_id,scan_embeddings - Graph traversal:
bfs,shortest_path,trace_derivation - Batch/maintenance:
find_cross_link_candidates,find_near_duplicates,random_sample,get_graph_metrics - Transactions and introspection
Key decisions:
find_cross_link_candidatesreturns candidate pairs but does not specify how — sqlite-vec does O(N²) internally, HNSW providers query incrementallyfind_near_duplicates(Bron-Kerbosch algorithm) stays in application code as business logic, not storagerandom_sampleis a provider operation — native sampling is far more efficient than loading everything to Python- No edge type/label field anywhere — the dumb-graph constraint is enforced at the data-model level
Step 5 — Two-Provider Proof Concept
Design two provider implementations to validate the contract:
-
Provider A (sqlite-vec): wraps the existing dual-write, sklearn matrix, recursive CTEs, dim-mismatch detection into the contract. All the workarounds become this provider's internal complexity, not application core's.
-
Provider B (KuzuDB): second implementation proving the contract is complete. Native HNSW means
set_embeddingis a column write,find_similarisQUERY_VECTOR_INDEX,shortest_pathisMATCH. No fallback chains — the provider is simpler because the substrate handles it.
Both providers pass the same test suite, proving the abstraction is sound.
Key Takeaways
-
The philosophy is the constraint. Read it before touching code — it tells you what the interface must respect.
-
Workarounds are signals. Dual-write, fallback chains, full-table loads, hand-rolled graph traversals in SQL — each one tells you where the boundary is leaking.
-
Design against what the codebase needs, not what the current substrate does. The interface represents the application's requirements, not the current provider's capabilities.
-
The two-provider proof catches leaks. If you can't implement a second provider against the same interface, the contract has holes.
-
Expensive operations define the performance profile.
find_similar,find_cross_link_candidates, andbfsare the operations that determine whether the system is fast or slow. The contract must make them implementable efficiently on a native substrate.