@konneal/engine 0.2.176 → 0.2.177

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.
@@ -1,8 +1,8 @@
1
1
  import {
2
2
  handleAsk
3
- } from "./chunk-T6T2QUO6.js";
4
- import "./chunk-ECOYO7EX.js";
5
- import "./chunk-B2MU4ZWM.js";
3
+ } from "./chunk-K4IEKILQ.js";
4
+ import "./chunk-JKBU2IPM.js";
5
+ import "./chunk-RO5LNSPH.js";
6
6
  import "./chunk-CLDMP5YH.js";
7
7
  import "./chunk-ZHT4456M.js";
8
8
  import "./chunk-D7MO4RVQ.js";
@@ -6,12 +6,14 @@ import {
6
6
  isRegisterShaped,
7
7
  printedCertificateNumber,
8
8
  queryFamily,
9
+ rankRegisterRows,
9
10
  registerAggregate,
10
11
  registerAggregateIntent,
12
+ registerBypassesCache,
11
13
  registerNote,
12
14
  registerTokens,
13
15
  searchRegister
14
- } from "./chunk-ECOYO7EX.js";
16
+ } from "./chunk-JKBU2IPM.js";
15
17
  import "./chunk-3FYJM7LH.js";
16
18
  export {
17
19
  aggregateBlock,
@@ -21,8 +23,10 @@ export {
21
23
  isRegisterShaped,
22
24
  printedCertificateNumber,
23
25
  queryFamily,
26
+ rankRegisterRows,
24
27
  registerAggregate,
25
28
  registerAggregateIntent,
29
+ registerBypassesCache,
26
30
  registerNote,
27
31
  registerTokens,
28
32
  searchRegister
@@ -20,6 +20,14 @@ export declare function isRegisterShaped(query: string): boolean;
20
20
  * narrates it (TODO.sota/04's machine-checkable ground truth, applied
21
21
  * to the register plane 2026-10-11). */
22
22
  export declare function registerAggregateIntent(query: string): "count" | "latest" | null;
23
+ /** The register's answers derive from the D1 snapshot, not the indexed
24
+ * corpus — but the answer cache (namespaced by INDEX_VERSION +
25
+ * corpus_gen) knows nothing about D1-plane loads, so a cached
26
+ * pre-register refusal preempted register-shaped questions the register
27
+ * answers (measured live 2026-10-28: cached refusals served over
28
+ * register-shaped asks until fresh). Such asks skip both cache reads
29
+ * and writes. */
30
+ export declare function registerBypassesCache(query: string): boolean;
23
31
  export interface RegisterAggregate {
24
32
  family: string;
25
33
  count: number;
@@ -84,6 +92,15 @@ export declare function searchRegister(db: any, query: string, force?: boolean):
84
92
  rows: RegisterRow[];
85
93
  tokens: string[];
86
94
  } | null>;
95
+ /** Rank the OR-matched candidates so the note's six rows carry the
96
+ * QUESTION's model, not just its holder: the live probes showed a
97
+ * distinctive model token losing its place to generic holder words
98
+ * ("Rice Lake Weighing Systems" matched 6 rows before "RL116XP" was
99
+ * ever consulted — the answer then denied a certificate the register
100
+ * holds). A model-token hit outranks a holder-token hit; numbers must
101
+ * match as standalone values (the "190 must not catch XK3190" rule).
102
+ * Measured 2026-10-28, the register-lane eval. */
103
+ export declare function rankRegisterRows(rows: RegisterRow[], tokens: string[]): RegisterRow[];
87
104
  export declare function registerNote(rows: RegisterRow[]): string;
88
105
  /** Each certificate's location rides the note as a link when its PDF
89
106
  * is in the R2 plane (TODO 7) — the user gets the document, not just
@@ -12,6 +12,9 @@ function registerAggregateIntent(query) {
12
12
  if (/\b(how many|count|number of|total|how much)\b/i.test(query)) return "count";
13
13
  return null;
14
14
  }
15
+ function registerBypassesCache(query) {
16
+ return isRegisterShaped(query) || registerAggregateIntent(query) !== null;
17
+ }
15
18
  async function registerAggregate(db, family) {
16
19
  try {
17
20
  const digits = family.replace(/^[A-Za-z]+/, "");
@@ -156,7 +159,7 @@ function buildRegisterQuery(tokens) {
156
159
  return `(holder LIKE ?${n} OR model LIKE ?${n} OR num LIKE ?${n})`;
157
160
  });
158
161
  return {
159
- sql: `SELECT num, family, holder, model, year, status, pdf_key FROM certificates WHERE family = ?FAMILY AND (${clauses.join(" OR ")}) LIMIT 6`,
162
+ sql: `SELECT num, family, holder, model, year, status, pdf_key FROM certificates WHERE family = ?FAMILY AND (${clauses.join(" OR ")}) LIMIT 40`,
160
163
  params
161
164
  };
162
165
  }
@@ -190,18 +193,36 @@ async function searchRegister(db, query, force = false) {
190
193
  }
191
194
  try {
192
195
  const res = await db.prepare(sql).bind(...params).all();
193
- return { rows: res.results ?? [], tokens };
196
+ const rows = rankRegisterRows(res.results ?? [], tokens);
197
+ return { rows, tokens };
194
198
  } catch {
195
199
  return { rows: [], tokens };
196
200
  }
197
201
  }
202
+ function rankRegisterRows(rows, tokens) {
203
+ const score = (row) => {
204
+ const model = (row.model || "").toLowerCase();
205
+ const holder = (row.holder || "").toLowerCase();
206
+ let s = 0;
207
+ for (const t of tokens) {
208
+ const tl = t.toLowerCase();
209
+ if (/^\d+$/.test(t)) {
210
+ if (new RegExp(`(^|[^a-z0-9])${tl}([^a-z0-9]|$)`).test(model)) s += 3;
211
+ else if (holder.includes(tl)) s += 1;
212
+ } else if (model.includes(tl)) s += 2;
213
+ else if (holder.includes(tl)) s += 1;
214
+ }
215
+ return s;
216
+ };
217
+ return [...rows].sort((a, b) => score(b) - score(a) || Number(b.year) - Number(a.year)).slice(0, 6);
218
+ }
198
219
  function registerNote(rows) {
199
220
  if (!rows.length) {
200
- return `Certificate register: NO certificate matching the asked holder or model appears in the ${P().publisher.name}-CS register snapshot. State plainly that no such certificate is in this register, and that the register is a snapshot rather than the live certification status.`;
221
+ return `Certificate register \u2014 THIS NOTE ANSWERS THE QUESTION. NO certificate matching the asked holder or model appears in the ${P().publisher.name}-CS register snapshot. Answer from THIS note: state plainly that no such certificate is in this register \u2014 never fall back to the indexed publications' absence phrasing \u2014 and say the register is a snapshot rather than the live certification status.`;
201
222
  }
202
223
  const lines = rows.map((r) => `- ${r.num}: holder ${r.holder}, model "${r.model}"${r.year ? `, issued ${r.year}` : ""} \u2014 status ${r.status}`);
203
224
  return [
204
- "Certificate register \u2014 the following certificates matched the asked holder or model. Quote the certificate number, the holder and the status verbatim; the register is a snapshot, so qualify any statement about current certification accordingly.",
225
+ "Certificate register \u2014 THIS NOTE ANSWERS THE QUESTION. The following certificates matched the asked holder or model; answer from THIS note. A model the note does not list is not among the matched rows \u2014 say so plainly instead of falling back to the indexed publications. Quote the certificate number, the holder and the status verbatim; the register is a snapshot, so qualify any statement about current certification accordingly.",
205
226
  ...lines
206
227
  ].join("\n");
207
228
  }
@@ -219,6 +240,7 @@ ${absent.join("\n")}`);
219
240
  export {
220
241
  isRegisterShaped,
221
242
  registerAggregateIntent,
243
+ registerBypassesCache,
222
244
  registerAggregate,
223
245
  aggregateNote,
224
246
  aggregateBlock,
@@ -227,6 +249,7 @@ export {
227
249
  registerTokens,
228
250
  buildRegisterQuery,
229
251
  searchRegister,
252
+ rankRegisterRows,
230
253
  registerNote,
231
254
  certificateLinks
232
255
  };
@@ -5,9 +5,10 @@ import {
5
5
  queryFamily,
6
6
  registerAggregate,
7
7
  registerAggregateIntent,
8
+ registerBypassesCache,
8
9
  registerNote,
9
10
  searchRegister
10
- } from "./chunk-ECOYO7EX.js";
11
+ } from "./chunk-JKBU2IPM.js";
11
12
  import {
12
13
  TOOL_DECLARATION,
13
14
  bindConstraintByQuantities,
@@ -32,7 +33,7 @@ import {
32
33
  tableRetyped,
33
34
  toolNote,
34
35
  verdictNote
35
- } from "./chunk-B2MU4ZWM.js";
36
+ } from "./chunk-RO5LNSPH.js";
36
37
  import {
37
38
  NO_CONTEXT,
38
39
  STAGE_NAMES,
@@ -2221,7 +2222,8 @@ async function handleAsk(env, ctx, req, tier, key) {
2221
2222
  const multiTurn = body?.multi_turn !== false;
2222
2223
  const gen = await corpusGen(env.CACHE);
2223
2224
  const withImage = effectiveImage || storedImage;
2224
- const cached = fresh || contextual || declaredCtx || draftAct || apiCallIntent || withImage || ablate ? null : await cacheGet(env, gen, ns, q.query, q.lang, salt);
2225
+ const registerBypass = registerBypassesCache(q.query);
2226
+ const cached = fresh || contextual || declaredCtx || draftAct || apiCallIntent || withImage || ablate || registerBypass ? null : await cacheGet(env, gen, ns, q.query, q.lang, salt);
2225
2227
  let probeTokensUsed = 0;
2226
2228
  const pipeline = async (channel, qq) => {
2227
2229
  const live2 = channel;
@@ -3080,7 +3082,7 @@ Answer account questions from these records ONLY: name the record when you use i
3080
3082
  } catch {
3081
3083
  }
3082
3084
  }
3083
- if (streamed.violations.length === 0 && canonical.length > 0 && !contextual && !declaredCtx && cacheableAnswer(canonical, refusalScope(answerCites(canonical))) && !ablate) {
3085
+ if (streamed.violations.length === 0 && canonical.length > 0 && !contextual && !declaredCtx && !registerBypass && cacheableAnswer(canonical, refusalScope(answerCites(canonical))) && !ablate) {
3084
3086
  ctx.waitUntil(
3085
3087
  env.CACHE.put(exactCacheKey(env.INDEX_VERSION, gen, ns, await sha256Hex(cacheKeyMaterial(q2.query, q2.lang, salt))), JSON.stringify({ answer: canonical, citations: cites, model, query_hash: queryHash, ...sourceQuality ? { source_quality: sourceQuality, confidence_note: streamedNote || qualityNote(sourceQuality) } : {}, ...streamedEntailment ? { entailment: streamedEntailment } : {} }), { expirationTtl: LIMITS.cacheTtlSec })
3086
3088
  );
@@ -3240,7 +3242,7 @@ Answer account questions from these records ONLY: name the record when you use i
3240
3242
  telemetryDecisions(env, ctx, tier, queryHash, decisionsOut ?? null, Date.now() - tStart);
3241
3243
  }
3242
3244
  const out = { answer, citations: finalCites, ...jsonQuality ? { source_quality: jsonQuality, confidence_note: entailmentNote || qualityNote(jsonQuality), ...entailment ? { entailment } : {}, ...jsonQuality === "ocr" ? { experimental_sources: experimentalSourceLabels(finalCites) } : {} } : {}, model: MODELS.member, query_hash: queryHash, follow_ups: refusalShaped(answer, refusalScope(answerCites(answer))) ? [] : understanding?.follow_ups ?? [], blocks: [...c2ns.blocks, ...verdictBlock ? [verdictBlock] : [], ...conditionBlock ? [conditionBlock] : [], ...aggregationBlock ? [aggregationBlock] : [], ...registerBlock ? [registerBlock] : [], ...completionBlocks], context_applied: ctxApplied, ...liveRecords ? { records: liveRecords } : {}, ...decisionsOut ? { decisions: decisionsOut } : {}, ...ablate ? { ablate } : {}, route: { route: routed.route, features: routed.features } };
3243
- const cacheable = !contextual && !declaredCtx && !ablate && cacheableAnswer(answer, refusalScope(answerCites(answer))) && finalAnchors.violations.length === 0;
3245
+ const cacheable = !contextual && !declaredCtx && !ablate && !registerBypass && cacheableAnswer(answer, refusalScope(answerCites(answer))) && finalAnchors.violations.length === 0;
3244
3246
  if (cacheable) {
3245
3247
  const ck = exactCacheKey(env.INDEX_VERSION, gen, ns, await sha256Hex(cacheKeyMaterial(q2.query, q2.lang, salt)));
3246
3248
  ctx.waitUntil(env.CACHE.put(ck, JSON.stringify(out), { expirationTtl: LIMITS.cacheTtlSec }));
@@ -1565,7 +1565,7 @@ var certificatesSearch = {
1565
1565
  handler: async (env, args) => {
1566
1566
  const query = String(args?.query ?? "").trim().slice(0, 160);
1567
1567
  if (!query) return null;
1568
- const { searchRegister, registerNote, certificateLinks } = await import("./certificates-F5G7GRWK.js");
1568
+ const { searchRegister, registerNote, certificateLinks } = await import("./certificates-QHFH2KLH.js");
1569
1569
  const reg = await searchRegister(env.DB, query, true);
1570
1570
  const output = reg?.rows?.length ? [registerNote(reg.rows), certificateLinks(reg.rows)].filter(Boolean).join("\n") : `No certificate was found for "${query}" in the certificates database (the register snapshot). State this as the search's result, with the searched string visible.`;
1571
1571
  return { name: "certificates.search", query, output };
@@ -4,7 +4,7 @@ import {
4
4
  parseToolCall,
5
5
  runTool,
6
6
  toolNote
7
- } from "./chunk-B2MU4ZWM.js";
7
+ } from "./chunk-RO5LNSPH.js";
8
8
  import "./chunk-ZHT4456M.js";
9
9
  import "./chunk-FNQ7UOEB.js";
10
10
  import "./chunk-HWWJAJWM.js";
@@ -4,13 +4,13 @@ import {
4
4
  handleMemories,
5
5
  scoreFaithfulness,
6
6
  scoreJudge
7
- } from "../../chunk-T6T2QUO6.js";
8
- import "../../chunk-ECOYO7EX.js";
7
+ } from "../../chunk-K4IEKILQ.js";
8
+ import "../../chunk-JKBU2IPM.js";
9
9
  import {
10
10
  TOOLS_REGISTRY,
11
11
  bindModelNode,
12
12
  modelGroundingBlock
13
- } from "../../chunk-B2MU4ZWM.js";
13
+ } from "../../chunk-RO5LNSPH.js";
14
14
  import {
15
15
  handleSearch
16
16
  } from "../../chunk-MUWO2EV6.js";
@@ -1175,13 +1175,13 @@ async function callToolOnce(env, ctx, _req, tier, key, name, args, t0) {
1175
1175
  // stream:false forces the JSON lane (anon defaults to SSE)
1176
1176
  body: JSON.stringify({ ...args, stream: false })
1177
1177
  });
1178
- const { runTool, TOOLS_REGISTRY: TOOLS_REGISTRY2 } = await import("../../tools-VHN54DWH.js");
1178
+ const { runTool, TOOLS_REGISTRY: TOOLS_REGISTRY2 } = await import("../../tools-DKQZGSWK.js");
1179
1179
  let payload;
1180
1180
  if (TOOLS_REGISTRY2.some((t) => t.name === name && t.audiences.includes("mcp"))) {
1181
1181
  const r = await runTool({ ...env, DB: env.DB }, { name, args }, "mcp");
1182
1182
  payload = r ? { tool: r.name, query: r.query, result: r.output } : { error: { message: `tool ${name} returned nothing for the given arguments` } };
1183
1183
  } else {
1184
- const res = name === "ask" ? await (await import("../../ask-ZWZFZBKZ.js")).handleAsk(env, ctx, inner, tier, key) : await (await import("../../search-KNPPTHMG.js")).handleSearch(env, ctx, inner, tier, key);
1184
+ const res = name === "ask" ? await (await import("../../ask-2UHDJPEU.js")).handleAsk(env, ctx, inner, tier, key) : await (await import("../../search-KNPPTHMG.js")).handleSearch(env, ctx, inner, tier, key);
1185
1185
  payload = await res.json().catch(() => ({ error: { message: "tool transport failed", status: res.status } }));
1186
1186
  }
1187
1187
  telemetry(env, ctx, tier, `mcp:${name}`, null, !payload?.error, JSON.stringify(payload).length, await sha256Hex(`${name}:${JSON.stringify(args ?? {})}`), void 0, void 0, { durationMs: Date.now() - t0 });
package/package.json CHANGED
@@ -27,7 +27,7 @@
27
27
  "playwright": "^1.62.1",
28
28
  "yaml": "^2.9.1"
29
29
  },
30
- "version": "0.2.176",
30
+ "version": "0.2.177",
31
31
  "description": "The Konneal engine: the publisher-agnostic build pipeline and API plane for standards intelligence (retrieval, answer contract, verdicts, evaluation).",
32
32
  "license": "BSD-3-Clause",
33
33
  "type": "module",
@@ -46,7 +46,7 @@ export type { Env };
46
46
  import { json, err, corsHeaders, readJson, validateQuery, type ApiKey } from "./lib/http";
47
47
  import { chargeTokens, checkQuota, clientIp, creditBudget, creditEstimate, estimateTokens, telemetry, tokenBudget, usageTotal } from "./quota";
48
48
  import { graphExpand, editionNote } from "./graph";
49
- import { registerNote, searchRegister, certificateLinks, registerAggregateIntent, registerAggregate, aggregateNote, aggregateBlock, queryFamily } from "./certificates";
49
+ import { registerNote, searchRegister, certificateLinks, registerAggregateIntent, registerAggregate, aggregateNote, aggregateBlock, queryFamily, registerBypassesCache } from "./certificates";
50
50
  import { TOOL_DECLARATION, parseToolCall, runTool, toolNote } from "./tools";
51
51
  import { operationsLane } from "./operations.ts";
52
52
  import { extractNameplate, nameplateRegisterQuery } from "./nameplate.ts";
@@ -594,7 +594,8 @@ async function handleAsk(
594
594
  // cache.py) and old-generation entries miss (oimlsmart/rag#72)
595
595
  const gen = await corpusGen(env.CACHE);
596
596
  const withImage = effectiveImage || storedImage;
597
- const cached = fresh || contextual || declaredCtx || draftAct || apiCallIntent || withImage || ablate ? null : await cacheGet(env, gen, ns, q.query, q.lang, salt);
597
+ const registerBypass = registerBypassesCache(q.query);
598
+ const cached = fresh || contextual || declaredCtx || draftAct || apiCallIntent || withImage || ablate || registerBypass ? null : await cacheGet(env, gen, ns, q.query, q.lang, salt);
598
599
  let probeTokensUsed = 0;
599
600
 
600
601
  // THE PIPELINE: one body, two deliveries. Every `return` inside is an
@@ -1837,7 +1838,7 @@ let convEntities: Array<{ entity: string; kind: string }> = [];
1837
1838
  } });
1838
1839
  } catch { /* the client may have closed; never block the close */ }
1839
1840
  }
