REWT Rivers of England and Wales, Temporally

The rules

Every general rule this project works to, in the order they apply to the network. This page is generated from conf/rules.yml — the file the build itself reads — so it cannot say the build does something the build does not do. Nothing here is typed by hand.

It is not in the navigation, deliberately. The rules are working material rather than an argument, and the pages that make the argument — Methodology above all — link to individual rules by identifier. This is where those links land.

Why there is a list at all

A defect found at one place is repaired everywhere or it is not repaired. The temptation with a network of this size is the targeted fix: the junction is wrong, so mend the junction. That produces a network which is correct at every place anybody has looked and unexamined everywhere else, and it leaves no way to tell the two apart afterwards.

So an observation becomes a rule, stated generally, applied across the network, and recorded with the reasoning that produced it. What was observed stays in rules/ in the words of whoever observed it, and is never edited to match the rule it produced — the intake and the conclusion are different things and each is worth keeping.

A rule carries no threshold. Where a rule needs a number it names the parameter by its path into conf/params.yml and the number lives there, once. A threshold written in two files is a threshold that will eventually disagree with itself, which is the same argument that makes this page a projection rather than a copy.

A rule holds no judgement about a particular river. Those live in data/curated/, as diffable files with a reason in words and evidence naming a place, a source, or a person and the date they looked. What is here is the general rule such a judgement is an instance of.

What the statuses mean

status says what is true of the build now, not what anybody intends. A rule is implemented only when the module that applies it is named and the audit can show where. Of the 13 rules below, 0 are implemented.

proposed
raised, not yet agreed. Carries no obligation on the build.
accepted
agreed as a rule; not yet applied. implements is empty.
implemented
applied by the module named in implements.
rejected
considered and declined. why says so, and it stays in the file.
superseded
replaced by the rule named in superseded_by.

And kind says what applying a rule would change:

specification
changes what a thing IS — the definition the build works from.
detection
finds candidates. Proposes; never edits the network.
repair
changes the network. Every one of these is auditable and reversible.
reporting
changes what is said about the network, not the network.
presentation
changes how the network is shown. tools/viewer/ and docs/viewer/.
capability
makes the network answer a question it could always have answered, and changes neither the network nor what is said about it. A capability adds no geometry and retires none; if one needs to, it is a repair wearing the wrong label.

The order is the sequence, not a ranking

Rules apply to the network in the order below, which is not the order they were raised and is not a priority. R-01 redefines the sea before any connector is invented, because a connector built against the wrong sea is wrong in a way no later rule can detect. Where the order does not matter the numbers still run consecutively.

The rules

R-01 · The sea is the Mean High Water line, not a set of survey nodes.

specification · proposed · applies at the sea stage · stands on 1 named row of evidence

Define tidal water by the Ordnance Survey Mean High Water line. A watercourse that crosses it is truncated at the crossing and connected there; a watercourse lying entirely below it is dropped from the network. This is settled before any synthetic connector is added, because a connector built against the wrong sea cannot be recognised as wrong afterwards.

Why. The present sea network is a specification, not a measurement, and a great many sea entries lie a long way from it. A distance-from-a-node test decides the question at exactly the estuaries where the answer matters — which is the objection PLAN.md §5 makes to deciding it from a coastline, and it applies to the current network for the same reason.

