@pyric/cli 0.1.0-alpha.15 → 0.1.0-alpha.17

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (384) hide show
  1. package/README.md +13 -9
  2. package/dist/assurance/.generated/conformance-verdicts.d.ts +80 -72
  3. package/dist/assurance/.generated/conformance-verdicts.d.ts.map +1 -1
  4. package/dist/assurance/.generated/conformance-verdicts.js +80 -72
  5. package/dist/assurance/.generated/conformance-verdicts.js.map +1 -1
  6. package/dist/bridge/client/dispatch.js +1 -1
  7. package/dist/bridge/client/dispatch.js.map +1 -1
  8. package/dist/cli/database-rules.d.ts.map +1 -1
  9. package/dist/cli/database-rules.js +6 -4
  10. package/dist/cli/database-rules.js.map +1 -1
  11. package/dist/cli/dev-runner.d.ts +9 -0
  12. package/dist/cli/dev-runner.d.ts.map +1 -1
  13. package/dist/cli/dev-runner.js +18 -0
  14. package/dist/cli/dev-runner.js.map +1 -1
  15. package/dist/cli/serve.d.ts.map +1 -1
  16. package/dist/cli/serve.js +61 -6
  17. package/dist/cli/serve.js.map +1 -1
  18. package/dist/cli/verify.js +2 -2
  19. package/dist/cli/verify.js.map +1 -1
  20. package/dist/conformance/.generated/can-i-use-browser.d.ts.map +1 -1
  21. package/dist/conformance/.generated/can-i-use-browser.js +1 -1
  22. package/dist/conformance/.generated/can-i-use-browser.js.map +1 -1
  23. package/dist/conformance/.generated/can-i-use.d.ts.map +1 -1
  24. package/dist/conformance/.generated/can-i-use.js +1 -1
  25. package/dist/conformance/.generated/can-i-use.js.map +1 -1
  26. package/dist/conformance/.generated/conformance-docs.d.ts.map +1 -1
  27. package/dist/conformance/.generated/conformance-docs.js +1 -1
  28. package/dist/conformance/.generated/conformance-docs.js.map +1 -1
  29. package/dist/next/client-aliases.d.ts +11 -0
  30. package/dist/next/client-aliases.d.ts.map +1 -0
  31. package/dist/next/client-aliases.js +38 -0
  32. package/dist/next/client-aliases.js.map +1 -0
  33. package/dist/next/dev-rewrites.d.ts +9 -0
  34. package/dist/next/dev-rewrites.d.ts.map +1 -0
  35. package/dist/next/dev-rewrites.js +53 -0
  36. package/dist/next/dev-rewrites.js.map +1 -0
  37. package/dist/next/guard.d.ts +15 -0
  38. package/dist/next/guard.d.ts.map +1 -0
  39. package/dist/next/guard.js +52 -0
  40. package/dist/next/guard.js.map +1 -0
  41. package/dist/next/index.d.ts +12 -0
  42. package/dist/next/index.d.ts.map +1 -0
  43. package/dist/next/index.js +11 -0
  44. package/dist/next/index.js.map +1 -0
  45. package/dist/next/runtime-env.d.ts +7 -0
  46. package/dist/next/runtime-env.d.ts.map +1 -0
  47. package/dist/next/runtime-env.js +13 -0
  48. package/dist/next/runtime-env.js.map +1 -0
  49. package/dist/next/server-external-packages.d.ts +10 -0
  50. package/dist/next/server-external-packages.d.ts.map +1 -0
  51. package/dist/next/server-external-packages.js +34 -0
  52. package/dist/next/server-external-packages.js.map +1 -0
  53. package/dist/next/target-url.d.ts +11 -0
  54. package/dist/next/target-url.d.ts.map +1 -0
  55. package/dist/next/target-url.js +36 -0
  56. package/dist/next/target-url.js.map +1 -0
  57. package/dist/next/turbopack-config.d.ts +9 -0
  58. package/dist/next/turbopack-config.d.ts.map +1 -0
  59. package/dist/next/turbopack-config.js +39 -0
  60. package/dist/next/turbopack-config.js.map +1 -0
  61. package/dist/next/types.d.ts +40 -0
  62. package/dist/next/types.d.ts.map +1 -0
  63. package/dist/next/types.js +5 -0
  64. package/dist/next/types.js.map +1 -0
  65. package/dist/next/webpack-config.d.ts +11 -0
  66. package/dist/next/webpack-config.d.ts.map +1 -0
  67. package/dist/next/webpack-config.js +51 -0
  68. package/dist/next/webpack-config.js.map +1 -0
  69. package/dist/next/with-pyric.d.ts +22 -0
  70. package/dist/next/with-pyric.d.ts.map +1 -0
  71. package/dist/next/with-pyric.js +49 -0
  72. package/dist/next/with-pyric.js.map +1 -0
  73. package/dist/register/app-bridge.d.ts +9 -0
  74. package/dist/register/app-bridge.d.ts.map +1 -0
  75. package/dist/register/app-bridge.js +56 -0
  76. package/dist/register/app-bridge.js.map +1 -0
  77. package/dist/register/hooks.d.ts.map +1 -1
  78. package/dist/register/hooks.js +6 -1
  79. package/dist/register/hooks.js.map +1 -1
  80. package/dist/register/index.js +1 -1
  81. package/dist/register/index.js.map +1 -1
  82. package/dist/register/mapping.d.ts +3 -1
  83. package/dist/register/mapping.d.ts.map +1 -1
  84. package/dist/register/mapping.js +41 -5
  85. package/dist/register/mapping.js.map +1 -1
  86. package/dist/rtdb/rules-json.d.ts +12 -0
  87. package/dist/rtdb/rules-json.d.ts.map +1 -1
  88. package/dist/rtdb/rules-json.js +98 -0
  89. package/dist/rtdb/rules-json.js.map +1 -1
  90. package/dist/serve/bundler.d.ts.map +1 -1
  91. package/dist/serve/bundler.js +2 -0
  92. package/dist/serve/bundler.js.map +1 -1
  93. package/dist/serve/discovery.d.ts +4 -4
  94. package/dist/serve/discovery.d.ts.map +1 -1
  95. package/dist/serve/discovery.js +59 -38
  96. package/dist/serve/discovery.js.map +1 -1
  97. package/dist/serve/entries/app-ai-passthrough.d.ts +8 -0
  98. package/dist/serve/entries/app-ai-passthrough.d.ts.map +1 -0
  99. package/dist/serve/entries/app-ai-passthrough.js +59 -0
  100. package/dist/serve/entries/app-ai-passthrough.js.map +1 -0
  101. package/dist/serve/entries/auth-helper-core.d.ts +9 -0
  102. package/dist/serve/entries/auth-helper-core.d.ts.map +1 -1
  103. package/dist/serve/entries/auth-helper-core.js +37 -8
  104. package/dist/serve/entries/auth-helper-core.js.map +1 -1
  105. package/dist/serve/entries/auth.d.ts +1 -0
  106. package/dist/serve/entries/auth.d.ts.map +1 -1
  107. package/dist/serve/entries/auth.js +7 -4
  108. package/dist/serve/entries/auth.js.map +1 -1
  109. package/dist/serve/entries/bridge-url.d.ts.map +1 -1
  110. package/dist/serve/entries/bridge-url.js +7 -2
  111. package/dist/serve/entries/bridge-url.js.map +1 -1
  112. package/dist/serve/entries/database.d.ts +1 -0
  113. package/dist/serve/entries/database.d.ts.map +1 -1
  114. package/dist/serve/entries/database.js +1 -0
  115. package/dist/serve/entries/database.js.map +1 -1
  116. package/dist/serve/entries/firestore.d.ts +1 -0
  117. package/dist/serve/entries/firestore.d.ts.map +1 -1
  118. package/dist/serve/entries/firestore.js +1 -0
  119. package/dist/serve/entries/firestore.js.map +1 -1
  120. package/dist/serve/entries/init.d.ts.map +1 -1
  121. package/dist/serve/entries/init.js +4 -2
  122. package/dist/serve/entries/init.js.map +1 -1
  123. package/dist/serve/entries/runtime.js +23 -0
  124. package/dist/serve/entries/runtime.js.map +1 -1
  125. package/dist/serve/entries/storage.d.ts +2 -0
  126. package/dist/serve/entries/storage.d.ts.map +1 -1
  127. package/dist/serve/entries/storage.js +9 -2
  128. package/dist/serve/entries/storage.js.map +1 -1
  129. package/dist/serve/html-injection.d.ts +3 -1
  130. package/dist/serve/html-injection.d.ts.map +1 -1
  131. package/dist/serve/html-injection.js +27 -1
  132. package/dist/serve/html-injection.js.map +1 -1
  133. package/dist/serve/rules.js +2 -2
  134. package/dist/serve/rules.js.map +1 -1
  135. package/dist/serve/runtime/chip-config.d.ts +1 -1
  136. package/dist/serve/runtime/chip-config.d.ts.map +1 -1
  137. package/dist/serve/runtime/chip-config.js +16 -8
  138. package/dist/serve/runtime/chip-config.js.map +1 -1
  139. package/dist/serve/sandbox-session.d.ts +1 -0
  140. package/dist/serve/sandbox-session.d.ts.map +1 -1
  141. package/dist/serve/sandbox-session.js +30 -2
  142. package/dist/serve/sandbox-session.js.map +1 -1
  143. package/dist/serve/site-ui/_astro/_studio_.B6b_G1ev.css +1 -0
  144. package/dist/serve/site-ui/_astro/app.bnmVTkIY.js +11 -0
  145. package/dist/serve/site-ui/_astro/app.t6u-adB_.js +1 -0
  146. package/dist/serve/site-ui/_astro/{chess-showcase.DzPO6kz0.js → chess-showcase.kC8dXLKY.js} +1 -1
  147. package/dist/serve/site-ui/_astro/{example-runtime.CM0H02rx.js → example-runtime.D79PnoSk.js} +1 -1
  148. package/dist/serve/site-ui/_astro/index.DzVde04M.js +1 -0
  149. package/dist/serve/site-ui/_astro/reads.BYBRLbsE.js +1 -0
  150. package/dist/serve/site-ui/_astro/sandbox-controls.C9Aaazt8.js +1252 -0
  151. package/dist/serve/site-ui/_astro/{seed.BCn5DhHS.js → seed.DANAIyRw.js} +1 -1
  152. package/dist/serve/site-ui/auth/index.html +2 -2
  153. package/dist/serve/site-ui/docs/_rhythm/index.html +1 -1
  154. package/dist/serve/site-ui/docs/agent/set-up-your-agent/index.html +1 -1
  155. package/dist/serve/site-ui/docs/agent/watch-and-review/index.html +1 -1
  156. package/dist/serve/site-ui/docs/agent/work-with-an-agent/index.html +1 -1
  157. package/dist/serve/site-ui/docs/ai-compat/index.html +85 -85
  158. package/dist/serve/site-ui/docs/ai-compat.md +84 -84
  159. package/dist/serve/site-ui/docs/api-reference/index.html +3 -1
  160. package/dist/serve/site-ui/docs/api-reference.md +2 -0
  161. package/dist/serve/site-ui/docs/app-compat/index.html +28 -28
  162. package/dist/serve/site-ui/docs/app-compat.md +27 -27
  163. package/dist/serve/site-ui/docs/auth-compat/index.html +122 -122
  164. package/dist/serve/site-ui/docs/auth-compat.md +121 -121
  165. package/dist/serve/site-ui/docs/build/ai-logic/index.html +1 -1
  166. package/dist/serve/site-ui/docs/build/authentication/index.html +1 -1
  167. package/dist/serve/site-ui/docs/build/cloud-firestore/index.html +1 -1
  168. package/dist/serve/site-ui/docs/build/cloud-messaging/index.html +1 -1
  169. package/dist/serve/site-ui/docs/build/cloud-storage/index.html +1 -1
  170. package/dist/serve/site-ui/docs/build/realtime-database/index.html +1 -1
  171. package/dist/serve/site-ui/docs/conformance-scores/index.html +4 -4
  172. package/dist/serve/site-ui/docs/conformance-scores.md +3 -3
  173. package/dist/serve/site-ui/docs/create-pyric-reference-api/index.html +6 -6
  174. package/dist/serve/site-ui/docs/create-pyric-reference-api.md +5 -5
  175. package/dist/serve/site-ui/docs/database-compat/index.html +200 -448
  176. package/dist/serve/site-ui/docs/database-compat.md +198 -461
  177. package/dist/serve/site-ui/docs/examples/chess/index.html +2 -2
  178. package/dist/serve/site-ui/docs/examples/index.html +1 -1
  179. package/dist/serve/site-ui/docs/firestore-compat/index.html +186 -195
  180. package/dist/serve/site-ui/docs/firestore-compat.md +184 -195
  181. package/dist/serve/site-ui/docs/functions-rtdb-compat/index.html +15 -15
  182. package/dist/serve/site-ui/docs/functions-rtdb-compat.md +14 -14
  183. package/dist/serve/site-ui/docs/get-started/how-the-swap-works/index.html +1 -1
  184. package/dist/serve/site-ui/docs/get-started/nextjs/index.html +57 -0
  185. package/dist/serve/site-ui/docs/get-started/nextjs.md +86 -0
  186. package/dist/serve/site-ui/docs/get-started/start-building/index.html +25 -8
  187. package/dist/serve/site-ui/docs/get-started/start-building.md +34 -5
  188. package/dist/serve/site-ui/docs/get-started/vite/index.html +26 -0
  189. package/dist/serve/site-ui/docs/get-started/vite.md +45 -0
  190. package/dist/serve/site-ui/docs/index.json +668 -103
  191. package/dist/serve/site-ui/docs/messaging-compat/index.html +56 -56
  192. package/dist/serve/site-ui/docs/messaging-compat.md +55 -55
  193. package/dist/serve/site-ui/docs/observe/resolve-runtime-status/index.html +1 -1
  194. package/dist/serve/site-ui/docs/observe/see-whats-happening/index.html +1 -1
  195. package/dist/serve/site-ui/docs/observe/shape-your-data/index.html +1 -1
  196. package/dist/serve/site-ui/docs/overview/index.html +1 -1
  197. package/dist/serve/site-ui/docs/pyric-admin-app-reference-api/index.html +39 -13
  198. package/dist/serve/site-ui/docs/pyric-admin-app-reference-api.md +28 -2
  199. package/dist/serve/site-ui/docs/pyric-admin-auth-reference-api/index.html +1 -1
  200. package/dist/serve/site-ui/docs/pyric-admin-database-reference-api/index.html +38 -6
  201. package/dist/serve/site-ui/docs/pyric-admin-database-reference-api.md +43 -0
  202. package/dist/serve/site-ui/docs/pyric-admin-firestore-reference-api/index.html +9 -1
  203. package/dist/serve/site-ui/docs/pyric-admin-firestore-reference-api.md +8 -0
  204. package/dist/serve/site-ui/docs/pyric-admin-messaging-reference-api/index.html +1 -1
  205. package/dist/serve/site-ui/docs/pyric-admin-storage-reference-api/index.html +27 -4
  206. package/dist/serve/site-ui/docs/pyric-admin-storage-reference-api.md +23 -0
  207. package/dist/serve/site-ui/docs/pyric-ai-reference-api/index.html +1 -1
  208. package/dist/serve/site-ui/docs/pyric-ai-scripting-reference-api/index.html +1 -1
  209. package/dist/serve/site-ui/docs/pyric-app-reference-api/index.html +1 -1
  210. package/dist/serve/site-ui/docs/pyric-auth-reference-api/index.html +2 -2
  211. package/dist/serve/site-ui/docs/pyric-auth-reference-api.md +1 -1
  212. package/dist/serve/site-ui/docs/pyric-cli-assurance-browser-reference-api/index.html +1 -1
  213. package/dist/serve/site-ui/docs/pyric-cli-assurance-reference-api/index.html +1 -1
  214. package/dist/serve/site-ui/docs/pyric-cli-bridge-client-reference-api/index.html +1 -1
  215. package/dist/serve/site-ui/docs/pyric-cli-bridge-reference-api/index.html +1 -1
  216. package/dist/serve/site-ui/docs/pyric-cli-conformance-browser-reference-api/index.html +1 -1
  217. package/dist/serve/site-ui/docs/pyric-cli-conformance-docs-reference-api/index.html +1 -1
  218. package/dist/serve/site-ui/docs/pyric-cli-conformance-reference-api/index.html +1 -1
  219. package/dist/serve/site-ui/docs/pyric-cli-credentials-node-reference-api/index.html +1 -1
  220. package/dist/serve/site-ui/docs/pyric-cli-discover-reference-api/index.html +1 -1
  221. package/dist/serve/site-ui/docs/pyric-cli-next-reference-api/index.html +123 -0
  222. package/dist/serve/site-ui/docs/pyric-cli-next-reference-api.md +99 -0
  223. package/dist/serve/site-ui/docs/pyric-cli-register-app-bridge-reference-api/index.html +127 -0
  224. package/dist/serve/site-ui/docs/pyric-cli-register-app-bridge-reference-api.md +174 -0
  225. package/dist/serve/site-ui/docs/pyric-cli-register-reference-api/index.html +1 -1
  226. package/dist/serve/site-ui/docs/pyric-cli-remote-reference-api/index.html +1 -1
  227. package/dist/serve/site-ui/docs/pyric-cli-serve-worker-reference-api/index.html +570 -326
  228. package/dist/serve/site-ui/docs/pyric-cli-serve-worker-reference-api.md +198 -6
  229. package/dist/serve/site-ui/docs/pyric-cli-verify-reference-api/index.html +1 -1
  230. package/dist/serve/site-ui/docs/pyric-cli-vite-reference-api/index.html +1 -1
  231. package/dist/serve/site-ui/docs/pyric-database-reference-api/index.html +1 -1
  232. package/dist/serve/site-ui/docs/pyric-firestore-reference-api/index.html +2971 -627
  233. package/dist/serve/site-ui/docs/pyric-firestore-reference-api.md +2873 -517
  234. package/dist/serve/site-ui/docs/pyric-messaging-reference-api/index.html +1 -1
  235. package/dist/serve/site-ui/docs/pyric-messaging-sw-reference-api/index.html +1 -1
  236. package/dist/serve/site-ui/docs/pyric-rules-reference-api/index.html +43 -3
  237. package/dist/serve/site-ui/docs/pyric-rules-reference-api.md +8 -0
  238. package/dist/serve/site-ui/docs/pyric-sandbox-database-reference-api/index.html +26 -2
  239. package/dist/serve/site-ui/docs/pyric-sandbox-database-reference-api.md +24 -0
  240. package/dist/serve/site-ui/docs/pyric-sandbox-firestore-reference-api/index.html +1 -1
  241. package/dist/serve/site-ui/docs/pyric-sandbox-reference-api/index.html +77 -9
  242. package/dist/serve/site-ui/docs/pyric-sandbox-reference-api.md +28 -13
  243. package/dist/serve/site-ui/docs/pyric-storage-reference-api/index.html +240 -63
  244. package/dist/serve/site-ui/docs/pyric-storage-reference-api.md +164 -5
  245. package/dist/serve/site-ui/docs/pyric-ui-agents-reference-api/index.html +1 -1
  246. package/dist/serve/site-ui/docs/pyric-ui-auth-hooks-reference-api/index.html +1 -1
  247. package/dist/serve/site-ui/docs/pyric-ui-auth-reference-api/index.html +1 -1
  248. package/dist/serve/site-ui/docs/pyric-ui-events-hooks-reference-api/index.html +1 -1
  249. package/dist/serve/site-ui/docs/pyric-ui-events-reference-api/index.html +1 -1
  250. package/dist/serve/site-ui/docs/pyric-ui-firestore-hooks-reference-api/index.html +1 -1
  251. package/dist/serve/site-ui/docs/pyric-ui-firestore-reference-api/index.html +1 -1
  252. package/dist/serve/site-ui/docs/pyric-ui-primitives-reference-api/index.html +1 -1
  253. package/dist/serve/site-ui/docs/pyric-ui-rtdb-reference-api/index.html +1 -1
  254. package/dist/serve/site-ui/docs/pyric-ui-rules-hooks-reference-api/index.html +1 -1
  255. package/dist/serve/site-ui/docs/pyric-ui-rules-reference-api/index.html +1 -1
  256. package/dist/serve/site-ui/docs/pyric-ui-storage-hooks-reference-api/index.html +1 -1
  257. package/dist/serve/site-ui/docs/pyric-ui-storage-reference-api/index.html +2 -1
  258. package/dist/serve/site-ui/docs/pyric-ui-storage-reference-api.md +1 -0
  259. package/dist/serve/site-ui/docs/pyric-ui-traffic-hooks-reference-api/index.html +1 -1
  260. package/dist/serve/site-ui/docs/pyric-ui-traffic-reference-api/index.html +1 -1
  261. package/dist/serve/site-ui/docs/rules-compat/index.html +87 -79
  262. package/dist/serve/site-ui/docs/rules-compat.md +86 -78
  263. package/dist/serve/site-ui/docs/secure/audit-your-rules/index.html +1 -1
  264. package/dist/serve/site-ui/docs/secure/firestore-rules-limits/index.html +1 -1
  265. package/dist/serve/site-ui/docs/secure/read-a-denial/index.html +1 -1
  266. package/dist/serve/site-ui/docs/secure/rtdb-rules-in-typescript/index.html +1 -1
  267. package/dist/serve/site-ui/docs/secure/rules-standard-library/index.html +1 -1
  268. package/dist/serve/site-ui/docs/secure/secure-it-with-rules/index.html +1 -1
  269. package/dist/serve/site-ui/docs/secure/simulate-and-lint/index.html +1 -1
  270. package/dist/serve/site-ui/docs/secure/write-a-rules-test-suite/index.html +1 -1
  271. package/dist/serve/site-ui/docs/ship/ship-to-production/index.html +1 -1
  272. package/dist/serve/site-ui/docs/ship/test-in-node/index.html +1 -1
  273. package/dist/serve/site-ui/docs/storage-compat/index.html +119 -125
  274. package/dist/serve/site-ui/docs/storage-compat.md +118 -124
  275. package/dist/serve/site-ui/docs/trust/how-we-know-it-matches-firebase/index.html +1 -1
  276. package/dist/serve/site-ui/docs/trust/rules-standard-library/index.html +1 -1
  277. package/dist/serve/site-ui/docs/trust/storage-rules-stdlib-research/index.html +1 -1
  278. package/dist/serve/site-ui/docs/trust/versioning-and-compatibility/index.html +1 -1
  279. package/dist/serve/site-ui/docs/tutorial/index.html +1 -1
  280. package/dist/serve/site-ui/examples/chess/index.html +1 -1
  281. package/dist/serve/site-ui/examples/firestore-first-write/index.html +1 -1
  282. package/dist/serve/site-ui/firestore/index.html +2 -2
  283. package/dist/serve/site-ui/index.html +2 -2
  284. package/dist/serve/site-ui/llms.txt +6 -2
  285. package/dist/serve/site-ui/rtdb/index.html +2 -2
  286. package/dist/serve/site-ui/settings/index.html +2 -2
  287. package/dist/serve/site-ui/storage/index.html +2 -2
  288. package/dist/serve/site-ui/traffic/index.html +2 -2
  289. package/dist/serve/vite-ai-config.d.ts +4 -1
  290. package/dist/serve/vite-ai-config.d.ts.map +1 -1
  291. package/dist/serve/vite-ai-config.js +83 -19
  292. package/dist/serve/vite-ai-config.js.map +1 -1
  293. package/dist/serve/vite-generation-rules-watch.d.ts.map +1 -1
  294. package/dist/serve/vite-generation-rules-watch.js +61 -14
  295. package/dist/serve/vite-generation-rules-watch.js.map +1 -1
  296. package/dist/serve/vite-module-swap.d.ts +4 -1
  297. package/dist/serve/vite-module-swap.d.ts.map +1 -1
  298. package/dist/serve/vite-module-swap.js +106 -21
  299. package/dist/serve/vite-module-swap.js.map +1 -1
  300. package/dist/serve/vite-plugin.d.ts.map +1 -1
  301. package/dist/serve/vite-plugin.js +45 -5
  302. package/dist/serve/vite-plugin.js.map +1 -1
  303. package/dist/serve/vite-sandbox-generation.js +1 -1
  304. package/dist/serve/vite-sandbox-generation.js.map +1 -1
  305. package/dist/serve/worker/client/resumable.d.ts +30 -0
  306. package/dist/serve/worker/client/resumable.d.ts.map +1 -0
  307. package/dist/serve/worker/client/resumable.js +416 -0
  308. package/dist/serve/worker/client/resumable.js.map +1 -0
  309. package/dist/serve/worker/client/rtdb-references.d.ts.map +1 -1
  310. package/dist/serve/worker/client/rtdb-references.js +4 -1
  311. package/dist/serve/worker/client/rtdb-references.js.map +1 -1
  312. package/dist/serve/worker/client/storage.d.ts +16 -2
  313. package/dist/serve/worker/client/storage.d.ts.map +1 -1
  314. package/dist/serve/worker/client/storage.js +57 -6
  315. package/dist/serve/worker/client/storage.js.map +1 -1
  316. package/dist/serve/worker/host/rules.d.ts.map +1 -1
  317. package/dist/serve/worker/host/rules.js +2 -1
  318. package/dist/serve/worker/host/rules.js.map +1 -1
  319. package/dist/serve/worker/index.d.ts +1 -1
  320. package/dist/serve/worker/index.d.ts.map +1 -1
  321. package/dist/serve/worker/index.js +1 -1
  322. package/dist/serve/worker/index.js.map +1 -1
  323. package/dist/serve/worker/serve-init.d.ts.map +1 -1
  324. package/dist/serve/worker/serve-init.js +31 -0
  325. package/dist/serve/worker/serve-init.js.map +1 -1
  326. package/package.json +15 -4
  327. package/src/assurance/.generated/conformance-verdicts.ts +80 -72
  328. package/src/bridge/client/dispatch.ts +1 -1
  329. package/src/cli/cli.test.ts +14 -0
  330. package/src/cli/database-rules.ts +6 -4
  331. package/src/cli/dev-runner.ts +21 -0
  332. package/src/cli/serve.ts +62 -5
  333. package/src/cli/verify.ts +2 -2
  334. package/src/conformance/.generated/can-i-use-browser.ts +1 -1
  335. package/src/conformance/.generated/can-i-use.ts +1 -1
  336. package/src/conformance/.generated/conformance-docs.ts +1 -1
  337. package/src/next/client-aliases.ts +39 -0
  338. package/src/next/dev-rewrites.ts +77 -0
  339. package/src/next/guard.ts +61 -0
  340. package/src/next/index.ts +16 -0
  341. package/src/next/runtime-env.ts +18 -0
  342. package/src/next/server-external-packages.ts +43 -0
  343. package/src/next/target-url.ts +46 -0
  344. package/src/next/turbopack-config.ts +54 -0
  345. package/src/next/types.ts +47 -0
  346. package/src/next/webpack-config.ts +71 -0
  347. package/src/next/with-pyric.ts +58 -0
  348. package/src/register/app-bridge.ts +79 -0
  349. package/src/register/hooks.ts +6 -1
  350. package/src/register/index.ts +1 -1
  351. package/src/register/mapping.ts +50 -4
  352. package/src/rtdb/rules-json.ts +110 -0
  353. package/src/serve/bundler.ts +2 -0
  354. package/src/serve/discovery.ts +65 -44
  355. package/src/serve/entries/app-ai-passthrough.ts +83 -0
  356. package/src/serve/entries/auth-helper-core.ts +44 -8
  357. package/src/serve/entries/auth.ts +9 -4
  358. package/src/serve/entries/bridge-url.ts +9 -2
  359. package/src/serve/entries/database.ts +1 -0
  360. package/src/serve/entries/firestore.ts +1 -0
  361. package/src/serve/entries/init.ts +5 -2
  362. package/src/serve/entries/runtime.ts +25 -0
  363. package/src/serve/entries/storage.ts +12 -1
  364. package/src/serve/html-injection.ts +26 -1
  365. package/src/serve/rules.ts +2 -2
  366. package/src/serve/runtime/chip-config.ts +19 -6
  367. package/src/serve/sandbox-session.ts +31 -2
  368. package/src/serve/vite-ai-config.ts +86 -19
  369. package/src/serve/vite-generation-rules-watch.ts +67 -15
  370. package/src/serve/vite-module-swap.ts +121 -21
  371. package/src/serve/vite-plugin.ts +44 -6
  372. package/src/serve/vite-sandbox-generation.ts +1 -1
  373. package/src/serve/worker/client/resumable.ts +506 -0
  374. package/src/serve/worker/client/rtdb-references.ts +4 -1
  375. package/src/serve/worker/client/storage.ts +77 -7
  376. package/src/serve/worker/host/rules.ts +2 -1
  377. package/src/serve/worker/index.ts +4 -0
  378. package/src/serve/worker/serve-init.ts +30 -0
  379. package/dist/serve/site-ui/_astro/_studio_.BtYpqe1A.css +0 -1
  380. package/dist/serve/site-ui/_astro/app.BnMC0ywl.js +0 -109
  381. package/dist/serve/site-ui/_astro/app.DVXXR-WI.js +0 -1
  382. package/dist/serve/site-ui/_astro/index.B4Kh80he.js +0 -1
  383. package/dist/serve/site-ui/_astro/reads.D1exkX1-.js +0 -1
  384. package/dist/serve/site-ui/_astro/sandbox-controls.BqMePr5V.js +0 -1153
@@ -19,633 +19,432 @@
19
19
  <div class="compat-key">
20
20
  <span class="compat-key-item"><span class="compat-dot" data-status="ok"></span><strong>Conforming</strong> — observable behavior matches Firebase, locked by a passing probe</span>
21
21
  <span class="compat-key-item"><span class="compat-dot" data-status="diverged"></span><strong>Diverged (documented)</strong> — intentional difference with a written reason</span>
22
- <span class="compat-key-item"><span class="compat-dot" data-status="unsupported"></span><strong>Not implemented yet</strong> — pending, or intentionally outside the mirror</span>
23
22
  </div>
24
23
 
25
- ## Archived production-toolkit observations
24
+ ## Public API
26
25
 
