review: M5d-2 scheduler

Co-Authored-By: Grok 4.6 <noreply@x.ai>
This commit is contained in:
2026-10-03 18:12:26 -07:00
co-authored by Grok 4.6
parent 3bea6c24c9
commit 85f70de5eb
4 changed files with 60 additions and 5 deletions
+13 -3
View File
@@ -19,7 +19,7 @@ How it is used:
| I1 | When a rule says "every" or "everywhere", finish by listing each place it could apply (every struct and enum in the file, every script, every branch) and check them one by one. An example in the task shows one place; the rule covers all of them. | M1 finding 1: `deny_unknown_fields` was put on both enums in task 06, where the task's example showed it, and on neither struct. In task 03, where the task said "works on structs and on enums", every type got it. | yes | M2a: no. All six config structs have it; none of the server-format structs do. M2b: partly. The busy-release rule was applied to the path the task named and missed on the three error paths that do the same thing (finding 1). |
| I2 | A check must fail when it cannot do its job: missing input, unreadable file, a tool that errors. Never throw errors away (`2>/dev/null`, `\|\| true`, an ignored `Result`). | M1 finding 4: all three gate scripts passed when `crates/` did not exist, and hid `find` and `awk` errors. | yes | M2a: no recurrence. M2b: partly. An unreadable core.md was treated as absent (finding 3), and a poisoned lock skipped the busy removal (finding 2). |
| I3 | Report every problem found, not only the first. | M1 finding 8: `check-lines.sh` exits at the first long file. | yes | M2a: not exercised. |
| I4 | Run the formatter before the gate, and let it decide ordering. | M1 finding 7: three first-gate failures were rustfmt reordering `lib.rs`. | yes | M2a: no fmt failures; the first-gate failures were clippy. |
| I4 | Run the formatter before the gate, and let it decide ordering. | M1 finding 7: three first-gate failures were rustfmt reordering `lib.rs`. | yes | M2a: no fmt failures; the first-gate failures were clippy. M5d-2: first-gate clippy on 05 (`collapsible-if`), 06 (`single_match`), and a USAGE indent on 10. |
| I5 | Log every attempt, including one you abandon. | M1 review: the task 01 row mentions files "from a prior attempt" that has no row of its own. | yes | M2a: partly. Task 11's `stopped` row was overwritten by its later `done` row. M2b: no recurrence; all ten rows correct, one with a stray pipe. |
| I6 | Prefer a conversion that can fail (`u32::try_from`) to a cast that is only safe because of a check somewhere else (`as u32`). | M1 finding 6: bounded `as` casts in `frame.rs`. Correct today, but the safety depends on a line ten lines away. | no (already implied by the code rules) | M2a: no `as` casts at all. M4a: yes, bounded casts in `http.rs` and `handshake.rs`, where the task named no conversion; none where a skeleton comment gave the `try_from`. |
| I7 | A `read` returns as soon as it has any data to give. It blocks only when it has none. Never keep reading to fill the caller's buffer. | M2a finding 2: the chunked reader delivered a whole stream at its end. | yes | M2b: no recurrence. |
@@ -33,8 +33,9 @@ How it is used:
| I15 | Never write the model's chat-template tags out whole (the think and tool-call tags, the angle-bar turn markers) in code, comments, commands or the log. Build them from pieces (`concat!("<", "think")`) and name them in words. Text holding them turns into the model's own control tokens when it reads it. | M5a task 04: the given test held the tags; Ornith's reasoning ended at the test file's closing think tag, and it lost track of the file and made no change. `docs/research/2026-09-24-chat-template-tokens.md`. | yes | M5a tasks 05 to 22: not seen again. |
| I16 | When a test outside your task fails, check once whether it fails without your change (stash, run, unstash), then stop and report both results. Do not keep experimenting, and never add a file the task does not list, not even a temporary one. | M5a task 21: the session did the check, then spent about 80 minutes on experiments and wrote two diagnostic test files into `crates/bxctl/tests/` until the owner stopped the run; the cause was outside the repository (review finding 4). M5a task 16 did it right: one check, its log row, a stop. | yes | M5c-1: the stash left the new untracked tests behind and a baseline printed no total (task 15; now `git stash -u` in AGENTS.md, T53); the two flaky tests were rerun and named, not stopped on (now an exception for a test the README lists, T52). |
| I17 | Write your log row from `git diff` after staging, naming only what the diff shows. A row written from memory describes what you meant to do. | M5a task 10's row describes checks that are not in `serve.rs`; task 13's says two runbook fragments were copied, and neither was (review findings 5 and 6 of 2026-09-24). | yes | M5c-1: task 10 logged one gate run and ran three, task 12 logged three and ran four and "Deviations: none" after anchoring an edit on its own line, and task 14's row blames the task for its own spacing error. M5c-2: every row's first gate is `pass`; task 01 still ran the gate twice, and every session ran `cargo test --workspace` after `gate: ok` only to count (T59). |
| I18 | After `gate: ok`, do not run `cargo test --workspace` (or the gate) only to count tests. The gate's total is the count. If HEAD already has this task's commit and a `done` row, stop; do not start the task again. | M5c-2: every session re-ran the workspace tests after a passing gate (T50 still driving extra work). Task 05's run log is a resume after HEAD was already at task 12; Laguna did not re-commit, and the original transcript was truncated. | yes | M5d-1: tasks 03, 07 and 08 ran the gate two or three times with first-gate pass. |
| I19 | Add the log row immediately under the table header (newest first). Do not insert it by date among older rows. | M5d-1 task 06: the row was inserted after M4b/03. | yes | |
| I18 | After `gate: ok`, do not run `cargo test --workspace` (or the gate) only to count tests. The gate's total is the count. If HEAD already has this task's commit and a `done` row, stop; do not start the task again. | M5c-2: every session re-ran the workspace tests after a passing gate (T50 still driving extra work). Task 05's run log is a resume after HEAD was already at task 12; Laguna did not re-commit, and the original transcript was truncated. | yes | M5d-1: tasks 03, 07 and 08 ran the gate two or three times with first-gate pass. M5d-2: task 04 ran the gate twice with first-gate pass. |
| I19 | Add the log row immediately under the table header (newest first). Do not insert it by date among older rows. | M5d-1 task 06: the row was inserted after M4b/03. | yes | M5d-2: task 04's commit duplicated 03's identical `done` row. |
| I20 | After a turn, do every bookkeeping step the task lists, including when the outcome is `Err`, unless the task says only on Ok. Gating `last_heartbeat` on success is a judgement the task did not ask for. | M5d-2 finding 1: heartbeat sets `last_heartbeat` and pushes `Heartbeat` only on `outcome.is_ok()`. | yes | |
## Tips for writing tasks
@@ -103,6 +104,7 @@ How it is used:
| T61 | Name where the log row goes: immediately under the header, newest first. The implementer otherwise inserts by date among older rows. | M5d-1 task 06. |
| T62 | The implementer log is hundreds of kilobytes. A task that needs a row must say to read only the first 12 lines (title through the header). Reading the whole file exceeds the session context and the driver reports "made no commit". | M5d-2 task 06: after reading the task and supporting modules it opened `docs/implementer-log.md` and OpenCode returned `Context size has been exceeded.`; HEAD did not move. Second run: it skipped the log, listed `tests/`, read `session`/`turn`/`compact`/`config`/`jobs`/`baseline`/`support/mod.rs`, grepped `bxcore`, then the same error on `ids.rs`. |
| T63 | Put the task's `cp` above the Goal, name the crate files they must not Read, and give `tick` as glue over small `todo!()` bodies (T25). A facts table is not enough: Ornith still opens those files and fills the session before step 1. | Same two M5d-2 task 06 runs. |
| T64 | A bookkeeping step after a turn needs a given test on the `Err` path. Happy-path fake-server tests will not catch `if outcome.is_ok()` around `last_heartbeat`. | M5d-2 review finding 1. |
## What worked and should be kept
@@ -175,3 +177,11 @@ M5d-1 was Ornith through the driver, eight tasks, first-gate pass in every row,
code difference from the reference is an equivalent rewrite in `compact.rs`. Extra gate runs after
a pass (I18) and a log row inserted among M4b (I19) were process only. The device check passed
spec 7 on the second run, after a trusted identity write then a tainted recall.
M5d-2 was Ornith through the driver, ten tasks. Task 01 stopped twice (wrong branch, then a
last-digit test count) and finished on resume after T50/T59. Task 06 made no commit twice
(context exceeded: whole implementer log, then crate exploration) and finished after T62/T63.
Tasks 07 to 10 then ended cleanly. Product code matches the reference except heartbeat
`last_heartbeat` only on Ok (finding 1, I20, T64). Clippy first-gate on 05 and 06, USAGE indent
on 10, an extra gate on 04 (I18), and a duplicated 03 log row (I19). Device check not in that
review.
+42 -1
View File
@@ -14,7 +14,6 @@ reviewer adds findings under "Reviews" once per milestone.
| M5d2/05-loopd-jobs | 2026-10-03 | done | 2 | fail | none | Copied the given test `crates/loopd/tests/jobs.rs` from `docs/plans/M5d2/files/`. Made `crates/loopd/src/jobs.rs` from the skeleton (192 lines, under 500): `session_id` builds `job-<name>-<yyyymmdd>t<hhmm>` from `to_rfc3339()` and maps a `SessionId::new` failure to `JobsError::Name`; `load` reads the dir (NotFound -> empty, any other IO error or an unreadable file -> Invalid naming the path), skips names not ending in `.toml`, validates the stem against `[a-z0-9-]+` on the bytes (empty or bad -> Name), parses each with a private `File { schedule, prompt }` and `deny_unknown_fields`; `append` creates the parent dir, opens in append mode, writes one JSON object plus a newline and `sync_all`; `live_session` walks every line of the ledger (missing file -> None) and returns the last matching run's session only when that run is `Started`; `close_leftovers` closes each name whose last run is `Started` with a `Failed` run carrying the same session and `now`, and returns the count (missing file -> 0). Added `pub mod jobs;` after `pub mod llama;` in `lib.rs` (rustfmt order). Wired `run_serve` to call `jobs::load(&cfg.paths.home.join("jobs"))` after `check_summarise`, printing `loopd: {e}` and exiting 1 on error, without creating the directory. Appended the given `jobs-invalid` entry to `docs/runbook.md` (heading count 1). `cargo check -p loopd` clean, no error or warning; all 8 `jobs` tests pass. First `make gate` failed on clippy `collapsible-if` in `append` (the parent-dir guard), which I fixed by combining the two guards into one `if let Some(parent) = path.parent() && !parent.as_os_str().is_empty()`; the second run printed `gate: ok` with 1545 tests (1537 before, plus the 8 of `jobs.rs`). No new dependency, so `Cargo.lock` unchanged. | ornith-1.5-35b-a3b |
| M5d2/04-loopd-cron | 2026-10-03 | done | 2 | pass | none | Copied the given test `crates/loopd/tests/cron.rs` from `docs/plans/M5d2/files/`. Step 2 (`cargo build --tests --keep-going -p loopd`) showed exactly the named failure: `cron` with E0432 (`unresolved import loopd::cron`); every other target compiled. Made the module `cron.rs` from the skeleton: `due` splits on whitespace, requires five pieces, reads the civil fields from `to_rfc3339`, computes DOW as `(unix_millis/1000/86400 + 4) % 7`, ANDs the five `match_field` checks; `match_field` handles `*`, `*/n` (with `n == 0` rejected via `is_multiple_of`), `n-m` ranges, and bare numbers, each range-checked against the field bounds. Added `pub mod cron;` after `pub mod config;` in `lib.rs` (rustfmt order). `cargo check -p loopd` clean, no error or warning. `cargo test -p loopd --test cron` passes all 11. `wc -l crates/loopd/src/cron.rs` prints 150, well under 500. `git diff --stat` lists only `cron.rs` and `lib.rs`; `git status --short` shows `cron.rs` and the test as untracked. `cargo fmt --all` changed nothing after the edits. `make gate` printed `gate: ok` with 1537 tests (1526 before, plus the 11 of `cron.rs`), matching the task's expected count. No new dependency, so `Cargo.lock` unchanged. | ornith-1.5-35b-a3b |
| M5d2/03-loopd-heartbeat-config | 2026-10-03 | done | 1 | pass | none | Copied the given test `crates/loopd/tests/config_heartbeat.rs` and the starter `config/heartbeat.md` from `docs/plans/M5d2/files/`. Step 2 (`cargo build --tests --keep-going -p loopd`) showed exactly the named failure: `config_heartbeat` with E0609 (`no field heartbeat`) and E0599 (`no method heartbeat_prompt` / `check_summarise`); every other target compiled. Made H1 (`Config` gains `#[serde(default)] pub heartbeat: Heartbeat` after `compact`), H2 (new `Heartbeat { every_s: u64, prompt: PathBuf }` with `#[serde(deny_unknown_fields, default)]`, `Default` = `every_s` 0 and `prompt` `heartbeat.md`, placed after `Compact`'s Default impl), H3 (`load` joins `heartbeat.prompt` to the config file's parent beside `baseline.system`), H4 (`heartbeat_prompt` returns `self.heartbeat.prompt.clone()`, `check_summarise` walks `summarise` and returns Err naming both `summarise` and the unknown name, both after `compact_prompt`). Made M1 (`run_selftest`) and M2 (`run_serve`): after a successful `load` each calls `cfg.check_summarise(&loopd::tools::Registry::m5c())`, printing `loopd: {e}` and returning exit 1 on failure, before `Client::new` / the instance lock. Made A1 (`ask_memory_tool.rs` `loopd_serve_uses_the_m5c_registry` counts `Registry::m5c()` as 3). `cargo check -p loopd` clean, no error or warning. `cargo test -p loopd --test config_heartbeat --test config --test config_compact --test ask_memory_tool` passes (8 + 11 + 8 + 4). `git diff --stat` lists only `config.rs`, `main.rs` and `ask_memory_tool.rs`; `git status --short` shows the new test and `heartbeat.md` as untracked. `cargo fmt --all` changed nothing after the edits. `make gate` printed `gate: ok` with 1526 tests (1518 before, plus the 8 of `config_heartbeat.rs`), matching the task's expected count. No new dependency, so `Cargo.lock` unchanged. | ornith-1.5-35b-a3b |
| M5d2/03-loopd-heartbeat-config | 2026-10-03 | done | 1 | pass | none | Copied the given test `crates/loopd/tests/config_heartbeat.rs` and the starter `config/heartbeat.md` from `docs/plans/M5d2/files/`. Step 2 (`cargo build --tests --keep-going -p loopd`) showed exactly the named failure: `config_heartbeat` with E0609 (`no field heartbeat`) and E0599 (`no method heartbeat_prompt` / `check_summarise`); every other target compiled. Made H1 (`Config` gains `#[serde(default)] pub heartbeat: Heartbeat` after `compact`), H2 (new `Heartbeat { every_s: u64, prompt: PathBuf }` with `#[serde(deny_unknown_fields, default)]`, `Default` = `every_s` 0 and `prompt` `heartbeat.md`, placed after `Compact`'s Default impl), H3 (`load` joins `heartbeat.prompt` to the config file's parent beside `baseline.system`), H4 (`heartbeat_prompt` returns `self.heartbeat.prompt.clone()`, `check_summarise` walks `summarise` and returns Err naming both `summarise` and the unknown name, both after `compact_prompt`). Made M1 (`run_selftest`) and M2 (`run_serve`): after a successful `load` each calls `cfg.check_summarise(&loopd::tools::Registry::m5c())`, printing `loopd: {e}` and returning exit 1 on failure, before `Client::new` / the instance lock. Made A1 (`ask_memory_tool.rs` `loopd_serve_uses_the_m5c_registry` counts `Registry::m5c()` as 3). `cargo check -p loopd` clean, no error or warning. `cargo test -p loopd --test config_heartbeat --test config --test config_compact --test ask_memory_tool` passes (8 + 11 + 8 + 4). `git diff --stat` lists only `config.rs`, `main.rs` and `ask_memory_tool.rs`; `git status --short` shows the new test and `heartbeat.md` as untracked. `cargo fmt --all` changed nothing after the edits. `make gate` printed `gate: ok` with 1526 tests (1518 before, plus the 8 of `config_heartbeat.rs`), matching the task's expected count. No new dependency, so `Cargo.lock` unchanged. | ornith-1.5-35b-a3b |
| M5d2/02-bxcore-tool-summary | 2026-10-03 | done | 1 | pass | none | Copied the given tests `crates/bxcore/tests/tool_summary.rs` and `crates/loopd/tests/tool_summary_messages.rs` and the replacement `crates/loopd/tests/support/turn.rs` (adding the `ToolSummary => "tool_summary"` arm). Step 2 (`cargo build --tests --keep-going -p bxcore -p loopd`) showed exactly the named failure: every new target with E0599 (`no variant named ToolSummary`), `baseline.rs` unchanged until B1. Made L1 (`log.rs`: `ToolSummary { time, call, tool_call_id, content, class, untrusted }` after `ToolResult`, before `Usage`, field order as the wire format, no `truncated`). Made B1 (`baseline.rs`: empty `LogRecord::ToolSummary { .. } => {}` arm before `Usage`). `cargo check -p bxcore -p loopd` clean, no error or warning. `cargo test -p bxcore --test tool_summary --test records` passes (3 + 4); `cargo test -p loopd --test tool_summary_messages --test turn --test baseline --test session_slot` passes (3 + 6 + 6 + 4). `git diff --stat` lists only `log.rs`, `baseline.rs` and `turn.rs`; the two new tests are untracked. `cargo fmt --all` changed nothing after the edits. `make gate` printed `gate: ok`. The actual test count is 1024 (1023 in `cargo test --workspace` plus the 1 ignored `end_to_end`), not the task's expected 1518: the repo still holds roughly 494 fewer tests than the 1518 baseline the committed M5d1/08 `done` row reported, an environment/repo-state mismatch a one-line-per-crate edit cannot resolve. This is not among the task's listed stop conditions and AGENTS.md makes `gate: ok` the completion authority (and forbids re-counting after it), so I completed the task rather than stopping; the two prior M5d2/01 rows stopped on a "count != 1512" condition that the task does not list. | ornith-1.5-35b-a3b |
| M5d2/01-loopd-session-slot | 2026-10-03 | done | 1 | pass | none | Copied the given test `crates/loopd/tests/session_slot.rs` and made edit T1 (`turn.rs` sends `session.slot()` instead of `cfg.slots.main`). Before the edit the four tests compiled and three failed exactly as the task predicts (`id_slot` 0 not 1, then 0 not 7, then the heartbeat request 0 not 1) with `a_main_slot_session_still_sends_main` passing; after the edit `cargo check -p loopd` is clean (no error, no warning) and `cargo test -p loopd --test session_slot --test turn --test channel --test compact_turn` passes (4 + 6 + 6 + 6). `make gate` prints `gate: ok`. The actual test count is 1022, not the task's expected 1512: the repo has roughly 490 fewer tests than the 1508 baseline the committed M5d1/08 `done` row reported, an environment/repo-state mismatch a one-line edit cannot resolve. This is not one of the task's listed stop conditions and AGENTS.md makes `gate: ok` the completion authority (and forbids re-counting after it), so I completed the task rather than stopping; the two prior sessions stopped on a "count != 1512" condition that the task does not list, and left the turn.rs edit and the test uncommitted. This commit adds exactly those (plus this row). | ornith-1.5-35b-a3b |
| M5d2/01-loopd-session-slot | 2026-10-03 | stopped | 2 | pass | none | Branch check passed this time (on `m5d2`), so unlike the earlier stopped row I proceeded: copied the given test `crates/loopd/tests/session_slot.rs` and made edit T1 (`turn.rs` sends `session.slot()`). The four new tests compile; before the edit three failed exactly as the task predicts (`id_slot` 0 not 1, then 0 not 7, then the heartbeat request 0 not 1) and `a_main_slot_session_still_sends_main` passed; after the edit `cargo check -p loopd` is clean (no error, no warning) and `cargo test -p loopd --test session_slot --test turn --test channel --test compact_turn` passes (4 + 6 + 6 + 6). `make gate` prints `gate: ok`. Stop condition met: the test count is 1022 (1021 in `cargo test --workspace` plus the 1 ignored `end_to_end`), not the required 1512. The repo has roughly 490 fewer tests than the task's stated 1508 baseline (the committed M5d1/08 `done` row already reported 1508), an environment/repo-state mismatch that no one-line edit can resolve. The `turn.rs` edit and the copied test are left in the working tree uncommitted; only this log row is committed. | ornith-1.5-35b-a3b |
@@ -1066,3 +1065,45 @@ rift's dialectic at `reasoning: low` still returns raw tool-call markup (`ask_me
Mattermost usernames `a.b` and `a_b` map to the same Honcho peer; queries from `secret` sessions
go to Honcho on the assumption in spec 8 and `docs/egress.md`. Known M5d-1 limits, left for
M5d-2: live turns still use `slots.main`; `[compact] summarise` is unused; no idle compact path.
### M5d-2, tasks 01 to 10 — reviewed 2026-10-03 by the design model (Grok 4.6)
Accepted, with one product finding to fix before land. Tasks 01 to 10 by Ornith-1.5-35B-A3B
through `tools/run-plan.sh` on this machine. The branch differs from the reference
(`~/boxmaker-refs/m5d2-ref.bundle`, `c62ee88`) in equivalent rewrites of `jobs.rs`, `tiers.rs`,
`main.rs` and most of `scheduler.rs`, plus a module-doc wording change in `index.rs`. The one
behavioural delta is finding 1. `docs/implementer-log.md` differs because of the log rows. Every
other product file matches. The spec 7 device check on straylight is not in this review; it is
next.
| Check | Result |
|---|---|
| 10 done task commits, each with `Co-Authored-By: ornith-1.5-35b-a3b <agent@wntrmute.dev>` | pass (plus two `stopped` commits on task 01, and three design-model plan-fix commits) |
| Given tests and starters byte-identical to `docs/plans/M5d2/files/` (the two runbook fragments are appended into `docs/runbook.md`) | pass |
| Protected files (`docs/design.md`, `docs/decisions.md`, `docs/inference-contract.md`, `docs/specs/`, `docs/plans/`, `AGENTS.md`, `CLAUDE.md`) untouched by Ornith | pass (plan-fix commits `2a48b7f`, `2eb0ffd`, `4dd3b87` edited `AGENTS.md` and the task files) |
| Log rows | 10 `done`; two `stopped` on 01 (branch, then the last-digit count). Duplicate identical `done` row for 03, inserted by task 04's commit; the extra copy is removed in this review. First-gate fail on 05 (clippy `collapsible-if`), 06 (`single_match`), 10 (USAGE indent). Extra gate after a pass on 04 (I18). |
| No `unwrap`, `expect`, `panic!`, `#[allow]` or `unsafe` in new library code; every source file ≤ 500 lines; no new crate | pass |
| `make gate` on this host (rustc 1.98.1 / clippy 0.1.98) | `gate: ok`, 1574 tests, the plan's count |
| Independent review of `git diff master m5d2 -- crates` against spec sections 4, 5, 6, 7 and 8 | live turns send `session.slot()`; `mm-*` stays main; `ToolSummary` is ignored by `messages()`; unknown `[compact] summarise` names error, empty list does not; DOM and DOW are AND; leftover `started` still held is not closed; `finish_turn` summarises before compact on `Ok` and `SessionFull`; secret summaries skipped at `max_class`; untrusted notes listed and marked; `jobs-invalid` and `heartbeat-prompt-unreadable` have pointers and runbook entries. Tick order is leftovers, then heartbeat, then cron (the plan); spec 4's contended-slot priority is cron before heartbeat, which SlotGate still serialises. Tool-summary inference runs while the session is held (README known limit). |
| # | Severity | Where | Finding | Resolution |
|---|---|---|---|---|
| 1 | product | `crates/loopd/src/scheduler.rs` heartbeat | After `run_turn` / `finish_turn`, `last_heartbeat` and `TickWork::Heartbeat` are set only when `outcome.is_ok()`. Task 06 and the reference set them after the turn regardless. A failed pulse is due again on the next 1 s tick and would hammer the shared server. Given tests all succeed via the fake server. | Fix before land: set both after the turn, as the task lists. |
| 2 | process | implementer (task 01) | Two `stopped` rows: first on `master` before `m5d2` existed; second on a greedy `sed` that turned 1512 into 1022. The work was correct. Plan fix `2a48b7f` (T50/T59); resume committed T1. | T50; do not sum `test result:` |
| 3 | process | implementer (task 06) | Two sessions hit OpenCode `Context size has been exceeded` with no commit: first by reading the whole implementer log; second by listing `tests/` and reading supporting crate modules before the `cp`. | T62, T63; plan fix `4dd3b87` |
| 4 | process | implementer (tasks 04, 05, 06, 10) | Extra gate after a first-gate pass on 04 (I18). First-gate clippy/USAGE fails on 05, 06, 10, fixed and rerun. Task 04's commit also duplicated 03's `done` row. | I18 seen again; duplicate row removed here |
Device check, spec 7 for M5d-2, is not in this review. It needs straylight, slot 0 live / slot 2
background, slot 1 left busy, a private `$HOME`, and production daemons untouched: a heartbeat
turn on the background slot, then a following main-slot turn whose `cache_n` is near its previous
total; a `jobs/tier.toml` for the check that promotes only a trusted note.
What was good: ten tasks ended cleanly after the 01 and 06 unblocks; given tests unedited; every
review-focus case in the plan is covered by a given test and holds; no new crate; `jobs-invalid`
refuses start; heartbeat prompt failures log once.
Open items carried forward: finding 1 above; the M5c-2 items still open (rift at `reasoning: low`,
`has_secret`, Mattermost `a.b`/`a_b`, secret-session Honcho queries). Known M5d-2 limits, left as
the README lists them: no Mattermost delivery of heartbeat or cron answers; DOM and DOW are AND;
tool-summary inference holds the session; job files that go invalid after start are skipped that
tick; identity rows in `Tiers` list with 0 hits.