# Sandhi (सन्धि): transboundary river flows against treaty schedules

Product spec and methods note. Owner: Sandhi module. Status: building (the integrator flips it
to live). Last data run: 21 October 2026.

Sandhi sets a water-sharing treaty's own text, formula and schedule beside modelled river flow.
The first treaty is the 1996 India-Bangladesh Treaty on Sharing of the Ganga/Ganges Waters at
Farakka, whose thirty-year term runs from 12 December 1996 to 12 December 2026. Sandhi answers four questions:

1. What exactly does the treaty say, and do the published copies agree?
2. In each 10-day period of each lean season since 1997, how much water does the model show
   arriving at Farakka, beside the treaty's 1949 to 1988 schedule?
3. What split would the Annexure I formula give on those flows, and how often do they fall under
   the treaty's thresholds (75,000, 70,000 and 50,000 cusecs)?
4. What does the current forecast show for the coming lean season?

Sandhi is neutral between the two countries. It makes no finding about compliance, estimates no
releases, and never presents model flows as measurements.

## Users and decisions

| User | What Sandhi gives them |
|---|---|
| Researchers and journalists in both countries | The same numbers, text and caveats on either side of the border |
| Officials and technical staff | How the formula behaves in wet and dry seasons; which periods fall under the treaty's thresholds |
| Basin and disaster authorities | Lean-season context at Farakka and the outlook for the coming season |

## Surfaces