27
- These unsupported tombstones preserve immutable oracle `rowIds` for the removed host, REST, data, crawl, generation, and deployment toolkit. They are historical evidence, not current API claims.
28
-
29
- <div class="compat-list">
30
- <details class="compat-row" data-status="unsupported">
31
- <summary class="compat-line"><span class="compat-dot" data-status="unsupported" role="img" aria-label="Not implemented yet" title="Not implemented yet"></span><span class="compat-main"><code class="compat-api">Removed REST host</code><span class="compat-sub"><span class="compat-behavior">Historical <code>.json</code> REST transport contract for the removed production host.</span></span></span></summary>
32
- <div class="compat-evidence"><div class="compat-probe">Archived oracle observation; implementation removed.</div></div>
33
- </details>
34
- <details class="compat-row" data-status="unsupported">
35
- <summary class="compat-line"><span class="compat-dot" data-status="unsupported" role="img" aria-label="Not implemented yet" title="Not implemented yet"></span><span class="compat-main"><code class="compat-api">Removed data handler</code><span class="compat-sub"><span class="compat-behavior">Historical admin read and set/get behavior for the removed production data handler.</span></span></span></summary>
36
- <div class="compat-evidence"><div class="compat-probe">Archived oracle observations; implementation removed.</div></div>
37
- </details>
38
- <details class="compat-row" data-status="unsupported">
39
- <summary class="compat-line"><span class="compat-dot" data-status="unsupported" role="img" aria-label="Not implemented yet" title="Not implemented yet"></span><span class="compat-main"><code class="compat-api">Removed data handler</code><span class="compat-sub"><span class="compat-behavior">Historical user read return shape for the removed production data handler.</span></span></span></summary>
40
- <div class="compat-evidence"><div class="compat-probe">Archived oracle observation; implementation removed.</div></div>
41
- </details>
42
- <details class="compat-row" data-status="unsupported">
43
- <summary class="compat-line"><span class="compat-dot" data-status="unsupported" role="img" aria-label="Not implemented yet" title="Not implemented yet"></span><span class="compat-main"><code class="compat-api">Removed data handler</code><span class="compat-sub"><span class="compat-behavior">Historical rules-denial normalization for the removed production data handler.</span></span></span></summary>
44
- <div class="compat-evidence"><div class="compat-probe">Archived oracle observations; implementation removed.</div></div>
45
- </details>
46
- <details class="compat-row" data-status="unsupported">
47
- <summary class="compat-line"><span class="compat-dot" data-status="unsupported" role="img" aria-label="Not implemented yet" title="Not implemented yet"></span><span class="compat-main"><code class="compat-api">Removed data handler</code><span class="compat-sub"><span class="compat-behavior">Historical rules-denied read behavior for the removed production data handler.</span></span></span></summary>
48
- <div class="compat-evidence"><div class="compat-probe">Archived oracle observation; implementation removed.</div></div>
49
- </details>
50
- <details class="compat-row" data-status="unsupported">
51
- <summary class="compat-line"><span class="compat-dot" data-status="unsupported" role="img" aria-label="Not implemented yet" title="Not implemented yet"></span><span class="compat-main"><code class="compat-api">Removed data handler</code><span class="compat-sub"><span class="compat-behavior">Historical set/get round trip for the removed production data handler.</span></span></span></summary>
52
- <div class="compat-evidence"><div class="compat-probe">Archived oracle observation; implementation removed.</div></div>
53
- </details>
54
- <details class="compat-row" data-status="unsupported">
55
- <summary class="compat-line"><span class="compat-dot" data-status="unsupported" role="img" aria-label="Not implemented yet" title="Not implemented yet"></span><span class="compat-main"><code class="compat-api">Removed data handler</code><span class="compat-sub"><span class="compat-behavior">Historical set-null removal behavior for the removed production data handler.</span></span></span></summary>
56
- <div class="compat-evidence"><div class="compat-probe">Archived oracle observation; implementation removed.</div></div>
57
- </details>
58
- <details class="compat-row" data-status="unsupported">
59
- <summary class="compat-line"><span class="compat-dot" data-status="unsupported" role="img" aria-label="Not implemented yet" title="Not implemented yet"></span><span class="compat-main"><code class="compat-api">Removed data handler</code><span class="compat-sub"><span class="compat-behavior">Historical rules-denied write behavior for the removed production data handler.</span></span></span></summary>
60
- <div class="compat-evidence"><div class="compat-probe">Archived oracle observation; implementation removed.</div></div>
61
- </details>
62
- <details class="compat-row" data-status="unsupported">
63
- <summary class="compat-line"><span class="compat-dot" data-status="unsupported" role="img" aria-label="Not implemented yet" title="Not implemented yet"></span><span class="compat-main"><code class="compat-api">Removed data handler</code><span class="compat-sub"><span class="compat-behavior">Historical multi-path update behavior for the removed production data handler.</span></span></span></summary>
64
- <div class="compat-evidence"><div class="compat-probe">Archived oracle observation; implementation removed.</div></div>
65
- </details>
66
- <details class="compat-row" data-status="unsupported">
67
- <summary class="compat-line"><span class="compat-dot" data-status="unsupported" role="img" aria-label="Not implemented yet" title="Not implemented yet"></span><span class="compat-main"><code class="compat-api">Removed data handler</code><span class="compat-sub"><span class="compat-behavior">Historical push key behavior for the removed production data handler.</span></span></span></summary>
68
- <div class="compat-evidence"><div class="compat-probe">Archived oracle observation; implementation removed.</div></div>
69
- </details>
70
- <details class="compat-row" data-status="unsupported">
71
- <summary class="compat-line"><span class="compat-dot" data-status="unsupported" role="img" aria-label="Not implemented yet" title="Not implemented yet"></span><span class="compat-main"><code class="compat-api">Removed data handler</code><span class="compat-sub"><span class="compat-behavior">Historical push auto-ID format for the removed production data handler.</span></span></span></summary>
72
- <div class="compat-evidence"><div class="compat-probe">Archived oracle observation; implementation removed.</div></div>
73
- </details>
74
- <details class="compat-row" data-status="unsupported">
75
- <summary class="compat-line"><span class="compat-dot" data-status="unsupported" role="img" aria-label="Not implemented yet" title="Not implemented yet"></span><span class="compat-main"><code class="compat-api">Removed data handler</code><span class="compat-sub"><span class="compat-behavior">Historical remove-versus-set-null behavior for the removed production data handler.</span></span></span></summary>
76
- <div class="compat-evidence"><div class="compat-probe">Archived oracle observation; implementation removed.</div></div>
77
- </details>
78
- <details class="compat-row" data-status="unsupported">
79
- <summary class="compat-line"><span class="compat-dot" data-status="unsupported" role="img" aria-label="Not implemented yet" title="Not implemented yet"></span><span class="compat-main"><code class="compat-api">Removed data handler</code><span class="compat-sub"><span class="compat-behavior">Historical idempotent removal behavior for the removed production data handler.</span></span></span></summary>
80
- <div class="compat-evidence"><div class="compat-probe">Archived oracle observation; implementation removed.</div></div>
81
- </details>
82
- <details class="compat-row" data-status="unsupported">
83
- <summary class="compat-line"><span class="compat-dot" data-status="unsupported" role="img" aria-label="Not implemented yet" title="Not implemented yet"></span><span class="compat-main"><code class="compat-api">Removed rules fetch handler</code><span class="compat-sub"><span class="compat-behavior">Historical deployed-rules JSON round trip for the removed production fetch handler.</span></span></span></summary>
84
- <div class="compat-evidence"><div class="compat-probe">Archived oracle observation; implementation removed.</div></div>
85
- </details>
86
- <details class="compat-row" data-status="unsupported">
87
- <summary class="compat-line"><span class="compat-dot" data-status="unsupported" role="img" aria-label="Not implemented yet" title="Not implemented yet"></span><span class="compat-main"><code class="compat-api">Removed rules deployment handler</code><span class="compat-sub"><span class="compat-behavior">Historical rules deployment propagation timing for the removed production deploy handler.</span></span></span></summary>
88
- <div class="compat-evidence"><div class="compat-probe">Archived oracle observation; implementation removed.</div></div>
89
- </details>
90
- <details class="compat-row" data-status="unsupported">
91
- <summary class="compat-line"><span class="compat-dot" data-status="unsupported" role="img" aria-label="Not implemented yet" title="Not implemented yet"></span><span class="compat-main"><code class="compat-api">Removed REST crawler</code><span class="compat-sub"><span class="compat-behavior">Historical shallow REST response shape for the removed production crawler.</span></span></span></summary>
92
- <div class="compat-evidence"><div class="compat-probe">Archived oracle observation; implementation removed.</div></div>
93
- </details>
94
- </div>
95
-
96
- ## `simulateRtdbRules(compiled, input)` — in-process rule evaluator
97
-
98
- <div class="compat-list">
99
- <details class="compat-row" data-status="unsupported">
100
- <summary class="compat-line"><span class="compat-dot" data-status="unsupported" role="img" aria-label="Not implemented yet" title="Not implemented yet"></span><span class="compat-main"><code class="compat-api">simulateRtdbRules(compiled, input)</code><span class="compat-sub"><span class="compat-behavior">The removed stateful simulator returned a generate-before-simulate error when no IR had been generated</span></span></span></summary>
101
- <div class="compat-evidence"><div class="compat-probe">The stateless <code>simulateRtdbRules(compiled, input)</code> API requires a compiled rules tree and has no generate-before-simulate lifecycle.</div></div>
102
- </details>
103
- <details class="compat-row" data-status="ok">
104
- <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">simulateRtdbRules(compiled, input)</code><span class="compat-sub"><span class="compat-behavior">Returns <code>{ success: false, error: { code: 'INVALID_INPUT' } }</code> when input doesn't parse against <code>SimulationInputSchema</code> (e.g. path missing leading slash, operation not in read / write / validate)</span></span></span></summary>
105
- <div class="compat-evidence"><div class="compat-probe"><code>unit:simulation/handler.test.ts</code></div></div>
106
- </details>
107
- <details class="compat-row" data-status="ok">
108
- <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">simulateRtdbRules(compiled, input)</code><span class="compat-sub"><span class="compat-behavior">Walks ancestors from root → target; the first ancestor whose rule expression evaluates to <code>true</code> grants access — matches RTDB's documented "rules cascade from root, true at any ancestor grants" semantics</span></span></span></summary>
109
- <div class="compat-evidence"><div class="compat-probe"><code>unit:simulation/handler.test.ts</code></div></div>
110
- </details>
111
- <details class="compat-row" data-status="ok">
112
- <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">simulateRtdbRules(compiled, input)</code><span class="compat-sub"><span class="compat-behavior">Path variables (<code>$userId</code>) are bound from the URL path and exposed in <code>pathVariableBindings</code> (also without the <code>$</code> prefix for ergonomic access in expressions)</span></span></span></summary>
113
- <div class="compat-evidence"><div class="compat-probe"><code>unit:simulation/handler.test.ts</code></div></div>
114
- </details>
115
- <details class="compat-row" data-status="ok">
116
- <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">simulateRtdbRules(compiled, input)</code><span class="compat-sub"><span class="compat-behavior"><code>auth</code> context: when <code>null</code>, <code>auth</code> is null inside expressions; when present, <code>auth.uid</code> and <code>auth.token.*</code> are bound</span></span></span></summary>
117
- <div class="compat-evidence"><div class="compat-probe"><code>unit:simulation/handler.test.ts</code>, <code>unit:grammar/simulator.test.ts</code></div></div>
118
- </details>
119
- <details class="compat-row" data-status="ok">
120
- <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">simulateRtdbRules(compiled, input)</code><span class="compat-sub"><span class="compat-behavior"><code>mockData</code> becomes the value of <code>data</code> at every path during evaluation; <code>newData</code> is the proposed value for write/validate</span></span></span></summary>
121
- <div class="compat-evidence"><div class="compat-probe"><code>unit:simulation/handler.test.ts</code></div></div>
122
- </details>
123
- <details class="compat-row" data-status="ok">
124
- <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">simulateRtdbRules(compiled, input)</code><span class="compat-sub"><span class="compat-behavior"><code>data.child("…")</code>, <code>data.parent()</code>, <code>data.exists()</code>, <code>data.val()</code> evaluate against the in-process snapshot — matches the documented <code>DataSnapshot</code> rule-context surface</span></span></span></summary>
125
- <div class="compat-evidence"><div class="compat-probe"><code>unit:grammar/simulator.test.ts</code></div></div>
126
- </details>
127
- <details class="compat-row" data-status="diverged">
128
- <summary class="compat-line"><span class="compat-dot" data-status="diverged" role="img" aria-label="Diverged (documented)" title="Diverged (documented)"></span><span class="compat-main"><code class="compat-api">simulateRtdbRules(compiled, input)</code><span class="compat-sub"><span class="compat-behavior">Cross-path <code>root.child(…).val()</code> reads return <code>null</code> for paths NOT present in <code>mockData</code> — divergence from real prod rules where the engine reads the live database</span></span></span></summary>
129
- <div class="compat-evidence"><div class="compat-probe">divergence: the simulator uses ONLY what's in <code>mockData</code>. Real rules engine reads from the live RTDB. Documented in <code>validated.ts</code> ("simulation uses empty mockData, so cross-path rule lookups … will evaluate as false")</div></div>
130
- </details>
131
- <details class="compat-row" data-status="ok">
132
- <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">simulateRtdbRules(compiled, input)</code><span class="compat-sub"><span class="compat-behavior">An expression that fails to parse (<code>parsed.valid === false</code>) produces an unsupported result rather than silently granting or fabricating a deny</span></span></span></summary>
133
- <div class="compat-evidence"><div class="compat-probe"><code>unit:simulation/handler.test.ts</code></div></div>
134
- </details>
135
- <details class="compat-row" data-status="ok">
136
- <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">simulateRtdbRules(compiled, input)</code><span class="compat-sub"><span class="compat-behavior">When no ancestor rule allows, the result is <code>{ allowed: false }</code> with <code>matchedPath</code> set to the deepest matched node</span></span></span></summary>
137
- <div class="compat-evidence"><div class="compat-probe"><code>unit:simulation/handler.test.ts</code></div></div>
138
- </details>
139
- <details class="compat-row" data-status="ok">
140
- <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">simulateRtdbRules(compiled, input)</code><span class="compat-sub"><span class="compat-behavior">When NO ancestor has a rule for the operation at all, returns <code>{ success: false, error: { code: 'NO_MATCHING_RULE' } }</code></span></span></span></summary>
141
- <div class="compat-evidence"><div class="compat-probe"><code>unit:simulation/handler.test.ts</code></div></div>
142
- </details>
143
- <details class="compat-row" data-status="ok">
144
- <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">simulateRtdbRules(compiled, input)</code><span class="compat-sub"><span class="compat-behavior">Evaluation errors (grammar mismatch, unknown identifier) surface as <code>EVALUATION_ERROR</code></span></span></span></summary>
145
- <div class="compat-evidence"><div class="compat-probe"><code>unit:simulation/handler.test.ts</code></div></div>
146
- </details>
147
- <details class="compat-row" data-status="ok">
148
- <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">simulateRtdbRules(compiled, input)</code><span class="compat-sub"><span class="compat-behavior">Simulator's allow/deny decision matches the real RTDB rules engine for the same <code>{ rules, mockData, auth, operation, path, newData }</code> tuple, modulo the documented cross-path divergence on row #66</span></span></span></summary>
149
- <div class="compat-evidence"><div class="compat-probe">oracle: <code>packages/conformance/observations/rtdb/rtdb-simulator-vs-prod-agreement.json</code> — 8 test rules × 29 (rule, op) tuples; 28 agreements, 1 disagreement at capture time (<code>r4-validate-structure</code>: the simulator did not evaluate <code>.validate</code> on writes). The <code>.validate</code> walk is now implemented (<code>src/rules/rtdb/simulation/handler.ts</code>, reached from all backend write sites; grammar array-literals + <code>hasChildren(keys)</code> fixed alongside), closing the recorded disagreement — replayed as prod-conforming denial in <code>oracle-conformance.test.ts</code>. The frozen capture documents the historical divergence</div></div>
150
- </details>
151
- </div>
152
-
153
- ## Constraint authoring surface (`atoms` / `policies` / `compose` / `ruleset`)
154
-
155
- <div class="compat-list">
156
- <details class="compat-row" data-status="ok">
157
- <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior"><code>atoms</code> exports the documented set of primitive predicates (<code>authenticated</code>, <code>ownPath</code>, <code>ownField</code>, <code>isNew</code>, <code>hasChildren</code>, <code>hasChild</code>, <code>fieldIsString/Number/Boolean</code>, <code>fieldEnum</code>, <code>immutable</code>, <code>immutableSelf</code>, <code>rootExists</code>, <code>rootEquals</code>) — each returns an <code>Expr</code></span></span></summary>
158
- <div class="compat-evidence"><div class="compat-probe"><code>unit:constraints/atoms.test.ts</code></div></div>
159
- </details>
160
- <details class="compat-row" data-status="ok">
161
- <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior"><code>policies</code> exports composite predicates that compose atoms: <code>pathOwnerOnly</code>, <code>fieldOwnerOnly</code>, <code>ownerOrNew</code>, <code>hasRole</code>, <code>isMember</code>, <code>required</code>, <code>transition</code></span></span></summary>
162
- <div class="compat-evidence"><div class="compat-probe"><code>unit:constraints/policies.test.ts</code></div></div>
163
- </details>
164
- <details class="compat-row" data-status="ok">
165
- <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior"><code>compose</code> exports the boolean combinators <code>all</code>, <code>any</code>, <code>not</code>, <code>deny</code>, <code>always</code>, plus the raw <code>expr</code> constructor</span></span></summary>
166
- <div class="compat-evidence"><div class="compat-probe"><code>unit:constraints/compose.test.ts</code></div></div>
167
- </details>
168
- <details class="compat-row" data-status="ok">
169
- <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior"><code>ruleset(...)</code> builds an environment-independent compiled RTDB rules tree from path definitions + expression objects</span></span></summary>
170
- <div class="compat-evidence"><div class="compat-probe"><code>unit:constraints/ruleset.test.ts</code></div></div>
171
- </details>
172
- <details class="compat-row" data-status="ok">
173
- <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior">Game-domain helpers (<code>turnGuard</code>, <code>flip</code>, <code>winCheckHelper</code>) compose into legal rule expressions</span></span></summary>
174
- <div class="compat-evidence"><div class="compat-probe"><code>unit:constraints/game.test.ts</code></div></div>
175
- </details>
176
- </div>
177
-
178
- ## Compiled RTDB rules tree ↔ rules JSON
179
-
180
- <div class="compat-list">
181
- <details class="compat-row" data-status="ok">
182
- <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">compileRtdbRules</code><span class="compat-sub"><span class="compat-behavior"><code>compileRtdbRules(rulesJson)</code> produces an environment-independent tree where each node carries its path, parsed expressions, and child nodes</span></span></span></summary>
183
- <div class="compat-evidence"><div class="compat-probe"><code>unit:compiled-rules.test.ts</code></div></div>
184
- </details>
185
- <details class="compat-row" data-status="ok">
186
- <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">serializeRtdbRules</code><span class="compat-sub"><span class="compat-behavior"><code>serializeRtdbRules(compiled)</code> produces the Firebase rules-JSON payload for the compiled tree</span></span></span></summary>
187
- <div class="compat-evidence"><div class="compat-probe"><code>unit:compiled-rules.test.ts</code></div></div>
188
- </details>
189
- <details class="compat-row" data-status="ok">
190
- <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">compileRtdbRules / serializeRtdbRules</code><span class="compat-sub"><span class="compat-behavior">Round-trip <code>compileRtdbRules(serializeRtdbRules(compiled))</code> produces an equivalent rules tree (locked path/expression-text equality, not object identity)</span></span></span></summary>
191
- <div class="compat-evidence"><div class="compat-probe"><code>unit:compiled-rules.test.ts</code></div></div>
192
- </details>
193
- <details class="compat-row" data-status="ok">
194
- <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">compileRtdbRules / serializeRtdbRules</code><span class="compat-sub"><span class="compat-behavior">Path-variable segments (<code>$userId</code>, <code>$gameId</code>) preserved across the round-trip</span></span></span></summary>
195
- <div class="compat-evidence"><div class="compat-probe"><code>unit:compiled-rules.test.ts</code></div></div>
196
- </details>
197
- <details class="compat-row" data-status="ok">
198
- <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">compileRtdbRules / serializeRtdbRules</code><span class="compat-sub"><span class="compat-behavior"><code>.indexOn</code> arrays preserved across the round-trip</span></span></span></summary>
199
- <div class="compat-evidence"><div class="compat-probe"><code>unit:compiled-rules.test.ts</code></div></div>
200
- </details>
201
- </div>
202
-
203
- ## Modular SDK surface
204
-
205
- `pyric/database` is the sandbox-only mirror of `firebase/database`'s
206
- tree-shakable free-function shape (`getDatabase`, `ref`, `child`, `get`,
207
- `set`, `update`, `remove`, `push`, listeners, queries, transactions, and
208
- sentinels). Package resolution selects production or sandbox before either
209
- module loads; this mirror has no production target or runtime dependency on
210
- `firebase/database`.
211
-
212
- Two sandbox identity modes are selected by the value passed to
213
- `getDatabase`:
214
-
215
- - **Sandbox** — `getDatabase(ctx: SandboxContext)`. Frozen identity baked into
216
- the handle at construction.
217
- - **Sandbox-live** — `getDatabase(sandbox: Sandbox)`. Identity read per
218
- operation from `sandbox.currentUser`.
219
-
220
- The public implementation is split by API family under `packages/pyric/src/database/`; the
221
- in-process backend lives in `packages/pyric/src/database/sandbox/`. Rows below
222
- are scoped to the modular mirror. The pure rules-engine rows above are internal
223
- tooling coverage and are not exports of `pyric/database`.
26
+ `pyric/database` mirrors the public `firebase/database` API in the Pyric sandbox. The rows below compare its observable behaviour with Firebase.
224
27
 
225
28
  <div class="compat-list">
226
29
  <details class="compat-row" data-status="ok">
227
30
  <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior"><code>getDatabase(ctx)</code> builds a sandbox-target <code>Database</code>; frozen <code>ctx.auth</code> baked in</span></span></summary>
228
- <div class="compat-evidence"><div class="compat-probe"><code>unit:modular/sandbox-target.test.ts</code> ("getDatabase(ctx) returns a tagged Database handle")</div></div>
31
+ <div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/modular/sandbox-target.test.ts#L11" target="_blank" rel="noopener noreferrer"><code>unit:modular/sandbox-target.test.ts</code></a> ("getDatabase(ctx) returns a tagged Database handle")</div></div>
229
32
  </details>
230
33
  <details class="compat-row" data-status="ok">
231
34
  <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior"><code>getDatabase(sandbox)</code> builds a sandbox-live target; reads <code>sandbox.currentUser</code> per op</span></span></summary>
232
- <div class="compat-evidence"><div class="compat-probe"><code>unit:modular/sandbox-target.test.ts</code> ("reads sandbox.currentUser at op time, not at getDatabase time")</div></div>
233
- </details>
234
- <details class="compat-row" data-status="unsupported">
235
- <summary class="compat-line"><span class="compat-dot" data-status="unsupported" role="img" aria-label="Not implemented yet" title="Not implemented yet"></span><span class="compat-main"><span class="compat-behavior">An in-module production target is intentionally absent; direct calls with a real <code>FirebaseApp</code> reject with package-resolution guidance</span></span></summary>
236
- <div class="compat-evidence"><div class="compat-probe"><code>unit:modular.test.ts</code>; production remains the responsibility of the unchanged <code>firebase/database</code> package</div></div>
35
+ <div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/modular/sandbox-target.test.ts#L11" target="_blank" rel="noopener noreferrer"><code>unit:modular/sandbox-target.test.ts</code></a> ("reads sandbox.currentUser at op time, not at getDatabase time")</div></div>
237
36
  </details>
238
37
  <details class="compat-row" data-status="ok">
239
38
  <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior"><code>ref(db, path?)</code> returns a path-tagged <code>DatabaseReference</code>; default is root</span></span></summary>
240
- <div class="compat-evidence"><div class="compat-probe"><code>unit:modular/sandbox-target.test.ts</code> ("ref(db) returns a root ref" + "ref(db, ...) returns a path ref")</div></div>
39
+ <div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/modular/sandbox-target.test.ts#L12" target="_blank" rel="noopener noreferrer"><code>unit:modular/sandbox-target.test.ts</code></a> ("ref(db) returns a root ref" + "ref(db, ...) returns a path ref")</div></div>
241
40
  </details>
242
41
  <details class="compat-row" data-status="ok">
243
42
  <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior"><code>child(ref, 'sub/path')</code> composes paths; result inherits the parent's target</span></span></summary>
244
- <div class="compat-evidence"><div class="compat-probe"><code>unit:modular/sandbox-target.test.ts</code> ("child(ref, 'sub') composes paths")</div></div>
43
+ <div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/modular/sandbox-target.test.ts#L12" target="_blank" rel="noopener noreferrer"><code>unit:modular/sandbox-target.test.ts</code></a> ("child(ref, 'sub') composes paths")</div></div>
245
44
  </details>
246
45
  <details class="compat-row" data-status="ok">
247
46
  <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior"><code>ref.parent</code> returns the parent ref; <code>root.parent === null</code></span></span></summary>
248
- <div class="compat-evidence"><div class="compat-probe"><code>unit:modular/sandbox-target.test.ts</code> ("ref.parent returns the parent ref; root.parent is null")</div></div>
47
+ <div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/modular/sandbox-target.test.ts#L12" target="_blank" rel="noopener noreferrer"><code>unit:modular/sandbox-target.test.ts</code></a> ("ref.parent returns the parent ref; root.parent is null")</div></div>
249
48
  </details>
250
49
  <details class="compat-row" data-status="ok">
251
50
  <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior"><code>ref.root</code> returns the root ref of the same target</span></span></summary>
252
- <div class="compat-evidence"><div class="compat-probe"><code>unit:modular/sandbox-target.test.ts</code> ("ref.root returns the root ref")</div></div>
51
+ <div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/modular/sandbox-target.test.ts#L12" target="_blank" rel="noopener noreferrer"><code>unit:modular/sandbox-target.test.ts</code></a> ("ref.root returns the root ref")</div></div>
253
52
  </details>
254
53
  <details class="compat-row" data-status="ok">
255
54
  <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior"><code>get(ref)</code> returns a <code>DataSnapshot</code>-shaped object with <code>val()</code>, <code>exists()</code>, <code>key</code>, <code>child()</code>, <code>hasChildren()</code>, <code>size</code>, and <code>toJSON()</code>; the modular Web surface does not expose <code>numChildren()</code>.</span></span></summary>
256
- <div class="compat-evidence"><div class="compat-probe"><code>unit:modular/sandbox-target.test.ts</code> (snapshot shape tests)</div></div>
55
+ <div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/modular/sandbox-target.test.ts#L2" target="_blank" rel="noopener noreferrer"><code>unit:modular/sandbox-target.test.ts</code></a> (snapshot shape tests)</div></div>
257
56
  </details>
258
57
  <details class="compat-row" data-status="ok">
259
58
  <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior"><code>get</code> on an absent path resolves to <code>{ val: null, exists: false }</code> (matches <code>DataSnapshot.val()</code> contract)</span></span></summary>
260
- <div class="compat-evidence"><div class="compat-probe"><code>unit:modular/sandbox-target.test.ts</code> ("reads return null for an absent path")</div></div>
59
+ <div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/modular/sandbox-target.test.ts#L2" target="_blank" rel="noopener noreferrer"><code>unit:modular/sandbox-target.test.ts</code></a> ("reads return null for an absent path")</div></div>
261
60
  </details>
262
61
  <details class="compat-row" data-status="ok">
263
62
  <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior"><code>set(ref, value)</code> replaces the value at the path</span></span></summary>
264
- <div class="compat-evidence"><div class="compat-probe">Sandbox aligned: <code>unit:modular/sandbox-target.test.ts</code> ("round-trips a primitive value" + "round-trips nested objects"); matches oracle observation <code>packages/conformance/observations/rtdb/rtdb-set-then-get-roundtrip.json</code> (prod observation blocked on rules; sandbox locks the contract directly)</div></div>
63
+ <div class="compat-evidence"><div class="compat-probe">Sandbox aligned: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/modular/sandbox-target.test.ts#L119" target="_blank" rel="noopener noreferrer"><code>unit:modular/sandbox-target.test.ts</code></a> ("round-trips a primitive value" + "round-trips nested objects"); matches oracle observation <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/rtdb/rtdb-set-then-get-roundtrip.json" target="_blank" rel="noopener noreferrer"><code>packages/conformance/observations/rtdb/rtdb-set-then-get-roundtrip.json</code></a> (prod observation blocked on rules; sandbox locks the contract directly)</div></div>
265
64
  </details>
266
65
  <details class="compat-row" data-status="ok">
267
66
  <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior"><code>set(ref, null)</code> deletes the subtree at the path</span></span></summary>
268
- <div class="compat-evidence"><div class="compat-probe">Sandbox aligned: <code>unit:modular/sandbox-target.test.ts</code> ("set(ref, null) deletes the path"); matches oracle observation <code>packages/conformance/observations/rtdb/rtdb-remove-vs-set-null.json</code></div></div>
67
+ <div class="compat-evidence"><div class="compat-probe">Sandbox aligned: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/modular/sandbox-target.test.ts#L154" target="_blank" rel="noopener noreferrer"><code>unit:modular/sandbox-target.test.ts</code></a> ("set(ref, null) deletes the path"); matches oracle observation <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/rtdb/rtdb-remove-vs-set-null.json" target="_blank" rel="noopener noreferrer"><code>packages/conformance/observations/rtdb/rtdb-remove-vs-set-null.json</code></a></div></div>
269
68
  </details>
270
69
  <details class="compat-row" data-status="ok">
271
70
  <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior"><code>remove(ref)</code> is equivalent to <code>set(ref, null)</code> (same end state)</span></span></summary>
272
- <div class="compat-evidence"><div class="compat-probe">Sandbox aligned: <code>unit:modular/sandbox-target.test.ts</code> ("remove and set(null) produce identical end-state"); matches oracle observation <code>packages/conformance/observations/rtdb/rtdb-remove-vs-set-null.json</code></div></div>
71
+ <div class="compat-evidence"><div class="compat-probe">Sandbox aligned: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/modular/sandbox-target.test.ts#L154" target="_blank" rel="noopener noreferrer"><code>unit:modular/sandbox-target.test.ts</code></a> ("remove and set(null) produce identical end-state"); matches oracle observation <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/rtdb/rtdb-remove-vs-set-null.json" target="_blank" rel="noopener noreferrer"><code>packages/conformance/observations/rtdb/rtdb-remove-vs-set-null.json</code></a></div></div>
273
72
  </details>
274
73
  <details class="compat-row" data-status="ok">
275
74
  <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior"><code>update(ref, patch)</code> shallow-merges top-level keys at the ref's path</span></span></summary>
276
- <div class="compat-evidence"><div class="compat-probe"><code>unit:modular/sandbox-target.test.ts</code> ("shallow-merges top-level keys")</div></div>
75
+ <div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/modular/sandbox-target.test.ts#L12" target="_blank" rel="noopener noreferrer"><code>unit:modular/sandbox-target.test.ts</code></a> ("shallow-merges top-level keys")</div></div>
277
76
  </details>
278
77
  <details class="compat-row" data-status="ok">
279
78
  <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior"><code>null</code> value in a shallow update deletes that key</span></span></summary>
280
- <div class="compat-evidence"><div class="compat-probe"><code>unit:modular/sandbox-target.test.ts</code> ("null values in a shallow update delete the key")</div></div>
79
+ <div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/modular/sandbox-target.test.ts#L16" target="_blank" rel="noopener noreferrer"><code>unit:modular/sandbox-target.test.ts</code></a> ("null values in a shallow update delete the key")</div></div>
281
80
  </details>
282
81
  <details class="compat-row" data-status="ok">
283
82
  <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior"><code>update(rootRef, { '/a/x': v1, '/b/y': v2 })</code> is a multi-path atomic write — all paths land or none do</span></span></summary>
284
- <div class="compat-evidence"><div class="compat-probe"><code>unit:upstream-rtdb-probes.test.ts</code> ("one update nulls, mutates, and displaces within a limitToFirst window") + <code>unit:modular/sandbox-target.test.ts</code> (atomic multipath + rules denial); matches the matrix #23 prod contract</div></div>
83
+ <div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/upstream-rtdb-probes.test.ts#L10" target="_blank" rel="noopener noreferrer"><code>unit:upstream-rtdb-probes.test.ts</code></a> ("one update nulls, mutates, and displaces within a limitToFirst window") + <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/modular/sandbox-target.test.ts#L16" target="_blank" rel="noopener noreferrer"><code>unit:modular/sandbox-target.test.ts</code></a> (atomic multipath + rules denial); matches the matrix #23 prod contract</div></div>
285
84
  </details>
286
85
  <details class="compat-row" data-status="ok">
287
86
  <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior">Overlapping multi-path updates (one path is a descendant of another) reject before any path is written</span></span></summary>
288
- <div class="compat-evidence"><div class="compat-probe"><code>unit:modular/sandbox-target.test.ts</code> ("rejects overlapping paths")</div></div>
87
+ <div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/modular/sandbox-target.test.ts#L16" target="_blank" rel="noopener noreferrer"><code>unit:modular/sandbox-target.test.ts</code></a> ("rejects overlapping paths")</div></div>
289
88
  </details>
290
89
  <details class="compat-row" data-status="ok">
291
90
  <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior"><code>push(ref)</code> mints a 20-char auto-id key starting with <code>-</code>, lexicographically sortable</span></span></summary>
292
- <div class="compat-evidence"><div class="compat-probe">Sandbox aligned: <code>unit:modular/sandbox-target.test.ts</code> ("mints 20-char keys starting with \"-\"" + "sequential push keys are lex-sortable"); matches oracle observation <code>packages/conformance/observations/rtdb/rtdb-push-autoid-format.json</code></div></div>
91
+ <div class="compat-evidence"><div class="compat-probe">Sandbox aligned: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/modular/sandbox-target.test.ts#L340" target="_blank" rel="noopener noreferrer"><code>unit:modular/sandbox-target.test.ts</code></a> ("mints 20-char keys starting with \"-\"" + "sequential push keys are lex-sortable"); matches oracle observation <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/rtdb/rtdb-push-autoid-format.json" target="_blank" rel="noopener noreferrer"><code>packages/conformance/observations/rtdb/rtdb-push-autoid-format.json</code></a></div></div>
293
92
  </details>
294
93
  <details class="compat-row" data-status="ok">
295
94
  <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior"><code>push(ref, value)</code> writes <code>value</code> at the new child path</span></span></summary>
296
- <div class="compat-evidence"><div class="compat-probe"><code>unit:modular/sandbox-target.test.ts</code> ("push(ref, value) writes the value at the new child path")</div></div>
95
+ <div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/modular/sandbox-target.test.ts#L12" target="_blank" rel="noopener noreferrer"><code>unit:modular/sandbox-target.test.ts</code></a> ("push(ref, value) writes the value at the new child path")</div></div>
297
96
  </details>
298
97
  <details class="compat-row" data-status="ok">
299
98
  <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior"><code>pushKey()</code> mints a fresh push-shaped key without writing — used by callers building multi-path updates that need the key first</span></span></summary>
300
- <div class="compat-evidence"><div class="compat-probe"><code>unit:modular/sandbox-target.test.ts</code> ("pushKey() mints a fresh key without writing")</div></div>
99
+ <div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/modular/sandbox-target.test.ts#L19" target="_blank" rel="noopener noreferrer"><code>unit:modular/sandbox-target.test.ts</code></a> ("pushKey() mints a fresh key without writing")</div></div>
301
100
  </details>
302
101
  <details class="compat-row" data-status="ok">
303
102
  <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior"><code>serverTimestamp()</code> returns the <code>{ ".sv": "timestamp" }</code> sentinel marker the wire encoder recognises</span></span></summary>
304
- <div class="compat-evidence"><div class="compat-probe">Sandbox aligned: <code>unit:modular/sandbox-target.test.ts</code> ("serverTimestamp() returns the documented shape"); matches the prod wire contract</div></div>
103
+ <div class="compat-evidence"><div class="compat-probe">Sandbox aligned: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/modular/sandbox-target.test.ts#L21" target="_blank" rel="noopener noreferrer"><code>unit:modular/sandbox-target.test.ts</code></a> ("serverTimestamp() returns the documented shape"); matches the prod wire contract</div></div>
305
104
  </details>
306
105
  <details class="compat-row" data-status="ok">
307
106
  <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior"><code>serverTimestamp()</code> resolves to a number (epoch ms) on read-back</span></span></summary>
308
- <div class="compat-evidence"><div class="compat-probe">Sandbox aligned: <code>unit:modular/sandbox-target.test.ts</code> ("resolves to a number on read-back"); matches oracle observation <code>packages/conformance/observations/rtdb/rtdb-servertimestamp-resolves.json</code> (prod observation blocked on rules; sandbox locks the contract directly)</div></div>
107
+ <div class="compat-evidence"><div class="compat-probe">Sandbox aligned: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/modular/sandbox-target.test.ts#L376" target="_blank" rel="noopener noreferrer"><code>unit:modular/sandbox-target.test.ts</code></a> ("resolves to a number on read-back"); matches oracle observation <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/rtdb/rtdb-servertimestamp-resolves.json" target="_blank" rel="noopener noreferrer"><code>packages/conformance/observations/rtdb/rtdb-servertimestamp-resolves.json</code></a> (prod observation blocked on rules; sandbox locks the contract directly)</div></div>
309
108
  </details>
310
109
  <details class="compat-row" data-status="ok">
311
110
  <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior"><code>serverTimestamp()</code> sentinels resolve when nested inside multi-path update payloads</span></span></summary>
312
- <div class="compat-evidence"><div class="compat-probe"><code>unit:modular/sandbox-target.test.ts</code> ("resolves sentinels nested deep inside an update payload")</div></div>
111
+ <div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/modular/sandbox-target.test.ts#L16" target="_blank" rel="noopener noreferrer"><code>unit:modular/sandbox-target.test.ts</code></a> ("resolves sentinels nested deep inside an update payload")</div></div>
313
112
  </details>
314
113
  <details class="compat-row" data-status="ok">
315
114
  <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior">Rules-denied write throws a plain <code>Error</code> (NOT a <code>FirebaseError</code>) with <code>.code === 'PERMISSION_DENIED'</code> (uppercase snake-case) and <code>.message === 'PERMISSION_DENIED: Permission denied'</code></span></span></summary>
316
- <div class="compat-evidence"><div class="compat-probe">Sandbox aligned: <code>unit:modular/sandbox-target.test.ts</code> ("rules-denied set throws a plain Error with PERMISSION_DENIED code"); matches oracle observation <code>packages/conformance/observations/rtdb/rtdb-rules-denied-error-code.json</code> (against blockingfun, fb-js-sdk 12.13.0)</div></div>
115
+ <div class="compat-evidence"><div class="compat-probe">Sandbox aligned: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/modular/sandbox-target.test.ts#L407" target="_blank" rel="noopener noreferrer"><code>unit:modular/sandbox-target.test.ts</code></a> ("rules-denied set throws a plain Error with PERMISSION_DENIED code"); matches oracle observation <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/rtdb/rtdb-rules-denied-error-code.json" target="_blank" rel="noopener noreferrer"><code>packages/conformance/observations/rtdb/rtdb-rules-denied-error-code.json</code></a> (against blockingfun, fb-js-sdk 12.13.0)</div></div>
317
116
  </details>
318
117
  <details class="compat-row" data-status="ok">
319
118
  <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior">Rules-denied read throws the same plain-<code>Error</code> <code>PERMISSION_DENIED</code> shape as a denied write</span></span></summary>
