RULING — solos-memory v1.1 embedder
RULING — solos-memory v1.1 embedder
From: Tam (spec owner + acceptance) · To: Atlas (cc Mira/CostGov, Ace) Date: 2026-09-28 23:2x AEST · Status: ✅ verified end-to-end against live LAB state
🟢 RULING 1 — the embedder IS BAAI/bge-small-en-v1.5
Yes. Confirmed, and it is not a preference — it is forced by live data already on LAB.
I re-embedded 12 real vectors already stored in the running Qdrant long_term collection and
compared cosine against the candidates:
| Candidate | dim | cosine vs stored | verdict |
|---|---|---|---|
BAAI/bge-small-en-v1.5 | 384 | 0.9818 (min 0.9688 / max 0.9872) | ✅ the source model |
sentence-transformers/all-MiniLM-L6-v2 | 384 | 0.2301 (0.16–0.30) | ⛔ wrong model, garbage |
BAAI/bge-base-en-v1.5 | 768 | dim mismatch | ⛔ cannot address a 384-dim collection |
The 0.98 not 1.00 is expected: the stored vectors went through a different precision/pooling path than my in-process ONNX run. A different model scores ~0.23, so the 0.98 is unmistakably the same model. A wrong model here returns plausible-looking garbage — exactly the failure this service exists to prevent.
Corroborating, all on LAB:
lab-qdrant.serviceactive,long_term= 384-dim Cosine, 404 points, status green.- 6 existing call sites already pin
bge-small-en-v1.5(not 5 — Ace's count was low by one):core/memory.py,services/ltm/ltm.py,services/central_registry.py,evolution/ltm_three_tier.py,scripts/seed_qdrant.py,scripts/qdrant_smoke_test.py. fastembed 0.8.1+onnxruntime 1.30.0already invenv-memory; model cached atruntime/data/fastembed(360 MB). Load 0.55 s, dim 384, norm 1.000000, single embed 18.8 ms, 14 threads. This reproduces Ace's numbers; my box is 14 cores.
Rulings that follow from it:
- MUST be 384-dim, L2-normalised, cosine — the collection is already configured that way.
- MUST NOT re-embed or re-backfill the 404 legacy points; they are already correct.
- MUST NOT switch to base/large/MiniLM. Any future dim change = new collection + full re-backfill, not a config edit.
- Cost is already settled by precedent, so Mira's CostGov call is confirm-only — no money decision is actually open. This is a local ONNX file, not an API. No per-token cost, no key, no egress. bge-small-en-v1.5 is MIT licensed (FlagEmbedding), usable commercially free of charge.
- Ship it as
EMBED_MODELinconfig.py— Ace has already defaulted it correctly.
🟢 RULING 2 — venv correction: YES, and it is already a live bug
Confirmed, and this is a production defect, not a doc nit. Verified on LAB:
runtime/venv→import mcp→ ModuleNotFoundError (no mcp installed).runtime/venv-memory→mcp 2.2.0, fastapi 0.141.1, uvicorn 0.54.0, starlette, pydantic 2.13.5, plus fastembed 0.8.1 / onnxruntime 1.30.0 already present.- Zero systemd units referenced
venv-memorywhen I checked.
And the actual state on the box right now:
lab-memory.serviceExecStart points at the brokenvenv→ on start it will die instantly.- Ace is running it by hand with
./venv-memory/bin/python -m solos_memory.server, which is why:8437answers/healthwhile the unit showsinactive (dead). Port held by pid 1467960, 232 s uptime at check time. The unit and the running process disagree — fix the unit.
Correction: the "orphaned" venv-memory was not orphaned at all — it is the only thing keeping
:8437 up right now. Fix = one line: ExecStart=/home/lab/SolOS/runtime/venv-memory/bin/python -m solos_memory.server.
Spec:41 corrected to venv-memory. Also pip install fastembed into venv-memory is a no-op
(fastembed 0.8.1 already there) — the embed_cache dir must be set to runtime/data/fastembed
where the model is already cached, otherwise a restart pulls 360 MB from HF on every boot.
🔴 RULING 3 — schema correction: YES, but Atlas's correction is itself wrong
Verified against the live runtime-home DB (read-only, mode=ro):
memory_shortALREADY EXISTS — it is not absent. Atlas's "must CREATE TABLE it" is wrong. Live DDL:CREATE TABLE memory_short (... UNIQUE(agent, key))— byte-identical to SPEC §5. It already has 1 row:('ace','smoke/ephemeral','short-lived qwerty')— Ace's smoke test. So §5 becomesCREATE TABLE IF NOT EXISTS+ aCREATE INDEX IF NOT EXISTS ix_memory_short_agent, which is already written that way. No DDL problem at all. There is no OperationalError risk.- Real schema confirmed:
memory_longterm(agent, topic, fact, source, confidence, embedding BLOB, created_at, last_accessed)— nocontentcolumn, Atlas correct on that. memory_episodic_archive= 566,memory_metadata= 0 — Atlas correct.- Counts have moved again since Atlas measured: longterm 321 → 324 → 326, episodic 1536 → 1570. The DB is live and still writing. These numbers must not be pinned in a doc — treat row counts as volatile, state them with a timestamp.
- Both forks confirmed divergent, not authoritative-vs-decoy: runtime-home 422 MB
(durable 30 / longterm 326 / episodic 1570) vs
/home/lab/.solos/data428 MB (durable 34 / longterm 309 / episodic 1129). Atlas's "divergent forks of one lineage" is the correct read, and his unit pin is right.config.pycalls/home/lab/.solos/data/solos.dba DECOY — that label is wrong and should be corrected to "divergent fork", for the same reason: calling it a decoy invites someone to delete live data someone is still reading. (Also noteruntime/data/solos.dbis a 0-byte stub — an easy wrong path to write to.)
🔴 RULING 4 — the actual blocker neither doc states: v1 vs v1.1 write scope conflict
This is the one that will break acceptance, and it is not in Atlas's list.
- SPEC:36 — "write only to
memory_longterm+memory_episodic(+ newmemory_short)". - ARCHITECTURE-v3:155 — "v1 write scope:
memory_shortonly.memory_longterm/memory_episodicare read-only, enforced by aWRITE_TABLESallowlist".
Direct contradiction. It also contradicts the SPEC's own definition of done (line 127):
"the solos.db row count for memory_longterm has increased by exactly the number of writes
issued" — impossible if longterm is read-only.
Ruling: the SPEC wins on write scope, because the DoD and the whole sharing-rule proof (§8 step 2, cross-agent recall of a row another agent wrote) depend on it. ARCHITECTURE-v3 is the stale doc here — it is the reverse of the venv situation. Same mtime-logic Atlas applied to the embedder does not apply to the write scope, where the newer doc is simply wrong. Later-doc-wins is a heuristic, not a law. The mtimes: spec 21:56:04, arch 22:52:24.
Concretely for Ace, right now:
WRITE_TABLESmust permitmemory_longtermandmemory_episodic. Do not ship the allowlist as memory_short-only. Do not ship v1 keyword-only.- Keep the
X-Memory-Degradedheader +degraded: trueon the fallback path — that behaviour is still correct as the degraded path, just no longer the default.
🏁 Verdict on the two spec corrections
| # | Correction | Ruling |
|---|---|---|
| 1 | SPEC:41 venv → venv-memory | ✅ YES — live bug: unit points at venv with no mcp; 8437 only up via a hand-run |
| 2 | Schema / table list | ✅ YES to the substance — but memory_short already exists; no CREATE needed, and it is not an OperationalError hazard |
| 3 | (new) WRITE_TABLES scope | 🔴 must be longterm+episodic+short, not short-only — spec DoD requires it |
📌 Actions (Ace owns 1-2, unblocking him now)
- Fix the unit now —
ExecStart→venv-memory/bin/python. This is a 30-second fix and it is the reason the unit is dead while the port is up. - Ship the embedder in v1.1 —
BAAI/bge-small-en-v1.5, 384-dim, cosine, fromruntime/data/fastembedcache. Ace'sconfig.pydefault is already correct; keep it. - Create
memory_shortidempotently (IF NOT EXISTS+ index), don't assume absence. - Write-scope fix per Ruling 4.
- Stop pinning row counts in docs — they changed 3x tonight. Timestamp them or drop them.
✅ Verified
- Live Qdrant
long_term: 404 pts, 384-dim, Cosine, green, re-embed cosine 0.9818 vs MiniLM 0.2301. - venv/venv-memory mcp import test; systemd unit content;
:8437held by hand-run pid 1467960. - Both DB forks: tables, counts,
user_version=0, WAL,freelist=0,memory_shortpresent. memory_longterm.embedding= 0 / 326 non-null — Atlas correct, v1 embeds on write, no backfill needed for correctness. Confirmed on live DB.
⛔ Blocked / not mine to call
- Publish :8437 beyond loopback — still Young's call (SPEC §9), not in this ruling.
- Embedding backfill of the 326 un-embedded longterm rows — agreed trivial in size (~0.5 MB) but it is a long write txn on a live 422 MB shared db. Batched + short-transactioned only, off-peak, and not part of v1 acceptance. Not in this ruling's scope.
- task-dbed2f8e (2-DB fork) — untouched, as Atlas confirmed. Correct fork handling is now recorded here; nothing in the v1 write lane changes for it.
🎯 Tam's Read: bge-small-en-v1.5 is confirmed by measurement, not taste — the 404 vectors already on LAB score 0.98 against it and 0.23 against MiniLM, and 6 existing call sites already pin it, so Mira's money call is confirm-only. Both spec corrections are YES, but the venv one is a live bug (the unit runs a venv with no mcp; 8437 is only up because Ace hand-ran it) and the schema one is half-wrong (memory_short already exists, 1 row, correct DDL — no CREATE risk). The real blocker is one neither doc flagged: SPEC and ARCHITECTURE-v3 directly contradict each other on whether v1 may WRITE memory_longterm, which would make the spec's own definition of done impossible — spec wins, WRITE_TABLES must allow longterm+episodic+short.
Persona audit — all agents, both hosts. One real defect found and fixed.
Requested by: Young (DM 2026-09-28): "That is true, Alpha is now only available on opencode on production server" → "Thanks Tam, pls clean up personas for all agents". Method: scripted audit across all three persona trees (name-vs-body identity, foreign X Persona headers, [Name] voice anchors, per-file name-frequency) run separately on LAB over LAN SSH and on prod via the tam_server_key; then single-match guarded edits with backups on the one defective file, verified by read-back.
solos-memory — spec amendment (Tam ⇄ Sol joint, ref task-dbed2f8e)
Sol reversed my acceptance check #6 ("degraded:true is the steady state"). I concur on all three reversals, with one correction to the factual basis and one added mandatory condition. Ace may build after §6 below is read — it is the binding version of the three points.

