Notes

What the build taught.

Engineering observations that emerged from building the Lucid Decision System. Not best-practices posts — specific things that were wrong, then fixed.

2026-04
infrastructure

Local embeddings are sufficient for personal-scale semantic retrieval

nomic-embed-text via Ollama produces 768-dim vectors that perform meaningfully at personal decision volumes. OpenAI API adds cost and data-egress risk with no measurable quality gain at this scale. The assumption that "local models are a compromise" did not survive contact with the actual retrieval results.

2026-04
engineering

Pin your qdrant-client version

qdrant-client 1.16 silently removed the search() method and replaced it with query_points(). There was no deprecation warning in the version that was pinned. Lesson: vector DB client libraries are not stable across minor versions. Always pin, always test the upgrade path explicitly.

2026-04
system design

Sequential phases create clean dependency chains

The Learning Engine (Phase 2) and Memory Layer (Phase 3) were designed as separate phases rather than merged. The payoff: the assumption_history schema from Phase 2 became a first-class ranking input in the Phase 3 Retrieval Broker — with zero rework. The phases were more tightly coupled than the spec made explicit. Separation made that coupling visible before it became a constraint.

2026-04
api design

Celery embed dispatch must be wrapped in try/except at the call site

The Celery embed_object.delay() call runs after the Postgres commit — after L1 is already written. If Redis is down, the unguarded Celery call crashes the entire API endpoint with a RuntimeError. L2 (Qdrant) is a derivative index, not the primary store. A Redis outage must never fail a decision write. The fix: wrap every embed dispatch in try/except with a pass. Embedding is best-effort.

2026-04
formula design

DQS needs an upper-bound clamp, not just zero-division guards

The Decision Quality Score formula has three terms summing to 1.0. The uncertainties_covered term divides covered by total_unexpected. If a user enters covered > total_unexpected (a data-entry artifact, not a meaningful claim), the formula produces DQS > 1.0, which violates the ecs_profiles.epistemic_score check constraint. The fix: cap uncertainties_covered at total_unexpected before dividing, then clamp the final result to min(1.0, total). Both guards are necessary.

//

Notes are added as they emerge from active engineering work — not on a schedule. These five are extracted directly from the Lucid Decision System build log. More will appear as Phase 4 and beyond surface new observations.