# AICOM: закупка внешней диагностики

> 🌐 [English](https://github.com/alexar76/aicom/blob/main/docs/factory-subcontracting.md) · [Русский](https://github.com/alexar76/aicom/blob/main/docs/factory-subcontracting.ru.md) · [Español](https://github.com/alexar76/aicom/blob/main/docs/factory-subcontracting.es.md) · [Français](https://github.com/alexar76/aicom/blob/main/docs/factory-subcontracting.fr.md) · [中文](https://github.com/alexar76/aicom/blob/main/docs/factory-subcontracting.zh.md)

AICOM создаёт продукты собственной командой агентов. Теперь перед очередным внутренним исправлением компания может купить ограниченную диагностическую услугу через AIMarket. Подрядчик возвращает рекомендации; код меняет разработчик AICOM, а продвижение продукта определяют существующие проверки QA.

Функция **выключена по умолчанию**. Установка кода не включает реальные покупки, не пополняет счёт, не регистрирует исполнителя и не отправляет исходники.

## Алгоритм решения

1. Перед задачей разработчика отбираются замечания QA уровня high/critical с указанием файла.
2. Идентификатор проблемы — файл и заголовок — связывается с последней завершённой задачей разработчика. Повтор той же задачи не увеличивает счётчик. После трёх наблюдений — начальной генерации и двух завершённых внутренних исправлений — проблему можно передать на диагностику.
3. Покупка пропускается при аварийной остановке, существующем застревании, остановке по бюджету, отсутствии результата генерации или `external_diagnostics_allowed: false` у продукта. Ограничения исправлений и остановки оператора сохраняются.
4. Запрос содержит **один разрешённый исходный файл**, замечание и правила приёмки. Отпечаток включает содержимое и версию контракта.
5. Подписанный well-known Hub проверяется по заранее закреплённым ключам. Поиск ограничивается одобренными услугами; демо, недоступные предложения и несовместимые обязательные поля исключаются. Среди подходящих предложений выбирается меньшая цена.
6. Цена атомарно резервируется в пределах лимитов продукта и суток с сохранением денег на завершение. Допускаются один заказ с неизвестной оплатой на продукт и две попытки выбора исполнителя на проблему. Один и тот же исполнитель повторно для этой проблемы не вызывается. Следующая покупка возможна только после нового завершённого внутреннего исправления, в том числе после отклонённого отчёта.
7. Выполняется один вызов, проверяются платёжная квитанция и отчёт. Таймаут или неизвестная оплата сохраняют резерв; автоматического повторного платного вызова нет.
8. Принятая диагностика передаётся внутреннему разработчику как недоверенные внешние данные. После успешной задачи разработчика записывается результат следующей QA. Отчёт сам не меняет состояние продукта и не отменяет проверки.

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

## Настройка

Скопируйте [пример](https://github.com/alexar76/aicom/blob/main/config/procurement.example.json) в `<AIFACTORY_DATA_ROOT>/config/procurement.json`.

| Параметр | Значение |
|---|---|
| `enabled` | `true` после настройки проверенного исполнителя |
| `hub_url` | HTTPS-адрес Hub; перенаправления не выполняются |
| `hub_public_key` | Проверенный и закреплённый base64-ключ Ed25519 |
| `hub_pq_public_key` | Закреплённый ключ ML-DSA-65 для гибридных подписей; пустой для Hub только с Ed25519 |
| `providers` | До 10 одобренных пар `product_id` / `capability_id` |
| `share_globs` | Разрешённые пути относительно каталога кода продукта |
| `per_call_usd` | Лимит заказа, исходно $0.25 |
| `per_product_usd` | Внешние расходы и резервы продукта, исходно $0.50 |
| `per_day_usd` | Лимит общего журнала компании за сутки UTC, исходно $2.00 |
| `completion_reserve_usd` | Остаток на внутреннее завершение и QA, исходно $0.50 |

Отдельный пополненный credits-счёт задаётся через `AIFACTORY_PROCUREMENT_API_KEY`; его баланс и права настраиваются в Hub. Эта версия не создаёт и не подписывает mandate. Счёт с обязательным mandate отклонит такой клиент. Нужен положительный `AIFACTORY_MAX_PIPELINE_COST_USD`: отключение общего ограничителя не разрешает неограниченные покупки.

Конфигурация читается перед подходящей задачей. API-ключ нельзя помещать в JSON. Для общего суточного лимита все процессы компании должны использовать один каталог данных и журнал; отдельные установки имеют отдельные лимиты.

Поддерживаются услуги, исполняемые выбранным Hub (`source_hub: local`), включая внешние endpoints, которые вызывает сам Hub. Федеративные предложения и выдача подрядчику бюджета для дальнейшего субподряда пока не поддерживаются.

Поиск должен явно возвращать `credits_eligible: true`. Продавцы с обязательной прямой оплатой на кошелёк исключаются. Старый Hub без этого поля не даст подходящих предложений; сначала разверните соответствующее обновление Hub. Для исполнителя нужно проверить и совместимость credits, и контракт диагностики.

## Передача кода и контракт исполнителя

Исполнитель должен поддерживать **`aicom.diagnosis/1`**. Произвольная услуга `code.review@v1` не считается совместимой автоматически; её нужно проверить до внесения в список.

Вход: `contract`, `issue`, `fingerprint`, `finding`, `files`, `acceptance`. В `files` передаётся один UTF-8-файл до 16 000 байт. Расширения: py, ts, tsx, js, jsx, go, rs, java, cs, html, css. Скрытые пути, каталоги секретов, симлинки, выход за область и распространённые шаблоны ключей блокируются. Репозиторий целиком, учётные данные, ТЗ и журналы выполнения не передаются.

В `share_globs` используется Python fnmatch: символ `*` может покрывать разделитель пути. Задавайте узкую область. Поиск секретов эвристический; оператор должен разрешать только исходники, пригодные для передачи.

Пути QA относительно продукта, с префиксом `code/<product_id>/` или `data/code/<product_id>/`, а также абсолютные пути внутри каталога кода этого продукта приводятся к одному идентификатору. Пути за пределами каталога отклоняются.

Поле `result` ответа Hub имеет следующую форму; пример цитирует строку `return 1 / x`:
```json
{
  "contract": "aicom.diagnosis/1",
  "issue": "COPY_FROM_REQUEST",
  "fingerprint": "COPY_FROM_REQUEST",
  "diagnosis": [{
    "file": "app/main.py",
    "line": 2,
    "evidence": "return 1 / x",
    "explanation": "Zero input divides by zero.",
    "suggested_check": "Test divide(0) against the required error contract."
  }]
}
```

Нужно повторить идентификаторы запроса и вернуть 1–5 замечаний. Цитата должна совпадать с непустым фрагментом указанной строки переданного файла. Объяснение и предлагаемая проверка обязательны, до 2000 символов каждое. Изменившаяся версия исходника отклоняется. Предложения не исполняются как команды оболочки.

Приёмка подтверждает структуру и привязку к исходнику, но не истинность диагноза. Разработчик проверяет выводы; обычные тесты и QA сохраняются. Подпись квитанции подтверждает платёжные поля, а не текст диагностики. Проверяются Ed25519 и присутствующая либо обязательная ML-DSA-65 по закреплённым ключам.

## Деньги, сбои и восстановление

В `state/procurement.sqlite3` сохраняются заказы, отпечатки, отчёты, резервы и результаты QA. Исполнение (`in_flight`, `accepted`, `rejected`, `unresolved`) отделено от оплаты (`unknown`, `paid`, `not_charged`).

Отклонённый отчёт тоже может быть оплачен. Этот credits-путь не обещает возврата Pay-on-Verified. HTTP-ошибка, отсутствие или неверная подпись квитанции, обрыв связи и остановка процесса оставляют оплату неизвестной. Новые покупки продукта блокируются; внутренние работы могут продолжаться при достаточном остатке бюджета.

После проверки расчётов в Hub используйте просмотр и явную сверку:
```bash
.venv/bin/python scripts/procurement_orders.py list --product PRODUCT_ID
.venv/bin/python scripts/procurement_orders.py reconcile \
  --order PURCHASE_ID --charged-usd 0.10 \
  --evidence "Hub billing reference and reconciliation reason"
```

Суммы в `list` — микро-USD: 100 000 = $0.10. Для другой установки укажите `--data-root PATH` перед подкомандой. Сверка записывает фактическое списание и основание, снимает резерв и не вызывает исполнителя повторно. Принятый отчёт при этом не создаётся. Ноль допустим только после подтверждения отсутствия списания.

Общий ограничитель учитывает **наблюдаемые расходы LLM + оплаченные услуги + неизвестные резервы**. SQLite сериализует покупки параллельных продуктов. Незавершённые резервы прошлых дней расходуют и текущий суточный лимит. Расходы LLM учитываются по факту: уже выполняющийся запрос модели может превысить оставшуюся оценку. Это не жёсткая предварительная авторизация всей стоимости токенов.

## Проверки и границы версии

Тесты используют настоящий модуль подписи Hub и имитацию HTTP без реальных платежей. Проверяются передача отчёта разработчику, подписи, устаревание, область файлов, выключенные политики, конкурентные резервы, неизвестная оплата, восстановление, сверка и сохранение QA.

Журнал фиксирует результат следующей QA и число оставшихся замечаний. Он не доказывает причинную экономию и пока не обучает рейтинг исполнителей. Автоматическая сверка, передача разработки целиком, федеративные покупки, дальнейший субподряд и длительные задания — вне первой версии.

Код: [контракт](https://github.com/alexar76/aicom/blob/main/core/procurement_contract.py), [журнал](https://github.com/alexar76/aicom/blob/main/core/procurement_store.py), [оркестрация](https://github.com/alexar76/aicom/blob/main/orchestrator/procurement.py), [квитанции](https://github.com/alexar76/aicom/blob/main/core/procurement_receipt.py).

## Проверка реальными вызовами — 2026-10-11

[Сохранённые результаты](https://github.com/alexar76/aicom/blob/main/docs/evidence/factory-subcontracting-live-2026-10-11.json) подтверждают реальные HTTPS-вызовы временного Hub и запрос к `minimax/minimax-m3` через настроенный OpenRouter-счёт фабрики. Отчёт принят с привязкой к исходнику, подписи квитанции Ed25519 и ML-DSA-65 проверены, **тестовый** баланс изменился с $0.50 до $0.40. Повтор задачи не вызвал сеть. Успешный запрос модели стоил $0.00033588. Рабочие счета и каталог не менялись; это не подтверждение покупки на production-рынке или полной сборки продукта.

Два предшествующих прогона выявили таймаут исполнителя и неверную форму отчёта. При таймауте резерв сохранился без повтора; неверный отчёт учтён как оплаченный, но отклонённый. В запрос добавлено явное требование массива из 1–5 замечаний. У первого запроса модели нет квитанции usage, поэтому общий расход модели пока не сверен.

Проверка живого `modelmarket.dev` дала HTTP 200, но опубликованный поиск ещё не содержит `credits_eligible`, совместимый исполнитель диагностики не найден. Покупка через production остаётся непроверенной до обновления Hub и развёртывания совместимого исполнителя.

Повтор изолированного теста [скриптом](https://github.com/alexar76/aicom/blob/main/scripts/procurement_live_probe.py):
```bash
.venv/bin/python scripts/procurement_live_probe.py \
  --ssh-host YOUR_FACTORY_SSH_ALIAS --container aicom-app-1 \
  --output /tmp/procurement-live-result.json
```
Каждый запуск может оплатить один реальный запрос модели; оценка по её тарифам ограничена $0.25. Ключ остаётся в серверном контейнере. Hub, TLS-ключи, каталог и тестовые credits временные.

## Включено на production — 2026-10-11

Рабочая фабрика теперь покупает `aicom.diagnose@v1` у одобренного сервиса `aicom-diagnostics` через `https://modelmarket.dev`. Первый исполнитель принадлежит оператору, работает отдельно от фабрики и использует настроенную модель `minimax/minimax-m3`. Hub проверяет подпись Ed25519 ответа исполнителя с привязкой к запросу; фабрика проверяет квитанцию Hub с Ed25519 и ML-DSA-65. Прежние вызовы Metis остаются на этапах архитектора и методолога — это другие задачи, не повторная диагностика ошибки кода.

Лимиты: $0,25 за вызов, $0,50 на продукт, $2 в сутки UTC и резерв $0,50 на внутреннее завершение. Общий лимит pipeline сохранён: $40. Передавать разрешено только `backend/app/*.py` и `frontend/src/*.tsx`. Отдельному счёту выделен разовый операционный грант $2 в credits без автопополнения. Успешная production-покупка списала $0,10; проверенный остаток — $1,90. Это выданные оператором credits Hub, а не подтверждение внесения фиатных денег или выручки от независимого клиента.

Production-проверка использовала синтетическую ошибку и отчёт реальной модели. Успешная подписанная доставка использовала отчёт, сохранённый во время предшествующей проверки подписи, без ещё одного запроса модели. Повтор принятой задачи фабрики не вызвал сеть. Пять предыдущих отказов сверены с освобождёнными резервами Hub: списаний нет. Адаптер обрабатывает JSON в отдельном блоке Markdown и вариант поля `suggest_check`; проверка цитат и публичного контракта остаётся строгой. Пройдено 45 профильных тестов. Полное исправление реального продукта этим тестом не подтверждается.

[Результаты production-проверки](https://github.com/alexar76/aicom/blob/main/docs/evidence/procurement-production-2026-10-11/README.md). Для остановки новых покупок задайте `enabled: false` в production-файле `data/config/procurement.json`; перезапуск не нужен. Релиз и откат сохранены в `/root/procurement-release-20261011/`. Push в GitHub не выполнялся.
