Task 06 opened the whole implementer log (~360 kB) after reading supporting modules; OpenCode returned "Context size has been exceeded" and made no commit. AGENTS.md and tasks 06-10 now say to read the first 12 lines only. Co-Authored-By: Grok 4.6 <noreply@x.ai>
9.7 KiB
M5d-2 task 10: MemoryCommand::Tiers and bxctl memory tiers
Branch: m5d2. Check it: git branch --show-current prints m5d2 and git status --short
is empty; otherwise stop. Never switch branch.
Commit subject: memoryd: Tiers listing; bxctl memory tiers
Log row: the Task cell is M5d2/10-memoryd-tiers.
Goal
MemoryCommand::Tiers lists each identity entry and each note with how many times recalls/
named it and when it was last found (spec 4.4). Untrusted notes are included and marked.
Identity entries list with 0 hits (README known limits). bxctl memory tiers prints that
listing. The starter config/jobs/tier.toml.example documents a weekly job; nothing in
loopd special-cases it.
Every place that changes
| Place | What happens there |
|---|---|
crates/bxcore/src/memory.rs |
MemoryCommand::Tiers after Sync |
crates/memoryd/src/tiers.rs |
new: list |
crates/memoryd/src/lib.rs |
pub mod tiers; |
crates/memoryd/src/serve/admin.rs |
Tiers arm |
crates/bxctl/src/memory.rs |
["tiers"] |
crates/bxctl/src/cli.rs |
USAGE names tiers |
crates/bxctl/tests/cli_memory_sync.rs |
USAGE pin matches U1 (edit S1) |
config/jobs/tier.toml.example |
new, given starter |
crates/memoryd/tests/tiers.rs |
new, given |
crates/bxctl/tests/cli_memory_tiers.rs |
new, given |
| everything else | no change: not cli_memory.rs (T19: new file) |
Files
- Copy, from
docs/plans/M5d2/files/(the same path under it): the new testscrates/memoryd/tests/tiers.rsandcrates/bxctl/tests/cli_memory_tiers.rs, and the starterconfig/jobs/tier.toml.example. - Create:
crates/memoryd/src/tiers.rs(function below). - Modify:
crates/bxcore/src/memory.rs,crates/memoryd/src/lib.rs,crates/memoryd/src/serve/admin.rs,crates/bxctl/src/memory.rs,crates/bxctl/src/cli.rs,crates/bxctl/tests/cli_memory_sync.rs,docs/implementer-log.md. - Do not touch: any other file. Do not edit
crates/bxctl/tests/cli_memory.rs.
Facts you need
-
MemoryCommandisrename_all = "snake_case". JSON is"tiers". Add the variant afterSync, with a doc comment.admin.rsmatch is exhaustive: add the arm in this task or it does not compile. -
Listing format, exact. One block per identity entry, then one per note, in file order:
identity memory.md hits 0 last - I like tea. identity user.md hits 0 last - The owner is Kyle. note session mm-a hits 2 last 2026-10-03T07:30:00.000Z private trusted launch window moved. note session mm-c hits 0 last - private untrusted example.org says the launch moved.Two spaces before the text line.
hitsandlastfor identity are always0and-. For a note,hitsis how manyrecalls/*.jsonllines contain a hit withkind == "noted",session == note.session,record ==that note's index innotes::parse(from 0).lastis the maximumtimestring among those lines, or-. Class ispublic/private/secret(notes::class_name). Trust isuntrustedwhenKind::Noted { untrusted: true }, elsetrusted. Moved/dropped notes: still list them asnoteusing the same line, classprivate,trusted(they have no untrusted flag). -
recalls/.store.path("recalls"). Missing directory: all counts 0. A file that exists but cannot be read is an error. Ignore a line that does not parse. Deserialize:#[derive(Deserialize)] struct Logged { time: String, hits: Vec<Hit>, } #[derive(Deserialize)] struct Hit { kind: String, session: String, record: i64, }(extra fields in the file are ignored: this is our format but old lines may grow; the given tests use exactly those fields plus
session/query/epochon the line. Usedeny_unknown_fieldsoff soepochandquerydo not fail.) -
Identity.
store.entries(MemoryFile::Memory)thenUser. Empty file: no identity lines for that file. -
Counts map. A nested
HashMap<(String, i64), (u64, String)>trips clippytype_complexity(-D warnings). Name a smallTally { n: u64, last: String }and returnHashMap<(String, i64), Tally>. Do not#[allow]. -
listreturnsResult<String, String>. Trailing newline on the last block is fine;bxctladds another when printingMemoryAdminReply.text(writeln!). The given bxctl test includes a trailing newline in the reply text and expects it pluswriteln!'s newline. -
adminarm.MemoryCommand::Tiers => tiers::list(&self.store, now)—nowis unused bylisttoday; keep the signaturelist(store, now)so the given tests compile. -
bxctl.["tiers"] => Ask::Admin(MemoryCommand::Tiers, String::new()).["tiers", _]is usage. USAGE: add| tierson thebxctl memoryline. -
rustfmt
modorder inmemoryd/src/lib.rs. -
The items the edits use. This table is complete.
Item What it does Store::entriesidentity notes::parse/class_namenotes Store::path("recalls")the jsonl directory MemoryFile::namememory.md/user.md
The function
crates/memoryd/src/tiers.rs — fill list. No todo!().
//! The tiering listing: identity entries and notes with recall-hit counts (M5d spec, 4.4).
use bxcore::{MemoryFile, Timestamp};
use serde::Deserialize;
use crate::files::Store;
use crate::notes::{self, Kind};
#[derive(Deserialize)]
struct Logged {
time: String,
hits: Vec<Hit>,
}
#[derive(Deserialize)]
struct Hit {
kind: String,
session: String,
record: i64,
}
/// One listing for `bxctl memory tiers` and for the tiering job's user text.
pub fn list(store: &Store, _now: Timestamp) -> Result<String, String> {
let _ = store;
Ok(String::new())
}
Replace the stub body in this task.
crates/bxcore/src/memory.rs
M1. After Sync, in MemoryCommand:
/// Identity entries and notes with recall-hit counts (M5d spec, section 4.4).
Tiers,
crates/memoryd/src/serve/admin.rs
A1. After the Sync arm, before the closing of the match:
MemoryCommand::Tiers => crate::tiers::list(&self.store, now),
crates/bxctl/src/memory.rs
B1. After ["sync"] => ..., before _ => return Err(UsageError):
["tiers"] => Ask::Admin(MemoryCommand::Tiers, String::new()),
crates/bxctl/src/cli.rs
U1. The bxctl memory USAGE line. The line
bxctl memory log [N] | undo <id> | pending | accept <id> | reject <id> | sync [--memory-socket PATH]
becomes
bxctl memory log [N] | undo <id> | pending | accept <id> | reject <id> | sync | tiers [--memory-socket PATH]
crates/bxctl/tests/cli_memory_sync.rs
S1. sync_parses pins the USAGE line from M5c. U1 changes it. The lines
USAGE.contains("| reject <id> | sync [--memory-socket PATH]\n"),
"{USAGE}"
become
USAGE.contains("| reject <id> | sync | tiers [--memory-socket PATH]\n"),
"{USAGE}"
Do not edit cli_memory.rs. This file is the M5c pin of the same USAGE string.
Steps
-
1. Copy the given files.
cp docs/plans/M5d2/files/crates/memoryd/tests/tiers.rs crates/memoryd/tests/ cp docs/plans/M5d2/files/crates/bxctl/tests/cli_memory_tiers.rs crates/bxctl/tests/ mkdir -p config/jobs cp docs/plans/M5d2/files/config/jobs/tier.toml.example config/jobs/tier.toml.example -
2. See them fail.
cargo build --tests --keep-going -p bxcore -p memoryd -p bxctl, timeout 600000 ms:tiersdoes not compile (E0432unresolvedmemoryd::tiers);cli_memory_tiersdoes not compile (E0599no variantTiers). After M1 alone,admin.rsfails (non-exhaustive patterns). Do M1, A1,tiers.rs, B1, U1 together in step 3. This step is the first build against the given tests. -
3. The edits. M1,
tiers.rswith a reallist,pub mod tiers;, A1, B1, U1, S1.cargo check -p bxcore -p memoryd -p bxctlprints no error and no warning.wc -l crates/memoryd/src/tiers.rsprints a number ≤ 500. -
4. The tests.
cargo test -p memoryd --test tiers(4) andcargo test -p bxctl --test cli_memory_tiers --test cli_memory --test cli_memory_sync(2 new): every test passes. -
5. Check.
git diff --statlists the files in "Every place that changes" except the new tests and starter (those are untracked untilgit add). -
6. Run the gate.
cargo fmt --all, thenmake gate, timeout 600000 ms. Last linegate: ok. Test count 1574 (1568 before, plus 6). Aftergate: ok, do not add uptest result:lines. -
7. Log and commit. Task cell
M5d2/10-memoryd-tiers. Read only the first 12 lines ofdocs/implementer-log.md; insert the row under the header. Stage withgit add crates/bxcore crates/memoryd crates/bxctl config/jobs docs/implementer-log.md Cargo.lock, then commit asAGENTS.mdsays.
Done when
make gateprintsgate: okwith 1574 tests.git diff HEAD~1 --statlists only the files this task names plus the log.
Stop and report if
- The branch check fails; a "before" block is not found exactly once; step 2 differs;
cargo checkwarns; a test fails; the gate's last line is notgate: ok; you editcli_memory.rs; you leave thecli_memory_syncUSAGE pin on the old string; identity entries show a non-zero hit count; untrusted notes are omitted; you add a cron crate; you special-case the nametierinloopd.
What the given tests pin
Identity lines are hits 0 last - with the entry text; notes join recalls/ on
(noted, session, record) and take the latest time; untrusted notes are listed and marked;
a hit on note 1 does not count for note 0; bxctl memory tiers parses and prints the reply.