Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
9400ec0
Add docs/contracts.md: the storage and memory contracts in public form
rohan-hotdata Oct 5, 2026
ec06eae
Add docs/guarantees.md: the guarantees ledger and M1 to M6
rohan-hotdata Oct 5, 2026
0f22f3f
Point the README at the public docs and add CONTRIBUTING.md
rohan-hotdata Oct 5, 2026
beb46f6
Add make verify with ruff and a relative link check
rohan-hotdata Oct 5, 2026
a7df5a2
Add docs/local.md and make local-up for a local RuntimeDB
rohan-hotdata Oct 5, 2026
f7c6667
Add scripts/measure_cloud.py for M1, M2, and M3
rohan-hotdata Oct 5, 2026
6f92592
Add scripts/measure_local.py for M6, M4, and M5
rohan-hotdata Oct 5, 2026
b1fca5c
Record that the bare RuntimeDB image refuses managed databases
rohan-hotdata Oct 5, 2026
16ea58a
Add AGENTS.md and a CLAUDE.md that imports it
rohan-hotdata Oct 5, 2026
a821ec6
Update plan.md to the nine tasks and five criteria of issue #1
rohan-hotdata Oct 5, 2026
2f29c5c
Add .env.template and run the measurement scripts with --env-file
rohan-hotdata Oct 5, 2026
a3e621c
Record M1, M2, and M3 from the cloud run of 2026-10-05
rohan-hotdata Oct 5, 2026
20ec374
Let measure_local.py run M4 to M6 against the cloud with --cloud
rohan-hotdata Oct 5, 2026
a6172ca
Record the cloud results of M4, M5, and M6
rohan-hotdata Oct 5, 2026
1d6bf71
Run the local RuntimeDB as Postgres, RustFS, and the engine
rohan-hotdata Oct 5, 2026
d3db750
Add CODEOWNERS so pull requests request a reviewer
rohan-hotdata Oct 5, 2026
5749e95
Record the local results of M4, M5, and M6
rohan-hotdata Oct 5, 2026
d350708
Use plain wording in the M5 local result
rohan-hotdata Oct 5, 2026
6739b20
Apply the phase 0 measurements to the brief and the contracts
rohan-hotdata Oct 5, 2026
c47a7a1
Split a long sentence in the contracts
rohan-hotdata Oct 5, 2026
745fbb8
Drop the placeholder wording now that M1 to M6 have results
rohan-hotdata Oct 5, 2026
afbdc8c
Remove the catalog volume on local-down and stop M3 from hanging
rohan-hotdata Oct 5, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions .env.template
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Copy to .env and fill in. .env is ignored by git. Run the scripts with:
# uv run --env-file .env scripts/measure_cloud.py
# uv run --env-file .env scripts/measure_local.py

# measure_cloud.py (M1, M2, M3)
HOTDATA_API_KEY=
# Optional. Without it, the framework picks the active workspace of the key.
HOTDATA_WORKSPACE=
HOTDATA_API_URL=https://api.hotdata.dev
# The throwaway database. The script refuses a name that already exists and deletes
# the database on exit. Use a new name for each run.
HOTMEMORY_MEASURE_DB=hotmemory-measure-1
HOTMEMORY_EMBEDDING_PROVIDER=sys_emb_openai

# measure_local.py (M4, M5, M6). It ignores the HOTDATA_* values above.
HOTMEMORY_LOCAL_URL=http://localhost:3000
HOTMEMORY_M5_SIZES=1000,10000,100000
1 change: 1 addition & 0 deletions .github/CODEOWNERS
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
* @hotdata-dev/engineers
39 changes: 39 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# Agent instructions

This file is for coding agents that work in this repository. It gives the commands and the
rules, and points to the files that hold everything else.

## Commands

```sh
make verify # the full check; run it before each commit
make local-up # start the local RuntimeDB stack (Postgres, RustFS, engine)
make local-down # stop it and delete its data
cp .env.template .env # then fill in .env; it is ignored by git
uv run --env-file .env scripts/measure_cloud.py # M1 to M3
uv run --env-file .env scripts/measure_local.py # M4 to M6; needs make local-up
uv run --env-file .env scripts/measure_local.py --cloud # M4 to M6 in the cloud
```

## Where things are

- [docs/internal/plan.md](docs/internal/plan.md): the current phase, its tasks, and its stop
conditions.
- [docs/internal/roadmap.md](docs/internal/roadmap.md): every phase and its status.
- [docs/contracts.md](docs/contracts.md): the record and the operations.
- [docs/guarantees.md](docs/guarantees.md): what a consumer can rely on, and the proof.
- [docs/internal/brief.md](docs/internal/brief.md): the design and its reasons.
- [CONTRIBUTING.md](CONTRIBUTING.md): the checks inside `make verify` and the test rules.

## Rules

