fix(ci): publish linked-client-pr gate as a Check Run to escape the 1000-status cap - #187
Merged
Merged
Conversation
WiktorStarczewski
added a commit
that referenced
this pull request
Aug 4, 2026
* fix(ci): publish linked-client-pr gate as a Check Run to escape the 1000-status cap (#187) * release: 0.15.1 (#198) * chore(deps): upgrade miden-client to 0.15.2 Bumps miden-client + miden-client-sqlite-store 0.15.0 -> 0.15.2 (pulls miden-note-transport-proto-build 0.4.1; miden-protocol resolves to 0.15.3). Updates the CI node-builder ref (MIDEN_CLIENT_REF) to the v0.15.2 tag commit so the node's miden-protocol matches the bundled client. 0.15.2 deprecates the no-hint Client::send_private_note; the web-client keeps the no-hint path (allow(deprecated)) since the JS API does not expose a block hint yet. * release: 0.15.1 * fix(ci): make Rust crates publishable to crates.io (#199) The "Publish Rust Crates on Release" job has failed since v0.15.0: - web-client depends on js-export-macro via a bare path dep (no version), so cargo publish refuses it, and js-export-macro was never in the publish list. - The Rust crates share the workspace version, which releases don't bump (only the npm versions move), so cargo publish hits "already exists". Fixes: - Bump the workspace version + internal dep version refs to 0.15.1. - Add js-export-macro as a workspace dep (path + version); web-client uses it via workspace = true so the published manifest carries a version. - Publish in dependency order (js-export-macro, idxdb-store, web-client, mobile-prover) and add the missing js-export-macro step. - web-client publishes with --no-verify: it's a wasm cdylib that can't build for the host target; CI verifies it for wasm32 instead. - Document the dual npm/Rust version bump in CONTRIBUTING. Verified: js-export-macro/idxdb-store/mobile-prover dry-run-publish cleanly at 0.15.1; web-client's manifest now resolves (its deps publish first in the workflow). * ci: allow crates-publish to be dispatched manually (#200) Add a workflow_dispatch trigger (with an optional ref input) to publish-crates-release.yml so the crates can be (re-)published off a chosen ref — e.g. to catch crates.io up to a fix that landed after a release tag, or to recover when a release's publish failed partway. The checkout ref falls back from the release tag to the dispatched ref. * feat(pswap): track PSWAP order lineages and expose reads + cancel-by-order (#176) * feat(web-client,react-sdk): track PSWAP order lineages + cancel-by-order Persist a lineage per partially-fillable swap order — the chain of remainder notes a PSWAP leaves behind as it is filled round by round — keyed by a stable orderId. Adds a `pswap` resource on MidenClient (lineages / lineagesFor / lineage / cancelByOrder) and four React hooks (usePswapLineages, usePswapLineagesFor, usePswapLineage, usePswapCancelByOrder). - pswap.rs: lineage reads + build_pswap_cancel_by_order with a terminal-state guard (only Active lineages can be cancelled), covering the raw binding, the pswap.js resource, and the React hook through the one shared layer. - PswapLineageRecord model exposes remainingOffered/remainingRequested as FungibleAsset (faucet + amount), depth, tip, state, and block numbers. - applyTransaction routes through the high-level apply so registered tx observers (PSWAP tracking) fire. - Tracks inicio-labs/miden-client vaibhav/pswap until the PSWAP API releases. * fix(pswap): drop redundant JS terminal-state guard, binding is authoritative The cancelByOrder resource duplicated the FullyFilled/Reclaimed check that buildPswapCancelByOrder already enforces in Rust, and its comment described the pre-guard behavior (request reaching the kernel). Remove the JS state block so the binding is the single guard, matching usePswapCancelByOrder; keep the lineage fetch for the creator account. * chore(deps): adopt published miden-client 0.15.2; drop the fork pin 0.15.2 ships the PSWAP API on crates.io, so pin the version and drop the inicio-labs/vaibhav/pswap git dependency. Adapt to 0.15.2's PswapLineageRecord: remainingOffered/remainingRequested are now plain amounts (the record no longer carries the faucet — it's recovered from the original note when needed), and the created/updated block accessors are gone (fields dropped upstream). Suppress the new send_private_note deprecation; the block-hint variant can be wired later. * refactor: remove protocol crates (#197) * feat(web-client): expose advice map accessors on TransactionRequest (#203) * feat(web-client): expose advice map accessors on TransactionRequest Add TransactionRequest.adviceMap() (returns a copy of the request's advice map) and TransactionRequest.extendAdviceMap(adviceMap) (merges entries into an already-built request and returns a new request, with last-write-wins semantics on key collisions). wasm-bindgen cannot return the native &mut AdviceMap, so this exposes the immutable builder-style shape instead. It lets a signer/guardian flow inject advice (e.g. a signature) that only becomes available after the request object is constructed, without going back through the builder. The pinned miden-client 0.15.2 already exposes the native advice_map()/advice_map_mut() methods, so no upstream change is needed. Closes #202 * docs(changelog): reference PR #203 for advice map entry * fix(react): add advice map methods to TransactionRequest mock The new TransactionRequest.adviceMap() / extendAdviceMap() methods made the hand-rolled mock in the react-sdk tests structurally incompatible with the TransactionRequest type, failing the typecheck. Add both to the shared createMockTransactionRequest factory. * release: 0.15.2 (#205) * fix: declare MSRV for js-export-macro to fix nightly (#209) * fix(web): export full public surface from the node entry (#206) * fix(web): export full public surface from the node entry The node export entry (js/node-index.js, used via the "node" condition for SSR / Next.js server / Vitest) re-exported only a hand-curated subset of the WASM classes. The browser entry exports the full surface and the .d.ts types advertise it, so importing an omitted class (e.g. BasicFungibleFaucetComponent, TransactionRequest, InputNoteRecord) — or a @miden-sdk/react hook that imports one — threw "X is not exported from '@miden-sdk/miden-sdk'" under node resolution. Add _reexport lines for every public napi class (71 were missing), plus the JS-layer helpers react needs (CompilerResource, getWasmOrThrow). ESM can't re-export a native addon's members dynamically, so the names are listed explicitly; node_export_parity.node.test.ts enforces completeness against the napi module so the two can't silently drift again. * docs(test): tighten node export parity comment per review * refactor(web): generate node re-exports instead of hand-maintaining them Per review: replace the hand-written napi re-export list (and the runtime parity test) with a codegen script that derives the _reexport block from the native module's exports, written into a <generated:napi-reexports> marker region. `gen:node-reexports` regenerates it; `check:node-reexports` (run in the node CI job, which already builds the napi binary) fails if it drifts. The hand-written remaps (WebClient -> WasmWebClient, the AccountType/AuthScheme enum shadows) and JS-layer helpers (CompilerResource, getWasmOrThrow) stay manual. * release: 0.15.3 (#210) * feat: add batch builder implementation (#31) * feat(web,react): add AggLayer bridge-out (B2AGG) note support (#211) * feat(web,react): add AggLayer bridge-out (B2AGG) note support Enables creating and submitting a B2AGG (Bridge-to-AggLayer) note entirely within web-sdk by consuming the agglayer functionality already re-exported by the bundled miden-client (miden_client::agglayer) — no new dependency and no miden-client change. - EthAddress JS model (20-byte Ethereum address) - Note.createB2AggNote(...) and WebClient.newB2AggTransactionRequest(...) - client.transactions.bridge(...) resource method (+ preview support) - @miden-sdk/react useBridge() hook - docs (CHANGELOG, READMEs, react-sdk guide) and unit tests Closes #173 * test(web): pin B2AGG resource argument order; drop changelog version floor Review follow-up: - transactions.test.js now asserts the resolved sender/bridge/faucet/destination ids by position, so a sender<->bridge<->faucet swap in #buildB2AggRequest would fail the test (the most important correctness property of this change). - CHANGELOG: drop the "(0.15.1+)" miden-client floor — unverifiable for the exact B2AggNote::create surface; the consumer-relevant fact is "no new dependency". * fix(web): use as_chunks for nightly clippy chunks_exact_to_as_chunks lint Pre-existing line surfaced by nightly clippy drift (the lint denies a constant-size chunks_exact under -D warnings); not related to the B2AGG feature, but it blocks this PR's Clippy WASM gate. Applies clippy's own suggestion; as_chunks is stable at the project MSRV (1.93). * fix(web-client): forward toU64s() on StorageResult (#194) * fix(web-client): forward toU64s() on StorageResult StorageResult (returned by StorageView.getItem/getMapItem) is a Word-like wrapper that forwarded toFelts()/toHex()/toBigInt() but not toU64s(), while being typed as Word. Reading raw u64 elements off a storage value — e.g. `account.storage().getItem(slot).toU64s()` — threw "toU64s is not a function" at runtime even though Word.toU64s() is declared on the type. This blocked the OpenZeppelin multisig client's AccountInspector (which inspects every multisig account on load), and thus every guardian transaction. Add the missing pass-through (mirroring toFelts) plus the .d.ts declaration and a test assertion. * docs(changelog): add entry for StorageResult.toU64s() fix * feat(web): expose full faucet metadata on BasicFungibleFaucetComponent (#204) * feat(web): expose full faucet metadata on BasicFungibleFaucetComponent Add tokenName(), tokenSupply(), description(), logoUri(), and externalLink() to BasicFungibleFaucetComponent so consumers can extract the complete token metadata of any fungible-faucet account (basic or network-style). In miden-standards 0.15.x the separate BasicFungibleFaucet and NetworkFungibleFaucet types were unified into a single FungibleFaucet component (the basic-vs-network distinction is now account configuration, not a component type), so the existing binding already works on network faucet accounts -- this just surfaces the metadata it wasn't exposing. A dedicated NetworkFungibleFaucet binding (as #162 originally proposed) would not compile against the pinned deps. Also adds BasicFungibleFaucetComponent to the typedoc curated exports. * fix(test): normalize null to undefined for faucet metadata getters on Node The new BasicFungibleFaucetComponent.description() / logoUri() / externalLink() getters return Rust Option<String>, which napi maps to null on Node.js (wasm-bindgen maps None to undefined on the browser). The shared Playwright test asserts toBeUndefined(), so the Node project failed. Register the three getters with the node-adapter's existing patchNullToUndefined shim, matching AccountStorage / NoteConsumability. * fix(test): normalize faucet metadata getters in the active node sdk path The previous attempt patched node-adapter.ts, but the per-test sdk is built by test-setup.ts's createNodeSdkWrapper -> patchNapiPrototypes, so that patch never ran on the test path (description() still returned napi null, failing toBeUndefined() on the Node project). Register the three BasicFungibleFaucetComponent getters in patchNapiPrototypes instead, and revert the node-adapter.ts change. Verified locally: the nodejs project's basic_fungible_faucet_component tests pass. * release: 0.15.4 (#213) * chore: prepare 0.15.4 * chore: drop empty Unreleased header from 0.15.4 changelog * fix(release): bump wallet example @miden-sdk/miden-sdk dep to ^0.15.4 * chore(release): bump wallet example @miden-sdk/react dep to ^0.15.4 * feat(web,react): create custom-script network notes (NetworkAccountTarget attachments) (#230) * feat(web): add NetworkAccountTarget WASM binding * feat(web): add Note.withAttachments/attachments/isNetworkNote bindings * feat(web): add NoteRecipient.fromScript (random serial) * fix(web): register NoteAttachment and NoteExecutionHint for napi by-value params * feat(web): export NetworkAccountTarget on node + typedoc surfaces * feat(web): declare NetworkNoteOptions/Result + createNetworkNote/buildNetworkNote types * feat(web): add standalone buildNetworkNote builder * fix(web): reject recipient+script and pin buildNetworkNote test assertions buildNetworkNote silently dropped `script` when both `recipient` and `script` were passed (via `??`); now throws instead, matching the "exactly one of recipient/script" contract. Also strengthens the NetworkAccountTarget construction test to pin executionHint as the second constructor arg, and adds coverage for the pre-built-target and assets-provided branches. * feat(web): add transactions.createNetworkNote resource method * fix(web): wrap script-recipient inputs in FeltArray for network notes buildNetworkNote and createNetworkNote passed a plain JS array straight into `new wasm.NoteStorage(...)`, which works under the napi array polyfill but throws `expected instance of FeltArray` against the real browser WASM bindings, breaking the script + inputs path outside of mocked unit tests. * test(web): integration gate for network-note attachment survives submit * feat(react): add CreateNetworkNoteOptions/NetworkNoteResult types * feat(react): export CreateNetworkNoteOptions/NetworkNoteResult from package barrel * feat(react): add useCreateNetworkNote hook * docs: document network-note creation (web + react) * fix(web): type NetworkNoteOptions.attachment as bigint[] to match runtime * refactor(react): reuse target.targetId() in useCreateNetworkNote * docs: link network-note CHANGELOG entries to web-sdk#230 * docs: link CHANGELOG entries to #230 and amend stale NetworkAccountTarget comment - CHANGELOG: use the linked ([#230](url)) form to match repo convention (per @juan518munoz). - note_attachment.rs: NetworkAccountTarget is re-exposed by this PR, so drop the 'type does not exist on this surface' clause (per @igamigo / #228). * fix(web): wrap network-note `inputs` bigints into Felt before FeltArray createNetworkNote / buildNetworkNote passed raw `inputs` into `new FeltArray(...)`, which throws `expected instance of Felt` against real WASM for any non-empty inputs. The React hook already wrapped each value in `new Felt(v)`; the web-client resource + standalone builders did not, and the unit tests only passed because `FeltArray` was mocked (the exact marshaling gap this feature's integration test was meant to close). - transactions.js / standalone.js: map `inputs` through `new wasm.Felt(v)`. - api-types.d.ts: `inputs?: Felt[]` -> `bigint[]`, matching the React surface and the sibling `attachment` field. - unit tests: assert the Felt-wrapping (Felt mock + FeltArray/NoteStorage args). - network_note integration test: exercise non-empty `inputs` marshaling end to end against real WASM, not a mock. - useCreateNetworkNote: reuse `senderId` for submit instead of re-parsing. Found by independent review of #230. * chore: prepare 0.15.5 (#232) * feat: re-add removed ntx functionality (#236) * chore: upgrade to client `0.15.4` (#238) * feat(web): expose AssetCallbackFlag on FungibleAsset (#240) * feat: expose manual transaction lifecycle on TransactionsResource (#235) * feat(web): expose manual transaction lifecycle on TransactionsResource Adds the four stages that submit() runs in one call as individual public methods, so each step can be benchmarked and error-handled independently (closes #233): const result = await client.transactions.executeRequest(account, request); const proven = await client.transactions.prove(result, { prover? }); const { blockNumber } = await client.transactions.submitProven(proven, result); await client.transactions.apply(result, blockNumber); - executeRequest: execute only — nothing proven, submitted, or persisted - prove: per-call prover override, falls back to the client default - submitProven: network submission, returns { blockNumber } - apply: persists to the local store and fires transaction observers Docs: api-types JSDoc (+ ProveOptions / SubmitProvenResult types, ProvenTransaction / TransactionResult / TransactionStoreUpdate typedoc re-exports), Docusaurus transactions page, web-client README, CHANGELOG under 0.15.6 (TBD). Covered by resource unit tests and a mock-chain integration test driving the four stages end-to-end. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * refactor(web): staged handles for manual transaction lifecycle Replace the flat executeRequest/prove/submitProven/apply methods with a staged pipeline: executeRequest() returns a TransactionExecution handle advanced via .prove() -> .submit() -> .apply(). Each stage carries its own context (result, proof, blockNumber), so callers never re-thread state and out-of-order calls are unrepresentable. - submit() now shares the prover-fallback helper (proveResult) with the staged path, so the two can no longer drift. - submitProven(proof, result) kept as the detached-proving escape hatch, now returning a TransactionSubmission handle. - TransactionSubmission.waitForConfirmation() added. - JSDoc documents the prover single-use hazard and the non-atomicity of the stages as a group. Docs updated across CHANGELOG, README, Docusaurus, and api-types JSDoc. Unit + mock-chain integration tests drive the staged handles. --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com> Co-authored-by: Wiktor Starczewski <poszerny@gmail.com> * release: 0.15.7 * feat(web): add BasicFungibleFaucetComponent.fromAccountStorage (#244) * feat: add BasicFungibleFaucetComponent.fromAccountStorage with tests * docs: changelog entry for fromAccountStorage * fix: address review — unwrap StorageView in fromAccountStorage facade, add #243 differentiator test * fix: patch fromAccountStorage via defineProperty — napi statics are read-only * fix: accept StorageView in napi fromAccountStorage via FromNapiValue fallback * feat(web): expose fungible asset vault key helpers (#247) * feat(web): expose fungible asset vault key helpers * docs: add fungible asset vault key changelog * test(web): normalize fungible asset value arrays * refactor(web): reshape vault round-trip to FungibleAsset.fromVaultEntry(key, value) Addresses the API-shape review of the vault-key helpers. - fromVaultKey(key, amount) -> fromVaultEntry(key, value): take the (key, value) word pair the vault actually stores. This mirrors the native from_key_value_words primitive and pairs 1:1 with the model's own getters, so `FungibleAsset.fromVaultEntry(a.vaultKey(), a.intoWord())` round-trips an asset read from vault data with zero decoding — previously a caller holding both words had to decompose the value word back to a scalar amount. Deletes the internal value-word re-encoding (which duplicated native to_value_word) and the redundant AssetAmount re-validation (from_key_value_words already validates), and the name no longer understates its inputs. - Make the key/value duality legible: vaultKey() (key word — faucet id + callback flag) and intoWord() (value word — amount) now cross-reference each other and fromVaultEntry; fromVaultEntry documents that the callback flag comes from the key. intoWord() is left in place (released API), documented as the value half. - Tests round-trip via the two getters and reject an oversized amount encoded into the value word plus an invalid key. Verified: cargo check --package miden-client-web --target wasm32-unknown-unknown (clean). * feat(web): add FungibleAsset.fromVaultKey(key, amount) convenience Restores the key + scalar amount ergonomic as a distinctly-named convenience alongside the symmetric fromVaultEntry(key, value): use fromVaultEntry when you already hold both vault words, fromVaultKey when you have the key word and the amount as a number. The key supplies the faucet id + callback flag; the amount is encoded into the value word (layout mirrors native to_value_word, noted so the two stay in lockstep). Test asserts fromVaultKey(key, amount) yields the same asset as fromVaultEntry(key, value). Verified: cargo check --package miden-client-web --target wasm32-unknown-unknown and cargo fmt --check (clean). * test(web): cover value-word rejection paths + correct max-amount docs Review follow-ups: - Correct the documented amount ceiling from 2^63 - 1 to the real AssetAmount::MAX (2^63 - 2^31) in the fromVaultEntry / fromVaultKey docs and the test comment (the enforced max is 2^63 - 2^31, not 2^63 - 1). - Add rejection tests for the two public error branches that were uncovered: fromVaultKey's own AssetAmount::new guard (over-max scalar amount), and a value word with non-zero upper limbs fed through fromVaultEntry. Verified: cargo check --package miden-client-web --target wasm32-unknown-unknown and cargo fmt --check (clean). * docs(changelog): file vault-entry feature under 0.15.8, not released 0.15.7 v0.15.7 is already tagged/published and did not contain these APIs, so the fromVaultEntry/fromVaultKey/vaultKey entry was misfiled under its section. Merged main (which carries the `## 0.15.8 (TBA)` staging section) and moved the entry there. package.json stays at 0.15.7, matching the repo's release flow (the version is bumped at release time, not when staging changelog entries). * style: prettier-format fungible asset test --------- Co-authored-by: Wiktor Starczewski <poszerny@gmail.com> * release: 0.15.8 (#248) * docs(readme): document single-threaded vs multi-threaded WASM builds (#250) Co-authored-by: WiktorStarczewski <wiktor.s@miden.team> --------- Co-authored-by: igamigo <ignacio.amigo@lambdaclass.com> Co-authored-by: Wiktor Starczewski <poszerny@gmail.com> Co-authored-by: VaibhavJindal <vaibhavjindal29@gmail.com> Co-authored-by: Utkarsh Sharma <114555115+0xnullifier@users.noreply.github.com> Co-authored-by: Claude Fable 5 <noreply@anthropic.com> Co-authored-by: WiktorStarczewski <wiktor.s@miden.team>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
The
linked-client-pr-readygate records its verdict as a commit status. GitHub caps each(commit SHA, status context)pair at 1000 statuses. This workflow re-evaluates every 15 minutes via cron, so a long-lived PR whose head SHA doesn't move steadily burns toward that cap. Once it's exhausted:POST /statusesreturns 422 ("maximum number of statuses"), andThe net effect: a
pending → successflip (e.g. when the linked miden-client PR finally ships in a release) is silently dropped, and the PR is left displaying a stalependingforever.This was observed live on #25: miden-client#2059 shipped in
v0.15.0, the gate correctly computedsuccess, but the job log ended withstatus cap reached … cannot POST. Intended verdict: successwhile the visible status stayed frozen at a weeks-oldpending / v0.14.9.Fix
Publish the verdict as a Check Run named
linked-client-pr-readyinstead of a commit status:GET /commits/{sha}/check-runs?check_name=…), so there's no accumulation — which also removes the need for the old idempotency guard.set -euo pipefail), instead of being downgraded to a warning. A verdict the gate can't record must never look like a pass.Verdict mapping:
ready → completed/success;pending → in_progress(blocks if required, flips tosuccesson a later run when upstream catches up).statuses: writeis dropped frompermissions(no longer used);checks: writewas already present.Compatibility / risk
linked-client-pr-readyis not currently a required check in either ruleset (main/next), so this change cannot block merges. If it's ever made required, rulesets match Check Runs by name exactly as they match status contexts — so requiringlinked-client-pr-readykeeps working unchanged.pull_request_targetsemantics are unchanged — still base-ref checkout, still no PR-supplied code executed, only metadata reads + the Check Run write.Testing
The workflow only executes from the base branch under
pull_request_target/schedule, so the new code can't be exercised by this PR's own CI and is validated locally instead:yaml.safe_loadparses the workflow.bash -non the extracted gate script: clean.jqbody builders produce valid Check Run payloads for both verdicts (in_progress withoutconclusion; completed withconclusion: success).