v1.5
latestThe indexing and truth-maintenance release. Point-in-time reads became a real index scan, lexical search became a real GIN predicate, graph traversal became a single capped recursive CTE, and extraction stopped silently falling back to heuristics. Billing and metering code is gone.
- Lexical search
- 550ms → 48ms
- Graph traversal
- 38.5ms → 2.16ms
- Extraction success
- 17% → 87.5%
- Public tables
- 57 → 33
Storage and indexing — Added
- A durable ingestion outbox with PostgreSQL SKIP LOCKED claiming, so concurrent workers never process the same observation twice and a crashed claim is retried rather than dropped.
- A composite bitemporal index on (organization_id, valid_to, valid_from), which makes point-in-time as_of reads a real index scan instead of a sequential filter.
- Entity-name trigram indexes plus a case-insensitive uniqueness constraint on (organization_id, user_id, lower(name)). A migration merged 2,670 pre-existing duplicate entity groups down to zero.
- A dedicated TruthSupersessionService that invalidates a contradicted memory with valid_to = now() and records lineage through memory_version.superseded_by_id, so stale facts are excluded from active retrieval instead of accumulating as contradictions. Backed by a partial unique index guaranteeing at most one current fact per slot.
- A closed, validated set of relationship types enforced by a database CHECK constraint, with self-loop rejection at the storage layer.
Retrieval — Added
- A cascade retrieval mode: lexical and graph evidence are gathered first, and the dense channel is only consulted when that primary evidence is judged insufficient.
- Precision-aware freshness scoring that honours temporal_precision and temporal_basis, so an explicitly approximate timestamp is not treated as an exact one.
Extraction reliability — Added
- Bounded retries with escalating temperature, plus robust JSON recovery for fenced code blocks, <think> reasoning traces, and leading preambles.
- Tolerant coercion of LLM schema drift: unknown memory types, travel mapped to event, list-shaped booleans, and empty titles are normalised instead of discarded. Measured session-level extraction success improved from roughly 17% to 87.5%.
Security and hygiene — Changed
- Secrets redaction now runs as a fail-closed stage at the head of the ingestion pipeline, so API keys, JWTs, bearer tokens, and passwords are scrubbed before extraction ever runs.
- Pair-clique edge generation in the knowledge graph was replaced with a bounded approach, removing the O(n²) blow-up as the graph grows.
Indexing — Fixed
- HNSW retrieval was collapsing on filtered queries and silently returning zero rows. ef_search is now tuned and hnsw.iterative_scan is enabled, so a filtered vector search no longer drops the tenant constraint to get an answer.
- Sequential lexical search was replaced with a proper GIN tsvector predicate, measured from roughly 550ms to 48ms.
- 2,745 colliding knowledge-graph edges were deduplicated and re-normalised.
Extraction — Fixed
- The fine-tuned extractor's context window was raised from 4,096 to 16,384 tokens. Real extraction prompts were 8,281 tokens and were being rejected outright, which silently downgraded sessions to a low-quality heuristic fallback.
- Structured-output enforcement now works. Ollama's OpenAI-compatible endpoint silently drops the format field, so schema constraints were never actually applied. Contexta detects an Ollama endpoint and uses the native Ollama route, constraining generation to a real JSON Schema.
Isolation — Fixed
- Tenant scoping on the graph layer was corrected so multi-hop traversal stays inside the requesting organization.
Measured — Performance
- Lexical retrieval: approximately 550ms to 48ms on the reference corpus.
- Multi-hop graph traversal: rewritten as a single recursive CTE with hard caps, approximately 38.5ms to 2.16ms — about 16x on the reference graph.
- Session-level extraction success: roughly 17% to 87.5%.
- Schema surface: 18 billing tables and 6 dead schemas dropped, taking the public table count from 57 to 33.
Removed — Removed
- All billing, metering, and credit-consumption code. Contexta Core no longer has a metering path to remove or bypass.
Upgrade notes
- 01Run the v1.5 migrations against a snapshot. The entity uniqueness constraint is created case-insensitively on lower(name) and will refuse to apply while case-variant duplicates exist; the shipped migration merges them first, which is the step that cleared 2,670 duplicate groups.
- 02The new relationship-type CHECK constraint is closed. If you have written custom relationship types directly against the graph tables, map them onto the validated set before upgrading.
- 03The 18 billing tables and 6 dead schemas are dropped. Any dashboard query, report, or export that referenced them needs to be removed in the same change.
- 04The offline embedding profile is unchanged at 1024 dimensions, so no vector migration is required. If you had switched the column to the 1536-d online profile, switch it back before re-indexing.
- 05Ollama endpoints are now detected and routed natively for structured output. A custom OpenAI-compatible base URL that is not recognisably Ollama will no longer receive a schema constraint — point the endpoint at Ollama directly.
- 06The outbox uses SKIP LOCKED claiming. Scaling ingestion workers is now safe, but any worker that previously assumed single-writer ordering should tolerate interleaved claims.