# `Gralkor.Generalisation`
[🔗](https://github.com/elimydlarz/jido_gralkor/blob/main/lib/gralkor/generalisation.ex#L1)

Struct and wire format for generalisations stored as graphiti episodes in a
dedicated partition (`"#{group_id}_gen"`).

The struct `:id` doubles as the graphiti episode UUID — `add_episode` accepts
an optional `uuid` parameter, so we control identity. This gives us update
(re-add with same uuid) and delete (`remove_episode(uuid)`) on generalisations.

## Wire format

Episode content uses a single-line metadata prefix followed by the free-text
generalisation:

    GEN|v1|{"id":"abc123","level":2,"confidence":0.85,"generalises":["def456"]}
    Eli prefers concise, structured responses by default.

The prefix is part of the text that graphiti embeds, so generalisations are
searchable by their content while remaining parseable on read.

See `ex-generalisation` in `TEST_TREES.md`.

# `t`

```elixir
@type t() :: %Gralkor.Generalisation{
  confidence: float(),
  content: String.t(),
  created_at: String.t() | nil,
  generalises: [String.t()],
  id: String.t(),
  level: non_neg_integer()
}
```

# `decode`

```elixir
@spec decode(String.t()) :: {:ok, t(), String.t()} | {:error, :not_a_generalisation}
```

Decodes a raw fact string (from a graphiti search result) into a
`%Generalisation{}` struct and the plain content.

Returns `{:ok, %Generalisation{}, plain_content}` on success,
`{:error, :not_a_generalisation}` for strings without the prefix,
or raises `GeneralisationParseFailed` for malformed metadata.

# `encode`

```elixir
@spec encode(t()) :: String.t()
```

Encodes a `%Generalisation{}` struct into an episode body string suitable for
`GraphitiPool.add_episode/5`. The returned string has the metadata prefix as
its first line, followed by the free-text content.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