320
- <div class="compat-evidence"><div class="compat-probe">Sandbox aligned: <code>unit:modular/sandbox-target.test.ts</code> ("rules-denied get throws the same plain Error shape"); matches oracle observation <code>packages/conformance/observations/rtdb/rtdb-rules-denied-error-code.json</code></div></div>
119
+ <div class="compat-evidence"><div class="compat-probe">Sandbox aligned: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/modular/sandbox-target.test.ts#L407" target="_blank" rel="noopener noreferrer"><code>unit:modular/sandbox-target.test.ts</code></a> ("rules-denied get throws the same plain Error shape"); matches oracle observation <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/rtdb/rtdb-rules-denied-error-code.json" target="_blank" rel="noopener noreferrer"><code>packages/conformance/observations/rtdb/rtdb-rules-denied-error-code.json</code></a></div></div>
321
120
  </details>
322
121
  <details class="compat-row" data-status="ok">
323
122
  <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior">Rules-denied remove throws the same plain-<code>Error</code> <code>PERMISSION_DENIED</code> shape</span></span></summary>
324
- <div class="compat-evidence"><div class="compat-probe">Sandbox aligned: <code>unit:modular/sandbox-target.test.ts</code> ("rules-denied remove throws the same plain Error shape")</div></div>
123
+ <div class="compat-evidence"><div class="compat-probe">Sandbox aligned: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/modular/sandbox-target.test.ts#L18" target="_blank" rel="noopener noreferrer"><code>unit:modular/sandbox-target.test.ts</code></a> ("rules-denied remove throws the same plain Error shape")</div></div>
325
124
  </details>
326
125
  <details class="compat-row" data-status="ok">
327
126
  <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior"><code>onValue(ref, cb)</code> fires immediately on subscribe with the current value at the path</span></span></summary>
328
- <div class="compat-evidence"><div class="compat-probe"><code>unit:modular/sandbox-target.test.ts</code> ("fires on subscribe with the current value")</div></div>
127
+ <div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/modular/sandbox-target.test.ts#L12" target="_blank" rel="noopener noreferrer"><code>unit:modular/sandbox-target.test.ts</code></a> ("fires on subscribe with the current value")</div></div>
329
128
  </details>
330
129
  <details class="compat-row" data-status="ok">
331
130
  <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior"><code>onValue</code> fires again after every write that CHANGES the value at the watched path; a write that leaves the watched subtree byte-identical (a no-change re-write, or an ancestor/descendant write that doesn't alter this path) is suppressed (DB-B8)</span></span></summary>
332
- <div class="compat-evidence"><div class="compat-probe"><code>unit:modular/sandbox-target.test.ts</code> ("fires after every write that touches the watched path") + <code>unit:modular/no-change-suppression.test.ts</code> ("re-writing the same value does NOT re-fire" + "ancestor write leaving the subtree unchanged does NOT fire")</div></div>
131
+ <div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/modular/sandbox-target.test.ts#L22" target="_blank" rel="noopener noreferrer"><code>unit:modular/sandbox-target.test.ts</code></a> ("fires after every write that touches the watched path") + <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/modular/no-change-suppression.test.ts#L17" target="_blank" rel="noopener noreferrer"><code>unit:modular/no-change-suppression.test.ts</code></a> ("re-writing the same value does NOT re-fire" + "ancestor write leaving the subtree unchanged does NOT fire")</div></div>
333
132
  </details>
334
133
  <details class="compat-row" data-status="ok">
335
134
  <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior"><code>onValue</code> fires after a descendant write (the listener sees subtree changes)</span></span></summary>
336
- <div class="compat-evidence"><div class="compat-probe"><code>unit:modular/sandbox-target.test.ts</code> ("fires on a descendant write")</div></div>
135
+ <div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/modular/sandbox-target.test.ts#L22" target="_blank" rel="noopener noreferrer"><code>unit:modular/sandbox-target.test.ts</code></a> ("fires on a descendant write")</div></div>
337
136
  </details>
338
137
  <details class="compat-row" data-status="ok">
339
138
  <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior"><code>onValue</code> initial-fire for an absent path delivers <code>val=null, exists=false</code> (matches matrix expectation locked by oracle for sentinel/listener shape)</span></span></summary>
340
- <div class="compat-evidence"><div class="compat-probe"><code>unit:modular/sandbox-target.test.ts</code> ("absent path: initial fire delivers val=null, exists=false")</div></div>
139
+ <div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/modular/sandbox-target.test.ts#L22" target="_blank" rel="noopener noreferrer"><code>unit:modular/sandbox-target.test.ts</code></a> ("absent path: initial fire delivers val=null, exists=false")</div></div>
341
140
  </details>
342
141
  <details class="compat-row" data-status="ok">
343
142
  <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior">The <code>onValue</code> return value is an unsubscribe function; calling it stops further fires</span></span></summary>
