Repository navigation
Identity contract: typed, self-verifying references (#42) - #45
Conversation
stele.identity gives each kind of evidence one typed reference with one
canonical string, and a resolver that checks it against the ledger, the
archive and the event chain:
stele:source:<source_id>
stele:snapshot:<file|tree>:<sha256>
stele:record:<record_id>@<artifact_hash>
stele:observation:<record_id>@<sha256>
stele:anchor:<record_id>@<artifact_hash>/<artifact>@<digest>#bytes=S-E
#pointer=P&render=R
stele:event:<seq>@<hash>
Every reference commits to content, so it resolves only while the evidence
it named is unchanged. parse_ref() is strict: one reference, one spelling.
An Observation (record, source, input snapshot, time) is new: what one run
saw, as a canonical record with its own digest.
ExtractionAdapter chunks now carry their anchor as `stele_anchor`, so a
consumer can check a quoted passage against the sealed evidence later.
BlobStore gains a public verify_snapshot(). CLI: python -m stele.identity
refs|resolve. Docs: docs/identity.md.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013p3FU6gUJ1MGinnmEgvHsm
|
I'm re-running the workflow once. If it fails the same way again, I'll treat the failure as real and investigate it properly. Generated by Claude Code Generated by Claude Code |
|
Taking over driving this PR to green.
I don't think they come from this PR:
That points to the runner's docker/gVisor setup during this run, not this PR. There is no fix to port. I'm re-running the job once. If it fails again, I'll treat it as this PR's failure and root-cause it. Generated by Claude Code |
|
Update: Evidence:
Why I can't settle it:
Proposed separate fix, so the next hang names the test and ends on its own:
Generated by Claude Code Generated by Claude Code |
|
On the hang at f989300 (
This commit's first run was cancelled and hasn't been re-run, so I'm re-running it once. If it hangs again, the per-test timeout and Generated by Claude Code |
Closes #42.
What
stele.identitygives each kind of evidence one typed reference with one canonical string, plus a resolver that checks the reference against the ledger, the archive and the event chain.SourceRefstele:source:<source_id>SourceSnapshotRefstele:snapshot:<file|tree>:<sha256>Snapshot(every blob re-hashed)RecordRefstele:record:<record_id>@<artifact_hash>ArtifactRecord(SEALED; INVALIDATED only on request)ObservationRefstele:observation:<record_id>@<sha256>Observation(new)AnchorRefstele:anchor:<record_id>@<artifact_hash>/<artifact>@<digest>#bytes=S-Eor#pointer=P&render=REventRefstele:event:<seq>@<hash>Event(the chain must verify with it as anchor)parse_ref()accepts a string only if formatting the parsed reference gives it back byte for byte. Anchor paths and pointers are percent-encoded.{record_id, source_id, snapshot, observed_at}: what one run saw, with its own digest. It resolves in any record state, because an observation stays true even if the run is later invalidated.ExtractionAdapterchunks now carrymetadata["stele_anchor"], so a consumer can later check a quoted passage against the sealed evidence.BlobStore.verify_snapshot(): a public wrapper around the existing re-hash check.python -m stele.identity refs|resolve.docs/identity.md, plus a line in the README docs table.Change from the issue text: the Observation reference includes the record id (
stele:observation:<record_id>@<sha256>), not just the digest. That way it can be resolved without an index from digest to observation.Tests
tests/test_identity.pyhas 49 tests:stele_anchorresolves to that chunk's own text.refsandresolve, with the exit codes.Local: 801 passed, 86 skipped.
ruffis clean on the changed files.Limits (also in the docs)
RecordRefchecks the archived tree object, not every artifact blob. AnAnchorRefre-hashes the one blob it reads.EventRefneeds the whole chain to verify, so a break anywhere stops every event from resolving.🤖 Generated with Claude Code
https://claude.ai/code/session_013p3FU6gUJ1MGinnmEgvHsm
Generated by Claude Code