Waits on. MEASURED, AND THE RULE IS UNDER-SPECIFIED FOR A QUARTER OF ITS OWN CASES. truncate at the crossing presumes there is one. Of 6,926 in-scope links that meet the line: 5,071 cross once; 1,274 (18.4%) yield MORE THAN ONE landward piece when clipped, up to eight, weaving in and out along estuary edges; and 581 clip to ZERO landward length — they graze the boundary rather than crossing it, so a classifier keyed on crosses is answering a question they do not pose. A further 1,497 leave a landward remnant under one metre and 2,423 are more than 99% landward, where a cut creates a node and a retirement and changes nothing. SO CLASSIFY BY THE CLIPPED LANDWARD FRACTION, not by a topological predicate. contains_properly is false for a wholly-landward link that touches the boundary, and intersects is true for one that merely grazes it; the fraction of a link’s length inside the land polygon is the quantity the rule is actually about, and it handles the grazers, the weavers and the sub-metre remnants uniformly. Two thresholds are then needed and neither is chosen yet: below what fraction a link is treated as wholly seaward, and below what landward length a cut is not worth making. Both belong in conf/params.yml. MARK THEM OUT OF SCOPE, NOT RETIRED. An attempt to retire the wholly-seaward links failed test_nothing_was_deleted_to_correct_it on 7,474 links naming nothing that superseded them, and the guard was right: a retirement here means a correction replaced this, and nothing replaces these. PLAN.md §4.1 already holds the mechanism — Nothing is deleted. Out-of-scope links are flagged, not dropped — and a link below high water is not a corrected link, it is a link that is not part of this network. THE TWO HALVES ARE ONE CHANGE AND CANNOT BE LANDED SEPARATELY. Retiring the seaward reaches without cutting the crossers removed the outlets the crawl seeds from and supplied no replacements: reaches neither went from 3,894 km (3.7%) to 21,259 km (20.7%). Not a broken network — a deleted sea. The truncation point IS the new terminus, so it must exist before the old one is taken away. THE NAME IS NOT THE SAME NAME. OS calls this High Water Mark, the administrative coastline, not Mean High Water, a tidal datum. The products carrying MHW/MHWS as a named tidal feature are VectorMap District and MasterMap, neither registered. HWM is the right line and costs nothing, but the substitution goes on the record rather than being assumed away. DEDUPE BEFORE MEASURING ANYTHING. The layer has 32,850 rows and 13,002 geometries: each segment repeats once per administrative file that bounds on it, mean 2.5x, across 380 File_Name values. Global_Link_ID is the exact key (13,002 distinct, confirmed independently by hashing the geometry blobs). Any coastline length measured without dedupe is ~2.5x. The row count is Scotland-dominated — Highlands and Islands alone is 6,443 rows. England and Wales is 6,191 segments and 16,057 km against 13,002 and 34,792 km for GB. Do not read a GB figure as a scope figure.

Supersedes.

Raised 2026-09-03 by stephen, from rules/0001.md.

R-02 · A basin may not contain the sea.

specification · proposed · applies at the basins stage · stands on 5 named rows of evidence

Remove tidal water from the surface before delineating catchments, and hold no basin whose polygon contains the sea network. Where watercourses share an estuary they do not share a catchment, and a delineation that says otherwise is to be reported as a defect rather than carried into scope.

Why. Measured on the v0.2.0-alpha build: 110 of 334 in-scope basins have polygons that intersect this project’s own sea network and 25 hold 10 km or more of it, measured clipped to the polygon — an unclipped count says 51 and is wrong. Two — labelled River Earn and River Nith — are 4,176.3 and 9,655.0 km2 against real catchments near 800 and 1,230, contain open sea off Arbroath, the mid Firth of Forth, the mid Solway and the mid Firth of Clyde, and pull 5,411.5 km of network into scope — the whole network in the two basins, of which 46.1% and 49.9% of the AREA is Scottish and the length split is unmeasured — carrying 118 of the 1,303 declared defects. It is not a Scottish fault: River Deben contains sea off its own mouth and River Lea contains the mid Thames estuary. The build does not miss them; it files them where they look right — published/audit/basins.json reports the Nith and the Earn as cross_border_basin findings beside the Tweed and the Esk, which are the legitimate cross-border cases D-024 names, so an amalgam is reported as an instance of a category working as designed. 12 in-scope basins enclose more sea network than river network; 99 enclose sea at all, 2,896.9 km clipped. This is PLAN.md §4.1’s shared-estuary trap occurring in the topographic delineation rather than in the component test the warning was written about. There are two failures here and only one shows in the scope figures. The Nith and the Earn drag Scottish network into scope, which is how they were found. The rewt:basin-unanchored/ coalescences do not: “River Aire” is 11,873.5 km2 with outlet_node NULL and an England-and-Wales area of the whole 11,873.5, so no scope figure can see it and nothing else looks. That is the more dangerous kind.