- Work the current GitHub issue in task order, on one branch from `main`.
- Do not push, open a pull request, or post a comment until the repository owner agrees.
- Never name a private repository, a customer, or a deployment detail in a committed file.
Before each commit, search the diff for the names that you know.
- Never write a measured number that you did not observe. Mark a claim that you read from
source, and did not observe, as not observed.
- The measurement scripts read credentials from the environment. Do not put a key, a
workspace id, or a database id in a file.
- If a change alters a behavior, update `docs/contracts.md` and `docs/guarantees.md` in the
same commit.
1 change: 1 addition & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
@AGENTS.md
74 changes: 74 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
# Contributing

## Prerequisites

- [uv](https://docs.astral.sh/uv/) installs the development tools and runs the scripts.
- GNU Make runs the targets below.
- Docker Desktop runs the local RuntimeDB stack. You need it only for `make local-up`.

## The one command

Run this command before each commit:

```sh
make verify
```

`make verify` is the full check. CI runs the same target. If it passes on your machine, it
passes in CI. It has no tiers, because the full check takes less than five seconds.

Today, `make verify` runs these checks, in this order:

1. `ruff check` over the Python files.
2. `ruff format --check` over the Python files and the Python code blocks in Markdown.
3. The link check. It reads each relative link in the Markdown files at the root and
under `docs/`. A link to a file that does not exist makes it fail.

When the library exists, strict `mypy` and the offline test suite join `make verify`
before the link check.

## The other targets

| Target | What it does |
|---|---|
| `make verify` | Runs every check above. |
| `make local-up` | Starts the local RuntimeDB stack: Postgres, RustFS, and the engine. [docs/local.md](docs/local.md) tells you how to point the library at it. |
| `make local-down` | Stops the stack and deletes its data. |
| `make local-pull` | Downloads newer images for the stack. |

## Rules of the harness

The library is deterministic. The offline suite needs no clock, no network, and no model.
These rules keep it that way.

- One conformance suite runs against every driver. Each answered guarantee in
[docs/guarantees.md](docs/guarantees.md) is one test. A driver that fails a conformance
test is not a driver.
- The in-memory driver is the reference for the Hotdata driver. A test builds the same
records in both drivers and compares the results of `search`.
- Four surfaces are frozen: the names in `__all__`, the method set of the `Store`
protocol, the fields and field types of the record for each schema version, and the
filter keys of `list` and `search`. A test compares each surface against a literal set.
A change to a frozen surface is a public contract change, and it needs a changelog
entry.
- A frozen surface is compared against a literal set, never against the thing that it
protects. A test that iterates over the protected thing turns a deletion into one test
fewer and not into a failure.
- Each row in the guarantees ledger names the test that proves it. A test reads the ledger.
A named test that does not exist makes it fail.
- Tests marked `hotdata` run the Hotdata driver against a real database. They need
`HOTMEMORY_TEST_DB` to name a throwaway database. Without it, they skip. They
run once for each pull request. They are the only tests that use the network.
- No test calls a model. `capture` takes a callable, and the tests pass a fake extractor
that returns fixed facts.
- There is no coverage gate, no mutation-testing gate, and no report generator. The output
of `make verify` is the report.

## Documents

Documents are checked like code. If you change a behavior, update
[docs/contracts.md](docs/contracts.md) and [docs/guarantees.md](docs/guarantees.md) in the
same pull request.

No file in this repository names a private repository, a customer, or a deployment detail.
This rule includes `docs/internal/`.
35 changes: 35 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
export RUNTIMEDB_IMAGE ?= ghcr.io/hotdata-dev/runtimedb:latest
export LOCAL_PORT ?= 3000

STORAGE_URL := http://127.0.0.1:9000

.PHONY: verify local-up local-down local-pull

verify:
uv run --group dev ruff check .
uv run --group dev ruff format --check .
uv run --no-project python scripts/check_links.py

local-up:
docker compose up -d --wait catalog storage
@for i in $$(seq 1 30); do \
curl -s -o /dev/null $(STORAGE_URL)/ && break; \
sleep 1; \
done
curl -fsS -o /dev/null -X PUT --aws-sigv4 "aws:amz:us-east-1:s3" \
--user hotmemory:hotmemory-local $(STORAGE_URL)/runtimedb
RUNTIMEDB_SECRET_KEY="$$(openssl rand -base64 32)" docker compose up -d runtimedb
@for i in $$(seq 1 60); do \
curl -fs -o /dev/null http://localhost:$(LOCAL_PORT)/health && \
echo "RuntimeDB is up at http://localhost:$(LOCAL_PORT)" && exit 0; \
sleep 1; \
done; \
echo "RuntimeDB did not answer /health in 60 seconds"; \
docker compose logs --tail 50 runtimedb; \
exit 1

local-down:
docker compose down -v

local-pull:
docker compose pull
35 changes: 25 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,37 @@

Agent memory as tables on [Hotdata](https://hotdata.dev).

A memory record is a row in a managed table. The row carries the text, an embedding, a
scope, tags, source references, and the time span the fact was true. Because the table is
an ordinary Hotdata table, an agent can join its memory to its own data in one SQL query.
No other memory store offers that join.
A memory record is a row in a managed table. The row carries the text, a scope, tags,
source references, and the time span in which the fact was true. The table is an ordinary
Hotdata table, so an agent can join its memory to its own data in one SQL query. None of
the memory systems that we surveyed stores memory as typed columns in the same engine as
the data of the consumer.

Status: design. The contracts are drafted in [docs/internal/brief.md](docs/internal/brief.md). No code
exists yet.
Status: design. No code exists yet. The contracts and the guarantees are written, and
phase 0 measured each guarantee that was open.

The library has two layers:

- A storage contract. Put, get, list, search, and delete records in a namespace. Records
are immutable. A new put under the same key creates a new revision.
- A memory contract. Remember facts, recall them inside a context budget, supersede a
subject, and forget by id or by horizon. Extraction from raw text is optional and takes
fact, and forget by id or by horizon. Extraction from raw text is optional, and it takes
a model callable that the caller supplies.

The first consumer is an incident investigator built on Hotdata. The library is not
specific to it. A plain Python agent, a LangGraph agent, or any process with a Hotdata API
key can use it.
The library runs in the process of the consumer and calls the Hotdata API with the API key
of the consumer. There is no hotmemory server. The first consumer is an incident
investigator built on Hotdata. The library is not specific to it. A plain Python agent, a
LangGraph agent, or any process with a Hotdata API key can use it.

## Documents

- [docs/contracts.md](docs/contracts.md): the record, the storage operations, the memory
operations, and the platform facts behind them.
- [docs/guarantees.md](docs/guarantees.md): each behavior that a consumer can rely on, its
state, and its proof.
- [docs/local.md](docs/local.md): how to run the library against a local RuntimeDB
container.
- [CONTRIBUTING.md](CONTRIBUTING.md): the one command that checks a change, and the rules
of the test suite.
- [docs/internal/](docs/internal/brief.md): the design brief, the survey, the roadmap, and
the plan for the current phase.
57 changes: 57 additions & 0 deletions compose.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# Local RuntimeDB for development and measurements. Started by `make local-up`.
#
# runtimedb shares the network namespace of storage, so 127.0.0.1:9000 is RustFS both
# inside runtimedb and on the host. Presigned upload URLs carry that address, so a
# client on the host can upload.
#
# No named volume. `make local-down` removes every container and its anonymous volumes, so
# the data is gone.

name: hotmemory

services:
catalog:
image: ${POSTGRES_IMAGE:-postgres:17}
environment:
POSTGRES_USER: runtimedb
POSTGRES_PASSWORD: runtimedb
POSTGRES_DB: runtimedb
healthcheck:
test: ["CMD", "pg_isready", "-U", "runtimedb", "-d", "runtimedb"]
interval: 1s
timeout: 3s
retries: 30

storage:
image: ${RUSTFS_IMAGE:-rustfs/rustfs:latest}
environment:
RUSTFS_ACCESS_KEY: hotmemory
RUSTFS_SECRET_KEY: hotmemory-local
ports:
- "127.0.0.1:9000:9000"
- "127.0.0.1:${LOCAL_PORT:-3000}:3000"

runtimedb:
image: ${RUNTIMEDB_IMAGE:-ghcr.io/hotdata-dev/runtimedb:latest}
network_mode: service:storage
depends_on:
catalog:
condition: service_healthy
storage:
condition: service_started
environment:
RUNTIMEDB_SECRET_KEY: ${RUNTIMEDB_SECRET_KEY:-}
RUNTIMEDB_AUTH__ALLOW_UNAUTHENTICATED: "true"
RUNTIMEDB_ENGINE__SQL_WRITES: "true"
RUNTIMEDB_CATALOG__TYPE: postgres
RUNTIMEDB_CATALOG__HOST: catalog
RUNTIMEDB_CATALOG__PORT: "5432"
RUNTIMEDB_CATALOG__DATABASE: runtimedb
RUNTIMEDB_CATALOG__USER: runtimedb
RUNTIMEDB_CATALOG__PASSWORD: runtimedb
RUNTIMEDB_STORAGE__TYPE: s3
RUNTIMEDB_STORAGE__BUCKET: runtimedb
RUNTIMEDB_STORAGE__REGION: us-east-1
RUNTIMEDB_STORAGE__ENDPOINT: http://127.0.0.1:9000
RUNTIMEDB_STORAGE__ACCESS_KEY: hotmemory
RUNTIMEDB_STORAGE__SECRET_KEY: hotmemory-local
Loading
Loading