memoryd (files, the check, edits, journal, index, recall, serving), the memory tools and built-in grants in brokerd, the memory block in loopd, bxctl reindex and memory, and the device checks with memoryd. Each task was built as a reference and gated. Each was replayed on its own at its end state (854 to 936 tests) and mutation-checked. Each skeleton compiles and fails its tests. Every todo comment was read beside its signature. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
105 lines
4.0 KiB
Rust
105 lines
4.0 KiB
Rust
//! Review (off by default, `[memory] review = true`): identity writes wait in
|
|
//! `memory/pending.jsonl` for the owner's `accept` or `reject` instead of being applied (M5a spec,
|
|
//! section 5). Notes are never staged.
|
|
|
|
use serde::{Deserialize, Serialize};
|
|
|
|
use proto::escape::escape_model_text;
|
|
use proto::{DataClass, MemoryArgs, Timestamp};
|
|
|
|
use crate::edit::{Context, apply};
|
|
use crate::files::Store;
|
|
use crate::journal::{self, Record};
|
|
|
|
/// One line of `pending.jsonl`: a write waiting, or what became of one.
|
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
|
#[serde(tag = "kind", rename_all = "snake_case", deny_unknown_fields)]
|
|
pub enum Line {
|
|
Waiting {
|
|
id: String,
|
|
time: String,
|
|
session: String,
|
|
call: u64,
|
|
taint: DataClass,
|
|
/// The call's arguments, as `brokerd` sent them.
|
|
arguments: String,
|
|
},
|
|
Accepted {
|
|
id: String,
|
|
time: String,
|
|
edit: String,
|
|
},
|
|
Rejected {
|
|
id: String,
|
|
time: String,
|
|
},
|
|
}
|
|
|
|
fn lines(store: &Store) -> Result<Vec<Line>, String> {
|
|
// Read `store.path("pending.jsonl")`: NotFound -> Ok(empty); another error -> Err(format!("{}:
|
|
// {e}", path.display())). Parse every line as a Line with serde_json; a bad one ->
|
|
// Err(format!("{}: line {}: {e}", path.display(), n + 1)).
|
|
todo!()
|
|
}
|
|
|
|
fn add(store: &Store, line: &Line) -> Result<(), String> {
|
|
// `serde_json::to_string(line)` (its error as a String), then `store.append("pending.jsonl",
|
|
// &format!("{text}\n"))`.
|
|
todo!()
|
|
}
|
|
|
|
/// The writes still waiting: `(id, session, arguments, taint, call)`.
|
|
fn waiting(store: &Store) -> Result<Vec<Line>, String> {
|
|
// The ids that have an Accepted or a Rejected line are done. Return a clone of every Waiting
|
|
// line whose id is not done, in file order.
|
|
todo!()
|
|
}
|
|
|
|
/// Stage an identity write. Its shape is checked now; its content when it is accepted.
|
|
pub fn submit(
|
|
store: &Store,
|
|
args: &MemoryArgs,
|
|
arguments: &str,
|
|
ctx: &Context,
|
|
) -> Result<String, String> {
|
|
// 1. `args.check()?`.
|
|
// 2. `day` is the first 10 characters of `ctx.now.to_rfc3339()`. `taken` = how many Waiting
|
|
// lines have an id starting format!("p-{day}-"). The id is format!("p-{day}-{:04}", taken +
|
|
// 1).
|
|
// 3. `add` a Waiting line: id, time `ctx.now.to_rfc3339()`, session, call and taint from ctx,
|
|
// arguments as given.
|
|
// 4. Ok(format!("waiting for the owner's review ({id}); nothing is changed until it is
|
|
// accepted")).
|
|
todo!()
|
|
}
|
|
|
|
/// The writes waiting, one per line, arguments escaped.
|
|
pub fn list(store: &Store) -> Result<String, String> {
|
|
// One line per `waiting` entry: format!("{id} {session} {}", escape_model_text(arguments)),
|
|
// joined by newlines; with none, "nothing is waiting for review".
|
|
todo!()
|
|
}
|
|
|
|
/// Apply a waiting write, with the same checks, against the file as it is now, and journal it.
|
|
pub fn accept(store: &Store, id: &str, now: Timestamp) -> Result<String, String> {
|
|
// 1. The Waiting line with this id among `waiting(store)?`, else Err(format!("nothing waiting
|
|
// as {id}")).
|
|
// 2. Parse its arguments as MemoryArgs (the error as a String).
|
|
// 3. A Context from its session, call and taint, untrusted false, now.
|
|
// 4. `apply(store, &args, &ctx)?`, then `journal::record(store, &ctx, &outcome)?` gives the
|
|
// edit id.
|
|
// 5. `add` an Accepted line { id, time now, edit }.
|
|
// 6. Ok(format!("accepted: {id} as {edit}: {}", outcome.answer)).
|
|
todo!()
|
|
}
|
|
|
|
/// Discard a waiting write; the journal records it.
|
|
pub fn reject(store: &Store, id: &str, now: Timestamp) -> Result<String, String> {
|
|
// 1. The Waiting line with this id, else Err(format!("nothing waiting as {id}")).
|
|
// 2. `add` a Rejected line { id, time now }.
|
|
// 3. `journal::append` a Record: its session and call, action "reject", file None, to empty, no
|
|
// entries, no notes, untrusted false, undoes Some(id).
|
|
// 4. Ok(format!("rejected: {id}")).
|
|
todo!()
|
|
}
|