5.9 KiB
type, summary, severity, opened, last_checked
| type | summary | severity | opened | last_checked |
|---|---|---|---|---|
| bug | A live read through the published `useShape` never starts against a broker deployed before `orm_start` was renamed `orm_start_graph` — it stays empty and pending forever, with one console error as the only trace | major | 2026-08-17 | 2026-08-17 |
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 NextGraph. Nothing in this package can work around it — the failure is a name lookup two hops below our surface.
The mechanism — one method name, kept in three places by hand
VERIFIED 2026-08-17 by reading /home/sylvain/projects/nextgraph/nextgraph-rs at 213338f6.
A live ORM read is one call, and it is streamed: the engine pushes into a callback rather than answering once. Nothing in the transport infers that — each hop looks the method up in a hand-maintained table mapping method name to the index of its callback argument, and a method missing from the table takes the plain request/response branch instead.
sdk/js/orm/src/connector/GraphOrmSubscription.ts— theOrmSubscriptionconstructor is the only place a live graph read begins:ng.orm_start_graph(scope.graphs, scope.subjects, shapeType, session.session_id, this.onBackendMessage). Five arguments, callback last.useShapereaches it throughOrmSubscription.getOrCreate(sdk/js/orm/src/frontendAdapters/react/useShape.ts).sdk/js/web/src/index.ts— the application-sidengproxy.streamed_apimust contain the method, orrpcposts the arguments verbatim to the parent window, callback included. A function is not structured-cloneable, so that post throws.sdk/js/api-web/main.ts— the broker-side proxy, an independent copy of the same table. Same fallback intomyWorker.postMessage, same throw.
The hops between the two tables carry no table of their own and cannot repair a mismatch: infra/ngnet/auth/src/main.ts forwards {method, args, streamed, port} unchanged, and engine/broker/auth/src/App.svelte re-appends a callback only when streamed is true.
The name changed. Both tables keyed the method as orm_start until commit d5ecd0fd (2026-01-16, "refactor discrete orm for yjs") renamed it to orm_start_graph in both files at once — while the ORM had been calling orm_start_graph. A build made before that commit therefore does not recognise the method a live read needs.
The application side is already correct, and that is what isolates the defect. This package pins @ng-org/web@0.1.2-alpha.13 (packages/polyfill/package.json), which is the version at 213338f6 — the rename is in it. @ng-org/api-web is private: true and has never been version-bumped past 0.1.2: it is not published, it ships only inside the broker build. Its table is whatever the operator last deployed, and no consumer can pin 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. A rejected start leaves the readiness promise pending forever 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. - Not affected:
watchShape,subscribeDoc,subscribeDocs,readUnion. These open onedoc_subscribeper document, anddoc_subscribe: 2has been in both tables continuously under a stable name.watchShapeis 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.
Not verified
The deployed broker's table was not read. https://nextgraph.net/redir/ returns a page containing none of the protocol markers, so it is not the bundle that holds it, and probing further was out of mandate. What is established is the mechanism and its exact precondition — a broker built before d5ecd0fd — not that the running deployment sits before it. The symptom was reported by the consuming application, not reproduced here.
What to do
- Do not build on live
useShape. UsewatchShapefor a reactive read,readUnionfor a one-shot one. This is stated in the contract's## Non-guaranteesso a consumer meets it without reading this leaf. - The fix is not ours. It is a NextGraph deployment: rebuild and redeploy the broker from a source at or after
d5ecd0fd. Nothing to change in this repository, and nothing to open upstream from here — report it, do not patch around it. - To check whether it is closed: open a live
useShapeagainst the deployment and watch for an initial payload. A permanently empty set with oneconsole.errornamingorm_start_graphmeans the table is still stale.
The shape worth remembering
A method name duplicated across three independently-deployed artifacts, with a silent fallback when they disagree, is a defect that cannot be caught by any one repository's tests. Renaming it in the two tables in one commit fixed the source; it did not fix anything already running. Anything this package routes through a streamed method inherits the same exposure — that is the reason subscribeDocs composing per-document doc_subscribe is worth more than it looks.