{"record":{"id":"9d4d08d52851f017","repo":"Hmbown/CodeWhale","slug":"burn-rate-must-be-at-least-0-01-hr","errorCode":null,"errorMessage":"Burn rate must be at least $0.01/hr.","messagePattern":"Burn rate must be at least \\$0\\.01/hr\\.","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/tui/src/operate.rs","lineNumber":282,"sourceCode":"        .or_else(|| value.as_str().and_then(|s| s.parse().ok()))\n}\n\nfn parse_burn_amount(amount: Option<f64>) -> Result<Option<OperateBurnRate>> {\n    let Some(amount) = amount else {\n        return Ok(None);\n    };\n    if !amount.is_finite() || amount <= 0.0 {\n        anyhow::bail!(\"Burn rate is optional. When set, it must be a positive $/hr.\");\n    }\n    if amount > 10_000.0 {\n        anyhow::bail!(\"Burn rate must be 10000 $/hr or less.\");\n    }\n    let rounded = (amount * 100.0).round() / 100.0;\n    if rounded <= 0.0 {\n        // A sub-cent rate rounds to a $0/hr target, which the pace governor\n        // would treat as unbounded — reject it instead of silently dropping\n        // the requested cap.\n        anyhow::bail!(\"Burn rate must be at least $0.01/hr.\");\n    }\n    Ok(Some(OperateBurnRate {\n        kind: \"usd_per_hour\".to_string(),\n        amount_usd_per_hour: rounded,\n    }))\n}\n\nfn derive_status(op: &Operation) -> (OperateStatus, Option<OperateIdleReason>) {\n    if op.status == OperateStatus::Cancelled {\n        return (OperateStatus::Cancelled, None);\n    }\n    if !op.credentials_present {\n        return (\n            OperateStatus::IdleBlocked,\n            Some(OperateIdleReason::MissingCredentials),\n        );\n    }\n    if op.direction.is_empty() {","sourceCodeStart":264,"sourceCodeEnd":300,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/src/operate.rs#L264-L300","documentation":"After rounding to cents, a burn rate of 0 or less is rejected. A sub-cent rate (e.g. 0.005) rounds to $0/hr, which the pace governor would treat as an unbounded rate, silently dropping the requested cap — so the library refuses it instead.","triggerScenarios":"Calling parse_burn_amount (via parse_burn_rate or normalize_burn_rate) with a positive but sub-$0.01 amount such as 0.001, or a value that rounds down to 0.","commonSituations":"Entering an intentionally tiny burn rate in config; unit confusion (entering fractions of a dollar); floating-point values below the cent granularity.","solutions":["Use a burn rate of at least 0.01 $/hr","Round or clamp the value up to $0.01/hr before parsing","If a near-zero cap is intended, disable the burn rate instead of passing a sub-cent value"],"exampleFix":"// before\nburn_rate: 0.004\n// after\nburn_rate: 0.01","handlingStrategy":"validation","validationCode":"fn burn_rate_ok(v: f64) -> bool { v.is_finite() && ((v * 100.0).round() / 100.0) >= 0.01 }","typeGuard":null,"tryCatchPattern":"match parse_burn_rate(input) { Err(e) if e.to_string().contains(\"$0.01\") => eprintln!(\"rate too small: {}\", e), Ok(v) => apply(v), Err(e) => return Err(e), }","preventionTips":["Round sub-cent rates up to 0.01 instead of down","Treat burn rate granularity as cents everywhere","Disable the cap explicitly rather than passing ~0"],"tags":["validation","rounding","config"],"backgroundTag":"value-out-of-range","analyzedSha":"73e0f67d83c59909b571efdfc88c4bc28c309cb1","analyzedAt":"2026-09-22T01:30:00.501Z","contentChangedAt":"2026-09-22T01:30:00.501Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}