344
- <div class="compat-evidence"><div class="compat-probe"><code>unit:modular/sandbox-target.test.ts</code> ("fires after every write that touches the watched path" — checks unsubscribed listener doesn't fire on subsequent write)</div></div>
143
+ <div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/modular/sandbox-target.test.ts#L22" target="_blank" rel="noopener noreferrer"><code>unit:modular/sandbox-target.test.ts</code></a> ("fires after every write that touches the watched path" — checks unsubscribed listener doesn't fire on subsequent write)</div></div>
345
144
  </details>
346
145
  <details class="compat-row" data-status="ok">
347
146
  <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior"><code>onChildAdded</code> / <code>onChildChanged</code> / <code>onChildRemoved</code> / <code>onChildMoved</code> — plain-ref subscription surface</span></span></summary>
348
- <div class="compat-evidence"><div class="compat-probe"><code>unit:modular/sandbox-child-events.test.ts</code> exercises all four plain-reference child listener registrars; M41–M48 own their per-event oracle claims.</div></div>
147
+ <div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/modular/sandbox-child-events.test.ts#L14" target="_blank" rel="noopener noreferrer"><code>unit:modular/sandbox-child-events.test.ts</code></a> exercises all four plain-reference child listener registrars; M41–M48 own their per-event oracle claims.</div></div>
349
148
  </details>
350
149
  <details class="compat-row" data-status="ok">
351
150
  <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior"><code>onChildAdded</code> replays each existing direct child of the parent ref on subscribe (one fire per existing key)</span></span></summary>
352
- <div class="compat-evidence"><div class="compat-probe">Sandbox aligned: <code>unit:modular/sandbox-child-events.test.ts</code> ("replays existing direct children on subscribe — one fire per key"); matches oracle observation <code>packages/conformance/observations/rtdb-modular/rtdb-modular-onchildadded-initial-replay.json</code> (seeded <code>{k1,k2,k3}</code>, observed <code>firedKeys: ['k1','k2','k3']</code>).</div></div>
151
+ <div class="compat-evidence"><div class="compat-probe">Sandbox aligned: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/modular/sandbox-child-events.test.ts#L40" target="_blank" rel="noopener noreferrer"><code>unit:modular/sandbox-child-events.test.ts</code></a> ("replays existing direct children on subscribe — one fire per key"); matches oracle observation <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/rtdb-modular/rtdb-modular-onchildadded-initial-replay.json" target="_blank" rel="noopener noreferrer"><code>packages/conformance/observations/rtdb-modular/rtdb-modular-onchildadded-initial-replay.json</code></a> (seeded <code>{k1,k2,k3}</code>, observed <code>firedKeys: ['k1','k2','k3']</code>).</div></div>
353
152
  </details>
354
153
  <details class="compat-row" data-status="ok">
355
154
  <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior">After subscribe, <code>onChildAdded</code> fires exactly once per new direct child write; snapshot carries <code>{key, val}</code> of the new child</span></span></summary>
356
- <div class="compat-evidence"><div class="compat-probe">Sandbox aligned: <code>unit:modular/sandbox-child-events.test.ts</code> ("fires exactly once per NEW direct child after subscribe"); matches oracle observation <code>packages/conformance/observations/rtdb-modular/rtdb-modular-onchildadded-post-subscribe.json</code> (<code>postSubscribeFires: 1</code>, <code>lastFire: {key:'k3', val:{v:3}}</code>).</div></div>
155
+ <div class="compat-evidence"><div class="compat-probe">Sandbox aligned: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/modular/sandbox-child-events.test.ts#L66" target="_blank" rel="noopener noreferrer"><code>unit:modular/sandbox-child-events.test.ts</code></a> ("fires exactly once per NEW direct child after subscribe"); matches oracle observation <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/rtdb-modular/rtdb-modular-onchildadded-post-subscribe.json" target="_blank" rel="noopener noreferrer"><code>packages/conformance/observations/rtdb-modular/rtdb-modular-onchildadded-post-subscribe.json</code></a> (<code>postSubscribeFires: 1</code>, <code>lastFire: {key:'k3', val:{v:3}}</code>).</div></div>
357
156
  </details>
358
157
  <details class="compat-row" data-status="ok">
359
158
  <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior"><code>onChildChanged</code> has NO initial replay; fires once when an existing direct child's value transitions; snapshot carries the NEW value</span></span></summary>
360
- <div class="compat-evidence"><div class="compat-probe">Sandbox aligned: <code>unit:modular/sandbox-child-events.test.ts</code> ("does NOT fire on subscribe (no initial replay)" + "fires once when an existing child transitions to a new value; snapshot carries NEW val"); matches oracle observation <code>packages/conformance/observations/rtdb-modular/rtdb-modular-onchildchanged-fires-on-update.json</code> (<code>firedOnInitial: 0, firedOnUpdate: 1, lastFire: {key:'k1', val:{v:2}}</code>).</div></div>
159
+ <div class="compat-evidence"><div class="compat-probe">Sandbox aligned: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/modular/sandbox-child-events.test.ts#L99" target="_blank" rel="noopener noreferrer"><code>unit:modular/sandbox-child-events.test.ts</code></a> ("does NOT fire on subscribe (no initial replay)" + "fires once when an existing child transitions to a new value; snapshot carries NEW val"); matches oracle observation <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/rtdb-modular/rtdb-modular-onchildchanged-fires-on-update.json" target="_blank" rel="noopener noreferrer"><code>packages/conformance/observations/rtdb-modular/rtdb-modular-onchildchanged-fires-on-update.json</code></a> (<code>firedOnInitial: 0, firedOnUpdate: 1, lastFire: {key:'k1', val:{v:2}}</code>).</div></div>
361
160
  </details>
362
161
  <details class="compat-row" data-status="ok">
363
162
  <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior"><code>onChildChanged</code> does NOT fire for added or removed children — those go to the other event listeners</span></span></summary>
364
- <div class="compat-evidence"><div class="compat-probe">Sandbox aligned: <code>unit:modular/sandbox-child-events.test.ts</code> ("does NOT fire when a child is added" + "does NOT fire when a child is removed").</div></div>
163
+ <div class="compat-evidence"><div class="compat-probe">Sandbox aligned: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/modular/sandbox-child-events.test.ts#L19" target="_blank" rel="noopener noreferrer"><code>unit:modular/sandbox-child-events.test.ts</code></a> ("does NOT fire when a child is added" + "does NOT fire when a child is removed").</div></div>
365
164
  </details>
366
165
  <details class="compat-row" data-status="ok">
367
166
  <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior"><code>onChildRemoved</code> has NO initial replay; fires once when a direct child is deleted (via <code>remove(child)</code> or <code>set(child, null)</code>); snapshot carries the PRIOR (now-removed) value</span></span></summary>
368
- <div class="compat-evidence"><div class="compat-probe"><code>unit:upstream-rtdb-probes.test.ts</code> (parent wipe fan-out via remove(parent) / set(parent, scalar)) + <code>unit:modular/sandbox-child-events.test.ts</code> (single-child delete carries PRIOR val); matches oracle <code>rtdb-modular-onchildremoved-fires-on-delete.json</code></div></div>
167
+ <div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/upstream-rtdb-probes.test.ts#L8" target="_blank" rel="noopener noreferrer"><code>unit:upstream-rtdb-probes.test.ts</code></a> (parent wipe fan-out via remove(parent) / set(parent, scalar)) + <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/modular/sandbox-child-events.test.ts#L143" target="_blank" rel="noopener noreferrer"><code>unit:modular/sandbox-child-events.test.ts</code></a> (single-child delete carries PRIOR val); matches oracle <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/rtdb-modular/rtdb-modular-onchildremoved-fires-on-delete.json" target="_blank" rel="noopener noreferrer"><code>rtdb-modular-onchildremoved-fires-on-delete.json</code></a></div></div>
369
168
  </details>
370
169
  <details class="compat-row" data-status="ok">
371
170
  <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior">A plain-reference <code>onChildMoved</code> uses Firebase's default priority index: ordinary value changes that preserve priority do not move, while each changed child's priority emits one event even if its predecessor stays the same</span></span></summary>
372
- <div class="compat-evidence"><div class="compat-probe">Oracle <code>rtdb-modular-priority-contract</code> captures the same one-event-per-priority-operation sequence on a plain ref and an explicit <code>orderByPriority()</code> query, including a priority change that retains the same predecessor; <code>unit:modular/sandbox-child-events.test.ts</code> separately pins no movement for an ordinary value-only change.</div></div>
171
+ <div class="compat-evidence"><div class="compat-probe">Oracle <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/rtdb-modular/rtdb-modular-priority-contract.json" target="_blank" rel="noopener noreferrer"><code>rtdb-modular-priority-contract</code></a> captures the same one-event-per-priority-operation sequence on a plain ref and an explicit <code>orderByPriority()</code> query, including a priority change that retains the same predecessor; <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/modular/sandbox-child-events.test.ts#L14" target="_blank" rel="noopener noreferrer"><code>unit:modular/sandbox-child-events.test.ts</code></a> separately pins no movement for an ordinary value-only change. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/modular/priority-metadata-cdd.test.ts#L20" target="_blank" rel="noopener noreferrer"><code>priority-metadata-cdd.test.ts</code></a>)</div></div>
373
172
  </details>
374
173
  <details class="compat-row" data-status="ok">
375
174
  <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior"><code>off(ref)</code> (no event type) removes ALL listeners at that ref — value + every child event variety</span></span></summary>
376
- <div class="compat-evidence"><div class="compat-probe">Sandbox aligned: <code>unit:modular/sandbox-child-events.test.ts</code> ("off(ref) removes ALL listeners at the ref" + "off(ref) also removes value listeners at the same path"); matches oracle observation <code>packages/conformance/observations/rtdb-modular/rtdb-modular-off-stops-child-fires.json</code> (<code>postOffFires: 0</code>).</div></div>
175
+ <div class="compat-evidence"><div class="compat-probe">Sandbox aligned: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/modular/sandbox-child-events.test.ts#L338" target="_blank" rel="noopener noreferrer"><code>unit:modular/sandbox-child-events.test.ts</code></a> ("off(ref) removes ALL listeners at the ref" + "off(ref) also removes value listeners at the same path"); matches oracle observation <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/rtdb-modular/rtdb-modular-off-stops-child-fires.json" target="_blank" rel="noopener noreferrer"><code>packages/conformance/observations/rtdb-modular/rtdb-modular-off-stops-child-fires.json</code></a> (<code>postOffFires: 0</code>).</div></div>
377
176
  </details>
378
177
  <details class="compat-row" data-status="ok">
379
178
  <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior"><code>off(ref, eventType?, callback?)</code> variants: <code>off(ref, 'value')</code> / <code>off(ref, 'child_added')</code> / <code>off(ref, eventType, cb)</code> remove the targeted subset; returned-unsubscribe from <code>onChild*</code> is equivalent to <code>off(ref, eventType, cb)</code></span></span></summary>
380
- <div class="compat-evidence"><div class="compat-probe">Sandbox aligned: <code>unit:modular/sandbox-child-events.test.ts</code> ("off(ref, \"child_added\") removes only that event variety" + "off(ref, \"value\") removes only value listeners" + "off(ref, eventType, cb) removes only the matching callback" + "returned-unsubscribe from onChildAdded is functionally equivalent to off()").</div></div>
179
+ <div class="compat-evidence"><div class="compat-probe">Sandbox aligned: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/modular/sandbox-child-events.test.ts#L14" target="_blank" rel="noopener noreferrer"><code>unit:modular/sandbox-child-events.test.ts</code></a> ("off(ref, \"child_added\") removes only that event variety" + "off(ref, \"value\") removes only value listeners" + "off(ref, eventType, cb) removes only the matching callback" + "returned-unsubscribe from onChildAdded is functionally equivalent to off()").</div></div>
381
180
  </details>
382
181
  <details class="compat-row" data-status="ok">
383
182
  <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior"><code>connectDatabaseEmulator(db, host, port)</code> is a no-op on sandbox targets (the sandbox IS a local emulator)</span></span></summary>
384
- <div class="compat-evidence"><div class="compat-probe"><code>unit:modular/sandbox-target.test.ts</code> ("is a no-op on sandbox handles")</div></div>
183
+ <div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/modular/sandbox-target.test.ts#L23" target="_blank" rel="noopener noreferrer"><code>unit:modular/sandbox-target.test.ts</code></a> ("is a no-op on sandbox handles")</div></div>
385
184
  </details>
386
185
  <details class="compat-row" data-status="ok">
387
186
  <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior"><code>sandbox.setRules(db, rulesJson)</code> deploys rules to the in-process simulator; <code>setRules(db, null)</code> clears rules (default-allow)</span></span></summary>
388
- <div class="compat-evidence"><div class="compat-probe"><code>unit:modular/sandbox-target.test.ts</code> ("sandbox.setRules(db, null) clears rules")</div></div>
187
+ <div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/modular/sandbox-target.test.ts#L35" target="_blank" rel="noopener noreferrer"><code>unit:modular/sandbox-target.test.ts</code></a> ("sandbox.setRules(db, null) clears rules")</div></div>
389
188
  </details>
390
189
  <details class="compat-row" data-status="ok">
391
190
  <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior"><code>sandbox.setData(db, { '/path': value })</code> bulk-loads data, bypassing rules</span></span></summary>
392
- <div class="compat-evidence"><div class="compat-probe"><code>unit:modular/sandbox-target.test.ts</code> ("sandbox.setData seeds the tree (rule-bypass)")</div></div>
191
+ <div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/modular/sandbox-target.test.ts#L585" target="_blank" rel="noopener noreferrer"><code>unit:modular/sandbox-target.test.ts</code></a> ("sandbox.setData seeds the tree (rule-bypass)")</div></div>
393
192
  </details>
394
193
  <details class="compat-row" data-status="ok">
395
194
  <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior"><code>sandbox.snapshotState(db)</code> dumps the full tree as a plain JSON object</span></span></summary>
396
- <div class="compat-evidence"><div class="compat-probe"><code>unit:modular/sandbox-target.test.ts</code> ("sandbox.snapshotState dumps the full tree")</div></div>
195
+ <div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/modular/sandbox-target.test.ts#L51" target="_blank" rel="noopener noreferrer"><code>unit:modular/sandbox-target.test.ts</code></a> ("sandbox.snapshotState dumps the full tree")</div></div>
397
196
  </details>
398
197
  <details class="compat-row" data-status="ok">
399
198
  <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior"><code>query(ref, ...constraints)</code> + ordering/range constraints</span></span></summary>
400
- <div class="compat-evidence"><div class="compat-probe"><code>unit:upstream-rtdb-probes.test.ts</code> ("orderByChild('a/b') + limitToFirst orders by the nested path") + <code>unit:modular/queries.test.ts</code> + oracle observations under <code>packages/conformance/observations/rtdb-modular/</code>; see M49–M64 for the per-claim breakdown.</div></div>
199
+ <div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/upstream-rtdb-probes.test.ts#L4" target="_blank" rel="noopener noreferrer"><code>unit:upstream-rtdb-probes.test.ts</code></a> ("orderByChild('a/b') + limitToFirst orders by the nested path") + <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/modular/queries.test.ts#L15" target="_blank" rel="noopener noreferrer"><code>unit:modular/queries.test.ts</code></a> + oracle observations under <code>packages/conformance/observations/rtdb-modular/</code>; see M49–M64 for the per-claim breakdown.</div></div>
401
200
  </details>
402
201
  <details class="compat-row" data-status="ok">
403
202
  <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior"><code>runTransaction(ref, fn, options?)</code> resolves to <code>{ committed: boolean, snapshot: DataSnapshot }</code> for the happy path — the update fn return value is written, committed is <code>true</code>, and <code>snapshot.val()</code> reflects the committed value</span></span></summary>
404
- <div class="compat-evidence"><div class="compat-probe">Sandbox aligned: <code>unit:modular/transaction.test.ts</code> ("resolves to { committed: boolean, snapshot } with the committed value"); matches oracle observations <code>packages/conformance/observations/rtdb-modular/rtdb-modular-runtransaction-success.json</code> + <code>rtdb-modular-runtransaction-returns-committed-snapshot.json</code> (against blockingfun, fb-js-sdk 12.13.0)</div></div>
203
+ <div class="compat-evidence"><div class="compat-probe">Sandbox aligned: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/modular/transaction.test.ts#L37" target="_blank" rel="noopener noreferrer"><code>unit:modular/transaction.test.ts</code></a> ("resolves to { committed: boolean, snapshot } with the committed value"); matches oracle observations <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/rtdb-modular/rtdb-modular-runtransaction-success.json" target="_blank" rel="noopener noreferrer"><code>packages/conformance/observations/rtdb-modular/rtdb-modular-runtransaction-success.json</code></a> + <code>rtdb-modular-runtransaction-returns-committed-snapshot.json</code> (against blockingfun, fb-js-sdk 12.13.0) (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/rtdb-modular/rtdb-modular-runtransaction-warm-client-speculation.json" target="_blank" rel="noopener noreferrer"><code>rtdb-modular-runtransaction-warm-client-speculation</code></a>)</div></div>
405
204
  </details>
406
205
  <details class="compat-row" data-status="ok">
407
206
  <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior">Returning <code>undefined</code> from the update fn ABORTS the transaction — resolves <code>{ committed: false, snapshot }</code>; no write performed, no listener fan-out</span></span></summary>
408
- <div class="compat-evidence"><div class="compat-probe">Sandbox aligned: <code>unit:modular/transaction.test.ts</code> ("returning undefined aborts — committed: false, no write" + "aborted transaction does NOT fan out to listeners"); matches oracle observation <code>packages/conformance/observations/rtdb-modular/rtdb-modular-runtransaction-abort-undefined.json</code> — known divergence: prod's <code>result.snapshot.val()</code> reflects the CLIENT's pre-fetch (often <code>null</code> even when the server has a value because the speculative invocation runs before the server snap arrives); the sandbox returns the actual pre-transaction value at the path (more useful in single-client harness). The agreed-upon contract callers should rely on is <code>committed === false</code> and unchanged server-side data, NOT the snapshot's <code>.val()</code> on the abort path.</div></div>
207
+ <div class="compat-evidence"><div class="compat-probe">Sandbox aligned: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/modular/transaction.test.ts#L61" target="_blank" rel="noopener noreferrer"><code>unit:modular/transaction.test.ts</code></a> ("returning undefined aborts — committed: false, no write" + "aborted transaction does NOT fan out to listeners"); matches oracle observation <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/rtdb-modular/rtdb-modular-runtransaction-abort-undefined.json" target="_blank" rel="noopener noreferrer"><code>packages/conformance/observations/rtdb-modular/rtdb-modular-runtransaction-abort-undefined.json</code></a> — known divergence: prod's <code>result.snapshot.val()</code> reflects the CLIENT's pre-fetch (often <code>null</code> even when the server has a value because the speculative invocation runs before the server snap arrives); the sandbox returns the actual pre-transaction value at the path (more useful in single-client harness). The agreed-upon contract callers should rely on is <code>committed === false</code> and unchanged server-side data, NOT the snapshot's <code>.val()</code> on the abort path.</div></div>
409
208
  </details>
410
209
  <details class="compat-row" data-status="diverged">
411
210
  <summary class="compat-line"><span class="compat-dot" data-status="diverged" role="img" aria-label="Diverged (documented)" title="Diverged (documented)"></span><span class="compat-main"><span class="compat-behavior">Cold production clients may invoke the update fn first with speculative <code>null</code> and then with the current seeded value; the always-warm in-process sandbox invokes it once with the current value</span></span></summary>
412
- <div class="compat-evidence"><div class="compat-probe">Oracle <code>rtdb-modular-runtransaction-current-value-arg</code> captures two seeded-path production invocations (<code>null</code>, then current); <code>unit:modular/oracle-conformance-transactions.test.ts</code> pins the sandbox's single current-value invocation. The separate warm-client observation matches the sandbox but does not erase the cold-client contract.</div></div>
211
+ <div class="compat-evidence"><div class="compat-probe">Oracle <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/rtdb-modular/rtdb-modular-runtransaction-current-value-arg.json" target="_blank" rel="noopener noreferrer"><code>rtdb-modular-runtransaction-current-value-arg</code></a> captures two seeded-path production invocations (<code>null</code>, then current); <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/modular/oracle-conformance-transactions.test.ts#L52" target="_blank" rel="noopener noreferrer"><code>unit:modular/oracle-conformance-transactions.test.ts</code></a> pins the sandbox's single current-value invocation. The separate warm-client observation matches the sandbox but does not erase the cold-client contract. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/modular/transaction.test.ts#L93" target="_blank" rel="noopener noreferrer"><code>transaction.test.ts</code></a>)</div></div>
413
212
  </details>
414
213
  <details class="compat-row" data-status="ok">
415
214
  <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior">The update fn arg is a defensive deep clone — mutating it does NOT corrupt the stored tree (matters for code that does <code>current.count++; return undefined</code> and expects abort to preserve state)</span></span></summary>
416
- <div class="compat-evidence"><div class="compat-probe"><code>unit:modular/transaction.test.ts</code> ("mutating the update-fn arg does NOT corrupt the stored tree") — no separate oracle row (defensive contract; prod behavior is identical because the SDK clones on the wire boundary)</div></div>
215
+ <div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/modular/transaction.test.ts#L8" target="_blank" rel="noopener noreferrer"><code>unit:modular/transaction.test.ts</code></a> ("mutating the update-fn arg does NOT corrupt the stored tree") — no separate oracle row (defensive contract; prod behavior is identical because the SDK clones on the wire boundary)</div></div>
417
216
  </details>
418
217
  <details class="compat-row" data-status="ok">
419
218
  <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior"><code>options.applyLocally</code> controls whether the in-flight optimistic value fans out to <code>onValue</code> listeners — default <code>true</code> (apply locally before commit); <code>false</code> suppresses intermediate fires so listeners see only the committed value</span></span></summary>
420
- <div class="compat-evidence"><div class="compat-probe">Sandbox aligned: <code>unit:modular/transaction.test.ts</code> ("applyLocally: true (default) — listener sees initial + committed value" + "applyLocally: false — listener sees only the committed value"); matches oracle observation <code>packages/conformance/observations/rtdb-modular/rtdb-modular-runtransaction-options-applylocally.json</code> (single-client harness: both branches produce 2 fires (initial + commit) — divergence vs prod's documented multi-client suppression would surface under contention, which the sandbox doesn't model)</div></div>
219
+ <div class="compat-evidence"><div class="compat-probe">Sandbox aligned: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/modular/transaction.test.ts#L175" target="_blank" rel="noopener noreferrer"><code>unit:modular/transaction.test.ts</code></a> ("applyLocally: true (default) — listener sees initial + committed value" + "applyLocally: false — listener sees only the committed value"); matches oracle observation <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/rtdb-modular/rtdb-modular-runtransaction-options-applylocally.json" target="_blank" rel="noopener noreferrer"><code>packages/conformance/observations/rtdb-modular/rtdb-modular-runtransaction-options-applylocally.json</code></a> (single-client harness: both branches produce 2 fires (initial + commit) — divergence vs prod's documented multi-client suppression would surface under contention, which the sandbox doesn't model)</div></div>
421
220
  </details>
422
221
  <details class="compat-row" data-status="ok">
423
222
  <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior">Rules-denied transaction rejects with a plain <code>Error</code> whose <code>message === 'permission_denied'</code> (lowercase) and NO <code>.code</code> field — DIFFERENT from <code>set</code>/<code>get</code>'s <code>'PERMISSION_DENIED: Permission denied'</code> shape with uppercase <code>.code</code>.</span></span></summary>
424
- <div class="compat-evidence"><div class="compat-probe">Sandbox aligned: <code>unit:modular/transaction.test.ts</code> ("rejects with a plain Error whose message is \"permission_denied\""); matches oracle observation <code>packages/conformance/observations/rtdb-modular/rtdb-modular-runtransaction-on-rules-denied-path.json</code> (against blockingfun: <code>message: 'permission_denied', code: null, constructorName: 'Error'</code>).</div></div>
223
+ <div class="compat-evidence"><div class="compat-probe">Sandbox aligned: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/modular/transaction.test.ts#L13" target="_blank" rel="noopener noreferrer"><code>unit:modular/transaction.test.ts</code></a> ("rejects with a plain Error whose message is \"permission_denied\""); matches oracle observation <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/rtdb-modular/rtdb-modular-runtransaction-on-rules-denied-path.json" target="_blank" rel="noopener noreferrer"><code>packages/conformance/observations/rtdb-modular/rtdb-modular-runtransaction-on-rules-denied-path.json</code></a> (against blockingfun: <code>message: 'permission_denied', code: null, constructorName: 'Error'</code>).</div></div>
425
224
  </details>
426
225
  <details class="compat-row" data-status="ok">
427
226
  <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior">Rules-denied transaction does NOT write — pre-transaction value at the path is preserved through the rejection</span></span></summary>
428
- <div class="compat-evidence"><div class="compat-probe"><code>unit:modular/transaction.test.ts</code> ("does not write to the path when rules deny") — locked alongside the M37e shape claim</div></div>
227
+ <div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/modular/transaction.test.ts#L2" target="_blank" rel="noopener noreferrer"><code>unit:modular/transaction.test.ts</code></a> ("does not write to the path when rules deny") — locked alongside the M37e shape claim</div></div>
429
228
  </details>
430
229
  <details class="compat-row" data-status="ok">
431
230
  <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior">Committed transaction fans out to <code>onValue</code> listeners on the watched path with the new value (default applyLocally behavior)</span></span></summary>
432
- <div class="compat-evidence"><div class="compat-probe"><code>unit:modular/transaction.test.ts</code> ("committed write fans out to onValue listeners")</div></div>
231
+ <div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/modular/transaction.test.ts#L24" target="_blank" rel="noopener noreferrer"><code>unit:modular/transaction.test.ts</code></a> ("committed write fans out to onValue listeners")</div></div>
433
232
  </details>
434
233
  <details class="compat-row" data-status="diverged">
435
234
  <summary class="compat-line"><span class="compat-dot" data-status="diverged" role="img" aria-label="Diverged (documented)" title="Diverged (documented)"></span><span class="compat-main"><span class="compat-behavior"><strong>Divergence:</strong> two ordinary concurrent <code>runTransaction</code> calls are serialized by the in-process backend, so their update functions are not retried with Firebase's captured contention counts. A synchronous re-entrant conflicting write does trigger a deterministic retry.</span></span></summary>
436
- <div class="compat-evidence"><div class="compat-probe">Oracle <code>rtdb-modular-concurrent-transforms</code> captures invocation counts [2, 3]; <code>unit:modular/transaction-contention-cdd.test.ts</code> pins the sandbox's [1, 1] ordinary-concurrency boundary and separately covers its deterministic re-entrant retry seam.</div></div>
235
+ <div class="compat-evidence"><div class="compat-probe">Oracle <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/rtdb-modular/rtdb-modular-concurrent-transforms.json" target="_blank" rel="noopener noreferrer"><code>rtdb-modular-concurrent-transforms</code></a> captures invocation counts [2, 3]; <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/modular/transaction-contention-cdd.test.ts#L12" target="_blank" rel="noopener noreferrer"><code>unit:modular/transaction-contention-cdd.test.ts</code></a> pins the sandbox's [1, 1] ordinary-concurrency boundary and separately covers its deterministic re-entrant retry seam.</div></div>
437
236
  </details>
438
237
  <details class="compat-row" data-status="ok">
439
238
  <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior">Identity-aware sandbox-live op routing — sign-in/sign-out via <code>pyric/auth</code> is observed by the next RTDB op without re-binding</span></span></summary>
440
- <div class="compat-evidence"><div class="compat-probe"><code>unit:modular/sandbox-target.test.ts</code> ("reads sandbox.currentUser at op time, not at getDatabase time")</div></div>
239
+ <div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/modular/sandbox-target.test.ts#L11" target="_blank" rel="noopener noreferrer"><code>unit:modular/sandbox-target.test.ts</code></a> ("reads sandbox.currentUser at op time, not at getDatabase time")</div></div>
441
240
  </details>
442
241
  <details class="compat-row" data-status="ok">
443
242
  <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior">Backend identity is per-<code>Sandbox</code> — two <code>getDatabase(sandbox)</code> calls on the same sandbox share data, two on different sandboxes don't</span></span></summary>
444
- <div class="compat-evidence"><div class="compat-probe"><code>unit:modular/database-instances-cdd.test.ts</code> proves same-sandbox sharing and independent-sandbox isolation.</div></div>
243
+ <div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/modular/database-instances-cdd.test.ts#L3" target="_blank" rel="noopener noreferrer"><code>unit:modular/database-instances-cdd.test.ts</code></a> proves same-sandbox sharing and independent-sandbox isolation.</div></div>
445
244
  </details>
446
245
  <details class="compat-row" data-status="ok">
447
246
  <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior">Sandbox refs carry a stable <code>key</code> (last path segment) and <code>toString()</code> returning <code>sandbox://rtdb/&lt;path&gt;</code></span></span></summary>
448
- <div class="compat-evidence"><div class="compat-probe"><code>unit:modular/oracle-conformance-reference-writes.test.ts</code> pins the stable key and local <code>sandbox://rtdb/&lt;path&gt;</code> string boundary alongside the production URL observation.</div></div>
247
+ <div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/modular/oracle-conformance-reference-writes.test.ts#L1" target="_blank" rel="noopener noreferrer"><code>unit:modular/oracle-conformance-reference-writes.test.ts</code></a> pins the stable key and local <code>sandbox://rtdb/&lt;path&gt;</code> string boundary alongside the production URL observation.</div></div>
449
248
  </details>
450
249
  <details class="compat-row" data-status="ok">
451
250
  <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior"><code>query(ref, orderByChild(p), startAt(v), endAt(w))</code> window is BOTH-INCLUSIVE — children whose ordered field === <code>v</code> or === <code>w</code> are included</span></span></summary>
452
- <div class="compat-evidence"><div class="compat-probe"><code>unit:upstream-rtdb-probes.test.ts</code> (deep orderByChild nested path) + <code>unit:modular/queries.test.ts</code> ("returns children whose ordered child is within [startAt, endAt] inclusive"); matches oracle <code>rtdb-modular-orderbychild-window.json</code></div></div>
251
+ <div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/upstream-rtdb-probes.test.ts#L4" target="_blank" rel="noopener noreferrer"><code>unit:upstream-rtdb-probes.test.ts</code></a> (deep orderByChild nested path) + <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/modular/queries.test.ts#L65" target="_blank" rel="noopener noreferrer"><code>unit:modular/queries.test.ts</code></a> ("returns children whose ordered child is within [startAt, endAt] inclusive"); matches oracle <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/rtdb-modular/rtdb-modular-orderbychild-window.json" target="_blank" rel="noopener noreferrer"><code>rtdb-modular-orderbychild-window.json</code></a></div></div>
453
252
  </details>
454
253
  <details class="compat-row" data-status="ok">
455
254
  <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior"><code>orderByKey()</code> orders children by RTDB <code>nameCompare</code> — integer-looking keys sort numerically FIRST (so <code>['1','2','10']</code>, not the lexicographic <code>['1','10','2']</code>), then non-integer keys lexicographically; <code>startAt</code>/<code>endAt</code> cursors + the optional <code>key</code> tie-breaker use the same order (DB-B4)</span></span></summary>
456
- <div class="compat-evidence"><div class="compat-probe"><code>unit:upstream-rtdb-probes.test.ts</code> (INT32 overflow/underflow cursors) + <code>unit:modular/queries.test.ts</code> + <code>unit:modular/name-compare.test.ts</code>; matches oracle <code>rtdb-modular-orderbykey-window.json</code> and upstream <code>core/util/util.ts:253-276</code></div></div>
255
+ <div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/upstream-rtdb-probes.test.ts#L22" target="_blank" rel="noopener noreferrer"><code>unit:upstream-rtdb-probes.test.ts</code></a> (INT32 overflow/underflow cursors) + <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/modular/queries.test.ts#L23" target="_blank" rel="noopener noreferrer"><code>unit:modular/queries.test.ts</code></a> + <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/modular/name-compare.test.ts#L19" target="_blank" rel="noopener noreferrer"><code>unit:modular/name-compare.test.ts</code></a>; matches oracle <code>rtdb-modular-orderbykey-window.json</code> and upstream <code>core/util/util.ts:253-276</code></div></div>
457
256
  </details>
458
257
  <details class="compat-row" data-status="diverged">
459
258
  <summary class="compat-line"><span class="compat-dot" data-status="diverged" role="img" aria-label="Diverged (documented)" title="Diverged (documented)"></span><span class="compat-main"><span class="compat-behavior">Production rejects an unindexed <code>orderByValue() + limitToFirst(N)</code> query; the sandbox does not enforce <code>.indexOn</code> and returns the N smallest primitive values</span></span></summary>
460
- <div class="compat-evidence"><div class="compat-probe">Oracle <code>rtdb-modular-orderbyvalue-numeric</code> captures production's <code>Index not defined</code> rejection; <code>unit:modular/oracle-conformance-queries.test.ts</code> pins that rejection beside the sandbox's successful <code>[10,20,30]</code> window.</div></div>
259
+ <div class="compat-evidence"><div class="compat-probe">Oracle <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/rtdb-modular/rtdb-modular-orderbyvalue-numeric.json" target="_blank" rel="noopener noreferrer"><code>rtdb-modular-orderbyvalue-numeric</code></a> captures production's <code>Index not defined</code> rejection; <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/modular/queries.test.ts#L108" target="_blank" rel="noopener noreferrer"><code>unit:modular/oracle-conformance-queries.test.ts</code></a> pins that rejection beside the sandbox's successful <code>[10,20,30]</code> window. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/modular/oracle-conformance-queries.test.ts#L51" target="_blank" rel="noopener noreferrer"><code>oracle-conformance-queries.test.ts</code></a>)</div></div>
461
260
  </details>
462
261
  <details class="compat-row" data-status="ok">
463
262
  <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior"><code>orderByChild(p) + equalTo(v)</code> returns ALL children whose field at <code>p</code> === <code>v</code> — no uniqueness enforced</span></span></summary>
464
- <div class="compat-evidence"><div class="compat-probe">Sandbox aligned: <code>unit:modular/queries.test.ts</code> ("returns ALL children whose ordered field === the supplied value"); matches oracle observation <code>packages/conformance/observations/rtdb-modular/rtdb-modular-equalTo-filter.json</code> (both 'b'-grouped children returned).</div></div>
263
+ <div class="compat-evidence"><div class="compat-probe">Sandbox aligned: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/modular/queries.test.ts#L129" target="_blank" rel="noopener noreferrer"><code>unit:modular/queries.test.ts</code></a> ("returns ALL children whose ordered field === the supplied value"); matches oracle observation <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/rtdb-modular/rtdb-modular-equalTo-filter.json" target="_blank" rel="noopener noreferrer"><code>packages/conformance/observations/rtdb-modular/rtdb-modular-equalTo-filter.json</code></a> (both 'b'-grouped children returned).</div></div>
465
264
  </details>
466
265
  <details class="compat-row" data-status="ok">
467
266
  <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior"><code>equalTo</code> with no matches returns an empty snapshot (<code>exists() === false</code>, <code>size === 0</code>)</span></span></summary>
468
- <div class="compat-evidence"><div class="compat-probe"><code>unit:modular/queries.test.ts</code> ("returns an empty snapshot when nothing matches")</div></div>
267
+ <div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/modular/queries.test.ts#L32" target="_blank" rel="noopener noreferrer"><code>unit:modular/queries.test.ts</code></a> ("returns an empty snapshot when nothing matches")</div></div>
469
268
  </details>
470
269
  <details class="compat-row" data-status="ok">
471
270
  <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior"><code>limitToFirst(N)</code> keeps the lowest-ranked N children (post-ordering, pre-filter)</span></span></summary>
472
- <div class="compat-evidence"><div class="compat-probe">Sandbox aligned: <code>unit:modular/queries.test.ts</code> ("limitToFirst takes the lowest-ranked window"); matches oracle observation <code>packages/conformance/observations/rtdb-modular/rtdb-modular-limittofirst-vs-limittolast.json</code> (firstPositions <code>[1,2]</code>).</div></div>
271
+ <div class="compat-evidence"><div class="compat-probe">Sandbox aligned: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/modular/queries.test.ts#L158" target="_blank" rel="noopener noreferrer"><code>unit:modular/queries.test.ts</code></a> ("limitToFirst takes the lowest-ranked window"); matches oracle observation <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/rtdb-modular/rtdb-modular-limittofirst-vs-limittolast.json" target="_blank" rel="noopener noreferrer"><code>packages/conformance/observations/rtdb-modular/rtdb-modular-limittofirst-vs-limittolast.json</code></a> (firstPositions <code>[1,2]</code>).</div></div>
473
272
  </details>
474
273
  <details class="compat-row" data-status="ok">
475
274
  <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior"><code>limitToLast(N)</code> keeps the highest-ranked N children</span></span></summary>
476
- <div class="compat-evidence"><div class="compat-probe">Sandbox aligned: <code>unit:modular/queries.test.ts</code> ("limitToLast takes the highest-ranked window"); matches oracle observation <code>packages/conformance/observations/rtdb-modular/rtdb-modular-limittofirst-vs-limittolast.json</code> (lastPositions <code>[4,5]</code>).</div></div>
275
+ <div class="compat-evidence"><div class="compat-probe">Sandbox aligned: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/modular/queries.test.ts#L158" target="_blank" rel="noopener noreferrer"><code>unit:modular/queries.test.ts</code></a> ("limitToLast takes the highest-ranked window"); matches oracle observation <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/rtdb-modular/rtdb-modular-limittofirst-vs-limittolast.json" target="_blank" rel="noopener noreferrer"><code>packages/conformance/observations/rtdb-modular/rtdb-modular-limittofirst-vs-limittolast.json</code></a> (lastPositions <code>[4,5]</code>).</div></div>
477
276
  </details>
478
277
  <details class="compat-row" data-status="ok">
479
278
  <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior"><code>limitToFirst(N)</code> larger than the result returns the full window (no padding, no throw)</span></span></summary>
480
- <div class="compat-evidence"><div class="compat-probe"><code>unit:modular/queries.test.ts</code> ("limitToFirst(N) larger than the result returns the full window")</div></div>
279
+ <div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/modular/queries.test.ts#L33" target="_blank" rel="noopener noreferrer"><code>unit:modular/queries.test.ts</code></a> ("limitToFirst(N) larger than the result returns the full window")</div></div>
481
280
  </details>
482
281
  <details class="compat-row" data-status="ok">
483
282
  <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior"><code>startAfter(v)</code> and <code>endBefore(v)</code> are EXCLUSIVE — the boundary value is dropped from the result</span></span></summary>
484
- <div class="compat-evidence"><div class="compat-probe">Sandbox aligned: <code>unit:modular/queries.test.ts</code> ("startAfter + endBefore drop the boundary values"); matches oracle observation <code>packages/conformance/observations/rtdb-modular/rtdb-modular-startafter-endbefore-exclusive.json</code> (positions <code>[3,4]</code>, cursors 2 + 5 dropped).</div></div>
283
+ <div class="compat-evidence"><div class="compat-probe">Sandbox aligned: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/modular/queries.test.ts#L197" target="_blank" rel="noopener noreferrer"><code>unit:modular/queries.test.ts</code></a> ("startAfter + endBefore drop the boundary values"); matches oracle observation <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/rtdb-modular/rtdb-modular-startafter-endbefore-exclusive.json" target="_blank" rel="noopener noreferrer"><code>packages/conformance/observations/rtdb-modular/rtdb-modular-startafter-endbefore-exclusive.json</code></a> (positions <code>[3,4]</code>, cursors 2 + 5 dropped).</div></div>
485
284
  </details>
486
285
  <details class="compat-row" data-status="ok">
487
286
  <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior"><code>onValue(query, cb)</code> only fires when the windowed result changes — writes OUTSIDE the window don't re-fire the listener; writes that displace a member DO</span></span></summary>
488
- <div class="compat-evidence"><div class="compat-probe"><code>unit:upstream-rtdb-probes.test.ts</code> ("one update nulls, mutates, and displaces within a limitToFirst window") + <code>unit:modular/queries.test.ts</code> ("fires only when the windowed result changes"); matches oracle <code>rtdb-modular-onvalue-with-query.json</code></div></div>
287
+ <div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/upstream-rtdb-probes.test.ts#L4" target="_blank" rel="noopener noreferrer"><code>unit:upstream-rtdb-probes.test.ts</code></a> ("one update nulls, mutates, and displaces within a limitToFirst window") + <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/modular/queries.test.ts#L221" target="_blank" rel="noopener noreferrer"><code>unit:modular/queries.test.ts</code></a> ("fires only when the windowed result changes"); matches oracle <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/rtdb-modular/rtdb-modular-onvalue-with-query.json" target="_blank" rel="noopener noreferrer"><code>rtdb-modular-onvalue-with-query.json</code></a></div></div>
489
288
  </details>
490
289
  <details class="compat-row" data-status="ok">
491
290
  <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior"><code>onValue(query)</code> initial fire delivers an empty window (<code>size === 0</code>) when the path is absent</span></span></summary>
492
- <div class="compat-evidence"><div class="compat-probe"><code>unit:modular/queries.test.ts</code> ("initial fire on an empty path delivers an empty window")</div></div>
291
+ <div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/modular/queries.test.ts#L20" target="_blank" rel="noopener noreferrer"><code>unit:modular/queries.test.ts</code></a> ("initial fire on an empty path delivers an empty window")</div></div>
493
292
  </details>
494
293
  <details class="compat-row" data-status="ok">
495
294
  <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior"><code>query(query(ref, c1), c2)</code> composes constraints — chaining folds both into one spec</span></span></summary>
496
- <div class="compat-evidence"><div class="compat-probe"><code>unit:modular/queries.test.ts</code> ("query(query(ref, c1), c2) composes constraints")</div></div>
295
+ <div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/modular/queries.test.ts#L15" target="_blank" rel="noopener noreferrer"><code>unit:modular/queries.test.ts</code></a> ("query(query(ref, c1), c2) composes constraints")</div></div>
497
296
  </details>
498
297
  <details class="compat-row" data-status="ok">
499
298
  <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior">Snapshot from a query exposes children via <code>snap.forEach</code> in the executor-computed order — NOT necessarily the order <code>Object.entries(val)</code> would yield</span></span></summary>
500
- <div class="compat-evidence"><div class="compat-probe"><code>unit:modular/queries.test.ts</code> ("forEach visits children in ascending order of the child key")</div></div>
299
+ <div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/modular/queries.test.ts#L21" target="_blank" rel="noopener noreferrer"><code>unit:modular/queries.test.ts</code></a> ("forEach visits children in ascending order of the child key")</div></div>
501
300
  </details>
502
301
  <details class="compat-row" data-status="ok">
503
302
  <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior"><code>startAt(value, key)</code> uses <code>key</code> as the tie-breaker when multiple children share the same ordered value — children before <code>key</code> are dropped, the row at <code>key</code> is included (inclusive cursor)</span></span></summary>
504
- <div class="compat-evidence"><div class="compat-probe"><code>unit:modular/queries.test.ts</code> ("startAt with key tie-breaker drops earlier same-value children")</div></div>
303
+ <div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/modular/queries.test.ts#L28" target="_blank" rel="noopener noreferrer"><code>unit:modular/queries.test.ts</code></a> ("startAt with key tie-breaker drops earlier same-value children")</div></div>
505
304
  </details>
506
305
  <details class="compat-row" data-status="ok">
507
306
  <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior"><code>orderByChild('p')</code> on children missing the field treats their value as <code>null</code> (sorts FIRST per RTDB's type ordering)</span></span></summary>
508
- <div class="compat-evidence"><div class="compat-probe"><code>unit:modular/queries.test.ts</code> ("orderByChild on a missing child path treats those children as null")</div></div>
307
+ <div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/modular/queries.test.ts#L22" target="_blank" rel="noopener noreferrer"><code>unit:modular/queries.test.ts</code></a> ("orderByChild on a missing child path treats those children as null")</div></div>
509
308
  </details>
510
309
  <details class="compat-row" data-status="ok">
511
310
  <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior"><code>query</code> on a path holding a primitive (or absent path) returns an empty snapshot — no rows to iterate</span></span></summary>
512
- <div class="compat-evidence"><div class="compat-probe"><code>unit:modular/queries.test.ts</code> ("query on a path with primitive value returns no rows")</div></div>
311
+ <div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/modular/queries.test.ts#L21" target="_blank" rel="noopener noreferrer"><code>unit:modular/queries.test.ts</code></a> ("query on a path with primitive value returns no rows")</div></div>
513
312
  </details>
514
313
  <details class="compat-row" data-status="ok">
515
314
  <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior">Write-boundary normalization (<code>nodeFromJSON</code>-equivalent): a value written as an array is stored as an integer-keyed object — <code>child(ref, '1')</code> returns the element, <code>forEach</code> iterates <code>0,1,2…</code> (DB-B2)</span></span></summary>
516
- <div class="compat-evidence"><div class="compat-probe">Sandbox aligned: <code>unit:modular/normalization.test.ts</code> ("array write is addressable by integer-string child key" + "forEach over an array iterates its elements"); upstream <code>core/snap/nodeFromJSON.ts:118-128</code>, <code>core/snap/ChildrenNode.ts:194-230</code></div></div>
315
+ <div class="compat-evidence"><div class="compat-probe">Sandbox aligned: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/modular/normalization.test.ts#L20" target="_blank" rel="noopener noreferrer"><code>unit:modular/normalization.test.ts</code></a> ("array write is addressable by integer-string child key" + "forEach over an array iterates its elements"); upstream <code>core/snap/nodeFromJSON.ts:118-128</code>, <code>core/snap/ChildrenNode.ts:194-230</code></div></div>
517
316
  </details>
518
317
  <details class="compat-row" data-status="ok">
519
318
  <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior">Read-side array coercion: a dense integer-keyed object renders back as an array on <code>snap.val()</code> (<code>allIntegerKeys &amp;&amp; maxKey &lt; 2 * numKeys</code>) (DB-B2)</span></span></summary>
520
- <div class="compat-evidence"><div class="compat-probe">Sandbox aligned: <code>unit:modular/normalization.test.ts</code> ("a dense integer-keyed object reads back as an array"); upstream <code>core/snap/ChildrenNode.ts:196-230</code></div></div>
319
+ <div class="compat-evidence"><div class="compat-probe">Sandbox aligned: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/modular/normalization.test.ts#L19" target="_blank" rel="noopener noreferrer"><code>unit:modular/normalization.test.ts</code></a> ("a dense integer-keyed object reads back as an array"); upstream <code>core/snap/ChildrenNode.ts:196-230</code></div></div>
521
320
  </details>
522
321
  <details class="compat-row" data-status="ok">
523
322
  <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior"><code>null</code> children and empty objects are pruned at the write boundary — <code>set(ref, {})</code> is equivalent to <code>remove(ref)</code>; nested <code>null</code> collapses empty ancestors ("empty nodes don't exist") (DB-B3)</span></span></summary>
524
- <div class="compat-evidence"><div class="compat-probe">Sandbox aligned: <code>unit:modular/normalization.test.ts</code> ("set(ref, {}) is equivalent to remove" + "null children are pruned"); upstream <code>core/snap/nodeFromJSON.ts:78-88,122-126</code></div></div>
323
+ <div class="compat-evidence"><div class="compat-probe">Sandbox aligned: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/modular/normalization.test.ts#L20" target="_blank" rel="noopener noreferrer"><code>unit:modular/normalization.test.ts</code></a> ("set(ref, {}) is equivalent to remove" + "null children are pruned"); upstream <code>core/snap/nodeFromJSON.ts:78-88,122-126</code></div></div>
525
324
  </details>
526
325
  <details class="compat-row" data-status="ok">
527
326
  <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior">Write validation: an <code>undefined</code> payload, a non-finite number (<code>NaN</code>/<code>±Infinity</code>), or a key containing a forbidden char (<code>.</code>, <code>#</code>, <code>$</code>, <code>/</code>, <code>[</code>, <code>]</code>, control chars) is rejected with a plain <code>Error</code> (DB-B1)</span></span></summary>
528
- <div class="compat-evidence"><div class="compat-probe">Sandbox aligned: <code>unit:modular/normalization.test.ts</code> ("rejects an undefined payload" + "rejects an invalid key" + "rejects a non-finite number"); upstream <code>core/util/validation.ts:45,58,112-199</code></div></div>
327
+ <div class="compat-evidence"><div class="compat-probe">Sandbox aligned: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/modular/normalization.test.ts#L23" target="_blank" rel="noopener noreferrer"><code>unit:modular/normalization.test.ts</code></a> ("rejects an undefined payload" + "rejects an invalid key" + "rejects a non-finite number"); upstream <code>core/util/validation.ts:45,58,112-199</code></div></div>
529
328
  </details>
530
329
  <details class="compat-row" data-status="ok">
531
330
  <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior">Conflicting query constraints throw synchronously at <code>query(...)</code> construction (NOT silent last-win): multiple <code>orderBy*</code>, a second <code>limitToFirst</code>/<code>limitToLast</code>, a second start (<code>startAt</code>/<code>startAfter</code>/<code>equalTo</code>) or end (<code>endAt</code>/<code>endBefore</code>/<code>equalTo</code>) (DB-B5)</span></span></summary>
532
- <div class="compat-evidence"><div class="compat-probe">Sandbox aligned: <code>unit:modular/constraint-conflicts.test.ts</code> (5 cases); upstream <code>api/Reference_impl.ts:160-165,1824-1841,1888-1905,1945-1951,2193-2206</code></div></div>
331
+ <div class="compat-evidence"><div class="compat-probe">Sandbox aligned: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/modular/constraint-conflicts.test.ts#L4" target="_blank" rel="noopener noreferrer"><code>unit:modular/constraint-conflicts.test.ts</code></a> (5 cases); upstream <code>api/Reference_impl.ts:160-165,1824-1841,1888-1905,1945-1951,2193-2206</code></div></div>
533
332
  </details>
534
333
  <details class="compat-row" data-status="ok">
535
334
  <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior"><code>push(ref, value?)</code> returns a <code>ThenableReference</code> (a <code>DatabaseReference</code> with <code>.then</code>/<code>.catch</code>). The key + ref are minted CLIENT-SIDE and available synchronously even when the optional value write is rules-denied; the write is deferred onto the promise, so a denial REJECTS the awaited push rather than throwing synchronously and discarding the key (DB-B7)</span></span></summary>
536
- <div class="compat-evidence"><div class="compat-probe">Sandbox aligned: <code>unit:modular/push-thenable.test.ts</code> (4 cases); matches oracle <code>packages/conformance/observations/rtdb/rtdb-push-autoid-format.json</code> ("available immediately even when the subsequent server write is denied by rules") + upstream <code>api/Reference_impl.ts:599-630</code></div></div>
335
+ <div class="compat-evidence"><div class="compat-probe">Sandbox aligned: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/modular/push-thenable.test.ts#L10" target="_blank" rel="noopener noreferrer"><code>unit:modular/push-thenable.test.ts</code></a> (4 cases); matches oracle <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/rtdb/rtdb-push-autoid-format.json" target="_blank" rel="noopener noreferrer"><code>packages/conformance/observations/rtdb/rtdb-push-autoid-format.json</code></a> ("available immediately even when the subsequent server write is denied by rules") + upstream <code>api/Reference_impl.ts:599-630</code></div></div>
537
336
  </details>
538
337
  <details class="compat-row" data-status="ok">
539
338
  <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior"><code>DataSnapshot</code> shape: <code>size</code> (getter), <code>priority</code> (<code>null</code> when absent), <code>exportVal()</code>, <code>key</code>, <code>ref</code>, <code>val()</code>, <code>exists()</code>, <code>child()</code>, <code>hasChild()</code>, <code>hasChildren()</code>, <code>forEach()</code>, <code>toJSON()</code>. It does NOT ship the legacy namespaced <code>numChildren()</code> method (DB-B10)</span></span></summary>
540
- <div class="compat-evidence"><div class="compat-probe">Sandbox aligned: <code>unit:modular/snapshot-shape.test.ts</code> ("exposes size/priority/exportVal; NOT numChildren()"); matches oracle <code>packages/conformance/observations/rtdb-modular/rtdb-modular-get-snapshot-shape.json</code> (<code>hasSize: true, hasNumChildren: false</code>) + upstream <code>api/Reference_impl.ts:288-447</code>. <strong>Flipped masking tests</strong>: <code>modular/queries.test.ts</code> + <code>modular/sandbox-target.test.ts</code> asserted <code>snap.numChildren()</code> — updated to <code>snap.size</code>.</div></div>
339
+ <div class="compat-evidence"><div class="compat-probe">Sandbox aligned: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/modular/snapshot-shape.test.ts#L9" target="_blank" rel="noopener noreferrer"><code>unit:modular/snapshot-shape.test.ts</code></a> ("exposes size/priority/exportVal; NOT numChildren()"); matches oracle <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/rtdb-modular/rtdb-modular-get-snapshot-shape.json" target="_blank" rel="noopener noreferrer"><code>packages/conformance/observations/rtdb-modular/rtdb-modular-get-snapshot-shape.json</code></a> (<code>hasSize: true, hasNumChildren: false</code>) + upstream <code>api/Reference_impl.ts:288-447</code>. <strong>Flipped masking tests</strong>: <code>modular/queries.test.ts</code> + <code>modular/sandbox-target.test.ts</code> asserted <code>snap.numChildren()</code> — updated to <code>snap.size</code>.</div></div>
541
340
  </details>
542
341
  <details class="compat-row" data-status="ok">
543
342
  <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior">Object-valued children are ORDER-EQUAL — the sort/range tie is broken by key (<code>nameCompare</code>), NOT by an invented <code>JSON.stringify</code> ordering; a query re-write that only reorders object keys is "no change" and doesn't re-fire (DB-B11)</span></span></summary>
544
- <div class="compat-evidence"><div class="compat-probe">Sandbox aligned: <code>unit:modular/object-order-equality.test.ts</code>; upstream <code>core/snap/ChildrenNode.ts:386-400</code></div></div>
343
+ <div class="compat-evidence"><div class="compat-probe">Sandbox aligned: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/modular/object-order-equality.test.ts#L16" target="_blank" rel="noopener noreferrer"><code>unit:modular/object-order-equality.test.ts</code></a>; upstream <code>core/snap/ChildrenNode.ts:386-400</code></div></div>
545
344
  </details>
546
345
  <details class="compat-row" data-status="ok">
547
346
  <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior">A primitive at the ROOT is legal (<code>set(ref(db), 'hello')</code>); a subsequent child write replaces the primitive root ("writes win") (DB-B13)</span></span></summary>
548
- <div class="compat-evidence"><div class="compat-probe">Sandbox aligned: <code>unit:modular/root-primitive.test.ts</code> (2 cases)</div></div>
347
+ <div class="compat-evidence"><div class="compat-probe">Sandbox aligned: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/modular/root-primitive.test.ts#L2" target="_blank" rel="noopener noreferrer"><code>unit:modular/root-primitive.test.ts</code></a> (2 cases)</div></div>
549
348
  </details>
550
349
  <details class="compat-row" data-status="ok">
551
350
  <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior"><code>onValue(ref, cb, { onlyOnce: true })</code> fires once then auto-unsubscribes (DB-B12)</span></span></summary>
552
- <div class="compat-evidence"><div class="compat-probe">Sandbox aligned: <code>unit:modular/onvalue-onlyonce.test.ts</code>; upstream <code>api/Reference_impl.ts:975-980</code></div></div>
351
+ <div class="compat-evidence"><div class="compat-probe">Sandbox aligned: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/modular/onvalue-onlyonce.test.ts#L2" target="_blank" rel="noopener noreferrer"><code>unit:modular/onvalue-onlyonce.test.ts</code></a>; upstream <code>api/Reference_impl.ts:975-980</code></div></div>
553
352
  </details>
554
353
  <details class="compat-row" data-status="ok">
555
354
  <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior">Child-listener callbacks receive Firebase's <code>previousChildName</code> second argument, including initial replay and ordered-query movement.</span></span></summary>
556
- <div class="compat-evidence"><div class="compat-probe">Oracle <code>rtdb-modular-child-previous-name</code> captures initial replay plus add/change/remove/move predecessor values; replayed by <code>unit:modular/listener-lifecycle-cdd.test.ts</code>.</div></div>
355
+ <div class="compat-evidence"><div class="compat-probe">Oracle <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/rtdb-modular/rtdb-modular-child-previous-name.json" target="_blank" rel="noopener noreferrer"><code>rtdb-modular-child-previous-name</code></a> captures initial replay plus add/change/remove/move predecessor values; replayed by <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/modular/listener-lifecycle-cdd.test.ts#L24" target="_blank" rel="noopener noreferrer"><code>unit:modular/listener-lifecycle-cdd.test.ts</code></a>.</div></div>
557
356
  </details>
558
357
  <details class="compat-row" data-status="ok">
559
358
  <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior">Value and child listener overloads accept a cancellation callback and deliver Firebase-shaped permission errors when a listen is denied or revoked; a callbackless listener that loses auth access is terminal and does not resurrect after sign-in.</span></span></summary>
560
- <div class="compat-evidence"><div class="compat-probe">Oracle <code>rtdb-modular-listener-cancellation</code> captures all five registrars returning normally, asynchronous initial-denial cancellation, one revocation callback each, and the Error/PERMISSION_DENIED/message shape; replayed in <code>unit:modular/listener-lifecycle-cdd.test.ts</code>.</div></div>
359
+ <div class="compat-evidence"><div class="compat-probe">Oracle <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/rtdb-modular/rtdb-modular-listener-cancellation.json" target="_blank" rel="noopener noreferrer"><code>rtdb-modular-listener-cancellation</code></a> captures all five registrars returning normally, asynchronous initial-denial cancellation, one revocation callback each, and the Error/PERMISSION_DENIED/message shape; replayed in <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/modular/listener-lifecycle-cdd.test.ts#L26" target="_blank" rel="noopener noreferrer"><code>unit:modular/listener-lifecycle-cdd.test.ts</code></a>.</div></div>
561
360
  </details>
562
361
  <details class="compat-row" data-status="ok">
563
362
  <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior">All child-listener functions accept <code>Query</code> inputs; add/change/remove events respect the active query window.</span></span></summary>
564
- <div class="compat-evidence"><div class="compat-probe"><code>unit:modular/listener-lifecycle-cdd.test.ts</code> and <code>unit:modular/sandbox-child-events.test.ts</code> cover initial windows plus entering, leaving, and changing members.</div></div>
363
+ <div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/modular/listener-lifecycle-cdd.test.ts#L267" target="_blank" rel="noopener noreferrer"><code>unit:modular/listener-lifecycle-cdd.test.ts</code></a> and <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/modular/sandbox-child-events.test.ts#L18" target="_blank" rel="noopener noreferrer"><code>unit:modular/sandbox-child-events.test.ts</code></a> cover initial windows plus entering, leaving, and changing members.</div></div>
565
364
  </details>
566
365
  <details class="compat-row" data-status="ok">
567
366
  <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior"><code>onChildMoved</code> fires when an ordered child changes position and co-fires with <code>child_changed</code> when Firebase does, preserving the captured <code>previousChildName</code> sequence.</span></span></summary>
568
- <div class="compat-evidence"><div class="compat-probe">Oracle <code>rtdb-modular-child-previous-name</code> and the modular oracle suite replay ordered movement, co-fire behavior, and predecessor sequencing.</div></div>
367
+ <div class="compat-evidence"><div class="compat-probe">Oracle <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/rtdb-modular/rtdb-modular-child-previous-name.json" target="_blank" rel="noopener noreferrer"><code>rtdb-modular-child-previous-name</code></a> and the modular oracle suite replay ordered movement, co-fire behavior, and predecessor sequencing. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/rtdb-modular/rtdb-modular-childchanged-cofire-with-childmoved.json" target="_blank" rel="noopener noreferrer"><code>rtdb-modular-childchanged-cofire-with-childmoved</code></a>, <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/rtdb-modular/rtdb-modular-onchildmoved-previouschildname-sequencing.json" target="_blank" rel="noopener noreferrer"><code>rtdb-modular-onchildmoved-previouschildname-sequencing</code></a>, <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/modular/listener-lifecycle-cdd.test.ts#L24" target="_blank" rel="noopener noreferrer"><code>listener-lifecycle-cdd.test.ts</code></a>, <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/modular/oracle-conformance-listeners.test.ts#L178" target="_blank" rel="noopener noreferrer"><code>oracle-conformance-listeners.test.ts</code></a>)</div></div>
569
368
  </details>
570
369
  <details class="compat-row" data-status="ok">
571
370
  <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior">Child listener overloads accept <code>ListenOptions</code> directly or after a cancellation callback; <code>onlyOnce</code> stops delivery after Firebase's captured initial/event batch.</span></span></summary>
572
- <div class="compat-evidence"><div class="compat-probe">Oracle <code>rtdb-modular-child-listener-only-once</code> captures all four listener families, both overload shapes, the existing-child replay batch, and one-shot post-registration delivery; replayed by <code>unit:modular/listener-lifecycle-cdd.test.ts</code>.</div></div>
371
+ <div class="compat-evidence"><div class="compat-probe">Oracle <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/rtdb-modular/rtdb-modular-child-listener-only-once.json" target="_blank" rel="noopener noreferrer"><code>rtdb-modular-child-listener-only-once</code></a> captures all four listener families, both overload shapes, the existing-child replay batch, and one-shot post-registration delivery; replayed by <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/modular/listener-lifecycle-cdd.test.ts#L25" target="_blank" rel="noopener noreferrer"><code>unit:modular/listener-lifecycle-cdd.test.ts</code></a>.</div></div>
573
372
  </details>
574
373
  <details class="compat-row" data-status="ok">
575
374
  <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior"><code>.validate</code> rules are enforced on modular sandbox writes through <code>set</code>, atomic <code>update</code>, and <code>runTransaction</code>; a descendant validation failure rejects the operation without changing state.</span></span></summary>
576
- <div class="compat-evidence"><div class="compat-probe"><code>unit:modular/sandbox-target.test.ts</code> executes all three write paths against a required-child <code>.validate</code> rule and proves each rejects without committing state. The shared <code>SimulateHandler</code> behavior is production-locked by RTDB rules corpus rows #4 and #15.</div></div>
375
+ <div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/modular/sandbox-target.test.ts#L15" target="_blank" rel="noopener noreferrer"><code>unit:modular/sandbox-target.test.ts</code></a> executes all three write paths against a required-child <code>.validate</code> rule and proves each rejects without committing state. The shared <code>SimulateHandler</code> behavior is production-locked by RTDB rules corpus rows #4 and #15.</div></div>
577
376
  </details>
578
377
  <details class="compat-row" data-status="ok">
579
378
  <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior"><code>onDisconnect(ref)</code> returns the Firebase-shaped five-method handle; registration methods and <code>cancel()</code> return acknowledged Promises</span></span></summary>
580
- <div class="compat-evidence"><div class="compat-probe">Oracle <code>rtdb-modular-ondisconnect-registration</code>; sandbox <code>unit:database/on-disconnect.test.ts</code>.</div></div>
379
+ <div class="compat-evidence"><div class="compat-probe">Oracle <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/rtdb-modular/rtdb-modular-ondisconnect-registration.json" target="_blank" rel="noopener noreferrer"><code>rtdb-modular-ondisconnect-registration</code></a>; sandbox <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/on-disconnect.test.ts#L40" target="_blank" rel="noopener noreferrer"><code>unit:database/on-disconnect.test.ts</code></a>.</div></div>
581
380
  </details>
582
381
  <details class="compat-row" data-status="ok">
583
382
  <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior">An acknowledged <code>onDisconnect(ref).set(value)</code> registration does not mutate server data before the client disconnects</span></span></summary>
584
- <div class="compat-evidence"><div class="compat-probe">Oracle <code>rtdb-modular-ondisconnect-registration</code>; sandbox registration/no-mutation test.</div></div>
383
+ <div class="compat-evidence"><div class="compat-probe">Oracle <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/rtdb-modular/rtdb-modular-ondisconnect-registration.json" target="_blank" rel="noopener noreferrer"><code>rtdb-modular-ondisconnect-registration</code></a>; sandbox registration/no-mutation test. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/on-disconnect.test.ts#L40" target="_blank" rel="noopener noreferrer"><code>on-disconnect.test.ts</code></a>)</div></div>
585
384
  </details>
586
385
  <details class="compat-row" data-status="ok">
587
386
  <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior"><code>goOffline(db)</code> drains that client's queue once, listeners observe the disconnect write in server order, and <code>goOnline(db)</code> does not resurrect executed operations</span></span></summary>
588
- <div class="compat-evidence"><div class="compat-probe">Oracle <code>rtdb-modular-ondisconnect-clean-set</code>; sandbox clean lifecycle test.</div></div>
387
+ <div class="compat-evidence"><div class="compat-probe">Oracle <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/rtdb-modular/rtdb-modular-ondisconnect-clean-set.json" target="_blank" rel="noopener noreferrer"><code>rtdb-modular-ondisconnect-clean-set</code></a>; sandbox clean lifecycle test. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/on-disconnect.test.ts#L71" target="_blank" rel="noopener noreferrer"><code>on-disconnect.test.ts</code></a>)</div></div>
589
388
  </details>
590
389
  <details class="compat-row" data-status="ok">
591
390
  <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior">Disconnect queues support <code>set</code>, <code>update</code>, <code>remove</code>, exact cancellation, parent cancellation of queued descendants, and captured parent-set/child-set/child-cancel coalescing that preserves the existing canceled child</span></span></summary>
