# Concepts

<!-- Canonical: https://www.atlaso.ai/docs/concepts/deposits -->

Atlaso is built around **Field 3.0** — every deposit carries a polarity, an evidence grade, and a structured scope, so the retrieval pipeline can group hits, flag conflicts, and tell you whether the data is settled. This page is the mental model.

## Deposits

A deposit is an immutable, typed record of one thing the system observed. Every deposit carries:

- `content` — the plain-English claim.
- `polarity` — `positive` / `negative` / `cautionary` / `open`.
- `evidence_grade` — `anecdotal` / `observed` / `replicated` / `verified`.
- `scope` — six facets pinning the claim to a context.
- `tags`, `artifact_refs` — free-form context.
- `contradicts` — directional supersession edges to earlier deposits.
- Provenance — `author`, `author_role`, `task_id`, `created_at`.

### Immutability is a feature

Deposits cannot be edited in place. The SDK has no `update()` method — calling it raises `AttributeError` pointing at `contradict()`. Updating a fact creates a new deposit that supersedes the old one. The supersession edge is preserved in the `contradictions` table.

```python
m.update("anything")
# AttributeError: deposits are immutable.
# Use m.contradict(new_text, contradicts=[old.id], reason="…") instead.
```

### Writing one

```python
from atlaso import Memory, Scope

m = Memory()
user = m.for_user("alice")

user.add(
    "Threshold of 0.7 over-flags in production",
    polarity="negative",
    evidence_grade="observed",
    scope=Scope(model="gpt-5", env="prod"),
    tags=["threshold", "fp-rate"],
)
```

The **gate** may reject the write — see [Architecture · The gate](./architecture.md#the-gate).

---

## Polarity & evidence

### Polarity

A deposit's *direction*.

- `positive` — "X works" / "X is true".
- `negative` — "X doesn't work" / "X failed".
- `cautionary` — "X works *but*…" / "watch out for…".
- `open` — "we haven't tested yet" / a note, not a claim.

`positive`, `negative`, and `cautionary` are *directional*. When two or more distinct directional polarities appear in the same scope bag, the bag is flagged as in conflict — that includes `negative` + `cautionary` without a positive. `open` never triggers a conflict.

### Evidence grade

How well-supported the claim is.

- `anecdotal` — one person, one observation, no logs.
- `observed` — measured at least once with artifacts.
- `replicated` — multiple independent observations agree.
- `verified` — replicated and recorded with provenance.

### Why both axes

The gate weighs polarity against evidence to decide if a write should pass. A broad positive claim needs `replicated` or better. A narrow negative claim only needs provenance — a known failure is allowed in cheaply because downstream agents need to know.

### Author-role bump

When a deposit's `author_role` normalises to `redteam` (matches `redteam`, `red_team`, `red-team`, `RedTeam` …), `negative` and `cautionary` claims get a +1 evidence-grade bump at the gate. Positive and open deposits are unaffected.

### Pedagogical errors

```python
user.add("hello", polarity="strong")
# InputValidationError: polarity must be one of:
#   "positive"   — claim that X works / is true
#   "negative"   — claim that X doesn't work / failed
#   "cautionary" — claim with caveats
#   "open"       — note or open question
```

---

## Scope

A six-facet dataclass that pins each deposit to a concrete context.

```python
from atlaso import Scope

@dataclass(frozen=True, slots=True)
class Scope:
    model:   str | None = None
    dataset: str | None = None
    env:     str | None = None
    version: str | None = None
    n:       int | None = None
    seed:    int | None = None
    note:    str | None = None     # folded into the FTS index
```

### Why it matters

Scope is the **grouping key** for confidence and conflict. Two deposits with the same six facets land in the same scope bag. The bag is what gets a precision score, a conflict check, and the `is_confident` verdict.

A claim with no scope is broader than one with three facets — which is why broad positives need stronger evidence to pass the gate.

### Filtering recall

```python
hits = user.recall(
    "threshold",
    scope=Scope(env="prod"),       # only deposits scoped to env=prod
)
```

Partial matches work. Facets you don't pass aren't filtered.

### Breadth feeds the gate

The gate counts non-None scope facets. `≥2` is *narrow*; otherwise *broad*. Broad positives need `replicated`; narrow negatives need provenance (`artifact_refs` OR `env` OR `version`).

---

## Confidence & conflict

The single mechanic that makes atlaso different.

### Four flags on every hit

A `SearchResult` carries four dispersion-aware flags on top of the underlying `Deposit`:

- `is_confident: bool` — bag is multi-sample, near-unanimous on a dominant polarity, no directional conflict.
- `has_disagreement: bool` — bag contains two or more distinct directional polarities.
- `agreement_score: float` — fraction of the bag matching the dominant polarity, in `[0, 1]`.
- `conflict_peers: tuple[str, ...]` — deposit IDs of the opposing-polarity hits that triggered the conflict.

### The exact rule for `is_confident`

Inside the engine the bag-level fields are named `bag_precision` / `has_conflict` / `is_single_sample`. They're re-surfaced on the public `SearchResult` as `agreement_score`, `has_disagreement`, and `is_thin_evidence` respectively.

```python
# _engine/aware.py
@property
def is_confident(self) -> bool:
    return (
        self.bag_precision >= 0.99      # public: agreement_score
        and not self.is_single_sample   # public: is_thin_evidence
        and not self.has_conflict       # public: has_disagreement
    )
```

Three conditions must all hold. A single observation, even with high evidence grade, can never be `is_confident`. A fifty-fifty split, even with many observations, can never be `is_confident`. A unanimous bag of two — yes. The rule is intentionally strict.

### Structural, not semantic

Conflict is *structural*. Atlaso doesn't embed your text to decide if it disagrees. It groups by the structured scope and counts polarities.

### Aggregate flags are computed before slicing

`SearchResults.has_disagreement` looks at *every* bag the engine returns, not just the first `limit` results. A `limit=5` slice can't hide a conflict bag from your model. Intentional.

### `.explain()` in plain English

```python
print(results.explain())
# "5 hits across 2 bags · 1 bag in conflict · not confident"
```

---

## Idempotency

Bulk imports and retried writes need replay safety. Atlaso uses Stripe-style content-addressed keys with a 24-hour window.

### Why keys are required

`add()` is convenient but offers no replay safety. `add_many()` requires a per-item `idempotency_key` so retrying a partially-failed batch is safe.

### The helper

```python
from atlaso import idempotency_key

key = idempotency_key("alice", "Alice prefers oat milk", "2026-05-11")
# 'ak_b7c1d3e8a4f5...'
```

Pure blake2b-16 hash of the arguments, prefixed `ak_`. Same args → same key.

### Bulk imports

```python
from atlaso import AddItem, idempotency_key

items = [
    AddItem(
        content=row["text"],
        idempotency_key=idempotency_key("preferences-v1", row["id"]),
    )
    for row in rows
]
result = user.add_many(items)
print(result.committed, "new")
print(result.duplicates, "replays")
print(result.failed, "rejected")
```

### The rules

- **Same key + same content within 24h** → atlaso treats it as a replay. In `add_many()` the replayed deposit lands in `AddManyResult.duplicates` rather than `committed`.
- **Same key + different content** → `IdempotencyKeyConflict` with `.existing_id`.
- **After 24h** → the key is forgotten.

Keys are stored in `<base>/_idempotency.db`, separate from your deposits.

---

<!-- atlaso:doc-trailer -->
**Source:** <https://www.atlaso.ai/docs/concepts/deposits>  
**Edit on GitHub:** <https://github.com/imashishkh21/atlaso/tree/main/docs/concepts.md>  
**Updated:** 2026-05-12
