From f4b71016128a09923107b94d7fefebc9401c814c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 11 Apr 2026 05:45:58 +0000 Subject: [PATCH 01/15] Initial plan From 28aac35080cc1ff5a86fe2f8b1ab993996de26f7 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 11 Apr 2026 05:48:40 +0000 Subject: [PATCH 02/15] Fix inspect suggestion for negative debit CSVs Agent-Logs-Url: https://github.com/davidfowl/tally/sessions/255e41f5-a5dd-4f02-b05a-3bcec9963882 Co-authored-by: davidfowl <95136+davidfowl@users.noreply.github.com> --- src/tally/commands/inspect.py | 35 ++++++++++++++++++++++++++-- tests/test_inspect.py | 43 +++++++++++++++++++++++++++++++++++ 2 files changed, 76 insertions(+), 2 deletions(-) diff --git a/src/tally/commands/inspect.py b/src/tally/commands/inspect.py index 9e98932..775bc90 100644 --- a/src/tally/commands/inspect.py +++ b/src/tally/commands/inspect.py @@ -154,6 +154,11 @@ def cmd_inspect(args): if spec.delimiter: print(f" - Delimiter: {repr(spec.delimiter)}") + # Analyze amount patterns first so the suggested format string can use + # {-amount} for exports where debits are stored as negative values. + analysis = _analyze_amount_column_detailed(filepath, spec.amount_column, has_header=True, dialect=dialect) + amount_token = _suggest_amount_token(analysis) + # Build suggested format string max_col = max(spec.date_column, spec.description_column, spec.amount_column) @@ -164,7 +169,7 @@ def cmd_inspect(args): elif i == spec.description_column: cols.append('{description}') elif i == spec.amount_column: - cols.append('{amount}') + cols.append(amount_token) else: cols.append('{_}') @@ -175,7 +180,6 @@ def cmd_inspect(args): print(f' delimiter: "{spec.delimiter}"') # Analyze amount patterns - detailed analysis with both signs - analysis = _analyze_amount_column_detailed(filepath, spec.amount_column, has_header=True, dialect=dialect) if analysis: print("\n" + "=" * 70) print("Amount Distribution:") @@ -454,6 +458,33 @@ def parse_amount(val): } +def _suggest_amount_token(analysis): + """Choose the amount token that best matches the detected sign convention.""" + if not analysis: + return '{amount}' + + positive_count = analysis['positive_count'] + negative_count = analysis['negative_count'] + total_count = positive_count + negative_count + if total_count == 0: + return '{amount}' + + positive_pct = positive_count / total_count * 100 + if positive_pct < 30: + return '{-amount}' + if positive_pct > 70: + return '{amount}' + + if negative_count > positive_count: + return '{-amount}' + if positive_count > negative_count: + return '{amount}' + + if analysis['negative_total'] > analysis['positive_total']: + return '{-amount}' + return '{amount}' + + def _analyze_columns(filepath, has_header=True, max_rows=100, dialect=None): """ Analyze all columns in a CSV to detect their types and patterns. diff --git a/tests/test_inspect.py b/tests/test_inspect.py index bd3ce85..cb886f5 100644 --- a/tests/test_inspect.py +++ b/tests/test_inspect.py @@ -1,5 +1,6 @@ """Tests for inspect command - CSV sniffing and column analysis.""" +from argparse import Namespace import csv import pytest import tempfile @@ -10,6 +11,7 @@ _analyze_columns, _analyze_amount_column_detailed, _detect_currency_symbol, + cmd_inspect, ) @@ -948,3 +950,44 @@ def test_tab_delimiter(self): assert spec.delimiter == '\t' finally: os.unlink(tmpfile) + + +class TestCmdInspect: + """Regression tests for inspect command suggestions.""" + + def test_suggests_negated_amount_for_negative_debits(self, capsys): + """Bank-style exports with negative debits should suggest {-amount}.""" + csv_content = """Date,Description,Amount +01/15/2025,GROCERY STORE,-123.45 +01/16/2025,PAYROLL,2500.00 +01/17/2025,COFFEE SHOP,-5.99 +01/18/2025,RENT,-1200.00 +""" + with tempfile.NamedTemporaryFile(mode='w', suffix='.csv', delete=False) as f: + f.write(csv_content) + tmpfile = f.name + + try: + cmd_inspect(Namespace(file=tmpfile, rows=3)) + captured = capsys.readouterr() + assert 'format: "{date:%m/%d/%Y}, {description}, {-amount}"' in captured.out + finally: + os.unlink(tmpfile) + + def test_keeps_amount_for_positive_debits(self, capsys): + """Credit-card-style exports with positive debits should keep {amount}.""" + csv_content = """Date,Description,Amount +01/15/2025,GROCERY STORE,123.45 +01/16/2025,COFFEE SHOP,5.99 +01/17/2025,PAYMENT,-500.00 +""" + with tempfile.NamedTemporaryFile(mode='w', suffix='.csv', delete=False) as f: + f.write(csv_content) + tmpfile = f.name + + try: + cmd_inspect(Namespace(file=tmpfile, rows=3)) + captured = capsys.readouterr() + assert 'format: "{date:%m/%d/%Y}, {description}, {amount}"' in captured.out + finally: + os.unlink(tmpfile) From f31f2fcd4c9a7a1afb492a9e3f445e5a5d558882 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 11 Apr 2026 05:49:39 +0000 Subject: [PATCH 03/15] Document inspect amount sign heuristic Agent-Logs-Url: https://github.com/davidfowl/tally/sessions/255e41f5-a5dd-4f02-b05a-3bcec9963882 Co-authored-by: davidfowl <95136+davidfowl@users.noreply.github.com> --- src/tally/commands/inspect.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/tally/commands/inspect.py b/src/tally/commands/inspect.py index 775bc90..bb50e5e 100644 --- a/src/tally/commands/inspect.py +++ b/src/tally/commands/inspect.py @@ -459,7 +459,14 @@ def parse_amount(val): def _suggest_amount_token(analysis): - """Choose the amount token that best matches the detected sign convention.""" + """ + Choose the amount token that best matches the detected sign convention. + + Prefer {-amount} when negative values clearly dominate (<30% positive), + keep {amount} when positive values clearly dominate (>70% positive), + and for mixed exports use the majority sign by count before falling back + to total magnitude as a final tie-breaker. + """ if not analysis: return '{amount}' From 555bddc3fa99a4749de18783ff6a1c5fc6041a77 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 11 Apr 2026 05:50:31 +0000 Subject: [PATCH 04/15] Clarify inspect sign detection thresholds Agent-Logs-Url: https://github.com/davidfowl/tally/sessions/255e41f5-a5dd-4f02-b05a-3bcec9963882 Co-authored-by: davidfowl <95136+davidfowl@users.noreply.github.com> --- src/tally/commands/inspect.py | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/src/tally/commands/inspect.py b/src/tally/commands/inspect.py index bb50e5e..8c40f9e 100644 --- a/src/tally/commands/inspect.py +++ b/src/tally/commands/inspect.py @@ -9,6 +9,9 @@ from ..colors import C from ..analyzer import auto_detect_csv_format +CLEARLY_NEGATIVE_DEBITS_THRESHOLD = 30 +CLEARLY_POSITIVE_DEBITS_THRESHOLD = 70 + def cmd_inspect(args): """Handle the 'inspect' subcommand - show CSV structure and sample rows.""" @@ -462,10 +465,13 @@ def _suggest_amount_token(analysis): """ Choose the amount token that best matches the detected sign convention. - Prefer {-amount} when negative values clearly dominate (<30% positive), - keep {amount} when positive values clearly dominate (>70% positive), - and for mixed exports use the majority sign by count before falling back - to total magnitude as a final tie-breaker. + Prefer {-amount} when negative values clearly dominate (fewer than + 30% positive rows), keep {amount} when positive values clearly dominate + (more than 70% positive rows), and for mixed exports use the majority + sign by count before falling back to total magnitude as a final + tie-breaker. Count is prioritized over magnitude because statement + exports commonly contain a small number of large payments or transfers + that would otherwise outweigh the day-to-day spending pattern. """ if not analysis: return '{amount}' @@ -477,9 +483,9 @@ def _suggest_amount_token(analysis): return '{amount}' positive_pct = positive_count / total_count * 100 - if positive_pct < 30: + if positive_pct < CLEARLY_NEGATIVE_DEBITS_THRESHOLD: return '{-amount}' - if positive_pct > 70: + if positive_pct > CLEARLY_POSITIVE_DEBITS_THRESHOLD: return '{amount}' if negative_count > positive_count: From e53abfec9d173d251fcadb868c02cf1e063f89ec Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 11 Apr 2026 05:57:51 +0000 Subject: [PATCH 05/15] Update inspect amount guidance Agent-Logs-Url: https://github.com/davidfowl/tally/sessions/8ba8ec62-9f28-4226-b7cc-88ecdb4a3555 Co-authored-by: davidfowl <95136+davidfowl@users.noreply.github.com> --- src/tally/commands/inspect.py | 104 ++++++++++++++++++++++------------ tests/test_inspect.py | 21 ++++--- 2 files changed, 82 insertions(+), 43 deletions(-) diff --git a/src/tally/commands/inspect.py b/src/tally/commands/inspect.py index 8c40f9e..b377885 100644 --- a/src/tally/commands/inspect.py +++ b/src/tally/commands/inspect.py @@ -157,12 +157,9 @@ def cmd_inspect(args): if spec.delimiter: print(f" - Delimiter: {repr(spec.delimiter)}") - # Analyze amount patterns first so the suggested format string can use - # {-amount} for exports where debits are stored as negative values. analysis = _analyze_amount_column_detailed(filepath, spec.amount_column, has_header=True, dialect=dialect) - amount_token = _suggest_amount_token(analysis) - # Build suggested format string + # Build suggested format string template max_col = max(spec.date_column, spec.description_column, spec.amount_column) cols = [] @@ -172,12 +169,12 @@ def cmd_inspect(args): elif i == spec.description_column: cols.append('{description}') elif i == spec.amount_column: - cols.append(amount_token) + cols.append('') else: cols.append('{_}') format_str = ', '.join(cols) - print(f"\n Suggested format string:") + print(f"\n Suggested format string template:") print(f' format: "{format_str}"') if spec.delimiter: print(f' delimiter: "{spec.delimiter}"') @@ -210,11 +207,18 @@ def cmd_inspect(args): truncated = desc[:45] + '...' if len(desc) > 45 else desc print(f" -${abs(amt):,.2f} {truncated}") - # Show amount modifier options (not recommendations) - print("\n Amount modifiers available:") - print(f" {{amount}} - use values as-is") - print(f" {{-amount}} - negate (flip sign)") - print(f" {{+amount}} - absolute value") + print("\n Sign observations:") + for line in _describe_amount_sign_observations(analysis): + print(f" - {line}") + + # Show amount modifier options with context + print("\n How to use amount tokens:") + print(f" {{amount}} - preserve the sign from the CSV") + print(f" {{-amount}} - flip the sign from the CSV") + print(f" {{+amount}} - take the absolute value") + + for line in _describe_amount_token_usage(analysis): + print(f" - {line}") # Detect currency symbol from amount column currency_symbol = _detect_currency_symbol(filepath, spec.amount_column, has_header=True, dialect=dialect) @@ -461,41 +465,69 @@ def parse_amount(val): } -def _suggest_amount_token(analysis): - """ - Choose the amount token that best matches the detected sign convention. - - Prefer {-amount} when negative values clearly dominate (fewer than - 30% positive rows), keep {amount} when positive values clearly dominate - (more than 70% positive rows), and for mixed exports use the majority - sign by count before falling back to total magnitude as a final - tie-breaker. Count is prioritized over magnitude because statement - exports commonly contain a small number of large payments or transfers - that would otherwise outweigh the day-to-day spending pattern. - """ +def _describe_amount_sign_observations(analysis): + """Describe what the sampled amount signs look like.""" if not analysis: - return '{amount}' + return ["No non-zero amounts were found in the sampled rows."] positive_count = analysis['positive_count'] negative_count = analysis['negative_count'] total_count = positive_count + negative_count - if total_count == 0: - return '{amount}' + positive_pct = positive_count / total_count * 100 if total_count else 0 + + if positive_count == 0: + return ["Observed only negative non-zero amounts in the sampled rows."] + + if negative_count == 0: + return ["Observed only positive non-zero amounts in the sampled rows."] - positive_pct = positive_count / total_count * 100 if positive_pct < CLEARLY_NEGATIVE_DEBITS_THRESHOLD: - return '{-amount}' + return [ + f"Observed mostly negative amounts ({negative_count} negative, {positive_count} positive).", + "Debits or spending may already be negative in this export.", + ] + if positive_pct > CLEARLY_POSITIVE_DEBITS_THRESHOLD: - return '{amount}' + return [ + f"Observed mostly positive amounts ({positive_count} positive, {negative_count} negative).", + "Debits or spending may already be positive in this export.", + ] + + return [ + f"Observed a mixed sign pattern ({positive_count} positive, {negative_count} negative).", + "Large payments, refunds, or transfers may be mixed with day-to-day spending.", + ] - if negative_count > positive_count: - return '{-amount}' - if positive_count > negative_count: - return '{amount}' - if analysis['negative_total'] > analysis['positive_total']: - return '{-amount}' - return '{amount}' +def _describe_amount_token_usage(analysis): + """Explain how {amount} and {-amount} relate to the observed signs.""" + if not analysis: + return ["Replace with the token that matches your CSV's sign convention."] + + positive_count = analysis['positive_count'] + negative_count = analysis['negative_count'] + total_count = positive_count + negative_count + positive_pct = positive_count / total_count * 100 if total_count else 0 + + if positive_count == 0 or positive_pct < CLEARLY_NEGATIVE_DEBITS_THRESHOLD: + return [ + "Use {amount} if you want to keep negative debits/spending negative in Tally.", + "Use {-amount} if you want to flip those debits/spending amounts to positive values in Tally.", + 'Replace in the template with the option you choose.', + ] + + if negative_count == 0 or positive_pct > CLEARLY_POSITIVE_DEBITS_THRESHOLD: + return [ + "Use {amount} if you want to keep positive debits/spending positive in Tally.", + "Use {-amount} if you want to flip that convention so those rows become negative in Tally.", + 'Replace in the template with the option you choose.', + ] + + return [ + "Use {amount} to preserve the CSV's original signs.", + "Use {-amount} to invert the CSV's original signs.", + 'Replace in the template after reviewing the sample rows above.', + ] def _analyze_columns(filepath, has_header=True, max_rows=100, dialect=None): diff --git a/tests/test_inspect.py b/tests/test_inspect.py index cb886f5..410c1ac 100644 --- a/tests/test_inspect.py +++ b/tests/test_inspect.py @@ -953,10 +953,10 @@ def test_tab_delimiter(self): class TestCmdInspect: - """Regression tests for inspect command suggestions.""" + """Regression tests for inspect command amount guidance.""" - def test_suggests_negated_amount_for_negative_debits(self, capsys): - """Bank-style exports with negative debits should suggest {-amount}.""" + def test_reports_negative_debit_observations(self, capsys): + """Bank-style exports should explain the observed negative debit pattern.""" csv_content = """Date,Description,Amount 01/15/2025,GROCERY STORE,-123.45 01/16/2025,PAYROLL,2500.00 @@ -970,12 +970,16 @@ def test_suggests_negated_amount_for_negative_debits(self, capsys): try: cmd_inspect(Namespace(file=tmpfile, rows=3)) captured = capsys.readouterr() - assert 'format: "{date:%m/%d/%Y}, {description}, {-amount}"' in captured.out + assert 'Suggested format string template:' in captured.out + assert 'format: "{date:%m/%d/%Y}, {description}, "' in captured.out + assert 'Observed mostly negative amounts (3 negative, 1 positive).' in captured.out + assert 'Use {amount} if you want to keep negative debits/spending negative in Tally.' in captured.out + assert 'Use {-amount} if you want to flip those debits/spending amounts to positive values in Tally.' in captured.out finally: os.unlink(tmpfile) - def test_keeps_amount_for_positive_debits(self, capsys): - """Credit-card-style exports with positive debits should keep {amount}.""" + def test_reports_positive_debit_observations(self, capsys): + """Credit-card-style exports should explain the observed positive debit pattern.""" csv_content = """Date,Description,Amount 01/15/2025,GROCERY STORE,123.45 01/16/2025,COFFEE SHOP,5.99 @@ -988,6 +992,9 @@ def test_keeps_amount_for_positive_debits(self, capsys): try: cmd_inspect(Namespace(file=tmpfile, rows=3)) captured = capsys.readouterr() - assert 'format: "{date:%m/%d/%Y}, {description}, {amount}"' in captured.out + assert 'format: "{date:%m/%d/%Y}, {description}, "' in captured.out + assert 'Observed mostly positive amounts (2 positive, 1 negative).' in captured.out + assert 'Use {amount} if you want to keep positive debits/spending positive in Tally.' in captured.out + assert 'Use {-amount} if you want to flip that convention so those rows become negative in Tally.' in captured.out finally: os.unlink(tmpfile) From 2dac064bb439607256a03227bfa01b9a458bd324 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 11 Apr 2026 05:58:31 +0000 Subject: [PATCH 06/15] Finalize inspect amount guidance behavior Agent-Logs-Url: https://github.com/davidfowl/tally/sessions/8ba8ec62-9f28-4226-b7cc-88ecdb4a3555 Co-authored-by: davidfowl <95136+davidfowl@users.noreply.github.com> --- tests/test_inspect.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/tests/test_inspect.py b/tests/test_inspect.py index 410c1ac..fdb835a 100644 --- a/tests/test_inspect.py +++ b/tests/test_inspect.py @@ -983,17 +983,18 @@ def test_reports_positive_debit_observations(self, capsys): csv_content = """Date,Description,Amount 01/15/2025,GROCERY STORE,123.45 01/16/2025,COFFEE SHOP,5.99 -01/17/2025,PAYMENT,-500.00 +01/17/2025,GAS STATION,45.00 +01/18/2025,PAYMENT,-500.00 """ with tempfile.NamedTemporaryFile(mode='w', suffix='.csv', delete=False) as f: f.write(csv_content) tmpfile = f.name try: - cmd_inspect(Namespace(file=tmpfile, rows=3)) + cmd_inspect(Namespace(file=tmpfile, rows=4)) captured = capsys.readouterr() assert 'format: "{date:%m/%d/%Y}, {description}, "' in captured.out - assert 'Observed mostly positive amounts (2 positive, 1 negative).' in captured.out + assert 'Observed mostly positive amounts (3 positive, 1 negative).' in captured.out assert 'Use {amount} if you want to keep positive debits/spending positive in Tally.' in captured.out assert 'Use {-amount} if you want to flip that convention so those rows become negative in Tally.' in captured.out finally: From c6b2accb35cf9f2cc25b53cc19fb22b8ba6251b5 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 11 Apr 2026 05:59:39 +0000 Subject: [PATCH 07/15] Address inspect review feedback Agent-Logs-Url: https://github.com/davidfowl/tally/sessions/8ba8ec62-9f28-4226-b7cc-88ecdb4a3555 Co-authored-by: davidfowl <95136+davidfowl@users.noreply.github.com> --- src/tally/commands/inspect.py | 8 ++++---- tests/test_inspect.py | 4 ++++ 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/tally/commands/inspect.py b/src/tally/commands/inspect.py index b377885..00b74de 100644 --- a/src/tally/commands/inspect.py +++ b/src/tally/commands/inspect.py @@ -502,7 +502,7 @@ def _describe_amount_sign_observations(analysis): def _describe_amount_token_usage(analysis): """Explain how {amount} and {-amount} relate to the observed signs.""" if not analysis: - return ["Replace with the token that matches your CSV's sign convention."] + return ["Replace in the format string template with the token that matches your CSV's sign convention."] positive_count = analysis['positive_count'] negative_count = analysis['negative_count'] @@ -513,20 +513,20 @@ def _describe_amount_token_usage(analysis): return [ "Use {amount} if you want to keep negative debits/spending negative in Tally.", "Use {-amount} if you want to flip those debits/spending amounts to positive values in Tally.", - 'Replace in the template with the option you choose.', + 'Replace in the format string template with the option you choose.', ] if negative_count == 0 or positive_pct > CLEARLY_POSITIVE_DEBITS_THRESHOLD: return [ "Use {amount} if you want to keep positive debits/spending positive in Tally.", "Use {-amount} if you want to flip that convention so those rows become negative in Tally.", - 'Replace in the template with the option you choose.', + 'Replace in the format string template with the option you choose.', ] return [ "Use {amount} to preserve the CSV's original signs.", "Use {-amount} to invert the CSV's original signs.", - 'Replace in the template after reviewing the sample rows above.', + 'Replace in the format string template after reviewing the sample rows above.', ] diff --git a/tests/test_inspect.py b/tests/test_inspect.py index fdb835a..9143a19 100644 --- a/tests/test_inspect.py +++ b/tests/test_inspect.py @@ -972,6 +972,8 @@ def test_reports_negative_debit_observations(self, capsys): captured = capsys.readouterr() assert 'Suggested format string template:' in captured.out assert 'format: "{date:%m/%d/%Y}, {description}, "' in captured.out + assert '1 positive amounts, totaling $2,500.00' in captured.out + assert '3 negative amounts, totaling $1,329.44' in captured.out assert 'Observed mostly negative amounts (3 negative, 1 positive).' in captured.out assert 'Use {amount} if you want to keep negative debits/spending negative in Tally.' in captured.out assert 'Use {-amount} if you want to flip those debits/spending amounts to positive values in Tally.' in captured.out @@ -994,6 +996,8 @@ def test_reports_positive_debit_observations(self, capsys): cmd_inspect(Namespace(file=tmpfile, rows=4)) captured = capsys.readouterr() assert 'format: "{date:%m/%d/%Y}, {description}, "' in captured.out + assert '3 positive amounts, totaling $174.44' in captured.out + assert '1 negative amounts, totaling $500.00' in captured.out assert 'Observed mostly positive amounts (3 positive, 1 negative).' in captured.out assert 'Use {amount} if you want to keep positive debits/spending positive in Tally.' in captured.out assert 'Use {-amount} if you want to flip that convention so those rows become negative in Tally.' in captured.out From fe2df842dc3ce84ed5358fb524e038029c2738c5 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 11 Apr 2026 06:00:48 +0000 Subject: [PATCH 08/15] Polish inspect amount wording Agent-Logs-Url: https://github.com/davidfowl/tally/sessions/8ba8ec62-9f28-4226-b7cc-88ecdb4a3555 Co-authored-by: davidfowl <95136+davidfowl@users.noreply.github.com> --- src/tally/commands/inspect.py | 10 ++++++++-- tests/test_inspect.py | 4 ++-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/tally/commands/inspect.py b/src/tally/commands/inspect.py index 00b74de..32ee02a 100644 --- a/src/tally/commands/inspect.py +++ b/src/tally/commands/inspect.py @@ -184,8 +184,8 @@ def cmd_inspect(args): print("\n" + "=" * 70) print("Amount Distribution:") print("-" * 70) - print(f" {analysis['positive_count']} positive amounts, totaling ${analysis['positive_total']:,.2f}") - print(f" {analysis['negative_count']} negative amounts, totaling ${analysis['negative_total']:,.2f}") + print(f" {_format_amount_distribution_line(analysis['positive_count'], 'positive', analysis['positive_total'])}") + print(f" {_format_amount_distribution_line(analysis['negative_count'], 'negative', analysis['negative_total'])}") # Show format observations if analysis['format_observations']: @@ -499,6 +499,12 @@ def _describe_amount_sign_observations(analysis): ] +def _format_amount_distribution_line(count, label, total): + """Format an amount distribution line with correct singular/plural wording.""" + noun = 'amount' if count == 1 else 'amounts' + return f"{count} {label} {noun}, totaling ${total:,.2f}" + + def _describe_amount_token_usage(analysis): """Explain how {amount} and {-amount} relate to the observed signs.""" if not analysis: diff --git a/tests/test_inspect.py b/tests/test_inspect.py index 9143a19..1666b9b 100644 --- a/tests/test_inspect.py +++ b/tests/test_inspect.py @@ -972,7 +972,7 @@ def test_reports_negative_debit_observations(self, capsys): captured = capsys.readouterr() assert 'Suggested format string template:' in captured.out assert 'format: "{date:%m/%d/%Y}, {description}, "' in captured.out - assert '1 positive amounts, totaling $2,500.00' in captured.out + assert '1 positive amount, totaling $2,500.00' in captured.out assert '3 negative amounts, totaling $1,329.44' in captured.out assert 'Observed mostly negative amounts (3 negative, 1 positive).' in captured.out assert 'Use {amount} if you want to keep negative debits/spending negative in Tally.' in captured.out @@ -997,7 +997,7 @@ def test_reports_positive_debit_observations(self, capsys): captured = capsys.readouterr() assert 'format: "{date:%m/%d/%Y}, {description}, "' in captured.out assert '3 positive amounts, totaling $174.44' in captured.out - assert '1 negative amounts, totaling $500.00' in captured.out + assert '1 negative amount, totaling $500.00' in captured.out assert 'Observed mostly positive amounts (3 positive, 1 negative).' in captured.out assert 'Use {amount} if you want to keep positive debits/spending positive in Tally.' in captured.out assert 'Use {-amount} if you want to flip that convention so those rows become negative in Tally.' in captured.out From f7c99e28b023462da6621bd4f58c37dd71cca5b6 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 11 Apr 2026 06:01:53 +0000 Subject: [PATCH 09/15] Harden inspect amount helpers Agent-Logs-Url: https://github.com/davidfowl/tally/sessions/8ba8ec62-9f28-4226-b7cc-88ecdb4a3555 Co-authored-by: davidfowl <95136+davidfowl@users.noreply.github.com> --- src/tally/commands/inspect.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/tally/commands/inspect.py b/src/tally/commands/inspect.py index 32ee02a..2ed98f0 100644 --- a/src/tally/commands/inspect.py +++ b/src/tally/commands/inspect.py @@ -473,7 +473,10 @@ def _describe_amount_sign_observations(analysis): positive_count = analysis['positive_count'] negative_count = analysis['negative_count'] total_count = positive_count + negative_count - positive_pct = positive_count / total_count * 100 if total_count else 0 + if total_count == 0: + return ["No non-zero amounts were found in the sampled rows."] + + positive_pct = positive_count / total_count * 100 if positive_count == 0: return ["Observed only negative non-zero amounts in the sampled rows."] @@ -499,10 +502,10 @@ def _describe_amount_sign_observations(analysis): ] -def _format_amount_distribution_line(count, label, total): +def _format_amount_distribution_line(count, sign_label, total): """Format an amount distribution line with correct singular/plural wording.""" noun = 'amount' if count == 1 else 'amounts' - return f"{count} {label} {noun}, totaling ${total:,.2f}" + return f"{count} {sign_label} {noun}, totaling ${total:,.2f}" def _describe_amount_token_usage(analysis): @@ -513,7 +516,10 @@ def _describe_amount_token_usage(analysis): positive_count = analysis['positive_count'] negative_count = analysis['negative_count'] total_count = positive_count + negative_count - positive_pct = positive_count / total_count * 100 if total_count else 0 + if total_count == 0: + return ["Replace in the format string template with the token that matches your CSV's sign convention."] + + positive_pct = positive_count / total_count * 100 if positive_count == 0 or positive_pct < CLEARLY_NEGATIVE_DEBITS_THRESHOLD: return [ From 948dc0bb374ac7bc387c2658b97de22f437ff202 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 11 Apr 2026 06:09:15 +0000 Subject: [PATCH 10/15] Remove inspect threshold wording Agent-Logs-Url: https://github.com/davidfowl/tally/sessions/47a849c0-779f-4f57-aea2-e5c955aacdd6 Co-authored-by: davidfowl <95136+davidfowl@users.noreply.github.com> --- src/tally/commands/inspect.py | 187 +--------------------------------- tests/test_inspect.py | 16 +-- 2 files changed, 13 insertions(+), 190 deletions(-) diff --git a/src/tally/commands/inspect.py b/src/tally/commands/inspect.py index 2ed98f0..5c010f6 100644 --- a/src/tally/commands/inspect.py +++ b/src/tally/commands/inspect.py @@ -9,9 +9,6 @@ from ..colors import C from ..analyzer import auto_detect_csv_format -CLEARLY_NEGATIVE_DEBITS_THRESHOLD = 30 -CLEARLY_POSITIVE_DEBITS_THRESHOLD = 70 - def cmd_inspect(args): """Handle the 'inspect' subcommand - show CSV structure and sample rows.""" @@ -328,143 +325,6 @@ def _detect_file_format(filepath): return result -def _analyze_amount_patterns(filepath, amount_col, has_header=True, delimiter=None, max_rows=1000): - """ - Analyze amount column patterns to help users understand their data's sign convention. - - Returns dict with: - - positive_count: number of positive amounts - - negative_count: number of negative amounts - - positive_total: sum of positive amounts - - negative_total: sum of negative amounts (as positive number) - - sign_convention: 'expenses_positive' or 'expenses_negative' - - suggest_negate: True if user should use {-amount} to normalize - - sample_credits: list of (description, amount) for likely transfers/income - """ - import re as re_mod - - positive_count = 0 - negative_count = 0 - positive_total = 0.0 - negative_total = 0.0 - sample_credits = [] # (description, amount) tuples - - def parse_amount(val): - """Parse amount string to float, handling currency symbols and parentheses.""" - if not val: - return None - val = val.strip() - # Remove currency symbols, commas - val = re_mod.sub(r'[$€£¥,]', '', val) - # Handle parentheses as negative - if val.startswith('(') and val.endswith(')'): - val = '-' + val[1:-1] - try: - return float(val) - except ValueError: - return None - - try: - with open(filepath, 'r', encoding='utf-8') as f: - if delimiter and delimiter.startswith('regex:'): - # Regex-based parsing - pattern = re_mod.compile(delimiter[6:]) - for i, line in enumerate(f): - if has_header and i == 0: - continue - if i >= max_rows: - break - line = line.strip() - if not line: - continue - match = pattern.match(line) - if match: - groups = match.groups() - if amount_col < len(groups): - amount = parse_amount(groups[amount_col]) - if amount is not None: - desc = groups[1] if len(groups) > 1 else '' - if amount >= 0: - positive_count += 1 - positive_total += amount - else: - negative_count += 1 - negative_total += abs(amount) - if len(sample_credits) < 10: - sample_credits.append((desc.strip(), amount)) - else: - # Standard CSV - reader = csv.reader(f) - if has_header: - headers = next(reader, None) - desc_col = 1 # default - for idx, h in enumerate(headers or []): - hl = h.lower() - if 'desc' in hl or 'merchant' in hl or 'payee' in hl or 'name' in hl: - desc_col = idx - break - else: - desc_col = 1 - - for i, row in enumerate(reader): - if i >= max_rows: - break - if amount_col < len(row): - amount = parse_amount(row[amount_col]) - if amount is not None: - desc = row[desc_col] if desc_col < len(row) else '' - if amount >= 0: - positive_count += 1 - positive_total += amount - else: - negative_count += 1 - negative_total += abs(amount) - if len(sample_credits) < 10: - sample_credits.append((desc.strip(), amount)) - except Exception: - return None - - total_count = positive_count + negative_count - if total_count == 0: - return None - - # Determine sign convention based on distribution - # Expenses positive: mostly positive amounts (typical credit card export) - # Expenses negative: mostly negative amounts (typical bank export) - positive_pct = positive_count / total_count * 100 - - if positive_pct > 70: - sign_convention = 'expenses_positive' - suggest_negate = False - rationale = "mostly positive amounts (expenses are positive)" - elif positive_pct < 30: - sign_convention = 'expenses_negative' - suggest_negate = True - rationale = "mostly negative amounts (expenses are negative)" - else: - # Mixed - harder to tell - if positive_total > negative_total: - sign_convention = 'expenses_positive' - suggest_negate = False - rationale = "total positive exceeds negative" - else: - sign_convention = 'expenses_negative' - suggest_negate = True - rationale = "total negative exceeds positive" - - return { - 'positive_count': positive_count, - 'negative_count': negative_count, - 'positive_total': positive_total, - 'negative_total': negative_total, - 'positive_pct': positive_pct, - 'sign_convention': sign_convention, - 'suggest_negate': suggest_negate, - 'rationale': rationale, - 'sample_credits': sample_credits, - } - - def _describe_amount_sign_observations(analysis): """Describe what the sampled amount signs look like.""" if not analysis: @@ -476,29 +336,14 @@ def _describe_amount_sign_observations(analysis): if total_count == 0: return ["No non-zero amounts were found in the sampled rows."] - positive_pct = positive_count / total_count * 100 - if positive_count == 0: - return ["Observed only negative non-zero amounts in the sampled rows."] + return [f"Observed {positive_count} positive and {negative_count} negative non-zero amounts in the sampled rows."] if negative_count == 0: - return ["Observed only positive non-zero amounts in the sampled rows."] - - if positive_pct < CLEARLY_NEGATIVE_DEBITS_THRESHOLD: - return [ - f"Observed mostly negative amounts ({negative_count} negative, {positive_count} positive).", - "Debits or spending may already be negative in this export.", - ] - - if positive_pct > CLEARLY_POSITIVE_DEBITS_THRESHOLD: - return [ - f"Observed mostly positive amounts ({positive_count} positive, {negative_count} negative).", - "Debits or spending may already be positive in this export.", - ] + return [f"Observed {positive_count} positive and {negative_count} negative non-zero amounts in the sampled rows."] return [ - f"Observed a mixed sign pattern ({positive_count} positive, {negative_count} negative).", - "Large payments, refunds, or transfers may be mixed with day-to-day spending.", + f"Observed {positive_count} positive and {negative_count} negative non-zero amounts in the sampled rows.", ] @@ -511,34 +356,12 @@ def _format_amount_distribution_line(count, sign_label, total): def _describe_amount_token_usage(analysis): """Explain how {amount} and {-amount} relate to the observed signs.""" if not analysis: - return ["Replace in the format string template with the token that matches your CSV's sign convention."] - - positive_count = analysis['positive_count'] - negative_count = analysis['negative_count'] - total_count = positive_count + negative_count - if total_count == 0: - return ["Replace in the format string template with the token that matches your CSV's sign convention."] - - positive_pct = positive_count / total_count * 100 - - if positive_count == 0 or positive_pct < CLEARLY_NEGATIVE_DEBITS_THRESHOLD: - return [ - "Use {amount} if you want to keep negative debits/spending negative in Tally.", - "Use {-amount} if you want to flip those debits/spending amounts to positive values in Tally.", - 'Replace in the format string template with the option you choose.', - ] - - if negative_count == 0 or positive_pct > CLEARLY_POSITIVE_DEBITS_THRESHOLD: - return [ - "Use {amount} if you want to keep positive debits/spending positive in Tally.", - "Use {-amount} if you want to flip that convention so those rows become negative in Tally.", - 'Replace in the format string template with the option you choose.', - ] + return ['Replace in the format string template with the option you choose.'] return [ "Use {amount} to preserve the CSV's original signs.", "Use {-amount} to invert the CSV's original signs.", - 'Replace in the format string template after reviewing the sample rows above.', + 'Replace in the format string template with the option you choose.', ] diff --git a/tests/test_inspect.py b/tests/test_inspect.py index 1666b9b..cdeb67b 100644 --- a/tests/test_inspect.py +++ b/tests/test_inspect.py @@ -956,7 +956,7 @@ class TestCmdInspect: """Regression tests for inspect command amount guidance.""" def test_reports_negative_debit_observations(self, capsys): - """Bank-style exports should explain the observed negative debit pattern.""" + """Bank-style exports should report the observed sign counts.""" csv_content = """Date,Description,Amount 01/15/2025,GROCERY STORE,-123.45 01/16/2025,PAYROLL,2500.00 @@ -974,14 +974,14 @@ def test_reports_negative_debit_observations(self, capsys): assert 'format: "{date:%m/%d/%Y}, {description}, "' in captured.out assert '1 positive amount, totaling $2,500.00' in captured.out assert '3 negative amounts, totaling $1,329.44' in captured.out - assert 'Observed mostly negative amounts (3 negative, 1 positive).' in captured.out - assert 'Use {amount} if you want to keep negative debits/spending negative in Tally.' in captured.out - assert 'Use {-amount} if you want to flip those debits/spending amounts to positive values in Tally.' in captured.out + assert 'Observed 1 positive and 3 negative non-zero amounts in the sampled rows.' in captured.out + assert "Use {amount} to preserve the CSV's original signs." in captured.out + assert "Use {-amount} to invert the CSV's original signs." in captured.out finally: os.unlink(tmpfile) def test_reports_positive_debit_observations(self, capsys): - """Credit-card-style exports should explain the observed positive debit pattern.""" + """Credit-card-style exports should report the observed sign counts.""" csv_content = """Date,Description,Amount 01/15/2025,GROCERY STORE,123.45 01/16/2025,COFFEE SHOP,5.99 @@ -998,8 +998,8 @@ def test_reports_positive_debit_observations(self, capsys): assert 'format: "{date:%m/%d/%Y}, {description}, "' in captured.out assert '3 positive amounts, totaling $174.44' in captured.out assert '1 negative amount, totaling $500.00' in captured.out - assert 'Observed mostly positive amounts (3 positive, 1 negative).' in captured.out - assert 'Use {amount} if you want to keep positive debits/spending positive in Tally.' in captured.out - assert 'Use {-amount} if you want to flip that convention so those rows become negative in Tally.' in captured.out + assert 'Observed 3 positive and 1 negative non-zero amounts in the sampled rows.' in captured.out + assert "Use {amount} to preserve the CSV's original signs." in captured.out + assert "Use {-amount} to invert the CSV's original signs." in captured.out finally: os.unlink(tmpfile) From ce332413fa17fa7f02a4e7c09552d790b121e7cf Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 11 Apr 2026 06:11:04 +0000 Subject: [PATCH 11/15] Simplify inspect sign reporting Agent-Logs-Url: https://github.com/davidfowl/tally/sessions/47a849c0-779f-4f57-aea2-e5c955aacdd6 Co-authored-by: davidfowl <95136+davidfowl@users.noreply.github.com> --- src/tally/commands/inspect.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/tally/commands/inspect.py b/src/tally/commands/inspect.py index 5c010f6..5db3f4b 100644 --- a/src/tally/commands/inspect.py +++ b/src/tally/commands/inspect.py @@ -336,12 +336,6 @@ def _describe_amount_sign_observations(analysis): if total_count == 0: return ["No non-zero amounts were found in the sampled rows."] - if positive_count == 0: - return [f"Observed {positive_count} positive and {negative_count} negative non-zero amounts in the sampled rows."] - - if negative_count == 0: - return [f"Observed {positive_count} positive and {negative_count} negative non-zero amounts in the sampled rows."] - return [ f"Observed {positive_count} positive and {negative_count} negative non-zero amounts in the sampled rows.", ] From 5de668ba379419326fa61a6b264c7d9ac8850d96 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 11 Apr 2026 06:12:06 +0000 Subject: [PATCH 12/15] Tidy inspect distribution output Agent-Logs-Url: https://github.com/davidfowl/tally/sessions/47a849c0-779f-4f57-aea2-e5c955aacdd6 Co-authored-by: davidfowl <95136+davidfowl@users.noreply.github.com> --- src/tally/commands/inspect.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tally/commands/inspect.py b/src/tally/commands/inspect.py index 5db3f4b..74b7314 100644 --- a/src/tally/commands/inspect.py +++ b/src/tally/commands/inspect.py @@ -181,8 +181,8 @@ def cmd_inspect(args): print("\n" + "=" * 70) print("Amount Distribution:") print("-" * 70) - print(f" {_format_amount_distribution_line(analysis['positive_count'], 'positive', analysis['positive_total'])}") - print(f" {_format_amount_distribution_line(analysis['negative_count'], 'negative', analysis['negative_total'])}") + print(" " + _format_amount_distribution_line(analysis['positive_count'], 'positive', analysis['positive_total'])) + print(" " + _format_amount_distribution_line(analysis['negative_count'], 'negative', analysis['negative_total'])) # Show format observations if analysis['format_observations']: From dd7f27d4c70d8bba24ad46445b91d8c39342416c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 11 Apr 2026 06:13:02 +0000 Subject: [PATCH 13/15] Clarify inspect token guidance Agent-Logs-Url: https://github.com/davidfowl/tally/sessions/47a849c0-779f-4f57-aea2-e5c955aacdd6 Co-authored-by: davidfowl <95136+davidfowl@users.noreply.github.com> --- src/tally/commands/inspect.py | 4 ++-- tests/test_inspect.py | 10 ++++++---- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/tally/commands/inspect.py b/src/tally/commands/inspect.py index 74b7314..df2153a 100644 --- a/src/tally/commands/inspect.py +++ b/src/tally/commands/inspect.py @@ -353,8 +353,8 @@ def _describe_amount_token_usage(analysis): return ['Replace in the format string template with the option you choose.'] return [ - "Use {amount} to preserve the CSV's original signs.", - "Use {-amount} to invert the CSV's original signs.", + "Use {amount} to keep amounts as they appear in the CSV.", + "Use {-amount} to flip the sign of amounts from the CSV.", 'Replace in the format string template with the option you choose.', ] diff --git a/tests/test_inspect.py b/tests/test_inspect.py index cdeb67b..648b59f 100644 --- a/tests/test_inspect.py +++ b/tests/test_inspect.py @@ -975,8 +975,9 @@ def test_reports_negative_debit_observations(self, capsys): assert '1 positive amount, totaling $2,500.00' in captured.out assert '3 negative amounts, totaling $1,329.44' in captured.out assert 'Observed 1 positive and 3 negative non-zero amounts in the sampled rows.' in captured.out - assert "Use {amount} to preserve the CSV's original signs." in captured.out - assert "Use {-amount} to invert the CSV's original signs." in captured.out + assert "Use {amount} to keep amounts as they appear in the CSV." in captured.out + assert "Use {-amount} to flip the sign of amounts from the CSV." in captured.out + assert "Replace in the format string template with the option you choose." in captured.out finally: os.unlink(tmpfile) @@ -999,7 +1000,8 @@ def test_reports_positive_debit_observations(self, capsys): assert '3 positive amounts, totaling $174.44' in captured.out assert '1 negative amount, totaling $500.00' in captured.out assert 'Observed 3 positive and 1 negative non-zero amounts in the sampled rows.' in captured.out - assert "Use {amount} to preserve the CSV's original signs." in captured.out - assert "Use {-amount} to invert the CSV's original signs." in captured.out + assert "Use {amount} to keep amounts as they appear in the CSV." in captured.out + assert "Use {-amount} to flip the sign of amounts from the CSV." in captured.out + assert "Replace in the format string template with the option you choose." in captured.out finally: os.unlink(tmpfile) From 9edd19ecae14ab7ad0d7735cd9cce9b5572fd70f Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 11 Apr 2026 06:13:59 +0000 Subject: [PATCH 14/15] Add inspect plus-amount guidance Agent-Logs-Url: https://github.com/davidfowl/tally/sessions/47a849c0-779f-4f57-aea2-e5c955aacdd6 Co-authored-by: davidfowl <95136+davidfowl@users.noreply.github.com> --- src/tally/commands/inspect.py | 1 + tests/test_inspect.py | 2 ++ 2 files changed, 3 insertions(+) diff --git a/src/tally/commands/inspect.py b/src/tally/commands/inspect.py index df2153a..1bd06a4 100644 --- a/src/tally/commands/inspect.py +++ b/src/tally/commands/inspect.py @@ -355,6 +355,7 @@ def _describe_amount_token_usage(analysis): return [ "Use {amount} to keep amounts as they appear in the CSV.", "Use {-amount} to flip the sign of amounts from the CSV.", + "Use {+amount} if you want all parsed amounts to be positive.", 'Replace in the format string template with the option you choose.', ] diff --git a/tests/test_inspect.py b/tests/test_inspect.py index 648b59f..bb5fc09 100644 --- a/tests/test_inspect.py +++ b/tests/test_inspect.py @@ -977,6 +977,7 @@ def test_reports_negative_debit_observations(self, capsys): assert 'Observed 1 positive and 3 negative non-zero amounts in the sampled rows.' in captured.out assert "Use {amount} to keep amounts as they appear in the CSV." in captured.out assert "Use {-amount} to flip the sign of amounts from the CSV." in captured.out + assert "Use {+amount} if you want all parsed amounts to be positive." in captured.out assert "Replace in the format string template with the option you choose." in captured.out finally: os.unlink(tmpfile) @@ -1002,6 +1003,7 @@ def test_reports_positive_debit_observations(self, capsys): assert 'Observed 3 positive and 1 negative non-zero amounts in the sampled rows.' in captured.out assert "Use {amount} to keep amounts as they appear in the CSV." in captured.out assert "Use {-amount} to flip the sign of amounts from the CSV." in captured.out + assert "Use {+amount} if you want all parsed amounts to be positive." in captured.out assert "Replace in the format string template with the option you choose." in captured.out finally: os.unlink(tmpfile) From 4e5a0a95e6e5b99661c1f8121cb534e27be69fc3 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 11 Apr 2026 06:14:59 +0000 Subject: [PATCH 15/15] Finalize neutral inspect wording Agent-Logs-Url: https://github.com/davidfowl/tally/sessions/47a849c0-779f-4f57-aea2-e5c955aacdd6 Co-authored-by: davidfowl <95136+davidfowl@users.noreply.github.com> --- src/tally/commands/inspect.py | 4 ++-- tests/test_inspect.py | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/tally/commands/inspect.py b/src/tally/commands/inspect.py index 1bd06a4..5218cd9 100644 --- a/src/tally/commands/inspect.py +++ b/src/tally/commands/inspect.py @@ -210,9 +210,9 @@ def cmd_inspect(args): # Show amount modifier options with context print("\n How to use amount tokens:") - print(f" {{amount}} - preserve the sign from the CSV") + print(f" {{amount}} - use amounts as they appear in the CSV") print(f" {{-amount}} - flip the sign from the CSV") - print(f" {{+amount}} - take the absolute value") + print(f" {{+amount}} - make all amounts positive") for line in _describe_amount_token_usage(analysis): print(f" - {line}") diff --git a/tests/test_inspect.py b/tests/test_inspect.py index bb5fc09..6de6ce2 100644 --- a/tests/test_inspect.py +++ b/tests/test_inspect.py @@ -955,8 +955,8 @@ def test_tab_delimiter(self): class TestCmdInspect: """Regression tests for inspect command amount guidance.""" - def test_reports_negative_debit_observations(self, capsys): - """Bank-style exports should report the observed sign counts.""" + def test_reports_negative_amount_observations(self, capsys): + """Exports with mostly negative amounts should report the observed sign counts.""" csv_content = """Date,Description,Amount 01/15/2025,GROCERY STORE,-123.45 01/16/2025,PAYROLL,2500.00 @@ -982,8 +982,8 @@ def test_reports_negative_debit_observations(self, capsys): finally: os.unlink(tmpfile) - def test_reports_positive_debit_observations(self, capsys): - """Credit-card-style exports should report the observed sign counts.""" + def test_reports_positive_amount_observations(self, capsys): + """Exports with mostly positive amounts should report the observed sign counts.""" csv_content = """Date,Description,Amount 01/15/2025,GROCERY STORE,123.45 01/16/2025,COFFEE SHOP,5.99