Commit Graph

3201 Commits

Author SHA1 Message Date
coopops b1b1775277 fix(contracts): migrate voskhod programs.program_type back to 'capital'
Typecheck / desktop (pull_request) Failing after 8m46s
Typecheck / controller (pull_request) Successful in 13m44s
Автоматическая CI/CD-миграция (soviet::migrate) раньше переименовывала
programs.program_type[id=4] voskhod в 'blagorost', но coagreements.type для
той же программы никогда не переименовывался и остаётся 'capital' — отсюда
расхождение двух таблиц на одном и том же боевом кооперативе, которое видно
любым прямым запросом к цепочке и вносит путаницу.

'capital' — каноничное значение по всей цепочке решений в этой сессии: это
то, что реально в coagreements.type, то, на что рассчитывает _capital_program
в lib/consts.hpp, и то, что теперь везде в controller/boot. Новый блок в
migrate() приводит programs.program_type к нему же — идемпотентно, сработает
автоматически на следующем деплое soviet, без ручных действий на проде.
soviet и capital пересобраны локально — компилируются чисто.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-19 12:35:37 +00:00
coopops 5f4281ac01 revert(contracts): _capital_program back to 'capital', drop stray blagorost migration
Typecheck / desktop (pull_request) Failing after 8m52s
Typecheck / controller (pull_request) Failing after 6m44s
Единственный источник правды — продовая цепочка voskhod: coagreements.type
для program_id=4 буквально 'capital' (проверено прямым запросом к
185.70.105.87), это боевое неизменяемое значение. Contract-константа
_capital_program в lib/consts.hpp была переименована в 'blagorost' коммитом
e1538bb5ab (2026-01-26) и никогда не доезжала до прода — из-за этого dev-цепочки
собирали программу с другим именем и падали на 'Недопустимый тип программы'
при попытке засеять её как 'capital'.

Заодно убран автоматический CI/CD-migrate в soviet::migrate(), который на
каждом деплое насильно переименовывал programs.program_type[id=4] voskhod в
'blagorost' — он и был причиной, что programs.program_type на проде уже
'blagorost', а coagreements.type там же остался 'capital' (миграция трогала
только одну из двух таблиц). Раз откатываем контракт на 'capital' насовсем —
эта миграция больше не нужна и мешает: на следующем деплое она снова
рассинхронила бы данные.

Boot-скрипты и BLAGOROST_AGREEMENT_TYPE в controller уже используют 'capital'
(предыдущий коммит) — теперь везде единое значение, dev-цепочка при
pnpm run reboot соберёт programs+coagreements так же, как боевой voskhod, без
какой-либо миграции на проде.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-19 11:58:50 +00:00
coopops d35e88d951 revert(capital): keep BLAGOROST_AGREEMENT_TYPE='capital', fix boot seed drift instead
Typecheck / controller (pull_request) Has been cancelled
Typecheck / desktop (pull_request) Has been cancelled
Прямой запрос к продовой цепочке voskhod (185.70.105.87) показал реальный
on-chain тип программы Благорост — 'capital' (program_id=4), не 'blagorost'.
Прод годами работает с этим значением, ошибок в логах нет. Предыдущий коммит
(5360daa471) поменял константу на 'blagorost' по данным ТОЛЬКО локальной
dev-цепочки mono-ai-2 — для прода это было бы регрессией. Откатываю.

Настоящий источник дрейфа — boot-сидинг: components/boot/src/init/cooperative.ts
createPrograms() и src/scripts/seed-capital/phases/01-programs.ts заводили
новый кооператив (createprog) с type='blagorost', расходясь с давно
задеплоенным продовым значением. Правлю сидинг под реальность, а не наоборот
(та же логика, что 'дрейф стандарт↔код — прав контракт': on-chain состояние
неизменяемо и авторитетно, код подстраивается). Заодно поправлены прочие
литералы 'blagorost' в тестах boot (capital.test.ts, capital-import.test.ts,
08-investments.ts, tests/capital/consts.ts), которые создавали/подписывали
программу под тем же неверным именем.

> monocoop@ reboot /home/admin/mono-ai-2
> cd components/boot/scripts && bash reboot.sh

Останавливаем и удаляем контейнеры с volumes...
Останавливаем blockchain контейнер...
Удаляем blockchain data...
Пересоздаем и запускаем базы данных...
Ждем готовности MongoDB...
MongoDB готов!
Ждем готовности PostgreSQL...
PostgreSQL готов!
Запускаем boot процесс...

> @coopenomics/boot@2026.6.24 boot /home/admin/mono-ai-2/components/boot
> esno src/index.ts boot

Node is healthy. Continue. {
  server_version: 'ee002f93',
  chain_id: 'db79c8409645082749ca50640d6f4ee511575acf26c4e2c8e4748e6bf6a01ed4',
  head_block_num: 10,
  last_irreversible_block_num: 9,
  last_irreversible_block_id: '000000093c561104c62b9058c82eab12559686eba92fdbe4bcf4df9bf2600131',
  head_block_id: '0000000ae4bffe027ec2a2c89ca92832422a84ec052649f1a80436fff5c61327',
  head_block_time: '2026-07-19T11:48:57.500',
  head_block_producer: 'eosio',
  virtual_block_cpu_limit: 1008036,
  virtual_block_net_limit: 1058054,
  block_cpu_limit: 1000000,
  block_net_limit: 1048576,
  server_version_string: 'v6.0.0',
  fork_db_head_block_num: 10,
  fork_db_head_block_id: '0000000ae4bffe027ec2a2c89ca92832422a84ec052649f1a80436fff5c61327',
  server_full_version_string: 'v6.0.0-ee002f9372237aa07b05879df772a95f9d54867f-dirty',
  total_cpu_weight: 0,
  total_net_weight: 0,
  earliest_available_block_num: 1,
  last_irreversible_block_time: '2026-07-19T11:48:57.000'
}
Created account: eosio.token
Created account: eosio.bpay
Created account: eosio.vpay
Created account: eosio.msig
Created account: eosio.wrap
Created account: eosio.power
Created account: eosio.saving
Created account: registrator
Created account: capital
Created account: loan
Created account: ledger
Created account: ledger2
Created account: test
Created account: meet
Created account: wallet
Created account: soviet
Created account: branch
Created account: marketplace
Created account: draft
Created account: gateway
Created account: fund
Created account: contributor
Created account: apps
Created account: expense
Created account: voskhod
ok -> init activation:  { result: 'ok' }
on set:  {
  name: 'eosio.boot',
  path: '/home/admin/mono-ai-2/components/contracts/build/contracts/system/contracts/eosio.boot',
  target: 'eosio'
}
wasm_path /home/admin/mono-ai-2/components/contracts/build/contracts/system/contracts/eosio.boot/eosio.boot.wasm
abi_path /home/admin/mono-ai-2/components/contracts/build/contracts/system/contracts/eosio.boot/eosio.boot.abi
contract setted:  eosio
Фича активирована:  ACTION_RETURN_VALUE
Фича активирована:  CONFIGURABLE_WASM_LIMITS2
Фича активирована:  BLOCKCHAIN_PARAMETERS
Фича активирована:  GET_SENDER
Фича активирована:  FORWARD_SETCODE
Фича активирована:  ONLY_BILL_FIRST_AUTHORIZER
Фича активирована:  RESTRICT_ACTION_TO_SELF
Фича активирована:  DISALLOW_EMPTY_PRODUCER_SCHEDULE
Фича активирована:  FIX_LINKAUTH_RESTRICTION
Фича активирована:  REPLACE_DEFERRED
Фича активирована:  NO_DUPLICATE_DEFERRED_ID
Фича активирована:  ONLY_LINK_TO_EXISTING_PERMISSION
Фича активирована:  RAM_RESTRICTIONS
Фича активирована:  WEBAUTHN_KEY
Фича активирована:  WTMSIG_BLOCK_SIGNATURES
Фича активирована:  GET_CODE_HASH
Фича активирована:  GET_BLOCK_NUM
Фича активирована:  CRYPTO_PRIMITIVES
on set:  {
  name: 'eosio.system',
  path: '/home/admin/mono-ai-2/components/contracts/build/contracts/system/contracts/eosio.system',
  target: 'eosio'
}
wasm_path /home/admin/mono-ai-2/components/contracts/build/contracts/system/contracts/eosio.system/eosio.system.wasm
abi_path /home/admin/mono-ai-2/components/contracts/build/contracts/system/contracts/eosio.system/eosio.system.abi
contract setted:  eosio
on set:  {
  name: 'eosio.token',
  path: '/home/admin/mono-ai-2/components/contracts/build/contracts/system/contracts/eosio.token',
  target: 'eosio.token'
}
wasm_path /home/admin/mono-ai-2/components/contracts/build/contracts/system/contracts/eosio.token/eosio.token.wasm
abi_path /home/admin/mono-ai-2/components/contracts/build/contracts/system/contracts/eosio.token/eosio.token.abi
contract setted:  eosio.token
on set:  {
  name: 'eosio.msig',
  path: '/home/admin/mono-ai-2/components/contracts/build/contracts/system/contracts/eosio.msig',
  target: 'eosio.msig'
}
wasm_path /home/admin/mono-ai-2/components/contracts/build/contracts/system/contracts/eosio.msig/eosio.msig.wasm
abi_path /home/admin/mono-ai-2/components/contracts/build/contracts/system/contracts/eosio.msig/eosio.msig.abi
contract setted:  eosio.msig
on set:  {
  name: 'eosio.wrap',
  path: '/home/admin/mono-ai-2/components/contracts/build/contracts/system/contracts/eosio.wrap',
  target: 'eosio.wrap'
}
wasm_path /home/admin/mono-ai-2/components/contracts/build/contracts/system/contracts/eosio.wrap/eosio.wrap.wasm
abi_path /home/admin/mono-ai-2/components/contracts/build/contracts/system/contracts/eosio.wrap/eosio.wrap.abi
contract setted:  eosio.wrap
on set:  {
  name: 'registrator',
  path: '/home/admin/mono-ai-2/components/contracts/build/contracts/registrator',
  target: 'registrator'
}
wasm_path /home/admin/mono-ai-2/components/contracts/build/contracts/registrator/registrator.wasm
abi_path /home/admin/mono-ai-2/components/contracts/build/contracts/registrator/registrator.abi
contract setted:  registrator
on set:  {
  name: 'soviet',
  path: '/home/admin/mono-ai-2/components/contracts/build/contracts/soviet',
  target: 'soviet'
}
wasm_path /home/admin/mono-ai-2/components/contracts/build/contracts/soviet/soviet.wasm
abi_path /home/admin/mono-ai-2/components/contracts/build/contracts/soviet/soviet.abi
contract setted:  soviet
on set:  {
  name: 'marketplace',
  path: '/home/admin/mono-ai-2/components/contracts/build/contracts/marketplace',
  target: 'marketplace'
}
wasm_path /home/admin/mono-ai-2/components/contracts/build/contracts/marketplace/marketplace.wasm
abi_path /home/admin/mono-ai-2/components/contracts/build/contracts/marketplace/marketplace.abi
contract setted:  marketplace
on set:  {
  name: 'draft',
  path: '/home/admin/mono-ai-2/components/contracts/build/contracts/draft',
  target: 'draft'
}
wasm_path /home/admin/mono-ai-2/components/contracts/build/contracts/draft/draft.wasm
abi_path /home/admin/mono-ai-2/components/contracts/build/contracts/draft/draft.abi
contract setted:  draft
on set:  {
  name: 'branch',
  path: '/home/admin/mono-ai-2/components/contracts/build/contracts/branch',
  target: 'branch'
}
wasm_path /home/admin/mono-ai-2/components/contracts/build/contracts/branch/branch.wasm
abi_path /home/admin/mono-ai-2/components/contracts/build/contracts/branch/branch.abi
contract setted:  branch
on set:  {
  name: 'gateway',
  path: '/home/admin/mono-ai-2/components/contracts/build/contracts/gateway',
  target: 'gateway'
}
wasm_path /home/admin/mono-ai-2/components/contracts/build/contracts/gateway/gateway.wasm
abi_path /home/admin/mono-ai-2/components/contracts/build/contracts/gateway/gateway.abi
contract setted:  gateway
on set:  {
  name: 'fund',
  path: '/home/admin/mono-ai-2/components/contracts/build/contracts/fund',
  target: 'fund'
}
wasm_path /home/admin/mono-ai-2/components/contracts/build/contracts/fund/fund.wasm
abi_path /home/admin/mono-ai-2/components/contracts/build/contracts/fund/fund.abi
contract setted:  fund
on set:  {
  name: 'ledger',
  path: '/home/admin/mono-ai-2/components/contracts/build/contracts/ledger',
  target: 'ledger'
}
wasm_path /home/admin/mono-ai-2/components/contracts/build/contracts/ledger/ledger.wasm
abi_path /home/admin/mono-ai-2/components/contracts/build/contracts/ledger/ledger.abi
contract setted:  ledger
on set:  {
  name: 'ledger2',
  path: '/home/admin/mono-ai-2/components/contracts/build/contracts/ledger2',
  target: 'ledger2'
}
wasm_path /home/admin/mono-ai-2/components/contracts/build/contracts/ledger2/ledger2.wasm
abi_path /home/admin/mono-ai-2/components/contracts/build/contracts/ledger2/ledger2.abi
contract setted:  ledger2
on set:  {
  name: 'test',
  path: '/home/admin/mono-ai-2/components/contracts/build/contracts/test',
  target: 'test'
}
wasm_path /home/admin/mono-ai-2/components/contracts/build/contracts/test/test.wasm
abi_path /home/admin/mono-ai-2/components/contracts/build/contracts/test/test.abi
contract setted:  test
on set:  {
  name: 'contributor',
  path: '/home/admin/mono-ai-2/components/contracts/build/contracts/contributor',
  target: 'contributor'
}
wasm_path /home/admin/mono-ai-2/components/contracts/build/contracts/contributor/contributor.wasm
abi_path /home/admin/mono-ai-2/components/contracts/build/contracts/contributor/contributor.abi
contract setted:  contributor
on set:  {
  name: 'capital',
  path: '/home/admin/mono-ai-2/components/contracts/build/contracts/capital',
  target: 'capital'
}
wasm_path /home/admin/mono-ai-2/components/contracts/build/contracts/capital/capital.wasm
abi_path /home/admin/mono-ai-2/components/contracts/build/contracts/capital/capital.abi
contract setted:  capital
on set:  {
  name: 'wallet',
  path: '/home/admin/mono-ai-2/components/contracts/build/contracts/wallet',
  target: 'wallet'
}
wasm_path /home/admin/mono-ai-2/components/contracts/build/contracts/wallet/wallet.wasm
abi_path /home/admin/mono-ai-2/components/contracts/build/contracts/wallet/wallet.abi
contract setted:  wallet
on set:  {
  name: 'loan',
  path: '/home/admin/mono-ai-2/components/contracts/build/contracts/loan',
  target: 'loan'
}
wasm_path /home/admin/mono-ai-2/components/contracts/build/contracts/loan/loan.wasm
abi_path /home/admin/mono-ai-2/components/contracts/build/contracts/loan/loan.abi
contract setted:  loan
on set:  {
  name: 'meet',
  path: '/home/admin/mono-ai-2/components/contracts/build/contracts/meet',
  target: 'meet'
}
wasm_path /home/admin/mono-ai-2/components/contracts/build/contracts/meet/meet.wasm
abi_path /home/admin/mono-ai-2/components/contracts/build/contracts/meet/meet.abi
contract setted:  meet
on set:  {
  name: 'apps',
  path: '/home/admin/mono-ai-2/components/contracts/build/contracts/apps',
  target: 'apps'
}
wasm_path /home/admin/mono-ai-2/components/contracts/build/contracts/apps/apps.wasm
abi_path /home/admin/mono-ai-2/components/contracts/build/contracts/apps/apps.abi
contract setted:  apps
on set:  {
  name: 'expense',
  path: '/home/admin/mono-ai-2/components/contracts/build/contracts/expense',
  target: 'expense'
}
wasm_path /home/admin/mono-ai-2/components/contracts/build/contracts/expense/expense.wasm
abi_path /home/admin/mono-ai-2/components/contracts/build/contracts/expense/expense.abi
contract setted:  expense
создаём токен
Токен создан:  { issuer: 'eosio', maximum_supply: '1000000000.0000 AXON' }
выпускаем токены
Токен выпущен:  { to: 'eosio', quantity: '100000.0000 AXON', memo: '' }
Updated account permissions: eosio.saving -> eosio.saving
Updated account permissions: registrator -> registrator
Updated account permissions: capital -> capital
Updated account permissions: loan -> loan
Updated account permissions: ledger -> ledger
Updated account permissions: ledger2 -> ledger2
Updated account permissions: test -> test
Updated account permissions: meet -> meet
Updated account permissions: wallet -> wallet
Updated account permissions: soviet -> soviet
Updated account permissions: branch -> branch
Updated account permissions: marketplace -> marketplace
Updated account permissions: draft -> draft
Updated account permissions: gateway -> gateway
Updated account permissions: fund -> fund
Updated account permissions: contributor -> contributor
Updated account permissions: apps -> apps
Updated account permissions: expense -> expense
Updated account permissions: voskhod -> registrator
Системный контракт инициализирован { version: 0, core: '4,AXON' }
Эмиссия инициализирована {
  init_supply: '1000.0000 AXON',
  tact_duration: 86400,
  emission_factor: 0.618
}
Система аренды инициализирована { args: { powerup_days: 1, min_powerup_fee: '0.0001 AXON' } }
Шаблон создан:  1
Шаблон создан:  2
Шаблон создан:  3
Шаблон создан:  4
Шаблон создан:  50
Шаблон создан:  51
Шаблон создан:  100
Шаблон создан:  101
Шаблон создан:  200
Шаблон создан:  201
Шаблон создан:  300
Шаблон создан:  301
Шаблон создан:  302
Шаблон создан:  303
Шаблон создан:  304
Шаблон создан:  501
Шаблон создан:  599
Шаблон создан:  600
Шаблон создан:  699
Шаблон создан:  700
Шаблон создан:  701
Шаблон создан:  702
Шаблон создан:  800
Шаблон создан:  801
Шаблон создан:  802
Шаблон создан:  900
Шаблон создан:  901
Шаблон создан:  994
Шаблон создан:  995
Шаблон создан:  996
Шаблон создан:  997
Шаблон создан:  998
Шаблон создан:  999
Шаблон создан:  1000
Шаблон создан:  1001
Шаблон создан:  1002
Шаблон создан:  1003
Шаблон создан:  1004
Шаблон создан:  1005
Шаблон создан:  1006
Шаблон создан:  1007
Шаблон создан:  1010
Шаблон создан:  1011
Шаблон создан:  1020
Шаблон создан:  1025
Шаблон создан:  1030
Шаблон создан:  1040
Шаблон создан:  1041
Шаблон создан:  1042
Шаблон создан:  1050
Шаблон создан:  1051
Шаблон создан:  1060
Шаблон создан:  1061
Шаблон создан:  1062
Шаблон создан:  1070
Шаблон создан:  1071
Шаблон создан:  1072
Шаблон создан:  1080
Шаблон создан:  1090
Шаблон создан:  2010
Шаблон создан:  2011
Арендуем ресурсы провайдеру
Аренда ресурсов:  {
  payer: 'eosio',
  receiver: 'voskhod',
  days: 1,
  payment: '10000.0000 AXON',
  transfer: true
}
{ from: 'eosio', to: 'voskhod', quantity: '1000.0000 AXON', memo: '' }
Перевод токенов:  { from: 'eosio', to: 'voskhod', quantity: '1000.0000 AXON', memo: '' }
Базовая инфраструктура установлена
Провайдер добавлен:  {
  username: 'voskhod',
  type: 'coop',
  short_name: 'ПК "Восход"',
  full_name: 'Потребительский Кооператив "ВОСХОД"',
  represented_by: {
    first_name: 'Иван',
    last_name: 'Иванов',
    middle_name: 'Иванович',
    position: 'Председатель',
    based_on: 'Решение общего собрания №1'
  },
  country: 'Российская Федерация',
  city: 'Москва',
  fact_address: '117593 г. Москва, муниципальный округ Ясенево, проезд Соловьиный, дом 1, помещение 1/1',
  full_address: '117593 г. Москва, муниципальный округ Ясенево, проезд Соловьиный, дом 1, помещение 1/1',
  email: 'copenomics@yandex.ru',
  phone: '+71234567890',
  details: { inn: '9728130611', ogrn: '1247700283346', kpp: '772801001' }
}
Vars сохранены с указанным _id и _created_at
Все документы удалены из коллекции sync
Все документы удалены из коллекции actions
Все документы удалены из коллекции deltas
Добавляем пайщика ant
{
  coopname: 'voskhod',
  referer: '',
  username: 'ant',
  type: 'individual',
  created_at: '2025-01-15T10:00:00',
  initial: '100.0000 RUB',
  minimum: '200.0000 RUB',
  spread_initial: true,
  meta: 'Основатель кооператива ВОСХОД',
  registration_hash: '1e87d2a7d35f1be95da5a676ae799262e970647d0ba69cd19c0eaff0afd5b488'
}
Добавлен пользователь:  ant
Устанавливаем дефолтный публичный ключ для ant
{
  coopname: 'voskhod',
  changer: 'voskhod',
  username: 'ant',
  public_key: 'EOS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV'
}
Изменён ключ для пользователя:  ant
Инициализируем пользователей в PostgreSQL
Подключение к PostgreSQL установлено для инициализации пользователей
Инициализировано 1 пользователей в PostgreSQL
Инициализируем vault в PostgreSQL
Подключение к PostgreSQL установлено для инициализации vault
Vault инициализирован в PostgreSQL
Создаём совет
{
  coopname: 'voskhod',
  username: 'ant',
  type: 'soviet',
  members: [
    {
      username: 'ant',
      is_voting: true,
      position_title: 'Председатель совета',
      position: 'chairman'
    }
  ],
  name: 'Совет',
  description: 'Совет кооператива ВОСХОД'
}
Создан совет:  {
  coopname: 'voskhod',
  username: 'ant',
  type: 'soviet',
  members: [
    {
      username: 'ant',
      is_voting: true,
      position_title: 'Председатель совета',
      position: 'chairman'
    }
  ],
  name: 'Совет',
  description: 'Совет кооператива ВОСХОД'
}
Создаём программы и соглашения
params {
  coopname: 'voskhod',
  username: 'voskhod',
  type: 'marketplace',
  title: 'КООПЛЕЙС',
  announce: '',
  description: '',
  preview: '',
  images: '',
  calculation_type: 'relative',
  fixed_membership_contribution: '0.0000 RUB',
  membership_percent_fee: 10000,
  meta: '',
  is_can_coop_spend_share_contributions: false
}
Программа установлена:  {
  coopname: 'voskhod',
  username: 'voskhod',
  type: 'marketplace',
  title: 'КООПЛЕЙС',
  announce: '',
  description: '',
  preview: '',
  images: '',
  calculation_type: 'relative',
  fixed_membership_contribution: '0.0000 RUB',
  membership_percent_fee: 10000,
  meta: '',
  is_can_coop_spend_share_contributions: false
}
params {
  coopname: 'voskhod',
  username: 'voskhod',
  type: 'generator',
  title: 'Целевая потребительская программа "Генератор"',
  announce: '',
  description: '',
  preview: '',
  images: '',
  calculation_type: 'free',
  fixed_membership_contribution: '0.0000 RUB',
  membership_percent_fee: 0,
  meta: '',
  is_can_coop_spend_share_contributions: true
}
Программа установлена:  {
  coopname: 'voskhod',
  username: 'voskhod',
  type: 'generator',
  title: 'Целевая потребительская программа "Генератор"',
  announce: '',
  description: '',
  preview: '',
  images: '',
  calculation_type: 'free',
  fixed_membership_contribution: '0.0000 RUB',
  membership_percent_fee: 0,
  meta: '',
  is_can_coop_spend_share_contributions: true
}
params {
  coopname: 'voskhod',
  username: 'voskhod',
  type: 'capital',
  title: 'Целевая потребительская программа "Благорост"',
  announce: '',
  description: '',
  preview: '',
  images: '',
  calculation_type: 'free',
  fixed_membership_contribution: '0.0000 RUB',
  membership_percent_fee: 0,
  meta: '',
  is_can_coop_spend_share_contributions: true
}
 ELIFECYCLE  Command failed with exit code 1.
