[object Object]

← back to Dw Unbuyable Recovery Pilot

Fail closed on Momentum search errors

e2d53f7737def76c5d1b51ee4303dcb44a229e34 · 2026-09-02 16:26:22 -0700 · Steve Abrams

Files touched

Diff

commit e2d53f7737def76c5d1b51ee4303dcb44a229e34
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Wed Sep 2 16:26:22 2026 -0700

    Fail closed on Momentum search errors
---
 tk11040-momentum-reconcile/measure-match.mjs       |  3 +-
 tk11040-momentum-reconcile/meilisearch-client.mjs  | 20 ++++++++++++
 .../meilisearch-client.test.mjs                    | 38 ++++++++++++++++++++++
 3 files changed, 60 insertions(+), 1 deletion(-)

diff --git a/tk11040-momentum-reconcile/measure-match.mjs b/tk11040-momentum-reconcile/measure-match.mjs
index 863cc45..8cbb7f1 100644
--- a/tk11040-momentum-reconcile/measure-match.mjs
+++ b/tk11040-momentum-reconcile/measure-match.mjs
@@ -1,6 +1,7 @@
 // TK-11040 unblock: measure how many of the 199 PR-Momentum non-joiners produce a
 // HIGH-CONFIDENCE feed match (exact pattern_name + exact color). READ-ONLY.
 import { execSync } from 'node:child_process';
+import { searchHits } from './meilisearch-client.mjs';
 const HOST='https://ms-e886719d86e7-4256.sfo.meilisearch.io';
 const KEY=process.env.MEILISEARCH_API_KEY;
 if(!KEY) throw new Error('MEILISEARCH_API_KEY is required');
@@ -10,7 +11,7 @@ const rows=JSON.parse(execSync(`psql -h /tmp -d dw_unified -tA -c "
   from shopify_products sp
   where sp.vendor='Phillipe Romano' and sp.status='ACTIVE' and not coalesce(sp.has_product_variant,false) and sp.supplier_name ~* 'mom'
     and not exists (select 1 from momentum_colorways m where (upper(m.alt_sku)=upper(sp.mfr_sku) or upper(m.momentum_sku)=upper(sp.mfr_sku) or upper(m.dw_sku)=upper(sp.dw_sku)) and m.hw_price>0)"`,{encoding:'utf8'}).trim());
-async function feed(q){const r=await fetch(`${HOST}/indexes/redesign-colors/search`,{method:'POST',headers:{Authorization:`Bearer ${KEY}`,'Content-Type':'application/json'},body:JSON.stringify({q,limit:20})});return (await r.json()).hits||[];}
+async function feed(q){return searchHits({host:HOST,key:KEY,query:q});}
 let confident=0, ambiguous=0, none=0; const samples=[];
 for(const r of rows){
   // title: "<Pattern>-<Colorway> <Type/DurableVinyl...> | Phillipe Romano"
diff --git a/tk11040-momentum-reconcile/meilisearch-client.mjs b/tk11040-momentum-reconcile/meilisearch-client.mjs
new file mode 100644
index 0000000..057a7d2
--- /dev/null
+++ b/tk11040-momentum-reconcile/meilisearch-client.mjs
@@ -0,0 +1,20 @@
+export async function searchHits({ fetchImpl = fetch, host, key, query }) {
+  const response = await fetchImpl(`${host}/indexes/redesign-colors/search`, {
+    method: 'POST',
+    headers: {
+      Authorization: `Bearer ${key}`,
+      'Content-Type': 'application/json',
+    },
+    body: JSON.stringify({ q: query, limit: 20 }),
+  });
+
+  if (!response.ok) {
+    throw new Error(`Meilisearch request failed with HTTP ${response.status}`);
+  }
+
+  const payload = await response.json();
+  if (!Array.isArray(payload.hits)) {
+    throw new Error('Meilisearch response is missing a hits array');
+  }
+  return payload.hits;
+}
diff --git a/tk11040-momentum-reconcile/meilisearch-client.test.mjs b/tk11040-momentum-reconcile/meilisearch-client.test.mjs
new file mode 100644
index 0000000..16f0475
--- /dev/null
+++ b/tk11040-momentum-reconcile/meilisearch-client.test.mjs
@@ -0,0 +1,38 @@
+import assert from 'node:assert/strict';
+import test from 'node:test';
+import { searchHits } from './meilisearch-client.mjs';
+
+test('returns hits from a successful response', async () => {
+  const hits = [{ number: '123', price: 42 }];
+  const result = await searchHits({
+    host: 'https://example.invalid',
+    key: 'test-only',
+    query: 'sample',
+    fetchImpl: async () => ({ ok: true, status: 200, json: async () => ({ hits }) }),
+  });
+  assert.deepEqual(result, hits);
+});
+
+test('fails closed on invalid authentication', async () => {
+  await assert.rejects(
+    searchHits({
+      host: 'https://example.invalid',
+      key: 'invalid-test-only',
+      query: 'sample',
+      fetchImpl: async () => ({ ok: false, status: 401 }),
+    }),
+    /HTTP 401/,
+  );
+});
+
+test('fails closed when the response schema is invalid', async () => {
+  await assert.rejects(
+    searchHits({
+      host: 'https://example.invalid',
+      key: 'test-only',
+      query: 'sample',
+      fetchImpl: async () => ({ ok: true, status: 200, json: async () => ({}) }),
+    }),
+    /missing a hits array/,
+  );
+});

← c3a2933 Record E2E proof for credential containment  ·  back to Dw Unbuyable Recovery Pilot  ·  Extend TK-11133 boundary proof 50bf192 →