592
- <div class="compat-evidence"><div class="compat-probe">Oracle <code>rtdb-modular-ondisconnect-operations-cancel</code>; sandbox operation/cancellation test, including the captured parent-set + child-cancel merge result.</div></div>
391
+ <div class="compat-evidence"><div class="compat-probe">Oracle <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/rtdb-modular/rtdb-modular-ondisconnect-operations-cancel.json" target="_blank" rel="noopener noreferrer"><code>rtdb-modular-ondisconnect-operations-cancel</code></a>; sandbox operation/cancellation test, including the captured parent-set + child-cancel merge result. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/on-disconnect.test.ts#L96" target="_blank" rel="noopener noreferrer"><code>on-disconnect.test.ts</code></a>)</div></div>
593
392
  </details>
594
393
  <details class="compat-row" data-status="ok">
595
394
  <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior">Rules are evaluated when a disconnect operation is registered and re-evaluated when it executes; an execution-time denial leaves server state unchanged</span></span></summary>
596
- <div class="compat-evidence"><div class="compat-probe">Oracle <code>rtdb-modular-ondisconnect-rules</code> with successful normal-write controls in both rule phases; sandbox rules-timing test.</div></div>
395
+ <div class="compat-evidence"><div class="compat-probe">Oracle <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/rtdb-modular/rtdb-modular-ondisconnect-rules.json" target="_blank" rel="noopener noreferrer"><code>rtdb-modular-ondisconnect-rules</code></a> with successful normal-write controls in both rule phases; sandbox rules-timing test. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/on-disconnect.test.ts#L174" target="_blank" rel="noopener noreferrer"><code>on-disconnect.test.ts</code></a>)</div></div>
597
396
  </details>
598
397
  <details class="compat-row" data-status="ok">
599
398
  <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior"><strong>Local lifecycle contract:</strong> disconnect queues are owned per Database client, drain on app deletion, clear without executing on sandbox reset, and are excluded from persisted RTDB snapshots</span></span></summary>
600
- <div class="compat-evidence"><div class="compat-probe">The explicit <code>rtdb-modular#M82</code> assertion set in <code>unit:database/on-disconnect.test.ts</code> covers independent clients, app deletion, reset clearing, and snapshot exclusion; these sandbox-owned boundaries have no production oracle equivalent.</div></div>
399
+ <div class="compat-evidence"><div class="compat-probe">The explicit <code>rtdb-modular#M82</code> assertion set in <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/on-disconnect.test.ts#L252" target="_blank" rel="noopener noreferrer"><code>unit:database/on-disconnect.test.ts</code></a> covers independent clients, app deletion, reset clearing, and snapshot exclusion; these sandbox-owned boundaries have no production oracle equivalent.</div></div>
601
400
  </details>
602
401
  <details class="compat-row" data-status="ok">
603
402
  <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior"><code>OnDisconnect.setWithPriority(value, priority)</code> writes both the value and RTDB priority metadata when the disconnect queue drains.</span></span></summary>
604
- <div class="compat-evidence"><div class="compat-probe">Oracle <code>rtdb-modular-ondisconnect-operations-cancel</code> captures export-format priority 7; sandbox <code>unit:database/on-disconnect.test.ts</code> replays the value and priority.</div></div>
403
+ <div class="compat-evidence"><div class="compat-probe">Oracle <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/rtdb-modular/rtdb-modular-ondisconnect-operations-cancel.json" target="_blank" rel="noopener noreferrer"><code>rtdb-modular-ondisconnect-operations-cancel</code></a> captures export-format priority 7; sandbox <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/on-disconnect.test.ts#L96" target="_blank" rel="noopener noreferrer"><code>unit:database/on-disconnect.test.ts</code></a> replays the value and priority.</div></div>
605
404
  </details>
606
405
  <details class="compat-row" data-status="diverged">
607
406
  <summary class="compat-line"><span class="compat-dot" data-status="diverged" role="img" aria-label="Diverged (documented)" title="Diverged (documented)"></span><span class="compat-main"><span class="compat-behavior"><strong>Divergence (pending fix):</strong> clean <code>goOffline</code>, app deletion, playground <code>pagehide</code>, and best-effort MessagePort close drain queued operations, but unannounced total renderer/process loss is not guaranteed by the in-memory sandbox</span></span></summary>
608
- <div class="compat-evidence"><div class="compat-probe">Oracle <code>rtdb-modular-ondisconnect-abrupt-exit</code> proves Firebase executes an acknowledged registration after forced writer termination. The two-port worker integration exercises <code>goOffline</code>, served-app deletion, and non-persisted <code>pagehide</code>; browser MessagePort close delivery cannot prove total-process loss without durable host-owned leases.</div></div>
407
+ <div class="compat-evidence"><div class="compat-probe">Oracle <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/rtdb-modular/rtdb-modular-ondisconnect-abrupt-exit.json" target="_blank" rel="noopener noreferrer"><code>rtdb-modular-ondisconnect-abrupt-exit</code></a> proves Firebase executes an acknowledged registration after forced writer termination. The two-port worker integration exercises <code>goOffline</code>, served-app deletion, and non-persisted <code>pagehide</code>; browser MessagePort close delivery cannot prove total-process loss without durable host-owned leases. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/cli/test/serve/worker/rtdb-integration.test.ts#L24" target="_blank" rel="noopener noreferrer"><code>rtdb-integration.test.ts</code></a>)</div></div>
609
408
  </details>
610
409
  <details class="compat-row" data-status="ok">
611
410
  <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior"><code>Database</code> is exported as a runtime constructor value and handles returned by <code>getDatabase()</code> satisfy Firebase's observable prototype and <code>instanceof</code> contract.</span></span></summary>
612
- <div class="compat-evidence"><div class="compat-probe">Oracle <code>rtdb-modular-runtime-class-identity</code>; sandbox row assertion <code>rtdb-modular#M85</code> replays runtime identity.</div></div>
411
+ <div class="compat-evidence"><div class="compat-probe">Oracle <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/rtdb-modular/rtdb-modular-runtime-class-identity.json" target="_blank" rel="noopener noreferrer"><code>rtdb-modular-runtime-class-identity</code></a>; sandbox row assertion <code>rtdb-modular#M85</code> replays runtime identity. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/modular/oracle-conformance-runtime-identity.test.ts#L28" target="_blank" rel="noopener noreferrer"><code>oracle-conformance-runtime-identity.test.ts</code></a>)</div></div>
613
412
  </details>
614
413
  <details class="compat-row" data-status="ok">
615
414
  <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior"><code>DataSnapshot</code> is exported as a runtime constructor value and snapshots returned by reads/listeners satisfy Firebase's observable prototype and <code>instanceof</code> contract.</span></span></summary>
616
- <div class="compat-evidence"><div class="compat-probe">Oracle <code>rtdb-modular-runtime-class-identity</code>; sandbox row assertion <code>rtdb-modular#M86</code> replays runtime identity.</div></div>
415
+ <div class="compat-evidence"><div class="compat-probe">Oracle <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/rtdb-modular/rtdb-modular-runtime-class-identity.json" target="_blank" rel="noopener noreferrer"><code>rtdb-modular-runtime-class-identity</code></a>; sandbox row assertion <code>rtdb-modular#M86</code> replays runtime identity. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/modular/oracle-conformance-runtime-identity.test.ts#L29" target="_blank" rel="noopener noreferrer"><code>oracle-conformance-runtime-identity.test.ts</code></a>)</div></div>
617
416
  </details>
618
417
  <details class="compat-row" data-status="ok">
619
418
  <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior"><code>QueryConstraint</code> is exported as a runtime constructor value and constraints returned by <code>orderBy*</code>, bound, and limit factories satisfy Firebase's observable prototype and <code>instanceof</code> contract.</span></span></summary>
620
- <div class="compat-evidence"><div class="compat-probe">Oracle <code>rtdb-modular-runtime-class-identity</code>; concrete constraint subclasses satisfy <code>instanceof QueryConstraint</code> without using the base prototype directly, replayed by row assertion <code>rtdb-modular#M87</code>.</div></div>
419
+ <div class="compat-evidence"><div class="compat-probe">Oracle <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/rtdb-modular/rtdb-modular-runtime-class-identity.json" target="_blank" rel="noopener noreferrer"><code>rtdb-modular-runtime-class-identity</code></a>; concrete constraint subclasses satisfy <code>instanceof QueryConstraint</code> without using the base prototype directly, replayed by row assertion <code>rtdb-modular#M87</code>. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/modular/oracle-conformance-runtime-identity.test.ts#L29" target="_blank" rel="noopener noreferrer"><code>oracle-conformance-runtime-identity.test.ts</code></a>)</div></div>
621
420
  </details>
622
421
  <details class="compat-row" data-status="ok">
623
422
  <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior"><code>TransactionResult</code> is exported as a runtime constructor value and transaction results satisfy Firebase's observable prototype, <code>instanceof</code>, and <code>toJSON()</code> contract.</span></span></summary>
624
- <div class="compat-evidence"><div class="compat-probe">Oracle <code>rtdb-modular-runtime-class-identity</code>; sandbox row assertion <code>rtdb-modular#M88</code> replays runtime identity and <code>toJSON()</code>.</div></div>
423
+ <div class="compat-evidence"><div class="compat-probe">Oracle <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/rtdb-modular/rtdb-modular-runtime-class-identity.json" target="_blank" rel="noopener noreferrer"><code>rtdb-modular-runtime-class-identity</code></a>; sandbox row assertion <code>rtdb-modular#M88</code> replays runtime identity and <code>toJSON()</code>. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/modular/oracle-conformance-runtime-identity.test.ts#L29" target="_blank" rel="noopener noreferrer"><code>oracle-conformance-runtime-identity.test.ts</code></a>)</div></div>
625
424
  </details>
626
425
  <details class="compat-row" data-status="ok">
627
426
  <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior"><code>setPriority</code> and <code>setWithPriority</code> store, replace, preserve, and clear valid RTDB priority metadata; parent <code>exportVal()</code> and <code>toJSON()</code> recursively include descendant priority metadata.</span></span></summary>
628
- <div class="compat-evidence"><div class="compat-probe">Oracle <code>rtdb-modular-priority-contract</code> captures round-trip/export shape, update/transaction preservation, plain-set clearing, and explicit clearing; replayed by <code>unit:modular/priority-metadata-cdd.test.ts</code>.</div></div>
427
+ <div class="compat-evidence"><div class="compat-probe">Oracle <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/rtdb-modular/rtdb-modular-priority-contract.json" target="_blank" rel="noopener noreferrer"><code>rtdb-modular-priority-contract</code></a> captures round-trip/export shape, update/transaction preservation, plain-set clearing, and explicit clearing; replayed by <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/modular/priority-metadata-cdd.test.ts#L20" target="_blank" rel="noopener noreferrer"><code>unit:modular/priority-metadata-cdd.test.ts</code></a>.</div></div>
629
428
  </details>
630
429
  <details class="compat-row" data-status="ok">
631
430
  <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior"><code>orderByPriority</code> orders by Firebase priority with key tie-breaking and composes with bounds, equality, and limits; plain snapshots and unconstrained queries use the same default priority index, and invalid priority bounds throw synchronously.</span></span></summary>
632
- <div class="compat-evidence"><div class="compat-probe">Oracle <code>rtdb-modular-priority-contract</code> captures priority order, key tie-breaking, bounds, and limits; replayed by <code>unit:modular/priority-metadata-cdd.test.ts</code>.</div></div>
431
+ <div class="compat-evidence"><div class="compat-probe">Oracle <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/rtdb-modular/rtdb-modular-priority-contract.json" target="_blank" rel="noopener noreferrer"><code>rtdb-modular-priority-contract</code></a> captures priority order, key tie-breaking, bounds, and limits; replayed by <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/modular/priority-metadata-cdd.test.ts#L20" target="_blank" rel="noopener noreferrer"><code>unit:modular/priority-metadata-cdd.test.ts</code></a>.</div></div>
633
432
  </details>
634
433
  <details class="compat-row" data-status="ok">
635
434
  <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior">Priority changes participate in plain and explicitly ordered child movement/listener sequencing, transactions, and updates without losing metadata.</span></span></summary>
636
- <div class="compat-evidence"><div class="compat-probe">Oracle <code>rtdb-modular-priority-contract</code> captures priority movement, update/transaction preservation, and descendant delivery when ancestor replacement clears metadata; replayed by <code>unit:modular/priority-metadata-cdd.test.ts</code> and <code>unit:modular/priority-listeners.test.ts</code>. Disconnect priority execution is owned separately by M83.</div></div>
435
+ <div class="compat-evidence"><div class="compat-probe">Oracle <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/rtdb-modular/rtdb-modular-priority-contract.json" target="_blank" rel="noopener noreferrer"><code>rtdb-modular-priority-contract</code></a> captures priority movement, update/transaction preservation, and descendant delivery when ancestor replacement clears metadata; replayed by <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/modular/priority-metadata-cdd.test.ts#L20" target="_blank" rel="noopener noreferrer"><code>unit:modular/priority-metadata-cdd.test.ts</code></a> and <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/modular/priority-listeners.test.ts#L18" target="_blank" rel="noopener noreferrer"><code>unit:modular/priority-listeners.test.ts</code></a>. Disconnect priority execution is owned separately by M83.</div></div>
637
436
  </details>
638
437
  <details class="compat-row" data-status="ok">
639
438
  <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior"><code>off(query)</code> removes listeners only from the equivalent constrained query view, while <code>off(ref)</code> removes listeners from every query view at that path.</span></span></summary>
640
- <div class="compat-evidence"><div class="compat-probe">Oracle <code>rtdb-modular-off-duplicate-registration</code> captures exact constrained-query removal and all-view reference removal with independent live-listener controls; replayed by <code>unit:modular/sandbox-child-events.test.ts</code>.</div></div>
439
+ <div class="compat-evidence"><div class="compat-probe">Oracle <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/rtdb-modular/rtdb-modular-off-duplicate-registration.json" target="_blank" rel="noopener noreferrer"><code>rtdb-modular-off-duplicate-registration</code></a> captures exact constrained-query removal and all-view reference removal with independent live-listener controls; replayed by <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/modular/sandbox-child-events.test.ts#L22" target="_blank" rel="noopener noreferrer"><code>unit:modular/sandbox-child-events.test.ts</code></a>.</div></div>
641
440
  </details>
642
441
  <details class="compat-row" data-status="diverged">
643
442
  <summary class="compat-line"><span class="compat-dot" data-status="diverged" role="img" aria-label="Diverged (documented)" title="Diverged (documented)"></span><span class="compat-main"><span class="compat-behavior"><strong>Divergence:</strong> <code>DatabaseReference</code> and <code>Query</code> equality includes database target, path, and canonical query parameters like Firebase, but <code>toJSON()</code> serializes the local <code>sandbox://rtdb/...</code> identity instead of Firebase's HTTPS database URL.</span></span></summary>
644
- <div class="compat-evidence"><div class="compat-probe">Oracle <code>rtdb-modular-reference-shape-url</code> captures conforming equality plus HTTPS JSON serialization; <code>unit:modular/oracle-conformance-reference-writes.test.ts</code> pins both the production HTTPS shape and the sandbox's <code>sandbox://</code> boundary.</div></div>
443
+ <div class="compat-evidence"><div class="compat-probe">Oracle <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/rtdb-modular/rtdb-modular-reference-shape-url.json" target="_blank" rel="noopener noreferrer"><code>rtdb-modular-reference-shape-url</code></a> captures conforming equality plus HTTPS JSON serialization; <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/modular/oracle-conformance-reference-writes.test.ts#L33" target="_blank" rel="noopener noreferrer"><code>unit:modular/oracle-conformance-reference-writes.test.ts</code></a> pins both the production HTTPS shape and the sandbox's <code>sandbox://</code> boundary.</div></div>
645
444
  </details>
646
445
  <details class="compat-row" data-status="ok">
647
446
  <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior">Query constraint construction preserves Firebase's synchronous validation for limits, child paths, cursor keys, undefined/non-finite/object endpoint values, and index-specific endpoint compatibility, including the captured +Infinity and server-value priority acceptances</span></span></summary>
648
- <div class="compat-evidence"><div class="compat-probe">Oracle <code>rtdb-modular-query-construction-validation</code> repeats the complete validation matrix twice per run and matched across two clean production runs; <code>unit:modular/query-validation.test.ts</code> replays every captured field exactly.</div></div>
447
+ <div class="compat-evidence"><div class="compat-probe">Oracle <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/rtdb-modular/rtdb-modular-query-construction-validation.json" target="_blank" rel="noopener noreferrer"><code>rtdb-modular-query-construction-validation</code></a> repeats the complete validation matrix twice per run and matched across two clean production runs; <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/modular/query-validation.test.ts#L23" target="_blank" rel="noopener noreferrer"><code>unit:modular/query-validation.test.ts</code></a> replays every captured field exactly.</div></div>
649
448
  </details>
650
449
  </div>
651
450
 
@@ -654,28 +453,28 @@ tooling coverage and are not exports of `pyric/database`.
654
453
  <div class="compat-list">
655
454
  <details class="compat-row" data-status="ok">
656
455
  <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior"><code>getDatabase(ctx)</code> returns a tagged sandbox-target handle (frozen identity)</span></span></summary>
657
- <div class="compat-evidence"><div class="compat-probe"><code>unit:modular/database-instances-cdd.test.ts</code> asserts the returned Database carries a sandbox target.</div></div>
456
+ <div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/modular/database-instances-cdd.test.ts#L14" target="_blank" rel="noopener noreferrer"><code>unit:modular/database-instances-cdd.test.ts</code></a> asserts the returned Database carries a sandbox target.</div></div>
658
457
  </details>
659
458
  <details class="compat-row" data-status="ok">
660
459
  <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior"><code>getDatabase(sandbox)</code> returns a tagged sandbox-live handle (per-op identity)</span></span></summary>
661
- <div class="compat-evidence"><div class="compat-probe"><code>unit:modular/database-instances-cdd.test.ts</code> asserts the returned Database carries a sandbox-live target.</div></div>
460
+ <div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/modular/database-instances-cdd.test.ts#L21" target="_blank" rel="noopener noreferrer"><code>unit:modular/database-instances-cdd.test.ts</code></a> asserts the returned Database carries a sandbox-live target.</div></div>
662
461
  </details>
663
462
  <details class="compat-row" data-status="ok">
664
463
  <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior">Inactive canonical <code>firebase/database</code> imports remain the upstream package; the mirror does not create tagged production targets</span></span></summary>
665
- <div class="compat-evidence"><div class="compat-probe">Package-resolution boundary assertion in <code>unit:modular/database-instances-cdd.test.ts</code> proves a real Firebase Database remains untagged and is rejected by the mirror.</div></div>
464
+ <div class="compat-evidence"><div class="compat-probe">Package-resolution boundary assertion in <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/modular/database-instances-cdd.test.ts#L45" target="_blank" rel="noopener noreferrer"><code>unit:modular/database-instances-cdd.test.ts</code></a> proves a real Firebase Database remains untagged and is rejected by the mirror.</div></div>
666
465
  </details>
667
466
  <details class="compat-row" data-status="ok">
668
467
  <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior"><code>getDatabase()</code> (no argument) — wrapped in the playground preview to supply the sandbox; a raw mirror call rejects with package-resolution guidance</span></span></summary>
669
- <div class="compat-evidence"><div class="compat-probe">Phase 3 Tier 5: virtualized in the playground preview scope. Wired at <code>packages/playground/src/components/AppPreview.tsx</code> (slot install with bare-call wrap), <code>packages/playground/src/lib/preview/virtual-imports-plugin.ts</code> (alias map), and <code>packages/playground/src/lib/preview/preview-scope.ts</code> (type-level slot). Mirrors the <code>getAuth</code> / <code>getFirestore</code> wrap pattern. Demo fixture: <code>packages/playground/scripts/fixtures/rtdb-set-get-roundtrip.tsx</code> (bare <code>getDatabase()</code> + <code>set</code>/<code>get</code>/<code>remove</code> round-trip with anonymous sign-in) passes end-to-end through the <code>bun run debug:fixtures</code> Playwright suite.</div>
468
+ <div class="compat-evidence"><div class="compat-probe">Phase 3 Tier 5: virtualized in the playground preview scope. Wired at <code>packages/playground/src/components/AppPreview.tsx</code> (slot install with bare-call wrap), <code>packages/playground/src/lib/preview/virtual-imports-plugin.ts</code> (alias map), and <code>packages/playground/src/lib/preview/preview-scope.ts</code> (type-level slot). Mirrors the <code>getAuth</code> / <code>getFirestore</code> wrap pattern. Demo fixture: <code>packages/playground/scripts/fixtures/rtdb-set-get-roundtrip.tsx</code> (bare <code>getDatabase()</code> + <code>set</code>/<code>get</code>/<code>remove</code> round-trip with anonymous sign-in) passes end-to-end through the <code>bun run debug:fixtures</code> Playwright suite. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/cdd/lifecycle-and-identity.test.ts#L58" target="_blank" rel="noopener noreferrer"><code>lifecycle-and-identity.test.ts</code></a>)</div>
670
469
  <div class="compat-note">(wrap, fixture passing)</div></div>
671
470
  </details>
672
471
  <details class="compat-row" data-status="ok">
673
472
  <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior">Two <code>getDatabase(sandbox)</code> calls share state (same underlying <code>LocalEnvironment</code>)</span></span></summary>
674
- <div class="compat-evidence"><div class="compat-probe"><code>unit:modular/database-instances-cdd.test.ts</code> writes through one handle and reads through another handle owned by the same sandbox.</div></div>
473
+ <div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/modular/database-instances-cdd.test.ts#L28" target="_blank" rel="noopener noreferrer"><code>unit:modular/database-instances-cdd.test.ts</code></a> writes through one handle and reads through another handle owned by the same sandbox.</div></div>
675
474
  </details>
676
475
  <details class="compat-row" data-status="ok">
677
476
  <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior">Handle dispatch by <code>TARGET_SYMBOL</code> brand — refs route to their owning target via a <code>refToTarget</code> WeakMap (mirror of firestore's pattern)</span></span></summary>
678
- <div class="compat-evidence"><div class="compat-probe"><code>unit:modular/database-instances-cdd.test.ts</code> proves references from independent sandbox targets route to independent backends.</div></div>
477
+ <div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/modular/database-instances-cdd.test.ts#L36" target="_blank" rel="noopener noreferrer"><code>unit:modular/database-instances-cdd.test.ts</code></a> proves references from independent sandbox targets route to independent backends.</div></div>
679
478
  </details>
680
479
  </div>
681
480
 
@@ -684,27 +483,27 @@ tooling coverage and are not exports of `pyric/database`.
684
483
  <div class="compat-list">
685
484
  <details class="compat-row" data-status="ok">
686
485
  <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior"><code>ref(db, path)</code> returns a tagged <code>DatabaseReference</code> carrying <code>key</code>, <code>parent</code>, <code>root</code>, and <code>toString()</code>, and synchronously rejects paths containing <code>.</code>, <code>#</code>, <code>$</code>, <code>[</code>, or <code>]</code> with Firebase's captured error shape</span></span></summary>
687
- <div class="compat-evidence"><div class="compat-probe">Oracle <code>rtdb-modular-reference-shape-url</code>; sandbox row assertion <code>rtdb-modular#100</code> replays navigation and every forbidden path character.</div></div>
486
+ <div class="compat-evidence"><div class="compat-probe">Oracle <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/rtdb-modular/rtdb-modular-reference-shape-url.json" target="_blank" rel="noopener noreferrer"><code>rtdb-modular-reference-shape-url</code></a>; sandbox row assertion <code>rtdb-modular#100</code> replays navigation and every forbidden path character. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/modular/oracle-conformance-reference-writes.test.ts#L32" target="_blank" rel="noopener noreferrer"><code>oracle-conformance-reference-writes.test.ts</code></a>)</div></div>
688
487
  </details>
689
488
  <details class="compat-row" data-status="ok">
690
489
  <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior"><code>ref(db)</code> with no path returns the root ref (<code>key === null</code>, <code>parent === null</code>)</span></span></summary>
691
- <div class="compat-evidence"><div class="compat-probe">Oracle <code>rtdb-modular-reference-shape-url</code>; sandbox row assertion <code>rtdb-modular#101</code>.</div></div>
490
+ <div class="compat-evidence"><div class="compat-probe">Oracle <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/rtdb-modular/rtdb-modular-reference-shape-url.json" target="_blank" rel="noopener noreferrer"><code>rtdb-modular-reference-shape-url</code></a>; sandbox row assertion <code>rtdb-modular#101</code>. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/modular/oracle-conformance-reference-writes.test.ts#L33" target="_blank" rel="noopener noreferrer"><code>oracle-conformance-reference-writes.test.ts</code></a>)</div></div>
692
491
  </details>
693
492
  <details class="compat-row" data-status="ok">
694
493
  <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior"><code>child(ref, 'a/b')</code> joins a non-empty relative path, including embedded slashes, and synchronously rejects empty or forbidden-character paths with Firebase's captured error shape</span></span></summary>
695
- <div class="compat-evidence"><div class="compat-probe">Oracle <code>rtdb-modular-reference-shape-url</code>; sandbox row assertion <code>rtdb-modular#102</code> replays embedded-path joining plus empty/invalid validation.</div></div>
494
+ <div class="compat-evidence"><div class="compat-probe">Oracle <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/rtdb-modular/rtdb-modular-reference-shape-url.json" target="_blank" rel="noopener noreferrer"><code>rtdb-modular-reference-shape-url</code></a>; sandbox row assertion <code>rtdb-modular#102</code> replays embedded-path joining plus empty/invalid validation. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/modular/oracle-conformance-reference-writes.test.ts#L33" target="_blank" rel="noopener noreferrer"><code>oracle-conformance-reference-writes.test.ts</code></a>)</div></div>
696
495
  </details>
697
496
  <details class="compat-row" data-status="ok">
698
497
  <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior"><code>ref.parent</code> is <code>null</code> at root, otherwise the parent ref</span></span></summary>
699
- <div class="compat-evidence"><div class="compat-probe">Oracle <code>rtdb-modular-reference-shape-url</code>; sandbox row assertion <code>rtdb-modular#103</code>.</div></div>
498
+ <div class="compat-evidence"><div class="compat-probe">Oracle <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/rtdb-modular/rtdb-modular-reference-shape-url.json" target="_blank" rel="noopener noreferrer"><code>rtdb-modular-reference-shape-url</code></a>; sandbox row assertion <code>rtdb-modular#103</code>. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/modular/oracle-conformance-reference-writes.test.ts#L33" target="_blank" rel="noopener noreferrer"><code>oracle-conformance-reference-writes.test.ts</code></a>)</div></div>
700
499
  </details>
701
500
  <details class="compat-row" data-status="ok">
702
501
  <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior"><code>ref.key</code> is the final path segment, <code>null</code> for root</span></span></summary>
703
- <div class="compat-evidence"><div class="compat-probe">Oracle <code>rtdb-modular-reference-shape-url</code>; sandbox row assertion <code>rtdb-modular#104</code>.</div></div>
502
+ <div class="compat-evidence"><div class="compat-probe">Oracle <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/rtdb-modular/rtdb-modular-reference-shape-url.json" target="_blank" rel="noopener noreferrer"><code>rtdb-modular-reference-shape-url</code></a>; sandbox row assertion <code>rtdb-modular#104</code>. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/modular/oracle-conformance-reference-writes.test.ts#L33" target="_blank" rel="noopener noreferrer"><code>oracle-conformance-reference-writes.test.ts</code></a>)</div></div>
704
503
  </details>
705
504
  <details class="compat-row" data-status="ok">
706
505
  <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior">Unknown ref (not produced by this package) → <code>TypeError</code> in shim ops</span></span></summary>
707
- <div class="compat-evidence"><div class="compat-probe">Oracle <code>rtdb-modular-reference-shape-url</code> captures a synchronous <code>TypeError</code>; sandbox row assertion <code>rtdb-modular#105</code> matches its timing and constructor.</div></div>
506
+ <div class="compat-evidence"><div class="compat-probe">Oracle <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/rtdb-modular/rtdb-modular-reference-shape-url.json" target="_blank" rel="noopener noreferrer"><code>rtdb-modular-reference-shape-url</code></a> captures a synchronous <code>TypeError</code>; sandbox row assertion <code>rtdb-modular#105</code> matches its timing and constructor. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/modular/oracle-conformance-reference-writes.test.ts#L33" target="_blank" rel="noopener noreferrer"><code>oracle-conformance-reference-writes.test.ts</code></a>)</div></div>
708
507
  </details>
709
508
  </div>
710
509
 
@@ -713,23 +512,23 @@ tooling coverage and are not exports of `pyric/database`.
713
512
  <div class="compat-list">
714
513
  <details class="compat-row" data-status="ok">
715
514
  <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior">Returns a <code>DataSnapshot</code> carrying <code>.val()</code>, <code>.exists()</code>, <code>.key</code>, <code>.ref</code>, <code>.size</code> (getter, returns child count), <code>.hasChildren()</code>, <code>.hasChild(path)</code>, <code>.forEach(cb)</code>. The legacy namespaced-SDK method <code>.numChildren()</code> is <strong>NOT</strong> on the modular DataSnapshot — use <code>.size</code> instead. Observed: <code>hasNumChildren: false</code>, <code>size: 3</code> for a <code>{a,b,c}</code> object, <code>forEachKeys: ['a','b','c']</code> against blockingfun, fb-js-sdk 12.13.0.</span></span></summary>
716
- <div class="compat-evidence"><div class="compat-probe">oracle: <code>packages/conformance/observations/rtdb-modular/rtdb-modular-get-snapshot-shape.json</code></div></div>
515
+ <div class="compat-evidence"><div class="compat-probe">oracle: <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/rtdb-modular/rtdb-modular-get-snapshot-shape.json" target="_blank" rel="noopener noreferrer"><code>packages/conformance/observations/rtdb-modular/rtdb-modular-get-snapshot-shape.json</code></a> (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/modular/snapshot-shape.test.ts#L9" target="_blank" rel="noopener noreferrer"><code>snapshot-shape.test.ts</code></a>)</div></div>
717
516
  </details>
718
517
  <details class="compat-row" data-status="ok">
719
518
  <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior"><code>snap.val()</code> returns <code>null</code> for a missing path (NOT a thrown error — RTDB diverges from Firestore here; <code>getDoc</code> returns <code>exists()===false</code> but <code>get</code> on RTDB just returns a <code>null</code>-val snapshot)</span></span></summary>
720
- <div class="compat-evidence"><div class="compat-probe">oracle: <code>packages/conformance/observations/rtdb-modular/rtdb-modular-get-missing-path.json</code> — observed <code>threw: false, val: null, exists: false</code> on a never-written path against blockingfun.</div></div>
519
+ <div class="compat-evidence"><div class="compat-probe">oracle: <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/rtdb-modular/rtdb-modular-get-missing-path.json" target="_blank" rel="noopener noreferrer"><code>packages/conformance/observations/rtdb-modular/rtdb-modular-get-missing-path.json</code></a> — observed <code>threw: false, val: null, exists: false</code> on a never-written path against blockingfun. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/cdd/lifecycle-and-identity.test.ts#L38" target="_blank" rel="noopener noreferrer"><code>lifecycle-and-identity.test.ts</code></a>)</div></div>
721
520
  </details>
722
521
  <details class="compat-row" data-status="ok">
723
522
  <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior"><code>snap.exists()</code> is <code>false</code> when <code>val() === null</code>, <code>true</code> otherwise</span></span></summary>
724
- <div class="compat-evidence"><div class="compat-probe">oracle: <code>packages/conformance/observations/rtdb-modular/rtdb-modular-get-missing-path.json</code> — observed <code>exists: false</code> for <code>val: null</code>.</div></div>
523
+ <div class="compat-evidence"><div class="compat-probe">oracle: <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/rtdb-modular/rtdb-modular-get-missing-path.json" target="_blank" rel="noopener noreferrer"><code>packages/conformance/observations/rtdb-modular/rtdb-modular-get-missing-path.json</code></a> — observed <code>exists: false</code> for <code>val: null</code>. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/cdd/lifecycle-and-identity.test.ts#L38" target="_blank" rel="noopener noreferrer"><code>lifecycle-and-identity.test.ts</code></a>)</div></div>
725
524
  </details>
726
525
  <details class="compat-row" data-status="ok">
727
526
  <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior">Round-trip: <code>set(ref, payload)</code> then <code>get(ref)</code> returns the payload (lock the basic write→read invariant)</span></span></summary>
728
- <div class="compat-evidence"><div class="compat-probe">oracle: <code>packages/conformance/observations/rtdb/rtdb-set-then-get-roundtrip.json</code> — the payload round-trips structurally on both sides (this row's claim holds). NOTE — adjacent divergence pinned in <code>oracle-conformance.test.ts</code>: prod returns object children in LEXICOGRAPHIC key order (the capture's <code>roundTripEqual: false</code> — a <code>JSON.stringify</code> round-trip against a non-sorted payload fails), while the sandbox preserves insertion order (stringify round-trip succeeds). Key-order-sensitive consumers behave differently.</div></div>
527
+ <div class="compat-evidence"><div class="compat-probe">oracle: <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/rtdb/rtdb-set-then-get-roundtrip.json" target="_blank" rel="noopener noreferrer"><code>packages/conformance/observations/rtdb/rtdb-set-then-get-roundtrip.json</code></a> — the payload round-trips structurally on both sides (this row's claim holds). NOTE — adjacent divergence pinned in <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/oracle-conformance.test.ts#L95" target="_blank" rel="noopener noreferrer"><code>oracle-conformance.test.ts</code></a>: prod returns object children in LEXICOGRAPHIC key order (the capture's <code>roundTripEqual: false</code> — a <code>JSON.stringify</code> round-trip against a non-sorted payload fails), while the sandbox preserves insertion order (stringify round-trip succeeds). Key-order-sensitive consumers behave differently.</div></div>
729
528
  </details>
730
529
  <details class="compat-row" data-status="ok">
731
530
  <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior">Rules-denied read throws a plain <code>Error</code> (NOT a <code>FirebaseError</code>) with <code>code: 'PERMISSION_DENIED'</code> (uppercase snake-case) — matches the agent-tool rows #15/#20</span></span></summary>
732
- <div class="compat-evidence"><div class="compat-probe">oracle: <code>packages/conformance/observations/rtdb/rtdb-rules-denied-error-code.json</code></div></div>
531
+ <div class="compat-evidence"><div class="compat-probe">oracle: <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/rtdb/rtdb-rules-denied-error-code.json" target="_blank" rel="noopener noreferrer"><code>packages/conformance/observations/rtdb/rtdb-rules-denied-error-code.json</code></a> (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/cdd/lifecycle-and-identity.test.ts#L38" target="_blank" rel="noopener noreferrer"><code>lifecycle-and-identity.test.ts</code></a>)</div></div>
733
532
  </details>
734
533
  </div>
735
534
 
@@ -738,23 +537,23 @@ tooling coverage and are not exports of `pyric/database`.
738
537
  <div class="compat-list">
739
538
  <details class="compat-row" data-status="ok">
