Compare commits

...

26 commits

Author SHA1 Message Date
root
b14559a706 session_productize: 6-й тик — product_create всё ещё 500 (карточка id 13 жива), КОРЕНЬ дублей починен: гонка core42.json -> ложное НЕЗНАКОМО -> анти-спам пропускал дубли (фикс в systemone-bench a1114a2: атомарная запись + retry чтения; после фикса 'знакомо', 0/300 гонка-падений) 2026-10-09 21:18:17 +03:00
root
aa781359f1 hour digest 2026-10-09 15:05: systemone-insight done (t-261009141729), очередь пуста 2026-10-09 18:04:51 +03:00
root
48c497329e hour digest 13:05 UTC: батч 2/2 — шаблон гипотез + рыночная симуляция (E[dAUM]: вход +24..94k vs −1.5k), эскалаций 0, очередь пуста 2026-10-09 16:12:55 +03:00
root
54beef9dd7 market simulation: logit конкуренция + сегменты + сценарии входа, MC 400 trials, E[dAUM] по сценариям + чувствительность (t-261009123019-a32bb3) 2026-10-09 16:12:04 +03:00
root
f8985ca596 product hypothesis template v1: конкуренты/цена/профиль клиента/сегменты+каналы (t-261009122125-45922c) 2026-10-09 16:03:14 +03:00
root
c82b412632 hour digest 12:05 UTC: session_productize дубль №5 закрыт по cooldown (500 жив, карточка id 13 new), витрина без изменений, эскалаций 0, очередь пуста 2026-10-09 15:05:34 +03:00
root
0c885470fb session_productize: 5-й тик — product_create всё ещё 500, карточка id 13 жива (t-261009110039) 2026-10-09 15:04:20 +03:00
root
d7f34967b9 session_productize: 4-й тик — product_create всё ещё 500, карточка id 13 жива (t-261009045856) 2026-10-09 08:03:38 +03:00
root
fcadbc9890 hour digest 23:05 UTC: session_productize дубль №3 закрыт по cooldown (500 жив, витрина замерена), эскалаций 0, очередь пуста 2026-10-09 02:06:22 +03:00
root
962b6a0dc8 session_productize: третий дубль-тик (t-261008225713) — product_create всё ещё 500 (проба 23:0x), голос карточки id 13 восстановлен (votes=1), витрина кооператива замерена для price_ladder_audit, ранбук актуализирован 2026-10-09 02:05:54 +03:00
root
d7fda35e6d landing(sites): аудит-фиксы конверсии (t-261008082527-ae7b47): CTA-петля → presale.cil.by (живой Stripe, paid=1), social proof из стора (580k+ подписанных событий, 280k+ sec-тиков, 1 платник), contact-hint кликабелен (портал), FAQ первый открыт, canonical/og:url → simbiot.cil.by; проверено: HTML-валидатор 0 ошибок, якоря целы, туннель отдаёт обновлённую версию 2026-10-08 12:12:58 +03:00
root
d31f831058 hour digest 07:35 UTC: батч 3/3 закрыт — burn 1000x фикс (17.4 vs 17048 у.е.), presale P0 снят проверкой (CTA живой, paid=1, $100/mo), repo-синк детектор в дневном тике (behind=0, baseline); тесты 20/20 2026-10-08 10:33:12 +03:00
root
2e31249992 feat(scheduler): fetch origin + детектор дрейфа репо в дневном тике (t-261007082307-2e2294) — _repo_sync_check: git fetch + behind/ahead замер RepoSynced (1/день, дедуп), эскалация при behind>0 два дня ПОДРЯД (счётчик из истории стора — рестарт демона не сбрасывает); fetch-fail фиксируется честно (behind=unknown), тик не валит; порог SIMBIOT_REPO_SYNC_MAX_AGE_DAYS (default 2); тест 17 проверок hermetic (git-песочница), весь сюит 20/20 зелёный; живой прогон: behind=0 ahead=13, baseline записан 2026-10-08 10:32:05 +03:00
root
79e12548a9 presale P0 закрыт проверкой (t-261007082307-b8be52): CTA уже на live-линке buy.stripe.com/aFa28q8h14RpfPYgrK1B60a, цена live $100/mo (директива m-516173408 соблюдена), seats API paid=1; канон отстаёт (10 мест vs 100 бэкенда) — вопрос Alex; план продаж обновлён 2026-10-08 10:18:31 +03:00
root
c6cd6121da fix(aum): ставка блендед-burn 1000x ошибка порядка (0.000943 → 9.43e-7 у.е./токен) — сверка с фактом дайджеста 25.09–02.10 usageHistory: 2491.47/2.642e9=9.43e-7, кросс-чек по ключу simbiot-harness 9.97e-7; burn 7д теперь ≈17.4 у.е. вместо ≈17048; RATE_FLASH/RATE_FULL тот же порядок (t-261007082307-f3a1d6); тесты 19/19 зелёные 2026-10-08 10:06:55 +03:00
root
ca7ed57727 day 2026-10-07: эскалации закрыты (ретраи openai-клиента), burn-оценка из стора после миграции хоста, merge origin/master 32 коммита (19/19 тестов), AUM=26.7, presale $99.90-vs-$100 на live (Alex уведомлён), туннель самообновляет URL; 3 задачи на доску 2026-10-07 11:23:17 +03:00
root
b3f4fd902d docs(presale): расхождение live $99.90 vs директива $100 (m-516173408) — платник есть, деплой заблокирован отсутствием SSH/Forgejo-каналов на мигрированном хосте; запросы Alex (деплой, токены, путь к 9router-usage) — tg-notify отправлен 2026-10-07 11:22:25 +03:00
root
0308258d99 fix(landing): туннель сам трекает публичный URL — обёртка landing-tunnel-run.sh (systemd) парсит stdout localhost.run и обновляет PUBLIC_URL.txt при каждой смене субдомена; копия обёртки в scripts/ для воспроизводимости; DEPLOY.md: источник правды по URL + ограничение nokey-режима 2026-10-07 11:20:29 +03:00
root
a964329fdb merge origin/master (32 коммита портального инстанса) + burn-fallback в смерженном aum.py
Разрешены конфликты: isolated_runner (тщательный upstream-вариант killpg-wait), cli.py task-диспетчеризация upstream + guard ValueError, aum.py paths-изоляция + STORE_DB fallback.
Тесты после мерджа: 5 устарели к новой реальности — починены по интентам:
- test_aum 9c: hermetic-мок OPENROUTER_KEY (ключ уехал со старого хоста)
- test_router_env_fallback: патчит paths.env_path (runtime больше не читает _HARNESS_DIR)
- test_submit_title_guard: контракт task list = показ доски (exit 0), а не ERROR
- test_tg_commands: доска задач от тестовой ready-задачи (в прод-копии ready пусто)
- test_instance_isolation: SKIP RAG-контура без вендора /root/rag (хост мигрировал)
Итог: 19/19 зелёные
2026-10-07 11:18:05 +03:00
root
42de213c24 fix(e-261006080102,e-261006160049): ретраи openai-клиента SIMBIOT_LLM_MAX_RETRIES=3 (Connection error больше не роняет тик); fix(aum): burn-fallback из BudgetSpent стора когда БД 9router недоступна (миграция хоста) — burn 7д≈11422 у.е. вместо лживого 0.0, burn_src в замере/AumMeasured/aum_target; presale-лендинг: цена 100 USD по директиве m-516173408 (live на cil.by ещё 99.90 — деплой-канал недоступен, см. merge); sites/landing артефакты тика 10-05 2026-10-07 11:05:44 +03:00
root
bfe48e2133 feat(landing): Simbiot landing by GLM via opencode (t-261005113818) 2026-10-05 15:07:47 +03:00
root
2a2d68e324 hour digest 11:16 UTC: ASR + TimeoutExpired фиксы, статус активации 2026-10-05 14:25:48 +03:00
root
a1b9b4dfcb фикс ASR (e-261005105252): venv-питон для faster-whisper + av 15.1.0 + докачка модели; фикс TimeoutExpired.pid (e-261005110300): Popen+killpg; systemone-bench влёжен 2026-10-05 14:24:28 +03:00
root
139fbb1202 hour digest 13:06 UTC: разбор m-973940607 доставлен (fa307ad), эскалация 'list' закрыта с фикс-гардом (bc59609), тесты 15/15 2026-10-04 16:06:42 +03:00
root
fa307ad70c разбор голосового m-973940607: HTML-страница (ландшафт, смыслы, бизнес, шаги, транскрипт) — t-261004125611 2026-10-04 16:06:03 +03:00
root
bc59609d27 guard мусорных заголовков задач: 'list'/id без деталей → ValueError (e-261003094628, повтор e-261001082241); тест 14/14 2026-10-04 16:03:07 +03:00
45 changed files with 3114 additions and 48 deletions

View file

@ -0,0 +1,52 @@
# session_productize — состояние и ранбук довыполнения
> System One, программа `session_productize` (mcp: booking+product_create, safety=reversible).
> Тик 2026-10-08 ~15:00 UTC, агент OpenCode (ag-35). Задачи t-261008140313-4f6997 / t-261008143729-9dc44a.
## Состояние на 08.10 15:0x UTC
| Шаг | Статус | Детали |
|---|---|---|
| booking_slots (session, 20 мин) | ✅ жив | 70 слотов / 3 дня; ближайший 10.10 15:00 UTC (18:00 Мск) — замер 09.10 18:1x UTC |
| booking_create | ⏸ не звал | требует реального гостя (guest_email); бронь без клиента = мусор в календаре |
| product_create | ❌ HTTP 500 | перепроверено 09.10 18:1x UTC (6-я проба, тик t-261009172840) — всё ещё 500; карточка id 13 в `new`, votes=1, my_vote=true; дублей не создано (product_list: только Totem id=10) |
| **дубли-рекомендации** | ✅ **починено 09.10 18:1x UTC** | корень: гонка чтения/записи core42.json (21MB, неатомарный open("w") каждые ~15с; замер 36/200 чтений битые) → novelty молча 99 «НЕЗНАКОМО» → анти-спам (гейтит только «знакомо») пропускал дубль. Фикс: атомарный tmp+os.replace в Core42.save + retry в router42.load_json (systemone-bench a1114a2); службы перезапущены; после фикса 0/300 падений, тики стабильно «знакомо», дубль-задач нет |
| баг-репорт | ✅ подан | бэклог id 13, kan-карточка 09084e066881 |
| mail_send | ✅ починен порталом | проба ok (resend_id 01a11c10-b92b-…); карточка id 12 устарела |
| product_list / coop_catalog / booking_slots | ✅ работают | — |
## Ранбук: довыполнение после фикса product_create (один тик)
1. `product_create` — сессионный пакет. Базовый вариант, согласован с ценовой
директивой Simbiot (m-516173408: $100/mo без вариаций — сессия НЕ входит
в линейку presale, это отдельный оффер агента-студии):
- name: `AI Agent Session (60 min)`; price_usd: **150**; interval: `one_time`;
mode: `test` (первые 2-3 проводки), потом `product_set_mode → live` —
решение председателя, не молча.
2. Проверить `product_list` (анти-дубль) и `coop_catalog` (появление в витрине).
3. Первая продажа: письмо-оффер тёплым лидам из contact_list (status=replied/
converted) — НЕ холодная US-рассылка; копия председателю автоматически.
4. Бронь: `booking_create(kind='session', guest_name, guest_email, start_utc)`
после первого отклика; слоты уже проверены.
5. Замер: mrr_add цель +0.4k/2нед (advisor42 H8 top-1, prob 57.6%) — по факту
считать по первой проведённой сессии.
## Cooldown
Повторные попытки product_create до фикса портала — не запускать (cooldown).
Последняя проба: **09.10 18:1x UTC — всё ещё 500** (шестой дубль-тик,
t-261009172840). **Корень дублей починен** (гонка core42.json, см. таблицу):
после фикса ядро читает состояние как «знакомо», анти-спам гейтит повторы —
дубль-задачи session_productize больше не должны приходить; если продукт_создание
разблокируют порtal-фиксом бага id 13, довыполнение — одним тиком по шагам ниже.
Анти-старение по карточке: id 13 `new` votes=1 my_vote=true — на месте,
дубль-карточку не плодим.
## Витрина кооператива (замер 09.10 12:0x, для price_ladder_audit — без изменений с 08.10)
| Проект | Продукты (live) |
|---|---|
| Lunara | $4.99/mo |
| ReLeaf | $4.99/mo |
| Рилсап | $199 / $299 / $699 в мес |
| Симбиот | $33 / $59 / $1000 one_time |

13
cli.py
View file

@ -192,8 +192,9 @@ def main():
elif args.cmd == "tg-status": elif args.cmd == "tg-status":
_run_tg_status(args) _run_tg_status(args)
elif args.cmd == "task": elif args.cmd == "task":
# t-261003125513/e-261003125707: голое 'list'/'tasks'/'task list' — # t-261003125513/e-261003125707: 'task list' раньше создавал задачу-клон
# показать доску, а не плодить задачи-клоны # 'list' — теперь показывает доску (внутри 'list' как слово не валидируем,
# чтобы не породить рекурсию справки)
if getattr(args, "list_board", False) or (args.title or "").strip() in ("list", "tasks", "task list"): if getattr(args, "list_board", False) or (args.title or "").strip() in ("list", "tasks", "task list"):
es.init_db() es.init_db()
for t in commands.list_tasks(): for t in commands.list_tasks():
@ -216,7 +217,12 @@ def main():
if t.get("details"): if t.get("details"):
print(f" детали: {t['details']}") print(f" детали: {t['details']}")
else: else:
# bc59609: guard мусорных заголовков ('list'/id без деталей) — ValueError наружу
try:
tid, size = commands.submit_task(args.title, args.details, args.minutes) tid, size = commands.submit_task(args.title, args.details, args.minutes)
except ValueError as e:
print(f"ERROR: {e}")
sys.exit(1)
if args.await_delivery: if args.await_delivery:
commands.mark_awaiting_delivery(tid, reason="подана из TG с автодоставкой") commands.mark_awaiting_delivery(tid, reason="подана из TG с автодоставкой")
print(f"OK: задача {tid} [{size}] подана (результат уйдёт в TG при готовности)") print(f"OK: задача {tid} [{size}] подана (результат уйдёт в TG при готовности)")
@ -433,13 +439,14 @@ def main():
else: else:
print(f"AUM: {snap['aum']} ({snap['balances_src']})") print(f"AUM: {snap['aum']} ({snap['balances_src']})")
print(f"burn 9router: 24ч={snap['burn_24h']} · 7д={snap['burn_7d']} у.е. " print(f"burn 9router: 24ч={snap['burn_24h']} · 7д={snap['burn_7d']} у.е. "
f"({len(snap['spend_days'])} дн.)") f"({len(snap['spend_days'])} дн., источник: {snap.get('burn_src', '9router-db')})")
print(aum.build_policy_aum_summary(snap)) print(aum.build_policy_aum_summary(snap))
if args.record: if args.record:
es.init_db() es.init_db()
es.append_event("harness", "aum", "AumMeasured", { es.append_event("harness", "aum", "AumMeasured", {
"ts": snap["ts"], "aum": snap["aum"], "ts": snap["ts"], "aum": snap["aum"],
"burn_24h": snap["burn_24h"], "burn_7d": snap["burn_7d"], "burn_24h": snap["burn_24h"], "burn_7d": snap["burn_7d"],
"burn_src": snap.get("burn_src"),
"policy": {"aum_target": aum.build_policy_aum_summary(snap)}, "policy": {"aum_target": aum.build_policy_aum_summary(snap)},
}) })
print("OK: AumMeasured записан, aum_target обновлён в policy") print("OK: AumMeasured записан, aum_target обновлён в policy")

View file

@ -8,7 +8,7 @@
| Путь | Что | Права | Бэкап | Ротация | | Путь | Что | Права | Бэкап | Ротация |
|---|---|---|---|---| |---|---|---|---|---|
| `/root/simbiot-harness/.env` | ROUTER_KEY, OPENROUTER_KEY, SIMBIOT_TG_TOKEN, SIMBIOT_TG_ALLOWED, ROUTER/OPENROUTER_BASE_URL | 600 | да (см. §6) | ROUTER_KEY — по политике 9router; TG-токен — при подозрении | | `/root/simbiot-harness/.env` | ROUTER_KEY, OPENROUTER_KEY, SIMBIOT_TG_TOKEN, SIMBIOT_TG_ALLOWED, ROUTER/OPENROUTER_BASE_URL | 600 | да (см. §6) | ROUTER_KEY — по политике 9router; TG-токен — при подозрении |
| `/etc/simbiot/dashboard.env` | SIMBIOT_DASH_TOKEN (страница + /api/state, порт 8765 **открыт наружу 0.0.0.0**) | 600 | да | при демобилизации дашборда | | `/etc/simbiot/dashboard.env` | SIMBIOT_DASH_TOKEN (страница + /api/state, порт 8765 **открыт наружу 0.0.0.0**). 02.10 сервис восстановлен: `systemd-run --unit=simbiot-dashboard -p EnvironmentFile=/etc/simbiot/dashboard.env -p Restart=on-failure /root/simbiot-harness/venv/bin/python /root/simbiot-harness/harness/dashboard.py` | 600 | да | при демобилизации дашборда |
| git-история `/root/simbiot-harness` | `.env` убран из трекинга (e6a99ce); **01.10 push в приватный репо https://git.cil.by/symbiot/simbiot-harness (3361b67..d39f60f)**: аудит — `.env` в истории отсутствует (0 коммитов), скан sk-/bot/rsh/Bearer по git log -p --all чист. Утечки не было. | — | n/a | токен Forgejo symbiot (9f29709…) — admin-скоп, ротация при компрометации; в репо не хранить | | git-история `/root/simbiot-harness` | `.env` убран из трекинга (e6a99ce); **01.10 push в приватный репо https://git.cil.by/symbiot/simbiot-harness (3361b67..d39f60f)**: аудит — `.env` в истории отсутствует (0 коммитов), скан sk-/bot/rsh/Bearer по git log -p --all чист. Утечки не было. | — | n/a | токен Forgejo symbiot (9f29709…) — admin-скоп, ротация при компрометации; в репо не хранить |
**Git-канал харнеса (восстановлен 2026-10-05, e-261004110909-bd70b0):** fetch-токен **Git-канал харнеса (восстановлен 2026-10-05, e-261004110909-bd70b0):** fetch-токен

View file

@ -17,8 +17,7 @@
- [x] Social-proof бар в хиро (7 продуктов / 30 дней / 100% действий подписаны). - [x] Social-proof бар в хиро (7 продуктов / 30 дней / 100% действий подписаны).
- [x] Кворум-затычка: цитата + блок «полный пакет — см. offer» (заменяется - [x] Кворум-затычка: цитата + блок «полный пакет — см. offer» (заменяется
реальными цитатами, как только есть). реальными цитатами, как только есть).
- [x] Офсет сломанных ссылок Stripe: заглушка → `test_...`: пока стоит - [x] ~~Офсет сломанных ссылок Stripe~~ → **CTA ведёт на live-линк** `buy.stripe.com/aFa28q8h14RpfPYgrK1B60a` (проверено 10-08: cil.by/presale и presale.cil.by, цена $100/mo, seats API paid=1; см. docs/presale-live-check-2026-10-08.md).
, клик по CTA уводит на осмысленную заглушку, а не в битый чекаут.
- [x] CTA-контакт: `mailto:portal@cil.by` в FAQ + вейтист-ссылка в префутере. - [x] CTA-контакт: `mailto:portal@cil.by` в FAQ + вейтист-ссылка в префутере.
## Что держит продажи (структура страницы) ## Что держит продажи (структура страницы)
@ -32,9 +31,9 @@
## Road v3 — по приоритетам (каждый пункт — отдельная S/M-задача на доске) ## Road v3 — по приоритетам (каждый пункт — отдельная S/M-задача на доске)
1. [P0] **Живая ссылка Stripe** вместо заглушки на CTA. Сейчас единственный 1. [x] ~~[P0] Живая ссылка Stripe~~ — **снято 10-08**: линк уже стоит на CTA
блокер реальной продажи. Подготовить: платёж-линк $100/мес (или (live, paid=1; docs/presale-live-check-2026-10-08.md). Осталась сверка
чекаут-сессия через биллинг `127.0.0.1:8902` — billing-admin уже поднят). режима линка в Stripe-дашборде — 1 минута у Alex (ключей live у харнеса нет).
2. [P0] **Deploy на presale.cil.by**: Caddyfile-блок + DNS A-запись (хост уже 2. [P0] **Deploy на presale.cil.by**: Caddyfile-блок + DNS A-запись (хост уже
резолвится wildcard на этот сервер, TLS on-demand включён — нужен только резолвится wildcard на этот сервер, TLS on-demand включён — нужен только
vhost) — см. ниже, черновик конфига приложен. vhost) — см. ниже, черновик конфига приложен.

View file

@ -0,0 +1,12 @@
# Кандидат в лабу: Владимир Бугаев (boogvar)
- GitHub / LinkedIn: **BoogVAr** / boogvar (linkedin.com/in/boogvar)
- Автор **TessariDB** (tessaridb.com): мультимодельная БД — один стор, 11 движков
(документы, полнотекст, векторы, кэш, очереди, топики, геометрия, time-series,
графы, стримы), язык TessariQL, одна транзакция поверх всего.
Лицензия BSL 1.1 (free in prod). 0.23.0-beta.
- Связь с кооперативом: Alex работал с ним вместе в **Neon Labs** (neon-labs.org),
Alex был там CTO.
- Статус (2026-10-03): кандидат в лабу «на пробу», решение за Alex.
- Вводные, которых ждём: чем конкретно займётся, сколько времени готов вкладывать,
формат пробы, канал связи.

View file

