← back to Tk 10821 Enrichment Health Classifier
add evidence-based enrichment health classifier
c572205eda60bacb86b33c5c9c86fd5c54e749b6 · 2026-08-30 12:57:59 -0700 · Steve Abrams
Files touched
A .gitignoreA README.mdA classify.mjsA fixtures/current-2026-08-30.jsonA test.mjsA verification/e2e-proof.json
Diff
commit c572205eda60bacb86b33c5c9c86fd5c54e749b6
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Sun Aug 30 12:57:59 2026 -0700
add evidence-based enrichment health classifier
---
.gitignore | 8 +++++++
README.md | 18 +++++++++++++++
classify.mjs | 49 ++++++++++++++++++++++++++++++++++++++++
fixtures/current-2026-08-30.json | 17 ++++++++++++++
test.mjs | 25 ++++++++++++++++++++
verification/e2e-proof.json | 34 ++++++++++++++++++++++++++++
6 files changed, 151 insertions(+)
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..378c83f
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,8 @@
+.env*
+node_modules/
+tmp/
+*.log
+.DS_Store
+dist/
+build/
+.next/
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..bd1ce03
--- /dev/null
+++ b/README.md
@@ -0,0 +1,18 @@
+# TK-10821 enrichment-health classifier
+
+This pure local guard prevents the Phase-3 monitor from equating a flat or
+growing **net pending count** with a dead enrichment cron. New work can enter
+while old work is processed, so backlog delta alone is not liveness.
+
+The classifier requires direct signals: time since `phase3_ai_at` advanced,
+completed rows in 24 hours, cron completion/exit state, and a sampled run's
+attempted/updated/no-data counts. It never calls an API or database.
+
+Current authoritative snapshot (2026-08-30): the cron is alive, with the last
+advance 8.81 hours ago and 543 completions in seven days. Throughput is low; a
+Wolf Gordon sample attempted 42 products and returned no AI data for all 42.
+That is `ALIVE_UPSTREAM_CONSTRAINED`, not `STALLED`.
+
+The three vendors named in the original ticket are excluded from this pipeline
+by rule and prior read-only evidence shows they are materially enriched. No paid
+drain should be started from this ticket's old premise.
diff --git a/classify.mjs b/classify.mjs
new file mode 100644
index 0000000..aabbc2d
--- /dev/null
+++ b/classify.mjs
@@ -0,0 +1,49 @@
+#!/usr/bin/env node
+// TK-10821: pure, local enrichment-health classifier.
+// It consumes an evidence snapshot and cannot call APIs, databases, or schedules.
+import fs from 'node:fs';
+
+export function classify(snapshot) {
+ const required = ['hours_since_last_advance', 'processed_24h', 'cron_last_exit', 'cron_completed_today', 'actionable_pending'];
+ const missing = required.filter(key => snapshot[key] === null || snapshot[key] === undefined);
+ if (missing.length) return { verdict: 'DEGRADED_OBSERVABILITY', stalled: null, reasons: [`missing: ${missing.join(', ')}`] };
+
+ const fresh = Number(snapshot.hours_since_last_advance) <= 48;
+ const processed = Number(snapshot.processed_24h) > 0;
+ const cronHealthy = snapshot.cron_last_exit === 0 && snapshot.cron_completed_today === true;
+ const stalled = !fresh && !processed && !cronHealthy;
+ const reasons = [
+ `last phase3 advance ${snapshot.hours_since_last_advance}h ago`,
+ `${snapshot.processed_24h} completed in 24h`,
+ `cron last_exit=${snapshot.cron_last_exit}, completed_today=${snapshot.cron_completed_today}`
+ ];
+ if (stalled) return { verdict: 'STALLED', stalled: true, reasons };
+
+ const attempted = Number(snapshot.sample_attempted || 0);
+ const updated = Number(snapshot.sample_updated || 0);
+ const noData = Number(snapshot.sample_skipped_no_data || 0);
+ if (attempted > 0 && updated / attempted < 0.05 && noData / attempted >= 0.8) {
+ return {
+ verdict: 'ALIVE_UPSTREAM_CONSTRAINED', stalled: false,
+ reasons: [...reasons, `sample run updated ${updated}/${attempted}; ${noData}/${attempted} returned no AI data`]
+ };
+ }
+
+ const previous = Number(snapshot.previous_actionable_pending);
+ const current = Number(snapshot.actionable_pending);
+ if (Number.isFinite(previous) && current >= previous) {
+ return {
+ verdict: 'ALIVE_NET_BACKLOG_FLAT_OR_GROWING', stalled: false,
+ reasons: [...reasons, `net pending ${previous}->${current}; net delta is not a liveness measurement`]
+ };
+ }
+ return { verdict: 'ALIVE_DRAINING', stalled: false, reasons };
+}
+
+if (process.argv[1] && new URL(import.meta.url).pathname === process.argv[1]) {
+ const input = process.argv[2];
+ if (!input) throw new Error('usage: node classify.mjs SNAPSHOT.json');
+ const result = classify(JSON.parse(fs.readFileSync(input, 'utf8')));
+ process.stdout.write(JSON.stringify(result, null, 2) + '\n');
+ process.exitCode = result.verdict === 'STALLED' || result.verdict === 'DEGRADED_OBSERVABILITY' ? 1 : 0;
+}
diff --git a/fixtures/current-2026-08-30.json b/fixtures/current-2026-08-30.json
new file mode 100644
index 0000000..426ef94
--- /dev/null
+++ b/fixtures/current-2026-08-30.json
@@ -0,0 +1,17 @@
+{
+ "source": "Kamatera read-only psql + full-monte phase3 log + local launchctl",
+ "captured_at": "2026-08-30T19:55:18Z",
+ "hours_since_last_advance": 8.81,
+ "processed_24h": 1,
+ "processed_7d": 543,
+ "cron_last_exit": 0,
+ "cron_completed_today": true,
+ "actionable_pending": 4236,
+ "previous_actionable_pending": 4210,
+ "raw_pending": 12219,
+ "sample_vendor": "wolf_gordon",
+ "sample_attempted": 42,
+ "sample_updated": 0,
+ "sample_skipped_no_data": 42,
+ "named_vendor_status": "excluded-by-rule; prior ticket evidence shows materially enriched, not dark"
+}
diff --git a/test.mjs b/test.mjs
new file mode 100644
index 0000000..3dd92e3
--- /dev/null
+++ b/test.mjs
@@ -0,0 +1,25 @@
+#!/usr/bin/env node
+import assert from 'node:assert/strict';
+import fs from 'node:fs';
+import { classify } from './classify.mjs';
+
+const current = JSON.parse(fs.readFileSync(new URL('./fixtures/current-2026-08-30.json', import.meta.url)));
+assert.deepEqual(classify(current), {
+ verdict: 'ALIVE_UPSTREAM_CONSTRAINED', stalled: false,
+ reasons: [
+ 'last phase3 advance 8.81h ago', '1 completed in 24h',
+ 'cron last_exit=0, completed_today=true',
+ 'sample run updated 0/42; 42/42 returned no AI data'
+ ]
+});
+
+const dead = { hours_since_last_advance: 56, processed_24h: 0, cron_last_exit: 1, cron_completed_today: false, actionable_pending: 4210 };
+assert.equal(classify(dead).verdict, 'STALLED');
+
+const inflowMasksWork = { hours_since_last_advance: 2, processed_24h: 50, cron_last_exit: 0, cron_completed_today: true, actionable_pending: 4300, previous_actionable_pending: 4210 };
+assert.equal(classify(inflowMasksWork).verdict, 'ALIVE_NET_BACKLOG_FLAT_OR_GROWING');
+assert.equal(classify(inflowMasksWork).stalled, false);
+
+const missing = { hours_since_last_advance: 2 };
+assert.equal(classify(missing).verdict, 'DEGRADED_OBSERVABILITY');
+console.log('PASS: current, true-stall, inflow-masks-work, and missing-evidence classifications');
diff --git a/verification/e2e-proof.json b/verification/e2e-proof.json
new file mode 100644
index 0000000..908607c
--- /dev/null
+++ b/verification/e2e-proof.json
@@ -0,0 +1,34 @@
+{
+ "ticket": "TK-10821",
+ "intent": "Prevent a flat net backlog from being misreported as a dead Phase-3 pipeline",
+ "risk_tier": "R1",
+ "environment": "pure local classifier over a captured read-only Kamatera/launchd/log snapshot",
+ "timestamp": "2026-08-30T19:55:18Z",
+ "baseline": "Monitor reported 4,236 remaining and NO drain; ticket alleged a 56-hour stall and three dark vendors",
+ "assertions": [
+ "Kamatera crontab contains Phase-3 tier jobs at 09:00, 10:00, and 11:00 UTC",
+ "max phase3_ai_at is 2026-08-30 11:06:50Z; 1 completion in 24h and 543 in 7d",
+ "today's tier-3 log completed 154 vendors successfully and failed 3",
+ "Wolf Gordon sample attempted 42, updated 0, skipped-no-data 42",
+ "local enrichment-health and phase3-monitor launchd jobs are loaded with last exit 0",
+ "current fixture classifies ALIVE_UPSTREAM_CONSTRAINED, not STALLED",
+ "a synthetic 56h/no-progress/failed-cron fixture classifies STALLED",
+ "a growing backlog with direct processing evidence remains alive",
+ "missing direct evidence fails closed as DEGRADED_OBSERVABILITY"
+ ],
+ "commands": [
+ "node --check classify.mjs",
+ "node test.mjs",
+ "node classify.mjs fixtures/current-2026-08-30.json"
+ ],
+ "side_effect_boundaries": {
+ "paid_api_calls": 0,
+ "production_batches_started": 0,
+ "database_or_shopify_writes": 0,
+ "deploys": 0,
+ "schedule_installs_or_reloads": 0,
+ "backend_kills": 0
+ },
+ "cleanup": "No external test state created",
+ "verdict": "PASS"
+}
(oldest)
·
back to Tk 10821 Enrichment Health Classifier
·
(newest)