Student edition · 90 minutes of dedicated work · 2026-09-09
This is one of two practical units for Chapter 11. 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.
- Implement durable admission, uncertainty and receipt transitions; settle a reservation exactly once from matching supplier evidence.
- Solve interpret discovery without inventing certainty 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 |
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 nineteen-chapter 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
Use a Python 3.14 Jupyter kernel and 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 complete source runtime uses Python 3.14, so this edition does not claim
compatibility with a hosted notebook service's default interpreter.
The collapsed cell contains 89 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. 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/ch11-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 pence 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 pence 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.
An ambiguous response leaves two systems with different knowledge
Lucy approves an order. The supplier stores it, then the network reply is lost. Locally we see a timeout; remotely an order exists. The central difficulty is not calculating the quantity again. It is deciding what evidence is available without creating another purchase. Distributed state means relevant records live in more than one independently changing system.
An intent is the durable local record of one exact operation before sending. An operation identity distinguishes that intent from another business operation. A receipt binds a supplier outcome to that identity and exact proposal. Reconciliation asks the external system for evidence and brings local state into agreement where the evidence is conclusive.
Separate the effect from the caller's observation
The function below deliberately stores a supplier record before raising. The exception describes what the caller observed; it does not reverse the stored effect. Predict both the local exception and the independent order count before running.
intro_supplier_orders = {}
def intro_accept_then_lose_reply(operation, proposal):
if operation in intro_supplier_orders and intro_supplier_orders[operation] != proposal:
raise ValueError("same identity used for different intent")
intro_supplier_orders.setdefault(operation, dict(proposal))
raise TimeoutError("reply lost after acceptance")
for intro_operation in ("opening-order", "opening-order"):
try:
intro_accept_then_lose_reply(intro_operation, {"sku": "MANGO", "quantity": 4})
except TimeoutError:
print("caller state UNKNOWN; supplier count", len(intro_supplier_orders))
assert len(intro_supplier_orders) == 1This fixture's stable-identity contract prevents a second effect when the same operation is repeated. A real supplier must explicitly support the relevant behavior; a local ID string alone cannot force a remote service to deduplicate. The book's supplier fixture lets us inspect the independent records so the experiment does not grade its own final prose.
Why a new identity creates a new operation
Repeat the experiment with a new identity after the lost reply. Nothing tells the supplier that the second identity is intended as a retry of the first. There are now two valid-looking intents for the same products. Identical payloads are not enough to conclude that two operations are one.
try:
intro_accept_then_lose_reply("opening-order-retry", {"sku": "MANGO", "quantity": 4})
except TimeoutError:
pass
assert len(intro_supplier_orders) == 2
print("Stored operation identities:", sorted(intro_supplier_orders))Unit B introduces this defect at the actual send boundary with a freshly generated UUID. A UUID is a generated identifier; uniqueness is useful for a new intent but harmful when it accidentally turns a retry into a new business operation. The repair reuses the recorded identity and proposal. The evidence is the supplier's order count, not a message claiming “retry handled.”
Read the local state machine
| State | What the local record says | What it does not establish |
|---|---|---|
| APPROVED | Exact intent has bounded authority | Supplier acceptance |
| SENDING | Send was admitted and intent recorded | A conclusive response |
| UNKNOWN | Available evidence cannot settle the outcome | Failure or permission for a new order |
| CONFIRMED | Matching accepted receipt was retained | Physical delivery |
| REJECTED | Matching conclusive rejection was retained | That every transport failure is rejection |
The reservation remains held while the outcome is uncertain. On a matching accepted receipt, money moves from reserved to spent exactly once. On a conclusive rejection, the reservation is released. A repeated identical receipt must not spend twice; a contradictory receipt must not silently overwrite the first outcome. Both identity and exact proposal need comparison.
Adapt vocabulary without weakening evidence
Another supplier may call its fields order_ref, payload and decision. An adapter maps these
names to the internal receipt. It must preserve operation and proposal and reject unknown
decisions. None from a lookup is not an accepted receipt. Nor is an unavailable lookup proof
that no remote order exists. Keep absence, unavailability and conclusive rejection distinct.
The core code uses SQLite for local intent and a local supplier fixture for independent effects. A loopback server, where used, listens on this machine rather than contacting a real supplier. Its subprocess and temporary database are supplied infrastructure. Your work constructs the transition boundary, repairs identity handling and normalizes changed discovery data.
Before coding, draw two columns, local ledger and supplier ledger, at four instants: before send, after acceptance, after lost reply and after lookup. Put unknown values explicitly in the table. The transfer task will change the receipt vocabulary and failure schedule while requiring the same accounting invariant.
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 labelled infrastructure; your own implementation and changed-case explanation are the evidence of learning.
The supplied helper imports below are available to your implementation. append_event(db, name, data) records an event in the current database transaction; it does not contact a provider or send an order.
import jsonMain practical: construct, connect and challenge
Lucy's supplier can accept an order and lose the reply. You will implement the local transitions around that uncertain interval, connect them to the real SQLite order schema and the simulated supplier, and save evidence for Unit B.
This unit starts only the repository's loopback supplier fixture. It cannot make a real purchase and uses no model, channel, or supplier credential.
1. Locate the cumulative code
import json
import os
import runpy
import sqlite3
import sys
import tempfile
import time
from pathlib import Path
from reference_organizations.store.agent import seed_lucy
from sovereign_agent.assistant_orders import SpendingPolicy, approve, propose
from sovereign_agent.assistant_work import claim, enqueue
from sovereign_agent.database import Database
assert sys.version_info >= (3, 14)
ROOT = COURSE_ROOT
independent_supplier = runpy.run_path(str(ROOT / "book/always_on/checkpoints/ch09.py"))[
"independent_supplier"
]Predict the two ledgers immediately after the supplier commits and drops the response. Which local amount remains reserved? How many remote orders exist?
2. Prepare one exact approved proposal
POLICY = SpendingPolicy(frozenset({"lucy"}), total_pence=2_000)
def approved_order(root, *, target="fixture", sku="SKU-VANILLA", quantity=6):
db = Database(root / "agent.sqlite")
seed_lucy(db)
enqueue(db, "chapter9:exercise", "lucy", "Replenish", subject=sku)
work = claim(db, "chapter9-student")
identifier = propose(db, work, sku, quantity, target=target)
digest = db.connection.execute(
"SELECT digest FROM assistant_orders WHERE id=?", (identifier,)
).fetchone()[0]
approve(
db,
identifier,
digest,
actor="lucy",
policy=POLICY,
expires=time.time() + 60,
)
return db, work, identifier
def order_observation(db, identifier):
row = db.connection.execute(
"SELECT status,proposal,receipt FROM assistant_orders WHERE id=?", (identifier,)
).fetchone()
money = db.connection.execute(
"SELECT reserved_pence,spent_pence FROM assistant_spending WHERE id=1"
).fetchone()
return {
"status": row["status"],
"proposal": json.loads(row["proposal"]),
"receipt": json.loads(row["receipt"]) if row["receipt"] else None,
"reserved_pence": money["reserved_pence"],
"spent_pence": money["spent_pence"],
}3. Construct the durable transition
Implement three events:
ADMITchanges one exact approved row toSENDINGbefore the network call;UNKNOWNchanges onlySENDINGtoUNKNOWNwhile keeping the reservation;RECEIPTvalidates the operation, exact proposal, and conclusive status, then settles the reservation once.
Terminal receipt replay must be idempotent. Any unsupported transition, wrong prior state, mismatched receipt, or nonconclusive receipt must raise.
def record_transition(db, work_id, identifier, event, receipt=None):
"""Record ADMIT, UNKNOWN, or RECEIPT and return the resulting observation."""
return NoneHint 1 — the decision
Commit SENDING before any external call. A timeout changes knowledge to UNKNOWN; it does not release money. Only exact conclusive evidence can settle it.
Hint 2 — the evidence
Read status, proposal, amount, and receipt for the row whose id and work_id both match. Compare canonical JSON for the stored and returned proposals.
Hint 3 — the structure
Use with db.immediate() as connection. Branch on the event, reject invalid prior states, update the order, and for a first conclusive receipt subtract amount from reserved and add it to spent only when accepted.
4. Challenge the visible contract
def grade_transition(candidate):
with tempfile.TemporaryDirectory(prefix="ch09-transition-visible-") as directory:
db, work, identifier = approved_order(Path(directory))
try:
rows = []
rows.append(candidate(db, work.id, identifier, "ADMIT"))
rows.append(candidate(db, work.id, identifier, "UNKNOWN"))
proposal = order_observation(db, identifier)["proposal"]
receipt = {"operation": identifier, "proposal": proposal, "status": "ACCEPTED"}
rows.append(candidate(db, work.id, identifier, "RECEIPT", receipt))
rows.append(candidate(db, work.id, identifier, "RECEIPT", receipt))
observed = [row["status"] for row in rows]
money = rows[-1]["reserved_pence"], rows[-1]["spent_pence"]
assert observed == ["SENDING", "UNKNOWN", "CONFIRMED", "CONFIRMED"]
assert money == (0, 1500)
mismatch = dict(receipt)
mismatch["proposal"] = {**proposal, "quantity": 99}
try:
candidate(db, work.id, identifier, "RECEIPT", mismatch)
except ValueError:
pass
else:
raise AssertionError("mismatched receipt accepted")
return {"status": "PASSED", "states": observed, "money": money}
except Exception as error:
return {"status": "NEEDS_WORK", "error": type(error).__name__}
finally:
db.close()
visible = grade_transition(record_transition)
VISIBLE_PASSED = visible["status"] == "PASSED"
print(json.dumps(visible, indent=2))
print("VISIBLE_CONTRACT", "PASSED" if VISIBLE_PASSED else "NEEDS_WORK")5. Connect the transition to the supplier
The local SENDING commit happens first. The supplier then commits one remote receipt and closes the connection. Your UNKNOWN transition retains 1,500 pence. Reconciliation reads the supplier's existing receipt and records it without another order.
connected = None
if VISIBLE_PASSED:
with tempfile.TemporaryDirectory(prefix="ch09-connected-") as directory:
root = Path(directory)
with independent_supplier(root) as (supplier, supplier_path):
db, work, identifier = approved_order(root, target=supplier.identity)
try:
before_send = record_transition(db, work.id, identifier, "ADMIT")
assert before_send["status"] == "SENDING"
try:
supplier.order(identifier, before_send["proposal"])
except OSError:
after_loss = record_transition(db, work.id, identifier, "UNKNOWN")
else:
raise AssertionError("fixture did not drop the first response")
with sqlite3.connect(supplier_path) as remote:
remote_count = remote.execute("SELECT count(*) FROM orders").fetchone()[0]
found = supplier.lookup(identifier)
final = record_transition(db, work.id, identifier, "RECEIPT", found)
connected = {
"operation": identifier,
"proposal": final["proposal"],
"after_loss": after_loss,
"final": final,
"supplier_orders": remote_count,
}
assert after_loss["status"] == "UNKNOWN"
assert (after_loss["reserved_pence"], after_loss["spent_pence"]) == (1500, 0)
assert final["status"] == "CONFIRMED"
assert (final["reserved_pence"], final["spent_pence"]) == (0, 1500)
assert remote_count == 1
finally:
db.close()
Path("ch11-unit-a-handoff-v1.json").write_text(
json.dumps(connected, indent=2, sort_keys=True) + "\n", encoding="utf-8"
)
print("CONNECTED", connected["after_loss"]["status"], connected["final"]["status"])
else:
print("CONNECTION_NOT_READY — repair record_transition, then run again.")Exit ticket
Explain why the local transaction cannot include the supplier commit, why UNKNOWN retains the reservation, and which exact evidence justifies CONFIRMED.
exercise_report = {
"unit": "ch11-a",
"attempted": 1,
"completed": int(VISIBLE_PASSED),
"failed": int(not VISIBLE_PASSED),
"skipped": 0,
"connection": "PASSED" if connected else "NOT_READY",
"handoff": "WRITTEN" if connected else "NOT_WRITTEN",
}
print("EXERCISE_REPORT=" + json.dumps(exercise_report, sort_keys=True))Changed-constraint construction: Interpret discovery without inventing certainty
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(operation, proposal, receipt). None means UNKNOWN. Otherwise require a dictionary whose operation equals the intended operation and proposal equals the exact proposal. ACCEPTED maps to CONFIRMED; REJECTED maps to REJECTED. Any other receipt or decision raises ValueError. Do not alter the proposal or assign a new operation ID.
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(operation, proposal, receipt):
raise NotImplementedError("Require matching conclusive supplier evidence")import copy
import json
TRANSFER_CASES = [
("no evidence", ["op-a", {"quantity": 4}, None], "UNKNOWN"),
(
"accepted evidence",
[
"op-a",
{"quantity": 4},
{"operation": "op-a", "proposal": {"quantity": 4}, "status": "ACCEPTED"},
],
"CONFIRMED",
),
(
"new identity",
[
"op-a",
{"quantity": 4},
{"operation": "op-b", "proposal": {"quantity": 4}, "status": "ACCEPTED"},
],
{"raises": "ValueError"},
),
(
"changed quantity",
[
"op-a",
{"quantity": 4},
{"operation": "op-a", "proposal": {"quantity": 5}, "status": "ACCEPTED"},
],
{"raises": "ValueError"},
),
]
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": "ch11-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 / "ch11-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": "ch11-a",
"transfer_passed": TRANSFER_PASSED,
"starting_evidence": course_submission["starting_evidence"],
"edition": "student",
},
sort_keys=True,
)
)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.