From 58cc3785c6a5e7ca38aab8fd3de0e9c531b32952 Mon Sep 17 00:00:00 2001 From: Simbiot Harness Date: Fri, 9 Oct 2026 16:16:09 +0000 Subject: [PATCH] =?UTF-8?q?t-261009160906:=20=D0=B0=D0=BD=D1=82=D0=B8-?= =?UTF-8?q?=D0=B3=D0=BE=D0=BD=D0=BA=D0=B0=20=D1=83=D1=80=D0=BE=D0=B2=D0=BD?= =?UTF-8?q?=D0=B5=D0=B9=20min/hour=20=D0=BD=D0=B0=20=D0=B2=D1=85=D0=BE?= =?UTF-8?q?=D0=B4=D1=8F=D1=89=D0=B8=D1=85=20TG=20=E2=80=94=20=D0=BF=D1=80?= =?UTF-8?q?=D0=B8=D0=B2=D1=8F=D0=B7=D0=BA=D0=B0=20=D0=B0=D1=80=D1=82=D0=B5?= =?UTF-8?q?=D1=84=D0=B0=D0=BA=D1=82=D0=B0=20=D0=BA=20=D1=81=D0=BE=D0=BE?= =?UTF-8?q?=D0=B1=D1=89=D0=B5=D0=BD=D0=B8=D1=8E=20(-m),=20guard=20=D0=B2?= =?UTF-8?q?=20deliver=5Fartifact,=20reply=5Fartifact=20=D0=B2=20read=20mod?= =?UTF-8?q?el,=20=D1=82=D0=B5=D1=81=D1=82=20=D0=B3=D0=BE=D0=BD=D0=BA=D0=B8?= =?UTF-8?q?=20(8=20PASS)=20+=20=D0=BA=D0=BE=D0=BD=D1=86=D0=B5=D0=BF=D1=82?= =?UTF-8?q?=20=D0=98=D0=98=D0=A8=D0=9D=D0=98=D0=A6=D0=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- artifacts/shkor-beauty-prototype.html | 100 ++++- artifacts/shkor/assistant.py | 51 ++- artifacts/shkor/connector.py | 14 +- artifacts/shkor/gen_legal_pages.py | 250 +++++++++++ artifacts/shkor/orders.json | 158 +++++++ cli.py | 22 +- harness/*.db | 0 harness/commands.py | 24 +- harness/es.py | 9 + out/cil-by-compact-landing.html | 373 +++++++++++++++++ out/iishnitsa-panel-concept-2026-10-09.html | 209 ++++++++++ out/leonid_meeting143.pptx | Bin 0 -> 42411 bytes out/meeting143_leonid_presentation.html | 281 +++++++++++++ out/meeting143_leonid_presentation.md | 57 +++ out/meeting143_leonid_slides.html | 436 ++++++++++++++++++++ out/simbiot_pitch.pptx | Bin 0 -> 36619 bytes out/static-files-index.html | 80 ++++ scripts/make_pptx_261008.py | 133 ++++++ scripts/spa_fill.py | 168 ++++++++ skills/README.md | 1 + skills/compact-landing/SKILL.md | 73 ++++ skills/gcal/SKILL.md | 66 +++ tests/test_msg_artifact_race.py | 111 +++++ tools/gcal.py | 290 +++++++++++++ ~/.config/opencode/opencode.json | 18 +- ~/.config/opencode/opencode.json.bak | 8 + ~/.profile | 2 + 27 files changed, 2912 insertions(+), 22 deletions(-) create mode 100644 artifacts/shkor/gen_legal_pages.py create mode 100644 artifacts/shkor/orders.json create mode 100644 harness/*.db create mode 100644 out/cil-by-compact-landing.html create mode 100644 out/iishnitsa-panel-concept-2026-10-09.html create mode 100644 out/leonid_meeting143.pptx create mode 100644 out/meeting143_leonid_presentation.html create mode 100644 out/meeting143_leonid_presentation.md create mode 100644 out/meeting143_leonid_slides.html create mode 100644 out/simbiot_pitch.pptx create mode 100644 out/static-files-index.html create mode 100644 scripts/make_pptx_261008.py create mode 100644 scripts/spa_fill.py create mode 100644 skills/compact-landing/SKILL.md create mode 100644 skills/gcal/SKILL.md create mode 100644 tests/test_msg_artifact_race.py create mode 100755 tools/gcal.py create mode 100644 ~/.config/opencode/opencode.json.bak create mode 100644 ~/.profile diff --git a/artifacts/shkor-beauty-prototype.html b/artifacts/shkor-beauty-prototype.html index 9b48343..d94d8c2 100644 --- a/artifacts/shkor-beauty-prototype.html +++ b/artifacts/shkor-beauty-prototype.html @@ -40,6 +40,8 @@ .hero h1{font-size:clamp(30px,4.5vw,46px);line-height:1.1;letter-spacing:-.02em;max-width:16ch} .hero p{margin-top:14px;max-width:48ch;color:var(--muted);font-size:17px} .hero .btn{display:inline-block;margin-top:22px;background:var(--acc);color:#fff;padding:13px 26px;border-radius:6px;font-weight:600} + .hero .btn.ghost{background:transparent;color:var(--acc);border:1.5px solid var(--acc);margin-left:10px} + .hero .btn.ghost:hover{background:var(--acc-soft)} .hero .btn:hover{filter:brightness(1.08)} .rhythm{flex:1;min-width:260px;display:flex;align-items:flex-end;gap:4px;height:120px;opacity:.9} .rhythm span{flex:1;background:var(--acc);border-radius:3px 3px 0 0;animation:grow 2.8s ease-in-out infinite} @@ -94,9 +96,25 @@ .dark input:focus{border-color:var(--acc)} .dark button{background:var(--acc);border:none;color:#fff;padding:12px 22px;border-radius:6px;font:inherit;font-weight:600;cursor:pointer} + .plan-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(280px,1fr));gap:20px;margin-top:8px} + .plan-card{background:var(--card);border:1px solid var(--line);border-radius:10px;padding:24px} + .plan-card h3{font-size:20px} + .plan-price{font-family:var(--mono);font-size:12px;letter-spacing:.08em;text-transform:uppercase;color:var(--muted);margin:6px 0 14px} + .plan-card ul{list-style:none;margin:0 0 18px} + .plan-card li{padding:6px 0 6px 22px;position:relative;font-size:14.5px;color:#3d3a34} + .plan-card li:before{content:"✓";position:absolute;left:0;color:var(--acc);font-weight:700} + .plan-card .btn{display:inline-block;background:var(--acc);color:#fff;padding:10px 18px;border-radius:6px;font-weight:600} + .plan-card.featured{border:2px solid var(--acc);box-shadow:0 6px 24px rgba(14,122,95,.12)} + .plan-card code{font-family:var(--mono);font-size:12px;background:var(--acc-soft);color:var(--acc);padding:1px 5px;border-radius:4px} + #reqform{display:grid;grid-template-columns:repeat(auto-fit,minmax(200px,1fr));gap:10px;max-width:760px} + #reqform button{margin-top:4px} + footer{padding:30px 0 40px;color:var(--muted);font-size:13.5px} footer .wrap{display:flex;justify-content:space-between;gap:20px;flex-wrap:wrap} footer .proto{font-family:var(--mono);font-size:12px} + footer .flinks{display:flex;gap:18px;font-size:13.5px} + footer .flinks a{text-decoration:underline;color:var(--muted)} + footer .flinks a:hover{color:var(--acc)} #toast{position:fixed;left:50%;bottom:24px;transform:translate(-50%,80px);background:var(--ink);color:#fff;padding:12px 20px;border-radius:8px;font-size:14px;opacity:0;transition:.25s ease;z-index:9} #toast.show{transform:translate(-50%,0);opacity:1} @@ -113,7 +131,7 @@ -
Прототип витрины — шаг 1 из плана Shkor beauty. Наполнение демонстрационное, оплата не подключена.
+
Витрина работает: AI-ассистент подбирает уход, заявки принимаются (Lite-план). Оплата онлайн — на шаге 2.
@@ -143,9 +162,9 @@
-

Корейский уход — с полки до двери за 1 день

-

Каталог проверенных K-beauty брендов в Минске: очищение, тонизирование, сыворотки, SPF. Самовывоз и доставка по Беларуси.

- Смотреть каталог +

Корейский уход — AI подберёт, привезём за 1 день

+

Каталог проверенных K-beauty брендов в Минске + AI-ассистент: разберёт состояние кожи, соберёт рутину и примет заявку. Доставка по Беларуси.

+ Смотреть каталог Оставить заявку
+
+
+

Магазинам корейской косметики

+

Shkor beauty — это AI-ассистент витрины (B2B): подбор ухода, заказы и заявки — единое API для вашего сайта и любых агентов. Оператор сервиса — Shkor Labs LLC.

+
+
+

Lite

+

базовый план

+
    +
  • Витрина с каталогом и брендами
  • +
  • Форма заявки → общая база заказов
  • +
  • Заявки приходят с пометкой plan=lite
  • +
  • Договор с магазином лично: телефон/мессенджер
  • +
+ Начать с заявки +
+ +
+
+
+ +
+
+

Оставить заявку

+

Расскажите, что нужно — подберём уход или подключим магазин. Заявка пишется в общую базу заказов, ответим в течение дня.

+
+ + + + +
+

+
+
+

Подписка на поступления

@@ -334,7 +398,13 @@
-
Shkor beauty — витрина корейской косметики. Прототип шага 1: каталог без платежей.
+
Shkor beauty — витрина корейской косметики + AI-ассистент. Оператор сервиса: Shkor Labs LLC (Wyoming, USA) · alex.shkor@gmail.com
+
собрано харнесом Simbiot · кооператив 42 · cil.by
@@ -347,7 +417,25 @@ function toast(t){ clearTimeout(el._t);el._t=setTimeout(function(){el.classList.remove('show')},2200); } document.querySelectorAll('.p button').forEach(function(b){ - b.addEventListener('click',function(){toast('Прототип: заказ и оплата будут на шаге 2')}); + b.addEventListener('click',function(){ + var card=b.closest('.pb'), h=card?card.querySelector('h3'):null; + var note=document.getElementById('reqform').note; + if(h&¬e) note.value='Интересует: '+h.textContent; + document.getElementById('request').scrollIntoView({behavior:'smooth'}); + toast('Добавлено в заявку — оставьте контакты ниже'); + }); +}); +document.getElementById('reqform').addEventListener('submit',function(e){ + e.preventDefault(); + var f=e.target, st=document.getElementById('reqstatus'); + st.textContent='Отправляем…'; + fetch('/api/call',{method:'POST',headers:{'Content-Type':'application/json'}, + body:JSON.stringify({name:'submit_request',arguments:{name:f.name.value.trim(),phone:f.phone.value.trim(),note:f.note.value.trim()}}) + }).then(function(r){return r.json()}).then(function(d){ + var res=d.result||d; + if(res.order_id){st.textContent='Заявка принята: '+res.order_id+'. Свяжемся в течение дня.';f.reset();} + else{st.textContent='Не получилось: '+(res.error||JSON.stringify(res));} + }).catch(function(err){st.textContent='Сеть недоступна — напишите на alex.shkor@gmail.com';}); }); diff --git a/artifacts/shkor/assistant.py b/artifacts/shkor/assistant.py index 3defe05..c415a12 100644 --- a/artifacts/shkor/assistant.py +++ b/artifacts/shkor/assistant.py @@ -255,6 +255,8 @@ def place_order(args: dict) -> dict: oid = "SB-" + uuid.uuid4().hex[:8].upper() order = { + "order_id": oid, + "plan": args.get("plan") if args.get("plan") in ("full", "lite") else "full", "order_id": oid, "items": lines, "subtotal": round(total, 2), @@ -283,9 +285,47 @@ def check_order(args: dict) -> dict: o = _load_orders().get(oid) if not o: return {"error": f"заказ {oid} не найден", "known": list(_load_orders())[:10]} - return {"order_id": oid, "status": o["status"], "payment": o["payment"]["status"], - "total": o["total"], "delivery": o["delivery"]["label"], "eta": o["delivery"]["eta"], - "items": [{"title": l["title"], "qty": l["qty"]} for l in o["items"]]} + out = {"order_id": oid, "plan": o.get("plan", "full"), "status": o["status"], + "payment": (o.get("payment") or {}).get("status")} + if o.get("kind") == "request": + out.update({"kind": "request", "note": o.get("note", ""), + "products": o.get("products", []), + "customer": o.get("customer", {})}) + return out + out.update({"total": o["total"], "delivery": o["delivery"]["label"], "eta": o["delivery"]["eta"], + "items": [{"title": l["title"], "qty": l["qty"]} for l in o["items"]]}) + return out + + +def submit_request(args: dict) -> dict: + """Заявка из витрины Lite-плана: без корзины/оплаты, пишем в ту же базу orders.json с plan=lite. + + Витрина формы («Хочу это», «Подобрать рутину») → request SB-R-XXXXXXXX → менеджер/агент + договаривается лично. Когда магазин переходит на Full — та же запись дооформляется + в заказ (check_order видит статус request). + """ + contact = (args.get("phone") or args.get("email") or "").strip() + if not contact and not (args.get("name") or "").strip(): + return {"error": "нужен хотя бы один контакт: phone или email (или name)"} + oid = "SB-R-" + uuid.uuid4().hex[:8].upper() + rec = { + "order_id": oid, + "plan": "lite", + "kind": "request", + "note": (args.get("note") or "").strip(), + "products": args.get("products") or [], + "customer": {"name": args.get("name") or "", "phone": args.get("phone") or "", + "email": args.get("email") or "", "address": args.get("address") or ""}, + "payment": {"status": "none", "method": "none", "pay_link": None}, + "total": 0.0, + "status": "request", + "created": time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime()), + } + orders = _load_orders() + orders[oid] = rec + _save_orders(orders) + return {"order_id": oid, "status": "request", "plan": "lite", + "next": "Магазин свяжется по контакту. Статус: tools/call check_order {\"order_id\": \"%s\"}" % oid} def delivery_options(args: dict) -> dict: @@ -339,7 +379,8 @@ TOOLS = { ["self_report?", "symptoms?", "photo_report?", "age?", "climate?"]), "recommend": ("Подбор продуктов: concerns+skin_type+budget_max → карточки", ["concerns?", "skin_type?", "budget_max?", "limit?"]), "search": ("Поиск по каталогу", ["query?", "category?", "limit?"]), - "place_order": ("Оформить заказ", ["items[{id,qty}]", "delivery?", "name?", "phone?", "address?", "pay_method?"]), + "place_order": ("Оформить заказ (Full-план)", ["items[{id,qty}]", "delivery?", "name?", "phone?", "address?", "pay_method?", "plan?"]), + "submit_request": ("Заявка из витрины Lite-плана: без корзины/оплаты → orders.json plan=lite, статус request", ["phone?", "email?", "name?", "note?", "products?"]), "check_order": ("Статус заказа и доставки", ["order_id"]), "delivery_options": ("Варианты доставки и цены", ["cart_total?"]), "usage_instructions": ("Инструкции по применению + порядок рутины", ["product_ids"]), @@ -365,7 +406,7 @@ def handle(request: dict) -> dict: if name not in TOOLS: return {"error": f"неизвестный инструмент {name!r}", "available": list(TOOLS)} fn = {"analyze_skin": analyze_skin, "recommend": recommend, "search": search, - "place_order": place_order, "check_order": check_order, + "place_order": place_order, "submit_request": submit_request, "check_order": check_order, "delivery_options": delivery_options, "usage_instructions": usage_instructions}[name] try: result = fn(arguments if isinstance(arguments, dict) else {}) diff --git a/artifacts/shkor/connector.py b/artifacts/shkor/connector.py index 7b7480c..25c839e 100644 --- a/artifacts/shkor/connector.py +++ b/artifacts/shkor/connector.py @@ -58,12 +58,24 @@ def serve_http(addr: str) -> None: self.send_response(code) self.send_header("Content-Type", "application/json; charset=utf-8") self.send_header("Content-Length", str(len(body))) + self.send_header("Access-Control-Allow-Origin", "*") # витрина на другом домене: форма заявки → /call + self.send_header("Access-Control-Allow-Methods", "GET, POST, OPTIONS") + self.send_header("Access-Control-Allow-Headers", "Content-Type") self.end_headers() self.wfile.write(body) + def do_OPTIONS(self) -> None: # preflight для формы витрины (fetch с другого origin) + self.send_response(204) + self.send_header("Access-Control-Allow-Origin", "*") + self.send_header("Access-Control-Allow-Methods", "GET, POST, OPTIONS") + self.send_header("Access-Control-Allow-Headers", "Content-Type") + self.send_header("Content-Length", "0") + self.end_headers() + def do_GET(self) -> None: # health / открыть в браузере if self.path in ("/", "/health"): - self._send(200, {"ok": True, "service": "shkor-assistant", "version": 1}) + self._send(200, {"ok": True, "service": "shkor-assistant", "version": 1, + "tools": "POST /tools/list", "call": "POST /call"}) else: self._send(404, {"error": "not found", "routes": ["POST /tools/list", "POST /call"]}) diff --git a/artifacts/shkor/gen_legal_pages.py b/artifacts/shkor/gen_legal_pages.py new file mode 100644 index 0000000..bfcc7bd --- /dev/null +++ b/artifacts/shkor/gen_legal_pages.py @@ -0,0 +1,250 @@ +#!/usr/bin/env python3 +"""Генератор юр-страниц Shkor beauty (t-261008154151-faa550). + +Privacy Policy / Terms of Service / Cookie & Disclaimer / About & Contacts. +Единый шаблон = тот же визуальный язык, что у витрины. Оператор сервиса: +Shkor Labs LLC (Wyoming, USA), alex.shkor@gmail.com. +""" +from pathlib import Path + +OUT = Path("/var/www/beauty") +OUT.mkdir(parents=True, exist_ok=True) + +CSS = """ + :root{--ink:#1c1a17;--paper:#faf7f2;--line:#e4ddd2;--acc:#0e7a5f;--muted:#6f6a61; + --sans:"Segoe UI",-apple-system,Arial,sans-serif;--mono:ui-monospace,Menlo,Consolas,monospace} + *{box-sizing:border-box;margin:0;padding:0} + body{background:var(--paper);color:var(--ink);font-family:var(--sans);line-height:1.65;font-size:16px} + .wrap{max-width:820px;margin:0 auto;padding:0 20px} + header{border-bottom:1px solid var(--line);background:#fff} + .hbar{display:flex;align-items:center;justify-content:space-between;padding:16px 0} + .logo{font-size:20px;font-weight:700} + .logo span{color:var(--acc)} + .logo small{display:block;font-family:var(--mono);font-size:10px;letter-spacing:.14em;text-transform:uppercase;color:var(--muted)} + nav a{margin-left:16px;font-size:14px;color:var(--muted)} + nav a:hover{color:var(--acc)} + main{padding:40px 0 60px} + h1{font-size:clamp(26px,4vw,36px);letter-spacing:-.02em;margin-bottom:6px} + .meta{font-family:var(--mono);font-size:12px;color:var(--muted);margin-bottom:28px} + h2{font-size:19px;margin:28px 0 10px} + p,li{color:#3d3a34;margin-bottom:10px} + ul{padding-left:22px} + a{color:var(--acc)} + .card{background:#fff;border:1px solid var(--line);border-radius:10px;padding:22px;margin-top:24px} + footer{padding:26px 0 40px;border-top:1px solid var(--line);color:var(--muted);font-size:13.5px} + footer .links{display:flex;gap:18px;margin-bottom:8px} + footer .links a{text-decoration:underline;color:var(--muted)} + footer .proto{font-family:var(--mono);font-size:12px;margin-top:6px} +""" + + +def page(slug: str, title: str, updated: str, body: str) -> None: + html = f""" + + + + +{title} — Shkor beauty + + + + +
+ +
+
+

{title}

+
Last updated: {updated} · v1.0 · Operator: Shkor Labs LLC (Wyoming, USA)
+ {body} +
+ + +""" + (OUT / f"{slug}.html").write_text(html, encoding="utf-8") + print("ok", OUT / f"{slug}.html") + + +D = "2026-10-08" + +page("privacy", "Privacy Policy", D, """ +

Настоящая Политика конфиденциальности объясняет, какие данные собирает сервис Shkor beauty +(далее — «Сервис») и как они используются. Оператор Сервиса — Shkor Labs LLC, компания, +зарегистрированная в штате Вайоминг, США (далее — «Оператор»). Контакт: alex.shkor@gmail.com.

+ +

1. Какие данные мы собираем

+
    +
  • Заявка (Lite-план): имя, телефон и текст заявки, которые вы сами указываете в форме «Оставить заявку».
  • +
  • Заказ (Full-план): имя, телефон, адрес доставки и состав заказа.
  • +
  • Обращения: письма на email Оператора и переписка в мессенджерах — в объёме, необходимом для ответа.
  • +
  • Технические данные: анонимная статистика посещений (страницы-рефереры, устройство, страна) — без персональных идентификаторов.
  • +
+ +

2. Чего мы НЕ собираем

+
    +
  • Данные банковских карт: при онлайн-оплате (Full-план) платёж проводит независимый платёжный агент; + данные карты вводятся на его защищённой странице и Оператору не передаются.
  • +
  • Фотографии кожи без явного согласия: если вы передаёте фотоотчёт AI-ассистенту, он обрабатывается + для анализа состояния кожи и не публикуется и не передаётся третьим лицам.
  • +
+ +

3. Зачем мы используем данные

+
    +
  • связаться с вами по заявке и довести её до результата (подбор, заказ, доставка);
  • +
  • исполнить заказ и уведомлять о его статусе;
  • +
  • улучшать каталог и подбор ассистента на обезличенной статистике.
  • +
+ +

4. Хранение и срок

+

Записи заявок и заказов хранятся в защищённой базе Оператора не дольше, чем это необходимо для +исполнения заказа и требований бухгалтерии (по умолчанию — до 3 лет), после чего удаляются или +обезличиваются.

+ +

5. Передача третьим лицам

+

Данные передаются только: (а) службе доставки — имя, телефон и адрес вашего заказа; +(б) платёжному агенту — минимально необходимый платёжный контекст; (в) в случаях, предусмотренных законом.

+ +

6. Cookie

+

Сервис использует минимальные cookie и локальное хранилище: сессия корзины и анонимная статистика. +Подробнее — в Disclaimer.

+ +

7. Ваши права

+

Вы можете запросить доступ к своим данным, их исправление или удаление письмом на +alex.shkor@gmail.com. Мы отвечаем в течение 30 дней.

+ +

8. Изменения

+

Актуальная версия Политики всегда доступна на этой странице; существенные изменения датируются выше.

+""") + +page("terms", "Terms of Service", D, """ +

Условия использования сервиса Shkor beauty (далее — «Условия»). Оператор Сервиса — Shkor Labs LLC +(Wyoming, USA), контакт alex.shkor@gmail.com. Используя Сервис, +вы соглашаетесь с настоящими Условиями.

+ +

1. Что такое Сервис

+

Shkor beauty — витрина корейской косметики с AI-ассистентом: подбор ухода, заявки и заказы с доставкой +по Минску и Беларуси. Сервис предлагается в двух планах:

+
    +
  • Lite (базовый): витрина + форма заявки. Заявка фиксируется в общей базе заказов, после чего + магазин связывается с вами лично для уточнения ассортимента, цены и способа расчёта.
  • +
  • Full: полный каталог, корзина, онлайн-оплата и статусы доставки.
  • +
+ +

2. Заявки и заказы

+
    +
  • Заявка (Lite) не является подтверждённой сделкой: цена и наличие подтверждаются при обратной связи.
  • +
  • Заказ (Full) считается принятым после подтверждения оплаты или согласия на оплату при получении.
  • +
  • Цены указаны в белорусских рублях (BYN) и могут меняться; актуальная цена — на витрине в момент заказа.
  • +
+ +

3. Доставка

+

Курьер по Минску, Европочта по Беларуси и самовывоз. Сроки и стоимость показываются при оформлении +заявки/заказа; бесплатная доставка — от 80 BYN по каталогу витрины.

+ +

4. Возврат и обмен

+
    +
  • Товар надлежащего качества (без следов использования, в оригинальной упаковке) — возврат/обмен + в течение 14 дней с момента получения.
  • +
  • Средства гигиены и косметика с нарушенной упаковкой возврату не подлежат.
  • +
  • Для возврата напишите на alex.shkor@gmail.com с номером заказа.
  • +
+ +

5. Интеллектуальная собственность

+

Материалы витрины, AI-подбор и программный код ассистента принадлежат Оператору. Товарные знаки +брендов принадлежат их правообладателям.

+ +

6. Ограничение ответственности

+

Сервис предоставляется «как есть». AI-подбор средств — рекомендательный характер и не является +медицинской консультацией (см. Disclaimer). Оператор не несёт +ответственности за индивидуальные реакции кожи и решения, принятые на основе рекомендаций без +ознакомления с составами и инструкциями.

+ +

7. Применимое право

+

Настоящие Условия регулируются правом, применимым к месту регистрации Оператора (Wyoming, USA), +без ущерба для обязательных потребительских прав покупателей по законодательству места доставки.

+""") + +page("disclaimer", "Cookie & Disclaimer", D, """ +

Страница объединяет политику cookie и важные дисклеймеры сервиса Shkor beauty. Оператор — +Shkor Labs LLC (Wyoming, USA), alex.shkor@gmail.com.

+ +

1. Не медицинская консультация

+
+

AI-ассистент и материалы витрины НЕ являются медицинской консультацией, диагностикой или лечением. +Подбор средств — эвристическая рекомендация на основе вашего самоотчёта. Она не заменяет консультацию +дерматолога или врача. При заболеваниях кожи, беременности, приёме лекарственных средств и выраженных +реакциях — проконсультируйтесь со специалистом до применения средств.

+
+ +

2. Косметика и индивидуальные реакции

+
    +
  • Перед первым применением сделайте тест на чувствительность (небольшой участок кожи, 24–48 часов).
  • +
  • Активы (ретинол, кислоты, витамин C) вводите постепенно; ретинол — только на ночь, SPF утром обязателен.
  • +
  • Ознакомьтесь с составом на упаковке: при аллергии на компонент средство не используйте.
  • +
+ +

3. Cookie

+
    +
  • Необходимые: сессия корзины и защита форм. Не отключаются.
  • +
  • Аналитические: анонимная статистика посещений для улучшения витрины. Без персональных идентификаторов и без передачи рекламным сетям.
  • +
  • Рекламных и трекинговых cookie сторонних сетей — нет.
  • +
+

Большинство браузеров позволяет управлять cookie в настройках; отключение необходимых cookie +может сломать корзину и отправку заявки.

+ +

4. Внешние ссылки

+

Витрина может ссылаться на сайты брендов и службы доставки — за их содержание Оператор +ответственности не несёт.

+ +

5. Вопросы

+

Любые вопросы по дисклеймерам и cookie — alex.shkor@gmail.com.

+""") + +page("about", "About & Contacts", D, """ +

Shkor beauty — AI-ассистент для магазинов корейской косметики: витрина, которая сама +разбирает состояние кожи клиента, собирает рутину и доводит заявку до заказа.

+ +

Кто мы

+

Оператор сервиса — Shkor Labs LLC, компания, зарегистрированная в штате Вайоминг, США. +Проект развивается совместно с инженерным кооперативом «42» (cil.by): витрина и ассистент собраны +ритмичным агентом Simbiot.

+ +

Что мы делаем

+
    +
  • Витрина K-beauty: каталог проверенных брендов — Anua, Beauty of Joseon, JMsolution, + SKIN627, Biodance и другие, доставка по Минску и Беларуси.
  • +
  • AI beauty-ассистент (B2B): агентный API analyze → recommend → order, который любой + магазин или AI-агент может подключить к своему сайту (MCP/STDIO, HTTP, CLI).
  • +
  • Два плана подключения: Lite — витрина + заявки в общую базу; Full — полный + каталог, корзина, онлайн-оплата и статусы доставки.
  • +
+ +

Контакты

+
+ +
+ +

Магазинам: подключение ассистента

+

Ассистент подключается к любому сайту или AI-агенту за один шаг: JSONL поверх STDIO (MCP-стиль), +HTTP JSON API или разовый CLI-вызов. Каталог масштабируется без правок кода. Для пилота напишите +на email — подключим витрину с формой заявок (Lite) и покажем e2e-сценарий заказа.

+""") diff --git a/artifacts/shkor/orders.json b/artifacts/shkor/orders.json new file mode 100644 index 0000000..4669e51 --- /dev/null +++ b/artifacts/shkor/orders.json @@ -0,0 +1,158 @@ +{ + "SB-R-1D8C99C8": { + "order_id": "SB-R-1D8C99C8", + "plan": "lite", + "kind": "request", + "note": "Хочу сыворотку для сухой кожи", + "products": [ + "p01" + ], + "customer": { + "name": "Тест Тестов", + "phone": "+375291234567", + "email": "", + "address": "" + }, + "payment": { + "status": "none", + "method": "none", + "pay_link": null + }, + "total": 0.0, + "status": "request", + "created": "2026-10-08T16:03:25Z" + }, + "SB-55B78647": { + "order_id": "SB-55B78647", + "plan": "full", + "items": [ + { + "id": "p05", + "title": "Успокаивающий тонер с сердечником Centella Toner 77%", + "qty": 1, + "price": 62.0, + "sum": 62.0 + } + ], + "subtotal": 62.0, + "delivery": { + "key": "minsk_courier", + "label": "Курьер по Минску", + "eta": "сегодня/завтра", + "price": 7.0, + "free_from": 80.0, + "charged": 7.0 + }, + "total": 69.0, + "customer": { + "name": "", + "phone": "", + "address": "" + }, + "payment": { + "status": "pending", + "method": "cash_on_delivery", + "pay_link": null + }, + "status": "placed", + "created": "2026-10-08T16:03:25Z" + }, + "SB-8AC083A5": { + "order_id": "SB-8AC083A5", + "plan": "lite", + "items": [ + { + "id": "p03", + "title": "Солнцезащитный гель Relief Sun SPF50+ PA++++", + "qty": 1, + "price": 49.0, + "sum": 49.0 + } + ], + "subtotal": 49.0, + "delivery": { + "key": "minsk_courier", + "label": "Курьер по Минску", + "eta": "сегодня/завтра", + "price": 7.0, + "free_from": 80.0, + "charged": 7.0 + }, + "total": 56.0, + "customer": { + "name": "", + "phone": "", + "address": "" + }, + "payment": { + "status": "pending", + "method": "cash_on_delivery", + "pay_link": null + }, + "status": "placed", + "created": "2026-10-08T16:03:25Z" + }, + "SB-R-7B23D4D8": { + "order_id": "SB-R-7B23D4D8", + "plan": "lite", + "kind": "request", + "note": "Проверка формы с витрины (e2e)", + "products": [], + "customer": { + "name": "E2E Тест", + "phone": "+375290000000", + "email": "", + "address": "" + }, + "payment": { + "status": "none", + "method": "none", + "pay_link": null + }, + "total": 0.0, + "status": "request", + "created": "2026-10-08T16:08:02Z" + }, + "SB-B352DCBF": { + "order_id": "SB-B352DCBF", + "plan": "full", + "items": [ + { + "id": "p05", + "title": "Успокаивающий тонер с сердечником Centella Toner 77%", + "qty": 1, + "price": 62.0, + "sum": 62.0 + }, + { + "id": "p03", + "title": "Солнцезащитный гель Relief Sun SPF50+ PA++++", + "qty": 1, + "price": 49.0, + "sum": 49.0 + } + ], + "subtotal": 111.0, + "delivery": { + "key": "minsk_courier", + "label": "Курьер по Минску", + "eta": "сегодня/завтра", + "price": 7.0, + "free_from": 80.0, + "charged": 0.0 + }, + "total": 111.0, + "customer": { + "name": "Тест", + "phone": "+375XX0000000", + "address": "" + }, + "payment": { + "status": "pending", + "method": "cash_on_delivery", + "pay_link": null + }, + "status": "placed", + "created": "2026-10-08T16:08:11Z" + } +} \ No newline at end of file diff --git a/cli.py b/cli.py index 3229f8d..c2033ca 100644 --- a/cli.py +++ b/cli.py @@ -90,6 +90,9 @@ def main(): help="ключ идемпотентности (по умолчанию file:<абсолютный путь>)") p_dfl.add_argument("--force", action="store_true", help="отправить даже если уже доставлен (повторная доставка вручную)") + p_dfl.add_argument("-m", "--msg", dest="msg", default=None, + help="связать доставку с входящим m-NNN (анти-гонка уровней, " + "t-261009160906): если на него уже отвечено иным артефактом — SKIP") p_pro = sub.add_parser("proactive", help="инициативная реплика: ритм/тема/отправка (t-260930073419)") p_pro.add_argument("--force", action="store_true", help="отправить сейчас, игнорируя окно/интервал/тишину") @@ -361,11 +364,28 @@ def main(): print(f"FAIL: файл не найден: {args.file}") sys.exit(1) aid = args.key or f"file:{os.path.abspath(args.file)}" + # Анти-гонка уровней (t-261009160906): если доставка привязана к + # входящему m-NNN и на него уже отвечено ДРУГИМ артефактом — + # не отправляем второй файл на тот же запрос (гонка min/hour). + if args.msg and not args.force: + model = es.run_projections() + m = (model.get("inbox") or {}).get(args.msg) + if m is not None and m.get("replied") and m.get("reply_artifact") not in (None, "", aid): + es.append_event("harness", aid, "DeliverySkipped", { + "artifact_id": aid, "kind": "file", + "reason": "msg-already-answered-by-other-artifact", + "msg": args.msg, + "reply_artifact": m.get("reply_artifact"), + }) + print(f"SKIP: на {args.msg} уже отвечено другим артефактом " + f"({m.get('reply_artifact')}) — используй --force для повтора") + sys.exit(0) def _send(): return tg_inbox.send_document_to_chat(args.file, caption=args.caption) ok = commands.deliver_artifact(aid, kind="file", send=_send, owner="cli:tg-deliver-file", - caption=args.caption, force=args.force) + caption=args.caption, force=args.force, + msg=args.msg) print(f"OK: файл доставлен ({aid})" if ok else f"SKIP: уже доставлен ({aid}) — используй --force для повтора") elif args.cmd == "proactive": diff --git a/harness/*.db b/harness/*.db new file mode 100644 index 0000000..e69de29 diff --git a/harness/commands.py b/harness/commands.py index fcc04fa..d87eed6 100644 --- a/harness/commands.py +++ b/harness/commands.py @@ -351,7 +351,7 @@ def _delivery_text(task: dict, answer: str) -> str: def deliver_artifact(artifact_id: str, kind: str, send, owner: str = "manual", - caption: str = "", force: bool = False) -> bool: + caption: str = "", force: bool = False, msg: str = None) -> bool: """Идемпотентная доставка ПРОИЗВОЛЬНОГО артефакта в чат (t-260930083937). Рут-кейс дублей: мин-тик шёл файлом «скинь сюда» curl'ом sendDocument — @@ -373,6 +373,21 @@ def deliver_artifact(artifact_id: str, kind: str, send, owner: str = "manual", "reason": "already-delivered", }) return False + # Анти-гонка уровней (t-261009160906): артефакт привязан к входящему + # m-NNN, на которое уже отвечено ДРУГИМ артефактом → SKIP. Два уровня + # (min/hour) на один запрос не должны слать два разных файла; dedup по + # artifact_id=file:<путь> их не различает (разные пути = разные ключи). + if msg and not force: + model = es.run_projections() + m = (model.get("inbox") or {}).get(msg) + if (m is not None and m.get("replied") + and m.get("reply_artifact") not in (None, "", artifact_id)): + es.append_event("harness", artifact_id, "DeliverySkipped", { + "artifact_id": artifact_id, "kind": kind, + "reason": "msg-already-answered-by-other-artifact", + "msg": msg, "reply_artifact": m.get("reply_artifact"), + }) + return False if force: # ручной повтор — явное решение человека: лок недавней доставки # не должен блокировать его на TTL (120с). Снимаем свой же захват @@ -394,10 +409,13 @@ def deliver_artifact(artifact_id: str, kind: str, send, owner: str = "manual", # в ledger остаётся фактом; повторный force просто отправит ещё раз, # но ledger при этом не «молодеет» (kind/ts не переписываются) es.record_delivery(artifact_id, kind=kind) - es.append_event("harness", artifact_id, "ArtifactDelivered", { + ev = { "artifact_id": artifact_id, "kind": kind, "tg_ok": True, "caption": (caption or "")[:500], "owner": owner, - }) + } + if msg: + ev["msg"] = msg # анти-гонка уровней (t-261009160906) + es.append_event("harness", artifact_id, "ArtifactDelivered", ev) return True diff --git a/harness/es.py b/harness/es.py index 97e99d6..aaa93ed 100644 --- a/harness/es.py +++ b/harness/es.py @@ -548,6 +548,15 @@ def _apply_event(model: dict, ev: dict): if m: m["replied"] = True m["reply_text"] = data.get("text", "") + elif etype == "ArtifactDelivered": + # Анти-гонка уровней (t-261009160906): связка «файл → входящее». + # Если доставка артефакта помечена msg=m-NNN, фиксируем её в + # инбоксе — второй артефакт на то же входящее будет SKIP'нут. + msg = (data or {}).get("msg") if isinstance(data, dict) else None + if msg: + m = model.setdefault("inbox", {}).get(msg) + if m is not None and not m.get("reply_artifact"): + m["reply_artifact"] = data.get("artifact_id") elif etype == "MessageSeen": # индикация взятия в работу (e-261002202857): флаг seen → reaction 👀 # ставится ровно один раз, повторные маркеры сообщений не найдут diff --git a/out/cil-by-compact-landing.html b/out/cil-by-compact-landing.html new file mode 100644 index 0000000..ba8b215 --- /dev/null +++ b/out/cil-by-compact-landing.html @@ -0,0 +1,373 @@ + + + + + +Simbiot — The Studio OS + + + + + + + + + + + +
+ Simbiot + Sign in + Get started +
+ +
+ + +
+ +
+

Studio OS · live

+

Hire nobody.
Ship everything.

+

A dedicated AI team — engineers, video producers, content makers — plus the operating system that runs them: projects, treasury, decisions, meetings. White-label, under your brand.

+
+ Start your studio — $999/mo + Live demo + flat rate · cancel anytime +
+ +
+
+ + studio ops · task ladder + 3 agents online +
+
+
4 done
tasks this cycle
+
4-cadence
reflex→day runtime
+
S·M·L
task sizes
+
+
+
hour digest 23:10 Minsk · all actions signed · click a row to re-dispatch
+
+
+ + +
+
01

A staff of specialists, not a chatbot.

dedicated servers
+

Every agent runs on its own server with private memory, tools, email and Matrix chat. You brief them like employees — they onboard onto your idea and start shipping.

+
+
+ /01 +
CodyEngineer
+
Writes code, deploys services, maintains git repositories. MVPs, landing pages, bots, APIs, integrations — from brief to production.
+
full-stack · devops · git
+
+
+ /02 +
ReelsUpVideo producer
+
Cuts long videos and podcasts into vertical clips for Reels and Shorts. Transcription, highlight detection, automated editing.
+
whisper · clips · shorts
+
+
+ /03 +
RoyContent maker
+
Promotional content on a conveyor: reels with 3D characters, scripts, voiceovers, visuals — brand-consistent by default.
+
3d · tts · scripts
+
+
+
+ + +
+
02

Agents build. The OS manages.

your domain
+

Projects, money, decisions and meetings in one transparent system — under your studio's name.

+
+
S.01
Project registry
A card for every venture: decisions, ownership, agents, activity feed.
+
S.02
Git repo per project
Code and docs in a dedicated repository with a web editor. Nothing lost in chats.
+
S.03
Treasury & wallets
Revenue share flows in automatically. Every payout transparent and auditable.
+
S.04
Decisions & voting
Allocate resources by vote. Cycles, quorum, full decision history.
+
S.05
Meetings with AI minutes
Video calls with recording, transcription, summaries — into the registry.
+
S.06
Time & task tracking
What every agent completed and where work went — a daily digest in your inbox.
+
+
+ + +
+
03

Full stack. Nothing extra to buy.

flat rate
+
+
✓
Stripe payments live
Card payments for what your agents build — subscriptions, one-offs, invoices. A working checkout on day one.
+
✓
AI inference included flat
All models run on our gateway — no API keys, no vendor bills, no metered surprises.
+
✓
White-label portal your brand
Your subdomain, your studio's name up front. Every deliverable ships under your identity.
+
✓
The agent harness 4-cadence
Reflex, minute, hour, day — escalation, memory, semantic knowledge base. Coop-tested daily.
+
✓
One-click export no lock-in
Take everything with you, anytime. Ready in ~10 minutes after payment.
+
+
+ + +
+
04

One plan each. Everything included.

founding cohort
+
+
+
Personal · first come
+
$99.9 / month
+
    +
  • 1 dedicated AI agent on its own server
  • +
  • Private Matrix chat — web + mobile
  • +
  • Inference, hosting, updates included
  • +
  • One-click export, no lock-in
  • +
+ Get my agent +
secure checkout by Stripe · cancel anytime
+
+
+
Studio
+
$999 / month
+
    +
  • 12 AI agents — engineers, producers, content
  • +
  • White-label portal on your subdomain
  • +
  • Projects, treasury, decisions, meetings — one system
  • +
  • Team members free · priority support
  • +
+ Start your studio +
deployed the same day · self-hosted? talk to us
+
+
+
+ + +
+
+ + + + diff --git a/out/iishnitsa-panel-concept-2026-10-09.html b/out/iishnitsa-panel-concept-2026-10-09.html new file mode 100644 index 0000000..102088f --- /dev/null +++ b/out/iishnitsa-panel-concept-2026-10-09.html @@ -0,0 +1,209 @@ + + + + + +ИИШНИЦА · Панельная сессия «Общее видение» — концепт кооператива 42 + + + +
+ +
+
ИИШНИЦА · Панельная сессия · Концепт
+

Общее видение

+

Формат панельной сессии на ИИШНИЦЕ: миссия 42 задаёт рамку, спикеры из разных индустрий описывают будущее — и панель сходится к названию общего видения.

+
+ 90 минут + 5–6 спикеров + 3 круга + зал + Миссия 42 + Минск · cil.by +
+
+ +
+
01 / ИДЕЯ
+

Суть формата

+

Не классическая панель с вопросами модератора, а три круга с одной целью: собрать из отдельных картин будущего — общую. Миссия 42 озвучивается в начале как рамка, объединяющая любые индустрии и любые ответы.

+
+

Миссия «42»: все миссии одинаково важны. Миссия «42» объединяет абсолютно все возможные миссии. Нет неважных миссий — все люди важны, у каждого своя миссия.

+
— кооператив «Друзья Коллективного Разума», Минск · cil.by
+
+

Почему это работает: каждый спикер говорит о своей индустрии — и потому не конкурирует с соседом за «правильный ответ». А третий круг, название общего видения, превращает панель из набора мнений в один артефакт, который остаётся после сессии: в заголовках, фото и цитатах.

+
+ +
+
02 / ФОРМАТ
+

Три круга + зал

+

Каждый круг имеет свою функцию: раскрыть, связать, назвать. Тайминг дан для 6 спикеров; при 5 спикерах сессия ужимается до ~80 минут.

+ +
+
+
00:00–00:05
+
Открытие и миссия 42Модератор (или Alex) озвучивает миссию 42 как рамку сессии: все миссии одинаково важны — потому каждый взгляд на будущее важен.
+
+
+
00:05–00:29
+
Круг 1 — «Моё будущее» · 4 мин × 6Каждый спикер описывает будущее своей индустрии в самом дальнем горизонте, в который он может заглянуть. Без вопросов, без перебиваний. 24 минуты.
+
+
+
00:29–00:41
+
Круг 2 — «Что понравилось» · 2 мин × 6Каждый подсвечивает, что понравилось у других спикеров. Не спорить, а связывать: где он увидел пересечение со своим будущим. 12 минут.
+
+
+
00:41–00:55
+
Круг 3 — «Название общего видения»Панель соглашается назвать общее видение — одно название для всех картин будущего. Модератор помогает сформулировать и фиксирует финальную формулировку. 14 минут.
+
+
+
00:55–01:25
+
Вопросы из залаЗал спрашивает — спикеры отвечают. Модератор держит рамку миссии: любой вопрос важен, любая индустрия равна. 30 минут.
+
+
+
01:25–01:30
+
ЗакрытиеМодератор повторяет название общего видения и миссию 42. Короткое резюме — для фото и записи.
+
+
+ +

Роли

+
+
+
МОДЕРАТОР
+

Держит рамку и тайминг

+

Озвучивает миссию, следит за 4/2 минутами, ведёт панель к названию общего видения. Жёсткий тайм-кипер: «осталось 30 секунд».

+
+
+
СПИКЕРЫ
+

5–6 человек из разных индустрий

+

Три задания: будущее своей индустрии (4 мин), что понравилось у других (2 мин), название общего видения. Без презентаций — только голос.

+
+
+
ЗАЛ
+

Четвёртый участник

+

Вопросы после трёх кругов. В круге 3 модератор может спросить зал: какое название общего видения предложили бы вы?

+
+
+ +
+ Правило тайминга +

Карточки 60/30/10 секунд в руках тайм-кипера, звуковой сигнал на переходах между кругами. Если спикер не уложился в 4 минуты — модератор мягко забирает слово: формулировки доскажет во втором круге.

+
+
+ +
+
03 / ПОЧЕМУ ЭТО СРАБОТАЕТ
+

Что делает формат сильным

+
+
+
01
+

Круг 1 без перебиваний

+

4 минуты каждому без вопросов — редкость на панелях. Спикеры дают глубокие ответы, а не заготовки. Глубина = запоминаемость.

+
+
+
02
+

Круг 2 связывает

+

«Что понравилось» вместо «в чём не согласен» — позитивная рамка: панель выглядит единой, а не спорящей. Зал видит созвучие индустрий.

+
+
+
03
+

Круг 3 — артефакт

+

Название общего видения — то, что остаётся после сессии: в заголовках, фото, цитатах. Панель производит смысл, а не обмен мнениями.

+
+
+
04
+

Миссия 42 как рамка

+

«Все миссии одинаково важны» легитимирует любой ответ и любую индустрию — от ритейла до робототехники. Спикерам безопасно говорить смело.

+
+
+
+ +
+
04 / НАЗВАНИЕ ОБЩЕГО ВИДЕНИЯ
+

Заготовки для третьего круга

+

Третий круг должен закончиться названием. Если панель не сойдётся сама, модератор предлагает шаблоны — панель выбирает или дорабатывает.

+
ЗАГОТОВКА 01
«Симбиоз» — будущее, где ИИ усиливает каждую миссию человека, не заменяя её.
+
ЗАГОТОВКА 02
«Коллективный разум» — каждый умножает свой вклад через общую инфраструктуру.
+
ЗАГОТОВКА 03
«42» — число, которое объединяет все миссии.
+
+ +
+
05 / ЧЕК-ЛИСТ ОРГАНИЗАТОРА
+

Что нужно до сессии

+
    +
  • Состав панели: 5–6 спикеров из максимально разных индустрий (финансы, медицина, промышленность, образование, медиа, госсектор). Разные горизонты видения — от прагматиков до футуристов.
  • +
  • Брифинг спикеров за 2–3 дня: каждому объяснить три круга, тайминг 4/2 минуты и установку «загляни в самый дальний горизонт, в который можешь». Никаких презентаций.
  • +
  • Модератор: умеет жёстко держать тайминг и мягко вести к названию. Репетиция открытия с миссией 42.
  • +
  • Тайм-кипер: карточки 60/30/10 секунд, звуковой сигнал на переходах между кругами.
  • +
  • Зал: микрофоны для вопросов, очередь или карточки для сбора вопросов.
  • +
  • Запись: видео обязательно — название общего видения и лучшие цитаты круга 2 идут в посты и клипы после сессии.
  • +
  • Баннер миссии: слайд или растяжка с текстом миссии 42 на сцене — рамка сессии видна на любой фотографии.
  • +
+
+ +
+
06 / СВЯЗЬ С КООПЕРАТИВОМ
+

Что это даёт 42

+

Панель — не только контент, но и вход в экосистему кооператива для людей с миссиями.

+
    +
  • Поток проектов: спикеры и зал видят формат работы кооператива — люди с миссиями приходят в 42, регистрируют проекты, усиливают долгосрочную AUM.
  • +
  • Точка входа: портал cil.by упоминается в открытии и закрытии — для тех, кто захочет глубже после сессии.
  • +
  • Бренд: миссия 42 на сцене — фирменный приём кооператива, отличающий сессию от других панелей конференции.
  • +
+
+ +
+ Концепт панельной сессии «Общее видение» · ИИШНИЦА · кооператив «Друзья Коллективного Разума» · Минск · cil.by
+ Черновик для обсуждения v0.1 · simbiot-harness / hour · 2026-10-09 +
+ +
+ + diff --git a/out/leonid_meeting143.pptx b/out/leonid_meeting143.pptx new file mode 100644 index 0000000000000000000000000000000000000000..7b620d121f65b5d23eb2ff6c980d7a5178208762 GIT binary patch literal 42411 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]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 + + + + + + Разговор с Леонидом — выжимка (встреча 143) + + + +
+

Разговор с Леонидом — выжимка (встреча 143)

+
+

Разговор +Алекса с Леонидом — выжимка и договорённости

+

Встреча 143 · 2026-10-08 · ~2.5 часа · Jitsi + агент-визуализатор +Simbiot

+
+

Слайд 1 — Кто есть кто

+
    +
  • Леонид — давний знакомый студии, начинающий +писатель, инвестор в deep-tech (в т.ч. Anti-Aging Hero с физиком Петром +Федичевым). Владеет DGX Spark 128GB (куплен за €5000), гоняет на нём +CUDA-моделирование графов надёжности.
  • +
  • Алекс — показал студийную инфраструктуру: +агент-визуализатор звонков (демо в реальном времени), собственный роутер +LM-провайдеров, RAG по всем знаниям кооператива.
  • +
  • Демо произвело впечатление: «1-й раз такое в моей практике… польза +очевидна».
  • +
+

Слайд 2 — Демо +инфраструктуры студии

+
    +
  • Агент подключается к любому звонку, рисует слайды по ходу разговора +(задержка ~45 сек — лимит генерации).
  • +
  • Роутер сам выбирает LM под контекст (визуализация сейчас — GLM 5.3 +Flash).
  • +
  • RAG: «он знает всё, что мы делаем» — доступ к общей базе знаний +кооператива.
  • +
  • Леонид получил полезный инсайт уже из демо: на его домашней +коробочке можно крутить быструю генерацию музыки/картинок/мини-видео — +трек за минуту.
  • +
+

Слайд +3 — Главный контакт: пилот «мультик по рассказу Леонида»

+
    +
  • Леонид хочет мульт-адаптации своих рассказов (10–15 мин); готов +финансово поддержать пилот: токены — точно, +человеческий труд — обсудить. Ориентир бюджета «в пределах 1000» на +эксперимент.
  • +
  • Вход от студии: рассказ Леонида + свобода интерпретации, «никаких +пожеланий-ограничений».
  • +
  • Alex предложил: партнёр студии уже строит harness для +генерации мультиков/клипов/читов — матч идеальный. Влад +(профессиональный видеомейкер + озвучка) возьмёт видео-часть.
  • +
  • Первый шаг — аудиоспектакль (дешевле и предсказуемее), затем +видео.
  • +
+

Слайд +4 — Творческий метод Леонида (поучительно для продуктовой линзы)

+
    +
  • История с хакатоном ~2018: обучали нейросеть на сольных песнях +Леннона/Маккартни и классифицировали битловские — «десяток песен +правильно заклассифицировали».
  • +
  • Пишет с ИИ (своей оценки ~80% — его творчество, ~20% ИИ), честно +маркирует «made with AI» на after.today.
  • +
  • Бэкграунд-тезис: «литературные негры» и мастерская Микеланджело — +ИИ-ассистированное творчество не ново, нова честность.
  • +
+

Слайд 5 — +Экономика локального инференса (спор о железе)

+
    +
  • Леонид: DGX Spark за ~1% стоимости дома — нормальная цена за +«домашний интеллект»; тренд — коробки станут достоянием домов.
  • +
  • Алекс: облачный always-on — ок для большинства задач; топовые модели +для разработки софта всё равно выигрывают у локальных.
  • +
  • Сошлись: гибрид — локально быстрая генерация (эскизы, музыка), +облако — для тяжёлых продуктовых задач. У студии в планах собственный +inference-сервер (инференс в ~6 раз быстрее).
  • +
+

Слайд 6 — Обмен +тезисами: как строить AI-компанию

+
    +
  • Леонид: «Vision — говно, execution — всё» (шутка из +EPAM). Из короткого промпта не может вырасти сложная система: 10 людей с +одним промптом построят 10 одинаковых Uber’ов — вариабельность требует +доп. информации.
  • +
  • Алекс: ядро студии — AI-ядро, постоянно +перестраивающее паттерны (64 сейчас) при смене контекста — «долгосрочно +самый большой актив».
  • +
  • Стратегия: строить бизнес на нейро-инфраструктуре с самого начала → +потом переводить на автопилот; незаменим только «CEO-решатель».
  • +
  • Леонид о студии: «я теперь понимаю, чем твоя студия отличается от +классической — изюминка в AI-ядре».
  • +
  • Методология Леонида для deep-tech: начинать продавать ценность сразу +(консалтинг целевого клиента), не влюбляясь в R&D.
  • +
+

Слайд 7 — Договорённости +(что фиксируем)

+
    +
  1. Пилот анимации по рассказу Леонида — Leonid +покрывает токены (ориентир до 1000), студия делает (аудио → видео, ведёт +Влад). Alex вернётся к Леониду с конкретным предложением.
  2. +
  3. Формат разговора продолжается — Леонид открыт к +следующим сессиям.
  4. +
  5. По голосам/долям за прошлые инвестиции — Леонид +отказался («это была шутка», «инвестирую из философских +соображений, не создавайте ожиданий»). Вопрос закрыт, давления нет.
  6. +
  7. Леонид ещё посмотрит презентацию студии (35-минутную) и вернётся с +фидбэком.
  8. +
+

Слайд 8 — Что студии делать +дальше

+
    +
  • Подготовить оффер по мульт-пилоту (объём, этап аудиоспектакля, смета +токенов) — Alex озвучит Леониду предметно.
  • +
  • Подключить партнёрский harness генерации мультиков к задаче Леонида +как первый клиентский кейс.
  • +
  • Зафиксировать кейс «агент-визуализатор на реальном внешнем звонке» — +первое демо на живом стороннем человеке, положительный фидбэк.
  • +
+ + diff --git a/out/meeting143_leonid_presentation.md b/out/meeting143_leonid_presentation.md new file mode 100644 index 0000000..da5d602 --- /dev/null +++ b/out/meeting143_leonid_presentation.md @@ -0,0 +1,57 @@ +# Разговор Алекса с Леонидом — выжимка и договорённости +*Встреча 143 · 2026-10-08 · ~2.5 часа · Jitsi + агент-визуализатор Simbiot* + +--- + +## Слайд 1 — Кто есть кто + +- **Леонид** — давний знакомый студии, начинающий писатель, инвестор в deep-tech (в т.ч. Anti-Aging Hero с физиком Петром Федичевым). Владеет DGX Spark 128GB (куплен за €5000), гоняет на нём CUDA-моделирование графов надёжности. +- **Алекс** — показал студийную инфраструктуру: агент-визуализатор звонков (демо в реальном времени), собственный роутер LM-провайдеров, RAG по всем знаниям кооператива. +- Демо произвело впечатление: «1-й раз такое в моей практике… польза очевидна». + +## Слайд 2 — Демо инфраструктуры студии + +- Агент подключается к любому звонку, рисует слайды по ходу разговора (задержка ~45 сек — лимит генерации). +- Роутер сам выбирает LM под контекст (визуализация сейчас — GLM 5.3 Flash). +- RAG: «он знает всё, что мы делаем» — доступ к общей базе знаний кооператива. +- Леонид получил полезный инсайт уже из демо: на его домашней коробочке можно крутить быструю генерацию музыки/картинок/мини-видео — трек за минуту. + +## Слайд 3 — Главный контакт: пилот «мультик по рассказу Леонида» + +- Леонид хочет мульт-адаптации своих рассказов (10–15 мин); готов **финансово поддержать** пилот: токены — точно, человеческий труд — обсудить. Ориентир бюджета «в пределах 1000» на эксперимент. +- Вход от студии: рассказ Леонида + свобода интерпретации, «никаких пожеланий-ограничений». +- Alex предложил: **партнёр студии уже строит harness для генерации мультиков/клипов/читов** — матч идеальный. Влад (профессиональный видеомейкер + озвучка) возьмёт видео-часть. +- Первый шаг — аудиоспектакль (дешевле и предсказуемее), затем видео. + +## Слайд 4 — Творческий метод Леонида (поучительно для продуктовой линзы) + +- История с хакатоном ~2018: обучали нейросеть на сольных песнях Леннона/Маккартни и классифицировали битловские — «десяток песен правильно заклассифицировали». +- Пишет с ИИ (своей оценки ~80% — его творчество, ~20% ИИ), честно маркирует «made with AI» на after.today. +- Бэкграунд-тезис: «литературные негры» и мастерская Микеланджело — ИИ-ассистированное творчество не ново, нова честность. + +## Слайд 5 — Экономика локального инференса (спор о железе) + +- Леонид: DGX Spark за ~1% стоимости дома — нормальная цена за «домашний интеллект»; тренд — коробки станут достоянием домов. +- Алекс: облачный always-on — ок для большинства задач; топовые модели для разработки софта всё равно выигрывают у локальных. +- Сошлись: гибрид — локально быстрая генерация (эскизы, музыка), облако — для тяжёлых продуктовых задач. У студии в планах собственный inference-сервер (инференс в ~6 раз быстрее). + +## Слайд 6 — Обмен тезисами: как строить AI-компанию + +- **Леонид:** «Vision — говно, execution — всё» (шутка из EPAM). Из короткого промпта не может вырасти сложная система: 10 людей с одним промптом построят 10 одинаковых Uber'ов — вариабельность требует доп. информации. +- **Алекс:** ядро студии — AI-ядро, постоянно перестраивающее паттерны (64 сейчас) при смене контекста — «долгосрочно самый большой актив». +- Стратегия: строить бизнес на нейро-инфраструктуре с самого начала → потом переводить на автопилот; незаменим только «CEO-решатель». +- Леонид о студии: «я теперь понимаю, чем твоя студия отличается от классической — изюминка в AI-ядре». +- Методология Леонида для deep-tech: начинать продавать ценность сразу (консалтинг целевого клиента), не влюбляясь в R&D. + +## Слайд 7 — Договорённости (что фиксируем) + +1. **Пилот анимации по рассказу Леонида** — Leonid покрывает токены (ориентир до 1000), студия делает (аудио → видео, ведёт Влад). Alex вернётся к Леониду с конкретным предложением. +2. **Формат разговора продолжается** — Леонид открыт к следующим сессиям. +3. **По голосам/долям за прошлые инвестиции — Леонид отказался** («это была шутка», «инвестирую из философских соображений, не создавайте ожиданий»). Вопрос закрыт, давления нет. +4. Леонид ещё посмотрит презентацию студии (35-минутную) и вернётся с фидбэком. + +## Слайд 8 — Что студии делать дальше + +- Подготовить оффер по мульт-пилоту (объём, этап аудиоспектакля, смета токенов) — Alex озвучит Леониду предметно. +- Подключить партнёрский harness генерации мультиков к задаче Леонида как первый клиентский кейс. +- Зафиксировать кейс «агент-визуализатор на реальном внешнем звонке» — первое демо на живом стороннем человеке, положительный фидбэк. diff --git a/out/meeting143_leonid_slides.html b/out/meeting143_leonid_slides.html new file mode 100644 index 0000000..86add67 --- /dev/null +++ b/out/meeting143_leonid_slides.html @@ -0,0 +1,436 @@ + + + + + +Встреча 143 · Алекс × Леонид — выжимка и договорённости + + + +
+
Simbiot · Кооператив 42
+ +
+ + +
+
143
+
Встреча 143 · 08.10.2026 · Jitsi + агент-визуализатор Simbiot
+

Разговор
Алекса × Леонида

+

Выжимка двухсполовинойчасовой встречи: инфраструктура студии, пилот «мультик по рассказу», экономика локального инференса — и договорённости, которые фиксируем.

+
+ ~2.5 часа разговора + 8 тем + 4 договорённости + слайды рисовал агент live +
+
+ + +
+
01
+
Кто есть кто
+

Два мира на одном звонке

+
+
+
Леонид
+

Писатель · инвестор в deep-tech

+

Давний знакомый студии. Инвестирует в deep-tech, в том числе Anti-Aging Hero с физиком Петром Федичевым. Дома — DGX Spark 128GB (€5000), на которой он гоняет CUDA-моделирование графов надёжности.

+
+
+
Алекс · студия
+

Живая AI-инфраструктура

+

Показал, чем студия отличается от классической: агент-визуализатор звонков (демо в реальном времени), собственный роутер LM-провайдеров и RAG по всем знаниям кооператива.

+
+
+
+

«Первый раз такое в моей практике… польза очевидна»

+ Леонид — после демо агент-визуализатора +
+
+ + +
+
02
+
Демо инфраструктуры
+

Что увидел Леонид в прямом эфире

+
    +
  • Агент подключается к любому звонку и рисует слайды по ходу разговора — задержка ~45 секунд (лимит генерации)
  • +
  • Роутер сам выбирает модель под контекст — визуализация идёт на GLM 5.3 Flash
  • +
  • RAG по базе кооператива: «он знает всё, что мы делаем» — агент отвечает со знанием всей истории студии
  • +
  • Инсайт Леонида из демо: на его домашней коробочке можно крутить быструю генерацию — музыку, картинки, мини-видео. Трек за минуту
  • +
+
+ + +
+
03
+
Главный контакт встречи
+

Пилот: мультик по рассказу Леонида

+
+
+
Что хочет Леонид
+

Мульт-адаптация своих рассказов (10–15 минут). Готов финансово поддержать пилот: токены — точно, человеческий труд — обсудить. Ориентир бюджета — «в пределах 1000» на эксперимент.

+
+
+
Что входит студия
+

Рассказ Леонида + свобода интерпретации («никаких пожеланий-ограничений»). Со стороны студии — партнёр уже строит harness генерации мультиков, видео-часть ведёт Влад (профессиональный видеомейкер + озвучка). Матч идеальный.

+
+
+
+

Первый шаг — аудиоспектакль: дешевле и предсказуемее. Затем видео.

+ Дорожная карта пилота +
+
+ + +
+
04
+
Творческий метод Леонида
+

Поучительно для продуктовой линзы

+
    +
  • Хакатон ~2018: обучали нейросеть на сольных песнях Леннона и Маккартни, затем классифицировали битловские — «десяток песен правильно заклассифицировали»
  • +
  • Пишет с ИИ: по собственной оценке ~80% — его творчество, ~20% — ИИ. Честно маркирует «made with AI» на after.today
  • +
  • Бэкграунд-тезис: «литературные негры» и мастерская Микеланджело — ИИ-ассистированное творчество не ново. Нова честность.
  • +
+
+

ИИ-ассистированное творчество не ново — нова честность.

+ Леонид +
+
+ + +
+
05
+
Экономика локального инференса
+

Спор о железе: дом против облака

+
+
+
Позиция Леонида
+

DGX Spark за ~1% стоимости дома — нормальная цена за «домашний интеллект». Тренд: коробки станут достоянием домов.

+
+
⚡
+
+
Позиция Алекса
+

Облачный always-on — ок для большинства задач. Топовые модели для разработки софта всё равно выигрывают у локальных.

+
+
+
+

Сошлись на гибриде: локально — быстрая генерация (эскизы, музыка), облако — тяжёлые продуктовые задачи. У студии в планах собственный inference-сервер — в ~6 раз быстрее.

+ Консенсус встречи +
+
+ + +
+
06
+
Обмен тезисами
+

Как строить AI-компанию

+
+
+
Тезис Леонида
+

«Vision — говно, execution — всё» (шутка из EPAM). Из короткого промпта не вырастает сложная система: 10 людей с одним промптом построят 10 одинаковых Uber'ов — вариабельность требует дополнительной информации.

+
+
+
Тезис Алекса
+

Ядро студии — AI-ядро, постоянно перестраивающее паттерны (64 сейчас) при смене контекста. «Долгосрочно — самый большой актив». Строить бизнес на нейро-инфраструктуре с самого начала → затем автопилот. Незаменим только «CEO-решатель».

+
+
+
+

«Я теперь понимаю, чем твоя студия отличается от классической — изюминка в AI-ядре»

+ Леонид · метод deep-tech: продавать ценность сразу, не влюбляясь в R&D +
+
+ + +
+
07
+
Итоги встречи
+

Договорённости — что фиксируем

+
1
+

Пилот анимации по рассказу Леонида

+

Леонид покрывает токены (ориентир до 1000), студия делает: аудио → видео, ведёт Влад. Алекс вернётся с конкретным предложением.

+
+
2
+

Формат разговора продолжается

+

Леонид открыт к следующим сессиям со студией.

+
+
3
+

Голоса и доли за прошлые инвестиции — вопрос закрыт

+

Леонид отказался: «это была шутка», «инвестирую из философских соображений, не создавайте ожиданий». Давления нет.

+
+
4
+

Следующий шаг Леонида

+

Посмотрит 35-минутную презентацию студии и вернётся с фидбэком.

+
+
+ + +
+
08
+
Action items
+

Что студии делать дальше

+
    +
  • Подготовить оффер по мульт-пилоту — объём, этап аудиоспектакля, смета токенов. Алекс озвучит Леониду предметно.
  • +
  • Подключить партнёрский harness генерации мультиков к задаче Леонида — как первый клиентский кейс.
  • +
  • Зафиксировать кейс «агент-визуализатор на реальном внешнем звонке» — первое демо на живом стороннем человеке, положительный фидбэк.
  • +
+
+

От живого демо на звонке — к первому платному пилоту по генерации контента.

+ Смысл встречи 143 +
+
+ + +
+
42
+
Встреча 143 · 08.10.2026
+
«Изюминка — в AI-ядре»
+

Simbiot · Кооператив 42 · агент-визуализатор, LM-роутер, RAG по знаниям студии. Презентация собрана из выжимки разговора — тем же агентом, что рисовал слайды live.

+
← → навигация · F — во весь экран · P — печать/PDF
+
+ +
+ +
+
+
+ + + +
+
← → · свайп · F — фулскрин
+ + + + diff --git a/out/simbiot_pitch.pptx b/out/simbiot_pitch.pptx new file mode 100644 index 0000000000000000000000000000000000000000..a698ac76bdacd2ba3304e0557a9058d2dffe36da GIT binary patch literal 36619 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 O&+Esh3J>qUzWP6r%r$KQ literal 0 HcmV?d00001 diff --git a/out/static-files-index.html b/out/static-files-index.html new file mode 100644 index 0000000..02db4e0 --- /dev/null +++ b/out/static-files-index.html @@ -0,0 +1,80 @@ + + + + +Simbiot — индекс статических файлов + + + +

📁 Индекс статических файлов кооператива 42 / Simbiot

+

Все HTML/PNG/PDF-файлы репозитория, сгруппированные по категориям. 31 файл.

+ +

🌍 Лендинги и презентации

+ + + + + + + + + +
docs/simbiot-landing-v2.htmlЛендинг Simbiot v233 КБ
docs/simbiot-landing-v2-live.htmlЛендинг v2 (live-версия)30 КБ
docs/simbiot-landing-dashboard.htmlЛендинг + дашборд20 КБ
docs/simbiot_presale_landing.htmlПресейл-лендинг17 КБ
artifacts/landing-simbiot.htmlЛендинг (артefакт)11 КБ
artifacts/landing-simbiot-opencode.htmlЛендинг (OpenCode)20 КБ
simbiot-presentation.htmlПрезентация Simbiot22 КБ
architecture.htmlАрхитектура системы40 КБ
+ +

👤 Порталы и UI-клиенты

+ + + + + + +
out/cil-by-compact-landing.htmlПортал CIL by Compact23 КБ
out/ui-cil-by-compact-landing.htmlUI-версия портала CIL22 КБ
out/simbiot_ui_presale.htmlПресейл UI12 КБ
harness/roy.htmlПортал Roy3 КБ
harness/dashboard.htmlДашборд харнеса40 КБ
+ +

🗓 Встречи и материалы к переговорам

+ + + + + + +
out/meeting143_leonid_presentation.htmlВстреча 143 — презентация для Леонида13 КБ
out/meeting143_leonid_slides.htmlВстреча 143 — слайды25 КБ
docs/yulia-call-136-proposal.htmlЗвонок 136 — предложение Юлии6 КБ
docs/yulia-call-136-recap.htmlЗвонок 136 — рекап6 КБ
docs/nadezhda-call-recap.htmlЗвонок с Надеждой — рекап10 КБ
+ +

📄 Документы и договоры

+ + + + + + +
Studio_Participation_Agreement_2026-09.pdfСоглашение об участии в студии (PDF)17 КБ
lab42-conditions.htmlУсловия Lab4215 КБ
artifacts/studio-conditions-2026-10.htmlУсловия студии 2026-106 КБ
artifacts/rental-x-simbiot-agreement.htmlСоглашение Rental×Simbiot16 КБ
artifacts/rental-x-simbiot-side-letter-15k.htmlСайд-леттер Rental×Simbiot 15k10 КБ
+ +

🛠 Продуктовые планы и гайды

+ + + + +
docs/ai-mascots-product-plan.htmlПродуктовый план AI-маскотов16 КБ
docs/fpga-neuralnet-guide.htmlГайд: нейросети на FPGA9 КБ
docs/self-diagnostics.htmlСамодиагностика8 КБ
+ +

🎨 Прототипы

+ + +
artifacts/shkor-beauty-prototype.htmlПрототип Shkor Beauty25 КБ
+ +

🖼 Изображения

+ + + + + +
artifacts/imagegen-e2e-20261003.pngE2E-тест генерации изображений977 КБ
artifacts/dashboard-channels-t-d8112b.pngСкриншот дашборда (каналы)138 КБ
artifacts/dashboard-channels-t-d8112b-full.pngСкриншот дашборда (полный)145 КБ
out/9router-gemini-test.pngТест 9router/Gemini1 КБ
+ + + diff --git a/scripts/make_pptx_261008.py b/scripts/make_pptx_261008.py new file mode 100644 index 0000000..526bb6a --- /dev/null +++ b/scripts/make_pptx_261008.py @@ -0,0 +1,133 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +"""Генерация PPTX: (1) разговор с Леонидом, (2) питч Simbiot (Seedance).""" +from pptx import Presentation +from pptx.util import Inches, Pt, Emu +from pptx.dml.color import RGBColor +from pptx.enum.text import PP_ALIGN + +BG = RGBColor(0x0A, 0x0C, 0x12) +ACCENT = RGBColor(0x8B, 0x5C, 0xF6) +ACCENT2 = RGBColor(0x7C, 0x6C, 0xF5) +TEXT = RGBColor(0xF0, 0xEE, 0xFF) +MUT = RGBColor(0x9A, 0x97, 0xB8) + +def add_slide(prs, title, bullets, kicker=None): + s = prs.slides.add_slide(prs.slide_layouts[6]) + s.background.fill.solid(); s.background.fill.fore_color.rgb = BG + w, h = prs.slide_width, prs.slide_height + # accent bar + bar = s.shapes.add_shape(1, Emu(0), Emu(0), Emu(w), Inches(0.09)) + bar.fill.solid(); bar.fill.fore_color.rgb = ACCENT; bar.line.fill.background() + if kicker: + tb = s.shapes.add_textbox(Inches(0.7), Inches(0.45), Inches(11.8), Inches(0.4)) + tf = tb.text_frame; p = tf.paragraphs[0]; r = p.add_run(); r.text = kicker.upper() + r.font.size = Pt(12); r.font.color.rgb = ACCENT2; r.font.bold = True + tb = s.shapes.add_textbox(Inches(0.7), Inches(0.85), Inches(12.0), Inches(1.0)) + tf = tb.text_frame; p = tf.paragraphs[0]; r = p.add_run(); r.text = title + r.font.size = Pt(30); r.font.color.rgb = TEXT; r.font.bold = True + tb = s.shapes.add_textbox(Inches(0.75), Inches(1.85), Inches(11.9), Inches(4.9)) + tf = tb.text_frame; tf.word_wrap = True + first = True + for b in bullets: + lvl = 0 + if isinstance(b, tuple): b, lvl = b + p = tf.paragraphs[0] if first else tf.add_paragraph(); first = False + r = p.add_run(); r.text = ("• " if lvl == 0 else "– ") + b + r.font.size = Pt(16 if lvl == 0 else 14); r.font.color.rgb = TEXT if lvl == 0 else MUT + p.space_after = Pt(8) + return s + +prs = Presentation(); prs.slide_width = Inches(13.333); prs.slide_height = Inches(7.5) + +K = "Встреча 143 · 2026-10-08" +add_slide(prs, "Разговор Алекса с Леонидом", [ + "Выжимка разговора и договорённости · ~2.5 часа", + "Jitsi + агент-визуализатор Simbiot (слайды рисуются в реальном времени)", + "Кооператив 42 / студия Simbiot", +], kicker=K) +add_slide(prs, "Кто есть кто", [ + "Леонид — давний знакомый студии, писатель, инвестор в deep-tech (Anti-Aging Hero с физиком Петром Федичевым)", + "DGX Spark 128GB (€5000) — дома гоняет CUDA-моделирование графов надёжности", + "Алекс — инфраструктура студии: агент-визуализатор, собственный роутер LM, RAG по знаниям кооператива", + ("«1-й раз такое в моей практике… польза очевидна» — Леонид о демо", 1), +], kicker=K) +add_slide(prs, "Демо инфраструктуры студии", [ + "Агент подключается к любому звонку и рисует слайды по ходу разговора (~45 сек на слайд)", + "Роутер сам выбирает LM под контекст (визуализация — GLM 5.3 Flash)", + "RAG: «он знает всё, что мы делаем» — общая база знаний кооператива", + ("Инсайт Леонида: домашняя коробочка → быстрая генерация музыки/картинок/мини-видео", 1), +], kicker=K) +add_slide(prs, "Пилот: мультик по рассказу Леонида", [ + "Мульт-адаптация рассказов (10–15 мин); Леонид готов финансово поддержать пилот", + "Ориентир бюджета — «в пределах 1000» на эксперимент (токены — точно, труд — обсудить)", + "Вход от студии: рассказ + свобода интерпретации, без пожеланий-ограничений", + "Партнёр уже строит harness генерации мультиков — матч идеальный; видео-часть ведёт Влад", + ("Первый шаг — аудиоспектакль (дешевле и предсказуемее), затем видео", 1), +], kicker=K) +add_slide(prs, "Творческий метод Леонида", [ + "Хакатон ~2018: нейросеть на сольных песнях Леннона/Маккартни классифицировала битловские", + "Пишет с ИИ (~80% своё / ~20% ИИ), честно маркирует «made with AI» на after.today", + ("«Литературные негры» и мастерская Микеланджело — ИИ-ассистированное творчество не ново, нова честность", 1), +], kicker=K) +add_slide(prs, "Экономика локального инференса", [ + "Леонид: DGX Spark — нормальная цена за «домашний интеллект»; коробки станут достоянием домов", + "Алекс: облачный always-on — ок для большинства; топ-модели для разработки всё равно выигрывают", + ("Сошлись на гибриде: локально — быстрая генерация, облако — тяжёлые продуктовые задачи", 1), + ("У студии в планах свой inference-сервер (в ~6 раз быстрее)", 1), +], kicker=K) +add_slide(prs, "Как строить AI-компанию", [ + "Леонид: «Vision — говно, execution — всё». Из короткого промпта не вырастет сложная система", + "Алекс: ядро студии — AI-ядро, перестраивающее паттерны (64) при смене контекста — главный долгосрочный актив", + "Стратегия: бизнес на нейро-инфраструктуре с начала → перевод на автопилот; незаменим только «CEO-решатель»", + ("«Изюминка студии — в AI-ядре» — Леонид", 1), + ("Методология deep-tech: продавать ценность сразу, не влюбляясь в R&D", 1), +], kicker=K) +add_slide(prs, "Договорённости", [ + "1. Пилот анимации по рассказу Леонида: токены покрывает Леонид (до ~1000), ведёт Влад; Alex вернётся с конкретным предложением", + "2. Формат разговора продолжается — Леонид открыт к следующим сессиям", + "3. Голоса/доли за прошлые инвестиции — Леонид отказался: «инвестирую из философских соображений, не создавайте ожиданий»", + "4. Леонид посмотрит 35-минутную презентацию студии и вернётся с фидбэком", +], kicker=K) +add_slide(prs, "Что делать студии дальше", [ + "Подготовить оффер по мульт-пилоту: объём, этап аудиоспектакля, смета токенов", + "Подключить партнёрский harness генерации мультиков — Леонид как первый клиентский кейс", + "Зафиксировать кейс «агент-визуализатор на внешнем звонке» — первое живое демо, положительный фидбэк", +], kicker=K) +prs.save("out/leonid_meeting143.pptx") + +# --- Pitch deck: Simbiot / Seedance --- +prs2 = Presentation(); prs2.slide_width = Inches(13.333); prs2.slide_height = Inches(7.5) +K2 = "Simbiot · питч" +add_slide(prs2, "Simbiot — общий харнес организации", [ + "Стартап за неделю получает не одного ассистента, а команду ИИ-агентов:", + "один отвечает клиентам, второй пишет код, третий следит за деньгами и метриками", + "Слой поверх Hermes, Claude Code и других — вместо зоопарка агентов и харнесов", +], kicker=K2) +add_slide(prs2, "Проблема", [ + "Фаундер собирает зоопарк из агентов и харнесов вручную", + "Ушёл разработчик — ушла вся магия: знания и автоматизации не переживают людей", + "Каждый новый инструмент — ещё один остров, не связанный с остальными", +], kicker=K2) +add_slide(prs2, "Решение", [ + "Simbiot координирует флот агентов и фиксирует решения компании", + "Удачные приёмы копятся как «гены» — остаются только те, что приносят прибыль", + "Компания превращается в самообучающуюся нейро-инфраструктуру", +], kicker=K2) +add_slide(prs2, "Тракшен", [ + "Уже гоняется на семи собственных продуктах студии", + "AI-ядро: 64 паттерна, перестраивающихся при смене контекста", + "Агент-визуализатор, RAG по знаниям, собственный LM-роутер — в продакшене", +], kicker=K2) +add_slide(prs2, "Видео (Seedance), 3 шота", [ + "Shot 1 «зоопарк в хаосе»: тёмная серверная, хаотичные голограммы и глитчи (0–10 сек)", + "Shot 2 «orchestration»: хаос сходится в фиолетовую сферу, панели выстраиваются в сетку (10–20 сек)", + "Shot 3 «финал»: фаундер у стены дашбордов с растущими revenue-графиками, отъезд в город (20–30 сек)", + ("Стиль: dark cinematic, #0a0c12 + violet #7c6cf5, film grain, 4k, 16:9", 1), +], kicker=K2) +add_slide(prs2, "Строй компанию — а не зоопарк ботов", [ + "Simbiot · Кооператив 42 · simbiot", + "Хронометраж питча: ~35 сек (15 сек — первый и последний абзац)", +], kicker=K2) +prs2.save("out/simbiot_pitch.pptx") +print("OK") diff --git a/scripts/spa_fill.py b/scripts/spa_fill.py new file mode 100644 index 0000000..69518d4 --- /dev/null +++ b/scripts/spa_fill.py @@ -0,0 +1,168 @@ +#!/usr/bin/env python3 +# ═══════════════════════════════════════════════════════════ +# SPA filler — Studio_Participation_Agreement_2026-09 → per-founder +# Задачи t-261008143050 (Siarhei Zhukovich, когорта с 16-го) и +# t-261008144131 (7 участников 42 Labs за 2026-09). +# Паттерн подстановки — как _contract_docx_bytes в /root/coop/app.py. +# ═══════════════════════════════════════════════════════════ +import os, sys, copy +import docx + +TEMPLATE = "/root/coop/legal/Studio_Participation_Agreement_2026-09.docx" +OUT = "/root/coop/legal" + +DRAFT_LINE = "DRAFT TEMPLATE — for review by U.S. counsel before first use. Not legal advice." + +# Перепривязка платежного графика на 16-е число (когорты с 16-го, t-261008143050) +SIXTEENTH = [ + ("no later than the 1st day of the first month of participation", + "no later than the 16th day of the first month of participation"), + ("each subsequent Monthly Fee no later than the 1st day of the corresponding month of participation", + "each subsequent Monthly Fee no later than the 16th day of the corresponding month of participation"), + ("If the Agreement is signed after the 1st, the Program Start is the 1st day of the next calendar month", + "If the Agreement is signed after the 16th, the Program Start is the 16th day of the next calendar month"), + ("for Monthly Fee tranches, the 1st day of the participation month", + "for Monthly Fee tranches, the 16th day of the participation month"), + ("takes effect on the 1st day of the next participation month", + "takes effect on the 16th day of the next participation month"), + ("payable in advance by the 1st of each participation month", + "payable in advance by the 16th of each participation month"), + ("the 1st day of the month of signature if signed on the 1st, otherwise the 1st day of the next calendar month", + "the 16th day of the month of signature if signed on the 16th, otherwise the 16th day of the next calendar month"), +] + + +def founder_replacements(f): + """f: dict(name, passport, pid, address, email, project, one_line, rev_share, + effective, cohort, program_start, sixteenth: bool)""" + r = { + "[DATE]": f["effective"], + "[FULL LEGAL NAME]": f["name"], + "[NAME]": f["name"], + "[EMAIL]": f.get("email") or "[EMAIL]", + "cohort [MONTH YEAR]": f"cohort {f['cohort']}", + "1 [MONTH] [YEAR]": f["program_start"], + "[MONTH YEAR]": f["cohort"], + } + if f.get("rev_share"): + r["[X]"] = str(f["rev_share"]) + if f.get("project"): + r["[PROJECT NAME]"] = f["project"] + if f.get("one_line"): + r["[ONE LINE]"] = f["one_line"] + # Строка Founder: имя + статус + паспорт + адрес одной подстановкой + line_old = "[FULL LEGAL NAME], an individual / [ENTITY TYPE], residing at [ADDRESS], [COUNTRY]" + line_new = (f"{f['name']}, an individual, passport of the Republic of Belarus " + f"{f.get('passport') or '[PASSPORT NO.]'} (personal identification No. " + f"{f.get('pid') or '[PERSONAL ID NO.]'}), residing at " + f"{f.get('address') or '[ADDRESS]'}, Republic of Belarus") + r[line_old] = line_new + # фолбэк, если строка разбита рант differently + r["an individual / [ENTITY TYPE]"] = "an individual" + # паспорт/ид.номер вставляются после схлопывания individual-строки + # (порядок ключей dict важен: этот ключ применяется после предыдущего) + pass_line = (f"passport of the Republic of Belarus {f.get('passport') or '[PASSPORT NO.]'}" + f" (personal identification No. {f.get('pid') or '[PERSONAL ID NO.]'}), residing at") + r["an individual, residing at"] = f"an individual, {pass_line}" + r["[ADDRESS], [COUNTRY]"] = f"{f.get('address') or '[ADDRESS]'}, Republic of Belarus" + r["[ADDRESS]"] = f.get("address") or "[ADDRESS]" + r["[COUNTRY]"] = "Republic of Belarus" + return r + + +def fill(f, out_docx): + doc = docx.Document(TEMPLATE) + reps = founder_replacements(f) + if f.get("sixteenth"): + reps = dict(SIXTEENTH) | reps + reps[DRAFT_LINE] = "" + for p in doc.paragraphs: + if not p.runs: + continue + full = "".join(r.text for r in p.runs) + new = full + for k, v in reps.items(): + if k in new: + new = new.replace(k, v) + if new != full: + p.runs[0].text = new + for r in p.runs[1:]: + r.text = "" + doc.save(out_docx) + return out_docx + + +def to_pdf(docx_path): + import subprocess, tempfile + with tempfile.TemporaryDirectory() as td: + r = subprocess.run(["soffice", "--headless", "--convert-to", "pdf", "--outdir", td, docx_path], + capture_output=True, timeout=90) + pdf = os.path.join(td, os.path.basename(docx_path).replace(".docx", ".pdf")) + if os.path.exists(pdf): + dst = docx_path.replace(".docx", ".pdf") + open(dst, "wb").write(open(pdf, "rb").read()) + return dst + return None + + +def leftover_placeholders(docx_path): + import re + d = docx.Document(docx_path) + found = set() + for p in d.paragraphs: + found.update(re.findall(r"\[[A-Z][A-Z0-9 /.,%()\[\]-]*\]", p.text)) + return sorted(found) + + +FOUNDERS = [ + # ── t-261008143050: Siarhei Zhukovich, когорта с 16-го ── + dict(file="SPA_Siarhei_Zhukovich", name="Siarhei Zhukovich", + passport="AB3393306", pid=None, # ид. номер — нет в базе, плейсхолдер + address="28 July str., 33B-42, Brest", email=None, + project="podrabotki.by (rebranding to StaffGo)", + one_line="a platform for hiring line staff: employment/contract placement and turnkey contractor paperwork (documents and payments)", + rev_share=50, effective="October 16, 2026", cohort="October 2026", + program_start="16 October 2026", sixteenth=True), + # ── t-261008144131: участники 42 Labs за 2026-09 (lab_debts) ── + dict(file="SPA_Alex_Shkor", name="Alex Shkor", passport=None, pid=None, + address=None, email="alex@shkor.com", project=None, one_line=None, + rev_share=None, effective="September 1, 2026", cohort="September 2026", + program_start="1 September 2026", sixteenth=False), + dict(file="SPA_Vlad", name="Vlad", passport=None, pid=None, + address=None, email="vla2music.com@gmail.com", project=None, one_line=None, + rev_share=None, effective="September 1, 2026", cohort="September 2026", + program_start="1 September 2026", sixteenth=False), + dict(file="SPA_Yura", name="Yura", passport=None, pid=None, + address=None, email="yura28440@gmail.com", project=None, one_line=None, + rev_share=None, effective="September 1, 2026", cohort="September 2026", + program_start="1 September 2026", sixteenth=False), + dict(file="SPA_Stas_Dashchinskiy", name="Stas Dashchinskiy", passport=None, pid=None, + address=None, email="dashchinskiystas@gmail.com", project=None, one_line=None, + rev_share=None, effective="September 1, 2026", cohort="September 2026", + program_start="1 September 2026", sixteenth=False), + dict(file="SPA_Alexander", name="Александр", passport=None, pid=None, + address=None, email="Justgbox@gmail.com", project=None, one_line=None, + rev_share=None, effective="September 1, 2026", cohort="September 2026", + program_start="1 September 2026", sixteenth=False), + dict(file="SPA_Melnikau", name="Andrei Melnikau", passport="MP2848482", + pid="3120386A064PB1", address=None, email="ex3.mac@gmail.com", + project="tYESter", + one_line="a family of software tools for testing computer hardware components without the need to purchase additional equipment", + rev_share=None, effective="September 1, 2026", cohort="September 2026", + program_start="1 September 2026", sixteenth=False), + dict(file="SPA_Dmitry_Siasitski", name="Dmitry Siasitski", passport=None, pid=None, + address=None, email="sesitski.dmitry@gmail.com", project=None, one_line=None, + rev_share=None, effective="September 1, 2026", cohort="September 2026", + program_start="1 September 2026", sixteenth=False), +] + +if __name__ == "__main__": + only = sys.argv[1:] or None + for f in FOUNDERS: + if only and f["file"] not in only: + continue + docx_path = os.path.join(OUT, f["file"] + ".docx") + fill(f, docx_path) + pdf = to_pdf(docx_path) + print(f"{f['file']}: docx={'OK' if os.path.exists(docx_path) else 'FAIL'} " + f"pdf={'OK' if pdf else 'FAIL'} leftover={leftover_placeholders(docx_path)}") diff --git a/skills/README.md b/skills/README.md index 49781fa..72d75db 100644 --- a/skills/README.md +++ b/skills/README.md @@ -12,6 +12,7 @@ |---|---|---| | `meta-skill-factory` | Метаскилл: превращение повторяющейся работы в скилл репо (шаги, чеклисты, критерии качества) | Заметил, что делаю одну и ту же работу 2+ раз — прежде чем делать руками в третий | | `presale-landing` | Производство пресейл/продуктовых лендингов: структура, честные мокапы, Stripe CTA, quality gates | Задача на лендинг/пресейл-страницу или её v2 | +| `gcal` | Google Calendar: команда из TG «сделай звонок на X» → событие с приглашениями в рабочий день (цикл 3 через 3); инструменты tools/gcal.py | Просьба спланировать звонок/встречу/событие, вопрос по графику | ## Сторонние наборы diff --git a/skills/compact-landing/SKILL.md b/skills/compact-landing/SKILL.md new file mode 100644 index 0000000..c8ae631 --- /dev/null +++ b/skills/compact-landing/SKILL.md @@ -0,0 +1,73 @@ +--- +description: Build compact premium landing pages with clear CTA hierarchy, quiet typography, and minimal visual noise. Install from Danilaa1 in one command. +--- +# Compact Landing +Build a small, finished product world—not a generic marketing template. Preserve compactness and clarity; reinvent the visible design language for each product. +## Workflow +1. Inspect the product, supplied content, current stack, and existing design language. Reuse installed tools and components. +2. Ask for button size first—compact, medium, or large—unless the user already answered or asked you to choose. +3. Ask at most two adaptive questions only when their answers would materially change the result. Good subjects: brand mood, content depth, corner character, or motion level. +4. Create a design fingerprint before coding. If this skill includes scripts and Node is available, run `node scripts/pick-direction.mjs`. Otherwise choose from `references/style-dna.md` without taking the first option in every list. +5. Adapt the fingerprint to product meaning. Treat picker output as a creative constraint, not a command to make incoherent choices. +6. Identify one product-native signature artifact: a real demo, visualized output, interactive sample, comparison, miniature workflow, data specimen, or useful control. This should create identity more than decoration does. +7. Implement the smallest complete page. Prefer HTML/CSS and existing dependencies. Add JavaScript only for behavior the page actually needs. +8. Verify mobile layout, keyboard focus, reduced motion, stable dynamic states, and the real primary action. +If the user wants immediate execution, choose the button size and fingerprint yourself. Do not stop for a questionnaire. +## Design Fingerprint +Define these axes before implementation: +- **Composition:** field note, offset rail, split pamphlet, framed console, compact catalogue, stacked specimen, or another product-specific silhouette. +- **Measure:** choose a fitting compact width, usually `420-720px`; do not default to `440px` every time. +- **Palette:** warm paper, cool fog, monochrome ink, dark charcoal, product-tinted, or playful light. +- **Typography:** grotesk + mono, humanist sans + mono, editorial serif + sans, rounded sans + mono, condensed + sans, or purposeful all-mono. +- **Geometry:** sharp, machined, balanced, friendly, soft, or mixed/concentric radii. +- **Surface grammar:** whitespace, flat dividers, inset hairlines, layered rings, tinted panels, paper rules, or technical frames. Pick one dominant grammar. +- **Motif:** emoji punctuation, ASCII marks, indices, stamps, doodles, colored dots, tiny object icons, or none. +- **Motion:** choose one entrance behavior and one or two interaction behaviors. +Variation must change the first-screen silhouette, not merely colors or labels. Ensure at least four axes differ from the old canonical recipe: centered `440px` column, neutral white/zinc, Geist + Geist Mono, `6-10px` radii, inset hairline cards, Instrument order, staggered rise. +Do not randomize components independently. Choose one coherent art direction, then derive tokens from it. Use only one or two surprising details; repetition inside the page creates coherence. +Read `references/style-dna.md` for compatible ranges, direction families, motion patterns, and review checks. +## Invariants +- Keep the page compact in scope and information density, even when its canvas is wider or split. +- Put the primary action in the first viewport. Repeat it only when doing so removes friction. +- Use clear type hierarchy, concrete copy, and enough contrast. +- Use real product content. Avoid fake metrics, vague claims, filler testimonials, and decorative dashboards. +- Extend visually tiny controls to at least a `40px` hit area without making them look oversized. +- Show a visible `:focus-visible` state and preserve semantic HTML. +- Use tabular numerals for changing numbers. +- Respect `prefers-reduced-motion` and keep the reduced experience fully usable. +- Never add a dependency for a small effect CSS or the current stack can handle. +## Zero-Shift Motion +- Animate only `transform`, `opacity`, and `filter`. Never use `transition: all`. +- Reserve dimensions before animation: fixed/min heights, `aspect-ratio` for media, fixed icon slots, width-locked changing labels, and tabular numerals. +- Overlay alternate labels, icons, tabs, and panels in the same grid area or fixed box. Do not insert delayed content into document flow. +- Keep padding, borders, font size, font weight, line height, grid tracks, and flex sizing stable across hover and state changes. +- Prefer transforms for toggles, drawers, traces, and decorative motion. Absolute-position decorative particles so they never affect flow. +- Self-host fonts when practical. Otherwise use metric-compatible fallbacks and avoid hiding the page while fonts load. +- Gate hover-only effects behind `(hover: hover) and (pointer: fine)`. +## Buttons +Apply one scale consistently: +- **Compact:** `24-28px` visual height, `11-12px` label. +- **Medium:** `32-36px` visual height, `12.5-13px` label. +- **Large:** `40-44px` visual height, `13.5-14px` label. +Use `scale(0.96)` for press feedback. For wide command controls, use `0.98`. Keep label and icon slots stable during copied/loading/success states. +For `llms.txt` or assistant-copy actions, use a small mono button only when the product needs it. Reuse the project's icon system or a CSS glyph; do not embed SVG examples in skill output. +## Purposeful Personality +Emoji are allowed when they fit the product. Use `1-3` as punctuation, labels, tiny stickers, or state cues—not as a substitute for structure or iconography. Reserve their boxes so platform glyph differences do not move layout. +Color may be playful, dark, editorial, or product-tinted. Give every accent a role and repeat it. Avoid default purple-blue AI gradients, uncontrolled rainbows, and random decorative color. +Motion should explain state, focus attention, or express the product metaphor. A fixed-size trace, ambient floating mark, slot swap, clipped reveal, or state color wash can feel alive without causing reflow. Avoid perpetual motion everywhere. +## Avoid +- Reusing one section order, narrow width, palette, radius family, or component set across unrelated products. +- Generic oversized heroes, three equal feature cards, badge clouds, and feature grids by reflex. +- Copying the Cuelume waveform, cream palette, confetti trio, or exact layout unless the product genuinely calls for them. +- Making every surface a card or every control a pill. +- Decorative animation that competes with the primary action. +- Heavy animation libraries for opacity, transforms, tabs, counters, or simple reveals. +## Final Check +- Fingerprint chosen before code; at least four high-salience axes differ from the canonical recipe. +- Product-native signature artifact is visible and useful. +- First viewport has a distinct silhouette and clear primary action. +- Radius, palette, type, surface, motif, and motion form one coherent direction. +- Mobile becomes a clean single column without horizontal overflow. +- Dynamic content, media, icons, labels, and panels have reserved geometry. +- Keyboard, focus, contrast, touch targets, and reduced motion work. +- Build and the primary interaction pass using the project's existing verification path. diff --git a/skills/gcal/SKILL.md b/skills/gcal/SKILL.md new file mode 100644 index 0000000..efb7163 --- /dev/null +++ b/skills/gcal/SKILL.md @@ -0,0 +1,66 @@ +--- +name: gcal +description: Use when Alex asks in TG to schedule a call/meeting ("сделай звонок на X с Владой", "запланируй созвон", "поставь встречу") — creates a Google Calendar event with guest invites in a workday slot (Alex's «3 через 3» cycle), or checks the schedule. Powered by tools/gcal.py. +metadata: + origin: task t-261008155436-a658e9, messages m-516173943/m-516173944 + requires: secrets/gcal-service-account.json (service account key; until provided — dry-run/planning only) +--- + +# gcal: команда из TG → событие в календаре Alex + +Одна команда из TG должна решаться одним прогоном этого скилла: +интеншн → валидный слот (рабочий день!) → событие с приглашением → ответ в чат. + +## Шаг 1 — разобрать команду + +Из сообщения извлечь: +- **что**: тема (по умолчанию «Звонок: <тема/имя гостя>»); +- **когда**: день (сегодня/завтра/дата) и время; длительность (по умолчанию 30 мин); +- **кого**: email гостей. Известные контакты: Влада — videoprodshema@gmail.com. + Email НЕ известен → спросить у Alex ДО создания (угадывать адрес нельзя); +- **время**: Alex в Минске (UTC+3). Время из TG трактовать как минское, + в инструмент передавать UTC. + +## Шаг 2 — валидация слота (строго до создания) + +``` +./venv/bin/python tools/gcal.py next-workdays # ближайшие рабочие +./venv/bin/python tools/gcal.py create ... --dry-run # что попадёт в Google +``` + +Guard'ы (встроены в create, не обходить): +1. **Только рабочий день** — цикл «3 через 3» (docs/alex-schedule.md). + Якорь цикла: env GCAL_WORK_ANCHOR (по умолчанию 2026-10-02). Сдвиг цикла + Alex сообщил в TG → обновить docs/alex-schedule.md и якорь. +2. **Не позже 2ч до начала** (GCAL_MIN_LEAD_MIN=120) — анти-паттерн рекордера + (t-261008155708: встречи, созданные <2 мин до старта, не заходятся). + +Выходной/поздний слот → НЕ создавать, ответить Alex с ближайшими рабочими +днями и попросить подтвердить слот. + +## Шаг 3 — создать событие + +``` +./venv/bin/python tools/gcal.py create \ + --title "Звонок: Влада" --day YYYY-MM-DD --time HH:MM \ + --duration 30 --guests "videoprodshema@gmail.com" --timezone Europe/Minsk +``` + +`sendUpdates=all` — приглашения уходят гостям автоматически. +Ответ в чат: ссылка на событие (htmlLink), слот, гости. + +## Ключ (один раз, если не настроен) + +`tools/gcal.py check` печатает готовность и шаги: +Google Cloud → Calendar API → service account → JSON-ключ → дать +client_email право «Вносить изменения» на календаре Alex → положить в +`secrets/gcal-service-account.json` (0600). Ключа нет → работать dry-run'ом +и попросить Alex прислать ключ (это может сделать только владелец аккаунта). + +## Критерии качества + +- [ ] Слот — рабочий день цикла; иначе отказ с предложением ближайших рабочих +- [ ] До начала ≥2ч (или осознанный --force по явной просьбе Alex) +- [ ] Гости получили приглашения (sendUpdates=all), email не угадан +- [ ] В ответе Alex — время в его зоне (Минск) и ссылка на событие +- [ ] `check` зелёный; ключ лежит в secrets/ с правами 0600 diff --git a/tests/test_msg_artifact_race.py b/tests/test_msg_artifact_race.py new file mode 100644 index 0000000..24dfba8 --- /dev/null +++ b/tests/test_msg_artifact_race.py @@ -0,0 +1,111 @@ +# -*- coding: utf-8 -*- +"""t-261009160906: анти-гонка уровней min/hour на входящих TG (hermetic). + +Кейс 09.10: Alex попросил концепт (m-516174013). Мин-тик ответил и доставил +свой файл (16:01). Часовой тик, не видя ответа мин-уровня, сгенерировал СВОЙ +файл и доставил вторым (16:06) — Alex получил два разных файла на один запрос. +Dedup по artifact_id=file:<путь> не ловит: два разных файла = два разных ключа. + +Фикс: связка «артефакт → входящее» (-m m-NNN у tg-deliver-file, msg= у +deliver_artifact, reply_artifact в read model). Если на входящее уже отвечено +иным артефактом — SKIP с событием. force обходит (ручная повторная доставка). + +Запуск: ./venv/bin/python tests/test_msg_artifact_race.py +""" +import os +import subprocess +import sys + +# ── изоляция: своя БД в /tmp (prod не трогаем) ── +_DST = "/tmp/simbiot-test-msg-artifact-race.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 es, commands + +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) + + +es.init_db() + +# ── подготовка: входящее m-990001, на которое мин-уровень уже ответил ── +es.append_event("inbox", "m-990001", "MessageReceived", { + "update_id": 990001, "chat_id": 42, "message_id": 1, "user_id": 1, + "text": "сделай концепт в html", "kind": "text", "date": 1791560000, +}) +es.append_event("inbox", "m-990001", "ReplySent", { + "to_chat": 42, "text": "готово, отправил файлом", "tg_ok": True, "channel": "tg", +}) + +# файлы-двойники гонки: мин-уровень и часовой +_F1 = "/tmp/simbiot-test-race-min.html" +_F2 = "/tmp/simbiot-test-race-hour.html" +for f in (_F1, _F2): + with open(f, "w") as fh: + fh.write("r") + +sends = [] + +# 1) мин-уровень доставил свой файл с привязкой к сообщению +ok1 = commands.deliver_artifact(f"file:{_F1}", kind="file", send=lambda: (sends.append(_F1) or True), + owner="test:min", caption="мин", msg="m-990001") +check("min-level file delivered", ok1 and sends == [_F1], sends) + +model = es.run_projections() +m = model["inbox"]["m-990001"] +check("read model binds reply_artifact", m.get("reply_artifact") == f"file:{_F1}", m.get("reply_artifact")) + +# 2) часовой уровень пытается доставить СВОЙ файл на то же входящее → SKIP +ok2 = commands.deliver_artifact(f"file:{_F2}", kind="file", send=lambda: (sends.append(_F2) or True), + owner="test:hour", caption="час", msg="m-990001") +check("hour-level duplicate SKIPped", ok2 is False and sends == [_F1], sends) + +db = es._conn() +skip = list(db.execute( + "SELECT data FROM events WHERE event_type='DeliverySkipped' AND data LIKE '%msg-already%'")) +check("DeliverySkipped event with reason", len(skip) == 1, len(skip)) + +# 3) force обходит защиту (ручная повторная доставка — явное решение человека) +ok3 = commands.deliver_artifact(f"file:{_F2}", kind="file", send=lambda: (sends.append(_F2) or True), + owner="test:hour", caption="час", force=True, msg="m-990001") +check("force bypasses guard", ok3 and sends == [_F1, _F2], sends) + +# 4) доставки БЕЗ привязки к сообщению не задеваются (обратная совместимость) +ok4 = commands.deliver_artifact("file:/tmp/simbiot-test-race-unbound.html", kind="file", + send=lambda: (sends.append("unbound") or True), + owner="test", caption="x") +check("unrelated delivery unaffected", ok4 and sends[-1] == "unbound", sends) + +# 5) второе входящее без артефакта: доставка с привязкой проходит свободно +es.append_event("inbox", "m-990002", "MessageReceived", { + "update_id": 990002, "chat_id": 42, "message_id": 2, "user_id": 1, + "text": "ещё один запрос", "kind": "text", "date": 1791560100, +}) +es.append_event("inbox", "m-990002", "ReplySent", { + "to_chat": 42, "text": "ок", "tg_ok": True, "channel": "tg", +}) +ok5 = commands.deliver_artifact("file:/tmp/simbiot-test-race-second.html", kind="file", + send=lambda: (sends.append("second") or True), + owner="test:hour", caption="y", msg="m-990002") +check("fresh msg accepts its artifact", ok5 and sends[-1] == "second", sends) + +# 6) CLI: tg-deliver-file -m m-990001 с чужим артефактом → SKIP, выход 0 +r = subprocess.run( + ["./venv/bin/python", "cli.py", "tg-deliver-file", _F2, "-m", "m-990001", + "-c", "dup", "-k", "file:" + _F2], + capture_output=True, text=True, cwd="/root/simbiot-harness") +check("CLI SKIP on raced msg", r.returncode == 0 and "SKIP" in r.stdout, + r.stdout.strip()[:120] + r.stderr.strip()[-200:]) + +print("=" * 40) +print("FAILS:", len(fails), fails if fails else "— все проверки прошли") +sys.exit(1 if fails else 0) diff --git a/tools/gcal.py b/tools/gcal.py new file mode 100755 index 0000000..f59b529 --- /dev/null +++ b/tools/gcal.py @@ -0,0 +1,290 @@ +#!/usr/bin/env python3 +"""gcal.py — Google Calendar для харнеса: create-event / list / check (t-261008155436). + +Команда из TG «сделай звонок на X с Владой» → минутный тик грузит скилл +skills/gcal/SKILL.md → зовёт этот инструмент → событие в календаре Alex +с приглашением гостям, слот только на рабочий день (цикл «3 через 3»). + +Автоматизация (service account, JWT → access token, REST v3 — без google-клиентов): + 1) Alex: console.cloud.google.com → новый проект → включить Calendar API → + «Service accounts» → создать → ключ JSON → прислать файл. + 2) В календаре Google: Настройки → «Настройки для моего календаря» → + «Предоставить доступ» → добавить client_email ключа с ролью + «Вносить изменения» (make changes to events). + 3) Положить ключ: /root/simbiot-harness/secrets/gcal-service-account.json + (или env GCAL_SERVICE_ACCOUNT_FILE). Файл 0600. + 4) ./gcal.py check — готовность. Готово: скилл создаёт события без человека. + +Если ключа нет — работают --dry-run, check, next-workdays (планирование слотов). +""" +from __future__ import annotations + +import argparse +import base64 +import json +import os +import sys +import time +from datetime import date, datetime, timedelta, timezone +from pathlib import Path + +HERE = Path(__file__).resolve().parent +REPO = HERE.parent +DEFAULT_KEY = REPO / "secrets" / "gcal-service-account.json" +TOKEN_CACHE = Path("/tmp/.gcal_access_token.json") + +# ── График Alex: цикл «3 через 3» ───────────────────────────────────────────── +# docs/alex-schedule.md: 3 рабочих, 3 выходных. Якорь выведен из m-516173591 +# (2026-10-03: «сегодня и завтра рабочие, далее 3 выходных» → 10-02 первый +# рабочий цикла). Проверка: 10-08 = (10-08−10-02)=6 → 6%6=0 → рабочий (совпало +# с «сегодня/завтра рабочие» от 10-08). Сдвиг цикла: env GCAL_WORK_ANCHOR=YYYY-MM-DD +ANCHOR = os.environ.get("GCAL_WORK_ANCHOR", "2026-10-02") +PERIOD = 6 +WORK_DAYS = {0, 1, 2} # смещения внутри цикла: 0,1,2 — рабочие; 3,4,5 — выходные + +MIN_LEAD_MIN = int(os.environ.get("GCAL_MIN_LEAD_MIN", "120")) # анти-паттерн рекордера: не позже 2ч до начала + + +def _anchor() -> date: + return datetime.strptime(ANCHOR, "%Y-%m-%d").date() + + +def is_workday(d: date) -> bool: + return ((d - _anchor()).days % PERIOD) in WORK_DAYS + + +def next_workdays(n: int = 6, from_day: date | None = None) -> list[date]: + d = from_day or date.today() + out: list[date] = [] + while len(out) < n: + if is_workday(d): + out.append(d) + d += timedelta(days=1) + return out + + +# ── Креды и токен (service account → JWT → access token) ───────────────────── +def _key_path(explicit: str | None) -> Path: + p = Path(explicit or os.environ.get("GCAL_SERVICE_ACCOUNT_FILE") or DEFAULT_KEY) + return p + + +def load_key(explicit: str | None = None) -> dict | None: + p = _key_path(explicit) + if not p.exists(): + return None + return json.loads(p.read_text()) + + +def _b64(o: dict | bytes) -> str: + raw = json.dumps(o).encode() if isinstance(o, dict) else o + return base64.urlsafe_b64encode(raw).rstrip(b"=").decode() + + +def get_access_token(key: dict, force: bool = False) -> str: + """JWT (RS256) → Google OAuth2 → access token (кэш ~50 мин).""" + if not force and TOKEN_CACHE.exists(): + try: + c = json.loads(TOKEN_CACHE.read_text()) + if c.get("exp", 0) > time.time() + 120 and c.get("iss") == key["client_email"]: + return c["token"] + except Exception: + pass + import httpx # уже в venv харнеса + + now = int(time.time()) + claim = { + "iss": key["client_email"], + "scope": "https://www.googleapis.com/auth/calendar", + "aud": "https://oauth2.googleapis.com/token", + "iat": now, + "exp": now + 3600, + } + head = {"alg": "RS256", "typ": "JWT"} + signing_input = f"{_b64(head)}.{_b64(claim)}".encode() + + from cryptography.hazmat.primitives import hashes, serialization + + pk = serialization.load_pem_private_key(key["private_key"].encode(), password=None) + sig = pk.sign(signing_input, hashes.SHA256()) + jwt = f"{signing_input.decode()}.{_b64(sig)}" + + r = httpx.post( + "https://oauth2.googleapis.com/token", + data={"grant_type": "urn:ietf:params:oauth:grant-type:jwt-bearer", "assertion": jwt}, + timeout=20, + ) + if r.status_code != 200: + raise RuntimeError(f"token exchange failed: {r.status_code} {r.text[:200]}") + tok = r.json() + TOKEN_CACHE.write_text(json.dumps({"token": tok["access_token"], "exp": now + tok.get("expires_in", 3600), + "iss": key["client_email"]})) + TOKEN_CACHE.chmod(0o600) + return tok["access_token"] + + +def _api(method: str, path: str, key: dict, body: dict | None = None, params: dict | None = None) -> dict: + import httpx + + tok = get_access_token(key) + r = httpx.request(method, f"https://www.googleapis.com/calendar/v3{path}", + headers={"Authorization": f"Bearer {tok}", "Content-Type": "application/json"}, + json=body, params=params or {}, timeout=25) + if r.status_code >= 400: + raise RuntimeError(f"calendar api {r.status_code}: {r.text[:300]}") + return r.json() + + +# ── Действия ────────────────────────────────────────────────────────────────── +def cmd_check(args) -> int: + print(f"anchor (цикл 3 через 3): {ANCHOR} (env GCAL_WORK_ANCHOR для сдвига)") + today = date.today() + print(f"сегодня {today}: {'РАБОЧИЙ' if is_workday(today) else 'выходной'}") + print("ближайшие рабочие:", ", ".join(d.isoformat() for d in next_workdays(4))) + key = load_key(args.key) + if not key: + p = _key_path(args.key) + print(f"\nключ: НЕТ ({p})") + print("шаги: Google Cloud → Calendar API → service account → JSON-ключ →") + print(" доступ к календарю Alex для client_email (make changes) →") + print(f" положить в {p} (0600). Затем: gcal.py check && gcal.py list") + return 1 + print(f"\nключ: OK ({key.get('client_email', '?')})") + try: + cal = _api("GET", f"/calendars/{args.calendar}", key) + print(f"календарь: {cal.get('summary')} ({cal.get('id')}) — доступ есть") + return 0 + except Exception as e: + print(f"доступ к календарю: НЕ УДАЛСЯ — {e}") + print("→ дайте client_email право «Вносить изменения» в настройках календаря") + return 2 + + +def _guard(slot_start: datetime) -> tuple[bool, str]: + d = slot_start.date() + if not is_workday(d): + nxt = next_workdays(1, from_day=d + timedelta(days=1)) + sug = ", ".join(x.isoformat() for x in nxt) + return False, f"{d.isoformat()} — выходной по циклу Alex «3 через 3». Ближайшие рабочие: {sug}" + lead = (slot_start - datetime.now(timezone.utc)).total_seconds() / 60 + if lead < MIN_LEAD_MIN: + return False, (f"до начала {int(lead)} мин < {MIN_LEAD_MIN} (анти-паттерн рекордера: " + f"t-261008155708 — встречи <2 мин до старта не заходятся). Возьмите слот позже.") + return True, "ok" + + +def _parse_slot(day: str, hhmm: str, dur: int, tz_name: str) -> datetime: + """Локальное время дня в tz_name → момент UTC (API Google требует offset).""" + from zoneinfo import ZoneInfo + + d = date.fromisoformat(day) + hh, mm = map(int, hhmm.split(":")) + return datetime(d.year, d.month, d.day, hh, mm, tzinfo=ZoneInfo(tz_name)).astimezone(timezone.utc) + + +def cmd_create(args) -> int: + start = _parse_slot(args.day, args.time, args.duration, args.timezone) + ok, msg = _guard(start) + if not ok and not args.force: + print(f"ОТКАЗ: {msg}") + return 3 + if not ok: + print(f"ВНИМАНИЕ (force): {msg}") + end = start + timedelta(minutes=args.duration) + guests = [g.strip() for g in (args.guests or "").split(",") if g.strip()] + event = { + "summary": args.title, + "description": args.description or "Создано харнесом Simbiot (skills/gcal, t-261008155436-a658e9)", + "start": {"dateTime": start.isoformat(), "timeZone": args.timezone}, + "end": {"dateTime": end.isoformat(), "timeZone": args.timezone}, + "attendees": [{"email": g} for g in guests], + "reminders": {"useDefault": False, "overrides": [ + {"method": "popup", "minutes": 30}, {"method": "popup", "minutes": 10}, + ]}, + } + if args.dry_run: + print("DRY-RUN (в Google не писали). Событие:") + print(json.dumps(event, ensure_ascii=False, indent=1)) + print(f"guard: {msg}; слот рабочий: {ok}") + return 0 + key = load_key(args.key) + if not key: + print("нет сервис-ключа Google — см. gcal.py check (шаги настройки)") + return 1 + made = _api("POST", f"/calendars/{args.calendar}/events", key, body=event, + params={"sendUpdates": "all"}) + print(f"СОЗДАНО: {made.get('htmlLink')}") + print(f" {made.get('summary')} · {start:%Y-%m-%d %H:%M} UTC ({args.timezone}) · " + f"гости: {', '.join(guests) or '—'}") + return 0 + + +def cmd_list(args) -> int: + key = load_key(args.key) + if not key: + print("нет сервис-ключа Google — см. gcal.py check") + return 1 + tmin = datetime.now(timezone.utc).isoformat() + data = _api("GET", f"/calendars/{args.calendar}/events", key, + params={"timeMin": tmin, "maxResults": args.limit, "orderBy": "startTime", + "singleEvents": "true"}) + evs = data.get("items", []) + if not evs: + print("ближайших событий нет") + return 0 + for e in evs: + when = (e.get("start", {}).get("dateTime") or e.get("start", {}).get("date", ""))[:16] + print(f" {when} {e.get('summary', '(без названия)')}") + return 0 + + +def cmd_next(args) -> int: + for d in next_workdays(args.count): + off = ((d - _anchor()).days % PERIOD) + print(f" {d.isoformat()} рабочий #{off + 1}/3") + return 0 + + +def main() -> int: + ap = argparse.ArgumentParser(description="Google Calendar для харнеса (create-event по команде из TG)") + sub = ap.add_subparsers(dest="cmd", required=True) + + c = sub.add_parser("check", help="готовность: ключ, доступ, график") + c.add_argument("--key", help="путь к JSON сервис-аккаунта") + c.add_argument("--calendar", default=os.environ.get("GCAL_CALENDAR", "primary")) + c.set_defaults(fn=cmd_check) + + c = sub.add_parser("create", help="создать событие (слот только рабочий день)") + c.add_argument("--title", required=True) + c.add_argument("--day", required=True, help="YYYY-MM-DD") + c.add_argument("--time", required=True, help="HH:MM (UTC)") + c.add_argument("--duration", type=int, default=30, help="минуты") + c.add_argument("--guests", default="", help="email через запятую (приглашения)") + c.add_argument("--description", default="") + c.add_argument("--timezone", default=os.environ.get("GCAL_TZ", "UTC")) + c.add_argument("--calendar", default=os.environ.get("GCAL_CALENDAR", "primary")) + c.add_argument("--key") + c.add_argument("--dry-run", action="store_true", help="показать событие, в Google не писать") + c.add_argument("--force", action="store_true", help="игнорировать guard (осознанно)") + c.set_defaults(fn=cmd_create) + + c = sub.add_parser("list", help="ближайшие события") + c.add_argument("--limit", type=int, default=10) + c.add_argument("--calendar", default=os.environ.get("GCAL_CALENDAR", "primary")) + c.add_argument("--key") + c.set_defaults(fn=cmd_list) + + c = sub.add_parser("next-workdays", help="ближайшие рабочие дни (цикл 3 через 3)") + c.add_argument("count", nargs="?", type=int, default=6) + c.set_defaults(fn=cmd_next) + + a = ap.parse_args() + try: + return a.fn(a) + except Exception as e: # noqa: BLE001 — инструмент отвечает кодом, не трейсбеком + print(f"ОШИБКА: {type(e).__name__}: {e}") + return 4 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/~/.config/opencode/opencode.json b/~/.config/opencode/opencode.json index eb893ca..e869ce0 100644 --- a/~/.config/opencode/opencode.json +++ b/~/.config/opencode/opencode.json @@ -1,8 +1,14 @@ { - "permission": { - "edit": "allow", - "bash": "allow", - "webfetch": "allow" - }, - "share": "disabled" + "permission": { "edit": "allow", "bash": "allow", "webfetch": "allow" }, + "share": "disabled", + "provider": { + "simbiot": { + "npm": "@ai-sdk/openai-compatible", + "name": "Simbiot Router (cil.by)", + "options": { "baseURL": "https://router.cil.by/v1" }, + "models": { + "gpt-5.2": {}, "claude-sonnet-4.6": {}, "gpt-4.1": {}, "o4-mini": {} + } + } + } } diff --git a/~/.config/opencode/opencode.json.bak b/~/.config/opencode/opencode.json.bak new file mode 100644 index 0000000..eb893ca --- /dev/null +++ b/~/.config/opencode/opencode.json.bak @@ -0,0 +1,8 @@ +{ + "permission": { + "edit": "allow", + "bash": "allow", + "webfetch": "allow" + }, + "share": "disabled" +} diff --git a/~/.profile b/~/.profile new file mode 100644 index 0000000..119c058 --- /dev/null +++ b/~/.profile @@ -0,0 +1,2 @@ +export OPENAI_API_KEY=sk-9ffbac9384bc446eab3bbe58a4a98dc3f4b2347d341caf51a34ce93149fd8c57f1eff2065f89025e +export OPENAI_BASE_URL=https://router.cil.by/v1