bulltrackers-module 1.0.336 → 1.0.337
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.
|
@@ -6,4 +6,5 @@ module.exports = {
|
|
|
6
6
|
|
|
7
7
|
"instrument-price-change-1d": { maxZeroPct: 100 }, // Because weekeends/holidays return 0 change, technically crypto means this can't hit 100% but it's usually quite close, so we override
|
|
8
8
|
"instrument-price-momentum-20d ": { maxZeroPct: 100 }, // Some assets can be very stagnant over a month, especially bonds or stablecoins
|
|
9
|
+
"mimetic-latency": { maxFlatlinePct: 100 },
|
|
9
10
|
};
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
* PURPOSE: Sequential Cursor-Based Dispatcher.
|
|
4
4
|
* BEHAVIOR: Dispatch -> Wait ETA -> Next Date.
|
|
5
5
|
* UPDATED: Added "Sweep" Protocol for OOM recovery & High-Mem Verification.
|
|
6
|
+
* UPDATED: Added checks to permanently skip Deterministic Failures (Quality Breakers).
|
|
6
7
|
*/
|
|
7
8
|
|
|
8
9
|
const { getExpectedDateStrings, getEarliestDataDates, normalizeName, DEFINITIVE_EARLIEST_DATES } = require('../utils/utils.js');
|
|
@@ -22,7 +23,7 @@ const STALE_LOCK_THRESHOLD_MS = 1000 * 60 * 15;
|
|
|
22
23
|
// =============================================================================
|
|
23
24
|
async function filterActiveTasks(db, date, pass, tasks, logger, forceRun = false) {
|
|
24
25
|
if (!tasks || tasks.length === 0) return [];
|
|
25
|
-
if (forceRun) return tasks; // Bypass check for Sweep Mode
|
|
26
|
+
if (forceRun) return tasks; // Bypass check for Sweep Mode (Handled separately in Sweep logic)
|
|
26
27
|
|
|
27
28
|
const checkPromises = tasks.map(async (t) => {
|
|
28
29
|
const taskName = normalizeName(t.name);
|
|
@@ -54,6 +55,16 @@ async function filterActiveTasks(db, date, pass, tasks, logger, forceRun = false
|
|
|
54
55
|
(Date.now() - new Date(data.completedAt).getTime() < 60 * 1000);
|
|
55
56
|
|
|
56
57
|
if (isJustFinished) return null;
|
|
58
|
+
|
|
59
|
+
// 3. DETERMINISTIC FAILURE CHECK (Break Infinite Loops)
|
|
60
|
+
// If the task failed due to Logic/Quality issues, never retry it automatically.
|
|
61
|
+
if (data.status === 'FAILED') {
|
|
62
|
+
const stage = data.error?.stage;
|
|
63
|
+
if (['QUALITY_CIRCUIT_BREAKER', 'SEMANTIC_GATE', 'SHARDING_LIMIT_EXCEEDED'].includes(stage)) {
|
|
64
|
+
if (logger) logger.log('WARN', `[Dispatcher] 🛑 Skipping deterministic failure for ${taskName} (${stage}).`);
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
57
68
|
}
|
|
58
69
|
return t;
|
|
59
70
|
});
|
|
@@ -249,12 +260,34 @@ async function handleSweepDispatch(config, dependencies, computationManifest, re
|
|
|
249
260
|
return { dispatched: 0 };
|
|
250
261
|
}
|
|
251
262
|
|
|
263
|
+
// [FIX] Filter out deterministic failures from Sweep to prevent loops
|
|
264
|
+
// Sweep is for OOM recovery. Quality failures will fail on High-Mem too.
|
|
265
|
+
const validTasks = [];
|
|
266
|
+
for (const task of pending) {
|
|
267
|
+
const name = normalizeName(task.name);
|
|
268
|
+
const ledgerPath = `computation_audit_ledger/${date}/passes/${passToRun}/tasks/${name}`;
|
|
269
|
+
const doc = await db.doc(ledgerPath).get();
|
|
270
|
+
if (doc.exists) {
|
|
271
|
+
const data = doc.data();
|
|
272
|
+
const stage = data.error?.stage;
|
|
273
|
+
if (['QUALITY_CIRCUIT_BREAKER', 'SEMANTIC_GATE', 'SHARDING_LIMIT_EXCEEDED'].includes(stage)) {
|
|
274
|
+
logger.log('WARN', `[Sweep] 🛑 Skipping deterministic failure for ${name} (${stage}).`);
|
|
275
|
+
continue;
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
validTasks.push(task);
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
if (validTasks.length === 0) {
|
|
282
|
+
logger.log('INFO', `[Sweep] ${date} only has deterministic failures. No dispatch.`);
|
|
283
|
+
return { dispatched: 0 };
|
|
284
|
+
}
|
|
285
|
+
|
|
252
286
|
// 2. FORCE High Mem & Skip Zombie Check
|
|
253
|
-
// We
|
|
254
|
-
// We do NOT check resource tier from previous runs, we FORCE 'high-mem'
|
|
287
|
+
// We use validTasks now
|
|
255
288
|
const currentDispatchId = crypto.randomUUID();
|
|
256
289
|
|
|
257
|
-
const tasksPayload =
|
|
290
|
+
const tasksPayload = validTasks.map(t => ({
|
|
258
291
|
...t,
|
|
259
292
|
action: 'RUN_COMPUTATION_DATE',
|
|
260
293
|
computation: t.name,
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* FILENAME: computation-system/helpers/computation_worker.js
|
|
3
3
|
* UPDATED: Fixed Firestore 'undefined' field error for dispatchId.
|
|
4
|
+
* UPDATED: Writes structured Error objects (with stage) to Ledger to prevent retry loops.
|
|
4
5
|
*/
|
|
5
6
|
|
|
6
7
|
const { executeDispatchTask } = require('../WorkflowOrchestrator.js');
|
|
@@ -92,7 +93,19 @@ async function handleComputationTask(message, config, dependencies) {
|
|
|
92
93
|
const isDeterministic = ['SHARDING_LIMIT_EXCEEDED', 'QUALITY_CIRCUIT_BREAKER', 'SEMANTIC_GATE'].includes(err.stage);
|
|
93
94
|
|
|
94
95
|
if (isDeterministic || (message.deliveryAttempt || 1) >= MAX_RETRIES) {
|
|
95
|
-
|
|
96
|
+
// [FIX] Write structured error payload so Dispatcher can see the 'stage'
|
|
97
|
+
// This prevents the Dispatcher from retrying Quality Broken tasks.
|
|
98
|
+
const errorPayload = {
|
|
99
|
+
message: err.message,
|
|
100
|
+
stage: err.stage || 'FATAL'
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
await db.doc(ledgerPath).set({
|
|
104
|
+
status: 'FAILED',
|
|
105
|
+
error: errorPayload,
|
|
106
|
+
failedAt: new Date()
|
|
107
|
+
}, { merge: true });
|
|
108
|
+
|
|
96
109
|
await recordRunAttempt(db, { date, computation, pass }, 'FAILURE', { message: err.message, stage: err.stage || 'FATAL' }, { peakMemoryMB: heartbeat.getPeak() }, triggerReason, resourceTier);
|
|
97
110
|
return;
|
|
98
111
|
}
|