@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.
- package/dist/cli.js +7150 -6352
- package/dist/lib/binanceMarketContextBackfill.js +2 -2
- package/dist/lib/coinMarketCapContextBackfill.js +1 -1
- package/dist/lib/coreResearch.js +261 -115
- package/dist/lib/marketContextPrepare.js +3 -3
- package/dist/lib/researchVisuals.js +27 -6
- package/dist/lib/strategyResearch.js +234 -245
- package/dist/scripts/aiPocketSearch.js +3343 -3029
- package/dist/scripts/backtest.js +192 -69
- package/dist/scripts/binanceMarketIngest.js +3 -3
- package/dist/scripts/coreResearch.js +263 -115
- package/dist/scripts/replay.js +483 -114
- package/dist/scripts/replayRunner.js +483 -114
- package/dist/scripts/researchVisuals.js +27 -6
- package/dist/scripts/runtimeParity.js +3 -3
- package/dist/scripts/signals.js +3 -3
- package/dist/scripts/signalsDaemon.js +3 -3
- package/dist/scripts/strategyRelease.js +6 -37
- package/dist/scripts/strategyResearch.js +239 -249
- package/package.json +7 -7
- package/dist/lib/researchMetricSnapshot.js +0 -74
|
@@ -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,
|
|
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
|
-
|
|
148
|
+
import_node_fs2 = require("fs");
|
|
57
149
|
import_promises = __toESM(require("fs/promises"));
|
|
58
150
|
import_backtest = require("@tradejs/core/backtest");
|
|
59
|
-
|
|
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
|
|
79
|
-
const input = (0,
|
|
80
|
-
for await (const chunk of input)
|
|
81
|
-
return
|
|
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 =
|
|
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(
|
|
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,
|
|
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
|
-
|
|
459
|
+
import_node_path3 = __toESM(require("path"));
|
|
351
460
|
import_node_os = __toESM(require("os"));
|
|
352
|
-
|
|
461
|
+
import_node_crypto2 = require("crypto");
|
|
353
462
|
init_io();
|
|
354
|
-
researchDirectory = (projectRoot, spec) =>
|
|
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 =
|
|
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}${
|
|
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 =
|
|
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(
|
|
381
|
-
const token = (0,
|
|
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(
|
|
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(
|
|
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(
|
|
440
|
-
const projectRoot =
|
|
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 =
|
|
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(
|
|
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,
|
|
586
|
+
isRetainedDeletion = async (directory, file, hash) => {
|
|
478
587
|
try {
|
|
479
|
-
const inventory = await readJson(
|
|
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 ===
|
|
590
|
+
(entry) => entry.path === file && entry.sha256 === hash
|
|
482
591
|
);
|
|
483
592
|
if (index < 0) return false;
|
|
484
|
-
const receipt = await readJson(
|
|
485
|
-
return ["applying", "completed"].includes(receipt.status) && receipt.files[0]?.path === file && receipt.files[0]?.sha256 ===
|
|
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
|
|
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
|
-
|
|
1827
|
-
|
|
1935
|
+
hash ^= index + 1;
|
|
1936
|
+
hash = Math.imul(hash, 16777619);
|
|
1828
1937
|
}
|
|
1829
|
-
return (
|
|
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
|
-
|
|
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
|
|
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
|
|
3777
|
-
var
|
|
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
|
|
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) =>
|
|
3813
|
-
let commonRoot = files.length ?
|
|
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 =
|
|
3816
|
-
return relative === ".." || relative.startsWith(`..${
|
|
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 =
|
|
3884
|
+
commonRoot = import_node_path4.default.dirname(commonRoot);
|
|
3819
3885
|
const selectors = [];
|
|
3820
|
-
if (files.length > 1e3 && commonRoot !==
|
|
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(
|
|
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 =
|
|
3879
|
-
if (relative === "" || !relative.startsWith(`..${
|
|
3880
|
-
guestFiles.add(
|
|
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,
|
|
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 =
|
|
3929
|
-
return relative !== "" && !relative.startsWith(`..${
|
|
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 =
|
|
3934
|
-
const resolve = (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(
|
|
4034
|
-
if (await import_promises3.default.realpath(
|
|
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",
|
|
4081
|
-
["candidates",
|
|
4082
|
-
["candidates",
|
|
4083
|
-
["ledger",
|
|
4084
|
-
["chart",
|
|
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",
|
|
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,
|
|
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,
|
|
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,
|
|
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:
|
|
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
|
-
|
|
4216
|
+
import_node_path6.default.join(directory, "retention.json")
|
|
4151
4217
|
);
|
|
4152
|
-
const receipts =
|
|
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 =
|
|
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: [
|
|
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 =
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
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 =
|
|
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
|
-
|
|
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 =
|
|
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(
|
|
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
|
-
|
|
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(
|
|
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(
|
|
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 =
|
|
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
|
-
|
|
4437
|
+
import_node_path7.default.join(root, id, "spec.json")
|
|
4372
4438
|
);
|
|
4373
4439
|
const previousState = await readJson(
|
|
4374
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
4474
|
+
control,
|
|
4409
4475
|
folder,
|
|
4410
|
-
async (runId) => writeJsonAtomic(
|
|
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(
|
|
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
|
-
|
|
4546
|
+
import_node_path7.default.join(parentDirectory, "state.json")
|
|
4484
4547
|
);
|
|
4485
4548
|
const parentSpec = await readJson(
|
|
4486
|
-
|
|
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
|
-
|
|
4576
|
+
import_node_path7.default.join(parentDirectory, "03-freeze-gates/result.json")
|
|
4514
4577
|
);
|
|
4515
4578
|
frozenSelection = await readJson(
|
|
4516
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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(
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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(
|
|
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(
|
|
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: ${
|
|
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(
|
|
5076
|
+
await writeJsonAtomic(import_node_path7.default.join(directory, "result.json"), conclusion);
|
|
5014
5077
|
await import_promises5.default.writeFile(
|
|
5015
|
-
|
|
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
|
|
5039
|
-
var
|
|
5040
|
-
var
|
|
5041
|
-
var
|
|
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 =
|
|
5252
|
-
const input = (0,
|
|
5253
|
-
const
|
|
5254
|
-
input.on("data", (chunk) =>
|
|
5255
|
-
const lines =
|
|
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 =
|
|
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
|
|
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
|
|
5601
|
+
var import_node_crypto7 = require("crypto");
|
|
5539
5602
|
var import_promises7 = __toESM(require("fs/promises"));
|
|
5540
|
-
var
|
|
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
|
|
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
|
|
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,
|
|
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
|
|
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
|
|
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,
|
|
6677
|
+
dirtyDiffSha256: (0, import_node_crypto9.createHash)("sha256").update(dirtyDiff).digest("hex")
|
|
6688
6678
|
} : {}
|
|
6689
6679
|
};
|
|
6690
6680
|
} catch {
|