@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.
- package/LICENSE +29 -0
- package/README.md +13 -0
- package/dist/admin.d.ts +26 -0
- package/dist/ai.d.ts +6 -0
- package/dist/anchors.d.ts +6 -0
- package/dist/answercache.d.ts +22 -0
- package/dist/ask.d.ts +5 -0
- package/dist/auth.d.ts +12 -0
- package/dist/bubble.d.ts +14 -0
- package/dist/chunk-LLWPT2XV.js +49 -0
- package/dist/chunk-MB74PTRM.js +114 -0
- package/dist/chunk-WOGQM7DJ.js +197 -0
- package/dist/chunk-WWNCWKKC.js +42 -0
- package/dist/completion.d.ts +5 -0
- package/dist/config.d.ts +154 -0
- package/dist/config.js +37 -0
- package/dist/context.d.ts +115 -0
- package/dist/conversations.d.ts +5 -0
- package/dist/drafts.d.ts +129 -0
- package/dist/env.d.ts +57 -0
- package/dist/faithfulness.d.ts +5 -0
- package/dist/grader.d.ts +3 -0
- package/dist/graph.d.ts +13 -0
- package/dist/hybrid.d.ts +7 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.js +5373 -0
- package/dist/internal_gateway.d.ts +14 -0
- package/dist/lexical.d.ts +7 -0
- package/dist/livedata.d.ts +77 -0
- package/dist/memories.d.ts +10 -0
- package/dist/modelplane.d.ts +61 -0
- package/dist/oidc.d.ts +73 -0
- package/dist/pipeline.d.ts +57 -0
- package/dist/profile.d.ts +2 -0
- package/dist/profile.gen.d.ts +70 -0
- package/dist/profile.js +8 -0
- package/dist/projects.d.ts +8 -0
- package/dist/prompts/conversational.md +8 -0
- package/dist/prompts/enrichment.md +3 -0
- package/dist/prompts/faithfulness.md +1 -0
- package/dist/prompts/grader.md +5 -0
- package/dist/prompts/listwise.md +3 -0
- package/dist/prompts/precision.md +1 -0
- package/dist/prompts/reflect.md +1 -0
- package/dist/prompts/relevancy.md +1 -0
- package/dist/prompts/research.md +10 -0
- package/dist/prompts/section-summary.md +5 -0
- package/dist/prompts/summarize.md +1 -0
- package/dist/prompts/system.md +18 -0
- package/dist/prompts/understanding.md +17 -0
- package/dist/quota.d.ts +13 -0
- package/dist/reflect.d.ts +5 -0
- package/dist/refs.d.ts +40 -0
- package/dist/refusal.d.ts +9 -0
- package/dist/refusal.js +9 -0
- package/dist/requestScope.d.ts +26 -0
- package/dist/requestScope.js +10 -0
- package/dist/research.d.ts +8 -0
- package/dist/search.d.ts +4 -0
- package/dist/selfquery.d.ts +7 -0
- package/dist/session.d.ts +1 -0
- package/dist/share.d.ts +2 -0
- package/dist/structural.d.ts +27 -0
- package/dist/tablecontext.d.ts +11 -0
- package/dist/understand.d.ts +11 -0
- package/dist/understandContract.d.ts +29 -0
- package/dist/verdict.d.ts +24 -0
- package/docs/API.md +451 -0
- package/docs/ARCHITECTURE.md +302 -0
- package/docs/AUDIT-2026-08-24.md +71 -0
- package/docs/CONTRIBUTOR-AUDIT-2026-08-25.md +147 -0
- package/docs/INGEST-ARCHITECTURE.md +158 -0
- package/docs/MCP.md +92 -0
- package/docs/METANORMA-AI-SERIALIZATION.md +247 -0
- package/docs/MKO-EXPORT-PIPELINE.md +147 -0
- package/docs/REDESIGN-NORMATIVE-RAG-ETSI.md +485 -0
- package/docs/RESEARCH-SOTA-2026.md +243 -0
- package/docs/ROADMAP-SOTA.md +130 -0
- package/docs/SOTA-STAGE-SPECS.md +509 -0
- package/docs/annealment/F1-verdict.md +27 -0
- package/docs/annealment/F10-notes.md +19 -0
- package/docs/annealment/F11-composition.md +17 -0
- package/docs/annealment/F12-passport.md +17 -0
- package/docs/annealment/F2-counterfactual.md +20 -0
- package/docs/annealment/F3-absence.md +21 -0
- package/docs/annealment/F4-instance.md +18 -0
- package/docs/annealment/F5-workflow.md +21 -0
- package/docs/annealment/F6-impact.md +21 -0
- package/docs/annealment/F7-editions.md +18 -0
- package/docs/annealment/F8-selfverify.md +19 -0
- package/docs/annealment/F9-projection-qa.md +17 -0
- package/docs/annealment/L0-locate.md +19 -0
- package/docs/annealment/L1-extract.md +18 -0
- package/docs/annealment/L2-nomenclature.md +22 -0
- package/docs/annealment/L3-geometry.md +23 -0
- package/docs/annealment/L4-composition.md +21 -0
- package/docs/annealment/L5-cross-standard.md +20 -0
- package/docs/annealment/L6-diachrony.md +21 -0
- package/docs/annealment/L7-perception.md +20 -0
- package/docs/annealment/L8-computation.md +22 -0
- package/docs/annealment/L9-instance-process.md +23 -0
- package/docs/annealment/README.md +10 -0
- package/docs/guidelines-metanorma-ai-programme.md +279 -0
- package/docs/identity-onboarding-rag.md +65 -0
- package/docs/identity-service.md +219 -0
- package/docs/knowledge-annealment.md +273 -0
- package/docs/konneal-extraction-plan.md +481 -0
- package/docs/metanorma-for-ai.md +270 -0
- package/docs/mirror-plan.md +36 -0
- package/docs/multi-sdo-architecture.md +191 -0
- package/docs/paper-annealment-comparison.md +259 -0
- package/docs/paper-assets/architecture.svg +94 -0
- package/docs/paper-assets/contract-v2.svg +94 -0
- package/docs/paper-assets/mko-ingest.svg +91 -0
- package/docs/paper-oiml-bulletin.md +402 -0
- package/docs/paper-oiml-bulletin.mdx +419 -0
- package/docs/product-branding-options.md +172 -0
- package/docs/projects-design.md +88 -0
- package/docs/sota-mechanisms.md +184 -0
- package/docs/spec-api.md +77 -0
- package/docs/spec-pipeline.md +126 -0
- package/docs/vector-adapter.md +88 -0
- package/package.json +70 -0
- package/profile/corpora.yaml +5 -0
- package/profile/datasets.yaml +14 -0
- package/profile/prompts.yaml +5 -0
- package/profile/publisher.yaml +17 -0
- package/profile/retrieval.yaml +1 -0
- package/profile/sources.yaml +5 -0
- package/profile/ui.yaml +7 -0
- package/scripts/gen_profile.mjs +33 -0
- package/workers/shared/ai.ts +21 -0
- package/workers/shared/auth.ts +16 -0
- package/workers/shared/chunk.ts +108 -0
- package/workers/shared/oidc.ts +312 -0
- package/workers/shared/router.ts +45 -0
- package/workers/shared/session.ts +104 -0
- package/workers/worker_internal/src/index.ts +157 -0
- package/workers/worker_internal/tsconfig.json +15 -0
- package/workers/worker_internal/wrangler.toml +32 -0
- package/workers/worker_mcp/src/index.ts +175 -0
- package/workers/worker_mcp/tsconfig.json +13 -0
- package/workers/worker_mcp/wrangler.toml +18 -0
- package/workers/worker_public/migrations/0002_conversations.sql +22 -0
- package/workers/worker_public/migrations/0003_shared_conversations.sql +9 -0
- package/workers/worker_public/migrations/0004_graph.sql +16 -0
- package/workers/worker_public/migrations/0005_documents.sql +19 -0
- package/workers/worker_public/migrations/0006_conversation_entities.sql +11 -0
- package/workers/worker_public/migrations/0007_chunks_fts.sql +43 -0
- package/workers/worker_public/migrations/0008_unit_payloads.sql +15 -0
- package/workers/worker_public/migrations/0009_chunks_unit.sql +8 -0
- package/workers/worker_public/migrations/0009_message_context.sql +7 -0
- package/workers/worker_public/migrations/0010_model_nodes.sql +33 -0
- package/workers/worker_public/migrations/0011_schema_union.sql +38 -0
- package/workers/worker_public/migrations/0012_memories.sql +15 -0
- package/workers/worker_public/migrations/0013_projects.sql +21 -0
- package/workers/worker_public/node_modules/@cloudflare/workers-types/2021-11-03/index.d.ts +16306 -0
- package/workers/worker_public/node_modules/@cloudflare/workers-types/2021-11-03/index.ts +16261 -0
- package/workers/worker_public/node_modules/@cloudflare/workers-types/2022-01-31/index.d.ts +16373 -0
- package/workers/worker_public/node_modules/@cloudflare/workers-types/2022-01-31/index.ts +16328 -0
- package/workers/worker_public/node_modules/@cloudflare/workers-types/2022-03-21/index.d.ts +16382 -0
- package/workers/worker_public/node_modules/@cloudflare/workers-types/2022-03-21/index.ts +16337 -0
- package/workers/worker_public/node_modules/@cloudflare/workers-types/2022-08-04/index.d.ts +16383 -0
- package/workers/worker_public/node_modules/@cloudflare/workers-types/2022-08-04/index.ts +16338 -0
- package/workers/worker_public/node_modules/@cloudflare/workers-types/2022-10-31/index.d.ts +16403 -0
- package/workers/worker_public/node_modules/@cloudflare/workers-types/2022-10-31/index.ts +16358 -0
- package/workers/worker_public/node_modules/@cloudflare/workers-types/2022-11-30/index.d.ts +16408 -0
- package/workers/worker_public/node_modules/@cloudflare/workers-types/2022-11-30/index.ts +16363 -0
- package/workers/worker_public/node_modules/@cloudflare/workers-types/2023-03-01/index.d.ts +16414 -0
- package/workers/worker_public/node_modules/@cloudflare/workers-types/2023-03-01/index.ts +16369 -0
- package/workers/worker_public/node_modules/@cloudflare/workers-types/2023-07-01/index.d.ts +16414 -0
- package/workers/worker_public/node_modules/@cloudflare/workers-types/2023-07-01/index.ts +16369 -0
- package/workers/worker_public/node_modules/@cloudflare/workers-types/README.md +135 -0
- package/workers/worker_public/node_modules/@cloudflare/workers-types/entrypoints.svg +53 -0
- package/workers/worker_public/node_modules/@cloudflare/workers-types/experimental/index.d.ts +17095 -0
- package/workers/worker_public/node_modules/@cloudflare/workers-types/experimental/index.ts +17050 -0
- package/workers/worker_public/node_modules/@cloudflare/workers-types/index.d.ts +16306 -0
- package/workers/worker_public/node_modules/@cloudflare/workers-types/index.ts +16261 -0
- package/workers/worker_public/node_modules/@cloudflare/workers-types/latest/index.d.ts +16447 -0
- package/workers/worker_public/node_modules/@cloudflare/workers-types/latest/index.ts +16402 -0
- package/workers/worker_public/node_modules/@cloudflare/workers-types/oldest/index.d.ts +16306 -0
- package/workers/worker_public/node_modules/@cloudflare/workers-types/oldest/index.ts +16261 -0
- package/workers/worker_public/node_modules/@cloudflare/workers-types/package.json +11 -0
- package/workers/worker_public/package.json +13 -0
- package/workers/worker_public/prompts/conversational.md +8 -0
- package/workers/worker_public/prompts/enrichment.md +3 -0
- package/workers/worker_public/prompts/faithfulness.md +1 -0
- package/workers/worker_public/prompts/grader.md +5 -0
- package/workers/worker_public/prompts/listwise.md +3 -0
- package/workers/worker_public/prompts/precision.md +1 -0
- package/workers/worker_public/prompts/reflect.md +1 -0
- package/workers/worker_public/prompts/relevancy.md +1 -0
- package/workers/worker_public/prompts/research.md +10 -0
- package/workers/worker_public/prompts/section-summary.md +5 -0
- package/workers/worker_public/prompts/summarize.md +1 -0
- package/workers/worker_public/prompts/system.md +18 -0
- package/workers/worker_public/prompts/understanding.md +17 -0
- package/workers/worker_public/public/app.js +166 -0
- package/workers/worker_public/public/index.html +48 -0
- package/workers/worker_public/public/style.css +147 -0
- package/workers/worker_public/schema.sql +248 -0
- package/workers/worker_public/src/admin.ts +358 -0
- package/workers/worker_public/src/ai.ts +71 -0
- package/workers/worker_public/src/anchors.ts +41 -0
- package/workers/worker_public/src/answercache.ts +72 -0
- package/workers/worker_public/src/ask.ts +1094 -0
- package/workers/worker_public/src/auth.ts +252 -0
- package/workers/worker_public/src/bubble.ts +111 -0
- package/workers/worker_public/src/completion.ts +75 -0
- package/workers/worker_public/src/config.ts +238 -0
- package/workers/worker_public/src/context.ts +238 -0
- package/workers/worker_public/src/conversations.ts +162 -0
- package/workers/worker_public/src/drafts.ts +497 -0
- package/workers/worker_public/src/env.ts +90 -0
- package/workers/worker_public/src/faithfulness.ts +63 -0
- package/workers/worker_public/src/grader.ts +89 -0
- package/workers/worker_public/src/graph.ts +63 -0
- package/workers/worker_public/src/hybrid.ts +77 -0
- package/workers/worker_public/src/index.ts +441 -0
- package/workers/worker_public/src/internal_gateway.ts +41 -0
- package/workers/worker_public/src/lexical.ts +86 -0
- package/workers/worker_public/src/lib/hit.ts +4 -0
- package/workers/worker_public/src/lib/http.ts +83 -0
- package/workers/worker_public/src/lib/router.ts +4 -0
- package/workers/worker_public/src/livedata.ts +334 -0
- package/workers/worker_public/src/memories.ts +81 -0
- package/workers/worker_public/src/modelplane.ts +213 -0
- package/workers/worker_public/src/oidc.ts +333 -0
- package/workers/worker_public/src/pipeline.ts +377 -0
- package/workers/worker_public/src/ports/blobs.ts +7 -0
- package/workers/worker_public/src/ports/cloudflare/adapters.ts +177 -0
- package/workers/worker_public/src/ports/kv.ts +8 -0
- package/workers/worker_public/src/ports/model.ts +28 -0
- package/workers/worker_public/src/ports/runtime.ts +13 -0
- package/workers/worker_public/src/ports/store.ts +20 -0
- package/workers/worker_public/src/ports/vector.ts +26 -0
- package/workers/worker_public/src/profile.gen.ts +101 -0
- package/workers/worker_public/src/profile.ts +16 -0
- package/workers/worker_public/src/projects.ts +108 -0
- package/workers/worker_public/src/prompts.d.ts +6 -0
- package/workers/worker_public/src/quota.ts +54 -0
- package/workers/worker_public/src/reflect.ts +67 -0
- package/workers/worker_public/src/refs.ts +107 -0
- package/workers/worker_public/src/refusal.ts +65 -0
- package/workers/worker_public/src/requestScope.ts +71 -0
- package/workers/worker_public/src/research.ts +126 -0
- package/workers/worker_public/src/search.ts +56 -0
- package/workers/worker_public/src/selfquery.ts +25 -0
- package/workers/worker_public/src/session.ts +4 -0
- package/workers/worker_public/src/share.ts +53 -0
- package/workers/worker_public/src/stages/conceptGraph.ts +68 -0
- package/workers/worker_public/src/stages/conceptSteer.ts +39 -0
- package/workers/worker_public/src/stages/corpusScope.ts +25 -0
- package/workers/worker_public/src/stages/dedup.ts +10 -0
- package/workers/worker_public/src/stages/dense.ts +73 -0
- package/workers/worker_public/src/stages/diversity.ts +33 -0
- package/workers/worker_public/src/stages/editionCover.ts +63 -0
- package/workers/worker_public/src/stages/editionSteer.ts +88 -0
- package/workers/worker_public/src/stages/familyBoost.ts +22 -0
- package/workers/worker_public/src/stages/federate.ts +22 -0
- package/workers/worker_public/src/stages/glossary.ts +65 -0
- package/workers/worker_public/src/stages/graphLane.ts +31 -0
- package/workers/worker_public/src/stages/hyde.ts +29 -0
- package/workers/worker_public/src/stages/index.ts +69 -0
- package/workers/worker_public/src/stages/lexicalUnion.ts +21 -0
- package/workers/worker_public/src/stages/multiQuery.ts +57 -0
- package/workers/worker_public/src/stages/overviewDemote.ts +14 -0
- package/workers/worker_public/src/stages/poolOpen.ts +10 -0
- package/workers/worker_public/src/stages/propagate.ts +15 -0
- package/workers/worker_public/src/stages/rerank.ts +47 -0
- package/workers/worker_public/src/stages/seal.ts +16 -0
- package/workers/worker_public/src/stages/sectionDescent.ts +61 -0
- package/workers/worker_public/src/stages/stdRefNudge.ts +35 -0
- package/workers/worker_public/src/stages/subQuery.ts +42 -0
- package/workers/worker_public/src/stages/termNudge.ts +24 -0
- package/workers/worker_public/src/stages/typedPin.ts +131 -0
- package/workers/worker_public/src/stages/types.ts +112 -0
- package/workers/worker_public/src/stages/windowFloor.ts +23 -0
- package/workers/worker_public/src/structural.ts +171 -0
- package/workers/worker_public/src/tablecontext.ts +41 -0
- package/workers/worker_public/src/understand.ts +72 -0
- package/workers/worker_public/src/understandContract.ts +67 -0
- package/workers/worker_public/src/verdict.ts +255 -0
- package/workers/worker_public/tsconfig.json +18 -0
- package/workers/worker_public/wrangler.toml +104 -0
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
# The SOTA mechanisms — the reference
|
|
2
|
+
|
|
3
|
+
This is the canonical description of every mechanism in the serving
|
|
4
|
+
stack, each with its staircase: what the layer below it cannot do. The
|
|
5
|
+
staircase runs from raw text (fewest dimensions) to executable models
|
|
6
|
+
(most) — every step adds structure, and every added structure unlocks
|
|
7
|
+
questions the previous step could not answer.
|
|
8
|
+
|
|
9
|
+
## The staircase of representations
|
|
10
|
+
|
|
11
|
+
```
|
|
12
|
+
plain text → clause-anchored documents → typed units (MKO)
|
|
13
|
+
→ machine-readable models (Primmel) → execution
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
| Step | Carries | Can answer | Cannot answer |
|
|
17
|
+
|---|---|---|---|
|
|
18
|
+
| Plain text | words | topical lookup | anything with a citation anchor — half the chunks carry no clause reference at all |
|
|
19
|
+
| Clause-anchored prose | words + location | "where does R 60 address creep?" with § | values inside tables, cross-references, anything typed |
|
|
20
|
+
| Typed units (MKO) | + tables/formulas/figures as objects, cross-references as links | "which test validates this requirement" (a link, not a sentence); figures | computed conformance |
|
|
21
|
+
| Machine models (Primmel) | + constraints, calculations, sequences, terms — machine-checkable | the machine limit itself; test order; instance parameters | executing them in an answer |
|
|
22
|
+
| Execution | the check, evaluated | **"is D_max 26 000 v valid for E_max 30 000 v?" → INVALID, computed** | — (the frontier top) |
|
|
23
|
+
|
|
24
|
+
Measured across the six comparison lanes (witness-graded, 18 rung-tagged
|
|
25
|
+
probes): plain 2/18 · adoc 3/18 · MKO 6/18 · Primmel 13/18 · ablation
|
|
26
|
+
12/18 · composed 14/18 · full serving 17–18/18.
|
|
27
|
+
|
|
28
|
+
## The serving mechanisms
|
|
29
|
+
|
|
30
|
+
### 1. Query understanding (meaning, never strings)
|
|
31
|
+
One small model reads every question: language, named publication,
|
|
32
|
+
edition, process intent, hypothetical answer, query variants,
|
|
33
|
+
decomposition for complex asks, standalone reformulation of follow-ups.
|
|
34
|
+
No keyword rules decide anything — the same judgements apply in every
|
|
35
|
+
language. *Below this step:* fixed keyword routers, which answer
|
|
36
|
+
differently depending on phrasing.
|
|
37
|
+
|
|
38
|
+
### 2. Hybrid retrieval (dense + lexical, fused)
|
|
39
|
+
The question's vector searches the corpus alongside a full-corpus BM25
|
|
40
|
+
scan; RRF fusion merges them; a terminology graph adds
|
|
41
|
+
concept→document candidates; multiple phrasings and sub-questions run
|
|
42
|
+
as parallel lanes; a hypothetical answer serves as an extra search key
|
|
43
|
+
(HyDE). *Below:* dense-only retrieval misses exact jargon ("n_LC");
|
|
44
|
+
lexical-only misses paraphrase.
|
|
45
|
+
|
|
46
|
+
### 3. Contextual enrichment (98.7% of non-synthetic chunks)
|
|
47
|
+
Every chunk carries a model-written preamble stating where it sits in
|
|
48
|
+
its document — written once at index time, its quality persists into
|
|
49
|
+
every future retrieval. The preamble lives in the INDEX (plus a 30-day
|
|
50
|
+
KV context cache), never in the build artifacts — a full index restore
|
|
51
|
+
must replay it from the durable record (`scripts/replay_enrichment.py`,
|
|
52
|
+
binding-embed, zero regeneration). Measured the hard way: a restore that
|
|
53
|
+
skipped the replay dropped enrichment to 0% and identifier queries
|
|
54
|
+
failed first ("what is OIML D 29?" — the preambles carry the
|
|
55
|
+
identifier-rich context that ranks them). *Below:* bare chunks retrieve
|
|
56
|
+
on local wording.
|
|
57
|
+
|
|
58
|
+
### 4. Structural retrieval over the clause tree
|
|
59
|
+
The corpus IS a tree (clause anchors chain parent→child). A hit's score
|
|
60
|
+
blends its ancestors' and descendants' scores — a section whose clauses
|
|
61
|
+
are collectively relevant rises; final evidence is presented in
|
|
62
|
+
document reading order; same-chain near-duplicates collapse. *Below:*
|
|
63
|
+
flat chunk retrieval — no notion of "the section around this clause".
|
|
64
|
+
|
|
65
|
+
### 5. Section-summary units (multi-granularity)
|
|
66
|
+
Depth-1 clause summaries are retrievable objects: a summary that ranks
|
|
67
|
+
descends to its quotable child clauses and retires itself — citations
|
|
68
|
+
always quote source text. *Below:* one grain per index — either too
|
|
69
|
+
coarse to cite or too fine to orient.
|
|
70
|
+
|
|
71
|
+
### 6. Vocabulary binding (the nomenclature bridge)
|
|
72
|
+
Everyday words ("my output keeps drifting") rarely match defined terms
|
|
73
|
+
("span stability"). A terminology index of the corpus's defined
|
|
74
|
+
concepts links the question to candidate terms (dense candidates +
|
|
75
|
+
cross-encoder rerank); the answer model adjudicates and leads with the
|
|
76
|
+
corpus's own term, quoting its definition. *Below:* every representation
|
|
77
|
+
measured fails the everyday-words→defined-term bridge — the one gap
|
|
78
|
+
that is a vocabulary problem, not a structure problem.
|
|
79
|
+
|
|
80
|
+
### 7. Reranking, edition steering, typed pin
|
|
81
|
+
A cross-encoder orders candidates (a stronger model re-orders hard
|
|
82
|
+
queries); family-relative steering demotes superseded editions while
|
|
83
|
+
keeping them citable when only they carry content — family is the
|
|
84
|
+
doctype+number, and the demotion is full-strength under either signal:
|
|
85
|
+
the successor edition of the SAME identifier is in the pool, or the
|
|
86
|
+
chunk's own status says superseded/unknown against the family's newest
|
|
87
|
+
(the corpus carries the supersession the registry's missing successor
|
|
88
|
+
links do not; in-force documents are exempt so a newer part-2 never
|
|
89
|
+
demotes a current part-1). A document NAMED in the question scopes
|
|
90
|
+
retrieval deterministically — read from the question text, never left
|
|
91
|
+
to the understanding model's per-call mood ("What is OIML D 29?" was a
|
|
92
|
+
coin flip before). Typed objects (tables/formulas/figures) are pinned a
|
|
93
|
+
window slot so the answer contract can reference them — and a query
|
|
94
|
+
that NAMES the artifact type ("figure", "table", "equation") dominates
|
|
95
|
+
unit selection, so the pin lands the object the question is about
|
|
96
|
+
(feeding multimodal generation for figures). *Below:* similarity-only
|
|
97
|
+
ranking answers from stale editions, guesses at doc scope, and never
|
|
98
|
+
surfaces a typed object.
|
|
99
|
+
|
|
100
|
+
### 8. The answer contract (claims are checkable)
|
|
101
|
+
Inline citations on every claim; normative values quoted verbatim from
|
|
102
|
+
the cited passage; tables/formulas/figures rendered as typed objects
|
|
103
|
+
exactly from the source, never re-typed; a deterministic post-check
|
|
104
|
+
enforces all of it (quoted spans must be contained in served passages;
|
|
105
|
+
a served table's data must carry its object reference) with one
|
|
106
|
+
corrected retry. *Below:* generated prose whose correctness cannot be
|
|
107
|
+
measured.
|
|
108
|
+
|
|
109
|
+
### 9. The verdict engine (conformance by execution)
|
|
110
|
+
When a question names a machine-checkable model object, the service
|
|
111
|
+
executes it: the question's values bind to the rule's own parameters, a
|
|
112
|
+
deterministic evaluator runs the check (OCL boolean expressions,
|
|
113
|
+
threshold limits), and the verdict — pass, the standard's own violation
|
|
114
|
+
word, or void naming the missing parameters — is attached as data the
|
|
115
|
+
model must present faithfully. Counterfactuals are free: hypothetical
|
|
116
|
+
values are just values. *Below:* quoting the rule and hoping the reader
|
|
117
|
+
does the arithmetic.
|
|
118
|
+
|
|
119
|
+
### 10. Provable absence
|
|
120
|
+
"Does R 60 constrain packaging?" enumerates the standard's entire model
|
|
121
|
+
plane and returns a certificate — absent, N nodes enumerated, 0
|
|
122
|
+
matches, scope named — never a bare "I found nothing". *Below:* refusal,
|
|
123
|
+
which asserts a search, not a proof.
|
|
124
|
+
|
|
125
|
+
### 11. Answer verification
|
|
126
|
+
Any answer can be checked against the corpus: verbatim-quote
|
|
127
|
+
containment, object-reference resolution, citation presence
|
|
128
|
+
(deterministic) plus a judged faithfulness score (labeled as judged).
|
|
129
|
+
*Below:* trust me.
|
|
130
|
+
|
|
131
|
+
### 12. Multimodal figure interpretation
|
|
132
|
+
A pinned figure unit's pixels ride the generation call (R2 asset →
|
|
133
|
+
base64 image part), so the model interprets the producer's drawing, not
|
|
134
|
+
just its stored caption. Two measured invariants: assets must be
|
|
135
|
+
vision-readable — vector-sourced rasters carrying black strokes on
|
|
136
|
+
transparent alpha flatten to a solid black rectangle inside vision
|
|
137
|
+
pipelines (browsers hid the defect by compositing on white;
|
|
138
|
+
`scripts/fix_figure_assets.py` detects and re-uploads white-flattened) —
|
|
139
|
+
and images ride their own short trailing user message, because long
|
|
140
|
+
passage text + image parts in ONE message triggers nondeterministic
|
|
141
|
+
provider 8005s that scale with payload. A third rule guards the blast
|
|
142
|
+
radius: pixels attach only when the question WANTS a drawing (names a
|
|
143
|
+
figure-ish artifact, or the pinned figure sits in the answering
|
|
144
|
+
clause) — and when the multimodal call flakes down to a text-only
|
|
145
|
+
fallback, the attach note drops with the pixels (a text model answers
|
|
146
|
+
the note, not the question — observed in the wild). *Below:* caption-
|
|
147
|
+
only answers that disclaim "the passage does not list" what the drawing
|
|
148
|
+
plainly shows.
|
|
149
|
+
|
|
150
|
+
### 13. Dataset scope and permission gating
|
|
151
|
+
Three databases (OIML Publications · OIML SMART Models · ISO/IEC
|
|
152
|
+
Conformity Assessment) are selectable per question: the sidebar toggles
|
|
153
|
+
persist a scope, every ask carries it, and the server intersects it
|
|
154
|
+
with what the SESSION may see — the ISO/IEC corpus requires the estate
|
|
155
|
+
permission `ai-preview` (a role code set in id.oimlsmart.org);
|
|
156
|
+
membership alone is not the bar, and the UI's lock is cosmetic. A
|
|
157
|
+
corpus-scope stage runs last of the pool-assembly stages (after the
|
|
158
|
+
lexical union refills the pool post-rerank) so disabling a database
|
|
159
|
+
actually disables it at the hit level. *Below:* one monolithic corpus
|
|
160
|
+
the user cannot narrow, gated (or not) on login alone.
|
|
161
|
+
|
|
162
|
+
### 14. The measurement gates
|
|
163
|
+
The golden suite (38 cases: doc-level, definitions, table values,
|
|
164
|
+
refusals, filters, verdicts, auth, French) runs ×3 against the live
|
|
165
|
+
service with witness-span containment grading; the annealment battery
|
|
166
|
+
(18 rung-tagged probes) measures capability per representation; EIR
|
|
167
|
+
(cited/retrieved) watches window precision; leakage probes gate every
|
|
168
|
+
promotion; the deploy pipeline guards branch, tests, version bump and
|
|
169
|
+
smoke. Current: golden 37–38/38 (97–100%), annealment 17–18/18 (mode 18/18 — the residuals are two single-case variances: the L5 cross-standard probe's retrieval and the L1 validity date's phrasing).
|
|
170
|
+
|
|
171
|
+
## The vector adapter (one door)
|
|
172
|
+
|
|
173
|
+
Every chunk crosses one boundary into any index: a pydantic wire schema
|
|
174
|
+
(corpus registry, size caps, anchor sanity) and target gating — an
|
|
175
|
+
index accepts only the corpora that belong to it. The wire schema
|
|
176
|
+
mirrors the serving contract; new producers register corpora in one
|
|
177
|
+
place, never ad hoc.
|
|
178
|
+
|
|
179
|
+
Index equality with the canonical chunk set is an OPERATIONS loop, not
|
|
180
|
+
a hope: reconciliation enumerates the index and deletes strays (upserts
|
|
181
|
+
never delete — measured 49,553 live vs 31,512 canonical before the
|
|
182
|
+
first run); the enrichment replay restores the contexts a full upsert
|
|
183
|
+
overwrites; the asset sweep keeps unit images vision-readable. Each
|
|
184
|
+
step is a script in `scripts/`, run between surgery and gate.
|
package/docs/spec-api.md
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# API surface spec
|
|
2
|
+
|
|
3
|
+
The worker's complete HTTP inventory, mirrored 1:1 by the `ROUTES`
|
|
4
|
+
registry in `workers/worker_public/src/index.ts` (dispatched by
|
|
5
|
+
`lib/router.ts`'s `matchRoute`). Adding a route = one registry entry +
|
|
6
|
+
its handler; this document changes with it. Request/response details
|
|
7
|
+
beyond the inventory: [API.md](API.md).
|
|
8
|
+
|
|
9
|
+
## Conventions
|
|
10
|
+
|
|
11
|
+
- **Dual publication**: `/api/*` is the browser surface (anonymous, or
|
|
12
|
+
member via session cookie / bubble Bearer); `/v1/*` is the integrator
|
|
13
|
+
surface (`Authorization: Bearer oiml_<hex>` API key). Same handler,
|
|
14
|
+
tier derived from the path prefix.
|
|
15
|
+
- **Admin routes** authenticate with `Bearer $ADMIN_TOKEN` (a worker
|
|
16
|
+
secret, not an API key).
|
|
17
|
+
- Every response is JSON unless noted; errors are
|
|
18
|
+
`{ "error": { "code", "message" } }`. CORS for `*.oimlsmart.org`.
|
|
19
|
+
- Trailing slashes are equivalent (segment-exact matching).
|
|
20
|
+
|
|
21
|
+
## The inventory
|
|
22
|
+
|
|
23
|
+
| Route | Method | Auth | Handler | Contract |
|
|
24
|
+
|---|---|---|---|---|
|
|
25
|
+
| `/`, `/api/`, `/index.html` | GET | none | `serveIndexPage` | The SPA shell from the assets binding, `must-revalidate` so deploys can never serve HTML referencing deleted fingerprinted assets. |
|
|
26
|
+
| `/auth/login` | GET | none | `handleLogin` | OIDC redirect (PKCE); `?mode=bubble&origin=` starts the embedded-panel flow. |
|
|
27
|
+
| `/auth/callback` | GET | none | `handleCallback` | OIDC callback; mints the session cookie; bubble mode postMessages the token to the validated origin. |
|
|
28
|
+
| `/auth/me` | GET | session | `handleMe` | `{ authenticated, name, email, roles, tier }`. |
|
|
29
|
+
| `/auth/logout` | GET, POST | none | `handleLogout` | Clears the session. |
|
|
30
|
+
| `/api/conversations` | * | session | `conversationsRoute` → `handleConversations` | List (GET) / create (POST) / delete (DELETE) the signed-in user's conversations. |
|
|
31
|
+
| `/api/conversations/:id` | * | session (owner) | `conversationsRoute` | One conversation (GET) / delete (DELETE). |
|
|
32
|
+
| `/api/conversations/:id/messages` | POST | session (owner) | `appendMessageRoute` → `handleAppendMessage` | Append a message turn. |
|
|
33
|
+
| `/api/conversations/:id/share` | POST | session (owner) | `shareRoute` → `handleShareConversation` | Publish a conversation to an unlisted share slug. (Was shadowed by the compound conversations branch pre-route-table — the pattern inventory made the collision visible and fixed it.) |
|
|
34
|
+
| `/api/shared/:slug` | GET | none | `getSharedRoute` → `handleGetShared` | Read a shared conversation. |
|
|
35
|
+
| `/api/datasets` | GET | none (session enriches) | `datasetsRoute` | The corpus catalog + starter questions (the UI's empty state — content from the API, never hardcoded in the client). Session-gated datasets carry `requires` (the estate permission, e.g. `the ai-preview permission (id.oimlsmart.org)`) and `enabled` reflects the permission, not just login. |
|
|
36
|
+
| `/api/memories` | * | session | `memoriesRoute` → `handleMemories` | Personalized memory files (#171): GET list / POST create-update (≤10 files × 8k chars) / DELETE by id — every query owner-filtered by the session sub. Selected ids ride `/api/ask` as `memories: [id…]` (≤4 per ask, injected as one bounded trusted-user-facts note; the selection salts the answer cache). |
|
|
37
|
+
| `/health` | GET | none | `healthRoute` | Liveness + deployed `index_version` (the deploy-drift guard reads this). |
|
|
38
|
+
| `/api/ask`, `/v1/ask` | POST | tier | `askRoute` → `handleAsk` | The answer contract: streamed or JSON answer, citations, typed blocks, context echo. Quotas per tier. Optional `datasets: [id…]` narrows the corpora searched (server-intersected with session permissions; an explicitly-empty list is a 400); optional `memories: [id…]` selects the member's memory files to inject. Both selections salt the answer cache — a scoped or memory-flavored answer never serves a plain ask. |
|
|
39
|
+
| `/api/absence`, `/v1/absence` | POST | tier | `absenceRoute` | Provable absence: exhaustive enumeration over a standard's model plane; `{ verdict: absent \| present, enumerated, matches }`. |
|
|
40
|
+
| `/api/verify`, `/v1/verify` | POST | tier | `verifyRoute` | Self-verification battery over a supplied (query, answer): quote anchors, unit references, citations present + judged faithfulness. |
|
|
41
|
+
| `/api/lane`, `/v1/lane` | POST | tier | `laneRoute` | Direct retrieval against a comparison index (dense + lexical fused), bypassing the full pipeline — for the annealment matrix and the /compare demo. |
|
|
42
|
+
| `/api/search`, `/v1/search` | POST | tier | `searchRoute` → `handleSearch` | Passage search (retrieval without generation). |
|
|
43
|
+
| `/api/feedback` | POST | none | `feedbackRoute` | Thumbs up/down keyed by query hash (privacy: hashes only). |
|
|
44
|
+
| `/admin/enrich`, `/v1/admin/enrich` | POST | ADMIN_TOKEN | `handleEnrich` | Contextual enrichment / embed+upsert through the vector adapter (the ONLY wire-stage upsert door). |
|
|
45
|
+
| `/admin/section`, `/v1/admin/section` | POST | ADMIN_TOKEN | `handleSectionUnit` | Build/serve depth-1 section-summary units. |
|
|
46
|
+
| `/admin/vectors` | POST | ADMIN_TOKEN | `handleVectors` | Read/query vectors for ops. |
|
|
47
|
+
| `/admin/caption` | POST | ADMIN_TOKEN | `handleCaption` | Figure captioning (vision lane). |
|
|
48
|
+
| `/assets/*` | GET | none | `unitAssetRoute` | Immutable unit-keyed figure images (`u:<id>.<ext>`) from R2, 1-year immutable cache. |
|
|
49
|
+
| `/api/research`, `/v1/research` | POST | session (member) | `researchRoute` → `handleResearch` | Deep-research dossier loop — members only (research spend stays with humans). |
|
|
50
|
+
| `/admin/judge`, `/v1/admin/judge` | POST | ADMIN_TOKEN | `handleJudge` | LLM-as-judge scoring (promotion gates). |
|
|
51
|
+
| `/v1/admin/keys` | POST | ADMIN_TOKEN | `handleCreateKey` | Issue an API key (`oiml_<hex>`, shown once). |
|
|
52
|
+
| `/v1/admin/keys` | GET | ADMIN_TOKEN | `handleListKeys` | List keys (no secrets). |
|
|
53
|
+
| `/v1/admin/stats` | GET | ADMIN_TOKEN | `adminStatsRoute` | 7-day telemetry: queries/day/tier, spend by model, feedback, error rate; prunes >90d rows. |
|
|
54
|
+
| `/docs/:slug.html`, `/docs/:slug.anchors.json` | GET | public | `docsRoute` | Rendered publication documents (metanorma-mirror layer 1) served from R2 under `docs/`, immutable cache; the anchors map (clause number → heading anchor id) powers citation deep links. Public OIML content only. |
|
|
55
|
+
| `/admin/enrich` | POST | ADMIN_TOKEN | `handleEnrich` | modes: default (context+embed+upsert in place), `context` (generate only, KV-cached), `ab` (generate at an explicit effort with NO side effects — the experiment lane; accepts an admin-gated prompt override for judged comparisons). |
|
|
56
|
+
| anything else | any | — | — | `404 not_found`. |
|
|
57
|
+
|
|
58
|
+
## Request semantics worth naming
|
|
59
|
+
|
|
60
|
+
- **`effort` on ask** (`/api/ask`, `/v1/ask`): `"low"` (default, 1 quota unit) or `"medium"` (member lane, raises reasoning effort AND the output budget — `effortBudget()`; 2 quota units). Effort changes the answer, so it salts both answer caches alongside the datasets/memory selections (`requestEffort`/`answerEffort` in config.ts).
|
|
61
|
+
- **`max_iterations` on research**: 1–3, clamped server-side.
|
|
62
|
+
|
|
63
|
+
## Dispatch semantics
|
|
64
|
+
|
|
65
|
+
`fetch` = OPTIONS preflight (204) → `matchRoute(ROUTES, method, path)` →
|
|
66
|
+
handler with `{ env, req, ctx, url, path, params }` → 404. Patterns are
|
|
67
|
+
segment-exact with `:param` capture and a trailing `*` wildcard
|
|
68
|
+
(`/assets/*`); entry order is irrelevant because no pattern overlaps
|
|
69
|
+
another. The `/api` and `/v1` twins deliberately share one handler so
|
|
70
|
+
the tier split can never diverge between the two publications.
|
|
71
|
+
|
|
72
|
+
## Isolation invariants (binding lint, `npm run lint:wrangler`)
|
|
73
|
+
|
|
74
|
+
No route in this worker may reference the internal tier: no ISO index
|
|
75
|
+
binding, no internal R2, no internal tokens. Federation happens through
|
|
76
|
+
the `INTERNAL_SERVICE` binding inside the ask pipeline only, gated by a
|
|
77
|
+
member session. The lint fails CI on any violation.
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
# Pipeline stage contracts
|
|
2
|
+
|
|
3
|
+
The retrieval pipeline (`worker_public`) is a fixed-sequence registry of
|
|
4
|
+
stages (`src/stages/index.ts`), each a self-contained module
|
|
5
|
+
implementing the `Stage` interface (`src/stages/types.ts`). `retrieve()`
|
|
6
|
+
(`src/pipeline.ts`) is composition only: it builds the context (the
|
|
7
|
+
parallel embed/lexical prelude), runs the registry, and projects the
|
|
8
|
+
result.
|
|
9
|
+
|
|
10
|
+
## The contract vocabulary
|
|
11
|
+
|
|
12
|
+
- **Context** (`PipelineContext`) — the mutable state every stage reads
|
|
13
|
+
and writes: `env`, `query` (the user's original wording), `rq` (the
|
|
14
|
+
retrieval query), `u` (query understanding, nullable), `filters` /
|
|
15
|
+
`filter` (the dense doc/edition scope), `vector`, `lexicalHits`,
|
|
16
|
+
`matches` (the candidate pool, Vectorize-match shape), `hits` (the
|
|
17
|
+
ranked pool), `finalHits` (the answer window), `glossary`, `opts`.
|
|
18
|
+
- **Guard** (`when`) — a pure read of the context; absent = always runs.
|
|
19
|
+
- **Failure mode** — `additive`: a throw is logged, the pipeline
|
|
20
|
+
continues with the context as the previous stage left it (the lane's
|
|
21
|
+
results were not written). `blocking` (default): the throw propagates
|
|
22
|
+
to the caller of `retrieve()`.
|
|
23
|
+
|
|
24
|
+
Adding a stage = one file in `src/stages/` + one registry entry. No
|
|
25
|
+
edits to existing stages, the runner, or `retrieve()`. A stage that
|
|
26
|
+
needs a new context field declares it on `PipelineContext` (the field's
|
|
27
|
+
owner is the stage that writes it).
|
|
28
|
+
|
|
29
|
+
## Registry order and invariants
|
|
30
|
+
|
|
31
|
+
Order is load-bearing. The phase structure:
|
|
32
|
+
|
|
33
|
+
```
|
|
34
|
+
candidate lanes → pool open → pool-level merges → refinement → window assembly
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
| # | Stage | Guard | Failure | Writes | Contract |
|
|
38
|
+
|---|-------|-------|---------|--------|----------|
|
|
39
|
+
| 1 | `dense` | — | blocking | `matches`, may mutate `filters.edition` | The primary Vectorize query. Three shapes: optimistic reuse (identical query + no filter only), filtered (with guessed-edition-pin drop and sparse-filter widen — the widen APPENDS unfiltered hits behind the filtered set), plain. Never unions optimistic hits into a diverged query. |
|
|
40
|
+
| 2 | `hyde` | `u.hypothetical_answer && !filter` | additive | appends `matches` | Hypothetical-answer vector search; candidates enter at `hydeDiscount`. |
|
|
41
|
+
| 3 | `glossary` | `env.GLOSSARY && vector` | additive | `glossary` | Vocabulary link: dense glossary candidates + cross-encoder rerank; top-3 distinct concepts. MUST run before the concept-graph and steering stages — they consume `glossary`. |
|
|
42
|
+
| 4 | `concept-graph` | `glossary.length && env.DB && vector` | additive | appends `matches` | Linked terms → defining doc numbers (D1, parallel per-term) → metadata-filtered dense merge at `conceptGraphDiscount`. |
|
|
43
|
+
| 5 | `graph-lane` | `opts.graphDocNumbers && vector` | additive | appends `matches` | Caller-resolved family/successor doc numbers merged at `graphLaneDiscount`. |
|
|
44
|
+
| 6 | `multi-query` | `u.query_variants` | blocking (per-variant catches inside) | REPLACES `matches` | RRF-fuses the primary ranking with each variant's (k=60, top `retrieveK`). |
|
|
45
|
+
| 7 | `sub-query` | `u.complexity === "complex" && u.sub_queries` | blocking (per-sub catches inside) | appends `matches` | Complementary-perspective union at `subQueryDiscount` (no RRF). |
|
|
46
|
+
| 8 | `pool-open` | — | blocking | `hits` | Converts the candidate pool to the `Hit` shape. Last writer of `matches`; every later stage operates on `hits`. |
|
|
47
|
+
| 9 | `lexical-union` | `lexicalHits.length` | blocking | appends `hits` | Full-corpus BM25 hits dense missed (dense metadata wins on id collision). |
|
|
48
|
+
| 10 | `federate` | `opts.federate` | additive (callback catches transport) | appends `hits` | Internal ISO/IEC passages at `federateDiscount`. |
|
|
49
|
+
| 11 | `seal` | `opts.sealScope` | blocking | filters `hits` | The declared context's hard cut: nothing outside the declared family reaches rerank. (The lexical lane is sealed at the SOURCE, in the prelude — not here.) |
|
|
50
|
+
| 12 | `overview-demote` | — | blocking | mutates `hits[].score` | Overview boilerplate demotion (`overviewDemotion`). |
|
|
51
|
+
| 13 | `family-boost` | boost only under `filter.doc_number`; sort always | blocking | mutates `hits[].score`, sorts | Family chunks decisively boosted for doc-scoped queries; the sort establishes the rerank-failure fallback order. |
|
|
52
|
+
| 14 | `rerank` | `hits.length > 1` | additive | `hits[].rerank_score`, sorts, family pin | Cross-encoder scores; vector order is the designed fallback. Post-rerank family pin for doc-scoped queries. |
|
|
53
|
+
| 15 | `lexical-rrf` | `hits.length > 1 && lexicalHits.length` | blocking | REPLACES `hits` order | RRF fusion with the full-corpus lexical ranking. Runs even when rerank failed (additive semantics preserve this). |
|
|
54
|
+
| 16 | `std-ref-nudge` | query names ISO/IEC/ASTM/EN | blocking | `hits[].rerank_score`, sorts | Standard-reference nudge: chunks CARRYING such a citation get `stdRefNudgeSpread × spread` — the citing clause is the answer to "which standard does X invoke", and generic family prose otherwise fills the window (l5a measured). |
|
|
55
|
+
| 17 | `edition-cover` | `!filters.edition && hits.length > 1` | additive | appends `hits` | Registry-driven cover: when a pool holds only stale editions of a document whose ACTIVE edition the documents registry knows, fetch the current edition's chunks and add at `editionCoverDiscount` — steering needs the successor present to demote. |
|
|
56
|
+
| 18 | `corpus-scope` | `opts.datasetScope` | blocking | filters `hits` | Dataset scope (the sidebar toggles): drops hits whose corpus the request excludes. LAST of the pool-assembly stages — the lexical union above refills the pool after rerank, so filtering earlier let excluded corpora back in. Corpora the toggle model doesn't name pass untouched. |
|
|
57
|
+
| 19 | `term-nudge` | `u.term` | blocking | `hits[].rerank_score`, sorts | Clause whose head IS the asked term gets `termNudgeSpread × spread` (decisive). |
|
|
58
|
+
| 20 | `concept-steer` | `glossary.length && hits.length > 1` | blocking | `hits[].rerank_score`, sorts | Vocabulary-link families boosted (`conceptSteerSpread`). |
|
|
59
|
+
| 21 | `edition-steer` | `!filters.edition && hits.length > 1` | blocking | `hits[].rerank_score`, sorts | Cross-pub recency boost + family-relative superseded-edition demotion (spread-scaled). |
|
|
60
|
+
| 22 | `structural-propagate` | — | blocking | REPLACES `hits` | FABLE TreeExpansion: score blends along the clause tree. |
|
|
61
|
+
| 23 | `diversity` | — | blocking | `finalHits` (from `hits`) | Per-publication caps (1 overview / 2–3 clauses; global overview cap 2/6); window cut to `rerankKeep`. FIRST writer of `finalHits`. |
|
|
62
|
+
| 21 | `typed-pin` | pin families resolvable | blocking (inner parent-fetch additive) | `finalHits` | Answer-contract v2: one typed unit guaranteed a slot (+ small-to-big parent fetch at `smallToBigDiscount`). |
|
|
63
|
+
| 22 | `section-descent` | a ranked depth-1 summary has children | additive | `finalHits` | Summary node → top child clauses at `sectionDescentDiscount`; the summary retires when children answer. |
|
|
64
|
+
| 23 | `dedup` | — | blocking | `finalHits` | FABLE ancestor-descendant same-chain collapse (≥0.5 text overlap). |
|
|
65
|
+
| 24 | `window-floor` | — | blocking | filters `finalHits` | Evidence-budget cut at `windowFloorFraction` of top; typed/family/unscored exempt; never fewer than two. |
|
|
66
|
+
|
|
67
|
+
## Ordering dependencies (why the order is what it is)
|
|
68
|
+
|
|
69
|
+
- **Lanes before the pool opens** (1–7): every candidate lane competes in
|
|
70
|
+
one pool; the pool is frozen at `pool-open`.
|
|
71
|
+
- **`glossary` before `concept-graph`/`concept-steer`/`typed-pin`**: all
|
|
72
|
+
three consume the vocabulary link.
|
|
73
|
+
- **`dense` before any lane that merges**: lanes are additive to the
|
|
74
|
+
primary ranking, never replacements (except `multi-query`, which
|
|
75
|
+
REPLACES `matches` — deliberate: RRF-fused variants subsume the
|
|
76
|
+
primary ranking).
|
|
77
|
+
- **`seal` before `overview-demote`/`rerank`**: the declared context is
|
|
78
|
+
a hard scope, not a preference — steering and reranking happen WITHIN
|
|
79
|
+
it.
|
|
80
|
+
- **`rerank` before every steering stage** (16–18): steering is
|
|
81
|
+
spread-scaled over rerank scores; steering before rerank would be
|
|
82
|
+
erased by the re-sort.
|
|
83
|
+
- **`structural-propagate` after steering, before `diversity`**:
|
|
84
|
+
propagation re-scores the full ranked pool; diversity then reads the
|
|
85
|
+
final order.
|
|
86
|
+
- **`diversity` is the FIRST writer of `finalHits`**: the window
|
|
87
|
+
assembly stages (21–24) operate only on the window.
|
|
88
|
+
- **`window-floor` LAST**: it is the terminal budget cut; anything
|
|
89
|
+
appended after it would escape the evidence-budget principle.
|
|
90
|
+
|
|
91
|
+
## Prefetch semantics (concurrent lane I/O)
|
|
92
|
+
|
|
93
|
+
A stage may declare `prefetch(c)`: kick its INDEPENDENT I/O off into
|
|
94
|
+
`c.lane[stage.name]` (a promise bag the stage owns). The runner invokes
|
|
95
|
+
every stage's prefetch — guard-checked — BEFORE running any stage, so
|
|
96
|
+
the independent lanes (hyde, glossary, graph-lane, multi-query,
|
|
97
|
+
sub-query) overlap with each other and with the dense lane instead of
|
|
98
|
+
serializing: ~5 summed round trips become ~1. `run()` awaits its own
|
|
99
|
+
promise and merges.
|
|
100
|
+
|
|
101
|
+
The invariants:
|
|
102
|
+
|
|
103
|
+
- **Only pre-pipeline state** — prefetch may read `u`, `vector`, `opts`,
|
|
104
|
+
never another stage's output. concept-graph deliberately does NOT
|
|
105
|
+
prefetch (its D1 lookup consumes glossary's results).
|
|
106
|
+
- **Merge order is registry order** — concurrency changes when I/O
|
|
107
|
+
completes, never when merges apply. Retrieval determinism is
|
|
108
|
+
untouched (the annealment gate re-verifies this).
|
|
109
|
+
- **Failure semantics unchanged** — a rejected prefetch promise throws
|
|
110
|
+
at the await inside `run()`, where the stage's failure mode applies.
|
|
111
|
+
|
|
112
|
+
## The prelude (not stages, deliberately)
|
|
113
|
+
|
|
114
|
+
`retrieve()` resolves the query fold, runs the dense embed and the
|
|
115
|
+
full-corpus lexical prefilter **in parallel**, and seal-filters the
|
|
116
|
+
lexical lane at the source. This parallelism is load-bearing (Option C:
|
|
117
|
+
the optimistic vector may already be resolved; embed and lexical must
|
|
118
|
+
not serialize) — it stays in the composition layer, not in stages, so
|
|
119
|
+
no stage boundary can introduce a serial round-trip on the hot path.
|
|
120
|
+
|
|
121
|
+
## Verification
|
|
122
|
+
|
|
123
|
+
- `tests/pipeline.test.ts` — the registry composition and each stage's
|
|
124
|
+
invariant over an in-memory fixture environment (no network).
|
|
125
|
+
- `tests/golden/` ×3 + annealment ×6 — the live gates (every threshold
|
|
126
|
+
change re-runs them; see `config.ts` THRESHOLDS docstrings).
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
# The vector adapter — producer formats → Vectorize, through one door
|
|
2
|
+
|
|
3
|
+
`ingest/vector_adapter.py` is the single boundary every chunk crosses to
|
|
4
|
+
become a vector. Producer projectors (`ingest/primmel.py`, `ingest/mko.py`,
|
|
5
|
+
`ingest/parse.py`) turn producer formats into dicts; the adapter turns
|
|
6
|
+
those dicts into the wire shape — validated, target-checked, and
|
|
7
|
+
schema-locked.
|
|
8
|
+
|
|
9
|
+
## Why one door (the failure modes it forecloses)
|
|
10
|
+
|
|
11
|
+
Hand-rolled per-script metadata — no tie between a chunk and its TARGET
|
|
12
|
+
index — fails in four characteristic ways, each of which the adapter
|
|
13
|
+
makes structurally impossible:
|
|
14
|
+
|
|
15
|
+
- **cross-index contamination** — a chunk written to an index its corpus
|
|
16
|
+
does not belong to (target gating refuses at adapt time and payload
|
|
17
|
+
time);
|
|
18
|
+
- **corpus vocabulary drift** — the same corpus under different names,
|
|
19
|
+
or edition values in inconsistent forms (the registry and the schema
|
|
20
|
+
are the single source of truth);
|
|
21
|
+
- **unciteable anchors** — producer UUID anchors reaching citations
|
|
22
|
+
(stripped at build time; serving hides any that remain as defense in
|
|
23
|
+
depth);
|
|
24
|
+
- **wire-time size failures** — metadata over the index limit,
|
|
25
|
+
discovered only at the upsert call (validated before any wire call).
|
|
26
|
+
|
|
27
|
+
## The contract
|
|
28
|
+
|
|
29
|
+
### ChunkMetaModel — the wire schema
|
|
30
|
+
|
|
31
|
+
Mirrors the serving contract (`workers/worker_public/src/pipeline.ts`
|
|
32
|
+
`ChunkMeta`). Pydantic (the ecosystem serialization rule: framework, not
|
|
33
|
+
hand-rolled `to_json`). `extra="forbid"` — an unknown field from a
|
|
34
|
+
producer is a SCHEMA CHANGE, decided here, never an accident at the wire.
|
|
35
|
+
|
|
36
|
+
Fields: `doc_id, docidentifier, doctype, doc_number, edition, language,
|
|
37
|
+
clause_anchor, clause_title, tier, corpus, text_ref, status,
|
|
38
|
+
superseded_by, unit_id, block, unit_hash, producer, source_lane,
|
|
39
|
+
linked_clause, linked_document, section_summary, child_anchors, ctx,
|
|
40
|
+
chunk_text`.
|
|
41
|
+
|
|
42
|
+
Validators (build-time, before any wire call):
|
|
43
|
+
|
|
44
|
+
- **corpus registry** — the value must be registered (`PRODUCTION_CORPORA`
|
|
45
|
+
∪ `LANE_CORPORA`). New corpora are added HERE, never ad hoc in a script.
|
|
46
|
+
- **clause_anchor sanity** — producer UUID anchors are stripped to `""`
|
|
47
|
+
(they are unciteable; serving treats them as garbage — this stops them
|
|
48
|
+
at the source).
|
|
49
|
+
- **chunk_text cap** — 2,800 chars (the 40016 lesson).
|
|
50
|
+
- **wire size** — serialized metadata ≤ 9,000 bytes (Vectorize hard limit
|
|
51
|
+
10KB).
|
|
52
|
+
|
|
53
|
+
### Target gating — the structural guard
|
|
54
|
+
|
|
55
|
+
`TARGET_CORPORA` maps every index to the corpora it may hold:
|
|
56
|
+
|
|
57
|
+
| Target | Legal corpora |
|
|
58
|
+
|---|---|
|
|
59
|
+
| `production` (`idx_oiml_public_v2`) | oiml, dirty, clean, synthetic, smart-model |
|
|
60
|
+
| `exp_plain` / `exp_adoc` / `exp_mko` / `exp_composed` | the same-named lane corpus |
|
|
61
|
+
| `primmel` | primmel |
|
|
62
|
+
| `primmel_flat` | primmel (the ablation indexes the same corpus, unlinked) |
|
|
63
|
+
|
|
64
|
+
`normalize_chunk(raw, target=…)` refuses a chunk foreign to its target at
|
|
65
|
+
ADAPT time; `chunk.upsert(vector, target)` refuses again at payload-build
|
|
66
|
+
time. A lane corpus can no longer enter production through any code path
|
|
67
|
+
that builds payloads through the adapter.
|
|
68
|
+
|
|
69
|
+
### Producer remaps live here, not in scripts
|
|
70
|
+
|
|
71
|
+
The production MKO flow remaps `corpus mko → oiml` (+`producer: mko`,
|
|
72
|
+
`tier: curated`) inside `normalize_chunk(target="production")`; the lane
|
|
73
|
+
MKO flow keeps its `exp_mko` namespace. Previously this remap lived in
|
|
74
|
+
`ingest/enrich.py` — buried in a driver, duplicated by hand elsewhere.
|
|
75
|
+
|
|
76
|
+
## Serving mirror
|
|
77
|
+
|
|
78
|
+
The TypeScript `ChunkMeta` interface in `pipeline.ts` is the serving-side
|
|
79
|
+
read of the same contract (plus its own optional fields). Change either
|
|
80
|
+
side deliberately: adapter schema → wire shape → serving interface.
|
|
81
|
+
|
|
82
|
+
## Call sites
|
|
83
|
+
|
|
84
|
+
- `scripts/index_comparison_lanes.py` — every lane upsert builds its
|
|
85
|
+
payload through `normalize_chunk(...).upsert(..., target=ADAPTER_TARGET[lane])`.
|
|
86
|
+
- Future producers (and the metanorma-ai deployment package's
|
|
87
|
+
document→vectors pipeline component) adopt the same door: project to
|
|
88
|
+
dicts, adapt through this module, never hand-roll a metadata dict again.
|
package/package.json
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@konneal/engine",
|
|
3
|
+
"workspaces": [
|
|
4
|
+
"workers/*"
|
|
5
|
+
],
|
|
6
|
+
"scripts": {
|
|
7
|
+
"typecheck": "tsc -p workers/worker_public",
|
|
8
|
+
"deploy:public": "npm run deploy -w workers/worker_public",
|
|
9
|
+
"dev:public": "npm run dev -w workers/worker_public",
|
|
10
|
+
"deploy": "./scripts/deploy.sh",
|
|
11
|
+
"test:golden": "node scripts/eval.mjs",
|
|
12
|
+
"test:e2e": "node tests/e2e.mjs",
|
|
13
|
+
"lint:wrangler": "python3 scripts/lint_wrangler.py",
|
|
14
|
+
"test:ui": "node tests/ui.mjs",
|
|
15
|
+
"test:bridge": "node --test tests/bridge.test.mjs",
|
|
16
|
+
"test:units": "node --test --experimental-strip-types tests/*.test.ts",
|
|
17
|
+
"build": "rm -rf dist dist-types && esbuild workers/worker_public/src/index.ts workers/worker_public/src/config.ts workers/worker_public/src/refusal.ts workers/worker_public/src/profile.ts workers/worker_public/src/requestScope.ts --bundle --format=esm --splitting --platform=neutral --outdir=dist --loader:.md=text && tsc -p tsconfig.build.json && cp dist-types/worker_public/src/*.d.ts dist/ && mkdir -p dist/prompts && cp workers/worker_public/prompts/*.md dist/prompts/",
|
|
18
|
+
"prepare": "npm run build",
|
|
19
|
+
"lint:ports": "node scripts/lint-ports.mjs"
|
|
20
|
+
},
|
|
21
|
+
"devDependencies": {
|
|
22
|
+
"@cloudflare/workers-types": "^5.20260911.1",
|
|
23
|
+
"esbuild": "^0.28.2",
|
|
24
|
+
"jsdom": "^29.1.1",
|
|
25
|
+
"playwright": "^1.62.1",
|
|
26
|
+
"yaml": "^2.9.1"
|
|
27
|
+
},
|
|
28
|
+
"dependencies": {
|
|
29
|
+
"@astrojs/markdown-satteri": "^0.4.1",
|
|
30
|
+
"@astrojs/mdx": "^4.3.14"
|
|
31
|
+
},
|
|
32
|
+
"version": "0.1.0",
|
|
33
|
+
"description": "The Konneal engine: the publisher-agnostic build pipeline and API plane for standards intelligence (retrieval, answer contract, verdicts, evaluation).",
|
|
34
|
+
"license": "BSD-3-Clause",
|
|
35
|
+
"type": "module",
|
|
36
|
+
"main": "./dist/index.js",
|
|
37
|
+
"exports": {
|
|
38
|
+
".": {
|
|
39
|
+
"types": "./dist/index.d.ts",
|
|
40
|
+
"default": "./dist/index.js"
|
|
41
|
+
},
|
|
42
|
+
"./setProfile": {
|
|
43
|
+
"types": "./dist/profile.d.ts",
|
|
44
|
+
"default": "./dist/profile.js"
|
|
45
|
+
},
|
|
46
|
+
"./config": {
|
|
47
|
+
"types": "./dist/config.d.ts",
|
|
48
|
+
"default": "./dist/config.js"
|
|
49
|
+
},
|
|
50
|
+
"./refusal": {
|
|
51
|
+
"types": "./dist/refusal.d.ts",
|
|
52
|
+
"default": "./dist/refusal.js"
|
|
53
|
+
},
|
|
54
|
+
"./requestScope": {
|
|
55
|
+
"types": "./dist/requestScope.d.ts",
|
|
56
|
+
"default": "./dist/requestScope.js"
|
|
57
|
+
}
|
|
58
|
+
},
|
|
59
|
+
"files": [
|
|
60
|
+
"dist",
|
|
61
|
+
"workers",
|
|
62
|
+
"docs",
|
|
63
|
+
"profile",
|
|
64
|
+
"scripts/gen_profile.mjs",
|
|
65
|
+
"README.md"
|
|
66
|
+
],
|
|
67
|
+
"publishConfig": {
|
|
68
|
+
"access": "public"
|
|
69
|
+
}
|
|
70
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
datasets:
|
|
2
|
+
- id: pub
|
|
3
|
+
label: Fixture Publications
|
|
4
|
+
description: The fixture publisher's corpus
|
|
5
|
+
corpora: [pub, dirty, clean, synthetic]
|
|
6
|
+
note: >-
|
|
7
|
+
Some passages come from the fixture corpus — cite them the same
|
|
8
|
+
way as every other passage.
|
|
9
|
+
- id: internal
|
|
10
|
+
label: Internal corpus
|
|
11
|
+
description: An access-restricted corpus proving the permission gate
|
|
12
|
+
session: true
|
|
13
|
+
permission: preview
|
|
14
|
+
corpora: [internal]
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# The engine's fixture profile — the reference matrix starts here. A
|
|
2
|
+
# real deployment (e.g. oimlsmart/ai) carries its own publisher facts;
|
|
3
|
+
# this fixture exists so the engine's tests and CI run against
|
|
4
|
+
# declared data, never hardcoded ones.
|
|
5
|
+
id: fixture
|
|
6
|
+
name: Fixture
|
|
7
|
+
full_name: The Fixture Publisher
|
|
8
|
+
product_name: Fixture Answers
|
|
9
|
+
description: >-
|
|
10
|
+
A minimal publisher profile exercising every declared surface: an
|
|
11
|
+
open dataset, a permission-gated dataset, production and lane
|
|
12
|
+
corpora, prompt vars and retrieval vocabulary.
|
|
13
|
+
domains:
|
|
14
|
+
public: fixture.example.org
|
|
15
|
+
identity:
|
|
16
|
+
issuer: https://id.fixture.example.org
|
|
17
|
+
codec: plain-slug
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
process_expansion: " fixture certification system framework application evaluation"
|
package/profile/ui.yaml
ADDED