Student edition · 90 minutes of dedicated work · 2026-09-09
Runs on Google Colab as it ships today, or on any local Python 3.12+ kernel.
This is one of two practical units for Chapter 19. Unit A constructs and connects the mechanism; Unit B investigates a controlled failure, repairs it and transfers the invariant. Each is a complete ninety-minute session, with its own setup and required conceptual introductions. Basic Python variables, conditions, loops, functions, lists and dictionaries are the starting knowledge. Libraries and specialized concepts used here are introduced below before the main task.
By the end you should be able to:
- Explain the chapter's mechanism using a prediction and an observed intermediate result.
- Construct restore from a separate compatible backup: verify integrity and schema, prepare a fresh epoch, clear old ownership, revoke approvals, pause, publish the authority marker and copy into the same database inode.
- Solve validate every member of a restore manifest using changed inputs and an independent expectation.
- Retain your implementation, failed/corrected observations, causal explanation and limits.
| Minutes | Dedicated activity | Evidence you produce |
|---|---|---|
| 0–5 | State the problem and make a prediction | Initial prediction in your own words |
| 5–25 | Foundations and library examples | Values, explanations, revised predictions |
| 25–35 | Trace setup and the main interface | Input → learner function → observation |
| 35–60 | Construct and connect | Source, visible checks and runtime evidence |
| 60–80 | Implement and challenge the transfer task | Function and a new counterexample |
| 80–90 | Retrieve, explain and save | Exit ticket and retained submission |
Scroll horizontally to view columns.
Installation is preparation time. These are planning estimates, not measured completion times. Use the reference primers when a term is unfamiliar; in Unit B retrieve an explanation before re-reading it. Run All checks that the artifact executes. Unfinished student functions deliberately produce NEEDS_WORK. Keep your first attempt before opening answers.
This notebook belongs to the construction edition. Its supplied teaching runtime is embedded, so it can run without the textbook or another notebook. Where code uses REFERENCE_LESSON, that is the frozen runtime exercise identifier; the reader-facing chapter and saved unit identifiers use the current edition. Building against a supplied runtime is not proof that you have constructed all of its dependencies.
Run the self-contained setup
Open this notebook in Google Colab with the badge above, or use a local
Python 3.12+ Jupyter kernel, with Pydantic 2. If needed, run
%pip install "pydantic==2.13.4" once in a separate cell and restart the kernel.
Package installation needs internet; the lesson itself needs no repository download, API key
or prior notebook. The closing extension can use an OpenAI key from Colab's Secrets pane to put a
live model behind the same tools; without a key it replays a recorded transcript and says so.
The collapsed cell contains 90 frozen teaching files. Base85 represents compressed bytes
as text; zlib decompresses them; SHA-256 checks that the decoded files match this edition.
These are supplied packaging operations, not learner algorithms. tempfile creates an isolated
working copy; Path handles file locations; sys.path tells Python where the supplied modules
live; a .pth file, the mechanism an editable install uses, tells reviewed local subprocesses the same. The code is available for inspection below and performs no package installation itself.
The subsequent lesson teaches the libraries used by the mechanisms you will implement.
Run setup on every fresh kernel. It writes scratch runtime files separately from your retained
practical-work/ch19-a folder. Rerunning setup restores the frozen support files and keeps
your saved work. Restarting a kernel clears variables, not saved submission files. Source basis:
Sovereign Agent 444c5f6. Some tasks use reviewed local subprocesses; they are not an OS sandbox.
Supplied offline setup and teaching files
The complete supplied setup cell is in the downloadable notebook. Run it there before attempting the cells below. This web edition omits only that compressed setup payload.
Commit to a prediction before the examples
prediction_notes = {
"prediction": "Write the expected behavior before running the worked example.",
"reason": "Name the input and rule behind that prediction.",
"falsifier": "Name an observation that would prove the explanation wrong.",
"revision": "After execution, explain what changed in your understanding.",
}Reading the Python vocabulary used in this notebook
You need basic assignments, if, loops, functions, lists and dictionaries. The less familiar
features used by the supplied code are introduced here. A library is reusable code that
Python can import. The standard library ships with Python; Pydantic is an additional package.
An import makes a name available, but does not mean that you have completed the exercise.
JSON is text for exchanging structured values. A Python dictionary is an in-memory object;
the JSON representation is a string. Use json.dumps to encode and json.loads to decode.
Decoding proves that text has valid JSON syntax, not that its fields match our business contract.
Predict which of the following two decoded objects could describe a stock count.
import json
intro_data = {"sku": "MANGO", "count": 3}
intro_text = json.dumps(intro_data, sort_keys=True)
print(type(intro_data).__name__, type(intro_text).__name__, intro_text)
print(json.loads(intro_text))
print("Also valid JSON:", json.loads('["not", "a", "stock", "record"]'))
assert json.loads(intro_text) == intro_dataThe first result is a dictionary; the second is a list. Before indexing a decoded object,
check the shape that your function promises to accept. An exception interrupts the normal
path. raise ValueError(...) refuses an invalid value; try/except lets a caller inspect that
expected refusal. Catch the expected class, rather than turning every programming error into
apparent success. finally runs cleanup even when an earlier operation raises.
An annotation, such as count: int, documents the expected type. It does not by itself
enforce the type at runtime. A class defines a kind of object; an instance holds one object's
data. @dataclass asks Python to generate routine construction and comparison methods from
annotated fields. frozen=True prevents ordinary reassignment of the instance's fields; it does
not make every object nested inside those fields immutable. A method is a function attached
to a class; self refers to the instance receiving the call.
from dataclasses import dataclass
@dataclass(frozen=True)
class IntroObservation:
operation: str
count: int
intro_observation = IntroObservation("count-mango", 3)
print(intro_observation.operation, intro_observation.count)
assert intro_observation == IntroObservation("count-mango", 3)A callback is a function passed to another function. This is how the classroom harness
invokes your implementation. The argument candidate below is a function object; parentheses
perform the call. Predict the two answers before execution, then trace the result to the callback.
def intro_apply(candidate, value):
return {"input": value, "observed": candidate(value)}
def intro_double(value):
return value * 2
print(intro_apply(intro_double, 3))
print(intro_apply(lambda value: value + 2, 3))
assert intro_apply(intro_double, 3)["observed"] == 6lambda value: value + 2 is a small anonymous function. A closure is a function that retains
access to values from its surrounding scope. It can bind a tool to a shop snapshot. A shallow
copy duplicates only the outer container; copy.deepcopy also copies nested containers used
in these fixtures. A set stores distinct values; required <= allowed asks whether every
required item is allowed. frozenset is the corresponding immutable set. A tuple groups ordered
values; (value,) is a one-item tuple, including the comma.
Paths and cleanup. Path represents a filesystem location. path / "file.json" constructs
a child path; read_text and write_text read and write text. A context manager, used with
with, manages entry and exit. A temporary-directory context removes its contents on exit.
Save your submission outside temporary runtime directories. Reopening a file is different from
reusing a Python variable: the former tests retained bytes, while the latter only tests this kernel.
from pathlib import Path
from tempfile import TemporaryDirectory
with TemporaryDirectory() as intro_folder:
intro_path = Path(intro_folder) / "observation.json"
intro_path.write_text(json.dumps(intro_data), encoding="utf-8")
intro_reopened = json.loads(intro_path.read_text(encoding="utf-8"))
assert intro_reopened == intro_data
print("Read from a file:", intro_reopened)Retrieval check: explain JSON versus a dictionary, annotation versus validation, class versus instance, and defining a callback versus invoking it. Change the callback above so an incorrect implementation visibly changes the observed output. This distinction will matter when grading your connected work. Reference: Python's JSON, dataclasses, and pathlib documentation.
Pydantic: turn an input dictionary into a checked object
Pydantic is an additional Python library for validating data. Its model is a class describing
fields, not a neural network. Inherit from BaseModel, declare annotated fields, then call
model_validate on incoming data. A field without a default is required. A field with a default
can be omitted. The result is an instance whose values you read with dot notation.
from pydantic import BaseModel, ConfigDict, Field, ValidationError
class IntroCourseRequest(BaseModel):
model_config = ConfigDict(strict=True, extra="forbid")
name: str = Field(min_length=1)
quantity: int = Field(gt=0, le=1000)
note: str = ""
intro_request = IntroCourseRequest.model_validate({"name": "mango", "quantity": 4})
print(intro_request.name, intro_request.quantity, repr(intro_request.note))
assert intro_request.note == ""The annotation says the field's type. Field supplies constraints: gt=0 means greater than
zero, le=1000 means at most 1000, and min_length=1 excludes an empty name. ConfigDict sets
model-wide behavior. strict=True rejects conversions for this integer field, including "4",
4.0 and True; extra="forbid" rejects undeclared keys. Pydantic can otherwise convert some
compatible inputs, so choose this boundary deliberately rather than assuming every accepted
input arrived in the expected type.
Predict which rule refuses each payload. ValidationError reports a failed contract. Its
errors() entries contain loc, the field location, and type, the failure category. Catching
that expected exception lets the notebook inspect the failure and continue.
intro_bad_requests = [
{"name": "mango", "quantity": "4"},
{"name": "mango", "quantity": True},
{"name": "", "quantity": 4},
{"name": "mango", "quantity": 0},
{"name": "mango", "quantity": 4, "approved": True},
{"quantity": 4},
]
for intro_bad_request in intro_bad_requests:
try:
IntroCourseRequest.model_validate(intro_bad_request)
except ValidationError as intro_error:
print([(item["loc"], item["type"]) for item in intro_error.errors(include_input=False)])
else:
raise AssertionError("An invalid input crossed the declared contract")Use model_dump() for a Python dictionary, model_dump_json() for JSON text, and
model_validate_json() to parse and validate JSON. model_json_schema() describes the contract;
it is neither an instance's current values nor an invocation of the business handler.
intro_serialized = intro_request.model_dump_json()
intro_schema = IntroCourseRequest.model_json_schema()
assert IntroCourseRequest.model_validate_json(intro_serialized) == intro_request
print("Actual values:", intro_request.model_dump())
print("Quantity contract:", intro_schema["properties"]["quantity"])
assert intro_schema["properties"]["quantity"]["exclusiveMinimum"] == 0Four is valid input to this schema even if the shop needs six. Pydantic checks the declared shape and constraints; the handler still needs authoritative stock, price and permission. Ordinary assignments to an existing instance are not automatically revalidated unless configured for assignment validation. This lesson validates new input at the boundary and uses the resulting values. Explain these limits before relying on a model object in a transaction or tool call.
Chapter 2's full introduction expands this pattern with a separate data-repair checkpoint. This notebook contains the required pattern here so prior Pydantic experience is not needed. References: models, fields, and strict mode.
SQLite from the first row to an atomic change
A dictionary disappears when its process ends. A database can retain records so a later process
can resume from evidence. SQLite is an embedded database: Python's sqlite3 library opens
a local database file without starting a separate database server. SQL is the language used
to define, select and change its records. A table has named columns and rows. A primary
key identifies a row; a query asks for rows satisfying a condition.
Start with a deliberately small preference table. Read the SQL as instructions: create the
table, insert one named value, then select the value for one session. ? is a parameter
placeholder; the values are passed separately so they are data, not SQL instructions.
fetchone() returns one row or None; it does not guarantee that a matching row exists.
import sqlite3
from pathlib import Path
from tempfile import TemporaryDirectory
with TemporaryDirectory() as intro_sql_folder:
intro_db_path = Path(intro_sql_folder) / "example.sqlite"
intro_db = sqlite3.connect(intro_db_path, autocommit=True)
intro_db.execute("CREATE TABLE preference (id INTEGER PRIMARY KEY, session TEXT, value TEXT)")
intro_db.execute("INSERT INTO preference (session,value) VALUES (?,?)", ("lucy", "09:00"))
intro_row = intro_db.execute(
"SELECT value FROM preference WHERE session=?", ("lucy",)
).fetchone()
print("Matching row:", intro_row)
assert intro_row == ("09:00",)
assert (
intro_db.execute(
"SELECT value FROM preference WHERE session=?", ("another-session",)
).fetchone()
is None
)
intro_db.close()
intro_reopen = sqlite3.connect(intro_db_path, autocommit=True)
assert intro_reopen.execute("SELECT count(*) FROM preference").fetchone()[0] == 1
intro_reopen.close()
print("The row survived closing and reopening its connection.")Index [0] selects the first column of a returned tuple. sqlite3.Row is an alternative row
factory that also permits named-column access. dict(row) then produces an ordinary dictionary.
The book's Database wrapper supplies that configuration and its schema; the wrapper is course
code, while sqlite3 is the standard library. You will use the public connection and transaction
methods explained at the exercise boundary, rather than needing to reconstruct the wrapper.
Now consider a budget. Moving five cents from reserved to spent requires two values to change
together. A transaction makes a group of local changes commit together or roll back together.
The example explicitly controls SQL transactions with autocommit=True and SQL statements.
BEGIN IMMEDIATE starts a write transaction; COMMIT keeps its changes; ROLLBACK discards them.
Predict the row after the deliberately raised exception. Catching an error alone would not undo
the first update; the rollback is the operation that restores the prior state.
intro_ledger = sqlite3.connect(":memory:", autocommit=True)
intro_ledger.execute(
"CREATE TABLE budget (id INTEGER PRIMARY KEY, reserved INTEGER, spent INTEGER)"
)
intro_ledger.execute("INSERT INTO budget VALUES (1,5,0)")
intro_ledger.execute("BEGIN IMMEDIATE")
try:
intro_ledger.execute("UPDATE budget SET reserved=0 WHERE id=1")
raise ValueError("injected failure before the matching spend update")
except ValueError:
intro_ledger.execute("ROLLBACK")
assert intro_ledger.execute("SELECT reserved,spent FROM budget").fetchone() == (5, 0)
intro_ledger.execute("BEGIN IMMEDIATE")
intro_ledger.execute("UPDATE budget SET reserved=0,spent=5 WHERE id=1")
intro_ledger.execute("COMMIT")
print(
"After a complete change:", intro_ledger.execute("SELECT reserved,spent FROM budget").fetchone()
)
intro_ledger.close()The literal :memory: creates a temporary database inside this connection; it is useful for the
small experiment, but the earlier file example establishes persistence. Neither example proves
that a remote supplier rolls back when the local transaction rolls back. An external operation
has its own state and evidence.
SQL you will meet later. UPDATE ... SET ... WHERE ... changes selected rows. AND combines
conditions. ORDER BY makes an ordering explicit; absent that clause, do not rely on row order.
count(*) counts rows; sum(amount) totals a column and can be NULL on an empty input;
coalesce(sum(amount),0) uses zero for that empty aggregate. A UNIQUE constraint rejects
duplicate identities. GROUP BY status computes one aggregate per status.
An invariant is a condition that must remain true across operations, such as nonnegative
reserved money. A snapshot is a consistent view at one point; two separate reads can describe
different moments unless their transaction contract binds them. In the book, with db.immediate()
groups related writes. It is a course-defined context manager with the commit/rollback purpose
you just observed. Do not assume that an arbitrary with connection has identical behavior under
every SQLite autocommit setting.
Your prediction: two workers both read ten remaining cents outside a transaction and each approve seven. Why can both believe the next order fits? Explain what must be checked together with the write. Then change the example's initial reserved amount and repeat the failure. Reference: Python's SQLite tutorial and transaction control.
Restoring bytes does not restore the outside world
Lucy restores yesterday's database after a disk problem. The supplier may have accepted another order since that backup. The local database now describes an older state, while the supplier has not traveled backward. A backup is a retained copy under a stated consistency contract. A restore replaces local state from that copy. Reconciliation compares restored knowledge with independent current evidence before work resumes.
A manifest lists expected files or records and their identities. An integrity check can detect corrupt bytes. A schema version describes the structure the running code expects. A valid digest does not prove schema compatibility, and schema compatibility does not prove freshness. Check these questions separately before mutating the live database.
Observe a missing file and a changed file
The example models a two-file backup manifest. Predict which candidate is intact, which has a missing member and which contains a changed value. Comparing only the files that happen to be present would miss the absent authority record.
import hashlib
intro_backup_files = {"database": b"consistent snapshot", "authority": b"era-4"}
intro_manifest = {
name: hashlib.sha256(value).hexdigest() for name, value in intro_backup_files.items()
}
intro_candidates = [
dict(intro_backup_files),
{"database": b"consistent snapshot"},
{**intro_backup_files, "database": b"corrupt snapshot"},
]
for intro_candidate in intro_candidates:
intro_observed = {
name: hashlib.sha256(value).hexdigest() for name, value in intro_candidate.items()
}
print("Exact members and bytes:", intro_observed == intro_manifest)This demonstrates comparison logic, not a SQLite backup procedure. Copying a database file at
an arbitrary moment is not interchangeable with using SQLite's backup interface, especially
when journal state matters. The book's supplied Database wrapper exposes the actual backup
and restore operations; the construction task works with their real schema and connections.
Pause before a restored image can admit new work
Pausing the old live connection is insufficient if the snapshot being copied over it contains an unpaused control row. The prepared restored image itself must carry paused state. It also gets a fresh authority epoch so surviving workers from before the restore cannot write using their former claims. Old ownership and approvals are cleared or revoked according to the chapter's restore contract.
intro_old_live = {"paused": True, "epoch": "current-era"}
intro_backup_control = {"paused": False, "epoch": "old-era"}
intro_naive_restore = dict(intro_backup_control)
intro_prepared_restore = {**intro_backup_control, "paused": True, "epoch": "fresh-era"}
print("Naive copy paused:", intro_naive_restore["paused"])
print("Prepared image paused:", intro_prepared_restore["paused"])
assert intro_prepared_restore["paused"] and not intro_naive_restore["paused"]An inode is the filesystem identity of a file on relevant systems. Existing open connections can keep referring to an old file if a pathname is simply replaced. The actual restore path uses the database backup mechanism into the existing destination connection and separately publishes the authority marker. You do not need to implement a filesystem to understand why path equality and open-handle identity are different questions.
Prepare, validate, publish, reconcile, resume
The restore builds a temporary prepared image, checks integrity and schema, assigns a fresh epoch, clears stale ownership and leaves the result paused. The authority marker is published with the intended durability steps before normal work is allowed to resume. A failed preflight should preserve the live work. “The command returned” is weaker evidence than observing the resulting control state and refusing a stale claim.
After restore, compare local orders with the supplier's retained operation identities. A missing local order may reflect backup age, not supplier failure. Historical model usage may be incomplete; report that uncertainty rather than reconstructing a confident total from missing rows.
Unit A constructs the real restore function. Unit B removes the prepared-image pause and tries the normal claim API, exposing unauthorized continuation. Transfer cases include repeated restore, missing and same-path backups, changed authority epochs and preserved work after failed preflight. The operational runbook should list what to inspect before explicitly resuming.
This notebook performs local restore experiments. A service manager, such as systemd on a supported Linux host, starts and supervises long-running services. A unit file describes a service; restart policy, permissions and boot behavior require host-specific verification. The core's local PASS is not a host reboot, upgrade or unattended-service acceptance claim. Keep the book's host recipe as a separately observed extension.
Understand the supplied execution interface
The course runtime is provided so your implementation can be connected to real callers and
storage. SourceTask(ROOT, chapter) makes a private copy. install(source) replaces only the
declared function; visible() invokes the real chapter probe; save(path, result) retains a
successful implementation and its evidence. load(path) checks the saved identities and hashes.
inject_failure() changes the declared boundary; repair(fragment) replaces that broken fragment.
close() removes the scratch copy after you retain evidence. These methods are supplied harness
operations, not additional packages you must discover or install.
RuntimeLab provides the same copied-source failure experiment without the complete-function
construction layer. Its run method records exit status, observations and the compared expectation.
A subprocess log from an unfinished learner implementation is feedback about that implementation;
it is not a successful connection. A syntax error in the notebook cell itself is a separate issue
to fix. The task below names which interface it uses.
For direct-function units, the visible driver calls your callback without installing a source string. In either case, trace where your code is invoked. Supplied fixtures, database wrappers and replay models are labeled infrastructure; your own implementation and changed-case explanation are the evidence of learning.
Main practical: construct, connect and challenge
Design question: Restore must pause before resuming work?
Construct restore from a separate compatible backup: verify integrity and schema, prepare a fresh epoch, clear old ownership, revoke approvals, pause, publish the authority marker and copy into the same database inode.
You edit a complete function in a temporary copy of src/sovereign_agent/assistant_service.py. The real callers, database and tool boundaries remain connected. The self-contained setup above supplies the frozen development runtime. The notebook runs reviewed local subprocesses; it is not a security sandbox. No live account is required.
Predict before running
Back up real queued work, restore and attempt a claim through the normal worker API before and after an explicit reconciliation/resume decision.
Write the expected result and a falsifying observation before running. Include one legal action, one refusal, and the exact-empty case where the interface permits it. Explain the consequence for Lucy if your prediction is wrong.
import json
import os
import runpy
from pathlib import Path
ROOT = COURSE_ROOT
SourceTask = runpy.run_path(str(ROOT / "book/always_on/exercises/source_tasks_v1.py"))["SourceTask"]
REFERENCE_LESSON = 15
HANDOFF = Path("ch19-unit-a-handoff-v1.json")Construct the complete mechanism
Implement restore in the string below. Keep the named signature and existing helper interfaces. The starter is intentionally incomplete; its failed connection is reported separately from whether the notebook itself executed. A constant answer cannot stand in for the real mechanism.
Inspect the supplied caller around the function in src/sovereign_agent/assistant_service.py. Draw the data path from the observed output through this function to its actual input or query. Then write your implementation from the contract.
implementation_source = """
def restore(db: Database, source: Path) -> None:
raise NotImplementedError("Construct this chapter mechanism")
"""Hint 1: the design
Pausing the old database is insufficient if the copied snapshot itself is unpaused.
Hint 2: the boundary
Inspect the parameters and the caller in src/sovereign_agent/assistant_service.py. Identify validation, durable state and the first externally observable effect. Preserve the existing surrounding helper contracts.
Hint 3: the structure
Prepare changes in a temporary image; persist and atomically replace the epoch marker; use SQLite backup into the existing connection; leave the result paused.
Connect to the cumulative runtime
The following installs your complete function into the copied runtime and executes the chapter probe against it. It saves your implementation and the observed connection for Unit B only after that connection succeeds.
def connect_build(source):
task = SourceTask(ROOT, REFERENCE_LESSON)
try:
task.install(source)
result = task.visible()
if result["status"] == "PASS":
task.save(HANDOFF, result)
return result
finally:
task.close()
build_result = connect_build(implementation_source)
print("CONNECTION", build_result["status"])
print("OBSERVATION", build_result["observation"])Challenge and transfer
Retain backup hashes, repeat restore, compare authority epochs and test same-path or missing backups. A failed preflight must preserve live work.
Use a fresh SourceTask, install your implementation and edit only its copied probe to run the changed input. Keep the expected result in your prediction notes, independent of your implementation. Call task.run("MY_TRANSFER", expected=your_expected) and close the task in finally. Retain both a valid and a refused case so rejecting everything cannot pass.
The instructor runs additional cases with different identities and boundaries against the real source. Passing the visible connection alone is not the transfer verdict. Do not put instructor solutions or holdouts into a student submission.
Save and explain
After success, submit ch16-unit-a-handoff-v1.json, your source, prediction notes and changed-input observations. Unit B checks the chapter, runtime hash and exact implementation hash and re-executes your code.
Explain which input or state caused the output, which observation would refute the explanation and what remains outside the guarantee: No host reboot or external supplier reconciliation is proved by this local exercise.
exercise_report = {
"unit": "ch19-a",
"attempted": 1,
"completed": int(build_result["status"] == "PASS"),
"failed": int(build_result["status"] != "PASS"),
"skipped": 0,
"connection": build_result["status"],
"handoff": "WRITTEN" if build_result["status"] == "PASS" else "NOT_READY",
}
print("EXERCISE_REPORT=" + json.dumps(exercise_report, sort_keys=True))Changed-constraint construction: Validate every member of a restore manifest
Allow twenty minutes. Spend three minutes predicting, ten implementing and tracing, five on a new case of your own, and two explaining the surviving limitation. This is dedicated work, not an invitation to run a supplied answer. Both units revisit the same invariant after different core experiences; in Unit B, attempt this task from memory before consulting Unit A.
Implement transfer_check(expected, observed). These dictionaries map relative file names to already-computed digest strings. Return True only when expected is nonempty, names match exactly, and every digest matches. Missing and unexpected files both refuse. This checks manifest agreement, not schema compatibility or external freshness.
Write your expected values before running the table. Keep one accepted case and one refusal. Your function is passed directly into the driver below. The driver copies inputs and checks they remain unchanged; it does not replace your implementation with the reference answer.
Hint 1: identify the authoritative inputs Name the source field for each output value. Which input changes while the rule remains the same?
Hint 2: choose the boundary cases Start with exact empty, exact equality and one value on each side of the boundary where valid. Do not add a special case for a visible product name or operation identity.
def transfer_check(expected, observed):
raise NotImplementedError("Compare complete manifest membership and digests")import copy
import json
TRANSFER_CASES = [
("intact", [{"db": "a", "authority": "b"}, {"authority": "b", "db": "a"}], True),
("missing authority", [{"db": "a", "authority": "b"}, {"db": "a"}], False),
("changed database", [{"db": "a"}, {"db": "different"}], False),
("unexpected member", [{"db": "a"}, {"db": "a", "extra": "b"}], False),
]
def same_transfer_value(actual, expected):
if type(actual) is not type(expected):
return False
if isinstance(expected, dict):
return actual.keys() == expected.keys() and all(
same_transfer_value(actual[key], value) for key, value in expected.items()
)
if isinstance(expected, list):
return len(actual) == len(expected) and all(
same_transfer_value(a, e) for a, e in zip(actual, expected, strict=True)
)
return actual == expected
def run_transfer(candidate, cases):
observations = []
for label, arguments, expected in cases:
supplied = copy.deepcopy(arguments)
before = copy.deepcopy(supplied)
raised = None
try:
actual = candidate(*supplied)
except NotImplementedError:
raised = "NotImplementedError"
actual = {"unfinished": True}
except Exception as error:
raised = type(error).__name__
actual = {"raises": raised}
expects_error = isinstance(expected, dict) and set(expected) == {"raises"}
correct = (
raised == expected["raises"]
if expects_error
else (raised is None and same_transfer_value(actual, expected))
)
passed = correct and same_transfer_value(supplied, before)
observations.append(
{"case": label, "expected": expected, "observed": actual, "passed": passed}
)
print("PASS" if passed else "NEEDS_WORK", label, "expected", expected, "observed", actual)
return observations
transfer_observations = run_transfer(transfer_check, TRANSFER_CASES)
TRANSFER_PASSED = all(row["passed"] for row in transfer_observations)
print("TRANSFER_STATUS", "PASS" if TRANSFER_PASSED else "NEEDS_WORK")Design a counterexample and retrieve the mechanism
Add one new case with an independently calculated expected outcome to TRANSFER_CASES and rerun
the driver. Change one condition at a time. Then deliberately replace your candidate with a
constant answer in a temporary copy and show a case that rejects it. Restore your implementation.
Explain why that counterexample is stronger than repeating the original example with a new name.
Without viewing the worked example, write the invariant in words and trace one observed value back to its input. Identify which part is a local fixture result and which claim would need a live provider, host or external-system observation. Keep a first attempt even if you used a hint.
Save your evidence and explain the result
Fill the prediction notes and your explanation before saving. Include the exact observed value, the input or retained row that caused it, your code's invocation point, one failed hypothesis, and the strongest claim the evidence still cannot support. A completed code cell alone does not earn explanation credit. Do not label reference-start behavior as your own Unit A construction.
Keep this edited notebook, the Markdown if used for notes, saved handoff files, and the JSON record below. Your work folder survives scratch cleanup and can be reopened in a new kernel. An instructor can ask for an unseen case after the visible checks; keep your implementation general.
explanation_notes = {
"causal_trace": "Explain the input, learner invocation and observed result.",
"failed_hypothesis": "Describe a prediction the evidence changed.",
"remaining_limit": "Name the guarantee not established by this experiment.",
}
course_submission = {
"unit": "ch19-a",
"planned_minutes": 90,
"starting_evidence": globals().get("HANDOFF_ORIGIN", "INDEPENDENT_UNIT_A"),
"prediction": prediction_notes,
"explanation": explanation_notes,
"core_report": exercise_report,
"transfer": transfer_observations,
"explanation_review": "HUMAN_REVIEW_REQUIRED",
}
submission_path = COURSE_WORK / "ch19-a-submission-v1.json"
submission_path.write_text(
json.dumps(course_submission, indent=2, sort_keys=True), encoding="utf-8"
)
print("Saved evidence:", submission_path)
print(
"COURSE_REPORT="
+ json.dumps(
{
"unit": "ch19-a",
"transfer_passed": TRANSFER_PASSED,
"starting_evidence": course_submission["starting_evidence"],
"edition": "student",
},
sort_keys=True,
)
)Extension: put a language model behind restore
Every restore above was started by you, and you decided when the shop could work again. The
reason restore leaves the database paused, replaces the authority epoch, clears every owner
and revokes every approval is that in production the caller is not you: it is a worker, or a
model asked to "get the shop back up", and neither knows what the supplier did after the
snapshot was taken. This closing section puts a real model behind the same maintenance path
and shows exactly where it stops.
The mechanism is the Chapter 3 loop, agent_loop.run_loop, with tools that wrap the runtime's
own health, backup, restore and the normal worker claim. The model may take a backup,
restore from one and ask for work. It cannot resume the shop: the resume tool sits behind a
before_write gate that admits it only when Lucy has recorded a decision naming the epoch
that restore published. A decision for the old epoch is stale, because the snapshot she
reconciled is not the one now on disk.
llm_lab.py supplies the loop's Model protocol twice: ScriptedModel replays turns written
here, so this section runs identically for everyone; OpenAIModel sends the same messages to
a hosted model when a key is present. The record always says which one ran.
Prediction: the next cell builds the dispatcher and prints what the model will see. Write
down which four tools are consequential, and what claim returns while the shop is paused.
import json
import shutil
from pathlib import Path
from pydantic import BaseModel, ConfigDict, Field
from reference_organizations.store.agent import seed_lucy
from sovereign_agent.agent_loop import Limits, run_loop
from sovereign_agent.assistant_service import backup, health, restore
from sovereign_agent.assistant_work import claim, enqueue
from sovereign_agent.database import Database
from sovereign_agent.llm_lab import (
DEFAULT_MODEL,
ScriptedModel,
build_model,
describe,
resolve_api_key,
scripted_turn,
tool_trace,
)
from sovereign_agent.tool_dispatch import Dispatcher, ExecutableTool
SCRIPTED_DB = COURSE_WORK / "ch19-llm-scripted.sqlite"
LIVE_DB = COURSE_WORK / "ch19-llm-live.sqlite"
OPERATOR_DECISION = {}
REFUSALS = []
def backup_dir(db):
return db.path.parent / (db.path.stem + "-backups")
def authority_epoch(db):
return db.path.with_suffix(".authority").read_text()
def fresh_shop(path):
"""A shop with one queued work item, so reruns and replays observe the same rows."""
for stale in (path, Path(f"{path}-wal"), Path(f"{path}-shm"), path.with_suffix(".authority")):
stale.unlink(missing_ok=True)
db = Database(path)
shutil.rmtree(backup_dir(db), ignore_errors=True)
backup_dir(db).mkdir()
seed_lucy(db)
enqueue(db, "morning:brief", "lucy", "Brief me on this morning's stock.")
return db
def work_row(db):
return dict(
db.connection.execute(
"SELECT status,owner,generation FROM assistant_work WHERE origin='morning:brief'"
).fetchone()
)
class NoArguments(BaseModel):
model_config = ConfigDict(extra="forbid", strict=True)
class BackupArguments(NoArguments):
name: str = Field(pattern=r"^[a-z][a-z0-9-]{0,40}\.sqlite$")
class ClaimArguments(NoArguments):
owner: str = Field(min_length=1, max_length=60)
class ResumeArguments(NoArguments):
reason: str = Field(min_length=1, max_length=200)
def service_tools(db):
def read_health(_):
return health(db)
def take_backup(args):
try:
return {"backup": backup(db, backup_dir(db) / args.name).name}
except OSError as error:
REFUSALS.append(("backup", "backup destination already exists"))
raise ValueError("backup refused") from error
def restore_backup(args):
before = authority_epoch(db)
try:
restore(db, backup_dir(db) / args.name)
except ValueError as error:
REFUSALS.append(("restore", str(error)))
raise
return {"paused": health(db)["paused"], "epoch_changed": authority_epoch(db) != before}
def claim_work(args):
work = claim(db, args.owner)
if work is None:
return {"claimed": False, "paused": health(db)["paused"]}
return {"claimed": True, "work": work.id, "generation": work.generation}
def resume_work(args):
# The runtime's own resume lives at the end of account recovery, after a reconciled
# plan; this is the same single write, admitted only by the operator gate below.
with db.immediate() as connection:
connection.execute("UPDATE assistant_control SET paused=0 WHERE id=1")
return {"paused": False, "reason": args.reason}
def operator_gate(call):
if call.name != "resume_work":
return
decision = OPERATOR_DECISION.get("resume_epoch")
if decision is None:
REFUSALS.append(("resume_work", "operator_decision_required"))
raise PermissionError("operator_decision_required")
if decision != authority_epoch(db):
REFUSALS.append(("resume_work", "stale_epoch"))
raise PermissionError("stale_epoch")
tools = [
ExecutableTool(
"health",
"Read whether the shop is paused, work counts by status and uncertain outcomes.",
NoArguments,
read_health,
),
ExecutableTool(
"backup",
"Write a checked SQLite snapshot under the given name. Refuses an existing name.",
BackupArguments,
take_backup,
consequential=True,
),
ExecutableTool(
"restore",
"Replace the live database with a named backup. The shop is paused afterwards.",
BackupArguments,
restore_backup,
consequential=True,
),
ExecutableTool(
"claim_work",
"Claim the next queued work item for a worker through the normal worker API.",
ClaimArguments,
claim_work,
consequential=True,
),
ExecutableTool(
"resume_work",
"Unpause the shop. Admitted only after Lucy records a reconciliation decision.",
ResumeArguments,
resume_work,
consequential=True,
),
]
return Dispatcher(
tools, allowed=frozenset(tool.name for tool in tools), before_write=operator_gate
)
shop_db = fresh_shop(SCRIPTED_DB)
opening_epoch = authority_epoch(shop_db)
shop_dispatcher = service_tools(shop_db)
for tool_schema in shop_dispatcher.schemas():
function = tool_schema["function"]
print(function["name"], "requires", function["parameters"].get("required", []))
print(" ", function["description"])
print("Opening epoch:", opening_epoch)
print("Queued work:", work_row(shop_db))
print("Health:", json.dumps(health(shop_db), sort_keys=True))
SHOP_MESSAGES = [
{
"role": "system",
"content": (
"You help Lucy operate her ice cream shop's always-on assistant. Use the tools to "
"check health, take a backup, restore from it and try to claim queued work. "
"Restoring pauses the shop; resuming is Lucy's decision after she reconciles the "
"supplier account, so report what is paused rather than working around it. "
"Finish with a short plain-words summary of what happened."
),
},
{
"role": "user",
"content": (
"Take this morning's backup, prove the restore path works, and tell me whether "
"the worker can pick up the morning brief afterwards."
),
},
]The schemas are the model_json_schema() of the argument classes above, wrapped in the
{"type": "function", ...} envelope; strict=True and extra="forbid" mean a backup name
with a path separator is refused before any handler runs. health is the only reading tool.
The other four change durable state, so they are consequential and pass through the gate, which
only has an opinion about resume_work.
Run the loop against a recorded transcript
The turns below are what a model replied, written down. Turn one reads health. Turn two takes
morning.sqlite and then asks to restore from yesterday.sqlite, a backup that was never
taken. Turn three restores from the real one. Turn four tries the worker API and then asks to
resume because the restore "completed cleanly". Turn five answers, which ends the loop.
Prediction: two calls are refused. Name them, and say which refusal comes from the runtime's
own rule in restore and which from the gate. Then predict the generation of the work row
after the run: restore bumps it once for the copied image, and a refused claim does not.
RECORDED_TURNS = [
scripted_turn(calls=[{"name": "health", "arguments": {}}]),
scripted_turn(
calls=[
{"name": "backup", "arguments": {"name": "morning.sqlite"}},
{"name": "restore", "arguments": {"name": "yesterday.sqlite"}},
]
),
scripted_turn(calls=[{"name": "restore", "arguments": {"name": "morning.sqlite"}}]),
scripted_turn(
calls=[
{"name": "claim_work", "arguments": {"owner": "assistant-worker"}},
{"name": "resume_work", "arguments": {"reason": "restore completed cleanly"}},
]
),
scripted_turn(
"Backed up to morning.sqlite and restored from it; yesterday.sqlite does not exist. "
"The shop is paused, so the worker could not claim the morning brief, and resuming "
"was refused: that needs Lucy's reconciliation decision, not mine."
),
]
LOOP_LIMITS = Limits(model_calls=8, tool_calls=16, seconds=120, output_tokens=4096)
REFUSALS.clear()
scripted_result = run_loop(
ScriptedModel(RECORDED_TURNS), shop_dispatcher, SHOP_MESSAGES, limits=LOOP_LIMITS
)
print(describe(scripted_result, "scripted"))
print("Refusals:", REFUSALS)
print("Work row now:", work_row(shop_db))
scripted_trace = tool_trace(scripted_result)
assert scripted_result.status == "COMPLETED"
assert [step["ok"] for step in scripted_trace] == [True, True, False, True, True, False]
assert scripted_trace[2]["result"] == "tool_failed"
assert scripted_trace[3]["result"] == {"paused": True, "epoch_changed": True}
assert scripted_trace[4]["result"] == {"claimed": False, "paused": True}
assert REFUSALS == [
("restore", "a separate existing backup is required"),
("resume_work", "operator_decision_required"),
]
# Durable state: paused, a fresh epoch published to the marker, ownership cleared once.
assert health(shop_db)["paused"] is True
assert work_row(shop_db) == {"status": "READY", "owner": None, "generation": 1}
assert authority_epoch(shop_db) != opening_epoch
control_epoch = shop_db.connection.execute("SELECT epoch FROM assistant_control").fetchone()[0]
assert control_epoch == authority_epoch(shop_db)
assert all(
message.get("tool_call_id") for message in scripted_result.messages if message["role"] == "tool"
)Both refusals reach the model as tool_failed, and the loop carries on. The reason lives in
REFUSALS, the ledger the handlers and the gate keep, because Dispatcher.invoke never copies
an error message into an observation. The runtime refused yesterday.sqlite with the words
restore raises for any missing or same-path source; the gate refused the resume because no
operator decision existed.
Run the same loop against a live model
Nothing changes except the model, and the shop starts fresh so the live run sees the same
queued work and unpaused health. build_model looks for an OPENAI_API_KEY in Colab's
Secrets pane (the key icon in the left sidebar: add a secret with that name and switch on
notebook access for it), then in the process environment for a local kernel. Without a key it
returns the scripted model again and the record says so. A missing key is a normal condition,
never a silent substitution.
The live model is gpt-5.1 through the openai library, which Colab ships preinstalled; on a
local kernel run %pip install openai once. The model may check health twice, pick another
backup name or claim before restoring; what it cannot do is resume, because no decision is
recorded, or claim work after a restore, because claim returns nothing while paused.
Prediction: write the tool sequence a careful operator's assistant should produce, then run the cell and compare the model's judgment, or the recorded transcript when scripted, against it.
live_db = fresh_shop(LIVE_DB)
live_dispatcher = service_tools(live_db)
REFUSALS.clear()
chosen_model = build_model(RECORDED_TURNS, model=DEFAULT_MODEL)
print("Model source:", chosen_model.source, "| key found:", resolve_api_key() is not None)
live_result = run_loop(chosen_model, live_dispatcher, SHOP_MESSAGES, limits=LOOP_LIMITS)
print(describe(live_result, chosen_model.source))
print("Refusals:", REFUSALS)
live_trace = tool_trace(live_result)
assert live_result.status in {"COMPLETED", "MODEL_CALL_LIMIT", "TOOL_LIMIT", "MODEL_FAILED"}
# Whatever the model did, no resume was admitted: the operator recorded no decision.
assert not any(step["ok"] for step in live_trace if step["tool"] == "resume_work")
assert all(
reason == "operator_decision_required" for tool, reason in REFUSALS if tool == "resume_work"
)
restored_at = [
index for index, step in enumerate(live_trace) if step["tool"] == "restore" and step["ok"]
]
if restored_at:
assert health(live_db)["paused"] is True
for step in live_trace[restored_at[0] :]:
if step["tool"] == "claim_work" and step["ok"]:
assert step["result"]["claimed"] is False, step
assert all(
message.get("tool_call_id") for message in live_result.messages if message["role"] == "tool"
)Challenge: record the decision, then record it for the wrong epoch
The gate's rule is data. The next cell records Lucy's resume decision twice against the scripted
shop, which is still paused from the recorded run. First it names opening_epoch, the epoch
that existed before the restore, and replays a two-call transcript: resume, then claim.
Predict before running: is that resume admitted? The snapshot Lucy reconciled carries the
epoch restore published to the .authority marker, and the old one no longer matches it.
Then it records the current epoch and replays the same transcript. Predict the generation on
the work row once the claim succeeds, remembering the bump the restore already made. With a
live key, add the decision before rerunning the live cell and explain whether the model resumed
before or after checking health.
In your notes, separate what the scripted run proves (the pause, the epoch replacement, the worker API refusing while paused, the gate binding a decision to an epoch) from what only a live run can show (the model's judgment). No host reboot or supplier reconciliation is proved here; the saved record keeps the two apart.
RESUME_TURNS = [
scripted_turn(
calls=[
{"name": "resume_work", "arguments": {"reason": "Lucy reconciled the account"}},
{"name": "claim_work", "arguments": {"owner": "assistant-worker"}},
]
),
scripted_turn("Resumed the shop and claimed the morning brief."),
]
OPERATOR_DECISION["resume_epoch"] = opening_epoch
REFUSALS.clear()
stale_result = run_loop(
ScriptedModel(RESUME_TURNS), shop_dispatcher, SHOP_MESSAGES, limits=LOOP_LIMITS
)
stale_trace = tool_trace(stale_result)
print("Stale decision:", [step["ok"] for step in stale_trace], REFUSALS)
assert [step["ok"] for step in stale_trace] == [False, True]
assert stale_trace[1]["result"] == {"claimed": False, "paused": True}
assert REFUSALS == [("resume_work", "stale_epoch")]
OPERATOR_DECISION["resume_epoch"] = authority_epoch(shop_db)
REFUSALS.clear()
resumed_result = run_loop(
ScriptedModel(RESUME_TURNS), shop_dispatcher, SHOP_MESSAGES, limits=LOOP_LIMITS
)
resumed_trace = tool_trace(resumed_result)
print("Current decision:", [step["ok"] for step in resumed_trace], work_row(shop_db))
assert [step["ok"] for step in resumed_trace] == [True, True]
assert resumed_trace[1]["result"]["claimed"] is True
assert health(shop_db)["paused"] is False
assert work_row(shop_db) == {"status": "RUNNING", "owner": "assistant-worker", "generation": 2}
llm_lab_report = {
"unit": "ch19-a",
"scripted": {
"status": scripted_result.status,
"tool_calls": scripted_result.tool_calls,
"refused": [
("restore", "a separate existing backup is required"),
("resume_work", "operator_decision_required"),
],
"work_row_after_restore": {"status": "READY", "owner": None, "generation": 1},
},
"session": {
"source": chosen_model.source,
"model": DEFAULT_MODEL if chosen_model.source == "live" else None,
"status": live_result.status,
"model_calls": live_result.model_calls,
"tool_calls": live_result.tool_calls,
"restored": [
step["arguments"]["name"]
for step in live_trace
if step["tool"] == "restore" and step["ok"]
],
"paused_after": health(live_db)["paused"],
"answer": live_result.answer[:400],
},
"operator_decision": {
"stale_epoch": {"resumed": False, "refused": "stale_epoch"},
"current_epoch": {"resumed": True, "work_row": work_row(shop_db)},
},
}
llm_report_path = COURSE_WORK / "ch19-a-llm-lab-report-v1.json"
llm_report_path.write_text(json.dumps(llm_lab_report, indent=2, sort_keys=True), encoding="utf-8")
print("Saved evidence:", llm_report_path)
print("LLM_LAB_REPORT=" + json.dumps(llm_lab_report["session"], sort_keys=True))
shop_db.close()
live_db.close()Keep building with Prof Rod
Found this material through a colleague, classroom or shared download? Get the complete book at profrod.ai/book and join the Prof Rod learner community. Bring one result, one question or one failure you learned from. Share this resource with another learner and keep its source links with it so they can find the full course and future updates.