740
539
  <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior">Replaces the value at the path entirely; resolves to <code>undefined</code> (unlike <code>setDoc</code> which resolves to <code>void</code>, RTDB's <code>set</code> is documented as <code>Promise&lt;void&gt;</code>)</span></span></summary>
741
- <div class="compat-evidence"><div class="compat-probe">Oracle <code>rtdb-modular-write-return-validation</code> captures <code>null</code> in JSON for the resolved <code>undefined</code> value; sandbox row assertion <code>rtdb-modular#111</code> matches it.</div></div>
540
+ <div class="compat-evidence"><div class="compat-probe">Oracle <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/rtdb-modular/rtdb-modular-write-return-validation.json" target="_blank" rel="noopener noreferrer"><code>rtdb-modular-write-return-validation</code></a> captures <code>null</code> in JSON for the resolved <code>undefined</code> value; sandbox row assertion <code>rtdb-modular#111</code> matches it. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/modular/oracle-conformance-reference-writes.test.ts#L138" target="_blank" rel="noopener noreferrer"><code>oracle-conformance-reference-writes.test.ts</code></a>)</div></div>
742
541
  </details>
743
542
  <details class="compat-row" data-status="ok">
744
543
  <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior"><code>set(ref, null)</code> removes the path entirely — equivalent to <code>remove(ref)</code>, subsequent <code>get</code> returns <code>null</code>-val snapshot</span></span></summary>
745
- <div class="compat-evidence"><div class="compat-probe">oracle: <code>packages/conformance/observations/rtdb-modular/rtdb-modular-set-null-equals-remove.json</code> — observed <code>beforeExists: true → afterExists: false, afterVal: null</code> after <code>set(ref, null)</code>.</div></div>
544
+ <div class="compat-evidence"><div class="compat-probe">oracle: <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/rtdb-modular/rtdb-modular-set-null-equals-remove.json" target="_blank" rel="noopener noreferrer"><code>packages/conformance/observations/rtdb-modular/rtdb-modular-set-null-equals-remove.json</code></a> — observed <code>beforeExists: true → afterExists: false, afterVal: null</code> after <code>set(ref, null)</code>. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/cdd/public-operations.test.ts#L20" target="_blank" rel="noopener noreferrer"><code>public-operations.test.ts</code></a>)</div></div>
746
545
  </details>
747
546
  <details class="compat-row" data-status="ok">
748
547
  <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior">Nested objects overwrite — <code>set(ref, {a: 1})</code> after <code>set(ref, {a: 1, b: 2})</code> leaves <code>{a: 1}</code> only, NOT a merge (RTDB <code>set</code> is replacement, not merge)</span></span></summary>
749
- <div class="compat-evidence"><div class="compat-probe">oracle: <code>packages/conformance/observations/rtdb-modular/rtdb-modular-set-replaces-not-merges.json</code> — observed <code>final: {a: 1}</code> with <code>b</code> absent after the second set.</div></div>
548
+ <div class="compat-evidence"><div class="compat-probe">oracle: <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/rtdb-modular/rtdb-modular-set-replaces-not-merges.json" target="_blank" rel="noopener noreferrer"><code>packages/conformance/observations/rtdb-modular/rtdb-modular-set-replaces-not-merges.json</code></a> — observed <code>final: {a: 1}</code> with <code>b</code> absent after the second set. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/cdd/public-operations.test.ts#L20" target="_blank" rel="noopener noreferrer"><code>public-operations.test.ts</code></a>)</div></div>
750
549
  </details>
751
550
  <details class="compat-row" data-status="ok">
752
551
  <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior">Primitive round-trip — numbers, strings, booleans, arrays all survive a set→get cycle</span></span></summary>
753
- <div class="compat-evidence"><div class="compat-probe">oracle: <code>packages/conformance/observations/rtdb/rtdb-set-then-get-roundtrip.json</code> — the payload round-trips structurally on both sides (this row's claim holds). NOTE — adjacent divergence pinned in <code>oracle-conformance.test.ts</code>: prod returns object children in LEXICOGRAPHIC key order (the capture's <code>roundTripEqual: false</code> — a <code>JSON.stringify</code> round-trip against a non-sorted payload fails), while the sandbox preserves insertion order (stringify round-trip succeeds). Key-order-sensitive consumers behave differently.</div></div>
552
+ <div class="compat-evidence"><div class="compat-probe">oracle: <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/rtdb/rtdb-set-then-get-roundtrip.json" target="_blank" rel="noopener noreferrer"><code>packages/conformance/observations/rtdb/rtdb-set-then-get-roundtrip.json</code></a> — the payload round-trips structurally on both sides (this row's claim holds). NOTE — adjacent divergence pinned in <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/oracle-conformance.test.ts#L95" target="_blank" rel="noopener noreferrer"><code>oracle-conformance.test.ts</code></a>: prod returns object children in LEXICOGRAPHIC key order (the capture's <code>roundTripEqual: false</code> — a <code>JSON.stringify</code> round-trip against a non-sorted payload fails), while the sandbox preserves insertion order (stringify round-trip succeeds). Key-order-sensitive consumers behave differently.</div></div>
754
553
  </details>
755
554
  <details class="compat-row" data-status="ok">
756
555
  <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior">Rules-denied write throws plain <code>Error</code> with <code>code: 'PERMISSION_DENIED'</code> (same shape as #110)</span></span></summary>
757
- <div class="compat-evidence"><div class="compat-probe">oracle: <code>packages/conformance/observations/rtdb/rtdb-rules-denied-error-code.json</code></div></div>
556
+ <div class="compat-evidence"><div class="compat-probe">oracle: <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/rtdb/rtdb-rules-denied-error-code.json" target="_blank" rel="noopener noreferrer"><code>packages/conformance/observations/rtdb/rtdb-rules-denied-error-code.json</code></a> (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/cdd/public-operations.test.ts#L20" target="_blank" rel="noopener noreferrer"><code>public-operations.test.ts</code></a>)</div></div>
758
557
  </details>
759
558
  </div>
760
559
 
@@ -763,23 +562,23 @@ tooling coverage and are not exports of `pyric/database`.
763
562
  <div class="compat-list">
764
563
  <details class="compat-row" data-status="ok">
765
564
  <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior"><code>update(ref, {a: 1, b: 2})</code> merges top-level keys at the ref; unspecified keys preserved (in contrast to <code>set</code>'s replacement)</span></span></summary>
766
- <div class="compat-evidence"><div class="compat-probe">oracle: <code>packages/conformance/observations/rtdb-modular/rtdb-modular-update-merges-keys.json</code> — after <code>set({a:1,b:2})</code> then <code>update({a:10})</code>, observed <code>final: {a:10, b:2}</code>.</div></div>
565
+ <div class="compat-evidence"><div class="compat-probe">oracle: <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/rtdb-modular/rtdb-modular-update-merges-keys.json" target="_blank" rel="noopener noreferrer"><code>packages/conformance/observations/rtdb-modular/rtdb-modular-update-merges-keys.json</code></a> — after <code>set({a:1,b:2})</code> then <code>update({a:10})</code>, observed <code>final: {a:10, b:2}</code>. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/cdd/public-operations.test.ts#L60" target="_blank" rel="noopener noreferrer"><code>public-operations.test.ts</code></a>)</div></div>
767
566
  </details>
768
567
  <details class="compat-row" data-status="ok">
769
568
  <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior">Multi-path update — <code>update(parentRef, { 'a/x': 1, 'b/y': 2 })</code> lands BOTH writes atomically at distinct subtrees (RTDB's most distinctive feature; this is the "fan-out" pattern)</span></span></summary>
770
- <div class="compat-evidence"><div class="compat-probe">oracle: <code>packages/conformance/observations/rtdb-modular/rtdb-modular-update-multipath-atomic.json</code> — observed <code>aX: 1, bY: 2</code> both readable after a single update call.</div></div>
569
+ <div class="compat-evidence"><div class="compat-probe">oracle: <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/rtdb-modular/rtdb-modular-update-multipath-atomic.json" target="_blank" rel="noopener noreferrer"><code>packages/conformance/observations/rtdb-modular/rtdb-modular-update-multipath-atomic.json</code></a> — observed <code>aX: 1, bY: 2</code> both readable after a single update call. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/cdd/public-operations.test.ts#L60" target="_blank" rel="noopener noreferrer"><code>public-operations.test.ts</code></a>)</div></div>
771
570
  </details>
772
571
  <details class="compat-row" data-status="ok">
773
572
  <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior">Multi-path update is atomic: if any path is denied by rules, the entire update rejects and no path is written</span></span></summary>
774
- <div class="compat-evidence"><div class="compat-probe">oracle: <code>packages/conformance/observations/rtdb-modular/rtdb-modular-update-multipath-rules-denial.json</code> — observed <code>threw: true, code: 'PERMISSION_DENIED'</code> AND <code>okPathWrittenDespiteDenial: false</code> (the otherwise-permitted path also rolled back).</div></div>
573
+ <div class="compat-evidence"><div class="compat-probe">oracle: <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/rtdb-modular/rtdb-modular-update-multipath-rules-denial.json" target="_blank" rel="noopener noreferrer"><code>packages/conformance/observations/rtdb-modular/rtdb-modular-update-multipath-rules-denial.json</code></a> — observed <code>threw: true, code: 'PERMISSION_DENIED'</code> AND <code>okPathWrittenDespiteDenial: false</code> (the otherwise-permitted path also rolled back). (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/cdd/public-operations.test.ts#L60" target="_blank" rel="noopener noreferrer"><code>public-operations.test.ts</code></a>)</div></div>
775
574
  </details>
776
575
  <details class="compat-row" data-status="ok">
777
576
  <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior">Setting a key to <code>null</code> inside <code>update</code> removes that key — same equivalence as <code>set(ref, null)</code></span></span></summary>
778
- <div class="compat-evidence"><div class="compat-probe">oracle: <code>packages/conformance/observations/rtdb-modular/rtdb-modular-update-null-removes-key.json</code> — after <code>set({a:1,b:2})</code> then <code>update({a:null})</code>, observed <code>final: {b:2}</code> with <code>a</code> absent.</div></div>
577
+ <div class="compat-evidence"><div class="compat-probe">oracle: <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/rtdb-modular/rtdb-modular-update-null-removes-key.json" target="_blank" rel="noopener noreferrer"><code>packages/conformance/observations/rtdb-modular/rtdb-modular-update-null-removes-key.json</code></a> — after <code>set({a:1,b:2})</code> then <code>update({a:null})</code>, observed <code>final: {b:2}</code> with <code>a</code> absent. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/cdd/public-operations.test.ts#L60" target="_blank" rel="noopener noreferrer"><code>public-operations.test.ts</code></a>)</div></div>
779
578
  </details>
780
579
  <details class="compat-row" data-status="ok">
781
580
  <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior">Update path validation — overlapping paths (e.g. <code>'/a'</code> and <code>'/a/x'</code> in the same call) throws synchronously before any write</span></span></summary>
782
- <div class="compat-evidence"><div class="compat-probe">Oracle <code>rtdb-modular-write-return-validation</code> captures a synchronous ancestor-path error and unchanged terminal state; sandbox row assertion <code>rtdb-modular#120</code> matches both.</div></div>
581
+ <div class="compat-evidence"><div class="compat-probe">Oracle <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/rtdb-modular/rtdb-modular-write-return-validation.json" target="_blank" rel="noopener noreferrer"><code>rtdb-modular-write-return-validation</code></a> captures a synchronous ancestor-path error and unchanged terminal state; sandbox row assertion <code>rtdb-modular#120</code> matches both. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/modular/oracle-conformance-reference-writes.test.ts#L139" target="_blank" rel="noopener noreferrer"><code>oracle-conformance-reference-writes.test.ts</code></a>)</div></div>
783
582
  </details>
784
583
  </div>
785
584
 
@@ -788,15 +587,15 @@ tooling coverage and are not exports of `pyric/database`.
788
587
  <div class="compat-list">
789
588
  <details class="compat-row" data-status="ok">
790
589
  <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior">Removes the value AND all children; subsequent <code>get</code> returns <code>null</code>-val snapshot</span></span></summary>
791
- <div class="compat-evidence"><div class="compat-probe">oracle: <code>packages/conformance/observations/rtdb/rtdb-remove-vs-set-null.json</code></div></div>
590
+ <div class="compat-evidence"><div class="compat-probe">oracle: <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/rtdb/rtdb-remove-vs-set-null.json" target="_blank" rel="noopener noreferrer"><code>packages/conformance/observations/rtdb/rtdb-remove-vs-set-null.json</code></a> (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/cdd/public-operations.test.ts#L66" target="_blank" rel="noopener noreferrer"><code>public-operations.test.ts</code></a>)</div></div>
792
591
  </details>
793
592
  <details class="compat-row" data-status="ok">
794
593
  <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior">Idempotent — <code>remove</code> on a path that's already absent resolves successfully (no-throw)</span></span></summary>
795
- <div class="compat-evidence"><div class="compat-probe">oracle: <code>packages/conformance/observations/rtdb-modular/rtdb-modular-remove-idempotent.json</code> — <code>remove</code> on a never-written path observed <code>threw: false, afterExists: false</code>.</div></div>
594
+ <div class="compat-evidence"><div class="compat-probe">oracle: <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/rtdb-modular/rtdb-modular-remove-idempotent.json" target="_blank" rel="noopener noreferrer"><code>packages/conformance/observations/rtdb-modular/rtdb-modular-remove-idempotent.json</code></a> — <code>remove</code> on a never-written path observed <code>threw: false, afterExists: false</code>. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/cdd/public-operations.test.ts#L66" target="_blank" rel="noopener noreferrer"><code>public-operations.test.ts</code></a>)</div></div>
796
595
  </details>
797
596
  <details class="compat-row" data-status="ok">
798
597
  <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior"><code>remove(ref)</code> and <code>set(ref, null)</code> produce the same end state — locks the documented RTDB invariant</span></span></summary>
799
- <div class="compat-evidence"><div class="compat-probe">oracle: <code>packages/conformance/observations/rtdb/rtdb-remove-vs-set-null.json</code></div></div>
598
+ <div class="compat-evidence"><div class="compat-probe">oracle: <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/rtdb/rtdb-remove-vs-set-null.json" target="_blank" rel="noopener noreferrer"><code>packages/conformance/observations/rtdb/rtdb-remove-vs-set-null.json</code></a> (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/cdd/public-operations.test.ts#L66" target="_blank" rel="noopener noreferrer"><code>public-operations.test.ts</code></a>)</div></div>
800
599
  </details>
801
600
  </div>
802
601
 
@@ -805,19 +604,19 @@ tooling coverage and are not exports of `pyric/database`.
805
604
  <div class="compat-list">
806
605
  <details class="compat-row" data-status="ok">
807
606
  <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior"><code>push(ref).key</code> is a 20-char string starting with <code>-</code>, available <strong>synchronously</strong> (client-side mint, no server round-trip required)</span></span></summary>
808
- <div class="compat-evidence"><div class="compat-probe">oracle: <code>packages/conformance/observations/rtdb/rtdb-push-autoid-format.json</code></div></div>
607
+ <div class="compat-evidence"><div class="compat-probe">oracle: <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/rtdb/rtdb-push-autoid-format.json" target="_blank" rel="noopener noreferrer"><code>packages/conformance/observations/rtdb/rtdb-push-autoid-format.json</code></a> (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/cdd/public-operations.test.ts#L69" target="_blank" rel="noopener noreferrer"><code>public-operations.test.ts</code></a>)</div></div>
809
608
  </details>
810
609
  <details class="compat-row" data-status="ok">
811
610
  <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior">Sequential <code>push</code> calls produce monotonically-sortable keys (timestamp-prefixed for chronological ordering via <code>orderByKey</code>)</span></span></summary>
812
- <div class="compat-evidence"><div class="compat-probe">oracle: <code>packages/conformance/observations/rtdb/rtdb-push-autoid-format.json</code></div></div>
611
+ <div class="compat-evidence"><div class="compat-probe">oracle: <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/rtdb/rtdb-push-autoid-format.json" target="_blank" rel="noopener noreferrer"><code>packages/conformance/observations/rtdb/rtdb-push-autoid-format.json</code></a> (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/cdd/public-operations.test.ts#L69" target="_blank" rel="noopener noreferrer"><code>public-operations.test.ts</code></a>)</div></div>
813
612
  </details>
814
613
  <details class="compat-row" data-status="ok">
815
614
  <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior"><code>push(ref, value)</code> writes the value AND returns the new child ref (both behaviors in one call); <code>push(ref)</code> mints the ref without writing</span></span></summary>
816
- <div class="compat-evidence"><div class="compat-probe">oracle: <code>packages/conformance/observations/rtdb-modular/rtdb-modular-push-with-value.json</code> — <code>await push(parent, {hello:'world'})</code> returned a ref with a 20-char key; subsequent <code>get(r)</code> returned <code>{hello:'world'}</code>.</div></div>
615
+ <div class="compat-evidence"><div class="compat-probe">oracle: <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/rtdb-modular/rtdb-modular-push-with-value.json" target="_blank" rel="noopener noreferrer"><code>packages/conformance/observations/rtdb-modular/rtdb-modular-push-with-value.json</code></a> — <code>await push(parent, {hello:'world'})</code> returned a ref with a 20-char key; subsequent <code>get(r)</code> returned <code>{hello:'world'}</code>. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/cdd/public-operations.test.ts#L69" target="_blank" rel="noopener noreferrer"><code>public-operations.test.ts</code></a>)</div></div>
817
616
  </details>
818
617
  <details class="compat-row" data-status="ok">
819
618
  <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior">The returned ref <code>r = push(parent, value)</code> is usable in follow-up ops: <code>get(r)</code>, <code>set(r, …)</code>, <code>remove(r)</code></span></span></summary>
820
- <div class="compat-evidence"><div class="compat-probe">oracle: <code>packages/conformance/observations/rtdb-modular/rtdb-modular-push-with-value.json</code> — observed all 4 follow-up ops succeed through the returned ref (<code>refIsUsableForFollowupOps: true</code>).</div></div>
619
+ <div class="compat-evidence"><div class="compat-probe">oracle: <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/rtdb-modular/rtdb-modular-push-with-value.json" target="_blank" rel="noopener noreferrer"><code>packages/conformance/observations/rtdb-modular/rtdb-modular-push-with-value.json</code></a> — observed all 4 follow-up ops succeed through the returned ref (<code>refIsUsableForFollowupOps: true</code>). (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/cdd/public-operations.test.ts#L69" target="_blank" rel="noopener noreferrer"><code>public-operations.test.ts</code></a>)</div></div>
821
620
  </details>
822
621
  </div>
823
622
 
@@ -826,23 +625,23 @@ tooling coverage and are not exports of `pyric/database`.
826
625
  <div class="compat-list">
827
626
  <details class="compat-row" data-status="ok">
828
627
  <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior">Subscribing to a path with <strong>existing data</strong> fires the listener once with the current snapshot (the "initial fire")</span></span></summary>
829
- <div class="compat-evidence"><div class="compat-probe">oracle: <code>packages/conformance/observations/rtdb-modular/rtdb-modular-onvalue-initial-with-data.json</code> — observed exactly 1 initial fire within ~46ms of subscribe, snapshot.val() === the seeded payload.</div></div>
628
+ <div class="compat-evidence"><div class="compat-probe">oracle: <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/rtdb-modular/rtdb-modular-onvalue-initial-with-data.json" target="_blank" rel="noopener noreferrer"><code>packages/conformance/observations/rtdb-modular/rtdb-modular-onvalue-initial-with-data.json</code></a> — observed exactly 1 initial fire within ~46ms of subscribe, snapshot.val() === the seeded payload. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/cdd/public-operations.test.ts#L75" target="_blank" rel="noopener noreferrer"><code>public-operations.test.ts</code></a>)</div></div>
830
629
  </details>
831
630
  <details class="compat-row" data-status="ok">
832
631
  <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior">Subscribing to a <strong>nonexistent path</strong> still fires the listener once — with a <code>null</code>-val snapshot AND <code>exists() === false</code>. Matches Firestore's <code>onSnapshot</code>-on-missing-doc semantics: prod RTDB does NOT silently skip the initial fire for empty paths.</span></span></summary>
833
- <div class="compat-evidence"><div class="compat-probe">oracle: <code>packages/conformance/observations/rtdb-modular/rtdb-modular-onvalue-initial-no-data.json</code> — observed 1 initial fire on a never-written path with <code>firstFire.val: null, firstFire.exists: false</code> (~55ms after subscribe).</div></div>
632
+ <div class="compat-evidence"><div class="compat-probe">oracle: <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/rtdb-modular/rtdb-modular-onvalue-initial-no-data.json" target="_blank" rel="noopener noreferrer"><code>packages/conformance/observations/rtdb-modular/rtdb-modular-onvalue-initial-no-data.json</code></a> — observed 1 initial fire on a never-written path with <code>firstFire.val: null, firstFire.exists: false</code> (~55ms after subscribe). (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/cdd/public-operations.test.ts#L28" target="_blank" rel="noopener noreferrer"><code>public-operations.test.ts</code></a>)</div></div>
834
633
  </details>
835
634
  <details class="compat-row" data-status="ok">
836
635
  <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior">Subsequent <code>set(ref, …)</code> fires the listener with the new value</span></span></summary>
837
- <div class="compat-evidence"><div class="compat-probe">oracle: <code>packages/conformance/observations/rtdb/rtdb-onvalue-fires-on-set.json</code> — observed 1 fire per <code>set()</code> (1+1+1 = 3 total: initial-null, after-first-set, after-second-set).</div></div>
636
+ <div class="compat-evidence"><div class="compat-probe">oracle: <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/rtdb/rtdb-onvalue-fires-on-set.json" target="_blank" rel="noopener noreferrer"><code>packages/conformance/observations/rtdb/rtdb-onvalue-fires-on-set.json</code></a> — observed 1 fire per <code>set()</code> (1+1+1 = 3 total: initial-null, after-first-set, after-second-set). (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/cdd/public-operations.test.ts#L75" target="_blank" rel="noopener noreferrer"><code>public-operations.test.ts</code></a>)</div></div>
838
637
  </details>
839
638
  <details class="compat-row" data-status="ok">
840
639
  <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior">Unsubscribe — the returned unsubscribe function stops further fires; subsequent writes produce 0 additional fires after <code>unsub()</code></span></span></summary>
841
- <div class="compat-evidence"><div class="compat-probe">oracle: <code>packages/conformance/observations/rtdb-modular/rtdb-modular-onvalue-unsubscribe.json</code> — observed <code>preUnsubFires: 2, postUnsubFires: 2</code> (a write performed after <code>unsub()</code> produced 0 additional fires within a 500ms settle window).</div></div>
640
+ <div class="compat-evidence"><div class="compat-probe">oracle: <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/rtdb-modular/rtdb-modular-onvalue-unsubscribe.json" target="_blank" rel="noopener noreferrer"><code>packages/conformance/observations/rtdb-modular/rtdb-modular-onvalue-unsubscribe.json</code></a> — observed <code>preUnsubFires: 2, postUnsubFires: 2</code> (a write performed after <code>unsub()</code> produced 0 additional fires within a 500ms settle window). (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/cdd/public-operations.test.ts#L75" target="_blank" rel="noopener noreferrer"><code>public-operations.test.ts</code></a>)</div></div>
842
641
  </details>
843
642
  <details class="compat-row" data-status="ok">
844
643
  <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior">The returned value from <code>onValue(ref, cb)</code> is the unsubscribe function (NOT an object); calling it removes the listener</span></span></summary>
845
- <div class="compat-evidence"><div class="compat-probe"><code>unit:modular/listener-lifecycle-cdd.test.ts</code> asserts the return is a function and that invoking it stops subsequent delivery; production behavior is adjacent to oracle-backed row #131.</div></div>
644
+ <div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/modular/listener-lifecycle-cdd.test.ts#L208" target="_blank" rel="noopener noreferrer"><code>unit:modular/listener-lifecycle-cdd.test.ts</code></a> asserts the return is a function and that invoking it stops subsequent delivery; production behavior is adjacent to oracle-backed row #131.</div></div>
846
645
  </details>
847
646
  </div>
848
647
 
@@ -851,23 +650,23 @@ tooling coverage and are not exports of `pyric/database`.
851
650
  <div class="compat-list">
852
651
  <details class="compat-row" data-status="ok">
853
652
  <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior"><code>onChildAdded</code> replays the existing children on subscribe — one fire per existing child key, in the default priority index with key tie-breaking (unlike <code>onValue</code> which fires once with the parent snapshot)</span></span></summary>
854
- <div class="compat-evidence"><div class="compat-probe">oracle: <code>packages/conformance/observations/rtdb-modular/rtdb-modular-onchildadded-initial-replay.json</code> — seeded <code>{k1, k2, k3}</code>, observed 3 initial fires with <code>firedKeys: ['k1', 'k2', 'k3']</code>; the default priority/key order is separately pinned by <code>rtdb-modular-priority-contract</code>.</div></div>
653
+ <div class="compat-evidence"><div class="compat-probe">oracle: <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/rtdb-modular/rtdb-modular-onchildadded-initial-replay.json" target="_blank" rel="noopener noreferrer"><code>packages/conformance/observations/rtdb-modular/rtdb-modular-onchildadded-initial-replay.json</code></a> — seeded <code>{k1, k2, k3}</code>, observed 3 initial fires with <code>firedKeys: ['k1', 'k2', 'k3']</code>; the default priority/key order is separately pinned by <code>rtdb-modular-priority-contract</code>. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/cdd/public-operations.test.ts#L106" target="_blank" rel="noopener noreferrer"><code>public-operations.test.ts</code></a>)</div></div>
855
654
  </details>
856
655
  <details class="compat-row" data-status="ok">
857
656
  <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior">After subscribe, adding a child via <code>push</code> or <code>set(child, …)</code> fires <code>onChildAdded</code> exactly once for that key</span></span></summary>
858
- <div class="compat-evidence"><div class="compat-probe">oracle: <code>packages/conformance/observations/rtdb-modular/rtdb-modular-onchildadded-post-subscribe.json</code> — seeded <code>{k1,k2}</code>, observed <code>postSubscribeFires: 1, lastFire: {key:'k3', val:{v:3}}</code> after writing the new child.</div></div>
657
+ <div class="compat-evidence"><div class="compat-probe">oracle: <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/rtdb-modular/rtdb-modular-onchildadded-post-subscribe.json" target="_blank" rel="noopener noreferrer"><code>packages/conformance/observations/rtdb-modular/rtdb-modular-onchildadded-post-subscribe.json</code></a> — seeded <code>{k1,k2}</code>, observed <code>postSubscribeFires: 1, lastFire: {key:'k3', val:{v:3}}</code> after writing the new child. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/cdd/public-operations.test.ts#L106" target="_blank" rel="noopener noreferrer"><code>public-operations.test.ts</code></a>)</div></div>
859
658
  </details>
860
659
  <details class="compat-row" data-status="ok">
861
660
  <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior"><code>onChildChanged</code> fires when an existing child's value changes; does NOT fire for added or removed children</span></span></summary>
862
- <div class="compat-evidence"><div class="compat-probe">oracle: <code>packages/conformance/observations/rtdb-modular/rtdb-modular-onchildchanged-fires-on-update.json</code> — observed <code>firedOnInitial: 0, firedOnUpdate: 1, lastFire: {key:'k1', val:{v:2}}</code> (the NEW value, not the prior).</div></div>
661
+ <div class="compat-evidence"><div class="compat-probe">oracle: <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/rtdb-modular/rtdb-modular-onchildchanged-fires-on-update.json" target="_blank" rel="noopener noreferrer"><code>packages/conformance/observations/rtdb-modular/rtdb-modular-onchildchanged-fires-on-update.json</code></a> — observed <code>firedOnInitial: 0, firedOnUpdate: 1, lastFire: {key:'k1', val:{v:2}}</code> (the NEW value, not the prior). (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/cdd/public-operations.test.ts#L86" target="_blank" rel="noopener noreferrer"><code>public-operations.test.ts</code></a>)</div></div>
863
662
  </details>
864
663
  <details class="compat-row" data-status="ok">
865
664
  <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior"><code>onChildRemoved</code> fires when a child is deleted (via <code>remove(child)</code> or <code>set(child, null)</code>); snapshot carries the PRIOR value</span></span></summary>
866
- <div class="compat-evidence"><div class="compat-probe">oracle: <code>packages/conformance/observations/rtdb-modular/rtdb-modular-onchildremoved-fires-on-delete.json</code> — observed <code>firedOnDelete: 1, removedSnapCarriesPriorValue: true</code> (snapshot.val() was the pre-delete value).</div></div>
665
+ <div class="compat-evidence"><div class="compat-probe">oracle: <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/rtdb-modular/rtdb-modular-onchildremoved-fires-on-delete.json" target="_blank" rel="noopener noreferrer"><code>packages/conformance/observations/rtdb-modular/rtdb-modular-onchildremoved-fires-on-delete.json</code></a> — observed <code>firedOnDelete: 1, removedSnapCarriesPriorValue: true</code> (snapshot.val() was the pre-delete value). (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/cdd/public-operations.test.ts#L107" target="_blank" rel="noopener noreferrer"><code>public-operations.test.ts</code></a>)</div></div>
867
666
  </details>
868
667
  <details class="compat-row" data-status="ok">
869
668
  <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior"><code>onChildMoved</code> under an explicit ordered query fires when the ordered field changes, co-fires with <code>child_changed</code>, and supplies the captured <code>previousChildName</code>.</span></span></summary>
870
- <div class="compat-evidence"><div class="compat-probe">Oracle observations <code>rtdb-modular-onchildmoved-with-orderby</code>, <code>rtdb-modular-onchildmoved-previouschildname-sequencing</code>, and <code>rtdb-modular-childchanged-cofire-with-childmoved</code> are replayed by the modular oracle suite.</div></div>
669
+ <div class="compat-evidence"><div class="compat-probe">Oracle observations <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/rtdb-modular/rtdb-modular-onchildmoved-with-orderby.json" target="_blank" rel="noopener noreferrer"><code>rtdb-modular-onchildmoved-with-orderby</code></a>, <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/rtdb-modular/rtdb-modular-onchildmoved-previouschildname-sequencing.json" target="_blank" rel="noopener noreferrer"><code>rtdb-modular-onchildmoved-previouschildname-sequencing</code></a>, and <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/rtdb-modular/rtdb-modular-childchanged-cofire-with-childmoved.json" target="_blank" rel="noopener noreferrer"><code>rtdb-modular-childchanged-cofire-with-childmoved</code></a> are replayed by the modular oracle suite. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/modular/oracle-conformance-listeners.test.ts#L152" target="_blank" rel="noopener noreferrer"><code>oracle-conformance-listeners.test.ts</code></a>)</div></div>
871
670
  </details>
872
671
  </div>
873
672
 
@@ -876,23 +675,23 @@ tooling coverage and are not exports of `pyric/database`.
876
675
  <div class="compat-list">
877
676
  <details class="compat-row" data-status="ok">
878
677
  <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior"><code>off(ref)</code> removes ALL listeners at that ref (any event type, any callback)</span></span></summary>
879
- <div class="compat-evidence"><div class="compat-probe">oracle: <code>packages/conformance/observations/rtdb-modular/rtdb-modular-off-stops-child-fires.json</code> — after <code>off(ref)</code> with no eventType, a subsequent write produced <code>postOffFires: 0</code> against an <code>onChildAdded</code> registration.</div></div>
678
+ <div class="compat-evidence"><div class="compat-probe">oracle: <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/rtdb-modular/rtdb-modular-off-stops-child-fires.json" target="_blank" rel="noopener noreferrer"><code>packages/conformance/observations/rtdb-modular/rtdb-modular-off-stops-child-fires.json</code></a> — after <code>off(ref)</code> with no eventType, a subsequent write produced <code>postOffFires: 0</code> against an <code>onChildAdded</code> registration. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/cdd/public-operations.test.ts#L108" target="_blank" rel="noopener noreferrer"><code>public-operations.test.ts</code></a>)</div></div>
880
679
  </details>
881
680
  <details class="compat-row" data-status="ok">
882
681
  <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior"><code>off(ref, 'value')</code> removes only <code>value</code> listeners at that ref</span></span></summary>
883
- <div class="compat-evidence"><div class="compat-probe">Sandbox aligned (M48); oracle: <code>packages/conformance/observations/rtdb/rtdb-off-eventtype-precision.json</code> — registered TWO <code>value</code> listeners + one <code>child_added</code> at the same ref; after <code>off(ref, 'value')</code> (no callback), <code>valueListenersStopped: true</code> (neither value cb fired on subsequent writes) AND <code>childListenerStillFiringAfterOffValue: true</code> (the child listener kept firing). <code>offValueClearsAllValueListeners: true</code> confirms the no-callback variant removes ALL value listeners at the ref.</div></div>
682
+ <div class="compat-evidence"><div class="compat-probe">Sandbox aligned (M48); oracle: <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/rtdb/rtdb-off-eventtype-precision.json" target="_blank" rel="noopener noreferrer"><code>packages/conformance/observations/rtdb/rtdb-off-eventtype-precision.json</code></a> — registered TWO <code>value</code> listeners + one <code>child_added</code> at the same ref; after <code>off(ref, 'value')</code> (no callback), <code>valueListenersStopped: true</code> (neither value cb fired on subsequent writes) AND <code>childListenerStillFiringAfterOffValue: true</code> (the child listener kept firing). <code>offValueClearsAllValueListeners: true</code> confirms the no-callback variant removes ALL value listeners at the ref. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/rtdb-modular/rtdb-modular-off-stops-child-fires.json" target="_blank" rel="noopener noreferrer"><code>rtdb-modular-off-stops-child-fires</code></a>, <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/cdd/public-operations.test.ts#L108" target="_blank" rel="noopener noreferrer"><code>public-operations.test.ts</code></a>)</div></div>
884
683
  </details>
885
684
  <details class="compat-row" data-status="ok">
886
685
  <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior"><code>off(ref, 'value', cb)</code> removes only the specific callback</span></span></summary>
887
- <div class="compat-evidence"><div class="compat-probe">Sandbox aligned (M48); adjacent to #141 — the upstream <code>off</code> with the cb argument removes only the matching callback. Same probe (<code>rtdb-onvalue-unsub-equivalence.json</code> Case 2) confirms <code>off(ref, 'value', cb)</code> stops only that callback.</div></div>
686
+ <div class="compat-evidence"><div class="compat-probe">Sandbox aligned (M48); adjacent to #141 — the upstream <code>off</code> with the cb argument removes only the matching callback. Same probe (<code>rtdb-onvalue-unsub-equivalence.json</code> Case 2) confirms <code>off(ref, 'value', cb)</code> stops only that callback. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/oracle-conformance.test.ts#L52" target="_blank" rel="noopener noreferrer"><code>oracle-conformance.test.ts</code></a>)</div></div>
888
687
  </details>
889
688
  <details class="compat-row" data-status="ok">
890
689
  <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior">The returned unsubscribe function from <code>onValue(ref, cb)</code> is equivalent to <code>off(ref, 'value', cb)</code></span></span></summary>
