test(python): assert agent identity survives a restart - #31
Merged
Conversation
docs/PRODUCTION_CHECKLIST.md §3 marks the substance of persistent agent identity proven, leaving one unchecked item: a regression test that identity survives a restart, guarding the SDK's `load_or_create` default against a future swap to the ephemeral `generate()`. Placed at the binding level rather than the SDK level. The named seam is `wrapper.py:120`, but what actually has to hold is that `AgentIdentity.load_or_create` returns a disk-backed key in a *fresh* process — that is the property a settlement system depends on, and it is testable without standing up a StateGraph. Each pubkey comes from a separately spawned `sys.executable`, so an in-memory-only key cannot pass; existing coverage (test_api.py::test_load_or_create_persists_across_calls) is same-process and would not catch the swap. Two tests: identity is stable across two fresh processes sharing a tmp_path identities dir, and distinct agent_ids get distinct keys (so a constant-key implementation can't pass the first). Hermetic — tmp_path only, with an explicit assertion that the real data/identities/ is untouched. Verified the guard bites: swapping the subprocess snippet to generate() fails both the persistence and the pubkey-equality assertion. Picked up by the existing `python-test` pytest run; no Makefile change. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The last unchecked item under §3 Persistent agent identity is now covered by crates/attestly-python/tests/test_identity_restart.py. Records where the test lives and that the guard was verified to bite. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Review flagged that the "did not touch the real data/identities/" assertion
used a cwd-relative Path("data"), so it inspected whatever happened to be
below the invocation directory rather than the real store — a check that
read stronger than it was. Derive the path from __file__ instead, matching
the REPO_ROOT convention in cross_language.py, so it holds no matter where
pytest is invoked from.
Actual hermeticity was never at risk: load_or_create is passed the absolute
tmp_path, so test identities are structurally confined there. This makes the
belt-and-braces assertion do what it claims.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Contributor
Author
Agent Run ReportImplementation Commits
Review ReportRequirements Checked
Gaps FoundNone blocking. One minor, non-blocking observation: the secondary hermeticity assertion checked a cwd-relative Fixes Made
Quality Gate
|
andyjsbell
marked this pull request as ready for review
August 5, 2026 16:17
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes the last unchecked item under §3 Persistent agent identity (Persona-B load-bearing) in
docs/PRODUCTION_CHECKLIST.md.§3's substance was already proven manually and marked
[x]. The one remaining item was a regression test guarding the SDK'sload_or_createdefault against a future swap to the ephemeralgenerate()— a settlement system can't use receipts whose signing keys rotate on restart.What changed
crates/attestly-python/tests/test_identity_restart.py(new) — two tests:test_identity_survives_restart— two separately spawned interpreters sharing atmp_pathidentities dir must print the same pubkey.test_restart_identity_is_per_agent— a third process with a differentagent_idmust print a different pubkey, ruling out a degenerate constant-key implementation passing the first test.docs/PRODUCTION_CHECKLIST.md— ticked the §3 item, pointing at the test.Placement
Binding-level (the recommended option in the dispatch prompt). The seam the checklist names is
wrapper.py:120, but the property that actually has to hold is thatAgentIdentity.load_or_createreturns a disk-backed key in a fresh process — testable without standing up aStateGraph.Existing coverage doesn't reach this:
test_api.py::test_load_or_create_persists_across_callsis same-process repeated calls, and the only othersubprocessuse (cross_language.py) spawns theattestly-verifybinary for the encoding contract. No cross-process identity test existed.The guard bites
Verified empirically, not just by reasoning — temporarily swapping the subprocess snippet to
AgentIdentity.generate(agent_id)fails the test, at two independent assertions (the identity file is never persisted, and the two pubkeys differ). The temporary edit was reverted; no production code was touched.Notes
tmp_pathonly; the realdata/identities/is never read or written.python-testtarget (pytest crates/attestly-python/tests).identity.rs,crates/attestly-python/src/lib.rs,wrapper.py, orservices/attestly-cloud/.Implements Step 3 of the production checklist — §3 Persistent agent identity.
🤖 Generated with Claude Code