@pyric/cli 0.1.0-alpha.16 → 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 (378) 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/cli/database-rules.d.ts.map +1 -1
  7. package/dist/cli/database-rules.js +6 -4
  8. package/dist/cli/database-rules.js.map +1 -1
  9. package/dist/cli/dev-runner.d.ts +9 -0
  10. package/dist/cli/dev-runner.d.ts.map +1 -1
  11. package/dist/cli/dev-runner.js +18 -0
  12. package/dist/cli/dev-runner.js.map +1 -1
  13. package/dist/cli/serve.d.ts.map +1 -1
  14. package/dist/cli/serve.js +61 -6
  15. package/dist/cli/serve.js.map +1 -1
  16. package/dist/cli/verify.js +2 -2
  17. package/dist/cli/verify.js.map +1 -1
  18. package/dist/conformance/.generated/can-i-use-browser.d.ts.map +1 -1
  19. package/dist/conformance/.generated/can-i-use-browser.js +1 -1
  20. package/dist/conformance/.generated/can-i-use-browser.js.map +1 -1
  21. package/dist/conformance/.generated/can-i-use.d.ts.map +1 -1
  22. package/dist/conformance/.generated/can-i-use.js +1 -1
  23. package/dist/conformance/.generated/can-i-use.js.map +1 -1
  24. package/dist/conformance/.generated/conformance-docs.d.ts.map +1 -1
  25. package/dist/conformance/.generated/conformance-docs.js +1 -1
  26. package/dist/conformance/.generated/conformance-docs.js.map +1 -1
  27. package/dist/next/client-aliases.d.ts +11 -0
  28. package/dist/next/client-aliases.d.ts.map +1 -0
  29. package/dist/next/client-aliases.js +38 -0
  30. package/dist/next/client-aliases.js.map +1 -0
  31. package/dist/next/dev-rewrites.d.ts +9 -0
  32. package/dist/next/dev-rewrites.d.ts.map +1 -0
  33. package/dist/next/dev-rewrites.js +53 -0
  34. package/dist/next/dev-rewrites.js.map +1 -0
  35. package/dist/next/guard.d.ts +15 -0
  36. package/dist/next/guard.d.ts.map +1 -0
  37. package/dist/next/guard.js +52 -0
  38. package/dist/next/guard.js.map +1 -0
  39. package/dist/next/index.d.ts +12 -0
  40. package/dist/next/index.d.ts.map +1 -0
  41. package/dist/next/index.js +11 -0
  42. package/dist/next/index.js.map +1 -0
  43. package/dist/next/runtime-env.d.ts +7 -0
  44. package/dist/next/runtime-env.d.ts.map +1 -0
  45. package/dist/next/runtime-env.js +13 -0
  46. package/dist/next/runtime-env.js.map +1 -0
  47. package/dist/next/server-external-packages.d.ts +10 -0
  48. package/dist/next/server-external-packages.d.ts.map +1 -0
  49. package/dist/next/server-external-packages.js +34 -0
  50. package/dist/next/server-external-packages.js.map +1 -0
  51. package/dist/next/target-url.d.ts +11 -0
  52. package/dist/next/target-url.d.ts.map +1 -0
  53. package/dist/next/target-url.js +36 -0
  54. package/dist/next/target-url.js.map +1 -0
  55. package/dist/next/turbopack-config.d.ts +9 -0
  56. package/dist/next/turbopack-config.d.ts.map +1 -0
  57. package/dist/next/turbopack-config.js +39 -0
  58. package/dist/next/turbopack-config.js.map +1 -0
  59. package/dist/next/types.d.ts +40 -0
  60. package/dist/next/types.d.ts.map +1 -0
  61. package/dist/next/types.js +5 -0
  62. package/dist/next/types.js.map +1 -0
  63. package/dist/next/webpack-config.d.ts +11 -0
  64. package/dist/next/webpack-config.d.ts.map +1 -0
  65. package/dist/next/webpack-config.js +51 -0
  66. package/dist/next/webpack-config.js.map +1 -0
  67. package/dist/next/with-pyric.d.ts +22 -0
  68. package/dist/next/with-pyric.d.ts.map +1 -0
  69. package/dist/next/with-pyric.js +49 -0
  70. package/dist/next/with-pyric.js.map +1 -0
  71. package/dist/register/app-bridge.d.ts +9 -0
  72. package/dist/register/app-bridge.d.ts.map +1 -0
  73. package/dist/register/app-bridge.js +56 -0
  74. package/dist/register/app-bridge.js.map +1 -0
  75. package/dist/register/hooks.d.ts.map +1 -1
  76. package/dist/register/hooks.js +6 -1
  77. package/dist/register/hooks.js.map +1 -1
  78. package/dist/register/index.js +1 -1
  79. package/dist/register/index.js.map +1 -1
  80. package/dist/register/mapping.d.ts +3 -1
  81. package/dist/register/mapping.d.ts.map +1 -1
  82. package/dist/register/mapping.js +41 -5
  83. package/dist/register/mapping.js.map +1 -1
  84. package/dist/rtdb/rules-json.d.ts +12 -0
  85. package/dist/rtdb/rules-json.d.ts.map +1 -1
  86. package/dist/rtdb/rules-json.js +98 -0
  87. package/dist/rtdb/rules-json.js.map +1 -1
  88. package/dist/serve/bundler.d.ts.map +1 -1
  89. package/dist/serve/bundler.js +2 -0
  90. package/dist/serve/bundler.js.map +1 -1
  91. package/dist/serve/discovery.d.ts +4 -4
  92. package/dist/serve/discovery.d.ts.map +1 -1
  93. package/dist/serve/discovery.js +59 -38
  94. package/dist/serve/discovery.js.map +1 -1
  95. package/dist/serve/entries/app-ai-passthrough.d.ts +8 -0
  96. package/dist/serve/entries/app-ai-passthrough.d.ts.map +1 -0
  97. package/dist/serve/entries/app-ai-passthrough.js +59 -0
  98. package/dist/serve/entries/app-ai-passthrough.js.map +1 -0
  99. package/dist/serve/entries/auth-helper-core.js +9 -7
  100. package/dist/serve/entries/auth-helper-core.js.map +1 -1
  101. package/dist/serve/entries/auth.d.ts +1 -0
  102. package/dist/serve/entries/auth.d.ts.map +1 -1
  103. package/dist/serve/entries/auth.js +1 -0
  104. package/dist/serve/entries/auth.js.map +1 -1
  105. package/dist/serve/entries/bridge-url.d.ts.map +1 -1
  106. package/dist/serve/entries/bridge-url.js +7 -2
  107. package/dist/serve/entries/bridge-url.js.map +1 -1
  108. package/dist/serve/entries/database.d.ts +1 -0
  109. package/dist/serve/entries/database.d.ts.map +1 -1
  110. package/dist/serve/entries/database.js +1 -0
  111. package/dist/serve/entries/database.js.map +1 -1
  112. package/dist/serve/entries/firestore.d.ts +1 -0
  113. package/dist/serve/entries/firestore.d.ts.map +1 -1
  114. package/dist/serve/entries/firestore.js +1 -0
  115. package/dist/serve/entries/firestore.js.map +1 -1
  116. package/dist/serve/entries/init.d.ts.map +1 -1
  117. package/dist/serve/entries/init.js +4 -2
  118. package/dist/serve/entries/init.js.map +1 -1
  119. package/dist/serve/entries/runtime.js +23 -0
  120. package/dist/serve/entries/runtime.js.map +1 -1
  121. package/dist/serve/entries/storage.d.ts +2 -0
  122. package/dist/serve/entries/storage.d.ts.map +1 -1
  123. package/dist/serve/entries/storage.js +9 -2
  124. package/dist/serve/entries/storage.js.map +1 -1
  125. package/dist/serve/html-injection.d.ts +3 -1
  126. package/dist/serve/html-injection.d.ts.map +1 -1
  127. package/dist/serve/html-injection.js +27 -1
  128. package/dist/serve/html-injection.js.map +1 -1
  129. package/dist/serve/rules.js +2 -2
  130. package/dist/serve/rules.js.map +1 -1
  131. package/dist/serve/runtime/chip-config.d.ts +1 -1
  132. package/dist/serve/runtime/chip-config.d.ts.map +1 -1
  133. package/dist/serve/runtime/chip-config.js +16 -8
  134. package/dist/serve/runtime/chip-config.js.map +1 -1
  135. package/dist/serve/sandbox-session.d.ts +1 -0
  136. package/dist/serve/sandbox-session.d.ts.map +1 -1
  137. package/dist/serve/sandbox-session.js +30 -2
  138. package/dist/serve/sandbox-session.js.map +1 -1
  139. package/dist/serve/site-ui/_astro/_studio_.B6b_G1ev.css +1 -0
  140. package/dist/serve/site-ui/_astro/app.bnmVTkIY.js +11 -0
  141. package/dist/serve/site-ui/_astro/app.t6u-adB_.js +1 -0
  142. package/dist/serve/site-ui/_astro/{chess-showcase.CnoxIle5.js → chess-showcase.kC8dXLKY.js} +1 -1
  143. package/dist/serve/site-ui/_astro/{example-runtime.R1d96L6-.js → example-runtime.D79PnoSk.js} +1 -1
  144. package/dist/serve/site-ui/_astro/index.DzVde04M.js +1 -0
  145. package/dist/serve/site-ui/_astro/reads.BYBRLbsE.js +1 -0
  146. package/dist/serve/site-ui/_astro/sandbox-controls.C9Aaazt8.js +1252 -0
  147. package/dist/serve/site-ui/_astro/{seed.EyI3VYEu.js → seed.DANAIyRw.js} +1 -1
  148. package/dist/serve/site-ui/auth/index.html +2 -2
  149. package/dist/serve/site-ui/docs/_rhythm/index.html +1 -1
  150. package/dist/serve/site-ui/docs/agent/set-up-your-agent/index.html +1 -1
  151. package/dist/serve/site-ui/docs/agent/watch-and-review/index.html +1 -1
  152. package/dist/serve/site-ui/docs/agent/work-with-an-agent/index.html +1 -1
  153. package/dist/serve/site-ui/docs/ai-compat/index.html +85 -85
  154. package/dist/serve/site-ui/docs/ai-compat.md +84 -84
  155. package/dist/serve/site-ui/docs/api-reference/index.html +3 -1
  156. package/dist/serve/site-ui/docs/api-reference.md +2 -0
  157. package/dist/serve/site-ui/docs/app-compat/index.html +28 -28
  158. package/dist/serve/site-ui/docs/app-compat.md +27 -27
  159. package/dist/serve/site-ui/docs/auth-compat/index.html +122 -122
  160. package/dist/serve/site-ui/docs/auth-compat.md +121 -121
  161. package/dist/serve/site-ui/docs/build/ai-logic/index.html +1 -1
  162. package/dist/serve/site-ui/docs/build/authentication/index.html +1 -1
  163. package/dist/serve/site-ui/docs/build/cloud-firestore/index.html +1 -1
  164. package/dist/serve/site-ui/docs/build/cloud-messaging/index.html +1 -1
  165. package/dist/serve/site-ui/docs/build/cloud-storage/index.html +1 -1
  166. package/dist/serve/site-ui/docs/build/realtime-database/index.html +1 -1
  167. package/dist/serve/site-ui/docs/conformance-scores/index.html +4 -4
  168. package/dist/serve/site-ui/docs/conformance-scores.md +3 -3
  169. package/dist/serve/site-ui/docs/create-pyric-reference-api/index.html +6 -6
  170. package/dist/serve/site-ui/docs/create-pyric-reference-api.md +5 -5
  171. package/dist/serve/site-ui/docs/database-compat/index.html +200 -448
  172. package/dist/serve/site-ui/docs/database-compat.md +198 -461
  173. package/dist/serve/site-ui/docs/examples/chess/index.html +2 -2
  174. package/dist/serve/site-ui/docs/examples/index.html +1 -1
  175. package/dist/serve/site-ui/docs/firestore-compat/index.html +186 -195
  176. package/dist/serve/site-ui/docs/firestore-compat.md +184 -195
  177. package/dist/serve/site-ui/docs/functions-rtdb-compat/index.html +15 -15
  178. package/dist/serve/site-ui/docs/functions-rtdb-compat.md +14 -14
  179. package/dist/serve/site-ui/docs/get-started/how-the-swap-works/index.html +1 -1
  180. package/dist/serve/site-ui/docs/get-started/nextjs/index.html +57 -0
  181. package/dist/serve/site-ui/docs/get-started/nextjs.md +86 -0
  182. package/dist/serve/site-ui/docs/get-started/start-building/index.html +25 -8
  183. package/dist/serve/site-ui/docs/get-started/start-building.md +34 -5
  184. package/dist/serve/site-ui/docs/get-started/vite/index.html +26 -0
  185. package/dist/serve/site-ui/docs/get-started/vite.md +45 -0
  186. package/dist/serve/site-ui/docs/index.json +668 -103
  187. package/dist/serve/site-ui/docs/messaging-compat/index.html +56 -56
  188. package/dist/serve/site-ui/docs/messaging-compat.md +55 -55
  189. package/dist/serve/site-ui/docs/observe/resolve-runtime-status/index.html +1 -1
  190. package/dist/serve/site-ui/docs/observe/see-whats-happening/index.html +1 -1
  191. package/dist/serve/site-ui/docs/observe/shape-your-data/index.html +1 -1
  192. package/dist/serve/site-ui/docs/overview/index.html +1 -1
  193. package/dist/serve/site-ui/docs/pyric-admin-app-reference-api/index.html +39 -13
  194. package/dist/serve/site-ui/docs/pyric-admin-app-reference-api.md +28 -2
  195. package/dist/serve/site-ui/docs/pyric-admin-auth-reference-api/index.html +1 -1
  196. package/dist/serve/site-ui/docs/pyric-admin-database-reference-api/index.html +38 -6
  197. package/dist/serve/site-ui/docs/pyric-admin-database-reference-api.md +43 -0
  198. package/dist/serve/site-ui/docs/pyric-admin-firestore-reference-api/index.html +9 -1
  199. package/dist/serve/site-ui/docs/pyric-admin-firestore-reference-api.md +8 -0
  200. package/dist/serve/site-ui/docs/pyric-admin-messaging-reference-api/index.html +1 -1
  201. package/dist/serve/site-ui/docs/pyric-admin-storage-reference-api/index.html +27 -4
  202. package/dist/serve/site-ui/docs/pyric-admin-storage-reference-api.md +23 -0
  203. package/dist/serve/site-ui/docs/pyric-ai-reference-api/index.html +1 -1
  204. package/dist/serve/site-ui/docs/pyric-ai-scripting-reference-api/index.html +1 -1
  205. package/dist/serve/site-ui/docs/pyric-app-reference-api/index.html +1 -1
  206. package/dist/serve/site-ui/docs/pyric-auth-reference-api/index.html +1 -1
  207. package/dist/serve/site-ui/docs/pyric-cli-assurance-browser-reference-api/index.html +1 -1
  208. package/dist/serve/site-ui/docs/pyric-cli-assurance-reference-api/index.html +1 -1
  209. package/dist/serve/site-ui/docs/pyric-cli-bridge-client-reference-api/index.html +1 -1
  210. package/dist/serve/site-ui/docs/pyric-cli-bridge-reference-api/index.html +1 -1
  211. package/dist/serve/site-ui/docs/pyric-cli-conformance-browser-reference-api/index.html +1 -1
  212. package/dist/serve/site-ui/docs/pyric-cli-conformance-docs-reference-api/index.html +1 -1
  213. package/dist/serve/site-ui/docs/pyric-cli-conformance-reference-api/index.html +1 -1
  214. package/dist/serve/site-ui/docs/pyric-cli-credentials-node-reference-api/index.html +1 -1
  215. package/dist/serve/site-ui/docs/pyric-cli-discover-reference-api/index.html +1 -1
  216. package/dist/serve/site-ui/docs/pyric-cli-next-reference-api/index.html +123 -0
  217. package/dist/serve/site-ui/docs/pyric-cli-next-reference-api.md +99 -0
  218. package/dist/serve/site-ui/docs/pyric-cli-register-app-bridge-reference-api/index.html +127 -0
  219. package/dist/serve/site-ui/docs/pyric-cli-register-app-bridge-reference-api.md +174 -0
  220. package/dist/serve/site-ui/docs/pyric-cli-register-reference-api/index.html +1 -1
  221. package/dist/serve/site-ui/docs/pyric-cli-remote-reference-api/index.html +1 -1
  222. package/dist/serve/site-ui/docs/pyric-cli-serve-worker-reference-api/index.html +570 -326
  223. package/dist/serve/site-ui/docs/pyric-cli-serve-worker-reference-api.md +198 -6
  224. package/dist/serve/site-ui/docs/pyric-cli-verify-reference-api/index.html +1 -1
  225. package/dist/serve/site-ui/docs/pyric-cli-vite-reference-api/index.html +1 -1
  226. package/dist/serve/site-ui/docs/pyric-database-reference-api/index.html +1 -1
  227. package/dist/serve/site-ui/docs/pyric-firestore-reference-api/index.html +2971 -627
  228. package/dist/serve/site-ui/docs/pyric-firestore-reference-api.md +2873 -517
  229. package/dist/serve/site-ui/docs/pyric-messaging-reference-api/index.html +1 -1
  230. package/dist/serve/site-ui/docs/pyric-messaging-sw-reference-api/index.html +1 -1
  231. package/dist/serve/site-ui/docs/pyric-rules-reference-api/index.html +43 -3
  232. package/dist/serve/site-ui/docs/pyric-rules-reference-api.md +8 -0
  233. package/dist/serve/site-ui/docs/pyric-sandbox-database-reference-api/index.html +26 -2
  234. package/dist/serve/site-ui/docs/pyric-sandbox-database-reference-api.md +24 -0
  235. package/dist/serve/site-ui/docs/pyric-sandbox-firestore-reference-api/index.html +1 -1
  236. package/dist/serve/site-ui/docs/pyric-sandbox-reference-api/index.html +77 -9
  237. package/dist/serve/site-ui/docs/pyric-sandbox-reference-api.md +28 -13
  238. package/dist/serve/site-ui/docs/pyric-storage-reference-api/index.html +240 -63
  239. package/dist/serve/site-ui/docs/pyric-storage-reference-api.md +164 -5
  240. package/dist/serve/site-ui/docs/pyric-ui-agents-reference-api/index.html +1 -1
  241. package/dist/serve/site-ui/docs/pyric-ui-auth-hooks-reference-api/index.html +1 -1
  242. package/dist/serve/site-ui/docs/pyric-ui-auth-reference-api/index.html +1 -1
  243. package/dist/serve/site-ui/docs/pyric-ui-events-hooks-reference-api/index.html +1 -1
  244. package/dist/serve/site-ui/docs/pyric-ui-events-reference-api/index.html +1 -1
  245. package/dist/serve/site-ui/docs/pyric-ui-firestore-hooks-reference-api/index.html +1 -1
  246. package/dist/serve/site-ui/docs/pyric-ui-firestore-reference-api/index.html +1 -1
  247. package/dist/serve/site-ui/docs/pyric-ui-primitives-reference-api/index.html +1 -1
  248. package/dist/serve/site-ui/docs/pyric-ui-rtdb-reference-api/index.html +1 -1
  249. package/dist/serve/site-ui/docs/pyric-ui-rules-hooks-reference-api/index.html +1 -1
  250. package/dist/serve/site-ui/docs/pyric-ui-rules-reference-api/index.html +1 -1
  251. package/dist/serve/site-ui/docs/pyric-ui-storage-hooks-reference-api/index.html +1 -1
  252. package/dist/serve/site-ui/docs/pyric-ui-storage-reference-api/index.html +2 -1
  253. package/dist/serve/site-ui/docs/pyric-ui-storage-reference-api.md +1 -0
  254. package/dist/serve/site-ui/docs/pyric-ui-traffic-hooks-reference-api/index.html +1 -1
  255. package/dist/serve/site-ui/docs/pyric-ui-traffic-reference-api/index.html +1 -1
  256. package/dist/serve/site-ui/docs/rules-compat/index.html +87 -79
  257. package/dist/serve/site-ui/docs/rules-compat.md +86 -78
  258. package/dist/serve/site-ui/docs/secure/audit-your-rules/index.html +1 -1
  259. package/dist/serve/site-ui/docs/secure/firestore-rules-limits/index.html +1 -1
  260. package/dist/serve/site-ui/docs/secure/read-a-denial/index.html +1 -1
  261. package/dist/serve/site-ui/docs/secure/rtdb-rules-in-typescript/index.html +1 -1
  262. package/dist/serve/site-ui/docs/secure/rules-standard-library/index.html +1 -1
  263. package/dist/serve/site-ui/docs/secure/secure-it-with-rules/index.html +1 -1
  264. package/dist/serve/site-ui/docs/secure/simulate-and-lint/index.html +1 -1
  265. package/dist/serve/site-ui/docs/secure/write-a-rules-test-suite/index.html +1 -1
  266. package/dist/serve/site-ui/docs/ship/ship-to-production/index.html +1 -1
  267. package/dist/serve/site-ui/docs/ship/test-in-node/index.html +1 -1
  268. package/dist/serve/site-ui/docs/storage-compat/index.html +119 -125
  269. package/dist/serve/site-ui/docs/storage-compat.md +118 -124
  270. package/dist/serve/site-ui/docs/trust/how-we-know-it-matches-firebase/index.html +1 -1
  271. package/dist/serve/site-ui/docs/trust/rules-standard-library/index.html +1 -1
  272. package/dist/serve/site-ui/docs/trust/storage-rules-stdlib-research/index.html +1 -1
  273. package/dist/serve/site-ui/docs/trust/versioning-and-compatibility/index.html +1 -1
  274. package/dist/serve/site-ui/docs/tutorial/index.html +1 -1
  275. package/dist/serve/site-ui/examples/chess/index.html +1 -1
  276. package/dist/serve/site-ui/examples/firestore-first-write/index.html +1 -1
  277. package/dist/serve/site-ui/firestore/index.html +2 -2
  278. package/dist/serve/site-ui/index.html +2 -2
  279. package/dist/serve/site-ui/llms.txt +6 -2
  280. package/dist/serve/site-ui/rtdb/index.html +2 -2
  281. package/dist/serve/site-ui/settings/index.html +2 -2
  282. package/dist/serve/site-ui/storage/index.html +2 -2
  283. package/dist/serve/site-ui/traffic/index.html +2 -2
  284. package/dist/serve/vite-ai-config.d.ts +4 -1
  285. package/dist/serve/vite-ai-config.d.ts.map +1 -1
  286. package/dist/serve/vite-ai-config.js +83 -19
  287. package/dist/serve/vite-ai-config.js.map +1 -1
  288. package/dist/serve/vite-generation-rules-watch.d.ts.map +1 -1
  289. package/dist/serve/vite-generation-rules-watch.js +61 -14
  290. package/dist/serve/vite-generation-rules-watch.js.map +1 -1
  291. package/dist/serve/vite-module-swap.d.ts +4 -1
  292. package/dist/serve/vite-module-swap.d.ts.map +1 -1
  293. package/dist/serve/vite-module-swap.js +106 -21
  294. package/dist/serve/vite-module-swap.js.map +1 -1
  295. package/dist/serve/vite-plugin.d.ts.map +1 -1
  296. package/dist/serve/vite-plugin.js +45 -5
  297. package/dist/serve/vite-plugin.js.map +1 -1
  298. package/dist/serve/vite-sandbox-generation.js +1 -1
  299. package/dist/serve/vite-sandbox-generation.js.map +1 -1
  300. package/dist/serve/worker/client/resumable.d.ts +30 -0
  301. package/dist/serve/worker/client/resumable.d.ts.map +1 -0
  302. package/dist/serve/worker/client/resumable.js +416 -0
  303. package/dist/serve/worker/client/resumable.js.map +1 -0
  304. package/dist/serve/worker/client/rtdb-references.d.ts.map +1 -1
  305. package/dist/serve/worker/client/rtdb-references.js +4 -1
  306. package/dist/serve/worker/client/rtdb-references.js.map +1 -1
  307. package/dist/serve/worker/client/storage.d.ts +16 -2
  308. package/dist/serve/worker/client/storage.d.ts.map +1 -1
  309. package/dist/serve/worker/client/storage.js +57 -6
  310. package/dist/serve/worker/client/storage.js.map +1 -1
  311. package/dist/serve/worker/host/rules.d.ts.map +1 -1
  312. package/dist/serve/worker/host/rules.js +2 -1
  313. package/dist/serve/worker/host/rules.js.map +1 -1
  314. package/dist/serve/worker/index.d.ts +1 -1
  315. package/dist/serve/worker/index.d.ts.map +1 -1
  316. package/dist/serve/worker/index.js +1 -1
  317. package/dist/serve/worker/index.js.map +1 -1
  318. package/dist/serve/worker/serve-init.d.ts.map +1 -1
  319. package/dist/serve/worker/serve-init.js +31 -0
  320. package/dist/serve/worker/serve-init.js.map +1 -1
  321. package/package.json +15 -4
  322. package/src/assurance/.generated/conformance-verdicts.ts +80 -72
  323. package/src/cli/cli.test.ts +14 -0
  324. package/src/cli/database-rules.ts +6 -4
  325. package/src/cli/dev-runner.ts +21 -0
  326. package/src/cli/serve.ts +62 -5
  327. package/src/cli/verify.ts +2 -2
  328. package/src/conformance/.generated/can-i-use-browser.ts +1 -1
  329. package/src/conformance/.generated/can-i-use.ts +1 -1
  330. package/src/conformance/.generated/conformance-docs.ts +1 -1
  331. package/src/next/client-aliases.ts +39 -0
  332. package/src/next/dev-rewrites.ts +77 -0
  333. package/src/next/guard.ts +61 -0
  334. package/src/next/index.ts +16 -0
  335. package/src/next/runtime-env.ts +18 -0
  336. package/src/next/server-external-packages.ts +43 -0
  337. package/src/next/target-url.ts +46 -0
  338. package/src/next/turbopack-config.ts +54 -0
  339. package/src/next/types.ts +47 -0
  340. package/src/next/webpack-config.ts +71 -0
  341. package/src/next/with-pyric.ts +58 -0
  342. package/src/register/app-bridge.ts +79 -0
  343. package/src/register/hooks.ts +6 -1
  344. package/src/register/index.ts +1 -1
  345. package/src/register/mapping.ts +50 -4
  346. package/src/rtdb/rules-json.ts +110 -0
  347. package/src/serve/bundler.ts +2 -0
  348. package/src/serve/discovery.ts +65 -44
  349. package/src/serve/entries/app-ai-passthrough.ts +83 -0
  350. package/src/serve/entries/auth-helper-core.ts +9 -7
  351. package/src/serve/entries/auth.ts +1 -0
  352. package/src/serve/entries/bridge-url.ts +9 -2
  353. package/src/serve/entries/database.ts +1 -0
  354. package/src/serve/entries/firestore.ts +1 -0
  355. package/src/serve/entries/init.ts +5 -2
  356. package/src/serve/entries/runtime.ts +25 -0
  357. package/src/serve/entries/storage.ts +12 -1
  358. package/src/serve/html-injection.ts +26 -1
  359. package/src/serve/rules.ts +2 -2
  360. package/src/serve/runtime/chip-config.ts +19 -6
  361. package/src/serve/sandbox-session.ts +31 -2
  362. package/src/serve/vite-ai-config.ts +86 -19
  363. package/src/serve/vite-generation-rules-watch.ts +67 -15
  364. package/src/serve/vite-module-swap.ts +121 -21
  365. package/src/serve/vite-plugin.ts +44 -6
  366. package/src/serve/vite-sandbox-generation.ts +1 -1
  367. package/src/serve/worker/client/resumable.ts +506 -0
  368. package/src/serve/worker/client/rtdb-references.ts +4 -1
  369. package/src/serve/worker/client/storage.ts +77 -7
  370. package/src/serve/worker/host/rules.ts +2 -1
  371. package/src/serve/worker/index.ts +4 -0
  372. package/src/serve/worker/serve-init.ts +30 -0
  373. package/dist/serve/site-ui/_astro/_studio_.BtYpqe1A.css +0 -1
  374. package/dist/serve/site-ui/_astro/app.4HeLP_FA.js +0 -109
  375. package/dist/serve/site-ui/_astro/app.BgSnCR5G.js +0 -1
  376. package/dist/serve/site-ui/_astro/index.BJiOemMX.js +0 -1
  377. package/dist/serve/site-ui/_astro/reads.BrKNdKAa.js +0 -1
  378. package/dist/serve/site-ui/_astro/sandbox-controls.BTb3EbQp.js +0 -1153
