@konneal/engine 0.1.0

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 (285) hide show
  1. package/LICENSE +29 -0
  2. package/README.md +13 -0
  3. package/dist/admin.d.ts +26 -0
  4. package/dist/ai.d.ts +6 -0
  5. package/dist/anchors.d.ts +6 -0
  6. package/dist/answercache.d.ts +22 -0
  7. package/dist/ask.d.ts +5 -0
  8. package/dist/auth.d.ts +12 -0
  9. package/dist/bubble.d.ts +14 -0
  10. package/dist/chunk-LLWPT2XV.js +49 -0
  11. package/dist/chunk-MB74PTRM.js +114 -0
  12. package/dist/chunk-WOGQM7DJ.js +197 -0
  13. package/dist/chunk-WWNCWKKC.js +42 -0
  14. package/dist/completion.d.ts +5 -0
  15. package/dist/config.d.ts +154 -0
  16. package/dist/config.js +37 -0
  17. package/dist/context.d.ts +115 -0
  18. package/dist/conversations.d.ts +5 -0
  19. package/dist/drafts.d.ts +129 -0
  20. package/dist/env.d.ts +57 -0
  21. package/dist/faithfulness.d.ts +5 -0
  22. package/dist/grader.d.ts +3 -0
  23. package/dist/graph.d.ts +13 -0
  24. package/dist/hybrid.d.ts +7 -0
  25. package/dist/index.d.ts +9 -0
  26. package/dist/index.js +5373 -0
  27. package/dist/internal_gateway.d.ts +14 -0
  28. package/dist/lexical.d.ts +7 -0
  29. package/dist/livedata.d.ts +77 -0
  30. package/dist/memories.d.ts +10 -0
  31. package/dist/modelplane.d.ts +61 -0
  32. package/dist/oidc.d.ts +73 -0
  33. package/dist/pipeline.d.ts +57 -0
  34. package/dist/profile.d.ts +2 -0
  35. package/dist/profile.gen.d.ts +70 -0
  36. package/dist/profile.js +8 -0
  37. package/dist/projects.d.ts +8 -0
  38. package/dist/prompts/conversational.md +8 -0
  39. package/dist/prompts/enrichment.md +3 -0
  40. package/dist/prompts/faithfulness.md +1 -0
  41. package/dist/prompts/grader.md +5 -0
  42. package/dist/prompts/listwise.md +3 -0
  43. package/dist/prompts/precision.md +1 -0
  44. package/dist/prompts/reflect.md +1 -0
  45. package/dist/prompts/relevancy.md +1 -0
  46. package/dist/prompts/research.md +10 -0
  47. package/dist/prompts/section-summary.md +5 -0
  48. package/dist/prompts/summarize.md +1 -0
  49. package/dist/prompts/system.md +18 -0
  50. package/dist/prompts/understanding.md +17 -0
  51. package/dist/quota.d.ts +13 -0
  52. package/dist/reflect.d.ts +5 -0
  53. package/dist/refs.d.ts +40 -0
  54. package/dist/refusal.d.ts +9 -0
  55. package/dist/refusal.js +9 -0
  56. package/dist/requestScope.d.ts +26 -0
  57. package/dist/requestScope.js +10 -0
  58. package/dist/research.d.ts +8 -0
  59. package/dist/search.d.ts +4 -0
  60. package/dist/selfquery.d.ts +7 -0
  61. package/dist/session.d.ts +1 -0
  62. package/dist/share.d.ts +2 -0
  63. package/dist/structural.d.ts +27 -0
  64. package/dist/tablecontext.d.ts +11 -0
  65. package/dist/understand.d.ts +11 -0
  66. package/dist/understandContract.d.ts +29 -0
  67. package/dist/verdict.d.ts +24 -0
  68. package/docs/API.md +451 -0
  69. package/docs/ARCHITECTURE.md +302 -0
  70. package/docs/AUDIT-2026-08-24.md +71 -0
  71. package/docs/CONTRIBUTOR-AUDIT-2026-08-25.md +147 -0
  72. package/docs/INGEST-ARCHITECTURE.md +158 -0
  73. package/docs/MCP.md +92 -0
  74. package/docs/METANORMA-AI-SERIALIZATION.md +247 -0
  75. package/docs/MKO-EXPORT-PIPELINE.md +147 -0
  76. package/docs/REDESIGN-NORMATIVE-RAG-ETSI.md +485 -0
  77. package/docs/RESEARCH-SOTA-2026.md +243 -0
  78. package/docs/ROADMAP-SOTA.md +130 -0
  79. package/docs/SOTA-STAGE-SPECS.md +509 -0
  80. package/docs/annealment/F1-verdict.md +27 -0
  81. package/docs/annealment/F10-notes.md +19 -0
  82. package/docs/annealment/F11-composition.md +17 -0
  83. package/docs/annealment/F12-passport.md +17 -0
  84. package/docs/annealment/F2-counterfactual.md +20 -0
  85. package/docs/annealment/F3-absence.md +21 -0
  86. package/docs/annealment/F4-instance.md +18 -0
  87. package/docs/annealment/F5-workflow.md +21 -0
  88. package/docs/annealment/F6-impact.md +21 -0
  89. package/docs/annealment/F7-editions.md +18 -0
  90. package/docs/annealment/F8-selfverify.md +19 -0
  91. package/docs/annealment/F9-projection-qa.md +17 -0
  92. package/docs/annealment/L0-locate.md +19 -0
  93. package/docs/annealment/L1-extract.md +18 -0
  94. package/docs/annealment/L2-nomenclature.md +22 -0
  95. package/docs/annealment/L3-geometry.md +23 -0
  96. package/docs/annealment/L4-composition.md +21 -0
  97. package/docs/annealment/L5-cross-standard.md +20 -0
  98. package/docs/annealment/L6-diachrony.md +21 -0
  99. package/docs/annealment/L7-perception.md +20 -0
  100. package/docs/annealment/L8-computation.md +22 -0
  101. package/docs/annealment/L9-instance-process.md +23 -0
  102. package/docs/annealment/README.md +10 -0
  103. package/docs/guidelines-metanorma-ai-programme.md +279 -0
  104. package/docs/identity-onboarding-rag.md +65 -0
  105. package/docs/identity-service.md +219 -0
  106. package/docs/knowledge-annealment.md +273 -0
  107. package/docs/konneal-extraction-plan.md +481 -0
  108. package/docs/metanorma-for-ai.md +270 -0
  109. package/docs/mirror-plan.md +36 -0
  110. package/docs/multi-sdo-architecture.md +191 -0
  111. package/docs/paper-annealment-comparison.md +259 -0
  112. package/docs/paper-assets/architecture.svg +94 -0
  113. package/docs/paper-assets/contract-v2.svg +94 -0
  114. package/docs/paper-assets/mko-ingest.svg +91 -0
  115. package/docs/paper-oiml-bulletin.md +402 -0
  116. package/docs/paper-oiml-bulletin.mdx +419 -0
  117. package/docs/product-branding-options.md +172 -0
  118. package/docs/projects-design.md +88 -0
  119. package/docs/sota-mechanisms.md +184 -0
  120. package/docs/spec-api.md +77 -0
  121. package/docs/spec-pipeline.md +126 -0
  122. package/docs/vector-adapter.md +88 -0
  123. package/package.json +70 -0
  124. package/profile/corpora.yaml +5 -0
  125. package/profile/datasets.yaml +14 -0
  126. package/profile/prompts.yaml +5 -0
  127. package/profile/publisher.yaml +17 -0
  128. package/profile/retrieval.yaml +1 -0
  129. package/profile/sources.yaml +5 -0
  130. package/profile/ui.yaml +7 -0
  131. package/scripts/gen_profile.mjs +33 -0
  132. package/workers/shared/ai.ts +21 -0
  133. package/workers/shared/auth.ts +16 -0
  134. package/workers/shared/chunk.ts +108 -0
  135. package/workers/shared/oidc.ts +312 -0
  136. package/workers/shared/router.ts +45 -0
  137. package/workers/shared/session.ts +104 -0
  138. package/workers/worker_internal/src/index.ts +157 -0
  139. package/workers/worker_internal/tsconfig.json +15 -0
  140. package/workers/worker_internal/wrangler.toml +32 -0
  141. package/workers/worker_mcp/src/index.ts +175 -0
  142. package/workers/worker_mcp/tsconfig.json +13 -0
  143. package/workers/worker_mcp/wrangler.toml +18 -0
  144. package/workers/worker_public/migrations/0002_conversations.sql +22 -0
  145. package/workers/worker_public/migrations/0003_shared_conversations.sql +9 -0
  146. package/workers/worker_public/migrations/0004_graph.sql +16 -0
  147. package/workers/worker_public/migrations/0005_documents.sql +19 -0
  148. package/workers/worker_public/migrations/0006_conversation_entities.sql +11 -0
  149. package/workers/worker_public/migrations/0007_chunks_fts.sql +43 -0
  150. package/workers/worker_public/migrations/0008_unit_payloads.sql +15 -0
  151. package/workers/worker_public/migrations/0009_chunks_unit.sql +8 -0
  152. package/workers/worker_public/migrations/0009_message_context.sql +7 -0
  153. package/workers/worker_public/migrations/0010_model_nodes.sql +33 -0
  154. package/workers/worker_public/migrations/0011_schema_union.sql +38 -0
  155. package/workers/worker_public/migrations/0012_memories.sql +15 -0
  156. package/workers/worker_public/migrations/0013_projects.sql +21 -0
  157. package/workers/worker_public/node_modules/@cloudflare/workers-types/2021-11-03/index.d.ts +16306 -0
  158. package/workers/worker_public/node_modules/@cloudflare/workers-types/2021-11-03/index.ts +16261 -0
  159. package/workers/worker_public/node_modules/@cloudflare/workers-types/2022-01-31/index.d.ts +16373 -0
  160. package/workers/worker_public/node_modules/@cloudflare/workers-types/2022-01-31/index.ts +16328 -0
  161. package/workers/worker_public/node_modules/@cloudflare/workers-types/2022-03-21/index.d.ts +16382 -0
  162. package/workers/worker_public/node_modules/@cloudflare/workers-types/2022-03-21/index.ts +16337 -0
  163. package/workers/worker_public/node_modules/@cloudflare/workers-types/2022-08-04/index.d.ts +16383 -0
  164. package/workers/worker_public/node_modules/@cloudflare/workers-types/2022-08-04/index.ts +16338 -0
  165. package/workers/worker_public/node_modules/@cloudflare/workers-types/2022-10-31/index.d.ts +16403 -0
  166. package/workers/worker_public/node_modules/@cloudflare/workers-types/2022-10-31/index.ts +16358 -0
  167. package/workers/worker_public/node_modules/@cloudflare/workers-types/2022-11-30/index.d.ts +16408 -0
  168. package/workers/worker_public/node_modules/@cloudflare/workers-types/2022-11-30/index.ts +16363 -0
  169. package/workers/worker_public/node_modules/@cloudflare/workers-types/2023-03-01/index.d.ts +16414 -0
  170. package/workers/worker_public/node_modules/@cloudflare/workers-types/2023-03-01/index.ts +16369 -0
  171. package/workers/worker_public/node_modules/@cloudflare/workers-types/2023-07-01/index.d.ts +16414 -0
  172. package/workers/worker_public/node_modules/@cloudflare/workers-types/2023-07-01/index.ts +16369 -0
  173. package/workers/worker_public/node_modules/@cloudflare/workers-types/README.md +135 -0
  174. package/workers/worker_public/node_modules/@cloudflare/workers-types/entrypoints.svg +53 -0
  175. package/workers/worker_public/node_modules/@cloudflare/workers-types/experimental/index.d.ts +17095 -0
  176. package/workers/worker_public/node_modules/@cloudflare/workers-types/experimental/index.ts +17050 -0
  177. package/workers/worker_public/node_modules/@cloudflare/workers-types/index.d.ts +16306 -0
  178. package/workers/worker_public/node_modules/@cloudflare/workers-types/index.ts +16261 -0
  179. package/workers/worker_public/node_modules/@cloudflare/workers-types/latest/index.d.ts +16447 -0
  180. package/workers/worker_public/node_modules/@cloudflare/workers-types/latest/index.ts +16402 -0
  181. package/workers/worker_public/node_modules/@cloudflare/workers-types/oldest/index.d.ts +16306 -0
  182. package/workers/worker_public/node_modules/@cloudflare/workers-types/oldest/index.ts +16261 -0
  183. package/workers/worker_public/node_modules/@cloudflare/workers-types/package.json +11 -0
  184. package/workers/worker_public/package.json +13 -0
  185. package/workers/worker_public/prompts/conversational.md +8 -0
  186. package/workers/worker_public/prompts/enrichment.md +3 -0
  187. package/workers/worker_public/prompts/faithfulness.md +1 -0
  188. package/workers/worker_public/prompts/grader.md +5 -0
  189. package/workers/worker_public/prompts/listwise.md +3 -0
  190. package/workers/worker_public/prompts/precision.md +1 -0
  191. package/workers/worker_public/prompts/reflect.md +1 -0
  192. package/workers/worker_public/prompts/relevancy.md +1 -0
  193. package/workers/worker_public/prompts/research.md +10 -0
  194. package/workers/worker_public/prompts/section-summary.md +5 -0
  195. package/workers/worker_public/prompts/summarize.md +1 -0
  196. package/workers/worker_public/prompts/system.md +18 -0
  197. package/workers/worker_public/prompts/understanding.md +17 -0
  198. package/workers/worker_public/public/app.js +166 -0
  199. package/workers/worker_public/public/index.html +48 -0
  200. package/workers/worker_public/public/style.css +147 -0
  201. package/workers/worker_public/schema.sql +248 -0
  202. package/workers/worker_public/src/admin.ts +358 -0
  203. package/workers/worker_public/src/ai.ts +71 -0
  204. package/workers/worker_public/src/anchors.ts +41 -0
  205. package/workers/worker_public/src/answercache.ts +72 -0
  206. package/workers/worker_public/src/ask.ts +1094 -0
  207. package/workers/worker_public/src/auth.ts +252 -0
  208. package/workers/worker_public/src/bubble.ts +111 -0
  209. package/workers/worker_public/src/completion.ts +75 -0
  210. package/workers/worker_public/src/config.ts +238 -0
  211. package/workers/worker_public/src/context.ts +238 -0
  212. package/workers/worker_public/src/conversations.ts +162 -0
  213. package/workers/worker_public/src/drafts.ts +497 -0
  214. package/workers/worker_public/src/env.ts +90 -0
  215. package/workers/worker_public/src/faithfulness.ts +63 -0
  216. package/workers/worker_public/src/grader.ts +89 -0
  217. package/workers/worker_public/src/graph.ts +63 -0
  218. package/workers/worker_public/src/hybrid.ts +77 -0
  219. package/workers/worker_public/src/index.ts +441 -0
  220. package/workers/worker_public/src/internal_gateway.ts +41 -0
  221. package/workers/worker_public/src/lexical.ts +86 -0
  222. package/workers/worker_public/src/lib/hit.ts +4 -0
  223. package/workers/worker_public/src/lib/http.ts +83 -0
  224. package/workers/worker_public/src/lib/router.ts +4 -0
  225. package/workers/worker_public/src/livedata.ts +334 -0
  226. package/workers/worker_public/src/memories.ts +81 -0
  227. package/workers/worker_public/src/modelplane.ts +213 -0
  228. package/workers/worker_public/src/oidc.ts +333 -0
  229. package/workers/worker_public/src/pipeline.ts +377 -0
  230. package/workers/worker_public/src/ports/blobs.ts +7 -0
  231. package/workers/worker_public/src/ports/cloudflare/adapters.ts +177 -0
  232. package/workers/worker_public/src/ports/kv.ts +8 -0
  233. package/workers/worker_public/src/ports/model.ts +28 -0
  234. package/workers/worker_public/src/ports/runtime.ts +13 -0
  235. package/workers/worker_public/src/ports/store.ts +20 -0
  236. package/workers/worker_public/src/ports/vector.ts +26 -0
  237. package/workers/worker_public/src/profile.gen.ts +101 -0
  238. package/workers/worker_public/src/profile.ts +16 -0
  239. package/workers/worker_public/src/projects.ts +108 -0
  240. package/workers/worker_public/src/prompts.d.ts +6 -0
  241. package/workers/worker_public/src/quota.ts +54 -0
  242. package/workers/worker_public/src/reflect.ts +67 -0
  243. package/workers/worker_public/src/refs.ts +107 -0
  244. package/workers/worker_public/src/refusal.ts +65 -0
  245. package/workers/worker_public/src/requestScope.ts +71 -0
  246. package/workers/worker_public/src/research.ts +126 -0
  247. package/workers/worker_public/src/search.ts +56 -0
  248. package/workers/worker_public/src/selfquery.ts +25 -0
  249. package/workers/worker_public/src/session.ts +4 -0
  250. package/workers/worker_public/src/share.ts +53 -0
  251. package/workers/worker_public/src/stages/conceptGraph.ts +68 -0
  252. package/workers/worker_public/src/stages/conceptSteer.ts +39 -0
  253. package/workers/worker_public/src/stages/corpusScope.ts +25 -0
  254. package/workers/worker_public/src/stages/dedup.ts +10 -0
  255. package/workers/worker_public/src/stages/dense.ts +73 -0
  256. package/workers/worker_public/src/stages/diversity.ts +33 -0
  257. package/workers/worker_public/src/stages/editionCover.ts +63 -0
  258. package/workers/worker_public/src/stages/editionSteer.ts +88 -0
  259. package/workers/worker_public/src/stages/familyBoost.ts +22 -0
  260. package/workers/worker_public/src/stages/federate.ts +22 -0
  261. package/workers/worker_public/src/stages/glossary.ts +65 -0
  262. package/workers/worker_public/src/stages/graphLane.ts +31 -0
  263. package/workers/worker_public/src/stages/hyde.ts +29 -0
  264. package/workers/worker_public/src/stages/index.ts +69 -0
  265. package/workers/worker_public/src/stages/lexicalUnion.ts +21 -0
  266. package/workers/worker_public/src/stages/multiQuery.ts +57 -0
  267. package/workers/worker_public/src/stages/overviewDemote.ts +14 -0
  268. package/workers/worker_public/src/stages/poolOpen.ts +10 -0
  269. package/workers/worker_public/src/stages/propagate.ts +15 -0
  270. package/workers/worker_public/src/stages/rerank.ts +47 -0
  271. package/workers/worker_public/src/stages/seal.ts +16 -0
  272. package/workers/worker_public/src/stages/sectionDescent.ts +61 -0
  273. package/workers/worker_public/src/stages/stdRefNudge.ts +35 -0
  274. package/workers/worker_public/src/stages/subQuery.ts +42 -0
  275. package/workers/worker_public/src/stages/termNudge.ts +24 -0
  276. package/workers/worker_public/src/stages/typedPin.ts +131 -0
  277. package/workers/worker_public/src/stages/types.ts +112 -0
  278. package/workers/worker_public/src/stages/windowFloor.ts +23 -0
  279. package/workers/worker_public/src/structural.ts +171 -0
  280. package/workers/worker_public/src/tablecontext.ts +41 -0
  281. package/workers/worker_public/src/understand.ts +72 -0
  282. package/workers/worker_public/src/understandContract.ts +67 -0
  283. package/workers/worker_public/src/verdict.ts +255 -0
  284. package/workers/worker_public/tsconfig.json +18 -0
  285. package/workers/worker_public/wrangler.toml +104 -0
