Skip to content

ci(contract): contract-change fragments instead of per-PR version bumps - #268

Merged
afeezorobsco-cyber merged 1 commit into
mainfrom
chore/contract-version-fragments
Oct 28, 2026
Merged

afeezorobsco-cyber merged 1 commit into
mainfrom
chore/contract-version-fragments

Conversation

@afeezorobsco-cyber

Copy link
Copy Markdown
Contributor

Why

Escrow has 9 open PRs that change contracts/escrow/src/lib.rs, and each one bumps CONTRACT_VERSION. They all edit the same line and pick the same "next" version, so every contract merge puts all the others into conflict. Only about one contract PR could land per sweep, and several broke on the side effects: #146 and #216 collided on the version, #199 ended up with CONTRACT_VERSION defined four times plus a duplicate error code 35, and #157's version test hardcoded the old literal.

What changes

  • contract-changes.d/: a PR that changes lib.rs adds a fragment (bump: major|minor|patch, then ---, then a summary) and leaves CONTRACT_VERSION alone. Same idea as changelog.d/.
  • Error codes: new Error variants use a provisional code, issue number × 100 + n (issue [Phase E5] ADR + implementation: emergency pause that can only stop new escrows #35 → 3500, 3501…). Two PRs can't pick the same number. Existing codes stay append-only.
  • CI: scripts/check-contract-change.mjs replaces the "Require a contract version bump when lib.rs changes" step. When lib.rs changes it requires a fragment, refuses CONTRACT_VERSION edits, refuses renumbering or removing existing codes, and requires new codes to be provisional (≥ 1000). A fold PR (one that deletes fragments and adds none) may bump the version and finalise provisional codes.
  • Maintainer fold: node scripts/fold-contract-changes.mjs [--dry-run] bumps the version once by the largest level, renumbers provisional codes to the next free final ones, writes changelog.d/contract-<version>.md (which fold-changelog.mjs then puts in ROADMAP.md), and deletes the fragments. Then refresh snapshots (cargo test) and bindings, and commit it all as one PR.
  • Version test: version_reports_the_contract_semver compares with CONTRACT_VERSION instead of a literal.
  • Docs: contract-changes.d/README.md, plus one rule each in AGENTS.md and CONTRIBUTING.md (most PRs here are agent-written, so AGENTS.md matters).

Tested

Locally, against throwaway commits on main:

Scenario Result
no lib.rs change pass
lib.rs changed, no fragment fail: "no fragment was added"
fragment + version bump fail: "CONTRACT_VERSION changed (2.3.0 -> 2.4.0)"
new error with final code 37 fail: "use a provisional code"
fragment + provisional codes 3500/3501/1200 pass
fold (minor + patch fragments) 2.3.0 -> 2.4.0; codes 1200, 3500, 3501 -> 37, 38, 39; fold PR passes the check

The changelog entry the fold writes is picked up by fold-changelog.mjs. cargo test --test create_escrow version_reports passes.

After merging

The 9 open contract PRs will fail the new step until they revert their CONTRACT_VERSION change, add a fragment, and switch any new error codes to provisional ones. I'll post one comment on each with exact instructions.

🤖 Generated with Claude Code

Nine open PRs each bumping CONTRACT_VERSION meant every contract merge put
all the others into conflict, and each picked the same 'next' version.

- contract-changes.d/: a PR that changes lib.rs adds a fragment (bump level
  plus summary) and leaves CONTRACT_VERSION alone. New Error variants use a
  provisional code (issue x 100 + n) so two PRs can't pick the same number.
- scripts/check-contract-change.mjs replaces the 'Require a contract version
  bump' CI step: fragment required, no version edits, error codes append-only,
  new codes provisional. A fold PR (deletes fragments) may bump and renumber.
- scripts/fold-contract-changes.mjs: maintainer fold. Bumps the version once
  by the largest level, assigns final error codes, writes a changelog.d
  entry, deletes the fragments.
- version test compares with CONTRACT_VERSION instead of a literal.
- AGENTS.md and CONTRIBUTING.md document the rule.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown

Malware scan: passed, with notes for the reviewer

Nothing malicious was detected in 5560be6. These changes touch sensitive areas and should get a careful look before CI is approved:

Where Rule Why
.github/workflows/ci.yml:78 workflow-change Changes a CI workflow; a maintainer must review before approving runs.

@afeezorobsco-cyber
afeezorobsco-cyber merged commit 2fc41f8 into main Oct 28, 2026
13 checks passed
@afeezorobsco-cyber
afeezorobsco-cyber deleted the chore/contract-version-fragments branch October 28, 2026 10:32
This was referenced Oct 28, 2026
afeezorobsco-cyber added a commit that referenced this pull request Oct 28, 2026
…to a new address (#199)

* feat(contract): payee rights transfer: payee reassigns future payout to a new address

Closes #45

- Add transfer_payee(escrow_id, current_payee, new_payee) restricted to
  the current payee (require_auth), only while Funded.
- Reject contract address and payer as new_payee (InvalidNewPayee = 8).
- Emit pay_xfer event with (escrow_id, old_payee, new_payee).
- 12 tests in contracts/escrow/tests/transfer_payee.rs covering:
  happy path, release/refund after transfer, chained transfers,
  auth rejection (stranger/payer/releaser), both rejected targets,
  and state checks (after release, after refund, unknown id).
- Threat model: I1 updated to document transfer_payee scope, payee
  actor updated (new on-chain power), T11 added (payee key surface).
- ROADMAP.md Phase E5 item added, changelog entry dated 2026-10-19.
- ARCHITECTURE_ESSENTIALS.md and README.md contract surface updated.

Design note: this is a rotation tool. A payee who has lost their key
cannot use it — they cannot sign current_payee.require_auth(). Key
loss remains an off-chain operational risk. Documented in T11.

* fix(ci): repair transfer_payee integration after merging main

Three CI jobs failed on this branch because the feature was written
against a pre-merge main and never re-integrated:

- Regenerate sdk/src/generated/contract-client.ts from the merged
  contract WASM with the pinned stellar CLI 28.1.0: the contract
  gained transfer_payee and error 35 (InvalidNewPayee), so the
  committed bindings no longer matched (issue #121 CI job).
- Update the create_escrow call in tests/transfer_payee.rs to the
  current signature (notify_payee + refund_to arguments).
- Commit the 12 Soroban test snapshots for the transfer_payee tests,
  which were never added (issue #120 snapshot-drift CI job).

Verified locally: cargo test (all suites incl. proptest model with
PROPTEST_CASES=64), snapshot drift clean, wasm build + artifact,
releaser-account tests and wasm, byte-identical rebuild via
check-wasm-reproducibility.sh, check-generated-bindings.sh OK,
resource baseline OK, and sdk typecheck/build/offline/milestones/
unit/runtime suites all pass.

* test(snapshots): refresh transfer_payee snapshots for main's cached Status entry

After merging main, cargo test writes the cached ["Status", id] ledger entry
(status-read optimisation) into the transfer_payee test snapshots, which were
generated before that change landed. All 12 files gain exactly one entry,
matching main's own snapshots.

* feat(contract): bump CONTRACT_VERSION to 1.1.0 for transfer_payee

CI (from #153) requires a CONTRACT_VERSION bump whenever lib.rs changes
against the base branch. transfer_payee is a new public function, so bump
the minor version and update the version_reports_the_contract_semver
assertion to match.

* fix(contract): keep failed releases recoverable (#154)

* fix(contract): keep failed releases recoverable

* fix(contract): reserve unique undeliverable error code

* fix(contract): reserve unique undeliverable error code

* test: enable revocation in undeliverable SAC fixture

* test(contract): refresh undeliverable payee snapshots

The status-read optimisation on main keeps a separate DataKey::Status
entry next to the escrow record. Regenerate the two undeliverable payee
snapshots so they include it: Funded after the rejected release, and
Released after the retry.

* docs(roadmap): record undeliverable payee handling (#55)

* test(releaser-account): refresh snapshot for status-read optimisation

Picked up from merging main's status-read change (#260): releaser-account's
own fixture now also has a separate DataKey::Status entry. Not touched by
this PR's change; regenerated by running its test suite.

---------

Co-authored-by: Ezekiel146 <320391431+Ezekiel146@users.noreply.github.com>
Co-authored-by: Pheelz <afeezorobsco@gmail.com>

* ci(contract): contract-change fragments instead of per-PR version bumps (#268)

Nine open PRs each bumping CONTRACT_VERSION meant every contract merge put
all the others into conflict, and each picked the same 'next' version.

- contract-changes.d/: a PR that changes lib.rs adds a fragment (bump level
  plus summary) and leaves CONTRACT_VERSION alone. New Error variants use a
  provisional code (issue x 100 + n) so two PRs can't pick the same number.
- scripts/check-contract-change.mjs replaces the 'Require a contract version
  bump' CI step: fragment required, no version edits, error codes append-only,
  new codes provisional. A fold PR (deletes fragments) may bump and renumber.
- scripts/fold-contract-changes.mjs: maintainer fold. Bumps the version once
  by the largest level, assigns final error codes, writes a changelog.d
  entry, deletes the fragments.
- version test compares with CONTRACT_VERSION instead of a literal.
- AGENTS.md and CONTRIBUTING.md document the rule.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* chore(changelog): fold changelog.d fragments into ROADMAP.md (#269)

Ran scripts/fold-changelog.mjs during the 2026-10-28 PR sweep.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* feat(contract): payee rights transfer — transfer_payee reassigns the release destination to a new address

Only the current payee may authorise this; only while Funded; new_payee may
not be the contract address or the payer. Rotation tool, not key-loss
recovery. 12 tests in contracts/escrow/tests/transfer_payee.rs.

Adds contract-changes.d/45-payee-rights-transfer.md (bump: minor) instead of
editing CONTRACT_VERSION, per contract-changes.d/README.md, and gives
Error::InvalidNewPayee the provisional code 4500 (issue #45 x 100) for the
maintainer fold to renumber. Regenerates the SDK bindings from the rebuilt
WASM and adds changelog.d/45-payee-rights-transfer.md for issue #45.

Co-authored-by: Codebuff <noreply@codebuff.com>

---------

Co-authored-by: Ezekiel <ezekieldigitalz@gmail.com>
Co-authored-by: Ezekiel146 <320391431+Ezekiel146@users.noreply.github.com>
Co-authored-by: Pheelz <afeezorobsco@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Codebuff <noreply@codebuff.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant