Hydrometric

Export the pooling group as a WINFAP-compatible .pt file

Brought by Chris Cameron-Hann · 21 Aug 2026, 09:12 · MIT

Progress

Shipped to dev
  1. Submitted
  2. Picked up
  3. In preview
  4. Shipped to dev

Submitted → the agent picks it up and drafts a spec and pull request → a person reviews on a preview → it ships to dev.hydrometric.io.

Request

When I have settled on a pooling group in Hydrometric I still want to open the same sites in WINFAP for a second opinion, and to hand the group to a reviewer who only has WINFAP. Add an Export button on the Pooling Group card that writes a .pt file: one row per pooled site with station number, name, record length, L-CV, L-skew, and the site weighting Hydrometric used. Same column order WINFAP expects so it imports without editing.

Agent spec

## Problem

Once an analyst has settled on a pooling group in Hydrometric they often want to open the same sites in WINFAP for a second opinion, or hand the group to a reviewer who only has WINFAP. Today the Pooling Group card offers no export, so the group has to be re-keyed by hand.

## Proposed change

Add an **Export .pt** button to the Pooling Group panel header. Clicking it downloads `pooling-group-<stationId>.pt`, a plain-text file with one row per pooled site in the order WINFAP expects, so it can be imported without editing. Columns, in the contributor's stated order:

1. Station number
2. Station name (river + name)
3. Record length (years)
4. L-CV (at-site)
5. L-skew (at-site)
6. Site weighting used by Hydrometric (the L-CV weight)

The export reflects the **effective** group: client-side exclusions are dropped, force-included stations are kept, and in read-only (original analysis) context fork edits are ignored — exactly what the table already displays.

The button is only rendered when `labsEnabled()` is true. Because the pooling panel is a client component and the flag is server-only, the flag is passed down once from the `(app)` layout via a small React context (`LabsFlagProvider` / `useLabsEnabled`), no new env var.

Nothing in the QMED chain, pooling formation or growth-curve fitting is touched — this is a formatting/download concern only.

## Files

- `packages/web/src/lib/export/winfap-pt.ts` — pure `buildWinfapPt(stations, opts)` → string (new)
- `packages/web/src/lib/export/__tests__/winfap-pt.test.ts` — unit tests (new)
- `packages/web/src/lib/export/table-export.ts` — export existing `triggerDownload` for reuse
- `packages/web/src/components/labs/labs-flag-context.tsx` — context + hook (new)
- `packages/web/src/app/(app)/layout.tsx` — wrap children in `LabsFlagProvider`
- `packages/web/src/components/analysis/pooling-panel.tsx` — Export button in `PanelHeader rightAction`

## Acceptance criteria

1. With `HYDROMETRIC_SHOW_LABS=true`, the Pooling Group panel shows an **Export .pt** button; with the flag unset the button is absent and no other behaviour changes.
2. Clicking it downloads `pooling-group-<stationId>.pt` with one row per displayed pooled site, columns in the order above, tab-separated, `\r\n` line endings, numeric values to 4 d.p.
3. Excluded stations are absent from the file; user-added stations are present; row order matches the on-screen table.
4. `buildWinfapPt` is covered by unit tests for: column order, exclusion filtering, number formatting, and neutralisation of CSV-injection-style names (reuse `neutraliseCsvCell`).
5. **Manual gate before un-drafting:** a reviewer imports the file into WINFAP 5 and confirms it loads without editing. This is the real acceptance test and cannot be automated here.

## Risks / open questions

- **The exact WINFAP `.pt` layout is not in this repo.** No fixture or doc here describes the header block, delimiter or column order WINFAP 5 actually parses. The draft implements the contributor's stated columns as tab-separated text with a single `#` comment header. If WINFAP needs a different delimiter, a version/header block, or extra columns (e.g. QMED, AREA, discordancy), the writer is a single pure function and can be corrected without touching the UI.
- Which L-CV/L-skew to emit is ambiguous: WINFAP works on deurbanised at-site L-moments for SM2025. The draft emits the at-site values the table shows (`lCV`, `lSkew`); if WINFAP expects the urban-adjusted or non-flood-adjusted values this needs a switch.
- Weighting: Hydrometric carries separate L-CV and L-skew weights; WINFAP shows one. The draft exports the L-CV weight and flags this with a TODO.

## Credit

Contributed by Chris Cameron-Hann under the MIT licence (Labs submission a1b2c3d4-0000-4000-8000-000000000001).