@@ -1,5 +1,5 @@
1
1
  <!DOCTYPE html><html lang="en"> <head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>pyric/database compatibility matrix · Pyric Studio</title><link rel="icon" type="image/svg+xml" href="/__pyric/ui/pyric-logo.svg"><link rel="preconnect" href="https://fonts.googleapis.com"><link rel="preconnect" href="https://fonts.gstatic.com" crossorigin><link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=JetBrains+Mono:wght@400;500;700&display=swap" rel="stylesheet"><link rel="stylesheet" href="/__pyric/ui/_astro/_slug_.CXh9yTP1.css">
2
- <link rel="stylesheet" href="/__pyric/ui/_astro/_slug_.BoKXYrnu.css"></head> <body> <header class="studio__bar" data-astro-cid-mw7aashj> <div class="studio__bar-inner" data-astro-cid-mw7aashj> <a class="studio__brand" href="/__pyric/ui/" aria-label="Pyric Studio" data-astro-cid-mw7aashj>Pyric</a> <nav class="studio__nav" aria-label="Studio tabs" data-astro-cid-mw7aashj> <a class="studio__nav-tab" href="/__pyric/ui/" data-astro-cid-mw7aashj> Home </a><a class="studio__nav-tab" href="/__pyric/ui/firestore" data-astro-cid-mw7aashj> Firestore </a><a class="studio__nav-tab" href="/__pyric/ui/auth" data-astro-cid-mw7aashj> Auth </a><a class="studio__nav-tab" href="/__pyric/ui/rtdb" data-astro-cid-mw7aashj> RTDB </a><a class="studio__nav-tab" href="/__pyric/ui/storage" data-astro-cid-mw7aashj> Storage </a><a class="studio__nav-tab" href="/__pyric/ui/traffic" data-astro-cid-mw7aashj> Traffic </a><a class="studio__nav-tab" href="/__pyric/ui/settings" data-astro-cid-mw7aashj> Settings </a><a class="studio__nav-tab" href="/__pyric/ui/docs" aria-current="page" data-astro-cid-mw7aashj> Docs </a> </nav> </div> </header> <div class="docs-shell" data-astro-cid-mw7aashj> <details class="docs-nav-drawer" data-astro-cid-mw7aashj> <summary data-astro-cid-mw7aashj>Navigate</summary> <nav class="docs-nav" aria-label="Documentation"> <div class="docs-search" data-index-url="/__pyric/ui/docs/index.json"> <div class="docs-search-field"> <svg class="docs-search-icon" viewBox="0 0 16 16" width="14" height="14" aria-hidden="true"> <path d="M11.5 11.5 15 15M13 7A6 6 0 1 1 1 7a6 6 0 0 1 12 0Z" fill="none" stroke="currentColor" stroke-width="1.4" stroke-linecap="round"></path> </svg> <input type="text" class="docs-search-input" placeholder="Search docs" aria-label="Search docs" autocomplete="off" spellcheck="false"> <kbd class="docs-search-kbd">⌘K</kbd> </div> <ul class="docs-search-results" hidden></ul> </div> <script type="module">let i=null;function d(t){return i||(i=fetch(t).then(e=>e.json())),i}function l(t,e){return t.title.toLowerCase().includes(e)||t.excerpt.toLowerCase().includes(e)?!0:t.headings.some(r=>r.text.toLowerCase().includes(e))}function u(t,e,r){if(e.innerHTML="",t.length===0){e.hidden=!1;{const n=document.createElement("li");n.className="docs-search-empty",n.textContent="No matches",e.appendChild(n)}return}e.hidden=!1;for(const n of t.slice(0,8)){const s=document.createElement("li"),c=document.createElement("a");c.href=n.path;const o=document.createElement("span");o.className="docs-search-result-title",o.textContent=n.title;const a=document.createElement("span");a.className="docs-search-result-meta",a.textContent=[n.group,n.section].filter(Boolean).join(" / "),c.append(o,a),s.appendChild(c),e.appendChild(s)}}document.querySelectorAll(".docs-search").forEach(t=>{const e=t.dataset.indexUrl,r=t.querySelector(".docs-search-input"),n=t.querySelector(".docs-search-results");r.addEventListener("input",async()=>{const s=r.value.trim().toLowerCase();if(!s){n.hidden=!0,n.innerHTML="";return}const{pages:c}=await d(e);r.value.trim().toLowerCase()===s&&u(c.filter(o=>l(o,s)),n)}),r.addEventListener("focus",()=>{d(e),n.childElementCount>0&&(n.hidden=!1)}),r.addEventListener("keydown",s=>{s.key==="Escape"&&(r.value="",n.hidden=!0,n.innerHTML="",r.blur())}),r.addEventListener("blur",()=>{window.setTimeout(()=>{n.hidden=!0},150)})});document.addEventListener("keydown",t=>{if(!(t.metaKey||t.ctrlKey)||t.key.toLowerCase()!=="k")return;const e=[...document.querySelectorAll(".docs-search-input")].find(r=>r.offsetParent!==null);e&&(t.preventDefault(),e.focus(),e.select())});</script> <details class="docs-nav-group"> <summary>Overview</summary> <div class="docs-nav-group-body"> <div class="docs-nav-section"> <ul> <li> <a href="/__pyric/ui/docs/overview/"> Overview </a> </li><li> <a href="/__pyric/ui/docs/tutorial/"> Tutorial </a> </li><li> <a href="/__pyric/ui/docs/examples/"> Examples </a> </li> </ul> </div> </div> </details><details class="docs-nav-group"> <summary>Get started</summary> <div class="docs-nav-group-body"> <div class="docs-nav-section"> <ul> <li> <a href="/__pyric/ui/docs/get-started/start-building/"> Quickstart </a> </li><li> <a href="/__pyric/ui/docs/get-started/how-the-swap-works/"> How the swap works </a> </li> </ul> </div> </div> </details><details class="docs-nav-group"> <summary>Build</summary> <div class="docs-nav-group-body"> <div class="docs-nav-section"> <ul> <li> <a href="/__pyric/ui/docs/build/authentication/"> Authentication </a> </li><li> <a href="/__pyric/ui/docs/build/cloud-firestore/"> Cloud Firestore </a> </li><li> <a href="/__pyric/ui/docs/build/realtime-database/"> Realtime Database </a> </li><li> <a href="/__pyric/ui/docs/build/cloud-storage/"> Cloud Storage </a> </li><li> <a href="/__pyric/ui/docs/build/cloud-messaging/"> Cloud Messaging </a> </li><li> <a href="/__pyric/ui/docs/build/ai-logic/"> AI Logic </a> </li> </ul> </div> </div> </details><details class="docs-nav-group"> <summary>Secure &amp; debug</summary> <div class="docs-nav-group-body"> <div class="docs-nav-section"> <ul> <li> <a href="/__pyric/ui/docs/secure/secure-it-with-rules/"> Security Rules </a> </li><li> <a href="/__pyric/ui/docs/secure/simulate-and-lint/"> Simulate and lint before you deploy </a> </li><li> <a href="/__pyric/ui/docs/secure/write-a-rules-test-suite/"> Write a rules test suite </a> </li><li> <a href="/__pyric/ui/docs/secure/read-a-denial/"> Read a denial and understand it </a> </li><li> <a href="/__pyric/ui/docs/secure/rules-standard-library/"> The rules standard library </a> </li><li> <a href="/__pyric/ui/docs/secure/rtdb-rules-in-typescript/"> RTDB rules in TypeScript </a> </li><li> <a href="/__pyric/ui/docs/secure/firestore-rules-limits/"> Rules limits </a> </li><li> <a href="/__pyric/ui/docs/secure/audit-your-rules/"> Audit rules and data </a> </li> </ul> </div><div class="docs-nav-section"> <h2 class="docs-nav-heading"> Examples </h2> <ul> <li> <a href="/__pyric/ui/docs/examples/chess/"> Chess showcase </a> </li> </ul> </div> </div> </details><details class="docs-nav-group"> <summary>Observe &amp; shape</summary> <div class="docs-nav-group-body"> <div class="docs-nav-section"> <ul> <li> <a href="/__pyric/ui/docs/observe/see-whats-happening/"> Traffic &amp; rule verdicts </a> </li><li> <a href="/__pyric/ui/docs/observe/resolve-runtime-status/"> Runtime status </a> </li><li> <a href="/__pyric/ui/docs/observe/shape-your-data/"> Seed, snapshot, replay </a> </li> </ul> </div> </div> </details><details class="docs-nav-group"> <summary>Ship &amp; test</summary> <div class="docs-nav-group-body"> <div class="docs-nav-section"> <ul> <li> <a href="/__pyric/ui/docs/ship/ship-to-production/"> Ship to production </a> </li><li> <a href="/__pyric/ui/docs/ship/test-in-node/"> Test in Node </a> </li> </ul> </div> </div> </details><details class="docs-nav-group"> <summary>Work with an agent</summary> <div class="docs-nav-group-body"> <div class="docs-nav-section"> <ul> <li> <a href="/__pyric/ui/docs/agent/set-up-your-agent/"> Connect an agent </a> </li><li> <a href="/__pyric/ui/docs/agent/work-with-an-agent/"> Use the MCP tools </a> </li><li> <a href="/__pyric/ui/docs/agent/watch-and-review/"> Watch in Pyric Studio </a> </li> </ul> </div> </div> </details><details class="docs-nav-group"> <summary>Trust</summary> <div class="docs-nav-group-body"> <div class="docs-nav-section"> <ul> <li> <a href="/__pyric/ui/docs/trust/versioning-and-compatibility/"> Versioning and the fb tag </a> </li><li> <a href="/__pyric/ui/docs/trust/storage-rules-stdlib-research/"> Storage Rules modules: evidence and design </a> </li><li> <a href="/__pyric/ui/docs/trust/rules-standard-library/"> Rules standard library reference </a> </li> </ul> </div> </div> </details><details class="docs-nav-group" open> <summary>Conformance</summary> <div class="docs-nav-group-body"> <div class="docs-nav-section"> <ul> <li> <a href="/__pyric/ui/docs/conformance-scores/"> Public API coverage </a> </li><li> <a href="/__pyric/ui/docs/trust/how-we-know-it-matches-firebase/"> How we know it matches Firebase </a> </li><li> <a href="/__pyric/ui/docs/app-compat/"> App </a> </li><li> <a href="/__pyric/ui/docs/ai-compat/"> AI Logic </a> </li><li> <a href="/__pyric/ui/docs/auth-compat/"> Auth </a> </li><li> <a href="/__pyric/ui/docs/firestore-compat/"> Firestore </a> </li><li> <a href="/__pyric/ui/docs/database-compat/" aria-current="page"> Realtime Database </a> </li><li> <a href="/__pyric/ui/docs/storage-compat/"> Storage </a> </li><li> <a href="/__pyric/ui/docs/messaging-compat/"> Messaging </a> </li><li> <a href="/__pyric/ui/docs/functions-rtdb-compat/"> Functions · RTDB </a> </li><li> <a href="/__pyric/ui/docs/rules-compat/"> Rules </a> </li> </ul> </div> </div> </details> <details class="docs-nav-group"> <summary>Reference</summary> <div class="docs-nav-group-body docs-nav-group-body--reference"> <div class="docs-nav-section"> <ul> <li> <a href="/__pyric/ui/docs/api-reference/"> API reference </a> </li> </ul> </div><div class="docs-nav-section"> <h2 class="docs-nav-heading">pyric</h2> <ul> <li> <a href="/__pyric/ui/docs/pyric-app-reference-api/"> pyric/app </a> </li><li> <a href="/__pyric/ui/docs/pyric-firestore-reference-api/"> pyric/firestore </a> </li><li> <a href="/__pyric/ui/docs/pyric-auth-reference-api/"> pyric/auth </a> </li><li> <a href="/__pyric/ui/docs/pyric-ai-reference-api/"> pyric/ai </a> </li><li> <a href="/__pyric/ui/docs/pyric-ai-scripting-reference-api/"> pyric/ai/scripting </a> </li><li> <a href="/__pyric/ui/docs/pyric-database-reference-api/"> pyric/database </a> </li><li> <a href="/__pyric/ui/docs/pyric-storage-reference-api/"> pyric/storage </a> </li><li> <a href="/__pyric/ui/docs/pyric-messaging-reference-api/"> pyric/messaging </a> </li><li> <a href="/__pyric/ui/docs/pyric-messaging-sw-reference-api/"> pyric/messaging/sw </a> </li><li> <a href="/__pyric/ui/docs/pyric-rules-reference-api/"> pyric/rules </a> </li><li> <a href="/__pyric/ui/docs/pyric-sandbox-reference-api/"> pyric/sandbox </a> </li><li> <a href="/__pyric/ui/docs/pyric-sandbox-firestore-reference-api/"> pyric/sandbox/firestore </a> </li><li> <a href="/__pyric/ui/docs/pyric-sandbox-database-reference-api/"> pyric/sandbox/database </a> </li> </ul> </div><div class="docs-nav-section"> <h2 class="docs-nav-heading">pyric-admin</h2> <ul> <li> <a href="/__pyric/ui/docs/pyric-admin-app-reference-api/"> pyric-admin/app </a> </li><li> <a href="/__pyric/ui/docs/pyric-admin-firestore-reference-api/"> pyric-admin/firestore </a> </li><li> <a href="/__pyric/ui/docs/pyric-admin-auth-reference-api/"> pyric-admin/auth </a> </li><li> <a href="/__pyric/ui/docs/pyric-admin-database-reference-api/"> pyric-admin/database </a> </li><li> <a href="/__pyric/ui/docs/pyric-admin-storage-reference-api/"> pyric-admin/storage </a> </li><li> <a href="/__pyric/ui/docs/pyric-admin-messaging-reference-api/"> pyric-admin/messaging </a> </li> </ul> </div><div class="docs-nav-section"> <h2 class="docs-nav-heading">create-pyric</h2> <ul> <li> <a href="/__pyric/ui/docs/create-pyric-reference-api/"> create-pyric </a> </li> </ul> </div><div class="docs-nav-section"> <h2 class="docs-nav-heading">@pyric/cli</h2> <ul> <li> <a href="/__pyric/ui/docs/pyric-cli-credentials-node-reference-api/"> @pyric/cli/credentials/node </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-verify-reference-api/"> @pyric/cli/verify </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-conformance-reference-api/"> @pyric/cli/conformance </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-conformance-browser-reference-api/"> @pyric/cli/conformance/browser </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-conformance-docs-reference-api/"> @pyric/cli/conformance/docs </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-assurance-reference-api/"> @pyric/cli/assurance </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-assurance-browser-reference-api/"> @pyric/cli/assurance/browser </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-bridge-reference-api/"> @pyric/cli/bridge </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-bridge-client-reference-api/"> @pyric/cli/bridge/client </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-discover-reference-api/"> @pyric/cli/discover </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-vite-reference-api/"> @pyric/cli/vite </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-serve-worker-reference-api/"> @pyric/cli/serve/worker </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-remote-reference-api/"> @pyric/cli/remote </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-register-reference-api/"> @pyric/cli/register </a> </li> </ul> </div><div class="docs-nav-section"> <h2 class="docs-nav-heading">@pyric/ui</h2> <ul> <li> <a href="/__pyric/ui/docs/pyric-ui-auth-reference-api/"> @pyric/ui/auth </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-auth-hooks-reference-api/"> @pyric/ui/auth/hooks </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-primitives-reference-api/"> @pyric/ui/primitives </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-firestore-reference-api/"> @pyric/ui/firestore </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-firestore-hooks-reference-api/"> @pyric/ui/firestore/hooks </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-rtdb-reference-api/"> @pyric/ui/rtdb </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-storage-reference-api/"> @pyric/ui/storage </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-storage-hooks-reference-api/"> @pyric/ui/storage/hooks </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-traffic-reference-api/"> @pyric/ui/traffic </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-traffic-hooks-reference-api/"> @pyric/ui/traffic/hooks </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-events-reference-api/"> @pyric/ui/events </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-events-hooks-reference-api/"> @pyric/ui/events/hooks </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-agents-reference-api/"> @pyric/ui/agents </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-rules-reference-api/"> @pyric/ui/rules </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-rules-hooks-reference-api/"> @pyric/ui/rules/hooks </a> </li> </ul> </div> </div> </details> </nav> </details> <nav class="docs-nav" aria-label="Documentation"> <div class="docs-search" data-index-url="/__pyric/ui/docs/index.json"> <div class="docs-search-field"> <svg class="docs-search-icon" viewBox="0 0 16 16" width="14" height="14" aria-hidden="true"> <path d="M11.5 11.5 15 15M13 7A6 6 0 1 1 1 7a6 6 0 0 1 12 0Z" fill="none" stroke="currentColor" stroke-width="1.4" stroke-linecap="round"></path> </svg> <input type="text" class="docs-search-input" placeholder="Search docs" aria-label="Search docs" autocomplete="off" spellcheck="false"> <kbd class="docs-search-kbd">⌘K</kbd> </div> <ul class="docs-search-results" hidden></ul> </div> <details class="docs-nav-group" id="nav-group-overview"> <summary>Overview</summary> <div class="docs-nav-group-body"> <div class="docs-nav-section"> <ul> <li> <a href="/__pyric/ui/docs/overview/"> Overview </a> </li><li> <a href="/__pyric/ui/docs/tutorial/"> Tutorial </a> </li><li> <a href="/__pyric/ui/docs/examples/"> Examples </a> </li> </ul> </div> </div> </details><details class="docs-nav-group" id="nav-group-get-started"> <summary>Get started</summary> <div class="docs-nav-group-body"> <div class="docs-nav-section"> <ul> <li> <a href="/__pyric/ui/docs/get-started/start-building/"> Quickstart </a> </li><li> <a href="/__pyric/ui/docs/get-started/how-the-swap-works/"> How the swap works </a> </li> </ul> </div> </div> </details><details class="docs-nav-group" id="nav-group-build"> <summary>Build</summary> <div class="docs-nav-group-body"> <div class="docs-nav-section"> <ul> <li> <a href="/__pyric/ui/docs/build/authentication/"> Authentication </a> </li><li> <a href="/__pyric/ui/docs/build/cloud-firestore/"> Cloud Firestore </a> </li><li> <a href="/__pyric/ui/docs/build/realtime-database/"> Realtime Database </a> </li><li> <a href="/__pyric/ui/docs/build/cloud-storage/"> Cloud Storage </a> </li><li> <a href="/__pyric/ui/docs/build/cloud-messaging/"> Cloud Messaging </a> </li><li> <a href="/__pyric/ui/docs/build/ai-logic/"> AI Logic </a> </li> </ul> </div> </div> </details><details class="docs-nav-group" id="nav-group-secure-debug"> <summary>Secure &amp; debug</summary> <div class="docs-nav-group-body"> <div class="docs-nav-section"> <ul> <li> <a href="/__pyric/ui/docs/secure/secure-it-with-rules/"> Security Rules </a> </li><li> <a href="/__pyric/ui/docs/secure/simulate-and-lint/"> Simulate and lint before you deploy </a> </li><li> <a href="/__pyric/ui/docs/secure/write-a-rules-test-suite/"> Write a rules test suite </a> </li><li> <a href="/__pyric/ui/docs/secure/read-a-denial/"> Read a denial and understand it </a> </li><li> <a href="/__pyric/ui/docs/secure/rules-standard-library/"> The rules standard library </a> </li><li> <a href="/__pyric/ui/docs/secure/rtdb-rules-in-typescript/"> RTDB rules in TypeScript </a> </li><li> <a href="/__pyric/ui/docs/secure/firestore-rules-limits/"> Rules limits </a> </li><li> <a href="/__pyric/ui/docs/secure/audit-your-rules/"> Audit rules and data </a> </li> </ul> </div><div class="docs-nav-section"> <h2 class="docs-nav-heading" id="nav-section-secure-debug-examples"> Examples </h2> <ul> <li> <a href="/__pyric/ui/docs/examples/chess/"> Chess showcase </a> </li> </ul> </div> </div> </details><details class="docs-nav-group" id="nav-group-observe-shape"> <summary>Observe &amp; shape</summary> <div class="docs-nav-group-body"> <div class="docs-nav-section"> <ul> <li> <a href="/__pyric/ui/docs/observe/see-whats-happening/"> Traffic &amp; rule verdicts </a> </li><li> <a href="/__pyric/ui/docs/observe/resolve-runtime-status/"> Runtime status </a> </li><li> <a href="/__pyric/ui/docs/observe/shape-your-data/"> Seed, snapshot, replay </a> </li> </ul> </div> </div> </details><details class="docs-nav-group" id="nav-group-ship-test"> <summary>Ship &amp; test</summary> <div class="docs-nav-group-body"> <div class="docs-nav-section"> <ul> <li> <a href="/__pyric/ui/docs/ship/ship-to-production/"> Ship to production </a> </li><li> <a href="/__pyric/ui/docs/ship/test-in-node/"> Test in Node </a> </li> </ul> </div> </div> </details><details class="docs-nav-group" id="nav-group-work-with-an-agent"> <summary>Work with an agent</summary> <div class="docs-nav-group-body"> <div class="docs-nav-section"> <ul> <li> <a href="/__pyric/ui/docs/agent/set-up-your-agent/"> Connect an agent </a> </li><li> <a href="/__pyric/ui/docs/agent/work-with-an-agent/"> Use the MCP tools </a> </li><li> <a href="/__pyric/ui/docs/agent/watch-and-review/"> Watch in Pyric Studio </a> </li> </ul> </div> </div> </details><details class="docs-nav-group" id="nav-group-trust"> <summary>Trust</summary> <div class="docs-nav-group-body"> <div class="docs-nav-section"> <ul> <li> <a href="/__pyric/ui/docs/trust/versioning-and-compatibility/"> Versioning and the fb tag </a> </li><li> <a href="/__pyric/ui/docs/trust/storage-rules-stdlib-research/"> Storage Rules modules: evidence and design </a> </li><li> <a href="/__pyric/ui/docs/trust/rules-standard-library/"> Rules standard library reference </a> </li> </ul> </div> </div> </details><details class="docs-nav-group" id="nav-group-conformance" open> <summary>Conformance</summary> <div class="docs-nav-group-body"> <div class="docs-nav-section"> <ul> <li> <a href="/__pyric/ui/docs/conformance-scores/"> Public API coverage </a> </li><li> <a href="/__pyric/ui/docs/trust/how-we-know-it-matches-firebase/"> How we know it matches Firebase </a> </li><li> <a href="/__pyric/ui/docs/app-compat/"> App </a> </li><li> <a href="/__pyric/ui/docs/ai-compat/"> AI Logic </a> </li><li> <a href="/__pyric/ui/docs/auth-compat/"> Auth </a> </li><li> <a href="/__pyric/ui/docs/firestore-compat/"> Firestore </a> </li><li> <a href="/__pyric/ui/docs/database-compat/" aria-current="page"> Realtime Database </a> </li><li> <a href="/__pyric/ui/docs/storage-compat/"> Storage </a> </li><li> <a href="/__pyric/ui/docs/messaging-compat/"> Messaging </a> </li><li> <a href="/__pyric/ui/docs/functions-rtdb-compat/"> Functions · RTDB </a> </li><li> <a href="/__pyric/ui/docs/rules-compat/"> Rules </a> </li> </ul> </div> </div> </details> <details class="docs-nav-group" id="nav-group-reference"> <summary>Reference</summary> <div class="docs-nav-group-body docs-nav-group-body--reference"> <div class="docs-nav-section"> <ul> <li> <a href="/__pyric/ui/docs/api-reference/"> API reference </a> </li> </ul> </div><div class="docs-nav-section"> <h2 class="docs-nav-heading">pyric</h2> <ul> <li> <a href="/__pyric/ui/docs/pyric-app-reference-api/"> pyric/app </a> </li><li> <a href="/__pyric/ui/docs/pyric-firestore-reference-api/"> pyric/firestore </a> </li><li> <a href="/__pyric/ui/docs/pyric-auth-reference-api/"> pyric/auth </a> </li><li> <a href="/__pyric/ui/docs/pyric-ai-reference-api/"> pyric/ai </a> </li><li> <a href="/__pyric/ui/docs/pyric-ai-scripting-reference-api/"> pyric/ai/scripting </a> </li><li> <a href="/__pyric/ui/docs/pyric-database-reference-api/"> pyric/database </a> </li><li> <a href="/__pyric/ui/docs/pyric-storage-reference-api/"> pyric/storage </a> </li><li> <a href="/__pyric/ui/docs/pyric-messaging-reference-api/"> pyric/messaging </a> </li><li> <a href="/__pyric/ui/docs/pyric-messaging-sw-reference-api/"> pyric/messaging/sw </a> </li><li> <a href="/__pyric/ui/docs/pyric-rules-reference-api/"> pyric/rules </a> </li><li> <a href="/__pyric/ui/docs/pyric-sandbox-reference-api/"> pyric/sandbox </a> </li><li> <a href="/__pyric/ui/docs/pyric-sandbox-firestore-reference-api/"> pyric/sandbox/firestore </a> </li><li> <a href="/__pyric/ui/docs/pyric-sandbox-database-reference-api/"> pyric/sandbox/database </a> </li> </ul> </div><div class="docs-nav-section"> <h2 class="docs-nav-heading">pyric-admin</h2> <ul> <li> <a href="/__pyric/ui/docs/pyric-admin-app-reference-api/"> pyric-admin/app </a> </li><li> <a href="/__pyric/ui/docs/pyric-admin-firestore-reference-api/"> pyric-admin/firestore </a> </li><li> <a href="/__pyric/ui/docs/pyric-admin-auth-reference-api/"> pyric-admin/auth </a> </li><li> <a href="/__pyric/ui/docs/pyric-admin-database-reference-api/"> pyric-admin/database </a> </li><li> <a href="/__pyric/ui/docs/pyric-admin-storage-reference-api/"> pyric-admin/storage </a> </li><li> <a href="/__pyric/ui/docs/pyric-admin-messaging-reference-api/"> pyric-admin/messaging </a> </li> </ul> </div><div class="docs-nav-section"> <h2 class="docs-nav-heading">create-pyric</h2> <ul> <li> <a href="/__pyric/ui/docs/create-pyric-reference-api/"> create-pyric </a> </li> </ul> </div><div class="docs-nav-section"> <h2 class="docs-nav-heading">@pyric/cli</h2> <ul> <li> <a href="/__pyric/ui/docs/pyric-cli-credentials-node-reference-api/"> @pyric/cli/credentials/node </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-verify-reference-api/"> @pyric/cli/verify </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-conformance-reference-api/"> @pyric/cli/conformance </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-conformance-browser-reference-api/"> @pyric/cli/conformance/browser </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-conformance-docs-reference-api/"> @pyric/cli/conformance/docs </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-assurance-reference-api/"> @pyric/cli/assurance </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-assurance-browser-reference-api/"> @pyric/cli/assurance/browser </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-bridge-reference-api/"> @pyric/cli/bridge </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-bridge-client-reference-api/"> @pyric/cli/bridge/client </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-discover-reference-api/"> @pyric/cli/discover </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-vite-reference-api/"> @pyric/cli/vite </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-serve-worker-reference-api/"> @pyric/cli/serve/worker </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-remote-reference-api/"> @pyric/cli/remote </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-register-reference-api/"> @pyric/cli/register </a> </li> </ul> </div><div class="docs-nav-section"> <h2 class="docs-nav-heading">@pyric/ui</h2> <ul> <li> <a href="/__pyric/ui/docs/pyric-ui-auth-reference-api/"> @pyric/ui/auth </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-auth-hooks-reference-api/"> @pyric/ui/auth/hooks </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-primitives-reference-api/"> @pyric/ui/primitives </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-firestore-reference-api/"> @pyric/ui/firestore </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-firestore-hooks-reference-api/"> @pyric/ui/firestore/hooks </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-rtdb-reference-api/"> @pyric/ui/rtdb </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-storage-reference-api/"> @pyric/ui/storage </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-storage-hooks-reference-api/"> @pyric/ui/storage/hooks </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-traffic-reference-api/"> @pyric/ui/traffic </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-traffic-hooks-reference-api/"> @pyric/ui/traffic/hooks </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-events-reference-api/"> @pyric/ui/events </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-events-hooks-reference-api/"> @pyric/ui/events/hooks </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-agents-reference-api/"> @pyric/ui/agents </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-rules-reference-api/"> @pyric/ui/rules </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-rules-hooks-reference-api/"> @pyric/ui/rules/hooks </a> </li> </ul> </div> </div> </details> </nav> <main class="doc-main" data-astro-cid-mw7aashj> <nav class="docs-breadcrumbs" aria-label="Breadcrumb"> <ol> <li> <a href="/__pyric/ui/docs/conformance-scores/">Conformance</a> </li><li> <span class="docs-breadcrumbs-sep" aria-hidden="true">/</span> <span aria-current="page">Realtime Database</span> </li> </ol> </nav> <article class="prose" data-astro-cid-mw7aashj> <!-- Generated from the conformance model (registry rows + surface contracts). Do not edit by hand; run bun run compat:generate. -->
2
+ <link rel="stylesheet" href="/__pyric/ui/_astro/_slug_.BoKXYrnu.css"></head> <body> <header class="studio__bar" data-astro-cid-mw7aashj> <div class="studio__bar-inner" data-astro-cid-mw7aashj> <a class="studio__brand" href="/__pyric/ui/" aria-label="Pyric Studio" data-astro-cid-mw7aashj>Pyric</a> <nav class="studio__nav" aria-label="Studio tabs" data-astro-cid-mw7aashj> <a class="studio__nav-tab" href="/__pyric/ui/" data-astro-cid-mw7aashj> Home </a><a class="studio__nav-tab" href="/__pyric/ui/firestore" data-astro-cid-mw7aashj> Firestore </a><a class="studio__nav-tab" href="/__pyric/ui/auth" data-astro-cid-mw7aashj> Auth </a><a class="studio__nav-tab" href="/__pyric/ui/rtdb" data-astro-cid-mw7aashj> RTDB </a><a class="studio__nav-tab" href="/__pyric/ui/storage" data-astro-cid-mw7aashj> Storage </a><a class="studio__nav-tab" href="/__pyric/ui/traffic" data-astro-cid-mw7aashj> Traffic </a><a class="studio__nav-tab" href="/__pyric/ui/settings" data-astro-cid-mw7aashj> Settings </a><a class="studio__nav-tab" href="/__pyric/ui/docs" aria-current="page" data-astro-cid-mw7aashj> Docs </a> </nav> </div> </header> <div class="docs-shell" data-astro-cid-mw7aashj> <details class="docs-nav-drawer" data-astro-cid-mw7aashj> <summary data-astro-cid-mw7aashj>Navigate</summary> <nav class="docs-nav" aria-label="Documentation"> <div class="docs-search" data-index-url="/__pyric/ui/docs/index.json"> <div class="docs-search-field"> <svg class="docs-search-icon" viewBox="0 0 16 16" width="14" height="14" aria-hidden="true"> <path d="M11.5 11.5 15 15M13 7A6 6 0 1 1 1 7a6 6 0 0 1 12 0Z" fill="none" stroke="currentColor" stroke-width="1.4" stroke-linecap="round"></path> </svg> <input type="text" class="docs-search-input" placeholder="Search docs" aria-label="Search docs" autocomplete="off" spellcheck="false"> <kbd class="docs-search-kbd">⌘K</kbd> </div> <ul class="docs-search-results" hidden></ul> </div> <script type="module">let i=null;function d(t){return i||(i=fetch(t).then(e=>e.json())),i}function l(t,e){return t.title.toLowerCase().includes(e)||t.excerpt.toLowerCase().includes(e)?!0:t.headings.some(r=>r.text.toLowerCase().includes(e))}function u(t,e,r){if(e.innerHTML="",t.length===0){e.hidden=!1;{const n=document.createElement("li");n.className="docs-search-empty",n.textContent="No matches",e.appendChild(n)}return}e.hidden=!1;for(const n of t.slice(0,8)){const s=document.createElement("li"),c=document.createElement("a");c.href=n.path;const o=document.createElement("span");o.className="docs-search-result-title",o.textContent=n.title;const a=document.createElement("span");a.className="docs-search-result-meta",a.textContent=[n.group,n.section].filter(Boolean).join(" / "),c.append(o,a),s.appendChild(c),e.appendChild(s)}}document.querySelectorAll(".docs-search").forEach(t=>{const e=t.dataset.indexUrl,r=t.querySelector(".docs-search-input"),n=t.querySelector(".docs-search-results");r.addEventListener("input",async()=>{const s=r.value.trim().toLowerCase();if(!s){n.hidden=!0,n.innerHTML="";return}const{pages:c}=await d(e);r.value.trim().toLowerCase()===s&&u(c.filter(o=>l(o,s)),n)}),r.addEventListener("focus",()=>{d(e),n.childElementCount>0&&(n.hidden=!1)}),r.addEventListener("keydown",s=>{s.key==="Escape"&&(r.value="",n.hidden=!0,n.innerHTML="",r.blur())}),r.addEventListener("blur",()=>{window.setTimeout(()=>{n.hidden=!0},150)})});document.addEventListener("keydown",t=>{if(!(t.metaKey||t.ctrlKey)||t.key.toLowerCase()!=="k")return;const e=[...document.querySelectorAll(".docs-search-input")].find(r=>r.offsetParent!==null);e&&(t.preventDefault(),e.focus(),e.select())});</script> <details class="docs-nav-group"> <summary>Overview</summary> <div class="docs-nav-group-body"> <div class="docs-nav-section"> <ul> <li> <a href="/__pyric/ui/docs/overview/"> Overview </a> </li><li> <a href="/__pyric/ui/docs/tutorial/"> Tutorial </a> </li><li> <a href="/__pyric/ui/docs/examples/"> Examples </a> </li> </ul> </div> </div> </details><details class="docs-nav-group"> <summary>Get started</summary> <div class="docs-nav-group-body"> <div class="docs-nav-section"> <ul> <li> <a href="/__pyric/ui/docs/get-started/start-building/"> Quickstart </a> </li><li> <a href="/__pyric/ui/docs/get-started/vite/"> Vite </a> </li><li> <a href="/__pyric/ui/docs/get-started/nextjs/"> Next.js </a> </li><li> <a href="/__pyric/ui/docs/get-started/how-the-swap-works/"> How the swap works </a> </li> </ul> </div> </div> </details><details class="docs-nav-group"> <summary>Build</summary> <div class="docs-nav-group-body"> <div class="docs-nav-section"> <ul> <li> <a href="/__pyric/ui/docs/build/authentication/"> Authentication </a> </li><li> <a href="/__pyric/ui/docs/build/cloud-firestore/"> Cloud Firestore </a> </li><li> <a href="/__pyric/ui/docs/build/realtime-database/"> Realtime Database </a> </li><li> <a href="/__pyric/ui/docs/build/cloud-storage/"> Cloud Storage </a> </li><li> <a href="/__pyric/ui/docs/build/cloud-messaging/"> Cloud Messaging </a> </li><li> <a href="/__pyric/ui/docs/build/ai-logic/"> AI Logic </a> </li> </ul> </div> </div> </details><details class="docs-nav-group"> <summary>Secure &amp; debug</summary> <div class="docs-nav-group-body"> <div class="docs-nav-section"> <ul> <li> <a href="/__pyric/ui/docs/secure/secure-it-with-rules/"> Security Rules </a> </li><li> <a href="/__pyric/ui/docs/secure/simulate-and-lint/"> Simulate and lint before you deploy </a> </li><li> <a href="/__pyric/ui/docs/secure/write-a-rules-test-suite/"> Write a rules test suite </a> </li><li> <a href="/__pyric/ui/docs/secure/read-a-denial/"> Read a denial and understand it </a> </li><li> <a href="/__pyric/ui/docs/secure/rules-standard-library/"> The rules standard library </a> </li><li> <a href="/__pyric/ui/docs/secure/rtdb-rules-in-typescript/"> RTDB rules in TypeScript </a> </li><li> <a href="/__pyric/ui/docs/secure/firestore-rules-limits/"> Rules limits </a> </li><li> <a href="/__pyric/ui/docs/secure/audit-your-rules/"> Audit rules and data </a> </li> </ul> </div><div class="docs-nav-section"> <h2 class="docs-nav-heading"> Examples </h2> <ul> <li> <a href="/__pyric/ui/docs/examples/chess/"> Chess showcase </a> </li> </ul> </div> </div> </details><details class="docs-nav-group"> <summary>Observe &amp; shape</summary> <div class="docs-nav-group-body"> <div class="docs-nav-section"> <ul> <li> <a href="/__pyric/ui/docs/observe/see-whats-happening/"> Traffic &amp; rule verdicts </a> </li><li> <a href="/__pyric/ui/docs/observe/resolve-runtime-status/"> Runtime status </a> </li><li> <a href="/__pyric/ui/docs/observe/shape-your-data/"> Seed, snapshot, replay </a> </li> </ul> </div> </div> </details><details class="docs-nav-group"> <summary>Ship &amp; test</summary> <div class="docs-nav-group-body"> <div class="docs-nav-section"> <ul> <li> <a href="/__pyric/ui/docs/ship/ship-to-production/"> Ship to production </a> </li><li> <a href="/__pyric/ui/docs/ship/test-in-node/"> Test in Node </a> </li> </ul> </div> </div> </details><details class="docs-nav-group"> <summary>Work with an agent</summary> <div class="docs-nav-group-body"> <div class="docs-nav-section"> <ul> <li> <a href="/__pyric/ui/docs/agent/set-up-your-agent/"> Connect an agent </a> </li><li> <a href="/__pyric/ui/docs/agent/work-with-an-agent/"> Use the MCP tools </a> </li><li> <a href="/__pyric/ui/docs/agent/watch-and-review/"> Watch in Pyric Studio </a> </li> </ul> </div> </div> </details><details class="docs-nav-group"> <summary>Trust</summary> <div class="docs-nav-group-body"> <div class="docs-nav-section"> <ul> <li> <a href="/__pyric/ui/docs/trust/versioning-and-compatibility/"> Versioning and the fb tag </a> </li><li> <a href="/__pyric/ui/docs/trust/storage-rules-stdlib-research/"> Storage Rules modules: evidence and design </a> </li><li> <a href="/__pyric/ui/docs/trust/rules-standard-library/"> Rules standard library reference </a> </li> </ul> </div> </div> </details><details class="docs-nav-group" open> <summary>Conformance</summary> <div class="docs-nav-group-body"> <div class="docs-nav-section"> <ul> <li> <a href="/__pyric/ui/docs/conformance-scores/"> Public API coverage </a> </li><li> <a href="/__pyric/ui/docs/trust/how-we-know-it-matches-firebase/"> How we know it matches Firebase </a> </li><li> <a href="/__pyric/ui/docs/app-compat/"> App </a> </li><li> <a href="/__pyric/ui/docs/ai-compat/"> AI Logic </a> </li><li> <a href="/__pyric/ui/docs/auth-compat/"> Auth </a> </li><li> <a href="/__pyric/ui/docs/firestore-compat/"> Firestore </a> </li><li> <a href="/__pyric/ui/docs/database-compat/" aria-current="page"> Realtime Database </a> </li><li> <a href="/__pyric/ui/docs/storage-compat/"> Storage </a> </li><li> <a href="/__pyric/ui/docs/messaging-compat/"> Messaging </a> </li><li> <a href="/__pyric/ui/docs/functions-rtdb-compat/"> Functions · RTDB </a> </li><li> <a href="/__pyric/ui/docs/rules-compat/"> Rules </a> </li> </ul> </div> </div> </details> <details class="docs-nav-group"> <summary>Reference</summary> <div class="docs-nav-group-body docs-nav-group-body--reference"> <div class="docs-nav-section"> <ul> <li> <a href="/__pyric/ui/docs/api-reference/"> API reference </a> </li> </ul> </div><div class="docs-nav-section"> <h2 class="docs-nav-heading">pyric</h2> <ul> <li> <a href="/__pyric/ui/docs/pyric-app-reference-api/"> pyric/app </a> </li><li> <a href="/__pyric/ui/docs/pyric-firestore-reference-api/"> pyric/firestore </a> </li><li> <a href="/__pyric/ui/docs/pyric-auth-reference-api/"> pyric/auth </a> </li><li> <a href="/__pyric/ui/docs/pyric-ai-reference-api/"> pyric/ai </a> </li><li> <a href="/__pyric/ui/docs/pyric-ai-scripting-reference-api/"> pyric/ai/scripting </a> </li><li> <a href="/__pyric/ui/docs/pyric-database-reference-api/"> pyric/database </a> </li><li> <a href="/__pyric/ui/docs/pyric-storage-reference-api/"> pyric/storage </a> </li><li> <a href="/__pyric/ui/docs/pyric-messaging-reference-api/"> pyric/messaging </a> </li><li> <a href="/__pyric/ui/docs/pyric-messaging-sw-reference-api/"> pyric/messaging/sw </a> </li><li> <a href="/__pyric/ui/docs/pyric-rules-reference-api/"> pyric/rules </a> </li><li> <a href="/__pyric/ui/docs/pyric-sandbox-reference-api/"> pyric/sandbox </a> </li><li> <a href="/__pyric/ui/docs/pyric-sandbox-firestore-reference-api/"> pyric/sandbox/firestore </a> </li><li> <a href="/__pyric/ui/docs/pyric-sandbox-database-reference-api/"> pyric/sandbox/database </a> </li> </ul> </div><div class="docs-nav-section"> <h2 class="docs-nav-heading">pyric-admin</h2> <ul> <li> <a href="/__pyric/ui/docs/pyric-admin-app-reference-api/"> pyric-admin/app </a> </li><li> <a href="/__pyric/ui/docs/pyric-admin-firestore-reference-api/"> pyric-admin/firestore </a> </li><li> <a href="/__pyric/ui/docs/pyric-admin-auth-reference-api/"> pyric-admin/auth </a> </li><li> <a href="/__pyric/ui/docs/pyric-admin-database-reference-api/"> pyric-admin/database </a> </li><li> <a href="/__pyric/ui/docs/pyric-admin-storage-reference-api/"> pyric-admin/storage </a> </li><li> <a href="/__pyric/ui/docs/pyric-admin-messaging-reference-api/"> pyric-admin/messaging </a> </li> </ul> </div><div class="docs-nav-section"> <h2 class="docs-nav-heading">create-pyric</h2> <ul> <li> <a href="/__pyric/ui/docs/create-pyric-reference-api/"> create-pyric </a> </li> </ul> </div><div class="docs-nav-section"> <h2 class="docs-nav-heading">@pyric/cli</h2> <ul> <li> <a href="/__pyric/ui/docs/pyric-cli-credentials-node-reference-api/"> @pyric/cli/credentials/node </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-verify-reference-api/"> @pyric/cli/verify </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-conformance-reference-api/"> @pyric/cli/conformance </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-conformance-browser-reference-api/"> @pyric/cli/conformance/browser </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-conformance-docs-reference-api/"> @pyric/cli/conformance/docs </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-assurance-reference-api/"> @pyric/cli/assurance </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-assurance-browser-reference-api/"> @pyric/cli/assurance/browser </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-bridge-reference-api/"> @pyric/cli/bridge </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-bridge-client-reference-api/"> @pyric/cli/bridge/client </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-discover-reference-api/"> @pyric/cli/discover </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-vite-reference-api/"> @pyric/cli/vite </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-next-reference-api/"> @pyric/cli/next </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-serve-worker-reference-api/"> @pyric/cli/serve/worker </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-remote-reference-api/"> @pyric/cli/remote </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-register-reference-api/"> @pyric/cli/register </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-register-app-bridge-reference-api/"> @pyric/cli/register/app-bridge </a> </li> </ul> </div><div class="docs-nav-section"> <h2 class="docs-nav-heading">@pyric/ui</h2> <ul> <li> <a href="/__pyric/ui/docs/pyric-ui-auth-reference-api/"> @pyric/ui/auth </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-auth-hooks-reference-api/"> @pyric/ui/auth/hooks </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-primitives-reference-api/"> @pyric/ui/primitives </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-firestore-reference-api/"> @pyric/ui/firestore </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-firestore-hooks-reference-api/"> @pyric/ui/firestore/hooks </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-rtdb-reference-api/"> @pyric/ui/rtdb </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-storage-reference-api/"> @pyric/ui/storage </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-storage-hooks-reference-api/"> @pyric/ui/storage/hooks </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-traffic-reference-api/"> @pyric/ui/traffic </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-traffic-hooks-reference-api/"> @pyric/ui/traffic/hooks </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-events-reference-api/"> @pyric/ui/events </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-events-hooks-reference-api/"> @pyric/ui/events/hooks </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-agents-reference-api/"> @pyric/ui/agents </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-rules-reference-api/"> @pyric/ui/rules </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-rules-hooks-reference-api/"> @pyric/ui/rules/hooks </a> </li> </ul> </div> </div> </details> </nav> </details> <nav class="docs-nav" aria-label="Documentation"> <div class="docs-search" data-index-url="/__pyric/ui/docs/index.json"> <div class="docs-search-field"> <svg class="docs-search-icon" viewBox="0 0 16 16" width="14" height="14" aria-hidden="true"> <path d="M11.5 11.5 15 15M13 7A6 6 0 1 1 1 7a6 6 0 0 1 12 0Z" fill="none" stroke="currentColor" stroke-width="1.4" stroke-linecap="round"></path> </svg> <input type="text" class="docs-search-input" placeholder="Search docs" aria-label="Search docs" autocomplete="off" spellcheck="false"> <kbd class="docs-search-kbd">⌘K</kbd> </div> <ul class="docs-search-results" hidden></ul> </div> <details class="docs-nav-group" id="nav-group-overview"> <summary>Overview</summary> <div class="docs-nav-group-body"> <div class="docs-nav-section"> <ul> <li> <a href="/__pyric/ui/docs/overview/"> Overview </a> </li><li> <a href="/__pyric/ui/docs/tutorial/"> Tutorial </a> </li><li> <a href="/__pyric/ui/docs/examples/"> Examples </a> </li> </ul> </div> </div> </details><details class="docs-nav-group" id="nav-group-get-started"> <summary>Get started</summary> <div class="docs-nav-group-body"> <div class="docs-nav-section"> <ul> <li> <a href="/__pyric/ui/docs/get-started/start-building/"> Quickstart </a> </li><li> <a href="/__pyric/ui/docs/get-started/vite/"> Vite </a> </li><li> <a href="/__pyric/ui/docs/get-started/nextjs/"> Next.js </a> </li><li> <a href="/__pyric/ui/docs/get-started/how-the-swap-works/"> How the swap works </a> </li> </ul> </div> </div> </details><details class="docs-nav-group" id="nav-group-build"> <summary>Build</summary> <div class="docs-nav-group-body"> <div class="docs-nav-section"> <ul> <li> <a href="/__pyric/ui/docs/build/authentication/"> Authentication </a> </li><li> <a href="/__pyric/ui/docs/build/cloud-firestore/"> Cloud Firestore </a> </li><li> <a href="/__pyric/ui/docs/build/realtime-database/"> Realtime Database </a> </li><li> <a href="/__pyric/ui/docs/build/cloud-storage/"> Cloud Storage </a> </li><li> <a href="/__pyric/ui/docs/build/cloud-messaging/"> Cloud Messaging </a> </li><li> <a href="/__pyric/ui/docs/build/ai-logic/"> AI Logic </a> </li> </ul> </div> </div> </details><details class="docs-nav-group" id="nav-group-secure-debug"> <summary>Secure &amp; debug</summary> <div class="docs-nav-group-body"> <div class="docs-nav-section"> <ul> <li> <a href="/__pyric/ui/docs/secure/secure-it-with-rules/"> Security Rules </a> </li><li> <a href="/__pyric/ui/docs/secure/simulate-and-lint/"> Simulate and lint before you deploy </a> </li><li> <a href="/__pyric/ui/docs/secure/write-a-rules-test-suite/"> Write a rules test suite </a> </li><li> <a href="/__pyric/ui/docs/secure/read-a-denial/"> Read a denial and understand it </a> </li><li> <a href="/__pyric/ui/docs/secure/rules-standard-library/"> The rules standard library </a> </li><li> <a href="/__pyric/ui/docs/secure/rtdb-rules-in-typescript/"> RTDB rules in TypeScript </a> </li><li> <a href="/__pyric/ui/docs/secure/firestore-rules-limits/"> Rules limits </a> </li><li> <a href="/__pyric/ui/docs/secure/audit-your-rules/"> Audit rules and data </a> </li> </ul> </div><div class="docs-nav-section"> <h2 class="docs-nav-heading" id="nav-section-secure-debug-examples"> Examples </h2> <ul> <li> <a href="/__pyric/ui/docs/examples/chess/"> Chess showcase </a> </li> </ul> </div> </div> </details><details class="docs-nav-group" id="nav-group-observe-shape"> <summary>Observe &amp; shape</summary> <div class="docs-nav-group-body"> <div class="docs-nav-section"> <ul> <li> <a href="/__pyric/ui/docs/observe/see-whats-happening/"> Traffic &amp; rule verdicts </a> </li><li> <a href="/__pyric/ui/docs/observe/resolve-runtime-status/"> Runtime status </a> </li><li> <a href="/__pyric/ui/docs/observe/shape-your-data/"> Seed, snapshot, replay </a> </li> </ul> </div> </div> </details><details class="docs-nav-group" id="nav-group-ship-test"> <summary>Ship &amp; test</summary> <div class="docs-nav-group-body"> <div class="docs-nav-section"> <ul> <li> <a href="/__pyric/ui/docs/ship/ship-to-production/"> Ship to production </a> </li><li> <a href="/__pyric/ui/docs/ship/test-in-node/"> Test in Node </a> </li> </ul> </div> </div> </details><details class="docs-nav-group" id="nav-group-work-with-an-agent"> <summary>Work with an agent</summary> <div class="docs-nav-group-body"> <div class="docs-nav-section"> <ul> <li> <a href="/__pyric/ui/docs/agent/set-up-your-agent/"> Connect an agent </a> </li><li> <a href="/__pyric/ui/docs/agent/work-with-an-agent/"> Use the MCP tools </a> </li><li> <a href="/__pyric/ui/docs/agent/watch-and-review/"> Watch in Pyric Studio </a> </li> </ul> </div> </div> </details><details class="docs-nav-group" id="nav-group-trust"> <summary>Trust</summary> <div class="docs-nav-group-body"> <div class="docs-nav-section"> <ul> <li> <a href="/__pyric/ui/docs/trust/versioning-and-compatibility/"> Versioning and the fb tag </a> </li><li> <a href="/__pyric/ui/docs/trust/storage-rules-stdlib-research/"> Storage Rules modules: evidence and design </a> </li><li> <a href="/__pyric/ui/docs/trust/rules-standard-library/"> Rules standard library reference </a> </li> </ul> </div> </div> </details><details class="docs-nav-group" id="nav-group-conformance" open> <summary>Conformance</summary> <div class="docs-nav-group-body"> <div class="docs-nav-section"> <ul> <li> <a href="/__pyric/ui/docs/conformance-scores/"> Public API coverage </a> </li><li> <a href="/__pyric/ui/docs/trust/how-we-know-it-matches-firebase/"> How we know it matches Firebase </a> </li><li> <a href="/__pyric/ui/docs/app-compat/"> App </a> </li><li> <a href="/__pyric/ui/docs/ai-compat/"> AI Logic </a> </li><li> <a href="/__pyric/ui/docs/auth-compat/"> Auth </a> </li><li> <a href="/__pyric/ui/docs/firestore-compat/"> Firestore </a> </li><li> <a href="/__pyric/ui/docs/database-compat/" aria-current="page"> Realtime Database </a> </li><li> <a href="/__pyric/ui/docs/storage-compat/"> Storage </a> </li><li> <a href="/__pyric/ui/docs/messaging-compat/"> Messaging </a> </li><li> <a href="/__pyric/ui/docs/functions-rtdb-compat/"> Functions · RTDB </a> </li><li> <a href="/__pyric/ui/docs/rules-compat/"> Rules </a> </li> </ul> </div> </div> </details> <details class="docs-nav-group" id="nav-group-reference"> <summary>Reference</summary> <div class="docs-nav-group-body docs-nav-group-body--reference"> <div class="docs-nav-section"> <ul> <li> <a href="/__pyric/ui/docs/api-reference/"> API reference </a> </li> </ul> </div><div class="docs-nav-section"> <h2 class="docs-nav-heading">pyric</h2> <ul> <li> <a href="/__pyric/ui/docs/pyric-app-reference-api/"> pyric/app </a> </li><li> <a href="/__pyric/ui/docs/pyric-firestore-reference-api/"> pyric/firestore </a> </li><li> <a href="/__pyric/ui/docs/pyric-auth-reference-api/"> pyric/auth </a> </li><li> <a href="/__pyric/ui/docs/pyric-ai-reference-api/"> pyric/ai </a> </li><li> <a href="/__pyric/ui/docs/pyric-ai-scripting-reference-api/"> pyric/ai/scripting </a> </li><li> <a href="/__pyric/ui/docs/pyric-database-reference-api/"> pyric/database </a> </li><li> <a href="/__pyric/ui/docs/pyric-storage-reference-api/"> pyric/storage </a> </li><li> <a href="/__pyric/ui/docs/pyric-messaging-reference-api/"> pyric/messaging </a> </li><li> <a href="/__pyric/ui/docs/pyric-messaging-sw-reference-api/"> pyric/messaging/sw </a> </li><li> <a href="/__pyric/ui/docs/pyric-rules-reference-api/"> pyric/rules </a> </li><li> <a href="/__pyric/ui/docs/pyric-sandbox-reference-api/"> pyric/sandbox </a> </li><li> <a href="/__pyric/ui/docs/pyric-sandbox-firestore-reference-api/"> pyric/sandbox/firestore </a> </li><li> <a href="/__pyric/ui/docs/pyric-sandbox-database-reference-api/"> pyric/sandbox/database </a> </li> </ul> </div><div class="docs-nav-section"> <h2 class="docs-nav-heading">pyric-admin</h2> <ul> <li> <a href="/__pyric/ui/docs/pyric-admin-app-reference-api/"> pyric-admin/app </a> </li><li> <a href="/__pyric/ui/docs/pyric-admin-firestore-reference-api/"> pyric-admin/firestore </a> </li><li> <a href="/__pyric/ui/docs/pyric-admin-auth-reference-api/"> pyric-admin/auth </a> </li><li> <a href="/__pyric/ui/docs/pyric-admin-database-reference-api/"> pyric-admin/database </a> </li><li> <a href="/__pyric/ui/docs/pyric-admin-storage-reference-api/"> pyric-admin/storage </a> </li><li> <a href="/__pyric/ui/docs/pyric-admin-messaging-reference-api/"> pyric-admin/messaging </a> </li> </ul> </div><div class="docs-nav-section"> <h2 class="docs-nav-heading">create-pyric</h2> <ul> <li> <a href="/__pyric/ui/docs/create-pyric-reference-api/"> create-pyric </a> </li> </ul> </div><div class="docs-nav-section"> <h2 class="docs-nav-heading">@pyric/cli</h2> <ul> <li> <a href="/__pyric/ui/docs/pyric-cli-credentials-node-reference-api/"> @pyric/cli/credentials/node </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-verify-reference-api/"> @pyric/cli/verify </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-conformance-reference-api/"> @pyric/cli/conformance </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-conformance-browser-reference-api/"> @pyric/cli/conformance/browser </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-conformance-docs-reference-api/"> @pyric/cli/conformance/docs </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-assurance-reference-api/"> @pyric/cli/assurance </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-assurance-browser-reference-api/"> @pyric/cli/assurance/browser </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-bridge-reference-api/"> @pyric/cli/bridge </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-bridge-client-reference-api/"> @pyric/cli/bridge/client </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-discover-reference-api/"> @pyric/cli/discover </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-vite-reference-api/"> @pyric/cli/vite </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-next-reference-api/"> @pyric/cli/next </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-serve-worker-reference-api/"> @pyric/cli/serve/worker </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-remote-reference-api/"> @pyric/cli/remote </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-register-reference-api/"> @pyric/cli/register </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-register-app-bridge-reference-api/"> @pyric/cli/register/app-bridge </a> </li> </ul> </div><div class="docs-nav-section"> <h2 class="docs-nav-heading">@pyric/ui</h2> <ul> <li> <a href="/__pyric/ui/docs/pyric-ui-auth-reference-api/"> @pyric/ui/auth </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-auth-hooks-reference-api/"> @pyric/ui/auth/hooks </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-primitives-reference-api/"> @pyric/ui/primitives </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-firestore-reference-api/"> @pyric/ui/firestore </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-firestore-hooks-reference-api/"> @pyric/ui/firestore/hooks </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-rtdb-reference-api/"> @pyric/ui/rtdb </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-storage-reference-api/"> @pyric/ui/storage </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-storage-hooks-reference-api/"> @pyric/ui/storage/hooks </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-traffic-reference-api/"> @pyric/ui/traffic </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-traffic-hooks-reference-api/"> @pyric/ui/traffic/hooks </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-events-reference-api/"> @pyric/ui/events </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-events-hooks-reference-api/"> @pyric/ui/events/hooks </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-agents-reference-api/"> @pyric/ui/agents </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-rules-reference-api/"> @pyric/ui/rules </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-rules-hooks-reference-api/"> @pyric/ui/rules/hooks </a> </li> </ul> </div> </div> </details> </nav> <main class="doc-main" data-astro-cid-mw7aashj> <nav class="docs-breadcrumbs" aria-label="Breadcrumb"> <ol> <li> <a href="/__pyric/ui/docs/conformance-scores/">Conformance</a> </li><li> <span class="docs-breadcrumbs-sep" aria-hidden="true">/</span> <span aria-current="page">Realtime Database</span> </li> </ol> </nav> <article class="prose" data-astro-cid-mw7aashj> <!-- Generated from the conformance model (registry rows + surface contracts). Do not edit by hand; run bun run compat:generate. -->
3
3
  <h1 id="pyricdatabase-compatibility-matrix"><code>pyric/database</code> compatibility matrix</h1>
