WEB-301 · Задача · Инфраструктура · web
Стенд ролей не самодостаточен: materialize расплющил pnpm-раскладку и разорвал peer-соседство (21 из 31 пакета неразрешим)
Закрыт
P0 · горит
ведёт: fix-wave
Суть
## Симптом
Подписанный владельцем стенд умирает на первом же `require` при `exec node server.js`:
```
Error: Cannot find module '@swc/helpers/_/_interop_require_default'
Require stack: .../bound-repo/.next/standalone/node_modules/next/dist/shared/lib/constants.js → server.js
```
## Корень
Шаг «materialize» в BUILD-PLAN заменяет **все** symlink на копии, чтобы выполнить требование процедуры «symlink в стенде запрещён» (`stand-build.log:1312`: «replaced 752 symlinks (752 directories, 0 files); stand is now self-contained»).
В pnpm-раскладке `node_modules/next` — symlink на `node_modules/.pnpm/next@16.2.6_…/node_modules/next`, а его peer-зависимости (`@swc/helpers`, `@next/env`, `caniuse-lite`, `baseline-browser-mapping`) лежат **рядом**, в том же `.pnpm/next@…/node_modules/`. Node резолвит их подъёмом вверх от физического пути модуля.
Materialize сделал `node_modules/next/` самостоятельным каталогом → физический путь сменился → подъём ведёт в `<stand>/node_modules/`, где `@swc` нет. Байты в стенде ЕСТЬ (22 записи `@swc` в `STAND-FILE-MANIFEST.json`), но код до них не дотягивается. Скрипт при этом отчитался «self-contained» — ровно наоборот.
## Масштаб (статический обход top-level пакетов и их dependencies)
Разрешимо 10 из 31. Неразрешимо 21: next (6), jimp (27), jsdom (20), @slack/bolt (10), mammoth (10), tesseract.js (9), node-telegram-bot-api (9), socket.io (7), twilio (7), @auth/core (5), @asamuzakjp/css-color (5), officeparser (4), sharp (3), googleapis (2), @xenova/transformers (2), rtf-parser (2), 7zip-min / franc-min / pdf-parse / stripe / tldts (по 1). Дефект системный, не единичный пакет.
## Почему не чинили на месте
- `standTreeSha256 = 25e2e198…` входит в подписанный владельцем манифест → любой добавленный файл даёт корректный `STAND_TREE_MISMATCH`.
- `NODE_PATH` был бы обходом, а не починкой: он заставил бы Node искать модули по пути, который стенд не объявляет, и похоронил бы ровно то свойство, которое вся конструкция доказывает.
- Лаунчер `start-standalone-v10.sh` — тоже замороженный артефакт (sha `940372c1…`).
Корректный исход: остановиться fail-closed. Расход $0.
## Фикс (волна standfix, M4, бриф `/Users/milamarty/work/standfix-brief.md`)
Один из трёх путей с обоснованием: перенос peer-соседей в `node_modules/<pkg>/node_modules/`; либо материализация pnpm-раскладки целиком с сохранением относительности; либо плоский `npm install --omit=dev`. Требование «symlink запрещён» сохраняется (`find -type l` == 0).
Затем пересборка стенда, перевыпуск `STAND-FILE-MANIFEST.json` / `STAND-BUILD-RECEIPT.json` / манифеста под НОВЫМ releaseId и **третья подпись владельца** (меняется standTreeSha256 → меняется подписываемый документ).
Источник: волна rolesbattery, отчёт M4 `/Users/milamarty/work/ROLESBATTERY-REPORT.md`, run-каталог `/Volumes/M4Ext/ops/paidlegs/rolesrun-20260821/` (21.08 01:42).
## 2026-08-23 PDFPOSTQA: N-A — stand/package issue, не PDF browser scenario.
PDFPOSTQA: бесплатные сценарии только; новые paid generation/export/import не запускались. Полный реестр и evidence: /Users/milamarty/work/PDFPOSTQA-REPORT.md; screenshots: /Users/milamarty/work/PDFPOSTQA-SHOTS/.
## 2026-08-23 BOARDSWEEP: DEAD-SOUL-CLAIM
- Evidence: updatedAt=2026-08-21T00:55:02.541Z, older than 24h; standfix trail is completed/archived and no active standfix wave/process is present now. Candidate for return to todo; status unchanged.
- buildFixed: <empty>; canonical: v4-081ff4fb5 / 081ff4fb5f6110f8001daec7ed910ccef2e395dc.
- Status preserved by sweep: in_progress.
## 2026-08-23 — WEB301_FIX_READY
Исправление закоммичено: 88dfe05260136f329da95c9ee167fe94fcc33dcc, ветка agent/web301-pnpm-20260823, worktree /Users/milamarty/work/roles-web301.
Корень: старый materialize оставлял/расплющивал pnpm symlink-раскладку и терял bucket-соседство peer-зависимостей. Добавлен tools/materialize_stand.py, подключён в V10/V11 builders: pnpm package symlink переносится вместе с bucket-local node_modules, включая peers; результат публикуется только symlink-free. Dangling/escaping links отвергаются fail-closed.
Доказательство чистой распаковки: python3 tools/test_materialize_stand.py — 6/6 tests PASS, Node resolves 31/31, symlinks 0. Canonical verify_stand_selfcontained.mjs: topLevelPackages=31, dependencyEdgesChecked=62, hiddenDependencies=0, RESULT=PASS. Негативный контроль после quarantine одного peer: hiddenDependencies=1, top-level packages with hidden runtime deps: 1 of 31, RESULT=FAIL, rc=1; дерево восстановлено и повторно прошло.
Полный отчёт: /Users/milamarty/work/WEB301-REPORT.md. Production deploy не выполнялся. Статус тикета сохранён как был.
Доказательства
[2026-08-27 02:20Z] epictriage (M1): FIX_READY, self-contained verify PASS, негативный контроль PASS — работа фактически сделана, нужна приёмка/посадка. -> review.
[2026-08-27 19:50Z] ПРИНЯТ acc301 (независимый, M4, Codex R9.5 автор): materialize/pnpm стенд самодостаточен — test_materialize 6/6 + clean-unpack 31/31, canonical verify_stand_selfcontained hiddenDependencies=0 RESULT=PASS, 0 симлинков, негатив (quarantine peer -> FAIL -> restore PASS). Production rebuild/signature = отдельная release-стадия. -> done.
Починено в
88dfe05
Воркер
не привязан — привязать:
curl -X POST https://bugs.wool2.online/api/web/assign -H 'content-type: application/json' \
-d '{"issueId":"WEB-301","session":"<имя tmux-сессии>","host":"m4"}'
Обновлён
2026-08-26T22:03:12.386Z