Given tests: Action::Turn (not To), send_turn of epoch 1, clippy on
compact_summary, jsonl-without-baseline, live turn after compact sends
the carry. send_turn maps Damaged to Local("log") as read_turn did.
Gate counts 1484 → 1508. Mutations recorded in the README.
Co-Authored-By: Grok 4.6 <noreply@x.ai>
5.4 KiB
M5d-1 task 05: when to compact, and the fallback summary
Branch: m5d1. Check it: git branch --show-current prints m5d1 and git status --short
is empty; otherwise stop. Never switch branch.
Commit subject: loopd: compaction threshold and the fallback summary
Log row: the Task cell is M5d1/05-loopd-compact-due.
Goal
A session is due to compact when the current epoch's last Usage has
cache_n + prompt_n at or above threshold × expect.n_ctx (spec 3.3). There is no idle timer.
If the summary call is context-full (task 06), the fallback is the epoch's User texts, newest
last, cut to summary_chars on a character boundary (spec 3.4).
This task is those two pure functions, the compact module, and the starter config/compact.md.
No inference, no disk besides copying the starter.
Every place that changes
| Place | What happens there |
|---|---|
crates/loopd/src/compact.rs |
new: due, fallback_summary, cap_chars |
crates/loopd/src/lib.rs |
pub mod compact; |
config/compact.md |
new, given starter |
crates/loopd/tests/compact_due.rs |
new, given |
| everything else | no change |
Files
- Copy, from
docs/plans/M5d1/files/(the same path under it): the new testcrates/loopd/tests/compact_due.rsand the starterconfig/compact.md. - Create:
crates/loopd/src/compact.rs(skeleton below). - Modify:
crates/loopd/src/lib.rs,docs/implementer-log.md. - Do not touch: any other file.
Facts you need
- Depth is saturating.
cache_n.saturating_add(prompt_n) as f64 >= threshold * (n_ctx as f64). 0.85 × 262144 is 222822.4, so 222822 is not due and 222823 is. 0.85 × 1000 is 850 exactly. - Cast.
u64 as f64is exact for these magnitudes (n_ctx is 262144). Do not useason untrusted indexes; these are our own config and the server's counts. - Fallback join.
"\n\n"between texts. Newest last: reverse theUsercontents, then join, then cap. - Cap.
s.chars().take(n).collect::<String>().n == 0is empty. A multi-byte character is not split. config/compact.mdis copied likeconfig/system.md. Task 06 reads it fromConfig::compact_prompt.- The items the edits use. This table is complete.
Item What it does bxcore::Usagecache_n,prompt_nLogRecord::User { content, .. }fallback texts str::chars,Iterator::takethe cap
Skeleton: crates/loopd/src/compact.rs
Write this file, then fill the two todo!() bodies from their comments and delete the
comments. Each comment is at most six lines.
//! Compaction: when a session starts a new epoch, the summary call, and writing the next files
//! (M5d spec, section 3). Later tasks fill the call and the write.
use bxcore::{LogRecord, Usage};
/// True when `cache_n + prompt_n` is at or above `threshold * n_ctx`.
pub fn due(usage: &Usage, threshold: f64, n_ctx: u64) -> bool {
// saturating_add the two counts; compare as f64 to `threshold * (n_ctx as f64)`.
todo!()
}
/// The epoch's `User` texts, newest last, joined with `"\n\n"`, then `cap_chars`.
pub fn fallback_summary(records: &[LogRecord], summary_chars: usize) -> String {
// Collect `&str` contents of every `User`, reverse, join `"\n\n"`, then `cap_chars`.
todo!()
}
pub(crate) fn cap_chars(s: &str, n: usize) -> String {
s.chars().take(n).collect()
}
crates/loopd/src/lib.rs
L1. After pub mod channel; add pub mod compact; (keep rustfmt's order; if rustfmt
moves the line, accept that).
Steps
-
1. Copy the given files.
cp docs/plans/M5d1/files/crates/loopd/tests/compact_due.rs crates/loopd/tests/ cp docs/plans/M5d1/files/config/compact.md config/compact.md -
2. Write the skeleton at
crates/loopd/src/compact.rsand make L1. See it fail:cargo test -p loopd --test compact_due, timeout 600000 ms: the twoduetests and the threefallbacktests panic ontodo!()(or fail their asserts if you filled the bodies early). If the test target does not compile (E0432unresolvedloopd::compact), themodline is missing. -
3. Fill
dueandfallback_summary. Delete each instruction comment.cargo check -p loopdprints no error and no warning. -
4. The tests.
cargo test -p loopd --test compact_due: 6 passed. -
5. Check.
git diff --statlistscrates/loopd/src/lib.rs,crates/loopd/src/compact.rs,config/compact.md.git status --shortshowscompact_due.rsas new. -
6. Run the gate.
cargo fmt --all, thenmake gate, timeout 600000 ms. Last linegate: ok. Test count 1490 (1484 before, plus 6). -
7. Log and commit. Task cell
M5d1/05-loopd-compact-due. Stage withgit add crates/loopd config/compact.md docs/implementer-log.md Cargo.lock, then commit asAGENTS.mdsays.
Done when
make gateprintsgate: okwith 1490 tests.git diff HEAD~1 --statlists onlycrates/loopd/src/lib.rs,crates/loopd/src/compact.rs,crates/loopd/tests/compact_due.rs,config/compact.mdand the log.
Stop and report if
- The branch check fails; step 2 fails to compile for a reason other than the missing module
or
todo!(); a test fails; the gate count is not 1490; you add an idle timer. There is none.
What the given tests pin
849/1000 is not due, 850 is; 222822/262144 is not due, 222823 is; threshold 1.0 is the full
window; fallback is newest-last with "\n\n"; the cap is in characters on a boundary; no
users is empty.