← back to Cncp Failure Collector
tick 4: fire-and-forget classify on new rows (5min per-process rate limit, env-var disable, server single-flight)
8a795141e7e11df0983c41b205c85d635c2cd5a5 · 2026-05-11 12:05:51 -0700 · SteveStudio2
Files touched
M ecosystem.config.jsM index.js
Diff
commit 8a795141e7e11df0983c41b205c85d635c2cd5a5
Author: SteveStudio2 <steve@designerwallcoverings.com>
Date: Mon May 11 12:05:51 2026 -0700
tick 4: fire-and-forget classify on new rows (5min per-process rate limit, env-var disable, server single-flight)
---
ecosystem.config.js | 3 ++-
index.js | 18 +++++++++++++++++-
2 files changed, 19 insertions(+), 2 deletions(-)
diff --git a/ecosystem.config.js b/ecosystem.config.js
index 23ad2de..75c3e61 100644
--- a/ecosystem.config.js
+++ b/ecosystem.config.js
@@ -17,7 +17,8 @@ module.exports = {
env: {
NODE_ENV: 'production',
CNCP_URL: 'http://127.0.0.1:3333',
- POLL_INTERVAL_MS: '30000'
+ POLL_INTERVAL_MS: '30000',
+ CLASSIFIER_ENABLED: '1'
}
}]
};
diff --git a/index.js b/index.js
index 62f5493..8f355dc 100644
--- a/index.js
+++ b/index.js
@@ -24,6 +24,9 @@ const { execSync, exec } = require('child_process');
const CNCP_URL = process.env.CNCP_URL || 'http://127.0.0.1:3333';
const POLL_INTERVAL_MS = parseInt(process.env.POLL_INTERVAL_MS, 10) || 30_000;
+const CLASSIFIER_ENABLED = (process.env.CLASSIFIER_ENABLED || '1') !== '0';
+const CLASSIFY_RATE_LIMIT_MS = 5 * 60 * 1000;
+const lastClassify = new Map(); // processName -> timestamp
const STATE_FILE = path.join(__dirname, 'state.json');
const ONCE = process.argv.includes('--once');
@@ -51,7 +54,20 @@ async function postTask(payload) {
body: JSON.stringify(payload)
});
if (!r.ok) { log('POST /api/failures failed', r.status, await r.text().catch(()=>'')); return null; }
- return await r.json();
+ const result = await r.json();
+ // Fire-and-forget classification on NEW rows only. Per-process 5-min rate limit + server-side single-flight.
+ if (CLASSIFIER_ENABLED && result.deduped === false && result.task && result.task.id) {
+ const name = payload.processName;
+ const last = lastClassify.get(name) || 0;
+ if (Date.now() - last >= CLASSIFY_RATE_LIMIT_MS) {
+ lastClassify.set(name, Date.now());
+ fetch(`${CNCP_URL}/api/failures/${result.task.id}/classify`, { method: 'POST' })
+ .then(rc => rc.json())
+ .then(d => log(`classify ${name}: ${d.classified ? d.task.severity + '/' + d.task.category : (d.busy ? 'busy' : 'failed: ' + (d.error || '?'))}`))
+ .catch(e => log(`classify ${name} threw:`, e.message));
+ }
+ }
+ return result;
} catch (e) {
log('POST /api/failures threw:', e.message);
return null;
← c57181c tick 3: launchd plist-driven .err.log tailing (97 com.steve.
·
back to Cncp Failure Collector
·
tick 6: README pass — covers ticks 2-5b features (classifier 4292cad →