Skip to content

Commit dfe4e23

Browse files
hyperpolymathclaude
andcommitted
fix: validate boundary constraints and wire orphaned orch_status command
validate_inference now rejects malformed boundary constraints (invalid identifiers, non-numeric RHS) instead of silently accepting them. Wire verisimdb_orch_status into the frontend message/model/update loop so it is no longer dead code. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 9ebc86e commit dfe4e23

5 files changed

Lines changed: 105 additions & 3 deletions

File tree

src-tauri/src/main.rs

Lines changed: 75 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -582,11 +582,43 @@ fn validate_inference(token: &str, constraints: Vec<String>) -> Result<bool, Str
582582
}
583583

584584
// Handle boundary checks: length > 0, count < 100, etc.
585+
// Validates constraint syntax: LHS must be a valid identifier, operator
586+
// must be a comparison, RHS must be a valid number. Runtime value checking
587+
// happens in the ReScript AntiCrash module which has access to model state.
585588
if constraint.contains(" > ") || constraint.contains(" < ") ||
586589
constraint.contains(" >= ") || constraint.contains(" <= ") {
587-
// For now, we don't have the actual values to check against
588-
// This would require integrating with the model state
589-
// Accept boundary constraints as valid declarations
590+
// Split on the operator to validate both sides
591+
let operators = [" >= ", " <= ", " > ", " < "];
592+
let mut validated = false;
593+
for op in &operators {
594+
if let Some(pos) = constraint.find(op) {
595+
let lhs = constraint[..pos].trim();
596+
let rhs = constraint[pos + op.len()..].trim();
597+
598+
// LHS must be a valid identifier (alphanumeric + underscores, not starting with digit)
599+
if lhs.is_empty() || lhs.starts_with(|c: char| c.is_ascii_digit()) ||
600+
!lhs.chars().all(|c| c.is_alphanumeric() || c == '_') {
601+
return Err(format!(
602+
"Invalid boundary constraint: '{}' is not a valid identifier in '{}'",
603+
lhs, constraint
604+
));
605+
}
606+
607+
// RHS must be a valid number (integer or float, optionally negative)
608+
if rhs.parse::<f64>().is_err() {
609+
return Err(format!(
610+
"Invalid boundary constraint: '{}' is not a valid number in '{}'",
611+
rhs, constraint
612+
));
613+
}
614+
615+
validated = true;
616+
break;
617+
}
618+
}
619+
if !validated {
620+
return Err(format!("Malformed boundary constraint: {}", constraint));
621+
}
590622
continue;
591623
}
592624

@@ -928,6 +960,46 @@ Commands:
928960
assert_eq!(result.unwrap(), true);
929961
}
930962

963+
#[test]
964+
fn test_validate_inference_valid_boundary_passes() {
965+
let token = "const x = 42;";
966+
let constraints = vec![
967+
"length > 0".to_string(),
968+
"count < 100".to_string(),
969+
"score >= -1.5".to_string(),
970+
"max_depth <= 999".to_string(),
971+
];
972+
let result = validate_inference(token, constraints);
973+
assert!(result.is_ok());
974+
}
975+
976+
#[test]
977+
fn test_validate_inference_invalid_boundary_rhs_rejected() {
978+
let token = "const x = 42;";
979+
let constraints = vec!["length > \"invalid\"".to_string()];
980+
let result = validate_inference(token, constraints);
981+
assert!(result.is_err());
982+
assert!(result.unwrap_err().contains("not a valid number"));
983+
}
984+
985+
#[test]
986+
fn test_validate_inference_invalid_boundary_lhs_rejected() {
987+
let token = "const x = 42;";
988+
let constraints = vec!["123bad > 5".to_string()];
989+
let result = validate_inference(token, constraints);
990+
assert!(result.is_err());
991+
assert!(result.unwrap_err().contains("not a valid identifier"));
992+
}
993+
994+
#[test]
995+
fn test_validate_inference_boundary_special_chars_rejected() {
996+
let token = "const x = 42;";
997+
let constraints = vec!["foo-bar > 5".to_string()];
998+
let result = validate_inference(token, constraints);
999+
assert!(result.is_err());
1000+
assert!(result.unwrap_err().contains("not a valid identifier"));
1001+
}
1002+
9311003
#[test]
9321004
fn test_vexation_initial_index_is_zero() {
9331005
// Reset tracker by creating a new one (in a real app we'd need better reset mechanisms)

src/Model.res

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,7 @@ type verisimdbState = {
257257
entityDetail: option<string>,
258258
telemetry: option<telemetrySnapshot>,
259259
telemetryVisible: bool,
260+
orchStatus: option<string>,
260261
}
261262

262263
/// The complete Model
@@ -411,6 +412,7 @@ let init = (): model => {
411412
entityDetail: None,
412413
telemetry: None,
413414
telemetryVisible: false,
415+
orchStatus: None,
414416
},
415417
humidity: Medium,
416418
viewMode: DarkStart,

src/Msg.res

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,8 @@ type verisimdbMsg =
123123
| FetchTelemetry
124124
| TelemetryLoaded(result<string, string>)
125125
| ToggleTelemetryPanel
126+
| FetchOrchStatus
127+
| OrchStatusLoaded(result<string, string>)
126128

127129
/// The unified message type
128130
type msg =

src/Update.res

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -701,6 +701,15 @@ let updateVeriSimDB = (model: model, msg: verisimdbMsg): (model, Tea_Cmd.t<msg>)
701701
{...model, verisimdb: {...db, telemetryVisible: !db.telemetryVisible}},
702702
Tea_Cmd.none,
703703
)
704+
| FetchOrchStatus => (
705+
model,
706+
TauriCmd.getOrchStatus(result => VeriSimDB(OrchStatusLoaded(result))),
707+
)
708+
| OrchStatusLoaded(result) =>
709+
switch result {
710+
| Ok(json) => ({...model, verisimdb: {...db, orchStatus: Some(json), queryError: None}}, Tea_Cmd.none)
711+
| Error(err) => ({...model, verisimdb: {...db, orchStatus: None, queryError: Some(err)}}, Tea_Cmd.none)
712+
}
704713
}
705714
}
706715

src/commands/TauriCmd.res

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -460,6 +460,23 @@ let getTelemetry = (tagger: result<string, string> => 'msg): Tea_Cmd.t<'msg> =>
460460
})
461461
}
462462

463+
/// Fetch orchestration status (consensus, federation, telemetry) from VeriSimDB.
464+
/// Invokes `verisimdb_orch_status` which hits GET /status on the Elixir layer.
465+
let getOrchStatus = (tagger: result<string, string> => 'msg): Tea_Cmd.t<'msg> => {
466+
Tea_Cmd.call(callbacks => {
467+
invoke("verisimdb_orch_status", ())
468+
->Promise.then(result => {
469+
callbacks.enqueue(tagger(Ok(result)))
470+
Promise.resolve()
471+
})
472+
->Promise.catch(_err => {
473+
callbacks.enqueue(tagger(Error("Orchestration status fetch failed")))
474+
Promise.resolve()
475+
})
476+
->ignore
477+
})
478+
}
479+
463480
/// Batch multiple Tauri commands together
464481
let batch = (commands: list<Tea_Cmd.t<'msg>>): Tea_Cmd.t<'msg> => {
465482
Tea_Cmd.batch(commands)

0 commit comments

Comments
 (0)