Skip to content

chore: Better check for stored meta not stale#3221

Merged
julienrbrt merged 3 commits intomainfrom
alex/exec_check
Apr 1, 2026
Merged

chore: Better check for stored meta not stale#3221
julienrbrt merged 3 commits intomainfrom
alex/exec_check

Conversation

@alpe
Copy link
Copy Markdown
Contributor

@alpe alpe commented Mar 31, 2026

Overview

Enhanced stale block detection during failover to prevent resuming outdated execution states.

Summary by CodeRabbit

  • Bug Fixes
    • Enhanced validation to prevent resuming stale in-progress executions when stored execution metadata doesn't match the requested inputs (timestamp/transactions).
  • Tests
    • Added a unit test covering reconcile behavior for started execution metadata, verifying correct resume vs. fresh-execution outcomes.
  • Documentation
    • Standardized changelog list formatting and added an “Unreleased” changes entry.

@alpe alpe requested a review from julienrbrt March 31, 2026 15:22
@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Mar 31, 2026

The latest Buf updates on your PR. Results from workflow CI / buf-check (pull_request).

BuildFormatLintBreakingUpdated (UTC)
✅ passed⏩ skipped✅ passed✅ passedApr 1, 2026, 7:21 PM

@claude
Copy link
Copy Markdown
Contributor

claude bot commented Mar 31, 2026

Claude finished @julienrbrt's task in 2m 12s —— View job


Code Review

  • Gather context and read changed files
  • Review execution/evm/execution.go
  • Review execution/evm/execution_reconcile_test.go
  • Review CHANGELOG.md
  • Post comprehensive review feedback

Overall Assessment

This is a solid improvement. The stale-in-progress check prevents resuming an execution that was built for different block inputs (different timestamp or transactions), which is the right defense against the failover/crash-recovery scenario. The general structure and logic are correct. A few issues worth addressing:


execution/evm/execution.go

hashTxs — ambiguous encoding (Major)

The function concatenates raw transaction bytes before hashing, which creates hash collisions for different tx-list shapes that flatten to the same byte stream. For example:

hashTxs(["ab", "cd"]) == hashTxs(["abcd"]) == SHA256("abcd")

This is the exact guard this PR is adding, so a collision here defeats its purpose. Fix: hash each element independently:

func hashTxs(txs [][]byte) []byte {
    if len(txs) == 0 {
        return nil
    }
    h := sha256.New()
    for _, tx := range txs {
        txHash := sha256.Sum256(tx)
        h.Write(txHash[:])
    }
    return h.Sum(nil)
}

Fix this →

Named return err reused for transient GetPayload call