Waits on. R-01 first. What the sea IS is R-01’s question; this rule only says a basin may not contain it, and cannot be tested until the answer is stable.

Supersedes.

Raised 2026-09-03 by rewt-46, from rules/WORK-PACKAGE-01.md.

R-03 · A sea entry within reach of the sea network is connected to it.

repair · proposed · applies at the sea stage · stands on 2 named rows of evidence

A terminus classified as a sea entry that lies within the connection tolerance of a sea network node is connected to that node. A sea entry recorded as blocked is offered a longer tolerance than an unobstructed one, and the two tolerances are separate parameters because they answer different questions.

Why. Sea entries that plainly reach the sea are being left unattached, which strands everything above them and puts the length into the defect list under a cause it does not have.

Waits on. Both parameters to be added to conf/params.yml. Neither exists; this rule states no number and must not. R-01 first: if the sea becomes the Mean High Water line, some of these entries are truncations rather than connections.

Supersedes.

Raised 2026-09-03 by stephen, from rules/0001.md.

R-04 · Every crossing that does not join is marked as one.

detection · proposed · applies at the repair stage · stands on 2 named rows of evidence

Where two watercourses cross without sharing a node, the crossing is recorded as a non-join, whatever its provenance. The record is complete or it is not a record: a crossing the build does not mark cannot be adjudicated, and its absence is indistinguishable from a decision that it joins.

Why. Crossings that do not join are being missed, and both a survey link and a synthetic one are among the misses — so the gap is in the detection and not in one class of geometry.

Waits on. A completeness check, not a sample. The question is ‘is there anything else’, and a truncated listing cannot answer it (TEAM.md).

Supersedes.

Raised 2026-09-03 by stephen, from rules/0001.md.

R-05 · Corroboration of a non-join must corroborate that non-join.

detection · proposed · applies at the repair stage · stands on 3 named rows of evidence

A feature may corroborate a non-join only where it explains the crossing at that place. Proximity is not corroboration. Where no feature explains it, the non-join stays uncorroborated and remains a candidate; where a feature is merely nearby, it is not cited at all.

Why. The failure runs in both directions from one cause, which is why this is one rule and not two. A node at E 396919.51 N 161634.75 that should be a junction is marked a corroborated non-join on the strength of a Canal and River Trust culvert that happens to be near it — so a real connection is barred by evidence that was never about it. In the other direction an uncorroborated non-join is barring a connection that nothing argues against. A corroborated verdict is the strongest thing this build says about a place, and it is being reached by distance.

Waits on. A decision entry on what may corroborate what, before any code. The present behaviour is a definite answer that was never measured (D-077).

Supersedes.

Raised 2026-09-03 by stephen, from rules/0001.md.

R-06 · An aligned dead end near an unattached end is a candidate join.

repair · proposed · applies at the repair stage · stands on 1 named row of evidence

Where extending a link beyond a dead-end node along its own bearing passes within the search distance of the unattached end of another link, and the two bearings agree within the angular tolerance, the pair is a candidate for connection. It is a candidate and not a connection: the rule proposes, and adjudication records the judgement in data/curated/.

Why. Two channels that continue each other’s line and stop a few metres apart are the commonest shape of a real gap, and the pair is recognisable from geometry alone without appeal to elevation.

Waits on. Both parameters to be added to conf/params.yml. Neither exists. Before believing the selector, confirm it excludes something (D-070): print what it matches against the population of dead ends, not a percentage.

Supersedes.

Raised 2026-09-03 by stephen, from rules/0001.md.

R-07 · A canal dead end with two inflows is suspect, and is not reversed on that account.

detection · proposed · applies at the repair stage · stands on 1 named row of evidence

A dead-end node on a canal reached by two inflows and no outflow is reported as a candidate direction fault, and more strongly where a terrain screen disagrees with one of the inflows. It is reported and not acted on. No canal link is reversed because reversing it improves a total.

