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

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, 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, 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}$/;
@@ -76,7 +169,7 @@ var init_io = __esm({
76
169
  sha256Json = (value) => sha256Text(canonicalJson(value));
77
170
  sha256File = async (filePath) => {
78
171
  const hash2 = (0, import_node_crypto.createHash)("sha256");
79
- const input = (0, import_node_fs.createReadStream)(filePath);
172
+ const input = (0, import_node_fs2.createReadStream)(filePath);
80
173
  for await (const chunk of input) hash2.update(chunk);
81
174
  return hash2.digest("hex");
82
175
  };
@@ -312,10 +405,26 @@ 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
  writeJsonAtomic = async (filePath, value) => {
318
- await import_promises.default.mkdir(import_node_path.default.dirname(filePath), { recursive: true });
427
+ await import_promises.default.mkdir(import_node_path2.default.dirname(filePath), { recursive: true });
319
428
  const temporaryPath = `${filePath}.${process.pid}.${Date.now()}.tmp`;
320
429
  await import_promises.default.writeFile(temporaryPath, `${canonicalJson(value)}
321
430
  `, "utf8");
@@ -336,30 +445,30 @@ __export(io_exports, {
336
445
  verifyResearchState: () => verifyResearchState,
337
446
  withResearchLock: () => withResearchLock
338
447
  });
339
- var import_promises2, import_node_path2, import_node_os, import_node_crypto3, researchDirectory, readJson, boundResearchPath, withResearchLock, verifyResearchState, verifyCompletedResearch, initializeResearchState, isRetainedDeletion;
448
+ var import_promises2, import_node_path3, import_node_os, import_node_crypto3, researchDirectory, readJson, boundResearchPath, withResearchLock, verifyResearchState, verifyCompletedResearch, initializeResearchState, isRetainedDeletion;
340
449
  var init_io2 = __esm({
341
450
  "src/lib/strategyResearch/io.ts"() {
342
451
  "use strict";
343
452
  import_promises2 = __toESM(require("fs/promises"));
344
- import_node_path2 = __toESM(require("path"));
453
+ import_node_path3 = __toESM(require("path"));
345
454
  import_node_os = __toESM(require("os"));
346
455
  import_node_crypto3 = require("crypto");
347
456
  init_io();
348
- researchDirectory = (projectRoot, spec) => import_node_path2.default.join(projectRoot, "data/research/strategy", spec.core.researchId);
457
+ researchDirectory = (projectRoot, spec) => import_node_path3.default.join(projectRoot, "data/research/strategy", spec.core.researchId);
349
458
  readJson = async (file) => JSON.parse(await import_promises2.default.readFile(file, "utf8"));
350
459
  boundResearchPath = async (root, relative) => {
351
- const target = import_node_path2.default.resolve(root, relative);
460
+ const target = import_node_path3.default.resolve(root, relative);
352
461
  const realRoot = await import_promises2.default.realpath(root);
353
462
  let ancestor = target;
354
463
  while (true) {
355
464
  try {
356
465
  const real = await import_promises2.default.realpath(ancestor);
357
- if (real !== realRoot && !real.startsWith(`${realRoot}${import_node_path2.default.sep}`))
466
+ if (real !== realRoot && !real.startsWith(`${realRoot}${import_node_path3.default.sep}`))
358
467
  throw new Error(`Research path escapes Project: ${relative}`);
359
468
  break;
360
469
  } catch (error) {
361
470
  if (error.code !== "ENOENT") throw error;
362
- const parent = import_node_path2.default.dirname(ancestor);
471
+ const parent = import_node_path3.default.dirname(ancestor);
363
472
  if (parent === ancestor) throw error;
364
473
  ancestor = parent;
365
474
  }
@@ -371,7 +480,7 @@ var init_io2 = __esm({
371
480
  projectRoot,
372
481
  "data/research/strategy/.lock"
373
482
  );
374
- await import_promises2.default.mkdir(import_node_path2.default.dirname(file), { recursive: true });
483
+ await import_promises2.default.mkdir(import_node_path3.default.dirname(file), { recursive: true });
375
484
  const token = (0, import_node_crypto3.randomUUID)();
376
485
  const acquire = () => import_promises2.default.writeFile(
377
486
  file,
@@ -405,7 +514,7 @@ var init_io2 = __esm({
405
514
  }
406
515
  };
407
516
  verifyResearchState = async (directory, state) => {
408
- const frozenSpec = await readJson(import_node_path2.default.join(directory, "spec.json"));
517
+ const frozenSpec = await readJson(import_node_path3.default.join(directory, "spec.json"));
409
518
  if (sha256Json(frozenSpec) !== state.specSha256)
410
519
  throw new Error("Frozen research spec changed");
411
520
  for (const [name, stage] of Object.entries(state.stages)) {
@@ -424,14 +533,14 @@ var init_io2 = __esm({
424
533
  };
425
534
  verifyCompletedResearch = async (directory, state) => {
426
535
  await verifyResearchState(directory, state);
427
- const manifest = await readJson(import_node_path2.default.join(directory, "manifest.json"));
536
+ const manifest = await readJson(import_node_path3.default.join(directory, "manifest.json"));
428
537
  if (manifest.specSha256 !== state.specSha256)
429
538
  throw new Error("Final manifest spec mismatch");
430
539
  for (const [relative, expected] of Object.entries(manifest.artifacts))
431
540
  if (await sha256File(await boundResearchPath(directory, relative)) !== expected)
432
541
  throw new Error(`Changed final research artifact: ${relative}`);
433
- const inventory = await readJson(import_node_path2.default.join(directory, "retention.json"));
434
- const projectRoot = import_node_path2.default.resolve(directory, "../../../..");
542
+ const inventory = await readJson(import_node_path3.default.join(directory, "retention.json"));
543
+ const projectRoot = import_node_path3.default.resolve(directory, "../../../..");
435
544
  for (const artifact of inventory.research.permanentArtifacts) {
436
545
  const file = await boundResearchPath(projectRoot, artifact.path);
437
546
  if (await sha256File(file) !== artifact.sha256)
@@ -440,7 +549,7 @@ var init_io2 = __esm({
440
549
  };
441
550
  initializeResearchState = async (directory, spec, lineage) => {
442
551
  await import_promises2.default.mkdir(directory, { recursive: true });
443
- const file = import_node_path2.default.join(directory, "state.json");
552
+ const file = import_node_path3.default.join(directory, "state.json");
444
553
  let previous;
445
554
  try {
446
555
  previous = await readJson(file);
@@ -464,18 +573,18 @@ var init_io2 = __esm({
464
573
  status: "running",
465
574
  stages: {}
466
575
  };
467
- await writeJsonAtomic(import_node_path2.default.join(directory, "spec.json"), spec);
576
+ await writeJsonAtomic(import_node_path3.default.join(directory, "spec.json"), spec);
468
577
  await writeJsonAtomic(file, state);
469
578
  return state;
470
579
  };
471
580
  isRetainedDeletion = async (directory, file, hash2) => {
472
581
  try {
473
- const inventory = await readJson(import_node_path2.default.join(directory, "retention.json"));
582
+ const inventory = await readJson(import_node_path3.default.join(directory, "retention.json"));
474
583
  const index = inventory.entries.findIndex(
475
584
  (entry) => entry.path === file && entry.sha256 === hash2
476
585
  );
477
586
  if (index < 0) return false;
478
- const receipt = await readJson(import_node_path2.default.join(directory, "retention", `${index}.json`));
587
+ const receipt = await readJson(import_node_path3.default.join(directory, "retention", `${index}.json`));
479
588
  return ["applying", "completed"].includes(receipt.status) && receipt.files[0]?.path === file && receipt.files[0]?.sha256 === hash2;
480
589
  } catch (error) {
481
590
  if (error.code === "ENOENT") return false;
@@ -2563,7 +2672,7 @@ var decodeResearchMetricSnapshot = (snapshot) => {
2563
2672
  };
2564
2673
 
2565
2674
  // src/lib/strategyResearch/orchestrator.ts
2566
- var import_node_path6 = __toESM(require("path"));
2675
+ var import_node_path7 = __toESM(require("path"));
2567
2676
 
2568
2677
  // src/lib/coreResearch/metrics.ts
2569
2678
  var DAY_MS2 = 24 * 60 * 60 * 1e3;
@@ -3764,16 +3873,16 @@ var testCharts = (conclusion) => {
3764
3873
 
3765
3874
  // src/lib/strategyResearch/retention.ts
3766
3875
  var import_promises4 = __toESM(require("fs/promises"));
3767
- var import_node_path5 = __toESM(require("path"));
3876
+ var import_node_path6 = __toESM(require("path"));
3768
3877
 
3769
3878
  // src/lib/strategyRelease/researchRetention.ts
3770
3879
  var import_promises3 = __toESM(require("fs/promises"));
3771
- var import_node_path4 = __toESM(require("path"));
3880
+ var import_node_path5 = __toESM(require("path"));
3772
3881
  var import_node_crypto4 = require("crypto");
3773
3882
  var import_strategyReleaseEvidence = require("@tradejs/infra/strategyReleaseEvidence");
3774
3883
 
3775
3884
  // src/lib/strategyRelease/researchFileConsumers.ts
3776
- var import_node_path3 = __toESM(require("path"));
3885
+ var import_node_path4 = __toESM(require("path"));
3777
3886
  var import_node_child_process = require("child_process");
3778
3887
  var import_node_util = require("util");
3779
3888
  var execFileAsync = (0, import_node_util.promisify)(import_node_child_process.execFile);
@@ -3804,15 +3913,15 @@ print(json.dumps(sorted(opened)))
3804
3913
  `;
3805
3914
  var assertNoResearchFileConsumers = async (files, run = runCommand) => {
3806
3915
  const virtualPids = /* @__PURE__ */ new Set();
3807
- const wanted = new Set(files.map((file) => import_node_path3.default.resolve(file)));
3808
- let commonRoot = files.length ? import_node_path3.default.dirname(files[0]) : "";
3916
+ const wanted = new Set(files.map((file) => import_node_path4.default.resolve(file)));
3917
+ let commonRoot = files.length ? import_node_path4.default.dirname(files[0]) : "";
3809
3918
  while (commonRoot && files.some((file) => {
3810
- const relative = import_node_path3.default.relative(commonRoot, file);
3811
- return relative === ".." || relative.startsWith(`..${import_node_path3.default.sep}`) || import_node_path3.default.isAbsolute(relative);
3919
+ const relative = import_node_path4.default.relative(commonRoot, file);
3920
+ return relative === ".." || relative.startsWith(`..${import_node_path4.default.sep}`) || import_node_path4.default.isAbsolute(relative);
3812
3921
  }))
3813
- commonRoot = import_node_path3.default.dirname(commonRoot);
3922
+ commonRoot = import_node_path4.default.dirname(commonRoot);
3814
3923
  const selectors = [];
3815
- if (files.length > 1e3 && commonRoot !== import_node_path3.default.parse(commonRoot).root) {
3924
+ if (files.length > 1e3 && commonRoot !== import_node_path4.default.parse(commonRoot).root) {
3816
3925
  selectors.push(["+D", commonRoot]);
3817
3926
  } else {
3818
3927
  for (let offset = 0; offset < files.length; offset += 100)
@@ -3836,7 +3945,7 @@ var assertNoResearchFileConsumers = async (files, run = runCommand) => {
3836
3945
  if (line.startsWith("c"))
3837
3946
  virtual = line.startsWith("ccom.apple.Virtualization.");
3838
3947
  if (line.startsWith("n")) {
3839
- if (!wanted.has(import_node_path3.default.resolve(line.slice(1)))) continue;
3948
+ if (!wanted.has(import_node_path4.default.resolve(line.slice(1)))) continue;
3840
3949
  if (!virtual || !/^\d+$/.test(pid))
3841
3950
  throw new Error(
3842
3951
  `Research cleanup found open files: ${line.slice(1)}`
@@ -3870,9 +3979,9 @@ var assertNoResearchFileConsumers = async (files, run = runCommand) => {
3870
3979
  for (const mount of container.Mounts) {
3871
3980
  if (mount.Type !== "bind") continue;
3872
3981
  for (const file of files) {
3873
- const relative = import_node_path3.default.relative(mount.Source, file);
3874
- if (relative === "" || !relative.startsWith(`..${import_node_path3.default.sep}`) && relative !== ".." && !import_node_path3.default.isAbsolute(relative)) {
3875
- guestFiles.add(import_node_path3.default.posix.join(mount.Destination, relative));
3982
+ const relative = import_node_path4.default.relative(mount.Source, file);
3983
+ if (relative === "" || !relative.startsWith(`..${import_node_path4.default.sep}`) && relative !== ".." && !import_node_path4.default.isAbsolute(relative)) {
3984
+ guestFiles.add(import_node_path4.default.posix.join(mount.Destination, relative));
3876
3985
  covered.add(file);
3877
3986
  }
3878
3987
  }
@@ -3920,13 +4029,13 @@ var mapFiles = async (entries, visit) => {
3920
4029
  };
3921
4030
  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);
3922
4031
  var inside = (root, file) => {
3923
- const relative = import_node_path4.default.relative(root, file);
3924
- return relative !== "" && !relative.startsWith(`..${import_node_path4.default.sep}`) && relative !== ".." && !import_node_path4.default.isAbsolute(relative);
4032
+ const relative = import_node_path5.default.relative(root, file);
4033
+ return relative !== "" && !relative.startsWith(`..${import_node_path5.default.sep}`) && relative !== ".." && !import_node_path5.default.isAbsolute(relative);
3925
4034
  };
3926
4035
  var applyResearchRetention = async (options) => {
3927
4036
  const projectRoot = await import_promises3.default.realpath(options.projectRoot);
3928
- const dataRoot = import_node_path4.default.join(projectRoot, "data");
3929
- const resolve = (file) => import_node_path4.default.resolve(projectRoot, file);
4037
+ const dataRoot = import_node_path5.default.join(projectRoot, "data");
4038
+ const resolve = (file) => import_node_path5.default.resolve(projectRoot, file);
3930
4039
  const receiptPath = resolve(options.receiptPath);
3931
4040
  const journalPath = `${receiptPath}.deleted.jsonl`;
3932
4041
  const permanent = new Set(
@@ -4025,8 +4134,8 @@ var applyResearchRetention = async (options) => {
4025
4134
  }))
4026
4135
  };
4027
4136
  if (!options.apply) return receipt;
4028
- await import_promises3.default.mkdir(import_node_path4.default.dirname(receiptPath), { recursive: true });
4029
- if (await import_promises3.default.realpath(import_node_path4.default.dirname(receiptPath)) !== import_node_path4.default.dirname(receiptPath)) {
4137
+ await import_promises3.default.mkdir(import_node_path5.default.dirname(receiptPath), { recursive: true });
4138
+ if (await import_promises3.default.realpath(import_node_path5.default.dirname(receiptPath)) !== import_node_path5.default.dirname(receiptPath)) {
4030
4139
  throw new Error("Cleanup receipt refuses symlinked directories");
4031
4140
  }
4032
4141
  const lock = await import_promises3.default.open(`${receiptPath}.lock`, "wx");
@@ -4072,17 +4181,17 @@ var createResearchRetentionInventory = async (projectRoot, directory, spec, stat
4072
4181
  const permanentArtifacts = [];
4073
4182
  for (const [role, file] of [
4074
4183
  ["note", notePath],
4075
- ["metrics", import_node_path5.default.join(directory, "result.json")],
4076
- ["candidates", import_node_path5.default.join(directory, "03-freeze-gates/result.json")],
4077
- ["candidates", import_node_path5.default.join(directory, "resolved-spec.json")],
4078
- ["ledger", import_node_path5.default.join(directory, "05-freeze-primary/result.json")],
4079
- ["chart", import_node_path5.default.join(directory, "report.html")],
4184
+ ["metrics", import_node_path6.default.join(directory, "result.json")],
4185
+ ["candidates", import_node_path6.default.join(directory, "03-freeze-gates/result.json")],
4186
+ ["candidates", import_node_path6.default.join(directory, "resolved-spec.json")],
4187
+ ["ledger", import_node_path6.default.join(directory, "05-freeze-primary/result.json")],
4188
+ ["chart", import_node_path6.default.join(directory, "report.html")],
4080
4189
  ...[
4081
4190
  "source.patch",
4082
4191
  "source-baseline.tar",
4083
4192
  "source-untracked.json",
4084
4193
  "source-jev-models.json"
4085
- ].map((file2) => ["source", import_node_path5.default.join(directory, file2)])
4194
+ ].map((file2) => ["source", import_node_path6.default.join(directory, file2)])
4086
4195
  ]) {
4087
4196
  try {
4088
4197
  permanentArtifacts.push({
@@ -4142,13 +4251,13 @@ var createResearchRetentionInventory = async (projectRoot, directory, spec, stat
4142
4251
  };
4143
4252
  var applyRetentionInventory = async (projectRoot, directory) => {
4144
4253
  const inventory = await readJson(
4145
- import_node_path5.default.join(directory, "retention.json")
4254
+ import_node_path6.default.join(directory, "retention.json")
4146
4255
  );
4147
- const receipts = import_node_path5.default.join(directory, "retention");
4256
+ const receipts = import_node_path6.default.join(directory, "retention");
4148
4257
  await import_promises4.default.mkdir(receipts, { recursive: true });
4149
4258
  for (let index = 0; index < inventory.entries.length; index++) {
4150
4259
  const entry = inventory.entries[index];
4151
- const receiptPath = import_node_path5.default.join(receipts, `${index}.json`);
4260
+ const receiptPath = import_node_path6.default.join(receipts, `${index}.json`);
4152
4261
  let receipt;
4153
4262
  try {
4154
4263
  receipt = await readJson(receiptPath);
@@ -4184,13 +4293,13 @@ var applyRetentionInventory = async (projectRoot, directory) => {
4184
4293
  entries: [entry],
4185
4294
  receiptPath,
4186
4295
  apply: true,
4187
- protectedPaths: [import_node_path5.default.join(directory, "retention.json")]
4296
+ protectedPaths: [import_node_path6.default.join(directory, "retention.json")]
4188
4297
  });
4189
4298
  }
4190
4299
  return { status: "completed", files: inventory.entries.length };
4191
4300
  };
4192
4301
  var finishResearchRetention = async (projectRoot, directory) => {
4193
- const statusPath = import_node_path5.default.join(directory, "retention-status.json");
4302
+ const statusPath = import_node_path6.default.join(directory, "retention-status.json");
4194
4303
  try {
4195
4304
  const result = await applyRetentionInventory(projectRoot, directory);
4196
4305
  await writeJsonAtomic(statusPath, result);
@@ -4217,7 +4326,7 @@ var runStrategyResearch = async (parameters) => {
4217
4326
  const directory = researchDirectory(projectRoot, spec);
4218
4327
  const lineage = await backend.lineage();
4219
4328
  const state = await initializeResearchState(directory, spec, lineage);
4220
- const statePath = import_node_path6.default.join(directory, "state.json");
4329
+ const statePath = import_node_path7.default.join(directory, "state.json");
4221
4330
  const elapsedBefore = state.elapsedMs ?? 0;
4222
4331
  const activeStart = Date.now();
4223
4332
  const elapsed = () => elapsedBefore + Date.now() - activeStart;
@@ -4227,7 +4336,7 @@ var runStrategyResearch = async (parameters) => {
4227
4336
  directory,
4228
4337
  state,
4229
4338
  conclusion: await readJson(
4230
- import_node_path6.default.join(directory, "result.json")
4339
+ import_node_path7.default.join(directory, "result.json")
4231
4340
  )
4232
4341
  };
4233
4342
  }
@@ -4237,13 +4346,13 @@ var runStrategyResearch = async (parameters) => {
4237
4346
  return writeJsonAtomic(statePath, state);
4238
4347
  };
4239
4348
  const event = async (stage2, status) => import_promises5.default.appendFile(
4240
- import_node_path6.default.join(directory, "events.jsonl"),
4349
+ import_node_path7.default.join(directory, "events.jsonl"),
4241
4350
  `${JSON.stringify({ at: (/* @__PURE__ */ new Date()).toISOString(), stage: stage2, status, specSha256: state.specSha256 })}
4242
4351
  `
4243
4352
  );
4244
4353
  const stage = async (name, work) => {
4245
4354
  const stageDirectory = await boundResearchPath(directory, name);
4246
- const resultPath = import_node_path6.default.join(stageDirectory, "result.json");
4355
+ const resultPath = import_node_path7.default.join(stageDirectory, "result.json");
4247
4356
  if (state.stages[name]?.status === "completed")
4248
4357
  return readJson(resultPath);
4249
4358
  if (elapsed() > spec.budget.maxMinutes * 6e4)
@@ -4262,7 +4371,7 @@ var runStrategyResearch = async (parameters) => {
4262
4371
  await save();
4263
4372
  await event(name, "started");
4264
4373
  await import_promises5.default.writeFile(
4265
- import_node_path6.default.join(directory, "report.html"),
4374
+ import_node_path7.default.join(directory, "report.html"),
4266
4375
  buildStrategyResearchReport({ spec, state, diagnostics })
4267
4376
  );
4268
4377
  try {
@@ -4270,7 +4379,7 @@ var runStrategyResearch = async (parameters) => {
4270
4379
  await writeJsonAtomic(resultPath, result);
4271
4380
  const artifacts = {};
4272
4381
  for (const file of await import_promises5.default.readdir(stageDirectory)) {
4273
- const full = import_node_path6.default.join(stageDirectory, file);
4382
+ const full = import_node_path7.default.join(stageDirectory, file);
4274
4383
  if ((await import_promises5.default.stat(full)).isFile())
4275
4384
  artifacts[`${name}/${file}`] = await sha256File(full);
4276
4385
  }
@@ -4282,7 +4391,7 @@ var runStrategyResearch = async (parameters) => {
4282
4391
  "source-jev-models.json"
4283
4392
  ]) {
4284
4393
  try {
4285
- artifacts[file] = await sha256File(import_node_path6.default.join(directory, file));
4394
+ artifacts[file] = await sha256File(import_node_path7.default.join(directory, file));
4286
4395
  } catch (error) {
4287
4396
  if (error.code !== "ENOENT")
4288
4397
  throw error;
@@ -4315,14 +4424,14 @@ var runStrategyResearch = async (parameters) => {
4315
4424
  let previousRunId;
4316
4425
  try {
4317
4426
  previousRunId = (await readJson(
4318
- import_node_path6.default.join(folder, "native-run.json")
4427
+ import_node_path7.default.join(folder, "native-run.json")
4319
4428
  )).runId;
4320
4429
  } catch (error) {
4321
4430
  if (error.code !== "ENOENT") throw error;
4322
4431
  }
4323
4432
  if (!previousRunId) {
4324
4433
  try {
4325
- if ((await import_promises5.default.stat(import_node_path6.default.join(folder, "backtest.log"))).size)
4434
+ if ((await import_promises5.default.stat(import_node_path7.default.join(folder, "backtest.log"))).size)
4326
4435
  throw new Error(
4327
4436
  "Previous native attempt has no recorded run identity; inspect its log before another launch"
4328
4437
  );
@@ -4343,7 +4452,7 @@ var runStrategyResearch = async (parameters) => {
4343
4452
  },
4344
4453
  variant,
4345
4454
  folder,
4346
- async (runId) => writeJsonAtomic(import_node_path6.default.join(folder, "native-run.json"), { runId }),
4455
+ async (runId) => writeJsonAtomic(import_node_path7.default.join(folder, "native-run.json"), { runId }),
4347
4456
  previousRunId,
4348
4457
  confirmation
4349
4458
  );
@@ -4358,15 +4467,15 @@ var runStrategyResearch = async (parameters) => {
4358
4467
  try {
4359
4468
  const inventory = await stage("01-preflight", async () => {
4360
4469
  const prior = [];
4361
- const root = import_node_path6.default.dirname(directory);
4470
+ const root = import_node_path7.default.dirname(directory);
4362
4471
  for (const id of await import_promises5.default.readdir(root)) {
4363
4472
  if (id === spec.core.researchId || id.startsWith(".")) continue;
4364
4473
  try {
4365
4474
  const previous = await readJson(
4366
- import_node_path6.default.join(root, id, "spec.json")
4475
+ import_node_path7.default.join(root, id, "spec.json")
4367
4476
  );
4368
4477
  const previousState = await readJson(
4369
- import_node_path6.default.join(root, id, "state.json")
4478
+ import_node_path7.default.join(root, id, "state.json")
4370
4479
  );
4371
4480
  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);
4372
4481
  if (overlap && previousState.holdoutOpenedAt)
@@ -4392,7 +4501,7 @@ var runStrategyResearch = async (parameters) => {
4392
4501
  let previous;
4393
4502
  try {
4394
4503
  previous = (await readJson(
4395
- import_node_path6.default.join(folder, "native-run.json")
4504
+ import_node_path7.default.join(folder, "native-run.json")
4396
4505
  )).runId;
4397
4506
  } catch (error) {
4398
4507
  if (error.code !== "ENOENT")
@@ -4402,7 +4511,7 @@ var runStrategyResearch = async (parameters) => {
4402
4511
  spec,
4403
4512
  control2,
4404
4513
  folder,
4405
- async (runId) => writeJsonAtomic(import_node_path6.default.join(folder, "native-run.json"), {
4514
+ async (runId) => writeJsonAtomic(import_node_path7.default.join(folder, "native-run.json"), {
4406
4515
  runId
4407
4516
  }),
4408
4517
  previous
@@ -4416,7 +4525,7 @@ var runStrategyResearch = async (parameters) => {
4416
4525
  );
4417
4526
  spec = { ...spec, testSince: partition.testSince };
4418
4527
  }
4419
- await writeJsonAtomic(import_node_path6.default.join(directory, "resolved-spec.json"), spec);
4528
+ await writeJsonAtomic(import_node_path7.default.join(directory, "resolved-spec.json"), spec);
4420
4529
  let gates = [];
4421
4530
  let primaryId = null;
4422
4531
  let frozenSelection;
@@ -4475,10 +4584,10 @@ var runStrategyResearch = async (parameters) => {
4475
4584
  core: { ...spec.core, researchId: spec.frozenFrom }
4476
4585
  });
4477
4586
  const parentState = await readJson(
4478
- import_node_path6.default.join(parentDirectory, "state.json")
4587
+ import_node_path7.default.join(parentDirectory, "state.json")
4479
4588
  );
4480
4589
  const parentSpec = await readJson(
4481
- import_node_path6.default.join(parentDirectory, "spec.json")
4590
+ import_node_path7.default.join(parentDirectory, "spec.json")
4482
4591
  );
4483
4592
  const { verifyCompletedResearch: verifyCompletedResearch2 } = await Promise.resolve().then(() => (init_io2(), io_exports));
4484
4593
  await verifyCompletedResearch2(parentDirectory, parentState);
@@ -4505,10 +4614,10 @@ var runStrategyResearch = async (parameters) => {
4505
4614
  "Frozen revalidation requires complete, unchanged rules/runtime and a strictly new period"
4506
4615
  );
4507
4616
  gates = await readJson(
4508
- import_node_path6.default.join(parentDirectory, "03-freeze-gates/result.json")
4617
+ import_node_path7.default.join(parentDirectory, "03-freeze-gates/result.json")
4509
4618
  );
4510
4619
  frozenSelection = await readJson(
4511
- import_node_path6.default.join(parentDirectory, "05-freeze-primary/result.json")
4620
+ import_node_path7.default.join(parentDirectory, "05-freeze-primary/result.json")
4512
4621
  );
4513
4622
  if (frozenSelection.schema !== "tradejs-research-selection/v1" || !Array.isArray(frozenSelection.compositions))
4514
4623
  throw new Error(
@@ -4637,7 +4746,7 @@ var runStrategyResearch = async (parameters) => {
4637
4746
  `06-raw-metrics-${variant.id}`,
4638
4747
  async (folder) => {
4639
4748
  const resolved = spec.frozenFrom ? (await run(spec, variant, `06-raw-revalidation-${variant.id}`)).variant : (await readJson(
4640
- import_node_path6.default.join(directory, `02-raw-${variant.id}`, "result.json")
4749
+ import_node_path7.default.join(directory, `02-raw-${variant.id}`, "result.json")
4641
4750
  )).variant;
4642
4751
  const dataset = await backend.loadDataset(
4643
4752
  spec,
@@ -4814,7 +4923,7 @@ var runStrategyResearch = async (parameters) => {
4814
4923
  for (const name of Object.keys(state.stages)) {
4815
4924
  try {
4816
4925
  const result = await readJson(
4817
- import_node_path6.default.join(directory, name, "result.json")
4926
+ import_node_path7.default.join(directory, name, "result.json")
4818
4927
  );
4819
4928
  if (result?.variant)
4820
4929
  executions.push({ stage: name, ...result.variant });
@@ -4822,7 +4931,7 @@ var runStrategyResearch = async (parameters) => {
4822
4931
  if (error.code !== "ENOENT") throw error;
4823
4932
  }
4824
4933
  }
4825
- await writeJsonAtomic(import_node_path6.default.join(directory, "result.json"), {
4934
+ await writeJsonAtomic(import_node_path7.default.join(directory, "result.json"), {
4826
4935
  ...conclusion,
4827
4936
  executions,
4828
4937
  rawCore: Object.entries(rawReports).map(([id, windows]) => ({
@@ -4836,7 +4945,7 @@ var runStrategyResearch = async (parameters) => {
4836
4945
  delete state.error;
4837
4946
  await save();
4838
4947
  await import_promises5.default.writeFile(
4839
- import_node_path6.default.join(directory, "report.html"),
4948
+ import_node_path7.default.join(directory, "report.html"),
4840
4949
  buildStrategyResearchReport({
4841
4950
  spec,
4842
4951
  state,
@@ -4924,7 +5033,7 @@ ${conclusion.nextAction} Current-snapshot universe; no deployment authorization.
4924
5033
  "Compact permanent note exceeds 8 MiB; archive source separately, never truncate metrics"
4925
5034
  );
4926
5035
  await import_promises5.default.writeFile(
4927
- import_node_path6.default.join(
5036
+ import_node_path7.default.join(
4928
5037
  noteDirectory,
4929
5038
  `${spec.core.createdAt.slice(0, 10)}-${spec.core.researchId}.md`
4930
5039
  ),
@@ -4933,7 +5042,7 @@ ${conclusion.nextAction} Current-snapshot universe; no deployment authorization.
4933
5042
  ).catch(async (error) => {
4934
5043
  if (error.code !== "EEXIST") throw error;
4935
5044
  const existing = await import_promises5.default.readFile(
4936
- import_node_path6.default.join(
5045
+ import_node_path7.default.join(
4937
5046
  noteDirectory,
4938
5047
  `${spec.core.createdAt.slice(0, 10)}-${spec.core.researchId}.md`
4939
5048
  ),
@@ -4945,19 +5054,19 @@ ${conclusion.nextAction} Current-snapshot universe; no deployment authorization.
4945
5054
  );
4946
5055
  });
4947
5056
  await writeJsonAtomic(
4948
- import_node_path6.default.join(directory, "retention.json"),
5057
+ import_node_path7.default.join(directory, "retention.json"),
4949
5058
  await createResearchRetentionInventory(
4950
5059
  projectRoot,
4951
5060
  directory,
4952
5061
  spec,
4953
5062
  state,
4954
- import_node_path6.default.join(
5063
+ import_node_path7.default.join(
4955
5064
  noteDirectory,
4956
5065
  `${spec.core.createdAt.slice(0, 10)}-${spec.core.researchId}.md`
4957
5066
  )
4958
5067
  )
4959
5068
  );
4960
- await writeJsonAtomic(import_node_path6.default.join(directory, "manifest.json"), {
5069
+ await writeJsonAtomic(import_node_path7.default.join(directory, "manifest.json"), {
4961
5070
  schema: "tradejs-strategy-research-manifest/v1",
4962
5071
  specSha256: state.specSha256,
4963
5072
  artifacts: Object.fromEntries(
@@ -4971,7 +5080,7 @@ ${conclusion.nextAction} Current-snapshot universe; no deployment authorization.
4971
5080
  "retention.json"
4972
5081
  ].map(async (file) => [
4973
5082
  file,
4974
- await sha256File(import_node_path6.default.join(directory, file))
5083
+ await sha256File(import_node_path7.default.join(directory, file))
4975
5084
  ])
4976
5085
  )
4977
5086
  )
@@ -4984,7 +5093,7 @@ ${conclusion.nextAction} Current-snapshot universe; no deployment authorization.
4984
5093
  );
4985
5094
  }
4986
5095
  parameters.onProgress?.(
4987
- `\u0418\u0442\u043E\u0433: ${conclusion.status}. \u041E\u0442\u0447\u0451\u0442: ${import_node_path6.default.join(directory, "report.html")}`
5096
+ `\u0418\u0442\u043E\u0433: ${conclusion.status}. \u041E\u0442\u0447\u0451\u0442: ${import_node_path7.default.join(directory, "report.html")}`
4988
5097
  );
4989
5098
  return { directory, state, conclusion };
4990
5099
  } catch (error) {
@@ -5005,9 +5114,9 @@ ${conclusion.nextAction} Current-snapshot universe; no deployment authorization.
5005
5114
  frozenGates: 0
5006
5115
  }
5007
5116
  };
5008
- await writeJsonAtomic(import_node_path6.default.join(directory, "result.json"), conclusion);
5117
+ await writeJsonAtomic(import_node_path7.default.join(directory, "result.json"), conclusion);
5009
5118
  await import_promises5.default.writeFile(
5010
- import_node_path6.default.join(directory, "report.html"),
5119
+ import_node_path7.default.join(directory, "report.html"),
5011
5120
  buildStrategyResearchReport({ spec, state, conclusion, diagnostics })
5012
5121
  );
5013
5122
  throw error;
@@ -5031,9 +5140,9 @@ var import_connectors = require("@tradejs/node/connectors");
5031
5140
 
5032
5141
  // src/lib/coreResearch/dataset.ts
5033
5142
  var import_node_crypto5 = require("crypto");
5034
- var import_node_fs2 = require("fs");
5035
- var import_node_path7 = __toESM(require("path"));
5036
- var import_node_readline = __toESM(require("readline"));
5143
+ var import_node_fs3 = require("fs");
5144
+ var import_node_path8 = __toESM(require("path"));
5145
+ var import_node_readline2 = __toESM(require("readline"));
5037
5146
  var asRecord = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : null;
5038
5147
  var finiteNumber = (value, field) => {
5039
5148
  const numeric = Number(value);
@@ -5243,11 +5352,11 @@ var readCoreResearchVariant = async (variant) => {
5243
5352
  const seen = /* @__PURE__ */ new Map();
5244
5353
  let duplicateRowsDropped = 0;
5245
5354
  for (const inputPath of variant.files) {
5246
- const filePath = import_node_path7.default.resolve(inputPath);
5247
- const input = (0, import_node_fs2.createReadStream)(filePath);
5355
+ const filePath = import_node_path8.default.resolve(inputPath);
5356
+ const input = (0, import_node_fs3.createReadStream)(filePath);
5248
5357
  const hash2 = (0, import_node_crypto5.createHash)("sha256");
5249
5358
  input.on("data", (chunk) => hash2.update(chunk));
5250
- const lines = import_node_readline.default.createInterface({ input, crlfDelay: Infinity });
5359
+ const lines = import_node_readline2.default.createInterface({ input, crlfDelay: Infinity });
5251
5360
  const fileTrades = [];
5252
5361
  let lineNumber = 0;
5253
5362
  let rows = 0;
@@ -5532,7 +5641,7 @@ init_io2();
5532
5641
  var import_node_child_process2 = require("child_process");
5533
5642
  var import_node_crypto8 = require("crypto");
5534
5643
  var import_promises7 = __toESM(require("fs/promises"));
5535
- var import_node_path9 = __toESM(require("path"));
5644
+ var import_node_path10 = __toESM(require("path"));
5536
5645
 
5537
5646
  // src/lib/aiTrainMetrics.ts
5538
5647
  var DAY_MS3 = 24 * 60 * 60 * 1e3;
@@ -5545,7 +5654,7 @@ var import_types2 = require("@tradejs/types");
5545
5654
  var import_node_crypto7 = require("crypto");
5546
5655
  var import_node_module = require("module");
5547
5656
  var import_promises6 = __toESM(require("fs/promises"));
5548
- var import_node_path8 = __toESM(require("path"));
5657
+ var import_node_path9 = __toESM(require("path"));
5549
5658
 
5550
5659
  // src/lib/aiTrainResearch.ts
5551
5660
  var DAY_MS4 = 24 * 60 * 60 * 1e3;
@@ -5596,81 +5705,7 @@ var fingerprintResearchContextEnvironment = (env = process.env) => fingerprintRe
5596
5705
  // src/lib/strategyResearch/native.ts
5597
5706
  var import_data = require("@tradejs/core/data");
5598
5707
  var import_coreResearch = require("@tradejs/infra/coreResearch");
5599
-
5600
- // src/lib/coreResearch/trace.ts
5601
- var import_node_fs3 = require("fs");
5602
- var import_node_readline2 = __toESM(require("readline"));
5603
- var import_node_path10 = __toESM(require("path"));
5604
- var summarizeCoreResearchTrace = async (filePaths = []) => {
5605
- const events = /* @__PURE__ */ Object.create(null);
5606
- const skipCounts = /* @__PURE__ */ Object.create(null);
5607
- const exitCodes = /* @__PURE__ */ Object.create(null);
5608
- const policyRejections = {};
5609
- const policyExamples = [];
5610
- for (const inputPath of filePaths) {
5611
- const filePath = import_node_path10.default.resolve(inputPath);
5612
- const input = (0, import_node_fs3.createReadStream)(filePath);
5613
- const lines = import_node_readline2.default.createInterface({ input, crlfDelay: Infinity });
5614
- let index = -1;
5615
- try {
5616
- for await (const line of lines) {
5617
- index++;
5618
- if (!line.trim()) continue;
5619
- let event;
5620
- try {
5621
- event = JSON.parse(line);
5622
- } catch (error) {
5623
- throw new Error(
5624
- `${filePath}:${index + 1} contains invalid trace JSON: ${String(error)}`
5625
- );
5626
- }
5627
- events[event.event] = (events[event.event] ?? 0) + 1;
5628
- if (event.event === "skip_summary") {
5629
- for (const [reason, count] of Object.entries(event.skipCounts)) {
5630
- skipCounts[reason] = (skipCounts[reason] ?? 0) + count;
5631
- }
5632
- }
5633
- if (event.event === "position_exited" && event.exitCode) {
5634
- exitCodes[event.exitCode] = (exitCodes[event.exitCode] ?? 0) + 1;
5635
- }
5636
- if ("researchEntryDecision" in event && event.researchEntryDecision && !event.researchEntryDecision.allowed) {
5637
- const reason = event.researchEntryDecision.reason;
5638
- policyRejections[reason] = (policyRejections[reason] ?? 0) + 1;
5639
- if (policyExamples.length < 20)
5640
- policyExamples.push({
5641
- timestamp: event.timestamp,
5642
- symbol: event.symbol,
5643
- decision: event.researchEntryDecision
5644
- });
5645
- }
5646
- }
5647
- } finally {
5648
- lines.close();
5649
- input.destroy();
5650
- }
5651
- }
5652
- return {
5653
- events: Object.fromEntries(Object.entries(events).sort()),
5654
- skipCounts: Object.fromEntries(
5655
- Object.entries(skipCounts).sort(
5656
- ([leftKey, leftValue], [rightKey, rightValue]) => rightValue - leftValue || (leftKey < rightKey ? -1 : leftKey > rightKey ? 1 : 0)
5657
- )
5658
- ),
5659
- exitCodes: Object.fromEntries(
5660
- Object.entries(exitCodes).sort(
5661
- ([leftKey, leftValue], [rightKey, rightValue]) => rightValue - leftValue || (leftKey < rightKey ? -1 : leftKey > rightKey ? 1 : 0)
5662
- )
5663
- ),
5664
- ...policyExamples.length ? {
5665
- researchPolicy: {
5666
- rejections: policyRejections,
5667
- examples: policyExamples
5668
- }
5669
- } : {}
5670
- };
5671
- };
5672
-
5673
- // src/lib/strategyResearch/native.ts
5708
+ init_trace();
5674
5709
  var import_jev4 = require("@tradejs/core/jev");
5675
5710
 
5676
5711
  // src/lib/backtest/jevOptions.ts