manual: инструкция «как пользоваться»; watch: первичный проход, коллекции по правилу ядра; приёмка: контур демона

This commit is contained in:
2026-10-02 03:43:25 +03:00
parent 2c7bca0d82
commit 3054705176
6 changed files with 648 additions and 27 deletions
+59 -3
View File
@@ -8,8 +8,10 @@
# Контуры:
# 1. Юнит-тесты всех модулей (gradle test --rerun-tasks).
# 2. Мутационная проверка тестов (e2e/mutation_check.py) — тесты обязаны падать на сломанном коде.
# 3. Сквозной recall@5 на эталонном корпусе (e2e/run_e2e.py).
# 4. MCP-протокол живым клиентом (scripts/mcp_probe.py), включая холодный старт.
# 3. Сквозной индекс эталонного корпуса: идемпотентность и ровно 3 коллекции.
# 4. Сквозной recall@5 на эталонном корпусе (e2e/run_e2e.py).
# 5. Демон слежения: первичный проход, 3 базы, отсутствие «locked» при параллельном чтении.
# 6. MCP-протокол живым клиентом (scripts/mcp_probe.py), включая холодный старт.
set -uo pipefail
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
@@ -81,7 +83,61 @@ if python3 e2e/run_e2e.py --cli "$CLI" --root "$CORPUS" --k 5 > /tmp/memo-e2e.lo
else
echo "ПРОВАЛ recall:"; tail -25 /tmp/memo-e2e.log; FAILED+=("recall"); fi
section "5. MCP: протокол + холодный старт"
section "5. демон слежения (memo-watch)"
# Демон проверяется отдельным контуром: три его дефекта (6 баз вместо 3, отсутствие первичного
# прохода, «locked» при параллельном чтении) прошли мимо юнит-тестов и зелёной приёмки.
WATCH="$ROOT/memo-watch/build/install/memo-watch/bin/memo-watch"
WROOT=/tmp/memo-accept-watch
rm -rf "$WROOT"; cp -r "$CORPUS" "$WROOT"
find "$WROOT" -name .memo -type d -prune -exec rm -rf {} + 2>/dev/null
"$WATCH" "$WROOT" > /tmp/memo-watch.log 2>&1 &
WPID=$!
WOK=1
# ждём не «на глаз», а по факту: пока в каждой коллекции не появятся чанки (максимум 120 с)
for _ in $(seq 1 120); do
READY=$(python3 - "$WROOT" <<'EOF'
import sqlite3, glob, sys
root = sys.argv[1]
dbs = sorted(glob.glob(root + "/**/.memo/index.db", recursive=True))
if len(dbs) != 3:
print(0); raise SystemExit
n = 0
for db in dbs:
try:
c = sqlite3.connect("file:" + db + "?mode=ro", uri=True)
if c.execute("SELECT COUNT(*) FROM chunks").fetchone()[0] > 0: n += 1
c.close()
except Exception:
pass
print(n)
EOF
)
[ "$READY" = "3" ] && break
sleep 1
done
WDBS=$(find "$WROOT" -name index.db | wc -l)
echo " баз индекса : $WDBS (ожидается 3)"
if [ "$WDBS" != "3" ]; then
echo "ПРОВАЛ: демон завёл $WDBS баз вместо 3"; WOK=0
fi
if [ "$READY" = "3" ]; then
echo "OK первичный проход: все 3 коллекции наполнены сразу после старта"
else
echo "ПРОВАЛ: первичного прохода нет — наполнено коллекций: $READY из 3"; WOK=0
fi
# параллельно с работающим демоном — тот же файл читаем поиском
WSEARCH=$("$CLI" search "$WROOT/life/books" "кто ведёт рассказ в романе" --k 1 2>&1)
if echo "$WSEARCH" | grep -qi "locked"; then
echo "ПРОВАЛ: database is locked при чтении во время работы демона"; WOK=0
elif [ -n "$WSEARCH" ]; then
echo "OK поиск при работающем демоне отвечает без locked"
else
echo "ПРОВАЛ: поиск при работающем демоне ничего не вернул"; WOK=0
fi
kill "$WPID" 2>/dev/null; wait "$WPID" 2>/dev/null
[ "$WOK" = "1" ] || FAILED+=("демон слежения")
section "6. MCP: протокол + холодный старт"
COLD=/tmp/memo-accept-cold
rm -rf "$COLD"; cp -r "$CORPUS" "$COLD"
find "$COLD" -name .memo -type d -prune -exec rm -rf {} + 2>/dev/null