@konneal/engine 0.2.73 → 0.2.75

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,12 +1,12 @@
1
1
  import {
2
2
  handleAsk
3
- } from "./chunk-V7PEVNNO.js";
4
- import "./chunk-LL3LWBZH.js";
3
+ } from "./chunk-XJPJO376.js";
4
+ import "./chunk-7HWJOFTQ.js";
5
5
  import "./chunk-SPKPH54J.js";
6
6
  import "./chunk-A3QHHUN5.js";
7
7
  import "./chunk-LVZTWUVJ.js";
8
8
  import "./chunk-2PCUAJJO.js";
9
- import "./chunk-3IRX4B7Q.js";
9
+ import "./chunk-VGVTIPFW.js";
10
10
  import "./chunk-3FYJM7LH.js";
11
11
  export {
12
12
  handleAsk
@@ -6,7 +6,7 @@ import {
6
6
  registerNote,
7
7
  registerTokens,
8
8
  searchRegister
9
- } from "./chunk-3IRX4B7Q.js";
9
+ } from "./chunk-VGVTIPFW.js";
10
10
  import "./chunk-3FYJM7LH.js";
11
11
  export {
12
12
  buildRegisterQuery,
@@ -45,13 +45,16 @@ async function rerank(ai, model, query, texts) {
45
45
  async function generateOnce(env, model, messages, effort) {
46
46
  for (let attempt2 = 0; attempt2 < 2; attempt2++) {
47
47
  try {
48
- const res = await env.AI.run(model, {
49
- messages,
50
- max_tokens: effortBudget(effort ?? answerEffort(env)),
51
- reasoning_effort: effort ?? answerEffort(env),
52
- temperature: 0.6,
53
- top_p: 0.95
54
- });
48
+ const res = await Promise.race([
49
+ env.AI.run(model, {
50
+ messages,
51
+ max_tokens: effortBudget(effort ?? answerEffort(env)),
52
+ reasoning_effort: effort ?? answerEffort(env),
53
+ temperature: 0.6,
54
+ top_p: 0.95
55
+ }),
56
+ new Promise((r) => setTimeout(() => r(null), 12e4))
57
+ ]);
55
58
  if (typeof res?.response === "string" && res.response.trim()) return res.response;
56
59
  if (typeof res?.choices?.[0]?.message?.content === "string" && res.choices[0].message.content.trim()) return res.choices[0].message.content;
57
60
  if (attempt2 === 0) console.error("generate returned empty:", model);
@@ -7,7 +7,7 @@ import {
7
7
  sessionFrom,
8
8
  telemetry,
9
9
  understandQuery
10
- } from "./chunk-LL3LWBZH.js";
10
+ } from "./chunk-7HWJOFTQ.js";
11
11
  import {
12
12
  corsHeaders,
13
13
  err,
@@ -121,7 +121,7 @@ function registerNote(rows) {
121
121
  ].join("\n");
122
122
  }
123
123
  function certificateLinks(rows) {
124
- const links = rows.filter((r) => r.pdf_key).map((r) => `- ${r.num}: https://www.ommisa.org/cert-pdf/${r.pdf_key}`);
124
+ const links = rows.filter((r) => r.pdf_key).map((r) => `- ${r.num}: https://www.oimlsmart.org/cert-pdf/${r.pdf_key}`);
125
125
  return links.length ? `The certificates' documents:
126
126
  ${links.join("\n")}` : "";
127
127
  }
@@ -39,7 +39,7 @@ import {
39
39
  tokenBudget,
40
40
  understandQuery,
41
41
  usageTotal
42
- } from "./chunk-LL3LWBZH.js";
42
+ } from "./chunk-7HWJOFTQ.js";
43
43
  import {
44
44
  corsHeaders,
45
45
  err,
@@ -72,7 +72,7 @@ import {
72
72
  certificateLinks,
73
73
  registerNote,
74
74
  searchRegister
75
- } from "./chunk-3IRX4B7Q.js";
75
+ } from "./chunk-VGVTIPFW.js";
76
76
  import {
77
77
  P
78
78
  } from "./chunk-3FYJM7LH.js";
@@ -1896,7 +1896,7 @@ async function runTool(db, call) {
1896
1896
  if (call.name !== "register_search") return null;
1897
1897
  const query = String(call.args?.query ?? "").trim().slice(0, 160);
1898
1898
  if (!query) return null;
1899
- const { searchRegister: searchRegister2, registerNote: registerNote2 } = await import("./certificates-EMHFAUWC.js");
1899
+ const { searchRegister: searchRegister2, registerNote: registerNote2 } = await import("./certificates-DNRUOYRF.js");
1900
1900
  const reg = await searchRegister2(db, query, true);
1901
1901
  const output = reg?.rows?.length ? registerNote2(reg.rows) : `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.`;
1902
1902
  return { name: call.name, query, output };
@@ -1992,11 +1992,18 @@ function catalogNote(anchors) {
1992
1992
  return `The COMPLETE name index of the platform's operations:
1993
1993
  ${anchors.map((a) => `- ${a}`).join("\n")}`;
1994
1994
  }
1995
- async function operationsCatalogNote(db, query) {
1996
- if (!isOperationIntent(query)) return void 0;
1995
+ async function operationsLane(db, query) {
1996
+ if (!isOperationIntent(query)) return { matched: [] };
1997
+ for (let attempt = 0; attempt < 2; attempt++) {
1998
+ const lane = await operationsLaneOnce(db, query, attempt > 0);
1999
+ if (lane !== void 0) return lane;
2000
+ }
2001
+ return { matched: [] };
2002
+ }
2003
+ async function operationsLaneOnce(db, query, retried) {
1997
2004
  try {
1998
- const rows = (await db.prepare("SELECT clause_anchor AS anchor, substr(text, 1, 300) AS text FROM chunks WHERE corpus = 'smart-ops' AND clause_anchor IS NOT NULL ORDER BY clause_anchor").all()).results ?? [];
1999
- const opRows = rows.map((r) => ({ anchor: String(r.anchor ?? ""), text: String(r.text ?? "") })).filter((r) => r.anchor);
2005
+ const rows = (await db.prepare("SELECT clause_anchor AS anchor, substr(text, 1, 300) AS text, docidentifier FROM chunks WHERE corpus = 'smart-ops' AND clause_anchor IS NOT NULL ORDER BY clause_anchor").all()).results ?? [];
2006
+ const opRows = rows.map((r) => ({ anchor: String(r.anchor ?? ""), text: String(r.text ?? ""), docidentifier: String(r.docidentifier ?? "Platform API") })).filter((r) => r.anchor);
2000
2007
  const matched = matchOperations(query, opRows);
2001
2008
  const parts = [];
2002
2009
  if (matched.length) {
@@ -2004,8 +2011,10 @@ async function operationsCatalogNote(db, query) {
2004
2011
  ${matched.map((m) => `- ${m.text}`).join("\n")}`);
2005
2012
  }
2006
2013
  parts.push(catalogNote(opRows.map((r) => r.anchor)));
2007
- return parts.filter(Boolean).join("\n\n") || void 0;
2008
- } catch {
2014
+ const note = parts.filter(Boolean).join("\n\n") || void 0;
2015
+ return { note, matched };
2016
+ } catch (e) {
2017
+ console.error(`operations-catalog: D1 read failed${retried ? " twice" : ""} \u2014 the note is dropped`, String(e).slice(0, 120));
2009
2018
  return void 0;
2010
2019
  }
2011
2020
  }
@@ -2191,7 +2200,10 @@ async function* sseTokens(stream, stats) {
2191
2200
  const decoder = new TextDecoder();
2192
2201
  let buf = "";
2193
2202
  while (true) {
2194
- const { done, value } = await reader.read();
2203
+ const { done, value } = await Promise.race([
2204
+ reader.read(),
2205
+ new Promise((r) => setTimeout(() => r({ done: true }), 12e4))
2206
+ ]);
2195
2207
  if (done) break;
2196
2208
  buf += decoder.decode(value, { stream: true });
2197
2209
  const lines = buf.split("\n");
@@ -2450,7 +2462,7 @@ async function handleAsk(env, ctx, req, tier, key) {
2450
2462
  }
2451
2463
  console.log("register-search:", JSON.stringify({ shaped: !!register, tokens: register?.tokens ?? null, rows: register?.rows?.length ?? null, first: register?.rows?.[0]?.num ?? null }));
2452
2464
  const regNote = register ? [registerNote(register.rows), certificateLinks(register.rows)].filter(Boolean).join("\n") : void 0;
2453
- const opsCatalog = await operationsCatalogNote(env.DB, q.query);
2465
+ const ops = await operationsLane(env.DB, q.query);
2454
2466
  if (understanding?.intent === "conversational") {
2455
2467
  const queryHash2 = await sha256Hex(q.query);
2456
2468
  const messages2 = [
@@ -2796,13 +2808,31 @@ Answer account questions from these records ONLY: name the record when you use i
2796
2808
  return matched.length ? matched : g;
2797
2809
  })();
2798
2810
  const vocabNote = glossaryForNote.length ? "Vocabulary binding \u2014 defined terms in the indexed corpus that may name this question's subject:\n" + glossaryForNote.map((g) => `- ${g.term} (${g.docidentifier}): ${g.definition}`).join("\n") + "\nIf the question describes a symptom or behavior in everyday words, OPEN the answer by naming the matching defined term, quote its definition, and cite its defining publication; keep using that term throughout. Match TIME SCALE carefully: change under a constant load over minutes/hours is creep; change over months/years of use is span stability or durability \u2014 do not call long-term drift creep." : void 0;
2811
+ const opsHits = ops.matched.map((m) => ({
2812
+ id: `ops:${m.anchor}`,
2813
+ score: 0.999,
2814
+ metadata: {
2815
+ doc_id: "smart-ops:catalog",
2816
+ docidentifier: m.docidentifier ?? "Platform API",
2817
+ doctype: "api-operation",
2818
+ doc_number: "",
2819
+ edition: "",
2820
+ language: "en",
2821
+ clause_anchor: m.anchor,
2822
+ clause_title: `Platform operation ${m.anchor}`,
2823
+ tier: "curated",
2824
+ corpus: "smart-ops",
2825
+ text_ref: ""
2826
+ },
2827
+ text: `${m.anchor} \u2014 ${m.text}`
2828
+ }));
2799
2829
  const { messages, usedHits } = buildMessages(
2800
2830
  q.query,
2801
- hits,
2831
+ [...opsHits, ...hits],
2802
2832
  q.lang,
2803
2833
  keptHistory,
2804
2834
  // stage-extracted graph facts (GraphRAG) ride the same note channel
2805
- [opsCatalog, processNote, eNote, regNote, contextNote(declaredCtx, docScope), accountNote, modelNote, vocabNote, memNote, machineNote, conditionNote, aggregationNote, boundaryNote, licenseNote, ...retrieved.notes ?? []].filter(Boolean).join("\n") || void 0,
2835
+ [ops.note, processNote, eNote, regNote, contextNote(declaredCtx, docScope), accountNote, modelNote, vocabNote, memNote, machineNote, conditionNote, aggregationNote, boundaryNote, licenseNote, ...retrieved.notes ?? []].filter(Boolean).join("\n") || void 0,
2806
2836
  summary,
2807
2837
  budget
2808
2838
  );
@@ -6,9 +6,26 @@ export declare function anchorWords(anchor: string): string[];
6
6
  export declare function matchOperations(query: string, rows: {
7
7
  anchor: string;
8
8
  text: string;
9
+ docidentifier?: string;
9
10
  }[]): {
10
11
  anchor: string;
11
12
  text: string;
13
+ docidentifier?: string;
12
14
  }[];
13
15
  export declare function catalogNote(anchors: string[]): string;
14
- export declare function operationsCatalogNote(db: any, query: string): Promise<string | undefined>;
16
+ /** The lane's whole result: the authoritative note (matched lines +
17
+ * name index) AND the matched rows themselves — the ask path rides the
18
+ * matched operations as CITABLE PASSAGES, because the refusal logic
19
+ * keys on passages and a side-note cannot outrank the passages'
20
+ * silence (observed live 2026-09-29: the note was present and the
21
+ * model still refused "the only API operation in the current context
22
+ * is the blob download"). */
23
+ export interface OperationsLane {
24
+ note?: string;
25
+ matched: {
26
+ anchor: string;
27
+ text: string;
28
+ docidentifier?: string;
29
+ }[];
30
+ }
31
+ export declare function operationsLane(db: any, query: string): Promise<OperationsLane>;
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  handleSearch
3
- } from "./chunk-OXTRNMTZ.js";
4
- import "./chunk-LL3LWBZH.js";
3
+ } from "./chunk-KO6DRI7C.js";
4
+ import "./chunk-7HWJOFTQ.js";
5
5
  import "./chunk-SPKPH54J.js";
6
6
  import "./chunk-A3QHHUN5.js";
7
7
  import "./chunk-LVZTWUVJ.js";
@@ -6,10 +6,10 @@ import {
6
6
  modelGroundingBlock,
7
7
  scoreFaithfulness,
8
8
  scoreJudge
9
- } from "../../chunk-V7PEVNNO.js";
9
+ } from "../../chunk-XJPJO376.js";
10
10
  import {
11
11
  handleSearch
12
- } from "../../chunk-OXTRNMTZ.js";
12
+ } from "../../chunk-KO6DRI7C.js";
13
13
  import {
14
14
  buildMessages,
15
15
  cfBlobs,
@@ -35,7 +35,7 @@ import {
35
35
  sessionFrom,
36
36
  telemetry,
37
37
  understandQuery
38
- } from "../../chunk-LL3LWBZH.js";
38
+ } from "../../chunk-7HWJOFTQ.js";
39
39
  import {
40
40
  authenticate,
41
41
  corsHeaders,
@@ -63,7 +63,7 @@ import {
63
63
  sha256Hex,
64
64
  today
65
65
  } from "../../chunk-2PCUAJJO.js";
66
- import "../../chunk-3IRX4B7Q.js";
66
+ import "../../chunk-VGVTIPFW.js";
67
67
  import {
68
68
  P,
69
69
  setProfile
@@ -959,7 +959,7 @@ async function handleMcp(env, ctx, req, tier, key) {
959
959
  // stream:false forces the JSON lane (anon defaults to SSE)
960
960
  body: JSON.stringify({ ...args, stream: false })
961
961
  });
962
- const res = name === "ask" ? await (await import("../../ask-MFFOHVQ2.js")).handleAsk(env, ctx, inner, tier, key) : await (await import("../../search-LN5Q7OSZ.js")).handleSearch(env, ctx, inner, tier, key);
962
+ const res = name === "ask" ? await (await import("../../ask-AJJCWGW6.js")).handleAsk(env, ctx, inner, tier, key) : await (await import("../../search-LVUQLA3C.js")).handleSearch(env, ctx, inner, tier, key);
963
963
  return res.json().catch(() => ({ error: { message: "tool transport failed", status: res.status } }));
964
964
  });
965
965
  if (out.ok && "accepted" in out) return new Response(null, { status: 202 });
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.73",
30
+ "version": "0.2.75",
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",
@@ -54,13 +54,19 @@ export async function generateOnce(env: any, model: string, messages: any[], eff
54
54
  // silently blindfolds figure answers)
55
55
  for (let attempt = 0; attempt < 2; attempt++) {
56
56
  try {
57
- const res: any = await env.AI.run(model, {
58
- messages,
59
- max_tokens: effortBudget(effort ?? answerEffort(env)),
60
- reasoning_effort: effort ?? answerEffort(env),
61
- temperature: 0.6,
62
- top_p: 0.95,
63
- });
57
+ // bounded: a STALLED call (no error, no response — observed live
58
+ // 2026-09-29, asks hanging at zero bytes for minutes) must fall
59
+ // through to the retry/fallback, never hang the ask forever
60
+ const res: any = await Promise.race([
61
+ env.AI.run(model, {
62
+ messages,
63
+ max_tokens: effortBudget(effort ?? answerEffort(env)),
64
+ reasoning_effort: effort ?? answerEffort(env),
65
+ temperature: 0.6,
66
+ top_p: 0.95,
67
+ }),
68
+ new Promise<null>((r) => setTimeout(() => r(null), 120_000)),
69
+ ]);
64
70
  // an EMPTY string is a failed generation, not an answer: the
65
71
  // platform intermittently returns empty bodies with a 200 (observed
66
72
  // live 2026-09-20 — answers went blank platform-wide while retrieval
@@ -41,7 +41,7 @@ import { chargeTokens, checkQuota, clientIp, estimateTokens, telemetry, tokenBud
41
41
  import { graphExpand, editionNote } from "./graph";
42
42
  import { registerNote, searchRegister, certificateLinks } from "./certificates";
43
43
  import { TOOL_DECLARATION, parseToolCall, runTool, toolNote } from "./tools";
44
- import { operationsCatalogNote } from "./operations.ts";
44
+ import { operationsLane } from "./operations.ts";
45
45
  import { extractNameplate, nameplateRegisterQuery } from "./nameplate.ts";
46
46
  import { P } from "./profile.ts";
47
47
 
@@ -232,7 +232,12 @@ async function* sseTokens(stream: ReadableStream<Uint8Array>, stats?: { usage?:
232
232
  const decoder = new TextDecoder();
233
233
  let buf = "";
234
234
  while (true) {
235
- const { done, value } = await reader.read();
235
+ // bounded read: a stalled stream (no error, no chunk) delivers what
236
+ // it has instead of hanging the ask at zero tokens
237
+ const { done, value } = await Promise.race([
238
+ reader.read(),
239
+ new Promise<{ done: true; value?: undefined }>((r) => setTimeout(() => r({ done: true }), 120_000)),
240
+ ]);
236
241
  if (done) break;
237
242
  buf += decoder.decode(value, { stream: true });
238
243
  const lines = buf.split("\n");
@@ -672,7 +677,7 @@ let convEntities: Array<{ entity: string; kind: string }> = [];
672
677
  }
673
678
  console.log("register-search:", JSON.stringify({ shaped: !!register, tokens: register?.tokens ?? null, rows: register?.rows?.length ?? null, first: register?.rows?.[0]?.num ?? null }));
674
679
  const regNote = register ? [registerNote(register.rows), certificateLinks(register.rows)].filter(Boolean).join("\n") : undefined;
675
- const opsCatalog = await operationsCatalogNote(env.DB, q.query);
680
+ const ops = await operationsLane(env.DB, q.query);
676
681
 
677
682
  // Conversational route, decided by query UNDERSTANDING (any language, any
678
683
  // phrasing) — not string matching. No retrieval: nothing in the corpus
@@ -1177,13 +1182,34 @@ let convEntities: Array<{ entity: string; kind: string }> = [];
1177
1182
  glossaryForNote.map((g) => `- ${g.term} (${g.docidentifier}): ${g.definition}`).join("\n") +
1178
1183
  "\nIf the question describes a symptom or behavior in everyday words, OPEN the answer by naming the matching defined term, quote its definition, and cite its defining publication; keep using that term throughout. Match TIME SCALE carefully: change under a constant load over minutes/hours is creep; change over months/years of use is span stability or durability — do not call long-term drift creep."
1179
1184
  : undefined;
1185
+ // the matched operations ride as CITABLE PASSAGES ahead of the
1186
+ // retrieved hits — the refusal logic and the citation machinery key on
1187
+ // passages, and a side-note cannot outrank the passages' silence
1188
+ const opsHits: Hit[] = ops.matched.map((m) => ({
1189
+ id: `ops:${m.anchor}`,
1190
+ score: 0.999,
1191
+ metadata: {
1192
+ doc_id: "smart-ops:catalog",
1193
+ docidentifier: m.docidentifier ?? "Platform API",
1194
+ doctype: "api-operation",
1195
+ doc_number: "",
1196
+ edition: "",
1197
+ language: "en",
1198
+ clause_anchor: m.anchor,
1199
+ clause_title: `Platform operation ${m.anchor}`,
1200
+ tier: "curated",
1201
+ corpus: "smart-ops",
1202
+ text_ref: "",
1203
+ },
1204
+ text: `${m.anchor} — ${m.text}`,
1205
+ }));
1180
1206
  const { messages, usedHits } = buildMessages(
1181
1207
  q.query,
1182
- hits,
1208
+ [...opsHits, ...hits],
1183
1209
  q.lang,
1184
1210
  keptHistory,
1185
1211
  // stage-extracted graph facts (GraphRAG) ride the same note channel
1186
- [opsCatalog, processNote, eNote, regNote, contextNote(declaredCtx, docScope), accountNote, modelNote, vocabNote, memNote, machineNote, conditionNote, aggregationNote, boundaryNote, licenseNote, ...(retrieved.notes ?? [])].filter(Boolean).join("\n") || undefined,
1212
+ [ops.note, processNote, eNote, regNote, contextNote(declaredCtx, docScope), accountNote, modelNote, vocabNote, memNote, machineNote, conditionNote, aggregationNote, boundaryNote, licenseNote, ...(retrieved.notes ?? [])].filter(Boolean).join("\n") || undefined,
1187
1213
  summary,
1188
1214
  budget,
1189
1215
  );
@@ -134,7 +134,7 @@ export function registerNote(rows: RegisterRow[]): string {
134
134
  * is in the R2 plane (TODO 7) — the user gets the document, not just
135
135
  * the fact. */
136
136
  export function certificateLinks(rows: RegisterRow[]): string {
137
- const links = rows.filter((r) => r.pdf_key).map((r) => `- ${r.num}: https://www.ommisa.org/cert-pdf/${r.pdf_key}`);
137
+ const links = rows.filter((r) => r.pdf_key).map((r) => `- ${r.num}: https://www.oimlsmart.org/cert-pdf/${r.pdf_key}`);
138
138
  return links.length ? `The certificates' documents:\n${links.join("\n")}` : "";
139
139
  }
140
140
 
@@ -38,7 +38,7 @@ export function anchorWords(anchor: string): string[] {
38
38
 
39
39
  const singular = (w: string) => (w.length > 3 && w.endsWith("s") ? w.slice(0, -1) : w);
40
40
 
41
- export function matchOperations(query: string, rows: { anchor: string; text: string }[]): { anchor: string; text: string }[] {
41
+ export function matchOperations(query: string, rows: { anchor: string; text: string; docidentifier?: string }[]): { anchor: string; text: string; docidentifier?: string }[] {
42
42
  const q = query.toLowerCase();
43
43
  const stems = ACTION_STEMS.filter((s) => q.includes(s));
44
44
  const objects = query
@@ -68,19 +68,45 @@ export function catalogNote(anchors: string[]): string {
68
68
  return `The COMPLETE name index of the platform's operations:\n${anchors.map((a) => `- ${a}`).join("\n")}`;
69
69
  }
70
70
 
71
- export async function operationsCatalogNote(db: any, query: string): Promise<string | undefined> {
72
- if (!isOperationIntent(query)) return undefined;
73
- try {
74
- const rows = (await db.prepare("SELECT clause_anchor AS anchor, substr(text, 1, 300) AS text FROM chunks WHERE corpus = 'smart-ops' AND clause_anchor IS NOT NULL ORDER BY clause_anchor").all()).results ?? [];
75
- const opRows = (rows as any[]).map((r) => ({ anchor: String(r.anchor ?? ""), text: String(r.text ?? "") })).filter((r) => r.anchor);
71
+ /** The lane's whole result: the authoritative note (matched lines +
72
+ * name index) AND the matched rows themselves — the ask path rides the
73
+ * matched operations as CITABLE PASSAGES, because the refusal logic
74
+ * keys on passages and a side-note cannot outrank the passages'
75
+ * silence (observed live 2026-09-29: the note was present and the
76
+ * model still refused "the only API operation in the current context
77
+ * is the blob download"). */
78
+ export interface OperationsLane {
79
+ note?: string;
80
+ matched: { anchor: string; text: string; docidentifier?: string }[];
81
+ }
82
+
83
+ export async function operationsLane(db: any, query: string): Promise<OperationsLane> {
84
+ if (!isOperationIntent(query)) return { matched: [] };
85
+ // one retry, and the failure is LOUD: a silently dropped note answers
86
+ // an operation question with the OIML refusal (observed live 2026-09-29
87
+ // — a transient D1 read failed into the catch and the lane vanished)
88
+ for (let attempt = 0; attempt < 2; attempt++) {
89
+ const lane = await operationsLaneOnce(db, query, attempt > 0);
90
+ if (lane !== undefined) return lane;
91
+ }
92
+ return { matched: [] };
93
+ }
94
+
95
+ async function operationsLaneOnce(db: any, query: string, retried: boolean): Promise<OperationsLane | undefined> {
96
+ try {
97
+ const rows = (await db.prepare("SELECT clause_anchor AS anchor, substr(text, 1, 300) AS text, docidentifier FROM chunks WHERE corpus = 'smart-ops' AND clause_anchor IS NOT NULL ORDER BY clause_anchor").all()).results ?? [];
98
+ const opRows = (rows as any[]).map((r) => ({ anchor: String(r.anchor ?? ""), text: String(r.text ?? ""), docidentifier: String(r.docidentifier ?? "Platform API") })).filter((r) => r.anchor);
76
99
  const matched = matchOperations(query, opRows);
77
100
  const parts: string[] = [];
78
101
  if (matched.length) {
79
102
  parts.push(`Operations whose name or description matches the question (AUTHORITATIVE — if one matches, the answer MUST name it and cite its line; never answer that the information is missing):\n${matched.map((m) => `- ${m.text}`).join("\n")}`);
80
103
  }
81
104
  parts.push(catalogNote(opRows.map((r) => r.anchor)));
82
- return parts.filter(Boolean).join("\n\n") || undefined;
83
- } catch {
84
- return undefined; // the lane is optional
105
+ const note = parts.filter(Boolean).join("\n\n") || undefined;
106
+ return { note, matched };
107
+ } catch (e) {
108
+ // the lane is optional — but never silent (the blind-failure lesson)
109
+ console.error(`operations-catalog: D1 read failed${retried ? " twice" : ""} — the note is dropped`, String(e).slice(0, 120));
110
+ return undefined;
85
111
  }
86
112
  }