fix: close the remaining review findings (cross-process lock, partials)

Second Codex (gpt-6.1-sol, xhigh) pass over 60634d3, each item verified
before fixing:

- new: the sync recheck hashed an existing empty database before schema
  setup, so the first sync falsely reported an external change. Hash
  after setup; a published snapshot already has the schema and keeps
  its bytes.
- sync, import, and entity edits now hold a cross-process lock
  (<db>.lock, std File::lock), so a sync cannot replace the database
  under an import running in another process. Import validates and
  downloads before taking the lock, keeping its error order. MSRV
  1.85 -> 1.89 for File::lock.
- the row-count rebuild branch also never downgrades history.
- cache reads no longer delete corrupt entries (a concurrent writer may
  have just replaced them); the next put overwrites atomically.
- idx-mcp validates the changes limit before querying and runs each
  requested section or analysis once.
- XLSX parsing bounds total cell text, so one large shared string
  referenced from many cells cannot exhaust memory.
- PDF/archive percentages must be 0-100%; HHI uses saturating math.

CLI goldens unchanged. Live A/B vs 0.2.4: 16 passed, discover hits the
IDX 403 on both builds (exit 4 vs 1, intended).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
hermes 2026-10-14 22:20:57 +00:00
commit 1e551385d0
10 changed files with 117 additions and 34 deletions

View file

@ -84,6 +84,7 @@ pub fn parse_id_percentage(s: &str) -> Result<i64, IdxError> {
whole_i
.checked_mul(100)
.and_then(|bps| bps.checked_add(frac_i))
.filter(|bps| (0..=10_000).contains(bps))
.ok_or_else(|| IdxError::ParseError(format!("Indonesian percentage '{s}' is out of range")))
}
@ -460,6 +461,8 @@ mod tests {
#[test]
fn huge_percentages_are_parse_errors_not_overflow() {
assert!(super::parse_id_percentage("9223372036854775807,99").is_err());
assert!(super::parse_id_percentage("40000000,00").is_err());
assert_eq!(super::parse_id_percentage("100,00").unwrap(), 10_000);
assert_eq!(super::parse_id_percentage("54,94").unwrap(), 5494);
}
}