4
4
  <div class="compat-stat">
5
5
  <p class="compat-stat-figure">
@@ -15,986 +15,784 @@
15
15
  <div class="compat-key">
16
16
  <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>
17
17
  <span class="compat-key-item"><span class="compat-dot" data-status="diverged"></span><strong>Diverged (documented)</strong> — intentional difference with a written reason</span>
18
- <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>
19
18
  </div>
20
- <h2 id="archived-production-toolkit-observations">Archived production-toolkit observations</h2>
21
- <p>These unsupported tombstones preserve immutable oracle <code>rowIds</code> for the removed host, REST, data, crawl, generation, and deployment toolkit. They are historical evidence, not current API claims.</p>
22
- <div class="compat-list">
23
- <details class="compat-row" data-status="unsupported">
24
- <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>
25
- <div class="compat-evidence"><div class="compat-probe">Archived oracle observation; implementation removed.</div></div>
26
- </details>
27
- <details class="compat-row" data-status="unsupported">
28
- <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>
29
- <div class="compat-evidence"><div class="compat-probe">Archived oracle observations; implementation removed.</div></div>
30
- </details>
31
- <details class="compat-row" data-status="unsupported">
32
- <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>
33
- <div class="compat-evidence"><div class="compat-probe">Archived oracle observation; implementation removed.</div></div>
34
- </details>
35
- <details class="compat-row" data-status="unsupported">
36
- <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>
37
- <div class="compat-evidence"><div class="compat-probe">Archived oracle observations; implementation removed.</div></div>
38
- </details>
39
- <details class="compat-row" data-status="unsupported">
40
- <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>
41
- <div class="compat-evidence"><div class="compat-probe">Archived oracle observation; implementation removed.</div></div>
42
- </details>
43
- <details class="compat-row" data-status="unsupported">
44
- <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>
45
- <div class="compat-evidence"><div class="compat-probe">Archived oracle observation; implementation removed.</div></div>
46
- </details>
47
- <details class="compat-row" data-status="unsupported">
48
- <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>
49
- <div class="compat-evidence"><div class="compat-probe">Archived oracle observation; implementation removed.</div></div>
50
- </details>
51
- <details class="compat-row" data-status="unsupported">
52
- <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>
53
- <div class="compat-evidence"><div class="compat-probe">Archived oracle observation; implementation removed.</div></div>
54
- </details>
55
- <details class="compat-row" data-status="unsupported">
56
- <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>
57
- <div class="compat-evidence"><div class="compat-probe">Archived oracle observation; implementation removed.</div></div>
58
- </details>
59
- <details class="compat-row" data-status="unsupported">
60
- <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>
61
- <div class="compat-evidence"><div class="compat-probe">Archived oracle observation; implementation removed.</div></div>
62
- </details>
63
- <details class="compat-row" data-status="unsupported">
64
- <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>
65
- <div class="compat-evidence"><div class="compat-probe">Archived oracle observation; implementation removed.</div></div>
66
- </details>
67
- <details class="compat-row" data-status="unsupported">
68
- <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>
69
- <div class="compat-evidence"><div class="compat-probe">Archived oracle observation; implementation removed.</div></div>
70
- </details>
71
- <details class="compat-row" data-status="unsupported">
72
- <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>
73
- <div class="compat-evidence"><div class="compat-probe">Archived oracle observation; implementation removed.</div></div>
74
- </details>
75
- <details class="compat-row" data-status="unsupported">
76
- <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>
77
- <div class="compat-evidence"><div class="compat-probe">Archived oracle observation; implementation removed.</div></div>
78
- </details>
79
- <details class="compat-row" data-status="unsupported">
80
- <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>
81
- <div class="compat-evidence"><div class="compat-probe">Archived oracle observation; implementation removed.</div></div>
82
- </details>
83
- <details class="compat-row" data-status="unsupported">
84
- <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>
85
- <div class="compat-evidence"><div class="compat-probe">Archived oracle observation; implementation removed.</div></div>
86
- </details>
87
- </div>
88
- <h2 id="simulatertdbrulescompiled-input--in-process-rule-evaluator"><code>simulateRtdbRules(compiled, input)</code> — in-process rule evaluator</h2>
89
- <div class="compat-list">
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">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>
92
- <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>
93
- </details>
94
- <details class="compat-row" data-status="ok">
95
- <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>
96
- <div class="compat-evidence"><div class="compat-probe"><code>unit:simulation/handler.test.ts</code></div></div>
97
- </details>
98
- <details class="compat-row" data-status="ok">
99
- <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>
100
- <div class="compat-evidence"><div class="compat-probe"><code>unit:simulation/handler.test.ts</code></div></div>
101
- </details>
102
- <details class="compat-row" data-status="ok">
103
- <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>
104
- <div class="compat-evidence"><div class="compat-probe"><code>unit:simulation/handler.test.ts</code></div></div>
105
- </details>
106
- <details class="compat-row" data-status="ok">
107
- <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>
108
- <div class="compat-evidence"><div class="compat-probe"><code>unit:simulation/handler.test.ts</code>, <code>unit:grammar/simulator.test.ts</code></div></div>
109
- </details>
110
- <details class="compat-row" data-status="ok">
111
- <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>
112
- <div class="compat-evidence"><div class="compat-probe"><code>unit:simulation/handler.test.ts</code></div></div>
113
- </details>
114
- <details class="compat-row" data-status="ok">
115
- <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>
116
- <div class="compat-evidence"><div class="compat-probe"><code>unit:grammar/simulator.test.ts</code></div></div>
117
- </details>
118
- <details class="compat-row" data-status="diverged">
119
- <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>
120
- <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>
121
- </details>
122
- <details class="compat-row" data-status="ok">
123
- <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>
124
- <div class="compat-evidence"><div class="compat-probe"><code>unit:simulation/handler.test.ts</code></div></div>
125
- </details>
126
- <details class="compat-row" data-status="ok">
127
- <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>
128
- <div class="compat-evidence"><div class="compat-probe"><code>unit:simulation/handler.test.ts</code></div></div>
129
- </details>
130
- <details class="compat-row" data-status="ok">
131
- <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>
132
- <div class="compat-evidence"><div class="compat-probe"><code>unit:simulation/handler.test.ts</code></div></div>
133
- </details>
134
- <details class="compat-row" data-status="ok">
135
- <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>
136
- <div class="compat-evidence"><div class="compat-probe"><code>unit:simulation/handler.test.ts</code></div></div>
137
- </details>
138
- <details class="compat-row" data-status="ok">
139
- <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>
140
- <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>
141
- </details>
142
- </div>
143
- <h2 id="constraint-authoring-surface-atoms--policies--compose--ruleset">Constraint authoring surface (<code>atoms</code> / <code>policies</code> / <code>compose</code> / <code>ruleset</code>)</h2>
144
- <div class="compat-list">
145
- <details class="compat-row" data-status="ok">
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>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>
147
- <div class="compat-evidence"><div class="compat-probe"><code>unit:constraints/atoms.test.ts</code></div></div>
148
- </details>
149
- <details class="compat-row" data-status="ok">
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>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>
151
- <div class="compat-evidence"><div class="compat-probe"><code>unit:constraints/policies.test.ts</code></div></div>
152
- </details>
153
- <details class="compat-row" data-status="ok">
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"><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>
155
- <div class="compat-evidence"><div class="compat-probe"><code>unit:constraints/compose.test.ts</code></div></div>
156
- </details>
157
- <details class="compat-row" data-status="ok">
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>ruleset(...)</code> builds an environment-independent compiled RTDB rules tree from path definitions + expression objects</span></span></summary>
159
- <div class="compat-evidence"><div class="compat-probe"><code>unit:constraints/ruleset.test.ts</code></div></div>
160
- </details>
161
- <details class="compat-row" data-status="ok">
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">Game-domain helpers (<code>turnGuard</code>, <code>flip</code>, <code>winCheckHelper</code>) compose into legal rule expressions</span></span></summary>
163
- <div class="compat-evidence"><div class="compat-probe"><code>unit:constraints/game.test.ts</code></div></div>
164
- </details>
165
- </div>
166
- <h2 id="compiled-rtdb-rules-tree--rules-json">Compiled RTDB rules tree ↔ rules JSON</h2>
167
- <div class="compat-list">
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"><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>
170
- <div class="compat-evidence"><div class="compat-probe"><code>unit:compiled-rules.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"><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>
174
- <div class="compat-evidence"><div class="compat-probe"><code>unit:compiled-rules.test.ts</code></div></div>
175
- </details>
176
- <details class="compat-row" data-status="ok">
177
- <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>
178
- <div class="compat-evidence"><div class="compat-probe"><code>unit:compiled-rules.test.ts</code></div></div>
179
- </details>
180
- <details class="compat-row" data-status="ok">
181
- <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>
182
- <div class="compat-evidence"><div class="compat-probe"><code>unit:compiled-rules.test.ts</code></div></div>
183
- </details>
184
- <details class="compat-row" data-status="ok">
185
- <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>
186
- <div class="compat-evidence"><div class="compat-probe"><code>unit:compiled-rules.test.ts</code></div></div>
187
- </details>
188
- </div>
189
- <h2 id="modular-sdk-surface">Modular SDK surface</h2>
190
- <p><code>pyric/database</code> is the sandbox-only mirror of <code>firebase/database</code>’s
191
- tree-shakable free-function shape (<code>getDatabase</code>, <code>ref</code>, <code>child</code>, <code>get</code>,
192
- <code>set</code>, <code>update</code>, <code>remove</code>, <code>push</code>, listeners, queries, transactions, and
193
- sentinels). Package resolution selects production or sandbox before either
194
- module loads; this mirror has no production target or runtime dependency on
195
- <code>firebase/database</code>.</p>
196
- <p>Two sandbox identity modes are selected by the value passed to
197
- <code>getDatabase</code>:</p>
198
- <ul>
199
- <li><strong>Sandbox</strong> — <code>getDatabase(ctx: SandboxContext)</code>. Frozen identity baked into
200
- the handle at construction.</li>
201
- <li><strong>Sandbox-live</strong> — <code>getDatabase(sandbox: Sandbox)</code>. Identity read per
202
- operation from <code>sandbox.currentUser</code>.</li>
203
- </ul>
204
- <p>The public implementation is split by API family under <code>packages/pyric/src/database/</code>; the
205
- in-process backend lives in <code>packages/pyric/src/database/sandbox/</code>. Rows below
206
- are scoped to the modular mirror. The pure rules-engine rows above are internal
207
- tooling coverage and are not exports of <code>pyric/database</code>.</p>
19
+ <h2 id="public-api">Public API</h2>
20
+ <p><code>pyric/database</code> mirrors the public <code>firebase/database</code> API in the Pyric sandbox. The rows below compare its observable behaviour with Firebase.</p>
208
21
  <div class="compat-list">
