You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Tracks items deferred from a multi-pass review of #491 (feat(merge): bitmask-OR mergeable channels for registry concurrency).
Resolved in shipped commits (not listed below):
a6f4884b — rholang_cli regression fix; assert_eq! → Result propagation through EventLogIndex::combine, compute_merged_state, resolve_conflicts, merge, dag_merger::conflicts_fn; BitmaskOr OR-fold dispatch via fold_multi_value; try_get_number_with_rnd migration with deletion of the panicking variant; 28 new test cases.
14f89956 — Registry.rho conflict-analysis comment refreshed for BitmaskOr semantics + dual-typing block (was item Protobuf -> CapnProto #20); docs/rholang/12-vaults-and-tokens.md patch-history phrasing rewritten (was item Port interpreter (and supporting code) to Rust #22) and Mergeable-Tagged Channels section added with footgun documentation; metaweta inline review comments addressed.
The bitmap monotonicity claim is verified — Registry.rho's interior bitmap writes are strictly bit-add (val | (1 << bit)) with no clearing path, so the BitmaskOrend & !prev diff representation is sound.
This issue groups remaining items by category. None are merge-blocking.
Code-level cleanup
1. unforgeable_name_rng is duplicated between casper and rholang
The PR's stated goal was "single source of truth" for tag identity, but the underlying primitive is now duplicated:
casper/src/rust/util/rholang/tools.rs:10 — Tools::unforgeable_name_rng (still used by 5 production sites: runtime.rs:884, 1008, 1072, block_api.rs:1445, plus indirect via system_deploy_util.rs)
rholang/src/rust/interpreter/merging/mergeable_tags.rs:47 — new identical copy
Bodies are byte-identical. Both compute Blake2b512Random::create_from_bytes(&DeployDataProto{deployer, timestamp, ..Default::default()}.encode_to_vec()). Won't drift today because both are stable, but a future change to one and not the other would silently break tag derivation for one consumer set.
Fix: consolidate. Either move the rholang copy out and have casper's Tools import it, or move both into a shared crate (crypto?) since the function is purely cryptographic.
2. is_mergeable_channel allocates Vec<Par> per call on every channel touch
Hot path — runs on every channel write/consume during deploy execution. The Vec<Par> allocation plus etuple.ps.clone() (clones the entire tuple body) is unnecessary; only the head element matters.
Fix: read head as Option<&Par> without owning the Vec:
fnis_mergeable_channel(&self,chan:&Par) -> Option<MergeType>{let head = chan.exprs.iter().find_map(|y| match&y.expr_instance{Some(ExprInstance::ETupleBody(etuple)) => etuple.ps.first(),
_ => None,})?;self.mergeable_tags.get(head).copied()// diagnostic trace block adapts to use Option<&Par> for head}
Avoids the allocation and the per-call Par::clone on tuple elements.
3. concurrent_registry_inserts_should_not_conflict test added in #[ignore] state
/// Exercises the conflict-detection path for two independent contracts both/// calling insertArbitrary. ... the test's `rejected.is_empty()` assertion/// encodes an obsolete premise and needs to be rewritten once the/// rejected-deploy recovery mechanism lands.#[tokio::test(flavor = "multi_thread", worker_threads = 4)]#[ignore = "assertion contradicts multi-parent DAG design; awaits rewrite"]asyncfnconcurrent_registry_inserts_should_not_conflict(){ ...}
Concerns:
470-line ignored test in the codebase that no one runs and that drifts out of date with each refactor.
The URI binding loop iterates a HashMap<Par, MergeType>. Currently default_mergeable_tags() has exactly one BitmaskOr entry, so iteration order doesn't matter. But the loop pattern matches every BitmaskOr-typed entry, and they'd all map to the same URI key. With N>1 BitmaskOr entries, the last-iterated wins — non-deterministic across runs because of HashMap iteration order.
Latent issue. Today nobody adds a second BitmaskOr tag. But a future PR that does (e.g., one BitmaskOr tag for registry, another for some other concurrent-bitmap structure) would silently expose the non-determinism: different validators might bind different Pars to the same URI based on hash-seed randomness, and merge dispatch would diverge.
Fix: make the URI binding key per-tag (e.g., "rho:system:bitmaskMergeableTag:{tag_index}"), OR enforce one-tag-per-URI at runtime by making urn_map.insert for the bitmask URI return an error on second insert.
5. mergeable_tags: HashMap<Par, MergeType> should be a BTreeMap (defense-in-depth)
The current consumer is_mergeable_channel only does .get(head) lookups (HashMap is fine), and create_rho_env iterates only to build the urn_map (single-entry today). No current site iterates the map to produce committed state.
But the type signature Arc<HashMap<Par, MergeType>> is exposed broadly: passed into runtime constructors, threaded through Reduce, accessible via mergeable_tags field. A future consumer that iterates the map to produce, say, a hash chain or a serialized list, would inherit non-determinism.
Fix: convert to Arc<BTreeMap<Par, MergeType>> everywhere. Lookup cost is O(log N) instead of O(1), but N is currently 2 — irrelevant. Future consumers can iterate without thinking.
6. Stale "Mirrors / redefined" doc comment on MergeType
/// Merge strategy for a mergeable channel. Mirrors/// `rholang::interpreter::merging::merge_type::MergeType` but is redefined/// here to avoid `rspace++` taking a dependency on `rholang`.
There is no rholang::interpreter::merging::merge_type::MergeType in the codebase — rholang imports MergeType from rspace++ directly. The comment describes a design that was either discarded or never landed.
Fix: delete the second sentence. Replace with a one-liner about the enum's semantics:
/// Merge strategy for a mergeable channel. `IntegerAdd` combines diffs by/// wrapping addition (vault balances, gas accumulators); `BitmaskOr` combines/// by bitwise OR through `u64` (registry interior-node bitmaps).
The "private key" isn't used for signing — only seeds the RNG. The pubkey + timestamp gives a deterministic seed; the priv key just satisfies the unforgeable_name_rng signature.
Fix: consider renaming the API to drop the "private key" framing — a simpler tag_name_from_seed(&[u8]) would make the no-cryptographic-use clearer.
The first rng.next() output is thrown away. This matches the original derivation pattern where the constants were originally located, and the pattern is load-bearing — but undocumented.
Fix: add a code comment explaining why the first rng.next() is skipped.
Logged at INFO level — one-shot startup diagnostic per runtime construction. Reasonable, but worth confirming it doesn't fire per-deploy or per-block in any test or replay path.
15. Pre-existing intra-branch wrapping vs cross-branch checked_add asymmetry
For IntegerAdd, an intra-branch overflow can produce a wrapped value that the cross-branch step accepts. Pre-existing behavior; PR #491 inherits via the new dispatch.
Verified safe under current gas-limit assumptions. Worth a code comment so future readers don't try to "fix" the inconsistency.
OLD code at conflict_set_merger.rs and event_log_index.rs used *existing += value. In release the same as wrapping_add; in debug Rust panics on overflow. PR #491's new wrapping_add always wraps regardless of build profile.
Validators run release, so consensus is unchanged. But debug-mode tests lose the implicit overflow check.
Fix: doc the silent-overflow contract on combine_mergeable_value so test authors know to use checked_add if they want the debug-mode guard.
Style nits
17. Patch-history phrasing in bridge_contract_concurrent_merge.rs extended comments
Two adjacent comment blocks anchor on patch-history.
Lines 13-20 (top-of-file commentary):
"Note on bridge findOrCreate regression: the bridge-v2.rho fixture in this branch also includes a SystemVault.findOrCreate(bridgeVaultAddr) call at bridge init time. That fix is required for the integration-level test_multi_block_state_evolution test to pass..."
Lines 246-258 (end-of-test commentary):
"The bridge findOrCreate fix (bridge-v2.rho calls findOrCreate on its own vault address at init) is exercised at integration level... A code-level equivalent ... was attempted but the in-process TestNode's per-deploy data lookups don't expose deployId data the way live gRPC getDataAtName does..."
After the PR merges, "this branch" no longer exists as a distinct artifact. "The fix" describes a state that's now just "the code." A future reader doesn't care that there was a fix.
Fix: rewrite the top block in invariant terms ("Bridge fixture invariant: the bridge-v2.rho contract calls findOrCreate at init..."); drop the tail block.
18. Brittle tokio::time::sleep(1ms) between deploy constructions
Pattern repeated 4× to ensure each deploy gets a distinct millisecond-resolution timestamp, avoiding signature collision when two deploys signed by the same key would otherwise be byte-identical.
Fix: use distinct keys for each deploy, OR pass an explicit time_stamp override that monotonically increments. Eliminates the sleep dependency.
The variable is named bridge3_deploy but the source is "Nil".to_string() — it's not a bridge contract, just a sibling-trigger.
Fix: rename to sibling3_trigger or nil_sibling_deploy.
Docs cleanup
20. Patch-history phrasing in docs/casper/README.md
"BitmaskOr was added to handle a class of failure where two registry inserts from sibling blocks both touched the same TreeHashMap interior node. Without bitmask merging, one of the inserts would be rejected at multi-parent merge..."
Frames the section around what got fixed. After 12 months, "was added" stops being interesting.
Fix:
"BitmaskOr exists for a specific class of channel: two writes to the same channel can each set distinct bits in a bitmap and combine without conflict. The motivating case is the registry's TreeHashMap interior nodes, where two inserts at different keys race on the parent's child-bitmap update."
21. Cross-repo "investigation report" reference is fragile
File:docs/testing-tracing.md
"A planned improvement is to surface orphan continuations directly in deploy results — see the 'How to make this easier next time' section of the bridge-deposit-orphan investigation report (in the system-integration repo, docs/)."
Two problems:
Cross-repo prose pointer that could rot.
"Tracked separately" pattern.
Fix: drop the forward-reference, OR replace with a real tracking-issue link.
The orphan-detection Python snippet uses random_state as a per-produce identity. Under deterministic execution, two produces with the same seed could produce identical random_state values — they'd be aliased in the set, and one orphan could mask another.
Fix: add a one-line comment in the snippet noting that random_state is unique per-produce in normal Rholang execution but ambiguity could arise in pathological cases (deterministic-replay testing with identical inputs).
Test scenarios
Cumulative test coverage gaps from sections 1-9
These are above and beyond the 28 tests the author shipped in a6f4884b (commutativity proptests, multi-value OR-fold, monotonicity, mismatch error propagation, TreeHashMap delete invariants, depth-1 stress, registry inserts).
Critical / high-value
CLI smoke test:cargo run --bin rholang-cli -- <Registry.rho> should construct a runtime without panic. The regression is fixed but no test prevents recurrence.
URI binding presence assertion: for the CLI and production runtime construction paths, assert urn_map.contains_key("rho:system:bitmaskMergeableTag") after construction.
Same-binary cross-validator parity: two validators running the same binary, same DAG state, registry-bitmap-modifying scenario; assert byte-identical body.deploys, body.rejected_deploys, post-state hash.
Tag-identity stability lock: regression test that non_negative_mergeable_tag_name() and bitmask_or_mergeable_tag_name() produce the exact byte sequences the rest of the system expects (locks the magic constants so future changes break visibly).
Section 1 / 2
Wrapping-add overflow regression: confirm no single-deploy diff can reach i64::MAX / max_deploys_per_block. If gas limits don't enforce this, file a separate consensus-safety issue.
try_get_number_with_rnd returns None for Map values: unit test that constructs a ListParWithRandom containing a Rholang Map and asserts the function returns None without panicking.
Section 3
is_mergeable_channel tuple-arity edge cases: zero-element tuple, single-element tuple matching a tag, single-element tuple NOT matching, two-element tuple where head matches, head-doesn't-match-but-second-element-does (must NOT match — only first element checked).
merge_chs lifecycle: assert that two consecutive evaluate() calls produce independent EvaluateResult.mergeable maps (no bleed-through).
URI-binding consistency: test that the Par returned by urn_map.get("rho:system:bitmaskMergeableTag") is byte-identical to mergeable_tags::bitmask_or_mergeable_tag_name() and to the key under which MergeType::BitmaskOr is registered in default_mergeable_tags().
Section 5
Leaf-Map fall-through test: two concurrent inserts that share a leaf path with the same key. Assert that exactly one is rejected (leaf conflict preserved).
Mixed interior+leaf merge test: deploy A inserts at key K1, deploy B inserts at key K2, where K1 and K2 share a common prefix at depth d but differ at depth d+1. Assert that the interior-node bit-set at depth d is mergeable (both deploys land) and the leaves at depth d+1 are different and both land.
Section 6
Bridge unit test for the orphan-send pattern: deploy a bridge contract that DOESN'T call findOrCreate and assert that a transfer hangs (verifies the bug exists), then deploy with findOrCreate and assert success. Documents the requirement.
CI grep check: assert that no production-resource Rholang contract has _deposit references without a corresponding findOrCreate call.
PR #491 follow-up: deferred review items
Tracks items deferred from a multi-pass review of #491 (
feat(merge): bitmask-OR mergeable channels for registry concurrency).Resolved in shipped commits (not listed below):
a6f4884b— rholang_cli regression fix;assert_eq!→Resultpropagation throughEventLogIndex::combine,compute_merged_state,resolve_conflicts,merge,dag_merger::conflicts_fn; BitmaskOr OR-fold dispatch viafold_multi_value;try_get_number_with_rndmigration with deletion of the panicking variant; 28 new test cases.14f89956— Registry.rho conflict-analysis comment refreshed for BitmaskOr semantics + dual-typing block (was item Protobuf -> CapnProto #20);docs/rholang/12-vaults-and-tokens.mdpatch-history phrasing rewritten (was item Port interpreter (and supporting code) to Rust #22) and Mergeable-Tagged Channels section added with footgun documentation; metaweta inline review comments addressed.The bitmap monotonicity claim is verified — Registry.rho's interior bitmap writes are strictly bit-add (
val | (1 << bit)) with no clearing path, so theBitmaskOrend & !prevdiff representation is sound.This issue groups remaining items by category. None are merge-blocking.
Code-level cleanup
1.
unforgeable_name_rngis duplicated between casper and rholangThe PR's stated goal was "single source of truth" for tag identity, but the underlying primitive is now duplicated:
casper/src/rust/util/rholang/tools.rs:10—Tools::unforgeable_name_rng(still used by 5 production sites:runtime.rs:884, 1008, 1072,block_api.rs:1445, plus indirect viasystem_deploy_util.rs)rholang/src/rust/interpreter/merging/mergeable_tags.rs:47— new identical copyBodies are byte-identical. Both compute
Blake2b512Random::create_from_bytes(&DeployDataProto{deployer, timestamp, ..Default::default()}.encode_to_vec()). Won't drift today because both are stable, but a future change to one and not the other would silently break tag derivation for one consumer set.Fix: consolidate. Either move the rholang copy out and have casper's
Toolsimport it, or move both into a shared crate (crypto?) since the function is purely cryptographic.2.
is_mergeable_channelallocatesVec<Par>per call on every channel touchFile:
rholang/src/rust/interpreter/reduce.rs:782-797Hot path — runs on every channel write/consume during deploy execution. The
Vec<Par>allocation plusetuple.ps.clone()(clones the entire tuple body) is unnecessary; only the head element matters.Fix: read head as
Option<&Par>without owning the Vec:Avoids the allocation and the per-call
Par::cloneon tuple elements.3.
concurrent_registry_inserts_should_not_conflicttest added in#[ignore]stateFile:
casper/tests/util/rholang/runtime_manager_test.rs:2113-2120Concerns:
bridge.rho(notbridge-v2.rho) — different fixture from the active testbridge_contract_concurrent_merge.Fix: ask the author. Likely outcomes:
bridge_contract_concurrent_merge.rsfor coverage (cleanest).4. Multiple
BitmaskOrtags would race for the same URIFile:
rholang/src/rust/interpreter/rho_runtime.rs:1097-1119The URI binding loop iterates a
HashMap<Par, MergeType>. Currentlydefault_mergeable_tags()has exactly one BitmaskOr entry, so iteration order doesn't matter. But the loop pattern matches every BitmaskOr-typed entry, and they'd all map to the same URI key. With N>1 BitmaskOr entries, the last-iterated wins — non-deterministic across runs because ofHashMapiteration order.Latent issue. Today nobody adds a second BitmaskOr tag. But a future PR that does (e.g., one BitmaskOr tag for registry, another for some other concurrent-bitmap structure) would silently expose the non-determinism: different validators might bind different Pars to the same URI based on hash-seed randomness, and merge dispatch would diverge.
Fix: make the URI binding key per-tag (e.g.,
"rho:system:bitmaskMergeableTag:{tag_index}"), OR enforce one-tag-per-URI at runtime by makingurn_map.insertfor the bitmask URI return an error on second insert.5.
mergeable_tags: HashMap<Par, MergeType>should be aBTreeMap(defense-in-depth)The current consumer
is_mergeable_channelonly does.get(head)lookups (HashMap is fine), andcreate_rho_enviterates only to build the urn_map (single-entry today). No current site iterates the map to produce committed state.But the type signature
Arc<HashMap<Par, MergeType>>is exposed broadly: passed into runtime constructors, threaded throughReduce, accessible viamergeable_tagsfield. A future consumer that iterates the map to produce, say, a hash chain or a serialized list, would inherit non-determinism.Fix: convert to
Arc<BTreeMap<Par, MergeType>>everywhere. Lookup cost is O(log N) instead of O(1), but N is currently 2 — irrelevant. Future consumers can iterate without thinking.6. Stale "Mirrors / redefined" doc comment on
MergeTypeFile:
rspace++/src/rspace/merger/merging_logic.rs:12-14There is no
rholang::interpreter::merging::merge_type::MergeTypein the codebase — rholang importsMergeTypefrom rspace++ directly. The comment describes a design that was either discarded or never landed.Fix: delete the second sentence. Replace with a one-liner about the enum's semantics:
7.
pub→pub(crate)field encapsulation (5 sites)rholang/src/rust/interpreter/reduce.rs:118pub merge_chspub(crate)rholang/src/rust/interpreter/reduce.rs:119pub mergeable_tagspub(crate)rholang/src/rust/interpreter/rho_runtime.rs:264pub merge_chspub(crate)casper/src/rust/util/rholang/runtime_manager.rs:68pub mergeable_tagspub(crate)casper/src/rust/rholang/types/eval_collector.rs:10pub mergeable_channelsVerified no external consumer accesses these directly;
pub(crate)would tighten the API surface.The exception is
EvaluateResult.mergeable(interpreter.rs:22) — genuinely part of the public API contract; keep aspub.8. Committed TODO in
bridge-v2.rhoFile:
casper/tests/resources/bridge-v2.rhoPR #491 added a 340-line test fixture that includes a TODO. Committed source — the TODO will rot.
Fix: apply the change, file a tracking issue and reference its number, or delete the TODO.
9.
bridge-v2.rhoexists in 3 unsynchronized copiessystem-integration/integration-tests/resources/bridge-v2.rhof1r3node/casper/tests/resources/bridge-v2.rhosystem-integration/docs/bridge-v2.rhoIdentical content; no automatic sync. Future fixes to one will diverge from the others.
Fix: add a CI drift check that fails when copies disagree, OR consolidate to a single source of truth.
10.
Arc::new(Genesis::default_mergeable_tags())boilerplatePR #491 added 12+ test sites that build
Arc::new(Genesis::default_mergeable_tags())inline.Fix: add a helper:
11.
BITMASK_OR_TAG_PKAPI uses misleading "private key" namingFile:
rholang/src/rust/interpreter/merging/mergeable_tags.rs:37-38The "private key" isn't used for signing — only seeds the RNG. The pubkey + timestamp gives a deterministic seed; the priv key just satisfies the
unforgeable_name_rngsignature.Fix: consider renaming the API to drop the "private key" framing — a simpler
tag_name_from_seed(&[u8])would make the no-cryptographic-use clearer.12. First
rng.next()discarded intag_nameFile:
rholang/src/rust/interpreter/merging/mergeable_tags.rs:56-66The first
rng.next()output is thrown away. This matches the original derivation pattern where the constants were originally located, and the pattern is load-bearing — but undocumented.Fix: add a code comment explaining why the first
rng.next()is skipped.13. Per-call diagnostic-trace cost when enabled
File:
rholang/src/rust/interpreter/reduce.rs:799-833When
RUST_LOG=f1r3fly.merge.tag_check=traceis enabled, every channel miss does:encode_to_vec()on the head Par (proto encoding — non-trivial)encode_to_vec()on each registered tag Par (currently N=2)Fix: memoize the registered tag hex strings at runtime startup. Per-call cost drops to one head encoding + N string lookups.
14. URI binding INFO log fires per runtime construction
File:
rholang/src/rust/interpreter/rho_runtime.rs:1107-1117Logged at INFO level — one-shot startup diagnostic per runtime construction. Reasonable, but worth confirming it doesn't fire per-deploy or per-block in any test or replay path.
15. Pre-existing intra-branch wrapping vs cross-branch checked_add asymmetry
Files:
casper/src/rust/merging/conflict_set_merger.rs:540-558wrapping_add(silent)checked_addrejects on overflow / negativeFor IntegerAdd, an intra-branch overflow can produce a wrapped value that the cross-branch step accepts. Pre-existing behavior; PR #491 inherits via the new dispatch.
Verified safe under current gas-limit assumptions. Worth a code comment so future readers don't try to "fix" the inconsistency.
16. Pre-existing
wrapping_adddebug-mode behavior changeOLD code at
conflict_set_merger.rsandevent_log_index.rsused*existing += value. In release the same aswrapping_add; in debug Rust panics on overflow. PR #491's newwrapping_addalways wraps regardless of build profile.Validators run release, so consensus is unchanged. But debug-mode tests lose the implicit overflow check.
Fix: doc the silent-overflow contract on
combine_mergeable_valueso test authors know to usechecked_addif they want the debug-mode guard.Style nits
17. Patch-history phrasing in
bridge_contract_concurrent_merge.rsextended commentsFile:
casper/tests/multi_node/bridge_contract_concurrent_merge.rsTwo adjacent comment blocks anchor on patch-history.
Lines 13-20 (top-of-file commentary):
Lines 246-258 (end-of-test commentary):
After the PR merges, "this branch" no longer exists as a distinct artifact. "The fix" describes a state that's now just "the code." A future reader doesn't care that there was a fix.
Fix: rewrite the top block in invariant terms ("Bridge fixture invariant: the bridge-v2.rho contract calls
findOrCreateat init..."); drop the tail block.18. Brittle
tokio::time::sleep(1ms)between deploy constructionsFile:
casper/tests/multi_node/bridge_contract_concurrent_merge.rs:89, 101, 115, 130Pattern repeated 4× to ensure each deploy gets a distinct millisecond-resolution timestamp, avoiding signature collision when two deploys signed by the same key would otherwise be byte-identical.
Fix: use distinct keys for each deploy, OR pass an explicit
time_stampoverride that monotonically increments. Eliminates the sleep dependency.19. Misleading variable name
bridge3_deployFile:
casper/tests/multi_node/bridge_contract_concurrent_merge.rs:129The variable is named
bridge3_deploybut the source is"Nil".to_string()— it's not a bridge contract, just a sibling-trigger.Fix: rename to
sibling3_triggerornil_sibling_deploy.Docs cleanup
20. Patch-history phrasing in
docs/casper/README.mdFrames the section around what got fixed. After 12 months, "was added" stops being interesting.
Fix:
21. Cross-repo "investigation report" reference is fragile
File:
docs/testing-tracing.mdTwo problems:
Fix: drop the forward-reference, OR replace with a real tracking-issue link.
22. Python correlation snippet uniqueness assumption
File:
docs/testing-tracing.mdThe orphan-detection Python snippet uses
random_stateas a per-produce identity. Under deterministic execution, two produces with the same seed could produce identicalrandom_statevalues — they'd be aliased in the set, and one orphan could mask another.Fix: add a one-line comment in the snippet noting that
random_stateis unique per-produce in normal Rholang execution but ambiguity could arise in pathological cases (deterministic-replay testing with identical inputs).Test scenarios
Cumulative test coverage gaps from sections 1-9
These are above and beyond the 28 tests the author shipped in
a6f4884b(commutativity proptests, multi-value OR-fold, monotonicity, mismatch error propagation, TreeHashMap delete invariants, depth-1 stress, registry inserts).Critical / high-value
cargo run --bin rholang-cli -- <Registry.rho>should construct a runtime without panic. The regression is fixed but no test prevents recurrence.urn_map.contains_key("rho:system:bitmaskMergeableTag")after construction.body.deploys,body.rejected_deploys, post-state hash.non_negative_mergeable_tag_name()andbitmask_or_mergeable_tag_name()produce the exact byte sequences the rest of the system expects (locks the magic constants so future changes break visibly).Section 1 / 2
i64::MAX / max_deploys_per_block. If gas limits don't enforce this, file a separate consensus-safety issue.try_get_number_with_rndreturns None for Map values: unit test that constructs aListParWithRandomcontaining a Rholang Map and asserts the function returns None without panicking.Section 3
is_mergeable_channeltuple-arity edge cases: zero-element tuple, single-element tuple matching a tag, single-element tuple NOT matching, two-element tuple where head matches, head-doesn't-match-but-second-element-does (must NOT match — only first element checked).merge_chslifecycle: assert that two consecutiveevaluate()calls produce independentEvaluateResult.mergeablemaps (no bleed-through).urn_map.get("rho:system:bitmaskMergeableTag")is byte-identical tomergeable_tags::bitmask_or_mergeable_tag_name()and to the key under whichMergeType::BitmaskOris registered indefault_mergeable_tags().Section 5
dbut differ at depthd+1. Assert that the interior-node bit-set at depthdis mergeable (both deploys land) and the leaves at depthd+1are different and both land.Section 6
findOrCreateand assert that a transfer hangs (verifies the bug exists), then deploy withfindOrCreateand assert success. Documents the requirement._depositreferences without a correspondingfindOrCreatecall.Summary
None merge-blocking. Items #1-5 are the highest-value follow-ups (real maintenance / correctness / future-proofing); the rest are incremental polish.
Co-Authored-By: Claude noreply@anthropic.com