# Эпизодическая память (_10_Episodic_memory)

## Назначение

Модуль реализует **эпизодическую память (ЭП)** — массив кадров в строгой исторической последовательности. Каждый кадр описывает правило: стимул (образ FinalImage) → ответное действие (образ действия, ОД) → эффект (разность getDiffImportance до и после) → ответный стимул оператора (при наличии в период ожидания). Кадры создаются только через ориентировочный рефлекс при выполнении условий по семантическому опыту и периоду ожидания.

## Основные понятия

- **Кадр эпизода:** perceptionNodeId, stimulusImageId, actionImageId, effect, responseStimulusImageId. Ответное действие + ответный стимул образуют «учительское» правило.
- **Период ожидания ответа** — response_waiting_period пульсов (по умолчанию 15). Закрытие по таймауту выполняется в EpisodicMemory_onPulse (вызов из puls.js), без setTimeout; при зависании вкладки логика не расходится с пульсом.
- **Режим тестирования** (EpisodicMemory_testMode) — пошаговые сообщения о проверке ОР, начале формирования кадра, ответном стимуле и отказе; на наличие прогресс-бара не влияет.

## Особенности поведения

- **Создание кадра и прогресс-бар:** при каждом **полном** срабатывании ОР (после прохождения порога `OR_well_known`, либо на втором проходе после ответа в ожидании, см. правила) вызывается `save_episodic_memory` — кадр создаётся и прогресс-бар периода ожидания запускается, не только в тестовом режиме.
- **Прогресс-бар виден только при активном ожидании в этой сессии:** контейнер прогресс-бара показывается только если `EpisodicMemory_waitingStartedAt != null` или `EpisodicMemory_barShowCompletePulsesLeft > 0`. После обновления страницы эти переменные не восстанавливаются из IndexedDB, поэтому прогресс-бар не отображается до первой активации стимула, запустившей ОР.
- **Период ожидания только в текущей сессии:** `isInWaitingPeriod()` возвращает true только при `EpisodicMemory_waitingStartedAt != null`. Незакрытый кадр, загруженный из IndexedDB (effect === null), не считается «активным ожиданием» — первый клик после обновления создаёт новый кадр с прогресс-баром от 0%, а не закрывает старый с показом 100%.
- **Деактивация стимулов:** при снятии всех стимулов (пустой список активных) ОР выходит без сообщения. Уже созданный кадр ЭП и период ожидания не отменяются; стимул уже записан в кадре.
- **Ответный стимул оператора:** при активации в периоде ожидания ОР закрывает кадр ответом (`closeLastFrame` / `EpisodicMemory_closeLastFrameWithResponse`), открывает следующий кадр и выполняет **повторный полный** ОР: новый цикл ФО и ещё один вызов `save_episodic_memory`. Порог `OR_well_known` для шага «ответ» не проверяется.

## Преемственность оценки эффекта (ЭП ↔ автоматизмы)

**Единая оценка эффекта:** один и тот же эффект вычисляется в ЭП и передаётся в автоматизм.

- В **closeLastFrame** эффект считается один раз: `effect = getDiffImportance() (после) − diffBefore` и записывается в кадр ЭП (`last.effect`).
- Тот же `last.effect` передаётся в штатный автоматизм ветки вызовом **Automatizms_addUsefulnessSample(staff.id, last.effect)**.
- Оценка не дублируется и не расходится: одно значение эффекта — одна запись в кадре ЭП и одно обновление Usefulness автоматизма (с усреднением по count). Алгоритм обеспечивает преемственность оценки эффекта между ЭП и автоматизмами.

- **Гештальт (ур.4):** после записи `effect` в кадр, до сброса глобалов ожидания по автоматизму, вызывается **`Gestalt_onEpisodeFeedbackClosed`** (`gestalt.js`): в **буфер резонанса** добавляется сигнатура закрытия; при наличии **`goalImageId`** у цикла ожидания обновляется соответствующая запись гештальта (статусы, инсайты). Не дублирует оценку эффекта — использует уже вычисленный `effect`. Подробнее — **`beast/_9_Conscience/about_gestalt.md`**.

