Repository navigation
Docket 0.1.3
[0.1.3] - 2026-10-19
Fixed
-
Every receipt since 0.1.0 named a tool version that was not running. The version was written
three times —src/docket/__init__.py's__version__,src/docket/receipt.py's
TOOL_VERSION, andsrc/docket/supervisor.py'sDOCKET_TOOL_VERSION— and each had to be
edited by hand on every release. Two of the three were still0.1.0when the distribution
installed 0.1.2, sodocket --versionsaid one thing,pip showsaid another, and the
tool.versionfield in every receipt named a release that was not the one that wrote it. A
receipt that misreports its author makes a false claim about provenance, which is the one thing
a receipt exists to make checkable. All three now import from a newsrc/docket/_version.py,
which derives the version from the installed distribution, falls back to the nearest
pyproject.toml, and then tounknown.FORMAT_VERSIONmoved there for the same reason. The
guard that had checked only__init__.pynow scans every module undersrc/for a version
literal, in both the assignment and the dict-entry form, because scanning one file is what let
the other two literals survive the first fix. -
examples/custom-agent/agent_sdk.pycrashed on the command its own README advertised. Under
docket run, the session attaches to the supervised run andsession.ledgerisNoneby
design — the supervisor is the only writer to that file, because two writers would produce two
competing hash chains. The example printedsession.ledger.pathunconditionally, so the
supervised command raisedAttributeErrorwhile the standalone one worked. Only the standalone
path was tested. The example now branches onsession.attachedand says what is true in each
case, andtests/test_examples.pydrives the supervised path and asserts the agent's own events
reached the run's ledger labelledreported_by: "agent"and that the receipt verifies. -
The
Typing :: Typedclassifier was a claim with nothing behind it.pyproject.toml
declared the classifier and the wheel shipped nopy.typed, so no consumer's type checker
would read the package's annotations at all. The package was annotated all along — and 65 of
those annotations were wrong, which nobody could see because nothing asked.py.typednow
ships, mypy runs in the lint job, and the 65 errors are fixed: narrowing that did not survive
a second method call,__exit__annotatedboolwhere it always returnsFalse, a signing
key accepted and then used as any key type, a metrics dict annotateddict[str, int]that
holds a decimal string, and the rest. A guard test fails if the marker disappears while the
classifier stays. -
docket keygendid not exist, sodocket run --signhad no way to produce a key.
src/docket/signing.pycould generate an Ed25519 keypair from the first release and nothing
called it: no command, no documentation. The only way to sign a receipt was to already know
the library function.docket keygen [PATH] [--force]writes the keypair at mode 0600, prints
the public key, refuses to replace an existing key without--force(replacing a signing key
invalidates every receipt signed with the old one), and exits 70 naming the install when the
optionalcryptographypackage is absent. Documented in the CLI reference, and tested end to
end: keygen, sign, verify, and a tampered receipt that fails both the totals and the signature. -
Two
asserts were added to narrow a type, andbanditrefused them. Anassertis
removed underpython -O, and a tool whose whole claim is that it does not skip checks cannot
rest one on a statement the interpreter is entitled to delete.verify.pynow tests
chain_problemdirectly — which is the narrowing that was wanted anyway, and drops the
chain_okalias that made the tuple unreachable — andpolicy.pyraises for the case that
cannot happen rather than asserting it. The line citations indocs/testing.mdmoved with the
code and were re-checked against the harness's site list again. -
Eight definitions in the package were dead code.
generate_keypairwas the visible one —
docket run --signwas a flag with no way to produce a key, and the function that could have
produced one was invisible. Seven more had the same shape:short_hash,mb,
money_or_zero,stable_seed,is_known,default_price_table,price_document_digest
and_gzip_bytes, each a one- or two-line helper with no caller anywhere in the tree. Dead
code is not harmless in a project whose pitch is that a receipt says what the tool did: a
package that carries functions nothing runs is making the same kind of claim about itself. All
eight are deleted, andtests/test_supply_chain.pynow fails when a top-level definition in
src/appears nowhere else insrc/,tests/,tools/,examples/orverify-js/. -
A signing key of the wrong type produced an
AttributeErrorfrom insidecryptography.
load_pem_private_keyreturns a union of every key type the library supports, and most of
them have no.sign(data). The call now refuses a non-Ed25519 key with a message naming the
file and the key it holds. -
Every link in the README was broken on the PyPI page.
README.mdis the package's long
description, and PyPI renders it with no repository around it, resolving a relative target
againsthttps://pypi.org/project/docket-ai/. All 29 relative links therefore 404ed there while
resolving perfectly on GitHub — invisible from the repository, which is why it survived three
releases. They are now absolute: a target the documentation site publishes points at the site,
so a reader arriving from PyPI lands in the documentation rather than in raw markdown, and
LICENSEpoints at the repository. Two guards were added: one fails on any relative target, one
fetches every absolute URL and fails on a non-200.
Changed
-
Documentationin[project.urls]pointed at a GitHub directory listing. It now points at the
live documentation site, which is what a reader arriving from PyPI is looking for. -
Two public properties replace a private attribute an example reached into:
Session.docket_numberandSession.ledger_path, the latter returningNonewhen a
supervisor owns the ledger rather than a path this session does not write to. -
The line citations in
docs/testing.mdwere re-based onto the code as it now stands. Each one
was re-checked againsttools/mutate.py's site list, so every number the page quotes is a
line the mutation harness would actually mutate. -
The conformance vectors were regenerated, because they carry
tool.versionand the spec states
that vectors move with the implementation in the same commit. Only the version string and the
tworeceipt_core_sha256digests that cover it changed; all three implementations still agree
(Python and Node: 88 correct, 0 wrong; Node's own suite: 100/100).