docs: ce que l'ORM offre pour ecrire et supprimer, et la vraie cause du useShape mort

This commit is contained in:
Sylvain Duchesne
2026-09-16 16:04:22 +02:00
parent dd874bb9c4
commit ee9319f89b
10 changed files with 719 additions and 68 deletions
@@ -1,42 +0,0 @@
---
id: T01.b
title: What the ORM offers for writing and deleting, and what the three remaining symbols are
parent: T01
depends_on: []
created: 2026-09-16
claimed_by: claude-f93872b5
claimed_at: 2026-09-16T15:25:00Z
blocked: false
---
## Description
**Reconnaissance, not implementation.** Piece 2 of the parent task rests on a fact nobody has established: upstream's shape for **deletion**. Specifying a wrapper before knowing it is how this project has invented before. This child establishes the facts and proposes the arbitrations; T01.e implements against its `## Result`.
**Read the target at the source.** `/home/sylvain/projects/nextgraph/nextgraph-rs` is a sibling checkout, at `213338f6` when this was planned — the commit the existing knowledge leaves were read at. Record in the leaf you write the commit you actually read.
**The three questions:**
1. **Deletion.** What does the target offer to delete an object? Starting facts, verified at planning: `sdk/js/orm/src/connector/` holds `getObjects.ts` and `insertObject.ts` and **no deletion file**; `sdk/js/orm/src/core.ts` exports exactly `initNg`, `ngSession`, `OrmSubscription`, `DiscreteOrmSubscription`, `insertObject`, `getObjects`, plus `getRaw` / `watch` / `effect` re-exported from `@ng-org/alien-deepsignals` and the types. Run the cascade of `.project/rules/nextgraph/no-divergence-from-nextgraph.md` — level 3 first, then level 2 (`sdk/js/lib-wasm`, `sdk/js/web`), then the engine's model — and report **which level answers**, with symbols. A shape the target *declares* and constructs nowhere is `declared-not-wired` and is the strongest thing to align on; an **absent implementation is not evidence** the target will not have one, and must not be reported as one.
2. **`getObjects` and `insertObject`.** Exact signature, parameters and return of each, precisely enough that a wrapper can be published at an identical signature without translating anything. `docs/api-contract.md` § 6 already records `getObjects` as level 3 VERIFIED (`sdk/js/orm/src/connector/getObjects.ts:23`) — check it rather than re-deriving it, and correct it if it has drifted.
3. **`OrmSubscription`, `DiscreteOrmSubscription`, `ngSession`.** What each one is, what it hands the caller, and whether an application coding against this package needs it. **Propose** an arbitration for each — publish as-is / publish under what name / do not publish — with the evidence. Do not implement. Weigh it against the rule's own tell: *a symbol that makes the caller handle what the target will never hand it* (a document's key, a store id, its own identity) is a divergence even when upstream exports it.
**Established facts — cite them, do not re-derive them from `nextgraph-rs`:** `.project/concepts/app-contract/knowledge_what-a-read-scope-is-upstream.md` (the `Scope` type and its two axes; a `graphs` entry is one document's graph; `did:ng:i` means the whole local dataset with no permission narrowing; the graph filter is literal; `OrmSubscription.readyPromise` distinguishes not-ready from ready-and-empty at level 3 while the React `useShape` adapter drops it; `OrmConfig`'s richness is real and unreachable), `knowledge_what-an-iri-says-upstream.md`, `knowledge_what-an-inbox-is-upstream.md`.
**The problem being served is a rights hole, not a missing convenience.** Today an application must import these straight from `@ng-org/orm`, and then receives **every identity's data** out of the shared wallet with nothing signalling it. The filter that closes it is `makeReadFilteredView` (`packages/polyfill/src/emulated-verifier/read-filter.ts`, annotated `kind=aligned level=1 ref=engine/verifier/src/verifier.rs:load_repo_from_read_cap`), applied per document on what comes back — where the target bounds it. For each candidate wrapper, report **how** that filter would have to apply, or why it cannot. A write path is the hard case: the filter is a *read* emulation, and `packages/polyfill/src/surface/ng-proxy.ts` already overrides `sparql_update` with an emulated write guard — read it before concluding.
**Deliverable.** A `knowledge_` leaf under `.project/concepts/app-contract/`, written by invoking `/concept document` rather than by writing the file directly. Every claim points at a **symbol**, never a line number. The arbitration proposals and every unresolved question go in this task's `## Result`, where T01.e will read them.
**Out of scope:** no edit under `packages/`, no contract edit, no version bump. If a fact you establish changes what the published contract says, name it in `## Result` and leave the edit to the child that owns it.
**Pass condition for this child** (it changes no code, so the implementation gate does not apply): `python3 ~/.claude/skills/concept/lint.py` → 0 ERROR; the leaf names the commit it was read at; every target-side claim carries a symbol reference; each of the three questions has either an answer or an explicitly stated open question.
**Resolved since planning (2026-09-16).** Add one question: verify `useShape` LIVE against the real broker. `bug_useshape-live-read-needs-a-newer-broker` may be stale — the owner reports the deployed broker generally tracks NextGraph's source, and the rename landed 2026-01-16. Do not conclude from "generally": measure it. If it works, close the bug leaf and the blocker recorded in T01.d dissolves.
## Progress
- 2026-09-16T15:25Z — claimed. Baselines taken BEFORE any edit: `bun test` 397 pass / 0 fail; `tsc -p packages/polyfill/tsconfig.json` exit 0; `tsc -p packages/polyfill/e2e/tsconfig.json` exit 0; `concept/lint.py` 0 ERROR (13 WARNING). `nextgraph-rs` read at `213338f697df15afc086c042f4bab1bffd01aa04` (clean but for one untracked engine test fixture).
- 2026-09-16T15:30Z — questions 1-3 settled by reading the target. Deletion answers at LEVEL 3 and is wired end to end (`DeepSignalSet.delete` -> `OrmPatch{op:remove}` -> `graph_orm_update` -> `Verifier::orm_frontend_update` -> `SparqlBuilder::remove_object`). Full chain recorded in the knowledge leaf.
- 2026-09-16T15:30Z — question 4: e2e baseline run started (`bun run test:e2e`) before adding anything.
## Result
@@ -0,0 +1,31 @@
---
id: T01.b
title: What the ORM offers for writing and deleting, and what the three remaining symbols are
parent: T01
depends_on: []
created: 2026-09-16
claimed_by: null
claimed_at: null
blocked: false
---
## Description
**Reconnaissance, not implementation.** Piece 2 of the parent task rests on a fact nobody has established: upstream's shape for **deletion**. Specifying a wrapper before knowing it is how this project has invented before. This child establishes the facts and proposes the arbitrations; T01.e implements against its `## Result
**Done:** reconnaissance only, no product code. Findings: `.project/concepts/app-contract/knowledge_what-the-orm-writes-and-deletes-upstream.md` (`nextgraph-rs` read at `213338f697df15afc086c042f4bab1bffd01aa04`).
1. **Deletion — LEVEL 3 answers and is fully wired.** `DeepSignalSet.delete` → `{op:"remove", path:"/<graph>|<subject>"}` → `deepPatchesToWasm` → `ng.graph_orm_update` → `Verifier::orm_frontend_update` → `SparqlBuilder::remove_object`, which deletes only the SHAPE's declared predicates. No document deletion exists at any level. Nothing to invent.
2. **`getObjects(shapeType, scope: Scope|string)`** returns a detached `deepClone` (mutations reach nothing); **`insertObject(shapeType, object)`** throws without `@graph`. `docs/api-contract.md` §6 checked — accurate, no drift.
3. **Proposals (owner decides):** publish `OrmSubscription` as-is — the read filter applies to `signalObject` exactly as `surface/use-shape.ts` already does. Do NOT publish `DiscreteOrmSubscription` (JSON-CRDT, no `@graph`, so a per-document filter has nothing to read) nor `ngSession` (hands the caller the SDK handle and the session — the rule's own tell).
4. **`useShape` live: MEASURED, still broken, and the bug leaf's mechanism was FALSE.** The name resolves; the deployed `orm_start_graph` takes `config` BEFORE the callback, so the published five-argument client lands its callback there. The deployment is AHEAD of the pinned `@ng-org/*`, not behind. Bug leaf rewritten, still open/major — T01.d's blocker STANDS.
**Open question for the owner:** the consumer reports `ngSet.delete()` "works again", yet no ORM subscription can start against this deployment. Which broker were they on?
**Surprises:** ORM writes go through `graph_orm_update`, NOT `ng.sparql_update` — so `surface/ng-proxy.ts`'s emulated write guard never sees an ORM add or delete (VERIFIED). `@ng-org/orm` is an optional peer with no lock entry; only the new runner imports it.
**Files:** `packages/polyfill/e2e/orm-live-read.ts`, `packages/polyfill/e2e/orm-live-entry.ts` (new); `.project/concepts/app-contract/{knowledge_what-the-orm-writes-and-deletes-upstream.md,bug_useshape-live-read-needs-a-newer-broker.md,_overview.md}`; `.project/concepts/e2e-harness/{_overview.md,knowledge_what-each-suite-judges.md}`; `_debt.md` reconciled and deleted.
**Gate:** `bun test` → 397 pass, 0 fail. `tsc -p packages/polyfill/tsconfig.json` → exit 0. `tsc -p packages/polyfill/e2e/tsconfig.json` → exit 0. `concept/lint.py` → 0 ERROR. e2e: `══ Polyfill e2e summary: 42 passed, 0 failed, 42 total — batch 4.0 min, slowest cold sync 30s ══` (unchanged) and `══ ORM live-read summary: 2 passed, 4 failed, 6 declared — run 1.7 min ══` (the new measurement; non-zero exit is its verdict on the deployment).
**Follow-ups:** add `@ng-org/orm` to `packages/polyfill` devDependencies so the new runner survives a clean `bun install`; `docs/api-contract.md` says nothing about deletion or `graph_orm_update` — T01.e's call.
@@ -44,3 +44,4 @@ The polyfill's published surface has drifted from the SDK's: invented names, inv
## Learnings digest
- T01.a — the taxonomy gains `announced-not-written`: its `ref` points at the `.md` in THIS repo where the statement is on record, never a source file, plus `announced-by=<who>@<date>`. Upstream citations now resolve-check only when the sibling checkout is present; announced records always. Two latent defects fell out: the test's closed set was missing `reserved-name`, and the rule said "five kinds" over six rows. All 92 existing refs already resolved.
- T01.b — deletion is answered at level 3 and fully wired: `DeepSignalSet.delete` -> a remove patch -> `graph_orm_update` -> `SparqlBuilder::remove_object`, which removes the SHAPE's predicates, not the subject; no document deletion exists at any level. `getObjects` returns a DETACHED clone; `insertObject` throws without `@graph`. Proposals: publish `OrmSubscription` as-is (the read filter applies to `signalObject`), publish NEITHER `DiscreteOrmSubscription` (JSON-CRDT, no `@graph`, nothing for a per-document filter to read) NOR `ngSession` (hands out the SDK handle and the session). MEASURED: `useShape` is still broken live, and the bug leaf's mechanism was FALSE - the deployment is AHEAD of our pinned `@ng-org/*`, its `orm_start_graph` takes `config` BEFORE the callback, so the five-argument client lands its callback in the wrong slot. T01.d's blocker STANDS. Also VERIFIED: ORM writes bypass `sparql_update` entirely, so the emulated write guard never sees an ORM add or delete.