1840
- if (streamed.violations.length === 0 && canonical.length > 0 && !contextual && !declaredCtx && cacheableAnswer(canonical, refusalScope(answerCites(canonical))) && !ablate) {
1841
+ if (streamed.violations.length === 0 && canonical.length > 0 && !contextual && !declaredCtx && !registerBypass && cacheableAnswer(canonical, refusalScope(answerCites(canonical))) && !ablate) {
1841
1842
  ctx.waitUntil(
1842
1843
  env.CACHE.put(exactCacheKey(env.INDEX_VERSION, gen, ns, await sha256Hex(cacheKeyMaterial(q.query, q.lang, salt))), JSON.stringify({ answer: canonical, citations: cites, model, query_hash: queryHash, ...(sourceQuality ? { source_quality: sourceQuality, confidence_note: streamedNote || qualityNote(sourceQuality) } : {}), ...(streamedEntailment ? { entailment: streamedEntailment } : {}) }), { expirationTtl: LIMITS.cacheTtlSec }),
1843
1844
  );
@@ -2096,7 +2097,7 @@ let convEntities: Array<{ entity: string; kind: string }> = [];
2096
2097
  telemetryDecisions(env, ctx, tier, queryHash, decisionsOut ?? null, Date.now() - tStart);
2097
2098
  }
2098
2099
  const out = { answer, citations: finalCites, ...(jsonQuality ? { source_quality: jsonQuality!, confidence_note: entailmentNote || qualityNote(jsonQuality!), ...(entailment ? { entailment } : {}), ...(jsonQuality === "ocr" ? { experimental_sources: experimentalSourceLabels(finalCites) } : {}) } : {}), model: MODELS.member, query_hash: queryHash, follow_ups: refusalShaped(answer!, refusalScope(answerCites(answer!))) ? [] : (understanding?.follow_ups ?? []), blocks: [...c2ns.blocks, ...(verdictBlock ? [verdictBlock] : []), ...(conditionBlock ? [conditionBlock] : []), ...(aggregationBlock ? [aggregationBlock] : []), ...(registerBlock ? [registerBlock] : []), ...completionBlocks], context_applied: ctxApplied, ...(liveRecords ? { records: liveRecords } : {}), ...(decisionsOut ? { decisions: decisionsOut } : {}), ...(ablate ? { ablate } : {}), route: { route: routed.route, features: routed.features } };
2099
- const cacheable = !contextual && !declaredCtx && !ablate && cacheableAnswer(answer!, refusalScope(answerCites(answer!))) && finalAnchors.violations.length === 0;
2100
+ const cacheable = !contextual && !declaredCtx && !ablate && !registerBypass && cacheableAnswer(answer!, refusalScope(answerCites(answer!))) && finalAnchors.violations.length === 0;
2100
2101
  if (cacheable) {
2101
2102
  const ck = exactCacheKey(env.INDEX_VERSION, gen, ns, await sha256Hex(cacheKeyMaterial(q.query, q.lang, salt)));
2102
2103
  ctx.waitUntil(env.CACHE.put(ck, JSON.stringify(out), { expirationTtl: LIMITS.cacheTtlSec }));
@@ -40,6 +40,17 @@ export function registerAggregateIntent(query: string): "count" | "latest" | nul
40
40
  return null;
41
41
  }
42
42
 
43
+ /** The register's answers derive from the D1 snapshot, not the indexed
44
+ * corpus — but the answer cache (namespaced by INDEX_VERSION +
45
+ * corpus_gen) knows nothing about D1-plane loads, so a cached
46
+ * pre-register refusal preempted register-shaped questions the register
47
+ * answers (measured live 2026-10-28: cached refusals served over
48
+ * register-shaped asks until fresh). Such asks skip both cache reads
49
+ * and writes. */
50
+ export function registerBypassesCache(query: string): boolean {
51
+ return isRegisterShaped(query) || registerAggregateIntent(query) !== null;
52
+ }
53
+
43
54
  export interface RegisterAggregate {
44
55
  family: string;
45
56
  count: number;
@@ -208,7 +219,7 @@ export function buildRegisterQuery(tokens: string[]): { sql: string; params: str
208
219
  return `(holder LIKE ?${n} OR model LIKE ?${n} OR num LIKE ?${n})`;
209
220
  });
210
221
  return {
211
- sql: `SELECT num, family, holder, model, year, status, pdf_key FROM certificates WHERE family = ?FAMILY AND (${clauses.join(" OR ")}) LIMIT 6`,
222
+ sql: `SELECT num, family, holder, model, year, status, pdf_key FROM certificates WHERE family = ?FAMILY AND (${clauses.join(" OR ")}) LIMIT 40`,
212
223
  params,
213
224
  };
214
225
  }