At line 771, err (the function's named return) is assigned: if _, err = c.engineClient.GetPayload(ctx, pid); err == nil. If GetPayload fails, err is left non-nil for the rest of the function body. It's re-assigned by getBlockInfo at line 790 via :=, so the function ultimately returns nil (correct), but this reuse of the named return for transient calls is fragile. Prefer a local variable:

if _, payloadErr := c.engineClient.GetPayload(ctx, pid); payloadErr == nil {
    ...
}
c.logger.Debug()...Err(payloadErr)...

CodeRabbit's ParentHash suggestion — not actionable here

CodeRabbit suggested also validating execMeta.ParentHash in the stale check. Note that saveExecMeta never populates ParentHash for ExecStageStarted entries — there's no parentHash parameter in the function signature. Comparing execMeta.ParentHash (always nil) against the actual parent would break all resume attempts. This would be a valid future improvement requiring both storing and checking the field, but it's out of scope for this PR.


execution/evm/execution_reconcile_test.go

range over map — non-deterministic subtest order

specs := map[string]struct{ ... }{ ... }
for name, spec := range specs {
    t.Run(name, func(t *testing.T) { ... })
}

range over a map randomizes the order. In practice this rarely causes flakiness for independent subtests, but it makes CI output harder to follow and is inconsistent with Go table-driven test conventions. Convert to a []struct{ name string; ... } slice.

Fix this →

mockReconcileEthRPCClient.HeaderByNumber — misleading error

The mock returns errors.New("header not found") which could be confused with a real failure if the test assertions ever change. Consider errors.New("unexpected HeaderByNumber call") to match the GetTxs mock style.


CHANGELOG.md

Two issues:

  1. Wrong section — The entry is under ### Changes but this is a bug fix (preventing stale state resumption on restart). It belongs under ### Fixed. The description is also written in commit-title style; a user-facing rewrite would be: "Fixed stale execution-resume during failover by validating timestamp and transaction inputs before reusing a stored payload ID".

  2. Orphan * at line 333 — A stray * was introduced (likely from the - → * normalization pass). This renders as an empty bullet in markdown and should be removed.

Fix CHANGELOG →


Summary

Area Severity Issue
hashTxs collision Major Different tx-lists can produce identical hashes
Named return err reuse Minor Fragile, should use local var
Test map ordering Minor Non-deterministic subtest order
Mock error message Nit Misleading error on HeaderByNumber
CHANGELOG section Minor Should be under Fixed, not Changes
CHANGELOG stray * Nit Orphan bullet at line 333

The hashTxs collision is the one issue worth fixing before merge — everything else is quality-of-life. The core logic and test coverage are correct.

@alpe alpe changed the title Better check for store meta not stale chore: Better check for store meta not stale Mar 31, 2026
@alpe alpe changed the title chore: Better check for store meta not stale chore: Better check for stored meta not stale Mar 31, 2026
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 31, 2026

📝 Walkthrough

Walkthrough

Reconciliation now validates stored ExecStageStarted metadata against the requested execution timestamp and transaction set before attempting payload retrieval. A tx-hash helper was introduced, logging/control flow for stale executions adjusted, changelog formatting updated, and a unit test added covering matching and mismatching scenarios.

Changes

Cohort / File(s) Summary
Changelog
CHANGELOG.md
Standardized list formatting across release sections and added an unreleased entry noting the execution/evm stale-meta validation change.
Execution metadata logic
execution/evm/execution.go
Added pre-check comparing stored execMeta.Timestamp and execMeta.TxHash to computed values before using an in-progress PayloadID; extracted tx hashing into hashTxs(txs) which returns nil for empty sets; adjusted logging/control flow for stale in-progress executions.
Reconciliation tests
execution/evm/execution_reconcile_test.go
New test TestReconcileExecutionAtHeight_StartedExecMeta exercising reconcile behavior for matching inputs (resumes via GetPayload) and mismatched timestamp/txs (do not resume). Adds local mock RPC clients to validate call counts and expected interactions.

Sequence Diagram(s)

mermaid
sequenceDiagram
rect rgba(100,149,237,0.5)
Participant Reconciler
end
rect rgba(60,179,113,0.5)
Participant Store
end
rect rgba(220,20,60,0.5)
Participant EngineRPC
end

Reconciler->>Store: Load ExecMeta(height)
alt ExecMeta.Stage == ExecStageStarted
    Reconciler->>Reconciler: compute requested txHash & timestamp
    alt txHash && timestamp match ExecMeta
        Reconciler->>EngineRPC: GetPayload(PayloadID)
        EngineRPC-->>Reconciler: Payload (resume)
    else different inputs
        Reconciler->>Reconciler: Treat as fresh execution (no GetPayload)
    end
else other stages
    Reconciler->>Reconciler: Normal reconciliation flow
end

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~30 minutes

Suggested reviewers

  • tac0turtle
  • randygrok

Poem

🐰
I thumped a note in changelogs bright,
Checked timestamps and hashes through the night.
Stale payloads hop aside with grace,
Fresh runs take their rightful place. 🥕

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title 'chore: Better check for stored meta not stale' clearly relates to the main change: improving stale metadata detection in execution reconciliation.
Description check ✅ Passed The description provides a clear overview of the enhancement but lacks specific context about implementation details, affected components, or technical rationale.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch alex/exec_check

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@codecov
Copy link
Copy Markdown

codecov bot commented Mar 31, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 61.43%. Comparing base (4a70e0b) to head (9197981).
⚠️ Report is 3 commits behind head on main.

Additional details and impacted files
@@           Coverage Diff           @@
##             main    #3221   +/-   ##
=======================================
  Coverage   61.43%   61.43%           
=======================================
  Files         120      120           
  Lines       12504    12504           
=======================================
  Hits         7682     7682           
  Misses       3960     3960           
  Partials      862      862           
Flag Coverage Δ
combined 61.43% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Copy link
Copy Markdown
Member

@julienrbrt julienrbrt left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm! this makes #3172 more readable as well so great

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (1)
execution/evm/execution_reconcile_test.go (1)

19-57: Use a slice-backed table so the subtests run in a stable order.

range over a map randomizes the case order, which makes failures a bit noisier to reproduce. A slice keeps this table-driven without losing determinism.

As per coding guidelines, "Use table-driven tests in Go unit tests" and "Ensure tests are deterministic in Go."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@execution/evm/execution_reconcile_test.go` around lines 19 - 57, The test
currently builds specs as a map (variable specs) and ranges over it, which
randomizes subtest order; change specs to a slice of structs that include a name
field (e.g., type case struct { name string; execMetaTimestamp int64;
execMetaTxs [][]byte; requestedTxs [][]byte; requestedTime time.Time;
expectFound bool; expectPayloadID bool; expectGetPayloads int }) and populate it
in the desired stable order, then iterate over that slice (for _, spec := range
cases { t.Run(spec.name, func(t *testing.T) { ... }) }) instead of ranging the
map so subtests run deterministically while keeping the same assertions and
using the same symbols (specs -> cases, t.Run).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@CHANGELOG.md`:
- Line 333: Remove the orphan list marker '*' from the links footer in
CHANGELOG.md (the stray asterisk that renders as an empty bullet); open
CHANGELOG.md, locate the lone '*' in the footer area and delete that line so the
list renders correctly without the empty bullet.
- Around line 12-14: Move the entry "chore: Better check for stored meta not
stale" out of the "### Added" section into the "### Fixed" section and rewrite
it in user-facing language that explains the bug and the user impact (e.g.,
"Fixed stale execution-resume behavior by improving validation of stored
metadata to prevent resuming with outdated state"). Update the changelog entry
to remove commit-style phrasing and use a concise, user-first sentence
describing the fix and its effect.

In `@execution/evm/execution.go`:
- Around line 758-785: The resume path for ExecStageStarted currently only
checks timestamp and tx hash (execMeta.Timestamp, execMeta.TxHash via
hashTxs(txs)) before returning the stored PayloadID (pid); you must also
validate that the current parent hash matches execMeta.ParentHash to avoid
resuming on a changed parent. Retrieve the current parent hash for the block
(height-1) from the chain/headers API available in this package and compare it
to execMeta.ParentHash; if they differ, treat the payload as stale (log and fall
through to fresh execution) rather than returning pid even if
c.engineClient.GetPayload(ctx, pid) succeeds. Ensure you still keep the existing
GetPayload existence check (c.engineClient.GetPayload) but only return pid when
both the payload exists and the parent hash matches execMeta.ParentHash.
- Around line 1048-1059: The hashTxs function currently concatenates raw tx
bytes which can produce ambiguous collisions; modify hashTxs to make the tx-list
encoding unambiguous by prefixing each tx with its length (or hashing each tx
separately with a domain separator) before writing into the sha256 hasher: e.g.,
for each tx in hashTxs write the tx length as a fixed-size varint/uint64 or a
clear separator and then write the tx bytes (or write sha256(tx) with a
separator), then return h.Sum(nil) so the digest uniquely represents the ordered
list.

---

Nitpick comments:
In `@execution/evm/execution_reconcile_test.go`:
- Around line 19-57: The test currently builds specs as a map (variable specs)
and ranges over it, which randomizes subtest order; change specs to a slice of
structs that include a name field (e.g., type case struct { name string;
execMetaTimestamp int64; execMetaTxs [][]byte; requestedTxs [][]byte;
requestedTime time.Time; expectFound bool; expectPayloadID bool;
expectGetPayloads int }) and populate it in the desired stable order, then
iterate over that slice (for _, spec := range cases { t.Run(spec.name, func(t
*testing.T) { ... }) }) instead of ranging the map so subtests run
deterministically while keeping the same assertions and using the same symbols
(specs -> cases, t.Run).
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: e12b9912-7b63-430f-b6da-57e2d9c96b2c

📥 Commits

Reviewing files that changed from the base of the PR and between 4a70e0b and 0d5a6f5.

📒 Files selected for processing (3)
  • CHANGELOG.md
  • execution/evm/execution.go
  • execution/evm/execution_reconcile_test.go

CHANGELOG.md Outdated
Comment on lines +12 to +14
### Added

* chore: Better check for stored meta not stale [#3221](https://github.com/evstack/ev-node/pull/3221)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Move this entry under Fixed and rewrite it user-first.

This change hardens stale execution-resume behavior, so it reads like a bug fix rather than a new feature, and chore: Better check for stored meta not stale is still commit-title/internal wording instead of changelog wording.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@CHANGELOG.md` around lines 12 - 14, Move the entry "chore: Better check for
stored meta not stale" out of the "### Added" section into the "### Fixed"
section and rewrite it in user-facing language that explains the bug and the
user impact (e.g., "Fixed stale execution-resume behavior by improving
validation of stored metadata to prevent resuming with outdated state"). Update
the changelog entry to remove commit-style phrasing and use a concise,
user-first sentence describing the fix and its effect.

<!-- Links -->

-
*
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Remove the orphan list marker in the links footer.

Line 333 renders as an empty bullet and looks like a leftover from the list-marker normalization.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@CHANGELOG.md` at line 333, Remove the orphan list marker '*' from the links
footer in CHANGELOG.md (the stray asterisk that renders as an empty bullet);
open CHANGELOG.md, locate the lone '*' in the footer area and delete that line
so the list renders correctly without the empty bullet.

Comment on lines 758 to 785
if execMeta.Stage == ExecStageStarted && len(execMeta.PayloadID) == 8 {
var pid engine.PayloadID
copy(pid[:], execMeta.PayloadID)
requestedTxHash := hashTxs(txs)
if execMeta.Timestamp != timestamp.Unix() || !bytes.Equal(execMeta.TxHash, requestedTxHash) {
c.logger.Debug().
Uint64("height", height).
Int64("execmeta_timestamp", execMeta.Timestamp).
Int64("requested_timestamp", timestamp.Unix()).
Msg("ExecuteTxs: ignoring stale in-progress execution for different block inputs")
} else {
var pid engine.PayloadID
copy(pid[:], execMeta.PayloadID)

// Validate payload still exists by attempting to retrieve it
if _, err = c.engineClient.GetPayload(ctx, pid); err == nil {
c.logger.Info().
// Validate payload still exists by attempting to retrieve it
if _, err = c.engineClient.GetPayload(ctx, pid); err == nil {
c.logger.Info().
Uint64("height", height).
Str("stage", execMeta.Stage).
Msg("ExecuteTxs: found in-progress execution with payloadID, returning payloadID for resume")
return nil, &pid, true, nil
}
// Payload is stale (expired or node restarted) - proceed with fresh execution
c.logger.Debug().
Uint64("height", height).
Str("stage", execMeta.Stage).
Msg("ExecuteTxs: found in-progress execution with payloadID, returning payloadID for resume")
return nil, &pid, true, nil
Str("payloadID", pid.String()).
Err(err).
Msg("ExecuteTxs: stale ExecMeta payloadID no longer valid in EL, will re-execute")
// Don't return - fall through to fresh execution
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

ExecStageStarted resumes still ignore parent-chain changes.

This gate only checks timestamp and tx hash before reusing pid. After a rollback/reorg, the parent at height-1 can change while both still match, so we can resume and later promote a payload that was built on the old parent. Please also validate the current parent hash before returning pid; ExecMeta already has a ParentHash field you can use.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@execution/evm/execution.go` around lines 758 - 785, The resume path for
ExecStageStarted currently only checks timestamp and tx hash
(execMeta.Timestamp, execMeta.TxHash via hashTxs(txs)) before returning the
stored PayloadID (pid); you must also validate that the current parent hash
matches execMeta.ParentHash to avoid resuming on a changed parent. Retrieve the
current parent hash for the block (height-1) from the chain/headers API
available in this package and compare it to execMeta.ParentHash; if they differ,
treat the payload as stale (log and fall through to fresh execution) rather than
returning pid even if c.engineClient.GetPayload(ctx, pid) succeeds. Ensure you
still keep the existing GetPayload existence check (c.engineClient.GetPayload)
but only return pid when both the payload exists and the parent hash matches
execMeta.ParentHash.

Comment on lines +1048 to +1059
func hashTxs(txs [][]byte) []byte {
if len(txs) == 0 {
return nil
}

h := sha256.New()
for _, tx := range txs {
h.Write(tx)
}

return h.Sum(nil)
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Make the tx-list hash unambiguous.

hashTxs hashes the raw concatenation of every tx, so different [][]byte values can collide when they flatten to the same byte stream. That weakens the stale-meta guard this PR is adding. Hash each element independently or include a separator/length per element.

Possible fix
 func hashTxs(txs [][]byte) []byte {
 	if len(txs) == 0 {
 		return nil
 	}
 
 	h := sha256.New()
 	for _, tx := range txs {
-		h.Write(tx)
+		txHash := sha256.Sum256(tx)
+		h.Write(txHash[:])
 	}
 
 	return h.Sum(nil)
 }
📝 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.

Suggested change
func hashTxs(txs [][]byte) []byte {
if len(txs) == 0 {
return nil
}
h := sha256.New()
for _, tx := range txs {
h.Write(tx)
}
return h.Sum(nil)
}
func hashTxs(txs [][]byte) []byte {
if len(txs) == 0 {
return nil
}
h := sha256.New()
for _, tx := range txs {
txHash := sha256.Sum256(tx)
h.Write(txHash[:])
}
return h.Sum(nil)
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@execution/evm/execution.go` around lines 1048 - 1059, The hashTxs function
currently concatenates raw tx bytes which can produce ambiguous collisions;
modify hashTxs to make the tx-list encoding unambiguous by prefixing each tx
with its length (or hashing each tx separately with a domain separator) before
writing into the sha256 hasher: e.g., for each tx in hashTxs write the tx length
as a fixed-size varint/uint64 or a clear separator and then write the tx bytes
(or write sha256(tx) with a separator), then return h.Sum(nil) so the digest
uniquely represents the ordered list.

@julienrbrt julienrbrt enabled auto-merge April 1, 2026 19:21
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (2)
CHANGELOG.md (2)

333-333: ⚠️ Potential issue | 🟡 Minor

Remove the orphan list marker in the links footer.

Line 333 is a stray bullet and renders as an empty list item.

Suggested edit
-*
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@CHANGELOG.md` at line 333, Remove the stray orphan list marker "*" in the
links/footer area (the lone asterisk at line 333) so it no longer renders an
empty list item; locate the "*" token in CHANGELOG.md near the links/footer
section and delete that single character to fix the rendered output.

12-15: ⚠️ Potential issue | 🟡 Minor

Move this Unreleased entry to ### Fixed and rewrite it user-first.

This is a bug-fix outcome (preventing stale execution resume), not a generic “Changes” item, and the current text is still commit-style/internal phrasing.

Suggested edit
-### Changes
-
-* Improve execution/evm check for stored meta not stale [`#3221`](https://github.com/evstack/ev-node/pull/3221)
+### Fixed
+
+* Prevent resuming execution from stale stored metadata during failover by validating execution timestamp and transaction set before restore. [`#3221`](https://github.com/evstack/ev-node/pull/3221)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@CHANGELOG.md` around lines 12 - 15, Move the Unreleased bullet "Improve
execution/evm check for stored meta not stale [`#3221`]" from the generic "###
Changes" section into the "### Fixed" section and rewrite the line in
user‑facing language (e.g., "Prevented resuming a stale execution by improving
EVM stored-metadata freshness checks") so it reads as the user-visible fix
rather than commit-style/internal wording; update the entry text where "Improve
execution/evm check for stored meta not stale" appears to the new phrasing and
ensure the PR link [`#3221`] remains appended.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@CHANGELOG.md`:
- Line 333: Remove the stray orphan list marker "*" in the links/footer area
(the lone asterisk at line 333) so it no longer renders an empty list item;
locate the "*" token in CHANGELOG.md near the links/footer section and delete
that single character to fix the rendered output.
- Around line 12-15: Move the Unreleased bullet "Improve execution/evm check for
stored meta not stale [`#3221`]" from the generic "### Changes" section into the
"### Fixed" section and rewrite the line in user‑facing language (e.g.,
"Prevented resuming a stale execution by improving EVM stored-metadata freshness
checks") so it reads as the user-visible fix rather than commit-style/internal
wording; update the entry text where "Improve execution/evm check for stored
meta not stale" appears to the new phrasing and ensure the PR link [`#3221`]
remains appended.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 30ad43c5-a70f-4e3c-82d3-2fd3ecca8910

📥 Commits

Reviewing files that changed from the base of the PR and between 0d5a6f5 and 9197981.

📒 Files selected for processing (1)
  • CHANGELOG.md

@julienrbrt julienrbrt added this pull request to the merge queue Apr 1, 2026
Merged via the queue into main with commit 022b565 Apr 1, 2026
31 of 32 checks passed
@julienrbrt julienrbrt deleted the alex/exec_check branch April 1, 2026 19:52
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.

2 participants