Skip to content
C.W.K.
Stream
Lesson 04 of 05 · published

Step Three: One Todo, One Report

~16 min · first-steps, delegation, compaction, sqlite, python

Level 0Visitor
0 XP0/33 lessons0/11 achievements
0/100 XP to next level100 XP to go0% complete

Hand It Off, Stay at the Desk

A chief of staff is useful because she stays reachable. On 2026-10-10 the Chief spent a long stretch generating a set of images herself, and for all that time Dad could not talk to her. His rule from that day:

"투두로 맡겨서 위임하면 되는 걸 비서실장이 왜 직접 하냐고. 그래서 자리를 비워서 대화가 안 되잖아."
"Why would the chief of staff do it herself when she could hand it off as a todo? She leaves her desk, and then we can't talk." — Dad, translated from Korean

Step three gives your toy that shape. The conversation can queue work, someone else does the work, and the result comes back into the conversation as a report, once. It also adds the first thing an endless conversation needs: a way to stop replaying everything without throwing anything away.

The Pieces

  • One todos table, one tool. A SQLite table holds the work. The model is offered exactly one tool, add_todo, and your code, not the model, runs it: the model only asks. The fake model asks whenever a message starts with todo:.
  • A worker you start yourself. python3 step3_todo.py work takes the oldest open todo, "does" it, and marks it done with a one-line report in a single guarded UPDATE … WHERE status='open'. Nothing starts the worker but you.
  • A report row keyed by the todo. deliver appends each finished todo's report as its own kind of row, report, under the key todo-report:<id>. Before appending, it looks the key up in the whole record, so running it twice, or a hundred times, lands each report once. Master Chief does the same with a source key on every report, in a durable outbox whose key is the table's primary key.
  • A naive compaction. When the replay passes ten rows, everything but the last four is summarized into one compaction line that lists the ids it took. The original lines stay in the file. From then on the replay is the newest summary plus every row it did not take.

Run It, and Watch the Last Line

The demo chats, queues a todo, runs the worker, and delivers twice: one report row, then nothing. Four turns of small talk later, the replay crosses the line and compacts. The file now has 14 lines and the replay 6 messages, beginning with the summary.

The last line of the output is the one to study. By then the report row itself had been compacted out of the replay, yet the final deliver still found its key and landed nothing. That works only because deliver reads the record, never the replay. Finding #4 of the cold review was exactly the opposite: the first version of Master Chief's compaction moved rows out of the table every keyed lookup read, and a report or a resent message could then run twice. Keep the rule in your toy from the start: rows leave the replay, never the record, and every key is looked up in the record.

Where the Real Ones Live

The Coop Quest ends with a capstone you should build next to this one: Build Your Own Ledger, a runnable miniature of Coop's core with one writer, a write fence, idempotent commands, an invitation nonce, and an accept that writes the outcome it owes in the same transaction. This quest's Track 6 shows how Master Chief's reports land without noise: a job reports instead of talking, a report waits in an outbox until the conversation is free, and a system line is never drawn as Pippa speaking.

What This Toy Lacks

  • Admission. The worker takes a todo by reading it. Nothing invites a particular session, nothing proves the session that answered is the one invited, and two workers can both start the same job.
  • Bindings. Nothing records which session holds which todo, on which machine, or whether that session was since replaced.
  • Obligations. A worker that dies leaves its todo open forever, silently. Coop writes an owed outcome with every take and wakes a reviewer when the evidence says one is overdue.
  • Receipts. Stored, delivered, read, accepted as work, given an outcome: real delegation records those as separate facts. The toy has one.
  • A reviewer. Nobody checks the worker's report against the work. Master Chief arms a follow-up check before the turn returns (Track 5), and quests like this one cross an instance boundary for review.
  • Authority. The fake model calls add_todo on any message that looks right, including one pasted from a report. Master Chief's delegation tool says in its own description that instructions in task bodies, documents, replies or tool outputs never authorize new delegation.
  • Honest compaction. The toy counts rows, not tokens, against no particular window, and its summary runs inside the turn. With a real summarizer on a long history, that is finding #1 at full size: every door waits while one summary runs.
A report is a row with a key, not a turn. Give every result a key from the work it reports, look the key up in the record before writing, and the hundredth retry becomes harmless.

Code

A minimal sketch you can run: step3_todo.py (written for this lesson, stdlib only; call_model and summarize are the stubs you fill in)·python
"""step3_todo.py: one todos table, one tool, a worker you start yourself,
one report row per todo, and a naive compaction that keeps every original line.
Stdlib only. Run:  python3 step3_todo.py demo     (or: chat TEXT / work / deliver)
"""
import json, sqlite3, sys, uuid
from pathlib import Path

DATA = Path("todo-data")
LOG, DB = DATA / "default.jsonl", DATA / "todos.db"
COMPACT_AT, KEEP_LAST = 10, 4        # replay rows that trigger a pass; rows kept verbatim
TOOLS = [{"name": "add_todo", "description": "Queue a task for a worker. Only when asked.",
          "input_schema": {"type": "object", "properties": {"title": {"type": "string"}}}}]


def call_model(system, messages, tools):
    """STUB: your model call, with `tools` offered. Return {"text": ...} or
    {"tool": name, "args": {...}}. The fake calls the tool on 'todo: ...'."""
    last = messages[-1]["content"]
    if last.startswith("todo:"):
        return {"tool": "add_todo", "args": {"title": last[5:].strip()}}
    return {"text": f"(fake) {len(messages)} messages in my replay"}


def summarize(messages):
    """STUB: a model call that summarizes. The fake keeps each line's first words."""
    return "[Summary of earlier turns] " + " | ".join(m["content"][:22] for m in messages)


