ADR-206: A Route Is Not Refused by What Its Handler Touches, and No Report Lists It Either

Status: Superseded by ADR-289 — its D1-D3 are D31-D33 there Date: September 24, 2026 Target Version: Nikaia 0.0.182 Answers: open-decisions.md’s may a route be refused by what its handler touches?, decided by the owner — option 1, and against that page’s recommendation of 3 Related: ADR-033 D4 (the touches column), ADR-108 D4 (the listing that does earn its keep), ADR-084 (a keyword is the most expensive thing a language adds), ADR-010 D1 (which reading gets the word), ADR-195 D5 (the last time this page’s framing was corrected by its answer)

1. Context

Every function in this language carries a touches column, inferred from its body and closed over what the body calls. So at the moment a program writes a handler, the compiler already knows whether that handler reaches the filesystem — transitively, which no other language’s http.server knows, because no other one has the column.

The question was what to do with that, and it named three options: refuse nothing (1), let a route declare what it may reach and refuse more (2), or list it in nikaia --trust and refuse nothing (3). The page recommended 3.

2. Decisions

D1 — No refusal. The column stays a fact.

There is no reaching: at a route, no word for it in the language, and no NK code. A handler that reads a file is a handler that reads a file.

The page’s own argument for this half stands and is not re-made here: a refusal earns its keep by refusing programs that would otherwise be wrong, and this one would mostly refuse programs that are right — Part III C.4’s cost paid for a property nobody was surprised by. The coarse version is also answered by a granularity the column does not have: touches says file(path) read, where the path is a parameter’s name and not a value.

D2 — And no listing either. nikaia --trust gains no section.

This is the half the page got wrong, and the owner’s reason is the one that decides it: in a real application almost every route reaches something. A report whose rows are nearly all non-empty tells a reviewer nothing they can act on. It is the argument this project already makes about a gate — a gate that is a fifth false alarm teaches people to ignore it (the ADR index’s own note on the citation guard) — and a report every row of which fires is the same mistake in the other tool.

A listing is not free. The page treated 3 as the cheap direction on the grounds that the listing is the data a later refusal would read, so nothing has to be undone. What it did not count is the design a useful one needs, and which nobody has done: which touches are ordinary for a handler and therefore filtered, and which a person asks for on purpose — a switch along the lines of --extended or --including http. That is a question with its own answer, and it is not a 0.1.0 target.

Option 1 costs nothing and stays extendable, which is the whole of why it is the right one now: no word is added to the language, no report grows a section nobody reads, and the column is still there the day somebody designs the filter.

D3 — The distinction the page missed: a listing of exceptions is short by construction.

ADR-108 D4’s listing works, and it is worth saying why, because it is what made 3 look free. That report lists every site that writes fs::Root::Anywhere or a literal "/" root — a way around a check. Such a list is short by construction: it holds the exceptions, and a program with none prints none. A list of what every handler touches is long by construction, and a report that is long by construction is read once.

So a report earns its keep the way a refusal does — by what it leaves out.

And this corrects a sentence this project wrote at 0.0.178. The question’s entry gained a paragraph saying fs::Root’s arrival was option 3 arriving for the filesystem before this question is answered for a route. It is not: one lists exceptions and the other would list the ordinary case, which is the difference between the two reports and not a step from one to the other. This is the second time an answer has corrected how open-decisions.md framed its own question (ADR-195 D5 was the first), and both corrections are the page working rather than failing: an entry written to be answerable is an entry whose framing can be argued with.

3. Consequences

4. What this does not decide, and what a later record would need

The listing is not refused for ever; it is refused for now, and what it would take is written down here so it does not have to be found again:

This site is open source. Improve this page.