chore: les paquets ng-org rattrapent le deploiement, et la lecture vive demarre
This commit is contained in:
@@ -1,46 +0,0 @@
|
||||
---
|
||||
type: bug
|
||||
summary: A live read through the published `useShape` never starts against the deployed broker — its `orm_start_graph` expects a `config` argument the published `@ng-org/orm` does not send, so the subscription stays empty and pending for ever with one console error as the only trace
|
||||
severity: major
|
||||
opened: 2026-08-17
|
||||
last_checked: 2026-09-16
|
||||
---
|
||||
|
||||
# `useShape` opens no live read against the deployed broker
|
||||
|
||||
The published `useShape` is a passthrough to `@ng-org/orm`'s hook (`packages/polyfill/src/surface/use-shape.ts`) — this package adds the read filter and forwards. Against the broker deployed today, the subscription that hook opens **never starts**: its set stays empty, its readiness never settles, no engine update ever arrives, and the only trace is one `console.error`. An application sees "this scope holds nothing", indefinitely, which is the exact confusion the rest of the read surface is built to prevent.
|
||||
|
||||
The cause is upstream, in the disagreement between the deployed broker and the published client packages. **Nothing in this package can work around it.**
|
||||
|
||||
## The mechanism — an ARGUMENT, not a name
|
||||
|
||||
MEASURED 2026-09-16 against the real broker by `packages/polyfill/e2e/orm-live-read.ts`, which drives the published `useShape` over the real `@ng-org/orm`. The engine answers:
|
||||
|
||||
```
|
||||
Deserialization error of config Error: invalid type: JsValue(Function), expected any valid JSON value
|
||||
```
|
||||
|
||||
A live ORM read is one call and it is streamed: `OrmSubscription`'s constructor issues `ng.orm_start_graph(graphs, subjects, shapeType, session_id, onBackendMessage)` — **five** arguments, callback last — and the engine pushes into that callback rather than answering once. What comes back instead names a parameter, `config`, that the client never sent.
|
||||
|
||||
The same measurement establishes where that parameter sits, by calling the SDK member directly with `{}` in position 4: **six arguments are ACCEPTED**, and the engine then streams back. So the deployed signature is `(graph_scope, subject_scope, shapeType, session_id, config, callback)`, and the published client's five-argument call lands its **callback** exactly where `config` is expected — which is precisely what the error describes.
|
||||
|
||||
**The direction is the opposite of what this leaf said until 2026-09-16.** The earlier reading — a broker built before `orm_start` was renamed `orm_start_graph` (upstream `d5ecd0fd`, 2026-01-16) — is **refuted**: the name resolves, and the call fails *inside* the function, on an argument. That arity exists in **no** commit of `nextgraph-rs` at `213338f6`: the one window that carried a `config` at all (`c38aa371` 2026-04-12 → `7c8abd0a` 2026-04-21, *"rm unused config param"*) put it **last**, after the callback, where a five-argument call would leave it merely absent. The deployment is therefore built from a source **ahead of** that checkout, and it is the published `@ng-org/orm@0.1.2-alpha.19` / `@ng-org/web@0.1.2-alpha.13` this package pins that are behind it.
|
||||
|
||||
**Why it fails in silence:** `orm_start_graph` is awaited inside a `try` whose `catch` is `console.error(e)`, and `resolveReady()` is called on one path only — the arrival of initial data (`GraphOrmSubscription.ts:handleInitialResponse`). A rejected start leaves the readiness promise pending for ever and the set permanently empty. This is the same upstream property already recorded in `docs/api-contract.md` (a failed read *is* an eternal pending upstream), reached here through a different door.
|
||||
|
||||
## What is affected, and what is not
|
||||
|
||||
- **Affected:** the published `useShape`, and only it. By the same argument, `insertObject` and `getObjects` — both built on `OrmSubscription` — cannot start either, so **no ORM write or deletion can be exercised against this deployment**.
|
||||
- **Not affected:** `watchShape`, `subscribeDoc`, `subscribeDocs`, `readUnion`. These open one `doc_subscribe` per document, and `doc_subscribe: 2` has been in the method tables continuously under a stable name and arity. `watchShape` is built on that path, not on the ORM subscription — it is a working reactive read today.
|
||||
|
||||
That is why the library's own suites and the reference application do not show it: nothing in this package calls `useShape` internally.
|
||||
|
||||
## What to do
|
||||
|
||||
- **Do not build on live `useShape`.** Use `watchShape` for a reactive read, `readUnion` for a one-shot one. This is stated in the contract's `## Non-guarantees` so a consumer meets it without reading this leaf.
|
||||
- **The fix is not ours, and it is an ALIGNMENT, not an upgrade in one direction.** Either the deployment rolls to a build whose `orm_start_graph` matches the published packages, or `@ng-org/orm` / `@ng-org/web` are republished against the deployed signature. Report it; do not patch around it, and do not pass a `config` argument from here — a client that guesses an unpublished arity breaks the day the two ends agree.
|
||||
- **To check whether it is closed:** `bun run e2e/orm-live-read.ts` from `packages/polyfill` (it needs `@ng-org/orm`, an optional peer `bun install` does not fetch). It exits non-zero while this is open and names what the engine expected. Green means the two ends agree and this leaf can go.
|
||||
|
||||
## The shape worth remembering
|
||||
|
||||
A method **signature** — name and arity together — duplicated across three independently-deployed artifacts, with a **silent** failure when they disagree, is a defect no single repository's tests can catch. Pinning the client packages does not help: the broker-side half (`@ng-org/api-web`, `private: true`, shipped only inside the broker build) is pinnable by nobody, so the two ends drift in either direction and the symptom is identical both ways. Anything this package routes through a streamed method inherits the same exposure — which is the reason `subscribeDocs` composing per-document `doc_subscribe` is worth more than it looks.
|
||||
@@ -46,3 +46,5 @@ The polyfill's published surface has drifted from the SDK's: invented names, inv
|
||||
- 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.
|
||||
- T01.c - `useShapeQuery` takes the SDK's `Scope`, re-exported as a passthrough. Our three literals became `StoreType`, internal only: VERIFIED they are upstream's own `store_type`, so only the NAME was invented. A store is now named by its REFERENCE (`privateStore()` / `protectedStore()` / `publicStore()`), and `createEntityDoc(store?)` takes one, private when omitted, as `doc_create` does. `listMyEntityDocs`, `resolveScopeGraph` and `resolveWriteGraph` unpublished, still internal - T01.d removes the read path's last caller. VERIFIED: public and protected share one store id here, so the reference must be the index document. `1.0.0-dev.5`.
|
||||
- Package refresh (2026-09-21) - `@ng-org/*` moved to web alpha.14 / orm alpha.21 / shex-orm alpha.9 / alien-deepsignals alpha.13. The live ORM read now STARTS against the deployed broker: `6 passed, 0 failed, 6 declared`, ready in 16ms, delete reached the document. `bug_useshape-live-read-needs-a-newer-broker` DELETED - the deployment was never behind, our packages were. T01.d's blocker LIFTS. Also surfaced: `OrmSubscription` is now `RdfOrmSubscription` (T01.e's proposal names the old one); `Scope.graphs` is REQUIRED and its `[]` means the EMPTY set while `subjects: []` means NO filtering - the two axes read `[]` in opposite directions, which T01.d must not get wrong; six e2e files imported `ng-e2e-helpers` without the polyfill declaring it, fixed as `workspace:*`.
|
||||
|
||||
|
||||
@@ -250,19 +250,26 @@ async function seededTripleCount(doc: Nuri, subject: string): Promise<number> {
|
||||
/**
|
||||
* CHARACTERISATION of the deployed broker's `orm_start_graph` ARITY.
|
||||
*
|
||||
* The failure the runner reports names a parameter — `config` — that the ORM does not
|
||||
* send and that `nextgraph-rs` at the read commit does not declare. Two upstream
|
||||
* arrangements could put one there, and they differ by POSITION:
|
||||
* The failure the runner reports named a parameter - `config` - that the client of
|
||||
* the day did not send. Two upstream arrangements could put one there, and they differ
|
||||
* by POSITION:
|
||||
* - config LAST, after the callback (upstream 2026-04-12 to 2026-04-21) — an
|
||||
* unsupplied `config`, never a Function;
|
||||
* - config at index 4, BEFORE the callback — the 5-argument call from `@ng-org/orm`
|
||||
* then lands its callback exactly there, which is what the error describes.
|
||||
* - config at index 4, BEFORE the callback: the 5-argument call of the client of
|
||||
* the day then lands its callback exactly there, which is what the error described.
|
||||
*
|
||||
* So this calls the SDK member directly with an empty config in position 4. It does not
|
||||
* go through the ORM, which is fixed at five arguments — this is the one question the
|
||||
* ORM cannot ask. The app-side proxy will mistake the `{}` for the stream callback, so
|
||||
* nothing usable comes back; what is being read is only whether the engine still
|
||||
* refuses on `config`.
|
||||
* THE UPDATE ANSWERED THAT QUESTION. `@ng-org/web@0.1.2-alpha.14` declares six
|
||||
* arguments with `config` at index 4 and routes the stream callback from index 5
|
||||
* (`dist/ngweb.js`: `orm_start_graph: 5`), and `@ng-org/orm@0.1.2-alpha.21` sends six
|
||||
* to match (`dist/connector/RdfOrmSubscription.js:159`). So the second arrangement was
|
||||
* the right reading, and the ordinary `liveStart` path now exercises it.
|
||||
*
|
||||
* This is therefore kept as a FALLBACK, not as the live question: the runner only asks
|
||||
* it when `liveStart` did not become ready, to say whether the engine still refuses on
|
||||
* `config` when the client no longer can be blamed. It calls the SDK member directly
|
||||
* rather than through the ORM. The app-side proxy takes index 5 as the stream callback,
|
||||
* so the `{}` at index 4 is read as a config and nothing usable comes back; what is
|
||||
* being read is only whether the start is refused.
|
||||
*/
|
||||
async liveStartWithConfig() {
|
||||
const s = await sessionReady;
|
||||
@@ -273,14 +280,12 @@ async function seededTripleCount(doc: Nuri, subject: string): Promise<number> {
|
||||
"store",
|
||||
undefined,
|
||||
)) as Nuri;
|
||||
// Six arguments against a declaration that has five: the declaration describes the
|
||||
// arity of the PUBLISHED client, and the arity of the DEPLOYED engine is the question.
|
||||
// Widened through `unknown`, never `any` — the call is deliberate, not unchecked.
|
||||
const startGraph = realNg.orm_start_graph as unknown as (
|
||||
...a: unknown[]
|
||||
) => Promise<unknown>;
|
||||
// Called against the PUBLISHED declaration, which now has these six arguments, so the
|
||||
// widening this used to need is gone: the arity is checked here rather than asserted.
|
||||
// What remains unknowable at compile time is the DEPLOYED engine's arity, which is
|
||||
// the question this asks.
|
||||
try {
|
||||
await startGraph([doc], [], LIVE_SHAPE, s.session_id, {}, () => {});
|
||||
await realNg.orm_start_graph([doc], [], LIVE_SHAPE, s.session_id, {}, () => {});
|
||||
return { doc, error: null };
|
||||
} catch (e) {
|
||||
return { doc, error: e instanceof Error ? e.message : String(e) };
|
||||
|
||||
@@ -261,9 +261,11 @@ async function main(): Promise<void> {
|
||||
}
|
||||
check(CHECKS[4]!, remaining === 0, `triples left for the seeded subject: ${remaining}`);
|
||||
|
||||
// Only worth asking when the five-argument call did NOT start: it is the diagnosis of
|
||||
// Only worth asking when the published call did NOT start: it is the diagnosis of
|
||||
// that failure, and a deployment that answers the published arity has nothing to
|
||||
// characterise.
|
||||
// characterise. Since `@ng-org/web@0.1.2-alpha.14` the published call is itself the
|
||||
// six-argument one, so this asks whether the engine refuses a start the client got
|
||||
// right - no longer whether the client was sending the wrong number.
|
||||
if (readyMs === null) {
|
||||
const withConfig = await sdk<{ doc: string; error: string | null }>(
|
||||
frame,
|
||||
@@ -273,12 +275,12 @@ async function main(): Promise<void> {
|
||||
CHECKS[5]!,
|
||||
withConfig.error === null
|
||||
? "six arguments with `{}` in position 4 are ACCEPTED — the deployed " +
|
||||
"`orm_start_graph` takes a `config` BEFORE the callback, so the published " +
|
||||
"client's five-argument call puts its callback there"
|
||||
"`orm_start_graph` takes a `config` BEFORE the callback, which is what the " +
|
||||
"published client now sends, so the refusal is not an arity mismatch"
|
||||
: `six arguments were refused too: ${withConfig.error}`,
|
||||
);
|
||||
} else {
|
||||
record(CHECKS[5]!, "not asked — the published five-argument call started");
|
||||
record(CHECKS[5]!, "not asked — the published six-argument call started");
|
||||
}
|
||||
} finally {
|
||||
if (page) await closeQuietly("the ORM live-read page", () => page!.close());
|
||||
|
||||
@@ -2,10 +2,7 @@
|
||||
"extends": "../../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"types": ["bun"],
|
||||
"noEmit": true,
|
||||
"paths": {
|
||||
"@ng-org/web": ["../node_modules/@ng-org/web"]
|
||||
}
|
||||
"noEmit": true
|
||||
},
|
||||
"include": ["."]
|
||||
}
|
||||
|
||||
Generated
+2
-9
@@ -21,8 +21,8 @@ importers:
|
||||
packages/ng-e2e-helpers:
|
||||
devDependencies:
|
||||
'@ng-org/web':
|
||||
specifier: 0.1.2-alpha.13
|
||||
version: 0.1.2-alpha.13
|
||||
specifier: 0.1.2-alpha.14
|
||||
version: 0.1.2-alpha.14
|
||||
playwright:
|
||||
specifier: ^1.61.1
|
||||
version: 1.61.1
|
||||
@@ -87,9 +87,6 @@ packages:
|
||||
peerDependencies:
|
||||
typescript: ^4.9.0 || ^5.0.0 || ^6.0.0
|
||||
|
||||
'@ng-org/web@0.1.2-alpha.13':
|
||||
resolution: {integrity: sha512-/xO0c+3NTphnws5Do2LDqgZWmAf+aNnYdChJKdU0dnp1U1iVSgi/y3yb8AYryf0v9sooj0aYJxt08B6DpirFMQ==}
|
||||
|
||||
'@ng-org/web@0.1.2-alpha.14':
|
||||
resolution: {integrity: sha512-FPQ7kyngMhAXK/WJtZ6pkoEJwGcau6GaBH2nhjjYorMq7OG//I+zwghat90FsIeMrfYsps0+5Y0UrRqfxNdUXA==}
|
||||
|
||||
@@ -514,10 +511,6 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- encoding
|
||||
|
||||
'@ng-org/web@0.1.2-alpha.13':
|
||||
dependencies:
|
||||
async-proxy: 0.4.1
|
||||
|
||||
'@ng-org/web@0.1.2-alpha.14':
|
||||
dependencies:
|
||||
async-proxy: 0.4.1
|
||||
|
||||
Reference in New Issue
Block a user