| Page | Path | What it shows |
|---|---|---|
| Overview | `/dashboard/sandhi/` | Days to (or since) 12 Dec 2026 in IST calendar days; schedule beside the modelled median, 10th to 90th percentile and the latest season; outlook for the coming season by period, with a note when it is not current; daily forecast from December; key facts; Teesta context; timeline of 25 dated, sourced events |
| Seasons | `/dashboard/sandhi/seasons.html` | Heat table of 30 seasons by 15 periods (flow, % of schedule, percentile, each side's share); season detail with daily flow and the formula split; counts under thresholds; trend; summary table; CSV export |
| Treaty | `/dashboard/sandhi/treaty.html` | Both source copies with SHA-256; formula check; Annexure I and II verbatim; calculator; Articles I to XII; differences between the copies; the 1977 Agreement for context |
| Methods | `/dashboard/sandhi/methods.html` | This note with live numbers from the exports |
| Product page | `/products/sandhi.html` | Product summary with live figures |

## Treaty text

Two published copies were read in full and compared word by word on 21 October 2026:

| Copy | Publisher | Form | SHA-256 |
|---|---|---|---|
| FAOLEX | Food and Agriculture Organization, record LEX-FAOC017351 ([PDF](https://faolex.fao.org/docs/pdf/bi-17351.pdf)) | Scanned typescript; names India first | `864edd97…cde3` |
| JRC Bangladesh | Joint Rivers Commission, Bangladesh, via the ADB Law and Policy Reform database ([PDF](https://lpr.adb.org/sites/default/files/resource/1610/ganges-water-sharing-treaty-1996.pdf)) | Text PDF; names Bangladesh first | `61c37557…82d4` |

`python3 -m trinetra.sandhi check-sources` re-downloads both copies and the 1977 text and compares
their SHA-256 with the values in `treaty.py`; all three matched when re-checked on 21 October
2026. A changed hash means the publisher replaced the file, and a person must read the new copy in
full before the encoded text is touched.

Each government's copy names itself first, which is normal treaty practice. Wording, numbers and
punctuation are kept as printed; typography is normalised (line breaks, spacing around "/").
Where the copies differ, both readings are kept in `treaty.json` and shown on the treaty page:

| Where | FAOLEX copy | JRC Bangladesh copy | Effect |
|---|---|---|---|
| Annexure I, condition | "during the period March 1 to May 10" | "during the period March 11 to May 10" | Both copies' Annexure II mark the same six periods, 11 March to 10 May. Sandhi follows those marks. |
| Annexure II, last February period | "21-28" | "21-28/29" | The period ends on the last day of February, so 29 February counts in leap years. |
| Annexure II, 1 to 10 January | 107,516 | 1,07,516 | Same number, different digit grouping. |
| Article II (iii) | "two governments" | "two Governments" | None |
| Article X | "five years' interval" | "five years interval" | None |
| Article XI | "ninety per cent" | "ninety percent" | None |
| Closing paragraph | "DONE at New Delhi 12th December, 1996" | "DONE at New Delhi on 12th December, 1996" | None |

The 1977 Agreement (Dacca, 5 November 1977; five-year term, Article XV) is quoted for context
from the Ministry of External Affairs' Indian Treaties Database, including its 80 per cent
minimum-release proviso (Article II (ii)), which comes up in the 2026 renewal discussion.

## The formula as code

`pipeline/trinetra/sandhi/treaty.py` (`shares`) and its browser mirror in
`dashboard/sandhi/sandhi.js` (`SD.shares`):

| Availability at Farakka | India | Bangladesh |
|---|---|---|
| 70,000 cusecs or less | 50% | 50% |
| more than 70,000, less than 75,000 | balance | 35,000 |
| 75,000 cusecs or more | 40,000 | balance |

* The formula is continuous: both neighbouring rows give 35,000 / 35,000 at 70,000 and
  40,000 / 35,000 at 75,000, so the boundary convention changes no number.
* Guarantee: in the six periods asterisked in Annexure II (11 to 20 March, 1 to 10 April and
  21 to 30 April for Bangladesh; 21 to 31 March, 11 to 20 April and 1 to 10 May for India) the
  named side receives at least 35,000 cusecs and the other side the balance.
* Availability under 35,000 cusecs in an asterisked period is not covered by the text. Sandhi
  caps the named side at the availability, gives the other side zero, and flags the period.
* Under 50,000 cusecs in any period, Article II (iii) calls for immediate consultations. Sandhi
  counts such periods and does not model the outcome.
* Article XI (not less than 90% of Bangladesh's share) applies only in the absence of agreement on
  adjustments following reviews. The data cannot show that condition, so it is not computed.
* Check: applied to the 1949 to 1988 flows printed in Annexure II, the code reproduces all 30
  printed shares (`python3 -m trinetra.sandhi check-formula`; unit tests; the treaty page).

## Data

| Source | What | Licence | Requests |
|---|---|---|---|
| Open-Meteo Flood API, `models=consolidated_v4` | GloFAS v4 reanalysis, daily, 1997-01-01 to the latest available (2025-05-31 at this run), both cells | CC BY 4.0; Copernicus EMS | 1, cached; refetch with `--refresh-history` |
| Open-Meteo Flood API, `models=forecast_v4` | Forecast archive (first day of each operational forecast), 2023-06-01 to yesterday, both cells | same | 1 per run |
| Open-Meteo Flood API, `ensemble=true` | 51 members for 210 days at Farakka (about 30 days medium range, then the seasonal forecast; values end about 7 months after it starts) | same | 1 per run |
| Open-Meteo Flood API, 7x7 grid | Last week's flow around each cell, to confirm the main channel | same | 1 per run |
| Open-Meteo Flood API, Teesta median | Ensemble median at the Teesta cell, used only as a consistency check | same | 1 per run |

A full first run weighs about 260 units against Open-Meteo's free tier (10,000 per day); a daily
refresh about 105. The free tier is for non-commercial use; a deployment needs a subscription or a
self-hosted instance.

**Cells** (from `docs/research/2026-10-data-sources.md`, re-checked each run): Ganga at Farakka
24.675 N, 87.925 E, about 14 km downstream of the barrage; Teesta 26.225 N, 88.975 E. At this run
the Farakka cell carried 99% of the largest mean flow among the 49 cells around it, and VARUNA's
Farakka point (24.825 N, 87.875 E, above the barrage) carried 99.5% of this cell's flow.

**Streams.** Sandhi uses the reanalysis where it exists and the forecast archive after it, so the
2026 season is the only one built from the archive. Where both exist (730 days, June 2023 to May
2025) the archive was 92% to 104% of the reanalysis (5th to 95th percentile of the daily ratio),
median 100%; on lean-season days 95% to 103%.

## Method

* **Units.** 1 m³/s = 1 / 0.3048³ = 35.3147 cusecs (the international foot is exactly 0.3048 m).
* **Dates.** GloFAS daily values as labelled by Open-Meteo in UTC. Timestamps in the exports are
  UTC; pages show IST. The count of days to 12 December 2026 and the timeline's "Upcoming" marks use
  the calendar date in IST, so they turn over at midnight IST.
* **Periods.** 1 to 10, 11 to 20, 21 to month end, January to May: 15 per season.
* **Availability.** Mean of the daily values in a period; a period with any missing day is dropped
  (none are missing for 1997 to 2026).
* **Schedule comparison.** Ratio of modelled availability to the Annexure II value, and the
  model's 1997 to 2026 median as a share of the schedule (66% from 11 January to 20 February,
  135% from 21 April to 31 May at this run). The schedule is an observed 1949 to 1988 average, so
  a gap combines model error with real change; Sandhi does not separate the two.
* **Percentiles.** Each value's Hazen plotting position, (rank - 0.5) / n, within the 1997 to 2026
  record for that period. Sample quantiles use linear interpolation (Hyndman and Fan type 7).
* **Low-flow tiers (heuristic).** The number of periods in a season under 50,000 cusecs, the
  Article II (iii) level: none, 1 or 2, 3 or more. At this run: 12, 6 and 12 seasons.
* **Trend.** Theil-Sen slope with its 95% interval and the Mann-Kendall test, for the 1 January to
  31 May mean and the 11 March to 10 May mean, and for each period; lag-1 autocorrelation of the
  residuals is reported. At this run the lean-season slope is +5,700 cusecs per decade with a 95%
  interval of −1,600 to +14,400 (p = 0.10): no trend distinguishable from year-to-year variation.
  Prose rounds slopes to the nearest 100 cusecs; the per-period table keeps whole cusecs.
* **Outlook.** Each member's mean over each period of the coming season, then quantiles across
  the 51 members; the chance under a threshold is the share of members below it. Periods the
  forecast does not fully cover are left out. If the forecast starts inside the lean season, the
  days before it come from the modelled record, identical in every member; `outlook.json` records
  them (`days_from_record` per period, `record_through`) and the overview labels those periods. The
  1996 formula is applied to the median for reference; the coming season starts after 12 December
  2026, thirty years after signature. The overview flags an outlook that is not current: one the
  last run could not refresh, or one for a season that has ended.
* **Consistency check.** The forecast median's mean over its days from January to April, as a share
  of the record's median for the same calendar days (the median across seasons of each month's mean,
  taken day by day, so a forecast issued mid-season is compared with the same part of the record):
  95% at Farakka and 10% at the Teesta cell at this run, which shows how far a seasonal forecast and
  the record can disagree at a single cell.

## Exports (`dashboard/data/sandhi/`)

| File | Contents |
|---|---|
| `manifest.json` | Provenance: sources with request URLs and retrieval times, files, cell checks, stream coverage and overlap, request count and weight, stale flag |
| `treaty.json` | Titles, sources with SHA-256, preamble, Articles I to XII and closing paragraphs (with readings where copies differ), Annexure I and II, formula constants, self-check, variants, 1977 excerpts |
| `timeline.json` | Dated events with sources; `derived` marks dates computed from the treaty text |
| `seasons.json` | Per season and period: availability, daily min and max, formula split and band, guarantee flags, % of schedule, percentile; per-season summaries; climatology by period; overall summary |
| `daily.json` | Daily cusecs for every lean season (for the season detail chart) |
| `trend.json` | Season means, Theil-Sen and Mann-Kendall results, reanalysis-only sensitivity, per-period slopes |
| `outlook.json` | Coming season by period (quantiles, chances under thresholds, formula at the median, percentile within the record, days taken from the record), daily ensemble quantiles, the last 120 days, consistency check |
| `teesta.json` | Treaty status with source, monthly climatology 1997 to 2025, last 12 months, latest day |

All files are real model output or official text; `"simulated": false` throughout.

## Neutrality rules

* Events and official statements only on the timeline, attributed and summarised without adjectives.
  For 2026 the timeline aims to list every official statement on the treaty found from either
  government (7 from India, 6 from Bangladesh and 5 joint events across 2024 to 2026 at this run);
  earlier years are a sample. Each entry was checked against its source on 21 October 2026.
  Both governments' statements are included where found.
* No words such as violation, shortfall or surplus. Ratios and counts are stated as such.
* Both treaty readings are shown where the copies differ; neither copy is primary.
* Tables follow the treaty's own column order (India, then Bangladesh). The two shares are drawn in
  indigo and gold: neither colour is in either flag, they stay apart for colour-blind readers, and
  neither is used for anything else. Comparisons with a reference (schedule, record median) run from
  grey (below) to blue (above), so no share colour or alarm colour carries a judgement.
* `tests/test_sandhi.py` checks the timeline for loaded words, and that 2026 carries at least four
  official statements from each government with counts no more than two apart.

## Accessibility

* Every chart has its numbers in a table beneath it (collapsed by default).
* The period charts (schedule, outlook, season detail) take keyboard focus: the left and right
  arrow keys step through the fifteen periods, and a live region reads each period's values.
* Month names sit under each month on the period axis, so all five stay readable at 375 px.
* Colours come from the design system 2.0 tokens, so every page works in the console's Night theme
  and in Day; the charts recolour on a theme switch without a reload.
* Text meets WCAG 2 AA on every surface in both themes; `node tools/qa/qa.mjs dashboard/sandhi products/sandhi.html --a11y`
  reports no serious or critical findings, and an axe run with the Day theme set does the same.

## Known limits

* All flows are modelled; the Joint Committee's daily records are not published as data.
* GloFAS routes each cell's water to one downstream cell, so it does not divert water into the
  Feeder Canal; the cell value is read as modelled flow arriving at Farakka.
* Representation of irrigation withdrawals, groundwater pumping and reservoirs upstream is not
  documented for this site. Observed studies report falling dry-season baseflow in the lower Ganges
  linked to groundwater depletion (Mukherjee and others 2018), which a model without that process
  would miss.
* The model's seasonal shape differs from the schedule's; threshold counts inherit that.
* Seasonal forecast skill for dry-season flow at Farakka has not been measured.
* On the Teesta, barrages and canal withdrawals in either country may not be represented.
* The timeline is compiled from public sources found by 21 October 2026 and is not complete.

## Pipeline and tests

```
cd pipeline
python3 -m trinetra.sandhi run              # fetch, compute, export
python3 -m trinetra.sandhi run --offline    # rebuild from pipeline/cache/sandhi/
python3 -m trinetra.sandhi run --refresh-history
python3 -m trinetra.sandhi check-formula
python3 -m trinetra.sandhi check-sources    # 3 requests; exit 1 if a treaty PDF's SHA-256 changed
cd .. && python3 -m pytest tests/test_sandhi.py -q
node tools/qa/qa.mjs dashboard/sandhi products/sandhi.html --a11y
```

A failed fetch keeps the last good export: without the reanalysis or archive nothing is written; a
missing ensemble keeps the previous `outlook.json` and marks the manifest stale. Tests run offline
against recorded responses in `tests/fixtures/sandhi/`.

## References

* Treaty texts: [FAOLEX copy](https://faolex.fao.org/docs/pdf/bi-17351.pdf); [JRC Bangladesh copy](https://lpr.adb.org/sites/default/files/resource/1610/ganges-water-sharing-treaty-1996.pdf); 1977 Agreement, [MEA Indian Treaties Database](https://www.mea.gov.in/Portal/LegalTreatiesDoc/BG77B1729.pdf).
* Ministry of External Affairs, India, Lok Sabha answers: [26 July 2024](https://www.mea.gov.in/lok-sabha.htm?dtl/38022/QUESTION_NO_833_RENEWAL_OF_GANGA_WATER_TREATY), [1 August 2025](https://www.mea.gov.in/lok-sabha.htm?dtl/39925/QUESTION_NO_2210_RENEWAL_OF_THE_GANGA_WATER_TREATY), [13 February 2026](https://www.mea.gov.in/lok-sabha.htm?dtl/40759/QUESTION_NO_2405_GANGA_WATER_SHARING_TREATY).
* [Open-Meteo Flood API](https://open-meteo.com/en/docs/flood-api); [GloFAS v4.0](https://confluence.ecmwf.int/display/CEMS/GloFAS+v4.0).
* Harrigan and others (2020), GloFAS-ERA5 operational global river discharge reanalysis, ESSD 12, 2043-2060, [doi:10.5194/essd-12-2043-2020](https://doi.org/10.5194/essd-12-2043-2020).
* Emerton and others (2018), GloFAS-Seasonal v1.0, GMD 11, 3327-3346, [doi:10.5194/gmd-11-3327-2018](https://doi.org/10.5194/gmd-11-3327-2018).
* Mukherjee, Bhanja and Wada (2018), Groundwater depletion causing reduction of baseflow triggering Ganges river summer drying, Scientific Reports 8, 12049, [doi:10.1038/s41598-018-30246-7](https://doi.org/10.1038/s41598-018-30246-7).
* Sen (1968), JASA 63, 1379-1389, [doi:10.1080/01621459.1968.10480934](https://doi.org/10.1080/01621459.1968.10480934); Mann (1945), Econometrica 13, 245-259, [doi:10.2307/1907187](https://doi.org/10.2307/1907187); Hamed and Rao (1998), J. Hydrology 204, 182-196, [doi:10.1016/S0022-1694(97)00125-X](https://doi.org/10.1016/S0022-1694(97)00125-X); Helsel and others (2020), USGS TM 4-A3, [doi:10.3133/tm4A3](https://doi.org/10.3133/tm4A3); Hyndman and Fan (1996), [doi:10.1080/00031305.1996.10473566](https://doi.org/10.1080/00031305.1996.10473566).
* Press items on the timeline are listed with their links in `timeline.json` and on the overview page.
