diff --git a/docs/GATE5-MEMORY-CONTRACT.md b/docs/GATE5-MEMORY-CONTRACT.md new file mode 100644 index 000000000..a89c9838e --- /dev/null +++ b/docs/GATE5-MEMORY-CONTRACT.md @@ -0,0 +1,78 @@ + + +> **Ownership.** This contract describes a `relayhistory` / `ai-hist` integration: +> provider CLI syntax, JSON output, exit-code behaviour and provider-specific +> traps. That is SDK/control-plane knowledge, not kernel design authority. +> RFC-0001 §4 and settled decision #13 keep the Rust kernel provider-neutral and +> closed-vocabulary, so this file lives under `docs/` — it previously sat under +> `kernel/`, where its location signalled kernel ownership regardless of what the +> text said, and a future implementer could reasonably have grown the kernel +> around `ai-hist` on that signal alone. + +# Gate 5 — what a relayhistory-backed MemoryProvider has to do + +Read of `AgentWorkforce/relayhistory` at `3e7df69`, 2026-09-08. RFC-0001 gate 5 +says relayhistory is "consumed over its serialization contract, not rewritten", +so this records that contract rather than proposing a design that ignores it. + +## What already exists on the flows side + +PR `#221` (issue `#220`) landed the seam, and it is the part that is hard to retrofit (`kernel/MEMORY.md` is titled "Step memory, slice 1 (#220)" because it names the issue): +`MemoryProvider`, `MemoryPack`, and a `memory.injected` journal entry carrying +the consuming `step_id` and initial `attempt`. That is RFC decision 10 — memory +tokens charged to the consuming step, itemized, no shared pools. + +What is wired is `FixedMemoryProvider`, which returns +`{"text":"fixed memory pack","citations":[]}` with synthetic usage of 7 input +tokens and `"0.002"`. `kernel/MEMORY.md` says so plainly: "a substrate stub: +there is no retrieval, relayhistory call, or claim about memory quality." + +## The contract to consume + +Retrieval is `ai-hist pack`: + + ai-hist pack [--project P] [--tag T] [--source S] + [--limit N] [--tokens N] [--fts] [--json] + +- `--json` emits `{ "query": ..., "entries": [...] }`. +- `--tokens N` is the budget, applied as `chars_budget = tokens * 4` + (`crates/ai-hist/src/lib.rs:2369`) — a four-characters-per-token + approximation, not a tokenizer. A provider must not report that figure back + as exact usage; decision 10's accounting is only checkable if the number + means something. + +**The trap worth writing down before anyone implements this.** `pack_entries` +calls `std::process::exit(1)` when there are no results, after printing +`{"query": ..., "entries": []}`. Exit 1 here means *no memory matched*, not +*the call failed*. A provider that treats nonzero as an error will report every +cold-start step as a memory failure, and a provider that treats it as fatal +will fail closed on exactly the runs that have nothing to remember yet. + +The trajectory side is `ai-hist push`, which takes `--install-service` and an +interval. Gate 5's bar is "every relayflow run pushes trajectories to +relayhistory **without opt-in code**", so that is a service the cell runs, not +a call each flow makes. + +## What gate 5 still needs, in order + +1. A `RelayhistoryMemoryProvider` over `ai-hist pack --json`, mapping a step's + declared `memory: { scope, query, budget }` onto the pack arguments and + reporting honest usage rather than the char-budget approximation. + + **It does not live in the kernel.** The adapter belongs at the + SDK/control-plane edge and reaches the kernel across the journal protocol + boundary. RFC-0001 §4 and settled decision #13 keep the Rust kernel small, + pure and closed-vocabulary, so `relayflowd` must not gain an `ai-hist` + dependency, a subprocess call, or any relayhistory-shaped vocabulary. + `MemoryProvider` in `kernel/relayflowd/src/memory.rs` stays an **injected + protocol seam** — the kernel declares the shape it will accept and never + learns who satisfies it. Reading item 1 as "implement this trait inside + relayflowd" is the failure mode this paragraph exists to prevent. +2. Exit-1-means-empty handled explicitly, with a test that a step whose query + matches nothing still runs and journals an empty pack. +3. The push side wired as a service. + +None of this is the acceptance test. RFC gate 5 asks for "an agent avoiding a +mistake recorded in a previous run's trajectory, with the citation in its +output" — a behavioural bar, not a passing suite. The three items above are +what makes attempting it possible. diff --git a/ops/SCOREBOARD.md b/ops/SCOREBOARD.md index 9b0066bbb..ccc8c4ad8 100644 --- a/ops/SCOREBOARD.md +++ b/ops/SCOREBOARD.md @@ -11,6 +11,6 @@ Every row starts RED and moves only on evidence. AMBER blocks nothing here | 4 — chief / harness | RED | not started | | 5 — memory | RED | scoped by harness directive 3 (relayfile + relayhistory per customer) | | 6 — integrations via relayfile | RED | **BLOCKED on gates 2-4** (Khaliq, 2026-08-28, option B — this repo has no example flows to convert; helper surface may be built now but does not make the gate green) — harness (design partner) needs slack/notion helpers; also unblocks its `REPLACE-WHEN: gate-2` shims | -| 7 — sandbox routing | RED | regression suite needs darwin-arm64 placement | +| 7 — sandbox routing | **AMBER** | in progress — #227 merged 2026-09-08 (`be3c95ed`). Routing is journaled as a `RoutingDecision` fact at `StepRouted` and dispatch consumes it without re-deciding. **Not GREEN**: gate 7 requires the same flow YAML to run locally and in cloud, and the cloud half is unproven. Design, test inventory and the mutation transcript live in the PR #227 review artifacts, not in this row. | | 8 — identity + credentials | RED | regression suite needs multi-principal runs | | 9 — self-improving agents | RED | depends on 5 + 8 |