mirror of
https://github.com/0xrsydn/idx-cli.git
synced 2026-11-07 09:45:36 +00:00
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:
parent
60634d3aef
commit
1e551385d0
10 changed files with 117 additions and 34 deletions
|
|
@ -233,7 +233,8 @@ impl Cache {
|
|||
runtime::warn(format!(
|
||||
"corrupted cache entry for {data_type}/{symbol}, treating as miss: {e}"
|
||||
));
|
||||
let _ = fs::remove_file(&path);
|
||||
// Not deleted: a concurrent writer may have just replaced it
|
||||
// with a valid entry. The next put overwrites it atomically.
|
||||
return Ok(None);
|
||||
}
|
||||
};
|
||||
|
|
@ -244,7 +245,6 @@ impl Cache {
|
|||
entry.schema_version,
|
||||
CURRENT_SCHEMA_VERSION
|
||||
));
|
||||
let _ = fs::remove_file(&path);
|
||||
return Ok(None);
|
||||
}
|
||||
Ok(Some(entry))
|
||||
|
|
@ -352,7 +352,7 @@ mod tests {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn corrupted_cache_entry_returns_none_and_deletes_file() {
|
||||
fn corrupted_cache_entry_is_a_miss_until_replaced() {
|
||||
let root = tmp();
|
||||
let cache = Cache::at(root.clone());
|
||||
|
||||
|
|
@ -371,12 +371,16 @@ mod tests {
|
|||
.expect("get should not error");
|
||||
assert_eq!(result, None, "corrupted entry should be treated as miss");
|
||||
|
||||
// The corrupted file should be deleted
|
||||
assert!(!path.exists(), "corrupted file should be deleted");
|
||||
// Left for the next put to replace (deleting could race a writer).
|
||||
cache
|
||||
.put("quote", "CORRUPT.JK", &T { v: 7 }, 3600)
|
||||
.expect("put");
|
||||
let result: Option<T> = cache.get("quote", "CORRUPT.JK").expect("get");
|
||||
assert_eq!(result, Some(T { v: 7 }));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn corrupted_cache_entry_get_stale_returns_none_and_deletes_file() {
|
||||
fn corrupted_cache_entry_get_stale_is_a_miss() {
|
||||
let root = tmp();
|
||||
let cache = Cache::at(root.clone());
|
||||
|
||||
|
|
@ -401,8 +405,7 @@ mod tests {
|
|||
"corrupted entry should be treated as miss in get_stale"
|
||||
);
|
||||
|
||||
// The corrupted file should be deleted
|
||||
assert!(!path.exists(), "corrupted file should be deleted");
|
||||
assert!(path.exists(), "left for the next put to replace");
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue