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>
This commit is contained in:
@@ -0,0 +1,130 @@
|
||||
# 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.
|
||||
|
||||
```rust
|
||||
//! 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.**
|
||||
|
||||
```sh
|
||||
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.
|
||||
Reference in New Issue
Block a user