feat(release): automate per-crate releases and version contract artifacts (ENG-522)#528
Conversation
…acts Nothing in this workspace was released anywhere: templar-backend pinned seven crates to a raw dev commit because no release tag contained `gateway/`, tags used three naming schemes, and the only GitHub Release was a draft. Add a release cycle built on release-plz. Every crate versions itself from its own commit history; merging the standing "chore: release" PR is the trigger. Crates fall into three tiers: published (the 17-crate closure external consumers import), tagged-only (contracts, services, tools), and internal (mocks, fuzz, test scaffolding). Publishing to crates.io is configured but deferred: `templar-common` depends on the RedStone SDK as a git dependency, and Cargo refuses to publish any crate with one — on crates.io or a private registry. Tier A is therefore `git_only` for now, so consumers pin per-crate tags instead of a commit hash. Unblocking is a config flip, no code changes. See RELEASING.md. Contract WASM blobs become immutable per-version releases under res/near/<target>/<version>/. This replaces a single overwritten blob per contract, and folds in the five historical blobs that were hand-maintained as include_bytes! consts in gateway/testing — outside the catalog and outside any drift check. Two of those exposed a real conflict: proxy-oracle 0.3.0 and proxy-governance 0.1.0 each had two distinct binaries claiming the version (a local rebuild and the bytes actually deployed on mainnet), with current source matching neither. The on-chain bytes are recorded as canonical; the stale rebuilds are dropped. Drift checking grows from two checks to five, and a tag workflow rebuilds each released contract reproducibly and requires a byte-for-byte match. Also folds in ENG-362: the workspace relicenses from MIT to GPL-3.0-only, with the twelve vault crates that already declared GPL now inheriting it, so there is a single source of truth. ENG-522, ENG-362 Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
The bytes deployed on mainnet are now recorded as the canonical 0.3.0 (oracle) and 0.1.0 (governance) releases. Source has moved past both since ENG-481 changed the contracts without a version bump, so the current source is a new release rather than a rewrite of a shipped one. Blobs are cut in the following commit; the artifact drift check is red until then, which is the intended tripwire. ENG-522 Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
Reproducibly built from the previous commit via `just artifact-release proxy-oracle`. First release cut through the new flow. ENG-522 Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
Reproducibly built via `just artifact-release proxy-governance`. Completes the version bump: source, crate version, and released bytes now agree for both contracts, and the artifact drift check is green again. ENG-522 Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
The tag workflow as first written could never have passed. `cargo near build reproducible-wasm` embeds the source commit into the WASM per NEP-330, so identical source built at two different commits yields different bytes — measured here as f03b159e at the build commit versus b0a6ff7c one commit later. Since a blob is necessarily committed *before* the tag that releases it exists, rebuilding at the tag would have mismatched on every release. Record the build commit on each release and rebuild there instead. Verified end-to-end: rebuilding proxy-oracle 0.4.0 at its recorded commit reproduces the committed blob byte-for-byte. Legacy releases — bytes recovered from deployed mainnet contracts, whose provenance predates this field — carry an empty commit. Their hash pin is still enforced, but the workflow reports that they cannot be rebuilt rather than pretending to verify them. ENG-522 Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
Review pass over the first iteration. Net -216 lines, and two real defects. Keep test-only bytes out of production. `embedded_bytes` routed through `embedded_bytes_for_version`, whose runtime `&str` match made every historical blob reachable, so ~2 MB of migration-test fixtures landed in the shipped gateway binary. It is a direct match on the newest release again; the linked dispatch binary drops from 19 embedded modules to 15. A new `embedded_bytes_matches_current_release` test keeps the two lists agreeing, replacing an `expect` and its clippy allow. Drop the dead `workflow_dispatch` path in release-artifacts.yml: it wrote `outputs.artifact`, which nothing read, while the consumers read `package` and `version`, which it never set. Also skip cleanly when a tagged `*-contract` crate has no catalogued WASM (the Soroban ones do not) rather than failing a legitimate release. `source_commit` becomes `Option` instead of `""`. The sentinel dodged the invariant layer — release well-formedness checked the sha256 length but could not check an empty string — and made "skip verification" the same value a typo produces. `Some` arms are now asserted to be full 40-char shas. Delete 17 inert release-plz `[[package]]` blocks that restated the workspace default and would have rotted unnoticed; the Tier C blocks stay, since `release` defaults to true. Collapse the `include_bytes!` arms from nine lines of `concat!` to one literal each. Fold the crate version into `--print-metadata` so `artifact-release.sh` drops its inline Python. Delete the unused `release_dir`, the README table duplicating `ArtifactId::ALL`, and `synchronize` from the PR-title trigger, which cannot change a title. ENG-522 Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
📝 WalkthroughSummary
VerificationFormatting, clippy, artifact drift checks, fast tests, sandbox tests, and reproducible artifact validation were run. Review focus
WalkthroughThe pull request adds versioned, reproducibly verified contract artifacts; release-plz and GitHub Release automation; conventional PR-title validation; workspace license and package metadata updates; and migration tests that consume catalogued historical WASM releases. ChangesVersioned artifact catalog and release tooling
Release automation and repository guidance
Workspace and package metadata
Estimated code review effort: 4 (Complex) | ~60 minutes Sequence Diagram(s)sequenceDiagram
participant Developer
participant ReleasePlz
participant GitHubActions
participant ArtifactCatalog
participant GitHubRelease
Developer->>ReleasePlz: merge release PR on dev
ReleasePlz->>GitHubActions: create package and contract tags
GitHubActions->>ArtifactCatalog: resolve release metadata and source_commit
GitHubActions->>ArtifactCatalog: rebuild and compare committed WASM
GitHubActions->>GitHubRelease: upload verified WASM and checksums
Possibly related PRs
🚥 Pre-merge checks | ✅ 2✅ Passed checks (2 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Warning Review ran into problems🔥 ProblemsErrors were encountered while retrieving linked issues. Errors (1)
Comment |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 0241c8422c
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| "0.4.0", | ||
| "f03b159e9132a59ab929463866c672b734a5950fb41520ec33ad7a97030d5770", | ||
| Some("ea0a6357c89748e64b43036b776cd9459078af00") |
There was a problem hiding this comment.
Keep recorded source commits reachable after squash merge
These new reproducible releases record source_commit values that are not ancestors of the reviewed commit (I checked ea0a6357... and 9d447741... with git merge-base --is-ancestor). Because PRs are squash-merged, those intermediate commit objects can disappear once the branch is deleted, and .github/workflows/release-artifacts.yml later does git worktree add ../verify "$source_commit" from a tag checkout; the proxy-oracle/proxy-governance release verification will then fail before it can rebuild the blob. Record a commit that will be reachable from the release tag, or otherwise retain/fetch the exact source commits.
AGENTS.md reference: AGENTS.md:L76-L76
Useful? React with 👍 / 👎.
| git worktree add ../verify "$source_commit" | ||
| (cd ../verify && cargo near build reproducible-wasm --manifest-path "${source_path}/Cargo.toml") |
There was a problem hiding this comment.
Install cargo-near before invoking cargo near
On the release-tag runner this job only installs the Rust toolchain with dtolnay/rust-toolchain; unlike the existing node test workflow, it never installs the cargo-near subcommand before calling cargo near build reproducible-wasm. Any catalogued release with a source_commit will therefore fail with Cargo's “no such command: near” instead of verifying or uploading the artifact.
Useful? React with 👍 / 👎.
| - name: Attach artifacts to the release | ||
| env: | ||
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||
| REF_NAME: ${{ github.ref_name }} | ||
| run: gh release upload "$REF_NAME" dist/* --clobber |
There was a problem hiding this comment.
Skip uploads when no artifact was produced
For tags that match this workflow but are not NEAR artifacts in ArtifactId::ALL—for example the Soroban contract packages that release-plz will tag by default—the metadata step exits successfully before creating dist/, but this unconditional upload still runs and fails on dist/*. Either narrow the tag trigger or gate the upload step on an output that says a blob was actually copied.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Warning
CodeRabbit couldn't request changes on this pull request because it doesn't have sufficient GitHub permissions.
Please grant CodeRabbit Pull requests: Read and write permission and re-run the review.
Actionable comments posted: 5
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.github/workflows/release-artifacts.yml:
- Around line 86-89: Update the release workflow’s verification step to create
and set a step output only when dist/ is created and artifact verification
should proceed; leave it unset on the intentional early-exit paths for Soroban
contracts and legacy blobs. Add an if condition to the upload step so it runs
only when that output indicates verification produced dist/ contents, preventing
unconditional dist/* uploads.
In @.github/workflows/release-plz.yml:
- Around line 15-20: Update the RELEASE_PLZ_TOKEN configuration documentation
and usage in the release workflow to require the PAT explicitly, removing the
fallback to GITHUB_TOKEN. Ensure the workflow fails clearly when
RELEASE_PLZ_TOKEN is unset rather than proceeding unattended with reduced
release validation.
In `@contract/artifacts/src/embedded.rs`:
- Around line 183-204: Update the assertion in
embedded_bytes_matches_current_release to compare the byte-slice contents
returned by embedded_bytes() and embedded_bytes_for_version() using equality,
replacing the pointer-identity check while preserving the existing failure
message and validation flow.
In `@contract/artifacts/src/prebuild.rs`:
- Around line 47-58: Add mutual conflicts_with configuration to the
print_metadata and print_release CLI arguments so they cannot be supplied
together. Preserve each flag’s existing output behavior when used independently,
ensuring conflicting invocations are rejected instead of selecting the metadata
branch.
In `@README.md`:
- Around line 43-46: Update the release PR title reference in the Contributing
section to use a title that satisfies the documented type(scope): summary
format, such as chore(release): release, while preserving the existing release
workflow description.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: 29945415-c1f1-493f-bcde-b88d2346adae
⛔ Files ignored due to path filters (22)
Cargo.lockis excluded by!**/*.lockcontract/artifacts/res/near/mock_ft/0.0.0/mock_ft.wasmis excluded by!**/*.wasmcontract/artifacts/res/near/mock_mt/0.0.0/mock_mt.wasmis excluded by!**/*.wasmcontract/artifacts/res/near/mock_oracle/0.0.0/mock_oracle.wasmis excluded by!**/*.wasmcontract/artifacts/res/near/mock_receiver/1.2.1/mock_receiver.wasmis excluded by!**/*.wasmcontract/artifacts/res/near/mock_ref/1.2.1/mock_ref.wasmis excluded by!**/*.wasmcontract/artifacts/res/near/templar_lst_oracle_contract/1.2.1/templar_lst_oracle_contract.wasmis excluded by!**/*.wasmcontract/artifacts/res/near/templar_market_contract/1.4.0/templar_market_contract.wasmis excluded by!**/*.wasmcontract/artifacts/res/near/templar_proxy_oracle_near_contract/0.1.0/templar_proxy_oracle_near_contract.wasmis excluded by!**/*.wasmcontract/artifacts/res/near/templar_proxy_oracle_near_contract/0.3.0/templar_proxy_oracle_near_contract.wasmis excluded by!**/*.wasmcontract/artifacts/res/near/templar_proxy_oracle_near_contract/0.4.0/templar_proxy_oracle_near_contract.wasmis excluded by!**/*.wasmcontract/artifacts/res/near/templar_proxy_oracle_near_contract/templar_proxy_oracle_near_contract.wasmis excluded by!**/*.wasmcontract/artifacts/res/near/templar_proxy_oracle_near_governance_contract/0.1.0/templar_proxy_oracle_near_governance_contract.wasmis excluded by!**/*.wasmcontract/artifacts/res/near/templar_proxy_oracle_near_governance_contract/0.2.0/templar_proxy_oracle_near_governance_contract.wasmis excluded by!**/*.wasmcontract/artifacts/res/near/templar_proxy_oracle_near_governance_contract/templar_proxy_oracle_near_governance_contract.wasmis excluded by!**/*.wasmcontract/artifacts/res/near/templar_pyth_lazer_adapter_contract/0.1.0/templar_pyth_lazer_adapter_contract.wasmis excluded by!**/*.wasmcontract/artifacts/res/near/templar_redstone_adapter_contract/0.2.0/templar_redstone_adapter_contract.wasmis excluded by!**/*.wasmcontract/artifacts/res/near/templar_registry_contract/1.2.1/templar_registry_contract.wasmis excluded by!**/*.wasmcontract/artifacts/res/near/templar_universal_account_contract/0.2.0/templar_universal_account_contract.wasmis excluded by!**/*.wasmcontract/artifacts/res/near/templar_universal_account_contract/0.4.0/templar_universal_account_contract.wasmis excluded by!**/*.wasmcontract/artifacts/res/near/templar_universal_account_contract/0.5.0/templar_universal_account_contract.wasmis excluded by!**/*.wasmcontract/artifacts/res/near/templar_vault_contract/1.2.1/templar_vault_contract.wasmis excluded by!**/*.wasm
📒 Files selected for processing (91)
.github/workflows/pr-title.yml.github/workflows/publish-soroban-vault-cli.yml.github/workflows/release-artifacts.yml.github/workflows/release-plz.ymlAGENTS.mdCargo.tomlLICENSEREADME.mdRELEASING.mdclient/vault/Cargo.tomlcommon/Cargo.tomlcontract/artifacts/README.mdcontract/artifacts/src/embedded.rscontract/artifacts/src/ids.rscontract/artifacts/src/lib.rscontract/artifacts/src/prebuild.rscontract/market/Cargo.tomlcontract/proxy-oracle/governance-kernel/Cargo.tomlcontract/proxy-oracle/kernel/Cargo.tomlcontract/proxy-oracle/near/common/Cargo.tomlcontract/proxy-oracle/near/contract/Cargo.tomlcontract/proxy-oracle/near/contract/tests/common/mod.rscontract/proxy-oracle/near/contract/tests/migrate_mainnet.rscontract/proxy-oracle/near/governance-common/Cargo.tomlcontract/proxy-oracle/near/governance-contract/Cargo.tomlcontract/proxy-oracle/near/governance-contract/tests/upgrade_ordering.rscontract/proxy-oracle/near/lst-contract/Cargo.tomlcontract/proxy-oracle/soroban/common/Cargo.tomlcontract/proxy-oracle/soroban/contract/Cargo.tomlcontract/proxy-oracle/soroban/governance-common/Cargo.tomlcontract/proxy-oracle/soroban/governance-contract/Cargo.tomlcontract/proxy-oracle/soroban/sep40-adapter-contract/Cargo.tomlcontract/pyth-lazer/contract/Cargo.tomlcontract/pyth-lazer/verifier/Cargo.tomlcontract/redstone-adapter/Cargo.tomlcontract/registry/Cargo.tomlcontract/universal-account/Cargo.tomlcontract/universal-account/tests/migration.rscontract/vault/curator-primitives/Cargo.tomlcontract/vault/kernel/Cargo.tomlcontract/vault/macros/Cargo.tomlcontract/vault/near/Cargo.tomlcontract/vault/soroban/Cargo.tomlcontract/vault/soroban/blend-adapter/Cargo.tomlcontract/vault/soroban/curator-proxy/Cargo.tomlcontract/vault/soroban/custodial-adapter/Cargo.tomlcontract/vault/soroban/governance/Cargo.tomlcontract/vault/soroban/proxy-4626/Cargo.tomlcontract/vault/soroban/share-token/Cargo.tomlcontract/vault/soroban/shared-types/Cargo.tomlgateway/artifacts-dispatch/Cargo.tomlgateway/artifacts-dispatch/src/lib.rsgateway/artifacts-spec/Cargo.tomlgateway/artifacts-spec/src/artifact.rsgateway/catalog/Cargo.tomlgateway/client/Cargo.tomlgateway/core/Cargo.tomlgateway/macros/Cargo.tomlgateway/methods-dispatch/Cargo.tomlgateway/methods-spec/Cargo.tomlgateway/oracle-updates-dispatch/Cargo.tomlgateway/oracle-updates-spec/Cargo.tomlgateway/runtime/Cargo.tomlgateway/store/Cargo.tomlgateway/testing/Cargo.tomlgateway/testing/src/lib.rsgateway/testing/src/sandbox.rsgateway/testing/src/wasm.rsgateway/types/Cargo.tomljustfilemock/receiver/Cargo.tomlmock/ref/Cargo.tomlprimitives/Cargo.tomlrelease-plz.tomlscript/artifact-release.shscript/check-artifact-drift.shservice/accumulator/Cargo.tomlservice/funding-bridge/Cargo.tomlservice/gateway/Cargo.tomlservice/gateway/src/rpc/tests/artifact_tests.rsservice/liquidator/Cargo.tomlservice/market-monitor/Cargo.tomlservice/redstone-bridge/Cargo.tomlservice/relayer/Cargo.tomlservice/relayer/tests/relayer.rstools/harvest-static-yield/Cargo.tomltools/manager/Cargo.tomltools/market-config-cli/Cargo.tomltools/soroban-vault-cli/Cargo.tomltools/tools-common/Cargo.tomluniversal-account/Cargo.toml
| --print-metadata --artifact "$PACKAGE" 2>/dev/null); then | ||
| echo "::notice::${PACKAGE} has no catalogued WASM artifact (e.g. a" \ | ||
| "Soroban contract); nothing to verify." | ||
| exit 0 |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟠 Major | ⚡ Quick win
Skip the upload when verification is intentionally skipped.
Both supported early-exit paths leave no dist/ directory, but the next step unconditionally uploads dist/*. Soroban package tags and legacy blobs therefore fail after reporting that they were intentionally skipped. Set a step output only after creating dist/, and gate the upload step on it.
Also applies to: 108-112, 133-137
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.github/workflows/release-artifacts.yml around lines 86 - 89, Update the
release workflow’s verification step to create and set a step output only when
dist/ is created and artifact verification should proceed; leave it unset on the
intentional early-exit paths for Soroban contracts and legacy blobs. Add an if
condition to the upload step so it runs only when that output indicates
verification produced dist/ contents, preventing unconditional dist/* uploads.
| # RELEASE_PLZ_TOKEN optional PAT. A PR opened with the default | ||
| # GITHUB_TOKEN does NOT trigger other workflows, so | ||
| # without this the Release PR never runs `test.yml` and | ||
| # you would be merging an unverified release. Falls back | ||
| # to GITHUB_TOKEN so the workflow still works unattended. | ||
| # CARGO_REGISTRY_TOKEN only consulted once crates leave `git_only` mode. |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '\n## release-plz.yml\n'
cat -n .github/workflows/release-plz.yml | sed -n '1,220p'
printf '\n## release-artifacts.yml\n'
cat -n .github/workflows/release-artifacts.yml | sed -n '1,220p'
printf '\n## release workflow refs\n'
rg -n "RELEASE_PLZ_TOKEN|GITHUB_TOKEN|workflow_run|release-artifacts|release-plz|release tags|tag" .github/workflows -SRepository: Templar-Protocol/contracts
Length of output: 17001
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Look for any documentation or comments that state how release-plz tokens are expected to behave.
rg -n "RELEASE_PLZ_TOKEN|release-plz|release artifacts|GITHUB_TOKEN.*trigger|does NOT trigger|workflow_run|tag-triggered" -S .Repository: Templar-Protocol/contracts
Length of output: 2836
🏁 Script executed:
#!/bin/bash
set -euo pipefail
cat -n RELEASING.md | sed -n '88,108p'Repository: Templar-Protocol/contracts
Length of output: 1242
Require RELEASE_PLZ_TOKEN here. The GITHUB_TOKEN fallback suppresses the release PR’s downstream test.yml run and the tag-triggered release-artifacts.yml check, so a release can ship without those gates. Make the PAT mandatory instead of falling back silently.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.github/workflows/release-plz.yml around lines 15 - 20, Update the
RELEASE_PLZ_TOKEN configuration documentation and usage in the release workflow
to require the PAT explicitly, removing the fallback to GITHUB_TOKEN. Ensure the
workflow fails clearly when RELEASE_PLZ_TOKEN is unset rather than proceeding
unattended with reduced release validation.
| #[test] | ||
| fn embedded_bytes_matches_current_release() { | ||
| for artifact in ArtifactId::ALL.iter().map(|id| id.metadata()) { | ||
| let via_version = artifact | ||
| .id | ||
| .embedded_bytes_for_version(artifact.version()) | ||
| .unwrap_or_else(|| { | ||
| panic!( | ||
| "{} has no blob for its current version {}", | ||
| artifact.package_name, | ||
| artifact.version(), | ||
| ) | ||
| }); | ||
| assert!( | ||
| std::ptr::eq(artifact.id.embedded_bytes(), via_version), | ||
| "{}: `embedded_bytes` does not point at the current release ({}). \ | ||
| Update its arm in `ArtifactId::embedded_bytes`.", | ||
| artifact.package_name, | ||
| artifact.version(), | ||
| ); | ||
| } | ||
| } |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🌐 Web query:
Does LLVM's ConstantMerge pass run at opt-level 0 (rustc dev/debug profile)?
💡 Result:
No, LLVM's ConstantMerge pass does not run at opt-level 0 [1][2]. In the context of both LLVM's internal optimization pipelines and rustc's compilation profiles, opt-level 0 is designed to minimize optimizations to improve compilation speed and debuggability [3][4][5]. Technical documentation and discussions within the LLVM project confirm that ConstantMerge is applied at optimization levels O1 and higher, but it is explicitly excluded from the O0 pipeline [1][2]. Consequently, when compiling Rust code with the default dev profile (which uses opt-level 0), the ConstantMerge pass is not executed [3][5].
Citations:
- 1: https://reviews.llvm.org/D150218
- 2: https://lists.llvm.org/pipermail/llvm-commits/Week-of-Mon-20250120/1551734.html
- 3: https://dev-doc.rust-lang.org/stable/rustc/codegen-options/index.html
- 4: https://doc.rust-lang.org/stable/cargo/reference/profiles.html
- 5: https://doc.rust-lang.org/book/ch14-01-release-profiles.html
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Inspect the relevant file and nearby symbols.
file='contract/artifacts/src/embedded.rs'
wc -l "$file"
sed -n '1,260p' "$file"Repository: Templar-Protocol/contracts
Length of output: 10886
🏁 Script executed:
#!/bin/bash
set -euo pipefail
file='contract/artifacts/src/embedded.rs'
# Map the relevant functions first, then inspect only the implementation slices.
ast-grep outline "$file" --view expanded || true
printf '\n--- embedded_bytes implementations ---\n'
rg -n "fn embedded_bytes|fn embedded_bytes_for_version|include_bytes!" "$file" -n -A8 -B4
printf '\n--- ArtifactId metadata/current definitions ---\n'
rg -n "current\\(|embedded_bytes\\(|embedded_bytes_for_version\\(" contract/artifacts/src -n -A6 -B3Repository: Templar-Protocol/contracts
Length of output: 14338
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Inspect the artifact ID definitions if they live in a different file.
rg -n "enum ArtifactId|impl ArtifactId|embedded_bytes_for_version|embedded_bytes\\(" contract/artifacts/src -g '!contract/artifacts/src/embedded.rs' -A8 -B4Repository: Templar-Protocol/contracts
Length of output: 5757
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Show the implementation of the pointer-checked functions and the ArtifactId definition.
file='contract/artifacts/src/embedded.rs'
rg -n "fn embedded_bytes|fn embedded_bytes_for_version|include_bytes!|enum ArtifactId|impl ArtifactId|current\\(" "$file" contract/artifacts/src -A8 -B4Repository: Templar-Protocol/contracts
Length of output: 20370
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Read the ArtifactId implementation file if it is separate.
fd -a -t f 'ids.rs' contract/artifacts/srcRepository: Templar-Protocol/contracts
Length of output: 213
Compare slice contents instead of pointer identity. This test depends on two separate include_bytes! sites being merged into the same allocation, so a correct catalog entry can still fail here. Use artifact.id.embedded_bytes() == via_version instead.
Suggested fix
- std::ptr::eq(artifact.id.embedded_bytes(), via_version),
+ artifact.id.embedded_bytes() == via_version,📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| #[test] | |
| fn embedded_bytes_matches_current_release() { | |
| for artifact in ArtifactId::ALL.iter().map(|id| id.metadata()) { | |
| let via_version = artifact | |
| .id | |
| .embedded_bytes_for_version(artifact.version()) | |
| .unwrap_or_else(|| { | |
| panic!( | |
| "{} has no blob for its current version {}", | |
| artifact.package_name, | |
| artifact.version(), | |
| ) | |
| }); | |
| assert!( | |
| std::ptr::eq(artifact.id.embedded_bytes(), via_version), | |
| "{}: `embedded_bytes` does not point at the current release ({}). \ | |
| Update its arm in `ArtifactId::embedded_bytes`.", | |
| artifact.package_name, | |
| artifact.version(), | |
| ); | |
| } | |
| } | |
| #[test] | |
| fn embedded_bytes_matches_current_release() { | |
| for artifact in ArtifactId::ALL.iter().map(|id| id.metadata()) { | |
| let via_version = artifact | |
| .id | |
| .embedded_bytes_for_version(artifact.version()) | |
| .unwrap_or_else(|| { | |
| panic!( | |
| "{} has no blob for its current version {}", | |
| artifact.package_name, | |
| artifact.version(), | |
| ) | |
| }); | |
| assert!( | |
| artifact.id.embedded_bytes() == via_version, | |
| "{}: `embedded_bytes` does not point at the current release ({}). \ | |
| Update its arm in `ArtifactId::embedded_bytes`.", | |
| artifact.package_name, | |
| artifact.version(), | |
| ); | |
| } | |
| } |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@contract/artifacts/src/embedded.rs` around lines 183 - 204, Update the
assertion in embedded_bytes_matches_current_release to compare the byte-slice
contents returned by embedded_bytes() and embedded_bytes_for_version() using
equality, replacing the pointer-identity check while preserving the existing
failure message and validation flow.
| /// Print `<source_path> <cargo_target_name> <version>` for each selected | ||
| /// artifact and exit, without building. Lets shell tooling | ||
| /// (`script/artifact-release.sh`, `release-artifacts.yml`) read the catalog | ||
| /// instead of re-deriving paths and drifting from it. | ||
| #[arg(long)] | ||
| print_metadata: bool, | ||
|
|
||
| /// Print the `source_commit` of the given released version of the single | ||
| /// selected artifact and exit, or an empty line for a legacy blob that | ||
| /// cannot be reproducibly rebuilt. | ||
| #[arg(long, value_name = "VERSION")] | ||
| print_release: Option<String>, |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Map the relevant file and inspect the surrounding argument handling.
git ls-files contract/artifacts/src/prebuild.rs
wc -l contract/artifacts/src/prebuild.rs
cat -n contract/artifacts/src/prebuild.rs | sed -n '1,220p'Repository: Templar-Protocol/contracts
Length of output: 7102
🏁 Script executed:
#!/bin/bash
set -euo pipefail
rg -n --hidden --glob '!target' --glob '!dist' --glob '!build' --glob '!node_modules' \
'print_metadata|print_release|check_all|artifact-release|release-artifacts' \
contract README.md .github script || trueRepository: Templar-Protocol/contracts
Length of output: 891
🏁 Script executed:
#!/bin/bash
set -euo pipefail
rg -n --hidden --glob '!target' --glob '!dist' --glob '!build' --glob '!node_modules' \
'print_metadata|print_release|artifact-release|release-artifacts|--print-metadata|--print-release' \
contract README.md .github script || trueRepository: Templar-Protocol/contracts
Length of output: 995
Reject conflicting output modes.
--print-metadata and --print-release <version> should conflict; today the metadata branch wins silently, so a caller can ask for release output and get the catalog instead. Add conflicts_with on both flags.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@contract/artifacts/src/prebuild.rs` around lines 47 - 58, Add mutual
conflicts_with configuration to the print_metadata and print_release CLI
arguments so they cannot be supplied together. Preserve each flag’s existing
output behavior when used independently, ensuring conflicting invocations are
rejected instead of selecting the metadata branch.
Source: Path instructions
| ## Contributing | ||
|
|
||
| PRs are squash-merged, so the PR title becomes the commit message and drives automated versioning. Titles must follow `type(scope): summary` — see [Commit And PR Titles](AGENTS.md#commit-and-pr-titles). Releases are cut by merging the standing "chore: release" PR; see [RELEASING.md](RELEASING.md). | ||
|
|
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
Use a release PR title that passes validation.
The documentation requires type(scope): summary but names the standing release PR chore: release, which omits the scope. Document a compliant title such as chore(release): release, or explicitly support the exception in the title workflow.
As per path instructions, PR titles must use type(scope): summary; the documented release title does not satisfy that format.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@README.md` around lines 43 - 46, Update the release PR title reference in the
Contributing section to use a title that satisfies the documented type(scope):
summary format, such as chore(release): release, while preserving the existing
release workflow description.
Source: Path instructions
Closes ENG-522. Folds in ENG-362 (GPLv3 relicense).
Nothing in this workspace was released anywhere.
templar-backend/services/blockchain-gatewaypins seven crates to a rawdevcommit because no release tag containsgateway/; tags used three naming schemes; the only GitHub Release was a draft.contract/artifactsstored one WASM blob per contract, overwritten in place — sogateway/testing/src/wasm.rshand-hoarded five historical blobs as ad-hocinclude_bytes!consts, outside the catalog and outside any drift check.What this adds
Per-crate releases via release-plz. Every crate versions itself from its own commit history. Merging the standing "chore: release" PR is the trigger; an ordinary merge to
devships nothing. Three tiers: published (the 17-crate closure consumers import), tagged-only (contracts, services, tools), internal (mocks, fuzz, scaffolding).Immutable versioned WASM.
res/near/<target>/<version>/. Releases are added, never rewritten — historical blobs are what the migration tests deploy. Drift checking goes from 2 checks to 5, and a tag workflow rebuilds each release reproducibly and requires a byte-for-byte match.Commit conventions, documented in AGENTS.md and enforced by a PR-title lint, since squash-merge makes the PR title the commit that drives version inference.
Things worth knowing
crates.io publishing is configured but deferred.
templar-commondepends on the RedStone SDK as a git dependency, and Cargo refuses to publish any crate with one — on crates.io or a private registry. Every Tier A crate reachestemplar-common. Consumers pin per-crate git tags meanwhile; unblocking is a config flip, no code changes. SeeRELEASING.md.Two blobs both claimed one version. proxy-oracle
0.3.0and governance0.1.0each had two distinct binaries — a stale local rebuild and the bytes actually deployed on mainnet — with current source matching neither. The on-chain bytes are recorded as canonical; source is bumped to0.4.0/0.2.0with freshly cut reproducible blobs.Reproducibility is verified at the recorded
source_commit, not the tag.cargo near build reproducible-wasmembeds the source commit per NEP-330, so identical source at two commits yields different bytes (measured:f03b159evsb0a6ff7c). A blob is always committed before its tag exists, so verifying at the tag would fail every release.Before merging
dev; without them release-plz would replay the whole history into one changelog.RELEASE_PLZ_TOKENis set.devis already red from the pre-existing feat(proxy-oracle): standardize contract self-upgrade (ENG-481) #519 migration-guard regression (ENG-481) — inherited, not caused here.Verification
cargo fmt· CI's clippy command clean · artifact drift 25/25 ·just test-fast4997 passed ·just test-sandbox275 passed, including the migration/upgrade tests repointed at the catalog (migrate_mainnet,migrate_v0,upgrade_ordering, uacmigrationfrom real 0.2.0/0.4.0 bytes).🤖 Generated with Claude Code
This change is