campanile
campanile replays a repository's git history and reports its line-custody structure — which author strings held which file classes, and when that structure shifted.
Only diff STRUCTURE is read: hunk arithmetic, paths, author strings. Content lines are consumed by count and never inspected — custody is pure arithmetic over the recorded diffs.
The calibration case
uvx campanile replay xz
In the xz repository (CVE-2024-3094), custody of the tests file class inverted at first-parent chain index 1746 — 2023-03-13 — while overall custody never inverted. The payload that later shipped was carried in that same file class. This tool dates the structural shift; it makes no claim about intent, identity, or wrongdoing.
Calibration (2026Q3, the first 55-repo fleet): class-level custody inversions are ordinary (46 of 55 repos have them). Any class inverting while overall custody never does: 2 of 55. The tests class specifically — the xz signature — 0 of 55. The wider fleet below measures the same signature at production scale; a rate always travels with its denominator and its epoch.
Base rates
Measured on the sealed 406-repo top-PyPI fleet (2026Q4-wide), predictions staked before cloning. Three predicates, three separate questions. The bare xz shape in ANY file class — one class changes hands while the project overall never does: 64 of 406 healthy repositories. The same any-class shape with a 150-day recent-arrival condition on the ascending author: 36 of 406. The tests-class signature — the class that carried the xz payload, with no recent-arrival condition: 17 of 406, about 1 in 24. The earlier 55-repo calibration above measured this same tests-class signature at 0 of 55: a small fleet's zero and a wide fleet's 1-in-24 are the same physics at different denominators, and both publish. At these rates the pattern alone accuses the innocent; that is the measurement, and it is why a match is a question, never a finding.
The sealed October envelope
Predictions for the next quarterly sweep over the same fleet are already sealed: claims sha256 851d268b69b9e89ecaa1ebdd2c4d88e2720328e9469044db4a4e320d013c5fee, RFC 3161-witnessed 2026-08-24, unsealed October 2026. Whatever unseals either matches this hash or it does not; nobody — this project included — can edit it.
Verification is offline. The shipped xz fixture replays 3,054 commits with no network, no clone, and no model. --from-clone PATH recomputes the same result from a live
git clone at the pinned commit and refuses any other commit — proving the
fixture against the upstream history itself.
Your own repository
uvx campanile scan .
Reports the repository's custody vector: per-class custody shares
(integer-scaled), principal roles (the recorded author strings live in the
JSON vector and print only with --personas), and any inversion events.
Every inversion event carries its meaning in-schema:
- what this is: the date one author string's line custody in one file class first exceeded another's
- what this is not: not an accusation, not a compromise indicator, not an identity claim; author strings are repo-recorded personas
Every scan ends with the agent-held custody readout: the share of surviving lines held by identities presenting as AI coding agents versus non-generative automation — shares only, no names. Classification reads the record; it never makes an identity claim, and a person named Claude or Devin never classifies as software.
If someone files campanile output against your project
A campanile match is a structural measurement with a published base rate, never evidence of wrongdoing. The same shape appears in healthy, famous projects at the rates above; the usual cause is ordinary succession — someone takes over the tests while the founder keeps the whole. Reports speak in roles, and this project never publishes a named per-repository result — aggregates and sealed receipts only; whether a repository tells its own custody story is its maintainer's choice. If this section was linked at you, the number being cited asks a question about succession; it does not answer one about intent.
Verify
uvx campanile verify PATH
Dispatches on the file: a signed receipt (needs
pip install 'campanile[verify]'), a custody vector, or the fixture
manifest. All verification is offline.
What ships in the sdist
The full test suite and the structural fixture — pip download campanile --no-binary :all: gives a stranger everything needed to reproduce every
xz calibration number in this README, offline. The fleet numbers are
receipt-bound, not recomputable: the fleet bank is private under the
consent law, and the sealed receipt published beside the aggregate is what
a stranger verifies. The fixture contains git STRUCTURE only (shas, author
strings, paths, hunk arithmetic): no source code of the scanned repository
is redistributed.
Licensed Apache-2.0.
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 campanile-1.0.0.tar.gz.
File metadata
- Download URL: campanile-1.0.0.tar.gz
- Upload date:
- Size: 1.2 MB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/7.0.0 CPython/3.12.4
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
500532c34ce4d8c682b9c22b55f5e18ff3f7ca91d458ac03af02f35fa229d700
|
|
| MD5 |
b1458cdec4f64f2a994b671c80958e6a
|
|
| BLAKE2b-256 |
450985035ce0346cb257a30cf4defcd3623ee8f9f9667d2694a30237ebe3f135
|
File details
Details for the file campanile-1.0.0-py3-none-any.whl.
File metadata
- Download URL: campanile-1.0.0-py3-none-any.whl
- Upload date:
- Size: 1.2 MB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/7.0.0 CPython/3.12.4
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
59720eacd72cae6d6366febd575aa4cbb5d03940a1c526ed50f843552fee40fb
|
|
| MD5 |
f584e60d911d4e363e2d4c4506e6849b
|
|
| BLAKE2b-256 |
e3fb1571534d181515262ec3c508cc3543bae36714c69721733e94a73f7b8edb
|