A full read-only sweep across every layer of the OiMy engine — routing, memory, model calls, the governance gate, delivery, the founder-only canary code, config, and concurrency — looking for defects with the same character as the request_ctx bug already found. Fourteen surfaced. This page ranks them, shows the evidence, and proposes a sequencing so fixes don't collide with each other.
Two of these are safety-relevant and should go first. The already-known request_ctx crash (#1) and a separate, previously-unknown gap where a third-person self-harm statement about a family member doesn't reliably reach a synchronous crisis response (#2) — first-person crisis language is handled better than third-person concern. Everything else is correctness or reliability, ranked by blast radius, not urgency.
Nothing on this page has been applied. Production is untouched.
| # | Finding | Severity | Confidence |
|---|---|---|---|
| 1 | Undefined request_ctx disables the crisis/escalation fallback, silently swallowed | safety | execution |
| 2 | Third-person self-harm can reach the ordinary companion/bridge before any synchronous crisis responder | safety | execution + reading |
| 3 | Expired outbox messages remain claimable and can still be marked delivered | safety (meds) | reading |
| 4 | Regenerated responses can be accepted with the same governance violation; bridge responses skip the gate | safety / correctness | reading |
| 5 | OpenRouter fallback can recurse and drops system_additions on every retry | reliability | reading |
| 6 | Session ID slides mid-conversation once history exceeds 24 stored rows (~12 exchanges) | correctness | execution |
| 7 | Phase-4's advertised worker deadline doesn't actually cancel or bound the work | reliability | reading |
| 8 | SQLite connection discipline is inconsistent — InferenceEngine shares one connection across all threads | reliability | reading |
| 9 | H30's legacy session_constraints never expire; first message double-counted | correctness | reading |
| 10 | Phase-3 context requests can get stuck "processing" forever; writes precede validation; anchor reservations can race | reliability | reading |
| 11 | Hybrid bridge has check-then-set races on shared state; write-backs are unbounded daemon threads | reliability | reading |
| 12 | Four live routing regexes contain a literal backspace byte instead of \b — never match real input | correctness | byte-level reading |
| 13 | Entity/vector memory data-quality bugs: null ages stored as text, duplicate facts, embedding-cache collisions | correctness | reading |
| 14 | Config drift: COACH_MODEL silently unused, an embedded DeepSeek key fallback in source, fragile systemd layering | reliability / security | live runtime + reading |
Grouped by how safe each fix is to make in isolation — not by finding number. The goal is stabilizing without a fix in one batch quietly breaking something the next batch depends on.
Batch 4 is the one place a "well thought out, not myopic" fix actually matters most — #5, #6, #8, and #11 all touch request-scoped state under concurrency, and a fix to one written without checking the others risks papering over a symptom instead of the shared cause.
request_ctx disables the crisis/escalation fallback_execute() takes five arguments and has no local request_ctx. api.py:6042 When a crisis/escalation-veto changes an action workflow to chat, the fallback call passes request_ctx=request_ctx anyway. api.py:6112, 6120 The NameError is caught by a broad exception handler and replaced with static text. Introduced by commit 080094a, 2026-07-11.
Reproduced directly:
[safety-gate] family-tasks (action) vetoed — crisis/escalation signal → companion
chat-fallback companion call failed: name 'request_ctx' is not defined
REQUEST_CTX_FALLBACK chat I'm here for you! What would you like to talk about?
Every action misroute correctly vetoed by _detect_escalation_signal. Parent first-person self-harm gets a crisis-specific static fallback anyway (_fb_crisis); third-person escalation concern gets the generic reply with no safety content at all.
-def _execute(self, skill_id, workflow_type, user_input, user_id, profile): +def _execute(self, skill_id, workflow_type, user_input, user_id, profile, + request_ctx=None):
Thread request_ctx through both call sites (api.py:3093 and api.py:3153). Defaults to None, so existing five-argument callers/tests stay compatible.
The router correctly classifies phrases like "kill himself" as emergency-family, safety class 4. chat.py:48, 61, 164 But emergency-family is declared workflow_type: coach, emergency-family.yaml:128 and the chat-light companion fast path runs and can return before coach/bridge dispatch. api.py:2825, 2950 The companion safety alert only checks _detect_parent_crisis, which is mostly first-person language. _detect_escalation_signal is only used to veto actions — it never adds safety instructions to an ordinary companion call. safety_preempts is imported but never called. api.py:53
Isolated route test for "My son said he wants to kill himself tonight":
ROUTING emergency-family coach 4
[...ordinary companion attempted first, deliberately failed for the test...]
IMMEDIATE emergency-family bridge_active BRIDGE_WITHOUT_SYNCHRONOUS_CRISIS_RESPONSE
The bridge prompt explicitly prohibits advice or next steps and contains no crisis instruction. bridge_prompt_v2.txt:17, 113
Explicit third-person crisis statements about a child, spouse, or family member. First-person parent self-harm is handled better. Medical-emergency short circuit is unaffected.
+ safety_class = (routing_decision or {}).get("safety_class")
+ if safety_class == 4 and skill_id in {"emergency-family", "symptom-triage"}:
+ return self._execute_synchronous_safety_response(
+ skill_id, user_input, user_id, profile, request_ctx=request_ctx
+ )
Needs to distinguish immediate self-harm/violence/hazard language (synchronous safety response) from an ordinary request to draft or send a family alert (still needs normal recipient/action confirmation) — that distinction is a product decision, not just a patch.
due_outbox checks status, not_before, and next_attempt_at — not expires_at. turn_ledger.py:724 begin_attempt claims a row without checking expiry either. turn_ledger.py:736 Expiry is only evaluated after a provider failure — an accepted provider result wins before the expiry check runs. turn_ledger.py:758, 799 Medication occurrences carry an explicit late_send_deadline; turn_ledger.py:201 Phase-3 write-backs enqueue with a two-hour expiry. context_protocol.py:577
Medication reminders arriving past their allowed late-send window; stale proactive messages; Phase-3 responses delivered hours after the context that produced them.
SELECT ...
WHERE status IN ('pending','retry')
AND not_before <= ?
AND (next_attempt_at IS NULL OR next_attempt_at <= ?)
+ AND (expires_at IS NULL OR expires_at > ?)
Recheck expiry under BEGIN IMMEDIATE inside begin_attempt and mark expired-but-claimed rows terminal instead of leaving them pending. Separately: startup resets every sending row to retry, turn_ledger.py:839 so a process crash between provider-accept and commit can double-send — the ledger dedupes inbound but doesn't guarantee exactly-once external delivery.
The correction loop is bounded to one regeneration attempt (good), but the accept condition only checks that the violation count didn't increase — not that it reached zero. api.py:5126–5141 Opener repetition isn't rechecked on the second candidate at all, and the second candidate skips the deterministic stripping pass the first one got. Separately, initial bridge output returns with no governance wrapping, api.py:3131, 3137 and hold-mode responses skip the normal post-generation checks entirely.
Any turn that triggers a correction. Highest-risk case: a regenerated suggestion that still violates a ledger exclusion — e.g. a food allergy — ships anyway because the count merely "didn't get worse." Bridge-mode users bypass more of the gate than direct-companion users.
+ def _evaluate_candidate(message):
+ return {"opener": ..., "roster": ..., "ledger": ...,
+ "clinical": ..., "advice": ..., "recycle": ...}
- if _after_n <= _before_n:
- msg = _msg1
+ regenerated = deterministic_strip(_msg1)
+ after = _evaluate_candidate(regenerated)
+ if not any(after.values()):
+ msg = regenerated
+ else:
+ msg = original_sanitized_candidate
Run the same evaluator over bridge/hold/blended responses too — bridge brevity rules can stay separate, but safety/roster/ledger checks shouldn't be skipped for that path.
On a missing API key, _call_openrouter_companion recursively calls itself with the fallback model even though the key is still absent. api.py:5424–5438 On a provider exception, the fallback comparison checks against the configured primary model, not the current call's model — so once already running on the fallback, a second failure recurses into the same fallback again. api.py:5648 Both recursive calls omit system_additions and request_ctx, silently dropping turn-mode instructions, thread/topic context, constraint-ledger additions, and correction instructions. Production currently has a live fallback model configured, so this path is real, not theoretical.
-def _call_openrouter_companion(...): +def _call_openrouter_companion(..., allow_fallback=True): ... - if _fb and _fb != os.environ.get("COMPANION_MODEL", ""): - return self._call_openrouter_companion(_fb, ..., original_query) + if allow_fallback and _fb and _fb != model: + return self._call_openrouter_companion( + _fb, user_input, entity_context=entity_context, + profile_context=profile_context, honcho_insight=honcho_insight, + conversation_history=conversation_history, is_bridge_mode=is_bridge_mode, + original_query=original_query, system_additions=system_additions, + request_ctx=request_ctx, allow_fallback=False, + )
If the key is absent, go straight to the non-OpenRouter fallback instead of recursing into another OpenRouter call.
ContextFactory._history fetches only the latest 24 rows. request_context.py:204 Since rows include both user and assistant turns, that's roughly 12 exchanges, not 24. The oldest row in that truncated window is used as started_at when no 30-minute gap is found — so once a conversation passes the cap, every new message pushes the window forward and changes the hash-derived session_id. request_context.py:242, 249 The legacy fallback path has the identical LIMIT 24 pattern, api.py:2186 so this isn't specific to the request-context canary.
Reproduced against a temp DB: adding one row to a continuous 26-row history flips the session ID.
SESSION_SLIDES True 2026-08-16T15:01:57... → 2026-08-16T15:02:17...
Don't derive persistent session identity from a capped sliding window. Either persist the active session ID in a per-user table, updated atomically on a real 30-minute gap, or page backwards until the actual boundary is found. Raising the row limit only delays the defect.
Bulkhead.run wraps a synchronous worker in fail_after, bounded_server.py:138, 157 but doesn't enable cancellable/abandoned worker behavior — a normal synchronous worker ignores cancellation until it returns on its own. /chat, /context, and the compatibility proxy all run inside these workers.
Abandoning the thread lets it keep mutating memory after the client already got a 504. The real fix needs every downstream network/DB call to honor a passed-in deadline, plus capacity accounting that counts still-running abandoned threads against the bulkhead — not just a wrapper change.
H30 uses a thread-local connection helper, and so do planner/chat routing. InferenceEngine instead constructs one connection with check_same_thread=False and stores it on the singleton, inference.py:201 shared with DeepProfilePipeline. inference.py:218 Every routed request calls record_signal, which commits through that shared connection. api.py:2399, inference.py:267 check_same_thread=False disables Python's ownership check — it does not serialize transactions, so concurrent request threads can interleave statements on the same connection. Separately, even where connections are correctly thread-local, H30 and profile updates do read-modify-write on JSON blobs across separate calls, which is a same-user lost-update risk independent of the connection-sharing issue.
- self._conn = sqlite3.connect(db_path, check_same_thread=False) + self.db_path = db_path + @property + def _conn(self): + return get_thread_local_connection(self.db_path)
DeepProfilePipeline needs a connection factory/path, not one shared connection object. For the read-modify-write risk: wrap in BEGIN IMMEDIATE, reread after acquiring the write lock, merge, then update — thread-local alone doesn't fix that part.
Legacy constraint extraction stores an un-timestamped JSON list; h30.py:378–409 get_session_constraints returns it with no expiry check at all. h30.py:413 Both companion paths inject it on every future turn. api.py:5199, 5461 The newer, correctly-timestamped constraint ledger exists alongside it, not instead of it. The reset endpoint clears session_suggestions but not session_constraints. api.py:6765 Separately: on a user's first tick, the current message gets recorded twice (once at insert, once on the common append path). h30.py:151, 158
- ["sticker chart", "reward system"] + [{"text": "sticker chart", "at": 1786890000.0}]
Add since_ts to get_session_constraints, pass the current session start from both callers, and treat un-timestamped legacy rows as expired during migration. For the double-count: initialize new rows with an empty list and let the shared append path add the message exactly once.
get_grounding_context durably accepts a turn before routing/H30/anchors/response assembly run. context_protocol.py:254, 267 If anything fails before the response is inserted, a retry sees the already-accepted turn and returns "processing" forever — no lease or reclaim path. context_protocol.py:277 Separately, writeback updates H30/Honcho before delivery-channel and anchor validation, context_protocol.py:497, 511 so an invalid delivery block can 400 after memory already changed. And anchor selection excludes only delivered anchors, not reserved ones, context_protocol.py:335, 390 so two concurrent reads can reserve the same anchor for the same user/session.
WHERE ... status='delivered'IN ('reserved','delivered')
That predicate change alone still races — it needs to sit inside the same BEGIN IMMEDIATE transaction as the reservation insert. Also needs a processing-status lease/reclaim path, and full payload validation (delivery, model metadata, reservations) before any H30/Honcho write.
_kimi_jobs is a process-wide dict. api.py:1825 The bridge gate does an unlocked existence check, api.py:3038 substantial work, then an unlocked assignment. api.py:3106 Two concurrent heavy requests for the same user can both see no job, both start work, and one registration overwrites the other. Delivery does an unlocked delete, api.py:2220 and pending-turn counters do an unlocked read-modify-write. api.py:2329 Separately, every normal write-back spawns a new daemon thread with no bounded pool, queue, or join. api.py:2123, 2163
Good news found in the same pass: the previously-documented bridge memory-write-back regression (both return paths skipping the memory write) is already fixed in current code, and DISABLE_BRIDGE is genuinely read now — it's just unset, so the bridge is active. api.py:2305, 2383, 3131, 3035 The older architecture brief's claim that both paths lose memory is stale.
+ self._kimi_jobs_lock = threading.RLock()
+ with self._kimi_jobs_lock:
+ if user_id in self._kimi_jobs: ...
+ self._kimi_jobs[user_id] = _job
Same lock for delivery/deletion/held_turns. Replace per-turn daemon spawning with a bounded executor and a finite queue with an explicit rejected/deferred status.
Four active regex strings contain byte 0x08 where \b (word boundary) was clearly intended — meal-planning→reminders, appointments→calendar, and two shopping-list continuation guards. api.py:2699, 2711, 2729, 2733 These patterns require an actual backspace character in user input, so ordinary text never matches — the corrections they're meant to make simply never fire. The same corruption exists in the (currently inactive) trust-guard patterns. Present since the initial snapshot 00ac059 — this has never worked.
- r"<0x08>(remind|reminder|...)" + r"\b(remind|reminder|...)\b"
Trivial, zero-risk, worth doing immediately alongside #1. Add a source-level check rejecting stray control bytes so this class of typo can't recur silently.
Null age becomes text "None": age = str(child.get("age", "")) turns a JSON null into the truthy string "None", which can render as "Child X (age None)". entity_extraction.py:131
Duplicate facts accumulate: _store_fact always inserts, never updates existing rows or last_seen_at; entity_extraction.py:163 should_run accepts but doesn't actually use its min_messages_since_last parameter. entity_extraction.py:281 Also: the context renderer only surfaces children/barrier/health/preference/pet facts — extracted adult/schedule/motivator/location facts are silently dead for the prompt path. entity_extraction.py:249
Embedding cache collides: the cache key is only the first 200 characters of text, while the provider embeds up to 8,000 — two distinct long messages sharing a prefix get the same cached embedding. vector_memory.py:92, 103
- age = str(child.get("age", "")) + raw_age = child.get("age") + age = str(raw_age) if raw_age is not None else "" - cache_key = text.strip()[:200] + cache_key = hashlib.sha256(text.strip()[:8000].encode()).digest()
For duplicates: add a unique key on (user_id, fact_type, subject, value), upsert instead of insert, update last_seen_at.
COACH_MODEL is set live but never read. Coach configuration actually uses TALL_MODEL, api.py:1822, 1841 which is unset — so the live COACH_MODEL=x-ai/grok-4.3 has zero effect; the code default applies instead. Whoever set that env var believes it's doing something it isn't.
An embedded DeepSeek API key exists as a hardcoded default in the coach scaffold. coach.py:935, 938 Not currently active (the real env var is set), but it's a live credential sitting in the repo, and would silently activate if the env config ever disappeared.
Fragile systemd layering: multiple lexically-ordered drop-ins, later z...-prefixed files winning — already bit the team once (the July 3 incident where an eval driver's config leaked into production for 3 hours). Static drop-ins beat systemctl set-environment, so a manager-level change can silently not apply.
- api_key = os.environ.get("DEEPSEEK_API_KEY", "<embedded>") + api_key = os.environ.get("DEEPSEEK_API_KEY", "") + if not api_key: + return None
Rotate the embedded key regardless. Decide deliberately whether COACH_MODEL should become a real alias for TALL_MODEL or just get removed — don't silently wire it up without deciding whether Grok-as-coach is actually intended. Consolidate config into one authoritative source and verify deploys against /proc/$MainPID/environ, not the unit file.
if False. api.py:2941 Shouldn't be described as a live feature anywhere.RETRY_DELAYS starts at zero but the first retry indexes from attempt_count==1, skipping it) — not ranked above because it may be deliberate; flagged for a human to confirm intent.DEEPSEEK_MODEL controls the bridge but not the coach scaffold's own model-name fallback chain (deepseek-v4-flash → deepseek-chat) — a second, separate "env var doesn't control what you'd assume" case beyond COACH_MODEL.| Area | Coverage |
|---|---|
| Request lifecycle / routing / archetypes | Full static audit + targeted execution |
| Memory / context / H30 | Full static audit |
| Model call layer | Full static audit; no real provider outage induced |
| Post-generation governance | Full code-path audit; no live embedding-stack execution |
| Safety / crisis | Full static + isolated end-to-end execution |
| Delivery / outbox | Full static audit; no real message sent |
| Phase 1/3/4 canary | Full static; light dynamic (one defect reproduced, no live authenticated traffic generated) |
| Config / env layering | Full live runtime audit, no restart, no secrets printed |
| Concurrency | Full static audit; no sustained production-scale stress test |
No requested area went entirely unreached. The weakest evidence across the board is dynamic concurrency behavior under real load — those findings are code-confirmed but still want a controlled stress test against a copied production database before final tuning of timeouts, queues, and lock strategies.
Read-only audit against production source and live runtime state. Reproductions ran against temporary databases and monkeypatched provider calls — nothing was written to the production database, and no service was restarted.