## Эффект закрытого кадра и неполное правило (нули)

- После `closeLastFrame` поле `effect` кадра **всегда число** в диапазоне **[-10, +10]** (`clampEpisodeFrameEffect` в `episodic_memory.js`): либо по разности `getDiffImportance()` и сохранённого `diffBefore`, либо **0**, если база для разности недоступна (нейтральная оценка).
- Если к моменту закрытия в кадре ещё нет **образа действия (ОД)** и/или **ответного стимула**, перед закрытием вызывается `episodicFrameFillRuleZerosForIncompleteClose`: в соответствующие поля записывается **0** (в открытом кадре до этого хранится `null`). Так кадр остаётся в истории `Episodes`, а не удаляется.
- **Истечение ожидания без реакции** (`EpisodicMemory_onPulse`): кадр **не** снимается с массива — выставляются нули и `closeLastFrame(null)` (как и при закрытии с реакцией, но без ответа оператора).
- При **новом стимуле ОР** предыдущий открытый кадр закрывается с `episodicFrameFillRuleZerosForIncompleteClose(prev)` и `closeLastFrame(stimulusImageId)` нового победителя — ответ в «старом» кадре подставляется из нового стимула.
- В **компактной сериализации** в файл/IndexedDB явная **«0»** в полях ОД и ответного стимула при разборе сохраняется как число **0** (`parseIntOrNull`), чтобы не смешивать с «пусто» (`null`).

## Персистенция клонов (IndexedDB + резерв)

Состояние `abstract_images.js` хранится в IndexedDB (`BEAST_AbstractImages_DB`, открытие через `openDatabase`, одно закэшированное соединение — как у образов действия). Запись: по пульсу, **сразу после** `closeLastFrame` (`AbstractImages_flushIndexedDB`) и при `beforeunload`, если ещё `absDirty`. При ошибке `put` дублируется снимок в **localStorage** (`BEAST_AbstractImages_LS`); при пустой записи в IndexedDB загрузка страницы подхватывает этот резерв. Полный снимок в файле — ключ **`AbstractImagesState`**.

## Индикатор «абстр» в инфокартине

Счётчик показывает **число клонов** из `AbstractPerceptionImages` + `AbstractActionImages` (появляются при **закрытии** кадра ЭП). Доп. суффикс `+N` — элементы массива `Abstractions` (`mental_automatism.js`). Щелчок по индикатору открывает сводку по всем трём наборам.

## Связь с абстрактными образами (`abstract_images.js`)

- При **каждом** вызове `closeLastFrame` вызывается `AbstractImages_cloneFromEpisodeFrame(last)`: создаётся или усиливается (усреднение `meanImportance`, `count`) абстракция **восприятия** по паре `(perceptionNodeId, stimulusImageId)`.
- **Абстракция действия** обновляется только если `actionImageId` ненулевой; значение **0** после «нуляции» означает отсутствие ОД — клон действия не строится.
- **`semanticStructure`** у каждой абстракции — объект `{ frames: [...] }`: для каждой пары условий **(узел ветки восприятия, `situationId`)** хранится снимок лучшего эпизодического правила (`ruleKind: episodic`, выбор по большему `effect`) либо учительского (`ruleKind: teacher`, есть `responseStimulusImageId` на ОД; вес «доверия» не через `effect`). Обновление — в `AbstractImages_cloneFromEpisodeFrame` через `AbstractImages_upsertSemanticRuleFromEpisodeFrame`; при учительском fallback ур.3 — ещё `AbstractImages_activateTeacherRuleOnAbstractions` (клоны по актуальному FinalImage и по ОД из правила). **Ур.3:** `AbstractImages_findRuleFrameMatchingContext` для shortcut / ментального автоматизма; мотор по этому кадру только если значимость кадра «положительна» в смысле `conscience_level_3_semanticStructureFrameAllowsMotor` (teacher или episodic с `effect > 0`). Иначе выполняется обход семантики+ЭП и учительский путь. Пока цикл ждёт кадр ЭП после автоматизма, повторный полный ур.3 не гоняется (см. `about_conscience.md`).
- **Ответ оператора после автоматизма (`conscience_level_2_operatorAnswerPipeline`):** при числовом `effect` и совпадающем кадре `semanticStructure` (ветка, ситуация, стимул) **`AbstractImages_applyEpisodeEffectToSemanticContextAbstractions`** усредняет `meanImportance` клонов восприятия и действия (положительный эффект усиливает, отрицательный ослабляет). При **положительном** `effect` дополнительно **`MentalAutomatizms_tryCreateFromSemanticStructureRule`** — ментальный автоматизм; оба шага в т.ч. при **`goalImageId === 0`** (ур.3).
- `MentalRules_registerFromEpisodeFrame` (`mental_automatism.js`): использует `abstractActionId || abstractPerceptionId`, поэтому при отсутствии ОД ментальное правило всё равно может усилиться по абстракции восприятия.