209
22
  <details class="compat-row" data-status="ok">
210
23
  <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>
211
- <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>
24
+ <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>
212
25
  </details>
213
26
  <details class="compat-row" data-status="ok">
214
27
  <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>
215
- <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>
216
- </details>
217
- <details class="compat-row" data-status="unsupported">
218
- <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>
219
- <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>
28
+ <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>
220
29
  </details>
221
30
  <details class="compat-row" data-status="ok">
222
31
  <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>
223
- <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>
32
+ <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>
224
33
  </details>
225
34
  <details class="compat-row" data-status="ok">
226
35
  <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>
227
- <div class="compat-evidence"><div class="compat-probe"><code>unit:modular/sandbox-target.test.ts</code> ("child(ref, 'sub') composes paths")</div></div>
36
+ <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>
228
37
  </details>
229
38
  <details class="compat-row" data-status="ok">
230
39
  <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>
231
- <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>
40
+ <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>
232
41
  </details>
233
42
  <details class="compat-row" data-status="ok">
234
43
  <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>
235
- <div class="compat-evidence"><div class="compat-probe"><code>unit:modular/sandbox-target.test.ts</code> ("ref.root returns the root ref")</div></div>
44
+ <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>
236
45
  </details>
237
46
  <details class="compat-row" data-status="ok">
