Skip to content

fix(harness): align ratios position by position and count a double only where flows meet - #91

Merged
jm-rivera merged 1 commit into
devfrom
fix/studio-ratio-provider-contracts
Oct 28, 2026
Merged

jm-rivera merged 1 commit into
devfrom
fix/studio-ratio-provider-contracts

Conversation

@jm-rivera

Copy link
Copy Markdown
Contributor

Deploy before DevelopmentFinanceObservatory/apis#166, whose DAC1 bilateral sibling option would otherwise let the first fault below move a requested denominator.

An outer calculation over shares or quotients reached through them to their datasets when it aligned provider scope and measure basis (_alignment_targets in services/harness/src/submission/service.py). Comparing two US shares for one year, each a CRS bilateral sector over the DAC1 total with accept_provider_difference, therefore targeted both DAC1 totals for bilateral alignment: once apis#166 offers DAC1 bilateral ODA the totals move silently, and without that option the valid comparison is refused. _input_groups now groups a step's inputs by position: a step whose inputs are all ratios, reached directly or through single-input steps such as an aggregate, aligns their numerators together and their denominators together; any other step reads a ratio as one figure and aligns nothing through it. A position under a ratio that accepts a provider difference is not aligned on provider, so the accepted total stands, and the outer step states the difference with the lead for a difference the question asked for. Gross ODA per person compared with net ODA per person still aligns its numerators' basis.

The double-count guard refused a sum of US bilateral health aid and US inclusive education aid, saying the education figure already held the health flows. _Flows.meet now refuses only when the two inputs' flows meet on every axis: donors and recipients from the base identity, sectors from the sector slot's codes with code-index expansion (120 meets 12220), and years from the requested windows. An axis it cannot read, and an unknown provider scope, count as overlapping. Donors are apart only when both sides name countries alone with none shared, so a donor group beside a member country is refused. compiler/sources.py exposes slot_codes and countries_apart for these reads.

Testing: tests/planner/test_agent.py (622), test_planner_seam.py, test_plan_display.py, compiler/test_sources.py and compiler/test_compile.py pass, with ruff and ty clean. New tests cover the two-share comparison in four arrangements (both accepting DAC1, a declared whole beside an accepting and a non-accepting DAC1 share, health over DAC1 beside education over a CRS total), the per-person basis comparison, and six sums apart or meeting on sector, years and recipients; each failed before its fix.

…ly where flows meet

An outer calculation over shares or quotients reached through them to
their datasets, so comparing two shares that each kept a DAC1 total could
move both totals to another provider scope, or refuse. Ratios now align
with each other numerator to numerator and denominator to denominator, a
ratio's accepted provider difference stands, and the outer step states any
remaining difference. The double-count guard refused sums of disjoint
flows; it now refuses only when donors, recipients, sectors and years all
overlap.
@jm-rivera
jm-rivera merged commit eefa15b into dev Oct 28, 2026
3 checks passed
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