Запускаем parser...
Запускаем контроллер...
Перезапуск завершен! пересоздаёт blockchain-data с нуля и гоняет
> monocoop@ boot /home/admin/mono-ai-2
> lerna run boot --scope @coopenomics/boot

> @coopenomics/boot:boot

> @coopenomics/boot@2026.6.24 boot /home/admin/mono-ai-2/components/boot
> esno src/index.ts boot
Node is healthy. Continue. {
  server_version: 'ee002f93',
  chain_id: 'db79c8409645082749ca50640d6f4ee511575acf26c4e2c8e4748e6bf6a01ed4',
  head_block_num: 123,
  last_irreversible_block_num: 122,
  last_irreversible_block_id: '0000007adc2e2215250e7ffbd9c7961faa675f57c3134be365b336292fda7e72',
  head_block_id: '0000007bf4fa5276948ff1ea2dce877a2032ac28be3a753271efd198ed5f79d5',
  head_block_time: '2026-07-19T11:49:54.000',
  head_block_producer: 'eosio',
  virtual_block_cpu_limit: 1128638,
  virtual_block_net_limit: 1184640,
  block_cpu_limit: 1000000,
  block_net_limit: 1048576,
  server_version_string: 'v6.0.0',
  fork_db_head_block_num: 123,
  fork_db_head_block_id: '0000007bf4fa5276948ff1ea2dce877a2032ac28be3a753271efd198ed5f79d5',
  server_full_version_string: 'v6.0.0-ee002f9372237aa07b05879df772a95f9d54867f-dirty',
  total_cpu_weight: 999999997,
  total_net_weight: 999999997,
  earliest_available_block_num: 1,
  last_irreversible_block_time: '2026-07-19T11:49:53.500'
}
error -> init activation:  AxiosError: Request failed with status code 500
    at settle (file:///home/admin/mono-ai-2/node_modules/.pnpm/axios@1.13.6/node_modules/axios/lib/core/settle.js:20:7)
    at IncomingMessage.handleStreamEnd (file:///home/admin/mono-ai-2/node_modules/.pnpm/axios@1.13.6/node_modules/axios/lib/adapters/http.js:844:13)
    at IncomingMessage.emit (node:events:531:35)
    at endReadableNT (node:internal/streams/readable:1698:12)
    at process.processTicksAndRejections (node:internal/process/task_queues:89:21)
    at Axios.request (file:///home/admin/mono-ai-2/node_modules/.pnpm/axios@1.13.6/node_modules/axios/lib/core/Axios.js:46:41)
    at process.processTicksAndRejections (node:internal/process/task_queues:103:5)
    at async startInfra (/home/admin/mono-ai-2/components/boot/src/init/infra.ts:45:22)
    at async boot (/home/admin/mono-ai-2/components/boot/src/init/booter.ts:6:22)
    at async Command.<anonymous> (/home/admin/mono-ai-2/components/boot/src/index.ts:144:7) {
  isAxiosError: true,
  code: 'ERR_BAD_RESPONSE',
  config: {
    transitional: {
      silentJSONParsing: true,
      forcedJSONParsing: true,
      clarifyTimeoutError: false,
      legacyInterceptorReqResOrdering: true
    },
    adapter: [ 'xhr', 'http', 'fetch' ],
    transformRequest: [ [Function: transformRequest] ],
    transformResponse: [ [Function: transformResponse] ],
    timeout: 0,
    xsrfCookieName: 'XSRF-TOKEN',
    xsrfHeaderName: 'X-XSRF-TOKEN',
    maxContentLength: -1,
    maxBodyLength: -1,
    env: { FormData: [Function], Blob: [class Blob] },
    validateStatus: [Function: validateStatus],
    headers: Object [AxiosHeaders] {
      Accept: 'application/json, text/plain, */*',
      'Content-Type': 'application/json',
      'User-Agent': 'axios/1.13.6',
      'Content-Length': '102',
      'Accept-Encoding': 'gzip, compress, deflate, br'
    },
    method: 'post',
    url: 'http://127.0.0.1:8898/v1/producer/schedule_protocol_feature_activations',
    data: '{"protocol_features_to_activate":["0ec7e080177b2c02b278d5088611686b49d739925a92d9bfcacd7fc6b74053bd"]}',
    allowAbsoluteUrls: true
  },
  request: <ref *1> ClientRequest {
    _events: [Object: null prototype] {
      abort: [Function (anonymous)],
      aborted: [Function (anonymous)],
      connect: [Function (anonymous)],
      error: [Function (anonymous)],
      socket: [Function (anonymous)],
      timeout: [Function (anonymous)],
      finish: [Function: requestOnFinish]
    },
    _eventsCount: 7,
    _maxListeners: undefined,
    outputData: [],
    outputSize: 0,
    writable: true,
    destroyed: true,
    _last: false,
    chunkedEncoding: false,
    shouldKeepAlive: true,
    maxRequestsOnConnectionReached: false,
    _defaultKeepAlive: true,
    useChunkedEncodingByDefault: true,
    sendDate: false,
    _removedConnection: false,
    _removedContLen: false,
    _removedTE: false,
    strictContentLength: false,
    _contentLength: 102,
    _hasBody: true,
    _trailer: '',
    finished: true,
    _headerSent: true,
    _closed: true,
    _header: 'POST /v1/producer/schedule_protocol_feature_activations HTTP/1.1\r\n' +
      'Accept: application/json, text/plain, */*\r\n' +
      'Content-Type: application/json\r\n' +
      'User-Agent: axios/1.13.6\r\n' +
      'Content-Length: 102\r\n' +
      'Accept-Encoding: gzip, compress, deflate, br\r\n' +
      'Host: 127.0.0.1:8898\r\n' +
      'Connection: keep-alive\r\n' +
      '\r\n',
    _keepAliveTimeout: 0,
    _onPendingData: [Function: nop],
    agent: Agent {
      _events: [Object: null prototype],
      _eventsCount: 2,
      _maxListeners: undefined,
      options: [Object: null prototype],
      defaultPort: 80,
      protocol: 'http:',
      requests: [Object: null prototype] {},
      sockets: [Object: null prototype] {},
      freeSockets: [Object: null prototype],
      keepAliveMsecs: 1000,
      keepAlive: true,
      maxSockets: Infinity,
      maxFreeSockets: 256,
      scheduling: 'lifo',
      maxTotalSockets: Infinity,
      totalSocketCount: 1,
      agentKeepAliveTimeoutBuffer: 1000,
      [Symbol(shapeMode)]: false,
      [Symbol(kCapture)]: false
    },
    socketPath: undefined,
    method: 'POST',
    maxHeaderSize: undefined,
    insecureHTTPParser: undefined,
    joinDuplicateHeaders: undefined,
    path: '/v1/producer/schedule_protocol_feature_activations',
    _ended: true,
    res: IncomingMessage {
      _events: [Object],
      _readableState: [ReadableState],
      _maxListeners: undefined,
      socket: null,
      httpVersionMajor: 1,
      httpVersionMinor: 1,
      httpVersion: '1.1',
      complete: true,
      rawHeaders: [Array],
      rawTrailers: [],
      joinDuplicateHeaders: undefined,
      aborted: false,
      upgrade: false,
      url: '',
      method: null,
      statusCode: 500,
      statusMessage: 'Internal Server Error',
      client: [Socket],
      _consuming: false,
      _dumped: false,
      req: [Circular *1],
      _eventsCount: 4,
      responseUrl: 'http://127.0.0.1:8898/v1/producer/schedule_protocol_feature_activations',
      redirects: [],
      [Symbol(shapeMode)]: true,
      [Symbol(kCapture)]: false,
      [Symbol(kHeaders)]: [Object],
      [Symbol(kHeadersCount)]: 8,
      [Symbol(kTrailers)]: null,
      [Symbol(kTrailersCount)]: 0
    },
    aborted: false,
    timeoutCb: null,
    upgradeOrConnect: false,
    parser: null,
    maxHeadersCount: null,
    reusedSocket: true,
    host: '127.0.0.1',
    protocol: 'http:',
    _redirectable: Writable {
      _events: [Object],
      _writableState: [WritableState],
      _maxListeners: undefined,
      _options: [Object],
      _ended: true,
      _ending: true,
      _redirectCount: 0,
      _redirects: [],
      _requestBodyLength: 102,
      _requestBodyBuffers: [],
      _eventsCount: 3,
      _onNativeResponse: [Function (anonymous)],
      _currentRequest: [Circular *1],
      _currentUrl: 'http://127.0.0.1:8898/v1/producer/schedule_protocol_feature_activations',
      _timeout: null,
      [Symbol(shapeMode)]: true,
      [Symbol(kCapture)]: false
    },
    [Symbol(shapeMode)]: false,
    [Symbol(kCapture)]: false,
    [Symbol(kBytesWritten)]: 0,
    [Symbol(kNeedDrain)]: false,
    [Symbol(corked)]: 0,
    [Symbol(kChunkedBuffer)]: [],
    [Symbol(kChunkedLength)]: 0,
    [Symbol(kSocket)]: Socket {
      connecting: false,
      _hadError: false,
      _parent: null,
      _host: null,
      _closeAfterHandlingError: false,
      _events: [Object],
      _readableState: [ReadableState],
      _writableState: [WritableState],
      allowHalfOpen: false,
      _maxListeners: undefined,
      _eventsCount: 6,
      _sockname: null,
      _pendingData: null,
      _pendingEncoding: '',
      server: null,
      _server: null,
      timeout: 5000,
      parser: null,
      _httpMessage: null,
      [Symbol(async_id_symbol)]: -1,
      [Symbol(kHandle)]: [TCP],
      [Symbol(lastWriteQueueSize)]: 0,
      [Symbol(timeout)]: Timeout {
        _idleTimeout: 5000,
        _idlePrev: [TimersList],
        _idleNext: [TimersList],
        _idleStart: 11165,
        _onTimeout: [Function: bound ],
        _timerArgs: undefined,
        _repeat: null,
        _destroyed: false,
        [Symbol(refed)]: false,
        [Symbol(kHasPrimitive)]: false,
        [Symbol(asyncId)]: 3415,
        [Symbol(triggerId)]: 3413,
        [Symbol(kAsyncContextFrame)]: undefined
      },
      [Symbol(kBuffer)]: null,
      [Symbol(kBufferCb)]: null,
      [Symbol(kBufferGen)]: null,
      [Symbol(shapeMode)]: true,
      [Symbol(kCapture)]: false,
      [Symbol(kSetNoDelay)]: true,
      [Symbol(kSetKeepAlive)]: true,
      [Symbol(kSetKeepAliveInitialDelay)]: 1,
      [Symbol(kBytesRead)]: 0,
      [Symbol(kBytesWritten)]: 0
    },
    [Symbol(kOutHeaders)]: [Object: null prototype] {
      accept: [Array],
      'content-type': [Array],
      'user-agent': [Array],
      'content-length': [Array],
      'accept-encoding': [Array],
      host: [Array]
    },
    [Symbol(errored)]: null,
    [Symbol(kHighWaterMark)]: 65536,
    [Symbol(kRejectNonStandardBodyWrites)]: false,
    [Symbol(kUniqueHeaders)]: null
  },
  response: {
    status: 500,
    statusText: 'Internal Server Error',
    headers: Object [AxiosHeaders] {
      server: 'nodeos/v6.0.0',
      'access-control-allow-origin': '*',
      'content-type': 'application/json',
      'content-length': '371'
    },
    config: {
      transitional: [Object],
      adapter: [Array],
      transformRequest: [Array],
      transformResponse: [Array],
      timeout: 0,
      xsrfCookieName: 'XSRF-TOKEN',
      xsrfHeaderName: 'X-XSRF-TOKEN',
      maxContentLength: -1,
      maxBodyLength: -1,
      env: [Object],
      validateStatus: [Function: validateStatus],
      headers: [Object [AxiosHeaders]],
      method: 'post',
      url: 'http://127.0.0.1:8898/v1/producer/schedule_protocol_feature_activations',
      data: '{"protocol_features_to_activate":["0ec7e080177b2c02b278d5088611686b49d739925a92d9bfcacd7fc6b74053bd"]}',
      allowAbsoluteUrls: true
    },
    request: <ref *1> ClientRequest {
      _events: [Object: null prototype],
      _eventsCount: 7,
      _maxListeners: undefined,
      outputData: [],
      outputSize: 0,
      writable: true,
      destroyed: true,
      _last: false,
      chunkedEncoding: false,
      shouldKeepAlive: true,
      maxRequestsOnConnectionReached: false,
      _defaultKeepAlive: true,
      useChunkedEncodingByDefault: true,
      sendDate: false,
      _removedConnection: false,
      _removedContLen: false,
      _removedTE: false,
      strictContentLength: false,
      _contentLength: 102,
      _hasBody: true,
      _trailer: '',
      finished: true,
      _headerSent: true,
      _closed: true,
      _header: 'POST /v1/producer/schedule_protocol_feature_activations HTTP/1.1\r\n' +
        'Accept: application/json, text/plain, */*\r\n' +
        'Content-Type: application/json\r\n' +
        'User-Agent: axios/1.13.6\r\n' +
        'Content-Length: 102\r\n' +
        'Accept-Encoding: gzip, compress, deflate, br\r\n' +
        'Host: 127.0.0.1:8898\r\n' +
        'Connection: keep-alive\r\n' +
        '\r\n',
      _keepAliveTimeout: 0,
      _onPendingData: [Function: nop],
      agent: [Agent],
      socketPath: undefined,
      method: 'POST',
      maxHeaderSize: undefined,
      insecureHTTPParser: undefined,
      joinDuplicateHeaders: undefined,
      path: '/v1/producer/schedule_protocol_feature_activations',
      _ended: true,
      res: [IncomingMessage],
      aborted: false,
      timeoutCb: null,
      upgradeOrConnect: false,
      parser: null,
      maxHeadersCount: null,
      reusedSocket: true,
      host: '127.0.0.1',
      protocol: 'http:',
      _redirectable: [Writable],
      [Symbol(shapeMode)]: false,
      [Symbol(kCapture)]: false,
      [Symbol(kBytesWritten)]: 0,
      [Symbol(kNeedDrain)]: false,
      [Symbol(corked)]: 0,
      [Symbol(kChunkedBuffer)]: [],
      [Symbol(kChunkedLength)]: 0,
      [Symbol(kSocket)]: [Socket],
      [Symbol(kOutHeaders)]: [Object: null prototype],
      [Symbol(errored)]: null,
      [Symbol(kHighWaterMark)]: 65536,
      [Symbol(kRejectNonStandardBodyWrites)]: false,
      [Symbol(kUniqueHeaders)]: null
    },
    data: { code: 500, message: 'Internal Service Error', error: [Object] }
  },
  status: 500
}
on set:  {
  name: 'eosio.boot',
  path: '/home/admin/mono-ai-2/components/contracts/build/contracts/system/contracts/eosio.boot',
  target: 'eosio'
}
wasm_path /home/admin/mono-ai-2/components/contracts/build/contracts/system/contracts/eosio.boot/eosio.boot.wasm
abi_path /home/admin/mono-ai-2/components/contracts/build/contracts/system/contracts/eosio.boot/eosio.boot.abi
contract setted:  eosio
on set:  {
  name: 'eosio.system',
  path: '/home/admin/mono-ai-2/components/contracts/build/contracts/system/contracts/eosio.system',
  target: 'eosio'
}
wasm_path /home/admin/mono-ai-2/components/contracts/build/contracts/system/contracts/eosio.system/eosio.system.wasm
abi_path /home/admin/mono-ai-2/components/contracts/build/contracts/system/contracts/eosio.system/eosio.system.abi
contract setted:  eosio
on set:  {
  name: 'eosio.token',
  path: '/home/admin/mono-ai-2/components/contracts/build/contracts/system/contracts/eosio.token',
  target: 'eosio.token'
}
wasm_path /home/admin/mono-ai-2/components/contracts/build/contracts/system/contracts/eosio.token/eosio.token.wasm
abi_path /home/admin/mono-ai-2/components/contracts/build/contracts/system/contracts/eosio.token/eosio.token.abi
 ELIFECYCLE  Command failed with exit code 1.

 Lerna (powered by Nx)   Running target boot for project @coopenomics/boot failed

Failed tasks:

- @coopenomics/boot:boot

Hint: run the command with --verbose for more details.

 ELIFECYCLE  Command failed with exit code 1.
(booter.ts → cooperative.ts createPrograms) — именно так локальная dev-цепочка
получила 'blagorost' вместо 'capital' и воспроизвела баг, которого на проде
никогда не было.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-19 11:50:04 +00:00
coopops 5360daa471 fix(capital): correct BLAGOROST_AGREEMENT_TYPE to match on-chain 'blagorost'
Typecheck / desktop (pull_request) Failing after 8m53s
Typecheck / controller (pull_request) Has been cancelled
Константа хранила 'capital', но boot/src/init/cooperative.ts createPrograms()
сеет программу с type='blagorost' на генезисе (soviet::createprog), на всех
кооперативах. account.adapter.ts искал coagreement по 'capital' — не находил,
programId откатывался на 0, и вместо wallet::signagree уходил
soviet::sndagreement, который на цепи падает: get_coagreement_or_fail не
находит type='capital' в coagreements — «Соглашение указанного типа не
найдено». Ловилось при приёме платежа регистрации (registerBlockchainAccount
из gateway.interactor при PaymentType.REGISTRATION), падало для КАЖДОГО
участника, зарегистрированного по офферте Благорост.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-19 11:41:03 +00:00
coopops 652f7c722e fix(cooptypes): add missing space after colon before eoap_definition placeholder
Typecheck / desktop (pull_request) Failing after 8m50s
Typecheck / controller (pull_request) Successful in 13m45s
п.1.1 «а именно:{{ doc_data.eoap_definition }}» без пробела — во всех шести
шаблонах ЦПП (994/995/996/998/999/1000), фраза идентична во всех программах.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-19 08:46:00 +00:00
coopops a2b132b457 fix(registration): resolve doc_data_hash for extension offers via registry spec
Typecheck / controller (pull_request) Has been cancelled
Typecheck / desktop (pull_request) Has been cancelled
Шаблоны оферт ЦПП (#996/#1000) с c7824545cf требуют PrivateData, но общий
путь регистрации (RegistrationDocumentsService) не передавал doc_data_hash —
регистрация пайщика по GENERATION/CAPITALIZATION падала на генерации превью.

Ядро остаётся слепым к расширениям: AgreementRegistrationSpec получает
опциональный resolve_doc_data_hash, который расширение-владелец прикрепляет
к своим офертам при регистрации в реестре. Capital резолвит hash свежим
чтением своего конфига на каждую генерацию (совет может пересохранить
параметры без перезапуска). Гранулярность — per-оферта: при разделении
параметров по программам ядро менять не потребуется.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-19 08:41:57 +00:00
coopops a6cc71d6a3 fix(extension): unwrap TypeORM [rows, count] tuple in patchConfig UPDATE...RETURNING
TypeORM возвращает для UPDATE/DELETE кортеж [rows, affectedCount] (в отличие
от INSERT с голым rows-массивом) — без распаковки row оказывался кортежем и
все поля config читались как undefined, роняя buildState онбординга.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-19 08:41:45 +00:00
coopops b67581ce80 fix(extension): atomic jsonb config patch to stop onboarding flag lost-updates
Typecheck / desktop (pull_request) Failing after 22m0s
Typecheck / controller (pull_request) Successful in 13m37s
extensions.config is a single jsonb blob shared by every flag/hash the L1
onboarding wizards (capital, chairman) write to. Every write site did the
classic read-modify-write: findByName() the whole row, spread+mutate one key
in app memory, then update() the whole config back. Two DecisionTrackedEvent
handlers firing concurrently (two council decisions tracked near-simultaneously)
each read the same stale snapshot and each write their own flag back — whichever
write lands last wins and silently reverts the other one's flag to its old value.
Symptom hit live: onboarding_generator_program_template_done reverted to false
(with its hash still present, proving the decision really was tracked) after
onboarding_generation_contract_template_done raced it to true.

Added ExtensionDomainRepository.patchConfig(name, patch), implemented as a single
`UPDATE extensions SET config = config || patch::jsonb ... RETURNING *` — no
app-side read before the write, Postgres serializes concurrent UPDATEs on the
row so two different-key patches can no longer clobber each other. Replaced
every config read-modify-write in capital's and chairman's onboarding services
(the events handler's flag write, loadPlugin's init/expire timestamps, the
hash writes in completeStep/completeGeneralMeet, saveProgramDocDataHash) with
this atomic patch. update() is untouched for other callers.

Live data recovered manually: onboarding_generator_program_template_done was
patched back to true for voskhod/capital (hash was already present, confirming
the decision had in fact been tracked before the race reverted the flag).
2026-07-19 07:56:00 +00:00
coopops 9030d28c6b fix(cooptypes): remove п.8.1 «Под Сайтом» from BlagorostProgramTemplate, renumber
Typecheck / desktop (pull_request) Failing after 9m37s
Typecheck / controller (pull_request) Successful in 14m3s
По решению: универсал-документ (ЦПП БЛАГОРОСТ_финал_универсал.docx.rtf,
~/blago/production/shared) — новый правильный эталон. В нём раздел 8 «Прочие условия»
содержит только 2 пункта (8.1 «Общество и Участник...», 8.2 «Изменения и дополнения...»),
без пункта про «Под Сайтом... прочие программные продукты» — ни в Положении, ни в
Оферте БЛАГОРОСТ (universal) такого пункта нет.

Убрал п.8.1 из rawContext, сдвинул 8.2→8.1, 8.3→8.2. Ссылка "(п. 7.1.)" в новом 8.2
не требует правки — форс-мажор остаётся разделом 7 независимо от сдвига в разделе 8.
Теперь 998 совпадает с универсалом точь-в-точь (сверено скриптом по номерам пунктов
и по тексту под каждым номером).
2026-07-19 07:31:02 +00:00
coopops 6e0e68c1e7 fix(cooptypes): correct cross-reference in GeneratorProgramTemplate п.12.3
Typecheck / controller (pull_request) Has been cancelled
Typecheck / desktop (pull_request) Has been cancelled
Systematic clause-numbering comparison against Положение_ЦПП_ГЕНЕРАТОР_универсал.docx.rtf
(~/blago/production/shared) found п.12.3 pointing at "(п. 12.1.)" — that's the "Под Сайтом"
definition clause, unrelated. The universal document (and the sentence's own subject —
"изменения условий ЦПП... возможность внесения которых прямо предусмотрена настоящим
Положением") both point to п.11.1, the форс-мажор/legislative-changes clause that actually
grants this right. Fixed the reference in rawContext only; left the orphaned translations
copy (994's dead legacy i18n block, unused by rendering) untouched.
2026-07-19 07:26:44 +00:00
coopops c079785019 fix(cooptypes): remove orphaned п.5.2 in BlagorostProgramTemplate, renumber section 5
Typecheck / desktop (pull_request) Failing after 9m2s
Typecheck / controller (pull_request) Successful in 13m50s
Сверил с ЦПП БЛАГОРОСТ_финал_универсал.docx.rtf (~/blago/production/shared) — в
актуальной версии Положения самостоятельный п.5.2 «Основной экономический эффект
функционирования Платформы достигается за счет членских взносов Участников при
расширении круга пользователей Платформы» отсутствует: его смысл слит в п.5.1
("...экономический эффект от функционирования которого заключается в следующем: ...").
Оферта (999/1000.Blagorost*, уже сверено) была такой всегда — 4.1 (источник) сразу
за которым 4.2 (дополнительный источник), без промежуточного пункта.

Убрал п.5.2 из rawContext, сдвинул 5.3–5.10 на 5.2–5.9, поправил внутреннюю ссылку
"указанными в п. 5.9." → "указанными в п. 5.8." в новом 5.9 (бывший 5.10). Дублирующий
текст в дохлом export const translations (не используется рендером, см. context) не трогал.
2026-07-18 18:55:46 +00:00
coopops bbc6453b24 fix(capital): await onSaved before advancing wizard past БЛАГОРОСТ step
Typecheck / desktop (pull_request) Failing after 8m49s
Typecheck / controller (pull_request) Successful in 13m45s
goNext() checked getFirstAvailableCouncilGroup() (which reads isDocParamsReady,
derived from onboardingState.capital_program_doc_data_hash) synchronously right
after saveParams() resolved. But saveParams() fired options.onSaved(hash) without
awaiting it, and the card's onSaved handler did `void handleDocParamsSaved()` —
an async refetch of onboarding state — so isDocParamsReady was still stale at the
moment goNext() decided which council group to jump to. getFirstAvailableCouncilGroup()
returned null, setWizardStepKey never ran, and the wizard just re-rendered the same
БЛАГОРОСТ step, looking like the just-saved params were never registered.

Fix: onSaved is now awaited inside saveParams(), and the card passes the
handleDocParamsSaved() promise through instead of firing it and forgetting it.
2026-07-18 18:27:57 +00:00
coopops b5eb0ea51f fix(capital): address PR #154 review — proper interface instead of as-cast, IInput['data'] canon
Typecheck / desktop (pull_request) Failing after 23m23s
Typecheck / controller (pull_request) Failing after 21m57s
document-domain.service.ts: replaced the inline `as Cooperative.Document.IGenerate & {...}`
hack with a dedicated GenerateDocumentWithPrivateDataDomainInterface for the doc_data
pre-generation branch (Cooperative.Document.IGenerate already carries a `[key: string]: any`
index signature, so the narrow interface is assignable without any cast at all).

Onboarding api/index.ts: saveProgramDocDataHash now takes
Mutations.Capital.SaveCapitalProgramDocDataHash.IInput['data'] and forwards it as-is to
variables.data, matching the SDK IInput['data'] canon used by completeStep right above it
instead of taking a bare string and rebuilding the payload inline. Updated both call sites.
2026-07-18 15:39:57 +00:00
coopops a072364d00 fix(capital): auto-generate CPP doc preview on step entry, restore section header formatting
Typecheck / desktop (pull_request) Failing after 20m30s
Typecheck / controller (pull_request) Successful in 12m40s
Preview for ГЕНЕРАТОР/БЛАГОРОСТ params no longer requires a manual button click on
first visit — onMounted now calls ensurePreview for the active wizard step. Also
fixes wizardStepOrder typing (was narrower than the runtime string values it holds).

Restores <h3> centered section-title markup in registry templates 994/995/996/998/999/1000
that was lost during the "parameterize CPP templates via private data" refactor, when
translations+context i18n structure was flattened into raw HTML.
2026-07-17 15:21:01 +00:00
coopops 5230fab7c6 fix(capital): add blagorost_goal_reason field, stop reusing blagorost_goal_expansion in clause 2.1.1
Typecheck / desktop (pull_request) Failing after 9m28s
Typecheck / controller (pull_request) Successful in 14m6s
Clause 2.1.1 of БЛАГОРОСТ Положение/Оферта needs different text than the
intro paragraph, but both were wired to the same doc_data.blagorost_goal_expansion
variable (confirmed against the original "универсал" RTF sources), so 2.1.1
rendered with the wrong sentence in all three registries (998, 999, 1000).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-17 07:41:24 +00:00
Alex Ant 345aadbd6a update
Typecheck / desktop (pull_request) Failing after 8m25s
Typecheck / controller (pull_request) Successful in 14m12s
2026-07-14 22:31:19 +03:00
Alex Ant 33f1ac1a15 fix(capital): restore CPP templates from RTF sources and improve parameter constructor
Typecheck / desktop (pull_request) Failing after 18m32s
Typecheck / controller (pull_request) Failing after 6m52s
Align cooptypes/factory templates 994–1000 with original document wording,
correct doc_data substitution points, update voskhod backfill values, and
add onboarding UI with descriptive field labels and doc data hash saving.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-14 14:27:27 +03:00
Alex Ant 45feef2676 fix(capital): correct CPP template mapping and onboarding widget canon
Typecheck / desktop (pull_request) Failing after 13m22s
Typecheck / controller (pull_request) Failing after 13m26s
Fix swapped doc_data placeholders in generator/blagorost templates, align the install widget with Base* components, and use one shared PrivateData hash in factory tests.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-08 21:49:30 +03:00
Alex Ant 02ce9400a2 fix(capital): remove unsafe any from CPP onboarding changes
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-28 11:05:20 +05:00
Alex Ant bfa2830650 feat(capital): collect CPP document parameters in onboarding
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-28 10:44:02 +05:00
Alex Ant c7824545cf feat(capital): parameterize CPP templates via private data 2026-06-28 09:50:03 +05:00
Alex Ant b9107e9ee9 chore(release): publish
Build bootstrap container / build (push) Successful in 1m45s
Release / release (push) Failing after 55m53s
Release / publish-packages (push) Has been skipped
Release / trigger-coopenomics-docs (push) Has been skipped
Release / trigger-mono-docs (push) Has been skipped
v2026.6.24
2026-06-24 22:04:51 +05:00
Alex Ant 4d151befe0 fix(install): блокировать «Продолжить» до минимума совета, удаление без ограничений.
На prod требуется 3 человека (на dev — 1): кнопка и подсказка, состав редактируется свободно до перехода на следующий шаг.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-24 22:03:12 +05:00
Alex Ant eb16d85abc fix(install): разблокировать мастер при прерванной установке и валидировать состав совета по окружению.
Не показываем заглушку техобслуживания на /install, разрешаем повтор install из maintenance без vars, проверяем минимум членов совета до записи в цепь (3 на prod, 1 на dev).

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-24 11:35:01 +05:00
ant ee5f7e1850 Merge pull request 'feat: выход из кооператива (выход пайщика, возврат паевого взноса)' (#137) from feat/membership-exit into dev
Reviewed-on: #137
2026-06-22 19:56:24 +00:00
Alex Ant 10bacaad13 refactor(membership-exit): обновление интерфейса и логики выхода из кооператива
Typecheck / desktop (pull_request) Failing after 25m29s
Typecheck / controller (pull_request) Successful in 27m23s
Изменены компоненты, связанные с выходом из кооператива. В меню заменены иконки и пути для поддержки и выхода. Обновлены метаданные и названия для кнопок и страниц, чтобы улучшить пользовательский интерфейс. В ExitButton добавлены параметры для настройки иконки и метки. Упрощена структура MembershipExitPage с акцентом на ключевые шаги выхода. Исправлены комментарии и типы для лучшего понимания кода.
2026-06-23 00:26:38 +05:00
coopops 1f68169b94 feat(exit): возврат при выходе — сет из 3 паевых кошельков (minshr+share+blago)
Typecheck / desktop (pull_request) Failing after 9m37s
Typecheck / controller (pull_request) Successful in 12m46s
confirmexit теперь обходит сет LEDGER2_EXIT_REFUND_WALLETS (w.reg.minshr +
w.wal.share + w.cap.blago): собирает доступные L3-балансы каждого (>0),
консолидирует на главный паевой (w.reg.minshr→o.reg.mvmin, w.cap.blago→
o.cap.wthcap) и ставит полную сумму на возврат единым платежом. Раньше
хардкодил только minshr+share — паевой в Благоросте оставался висеть.

Сет задан один раз в wallets.hpp (источник истины), генерируется gen:from-cpp
в cooptypes (LEDGER2_EXIT_REFUND_WALLETS / EXIT_REFUND_WALLET_NAMES) и обходится
backend-preview (getReturnPreview) — расчёт на фронте всегда совпадает с тем,
что реально вернёт контракт. Подпись «планируемая сумма / итог фиксирует Совет»
убрана из ExitButton и ExitOverlay (сумма теперь авторитетна).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-16 14:36:52 +00:00
coopops 95891b2319 fix(desktop): развёл иконки меню «Подключение» и «Реквизиты»
Typecheck / desktop (pull_request) Failing after 8m41s
Typecheck / controller (pull_request) Failing after 6m39s
Обе были 'fas fa-link' (одинаковые + FontAwesome запрещён каноном). На Material:
- Подключение → 'link'
- Реквизиты → 'account_balance' (банковские реквизиты)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-16 13:38:23 +00:00
coopops f41f1444b1 fix(desktop): реестр пайщиков — статус «Вышел из кооператива», дата вступления, без удаления
Typecheck / desktop (pull_request) Failing after 8m55s
Typecheck / controller (pull_request) Successful in 13m48s
- статус: вышедший пайщик (participant_account стёрт delpartcpnt + user_account
  blocked) больше не показывается «Активный пайщик» — добавлен терминал
  «Вышел из кооператива» по user_account.status
- дата вступления: фолбэк participant_account.created_at → user_account.registered_at
  (у вышедших пайщик-запись стёрта, дата больше не «отсутствует»)
- убрана кнопка удаления пайщика (таблица + карточка + диалог + машинерия):
  пайщика из реестра не удаляют

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-16 12:48:14 +00:00
coopops da450ec605 fix(desktop): «платёжка» → «чек об оплате» в UI реестра платежей
Typecheck / desktop (pull_request) Failing after 6m42s
Typecheck / controller (pull_request) Has been cancelled
Подсказки/лейблы переименованы под каноничную терминологию (чек об оплате —
подтверждающий документ, не «платёжка»):
- тултипы «Чек об оплате приложен / не приложен» (desktop + mobile)
- fileKindLabel PAYMENT_PROOF → «Чек об оплате»
- расчётный ПЛАТЁЖ возврата/доплаты (вместо «платёжка») в подсказках/алертах
  ReportExpenseAdvance + согласование рода
- комментарии приведены к новой терминологии (чек / расчётный платёж)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-16 12:16:31 +00:00
coopops 7fdfd8385a feat(gateway): ядровый «чек об оплате» исходящих платежей по payment_hash
Typecheck / desktop (pull_request) Failing after 8m31s
Typecheck / controller (pull_request) Successful in 14m26s
Культура денег: входящие — подтверждением, исходящие — чеком. Единый механизм
на уровне ядра (gateway), привязка по payment_hash — один на все исходящие
(возврат паевого/withdrawal/registration-refund/аванс расхода), переиспользуется
расширениями. Контроль мягкий: статус не трогаем, но зеркалим proof_count →
реестр рисует «чек приложен / не приложен».

Backend (core gateway):
- таблица payment_files + бакет gateway:files (@UseBucket), PaymentFilesService
  (upload/read-url/list/delete + зеркалирование proof_count в платёж)
- enum PaymentFileKind (PAYMENT_PROOF), DTO, резолвер uploadPaymentProof /
  paymentProofs / paymentFile; провайдеры в typeorm.module + gateway.module

SDK: selectors/mutations/queries gateway + zeus regen

Desktop:
- features/Payment/AttachPaymentProof — панель чека по payment-hash
- реестр: панель + индикатор «чек приложен» для ЛЮБОГО исходящего (PAID/COMPLETED)
- конвергенция expense: чек ушёл в ядро, AttachExpenseProof оставляет только
  закрывающие документы (DIRECT)

Терминология: «чек об оплате» (не «платёжка»/«первичка»).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-16 09:17:09 +00:00
coopops 39433753c2 fix(membership-exit): терминальный экран после выхода + гард повторного выхода
Typecheck / desktop (pull_request) Failing after 8m47s
Typecheck / controller (pull_request) Has been cancelled
После completexit строка registrator::exits стирается (терминал=erase) →
getMembershipExit возвращал null → оверлей пропадал, кабинет разблокировался,
пайщик видел стол и мог подать выход повторно (гарда не было).

- enum MembershipExitStatus += COMPLETED
- getMembershipExit: терминальная фаза по blocked-аккаунту + персистентному
  MEMBERSHIP_EXIT-платежу (сумма возврата + статус 'Оплачено')
- createMembershipExit: гард — заблокированный аккаунт не может выйти повторно
  (контракт уже блокирует через get_participant_or_fail; отсекаем раньше)
- ExitOverlay: терминальная фаза 'Вы вышли из кооператива' (сумма + 'Оплачено'
  + ожидайте поступления), она же UX-гард — перекрывает кабинет
- SDK Zeus regen под новый enum

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-16 08:57:54 +00:00
coopops 560321853c Merge remote-tracking branch 'origin/dev' into feat/membership-exit
Typecheck / desktop (pull_request) Has been cancelled
Typecheck / controller (pull_request) Has been cancelled
2026-06-16 08:42:23 +00:00
ant f42b703098 Merge pull request 'ci(release): независимый деплой окружений (testnet/main разным темпом)' (#150) from ci/independent-env-deploy into dev
Reviewed-on: #150
2026-06-16 08:17:55 +00:00
ant d7e08785eb Merge pull request 'fix(capital): регистрация долей Благороста при создании проекта по событию (не ждать крон)' (#151) from fix/capital-share-onproject-event into dev
Reviewed-on: #151
Reviewed-by: Алексей Муравьев <chairman.voskhod@gmail.com>
2026-06-16 08:15:29 +00:00
coopops 7efd19f1bd chore(release): publish
Build bootstrap container / build (push) Has been cancelled
Release / publish-packages (push) Failing after 8m37s
Release / release (push) Successful in 27m21s
Release / trigger-coopenomics-docs (push) Successful in 1s
Release / trigger-mono-docs (push) Successful in 2s
v2026.6.16-1
2026-06-16 08:11:23 +00:00
coopops cce099748a fix(capital/state): program_expense_* в хвост через binary_extension — фикс нечитаемой таблицы state на проде
Инцидент 2026-06-16: коммит d02c0ce (18 мая) добавил program_expense_pool и
program_expense_reserved в global_state ПЕРЕД полем config напрямую, без
binary_extension. После деплоя на прод таблица state перестала читаться:
запись сериализована старым layout (config сразу после
program_membership_cumulative_reward_per_share), а новый ABI ждёт два asset
перед config → unpack натыкается на байты config (get table → "Invalid
symbol ...Y@" = double 100.0 = config.expense_pool_percent). Любой action,
читающий global_state, падал.

Фикс: оба поля перенесены в ХВОСТ struct (после config) и обёрнуты в
eosio::binary_extension<asset>. Старая запись прода читается без изменений
(хвостовые extension опциональны → пусто = 0), новая логика программных
расходов сохранена. Поля в таблицу EOSIO дописываются только в конец и только
binary_extension'ом.

utility-функции State:: (topup/reserve/release/consume/spend) переведены на
optional-семантику через ext_or_zero(); инвариант — оба поля материализуются
вместе, чтобы порядок хвостовых extension был консистентным. Прямого доступа к
полям вне State:: нет. Собрано в CDT-докере: capital.wasm слинкован, в ABI
program_expense_* → asset$ после config.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-16 08:06:16 +00:00
coopops 3d04f76446 fix(capital): заводить доли только в active-проектах, не в pending
Typecheck / desktop (pull_request) Has been cancelled
Typecheck / controller (pull_request) Has been cancelled
По решению пользователя заход долей Благороста — только в активные проекты.
В pending больше не заводим: при переходе pending→active придёт новая дельта
capital::projects со status=active, на ней листенер и зарегистрирует.

- Листенер ProgramShareRegistrationOnProjectDeltaListener: гейт pending|active → только active.
- ProgramShareRegistrationService.findActiveProjects (путь крона и wallet-листенера): фильтр pending|active → только active.
- Тест: pending теперь НЕ реагирует; active — позитивный кейс.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-16 08:04:08 +00:00
coopops 532e34c53b ci(release): promote по умолчанию шлёт текущий HEAD (не последний тег)
Typecheck / desktop (pull_request) Has been cancelled
Typecheck / controller (pull_request) Has been cancelled
Дефолт promote.sh — текущий HEAD чекаута; второй аргумент по-прежнему задаёт явный
ref (ветка/тег/sha). Типичный путь: cut на dev → promote testnet → promote main,
все три от того же HEAD. testnet/main остаются независимыми FF-указателями.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-16 07:45:04 +00:00
coopops a0e0c9310c fix(desktop): confirm-выхода — auth-aware навигация вместо молчаливого бойнса на логин
Typecheck / controller (pull_request) Has been cancelled
Typecheck / desktop (pull_request) Has been cancelled
Ссылка из письма открывается часто без сессии кабинета (другой браузер/инкогнито/
после logout). Подтверждение работает по токену без входа, но wallet защищён —
гард молча редиректил на login-redirect, и пайщик не понимал, что произошло.

- есть сессия → 'Перейти в кабинет' → wallet (глобальный ExitOverlay сам покажет
  'на рассмотрении Совета')
- нет сессии → 'Войти в кабинет' → signin + пояснение, что войти нужно для слежения
  за статусом и суммой возврата

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-16 07:40:02 +00:00
coopops 0d51973a7b fix(capital): регистрировать доли Благороста при создании проекта по событию, без ожидания крона
Typecheck / desktop (pull_request) Successful in 13m55s
Typecheck / controller (pull_request) Has been cancelled
Доли пайщиков Благороста регистрировались в проект только периодическим
scheduler'ом (regshare) и точечным листенером на дельту кошелька. Новый
проект баланс не меняет → wallet-листенер молчит, и наполнение нового
проекта зависело ТОЛЬКО от крона. Если компонент быстро прогнали
pending→…→result до тика крона, пайщики в него не попадали, а откат
result→active контракт не допускает — доли в компоненте терялись
безвозвратно (voskhod, компонент 011bcd92…, 2026-06-16).

- Новый ProgramShareRegistrationOnProjectDeltaListener на
  delta::capital::projects: при появлении проекта в статусе pending|active
  сразу регистрирует доли всех активных пайщиков (неблокирующе, fire-and-forget,
  переиспользуя ядро syncContributor — тот же путь, что и крон).
- ProgramShareRegistrationService.syncProgramSharesForProject — обход пайщиков
  по одному проекту; syncContributor переведён с ProjectDomainEntity[] на
  projectHashes: string[].
- Крон ОСТАВЛЕН как reconciliation-бэкстоп: он ещё и ДОобновляет уже
  зарегистрированные доли при дрейфе баланса (контракт upsert_contributor_segment
  это допускает) и подбирает события, потерянные при downtime контроллера.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-16 07:30:10 +00:00
coopops 9c2729d243 ci(release): независимый деплой окружений — testnet/main как FF-указатели на теги dev
Typecheck / desktop (pull_request) Successful in 13m58s
Typecheck / controller (pull_request) Successful in 13m15s
Паровоз dev→testnet→main заменён независимыми указателями: каждое окружение —
самостоятельный fast-forward-указатель на выбранный релизный тег dev. Прод и тест
катятся разным темпом и на разные версии одновременно, без диверджа и конфликтов
(оба — FF-указатели на линейную историю dev).

- promote.sh <testnet|main> [ref]: FF выбранного тега (по умолчанию — последний на
  origin/dev) на ветку окружения; main больше не зависит от testnet; только вперёд.
- release.yaml: workflow_dispatch получил inputs environment+ref — откат на старую
  версию / редеплой без бампа / hotfix в один контур (то, что FF не умеет). Сборка
  идёт из вычекнутого ref, окружение — из inputs.environment. Авто-триггер по
  изменению lerna.json сохранён.
- RELEASE.md: модель и инварианты переписаны под независимые указатели.

Принципы деплоя сохранены: бамп версии один раз на dev, образы по версии,
BUILD_MODE по окружению, webhooks per env, npm publish/доки только на main.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-16 06:49:12 +00:00
coopops 00e15be65e fix(desktop): убрал '*/' из SCSS-комментария — ломал sass-парсер
Typecheck / desktop (pull_request) Failing after 8m29s
Typecheck / controller (pull_request) Successful in 14m13s
'text-h*/inline' досрочно закрывал блочный комментарий → expected '{'.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-16 06:31:10 +00:00
coopops a349cfcf6a fix(desktop): диалог выхода — придавил заголовок заявления к канону + soft-панель итога
Typecheck / desktop (pull_request) Has been cancelled
Typecheck / controller (pull_request) Has been cancelled
- заголовок документа из DocumentHtmlReader (3rem) прижат к h2 по канону,
  :deep + !important как в ReadStatement.vue
- 'Сумма к возврату' оформлена soft-панелью (label+hint слева, mono-значение
  справа) вместо висящего снизу текста

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-16 06:26:03 +00:00
coopops 07ec6d9a5b chore(release): publish
Build bootstrap container / build (push) Failing after 1m27s
Release / release (push) Failing after 16m43s
Release / trigger-mono-docs (push) Has been skipped
Release / publish-packages (push) Has been skipped
Release / trigger-coopenomics-docs (push) Has been skipped
v2026.6.16
2026-06-16 06:02:31 +00:00
coopops 102eeb319e fix(desktop): суммы выхода через formatAsset2Digits + одна сумма без разбивки
Typecheck / controller (pull_request) Has been cancelled
Typecheck / desktop (pull_request) Has been cancelled
- Суммы к возврату форматируются канонной formatAsset2Digits ("300.0000 RUB"
  → "300,00 RUB") в диалоге заявления и в оверлее — вместо сырого asset.
- Убрана разбивка "целевой + минимальный" (оба паевые, суммируются): показываем
  одну строку "Сумма к возврату".

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-16 05:59:14 +00:00
coopops 2f1e078fe2 fix(desktop): один лоадер в диалоге заявления о выходе
Typecheck / desktop (pull_request) Has been cancelled
Typecheck / controller (pull_request) Has been cancelled
Было две крутилки рядом («Формирование заявления…» + «Расчёт суммы…»).
Теперь заявление и сумма грузятся параллельно (Promise.allSettled) под одним
центрированным лоадером и показываются вместе. Тот же стиль лоадера — на фазе
проверки реквизитов. Сумма некритична: документ подписывается и без предрасчёта.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-16 05:55:22 +00:00
coopops 554798b74b fix(desktop): пункт «Выход из кооператива» в конец меню + надёжный гейт реквизитов
Typecheck / desktop (pull_request) Has been cancelled
Typecheck / controller (pull_request) Has been cancelled
- Пункт меню «Выход из кооператива» перенесён в самый низ (после «Поддержки»),
  иконка group_remove вместо logout — чтобы не сливаться с кнопкой «Выйти»
  (выход из кабинета, дверь-logout) внизу панели.
- Гейт реквизитов сделан fail-open: блокируем выход ТОЛЬКО при достоверно
  пустом списке методов; null/непонятный ответ не блокирует. + реактивный
  фолбэк: если бэкенд отклонил подачу из-за отсутствия реквизитов — диалог
  переключается на баннер. Авторитетный источник — бэкенд (createMembershipExit).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-16 05:44:34 +00:00
ant 0ae8702ade Merge pull request 'fix(registration): pending-счёт не показывать как «платёж принят»' (#149) from fix/registration-pending-display into dev
Build bootstrap container / build (push) Has been cancelled
Reviewed-on: #149
Reviewed-by: Алексей Муравьев <chairman.voskhod@gmail.com>
2026-06-16 05:36:38 +00:00
coopops 2d71706840 fix(registration): pending-счёт не считать «оплачено» — вести на оплату, а не на ожидание
Typecheck / desktop (pull_request) Successful in 13m50s
Typecheck / controller (pull_request) Has been cancelled
Счёт регистрационного взноса создаётся со статусом pending сразу при заходе на
шаг оплаты («счёт выставлен», деньги ещё не получены). При возврате на страницу
(перезагрузка/повторный вход) роутинг считал сам факт наличия платежа за «этап
оплаты пройден» и вёл на экран ожидания, где дефолтная ветка показывала «Ваш
платеж принят» — хотя оплаты не было и статус не менялся.

- SignUp: на экран ожидания/отказа ведём только при PAID/COMPLETED либо
  терминальном статусе (отказ/отмена/возврат); pending → шаг оплаты (QR + поллинг).
- WaitingRegistration: для pending — отдельная ветка «Ожидаем поступление оплаты»
  + кнопка «Перейти к оплате»; «платёж принят» остаётся только для PAID/COMPLETED.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-16 05:29:50 +00:00