238
47
  <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>
239
- <div class="compat-evidence"><div class="compat-probe"><code>unit:modular/sandbox-target.test.ts</code> (snapshot shape tests)</div></div>
48
+ <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>
240
49
  </details>
241
50
  <details class="compat-row" data-status="ok">
242
51
  <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>
243
- <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>
52
+ <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>
244
53
  </details>
245
54
  <details class="compat-row" data-status="ok">
246
55
  <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>
247
- <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>
56
+ <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>
248
57
  </details>
249
58
  <details class="compat-row" data-status="ok">
250
59
  <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>
251
- <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>
60
+ <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>
252
61
  </details>
253
62
  <details class="compat-row" data-status="ok">
254
63
  <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>
255
- <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>
64
+ <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>
256
65
  </details>
257
66
  <details class="compat-row" data-status="ok">
258
67
  <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>
259
- <div class="compat-evidence"><div class="compat-probe"><code>unit:modular/sandbox-target.test.ts</code> ("shallow-merges top-level keys")</div></div>
68
+ <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>
260
69
  </details>
261
70
  <details class="compat-row" data-status="ok">
262
71
  <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>
263
- <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>
72
+ <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>
264
73
  </details>
265
74
  <details class="compat-row" data-status="ok">
266
75
  <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>
267
- <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>
76
+ <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>
268
77
  </details>
