# Источник и свежесть

> Как привязать запись к документу через source_id и source_hash и как узнать, что документ изменился и факт устарел.

К концу этой страницы вы сможете:

1. сохранять факты из документа так, чтобы было видно, откуда они взялись;
2. проверять, не изменился ли документ, через `validate_memory` и `search`;
3. обновлять устаревшие факты, ничего не теряя по пути.

Пример на всю страницу: в репозитории есть `docs/deploy.md`. Сегодня в нём написано, что staging выкатывается через systemd-юнит `app-staging`. Через неделю команда переходит на Docker Compose и правит документ.

## Три поля происхождения

| Поле | Пример | Что значит |
| --- | --- | --- |
| `source_id` | `docs/deploy.md` | Какой документ, файл или страница. Любая устойчивая строка. |
| `source_hash` | `sha256:9f2c…` | Хеш содержимого источника в момент чтения. Только вместе с `source_id`. |
| `source_revision` | `a1b2c3d` | Версия источника: коммит, номер редакции, дата. Для человека и аудита. |

**Это сведения о происхождении, а не доказательство.** Сервер не читает ваш документ и не проверяет, что хеш настоящий. Поэтому запись с хешем, как и любая новая, получает статус `unverified`. Зато хеш позволяет потом спросить: «документ всё ещё тот же?»

`source_hash` без `source_id` сервер отклоняет: `source_id is required when source_hash is provided`. Хеш без указания, чего он хеш, бесполезен.

## Как получить хеш

Подойдёт любой способ, если он **каждый раз одинаковый**: сервер просто сравнивает строки.

```bash
# хеш содержимого файла
sha256sum docs/deploy.md

# или хеш файла в Git и коммит как ревизия
git rev-parse HEAD:docs/deploy.md
git rev-parse --short HEAD
```

## Пример: документ изменился

1. **Агент читает документ и сохраняет факты.** Хеш файла сегодня — `sha256:aaa…`.

   ```json title="remember"
   {
     "content": "staging выкатывается через systemd-юнит app-staging",
     "workspace": "docs",
     "source": "agent",
     "source_id": "docs/deploy.md",
     "source_hash": "sha256:aaa…",
     "source_revision": "a1b2c3d",
     "fact_key": "deploy.staging.method",
     "confidence": 0.9
   }
   ```

   Второй факт из того же файла — «перед выкаткой staging запускается make migrate» — сохраняется с тем же `source_id` и хешем.

2. **Документ поменяли.** Теперь в нём Docker Compose, и хеш стал `sha256:bbb…`.

3. **Агент проверяет свежесть** — например, в начале сессии или после `git pull`:

   ```json title="validate_memory"
   {
     "workspace": "docs",
     "source_id": "docs/deploy.md",
     "current_source_hash": "sha256:bbb…"
   }
   ```

   Ответ:

   ```json
   {
     "workspace": "docs",
     "source_id": "docs/deploy.md",
     "current_source_hash": "sha256:bbb…",
     "checked": 2,
     "stale_ids": ["4d428a41-…", "7c1f09e2-…"],
     "conflict_ids": [],
     "stale_count": 2,
     "conflict_count": 0
   }
   ```

   Обе записи сохранены с другим хешем — значит, они могли устареть.

4. **Агент убирает устаревшее и сохраняет новое.** Удалить все записи источника одним вызовом:

   ```json title="forget"
   { "workspace": "docs", "source_id": "docs/deploy.md" }
   ```

   Ответ содержит `forgotten_count: 2`. Затем агент перечитывает документ и сохраняет актуальные факты с `source_hash: "sha256:bbb…"`. Если изменился только один факт, можно удалить конкретную запись по `memory_id`, а остальные пересохранить с новым хешем.

Порядок важен: сначала `forget` по `source_id`, потом новые `remember`. Иначе `forget` по источнику удалит и только что сохранённые свежие записи.

## stale не хранится — он вычисляется

Сравнение с хешем происходит **в момент запроса**. Если вызвать `search` без `current_source_hash`, старые записи снова будут выглядеть как `unverified`. Поэтому, увидев `stale`, агент должен что-то сделать — обновить или удалить запись, а не просто «запомнить, что она устарела».

Исключение — записи, которые отметил [архивариус](/memory/archivist/): если новая запись опровергает старую, он сохраняет у старой статус `stale` и понижает её уверенность. Такие записи остаются `stale` в любом поиске.

## Свежесть в search

`search` тоже принимает `current_source_hash` и `exclude_stale`:

```json title="search"
{
  "query": "staging выкатка",
  "workspace": "docs",
  "current_source_hash": "sha256:bbb…",
  "exclude_stale": true
}
```

- `current_source_hash` — записи, у которых сохранён **другой** хеш, получат статус `stale`;
- `exclude_stale: true` — такие записи не попадут в ответ.

В `search` хеш сравнивается со **всеми** найденными записями, у которых есть `source_hash`, — фильтра по `source_id` там нет. Если в области лежат факты из разных документов, проверяйте свежесть через `validate_memory` с конкретным `source_id`, а `current_source_hash` в `search` используйте, когда область отведена под один источник.

## Когда проверять

- в начале сессии — для ключевых документов проекта (README, правила деплоя, API-контракт);
- после `git pull` или слияния веток, если менялась документация;
- перед тем как действовать по факту с высоким риском (выкатка, миграция, удаление данных).

## Что дальше