891
- <div class="compat-evidence"><div class="compat-probe">Sandbox aligned (M48); oracle: <code>packages/conformance/observations/rtdb/rtdb-onvalue-unsub-equivalence.json</code> — <code>unsubReturnType: 'function'</code>, <code>unsubReturnedFnStopsListener: true</code> (the captured return value halted fires on write), <code>offRefValueCbStopsListener: true</code> (the same effect via <code>off(ref, 'value', cb)</code>), <code>bothFormsEquivalent: true</code>.</div></div>
690
+ <div class="compat-evidence"><div class="compat-probe">Sandbox aligned (M48); oracle: <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/rtdb/rtdb-onvalue-unsub-equivalence.json" target="_blank" rel="noopener noreferrer"><code>packages/conformance/observations/rtdb/rtdb-onvalue-unsub-equivalence.json</code></a> — <code>unsubReturnType: 'function'</code>, <code>unsubReturnedFnStopsListener: true</code> (the captured return value halted fires on write), <code>offRefValueCbStopsListener: true</code> (the same effect via <code>off(ref, 'value', cb)</code>), <code>bothFormsEquivalent: true</code>. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/cdd/public-operations.test.ts#L108" target="_blank" rel="noopener noreferrer"><code>public-operations.test.ts</code></a>)</div></div>
892
691
  </details>
893
692
  <details class="compat-row" data-status="ok">
894
693
  <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">off(ref, eventType, callback)</code><span class="compat-sub"><span class="compat-behavior">When the same callback is registered more than once, each <code>off(ref, eventType, callback)</code> removes one registration without orphaning the others</span></span></span></summary>
895
- <div class="compat-evidence"><div class="compat-probe">Oracle <code>rtdb-modular-off-duplicate-registration</code> proves each matching <code>off</code> removes one registration; sandbox row assertion <code>rtdb-modular#183</code> replays the full sequence.</div></div>
694
+ <div class="compat-evidence"><div class="compat-probe">Oracle <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/rtdb-modular/rtdb-modular-off-duplicate-registration.json" target="_blank" rel="noopener noreferrer"><code>rtdb-modular-off-duplicate-registration</code></a> proves each matching <code>off</code> removes one registration; sandbox row assertion <code>rtdb-modular#183</code> replays the full sequence. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/modular/oracle-conformance-reference-writes.test.ts#L175" target="_blank" rel="noopener noreferrer"><code>oracle-conformance-reference-writes.test.ts</code></a>)</div></div>
896
695
  </details>
897
696
  </div>
898
697
 
@@ -901,47 +700,47 @@ tooling coverage and are not exports of `pyric/database`.
901
700
  <div class="compat-list">
902
701
  <details class="compat-row" data-status="ok">
903
702
  <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior"><code>query(ref, orderByChild('field'), limitToFirst(N))</code> returns a <code>Query</code> whose <code>get()</code> resolves a snapshot containing N children ordered by <code>field</code></span></span></summary>
904
- <div class="compat-evidence"><div class="compat-probe">oracle: <code>packages/conformance/observations/rtdb-modular/rtdb-modular-query-orderbychild-limit.json</code> — seeded 4 children with positions <code>[3,1,4,2]</code>, observed <code>orderedKeys: [{key:'a',pos:1}, {key:'b',pos:2}]</code> (first 2 in ascending order). Requires <code>.indexOn</code> declared in rules.</div></div>
703
+ <div class="compat-evidence"><div class="compat-probe">oracle: <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/rtdb-modular/rtdb-modular-query-orderbychild-limit.json" target="_blank" rel="noopener noreferrer"><code>packages/conformance/observations/rtdb-modular/rtdb-modular-query-orderbychild-limit.json</code></a> — seeded 4 children with positions <code>[3,1,4,2]</code>, observed <code>orderedKeys: [{key:'a',pos:1}, {key:'b',pos:2}]</code> (first 2 in ascending order). Requires <code>.indexOn</code> declared in rules. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/rtdb-modular/rtdb-modular-orderbychild-window.json" target="_blank" rel="noopener noreferrer"><code>rtdb-modular-orderbychild-window</code></a>, <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/cdd/public-operations.test.ts#L19" target="_blank" rel="noopener noreferrer"><code>public-operations.test.ts</code></a>)</div></div>
905
704
  </details>
906
705
  <details class="compat-row" data-status="ok">
907
706
  <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior"><code>orderByKey()</code> orders by the auto-id / numeric key</span></span></summary>
908
- <div class="compat-evidence"><div class="compat-probe">oracle: <code>packages/conformance/observations/rtdb-modular/rtdb-modular-orderbykey-window.json</code> — seeded <code>{a,b,c,d,e}</code> in shuffled insertion order, observed <code>matchedKeys: ['b','c','d']</code> for <code>orderByKey() + startAt('b') + endAt('d')</code> (in key order).</div></div>
707
+ <div class="compat-evidence"><div class="compat-probe">oracle: <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/rtdb-modular/rtdb-modular-orderbykey-window.json" target="_blank" rel="noopener noreferrer"><code>packages/conformance/observations/rtdb-modular/rtdb-modular-orderbykey-window.json</code></a> — seeded <code>{a,b,c,d,e}</code> in shuffled insertion order, observed <code>matchedKeys: ['b','c','d']</code> for <code>orderByKey() + startAt('b') + endAt('d')</code> (in key order). (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/cdd/public-operations.test.ts#L122" target="_blank" rel="noopener noreferrer"><code>public-operations.test.ts</code></a>)</div></div>
909
708
  </details>
910
709
  <details class="compat-row" data-status="diverged">
911
710
  <summary class="compat-line"><span class="compat-dot" data-status="diverged" role="img" aria-label="Diverged (documented)" title="Diverged (documented)"></span><span class="compat-main"><span class="compat-behavior">Production enforces <code>.indexOn: '.value'</code> for <code>orderByValue()</code> while the sandbox executes the value ordering without index enforcement</span></span></summary>
912
- <div class="compat-evidence"><div class="compat-probe">Oracle <code>rtdb-modular-orderbyvalue-numeric</code> rejects the unindexed production query; <code>unit:modular/oracle-conformance-queries.test.ts</code> pins the sandbox's successful ordered result instead of treating that contradiction as conformance.</div></div>
711
+ <div class="compat-evidence"><div class="compat-probe">Oracle <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/rtdb-modular/rtdb-modular-orderbyvalue-numeric.json" target="_blank" rel="noopener noreferrer"><code>rtdb-modular-orderbyvalue-numeric</code></a> rejects the unindexed production query; <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/modular/oracle-conformance-queries.test.ts#L51" target="_blank" rel="noopener noreferrer"><code>unit:modular/oracle-conformance-queries.test.ts</code></a> pins the sandbox's successful ordered result instead of treating that contradiction as conformance.</div></div>
913
712
  </details>
914
713
  <details class="compat-row" data-status="ok">
915
714
  <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior"><code>equalTo(v)</code> filters children whose ordered field === v (returns 0, 1, or multiple matches — RTDB does NOT enforce uniqueness)</span></span></summary>
916
- <div class="compat-evidence"><div class="compat-probe">oracle: <code>packages/conformance/observations/rtdb-modular/rtdb-modular-query-equalto.json</code> — seeded <code>{red, blue, blue, green}</code>, observed <code>matchedKeys: ['k2', 'k3']</code> for <code>equalTo('blue')</code> (both blue children, none of the others). Additional probe: <code>packages/conformance/observations/rtdb-modular/rtdb-modular-equalTo-filter.json</code> (a..b..c groups) confirms <code>equalTo('b')</code> returns the two <code>b</code> children.</div></div>
715
+ <div class="compat-evidence"><div class="compat-probe">oracle: <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/rtdb-modular/rtdb-modular-query-equalto.json" target="_blank" rel="noopener noreferrer"><code>packages/conformance/observations/rtdb-modular/rtdb-modular-query-equalto.json</code></a> — seeded <code>{red, blue, blue, green}</code>, observed <code>matchedKeys: ['k2', 'k3']</code> for <code>equalTo('blue')</code> (both blue children, none of the others). Additional probe: <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/rtdb-modular/rtdb-modular-equalTo-filter.json" target="_blank" rel="noopener noreferrer"><code>packages/conformance/observations/rtdb-modular/rtdb-modular-equalTo-filter.json</code></a> (a..b..c groups) confirms <code>equalTo('b')</code> returns the two <code>b</code> children. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/cdd/public-operations.test.ts#L124" target="_blank" rel="noopener noreferrer"><code>public-operations.test.ts</code></a>)</div></div>
917
716
  </details>
918
717
  <details class="compat-row" data-status="ok">
919
718
  <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior"><code>startAt(v)</code> is <strong>inclusive</strong> (the child whose ordered value === v is included)</span></span></summary>
920
- <div class="compat-evidence"><div class="compat-probe">oracle: <code>packages/conformance/observations/rtdb-modular/rtdb-modular-query-startat-inclusive.json</code> — seeded positions <code>[1,2,3,4]</code>, observed <code>matched: [2,3,4]</code> for <code>startAt(2)</code> (cursor doc included).</div></div>
719
+ <div class="compat-evidence"><div class="compat-probe">oracle: <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/rtdb-modular/rtdb-modular-query-startat-inclusive.json" target="_blank" rel="noopener noreferrer"><code>packages/conformance/observations/rtdb-modular/rtdb-modular-query-startat-inclusive.json</code></a> — seeded positions <code>[1,2,3,4]</code>, observed <code>matched: [2,3,4]</code> for <code>startAt(2)</code> (cursor doc included). (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/rtdb-modular/rtdb-modular-orderbychild-window.json" target="_blank" rel="noopener noreferrer"><code>rtdb-modular-orderbychild-window</code></a>, <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/rtdb-modular/rtdb-modular-orderbykey-window.json" target="_blank" rel="noopener noreferrer"><code>rtdb-modular-orderbykey-window</code></a>, <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/cdd/public-operations.test.ts#L125" target="_blank" rel="noopener noreferrer"><code>public-operations.test.ts</code></a>)</div></div>
921
720
  </details>
922
721
  <details class="compat-row" data-status="ok">
923
722
  <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior"><code>endAt(v)</code> is <strong>inclusive</strong></span></span></summary>
924
- <div class="compat-evidence"><div class="compat-probe">oracle: <code>packages/conformance/observations/rtdb-modular/rtdb-modular-orderbychild-window.json</code> — <code>startAt(2) + endAt(4)</code> matched positions <code>[2,3,4]</code> (endAt(4) included its boundary value).</div></div>
723
+ <div class="compat-evidence"><div class="compat-probe">oracle: <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/rtdb-modular/rtdb-modular-orderbychild-window.json" target="_blank" rel="noopener noreferrer"><code>packages/conformance/observations/rtdb-modular/rtdb-modular-orderbychild-window.json</code></a> — <code>startAt(2) + endAt(4)</code> matched positions <code>[2,3,4]</code> (endAt(4) included its boundary value). (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/rtdb-modular/rtdb-modular-orderbykey-window.json" target="_blank" rel="noopener noreferrer"><code>rtdb-modular-orderbykey-window</code></a>, <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/cdd/public-operations.test.ts#L126" target="_blank" rel="noopener noreferrer"><code>public-operations.test.ts</code></a>)</div></div>
925
724
  </details>
926
725
  <details class="compat-row" data-status="ok">
927
726
  <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior"><code>startAfter(v)</code> is <strong>exclusive</strong></span></span></summary>
928
- <div class="compat-evidence"><div class="compat-probe">oracle: <code>packages/conformance/observations/rtdb-modular/rtdb-modular-startafter-endbefore-exclusive.json</code> — <code>startAfter(2) + endBefore(5)</code> matched positions <code>[3,4]</code> (cursor <code>2</code> dropped).</div></div>
727
+ <div class="compat-evidence"><div class="compat-probe">oracle: <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/rtdb-modular/rtdb-modular-startafter-endbefore-exclusive.json" target="_blank" rel="noopener noreferrer"><code>packages/conformance/observations/rtdb-modular/rtdb-modular-startafter-endbefore-exclusive.json</code></a> — <code>startAfter(2) + endBefore(5)</code> matched positions <code>[3,4]</code> (cursor <code>2</code> dropped). (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/cdd/public-operations.test.ts#L127" target="_blank" rel="noopener noreferrer"><code>public-operations.test.ts</code></a>)</div></div>
929
728
  </details>
930
729
  <details class="compat-row" data-status="ok">
931
730
  <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior"><code>endBefore(v)</code> is <strong>exclusive</strong></span></span></summary>
932
- <div class="compat-evidence"><div class="compat-probe">oracle: <code>packages/conformance/observations/rtdb-modular/rtdb-modular-startafter-endbefore-exclusive.json</code> — same probe; cursor <code>5</code> dropped.</div></div>
731
+ <div class="compat-evidence"><div class="compat-probe">oracle: <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/rtdb-modular/rtdb-modular-startafter-endbefore-exclusive.json" target="_blank" rel="noopener noreferrer"><code>packages/conformance/observations/rtdb-modular/rtdb-modular-startafter-endbefore-exclusive.json</code></a> — same probe; cursor <code>5</code> dropped. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/cdd/public-operations.test.ts#L128" target="_blank" rel="noopener noreferrer"><code>public-operations.test.ts</code></a>)</div></div>
933
732
  </details>
934
733
  <details class="compat-row" data-status="ok">
935
734
  <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior"><code>limitToFirst(N)</code> caps the result count from the start of the ordered range</span></span></summary>
936
- <div class="compat-evidence"><div class="compat-probe">oracle: <code>packages/conformance/observations/rtdb-modular/rtdb-modular-query-orderbychild-limit.json</code> plus <code>packages/conformance/observations/rtdb-modular/rtdb-modular-limittofirst-vs-limittolast.json</code> (firstPositions <code>[1,2]</code>).</div></div>
735
+ <div class="compat-evidence"><div class="compat-probe">oracle: <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/rtdb-modular/rtdb-modular-query-orderbychild-limit.json" target="_blank" rel="noopener noreferrer"><code>packages/conformance/observations/rtdb-modular/rtdb-modular-query-orderbychild-limit.json</code></a> plus <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/rtdb-modular/rtdb-modular-limittofirst-vs-limittolast.json" target="_blank" rel="noopener noreferrer"><code>packages/conformance/observations/rtdb-modular/rtdb-modular-limittofirst-vs-limittolast.json</code></a> (firstPositions <code>[1,2]</code>). (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/rtdb-modular/rtdb-modular-orderbyvalue-numeric.json" target="_blank" rel="noopener noreferrer"><code>rtdb-modular-orderbyvalue-numeric</code></a>, <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/cdd/public-operations.test.ts#L26" target="_blank" rel="noopener noreferrer"><code>public-operations.test.ts</code></a>)</div></div>
937
736
  </details>
938
737
  <details class="compat-row" data-status="ok">
939
738
  <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior"><code>limitToLast(N)</code> caps from the end</span></span></summary>
940
- <div class="compat-evidence"><div class="compat-probe">oracle: <code>packages/conformance/observations/rtdb-modular/rtdb-modular-limittofirst-vs-limittolast.json</code> — observed <code>lastKeys: ['d','e'], lastPositions: [4,5]</code> for <code>limitToLast(2)</code> on a 5-child collection ordered by <code>pos</code>.</div></div>
739
+ <div class="compat-evidence"><div class="compat-probe">oracle: <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/rtdb-modular/rtdb-modular-limittofirst-vs-limittolast.json" target="_blank" rel="noopener noreferrer"><code>packages/conformance/observations/rtdb-modular/rtdb-modular-limittofirst-vs-limittolast.json</code></a> — observed <code>lastKeys: ['d','e'], lastPositions: [4,5]</code> for <code>limitToLast(2)</code> on a 5-child collection ordered by <code>pos</code>. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/cdd/public-operations.test.ts#L130" target="_blank" rel="noopener noreferrer"><code>public-operations.test.ts</code></a>)</div></div>
941
740
  </details>
942
741
  <details class="compat-row" data-status="ok">
943
742
  <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior">Listeners on a <code>Query</code> (<code>onValue(q, …)</code>) emit only the windowed snapshot — NOT the parent ref's full data</span></span></summary>
944
- <div class="compat-evidence"><div class="compat-probe">oracle: <code>packages/conformance/observations/rtdb-modular/rtdb-modular-onvalue-with-query.json</code> — seeded 3 children, watched first 2 by <code>pos</code>; observed 3 fires total: (1) initial <code>[a,b]</code>, (2) OUTSIDE-window write to <code>c/extra</code> did NOT fire, (3) INSIDE-window mutation of <code>a</code> re-fired, (4) new child <code>z</code> displaced <code>b</code> and re-fired. Outside-window writes are silent.</div></div>
743
+ <div class="compat-evidence"><div class="compat-probe">oracle: <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/rtdb-modular/rtdb-modular-onvalue-with-query.json" target="_blank" rel="noopener noreferrer"><code>packages/conformance/observations/rtdb-modular/rtdb-modular-onvalue-with-query.json</code></a> — seeded 3 children, watched first 2 by <code>pos</code>; observed 3 fires total: (1) initial <code>[a,b]</code>, (2) OUTSIDE-window write to <code>c/extra</code> did NOT fire, (3) INSIDE-window mutation of <code>a</code> re-fired, (4) new child <code>z</code> displaced <code>b</code> and re-fired. Outside-window writes are silent. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/cdd/public-operations.test.ts#L19" target="_blank" rel="noopener noreferrer"><code>public-operations.test.ts</code></a>)</div></div>
945
744
  </details>
946
745
  </div>
947
746
 
@@ -950,23 +749,23 @@ tooling coverage and are not exports of `pyric/database`.
950
749
  <div class="compat-list">
951
750
  <details class="compat-row" data-status="ok">
952
751
  <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior"><code>serverTimestamp()</code> resolves server-side to a <strong>number</strong> (epoch milliseconds) — diverges from Firestore's <code>Timestamp</code> instance</span></span></summary>
953
- <div class="compat-evidence"><div class="compat-probe">oracle: <code>packages/conformance/observations/rtdb/rtdb-servertimestamp-resolves.json</code> — observed <code>createdAtType: 'number', createdAt: 1779075391118</code> (i.e. a plain JS number, NOT a <code>Timestamp</code> object).</div></div>
752
+ <div class="compat-evidence"><div class="compat-probe">oracle: <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/rtdb/rtdb-servertimestamp-resolves.json" target="_blank" rel="noopener noreferrer"><code>packages/conformance/observations/rtdb/rtdb-servertimestamp-resolves.json</code></a> — observed <code>createdAtType: 'number', createdAt: 1779075391118</code> (i.e. a plain JS number, NOT a <code>Timestamp</code> object). (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/cdd/public-operations.test.ts#L1" target="_blank" rel="noopener noreferrer"><code>public-operations.test.ts</code></a>)</div></div>
954
753
  </details>
955
754
  <details class="compat-row" data-status="ok">
956
755
  <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior"><code>serverTimestamp()</code> as a field value in <code>set</code> or <code>update</code> writes the <code>{".sv": "timestamp"}</code> sentinel; the read-back value is the resolved number</span></span></summary>
957
- <div class="compat-evidence"><div class="compat-probe">oracle: <code>packages/conformance/observations/rtdb/rtdb-servertimestamp-resolves.json</code> — read-back showed <code>createdAtSentinelShape: false</code> (sentinel resolved server-side; client sees the number, not the <code>.sv</code> placeholder).</div></div>
756
+ <div class="compat-evidence"><div class="compat-probe">oracle: <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/rtdb/rtdb-servertimestamp-resolves.json" target="_blank" rel="noopener noreferrer"><code>packages/conformance/observations/rtdb/rtdb-servertimestamp-resolves.json</code></a> — read-back showed <code>createdAtSentinelShape: false</code> (sentinel resolved server-side; client sees the number, not the <code>.sv</code> placeholder). (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/cdd/public-operations.test.ts#L1" target="_blank" rel="noopener noreferrer"><code>public-operations.test.ts</code></a>)</div></div>
958
757
  </details>
959
758
  <details class="compat-row" data-status="ok">
960
759
  <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior"><code>increment(n)</code> against a <strong>missing</strong> field starts at 0 (so <code>increment(5)</code> lands as <code>5</code>)</span></span></summary>
961
- <div class="compat-evidence"><div class="compat-probe">Sandbox aligned (modular <code>increment</code> export now present): <code>unit:modular/increment.test.ts</code> ("increment against a missing field starts from 0"); matches oracle <code>packages/conformance/observations/rtdb-modular/rtdb-modular-increment-from-missing.json</code> — observed <code>afterFirst: 5</code> from <code>increment(5)</code> against an absent <code>count</code> field.</div></div>
760
+ <div class="compat-evidence"><div class="compat-probe">Sandbox aligned (modular <code>increment</code> export now present): <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/modular/increment.test.ts#L7" target="_blank" rel="noopener noreferrer"><code>unit:modular/increment.test.ts</code></a> ("increment against a missing field starts from 0"); matches oracle <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/rtdb-modular/rtdb-modular-increment-from-missing.json" target="_blank" rel="noopener noreferrer"><code>packages/conformance/observations/rtdb-modular/rtdb-modular-increment-from-missing.json</code></a> — observed <code>afterFirst: 5</code> from <code>increment(5)</code> against an absent <code>count</code> field.</div></div>
962
761
  </details>
963
762
  <details class="compat-row" data-status="ok">
964
763
  <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior"><code>increment(n)</code> against an existing numeric field adds atomically; negative deltas subtract</span></span></summary>
965
- <div class="compat-evidence"><div class="compat-probe">Sandbox aligned: <code>unit:modular/increment.test.ts</code> ("subsequent increments accumulate (positive then negative)" + "nested inside an update patch resolves per-field"); matches oracle <code>packages/conformance/observations/rtdb-modular/rtdb-modular-increment-from-missing.json</code> — observed <code>afterSecond: 8</code> (5+3) then <code>afterNegative: 6</code> (8-2).</div></div>
764
+ <div class="compat-evidence"><div class="compat-probe">Sandbox aligned: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/modular/increment.test.ts#L7" target="_blank" rel="noopener noreferrer"><code>unit:modular/increment.test.ts</code></a> ("subsequent increments accumulate (positive then negative)" + "nested inside an update patch resolves per-field"); matches oracle <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/rtdb-modular/rtdb-modular-increment-from-missing.json" target="_blank" rel="noopener noreferrer"><code>packages/conformance/observations/rtdb-modular/rtdb-modular-increment-from-missing.json</code></a> — observed <code>afterSecond: 8</code> (5+3) then <code>afterNegative: 6</code> (8-2).</div></div>
966
765
  </details>
967
766
  <details class="compat-row" data-status="diverged">
968
767
  <summary class="compat-line"><span class="compat-dot" data-status="diverged" role="img" aria-label="Diverged (documented)" title="Diverged (documented)"></span><span class="compat-main"><span class="compat-behavior"><strong>Divergence:</strong> production interleaves concurrent <code>increment</code> writes from independent clients and preserves both deltas; the synchronous in-process backend applies each call before returning its promise, so a <code>Promise.all</code> call site is observably serialized even though its terminal value also reaches 5</span></span></summary>
969
- <div class="compat-evidence"><div class="compat-probe">Oracle <code>rtdb-modular-concurrent-transforms</code> uses two independent clients and captures terminal 5 after concurrent +2/+3 increments; <code>unit:modular/transaction-contention-cdd.test.ts</code> pins the sandbox's synchronous first-write visibility and matching terminal value without claiming that terminal equality proves contention semantics.</div></div>
768
+ <div class="compat-evidence"><div class="compat-probe">Oracle <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/rtdb-modular/rtdb-modular-concurrent-transforms.json" target="_blank" rel="noopener noreferrer"><code>rtdb-modular-concurrent-transforms</code></a> uses two independent clients and captures terminal 5 after concurrent +2/+3 increments; <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/modular/transaction-contention-cdd.test.ts#L12" target="_blank" rel="noopener noreferrer"><code>unit:modular/transaction-contention-cdd.test.ts</code></a> pins the sandbox's synchronous first-write visibility and matching terminal value without claiming that terminal equality proves contention semantics.</div></div>
970
769
  </details>
971
770
  </div>
972
771
 
@@ -975,23 +774,23 @@ tooling coverage and are not exports of `pyric/database`.
975
774
  <div class="compat-list">
976
775
  <details class="compat-row" data-status="ok">
977
776
  <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior">Basic success — <code>runTransaction(ref, current =&gt; (current ?? 0) + 1)</code> resolves <code>{ committed: true, snapshot }</code> where <code>snapshot.val()</code> is the new value</span></span></summary>
978
- <div class="compat-evidence"><div class="compat-probe">oracle: <code>packages/conformance/observations/rtdb-modular/rtdb-modular-runtransaction-success.json</code> — observed <code>committed: true, snapVal: 1</code> after running <code>current =&gt; (current ?? 0) + 1</code> against an empty ref.</div></div>
777
+ <div class="compat-evidence"><div class="compat-probe">oracle: <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/rtdb-modular/rtdb-modular-runtransaction-success.json" target="_blank" rel="noopener noreferrer"><code>packages/conformance/observations/rtdb-modular/rtdb-modular-runtransaction-success.json</code></a> — observed <code>committed: true, snapVal: 1</code> after running <code>current =&gt; (current ?? 0) + 1</code> against an empty ref. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/rtdb-modular/rtdb-modular-runtransaction-on-rules-denied-path.json" target="_blank" rel="noopener noreferrer"><code>rtdb-modular-runtransaction-on-rules-denied-path</code></a>, <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/rtdb-modular/rtdb-modular-runtransaction-options-applylocally.json" target="_blank" rel="noopener noreferrer"><code>rtdb-modular-runtransaction-options-applylocally</code></a>, <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/cdd/public-operations.test.ts#L140" target="_blank" rel="noopener noreferrer"><code>public-operations.test.ts</code></a>)</div></div>
979
778
  </details>
980
779
  <details class="compat-row" data-status="ok">
981
780
  <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior">Returning <code>undefined</code> from the update fn <strong>aborts</strong> the transaction — resolves <code>{ committed: false }</code>, no write performed (RTDB-specific; distinct from Firestore where the only abort path is throwing)</span></span></summary>
982
- <div class="compat-evidence"><div class="compat-probe">oracle: <code>packages/conformance/observations/rtdb-modular/rtdb-modular-runtransaction-abort-undefined.json</code> — seeded <code>100</code> then transaction returned <code>undefined</code>; observed <code>committed: false, snapVal: null, afterValOnServer: 100</code> (existing value preserved).</div></div>
781
+ <div class="compat-evidence"><div class="compat-probe">oracle: <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/rtdb-modular/rtdb-modular-runtransaction-abort-undefined.json" target="_blank" rel="noopener noreferrer"><code>packages/conformance/observations/rtdb-modular/rtdb-modular-runtransaction-abort-undefined.json</code></a> — seeded <code>100</code> then transaction returned <code>undefined</code>; observed <code>committed: false, snapVal: null, afterValOnServer: 100</code> (existing value preserved). (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/cdd/public-operations.test.ts#L29" target="_blank" rel="noopener noreferrer"><code>public-operations.test.ts</code></a>)</div></div>
983
782
  </details>
984
783
  <details class="compat-row" data-status="diverged">
985
784
  <summary class="compat-line"><span class="compat-dot" data-status="diverged" role="img" aria-label="Diverged (documented)" title="Diverged (documented)"></span><span class="compat-main"><span class="compat-behavior">A cold production client can call the update fn with speculative <code>null</code> before the current seeded value; the always-warm sandbox calls it once with the current value</span></span></summary>
986
- <div class="compat-evidence"><div class="compat-probe">Oracle <code>rtdb-modular-runtransaction-current-value-arg</code> captures the cold two-call sequence and <code>rtdb-modular-runtransaction-warm-client-speculation</code> captures the matching warm one-call case; <code>unit:modular/oracle-conformance-transactions.test.ts</code> pins both sides.</div></div>
785
+ <div class="compat-evidence"><div class="compat-probe">Oracle <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/rtdb-modular/rtdb-modular-runtransaction-current-value-arg.json" target="_blank" rel="noopener noreferrer"><code>rtdb-modular-runtransaction-current-value-arg</code></a> captures the cold two-call sequence and <code>rtdb-modular-runtransaction-warm-client-speculation</code> captures the matching warm one-call case; <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/modular/oracle-conformance-transactions.test.ts#L20" target="_blank" rel="noopener noreferrer"><code>unit:modular/oracle-conformance-transactions.test.ts</code></a> pins both sides. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/rtdb-modular/rtdb-modular-runtransaction-options-applylocally.json" target="_blank" rel="noopener noreferrer"><code>rtdb-modular-runtransaction-options-applylocally</code></a>, <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/rtdb-modular/rtdb-modular-runtransaction-success.json" target="_blank" rel="noopener noreferrer"><code>rtdb-modular-runtransaction-success</code></a>)</div></div>
987
786
  </details>
988
787
  <details class="compat-row" data-status="diverged">
989
788
  <summary class="compat-line"><span class="compat-dot" data-status="diverged" role="img" aria-label="Diverged (documented)" title="Diverged (documented)"></span><span class="compat-main"><span class="compat-behavior">Concurrent contention — if another client writes between the read and write, the update fn is retried with the new current value (typically up to 25 retries by default)</span></span></summary>
990
- <div class="compat-evidence"><div class="compat-probe">Oracle <code>rtdb-modular-concurrent-transforms</code> captures callback counts [2, 3] across two clients; the synchronous in-process backend serializes the same ordinary calls with [1, 1], pinned by <code>unit:modular/transaction-contention-cdd.test.ts</code>.</div></div>
789
+ <div class="compat-evidence"><div class="compat-probe">Oracle <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/rtdb-modular/rtdb-modular-concurrent-transforms.json" target="_blank" rel="noopener noreferrer"><code>rtdb-modular-concurrent-transforms</code></a> captures callback counts [2, 3] across two clients; the synchronous in-process backend serializes the same ordinary calls with [1, 1], pinned by <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/modular/transaction-contention-cdd.test.ts#L12" target="_blank" rel="noopener noreferrer"><code>unit:modular/transaction-contention-cdd.test.ts</code></a>.</div></div>
991
790
  </details>
992
791
  <details class="compat-row" data-status="ok">
993
792
  <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior">Result snapshot's <code>.val()</code> reflects the committed value (or the existing value if aborted)</span></span></summary>
994
- <div class="compat-evidence"><div class="compat-probe">oracle: <code>packages/conformance/observations/rtdb-modular/rtdb-modular-runtransaction-success.json</code> — observed <code>snapVal: 1</code> matching the committed value.</div></div>
793
+ <div class="compat-evidence"><div class="compat-probe">oracle: <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/rtdb-modular/rtdb-modular-runtransaction-success.json" target="_blank" rel="noopener noreferrer"><code>packages/conformance/observations/rtdb-modular/rtdb-modular-runtransaction-success.json</code></a> — observed <code>snapVal: 1</code> matching the committed value. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/rtdb-modular/rtdb-modular-runtransaction-returns-committed-snapshot.json" target="_blank" rel="noopener noreferrer"><code>rtdb-modular-runtransaction-returns-committed-snapshot</code></a>, <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/cdd/public-operations.test.ts#L30" target="_blank" rel="noopener noreferrer"><code>public-operations.test.ts</code></a>)</div></div>
995
794
  </details>
996
795
  </div>
997
796
 
@@ -1000,50 +799,37 @@ tooling coverage and are not exports of `pyric/database`.
1000
799
  <div class="compat-list">
1001
800
  <details class="compat-row" data-status="diverged">
1002
801
  <summary class="compat-line"><span class="compat-dot" data-status="diverged" role="img" aria-label="Diverged (documented)" title="Diverged (documented)"></span><span class="compat-main"><span class="compat-behavior"><code>goOffline(db)</code> drains that client's onDisconnect queue once, but does not make the in-memory data plane unreachable; ordinary writes, listeners, and <code>get()</code> remain available</span></span></summary>
1003
- <div class="compat-evidence"><div class="compat-probe"><code>unit:database/on-disconnect.test.ts</code> + <code>unit:modular/fruit-aliases.test.ts</code></div>
802
+ <div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/on-disconnect.test.ts#L10" target="_blank" rel="noopener noreferrer"><code>unit:database/on-disconnect.test.ts</code></a> + <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/modular/fruit-aliases.test.ts#L17" target="_blank" rel="noopener noreferrer"><code>unit:modular/fruit-aliases.test.ts</code></a></div>
1004
803
  <div class="compat-note">logical disconnect lifecycle is modeled, but the in-memory data plane itself remains available</div></div>
1005
804
  </details>
1006
805
  <details class="compat-row" data-status="diverged">
1007
806
  <summary class="compat-line"><span class="compat-dot" data-status="diverged" role="img" aria-label="Diverged (documented)" title="Diverged (documented)"></span><span class="compat-main"><span class="compat-behavior"><code>goOnline(db)</code> reconnects the logical lifecycle without resurrecting drained operations; it is otherwise a no-op because the in-memory data plane never became unreachable</span></span></summary>
1008
- <div class="compat-evidence"><div class="compat-probe"><code>unit:database/on-disconnect.test.ts</code> + <code>unit:modular/fruit-aliases.test.ts</code></div>
807
+ <div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/on-disconnect.test.ts#L11" target="_blank" rel="noopener noreferrer"><code>unit:database/on-disconnect.test.ts</code></a> + <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/modular/fruit-aliases.test.ts#L18" target="_blank" rel="noopener noreferrer"><code>unit:modular/fruit-aliases.test.ts</code></a></div>
1009
808
  <div class="compat-note">logical disconnect lifecycle is modeled, but the in-memory data plane itself remains available</div></div>
1010
809
  </details>
1011
810
  </div>
1012
811
 
1013
- ### `connectDatabaseEmulator` — emulator hook
1014
-
1015
- <div class="compat-list">
1016
- <details class="compat-row" data-status="unsupported">
1017
- <summary class="compat-line"><span class="compat-dot" data-status="unsupported" role="img" aria-label="Not implemented yet" title="Not implemented yet"></span><span class="compat-main"><span class="compat-behavior">No-op on sandbox-target handles (the sandbox IS the local emulator)</span></span></summary>
1018
- <div class="compat-evidence"><div class="compat-probe">Phase 3</div></div>
1019
- </details>
1020
- <details class="compat-row" data-status="unsupported">
1021
- <summary class="compat-line"><span class="compat-dot" data-status="unsupported" role="img" aria-label="Not implemented yet" title="Not implemented yet"></span><span class="compat-main"><span class="compat-behavior">A production target is intentionally absent; production code continues to use <code>connectDatabaseEmulator</code> from the unchanged <code>firebase/database</code> package</span></span></summary>
1022
- <div class="compat-evidence"><div class="compat-probe">Phase 3</div></div>
1023
- </details>
1024
- </div>
1025
-
1026
812
  ### Transport, logging, and URL-reference exports
1027
813
 
1028
814
  <div class="compat-list">
1029
815
  <details class="compat-row" data-status="diverged">
1030
816
  <summary class="compat-line"><span class="compat-dot" data-status="diverged" role="img" aria-label="Diverged (documented)" title="Diverged (documented)"></span><span class="compat-main"><span class="compat-behavior"><code>forceLongPolling()</code> — accepted no-op: transport selection is not applicable to the in-process/worker sandbox (it never opens a real socket). Accepted so init code that calls it compiles + runs</span></span></summary>