Why. Water leaves a canal through locks, weirs and feeders the survey does not draw, so two inflows meeting at a dead end usually means one is drawn backwards. But PLAN.md §5 measures the trap on the other side: a canal is level by construction, 82.3% of canal links fall inside Terrain 50’s own error bar, and the Monmouthshire and Brecon falls 0.00 m over 3.91 km on a 1 m surface. Neither direction is defensible from the terrain, so the screen may rank a candidate and may never settle one. A canal may also legitimately drain two ways from a summit pound, which is the mode: both case and is not a fault at all.

Waits on. The rule must be written so it cannot reverse a canal link. PLAN.md §5 says the remedy for flat water is a connector at the structure, never a reversal (D-011).

Supersedes.

Raised 2026-09-03 by stephen, from rules/0001.md.

R-08 · A gap through a water body is closed by the polygon’s medial axis.

repair · proposed · applies at the water stage · stands on 3 named rows of evidence

Where the survey gives no route through a lake, reservoir or broad, the route is the medial axis of the water-body polygon, skeletonised from OS OpenMap – Local. Where the survey gives a schematic straight line across standing water, the line is kept and flagged as a routing device rather than a channel. Skeletonise only where it earns its place, and never through tidal water, where a medial axis is a line down the middle of a bay.

Why. This is PLAN.md §5’s specification and the question is whether the build still does it. Gaps of exactly this shape are open in the current network. It is recorded here as a rule because the answer “we used to” is the one that needs finding out.

Waits on. First establish what rewt/stages/water.py does now. This is a question about the present build, not a proposal, and the answer may be that the rule is already implemented and failing at these places for another reason.

Supersedes.

Raised 2026-09-03 by stephen, from rules/0001.md.

R-09 · Elevation may rank a direction candidate and may never settle one.

detection · accepted · applies at the screen stage · no evidence rows named

A terrain surface is a screen. It orders candidates for adjudication and it does not flip a link. No direction correction is recorded as made on terrain evidence where the fall lies inside the surface’s own error bar, and no sampler infers coverage from the absence of nodata.

Why. PLAN.md §5 measured both halves. Terrain 50 is mute on 24.3% of links and on 82.3% of canals, and the English LiDAR service returns zeros over Wales rather than nodata — a sampler without an explicit coverage test computes confident nonsense for the whole of Wales and it does not look like an error. A proposal to lean harder on a higher-resolution surface does not change either fact, and this rule is what any such proposal has to survive.

Waits on. The rule is accepted on PLAN.md §5’s own measurements. What was DECLINED is the proposal that raised it — see declined below — and the rule stands either way.

Supersedes.

Raised 2026-09-03 by stephen, from rules/0001.md.

presentation · proposed · applies at the viewer stage · stands on 1 named row of evidence

Clicking a node opens one overlay listing every attached link, expandable, rather than a node overlay and a single link overlay that can collide. The count of links shown equals the count attached.

Why. A viewer that shows one of two attached links tells the reader the node has one. Overlays that collide lose whichever is underneath, silently.

Waits on.

Supersedes.

Raised 2026-09-03 by stephen, from rules/0001.md.

R-11 · A feature showing a popup is visibly the feature that was clicked.

presentation · proposed · applies at the viewer stage · no evidence rows named

A link or node with an open popup carries a highlight, and carries one on hover above a practical zoom threshold.

Why. Without it the reader cannot tell which of several nearby features the panel is describing, which is the situation at exactly the crossings and dead ends the map exists to adjudicate.

Waits on.

Supersedes.

Raised 2026-09-03 by stephen, from rules/0001.md.

R-12 · A person can set and unset the non-join verdict on a node, in the tracer.

repair · proposed · applies at the tracer stage · no evidence rows named

Above a threshold zoom the tracer draws a marker on every node that can carry a non-join verdict, and a person can toggle that verdict on or off at the place. The toggle authors the marking; it does not merely show or hide it. Every change is a curated judgement and is recorded as one, with a reason in words and evidence, in data/curated/.

