bulltrackers-module 1.0.337 → 1.0.338

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.
@@ -3,7 +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
+ * UPDATED: Added Safety Checks to permanently skip Deterministic Failures.
7
7
  */
8
8
 
9
9
  const { getExpectedDateStrings, getEarliestDataDates, normalizeName, DEFINITIVE_EARLIEST_DATES } = require('../utils/utils.js');
@@ -23,7 +23,7 @@ const STALE_LOCK_THRESHOLD_MS = 1000 * 60 * 15;
23
23
  // =============================================================================
24
24
  async function filterActiveTasks(db, date, pass, tasks, logger, forceRun = false) {
25
25
  if (!tasks || tasks.length === 0) return [];
26
- if (forceRun) return tasks; // Bypass check for Sweep Mode (Handled separately in Sweep logic)
26
+ if (forceRun) return tasks; // Bypass check for Sweep Mode (Handled separately)
27
27
 
28
28
  const checkPromises = tasks.map(async (t) => {
29
29
  const taskName = normalizeName(t.name);
@@ -184,15 +184,12 @@ async function handlePassVerification(config, dependencies, computationManifest,
184
184
 
185
185
  const missingTasks = [];
186
186
 
187
- // Optimize: Batch fetch statuses if possible, but for now loop is safer for memory
188
- // In production, we might want p-limit here.
189
187
  for (const date of sessionDates) {
190
188
  const [dailyStatus, availability] = await Promise.all([
191
189
  fetchComputationStatus(date, config, dependencies),
192
190
  checkRootDataAvailability(date, config, dependencies, DEFINITIVE_EARLIEST_DATES)
193
191
  ]);
194
192
 
195
- // Need previous status for historical calcs
196
193
  let prevDailyStatus = null;
197
194
  if (calcsInPass.some(c => c.isHistorical)) {
198
195
  const prevD = new Date(date + 'T00:00:00Z');
@@ -202,8 +199,6 @@ async function handlePassVerification(config, dependencies, computationManifest,
202
199
 
203
200
  const report = analyzeDateExecution(date, calcsInPass, availability ? availability.status : {}, dailyStatus, manifestMap, prevDailyStatus);
204
201
 
205
- // We only care about Runnable (New) or ReRuns (Changed/Failed)
206
- // We ignore Blocked (impossible to run) and Impossible (permanent fail)
207
202
  const pending = [...report.runnable, ...report.reRuns];
208
203
 
209
204
  if (pending.length > 0) {
@@ -244,7 +239,6 @@ async function handleSweepDispatch(config, dependencies, computationManifest, re
244
239
  checkRootDataAvailability(date, config, dependencies, DEFINITIVE_EARLIEST_DATES)
245
240
  ]);
246
241
 
247
- // Previous Status Fetch (simplified for brevity, assume historical dependency check works or fails safe)
248
242
  let prevDailyStatus = null;
249
243
  if (calcsInPass.some(c => c.isHistorical)) {
250
244
  const prevD = new Date(date + 'T00:00:00Z');
@@ -260,8 +254,8 @@ async function handleSweepDispatch(config, dependencies, computationManifest, re
260
254
  return { dispatched: 0 };
261
255
  }
262
256
 
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.
257
+ // [FIX] Filter out deterministic failures from Sweep.
258
+ // If it failed due to 'QUALITY_CIRCUIT_BREAKER', High-Mem won't fix it.
265
259
  const validTasks = [];
266
260
  for (const task of pending) {
267
261
  const name = normalizeName(task.name);
@@ -284,7 +278,6 @@ async function handleSweepDispatch(config, dependencies, computationManifest, re
284
278
  }
285
279
 
286
280
  // 2. FORCE High Mem & Skip Zombie Check
287
- // We use validTasks now
288
281
  const currentDispatchId = crypto.randomUUID();
289
282
 
290
283
  const tasksPayload = validTasks.map(t => ({
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * FILENAME: computation-system/helpers/computation_worker.js
3
+ * UPDATED: Fixed Error Propagation Bug. Preserves 'stage' property when re-throwing logic errors.
3
4
  * UPDATED: Fixed Firestore 'undefined' field error for dispatchId.
4
- * UPDATED: Writes structured Error objects (with stage) to Ledger to prevent retry loops.
5
5
  */
6
6
 
7
7
  const { executeDispatchTask } = require('../WorkflowOrchestrator.js');
@@ -47,7 +47,7 @@ async function handleComputationTask(message, config, dependencies) {
47
47
 
48
48
  logger.log('INFO', `[Worker] 📥 Task: ${computation} (${date}) [Tier: ${resourceTier}]`);
49
49
 
50
- // [FIX] Build document object and only add dispatchId if it is defined
50
+ // [FIX] Build document object and only add dispatchId if it is defined (prevents Firestore "undefined" error)
51
51
  const leaseData = {
52
52
  status: 'IN_PROGRESS',
53
53
  workerId: process.env.K_REVISION || os.hostname(),
@@ -72,7 +72,16 @@ async function handleComputationTask(message, config, dependencies) {
72
72
  const failureReport = result?.updates?.failureReport || [];
73
73
  const successUpdates = result?.updates?.successUpdates || {};
74
74
 
75
- if (failureReport.length > 0) throw new Error(failureReport[0].error.message);
75
+ // [CRITICAL FIX] Correctly propagate the Error Stage.
76
+ // Previously, 'throw new Error(msg)' stripped the 'stage' property, causing the
77
+ // catch block to treat Deterministic errors (Quality/Logic) as System errors (Transient),
78
+ // triggering infinite Pub/Sub retries.
79
+ if (failureReport.length > 0) {
80
+ const reportedError = failureReport[0].error;
81
+ const errorObj = new Error(reportedError.message);
82
+ errorObj.stage = reportedError.stage; // Preserve stage (e.g. 'QUALITY_CIRCUIT_BREAKER')
83
+ throw errorObj;
84
+ }
76
85
 
77
86
  const calcUpdate = successUpdates[normalizeName(computation)] || {};
78
87
  const metrics = {
@@ -92,9 +101,11 @@ async function handleComputationTask(message, config, dependencies) {
92
101
  clearInterval(heartbeat.timer);
93
102
  const isDeterministic = ['SHARDING_LIMIT_EXCEEDED', 'QUALITY_CIRCUIT_BREAKER', 'SEMANTIC_GATE'].includes(err.stage);
94
103
 
104
+ // If error is deterministic (Logic/Quality), we record FAILURE and RETURN.
105
+ // This ACKs the message and stops the retry loop.
95
106
  if (isDeterministic || (message.deliveryAttempt || 1) >= MAX_RETRIES) {
96
- // [FIX] Write structured error payload so Dispatcher can see the 'stage'
97
- // This prevents the Dispatcher from retrying Quality Broken tasks.
107
+
108
+ // Write structured error to Ledger so Dispatcher can see the 'stage' later
98
109
  const errorPayload = {
99
110
  message: err.message,
100
111
  stage: err.stage || 'FATAL'
@@ -109,6 +120,8 @@ async function handleComputationTask(message, config, dependencies) {
109
120
  await recordRunAttempt(db, { date, computation, pass }, 'FAILURE', { message: err.message, stage: err.stage || 'FATAL' }, { peakMemoryMB: heartbeat.getPeak() }, triggerReason, resourceTier);
110
121
  return;
111
122
  }
123
+
124
+ // If non-deterministic (Network/System), throw to trigger Pub/Sub Retry
112
125
  throw err;
113
126
  }
114
127
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bulltrackers-module",
3
- "version": "1.0.337",
3
+ "version": "1.0.338",
4
4
  "description": "Helper Functions for Bulltrackers.",
5
5
  "main": "index.js",
6
6
  "files": [