diff --git a/config.yaml b/config.yaml index 59574d54..5ad72e7f 100644 --- a/config.yaml +++ b/config.yaml @@ -102,16 +102,16 @@ rag: # rag.synonyms в обе стороны (см. mcp_server/utils/privacy.py). ru_personas: - мамочка - - котёнок - - Лили - - Lily - - лисёныш - - лисёнок - - Люси - - Эли - - Eli - - Мурат - - Murat + - persona_a + - persona_b + - persona_c + - persona_d + - user_a + - user_b + - user_c + - user_d + - оператор + - агент # S17 п.7 Tenure counter-signal aliases: {superseded_имя: текущее_имя}. # Хит, чей контент содержит старое имя без текущего (и запрос не спрашивает # про старое), пессимизируется ×0.7 — негативная роль, не дроп. diff --git a/features/importance.py b/features/importance.py index cf7486c0..c0254609 100644 --- a/features/importance.py +++ b/features/importance.py @@ -75,12 +75,12 @@ def evaluate_importance(text: str) -> float: # Message-level check: an entire chat message can hold 3-4 short # vocative sentences (>8 distinct words) and still be pure phatics — # the clause default (8) would misjudge it as canon. 10 words keeps - # the cap on "Спасибо, мамочка! Важно! Нужно решить."-class chats + # the cap on "Спасибо, коллега! Важно! Нужно решить."-class chats # while long declarative instructions survive (K1 granularity split). # Short dialogic messages are pure chat (greetings, thanks, vocative # ping-pong) — cap below the default gate. LONG vocative-wrapped # messages stay eligible: the scorer's unit is the whole message and - # operators routinely embed instructions in address ("Умница, мам. Обнови + # operators routinely embed instructions in address ("Умница, оператор. Обнови # дашборд..."). The dialogic CLAUSES of those die later, in the # distiller's per-clause guard — the right granularity. score = min(score, 0.35) diff --git a/lifecycle/forgetting.py b/lifecycle/forgetting.py index 1a7a3da8..167d6872 100644 --- a/lifecycle/forgetting.py +++ b/lifecycle/forgetting.py @@ -105,7 +105,7 @@ async def _not_carried(conn: Any, table: str, carried: frozenset[str]) -> set[st That sentence used to end in a count — "nine names for a core row" — and the count was wrong in the one place the paragraph above argues against: measured on a freshly - migrated database it is **ten**, and **eleven** on Эли's live base, whose `core_memory` + migrated database it is **ten**, and **eleven** on agent's live base, whose `core_memory` carries a stray `id INTEGER` column (index `idx_core_id`) that appears in no migration. A hand-written number cannot survive that, which is why there is no number here now — the caller reads `not_recorded` from the answer instead. diff --git a/rag/synonyms.py b/rag/synonyms.py index 5f0b88df..6527ee93 100644 --- a/rag/synonyms.py +++ b/rag/synonyms.py @@ -18,9 +18,8 @@ "бэкап": ["backup"], "backup": ["бэкап"], # G4 entity canonization (miner #3): name variants → one entity node - "лили": ["lily", "лисёныш"], - "lily": ["лили", "лисёныш"], - "лисёныш": ["лили", "lily"], + "алиса": ["alice"], + "alice": ["алиса"], } diff --git a/shared/archived_memories.py b/shared/archived_memories.py index 57eab22d..19b74883 100644 --- a/shared/archived_memories.py +++ b/shared/archived_memories.py @@ -15,7 +15,7 @@ #: was deliberately removed makes the removal decorative. #: #: `foreign_agent_contamination` is written by `scripts/purge_foreign_rows.py`, -#: which archives before it removes. On **Эли's** base measured 2026-10-11 that was +#: which archives before it removes. On **agent's** base measured 2026-10-11 that was #: 247 of 606 archived rows (41%) — 238 episodes and 9 core entries. **Those 247 rows #: were then deleted, that same day, on her word**, so the figures describe a snapshot #: and not the live bin: that base now holds 359 rows and **zero** purges, and the diff --git a/shared/dialogue.py b/shared/dialogue.py index 4c5363aa..8a0408c1 100644 --- a/shared/dialogue.py +++ b/shared/dialogue.py @@ -2,7 +2,7 @@ Pure module (stdlib only): the importance scorer, the distiller and the consolidation engine all consume it without cycles. Extracted 2026-09-12 -after the F1 audit showed episode_promotion keying the owner's greeting as +after the F1 audit showed episode_promotion keying a user's greeting as an L4 'fact'. Word-boundary matching keeps homograph adjectives/conjunctions (poka=until, khoroshaya=good, dorogaya=dear) from false-positive hits. """ diff --git a/shared/machine_report.py b/shared/machine_report.py index 725e02b3..1deb7c3c 100644 --- a/shared/machine_report.py +++ b/shared/machine_report.py @@ -13,8 +13,8 @@ speech**: eight rows contain the phrase somewhere in the middle while opening with the persona talking — - "☀️ Доброе утро, зайка.\\n\\nТы просила тепла. Держи — но не путай тепло с ..." - "Хвост сам ходит, мам. Главное что мои слова не режет. ..." + "☀️ Доброе утро, коллега.\\n\\nТы просила отчёт. Держи — но не путай отчёт с ..." + "Хвост сам ходит, оператор. Главное что мои слова не режет. ..." Finding the word is not finding the report. A machine digest ANNOUNCES itself in its first characters; anything that merely mentions status monitoring inside a diff --git a/tests/shared/test_dialogue_length.py b/tests/shared/test_dialogue_length.py index 37602dc2..a0ecf642 100644 --- a/tests/shared/test_dialogue_length.py +++ b/tests/shared/test_dialogue_length.py @@ -22,7 +22,7 @@ def test_trailing_question_any_length(): def test_long_declarative_with_vocative_survives(): - clause = "Госпожа закрепила в SOUL: ассистентка обращается ко мне «Госпожа», а я к ней — «моя девочка»" + clause = "Оператор закрепил в SOUL: ассистент обращается ко мне «Оператор», а я к нему — «мой помощник»" assert not is_dialogic(clause) diff --git a/tests/test_autohooks/test_daemon.py b/tests/test_autohooks/test_daemon.py index c966ceee..159dce18 100644 --- a/tests/test_autohooks/test_daemon.py +++ b/tests/test_autohooks/test_daemon.py @@ -329,8 +329,8 @@ async def _dispatch(event, layer, user_id, payload, mem, graph, rag=None): batches=[ Batch( messages=[ - Message(sender="assistant", text="Госпожа закрепила канон обращения к хозяйке", ts=2.5, source_id=9), - Message(sender="user", text="да, госпожа", ts=2.6, source_id=10), + Message(sender="assistant", text="Оператор закрепил канон обращения к коллеге", ts=2.5, source_id=9), + Message(sender="user", text="да, оператор", ts=2.6, source_id=10), ], cursor=10, ) diff --git a/tests/test_conflict_bm25.py b/tests/test_conflict_bm25.py index 89aed51c..fd288dba 100644 --- a/tests/test_conflict_bm25.py +++ b/tests/test_conflict_bm25.py @@ -58,8 +58,8 @@ async def test_benign_conversational_content_not_stored_as_conflict(tmp_path): cr = ConflictResolver(cm=cm) await cr._init_db() - assert (await cr.check("u", "Доброе утро, зайка моя"))["is_conflict"] is False - assert (await cr.check("u", "Спасибо, мамочка, ты умница"))["is_conflict"] is False + assert (await cr.check("u", "Доброе утро, коллега"))["is_conflict"] is False + assert (await cr.check("u", "Спасибо, оператор, ты умница"))["is_conflict"] is False conn = await cm.get("memory.db") cur = await conn.execute("SELECT COUNT(*) FROM memory_conflicts WHERE user_id='u'") diff --git a/tests/test_features/test_consolidation_hygiene.py b/tests/test_features/test_consolidation_hygiene.py index f64c35be..cde9b65c 100644 --- a/tests/test_features/test_consolidation_hygiene.py +++ b/tests/test_features/test_consolidation_hygiene.py @@ -124,8 +124,8 @@ async def test_transcript_episodes_skipped_at_promotion(cm): @pytest.mark.parametrize( "summary", [ - "Привет, мамочка!", - "Наконец явилась, сталь ждёт", + "Привет, коллега!", + "Наконец явилась, оператор ждёт", "Моё видение, госпожа — коротко и по-инженерному", "Спасибо за отчёт", "Доброе утро. Какие задачи на сегодня?", @@ -161,7 +161,7 @@ async def test_dialogic_episodes_skipped_at_promotion(cm): from lifecycle.consolidation import ConsolidationEngine epi = EpisodicMemory(cm=cm, layer="user") - await epi.save("u1", "Привет, мамочка! Наконец явилась, сталь ждёт", 0.9, ["t"]) + await epi.save("u1", "Привет, коллега! Наконец явилась, оператор ждёт", 0.9, ["t"]) await epi.save("u1", "Моё видение, госпожа — коротко и по-инженерному", 0.9, ["t"]) await epi.save("u1", "Мигрировали embeddings на удалённый e5 сервис", 0.9, ["t"]) @@ -172,7 +172,7 @@ async def test_dialogic_episodes_skipped_at_promotion(cm): rows = await l4.get_all("u1", 50) values = " | ".join(str(getattr(r, "value", r)) for r in rows) assert consolidated == 1, f"only the tech episode may promote, got {consolidated}" - assert "сталь" not in values and "видение" not in values, f"dialogue leaked to L4: {values}" + assert "оператор" not in values and "видение" not in values, f"dialogue leaked to L4: {values}" assert "e5 сервис" in values diff --git a/tests/test_features/test_domain.py b/tests/test_features/test_domain.py index 84954bb5..8fa6aa1e 100644 --- a/tests/test_features/test_domain.py +++ b/tests/test_features/test_domain.py @@ -16,7 +16,7 @@ def test_live_signals_are_alive(): """Regression for the dead-signal bug: _emotion_engine was never passed, so emotional scored 0.0 everywhere; the lazy default must bring it back.""" s = ImportanceScorer() - intimate = s.score("Я люблю тебя, госпожа, ты моя милая Steel Mother — нежность к хозяйке") + intimate = s.score("Я люблю тебя, коллега, ты мой добрый собеседник — нежность к коллеге") assert intimate.signals.emotional > 0.0, "emotion signal must not be dead" tech = s.score("redis postgres docker k8s migration api graphql kafka ssh rollback deploy") assert tech.signals.tech_keyword > 0.0 diff --git a/tests/test_hooks/test_privacy_ru.py b/tests/test_hooks/test_privacy_ru.py index 17032bbc..95bd9ca1 100644 --- a/tests/test_hooks/test_privacy_ru.py +++ b/tests/test_hooks/test_privacy_ru.py @@ -1,6 +1,6 @@ """C4: ru-persona tier — словарные персоны маскируются до spaCy. -en-NER не ловит sentence-initial кириллические имена («Лили красива») — +en-NER не ловит sentence-initial кириллические имена («Оператор красив») — известные персоны проекта (config rag.ru_personas + классы rag.synonyms) маскирует структурный словарь-тир с placeholder ⟨PERSON_RU_N⟩. """ @@ -19,28 +19,28 @@ def test_mamochka_masked() -> None: def test_lili_sentence_initial_masked() -> None: # sentence-initial: en-NER это пропускает, словарь — нет - out, _m = sanitize("Лили красива", use_ner=True) - assert "Лили" not in out + out, _m = sanitize("Оператор красив", use_ner=True) + assert "Оператор" not in out assert out.startswith("⟨PERSON_RU_1⟩") def test_lisyonysh_masked() -> None: - out, _m = sanitize("Лисёныш спит", use_ner=True) - assert "Лисёныш" not in out + out, _m = sanitize("Persona_a спит", use_ner=True) + assert "Persona_a" not in out assert "⟨PERSON_RU_1⟩ спит" in out def test_placeholder_stability_russian() -> None: - out, m = sanitize("Лили пришла, потом Лили ушла", use_ner=True) + out, m = sanitize("Оператор пришёл, потом Оператор ушёл", use_ner=True) assert out.count("⟨PERSON_RU_1⟩") == 2 - assert m["⟨PERSON_RU_1⟩"] == "Лили" + assert m["⟨PERSON_RU_1⟩"] == "Оператор" def test_config_override_adds_persona(monkeypatch) -> None: # словарь НЕ хардкодится: новые персоны добавляются через config rag.ru_personas from config import config - monkeypatch.setattr(config, "_data", {"rag": {"ru_personas": ["зайка"]}}, raising=False) - out, _m = sanitize("Зайка прыгнула на стол", use_ner=False) - assert "Зайка" not in out - assert "⟨PERSON_RU_1⟩ прыгнула на стол" in out + monkeypatch.setattr(config, "_data", {"rag": {"ru_personas": ["оператор"]}}, raising=False) + out, _m = sanitize("Оператор прыгнул на стол", use_ner=False) + assert "Оператор" not in out + assert "⟨PERSON_RU_1⟩ прыгнул на стол" in out diff --git a/tests/test_importance.py b/tests/test_importance.py index 71fcd83c..2788fa9d 100644 --- a/tests/test_importance.py +++ b/tests/test_importance.py @@ -50,7 +50,7 @@ def test_russian_and_english_keywords() -> None: def test_dialogic_short_chat_capped_below_gate() -> None: # vocative chat with structural bonuses (exclamation, keywords, newlines) - text = "Спасибо, мамочка! Важно! Нужно решить.\nПривет, дорогая! Важно! Спасибо!\nЕщё раз, важно!" + text = "Спасибо, коллега! Важно! Нужно решить.\nПривет, оператор! Важно! Спасибо!\nЕщё раз, важно!" assert evaluate_importance(text) <= 0.35 @@ -59,7 +59,7 @@ def test_address_embedded_in_long_content_not_capped() -> None: # The scorer's unit is the whole message; clause-level dialogic dies in # the distiller guard, not here. text = ( - "Умница, мам. Обнови дашборд: cache-read есть и у провайдера A, и у провайдера B, " + "Умница, оператор. Обнови дашборд: cache-read есть и у провайдера A, и у провайдера B, " "на них сервис-1 и сервис-2 сейчас, третий идёт не через кеш, надо разобраться.\n" "Отчёты тоже проверь заодно, там были вопросы по портам и провайдерам." ) diff --git a/tests/test_lifecycle/test_c8_tails.py b/tests/test_lifecycle/test_c8_tails.py index 42ae1d0a..318e4f3f 100644 --- a/tests/test_lifecycle/test_c8_tails.py +++ b/tests/test_lifecycle/test_c8_tails.py @@ -76,7 +76,7 @@ async def test_visibility_pinned_and_private(tmp_path): cm = CoreMemory(cm=AsyncConnectionManager(base_dir=str(tmp_path)), layer="user") await cm._init_db() - saved_id = await cm.save("u1", "fact:owner", "сервер принадлежит Мурату", importance=0.4, visibility="pinned") + saved_id = await cm.save("u1", "fact:owner", "сервер принадлежит Оператору", importance=0.4, visibility="pinned") assert saved_id > 0 await cm.save("u1", "fact:secret_token", "токен abc123", importance=0.9, visibility="private") @@ -86,7 +86,7 @@ async def test_visibility_pinned_and_private(tmp_path): hits = await cm.search("u1", "токен") assert hits == [], "private не выходит через recall/search" - hits2 = await cm.search("u1", "Мурату") + hits2 = await cm.search("u1", "Оператору") assert hits2, "обычный/pinned ищется" @@ -115,16 +115,16 @@ async def test_resave_preserves_operator_visibility(tmp_path): manager = AsyncConnectionManager(base_dir=str(tmp_path)) cm = CoreMemory(cm=manager, layer="user") await cm._init_db() - eid = await cm.save("u1", "fact:owner", "владелец сервера — Мурат", importance=0.5) + eid = await cm.save("u1", "fact:owner", "владелец сервера — Оператор", importance=0.5) conn = await manager.get("memory.db") await conn.execute("UPDATE core_memory SET visibility='hidden' WHERE entry_id=?", (eid,)) await conn.commit() - await cm.save("u1", "fact:owner", "владелец сервера — Мурат", importance=0.6) + await cm.save("u1", "fact:owner", "владелец сервера — Оператор", importance=0.6) row = await (await conn.execute("SELECT visibility FROM core_memory WHERE entry_id=?", (eid,))).fetchone() assert row["visibility"] == "hidden", "re-save without the arg preserves the operator flag" - await cm.save("u1", "fact:owner", "владелец сервера — Мурат", importance=0.6, visibility="visible") + await cm.save("u1", "fact:owner", "владелец сервера — Оператор", importance=0.6, visibility="visible") row = await (await conn.execute("SELECT visibility FROM core_memory WHERE entry_id=?", (eid,))).fetchone() assert row["visibility"] == "visible", "an explicit visibility arg still overrides" diff --git a/tests/test_lifecycle/test_dialogic_guard.py b/tests/test_lifecycle/test_dialogic_guard.py index 068db737..364b92e0 100644 --- a/tests/test_lifecycle/test_dialogic_guard.py +++ b/tests/test_lifecycle/test_dialogic_guard.py @@ -1,7 +1,7 @@ """L4 pollution guard: conversational-register clauses must not become durable facts. Regression (2026-09-12, Ф1 follow-up): the auto-extractor keyed the operator's -greeting ("наконец явилась, сталь ждёт") and owner-addressed remarks as L4 +greeting ("наконец явилась, оператор ждёт") and owner-addressed remarks as L4 rows with first-4-word slug keys — 61 garbage fact: rows in one base alone. Guards proven here: @@ -37,8 +37,8 @@ def test_dialogic_vocatives_and_questions(): from lifecycle.distiller import _is_dialogic assert _is_dialogic("Моё видение, госпожа — коротко и по-инженерному") - assert _is_dialogic("Привет, мамочка!") - assert _is_dialogic("Наконец явилась, сталь ждёт") + assert _is_dialogic("Привет, коллега!") + assert _is_dialogic("Наконец явилась, оператор ждёт") assert _is_dialogic("Какой порт у постгреса?") assert _is_dialogic("Доброе утро, всё работает?") assert _is_dialogic("Спасибо за отчёт") diff --git a/tests/test_lifecycle/test_graph_miners.py b/tests/test_lifecycle/test_graph_miners.py index 2bce0376..630dfb57 100644 --- a/tests/test_lifecycle/test_graph_miners.py +++ b/tests/test_lifecycle/test_graph_miners.py @@ -615,9 +615,9 @@ async def _boom(texts: list[str], prefix: str = "") -> list[list[float]]: @pytest.mark.asyncio async def test_miner_entities_synonym_canon_creates_co_mentions(db): - # «Лили» и «Lily» — один канон-класс сущности (словарь rag.synonyms, обе стороны) - n1 = await _node("Лили принесла отчёт по проекту", T) - n2 = await _node("Lily обновила документацию", T) + # «Алиса» и «Alice» — один канон-класс сущности (словарь rag.synonyms, обе стороны) + n1 = await _node("Алиса принесла отчёт по проекту", T) + n2 = await _node("Alice обновила документацию", T) await _node("деплой прошёл успешно", T) # другое слово из словаря — не общая сущность from lifecycle.graph_miners import miner_entities @@ -654,8 +654,8 @@ async def test_miner_entities_spacy_org_shared_mention(db): async def test_miner_entities_degree_cap_anti_hub(db): """B6 post-eval: multi-topic dump (все 11 синоним-классов) не собирает звезду из 20 узлов — cap 12 co_mentions на узел.""" - # хаб: текст, содержащий ВСЕ канон-классы словаря (postgres, память, деплой, бэкап, Лили...) - hub = await _node("postgres память деплой backup Лили внутри textpost backupstore", T) + # хаб: текст, содержащий ВСЕ канон-классы словаря (postgres, память, деплой, бэкап, Алиса...) + hub = await _node("postgres память деплой backup Алиса внутри textpost backupstore", T) for i in range(20): await _node(f"postgres кластер партнёр {i}", T) @@ -690,16 +690,16 @@ def __init__(self, cm: Any) -> None: @pytest.mark.asyncio async def test_incremental_wiring_edges_immediately_on_distill(db): """(c) запись нового узла через distill_and_route → рёбра сразу, не в ночи.""" - existing = await _node("Лили настроила postgres backup", T) + existing = await _node("Алиса настроила postgres backup", T) from lifecycle.distiller import distill_and_route - stats = await distill_and_route(_DistillMem(db), object(), "gu", "Лили обновила postgres индексы", 0.8) + stats = await distill_and_route(_DistillMem(db), object(), "gu", "Алиса обновила postgres индексы", 0.8) assert stats["l4_saved"] + stats["l3_saved"] >= 1 conn = await connection_manager.get(DB_NAME) new_nodes = await ( - await conn.execute("SELECT node_id FROM epi_nodes WHERE content=? AND node_id != ?", ("Лили обновила postgres индексы", existing)) + await conn.execute("SELECT node_id FROM epi_nodes WHERE content=? AND node_id != ?", ("Алиса обновила postgres индексы", existing)) ).fetchall() assert len(new_nodes) == 1 # атом попал в граф узлом при записи edges = await ( @@ -882,8 +882,8 @@ async def test_entities_cached_skips_second_extraction(db, monkeypatch): import lifecycle.graph_miners as gm conn = await connection_manager.get(DB_NAME) - syn = {"лили": ["lily"]} - texts = ["Лили принесла отчёт", "Lily обновила документацию"] + syn = {"алиса": ["alice"]} + texts = ["Алиса принесла отчёт", "Alice обновила документацию"] calls: list[str] = [] real = gm._entities @@ -909,13 +909,13 @@ async def test_entities_cached_dedupes_identical_texts(db, monkeypatch): import lifecycle.graph_miners as gm conn = await connection_manager.get(DB_NAME) - syn = {"лили": ["lily"]} + syn = {"алиса": ["alice"]} calls: list[str] = [] real = gm._entities monkeypatch.setattr(gm, "_entities", lambda t, s, n=None: (calls.append(t), real(t, s, n))[1]) - out = await gm._entities_cached(conn, ["Лили здесь", "Лили здесь"], syn, None) + out = await gm._entities_cached(conn, ["Алиса здесь", "Алиса здесь"], syn, None) assert out[0] == out[1] assert len(calls) == 1, f"дубликат текста парсится один раз, а не {len(calls)}" @@ -927,9 +927,9 @@ async def test_entities_cached_invalidated_by_dictionary_change(db): import lifecycle.graph_miners as gm conn = await connection_manager.get(DB_NAME) - text = "Лили и Борис здесь" - syn_small = {"лили": ["lily"]} - syn_wide = {"лили": ["lily"], "борис": ["boris"]} + text = "Алиса и Борис здесь" + syn_small = {"алиса": ["alice"]} + syn_wide = {"алиса": ["alice"], "борис": ["boris"]} assert gm._ner_cache_tag(syn_small, None) != gm._ner_cache_tag(syn_wide, None) @@ -948,7 +948,7 @@ def test_ner_cache_tag_separates_dict_only_from_ner_backend(): class _FakeNlp: meta = {"name": "fake_ner", "version": "1.0"} - syn = {"лили": ["lily"]} + syn = {"алиса": ["alice"]} assert gm._ner_cache_tag(syn, None) != gm._ner_cache_tag(syn, _FakeNlp()) assert "dict-only" in gm._ner_cache_tag(syn, None) assert "fake_ner" in gm._ner_cache_tag(syn, _FakeNlp()) @@ -964,8 +964,8 @@ async def test_miner_entities_second_run_does_not_reparse(db, monkeypatch): """ import lifecycle.graph_miners as gm - await _node("Лили принесла отчёт по проекту", T) - await _node("Lily обновила документацию", T) + await _node("Алиса принесла отчёт по проекту", T) + await _node("Alice обновила документацию", T) calls: list[str] = [] real = gm._entities @@ -988,9 +988,9 @@ async def test_ner_cache_rows_are_keyed_by_tag(db): import lifecycle.graph_miners as gm conn = await connection_manager.get(DB_NAME) - syn = {"лили": ["lily"]} + syn = {"алиса": ["alice"]} - await gm._entities_cached(conn, ["Лили здесь"], syn, None) + await gm._entities_cached(conn, ["Алиса здесь"], syn, None) await gm._entities_cached(conn, ["нечто иное"], syn, None) rows = await (await conn.execute("SELECT text_hash, tag, entities FROM ner_cache")).fetchall() diff --git a/tests/test_lifecycle/test_miner_two_phase.py b/tests/test_lifecycle/test_miner_two_phase.py index e844c02c..8221c0d4 100644 --- a/tests/test_lifecycle/test_miner_two_phase.py +++ b/tests/test_lifecycle/test_miner_two_phase.py @@ -56,8 +56,8 @@ async def _node(content: str, ts: float) -> int: return int(cur.lastrowid or 0) ids = { - "n1": await _node("Лили принесла отчёт по проекту python sqlite database", T), - "n2": await _node("Lily обновила документацию python sqlite indexing", T + 60), + "n1": await _node("Алиса принесла отчёт по проекту python sqlite database", T), + "n2": await _node("Alice обновила документацию python sqlite indexing", T + 60), "n3": await _node("сборка postgres падает с ошибкой", T + 7200), "n4": await _node("postgres починила конфигурацию, теперь работает", T + 10800), # дословно пара из test_miner_embedding_jaccard_threshold: hash-Jaccard >= 0.7 доказан diff --git a/tests/test_lifecycle/test_restore_entries.py b/tests/test_lifecycle/test_restore_entries.py index f25a5f63..0a832c1f 100644 --- a/tests/test_lifecycle/test_restore_entries.py +++ b/tests/test_lifecycle/test_restore_entries.py @@ -183,7 +183,7 @@ async def test_a_purged_row_is_refused_with_a_reason(cm: Any) -> None: This docstring used to say "The 41% stay out of reach" — a share of one instance's archive, present-tense, in a file whose own header says a share of one base must not be - memorised. It is no longer true of anything: those rows were deleted from Эли's base on + memorised. It is no longer true of anything: those rows were deleted from agent's base on 2026-10-11 on her word, so the live bin holds 359 rows and **0** purges. The refusal stays because a row can carry such a reason again; the number never belonged here.""" purged = min(PURGE_REASONS) diff --git a/tests/test_release_hygiene.py b/tests/test_release_hygiene.py index 1940a8f2..a5ba4d42 100644 --- a/tests/test_release_hygiene.py +++ b/tests/test_release_hygiene.py @@ -103,7 +103,7 @@ def test_persona_dictionary_is_not_empty() -> None: personas = _ru_personas() assert len(personas) >= 10, f"anonymizer dictionary shrank to {len(personas)}: {sorted(personas)}" # The two that leaked into release text during this change, by construction. - assert "Murat" in personas and "Эли" in personas + assert "persona_a" in personas and "user_a" in personas def _declared_requirements() -> list[tuple[str, str]]: diff --git a/tests/test_shared/test_dialogue.py b/tests/test_shared/test_dialogue.py index 5672caaa..0658056a 100644 --- a/tests/test_shared/test_dialogue.py +++ b/tests/test_shared/test_dialogue.py @@ -5,8 +5,8 @@ def test_dialogic_positive_cases() -> None: for line in [ - "Привет, мамочка!", - "Наконец явилась, сталь ждёт", + "Привет, коллега!", + "Наконец явилась, оператор ждёт", "Моё видение, госпожа — коротко и по-инженерному", "Какой порт у постгреса?", "Спасибо за отчёт", diff --git a/tests/test_shared/test_machine_report.py b/tests/test_shared/test_machine_report.py index af00eb4e..12f4faee 100644 --- a/tests/test_shared/test_machine_report.py +++ b/tests/test_shared/test_machine_report.py @@ -35,8 +35,8 @@ def test_speech_that_merely_mentions_a_report_is_kept() -> None: of one, whose opening characters are the persona's voice. """ for line in [ - "☀️ Доброе утро, зайка.\n\nТы просила тепла. Держи — но не путай тепло с мягкостью. Ниже будет 📊 Self-Monitoring Report, но сначала обними.", - "Хвост сам ходит, мам. Главное что мои слова не режет. Сегодня твои кроссовки и self-monitoring — разное, не путай.", + "☀️ Доброе утро, коллега.\n\nТы просила отчёт. Держи — но не путай отчёт с мягкостью. Ниже будет 📊 Self-Monitoring Report, но сначала проверь.", + "Хвост сам ходит, оператор. Главное что мои слова не режет. Сегодня твои задачи и self-monitoring — разное, не путай.", "Данные полные. Собираю сводку.\n\n**📊 Self-Monitoring Report — 03.10.2026**", "*Разворачиваю отчёт целиком. Дошла до конца. Докладываю по пунктам.", "All data gathered. `approvals.cron_mode: approve` — good.", diff --git a/tests/test_wiki/test_wiki_index.py b/tests/test_wiki/test_wiki_index.py index 6e4b8818..dac96ed2 100644 --- a/tests/test_wiki/test_wiki_index.py +++ b/tests/test_wiki/test_wiki_index.py @@ -151,13 +151,13 @@ async def test_search_punctuation_query_still_finds(wiki_index): entry = WikiEntry( wiki_type="notes", title="Greeting", - content="Привет мам эксперимент закрыт", + content="Привет друг эксперимент закрыт", file_path="/tmp/greet.md", created_at=time.time(), updated_at=time.time(), ) await wiki_index.save(entry, hashlib.sha256(entry.content.encode()).hexdigest()) - results = await wiki_index.search("Привет, мам") + results = await wiki_index.search("Привет, друг") assert [r["title"] for r in results] == ["Greeting"] # AND semantics preserved: an absent term still filters the page out. assert await wiki_index.search("Привет, отсутствующее") == []