From 4dd3b87db332203bccafd68623e5059197aa8a45 Mon Sep 17 00:00:00 2001 From: Kyle Isom Date: Sat, 3 Oct 2026 16:04:38 -0700 Subject: [PATCH] docs: M5d-2 task 06: cp first, tick is glue The second 06 session listed tests/, read eight loopd modules and support/mod.rs, grepped bxcore, then hit Context size exceeded with no commit. The cp is now the first action; tick is glue over two todo!() bodies; run-plan tells the session to run step 1 before reading crate source. Co-Authored-By: Grok 4.6 --- AGENTS.md | 4 ++ docs/implementer-lessons.md | 3 +- .../M5d2/06-loopd-scheduler-heartbeat.md | 71 +++++++++++++------ docs/plans/M5d2/07-loopd-scheduler-cron.md | 4 ++ tools/run-plan.sh | 2 +- 5 files changed, 61 insertions(+), 23 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 2ba3b14..bf6b608 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -8,6 +8,8 @@ Unix sockets. You are implementing it one task at a time. 1. The owner gives you one task file, `docs/plans/M/NN-name.md`. Read it fully. Do that task and nothing else. Do not start the next task. 2. Do the steps in order. Where a step shows a command and its expected output, run it and compare. + The first step is almost always a `cp`. Run that command before you Read any crate source. + Do not `ls` a tests directory. Do not read `tests/support/`. 3. Do not read `docs/design.md` or other plans unless the task tells you to. The task file quotes what you need. Do not read `docs/implementer-log.md` until the log-and-commit step, and then only the first 12 lines (the title through the header row). The rest of that file will fill @@ -72,6 +74,8 @@ These come from defects found in review. The evidence is in `docs/implementer-le - Report every problem you find, not only the first. - Log every attempt, including one you abandon. - Never end a turn by describing what you are about to do. Do it, then report. +- Run the task's step 1 (the `cp`) before you Read crate source. Do not `ls` a tests directory. + Do not read `tests/support/`. The facts table is the API. - A refused or failed tool call is not a reason to end your turn. Read the error, then continue with another tool call. - Fill `todo!()` bodies one at a time, and run `cargo check` after each. diff --git a/docs/implementer-lessons.md b/docs/implementer-lessons.md index 45032c5..4ae94b7 100644 --- a/docs/implementer-lessons.md +++ b/docs/implementer-lessons.md @@ -101,7 +101,8 @@ How it is used: | T59 | After `gate: ok`, the task is done counting. Do not tell the implementer to run `cargo test --workspace` (or the gate) again only to compare a number. AGENTS.md already forbids a second gate "only to count"; say the same of the workspace test run. | M5c-2: all twelve sessions ran `cargo test --workspace` after a passing gate to rediscover the total, even with T50's wording in the task. | | T60 | The driver must not truncate a task's run log, and must not start a task whose commit and `done` row are already in HEAD. A power-loss resume continues the existing log file; it does not open it for write from offset zero, and it skips a task that is already committed. | M5c-2 task 05: `.state/runs/M5c2/05-memoryd-honcho-readers.log` is four `=== …` resume sections after HEAD was at task 12; the original transcript is gone. Commit `b370c60` was already complete. | | 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. | +| 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. | ## What worked and should be kept diff --git a/docs/plans/M5d2/06-loopd-scheduler-heartbeat.md b/docs/plans/M5d2/06-loopd-scheduler-heartbeat.md index a442bb9..527fe77 100644 --- a/docs/plans/M5d2/06-loopd-scheduler-heartbeat.md +++ b/docs/plans/M5d2/06-loopd-scheduler-heartbeat.md @@ -5,6 +5,14 @@ is empty; otherwise stop. Never switch branch. **Commit subject:** `loopd: heartbeat tick on the background slot` **Log row:** the Task cell is `M5d2/06-loopd-scheduler-heartbeat`. +**First action.** Run this before any Read of crate source. Do not `ls` `crates/loopd/tests/`. +Do not Read `tests/support/`, `session.rs`, `turn.rs`, `compact.rs`, `config.rs`, `jobs.rs`, +`baseline.rs`, `bxcore`, or `docs/implementer-log.md`. The signatures below are complete. + +```sh +cp docs/plans/M5d2/files/crates/loopd/tests/scheduler_heartbeat.rs crates/loopd/tests/ +``` + ## Goal A `Scheduler` whose `tick(&Context, now)` runs the standing `heartbeat` session when @@ -38,11 +46,8 @@ Tests call `tick(ctx, now)` with injected time. No sleeps (T27). `crates/loopd/src/main.rs`, `docs/implementer-log.md`. - Do not touch: any other file. -**Read only what the step names.** Step 1 is the `cp`. Do not open `docs/implementer-log.md`, -`docs/runbook.md`, or `crates/loopd/src/{turn,session,compact,baseline,config}.rs` to start. -The facts table is complete. At step 7, read only the first 12 lines of -`docs/implementer-log.md` (title through the header row) and insert the new row immediately -under the header. The rest of that file fills the session (~360 kB). +At step 7, read only the first 12 lines of `docs/implementer-log.md`. The rest of that file +fills the session (~360 kB). ## Facts you need @@ -93,15 +98,25 @@ under the header. The rest of that file fills the session (~360 kB). That sleep is production only. Tests never sleep. - **`channel.rs` stays ≤ 500 lines.** `hold` is about 15 lines. +- **Uses.** The skeleton compiles with only `Context` and `Timestamp`. When filling `heartbeat`, + add `use std::fs`, `Baseline`, `compact`, `Session`/`SessionError`, `Runtime`/`run_turn`, + `TurnEvent`. When filling leftovers, add `jobs::{self, Run, RunStatus}`. - The items the edits use. This table is complete. - | Item | What it does | + | Item | Signature | |---|---| - | `Session::slot` / `create` / `open` | standing session | - | `run_turn` / `finish_turn` | the pulse | - | `Baseline::assemble` | new session | - | `jobs::close_leftovers` / `live_session` / `append` | leftovers | - | `Timestamp::unix_millis` / `to_rfc3339` | due and the time line | + | `Session::open` | `open(home: &Path, id: SessionId) -> Result` | + | `Session::create` | `create(home, id, baseline, slot: u32) -> Result` | + | `SessionError::NotFound` | `_` | + | `Baseline::assemble` | `assemble(&ctx.cfg, &ctx.registry) -> Result` | + | `Runtime` | `{ cfg: &ctx.cfg, client: &ctx.client, port: &*ctx.port, registry: &ctx.registry }` | + | `run_turn` | `run_turn(&mut session, &runtime, &text, None, &mut emit)` | + | `compact::finish_turn` | `finish_turn(&mut session, &runtime, &*ctx.memory, &text, None, &mut emit, outcome)` | + | `jobs::load` | `load(dir: &Path) -> Result, JobsError>` | + | `jobs::live_session` | `live_session(ledger, name) -> Result, _>` | + | `jobs::append` | `append(ledger, &Run { time, name, session, status })` | + | `RunStatus::Failed` | leftover close | + | `Timestamp` | `unix_millis() -> u64`, `to_rfc3339() -> String`, `now()` | ## The edits, exactly @@ -134,8 +149,9 @@ pub struct Busy; ### `crates/loopd/src/scheduler.rs` -Write this module. Fill `tick`. Cron jobs stay a comment until task 07: after heartbeat, do -not iterate job files yet. +Write this module. `tick` is already glue. Fill `heartbeat` then `close_idle_leftovers`, +`cargo check -p loopd` after each (T25). Do not iterate job files (task 07). Do not add +`run_jobs`. ```rust //! Isolated heartbeat (and, from task 07, cron) turns (M5d spec, section 4). @@ -170,14 +186,26 @@ impl Scheduler { } pub fn tick(&mut self, ctx: &Context, now: Timestamp) -> Vec { - let _ = (ctx, now); - Vec::new() + let mut work = Vec::new(); + if !self.leftovers_closed { + close_idle_leftovers(ctx, now); + self.leftovers_closed = true; + } + self.heartbeat(ctx, now, &mut work); + work + } + + fn heartbeat(&mut self, ctx: &Context, now: Timestamp, work: &mut Vec) { + let _ = (ctx, now, work); + todo!() } } -``` -Replace the `tick` body in this task. `leftovers_closed` is set true after the first leftover -pass so it runs once. +fn close_idle_leftovers(ctx: &Context, now: Timestamp) { + let _ = (ctx, now); + todo!() +} +``` ## Steps @@ -190,9 +218,10 @@ pass so it runs once. - [ ] **2. See it fail.** `cargo build --tests --keep-going -p loopd`, timeout 600000 ms: test target `scheduler_heartbeat` does not compile (`E0432` unresolved `loopd::scheduler`, `E0599` no method `hold`). Every other target compiles. -- [ ] **3. The edits.** C1, C2, `scheduler.rs`, `pub mod scheduler;`, main thread. Fill - `tick`. `cargo check -p loopd` prints no error and no warning. - `wc -l crates/loopd/src/scheduler.rs` and `wc -l crates/loopd/src/channel.rs` each ≤ 500. +- [ ] **3. The edits.** C1, C2, `scheduler.rs` (fill `heartbeat` then `close_idle_leftovers`, + `cargo check -p loopd` after each), `pub mod scheduler;`, main thread. `cargo check -p loopd` + prints no error and no warning. `wc -l crates/loopd/src/scheduler.rs` and + `wc -l crates/loopd/src/channel.rs` each ≤ 500. - [ ] **4. The tests.** `cargo test -p loopd --test scheduler_heartbeat --test channel --test session_slot`: every test passes, 6 in `scheduler_heartbeat`. - [ ] **5. The runbook.** diff --git a/docs/plans/M5d2/07-loopd-scheduler-cron.md b/docs/plans/M5d2/07-loopd-scheduler-cron.md index 7fb7999..e5d5e47 100644 --- a/docs/plans/M5d2/07-loopd-scheduler-cron.md +++ b/docs/plans/M5d2/07-loopd-scheduler-cron.md @@ -5,6 +5,10 @@ is empty; otherwise stop. Never switch branch. **Commit subject:** `loopd: cron tick, new session per run` **Log row:** the Task cell is `M5d2/07-loopd-scheduler-cron`. +**First action.** Copy the given test before any other Read of crate source. Do not `ls` +`crates/loopd/tests/`. Do not Read `tests/support/` or `docs/implementer-log.md` (except the +first 12 lines at the log step). + ## Goal After the heartbeat work, `tick` loads `{home}/jobs/*.toml` and runs each job whose schedule diff --git a/tools/run-plan.sh b/tools/run-plan.sh index dcac1df..53a6d4a 100755 --- a/tools/run-plan.sh +++ b/tools/run-plan.sh @@ -94,7 +94,7 @@ for task in "$plan"/[0-9][0-9]-*.md; do number=$(echo "$name" | cut -c1-2) [ "$number" -ge "$first" ] || continue row="Your log row's Task cell is \`$milestone/$name\`, and you are $agent: write $agent in its Model column, and end the commit message with this line, exactly: $trailer" - prompt="Read \`$task\` and do exactly that task. $row" + prompt="Read \`$task\` and do exactly that task. Do its steps in order. Run step 1 (usually a cp) before you Read any crate source, tests/support, or docs/implementer-log.md. $row" if [ "${DRY_RUN:-0}" = 1 ]; then echo "opencode run --agent implement -m $model ${OPENCODE_FLAGS:-} --title $milestone/$name \"$prompt\""