e-261014163808-4da3ec: TTFT-watchdog+ретрай висящего апстрим-вызова в LLM-воркере — зависание до первого токена больше не роняет весь тик (2 павших hour-тика 13:08/16:11, batch TaskFailed); тест test_ttft_retry 7 PASS
This commit is contained in:
parent
744edd537e
commit
acfdf3e5b9
8 changed files with 809 additions and 1 deletions
BIN
artifacts/alex_photo.png
Normal file
BIN
artifacts/alex_photo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.1 MiB |
119
artifacts/antivirus-whitelabel-proposal.html
Normal file
119
artifacts/antivirus-whitelabel-proposal.html
Normal file
|
|
@ -0,0 +1,119 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="ru">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Приложение к соглашению — White label партнёрство (антивирусный продукт)</title>
|
||||||
|
<style>
|
||||||
|
:root{
|
||||||
|
--paper:#faf8f3; --ink:#15150f; --signal:#FF4F00;
|
||||||
|
--dim:#6b675e; --line:#d8d3c8;
|
||||||
|
}
|
||||||
|
*{margin:0;padding:0;box-sizing:border-box}
|
||||||
|
body{background:var(--paper);color:var(--ink);font:16px/1.65 -apple-system,"Segoe UI",Roboto,Arial,sans-serif;-webkit-font-smoothing:antialiased}
|
||||||
|
.wrap{max-width:860px;margin:0 auto;padding:56px 24px 100px}
|
||||||
|
header{border-bottom:3px solid var(--ink);padding-bottom:26px;margin-bottom:40px}
|
||||||
|
.kicker{font-family:ui-monospace,Menlo,monospace;font-size:12px;letter-spacing:.22em;color:var(--signal);font-weight:700;text-transform:uppercase}
|
||||||
|
h1{font-size:clamp(26px,4.5vw,40px);line-height:1.1;font-weight:800;margin:10px 0 12px}
|
||||||
|
.sub{font-size:16px;color:var(--dim)}
|
||||||
|
section{margin:36px 0 0}
|
||||||
|
h2{font-size:19px;font-weight:800;margin:0 0 10px;border-left:3px solid var(--signal);padding-left:10px}
|
||||||
|
p{margin:10px 0}
|
||||||
|
.clause{border:1px solid var(--line);background:#fff;padding:16px 20px;margin:14px 0}
|
||||||
|
.clause .cn{font-family:ui-monospace,Menlo,monospace;font-size:11px;color:var(--signal);font-weight:700;letter-spacing:.15em}
|
||||||
|
.clause h4{font-size:15px;margin:4px 0 8px;font-weight:800}
|
||||||
|
.clause p, .clause li{font-size:14.5px}
|
||||||
|
ul{margin:8px 0 8px 20px}
|
||||||
|
li{margin:6px 0} li::marker{color:var(--signal)}
|
||||||
|
.sign{border:3px double var(--ink);background:#fff;padding:22px;margin:30px 0;display:grid;grid-template-columns:1fr 1fr;gap:20px}
|
||||||
|
@media(max-width:700px){.sign{grid-template-columns:1fr}}
|
||||||
|
.sig .org{font-family:ui-monospace,Menlo,monospace;font-size:11px;letter-spacing:.12em;text-transform:uppercase;color:var(--dim);margin-bottom:14px}
|
||||||
|
.sig .line{border-bottom:1px solid var(--ink);height:38px;margin-bottom:8px}
|
||||||
|
.sig .field{font-size:12.5px;color:var(--dim)}
|
||||||
|
footer{margin-top:50px;border-top:1px solid var(--line);padding-top:16px;color:var(--dim);font-size:12.5px}
|
||||||
|
.draft{display:inline-block;border:1px solid var(--signal);color:var(--signal);font-family:ui-monospace,Menlo,monospace;font-size:11px;letter-spacing:.12em;padding:4px 10px;text-transform:uppercase;margin-top:12px}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="wrap">
|
||||||
|
|
||||||
|
<header>
|
||||||
|
<div class="kicker">Приложение к соглашению</div>
|
||||||
|
<h1>Партнёрство по модели White Label</h1>
|
||||||
|
<div class="sub">Антивирусный продукт партнёра, выпускаемый под собственным брендом кооператива.</div>
|
||||||
|
<span class="draft">Черновик · на согласование</span>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<h2>1. Предмет</h2>
|
||||||
|
<div class="clause">
|
||||||
|
<p>Мы предлагаем партнёрство по модели white label: мы берём ваш антивирусный продукт, выпускаем его под собственным брендом и выводим на рынок — маркетинг, продажи, поддержка клиентов и операционный слой остаются на нашей стороне. Вы фокусируетесь на продукте и технологических обновлениях.</p>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<h2>2. Роли сторон</h2>
|
||||||
|
<div class="clause">
|
||||||
|
<div class="cn">2.1</div>
|
||||||
|
<h4>Партнёр (вы)</h4>
|
||||||
|
<ul>
|
||||||
|
<li>предоставляет готовый антивирусный продукт и его обновления;</li>
|
||||||
|
<li>обеспечивает работоспособность ядра и критических исправлений;</li>
|
||||||
|
<li>передаёт документацию, необходимую для продвижения и поддержки.</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div class="clause">
|
||||||
|
<div class="cn">2.2</div>
|
||||||
|
<h4>Мы (кооператив)</h4>
|
||||||
|
<ul>
|
||||||
|
<li>выпускаем продукт под собственным брендом (white label);</li>
|
||||||
|
<li>берём на себя маркетинг, привлечение клиентов и продажи;</li>
|
||||||
|
<li>обеспечиваем первую линию поддержки пользователей;</li>
|
||||||
|
<li>ведём юрлицо, платёжную инфраструктуру и биллинг.</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<h2>3. Экономика</h2>
|
||||||
|
<div class="clause">
|
||||||
|
<p>Выручка от продаж продукта распределяется между сторонами. Конкретная доля каждой стороны, периодичность выплат и порядок учёта фиксируются в основном соглашении. Базовый ориентир для обсуждения — равное участие: продукт с одной стороны, дистрибуция и бренд — с другой.</p>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<h2>4. Права на бренд и продукт</h2>
|
||||||
|
<div class="clause">
|
||||||
|
<p>Интеллектуальная собственность на продукт остаётся за партнёром. Кооперативу передаётся право использования продукта под своим брендом в объёме, необходимом для исполнения настоящего партнёрства. Права на клиентскую базу, маркетинговые материалы и бренд, под которым выпускается продукт, принадлежат кооперативу.</p>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<h2>5. Следующие шаги</h2>
|
||||||
|
<div class="clause">
|
||||||
|
<ul>
|
||||||
|
<li>согласовать доли и условия основного соглашения;</li>
|
||||||
|
<li>определить бренд и упаковку продукта под нашим именем;</li>
|
||||||
|
<li>согласовать план запуска и первые продажи.</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<div class="sign">
|
||||||
|
<div class="sig">
|
||||||
|
<div class="org">От кооператива</div>
|
||||||
|
<div class="line"></div>
|
||||||
|
<div class="field">Подпись / ФИО / дата</div>
|
||||||
|
</div>
|
||||||
|
<div class="sig">
|
||||||
|
<div class="org">От партнёра</div>
|
||||||
|
<div class="line"></div>
|
||||||
|
<div class="field">Подпись / ФИО / дата</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<footer>Черновик для обсуждения. Не является офертой. Правки согласовываются сторонами до подписания.</footer>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
85
artifacts/iishnitsa-vision-session.html
Normal file
85
artifacts/iishnitsa-vision-session.html
Normal file
|
|
@ -0,0 +1,85 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="ru">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Общее видение — панельная сессия для ИИШНИЦЫ</title>
|
||||||
|
<style>
|
||||||
|
body{font-family:system-ui,-apple-system,sans-serif;max-width:820px;margin:0 auto;padding:24px;color:#222;background:#fafafa;line-height:1.55}
|
||||||
|
h1{color:#1a5276} h2{color:#2874a6;border-bottom:2px solid #d6eaf8;padding-bottom:4px;margin-top:32px}
|
||||||
|
table{border-collapse:collapse;width:100%;margin:12px 0;background:#fff}
|
||||||
|
th,td{border:1px solid #ccd;padding:8px 10px;text-align:left;vertical-align:top}
|
||||||
|
th{background:#d6eaf8}
|
||||||
|
.mission{background:#eafaf1;border-left:4px solid #27ae60;padding:12px 16px;margin:16px 0}
|
||||||
|
.circle{display:inline-block;width:14px;height:14px;border-radius:50%;margin-right:6px;vertical-align:-2px}
|
||||||
|
.c1{background:#e74c3c}.c2{background:#f39c12}.c3{background:#8e44ad}
|
||||||
|
.check li{margin:6px 0;list-style:none}
|
||||||
|
.check input{margin-right:8px}
|
||||||
|
blockquote{border-left:3px solid #aaa;margin:8px 0;padding:4px 12px;color:#555;font-style:italic}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<h1>Панельная сессия «Общее видение»</h1>
|
||||||
|
<p><b>Формат:</b> ИИШНИЦА · 90 минут · панель + интерактив из зала.</p>
|
||||||
|
|
||||||
|
<div class="mission">
|
||||||
|
<b>Миссия кооператива 42 как рамка сессии:</b> мы верим, что осмысленное будущее строится вместе — каждый привносит своё видение, а общее оказывается больше суммы личных. Сессия — микромодель кооператива: сначала каждый мечтает в одиночку, потом замечает чужое, затем вместе рождается общее имя будущего.
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h2>Драматургия: три круга</h2>
|
||||||
|
<ol>
|
||||||
|
<li><span class="circle c1"></span><b>Круг 1 — «Моё будущее» (4 мин/участник):</b> каждый панелист без прерываний рассказывает, каким он видит своё (или общего «нашего») будущее. Без оценок, без споров — просто мечта вслух.</li>
|
||||||
|
<li><span class="circle c2"></span><b>Круг 2 — «Что понравилось у других» (2 мин/участник):</b> каждый называет 1–2 образа из чужих рассказов, которые отозвались, и почему. Здесь рождается ткань общего.</li>
|
||||||
|
<li><span class="circle c3"></span><b>Круг 3 — «Название общего видения» (10–15 мин):</b> панель + зал вместе придумывают и выбирают название общего видения. Голосование аплодисментами/руками.</li>
|
||||||
|
</ol>
|
||||||
|
|
||||||
|
<h2>Тайминг на 90 минут</h2>
|
||||||
|
<table>
|
||||||
|
<tr><th>Время</th><th>Блок</th><th>Кто</th></tr>
|
||||||
|
<tr><td>0:00–0:10</td><td>Открытие: миссия 42 как рамка, представление панелей, правила сессии</td><td>Модератор</td></tr>
|
||||||
|
<tr><td>0:10–0:30</td><td>Круг 1 «Моё будущее» — 4 мин × 5 панелей (или 5 мин × 4)</td><td>Панелисты</td></tr>
|
||||||
|
<tr><td>0:30–0:42</td><td>Круг 2 «Что понравилось у других» — 2 мин × панелей</td><td>Панелисты</td></tr>
|
||||||
|
<tr><td>0:42–0:50</td><td>Пауза / музыкальная интерлудия, зал пишет карточки с вариантами названий</td><td>Все</td></tr>
|
||||||
|
<tr><td>0:50–1:05</td><td>Круг 3: сбор вариантов названий, короткая защита 2–3 финалистов, голосование</td><td>Панель + зал</td></tr>
|
||||||
|
<tr><td>1:05–1:20</td><td>Объявление названия общего видения + короткий синтез модератора</td><td>Модератор</td></tr>
|
||||||
|
<tr><td>1:20–1:30</td><td>Вопросы из зала (блиц, по 1 мин)</td><td>Зал</td></tr>
|
||||||
|
<tr><td>1:30</td><td>Закрытие: фото с названием, анонс продолжения</td><td>Модератор</td></tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<h2>Роли</h2>
|
||||||
|
<table>
|
||||||
|
<tr><th>Роль</th><th>Задачи</th></tr>
|
||||||
|
<tr><td>Модератор</td><td>Держит тайминг и рамку миссии 42, режет переспуски, ведёт круг 3 и голосование</td></tr>
|
||||||
|
<tr><td>Тайм-кипер</td><td>Видимый таймер (4/2 мин), карточки «30 секунд»</td></tr>
|
||||||
|
<tr><td>Панелисты (4–5)</td><td>Проходят три круга, готовы к вопросам из зала</td></tr>
|
||||||
|
<tr><td>Сборщик названий</td><td>Собирает карточки зала, группирует похожие, выносит финалистов</td></tr>
|
||||||
|
<tr><td>Фотограф/протокол</td><td>Фото финального названия, короткий конспект для отчёта</td></tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<h2>Заготовки названий (если зал молчит — модерируем от них)</h2>
|
||||||
|
<ul>
|
||||||
|
<li>«Будущее, которое мы узнаем»</li>
|
||||||
|
<li>«Общее небо»</li>
|
||||||
|
<li>«Точка сборки 42»</li>
|
||||||
|
<li>«Мы — будущего авторы»</li>
|
||||||
|
<li>«Дом, который построим вместе»</li>
|
||||||
|
<li>«Ответ на главный вопрос» (отсылка к 42)</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h2>Чек-лист организатора</h2>
|
||||||
|
<ul class="check">
|
||||||
|
<li><input type="checkbox"> Подтвердить панелей (4–5) и briefing им за 2–3 дня: «приходите с образом будущего на 4 минуты»</li>
|
||||||
|
<li><input type="checkbox"> Зал: таймер с крупными цифрами, карточки + маркеры для названий</li>
|
||||||
|
<li><input type="checkbox"> Распечатать заготовки названий (плашки А4)</li>
|
||||||
|
<li><input type="checkbox"> Слайд-открывашка: миссия 42 + правила трёх кругов</li>
|
||||||
|
<li><input type="checkbox"> Микрофоны для зала (минимум 2 бегунка)</li>
|
||||||
|
<li><input type="checkbox"> Назначить тайм-кипера, сборщика названий, фотографа</li>
|
||||||
|
<li><input type="checkbox"> Репетиция открытия с модератором (10 мин накануне)</li>
|
||||||
|
<li><input type="checkbox"> Договориться, куда попадает итог: стена/вики/чат мероприятия</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<blockquote>Правило сессии: в первых двух кругах никто никого не перебивает и не оценивает. Критика допускается только в вопросах из зала — и то бережно.</blockquote>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
166
artifacts/laws-rb-dao-supplement.html
Normal file
166
artifacts/laws-rb-dao-supplement.html
Normal file
|
|
@ -0,0 +1,166 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="ru">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Законы кооператива для РБ — адаптация DAO Supplement (Вайоминг) под кооператив 42</title>
|
||||||
|
<style>
|
||||||
|
:root{--ink:#1a1d23;--mut:#5b6472;--bg:#f7f6f3;--card:#fff;--line:#e5e2da;--acc:#2b5cff;--ok:#1e7f4f;--warn:#b05a00;--bad:#b3261e}
|
||||||
|
*{box-sizing:border-box}
|
||||||
|
body{margin:0;font:16px/1.65 Georgia,'Times New Roman',serif;color:var(--ink);background:var(--bg)}
|
||||||
|
.wrap{max-width:900px;margin:0 auto;padding:32px 20px 80px}
|
||||||
|
header.doc{border-bottom:3px double var(--ink);padding-bottom:20px;margin-bottom:28px}
|
||||||
|
.kicker{font-family:system-ui,sans-serif;font-size:12px;letter-spacing:.14em;text-transform:uppercase;color:var(--acc);font-weight:600}
|
||||||
|
h1{font-size:32px;line-height:1.2;margin:10px 0 8px}
|
||||||
|
.meta{font-family:system-ui,sans-serif;font-size:13px;color:var(--mut)}
|
||||||
|
.disclaimer{background:#fff8e6;border:1px solid #ecd9a8;border-radius:10px;padding:14px 18px;font-family:system-ui,sans-serif;font-size:14px;margin:24px 0}
|
||||||
|
h2{font-size:22px;margin:38px 0 12px;padding-top:14px;border-top:1px solid var(--line)}
|
||||||
|
h3{font-size:17px;margin:22px 0 8px}
|
||||||
|
.card{background:var(--card);border:1px solid var(--line);border-radius:12px;padding:18px 22px;margin:16px 0}
|
||||||
|
.grid{display:grid;grid-template-columns:1fr 1fr;gap:14px}
|
||||||
|
@media(max-width:720px){.grid{grid-template-columns:1fr}}
|
||||||
|
table{width:100%;border-collapse:collapse;font-family:system-ui,sans-serif;font-size:13.5px;background:var(--card);border-radius:10px;overflow:hidden;margin:14px 0}
|
||||||
|
th{background:#232833;color:#fff;text-align:left;padding:9px 12px;font-weight:600}
|
||||||
|
td{border-bottom:1px solid var(--line);padding:9px 12px;vertical-align:top}
|
||||||
|
tr:last-child td{border-bottom:none}
|
||||||
|
.tag{display:inline-block;font-family:system-ui,sans-serif;font-size:11px;font-weight:700;letter-spacing:.06em;padding:2px 8px;border-radius:99px;vertical-align:2px}
|
||||||
|
.t-wy{background:#e8eefc;color:#1d3fae}
|
||||||
|
.t-rb{background:#e6f4ec;color:#15663c}
|
||||||
|
.t-diff{background:#fdecea;color:#8f2019}
|
||||||
|
.t-ok{background:#e6f4ec;color:#15663c}
|
||||||
|
.t-risk{background:#fff1e2;color:#8a4a00}
|
||||||
|
.law-ref{font-family:system-ui,sans-serif;font-size:12.5px;color:var(--mut);display:block;margin-top:4px}
|
||||||
|
ol.cl,ul{padding-left:22px}
|
||||||
|
ol.cl li{margin:8px 0}
|
||||||
|
.num{font-family:system-ui,sans-serif;font-weight:700;color:var(--acc);font-size:13px}
|
||||||
|
blockquote{border-left:3px solid var(--acc);margin:14px 0;padding:6px 18px;background:#f2f5ff;border-radius:0 10px 10px 0;font-style:italic}
|
||||||
|
footer.doc{margin-top:48px;border-top:3px double var(--ink);padding-top:16px;font-family:system-ui,sans-serif;font-size:12.5px;color:var(--mut)}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="wrap">
|
||||||
|
|
||||||
|
<header class="doc">
|
||||||
|
<div class="kicker">Кооператив 42 · документы · черновик для обсуждения</div>
|
||||||
|
<h1>Законы кооператива для Республики Беларусь</h1>
|
||||||
|
<div class="meta">Адаптация структуры Wyoming DAO Supplement (W.S. 17-31) под производственный кооператив · к задаче t-261014120323 · основа — разбор t-261014111325 · 14.10.2026</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<div class="disclaimer">
|
||||||
|
<b>Статус документа:</b> концепт/черновик для обсуждения с юристами и правлением. Это не юридическое заключение и не проект устава: статьи сопоставлены по смыслу, точные реквизиты и редакции норм должны быть проверены юристом перед любым использованием. Требование «проконсультироваться с юристами» зафиксировано участниками кооператива ранее (голосовая заметка 23.09.2026).
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h2>0. Идея документа</h2>
|
||||||
|
<p>Вайоминг сделал из DAO юридическое лицо: не новую форму, а <b>режим поверх существующей LLC</b>. Для Беларуси тот же ход даёт готовая форма — <b>производственный кооператив (артель)</b>: она member-owned, предполагает коллегиальное управление и распределение результата по участию, а «цифровой слой» поверх неё легализован <b>Декретом №8 «О развитии цифровой экономики»</b> (ПВТ): права можно закреплять в токенах, а сделки совершать смарт-контрактами.</p>
|
||||||
|
<blockquote>Формула: <b>кооператив (юрлицо) + токены (учёт прав) + смарт-контракты (исполнение решений)</b> — белорусский эквивалент вайомингской DAO LLC.</blockquote>
|
||||||
|
|
||||||
|
<h2>1. Статус юридического лица</h2>
|
||||||
|
<div class="card">
|
||||||
|
<p><b>Вайоминг (§104):</b> DAO — это LLC, в уставе которой есть заявление о DAO-статусе и NOTICE OF RESTRICTIONS; в названии — «DAO LLC».<span class="tag t-wy">WY</span></p>
|
||||||
|
<p><b>Адаптация для РБ:</b> регистрируется производственный кооператив в ЕГРЮЛ. В уставе — раздел «Цифровой режим управления»: фиксируется, что (а) права членов учитываются наряду с реестром в распределённом реестре (блокчейне), (б) решения органов оформляются on-chain операциями, (в) действует внутренний протокол «Конституция кооператива» (код + текст), на который устав прямо ссылается.</p>
|
||||||
|
<span class="law-ref">Право РБ: ст. 104–113 ГК РБ (производственный кооператив/артель); устав обязателен, регистрация в ЕГР; фирменное наименование с указанием «производственный кооператив»/«артель».</span>
|
||||||
|
<p><span class="tag t-risk">риск</span> В РБ нет аналога «DAO-статуса» как регистрационной пометки — цифровой режим существует только внутри устава и локальных актов. Это не блокер: вайомингский статус тоже по сути содержательный, а не регистрационный.</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h2>2. Применимое право</h2>
|
||||||
|
<div class="card">
|
||||||
|
<p><b>Вайоминг (§103):</b> DAO Supplement + весь LLC Act поверх, кроме противоречий.<span class="tag t-wy">WY</span></p>
|
||||||
|
<p><b>Адаптация для РБ:</b> иерархия источников: (1) ГК РБ и законодательство о юрлицах; (2) закон о регистре/Декрет №8 для цифрового слоя; (3) устав кооператива; (4) внутренние акты — «Конституция» (протокол управления); (5) регламенты органов.</p>
|
||||||
|
<span class="law-ref">Право РБ: Декрет №8 от 21.12.2017 «О развитии цифровой экономики» — правовой режим ПВТ: токены, смарт-контракты, операции с ними; для полного эффекта кооперативу целесообразен статус резидента ПВТ.</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h2>3. Органы управления и смарт-контракты</h2>
|
||||||
|
<div class="card">
|
||||||
|
<p><b>Вайоминг (§106, §109):</b> управляют члены и смарт-контракты; публичный идентификатор контракта обязателен, за 30 дней не дали — роспуск.<span class="tag t-wy">WY</span></p>
|
||||||
|
<p><b>Адаптация для РБ:</b> классическая тройка кооператива сохраняется — <b>общее собрание</b> (высший орган), <b>правление</b>, <b>председатель/директор</b>. Смарт-контракт не заменяет органы, а <b>исполняет их решения</b>: голосования собрания проводятся on-chain, итог автоматически оформляется записью в реестре.</p>
|
||||||
|
<ul>
|
||||||
|
<li><b>Кворум и решения</b> — параметры смарт-контракта, совпадающие с уставными (пример: кворум ⅔, решения большинства от принявших участие).</li>
|
||||||
|
<li><b>Публичный адрес контракта</b> — аналог вайомингского идентификатора: указывается в уставе; изменение = уставная процедура. Неактуальный адрес блокирует on-chain операции (мягкий аналог вайомингского роспуска — операционный стоп).</li>
|
||||||
|
<li><b>Обновляемость:</b> контракт upgradable через правление + собрание (двойное подтверждение), история версий публична.</li>
|
||||||
|
</ul>
|
||||||
|
<span class="law-ref">Право РБ: высший орган производственного кооператива — общее собрание; текущее управление — правление/председатель; компетенция и порядок — устав.</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h2>4. Иерархия норм: контракт vs устав</h2>
|
||||||
|
<div class="card">
|
||||||
|
<p><b>Вайоминг (§115):</b> смарт-контракт > устав > operating agreement.<span class="tag t-wy">WY</span></p>
|
||||||
|
<p><b>Ключевое отличие:</b> в РБ так нельзя — юридически устав всегда выше. Решение — <b>однонаправленная синхронизация:</b> устав признаёт записи реестра доказательством состояний (состав членов, голоса, решения), а смарт-контракт выступает «исполнительным механизмом» уставных процедур. Коллизия разрешается в пользу устава и решения собрания; fork контракта возможен только решением собрания.</p>
|
||||||
|
<table>
|
||||||
|
<tr><th>Слой</th><th>Вайоминг DAO</th><th>Кооператив РБ</th></tr>
|
||||||
|
<tr><td>Юридическая норма</td><td>Supplement + LLC Act</td><td>ГК РБ + Декрет №8</td></tr>
|
||||||
|
<tr><td>Учредительный документ</td><td>Articles (DAO-статус)</td><td>Устав кооператива</td></tr>
|
||||||
|
<tr><td>Operating agreement</td><td>Да</td><td>«Конституция» (протокол управления)</td></tr>
|
||||||
|
<tr><td>Что выше при конфликте</td><td>Смарт-контракт</td><td>Устав и собрание (контракт — исполнение)</td></tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h2>5. Обязанности членов (фидуциарный вопрос)</h2>
|
||||||
|
<div class="card">
|
||||||
|
<p><b>Вайоминг (§110):</b> fiduciary duties по умолчанию отключены, остаётся implied covenant of good faith.<span class="tag t-wy">WY</span></p>
|
||||||
|
<p><b>Адаптация для РБ:</b> «отключить» обязанности членов уставом нельзя — но кооперативная форма и не предполагает жёстких фидуциарных обязанностей как корпорация. Практически: в уставе фиксируется <b>минимальный стандарт добросовестности</b> (запрет конфликтных сделок без раскрытия, запрет подрыва платформы), а детально регулирует «Конституция».</p>
|
||||||
|
<p><span class="tag t-diff">отличие</span> РБ-режим защитнее вайомингского: полный отказ от обязанностей недоступен, зато это снижает репутационные риски при работе с партнёрами (ПВТ, институционалы).</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h2>6. Пай, доли и голоса</h2>
|
||||||
|
<div class="card">
|
||||||
|
<p><b>Вайоминг (§111):</b> доля пропорциональна digital-взносу, либо 1 член = 1 голос.<span class="tag t-wy">WY</span></p>
|
||||||
|
<p><b>Адаптация для РБ:</b> классика кооператива — <b>1 член = 1 голос</b> (это и есть «member-weight=1» из вайомингского правила по умолчанию). Паевой взнос учитывается в токенах на адресах членов: токен = <b>инструмент учёта пая и доли в фонде кооператива</b>, не эмиссия «ценной бумаги» в смысле законодательства о ценных бумагах. Трудовое участие (основание распределения прибыли) протоколируется on-chain как вклад.</p>
|
||||||
|
<span class="law-ref">Право РБ: прибыль производственного кооператива распределяется, как правило, в соответствии с трудовым участием; пай и порядок возврата пая — устав. Минимум членов — 3; лимит «нетрудовых» членов устанавливается законодательством.</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h2>7. Информация и прозрачность</h2>
|
||||||
|
<div class="card">
|
||||||
|
<p><b>Вайоминг (§112):</b> если данные в открытом блокчейне — права на отчётность нет.<span class="tag t-wy">WY</span></p>
|
||||||
|
<p><b>Адаптация для РБ:</b> реестр членов, голосования, решения и версии контракта публикуются в открытом распределённом реестре. Отчётность для членов и третьих лиц обеспечивается самим реестром + уставной нормой «запись реестра = доказательство». Личные данные — вне реестра (псевдонимизация адресов), соответствие законодательству о защите персональных данных.</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h2>8. Вход и выход</h2>
|
||||||
|
<div class="card">
|
||||||
|
<p><b>Вайоминг (§113):</b> только по правилам устава; по умолчанию выход = потеря всех прав.<span class="tag t-wy">WY</span></p>
|
||||||
|
<p><b>Адаптация для РБ:</b> приём — решение собрания (on-line голосование + запись в реестре + юр. действие в ЕГР). Выход — заявление члена, кооператив обязан выплатить <b>паевой взнос и причитающуюся долю</b> в порядке и сроки устава (в цифровом слое — расчёт смарт-контрактом). Ключевое отличие от Вайоминга: полностью «обнулить» выходящего нельзя — пай подлежит возврату; токены членства сжигаются, имущественные права — по уставу.</p>
|
||||||
|
<p><span class="tag t-diff">отличие</span> РБ защищает выходящего сильнее вайомингского дефолта. Плюс для доверия партнёров, минус — нужен ликвидностный буфер в уставе (норма о фонде выплаты паёв).</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h2>9. Реорганизация и ликвидация</h2>
|
||||||
|
<div class="card">
|
||||||
|
<p><b>Вайоминг (§114):</b> роспуск: срок, большинство >50%, событие контракта, год бездействия и т.д.<span class="tag t-wy">WY</span></p>
|
||||||
|
<p><b>Адаптация для РБ:</b> в основу берутся основания ГК РБ (решение собрания, суд, реорганизация); цифровой слой добавляет «on-chain триггеры»: смарт-контракт может <i>инициировать</i> голосование о ликвидации (например, по событию «1 год без операций» — прямой аналог вайомингского dormancy), но юридическую силу имеет только решение собрания и регистратор. После ликвидации — выплата паёв, контракт замораживается (archive-режим).</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h2>10. Иностранные члены</h2>
|
||||||
|
<div class="card">
|
||||||
|
<p><b>Вайоминг (§116):</b> foreign DAO не сертифицируется — только местное формирование.<span class="tag t-wy">WY</span></p>
|
||||||
|
<p><b>Адаптация для РБ:</b> члены — физлица и юрлица, включая иностранных; ограничений по национальности членов устав не вводит. Кросс-граничный вывод: для работы DAO-кооператива в других юрисдикциях не создаётся «иностранная копия» — заключается договорная модель (дочерние структуры/партнёрства), ядро прав остаётся в РБ-кооперативе. Для внешних DAO-LLC (Вайоминг), желающих войти в экосистему, — членство юрлица через представителя.</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h2>11. Сводная таблица соответствия</h2>
|
||||||
|
<table>
|
||||||
|
<tr><th>#</th><th>Wyoming DAO Supplement</th><th>Эквивалент РБ</th><th>Статус</th></tr>
|
||||||
|
<tr><td>104</td><td>DAO-статус LLC в уставе</td><td>«Цифровой режим управления» в уставе кооператива</td><td><span class="tag t-ok">переносимо</span></td></tr>
|
||||||
|
<tr><td>103</td><td>Supplement + LLC Act</td><td>ГК РБ + Декрет №8 + устав</td><td><span class="tag t-ok">переносимо</span></td></tr>
|
||||||
|
<tr><td>106/109</td><td>Управление членами и контрактами</td><td>Собрание + правление; контракт исполняет решения</td><td><span class="tag t-ok">переносимо</span></td></tr>
|
||||||
|
<tr><td>115</td><td>Контракт > устава</td><td>Устав выше; реестр = доказательство состояний</td><td><span class="tag t-diff">различие</span></td></tr>
|
||||||
|
<tr><td>110</td><td>Fiduciary duties off</td><td>Стандарт добросовестности в уставе</td><td><span class="tag t-diff">различие</span></td></tr>
|
||||||
|
<tr><td>111</td><td>Взнос digital assets / 1 член = 1 голос</td><td>1 член = 1 голос; пай в токенах учёта</td><td><span class="tag t-ok">переносимо</span></td></tr>
|
||||||
|
<tr><td>112</td><td>Открытый блокчейн = отчётность</td><td>Открытый реестр + псевдонимизация</td><td><span class="tag t-ok">переносимо</span></td></tr>
|
||||||
|
<tr><td>113</td><td>Выход = потеря прав</td><td>Выход с выплатой пая (обязательна)</td><td><span class="tag t-diff">различие</span></td></tr>
|
||||||
|
<tr><td>114</td><td>Триггеры роспуска контрактом</td><td>On-chain триггер → голосование → юр. ликвидация</td><td><span class="tag t-ok">переносимо</span></td></tr>
|
||||||
|
<tr><td>116</td><td>Без foreign DAO</td><td>Иностранные члены да; «копий» юрлица нет</td><td><span class="tag t-ok">переносимо</span></td></tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<h2>12. Что дальше (дорожная карта)</h2>
|
||||||
|
<ol class="cl">
|
||||||
|
<li><span class="num">1.</span> <b>Юридическая экспертиза:</b> подтвердить статьи ГК РБ и объём Декрета №8 для выбранной модели; уточнить режим токена учёта пая с юристом ПВТ.</li>
|
||||||
|
<li><span class="num">2.</span> <b>Резидентство ПВТ:</b> подать документы — без него цифровой слой (токены, смарт-контракты) не получает льготного режима Декрета №8.</li>
|
||||||
|
<li><span class="num">3.</span> <b>Конституция v1:</b> зафиксировать протокол управления (кворумы, роли, обновление контракта) — демо: t-261014121824.</li>
|
||||||
|
<li><span class="num">4.</span> <b>Устав:</b> внести разделы «Цифровой режим управления», «Фонд выплаты паёв», «Стандарт добросовестности».</li>
|
||||||
|
<li><span class="num">5.</span> <b>Пилот on-chain решений:</b> вести собрания правления через реестр параллельно с бумажным протоколом (dual-running) до полной уверенности.</li>
|
||||||
|
</ol>
|
||||||
|
|
||||||
|
<footer class="doc">
|
||||||
|
Кооператив 42 · черновик законов РБ (адаптация Wyoming DAO Supplement, W.S. 17-31) · артефакт t-261014120323-20b83f · не является юридическим заключением; перед регистрационными действиями — обязательная проверка юристом.
|
||||||
|
</footer>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
205
artifacts/pvt-blockchain-constitution-demo.html
Normal file
205
artifacts/pvt-blockchain-constitution-demo.html
Normal file
|
|
@ -0,0 +1,205 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="ru">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Конституция кооператива на блокчейне — интерактивное демо для ПВТ</title>
|
||||||
|
<style>
|
||||||
|
:root{--ink:#0d1117;--bg:#0a0e14;--panel:#11161f;--panel2:#161d29;--line:#232c3b;--mut:#8b96a8;--acc:#4d8dff;--acc2:#22c55e;--warn:#f59e0b;--bad:#ef4444;--vio:#a78bfa}
|
||||||
|
*{box-sizing:border-box}
|
||||||
|
body{margin:0;font-family:'SF Mono','Cascadia Code',Consolas,monospace;background:var(--bg);color:#dbe2ee;font-size:14.5px;line-height:1.6}
|
||||||
|
.wrap{max-width:1060px;margin:0 auto;padding:28px 18px 90px}
|
||||||
|
header{border:1px solid var(--line);border-radius:14px;padding:22px 26px;margin-bottom:22px;background:linear-gradient(135deg,#101726,#0d1420)}
|
||||||
|
.kicker{font-size:11px;letter-spacing:.22em;text-transform:uppercase;color:var(--acc);font-weight:700}
|
||||||
|
h1{margin:8px 0 6px;font-size:26px;line-height:1.25}
|
||||||
|
.sub{color:var(--mut);font-size:13px}
|
||||||
|
.cols{display:grid;grid-template-columns:300px 1fr;gap:18px}
|
||||||
|
@media(max-width:860px){.cols{grid-template-columns:1fr}}
|
||||||
|
.panel{background:var(--panel);border:1px solid var(--line);border-radius:12px;overflow:hidden}
|
||||||
|
.panel h3{margin:0;padding:12px 16px;font-size:12px;letter-spacing:.14em;text-transform:uppercase;color:var(--mut);border-bottom:1px solid var(--line);background:var(--panel2)}
|
||||||
|
.pbody{padding:14px 16px}
|
||||||
|
.member{display:flex;align-items:center;gap:10px;padding:9px 10px;border:1px solid var(--line);border-radius:10px;margin:8px 0;background:var(--panel2);cursor:pointer;transition:border-color .15s}
|
||||||
|
.member:hover{border-color:var(--acc)}
|
||||||
|
.member.sel{border-color:var(--acc);box-shadow:0 0 0 1px var(--acc) inset}
|
||||||
|
.avatar{width:30px;height:30px;border-radius:8px;display:flex;align-items:center;justify-content:center;font-size:14px;font-weight:700;color:#fff}
|
||||||
|
.mname{font-weight:700;font-size:13.5px}
|
||||||
|
.mrole{font-size:11px;color:var(--mut)}
|
||||||
|
.voteweight{margin-left:auto;font-size:11px;color:var(--vio)}
|
||||||
|
.ledger{background:#080b10;border-radius:10px;border:1px solid var(--line);padding:12px;max-height:340px;overflow-y:auto;font-size:12px}
|
||||||
|
.tx{border-left:2px solid var(--acc2);padding:6px 10px;margin:7px 0;background:#0c121b;border-radius:0 8px 8px 0}
|
||||||
|
.tx.gov{border-left-color:var(--vio)}
|
||||||
|
.tx.peg{border-left-color:var(--acc)}
|
||||||
|
.tx.err{border-left-color:var(--bad)}
|
||||||
|
.txhead{display:flex;justify-content:space-between;gap:8px;font-weight:700;color:#e8eefb;font-size:11.5px}
|
||||||
|
.txbody{color:var(--mut);margin-top:3px;white-space:pre-wrap}
|
||||||
|
.ok{color:var(--acc2);font-weight:700}.warn{color:var(--warn);font-weight:700}.err{color:var(--bad);font-weight:700}.vio{color:var(--vio)}
|
||||||
|
.btnrow{display:flex;flex-wrap:wrap;gap:10px;margin:14px 0}
|
||||||
|
button{font-family:inherit;font-size:12.5px;font-weight:700;padding:10px 14px;border-radius:10px;border:1px solid var(--acc);background:#15243f;color:#bcd6ff;cursor:pointer;transition:.15s}
|
||||||
|
button:hover{background:#1b3157}
|
||||||
|
button:disabled{opacity:.35;cursor:not-allowed}
|
||||||
|
button.ghost{border-color:var(--line);background:var(--panel2);color:var(--mut)}
|
||||||
|
.kv{display:grid;grid-template-columns:auto 1fr;gap:4px 12px;font-size:12.5px}
|
||||||
|
.kv b{color:var(--mut);font-weight:400}
|
||||||
|
.bar{height:10px;background:#0b1119;border:1px solid var(--line);border-radius:99px;overflow:hidden;display:flex}
|
||||||
|
.seg{height:100%}
|
||||||
|
.legend{display:flex;flex-wrap:wrap;gap:12px;font-size:11px;color:var(--mut);margin-top:8px}
|
||||||
|
.dot{display:inline-block;width:9px;height:9px;border-radius:3px;margin-right:5px;vertical-align:-1px}
|
||||||
|
.note{background:#101726;border:1px solid var(--line);border-radius:10px;padding:12px 14px;font-size:12px;color:var(--mut);margin-top:12px}
|
||||||
|
.note b{color:#c8d4e6}
|
||||||
|
.sec2{margin-top:22px}
|
||||||
|
.timeline{display:grid;gap:10px}
|
||||||
|
.step{display:grid;grid-template-columns:34px 1fr;gap:12px;align-items:start}
|
||||||
|
.n{width:30px;height:30px;border-radius:9px;background:#15243f;border:1px solid var(--acc);color:#bcd6ff;display:flex;align-items:center;justify-content:center;font-weight:700;font-size:13px}
|
||||||
|
.step .t{font-weight:700;font-size:13.5px}
|
||||||
|
.step .d{color:var(--mut);font-size:12.5px}
|
||||||
|
footer{margin-top:34px;border-top:1px solid var(--line);padding-top:14px;font-size:11.5px;color:var(--mut)}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="wrap">
|
||||||
|
|
||||||
|
<header>
|
||||||
|
<div class="kicker">Кооператив 42 · Интерактивное демо для ПВТ</div>
|
||||||
|
<h1>Конституция кооператива на блокчейне</h1>
|
||||||
|
<div class="sub">Как устав и законы кооператива исполняются on-chain: голосование → решение → юридическое действие. Смарт-контракт — исполнительный механизм, устав — источник власти (ГК РБ + Декрет №8). Основа контента: док законов t-261014120323.</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<div class="cols">
|
||||||
|
<div>
|
||||||
|
<div class="panel">
|
||||||
|
<h3>Реестр членов</h3>
|
||||||
|
<div class="pbody">
|
||||||
|
<div class="kv" style="margin-bottom:10px"><b>Всего членов:</b><span id="mcount">—</span></div>
|
||||||
|
<div id="members"></div>
|
||||||
|
<div class="note" style="margin-top:10px">Каждый член — адрес в реестре. <b>1 член = 1 голос</b> (§111 WY / устав). Кликните — проголосовать от имени члена.</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="panel" style="margin-top:16px">
|
||||||
|
<h3>Смарт-контракт</h3>
|
||||||
|
<div class="pbody">
|
||||||
|
<div class="kv">
|
||||||
|
<b>Адрес:</b><span>0x42…C0NF (в уставе)</span>
|
||||||
|
<b>Версия:</b><span id="cver">v1.0</span>
|
||||||
|
<b>Кворум:</b><span id="cq">—</span>
|
||||||
|
<b>Порог:</b><span>50%+1 от голосовавших</span>
|
||||||
|
<b>Upgradable:</b><span class="ok">да, dual-confirm</span>
|
||||||
|
</div>
|
||||||
|
<div class="note">Публичный адрес контракта фиксируется в уставе (аналог §109 WY: неактуальный идентификатор блокирует on-chain операции).</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<div class="panel">
|
||||||
|
<h3>Активное предложение (on-chain governance)</h3>
|
||||||
|
<div class="pbody">
|
||||||
|
<div id="proposal" class="tx gov"><div class="txhead"><span>PROPOSAL-1 · governance</span><span id="pstate">открыто</span></div><div class="txbody">Принять Конституцию кооператива v1: цифровой режим управления, учёт паёв в токенах, решения органов оформляются on-chain.</div></div>
|
||||||
|
<div style="margin:12px 0 6px;font-size:12px;color:var(--mut)">Явка / кворум ⅔</div>
|
||||||
|
<div class="bar" id="bar"></div>
|
||||||
|
<div class="legend">
|
||||||
|
<span><span class="dot" style="background:#22c55e"></span>За</span>
|
||||||
|
<span><span class="dot" style="background:#ef4444"></span>Против</span>
|
||||||
|
<span><span class="dot" style="background:#232c3b"></span>Не голосовали</span>
|
||||||
|
<span id="tally" style="margin-left:auto"></span>
|
||||||
|
</div>
|
||||||
|
<div class="btnrow">
|
||||||
|
<button id="btnVote" onclick="castVote()">Проголосовать (за)</button>
|
||||||
|
<button class="ghost" onclick="castVote(true)">Против</button>
|
||||||
|
<button id="btnExec" onclick="executeProposal()" disabled>Исполнить решение</button>
|
||||||
|
<button class="ghost" onclick="location.reload()">Сброс демо</button>
|
||||||
|
</div>
|
||||||
|
<div id="verdict" class="note">Выберите члена слева и проголосуйте. Когда явка достигнет кворума и большинство — «за», кнопка «Исполнить решение» активируется: смарт-контракт оформит решение собрания on-chain.</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="panel" style="margin-top:16px">
|
||||||
|
<h3>Журнал реестра (ledger)</h3>
|
||||||
|
<div class="ledger" id="ledger">
|
||||||
|
<div class="tx peg"><div class="txhead"><span>GENESIS · peg</span><span>#0</span></div><div class="txbody">Реестр привязан к уставу кооператива (РБ): состав органов, паи, правила — из юрдокумента. Токен = учёт пая, не ценная бумага (Декрет №8).</div></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="panel sec2">
|
||||||
|
<h3>Как решение становится юридическим фактом (5 шагов)</h3>
|
||||||
|
<div class="pbody timeline">
|
||||||
|
<div class="step"><div class="n">1</div><div><div class="t">Предложение (proposal)</div><div class="d">Правление вносит предложение on-chain. В уставе закреплено: повестка собрания = активные предложения реестра.</div></div></div>
|
||||||
|
<div class="step"><div class="n">2</div><div><div class="t">Голосование членов (1 член = 1 голос)</div><div class="d">Члены голосуют подписью со своего адреса. Тайна личности — псевдонимизация, вес — по уставу, не по капиталу.</div></div></div>
|
||||||
|
<div class="step"><div class="n">3</div><div><div class="t">Кворум + порог</div><div class="d">Смарт-контракт считает явку и голоса по уставным параметрам (здесь: кворум ⅔, порог 50%+1). Невозможность «накрутки» — математика реестра.</div></div></div>
|
||||||
|
<div class="step"><div class="n">4</div><div><div class="t">Исполнение (execute)</div><div class="d">Контракт оформляет решение операцией в реестре; для решений, требующих юрдействия (реестр юрлиц, договоры), формируется пакет для подписи председателем — принцип «контракт исполняет, устав властвует».</div></div></div>
|
||||||
|
<div class="step"><div class="n">5</div><div><div class="t">Прозрачность (audit)</div><div class="d">Любой партнёр (ПВТ, аудит, член) проверяет историю голосований и решений в открытом реестре — без запроса отчётности (§112 WY).</div></div></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="panel sec2">
|
||||||
|
<h3>Привязка к праву РБ (для юристов ПВТ)</h3>
|
||||||
|
<div class="pbody" style="font-size:13px">
|
||||||
|
<ul style="margin:0;padding-left:18px;color:#c3cddd">
|
||||||
|
<li><b>Юрлицо:</b> производственный кооператив (ГК РБ, ст. 104–113) — устав обязателен, собрание — высший орган.</li>
|
||||||
|
<li><b>Цифровой слой:</b> Декрет №8 «О развитии цифровой экономики» — токены (учёт пая), смарт-контракты (оформление сделок/решений), резидентство ПВТ.</li>
|
||||||
|
<li><b>Иерархия:</b> устав выше контракта — реестр признаётся доказательством состояний, контракт — исполнительный механизм; коллизии решает собрание.</li>
|
||||||
|
<li><b>Защита членов:</b> выход — с выплатой пая (обязательна); контрактом это автоматизируется, но не отменяется.</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<footer>Демо-симуляция работы Конституции кооператива on-chain · Кооператив 42 · артефакт t-261014121824-a70757 · контент по док законов РБ (t-261014120323) · не юрзаключение, для показа продукта партнёрам ПВТ.</footer>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
const MEMBERS=[
|
||||||
|
{n:'Алекс',r:'председатель / продукт',c:'#4d8dff'},
|
||||||
|
{n:'Юра',r:'правление / операции',c:'#22c55e'},
|
||||||
|
{n:'Марина',r:'финансы и договоры',c:'#f59e0b'},
|
||||||
|
{n:'Дима',r:'инженерия',c:'#a78bfa'},
|
||||||
|
{n:'Стас',r:'партнёрства',c:'#ef4444'},
|
||||||
|
{n:'Наталья',r:'юрист-консультант',c:'#14b8a6'}
|
||||||
|
];
|
||||||
|
let sel=null, votes={}, done=false, ntx=0;
|
||||||
|
const QUORUM=Math.ceil(MEMBERS.length*2/3); // ⅔
|
||||||
|
function $(id){return document.getElementById(id)}
|
||||||
|
function log(kind,head,body){ntx++;const d=document.createElement('div');d.className='tx '+kind;d.innerHTML='<div class="txhead"><span>'+head+'</span><span>#'+ntx+'</span></div><div class="txbody">'+body+'</div>';$('ledger').prepend(d)}
|
||||||
|
function renderMembers(){
|
||||||
|
$('mcount').textContent=MEMBERS.length+' (3 физлица минимум по ГК — норма соблюдена)';
|
||||||
|
$('members').innerHTML=MEMBERS.map((m,i)=>'<div class="member'+(sel===i?' sel':'')+'" onclick="pick('+i+')"><div class="avatar" style="background:'+m.c+'">'+m.n[0]+'</div><div><div class="mname">'+m.n+'</div><div class="mrole">'+m.r+'</div></div><div class="voteweight">1 голос</div></div>').join('');
|
||||||
|
}
|
||||||
|
function pick(i){sel=i;renderMembers();log('peg','SELECT_SIGNER','подписант: '+MEMBERS[i].n+' ('+MEMBERS[i].r+') — голос будет подписан адресом члена')}
|
||||||
|
function renderVotes(){
|
||||||
|
const v=MEMBERS.map((m,i)=>votes[i]);
|
||||||
|
const yes=v.filter(x=>x===true).length,no=v.filter(x=>x===false).length,abst=MEMBERS.length-yes-no;
|
||||||
|
$('bar').innerHTML='<div class="seg" style="width:'+(yes/MEMBERS.length*100)+'%;background:#22c55e"></div><div class="seg" style="width:'+(no/MEMBERS.length*100)+'%;background:#ef4444"></div>';
|
||||||
|
$('tally').innerHTML='<span class="ok">'+yes+' за</span> · <span class="err">'+no+' против</span> · '+abst+' молчат';
|
||||||
|
const turnout=yes+no;
|
||||||
|
$('cq').textContent=turnout+'/'+MEMBERS.length+' (нужно '+QUORUM+')';
|
||||||
|
const q=turnout>=QUORUM, pass=q&&yes>no;
|
||||||
|
if(!done){
|
||||||
|
$('btnExec').disabled=!pass;
|
||||||
|
$('pstate').innerHTML=pass?'<span class="ok">кворум есть · большинство ЗА</span>':'<span class="warn">ждёт голосов</span>';
|
||||||
|
if(pass)$('verdict').innerHTML='<b>Кворум достигнут, большинство — за.</b> Нажмите «Исполнить решение»: смарт-контракт оформит решение собрания операцией в реестре (шаг 4).';
|
||||||
|
else $('verdict').innerHTML='Явка '+(turnout)+' из '+MEMBERS.length+', кворум '+QUORUM+'. Голосуйте от имени разных членов (клик слева → кнопка).';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function castVote(against){
|
||||||
|
if(done){log('err','ERROR','предложение уже исполнено — реестр неизменяем');return}
|
||||||
|
if(sel===null){log('err','ERROR','выберите члена в реестре слева');return}
|
||||||
|
if(votes[sel]!==undefined){log('err','REJECT','двойной голос отклонён: адрес '+MEMBERS[sel].n+' уже голосовал (анти-манипуляция реестра)');return}
|
||||||
|
votes[sel]=!against;
|
||||||
|
log('gov','VOTE',''+MEMBERS[sel].n+' → '+(against?'ПРОТИВ':'ЗА')+' · подпись адреса принята');
|
||||||
|
renderVotes();
|
||||||
|
}
|
||||||
|
function executeProposal(){
|
||||||
|
const yes=Object.values(votes).filter(x=>x).length;
|
||||||
|
if($('btnExec').disabled){log('err','EXECUTE_REJECT','условия устава не выполнены (кворум/порог)');return}
|
||||||
|
done=true;
|
||||||
|
$('pstate').innerHTML='<span class="ok">ИСПОЛНЕНО</span>';
|
||||||
|
log('gov','EXECUTE','решение собрания оформлено: Конституция v1 принята ('+yes+' за, кворум '+QUORUM+') · запись реестра = протокол собрания');
|
||||||
|
log('peg','LEGAL_BRIDGE','сформирован пакет юрдействий: протокол для подписи председателем, ссылка на запись реестра — устав выше контракта, контракт его исполняет');
|
||||||
|
$('verdict').innerHTML='<b>Готово.</b> Решение зафиксировано в реестре (неизменяемо), юрпакет передан на подпись. Дальше по дорожной карте: юристы ПВТ → резидентство → устав с «цифровым режимом» → dual-running пилот.';
|
||||||
|
$('btnVote').disabled=true;$('btnExec').disabled=true;
|
||||||
|
}
|
||||||
|
renderMembers();renderVotes();
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
@ -117,6 +117,131 @@ def _wire_tool_budget(agent, max_calls: int = 30):
|
||||||
return agent
|
return agent
|
||||||
|
|
||||||
|
|
||||||
|
# ── e-261014163808-4da3ec: TTFT-watchdog + ретрай висящего вызова ──────────
|
||||||
|
#
|
||||||
|
# Диагноз (13:08 и 16:11 14-10-2026, павшие hour-тики): 9router принимает
|
||||||
|
# POST и открывает стрим, но НИ ОДНОГО байта контента не приходит — в логе
|
||||||
|
# роутера «▶ POST ... STREAM» есть, «📊 DONE» после него нет. Клиентский
|
||||||
|
# timeout=90 и SDK-ретраи не спасали: 4 попытки × тот же висящий апстрим,
|
||||||
|
# в итоге APITimeoutError уносил ВЕСЬ тик (TaskFailed по батчу, CycleError,
|
||||||
|
# эскалация). Воркер — subprocess, повторить механизм с уровня нельзя,
|
||||||
|
# поэтому фикс в самом воркере.
|
||||||
|
#
|
||||||
|
# Решение: обёртка стрима модели. Признак живого апстрима — первый
|
||||||
|
# content-chunk; пока его нет, watchdog: N секунд без токена → стрим
|
||||||
|
# закрываем и делаем СВЕЖИЙ вызов (новый POST — роутер сам уводит его на
|
||||||
|
# живой апстрим). Начавший отвечать апстрим не трогаем: обрыв на середине
|
||||||
|
# ответа дороже редкого зависания.
|
||||||
|
#
|
||||||
|
# Гарантии: strands дождётся события message_stop новой попытки — цикл
|
||||||
|
# агента консистентен; BudgetSpent идемпотентен (usage по фактически
|
||||||
|
# дошедшим assistant-сообщениям); инструменты исполняются после конца
|
||||||
|
# стрима, так что дублей tool-вызовов нет.
|
||||||
|
|
||||||
|
_TTFT_FIRST_TOKEN_BUDGET = float(os.environ.get("SIMBIOT_LLM_TTFT_BUDGET", "45"))
|
||||||
|
|
||||||
|
|
||||||
|
async def _aclose_gen(gen):
|
||||||
|
"""Мягко закрыть асинхронный генератор (отмена висящего стрима)."""
|
||||||
|
close = getattr(gen, "aclose", None)
|
||||||
|
if close is not None:
|
||||||
|
try:
|
||||||
|
await close()
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def _chunk_type(chunk) -> str:
|
||||||
|
"""Тип чанка стрима strands: атрибут или ключ словаря."""
|
||||||
|
ct = getattr(chunk, "chunk_type", None)
|
||||||
|
if not ct and isinstance(chunk, dict):
|
||||||
|
ct = chunk.get("chunk_type")
|
||||||
|
return ct or ""
|
||||||
|
|
||||||
|
|
||||||
|
def _with_ttft_retry(model, level: str, call_timeout: float):
|
||||||
|
"""Обернуть модель: watchdog первого токена + ретраи висящих вызовов.
|
||||||
|
|
||||||
|
first_budget — сколько ждать ПЕРВЫЙ content-chunk; capped сверху
|
||||||
|
клиентским call_timeout (старый таймаут продолжает страховать весь
|
||||||
|
вызов на случай совсем глухого апстрима). Попыток 3: нормальный
|
||||||
|
TTFT роутера 2-13с (лог), 45с — запас x3; 3×45 + ретраи SDK не
|
||||||
|
пробивают tick_timeout уровня (min 480с, hour 2880с).
|
||||||
|
"""
|
||||||
|
import asyncio
|
||||||
|
import time as _time
|
||||||
|
|
||||||
|
first_budget = min(_TTFT_FIRST_TOKEN_BUDGET, max(float(call_timeout), 1.0))
|
||||||
|
max_attempts = 3
|
||||||
|
base_stream = model.stream
|
||||||
|
|
||||||
|
def _note_retry(attempt: int, why: str):
|
||||||
|
# след в стор: сколько раз сработал watchdog (видно из трейса тика)
|
||||||
|
try:
|
||||||
|
from harness import es
|
||||||
|
es.append_event("trace", f"ttft-retry:{level}", "TTFTWatchdogRetry", {
|
||||||
|
"level": level, "attempt": attempt, "reason": why,
|
||||||
|
"first_token_budget_s": first_budget,
|
||||||
|
})
|
||||||
|
except Exception:
|
||||||
|
pass # телеметрия — best effort
|
||||||
|
|
||||||
|
class _TTFTModel:
|
||||||
|
"""Прозрачный прокси: всё делегируется модели, stream() — под watchdog.
|
||||||
|
|
||||||
|
Не наследуем Model (ABC с abstractmethods): strands_harness.resolve_model
|
||||||
|
принимает по isinstance(model, (Model, ModelRouter)), поэтому после
|
||||||
|
делегирования регистрируемся как Model через Model.register(_TTFTModel)
|
||||||
|
— и делегирование __getattr__, и валидный isinstance для create_harness.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, inner):
|
||||||
|
object.__setattr__(self, "_inner", inner)
|
||||||
|
|
||||||
|
def __getattr__(self, name):
|
||||||
|
return getattr(object.__getattribute__(self, "_inner"), name)
|
||||||
|
|
||||||
|
async def stream(self, *args, **kwargs):
|
||||||
|
attempts = 0
|
||||||
|
while True:
|
||||||
|
attempts += 1
|
||||||
|
gen = base_stream(*args, **kwargs)
|
||||||
|
it = gen.__aiter__()
|
||||||
|
got_content = False
|
||||||
|
try:
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
chunk = await asyncio.wait_for(
|
||||||
|
it.__anext__(), timeout=first_budget)
|
||||||
|
except StopAsyncIteration:
|
||||||
|
# пустой стрим без контента — тоже симптом висения,
|
||||||
|
# но ретраить бессмысленно: считаем ответом и уходим
|
||||||
|
return
|
||||||
|
ct = _chunk_type(chunk)
|
||||||
|
if ct in ("content_delta", "content_start"):
|
||||||
|
got_content = True
|
||||||
|
yield chunk
|
||||||
|
if got_content:
|
||||||
|
break
|
||||||
|
except asyncio.TimeoutError:
|
||||||
|
why = f"{first_budget:.0f}s без первого токена (попытка {attempts})"
|
||||||
|
if attempts < max_attempts:
|
||||||
|
_note_retry(attempts, why)
|
||||||
|
await _aclose_gen(gen)
|
||||||
|
continue # свежий POST: роутер уведёт на живой апстрим
|
||||||
|
raise TimeoutError(
|
||||||
|
f"апстрим завис до первого токена: {max_attempts} попытки "
|
||||||
|
f"по {first_budget:.0f}s — {why}") from None
|
||||||
|
# апстрим живой (контент пошёл) — доедаем без watchdog
|
||||||
|
async for chunk in it:
|
||||||
|
yield chunk
|
||||||
|
return
|
||||||
|
|
||||||
|
from strands.models.model import Model as _StrandsModel
|
||||||
|
_StrandsModel.register(_TTFTModel)
|
||||||
|
return _TTFTModel(model)
|
||||||
|
|
||||||
|
|
||||||
def build_level_model_from_payload(payload: dict):
|
def build_level_model_from_payload(payload: dict):
|
||||||
"""Модель для воркера — та же сборка, что и в runtime.build_level_model:
|
"""Модель для воркера — та же сборка, что и в runtime.build_level_model:
|
||||||
9router понимает chat/completions (usage там же), Responses API — шим без usage.
|
9router понимает chat/completions (usage там же), Responses API — шим без usage.
|
||||||
|
|
@ -144,7 +269,7 @@ def build_level_model_from_payload(payload: dict):
|
||||||
# «Connection error» к роутеру ронял весь тик с первой попытки; openai SDK
|
# «Connection error» к роутеру ронял весь тик с первой попытки; openai SDK
|
||||||
# умеет сам ретраить connection-error/429/5xx с экспонентой — даём запас.
|
# умеет сам ретраить connection-error/429/5xx с экспонентой — даём запас.
|
||||||
max_retries = int(os.environ.get("SIMBIOT_LLM_MAX_RETRIES", "3"))
|
max_retries = int(os.environ.get("SIMBIOT_LLM_MAX_RETRIES", "3"))
|
||||||
return OpenAIModel(
|
base = OpenAIModel(
|
||||||
# timeout открытого клиента (openai SDK) режет каждый chat.completions
|
# timeout открытого клиента (openai SDK) режет каждый chat.completions
|
||||||
# вызов; при зависании апстрима получаем APITimeoutError за N сек и
|
# вызов; при зависании апстрима получаем APITimeoutError за N сек и
|
||||||
# strands event-loop завершает тик штатно (без потери предыдущих шагов)
|
# strands event-loop завершает тик штатно (без потери предыдущих шагов)
|
||||||
|
|
@ -154,6 +279,11 @@ def build_level_model_from_payload(payload: dict):
|
||||||
model_id=model_id,
|
model_id=model_id,
|
||||||
**({"params": params} if params else {}),
|
**({"params": params} if params else {}),
|
||||||
)
|
)
|
||||||
|
# e-261014163808-4da3ec (часовой фикс): ретрай висящего ДО-ПЕРВОГО-ТОКЕНА
|
||||||
|
# апстрим-вызова. Кейс дня (router-лог 16:09:50): POST принят, TTFT так и
|
||||||
|
# не пришёл, ~90с спустя APITimeoutError унёс ВЕСЬ тик вместе с claimed-
|
||||||
|
# батчем. SDK-ретраи не спасали: 4 попытки × 90с висения = тик мёртв.
|
||||||
|
return _with_ttft_retry(base, payload.get("level") or "hour", call_timeout)
|
||||||
|
|
||||||
|
|
||||||
def _emit(payload, obj):
|
def _emit(payload, obj):
|
||||||
|
|
|
||||||
0
tasks.db
Normal file
0
tasks.db
Normal file
103
tests/test_ttft_retry.py
Normal file
103
tests/test_ttft_retry.py
Normal file
|
|
@ -0,0 +1,103 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
"""E2E-проверка TTFT-watchdog обёртки воркера (e-261014163808-4da3ec).
|
||||||
|
|
||||||
|
Три сценария на фейковом стриме:
|
||||||
|
1. живой апстрим (контент сразу) → все чанки, ни одного ретрая;
|
||||||
|
2. висит до первого токена, оживёт на 2-й попытке → ретрай, ответ完整;
|
||||||
|
3. висит всегда (3 попытки) → TimeoutError с внятным текстом.
|
||||||
|
|
||||||
|
Плюс дым: сборка модели payload-ом возвращает прокси с рабочим stream().
|
||||||
|
Запуск: ./venv/bin/python tests/test_ttft_retry.py
|
||||||
|
"""
|
||||||
|
import asyncio
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import types
|
||||||
|
|
||||||
|
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||||
|
|
||||||
|
from harness import _llm_worker as W # noqa: E402
|
||||||
|
|
||||||
|
FAILS = []
|
||||||
|
|
||||||
|
|
||||||
|
def check(name, cond, extra=""):
|
||||||
|
print(("PASS " if cond else "FAIL ") + name + (f" | {extra}" if extra and not cond else ""))
|
||||||
|
if not cond:
|
||||||
|
FAILS.append(name)
|
||||||
|
|
||||||
|
|
||||||
|
def chunk(ct):
|
||||||
|
return {"chunk_type": ct}
|
||||||
|
|
||||||
|
|
||||||
|
def fake_stream_model(script):
|
||||||
|
"""script: список «попыток», каждая — список чанков или raise Hang."""
|
||||||
|
calls = {"n": 0}
|
||||||
|
|
||||||
|
class M:
|
||||||
|
async def stream(self, *a, **k):
|
||||||
|
i = calls["n"]
|
||||||
|
calls["n"] += 1
|
||||||
|
act = script[i]
|
||||||
|
if act == "HANG":
|
||||||
|
await asyncio.sleep(30) # дольше watchdog-бюджета
|
||||||
|
yield chunk("message_start")
|
||||||
|
return
|
||||||
|
for c in act:
|
||||||
|
yield chunk(c)
|
||||||
|
|
||||||
|
return M(), calls
|
||||||
|
|
||||||
|
|
||||||
|
async def run(model):
|
||||||
|
wrapped = W._with_ttft_retry(model, "hour", 90)
|
||||||
|
out = []
|
||||||
|
async for c in wrapped.stream():
|
||||||
|
out.append(W._chunk_type(c))
|
||||||
|
return out
|
||||||
|
|
||||||
|
|
||||||
|
async def main():
|
||||||
|
os.environ["SIMBIOT_LLM_TTFT_BUDGET"] = "0.5" # быстрый watchdog для теста
|
||||||
|
W._TTFT_FIRST_TOKEN_BUDGET = 0.5
|
||||||
|
|
||||||
|
# 1. живой апстрим
|
||||||
|
m, calls = fake_stream_model([
|
||||||
|
["message_start", "content_delta", "content_delta", "message_stop"]])
|
||||||
|
out = await run(m)
|
||||||
|
check("живой стрим проходит целиком",
|
||||||
|
out == ["message_start", "content_delta", "content_delta", "message_stop"],
|
||||||
|
str(out))
|
||||||
|
check("живой стрим — 1 попытка", calls["n"] == 1, str(calls))
|
||||||
|
|
||||||
|
# 2. виснет, оживает на 2-й
|
||||||
|
m, calls = fake_stream_model([
|
||||||
|
"HANG",
|
||||||
|
["message_start", "content_delta", "message_stop"]])
|
||||||
|
out = await run(m)
|
||||||
|
check("зависший вызов ретраится и отвечает",
|
||||||
|
out == ["message_start", "content_delta", "message_stop"], str(out))
|
||||||
|
check("ретрай сделал ровно 2 попытки", calls["n"] == 2, str(calls))
|
||||||
|
|
||||||
|
# 3. виснет всегда
|
||||||
|
m, calls = fake_stream_model(["HANG", "HANG", "HANG"])
|
||||||
|
try:
|
||||||
|
await run(m)
|
||||||
|
check("глухой апстрим даёт TimeoutError", False, "не упал")
|
||||||
|
except TimeoutError as e:
|
||||||
|
check("глухой апстрим даёт TimeoutError", "завис до первого токена" in str(e), str(e))
|
||||||
|
check("глухой апстрим — ровно 3 попытки", calls["n"] == 3, str(calls))
|
||||||
|
|
||||||
|
# 4. делегирование атрибутов прокси
|
||||||
|
m, _ = fake_stream_model([["content_delta"]])
|
||||||
|
m.foo = "bar"
|
||||||
|
wrapped = W._with_ttft_retry(m, "hour", 90)
|
||||||
|
check("прокси делегирует атрибуты", getattr(wrapped, "foo", None) == "bar")
|
||||||
|
|
||||||
|
print()
|
||||||
|
print("ИТОГ:", "ВСЕ ОК" if not FAILS else f"ПРОВАЛЫ: {FAILS}")
|
||||||
|
sys.exit(1 if FAILS else 0)
|
||||||
|
|
||||||
|
|
||||||
|
asyncio.run(main())
|
||||||
Loading…
Reference in a new issue