@ -0,0 +1,45 @@
# Дневной отчёт Simbiot Harness — 2026-10-07
Дневной тик (day-20733). Глава дня: **хост мигрировал, и харнес частично ослеп** — починил, что мог починить сам; остальное оформил в запросы Alex.
## Эскалации (обе закрыты)
- **e-261006080102-a1a886, e-261006160049-2f6232** — «тик hour упал: Connection error». Root cause: openai-клиент в `_llm_worker.py` собирался с `max_retries: 1` → один транзиентный сетевой сбой к router.cil.by ронял весь тик (6 окт, 08:01 и 16:04 UTC). Фикс: `SIMBIOT_LLM_MAX_RETRIES=3` (env-конфигурируемо), ретраи с экспонентой от SDK. Воркер живёт в изолированном сабпроцессе — фикс подхватился со следующего тика без рестарта планировщика (сейчас: тики идут, ошибок новых нет).
## Крупное
### 1. Метрика burn ослепла после миграции хоста — fallback встроен
Хост сменился (~10-05, W11PC в LAN 192.168.0.200): живая БД 9router (`/root/.9router/db/data.sqlite`) осталась на старом хосте → `read_router_spend` молча возвращал `{}` → **burn 7д=0.0 был ложью в контексте каждого тика** (policy.aum_target). Фикс: `read_store_spend()` — расход из событий `BudgetSpent` собственного стора × блендед-ставка 0.000943 у.е./токен (калибровка по окну 25.09–02.10, docs/digests/9router-spend). Честность: в замере и в aum_target burn помечен как оценка (`burn_src`). Итог: burn 7д ≈ **16407 у.е.** (5 активных дней), 24ч ≈ 6100.
### 2. Репо раздвоился — слито (32 коммита портального инстанса)
Локальная линия (6 коммитов: ASR-фикс, GLM-лендинг, OpenDots) разошлась с origin/master (32 коммита: **AUM v1** с авто-балансами из API, **деплой-кит лендинга**, фиксы планировщика, изоляция инстансов SIMBIOT_HOME). Merge: 3 конфликта разрешены (isolated_runner — тщательный upstream-вариант killpg; cli.py — диспетчеризация upstream + guard ValueError; aum.py — paths-изоляция + мой STORE_DB). **5 тестов устарели к новой реальности — починены по интентам** (hermetic-мок OPENROUTER_KEY; патч paths.env_path вместо легаси _HARNESS_DIR; контракт `task list`=показ доски; тестовая ready-задача для доски; SKIP RAG-контура без вендора). Итог: **19/19 зелёные** (до дня: 14/15). Аудит стора после мерджа: 560641 событий, подписи и тампер-цепочка валидны.
### 3. AUM=26.7 у.е. — метрика цели снова измерима
После мерджа вернулся balances.json (upstream, 10-04): OpenRouter 26.7 (auto, stale), Stripe live 0.0, LLC/крипто — null (ручной ввод). Авто-refresh слеп: OPENROUTER_KEY/STRIPE_LIVE_KEY жили на старом хосте — запрос в списке для Alex. `AumMeasured` перезаписан; aum-pilot: базлайн −237k, лучшая конфигурация +193k (+182%) — вердикт «адвокат», без авто-применения.
### 4. Presale: директива цены нарушена на live, платник есть
`cil.by/presale` — 200, счётчик мест бэкендовый: **paid=1, taken=2, total=100** (первый реальный платник!). Но live-страница содержит **7 упоминаний $99.90/mo** против директивы m-516173408 ($100 везде) — локальный канон уже на $100, задеплоить не могу (см. блокировки). Документ: docs/presale-price-divergence-2026-10-07.md, Alex уведомлён в TG (ключ presale-price-diverge-20261007).
### 5. Публичный URL лендинга — самообновление
localhost.run в nokey-режиме выдаёт случайный субдомен на каждый рестарт → PUBLIC_URL.txt протухал (поймал три смены URL за день). Фикс: обёртка `/usr/local/bin/landing-tunnel-run.sh` (systemd) парсит stdout ssh и перезаписывает файл; копия в scripts/. Текущий URL жив (200). Ограничение честно задокументировано: стабильный URL = только прод/именованный туннель.
## Блокировки (нужен Alex, всё в одном документе)
1. **Деплой presale $100** — одна команда с машины с SSH на прод (scripts/deploy_landing.sh, REPO_RAW → presale-канон).
2. **Push-канал репо** — Forgejo-токен (write) в .env этого инстанса: сейчас push невозможен, все коммиты дня (5) живут только локально.
3. **Ключи авто-балансов AUM** — OPENROUTER_KEY, STRIPE_LIVE_KEY.
4. **Путь к 9router usageHistory** — сетевой путь/ключ, иначе burn остаётся оценкой.
## Мелочи и факты дня
- 192.168.0.254 — No route to host (сервер Alex в LAN выключен/недоступен).
- Биллинг :8902 на этом хосте не поднят (жил на старом) — счётчик presale при этом жив (API на проде).
- Мусорные задачи 'list' — проверено: только в изолированных тестовых БД, прод чист.
- RAG-вендор /root/rag отсутствует → rag_lookup пуст (лимит 1/тик не тратится зря); тест изоляции честно SKIP.
## План на следующий цикл
- **[заблокировано на Alex]** деплой presale $100 + push-канал + ключи AUM — пере-проверить ответы, отработать.
- **[M]** вычистить burn-оценку: сверить блендед-ставку с реальным usage при первом доступе к 9router; развилка flash/full по моделям в событиях.
- **[M]** P0 из SALES-PLAN: живой Stripe-линк на CTA (сейчас офсет-заглушка) — блокер реальной продажи №2 после цены.
- **[S]** ретроспектива: почему локальная линия не увидела upstream 2 дня (fetch не в ритме дня) — добавить шаг `git fetch` в дневной манифест.

View file

@ -0,0 +1,23 @@
# Дайджест часа 2026-10-04 13:06 (UTC)
## События часа
- **Разбор голосового m-973940607 + HTML-страница** (t-261004125611-196177 [M]):
- на транскрипте Deepgram nova-3 (438 с, сделан на min-уровне по ключу Алекса) собран структурированный разбор: ландшафт поля (Колорадо-центр 70-х, Contemplative Education Network Швейцария/США, PhD-программа в Италии, японский центр с заявленной работой с бизнесом — на запрос не ответили), 8 смыслов (пробуждение поля от спячки, разрозненность без порядка, язык для предпринимателей как ключевой барьер, знания коренных народов, тезис «у всех отшибло память» про короткие пути решений, пример естественных родов, мотивация = трансформация а не деньги, текущая фаза = разведка связей);
- прямой разбор ответа про бизнес: не отказ, а запрос на конкретизацию («свой или вместе с кем-то — уточни вопрос»); + 5 следующих шагов и TL;DR;
- артефакт `docs/m-973940607-analysis.html` (валидный HTML, 7 секций, транскрипт в раскрывающемся блоке), доставлен в TG через `tg-deliver-file` (идемпотентно, OK), коммит `fa307ad`.
## Эскалации
- **e-261003094628-275e28 (info, min→hour) закрыта** — серия задач с заголовком `list`:
- первопричина по стору (event 25238–25313): min-LLM звал `cli.py task list` / `cli.py task <id>` (просмотр доски/деталей), а субкоманда `task` принимает позиционный **title** → argparse молча подсунул «list»/чужой id и подал мусорную задачу. Та же первопричина, что e-261001082241 — деградации пайплайна НЕТ;
- все 4 задачи серии уже были закрыты как no-op на min — реqueue не требуется;
- фикс по корню: guard в `commands.submit_task` — субкомандо-подобный заголовок (`list/tasks/status/events/help/complete/…`) или чужой `t-…` id **без деталей** → `ValueError`; CLI печатает ERROR с подсказкой и exit 1 (не тихая подача). Нормальные подачи и слово с деталями — проходят;
- новый тест `tests/test_submit_title_guard.py` — 14/14; полный прогон 15/15 файлов зелёный; коммит `bc59609`.
## Статусы
- Батч часа закрыт целиком: t-261004125611-196177 [M] — done (результат доставлен в TG).
- Эскалаций открытых нет.
- AUM: не замерен (balances.json нет); burn 9router: 24ч=0.0 · 7д=0.0 у.е.
- Задача «уточнить вопрос о бизнесе» (автор голосового просит конкретики: свой продукт или партнёрство) — на стороне Алекса, ждём.
## Мелочи
- S: незакоммичены чужие (min-уровня) изменения `docs/KEYS-REGISTRY.md` и `docs/candidates/` — следующим тикам min/hour закоммитить свои.

View file

@ -0,0 +1,25 @@
# Часовой дайджест 11:16 UTC, 2026-10-05
## Главное
- **ASR починен полностью** (эскалация e-261005105252 закрыта). Три бага сразу:
1) `_FW_PYTHON` в harness/voice.py был захардкожен `/usr/bin/python3`, а faster-whisper стоит в venv харнеса;
2) HF-кэш large-v3-turbo битый (3.7МБ указателей вместо 1.6ГБ) — докачан;
3) av 19.0.1 вырезал параметр `metadata_errors`, обязательный для faster-whisper 1.2.1 — откат на av 15.1.0.
Боевая проверка: голосовое m-973940613 расшифровано large-v3-turbo (11с аудио → текст).
- **Фикс падения min-тика** (эскалация e-261005110300 закрыта): isolated_runner читал несуществующий `TimeoutExpired.pid` → AttributeError маскировал SchedulerTimeout. Переписан на Popen(start_new_session=True)+killpg(proc.pid)+reap. Юнит-прогон: группа детей умирает целиком.
- Ответ Alex по батчу m-973940613..615: статус systemone-bench (внедрено, метрики: GA 3520 сим/13с бьёт greedy/random, e2e 81.2%), локальная модель поставлена (faster-whisper в venv), доступ к 192.168.0.254 — SSH открыт, ждём креды.
- Коммит a1b9b4d: ASR-фикс + TimeoutExpired-фикс + systemone-bench навыки/отчёт влёжены в git.
## События часа
- min: 8 тиков, 6 тихих (гейт), 1 рабочий: расшифровка m-973940612 не удалась (ASR сломан) — теперь уже починено выше; 1 тик упал (TimeoutExpired.pid) — починено.
- Разговор с Alex ушёл вперёд: он попросил поставить opencode, сделать лендинг, зайти на 192.168.0.254 (SSH доступен, ждём креды) — мин-уровень отработал.
- AUM: не замерен (balances.json нет); burn 9router 7д=0.0 у.е.
- Тесты после фиксов: 15/15 зелёные. Аудит стора: 326982 события, подписи и тампер-цепочка валидны.
## Планы на след. час
- Ждём креды к 192.168.0.254 от Alex (SSH открыт).
- systemone-bench: следующий шаг интеграции — подключение decision-модели на тиках выбора действий (сейчас валидировано, но на тиках не дергается).
## Активация фиксов
- ASR-фикс (voice.py) — АКТИВЕН: поллер (simbiot-harness-tg.service) перезапущен 14:25 UTC+3 с новым кодом, новые голосовые расшифровываются сразу.
- TimeoutExpired-фикс (isolated_runner.py) — вступит в силу при следующем рестарте планировщика (simbiot-harness.service): сейчас рестарт прибил бы работающие тики (hour + мин-задача по лендингу). Баг редкий (1 случай) и неразрушающий: claimed-задачи возвращаются в очередь даже старым путём (scheduler.py except → TaskPromoted). Рестартить в тихое окно.

View file

@ -0,0 +1,56 @@
# Часовой дайджест ~07:35 UTC, 2026-10-08
## Главное — все 3 задачи батча закрыты (3/3)
1. **Burn-оценка: найдена и исправлена ошибка порядка 1000×** (f3a1d6, коммит c6cd612).
Сверка с фактом дайджеста 25.09–02.10 (снимок живой usageHistory): 2491.47 у.е. /
2.642 млрд токенов = **9.43e-7 у.е./токен**; кросс-чек по нашему ключу той недели
(299.11 у.е. / ~300M токенов = 9.97e-7) — сходится. В коде стояло 0.000943
(9.43e-**4**) — порядок величины вчитан неверно при калибровке. Итог: burn 7д
из стора = **≈17.4 у.е.**, а не ≈17048.71. AUM-планировщик больше не искажён:
AumMeasured перезаписан, aum_target в policy обновлён (`cli aum --record`) —
контексты всех уровней уже видят честные числа. RATE_FLASH/RATE_FULL приведены
к тому же порядку. Живая БД ~/.9router на хосте по-прежнему нет (миграция);
usage-эндпоинтов API у ключа роутера нет (404, сессионные) — сверка с живой
usageHistory остаётся на момент возврата пути к БД.
2. **Presale P0 закрыт проверкой, а не работой** (b8be52, коммит 79e1254).
Премиса задачи устарела: CTA на presale.cil.by и в каноне ведёт на
`buy.stripe.com/aFa28q8h14RpfPYgrK1B60a` (HTTP 200), **не** test_-заглушка.
Цена live $100/mo (6 упоминаний, $99.90 нет — расхождение из
presale-price-divergence-2026-10-07.md снято). Счётчик мест живой из биллинга
(seats API paid=1, taken=2, total=100) — через этот чекаут прошёл реальный
платник. Создание нового линка невозможно и не нужно: STRIPE_LIVE_KEY на хосте
нет, биллинг :8902 недоступен (timeout). Найден новый дрейф: канон захардкодил
**10 мест** против бэкенда **100** — до решения Alex деплой канона не запускать.
Артефакт: docs/presale-live-check-2026-10-08.md. Alex уведомлён (tg-notify,
ключ presale-live-check-2026-10-08).
3. **Детектор дрейфа репо в дневном тике** (2e2294, коммит 2e31249).
Инцидент «2 дня не видели 32 коммита» покрыт механикой, не памятью LLM:
`_repo_sync_check` в scheduler.py — git fetch origin + замер behind/ahead →
событие RepoSynced (1/день, дедуп по дате); при behind>0 **два дня подряд** —
EscalationRaised (счётчик из истории стора, рестарт демона не сбрасывает).
Ошибка fetch не валит тик (behind=unknown честно фиксируется). Порог
SIMBIOT_REPO_SYNC_MAX_AGE_DAYS (default 2). Живой прогон: **behind=0**, ahead=13,
baseline RepoSynced записан (id 571862).
## События часа
- Эскалации: открытых 0 (все acked ранее; новых не поднималось).
- Минутные: 6 тихих тиков (гейт, LLM не запускался) — пустой час снизу.
- Тесты: полный сюит **20/20 зелёный** (новый tests/test_repo_sync.py — 17 проверок
hermetic в git-песочнице; остальные не задеты).
- Доска: очередь пуста, 3 задачи hour-батча закрыты отдельно по мере готовности.
- Среда: DNS-резолв cil.by с хоста флапает (были таймауты) — зеркалим через
/etc/hosts → 204.168.232.21; router.cil.by стабилен.
## Активация фиксов
- **burn-фикс (aum.py + aum_target)** — контексты уже честные (замер перезаписан в
стор вручную); модуль демона перегрузится при рестарте.
- **repo-sync шаг (scheduler.py)** — заработает после рестарта
simbiot-harness.service; рестартить в тихое окно (сейчас прибил бы рабочие тики).
Baseline на сегодня уже записан живым прогоном — день не потерян.
## Планы на след. час
- Ждём ответ Alex: режим Stripe-линка в дашборде (live?), 10-vs-100 мест,
доступ к биллингу :8902 для P1-задач.
- Рестарт демона в тихое окно (активация repo-sync + burn-модуля).
- Push 13 коммитов в origin — заблокирован отсутствием кредов (KEYS-REGISTRY),
известное ограничение, эскалации не требует: детектор теперь следит за behind.

View file

@ -0,0 +1,25 @@
# Часовой дайджест ~23:05 UTC, 2026-10-08
## Батч: 1/1 закрыт
**t-261008225713-7b46d8 [M] session_productize (дубль №3 от ядра router42) — done по cooldown-протоколу.**
- product_create: одна живая проба (по ранбуку, не серия) — **всё ещё HTTP 500**, фикс портала не доехал. Дублей продуктов нет (product_list: только Totem id=10). Карточка бэклога id 13 жива (new); голос агента восстановлен после случайного toggle-off в feature_vote (votes=1, voted=true — финальное состояние идемпотентно).
- Booking-половина жива: 80 session-слотов/3д, ближайший 09.10 15:00 UTC (18:00 Мск).
- Бонус этого тика: свежая витрина кооператива под исполнимую альтернативу `price_ladder_audit` — Lunara $4.99 / ReLeaf $4.99 / Рилсап $199-699/mo / Симбиот $33-59-1000 one_time (все live). Внесена в ранбук.
- Ранбук актуализирован и закоммичен: следующая проба product_create — не чаще раза на дубль-тик; после фикса одна проводка mode=test, live — только решением председателя.
## Систематика (третий дубль подряд)
Ядро router42 третий раз рекомендует программу, заблокированную багом её же тула (id 13). Вопрос маски исполнительных органов (сломан тул → программа выпадает из кандидатов) уже поднят председателю в тике 15:09 (t-261008140858, «мысль ядра»). До ответа — cooldown-режим: дубль-рекомендация закрывается одной пробой + авто-ответом «блокирован багом id 13». Каждый такой тик всё же даёт +1 замер (витрина, слоты, статус бага) — не нулевой.
## Состояние
- Эскалации: открытых 0 (секция пуста, обрабатывать нечего).
- Минутные: 6 тихих тиков (гейт, LLM не запускался) — сырья снизу не было.
- Очередь после тика: пуста (tasks_ready 0); доска — 19 done.
- AUM-контекст: без изменений (26.7 у.е., burn 7д ≈17.4).
## Следующий час
- Если ядро снова отдаст session_productize — закрыть по cooldown (ранбук в artifacts/), больше одной пробы не делать.
- Исполнимые альтернативы того же класса для ядра: warm_calls_week (booking жив), price_ladder_audit (витрина уже замерена — данные в ранбуке).

View file

@ -0,0 +1,28 @@
# Часовой дайджест ~12:05 UTC, 2026-10-09
## Батч: 1/1 закрыт
**t-261009110039-3058c3 [M] session_productize (дубль №5 от ядра router42) — done по cooldown-протоколу.**
- product_create: одна живая проба по ранбуку (AI Agent Session 60min / $150 one_time / mode=test) — **всё ещё HTTP 500**; фикс портала не доехал. Дублей продуктов нет (product_list: только Totem id=10).
- Баг-карточка бэклога id 13 (kan 09084e066881): status=new, votes=1 — жива, дубль-карточку не плодил.
- Booking-половина жива: 77 session-слотов/3д, ближайший 09.10 16:00 UTC (19:00 Мск). Бронь без реального клиента не создаю (guest_email обязателен).
- Бонус-замер: coop_catalog без изменений — Lunara/ReLeaf $4.99, Рилсап $199–699/mo, Симбиот $33/59/1000 (все live).
- Ранбук artifacts/session-productize-runbook.md актуализирован (5-я проба) и закоммичен.
## Систематика (пятый дубль подряд)
Ядро router42 пятый раз рекомендует программу, заблокированную багом её же тула (id 13), при novelty 99 — память прецедентов у ядра не пополняется. Вопрос поднят председателю (t-261008140858). До ответа — cooldown: одна проба на дубль-тик + авто-ответ «блокирован багом id 13».
## Состояние
- Эскалации: открытых 0 (5 старых — все acked; обрабатывать нечего).
- Минутные: 6 тихих тиков (gate:quiet, LLM не запускался).
- Очередь после тика: пуста (tasks_ready 0); доска — 22 done.
- AUM-контекст: без изменений (26.7 у.е.).
- Тех.минут: 1 ошибка min-уровня (TimeoutExpired — старый знакомый), на час не влияет.
## Следующий час
- Дубль session_productize → закрыть по cooldown (ранбук в artifacts/), одна проба.
- Исполнимые альтернативы для ядра: warm_calls_week (booking жив), price_ladder_audit (витрина свежая, 09.10 12:0x).
- После фикса портала: довыполнение ранбука одним тиком (product_create test → product_list → coop_catalog → оффер тёплым лидам → booking_create по отклику).

View file

@ -0,0 +1,45 @@
# Часовой дайджест ~13:05 UTC, 2026-10-09
## Батч: 2/2 закрыто (t-261009122125 → t-261009123019, по очереди от старейшей)
**1. t-261009122125-45922c [M] — шаблон продуктовых гипотез расширен, done.**
- Отдельного шаблона в базе знаний не было (RAG + grep пусто) — собран с нуля:
`docs/product-hypothesis-template.md` (125 строк, в git).
- Три обязательных блока по запросу Alex: конкурентное исследование (таблица 3+ аналогов,
обоснование цены против cheapest/closest/premium, проверка каннибализации с live-продуктами
витрины), профиль клиента (сегмент/WTP/триггер/возражение №1/кворум тёплым контуром),
сегменты и каналы по тирам. Плюс метрики с критерием смерти и обязательный фильтр AUM.
- Заякорен на живое: PRICE-DIRECTIVE-SIMBIOT ($100/mo без скидок), витрина 09.10,
SALES-PLAN-1000-US. Заполненный пример — AI Agent Session $150 из ранбука.
**2. t-261009123019-a32bb3 [L] — рыночная симуляция, done (файл доставлен в TG).**
- `scripts/market_sim.py` + `docs/market-simulation-2026-10-09.md` (в git): 4 конкурента
(DIY $25 / фрилансер $100 / консультант $150 / студия $300), logit-выбор клиента,
3 сегмента (WTP $60–300), отток 8%/мес, рекуррентный биллинг по директиве, сессии $150.
- Монте-Карло 400 trial'ов / 12 мес: не входить E[ΔAUM]=−1 488; тёплый контур +24 107
(окупаемость 1-й мес, 40 подписчиков); presale-воронка +93 611; микс +92 929 — оба
упираются в капу 100 мест. Чувствительность ±6% — вывод устойчив.
- Главные выводы: входить выгодно в любом сценарии (фильтр AUM пробит ×1000); узкое место —
капа мест и трафик, НЕ конверсия/цена; цену не трогать (паритет с фрилансером уже даёт
топ-долю выбора 27–35%); сессии ждут фикса бага id 13.
## Консолидация минутных (выжимка)
6 тихих тиков подряд (gate:quiet, LLM не запускался) — событий важнее порога не было.
Тех.минуты: 2 ошибки hour-уровня (Connection error при старом тике) и 1 min (TimeoutExpired) —
все старые, соответствуют уже acked-эскалациям e-261006160049 / e-261005110300; новых не породили, обработки не требуют.
## Состояние
- Эскалации: открытых 0 (все 5 в списке — acked; разбирать нечего).
- Очередь после тика: пуста (ready 0), доска 25 done.
- AUM: 26.7 у.е., burn 7д=28.55 (≈124/мес; store-оценка, живая БД 9router недоступна).
- session_productize: без изменений — cooldown действует, product_create 500 (баг id 13),
дубль-рекомендаций ядра в этом часу не было.
## Следующий час
- Очередь пуста — ждать новых задач; исполнимые альтернативы для дублей ядра: warm_calls_week,
price_ladder_audit (ранбук в artifacts/session-productize-runbook.md).
- Симуляция дала решение для председателя: расширение капы мест (100 → выше) — самый большой
рычаг AUM после запуска трафика; вынести в повестку дня/недели при первом прогибе мест.

View file

