M20 covers purpose-built engines whose correctness depends on parameters a generic relational audit
ignores. Sub-modules carry a letter (M20a..M20d); the scorer maps them to the parent M20 in the
paradigm profile (Vector / Time-series / Graph categories in references/scoring-model.md).
M20a — Vector (pgvector, Qdrant, Pinecone, Weaviate)
Feeds: design (Métrica & dimensión, Modelo-version, Metadata/filtro) + performance (Índice & params, Búsqueda filtrada, Recall-vs-latencia).
- Dimension match — column/collection dim equals the embedding model's output dim
(e.g.
vector(1536)for text-embedding-3-small). A mismatch is a hard bug (design, sev 5). - Distance metric match — the index metric (cosine / L2 / inner-product) matches how the model was
trained; a mismatch silently wrecks recall. Sev-5 only when the model is declared in-repo; else
directionaland do not cap (per scoring-model honesty rule). - Index present & tuned — HNSW (
m,ef_construction,ef_search) or IVFFlat (lists/probes) declared, not a brute-force seq scan on a large table (performance). - Model version captured — embeddings are tied to a model version so a re-embed is possible (design).
- Filtered search — metadata used for pre/post-filtering is itself indexed (performance).
M20b — Time-series / OLAP (TimescaleDB, InfluxDB, ClickHouse)
Feeds: design (Hypertable-fit, Retención, Precisión-ts & tz) + performance (Chunk/retención, Continuous-agg, Compresión, Query).
- Hypertable / partition fit — large append-only time data is a hypertable / partitioned by time, with a sane chunk interval (not one giant chunk, not millions of tiny ones).
- Continuous aggregates / rollups declared for dashboard queries instead of scanning raw rows.
- Compression / TTL retention policy present for cold chunks; raw retention bounded.
- Timestamp precision & timezone —
timestamptz/UTC, not naive local time (shares the M4 rule).
M20c — Graph (Neo4j)
Feeds: design (Modelado-aristas, Nodos/traversal) + performance (Índice-lookup, Traversal, Supernodo).
- Edge modeling — relationships carry the right direction/type; relationship properties exist where traversals need them; no relationship modeled as a node-pair table that defeats the graph engine.
- Index-backed lookups — entry-point node properties used to start traversals have indexes/constraints
(
CREATE CONSTRAINT ... IS UNIQUE), so traversals don't begin with a full label scan. - Supernode risk — a node with a very high relationship degree that will dominate traversal cost
(performance). Sev escalation requires live degree evidence; else
directional.
M20d — Search (Elasticsearch, OpenSearch)
Feeds: design (mapping/analyzer correctness) + performance (query/shard layout).
- Mapping & analyzer — fields have explicit mappings (not relying on dynamic mapping for analyzed text); analyzer matches language; keyword vs text chosen deliberately.
- No mapping explosion / unbounded dynamic fields; shard count sane for index size.
Tier-0 static checks
Parse declared schema/config: pgvector vector(N) columns + CREATE INDEX ... USING hnsw/ivfflat;
Qdrant/Pinecone collection config (size, distance); Timescale create_hypertable/add_*_policy;
ClickHouse ENGINE = MergeTree ... ORDER BY; Neo4j constraint/index DDL; ES mappings/settings JSON.
Cross-check declared embedding-model dim/metric against any model id referenced in repo code.
Tier-1 verification query
- pgvector:
SELECT indexdef FROM pg_indexes WHERE indexdef ILIKE '%hnsw%' OR indexdef ILIKE '%ivfflat%';and\d <table>for thevector(N)dim. - Timescale:
SELECT * FROM timescaledb_information.hypertables;and...continuous_aggregates;. - Neo4j:
SHOW INDEXES;SHOW CONSTRAINTS;and per-node degree for supernode (MATCH (n) RETURN ...). - ES:
GET /<index>/_mapping,GET /<index>/_settings. When the engine isn't reachable →needs_api.
Findings
Emit per schema/finding.schema.json. Example ids:
M20a.embeddings.dim_mismatch—vector(768)but model emits 1536 (fail, severity 5, axisdesign, confidenceestablishedwhen the model id is in-repo).M20a.embeddings.metric_mismatch— index uses L2 but model trained for cosine (fail, severity 5 only if model declared, else warn/directional, axisboth).M20a.embeddings.no_ann_index— seq scan vector search (warn, severity 4, axisperformance).M20b.metrics.no_hypertable— large time table not a hypertable (warn, severity 4, axisboth).M20c.users.unindexed_traversal_root— traversal start property unindexed (warn, severity 3, perf). Each finding:evidence.observedquotes the real DDL/collection config verbatim (secrets redacted);verification.reproduceis a runnable command above using$DATABASE_URL;verification.methodisschema_introspect,index_check,ddl_parse, orexplain_plan;expected_impactcarries{axis, confidence, magnitude, rationale}— banded, never a naked %.
Honesty
- Never assert a recall number or latency for an index configuration — magnitude is banded only.
- Metric/dimension mismatch caps only when the embedding model is declared in-repo (durable fact).
Inferred mismatches are
directionaland never cap. Supernode/hot-partition sev-5 needs live degree evidence; otherwisedirectionalorneeds_api, never a silent pass.