## Приоритет стимулов «цепочки» ЭП (ОР и ФО)

Подряд идущие **с конца** массива `Episodes` кадры, у **каждого** из которых зафиксирована **реакция** (`actionImageId`), образуют **связную цепочку**. Если в суффиксе не меньше двух таких кадров (параметр `EpisodicMemory_chainPriorityMinFrames`), все их `stimulusImageId` и `responseStimulusImageId` получают **приоритет**:

- в **ориентировочном рефлексе** для них **не** вычитается гистерезис конкуренции, актуальность **усиливается** (`findMostActualStimulus` в `orientation_reflex.js`);
- при запуске **главного цикла ФО** (`stimuls_consciousness`) на них **не** распространяются понижающие множители (несовпадение темы/ситуации, ImportantProblem) и порог прерывания текущего мышления берётся как у «равной значимости» (ratio 1), чтобы цепочка не обрывалась чужими стимулами без реакции/ответа. Для остальных стимулов та же «тишина мыслей» действует **только пока главный цикл не `expired`**; победитель ОР помечается в контексте и не отсекается этим порогом (подробности — в **about_episodic_memory_rules.md** и **about_conscience.md**).

Подробнее — **about_episodic_memory_rules.md** (раздел про цепочки и прерывание).

## Связь с другими модулями

- Создание кадра запускается из **ОР** (_8_Hippocampus) при вызове save_episodic_memory.
- Заполнение actionImageId — из **genetic_reflexes_engine** (EpisodicMemory_setLastFrameActionFromActionId) при выполнении реакции.
- Отображение последних кадров, прогресс-бар и кнопки — в **_7_perception_tree** (updateActiveBranchView); видимость прогресс-бара по условию активного ожидания в этой сессии.
- **Результат ЭП — автоматизмы:** (1) При срабатывании рефлекса (в т.ч. задержанного) в setLastFrameActionFromActionId сразу создаётся автоматизм по образу действия, привязанный к ветке, с нулевым Usefulness; если к ветке ещё не привязан автоматизм — он назначается штатным. (2) При закрытии кадра (ответный стимул в период ожидания или таймаут) выявленный эффект записывается в штатный автоматизм ветки через addUsefulnessSample (усреднение Usefulness); см. выше **преемственность оценки эффекта**. Если сработал штатный автоматизм и затем пришёл ответный стимул — тот же штатный автоматизм дополняется новым значением значимости (усреднение).

## Подробные правила

Полное описание правил и условий формирования кадров, работы тестового режима и связи с ориентировочным рефлексом и условными рефлексами см. в **about_episodic_memory_rules.md** (там же **§9** — таблица: эффект, нули в полях правила, абстракции).

## Файлы

- **episodic_memory.js** — массив Episodes, save_episodic_memory, closeLastFrame, isInWaitingPeriod, setLastFrameActionFromActionId, персистенция (IndexedDB, компактный формат в файл), кнопки сброса и тестового режима.
