How it works
Long-running agents exceed useful model context. Naive summarization loses detail, stale summaries override newer instructions, and fresh runs restart work incorrectly. Remaind separates immutable raw truth from derived state, with a mechanical gate at every boundary.
One folder, three sources of truth
Everything lives in a single .context/ directory per project — an append-only raw log, derived working state, and a compact human-readable handover.
.context/
├── CONTRACT.md # the binding contract
├── active/
│ ├── state.json # derived working state
│ ├── handover.md # compact continuity document
│ └── (resume_packet.md, history/ — runtime)
├── logs/
│ └── events.jsonl # append-only raw timeline — source of truth
├── schemas/ # JSON Schemas + threshold/redaction/tools config
└── (db/context.sqlite, artifacts/ — runtime)When sources disagree, an explicit authority order resolves truth — lower wins. A stale summary can never override a newer user instruction.
- 1Latest explicit user instruction
- 2logs/events.jsonl — the raw event log
- 3active/state.json — derived working state
- 4active/handover.md — the compact handover
- 5Derived memories
The lifecycle
Remaind is a substrate, not a framework — your agent loop reads and writes it. Six operations cover the full cycle.
The append-only event ledger
Every user message, tool call, decision, and file change is appended to events.jsonl — one JSON event per line, validated against a JSON Schema before it is written. The raw log is the source of truth: never rewritten, never lossy.
- Redaction by default. Nine secret patterns (OpenAI/Anthropic keys, JWTs, AWS credentials, private keys, bearer tokens, …) are replaced with deterministic hashes before the event touches disk.
- Large outputs are routed out. Content over the byte threshold — or any binary payload — is written to
artifacts/; the event keeps head/tail excerpts, a SHA-256, and the path. The raw log stays small. - Importance is load-bearing. It drives what compaction must preserve.
| Level | Meaning | Compaction rule |
|---|---|---|
| 0 | trace / debug | verbose logs you'll rarely need again |
| 1 | normal useful event | routine tool calls, assistant turns |
| 2 | decision · correction · constraint · file change · blocker · result | must survive every compaction |
| 3 | remember-instruction · active next step · active blocker · critical user instruction | must be explicit in state or handover |
Validated compaction
Compaction reduces the active context while preserving everything load-bearing. A compactor only proposes a candidate; a structured validator decides whether to accept it.
The validator runs six mechanical checks. If any is false, the candidate is rejected and the prior handover is kept untouched — a validation event records why. The compactor proposes; only the validator disposes.
The mechanical resume gate
Resume is internal and seamless by default. The gate interrupts the user only when something is mechanically wrong:
- raw log, state, and handover conflict
next_stepis missing- the latest user instruction is not represented
- resume confidence is low and the next planned tool is flagged
mutates/spends_money/external_side_effectintools.yaml
Tool risk is read from configuration — never inferred from prose.
Atomic and recoverable
Derived files are written atomically: copy the current version to history/, write a temp file, fsync, rename over the target, fsync the directory. There is no partial-write window.
Every state and handover write snapshots the prior version. rollback --to <timestamp> restores the derived files as of any point in history — and events.jsonl is never touched. Rollback is itself reversible.