@tradejs/cli 3.1.29-beta.291 → 3.1.29-beta.296

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.
@@ -35,6 +35,98 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
35
35
  ));
36
36
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
37
37
 
38
+ // src/lib/coreResearch/trace.ts
39
+ async function* readCoreResearchTrace(filePaths) {
40
+ for (const inputPath of filePaths) {
41
+ const filePath = import_node_path.default.resolve(inputPath);
42
+ const input = (0, import_node_fs.createReadStream)(filePath);
43
+ const lines = import_node_readline.default.createInterface({ input, crlfDelay: Infinity });
44
+ let index = -1;
45
+ try {
46
+ for await (const line of lines) {
47
+ index++;
48
+ if (!line.trim()) continue;
49
+ let event;
50
+ try {
51
+ event = JSON.parse(line);
52
+ } catch (error) {
53
+ throw new Error(
54
+ `${filePath}:${index + 1} contains invalid trace JSON: ${String(error)}`
55
+ );
56
+ }
57
+ yield event;
58
+ }
59
+ } finally {
60
+ lines.close();
61
+ input.destroy();
62
+ }
63
+ }
64
+ }
65
+ var import_node_fs, import_node_readline, import_node_path, summarizeCoreResearchTrace;
66
+ var init_trace = __esm({
67
+ "src/lib/coreResearch/trace.ts"() {
68
+ "use strict";
69
+ import_node_fs = require("fs");
70
+ import_node_readline = __toESM(require("readline"));
71
+ import_node_path = __toESM(require("path"));
72
+ summarizeCoreResearchTrace = async (filePaths = []) => {
73
+ const events = /* @__PURE__ */ Object.create(null);
74
+ const skipCounts = /* @__PURE__ */ Object.create(null);
75
+ const exitCodes = /* @__PURE__ */ Object.create(null);
76
+ const policyRejections = {};
77
+ const policyExamples = [];
78
+ for await (const event of readCoreResearchTrace(filePaths)) {
79
+ events[event.event] = (events[event.event] ?? 0) + 1;
80
+ if (event.event === "skip_summary") {
81
+ for (const [reason, count] of Object.entries(event.skipCounts)) {
82
+ skipCounts[reason] = (skipCounts[reason] ?? 0) + count;
83
+ }
84
+ }
85
+ if (event.event === "position_exited" && event.exitCode) {
86
+ exitCodes[event.exitCode] = (exitCodes[event.exitCode] ?? 0) + 1;
87
+ }
88
+ if ("researchEntryDecision" in event && event.researchEntryDecision && !event.researchEntryDecision.allowed) {
89
+ const reason = event.researchEntryDecision.reason;
90
+ policyRejections[reason] = (policyRejections[reason] ?? 0) + 1;
91
+ if (policyExamples.length < 20)
92
+ policyExamples.push({
93
+ timestamp: event.timestamp,
94
+ symbol: event.symbol,
95
+ decision: event.researchEntryDecision
96
+ });
97
+ }
98
+ }
99
+ return {
100
+ events: Object.fromEntries(Object.entries(events).sort()),
101
+ skipCounts: Object.fromEntries(
102
+ Object.entries(skipCounts).sort(
103
+ ([leftKey, leftValue], [rightKey, rightValue]) => rightValue - leftValue || (leftKey < rightKey ? -1 : leftKey > rightKey ? 1 : 0)
104
+ )
105
+ ),
106
+ exitCodes: Object.fromEntries(
107
+ Object.entries(exitCodes).sort(
108
+ ([leftKey, leftValue], [rightKey, rightValue]) => rightValue - leftValue || (leftKey < rightKey ? -1 : leftKey > rightKey ? 1 : 0)
109
+ )
110
+ ),
111
+ ...policyExamples.length ? {
112
+ researchPolicy: {
113
+ rejections: policyRejections,
114
+ examples: policyExamples
115
+ }
116
+ } : {}
117
+ };
118
+ };
119
+ }
120
+ });
121
+
122
+ // src/lib/coreResearch/support.ts
123
+ var init_support = __esm({
124
+ "src/lib/coreResearch/support.ts"() {
125
+ "use strict";
126
+ init_trace();
127
+ }
128
+ });
129
+
38
130
  // src/lib/coreResearch/types.ts
39
131
  var CORE_RESEARCH_SCHEMA;
