loopd: compact summary on the background slot
Co-Authored-By: ornith-1.5-35b-a3b <agent@wntrmute.dev>
This commit is contained in:
@@ -92,6 +92,7 @@ reviewer adds findings under "Reviews" once per milestone.
|
||||
| M4b/05-brokerd-config-gateway | 2026-09-24 | done | 1 | pass | none | Copied `docs/plans/M4b/files/crates/brokerd/tests/config_gateway.rs` and the new `support/rig.rs` (whose `Config` gained `gateway: None`) into `crates/brokerd/tests/`. In `config.rs`: added the `Gateway { socket: PathBuf }` struct (`#[serde(deny_unknown_fields, default)]`) before `default_podman`, the `gateway: Option<Gateway>` field (with `#[serde(default)]`) after `runner` in `Config`, and the `gateway_socket()` method before `audit_dir` — `None` when the section is absent, the default `<home>/run/gateway/gateway.sock` when the socket is empty, else the path as given. `cargo fmt --all` then `cargo test -p brokerd` green with `config_gateway` 4 passed; `make gate` prints `gate: ok` on the first run. | Ornith-1.5-35B-A3B |
|
||||
| M4b/04-brokerd-answer-post | 2026-09-24 | done | 1 | pass | none | Moved `enum Answer` and `struct Answered` from `ledger.rs` into `approvals.rs` before `pub struct Entry` (with doc comments), added `pub use crate::approvals::{Answer, Answered};` next to the `Verdict` use and `DecisionRecord` to the `use proto` line in `approvals.rs`. Added the `post` field to `Answer::Approved`/`Answer::Refused`, threaded it through `ledger::answer` (step 3 binds `post`, step 6 writes it into `AuditEvent::Approval` instead of `None`), and gave the two `bxctl` answers in `admin.rs` `post: None`. Added `Table::contains` before `take`. Copied `ledger_post.rs`, `broker_pending.rs`, `broker_sequence.rs` and `ledger_answer.rs` into `crates/brokerd/tests/`; every suite passes, `ledger_post` 3 passed, `ledger.rs` is 487 lines, and `make gate` prints `gate: ok` on the first run. | Ornith-1.5-35B-A3B |
|
||||
| M4b/03-bxctl-deny | 2026-09-24 | done | 1 | pass | none | Copied `docs/plans/M4b/files/crates/bxctl/tests/cli_deny.rs` into `crates/bxctl/tests/`. In `parse`, `"refuse" => parse_refuse(rest, home)` became `"refuse" | "deny" => parse_refuse(rest, home)`, so `deny` produces the same `Command::Refuse` as `refuse` for every argument list including the error cases. Added a blank line and the two-line `bxctl deny <id> [--reason TEXT] [--admin-socket PATH]` / `Another name for refuse.` block to `USAGE` after the `refuse` block, keeping the two-space and six-space indents. The copied test's `deny_parses_as_refuse` and `usage_names_deny` both failed before the changes and pass after; `cargo test -p bxctl` is green and `make gate` prints `gate: ok` on the first run. | Ornith-1.5-35B-A3B |
|
||||
| M5d1/06-loopd-compact-summary | 2026-10-03 | done | 1 | pass | none | Edited `compact.rs` E1 (module doc, added the `use`s for `neutralise`, `messages_with_carry`, `Config`, `ChatMessage`/`ChatRequest`/`Client`, `is_context_full`, and the `CompactError` enum plus its `Display`/`Error` impls whose `Display` ends with the `session-compact-failed` pointer) and E2 (added `summarise`: read `compact.md` with the path in the error, build the background-slot request with empty `tools` and thinking on, call `chat_with_retry`, neutralise-and-cap the completion, fall back only on a context-full 400, else `Failed`). Copied `tests/compact_summary.rs`, appended the runbook's `session-compact-failed` entry. `compact.rs` is 92 lines; `cargo check` clean, all 12 compact tests pass, `make gate` prints `gate: ok`, 1496 tests. | Ornith-1.5-35B-A3B |
|
||||
| M4b/02-proto-gateway-wire | 2026-09-24 | done | 2 | fail | none | Copied `gateway.rs`, `gateway_wire.rs` and the eight `gateway_*.json` fixtures into place; added `pub mod gateway;` after `pub mod frame;` and the re-export to `lib.rs`, the four `Message` variants plus the `use` to `wire.rs`, and the four `kind` arms (changing "fourteen" to "eighteen") in `broker.rs`. Filled `is_mattermost_id` (`id.len() == 26 && every byte a-z0-9`) and `ApprovalReply::check` in the given order: `by`, `post`, then a reason over `MAX_REASON` chars by `chars().count()`. All 3 `gateway_wire` tests pass; the first `make gate` failed on clippy `collapsible_if`, fixed by collapsing the nested `if let` into a let-chain guard, and the second prints `gate: ok`. | Ornith-1.5-35B-A3B |
|
||||
| M4b/01-proto-escape | 2026-09-24 | done | 1 | pass | none | Copied `docs/plans/M4b/files/crates/proto/src/escape.rs`, `bxctl/src/escape.rs` and `proto/tests/escape.rs` into place, and added `pub mod escape;` to `proto/src/lib.rs` after `pub mod class;`. The bxctl file is now the one-line re-export `pub use proto::escape::{escape_json_text, escape_model_text};`, so no bxctl source or test changed and behaviour is identical. `cargo test -p proto --test escape` reports 8 passed; every bxctl suite passes unchanged (the moved escape test now runs against `proto::escape`); `cargo fmt --all` then `make gate` prints `gate: ok` on the first run. | Ornith-1.5-35B-A3B |
|
||||
| M4a/16-gatewayd-secret-file-race | 2026-09-24 | done | 2 | pass | Reworded the template comment in `read_checked` to drop the literal `check_file` token, so step 5's grep prints only the credential branch's `std::fs::read(&path)` | Replaced `check_file` with `pub fn read_checked(path, between) -> Result<Zeroizing<Vec<u8>>, String>`: check the path (absolute; `symlink_metadata` as `named`; not a symlink; a regular file), call `between()`, open the path, take `opened` metadata, refuse if `(opened.dev(), opened.ino()) != (named.dev(), named.ino())`, then the owner-uid and mode checks on `opened` (not `named`), then read the whole file into a `Zeroizing<Vec<u8>>` from that same handle. Added `use std::io::Read;`. In `load`, the File branch now calls `read_checked(path, &|| {})` and `value(std::mem::take(&mut *bytes))` so nothing unwiped remains. The three suites pass (5, 8, 5); the only remaining `std::fs::read(` is the credential branch and `check_file` is gone. `make gate` prints `gate: ok`. | Ornith-1.5-35B-A3B |
|
||||
|
||||
@@ -1156,3 +1156,39 @@ sources), or remove the table if the workspace needs no key, then restart `memor
|
||||
|
||||
**Check.** After the restart the waiting turns are sent, no line names this entry, and `bxctl
|
||||
memory sync` shows no `key refused` line.
|
||||
|
||||
## session-compact-failed
|
||||
|
||||
**What you see.** `loopd` logs that it could not compact a session, then this entry. The session
|
||||
stays on its current epoch. A turn that was already too large to fit (`session_full`) stays
|
||||
`session_full`. A turn that finished stays finished; the next turn on that session may be
|
||||
`session_full` if it would not fit.
|
||||
|
||||
```text
|
||||
loopd: could not compact session chat-0-0: compact.md: No such file or directory (os error 2); see docs/runbook.md#session-compact-failed
|
||||
```
|
||||
|
||||
**Why.** Compaction needs a summary of the current epoch (a completion on the background slot
|
||||
with `compact.md` as the last user message, or the fallback of that epoch's owner texts). If the
|
||||
prompt file cannot be read, the summary call fails for a reason other than a full context, or
|
||||
writing the next epoch's files fails, `loopd` refuses to guess a prefix. It does not start a new
|
||||
epoch from an empty summary when the model call failed.
|
||||
|
||||
**Confirm.**
|
||||
|
||||
```sh
|
||||
ls -l "$(dirname "$BOXMAKER_HOME/../config")/compact.md" 2>/dev/null
|
||||
ls -l "$BOXMAKER_HOME/sessions/<id>"/
|
||||
tail -n 5 "$BOXMAKER_HOME/sessions/<id>/0.jsonl"
|
||||
```
|
||||
|
||||
`compact.md` lives next to `system.md` (the path in `loopd.toml`'s `[baseline] system`). A last
|
||||
line of type `epoch_end` without `<n>.baseline.json` and `<n>.jsonl` is a compact that stopped
|
||||
halfway; the next compact resumes it.
|
||||
|
||||
**Fix.** Restore `compact.md` beside `system.md` (the starter is `config/compact.md` in the
|
||||
repository). Check that `inferproxy` still answers. If the session directory is mid-write, do not
|
||||
delete `EpochEnd`; the next compact continues from it.
|
||||
|
||||
**Check.** The next turn on that session either runs on the current epoch, or compact succeeds
|
||||
and the next turn's log is `<n>.jsonl` with `epoch` n.
|
||||
|
||||
Reference in New Issue
Block a user