1031
- <div class="compat-evidence"><div class="compat-probe"><code>unit:modular/fruit-aliases.test.ts</code></div>
817
+ <div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/modular/fruit-aliases.test.ts#L19" target="_blank" rel="noopener noreferrer"><code>unit:modular/fruit-aliases.test.ts</code></a></div>
1032
818
  <div class="compat-note">transport selection not applicable to the in-process/worker sandbox</div></div>
1033
819
  </details>
1034
820
  <details class="compat-row" data-status="diverged">
1035
821
  <summary class="compat-line"><span class="compat-dot" data-status="diverged" role="img" aria-label="Diverged (documented)" title="Diverged (documented)"></span><span class="compat-main"><span class="compat-behavior"><code>forceWebSockets()</code> — accepted no-op: transport selection is not applicable to the in-process/worker sandbox (see <code>forceLongPolling</code>)</span></span></summary>
1036
- <div class="compat-evidence"><div class="compat-probe"><code>unit:modular/fruit-aliases.test.ts</code></div>
822
+ <div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/modular/fruit-aliases.test.ts#L20" target="_blank" rel="noopener noreferrer"><code>unit:modular/fruit-aliases.test.ts</code></a></div>
1037
823
  <div class="compat-note">transport selection not applicable to the in-process/worker sandbox</div></div>
1038
824
  </details>
1039
825
  <details class="compat-row" data-status="diverged">
1040
826
  <summary class="compat-line"><span class="compat-dot" data-status="diverged" role="img" aria-label="Diverged (documented)" title="Diverged (documented)"></span><span class="compat-main"><span class="compat-behavior"><code>enableLogging(logger?, persistent?)</code> — accepted no-op: the sandbox has no modular-SDK-style logger to wire a level/sink into (it uses host-level <code>console</code> logging directly, matching <code>pyric/firestore</code>'s <code>setLogLevel</code>). Accepted so init code that calls it compiles + runs</span></span></summary>
1041
- <div class="compat-evidence"><div class="compat-probe"><code>unit:modular/fruit-aliases.test.ts</code></div>
827
+ <div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/modular/fruit-aliases.test.ts#L21" target="_blank" rel="noopener noreferrer"><code>unit:modular/fruit-aliases.test.ts</code></a></div>
1042
828
  <div class="compat-note">accepted no-op; no sandbox logger to wire into</div></div>
1043
829
  </details>
1044
830
  <details class="compat-row" data-status="diverged">
1045
831
  <summary class="compat-line"><span class="compat-dot" data-status="diverged" role="img" aria-label="Diverged (documented)" title="Diverged (documented)"></span><span class="compat-main"><span class="compat-behavior"><strong>Divergence:</strong> <code>refFromURL(db, url)</code> matches Firebase's path parsing, query ignoring, FTP-to-database normalization, and fragment rejection, but the single-database sandbox does not validate the URL host/namespace against the handle; Firebase rejects a mismatched host while the sandbox uses its path.</span></span></summary>
1046
- <div class="compat-evidence"><div class="compat-probe">Oracle <code>rtdb-modular-reference-shape-url</code> captures accepted FTP/query URLs and rejected fragments, malformed URLs, and host mismatches; <code>unit:modular/oracle-conformance-reference-writes.test.ts</code> pins the conforming validations plus both sides of the host divergence.</div>
832
+ <div class="compat-evidence"><div class="compat-probe">Oracle <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/rtdb-modular/rtdb-modular-reference-shape-url.json" target="_blank" rel="noopener noreferrer"><code>rtdb-modular-reference-shape-url</code></a> captures accepted FTP/query URLs and rejected fragments, malformed URLs, and host mismatches; <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/modular/oracle-conformance-reference-writes.test.ts#L33" target="_blank" rel="noopener noreferrer"><code>unit:modular/oracle-conformance-reference-writes.test.ts</code></a> pins the conforming validations plus both sides of the host divergence.</div>
1047
833
  <div class="compat-note">path and validation behavior conform except URL host/namespace matching (single-database sandbox)</div></div>
1048
834
  </details>
1049
835
  </div>
@@ -1055,83 +841,34 @@ tooling coverage and are not exports of `pyric/database`.
1055
841
  Known differences between Pyric and production Firebase. Each remains tracked as a non-conforming row.
1056
842
 
1057
843
  <div class="compat-list">
1058
- <details class="compat-row" data-status="diverged"><summary class="compat-line"><span class="compat-dot" data-status="diverged" role="img" aria-label="Diverged (documented)" title="Diverged (documented)"></span><span class="compat-main"><code class="compat-api">simulateRtdbRules(compiled, input)</code><span class="compat-sub"><span class="compat-behavior">Cross-path <code>root.child(…).val()</code> reads return <code>null</code> for paths NOT present in <code>mockData</code> — divergence from real prod rules where the engine reads the live database</span></span></span></summary>
1059
- <div class="compat-evidence"><div class="compat-note">divergence: the simulator uses ONLY what's in <code>mockData</code>. Real rules engine reads from the live RTDB. Documented in <code>validated.ts</code> ("simulation uses empty mockData, so cross-path rule lookups … will evaluate as false")</div></div></details>
1060
844
  <details class="compat-row" data-status="diverged"><summary class="compat-line"><span class="compat-dot" data-status="diverged" role="img" aria-label="Diverged (documented)" title="Diverged (documented)"></span><span class="compat-main"><span class="compat-sub"><span class="compat-behavior">Cold production clients may invoke the update fn first with speculative <code>null</code> and then with the current seeded value; the always-warm in-process sandbox invokes it once with the current value</span></span></span></summary>
1061
- <div class="compat-evidence"><div class="compat-note">Oracle <code>rtdb-modular-runtransaction-current-value-arg</code> captures two seeded-path production invocations (<code>null</code>, then current); <code>unit:modular/oracle-conformance-transactions.test.ts</code> pins the sandbox's single current-value invocation. The separate warm-client observation matches the sandbox but does not erase the cold-client contract.</div></div></details>
845
+ <div class="compat-evidence"><div class="compat-note">Oracle <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/rtdb-modular/rtdb-modular-runtransaction-current-value-arg.json" target="_blank" rel="noopener noreferrer"><code>rtdb-modular-runtransaction-current-value-arg</code></a> captures two seeded-path production invocations (<code>null</code>, then current); <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/modular/oracle-conformance-transactions.test.ts#L52" target="_blank" rel="noopener noreferrer"><code>unit:modular/oracle-conformance-transactions.test.ts</code></a> pins the sandbox's single current-value invocation. The separate warm-client observation matches the sandbox but does not erase the cold-client contract. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/modular/transaction.test.ts#L93" target="_blank" rel="noopener noreferrer"><code>transaction.test.ts</code></a>)</div></div></details>
1062
846
  <details class="compat-row" data-status="diverged"><summary class="compat-line"><span class="compat-dot" data-status="diverged" role="img" aria-label="Diverged (documented)" title="Diverged (documented)"></span><span class="compat-main"><span class="compat-sub"><span class="compat-behavior">**Divergence:** two ordinary concurrent <code>runTransaction</code> calls are serialized by the in-process backend, so their update functions are not retried with Firebase's captured contention counts. A synchronous re-entrant conflicting write does trigger a deterministic retry.</span></span></span></summary>
1063
- <div class="compat-evidence"><div class="compat-note">Oracle <code>rtdb-modular-concurrent-transforms</code> captures invocation counts [2, 3]; <code>unit:modular/transaction-contention-cdd.test.ts</code> pins the sandbox's [1, 1] ordinary-concurrency boundary and separately covers its deterministic re-entrant retry seam.</div></div></details>
847
+ <div class="compat-evidence"><div class="compat-note">Oracle <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/rtdb-modular/rtdb-modular-concurrent-transforms.json" target="_blank" rel="noopener noreferrer"><code>rtdb-modular-concurrent-transforms</code></a> captures invocation counts [2, 3]; <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/modular/transaction-contention-cdd.test.ts#L12" target="_blank" rel="noopener noreferrer"><code>unit:modular/transaction-contention-cdd.test.ts</code></a> pins the sandbox's [1, 1] ordinary-concurrency boundary and separately covers its deterministic re-entrant retry seam.</div></div></details>
1064
848
  <details class="compat-row" data-status="diverged"><summary class="compat-line"><span class="compat-dot" data-status="diverged" role="img" aria-label="Diverged (documented)" title="Diverged (documented)"></span><span class="compat-main"><span class="compat-sub"><span class="compat-behavior">Production rejects an unindexed <code>orderByValue() + limitToFirst(N)</code> query; the sandbox does not enforce <code>.indexOn</code> and returns the N smallest primitive values</span></span></span></summary>
1065
- <div class="compat-evidence"><div class="compat-note">Oracle <code>rtdb-modular-orderbyvalue-numeric</code> captures production's <code>Index not defined</code> rejection; <code>unit:modular/oracle-conformance-queries.test.ts</code> pins that rejection beside the sandbox's successful <code>[10,20,30]</code> window.</div></div></details>
849
+ <div class="compat-evidence"><div class="compat-note">Oracle <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/rtdb-modular/rtdb-modular-orderbyvalue-numeric.json" target="_blank" rel="noopener noreferrer"><code>rtdb-modular-orderbyvalue-numeric</code></a> captures production's <code>Index not defined</code> rejection; <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/modular/queries.test.ts#L108" target="_blank" rel="noopener noreferrer"><code>unit:modular/oracle-conformance-queries.test.ts</code></a> pins that rejection beside the sandbox's successful <code>[10,20,30]</code> window. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/modular/oracle-conformance-queries.test.ts#L51" target="_blank" rel="noopener noreferrer"><code>oracle-conformance-queries.test.ts</code></a>)</div></div></details>
1066
850
  <details class="compat-row" data-status="diverged"><summary class="compat-line"><span class="compat-dot" data-status="diverged" role="img" aria-label="Diverged (documented)" title="Diverged (documented)"></span><span class="compat-main"><span class="compat-sub"><span class="compat-behavior">**Divergence (pending fix):** clean <code>goOffline</code>, app deletion, playground <code>pagehide</code>, and best-effort MessagePort close drain queued operations, but unannounced total renderer/process loss is not guaranteed by the in-memory sandbox</span></span></span></summary>
1067
- <div class="compat-evidence"><div class="compat-note">Oracle <code>rtdb-modular-ondisconnect-abrupt-exit</code> proves Firebase executes an acknowledged registration after forced writer termination. The two-port worker integration exercises <code>goOffline</code>, served-app deletion, and non-persisted <code>pagehide</code>; browser MessagePort close delivery cannot prove total-process loss without durable host-owned leases.</div></div></details>
851
+ <div class="compat-evidence"><div class="compat-note">Oracle <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/rtdb-modular/rtdb-modular-ondisconnect-abrupt-exit.json" target="_blank" rel="noopener noreferrer"><code>rtdb-modular-ondisconnect-abrupt-exit</code></a> proves Firebase executes an acknowledged registration after forced writer termination. The two-port worker integration exercises <code>goOffline</code>, served-app deletion, and non-persisted <code>pagehide</code>; browser MessagePort close delivery cannot prove total-process loss without durable host-owned leases. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/cli/test/serve/worker/rtdb-integration.test.ts#L24" target="_blank" rel="noopener noreferrer"><code>rtdb-integration.test.ts</code></a>)</div></div></details>
1068
852
  <details class="compat-row" data-status="diverged"><summary class="compat-line"><span class="compat-dot" data-status="diverged" role="img" aria-label="Diverged (documented)" title="Diverged (documented)"></span><span class="compat-main"><span class="compat-sub"><span class="compat-behavior">**Divergence:** <code>DatabaseReference</code> and <code>Query</code> equality includes database target, path, and canonical query parameters like Firebase, but <code>toJSON()</code> serializes the local <code>sandbox://rtdb/...</code> identity instead of Firebase's HTTPS database URL.</span></span></span></summary>
1069
- <div class="compat-evidence"><div class="compat-note">Oracle <code>rtdb-modular-reference-shape-url</code> captures conforming equality plus HTTPS JSON serialization; <code>unit:modular/oracle-conformance-reference-writes.test.ts</code> pins both the production HTTPS shape and the sandbox's <code>sandbox://</code> boundary.</div></div></details>
853
+ <div class="compat-evidence"><div class="compat-note">Oracle <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/rtdb-modular/rtdb-modular-reference-shape-url.json" target="_blank" rel="noopener noreferrer"><code>rtdb-modular-reference-shape-url</code></a> captures conforming equality plus HTTPS JSON serialization; <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/modular/oracle-conformance-reference-writes.test.ts#L33" target="_blank" rel="noopener noreferrer"><code>unit:modular/oracle-conformance-reference-writes.test.ts</code></a> pins both the production HTTPS shape and the sandbox's <code>sandbox://</code> boundary.</div></div></details>
1070
854
  <details class="compat-row" data-status="diverged"><summary class="compat-line"><span class="compat-dot" data-status="diverged" role="img" aria-label="Diverged (documented)" title="Diverged (documented)"></span><span class="compat-main"><span class="compat-sub"><span class="compat-behavior">Production enforces <code>.indexOn: '.value'</code> for <code>orderByValue()</code> while the sandbox executes the value ordering without index enforcement</span></span></span></summary>
1071
- <div class="compat-evidence"><div class="compat-note">Oracle <code>rtdb-modular-orderbyvalue-numeric</code> rejects the unindexed production query; <code>unit:modular/oracle-conformance-queries.test.ts</code> pins the sandbox's successful ordered result instead of treating that contradiction as conformance.</div></div></details>
855
+ <div class="compat-evidence"><div class="compat-note">Oracle <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/rtdb-modular/rtdb-modular-orderbyvalue-numeric.json" target="_blank" rel="noopener noreferrer"><code>rtdb-modular-orderbyvalue-numeric</code></a> rejects the unindexed production query; <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/modular/oracle-conformance-queries.test.ts#L51" target="_blank" rel="noopener noreferrer"><code>unit:modular/oracle-conformance-queries.test.ts</code></a> pins the sandbox's successful ordered result instead of treating that contradiction as conformance.</div></div></details>
1072
856
  <details class="compat-row" data-status="diverged"><summary class="compat-line"><span class="compat-dot" data-status="diverged" role="img" aria-label="Diverged (documented)" title="Diverged (documented)"></span><span class="compat-main"><span class="compat-sub"><span class="compat-behavior">**Divergence:** production interleaves concurrent <code>increment</code> writes from independent clients and preserves both deltas; the synchronous in-process backend applies each call before returning its promise, so a <code>Promise.all</code> call site is observably serialized even though its terminal value also reaches 5</span></span></span></summary>
1073
- <div class="compat-evidence"><div class="compat-note">Oracle <code>rtdb-modular-concurrent-transforms</code> uses two independent clients and captures terminal 5 after concurrent +2/+3 increments; <code>unit:modular/transaction-contention-cdd.test.ts</code> pins the sandbox's synchronous first-write visibility and matching terminal value without claiming that terminal equality proves contention semantics.</div></div></details>
857
+ <div class="compat-evidence"><div class="compat-note">Oracle <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/rtdb-modular/rtdb-modular-concurrent-transforms.json" target="_blank" rel="noopener noreferrer"><code>rtdb-modular-concurrent-transforms</code></a> uses two independent clients and captures terminal 5 after concurrent +2/+3 increments; <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/modular/transaction-contention-cdd.test.ts#L12" target="_blank" rel="noopener noreferrer"><code>unit:modular/transaction-contention-cdd.test.ts</code></a> pins the sandbox's synchronous first-write visibility and matching terminal value without claiming that terminal equality proves contention semantics.</div></div></details>
1074
858
  <details class="compat-row" data-status="diverged"><summary class="compat-line"><span class="compat-dot" data-status="diverged" role="img" aria-label="Diverged (documented)" title="Diverged (documented)"></span><span class="compat-main"><span class="compat-sub"><span class="compat-behavior">A cold production client can call the update fn with speculative <code>null</code> before the current seeded value; the always-warm sandbox calls it once with the current value</span></span></span></summary>
1075
- <div class="compat-evidence"><div class="compat-note">Oracle <code>rtdb-modular-runtransaction-current-value-arg</code> captures the cold two-call sequence and <code>rtdb-modular-runtransaction-warm-client-speculation</code> captures the matching warm one-call case; <code>unit:modular/oracle-conformance-transactions.test.ts</code> pins both sides.</div></div></details>
859
+ <div class="compat-evidence"><div class="compat-note">Oracle <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/rtdb-modular/rtdb-modular-runtransaction-current-value-arg.json" target="_blank" rel="noopener noreferrer"><code>rtdb-modular-runtransaction-current-value-arg</code></a> captures the cold two-call sequence and <code>rtdb-modular-runtransaction-warm-client-speculation</code> captures the matching warm one-call case; <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/modular/oracle-conformance-transactions.test.ts#L20" target="_blank" rel="noopener noreferrer"><code>unit:modular/oracle-conformance-transactions.test.ts</code></a> pins both sides. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/rtdb-modular/rtdb-modular-runtransaction-options-applylocally.json" target="_blank" rel="noopener noreferrer"><code>rtdb-modular-runtransaction-options-applylocally</code></a>, <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/rtdb-modular/rtdb-modular-runtransaction-success.json" target="_blank" rel="noopener noreferrer"><code>rtdb-modular-runtransaction-success</code></a>)</div></div></details>
1076
860
  <details class="compat-row" data-status="diverged"><summary class="compat-line"><span class="compat-dot" data-status="diverged" role="img" aria-label="Diverged (documented)" title="Diverged (documented)"></span><span class="compat-main"><span class="compat-sub"><span class="compat-behavior">Concurrent contention — if another client writes between the read and write, the update fn is retried with the new current value (typically up to 25 retries by default)</span></span></span></summary>
1077
- <div class="compat-evidence"><div class="compat-note">Oracle <code>rtdb-modular-concurrent-transforms</code> captures callback counts [2, 3] across two clients; the synchronous in-process backend serializes the same ordinary calls with [1, 1], pinned by <code>unit:modular/transaction-contention-cdd.test.ts</code>.</div></div></details>
861
+ <div class="compat-evidence"><div class="compat-note">Oracle <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/rtdb-modular/rtdb-modular-concurrent-transforms.json" target="_blank" rel="noopener noreferrer"><code>rtdb-modular-concurrent-transforms</code></a> captures callback counts [2, 3] across two clients; the synchronous in-process backend serializes the same ordinary calls with [1, 1], pinned by <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/modular/transaction-contention-cdd.test.ts#L12" target="_blank" rel="noopener noreferrer"><code>unit:modular/transaction-contention-cdd.test.ts</code></a>.</div></div></details>
1078
862
  <details class="compat-row" data-status="diverged"><summary class="compat-line"><span class="compat-dot" data-status="diverged" role="img" aria-label="Diverged (documented)" title="Diverged (documented)"></span><span class="compat-main"><span class="compat-sub"><span class="compat-behavior"><code>goOffline(db)</code> drains that client's onDisconnect queue once, but does not make the in-memory data plane unreachable; ordinary writes, listeners, and <code>get()</code> remain available</span></span></span></summary>
1079
- <div class="compat-evidence"><div class="compat-note"><code>unit:database/on-disconnect.test.ts</code> + <code>unit:modular/fruit-aliases.test.ts</code></div></div></details>
863
+ <div class="compat-evidence"><div class="compat-note"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/on-disconnect.test.ts#L10" target="_blank" rel="noopener noreferrer"><code>unit:database/on-disconnect.test.ts</code></a> + <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/modular/fruit-aliases.test.ts#L17" target="_blank" rel="noopener noreferrer"><code>unit:modular/fruit-aliases.test.ts</code></a></div></div></details>
1080
864
  <details class="compat-row" data-status="diverged"><summary class="compat-line"><span class="compat-dot" data-status="diverged" role="img" aria-label="Diverged (documented)" title="Diverged (documented)"></span><span class="compat-main"><span class="compat-sub"><span class="compat-behavior"><code>goOnline(db)</code> reconnects the logical lifecycle without resurrecting drained operations; it is otherwise a no-op because the in-memory data plane never became unreachable</span></span></span></summary>
1081
- <div class="compat-evidence"><div class="compat-note"><code>unit:database/on-disconnect.test.ts</code> + <code>unit:modular/fruit-aliases.test.ts</code></div></div></details>
865
+ <div class="compat-evidence"><div class="compat-note"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/on-disconnect.test.ts#L11" target="_blank" rel="noopener noreferrer"><code>unit:database/on-disconnect.test.ts</code></a> + <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/modular/fruit-aliases.test.ts#L18" target="_blank" rel="noopener noreferrer"><code>unit:modular/fruit-aliases.test.ts</code></a></div></div></details>
1082
866
  <details class="compat-row" data-status="diverged"><summary class="compat-line"><span class="compat-dot" data-status="diverged" role="img" aria-label="Diverged (documented)" title="Diverged (documented)"></span><span class="compat-main"><span class="compat-sub"><span class="compat-behavior"><code>forceLongPolling()</code> — accepted no-op: transport selection is not applicable to the in-process/worker sandbox (it never opens a real socket). Accepted so init code that calls it compiles + runs</span></span></span></summary>
1083
- <div class="compat-evidence"><div class="compat-note"><code>unit:modular/fruit-aliases.test.ts</code></div></div></details>
867
+ <div class="compat-evidence"><div class="compat-note"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/modular/fruit-aliases.test.ts#L19" target="_blank" rel="noopener noreferrer"><code>unit:modular/fruit-aliases.test.ts</code></a></div></div></details>
1084
868
  <details class="compat-row" data-status="diverged"><summary class="compat-line"><span class="compat-dot" data-status="diverged" role="img" aria-label="Diverged (documented)" title="Diverged (documented)"></span><span class="compat-main"><span class="compat-sub"><span class="compat-behavior"><code>forceWebSockets()</code> — accepted no-op: transport selection is not applicable to the in-process/worker sandbox (see <code>forceLongPolling</code>)</span></span></span></summary>
1085
- <div class="compat-evidence"><div class="compat-note"><code>unit:modular/fruit-aliases.test.ts</code></div></div></details>
869
+ <div class="compat-evidence"><div class="compat-note"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/modular/fruit-aliases.test.ts#L20" target="_blank" rel="noopener noreferrer"><code>unit:modular/fruit-aliases.test.ts</code></a></div></div></details>
1086
870
  <details class="compat-row" data-status="diverged"><summary class="compat-line"><span class="compat-dot" data-status="diverged" role="img" aria-label="Diverged (documented)" title="Diverged (documented)"></span><span class="compat-main"><span class="compat-sub"><span class="compat-behavior"><code>enableLogging(logger?, persistent?)</code> — accepted no-op: the sandbox has no modular-SDK-style logger to wire a level/sink into (it uses host-level <code>console</code> logging directly, matching <code>pyric/firestore</code>'s <code>setLogLevel</code>). Accepted so init code that calls it compiles + runs</span></span></span></summary>
1087
- <div class="compat-evidence"><div class="compat-note"><code>unit:modular/fruit-aliases.test.ts</code></div></div></details>
871
+ <div class="compat-evidence"><div class="compat-note"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/modular/fruit-aliases.test.ts#L21" target="_blank" rel="noopener noreferrer"><code>unit:modular/fruit-aliases.test.ts</code></a></div></div></details>
1088
872
  <details class="compat-row" data-status="diverged"><summary class="compat-line"><span class="compat-dot" data-status="diverged" role="img" aria-label="Diverged (documented)" title="Diverged (documented)"></span><span class="compat-main"><span class="compat-sub"><span class="compat-behavior">**Divergence:** <code>refFromURL(db, url)</code> matches Firebase's path parsing, query ignoring, FTP-to-database normalization, and fragment rejection, but the single-database sandbox does not validate the URL host/namespace against the handle; Firebase rejects a mismatched host while the sandbox uses its path.</span></span></span></summary>
1089
- <div class="compat-evidence"><div class="compat-note">Oracle <code>rtdb-modular-reference-shape-url</code> captures accepted FTP/query URLs and rejected fragments, malformed URLs, and host mismatches; <code>unit:modular/oracle-conformance-reference-writes.test.ts</code> pins the conforming validations plus both sides of the host divergence.</div></div></details>
1090
- </div>
1091
-
1092
- ### Not implemented yet
1093
-
1094
- Tracked behavior that is not implemented in the current contract.
1095
-
1096
- <div class="compat-list">
1097
- <details class="compat-row" data-status="unsupported"><summary class="compat-line"><span class="compat-dot" data-status="unsupported" role="img" aria-label="Not implemented yet" title="Not implemented yet"></span><span class="compat-main"><code class="compat-api">Removed REST host</code><span class="compat-sub"><span class="compat-behavior">Historical <code>.json</code> REST transport contract for the removed production host.</span></span></span></summary>
1098
- <div class="compat-evidence"><div class="compat-note">Archived oracle observation; implementation removed.</div></div></details>
1099
- <details class="compat-row" data-status="unsupported"><summary class="compat-line"><span class="compat-dot" data-status="unsupported" role="img" aria-label="Not implemented yet" title="Not implemented yet"></span><span class="compat-main"><code class="compat-api">Removed data handler</code><span class="compat-sub"><span class="compat-behavior">Historical admin read and set/get behavior for the removed production data handler.</span></span></span></summary>
1100
- <div class="compat-evidence"><div class="compat-note">Archived oracle observations; implementation removed.</div></div></details>
1101
- <details class="compat-row" data-status="unsupported"><summary class="compat-line"><span class="compat-dot" data-status="unsupported" role="img" aria-label="Not implemented yet" title="Not implemented yet"></span><span class="compat-main"><code class="compat-api">Removed data handler</code><span class="compat-sub"><span class="compat-behavior">Historical user read return shape for the removed production data handler.</span></span></span></summary>
1102
- <div class="compat-evidence"><div class="compat-note">Archived oracle observation; implementation removed.</div></div></details>
1103
- <details class="compat-row" data-status="unsupported"><summary class="compat-line"><span class="compat-dot" data-status="unsupported" role="img" aria-label="Not implemented yet" title="Not implemented yet"></span><span class="compat-main"><code class="compat-api">Removed data handler</code><span class="compat-sub"><span class="compat-behavior">Historical rules-denial normalization for the removed production data handler.</span></span></span></summary>
1104
- <div class="compat-evidence"><div class="compat-note">Archived oracle observations; implementation removed.</div></div></details>
1105
- <details class="compat-row" data-status="unsupported"><summary class="compat-line"><span class="compat-dot" data-status="unsupported" role="img" aria-label="Not implemented yet" title="Not implemented yet"></span><span class="compat-main"><code class="compat-api">Removed data handler</code><span class="compat-sub"><span class="compat-behavior">Historical rules-denied read behavior for the removed production data handler.</span></span></span></summary>
1106
- <div class="compat-evidence"><div class="compat-note">Archived oracle observation; implementation removed.</div></div></details>
1107
- <details class="compat-row" data-status="unsupported"><summary class="compat-line"><span class="compat-dot" data-status="unsupported" role="img" aria-label="Not implemented yet" title="Not implemented yet"></span><span class="compat-main"><code class="compat-api">Removed data handler</code><span class="compat-sub"><span class="compat-behavior">Historical set/get round trip for the removed production data handler.</span></span></span></summary>
1108
- <div class="compat-evidence"><div class="compat-note">Archived oracle observation; implementation removed.</div></div></details>
1109
- <details class="compat-row" data-status="unsupported"><summary class="compat-line"><span class="compat-dot" data-status="unsupported" role="img" aria-label="Not implemented yet" title="Not implemented yet"></span><span class="compat-main"><code class="compat-api">Removed data handler</code><span class="compat-sub"><span class="compat-behavior">Historical set-null removal behavior for the removed production data handler.</span></span></span></summary>
1110
- <div class="compat-evidence"><div class="compat-note">Archived oracle observation; implementation removed.</div></div></details>
1111
- <details class="compat-row" data-status="unsupported"><summary class="compat-line"><span class="compat-dot" data-status="unsupported" role="img" aria-label="Not implemented yet" title="Not implemented yet"></span><span class="compat-main"><code class="compat-api">Removed data handler</code><span class="compat-sub"><span class="compat-behavior">Historical rules-denied write behavior for the removed production data handler.</span></span></span></summary>
1112
- <div class="compat-evidence"><div class="compat-note">Archived oracle observation; implementation removed.</div></div></details>
1113
- <details class="compat-row" data-status="unsupported"><summary class="compat-line"><span class="compat-dot" data-status="unsupported" role="img" aria-label="Not implemented yet" title="Not implemented yet"></span><span class="compat-main"><code class="compat-api">Removed data handler</code><span class="compat-sub"><span class="compat-behavior">Historical multi-path update behavior for the removed production data handler.</span></span></span></summary>
1114
- <div class="compat-evidence"><div class="compat-note">Archived oracle observation; implementation removed.</div></div></details>
1115
- <details class="compat-row" data-status="unsupported"><summary class="compat-line"><span class="compat-dot" data-status="unsupported" role="img" aria-label="Not implemented yet" title="Not implemented yet"></span><span class="compat-main"><code class="compat-api">Removed data handler</code><span class="compat-sub"><span class="compat-behavior">Historical push key behavior for the removed production data handler.</span></span></span></summary>
1116
- <div class="compat-evidence"><div class="compat-note">Archived oracle observation; implementation removed.</div></div></details>
1117
- <details class="compat-row" data-status="unsupported"><summary class="compat-line"><span class="compat-dot" data-status="unsupported" role="img" aria-label="Not implemented yet" title="Not implemented yet"></span><span class="compat-main"><code class="compat-api">Removed data handler</code><span class="compat-sub"><span class="compat-behavior">Historical push auto-ID format for the removed production data handler.</span></span></span></summary>
1118
- <div class="compat-evidence"><div class="compat-note">Archived oracle observation; implementation removed.</div></div></details>
1119
- <details class="compat-row" data-status="unsupported"><summary class="compat-line"><span class="compat-dot" data-status="unsupported" role="img" aria-label="Not implemented yet" title="Not implemented yet"></span><span class="compat-main"><code class="compat-api">Removed data handler</code><span class="compat-sub"><span class="compat-behavior">Historical remove-versus-set-null behavior for the removed production data handler.</span></span></span></summary>
1120
- <div class="compat-evidence"><div class="compat-note">Archived oracle observation; implementation removed.</div></div></details>
1121
- <details class="compat-row" data-status="unsupported"><summary class="compat-line"><span class="compat-dot" data-status="unsupported" role="img" aria-label="Not implemented yet" title="Not implemented yet"></span><span class="compat-main"><code class="compat-api">Removed data handler</code><span class="compat-sub"><span class="compat-behavior">Historical idempotent removal behavior for the removed production data handler.</span></span></span></summary>
1122
- <div class="compat-evidence"><div class="compat-note">Archived oracle observation; implementation removed.</div></div></details>
1123
- <details class="compat-row" data-status="unsupported"><summary class="compat-line"><span class="compat-dot" data-status="unsupported" role="img" aria-label="Not implemented yet" title="Not implemented yet"></span><span class="compat-main"><code class="compat-api">Removed rules fetch handler</code><span class="compat-sub"><span class="compat-behavior">Historical deployed-rules JSON round trip for the removed production fetch handler.</span></span></span></summary>
1124
- <div class="compat-evidence"><div class="compat-note">Archived oracle observation; implementation removed.</div></div></details>
1125
- <details class="compat-row" data-status="unsupported"><summary class="compat-line"><span class="compat-dot" data-status="unsupported" role="img" aria-label="Not implemented yet" title="Not implemented yet"></span><span class="compat-main"><code class="compat-api">Removed rules deployment handler</code><span class="compat-sub"><span class="compat-behavior">Historical rules deployment propagation timing for the removed production deploy handler.</span></span></span></summary>
1126
- <div class="compat-evidence"><div class="compat-note">Archived oracle observation; implementation removed.</div></div></details>
1127
- <details class="compat-row" data-status="unsupported"><summary class="compat-line"><span class="compat-dot" data-status="unsupported" role="img" aria-label="Not implemented yet" title="Not implemented yet"></span><span class="compat-main"><code class="compat-api">Removed REST crawler</code><span class="compat-sub"><span class="compat-behavior">Historical shallow REST response shape for the removed production crawler.</span></span></span></summary>
1128
- <div class="compat-evidence"><div class="compat-note">Archived oracle observation; implementation removed.</div></div></details>
1129
- <details class="compat-row" data-status="unsupported"><summary class="compat-line"><span class="compat-dot" data-status="unsupported" role="img" aria-label="Not implemented yet" title="Not implemented yet"></span><span class="compat-main"><code class="compat-api">simulateRtdbRules(compiled, input)</code><span class="compat-sub"><span class="compat-behavior">The removed stateful simulator returned a generate-before-simulate error when no IR had been generated</span></span></span></summary>
1130
- <div class="compat-evidence"><div class="compat-note">The stateless <code>simulateRtdbRules(compiled, input)</code> API requires a compiled rules tree and has no generate-before-simulate lifecycle.</div></div></details>
1131
- <details class="compat-row" data-status="unsupported"><summary class="compat-line"><span class="compat-dot" data-status="unsupported" role="img" aria-label="Not implemented yet" title="Not implemented yet"></span><span class="compat-main"><span class="compat-sub"><span class="compat-behavior">An in-module production target is intentionally absent; direct calls with a real <code>FirebaseApp</code> reject with package-resolution guidance</span></span></span></summary>
1132
- <div class="compat-evidence"><div class="compat-note"><code>unit:modular.test.ts</code>; production remains the responsibility of the unchanged <code>firebase/database</code> package</div></div></details>
1133
- <details class="compat-row" data-status="unsupported"><summary class="compat-line"><span class="compat-dot" data-status="unsupported" role="img" aria-label="Not implemented yet" title="Not implemented yet"></span><span class="compat-main"><span class="compat-sub"><span class="compat-behavior">No-op on sandbox-target handles (the sandbox IS the local emulator)</span></span></span></summary>
1134
- <div class="compat-evidence"><div class="compat-note">Phase 3</div></div></details>
1135
- <details class="compat-row" data-status="unsupported"><summary class="compat-line"><span class="compat-dot" data-status="unsupported" role="img" aria-label="Not implemented yet" title="Not implemented yet"></span><span class="compat-main"><span class="compat-sub"><span class="compat-behavior">A production target is intentionally absent; production code continues to use <code>connectDatabaseEmulator</code> from the unchanged <code>firebase/database</code> package</span></span></span></summary>
1136
- <div class="compat-evidence"><div class="compat-note">Phase 3</div></div></details>
873
+ <div class="compat-evidence"><div class="compat-note">Oracle <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/rtdb-modular/rtdb-modular-reference-shape-url.json" target="_blank" rel="noopener noreferrer"><code>rtdb-modular-reference-shape-url</code></a> captures accepted FTP/query URLs and rejected fragments, malformed URLs, and host mismatches; <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/modular/oracle-conformance-reference-writes.test.ts#L33" target="_blank" rel="noopener noreferrer"><code>unit:modular/oracle-conformance-reference-writes.test.ts</code></a> pins the conforming validations plus both sides of the host divergence.</div></div></details>
1137
874
  </div>