memoryd: a staged write's id counts every write staged that day
This commit is contained in:
@@ -92,8 +92,8 @@ pub fn submit(
|
|||||||
) -> Result<String, String> {
|
) -> Result<String, String> {
|
||||||
// 1. `args.check()?`.
|
// 1. `args.check()?`.
|
||||||
// 2. `day` is the first 10 characters of `ctx.now.to_rfc3339()`. `taken` = how many Waiting
|
// 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 +
|
// lines of `lines(store)?`, done or not, have an id starting format!("p-{day}-"): an id is
|
||||||
// 1).
|
// never used twice. 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,
|
// 3. `add` a Waiting line: id, time `ctx.now.to_rfc3339()`, session, call and taint from ctx,
|
||||||
// arguments as given.
|
// arguments as given.
|
||||||
// 4. Ok(format!("waiting for the owner's review ({id}); nothing is changed until it is
|
// 4. Ok(format!("waiting for the owner's review ({id}); nothing is changed until it is
|
||||||
@@ -101,7 +101,7 @@ pub fn submit(
|
|||||||
args.check()?;
|
args.check()?;
|
||||||
let rfc = ctx.now.to_rfc3339();
|
let rfc = ctx.now.to_rfc3339();
|
||||||
let day = rfc.get(..10).unwrap_or("0000-00-00");
|
let day = rfc.get(..10).unwrap_or("0000-00-00");
|
||||||
let taken = waiting(store)?
|
let taken = lines(store)?
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|line| match line {
|
.filter(|line| match line {
|
||||||
Line::Waiting { id, .. } => id.starts_with(&format!("p-{day}-")),
|
Line::Waiting { id, .. } => id.starts_with(&format!("p-{day}-")),
|
||||||
|
|||||||
@@ -0,0 +1,78 @@
|
|||||||
|
//! A staged write's id is never one used before that day, so a write staged after another was
|
||||||
|
//! accepted or rejected stays waiting and can be accepted (M5a spec, section 5; review of
|
||||||
|
//! 2026-09-24). Do not edit.
|
||||||
|
|
||||||
|
#[path = "support/tmp.rs"]
|
||||||
|
mod tmp;
|
||||||
|
|
||||||
|
use memoryd::edit::Context;
|
||||||
|
use memoryd::files::Store;
|
||||||
|
use memoryd::review;
|
||||||
|
use proto::{DataClass, MemoryArgs, MemoryFile, Timestamp};
|
||||||
|
use tmp::TempDir;
|
||||||
|
|
||||||
|
fn now() -> Timestamp {
|
||||||
|
Timestamp::parse("2026-09-24T20:14:37.123Z").unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn setup(tag: &str) -> (TempDir, Store) {
|
||||||
|
let dir = TempDir::new(tag);
|
||||||
|
let starter = dir.write("etc/soul.md", "Soul.\n");
|
||||||
|
let store = Store::new(&dir.path().join("memory"));
|
||||||
|
store.init(&starter).unwrap();
|
||||||
|
(dir, store)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ctx() -> Context {
|
||||||
|
Context {
|
||||||
|
session: "mm-s1".to_string(),
|
||||||
|
call: 7,
|
||||||
|
taint: DataClass::Private,
|
||||||
|
untrusted: false,
|
||||||
|
now: now(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Stage an add of `text` to memory.md; the answer.
|
||||||
|
fn stage(store: &Store, text: &str) -> String {
|
||||||
|
let json = format!(r#"{{"action":"add","file":"memory","text":"{text}"}}"#);
|
||||||
|
let args: MemoryArgs = serde_json::from_str(&json).unwrap();
|
||||||
|
review::submit(store, &args, &json, &ctx()).unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn waiting_as(id: &str) -> String {
|
||||||
|
format!("waiting for the owner's review ({id}); nothing is changed until it is accepted")
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn a_write_staged_after_an_accepted_one_gets_a_new_id_and_can_be_accepted() {
|
||||||
|
let (_dir, store) = setup("review-ids-accepted");
|
||||||
|
assert_eq!(stage(&store, "a"), waiting_as("p-2026-09-24-0001"));
|
||||||
|
review::accept(&store, "p-2026-09-24-0001", now()).unwrap();
|
||||||
|
assert_eq!(stage(&store, "b"), waiting_as("p-2026-09-24-0002"));
|
||||||
|
let listed = review::list(&store).unwrap();
|
||||||
|
assert_eq!(listed.lines().count(), 1, "{listed}");
|
||||||
|
assert!(
|
||||||
|
listed.starts_with("p-2026-09-24-0002 mm-s1 {"),
|
||||||
|
"{listed}"
|
||||||
|
);
|
||||||
|
review::accept(&store, "p-2026-09-24-0002", now()).unwrap();
|
||||||
|
assert_eq!(store.entries(MemoryFile::Memory).unwrap(), ["a", "b"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn ids_count_every_write_staged_that_day_waiting_or_done() {
|
||||||
|
let (_dir, store) = setup("review-ids-mixed");
|
||||||
|
stage(&store, "a");
|
||||||
|
stage(&store, "b");
|
||||||
|
stage(&store, "c");
|
||||||
|
review::reject(&store, "p-2026-09-24-0001", now()).unwrap();
|
||||||
|
review::accept(&store, "p-2026-09-24-0003", now()).unwrap();
|
||||||
|
assert_eq!(stage(&store, "d"), waiting_as("p-2026-09-24-0004"));
|
||||||
|
let listed = review::list(&store).unwrap();
|
||||||
|
let ids: Vec<&str> = listed
|
||||||
|
.lines()
|
||||||
|
.filter_map(|line| line.split(" ").next())
|
||||||
|
.collect();
|
||||||
|
assert_eq!(ids, ["p-2026-09-24-0002", "p-2026-09-24-0004"], "{listed}");
|
||||||
|
}
|
||||||
@@ -6,6 +6,7 @@ reviewer adds findings under "Reviews" once per milestone.
|
|||||||
|
|
||||||
| Task | Date | Status | Gate runs | First gate | Deviations | Notes | Model |
|
| Task | Date | Status | Gate runs | First gate | Deviations | Notes | Model |
|
||||||
|---|---|---|---|---|---|---|---|
|
|---|---|---|---|---|---|---|---|
|
||||||
|
| M5a/20-review-ids | 2026-09-25 | done | 1 | pass | none | The two edits exactly as the task gives, both in `submit` in `crates/memoryd/src/review.rs`: the comment block "how many Waiting lines have an id starting format!(\"p-{day}-\"). The id is format!(\"p-{day}-{:04}\", taken + 1)." became "how many Waiting lines of `lines(store)?`, done or not, have an id starting format!(\"p-{day}-\"): an id is never used twice. The id is format!(\"p-{day}-{:04}\", taken + 1)."; and `let taken = waiting(store)?` became `let taken = lines(store)?`, the `.iter()`/`.filter(..)`/`.count()` after it unchanged. `waiting` untouched, since `list`/`accept`/`reject` still use it. Copied `crates/memoryd/tests/review_ids.rs` from `docs/plans/M5a/files/`. `cargo fmt --all`; step 2 showed exactly the two named tests failing (`a_write_staged_after_an_accepted_one_gets_a_new_id_and_can_be_accepted` expected `p-2026-09-24-0002` got `p-2026-09-24-0001`; `ids_count_every_write_staged_that_day_waiting_or_done` expected `p-2026-09-24-0004` got `p-2026-09-24-0002`); after the edits both pass and `cargo test -p memoryd` every suite passes; `make gate` printed `gate: ok`. `cargo test --workspace` reports 940 passed, plus the gate's separate loopd `end_to_end` ignored test = 941 total. No new dependency, so `Cargo.lock` is unchanged. | ? |
|
||||||
| M5a/19-no-forged-headers | 2026-09-25 | done | 2 | timed out (shell 2 min) | none | Two edits exactly as the task gives. `crates/proto/src/memory.rs`: the doc comment above `pub fn entry_problem` (the two lines "holding a line that is only the separator") became the three lines explaining a line starting with the separator, spaces aside, would read as a header in notes.md and a header says where an entry came from; and in the body, `if line.trim() == SEPARATOR { return Some(format!("an entry cannot hold a line that is only {SEPARATOR}")); }` became `if line.trim_start().starts_with(SEPARATOR) { return Some(format!("an entry cannot hold a line that starts with {SEPARATOR}")); }`, the for loop and closing braces unchanged. `crates/memoryd/src/notes.rs` `parse_header`: the `untrusted` match `Some(s) => *s == "untrusted"` became `Some(&"untrusted") => true, Some(_) => return None`, so a `noted` header whose last part is anything other than `untrusted` is not a header. Copied three test files from `docs/plans/M5a/files/`: `crates/proto/tests/memory_args.rs` and `crates/memoryd/tests/notes.rs` (new versions) and the new `crates/memoryd/tests/forgery.rs` (not the `-task02`/`-task06` files). `cargo fmt --all`. Step 2 showed exactly the three named tests failing (`entries_are_not_empty_not_too_long_and_hold_no_separator_line`, `a_noted_header_with_an_unknown_last_part_is_not_a_header`, `a_note_holding_a_header_line_is_refused_and_nothing_is_written`); the fourth, `recall_finds_no_identity_in_what_an_untrusted_session_wrote`, already passed. After the edits the step-2 command passed and `cargo test -p proto -p memoryd -p brokerd` every suite passed. `make gate` printed `gate: ok`; `cargo test --workspace` reported 938 passed and the gate's separate loopd `end_to_end` ignored test added 1, for 939 total. First `make gate` run hit the shell's 120 s timeout before finishing (the loopd daemon test needs more than 2 min); the second run completed with `gate: ok`. No new dependency, so `Cargo.lock` is unchanged. | Ornith-1.5-35B-A3B |
|
| M5a/19-no-forged-headers | 2026-09-25 | done | 2 | timed out (shell 2 min) | none | Two edits exactly as the task gives. `crates/proto/src/memory.rs`: the doc comment above `pub fn entry_problem` (the two lines "holding a line that is only the separator") became the three lines explaining a line starting with the separator, spaces aside, would read as a header in notes.md and a header says where an entry came from; and in the body, `if line.trim() == SEPARATOR { return Some(format!("an entry cannot hold a line that is only {SEPARATOR}")); }` became `if line.trim_start().starts_with(SEPARATOR) { return Some(format!("an entry cannot hold a line that starts with {SEPARATOR}")); }`, the for loop and closing braces unchanged. `crates/memoryd/src/notes.rs` `parse_header`: the `untrusted` match `Some(s) => *s == "untrusted"` became `Some(&"untrusted") => true, Some(_) => return None`, so a `noted` header whose last part is anything other than `untrusted` is not a header. Copied three test files from `docs/plans/M5a/files/`: `crates/proto/tests/memory_args.rs` and `crates/memoryd/tests/notes.rs` (new versions) and the new `crates/memoryd/tests/forgery.rs` (not the `-task02`/`-task06` files). `cargo fmt --all`. Step 2 showed exactly the three named tests failing (`entries_are_not_empty_not_too_long_and_hold_no_separator_line`, `a_noted_header_with_an_unknown_last_part_is_not_a_header`, `a_note_holding_a_header_line_is_refused_and_nothing_is_written`); the fourth, `recall_finds_no_identity_in_what_an_untrusted_session_wrote`, already passed. After the edits the step-2 command passed and `cargo test -p proto -p memoryd -p brokerd` every suite passed. `make gate` printed `gate: ok`; `cargo test --workspace` reported 938 passed and the gate's separate loopd `end_to_end` ignored test added 1, for 939 total. First `make gate` run hit the shell's 120 s timeout before finishing (the loopd daemon test needs more than 2 min); the second run completed with `gate: ok`. No new dependency, so `Cargo.lock` is unchanged. | Ornith-1.5-35B-A3B |
|
||||||
| M5a/18-repeated-code | 2026-09-25 | done | 2 | pass | none | The four edits exactly as the task gives: `edit.rs` added the private `claim(file, before, &mut named, old)` one blank line above `consolidate`, and replaced the four identical `Keep`/`Rewrite`/`Move`/`Drop` blocks (the `position` check plus the `named.insert` duplicate guard) with `claim(file, before, &mut named, &old)?;`, leaving the `New` arm untouched; `proto/src/memory.rs` added the private `entry(text, max)` one blank line above `impl RecallArgs`, and rewrote `MemoryArgs::check` to call `entry` with `?` instead of the nine `if let Some(problem) = entry_problem(..) { return Err(problem); }` blocks (the `Consolidate` arm's `map_err(|problem| problem.to_string())` became `map_err(str::to_string)`), `RecallArgs::check` unchanged; `brokerd/src/builtin.rs` replaced the two `GrantProblem {..}` inline closures in `grants` with a single `let problem = |error: String| GrantProblem {..}` and `map_err(|e| problem(e.to_string()))`; `bxctl/src/memory.rs` added `let unreachable = |e: &dyn std::fmt::Display| format!("cannot reach memoryd at {}: {e}", socket.display())` and used it for connect, the two `set_*_timeout` sets and the `write_frame` (the `memoryd did not answer` line was left as its own sentence). `cargo fmt --all`; the step-2 greps print 1, 0, 2, 1 in order; `cargo test -p memoryd -p proto -p brokerd -p bxctl` every suite passes; `make gate` printed `gate: ok` with 936 tests, unchanged. No new dependency, so `Cargo.lock` is unchanged. | Ornith-1.5-35B-A3B |
|
| M5a/18-repeated-code | 2026-09-25 | done | 2 | pass | none | The four edits exactly as the task gives: `edit.rs` added the private `claim(file, before, &mut named, old)` one blank line above `consolidate`, and replaced the four identical `Keep`/`Rewrite`/`Move`/`Drop` blocks (the `position` check plus the `named.insert` duplicate guard) with `claim(file, before, &mut named, &old)?;`, leaving the `New` arm untouched; `proto/src/memory.rs` added the private `entry(text, max)` one blank line above `impl RecallArgs`, and rewrote `MemoryArgs::check` to call `entry` with `?` instead of the nine `if let Some(problem) = entry_problem(..) { return Err(problem); }` blocks (the `Consolidate` arm's `map_err(|problem| problem.to_string())` became `map_err(str::to_string)`), `RecallArgs::check` unchanged; `brokerd/src/builtin.rs` replaced the two `GrantProblem {..}` inline closures in `grants` with a single `let problem = |error: String| GrantProblem {..}` and `map_err(|e| problem(e.to_string()))`; `bxctl/src/memory.rs` added `let unreachable = |e: &dyn std::fmt::Display| format!("cannot reach memoryd at {}: {e}", socket.display())` and used it for connect, the two `set_*_timeout` sets and the `write_frame` (the `memoryd did not answer` line was left as its own sentence). `cargo fmt --all`; the step-2 greps print 1, 0, 2, 1 in order; `cargo test -p memoryd -p proto -p brokerd -p bxctl` every suite passes; `make gate` printed `gate: ok` with 936 tests, unchanged. No new dependency, so `Cargo.lock` is unchanged. | Ornith-1.5-35B-A3B |
|
||||||
| M5a/17-memoryd-no-slicing | 2026-09-25 | done | 1 | pass | none | The five slice-to-`get` edits exactly as the task gives: `notes.rs` `minute` two lines -> `format!("{}Z", s.get(..16).unwrap_or(&s))`; `journal.rs` `append` the five-line `day` -> `rfc.get(..10).unwrap_or("0000-00-00")` and `&day[..7]` -> `day.get(..7).unwrap_or("0000-00")`; `review.rs` `submit` `&rfc[..10]` -> `rfc.get(..10).unwrap_or("0000-00-00")`; `recall.rs` `recall` `&rfc[..7]` -> `rfc.get(..7).unwrap_or("0000-00")`. Left `&all[all.len().saturating_sub(count)..]` in `log` as instructed. `cargo fmt --all`; the step-2 grep for `\[..[0-9]+\]` prints nothing; `cargo test -p memoryd` every suite passes; `make gate` printed `gate: ok` on the first run, 936 tests unchanged. | Ornith-1.5-35B-A3B |
|
| M5a/17-memoryd-no-slicing | 2026-09-25 | done | 1 | pass | none | The five slice-to-`get` edits exactly as the task gives: `notes.rs` `minute` two lines -> `format!("{}Z", s.get(..16).unwrap_or(&s))`; `journal.rs` `append` the five-line `day` -> `rfc.get(..10).unwrap_or("0000-00-00")` and `&day[..7]` -> `day.get(..7).unwrap_or("0000-00")`; `review.rs` `submit` `&rfc[..10]` -> `rfc.get(..10).unwrap_or("0000-00-00")`; `recall.rs` `recall` `&rfc[..7]` -> `rfc.get(..7).unwrap_or("0000-00")`. Left `&all[all.len().saturating_sub(count)..]` in `log` as instructed. `cargo fmt --all`; the step-2 grep for `\[..[0-9]+\]` prints nothing; `cargo test -p memoryd` every suite passes; `make gate` printed `gate: ok` on the first run, 936 tests unchanged. | Ornith-1.5-35B-A3B |
|
||||||
|
|||||||
Reference in New Issue
Block a user