Ecosystem metrics

Commits
977
up 19.6%
Releases
10
up 42.9%
Contributors
59
up 25.5%
Merges
16
down 30.4%

Repository Explorer

10635 commits in all time May 15, 2026 15:38 – Aug 13, 2026 15:38 UTC
chore(release): [skip ci] Liquid Client
# [1.0.0-canary.10](https://github.com/algorandfoundation/liquid-auth-js/compare/v1.0.0-canary.9...v1.0.0-canary.10) (2026-08-13)

### Bug Fixes

* security patches ([4cfb56f](https://github.com/algorandfoundation/liquid-auth-js/commit/4cfb56f1969d6efabb9fe99f41eb0399e093196c))
Git Commit 763efb0d Branch main Document 3/10 ++ 3 --
PhearZero liquid-auth
fix: security patches
Git Commit a003a0fd Branch chore/harden-existing-links Document 2/495 ++ 865 --
PhearZero nodekit
Merge 57e157e42ce6de54825a07dfe0b00b20d98203c1 into c64f88aab5aeeb5cfed35723d480ba8ca832be0d
Git Commit d9e976a0 Branch pull/187/merge Document 2/155 ++ 152 --
PhearZero nodekit
fix: bump dependencies
Git Commit 57e157e4 Branch chore/security-fixes Document 2/155 ++ 152 --
meetthosar devportal
docs: add AppSizeSponsor app field for AVM v13
Git Commit c3713684 Branch feat/allow-epp-schema-change Document 2/15 ++ 14 --
Merge 06bd3b8216abfccdbde129d2235a148d9430f673 into d2de6fadecafa220eaa47dbd00ea1221fc6af26f
Git Commit b87b2720 Branch pull/165/merge Document 83/12,754 ++ 12,224 --
meetthosar devportal
docs: add size-changing example to application update transaction
Git Commit c5049b58 Branch feat/allow-epp-schema-change Document 1/28 ++ 1 --
Refuse to sign a release whose tag and version disagree (#25)
sdk/pyproject.toml declared version = "0.1.0" while the repo carries tags
v0.2.0 and v0.2.1 - and both of those tags ALSO carry "0.1.0". Two independent
claims about which release this is, with nothing binding them.

The first real run of this workflow would therefore have built
trelyan_pq-0.1.0-*.whl, then SLSA-attested and Sigstore-signed those exact
bytes under a v0.2.2 tag. The attestation would have been cryptographically
valid and described the wrong release - which is worse than an unsigned
artifact, because it carries an assurance nobody can act on.

Nothing was published: release.yml was first committed 2026-06-18, AFTER both
tags (it is absent at v0.2.0 and v0.2.1), it has never run, and both GitHub
Releases have zero assets. The mislabelling was latent.

Bumps the declared version to 0.2.2 - verified by building: the wheel is now
trelyan_pq-0.2.2-py3-none-any.whl, previously it would have been 0.1.0 - and
adds a gate in the build job, before anything is compiled or signed, that
fails when the tag does not match. Simulated all three ways: v0.2.2 allowed,
v0.2.1 and v9.9.9 blocked.

Security impact: no crypto or protocol change. Closes a path where a signed,
SLSA-attested artifact could name a different release than its tag.
Git Commit c1e451f3 Branch main Document 2/23 ++ 1 --
meetthosar devportal
docs: note extra program pages can change after creation
Git Commit 75c86b2a Branch feat/allow-epp-schema-change Document 1/1 ++ 1 --
Merge 9a43d0904c72542968a74bbba96f3ef3e297fbd8 into 100dc5fa507537fffa496e22706c3a6c1e9bca2a
Git Commit f591b8cd Branch pull/599/merge Document 2/607 ++ 415 --
chore(deps): bump the all group across 1 directory with 8 updates
Bumps the all group with 8 updates in the / directory:

| Package | From | To |
| --- | --- | --- |
| [@algorandfoundation/tealscript](https://github.com/algorandfoundation/TEALScript) | `0.106.3` | `0.107.2` |
| [@typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin) | `8.64.0` | `8.66.0` |
| [prettier](https://github.com/prettier/prettier) | `3.9.5` | `3.9.6` |
| [rolldown](https://github.com/rolldown/rolldown/tree/HEAD/packages/rolldown) | `1.2.0` | `1.2.3` |
| [rolldown-plugin-dts](https://github.com/sxzz/rolldown-plugin-dts) | `0.23.2` | `0.28.0` |
| [semantic-release](https://github.com/semantic-release/semantic-release) | `25.0.7` | `25.0.9` |
| [tsx](https://github.com/privatenumber/tsx) | `4.23.1` | `4.23.9` |
| [typescript-eslint](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/typescript-eslint) | `8.64.0` | `8.66.0` |



Updates `@algorandfoundation/tealscript` from 0.106.3 to 0.107.2
- [Release notes](https://github.com/algorandfoundation/TEALScript/releases)
- [Commits](https://github.com/algorandfoundation/TEALScript/compare/0.106.3...0.107.2)

Updates `@typescript-eslint/eslint-plugin` from 8.64.0 to 8.66.0
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.66.0/packages/eslint-plugin)

Updates `prettier` from 3.9.5 to 3.9.6
- [Release notes](https://github.com/prettier/prettier/releases)
- [Changelog](https://github.com/prettier/prettier/blob/main/CHANGELOG.md)
- [Commits](https://github.com/prettier/prettier/compare/3.9.5...3.9.6)

Updates `rolldown` from 1.2.0 to 1.2.3
- [Release notes](https://github.com/rolldown/rolldown/releases)
- [Changelog](https://github.com/rolldown/rolldown/blob/main/CHANGELOG.md)
- [Commits](https://github.com/rolldown/rolldown/commits/v1.2.3/packages/rolldown)

Updates `rolldown-plugin-dts` from 0.23.2 to 0.28.0
- [Release notes](https://github.com/sxzz/rolldown-plugin-dts/releases)
- [Commits](https://github.com/sxzz/rolldown-plugin-dts/compare/v0.23.2...v0.28.0)

Updates `semantic-release` from 25.0.7 to 25.0.9
- [Release notes](https://github.com/semantic-release/semantic-release/releases)
- [Commits](https://github.com/semantic-release/semantic-release/compare/v25.0.7...v25.0.9)

Updates `tsx` from 4.23.1 to 4.23.9
- [Release notes](https://github.com/privatenumber/tsx/releases)
- [Changelog](https://github.com/privatenumber/tsx/blob/master/release.config.cjs)
- [Commits](https://github.com/privatenumber/tsx/compare/v4.23.1...v4.23.9)

Updates `typescript-eslint` from 8.64.0 to 8.66.0
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/typescript-eslint/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.66.0/packages/typescript-eslint)

---
updated-dependencies:
- dependency-name: "@algorandfoundation/tealscript"
  dependency-version: 0.107.2
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: all
- dependency-name: "@typescript-eslint/eslint-plugin"
  dependency-version: 8.66.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: all
- dependency-name: prettier
  dependency-version: 3.9.6
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: all
- dependency-name: rolldown
  dependency-version: 1.2.3
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: all
- dependency-name: rolldown-plugin-dts
  dependency-version: 0.28.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: all
- dependency-name: semantic-release
  dependency-version: 25.0.9
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: all
- dependency-name: tsx
  dependency-version: 4.23.9
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: all
- dependency-name: typescript-eslint
  dependency-version: 8.66.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: all
...

Signed-off-by: dependabot[bot] <support@github.com>
Git Commit 9a43d090 Branch dependabot/npm_and_yarn/all-13923b79ef Document 2/607 ++ 415 --
Refuse to sign a release whose tag and version disagree
sdk/pyproject.toml declared version = "0.1.0" while the repo carries tags
v0.2.0 and v0.2.1 - and both of those tags ALSO carry "0.1.0". Two independent
claims about which release this is, with nothing binding them.

The first real run of this workflow would therefore have built
trelyan_pq-0.1.0-*.whl, then SLSA-attested and Sigstore-signed those exact
bytes under a v0.2.2 tag. The attestation would have been cryptographically
valid and described the wrong release - which is worse than an unsigned
artifact, because it carries an assurance nobody can act on.

Nothing was published: release.yml was first committed 2026-06-18, AFTER both
tags (it is absent at v0.2.0 and v0.2.1), it has never run, and both GitHub
Releases have zero assets. The mislabelling was latent.

Bumps the declared version to 0.2.2 - verified by building: the wheel is now
trelyan_pq-0.2.2-py3-none-any.whl, previously it would have been 0.1.0 - and
adds a gate in the build job, before anything is compiled or signed, that
fails when the tag does not match. Simulated all three ways: v0.2.2 allowed,
v0.2.1 and v9.9.9 blocked.

Security impact: no crypto or protocol change. Closes a path where a signed,
SLSA-attested artifact could name a different release than its tag.
Git Commit 6e76dd80 Branch ci/release-tag-must-match-version Document 2/23 ++ 1 --
docs(PINNED_BUILD): the pin is the network's release, not staleness (#7)
Corrects the note added earlier on this branch, which was misleading in three
ways. All three were found by follow-up verification and adversarial review
after that note was written.

1. It said the pin is "several commits behind main" without the decisive fact:
   go-algorand's go.mod requires github.com/algorand/falcon v0.1.0, and that
   tag dereferences to exactly ce15e75b. It is the repo's ONLY tag. The pin is
   alignment with the code defining on-chain falcon_verify behaviour - which is
   the point for a signer whose output must be accepted by that verifier.
   main (956d9bc0) is 7 ahead / 0 behind, and UNRELEASED.

2. It called the PR #16 change "a malleability gap". Wrong term. Signature
   malleability means distinct valid signatures for one (key, message) pair;
   that does not occur on either build. It is non-canonical input acceptance in
   a parser. The original wording would have pointed an auditor at the wrong
   invariant.

3. It warned that TRELYAN's off-chain accept/reject "can diverge from what the
   chain accepts". Misleading: falcon_det1024_convert_compressed_to_ct is not
   bound by this SDK at all - trelyan_pq.falcon binds only
   shake256_init_prng_from_system, keygen, sign_compressed and
   verify_compressed. The affected function is unreachable from TRELYAN. It IS
   reachable in go-algorand via GetFixedLengthHashableRepresentation ->
   ConvertToCT, making it an upstream report rather than a local divergence.

Also records that bumping was tested, not assumed: both trees built with
FALCON_FPEMU=1 and compared over 120 signatures (12 keypairs x 10 message
shapes) plus the committed KAT - byte-identical throughout, each build
accepting the other's output. A bump would be safe but would make this repo
stricter than the deployed verifier for no reachable benefit.

Adds the more precise description of the underflow: sig_compressed_len - 2
wraps to SIZE_MAX on a 1-byte input, so the decoder receives a pointer past the
buffer with a claimed length of 2^64-1.

Docs only. Pinned commit and both digests unchanged.

Co-authored-by: Brandon Sellam <brandon.sellam@gmail.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Git Commit 3ff4f886 Branch main Document 1/29 ++ 10 --
docs(PINNED_BUILD): the pin is the network's release, not staleness
Corrects the note added earlier on this branch, which was misleading in three
ways. All three were found by follow-up verification and adversarial review
after that note was written.

1. It said the pin is "several commits behind main" without the decisive fact:
   go-algorand's go.mod requires github.com/algorand/falcon v0.1.0, and that
   tag dereferences to exactly ce15e75b. It is the repo's ONLY tag. The pin is
   alignment with the code defining on-chain falcon_verify behaviour - which is
   the point for a signer whose output must be accepted by that verifier.
   main (956d9bc0) is 7 ahead / 0 behind, and UNRELEASED.

2. It called the PR #16 change "a malleability gap". Wrong term. Signature
   malleability means distinct valid signatures for one (key, message) pair;
   that does not occur on either build. It is non-canonical input acceptance in
   a parser. The original wording would have pointed an auditor at the wrong
   invariant.

3. It warned that TRELYAN's off-chain accept/reject "can diverge from what the
   chain accepts". Misleading: falcon_det1024_convert_compressed_to_ct is not
   bound by this SDK at all - trelyan_pq.falcon binds only
   shake256_init_prng_from_system, keygen, sign_compressed and
   verify_compressed. The affected function is unreachable from TRELYAN. It IS
   reachable in go-algorand via GetFixedLengthHashableRepresentation ->
   ConvertToCT, making it an upstream report rather than a local divergence.

Also records that bumping was tested, not assumed: both trees built with
FALCON_FPEMU=1 and compared over 120 signatures (12 keypairs x 10 message
shapes) plus the committed KAT - byte-identical throughout, each build
accepting the other's output. A bump would be safe but would make this repo
stricter than the deployed verifier for no reachable benefit.

Adds the more precise description of the underflow: sig_compressed_len - 2
wraps to SIZE_MAX on a 1-byte input, so the decoder receives a pointer past the
buffer with a claimed length of 2^64-1.

Docs only. Pinned commit and both digests unchanged.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Git Commit 34b480da Branch pull/7/head Document 1/29 ++ 10 --
Gate the release on the tests passing (#20)
release.yml had no test job. The graph was

    build -> provenance -> cosign-sign -> pypi-publish

with `build`'s only substantive step being `python -m build`, and no occurrence
of `pytest` or verify_pinned_digest anywhere in the file. A version tag
therefore produced a signed, SLSA-attested, Rekor-logged artifact without one
test having run.

The attestations were never WRONG about this. SLSA provenance attests to build
provenance and does not claim tests passed, and RELEASES.md's verify commands
say exactly that. The gap is that "signed, attested, transparency-logged" reads
as "vouched for" to essentially every downstream consumer, and here it vouched
only for where the bytes came from.

Adds a `test` job and makes `build` need it, so nothing is built, signed or
attested until the suite is green.

Cross-workflow `needs:` does not exist in GitHub Actions, so ci.yml cannot gate
this workflow and the check has to be duplicated here. That is why this is a
small fast job rather than the full 3-OS matrix: a release gate has to be cheap
enough that nobody is tempted to route around it.

The step also asserts 15+ tests actually EXECUTED (17 locally). pytest exits 5
on "no tests collected", but a suite that silently shrank still exits 0 - and a
release gate that passes because nothing ran is worse than no gate at all,
because it ships a signature saying otherwise. Same reasoning as the guards in
the CI workflow, applied to the path that publishes.

Found by an adversarial claim-vs-enforcement sweep (TCE-23).

Co-authored-by: Brandon Sellam <brandon.sellam@gmail.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Git Commit 8082e6b4 Branch main Document 1/52 ++ 0 --
Delete an inert workflow copy and fix two dangling audit references (#19)
AUDIT_READINESS.md:172 points an auditor at `sdk/ci/ci.yml` as the repo's CI
definition. That file does not exist:

  git ls-files sdk/ci/  ->  release.yml, verify_pinned_digest.py

scripts/verify_all.sh:10 makes the same reference, claiming to "mirror the
sdk/ci/ci.yml signature-kat job recipe EXACTLY". Both now point at
.github/workflows/ci.yml, which is the file that actually runs. An auditor
following the readiness doc hits a 404 on its first CI citation.

sdk/ci/release.yml is deleted. GitHub Actions only executes YAML under
.github/workflows/, so it never ran, and it had drifted into being the WORSE
copy: ad20b42 SHA-pinned every action in the live workflow while this one kept
floating tags -

  actions/checkout@v5                vs  @fbc6f39... # v5.0.1
  actions/setup-python@v6            vs  @ece7cb0... # v6.0.0
  actions/upload-artifact@v4         vs  @ea165f8... # v4.6.2
  actions/download-artifact@v4       vs  @d3f86a1... # v4.3.0
  sigstore/cosign-installer@v4.1.0   vs  @ba7bc0a... # v4.1.0
  softprops/action-gh-release@v2.6.2 vs  @3bb1273... # v2.6.2

19 differing lines, all pins. An inert file is harmless right until someone
copies it back - and this is the release workflow, the one that signs
artifacts and publishes them.

Also gitignores .venv-contracts/, the local virtualenv the contract suite
needs. It was untracked and unignored, which is how a `git add -A` in this
directory sweeps 1757 site-packages files into a commit.

Nothing references the deleted file; verify_all.sh still parses (bash -n).

Found by an adversarial claim-vs-enforcement sweep (TCE-15).

Co-authored-by: Brandon Sellam <brandon.sellam@gmail.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Git Commit d52b0570 Branch main Document 4/5 ++ 182 --
Run the contract suite in CI (#18)
The contract suite has never run in CI. It is the only thing that exercises
inscription.py at all - falcon_verify on-chain, the C1 ownership conjunction,
write-once, cross-cell replay, the update_owner guards - and the only record
that it passed was a hand-run note in LOCALNET_VALIDATION_2026-06-01.md, dated
two weeks BEFORE 2ec798e changed the contract.

So the flagship's on-chain logic has had no automated coverage of any kind,
and the drift in 2ec798e went unnoticed partly for that reason.

It was never wired up because it needs three things at once:

  1. a LocalNet (algod on :4001)   - without it: 22x urllib.error.URLError
  2. the Falcon C library          - without it: 19x "Could not load Falcon
                                     shared library at './libfalcondet1024.so'"
  3. the committed typed client    - already in the repo

Both failure modes above were reproduced locally before writing this job, so
the recipe is derived from what actually breaks, not from guesswork. The
pinned compile venv (puyapy) is deliberately NOT needed: these tests deploy
the committed client, they do not rebuild the contract.

The library is built from the pinned tarball behind the same digest + FPEMU
gate signature-kat uses. These tests produce REAL det1024 signatures that the
AVM's falcon_verify opcode must accept, so the signer has to be the pinned
build for exactly the reason that job states.

Two guards, both from failures seen elsewhere in this workflow:

  * LocalNet health is polled before the suite runs. A half-started sandbox
    fails with connection errors that read like test failures, destroying the
    signal this job exists to provide.
  * The run asserts 20+ tests actually EXECUTED. pytest exits 5 on "no tests
    collected", but a suite that silently shrank to a handful still exits 0 -
    and "green meaning nothing ran" is the precise defect this job is here to
    end.

Co-authored-by: Brandon Sellam <brandon.sellam@gmail.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Git Commit e4675c93 Branch main Document 1/80 ++ 0 --
Run the contract suite in CI
The contract suite has never run in CI. It is the only thing that exercises
inscription.py at all - falcon_verify on-chain, the C1 ownership conjunction,
write-once, cross-cell replay, the update_owner guards - and the only record
that it passed was a hand-run note in LOCALNET_VALIDATION_2026-06-01.md, dated
two weeks BEFORE 2ec798e changed the contract.

So the flagship's on-chain logic has had no automated coverage of any kind,
and the drift in 2ec798e went unnoticed partly for that reason.

It was never wired up because it needs three things at once:

  1. a LocalNet (algod on :4001)   - without it: 22x urllib.error.URLError
  2. the Falcon C library          - without it: 19x "Could not load Falcon
                                     shared library at './libfalcondet1024.so'"
  3. the committed typed client    - already in the repo

Both failure modes above were reproduced locally before writing this job, so
the recipe is derived from what actually breaks, not from guesswork. The
pinned compile venv (puyapy) is deliberately NOT needed: these tests deploy
the committed client, they do not rebuild the contract.

The library is built from the pinned tarball behind the same digest + FPEMU
gate signature-kat uses. These tests produce REAL det1024 signatures that the
AVM's falcon_verify opcode must accept, so the signer has to be the pinned
build for exactly the reason that job states.

Two guards, both from failures seen elsewhere in this workflow:

  * LocalNet health is polled before the suite runs. A half-started sandbox
    fails with connection errors that read like test failures, destroying the
    signal this job exists to provide.
  * The run asserts 20+ tests actually EXECUTED. pytest exits 5 on "no tests
    collected", but a suite that silently shrank to a handful still exits 0 -
    and "green meaning nothing ran" is the precise defect this job is here to
    end.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Git Commit c52fe337 Branch pull/18/head Document 1/80 ++ 0 --
Say that the algo-pqc-kit KAT has produced zero comparisons (#24)
The module docstring reads as evidence: "ANY divergence in the Gaussian
sampler, rounding, or compressed encoding surfaces here as a byte mismatch.
That divergence-detection is the entire point of this KAT." Nothing in it has
ever run.

Two independent reasons, and the second is the one that gets missed:

  1. Nothing installs algo-pqc-kit. pyproject declares only `algorand` and
     `dev`; both CI test jobs run pip install -e ".[dev]". All three
     differential tests carry @requires_apk and skip on every run.
  2. Installing the package would not be enough. _ApkAdapter is wired to a
     GUESSED upstream API shape and degrades to NotImplementedError -> skip if
     that guess is wrong. The upstream API still has to be confirmed.

The fixture IS populated, so this is a working test wired to a dependency no
environment provides - which is why it reads green.

Adds TRELYAN_REQUIRE_INTEROP=1, mirroring TRELYAN_REQUIRE_KAT in
test_signature_kat.py, where a skip fails the job. CI does NOT set it, because
the dependency genuinely is not available. It exists so the inertness is
testable on demand rather than taken on trust, and so that pinning upstream
later is one environment variable away from being enforced.

No behaviour change: unset, the new test skips like the other three.

Co-authored-by: Brandon Sellam <brandon.sellam@gmail.com>
Git Commit 7d513e69 Branch main Document 1/59 ++ 0 --
Stop passing a signature where the SDK expects a private key (#23)
interop_algo_pqc_kit.py called

    c.inscribe(cell, artifact_hash, sig, b"ipfs://demo")

but inscribe()'s third positional parameter is a PRIVATE KEY: the client
re-derives M and signs internally (inscription.py:135-136). The signature
reached falcon.py:192 and raised "privkey must be 2305 bytes, got ...".

This happened in BOTH branches, so the flagship's documented interop example
had never run as shipped. Nothing caught it because no test in this repo
touches examples/ at all -- and the file's own "NOT yet executed end-to-end"
disclaimer was doing the job a test should have been doing.

The INTEROP_RECONCILED=True branch cannot be written against today's SDK
regardless of how the encoding question lands: submitting an externally
produced signature needs an inscribe_presigned() entry point, and none
exists. That branch now stops with an explicit error naming the missing API
instead of shipping code that cannot run.

Adds tests/test_examples_call_sites.py, which binds every example's SDK call
sites to the real inspect.signature and checks provenance: a value produced
by .sign() must never land in a `privkey` parameter. Arity alone cannot see
this -- a signature and a private key are both bytes and both fit the slot.

Security impact: none at runtime. Fail-closed, no key disclosure, nothing
deployed. The defect is that an audit-bound repo shipped an examples/ file
that could not execute, and had no mechanism that would ever say so.

Co-authored-by: Brandon Sellam <brandon.sellam@gmail.com>
Git Commit c20ba21a Branch main Document 2/238 ++ 22 --
One build_message per side, and a test that compares them (#22)
There were FOUR copies of the signed-message construction and no test
compared any two:

  sdk/src/trelyan_pq/message.py    the SDK's, published to PyPI
  contracts/falcon_det1024.py      the contracts-side signer
  contracts/deploy_testnet.py      a local copy - deleted here
  contracts/test_inscription.py    a local copy - deleted here

The deleted copies were not harmless duplication. deploy_testnet.py's omitted
the length checks entirely:

  return DOMAIN_TAG + app_id.to_bytes(8,"big") + cell_id.to_bytes(8,"big") \
         + artifact_hash + genesis

so a 31- or 33-byte artifact hash produced a 101- or 103-byte M and was signed
without complaint - in the script that signs REAL TestNet inscriptions. The
ABI's StaticArray[Byte, 32] stops that reaching the chain, which is why this is
low rather than high, but "a later layer catches it" is a mitigation, not a
reason for the earlier layer to be wrong.

Both files already imported falcon_det1024, so removing the local definitions
is a deletion, not a refactor.

Two implementations remain on purpose: contracts/ has to work without the SDK
installed. Two is a decision; four was an accident. What makes two safe is that
something compares them, which is what
sdk/tests/test_build_message_differential.py now does:

  - both implementations agree across six inputs, including uint64 max and the
    live TestNet vector
  - both reproduce the exact M the chain accepted for cell 763809098, read from
    the live inscription record - an anchor that is neither implementation's
    opinion but what TestNet actually took
  - both reject wrong-length artifact and genesis hashes, which is the
    divergence that actually existed
  - the two names resolve to different function objects, so the comparison
    cannot become a function compared with itself

Mutation-proved: flipping the contracts-side cell_id to little-endian turns 5
of the 15 tests red. Restored: 15 passed.

Fixes AUDIT_READINESS.md S2.3, which cited three tests as covering this; each
exercised one implementation and none compared two.

Contract suite still 22 passed on LocalNet.

Note for reviewers: the SDK suite reports 3 unrelated errors on this machine -
PermissionError on pytest's tmp_path directory, in test_seal.py and
test_isolated_signer.py, both last modified in 2ec798e and untouched here. It
is local file-locking, not this change; CI runs on Linux.

Found by an independent re-check of the risk register (TCE-26).

Co-authored-by: Brandon Sellam <brandon.sellam@gmail.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Git Commit 751398e3 Branch main Document 3/141 ++ 12 --
Test the freeze and manager guards at register_cell (#21)
register_cell asserts three things about a Cell ASA:

  :205  clawback == zero_address
  :206  freeze   == zero_address
  :207  manager  == zero_address

Only :205 had a test. test_register_rejects_clawback_cell's docstring claims
to cover "clawback (or freeze/manager)" but its fixture sets only `clawback`,
and the asserts are sequential, so it trips :205 and the other two are never
evaluated. The words freeze and manager appear in this file only in prose.
Deleting :206 and :207 turns nothing red.

Not cosmetic. A live freeze address can freeze the holder AFTER registration,
and a live manager can RE-ADD clawback and freeze afterwards - which restores
both AssetHoldingGet timing vectors the hardening closed at the source. The
manager guard is the load-bearing one: it is what makes the cell's config
immutable rather than merely clean at registration time.

Adds one fixture per guard and asserts WHICH guard fired, so a future
reordering of the three asserts cannot silently leave one uncovered again.

VERIFICATION IS INCOMPLETE, and deliberately not overstated: unlike the other
contract test added in this series, these two are NOT mutation-proved. Docker
Desktop on this machine wedged part-way through (orphaned runtime sockets that
cannot be deleted without a reboot - my own hard-kill of the process is the
likely cause), so LocalNet is unavailable and the suite cannot run here.

What HAS been checked:
  - both tests collect (24 collected, up from 22)
  - the asserted strings match the contract's messages exactly, one occurrence
    each in inscription.py and in the test
  - the fixtures mirror test_register_rejects_clawback_cell, which passes

What has NOT been checked: that they fail when :206/:207 are deleted. The CI
job in the contract-suite PR runs LocalNet successfully on a GitHub runner, so
that is where this should be confirmed.

Found by an adversarial claim-vs-enforcement sweep (TCE-19).

Co-authored-by: Brandon Sellam <brandon.sellam@gmail.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Git Commit f08d0926 Branch main Document 1/46 ++ 0 --