Skip to content

fix: rates.py for_period ambiguity guard - #122

Merged
svedbg merged 1 commit into
mainfrom
fix/rates-for-period-ambiguity
Sep 6, 2026
Merged

fix: rates.py for_period ambiguity guard#122
svedbg merged 1 commit into
mainfrom
fix/rates-for-period-ambiguity

Conversation

@svedbg

@svedbg svedbg commented Sep 6, 2026

Copy link
Copy Markdown
Owner

Summary

Found by adversarial review of the rates-extraction-coverage work (PR #119): scripts/rates.py's for_period() had no uniqueness guard, unlike every other lookup in the module - load_flat()/_extract_one() and test/rates_test.py's own extract() all refuse when a pattern matches more than once, but for_period() returned the FIRST matching row via re.finditer() with no check for a second one agreeing or not.

A duplicated or decoy period row above the real one - the exact failure extract() exists to catch in the reference file itself - would silently hand for_period() callers (audit.py's B1/B4/B5/F5 checks) whichever value came first. Proved it: a decoy row with a conflicting figure ahead of the real МРЗ row made for_period() return the decoy's 999.99 instead of 620.20, silently. It now returns None, forcing the caller to refuse rather than audit against a wrong number.

Touches shipped code (skills/trz-expert/scripts/rates.py), not just test/ - kept as its own PR for that reason.

Test plan

  • full free battery (pre-commit hook ran rates_test, skill_test, all 5 suites, k_checker, audit checker)
  • sabotage/revert proof (duplicate conflicting МРЗ row → None instead of the wrong value)

🤖 Generated with Claude Code

https://claude.ai/code/session_01XvbzEbQwxwxxHAtXfPMrno

Adversarial review of the rates_test.py extraction-coverage work found
that for_period() had no uniqueness guard, unlike every other lookup in
this module: load_flat()/_extract_one() and test/rates_test.py's own
extract() all refuse (None, or a loud failure) when a pattern matches
more than once, but for_period() returned the FIRST matching row via
re.finditer() and never checked for a second one agreeing or not.

A duplicated or decoy period row above the real one - the exact failure
extract() exists to catch in the reference file itself - would silently
hand for_period() callers (audit.py's B1/B4/B5/F5 checks) whichever
value came first, not necessarily the right one. Proved it: a decoy row
with a conflicting figure ahead of the real МРЗ row made for_period()
return the decoy's 999.99 instead of 620.20, silently, before this fix;
it now returns None, forcing the caller to refuse the check rather than
audit against a wrong number - the whole reason this module exists.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XvbzEbQwxwxxHAtXfPMrno
@svedbg
svedbg force-pushed the fix/rates-for-period-ambiguity branch from 14cc4a9 to 33eda5c Compare September 6, 2026 13:16
@svedbg
svedbg merged commit 8e482f5 into main Sep 6, 2026
12 checks passed
@svedbg
svedbg deleted the fix/rates-for-period-ambiguity branch September 6, 2026 13:17
@svedbg svedbg mentioned this pull request Sep 6, 2026
svedbg added a commit that referenced this pull request Sep 6, 2026
Version bump only. scripts/rates.py ships with the plugin and its
for_period() behaviour changed (PR #122, already on main): it used to
return the first matching row for a period silently; it now refuses
(None) when two rows disagree on the same period, rather than risk
auditing against whichever row a table restructure happened to put
first. Anyone who installed 2.19.1 via /plugin has no way to know
whether their copy carries this fix without a version bump.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XvbzEbQwxwxxHAtXfPMrno
svedbg added a commit that referenced this pull request Sep 6, 2026
Version bump only. scripts/rates.py ships with the plugin and its
for_period() behaviour changed (PR #122, already on main): it used to
return the first matching row for a period silently; it now refuses
(None) when two rows disagree on the same period, rather than risk
auditing against whichever row a table restructure happened to put
first. Anyone who installed 2.19.1 via /plugin has no way to know
whether their copy carries this fix without a version bump.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XvbzEbQwxwxxHAtXfPMrno
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant