Chain-of-custody for AI-assisted analyses: an append-only, hash-chained trail of provenance, decisions, and executions, with integrity checks that flag when a reported result no longer follows from the current analysis.
Project description
auditlite
Chain-of-custody for AI-assisted analyses.
Traditional reproducibility asks: can the analysis be rerun? AI-assisted analysis adds a second question: can it be reconstructed? The final result can look coherent while the reasoning path is scattered across prompts, generated code, manual edits, reruns, copied outputs, and decisions nobody wrote down. Three months later — or one reviewer letter later — nobody can say which code the model wrote, why an exclusion happened, or whether Table 1 still comes from the current script.
auditlite keeps one append-only, hash-chained trail per project
(audit/trail.jsonl) and records what changed, why it changed, who or
what changed it, and whether the reported result still follows from the
current analysis.
What it is not. auditlite is not an AI detector — it records declared provenance and flags where declarations are missing. An intact hash chain shows the trail has not been rewritten after the fact; it does not show the recorded events are true. Checks report support for reconstruction, never correctness of the science.
Install
pip install auditlite
Python ≥ 3.10, no dependencies.
What gets recorded
| Kind | Captures |
|---|---|
provenance |
which code was human-written / AI-generated / AI-modified; model, version, prompt, context files, external tools (declared) |
decision |
why a variable, model, exclusion, transformation, or sensitivity analysis was chosen; whether an AI suggestion was accepted, edited, or rejected; whether the AI introduced a new analytic decision |
run |
exact script hash, command, Python + platform + package versions, declared seeds, input and output file hashes, exit code |
claim |
a manuscript table or figure tied to the run and file it came from |
deviation |
departures from the analysis plan, with the reason |
The checks
auditlite check runs seven integrity checks. Each answers three ways —
ok, flag, or not_checkable — because a check that lacks the events
it needs should say so rather than guess.
- chain — has the trail itself been edited, reordered, or truncated?
- script_drift — were scripts edited after their last recorded run?
- input_drift — does the data on disk still match what the runs consumed?
- stale_outputs — were outputs modified outside a recorded run?
- claim_freshness — do manuscript tables/figures still match the files they were recorded from?
- provenance_coverage — did any script execute without a declared origin?
- decision_rationale — were decisions recorded without a why (worst when the AI introduced them)?
Exit code 1 if anything is flagged, so it drops into CI or a pre-submission checklist as-is.
Workflow
auditlite init --project lung-registry-survival
# declare provenance when code enters the project
auditlite provenance analysis.py --origin ai_generated \
--model claude-sonnet-5 --prompt "primary survival analysis per SAP v2" \
--context sap_v2.md --context codebook.csv
# trace analytic decisions as they happen
auditlite decision --what exclusion --choice "drop ECOG 4" \
--why "n=3 in stratum, prespecified minimum is 10" \
--source ai_suggested --disposition edited
# run through auditlite so the execution is fingerprinted
auditlite run --input data.csv --output table1.csv --seed numpy=42 \
-- python analysis.py
# tie manuscript numbers to the run that produced them
auditlite claim table1 table1.csv
# before submission (or in CI)
auditlite check
auditlite report # writes audit/AUDIT.md
Same API from Python:
import auditlite
trail = auditlite.init(project="lung-registry-survival")
trail.provenance("analysis.py", origin="ai_modified", model="claude-sonnet-5")
trail.decision("model", "logistic regression", "binary outcome, prespecified")
trail.run(["python", "analysis.py"], inputs=["data.csv"], outputs=["table1.csv"])
trail.claim("table1", "table1.csv")
for result in auditlite.run_checks(trail):
print(result.check, result.status, result.detail)
The trail format
One JSON object per line. Every record carries the SHA-256 of the previous record, so any later edit breaks the chain from that point forward:
{"seq": 4, "ts": "2026-07-11T09:12:03+00:00", "kind": "run", "actor": "human",
"body": {"command": ["python", "analysis.py"], "scripts": [...], "inputs": [...],
"outputs": [...], "seeds": {"numpy": 42}, "exit_code": 0,
"environment": {"python": "3.12.4", "platform": "...", "packages": {...}}},
"prev": "9f2c…", "hash": "a41b…"}
Plain text, local-first, no server, no account. Commit audit/ next to
the analysis and the trail travels with the project.
Siblings
- recoverlite checks whether a planned method can recover its target.
- assesslite checks whether a conclusion survives plausible assumptions.
- auditlite checks whether the entire analytic chain can be reconstructed and defended.
License
Apache-2.0. © Heidi Helena Andersen.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file auditlite-0.1.0.tar.gz.
File metadata
- Download URL: auditlite-0.1.0.tar.gz
- Upload date:
- Size: 24.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
af41686e4b8692378576d5efccf05350baf531bf44793eb792cfe050b13d3d49
|
|
| MD5 |
676ee275feb406ecd05873958581a19e
|
|
| BLAKE2b-256 |
d429a0892f32ea637250828db4f7412db07b9340ac419bc7187e9aa00c6c6f2f
|
Provenance
The following attestation bundles were made for auditlite-0.1.0.tar.gz:
Publisher:
publish.yml on heidihelena/auditlite
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
auditlite-0.1.0.tar.gz -
Subject digest:
af41686e4b8692378576d5efccf05350baf531bf44793eb792cfe050b13d3d49 - Sigstore transparency entry: 2144165339
- Sigstore integration time:
-
Permalink:
heidihelena/auditlite@296b4fbba076a782474d5b35515b11e58bcdd00e -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/heidihelena
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@296b4fbba076a782474d5b35515b11e58bcdd00e -
Trigger Event:
release
-
Statement type:
File details
Details for the file auditlite-0.1.0-py3-none-any.whl.
File metadata
- Download URL: auditlite-0.1.0-py3-none-any.whl
- Upload date:
- Size: 22.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
61b38ea478d780f5e7fda28e61f69e566beb626f21feafae4d261e7067d368bd
|
|
| MD5 |
d9c4cefbd8c1651dadcaefe2b8704c2e
|
|
| BLAKE2b-256 |
530e43f676624e658de077643d0e9fb143937ece395de1fb4ec344625fd9a990
|
Provenance
The following attestation bundles were made for auditlite-0.1.0-py3-none-any.whl:
Publisher:
publish.yml on heidihelena/auditlite
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
auditlite-0.1.0-py3-none-any.whl -
Subject digest:
61b38ea478d780f5e7fda28e61f69e566beb626f21feafae4d261e7067d368bd - Sigstore transparency entry: 2144165563
- Sigstore integration time:
-
Permalink:
heidihelena/auditlite@296b4fbba076a782474d5b35515b11e58bcdd00e -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/heidihelena
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@296b4fbba076a782474d5b35515b11e58bcdd00e -
Trigger Event:
release
-
Statement type: