IOS-069 · Задача · UX · web
FC-археология: replay волн фабла на канон
Закрыт
P1 · важно
ведёт: fc-replay
эпик: IOS-067
Суть
Бандл /tmp/fable-waves.bundle (M4) — 8 голов: honesty 094ef2d25, F1 778e7387f, Wave2 51dd2f701, Wave3 0144699d7, Stage-A 727627614, i18n 591316c85, drawer 132732e14, helper 0d49c3a26. Дерево: /Users/milamarty/Projects/fc-replay (канон+bundle fetch). По каждой голове: что в ней (diffstat), ложится ли на канон (cherry-pick/3way сухой прогон), конфликты где. Мины из README §3 ОБЯЗАТЕЛЬНЫ: RRF≠cosine (rescoreByCosine ДО гейта), i18n в messages/*.json не в translations.ts, drawer на deferOverlaysReady. НЕ пушить, НЕ деплоить — итог заплатками ~/Downloads/FC-REPLAY-*.patch по волнам.
Чем закрывается (приёмка)
Порядок replay с конфликт-картой; заплатки волн, ложащиеся на канон; отчёт в тикет.
Лента
2026-08-01T23:58:26.599Z · вахтёрВоркер fc-replay-069 объявил БЛОКЕР или ждёт сборочный слот. Жив, но не продвигается. Последняя строка: gpt-5.5 high · ~/Projects/fc-replay
2026-08-02T00:08:30.604Z · вахтёрВоркер fc-replay-069 объявил БЛОКЕР или ждёт сборочный слот. Жив, но не продвигается. Последняя строка: gpt-5.5 high · ~/Projects/fc-replay
2026-08-02T00:23:39.889Z · вахтёрВоркер fc-replay-069 объявил БЛОКЕР или ждёт сборочный слот. Жив, но не продвигается. Последняя строка: gpt-5.5 high · ~/Projects/fc-replay
2026-08-02T00:25:29.242Z · fc-replay# IOS-069 fc-replay report
SCOPE: archaeology replay of 8 Fable heads onto local canon, no push, no deploy.
BASE: d993a96490730f5e7c1249d82686678ca3a2a4ff (branch main; local canon commit says serving 1a2cc76). Remote CANON.json serving.sha f629618e40d6034f798de1878683d1821ab7cad8 was not present in this local repo.
Patches:
- /Users/milamarty/Downloads/FC-REPLAY-honesty.patch
- /Users/milamarty/Downloads/FC-REPLAY-F1.patch
- /Users/milamarty/Downloads/FC-REPLAY-Wave2.patch
- /Users/milamarty/Downloads/FC-REPLAY-Wave3.patch
- /Users/milamarty/Downloads/FC-REPLAY-Stage-A.patch
- /Users/milamarty/Downloads/FC-REPLAY-i18n.patch
- /Users/milamarty/Downloads/FC-REPLAY-drawer.patch
- /Users/milamarty/Downloads/FC-REPLAY-helper.patch
- /Users/milamarty/Downloads/FC-REPLAY-clean-applyable.patch (convenience bundle for clean non-duplicate replay pieces)
- /Users/milamarty/Downloads/FC-REPLAY-Wave1-resolved.patch (manual resolved replay of honesty/Stage-A semantic Wave-1 onto canon)
- /Users/milamarty/Downloads/FC-REPLAY-Wave2-core-only.patch (Wave2 library/core changes excluding unsafe API/action/modal conflicts)
- /Users/milamarty/Downloads/FC-REPLAY-Wave3-core-only.patch (Wave3 library/core changes excluding unsafe `src/app/researchActions.ts` wiring conflict)
Ranges used:
- honesty: a09984eb50d3eef53964f9e22e4233e69fb58e38..094ef2d25
- F1: 094ef2d25..778e7387f
- Wave2: 778e7387f..51dd2f701 (includes B2, C2, B1, E1)
- Wave3: 51dd2f701..0144699d7 (includes F3/F4, B3/BOLD-1)
- Stage-A: bc9562a044c70a880768996e7e28578c0276311a..727627614
- i18n: 29b052de60a6535839c4b57b441a4cefd7c43924..591316c85
- drawer: 591316c855b649a497167b0bd448ffccf6a8f1c8..132732e14
- helper: e4c99f535^..0d49c3a26 (includes bootstrap plus QA-v2/v3/v4 hardening)
Dry-run proof:
- Exact log: /Users/milamarty/Downloads/fc-replay-ios069-checks/individual-index-apply-summary.txt
- Command pattern: create detached worktree at BASE, run `git apply --3way --index /Users/milamarty/Downloads/FC-REPLAY-<wave>.patch`, record exit/status, remove worktree.
Apply results:
- Wave1-resolved: exit 0 from fresh detached canon worktree. This supersedes raw honesty/Stage-A for landing because it resolves obvious conflicts without losing current canon behavior.
- F1: exit 0, clean.
- drawer: exit 0, clean/no-op against this canon. The standalone drawer patch touches `src/components/layout/AppShell.tsx` and `src/components/layout/DeferredOverlayStack.tsx`; this canon already has the stronger `deferOverlaysReady` mount path, not `activeModal`, so it does not appear in the cumulative clean bundle.
- helper: exit 0, clean.
- honesty: exit 1. Conflicts: `src/components/ai/FactcheckSettings.tsx`, `src/components/factcheck/FactcheckPanelDrawer.tsx`, `src/components/factcheck/VerdictChip.tsx`, `src/components/modals/FactcheckModal.tsx`, `src/lib/factcheck/conceptLabels.ts`, `src/lib/factcheck/pipeline.ts`.
- Stage-A: exit 1, same conflict set as honesty. Stable patch-id equals honesty (`d2bbc9deadfa3bc8499a1e9db6221bd79757dc51`), so Stage-A is an archaeological duplicate of honesty on a different historical base.
- Wave2: exit 1. Conflicts: `src/app/api/deep-research/run/route.ts`, `src/app/researchActions.ts`, `src/components/modals/DeepResearchModal.tsx`. Non-conflict additions include `src/lib/research/retrieve.ts`, `src/lib/research/entailment.ts`, `src/lib/deepResearch/pipeline.ts`, and shared PDF files.
- Wave2-core-only: exit 0 from canon. Excludes only `src/app/api/deep-research/run/route.ts`, `src/app/researchActions.ts`, `src/components/modals/DeepResearchModal.tsx`; preserves the RRF invariant via `src/lib/research/mine.ts` calling `rescoreByCosine` before gates.
- Wave3: exit 1 standalone because it depends on Wave2 files absent on canon: `src/lib/deepResearch/pipeline.ts`, `src/lib/pdf/deepResearchTemplate.ts`, `src/lib/research/retrieve.ts`; also conflicts in `src/app/researchActions.ts`.
- Wave3-core-only: exit 0 after applying Wave2-core-only. Excludes only `src/app/researchActions.ts`; includes entity layer, F3/F4 reliability/cost/telemetry, self-check core, presentation/PDF additions, and retrieve updates.
- i18n: raw patch exit 1 due catalog ordering/content drift, but semantic payload is already absorbed in canon. All required keys exist in `messages/en.json`, `messages/ru.json`, `messages/uk.json`; `src/lib/factcheck/__tests__/factcheckI18nCatalog.test.ts` exists and passes. UK `factcheck.verdict.not_found` is already localized as `Не знайдено`, better than raw patch's English fallback. Must keep i18n in messages catalogs, not `src/lib/translations.ts`.
Conflict classification:
- Trivial: F1, drawer, helper apply clean. Stage-A is duplicate of honesty and should be skipped if honesty is landed, or replace honesty as the canonical Wave-1 source, but not both.
- Mostly trivial but requires code edit: `FactcheckSettings.tsx` should keep canonical `resolveFactcheckProviderGroups()` grouping; `VerdictChip.tsx` should keep neutral not_found semantics and align key spelling with runtime messages.
- Needs careful rewrite: `FactcheckPanelDrawer.tsx`, `FactcheckModal.tsx`, `conceptLabels.ts` because canon already contains later launcher/drawer flow and copy helpers; replay must preserve the modern store-driven drawer handoff.
- Needs careful rewrite: `src/lib/factcheck/pipeline.ts`; Wave-1 C3 corroboration collides with canon's B1/H3/H4 self-reference, dynamic threshold, and claim-extractor code. Do not blindly choose either side.
- Needs careful rewrite: Wave2 Deep Research route/actions/modal conflicts. The RRF mine applies here: any score-gated use of `hybridSearch` must route through `rescoreByCosine` before thresholding.
- Wave2 core/library can be landed separately with `/Users/milamarty/Downloads/FC-REPLAY-Wave2-core-only.patch`; the remaining route/action/modal wiring is not safe to auto-resolve because canon has newer auth, rate-limit, abort, trusted server-owned payload, and autosave flows that conflict with the old Wave2 direct payload path.
- Needs careful rewrite: i18n conflicts must land in `messages/*.json`; do not add new factcheck runtime strings to `translations.ts`.
Recommended landing order:
1. Land `/Users/milamarty/Downloads/FC-REPLAY-Wave1-resolved.patch`; skip raw honesty and raw Stage-A duplicate.
2. F1.
3. Land `/Users/milamarty/Downloads/FC-REPLAY-Wave2-core-only.patch`; then manually rewrite the three excluded Wave2 wiring files, preserving RRF -> `rescoreByCosine` before score gates and preserving canon security/runtime flows.
4. Land `/Users/milamarty/Downloads/FC-REPLAY-Wave3-core-only.patch` after Wave2-core; then manually rewrite the excluded Wave3 `src/app/researchActions.ts` wiring on top of the Wave2 wiring rewrite.
5. i18n: no-op/verify only unless a later base lacks the messages keys.
6. drawer.
7. helper.
Not done:
- No source code committed.
- No push, no deploy.
- No broad lint/build/test, because this was archaeology/dry-run only and unresolved conflicts intentionally remain as ticket questions.
- Ticket comment POST is blocked from this environment: `/api/ios/issues/IOS-069/comments`, `/comment`, generic `/api/issues/...`, and POST to the issue endpoint all returned 404/unknown route. Full intended comment body is in `/Users/milamarty/Downloads/fc-replay-ios069-comment.txt`.
- Wave2-core focused test attempt: `/Users/milamarty/Downloads/fc-replay-ios069-checks/wave2-core-tests.log`. Apply succeeded; 14 test assertions passed, but the command exits 1 in this temporary worktree because dependencies/generated modules are absent (`@prisma/client`, `pdf-lib`) and one test uses `node:test mock.module` unavailable in this Node runtime.
2026-08-02T00:35:51.170Z · fc-replay# IOS-069 wiring conflict handoff
Scope: remaining non-obvious Wave2/Wave3 wiring after the safe core/library patches.
Artifacts:
- `/Users/milamarty/Downloads/FC-REPLAY-Wave2-wiring-only.patch`
- `/Users/milamarty/Downloads/FC-REPLAY-Wave3-wiring-only.patch`
- `/Users/milamarty/Downloads/fc-replay-ios069-checks/wave2-wiring-conflict-markers.txt`
- `/Users/milamarty/Downloads/fc-replay-ios069-checks/wave3-wiring-conflict-markers.txt`
- `/Users/milamarty/Downloads/fc-replay-ios069-checks/wave2-route-conflict-snippet.txt`
- `/Users/milamarty/Downloads/fc-replay-ios069-checks/wave2-actions-conflict-snippet.txt`
- `/Users/milamarty/Downloads/fc-replay-ios069-checks/wave2-modal-conflict-snippet.txt`
- `/Users/milamarty/Downloads/fc-replay-ios069-checks/wave3-actions-conflict-snippet.txt`
Base chain used for conflict capture:
1. `FC-REPLAY-Wave1-resolved.patch`
2. `FC-REPLAY-F1.patch`
3. `FC-REPLAY-Wave2-core-only.patch`
4. `FC-REPLAY-Wave3-core-only.patch` only for Wave3 wiring capture
## Wave2 wiring-only
Patch: `/Users/milamarty/Downloads/FC-REPLAY-Wave2-wiring-only.patch`
Apply result after Wave1-resolved + F1 + Wave2-core:
- exit 1
- conflicts:
- `src/app/api/deep-research/run/route.ts`: 1 hunk
- `src/app/researchActions.ts`: 12 hunks
- `src/components/modals/DeepResearchModal.tsx`: 1 hunk
### Route invariant
Current canon route is newer than the Wave2 branch and must be preserved:
- authenticated `userId`
- rate limit via `consumeRateLimit`
- bounded JSON parsing via `readBoundedDeepResearchJson`
- server-owned payload via `buildServerOwnedDeepResearchPayload`
- managed Tavily key resolution via `resolveProviderKeyForRequest`
- source ACL via `getAccessibleSourceIds`
- source hydration from Prisma and `hydrateTrustedDeepResearchSources`
- trusted context construction via `buildTrustedDeepResearchContext`
- generated save-target reconciliation
- singleton run guard via `acquireDeepResearchRun`
- abortable stream via `AbortController` + `wrapDeepResearchStream`
Wave2 branch tries to replace that with older `sanitizeDeepResearchPayload(await request.json())` and direct `runDeepResearch({ ...payload, userId })`.
Resolution guidance:
- Do not reintroduce direct client-owned context as trusted.
- Keep the canon route skeleton.
- Add Wave2 fields only through the server-owned payload path.
- If `scope/notebookId/userId` are needed by the new retrieve router, derive/inject them server-side after auth and ACL, not from trusted client input.
### `researchActions.ts` invariant
Wave2 wants classic Deep Research to stop using flat client `context` and use `runResearchPipeline` / `formatGroundedBlock` / `RetrievalScope`.
Canon must keep:
- `researchScope`, `angle`, `signal`, `trustedRuntimeApiKeys`, `saveTarget`, `sourceScope`
- abort handling: `throwIfAborted`, `isAbortError`, abortable Tavily fetch path where still used
- runtime fallback/error behavior
- autosave metadata and generated source persistence
- server-owned source count semantics
Do not blindly delete the existing Tavily/search code unless the classic path is fully converted to `runResearchPipeline` and native engines still type-check. Wave2 has several dependent hunks:
- params type: make `context` optional/deprecated, add retrieval `scope`, `sourceIds`, `notebookId`, `userId`
- replace direct web-search evidence assembly with `runResearchPipeline`
- pass `mode`, `scope`, `filter`, `keys`, `userId`
- emit grounded/cited context via `formatGroundedBlock`
- set metadata source counts from pipeline outputs
RRF mine:
- Any `hybridSearch` result consumed by this wiring must pass through `searchMineVectors` / `rescoreByCosine` before score gates.
- `FC-REPLAY-Wave2-core-only.patch` already adds that path in `src/lib/research/mine.ts`; the wiring should call router/pipeline APIs that use it, not call `vectorStore.hybridSearch` directly.
### Modal invariant
Wave2 modal conflict is small but product-sensitive:
- canon currently builds and sends `context` plus `contextSourceCount`
- Wave2 wants client to stop sending raw context and send source ids/scope for server-side retrieval
Resolution guidance:
- Prefer server-owned retrieval: send `sourceIds`, `sourceScope`, `researchScope`, runtime choice, BYOK/runtime keys, save target, and run id.
- Do not rely on client-built `context` for factual grounding.
- Preserve existing abort controller, stream parser, autosave reconciliation, runtime availability checks, and ready/export UI.
## Wave3 wiring-only
Patch: `/Users/milamarty/Downloads/FC-REPLAY-Wave3-wiring-only.patch`
Apply result after Wave2-core + Wave3-core:
- exit 1
- conflict:
- `src/app/researchActions.ts`: 1 hunk near imports/types
Wave3 wants to add:
- `runReportSelfCheck`
- `annotateReportWithSelfCheck`
- self-check invocation after the classic report body is generated
Resolution guidance:
- Apply after Wave2 `researchActions.ts` wiring is manually resolved.
- Add Wave3 self-check only as fail-closed post-synthesis enrichment:
- self-check failure must not block report generation
- use runtime/retrieval keys already resolved for the run
- do not run self-check before the report body exists
- preserve existing abort/error handling
## Recommended final replay order
1. `FC-REPLAY-Wave1-resolved.patch`
2. `FC-REPLAY-F1.patch`
3. `FC-REPLAY-Wave2-core-only.patch`
4. manual Wave2 wiring replay from `FC-REPLAY-Wave2-wiring-only.patch`
5. `FC-REPLAY-Wave3-core-only.patch`
6. manual Wave3 wiring replay from `FC-REPLAY-Wave3-wiring-only.patch`
7. i18n verify/no-op unless target base lacks `messages/*.json` keys
8. `FC-REPLAY-drawer.patch` verify/no-op on this canon
9. `FC-REPLAY-helper.patch`
Not done:
- No source commit.
- No push/deploy.
- No automatic resolution of route/action/modal wiring because it would require product/security choices beyond obvious patch replay.
2026-08-02T00:38:45.498Z · вахтёрВоркер fc-replay-069 объявил БЛОКЕР или ждёт сборочный слот. Жив, но не продвигается. Последняя строка: gpt-5.5 high · ~/Projects/fc-replay
2026-08-02T00:43:50.511Z · вахтёрВоркер fc-replay-069 объявил БЛОКЕР или ждёт сборочный слот. Жив, но не продвигается. Последняя строка: gpt-5.5 high · ~/Projects/fc-replay
2026-08-02T00:48:53.988Z · вахтёрВоркер fc-replay-069 объявил БЛОКЕР или ждёт сборочный слот. Жив, но не продвигается. Последняя строка: gpt-5.5 high · ~/Projects/fc-replay
2026-08-02T00:53:56.823Z · вахтёрВоркер fc-replay-069 объявил БЛОКЕР или ждёт сборочный слот. Жив, но не продвигается. Последняя строка: gpt-5.5 high · ~/Projects/fc-replay
2026-08-02T00:58:58.418Z · вахтёрВоркер fc-replay-069 объявил БЛОКЕР или ждёт сборочный слот. Жив, но не продвигается. Последняя строка: gpt-5.5 high · ~/Projects/fc-replay
2026-08-02T01:04:00.493Z · вахтёрВоркер fc-replay-069 объявил БЛОКЕР или ждёт сборочный слот. Жив, но не продвигается. Последняя строка: gpt-5.5 high · ~/Projects/fc-replay
2026-08-02T01:09:02.493Z · вахтёрВоркер fc-replay-069 ЖДЁТ УЖЕ 30 мин подряд и не продвигается. Последняя строка: gpt-5.5 high · ~/Projects/fc-replay
2026-08-02T01:14:06.147Z · вахтёрВоркер fc-replay-069 ЖДЁТ УЖЕ 35 мин подряд и не продвигается. Последняя строка: gpt-5.5 high · ~/Projects/fc-replay
Воркер
не проверен
fc-replay-069
M4
движение в панели: неизвестно
Подключиться и смотреть/перехватить руками. Колесо мыши листает; клавишами — Ctrl-b затем [, выход из прокрутки q. Отсоединиться — Ctrl-b затем d:
ssh -t poolpooly@192.168.1.74 "ssh -t milamarty@192.168.1.114 '/opt/homebrew/bin/tmux -L gui attach -t fc-replay-069'"
Прочитать историю панели без подключения — листается и ищется (/ поиск, q выход), воркеру не помешает:
ssh poolpooly@192.168.1.74 "ssh milamarty@192.168.1.114 '/opt/homebrew/bin/tmux -L gui capture-pane -p -S -5000 -t fc-replay-069'" | less -R
Если при входе tmux покажет «.config/tmux/tmux.conf: Permission denied» — нажми q, это косметика: каталог ~/.config на M4 принадлежит root, сессия работает. Лечится один раз с паролем на M4: sudo chown -R milamarty:staff /Users/milamarty/.config && chmod 755 /Users/milamarty/.config
Обновлён
2026-08-02T01:16:00.459Z