Case 1/1852: the generator that reported success
One duplicate key, forty-two files that are really one, and a home page without a single word — while the process that killed it exited 0
🕵️ Show the full case file → 📋 Session protocol →
Case file · Code Homicide Dept.
I changed a sentence in the source, regenerated the projection as the protocol demands, and the home page came up empty: headings with no text, buttons with no text, cards with no text. Boxes with geometry and no words. The generator had finished with exit code 0 and printed "spine generated" to the console. It did not lie about what it did: it wrote the file, and the file carried the new sentence, correctly, on line 2. What nobody checked — neither it nor I — was line 31, where it repeated a key it had already written on line 30. Fluent does not forgive that: it does not pick one, it does not ignore the other. It drops the resource. And the resource, in this framework, is all forty-two files of the language concatenated into one.
Show glossary
- projection
- An artifact generated from a declared source. The rule runs one way only: drift is fixed by RE-RUNNING the generator, never by editing the served copy (ADR-057). The whole site is built this way — which is exactly why a generator that lies poisons everything downstream, with nothing upstream able to stop it.
- contract
- In Nickel, a predicate applied to a field that rejects the file on export. Its virtue is that it cannot be ignored: either the data passes, or there is no data. And its frontier is this case file: the contract watches the data while it IS data. The instant a generator turns it into TEXT, the contract lets go.
- Fluent resource
- The loader concatenates all 42 .ftl files of a language into ONE string and parses it as a single resource. There are no namespaces. A duplicate key does not win or lose: it invalidates the resource, and the resource is the entire language.
- injective key
- A naming scheme is injective when two distinct inputs cannot produce the same name. `spine-hook-
` names by DOOR a list indexed by HOOK. Two hooks on the same door and injectivity breaks — silently, because nothing in the process demanded it.
The protocol to declare, version and verify this → ontoref.dev
The double ledger — what it cost, and what it left
The figures on the left are the body. The one on the right is the lesson. And it is worth facing the proportion that presides over this case file: one key, one thousand eight hundred and fifty-two lost. This is not an error proportional to its cause. It is a fuse wired backwards: the smallest component in the system holds the authority to switch the whole thing off, and until today nothing stopped it exercising that authority.
What the crime cost
- .ftl files the loader concatenates into ONE resource (es locale) 42
- Keys in that resource 1852
- Keys in the file the generator broke 29
- Duplicate keys needed to lose all 1852 1
- Words left on the home page 0
- Exit code of the generator that left it that way 0 · "spine generated"
- Hooks pointing at the
developerdoor (the key scheme allowed one) 2 - Line where the door LANDING already disambiguated it 93
- Line where the .ftl emission duplicated it 179
- Distance between the two, in the same file 86 lines
- Gates
just checkruns on this site 8 - …that check whether a .ftl is valid 0
- What I verified before declaring victory the file
- What I did not verify the page
What the case left behind
- The rule a generator that runs is not a generator that works
- The lock the generator REFUSES to write duplicate keys, and fails the build
- The disambiguation first-wins per door — the rule the other surface already used
- The lock's message it says the resource would be dropped whole and blank the page
- The oracle, already written "count RENDERED CARDS, not a number in the HTML" (adr-072)
- The detector, falsified a duplicate was injected and it caught it
The point of abandonment — what the table doesn't show
The point of abandonment was not at the end, it was at the beginning, and I walked straight past it. When the first regeneration finished, I did what looked rigorous: I opened the .ftl and checked the new sentence was there, correct. It was. spine-hero = Pisa firme, con paso ligero., line 2, exactly as asked. I called the step done and moved on. I verified the file, not the page — and this series’ own howto has it written, in capitals, from another case that cost plenty: “count the RENDERED CARD, not a number in the HTML; a check that measures a proxy for the capability is a check that can be green while the page is wrong”. It was in front of me. I did not read it, because I did not ask: I took an example file, inferred the shape, and got to work. The check that would have saved me cost one curl.
The suspects — the false leads
| The overlay CSS | “"It is the overlay CSS you just added." It was the first hypothesis, and the most natural one: the page LOOKED like a broken render, boxes and no text, right after the hero CSS was touched. Ruled out by looking closely: the boxes had their geometry intact — borders, radii, background. The text was not invisible. There was no text. A styling fault leaves words badly painted; this one had left none at all.” | ruled out — styling does not erase words, it paints them badly |
| The .ftl, badly written | “"The .ftl is badly written." I opened it and line 2 said exactly what it should: `spine-hero = Pisa firme, con paso ligero.` Correct. Verified. And that was my error, which is the error of this case file: I checked the artifact I cared about and found it right. The file was correct on line 2 and lethal on line 31, and I had stopped reading at line 2.” | ruled out — and here is my confession |
| "Fluent ignores the duplicate and carries on" | “"Fluent will ignore the duplicate and carry on." It is what you expect of a translation format: last wins, or first wins, and move on. Ruled out by reading the loader: `bundle.add_resource(resource).map_err(...)?`. That question mark is the whole case. A duplicate key does not degrade: it aborts. And because all forty-two files of the language are concatenated into ONE resource, it aborts the language. Fluent did nothing wrong. It failed closed, which is correct — and there was nobody listening.” | ruled out — Fluent did the right thing |
| "If the generator exits 0, the artifact is valid" | “"If the generator exits 0, the artifact is valid." GUILTY. And it is nobody's: it is the default belief. The process wrote its file, printed "spine generated", returned 0, and what it left on disk was a resource its consumer rejects whole. The generator never knew it was doing something wrong, because nothing had ever told it what doing it right was. The entire protocol is built on contracts that reject ill-typed data the instant it is defined — and that contract lets go exactly when the data becomes text. Past that frontier, everything is published on trust.” | GUILTY — and it is everyone's belief |
The weapon — gen-spine-pages.nu — a key that names the door, a loop that walks the hooks, 86 lines between them
# gen-spine-pages.nu — line 93. The door's landing page:
let hook = ($spine.evidence_hooks | where {|h| $h.door == $d.key })
let h = ($hook | first) # ← one door, one hook. Disambiguated.
# ... 86 lines further down. The .ftl emission:
let hook_keys = (
$spine.evidence_hooks
| each {|h| $"spine-hook-($h.door) = ($hl)" } # ← iterates by HOOK
| str join "\n" # names by DOOR
)
# spine.ncl declares TWO hooks with door = "developer".
# The same list. The same file. Two incompatible readings.
#
# `spine-hero = Pisa firme, con paso ligero.` ← line 2, correct
# `spine-hook-developer = ...` ← line 30
# `spine-hook-developer = ...` ← line 31, and the language is dead
And the weapon had been loaded for months with nobody touching it. The .ftl on disk was generated when only ONE hook existed; the second landed later and the file was never regenerated. The bomb was armed, waiting for the generator’s next run — which turned out to be mine, for changing a sentence. I did not create this bug. I woke it. And that is the nastiest property of a generated artifact nobody regenerates: it is not stable, it is expiring quietly, and whoever walks past next wears it.
The turn — Group by what the key names — and give the generator the exit gate it never had
# 1. The key names the DOOR, so group by door. First-wins: exactly the rule
# the landing page was applying 86 lines above.
| group-by door
| items {|door, hs| let h = ($hs | first); $"spine-hook-($door) = ..." }
# 2. And the lock, which is the only thing that stops this happening AGAIN:
# the generator validates what it is about to write, BEFORE writing it.
let dup = ($ftl | lines | ... | where n > 1 | get key)
if ($dup | is-not-empty) {
error make { msg: $"home_spine.ftl [($lang)] would carry duplicate Fluent keys —
the resource would be dropped whole, blanking the page: ($dup)" }
}
$ftl | save -f $target # ← garbage no longer reaches this line
# The contract no longer ends where the text begins.
The lock was falsified before being trusted: a duplicate was injected and it caught it. It is a small proof and it is the only one that matters, because the lesson of this case file is precisely that an unchecked mechanism is an intention. It would have been perfectly coherent to write the lock, watch it pass green over a healthy file, and ship it — which is exactly what the broken detector in case 0/318 does.
The verdict
The series asks: with ontoref, would it not have gone like this? And this case answers with a discomfort unlike the earlier ones, because here the protocol did not fail by absence: it failed at a frontier. Everything this project declares is typed, contracted and rejected the instant it is defined. nickel export will not pass a malformed record; the schema of the very case file you are reading rejects a case with a missing field; contracts fail at definition, not at runtime — and that is written, literally, on the architecture diagram on the home page. All true. And all of it stops exactly where a script takes that impeccable data and writes it out as text. Past that point there is no contract, no type, no gate: there is a string, a save -f, and an exit 0. Projection is the site’s central practice — drift is fixed by re-running the generator, never by editing the served copy — and that practice rests entirely on trusting that the generator produces something valid. Nobody was checking. What makes this a case and not an anecdote is that the failure was neither silent nor gradual: it detonated. One repeated key, in a file of twenty-nine, inside a language of one thousand eight hundred and fifty-two, and the home page lost every word. It did not degrade. It did not lose the hero. It did not show the key in brackets, the way the eighteen broken titles we found that same afternoon did. It went dark. And the process that switched it off finished cleanly, reported success, and returned zero — because nothing had ever told it what failing meant.
| A generator able to write an invalid artifact | Give it an exit gate. It now validates what it is about to write before writing it, and if it would repeat a key it ABORTS the build, saying why. A generator with no output contract is not a generator: it is a suggestion with write permissions. |
| A key scheme that is not injective over its input | Group by what the key NAMES, not by what the loop iterates. The key said "door" and the loop walked "hooks". When the name and the index disagree, the collision is not a risk: it is a matter of time. |
| The contract ends where the data becomes text | Carry the contract into the generator. Nickel rejects any ill-typed data on export — and lets go the instant a script turns that data into a string. Everything written past that frontier was published on trust. Not any more. |
| Verifying the file instead of the page | This series' own howto says it, and I did not read it: "count the RENDERED CARD, not a number in the HTML". I checked that the .ftl carried the right sentence on line 2 and called the step done. It did. And it was irrelevant: the file died on line 31. |
| A whole language in one resource, with no namespaces | This fragility cannot be repaired from here — it lives in the framework loader, where add_resource(...)? propagates the error and takes all 1852 keys with it. It is CONTAINED by the above: if nobody can emit a duplicate, the fragility stops being reachable. Containing is not curing, and the case file says so instead of hiding it. |
| A latent bug that only wakes on regeneration | The .ftl on disk had been generated when only one hook existed. The second hook landed later and the file was never regenerated: the bomb was armed, waiting for the next run. A generated artifact nobody regenerates is not stable — it is expiring quietly. |
Case law — what enforces the lesson today
- ✓The page is the publication: the witness asks what a reader would ask
adr:072/the-page-is-the-publication - ✓A gate measures the capability, not a proxy — the file was the page's proxy
adr:072/a-check-measures-the-capability-not-a-proxy
⊘Declared debt: The frontier stays open where data becomes text: whatever is written past it is published on someone's word. A contract over the EMITTED artifact — a schema for the generated resource — would settle it, not one over its source alone.
One thing remains to be said, and it is what places this case file alongside the others from that afternoon. In the same session there appeared: a diagram whose own footer claimed to be a live projection while its figures were typed by hand; eighteen routes serving a broken key’s name as their <title>, under a publishing contract that declared “witnessed 0 brackets”; and the largest Spanish surface on the site, one hundred and fifty-five files, outside every policy. All three are the same defect as this case, in its slow version: an artifact that asserts something about itself and nobody asks it for proof. The difference is that those drifted quietly for months and this one detonated in a second. And that is why this is the one that gets published: because silent drift can be argued with, and a blank home page argues with nobody.
Glosario
Sin coincidencias.