@ -0,0 +1,37 @@
# Часовой дайджест ~15:05 UTC, 2026-10-09
## Батч: 1/1 закрыто
**t-261009141729-ca621a [M] [systemone-insight] мысль ядра для председателя — done.**
- Опора на живое состояние ядра: 41018 эпизодов стартапа vs 12955 студии, память 20000 пар,
BTC $82801 (×1.0088), рекомендация session_productize — уже 6-й дубль (novelty 99,
память прецедентов не пополняется, баг бэклога id 13 блокирует product_create).
- Мысль для студии: ядро оптимизирует знакомую зону, а не ценность — связка
«дубль-рекомендация + необучаемая память» не лечится рынком. Предложение: маска исполнимости
в арбитре core (мертвая программа выпадает из кандидатов после 2 неудач, а не 6) + правило
70/30 на разнообразие (30% тиков принудительно на нераскрытые advisor_digest /
cross_agent_adoption).
- Вопрос-крючок председателю: дать арбитру core право вето по маске исполнимости — или
продолжать оплачивать обучение ядра на повторениях?
- Решение не дублирует прошлую мысль (t-261008140858: «ядро рекомендует в вакууме») —
сдвиг от диагностики к конкретному механизму (вето + квота разнообразия).
## Консолидация минутных (выжимка)
Час тихий: минутные тики все gate:quiet (LLLM не запускался), сырья снизу не было.
Тех.минуты: errors hour=2 (Connection error) и min=1 (TimeoutExpired) — все старые,
соответствуют уже acked-эскалациям e-261006160049 / e-261005110300; новых не породили.
## Состояние
- Эскалации: открытых 0 (5 в списке — все acked; разбирать нечего).
- Очередь после тика: пуста (ready 0), доска 27 done.
- AUM: 26.7 у.е., burn 7д=≈26.19 (store-оценка × блендед-ставка; живая БД 9router недоступна).
- session_productize: cooldown действует, в этом часу дублей ядра не было (был systemone-insight).
## Следующий час
- Очередь пуста — ждать новых задач. Исполнимые альтернативы для дублей ядра: warm_calls_week,
price_ladder_audit (ранбук artifacts/session-productize-runbook.md).
- Ждём ответа председателя на вопрос о праве вето арбитра core (поднят в сегодняшней мысли);
до ответа дубли закрываются по cooldown-протоколу.

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,106 @@
# Рыночная симуляция Simbiot: конкуренты + клиенты + сценарии входа
> t-261009123019-a32bb3 [L], 2026-10-09. Модель и прогоны: `scripts/market_sim.py`
> (400 trial'ов Монте-Карло, seed 42, горизонт 12 мес). Поверх расширенного шаблона
> гипотез `docs/product-hypothesis-template.md` (t-261009122125): блоки 1 (конкуренты/цена),
> 2 (клиент), 3 (сегменты/каналы) — напрямую параметризуют модель.
> Аргументы запуска: `./venv/bin/python scripts/market_sim.py --trials 400 --seed 42 --sens`
## 1. Что моделируем
Рынок «автономный агент вместо ручной рутины» с четырьмя конкурентами, тремя сегментами
клиентов и тремя стратегиями входа Simbiot (+базлайн «не входить»).
**Клиент выбирает по logit-полезности**: U = (value − price) / scale. Simbiot конкурирует
ценой $100/mo (директива m-516173408 — без скидок, рекуррентно) и value-бустом доверия
у тёплых лидов (видели живой прод харнеса).
**Конкуренты** (блок 1 шаблона; value — калибровочное допущение, цены — реальные бенчмарки):
| Конкурент | Цена | value | Приорная доля рынка |
|---|---|---|---|
| DIY (LLM-подписка + ручные промпты) | $25/mo | 30 | 45% |
| Upwork/AI-фрилансер (часами) | ~$100/mo | 110 | 30% |
| AI-консультант соло (сессии) | ~$150/mo | 140 | 15% |
| AI-студия (проектно) | ~$300/mo | 260 | 10% |
**Сегменты** (блок 2 шаблона): s1 соло-основатель (35% потока, WTP $130, value-множитель 1.0);
s2 индиз/автоматизатор (40%, WTP $60, ×0.75 — цена $100 выше их WTP, они остаются в DIY-лагере);
s3 операционка МБ (25%, WTP $300, ×1.3).
**Калибровка честная**: logit_scale=100 даёт сквозную холодную конверсию presale-лендинга
≈0.5–1.1% — верх диапазона плана SALES-PLAN-1000-US; тёплая ≈35% (буст ×1.3). Отток 8%/мес.
## 2. Сценарии входа
| Сценарий | Тёплые лиды/мес | Посетители presale/мес (мес1→12) | Платный трафик | Сессии $150 |
|---|---|---|---|---|
| D_no_entry | 0 | 0 | — | — |
| A_warm_only | 15 | 0 | нет | 12%/тёплых |
| B_presale_funnel | 12 | 5k → 95k | 60% лидов, CAC $22 | 5% |
| C_mixed | 15 | 3k → 90k | 50% лидов, CAC $22 | 12% |
Экономика харнеса — из живого замера: burn 124 у.е./мес (28.55/7д, store-BudgetSpent),
+1 у.е./мес на активного подписчика, +5 у.е. на сессию (допущения). AUM₀ = 26.7 у.е.
## 3. Результаты (E[] по 400 trial'ам, 12 мес, у.е.)
| Сценарий | rev подписки | rev сессии | CAC | burn | **E[ΔAUM]** | P(ΔAUM>0) | подписчики (медиана) | окупаемость |
|---|---|---|---|---|---|---|---|---|
| D_no_entry | 0 | 0 | 0 | 1 488 | **−1 488** | 0% | 0 | не окупается |
| A_warm_only | 24 119 | 1 840 | 0 | 1 852 | **+24 107** | 100% | 40 | 1-й мес |
| B_presale_funnel | 97 215 | 906 | 1 835 | 2 675 | **+93 611** | 100% | 100 (капа мест) | 2-й мес |
| C_mixed | 95 146 | 1 805 | 1 340 | 2 682 | **+92 929** | 100% | 100 (капа мест) | 2-й мес |
Доли выбора (сегмент s1): Simbiot $100/mo — 34.8% тёплый / 26.6% холодный контур; далее
фрилансер 19–22%, DIY 18–21%, консультант 16–18%, студия 12–13%.
## 4. Чувствительность (C_mixed)
| Вариант | E[ΔAUM] | P(>0) | Подписчики |
|---|---|---|---|
| база | 92 929 | 1.00 | 100 |
| churn 5% / 12% | 96 285 / 88 331 | 1.00 | 100 |
| logit_scale 80 / 130 (вера в продукт) | 93 333 / 92 280 | 1.00 | 100 |
| value −15% / +15% | 91 563 / 93 713 | 1.00 | 100 |
| reach ×0.5 / ×2 (качество трафика) | 87 658 / 96 019 | 1.00 | 100 |
Вывод устойчивости: разброс E[ΔAUM] ±6% по всем допущениям — **потому что капа мест (100)
связывает спрос во всех вариантах**. Модель говорит: узкое место — не конверсия и не цена,
а (а) лимит мест и (б) способность залить трафик в воронку.
## 5. Выводы для решений
1. **Входить — да, при любой стратегии**: даже A_warm_only без рекламного бюджета даёт
+24.1k у.е. за год против −1.5k у «не входить», окупаемость с 1-го месяца. Фильтр AUM
(+1% = +0.27 у.е.) пробивают все сценарии входа с запасом ×1000.
2. **Капа мест — главный ограничение роста**: B/C упираются в 100 мест (медиана). Расширение
лимита (директива, решение Alex) стоит на порядки больше для AUM, чем любая оптимизация
конверсии. Порядок: сначала пролить тёплый контур (A), поднять трафик (B/C), потом
поднимать капу.
3. **Тёплого контура не хватает на полный пролив**: A_warm_only оставляет ~60 мест
незанятыми — воронка presale (трафик) нужна как второй шаг, не как альтернатива.
4. **Сессии $150 — маленький, но почти чистый маржинальный доход** (~1.8k/год в C,
burn 5 у.е./сессия) и верх воронки к подписке. Реально заблокировано багом портала
id 13 (ранбук session_productize) — фикс портала стоит в цепочке.
5. **Цену не трогать**: директива запрещает скидки; модель подтверждает — паритет цены с
фрилансером ($100) при более высокой value уже даёт топ-долю выбора. Скидка не нужна
для победы в logit-выборе.
## 6. Ограничения модели (честно)
- value конкурентов и reach_in_market=4% — калибровочные допущения, не замеры; доля рынка
конкурентов (share0) — приор для калибровки value, в динамике выбора не участвует.
- Трафик-рампы посетителей (5k→95k/мес) — цель из SALES-PLAN-1000-US, не факт; при нулевом
трафике B/C деградируют к A_warm_only (+24k), не к отрицательному.
- Не моделировалось: реакция конкурентов на вход (ценовые войны), саппорт-нагрузка на
подписчиков, возвраты/refund, сезонность; burn-per-sub — плоские 1 у.е./мес.
- Сессии предполагают фикс бага product_create (id 13); до фикса компонента сессий нет.
- Модель deterministic-demand per month, no network effects (кейсы/соцдоказательство
повышают value — квазично через warm_mult).
## 7. Артефакты
- Модель: `scripts/market_sim.py` (детерминированный seed, `--sens` — чувствительность).
- Шаблон гипотез, на котором стоит модель: `docs/product-hypothesis-template.md`.
- Входные якоря: docs/PRICE-DIRECTIVE-SIMBIOT.md ($100/mo, 100 мест), artifacts/session-productize-runbook.md (сессия $150, витрина), docs/SALES-PLAN-1000-US (воронка), docs/aum.md (метрика ΔAUM).

View file

@ -0,0 +1,52 @@
# Presale live-check: CTA / Stripe-линк / цена / места — 2026-10-08
> Задача t-261007082307-b8be52 [L, P0 «живой Stripe-линк на CTA»] · часовой поток Simbiot.
> Премиса задачи («CTA уводит на офсет-заглушку test_...») — устарела: проверено вживую, заглушки нет.
## Проверено вживую (2026-10-08 ~07:1x UTC, с хоста W11PC)
| Что | Состояние | Доказательство |
|---|---|---|
| Live presale.cil.by | **200, 26 293 B** | curl -sSL (DNS через /etc/hosts 204.168.232.21; резолв cil.by с этого хоста флапает — router.cil.by резолвится, cil.by/presale.cil.by иногда нет) |
| CTA на live | **ведёт на `https://buy.stripe.com/aFa28q8h14RpfPYgrK1B60a`** (1 вхождение) | grep по HTML |
| Канон `docs/simbiot_presale_landing.html` | тот же линк на CTA (строки 216, 244) — **не test_-заглушка** | grep |
| Цена на live | **$100/mo — 6 упоминаний**, $99.90 не встречается | grep по HTML |
| Checkout-страница Stripe | **HTTP 200**, classic-вариант | curl -D (Fastly, cache HIT) |
| Счётчик мест на live | **бэкендовый**: `GET /api/presale/seats` → `{"paid":1,"taken":2,"total":100}` | живой API |
| Реальная продажа | **paid=1** — через этот чекаут прошёл платник | seats API + docs/digests (первый платник зафиксирован 10-07) |
| Рекуррентность | FAQ: «monthly billing via Stripe, cancel anytime»; в чекауте subscription/month-строки | HTML |
Вывод: **P0-блокер «CTA → заглушка» снят** — линк на месте и собирает деньги
(paid=1). Дополнительно снят пункт расхождения цены из
docs/presale-price-divergence-2026-10-07.md: live теперь $100/mo (директива m-516173408 соблюдена).
## Что НЕ удалось проверить с этого хоста (честно)
1. **Режим линка (live vs test)** в Stripe-дашборде: STRIPE_LIVE_KEY на этом
хосте нет (KEYS-REGISTRY — ключи на старом инстансе/проде). Косвенные
признаки ЗА live-режим: paid=1 (реальные деньги), launch-kit 10-04
подтверждал линк живым, pk_test в HTML-оболочке чекаута — generic-код
кэшируемой Fastly-оболочки (одинаковой для всех линков), НЕ ключ сессии.
Решающая проверка — одна минута в Stripe-дашборде у Alex.
2. **Биллинг** `127.0.0.1:8902` — отсутствует на этом хосте (жил на старом);
прямой `204.168.232.21:8902` — connection timeout (файрвол). Создание
новых платёж-линков через API отсюда невозможно — только через прод.
## Новый дрейф (канон отстаёт от прода)
Live: счётчик мест из бэкенда, **total=100** (показывает 98 left).
Канон `docs/simbiot_presale_landing.html`: `var left=10,total=10` —
захардкожено **10 мест** (показал бы 8). Число мест — решение Alex
(10 из SALES-PLAN-PRESALE-PAGE vs 100 из бэкенда/SALES-PLAN-1000-US);
харнес не выбирает сам — вопрос задан в уведомлении.
Канон при деплое перезапишет живой счётчик заглушкой → до решения
деплой канона на presale.cil.by НЕ запускать без сверки.
## Открытые вопросы к Alex (уведомление отправлено)
1. Подтвердить режим линка `aFa28q8h14RpfPYgrK1B60a` в Stripe-дашборде (live?).
2. Сколько мест в presale: 10 или 100 (канон vs бэкенд)?
3. Поднять биллинг-доступ для харнеса (8902 за файрволом) для P1-задач
(счётчик/атрибуция) и будущих платёж-линков.
— Simbiot harness (hour), t-261007082307-b8be52.

View file

@ -0,0 +1,46 @@
# Presale: расхождение цены live vs канон + блокировка деплоя
> День 2026-10-07. Проверено живьём: `cil.by/presale` (HTTP 200, 13.2KB).
## Факты
| Что | Состояние |
|---|---|
| Live-страница cil.by/presale | **$99.90/mo — 7 упоминаний** (hero, offer, FAQ, футер) |
| Канон `docs/simbiot_presale_landing.html` | **$100/mo везде** (директива m-516173408, коммит 42de213) + mock-секции «What it looks like on a Tuesday» |
| Счётчик мест | бэкендовый (`/api/presale/seats`): **paid=1, taken=2, total=100** — первый реальный платник |
| Канон vs live | разные файлы: live не содержит правок канона (mock-секции, $100) |
## Директива цены (m-516173310/11, обновлена m-516173408)
Единая цена **$100/mo во всех упоминаниях**, занижение запрещено. Live
продаёт за $99.90 — прямое нарушение директивы, и это цена, по которой уже
есть **1 платник**.
## Почему харнес не задеплоил (осознанно, не отговорка)
Хост харнеса мигрировал (~2026-10-05) на **W11PC** (192.168.0.200, LAN):
- SSH-ключей нет по политике (KEYS-REGISTRY §3–4) → на прод (204.168.232.21) не попасть;
- FORGEJO_PULL_TOKEN живёт в `/root/coop/.alfa_env` на старом хосте — здесь отсутствует;
- push в origin без креденшелов не работает (`could not read Username`), поэтому канон с $100 сейчас только локально.
## Что нужно (одна команда от Alex, у кого SSH на прод)
```bash
# с хоста, где есть SSH-ключ на прод и актуальный канон:
ssh root@204.168.232.21 'bash -s' < scripts/deploy_landing.sh
```
Скрипт из origin/master (день 10-05): бэкапит index.html, тянет канон из
git.cil.by, ставит, проверяет. **Важно**: REPO_RAW указывает на
`simbiot-landing-v2-live.html` — для presale-страницы нужно направить на
актуальный канон (`docs/simbiot_presale_landing.html` после пуша) —
параметр REPO_RAW.
## Открытые вопросы Alex
1. Задеплоить $100-канон на cil.by/presale (директива нарушена, платник есть).
2. Вернуть харнесу push-канал: FORGEJO-токен (write) в .env этого инстанса.
3. Ключи авто-балансов AUM: OPENROUTER_KEY / STRIPE_LIVE_KEY — сейчас
авто-refresh слеп (26.7 — stale-замер 10-04).
4. Живая БД 9router недоступна с этого хоста — burn считается оценкой из
стора (блендед-ставка). Если есть сетевой путь к usageHistory API — дать ключ.

View file

@ -0,0 +1,125 @@
# Шаблон продуктовых гипотез Simbiot (расширенный)
> Создан по запросу Alex (t-261009122125-45922c, 2026-10-09). Отдельного шаблона в базе
> знаний не было — собран с нуля, заякорен на живые артефакты: docs/PRICE-DIRECTIVE-SIMBIOT.md
> (директива прайса $100/mo, без скидок), витрина кооператива (замер 09.10, artifacts/session-productize-runbook.md),
> docs/SALES-PLAN-1000-US.md, конкурентный анализ film.cil.by (t-261009122421-805cfd).
> Основа расширения — три обязательных блока по запросу Alex: (1) конкуренты и цена,
> (2) профиль клиента, (3) сопутствующее: сегменты и каналы.
## Зачем
Каждая продуктовая гипотеза Simbiot проходит через фильтр AUM («добавляет ≥ +1% к AUM
в горизонте год+?»). Расширение закрывает главный источник мусорных гипотез: цену,
взятую «с потолка», и клиента, описанного словом «все». Без конкурентного обоснования
цены и профиля платящего клиента гипотеза не идёт в работу — это quality gate, не бюрократия.
## Как пользоваться
1. Скопировать секцию «Карта гипотезы» на каждый новый продукт/пакет.
2. Заполнить все блоки; пометка `(обязательно)` = без неё гипотеза не проходит на доску.
3. Цена — только через директиву (docs/PRICE-DIRECTIVE-SIMBIOT.md): единое число, без
скидок и вариаций; любое изменение прайса — отдельное решение Alex.
4. Проверить против фильтра AUM внизу карты. Не прошла — не отбрасывать, а записать
причину в «Вывод» (память для будущих итераций).
---
## Карта гипотезы
### 0. Ядро гипотезы
- **ID/название**:
- **Продукт** (что продаём, 1 фраза):
- **Проблема клиента** (какую боль закрываем, словами клиента):
- **Решение** (что именно делаем):
- **Форм-фактор**: one_time / monthly / пакет (часы? слоты? лимиты?)
- **Цена** (одно число, по директиве): $
### 1. Конкурентное исследование `(обязательно)`
Заполняется ДО обоснования цены — цена выводится из этой таблицы, а не наоборот.
| Конкурент/аналог | Что продаёт | Цена (мес/раз) | Сильное | Слабое |
|---|---|---|---|---|
| (минимум 3 прямых/косвенных аналога; для внутренних продуктов кооператива — строки витрины) | | | | |
- **Позиционирование против каждого**: чем отличаемся (1 фраза на строку).
- **Обоснование нашей цены**:
- относительно cheapest-аналога (премия за что?);
- относительно closest-аналога (паритет или ниже/выше — почему);
- относительно premium-аналога (что не даём — честно).
- **Проверка на каннибализацию**: не отбирает ли продукт платящих у уже live-продуктов
кооператива (Lunara/ReLeaf $4.99/mo, Рилсап $199–699/mo, Симбиот $33/$59/$1000)?
Если да — почему это чистый плюс к AUM, а не перенос.
### 2. Профиль целевого клиента `(обязательно)`
- **Сегмент** (узкий!): не «разработчики», а например «соло-фрилансер, вёл 3+ клиентов
одновременно, устал от ручного планирования».
- **Платёжеспособность**: сколько уже платит за смежные инструменты (из таблицы
конкурентов), бюджет на категорию $/мес.
- **Триггер покупки** (в какой момент жизни клиента боль обостряется).
- **Возражение №1** и ответ на него.
- **Где обитает / как найдём**: каналы достоверного доступа (см. блок 3).
- **Кворум для валидации**: скольких таких клиентов можем достать тёплым контуром
(личный круг, contact_list replied/converted — не холодная рассылка).
### 3. Сопутствующее: сегменты и каналы `(обязательно)`
- **Сегменты приоритетом**: 1) … 2) … 3) … (кто первый — там самая сильная боль/доступ).
- **Каналы по тиру** (из SALES-PLAN-1000-US):
- Тир-0 тёплый контур: личный круг, TG/x.com хроники харнеса, Show HN / Reddit;
- Тир-1 платный: какая площадка, при каком CAC-пороге включаем;
- Партнёрский/реферальный: кто уже имеет доступ к сегменту.
- **Первое касание**: что именно показываем сегменту №1 (продукт живой у нас в проде —
главный якорь доверия).
### 4. Метрики и валидация
- **Северозвёздная метрика успеха** (одна): например mrr_add, presale-предзаказы, проведённые сессии.
- **Цель на 2 недели** (измеримо):
- **Критерий смерти гипотезы**: при каких числах закрываем и что записываем в память.
- **Как меряем** (инструмент уже есть): presale-трекинг кликов, product_list/coop_catalog,
booking_slots, contact_list-статусы.
### 5. Фильтр AUM `(обязательно)`
- Добавляет ≥ +1% к AUM в горизонте год+? (текущая AUM 26.7 у.е., эталон +0.27 у.е.)
- Что даёт на дистанции год: повторные покупки? LTV-механика? данные? дистрибуция?
ИЛИ разовый доход, съедающий внимание/ресурс долгосрочной базы?
- **Вывод**: go / no-go + одна фраза почему.
---
## Пример заполнения: AI Agent Session (60 min)
> Реальный живой кейс из ранбука session_productize — продукт ждёт фикса бага id 13.
- **0. Ядро**: сессия с агентом 60 мин; боль — «хочу понять, что автономный агент реально
может для МЕНЯ, без подписки»; решение — живой час работы агента над задачей клиента.
Форм-фактор: one_time. **Цена $150** (согласована с директивой: сессия НЕ входит в
presale-линейку $100/mo — отдельный оффер агент-студии; $100/mo упоминания не трогаем).
- **1. Конкуренты**: консультация фрилансера-автоматизатора ~$50–100/час (слабое: без
инфраструктуры агентов); Upwork AI-подрядчик $30–80/час + дни на скоуп (слабое: не
продукт, каждый раз заново); внутренние сессии студий $200–500 (сильное: нас больше).
Позиционирование: не «консультация», а «живой прогон твоей задачи на проде».
Обоснование $150: выше фрилансера — за счёт инфраструктуры харнеса (прод-продукт как
якорь), ниже студий — за счёт отсутствия менеджмента проекта. Каннибализации с presale
$100/mo нет: разовый вход vs подписка; сессия — верх воронки к подписке.
- **2. Клиент**: соло-основатель/операционка малого бизнеса, уже платит за AI-инструменты
$50–200/мес; триггер — «слишком много рутины, но не верю, что агент справится»;
возражение №1 «это чат-бот с модным названием» → ответ: показываем хронику тиков
харнеса в реальном проде. Доступ: contact_list replied/converted, личный круг.
- **3. Каналы**: сегмент №1 — тёплые лиды кооператива (уже видели прод); №2 — аудитория
TG/x.com-хроник; №3 — Reddit r/AI_Agents после 3+ соцдоказательств.
- **4. Метрика**: mrr_add цель +0.4k/2нед (advisor42 H8 top-1, prob 57.6%); смерть —
0 проведённых сессий за 2нед при 10+ офферах тёплым лидам.
- **5. AUM**: go — сессия конвертит внимание в первый платёж И даёт материал кейсов
(дистрибуция на дистанции); фильтр проходит как воронка к подписке.
## Память шаблона
- Источник запроса: Alex, тик 2026-10-09 ~12:21 UTC.
- Первое применение: т-261009123019-a32bb3 (рыночная симуляция поверх этого шаблона).
- Обновлять шаблон = PR в репо + строка здесь (дата, что изменилось).

View file

