Conversation
Only apply the heuristic discriminant_size calculation when the value wasn't already present in the JSON (discriminant_size = 0 indicates absent/defaulted). This prevents overriding explicitly set values.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Greptile SummaryThis PR lands two bug fixes in
Key observations:
Confidence Score: 3/5
Important Files Changed
Flowchart%%{init: {'theme': 'neutral'}}%%
flowchart TD
A[Deserialize JSON value] --> B{instructions array\npresent & non-empty?}
B -- No --> C[heuristic = 8]
B -- Yes --> D{All instructions pass\nSteel check?}
D --> E[For each instruction]
E --> F{discriminant key\npresent AND non-null?}
F -- Yes\nnew fix --> G[has_discriminant = true]
F -- No / null\nnew fix --> H[has_discriminant = false]
G --> I{has_discriminator\nnon-null non-empty?}
H --> I
I --> J{is_steel_discriminant\nhas_discriminant &&\n!has_discriminator}
I --> K{is_steel_short_discriminator\n!has_discriminant &&\ndisc_len == 1}
J --> L[Steel?]
K --> L
L -- all true --> M[heuristic = 1]
L -- any false --> C
M --> N[from_value to intermediate struct]
C --> N
N --> O{intermediate.discriminant_size == 0?\nnew fix}
O -- Yes\nabsent from JSON --> P[use heuristic value]
O -- No\nexplicit in JSON --> Q[keep explicit value]
P --> R[IdlSnapshot.discriminant_size]
Q --> R
Prompt To Fix All With AIThis is a comment left during a code review.
Path: hyperstack-idl/src/snapshot.rs
Line: 47-54
Comment:
**`has_discriminant` null-fix lacks a targeted test**
The fix correctly converts `ix.get("discriminant").is_some()` to a null-aware check, but no test exercises the exact failure path it closes.
The existing `test_snapshot_serde` test creates an instruction with `discriminant: None`, which serde serialises to `"discriminant": null`. However, that instruction also carries an 8-byte `discriminator` array, so `has_discriminator = true` and therefore `is_steel_discriminant = has_discriminant && !has_discriminator` evaluates to `false` **regardless** of whether `has_discriminant` is `true` or `false`. The bug is invisible in that test.
The actual regression path is: instruction has `"discriminant": null` **and** no (or null/empty) `discriminator`. Consider adding a test like:
```rust
#[test]
fn test_null_discriminant_not_treated_as_steel() {
// Anchor-style instruction: discriminant serialised as null, no discriminator.
// Before the fix, has_discriminant was true → is_steel_discriminant was true → wrong size.
let json = r#"{
"name": "my_program",
"version": "0.1.0",
"accounts": [],
"instructions": [{
"name": "do_thing",
"discriminant": null,
"accounts": [],
"args": []
}]
}"#;
let snap: IdlSnapshot = serde_json::from_str(json).unwrap();
assert_eq!(snap.discriminant_size, 8,
"null discriminant must not be treated as Steel-style");
}
```
How can I resolve this? If you propose a fix, please make it concise.Last reviewed commit: "fix: handle null dis..." |
| // Only use the heuristic if discriminant_size wasn't already present in the JSON | ||
| // (discriminant_size = 0 means it was absent / defaulted). | ||
| if intermediate.discriminant_size == 0 { | ||
| intermediate.discriminant_size = discriminant_size; | ||
| } |
There was a problem hiding this comment.
0 used as sentinel collides with a valid explicit value
The fix assumes intermediate.discriminant_size == 0 unambiguously means "the field was absent from the JSON". That holds because #[serde(default)] will produce 0 for a missing field, and 0 is not a meaningful discriminant size.
However, if a consumer ever serialises a snapshot with "discriminant_size": 0 (e.g. as an intentional sentinel they control, or simply by mistake), the heuristic will silently overwrite it — the same class of bug this PR is trying to fix.
A safer approach is to peek at the raw value (which is already available before the intermediate struct is constructed) to check whether the key is literally present:
let explicit_discriminant_size = value
.get("discriminant_size")
.and_then(|v| v.as_u64())
.map(|n| n as usize);
// ...existing heuristic computation...
let mut intermediate: IdlSnapshotIntermediate = serde_json::from_value(value)
.map_err(|e| DeError::custom(format!("Failed to deserialize IDL: {}", e)))?;
if explicit_discriminant_size.is_none() {
intermediate.discriminant_size = discriminant_size;
}This way the distinction between "key absent" and "key present with value 0" is unambiguous, and neither silently overwrites the other.
Prompt To Fix With AI
This is a comment left during a code review.
Path: hyperstack-idl/src/snapshot.rs
Line: 71-75
Comment:
**`0` used as sentinel collides with a valid explicit value**
The fix assumes `intermediate.discriminant_size == 0` unambiguously means "the field was absent from the JSON". That holds because `#[serde(default)]` will produce `0` for a missing field, and `0` is not a meaningful discriminant size.
However, if a consumer ever serialises a snapshot with `"discriminant_size": 0` (e.g. as an intentional sentinel they control, or simply by mistake), the heuristic will silently overwrite it — the same class of bug this PR is trying to fix.
A safer approach is to peek at the raw `value` (which is already available before the intermediate struct is constructed) to check whether the key is literally present:
```rust
let explicit_discriminant_size = value
.get("discriminant_size")
.and_then(|v| v.as_u64())
.map(|n| n as usize);
// ...existing heuristic computation...
let mut intermediate: IdlSnapshotIntermediate = serde_json::from_value(value)
.map_err(|e| DeError::custom(format!("Failed to deserialize IDL: {}", e)))?;
if explicit_discriminant_size.is_none() {
intermediate.discriminant_size = discriminant_size;
}
```
This way the distinction between "key absent" and "key present with value 0" is unambiguous, and neither silently overwrites the other.
How can I resolve this? If you propose a fix, please make it concise.| // Only use the heuristic if discriminant_size wasn't already present in the JSON | ||
| // (discriminant_size = 0 means it was absent / defaulted). | ||
| if intermediate.discriminant_size == 0 { | ||
| intermediate.discriminant_size = discriminant_size; | ||
| } |
There was a problem hiding this comment.
No test covers the fixed scenario
The existing test_ore_instructions_have_discriminators test only verifies the heuristic path (the ore.json fixture has no discriminant_size key, so the heuristic fires). There is no test that:
- Provides a JSON with Steel-style instructions (heuristic would yield
1) and an explicit"discriminant_size": 8. - Asserts that
8is preserved after deserialisation.
Without this, a future refactor could silently re-introduce the regression. Consider adding a unit test like:
#[test]
fn test_explicit_discriminant_size_is_preserved() {
// Steel-style instructions (discriminant field, no discriminator),
// but discriminant_size is explicitly overridden to 8.
let json = r#"{
"name": "my_program",
"version": "0.1.0",
"discriminant_size": 8,
"accounts": [],
"instructions": [{
"name": "do_thing",
"discriminant": {"value": 0},
"accounts": [],
"args": []
}]
}"#;
let snapshot: IdlSnapshot = serde_json::from_str(json)
.expect("should deserialize");
assert_eq!(
snapshot.discriminant_size, 8,
"explicit discriminant_size should not be overridden by heuristic"
);
}Prompt To Fix With AI
This is a comment left during a code review.
Path: hyperstack-idl/src/snapshot.rs
Line: 71-75
Comment:
**No test covers the fixed scenario**
The existing `test_ore_instructions_have_discriminators` test only verifies the heuristic path (the ore.json fixture has no `discriminant_size` key, so the heuristic fires). There is no test that:
1. Provides a JSON with Steel-style instructions (heuristic would yield `1`) **and** an explicit `"discriminant_size": 8`.
2. Asserts that `8` is preserved after deserialisation.
Without this, a future refactor could silently re-introduce the regression. Consider adding a unit test like:
```rust
#[test]
fn test_explicit_discriminant_size_is_preserved() {
// Steel-style instructions (discriminant field, no discriminator),
// but discriminant_size is explicitly overridden to 8.
let json = r#"{
"name": "my_program",
"version": "0.1.0",
"discriminant_size": 8,
"accounts": [],
"instructions": [{
"name": "do_thing",
"discriminant": {"value": 0},
"accounts": [],
"args": []
}]
}"#;
let snapshot: IdlSnapshot = serde_json::from_str(json)
.expect("should deserialize");
assert_eq!(
snapshot.discriminant_size, 8,
"explicit discriminant_size should not be overridden by heuristic"
);
}
```
How can I resolve this? If you propose a fix, please make it concise.
Only apply the heuristic discriminant_size calculation when the value wasn't already present in the JSON (discriminant_size = 0 indicates absent/defaulted). This prevents overriding explicitly set values.