269
78
  <details class="compat-row" data-status="ok">
270
79
  <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>
271
- <div class="compat-evidence"><div class="compat-probe"><code>unit:modular/sandbox-target.test.ts</code> ("rejects overlapping paths")</div></div>
80
+ <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>
272
81
  </details>
273
82
  <details class="compat-row" data-status="ok">
274
83
  <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>
275
- <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>
84
+ <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>
276
85
  </details>
277
86
  <details class="compat-row" data-status="ok">
278
87
  <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>
279
- <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>
88
+ <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>
280
89
  </details>
281
90
  <details class="compat-row" data-status="ok">
282
91
  <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>
283
- <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>
92
+ <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>
284
93
  </details>
285
94
  <details class="compat-row" data-status="ok">
286
95
  <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>
287
- <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>
96
+ <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>
288
97
  </details>
289
98
  <details class="compat-row" data-status="ok">
290
99
  <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>
291
- <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>
100
+ <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>
292
101
  </details>
293
102
  <details class="compat-row" data-status="ok">
294
103
  <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>
295
- <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>
104
+ <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>
296
105
  </details>
297
106
  <details class="compat-row" data-status="ok">
298
107
  <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>
299
- <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>
108
+ <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>
300
109
  </details>
301
110
  <details class="compat-row" data-status="ok">
302
111
  <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>
303
- <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>
112
+ <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>
304
113
  </details>
305
114
  <details class="compat-row" data-status="ok">
306
115
  <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>
307
- <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>
116
+ <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>
308
117
  </details>
309
118
  <details class="compat-row" data-status="ok">
310
119
  <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>
311
- <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>
120
+ <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>
312
121
  </details>
313
122
  <details class="compat-row" data-status="ok">
314
123
  <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>
315
- <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>
124
+ <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>
316
125
  </details>
317
126
  <details class="compat-row" data-status="ok">
318
127
  <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>
319
- <div class="compat-evidence"><div class="compat-probe"><code>unit:modular/sandbox-target.test.ts</code> ("fires on a descendant write")</div></div>
128
+ <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>
320
129
  </details>
321
130
  <details class="compat-row" data-status="ok">
322
131
  <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>
323
- <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>
132
+ <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>
324
133
  </details>
325
134
  <details class="compat-row" data-status="ok">
326
135
  <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>
327
- <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>
136
+ <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>
328
137
  </details>
329
138
  <details class="compat-row" data-status="ok">
330
139
  <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>
331
- <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>
140
+ <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>
332
141
  </details>
333
142
  <details class="compat-row" data-status="ok">
334
143
  <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>
335
- <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>
144
+ <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>
336
145
  </details>
337
146
  <details class="compat-row" data-status="ok">
338
147
  <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>
339
- <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>
148
+ <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>
340
149
  </details>
341
150
  <details class="compat-row" data-status="ok">
342
151
  <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>
343
- <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>
152
+ <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>
344
153
  </details>
345
154
  <details class="compat-row" data-status="ok">
346
155
  <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>
347
- <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>
156
+ <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>
348
157
  </details>
349
158
  <details class="compat-row" data-status="ok">
350
159
  <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>
351
- <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>
160
+ <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>
352
161
  </details>
353
162
  <details class="compat-row" data-status="ok">
354
163
  <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>
355
- <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>
164
+ <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>
356
165
  </details>
357
166
  <details class="compat-row" data-status="ok">
358
167
  <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>
359
- <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>
168
+ <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>
360
169
  </details>
361
170
  <details class="compat-row" data-status="ok">
362
171
  <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>
363
- <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>
172
+ <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>
364
173
  </details>
365
174
  <details class="compat-row" data-status="ok">
366
175
  <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>
367
- <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>
176
+ <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>
368
177
  </details>
369
178
  <details class="compat-row" data-status="ok">
370
179
  <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>
371
- <div class="compat-evidence"><div class="compat-probe"><code>unit:modular/sandbox-target.test.ts</code> ("sandbox.setRules(db, null) clears rules")</div></div>
180
+ <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>
372
181
  </details>
373
182
  <details class="compat-row" data-status="ok">
374
183
  <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>
375
- <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>
184
+ <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>
376
185
  </details>
377
186
  <details class="compat-row" data-status="ok">
378
187
  <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>
379
- <div class="compat-evidence"><div class="compat-probe"><code>unit:modular/sandbox-target.test.ts</code> ("sandbox.snapshotState dumps the full tree")</div></div>
188
+ <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>
380
189
  </details>
381
190
  <details class="compat-row" data-status="ok">
382
191
  <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>
383
- <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>
192
+ <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>
384
193
  </details>
385
194
  <details class="compat-row" data-status="ok">
386
195
  <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>
387
- <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>
196
+ <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>
388
197
  </details>
389
198
  <details class="compat-row" data-status="ok">
390
199
  <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>
391
- <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>
200
+ <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>
392
201
  </details>
393
202
  <details class="compat-row" data-status="diverged">
394
203
  <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>
395
- <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>
204
+ <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>
396
205
  </details>
397
206
  <details class="compat-row" data-status="ok">
398
207
  <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>
399
- <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>
208
+ <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>
400
209
  </details>
401
210
  <details class="compat-row" data-status="ok">
402
211
  <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>
403
- <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>
212
+ <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>
404
213
  </details>
405
214
  <details class="compat-row" data-status="ok">
406
215
  <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>
407
- <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>
216
+ <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>
408
217
  </details>
409
218
  <details class="compat-row" data-status="ok">
410
219
  <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>
411
- <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>
220
+ <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>
412
221
  </details>
413
222
  <details class="compat-row" data-status="ok">
414
223
  <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>
415
- <div class="compat-evidence"><div class="compat-probe"><code>unit:modular/transaction.test.ts</code> ("committed write fans out to onValue listeners")</div></div>
224
+ <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>
416
225
  </details>
417
226
  <details class="compat-row" data-status="diverged">
418
227
  <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>
419
- <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>
228
+ <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>
420
229
  </details>
421
230
  <details class="compat-row" data-status="ok">
422
231
  <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>
423
- <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>
232
+ <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>
424
233
  </details>
425
234
  <details class="compat-row" data-status="ok">
426
235
  <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>
427
- <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>
236
+ <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>
428
237
  </details>
429
238
  <details class="compat-row" data-status="ok">
430
239
  <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/&#x3C;path></code></span></span></summary>
431
- <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/&#x3C;path></code> string boundary alongside the production URL observation.</div></div>
240
+ <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/&#x3C;path></code> string boundary alongside the production URL observation.</div></div>
432
241
  </details>
433
242
  <details class="compat-row" data-status="ok">
434
243
  <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>
435
- <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>
244
+ <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>
436
245
  </details>
437
246
  <details class="compat-row" data-status="ok">
438
247
  <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>
439
- <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>
248
+ <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>
440
249
  </details>
441
250
  <details class="compat-row" data-status="diverged">
442
251
  <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>
443
- <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>
252
+ <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>
444
253
  </details>
445
254
  <details class="compat-row" data-status="ok">
446
255
  <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>
447
- <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>
256
+ <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>
448
257
  </details>
449
258
  <details class="compat-row" data-status="ok">
450
259
  <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>
451
- <div class="compat-evidence"><div class="compat-probe"><code>unit:modular/queries.test.ts</code> ("returns an empty snapshot when nothing matches")</div></div>
260
+ <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>
452
261
  </details>
453
262
  <details class="compat-row" data-status="ok">
454
263
  <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>
455
- <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>
264
+ <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>
456
265
  </details>
457
266
  <details class="compat-row" data-status="ok">
458
267
  <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>
459
- <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>
268
+ <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>
460
269
  </details>
461
270
  <details class="compat-row" data-status="ok">
462
271
  <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>
463
- <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>
272
+ <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>
464
273
  </details>
465
274
  <details class="compat-row" data-status="ok">
466
275
  <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>
467
- <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>
276
+ <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>
468
277
  </details>
469
278
  <details class="compat-row" data-status="ok">
470
279
  <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>
471
- <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>
280
+ <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>
472
281
  </details>
473
282
  <details class="compat-row" data-status="ok">
474
283
  <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>
475
- <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>
284
+ <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>
476
285
  </details>
477
286
  <details class="compat-row" data-status="ok">
478
287
  <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>
479
- <div class="compat-evidence"><div class="compat-probe"><code>unit:modular/queries.test.ts</code> ("query(query(ref, c1), c2) composes constraints")</div></div>
288
+ <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>
480
289
  </details>
481
290
  <details class="compat-row" data-status="ok">
482
291
  <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>
483
- <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>
292
+ <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>
484
293
  </details>
485
294
  <details class="compat-row" data-status="ok">
486
295
  <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>
487
- <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>
296
+ <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>
488
297
  </details>
489
298
  <details class="compat-row" data-status="ok">
490
299
  <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>
491
- <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>
300
+ <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>
492
301
  </details>
493
302
  <details class="compat-row" data-status="ok">
494
303
  <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>
495
- <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>
304
+ <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>
496
305
  </details>
497
306
  <details class="compat-row" data-status="ok">
498
307
  <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>
499
- <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>
308
+ <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>
500
309
  </details>
501
310
  <details class="compat-row" data-status="ok">
502
311
  <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 &#x26;&#x26; maxKey &#x3C; 2 * numKeys</code>) (DB-B2)</span></span></summary>
503
- <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>
312
+ <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>
504
313
  </details>
505
314
  <details class="compat-row" data-status="ok">
506
315
  <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>
507
- <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>
316
+ <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>
508
317
  </details>
509
318
  <details class="compat-row" data-status="ok">
510
319
  <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>
511
- <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>
320
+ <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>
512
321
  </details>
513
322
  <details class="compat-row" data-status="ok">
514
323
  <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>
515
- <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>
324
+ <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>
516
325
  </details>
517
326
  <details class="compat-row" data-status="ok">
518
327
  <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>
519
- <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>
328
+ <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>
520
329
  </details>
521
330
  <details class="compat-row" data-status="ok">
522
331
  <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>
523
- <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>
332
+ <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>
524
333
  </details>
525
334
  <details class="compat-row" data-status="ok">
526
335
  <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>
527
- <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>
336
+ <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>
528
337
  </details>
529
338
  <details class="compat-row" data-status="ok">
530
339
  <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>
