Maintaining this documentation¶
This page is for people changing the docs, the README, or the benchmark numbers. It is not needed to use Axiomize.
What builds what¶
| Source of truth | Built into |
|---|---|
README.md |
GitHub repository page, and the PyPI project description |
docs/*.md |
https://furox-art.github.io/axiomize/ via mkdocs build --strict |
skills/axiomize/*.md |
the agent skill pack; skills/axiomize/tools/*.py are also packaged inside the wheel |
.github/scripts/stage_docs.py |
copies a few skills/axiomize/ pages into docs/ before the site builds |
docs/rigor.md, docs/archetypes.md, docs/adaptive-workflow.md, docs/skill.md and
docs/examples.md are generated by .github/scripts/stage_docs.py and are git-ignored. Edit
the source under skills/axiomize/, never the generated page.
Pages that are not in the site nav¶
These exist under docs/, are excluded from the nav by exclude_docs in mkdocs.yml, and are
reachable only through direct repository links:
publishing-checklist.md— prepared text for third-party skill registries. Internal process, not user documentation.security-ci-contract.md— how CI enforces the security rules. Maintainer-facing.security-maintenance.md— dependency and advisory upkeep.security-release-checklist.md— release-time security steps.security-audit-1.11.2.md— engineering scope of the 1.11.2 hardening pass.
User-facing security documentation is docs/security.md, docs/security-non-goals.md and
docs/security-versioning.md, all of which stay in the nav.
Rules that CI enforces¶
python .github/scripts/readme_example_check.py fails the build when:
pip install axiomizeis missing fromREADME.mdordocs/quickstart.md.- A relative link or in-document anchor in
README.mdordocs/quickstart.mddoes not resolve. Absolutehttp(s)URLs are not fetched, so a broken external link is your responsibility. - The output block quoted in
README.mdordocs/quickstart.mddiffers from a realexamples/quickstart_sir.pyrun. - A documented CLI entry point is missing from the installed distribution or does not answer.
- A page named in the
mkdocs.ymlnav does not exist underdocs/.
python -m mkdocs build --strict fails the build on any broken internal link or missing page.
Adding a benchmark number¶
Anything numeric you publish has to be reproducible by someone else. Before adding a score:
- Record the commit, the sha256 of
skills/axiomize/tools/benchmark_runner.py,benchmarks/ideas.jsonandbenchmarks/rubric.md, the interpreter version and the run timestamp.docs/benchmark-results.mdcarries a table in exactly this shape; follow it. - Give the command that regenerates the number verbatim.
- State what the number does not prove. The automated layer checks report structure, not mathematics, and the rubric's real gate is a combined automatic plus human score that is not recorded in this repository.
- If a run cannot be reproduced, put it under a clearly labelled history heading and say why.
Do not present an illustrative or placeholder figure as a measurement.
Adding a worked example¶
Full examples follow the eight-phase structure in examples/epidemic-sir.md. Every example must
open with the parameter-provenance note, because the ranges in these files are illustrative and
carry no citation:
> **Parameter provenance:** the values below are illustrative. `lit.` / `data.` / `est.`
> mark the intended weight of a range; this file cites no external source. Reading material
> for the workflow, not a literature-backed model.
Then add the file to docs/example-gallery.md, otherwise nothing links to it.
Registry version claims in the README¶
pyproject.toml, src/axiomize/__init__.py, package.json, .github/pypi-release-trigger and
the first ## [version] heading in CHANGELOG.md are kept in lockstep by
.github/scripts/check_release_contract.py. That gate checks the repository. It says nothing
about what a registry is currently serving, so the two can legitimately differ for a while:
between a release commit landing on main and the release actually running, the registry still
serves the previous tarball.
Check the registry directly before making any claim about a published version:
# npm: latest tag and the full version list
curl -s https://registry.npmjs.org/axiomize \
| python -c "import json,sys; d=json.load(sys.stdin); print(d['dist-tags']['latest'], sorted(d['versions']))"
# npm: published digests for integrity checks
curl -s https://registry.npmjs.org/axiomize/1.12.4 \
| python -c "import json,sys; d=json.load(sys.stdin); print(d['dist']['integrity'], d['dist']['shasum'])"
# PyPI: latest version and per-file digests
curl -s https://pypi.org/pypi/axiomize/json \
| python -c "import json,sys; d=json.load(sys.stdin); print(d['info']['version']); [print(' ', u['filename'], u['digests']['sha256']) for u in d['urls']]"
The npm version badge follows the PyPI version. It is present only when the registry version
matches; otherwise the README must say which of the two states it is in, because a repository fix
is not a published fix. As of 1.12.5 both registries report 1.12.5 as latest, so the badge is
in place.
Supply-chain attestations: what exists and what does not¶
Do not write "published with provenance" without checking which registry you mean. The two
ecosystems differ here, and the npm package is the weaker of the pair. State at 1.12.5:
| Registry | Mechanism | Endpoint | State at 1.12.5 |
|---|---|---|---|
| PyPI | PEP 740 attestations, Sigstore/Fulcio + transparency log | https://pypi.org/integrity/<project>/<version>/<filename>/provenance |
present for both the wheel and the sdist |
| npm | Sigstore attestations, only with trusted publishing (OIDC/provenance) | https://registry.npmjs.org/-/npm/v1/attestations/<pkg>@<version> |
absent — 1.12.5 was published in token mode |
The asymmetry is real and worth being explicit about: PyPI 1.12.5 is attested, npm 1.12.5 is
not. Both PyPI files serve a bundle naming GitHub / Furox-Art/axiomize / release.yml,
environment pypi, predicate https://docs.pypi.org/attestations/publish/v1, one transparency-log
entry each, and the attested subject digest equals the digest of the file a consumer downloads.
The npm tarball is digest-verifiable only.
Reading a 404 correctly¶
The two registries use different URL shapes, and getting them wrong looks like an absent attestation when it is not:
# PyPI: PER-FILE. A directory-style URL names no artifact and 404s even when every
# file in that release is attested, so it proves nothing either way.
curl -s -o /dev/null -w '%{http_code}\n' \
https://pypi.org/integrity/axiomize/1.12.5/axiomize-1.12.5.tar.gz/provenance # 200
curl -s -o /dev/null -w '%{http_code}\n' \
https://pypi.org/integrity/axiomize/1.12.5/ # 404, meaningless
# npm: PER-VERSION.
curl -s -o /dev/null -w '%{http_code}\n' \
https://registry.npmjs.org/-/npm/v1/attestations/axiomize@1.12.5 # 404, genuinely absent
A 404 on a correct URL means no attestation for that artifact. Confirm the endpoint can also say "yes" and "no" before trusting either answer, by querying a package known to publish with trusted publishing and one known not to:
curl -s -o /dev/null -w '%{http_code}\n' https://registry.npmjs.org/-/npm/v1/attestations/vite@6.0.5 # 200
curl -s -o /dev/null -w '%{http_code}\n' https://registry.npmjs.org/-/npm/v1/attestations/left-pad@1.3.0 # 404
.github/scripts/check_provenance_claims.py runs that negative control on every CI run, and fails
if the endpoint stops discriminating.
Three different things, easy to confuse¶
| Thing | Where it lives | What it proves | What it does not prove |
|---|---|---|---|
| PEP 740 attestation | pypi.org/integrity/.../provenance |
A build workflow signed this exact file digest, through the Sigstore transparency log | That the build was correct, or that its inputs were trustworthy |
npm dist.signatures |
registry metadata, ECDSA under npm's registry key | The registry has not altered its own metadata for this version | Anything about the build. Every npm version has these, published or broken, token or OIDC. Transport integrity, not provenance. |
dist.integrity / digests.sha256 |
registry metadata | The bytes you downloaded are the bytes the registry published | That anyone built them, or from which commit |
A package can have all three, two, or none. 1.12.5 has an attestation on PyPI, registry metadata
signatures on npm, and digests on both.
What a consumer can verify today¶
PyPI — attestation or digest¶
# 1. attestation: confirm the bundle names this repo and workflow, and that its
# subject digest matches what you downloaded
curl -s https://pypi.org/integrity/axiomize/1.12.5/axiomize-1.12.5.tar.gz/provenance \
| python -c "import base64,json,sys; b=json.load(sys.stdin)['attestation_bundles'][0]; s=json.loads(base64.b64decode(b['attestations'][0]['envelope']['statement'])); print(b['publisher']); print([(x['name'],x['digest']['sha256']) for x in s['subject']])"
# 2. digest: pin it
pip download axiomize==1.12.5 --no-deps -d /tmp/ax
python -c "import hashlib; print(hashlib.sha256(open('/tmp/ax/axiomize-1.12.5-py3-none-any.whl','rb').read()).hexdigest())"
Compare against 2efd63813e5143b26991738cea643fd874491991e84543cf58706103f3715be5 (wheel) and
a0ac70a91b62b314e342f6a536e8bc00628795f24efeb60d03b577476932dff4 (sdist) at 1.12.5.
npm — digest only¶
npm view axiomize@1.12.5 dist.integrity
# sha512-3NVuYiKiEWaZVrEo1Ilp2e+d7yq5ZkuwqVsZUk7qfoY6LQM9D9eSWhhgZbMpdW/whwe4n1tQFavsX8bI1uk3zA==
npm view axiomize@1.12.5 dist.shasum
# d509b75fc654fb5f52e2e62a886fbdd54a31dfd6
npm pack axiomize@1.12.5, then hash the tarball with SHA-512 and base64 to compare against
dist.integrity. npm ci and npm install enforce dist.integrity automatically when a
package-lock.json pins it, which is the practical pinning mechanism on the npm side.
Pinning by digest is the honest recommendation for this project today. It proves the bytes you review are the bytes that were published. It does not prove who built them, so it does not replace the PyPI attestation, and the npm gap is not closed by it.
The in-repo integrity stories, which are different again¶
Do not conflate package provenance with the hashes this project computes at runtime:
axiomize.portable-bundle.v1export carries a SHA-256 over the canonical Model IR- the run ledger records a content hash and verifies it when a stored run is loaded
docs/benchmark-results.mdrecords commit plus runner, case-set and rubric sha256
Those bind artifacts to their content and inputs. None of them is a registry or build attestation.
Trusted publishers: one satisfied, one pending¶
Attestations require a registered trusted publisher; a long-lived API token cannot mint one. The
PyPI side is already configured and evidenced by the bundles above. The npm side is not, which is
the whole reason 1.12.5 is digest-only there.
| Registry | Owner / org | Repository | Workflow | Environment | Status |
|---|---|---|---|---|---|
| PyPI | Furox-Art |
axiomize |
release.yml |
pypi |
satisfied — evidenced by the 1.12.5 bundles naming this repo and workflow |
| npm | Furox-Art |
axiomize |
release.yml |
npm |
pending — no trusted publisher registered |
For npm, register the trusted publisher at npmjs.com for axiomize with owner Furox-Art,
repository axiomize, workflow filename release.yml and environment npm. The release job
already defaults to OIDC and already fails loudly if id-token:write is not granted; it currently
publishes in token mode because vars.npm_publish_mode is token. Once the publisher is
registered, unset that variable so the OIDC path is taken, then delete the NPM_TOKEN secret.
Once npm is republished that way, add its attestations URL to the table above and update this page's claim block. Do not backfill an attestation onto an already-published version: attestations are bound to a publish event.
Machine-checked claim block¶
.github/scripts/check_provenance_claims.py parses this block on every CI run and fails the build
if a documented attested value disagrees with the live registry. Editing the value here to match
what you want to be true does not help: the check reads the registry, not the file.
The current block, verified 2026-10-04 against the live endpoints:
[
{"registry": "pypi", "package": "axiomize", "version": "1.12.5",
"filename": "axiomize-1.12.5-py3-none-any.whl", "attested": true},
{"registry": "pypi", "package": "axiomize", "version": "1.12.5",
"filename": "axiomize-1.12.5.tar.gz", "attested": true},
{"registry": "npm", "package": "axiomize", "version": "1.12.5", "attested": false}
]
How the check behaves, so it is not mistaken for something weaker than it is:
- a claim of
attested: truethat resolves to 404 fails - a claim of
attested: falsethat resolves to a real bundle fails - a 200 that is not a parseable bundle with a
publisherand at least one attestation fails - a registry that cannot be reached at all fails the check; an unconfirmed claim never passes silently
- the npm endpoint is negative-controlled against
left-pad@1.3.0on every run; if that stops returning 404 the gate fails, because a 200 would no longer mean anything - removing this block entirely fails, since a claim that cannot be parsed cannot be trusted
Run it locally with python .github/scripts/check_provenance_claims.py. Use --offline only to
inspect the table without network access; CI never passes that flag.
PyPI description sync (manual step)¶
README.md is the readme in pyproject.toml, so the PyPI long description is whatever
README.md said at the moment the release wheel was built. Nothing republishes it
automatically, which means README edits merged after a release do not reach the PyPI page until
the next release. That is the whole reason the published description can lag the repository.
After each release, confirm the two agree:
# what PyPI currently renders as the description
curl -s https://pypi.org/pypi/axiomize/json | python -c "import json,sys; print(json.load(sys.stdin)['info']['description'])"
# what this checkout would render
python -c "print(open('README.md', encoding='utf-8').read())"
If they differ, the published description is stale. The only correct fix is a new release built
from a commit whose README.md is current; do not edit the PyPI page by hand, and do not upload
a file to a released version. Until that release exists, the repository README is the accurate
document.
Checklist before tagging:
- [ ]
README.mdstates the version being released. - [ ]
docs/quickstart.mdandREADME.mdoutput blocks match a real run. - [ ]
CHANGELOG.mdhas an entry for the new version. - [ ]
CITATION.cffversionanddate-releasedmatch the release. - [ ] PyPI metadata refreshed and re-checked with the commands above.
After tagging, confirm the registries actually moved. This is the step that catches a release that published Python but not npm, or the reverse:
- [ ]
curl -s https://pypi.org/pypi/axiomize/jsonreports the new version, and each file'sprovenancelink returns 200. - [ ]
curl -s https://registry.npmjs.org/axiomizelists the new version inversionsand the new version underdist-tags.latest. - [ ]
node --checkpasses onindex.jsandbin/axiomize.jsas published, not just in the repo. - [ ] If npm was published with trusted publishing, its attestations URL returns 200. If it was published with a token, the README table says so rather than implying provenance.
Local commands¶
pip install -r requirements-test.txt
pip install -e .
pip install mkdocs-material
pytest tests/ -v # test suite
python skills/axiomize/tools/check_skill.py # skill frontmatter, links, compile
python .github/scripts/check_release_contract.py # version lockstep
python .github/scripts/stage_docs.py # stage generated docs pages
python .github/scripts/readme_example_check.py # README/docs claims vs real behavior
python -m mkdocs build --strict # site build, warnings are failures