Files
boxmaker/docs/plans/M5d1/05-loopd-compact-due.md
T
kyleandGrok 4.6 3726ae585f docs: M5d-1 and M5d-2 implementation plans for Ornith
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>
2026-10-02 21:54:37 -07:00

5.4 KiB
Raw Blame History

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 test crates/loopd/tests/compact_due.rs and the starter config/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 f64 is exact for these magnitudes (n_ctx is 262144). Do not use as on untrusted indexes; these are our own config and the server's counts.
  • Fallback join. "\n\n" between texts. Newest last: reverse the User contents, then join, then cap.
  • Cap. s.chars().take(n).collect::<String>(). n == 0 is empty. A multi-byte character is not split.
  • config/compact.md is copied like config/system.md. Task 06 reads it from Config::compact_prompt.
  • The items the edits use. This table is complete.
    Item What it does
    bxcore::Usage cache_n, prompt_n
    LogRecord::User { content, .. } fallback texts
    str::chars, Iterator::take the 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.rs and make L1. See it fail: cargo test -p loopd --test compact_due, timeout 600000 ms: the two due tests and the three fallback tests panic on todo!() (or fail their asserts if you filled the bodies early). If the test target does not compile (E0432 unresolved loopd::compact), the mod line is missing.

  • 3. Fill due and fallback_summary. Delete each instruction comment. cargo check -p loopd prints no error and no warning.

  • 4. The tests. cargo test -p loopd --test compact_due: 6 passed.

  • 5. Check. git diff --stat lists crates/loopd/src/lib.rs, crates/loopd/src/compact.rs, config/compact.md. git status --short shows compact_due.rs as new.

  • 6. Run the gate. cargo fmt --all, then make gate, timeout 600000 ms. Last line gate: ok. Test count 1489 (1483 before, plus 6).

  • 7. Log and commit. Task cell M5d1/05-loopd-compact-due. Stage with git add crates/loopd config/compact.md docs/implementer-log.md Cargo.lock, then commit as AGENTS.md says.

Done when

  • make gate prints gate: ok with 1489 tests.
  • git diff HEAD~1 --stat lists only crates/loopd/src/lib.rs, crates/loopd/src/compact.rs, crates/loopd/tests/compact_due.rs, config/compact.md and 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 1489; 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.