531
- <div class="compat-evidence"><div class="compat-probe">Sandbox aligned: <code>unit:modular/root-primitive.test.ts</code> (2 cases)</div></div>
340
+ <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>
532
341
  </details>
533
342
  <details class="compat-row" data-status="ok">
534
343
  <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>
535
- <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>
344
+ <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>
536
345
  </details>
537
346
  <details class="compat-row" data-status="ok">
538
347
  <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>
539
- <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>
348
+ <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>
540
349
  </details>
541
350
  <details class="compat-row" data-status="ok">
542
351
  <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>
543
- <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>
352
+ <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>
544
353
  </details>
545
354
  <details class="compat-row" data-status="ok">
546
355
  <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>
547
- <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>
356
+ <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>
548
357
  </details>
549
358
  <details class="compat-row" data-status="ok">
550
359
  <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>
551
- <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>
360
+ <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>
552
361
  </details>
553
362
  <details class="compat-row" data-status="ok">
554
363
  <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>
555
- <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>
364
+ <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>
556
365
  </details>
557
366
  <details class="compat-row" data-status="ok">
558
367
  <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>
559
- <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>
368
+ <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>
560
369
  </details>
561
370
  <details class="compat-row" data-status="ok">
562
371
  <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>
563
- <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>
372
+ <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>
564
373
  </details>
565
374
  <details class="compat-row" data-status="ok">
566
375
  <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>
567
- <div class="compat-evidence"><div class="compat-probe">Oracle <code>rtdb-modular-ondisconnect-registration</code>; sandbox registration/no-mutation test.</div></div>
376
+ <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>
568
377
  </details>
569
378
  <details class="compat-row" data-status="ok">
570
379
  <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>
571
- <div class="compat-evidence"><div class="compat-probe">Oracle <code>rtdb-modular-ondisconnect-clean-set</code>; sandbox clean lifecycle test.</div></div>
380
+ <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>
572
381
  </details>
573
382
  <details class="compat-row" data-status="ok">
574
383
  <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>
575
- <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>
384
+ <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>
576
385
  </details>
577
386
  <details class="compat-row" data-status="ok">
578
387
  <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>
579
- <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>
388
+ <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>
580
389
  </details>
581
390
  <details class="compat-row" data-status="ok">
582
391
  <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>
583
- <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>
392
+ <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>
584
393
  </details>
585
394
  <details class="compat-row" data-status="ok">
586
395
  <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>
587
- <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>
396
+ <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>
588
397
  </details>
589
398
  <details class="compat-row" data-status="diverged">
590
399
  <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>
591
- <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>
400
+ <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>
592
401
  </details>
593
402
  <details class="compat-row" data-status="ok">
594
403
  <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>
595
- <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>
404
+ <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>
596
405
  </details>
597
406
  <details class="compat-row" data-status="ok">
598
407
  <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>
599
- <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>
408
+ <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>
600
409
  </details>
601
410
  <details class="compat-row" data-status="ok">
602
411
  <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>
603
- <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>
412
+ <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>
604
413
  </details>
605
414
  <details class="compat-row" data-status="ok">
606
415
  <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>
607
- <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>
416
+ <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>
608
417
  </details>
609
418
  <details class="compat-row" data-status="ok">
610
419
  <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>
611
- <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>
420
+ <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>
612
421
  </details>
613
422
  <details class="compat-row" data-status="ok">
614
423
  <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>
615
- <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>
424
+ <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>
616
425
  </details>
617
426
  <details class="compat-row" data-status="ok">
618
427
  <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>
619
- <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>
428
+ <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>
620
429
  </details>
621
430
  <details class="compat-row" data-status="ok">
622
431
  <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>
623
- <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>
432
+ <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>
624
433
  </details>
625
434
  <details class="compat-row" data-status="diverged">
626
435
  <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>
627
- <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>
436
+ <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>
628
437
  </details>
629
438
  <details class="compat-row" data-status="ok">
630
439
  <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>
631
- <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>
440
+ <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>
632
441
  </details>
633
442
  </div>
634
443
  <h3 id="getdatabasetarget--initializer"><code>getDatabase(target)</code> — initializer</h3>
635
444
  <div class="compat-list">
636
445
  <details class="compat-row" data-status="ok">
637
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"><code>getDatabase(ctx)</code> returns a tagged sandbox-target handle (frozen identity)</span></span></summary>
638
- <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>
447
+ <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>
639
448
  </details>
640
449
  <details class="compat-row" data-status="ok">
641
450
  <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>
642
- <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>
451
+ <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>
643
452
  </details>
644
453
  <details class="compat-row" data-status="ok">
645
454
  <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>
646
- <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>
455
+ <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>
647
456
  </details>
648
457
  <details class="compat-row" data-status="ok">
649
458
  <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>
650
- <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>
459
+ <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>
651
460
  <div class="compat-note">(wrap, fixture passing)</div></div>
652
461
  </details>
653
462
  <details class="compat-row" data-status="ok">
654
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">Two <code>getDatabase(sandbox)</code> calls share state (same underlying <code>LocalEnvironment</code>)</span></span></summary>
655
- <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>
464
+ <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>
656
465
  </details>
657
466
  <details class="compat-row" data-status="ok">
658
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">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>
659
- <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>
468
+ <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>
660
469
  </details>
661
470
  </div>
662
471
  <h3 id="refdb-path--child--parent--root"><code>ref(db, path)</code> / <code>child</code> / <code>parent</code> / <code>root</code></h3>
663
472
  <div class="compat-list">
664
473
  <details class="compat-row" data-status="ok">
665
474
  <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>
666
- <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>
475
+ <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>
667
476
  </details>
668
477
  <details class="compat-row" data-status="ok">
669
478
  <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>
670
- <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>
479
+ <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>
671
480
  </details>
672
481
  <details class="compat-row" data-status="ok">
673
482
  <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>
674
- <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>
483
+ <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>
675
484
  </details>
676
485
  <details class="compat-row" data-status="ok">
677
486
  <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>
678
- <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>
487
+ <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>
679
488
  </details>
680
489
  <details class="compat-row" data-status="ok">
681
490
  <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>
682
- <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>
491
+ <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>
683
492
  </details>
684
493
  <details class="compat-row" data-status="ok">
685
494
  <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>
686
- <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>
495
+ <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>
687
496
  </details>
688
497
  </div>
689
498
  <h3 id="getref--single-read"><code>get(ref)</code> — single read</h3>
690
499
  <div class="compat-list">
691
500
  <details class="compat-row" data-status="ok">
692
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">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>
693
- <div class="compat-evidence"><div class="compat-probe">oracle: <code>packages/conformance/observations/rtdb-modular/rtdb-modular-get-snapshot-shape.json</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-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>
694
503
  </details>
695
504
  <details class="compat-row" data-status="ok">
696
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"><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>
697
- <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>
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-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>
698
507
  </details>
699
508
  <details class="compat-row" data-status="ok">
700
509
  <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>
701
- <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>
510
+ <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>
702
511
  </details>
703
512
  <details class="compat-row" data-status="ok">
704
513
  <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>
705
- <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>
514
+ <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>
706
515
  </details>
707
516
  <details class="compat-row" data-status="ok">
708
517
  <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>
709
- <div class="compat-evidence"><div class="compat-probe">oracle: <code>packages/conformance/observations/rtdb/rtdb-rules-denied-error-code.json</code></div></div>
518
+ <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>
710
519
  </details>
711
520
  </div>
712
521
  <h3 id="setref-value--full-write"><code>set(ref, value)</code> — full write</h3>
713
522
  <div class="compat-list">
714
523
  <details class="compat-row" data-status="ok">
715
524
  <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&#x3C;void></code>)</span></span></summary>
716
- <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>
525
+ <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>
717
526
  </details>
718
527
  <details class="compat-row" data-status="ok">
719
528
  <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>
720
- <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>
529
+ <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>
721
530
  </details>
722
531
  <details class="compat-row" data-status="ok">
723
532
  <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>
724
- <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>
533
+ <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>
725
534
  </details>
726
535
  <details class="compat-row" data-status="ok">
727
536
  <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>
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>
537
+ <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
538
  </details>
730
539
  <details class="compat-row" data-status="ok">
731
540
  <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>
732
- <div class="compat-evidence"><div class="compat-probe">oracle: <code>packages/conformance/observations/rtdb/rtdb-rules-denied-error-code.json</code></div></div>
541
+ <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>
733
542
  </details>
734
543
  </div>
735
544
  <h3 id="updateref-values--partial--multi-path-update"><code>update(ref, values)</code> — partial / multi-path update</h3>
736
545
  <div class="compat-list">
737
546
  <details class="compat-row" data-status="ok">
738
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"><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>
739
- <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>
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-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>
740
549
  </details>
741
550
  <details class="compat-row" data-status="ok">
742
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">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>
743
- <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>
552
+ <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>
744
553
  </details>
745
554
  <details class="compat-row" data-status="ok">
746
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">Multi-path update is atomic: if any path is denied by rules, the entire update rejects and no path is written</span></span></summary>
747
- <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>
556
+ <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>
748
557
  </details>
749
558
  <details class="compat-row" data-status="ok">
750
559
  <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>
751
- <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>
560
+ <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>
752
561
  </details>
753
562
  <details class="compat-row" data-status="ok">
754
563
  <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>
755
- <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>
564
+ <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>
756
565
  </details>
757
566
  </div>
758
567
  <h3 id="removeref--delete"><code>remove(ref)</code> — delete</h3>
759
568
  <div class="compat-list">
760
569
  <details class="compat-row" data-status="ok">
761
570
  <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>
762
- <div class="compat-evidence"><div class="compat-probe">oracle: <code>packages/conformance/observations/rtdb/rtdb-remove-vs-set-null.json</code></div></div>
571
+ <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>
763
572
  </details>
764
573
  <details class="compat-row" data-status="ok">
765
574
  <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>
766
- <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>
575
+ <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>
767
576
  </details>
768
577
  <details class="compat-row" data-status="ok">
769
578
  <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>
770
- <div class="compat-evidence"><div class="compat-probe">oracle: <code>packages/conformance/observations/rtdb/rtdb-remove-vs-set-null.json</code></div></div>
579
+ <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>
771
580
  </details>
772
581
  </div>
773
582
  <h3 id="pushref-value--auto-id-append"><code>push(ref, value?)</code> — auto-id append</h3>
774
583
  <div class="compat-list">
775
584
  <details class="compat-row" data-status="ok">
776
585
  <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>
777
- <div class="compat-evidence"><div class="compat-probe">oracle: <code>packages/conformance/observations/rtdb/rtdb-push-autoid-format.json</code></div></div>
586
+ <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>
778
587
  </details>
779
588
  <details class="compat-row" data-status="ok">
780
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">Sequential <code>push</code> calls produce monotonically-sortable keys (timestamp-prefixed for chronological ordering via <code>orderByKey</code>)</span></span></summary>
781
- <div class="compat-evidence"><div class="compat-probe">oracle: <code>packages/conformance/observations/rtdb/rtdb-push-autoid-format.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-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>
782
591
  </details>
783
592
  <details class="compat-row" data-status="ok">
784
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"><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>
785
- <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>
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-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>
786
595
  </details>
787
596
  <details class="compat-row" data-status="ok">
788
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">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>
789
- <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>
598
+ <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>
790
599
  </details>
791
600
  </div>
792
601
  <h3 id="onvalueref-cb--value-level-listener"><code>onValue(ref, cb)</code> — value-level listener</h3>
793
602
  <div class="compat-list">
794
603
  <details class="compat-row" data-status="ok">
795
604
  <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>
796
- <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>
605
+ <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>
797
606
  </details>
798
607
  <details class="compat-row" data-status="ok">
799
608
  <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>
800
- <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>
609
+ <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>
801
610
  </details>
802
611
  <details class="compat-row" data-status="ok">
803
612
  <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>
804
- <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>
613
+ <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>
805
614
  </details>
806
615
  <details class="compat-row" data-status="ok">
807
616
  <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>
808
- <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>
617
+ <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>
809
618
  </details>
810
619
  <details class="compat-row" data-status="ok">
811
620
  <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>
812
- <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>
621
+ <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>
813
622
  </details>
814
623
  </div>
815
624
  <h3 id="onchildadded--onchildchanged--onchildremoved--onchildmoved"><code>onChildAdded</code> / <code>onChildChanged</code> / <code>onChildRemoved</code> / <code>onChildMoved</code></h3>
816
625
  <div class="compat-list">
817
626
  <details class="compat-row" data-status="ok">
818
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"><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>
819
- <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>
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-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>
820
629
  </details>
821
630
  <details class="compat-row" data-status="ok">
822
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">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>
823
- <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>
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-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>
824
633
  </details>
825
634
  <details class="compat-row" data-status="ok">
826
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"><code>onChildChanged</code> fires when an existing child's value changes; does NOT fire for added or removed children</span></span></summary>
827
- <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>
636
+ <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>
828
637
  </details>
829
638
  <details class="compat-row" data-status="ok">
830
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"><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>
831
- <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>
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-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>
832
641
  </details>
833
642
  <details class="compat-row" data-status="ok">
834
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"><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>
835
- <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>
644
+ <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>
836
645
  </details>
837
646
  </div>
838
647
  <h3 id="offref-eventtype-callback--unsubscribe-variants"><code>off(ref, eventType?, callback?)</code> — unsubscribe variants</h3>
839
648
  <div class="compat-list">
840
649
  <details class="compat-row" data-status="ok">
841
650
  <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>
842
- <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>
651
+ <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>
843
652
  </details>
844
653
  <details class="compat-row" data-status="ok">
845
654
  <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>
846
- <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>
655
+ <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>
847
656
  </details>
848
657
  <details class="compat-row" data-status="ok">
849
658
  <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>
850
- <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>
659
+ <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>
851
660
  </details>
852
661
  <details class="compat-row" data-status="ok">
853
662
  <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>
854
- <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>
663
+ <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>
855
664
  </details>
856
665
  <details class="compat-row" data-status="ok">
857
666
  <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>
858
- <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>
667
+ <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>
859
668
  </details>
860
669
  </div>
861
670
  <h3 id="queryref-constraints--ordering--bounds--limits"><code>query(ref, ...constraints)</code> + ordering / bounds / limits</h3>
862
671
  <div class="compat-list">
863
672
  <details class="compat-row" data-status="ok">
864
673
  <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>
865
- <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>
674
+ <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>
866
675
  </details>
867
676
  <details class="compat-row" data-status="ok">
868
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>orderByKey()</code> orders by the auto-id / numeric key</span></span></summary>
869
- <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>
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-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>
870
679
  </details>
871
680
  <details class="compat-row" data-status="diverged">
872
681
  <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>
873
- <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>
682
+ <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>
874
683
  </details>
875
684
  <details class="compat-row" data-status="ok">
876
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>equalTo(v)</code> filters children whose ordered field === v (returns 0, 1, or multiple matches — RTDB does NOT enforce uniqueness)</span></span></summary>
877
- <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>
686
+ <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>
878
687
  </details>
879
688
  <details class="compat-row" data-status="ok">
880
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"><code>startAt(v)</code> is <strong>inclusive</strong> (the child whose ordered value === v is included)</span></span></summary>
881
- <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>
690
+ <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>
882
691
  </details>
883
692
  <details class="compat-row" data-status="ok">
884
693
  <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>
885
- <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>
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-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>
886
695
  </details>
887
696
  <details class="compat-row" data-status="ok">
888
697
  <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>
889
- <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>
698
+ <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>
890
699
  </details>
891
700
  <details class="compat-row" data-status="ok">
892
701
  <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>
893
- <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>
702
+ <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>
894
703
  </details>
895
704
  <details class="compat-row" data-status="ok">
896
705
  <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>
897
- <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>
706
+ <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>
898
707
  </details>
899
708
  <details class="compat-row" data-status="ok">
900
709
  <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>
901
- <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>
710
+ <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>
902
711
  </details>
903
712
  <details class="compat-row" data-status="ok">
904
713
  <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>
905
- <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>
714
+ <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>
906
715
  </details>
907
716
  </div>
908
717
  <h3 id="sentinels--servertimestamp--incrementn">Sentinels — <code>serverTimestamp()</code> / <code>increment(n)</code></h3>
909
718
  <div class="compat-list">
910
719
  <details class="compat-row" data-status="ok">
911
720
  <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>
912
- <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>
721
+ <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>
913
722
  </details>
914
723
  <details class="compat-row" data-status="ok">
915
724
  <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>
916
- <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>
725
+ <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>
917
726
  </details>
918
727
  <details class="compat-row" data-status="ok">
919
728
  <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>
920
- <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>
729
+ <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>
921
730
  </details>
922
731
  <details class="compat-row" data-status="ok">
923
732
  <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>
924
- <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>
733
+ <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>
925
734
  </details>
926
735
  <details class="compat-row" data-status="diverged">
927
736
  <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>
928
- <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>
737
+ <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>
929
738
  </details>
930
739
  </div>
931
740
  <h3 id="runtransactionref-transactionupdate-options--optimistic-concurrency"><code>runTransaction(ref, transactionUpdate, options?)</code> — optimistic concurrency</h3>
932
741
  <div class="compat-list">
933
742
  <details class="compat-row" data-status="ok">
934
743
  <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 => (current ?? 0) + 1)</code> resolves <code>{ committed: true, snapshot }</code> where <code>snapshot.val()</code> is the new value</span></span></summary>
