From 33b96fdc8d03df534cddcb85e06465ca40429adc Mon Sep 17 00:00:00 2001 From: Sylvain Duchesne Date: Mon, 10 Aug 2026 14:51:17 +0200 Subject: [PATCH] =?UTF-8?q?docs(concept):=20la=20r=C3=A8gle=20interdit=20l?= =?UTF-8?q?a=20divergence,=20elle=20ne=20la=20met=20plus=20en=20balance?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit La feuille testait ce que l'APPELANT apprendrait. Le propriétaire du projet a énoncé la règle plus large : rester au plus près de NextGraph, et n'admettre aucune implémentation qui en diverge — que l'appelant s'en aperçoive ou non. L'ordre des deux questions est ce qui a manqué. Sur la fusion de `readUnion`, posée en premier, « l'appelant devra-t-il désapprendre ? » ne tranchait pas : « une entité par document » est une bonne pratique par ailleurs, alors que désapprendrait-il au juste ? Le raisonnement a piétiné des heures là-dessus. Posée en premier, « la cible fait-elle ça ? » a demandé un regard : le niveau 1 rend les sujets réels, et l'ORM du niveau 3 porte `@id` ET `@graph` sur chaque objet en fabriquant le premier quand on l'omet. Divergence, fin. « Devra-t-il désapprendre ? » reste, mais mesure la gravité d'une divergence inévitable — jamais son autorisation. Et un tell est ajouté, celui qui a produit ce défaut : une recommandation que le code impose au lieu de la guider, en rendant l'autre disposition invisible. --- .project/concepts/app-contract/_overview.md | 4 +-- .../rule_no-divergence-from-nextgraph.md | 27 +++++++++++++++++++ .../rule_would-the-caller-unlearn-it.md | 21 --------------- 3 files changed, 29 insertions(+), 23 deletions(-) create mode 100644 .project/concepts/app-contract/rule_no-divergence-from-nextgraph.md delete mode 100644 .project/concepts/app-contract/rule_would-the-caller-unlearn-it.md diff --git a/.project/concepts/app-contract/_overview.md b/.project/concepts/app-contract/_overview.md index 9f81cbc..cd28904 100644 --- a/.project/concepts/app-contract/_overview.md +++ b/.project/concepts/app-contract/_overview.md @@ -27,12 +27,12 @@ vocabulary: This library exists so an application can be **written today against the NextGraph that does not ship yet**, and keep its code when it does. Everything under this concept governs that boundary: what the package publishes, what a caller may rely on, what it must not, and what disappears at migration. -The distinguishing question, asked at every surface choice: **would this make a caller learn something it has to UNLEARN?** If yes it is a deviation, whatever it buys — see `rule_would-the-caller-unlearn-it`. +The distinguishing question, asked at every choice — published signature as much as internal mechanism: **does the target do this?** If it does it differently, we do it their way, whatever that costs. *Would a caller have to unlearn it?* comes second, and measures how bad a divergence is — never whether one is allowed. See `rule_no-divergence-from-nextgraph`. This repo is the **provider** of `contract_sdk-surface`; consuming applications live in other repos and pull it. The per-symbol ruling, with an epistemic label on every target-side claim, stays here in `docs/api-contract.md` — that is maintainer material, not the engagement. ## Read first - `contract_sdk-surface` — the engagement itself, written from the caller's point of view. -- `rule_would-the-caller-unlearn-it` — the test that decides what may be published. +- `rule_no-divergence-from-nextgraph` — the test that decides what may be built at all. - `knowledge_what-an-app-deletes-at-migration` — the two fates a published symbol can have. diff --git a/.project/concepts/app-contract/rule_no-divergence-from-nextgraph.md b/.project/concepts/app-contract/rule_no-divergence-from-nextgraph.md new file mode 100644 index 0000000..18d6e54 --- /dev/null +++ b/.project/concepts/app-contract/rule_no-divergence-from-nextgraph.md @@ -0,0 +1,27 @@ +--- +type: rule +summary: Never implement anything that diverges from NextGraph — ask "does the target do this?" first, and only then how badly a caller would be misled +--- + +# No divergence from NextGraph + +**Rule:** Never implement anything that diverges from NextGraph. At every choice — a published signature as much as an internal mechanism — the **first** question is *does the target do this?* Verified in `nextgraph-rs`, or stated by its author. If the target does it differently, we do it their way, whatever that costs. Only where the target answers nothing at all do we invent, and then deliberately, documented, and named as a bet. + +"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. + +**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. + +Earlier instances, all of the same shape: *"every document has a native inbox"*, written from general reasoning, false, and already an implementation; and a per-document inbox pointed at its owner's inbox to absorb a measured cost, emulating a many-to-one relation the target cannot express. + +**The pressure to deviate never announces itself as one.** It arrives as a cost, a latency, an ergonomic wrinkle — all real, all legitimate. That disguise is what makes it dangerous. When shape and cost conflict: keep the shape and attack the cost elsewhere (the lever is usually *who* pays and *when*). If the cost is genuinely unsolvable, say so rather than bending the model quietly. + +**How to apply.** The tells, each of which has produced a real hole here: + +- **A recommendation enforced by the code.** Ours may guide placement; it may not make the other arrangement invisible. A read path that silently normalises what it did not expect is the signature. +- **A symbol that makes the caller handle what the target will never hand it** — a document's key, an inbox address, a store id, its own identity. +- **A named exception stops being one the moment it is published.** A door documented as *"only this internal caller uses it"* is a door any application can open; the note is not a mechanism. Move it out of the published surface instead. +- **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. diff --git a/.project/concepts/app-contract/rule_would-the-caller-unlearn-it.md b/.project/concepts/app-contract/rule_would-the-caller-unlearn-it.md deleted file mode 100644 index 8231315..0000000 --- a/.project/concepts/app-contract/rule_would-the-caller-unlearn-it.md +++ /dev/null @@ -1,21 +0,0 @@ ---- -type: rule -summary: Before publishing anything, ask whether a caller would have to unlearn it — that outranks cost, latency and convenience ---- - -# Would the caller have to UNLEARN it? - -**Rule:** Before adding, keeping or changing anything on the published surface, ask: *would an application coding against this learn something it has to **unlearn** at migration?* If yes, it is a deviation — whatever it buys in cost, latency or ergonomics. Both halves are binding: the **surface** must be as close as possible to the future SDK, and the **implementation** as close as possible to what NextGraph actually plans. Where upstream's behaviour is known, it is a specification, not a reference. - -"Known" is narrow: read in `nextgraph-rs`, 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. - -**Why:** this library's entire value is that an application keeps its code. A layer that teaches a false model destroys exactly what it was built to produce, and it does so silently: nothing fails, the application simply learns a habit that has no successor. Two instances, both caught only by asking the question — *"every document has a native inbox"*, written from general reasoning, false, and already an implementation; and a per-document inbox pointed at its owner's inbox to absorb a measured cost, emulating a many-to-one relation the target cannot express and which would have made applications tag their deposits, for nothing. - -**The pressure to deviate never announces itself as one.** It arrives as a cost, a latency, an ergonomic wrinkle — all real, all legitimate. That disguise is what makes it dangerous. When shape and cost conflict: keep the shape and attack the cost elsewhere (usually the lever is *who* pays and *when*). If the cost is genuinely unsolvable, say so rather than bending the model quietly. - -**How to apply:** the tell is a symbol that makes the caller handle something the target will never hand it — a key, an inbox address, a store id, its own identity. Two secondary tells, both of which have produced real holes here: - -- **A named exception stops being one the moment it is published.** A door documented as *"only this internal caller uses it"* is a door any application can open; the note is not a mechanism. Move it out of the published surface instead. -- **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; inertia has repeatedly left published what nothing calls. - -Applies to the published surface first, but also to internal choices that shape it: an emulation whose relation the target cannot express will surface as a habit sooner or later. When a deviation is genuinely necessary, it must be **deliberate, documented and invisible to the caller** — what is forbidden is the silent one, adopted because it was convenient.