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
|
|
@ -171,7 +171,8 @@ earnings, analyst sentiment, insights, and news. Sections that fail are listed u
|
|||
)]
|
||||
async fn get_company(&self, params: Parameters<CompanyParams>) -> ToolResult {
|
||||
let CompanyParams { symbol, sections } = params.0;
|
||||
let sections = sections.unwrap_or_else(|| CompanySection::ALL.to_vec());
|
||||
let mut sections = sections.unwrap_or_else(|| CompanySection::ALL.to_vec());
|
||||
dedup_in_order(&mut sections);
|
||||
if let Some(err) = empty_input("sections", §ions) {
|
||||
return err;
|
||||
}
|
||||
|
|
@ -214,7 +215,8 @@ are listed under `errors`; the call fails only if every analysis fails."
|
|||
)]
|
||||
async fn analyze_stock(&self, params: Parameters<AnalyzeParams>) -> ToolResult {
|
||||
let AnalyzeParams { symbol, kinds } = params.0;
|
||||
let kinds = kinds.unwrap_or_else(|| AnalysisKind::ALL.to_vec());
|
||||
let mut kinds = kinds.unwrap_or_else(|| AnalysisKind::ALL.to_vec());
|
||||
dedup_in_order(&mut kinds);
|
||||
if let Some(err) = empty_input("kinds", &kinds) {
|
||||
return err;
|
||||
}
|
||||
|
|
@ -306,14 +308,10 @@ release dates, optionally for one ticker."
|
|||
limit,
|
||||
} = params.0;
|
||||
self.local(move |investor| {
|
||||
let limit = bounded_limit("limit", limit, CHANGES_LIMIT, MAX_CHANGES_LIMIT)?;
|
||||
let mut changes = investor.changes(&from, &to, ticker.as_deref())?;
|
||||
let total = changes.len();
|
||||
changes.truncate(bounded_limit(
|
||||
"limit",
|
||||
limit,
|
||||
CHANGES_LIMIT,
|
||||
MAX_CHANGES_LIMIT,
|
||||
)?);
|
||||
changes.truncate(limit);
|
||||
Ok(json!({ "from": from, "to": to, "total": total, "changes": changes }))
|
||||
})
|
||||
.await
|
||||
|
|
@ -449,6 +447,18 @@ fn checked_symbols(symbols: Vec<String>) -> Result<Vec<String>, ToolResult> {
|
|||
Ok(symbols)
|
||||
}
|
||||
|
||||
/// Each section or analysis runs once, however often it is requested.
|
||||
fn dedup_in_order<T: PartialEq + Copy>(values: &mut Vec<T>) {
|
||||
let mut seen = Vec::with_capacity(values.len());
|
||||
values.retain(|value| {
|
||||
let new = !seen.contains(value);
|
||||
if new {
|
||||
seen.push(*value);
|
||||
}
|
||||
new
|
||||
});
|
||||
}
|
||||
|
||||
fn bounded_limit(
|
||||
field: &str,
|
||||
value: Option<usize>,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue