bulltrackers-module 1.0.244 → 1.0.246

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.
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * @fileoverview Main Orchestrator. Coordinates the topological execution.
3
- * UPDATED: Added 'executeDispatchTask' for trusted execution from Smart Dispatcher.
3
+ * UPDATED: Implements State Simulation for accurate single-pass reporting.
4
4
  */
5
5
  const { normalizeName, DEFINITIVE_EARLIEST_DATES } = require('./utils/utils');
6
6
  const { checkRootDataAvailability } = require('./data/AvailabilityChecker');
@@ -21,6 +21,8 @@ function groupByPass(manifest) {
21
21
 
22
22
  /**
23
23
  * Analyzes whether calculations should run, be skipped, or are blocked.
24
+ * NOW WITH SIMULATION: Updates a local status map as it progresses to ensure
25
+ * downstream dependencies 'see' the decisions made by upstream calculations.
24
26
  */
25
27
  function analyzeDateExecution(dateStr, calcsInPass, rootDataStatus, dailyStatus, manifestMap, prevDailyStatus = null) {
26
28
  const report = {
@@ -32,11 +34,16 @@ function analyzeDateExecution(dateStr, calcsInPass, rootDataStatus, dailyStatus,
32
34
  skipped: []
33
35
  };
34
36
 
37
+ // [SIMULATION STATE] Clone the initial DB status.
38
+ // We will update this locally as we make decisions, allowing 'future' calcs
39
+ // in this list to see the predicted state of their dependencies.
40
+ const simulationStatus = { ...dailyStatus };
41
+
35
42
  const isTargetToday = (dateStr === new Date().toISOString().slice(0, 10));
36
43
 
37
- const isDepSatisfied = (depName, dailyStatus, manifestMap) => {
44
+ const isDepSatisfied = (depName, currentStatusMap, manifestMap) => {
38
45
  const norm = normalizeName(depName);
39
- const stored = dailyStatus[norm];
46
+ const stored = currentStatusMap[norm];
40
47
  const depManifest = manifestMap.get(norm);
41
48
 
42
49
  if (!stored) return false;
@@ -49,20 +56,37 @@ function analyzeDateExecution(dateStr, calcsInPass, rootDataStatus, dailyStatus,
49
56
 
50
57
  for (const calc of calcsInPass) {
51
58
  const cName = normalizeName(calc.name);
52
- const stored = dailyStatus[cName];
59
+
60
+ // Use simulationStatus instead of dailyStatus
61
+ const stored = simulationStatus[cName];
53
62
 
54
63
  const storedHash = stored ? stored.hash : null;
55
64
  const storedCategory = stored ? stored.category : null;
56
65
  const currentHash = calc.hash;
57
66
 
67
+ // Decision Helpers
68
+ const markImpossible = (reason) => {
69
+ report.impossible.push({ name: cName, reason });
70
+ // UPDATE SIMULATION: Downstream deps will now see this as IMPOSSIBLE
71
+ simulationStatus[cName] = { hash: STATUS_IMPOSSIBLE, category: calc.category };
72
+ };
73
+
74
+ const markRunnable = (isReRun = false, reRunDetails = null) => {
75
+ if (isReRun) report.reRuns.push(reRunDetails);
76
+ else report.runnable.push(calc);
77
+ // UPDATE SIMULATION: Downstream deps will see this as SUCCESS (matching hash)
78
+ simulationStatus[cName] = { hash: currentHash, category: calc.category };
79
+ };
80
+
58
81
  let migrationOldCategory = null;
59
82
  if (storedCategory && storedCategory !== calc.category) {
60
83
  migrationOldCategory = storedCategory;
61
84
  }
62
85
 
63
- // 1. Check Impossible
86
+ // 1. Check Impossible (Previously recorded)
64
87
  if (storedHash === STATUS_IMPOSSIBLE) {
65
88
  report.skipped.push({ name: cName, reason: 'Permanently Impossible' });
89
+ // Simulation state remains IMPOSSIBLE
66
90
  continue;
67
91
  }
68
92
 
@@ -80,40 +104,46 @@ function analyzeDateExecution(dateStr, calcsInPass, rootDataStatus, dailyStatus,
80
104
 
81
105
  if (missingRoots.length > 0) {
82
106
  if (!isTargetToday) {
83
- report.impossible.push({ name: cName, reason: `Missing Root Data: ${missingRoots.join(', ')} (Historical)` });
107
+ // If it's a past date and root data is missing, it's permanently impossible.
108
+ markImpossible(`Missing Root Data: ${missingRoots.join(', ')} (Historical)`);
84
109
  } else {
110
+ // If it's today, we might just be early. Block, don't Impossible.
85
111
  report.blocked.push({ name: cName, reason: `Missing Root Data: ${missingRoots.join(', ')} (Waiting)` });
112
+ // We DO NOT update simulationStatus here because it's not permanently dead, just waiting.
86
113
  }
87
114
  continue;
88
115
  }
89
116
 
90
- // 3. Dependency Check
117
+ // 3. Dependency Check (Using Simulation Status)
91
118
  let dependencyIsImpossible = false;
92
119
  const missingDeps = [];
93
120
 
94
121
  if (calc.dependencies) {
95
122
  for (const dep of calc.dependencies) {
96
123
  const normDep = normalizeName(dep);
97
- const depStored = dailyStatus[normDep];
124
+
125
+ // LOOK AT SIMULATION STATUS, NOT DB SNAPSHOT
126
+ const depStored = simulationStatus[normDep];
98
127
 
99
128
  if (depStored && depStored.hash === STATUS_IMPOSSIBLE) {
100
129
  dependencyIsImpossible = true;
101
130
  break;
102
131
  }
103
132
 
104
- if (!isDepSatisfied(dep, dailyStatus, manifestMap)) {
133
+ if (!isDepSatisfied(dep, simulationStatus, manifestMap)) {
105
134
  missingDeps.push(dep);
106
135
  }
107
136
  }
108
137
  }
109
138
 
110
139
  if (dependencyIsImpossible) {
111
- report.impossible.push({ name: cName, reason: 'Dependency is Impossible' });
140
+ markImpossible('Dependency is Impossible');
112
141
  continue;
113
142
  }
114
143
 
115
144
  if (missingDeps.length > 0) {
116
145
  report.failedDependency.push({ name: cName, missing: missingDeps });
146
+ // Do not update simulation status; downstream will see this as 'missing' (Blocked)
117
147
  continue;
118
148
  }
119
149
 
@@ -137,16 +167,16 @@ function analyzeDateExecution(dateStr, calcsInPass, rootDataStatus, dailyStatus,
137
167
 
138
168
  // 5. Runnable Decision
139
169
  if (!storedHash) {
140
- report.runnable.push(calc);
170
+ markRunnable();
141
171
  } else if (storedHash !== currentHash) {
142
- report.reRuns.push({
172
+ markRunnable(true, {
143
173
  name: cName,
144
174
  oldHash: storedHash,
145
175
  newHash: currentHash,
146
176
  previousCategory: migrationOldCategory
147
177
  });
148
178
  } else if (migrationOldCategory) {
149
- report.reRuns.push({
179
+ markRunnable(true, {
150
180
  name: cName,
151
181
  reason: 'Category Migration',
152
182
  previousCategory: migrationOldCategory,
@@ -154,6 +184,8 @@ function analyzeDateExecution(dateStr, calcsInPass, rootDataStatus, dailyStatus,
154
184
  });
155
185
  } else {
156
186
  report.skipped.push({ name: cName });
187
+ // Even if skipped, ensure simulation status is fresh/set (it usually is from clone)
188
+ simulationStatus[cName] = { hash: currentHash, category: calc.category };
157
189
  }
158
190
  }
159
191
 
@@ -177,10 +209,8 @@ async function executeDispatchTask(dateStr, pass, targetComputation, config, dep
177
209
  }
178
210
 
179
211
  // 2. Fetch Root Data References (Required for execution streaming)
180
- // Even though Dispatcher checked existence, we need the actual Refs/Data objects now.
181
212
  const rootData = await checkRootDataAvailability(dateStr, config, dependencies, DEFINITIVE_EARLIEST_DATES);
182
213
 
183
- // Safety Fallback (Should be impossible if Dispatcher is working)
184
214
  if (!rootData) {
185
215
  logger.log('ERROR', `[Executor] FATAL: Root data missing for ${targetComputation} on ${dateStr}. Dispatcher desync?`);
186
216
  return;
@@ -239,18 +269,14 @@ async function executeDispatchTask(dateStr, pass, targetComputation, config, dep
239
269
 
240
270
  /**
241
271
  * Legacy/Orchestrator Mode execution (Performs analysis).
242
- * Kept for manual runs or full-system validation.
243
272
  */
244
273
  async function runDateComputation(dateStr, passToRun, calcsInThisPass, config, dependencies, computationManifest) {
245
- // ... [Previous implementation of runDateComputation can remain here if needed for backward compatibility,
246
- // ... or can be removed if the system is fully migrated. Keeping logic for "Dispatcher uses analyzeDateExecution"]
247
-
248
- // Re-exporting executeDispatchTask as the primary worker entry point.
274
+ // Legacy support logic...
249
275
  }
250
276
 
251
277
  module.exports = {
252
278
  runDateComputation,
253
- executeDispatchTask, // <--- NEW EXPORT
279
+ executeDispatchTask,
254
280
  groupByPass,
255
281
  analyzeDateExecution
256
282
  };
@@ -10,9 +10,13 @@ const { normalizeName, getExpectedDateStrings, DEFINITIVE_EARLIEST_DATES } = req
10
10
  const { checkRootDataAvailability } = require('../data/AvailabilityChecker');
11
11
  const { FieldValue } = require('@google-cloud/firestore');
12
12
  const pLimit = require('p-limit');
13
+ const path = require('path');
14
+
13
15
 
14
16
  // Attempt to load package.json to get version. Path depends on where this is invoked.
15
- let packageVersion = '1.0.301'; // Bumped version to reflect logic change
17
+ const packageJson = require(path.join(__dirname, '..', '..', 'package.json'));
18
+
19
+ const packageVersion = packageJson.version;
16
20
 
17
21
 
18
22
  /**
@@ -22,7 +26,8 @@ let packageVersion = '1.0.301'; // Bumped version to reflect logic change
22
26
  */
23
27
  async function ensureBuildReport(config, dependencies, manifest) {
24
28
  const { db, logger } = dependencies;
25
- const buildId = `v${packageVersion}`;
29
+ const now = new Date();
30
+ const buildId = `v${packageVersion}_${now.getFullYear()}-${String(now.getMonth()+1).padStart(2,'0')}-${String(now.getDate()).padStart(2,'0')}_${String(now.getHours()).padStart(2,'0')}-${String(now.getMinutes()).padStart(2,'0')}-${String(now.getSeconds()).padStart(2,'0')}`;
26
31
  const reportRef = db.collection('computation_build_records').doc(buildId);
27
32
 
28
33
  try {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bulltrackers-module",
3
- "version": "1.0.244",
3
+ "version": "1.0.246",
4
4
  "description": "Helper Functions for Bulltrackers.",
5
5
  "main": "index.js",
6
6
  "files": [