← back to Dw Unbuyable Recovery Pilot
Fail closed on Momentum search errors
e2d53f7737def76c5d1b51ee4303dcb44a229e34 · 2026-09-02 16:26:22 -0700 · Steve Abrams
Files touched
M tk11040-momentum-reconcile/measure-match.mjsA tk11040-momentum-reconcile/meilisearch-client.mjsA tk11040-momentum-reconcile/meilisearch-client.test.mjs
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 →