Uh oh!
There was an error while loading. Please reload this page.
fix(summary): align CLI success/failure with the ingestor — inserted-based rate + full has_failures - #193
Merged
Merged
Conversation
…cess + wrong staging-reclaim) Found in the 2026-07-08 prod audit. The CLI's Summary methods diverged from the ingestor's own success/failure determination, so a partial run could report success AND reclaim (delete) the staged source: - HasFailures() was `FailedRecords>0 || FileTransferFailures>0` — it IGNORED skipped rows, inserted<total, and api_sent<inserted. The ingestor's IngestionSummary.has_failures (data-ingestors ingestors/base.py) counts all five. So a run that silently SKIPPED rows or inserted fewer than total was classified "succeeded" → exit 0 AND the staging-reclaim gate deleted the user's source copy. Now mirrors the ingestor exactly. Every counter it reads is emitted unconditionally by the ingestor banner (reporting.py) and parsed, so the new inserted<total / api_sent<inserted clauses can't false-positive on a clean run (all counters equal). - SuccessRate() was ProcessedRecords/TotalRecords; the ingestor's banner uses inserted/total. processed (passed validation) is a superset of inserted (rows actually in MySQL), so the CLI OVERSTATED success (e.g. 100 processed / 70 inserted showed 100%, not 70%) in the panel header + --output-json `success_rate`. Now inserted/total, matching the banner. - RenderSummary headline: regraded to hard-failures (DB/transfer errors) → red, softer partial (skips / short insert-or-sync) → yellow, clean → green — so the broader HasFailures drives exit-code + reclaim while the headline still distinguishes a hard failure from a partial. Behavior change (intended): a run with skipped rows, inserted<total, or api_sent<inserted now exits 9 (was 0) and its staged source is KEPT (not reclaimed) — matching what the ingestor already reports in its own banner. Tests: HasFailures + SuccessRate matrices rewritten to the ingestor-aligned semantics (incl. the processed-overstates case + one case per new clause); RenderSummary headline cases + one classifyPushOutcome fixture updated to realistic full-counter summaries. Full cli suite green. Standalone CLI correctness fix (files untouched by the in-flight CLI branches). Relates to the data-ingest epic backend#1008 / test-hardening backend#1009. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 0b15162. Configure here.
Uh oh!
There was an error while loading. Please reload this page.
…skips" RenderSummary routed every non-hard-failure through the yellow "completed with skips" headline, including runs where SkippedRecords==0 and the only shortfall was inserted<total or api_sent<inserted. Exit code and staging-reclaim were correct, but operators could misread an insert/API shortfall as a validator skip. Word the yellow headline by the actual shortfall: "skips" only when rows were skipped, else "partially". Adds a RenderSummary headline case for the zero-skip partial. (Bugbot #193.) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Merging develop brought TestRunIngestionRun_Matrix (#1009), whose succeededResult() fixture set only TotalRecords+InsertedRecords. Under this PR's ingestor-aligned HasFailures(), api_sent(0) < inserted(2) classifies that as completed_with_failures → exit 9, no reclaim, breaking the "succeeded" row. Set APISentRecords so every stage counter is equal — a clean run — matching the coverage_test.go "clean" fixture. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
saadqbal
approved these changes
Jul 9, 2026
Uh oh!
There was an error while loading. Please reload this page.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for freeto join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

fix(summary): align CLI success/failure with the ingestor (silent-success + wrong staging-reclaim)
Found in the 2026-07-08 prod audit. The CLI's Summary methods diverged from the
ingestor's own success/failure determination, so a partial run could report
success AND reclaim (delete) the staged source:
HasFailures() was
FailedRecords>0 || FileTransferFailures>0— it IGNOREDskipped rows, inserted<total, and api_sent<inserted. The ingestor's
IngestionSummary.has_failures (data-ingestors ingestors/base.py) counts all
five. So a run that silently SKIPPED rows or inserted fewer than total was
classified "succeeded" → exit 0 AND the staging-reclaim gate deleted the
user's source copy. Now mirrors the ingestor exactly. Every counter it reads
is emitted unconditionally by the ingestor banner (reporting.py) and parsed,
so the new inserted<total / api_sent<inserted clauses can't false-positive on
a clean run (all counters equal).
SuccessRate() was ProcessedRecords/TotalRecords; the ingestor's banner uses
inserted/total. processed (passed validation) is a superset of inserted (rows
actually in MySQL), so the CLI OVERSTATED success (e.g. 100 processed / 70
inserted showed 100%, not 70%) in the panel header + --output-json
success_rate. Now inserted/total, matching the banner.RenderSummary headline: regraded to hard-failures (DB/transfer errors) → red,
softer partial (skips / short insert-or-sync) → yellow, clean → green — so
the broader HasFailures drives exit-code + reclaim while the headline still
distinguishes a hard failure from a partial.
Behavior change (intended): a run with skipped rows, inserted<total, or
api_sent<inserted now exits 9 (was 0) and its staged source is KEPT (not
reclaimed) — matching what the ingestor already reports in its own banner.
Tests: HasFailures + SuccessRate matrices rewritten to the ingestor-aligned
semantics (incl. the processed-overstates case + one case per new clause);
RenderSummary headline cases + one classifyPushOutcome fixture updated to
realistic full-counter summaries. Full cli suite green.
Standalone CLI correctness fix (files untouched by the in-flight CLI branches).
Relates to the data-ingest epic backend#1008 / test-hardening backend#1009.
Co-Authored-By: Claude Opus 4.8 (1M context) noreply@anthropic.com
Note
High Risk
Changes when ingestion is considered successful and when staged source is reclaimed—incorrect classification could still cause data loss or leave staging when users expect cleanup; behavior intentionally shifts exit 0 → 9 for several real-world partial outcomes.
Overview
Aligns ingestion exit codes, staging reclaim, and reported success with the ingestor’s own failure rules so partial runs no longer look successful and delete the user’s staged source.
HasFailures()now matches ingestorhas_failures: treats skipped rows,inserted < total, andapi_sent < insertedas failures—not only DB/file-transfer errors. Those cases exit 9 and keep staged data instead of reclaiming on exit 0.SuccessRate()uses inserted / total (not processed / total), so the panel and--output-jsonsuccess_rateno longer overstate success when rows validated but did not land in MySQL.RenderSummaryseparates hard failures (DB/transfer) from softer partials: skips vs “partially” when shortfall isn’t from skips, while still driving non-clean outcomes through the broaderHasFailures().Tests update fixtures to full counter sets (including
APISentRecordson “clean” rows) and expand matrices for the new semantics.Reviewed by Cursor Bugbot for commit afdbef4. Bugbot is set up for automated code reviews on this repo. Configure here.