@@ -257,20 +268,49 @@ export async function searchRegister(db: any, query: string, force = false): Pro
257
268
  }
258
269
  try {
259
270
  const res = await db.prepare(sql).bind(...params).all();
260
- return { rows: (res.results ?? []) as RegisterRow[], tokens };
271
+ const rows = rankRegisterRows((res.results ?? []) as RegisterRow[], tokens);
272
+ return { rows, tokens };
261
273
  } catch {
262
274
  // a missing table or a D1 hiccup degrades to no-note honestly
263
275
  return { rows: [], tokens };
264
276
  }
265
277
  }
266
278
 
279
+ /** Rank the OR-matched candidates so the note's six rows carry the
280
+ * QUESTION's model, not just its holder: the live probes showed a
281
+ * distinctive model token losing its place to generic holder words
282
+ * ("Rice Lake Weighing Systems" matched 6 rows before "RL116XP" was
283
+ * ever consulted — the answer then denied a certificate the register
284
+ * holds). A model-token hit outranks a holder-token hit; numbers must
285
+ * match as standalone values (the "190 must not catch XK3190" rule).
286
+ * Measured 2026-10-28, the register-lane eval. */
287
+ export function rankRegisterRows(rows: RegisterRow[], tokens: string[]): RegisterRow[] {
288
+ const score = (row: RegisterRow): number => {
289
+ const model = (row.model || "").toLowerCase();
290
+ const holder = (row.holder || "").toLowerCase();
291
+ let s = 0;
292
+ for (const t of tokens) {
293
+ const tl = t.toLowerCase();
294
+ if (/^\d+$/.test(t)) {
295
+ if (new RegExp(`(^|[^a-z0-9])${tl}([^a-z0-9]|$)`).test(model)) s += 3;
296
+ else if (holder.includes(tl)) s += 1;
297
+ } else if (model.includes(tl)) s += 2;
298
+ else if (holder.includes(tl)) s += 1;
299
+ }
300
+ return s;
301
+ };
302
+ return [...rows]
303
+ .sort((a, b) => score(b) - score(a) || Number(b.year) - Number(a.year))
304
+ .slice(0, 6);
305
+ }
306
+
267
307
  export function registerNote(rows: RegisterRow[]): string {
268
308
  if (!rows.length) {
269
- return `Certificate register: NO certificate matching the asked holder or model appears in the ${P().publisher.name}-CS register snapshot. State plainly that no such certificate is in this register, and that the register is a snapshot rather than the live certification status.`;
309
+ return `Certificate register — THIS NOTE ANSWERS THE QUESTION. NO certificate matching the asked holder or model appears in the ${P().publisher.name}-CS register snapshot. Answer from THIS note: state plainly that no such certificate is in this register — never fall back to the indexed publications' absence phrasing — and say the register is a snapshot rather than the live certification status.`;
270
310
  }
271
311
  const lines = rows.map((r) => `- ${r.num}: holder ${r.holder}, model "${r.model}"${r.year ? `, issued ${r.year}` : ""} — status ${r.status}`);
272
312
  return [
273
- "Certificate register — the following certificates matched the asked holder or model. Quote the certificate number, the holder and the status verbatim; the register is a snapshot, so qualify any statement about current certification accordingly.",
313
+ "Certificate register — THIS NOTE ANSWERS THE QUESTION. The following certificates matched the asked holder or model; answer from THIS note. A model the note does not list is not among the matched rows — say so plainly instead of falling back to the indexed publications. Quote the certificate number, the holder and the status verbatim; the register is a snapshot, so qualify any statement about current certification accordingly.",
274
314
  ...lines,
275
315
  ].join("\n");
276
316
  }