@@ -0,0 +1,158 @@
1
+ # Ingest Architecture — Object Input Interface → RAG Data Preprocessor → RAG Worker
2
+
3
+ *Formalizes the pipeline shape (2026-08-27). Companion pieces:
4
+ `SOTA-STAGE-SPECS.md` (schemas), `METANORMA-AI-SERIALIZATION.md` (upstream
5
+ serialization contract), `ROADMAP-SOTA.md` (phasing).*
6
+
7
+ ---
8
+
9
+ ## The principle: facts in source data, derivations in the preprocessor
10
+
11
+ The relaton audit made this concrete. Relaton's `status` FIELD lies in 58
12
+ records (claims in-force while carrying a successor edge) — because it is a
13
+ **derived fact stored as data**, the classic copy-drift bug. The
14
+ preprocessor therefore DERIVES status from successor edges and ignores the
15
+ field; nothing needed "fixing" for serving to be correct.
16
+
17
+ What derivation cannot invent: **missing edges**. 36 of 224 families have no
18
+ active edition because a successor edge was never recorded (e.g. `B 18:2016`
19
+ and `B 18:2017` both terminal). Those are genuine upstream gaps — they must
20
+ be fixed in relaton-data-oiml; the preprocessor's job is to SURFACE them
21
+ (the registry's no-active families are the worklist), not hide them.
22
+
23
+ | Data problem | Fix where | Mechanism |
24
+ |---|---|---|
25
+ | Status field contradicts edges (58) | nowhere — derive | preprocessor: `derived_status` |
26
+ | Language instances carry primaries | nowhere — collapse | preprocessor: canonical-id dedup |
27
+ | Non-publication records (Bulletins) | nowhere — filter | preprocessor: series filter |
28
+ | Missing successor edges (36 families) | **relaton-data-oiml** | registry worklist → upstream PRs |
29
+ | Wrong/missing citation edges | **relaton-data-oiml** | graph build report → upstream PRs |
30
+
31
+ The loop: preprocessor emits a data-quality report every build; the report
32
+ drives upstream fixes; upstream fixes flow back on the next build. Data
33
+ gets better because serving keeps score, not because anyone audits YAML by
34
+ hand.
35
+
36
+ ## The three layers
37
+
38
+ ```
39
+ ┌──────────────────────────────────────────────────────────────────┐
40
+ │ OBJECT INPUT INTERFACE (OII) │
41
+ │ The typed object contract. Four NATIVE object systems, each │
42
+ │ serialized from its own lutaml-model gem — never renderings: │
43
+ │ • Metanorma — document content model (metanorma-document) │
44
+ │ • Relaton — bibliographic records (relaton) │
45
+ │ • Glossarist — terminology concepts (glossarist) │
46
+ │ • PubID — canonical identifiers (pubid) │
47
+ │ Contract: the AI-serialization node projection (documents + │
48
+ │ typed nodes + edges), schema_versioned, validated at entry. │
49
+ └──────────────────────────┬───────────────────────────────────────┘
50
+ ▼
51
+ ┌──────────────────────────────────────────────────────────────────┐
52
+ │ RAG DATA PREPROCESSOR (RDP) — ingest/*, invariant-gated │
53
+ │ identity pubid-canonical DocumentRecord → D1 registry │
54
+ │ (derived status, active flags — the SSOT) │
55
+ │ structure typed nodes → ChunkRecordV2 (per block type) │
56
+ │ derivation registry + graph projection (relaton edges + │
57
+ │ glossarist defines edges) │
58
+ │ enrichment contextual contexts (quality-first lane, KV- │
59
+ │ cached, content-hash invalidated) │
60
+ │ embedding per-type embed_input → Vectorize (public/internal │
61
+ │ split — isolation is structural) │
62
+ │ verification invariants per stage + data-quality report → │
63
+ │ upstream worklist │
64
+ │ orchestration INDEX_VERSION bump → cache flush protocol │
65
+ └──────────────────────────┬───────────────────────────────────────┘
66
+ ▼
67
+ ┌──────────────────────────────────────────────────────────────────┐
68
+ │ RAG WORKERS — serving only, never parse source data │
69
+ │ rag-public ask/search pipeline + registry notes + graph lane │
70
+ │ rag-internal ISO federation (isolation-enforced) │
71
+ │ rag-mcp MCP tools for the agent ecosystem │
72
+ │ They consume: Vectorize + D1 (registry, graph) + KV (caches) │
73
+ └──────────────────────────────────────────────────────────────────┘
74
+ ```
75
+
76
+ ## Current state vs the target
77
+
78
+ | Component | State |
79
+ |---|---|
80
+ | OII | de-facto: parse.py (HTML-first) + graph.py (relaton/glossarist). Target: consume the four native object serializations; the AI-serialization proposal (metanorma#592) is the upstream contract |
81
+ | Identity SSOT | ✅ documents registry in D1 (derived status, active flags, supersession chains) |
82
+ | Registry serving | ✅ edition notes injected into doc-scoped asks; `GET /api/documents?family=` public API |
83
+ | Graph projection | ✅ 7,128 nodes / 6,486 edges in D1; query lane live (defined_terms → defines → candidates) |
84
+ | Chunking | prose-only today; ChunkRecordV2 typed blocks = next build (tables/equations/requirements) |
85
+ | Enrichment | ✅ 98.7% corpus (the residual ~400 are persistent empty enrichments), KV-cached, replayed from the durable record after full restores |
86
+ | Verification | unit suites + contract tests in CI (`npm run test:units`, pydantic↔TS wire contract, schema-union drift); the data-quality report loop = next build |
87
+ | Orchestration | manual commands today; one-command pipeline (`ingest run --doc X`) when the above land |
88
+
89
+ ## Corpus operations (the wire loop, as of 2026-09-08)
90
+
91
+ ```
92
+ parse corpora → artifacts/chunks.jsonl (identifiers sanitized,
93
+ placeholders never win — language markers and :0000/:XXXX
94
+ fall to the slug-derived identity)
95
+ retrieval-plane primmel export → artifacts/model_retrieval_chunks.jsonl
96
+ (retrieval text/facets); the projection derives
97
+ artifacts/model_typed_chunks.jsonl (the ONLY unit_id/
98
+ block source) — separate files: one shared name had the
99
+ two derivations overwriting each other
100
+ embed content-aware resume: artifacts/embed_text_hashes.json
101
+ (id → text hash) — changed text re-embeds, untouched ids
102
+ never do (model chunk ids are content-independent hashes;
103
+ id-keyed resume alone would pair stale vectors with fresh
104
+ metadata)
105
+ upsert the internal worker's /admin/sync binding route
106
+ (rag-internal.<account>.workers.dev — no REST token
107
+ needed); the CLI's REST path defaults to the production
108
+ index idx_oiml_public_v2
109
+ restore scripts/restore_missing.py [sources...] [--batch N --pace S] —
110
+ reconcile's twin: reconcile deletes strays, restore fills
111
+ gaps (presence probe, then upsert only missing ids; paced
112
+ under the rolling Vectorize upsert quota — sustained
113
+ 25-50-vector batches clamp after ~1k vectors; 10 × 25s
114
+ sustains; skips hard-failed batches, rerun to pick them
115
+ up)
116
+ enrich-replay scripts/replay_enrichment.py [--apply --pace S] — REQUIRED
117
+ after every full upsert OR raw restore: enrichment lives
118
+ only in the index (and the KV context cache), so both
119
+ overwrite it with raw text; the replay re-embeds
120
+ context+text from the durable record
121
+ (artifacts/enriched-contexts.jsonl) via the binding —
122
+ zero model generation, ≈$0.30
123
+ reconcile scripts/reconcile_index.py — enumerate the index (wrangler
124
+ list-vectors), diff against the canonical chunk set
125
+ declared ONCE in ingest/config.py
126
+ (CANONICAL_CHUNK_SOURCES — four derivations: prose parse,
127
+ retrieval plane, projection, MKO typed units), delete
128
+ strays (--apply). Upserts never delete; this closes the
129
+ loop.
130
+ cache scripts/invalidate_answer_cache.py (or wrangler kv put
131
+ sys:corpus_gen) after any corpus surgery
132
+ assets scripts/fix_figure_assets.py [--apply] — unit assets must
133
+ stay vision-readable (black-on-transparent rasters read
134
+ as solid black after alpha flattening); detects and
135
+ re-uploads white-flattened
136
+ documents scripts/upload_documents.py [--apply] — the rendered
137
+ publications for citation deep links (R2 docs/<slug>.html
138
+ + clause-anchor maps, served at /docs/*); rerun when the
139
+ clean corpus's renderings change
140
+ graph ingest.cli graph (build) + graph --corpus apply (D1,
141
+ wrangler; absolute --file path)
142
+ gates scripts/gates.sh — golden ×N + annealment ×M
143
+ ```
144
+
145
+ The vector adapter (`ingest/vector_adapter.py`, contract in
146
+ `docs/vector-adapter.md`) remains the ONLY door from any producer to any
147
+ index: wire schema + target gating.
148
+
149
+ ## PubID as the identity backbone
150
+
151
+ Node ids, registry keys, and chunk metadata all key on the canonical
152
+ identifier (`OIML R 60-1:2021`). Today graph.py normalizes identifiers with
153
+ its own regex; the target is the **pubid** gem's parser/generator as the
154
+ single authority (parse any spelling → canonical form → round-trip), so
155
+ `R60`, `OIML R60:2021 (E)`, and `r 60-1` all collapse to one identity
156
+ before they touch the registry. This is also an upstream piece of the
157
+ AI-serialization bundle: identifiers in the projection should BE pubid
158
+ objects serialized.
package/docs/MCP.md ADDED
@@ -0,0 +1,92 @@
1
+ # OIML SMART AI — MCP Server
2
+
3
+ *Agent-native access to the OIML publications corpus via the Model
4
+ Context Protocol. For chat/API usage see [API.md](API.md); MCP is the
5
+ machine contract — including the publication registry, which is
6
+ deliberately NOT part of the chatbot API.*
7
+
8
+ ---
9
+
10
+ ## 1. Connecting
11
+
12
+ **Endpoint:** `https://rag-mcp.oimlsmart-06c.workers.dev/mcp`
13
+ **Transport:** Streamable HTTP (MCP `2025-06-18`) — JSON-RPC 2.0 over
14
+ `POST /mcp`, stateless. `GET /` returns a service descriptor.
15
+
16
+ **Claude Desktop** (`claude_desktop_config.json`):
17
+ ```json
18
+ {
19
+ "mcpServers": {
20
+ "oiml": {
21
+ "type": "http",
22
+ "url": "https://rag-mcp.oimlsmart-06c.workers.dev/mcp"
23
+ }
24
+ }
25
+ }
26
+ ```
27
+ **Any MCP client:** point it at the URL above; no session handshake
28
+ state is required beyond the standard `initialize` call.
29
+
30
+ **Authorization:** the server is public-tier by default. Setting the
31
+ `RAG_API_KEY` secret (operator) raises its quota class; per-caller auth
32
+ is passed through the underlying API key if configured.
33
+
34
+ ## 2. Tools
35
+
36
+ ### `oiml_search` — ranked passage retrieval
37
+ ```jsonc
38
+ // input
39
+ { "query": "maximum permissible error load cell", "top_k": 3 } // top_k 1–10
40
+ ```
41
+ Returns passages as text blocks:
42
+ ```
43
+ OIML R 76-1:2006 §2.5.4 — 2.5.4 Maximum permissible error, mpe (T.5.4.4)
44
+ <snippet>
45
+ ```
46
+ Use for: scanning the corpus, gathering context, cheap lookups (no
47
+ generation cost).
48
+
49
+ ### `oiml_ask` — grounded, citation-linked answers
50
+ ```jsonc
51
+ { "query": "What does OIML R 60-1 require for verification intervals?" }
52
+ ```
53
+ Returns the full answer plus a `Sources:` line of cited passages. Every
54
+ claim cites the exact publication and clause; normative values carry
55
+ verbatim quote anchors. Off-corpus questions return the canonical
56
+ refusal sentence.
57
+
58
+ ### `oiml_documents` — publication registry (editions & status)
59
+ ```jsonc
60
+ { "family": "R-60" } // series letter + number, e.g. "R-60", "B-18", "D-11"
61
+ ```
62
+ Returns every edition of the family with derived status:
63
+ ```
64
+ OIML R 60:1991 — superseded → superseded by OIML R 60:2000
65
+ OIML R 60:2021 — in-force [ACTIVE]
66
+ OIML R 60-1:2017 — superseded → superseded by OIML R 60-1:2021
67
+ OIML R 60-1:2021 — in-force [ACTIVE]
68
+ …
69
+ ```
70
+ `[ACTIVE]` is DERIVED from successor edges (terminal node of the
71
+ supersession chain), not from a status field — relaton's status field
72
+ contradicts its own edges in ~9% of records. Use for: "current/latest
73
+ edition", edition history, supersession questions. A family with no
74
+ `[ACTIVE]` line means the bibliographic data has a gap (missing
75
+ successor edge) — the registry surfaces it rather than guessing.
76
+
77
+ ## 3. Protocol notes
78
+
79
+ - Notifications (no `id`) → `202`, no body
80
+ - Unknown method → `-32601`
81
+ - Tool failures → result with `isError: true` and the message (never a
82
+ dropped call)
83
+ - `initialize` responds with capabilities `{tools:{}}`; `tools/list`
84
+ returns the three tool definitions with JSON schemas
85
+
86
+ ## 4. Implementation & isolation
87
+
88
+ The server (`workers/worker_mcp`, `rag-mcp`) holds **no corpus access
89
+ of its own**: `oiml_search`/`oiml_ask` proxy the rag-public API (so
90
+ audience isolation stays enforced in exactly one place), and
91
+ `oiml_documents` reads the derived D1 registry via a read-only binding
92
+ (public OIML metadata only — no ISO/IEC content).
@@ -0,0 +1,247 @@
1
+ # Metanorma AI Serialization — Adapter Format Proposal
2
+
3
+ *Proposed output flavor for `metanorma-document` (and the concrete answer to
4
+ [metanorma/metanorma#592](https://github.com/metanorma/metanorma/issues/592)):
5
+ a machine serialization of a Metanorma document designed for RAG ingestion
6
+ and AI training corpora. Draft v1.0.0-proposal, 2026-08-26.*
7
+
8
+ ---
9
+
10
+ ## 1. Positioning
11
+
12
+ *Evidence alignment (2025–26): the projection's shape is exactly what the
13
+ normative-RAG literature converges on — [ETSI arXiv:2604.09868] recovers
14
+ InfoUnits + parthood/citation edges from PDFs by heuristic and shows
15
+ structure preservation is the retrieval win; [STC arXiv:2605.00318]
16
+ quantifies typed table rows (MRR 0.36→0.59); [RDR2 arXiv:2510.04293],
17
+ [SF-RAG arXiv:2602.13647], [SPIRE arXiv:2604.20849] all consume
18
+ structure trees/natively-hierarchical indexes; [MAHA arXiv:2510.14592]
19
+ indexes tables as structured objects and equations as LaTeX+description.
20
+ Metanorma can emit natively what all of them recover heuristically.*
21
+
22
+
23
+ Metanorma already has the right substrate: `metanorma-document`
24
+ deserializes semantic XML into typed lutaml-model classes and serializes
25
+ them to XML/YAML/JSON natively. This proposal does NOT invent a new
26
+ document model — it defines a **projection flavor**: a stable, typed,
27
+ chunk-ready node graph derived from the existing model.
28
+
29
+ ```
30
+ adoc source ──compile──▶ semantic XML ──Root.from_xml──▶ metanorma-document MODEL
31
+ │
32
+ ┌───────────────────────────────┤──▶ HTML / PDF (renderers)
33
+ └──▶ AiSerialization::Root.to_json / .to_yaml / .to_xml ◀── THIS PROPOSAL
34
+ ```
35
+
36
+ Properties:
37
+
38
+ - **Derived output** (like renderings): lossy by design, never a source
39
+ format; the model and semantic XML remain canonical.
40
+ - **Encoding-agnostic**: implemented as lutaml-model classes with
41
+ `mapping` blocks — JSON, YAML, and XML serializations of ONE schema.
42
+ No hand-rolled `to_h`/`to_json` anywhere (house rule).
43
+ - **Chunk-ready**: every node is self-contained (document identity +
44
+ breadcrumb + typed payload), so RAG consumers embed nodes without
45
+ re-deriving structure.
46
+ - **Graph-preserving**: a flat edge list exposes structure and semantics
47
+ (containment, requirements→conformance tests, term links, citations).
48
+ - **Training-corpus friendly**: provenance, license fields, and stable
49
+ node hashes make the same export usable as a curated AI-training
50
+ corpus (the #592 ask).
51
+
52
+ ## 2. Envelope
53
+
54
+ ```jsonc
55
+ {
56
+ "schema": "metanorma.ai-serialization", // const identifier
57
+ "schema_version": "1.0.0", // semver
58
+ "generated": {
59
+ "tool": "metanorma-document",
60
+ "model_version": "x.y.z", // gem version
61
+ "flavor": "oiml", // document flavor used
62
+ "timestamp": "2026-08-26T00:00:00Z"
63
+ },
64
+ "document": { /* DocumentIdentity, §3 */ },
65
+ "nodes": [ /* typed nodes, §4 */ ],
66
+ "edges": [ /* flat edge list, §5 */ ],
67
+ "datasets": { /* optional Glossarist/Relaton embeds, §6 */ },
68
+ "rights": { "license": "...", "copyright": "..." } // corpus-safe provenance
69
+ }
70
+ ```
71
+
72
+ ## 3. Document identity node
73
+
74
+ ```jsonc
75
+ "document": {
76
+ "docidentifier": "OIML R 60-1", // authoritative, never slug-derived
77
+ "part": "1", // or "annex": "A"
78
+ "edition": "2017",
79
+ "language": "en",
80
+ "doctype": "R",
81
+ "status": "in-force", // in-force | superseded | withdrawn | joint
82
+ "superseded_by": null,
83
+ "title": "Metrological regulation for load cells — Part 1",
84
+ "family": "R-60", // series node for part graphs
85
+ "source": { "file": "document.xml", "hash": "sha256:…" },
86
+ "canonical_id": "OIML R 60-1:2017:en"
87
+ }
88
+ ```
89
+
90
+ ## 4. Typed nodes
91
+
92
+ Node classes map 1:1 onto existing model classes (right column = source in
93
+ `metanorma-document`):
94
+
95
+ | Node type | Source model class | Notes |
96
+ |---|---|---|
97
+ | `clause` | `BasicDocument::Blocks::BasicBlock` (+subclasses) | carries `anchor`, `obligation`, breadcrumb |
98
+ | `table` | `BasicDocument::Tables::TableBlock` | columns/units/rows payload |
99
+ | `formula` | `BasicDocument::AncillaryBlocks::FormulaBlock` + `StemElement` | asciimath/latex/description |
100
+ | `figure` | `BasicDocument::Figure` | alt text, image ref |
101
+ | `term` | `StandardDocument` term entries | links to Glossarist concept |
102
+ | `requirement` | `Document::Components::Blocks::RequirementModel` | ModSpec projection |
103
+ | `permission` | `…/PermissionModel` | ModSpec projection |
104
+ | `recommendation` | requirement variants | ModSpec projection |
105
+ | `conformance_class` / `conformance_test` | ModSpec classes | `modspec-ruby` objects |
106
+ | `reference` | `BibData`/bibitem entries | Relaton key |
107
+ | `amendment`/`change` | `BasicDocument::Change` | editorial vs technical |
108
+
109
+ **Common fields (all nodes):**
110
+ ```jsonc
111
+ {
112
+ "id": "n:tbl-4.1.2-1", // stable: "n:" + model anchor/id
113
+ "type": "table",
114
+ "parent": "n:cl-4.1.2", // containment (also in edges)
115
+ "anchor": "tbl-4.1.2-1",
116
+ "breadcrumb": ["4 Metrological requirements", "4.1 Classification"],
117
+ "obligation": "normative", // when the model carries it
118
+ "hash": "sha256:…", // content hash — enrichment invalidation
119
+ "text": "…" // human-readable display text
120
+ }
121
+ ```
122
+
123
+ **Typed payloads:**
124
+ ```jsonc
125
+ // table
126
+ { "columns": [{ "label": "Load m", "unit": "e", "scope": "per verification interval" }],
127
+ "rows": [["0 ≤ m ≤ 5·10³", "0.5e"]],
128
+ "caption": "Maximum permissible errors",
129
+ "embed_text": "Table: Maximum permissible errors; columns: Load m [e], MPE; row: 0 ≤ m ≤ 5·10³ | 0.5e" }
130
+
131
+ // formula
132
+ { "asciimath": "n_LC <= …", "latex": "n_{LC} \\leq …",
133
+ "described": "limit on the number of verification intervals" }
134
+
135
+ // term
136
+ { "concept": "load-cell", "designation": "load cell",
137
+ "definition": "…", "vocab": "oiml-complete", "glossarist_id": "…" }
138
+
139
+ // requirement (ModSpec projection)
140
+ { "identifier": "/req/oiml-r60-1/classification/nlc-limit",
141
+ "class": "/req/oiml-r60-1/classification",
142
+ "obligation": "requirement",
143
+ "statement": "The number of load cell verification intervals n_LC shall be within…",
144
+ "inherits": [], "subject": "load cell", "inherits:": [] }
145
+
146
+ // conformance_test
147
+ { "identifier": "/conf/oiml-r60-1/classification/nlc-limit",
148
+ "class": "/conf/oiml-r60-1/classification",
149
+ "tests": "/req/oiml-r60-1/classification/nlc-limit",
150
+ "method": "Inspect the marking and the accompanying documents…" }
151
+
152
+ // reference
153
+ { "key": "IEC61000-4-2", "cited": "IEC 61000-4-2:2008", "relaton_id": "…" }
154
+ ```
155
+
156
+ `embed_text` is a PRODUCED convenience (deterministic serialization of the
157
+ payload for embedding); consumers may ignore it and serialize their own way.
158
+
159
+ ## 5. Edges
160
+
161
+ ```jsonc
162
+ { "from": "n:tbl-4.1.2-1", "to": "n:cl-4.1.2", "kind": "part_of" }
163
+ ```
164
+ Kinds: `part_of` (containment), `cites` (reference node → external docid),
165
+ `supersedes`, `defines` (term → concept), `tested_by` /
166
+ `requirement_of` (requirement ↔ conformance test), `class_of`,
167
+ `amends`, `variant_of` (language/edition variants).
168
+
169
+ ## 6. Dataset embeds (optional)
170
+
171
+ ```jsonc
172
+ "datasets": {
173
+ "glossarist": { "id": "oiml-complete", "concepts": 6031, "embedded": false, "ref": "…" },
174
+ "relaton": { "id": "relaton-data-oiml", "records": 5707, "embedded": false, "ref": "…" }
175
+ }
176
+ ```
177
+ Embedding the full datasets inline is OPTIONAL (size); the fields declare
178
+ which datasets the document's edges/terms resolve against.
179
+
180
+ ## 7. Serializations
181
+
182
+ One lutaml-model class set; three mappings (idiomatic examples):
183
+
184
+ - **JSON / YAML**: attribute names as above (`Metanorma::AiSerialization::Root.to_json`)
185
+ - **XML**: elements for complex parts, attributes for scalars —
186
+ `<node type="table" id="n:tbl-4.1.2-1" anchor="tbl-4.1.2-1"><caption>…</caption>…`
187
+
188
+ Consumers pick an encoding; the schema is the contract.
189
+
190
+ ## 8. Conformance (producers MUST)
191
+
192
+ 1. Emit `schema`, `schema_version`, full document identity, and node
193
+ `id`/`type`/`anchor` for every node.
194
+ 2. Node ids stable across regenerations for unchanged content (anchor +
195
+ content hash basis).
196
+ 3. Tables carry columns+rows (never linearized strings only); formulas
197
+ carry at least two of {asciimath, latex, described}.
198
+ 4. Edges only reference present node ids or declared external keys.
199
+ 5. `rights` present when the source declares license/copyright.
200
+ Consumers SHOULD treat unknown node types as opaque text nodes
201
+ (forward compatibility).
202
+
203
+ ## 9. Our consumer mapping (rag ingest)
204
+
205
+ | AI Serialization | ChunkRecordV2 (Stage 2 spec) |
206
+ |---|---|
207
+ | `document` | `DocumentRecord` |
208
+ | `nodes[type=clause]` | chunk `block: clause` |
209
+ | `nodes[type=table]` | chunk `block: table` + `table` payload |
210
+ | `nodes[type=formula]` | `block: equation` |
211
+ | `nodes[type=term]` | `block: definition` |
212
+ | `requirement`/`conformance_test` | `block: requirement` |
213
+ | `edges` (cites/supersedes/part_of/tested_by) | D1 graph projection |
214
+
215
+ ## 10. Implementation sketch (upstream)
216
+
217
+ A `metanorma-document` flavor module — class set + a walk:
218
+
219
+ ```ruby
220
+ module Metanorma
221
+ module AiSerialization
222
+ class Root < Lutaml::Model::Serializable
223
+ attribute :schema, :string
224
+ attribute :schema_version, :string
225
+ attribute :document, DocumentIdentity
226
+ attribute :nodes, Node, collection: true
227
+ attribute :edges, Edge, collection: true
228
+ json do |m| m.map "schema", to: :schema # … end
229
+ xml do |m| m.root "ai-serialization" # … end
230
+ end
231
+ module Project # the walk: model → nodes/edges
232
+ def self.call(doc_model) = … # visits typed model classes only
233
+ end
234
+ end
235
+ end
236
+ ```
237
+ CLI: `metanorma compile doc.adoc -x ai-serialization,json` (a new output
238
+ format alongside html/pdf/… — the #592 ask reduced to one flavor entry).
239
+
240
+ ## 11. Open questions for upstream
241
+
242
+ 1. Version stability contract for the projection (semver gates?).
243
+ 2. Whether `embed_text` serializations belong in the spec or a separate
244
+ "AI profile".
245
+ 3. Multi-document collections (per-document envelopes vs collection
246
+ envelope with cross-doc edges).
247
+ 4. License signaling for training-corpus redistribution.
@@ -0,0 +1,147 @@
1
+ # MKO export pipeline — mn-samples-oiml → RAG
2
+
3
+ **Status: producer + consumer validated end to end on the real corpus
4
+ (2026-08-28).** This is the producer-native path that replaces HTML
5
+ scraping for the clean corpus. Wire contract: MN 116 (metanorma/docs PR
6
+ #9, sources/116).
7
+
8
+ This pipeline is the first concrete realization of the ETSI-informed
9
+ redesign gaps ([REDESIGN-NORMATIVE-RAG-ETSI.md](REDESIGN-NORMATIVE-RAG-ETSI.md)):
10
+ native section `part_of` (G-ETSI-2), native `cites` edges (G-ETSI-3),
11
+ atomic typed tables (G-ETSI-5) — from the producer, with no recovery
12
+ heuristics.
13
+
14
+ ## What MKO is
15
+
16
+ One Metanorma document → one `<short>.mko/` bundle of typed,
17
+ addressable knowledge objects:
18
+
19
+ | Component | Content |
20
+ |---|---|
21
+ | `manifest.json` | schema id, per-component SHA-256 hashes (verified at ingest) |
22
+ | `document.json` | identity (parsed docids, doctype, edition, status, languages), relations, numbered structure tree |
23
+ | `units.jsonl` | typed units — clause/annex/term/table/figure/formula/note/example/sourcecode/requirement/reference — each with anchor, number, breadcrumb, plain text, typed payload, content hash |
24
+ | `edges.jsonl` | the graph: `part_of` containment, `cites` (reference **and** term units), `defines` (term→concept), `class_of`, document-level relations (`doc:<short> → ext:<id>`, Relaton type verbatim: obsoletes/hasPart/…) |
25
+ | `bibdata.json` | the document's own record as **native Relaton JSON** |
26
+ | `bibliography.jsonl` | every **cited document** as native objects: Relaton item + native pubid parse + pubid render |
27
+ | `glossary.json` | term entries as **native Glossarist concepts** |
28
+ | `identifiers.json` | the document's identifiers with **native pubid parses** |
29
+
30
+ Semantic anchors over GUIDs, content-hash-stable unit ids, byte-identical
31
+ determinism, zero flavor knowledge in the walk.
32
+
33
+ ## Pipeline for mn-samples-oiml
34
+
35
+ The samples already carry compiled `document.xml` +
36
+ `document.presentation.xml` in-tree (OIML compiles through the iso
37
+ backend: `flavor="iso"`), so today the export is a one-liner per
38
+ document (from the metanorma-document checkout, branch
39
+ `feat/model-validation-l1-declarations`, PR #45):
40
+
41
+ ```bash
42
+ # one command, gated (live since 2026-08-29):
43
+ .venv/bin/python -m ingest.cli mko # export → ingest → enrich → verify-gate → wire → fts → graph
44
+ .venv/bin/python -m ingest.cli mko --dry # counts + coverage check only
45
+ ```
46
+
47
+ The pipeline enforces the stage order this path proved matters: the
48
+ `verify` gate refuses to wire chunks that lack contextual preambles
49
+ (the 2026-08-28 regression — un-enriched upserts dropped retrieval
50
+ R@5 from 95% to 90%), and it stops before shipping (INDEX_VERSION
51
+ bump + deploy stay human). Stage 5 (wire) needs
52
+ `CLOUDFLARE_API_TOKEN` in the environment; enrichment upserts
53
+ enriched vectors live as it runs, so wiring is idempotent
54
+ bookkeeping afterwards. The underlying per-stage commands (for
55
+ reference / partial runs):
56
+
57
+ When the umbrella wiring lands (metanorma PR #591) this becomes
58
+ `metanorma compile document.adoc -x mko` and the Ruby step disappears —
59
+ the ingest side does not change.
60
+
61
+ ## Worked example: OIML R 60-1 (load cells)
62
+
63
+ `oiml-r-60-1.mko` → ingest output (producer @e6918cb):
64
+
65
+ ```
66
+ 155 units (clause=72, term=59, table=6, figure=4, example=2, reference=12)
67
+ → 136 chunks, 59 terms, 12 cited docs, 541 graph rows
68
+ canonical: OIML R 60-1 (edition 2)
69
+ ```
70
+
71
+ **Full-corpus run (2026-08-29, live):** 36 documents → 3,020 enriched
72
+ chunks (clause 1,913 / table 371 / term 113 / note 77 / formula 37 /
73
+ annex 32 / example 8), 113 glossary concepts, 342 cited docs, 3,561
74
+ section nodes + cites/defines edges in D1. Retrieval held at baseline
75
+ (R@5 95%, AP 0.875, MRR 0.893) after widening the rerank window to 10
76
+ for the enlarged clean lane.
77
+ ## Full-corpus validation (2026-08-28)
78
+
79
+ All **37** compiled sample documents (R/D/B/G/E + OIML-CS admin + parts
80
+ + amendments) export and ingest:
81
+
82
+ ```
83
+ 37/37 bundles → 3053 chunks, 113 native Glossarist concepts,
84
+ 342 cited documents (native Relaton + pubid), ~11k graph rows
85
+ ```
86
+
87
+ Includes the amendment cases: R 60/A1 keeps its 17 terms in an annex
88
+ (recovered into the glossary), and the OIML-CS admin documents keep
89
+ their whole body in `<preface>` (walked as clauses). Clause text now
90
+ carries list content in document order, not just paragraphs.
91
+
92
+ *Counting notes (verified against the artifacts): the 539 graph rows are
93
+ 242 edges (147 `part_of`, 36 `cites`, 59 `defines`) + 297 node inserts;
94
+ figures and references become bibliography/graph objects, not chunks, so
95
+ 135 chunks = clause 68 + term 59 + table 6 + example 2 (3 clause units
96
+ merge/empty; the source XML carries 63 raw `<term>` tags, 4 of them
97
+ nested/boilerplate).*
98
+
99
+ - 59 Glossarist-native concepts (designations, definition, sources,
100
+ `language_code: eng`) — the glossary-heavy load-cell vocabulary lands
101
+ directly in the terminology lane, no scraping.
102
+ - 6 tables as typed payloads (columns/rows), atomic — G-ETSI-5.
103
+ - 539 graph rows: 147 `part_of` (section parthood — G-ETSI-2), 36
104
+ `cites` (12 references + term sources — G-ETSI-3), 59 `defines`
105
+ (edges total 242; the remaining rows are node inserts).
106
+ - 12 cited documents (OIML V 1:2013, R 111, …) as native Relaton items —
107
+ the citation targets are addressable objects, not strings.
108
+
109
+ ## Answer contract v2 stages (live)
110
+
111
+ - `to_payload_sql` → `artifacts/mko_unit_payloads.sql` → D1 `unit_payloads` (573 typed units; `mirror` stripped for serving size, 60KB cap)
112
+ - Figure assets → R2 `rag-public-assets` at unit-keyed keys, served at `/assets/u:<id>.<ext>` (immutable); `unit_payloads.payload.uri` updated to the canonical route (producer `uri` is null today — upstream asset-contract ask, metanorma-document#50)
113
+
114
+ ## How the artifacts feed the existing stages
115
+
116
+ `artifacts/mko_chunks.jsonl` uses the **same chunk schema** the
117
+ parse/enrich/embed stages already consume (`chunk_text`, `doc_id`,
118
+ `docidentifier`, `clause_anchor`, `clause_title`, `tier`, `corpus`,
119
+ `status`, `superseded_by`, `text_ref`) plus new keys: `block` (unit
120
+ type), `unit_id`/`unit_hash` (incremental re-index keys), and typed
121
+ payloads (`metadata.table` with columns/rows, `metadata.formula` with
122
+ description/asciimath, `metadata.term`, `metadata.requirement`).
123
+ `artifacts/mko_graph.sql` targets the same D1 `graph_nodes`/`graph_edges`
124
+ tables as `ingest/graph.py`. `mko_glossary.json` and
125
+ `mko_bibliography.json` are new lanes (terminology exact-match; cited-doc
126
+ registry).
127
+
128
+ ## Gaps / next steps
129
+
130
+ 1. **CLI**: `-x mko` is wired in metanorma PR #591 (unreleased). Until
131
+ it ships, use the Ruby export above (works against the in-tree
132
+ compiled XML).
133
+ 2. **pubid has no OIML flavor**: OIML identifiers ("OIML R 60-1:2017")
134
+ are not pubid-parsed → `pubid: null` in bibliography lines. Doc node
135
+ ids still normalize (`doc:OIML-R-60-1-2017` — same shape as
136
+ `graph.py#norm_id`, so the graphs join). Upstream ask filed:
137
+ pubid/pubid#342.
138
+ 3. **Document-level relations**: the samples' semantic XML embeds no
139
+ relaton `<relation>` elements, so `doc:` edges are empty for them;
140
+ the relaton-data-oiml join stays authoritative for
141
+ status/supersession until flavors embed relations at compile time.
142
+ 4. **Collections** (r060, r129, r138: parts + amendments via
143
+ `collection.yml`): export is per document today; a collection-level
144
+ manifest with cross-document edges is future MN 116 work.
145
+ 5. **Multilingual editions** (FR/AR/…): units carry `lang`; interlinear
146
+ alignment (same unit id across languages) is specified in MN 116 but
147
+ needs producer work on translated sources.