seed-capital фаза 08-investments — программно подготавливает стенд для съёмки:
- soviet::sndagreement для petrov+ekaterina (соглашения wallet и blagorost
ЦПП), создаёт пользовательские кошельки
- capital::getclearance + soviet::confirmapprv — приложения к УХД-договору
для инвесторов
- capital::startproject + capital::openproject — компонент в active со
включённым приёмом инвестиций
- wallet::createdeposit + gateway::completeincome — пополняет кошельки
инвесторов (600 000 ₽ petrov, 300 000 ₽ ekaterina)
- capital::createinvest — petrov 500 000 ₽ → компонент MVP v1
- capital::createpinv — ekaterina 200 000 ₽ → программа «Капитализация»
Сценарий blagorost/investments.mjs снимает 3 кадра:
- toggle «Принимает инвестиции» включён в sidebar (выделен)
- вкладка «План» компонента: строка «Привлекаемые инвестиции» в колонке
«Факт» с реально поступившими средствами
- профиль с выделенной кнопкой «Инвестировать» под Кошельком Благороста
Проза investments.md описывает два пути инвестирования:
1) В компонент — пайщик сам приоритизирует, кнопка «Инвестировать (I)»
на карточке компонента
2) В программу «Капитализация» — кнопка «Инвестировать» на профиле,
совет распределяет средства между компонентами
Таблица сравнения двух путей; admonition про источник средств (основной
кошелёк) и про разделение паевых взносов пайщиков и финансирования
от кооператива.
mkdocs.yml: новая страница в разделе «Благорост» (Инвестирование).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
master-and-plan:
- seed-capital фаза 07 — программно одобряет УХД-договоры всех contributors
(soviet::confirmapprv через contributor_hash), подписывает приложение
председателя к компоненту (capital::getclearance + confirmapprv), назначает
мастера (capitalSetMaster) и устанавливает план 160 ч × 1500 ₽ + 50 000 ₽
расходов (capitalSetPlan)
- сценарий blagorost/master-and-plan.mjs снимает 3 кадра:
страница компонента с мастером, вкладка «План» компонента (таблица
план/факт со всеми расчётными пулами от контракта), сводный план проекта
- проза master-and-plan.md описывает роль мастера, три значения плана
(часы/ставка/доп.расходы), как контракт разворачивает их в полные пулы,
что разблокирует переключатель «Принимает инвестиции»
- mkdocs.yml: новая страница в разделе «Благорост»
project-create:
- сценарий расширен: после Мастерской открывает карточку проекта (статус
«Ожидает», sidebar выделен), переключает в «Активен» через UpdateStatus
dropdown, переходит в карточку компонента и активирует её
- через lib/annotate.mjs накладывает красные рамки на ключевые элементы:
«+ Проект (P)» в FAB, «+» в строке проекта, «Статус» в sidebar
- проза уточнена: Active разблокирует только приём коммитов; toggle
«Принимает инвестиции» в sidebar — только после плана; в проекты
напрямую не инвестируем
profile:
- второй кадр с прокруткой к таблице взносов
- проза описывает роли (Соавтор/Исполнитель/Инвестор/Координатор)
и отдельно строку «Получено в Благорост» (доля от других пайщиков
по правилам ЦПП)
infra:
- harness.mjs: shot() возвращает абсолютный path для пост-обработки PNG
(annotate без сериализации в manifest)
- seed-capital/index.ts: phase 07 в реестре фаз
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
seed-capital — поэтапная подготовка стенда для doc-shoot. Диспетчер принимает
список фаз и --up-to=<phase> — стенд можно остановить в любой точке для ручного
теста UI на конкретном этапе.
Фазы:
- 01-programs / 02-extension-config / 03-projects (реестр из _blago/INDEX.md + Кошелёк пайщика)
- 04-contributor (председатель ant) / 05-additional-contributors (ivanpetrov, ekaterina)
- 06-create-project-koshelek (рабочий проект для серии «Генерация»)
Сценарии и документация (components/docs/docs/new/blagorost/):
- adaptation, profile, projects-list, project-create
Orchestrator bin/shoot.mjs: поддержка meta.fixtures (явный список пайщиков для
seed-фаз) + meta.prepare (запуск нужных фаз перед сценарием).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Без merge'а старой ветки 989-4 (там 47/48 коммитов — устаревшие версии ledger2/reports работы, уже сделанной заново в 989-1/2/3 и доехавшей до reports). Принесена только инфраструктура для съёмки документации:
- docs-harness/ — playwright-сценарии + хелперы (lib/{harness,annotate,install,render-md}.mjs), run.mjs, package.json, README, scenarios/auth/signin.mjs
- components/boot/src/scripts/add-plain-participant.ts — генератор фикстур пайщиков для harness
- components/docs/docs/new/auth/signin.md + 3 PNG в assets/new/auth/signin/ — эталонная инструкция «Вход пайщика»
- components/docs/mkdocs.yml — добавлена секция «Вход в систему» в nav (без удаления ссылки «Реестр стандартов» — это посторонний шум 989-4)
Проверено: npm install проходит, синтаксис всех .mjs чистый, run.mjs стартует и доходит до загрузки сценария — дальше нужны живые chain/parser/controller/desktop и фикстура ivanpetrov (генерируется add-plain-participant).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Председатель больше не может откатывать операцию через UI: action `ledger2::revert`
сохранён, но принимает только подпись от whitelisted-контрактов
(`check_auth_and_get_payer_or_fail(contracts_whitelist)` без ветки `has_auth(coopname)`).
Why: ledger2 — учётный слой; зеркальная проводка председателем top-level
рассинхронизирует state контрактов-инициаторов (registrator/wallet/capital).
Контракты-инициаторы знают свой operation_code и могут собрать корректные
параметры зеркала из cooptypes/operations.hpp; они же одновременно откатывают
свои домены (participants/deposits/contributors).
Контракт ledger2:
- revert.cpp: убрана ветка has_auth(coopname); top-level от пайщика/председателя
падает на whitelist-check. Все остальные проверки (запрет o.mig.*, валидация
mirror-параметров) сохранены.
Backend (controller):
- удалены: revertOperation mutation, RevertOperationInputDTO, метод service
revertOperation + computeMirrorParams + WALLET_OP_CODE map, RevertBlockchainDomainInterface
и адаптер revert(), Ledger2StatePort.getOperationByGlobalSequence + repo-метод
- осталось: walmove (с UI), порт revert на cooptypes для контрактных потребителей
SDK:
- удалена mutation/ledger2/revertOperation.ts; cooptypes Actions.Revert + IRevert
оставлены (нужны другим контрактам).
UI (desktop):
- удалён RevertOperationDialog.vue
- из OperationsPage убраны: import RevertOperationDialog, useSession/storeToRefs,
revertDialog state, canRevert/openRevertFor/onRevertSuccess, кнопка ↩
- Ledger2 store/api/types: убран revertOperation method/IRevertOperationInput
Тесты:
- удалены revert.test.ts + adjust/revert.ts helper
- walmove.test.ts (3 теста) + остальной suite — зелёный
Полный suite: 92 passed | 1 skipped | 0 failed (clean reboot).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Все 90 тестов теперь зелёные (89 passed | 1 skipped | 0 failed).
Изменения:
* boot/src/init/infra.ts: 2s → 8s sleep между setContract и createToken.
На свежем nodeos setabi последнего контракта не успевал коммититься,
eosjs.getAbi('eosio.token') падал с "Read past end of buffer".
* boot/src/tests/registrator/registerUser.ts: legacy fund::coopwallet
(circulating_account/initial_account) → ledger2::accounts (счета 80, 86).
После Epic 1 confirmreg перестал вызывать Ledger::add, поэтому
legacy-баланс не пополняется. Теперь сравниваем приращение balance счёта
80 (Паевой фонд, на который попадает minimum через PUT_MINSHARE) и
счёта 86 (Целевое финансирование, через PAY_ENTRANCE).
* boot/src/tests/capital-import.test.ts: ожидаемый contributor.status
скорректирован 'active' → 'import'. ImportContributor создаёт запись
со статусом IMPORT (см. Status::IMPORT в contributors.hpp); перевод
в ACTIVE — отдельным шагом.
* boot/src/tests/ledger2-read-layer.test.ts: фильтр accountId без
actionNames возвращает все sibling-actions процесса (по дизайну
репозитория). Тест явно передаёт actionNames=['debit','credit'],
чтобы проверить только прямые проводки.
* boot/src/tests/capital.test.ts: regshare-идемпотентность — добавлен
sleep(2000) перед повторным regshare, чтобы expiration транзакции
отличался (иначе nodeos отклоняет как duplicate transaction).
* controller/schema.gql + sdk/zeus: регенерированы из ProcessRegistry
и Ledger2-резолверов (operationCode/operationCodes вместо action*).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
GraphQL HISTORY_QUERY: actionCode → operationCode; фильтр actionCodes →
operationCodes; ожидаемый код в filter-тесте mig.share → o.mig.share.
Регрессии после operations/processes refactor закрыты:
17 failed → 4 failed (84 → 86 passed). Оставшиеся 4 — pre-existing,
не связаны с рефакторингом нейминга:
- capital-import: contributor.status='import' вместо 'active'
(поведение capital-контракта, не наша область).
- registrator x2: compareTokenAmounts на legacy
fund::coopwallet.circulating_account.available, который не пополняется
с Epic 1 (Ledger::add удалён).
- ledger2-read-layer: фильтр accountId возвращает sibling-actions
одного процесса (включая wallet_to=80000), тест ожидает строгий 51000.
Pre-existing разрыв между документированным поведением фильтра
(через process_hash IN ...) и ожиданиями теста.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Два пробела после [116] «reports встроенный extension»:
- reports отсутствовал в getDefaultApps(), поэтому не оседал в postgres через installDefaultApps на старте controller'а — приходилось ставить руками.
- ReportsExtensionModule не имел метода initialize(), и lifecycle-service кидал «moduleInstance.initialize is not a function» при runApp.
Фиксы:
- getDefaultApps(): +reports (enabled=true, builtinDefaultConfig).
- ReportsExtensionModule: async initialize() {} — stub по образцу BuiltinPluginModule (нет своего крона/состояния).
- postgres-init.ts: дублирующий seed в initExtensionsInPostgres (ON CONFLICT DO NOTHING) — чтобы boot:extra сразу давал столу появиться, не ждать следующего onModuleInit.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Первый прогон показал: Σ wallets после миграции = 1_000_182_700 (testnet
накопил wallet-балансы при boot процессе создания 5 пайщиков), но AC8
ожидал ровно seedCash=5000. Это моя ошибка в ассерте — seed добавляет
seedCash к baseline, а не формирует всю сумму.
Фикс:
- Добавлена переменная baselineWalletsTotal (измеряется в seed-фазе ДО
первой миграции, как и baselineCashAcc/ShareAcc/EntryAcc).
- AC8 теперь проверяет totalWallets ≈ baselineWalletsTotal + seedCash.
Прогон полного pnpm test:all после фикса: 76 passed | 3 failed | 1 skipped.
Все 3 оставшихся fail — pre-existing, не связаны с ledger2-рефактором:
- registrator тесты ждут legacy `circulating_account`, отключённый ещё в
Epic 1 intro (b67d41b00f).
- capital-import тест на `status === 'active'` — логика importcontr.cpp
не менялась в этом PR.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
После [562-13] da8c443625 допуск (apprvappndx) и регистрация доли (regshare) разведены на два отдельных действия: signAppendix добавляет project_hash в appendixes контрибьютора, но сегмент в проекте больше не создаёт автоматически. Тесты 3-х спеков отражали старый «автоматический» флоу и падали на `Сегмент пайщика не найден` и `capital_contributor_shares = 0`.
Починка — без ослабления ассертов:
- processRegShare.ts: хелпер над capital::regshare
- A) "вклады… зарегистрированы автоматически" → "регистрируем доли через capital::regshare отдельным действием": спек сам вызывает regshare с user_shares = balance, жёстко проверяет segment.is_contributor=1, capital_contributor_shares==balance, Σ сегментов == Σ балансов, total_capital_contributors_shares==то же
- B) новый спек "regshare идемпотентен" — покрывает upsert-семантику regshare.cpp
- C) "тест ВЫСОКОЙ ТОЧНОСТИ": после signAppendix добавлен regshare по балансу — сегменты существуют до rfrshsegment/commitToResult; ассерт totalSharePercent≈100% остался
Результат: 60 passed | 1 skipped (61) против 3 failed | 56 passed | 1 skipped (60) до правки. Никаких .skip / try-catch-swallow — тесты честно зелёные.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Silent-catch в blockchain-consumer ACK'ил сообщения даже при упавшем saveDelta —
данные терялись безвозвратно; wallet::deposits/candidates2 не доходили до PG.
Consumer использовал `consumer-${random}` — каждый рестарт плодил zombie
со своими pending. Parser XTRIM MAXLEN=1000 при burst'ах удалял свежие
сообщения до того, как controller их прочитывал. rows$.subscribe без
error-handler'а молча убивал весь поток парсера при WS-разрыве.
Controller (blockchain-consumer.service.ts + redis-stream.service.ts):
- processDelta/processAction: убран try/catch-log-only → ошибки бросаются
до handleMessage, сообщение остаётся pending в группе для retry.
- processAction: убран setTimeout(3s) fire-and-forget → ACK теперь после
фактического save, а не до него.
- consumerName: стабильное 'coopback-main' вместо random.
- onModuleInit: recoverOwnPending (XREADGROUP STREAMS 0) доигрывает свои
pending после рестарта; reclaimStalePending (XAUTOCLAIM idle>5min)
забирает pending у зомби-consumer'ов.
- Периодические: XAUTOCLAIM 1/min + XTRIM MINID <first-pending-id> 1/30s
— Redis-память ограничена фактической consumption, не придуманным числом.
Parser (RedisNotifier + DeltaParser + ActionParser + config):
- publishEvent/publishDelta/publishFork: XTRIM убран (trim — роль consumer'а).
- REDIS_STREAM_LIMIT удалён из config.ts / .env-example / AGENTS.md.
- rows$.subscribe({next,error,complete}) вместо callback'а: ошибка/complete
триггерят process.exit, docker по restart:unless-stopped поднимает заново.
Integration tests (components/boot/src/tests/, +382 строк):
- process-registry.test.ts (Epic 4 e2e): 5 кейсов — reg.regist 2×apply,
wall.deposit, processes listing, hex-64 validation, 404 на unknown hash.
- ledger2-read-layer.test.ts (Story 1.23 e2e): 5 кейсов — on-chain↔
GraphQL срез accounts/wallets/history + фильтры actionCodes/accountId.
- shared/apiClient.ts: login chairman (eosjs-ecc) + gql + waitUntil.
Проверка на живой среде после fix'а:
- XLEN notifications = 70 (раньше 1001 фиксированно или потеря при burst).
- pending=0, lag=0 — ни одного silent drop, XAUTOCLAIM очистил зомби.
- blockchain_deltas впервые содержит wallet::deposits=3 (было 0).
- 18/18 boot integration + 77/77 controller unit тестов зелёные.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Все блокирующие находки code review.
CRITICAL
tests/unit/process-registry: переписаны под текущую архитектуру.
Epic 1 addendum удалил wjournal/journal, Phase A стал читать
blockchain_actions, но тесты остались мокать deltaRepo с _ledger2
якорями → 4 из 6 кейсов падали с NotFoundException. Commit message
Epic 4 "67/67 passed" стал неверен. Теперь:
- Phase A мокается через actionRepository (name='apply', action_code
в data, process_hash в data);
- Phase B — deltaRepo с правильными таблицами (candidates2, results,
deposits, etc.);
- processType выводится из ACTION_CODE_TO_PROCESS_TYPE (не из
delta.value.process_type);
- добавлены кейсы reg.regist (2 apply под одним hash) и mig.opening;
- account: '_ledger2' → 'ledger2' (bug с переименованием eeb4d50).
Результат: 8/8 ProcessRegistry-тестов зелёные, всего suite 77/77.
HIGH — PROCESS_HASH_LOCATOR: неверные имена таблиц/полей.
Проверено по C++ контрактам:
registrator::regs → candidates2 (field: registration_hash)
capital::segments удалена из локатора (project_hash, не result_hash)
capital::debts/result_hash→ debts (только для cap.debt, поле debt_hash)
capital::properties → pgproperties (field: property_hash)
marketplace::requests/request_hash → hash (поле именно hash)
Без этого getProcess для reg.regist/mkt.offereq/cap.act2*/cap.act2prp
возвращал пустое delta_history — UI был пустым на главном use-case.
HIGH — cap.act2res унификация (пользовательское уточнение).
Акт-2 это ОДИН процесс с ДВУМЯ эффектами (приём РИД в пай + погашение
займа), а не два процесса. В C++ ACTION_REGISTRY оба action_code
(cap.act2shr + cap.act2ln) уже указывают на CAPITAL_ACT2_RESULT =
"cap.act2res". Backend теперь то же:
- ACTION_CODE_TO_PROCESS_TYPE: cap.act2shr / cap.act2ln → cap.act2res;
- PROCESS_HASH_LOCATOR: cap.act2res → [results] (segments/debts
связаны через project_hash, не result_hash — не часть этого процесса);
- IProcessType: заменили cap.act2shr | cap.act2ln на cap.act2res.
UI при рендере process.actions видит два apply с разными action_code
→ группирует и показывает два эффекта раздельно внутри одной карточки
процесса (discriminator = action.data.action_code).
HIGH — Migration V2.1.0: индексы были без LOWER() → планировщик не
выбирал их, все getProcess упирались в seq-scan blockchain_deltas.
Теперь все expression-индексы построены на LOWER(value->>'field')
(совпадает с сервисным WHERE). Обновлены имена таблиц/полей согласно
новому PROCESS_HASH_LOCATOR. coopname не в индексе — OR (scope/value)
не покрывается одним expression-index, постфильтр дёшев (≤5 строк
per hash). CONCURRENTLY не используется (DEV-стенд).
HIGH — listProcesses GROUP BY двоил мульти-операционные процессы.
Было: GROUP BY action_code, hash, coopname — для reg.regist (entrfee+
minshare) и cap.act2res (shr+ln) показывало по две строки, totalCount
(COUNT DISTINCT hash) не совпадал с items.length.
Стало: GROUP BY LOWER(hash), coopname; MIN(action_code) для вывода
processType (у мульти-action процессов оба action_code маппятся в
один type, так что MIN даёт корректный тип).
HIGH — Убрать N+1 per-row counts в listProcesses.
Было: на каждую строку page 3 SQL (countActionsByHash +
countDeltasByHash + countDocumentsByHash). 100 строк × 3 SQL на
request → connection pool exhaustion под нагрузкой. Поля
actionCount/deltaCount/documentCount удалены из ProcessSummary DTO
и IProcessSummary interface. UI запрашивает getProcess(hash) при
раскрытии конкретного процесса — там счётчики выводимы из
actions.length/delta_history.length/documents.length.
Silent fixes:
- LIMIT/OFFSET в listProcesses: параметризация через $n вместо
литералов (безопасность + корректность при edge NaN).
- compareByBlock: tiebreaker по global_sequence (BigInt) когда
block_num + created_at совпадают — детерминированный порядок.
- blockchain-consumer: строгая проверка непустой строки в
value.coopname перед fallback на scope (пустой "" теряет дельту).
- Удалён dead LEDGER2_ACTION_NAMES.
- Устаревший комментарий ProcessRegistryDomainModule → blockchain_actions.
- add-test-user.ts: cleos-подсказки обновлены с wjournal на
candidates2/accounts2/wallets2.
Преднамеренно НЕ трогаю:
- Cross-tenant coopname validation (E1): оставлено для federation.
- Fork purgeAfterBlock (E6): вне scope Story 1.23, общесистемный
вопрос для отдельной задачи.
- Redis cache hardening (E9-E11): TTL 60s acceptable для MVP.
- P1 AC5: падать если accounts[99] не создан при свежем миграционном прогоне (было silent-pass на undefined) [ledger2-migrate.test.ts]
- P2 AC6: expect(acc).toBeDefined() перед чтением .balance у cash/share/entry (TypeError → понятный fail) [ledger2-migrate.test.ts]
- P3 AC7: snapshot meta + балансов ДО и ПОСЛЕ повторного migrate() — проверяем что migrated_coops/last_migrated_coop_index/debit_balance(51)/credit_balance(80) не изменились [ledger2-migrate.test.ts]
- P5 Заменить literals Number().toBe(0)/(1) на LedgerAccountType.ACTIVE/PASSIVE enum (экспортирован из walletUtils) — устойчивость к переименованию типов [ledger2-migrate.test.ts + walletUtils.ts]
- P6 depositToWallet(amount): Number.isFinite + amount > 0 guard — NaN/Infinity/отрицательные значения теперь дают понятную ошибку до сериализации в asset [depositToWallet.ts]
- P7 Dead-code cleanup в wallet.test.ts: убраны tester1/walletProgramStates1/addUser — никогда не использовались в ассертах; вместо этого добавлены expect().toBeDefined() на wallet/program/depositId/userWallet
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- D1: sender-guard в walletop/debit/credit — допустим только inline из apply (парность double-entry гарантирована, top-level вызов с ledger2@active запрещён)
- D2: TODO(payer) в walletop/debit/credit — payer=get_self() временно до общего перехода на coopname+eosio.code
- D3: read_legacy_balances аварит на неожиданном legacy id + проверяет символ валюты (silent-drop заменён на check(false))
- D4: повторный migrate() после полного прогона — тихий no-op без eosio::check (тест AC7 обновлён)
- apply: require_recipient после валидации coopname (не до)
- walletop: ISSUE требует wallet_from==0, BLOCK/UNBLOCK требуют wallet_to==0
- walletop/debit/credit: убран require_recipient (дублировал notification из apply)
- accounts2::is_empty() теперь включает поле balance
- actions.hpp: добавлен static_assert(wallets_exist_in_registry) — compile-time проверка что wallet_id из ACTION_REGISTRY существуют в LEDGER2_WALLET_REGISTRY
- migrate: guard против permanent-lock на пустой таблице cooperatives
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
FIXES после live-прогона через pnpm run reboot + add-test-user:
1) parser/src/config.ts: добавлены ledger2+marketplace в subscribedContracts —
без этого parser не пускал ledger2-дельты в Redis stream и controller не
получал wjournal/journal, т.е. весь pipeline стоял.
2) controller/src/infrastructure/blockchain/blockchain-consumer.service.ts:
processDeltaDelayed фильтровал по `delta.value?.coopname`, но ledger2
(wjournal/journal/wallets/accounts) и большинство кооп-scope таблиц
хранят coopname В SCOPE, а не в value.jsonb. Добавлен fallback на scope:
`deltaCoop = delta.value?.coopname ?? delta.scope`.
3) domain/process-registry/services/process-registry.service.ts:
- LEDGER2_CODE изменён с `_ledger2` на `ledger2` (имя on-chain аккаунта
реальное, без подчёркивания — verified через cleos get abi ledger2).
- phase A + listProcesses: coopname-скоупинг по `d.scope` (вместо
несуществующего value->>'coopname' для ledger2 journals).
- phase B (scanEntityDeltas): принимает оба варианта — `scope = $coop
OR value->>'coopname' = $coop`, чтобы охватить и per-coop-scope
таблицы, и singleton-scope контракты (registrator.regs).
- LOWER() обе стороны при сравнении process_hash: ончейн хранит
checksum256 uppercase, а нормализация API — lowercase.
- listProcesses возвращает processHash в lowercase через LOWER() в
SELECT (единообразно с getProcess).
- countDeltasByHash/countDocumentsByHash: LOWER() + scope=coop.
4) migrations/V2.1.0: expression-индексы ledger2 journals теперь на
(process_hash, scope) и (process_type, scope) — совпадают с фактическим
where-условием сервиса.
5) cooptypes/common/names: `_ledger2.production/testnet = "ledger2"`
(без подчёркивания, соответствует on-chain имени).
6) boot: добавлен CLI `pnpm run cli add-test-user <username>` для smoke-
проверки Epic 4 на живом стенде после reboot. Также установлен
`registration_hash: generateRandomSHA256()` в:
- boot/init/infra.ts: adduser(ant) + adduser для 4 дополнительных
членов совета (boot:extra mode);
- boot/init/participant.ts: addUser+addUser2.
ПРОВЕРКА (live через curl + JWT подписанный JWT_SECRET из .env):
cleos get table ledger2 voskhod wjournal — 2 записи:
id=0 reg.minshare, process_type=reg.regist, process_hash=<sha256>
id=1 reg.entrfee, process_type=reg.regist, process_hash=<sha256>
(тот же hash — мульти-операционный процесс reg.regist)
query { process(hash:"28fe0b46...",coopname:"voskhod") }
→ process_type="reg.regist"
delta_history: 4 (2 wjournal + 2 journal)
actions: 3 (registrator::adduser + 2×ledger2::apply)
documents: []
query { processes(filter:{coopname:"voskhod"}, pagination:{...}) }
→ totalCount=1, processHash lowercase, actionCount=3, deltaCount=4
Redis cache: ключ `process::voskhod::<hash>`, TTL=60s ✓
Auth: без JWT → 401 Unauthorized ✓
pnpm test — 67/67 passed (все unit-тесты по-прежнему зелёные).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
— migrate.cpp: для каждой ненулевой laccount сумма total=available+blocked кладётся в ledger2::accounts[legacy_id*1000] в правильное плечо оборотов (ACTIVE/A-P → debit_balance, PASSIVE → credit_balance); при наличии целевого фонда параллельно заполняется wallets (SHARE_FUND=2, ENTRANCE_FEES=3, LONG_TERM_LOANS=6)
— маппинг legacy→ledger2 задокументирован в PRD §4.1.5 таблицей (FR-L-13) + issue 989-1: 51→51000(A,Dr), 80→80000(P,Cr)+wallet2, 861→861000(P,Cr)+wallet3, 67→67000(P,Cr)+wallet6
— зафиксирована follow-up миграция (Story 1.11 backlog): вычленение минимальных паевых взносов из wallets[SHARE_FUND=2] в wallets[MIN_SHARE_FUND=1] по количеству пайщиков × размер обязательного пая; accounts не трогается (проводка Dr51/Cr80 одна и та же)
— интеграционный тест расширен с 8 до 9 AC: AC5/AC6 теперь проверяют что BANK_ACCOUNT и MEMBER_DEBT мигрируют в accounts (Dr), но не создают кошельков; AC7 отдельно проверяет credit_balance для 80000/861000/67000
— legacy одноконтурный, поэтому Σ Dr ≠ Σ Cr после миграции — это ожидаемо и задокументировано в миграции
Все mono-ai-N теперь могут работать одновременно на изолированной
инфраструктуре. Имена контейнеров автогенерируются compose-проектом
(префикс из .env), хост-порты и URL берутся из .env.
- docker-compose.yaml: убраны статические container_name, host-порты
через ${VAR:-default} для обратной совместимости
- boot scripts (reboot/clean_reboot/extra_reboot/clear): source корневого
.env и docker exec → docker compose exec -T (через service name)
- networks.sh, preactivate.sh: cleos/curl используют ${CHAIN_URL:-...}
- boot health.ts, configs/index.ts, configs/networks.ts: читают
process.env.CHAIN_URL вместо hardcoded localhost:8888
- configs/contracts.ts: добавлен ledger2
- init/infra.ts: addUser получил недостающий registration_hash
В каждой папке mono-ai-N нужен локальный .env (в .gitignore) с
INSTANCE_INDEX, COMPOSE_PROJECT_NAME, host-портами и
CHAIN_URL/API_URL/MONGODB_URL. Без .env compose поднимется на дефолтных
портах (как у mono-ai-1).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>