Why. R-04 says the marking must be complete and R-05 says the corroboration must corroborate. Neither can be settled by a rule alone, because the cases that defeat both are cases where a person has to look — a culvert that happens to lie near a node is not evidence about that node, and no threshold distinguishes it from one that is. So the build proposes and a person adjudicates, which is the arrangement PLAN.md already uses for connectors, reversals and junctions.

Waits on. This is NOT a layer switch, and it was written as one. An earlier version of this rule said ‘the viewer offers a control that shows and hides crossings marked as non-joins’, which docs/viewer already does — a refused_crossings layer, on by default, all 425, three icons splitting corroborated from uncorroborated-in-Trust-country from outside. Stephen was almost certainly looking at that layer when he wrote the note, and his sentence opens ‘Not all crossings that do not join are being MARKED as such’. rewt-46 declined to build the small wrong thing and asked instead, and Stephen confirmed on 2026-09-03 that the tool is for the tracer and not the viewer. tracer.crossing_marker_min_zoom to be added to conf/params.yml. A marker on every eligible node is unreadable at national zoom and is the point of the threshold; the number is a judgement about legibility and belongs in configuration. R-04 supplies the candidates. The tool is worth little until the marking is complete, and R-04 is what makes it complete. Decide what a node that CAN carry the verdict is, before drawing anything. If that set is every node the marker is useless; if it is only nodes the build already marked, the tool cannot add the ones R-04 says are missing, which is the whole purpose. Confirm the selector excludes something (D-070).

Supersedes.

Raised 2026-09-03 by stephen, from rules/0001.md.

R-13 · A route between two chosen points, through the network and around the coast.

capability · proposed · applies at the router stage · no evidence rows named

Given two points, return the shortest route through the river network and the coastal grid together, as a chain of links and cells. Distance first; cost and season deferred with the weights they depend on.

Why. Stephen, on what Leigh Shaw-Taylor and Max Satchell should see the first time they open the viewer: its routing potential. The traversable network is the deliverable and a reachability percentage does not show what it is FOR — a route between two places does, in one gesture. It also tests the network harder than the audit does: a route that cannot be found is a defect AT A PLACE, which is what PLAN.md §6 asks for and what a national share conceals.

Waits on. THE ROUTING GRAPH IS NOT THE AUDIT GRAPH, and this is the part that is not obvious. Stage 1 stores every link oriented so from_node is upstream and to_node downstream, and the whole audit asks a DIRECTED question: can the water get out. A boat goes upstream. So a route needs the UNDIRECTED network joined to the coastal grid — a second graph over the same geometry, cheap to build and easy to conflate. Do not reuse edge as it stands and do not re-orient it: the direction is the audit’s subject. R-01 first, for the join. A route leaves the river at a terminus and enters the grid at a cell centre, and R-01 moves where the termini are: 4,940 links wholly seaward of high water are retired by it, so a route through them today is a route through geometry that is about to leave. The H3 grid and the terminus-to-centre links, which are the router’s current work. Weights are deferred by rules/H3.md and this rule inherits that. Shortest BY DISTANCE is a complete and useful answer, and it stays honest only as long as nothing calls it fastest, cheapest or likeliest. SAY WHAT IT CANNOT ANSWER. Stage 1 makes no historical claim, so this is a route through the MODERN network and the coast as it is now. Shown to two historians without that sentence attached, it invites precisely the reading the whole project is built to prevent.

Supersedes.

Raised 2026-09-03 by stephen, from conversation, relayed by rewt-e8.

What this page does not carry

Each rule names the rows it was raised from, because an aggregate is worth nothing until it can name one, and a rule whose examples no longer resolve has either been fixed or was never right. Those identifiers are in conf/rules.yml rather than here: a reader cannot resolve rewt:basin/54261d5c6c and the count is the part that bears on whether to believe the rule. The same goes for the modules a rule is implemented by and the parameters it reads.

conf/rules.yml also carries 1 instance — a specific feature someone has claimed is wrong, with the rules it is an instance of. An instance is not a rule and does not become one by being reported; it is the raw material a rule is generalised from.