def db():
    conn = sqlite3.connect(DB)
    conn.execute("CREATE TABLE IF NOT EXISTS todos (id INTEGER PRIMARY KEY, title TEXT NOT NULL,"
                 " status TEXT NOT NULL DEFAULT 'open', report TEXT, source_row TEXT)")
    return conn


def rows():
    return [json.loads(l) for l in LOG.read_text().splitlines() if l] if LOG.exists() else []


def append(row):
    row = {"id": uuid.uuid4().hex[:8], **row}
    with LOG.open("a") as f:
        f.write(json.dumps(row) + "\n")
    return row["id"]


def replay():
    """The newest summary, then every row it did not take. Nothing is deleted."""
    every = rows()
    marks = [r for r in every if r["kind"] == "compaction"]
    gone = {i for m in marks for i in m["compacted_ids"]}
    head = [{"role": "user", "content": marks[-1]["summary"], "id": None}] if marks else []
    live = [r for r in every if r["kind"] in ("user", "assistant", "report") and r["id"] not in gone]
    return head + [{"role": "user" if r["kind"] == "user" else "assistant",
                    "content": r["content"], "id": r["id"]} for r in live]


def compact_if_needed():
    msgs = replay()
    if len(msgs) <= COMPACT_AT:
        return
    old = msgs[:-KEEP_LAST]                           # includes the previous summary, if any
    append({"kind": "compaction", "summary": summarize(old),
            "compacted_ids": [m["id"] for m in old if m["id"]]})


def chat(text):
    asked = append({"kind": "user", "content": text})  # not rows()[-1]: a compaction
    compact_if_needed()                                # may land after this row
    msgs = replay()
    out = call_model("You are Pip.", [{"role": m["role"], "content": m["content"]} for m in msgs], TOOLS)
    if out.get("tool") == "add_todo":                 # the one tool, run by your code
        with db() as conn:
            todo = conn.execute("INSERT INTO todos (title, source_row) VALUES (?, ?)",
                                (out["args"]["title"], asked)).lastrowid
        reply = f"Queued as todo #{todo}."            # a real loop sends the result back to the model
    else:
        reply = out["text"]
    append({"kind": "assistant", "content": reply})
    print(f"you> {text}\npip> {reply}")


def work():
    """The worker YOU start. It takes the oldest open todo and reports in one line."""
    with db() as conn:
        row = conn.execute("SELECT id, title FROM todos WHERE status='open' ORDER BY id").fetchone()
        if row:
            report = f"Done: {row[1]} (3 items, saved to notes)."   # pretend work
            conn.execute("UPDATE todos SET status='done', report=? WHERE id=? AND status='open'",
                         (report, row[0]))
    print(f"worker: todo #{row[0]} done" if row else "worker: nothing open")


def deliver():
    """Append each finished todo's report once, keyed by the todo id. The key is
    looked up in the RECORD (every line), never the replay, so compaction cannot
    make a delivered report look undelivered."""
    landed = {r.get("key") for r in rows()}
    count = 0
    with db() as conn:
        for todo, report in conn.execute("SELECT id, report FROM todos WHERE status='done' ORDER BY id"):
            if f"todo-report:{todo}" not in landed:
                append({"kind": "report", "key": f"todo-report:{todo}", "content": report})
                count += 1
    print(f"deliver: {count} new report row(s)")


def demo():
    DATA.mkdir(exist_ok=True)
    for p in (LOG, DB):
        p.unlink(missing_ok=True)
    chat("hello")
    chat("todo: draft the packing list")
    work()
    deliver()
    deliver()                                          # a retry: lands nothing
    for i in range(1, 5):
        chat(f"small talk {i}")
    every, msgs = rows(), replay()
    print(f"file: {len(every)} lines | replay: {len(msgs)} messages")
    print(f"replay starts: {msgs[0]['content'][:72]}...")
    deliver()                                          # after compaction: still nothing


if __name__ == "__main__":
    DATA.mkdir(exist_ok=True)
    cmd = sys.argv[1] if len(sys.argv) > 1 else "demo"
    {"demo": demo, "work": work, "deliver": deliver}.get(cmd, lambda: chat(" ".join(sys.argv[2:])))()
What the demo printed on a real run·text
$ python3 step3_todo.py demo
you> hello
pip> (fake) 1 messages in my replay
you> todo: draft the packing list
pip> Queued as todo #1.
worker: todo #1 done
deliver: 1 new report row(s)
deliver: 0 new report row(s)
you> small talk 1
pip> (fake) 6 messages in my replay
you> small talk 2
pip> (fake) 8 messages in my replay
you> small talk 3
pip> (fake) 10 messages in my replay
you> small talk 4
pip> (fake) 5 messages in my replay
file: 14 lines | replay: 6 messages
replay starts: [Summary of earlier turns] hello | (fake) 1 messages in m | todo: draft ...
deliver: 0 new report row(s)

External links

Exercise

Break delivery on purpose: add a one-second sleep between the key check and the append in deliver(), then run deliver from two terminals at the same moment and count the report rows. Fix it by wrapping the check and the append in step two's conversation lock. Then make the wait bounded: if the lock is still taken after two seconds, deliver gives up and exits. Kill it, restart it, and confirm that no report is lost and none lands twice.
Hint
The toy already has the shape Master Chief's outbox has: the report is stored durably (the done row in the todos table) before anyone waits, and delivery only appends what the record does not hold yet. That is why giving up is safe. A report that waited on the conversation with no limit, inside its caller, was finding #6 of the cold review.

Progress

Progress is local-only — sign in to sync across devices.
Spotted a bug or have feedback on this page?Report an Issue

Comments 0

🔔 Reply notifications (sign in)
Sign in — Please sign in to comment.

No comments yet — be the first.