# Source and freshness

> Link a record to a document with source_id and source_hash, and find out when the document changed and the fact went stale.

By the end of this page you will be able to:

1. save facts from a document so their origin is visible;
2. check whether the document changed, with `validate_memory` and `search`;
3. update stale facts without losing anything along the way.

Running example: the repository has `docs/deploy.md`. Today it says staging is deployed through the `app-staging` systemd unit. A week later the team moves to Docker Compose and edits the document.

## Three provenance fields

| Field | Example | Meaning |
| --- | --- | --- |
| `source_id` | `docs/deploy.md` | Which document, file or page. Any stable string. |
| `source_hash` | `sha256:9f2c…` | Hash of the source content when it was read. Only together with `source_id`. |
| `source_revision` | `a1b2c3d` | Source version: a commit, revision number or date. For people and audit. |

**This is provenance, not proof.** The server does not read your document or check that the hash is genuine. So a record with a hash, like any new record, gets the `unverified` status. What the hash gives you is the ability to ask later: "is the document still the same?"

The server rejects `source_hash` without `source_id`: `source_id is required when source_hash is provided`. A hash without saying what it hashes is useless.

## Getting a hash

Any method works as long as it is **the same every time**: the server simply compares strings.

```bash
# hash of the file content
sha256sum docs/deploy.md

# or the file's Git hash, with the commit as the revision
git rev-parse HEAD:docs/deploy.md
git rev-parse --short HEAD
```

## Example: the document changed

1. **The agent reads the document and saves facts.** Today the file hash is `sha256:aaa…`.

   ```json title="remember"
   {
     "content": "staging is deployed through the app-staging systemd unit",
     "workspace": "docs",
     "source": "agent",
     "source_id": "docs/deploy.md",
     "source_hash": "sha256:aaa…",
     "source_revision": "a1b2c3d",
     "fact_key": "deploy.staging.method",
     "confidence": 0.9
   }
   ```

   A second fact from the same file — "make migrate runs before every staging deploy" — is saved with the same `source_id` and hash.

2. **The document is edited.** It now describes Docker Compose, and the hash is `sha256:bbb…`.

3. **The agent checks freshness** — for example at the start of a session or after `git pull`:

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

   Response:

   ```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
   }
   ```

   Both records were saved with a different hash, so they may be out of date.

4. **The agent removes the stale facts and saves new ones.** Delete every record from the source in one call:

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

   The response includes `forgotten_count: 2`. Then the agent re-reads the document and saves the current facts with `source_hash: "sha256:bbb…"`. If only one fact changed, you can delete that record by `memory_id` and re-save the others with the new hash.

Order matters: `forget` by `source_id` first, then the new `remember` calls. Otherwise `forget` by source also deletes the fresh records you just saved.

## stale is computed, not stored

The hash comparison happens **at query time**. If you call `search` without `current_source_hash`, the old records look `unverified` again. So when the agent sees `stale`, it has to act — update or delete the record — rather than just "remember that it is outdated".

The exception is records marked by the [archivist](/memory/archivist/): when a new record contradicts an older one, it stores `stale` on the older record and lowers its confidence. Those records stay `stale` in every search.

## Freshness in search

`search` also accepts `current_source_hash` and `exclude_stale`:

```json title="search"
{
  "query": "staging deploy",
  "workspace": "docs",
  "current_source_hash": "sha256:bbb…",
  "exclude_stale": true
}
```

- `current_source_hash` — records stored with a **different** hash get the `stale` status;
- `exclude_stale: true` — those records are left out of the response.

In `search`, the hash is compared with **every** matching record that has a `source_hash` — there is no `source_id` filter there. If a workspace holds facts from several documents, check freshness with `validate_memory` and a specific `source_id`, and use `current_source_hash` in `search` only when the workspace is dedicated to one source.

## When to check

- at the start of a session, for the project's key documents (README, deployment rules, API contract);
- after `git pull` or a merge that touched documentation;
- before acting on a fact with high stakes (a deploy, a migration, deleting data).

## Where next