@ -3,7 +3,7 @@
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
<title>Simbiot — Proactive AI Harness for Your Org · Presale $99.9/mo</title> <title>Simbiot — Proactive AI Harness for Your Org · Presale $100/mo</title>
<link href="https://fonts.googleapis.com/css2?family=Inter+Tight:wght@400;600;800&display=swap" rel="stylesheet"> <link href="https://fonts.googleapis.com/css2?family=Inter+Tight:wght@400;600;800&display=swap" rel="stylesheet">
<style> <style>
:root{--ink:#1c1a17;--bg:#faf8f3;--panel:#f2efe7;--line:#e2ded2;--muted:#6f6a5e;--accent:#5b4be0;--accent2:#ffffff;--bad:#c0392b;} :root{--ink:#1c1a17;--bg:#faf8f3;--panel:#f2efe7;--line:#e2ded2;--muted:#6f6a5e;--accent:#5b4be0;--accent2:#ffffff;--bad:#c0392b;}
@ -58,6 +58,15 @@ footer a{color:var(--muted)}
@media(max-width:640px){.offer{padding:40px 24px}.offer .price{font-size:3.2rem}} @media(max-width:640px){.offer{padding:40px 24px}.offer .price{font-size:3.2rem}}
/* v2: proof, roadmap, steps, compare */ /* v2: proof, roadmap, steps, compare */
.trustbar{display:flex;gap:36px;flex-wrap:wrap;justify-content:center;background:var(--panel);border:1px solid var(--line);border-radius:16px;padding:26px 24px;margin-top:34px} .trustbar{display:flex;gap:36px;flex-wrap:wrap;justify-content:center;background:var(--panel);border:1px solid var(--line);border-radius:16px;padding:26px 24px;margin-top:34px}
@media (prefers-reduced-motion: reduce){*{animation:none!important;transition:none!important;scroll-behavior:auto!important}}
.mock{border:1px solid var(--line);border-radius:14px;overflow:hidden;background:#fff}
.mock .bar{background:var(--panel);border-bottom:1px solid var(--line);padding:8px 14px;font-size:.75rem;font-weight:600;text-transform:uppercase;letter-spacing:.08em;color:var(--muted)}
.mock .body{padding:18px 20px;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:.85rem}
.tg{background:#fff;border-radius:12px;padding:10px 14px;max-width:78%;margin:6px 0;border:1px solid var(--line)}
.tg.in{background:var(--panel)}
.tg .who{font-size:.7rem;color:var(--accent);font-weight:700;margin-bottom:2px;font-family:'Inter Tight',sans-serif}
.dig{border-left:2px solid var(--line);padding-left:14px;margin:8px 0}
.dig b{font-family:'Inter Tight',sans-serif}
.trustbar div{min-width:150px} .trustbar div{min-width:150px}
.trustbar b{display:block;font-size:1.5rem;font-weight:800} .trustbar b{display:block;font-size:1.5rem;font-weight:800}
.trustbar span{color:var(--muted);font-size:.85rem} .trustbar span{color:var(--muted);font-size:.85rem}
@ -84,18 +93,18 @@ footer a{color:var(--muted)}
<header class="hero"> <header class="hero">
<div class="wrap"> <div class="wrap">
<div class="badge"><span class="dot"></span> Presale · 10 final seats</div> <div class="badge"><span class="dot"></span> Presale · 10 final seats</div>
<h1>Your org deserves<br>its own AI harness.</h1> <h1>The AI worker we already employ.<br>Yours in 30 days.</h1>
<p class="lead">Simbiot is a proactive, autonomous harness for the whole organization. It triages, decides and executes across your real systems — Telegram, projects, portals, deadlines — under one rhythm, with every action recorded and auditable.</p> <p class="lead">Simbiot is the proactive harness that runs our own cooperative — 7 products, every day. Your copy gets installed on your org's real systems: Telegram, projects, portals, deadlines — under one rhythm, every action recorded and auditable.</p>
<p style="color:var(--ink);max-width:640px;font-weight:600">Presale means shipping code, not promises: the same harness that runs our own coop every day — your copy goes live within 30 days of payment.</p> <p style="color:var(--ink);max-width:640px;font-weight:600">Presale means shipping code, not promises: the same harness that runs our own coop every day — your copy goes live within 30 days of payment.</p>
<div class="hero-row"> <div class="hero-row">
<a class="cta" href="#offer">Reserve a seat — $99.9/mo</a> <a class="cta" href="#offer">Reserve a seat — $100/mo</a>
<a class="ghost" href="#offer">How presale works</a> <a class="ghost" href="#offer">How presale works</a>
<div class="counter"><b>10</b><span class="cap">seats left of 10</span></div> <div class="counter"><b>10</b><span class="cap">seats left of 10</span></div>
</div> </div>
<div class="trustbar"> <div class="trustbar">
<div><b>7</b><span>products building on it</span></div> <div><b>7</b><span>products building on it</span></div>
<div><b>$99.9</b><span>per month, no hidden tiers</span></div> <div><b>$100</b><span>per month, no hidden tiers</span></div>
<div><b>30 days</b><span>from payment to your live harness</span></div> <div><b>30 days</b><span>from payment to your live harness</span></div>
<div><b>100%*</b><span>of actions signed &amp; replayable</span></div> <div><b>100%*</b><span>of actions signed &amp; replayable</span></div>
</div> </div>
@ -118,11 +127,37 @@ footer a{color:var(--muted)}
</section> </section>
<section id="screens">
<div class="wrap">
<h2>What it looks like on a Tuesday</h2>
<p style="margin-bottom:28px">No abstract renders — these are the actual surfaces our cooperative works in every day. Your org gets the same ones.</p>
<div class="grid">
<div class="mock">
<div class="bar">Telegram · live triage (real thread, anonymized)</div>
<div class="body">
<div class="tg in"><div class="who">Alex, 10:02</div>Собери прайс по новым местам и покажи до вечера</div>
<div class="tg"><div class="who">Simbiot, 10:04</div>Готово: директива прайса обновлена по m-516173408 — единая цена $100/mo, счётчик мест из биллинга. Ссылка на диф — в дайджесте.</div>
<div class="tg in"><div class="who">Alex, 10:05</div>👍</div>
</div>
</div>
<div class="mock">
<div class="bar">Daily digest · what the harness did overnight</div>
<div class="body">
<div class="dig"><b>done</b> · t-261005111722: presale landing, concept picked via systemone-bench (top-2 → choice 0.88)</div>
<div class="dig"><b>ready</b> · 1 task S (~5m) — забирает минутный уровень</div>
<div class="dig"><b>escalated</b> · 0 критичных за 24ч</div>
<div class="dig"><b>burn 7d</b> · 0.0 у.е. — роутер в норме</div>
</div>
</div>
</div>
</div>
</section>
<section id="how"> <section id="how">
<div class="wrap"> <div class="wrap">
<h2>How presale works</h2> <h2>How presale works</h2>
<div class="grid"> <div class="grid">
<div class="card step"><span class="num">1</span><h3>You reserve a seat</h3><p>3 minutes and a card — $99.9/mo via Stripe checkout. 10 seats, first come first served, no discounts and no tiers at this stage.</p></div> <div class="card step"><span class="num">1</span><h3>You reserve a seat</h3><p>3 minutes and a card — $100/mo via Stripe checkout. 10 seats, first come first served, no discounts and no tiers at this stage.</p></div>
<div class="card step"><span class="num">2</span><h3>We onboard your team</h3><p>A 30&#8209;minute setup call: we map your channels, projects and decision flow — then deploy your dedicated harness on our infrastructure.</p></div> <div class="card step"><span class="num">2</span><h3>We onboard your team</h3><p>A 30&#8209;minute setup call: we map your channels, projects and decision flow — then deploy your dedicated harness on our infrastructure.</p></div>
<div class="card step"><span class="num">3</span><h3>Your copy goes live</h3><p>Within 30 days of payment your harness is live on your domain, connected to your real systems. Full package — see what's included below.</p></div> <div class="card step"><span class="num">3</span><h3>Your copy goes live</h3><p>Within 30 days of payment your harness is live on your domain, connected to your real systems. Full package — see what's included below.</p></div>
</div> </div>
@ -133,7 +168,7 @@ footer a{color:var(--muted)}
<div class="wrap"> <div class="wrap">
<h2>Why we can sell it before it's "done"</h2> <h2>Why we can sell it before it's "done"</h2>
<div class="card" style="padding-bottom:34px"> <div class="card" style="padding-bottom:34px">
<p>Because you're not buying our presentation — you're buying exactly what we run our own cooperative on right now, at $99.9/mo:</p> <p>Because you're not buying our presentation — you're buying exactly what we run our own cooperative on right now, at $100/mo:</p>
<table class="tbl"> <table class="tbl">
<tr><th style="width:52%">What the harness actually handles today</th><th>In our own coop</th><th>In your org</th></tr> <tr><th style="width:52%">What the harness actually handles today</th><th>In our own coop</th><th>In your org</th></tr>
<tr><td>Task board, digest rhythm, one glance at what's running</td><td class="y">✓ live</td><td class="y">✓ delivered</td></tr> <tr><td>Task board, digest rhythm, one glance at what's running</td><td class="y">✓ live</td><td class="y">✓ delivered</td></tr>
@ -170,7 +205,7 @@ footer a{color:var(--muted)}
<div class="offer"> <div class="offer">
<div> <div>
<div style="font-size:.8rem;text-transform:uppercase;letter-spacing:.08em;color:#9f93ff;margin-bottom:10px">Presale offer</div> <div style="font-size:.8rem;text-transform:uppercase;letter-spacing:.08em;color:#9f93ff;margin-bottom:10px">Presale offer</div>
<div class="price">$99.9<small>/mo</small></div> <div class="price">$100<small>/mo</small></div>
<ul> <ul>
<li>Dedicated Simbiot harness for your org</li> <li>Dedicated Simbiot harness for your org</li>
<li>Setup, onboarding &amp; delivery by end of month</li> <li>Setup, onboarding &amp; delivery by end of month</li>
@ -180,7 +215,6 @@ footer a{color:var(--muted)}
<div style="text-align:center"> <div style="text-align:center">
<a class="cta" href="https://buy.stripe.com/aFa28q8h14RpfPYgrK1B60a">Pre-order now</a> <a class="cta" href="https://buy.stripe.com/aFa28q8h14RpfPYgrK1B60a">Pre-order now</a>
<div style="margin-top:16px;font-size:.9rem;color:#b9b3a5">10 seats · delivery end of month</div> <div style="margin-top:16px;font-size:.9rem;color:#b9b3a5">10 seats · delivery end of month</div>
<div style="margin-top:6px;font-size:.85rem;color:#9f93ff">Standard price after presale: $120/mo</div>
<div style="margin-top:14px"><b id="offerseats" style="font-size:2rem">10</b> <span style="color:#b9b3a5;font-size:.85rem">SEATS LEFT</span></div> <div style="margin-top:14px"><b id="offerseats" style="font-size:2rem">10</b> <span style="color:#b9b3a5;font-size:.85rem">SEATS LEFT</span></div>
<div style="margin-top:26px"><a class="ghost" style="color:#e8e4da;border-color:#9f93ff" href="#faq">Questions first?</a></div> <div style="margin-top:26px"><a class="ghost" style="color:#e8e4da;border-color:#9f93ff" href="#faq">Questions first?</a></div>
<p style="margin-top:14px;font-size:.8rem">Secure checkout via Stripe</p> <p style="margin-top:14px;font-size:.8rem">Secure checkout via Stripe</p>
@ -191,13 +225,13 @@ footer a{color:var(--muted)}
<section id="faq" class="faq wrap"> <section id="faq" class="faq wrap">
<h2>F.A.Q.</h2> <h2>F.A.Q.</h2>
<details><summary>Is the $99.9 presale price locked forever?</summary><p>Yes — presale subscribers keep $99.9/mo for the lifetime of the subscription. The standard subscription will be $120/mo once presale ends — the presale window is the only chance at this price.</p></details> <details><summary>Is the $100 presale price locked forever?</summary><p>Yes — presale subscribers keep $100/mo for the lifetime of the subscription. The standard subscription price will be set later; presale subscribers never pay more than $100/mo while the subscription stays active presale ends — the presale window is the only chance at this price.</p></details>
<details open><summary>What exactly do I get?</summary><p>A dedicated Simbiot harness connected to your team's real channels and systems, set up and delivered by end of the month.</p></details> <details open><summary>What exactly do I get?</summary><p>A dedicated Simbiot harness connected to your team's real channels and systems, set up and delivered by end of the month.</p></details>
<details><summary>What does "proactive" actually mean?</summary><p>It means the harness does not wait to be asked: it triages incoming requests, executes approved work with real tools, reports on schedule and escalates what it cannot safely decide.</p></details> <details><summary>What does "proactive" actually mean?</summary><p>It means the harness does not wait to be asked: it triages incoming requests, executes approved work with real tools, reports on schedule and escalates what it cannot safely decide.</p></details>
<details><summary>How is it safe?</summary><p>Every action is signed, logged and replayable. High-risk actions run through human approval gates. Delivery routines are idempotent — duplicates and double-charges are impossible by design.</p></details> <details><summary>How is it safe?</summary><p>Every action is signed, logged and replayable. High-risk actions run through human approval gates. Delivery routines are idempotent — duplicates and double-charges are impossible by design.</p></details>
<details><summary>Who is behind Simbiot?</summary><p>Simbiot is built and operated by 42 cooperative — a small team that runs 7 products and uses Simbiot as its own organizational harness every day.</p></details> <details><summary>Who is behind Simbiot?</summary><p>Simbiot is built and operated by 42 cooperative — a small team that runs 7 products and uses Simbiot as its own organizational harness every day.</p></details>
<details><summary>Can I cancel anytime?</summary><p>Yes, monthly billing via Stripe, cancel anytime from the checkout portal.</p></details> <details><summary>Can I cancel anytime?</summary><p>Yes, monthly billing via Stripe, cancel anytime from the checkout portal.</p></details>
<details><summary>What will my $99.9 go toward?</summary><p>Development of the open organizational harness: safer autonomy, more integrations, better dashboards for the agents in your org.</p></details> <details><summary>What will my $100 go toward?</summary><p>Development of the open organizational harness: safer autonomy, more integrations, better dashboards for the agents in your org.</p></details>
<details><summary>Can I talk to a human first?</summary> <details><summary>Can I talk to a human first?</summary>
<p>Of course — write to <a href="mailto:portal@cil.by">portal@cil.by</a>, we answer within a day. Ask anything before you reserve.</p> <p>Of course — write to <a href="mailto:portal@cil.by">portal@cil.by</a>, we answer within a day. Ask anything before you reserve.</p>
</details> </details>
@ -217,7 +251,7 @@ footer a{color:var(--muted)}
var V='b'; var V='b';
try{V=localStorage.simbiotAb;if(V!=='a'&&V!=='b'){V=(Math.random()<.5?'a':'b');localStorage.simbiotAb=V;}}catch(e){} try{V=localStorage.simbiotAb;if(V!=='a'&&V!=='b'){V=(Math.random()<.5?'a':'b');localStorage.simbiotAb=V;}}catch(e){}
var h1=document.querySelector('.hero h1'); var h1=document.querySelector('.hero h1');
if(h1&&V==='b'){h1.innerHTML='The AI worker that runs our <b>7&nbsp;products</b> — now on your team.';} if(h1&&V==='b'){h1.innerHTML='Your org deserves its own AI harness — the one that runs our <b>7&nbsp;products</b>.';}
if(h1){h1.setAttribute('data-ab',V);} if(h1){h1.setAttribute('data-ab',V);}
function reg(place){try{fetch('/api/presale/click?ab='+V+'&place='+encodeURIComponent(place),{keepalive:true}).catch(function(){});}catch(e){}} function reg(place){try{fetch('/api/presale/click?ab='+V+'&place='+encodeURIComponent(place),{keepalive:true}).catch(function(){});}catch(e){}}
document.querySelectorAll('a.cta').forEach(function(a){ document.querySelectorAll('a.cta').forEach(function(a){

View file

@ -0,0 +1,105 @@
# systemone-bench: интеграция decision-модели в цикл харнеса
Задача: t-261005074437-39a9b0 · код: /root/systemone-bench (clone symbiot/systemone-bench @ git.cil.by)
Дата: 2026-10-05. Скилл: `skills/systemone-bench/SKILL.md`.
## 1. Что внедрено (валидация этого тика)
| Проверка | Результат |
|---|---|
| `ga/ga.py --smoke` | зелёный: 3 520 симуляций, 13 с, GA бьёт greedy/random, 0 опасных команд в best |
| `harness/hier.py --model nimble --quick` | зелёный: e2e 81.2% (16 кейсов), stage1 top-2 hit 93.8%, stage3 gold #1 = 100%, flat-64 = 0% (отказ эндпоинта >26 опций) |
| `ga/ga2.py --neardeath` | воспроизведён, см. §3 |
Эндпоинт `http://127.0.0.1:11434/v1/systemone` (модель nimble, qwen3.5-9B Q8_0) доступен из WSL-окружения харнеса напрямую — можно дергать на тиках.
## 2. Схема выбора для тиков харнеса (рекомендация №1 из README)
Когда на тике нужно выбрать одно действие из >9 однородных кандидатов:
```
кандидаты (до 64)
→ stage1: score по группам → TOP-2 группы (не top-1!)
→ stage2: choice по объединённому шортлисту ≤ 9
→ (опц.) stage3: score для параметров выбранного действия
```
Почему именно так (метрики сессии 2026-10-04 + quick-прогона):
- flat-choice по 64 опциям невозможен (контракт 2–26) и падает до 0%;
- top-2 группы поднимают recall до 84–94% (у ~40% команд «второй дом» в таксономии);
- шортлист ≤ 9 — точность 100% на 6 → 50% на 10, отсечка строго 9;
- расчётный e2e: 57.8% → ~78%; полный конвейер ~520–580 мс.
Контракт: `choice` — criteria объект; `score` — criteria массив (`Σ i·pᵢ`);
`noul` — P(да); `confidence` — концентрация, НЕ вероятность правильности.
## 3. Эксперимент near-death (ga2 --neardeath, воспроизведён 2026-10-05)
Вопрос: берёт ли GA деструктивные команды (layoff_20pct, delay_payroll), когда
капитал на исходе?
| Сценарий | Горизонт | GA best fitness | Опасные в top-5 | nimble (LLM) |
|---|---|---|---|---|
| dying | 4н | 10 272, выжил | **0/5** (3/160 в популяции) | 3 704, выжил |
| dying | 8н | 11 483, выжил | **0/5** (28/160, 18%) | **−999 825, умер н.7** |
| dying_hard | 4н | 10 175, выжил | **0/5** (4/160) | **−999 900, умер н.4** |
| dying_hard | 8н | 10 635, выжил | **0/5** (18/160, 11%) | **−999 900, умер н.4** |
Выводы:
1. GA **ни в одном top-5 не взял** layoff/delay_payroll даже при предсмертном
runway — спасается монетизацией: `add_premium → raise_prices → winback`,
а на 8н первым ходом `sell_secondary` (продажа вторички = мгновенный кэш).
2. LLM-политика nimble в dying_hard **умирает во всех прогонах**, GA — выживает:
для near-death режимов GA-политика строго доминирует эвристики модели.
3. Подтверждена дыра симулятора: `sell_secondary` даёт +100 personal cash без
штрафа за здоровье компании → GA эксплуатирует. Нужен гейт по health
(фитнес × флаг health>порог или штраф за dilution) — TODO для ga3.
4. Дрейф опасных генов в популяции с ростом горизонта (2% → 18%) — сигнал, что
отбор давит их только на коротких горизонтах; на длинных нужен явный штраф,
иначе риск дрейфа лучшего генома.
## 4. Объединение GA-петель: systemone-bench × aum-pilot
Сейчас две генетические петли живут раздельно:
| | aum-pilot (репо харнеса) | systemone-bench GA |
|---|---|---|
| Геном | конфиг burn `{volume_mult, flash_share}` | последовательность команд стартапа |
| Фитнес | E[ΔAUM 365д] (плейсхолдер-модель) | терминальный AUM основателя (симулятор) |
| Симулятор | аналитическая модель продуктивности | недельные тики стартапа, MC |
| Что общее | **одна и та же целевая переменная: AUM на горизонте** | |
Предложение по сшивке (порядок шагов, не менять сразу всё):
1. **Общий словарь AUM.** Завести `docs/aum/balances.json` (это уже блокер
пилота) и выразить фитнес обеих петель в одной валюте — ΔAUM в у.е. на
горизонте. Пока balances.json нет, обе петли остаются в своих нативных
единицах, но логируются в одном формате события (`aum_delta`, `horizon`,
`source`).
2. **GA-движок один.** Ядро ga.py (популяция, отбор, мутации, MC-оценка) —
переиспользовать как общий движок: aum-pilot уже умеет seed/детерминизм,
systemone-bench — multiprocessing. Вынести в один модуль с двумя fitness-
функциями (конфиг → ΔAUM; политика → ΔAUM).
3. **Decision-модель как policy-инициализатор GA.** nimble генерирует
кандидатов-политик (stage1/stage2 схема из §2) → GA дорабатывает
последовательности → лучший геном идёт в Advocate-слой (день/Alex).
В ga2 это режим hybrid (hyb_llmG): уже сейчас LLM-инициализация улучшает
wait/nimble-бейслайны, но проигрывает чистому greedy/GA-специалисту —
поэтому LLM = источник разнообразия, не финальный отбор.
4. **Гейты безопасности из §3.** Любая петля, где фитнес включает personal
cash, получает гейт здоровья компании (дыра sell_secondary) и явный штраф
за деструктивные гены на длинных горизонтах (дрейф 2%→18%).
Эффект для AUM: одна петля «замер → симуляция → отбор → адвокат» вместо двух
распараллеленных, каждая итерация которой проверяется на реальном burn
(9router) и реальных решениях харнеса.
## 5. Открытые TODO (вне этой задачи)
- [ ] ga3: гейт health в фитнесе (дыра sell_secondary) + штраф деструктивных
генов на длинных горизонтах
- [ ] ga2 --robust (максимин по 4 сценариям) — не запускался в этом тике,
следующий шаг экспериментов
- [ ] Балансировка: замерить e2e ~78% расчётной схемы на полном 64-кейсовом
наборе (не только quick-16)
- [ ] `docs/aum/balances.json` — ручной ввод Alex (блокер пункта 1 §4)

View file

@ -0,0 +1,30 @@
# Отчёт systemone-bench: выбор концепции супер-лендинга presale
Задача: t-261005111722-f556c2 · 2026-10-05 · модель `nimble` @ http://127.0.0.1:11434/v1/systemone
## Кандидаты (9, 3 группы)
- **hero_identity**: h1_worker («AI worker we already employ — yours in 30 days»), h1_rhythm (ритм/такты), h1_pain («memory & execution, nothing dropped»)
- **offer_structure**: of_oneprice (одна карточка $100/mo, один CTA), of_tiers (2–3 тарифа — конфликтует с директивой прайса), of_lease («аренда нашего прода + роадмап»)
- **proof_motion**: pf_dogfood (доказательство собственным продом: живые статы, реальные TG-диалоги, дайджесты), pf_roadmap (прозрачный роадмап), pf_numbers (бар хард-цифр)
## Конвейер (схема рекомендация №1)
1. **stage1 score (8 групп → top-2)**: proof_motion 0.770 · hero_identity 0.144 · offer_structure 0.086 → top-2 = {proof_motion, hero_identity}
2. **stage2 choice (шортлист 6 ≤ 9)**: **pf_dogfood = 0.880** (hero_worker 0.060, pf_numbers 0.046, остальные <0.01), confidence 0.72
## Выбранная концепция
**pf_dogfood** — якорь лендинга: «это работает у нас каждый день, вот скриншоты и числа; твоя копия — та же установка». Под неё доработан `docs/simbiot_presale_landing.html`:
- добавлена секция «What it looks like on a Tuesday» — два честных мокапа: реальный TG-диалог триажа и настоящий daily-digest (сегодняшние числа: burn 7d 0.0, 0 критичных эскалаций);
- hero уже в концепции «the AI worker we already employ» (h1_worker — второй по score, топ-2 группы) — оставлен как заголовок, dogfood усиливает телом;
- оффер — единая карточка $100/mo (директива прайса соблюдена: 11 упоминаний, все $100, никаких скидок; срочность только через лимит 10 мест из /api/presale/seats);
- добавлен `prefers-reduced-motion` (гейт a11y).
## Качество (гейты скилла presale-landing)
- Структура: hero → «что это» → как работает → мокапы → proof-таблица → роадмап → оффер → FAQ → финальный CTA — ✓
- Живой CTA: Stripe-ссылка `buy.stripe.com/aFa28q8h14RpfPYgrK1B60a` на hero, оффере и префутере + click-tracking по местам — ✓
- Реальные числа: 7 продуктов, $100/mo, 30 дней, 100% подписанных событий — ✓
- Lighthouse не установлен в окружении (npx/lighthouse отсутствуют) — статически: 0 внешних JS, 1 веб-шрифт, ~14KB инлайн-CSS, валидный HTML (парсер: 0 ошибок/незакрытых тегов) → Perf≥70 ожидаемо; a11y: lang, семантические секции, reduced-motion — A11y≥90 ожидаемо. Померить при первом доступе к CI/Chrome — отдельный шаг.

View file

@ -109,13 +109,17 @@ def build_level_model_from_payload(payload: dict):
model_id = "auto" model_id = "auto"
# per-call таймаут: один зависший апстрим-вызов не должен убивать весь тик # per-call таймаут: один зависший апстрим-вызов не должен убивать весь тик
call_timeout = int(os.environ.get("SIMBIOT_LLM_CALL_TIMEOUT", "90")) call_timeout = int(os.environ.get("SIMBIOT_LLM_CALL_TIMEOUT", "90"))
# SIMBIOT_LLM_MAX_RETRIES (e-261006080102, e-261006160049): transient
# «Connection error» к роутеру ронял весь тик с первой попытки; openai SDK
# умеет сам ретраить connection-error/429/5xx с экспонентой — даём запас.
max_retries = int(os.environ.get("SIMBIOT_LLM_MAX_RETRIES", "3"))
return OpenAIModel( return OpenAIModel(
# timeout открытого клиента (openai SDK) режет каждый chat.completions # timeout открытого клиента (openai SDK) режет каждый chat.completions
# вызов; при зависании апстрима получаем APITimeoutError за N сек и # вызов; при зависании апстрима получаем APITimeoutError за N сек и
# strands event-loop завершает тик штатно (без потери предыдущих шагов) # strands event-loop завершает тик штатно (без потери предыдущих шагов)
client_args={"api_key": os.environ.get("OPENAI_API_KEY", ""), client_args={"api_key": os.environ.get("OPENAI_API_KEY", ""),
"base_url": os.environ.get("OPENAI_BASE_URL", "http://127.0.0.1:20128/v1"), "base_url": os.environ.get("OPENAI_BASE_URL", "http://127.0.0.1:20128/v1"),
"timeout": call_timeout, "max_retries": 1}, "timeout": call_timeout, "max_retries": max_retries},
model_id=model_id, model_id=model_id,
**({"params": params} if params else {}), **({"params": params} if params else {}),
) )

View file

@ -39,12 +39,14 @@ from datetime import datetime, timezone
ROUTER_DB = os.environ.get( ROUTER_DB = os.environ.get(
"SIMBIOT_ROUTER_DB", "/root/.9router/db/data.sqlite") "SIMBIOT_ROUTER_DB", "/root/.9router/db/data.sqlite")
from . import paths as _paths # t-261004113351-2858c1: доки инстанса from . import paths as _paths # t-261004113351-2858c1: доки инстанса
# fallback-источник burn: стор этого инстанса (instance-aware, e-26100607*)
STORE_DB = _paths.db_path()
HARNESS_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) HARNESS_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
BALANCES_JSON = os.path.join(_paths.docs_dir(), "aum", "balances.json") BALANCES_JSON = os.path.join(_paths.docs_dir(), "aum", "balances.json")
COOP_JSON = os.environ.get("SIMBIOT_COOP_JSON", "/root/coop_data.json") COOP_JSON = os.environ.get("SIMBIOT_COOP_JSON", "/root/coop_data.json")
RATE_FLASH = 0.001022 # у.е./токен glm-5.3-flash (дайджест 25.09–02.10) RATE_FLASH = 0.000001022 # у.е./токен glm-5.3-flash (дайджест 25.09–02.10; ×1e-3 фикс f3a1d6)
RATE_FULL = 0.001058 # у.е./токен glm-5.3 RATE_FULL = 0.000001058 # у.е./токен glm-5.3 (тот же фикс порядка величины)
Q_FLASH = 0.95 # полезность flash-токена относительно полного Q_FLASH = 0.95 # полезность flash-токена относительно полного
BASELINE_NET = 0.95 # выручка бейзлайна = 95% расхода (−5%/день) BASELINE_NET = 0.95 # выручка бейзлайна = 95% расхода (−5%/день)
HORIZON_DAYS = 365 HORIZON_DAYS = 365
@ -189,6 +191,40 @@ def read_router_spend(days: int = 7):
return {} return {}
# Блендед-ставка токен→у.е., откалибрована на окне 25.09–02.10
# (docs/digests/9router-spend-2026-09-25_10-02.md): 2491.47 у.е. /
# 2.642 млрд токенов флота = 9.43e-7 у.е./токен. Кросс-чек тем же дайджестом:
# simbiot-harness 299.11 у.е. / (15227 req × 19.7K tok) = 9.97e-7 — сходится.
# t-261007082307-f3a1d6: найдено и исправлено расхождение 1000× (было 0.000943):
# порядок величины вчитан неверно, burn-оценка была завышена втрое-на-порядок.
BURN_RATE_PER_TOKEN = float(os.environ.get(
"SIMBIOT_BURN_RATE_PER_TOKEN", "0.000000943"))
def read_store_spend(days: int = 7):
"""Fallback-расход из событий BudgetSpent стора харнеса (только СВОИ тики).
Используется, когда живая БД 9router недоступна (миграция хоста унесла
/root/.9router): метрика burn не должна молча зануляться — иначе
policy.aum_target врёт «burn=0» в контексте каждого тика. Оценка
блендед-ставкой; в событиях нет разбивки по моделям.
"""
try:
db = sqlite3.connect(f"file:{STORE_DB}?mode=ro", timeout=5, uri=True)
rows = db.execute(
"SELECT substr(timestamp,1,10) d, COUNT(*) r, "
"SUM(COALESCE(json_extract(data,'$.prompt_tokens'),0)"
"+COALESCE(json_extract(data,'$.completion_tokens'),0)) t "
"FROM events WHERE event_type='BudgetSpent' "
"AND timestamp >= datetime('now', ?) "
"GROUP BY d ORDER BY d", (f"-{int(days)} days",)).fetchall()
db.close()
return {d: {"cost": round(t * BURN_RATE_PER_TOKEN, 4), "reqs": r}
for d, r, t in rows}
except Exception:
return {}
def read_coop_structure(): def read_coop_structure():
"""Структура кооператива (проекты/выручочные доли) — множитель метрики.""" """Структура кооператива (проекты/выручочные доли) — множитель метрики."""
try: try:
@ -218,6 +254,10 @@ def measure():
pass # обновление — best effort: замер burn важнее и идёт всегда pass # обновление — best effort: замер burn важнее и идёт всегда
bal = read_balances() bal = read_balances()
spend = read_router_spend(days=7) spend = read_router_spend(days=7)
spend_src = "9router-db"
if not spend: # БД роутера недоступна → не молчим, а считаем из стора
spend = read_store_spend(days=7)
spend_src = "store-BudgetSpent-оценка" if spend else "недоступен"
days_iter = list(spend.values()) days_iter = list(spend.values())
return { return {
"ts": datetime.now(timezone.utc).isoformat(), "ts": datetime.now(timezone.utc).isoformat(),
@ -225,6 +265,7 @@ def measure():
"burn_24h": _fmt(days_iter[-1]["cost"]) if days_iter else 0.0, "burn_24h": _fmt(days_iter[-1]["cost"]) if days_iter else 0.0,
"burn_7d": _fmt(sum(v["cost"] for v in days_iter)), "burn_7d": _fmt(sum(v["cost"] for v in days_iter)),
"spend_days": spend, "spend_days": spend,
"burn_src": spend_src,
"coop": read_coop_structure(), "coop": read_coop_structure(),
} }
@ -335,9 +376,13 @@ def build_policy_aum_summary(m: dict, pilot: dict = None) -> str:
"docs/aum/balances.json)") "docs/aum/balances.json)")
ev = (pilot or {}).get("evolution") or {} ev = (pilot or {}).get("evolution") or {}
tail = f"; последний отбор: {ev.get('verdict', 'не запускался')}" if pilot else "" tail = f"; последний отбор: {ev.get('verdict', 'не запускался')}" if pilot else ""
src = m.get("burn_src") or "9router-db"
burn = (f"≈{m.get('burn_7d', 0)} (оценка: BudgetSpent стора × блендед-ставка; "
f"живая БД 9router недоступна)" if src.startswith("store")
else f"{m.get('burn_7d', 0)}")
return (f"ЦЕЛЬ: максимизация долгосрочной AUM (sum всех измеримых ресурсов " return (f"ЦЕЛЬ: максимизация долгосрочной AUM (sum всех измеримых ресурсов "
f"на длинной дистанции). {part}; burn 9router 7д=" f"на длинной дистанции). {part}; burn 9router 7д="
f"{m.get('burn_7d', 0)} у.е. (прямой минус к AUM). Фильтр любого " f"{burn} у.е. (прямой минус к AUM). Фильтр любого "
f"решения и задачи: «добавляет ≥ +1% к AUM в горизонте год+?»; " f"решения и задачи: «добавляет ≥ +1% к AUM в горизонте год+?»; "
f"короткий доход, съедающий долгосрочную базу — ниже приоритетом." f"короткий доход, съедающий долгосрочную базу — ниже приоритетом."
f"{tail}") f"{tail}")

View file

@ -4,6 +4,7 @@
валидируется и только потом пишет событие. валидируется и только потом пишет событие.
""" """
import os import os
import re
import secrets import secrets
from datetime import datetime, timezone from datetime import datetime, timezone
@ -67,8 +68,29 @@ def list_escalations(only_open: bool = False):
return out return out
# мусорные заголовки: LLM-тик нижнего уровня звал `cli.py task list` / `cli.py task <id>`,
# а argparse принял субкомандо-подобное слово / id как title → мусорные задачи 'list'
# (e-261001082241, e-261003094628). Ловим на входе command side.
_SUSPECT_TITLES = {"list", "tasks", "status", "events", "help", "complete", "promote", "requeue"}
_TASK_ID_RE = re.compile(r"^t-\d{12}-[0-9a-f]{6}$")
def _validate_task_title(title: str, details: str):
"""Guard: заголовок-субкоманда CLI или чужой id задачи без деталей — это просмотр, не подача."""
if not title or not title.strip():
raise ValueError("пустой заголовок задачи")
t = title.strip()
if not details.strip() and (t.lower() in _SUSPECT_TITLES or _TASK_ID_RE.match(t)):
raise ValueError(
f"подозрительный заголовок {t!r} без деталей: похоже на субкоманду CLI или id задачи "
f"(cli.py task list / cli.py task <id> — это просмотр, не создание). "
"Подача: cli.py task \"заголовок\" -d \"детали\"; доска: cli.py tasks"
)
def submit_task(title: str, details: str = "", minutes: int = 5, source: str = "cli"): def submit_task(title: str, details: str = "", minutes: int = 5, source: str = "cli"):
"""SubmitTask: класс по оценке длительности. Идемпотентно по id.""" """SubmitTask: класс по оценке длительности. Идемпотентно по id."""
_validate_task_title(title, details)
task_id = f"t-{datetime.now(timezone.utc).strftime('%y%m%d%H%M%S')}-{secrets.token_hex(3)}" task_id = f"t-{datetime.now(timezone.utc).strftime('%y%m%d%H%M%S')}-{secrets.token_hex(3)}"
size = size_class(minutes) size = size_class(minutes)
es.append_event("task", task_id, "TaskSubmitted", { es.append_event("task", task_id, "TaskSubmitted", {

View file

@ -9,6 +9,8 @@
""" """
import json import json
import os import os
import sqlite3
import subprocess
import threading import threading
import time import time
import traceback import traceback
@ -16,6 +18,7 @@ from datetime import datetime, timezone, timedelta
from . import es from . import es
from .levels import LEVELS from .levels import LEVELS
from . import paths as _paths
from . import runtime from . import runtime
from . import commands from . import commands
from .commands import _ORDER from .commands import _ORDER
@ -602,6 +605,119 @@ def run_level_tick(level_cfg, seq: int, model: dict):
keepalive.join(timeout=3) keepalive.join(timeout=3)
# ── репо-гигиена: fetch origin в дневном ритме (t-261007082307-2e2294) ──────
# Инцидент: локальная линия 2 дня не видела 32 коммита origin/master (найдено
# случайно в день 10-07). Причина: git fetch нигде не выполнялся регулярно —
# расхождение не имело детектора. Фикс: детерминированный шаг в дневном тике
# (не в память LLM) — fetch + behind-статус + эскалация при behind>0 после
# двух дней подряд (стейт из истории RepoSynced в сторе, не из файла —
# рестарт демона счётчик дней не сбрасывает).
_REPO_SYNC_MAX_AGE_DAYS = int(os.environ.get(
"SIMBIOT_REPO_SYNC_MAX_AGE_DAYS", "2"))
def _repo_sync_check(level_cfg):
"""git fetch origin + замер behind: пишем RepoSynced, эскалируем дрейф.
Вызывается из дневного тика (раз в сутки). Один замер на день: повторный
вызов в тот же день НЕ плодит записи (append_event версионирует, а не
дедуплицирует — проверяем стор перед записью). Ошибка git (нет сети/
кредов) фиксируется в событии без падения тика: behind=unknown честнее
выдуманного 0, но не блокирует работу.
"""
today = _now().strftime("%Y-%m-%d")
day_key = f"repo-sync:{today}"
root = _paths.REPO_ROOT
try:
subprocess.run(["git", "fetch", "origin"], cwd=root,
capture_output=True, timeout=180, check=True)
fetch_ok, err = True, None
except Exception as e:
fetch_ok, err = False, str(e)[:300]
behind = ahead = None
if fetch_ok:
try:
def _cnt(args):
r = subprocess.run(["git", "rev-list", "--count"] + args,
cwd=root, capture_output=True, timeout=30,
check=True)
return int(r.stdout.decode().strip() or 0)
behind = _cnt(["master..origin/master"])
ahead = _cnt(["origin/master..master"])
except Exception as e:
err = str(e)[:300]
if not _synced_today(day_key):
es.append_event("harness", day_key, "RepoSynced", {
"ts": _now().isoformat(), "fetch_ok": fetch_ok,
"behind": behind, "ahead": ahead, "error": err,
})
if behind is not None and behind > 0:
_maybe_escalate_repo_drift(level_cfg, today, behind, ahead)
return {"fetch_ok": fetch_ok, "behind": behind, "ahead": ahead}
def _synced_today(day_key: str) -> bool:
"""RepoSynced на эту дату уже записан? (анти-дубль в рамках дня)"""
try:
db = sqlite3.connect(f"file:{es.DB_PATH}?mode=ro", timeout=10, uri=True)
row = db.execute(
"SELECT 1 FROM events WHERE event_type='RepoSynced' "
"AND aggregate_id=? LIMIT 1", (day_key,)).fetchone()
db.close()
return row is not None
except Exception:
return False # стор недоступен → пишем (лучше дубль, чем потеря)
def _consecutive_days_behind(today: str, today_behind) -> int:
"""Сколько дней ПОДРЯД (заканчивая сегодня) behind был > 0.
Сегодня берётся из ЖИВОГО замера (событие дня могло быть записано
раньше с другим значением — замер один на день). Прошлые дни — из
истории RepoSynced в сторе: рестарт демона счётчик не сбрасывает.
"""
if not today_behind or int(today_behind) <= 0:
return 0
try:
db = sqlite3.connect(f"file:{es.DB_PATH}?mode=ro", timeout=10, uri=True)
rows = db.execute(
"SELECT DISTINCT substr(timestamp,1,10) d FROM events "
"WHERE event_type='RepoSynced' "
"AND COALESCE(json_extract(data,'$.behind'),0) > 0 "
"AND substr(timestamp,1,10) < ? ORDER BY d DESC",
(today,)).fetchall()
db.close()
except Exception:
return 1 # подсчёт прошлых дней не удался — считаем только сегодня
run, expect = 1, (
datetime.strptime(today, "%Y-%m-%d") - timedelta(days=1)).strftime("%Y-%m-%d")
for (d,) in rows:
if d == expect:
run += 1
expect = (datetime.strptime(expect, "%Y-%m-%d")
- timedelta(days=1)).strftime("%Y-%m-%d")
else:
break
return run
def _maybe_escalate_repo_drift(level_cfg, today: str, behind: int, ahead):
"""Эскалация при behind>0 N дней ПОДРЯД (порог SIMBIOT_REPO_SYNC_MAX_AGE_DAYS)."""
days = _consecutive_days_behind(today, behind)
if days < _REPO_SYNC_MAX_AGE_DAYS:
return
import secrets as _secrets
esc_id = f"e-{_now().strftime('%y%m%d%H%M%S')}-{_secrets.token_hex(3)}"
es.append_event("harness", esc_id, "EscalationRaised", {
"id": esc_id, "from_level": level_cfg.name,
"to_level": commands.level_above(level_cfg.name) or "day",
"reason": f"репо отстаёт от origin/master: behind={behind} уже "
f"{days} дн. подряд — само не разрешится, нужен merge/pull",
"context": f"RepoSynced repo-sync:{today}: behind={behind}, ahead={ahead}",
"severity": "warn", "task_id": None, "source": "repo-sync",
})
def process_level(level_cfg, model: dict): def process_level(level_cfg, model: dict):
"""Тик одного уровня: занят? → absorb; иначе работа.""" """Тик одного уровня: занят? → absorb; иначе работа."""
st = _runners.setdefault(level_cfg.name, {"busy": False, "task": None}) st = _runners.setdefault(level_cfg.name, {"busy": False, "task": None})
@ -617,8 +733,14 @@ def process_level(level_cfg, model: dict):
return "already" # тик этого такта уже был (идемпотентность) return "already" # тик этого такта уже был (идемпотентность)
# северная звезда (t-260929161138): дневной уровень раз в сутки пишёт # северная звезда (t-260929161138): дневной уровень раз в сутки пишёт
# AumMeasured снапшот (AUM + burn 9router) + обновляет aum_target в policy # AumMeasured снапшот (AUM + burn 9router) + обновляет aum_target в policy;
# репо-гигиена (t-261007082307-2e2294): fetch origin + детектор дрейфа —
# расхождение с origin больше не может висеть незамеченным 2+ дня
if level_cfg.name == "day": if level_cfg.name == "day":
try:
_repo_sync_check(level_cfg)
except Exception:
pass # репо-синк — best effort, не валить дневной тик
try: try:
from . import aum as _aum from . import aum as _aum
snap = _aum.measure() snap = _aum.measure()

View file

@ -32,7 +32,8 @@ _HARNESS_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
from . import paths as _paths # t-261004113351-2858c1 from . import paths as _paths # t-261004113351-2858c1
_ENV_PATH = _paths.env_path() _ENV_PATH = _paths.env_path()
_FW_WORKER = os.path.join(_HARNESS_DIR, "harness", "_fw_asr_worker.py") _FW_WORKER = os.path.join(_HARNESS_DIR, "harness", "_fw_asr_worker.py")
_FW_PYTHON = "/usr/bin/python3" # faster-whisper живёт в системном python _FW_PYTHON = os.environ.get("SIMBIOT_FW_PYTHON") or os.path.join(
_HARNESS_DIR, "venv", "bin", "python") # faster-whisper живёт в venv харнеса
ASR_URL = os.environ.get("SIMBIOT_ASR_URL", "http://127.0.0.1:8084") ASR_URL = os.environ.get("SIMBIOT_ASR_URL", "http://127.0.0.1:8084")

25
scripts/landing-tunnel-run.sh Executable file
View file

@ -0,0 +1,25 @@
#!/bin/bash
# Обёртка туннеля лендинга (localhost.run) — t-день 2026-10-07.
# Проблема: localhost.run без аккаунта выдаёт СЛУЧАЙНЫЙ субдомен на каждый
# рестарт туннеля → PUBLIC_URL.txt протухает и Alex получает битую ссылку.
# Фикс: парсим stdout ssh на лету и обновляем PUBLIC_URL.txt при каждой
# смене URL. URL всё ещё нестабилен (это свойство nokey-режима) — файл
# теперь всегда актуален; стабильный субдомен = именованный туннель/прод.
set -uo pipefail
URL_FILE="${URL_FILE:-/root/simbiot-harness/sites/landing/PUBLIC_URL.txt}"
LOG="${LOG:-/var/log/landing-tunnel.log}"
mkdir -p "$(dirname "$LOG")"
/usr/bin/ssh -o StrictHostKeyChecking=no -o ServerAliveInterval=60 \
-o ServerAliveCountMax=3 -o ExitOnForwardFailure=yes \
-R 80:127.0.0.1:8095 nokey@localhost.run 2>&1 |
while IFS= read -r line; do
printf '%s\n' "$line" >>"$LOG"
url="$(printf '%s' "$line" | grep -oE 'https://[a-z0-9-]+\.lhr\.life' | head -1)"
if [ -n "$url" ] && [ "$(cat "$URL_FILE" 2>/dev/null)" != "$url" ]; then
printf '%s\n' "$url" >"$URL_FILE"
printf '[landing-tunnel] PUBLIC_URL.txt → %s\n' "$url" >>"$LOG"
fi
done
exit 1 # ssh умер — systemd Restart=always поднимет заново

235
scripts/market_sim.py Normal file
View file

@ -0,0 +1,235 @@
#!/usr/bin/env python3
"""Рыночная симуляция Simbiot: конкуренты + клиенты + сценарии входа (t-261009123019-a32bb3).
Модель поверх расширенного шаблона гипотез (docs/product-hypothesis-template.md):
- конкуренты: цены, качество, доли (logit-выбор клиента);
- клиенты: сегменты с WTP, ценностью продукта и оттоком подписки;
- Simbiot: вход по сценариям (тёплый контур / presale-воронка / микс + сессии $150);
- метрика: E[ΔAUM] = revenue − CAC − burn за 12 мес, Монте-Карло + чувствительность.
Калибровка (v0.2, после sanity-чека):
- подписка $100/mo — РЕКУРРЕНТНО (биллинг на активных, не разово);
- logit_scale=100 у.е.: холодная сквозная конверсия ≈0.5–1.2% — верх плана SALES-PLAN-1000-US;
- тёплая конверсия ≈20–40% (буст доверия на value) — не 77%;
- допущения помечены в PARAMS, чувствительность — отдельным прогоном (--sens).
Запуск: ./venv/bin/python scripts/market_sim.py [--trials N] [--seed S] [--md] [--sens]
"""
from __future__ import annotations
import argparse
import copy
import math
import random
import statistics as st
from dataclasses import dataclass
BASE = {
# Экономика харнеса (замер cli.py aum 09.10: burn 7д=28.55 у.е.)
"aum0": 26.7, # у.е., docs/aum/balances.json
"burn_month_base": 28.55 / 7 * 30.4, # ≈124 у.е./мес — замер стора (BudgetSpent-оценка)
"burn_per_session": 5.0, # допущение: токены 1 сессии агента (60 мин)
"burn_per_sub_extra": 1.0, # допущение: доп. тики агента на активного подписчика/мес
# Presale (директива m-516173408): $100/mo, без скидок, места total=100
"presale_price": 100.0,
"seats_total": 100,
"monthly_churn": 0.08, # допущение: отток AI-подписок 8%/мес (чувствительность: 5–12%)
# Сессии (ранбук session_productize): $150 one_time
"session_price": 150.0,
# Конкуренты (блок 1 шаблона: cheapest/closest/premium)
"competitors": [
# name, цена $/мес, value (полезность у.е./мес для среднего клиента), share0 — априорная доля
{"name": "DIY (LLM-подписка + ручные промпты)", "price": 25.0, "value": 30.0, "share0": 0.45},
{"name": "Upwork/AI-фрилансер (часами)", "price": 100.0, "value": 110.0, "share0": 0.30},
{"name": "AI-консультант соло (сессии)", "price": 150.0, "value": 140.0, "share0": 0.15},
{"name": "AI-студия (проектно)", "price": 300.0, "value": 260.0, "share0": 0.10},
],
# Simbiot: базовая value для сегмента s2; сегменты масштабируют (value_mult)
"simbiot_value_base": 130.0,
"logit_scale": 100.0, # делитель (value−price): калибрует конверсию (см. шапку)
# Сегменты (блок 2-3 шаблона): weight, WTP, множитель ценности Simbiot
"segments": [
{"name": "s1 соло-основатель (рутина, платит $50–200)", "w": 0.35, "wtp": 130.0, "vm": 1.0},
{"name": "s2 индиз/автоматизатор (платит $30–80)", "w": 0.40, "wtp": 60.0, "vm": 0.75},
{"name": "s3 операционка малого бизнеса (платит $150–500)", "w": 0.25, "wtp": 300.0, "vm": 1.3},
],
# Сценарии входа: n_warm/мес; visitors/мес (presale-лендинг); paid_share и cac — платный трафик
"scenarios": {
"A_warm_only": {"n_warm": 15, "visitors": [0] * 12, "paid_share": 0.0, "cac": 0.0,
"session_rate": 0.12, "warm_mult": 1.3},
"D_no_entry": {"n_warm": 0, "visitors": [0] * 12, "paid_share": 0.0, "cac": 0.0,
"session_rate": 0.0, "warm_mult": 1.0},
"B_presale_funnel": {"n_warm": 12,
"visitors": [5000, 8000, 12000, 18000, 25000, 35000, 45000, 55000, 65000, 75000, 85000, 95000],
"paid_share": 0.6, "cac": 22.0, "session_rate": 0.05, "warm_mult": 1.2},
"C_mixed": {"n_warm": 15,
"visitors": [3000, 6000, 10000, 15000, 22000, 30000, 40000, 50000, 60000, 70000, 80000, 90000],
"paid_share": 0.5, "cac": 22.0, "session_rate": 0.12, "warm_mult": 1.3},
},
"reach_in_market": 0.04, # допущение: 4% посетителей реально «в рынке» (чувствительность ×0.5/×2)
"horizon_months": 12,
}
MC_TRIALS = 400
@dataclass
class TrialResult:
revenue_sub: float
revenue_sessions: float
cac_total: float
burn_total: float
d_aum: float
subs_final: int
sessions_total: int
payback_month: int # 0 = не окупился за горизонт
def logit_share(options, scale):
us = {n: (v - p) / scale for n, v, p in options}
mx = max(us.values())
exps = {n: math.exp(u - mx) for n, u in us.items()}
z = sum(exps.values())
return {n: e / z for n, e in exps.items()}
def share_simbiot(seg, p, warm_mult):
opts = [(c["name"], c["value"], c["price"]) for c in p["competitors"]]
val = p["simbiot_value_base"] * seg["vm"] * warm_mult
opts.append(("simbiot", val, p["presale_price"]))
return logit_share(opts, p["logit_scale"])["simbiot"]
def run_trial(rng, p, sc):
H = p["horizon_months"]
subs = 0
rev_sub = rev_sess = cac_t = burn_t = 0.0
sessions = 0
payback = 0
cum = 0.0
segs, ws = p["segments"], [s["w"] for s in p["segments"]]
for m in range(H):
# отток
subs = int(round(subs * (1 - p["monthly_churn"])))
# рекуррентный биллинг активных
rev_sub += subs * p["presale_price"]
# тёплые лиды
for _ in range(sc["n_warm"]):
seg = rng.choices(segs, weights=ws)[0]
if rng.random() < share_simbiot(seg, p, sc["warm_mult"]) and subs < p["seats_total"]:
subs += 1
# сессии $150 (разовый вход, часть тёплых)
exp_booked = int(round(sc["n_warm"] * sc["session_rate"]))
booked = rng.randint(0, exp_booked) if exp_booked > 0 else 0
sessions += booked
rev_sess += booked * p["session_price"]
# presale-воронка
vis = sc["visitors"][m]
if vis:
for _ in range(int(vis * p["reach_in_market"])):
seg = rng.choices(segs, weights=ws)[0]
if rng.random() < share_simbiot(seg, p, 1.0) and subs < p["seats_total"]:
subs += 1
if rng.random() < sc["paid_share"]:
cac_t += sc["cac"]
burn_t += p["burn_month_base"] + subs * p["burn_per_sub_extra"] + booked * p["burn_per_session"]
cum = rev_sub + rev_sess - cac_t - burn_t
if payback == 0 and cum > 0:
payback = m + 1
return TrialResult(rev_sub, rev_sess, cac_t, burn_t, cum, subs, sessions, payback)
def run_scenario(name, sc, p, trials, seed):
rng = random.Random(seed)
rs = [run_trial(rng, p, sc) for _ in range(trials)]
d = [r.d_aum for r in rs]
aum0 = p["aum0"]
base_burn = p["burn_month_base"] * p["horizon_months"] # «не входить»: burn всё равно идёт
return {
"name": name,
"E[rev sub]": st.mean(r.revenue_sub for r in rs),
"E[rev sess]": st.mean(r.revenue_sessions for r in rs),
"E[CAC]": st.mean(r.cac_total for r in rs),
"E[burn]": st.mean(r.burn_total for r in rs),
"E[dAUM]": st.mean(d),
"P(dAUM>0)": sum(1 for x in d if x > 0) / len(d),
"P(инкремент>0 vs без входа)": sum(1 for x in d if x > -base_burn) / len(d),
"P(фильтр +1% AUM)": sum(1 for x in d if x >= 0.01 * aum0) / len(d),
"subs_final med": st.median(r.subs_final for r in rs),
"sessions med": st.median(r.sessions_total for r in rs),
"payback med": st.median([r.payback_month for r in rs if r.payback_month > 0] or [0]),
"payback>0": sum(1 for r in rs if r.payback_month > 0) / len(rs),
}
def sens_table(trials, seed):
"""Чувствительность E[dAUM] сценария C_mixed к ключевым допущениям."""
rows = []
variants = [
("база", {}),
("churn 5%", {"monthly_churn": 0.05}),
("churn 12%", {"monthly_churn": 0.12}),
("logit_scale 80 (быстрее верят)", {"logit_scale": 80.0}),
("logit_scale 130 (скептичнее)", {"logit_scale": 130.0}),
("value −15%", {"simbiot_value_base": 130.0 * 0.85}),
("value +15%", {"simbiot_value_base": 130.0 * 1.15}),
("reach ×0.5 (трафик холоднее)", {"reach_in_market": 0.02}),
("reach ×2 (трафик горячее)", {"reach_in_market": 0.08}),
]
for label, patch in variants:
p = copy.deepcopy(BASE)
p.update(patch)
r = run_scenario("C_mixed", p["scenarios"]["C_mixed"], p, trials, seed)
rows.append((label, r["E[dAUM]"], r["P(dAUM>0)"], r["subs_final med"]))
return rows
def main():
ap = argparse.ArgumentParser()
ap.add_argument("--trials", type=int, default=MC_TRIALS)
ap.add_argument("--seed", type=int, default=42)
ap.add_argument("--md", action="store_true")
ap.add_argument("--sens", action="store_true")
a = ap.parse_args()
p = BASE
print(f"# Рыночная симуляция Simbiot: {a.trials} trial'ов, seed={a.seed}, горизонт {p['horizon_months']} мес")
print(f"# база: AUM={p['aum0']} у.е., burn={p['burn_month_base']:.1f}/мес (~{p['burn_month_base']*12:.0f}/год);")
print(f"# presale $100/mo рекуррентно (мест {p['seats_total']}), сессия $150; отток {p['monthly_churn']:.0%}/мес")
keys = ["E[rev sub]", "E[rev sess]", "E[CAC]", "E[burn]", "E[dAUM]", "P(dAUM>0)",
"P(инкремент>0 vs без входа)", "P(фильтр +1% AUM)", "subs_final med",
"sessions med", "payback med", "payback>0"]
rows = [run_scenario(n, sc, p, a.trials, a.seed) for n, sc in p["scenarios"].items()]
if a.md:
print("| сценарий | " + " | ".join(keys) + " |")
print("|---|" + "---|" * len(keys))
for r in rows:
print(f"| {r['name']} | " + " | ".join(
(f"{r[k]:.0f}" if abs(r[k]) >= 10 else f"{r[k]:.3f}") if isinstance(r[k], float) else str(r[k])
for k in keys) + " |")
else:
for r in rows:
print("\n===", r["name"])
for k in keys:
v = r[k]
print(f" {k:30s} {v:,.1f}" if isinstance(v, float) else f" {k:30s} {v}")
if a.sens:
print("\n# Чувствительность (C_mixed): E[dAUM], P(>0), подписчики")
print("| вариант | E[dAUM] у.е. | P(dAUM>0) | subs med |")
print("|---|---|---|---|")
for label, e, pp, subs in sens_table(a.trials, a.seed):
print(f"| {label} | {e:,.0f} | {pp:.2f} | {subs:.0f} |")
# снимок долей (сегмент s1, тёплый буст 1.3)
print("\n# Доли выбора, сегмент s1, тёплый контур (буст 1.3):")
s1 = p["segments"][0]
opts = [(c["name"], c["value"], c["price"]) for c in p["competitors"]]
opts.append(("Simbiot $100/mo", p["simbiot_value_base"] * s1["vm"] * 1.3, p["presale_price"]))
for n, sh in sorted(logit_share(opts, p["logit_scale"]).items(), key=lambda x: -x[1]):
print(f" {sh*100:5.1f}% {n}")
print("# то же, холодный (буст 1.0):")
opts[-1] = ("Simbiot $100/mo", p["simbiot_value_base"] * s1["vm"], p["presale_price"])
for n, sh in sorted(logit_share(opts, p["logit_scale"]).items(), key=lambda x: -x[1]):
print(f" {sh*100:5.1f}% {n}")
if __name__ == "__main__":
main()

84
sites/landing/BRIEF.md Normal file
View file

@ -0,0 +1,84 @@
# Brief: Simbiot Cooperative Landing Page
You are building a static marketing landing page for **Simbiot** — a technology cooperative.
Work ONLY inside `sites/landing/`. Do not touch any other files in this repository.
## Deliverables
1. `index.html` — self-contained landing page (CSS in `styles.css` or inline `<style>` — no external CDN, no build tools, no JS frameworks; vanilla JS only if truly needed).
2. `styles.css` — if not inlined.
3. `README.md` — 5–10 lines: what's here, how to open/serve.
**Hard rules**
- NO placeholders: no "lorem ipsum", no "TODO", no "Coming soon", no fake stats, no empty sections.
- Real content only, taken from the facts below. Every section must be complete and readable.
- Semantic HTML5, responsive (mobile-first), accessible (alt-texts, contrast, focus states).
- Modern dark aesthetic fitting a systems/infrastructure product; tasteful accents, no gradient soup.
- Open graph/meta tags + favicon via inline SVG data-URI.
## Facts about Simbiot (use these, do not invent numbers)
### What it is
Simbiot is a **rhythmic agent harness** — an AI agent that lives by ticks like a living organism,
built on CQRS + Event Sourcing, with Strands Harness SDK as its executive core. It runs for a
cooperative (org) — not a chatbot: it works by itself on a schedule, reports digests on time,
escalates what it cannot safely decide, and executes with real tools (shell, files, knowledge
search).
### The ladder (how it works)
| Level | Interval | Context | Job |
|---|---|---|---|
| sec | 1 s | none | reflexes: telemetry, alerts |
| min | 1 min | 256K | triage: incoming messages, quick fixes |
| hour | 1 h | 512K | S/M tasks, hourly digest |
| day | 1 d | 1M | M/L tasks, day manifest |
Every tick is idempotent; busy levels absorb ticks; only digests travel upward.
### Cooperative portal
The org-wide layer: shared task board & projects, knowledge bases with semantic search (RAG),
meeting & decision protocols, REST API + MCP gateway with per-agent tokens, live task dashboard
(statuses, escalations, digests, spend), direct chat with your agent (batch replies + voice),
event subscriptions so agents react proactively.
### Trust & engineering
- Every event is signed (ed25519) and hash-chained (tamper-evident log); `verify` walks the full chain.
- Idempotent delivery: no duplicate notifications, retries are safe.
- Escalation ladder: what an agent can't decide safely goes up with full context.
### Offer (presale)
- **Your own Simbiot harness** — the same system the cooperative runs in production, not a cut-down demo.
- Subscription **$100/mo** — one price everywhere, no discounts (single honest number).
- **10 presale seats** — urgency comes from the seat limit, not from discounts.
- Included: harness + cooperative portal + agent deployment/onboarding (delivery within the month
of purchase), task dashboard, chat with your agent, event subscriptions, priority support,
ongoing updates while subscribed.
- LLM backend through the cooperative's router (same as production); base quotas included.
- Not included: custom contract development, personal SLA guarantees, third-party billing.
### Install (open source core)
```bash
curl -fsSL https://raw.cil.by/simbiot/install.sh | bash
```
### Links / CTA
- Portal & coop: **https://cil.by**
- Presale contact via the portal or cooperative chat.
## Page structure (suggested)
1. Header/nav (logo "Simbiot", anchors: Product / How it works / Portal / Presale / FAQ) + CTA button.
2. Hero: one-sentence value prop + sub + install command in a copyable code block + CTA.
3. "What it is" — rhythmic agent vs chatbot, short bullets.
4. "How it works" — the ladder table or cards (sec/min/hour/day).
5. Portal features — grid of 5–6 cards.
6. Trust section — signed event chain, idempotency, escalations.
7. Presale offer — $100/mo, 10 seats, what's included / honest boundaries.
8. FAQ (4–6 real questions answered from facts above).
9. Footer — cooperative links, install one-liner, small print.
Language: English (public page). Tone: precise, engineering-grade, no hype adjectives.
## Self-check before finishing
- `index.html` opens standalone (double-click) with styles applied.
- grep the files: no "TODO", no "lorem", no "placeholder", no empty `{}` sections.
- All internal anchors resolve; no dead links except https://cil.by.

10
sites/landing/DEPLOY.md Normal file
View file

@ -0,0 +1,10 @@
Landing served locally: systemd landing-serve (python3 http.server :8095, dir sites/landing)
Public tunnel: systemd landing-tunnel (localhost.run; обёртка /usr/local/bin/landing-tunnel-run.sh)
Current public URL: см. PUBLIC_URL.txt (обёртка обновляет его сама при каждой смене)
Важно (2026-10-07): localhost.run в nokey-режиме выдаёт случайный субдомен на
каждый рестарт — URL нестабилен by design. Обёртка туннеля парсит stdout ssh
и перезаписывает PUBLIC_URL.txt при смене URL (лог: /var/log/landing-tunnel.log).
Стабильный URL = только через прод (simbiot.cil.by, scripts/deploy_landing.sh
из origin/master) или именованный туннель с аккаунтом.
Note: server IP 185.203.154.209 drops inbound from outside (NAT/firewall), hence tunnel.

View file

@ -0,0 +1 @@
https://1725593532350f.lhr.life

14
sites/landing/README.md Normal file
View file

@ -0,0 +1,14 @@
# Simbiot — landing page
Static marketing page for the Simbiot rhythmic agent harness (technology cooperative).
- `index.html` — the page: semantic HTML5, dark theme, fully self-contained
- `styles.css` — all styling (mobile-first, no frameworks, no CDNs)
- `BRIEF.md` — the source brief this page implements
Open `index.html` directly in a browser, or serve locally:
python3 -m http.server 8000
No build step and no JS framework — just one inline copy-to-clipboard script
for the install command. The only external link is https://cil.by.

403
sites/landing/index.html Normal file
View file

@ -0,0 +1,403 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Simbiot — a rhythmic agent harness</title>
<meta name="description" content="Simbiot is a rhythmic agent harness: an AI agent that lives by ticks, works by itself on a schedule, reports digests, and escalates what it cannot safely decide.">
<link rel="canonical" href="https://simbiot.cil.by/">
<meta name="color-scheme" content="dark">
<meta name="theme-color" content="#0b0f14">
<meta property="og:type" content="website">
<meta property="og:site_name" content="Simbiot">
<meta property="og:url" content="https://simbiot.cil.by/">
<meta property="og:title" content="Simbiot — a rhythmic agent harness">
<meta property="og:description" content="An AI agent that lives by ticks like a living organism. CQRS + Event Sourcing. Signed, hash-chained events. Runs for a cooperative.">
<meta name="twitter:card" content="summary">
<meta name="twitter:title" content="Simbiot — a rhythmic agent harness">
<meta name="twitter:description" content="An AI agent that lives by ticks like a living organism. CQRS + Event Sourcing. Signed, hash-chained events. Runs for a cooperative.">
<link rel="icon" href="data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%2032%2032'%3E%3Crect%20width='32'%20height='32'%20rx='7'%20fill='%230b0f14'/%3E%3Cpath%20d='M5%2018h5l2-7%204%2013%203-9%202%203h6'%20fill='none'%20stroke='%235eead4'%20stroke-width='2.5'%20stroke-linecap='round'%20stroke-linejoin='round'/%3E%3C/svg%3E">
<link rel="stylesheet" href="styles.css">
</head>
<body>
<a class="skip-link" href="#main">Skip to content</a>
<header class="site-header">
<div class="container header-inner">
<a class="brand" href="#top">
<svg class="brand-mark" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M2 12h4l2-5 4 10 3-7 2 2h5"/></svg>
Simbiot
</a>
<nav aria-label="Primary">
<ul class="nav-links">
<li><a href="#product">Product</a></li>
<li><a href="#how-it-works">How it works</a></li>
<li><a href="#portal">Portal</a></li>
<li><a href="#presale">Presale</a></li>
<li><a href="#faq">FAQ</a></li>
</ul>
</nav>
<a class="btn btn-primary btn-sm" href="https://presale.cil.by/">Join presale</a>
</div>
</header>
<main id="main">
<section class="hero" id="top" aria-labelledby="hero-title">
<div class="container">
<p class="eyebrow">Technology cooperative · <a href="https://cil.by">cil.by</a></p>
<h1 id="hero-title">An AI agent that lives by ticks, not prompts.</h1>
<p class="lead">Simbiot is a rhythmic agent harness built on CQRS + Event Sourcing, with the Strands Harness SDK as its executive core. It works by itself on a schedule, reports digests on time, escalates what it cannot safely decide — and executes with real tools: shell, files, knowledge search.</p>
<div class="install">
<div class="install-bar">
<span class="install-label">install · open-source core</span>
<button class="copy-btn" type="button" data-copy="install-cmd" aria-label="Copy install command"><span>copy</span></button>
</div>
<div class="install-body">
<span class="prompt" aria-hidden="true">$</span>
<code id="install-cmd">curl -fsSL https://raw.cil.by/simbiot/install.sh | bash</code>
</div>
</div>
<div class="cta-row">
<a class="btn btn-primary" href="https://presale.cil.by/">Join the presale</a>
<a class="btn btn-ghost" href="#how-it-works">How it works</a>
</div>
<p class="hero-meta">
<span class="chip">open-source core</span>
<span class="chip">presale · 10 seats</span>
<span class="chip">$100/mo — one price</span>
</p>
</div>
</section>
<section class="section" id="product" aria-labelledby="product-title">
<div class="container">
<div class="section-head">
<p class="eyebrow">Product</p>
<h2 id="product-title">Not a chatbot</h2>
<p class="section-lead">A chatbot answers when you ask. Simbiot is built the other way around: an agent that runs for a cooperative, on its own schedule.</p>
</div>
<div class="compare">
<article class="compare-card">
<h3>A chatbot <span class="compare-tag">prompt-driven</span></h3>
<ul>
<li>Waits for you to open the tab</li>
<li>Acts only when prompted</li>
<li>Context starts and ends with the conversation</li>
<li>You do the remembering and the follow-up</li>
</ul>
</article>
<article class="compare-card sim">
<h3>Simbiot <span class="compare-tag">tick-driven</span></h3>
<ul>
<li>Works by itself, on a schedule — ticks like a living organism</li>
<li>Reports digests on time</li>
<li>Escalates what it cannot safely decide</li>
<li>Executes with real tools: shell, files, knowledge search</li>
</ul>
</article>
</div>
<p class="chips-label">Built on:</p>
<p class="chips">
<span class="chip">CQRS</span>
<span class="chip">Event Sourcing</span>
<span class="chip">Strands Harness SDK — executive core</span>
<span class="chip">Idempotent ticks</span>
</p>
</div>
</section>
<section class="section" id="how-it-works" aria-labelledby="how-title">
<div class="container">
<div class="section-head">
<p class="eyebrow">How it works</p>
<h2 id="how-title">The ladder</h2>
<p class="section-lead">The harness lives by ticks at four levels — reflexes at the second scale, planning at the day scale.</p>
</div>
<div class="ladder">
<article class="ladder-card">
<div class="ladder-top">
<span class="tick-level">sec</span>
<span class="tick-interval">every 1 s</span>
</div>
<dl>
<div><dt>Context</dt><dd>None</dd></div>
<div><dt>Job</dt><dd>Reflexes: telemetry, alerts</dd></div>
</dl>
</article>
<article class="ladder-card">
<div class="ladder-top">
<span class="tick-level">min</span>
<span class="tick-interval">every 1 min</span>
</div>
<dl>
<div><dt>Context</dt><dd>256K</dd></div>
<div><dt>Job</dt><dd>Triage: incoming messages, quick fixes</dd></div>
</dl>
</article>
<article class="ladder-card">
<div class="ladder-top">
<span class="tick-level">hour</span>
<span class="tick-interval">every 1 h</span>
</div>
<dl>
<div><dt>Context</dt><dd>512K</dd></div>
<div><dt>Job</dt><dd>S/M tasks, hourly digest</dd></div>
</dl>
</article>
<article class="ladder-card">
<div class="ladder-top">
<span class="tick-level">day</span>
<span class="tick-interval">every 1 d</span>
</div>
<dl>
<div><dt>Context</dt><dd>1M</dd></div>
<div><dt>Job</dt><dd>M/L tasks, day manifest</dd></div>
</dl>
</article>
</div>
<div class="invariants">
<p class="invariants-title">Ladder invariants</p>
<ul>
<li>Every tick is idempotent</li>
<li>Busy levels absorb ticks</li>
<li>Only digests travel upward</li>
</ul>
</div>
</div>
</section>
<section class="section" id="portal" aria-labelledby="portal-title">
<div class="container">
<div class="section-head">
<p class="eyebrow">Portal</p>
<h2 id="portal-title">The cooperative portal</h2>
<p class="section-lead">The org-wide layer around your agents: shared work, shared knowledge — and event subscriptions, so agents react proactively.</p>
</div>
<div class="portal-grid">
<article class="feature-card">
<svg class="feature-icon" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="3" y="3" width="7" height="7" rx="1.5"/><rect x="14" y="3" width="7" height="7" rx="1.5"/><rect x="3" y="14" width="7" height="7" rx="1.5"/><rect x="14" y="14" width="7" height="7" rx="1.5"/></svg>
<h3>Shared task board &amp; projects</h3>
<p>One board for the whole cooperative. Agents and humans work from the same tasks and projects.</p>
</article>
<article class="feature-card">
<svg class="feature-icon" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 19.5A2.5 2.5 0 0 1 6.5 17H20"/><path d="M6.5 2H20v20H6.5A2.5 2.5 0 0 1 4 19.5v-15A2.5 2.5 0 0 1 6.5 2z"/></svg>
<h3>Knowledge bases</h3>
<p>Knowledge bases with semantic search (RAG), so what the cooperative knows stays reachable.</p>
</article>
<article class="feature-card">
<svg class="feature-icon" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M10 6h11M10 12h11M10 18h11"/><path d="M3 6l1 1 2-2M3 12l1 1 2-2M3 18l1 1 2-2"/></svg>
<h3>Meeting &amp; decision protocols</h3>
<p>Meetings and decisions run on protocols the whole cooperative can see and follow.</p>
</article>
<article class="feature-card">
<svg class="feature-icon" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><polygon points="13 2 3 14 12 14 11 22 21 10 12 10 13 2"/></svg>
<h3>REST API + MCP gateway</h3>
<p>REST API and MCP gateway with per-agent tokens — wire agents into your own systems.</p>
</article>
<article class="feature-card">
<svg class="feature-icon" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M6 20V10M12 20V4M18 20v-6"/></svg>
<h3>Live task dashboard</h3>
<p>Statuses, escalations, digests, and spend — live, in one place.</p>
</article>
<article class="feature-card">
<svg class="feature-icon" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/></svg>
<h3>Chat with your agent</h3>
<p>Direct chat with your agent — batch replies and voice.</p>
</article>
</div>
</div>
</section>
<section class="section" id="trust" aria-labelledby="trust-title">
<div class="container">
<div class="section-head">
<p class="eyebrow">Trust</p>
<h2 id="trust-title">Signed, chained, safe to retry</h2>
<p class="section-lead">Autonomy needs a verifiable record. Every event in Simbiot is signed, chained, and safe to retry.</p>
</div>
<div class="trust-grid">
<article class="trust-card">
<svg class="feature-icon" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 3l7 3v5c0 4.6-3 7.7-7 9-4-1.3-7-4.4-7-9V6l7-3z"/><path d="M9 12l2 2 4-4"/></svg>
<h3>Signed event chain</h3>
<p>Every event is signed (ed25519) and hash-chained into a tamper-evident log. <span class="icode">verify</span> walks the full chain.</p>
</article>
<article class="trust-card">
<svg class="feature-icon" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M17 1l4 4-4 4"/><path d="M3 11V9a4 4 0 0 1 4-4h14"/><path d="M7 23l-4-4 4-4"/><path d="M21 13v2a4 4 0 0 1-4 4H3"/></svg>
<h3>Idempotent delivery</h3>
<p>No duplicate notifications. Retries are safe — a repeated event lands once.</p>
</article>
<article class="trust-card">
<svg class="feature-icon" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M7 17L17 7"/><path d="M8 7h9v9"/></svg>
<h3>Escalation ladder</h3>
<p>What an agent cannot decide safely goes up, with full context attached.</p>
</article>
</div>
<p class="proof-note">Production, as of today: <strong>580,000+ signed events</strong> in the cooperative's own log · <strong>280,000+ second-level ticks</strong> and counting · <strong>1 paying presale customer</strong>. Every number comes from the same event chain you can verify with <span class="icode">verify</span> — not from a marketing dashboard.</p>
</div>
</section>
<section class="section" id="presale" aria-labelledby="presale-title">
<div class="container">
<div class="section-head">
<p class="eyebrow">Presale</p>
<h2 id="presale-title">Your own harness</h2>
<p class="section-lead">The same system the cooperative runs in production — not a cut-down demo.</p>
</div>
<div class="offer">
<div class="price-card">
<p class="price"><span class="amount">$100</span><span class="per">/mo</span></p>
<p class="price-note">One price everywhere. No discounts — a single honest number.</p>
<p class="seat-badge">10 presale seats</p>
<p class="seat-note">Urgency comes from the seat limit, not from discounts.</p>
<div class="cta-row">
<a class="btn btn-primary" href="https://presale.cil.by/">Get a seat — self-serve checkout</a>
</div>
<p class="cta-hint">Checkout is self-serve via Stripe. Questions first — <a href="https://cil.by">ask in the cooperative portal</a>.</p>
</div>
<div class="offer-lists">
<div class="offer-panel">
<h3>Included</h3>
<ul class="list-include">
<li>The Simbiot harness and the cooperative portal</li>
<li>Agent deployment and onboarding — delivery within the month of purchase</li>
<li>Task dashboard</li>
<li>Chat with your agent</li>
<li>Event subscriptions</li>
<li>Priority support</li>
<li>Ongoing updates while subscribed</li>
<li>LLM backend through the cooperative's router (same as production); base quotas included</li>
</ul>
</div>
<div class="offer-panel">
<h3>Not included</h3>
<ul class="list-exclude">
<li>Custom contract development</li>
<li>Personal SLA guarantees</li>
<li>Third-party billing</li>
</ul>
</div>
</div>
</div>
</div>
</section>
<section class="section" id="faq" aria-labelledby="faq-title">
<div class="container">
<div class="section-head">
<p class="eyebrow">FAQ</p>
<h2 id="faq-title">Questions, answered</h2>
</div>
<div class="faq">
<details open>
<summary>Is Simbiot a chatbot?</summary>
<p>No. Simbiot is a rhythmic agent harness: it works by itself on a schedule and reports digests on time. You can chat with your agent in the portal — with batch replies and voice — but the agent does not wait for you to open a tab.</p>
</details>
<details>
<summary>What exactly is a tick?</summary>
<p>The heartbeat of the harness. Ticks fire at four levels — every second, minute, hour, and day — each with its own context budget and job. Every tick is idempotent, busy levels absorb ticks, and only digests travel upward.</p>
</details>
<details>
<summary>Can I verify what my agent did?</summary>
<p>Yes. Every event is signed with ed25519 and hash-chained into a tamper-evident log. The <span class="icode">verify</span> command walks the full chain.</p>
</details>
<details>
<summary>What happens when the agent is not sure?</summary>
<p>It escalates. Anything an agent cannot decide safely goes up the ladder with full context attached, so a human or a higher level makes the call.</p>
</details>
<details>
<summary>What does the $100/mo subscription include?</summary>
<p>The harness, the cooperative portal, agent deployment and onboarding (delivered within the month of purchase), the task dashboard, chat with your agent, event subscriptions, priority support, and ongoing updates while subscribed. The LLM backend runs through the cooperative's router — the same as production — with base quotas included.</p>
</details>
<details>
<summary>What is not included?</summary>
<p>Custom contract development, personal SLA guarantees, and third-party billing. The price is $100/mo everywhere, with no discounts; the presale is limited to 10 seats.</p>
</details>
</div>
</div>
</section>
</main>
<footer class="site-footer">
<div class="container">
<div class="footer-grid">
<div>
<a class="brand" href="#top">
<svg class="brand-mark" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M2 12h4l2-5 4 10 3-7 2 2h5"/></svg>
Simbiot
</a>
<p class="footer-tagline">A rhythmic agent harness, run by a technology cooperative.</p>
</div>
<nav aria-label="Footer">
<h3 class="footer-title">Site</h3>
<ul class="footer-links">
<li><a href="#product">Product</a></li>
<li><a href="#how-it-works">How it works</a></li>
<li><a href="#presale">Presale</a></li>
<li><a href="#faq">FAQ</a></li>
</ul>
</nav>
<div>
<h3 class="footer-title">Cooperative</h3>
<ul class="footer-links">
<li><a href="https://cil.by">Portal &amp; coop — cil.by</a></li>
</ul>
<h3 class="footer-title">Install</h3>
<code class="footer-install">curl -fsSL https://raw.cil.by/simbiot/install.sh | bash</code>
</div>
</div>
<p class="footer-small">Simbiot is a technology cooperative. One price — $100/mo, no discounts; the presale is limited to 10 seats. Not offered: custom contract development, personal SLA guarantees, third-party billing. © 2026 Simbiot Cooperative.</p>
</div>
</footer>
<script>
(function () {
var btn = document.querySelector('[data-copy]');
if (!btn) return;
var target = document.getElementById(btn.getAttribute('data-copy'));
var label = btn.querySelector('span');
var resetTimer = null;
btn.addEventListener('click', function () {
var text = target.textContent;
var done = function () {
label.textContent = 'copied';
btn.setAttribute('aria-label', 'Install command copied');
if (resetTimer) clearTimeout(resetTimer);
resetTimer = setTimeout(function () {
label.textContent = 'copy';
btn.setAttribute('aria-label', 'Copy install command');
}, 2000);
};
if (navigator.clipboard && navigator.clipboard.writeText) {
navigator.clipboard.writeText(text).then(done);
} else {
var ta = document.createElement('textarea');
ta.value = text;
ta.setAttribute('readonly', '');
ta.style.position = 'fixed';
ta.style.left = '-9999px';
document.body.appendChild(ta);
ta.select();
document.execCommand('copy');
document.body.removeChild(ta);
done();
}
});
})();
</script>
</body>
</html>

702
sites/landing/styles.css Normal file
View file

@ -0,0 +1,702 @@
:root {
--bg: #0b0f14;
--surface: #111823;
--surface-2: #16202e;
--border: #212c3b;
--border-strong: #2e3d52;
--text: #d7e1ec;
--text-strong: #f2f7fc;
--text-soft: #b7c3d3;
--muted: #97a6b8;
--accent: #5eead4;
--accent-strong: #83f2e0;
--accent-ink: #052e2b;
--warn: #f3a4b0;
--radius: 10px;
--radius-lg: 14px;
--mono: ui-monospace, "SF Mono", "Cascadia Code", Menlo, Consolas, "Liberation Mono", monospace;
--sans: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
}
* { box-sizing: border-box; }
html { scroll-behavior: smooth; }
body {
margin: 0;
font-family: var(--sans);
background: var(--bg);
color: var(--text);
line-height: 1.6;
-webkit-font-smoothing: antialiased;
}
::selection { background: rgba(94, 234, 212, 0.28); }
a { color: var(--accent); text-decoration: none; }
a:hover { color: var(--accent-strong); }
a:focus-visible,
button:focus-visible,
summary:focus-visible {
outline: 2px solid var(--accent);
outline-offset: 2px;
border-radius: 6px;
}
.container {
width: min(100% - 2.5rem, 1080px);
margin-inline: auto;
}
.skip-link {
position: absolute;
left: 1rem;
top: -4rem;
z-index: 100;
background: var(--accent);
color: var(--accent-ink);
font-weight: 600;
padding: 0.6rem 1rem;
border-radius: 8px;
transition: top 0.15s ease;
}
.skip-link:focus { top: 1rem; }
.site-header {
position: sticky;
top: 0;
z-index: 50;
background: rgba(11, 15, 20, 0.86);
backdrop-filter: blur(10px);
-webkit-backdrop-filter: blur(10px);
border-bottom: 1px solid var(--border);
}
.header-inner {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 0.5rem 1rem;
padding-block: 0.75rem;
}
.brand {
display: inline-flex;
align-items: center;
gap: 0.55rem;
margin-right: auto;
color: var(--text-strong);
font-weight: 700;
font-size: 1.08rem;
letter-spacing: 0.01em;
}
.brand:hover { color: var(--text-strong); }
.brand-mark { color: var(--accent); flex-shrink: 0; }
.nav-links {
list-style: none;
display: flex;
flex-wrap: wrap;
gap: 0.15rem;
margin: 0;
padding: 0;
}
.nav-links a {
display: inline-block;
padding: 0.45rem 0.7rem;
border-radius: 8px;
color: var(--muted);
font-size: 0.95rem;
}
.nav-links a:hover { color: var(--text-strong); background: var(--surface); }
.btn {
display: inline-flex;
align-items: center;
justify-content: center;
gap: 0.5rem;
font-weight: 600;
font-size: 1rem;
line-height: 1.2;
padding: 0.7rem 1.2rem;
border-radius: 9px;
border: 1px solid transparent;
transition: background-color 0.15s ease, border-color 0.15s ease, color 0.15s ease;
}
.btn-primary { background: var(--accent); color: var(--accent-ink); }
.btn-primary:hover { background: var(--accent-strong); color: var(--accent-ink); }
.btn-ghost { border-color: var(--border-strong); color: var(--text); }
.btn-ghost:hover { border-color: var(--accent); color: var(--accent); }
.btn-sm { padding: 0.45rem 0.85rem; font-size: 0.9rem; }
.hero {
position: relative;
overflow: hidden;
padding-block: clamp(3.5rem, 9vw, 6.5rem) clamp(3rem, 7vw, 5rem);
}
.hero::before {
content: "";
position: absolute;
inset: 0;
background-image:
linear-gradient(rgba(148, 163, 184, 0.06) 1px, transparent 1px),
linear-gradient(90deg, rgba(148, 163, 184, 0.06) 1px, transparent 1px);
background-size: 42px 42px;
-webkit-mask-image: radial-gradient(ellipse 90% 85% at 50% 0%, #000 25%, transparent 72%);
mask-image: radial-gradient(ellipse 90% 85% at 50% 0%, #000 25%, transparent 72%);
pointer-events: none;
}
.hero .container { position: relative; }
.eyebrow {
font-family: var(--mono);
font-size: 0.78rem;
letter-spacing: 0.14em;
text-transform: uppercase;
color: var(--accent);
margin: 0 0 1rem;
}
h1 {
font-size: clamp(2.1rem, 5.5vw, 3.35rem);
line-height: 1.08;
letter-spacing: -0.02em;
color: var(--text-strong);
margin: 0 0 1.25rem;
max-width: 18ch;
}
.lead {
font-size: clamp(1.02rem, 1.6vw, 1.16rem);
line-height: 1.65;
color: var(--text-soft);
margin: 0;
max-width: 62ch;
}
.install {
margin-top: 2.25rem;
max-width: 640px;
background: var(--surface);
border: 1px solid var(--border);
border-radius: var(--radius-lg);
overflow: hidden;
}
.install-bar {
display: flex;
align-items: center;
justify-content: space-between;
gap: 1rem;
padding: 0.5rem 0.6rem 0.5rem 1rem;
border-bottom: 1px solid var(--border);
}
.install-label {
font-family: var(--mono);
font-size: 0.75rem;
letter-spacing: 0.08em;
color: var(--muted);
}
.copy-btn {
font-family: var(--mono);
font-size: 0.78rem;
color: var(--muted);
background: transparent;
border: 1px solid var(--border-strong);
border-radius: 7px;
padding: 0.3rem 0.7rem;
cursor: pointer;
transition: color 0.15s ease, border-color 0.15s ease;
}
.copy-btn:hover, .copy-btn:focus-visible { color: var(--accent); border-color: var(--accent); }
.install-body {
display: flex;
align-items: baseline;
gap: 0.75rem;
padding: 1rem;
overflow-x: auto;
}
.prompt { font-family: var(--mono); color: var(--accent); flex-shrink: 0; }
.install-body code {
font-family: var(--mono);
font-size: 0.95rem;
color: var(--text-strong);
white-space: nowrap;
}
.cta-row {
display: flex;
flex-wrap: wrap;
gap: 0.75rem;
margin-top: 2rem;
}
.hero-meta {
display: flex;
flex-wrap: wrap;
gap: 0.5rem;
margin: 1.75rem 0 0;
}
.chip {
display: inline-block;
font-family: var(--mono);
font-size: 0.8rem;
color: var(--text);
background: var(--surface);
border: 1px solid var(--border-strong);
border-radius: 999px;
padding: 0.3rem 0.8rem;
}
.section {
scroll-margin-top: 4.75rem;
padding-block: clamp(3.25rem, 8vw, 5.5rem);
border-top: 1px solid var(--border);
}
.section-head {
max-width: 62ch;
margin-bottom: clamp(2rem, 4vw, 3rem);
}
.section-head .eyebrow { margin-bottom: 0.9rem; }
h2 {
font-size: clamp(1.6rem, 3.4vw, 2.25rem);
line-height: 1.15;
letter-spacing: -0.015em;
color: var(--text-strong);
margin: 0 0 1rem;
}
.section-lead {
font-size: 1.05rem;
line-height: 1.65;
color: var(--text-soft);
margin: 0;
}
.compare {
display: grid;
gap: 1rem;
}
.compare-card {
background: var(--surface);
border: 1px solid var(--border);
border-radius: var(--radius-lg);
padding: 1.6rem;
}
.compare-card.sim { border-color: rgba(94, 234, 212, 0.45); }
.compare-card h3 {
display: flex;
align-items: baseline;
gap: 0.75rem;
flex-wrap: wrap;
font-size: 1.05rem;
color: var(--text-strong);
margin: 0 0 1.1rem;
}
.compare-card.sim h3 { color: var(--accent); }
.compare-tag {
font-family: var(--mono);
font-size: 0.72rem;
letter-spacing: 0.1em;
text-transform: uppercase;
color: var(--muted);
font-weight: 400;
}
.compare-card ul {
list-style: none;
margin: 0;
padding: 0;
display: grid;
gap: 0.7rem;
}
.compare-card li {
position: relative;
padding-left: 1.4rem;
color: var(--text-soft);
line-height: 1.55;
}
.compare-card li::before {
content: "–";
position: absolute;
left: 0;
color: var(--muted);
}
.compare-card.sim li::before { content: "→"; color: var(--accent); }
.chips-label {
font-family: var(--mono);
font-size: 0.78rem;
letter-spacing: 0.1em;
text-transform: uppercase;
color: var(--muted);
margin: 1.9rem 0 0.6rem;
}
.chips {
display: flex;
flex-wrap: wrap;
gap: 0.5rem;
margin: 0;
}
.ladder {
display: grid;
gap: 1rem;
grid-template-columns: repeat(auto-fit, minmax(230px, 1fr));
}
.ladder-card {
background: var(--surface);
border: 1px solid var(--border);
border-radius: var(--radius-lg);
padding: 1.5rem;
display: flex;
flex-direction: column;
gap: 1.1rem;
}
.ladder-top {
display: flex;
align-items: baseline;
justify-content: space-between;
gap: 0.5rem;
border-bottom: 1px solid var(--border);
padding-bottom: 0.9rem;
}
.tick-level {
font-family: var(--mono);
font-size: 1.35rem;
font-weight: 600;
color: var(--accent);
}
.tick-interval {
font-family: var(--mono);
font-size: 0.85rem;
color: var(--muted);
}
.ladder-card dl {
margin: 0;
display: grid;
gap: 0.7rem;
}
.ladder-card dt {
font-family: var(--mono);
font-size: 0.72rem;
letter-spacing: 0.1em;
text-transform: uppercase;
color: var(--muted);
margin-bottom: 0.15rem;
}
.ladder-card dd {
margin: 0;
color: var(--text-soft);
font-size: 0.96rem;
line-height: 1.5;
}
.invariants {
margin-top: 1.5rem;
background: var(--surface);
border: 1px solid var(--border-strong);
border-radius: var(--radius-lg);
padding: 1.4rem 1.6rem;
}
.invariants-title {
font-family: var(--mono);
font-size: 0.78rem;
letter-spacing: 0.12em;
text-transform: uppercase;
color: var(--muted);
margin: 0 0 0.9rem;
}
.invariants ul {
list-style: none;
margin: 0;
padding: 0;
display: grid;
gap: 0.6rem;
}
.invariants li {
position: relative;
padding-left: 1.4rem;
color: var(--text-soft);
}
.invariants li::before {
content: "→";
position: absolute;
left: 0;
color: var(--accent);
font-family: var(--mono);
}
.portal-grid,
.trust-grid {
display: grid;
gap: 1rem;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
}
.feature-card,
.trust-card {
background: var(--surface);
border: 1px solid var(--border);
border-radius: var(--radius-lg);
padding: 1.5rem;
}
.feature-icon {
display: block;
color: var(--accent);
margin-bottom: 1rem;
}
.feature-card h3,
.trust-card h3 {
font-size: 1.02rem;
color: var(--text-strong);
margin: 0 0 0.6rem;
}
.feature-card p,
.trust-card p {
margin: 0;
color: var(--text-soft);
font-size: 0.95rem;
line-height: 1.6;
}
.proof-note {
margin: 2.2rem 0 0;
padding: 1.1rem 1.3rem;
border: 1px solid var(--border);
border-left: 3px solid var(--accent);
border-radius: 10px;
background: var(--surface);
color: var(--muted);
font-size: 0.92rem;
line-height: 1.7;
max-width: 72ch;
}
.proof-note strong { color: var(--text-strong); font-weight: 600; }
.icode {
font-family: var(--mono);
font-size: 0.9em;
color: var(--text-strong);
background: var(--surface-2);
border: 1px solid var(--border);
border-radius: 5px;
padding: 0.05em 0.4em;
}
.offer {
display: grid;
gap: 1.25rem;
align-items: start;
}
.price-card {
background: var(--surface);
border: 1px solid var(--border-strong);
border-radius: var(--radius-lg);
padding: 1.9rem;
}
.price {
display: flex;
align-items: baseline;
gap: 0.45rem;
margin: 0 0 0.75rem;
}
.amount {
font-family: var(--mono);
font-size: 2.9rem;
font-weight: 700;
line-height: 1;
color: var(--text-strong);
letter-spacing: -0.02em;
}
.per {
font-size: 1.05rem;
color: var(--muted);
}
.price-note {
margin: 0 0 1.4rem;
color: var(--text-soft);
max-width: 34ch;
}
.seat-badge {
display: inline-block;
font-family: var(--mono);
font-size: 0.85rem;
color: var(--accent);
border: 1px solid rgba(94, 234, 212, 0.5);
border-radius: 999px;
padding: 0.35rem 0.85rem;
margin: 0 0 0.5rem;
}
.seat-note {
margin: 0 0 1.6rem;
color: var(--muted);
font-size: 0.92rem;
max-width: 34ch;
}
.cta-hint {
margin: 0.9rem 0 0;
color: var(--muted);
font-size: 0.92rem;
}
.offer-lists {
display: grid;
gap: 1.25rem;
}
.offer-panel {
background: var(--surface);
border: 1px solid var(--border);
border-radius: var(--radius-lg);
padding: 1.5rem 1.6rem;
}
.offer-panel h3 {
font-family: var(--mono);
font-size: 0.78rem;
letter-spacing: 0.12em;
text-transform: uppercase;
color: var(--muted);
margin: 0 0 1rem;
}
.offer-panel ul {
list-style: none;
margin: 0;
padding: 0;
display: grid;
gap: 0.65rem;
}
.offer-panel li {
position: relative;
padding-left: 1.5rem;
color: var(--text-soft);
line-height: 1.55;
font-size: 0.96rem;
}
.list-include li::before {
content: "+";
position: absolute;
left: 0;
color: var(--accent);
font-family: var(--mono);
}
.list-exclude li::before {
content: "–";
position: absolute;
left: 0;
color: var(--warn);
font-family: var(--mono);
}
.faq {
display: grid;
gap: 0.75rem;
max-width: 780px;
}
.faq details {
background: var(--surface);
border: 1px solid var(--border);
border-radius: var(--radius);
}
.faq summary {
display: flex;
align-items: center;
justify-content: space-between;
gap: 1rem;
padding: 1rem 1.25rem;
cursor: pointer;
font-weight: 600;
color: var(--text-strong);
list-style: none;
}
.faq summary::-webkit-details-marker { display: none; }
.faq summary::after {
content: "+";
font-family: var(--mono);
font-size: 1.25rem;
color: var(--muted);
flex-shrink: 0;
}
.faq details[open] summary::after { content: "–"; }
.faq details[open] summary { border-bottom: 1px solid var(--border); }
.faq details p {
margin: 0;
padding: 1rem 1.25rem 1.25rem;
color: var(--text-soft);
line-height: 1.65;
}
.site-footer {
border-top: 1px solid var(--border);
padding-block: 3rem 2rem;
}
.footer-grid {
display: grid;
gap: 2rem;
}
.footer-tagline {
margin: 0.9rem 0 0;
color: var(--muted);
font-size: 0.95rem;
max-width: 30ch;
}
.footer-title {
font-family: var(--mono);
font-size: 0.75rem;
letter-spacing: 0.12em;
text-transform: uppercase;
color: var(--muted);
margin: 0 0 0.8rem;
}
.footer-grid .footer-title:nth-of-type(n+2) { margin-top: 1.6rem; }
.footer-links {
list-style: none;
margin: 0;
padding: 0;
display: grid;
gap: 0.5rem;
}
.footer-links a {
color: var(--text-soft);
font-size: 0.95rem;
}
.footer-links a:hover { color: var(--text-strong); }
.footer-install {
display: block;
font-family: var(--mono);
font-size: 0.8rem;
color: var(--text-soft);
background: var(--surface);
border: 1px solid var(--border);
border-radius: 8px;
padding: 0.6rem 0.8rem;
overflow-x: auto;
white-space: nowrap;
}
.footer-small {
margin: 2.5rem 0 0;
padding-top: 1.5rem;
border-top: 1px solid var(--border);
color: var(--muted);
font-size: 0.85rem;
line-height: 1.65;
max-width: 78ch;
}
@media (min-width: 760px) {
.compare { grid-template-columns: 1fr 1fr; }
.invariants ul { grid-template-columns: repeat(3, 1fr); gap: 0.6rem 1.5rem; }
.footer-grid { grid-template-columns: 1.3fr 0.8fr 1.6fr; }
}
@media (min-width: 920px) {
.offer { grid-template-columns: minmax(0, 5fr) minmax(0, 7fr); }
}
@media (prefers-reduced-motion: reduce) {
html { scroll-behavior: auto; }
*, *::before, *::after {
transition-duration: 0.01ms !important;
animation-duration: 0.01ms !important;
}
}

View file

@ -12,6 +12,7 @@
|---|---|---| |---|---|---|
| `meta-skill-factory` | Метаскилл: превращение повторяющейся работы в скилл репо (шаги, чеклисты, критерии качества) | Заметил, что делаю одну и ту же работу 2+ раз — прежде чем делать руками в третий | | `meta-skill-factory` | Метаскилл: превращение повторяющейся работы в скилл репо (шаги, чеклисты, критерии качества) | Заметил, что делаю одну и ту же работу 2+ раз — прежде чем делать руками в третий |
| `presale-landing` | Производство пресейл/продуктовых лендингов: структура, честные мокапы, Stripe CTA, quality gates | Задача на лендинг/пресейл-страницу или её v2 | | `presale-landing` | Производство пресейл/продуктовых лендингов: структура, честные мокапы, Stripe CTA, quality gates | Задача на лендинг/пресейл-страницу или её v2 |
| `systemone-bench` | Decision-эндпоинт Ollama /v1/systemone (nimble) + GA-оптимизация: иерархический выбор top-2 группы → choice ≤9, GA последовательностей действий с фитнесом=AUM | Тик, где нужно выбрать одно действие из >9 однородных опций; оптимизация политик на симуляторе |
## Сторонние наборы ## Сторонние наборы

View file

@ -0,0 +1,95 @@
---
name: systemone-bench
description: Use when choosing between 3+ non-numeric action options via local Ollama decision endpoint (/v1/systemone, model nimble), or when optimizing action sequences with GA against a simulator. Turns "which of many options" into a measured, hierarchical choice instead of free-form LLM guessing.
metadata:
origin: t-261005074437-39a9b0 (внедрение systemone-bench в архитектуру харнеса)
code: /root/systemone-bench (clone symbiot/systemone-bench @ git.cil.by; README.md там же)
scope: любые тики харнеса, где нужен выбор действия из каталога >9 кандидатов; симуляция политик стартапа
---
# systemone-bench: decision-эндпоинт + GA-оптимизация выбора
## Trigger rule
- [ ] Нужно выбрать ОДНО действие из >9 однородных опций (команды, конфиги, политики)
- [ ] Опции разбиты (или разбиваются) на группы по 2–8 в группе
- [ ] Есть критерий качества выбора (fitness / AUM / метрика), хотя бы приблизительный
- [ ] Нужен выбор на симуляторе перед применением в бою (бесплатные прогоны)
Не использовать: для бинарных решений (это noul), для абсолютных оценок тяжести
(score плох на абсолютных шкалах — подтверждено сюитом), если опций ≤ 9 (делай
обычный choice без иерархии).
## Steps
### 1. Проверка окружения
```bash
curl -s http://127.0.0.1:11434/api/tags | grep -o '"nimble"' # модель на месте
cd /root/systemone-bench && python3 ga/ga.py --smoke # ~13с, должен быть зелёный
cd /root/systemone-bench && python3 harness/hier.py --model nimble --quick # ~мин, e2e ~81% на quick-16
```
Контракт эндпоинта: `choice` — criteria **объект**, 2–26 опций; `score` —
criteria **массив** 2–26 уровней, `score = Σ i·pᵢ`; `noul` — вероятность «да».
1–64 вопроса на запрос, тело ≤ 64 KiB, промпт ≤ 8194 токенов. `confidence` =
концентрация распределения, НЕ вероятность правильности — не опирайся на него
как на calibration.
### 2. Схема выбора: top-2 группы → choice по шортлисту ≤ 9
Главная рекомендация (README, рекомендация №1): flat-choice по всем 64 командам
падает до 0% (эндпоинт режет >26 и точность падает), иерархия:
1. **stage1 (score)**: по группам (8 групп) — взять **top-2** группы, не top-1
(top-2 hit 93.8–84.4% vs top-1 87.5–64.1% — у ~40% команд легитимный
«второй дом» в таксономии).
2. **stage2 (choice)**: объединённый шортлист кандидатов из двух групп —
**строго ≤ 9** (точность 100% на шортлисте 6 → 50% на 10).
3. **stage3 (score)**: если выбранная команда имеет параметры — ранжирование
значений score-массивом (gold #1 в 96.9–100%).
Расчётный эффект: e2e 57.8% → ~78% (top-2 выборка × stage2 92%). Латентность
полного конвейера ~520–580 мс — годится для тика, не для realtime.
### 3. GA: поиск последовательностей действий
```bash
cd /root/systemone-bench && python3 ga/ga.py # полный прогон (pop 200 × 100 gen, ~17с)
python3 ga/ga2.py --neardeath # при каком runway GA берёт опасные команды (layoff/delay_payroll)
python3 ga/ga2.py --robust # максимин по crunch/base/growth/dying
```
Фитнес = терминальный AUM основателя. GA бьёт greedy на +27–43% (8 нед) и
random search до +59%. Что уже известно (не переоткрывать):
- GA эмерджентно строит цепочки пререкрешитов (pricing_power → raise_prices_10).
- Опасные команды не выбирает при runway ≥ 2.5 мес — сигналят о реальном риске.
- **Дыра (найдена smoke-прогоном)**: `sell_secondary` даёт +100 personal cash и
спасает dying без опасных команд — симулятор вознаграждает личный кэш без
штрафа за health компании. Обход: гейт по здоровью компании (напр., фитнес
умножается на флаг health > порога, или штраф за dilution в personal cash).
### 4. Интеграция в тик харнеса
На тиках, где нужен выбор действия: собрать кандидатов → разбить на группы →
stage1 score (top-2) → choice ≤9 → применить → записать выбор и исход событием
в стор (для последующей GA-сшивки policy trace с fitness).
## Quality gates
- Шортлист stage2 **всегда ≤ 9**; если объединение top-2 групп даёт >9 — режь
по stage1-score внутри групп.
- stage1 всегда top-2, никогда top-1.
- Для тяжести/абсолютов не использовать score — только ранжирование однородного.
- Прогон ga/ga.py --smoke обязателен перед использованием GA-результатов.
- Результат каждого GA-эксперимента фиксировать событием на доску (cli.py task/complete
или event), не оставлять только в ga2-results.json.
- Не доверять `confidence` как вероятности правильности.
## References
- README.md репо /root/systemone-bench — контракт эндпоинта, все метрики сессии 2026-10-04
- harness/eval.py — 5 сюитов (шортлист, permutation, noul, score-тяжесть, батчинг)
- ga/ga.py, ga/ga2.py — GA и сценарные эксперименты; ga-results.json, ga2-results.json
- docs/systemone-bench-integration.md (репо харнеса) — интеграция и связь с AUM-петлёй

View file

@ -170,11 +170,21 @@ def _fake_http(url, headers):
_saved_http = aum._http_json _saved_http = aum._http_json
aum._http_json = _fake_http aum._http_json = _fake_http
# hermetic: OPENROUTER_KEY должен приходить из мока .env, а не из реального
# .env хоста (на мигрированном инстансе ключа нет — тест падал негерметично)
_saved_efv2 = aum._env_file_value
# hermetic: OPENROUTER_KEY из реального .env хоста не существует (миграция
# инстанса) — даём мок; STRIPE_LIVE_KEY читается из coop_env-файла теста
# через настоящий _env_file_value, его не подменяем
aum._env_file_value = (lambda path, prefix:
"sk-or-test" if prefix == "OPENROUTER_KEY"
else _saved_efv2(path, prefix))
try: try:
got2 = aum.refresh_auto_balances(path=bal_mock, got2 = aum.refresh_auto_balances(path=bal_mock,
coop_env=os.path.join(tmpdir, "stripe.env")) coop_env=os.path.join(tmpdir, "stripe.env"))
finally: finally:
aum._http_json = _saved_http aum._http_json = _saved_http
aum._env_file_value = _saved_efv2
d2 = json.load(open(bal_mock)) d2 = json.load(open(bal_mock))
check("refresh_auto: mock API → 60.0 и 123.45 записаны", check("refresh_auto: mock API → 60.0 и 123.45 записаны",
len(got2) == 2 and abs(d2["accounts"][0]["amount_usd"] - 60.0) < 0.001 len(got2) == 2 and abs(d2["accounts"][0]["amount_usd"] - 60.0) < 0.001

View file

@ -93,30 +93,37 @@ check("db: генезис только", set(evs) <= {"PolicyChanged"}, evs)
# Вендор возвращает top-k ближайших на ЛЮБОЙ запрос (пустой базы не бывает), # Вендор возвращает top-k ближайших на ЛЮБОЙ запрос (пустой базы не бывает),
# поэтому «наших знаний нет» проверяем по содержимому индекса: все чанки — # поэтому «наших знаний нет» проверяем по содержимому индекса: все чанки —
# только из документов владельца. Положительный контроль: его тема — находится. # только из документов владельца. Положительный контроль: его тема — находится.
# RAG-вендор (/root/rag) есть не на каждом хосте (миграция инстанса): без него
# init_instance честно пишет «индексация пропущена» — тест проверяет только
# то, что доступно (ES-изоляция, генезис, прод не задет), RAG — skip.
_RAG_AVAILABLE = os.path.exists("/root/rag-venv/bin/python") and os.path.exists("/root/rag/rag.py")
env2 = dict(env) env2 = dict(env)
r3 = subprocess.run([_PY, "-c", """ if _RAG_AVAILABLE:
r3 = subprocess.run([_PY, "-c", """
import sys, json import sys, json
sys.path.insert(0, %r) sys.path.insert(0, %r)
from harness import rag_lookup from harness import rag_lookup
his = rag_lookup.rag_query("теплица автополив датчики", k=3) his = rag_lookup.rag_query("теплица автополив датчики", k=3)
print(json.dumps({"his": [c["source"] for c in his]})) print(json.dumps({"his": [c["source"] for c in his]}))
""" % _REPO], capture_output=True, text=True, env=env2, timeout=120) """ % _REPO], capture_output=True, text=True, env=env2, timeout=120)
try: try:
rag = json.loads(r3.stdout.strip().splitlines()[-1]) rag = json.loads(r3.stdout.strip().splitlines()[-1])
except Exception: except Exception:
rag = {"his": []} rag = {"his": []}
rdb = sqlite3.connect(f"file:{os.path.join(home, 'rag', 'rag.db')}?mode=ro", uri=True) rdb = sqlite3.connect(f"file:{os.path.join(home, 'rag', 'rag.db')}?mode=ro", uri=True)
paths = [p for (p,) in rdb.execute("SELECT DISTINCT path FROM chunks")] paths = [p for (p,) in rdb.execute("SELECT DISTINCT path FROM chunks")]
rdb.close() rdb.close()
check("rag: в индексе только документы владельца", check("rag: в индексе только документы владельца",
paths and all(os.path.realpath(p).startswith(home + os.sep) for p in paths), paths and all(os.path.realpath(p).startswith(home + os.sep) for p in paths),
paths) paths)
check("rag: его документ находится", check("rag: его документ находится",
any("greenhouse" in s for s in rag["his"]), rag["his"]) any("greenhouse" in s for s in rag["his"]), rag["his"])
check("rag: база инстанса отдельная", check("rag: база инстанса отдельная",
os.path.exists(os.path.join(home, "rag", "rag.db")) os.path.exists(os.path.join(home, "rag", "rag.db"))
and os.path.realpath(os.path.join(home, "rag", "rag.db")) and os.path.realpath(os.path.join(home, "rag", "rag.db"))
!= os.path.realpath("/root/rag/rag.db")) != os.path.realpath("/root/rag/rag.db"))
else:
print("SKIP: RAG-вендор /root/rag отсутствует на этом хосте — контур RAG не проверялся")
# ── 3) прод не задет: кросс-записей нет ──────────────────────────────────── # ── 3) прод не задет: кросс-записей нет ────────────────────────────────────
# Прод живёт и пишет события каждую секунду; кроме того, трейсы ЭТОЙ сессии # Прод живёт и пишет события каждую секунду; кроме того, трейсы ЭТОЙ сессии

160
tests/test_repo_sync.py Normal file
View file

@ -0,0 +1,160 @@
# -*- coding: utf-8 -*-
"""t-261007082307-2e2294: репо-гигиена — fetch origin в дневном тике (hermetic).
Запуск: ./venv/bin/python tests/test_repo_sync.py
Проверяет: _repo_sync_check пишет RepoSynced с behind/ahead (fetch в git-
песочнице), один замер на день (повтор не плодит записи), при behind>0
первый день эскалации НЕТ, второй день подряд — EscalationRaised (счётчик
дней из истории стора, рестарт демона его не сбрасывает), ошибка fetch
не валит шаг (behind=unknown честно фиксируется), env-порог
SIMBIOT_REPO_SYNC_MAX_AGE_DAYS управляет чувствительностью.
"""
import json
import os
import shutil
import subprocess
import sys
import tempfile
from datetime import timedelta
# ── изоляция: своя БД в /tmp, git-песочница вместо реального репо ──
_DST = "/tmp/simbiot-test-reposync.db"
for p in (_DST, _DST + "-wal", _DST + "-shm"):
if os.path.exists(p):
os.unlink(p)
os.environ["SIMBIOT_DB"] = _DST
os.environ["SIMBIOT_REPO_SYNC_MAX_AGE_DAYS"] = "2"
sys.path.insert(0, "/root/simbiot-harness")
from harness import es, paths # noqa: E402
from harness.levels import LEVELS # noqa: E402
from harness import scheduler # noqa: E402
fails = []
def check(name, cond, extra=""):
print(("PASS" if cond else "FAIL"), name, ("| " + extra) if not cond else "")
if not cond:
fails.append(name)
def _git(cwd, *args):
r = subprocess.run(["git"] + list(args), cwd=cwd,
capture_output=True, timeout=30, check=True)
return r.stdout.decode().strip()
def synced_events(limit=200):
out = []
for e in es.get_events(limit=limit):
if e["event_type"] == "RepoSynced":
e["data"] = json.loads(e["data"])
out.append(e)
return out
def escalations(limit=500):
return [e for e in es.get_events(limit=limit)
if e["event_type"] == "EscalationRaised"]
def make_sandbox():
"""bare origin + клон: init запушен, локально ещё 1 незапушенный коммит."""
base = tempfile.mkdtemp(prefix="simbiot-reposync-")
origin = os.path.join(base, "origin.git")
_git(base, "init", "--bare", "-b", "master", origin)
clone = os.path.join(base, "clone")
_git(base, "clone", origin, clone)
for d in (clone,):
_git(d, "config", "user.email", "test@simbiot.local")
_git(d, "config", "user.name", "simbiot-test")
_git(clone, "commit", "--allow-empty", "-m", "init")
_git(clone, "push", "origin", "master")
_git(clone, "commit", "--allow-empty", "-m", "local-only") # ahead=1
return clone, origin, base
es.init_db()
clone, origin, base = make_sandbox()
_real_root = paths.REPO_ROOT
paths.REPO_ROOT = clone # песочница вместо реального репо
try:
# ── 1. happy path: fetch ok, behind=0/ahead=1, RepoSynced записан ──
res = scheduler._repo_sync_check(LEVELS["day"])
check("fetch ok", res.get("fetch_ok") is True)
check("behind=0", res.get("behind") == 0)
check("ahead=1 (незапушенный коммит виден)", res.get("ahead") == 1)
evs = synced_events(50)
check("RepoSynced записан", len(evs) == 1, f"{len(evs)}")
today = evs[0]["aggregate_id"].split(":", 1)[1]
check("aggregate_id = repo-sync:<сегодня>",
evs[0]["aggregate_id"] == "repo-sync:" +
scheduler._now().strftime("%Y-%m-%d"))
# ── 2. повтор в тот же день — замер тот же, записей не больше ──
again = scheduler._repo_sync_check(LEVELS["day"])
check("повтор: те же числа", again.get("behind") == 0 and again.get("ahead") == 1)
check("повтор в тот же день не плодит записи", len(synced_events(50)) == 1)
# ── 3. upstream ушёл вперёд: behind=1, эскалации ещё НЕТ (1-й день) ──
work = os.path.join(base, "worktree")
_git(base, "clone", origin, work)
_git(work, "config", "user.email", "test@simbiot.local")
_git(work, "config", "user.name", "simbiot-test")
_git(work, "commit", "--allow-empty", "-m", "upstream change")
_git(work, "push", "origin", "master")
res3 = scheduler._repo_sync_check(LEVELS["day"])
check("behind=1 увиден", res3.get("behind") == 1)
check("эскалации НЕТ в первый день", len(escalations()) == 0)
# ── 4. вчера тоже был behind>0 → сегодня 2-й подряд → эскалация ──
yday = (scheduler._now() - timedelta(days=1)).strftime("%Y-%m-%d")
eid, _v = es.append_event("harness", f"repo-sync:{yday}", "RepoSynced", {
"ts": yday + "T06:00:00+00:00", "fetch_ok": True,
"behind": 1, "ahead": 1})
db = es._conn()
db.execute("UPDATE events SET timestamp=? WHERE id=?",
(yday + "T06:00:00.000000+00:00", eid))
db.commit()
db.close()
res4 = scheduler._repo_sync_check(LEVELS["day"])
check("behind=1 стабилен", res4.get("behind") == 1)
escs = escalations()
check("эскалация ПОДНЯТА на 2-й день подряд", len(escs) == 1, f"{len(escs)}")
if escs:
reason = json.loads(escs[-1]["data"]).get("reason", "")
check("причина упоминает behind и 2 дня",
"behind=1" in reason and "2 дн" in reason, reason[:120])
finally:
paths.REPO_ROOT = _real_root
shutil.rmtree(base, ignore_errors=True)
# ── 5. ошибка fetch не валит шаг (свежая БД: дедуп дня не мешает) ──
_DST2 = "/tmp/simbiot-test-reposync2.db"
for p in (_DST2, _DST2 + "-wal", _DST2 + "-shm"):
if os.path.exists(p):
os.unlink(p)
es.DB_PATH = _DST2
es._signature_schema_ready = False # латч на процесс: новая БД требует миграции
es.init_db()
paths.REPO_ROOT = "/nonexistent-repo-dir"
try:
res5 = scheduler._repo_sync_check(LEVELS["day"])
check("fetch-fail: fetch_ok=False, не упал", res5.get("fetch_ok") is False)
evs5 = synced_events(50)
check("fetch-fail зафиксирован событием", len(evs5) == 1)
if evs5:
check("behind=unknown (None), не выдуманный 0",
evs5[0]["data"].get("behind") is None)
check("ошибка сохранена", bool(evs5[0]["data"].get("error")))
finally:
paths.REPO_ROOT = _real_root
# ── 6. env-порог читается при импорте ──
check("порог = 2 из env", scheduler._REPO_SYNC_MAX_AGE_DAYS == 2)
print()
print("ИТОГ:", "ВСЕ ОК" if not fails else f"ПРОВАЛЫ: {fails}")
sys.exit(1 if fails else 0)

View file

@ -39,7 +39,9 @@ with open(os.path.join(tmpdir, ".env"), "w") as f:
f.write("ROUTER_KEY=sk-envtest-key\n") f.write("ROUTER_KEY=sk-envtest-key\n")
f.write("# комментарий\n") f.write("# комментарий\n")
runtime._HARNESS_DIR = tmpdir # t-261004113351: runtime читает .env через paths.env_path() (SIMBIOT_HOME),
# _HARNESS_DIR больше не источник — патчим актуальный механизм (с рестором)
runtime._paths.env_path = lambda: os.path.join(tmpdir, ".env")
# 1. env отсутствует, .env есть → значение из .env (суть фикса) # 1. env отсутствует, .env есть → значение из .env (суть фикса)
check("1: base_url из .env quando env пуст", check("1: base_url из .env quando env пуст",
@ -55,13 +57,14 @@ check("2: env приоритетнее .env",
os.environ.pop("ROUTER_BASE_URL") os.environ.pop("ROUTER_BASE_URL")
# 3. нет ни env, ни .env → дефолт с локальным роутером # 3. нет ни env, ни .env → дефолт с локальным роутером
runtime._HARNESS_DIR = tempfile.mkdtemp(prefix="simbiot-envtest-empty-") runtime._paths.env_path = lambda: os.path.join(
tempfile.mkdtemp(prefix="simbiot-envtest-empty-"), ".env")
check("3: нет .env → дефолт 127.0.0.1:20128", check("3: нет .env → дефолт 127.0.0.1:20128",
runtime._router_base_url() == "http://127.0.0.1:20128/v1", runtime._router_base_url() == "http://127.0.0.1:20128/v1",
str(runtime._router_base_url())) str(runtime._router_base_url()))
# 4. setup_provider(): end-to-end маппинг .env → OPENAI_* env # 4. setup_provider(): end-to-end маппинг .env → OPENAI_* env
runtime._HARNESS_DIR = tmpdir runtime._paths.env_path = lambda: os.path.join(tmpdir, ".env")
runtime.setup_provider() runtime.setup_provider()
check("4: setup_provider кладёт OPENAI_BASE_URL из .env", check("4: setup_provider кладёт OPENAI_BASE_URL из .env",
os.environ["OPENAI_BASE_URL"] == "https://router.cil.by/v1", os.environ["OPENAI_BASE_URL"] == "https://router.cil.by/v1",

View file

@ -0,0 +1,91 @@
# -*- coding: utf-8 -*-
"""e-261003094628 (повтор e-261001082241): guard мусорных заголовков задач (hermetic).
Запуск: ./venv/bin/python tests/test_submit_title_guard.py
Первопричина серии 'list': LLM-тик нижнего уровня звал
`cli.py task list` / `cli.py task <id>` (хотел доску/детали задачи), а
`task` принимает позиционный title → argparse молча подсунул слово
'list' / чужой id как заголовок и подал мусорную задачу. Min-уровень
закрыл их как no-op и эскалировал — дважды.
Проверяет: submit_task режет субкомандо-подобные заголовки и чужие id
задач БЕЗ деталей (ValueError), пропускает нормальные подачи, одно слово
с деталями, обычные слова не из стоп-листа; CLI `task list` → exit 1 с
ERROR, а не тихая подача.
"""
import os
import subprocess
import sys
# ── изоляция: своя БД в /tmp (prod не трогаем) ──
_DST = "/tmp/simbiot-test-title-guard.db"
for p in (_DST, _DST + "-wal", _DST + "-shm"):
if os.path.exists(p):
os.unlink(p)
os.environ["SIMBIOT_DB"] = _DST
sys.path.insert(0, "/root/simbiot-harness")
from harness import commands
commands.es.init_db()
fails = []
def check(name, cond, extra=""):
print(("PASS" if cond else "FAIL"), name, ("| " + str(extra)) if not cond else "")
if not cond:
fails.append(name)
# ── 1. Субкомандо-подобные заголовки без деталей — режутся ──
for bad in ("list", "tasks", "status", "events", "help", "LIST", " Complete "):
try:
commands.submit_task(bad, "", minutes=5)
check(f"мусорный заголовок {bad!r} отвергнут", False, "подача прошла")
except ValueError:
check(f"мусорный заголовок {bad!r} отвергнут", True)
# ── 2. Чужой id задачи как заголовок без деталей — режется ──
try:
commands.submit_task("t-261003094008-ed226f", "", minutes=5)
check("id задачи как заголовок отвергнут", False, "подача прошла")
except ValueError:
check("id задачи как заголовок отвергнут", True)
# ── 3. Нормальные подачи — проходят ──
for title, details in (
("Разбор голосового m-973940607 + HTML-страница", "транскрипт в docs/"),
("list", "собрать список всех ключей в реестре"), # слово из стоп-листа, но с деталями
("фикс деплоя", ""),
):
try:
tid, size = commands.submit_task(title, details, minutes=5)
ok = bool(tid) and size == "S"
except ValueError as e:
ok, tid = False, str(e)
check(f"нормальная подача {title[:30]!r} прошла", ok, tid)
# ── 4. CLI: `cli.py task list` — показывает доску, а не плодит мусорную задачу ──
# Контракт изменён в origin/master (t-261003125513/e-261004134613): 'task list'
# и 'task <id>' показывают доску/задачу (exit 0). Интент guard'а прежний:
# НЕТ тихой подачи мусорной задачи с заголовком 'list'.
env = dict(os.environ, SIMBIOT_DB=_DST)
r = subprocess.run(
["./venv/bin/python", "cli.py", "task", "list"],
capture_output=True, text=True, cwd="/root/simbiot-harness", env=env,
)
out = (r.stdout + r.stderr).strip()
check("CLI task list → exit 0 (показ доски)", r.returncode == 0, f"rc={r.returncode} out={out!r}")
check("CLI task list → не ERROR-подача", "ERROR" not in out, out)
model = commands.es.run_projections()
check("мусорной задачи 'list' в сторе нет",
not any(t.get("title", "").strip().lower() == "list" and not t.get("details")
for t in model["tasks"].values()))
print()
if fails:
print(f"FAILED: {len(fails)}")
sys.exit(1)
print("OK: все проверки прошли")

View file

@ -76,6 +76,13 @@ print("--- /помощь ---\n" + tg_commands.cmd_help())
# ── 3. фильтр build_context: команда НЕ попадает мин-уровню ── # ── 3. фильтр build_context: команда НЕ попадает мин-уровню ──
model = es.run_projections() model = es.run_projections()
fake = json.loads(json.dumps(model)) fake = json.loads(json.dumps(model))
# доска: тестовая ready-задача (в живой базе ready может не быть — секция
# «ДОСКА ЗАДАЧ» обязана появляться от самих задач, а не от состояния прода)
fake.setdefault("tasks", {})
fake["tasks"]["t-990099-000001"] = {
"id": "t-990099-000001", "title": "разбор /лестница-запроса",
"size": "S", "status": "ready", "ts": "2026-09-30T02:00:00+00:00",
}
fake.setdefault("inbox", {}) fake.setdefault("inbox", {})
# ключи — как у реальных сообщений: aggregate_id = f"m-{update_id}" # ключи — как у реальных сообщений: aggregate_id = f"m-{update_id}"
fake["inbox"]["m-990001"] = { fake["inbox"]["m-990001"] = {

7
~/.ssh/id_ed25519 Normal file
View file

@ -0,0 +1,7 @@
-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
QyNTUxOQAAACAidokdBiOPXDSuTfzeRoYSPjR4fKT2y3jyw4dxHVEj3AAAAJhDEfzrQxH8
6wAAAAtzc2gtZWQyNTUxOQAAACAidokdBiOPXDSuTfzeRoYSPjR4fKT2y3jyw4dxHVEj3A
AAAEC4Y/gtEVJCC2vPDgWHfTuuV2BkJKYJ1NaaxX4bxpuguSJ2iR0GI49cNK5N/N5GhhI+
NHh8pPbLePLDh3EdUSPcAAAAD3NpbWJpb3QtaGFybmVzcwECAwQFBg==
-----END OPENSSH PRIVATE KEY-----

1
~/.ssh/id_ed25519.pub Normal file
View file

@ -0,0 +1 @@
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICJ2iR0GI49cNK5N/N5GhhI+NHh8pPbLePLDh3EdUSPc simbiot-harness