{"record":{"id":"4b7bfe9df44262ab","repo":"Hmbown/CodeWhale","slug":"cloud-job-id-must-look-like-cloud-hex","errorCode":null,"errorMessage":"cloud job id must look like cloud_<hex>","messagePattern":"cloud job id must look like cloud_<hex>","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/tui/src/cloud_dispatch.rs","lineNumber":368,"sourceCode":"        for entry in fs::read_dir(&self.root).context(\"failed to read cloud-jobs\")? {\n            let entry = entry?;\n            let name = entry.file_name();\n            let name = name.to_string_lossy();\n            if !name.starts_with(\"cloud_\") || !name.ends_with(\".json\") {\n                continue;\n            }\n            let body = fs::read(entry.path())?;\n            if let Ok(job) = serde_json::from_slice::<CloudJob>(&body) {\n                jobs.push(job);\n            }\n        }\n        jobs.sort_by_key(|a| std::cmp::Reverse(a.created_unix));\n        Ok(jobs)\n    }\n\n    fn job_path(&self, id: &str) -> Result<PathBuf> {\n        if !valid_job_id(id) {\n            bail!(\"cloud job id must look like cloud_<hex>\");\n        }\n        Ok(self.root.join(format!(\"{id}.json\")))\n    }\n}\n\n/// Classify a git remote. Named `github` / `cnb` / `gitee` win over URL.\npub fn classify_remote(name: &str, url: &str) -> Option<Forge> {\n    match name.trim().to_ascii_lowercase().as_str() {\n        \"github\" => Some(Forge::Github),\n        \"cnb\" => Some(Forge::Cnb),\n        \"gitee\" => Some(Forge::Gitee),\n        _ => classify_url(url),\n    }\n}\n\n/// True when `branch` is a forge default that a non-force push could\n/// fast-forward past review (`main` / `master` / `HEAD`).\npub fn is_forge_default_branch(branch: &str) -> bool {","sourceCodeStart":350,"sourceCodeEnd":386,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/src/cloud_dispatch.rs#L350-L386","documentation":"The cloud job store refuses to build a filesystem path for a job id that does not match the expected shape `cloud_<hex>`. Job ids are used directly as filenames (`<id>.json`), so this check prevents path traversal and malformed ids from reaching the disk before any save or load happens.","triggerScenarios":"Calling CloudJobStore::save or CloudJobStore::load (or higher-level flows that pass through them, e.g. confirm_job) with an id string that is empty, lacks the `cloud_` prefix, or whose suffix is not hexadecimal.","commonSituations":"Passing a user-typed or clipboard-pasted job id that includes whitespace or a short label; persisting/reusing an id from a different system; parsing an id out of a log line and picking up surrounding text; older persisted records written before the `cloud_` convention was introduced.","solutions":["Print/inspect the id and ensure it matches `cloud_` followed by hexadecimal characters (e.g. `cloud_deadbeef`).","Trim whitespace and strip surrounding quotes/brackets when the id comes from user input, config, or logs.","Obtain the id from the API that created the job (e.g. a DispatchOutcome or the store's own listing) instead of hand-constructing it.","If old records use a legacy id format, migrate or re-create them; the validator is intentionally strict."],"exampleFix":"// before\nstore.load(\"abc123\")?;\n// after\nlet id = raw_id.trim();\nif valid_job_id(id) { store.load(id)?; }","handlingStrategy":"validation","validationCode":"fn is_valid_job_id(id: &str) -> bool {\n    id.starts_with(\"cloud_\") && !id.as_bytes()[6..].is_empty()\n        && id[6..].bytes().all(|b| b.is_ascii_hexdigit())\n}","typeGuard":"fn valid_job_id(id: &str) -> bool {\n    let hex = id.strip_prefix(\"cloud_\").unwrap_or(\"\");\n    !hex.is_empty() && hex.bytes().all(|b| b.is_ascii_hexdigit())\n}","tryCatchPattern":"match store.load(id) {\n    Ok(job) => job,\n    Err(e) if e.to_string().contains(\"cloud_<hex>\") => {\n        eprintln!(\"invalid job id '{id}': expected cloud_<hex>\");\n        return Err(e);\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["Always obtain ids from the store listing or dispatch result, never free text.","Trim and normalize user input before use.","Centralize the id format check in one helper (valid_job_id) and reuse it at every input boundary.","Add a form/UI-level regex check (^cloud_[0-9a-f]+$) before submission."],"tags":["validation","filesystem","identifier"],"backgroundTag":"invalid-identifier-format","analyzedSha":"73e0f67d83c59909b571efdfc88c4bc28c309cb1","analyzedAt":"2026-09-22T01:30:00.501Z","contentChangedAt":"2026-09-22T01:30:00.501Z","schemaVersion":2},"datasetVersion":"2026-09-22T16:17:23.217Z"}