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
Hi maintainers and thanks for the configuration handler work that landed in #129.
This report comes from my local work on a PR attempt for issue #63 during UnitaryHack 2026. I was not able to open a PR for it because of the UnitaryHack 2026 PR limit, so I continued testing the current issue target branch locally and found a few behavior mismatches that seem worth tracking.
I tested against the current target branch, not main:
baseline: cargo test config::base passes all 8 existing config tests
The main issue is that a quantum_config.toml written to the schema described in #63 does not currently deserialize. The other items are smaller follow-ups found while checking the same code path.
1. A config written to the #63 documented schema does not load
Issue #63 describes a TOML layout intended to be easy to write manually. A config following that layout currently fails at deserialization because the implemented Rust structs expect different field names or values:
ERROR: unknown variant `simulator`, expected `Simulator` or `Device`
After changing only the enum casing, the next error is:
ERROR: missing field `device_specs`
After changing only [devices.specs] to [devices.device_specs], the next error is:
ERROR: missing field `priority`
The implementation is internally consistent when it reads back what it writes. The mismatch is specifically between the documented/issue schema and the currently accepted TOML shape.
2. add_device reverses equal-priority insertion order
Device::priority is documented as keeping equal-priority devices in insertion order, oldest first.
Config::new behaves that way because it uses a stable sort. add_device does the opposite for equal priorities.
Expected result:
Adding devices A, B, and C with equal priority should produce the same order as constructing the config with those devices at once:
["A", "B", "C"]
Actual result:
Config::new order = ["A", "B", "C"]
add_device order = ["C", "B", "A"]
That inserts the new device before existing devices with the same priority. The inline comment in add_device also says it is finding the first inactive device, but the code is actually ordering by priority.
3. extra serializes with non-deterministic key order
Device.extra is currently:
Option<HashMap<String, toml::Value>>
Because it is a std::collections::HashMap, the emitted key order changes across process runs. That makes to_file output noisy for configs with two or more extra keys.
Expected result:
Serializing the same config repeatedly should produce stable bytes, especially for a config file that may be committed and reviewed in diffs.
Actual result:
With keys inserted as k0..k7, four fresh test processes emitted:
There must be at least one `is_active = true` device.
On the current target branch:
Config::new("t".into(),"v".into(),vec![])
returns Ok, and the active-device count is 0.
This may be intentional, since commit f4ae76d is titled fix: remove check for at least one active device and add priority. I am including it only because the issue text still lists it as a requirement, while Config::new still returns a Result that is now always Ok.
Reproduction commands
From a clean checkout:
git clone https://github.com/hhat-lang/hhat_lang.git
cd hhat_lang
git fetch origin
git switch --track origin/dev/rust_impl/pre0.3beta/arch_scaffold
git pull --ff-only origin dev/rust_impl/pre0.3beta/arch_scaffold
git rev-parse HEAD
cd rust/hhat_lang
cargo test config::base
Then append this temporary test module to the end of src/config/base.rs in the Rust crate (or rust/hhat_lang/src/config/base.rs from the repository root) and run the commands below it.
#[cfg(test)]mod issue63_repro {use std::collections::HashMap;usesuper::{Config,Device,DeviceKind,DeviceQuantumLowLevel,DeviceSpecs};fnmk(name:&str,priority:isize,extra:Option<HashMap<String, toml::Value>>) -> Device{Device::new(
name.to_string(),true,"IBM".to_string(),DeviceKind::Simulator,"superconducting".to_string(),vec!["gate".to_string()],DeviceSpecs{max_n_qubits:5,qubit_topology:vec![(0,1)],},DeviceQuantumLowLevel{name:"openqasm".to_string(),package_name:"qiskit".to_string(),package_version:"0.1.0".to_string(),file_extension:"qasm".to_string(),},
extra,
priority,)}fnnames(config:&Config) -> Vec<String>{
config
.get_devices().iter().map(|device| device.name.clone()).collect()}fnload(label:&str,src:&str){match toml::from_str::<Config>(src){Ok(_) => println!(" [{label}] LOADS OK"),Err(err) => println!(" [{label}] ERROR: {}", err.message()),}}#[test]fnfinding_1_documented_schema_does_not_load(){println!("FINDING 1 - loading configs written to the documented schema:");load("documented schema (all three divergences)",r#"title = "H-hat Compiler Quantum Device Configuration"version = "0.1.0"[[devices]]name = "AerSimulator"is_active = truevendor = "IBM"device_kind = "simulator"device_platform = "superconducting"computation_paradigm = ["gate"][devices.specs]max_n_qubits = 32qubit_topology = [][devices.qll]name = "openqasm"package_name = "qiskit"package_version = "1.0.0"file_extension = "qasm""#,);load("only device_kind = \"simulator\"","title=\"t\"\nversion=\"0.1.0\"\n[[devices]]\nname=\"X\"\nis_active=true\nvendor=\"IBM\"\ndevice_kind=\"simulator\"\ndevice_platform=\"sc\"\ncomputation_paradigm=[\"gate\"]\npriority=0\n[devices.device_specs]\nmax_n_qubits=5\nqubit_topology=[]\n[devices.qll]\nname=\"openqasm\"\npackage_name=\"q\"\npackage_version=\"1\"\nfile_extension=\"qasm\"\n");load("only [devices.specs] section","title=\"t\"\nversion=\"0.1.0\"\n[[devices]]\nname=\"X\"\nis_active=true\nvendor=\"IBM\"\ndevice_kind=\"Simulator\"\ndevice_platform=\"sc\"\ncomputation_paradigm=[\"gate\"]\npriority=0\n[devices.specs]\nmax_n_qubits=5\nqubit_topology=[]\n[devices.qll]\nname=\"openqasm\"\npackage_name=\"q\"\npackage_version=\"1\"\nfile_extension=\"qasm\"\n");load("only priority omitted","title=\"t\"\nversion=\"0.1.0\"\n[[devices]]\nname=\"X\"\nis_active=true\nvendor=\"IBM\"\ndevice_kind=\"Simulator\"\ndevice_platform=\"sc\"\ncomputation_paradigm=[\"gate\"]\n[devices.device_specs]\nmax_n_qubits=5\nqubit_topology=[]\n[devices.qll]\nname=\"openqasm\"\npackage_name=\"q\"\npackage_version=\"1\"\nfile_extension=\"qasm\"\n");}#[test]fnfinding_2_add_device_reverses_equal_priority(){let via_new = Config::new("t".into(),"v".into(),vec![mk("A",1,None), mk("B",1,None), mk("C",1,None)],).unwrap();letmut via_add = Config::new("t".into(),"v".into(),vec![mk("A",1,None)]).unwrap();
via_add.add_device(mk("B",1,None));
via_add.add_device(mk("C",1,None));println!("FINDING 2 - equal priorities; devices added in order A, B, C:");println!(" Config::new order = {:?} (matches doc: oldest first)",
names(&via_new));println!(" add_device order = {:?} (doc says this should match the above)",
names(&via_add));assert_eq!(
names(&via_new),
vec!["A".to_string(),"B".to_string(),"C".to_string()]);assert_eq!(names(&via_add), names(&via_new));}#[test]fnfinding_3_extra_serialization_is_nondeterministic(){letmut extra = HashMap::new();for i in0..8{
extra.insert(format!("k{i}"), toml::Value::Integer(i));}let config = Config::new("t".into(),"v".into(),vec![mk("D",0,Some(extra))]).unwrap();let order:Vec<String> = toml::to_string(&config).unwrap().lines().filter(|line| line.trim_start().starts_with('k')).map(|line| line.split('=').next().unwrap().trim().to_string()).collect();println!("FINDING 3 - extra keys inserted k0..k7, emitted as {:?}",
order
);}#[test]fnfinding_4_partialeq_ignores_extra(){fnassert_partial_eq<T:PartialEq>(){}assert_partial_eq::<toml::Value>();let with_extra = Config::new("t".into(),"v".into(),vec![mk("D",0,Some(HashMap::from([("retired".to_string(),true.into())])),)],).unwrap();let without_extra =
Config::new("t".into(),"v".into(),vec![mk("D",0,None)]).unwrap();println!("FINDING 4 - (config with extra) == (config without extra) ? {}",
with_extra == without_extra
);assert!(with_extra == without_extra);}#[test]fnfinding_5_empty_config_accepted(){let empty = Config::new("t".into(),"v".into(),vec![]).unwrap();println!("FINDING 5 - empty config accepted; #active devices = {}",
empty.get_active_devices().count());assert_eq!(empty.get_active_devices().count(),0);}}
Run:
cargo test config::base::issue63_repro -- --nocapture
foriin 1 2 3 4;do
cargo test config::base::issue63_repro::finding_3_extra_serialization_is_nondeterministic -- --nocapture
done
Observed output on 998624d7647afd998c8243dd6e662b3f426ad60a, abridged:
FINDING 1 - loading configs written to the documented schema:
[documented schema (all three divergences)] ERROR: unknown variant `simulator`, expected `Simulator` or `Device`
[only device_kind = "simulator"] ERROR: unknown variant `simulator`, expected `Simulator` or `Device`
[only [devices.specs] section] ERROR: missing field `device_specs`
[only priority omitted] ERROR: missing field `priority`
FINDING 2 - equal priorities; devices added in order A, B, C:
Config::new order = ["A", "B", "C"] (matches doc: oldest first)
add_device order = ["C", "B", "A"] (doc says this should match the above)
assertion failed: left ["C", "B", "A"], right ["A", "B", "C"]
FINDING 3 - extra keys inserted k0..k7, emitted as ["k1", "k2", "k6", "k7", "k3", "k0", "k5", "k4"]
FINDING 4 - (config with extra) == (config without extra) ? true
FINDING 5 - empty config accepted; #active devices = 0
Finding 2 fails intentionally in the repro so the ordering mismatch is visible. The other findings print their evidence and pass.
Hi maintainers and thanks for the configuration handler work that landed in #129.
This report comes from my local work on a PR attempt for issue #63 during UnitaryHack 2026. I was not able to open a PR for it because of the UnitaryHack 2026 PR limit, so I continued testing the current issue target branch locally and found a few behavior mismatches that seem worth tracking.
I tested against the current target branch, not
main:dev/rust_impl/pre0.3beta/arch_scaffold998624d7647afd998c8243dd6e662b3f426ad60a(merge of feat: add configuration handler #129)cargo test config::basepasses all 8 existing config testsThe main issue is that a
quantum_config.tomlwritten to the schema described in #63 does not currently deserialize. The other items are smaller follow-ups found while checking the same code path.1. A config written to the #63 documented schema does not load
Issue #63 describes a TOML layout intended to be easy to write manually. A config following that layout currently fails at deserialization because the implemented Rust structs expect different field names or values:
device_kind = "simulator"/"device"device_kind = "Simulator"/"Device"unknown variant 'simulator', expected 'Simulator' or 'Device'[devices.specs][devices.device_specs]missing field 'device_specs'prioritykey in the documented schemapriority: isizemissing field 'priority'Expected result:
A hand-written config using the schema from #63 loads through
Config::from_file.Actual result:
It fails to deserialize. For example:
prints:
After changing only the enum casing, the next error is:
After changing only
[devices.specs]to[devices.device_specs], the next error is:The implementation is internally consistent when it reads back what it writes. The mismatch is specifically between the documented/issue schema and the currently accepted TOML shape.
2.
add_devicereverses equal-priority insertion orderDevice::priorityis documented as keeping equal-priority devices in insertion order, oldest first.Config::newbehaves that way because it uses a stable sort.add_devicedoes the opposite for equal priorities.Expected result:
Adding devices
A,B, andCwith equal priority should produce the same order as constructing the config with those devices at once:Actual result:
The direct cause appears to be:
That inserts the new device before existing devices with the same priority. The inline comment in
add_devicealso says it is finding the first inactive device, but the code is actually ordering by priority.3.
extraserializes with non-deterministic key orderDevice.extrais currently:Because it is a
std::collections::HashMap, the emitted key order changes across process runs. That makesto_fileoutput noisy for configs with two or moreextrakeys.Expected result:
Serializing the same config repeatedly should produce stable bytes, especially for a config file that may be committed and reviewed in diffs.
Actual result:
With keys inserted as
k0..k7, four fresh test processes emitted:The
tomlcrate'spreserve_orderfeature does not make a standardHashMapdeterministic.4.
PartialEq for ConfigignoresextraThe custom
PartialEqimplementation forConfigintentionally skipsDevice.extrawith this comment:// Since toml::Value doesn't implement PartialEq, we define the equality up to the `extra` fieldtoml::Valuedoes implementPartialEq; this compiles:Actual effect:
Two configs that differ only in
extracompare equal:This also means the current round-trip tests would not catch a regression that dropped or corrupted
extra.5. At-least-one-active-device validation is not enforced
Issue #63 says:
On the current target branch:
returns
Ok, and the active-device count is 0.This may be intentional, since commit
f4ae76dis titledfix: remove check for at least one active device and add priority. I am including it only because the issue text still lists it as a requirement, whileConfig::newstill returns aResultthat is now alwaysOk.Reproduction commands
From a clean checkout:
Then append this temporary test module to the end of
src/config/base.rsin the Rust crate (orrust/hhat_lang/src/config/base.rsfrom the repository root) and run the commands below it.Run:
Observed output on
998624d7647afd998c8243dd6e662b3f426ad60a, abridged:Finding 2 fails intentionally in the repro so the ordering mismatch is visible. The other findings print their evidence and pass.