40
132
  var init_types = __esm({
@@ -48,15 +140,16 @@ var init_types = __esm({
48
140
  function assert(condition, message) {
49
141
  if (!condition) throw new Error(`Invalid core research spec: ${message}`);
50
142
  }
51
- var import_node_crypto, import_node_fs, import_promises, import_backtest, import_node_path, RESEARCH_ID_RE, SHA256_RE, canonicalJson, sha256Text, sha256Json, sha256File, assertFinite, validateCoreResearchSpec, loadCoreResearchSpec, writeJsonAtomic;
143
+ var import_node_crypto, import_node_fs2, import_promises, import_backtest, import_node_path2, RESEARCH_ID_RE, SHA256_RE, canonicalJson, sha256Text, sha256Json, sha256File, assertFinite, validateCoreResearchSpec, loadCoreResearchSpec, writeJsonAtomic;
52
144
  var init_io = __esm({
53
145
  "src/lib/coreResearch/io.ts"() {
54
146
  "use strict";
55
147
  import_node_crypto = require("crypto");
56
- import_node_fs = require("fs");
148
+ import_node_fs2 = require("fs");
57
149
  import_promises = __toESM(require("fs/promises"));
58
150
  import_backtest = require("@tradejs/core/backtest");
59
- import_node_path = __toESM(require("path"));
151
+ import_node_path2 = __toESM(require("path"));
152
+ init_support();
60
153
  init_types();
61
154
  RESEARCH_ID_RE = /^[a-z0-9][a-z0-9._-]{2,120}$/;
62
155
  SHA256_RE = /^[a-f0-9]{64}$/;
@@ -75,10 +168,10 @@ var init_io = __esm({
75
168
  sha256Text = (value) => (0, import_node_crypto.createHash)("sha256").update(value).digest("hex");
76
169
  sha256Json = (value) => sha256Text(canonicalJson(value));
77
170
  sha256File = async (filePath) => {
78
- const hash2 = (0, import_node_crypto.createHash)("sha256");
79
- const input = (0, import_node_fs.createReadStream)(filePath);
80
- for await (const chunk of input) hash2.update(chunk);
81
- return hash2.digest("hex");
171
+ const hash = (0, import_node_crypto.createHash)("sha256");
172
+ const input = (0, import_node_fs2.createReadStream)(filePath);
173
+ for await (const chunk of input) hash.update(chunk);
174
+ return hash.digest("hex");
82
175
  };
83
176
  assertFinite = (value, name) => {
84
177
  assert(typeof value === "number" && Number.isFinite(value), name);
@@ -312,16 +405,32 @@ var init_io = __esm({
312
405
  Boolean(spec.artifacts?.ledgerPath?.trim()),
313
406
  "artifacts.ledgerPath is required"
314
407
  );
408
+ if (spec.support !== void 0) {
409
+ assert(
410
+ spec.support && typeof spec.support === "object",
411
+ "support must be an object"
412
+ );
413
+ for (const [key, value2] of [
414
+ ["minimumEvents", spec.support.minimumEvents],
415
+ ["minimumBlocks", spec.support.minimumBlocks],
416
+ ...spec.support.minimumCommonSetups === void 0 ? [] : [["minimumCommonSetups", spec.support.minimumCommonSetups]]
417
+ ]) {
418
+ assert(
419
+ typeof value2 === "number" && Number.isSafeInteger(value2) && value2 > 0,
420
+ `support.${key} must be a positive integer`
421
+ );
422
+ }
423
+ }
315
424
  return spec;
316
425
  };
317
426
  loadCoreResearchSpec = async (specPath) => {
318
- const resolvedPath = import_node_path.default.resolve(specPath);
427
+ const resolvedPath = import_node_path2.default.resolve(specPath);
319
428
  const parsed = JSON.parse(await import_promises.default.readFile(resolvedPath, "utf8"));
320
429
  const spec = validateCoreResearchSpec(parsed);
321
430
  return { resolvedPath, spec, specSha256: sha256Json(spec) };
322
431
  };
323
432
  writeJsonAtomic = async (filePath, value) => {
324
- await import_promises.default.mkdir(import_node_path.default.dirname(filePath), { recursive: true });
433
+ await import_promises.default.mkdir(import_node_path2.default.dirname(filePath), { recursive: true });
325
434
  const temporaryPath = `${filePath}.${process.pid}.${Date.now()}.tmp`;
326
435
  await import_promises.default.writeFile(temporaryPath, `${canonicalJson(value)}
327
436
  `, "utf8");
@@ -342,30 +451,30 @@ __export(io_exports, {
342
451
  verifyResearchState: () => verifyResearchState,
343
452
  withResearchLock: () => withResearchLock
344
453
  });
345
- var import_promises2, import_node_path2, import_node_os, import_node_crypto3, researchDirectory, readJson, boundResearchPath, withResearchLock, verifyResearchState, verifyCompletedResearch, initializeResearchState, isRetainedDeletion;
454
+ var import_promises2, import_node_path3, import_node_os, import_node_crypto2, researchDirectory, readJson, boundResearchPath, withResearchLock, verifyResearchState, verifyCompletedResearch, initializeResearchState, isRetainedDeletion;
346
455
  var init_io2 = __esm({
347
456
  "src/lib/strategyResearch/io.ts"() {
348
457
  "use strict";
349
458
  import_promises2 = __toESM(require("fs/promises"));
350
- import_node_path2 = __toESM(require("path"));
459
+ import_node_path3 = __toESM(require("path"));
351
460
  import_node_os = __toESM(require("os"));
352
- import_node_crypto3 = require("crypto");
461
+ import_node_crypto2 = require("crypto");
353
462
  init_io();
354
- researchDirectory = (projectRoot, spec) => import_node_path2.default.join(projectRoot, "data/research/strategy", spec.core.researchId);
463
+ researchDirectory = (projectRoot, spec) => import_node_path3.default.join(projectRoot, "data/research/strategy", spec.core.researchId);
355
464
  readJson = async (file) => JSON.parse(await import_promises2.default.readFile(file, "utf8"));
356
465
  boundResearchPath = async (root, relative) => {
357
- const target = import_node_path2.default.resolve(root, relative);
466
+ const target = import_node_path3.default.resolve(root, relative);
358
467
  const realRoot = await import_promises2.default.realpath(root);
359
468
  let ancestor = target;
360
469
  while (true) {
361
470
  try {
362
471
  const real = await import_promises2.default.realpath(ancestor);
363
- if (real !== realRoot && !real.startsWith(`${realRoot}${import_node_path2.default.sep}`))
472
+ if (real !== realRoot && !real.startsWith(`${realRoot}${import_node_path3.default.sep}`))
364
473
  throw new Error(`Research path escapes Project: ${relative}`);
365
474
  break;
366
475
  } catch (error) {
367
476
  if (error.code !== "ENOENT") throw error;
368
- const parent = import_node_path2.default.dirname(ancestor);
477
+ const parent = import_node_path3.default.dirname(ancestor);
369
478
  if (parent === ancestor) throw error;
370
479
  ancestor = parent;
371
480
  }
@@ -377,8 +486,8 @@ var init_io2 = __esm({
377
486
  projectRoot,
378
487
  "data/research/strategy/.lock"
379
488
  );
380
- await import_promises2.default.mkdir(import_node_path2.default.dirname(file), { recursive: true });
381
- const token = (0, import_node_crypto3.randomUUID)();
489
+ await import_promises2.default.mkdir(import_node_path3.default.dirname(file), { recursive: true });
490
+ const token = (0, import_node_crypto2.randomUUID)();
382
491
  const acquire = () => import_promises2.default.writeFile(
383
492
  file,
384
493
  JSON.stringify({ pid: process.pid, host: import_node_os.default.hostname(), token }),
@@ -411,7 +520,7 @@ var init_io2 = __esm({
411
520
  }
412
521
  };
413
522
  verifyResearchState = async (directory, state) => {
414
- const frozenSpec = await readJson(import_node_path2.default.join(directory, "spec.json"));
523
+ const frozenSpec = await readJson(import_node_path3.default.join(directory, "spec.json"));
415
524
  if (sha256Json(frozenSpec) !== state.specSha256)
416
525
  throw new Error("Frozen research spec changed");
417
526
  for (const [name, stage] of Object.entries(state.stages)) {
@@ -430,14 +539,14 @@ var init_io2 = __esm({
430
539
  };
431
540
  verifyCompletedResearch = async (directory, state) => {
432
541
  await verifyResearchState(directory, state);
433
- const manifest = await readJson(import_node_path2.default.join(directory, "manifest.json"));
542
+ const manifest = await readJson(import_node_path3.default.join(directory, "manifest.json"));
434
543
  if (manifest.specSha256 !== state.specSha256)
435
544
  throw new Error("Final manifest spec mismatch");
436
545
  for (const [relative, expected] of Object.entries(manifest.artifacts))
437
546
  if (await sha256File(await boundResearchPath(directory, relative)) !== expected)
438
547
  throw new Error(`Changed final research artifact: ${relative}`);
439
- const inventory = await readJson(import_node_path2.default.join(directory, "retention.json"));
440
- const projectRoot = import_node_path2.default.resolve(directory, "../../../..");
548
+ const inventory = await readJson(import_node_path3.default.join(directory, "retention.json"));
549
+ const projectRoot = import_node_path3.default.resolve(directory, "../../../..");
441
550
  for (const artifact of inventory.research.permanentArtifacts) {
442
551
  const file = await boundResearchPath(projectRoot, artifact.path);
443
552
  if (await sha256File(file) !== artifact.sha256)
@@ -446,7 +555,7 @@ var init_io2 = __esm({
446
555
  };
447
556
  initializeResearchState = async (directory, spec, lineage) => {
448
557
  await import_promises2.default.mkdir(directory, { recursive: true });
449
- const file = import_node_path2.default.join(directory, "state.json");
558
+ const file = import_node_path3.default.join(directory, "state.json");
450
559
  let previous;
451
560
  try {
452
561
  previous = await readJson(file);
@@ -470,19 +579,19 @@ var init_io2 = __esm({
470
579
  status: "running",
471
580
  stages: {}
472
581
  };
473
- await writeJsonAtomic(import_node_path2.default.join(directory, "spec.json"), spec);
582
+ await writeJsonAtomic(import_node_path3.default.join(directory, "spec.json"), spec);
474
583
  await writeJsonAtomic(file, state);
475
584
  return state;
476
585
  };
477
- isRetainedDeletion = async (directory, file, hash2) => {
586
+ isRetainedDeletion = async (directory, file, hash) => {
478
587
  try {
479
- const inventory = await readJson(import_node_path2.default.join(directory, "retention.json"));
588
+ const inventory = await readJson(import_node_path3.default.join(directory, "retention.json"));
480
589
  const index = inventory.entries.findIndex(
481
- (entry) => entry.path === file && entry.sha256 === hash2
590
+ (entry) => entry.path === file && entry.sha256 === hash
482
591
  );
483
592
  if (index < 0) return false;
484
- const receipt = await readJson(import_node_path2.default.join(directory, "retention", `${index}.json`));
485
- return ["applying", "completed"].includes(receipt.status) && receipt.files[0]?.path === file && receipt.files[0]?.sha256 === hash2;
593
+ const receipt = await readJson(import_node_path3.default.join(directory, "retention", `${index}.json`));
594
+ return ["applying", "completed"].includes(receipt.status) && receipt.files[0]?.path === file && receipt.files[0]?.sha256 === hash;
486
595
  } catch (error) {
487
596
  if (error.code === "ENOENT") return false;
488
597
  throw error;
@@ -1818,15 +1927,15 @@ var createPocketResult = (rows, predicates, mask, validationRows, testRows, obje
1818
1927
  };
1819
1928
  };
1820
1929
  var hashMask = (mask) => {
1821
- let hash2 = 2166136261;
1930
+ let hash = 2166136261;
1822
1931
  for (let index = 0; index < mask.length; index += 1) {
1823
1932
  if (mask[index] !== 1) {
1824
1933
  continue;
1825
1934
  }
1826
- hash2 ^= index + 1;
1827
- hash2 = Math.imul(hash2, 16777619);
1935
+ hash ^= index + 1;
1936
+ hash = Math.imul(hash, 16777619);
1828
1937
  }
1829
- return (hash2 >>> 0).toString(36);
1938
+ return (hash >>> 0).toString(36);
1830
1939
  };
1831
1940
  var isBetterRepresentativePocket = (candidate, existing, compare) => candidate.depth < existing.depth || candidate.depth === existing.depth && candidate.condition.length < existing.condition.length || candidate.depth === existing.depth && candidate.condition.length === existing.condition.length && compare(candidate, existing) < 0;
1832
1941
  var estimateCombinationCount = (poolSize, maxDepth, maxCombinations) => {
@@ -2523,52 +2632,8 @@ var policyVariant = (spec, coreId, policy, id, jev) => {
2523
2632
 
2524
2633
  // src/lib/strategyResearch/orchestrator.ts
2525
2634
  var import_promises5 = __toESM(require("fs/promises"));
2526
-
2527
- // src/lib/researchMetricSnapshot.ts
2528
- var import_node_crypto2 = require("crypto");
2529
- var import_node_zlib = require("zlib");
2530
- var MAX_BYTES = 256 * 1024 * 1024;
2531
- var hash = (bytes) => (0, import_node_crypto2.createHash)("sha256").update(bytes).digest("hex");
2532
- var validatePermanentResearchMetrics = (value) => {
2533
- if (!value || typeof value !== "object") return;
2534
- for (const [key, child] of Object.entries(value)) {
2535
- if (["matchedPairs", "tradeRows", "allProjectedLeaves"].includes(key) && Array.isArray(child) && child.length)
2536
- throw new Error(`Temporary payload ${key} is not a permanent metric`);
2537
- if (key === "trades" && Array.isArray(child) && child.some(
2538
- (row) => row && typeof row === "object" && ("signalId" in row || "entryTimestamp" in row)
2539
- ))
2540
- throw new Error("Temporary trade records are not permanent metrics");
2541
- validatePermanentResearchMetrics(child);
2542
- }
2543
- };
2544
- var encodeResearchMetricSnapshot = (metrics) => {
2545
- validatePermanentResearchMetrics(metrics);
2546
- const bytes = Buffer.from(JSON.stringify(metrics));
2547
- if (bytes.length > MAX_BYTES)
2548
- throw new Error("Metric snapshot exceeds decoded size limit");
2549
- return {
2550
- schema: "tradejs-metric-snapshot/v1",
2551
- encoding: "gzip+base64",
2552
- decodedBytes: bytes.length,
2553
- sha256: hash(bytes),
2554
- payload: (0, import_node_zlib.gzipSync)(bytes, { level: 9 }).toString("base64")
2555
- };
2556
- };
2557
- var decodeResearchMetricSnapshot = (snapshot) => {
2558
- if (!snapshot || typeof snapshot !== "object" || !("schema" in snapshot) || snapshot.schema !== "tradejs-metric-snapshot/v1" || !("encoding" in snapshot) || snapshot.encoding !== "gzip+base64" || !("decodedBytes" in snapshot) || typeof snapshot.decodedBytes !== "number" || !Number.isSafeInteger(snapshot.decodedBytes) || snapshot.decodedBytes <= 0 || snapshot.decodedBytes > MAX_BYTES || !("sha256" in snapshot) || typeof snapshot.sha256 !== "string" || !/^[a-f0-9]{64}$/.test(snapshot.sha256) || !("payload" in snapshot) || typeof snapshot.payload !== "string")
2559
- throw new Error("Invalid metric snapshot envelope");
2560
- const bytes = (0, import_node_zlib.gunzipSync)(Buffer.from(snapshot.payload, "base64"), {
2561
- maxOutputLength: snapshot.decodedBytes
2562
- });
2563
- if (bytes.length !== snapshot.decodedBytes || hash(bytes) !== snapshot.sha256)
2564
- throw new Error("Metric snapshot checksum or size mismatch");
2565
- const metrics = JSON.parse(bytes.toString("utf8"));
2566
- validatePermanentResearchMetrics(metrics);
2567
- return metrics;
2568
- };
2569
-
2570
- // src/lib/strategyResearch/orchestrator.ts
2571
- var import_node_path6 = __toESM(require("path"));
2635
+ var import_researchMetricSnapshot2 = require("@tradejs/infra/researchMetricSnapshot");
2636
+ var import_node_path7 = __toESM(require("path"));
2572
2637
 
2573
2638
  // src/lib/coreResearch/metrics.ts
2574
2639
  var DAY_MS2 = 24 * 60 * 60 * 1e3;
@@ -3769,16 +3834,17 @@ var testCharts = (conclusion) => {
3769
3834
 
3770
3835
  // src/lib/strategyResearch/retention.ts
3771
3836
  var import_promises4 = __toESM(require("fs/promises"));
3772
- var import_node_path5 = __toESM(require("path"));
3837
+ var import_node_path6 = __toESM(require("path"));
3773
3838
 
3774
3839
  // src/lib/strategyRelease/researchRetention.ts
3775
3840
  var import_promises3 = __toESM(require("fs/promises"));
3776
- var import_node_path4 = __toESM(require("path"));
3777
- var import_node_crypto4 = require("crypto");
3841
+ var import_researchMetricSnapshot = require("@tradejs/infra/researchMetricSnapshot");
3842
+ var import_node_path5 = __toESM(require("path"));
3843
+ var import_node_crypto3 = require("crypto");
3778
3844
  var import_strategyReleaseEvidence = require("@tradejs/infra/strategyReleaseEvidence");
3779
3845
 
3780
3846
  // src/lib/strategyRelease/researchFileConsumers.ts
3781
- var import_node_path3 = __toESM(require("path"));
3847
+ var import_node_path4 = __toESM(require("path"));
3782
3848
  var import_node_child_process = require("child_process");
3783
3849
  var import_node_util = require("util");
3784
3850
  var execFileAsync = (0, import_node_util.promisify)(import_node_child_process.execFile);
@@ -3809,15 +3875,15 @@ print(json.dumps(sorted(opened)))
3809
3875
  `;
3810
3876
  var assertNoResearchFileConsumers = async (files, run = runCommand) => {
3811
3877
  const virtualPids = /* @__PURE__ */ new Set();
3812
- const wanted = new Set(files.map((file) => import_node_path3.default.resolve(file)));
3813
- let commonRoot = files.length ? import_node_path3.default.dirname(files[0]) : "";
3878
+ const wanted = new Set(files.map((file) => import_node_path4.default.resolve(file)));
3879
+ let commonRoot = files.length ? import_node_path4.default.dirname(files[0]) : "";
3814
3880
  while (commonRoot && files.some((file) => {
3815
- const relative = import_node_path3.default.relative(commonRoot, file);
3816
- return relative === ".." || relative.startsWith(`..${import_node_path3.default.sep}`) || import_node_path3.default.isAbsolute(relative);
3881
+ const relative = import_node_path4.default.relative(commonRoot, file);
3882
+ return relative === ".." || relative.startsWith(`..${import_node_path4.default.sep}`) || import_node_path4.default.isAbsolute(relative);
3817
3883
  }))
3818
- commonRoot = import_node_path3.default.dirname(commonRoot);
3884
+ commonRoot = import_node_path4.default.dirname(commonRoot);
3819
3885
  const selectors = [];
3820
- if (files.length > 1e3 && commonRoot !== import_node_path3.default.parse(commonRoot).root) {
3886
+ if (files.length > 1e3 && commonRoot !== import_node_path4.default.parse(commonRoot).root) {
3821
3887
  selectors.push(["+D", commonRoot]);
3822
3888
  } else {
3823
3889
  for (let offset = 0; offset < files.length; offset += 100)
@@ -3841,7 +3907,7 @@ var assertNoResearchFileConsumers = async (files, run = runCommand) => {
3841
3907
  if (line.startsWith("c"))
3842
3908
  virtual = line.startsWith("ccom.apple.Virtualization.");
3843
3909
  if (line.startsWith("n")) {
3844
- if (!wanted.has(import_node_path3.default.resolve(line.slice(1)))) continue;
3910
+ if (!wanted.has(import_node_path4.default.resolve(line.slice(1)))) continue;
3845
3911
  if (!virtual || !/^\d+$/.test(pid))
3846
3912
  throw new Error(
3847
3913
  `Research cleanup found open files: ${line.slice(1)}`
@@ -3875,9 +3941,9 @@ var assertNoResearchFileConsumers = async (files, run = runCommand) => {
3875
3941
  for (const mount of container.Mounts) {
3876
3942
  if (mount.Type !== "bind") continue;
3877
3943
  for (const file of files) {
3878
- const relative = import_node_path3.default.relative(mount.Source, file);
3879
- if (relative === "" || !relative.startsWith(`..${import_node_path3.default.sep}`) && relative !== ".." && !import_node_path3.default.isAbsolute(relative)) {
3880
- guestFiles.add(import_node_path3.default.posix.join(mount.Destination, relative));
3944
+ const relative = import_node_path4.default.relative(mount.Source, file);
3945
+ if (relative === "" || !relative.startsWith(`..${import_node_path4.default.sep}`) && relative !== ".." && !import_node_path4.default.isAbsolute(relative)) {
3946
+ guestFiles.add(import_node_path4.default.posix.join(mount.Destination, relative));
3881
3947
  covered.add(file);
3882
3948
  }
3883
3949
  }
@@ -3923,15 +3989,15 @@ var mapFiles = async (entries, visit) => {
3923
3989
  if (worker.status === "rejected") throw worker.reason;
3924
3990
  return results;
3925
3991
  };
3926
- var fileSha256 = async (file, bytes) => bytes <= 1024 * 1024 ? (0, import_node_crypto4.createHash)("sha256").update(await import_promises3.default.readFile(file)).digest("hex") : (0, import_strategyReleaseEvidence.strategyEvidenceFileSha256)(file);
3992
+ var fileSha256 = async (file, bytes) => bytes <= 1024 * 1024 ? (0, import_node_crypto3.createHash)("sha256").update(await import_promises3.default.readFile(file)).digest("hex") : (0, import_strategyReleaseEvidence.strategyEvidenceFileSha256)(file);
3927
3993
  var inside = (root, file) => {
3928
- const relative = import_node_path4.default.relative(root, file);
3929
- return relative !== "" && !relative.startsWith(`..${import_node_path4.default.sep}`) && relative !== ".." && !import_node_path4.default.isAbsolute(relative);
3994
+ const relative = import_node_path5.default.relative(root, file);
3995
+ return relative !== "" && !relative.startsWith(`..${import_node_path5.default.sep}`) && relative !== ".." && !import_node_path5.default.isAbsolute(relative);
3930
3996
  };
3931
3997
  var applyResearchRetention = async (options) => {
3932
3998
  const projectRoot = await import_promises3.default.realpath(options.projectRoot);
3933
- const dataRoot = import_node_path4.default.join(projectRoot, "data");
3934
- const resolve = (file) => import_node_path4.default.resolve(projectRoot, file);
3999
+ const dataRoot = import_node_path5.default.join(projectRoot, "data");
4000
+ const resolve = (file) => import_node_path5.default.resolve(projectRoot, file);
3935
4001
  const receiptPath = resolve(options.receiptPath);
3936
4002
  const journalPath = `${receiptPath}.deleted.jsonl`;
3937
4003
  const permanent = new Set(
@@ -3986,7 +4052,7 @@ var applyResearchRetention = async (options) => {
3986
4052
  throw new Error(
3987
4053
  "Complete v2 note needs one verified metric snapshot"
3988
4054
  );
3989
- decodeResearchMetricSnapshot(JSON.parse(blocks[0][1]));
4055
+ (0, import_researchMetricSnapshot.decodeResearchMetricSnapshot)(JSON.parse(blocks[0][1]));
3990
4056
  }
3991
4057
  } finally {
3992
4058
  await handle.close();
@@ -4030,8 +4096,8 @@ var applyResearchRetention = async (options) => {
4030
4096
  }))
4031
4097
  };
4032
4098
  if (!options.apply) return receipt;
4033
- await import_promises3.default.mkdir(import_node_path4.default.dirname(receiptPath), { recursive: true });
4034
- if (await import_promises3.default.realpath(import_node_path4.default.dirname(receiptPath)) !== import_node_path4.default.dirname(receiptPath)) {
4099
+ await import_promises3.default.mkdir(import_node_path5.default.dirname(receiptPath), { recursive: true });
4100
+ if (await import_promises3.default.realpath(import_node_path5.default.dirname(receiptPath)) !== import_node_path5.default.dirname(receiptPath)) {
4035
4101
  throw new Error("Cleanup receipt refuses symlinked directories");
4036
4102
  }
4037
4103
  const lock = await import_promises3.default.open(`${receiptPath}.lock`, "wx");
@@ -4077,17 +4143,17 @@ var createResearchRetentionInventory = async (projectRoot, directory, spec, stat
4077
4143
  const permanentArtifacts = [];
4078
4144
  for (const [role, file] of [
4079
4145
  ["note", notePath],
4080
- ["metrics", import_node_path5.default.join(directory, "result.json")],
4081
- ["candidates", import_node_path5.default.join(directory, "03-freeze-gates/result.json")],
4082
- ["candidates", import_node_path5.default.join(directory, "resolved-spec.json")],
4083
- ["ledger", import_node_path5.default.join(directory, "05-freeze-primary/result.json")],
4084
- ["chart", import_node_path5.default.join(directory, "report.html")],
4146
+ ["metrics", import_node_path6.default.join(directory, "result.json")],
4147
+ ["candidates", import_node_path6.default.join(directory, "03-freeze-gates/result.json")],
4148
+ ["candidates", import_node_path6.default.join(directory, "resolved-spec.json")],
4149
+ ["ledger", import_node_path6.default.join(directory, "05-freeze-primary/result.json")],
4150
+ ["chart", import_node_path6.default.join(directory, "report.html")],
4085
4151
  ...[
4086
4152
  "source.patch",
4087
4153
  "source-baseline.tar",
4088
4154
  "source-untracked.json",
4089
4155
  "source-jev-models.json"
4090
- ].map((file2) => ["source", import_node_path5.default.join(directory, file2)])
4156
+ ].map((file2) => ["source", import_node_path6.default.join(directory, file2)])
4091
4157
  ]) {
4092
4158
  try {
4093
4159
  permanentArtifacts.push({
@@ -4101,11 +4167,11 @@ var createResearchRetentionInventory = async (projectRoot, directory, spec, stat
4101
4167
  }
4102
4168
  const files = /* @__PURE__ */ new Map();
4103
4169
  for (const stage of Object.values(state.stages)) {
4104
- for (const [relative, hash2] of Object.entries(stage.artifacts)) {
4170
+ for (const [relative, hash] of Object.entries(stage.artifacts)) {
4105
4171
  const file = await boundResearchPath(directory, relative);
4106
4172
  if (relative.endsWith("/result.json")) {
4107
4173
  const result = await readJson(file);
4108
- if (result?.dataset) files.set(file, hash2);
4174
+ if (result?.dataset) files.set(file, hash);
4109
4175
  }
4110
4176
  if (relative.endsWith("/sources.json") || relative.endsWith("/payloads.json")) {
4111
4177
  const sources = await readJson(file);
@@ -4122,13 +4188,13 @@ var createResearchRetentionInventory = async (projectRoot, directory, spec, stat
4122
4188
  }
4123
4189
  }
4124
4190
  const entries = [];
4125
- for (const [file, hash2] of files) {
4191
+ for (const [file, hash] of files) {
4126
4192
  const stat = await import_promises4.default.stat(file);
4127
4193
  entries.push({
4128
4194
  path: file,
4129
4195
  kind: "research_payload",
4130
4196
  researchId: spec.core.researchId,
4131
- sha256: hash2,
4197
+ sha256: hash,
4132
4198
  bytes: stat.size,
4133
4199
  createdAt: stat.birthtimeMs,
4134
4200
  verified: true,
@@ -4147,13 +4213,13 @@ var createResearchRetentionInventory = async (projectRoot, directory, spec, stat
4147
4213
  };
4148
4214
  var applyRetentionInventory = async (projectRoot, directory) => {
4149
4215
  const inventory = await readJson(
4150
- import_node_path5.default.join(directory, "retention.json")
4216
+ import_node_path6.default.join(directory, "retention.json")
4151
4217
  );
4152
- const receipts = import_node_path5.default.join(directory, "retention");
4218
+ const receipts = import_node_path6.default.join(directory, "retention");
4153
4219
  await import_promises4.default.mkdir(receipts, { recursive: true });
4154
4220
  for (let index = 0; index < inventory.entries.length; index++) {
4155
4221
  const entry = inventory.entries[index];
4156
- const receiptPath = import_node_path5.default.join(receipts, `${index}.json`);
4222
+ const receiptPath = import_node_path6.default.join(receipts, `${index}.json`);
4157
4223
  let receipt;
4158
4224
  try {
4159
4225
  receipt = await readJson(receiptPath);
@@ -4189,13 +4255,13 @@ var applyRetentionInventory = async (projectRoot, directory) => {
4189
4255
  entries: [entry],
4190
4256
  receiptPath,
4191
4257
  apply: true,
4192
- protectedPaths: [import_node_path5.default.join(directory, "retention.json")]
4258
+ protectedPaths: [import_node_path6.default.join(directory, "retention.json")]
4193
4259
  });
4194
4260
  }
4195
4261
  return { status: "completed", files: inventory.entries.length };
4196
4262
  };
4197
4263
  var finishResearchRetention = async (projectRoot, directory) => {
4198
- const statusPath = import_node_path5.default.join(directory, "retention-status.json");
4264
+ const statusPath = import_node_path6.default.join(directory, "retention-status.json");
4199
4265
  try {
4200
4266
  const result = await applyRetentionInventory(projectRoot, directory);
4201
4267
  await writeJsonAtomic(statusPath, result);
@@ -4222,7 +4288,7 @@ var runStrategyResearch = async (parameters) => {
4222
4288
  const directory = researchDirectory(projectRoot, spec);
4223
4289
  const lineage = await backend.lineage();
4224
4290
  const state = await initializeResearchState(directory, spec, lineage);
4225
- const statePath = import_node_path6.default.join(directory, "state.json");
4291
+ const statePath = import_node_path7.default.join(directory, "state.json");
4226
4292
  const elapsedBefore = state.elapsedMs ?? 0;
4227
4293
  const activeStart = Date.now();
4228
4294
  const elapsed = () => elapsedBefore + Date.now() - activeStart;
@@ -4232,7 +4298,7 @@ var runStrategyResearch = async (parameters) => {
4232
4298
  directory,
4233
4299
  state,
4234
4300
  conclusion: await readJson(
4235
- import_node_path6.default.join(directory, "result.json")
4301
+ import_node_path7.default.join(directory, "result.json")
4236
4302
  )
4237
4303
  };
4238
4304
  }
@@ -4242,13 +4308,13 @@ var runStrategyResearch = async (parameters) => {
4242
4308
  return writeJsonAtomic(statePath, state);
4243
4309
  };
4244
4310
  const event = async (stage2, status) => import_promises5.default.appendFile(
4245
- import_node_path6.default.join(directory, "events.jsonl"),
4311
+ import_node_path7.default.join(directory, "events.jsonl"),
4246
4312
  `${JSON.stringify({ at: (/* @__PURE__ */ new Date()).toISOString(), stage: stage2, status, specSha256: state.specSha256 })}
4247
4313
  `
4248
4314
  );
4249
4315
  const stage = async (name, work) => {
4250
4316
  const stageDirectory = await boundResearchPath(directory, name);
4251
- const resultPath = import_node_path6.default.join(stageDirectory, "result.json");
4317
+ const resultPath = import_node_path7.default.join(stageDirectory, "result.json");
4252
4318
  if (state.stages[name]?.status === "completed")
4253
4319
  return readJson(resultPath);
4254
4320
  if (elapsed() > spec.budget.maxMinutes * 6e4)
@@ -4267,7 +4333,7 @@ var runStrategyResearch = async (parameters) => {
4267
4333
  await save();
4268
4334
  await event(name, "started");
4269
4335
  await import_promises5.default.writeFile(
4270
- import_node_path6.default.join(directory, "report.html"),
4336
+ import_node_path7.default.join(directory, "report.html"),
4271
4337
  buildStrategyResearchReport({ spec, state, diagnostics })
4272
4338
  );
4273
4339
  try {
@@ -4275,7 +4341,7 @@ var runStrategyResearch = async (parameters) => {
4275
4341
  await writeJsonAtomic(resultPath, result);
4276
4342
  const artifacts = {};
4277
4343
  for (const file of await import_promises5.default.readdir(stageDirectory)) {
4278
- const full = import_node_path6.default.join(stageDirectory, file);
4344
+ const full = import_node_path7.default.join(stageDirectory, file);
4279
4345
  if ((await import_promises5.default.stat(full)).isFile())
4280
4346
  artifacts[`${name}/${file}`] = await sha256File(full);
4281
4347
  }
@@ -4287,7 +4353,7 @@ var runStrategyResearch = async (parameters) => {
4287
4353
  "source-jev-models.json"
4288
4354
  ]) {
4289
4355
  try {
4290
- artifacts[file] = await sha256File(import_node_path6.default.join(directory, file));
4356
+ artifacts[file] = await sha256File(import_node_path7.default.join(directory, file));
4291
4357
  } catch (error) {
4292
4358
  if (error.code !== "ENOENT")
4293
4359
  throw error;
@@ -4320,14 +4386,14 @@ var runStrategyResearch = async (parameters) => {
4320
4386
  let previousRunId;
4321
4387
  try {
4322
4388
  previousRunId = (await readJson(
4323
- import_node_path6.default.join(folder, "native-run.json")
4389
+ import_node_path7.default.join(folder, "native-run.json")
4324
4390
  )).runId;
4325
4391
  } catch (error) {
4326
4392
  if (error.code !== "ENOENT") throw error;
4327
4393
  }
4328
4394
  if (!previousRunId) {
4329
4395
  try {
4330
- if ((await import_promises5.default.stat(import_node_path6.default.join(folder, "backtest.log"))).size)
4396
+ if ((await import_promises5.default.stat(import_node_path7.default.join(folder, "backtest.log"))).size)
4331
4397
  throw new Error(
4332
4398
  "Previous native attempt has no recorded run identity; inspect its log before another launch"
4333
4399
  );
@@ -4348,7 +4414,7 @@ var runStrategyResearch = async (parameters) => {
4348
4414
  },
4349
4415
  variant,
4350
4416
  folder,
4351
- async (runId) => writeJsonAtomic(import_node_path6.default.join(folder, "native-run.json"), { runId }),
4417
+ async (runId) => writeJsonAtomic(import_node_path7.default.join(folder, "native-run.json"), { runId }),
4352
4418
  previousRunId,
4353
4419
  confirmation
4354
4420
  );
@@ -4363,15 +4429,15 @@ var runStrategyResearch = async (parameters) => {
4363
4429
  try {
4364
4430
  const inventory = await stage("01-preflight", async () => {
4365
4431
  const prior = [];
4366
- const root = import_node_path6.default.dirname(directory);
4432
+ const root = import_node_path7.default.dirname(directory);
4367
4433
  for (const id of await import_promises5.default.readdir(root)) {
4368
4434
  if (id === spec.core.researchId || id.startsWith(".")) continue;
4369
4435
  try {
4370
4436
  const previous = await readJson(
4371
- import_node_path6.default.join(root, id, "spec.json")
4437
+ import_node_path7.default.join(root, id, "spec.json")
4372
4438
  );
4373
4439
  const previousState = await readJson(
4374
- import_node_path6.default.join(root, id, "state.json")
4440
+ import_node_path7.default.join(root, id, "state.json")
4375
4441
  );
4376
4442
  const overlap = previous.core.strategy === spec.core.strategy && previous.core.window.start < spec.core.window.end && previous.core.window.end > (spec.testSince || spec.core.window.start);
4377
4443
  if (overlap && previousState.holdoutOpenedAt)
@@ -4388,7 +4454,7 @@ var runStrategyResearch = async (parameters) => {
4388
4454
  });
4389
4455
  let partitionControl;
4390
4456
  if (spec.testSince === 0) {
4391
- const control2 = spec.core.variants.find(
4457
+ const control = spec.core.variants.find(
4392
4458
  (variant) => variant.role === "control"
4393
4459
  );
4394
4460
  partitionControl = await stage(
@@ -4397,7 +4463,7 @@ var runStrategyResearch = async (parameters) => {
4397
4463
  let previous;
4398
4464
  try {
4399
4465
  previous = (await readJson(
4400
- import_node_path6.default.join(folder, "native-run.json")
4466
+ import_node_path7.default.join(folder, "native-run.json")
4401
4467
  )).runId;
4402
4468
  } catch (error) {
4403
4469
  if (error.code !== "ENOENT")
@@ -4405,9 +4471,9 @@ var runStrategyResearch = async (parameters) => {
4405
4471
  }
4406
4472
  return backend.runVariant(
4407
4473
  spec,
4408
- control2,
4474
+ control,
4409
4475
  folder,
4410
- async (runId) => writeJsonAtomic(import_node_path6.default.join(folder, "native-run.json"), {
4476
+ async (runId) => writeJsonAtomic(import_node_path7.default.join(folder, "native-run.json"), {
4411
4477
  runId
4412
4478
  }),
4413
4479
  previous
@@ -4421,15 +4487,12 @@ var runStrategyResearch = async (parameters) => {
4421
4487
  );
4422
4488
  spec = { ...spec, testSince: partition.testSince };
4423
4489
  }
4424
- await writeJsonAtomic(import_node_path6.default.join(directory, "resolved-spec.json"), spec);
4490
+ await writeJsonAtomic(import_node_path7.default.join(directory, "resolved-spec.json"), spec);
4425
4491
  let gates = [];
4426
4492
  let primaryId = null;
4427
4493
  let frozenSelection;
4428
4494
  const raw = {};
4429
4495
  const discovered = [];
4430
- const control = spec.core.variants.find(
4431
- (variant) => variant.role === "control"
4432
- );
4433
4496
  const currentPolicy = {
4434
4497
  schema: "tradejs-research-entry-policy/v1",
4435
4498
  strategy: spec.core.strategy,
@@ -4480,10 +4543,10 @@ var runStrategyResearch = async (parameters) => {
4480
4543
  core: { ...spec.core, researchId: spec.frozenFrom }
4481
4544
  });
4482
4545
  const parentState = await readJson(
4483
- import_node_path6.default.join(parentDirectory, "state.json")
4546
+ import_node_path7.default.join(parentDirectory, "state.json")
4484
4547
  );
4485
4548
  const parentSpec = await readJson(
4486
- import_node_path6.default.join(parentDirectory, "spec.json")
4549
+ import_node_path7.default.join(parentDirectory, "spec.json")
4487
4550
  );
4488
4551
  const { verifyCompletedResearch: verifyCompletedResearch2 } = await Promise.resolve().then(() => (init_io2(), io_exports));
4489
4552
  await verifyCompletedResearch2(parentDirectory, parentState);
@@ -4510,10 +4573,10 @@ var runStrategyResearch = async (parameters) => {
4510
4573
  "Frozen revalidation requires complete, unchanged rules/runtime and a strictly new period"
4511
4574
  );
4512
4575
  gates = await readJson(
4513
- import_node_path6.default.join(parentDirectory, "03-freeze-gates/result.json")
4576
+ import_node_path7.default.join(parentDirectory, "03-freeze-gates/result.json")
4514
4577
  );
4515
4578
  frozenSelection = await readJson(
4516
- import_node_path6.default.join(parentDirectory, "05-freeze-primary/result.json")
4579
+ import_node_path7.default.join(parentDirectory, "05-freeze-primary/result.json")
4517
4580
  );
4518
4581
  if (frozenSelection.schema !== "tradejs-research-selection/v1" || !Array.isArray(frozenSelection.compositions))
4519
4582
  throw new Error(
@@ -4642,7 +4705,7 @@ var runStrategyResearch = async (parameters) => {
4642
4705
  `06-raw-metrics-${variant.id}`,
4643
4706
  async (folder) => {
4644
4707
  const resolved = spec.frozenFrom ? (await run(spec, variant, `06-raw-revalidation-${variant.id}`)).variant : (await readJson(
4645
- import_node_path6.default.join(directory, `02-raw-${variant.id}`, "result.json")
4708
+ import_node_path7.default.join(directory, `02-raw-${variant.id}`, "result.json")
4646
4709
  )).variant;
4647
4710
  const dataset = await backend.loadDataset(
4648
4711
  spec,
@@ -4819,7 +4882,7 @@ var runStrategyResearch = async (parameters) => {
4819
4882
  for (const name of Object.keys(state.stages)) {
4820
4883
  try {
4821
4884
  const result = await readJson(
4822
- import_node_path6.default.join(directory, name, "result.json")
4885
+ import_node_path7.default.join(directory, name, "result.json")
4823
4886
  );
4824
4887
  if (result?.variant)
4825
4888
  executions.push({ stage: name, ...result.variant });
@@ -4827,7 +4890,7 @@ var runStrategyResearch = async (parameters) => {
4827
4890
  if (error.code !== "ENOENT") throw error;
4828
4891
  }
4829
4892
  }
4830
- await writeJsonAtomic(import_node_path6.default.join(directory, "result.json"), {
4893
+ await writeJsonAtomic(import_node_path7.default.join(directory, "result.json"), {
4831
4894
  ...conclusion,
4832
4895
  executions,
4833
4896
  rawCore: Object.entries(rawReports).map(([id, windows]) => ({
@@ -4841,7 +4904,7 @@ var runStrategyResearch = async (parameters) => {
4841
4904
  delete state.error;
4842
4905
  await save();
4843
4906
  await import_promises5.default.writeFile(
4844
- import_node_path6.default.join(directory, "report.html"),
4907
+ import_node_path7.default.join(directory, "report.html"),
4845
4908
  buildStrategyResearchReport({
4846
4909
  spec,
4847
4910
  state,
@@ -4905,7 +4968,7 @@ ${JSON.stringify(spec, null, 2)}
4905
4968
  ## Metrics snapshot (machine-readable)
4906
4969
 
4907
4970
  \`\`\`json
4908
- ${JSON.stringify(encodeResearchMetricSnapshot(compact))}
4971
+ ${JSON.stringify((0, import_researchMetricSnapshot2.encodeResearchMetricSnapshot)(compact))}
4909
4972
  \`\`\`
4910
4973
 
4911
4974
  ## Reported metrics
@@ -4929,7 +4992,7 @@ ${conclusion.nextAction} Current-snapshot universe; no deployment authorization.
4929
4992
  "Compact permanent note exceeds 8 MiB; archive source separately, never truncate metrics"
4930
4993
  );
4931
4994
  await import_promises5.default.writeFile(
4932
- import_node_path6.default.join(
4995
+ import_node_path7.default.join(
4933
4996
  noteDirectory,
4934
4997
  `${spec.core.createdAt.slice(0, 10)}-${spec.core.researchId}.md`
4935
4998
  ),
@@ -4938,7 +5001,7 @@ ${conclusion.nextAction} Current-snapshot universe; no deployment authorization.
4938
5001
  ).catch(async (error) => {
4939
5002
  if (error.code !== "EEXIST") throw error;
4940
5003
  const existing = await import_promises5.default.readFile(
4941
- import_node_path6.default.join(
5004
+ import_node_path7.default.join(
4942
5005
  noteDirectory,
4943
5006
  `${spec.core.createdAt.slice(0, 10)}-${spec.core.researchId}.md`
4944
5007
  ),
@@ -4950,19 +5013,19 @@ ${conclusion.nextAction} Current-snapshot universe; no deployment authorization.
4950
5013
  );
4951
5014
  });
4952
5015
  await writeJsonAtomic(
4953
- import_node_path6.default.join(directory, "retention.json"),
5016
+ import_node_path7.default.join(directory, "retention.json"),
4954
5017
  await createResearchRetentionInventory(
4955
5018
  projectRoot,
4956
5019
  directory,
4957
5020
  spec,
4958
5021
  state,
4959
- import_node_path6.default.join(
5022
+ import_node_path7.default.join(
4960
5023
  noteDirectory,
4961
5024
  `${spec.core.createdAt.slice(0, 10)}-${spec.core.researchId}.md`
4962
5025
  )
4963
5026
  )
4964
5027
  );
4965
- await writeJsonAtomic(import_node_path6.default.join(directory, "manifest.json"), {
5028
+ await writeJsonAtomic(import_node_path7.default.join(directory, "manifest.json"), {
4966
5029
  schema: "tradejs-strategy-research-manifest/v1",
4967
5030
  specSha256: state.specSha256,
4968
5031
  artifacts: Object.fromEntries(
@@ -4976,7 +5039,7 @@ ${conclusion.nextAction} Current-snapshot universe; no deployment authorization.
4976
5039
  "retention.json"
4977
5040
  ].map(async (file) => [
4978
5041
  file,
4979
- await sha256File(import_node_path6.default.join(directory, file))
5042
+ await sha256File(import_node_path7.default.join(directory, file))
4980
5043
  ])
4981
5044
  )
4982
5045
  )
@@ -4989,7 +5052,7 @@ ${conclusion.nextAction} Current-snapshot universe; no deployment authorization.
4989
5052
  );
4990
5053
  }
4991
5054
  parameters.onProgress?.(
4992
- `\u0418\u0442\u043E\u0433: ${conclusion.status}. \u041E\u0442\u0447\u0451\u0442: ${import_node_path6.default.join(directory, "report.html")}`
5055
+ `\u0418\u0442\u043E\u0433: ${conclusion.status}. \u041E\u0442\u0447\u0451\u0442: ${import_node_path7.default.join(directory, "report.html")}`
4993
5056
  );
4994
5057
  return { directory, state, conclusion };
4995
5058
  } catch (error) {
@@ -5010,9 +5073,9 @@ ${conclusion.nextAction} Current-snapshot universe; no deployment authorization.
5010
5073
  frozenGates: 0
5011
5074
  }
5012
5075
  };
5013
- await writeJsonAtomic(import_node_path6.default.join(directory, "result.json"), conclusion);
5076
+ await writeJsonAtomic(import_node_path7.default.join(directory, "result.json"), conclusion);
5014
5077
  await import_promises5.default.writeFile(
5015
- import_node_path6.default.join(directory, "report.html"),
5078
+ import_node_path7.default.join(directory, "report.html"),
5016
5079
  buildStrategyResearchReport({ spec, state, conclusion, diagnostics })
5017
5080
  );
5018
5081
  throw error;
@@ -5035,10 +5098,10 @@ var import_registry = require("@tradejs/node/registry");
5035
5098
  var import_connectors = require("@tradejs/node/connectors");
5036
5099
 
5037
5100
  // src/lib/coreResearch/dataset.ts
5038
- var import_node_crypto5 = require("crypto");
5039
- var import_node_fs2 = require("fs");
5040
- var import_node_path7 = __toESM(require("path"));
5041
- var import_node_readline = __toESM(require("readline"));
5101
+ var import_node_crypto4 = require("crypto");
5102
+ var import_node_fs3 = require("fs");
5103
+ var import_node_path8 = __toESM(require("path"));
5104
+ var import_node_readline2 = __toESM(require("readline"));
5042
5105
  var asRecord = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : null;
5043
5106
  var finiteNumber = (value, field) => {
5044
5107
  const numeric = Number(value);
@@ -5248,11 +5311,11 @@ var readCoreResearchVariant = async (variant) => {
5248
5311
  const seen = /* @__PURE__ */ new Map();
5249
5312
  let duplicateRowsDropped = 0;
5250
5313
  for (const inputPath of variant.files) {
5251
- const filePath = import_node_path7.default.resolve(inputPath);
5252
- const input = (0, import_node_fs2.createReadStream)(filePath);
5253
- const hash2 = (0, import_node_crypto5.createHash)("sha256");
5254
- input.on("data", (chunk) => hash2.update(chunk));
5255
- const lines = import_node_readline.default.createInterface({ input, crlfDelay: Infinity });
5314
+ const filePath = import_node_path8.default.resolve(inputPath);
5315
+ const input = (0, import_node_fs3.createReadStream)(filePath);
5316
+ const hash = (0, import_node_crypto4.createHash)("sha256");
5317
+ input.on("data", (chunk) => hash.update(chunk));
5318
+ const lines = import_node_readline2.default.createInterface({ input, crlfDelay: Infinity });
5256
5319
  const fileTrades = [];
5257
5320
  let lineNumber = 0;
5258
5321
  let rows = 0;
@@ -5301,7 +5364,7 @@ var readCoreResearchVariant = async (variant) => {
5301
5364
  trades.push(trade);
5302
5365
  fileTrades.push(trade);
5303
5366
  }
5304
- const fileSha2562 = hash2.digest("hex");
5367
+ const fileSha2562 = hash.digest("hex");
5305
5368
  for (const trade of fileTrades) trade.sourceSha256 = fileSha2562;
5306
5369
  sources.push({
5307
5370
  path: filePath,
@@ -5321,7 +5384,7 @@ var readCoreResearchVariant = async (variant) => {
5321
5384
  };
5322
5385
 
5323
5386
  // src/lib/backtest/checkpoint.ts
5324
- var import_node_crypto6 = require("crypto");
5387
+ var import_node_crypto5 = require("crypto");
5325
5388
  var import_constants = require("@tradejs/core/constants");
5326
5389
  var import_backtest2 = require("@tradejs/core/backtest");
5327
5390
  var import_redis = require("@tradejs/infra/redis");
@@ -5535,9 +5598,9 @@ init_io2();
5535
5598
 
5536
5599
  // src/lib/aiTrainResearch.ts
5537
5600
  var import_node_child_process2 = require("child_process");
5538
- var import_node_crypto8 = require("crypto");
5601
+ var import_node_crypto7 = require("crypto");
5539
5602
  var import_promises7 = __toESM(require("fs/promises"));
5540
- var import_node_path9 = __toESM(require("path"));
5603
+ var import_node_path10 = __toESM(require("path"));
5541
5604
 
5542
5605
  // src/lib/aiTrainMetrics.ts
5543
5606
  var DAY_MS3 = 24 * 60 * 60 * 1e3;
@@ -5547,10 +5610,10 @@ var import_strategies = require("@tradejs/node/strategies");
5547
5610
  var import_types2 = require("@tradejs/types");
5548
5611
 
5549
5612
  // src/lib/strategyGateFingerprint.ts
5550
- var import_node_crypto7 = require("crypto");
5613
+ var import_node_crypto6 = require("crypto");
5551
5614
  var import_node_module = require("module");
5552
5615
  var import_promises6 = __toESM(require("fs/promises"));
5553
- var import_node_path8 = __toESM(require("path"));
5616
+ var import_node_path9 = __toESM(require("path"));
5554
5617
 
5555
5618
  // src/lib/aiTrainResearch.ts
5556
5619
  var DAY_MS4 = 24 * 60 * 60 * 1e3;
@@ -5591,7 +5654,7 @@ var normalizeForStableJson = (value) => {
5591
5654
  }
5592
5655
  return value;
5593
5656
  };
5594
- var fingerprintResearchValue = (value) => (0, import_node_crypto8.createHash)("sha256").update(JSON.stringify(normalizeForStableJson(value))).digest("hex").slice(0, 16);
5657
+ var fingerprintResearchValue = (value) => (0, import_node_crypto7.createHash)("sha256").update(JSON.stringify(normalizeForStableJson(value))).digest("hex").slice(0, 16);
5595
5658
  var fingerprintResearchContextEnvironment = (env = process.env) => fingerprintResearchValue(
5596
5659
  Object.fromEntries(
5597
5660
  RESEARCH_CONTEXT_ENV_KEYS.map((key) => [key, env[key] ?? null])
@@ -5601,81 +5664,7 @@ var fingerprintResearchContextEnvironment = (env = process.env) => fingerprintRe
5601
5664
  // src/lib/strategyResearch/native.ts
5602
5665
  var import_data = require("@tradejs/core/data");
5603
5666
  var import_coreResearch = require("@tradejs/infra/coreResearch");
5604
-
5605
- // src/lib/coreResearch/trace.ts
5606
- var import_node_fs3 = require("fs");
5607
- var import_node_readline2 = __toESM(require("readline"));
5608
- var import_node_path10 = __toESM(require("path"));
5609
- var summarizeCoreResearchTrace = async (filePaths = []) => {
5610
- const events = /* @__PURE__ */ Object.create(null);
5611
- const skipCounts = /* @__PURE__ */ Object.create(null);
5612
- const exitCodes = /* @__PURE__ */ Object.create(null);
5613
- const policyRejections = {};
5614
- const policyExamples = [];
5615
- for (const inputPath of filePaths) {
5616
- const filePath = import_node_path10.default.resolve(inputPath);
5617
- const input = (0, import_node_fs3.createReadStream)(filePath);
5618
- const lines = import_node_readline2.default.createInterface({ input, crlfDelay: Infinity });
5619
- let index = -1;
5620
- try {
5621
- for await (const line of lines) {
5622
- index++;
5623
- if (!line.trim()) continue;
5624
- let event;
5625
- try {
5626
- event = JSON.parse(line);
5627
- } catch (error) {
5628
- throw new Error(
5629
- `${filePath}:${index + 1} contains invalid trace JSON: ${String(error)}`
5630
- );
5631
- }
5632
- events[event.event] = (events[event.event] ?? 0) + 1;
5633
- if (event.event === "skip_summary") {
5634
- for (const [reason, count] of Object.entries(event.skipCounts)) {
5635
- skipCounts[reason] = (skipCounts[reason] ?? 0) + count;
5636
- }
5637
- }
5638
- if (event.event === "position_exited" && event.exitCode) {
5639
- exitCodes[event.exitCode] = (exitCodes[event.exitCode] ?? 0) + 1;
5640
- }
5641
- if ("researchEntryDecision" in event && event.researchEntryDecision && !event.researchEntryDecision.allowed) {
5642
- const reason = event.researchEntryDecision.reason;
5643
- policyRejections[reason] = (policyRejections[reason] ?? 0) + 1;
5644
- if (policyExamples.length < 20)
5645
- policyExamples.push({
5646
- timestamp: event.timestamp,
5647
- symbol: event.symbol,
5648
- decision: event.researchEntryDecision
5649
- });
5650
- }
5651
- }
5652
- } finally {
5653
- lines.close();
5654
- input.destroy();
5655
- }
5656
- }
5657
- return {
5658
- events: Object.fromEntries(Object.entries(events).sort()),
5659
- skipCounts: Object.fromEntries(
5660
- Object.entries(skipCounts).sort(
5661
- ([leftKey, leftValue], [rightKey, rightValue]) => rightValue - leftValue || (leftKey < rightKey ? -1 : leftKey > rightKey ? 1 : 0)
5662
- )
5663
- ),
5664
- exitCodes: Object.fromEntries(
5665
- Object.entries(exitCodes).sort(
5666
- ([leftKey, leftValue], [rightKey, rightValue]) => rightValue - leftValue || (leftKey < rightKey ? -1 : leftKey > rightKey ? 1 : 0)
5667
- )
5668
- ),
5669
- ...policyExamples.length ? {
5670
- researchPolicy: {
5671
- rejections: policyRejections,
5672
- examples: policyExamples
5673
- }
5674
- } : {}
5675
- };
5676
- };
5677
-
5678
- // src/lib/strategyResearch/native.ts
5667
+ init_trace();
5679
5668
  var import_jev4 = require("@tradejs/core/jev");
5680
5669
 
5681
5670
  // src/lib/backtest/jevOptions.ts
@@ -6632,7 +6621,7 @@ init_io();
6632
6621
  var import_promises11 = __toESM(require("fs/promises"));
6633
6622
  var import_node_path15 = __toESM(require("path"));
6634
6623
  var import_node_child_process4 = require("child_process");
6635
- var import_node_crypto10 = require("crypto");
6624
+ var import_node_crypto9 = require("crypto");
6636
6625
  var import_coreResearch2 = require("@tradejs/infra/coreResearch");
6637
6626
 
6638
6627
  // src/lib/coreResearch/artifacts.ts
@@ -6646,9 +6635,10 @@ init_io();
6646
6635
  init_io();
6647
6636
 
6648
6637
  // src/lib/coreResearch/statistics.ts
6649
- var import_node_crypto9 = require("crypto");
6638
+ var import_node_crypto8 = require("crypto");
6650
6639
 
6651
6640
  // src/lib/coreResearch/orchestrator.ts
6641
+ init_trace();
6652
6642
  init_types();
6653
6643
  var createCoreResearchSpecTemplate = (params) => {
6654
6644
  const rootDir = import_node_path15.default.relative(
@@ -6684,7 +6674,7 @@ var createCoreResearchSpecTemplate = (params) => {
6684
6674
  gitSha,
6685
6675
  ...sourceRepository ? { sourceRepository } : {},
6686
6676
  ...dirtyDiff ? {
6687
- dirtyDiffSha256: (0, import_node_crypto10.createHash)("sha256").update(dirtyDiff).digest("hex")
6677
+ dirtyDiffSha256: (0, import_node_crypto9.createHash)("sha256").update(dirtyDiff).digest("hex")
6688
6678
  } : {}
6689
6679
  };
6690
6680
  } catch {