docs: une définition est un fait, son absence de câblage est un silence

J'ai classé readLinks comme notre invention parce qu'AddLinkV0 n'est construit
nulle part en amont. C'était raisonner depuis l'absence — ce que cette règle
interdit, et que je citais dans la même session. La bonne lecture était que la
cible a DÉFINI la forme et ne l'a pas câblée : nous l'implémentons, ce qui est
le meilleur alignement disponible.

Les deux arrivent ensemble et l'absence est la plus bruyante, ce qui lui fait
gagner des arguments qu'elle devrait perdre. D'où la règle : lire la
déclaration, et laisser le silence autour ne rien dire.

Elle gagne sa place dans la taxonomie, parce que la confondre avec « invention »
fait croire qu'on est plus loin de la cible qu'on ne l'est — et un lecteur
ultérieur, voyant « notre idée », se sentira libre de la changer.

Cinq catégories désormais, et une provenance à côté de chaque symbole plutôt que
dans un document qui dérive : le niveau qui répond, la référence amont, la
catégorie. Un test l'exige sur la surface publiée. Il ne peut pas vérifier que
la citation est vraie, et c'est acceptable : une citation se réfute en quelques
secondes, une prose non — docs/api-contract.md affirmait un passthrough « 1:1 »
faux sur deux points et un décompte de membres erroné, sans que rien n'oblige
personne à aller voir.

La classification vaut aussi pour le code non publié qui PRÉTEND ressembler à la
cible : la faute qui motive tout ceci a été commise sur une fonction interne, et
n'être pas exportée n'a protégé personne.
This commit is contained in:
Sylvain Duchesne
2026-08-16 22:35:59 +02:00
parent 98ee511d3a
commit 6138d831da
@@ -9,6 +9,10 @@ summary: Never implement anything that diverges from NextGraph — ask "does the
"Known" is narrow: read at the source, or stated by its author. Never inferred from what an npm package happens to expose, and **never inferred from an absent implementation***"the engine does not do X"* says nothing about whether the target will. "Known" is narrow: read at the source, or stated by its author. Never inferred from what an npm package happens to expose, and **never inferred from an absent implementation***"the engine does not do X"* says nothing about whether the target will.
**A definition is a fact; its being unwired is an absence.** The two arrive together and the absence is the louder one, which is how it wins arguments it should lose. A type the target has declared but nothing constructs yet is the *strongest* thing to align on — implementing it is not inventing, it is realising a shape the target wrote down. Read the declaration and let the silence around it say nothing.
That distinction has its own place in the taxonomy below, because collapsing it into "invention" makes us believe we are further from the target than we are — and then a later reader, seeing "our own idea", feels free to change it.
**The second question measures gravity, not permission:** *would an application coding against this have to unlearn it?* Use it to rank what to fix first, and to judge an unavoidable divergence. Never use it to authorise one. **The second question measures gravity, not permission:** *would an application coding against this have to unlearn it?* Use it to rank what to fix first, and to judge an unavoidable divergence. Never use it to authorise one.
**Why the order matters — it is the failure this rule was rewritten for.** `readUnion` folded every triple of a document into one bag keyed by the document, so two entities written under two subjects came back merged and one written under another subject came back relabelled, silently. Asked the second question first, the answer was ambiguous: *one entity per document* is good placement practice anyway, so what exactly would a caller unlearn? The reasoning stalled there for hours. Asked the first question, it took one look: level 1 returns real subjects, and level 3's ORM carries `@id` **and** `@graph` on every object and generates the former when omitted — several objects per graph is the designed case. Divergence, done, no debate about perception. **Why the order matters — it is the failure this rule was rewritten for.** `readUnion` folded every triple of a document into one bag keyed by the document, so two entities written under two subjects came back merged and one written under another subject came back relabelled, silently. Asked the second question first, the answer was ambiguous: *one entity per document* is good placement practice anyway, so what exactly would a caller unlearn? The reasoning stalled there for hours. Asked the first question, it took one look: level 1 returns real subjects, and level 3's ORM carries `@id` **and** `@graph` on every object and generates the former when omitted — several objects per graph is the designed case. Divergence, done, no debate about perception.
@@ -25,3 +29,19 @@ Earlier instances, all of the same shape: *"every document has a native inbox"*,
- **A symbol kept because it was already there is not a decision.** At every surface change, re-ask whether an application still needs each neighbouring symbol. - **A symbol kept because it was already there is not a decision.** At every surface change, re-ask whether an application still needs each neighbouring symbol.
When a divergence is genuinely unavoidable it must be **deliberate, documented and invisible to the caller**. What is forbidden is the silent one, adopted because it was convenient. When a divergence is genuinely unavoidable it must be **deliberate, documented and invisible to the caller**. What is forbidden is the silent one, adopted because it was convenient.
## Every symbol says where it comes from
Answering *does the target do this?* once, in conversation, does not survive the week. So each symbol carries its own provenance, beside the code rather than in a document that drifts: the **level** that answers (1 the engine, 2 the binding, 3 the ORM), the upstream `path:symbol` it aligns on, and one of five kinds.
| kind | meaning |
|---|---|
| `passthrough` | upstream's own symbol, same name and shape |
| `aligned` | our name or ergonomics, upstream's semantics and model |
| `declared-not-wired` | upstream **defines** the shape; nothing constructs it yet; we implement it |
| `invention` | nothing at any level answers — a deliberate, documented bet |
| `divergent` | upstream does this, and we do it differently |
A test requires the annotation on every published symbol; it cannot check that the citation is *true*, and that is fine — a citation is refutable by a reader in seconds, prose is not. The classification also applies to unpublished code that **claims to resemble the target**: the mistake this section exists for was made on an internal function, and being unexported protected nobody.
**Why this exists.** A maintainer read that `AddLinkV0` is defined upstream but constructed nowhere, and reported our implementation of it as our own invention — reasoning from the absence while quoting, in the same session, the rule against exactly that. The correct reading was `declared-not-wired`, which is the best alignment available. Prose said otherwise elsewhere too: `docs/api-contract.md` claimed `docs.*` was a 1:1 passthrough while two of its signatures diverged, and stated a member count that was simply wrong. Both errors are the same one — a claim about the target that nothing forced anyone to check.