935
- <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 => (current ?? 0) + 1</code> against an empty ref.</div></div>
744
+ <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 => (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>
936
745
  </details>
937
746
  <details class="compat-row" data-status="ok">
938
747
  <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>
939
- <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>
748
+ <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>
940
749
  </details>
941
750
  <details class="compat-row" data-status="diverged">
942
751
  <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>
943
- <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>
752
+ <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>
944
753
  </details>
945
754
  <details class="compat-row" data-status="diverged">
946
755
  <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>
947
- <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>
756
+ <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>
948
757
  </details>
949
758
  <details class="compat-row" data-status="ok">
950
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">Result snapshot's <code>.val()</code> reflects the committed value (or the existing value if aborted)</span></span></summary>
951
- <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>
760
+ <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>
952
761
  </details>
953
762
  </div>
954
763
  <h3 id="goonline--gooffline--connection-control"><code>goOnline</code> / <code>goOffline</code> — connection control</h3>
955
764
  <div class="compat-list">
956
765
  <details class="compat-row" data-status="diverged">
957
766
  <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>
958
- <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>
767
+ <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>
959
768
  <div class="compat-note">logical disconnect lifecycle is modeled, but the in-memory data plane itself remains available</div></div>
960
769
  </details>
961
770
  <details class="compat-row" data-status="diverged">
962
771
  <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>
963
- <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>
772
+ <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>
964
773
  <div class="compat-note">logical disconnect lifecycle is modeled, but the in-memory data plane itself remains available</div></div>
965
774
  </details>
966
775
  </div>
967
- <h3 id="connectdatabaseemulator--emulator-hook"><code>connectDatabaseEmulator</code> — emulator hook</h3>
968
- <div class="compat-list">
969
- <details class="compat-row" data-status="unsupported">
970
- <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>
971
- <div class="compat-evidence"><div class="compat-probe">Phase 3</div></div>
972
- </details>
973
- <details class="compat-row" data-status="unsupported">
974
- <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>
975
- <div class="compat-evidence"><div class="compat-probe">Phase 3</div></div>
976
- </details>
977
- </div>
978
776
  <h3 id="transport-logging-and-url-reference-exports">Transport, logging, and URL-reference exports</h3>
979
777
  <div class="compat-list">
980
778
  <details class="compat-row" data-status="diverged">
981
779
  <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>
982
- <div class="compat-evidence"><div class="compat-probe"><code>unit:modular/fruit-aliases.test.ts</code></div>
780
+ <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>
983
781
  <div class="compat-note">transport selection not applicable to the in-process/worker sandbox</div></div>
984
782
  </details>
985
783
  <details class="compat-row" data-status="diverged">
986
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"><code>forceWebSockets()</code> — accepted no-op: transport selection is not applicable to the in-process/worker sandbox (see <code>forceLongPolling</code>)</span></span></summary>
987
- <div class="compat-evidence"><div class="compat-probe"><code>unit:modular/fruit-aliases.test.ts</code></div>
785
+ <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>
988
786
  <div class="compat-note">transport selection not applicable to the in-process/worker sandbox</div></div>
989
787
  </details>
990
788
  <details class="compat-row" data-status="diverged">
991
789
  <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>
992
- <div class="compat-evidence"><div class="compat-probe"><code>unit:modular/fruit-aliases.test.ts</code></div>
790
+ <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>
993
791
  <div class="compat-note">accepted no-op; no sandbox logger to wire into</div></div>
994
792
  </details>
995
793
  <details class="compat-row" data-status="diverged">
996
794
  <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>
997
- <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>
795
+ <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>
998
796
  <div class="compat-note">path and validation behavior conform except URL host/namespace matching (single-database sandbox)</div></div>
999
797
  </details>
1000
798
  </div>
@@ -1002,80 +800,34 @@ tooling coverage and are not exports of <code>pyric/database</code>.</p>
1002
800
  <h3 id="documented-divergences">Documented divergences</h3>
1003
801
  <p>Known differences between Pyric and production Firebase. Each remains tracked as a non-conforming row.</p>
1004
802
  <div class="compat-list">
1005
- <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>
1006
- <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>
1007
803
  <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>
1008
- <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>
804
+ <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>
1009
805
  <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>
1010
- <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>
806
+ <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>
1011
807
  <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>
1012
- <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>
808
+ <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>
1013
809
  <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>
1014
- <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>
810
+ <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>
1015
811
  <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>
1016
- <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>
812
+ <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>
1017
813
  <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>
1018
- <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>
814
+ <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>
1019
815
  <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>
1020
- <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>
816
+ <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>
1021
817
  <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>
1022
- <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>
818
+ <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>
1023
819
  <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>
1024
- <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>
820
+ <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>
1025
821
  <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>
1026
- <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>
822
+ <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>
1027
823
  <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>
1028
- <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>
824
+ <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>
1029
825
  <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>
1030
- <div class="compat-evidence"><div class="compat-note"><code>unit:modular/fruit-aliases.test.ts</code></div></div></details>
826
+ <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>
1031
827
  <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>
1032
- <div class="compat-evidence"><div class="compat-note"><code>unit:modular/fruit-aliases.test.ts</code></div></div></details>
828
+ <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>
1033
829
  <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>
1034
- <div class="compat-evidence"><div class="compat-note"><code>unit:modular/fruit-aliases.test.ts</code></div></div></details>
830
+ <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>
1035
831
  <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>
1036
- <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>
1037
- </div>
1038
- <h3 id="not-implemented-yet">Not implemented yet</h3>
1039
- <p>Tracked behavior that is not implemented in the current contract.</p>
1040
- <div class="compat-list">
1041
- <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>
1042
- <div class="compat-evidence"><div class="compat-note">Archived oracle observation; implementation removed.</div></div></details>
1043
- <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>
1044
- <div class="compat-evidence"><div class="compat-note">Archived oracle observations; implementation removed.</div></div></details>
1045
- <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>
1046
- <div class="compat-evidence"><div class="compat-note">Archived oracle observation; implementation removed.</div></div></details>
1047
- <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>
1048
- <div class="compat-evidence"><div class="compat-note">Archived oracle observations; implementation removed.</div></div></details>
1049
- <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>
1050
- <div class="compat-evidence"><div class="compat-note">Archived oracle observation; implementation removed.</div></div></details>
1051
- <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>
1052
- <div class="compat-evidence"><div class="compat-note">Archived oracle observation; implementation removed.</div></div></details>
1053
- <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>
1054
- <div class="compat-evidence"><div class="compat-note">Archived oracle observation; implementation removed.</div></div></details>
1055
- <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>
1056
- <div class="compat-evidence"><div class="compat-note">Archived oracle observation; implementation removed.</div></div></details>
1057
- <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>
1058
- <div class="compat-evidence"><div class="compat-note">Archived oracle observation; implementation removed.</div></div></details>
1059
- <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>
1060
- <div class="compat-evidence"><div class="compat-note">Archived oracle observation; implementation removed.</div></div></details>
1061
- <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>
1062
- <div class="compat-evidence"><div class="compat-note">Archived oracle observation; implementation removed.</div></div></details>
1063
- <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>
1064
- <div class="compat-evidence"><div class="compat-note">Archived oracle observation; implementation removed.</div></div></details>
1065
- <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>
1066
- <div class="compat-evidence"><div class="compat-note">Archived oracle observation; implementation removed.</div></div></details>
1067
- <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>
1068
- <div class="compat-evidence"><div class="compat-note">Archived oracle observation; implementation removed.</div></div></details>
1069
- <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>
1070
- <div class="compat-evidence"><div class="compat-note">Archived oracle observation; implementation removed.</div></div></details>
1071
- <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>
1072
- <div class="compat-evidence"><div class="compat-note">Archived oracle observation; implementation removed.</div></div></details>
1073
- <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>
1074
- <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>
1075
- <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>
1076
- <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>
1077
- <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>
1078
- <div class="compat-evidence"><div class="compat-note">Phase 3</div></div></details>
1079
- <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>
1080
- <div class="compat-evidence"><div class="compat-note">Phase 3</div></div></details>
1081
- </div> </article> </main> <aside class="doc-toc" aria-label="On this page" data-astro-cid-mw7aashj> <h2 class="doc-toc-heading" data-astro-cid-mw7aashj>On this page</h2> <ul data-astro-cid-mw7aashj> <li data-depth="2" data-astro-cid-mw7aashj> <a href="#status-legend" data-astro-cid-mw7aashj>Status legend</a> </li><li data-depth="2" data-astro-cid-mw7aashj> <a href="#archived-production-toolkit-observations" data-astro-cid-mw7aashj>Archived production-toolkit observations</a> </li><li data-depth="2" data-astro-cid-mw7aashj> <a href="#simulatertdbrulescompiled-input--in-process-rule-evaluator" data-astro-cid-mw7aashj>simulateRtdbRules(compiled, input) — in-process rule evaluator</a> </li><li data-depth="2" data-astro-cid-mw7aashj> <a href="#constraint-authoring-surface-atoms--policies--compose--ruleset" data-astro-cid-mw7aashj>Constraint authoring surface (atoms / policies / compose / ruleset)</a> </li><li data-depth="2" data-astro-cid-mw7aashj> <a href="#compiled-rtdb-rules-tree--rules-json" data-astro-cid-mw7aashj>Compiled RTDB rules tree ↔ rules JSON</a> </li><li data-depth="2" data-astro-cid-mw7aashj> <a href="#modular-sdk-surface" data-astro-cid-mw7aashj>Modular SDK surface</a> </li><li data-depth="3" data-astro-cid-mw7aashj> <a href="#getdatabasetarget--initializer" data-astro-cid-mw7aashj>getDatabase(target) — initializer</a> </li><li data-depth="3" data-astro-cid-mw7aashj> <a href="#refdb-path--child--parent--root" data-astro-cid-mw7aashj>ref(db, path) / child / parent / root</a> </li><li data-depth="3" data-astro-cid-mw7aashj> <a href="#getref--single-read" data-astro-cid-mw7aashj>get(ref) — single read</a> </li><li data-depth="3" data-astro-cid-mw7aashj> <a href="#setref-value--full-write" data-astro-cid-mw7aashj>set(ref, value) — full write</a> </li><li data-depth="3" data-astro-cid-mw7aashj> <a href="#updateref-values--partial--multi-path-update" data-astro-cid-mw7aashj>update(ref, values) — partial / multi-path update</a> </li><li data-depth="3" data-astro-cid-mw7aashj> <a href="#removeref--delete" data-astro-cid-mw7aashj>remove(ref) — delete</a> </li><li data-depth="3" data-astro-cid-mw7aashj> <a href="#pushref-value--auto-id-append" data-astro-cid-mw7aashj>push(ref, value?) — auto-id append</a> </li><li data-depth="3" data-astro-cid-mw7aashj> <a href="#onvalueref-cb--value-level-listener" data-astro-cid-mw7aashj>onValue(ref, cb) — value-level listener</a> </li><li data-depth="3" data-astro-cid-mw7aashj> <a href="#onchildadded--onchildchanged--onchildremoved--onchildmoved" data-astro-cid-mw7aashj>onChildAdded / onChildChanged / onChildRemoved / onChildMoved</a> </li><li data-depth="3" data-astro-cid-mw7aashj> <a href="#offref-eventtype-callback--unsubscribe-variants" data-astro-cid-mw7aashj>off(ref, eventType?, callback?) — unsubscribe variants</a> </li><li data-depth="3" data-astro-cid-mw7aashj> <a href="#queryref-constraints--ordering--bounds--limits" data-astro-cid-mw7aashj>query(ref, ...constraints) + ordering / bounds / limits</a> </li><li data-depth="3" data-astro-cid-mw7aashj> <a href="#sentinels--servertimestamp--incrementn" data-astro-cid-mw7aashj>Sentinels — serverTimestamp() / increment(n)</a> </li><li data-depth="3" data-astro-cid-mw7aashj> <a href="#runtransactionref-transactionupdate-options--optimistic-concurrency" data-astro-cid-mw7aashj>runTransaction(ref, transactionUpdate, options?) — optimistic concurrency</a> </li><li data-depth="3" data-astro-cid-mw7aashj> <a href="#goonline--gooffline--connection-control" data-astro-cid-mw7aashj>goOnline / goOffline — connection control</a> </li><li data-depth="3" data-astro-cid-mw7aashj> <a href="#connectdatabaseemulator--emulator-hook" data-astro-cid-mw7aashj>connectDatabaseEmulator — emulator hook</a> </li><li data-depth="3" data-astro-cid-mw7aashj> <a href="#transport-logging-and-url-reference-exports" data-astro-cid-mw7aashj>Transport, logging, and URL-reference exports</a> </li><li data-depth="2" data-astro-cid-mw7aashj> <a href="#current-gaps" data-astro-cid-mw7aashj>Current gaps</a> </li><li data-depth="3" data-astro-cid-mw7aashj> <a href="#documented-divergences" data-astro-cid-mw7aashj>Documented divergences</a> </li><li data-depth="3" data-astro-cid-mw7aashj> <a href="#not-implemented-yet" data-astro-cid-mw7aashj>Not implemented yet</a> </li> </ul> </aside> </div> <script type="module">const a=document.querySelector("[data-api-symbol-filter]"),d=[...document.querySelectorAll("[data-api-symbol-group]")],n=document.querySelector("[data-api-symbol-empty]");a?.addEventListener("input",()=>{const t=a.value.trim().toLowerCase();let o=0;for(const i of d){let e=0;i.querySelectorAll("[data-api-symbol]").forEach(l=>{const r=!t||(l.dataset.apiSymbol??"").includes(t);l.hidden=!r,r&&e++}),i.hidden=e===0,o+=e}n&&(n.hidden=o>0)});</script> <script type="module">const s=new Map;document.querySelectorAll('.doc-toc a[href^="#"]').forEach(t=>s.set(decodeURIComponent(t.hash.slice(1)),t));if(s.size>0){const t=new Set,n=()=>{let o=null;for(const e of s.keys())if(t.has(e)){o=e;break}s.forEach((e,c)=>e.classList.toggle("active",c===o))},r=new IntersectionObserver(o=>{for(const e of o)e.isIntersecting?t.add(e.target.id):t.delete(e.target.id);n()},{rootMargin:"-72px 0px -60% 0px"});s.forEach((o,e)=>{const c=document.getElementById(e);c&&r.observe(c)})}</script> <script type="module">document.querySelector('.studio__nav [aria-current="page"]')?.scrollIntoView({block:"nearest",inline:"center"});</script> </body></html>
832
+ <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>
833
+ </div> </article> </main> <aside class="doc-toc" aria-label="On this page" data-astro-cid-mw7aashj> <h2 class="doc-toc-heading" data-astro-cid-mw7aashj>On this page</h2> <ul data-astro-cid-mw7aashj> <li data-depth="2" data-astro-cid-mw7aashj> <a href="#status-legend" data-astro-cid-mw7aashj>Status legend</a> </li><li data-depth="2" data-astro-cid-mw7aashj> <a href="#public-api" data-astro-cid-mw7aashj>Public API</a> </li><li data-depth="3" data-astro-cid-mw7aashj> <a href="#getdatabasetarget--initializer" data-astro-cid-mw7aashj>getDatabase(target) — initializer</a> </li><li data-depth="3" data-astro-cid-mw7aashj> <a href="#refdb-path--child--parent--root" data-astro-cid-mw7aashj>ref(db, path) / child / parent / root</a> </li><li data-depth="3" data-astro-cid-mw7aashj> <a href="#getref--single-read" data-astro-cid-mw7aashj>get(ref) — single read</a> </li><li data-depth="3" data-astro-cid-mw7aashj> <a href="#setref-value--full-write" data-astro-cid-mw7aashj>set(ref, value) — full write</a> </li><li data-depth="3" data-astro-cid-mw7aashj> <a href="#updateref-values--partial--multi-path-update" data-astro-cid-mw7aashj>update(ref, values) — partial / multi-path update</a> </li><li data-depth="3" data-astro-cid-mw7aashj> <a href="#removeref--delete" data-astro-cid-mw7aashj>remove(ref) — delete</a> </li><li data-depth="3" data-astro-cid-mw7aashj> <a href="#pushref-value--auto-id-append" data-astro-cid-mw7aashj>push(ref, value?) — auto-id append</a> </li><li data-depth="3" data-astro-cid-mw7aashj> <a href="#onvalueref-cb--value-level-listener" data-astro-cid-mw7aashj>onValue(ref, cb) — value-level listener</a> </li><li data-depth="3" data-astro-cid-mw7aashj> <a href="#onchildadded--onchildchanged--onchildremoved--onchildmoved" data-astro-cid-mw7aashj>onChildAdded / onChildChanged / onChildRemoved / onChildMoved</a> </li><li data-depth="3" data-astro-cid-mw7aashj> <a href="#offref-eventtype-callback--unsubscribe-variants" data-astro-cid-mw7aashj>off(ref, eventType?, callback?) — unsubscribe variants</a> </li><li data-depth="3" data-astro-cid-mw7aashj> <a href="#queryref-constraints--ordering--bounds--limits" data-astro-cid-mw7aashj>query(ref, ...constraints) + ordering / bounds / limits</a> </li><li data-depth="3" data-astro-cid-mw7aashj> <a href="#sentinels--servertimestamp--incrementn" data-astro-cid-mw7aashj>Sentinels — serverTimestamp() / increment(n)</a> </li><li data-depth="3" data-astro-cid-mw7aashj> <a href="#runtransactionref-transactionupdate-options--optimistic-concurrency" data-astro-cid-mw7aashj>runTransaction(ref, transactionUpdate, options?) — optimistic concurrency</a> </li><li data-depth="3" data-astro-cid-mw7aashj> <a href="#goonline--gooffline--connection-control" data-astro-cid-mw7aashj>goOnline / goOffline — connection control</a> </li><li data-depth="3" data-astro-cid-mw7aashj> <a href="#transport-logging-and-url-reference-exports" data-astro-cid-mw7aashj>Transport, logging, and URL-reference exports</a> </li><li data-depth="2" data-astro-cid-mw7aashj> <a href="#current-gaps" data-astro-cid-mw7aashj>Current gaps</a> </li><li data-depth="3" data-astro-cid-mw7aashj> <a href="#documented-divergences" data-astro-cid-mw7aashj>Documented divergences</a> </li> </ul> </aside> </div> <script type="module">const a=document.querySelector("[data-api-symbol-filter]"),d=[...document.querySelectorAll("[data-api-symbol-group]")],n=document.querySelector("[data-api-symbol-empty]");a?.addEventListener("input",()=>{const t=a.value.trim().toLowerCase();let o=0;for(const i of d){let e=0;i.querySelectorAll("[data-api-symbol]").forEach(l=>{const r=!t||(l.dataset.apiSymbol??"").includes(t);l.hidden=!r,r&&e++}),i.hidden=e===0,o+=e}n&&(n.hidden=o>0)});</script> <script type="module">const s=new Map;document.querySelectorAll('.doc-toc a[href^="#"]').forEach(t=>s.set(decodeURIComponent(t.hash.slice(1)),t));if(s.size>0){const t=new Set,n=()=>{let o=null;for(const e of s.keys())if(t.has(e)){o=e;break}s.forEach((e,c)=>e.classList.toggle("active",c===o))},r=new IntersectionObserver(o=>{for(const e of o)e.isIntersecting?t.add(e.target.id):t.delete(e.target.id);n()},{rootMargin:"-72px 0px -60% 0px"});s.forEach((o,e)=>{const c=document.getElementById(e);c&&r.observe(c)})}</script> <script type="module">document.querySelector('.studio__nav [aria-current="page"]')?.scrollIntoView({block:"nearest",inline:"center"});</script> </body></html>