bulltrackers-module 1.0.252 → 1.0.253

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,7 +1,7 @@
1
1
  /**
2
2
  * FILENAME: computation-system/helpers/computation_dispatcher.js
3
3
  * PURPOSE: "Smart Dispatcher" - Analyzes state and only dispatches valid, runnable tasks.
4
- * UPDATED: Implements pre-dispatch analysis to guarantee worker success.
4
+ * UPDATED: Implements Audit Ledger creation (PENDING state) before dispatch.
5
5
  */
6
6
 
7
7
  const { getExpectedDateStrings, normalizeName, DEFINITIVE_EARLIEST_DATES } = require('../utils/utils.js');
@@ -9,6 +9,7 @@ const { groupByPass, analyzeDateExecution } = require('../WorkflowOrchestrat
9
9
  const { PubSubUtils } = require('../../core/utils/pubsub_utils');
10
10
  const { fetchComputationStatus, updateComputationStatus } = require('../persistence/StatusRepository');
11
11
  const { checkRootDataAvailability } = require('../data/AvailabilityChecker');
12
+ const { commitBatchInChunks } = require('../persistence/FirestoreUtils'); // [NEW IMPORT]
12
13
  const pLimit = require('p-limit');
13
14
 
14
15
  const TOPIC_NAME = 'computation-tasks';
@@ -19,7 +20,7 @@ const STATUS_IMPOSSIBLE = 'IMPOSSIBLE';
19
20
  * Performs full pre-flight checks (Root Data, Dependencies, History) before emitting.
20
21
  */
21
22
  async function dispatchComputationPass(config, dependencies, computationManifest) {
22
- const { logger } = dependencies;
23
+ const { logger, db } = dependencies; // Added db destructuring
23
24
  const pubsubUtils = new PubSubUtils(dependencies);
24
25
  const passToRun = String(config.COMPUTATION_PASS_TO_RUN);
25
26
 
@@ -86,7 +87,6 @@ async function dispatchComputationPass(config, dependencies, computationManifest
86
87
  });
87
88
 
88
89
  // Mark Blocked/Failed Deps (Temporary Failure)
89
- // We write these so the status reflects reality, but we DO NOT dispatch them.
90
90
  [...report.blocked, ...report.failedDependency].forEach(item => {
91
91
  statusUpdates[item.name] = { hash: false, category: 'unknown', reason: item.reason };
92
92
  });
@@ -103,6 +103,7 @@ async function dispatchComputationPass(config, dependencies, computationManifest
103
103
  date: dateStr,
104
104
  pass: passToRun,
105
105
  computation: normalizeName(item.name),
106
+ hash: item.hash || item.newHash, // [NEW] Ensure Hash is passed for Ledger
106
107
  timestamp: Date.now()
107
108
  });
108
109
  });
@@ -116,7 +117,30 @@ async function dispatchComputationPass(config, dependencies, computationManifest
116
117
 
117
118
  // 4. Batch Dispatch Valid Tasks
118
119
  if (tasksToDispatch.length > 0) {
119
- logger.log('INFO', `[Dispatcher] Generated ${tasksToDispatch.length} VALID tasks. Dispatching...`);
120
+ // --- [NEW] STEP 4.1: CREATE AUDIT LEDGER ENTRIES ---
121
+ logger.log('INFO', `[Dispatcher] 📝 Creating Audit Ledger entries for ${tasksToDispatch.length} tasks...`);
122
+
123
+ const ledgerWrites = [];
124
+ for (const task of tasksToDispatch) {
125
+ const ledgerRef = db.collection(`computation_audit_ledger/${task.date}/passes/${task.pass}/tasks`).doc(task.computation);
126
+ ledgerWrites.push({
127
+ ref: ledgerRef,
128
+ data: {
129
+ status: 'PENDING',
130
+ computation: task.computation,
131
+ expectedHash: task.hash || 'unknown',
132
+ createdAt: new Date(),
133
+ retries: 0
134
+ },
135
+ options: { merge: true } // Merge allows updating retries/timestamps without wiping history
136
+ });
137
+ }
138
+
139
+ // Commit Ledger writes using chunked batch utility
140
+ await commitBatchInChunks(config, dependencies, ledgerWrites, 'AuditLedger Creation');
141
+ // ---------------------------------------------------
142
+
143
+ logger.log('INFO', `[Dispatcher] ✅ Generated ${tasksToDispatch.length} VALID tasks. Dispatching to Pub/Sub...`);
120
144
 
121
145
  await pubsubUtils.batchPublishTasks(dependencies, {
122
146
  topicName: TOPIC_NAME,
@@ -1,30 +1,36 @@
1
1
  /**
2
2
  * @fileoverview Handles saving computation results with observability and Smart Cleanup.
3
+ * UPDATED: Implements Audit Ledger completion logic ("Closing the Ledger").
3
4
  */
4
5
  const { commitBatchInChunks } = require('./FirestoreUtils');
5
6
  const { updateComputationStatus } = require('./StatusRepository');
6
7
  const { batchStoreSchemas } = require('../utils/schema_capture');
7
8
  const { generateProcessId, PROCESS_TYPES } = require('../logger/logger');
9
+ // Note: normalizeName is typically needed for doc IDs, but keys in stateObj are usually already normalized.
10
+ // If not, ensure it is imported. Based on StandardExecutor, keys are normalized.
8
11
 
9
12
  async function commitResults(stateObj, dStr, passName, config, deps, skipStatusWrite = false) {
10
13
  const successUpdates = {};
11
14
  const schemas = [];
12
15
  const cleanupTasks = []; // Tasks to delete old data
13
- const { logger } = deps;
16
+ const { logger, db } = deps;
14
17
  const pid = generateProcessId(PROCESS_TYPES.STORAGE, passName, dStr);
18
+
19
+ // [NEW] Extract numeric pass ID from string (e.g., "Pass 1" -> "1")
20
+ const passNum = passName.replace(/[^0-9]/g, '');
15
21
 
16
22
  for (const name in stateObj) {
17
23
  const calc = stateObj[name];
18
24
  try {
19
25
  const result = await calc.getResult();
20
26
 
21
- // [UPDATE] Validate Result: Check for Null, Empty Object, or Zero
27
+ // Validate Result: Check for Null, Empty Object, or Zero
22
28
  const isEmpty = !result ||
23
29
  (typeof result === 'object' && Object.keys(result).length === 0) ||
24
30
  (typeof result === 'number' && result === 0);
25
31
 
26
32
  if (isEmpty) {
27
- // [UPDATE] Mark status as FALSE (Failed/Empty) so it re-runs or is flagged
33
+ // Mark status as FALSE (Failed/Empty) so it re-runs or is flagged
28
34
  if (calc.manifest.hash) {
29
35
  successUpdates[name] = {
30
36
  hash: false,
@@ -35,7 +41,7 @@ async function commitResults(stateObj, dStr, passName, config, deps, skipStatusW
35
41
  continue;
36
42
  }
37
43
 
38
- const mainDocRef = deps.db.collection(config.resultsCollection)
44
+ const mainDocRef = db.collection(config.resultsCollection)
39
45
  .doc(dStr)
40
46
  .collection(config.resultsSubcollection)
41
47
  .doc(calc.manifest.category)
@@ -44,6 +50,22 @@ async function commitResults(stateObj, dStr, passName, config, deps, skipStatusW
44
50
 
45
51
  const updates = await prepareAutoShardedWrites(result, mainDocRef, logger);
46
52
 
53
+ // --- [NEW] ADD AUDIT LEDGER COMPLETION TO BATCH ---
54
+ if (passNum && calc.manifest) {
55
+ const ledgerRef = db.collection(`computation_audit_ledger/${dStr}/passes/${passNum}/tasks`).doc(name);
56
+ updates.push({
57
+ ref: ledgerRef,
58
+ data: {
59
+ status: 'COMPLETED',
60
+ completedAt: new Date(),
61
+ actualHash: calc.manifest.hash,
62
+ _verified: true
63
+ },
64
+ options: { merge: true }
65
+ });
66
+ }
67
+ // --------------------------------------------------
68
+
47
69
  // Capture Schema
48
70
  if (calc.manifest.class.getSchema) {
49
71
  const { class: _cls, ...safeMetadata } = calc.manifest;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bulltrackers-module",
3
- "version": "1.0.252",
3
+ "version": "1.0.253",
4
4
  "description": "Helper Functions for Bulltrackers.",
5
5
  "main": "index.js",
6
6
  "files": [