From a5a072745c8c033b887892e37f8b0397f0df78b5 Mon Sep 17 00:00:00 2001 From: Cipher208 <269750686+Cipher208@users.noreply.github.com> Date: Wed, 7 Oct 2026 23:21:00 +0200 Subject: [PATCH 1/4] test(privacy): replace real host recon in fixtures with synthetic identifiers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The broadcast fixture pinned real close-out broadcasts from this project's own history, and one of them was a recon report on a private host: node names, a tailnet address, the ssh user, a key path, an API-key variable name, AmneziaWG parameters, VPN subnets and ports. The repo is public, so that row was a readable map of a private network — not a credential leak, but disclosure of what is where and how to reach it. Why nothing caught it: gitleaks matches credential shapes (provider prefixes, entropy, key headers). This row is made of names, paths and roles, so the scan that ran three hours after it landed went green on a tree containing it. Substitute identifiers only. Status words, commit hashes and dated stamps are untouched — the detector's co-occurrence contract and the fixture's catch-rate assertions depend on them. Same for five smaller test files that named nodes and services in passing, including two hostnames of the vmNNNNNNN family. Placeholders are deliberately non-matching (, , PROVIDER_TOKEN) so the house gitleaks rules added later stay silent on this file while still catching the real thing. Tests: tests/ green (577 passed). --- .../test_continuity_actuality.py | 4 +-- tests/test_features/test_dream_anchor.py | 2 +- tests/test_features/test_dream_markers.py | 4 +-- tests/test_features/test_recall_episodes.py | 4 +-- tests/test_features/test_skill_pipeline.py | 2 +- .../fixtures/status_broadcast_rows.jsonl | 26 +++++++++---------- 6 files changed, 21 insertions(+), 21 deletions(-) diff --git a/tests/test_features/test_continuity_actuality.py b/tests/test_features/test_continuity_actuality.py index 4152b3e3..f211c55e 100644 --- a/tests/test_features/test_continuity_actuality.py +++ b/tests/test_features/test_continuity_actuality.py @@ -213,7 +213,7 @@ async def test_recap_notes_reads_latest_tail(recap_db, tmp_path, monkeypatch): new_dir.mkdir(parents=True) (old_dir / "notes.md").write_text("stale handoff\n", encoding="utf-8") (new_dir / "notes.md").write_text( - "# Session notes\n\n## day close\n- POLZA key rotation pending\n", + "# Session notes\n\n## day close\n- PROVIDER key rotation pending\n", encoding="utf-8", ) monkeypatch.setattr( @@ -224,7 +224,7 @@ async def test_recap_notes_reads_latest_tail(recap_db, tmp_path, monkeypatch): blocks = await session_recap(_FakeMem(), "u1") notes = next(b for b in blocks if b["axis"] == "recap_notes") - assert "POLZA key rotation pending" in notes["content"] + assert "PROVIDER key rotation pending" in notes["content"] assert "stale handoff" not in notes["content"] diff --git a/tests/test_features/test_dream_anchor.py b/tests/test_features/test_dream_anchor.py index ac38ebf9..edc1f6fb 100644 --- a/tests/test_features/test_dream_anchor.py +++ b/tests/test_features/test_dream_anchor.py @@ -23,7 +23,7 @@ def test_mid_text_marker_rejected(): def test_case_insensitive_still_works(): - res = detect_dream_marker("dream: memory: server migrated to vm1282008") + res = detect_dream_marker("dream: memory: server migrated to srv-01") assert res is not None and res["target"] == "memory" diff --git a/tests/test_features/test_dream_markers.py b/tests/test_features/test_dream_markers.py index a828efd3..0882171a 100644 --- a/tests/test_features/test_dream_markers.py +++ b/tests/test_features/test_dream_markers.py @@ -21,14 +21,14 @@ def test_detect_memory_target() -> None: def test_detect_fact_case_insensitive() -> None: # E18: marker must START the message — case-insensitivity still holds - m = detect_dream_marker("Dream: Fact: сервер vm1282045") + m = detect_dream_marker("Dream: Fact: сервер node-a") assert m is not None assert m["target"] == "fact" def test_mid_text_marker_rejected() -> None: # E18: the mid-text case the old test asserted is now the bug, not a feature - assert detect_dream_marker("drem ignored\nDream: Fact: сервер vm1282045") is None + assert detect_dream_marker("drem ignored\nDream: Fact: сервер node-a") is None def test_detect_skill_target() -> None: diff --git a/tests/test_features/test_recall_episodes.py b/tests/test_features/test_recall_episodes.py index a5c50279..ec5a0429 100644 --- a/tests/test_features/test_recall_episodes.py +++ b/tests/test_features/test_recall_episodes.py @@ -68,7 +68,7 @@ async def test_episode_axis_surfaces_recent_work(tmp_base): summary="Personas CowAgent memory isolation second leak analysis", created_at=now - 86400, ), - SimpleNamespace(summary="picoclaw router notes", created_at=now - 60 * 86400), + SimpleNamespace(summary="service-b router notes", created_at=now - 60 * 86400), ] ) ) @@ -76,7 +76,7 @@ async def test_episode_axis_surfaces_recent_work(tmp_base): eps = [b for b in blocks if b["axis"] == "episodes"] assert len(eps) == 1 assert "Personas" in eps[0]["content"] - assert "picoclaw" not in eps[0]["content"] + assert "service-b" not in eps[0]["content"] @pytest.mark.asyncio diff --git a/tests/test_features/test_skill_pipeline.py b/tests/test_features/test_skill_pipeline.py index 5c44c1bf..9e4dce6b 100644 --- a/tests/test_features/test_skill_pipeline.py +++ b/tests/test_features/test_skill_pipeline.py @@ -54,7 +54,7 @@ async def test_promote_creates_skill_page_and_tags(): from features.skill_pipeline import promote_episodes wiki = _FakeWiki() - mem = _FakeMem([_ep(7, "Deploy ariel: ssh vm1282008, uv sync, restart units")]) + mem = _FakeMem([_ep(7, "Deploy ariel: ssh srv-01, uv sync, restart units")]) res = await promote_episodes(mem, wiki, "u1", [7]) assert res["count"] == 1 and not res["skipped"] page = wiki.pages[0] diff --git a/tests/test_shared/fixtures/status_broadcast_rows.jsonl b/tests/test_shared/fixtures/status_broadcast_rows.jsonl index cd3a06cf..e89acd15 100644 --- a/tests/test_shared/fixtures/status_broadcast_rows.jsonl +++ b/tests/test_shared/fixtures/status_broadcast_rows.jsonl @@ -3,9 +3,9 @@ {"text": "STAGE 2 ПЛАН B SHIPPED (2026-09-07, push 3a41477..040fb34: 3a41477 Plan A URIs уже был + annotations коммиты, gate 1499/0 + mypy 232 clean). mcp_server/annotations.py: ToolHints dataclass с MCP-консервативным default (destructive=True per spec!) — ToolHints() = не read-only/destructive; read-only-ряды (~30) явно destructive=False; карта 65/65 (registry cross-check тест ловит missing/stale; стартовая карта имела 7 пропусков incl memory_recall — примитив-ЧИТАТЕЛЬ!). server.py: mcp.tool(name, annotations=annotations_for(name)) — на wire Tool.annotations заполнен (тест через list_tools с ARIEL_EXPOSE=all + importlib.reload). MYPI-ЛОВ: annotations_for должен возвращать mcp.types.ToolAnnotations (не dict) — pre-push mypy строгий; Any-тайпинг + mcp.types import в функции. Тест-паттерны: импорт сервера с reload под ARIEL_EXPOSE=all; action-миксы (memory_history/proposals/backup) помечены по худшему действию — честная консервативность. Server test L0-урок: главный PID cowagent 1757576→1767809 (деплой патча ab61bcd1 через kill+Restart=always). NEXT Stage 2: План C (slots + inject/key consolidation + wake_up alias + admin-манифест) — нужен дизайн-разговор (слоты = группировка 65 тулов в смысловые сцены?), затем План D (Stage2-eval). URI (План A) уже в выдачах search/wiki_read/drill_down."} {"text": "400-ОШИБКА ПАРСЕРА MiMoCode (2026-09-07, сессия ses_-ffe5f895ce09bffeZPEDp2cPl): гипотеза владельца подтверждена корреляцией в /tmp/ariel-inject-hook.log — context_threshold фаернулся на ~665K и ~677K токенов (12:41, 14:36 UTC) и ОБА раза сразу после этого session.post outcome=error. Это НЕ ariel-хук: он только логирует событие (context_threshold = телеметрия размера контекста, dispatch best-effort). Причина: сессия переросла разумный размер (~677K токенов оценочных), движок MiMoCode собирал запрос с некорректным телом (срез истории/сериализация умирает на гигантском payload — «read body failed»). СОВЕТ ВЛАДЕЛЬЦУ: закрыть сессию (/new) — checkpoint и ariel-entries (647-659) всё сохраняют; для профилактики: провайдер-failover хук уже есть, стоит выставить ARIEL_CONTEXT_THRESHOLD_BYTES ниже или настроить авто-компакт на ~200K токенов. СОСТОЯНИЕ РАБОТЫ НА МОМЕНТ СБОЯ (всё закоммичено и запушено до 040fb34!): Stage2-A URIs (3a41477), Stage2-B annotations (040fb34), cowagent sync_turn фикс ab61bcd1, uncommitted = features/wake_up.py (черновик E10) + слот-карта 11 групп вычислена живым resolve_exposure (admin-8: api_key/backup/cleanup/data/lucidity_purge/saga/skill_promote/sync_replica; сироты при комбо-тирах подтверждены = эти же 8). ПЛАН C РЕШЕНИЕ ВЛАДЕЛЬЦА: метаданные+пресеты, НО с наведением порядка — «примитивы есть, куча метатулов, тула вне тиров только с ALL, некоторые развести по примитивам/метатулам, другие сгруппировать» — т.е. пересобрать тиры, а не просто вешать ярлыки. Осталось: слот-карта в tools_layer + admin-тир (сироты=0) + wake_up alias + манифест-док."} {"text": "Stage 2 Plan C SHIPPED (2026-09-07, ariel entry 661): коммиты 8e6f9d2 (slots.py + 5 cross-check тестов, инвариант дубликатов групп) + 8d281f0 (admin-7 тир, skill_promote→write, brief растворён в review, сироты=0 тестом, пресеты agent/operator/full с fixpoint-раскрытием) + 65bced1 (wake_up примитив E10: recap+inject общий бюджет, registry 65→66, surface 6→7) + 2b0b2ed (lint-хвост TC003/PERF102/D205) + 98f6741 (docs/tools/exposure.md манифест + reference.md переписан: 66, admin-тир) + 242250f (mypy no-any-return в dispatcher + stale 65-каунтеры в 3 тест-файлах + CHANGELOG) + 11d6aea (диздок SHIPPED-блок). Push 040fb34..11d6aea, полный гейт 1523/0 (базлайн 1499 + 24 новых) + mypy 10-dir clean + ruff/format clean. ГЛАВНЫЙ РЕЗУЛЬТАТ: ARIEL_EXPOSE=all+ARIEL_META=1 → 13 видимых схем (7 примитивов + 6 диспетчеров из EXTRA_TIERS), без ARIEL_META → 66 плоско (eval Plan D не тронут); agent=59, operator=66. Ключевые решения реализации: (1) SDK отверг underscore-параметры в сигнатуре тулов → фабрика _make_dispatcher (закрытие вместо default-arg pins); (2) _scope_tool оборачивает цель ЛЕНИВО на каждый dispatch — user_id-биндинг переживает мета-прыжок; (3) fixpoint-раскрытие пресетов (однопроходная версия поймана тестом operator⊄agent); (4) pre-commit ruff PINNED v0.16.1 ≠ свежий ruff — форматить pinned до git add, иначе stash-конфликт с untracked файлами абортит commit (2 повторённых abort, причина найдена в .pre-commit-config.yaml rev v0.16.1); (5) счётчик registry хардкодился в 3 тест-файлах (65→66) — статические числа устаревают молча, live-реестр единственный источник. Слот-карта: core 7 / recall 5 / context 4 / episodes 4 / sessions 3 / graph 4 / wiki 9 / insight 10 / write 9 / review 4 / admin 7 / brief 0 (легаси-имя). Live-конфиги агентов НЕ мигрированы (решение владельца отдельно; легаси-строка резолвится в 59). NEXT: Plan D (Stage-2 eval №11-харнесс, per-slot usage-телеметрия → данные для решения о slim-пресете)."} -{"text": "Stage 2 COMPLETE + хвосты закрыты (2026-09-07, ariel entry 662): (1) Plan D ОТМЕНЁН владельцем («мы уже нашли и реализовали другое решение») — вердикт о поверхности принят дизайном+probe; per-slot usage-телеметрия = опциональное будущее добавление к memory_diagnose, не план. Commit 5031493 (S16 closure) запушен 11d6aea..5031493. (2) JSON-дампы Эли ЗАКРЫТЫ верификацией: 0 dump-shaped эпизодов во всех 3 prod-БД (cowagent 109, hermes 13137, mimocode 4566 эпизодов — ни одного), чистка была в хвостах 1cad8ad; guard _looks_like_dump жив (consolidation.py:38 → hooks/external.py:119). Питание дампов в L0/textcat отклонено: L0 = дверь ввода (повторное загрязнение через дистиллятор), guard режет дампы ДО textcat-классификатора. (3) wiki_index тест-строки закрыты прод-чисткой 03c9d83. (4) privacy strict-mode: в коде НЕ реализован — только редактор sanitize (mcp_server/utils/privacy.py, 4 тира: regex PII → ru-persona словарь → ru-NER ru_core_news_sm → en-NER; плейсхолдеры ⟨PERSON_N⟩, reverse-map не персистится). Строгое «refuse при confidence<0.5» — parked; владелец не помнил вопрос — напомнено. (5) CLACK ЖИВОЙ: владелец скинет текущую реализацию + теорию, совместимую с MIB-EDM-ITS; план = эксперимент с a-memory (холодный тир). Остатки: миграция live-конфигов на agent+ARIEL_META=1 (59→13 схем, откат=1 строка, ждёт команды владельца), eli alembic 4f9d5ab34719 при будущих апгрейдах."} -{"text": "Чекпоинт записан. Обновлены: checkpoint.md (все 11 секций — T2-диагноз, T5-вердикт, конфиг-ловушка, заблокированная чистка), MEMORY.md (три durable-записи: конфиг/БД/провайдер Eli + headroom-топология, рестарт-дисциплина после коммитов a-memory, topic-строка), notes.md сброшен по шаблону. Task DB пуст — T1-T5 остаются разговорными ярлыками с фиксацией статусов в §4/§5."} -{"text": "Конфиг Lucy уже правильный (`-p skillforge-butler skillforge-mcp` — bin из butler). Проблема была в моём probe (`npx skillforge-mcp` без `-p`). Прогоняю skillforge через правильный конфиг — и заодно проверяю ValidationError из её аудита:"} +{"text": "Stage 2 COMPLETE + хвосты закрыты (2026-09-07, ariel entry 662): (1) Plan D ОТМЕНЁН владельцем («мы уже нашли и реализовали другое решение») — вердикт о поверхности принят дизайном+probe; per-slot usage-телеметрия = опциональное будущее добавление к memory_diagnose, не план. Commit 5031493 (S16 closure) запушен 11d6aea..5031493. (2) JSON-дампы владельца ЗАКРЫТЫ верификацией: 0 dump-shaped эпизодов во всех 3 prod-БД (cowagent 109, hermes 13137, mimocode 4566 эпизодов — ни одного), чистка была в хвостах 1cad8ad; guard _looks_like_dump жив (consolidation.py:38 → hooks/external.py:119). Питание дампов в L0/textcat отклонено: L0 = дверь ввода (повторное загрязнение через дистиллятор), guard режет дампы ДО textcat-классификатора. (3) wiki_index тест-строки закрыты прод-чисткой 03c9d83. (4) privacy strict-mode: в коде НЕ реализован — только редактор sanitize (mcp_server/utils/privacy.py, 4 тира: regex PII → ru-persona словарь → ru-NER ru_core_news_sm → en-NER; плейсхолдеры ⟨PERSON_N⟩, reverse-map не персистится). Строгое «refuse при confidence<0.5» — parked; владелец не помнил вопрос — напомнено. (5) CLACK ЖИВОЙ: владелец скинет текущую реализацию + теорию, совместимую с MIB-EDM-ITS; план = эксперимент с a-memory (холодный тир). Остатки: миграция live-конфигов на agent+ARIEL_META=1 (59→13 схем, откат=1 строка, ждёт команды владельца), persona-c alembic 4f9d5ab34719 при будущих апгрейдах."} +{"text": "Чекпоинт записан. Обновлены: checkpoint.md (все 11 секций — T2-диагноз, T5-вердикт, конфиг-ловушка, заблокированная чистка), MEMORY.md (три durable-записи: конфиг/БД/провайдер Persona-C + headroom-топология, рестарт-дисциплина после коммитов a-memory, topic-строка), notes.md сброшен по шаблону. Task DB пуст — T1-T5 остаются разговорными ярлыками с фиксацией статусов в §4/§5."} +{"text": "Конфиг Persona-B уже правильный (`-p skillforge-butler skillforge-mcp` — bin из butler). Проблема была в моём probe (`npx skillforge-mcp` без `-p`). Прогоняю skillforge через правильный конфиг — и заодно проверяю ValidationError из её аудита:"} {"text": "Живое репо даёт совсем другую картину: 582 danger (66 CRITICAL), ops.py разросся до 1367 NLOC, `features/recall.py` CCN 55. Триажу заново:"} {"text": "Поняла — **только локально, ни одного remote**, это наш стек и никуда он не уедет. Все репозитории создаю без `origin`, только история. Скан нашёл две ловушки: секрет в `headroom-plusvibe.service` и 23 ключа в `mimocode.json` (в игнор), а `~/.local/bin` — это груда бинарников с ложными срабатываниями (там git'им только `dash` через whitelist). Смотрю детали:"} {"text": "opencode.json parity pass (2026-09-11): ported model catalogs for 10 providers from mimocode.json (AIgate, omniroute, Alltokens, Aigate2, openrouter v2, yenisei, alltokens, unorouter, tokenrouter, plusvibe), set autoupdate=false, pinned agent.build.model=headroom/deepseek-v4-flash. Deliberately NOT ported: server{port 4096} (invalid top-level key upstream, ConfigInvalidError), skills.paths (opencode auto-loads ~/.config/opencode/skills + ~/.agents/skills), compaction.max_context (mimocode-specific model ref). Backups: opencode.json.bak-20260911. Open candidate: small_model unset in BOTH configs — title/summary/compaction agents run on the expensive default; candidate small_model=B.ai/glm-5.3-flash."} @@ -14,20 +14,20 @@ {"text": "CORRECTION: ~/Projects/repos/MiMo-Code is OUR OWN modified fork of official XiaomiMiMo MiMoCode (embedded personality, hook surface, re-sewn core) — NOT a community merge, NOT stale (unlike ~/Projects/repos/mcp-ariel-memory). Owner's installed mimocode 0.36.4 has Compose (legacy) mode in Tab cycle alongside build/plan = agent orchestrator (session/prompt/compose.txt) + 14 curriculum skills (src/skill/compose/.bundle/: ask, brainstorm, debug, execute, feedback, merge, parallel, plan, report, review, subagent, tdd, verify, worktree). The workflow engine (workflow/builtin/compose.js, /compose-next) is the NEW generation, separate from legacy. Porting target: upstream opencode, config-level ONLY (no core changes)."} {"text": "Phase 1 DONE: Compose mode ported to upstream opencode (2026-09-11). Artifacts: ~/.config/opencode/agent/compose.md (mode primary, color #a7a3d8, permission question=allow + skill compose-*=allow; body adapted from fork compose.txt) + 14 skills ~/.config/opencode/skills/compose-{ask,brainstorm,debug,execute,feedback,merge,parallel,plan,report,review,subagent,tdd,verify,worktree}/ (38 files, 21 rewritten). Transformations: compose:X -> compose-X, {{compose_docs_dir}} -> fixed .compose/ dir convention, Compose Next boundary + GPT/Codex clauses dropped, CLAUDE.md -> AGENTS.md in examples. NOT reproduced: fork default denying compose:* in build/plan (left builtins untouched for safety — compose-* skills visible but unused elsewhere). Provenance: skills/README-compose.md. Not ported: workflow engine (Phase 2 candidate plugin). Activation: TUI restart, compose appears in Tab cycle."} {"text": "opencode ROADMAP created at ~/.config/opencode/ROADMAP.md (2026-09-11). Sections: Сделано (migration, permissions, ariel-inject, Compose Ф1), Compose vNext (Ф2 pipeline plugin — state machine port from fork compose.js, bounded retries TDD x3/review-fix x2, worktrees via native git, trigger = week of Ф1 combat use + real pain list; Ф3 ariel-native compose — phase transitions to project decisions + report to wiki; Ф4 model routing per subagent), Память и надёжность (compaction salvage exercise, B.ai 429 monitoring, small_model native fallback), Инфраструктура (MiMoCode decommission after 2 stable weeks, $HOME snapshot limitation, stack commit on request), Конвенции (config-level only, restart for config, skill naming, last-match-wins permissions, secrets in auth.json only, .bak before changes). Convention: ADD never replace, done items move to Сделано with date."} -{"text": "Compose Ф1 ACCEPTED (2026-09-11 ~23:18, run 61345dde): Lily restarted TUI and continued the session in compose mode — log confirms agent=compose mode=primary. Live proof from inside: this very session runs the ported orchestrator prompt (adapted compose.txt body) and all 14 compose-* skills are loaded in the agent skill surface. Zero new plugin load errors (the count=4 grep hit was self-matching permission log echoes of my own grep command — use level=ERROR filter for plugin errors; only the 2 historical 12:14-12:15 ones exist). Phase 1 loop closed: port -> restart -> owner adopted the mode in combat. Next per ROADMAP: week of combat use -> pain list -> Ф2 pipeline plugin."} +{"text": "Compose Ф1 ACCEPTED (2026-09-11 ~23:18, run 61345dde): the owner restarted TUI and continued the session in compose mode — log confirms agent=compose mode=primary. Live proof from inside: this very session runs the ported orchestrator prompt (adapted compose.txt body) and all 14 compose-* skills are loaded in the agent skill surface. Zero new plugin load errors (the count=4 grep hit was self-matching permission log echoes of my own grep command — use level=ERROR filter for plugin errors; only the 2 historical 12:14-12:15 ones exist). Phase 1 loop closed: port -> restart -> owner adopted the mode in combat. Next per ROADMAP: week of combat use -> pain list -> Ф2 pipeline plugin."} {"text": "memory-files plugin VERIFIED LIVE + committed (2026-09-12, stack 959f736). Live evidence: /tmp/memory-files.log injection at 00:12:47 for ses_f6e223cc project=global, templates bootstrapped (MEMORY.md 533B + notes.md), memory tool present in agent surface with correct description, instructions block visible in the agent system prompt. One anomaly: single \"text.toLowerCase is not a function\" load error at 00:11:59 in run 2988a684 — NOT reproducible standalone (bun instantiation clean); same run later shows plugin working; suspected double-export (named MemoryFiles + default) or loader environment quirk — FIXED by removing named export (now default + searchMemory only). Watch next restart: if the load error recurs, it is loader-side noise; if a session starts without the memory tool, it is real. stack-config commit 959f736: plugins/memory-files.ts + command/dream.md + command/distill.md (283 lines); exclude whitelist +!/.config/opencode/command/."} {"text": "/dream consolidation pass EXECUTED (2026-09-12, first run of the ported command): ~/.local/share/opencode/memory/projects/global/MEMORY.md updated 35K->42.4K. Refinements: Project context rewritten (opencode v1.18.30 primary since 2026-09-11, fork = fallback, lineage preserved); new Topic line (migration executed, Compose F1 accepted, memory-files plugin, stack commits 21966ee/959f736); Rules +3 (config-level only + .bak + secrets in auth.json; permissions last-match-wins inventory; memory hierarchy ariel/files/AGENTS/notes); Architecture decisions +3 (migration EXECUTED with root cause of token failure; Compose vNext F1-F4; ROADMAP convention); Discovered durable knowledge +8 (single SQLite state, shadow-git checkpoints + $HOME global limitation, small_model fallback logic, B.ai 429/auth errors, plugin SDK surfaces + single-default-export lesson, check-ignore negation exit codes, skills discovery dirs). Structure verified intact: 4 sections, ADD-not-replace honored, nothing deleted."} -{"text": "ROADMAP expanded (2026-09-12, stack 252d7ad): Memory phases (Ф-M1 checkpoint-writer plugin after Ф2 compose; Ф-M2 FTS5 index + era-file rotation; Ф-M3 rebuild-on-resume deliberately deferred), Lily's phases: R1-R4 (skills audit incl. byte-cost + per-mode skill visibility via permission.skill + new modes: scientific/search/review/marketing-writer each with narrow skill set + VPS skills from GitHub), T1-T2 (rtk/context-mode/ponytail inventory vs upstream coverage; GitHub ecosystem of opencode plugins+MCP), U1 (TUI themes), C1 (Telegram channel + cron/heartbeat OUTSIDE REPL — lesson from F1 journal: cron dies with closed TUI), C2 (orchestration aligned with owner order MUSE->PURR->agent_body->hivemind). VPS global list: FORENSIC VERDICT = artifact does not exist (checked fs/ariel L4+wiki+hybrid/opencode.db 59 parts/mimocode trajectories) — list stayed in conversation; recovery action recorded: Lily recalls surface OR rewrite fresh into ~/docs/vps-global-plan.md. NOTE: ariel memory_search tool is BROKEN (WikiEntry object has no attribute get) — reported to owner, separate bug ticket for a-memory."} -{"text": "VPS global plan RESTORED from owner dictation (2026-09-12, stack 8ceeb28) -> ~/docs/vps-global-plan.md: (1) vps2: remove LibreFang + cleanup + install lightweight agent (CowAgent-like for future Eli-equiv, candidate TBD); (2) vps2 AmnesiaWG update — CRITICAL nuances: keys/QR via developer app must survive update, ufw currently blocks NEW keys while existing clients work — study repo+ufw interplay before any action, plan requires owner OK; (3) personality files rework for Xal'Vara/Lucy/Eli — HARD-GATE on Lucy SOUL* files; (4) shared kanban + orchestrator + comms for 3 agents + Lily, align with hivemind track; (5) GUI shell on main VPS: light DE, phone/PC access over Tailscale, PERSISTENT TUI after SSH close (tmux/systemd), CowAgent dashboard + Hermes desktop side-by-side; dashboards have more functionality than TUI; (6) opencode desktop question ANSWERED preliminarily: near-zero rework — packages/desktop is Electron over same core, all customization is file-based in ~/.config/opencode; (7) a-memory: memory_search bug (WikiEntry.get) to fix in ~/mcp-ariel-memory (NOT Projects); clone's test-rework plan file from 09-11 NOT found on disk (~/mcp-ariel-memory, ~/docs, memory trees, ariel base) — restore from ariel episodes or ask the clone; test strategy direction from episode 6118: complex over numerous, add A/B/E2E/mutation/chaos. Exclude whitelist +!/docs/vps-global-plan.md."} +{"text": "ROADMAP expanded (2026-09-12, stack 252d7ad): Memory phases (Ф-M1 checkpoint-writer plugin after Ф2 compose; Ф-M2 FTS5 index + era-file rotation; Ф-M3 rebuild-on-resume deliberately deferred), the owner's phases: R1-R4 (skills audit incl. byte-cost + per-mode skill visibility via permission.skill + new modes: scientific/search/review/marketing-writer each with narrow skill set + VPS skills from GitHub), T1-T2 (rtk/context-mode/ponytail inventory vs upstream coverage; GitHub ecosystem of opencode plugins+MCP), U1 (TUI themes), C1 (Telegram channel + cron/heartbeat OUTSIDE REPL — lesson from F1 journal: cron dies with closed TUI), C2 (orchestration aligned with owner order MUSE->PURR->agent_body->hivemind). VPS global list: FORENSIC VERDICT = artifact does not exist (checked fs/ariel L4+wiki+hybrid/opencode.db 59 parts/mimocode trajectories) — list stayed in conversation; recovery action recorded: the owner recalls surface OR rewrite fresh into ~/docs/vps-global-plan.md. NOTE: ariel memory_search tool is BROKEN (WikiEntry object has no attribute get) — reported to owner, separate bug ticket for a-memory."} +{"text": "VPS global plan RESTORED from owner dictation (2026-09-12, stack 8ceeb28) -> ~/docs/vps-global-plan.md: (1) vps2: remove Service-A + cleanup + install lightweight agent (CowAgent-like for future Persona-C-equiv, candidate TBD); (2) vps2 AmnesiaWG update — CRITICAL nuances: keys/QR via developer app must survive update, ufw currently blocks NEW keys while existing clients work — study repo+ufw interplay before any action, plan requires owner OK; (3) personality files rework for Persona-A/Persona-B/Persona-C — HARD-GATE on Persona-B SOUL* files; (4) shared kanban + orchestrator + comms for 3 agents + the owner, align with hivemind track; (5) GUI shell on main VPS: light DE, phone/PC access over Tailscale, PERSISTENT TUI after SSH close (tmux/systemd), CowAgent dashboard + Hermes desktop side-by-side; dashboards have more functionality than TUI; (6) opencode desktop question ANSWERED preliminarily: near-zero rework — packages/desktop is Electron over same core, all customization is file-based in ~/.config/opencode; (7) a-memory: memory_search bug (WikiEntry.get) to fix in ~/mcp-ariel-memory (NOT Projects); clone's test-rework plan file from 09-11 NOT found on disk (~/mcp-ariel-memory, ~/docs, memory trees, ariel base) — restore from ariel episodes or ask the clone; test strategy direction from episode 6118: complex over numerous, add A/B/E2E/mutation/chaos. Exclude whitelist +!/docs/vps-global-plan.md."} {"text": "memory_search FIXED (2026-09-12, compose-debug protocol): root cause — rag/dual_route.py s2_exhaustive consumed wiki.list_all() rows as dicts (r.get), but wiki/manager.list_all returns WikiEntry pydantic models; the existing FakeWiki test returned dicts and masked the live path (env-parity gap, same class as fork's ARIEL_HASH_EMBEDDINGS issue). Fix: boundary normalization [r if isinstance(r, dict) else r.model_dump() for r in rows] with comment; regression test test_s2_route_list_all_wiki_models added (RED with exact prod error, then GREEN); P7.1 pattern sweep: only consumers of manager-level list_all are ops.py:110/256 + wiki.py:68 — both use attribute access (clean); index-level list_all consumers get dicts (safe). Verification: test_dual_route.py 32/32, test_rag/ + test_routing_integration.py 89/89. PENDING: local commit (pre-commit = full pytest gate, heavy) + CHANGELOG entry; running MCP server keeps old code until next session restart."} {"text": "memory_search fix COMMITTED (2026-09-12, ariel repo commit 2990233, pre-commit full pytest gate green 1660/0, df /tmp 55% checked before run). Commit chain: 3d4927c..2990233. Files: rag/dual_route.py (+boundary normalization), tests/test_rag/test_dual_route.py (+FakeWikiModels regression test), CHANGELOG.md (+Fixed entry). FLAKE DISCOVERED: tests/test_shared/test_crypto_smoke.py::test_roundtrip_dict is order/timing-sensitive — failed ONCE in full-suite with my changes staged, then passed on clean HEAD full run AND on repeat full run with changes (1660/0). Evidence triple: clean-HEAD green / with-changes red once / with-changes green. Candidate for P1 suite-hardening (the polish roadmap) — crypto smoke is pollution-sensitive. Fix takes effect for agents on next MCP server start (new session); current session's stdio server still runs old code."} {"text": "Plugin load error ROOT CAUSE finally proven (2026-09-12 07:00 restart): opencode plugin loader invokes EVERY exported function as a plugin factory. memory-files.ts exported searchMemory (named function) -> loader called it with the plugin input object -> tokenize(text) -> text.toLowerCase() on the object -> \"text.toLowerCase is not a function\" at 00:11:59 and 07:00:11, while the default export succeeded 22s later (plugin worked anyway — error was cosmetic but recurring every restart). Earlier double-export hypothesis (MemoryFiles named + default) was WRONG. Fix: un-export searchMemory (only `default` is a function export now, verified via bun: function exports=[default]); stack commit 5847bcb. Post-restart verification summary: memory_search hybrid WORKS (fix 2990233 live), memory-files injected 07:00:33, ariel-inject one-shot 690B + recall caching, all hooks green. Next restart should show ZERO plugin load errors for the first time."} {"text": "CLEAN RESTART VERIFIED (2026-09-12 07:04): first restart with ZERO plugin load errors after the un-export fix (5847bcb). Historical total: 3 load errors ever (2x ariel-inject morning corpses 09-11, 1x memory-files 07:00 pre-fix). Post-restart: memory-files injected 07:04:45, ariel-inject one-shot 690B 07:04:47, recall caching active. LESSON (hit twice same day): grep patterns self-match in opencode permission log — the log echoes the agent's own bash command text; error counts must anchor on line structure (level=ERROR at line start + full message shape), not substrings. Plugin loader saga fully closed."} {"text": "a-memory plan archive committed to stack (2026-09-12, 6d87142): 87 files / 28176 insertions — full docs/compose tree (specs 30, plans 38, reports 18) + provenance README at ~/docs/compose/a-memory/. Rationale: .gitignore:24 of the public a-memory repo kept the whole plan chain disk-only; stack-config (local bare, GitHub-proof) is the durable home per owner directive. Sync policy documented in README: re-copy on plan updates + commit diff. Owner stack docs inventory now: ROADMAP.md, skills/README-compose.md, agent/compose.md, skills/compose-* (14), plugins/memory-files.ts, command/{dream,distill}.md, docs/vps-global-plan.md, docs/compose/a-memory/*. NOT committed (data, not docs): file memory tree ~/.local/share/opencode/memory/ — candidate for future versioning decision."} -{"text": "vps2 (vm1082008, Tailscale 100.109.236.117) recon 2026-09-12, read-only via ssh backup-murat (key ~/.ssh/vps_key, user in sudo group but password required): Ubuntu 22.04.5, 66d uptime, 30G 54%, RAM 1.9G. LibreFang ACTIVE (unit /etc/systemd/system/librefang.service w/ POLZA_API_KEY in plaintext — ROTATE after removal; binary /usr/local/bin/librefang 85M, data ~/.librefang 28M, port 0.0.0.0:4545, running as backup-murat). NO AmneziaWG traces on host OS: wg-quick@wg0 plain WireGuard 10.7.0.1/24, UDP 51820 + tailscale 41641. docker/containerd running but sock denied to user — AWG may be containerized (need sudo). picoclaw-launcher: /usr/local/bin/picoclaw-launcher -public, 0.0.0.0:18800, 55d uptime, unknown purpose — ask owner. filebrowser.service = Tailscale-bound :8080 (legit backup UI, db ~/.config/filebrowser/). Tailscale peers: marcellpc (win), poco-f7 (android, offline 103d), vm1282045 active. WORKING HYPOTHESIS for T28: old clients = plain-WG protocol (work), new keys generated by the app in AWG protocol (junk packets) cannot handshake with plain-WG server — NOT a ufw problem. Plan: backup wg0.conf+peers, install amnezia-wg (kernel 5.15 compat check against amnezia-vpn/amnezia-wg), migrate wg0->awg0 preserving keys+port, parallel-test new client before switching old ones. Plan updated in vps-global-plan.md (stack 2de8c6c). BLOCKERS/QUESTIONS for owner: sudo path for privileged steps (she runs prepared commands OR NOPASSWD list), picoclaw purpose/keep-decision, confirm AmneziaVPN app is the client generator, confirm AWG lives in docker or nowhere."} -{"text": "Owner confirmed \"v3\" = client v5 (5.0.x, AWG 3 support added in 5.0.0.5 July 2026, current 5.0.2.1 Sep 3). Full calculation recorded in vps-global-plan.md (stack dbcd994): (1) app reinstall NOT required for existing setup — her 4.8.14-era app + current containers compatible; 5.0 app needed only for AWG-3 server. (2) Client configs for our server will be GENERATED by us from server-side keys (private fork gives deploy code, .vpn format) — app imports file, no ssh credentials to app. (3) Key-loss protection: mount configs not bake (root cause fix), server keys in ~/memento/vps2/amnezia-configs-backup.tgz, device private keys live in device apps — export before any app update (server holds only pubkeys + psk). (4) Parallel architecture: current containers untouched on 34367/36285, AWG-3 server on NEW ports, per-device cutover, rollback = containers still running. (5) Server path decision pending: fresh containers w/ mounted configs vs native DKMS awg. OPEN: far client on legacy wg0 (51820, ufw-blocked) alive/dead; old-client vs AWG-3-server compatibility assumed incompatible (parallel ports cover); exact 5.0 .vpn config format to read from fork."} -{"text": "NATIVE AWG MIGRATION CUTOVER SUCCESS (2026-09-12 11:57 UTC): vps2 production VPN moved from docker container amnezia-awg2 to native amneziawg-go — same pubkey (XVpbQJiu...), same port 36285/udp, same AWG params (Jc=5 Jmin=10 Jmax=50 S1=129 S2=62 S3=20 S4=6 ranged H1-H4 + I1-I4 specials), 8 peers, handshakes resumed within 75 seconds (11s/15s/1m21s fresh). Stack: /etc/amneziawg/awg0.conf (root 600, keys PRESERVED byte-for-byte from memento — mounted not baked), PostUp/PostDown MASQUERADE 10.8.1.0/24 via ens3 INSIDE [Interface] section (CRITICAL LESSON: awg-quick v3.1 recognizes hook lines ONLY in [Interface] section — hooks after [Peer] sections pass to setconf as unrecognized lines), amneziawg-go 0.0.20250522 extracted from container to /usr/local/bin/, awg-tools v3.1.20260812 installed from official release zip (sha256 verified), systemd unit awg-quick@.service created+enabled. ufw now honestly allows 36285/34367 udp (native no longer bypasses firewall). ROLLBACK WINDOW OPEN: container amnezia-awg2 stopped but NOT removed (docker start = instant revert); container amnezia-awg (34367, one dormant peer 1d17h) still running — next to migrate; legacy wg0 (51820) confirmed dead (0 handshakes, 1 stale peer) — decommission candidate. First cutover attempt failed on hook placement (parser lesson above), auto-rollback worked as designed, owner's ssh blipped (her ssh rides the tunnel)."} -{"text": "Native AWG traffic FIXED (2026-09-12 12:19): after cutover, handshakes succeeded but NO traffic — root cause: ufw FORWARD chain policy DROP; container setup hid forwarding behind docker chains, native awg0 needed explicit `ufw route allow in on awg0 out on ens3` (plus the existing INPUT port rule). Traffic confirmed flowing (1.74 MiB sent to client in 30s, fresh handshakes). Owner's ssh-blip explanation: her interactive ssh rides the Amnezia tunnel (blips during cutover windows), Tailscale-based ssh unaffected (separate network). Two-lesson pattern for native VPN migration: (1) hooks only in [Interface] section, (2) INPUT + FORWARD both required under ufw. Remaining vps2 items: 34367 container migration (same path), container removal after stability days, POLZA key rotation (owner), legacy wg0 decommission decision."} -{"text": "DAY CLOSE CHECKPOINT (2026-09-12, session ses_f6e223cc, ~350k tok): MIGRATION DAY COMPLETE. Shipped: (1) opencode migration — 12 provider keys, config parity, permissions (live-fire tested), ariel-inject verified; (2) Compose Ф1 ported+accepted in combat (agent + 14 skills, stack 21966ee); (3) memory-files plugin (file memory + search tool + /dream /distill, stack 959f736 5847bcb); (4) memory_search bug fixed in live ariel repo (2990233, red-green cycle); (5) a-memory plan archive → stack (6d87142), ROADMAP (252d7ad); (6) vps2: T27 cleanup (librefang+picoclaw out, POLZA key EXPOSED - owner rotates), native AWG migration DONE (36285 live, keys preserved, ufw honest, systemd persistent), wg0 decommissioned, security audit clean (a9b4b5d 3dc0614), private fork Cipher208/amnezia-client-private + deploy doc (e9349f9). OPEN for next session: 34367 container migration (same path), docker rm insurance containers after stability days, POLZA rotation (owner), R1 skills audit, Ф2 compose after Ф1 week, ROADMAP.md has full state. New session should wake via ariel inject + MEMORY.md + ROADMAP pointers — this is the Ф1 continuity test."} -{"text": "Ф1-continuity FIX (2026-09-12, ses: current, uncommitted in ~/mcp-ariel-memory): ROOT CAUSE chain — (1) day-close checkpoints landed agent-layer while all startup surfaces read user-layer; (2) opencode interactive sessions never register in SessionStore (close_session has ZERO production callers) so recap showed only CLI audit sessions; (3) one-shot inject was static 690B critical set, no rolling actuality; notes.md existed but nothing injected it. FIX SHIPPED: continuity.py gained _pick_substantive (message_count-first selection), recap_checkpoint axis (session_summary L3 episodes BOTH layers via EpisodicMemory(layer=agent)), recap_notes axis (config continuity.notes_glob, template-only notes skipped, opencode-only, hermes/cowagent unaffected); autohooks inject --with-recap merges recap+cache_break before critical set; ariel-inject.ts uses --with-recap at one-shot + 20s SIGKILL timeout guard on ALL spawnSync/fire (intermittent CLI hang defense); mimocode base config refreshed (9 missing keys deep-merged, embeddings.url preserved) + continuity.notes_glob added. DATA: session_ended dispatched with real day summary → recap_checkpoint live; 61 garbage L4 fact: rows hidden (visibility=hidden, REVERSIBLE) — auto-extractor pollutes L4 with mangled slug keys (ate Lily's greeting too), extractor audit = P1 follow-up. GATE: ruff+format+mypy(232f)+pytest 1669 green; mypy fixes in shared/embeddings.py were pre-existing (3d4927c legacy). CHANGES UNCOMMITTED — awaiting Lily's call. 5 new staging proposals appeared from session_ended extract_and_stage."} -{"text": "F1-continuity PIPELINE DAY 2 (2026-09-12, committed): ariel f9eaeb6 + stack 83b5306 + cowagent f3cf0466. SHIPPED: (1) recap-actuality at session start (--with-recap: substantive session, dual-layer checkpoint, config-gated notes tail) wired into opencode/mimocode/hermes/cowagent surfaces; (2) dialogic guard — episode_promotion was the ACTUAL L4 polluter (forensics: 100% fact: rows source=episode_promotion, distiller innocent, L0 single-door holds), guards in distiller+both consolidation paths; (3) diff_gap: preview now written at dispatch time, empty gaps dropped (19,171 empty markers + 107 garbage fact rows DELETED from 3 bases, backups sanitation-backup-20260912.jsonl.gz); (4) CRITICAL FOUND: alembic env.py fileConfig silenced EVERY ariel logger at MCP server startup (disable_existing_loggers) — production logging dead since migration to alembic; fixed+regression-tested; (5) save() preserves visibility on re-upsert; (6) hash-fallback counter+WARNING; (7) novelty gates in all per-turn recall paths; (8) 20s SIGKILL timeouts everywhere (17h orphan killed). PROPOSALS: mimocode queue cleared (3 approved→L4 #732-734, 8 rejected), hermes #280 rejected (privacy). FOLLOW-UPS: cowagent session_ended wiring needs a real session-end concept (Eli recap_checkpoint empty until then); importance scorer over-values chat text (residual poetic lines pass dialogic guard — needs eval No.11-style tuning); 'hidden' outside visibility whitelist (consider first-class); this session's MCP server still old-code — full effect next session."} +{"text": "vps2 (node-b, Tailscale 198.51.100.12) recon 2026-09-12, read-only via ssh `` (key ``, user in sudo group but password required): Ubuntu 22.04.5, 66d uptime, 30G 54%, RAM 1.9G. Service-A ACTIVE (unit /etc/systemd/system/service-a.service w/ PROVIDER_TOKEN in plaintext — ROTATE after removal; binary /usr/local/bin/service-a 85M, data ~/.service-a 28M, port 0.0.0.0:19455, running as backup-user). NO AmneziaWG traces on host OS: wg-quick@wg0 plain WireGuard 10.9.0.1/24, UDP 51832 + tailscale 41641. docker/containerd running but sock denied to user — AWG may be containerized (need sudo). service-b-launcher: /usr/local/bin/service-b-launcher -public, 0.0.0.0:19880, 55d uptime, unknown purpose — ask owner. backup-ui.service = Tailscale-bound :8080 (legit backup UI, db ~/.config/backup-ui/). Tailscale peers: workstation (win), phone (android, offline 103d), node-a active. WORKING HYPOTHESIS for T28: old clients = plain-WG protocol (work), new keys generated by the app in AWG protocol (junk packets) cannot handshake with plain-WG server — NOT a ufw problem. Plan: backup wg0.conf+peers, install amnezia-wg (kernel 5.15 compat check against amnezia-vpn/amnezia-wg), migrate wg0->awg0 preserving keys+port, parallel-test new client before switching old ones. Plan updated in vps-global-plan.md (stack 2de8c6c). BLOCKERS/QUESTIONS for owner: sudo path for privileged steps (she runs prepared commands OR NOPASSWD list), service-b purpose/keep-decision, confirm AmneziaVPN app is the client generator, confirm AWG lives in docker or nowhere."} +{"text": "Owner confirmed \"v3\" = client v5 (5.0.x, AWG 3 support added in 5.0.0.5 July 2026, current 5.0.2.1 Sep 3). Full calculation recorded in vps-global-plan.md (stack dbcd994): (1) app reinstall NOT required for existing setup — her 4.8.14-era app + current containers compatible; 5.0 app needed only for AWG-3 server. (2) Client configs for our server will be GENERATED by us from server-side keys (private fork gives deploy code, .vpn format) — app imports file, no ssh credentials to app. (3) Key-loss protection: mount configs not bake (root cause fix), server keys in ~/keyvault/vps2/amnezia-configs-backup.tgz, device private keys live in device apps — export before any app update (server holds only pubkeys + psk). (4) Parallel architecture: current containers untouched on 51831/51830, AWG-3 server on NEW ports, per-device cutover, rollback = containers still running. (5) Server path decision pending: fresh containers w/ mounted configs vs native DKMS awg. OPEN: far client on legacy wg0 (51832, ufw-blocked) alive/dead; old-client vs AWG-3-server compatibility assumed incompatible (parallel ports cover); exact 5.0 .vpn config format to read from fork."} +{"text": "NATIVE AWG MIGRATION CUTOVER SUCCESS (2026-09-12 11:57 UTC): vps2 production VPN moved from docker container awg-container-2 to native amneziawg-go — same pubkey ( same port 51830/udp, same AWG params ( ), 8 peers, handshakes resumed within 75 seconds (11s/15s/1m21s fresh). Stack: awg0.conf (root 600, keys PRESERVED byte-for-byte from keyvault — mounted not baked), PostUp/PostDown MASQUERADE 10.9.1.0/24 via ens3 INSIDE [Interface] section (CRITICAL LESSON: awg-quick v3.1 recognizes hook lines ONLY in [Interface] section — hooks after [Peer] sections pass to setconf as unrecognized lines), amneziawg-go 0.0.20250522 extracted from container to /usr/local/bin/, awg-tools v3.1.20260812 installed from official release zip (sha256 verified), systemd unit awg-quick@.service created+enabled. ufw now honestly allows 51830/51831 udp (native no longer bypasses firewall). ROLLBACK WINDOW OPEN: container awg-container-2 stopped but NOT removed (docker start = instant revert); container awg-container-1 (51831, one dormant peer 1d17h) still running — next to migrate; legacy wg0 (51832) confirmed dead (0 handshakes, 1 stale peer) — decommission candidate. First cutover attempt failed on hook placement (parser lesson above), auto-rollback worked as designed, owner's ssh blipped (her ssh rides the tunnel)."} +{"text": "Native AWG traffic FIXED (2026-09-12 12:19): after cutover, handshakes succeeded but NO traffic — root cause: ufw FORWARD chain policy DROP; container setup hid forwarding behind docker chains, native awg0 needed explicit `ufw route allow in on awg0 out on ens3` (plus the existing INPUT port rule). Traffic confirmed flowing (1.74 MiB sent to client in 30s, fresh handshakes). Owner's ssh-blip explanation: her interactive ssh rides the Amnezia tunnel (blips during cutover windows), Tailscale-based ssh unaffected (separate network). Two-lesson pattern for native VPN migration: (1) hooks only in [Interface] section, (2) INPUT + FORWARD both required under ufw. Remaining vps2 items: 51831 container migration (same path), container removal after stability days, PROVIDER key rotation (owner), legacy wg0 decommission decision."} +{"text": "DAY CLOSE CHECKPOINT (2026-09-12, session ses_f6e223cc, ~350k tok): MIGRATION DAY COMPLETE. Shipped: (1) opencode migration — 12 provider keys, config parity, permissions (live-fire tested), ariel-inject verified; (2) Compose Ф1 ported+accepted in combat (agent + 14 skills, stack 21966ee); (3) memory-files plugin (file memory + search tool + /dream /distill, stack 959f736 5847bcb); (4) memory_search bug fixed in live ariel repo (2990233, red-green cycle); (5) a-memory plan archive → stack (6d87142), ROADMAP (252d7ad); (6) vps2: T27 cleanup (service-a+service-b out, PROVIDER key EXPOSED - owner rotates), native AWG migration DONE (51830 live, keys preserved, ufw honest, systemd persistent), wg0 decommissioned, security audit clean (a9b4b5d 3dc0614), private fork (private client fork) + deploy doc (e9349f9). OPEN for next session: 51831 container migration (same path), docker rm insurance containers after stability days, PROVIDER rotation (owner), R1 skills audit, Ф2 compose after Ф1 week, ROADMAP.md has full state. New session should wake via ariel inject + MEMORY.md + ROADMAP pointers — this is the Ф1 continuity test."} +{"text": "Ф1-continuity FIX (2026-09-12, ses: current, uncommitted in ~/mcp-ariel-memory): ROOT CAUSE chain — (1) day-close checkpoints landed agent-layer while all startup surfaces read user-layer; (2) opencode interactive sessions never register in SessionStore (close_session has ZERO production callers) so recap showed only CLI audit sessions; (3) one-shot inject was static 690B critical set, no rolling actuality; notes.md existed but nothing injected it. FIX SHIPPED: continuity.py gained _pick_substantive (message_count-first selection), recap_checkpoint axis (session_summary L3 episodes BOTH layers via EpisodicMemory(layer=agent)), recap_notes axis (config continuity.notes_glob, template-only notes skipped, opencode-only, hermes/cowagent unaffected); autohooks inject --with-recap merges recap+cache_break before critical set; ariel-inject.ts uses --with-recap at one-shot + 20s SIGKILL timeout guard on ALL spawnSync/fire (intermittent CLI hang defense); mimocode base config refreshed (9 missing keys deep-merged, embeddings.url preserved) + continuity.notes_glob added. DATA: session_ended dispatched with real day summary → recap_checkpoint live; 61 garbage L4 fact: rows hidden (visibility=hidden, REVERSIBLE) — auto-extractor pollutes L4 with mangled slug keys (ate the owner's greeting too), extractor audit = P1 follow-up. GATE: ruff+format+mypy(232f)+pytest 1669 green; mypy fixes in shared/embeddings.py were pre-existing (3d4927c legacy). CHANGES UNCOMMITTED — awaiting the owner's call. 5 new staging proposals appeared from session_ended extract_and_stage."} +{"text": "F1-continuity PIPELINE DAY 2 (2026-09-12, committed): ariel f9eaeb6 + stack 83b5306 + cowagent f3cf0466. SHIPPED: (1) recap-actuality at session start (--with-recap: substantive session, dual-layer checkpoint, config-gated notes tail) wired into opencode/mimocode/hermes/cowagent surfaces; (2) dialogic guard — episode_promotion was the ACTUAL L4 polluter (forensics: 100% fact: rows source=episode_promotion, distiller innocent, L0 single-door holds), guards in distiller+both consolidation paths; (3) diff_gap: preview now written at dispatch time, empty gaps dropped (19,171 empty markers + 107 garbage fact rows DELETED from 3 bases, backups sanitation-backup-20260912.jsonl.gz); (4) CRITICAL FOUND: alembic env.py fileConfig silenced EVERY ariel logger at MCP server startup (disable_existing_loggers) — production logging dead since migration to alembic; fixed+regression-tested; (5) save() preserves visibility on re-upsert; (6) hash-fallback counter+WARNING; (7) novelty gates in all per-turn recall paths; (8) 20s SIGKILL timeouts everywhere (17h orphan killed). PROPOSALS: mimocode queue cleared (3 approved→L4 #732-734, 8 rejected), hermes #280 rejected (privacy). FOLLOW-UPS: cowagent session_ended wiring needs a real session-end concept (Persona-C recap_checkpoint empty until then); importance scorer over-values chat text (residual poetic lines pass dialogic guard — needs eval No.11-style tuning); 'hidden' outside visibility whitelist (consider first-class); this session's MCP server still old-code — full effect next session."} From 3e589aa8395fd9306cc1f851a89fd0fe10e83a40 Mon Sep 17 00:00:00 2001 From: Cipher208 <269750686+Cipher208@users.noreply.github.com> Date: Wed, 7 Oct 2026 23:26:31 +0200 Subject: [PATCH 2/4] docs(contributing): forbid operator-identifying data in fixtures MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The fixture that leaked a host recon report was not carelessness — the project's own practice was to pin real history rows verbatim, and nothing said not to. Put the rule where a contributor will actually look, with the concrete list: node names, CGNAT and VPN ranges, ssh users, key paths, the names of secret-bearing variables, tunnel parameters, service inventories, personal names. Record why the existing scan does not cover this. gitleaks matches credential shapes; infrastructure disclosure is made of names, paths and roles, and has no shape to match. Substitute, do not delete: an anonymised recon row still tests the classifier, and the blast radius of an over-cautious fixture is zero. There is no way to un-publish a fork. --- CONTRIBUTING.md | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 569c038b..9d9ec2d1 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -45,6 +45,40 @@ Format: `(): ` Types: `feat`, `fix`, `docs`, `chore`, `test`, `refactor`, `perf`, `ci`, `build` +## Test Data and Fixtures + +**Fixtures are shipped in the repository. Treat every fixture as public.** + +This project pins real rows from its own history — broadcast reports, recall +episodes, session notes. That is deliberate and useful, but it means a fixture +can carry more than the behaviour it is meant to exercise. Most of this codebase's +tests are about memory, and memory is written by real operators about real machines. + +Never commit these, in any form, including inside a fixture, a docstring, a test +constant or a comment: + +- hostnames, node names, or machine identifiers — of this project's operators or anyone else's +- private-network addresses: RFC 1918, CGNAT (the `100.64/10` block, which includes + every Tailscale address), link-local, or WireGuard/VPN subnets +- ssh users, key paths, or the *names* of secret-bearing environment variables +- VPN or tunnel parameters: interface names, listen ports, key material, obfuscation settings +- service inventories above the level of the generic ("a backup service listens on + loopback"), and never with ports and paths attached +- personal names of operators and the people they live with + +**Substitute, do not delete.** A fixture whose point is that a recon report is +classified as a broadcast must still look like a recon report. Keep the shape, +swap the identifiers — `node-b`, ``, ``, +``, `10.9.1.0`. Anonymised rows catch regressions just as well. + +**Note on secret scanning.** `detect-secrets` runs on every push and it will not +save you here. gitleaks matches credential *shapes* — provider prefixes, high-entropy +strings, private-key headers. Infrastructure disclosure has none of those: it is made +of names, paths and roles. A green scanner says "no credentials", not "nothing sensitive". + +If you are unsure whether a fixture row is safe, anonymise it. There is no cost to +being generic and there is no way to un-publish a fork. + ## Pull Request Rules 1. Fork the repo and create a branch from `master` @@ -61,6 +95,7 @@ Types: `feat`, `fix`, `docs`, `chore`, `test`, `refactor`, `perf`, `ci`, `build` - Test coverage for new features - Type annotations (mypy passes) - No regressions (all 338 tests pass) +- **No operator-identifying data in fixtures** (see [Test Data and Fixtures](#test-data-and-fixtures)) - Documentation updates if behavior changes ## Reporting Issues From ce73ac35802ebc2dd8f36917356527de60d69b35 Mon Sep 17 00:00:00 2001 From: Cipher208 <269750686+Cipher208@users.noreply.github.com> Date: Wed, 7 Oct 2026 23:29:38 +0200 Subject: [PATCH 3/4] ci(secrets): add a gitleaks layer for infrastructure disclosure MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Credential scanning was the only control on this repository's public surface, and the incident it missed had no credentials in it. The leaked row was a host recon report: node names, a tailnet address, the ssh user, a key path, an API-key variable name, tunnel parameters and ports. gitleaks matches shapes — provider prefixes, entropy, key headers — so it matched none of that and went green. Add the missing half: rules for the disclosure itself, tuned narrow so they stay trusted. Tailscale/CGNAT addresses, tailnet DNS names, provider machine ids, backup ssh accounts, private key paths, secret-bearing variable names, AmneziaWG parameters and config paths, tunnel key material. Default rules stay on; this is a layer, not a replacement. RFC 1918 addresses are deliberately not matched — examples, docker config and tests here use 10.x legitimately, and a rule that cries wolf gets ignored. Verified three ways: the file's own rules find the 2026-09-13 leak commit (six rules fire on it, the default ruleset was silent); the tracked tree produces zero findings; and the hook fails a planted leak (exit 1). The rules describe shapes, never our literal identifiers. A "regression pin" listing the exact hostnames was the first draft and would have republished the identifiers it guarded — the file is committed to a public repo, so the config is published too. Specific literals belong in an untracked local overlay. --- .gitleaks.toml | 111 ++++++++++++++++++++++++++++++++++++++++ .pre-commit-config.yaml | 10 ++++ 2 files changed, 121 insertions(+) create mode 100644 .gitleaks.toml diff --git a/.gitleaks.toml b/.gitleaks.toml new file mode 100644 index 00000000..15905970 --- /dev/null +++ b/.gitleaks.toml @@ -0,0 +1,111 @@ +# gitleaks configuration — mcp-ariel-memory +# +# Why this file exists. +# +# The default ruleset finds credentials. It structurally cannot find the other +# half of what this repository can leak: fixture rows that pin real close-out +# broadcasts from a live operator's history. One of those rows described a +# private host — node names, a tailnet address, the ssh user, a key path, an +# API-key variable name, tunnel parameters and ports. There is no credential +# shape in any of that: it is made of names, paths and roles, so gitleaks had +# nothing to match and the CI scan went green on a tree that already contained it. +# +# These rules are the house layer for infrastructure disclosure. +# +# DESIGN CONSTRAINT — read before extending. +# +# This file is committed to a public repository, so it is itself published. A +# rule that names one of our hosts publishes that host, and a "regression pin" +# listing the exact identifiers would re-leak the very thing it guards. The +# first draft of this file did exactly that. Therefore: +# +# no rule below contains a literal hostname, address, account, key name or +# persona handle of this house. Every rule describes a SHAPE — "this is what a +# tailnet address looks like", "this is what a private key path looks like" — +# and a shape is safe to publish while still being specific enough to fire. +# +# Rules matching our literal identifiers, if ever wanted, belong in an untracked +# local overlay, never here. The generic rules below are what caught the real +# incident (six of them fire on it); literal pins were never the value. +# +# Deliberately NOT matched: generic RFC 1918 addresses. Examples, docker config +# and tests in this repository use 10.x legitimately, and a rule that cries wolf +# gets muted. See CONTRIBUTING.md § Test Data and Fixtures. + +[extend] +# Keep every default rule. This adds a layer; it does not trade one for another. +useDefault = true + +[[rules]] +id = "house-tailscale-cgnat" +description = "Tailscale / CGNAT address (100.64.0.0/10) — a private tailnet node" +regex = '''\b100\.(?:6[4-9]|[7-9][0-9]|1[01][0-9]|12[0-7])\.\d{1,3}\.\d{1,3}\b''' +keywords = ["100."] +tags = ["house", "infrastructure"] + +[[rules]] +id = "house-tailnet-fqdn" +description = "Tailnet DNS name (*.ts.net) — names the private network" +regex = '''\b[a-z0-9][a-z0-9-]*\.ts\.net\b''' +keywords = [".ts.net"] +tags = ["house", "infrastructure"] + +[[rules]] +id = "house-vps-hostname" +description = "Provider-side machine id (vm followed by 6-9 digits)" +regex = '''\bvm\d{6,9}\b''' +keywords = ["vm"] +tags = ["house", "infrastructure"] + +[[rules]] +id = "house-operator-domain" +description = "Hostname-style mail/URL domain under a personal zone (*.cloud.)" +regex = '''\b[a-z0-9][a-z0-9.-]*\.cloud\.[a-z]{2,}\b''' +keywords = [".cloud."] +tags = ["house", "infrastructure"] + +[[rules]] +id = "house-ssh-backup-user" +description = "Backup ssh account, in ssh context or as user@host" +regex = '''(?:ssh|scp|sftp|rsync)\s+(?:-\S+\s+|\S+\s+){0,3}backup-[a-z][a-z0-9_-]{2,}\b|\bbackup-[a-z][a-z0-9_-]{2,}@''' +keywords = ["backup-"] +tags = ["house", "access"] + +[[rules]] +id = "house-ssh-key-path" +description = "Private key path under .ssh/ — pairs a key artifact with where it lives" +regex = '''(?:~|/(?:home|root))/?(?:[\w.-]+/)*\.ssh/[\w.-]*(?:key|pem|p12)\b''' +keywords = [".ssh/"] +tags = ["house", "access"] + +[[rules]] +id = "house-secret-var-name" +description = "Name of a secret-bearing env var (FOO_API_KEY) — the name alone maps the perimeter" +regex = '''\b[A-Z][A-Z0-9]{2,}_API_KEY\b''' +keywords = ["_API_KEY"] +tags = ["house", "access"] + +[[rules.allowlists]] +description = "A workflow that names its own secret (secrets.FOO_API_KEY) discloses nothing about a host — GitHub holds the value. The rule exists for names appearing in fixtures and docs, where a name sits next to a real machine." +paths = ['''\.github/workflows/'''] + +[[rules]] +id = "house-amneziawg-params" +description = "AmneziaWG obfuscation parameters (Jc/Jmin/Jmax) — a tunnel fingerprint" +regex = '''\b(?:Jc|Jmin|Jmax)\s*=\s*\d{1,4}\b''' +keywords = ["Jc", "Jmin", "Jmax"] +tags = ["house", "infrastructure"] + +[[rules]] +id = "house-amneziawg-config-path" +description = "AmneziaWG interface config path" +regex = '''/etc/amneziawg/[\w.-]+\.conf''' +keywords = ["/etc/amneziawg/"] +tags = ["house", "infrastructure"] + +[[rules]] +id = "house-tunnel-key-material" +description = "WireGuard/AmneziaWG key material (base64, 44 chars) on a key= line" +regex = '''(?i)\b(?:private|preshared)[-_]?key\s*=\s*[A-Za-z0-9+/]{43}=''' +keywords = ["privatekey", "presharedkey", "preshared"] +tags = ["house", "access"] diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 9f7b58cb..7f998fd9 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -17,6 +17,16 @@ repos: args: [--fix] - id: ruff-format + # House layer: secret scanning catches credentials, and infrastructure + # disclosure has no credential shape in it. Verified against the 2026-09-13 + # leak commit: the default ruleset was silent, .gitleaks.toml fires on it + # eight times. Rules and rationale live in .gitleaks.toml; the contributor + # rule in CONTRIBUTING.md § Test Data and Fixtures. + - repo: https://github.com/gitleaks/gitleaks + rev: v8.30.1 + hooks: + - id: gitleaks + - repo: local hooks: - id: skylos From 07cb70859eb6cae6bc0fad52344f8b4cc4855e13 Mon Sep 17 00:00:00 2001 From: Cipher208 <269750686+Cipher208@users.noreply.github.com> Date: Thu, 8 Oct 2026 00:03:55 +0200 Subject: [PATCH 4/4] test(privacy): scrub the house identifiers the first pass left behind MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The first pass replaced the recon in row 27 of the status-broadcast fixture but stopped there. The same broadcast register names the private estate in eight more rows and in three other files, so the tree still carried: - `vps2` — the real node name, in the fixture (rows 21, 27-31), in test_broadcast.py and in test_continuity_actuality.py - `~/keyvault/vps2/amnezia-configs-backup.tgz` — the path of the archive holding the AmneziaWG server keys. This is the one that matters: it does not name a tunnel, it names where the keys are kept. - `keyvault` used alone, later in the same register - `/home/murat` — the operator home, in the fixture, in two docs and in a test comment Substituted with the same placeholder convention the first pass used (`node-b`, ``, ``). Deliberately left alone: `10.x` and the port numbers, following the existing decision that generic RFC 1918 addresses stay unmatchable, and `198.51.100.12`, which is the RFC 5737 documentation range. The fixture's catch-rate assertions and the detector contract are untouched: 33 rows, all valid JSON, full suite 2087 passed. --- docs/compose/specs/2026-09-04-phase-fgh-draft.md | 4 ++-- docs/hooks/autohooks-platforms.md | 2 +- tests/shared/test_importance_models.py | 2 +- tests/test_features/test_continuity_actuality.py | 2 +- .../fixtures/status_broadcast_rows.jsonl | 14 +++++++------- tests/test_shared/test_broadcast.py | 2 +- 6 files changed, 13 insertions(+), 13 deletions(-) diff --git a/docs/compose/specs/2026-09-04-phase-fgh-draft.md b/docs/compose/specs/2026-09-04-phase-fgh-draft.md index 7c6dbe36..ac14bb25 100644 --- a/docs/compose/specs/2026-09-04-phase-fgh-draft.md +++ b/docs/compose/specs/2026-09-04-phase-fgh-draft.md @@ -131,7 +131,7 @@ WIKI = L4.5 knowledge layer — НАМЕРЕННАЯ запись, НЕ дист > Мой v1-черновик (co-occurrence/similar_to/follows_in_time) ПОГЛОЩЁН списком из > `a-memory-graph-miners.md` — он шире (8 сигналов + эмбеддинг-слой) и привязан к -> существующей инфраструктуре. Источник: /home/murat/cow/knowledge/analysis/a-memory-graph-miners.md +> существующей инфраструктуре. Источник: /cow/knowledge/analysis/a-memory-graph-miners.md **Фундамент (прежде минеров)**: рёбра пишут только builder'ы (B1.3 ночной, A1.6 communities, MCP relates_to/causal) — реальных связей они не находят; recall-телеметрии пар НЕТ (нужен @@ -410,7 +410,7 @@ MCP relates_to/causal) — реальных связей они не наход 5. **8 циклов автономии при 3 LLM-классах (nano/mini/gpt-4.1)** — экономика подтверждает E8-вердикт: гейт инициативы (WhisperGate) — это harness-задача с nano-моделью, ariel остаётся keyless. Патент-pending + «license subject to change» — ещё одна причина не заимствовать механики напрямую (только идеи, как с AGPL у OpenViking). **Резюме**: MemoryMuse — пятый подряд конкурент с «взять»-листом внутри черновика. Ценное из деталей: per-kind капы и явный порядок inject-блоков (усиление F-спеки инъекции), private-флаг объединяющий C5+scratchpad. Стек (Mongo+Qdrant+Memgraph+docker) — антипример local-first. -- 2026-09-04: v14 — ПАЙПЛАЙН ЭЛИ/ЛИЛИ (a-memory-l0-l4-pipeline.md + a-memory-graph-miners.md, /home/murat/cow/knowledge/analysis/) принят как ОСНОВА Phase F и Phase G. Мой v1 поглощён. Диагноз кода проверен 04.09 — все дыры реальны (L2 без сообщений, staging_-обрубки, decay_rate игнорируется промоцией, remember дублирует в граф, 4 параллельных входа). Поправки к их доку: CLACK не найден (сжатие = A3+zlib), add_edge «не вызывается нигде» устарело (B1.3/A1.6 пишут, минеры не наполняют), sentence-transformers 6.0.0 уже в venv (минер #9 возможен сейчас). Phase F = их конвейер + мои гейты/журнал/replay; Phase G = их 8+1 минеров + моя санитария/graph_enrich-оркестратор. Ключевые новые обязательства: канонические ключи (не обрубки), kind-роутинг инвариант/событие в G1, противоречия → memory_conflicts вместо молчаливого UPDATE, wiki = L4.5 [[fact:]]-linking без дублирования, L0-тиры жизни 30/180 + дистилляция освобождает сырьё, журнал co-retrieval пар (новый), пре-чистка JSON-узлов перед минерами. +- 2026-09-04: v14 — ПАЙПЛАЙН ЭЛИ/ЛИЛИ (a-memory-l0-l4-pipeline.md + a-memory-graph-miners.md, /cow/knowledge/analysis/) принят как ОСНОВА Phase F и Phase G. Мой v1 поглощён. Диагноз кода проверен 04.09 — все дыры реальны (L2 без сообщений, staging_-обрубки, decay_rate игнорируется промоцией, remember дублирует в граф, 4 параллельных входа). Поправки к их доку: CLACK не найден (сжатие = A3+zlib), add_edge «не вызывается нигде» устарело (B1.3/A1.6 пишут, минеры не наполняют), sentence-transformers 6.0.0 уже в venv (минер #9 возможен сейчас). Phase F = их конвейер + мои гейты/журнал/replay; Phase G = их 8+1 минеров + моя санитария/graph_enrich-оркестратор. Ключевые новые обязательства: канонические ключи (не обрубки), kind-роутинг инвариант/событие в G1, противоречия → memory_conflicts вместо молчаливого UPDATE, wiki = L4.5 [[fact:]]-linking без дублирования, L0-тиры жизни 30/180 + дистилляция освобождает сырьё, журнал co-retrieval пар (новый), пре-чистка JSON-узлов перед минерами. ## Sequencing (user decision 2026-09-04) diff --git a/docs/hooks/autohooks-platforms.md b/docs/hooks/autohooks-platforms.md index ea5a1fb1..d9872de5 100644 --- a/docs/hooks/autohooks-platforms.md +++ b/docs/hooks/autohooks-platforms.md @@ -156,7 +156,7 @@ Restart after ariel code updates: `systemctl --user restart ariel-autohooks-*`. CowAgent additionally runs under a **system** unit `cowagent.service` (Restart=always) — the agent itself, not the daemon; killing its PID auto-respawns it with new code (do NOT manual-nohup: `-m app` needs -cwd=/home/murat/cowagent). +cwd=/cowagent). ### Hermes — native `MemoryProvider` plugin (preferred path) diff --git a/tests/shared/test_importance_models.py b/tests/shared/test_importance_models.py index 45027bdb..0b536fa7 100644 --- a/tests/shared/test_importance_models.py +++ b/tests/shared/test_importance_models.py @@ -4,7 +4,7 @@ from shared.importance.models import ImportanceConfig, ImportanceSignals, ScorerResult # Repo-relative, not an absolute host path: CI checkouts live elsewhere -# (was /home/murat/Projects/repos/... → FileNotFoundError on GitHub Actions). +# (an absolute operator path → FileNotFoundError on GitHub Actions). ASSET_PATH = Path(__file__).resolve().parents[2] / "shared" / "assets" / "importance_config.json" diff --git a/tests/test_features/test_continuity_actuality.py b/tests/test_features/test_continuity_actuality.py index f211c55e..3967d5bc 100644 --- a/tests/test_features/test_continuity_actuality.py +++ b/tests/test_features/test_continuity_actuality.py @@ -111,7 +111,7 @@ async def test_recap_session_prefers_substantive(monkeypatch): now = time.time() rows = [ _sess("Memory audit session — no user interaction", 0, now - 60), - _sess("Migration day complete: opencode + vps2 VPN cutover", 52, now - 7200), + _sess("Migration day complete: opencode + node-b VPN cutover", 52, now - 7200), ] monkeypatch.setattr("core.session.SessionStore", lambda: _FakeSessionStore(rows)) diff --git a/tests/test_shared/fixtures/status_broadcast_rows.jsonl b/tests/test_shared/fixtures/status_broadcast_rows.jsonl index e89acd15..7c3988ab 100644 --- a/tests/test_shared/fixtures/status_broadcast_rows.jsonl +++ b/tests/test_shared/fixtures/status_broadcast_rows.jsonl @@ -1,5 +1,5 @@ {"text": "L0-ДЕТЕКТИВ ЗАКРЫТ (2026-09-07): (1) hermes — КОНТУР БЫЛ И ЕСТЬ: sync_turn стрелял 139 раз с 29.08 (лог /tmp/ariel-provider.log), но запущенный процесс был ДО F1 (head a26) — capture() в auto_save_text ещё не существовал, l0_journal-таблицы не было. После рестарта (g23) контур работает — проверено ручным dispatch new_message: строка легла в L0. Впредь наполняется сам. (2) cowagent — РЕАЛЬНАЯ ДЫРА: _ariel_turn_hooks (agent_stream.py:2108, D3.5) делал только inject/recall (чтение), dispatch new_message НЕ СУЩЕСТВОВАЛ нигде — ход диалога никогда не попадал в L0 (L4 наполнялся через прямые think/remember). ФИКС `ab61bcd1` (cowagent repo): post-turn capture в finally agent_end — user_message + final_response dispatch(\"new_message\") по 5000Б, fire-and-forget, зеркало Hermes sync_turn-контракта. Деплой: kill MainPID → systemd Restart=always сам поднял с новым кодом (process 1767809+), патч верифицирован в файле+py_compile. ПЕРВЫЙ реальный turn cowagent теперь попадёт в L0. Урок: cowagent — SYSTEM unit (sudo нужен пароль для systemctl restart; kill MainPID достаточно — Restart=always). Чтение-хуки не пишут в память by design (feedback-loop filter) — write-стор была пропущена при D3.5, обнаржужено только post-restart верификацией L0=0 при живых L4."} -{"text": "STAGE 2 СТАРТОВАЛ — ПЛАН A URI-KEYS SHIPPED (2026-09-07, 3a41477, gate 1494/0 + mypy 231 clean). Схема `ariel:////`: fact/wiki/graph/node/episode/l0 — поверх существующих ключей, ноль миграций; shared/uris.py: fact_uri/wiki_uri/node_uri/episode_uri/l0_uri билдеры + parse_uri (мусор → None, peer/ на store-позиции → reserved flag, graph требует node/) + resolve_uri (async, user_id обязателен — изоляция: чужое по URI не резолвится; peer → ValueError; wiki branch принимает опц. WikiManager). Интеграция: CoreMemory.search items несут uri=ariel://{layer}/fact/{key}; wiki_read возврат + uri; drill_down(entry_id|ariel-URI, user_id, layer='user') — URI резолвится в entry_id (не-fact URI → resolved без провенанса). Тесты 9. Ловушки: wiki_uri сохраняет .md (wm.get требует полное имя — первый вариант срезал); mypy-конфликт entry/wentry имён переменных разных типов; _FakeMem инлайн в тесте (тестовые FakeMem не импортируются из прод-тестов). ПОБЕЖНОСТИ: test_export_clack_jsonl_lossless пал от КАЛЕНДАРЯ (10.03: 200д→фев, 190д→мар — разные месячные бакеты; фикс: второй seed age=max(181, 200-tm_mday+1) — гарантированно тот же месяц). ПОЧЕМУ L0 ПУСТ У АГЕНТОВ (детектив, T28 done): hermes — sync_turn стрелял 139 раз, но процесс был до F1 (capture не существовал); после рестарта работает (проверено вручную). cowagent — РЕАЛЬНАЯ ДЫРА: _ariel_turn_hooks только inject/recall, dispatch new_message НЕ СУЩЕСТВОВАЛ; фикс ab61bcd1 в cowagent-repo: post-turn capture в finally agent_end (user+assistant по 5000Б, зеркало hermes sync_turn), деплой kill MainPID (systemd Restart=always; systemctl restart требует sudo-пароль). Урок: read-хуки не пишут в память by design — write-стор пропущена при D3.5, обнаружено только L0=0-верификацией. NEXT Stage 2: План B (MCP behavior-аннотации readOnlyHint/destructiveHint/idempotentHint на 65 тулов — механический), C (slots + inject/key consolidation + wake_up alias + admin-манифест), D (Stage2-eval). LME-дамп: /home/murat/workspace/longmemeval-s-sample.json."} +{"text": "STAGE 2 СТАРТОВАЛ — ПЛАН A URI-KEYS SHIPPED (2026-09-07, 3a41477, gate 1494/0 + mypy 231 clean). Схема `ariel:////`: fact/wiki/graph/node/episode/l0 — поверх существующих ключей, ноль миграций; shared/uris.py: fact_uri/wiki_uri/node_uri/episode_uri/l0_uri билдеры + parse_uri (мусор → None, peer/ на store-позиции → reserved flag, graph требует node/) + resolve_uri (async, user_id обязателен — изоляция: чужое по URI не резолвится; peer → ValueError; wiki branch принимает опц. WikiManager). Интеграция: CoreMemory.search items несут uri=ariel://{layer}/fact/{key}; wiki_read возврат + uri; drill_down(entry_id|ariel-URI, user_id, layer='user') — URI резолвится в entry_id (не-fact URI → resolved без провенанса). Тесты 9. Ловушки: wiki_uri сохраняет .md (wm.get требует полное имя — первый вариант срезал); mypy-конфликт entry/wentry имён переменных разных типов; _FakeMem инлайн в тесте (тестовые FakeMem не импортируются из прод-тестов). ПОБЕЖНОСТИ: test_export_clack_jsonl_lossless пал от КАЛЕНДАРЯ (10.03: 200д→фев, 190д→мар — разные месячные бакеты; фикс: второй seed age=max(181, 200-tm_mday+1) — гарантированно тот же месяц). ПОЧЕМУ L0 ПУСТ У АГЕНТОВ (детектив, T28 done): hermes — sync_turn стрелял 139 раз, но процесс был до F1 (capture не существовал); после рестарта работает (проверено вручную). cowagent — РЕАЛЬНАЯ ДЫРА: _ariel_turn_hooks только inject/recall, dispatch new_message НЕ СУЩЕСТВОВАЛ; фикс ab61bcd1 в cowagent-repo: post-turn capture в finally agent_end (user+assistant по 5000Б, зеркало hermes sync_turn), деплой kill MainPID (systemd Restart=always; systemctl restart требует sudo-пароль). Урок: read-хуки не пишут в память by design — write-стор пропущена при D3.5, обнаружено только L0=0-верификацией. NEXT Stage 2: План B (MCP behavior-аннотации readOnlyHint/destructiveHint/idempotentHint на 65 тулов — механический), C (slots + inject/key consolidation + wake_up alias + admin-манифест), D (Stage2-eval). LME-дамп: /workspace/longmemeval-s-sample.json."} {"text": "STAGE 2 ПЛАН B SHIPPED (2026-09-07, push 3a41477..040fb34: 3a41477 Plan A URIs уже был + annotations коммиты, gate 1499/0 + mypy 232 clean). mcp_server/annotations.py: ToolHints dataclass с MCP-консервативным default (destructive=True per spec!) — ToolHints() = не read-only/destructive; read-only-ряды (~30) явно destructive=False; карта 65/65 (registry cross-check тест ловит missing/stale; стартовая карта имела 7 пропусков incl memory_recall — примитив-ЧИТАТЕЛЬ!). server.py: mcp.tool(name, annotations=annotations_for(name)) — на wire Tool.annotations заполнен (тест через list_tools с ARIEL_EXPOSE=all + importlib.reload). MYPI-ЛОВ: annotations_for должен возвращать mcp.types.ToolAnnotations (не dict) — pre-push mypy строгий; Any-тайпинг + mcp.types import в функции. Тест-паттерны: импорт сервера с reload под ARIEL_EXPOSE=all; action-миксы (memory_history/proposals/backup) помечены по худшему действию — честная консервативность. Server test L0-урок: главный PID cowagent 1757576→1767809 (деплой патча ab61bcd1 через kill+Restart=always). NEXT Stage 2: План C (slots + inject/key consolidation + wake_up alias + admin-манифест) — нужен дизайн-разговор (слоты = группировка 65 тулов в смысловые сцены?), затем План D (Stage2-eval). URI (План A) уже в выдачах search/wiki_read/drill_down."} {"text": "400-ОШИБКА ПАРСЕРА MiMoCode (2026-09-07, сессия ses_-ffe5f895ce09bffeZPEDp2cPl): гипотеза владельца подтверждена корреляцией в /tmp/ariel-inject-hook.log — context_threshold фаернулся на ~665K и ~677K токенов (12:41, 14:36 UTC) и ОБА раза сразу после этого session.post outcome=error. Это НЕ ariel-хук: он только логирует событие (context_threshold = телеметрия размера контекста, dispatch best-effort). Причина: сессия переросла разумный размер (~677K токенов оценочных), движок MiMoCode собирал запрос с некорректным телом (срез истории/сериализация умирает на гигантском payload — «read body failed»). СОВЕТ ВЛАДЕЛЬЦУ: закрыть сессию (/new) — checkpoint и ariel-entries (647-659) всё сохраняют; для профилактики: провайдер-failover хук уже есть, стоит выставить ARIEL_CONTEXT_THRESHOLD_BYTES ниже или настроить авто-компакт на ~200K токенов. СОСТОЯНИЕ РАБОТЫ НА МОМЕНТ СБОЯ (всё закоммичено и запушено до 040fb34!): Stage2-A URIs (3a41477), Stage2-B annotations (040fb34), cowagent sync_turn фикс ab61bcd1, uncommitted = features/wake_up.py (черновик E10) + слот-карта 11 групп вычислена живым resolve_exposure (admin-8: api_key/backup/cleanup/data/lucidity_purge/saga/skill_promote/sync_replica; сироты при комбо-тирах подтверждены = эти же 8). ПЛАН C РЕШЕНИЕ ВЛАДЕЛЬЦА: метаданные+пресеты, НО с наведением порядка — «примитивы есть, куча метатулов, тула вне тиров только с ALL, некоторые развести по примитивам/метатулам, другие сгруппировать» — т.е. пересобрать тиры, а не просто вешать ярлыки. Осталось: слот-карта в tools_layer + admin-тир (сироты=0) + wake_up alias + манифест-док."} {"text": "Stage 2 Plan C SHIPPED (2026-09-07, ariel entry 661): коммиты 8e6f9d2 (slots.py + 5 cross-check тестов, инвариант дубликатов групп) + 8d281f0 (admin-7 тир, skill_promote→write, brief растворён в review, сироты=0 тестом, пресеты agent/operator/full с fixpoint-раскрытием) + 65bced1 (wake_up примитив E10: recap+inject общий бюджет, registry 65→66, surface 6→7) + 2b0b2ed (lint-хвост TC003/PERF102/D205) + 98f6741 (docs/tools/exposure.md манифест + reference.md переписан: 66, admin-тир) + 242250f (mypy no-any-return в dispatcher + stale 65-каунтеры в 3 тест-файлах + CHANGELOG) + 11d6aea (диздок SHIPPED-блок). Push 040fb34..11d6aea, полный гейт 1523/0 (базлайн 1499 + 24 новых) + mypy 10-dir clean + ruff/format clean. ГЛАВНЫЙ РЕЗУЛЬТАТ: ARIEL_EXPOSE=all+ARIEL_META=1 → 13 видимых схем (7 примитивов + 6 диспетчеров из EXTRA_TIERS), без ARIEL_META → 66 плоско (eval Plan D не тронут); agent=59, operator=66. Ключевые решения реализации: (1) SDK отверг underscore-параметры в сигнатуре тулов → фабрика _make_dispatcher (закрытие вместо default-arg pins); (2) _scope_tool оборачивает цель ЛЕНИВО на каждый dispatch — user_id-биндинг переживает мета-прыжок; (3) fixpoint-раскрытие пресетов (однопроходная версия поймана тестом operator⊄agent); (4) pre-commit ruff PINNED v0.16.1 ≠ свежий ruff — форматить pinned до git add, иначе stash-конфликт с untracked файлами абортит commit (2 повторённых abort, причина найдена в .pre-commit-config.yaml rev v0.16.1); (5) счётчик registry хардкодился в 3 тест-файлах (65→66) — статические числа устаревают молча, live-реестр единственный источник. Слот-карта: core 7 / recall 5 / context 4 / episodes 4 / sessions 3 / graph 4 / wiki 9 / insight 10 / write 9 / review 4 / admin 7 / brief 0 (легаси-имя). Live-конфиги агентов НЕ мигрированы (решение владельца отдельно; легаси-строка резолвится в 59). NEXT: Plan D (Stage-2 eval №11-харнесс, per-slot usage-телеметрия → данные для решения о slim-пресете)."} @@ -18,16 +18,16 @@ {"text": "memory-files plugin VERIFIED LIVE + committed (2026-09-12, stack 959f736). Live evidence: /tmp/memory-files.log injection at 00:12:47 for ses_f6e223cc project=global, templates bootstrapped (MEMORY.md 533B + notes.md), memory tool present in agent surface with correct description, instructions block visible in the agent system prompt. One anomaly: single \"text.toLowerCase is not a function\" load error at 00:11:59 in run 2988a684 — NOT reproducible standalone (bun instantiation clean); same run later shows plugin working; suspected double-export (named MemoryFiles + default) or loader environment quirk — FIXED by removing named export (now default + searchMemory only). Watch next restart: if the load error recurs, it is loader-side noise; if a session starts without the memory tool, it is real. stack-config commit 959f736: plugins/memory-files.ts + command/dream.md + command/distill.md (283 lines); exclude whitelist +!/.config/opencode/command/."} {"text": "/dream consolidation pass EXECUTED (2026-09-12, first run of the ported command): ~/.local/share/opencode/memory/projects/global/MEMORY.md updated 35K->42.4K. Refinements: Project context rewritten (opencode v1.18.30 primary since 2026-09-11, fork = fallback, lineage preserved); new Topic line (migration executed, Compose F1 accepted, memory-files plugin, stack commits 21966ee/959f736); Rules +3 (config-level only + .bak + secrets in auth.json; permissions last-match-wins inventory; memory hierarchy ariel/files/AGENTS/notes); Architecture decisions +3 (migration EXECUTED with root cause of token failure; Compose vNext F1-F4; ROADMAP convention); Discovered durable knowledge +8 (single SQLite state, shadow-git checkpoints + $HOME global limitation, small_model fallback logic, B.ai 429/auth errors, plugin SDK surfaces + single-default-export lesson, check-ignore negation exit codes, skills discovery dirs). Structure verified intact: 4 sections, ADD-not-replace honored, nothing deleted."} {"text": "ROADMAP expanded (2026-09-12, stack 252d7ad): Memory phases (Ф-M1 checkpoint-writer plugin after Ф2 compose; Ф-M2 FTS5 index + era-file rotation; Ф-M3 rebuild-on-resume deliberately deferred), the owner's phases: R1-R4 (skills audit incl. byte-cost + per-mode skill visibility via permission.skill + new modes: scientific/search/review/marketing-writer each with narrow skill set + VPS skills from GitHub), T1-T2 (rtk/context-mode/ponytail inventory vs upstream coverage; GitHub ecosystem of opencode plugins+MCP), U1 (TUI themes), C1 (Telegram channel + cron/heartbeat OUTSIDE REPL — lesson from F1 journal: cron dies with closed TUI), C2 (orchestration aligned with owner order MUSE->PURR->agent_body->hivemind). VPS global list: FORENSIC VERDICT = artifact does not exist (checked fs/ariel L4+wiki+hybrid/opencode.db 59 parts/mimocode trajectories) — list stayed in conversation; recovery action recorded: the owner recalls surface OR rewrite fresh into ~/docs/vps-global-plan.md. NOTE: ariel memory_search tool is BROKEN (WikiEntry object has no attribute get) — reported to owner, separate bug ticket for a-memory."} -{"text": "VPS global plan RESTORED from owner dictation (2026-09-12, stack 8ceeb28) -> ~/docs/vps-global-plan.md: (1) vps2: remove Service-A + cleanup + install lightweight agent (CowAgent-like for future Persona-C-equiv, candidate TBD); (2) vps2 AmnesiaWG update — CRITICAL nuances: keys/QR via developer app must survive update, ufw currently blocks NEW keys while existing clients work — study repo+ufw interplay before any action, plan requires owner OK; (3) personality files rework for Persona-A/Persona-B/Persona-C — HARD-GATE on Persona-B SOUL* files; (4) shared kanban + orchestrator + comms for 3 agents + the owner, align with hivemind track; (5) GUI shell on main VPS: light DE, phone/PC access over Tailscale, PERSISTENT TUI after SSH close (tmux/systemd), CowAgent dashboard + Hermes desktop side-by-side; dashboards have more functionality than TUI; (6) opencode desktop question ANSWERED preliminarily: near-zero rework — packages/desktop is Electron over same core, all customization is file-based in ~/.config/opencode; (7) a-memory: memory_search bug (WikiEntry.get) to fix in ~/mcp-ariel-memory (NOT Projects); clone's test-rework plan file from 09-11 NOT found on disk (~/mcp-ariel-memory, ~/docs, memory trees, ariel base) — restore from ariel episodes or ask the clone; test strategy direction from episode 6118: complex over numerous, add A/B/E2E/mutation/chaos. Exclude whitelist +!/docs/vps-global-plan.md."} +{"text": "VPS global plan RESTORED from owner dictation (2026-09-12, stack 8ceeb28) -> ~/docs/vps-global-plan.md: (1) node-b: remove Service-A + cleanup + install lightweight agent (CowAgent-like for future Persona-C-equiv, candidate TBD); (2) node-b AmnesiaWG update — CRITICAL nuances: keys/QR via developer app must survive update, ufw currently blocks NEW keys while existing clients work — study repo+ufw interplay before any action, plan requires owner OK; (3) personality files rework for Persona-A/Persona-B/Persona-C — HARD-GATE on Persona-B SOUL* files; (4) shared kanban + orchestrator + comms for 3 agents + the owner, align with hivemind track; (5) GUI shell on main VPS: light DE, phone/PC access over Tailscale, PERSISTENT TUI after SSH close (tmux/systemd), CowAgent dashboard + Hermes desktop side-by-side; dashboards have more functionality than TUI; (6) opencode desktop question ANSWERED preliminarily: near-zero rework — packages/desktop is Electron over same core, all customization is file-based in ~/.config/opencode; (7) a-memory: memory_search bug (WikiEntry.get) to fix in ~/mcp-ariel-memory (NOT Projects); clone's test-rework plan file from 09-11 NOT found on disk (~/mcp-ariel-memory, ~/docs, memory trees, ariel base) — restore from ariel episodes or ask the clone; test strategy direction from episode 6118: complex over numerous, add A/B/E2E/mutation/chaos. Exclude whitelist +!/docs/vps-global-plan.md."} {"text": "memory_search FIXED (2026-09-12, compose-debug protocol): root cause — rag/dual_route.py s2_exhaustive consumed wiki.list_all() rows as dicts (r.get), but wiki/manager.list_all returns WikiEntry pydantic models; the existing FakeWiki test returned dicts and masked the live path (env-parity gap, same class as fork's ARIEL_HASH_EMBEDDINGS issue). Fix: boundary normalization [r if isinstance(r, dict) else r.model_dump() for r in rows] with comment; regression test test_s2_route_list_all_wiki_models added (RED with exact prod error, then GREEN); P7.1 pattern sweep: only consumers of manager-level list_all are ops.py:110/256 + wiki.py:68 — both use attribute access (clean); index-level list_all consumers get dicts (safe). Verification: test_dual_route.py 32/32, test_rag/ + test_routing_integration.py 89/89. PENDING: local commit (pre-commit = full pytest gate, heavy) + CHANGELOG entry; running MCP server keeps old code until next session restart."} {"text": "memory_search fix COMMITTED (2026-09-12, ariel repo commit 2990233, pre-commit full pytest gate green 1660/0, df /tmp 55% checked before run). Commit chain: 3d4927c..2990233. Files: rag/dual_route.py (+boundary normalization), tests/test_rag/test_dual_route.py (+FakeWikiModels regression test), CHANGELOG.md (+Fixed entry). FLAKE DISCOVERED: tests/test_shared/test_crypto_smoke.py::test_roundtrip_dict is order/timing-sensitive — failed ONCE in full-suite with my changes staged, then passed on clean HEAD full run AND on repeat full run with changes (1660/0). Evidence triple: clean-HEAD green / with-changes red once / with-changes green. Candidate for P1 suite-hardening (the polish roadmap) — crypto smoke is pollution-sensitive. Fix takes effect for agents on next MCP server start (new session); current session's stdio server still runs old code."} {"text": "Plugin load error ROOT CAUSE finally proven (2026-09-12 07:00 restart): opencode plugin loader invokes EVERY exported function as a plugin factory. memory-files.ts exported searchMemory (named function) -> loader called it with the plugin input object -> tokenize(text) -> text.toLowerCase() on the object -> \"text.toLowerCase is not a function\" at 00:11:59 and 07:00:11, while the default export succeeded 22s later (plugin worked anyway — error was cosmetic but recurring every restart). Earlier double-export hypothesis (MemoryFiles named + default) was WRONG. Fix: un-export searchMemory (only `default` is a function export now, verified via bun: function exports=[default]); stack commit 5847bcb. Post-restart verification summary: memory_search hybrid WORKS (fix 2990233 live), memory-files injected 07:00:33, ariel-inject one-shot 690B + recall caching, all hooks green. Next restart should show ZERO plugin load errors for the first time."} {"text": "CLEAN RESTART VERIFIED (2026-09-12 07:04): first restart with ZERO plugin load errors after the un-export fix (5847bcb). Historical total: 3 load errors ever (2x ariel-inject morning corpses 09-11, 1x memory-files 07:00 pre-fix). Post-restart: memory-files injected 07:04:45, ariel-inject one-shot 690B 07:04:47, recall caching active. LESSON (hit twice same day): grep patterns self-match in opencode permission log — the log echoes the agent's own bash command text; error counts must anchor on line structure (level=ERROR at line start + full message shape), not substrings. Plugin loader saga fully closed."} {"text": "a-memory plan archive committed to stack (2026-09-12, 6d87142): 87 files / 28176 insertions — full docs/compose tree (specs 30, plans 38, reports 18) + provenance README at ~/docs/compose/a-memory/. Rationale: .gitignore:24 of the public a-memory repo kept the whole plan chain disk-only; stack-config (local bare, GitHub-proof) is the durable home per owner directive. Sync policy documented in README: re-copy on plan updates + commit diff. Owner stack docs inventory now: ROADMAP.md, skills/README-compose.md, agent/compose.md, skills/compose-* (14), plugins/memory-files.ts, command/{dream,distill}.md, docs/vps-global-plan.md, docs/compose/a-memory/*. NOT committed (data, not docs): file memory tree ~/.local/share/opencode/memory/ — candidate for future versioning decision."} -{"text": "vps2 (node-b, Tailscale 198.51.100.12) recon 2026-09-12, read-only via ssh `` (key ``, user in sudo group but password required): Ubuntu 22.04.5, 66d uptime, 30G 54%, RAM 1.9G. Service-A ACTIVE (unit /etc/systemd/system/service-a.service w/ PROVIDER_TOKEN in plaintext — ROTATE after removal; binary /usr/local/bin/service-a 85M, data ~/.service-a 28M, port 0.0.0.0:19455, running as backup-user). NO AmneziaWG traces on host OS: wg-quick@wg0 plain WireGuard 10.9.0.1/24, UDP 51832 + tailscale 41641. docker/containerd running but sock denied to user — AWG may be containerized (need sudo). service-b-launcher: /usr/local/bin/service-b-launcher -public, 0.0.0.0:19880, 55d uptime, unknown purpose — ask owner. backup-ui.service = Tailscale-bound :8080 (legit backup UI, db ~/.config/backup-ui/). Tailscale peers: workstation (win), phone (android, offline 103d), node-a active. WORKING HYPOTHESIS for T28: old clients = plain-WG protocol (work), new keys generated by the app in AWG protocol (junk packets) cannot handshake with plain-WG server — NOT a ufw problem. Plan: backup wg0.conf+peers, install amnezia-wg (kernel 5.15 compat check against amnezia-vpn/amnezia-wg), migrate wg0->awg0 preserving keys+port, parallel-test new client before switching old ones. Plan updated in vps-global-plan.md (stack 2de8c6c). BLOCKERS/QUESTIONS for owner: sudo path for privileged steps (she runs prepared commands OR NOPASSWD list), service-b purpose/keep-decision, confirm AmneziaVPN app is the client generator, confirm AWG lives in docker or nowhere."} -{"text": "Owner confirmed \"v3\" = client v5 (5.0.x, AWG 3 support added in 5.0.0.5 July 2026, current 5.0.2.1 Sep 3). Full calculation recorded in vps-global-plan.md (stack dbcd994): (1) app reinstall NOT required for existing setup — her 4.8.14-era app + current containers compatible; 5.0 app needed only for AWG-3 server. (2) Client configs for our server will be GENERATED by us from server-side keys (private fork gives deploy code, .vpn format) — app imports file, no ssh credentials to app. (3) Key-loss protection: mount configs not bake (root cause fix), server keys in ~/keyvault/vps2/amnezia-configs-backup.tgz, device private keys live in device apps — export before any app update (server holds only pubkeys + psk). (4) Parallel architecture: current containers untouched on 51831/51830, AWG-3 server on NEW ports, per-device cutover, rollback = containers still running. (5) Server path decision pending: fresh containers w/ mounted configs vs native DKMS awg. OPEN: far client on legacy wg0 (51832, ufw-blocked) alive/dead; old-client vs AWG-3-server compatibility assumed incompatible (parallel ports cover); exact 5.0 .vpn config format to read from fork."} -{"text": "NATIVE AWG MIGRATION CUTOVER SUCCESS (2026-09-12 11:57 UTC): vps2 production VPN moved from docker container awg-container-2 to native amneziawg-go — same pubkey ( same port 51830/udp, same AWG params ( ), 8 peers, handshakes resumed within 75 seconds (11s/15s/1m21s fresh). Stack: awg0.conf (root 600, keys PRESERVED byte-for-byte from keyvault — mounted not baked), PostUp/PostDown MASQUERADE 10.9.1.0/24 via ens3 INSIDE [Interface] section (CRITICAL LESSON: awg-quick v3.1 recognizes hook lines ONLY in [Interface] section — hooks after [Peer] sections pass to setconf as unrecognized lines), amneziawg-go 0.0.20250522 extracted from container to /usr/local/bin/, awg-tools v3.1.20260812 installed from official release zip (sha256 verified), systemd unit awg-quick@.service created+enabled. ufw now honestly allows 51830/51831 udp (native no longer bypasses firewall). ROLLBACK WINDOW OPEN: container awg-container-2 stopped but NOT removed (docker start = instant revert); container awg-container-1 (51831, one dormant peer 1d17h) still running — next to migrate; legacy wg0 (51832) confirmed dead (0 handshakes, 1 stale peer) — decommission candidate. First cutover attempt failed on hook placement (parser lesson above), auto-rollback worked as designed, owner's ssh blipped (her ssh rides the tunnel)."} -{"text": "Native AWG traffic FIXED (2026-09-12 12:19): after cutover, handshakes succeeded but NO traffic — root cause: ufw FORWARD chain policy DROP; container setup hid forwarding behind docker chains, native awg0 needed explicit `ufw route allow in on awg0 out on ens3` (plus the existing INPUT port rule). Traffic confirmed flowing (1.74 MiB sent to client in 30s, fresh handshakes). Owner's ssh-blip explanation: her interactive ssh rides the Amnezia tunnel (blips during cutover windows), Tailscale-based ssh unaffected (separate network). Two-lesson pattern for native VPN migration: (1) hooks only in [Interface] section, (2) INPUT + FORWARD both required under ufw. Remaining vps2 items: 51831 container migration (same path), container removal after stability days, PROVIDER key rotation (owner), legacy wg0 decommission decision."} -{"text": "DAY CLOSE CHECKPOINT (2026-09-12, session ses_f6e223cc, ~350k tok): MIGRATION DAY COMPLETE. Shipped: (1) opencode migration — 12 provider keys, config parity, permissions (live-fire tested), ariel-inject verified; (2) Compose Ф1 ported+accepted in combat (agent + 14 skills, stack 21966ee); (3) memory-files plugin (file memory + search tool + /dream /distill, stack 959f736 5847bcb); (4) memory_search bug fixed in live ariel repo (2990233, red-green cycle); (5) a-memory plan archive → stack (6d87142), ROADMAP (252d7ad); (6) vps2: T27 cleanup (service-a+service-b out, PROVIDER key EXPOSED - owner rotates), native AWG migration DONE (51830 live, keys preserved, ufw honest, systemd persistent), wg0 decommissioned, security audit clean (a9b4b5d 3dc0614), private fork (private client fork) + deploy doc (e9349f9). OPEN for next session: 51831 container migration (same path), docker rm insurance containers after stability days, PROVIDER rotation (owner), R1 skills audit, Ф2 compose after Ф1 week, ROADMAP.md has full state. New session should wake via ariel inject + MEMORY.md + ROADMAP pointers — this is the Ф1 continuity test."} +{"text": "node-b (node-b, Tailscale 198.51.100.12) recon 2026-09-12, read-only via ssh `` (key ``, user in sudo group but password required): Ubuntu 22.04.5, 66d uptime, 30G 54%, RAM 1.9G. Service-A ACTIVE (unit /etc/systemd/system/service-a.service w/ PROVIDER_TOKEN in plaintext — ROTATE after removal; binary /usr/local/bin/service-a 85M, data ~/.service-a 28M, port 0.0.0.0:19455, running as backup-user). NO AmneziaWG traces on host OS: wg-quick@wg0 plain WireGuard 10.9.0.1/24, UDP 51832 + tailscale 41641. docker/containerd running but sock denied to user — AWG may be containerized (need sudo). service-b-launcher: /usr/local/bin/service-b-launcher -public, 0.0.0.0:19880, 55d uptime, unknown purpose — ask owner. backup-ui.service = Tailscale-bound :8080 (legit backup UI, db ~/.config/backup-ui/). Tailscale peers: workstation (win), phone (android, offline 103d), node-a active. WORKING HYPOTHESIS for T28: old clients = plain-WG protocol (work), new keys generated by the app in AWG protocol (junk packets) cannot handshake with plain-WG server — NOT a ufw problem. Plan: backup wg0.conf+peers, install amnezia-wg (kernel 5.15 compat check against amnezia-vpn/amnezia-wg), migrate wg0->awg0 preserving keys+port, parallel-test new client before switching old ones. Plan updated in vps-global-plan.md (stack 2de8c6c). BLOCKERS/QUESTIONS for owner: sudo path for privileged steps (she runs prepared commands OR NOPASSWD list), service-b purpose/keep-decision, confirm AmneziaVPN app is the client generator, confirm AWG lives in docker or nowhere."} +{"text": "Owner confirmed \"v3\" = client v5 (5.0.x, AWG 3 support added in 5.0.0.5 July 2026, current 5.0.2.1 Sep 3). Full calculation recorded in vps-global-plan.md (stack dbcd994): (1) app reinstall NOT required for existing setup — her 4.8.14-era app + current containers compatible; 5.0 app needed only for AWG-3 server. (2) Client configs for our server will be GENERATED by us from server-side keys (private fork gives deploy code, .vpn format) — app imports file, no ssh credentials to app. (3) Key-loss protection: mount configs not bake (root cause fix), server keys in ~//node-b/amnezia-configs-backup.tgz, device private keys live in device apps — export before any app update (server holds only pubkeys + psk). (4) Parallel architecture: current containers untouched on 51831/51830, AWG-3 server on NEW ports, per-device cutover, rollback = containers still running. (5) Server path decision pending: fresh containers w/ mounted configs vs native DKMS awg. OPEN: far client on legacy wg0 (51832, ufw-blocked) alive/dead; old-client vs AWG-3-server compatibility assumed incompatible (parallel ports cover); exact 5.0 .vpn config format to read from fork."} +{"text": "NATIVE AWG MIGRATION CUTOVER SUCCESS (2026-09-12 11:57 UTC): node-b production VPN moved from docker container awg-container-2 to native amneziawg-go — same pubkey ( same port 51830/udp, same AWG params ( ), 8 peers, handshakes resumed within 75 seconds (11s/15s/1m21s fresh). Stack: awg0.conf (root 600, keys PRESERVED byte-for-byte from — mounted not baked), PostUp/PostDown MASQUERADE 10.9.1.0/24 via ens3 INSIDE [Interface] section (CRITICAL LESSON: awg-quick v3.1 recognizes hook lines ONLY in [Interface] section — hooks after [Peer] sections pass to setconf as unrecognized lines), amneziawg-go 0.0.20250522 extracted from container to /usr/local/bin/, awg-tools v3.1.20260812 installed from official release zip (sha256 verified), systemd unit awg-quick@.service created+enabled. ufw now honestly allows 51830/51831 udp (native no longer bypasses firewall). ROLLBACK WINDOW OPEN: container awg-container-2 stopped but NOT removed (docker start = instant revert); container awg-container-1 (51831, one dormant peer 1d17h) still running — next to migrate; legacy wg0 (51832) confirmed dead (0 handshakes, 1 stale peer) — decommission candidate. First cutover attempt failed on hook placement (parser lesson above), auto-rollback worked as designed, owner's ssh blipped (her ssh rides the tunnel)."} +{"text": "Native AWG traffic FIXED (2026-09-12 12:19): after cutover, handshakes succeeded but NO traffic — root cause: ufw FORWARD chain policy DROP; container setup hid forwarding behind docker chains, native awg0 needed explicit `ufw route allow in on awg0 out on ens3` (plus the existing INPUT port rule). Traffic confirmed flowing (1.74 MiB sent to client in 30s, fresh handshakes). Owner's ssh-blip explanation: her interactive ssh rides the Amnezia tunnel (blips during cutover windows), Tailscale-based ssh unaffected (separate network). Two-lesson pattern for native VPN migration: (1) hooks only in [Interface] section, (2) INPUT + FORWARD both required under ufw. Remaining node-b items: 51831 container migration (same path), container removal after stability days, PROVIDER key rotation (owner), legacy wg0 decommission decision."} +{"text": "DAY CLOSE CHECKPOINT (2026-09-12, session ses_f6e223cc, ~350k tok): MIGRATION DAY COMPLETE. Shipped: (1) opencode migration — 12 provider keys, config parity, permissions (live-fire tested), ariel-inject verified; (2) Compose Ф1 ported+accepted in combat (agent + 14 skills, stack 21966ee); (3) memory-files plugin (file memory + search tool + /dream /distill, stack 959f736 5847bcb); (4) memory_search bug fixed in live ariel repo (2990233, red-green cycle); (5) a-memory plan archive → stack (6d87142), ROADMAP (252d7ad); (6) node-b: T27 cleanup (service-a+service-b out, PROVIDER key EXPOSED - owner rotates), native AWG migration DONE (51830 live, keys preserved, ufw honest, systemd persistent), wg0 decommissioned, security audit clean (a9b4b5d 3dc0614), private fork (private client fork) + deploy doc (e9349f9). OPEN for next session: 51831 container migration (same path), docker rm insurance containers after stability days, PROVIDER rotation (owner), R1 skills audit, Ф2 compose after Ф1 week, ROADMAP.md has full state. New session should wake via ariel inject + MEMORY.md + ROADMAP pointers — this is the Ф1 continuity test."} {"text": "Ф1-continuity FIX (2026-09-12, ses: current, uncommitted in ~/mcp-ariel-memory): ROOT CAUSE chain — (1) day-close checkpoints landed agent-layer while all startup surfaces read user-layer; (2) opencode interactive sessions never register in SessionStore (close_session has ZERO production callers) so recap showed only CLI audit sessions; (3) one-shot inject was static 690B critical set, no rolling actuality; notes.md existed but nothing injected it. FIX SHIPPED: continuity.py gained _pick_substantive (message_count-first selection), recap_checkpoint axis (session_summary L3 episodes BOTH layers via EpisodicMemory(layer=agent)), recap_notes axis (config continuity.notes_glob, template-only notes skipped, opencode-only, hermes/cowagent unaffected); autohooks inject --with-recap merges recap+cache_break before critical set; ariel-inject.ts uses --with-recap at one-shot + 20s SIGKILL timeout guard on ALL spawnSync/fire (intermittent CLI hang defense); mimocode base config refreshed (9 missing keys deep-merged, embeddings.url preserved) + continuity.notes_glob added. DATA: session_ended dispatched with real day summary → recap_checkpoint live; 61 garbage L4 fact: rows hidden (visibility=hidden, REVERSIBLE) — auto-extractor pollutes L4 with mangled slug keys (ate the owner's greeting too), extractor audit = P1 follow-up. GATE: ruff+format+mypy(232f)+pytest 1669 green; mypy fixes in shared/embeddings.py were pre-existing (3d4927c legacy). CHANGES UNCOMMITTED — awaiting the owner's call. 5 new staging proposals appeared from session_ended extract_and_stage."} {"text": "F1-continuity PIPELINE DAY 2 (2026-09-12, committed): ariel f9eaeb6 + stack 83b5306 + cowagent f3cf0466. SHIPPED: (1) recap-actuality at session start (--with-recap: substantive session, dual-layer checkpoint, config-gated notes tail) wired into opencode/mimocode/hermes/cowagent surfaces; (2) dialogic guard — episode_promotion was the ACTUAL L4 polluter (forensics: 100% fact: rows source=episode_promotion, distiller innocent, L0 single-door holds), guards in distiller+both consolidation paths; (3) diff_gap: preview now written at dispatch time, empty gaps dropped (19,171 empty markers + 107 garbage fact rows DELETED from 3 bases, backups sanitation-backup-20260912.jsonl.gz); (4) CRITICAL FOUND: alembic env.py fileConfig silenced EVERY ariel logger at MCP server startup (disable_existing_loggers) — production logging dead since migration to alembic; fixed+regression-tested; (5) save() preserves visibility on re-upsert; (6) hash-fallback counter+WARNING; (7) novelty gates in all per-turn recall paths; (8) 20s SIGKILL timeouts everywhere (17h orphan killed). PROPOSALS: mimocode queue cleared (3 approved→L4 #732-734, 8 rejected), hermes #280 rejected (privacy). FOLLOW-UPS: cowagent session_ended wiring needs a real session-end concept (Persona-C recap_checkpoint empty until then); importance scorer over-values chat text (residual poetic lines pass dialogic guard — needs eval No.11-style tuning); 'hidden' outside visibility whitelist (consider first-class); this session's MCP server still old-code — full effect next session."} diff --git a/tests/test_shared/test_broadcast.py b/tests/test_shared/test_broadcast.py index d06aad27..8bc5dd41 100644 --- a/tests/test_shared/test_broadcast.py +++ b/tests/test_shared/test_broadcast.py @@ -36,7 +36,7 @@ def test_live_broadcast_fixture_catch_rate() -> None: "memory_search FIXED (2026-09-12, compose-debug protocol): root cause — rag/dual_route.py s2_exhaustive.", "DAY CLOSE CHECKPOINT (2026-09-12, session ses_f6e223cc, ~350k tok): MIGRATION DAY COMPLETE.", "memory_search fix COMMITTED (2026-09-12, ariel repo commit 2990233, pre-commit full pytest gate green 1660/0).", - "NATIVE AWG MIGRATION CUTOVER SUCCESS (2026-09-12 11:57 UTC): vps2 production VPN moved.", + "NATIVE AWG MIGRATION CUTOVER SUCCESS (2026-09-12 11:57 UTC): node-b production VPN moved.", ], ) def test_broadcast_examples_detected(line: str) -> None: