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 <noreply@x.ai>
This commit is contained in:
@@ -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<n>/NN-name.md`. Read it fully. Do that task and
|
1. The owner gives you one task file, `docs/plans/M<n>/NN-name.md`. Read it fully. Do that task and
|
||||||
nothing else. Do not start the next task.
|
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.
|
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
|
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
|
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
|
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.
|
- Report every problem you find, not only the first.
|
||||||
- Log every attempt, including one you abandon.
|
- Log every attempt, including one you abandon.
|
||||||
- Never end a turn by describing what you are about to do. Do it, then report.
|
- 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
|
- A refused or failed tool call is not a reason to end your turn. Read the error, then continue
|
||||||
with another tool call.
|
with another tool call.
|
||||||
- Fill `todo!()` bodies one at a time, and run `cargo check` after each.
|
- Fill `todo!()` bodies one at a time, and run `cargo check` after each.
|
||||||
|
|||||||
@@ -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. |
|
| 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. |
|
| 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. |
|
| 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
|
## What worked and should be kept
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,14 @@ is empty; otherwise stop. Never switch branch.
|
|||||||
**Commit subject:** `loopd: heartbeat tick on the background slot`
|
**Commit subject:** `loopd: heartbeat tick on the background slot`
|
||||||
**Log row:** the Task cell is `M5d2/06-loopd-scheduler-heartbeat`.
|
**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
|
## Goal
|
||||||
|
|
||||||
A `Scheduler` whose `tick(&Context, now)` runs the standing `heartbeat` session when
|
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`.
|
`crates/loopd/src/main.rs`, `docs/implementer-log.md`.
|
||||||
- Do not touch: any other file.
|
- Do not touch: any other file.
|
||||||
|
|
||||||
**Read only what the step names.** Step 1 is the `cp`. Do not open `docs/implementer-log.md`,
|
At step 7, read only the first 12 lines of `docs/implementer-log.md`. The rest of that file
|
||||||
`docs/runbook.md`, or `crates/loopd/src/{turn,session,compact,baseline,config}.rs` to start.
|
fills the session (~360 kB).
|
||||||
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).
|
|
||||||
|
|
||||||
## Facts you need
|
## 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.
|
That sleep is production only. Tests never sleep.
|
||||||
- **`channel.rs` stays ≤ 500 lines.** `hold` is about 15 lines.
|
- **`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.
|
- The items the edits use. This table is complete.
|
||||||
|
|
||||||
| Item | What it does |
|
| Item | Signature |
|
||||||
|---|---|
|
|---|---|
|
||||||
| `Session::slot` / `create` / `open` | standing session |
|
| `Session::open` | `open(home: &Path, id: SessionId) -> Result<Session, SessionError>` |
|
||||||
| `run_turn` / `finish_turn` | the pulse |
|
| `Session::create` | `create(home, id, baseline, slot: u32) -> Result<Session, SessionError>` |
|
||||||
| `Baseline::assemble` | new session |
|
| `SessionError::NotFound` | `_` |
|
||||||
| `jobs::close_leftovers` / `live_session` / `append` | leftovers |
|
| `Baseline::assemble` | `assemble(&ctx.cfg, &ctx.registry) -> Result<Baseline, _>` |
|
||||||
| `Timestamp::unix_millis` / `to_rfc3339` | due and the time line |
|
| `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<Vec<Job>, JobsError>` |
|
||||||
|
| `jobs::live_session` | `live_session(ledger, name) -> Result<Option<SessionId>, _>` |
|
||||||
|
| `jobs::append` | `append(ledger, &Run { time, name, session, status })` |
|
||||||
|
| `RunStatus::Failed` | leftover close |
|
||||||
|
| `Timestamp` | `unix_millis() -> u64`, `to_rfc3339() -> String`, `now()` |
|
||||||
|
|
||||||
## The edits, exactly
|
## The edits, exactly
|
||||||
|
|
||||||
@@ -134,8 +149,9 @@ pub struct Busy;
|
|||||||
|
|
||||||
### `crates/loopd/src/scheduler.rs`
|
### `crates/loopd/src/scheduler.rs`
|
||||||
|
|
||||||
Write this module. Fill `tick`. Cron jobs stay a comment until task 07: after heartbeat, do
|
Write this module. `tick` is already glue. Fill `heartbeat` then `close_idle_leftovers`,
|
||||||
not iterate job files yet.
|
`cargo check -p loopd` after each (T25). Do not iterate job files (task 07). Do not add
|
||||||
|
`run_jobs`.
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
//! Isolated heartbeat (and, from task 07, cron) turns (M5d spec, section 4).
|
//! 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<TickWork> {
|
pub fn tick(&mut self, ctx: &Context, now: Timestamp) -> Vec<TickWork> {
|
||||||
let _ = (ctx, now);
|
let mut work = Vec::new();
|
||||||
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<TickWork>) {
|
||||||
|
let _ = (ctx, now, work);
|
||||||
|
todo!()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
|
||||||
|
|
||||||
Replace the `tick` body in this task. `leftovers_closed` is set true after the first leftover
|
fn close_idle_leftovers(ctx: &Context, now: Timestamp) {
|
||||||
pass so it runs once.
|
let _ = (ctx, now);
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
## Steps
|
## 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
|
- [ ] **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`,
|
target `scheduler_heartbeat` does not compile (`E0432` unresolved `loopd::scheduler`,
|
||||||
`E0599` no method `hold`). Every other target compiles.
|
`E0599` no method `hold`). Every other target compiles.
|
||||||
- [ ] **3. The edits.** C1, C2, `scheduler.rs`, `pub mod scheduler;`, main thread. Fill
|
- [ ] **3. The edits.** C1, C2, `scheduler.rs` (fill `heartbeat` then `close_idle_leftovers`,
|
||||||
`tick`. `cargo check -p loopd` prints no error and no warning.
|
`cargo check -p loopd` after each), `pub mod scheduler;`, main thread. `cargo check -p loopd`
|
||||||
`wc -l crates/loopd/src/scheduler.rs` and `wc -l crates/loopd/src/channel.rs` each ≤ 500.
|
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
|
- [ ] **4. The tests.** `cargo test -p loopd --test scheduler_heartbeat --test channel --test
|
||||||
session_slot`: every test passes, 6 in `scheduler_heartbeat`.
|
session_slot`: every test passes, 6 in `scheduler_heartbeat`.
|
||||||
- [ ] **5. The runbook.**
|
- [ ] **5. The runbook.**
|
||||||
|
|||||||
@@ -5,6 +5,10 @@ is empty; otherwise stop. Never switch branch.
|
|||||||
**Commit subject:** `loopd: cron tick, new session per run`
|
**Commit subject:** `loopd: cron tick, new session per run`
|
||||||
**Log row:** the Task cell is `M5d2/07-loopd-scheduler-cron`.
|
**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
|
## Goal
|
||||||
|
|
||||||
After the heartbeat work, `tick` loads `{home}/jobs/*.toml` and runs each job whose schedule
|
After the heartbeat work, `tick` loads `{home}/jobs/*.toml` and runs each job whose schedule
|
||||||
|
|||||||
+1
-1
@@ -94,7 +94,7 @@ for task in "$plan"/[0-9][0-9]-*.md; do
|
|||||||
number=$(echo "$name" | cut -c1-2)
|
number=$(echo "$name" | cut -c1-2)
|
||||||
[ "$number" -ge "$first" ] || continue
|
[ "$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"
|
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
|
if [ "${DRY_RUN:-0}" = 1 ]; then
|
||||||
echo "opencode run --agent implement -m $model ${OPENCODE_FLAGS:-} --title $milestone/$name \"$prompt\""
|
echo "opencode run --agent implement -m $model ${OPENCODE_FLAGS:-} --title $milestone/$name \"$prompt\""
|
||||||
|
|||||||
Reference in New Issue
Block a user