diff --git a/diagnostic/build-182f71ab.json b/diagnostic/build-182f71ab.json new file mode 100644 index 00000000..10822b43 --- /dev/null +++ b/diagnostic/build-182f71ab.json @@ -0,0 +1,86 @@ +{ + "generated_at": "2026-06-19T15:45:31.881021+00:00", + "commit": "182f71ab", + "diagnostic_logd": "diagnostic/build-182f71ab.logd", + "diagnostic_logd_error": null, + "chunked": false, + "chunk_size_bytes": null, + "password": "24bc9188a5b5373bc6d7", + "decrypt_command": "encryptly unpack diagnostic/build-182f71ab.logd --password 24bc9188a5b5373bc6d7", + "total_modules": 10, + "passed": 8, + "failed": 2, + "modules": [ + { + "name": "backend", + "status": "PASS", + "elapsed_seconds": 37.607, + "artifact": "/Volumes/Soengkit\u786c\u76d8/Codex\u65b0\u9879\u76ee\u4e0e\u8f93\u51fa/2026-06-19_Agent Bounty Workbench/02_candidate_bounties/peter7896-zeroeye-ws-delta-4/backend/target", + "output": "\u001b[1m\u001b[92m Compiling\u001b[0m proc-macro2 v1.0.106\n\u001b[1m\u001b[92m Compiling\u001b[0m quote v1.0.45\n\u001b[1m\u001b[92m Compiling\u001b[0m unicode-ident v1.0.24\n\u001b[1m\u001b[92m Compiling\u001b[0m libc v0.2.186\n\u001b[1m\u001b[92m Compiling\u001b[0m cfg-if v1.0.4\n\u001b[1m\u001b[92m Compiling\u001b[0m smallvec v1.15.2\n\u001b[1m\u001b[92m Compiling\u001b[0m pin-project-lite v0.2.17\n\u001b[1m\u001b[92m Compiling\u001b[0m stable_deref_trait v1.2.1\n\u001b[1m\u001b[92m Compiling\u001b[0m memchr v2.8.2\n\u001b[1m\u001b[92m Compiling\u001b[0m once_cell v1.21.4\n\u001b[1m\u001b[92m Compiling\u001b[0m futures-core v0.3.32\n\u001b[1m\u001b[92m Compiling\u001b[0m parking_lot_core v0.9.12\n\u001b[1m\u001b[92m Compiling\u001b[0m serde_core v1.0.228\n\u001b[1m\u001b[92m Compiling\u001b[0m bytes v1.11.1\n\u001b[1m\u001b[92m Compiling\u001b[0m scopeguard v1.2.0\n\u001b[1m\u001b[92m Compiling\u001b[0m core-foundation-sys v0.8.7\n\u001b[1m\u001b[92m Compiling\u001b[0m itoa v1.0.18\n\u001b[1m\u001b[92m Compiling\u001b[0m lock_api v0.4.14\n\u001b[1m\u001b[92m Compiling\u001b[0m futures-sink v0.3.32\n\u001b[1m\u001b[92m Compiling\u001b[0m serde v1.0.228\n\u001b[1m\u001b[92m Compiling\u001b[0m bitflags v2.13.0\n\u001b[1m\u001b[92m Compiling\u001b[0m slab v0.4.12\n\u001b[1m\u001b[92m Compiling\u001b[0m futures-channel v0.3.32\n\u001b[1m\u001b[92m Compiling\u001b[0m tracing-core v0.1.36\n\u001b[1m\u001b[92m Compiling\u001b[0m writeable v0.6.3\n\u001b[1m\u001b[92m Compiling\u001b[0m litemap v0.8.2\n\u001b[1m\u001b[92m Compiling\u001b[0m http v1.4.2\n\u001b[1m\u001b[92m Compiling\u001b[0m equivalent v1.0.2\n\u001b[1m\u001b[92m Compiling\u001b[0m futures-task v0.3.32\n\u001b[1m\u001b[92m Compiling\u001b[0m utf8_iter v1.0.4\n\u001b[1m\u001b[92m Compiling\u001b[0m icu_properties_data v2.2.0\n\u001b[1m\u001b[92m Compiling\u001b[0m icu_normalizer_data v2.2.0\n\u001b[1m\u001b[92m Compiling\u001b[0m hashbrown v0.17.1\n\u001b[1m\u001b[92m Compiling\u001b[0m futures-io v0.3.32\n\u001b[1m\u001b[92m Compiling\u001b[0m getrandom v0.4.2\n\u001b[1m\u001b[92m Compiling\u001b[0m indexmap v2.14.0\n\u001b[1m\u001b[92m Compiling\u001b[0m rustix v1.1.4\n\u001b[1m\u001b[92m Compiling\u001b[0m percent-encoding v2.3.2\n\u001b[1m\u001b[92m Compiling\u001b[0m version_check v0.9.5\n\u001b[1m\u001b[92m Compiling\u001b[0m errno v0.3.14\n\u001b[1m\u001b[92m Compiling\u001b[0m signal-hook-registry v1.4.8\n\u001b[1m\u001b[92m Compiling\u001b[0m socket2 v0.6.4\n\u001b[1m\u001b[92m Compiling\u001b[0m mio v1.2.1\n\u001b[1m\u001b[92m Compiling\u001b[0m generic-array v0.14.7\n\u001b[1m\u001b[92m Compiling\u001b[0m parking_lot v0.12.5\n\u001b[1m\u001b[92m Compiling\u001b[0m http-body v1.0.1\n\u001b[1m\u001b[92m Compiling\u001b[0m httparse v1.10.1\n\u001b[1m\u001b[92m Compiling\u001b[0m system-configuration-sys v0.6.0\n\u001b[1m\u001b[92m Compiling\u001b[0m security-framework-sys v2.17.0\n\u001b[1m\u001b[92m Compiling\u001b[0m core-foundation v0.10.1\n\u001b[1m\u001b[92m Compiling\u001b[0m syn v2.0.117\n\u001b[1m\u001b[92m Compiling\u001b[0m fnv v1.0.7\n\u001b[1m\u001b[92m Compiling\u001b[0m atomic-waker v1.1.2\n\u001b[1m\u001b[92m Compiling\u001b[0m typenum v1.20.1\n\u001b[1m\u001b[92m Compiling\u001b[0m tower-service v0.3.3\n\u001b[1m\u001b[92m Compiling\u001b[0m fastrand v2.4.1\n\u001b[1m\u001b[92m Compiling\u001b[0m try-lock v0.2.5\n\u001b[1m\u001b[92m Compiling\u001b[0m zmij v1.0.21\n\u001b[1m\u001b[92m Compiling\u001b[0m native-tls v0.2.18\n\u001b[1m\u001b[92m Compiling\u001b[0m want v0.3.1\n\u001b[1m\u001b[92m Compiling\u001b[0m security-framework v3.7.0\n\u001b[1m\u001b[92m Compiling\u001b[0m core-foundation v0.9.4\n\u001b[1m\u001b[92m Compiling\u001b[0m form_urlencoded v1.2.2\n\u001b[1m\u001b[92m Compiling\u001b[0m aho-corasick v1.1.4\n\u001b[1m\u001b[92m Compiling\u001b[0m regex-syntax v0.8.11\n\u001b[1m\u001b[92m Compiling\u001b[0m synstructure v0.13.2\n\u001b[1m\u001b[92m Compiling\u001b[0m serde_json v1.0.150\n\u001b[1m\u001b[92m Compiling\u001b[0m tempfile v3.27.0\n\u001b[1m\u001b[92m Compiling\u001b[0m zerofrom-derive v0.1.7\n\u001b[1m\u001b[92m Compiling\u001b[0m yoke-derive v0.8.2\n\u001b[1m\u001b[92m Compiling\u001b[0m zerovec-derive v0.11.3\n\u001b[1m\u001b[92m Compiling\u001b[0m displaydoc v0.2.6\n\u001b[1m\u001b[92m Compiling\u001b[0m serde_derive v1.0.228\n\u001b[1m\u001b[92m Compiling\u001b[0m tokio-macros v2.7.0\n\u001b[1m\u001b[92m Compiling\u001b[0m futures-macro v0.3.32\n\u001b[1m\u001b[92m Compiling\u001b[0m tracing-attributes v0.1.31\n\u001b[1m\u001b[92m Compiling\u001b[0m utf8parse v0.2.2\n\u001b[1m\u001b[92m Compiling\u001b[0m futures-util v0.3.32\n\u001b[1m\u001b[92m Compiling\u001b[0m tokio v1.52.3\n\u001b[1m\u001b[92m Compiling\u001b[0m autocfg v1.5.1\n\u001b[1m\u001b[92m Compiling\u001b[0m regex-automata v0.4.14\n\u001b[1m\u001b[92m Compiling\u001b[0m system-configuration v0.7.0\n\u001b[1m\u001b[92m Compiling\u001b[0m zerofrom v0.1.8\n\u001b[1m\u001b[92m Compiling\u001b[0m tracing v0.1.44\n\u001b[1m\u001b[92m Compiling\u001b[0m anstyle-parse v1.0.0\n\u001b[1m\u001b[92m Compiling\u001b[0m sync_wrapper v1.0.2\n\u001b[1m\u001b[92m Compiling\u001b[0m num-traits v0.2.19\n\u001b[1m\u001b[92m Compiling\u001b[0m crossbeam-utils v0.8.21\n\u001b[1m\u001b[92m Compiling\u001b[0m tower-layer v0.3.3\n\u001b[1m\u001b[92m Compiling\u001b[0m ipnet v2.12.0\n\u001b[1m\u001b[92m Compiling\u001b[0m anstyle v1.0.14\n\u001b[1m\u001b[92m Compiling\u001b[0m base64 v0.22.1\n\u001b[1m\u001b[92m Compiling\u001b[0m is_terminal_polyfill v1.70.2\n\u001b[1m\u001b[92m Compiling\u001b[0m yoke v0.8.3\n\u001b[1m\u001b[92m Compiling\u001b[0m log v0.4.32\n\u001b[1m\u001b[92m Compiling\u001b[0m anstyle-query v1.1.5\n\u001b[1m\u001b[92m Compiling\u001b[0m colorchoice v1.0.5\n\u001b[1m\u001b[92m Compiling\u001b[0m anstream v1.0.0\n\u001b[1m\u001b[92m Compiling\u001b[0m block-buffer v0.10.4\n\u001b[1m\u001b[92m Compiling\u001b[0m crypto-common v0.1.7\n\u001b[1m\u001b[92m Compiling\u001b[0m http-body-util v0.1.3\n\u001b[1m\u001b[92m Compiling\u001b[0m clap_lex v1.1.0\n\u001b[1m\u001b[92m Compiling\u001b[0m toml_write v0.1.2\n\u001b[1m\u001b[92m Compiling\u001b[0m heck v0.5.0\n\u001b[1m\u001b[92m Compiling\u001b[0m anyhow v1.0.102\n\u001b[1m\u001b[92m Compiling\u001b[0m ryu v1.0.23\n\u001b[1m\u001b[92m Compiling\u001b[0m lazy_static v1.5.0\n\u001b[1m\u001b[92m Compiling\u001b[0m winnow v0.7.15\n\u001b[1m\u001b[92m Compiling\u001b[0m zeroize v1.9.0\n\u001b[1m\u001b[92m Compiling\u001b[0m strsim v0.11.1\n\u001b[1m\u001b[92m Compiling\u001b[0m thiserror v2.0.18\n\u001b[1m\u001b[92m Compiling\u001b[0m clap_builder v4.6.0\n\u001b[1m\u001b[92m Compiling\u001b[0m serde_spanned v0.6.9\n\u001b[1m\u001b[92m Compiling\u001b[0m toml_datetime v0.6.11\n\u001b[1m\u001b[92m Compiling\u001b[0m tracing-serde v0.2.0\n\u001b[1m\u001b[92m Compiling\u001b[0m toml_edit v0.22.27\n\u001b[1m\u001b[92m Compiling\u001b[0m serde_urlencoded v0.7.1\n\u001b[1m\u001b[92m Compiling\u001b[0m zerovec v0.11.6\n\u001b[1m\u001b[92m Compiling\u001b[0m zerotrie v0.2.4\n\u001b[1m\u001b[92m Compiling\u001b[0m futures-executor v0.3.32\n\u001b[1m\u001b[92m Compiling\u001b[0m rustls-pki-types v1.14.1\n\u001b[1m\u001b[92m Compiling\u001b[0m sharded-slab v0.1.7\n\u001b[1m\u001b[92m Compiling\u001b[0m clap_derive v4.6.1\n\u001b[1m\u001b[92m Compiling\u001b[0m matchers v0.2.0\n\u001b[1m\u001b[92m Compiling\u001b[0m digest v0.10.7\n\u001b[1m\u001b[92m Compiling\u001b[0m tracing-log v0.2.0\n\u001b[1m\u001b[92m Compiling\u001b[0m thiserror-impl v2.0.18\n\u001b[1m\u001b[92m Compiling\u001b[0m cpufeatures v0.2.17\n\u001b[1m\u001b[92m Compiling\u001b[0m iana-time-zone v0.1.65\n\u001b[1m\u001b[92m Compiling\u001b[0m thread_local v1.1.9\n\u001b[1m\u001b[92m Compiling\u001b[0m encoding_rs v0.8.35\n\u001b[1m\u001b[92m Compiling\u001b[0m hashbrown v0.14.5\n\u001b[1m\u001b[92m Compiling\u001b[0m nu-ansi-term v0.50.3\n\u001b[1m\u001b[92m Compiling\u001b[0m mime v0.3.17\n\u001b[1m\u001b[92m Compiling\u001b[0m toml v0.8.23\n\u001b[1m\u001b[92m Compiling\u001b[0m tinystr v0.8.3\n\u001b[1m\u001b[92m Compiling\u001b[0m potential_utf v0.1.5\n\u001b[1m\u001b[92m Compiling\u001b[0m tokio-util v0.7.18\n\u001b[1m\u001b[92m Compiling\u001b[0m icu_collections v2.2.0\n\u001b[1m\u001b[92m Compiling\u001b[0m icu_locale_core v2.2.0\n\u001b[1m\u001b[92m Compiling\u001b[0m tokio-native-tls v0.3.1\n\u001b[1m\u001b[92m Compiling\u001b[0m tower v0.5.3\n\u001b[1m\u001b[92m Compiling\u001b[0m clap v4.6.1\n\u001b[1m\u001b[92m Compiling\u001b[0m dashmap v6.2.1\n\u001b[1m\u001b[92m Compiling\u001b[0m h2 v0.4.14\n\u001b[1m\u001b[92m Compiling\u001b[0m tracing-subscriber v0.3.23\n\u001b[1m\u001b[92m Compiling\u001b[0m chrono v0.4.45\n\u001b[1m\u001b[92m Compiling\u001b[0m sha2 v0.10.9\n\u001b[1m\u001b[92m Compiling\u001b[0m icu_provider v2.2.0\n\u001b[1m\u001b[92m Compiling\u001b[0m futures v0.3.32\n\u001b[1m\u001b[92m Compiling\u001b[0m regex v1.12.4\n\u001b[1m\u001b[92m Compiling\u001b[0m async-trait v0.1.89\n\u001b[1m\u001b[92m Compiling\u001b[0m uuid v1.23.3\n\u001b[1m\u001b[92m Compiling\u001b[0m icu_properties v2.2.0\n\u001b[1m\u001b[92m Compiling\u001b[0m icu_normalizer v2.2.0\n\u001b[1m\u001b[92m Compiling\u001b[0m hyper v1.10.1\n\u001b[1m\u001b[92m Compiling\u001b[0m hyper-util v0.1.20\n\u001b[1m\u001b[92m Compiling\u001b[0m idna_adapter v1.2.2\n\u001b[1m\u001b[92m Compiling\u001b[0m idna v1.1.0\n\u001b[1m\u001b[92m Compiling\u001b[0m url v2.5.8\n\u001b[1m\u001b[92m Compiling\u001b[0m tower-http v0.6.11\n\u001b[1m\u001b[92m Compiling\u001b[0m hyper-tls v0.6.0\n\u001b[1m\u001b[92m Compiling\u001b[0m reqwest v0.12.28\n\u001b[1m\u001b[92m Compiling\u001b[0m tent-backend v0.1.0 (/Volumes/Soengkit\u786c\u76d8/Codex\u65b0\u9879\u76ee\u4e0e\u8f93\u51fa/2026-06-19_Agent Bounty Workbench/02_candidate_bounties/peter7896-zeroeye-ws-delta-4/backend)\n\u001b[1m\u001b[33mwarning\u001b[0m\u001b[1m: unused import: `warn`\u001b[0m\n \u001b[1m\u001b[94m--> \u001b[0msrc/ai/embeddings.rs:28:28\n \u001b[1m\u001b[94m|\u001b[0m\n\u001b[1m\u001b[94m28\u001b[0m \u001b[1m\u001b[94m|\u001b[0m use tracing::{debug, info, warn};\n \u001b[1m\u001b[94m|\u001b[0m \u001b[1m\u001b[33m^^^^\u001b[0m\n \u001b[1m\u001b[94m|\u001b[0m\n \u001b[1m\u001b[94m= \u001b[0m\u001b[1mnote\u001b[0m: `#[warn(unused_imports)]` (part of `#[warn(unused)]`) on by default\n\n\u001b[1m\u001b[33mwarning\u001b[0m\u001b[1m: unused import: `error`\u001b[0m\n \u001b[1m\u001b[94m--> \u001b[0msrc/ai/inference.rs:25:22\n \u001b[1m\u001b[94m|\u001b[0m\n\u001b[1m\u001b[94m25\u001b[0m \u001b[1m\u001b[94m|\u001b[0m use tracing::{debug, error, info, warn};\n \u001b[1m\u001b[94m|\u001b[0m \u001b[1m\u001b[33m^^^^^\u001b[0m\n\n\u001b[1m\u001b[33mwarning\u001b[0m\u001b[1m: unused imports: `error` and `warn`\u001b[0m\n \u001b[1m\u001b[94m--> \u001b[0msrc/ai/mod.rs:40:22\n \u001b[1m\u001b[94m|\u001b[0m\n\u001b[1m\u001b[94m40\u001b[0m \u001b[1m\u001b[94m|\u001b[0m use tracing::{debug, error, info, warn};\n \u001b[1m\u001b[94m|\u001b[0m \u001b[1m\u001b[33m^^^^^\u001b[0m \u001b[1m\u001b[33m^^^^\u001b[0m\n\n\u001b[1m\u001b[33mwarning\u001b[0m\u001b[1m: unused imports: `c_int` and `c_uint`\u001b[0m\n \u001b[1m\u001b[94m--> \u001b[0msrc/connector/bridge.rs:38:20\n \u001b[1m\u001b[94m|\u001b[0m\n\u001b[1m\u001b[94m38\u001b[0m \u001b[1m\u001b[94m|\u001b[0m use std::os::raw::{c_int, c_uint, c_ulong};\n \u001b[1m\u001b[94m|\u001b[0m \u001b[1m\u001b[33m^^^^^\u001b[0m \u001b[1m\u001b[33m^^^^^^\u001b[0m\n\n\u001b[1m\u001b[33mwarning\u001b[0m\u001b[1m: unused import: `std::ffi::CString`\u001b[0m\n \u001b[1m\u001b[94m--> \u001b[0msrc/connector/legacy.rs:35:5\n \u001b[1m\u001b[94m|\u001b[0m\n\u001b[1m\u001b[94m35\u001b[0m \u001b[1m\u001b[94m|\u001b[0m use std::ffi::CString;\n \u001b[1m\u001b[94m|\u001b[0m \u001b[1m\u001b[33m^^^^^^^^^^^^^^^^^\u001b[0m\n\n\u001b[1m\u001b[33mwarning\u001b[0m\u001b[1m: unused import: `c_char`\u001b[0m\n \u001b[1m\u001b[94m--> \u001b[0msrc/connector/legacy.rs:36:20\n \u001b[1m\u001b[94m|\u001b[0m\n\u001b[1m\u001b[94m36\u001b[0m \u001b[1m\u001b[94m|\u001b[0m use std::os::raw::{c_char, c_ulong};\n \u001b[1m\u001b[94m|\u001b[0m \u001b[1m\u001b[33m^^^^^^\u001b[0m\n\n\u001b[1m\u001b[33mwarning\u001b[0m\u001b[1m: unused import: `CStr`\u001b[0m\n \u001b[1m\u001b[94m--> \u001b[0msrc/connector/types.rs:27:16\n \u001b[1m\u001b[94m|\u001b[0m\n\u001b[1m\u001b[94m27\u001b[0m \u001b[1m\u001b[94m|\u001b[0m use std::ffi::{CStr, CString};\n \u001b[1m\u001b[94m|\u001b[0m \u001b[1m\u001b[33m^^^^\u001b[0m\n\n\u001b[1m\u001b[33mwarning\u001b[0m\u001b[1m: unused imports: `c_double` and `c_long`\u001b[0m\n \u001b[1m\u001b[94m--> \u001b[0msrc/connector/types.rs:29:28\n \u001b[1m\u001b[94m|\u001b[0m\n\u001b[1m\u001b[94m29\u001b[0m \u001b[1m\u001b[94m|\u001b[0m use std::os::raw::{c_char, c_double, c_int, c_uint, c_void, c_long, c_ulong};\n \u001b[1m\u001b[94m|\u001b[0m \u001b[1m\u001b[33m^^^^^^^^\u001b[0m \u001b[1m\u001b[33m^^^^^^\u001b[0m\n\n\u001b[1m\u001b[33mwarning\u001b[0m\u001b[1m: unused import: `AtomicBool`\u001b[0m\n \u001b[1m\u001b[94m--> \u001b[0msrc/legacy/deprecations.rs:14:25\n \u001b[1m\u001b[94m|\u001b[0m\n\u001b[1m\u001b[94m14\u001b[0m \u001b[1m\u001b[94m|\u001b[0m use std::sync::atomic::{AtomicBool, AtomicU64, AtomicUsize, Ordering};\n \u001b[1m\u001b[94m|\u001b[0m \u001b[1m\u001b[33m^^^^^^^^^^\u001b[0m\n\n\u001b[1m\u001b[33mwarning\u001b[0m\u001b[1m: unused imports: `EntityKind` and `legacy_normalize_phone_number`\u001b[0m\n \u001b[1m\u001b[94m--> \u001b[0msrc/legacy/v1_compat.rs:8:47\n \u001b[1m\u001b[94m|\u001b[0m\n\u001b[1m\u001b[94m8\u001b[0m \u001b[1m\u001b[94m|\u001b[0m use crate::legacy::deprecations::{LegacyUuid, EntityKind, LegacyPagination, legacy_normalize_phone_number};\n \u001b[1m\u001b[94m|\u001b[0m \u001b[1m\u001b[33m^^^^^^^^^^\u001b[0m \u001b[1m\u001b[33m^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\u001b[0m\n\n\u001b[1m\u001b[33mwarning\u001b[0m\u001b[1m: unused import: `super::ProtocolError`\u001b[0m\n \u001b[1m\u001b[94m--> \u001b[0msrc/protocol/validate.rs:27:5\n \u001b[1m\u001b[94m|\u001b[0m\n\u001b[1m\u001b[94m27\u001b[0m \u001b[1m\u001b[94m|\u001b[0m use super::ProtocolError;\n \u001b[1m\u001b[94m|\u001b[0m \u001b[1m\u001b[33m^^^^^^^^^^^^^^^^^^^^\u001b[0m\n\n\u001b[1m\u001b[33mwarning\u001b[0m\u001b[1m: unused import: `MAX_MESSAGE_SIZE`\u001b[0m\n \u001b[1m\u001b[94m--> \u001b[0msrc/protocol/codec.rs:25:38\n \u001b[1m\u001b[94m|\u001b[0m\n\u001b[1m\u001b[94m25\u001b[0m \u001b[1m\u001b[94m|\u001b[0m use crate::protocol::{ProtocolError, MAX_MESSAGE_SIZE, MIN_COMPATIBLE_VERSION, PROTOCOL_VERSION};\n \u001b[1m\u001b[94m|\u001b[0m \u001b[1m\u001b[33m^^^^^^^^^^^^^^^^\u001b[0m\n\n\u001b[1m\u001b[33mwarning\u001b[0m\u001b[1m: unused import: `Write`\u001b[0m\n \u001b[1m\u001b[94m--> \u001b[0msrc/protocol/codec.rs:26:29\n \u001b[1m\u001b[94m|\u001b[0m\n\u001b[1m\u001b[94m26\u001b[0m \u001b[1m\u001b[94m|\u001b[0m use std::io::{Cursor, Read, Write};\n \u001b[1m\u001b[94m|\u001b[0m \u001b[1m\u001b[33m^^^^^\u001b[0m\n\n\u001b[1m\u001b[33mwarning\u001b[0m\u001b[1m: unused import: `Ordering`\u001b[0m\n \u001b[1m\u001b[94m--> \u001b[0msrc/protocol/rpc.rs:25:36\n \u001b[1m\u001b[94m|\u001b[0m\n\u001b[1m\u001b[94m25\u001b[0m \u001b[1m\u001b[94m|\u001b[0m use std::sync::atomic::{AtomicU64, Ordering};\n \u001b[1m\u001b[94m|\u001b[0m \u001b[1m\u001b[33m^^^^^^^^\u001b[0m\n\n\u001b[1m\u001b[33mwarning\u001b[0m\u001b[1m: unused imports: `Duration` and `Instant`\u001b[0m\n \u001b[1m\u001b[94m--> \u001b[0msrc/protocol/rpc.rs:27:17\n \u001b[1m\u001b[94m|\u001b[0m\n\u001b[1m\u001b[94m27\u001b[0m \u001b[1m\u001b[94m|\u001b[0m use std::time::{Duration, Instant};\n \u001b[1m\u001b[94m|\u001b[0m \u001b[1m\u001b[33m^^^^^^^^\u001b[0m \u001b[1m\u001b[33m^^^^^^^\u001b[0m\n\n\u001b[1m\u001b[33mwarning\u001b[0m\u001b[1m: unused imports: `Deserialize` and `Serialize`\u001b[0m\n \u001b[1m\u001b[94m--> \u001b[0msrc/protocol/rpc.rs:28:13\n \u001b[1m\u001b[94m|\u001b[0m\n\u001b[1m\u001b[94m28\u001b[0m \u001b[1m\u001b[94m|\u001b[0m use serde::{Deserialize, Serialize};\n \u001b[1m\u001b[94m|\u001b[0m \u001b[1m\u001b[33m^^^^^^^^^^^\u001b[0m \u001b[1m\u001b[33m^^^^^^^^^\u001b[0m\n\n\u001b[1m\u001b[33mwarning\u001b[0m\u001b[1m: unused import: `MAX_MESSAGE_SIZE`\u001b[0m\n \u001b[1m\u001b[94m--> \u001b[0msrc/protocol/rpc.rs:31:28\n \u001b[1m\u001b[94m|\u001b[0m\n\u001b[1m\u001b[94m31\u001b[0m \u001b[1m\u001b[94m|\u001b[0m use super::{ProtocolError, MAX_MESSAGE_SIZE, DEFAULT_TIMEOUT_MS};\n \u001b[1m\u001b[94m|\u001b[0m \u001b[1m\u001b[33m^^^^^^^^^^^^^^^^\u001b[0m\n\n\u001b[1m\u001b[33mwarning\u001b[0m\u001b[1m: unused imports: `FrameDecoder` and `FrameEncoder`\u001b[0m\n \u001b[1m\u001b[94m--> \u001b[0msrc/protocol/rpc.rs:32:27\n \u001b[1m\u001b[94m|\u001b[0m\n\u001b[1m\u001b[94m32\u001b[0m \u001b[1m\u001b[94m|\u001b[0m use super::codec::{Frame, FrameEncoder, FrameDecoder, FLAG_REQUIRES_ACK};\n \u001b[1m\u001b[94m|\u001b[0m \u001b[1m\u001b[33m^^^^^^^^^^^^\u001b[0m \u001b[1m\u001b[33m^^^^^^^^^^^^\u001b[0m\n\n\u001b[1m\u001b[33mwarning\u001b[0m\u001b[1m: use of deprecated unit variant `legacy::deprecations::EntityKind::Team`: Teams are now Organizations. Use Organization instead.\u001b[0m\n \u001b[1m\u001b[94m--> \u001b[0msrc/legacy/deprecations.rs:244:25\n \u001b[1m\u001b[94m|\u001b[0m\n\u001b[1m\u001b[94m244\u001b[0m \u001b[1m\u001b[94m|\u001b[0m EntityKind::Team => \"org\", // Legacy mapping\n \u001b[1m\u001b[94m|\u001b[0m \u001b[1m\u001b[33m^^^^\u001b[0m\n \u001b[1m\u001b[94m|\u001b[0m\n \u001b[1m\u001b[94m= \u001b[0m\u001b[1mnote\u001b[0m: `#[warn(deprecated)]` on by default\n\n\u001b[1m\u001b[33mwarning\u001b[0m\u001b[1m: use of deprecated unit variant `legacy::deprecations::EntityKind::Project`: Projects were removed in the Platform v2 migration\u001b[0m\n \u001b[1m\u001b[94m--> \u001b[0msrc/legacy/deprecations.rs:245:25\n \u001b[1m\u001b[94m|\u001b[0m\n\u001b[1m\u001b[94m245\u001b[0m \u001b[1m\u001b[94m|\u001b[0m EntityKind::Project => \"workspace\", // Legacy mapping\n \u001b[1m\u001b[94m|\u001b[0m \u001b[1m\u001b[33m^^^^^^^\u001b[0m\n\n\u001b[1m\u001b[33mwarning\u001b[0m\u001b[1m: use of deprecated unit variant `legacy::deprecations::EntityKind::Team`: Teams are now Organizations. Use Organization instead.\u001b[0m\n \u001b[1m\u001b[94m--> \u001b[0msrc/legacy/deprecations.rs:266:25\n \u001b[1m\u001b[94m|\u001b[0m\n\u001b[1m\u001b[94m266\u001b[0m \u001b[1m\u001b[94m|\u001b[0m EntityKind::Team\n \u001b[1m\u001b[94m|\u001b[0m \u001b[1m\u001b[33m^^^^\u001b[0m\n\n\u001b[1m\u001b[33mwarning\u001b[0m\u001b[1m: use of deprecated unit variant `legacy::deprecations::EntityKind::Project`: Projects were removed in the Platform v2 migration\u001b[0m\n \u001b[1m\u001b[94m--> \u001b[0msrc/legacy/deprecations.rs:267:31\n \u001b[1m\u001b[94m|\u001b[0m\n\u001b[1m\u001b[94m267\u001b[0m \u001b[1m\u001b[94m|\u001b[0m | EntityKind::Project\n \u001b[1m\u001b[94m|\u001b[0m \u001b[1m\u001b[33m^^^^^^^\u001b[0m\n\n\u001b[1m\u001b[33mwarning\u001b[0m\u001b[1m: variable does not need to be mutable\u001b[0m\n \u001b[1m\u001b[94m--> \u001b[0msrc/connector/bridge.rs:317:13\n \u001b[1m\u001b[94m|\u001b[0m\n\u001b[1m\u001b[94m317\u001b[0m \u001b[1m\u001b[94m|\u001b[0m let mut buffer = unsafe { &mut *c_buffer };\n \u001b[1m\u001b[94m|\u001b[0m \u001b[1m\u001b[94m----\u001b[0m\u001b[1m\u001b[33m^^^^^^\u001b[0m\n \u001b[1m\u001b[94m|\u001b[0m \u001b[1m\u001b[94m|\u001b[0m\n \u001b[1m\u001b[94m|\u001b[0m \u001b[1m\u001b[94mhelp: remove this `mut`\u001b[0m\n \u001b[1m\u001b[94m|\u001b[0m\n \u001b[1m\u001b[94m= \u001b[0m\u001b[1mnote\u001b[0m: `#[warn(unused_mut)]` (part of `#[warn(unused)]`) on by default\n\n\u001b[1m\u001b[33mwarning\u001b[0m\u001b[1m: unused variable: `initialized`\u001b[0m\n \u001b[1m\u001b[94m--> \u001b[0msrc/connector/bridge.rs:440:13\n \u001b[1m\u001b[94m|\u001b[0m\n\u001b[1m\u001b[94m440\u001b[0m \u001b[1m\u001b[94m|\u001b[0m let initialized = Arc::new(AtomicBool::new(true));\n \u001b[1m\u001b[94m|\u001b[0m \u001b[1m\u001b[33m^^^^^^^^^^^\u001b[0m \u001b[1m\u001b[33mhelp: if this is intentional, prefix it with an underscore: `_initialized`\u001b[0m\n \u001b[1m\u001b[94m|\u001b[0m\n \u001b[1m\u001b[94m= \u001b[0m\u001b[1mnote\u001b[0m: `#[warn(unused_variables)]` (part of `#[warn(unused)]`) on by default\n\n\u001b[1m\u001b[33mwarning\u001b[0m\u001b[1m: variable does not need to be mutable\u001b[0m\n \u001b[1m\u001b[94m--> \u001b[0msrc/connector/legacy.rs:267:13\n \u001b[1m\u001b[94m|\u001b[0m\n\u001b[1m\u001b[94m267\u001b[0m \u001b[1m\u001b[94m|\u001b[0m let mut buffer = unsafe { &mut *c_buffer };\n \u001b[1m\u001b[94m|\u001b[0m \u001b[1m\u001b[94m----\u001b[0m\u001b[1m\u001b[33m^^^^^^\u001b[0m\n \u001b[1m\u001b[94m|\u001b[0m \u001b[1m\u001b[94m|\u001b[0m\n \u001b[1m\u001b[94m|\u001b[0m \u001b[1m\u001b[94mhelp: remove this `mut`\u001b[0m\n\n\u001b[1m\u001b[33mwarning\u001b[0m\u001b[1m: unused variable: `value`\u001b[0m\n \u001b[1m\u001b[94m--> \u001b[0msrc/legacy/deprecations.rs:508:15\n \u001b[1m\u001b[94m|\u001b[0m\n\u001b[1m\u001b[94m508\u001b[0m \u001b[1m\u001b[94m|\u001b[0m for (key, value) in configs {\n \u001b[1m\u001b[94m|\u001b[0m \u001b[1m\u001b[33m^^^^^\u001b[0m \u001b[1m\u001b[33mhelp: if this is intentional, prefix it with an underscore: `_value`\u001b[0m\n\n\u001b[1m\u001b[33mwarning\u001b[0m\u001b[1m: unused variable: `obj`\u001b[0m\n \u001b[1m\u001b[94m--> \u001b[0msrc/protocol/validate.rs:282:25\n \u001b[1m\u001b[94m|\u001b[0m\n\u001b[1m\u001b[94m282\u001b[0m \u001b[1m\u001b[94m|\u001b[0m if let Some(obj) = value.as_object() {\n \u001b[1m\u001b[94m|\u001b[0m \u001b[1m\u001b[33m^^^\u001b[0m \u001b[1m\u001b[33mhelp: if this is intentional, prefix it with an underscore: `_obj`\u001b[0m\n\n\u001b[1m\u001b[33mwarning\u001b[0m\u001b[1m: type `BridgeStats` is more private than the item `ConnectorBridge::stats`\u001b[0m\n \u001b[1m\u001b[94m--> \u001b[0msrc/connector/bridge.rs:415:5\n \u001b[1m\u001b[94m|\u001b[0m\n\u001b[1m\u001b[94m415\u001b[0m \u001b[1m\u001b[94m|\u001b[0m pub fn stats(&self) -> BridgeStats {\n \u001b[1m\u001b[94m|\u001b[0m \u001b[1m\u001b[33m^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\u001b[0m \u001b[1m\u001b[33mmethod `ConnectorBridge::stats` is reachable at visibility `pub`\u001b[0m\n \u001b[1m\u001b[94m|\u001b[0m\n\u001b[1m\u001b[92mnote\u001b[0m: but type `BridgeStats` is only usable at visibility `pub(self)`\n \u001b[1m\u001b[94m--> \u001b[0msrc/connector/bridge.rs:225:1\n \u001b[1m\u001b[94m|\u001b[0m\n\u001b[1m\u001b[94m225\u001b[0m \u001b[1m\u001b[94m|\u001b[0m struct BridgeStats {\n \u001b[1m\u001b[94m|\u001b[0m \u001b[1m\u001b[92m^^^^^^^^^^^^^^^^^^\u001b[0m\n \u001b[1m\u001b[94m= \u001b[0m\u001b[1mnote\u001b[0m: `#[warn(private_interfaces)]` on by default\n\n\u001b[1m\u001b[33mwarning\u001b[0m\u001b[1m: type `CircuitState` is more private than the item `ConnectorBridge::circuit_state`\u001b[0m\n \u001b[1m\u001b[94m--> \u001b[0msrc/connector/bridge.rs:423:5\n \u001b[1m\u001b[94m|\u001b[0m\n\u001b[1m\u001b[94m423\u001b[0m \u001b[1m\u001b[94m|\u001b[0m pub fn circuit_state(&self) -> CircuitState {\n \u001b[1m\u001b[94m|\u001b[0m \u001b[1m\u001b[33m^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\u001b[0m \u001b[1m\u001b[33mmethod `ConnectorBridge::circuit_state` is reachable at visibility `pub`\u001b[0m\n \u001b[1m\u001b[94m|\u001b[0m\n\u001b[1m\u001b[92mnote\u001b[0m: but type `CircuitState` is only usable at visibility `pub(self)`\n \u001b[1m\u001b[94m--> \u001b[0msrc/connector/bridge.rs:79:1\n \u001b[1m\u001b[94m|\u001b[0m\n\u001b[1m\u001b[94m 79\u001b[0m \u001b[1m\u001b[94m|\u001b[0m enum CircuitState {\n \u001b[1m\u001b[94m|\u001b[0m \u001b[1m\u001b[92m^^^^^^^^^^^^^^^^^\u001b[0m\n\n\u001b[1m\u001b[33mwarning\u001b[0m\u001b[1m: constant `NCP_TEMPERATURE` is never used\u001b[0m\n \u001b[1m\u001b[94m--> \u001b[0msrc/ai/mod.rs:53:7\n \u001b[1m\u001b[94m|\u001b[0m\n\u001b[1m\u001b[94m53\u001b[0m \u001b[1m\u001b[94m|\u001b[0m const NCP_TEMPERATURE: f64 = 0.42;\n \u001b[1m\u001b[94m|\u001b[0m \u001b[1m\u001b[33m^^^^^^^^^^^^^^^\u001b[0m\n \u001b[1m\u001b[94m|\u001b[0m\n \u001b[1m\u001b[94m= \u001b[0m\u001b[1mnote\u001b[0m: `#[warn(dead_code)]` (part of `#[warn(unused)]`) on by default\n\n\u001b[1m\u001b[33mwarning\u001b[0m\u001b[1m: constant `MIN_CONFIDENCE_THRESHOLD` is never used\u001b[0m\n \u001b[1m\u001b[94m--> \u001b[0msrc/ai/mod.rs:61:7\n \u001b[1m\u001b[94m|\u001b[0m\n\u001b[1m\u001b[94m61\u001b[0m \u001b[1m\u001b[94m|\u001b[0m const MIN_CONFIDENCE_THRESHOLD: f64 = 0.65;\n \u001b[1m\u001b[94m|\u001b[0m \u001b[1m\u001b[33m^^^^^^^^^^^^^^^^^^^^^^^^\u001b[0m\n\n\u001b[1m\u001b[33mwarning\u001b[0m\u001b[1m: constant `MAX_INFERENCE_RETRIES` is never used\u001b[0m\n \u001b[1m\u001b[94m--> \u001b[0msrc/ai/mod.rs:65:7\n \u001b[1m\u001b[94m|\u001b[0m\n\u001b[1m\u001b[94m65\u001b[0m \u001b[1m\u001b[94m|\u001b[0m const MAX_INFERENCE_RETRIES: u32 = 5;\n \u001b[1m\u001b[94m|\u001b[0m \u001b[1m\u001b[33m^^^^^^^^^^^^^^^^^^^^^\u001b[0m\n\n\u001b[1m\u001b[33mwarning\u001b[0m\u001b[1m: fields `discovery`, `broker`, and `registry` are never read\u001b[0m\n \u001b[1m\u001b[94m--> \u001b[0msrc/ai/mod.rs:173:5\n \u001b[1m\u001b[94m|\u001b[0m\n\u001b[1m\u001b[94m171\u001b[0m \u001b[1m\u001b[94m|\u001b[0m pub struct AiOrchestrator {\n \u001b[1m\u001b[94m|\u001b[0m \u001b[1m\u001b[94m--------------\u001b[0m \u001b[1m\u001b[94mfields in this struct\u001b[0m\n\u001b[1m\u001b[94m172\u001b[0m \u001b[1m\u001b[94m|\u001b[0m /// Reference to the service discovery subsystem\n\u001b[1m\u001b[94m173\u001b[0m \u001b[1m\u001b[94m|\u001b[0m discovery: Arc>,\n \u001b[1m\u001b[94m|\u001b[0m \u001b[1m\u001b[33m^^^^^^^^^\u001b[0m\n\u001b[1m\u001b[94m174\u001b[0m \u001b[1m\u001b[94m|\u001b[0m /// Reference to the message broker subsystem\n\u001b[1m\u001b[94m175\u001b[0m \u001b[1m\u001b[94m|\u001b[0m broker: Arc>,\n \u001b[1m\u001b[94m|\u001b[0m \u001b[1m\u001b[33m^^^^^^\u001b[0m\n\u001b[1m\u001b[94m176\u001b[0m \u001b[1m\u001b[94m|\u001b[0m /// Reference to the service registry subsystem\n\u001b[1m\u001b[94m177\u001b[0m \u001b[1m\u001b[94m|\u001b[0m registry: Arc>,\n \u001b[1m\u001b[94m|\u001b[0m \u001b[1m\u001b[33m^^^^^^^^\u001b[0m\n\n\u001b[1m\u001b[33mwarning\u001b[0m\u001b[1m: field `window_start` is never read\u001b[0m\n \u001b[1m\u001b[94m--> \u001b[0msrc/ai/embeddings.rs:661:5\n \u001b[1m\u001b[94m|\u001b[0m\n\u001b[1m\u001b[94m658\u001b[0m \u001b[1m\u001b[94m|\u001b[0m pub struct ContextWindowManager {\n \u001b[1m\u001b[94m|\u001b[0m \u001b[1m\u001b[94m--------------------\u001b[0m \u001b[1m\u001b[94mfield in this struct\u001b[0m\n\u001b[1m\u001b[94m...\u001b[0m\n\u001b[1m\u001b[94m661\u001b[0m \u001b[1m\u001b[94m|\u001b[0m window_start: Instant,\n \u001b[1m\u001b[94m|\u001b[0m \u001b[1m\u001b[33m^^^^^^^^^^^^\u001b[0m\n\n\u001b[1m\u001b[33mwarning\u001b[0m\u001b[1m: constant `MAX_RETRIES` is never used\u001b[0m\n \u001b[1m\u001b[94m--> \u001b[0msrc/ai/inference.rs:41:7\n \u001b[1m\u001b[94m|\u001b[0m\n\u001b[1m\u001b[94m41\u001b[0m \u001b[1m\u001b[94m|\u001b[0m const MAX_RETRIES: u32 = 3;\n \u001b[1m\u001b[94m|\u001b[0m \u001b[1m\u001b[33m^^^^^^^^^^^\u001b[0m\n\n\u001b[1m\u001b[33mwarning\u001b[0m\u001b[1m: constant `RETRY_BASE_DELAY_MS` is never used\u001b[0m\n \u001b[1m\u001b[94m--> \u001b[0msrc/ai/inference.rs:44:7\n \u001b[1m\u001b[94m|\u001b[0m\n\u001b[1m\u001b[94m44\u001b[0m \u001b[1m\u001b[94m|\u001b[0m const RETRY_BASE_DELAY_MS: u64 = 1000;\n \u001b[1m\u001b[94m|\u001b[0m \u001b[1m\u001b[33m^^^^^^^^^^^^^^^^^^^\u001b[0m\n\n\u001b[1m\u001b[33mwarning\u001b[0m\u001b[1m: fields `api_key`, `base_url`, and `client` are never read\u001b[0m\n \u001b[1m\u001b[94m--> \u001b[0msrc/ai/inference.rs:453:5\n \u001b[1m\u001b[94m|\u001b[0m\n\u001b[1m\u001b[94m452\u001b[0m \u001b[1m\u001b[94m|\u001b[0m pub struct AnthropicClient {\n \u001b[1m\u001b[94m|\u001b[0m \u001b[1m\u001b[94m---------------\u001b[0m \u001b[1m\u001b[94mfields in this struct\u001b[0m\n\u001b[1m\u001b[94m453\u001b[0m \u001b[1m\u001b[94m|\u001b[0m api_key: String,\n \u001b[1m\u001b[94m|\u001b[0m \u001b[1m\u001b[33m^^^^^^^\u001b[0m\n\u001b[1m\u001b[94m454\u001b[0m \u001b[1m\u001b[94m|\u001b[0m base_url: String,\n \u001b[1m\u001b[94m|\u001b[0m \u001b[1m\u001b[33m^^^^^^^^\u001b[0m\n\u001b[1m\u001b[94m455\u001b[0m \u001b[1m\u001b[94m|\u001b[0m models: Vec,\n\u001b[1m\u001b[94m456\u001b[0m \u001b[1m\u001b[94m|\u001b[0m client: reqwest::Client,\n \u001b[1m\u001b[94m|\u001b[0m \u001b[1m\u001b[33m^^^^^^\u001b[0m\n \u001b[1m\u001b[94m|\u001b[0m\n \u001b[1m\u001b[94m= \u001b[0m\u001b[1mnote\u001b[0m: `AnthropicClient` has a derived impl for the trait `Debug`, but this is intentionally ignored during dead code analysis\n\n\u001b[1m\u001b[33mwarning\u001b[0m\u001b[1m: field `routing_table` is never read\u001b[0m\n \u001b[1m\u001b[94m--> \u001b[0msrc/ai/inference.rs:719:5\n \u001b[1m\u001b[94m|\u001b[0m\n\u001b[1m\u001b[94m716\u001b[0m \u001b[1m\u001b[94m|\u001b[0m pub struct ModelRouter {\n \u001b[1m\u001b[94m|\u001b[0m \u001b[1m\u001b[94m-----------\u001b[0m \u001b[1m\u001b[94mfield in this struct\u001b[0m\n\u001b[1m\u001b[94m...\u001b[0m\n\u001b[1m\u001b[94m719\u001b[0m \u001b[1m\u001b[94m|\u001b[0m routing_table: RwLock>,\n \u001b[1m\u001b[94m|\u001b[0m \u001b[1m\u001b[33m^^^^^^^^^^^^^\u001b[0m\n \u001b[1m\u001b[94m|\u001b[0m\n \u001b[1m\u001b[94m= \u001b[0m\u001b[1mnote\u001b[0m: `ModelRouter` has a derived impl for the trait `Debug`, but this is intentionally ignored during dead code analysis\n\n\u001b[1m\u001b[33mwarning\u001b[0m\u001b[1m: field `cost_history` is never read\u001b[0m\n \u001b[1m\u001b[94m--> \u001b[0msrc/ai/inference.rs:951:5\n \u001b[1m\u001b[94m|\u001b[0m\n\u001b[1m\u001b[94m947\u001b[0m \u001b[1m\u001b[94m|\u001b[0m pub struct TokenCounter {\n \u001b[1m\u001b[94m|\u001b[0m \u001b[1m\u001b[94m------------\u001b[0m \u001b[1m\u001b[94mfield in this struct\u001b[0m\n\u001b[1m\u001b[94m...\u001b[0m\n\u001b[1m\u001b[94m951\u001b[0m \u001b[1m\u001b[94m|\u001b[0m cost_history: RwLock>,\n \u001b[1m\u001b[94m|\u001b[0m \u001b[1m\u001b[33m^^^^^^^^^^^^\u001b[0m\n\n\u001b[1m\u001b[33mwarning\u001b[0m\u001b[1m: constant `HEALTH_CHECK_TIMEOUT_MS` is never used\u001b[0m\n \u001b[1m\u001b[94m--> \u001b[0msrc/connector/bridge.rs:64:7\n \u001b[1m\u001b[94m|\u001b[0m\n\u001b[1m\u001b[94m64\u001b[0m \u001b[1m\u001b[94m|\u001b[0m const HEALTH_CHECK_TIMEOUT_MS: u64 = 1000;\n \u001b[1m\u001b[94m|\u001b[0m \u001b[1m\u001b[33m^^^^^^^^^^^^^^^^^^^^^^^\u001b[0m\n\n\u001b[1m\u001b[33mwarning\u001b[0m\u001b[1m: field `id` is never read\u001b[0m\n \u001b[1m\u001b[94m--> \u001b[0msrc/connector/bridge.rs:152:5\n \u001b[1m\u001b[94m|\u001b[0m\n\u001b[1m\u001b[94m151\u001b[0m \u001b[1m\u001b[94m|\u001b[0m struct PoolEntry {\n \u001b[1m\u001b[94m|\u001b[0m \u001b[1m\u001b[94m---------\u001b[0m \u001b[1m\u001b[94mfield in this struct\u001b[0m\n\u001b[1m\u001b[94m152\u001b[0m \u001b[1m\u001b[94m|\u001b[0m id: usize,\n \u001b[1m\u001b[94m|\u001b[0m \u001b[1m\u001b[33m^^\u001b[0m\n\n\u001b[1m\u001b[33mwarning\u001b[0m\u001b[1m: method `stats` is never used\u001b[0m\n \u001b[1m\u001b[94m--> \u001b[0msrc/connector/bridge.rs:195:8\n \u001b[1m\u001b[94m|\u001b[0m\n\u001b[1m\u001b[94m163\u001b[0m \u001b[1m\u001b[94m|\u001b[0m impl ConnectionPool {\n \u001b[1m\u001b[94m|\u001b[0m \u001b[1m\u001b[94m-------------------\u001b[0m \u001b[1m\u001b[94mmethod in this implementation\u001b[0m\n\u001b[1m\u001b[94m...\u001b[0m\n\u001b[1m\u001b[94m195\u001b[0m \u001b[1m\u001b[94m|\u001b[0m fn stats(&self) -> PoolStats {\n \u001b[1m\u001b[94m|\u001b[0m \u001b[1m\u001b[33m^^^^^\u001b[0m\n\n\u001b[1m\u001b[33mwarning\u001b[0m\u001b[1m: struct `PoolStats` is never constructed\u001b[0m\n \u001b[1m\u001b[94m--> \u001b[0msrc/connector/bridge.rs:203:8\n \u001b[1m\u001b[94m|\u001b[0m\n\u001b[1m\u001b[94m203\u001b[0m \u001b[1m\u001b[94m|\u001b[0m struct PoolStats {\n \u001b[1m\u001b[94m|\u001b[0m \u001b[1m\u001b[33m^^^^^^^^^\u001b[0m\n\n\u001b[1m\u001b[33mwarning\u001b[0m\u001b[1m: fields `circuit_breaker_trips` and `health_check_failures` are never read\u001b[0m\n \u001b[1m\u001b[94m--> \u001b[0msrc/connector/bridge.rs:229:5\n \u001b[1m\u001b[94m|\u001b[0m\n\u001b[1m\u001b[94m225\u001b[0m \u001b[1m\u001b[94m|\u001b[0m struct BridgeStats {\n \u001b[1m\u001b[94m|\u001b[0m \u001b[1m\u001b[94m-----------\u001b[0m \u001b[1m\u001b[94mfields in this struct\u001b[0m\n\u001b[1m\u001b[94m...\u001b[0m\n\u001b[1m\u001b[94m229\u001b[0m \u001b[1m\u001b[94m|\u001b[0m circuit_breaker_trips: u64,\n \u001b[1m\u001b[94m|\u001b[0m \u001b[1m\u001b[33m^^^^^^^^^^^^^^^^^^^^^\u001b[0m\n\u001b[1m\u001b[94m230\u001b[0m \u001b[1m\u001b[94m|\u001b[0m health_check_failures: u64,\n \u001b[1m\u001b[94m|\u001b[0m \u001b[1m\u001b[33m^^^^^^^^^^^^^^^^^^^^^\u001b[0m\n \u001b[1m\u001b[94m|\u001b[0m\n \u001b[1m\u001b[94m= \u001b[0m\u001b[1mnote\u001b[0m: `BridgeStats` has derived impls for the traits `Clone` and `Debug`, but these are intentionally ignored during dead code analysis\n\n\u001b[1m\u001b[33mwarning\u001b[0m\u001b[1m: field `consumers` is never read\u001b[0m\n \u001b[1m\u001b[94m--> \u001b[0msrc/messaging/mod.rs:38:5\n \u001b[1m\u001b[94m|\u001b[0m\n\u001b[1m\u001b[94m35\u001b[0m \u001b[1m\u001b[94m|\u001b[0m pub struct MessageBroker {\n \u001b[1m\u001b[94m|\u001b[0m \u001b[1m\u001b[94m-------------\u001b[0m \u001b[1m\u001b[94mfield in this struct\u001b[0m\n\u001b[1m\u001b[94m...\u001b[0m\n\u001b[1m\u001b[94m38\u001b[0m \u001b[1m\u001b[94m|\u001b[0m consumers: DashMap>>,\n \u001b[1m\u001b[94m|\u001b[0m \u001b[1m\u001b[33m^^^^^^^^^\u001b[0m\n\n\u001b[1m\u001b[33mwarning\u001b[0m\u001b[1m: field `version` is never read\u001b[0m\n \u001b[1m\u001b[94m--> \u001b[0msrc/protocol/messages.rs:293:5\n \u001b[1m\u001b[94m|\u001b[0m\n\u001b[1m\u001b[94m291\u001b[0m \u001b[1m\u001b[94m|\u001b[0m pub struct MessageRegistry {\n \u001b[1m\u001b[94m|\u001b[0m \u001b[1m\u001b[94m---------------\u001b[0m \u001b[1m\u001b[94mfield in this struct\u001b[0m\n\u001b[1m\u001b[94m292\u001b[0m \u001b[1m\u001b[94m|\u001b[0m handlers: HashMap,\n\u001b[1m\u001b[94m293\u001b[0m \u001b[1m\u001b[94m|\u001b[0m version: u32,\n \u001b[1m\u001b[94m|\u001b[0m \u001b[1m\u001b[33m^^^^^^^\u001b[0m\n\n\u001b[1m\u001b[33mwarning\u001b[0m\u001b[1m: field `version` is never read\u001b[0m\n \u001b[1m\u001b[94m--> \u001b[0msrc/protocol/serialize.rs:258:5\n \u001b[1m\u001b[94m|\u001b[0m\n\u001b[1m\u001b[94m255\u001b[0m \u001b[1m\u001b[94m|\u001b[0m struct Schema {\n \u001b[1m\u001b[94m|\u001b[0m \u001b[1m\u001b[94m------\u001b[0m \u001b[1m\u001b[94mfield in this struct\u001b[0m\n\u001b[1m\u001b[94m...\u001b[0m\n\u001b[1m\u001b[94m258\u001b[0m \u001b[1m\u001b[94m|\u001b[0m version: u32,\n \u001b[1m\u001b[94m|\u001b[0m \u001b[1m\u001b[33m^^^^^^^\u001b[0m\n\n\u001b[1m\u001b[33mwarning\u001b[0m\u001b[1m: fields `required` and `default_value` are never read\u001b[0m\n \u001b[1m\u001b[94m--> \u001b[0msrc/protocol/serialize.rs:264:5\n \u001b[1m\u001b[94m|\u001b[0m\n\u001b[1m\u001b[94m261\u001b[0m \u001b[1m\u001b[94m|\u001b[0m struct SchemaField {\n \u001b[1m\u001b[94m|\u001b[0m \u001b[1m\u001b[94m-----------\u001b[0m \u001b[1m\u001b[94mfields in this struct\u001b[0m\n\u001b[1m\u001b[94m...\u001b[0m\n\u001b[1m\u001b[94m264\u001b[0m \u001b[1m\u001b[94m|\u001b[0m required: bool,\n \u001b[1m\u001b[94m|\u001b[0m \u001b[1m\u001b[33m^^^^^^^^\u001b[0m\n\u001b[1m\u001b[94m265\u001b[0m \u001b[1m\u001b[94m|\u001b[0m default_value: Option,\n \u001b[1m\u001b[94m|\u001b[0m \u001b[1m\u001b[33m^^^^^^^^^^^^^\u001b[0m\n\n\u001b[1m\u001b[33mwarning\u001b[0m\u001b[1m: variant `Custom` is never constructed\u001b[0m\n \u001b[1m\u001b[94m--> \u001b[0msrc/protocol/serialize.rs:276:5\n \u001b[1m\u001b[94m|\u001b[0m\n\u001b[1m\u001b[94m269\u001b[0m \u001b[1m\u001b[94m|\u001b[0m enum FieldValidation {\n \u001b[1m\u001b[94m|\u001b[0m \u001b[1m\u001b[94m---------------\u001b[0m \u001b[1m\u001b[94mvariant in this enum\u001b[0m\n\u001b[1m\u001b[94m...\u001b[0m\n\u001b[1m\u001b[94m276\u001b[0m \u001b[1m\u001b[94m|\u001b[0m Custom(String),\n \u001b[1m\u001b[94m|\u001b[0m \u001b[1m\u001b[33m^^^^^^\u001b[0m\n\n\u001b[1m\u001b[33mwarning\u001b[0m\u001b[1m: fields `next_request_id`, `pending_requests`, `serializer`, and `timeout_ms` are never read\u001b[0m\n \u001b[1m\u001b[94m--> \u001b[0msrc/protocol/rpc.rs:218:5\n \u001b[1m\u001b[94m|\u001b[0m\n\u001b[1m\u001b[94m217\u001b[0m \u001b[1m\u001b[94m|\u001b[0m pub struct RpcClient {\n \u001b[1m\u001b[94m|\u001b[0m \u001b[1m\u001b[94m---------\u001b[0m \u001b[1m\u001b[94mfields in this struct\u001b[0m\n\u001b[1m\u001b[94m218\u001b[0m \u001b[1m\u001b[94m|\u001b[0m next_request_id: AtomicU64,\n \u001b[1m\u001b[94m|\u001b[0m \u001b[1m\u001b[33m^^^^^^^^^^^^^^^\u001b[0m\n\u001b[1m\u001b[94m219\u001b[0m \u001b[1m\u001b[94m|\u001b[0m pending_requests: Arc, RpcError>>>>>,\n \u001b[1m\u001b[94m|\u001b[0m \u001b[1m\u001b[33m^^^^^^^^^^^^^^^^\u001b[0m\n\u001b[1m\u001b[94m220\u001b[0m \u001b[1m\u001b[94m|\u001b[0m serializer: Serializer,\n \u001b[1m\u001b[94m|\u001b[0m \u001b[1m\u001b[33m^^^^^^^^^^\u001b[0m\n\u001b[1m\u001b[94m221\u001b[0m \u001b[1m\u001b[94m|\u001b[0m timeout_ms: u64,\n \u001b[1m\u001b[94m|\u001b[0m \u001b[1m\u001b[33m^^^^^^^^^^\u001b[0m\n\n\u001b[1m\u001b[33mwarning\u001b[0m\u001b[1m: field `serializer` is never read\u001b[0m\n \u001b[1m\u001b[94m--> \u001b[0msrc/protocol/rpc.rs:271:5\n \u001b[1m\u001b[94m|\u001b[0m\n\u001b[1m\u001b[94m269\u001b[0m \u001b[1m\u001b[94m|\u001b[0m pub struct RpcServer {\n \u001b[1m\u001b[94m|\u001b[0m \u001b[1m\u001b[94m---------\u001b[0m \u001b[1m\u001b[94mfield in this struct\u001b[0m\n\u001b[1m\u001b[94m270\u001b[0m \u001b[1m\u001b[94m|\u001b[0m handlers: HashMap,\n\u001b[1m\u001b[94m271\u001b[0m \u001b[1m\u001b[94m|\u001b[0m serializer: Serializer,\n \u001b[1m\u001b[94m|\u001b[0m \u001b[1m\u001b[33m^^^^^^^^^^\u001b[0m\n\n\u001b[1m\u001b[33mwarning\u001b[0m\u001b[1m: field `events` is never read\u001b[0m\n \u001b[1m\u001b[94m--> \u001b[0msrc/registry/mod.rs:31:5\n \u001b[1m\u001b[94m|\u001b[0m\n\u001b[1m\u001b[94m28\u001b[0m \u001b[1m\u001b[94m|\u001b[0m pub struct ServiceRegistry {\n \u001b[1m\u001b[94m|\u001b[0m \u001b[1m\u001b[94m---------------\u001b[0m \u001b[1m\u001b[94mfield in this struct\u001b[0m\n\u001b[1m\u001b[94m...\u001b[0m\n\u001b[1m\u001b[94m31\u001b[0m \u001b[1m\u001b[94m|\u001b[0m events: Arc>>,\n \u001b[1m\u001b[94m|\u001b[0m \u001b[1m\u001b[33m^^^^^^\u001b[0m\n\n\u001b[1m\u001b[33mwarning\u001b[0m: `tent-backend` (lib) generated 52 warnings (run `cargo fix --lib -p tent-backend` to apply 23 suggestions)\n\u001b[1m\u001b[92m Finished\u001b[0m `dev` profile [unoptimized + debuginfo] target(s) in 36.92s" + }, + { + "name": "frontend", + "status": "PASS", + "elapsed_seconds": 11.704, + "artifact": "/Volumes/Soengkit\u786c\u76d8/Codex\u65b0\u9879\u76ee\u4e0e\u8f93\u51fa/2026-06-19_Agent Bounty Workbench/02_candidate_bounties/peter7896-zeroeye-ws-delta-4/frontend/dist", + "output": "> tent-frontend@0.0.0 build\n> tsc -b && vite build\n\nvite v6.4.3 building for production...\ntransforming...\n\u2713 100 modules transformed.\nrendering chunks...\ncomputing gzip size...\ndist/index.html 0.62 kB \u2502 gzip: 0.34 kB\ndist/assets/state-BkjSKDbY.js 8.91 kB \u2502 gzip: 3.54 kB \u2502 map: 57.15 kB\ndist/assets/vendor-CREcWLHI.js 48.93 kB \u2502 gzip: 17.25 kB \u2502 map: 481.27 kB\ndist/assets/index-CyxcoTyU.js 231.32 kB \u2502 gzip: 72.16 kB \u2502 map: 1,044.42 kB\n\u2713 built in 620ms" + }, + { + "name": "market", + "status": "PASS", + "elapsed_seconds": 7.431, + "artifact": "/Volumes/Soengkit\u786c\u76d8/Codex\u65b0\u9879\u76ee\u4e0e\u8f93\u51fa/2026-06-19_Agent Bounty Workbench/02_candidate_bounties/peter7896-zeroeye-ws-delta-4/market/market", + "output": "" + }, + { + "name": "frailbox", + "status": "FAIL", + "elapsed_seconds": 0.078, + "artifact": null, + "output": "gcc -Wall -Wextra -Wpedantic -std=c2x -O2 -g -D_FORTIFY_SOURCE=3 -fstack-protector-strong -fPIE -Iinclude -MMD -MP -c src/arena.c -o build/src/arena.o\nsrc/arena.c:17:23: error: use of undeclared identifier 'MAP_HUGETLB'\n 17 | mmap_flags |= MAP_HUGETLB;\n | ^~~~~~~~~~~\nsrc/arena.c:179:17: warning: comparison of distinct pointer types ('const void *' and 'char *') [-Wcompare-distinct-pointer-types]\n 179 | ptr < (char *)region->start + region->size) {\n | ~~~ ^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n1 warning and 1 error generated.\nmake: *** [build/src/arena.o] Error 1" + }, + { + "name": "engine", + "status": "PASS", + "elapsed_seconds": 5.971, + "artifact": "/Volumes/Soengkit\u786c\u76d8/Codex\u65b0\u9879\u76ee\u4e0e\u8f93\u51fa/2026-06-19_Agent Bounty Workbench/02_candidate_bounties/peter7896-zeroeye-ws-delta-4/frailbox/engine/build/trial-engine", + "output": "[ 11%] Building CXX object CMakeFiles/trial-engine.dir/main.cpp.o\n[ 22%] Building CXX object CMakeFiles/trial-engine.dir/core/math.cpp.o\n[ 33%] Building CXX object CMakeFiles/trial-engine.dir/core/ecs.cpp.o\n[ 44%] Building CXX object CMakeFiles/trial-engine.dir/dynamics/rigidbody.cpp.o\n[ 55%] Building CXX object CMakeFiles/trial-engine.dir/dynamics/constraint.cpp.o\n[ 66%] Building CXX object CMakeFiles/trial-engine.dir/collision/collision.cpp.o\n[ 77%] Building CXX object CMakeFiles/trial-engine.dir/Volumes/Soengkit\u786c\u76d8/Codex\u65b0\u9879\u76ee\u4e0e\u8f93\u51fa/2026-06-19_Agent_Bounty_Workbench/02_candidate_bounties/peter7896-zeroeye-ws-delta-4/frailbox/wat.cpp.o\n[ 88%] Building CXX object CMakeFiles/trial-engine.dir/Volumes/Soengkit\u786c\u76d8/Codex\u65b0\u9879\u76ee\u4e0e\u8f93\u51fa/2026-06-19_Agent_Bounty_Workbench/02_candidate_bounties/peter7896-zeroeye-ws-delta-4/frailbox/engine.cpp.o\n[100%] Linking CXX executable trial-engine\n[100%] Built target trial-engine" + }, + { + "name": "compliance", + "status": "PASS", + "elapsed_seconds": 1.539, + "artifact": "/Volumes/Soengkit\u786c\u76d8/Codex\u65b0\u9879\u76ee\u4e0e\u8f93\u51fa/2026-06-19_Agent Bounty Workbench/02_candidate_bounties/peter7896-zeroeye-ws-delta-4/compliance/build", + "output": "\u6ce8: ComplianceAuditor.java\u4f7f\u7528\u6216\u8986\u76d6\u4e86\u5df2\u8fc7\u65f6\u7684 API\u3002\n\u6ce8: \u6709\u5173\u8be6\u7ec6\u4fe1\u606f, \u8bf7\u4f7f\u7528 -Xlint:deprecation \u91cd\u65b0\u7f16\u8bd1\u3002" + }, + { + "name": "v2-market-stream", + "status": "PASS", + "elapsed_seconds": 0.064, + "artifact": null, + "output": "Syntax OK" + }, + { + "name": "nfc-scanner", + "status": "PASS", + "elapsed_seconds": 0.033, + "artifact": null, + "output": "" + }, + { + "name": "openapi-haskell", + "status": "FAIL", + "elapsed_seconds": 3.007, + "artifact": null, + "output": "[1 of 8] Compiling Network.HTTP.Types ( Network/HTTP/Types.hs, nothing )\n[2 of 8] Compiling Network.Wai ( Network/Wai.hs, nothing )\n[3 of 8] Compiling Network.Wai.Handler.Warp ( Network/Wai/Handler/Warp.hs, nothing )\n[4 of 8] Compiling Network.Wai.Logger ( Network/Wai/Logger.hs, nothing )\n[5 of 8] Compiling Tent.OpenAPI.Types ( Types.hs, /var/folders/3k/nc3_8hy10_x8dxn_6c6nsh5m0000gn/T/ghc85448_0/ghc_4.o, /var/folders/3k/nc3_8hy10_x8dxn_6c6nsh5m0000gn/T/ghc85448_0/ghc_4.dyn_o )\nTypes.hs:50:1: error: [GHC-61948]\n Could not find module \u2018Data.Aeson\u2019.\n Perhaps you meant Data.Version (from base-4.20.2.0)\n Use -v to see a list of the files searched for.\n |\n50 | import Data.Aeson (FromJSON(parseJSON), ToJSON(toJSON), Value(Object), (.!=), (.:?), (.=))\n | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nTypes.hs:51:1: error: [GHC-87110]\n Could not find module \u2018Data.Aeson.Types\u2019.\n Use -v to see a list of the files searched for.\n |\n51 | import Data.Aeson.Types (Parser, parseMaybe)\n | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nTypes.hs:71:1: error: [GHC-61948]\n Could not find module \u2018Data.Aeson\u2019.\n Perhaps you meant Data.Version (from base-4.20.2.0)\n Use -v to see a list of the files searched for.\n |\n71 | import qualified Data.Aeson as A\n | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nTypes.hs:72:1: error: [GHC-87110]\n Could not find module \u2018Data.Aeson.Key\u2019.\n Use -v to see a list of the files searched for.\n |\n72 | import qualified Data.Aeson.Key as K\n | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nTypes.hs:73:1: error: [GHC-87110]\n Could not find module \u2018Data.Aeson.KeyMap\u2019.\n Use -v to see a list of the files searched for.\n |\n73 | import qualified Data.Aeson.KeyMap as KM\n | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nTypes.hs:74:1: error: [GHC-61948]\n Could not find module \u2018Data.HashMap.Strict\u2019.\n Perhaps you meant\n Data.Map.Strict (from containers-0.7)\n Data.IntMap.Strict (from containers-0.7)\n Use -v to see a list of the files searched for.\n |\n74 | import qualified Data.HashMap.Strict as HM\n | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nTypes.hs:77:1: error: [GHC-87110]\n Could not find module \u2018Data.Yaml\u2019.\n Use -v to see a list of the files searched for.\n |\n77 | import qualified Data.Yaml as Y\n | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^" + }, + { + "name": "openapi-tools", + "status": "PASS", + "elapsed_seconds": 0.023, + "artifact": null, + "output": "" + } + ], + "pr_note": "Include the encrypted diagnostic logd artifact(s): diagnostic/build-182f71ab.logd. The encrypted .logd is the required diagnostic content for PR review; this JSON file is metadata. Maintainers may ask you to remove these diagnostic artifacts before merging." +} diff --git a/diagnostic/build-182f71ab.logd b/diagnostic/build-182f71ab.logd new file mode 100644 index 00000000..d6850d39 Binary files /dev/null and b/diagnostic/build-182f71ab.logd differ diff --git a/market/orderbook/delta.go b/market/orderbook/delta.go new file mode 100644 index 00000000..5a189566 --- /dev/null +++ b/market/orderbook/delta.go @@ -0,0 +1,208 @@ +package orderbook + +import ( + "crypto/sha256" + "encoding/hex" + "fmt" + "sort" + "strings" + "time" + + "github.com/shopspring/decimal" + "github.com/tent-of-trials/market/types" +) + +type DepthDeltaLevel struct { + Side string `json:"side"` + Price decimal.Decimal `json:"price"` + Quantity decimal.Decimal `json:"quantity"` + Count int64 `json:"order_count,omitempty"` +} + +type DepthDelta struct { + Symbol types.Symbol `json:"symbol"` + PreviousSequence uint64 `json:"previous_sequence"` + Sequence uint64 `json:"sequence"` + Updates []DepthDeltaLevel `json:"updates"` + Checksum string `json:"checksum,omitempty"` +} + +var ( + ErrInvalidDeltaSymbol = &BookError{"invalid delta symbol"} + ErrInvalidDeltaSequence = &BookError{"invalid delta sequence"} + ErrInvalidDeltaSide = &BookError{"invalid delta side"} + ErrInvalidDeltaPrice = &BookError{"invalid delta price"} + ErrInvalidDeltaQuantity = &BookError{"invalid delta quantity"} + ErrDeltaChecksumMismatch = &BookError{"delta checksum mismatch"} +) + +func (ob *OrderBook) ApplySnapshot(snapshot types.DepthUpdate, sequence uint64) error { + ob.mu.Lock() + defer ob.mu.Unlock() + + if ob.closed { + return ErrBookClosed + } + if snapshot.Symbol != ob.symbol { + return fmt.Errorf("%w: got %q want %q", ErrInvalidDeltaSymbol, snapshot.Symbol, ob.symbol) + } + + bids, err := normalizeLevels(snapshot.Bids, true, ob.config.MaxDepth) + if err != nil { + return err + } + asks, err := normalizeLevels(snapshot.Asks, false, ob.config.MaxDepth) + if err != nil { + return err + } + + ob.bids = levelsToPointers(bids) + ob.asks = levelsToPointers(asks) + ob.sequence = sequence + ob.updatedAt = time.Now() + return nil +} + +func (ob *OrderBook) ApplyDelta(delta DepthDelta) error { + ob.mu.Lock() + defer ob.mu.Unlock() + + if ob.closed { + return ErrBookClosed + } + if delta.Symbol != ob.symbol { + return fmt.Errorf("%w: got %q want %q", ErrInvalidDeltaSymbol, delta.Symbol, ob.symbol) + } + if delta.Sequence <= ob.sequence { + return fmt.Errorf("%w: got %d after %d", ErrInvalidDeltaSequence, delta.Sequence, ob.sequence) + } + if delta.PreviousSequence != 0 && delta.PreviousSequence != ob.sequence { + return fmt.Errorf("%w: previous %d does not match current %d", ErrInvalidDeltaSequence, delta.PreviousSequence, ob.sequence) + } + + bids := clonePointerLevels(ob.bids) + asks := clonePointerLevels(ob.asks) + for _, update := range delta.Updates { + side, err := normalizeSide(update.Side) + if err != nil { + return err + } + if update.Price.LessThanOrEqual(decimal.Zero) { + return fmt.Errorf("%w: %s price %s", ErrInvalidDeltaPrice, side, update.Price.String()) + } + if update.Quantity.IsNegative() { + return fmt.Errorf("%w: %s quantity %s", ErrInvalidDeltaQuantity, side, update.Quantity.String()) + } + count := update.Count + if count <= 0 && update.Quantity.GreaterThan(decimal.Zero) { + count = 1 + } + level := types.Level{ + Price: update.Price, + Quantity: update.Quantity, + Count: count, + } + + if side == "bid" { + bids = upsertDeltaLevel(bids, level, true, ob.config.MaxDepth) + } else { + asks = upsertDeltaLevel(asks, level, false, ob.config.MaxDepth) + } + } + + if delta.Checksum != "" { + actual := DepthChecksum(bids, asks) + if !strings.EqualFold(delta.Checksum, actual) { + return fmt.Errorf("%w: got %s want %s", ErrDeltaChecksumMismatch, actual, delta.Checksum) + } + } + + ob.bids = levelsToPointers(bids) + ob.asks = levelsToPointers(asks) + ob.sequence = delta.Sequence + ob.updatedAt = time.Now() + return nil +} + +func DepthChecksum(bids, asks []types.Level) string { + h := sha256.New() + writeLevels := func(side string, levels []types.Level) { + for _, level := range levels { + fmt.Fprintf(h, "%s:%s:%s:%d;", side, level.Price.String(), level.Quantity.String(), level.Count) + } + } + writeLevels("bid", bids) + writeLevels("ask", asks) + return hex.EncodeToString(h.Sum(nil)) +} + +func normalizeSide(side string) (string, error) { + switch strings.ToLower(strings.TrimSpace(side)) { + case "bid", "bids", "buy": + return "bid", nil + case "ask", "asks", "sell": + return "ask", nil + default: + return "", fmt.Errorf("%w: %q", ErrInvalidDeltaSide, side) + } +} + +func normalizeLevels(levels []types.Level, bids bool, maxDepth int) ([]types.Level, error) { + result := make([]types.Level, 0, len(levels)) + for _, level := range levels { + if level.Price.LessThanOrEqual(decimal.Zero) { + return nil, fmt.Errorf("%w: snapshot price %s", ErrInvalidDeltaPrice, level.Price.String()) + } + if level.Quantity.IsNegative() { + return nil, fmt.Errorf("%w: snapshot quantity %s", ErrInvalidDeltaQuantity, level.Quantity.String()) + } + if level.Count <= 0 && level.Quantity.GreaterThan(decimal.Zero) { + level.Count = 1 + } + result = upsertDeltaLevel(result, level, bids, maxDepth) + } + return result, nil +} + +func upsertDeltaLevel(levels []types.Level, level types.Level, bids bool, maxDepth int) []types.Level { + result := make([]types.Level, 0, len(levels)+1) + for _, existing := range levels { + if existing.Price.Equal(level.Price) { + continue + } + result = append(result, existing) + } + if level.Quantity.GreaterThan(decimal.Zero) { + result = append(result, level) + } + + sort.Slice(result, func(i, j int) bool { + if bids { + return result[i].Price.GreaterThan(result[j].Price) + } + return result[i].Price.LessThan(result[j].Price) + }) + if maxDepth > 0 && len(result) > maxDepth { + return result[:maxDepth] + } + return result +} + +func clonePointerLevels(levels []*types.Level) []types.Level { + result := make([]types.Level, 0, len(levels)) + for _, level := range levels { + if level != nil { + result = append(result, *level) + } + } + return result +} + +func levelsToPointers(levels []types.Level) []*types.Level { + result := make([]*types.Level, 0, len(levels)) + for i := range levels { + level := levels[i] + result = append(result, &level) + } + return result +} diff --git a/market/orderbook/delta_test.go b/market/orderbook/delta_test.go new file mode 100644 index 00000000..2b07ab6c --- /dev/null +++ b/market/orderbook/delta_test.go @@ -0,0 +1,227 @@ +package orderbook + +import ( + "errors" + "fmt" + "testing" + + "github.com/shopspring/decimal" + "github.com/tent-of-trials/market/types" +) + +func TestApplySnapshotThenValidDelta(t *testing.T) { + book := newTestBook(t) + + initial := types.DepthUpdate{ + Symbol: types.Symbol("BTC-USD"), + Bids: []types.Level{ + level("100.00", "1.00", 1), + }, + Asks: []types.Level{ + level("101.00", "1.00", 1), + }, + } + if err := book.ApplySnapshot(initial, 10); err != nil { + t.Fatalf("ApplySnapshot() error = %v", err) + } + + wantBids := []types.Level{ + level("100.00", "2.25", 3), + level("99.50", "1.40", 1), + } + wantAsks := []types.Level{ + level("102.00", "0.75", 1), + } + + delta := DepthDelta{ + Symbol: types.Symbol("BTC-USD"), + PreviousSequence: 10, + Sequence: 11, + Updates: []DepthDeltaLevel{ + {Side: "bid", Price: dec("100.00"), Quantity: dec("2.25"), Count: 3}, + {Side: "bid", Price: dec("99.50"), Quantity: dec("1.40")}, + {Side: "ask", Price: dec("101.00"), Quantity: decimal.Zero}, + {Side: "ask", Price: dec("102.00"), Quantity: dec("0.75")}, + }, + Checksum: DepthChecksum(wantBids, wantAsks), + } + + if err := book.ApplyDelta(delta); err != nil { + t.Fatalf("ApplyDelta() error = %v", err) + } + + got := book.GetSnapshot() + assertLevels(t, got.Bids, wantBids) + assertLevels(t, got.Asks, wantAsks) + if book.sequence != 11 { + t.Fatalf("sequence = %d, want 11", book.sequence) + } +} + +func TestApplyDeltaRejectsMalformedPayloadsWithoutMutation(t *testing.T) { + tests := []struct { + name string + delta DepthDelta + want error + }{ + { + name: "wrong symbol", + delta: validDelta(11, 10, DepthDeltaLevel{Side: "bid", Price: dec("100.00"), Quantity: dec("2.00")}), + want: ErrInvalidDeltaSymbol, + }, + { + name: "stale sequence", + delta: DepthDelta{ + Symbol: types.Symbol("BTC-USD"), + PreviousSequence: 9, + Sequence: 10, + Updates: []DepthDeltaLevel{{Side: "bid", Price: dec("100.00"), Quantity: dec("2.00")}}, + }, + want: ErrInvalidDeltaSequence, + }, + { + name: "out of order previous sequence", + delta: DepthDelta{ + Symbol: types.Symbol("BTC-USD"), + PreviousSequence: 8, + Sequence: 11, + Updates: []DepthDeltaLevel{{Side: "bid", Price: dec("100.00"), Quantity: dec("2.00")}}, + }, + want: ErrInvalidDeltaSequence, + }, + { + name: "invalid side", + delta: validDelta(11, 10, DepthDeltaLevel{Side: "middle", Price: dec("100.00"), Quantity: dec("2.00")}), + want: ErrInvalidDeltaSide, + }, + { + name: "invalid bid price", + delta: validDelta(11, 10, DepthDeltaLevel{Side: "bid", Price: decimal.Zero, Quantity: dec("2.00")}), + want: ErrInvalidDeltaPrice, + }, + { + name: "invalid ask quantity", + delta: validDelta(11, 10, DepthDeltaLevel{Side: "ask", Price: dec("101.00"), Quantity: dec("-1.00")}), + want: ErrInvalidDeltaQuantity, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + book := seededBook(t) + before := snapshotKey(book.GetSnapshot()) + + if tt.name == "wrong symbol" { + tt.delta.Symbol = types.Symbol("ETH-USD") + } + + err := book.ApplyDelta(tt.delta) + if !errors.Is(err, tt.want) { + t.Fatalf("ApplyDelta() error = %v, want %v", err, tt.want) + } + if got := snapshotKey(book.GetSnapshot()); got != before { + t.Fatalf("book mutated after invalid delta:\nbefore %s\nafter %s", before, got) + } + if book.sequence != 10 { + t.Fatalf("sequence mutated to %d, want 10", book.sequence) + } + }) + } +} + +func TestApplyDeltaRejectsChecksumMismatchWithoutMutation(t *testing.T) { + book := seededBook(t) + before := snapshotKey(book.GetSnapshot()) + + delta := validDelta(11, 10, DepthDeltaLevel{ + Side: "ask", + Price: dec("102.00"), + Quantity: dec("0.75"), + }) + delta.Checksum = "not-the-calculated-book-checksum" + + err := book.ApplyDelta(delta) + if !errors.Is(err, ErrDeltaChecksumMismatch) { + t.Fatalf("ApplyDelta() error = %v, want %v", err, ErrDeltaChecksumMismatch) + } + if got := snapshotKey(book.GetSnapshot()); got != before { + t.Fatalf("book mutated after checksum mismatch:\nbefore %s\nafter %s", before, got) + } + if book.sequence != 10 { + t.Fatalf("sequence mutated to %d, want 10", book.sequence) + } +} + +func newTestBook(t *testing.T) *OrderBook { + t.Helper() + return NewOrderBook(types.Symbol("BTC-USD"), Config{ + MaxDepth: 5, + PriceDecimals: 2, + VolumeDecimals: 8, + }) +} + +func seededBook(t *testing.T) *OrderBook { + t.Helper() + book := newTestBook(t) + err := book.ApplySnapshot(types.DepthUpdate{ + Symbol: types.Symbol("BTC-USD"), + Bids: []types.Level{level("100.00", "1.00", 1)}, + Asks: []types.Level{level("101.00", "1.00", 1)}, + }, 10) + if err != nil { + t.Fatalf("ApplySnapshot() error = %v", err) + } + return book +} + +func validDelta(sequence, previous uint64, updates ...DepthDeltaLevel) DepthDelta { + return DepthDelta{ + Symbol: types.Symbol("BTC-USD"), + PreviousSequence: previous, + Sequence: sequence, + Updates: updates, + } +} + +func level(price, quantity string, count int64) types.Level { + return types.Level{ + Price: dec(price), + Quantity: dec(quantity), + Count: count, + } +} + +func dec(value string) decimal.Decimal { + parsed, err := decimal.NewFromString(value) + if err != nil { + panic(err) + } + return parsed +} + +func assertLevels(t *testing.T, got, want []types.Level) { + t.Helper() + if len(got) != len(want) { + t.Fatalf("level count = %d, want %d; got %#v", len(got), len(want), got) + } + for i := range want { + if !got[i].Price.Equal(want[i].Price) || + !got[i].Quantity.Equal(want[i].Quantity) || + got[i].Count != want[i].Count { + t.Fatalf("level[%d] = %#v, want %#v", i, got[i], want[i]) + } + } +} + +func snapshotKey(snapshot *types.DepthUpdate) string { + return fmt.Sprintf("bids=%s asks=%s", levelsKey(snapshot.Bids), levelsKey(snapshot.Asks)) +} + +func levelsKey(levels []types.Level) string { + key := "" + for _, level := range levels { + key += fmt.Sprintf("%s/%s/%d;", level.Price.String(), level.Quantity.String(), level.Count) + } + return key +}