Eight closed tasks for epochs and compaction, then ten for the scheduler, ToolSummary, and tiers. Given tests first. Gate 1456 → 1507 → 1572. Co-Authored-By: Grok 4.6 <noreply@x.ai>
7.9 KiB
M5d-1 task 01: [compact] on loopd.toml
Branch: m5d1. Check it: git branch --show-current prints m5d1 and git status --short
is empty; otherwise stop. Never switch branch.
Commit subject: loopd: [compact] threshold, summary_chars and summarise
Log row: the Task cell is M5d1/01-loopd-compact-config.
Goal
M5d-1 compact after a turn whose last Usage is at or above a fraction of expect.n_ctx (spec
3.3, 5). This task adds only the [compact] table so later tasks can read it:
threshold(float, default0.85)summary_chars(default4000)summarise(list of tool names, default["http_fetch"])
Nothing in this task uses the values. summarise is stored so a spec-complete loopd.toml
loads; M5d-2 reads it. Unknown keys in [compact] are errors, as in every table we define.
Config::compact_prompt is compact.md beside [baseline] system (the same directory join
load already does for system.md). The starter file is copied in task 05.
Every place that changes
This is the complete list.
| Place | What happens there |
|---|---|
crates/loopd/src/config.rs |
Compact; Config.compact; Config::compact_prompt (edits C1 to C3) |
crates/loopd/tests/config_compact.rs |
new, given |
| everything else | no change: not session.rs, not the existing config.rs tests or fixtures |
Files
- Copy, from
docs/plans/M5d1/files/(the same path under it): the new testcrates/loopd/tests/config_compact.rs. - Modify:
crates/loopd/src/config.rs,docs/implementer-log.md. - Do not touch: any other file.
Facts you need
Configalready has#[serde(default)]on optional tables (sampling,limits,paths,channel,loop,baseline,broker,memory).[compact]is the same: missing meansCompact::default().thresholdisf64.Sampling.temperatureis alreadyf64onConfig, soPartialEqonConfigstill compiles. Do not add a range check: the spec does not name one.summarisedefault.vec!["http_fetch".to_string()]. An explicitsummarise = []is allowed and is empty. Unknown names are not checked here (M5d-2).compact_prompt.self.baseline.system.with_file_name("compact.md"). Afterload,baseline.systemis already joined to the config file's directory, so the prompt follows it into a subdirectory whensystemdoes (prompts/agent.md→prompts/compact.md).- The items the edits use. This table is complete;
serdeis already a dependency ofloopd.Item What it does #[serde(deny_unknown_fields, default)]on Compact: missing table usesDefault; unknown keys errorPath::with_file_namecompact.mdbesidesystem.mdVec<String>summarise
The edits, exactly
Nothing else in this file changes. Each quoted "before" block occurs exactly once in its file when its turn comes (make them in order). Keep the fields in the order shown.
crates/loopd/src/config.rs
C1. After the Memory struct (the lines)
pub struct Memory {
pub socket: std::path::PathBuf,
}
become
pub struct Memory {
pub socket: std::path::PathBuf,
}
/// `[compact]`: when a session starts a new epoch, and how long a summary may be (M5d spec,
/// section 5). `summarise` is stored here and unused until M5d-2.
#[derive(Debug, Clone, PartialEq, Deserialize)]
#[serde(deny_unknown_fields, default)]
pub struct Compact {
pub threshold: f64,
pub summary_chars: usize,
pub summarise: Vec<String>,
}
impl Default for Compact {
fn default() -> Self {
Self {
threshold: 0.85,
summary_chars: 4_000,
summarise: vec!["http_fetch".to_string()],
}
}
}
C2. The lines
#[serde(default)]
pub memory: Memory,
}
become
#[serde(default)]
pub memory: Memory,
#[serde(default)]
pub compact: Compact,
}
C3. After channel_socket (the lines)
pub fn channel_socket(&self) -> std::path::PathBuf {
if self.channel.socket.as_os_str().is_empty() {
self.paths.home.join("run/loop/loop.sock")
} else {
self.channel.socket.clone()
}
}
}
become
pub fn channel_socket(&self) -> std::path::PathBuf {
if self.channel.socket.as_os_str().is_empty() {
self.paths.home.join("run/loop/loop.sock")
} else {
self.channel.socket.clone()
}
}
/// `compact.md` beside the system prompt: the summary call's last user message.
pub fn compact_prompt(&self) -> std::path::PathBuf {
self.baseline.system.with_file_name("compact.md")
}
}
Steps
-
1. Copy the given test. From the repository root, one command:
cp docs/plans/M5d1/files/crates/loopd/tests/config_compact.rs crates/loopd/tests/Then
git status --shortlists one new file,crates/loopd/tests/config_compact.rs. -
2. See it fail.
cargo build --tests --keep-going -p loopd, with a shell timeout of 600000 ms: one test target,config_compact, does not compile, withE0609(no field \compact` on type `Config`) andE0599(no method named `compact_prompt`/no variant or associated item named `default` for `Compact``). Every other target compiles. -
3. The edits. Make edits C1 to C3, in order.
cargo check -p loopdprints no error and no warning. -
4. The tests.
cargo test -p loopd --test config_compact --test config: every test passes, 8 inconfig_compactand the others as before. -
5. Check.
git diff --statlists exactlycrates/loopd/src/config.rs. -
6. Run the gate.
cargo fmt --all, thenmake gate, with a shell timeout of 600000 ms. Expected last line:gate: ok. The test count is 1464 after this task, 1456 before it (1455 passed incargo test --workspace, plus 1), and the 8 new ones are the tests ofconfig_compact.rs. To count, add up theN passedof everytest result:line the gate'scargo test --workspaceprinted, and add 1. Never run the gate again only to count. -
7. Log and commit. Add your row to
docs/implementer-log.md; its Task cell isM5d1/01-loopd-compact-config. Stage withgit add crates/loopd docs/implementer-log.md Cargo.lock, then commit asAGENTS.mdsays.Cargo.lockdoes not change in this task; thegit addis harmless.
Done when
make gateprintsgate: okwith 1464 tests.git diff HEAD~1 --statlists onlycrates/loopd/src/config.rs,crates/loopd/tests/config_compact.rsanddocs/implementer-log.md.
Stop and report if
- The branch check at the top fails.
- A "before" block is not in its file exactly as shown when its turn comes, or is there more than once.
- Step 2 shows a different result: another target failing, or other errors.
cargo checkin step 3 prints an error or a warning. Quote it, and stop.- A test fails in step 4. Do not change the test: stop, and give the failing tests' names and their messages.
- The fix seems to need a change anywhere else: a fixture,
config.rstests, a range check onthreshold. It does not; stop and say why you think so. - A test outside
config_compact.rsfails in the gate: check once whether it fails without your change (git stash -u, run it,git stash pop), then stop and report both results. - The gate passes with a count other than 1464.
- A comment in an edit does not fit the code under it. Quote it and stop.
What the given tests pin
Each of these changes to the finished code makes at least one given test fail: the default
threshold moved; summary_chars default moved; summarise default emptied or given another
name; unknown keys accepted; a misspelt summary_char accepted; a partial table wiping the
other defaults; compact_prompt using home or the config file's name instead of system.md's
directory; Compact::default disagreeing with a missing table.