@tradejs/cli 2.0.18 → 2.0.20
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 +14522 -15704
- package/dist/lib/aiExport.js +15 -1608
- package/dist/lib/aiPocketSearch.js +499 -67
- package/dist/lib/aiPocketSearchCli.js +113 -0
- package/dist/lib/aiTrainCharts.js +14 -1
- package/dist/lib/aiTrainMetrics.js +4 -1
- package/dist/lib/aiTrainResearch.js +88 -54
- package/dist/lib/binanceMarketContextBackfill.js +7 -6
- package/dist/lib/coinMarketCapContextBackfill.js +19 -18
- package/dist/lib/coreResearch.js +2738 -0
- package/dist/lib/derivativesContextBackfill.js +10 -9
- package/dist/lib/executionCalibration.js +6 -3
- package/dist/lib/hyperliquidWhaleBackfill.js +8 -8
- package/dist/lib/hyperliquidWhaleStream.js +4 -4
- package/dist/lib/marketContextPrepare.js +83 -1686
- package/dist/lib/runEnvironment.js +14 -1666
- package/dist/lib/runFormatting.js +1 -1607
- package/dist/lib/runtimeDebugEvidence.js +2 -48
- package/dist/lib/runtimeLineage.js +81 -48
- package/dist/lib/runtimeRedis.js +6 -57
- package/dist/lib/runtimeScorecard.js +153 -10
- package/dist/lib/runtimeStrategyBacktest.js +2 -48
- package/dist/lib/strategyGateFingerprint.js +80 -0
- package/dist/lib/strategyRelease.js +1365 -0
- package/dist/scripts/aiExport.js +17 -1610
- package/dist/scripts/aiPocketSearch.js +1070 -1770
- package/dist/scripts/aiTrain.js +108 -1688
- package/dist/scripts/backtest.js +120 -1760
- package/dist/scripts/binanceMarketIngest.js +5 -4
- package/dist/scripts/candlesMigrateProvider.js +6 -5186
- package/dist/scripts/cleanupMarketContext.js +4 -3
- package/dist/scripts/continuity.js +4 -3
- package/dist/scripts/coreResearch.js +2830 -0
- package/dist/scripts/derivativesIngest.js +6 -4
- package/dist/scripts/derivativesIngestCoinalyzeAll.js +5 -4
- package/dist/scripts/doctor.js +2 -5184
- package/dist/scripts/executionCalibration.js +6 -3
- package/dist/scripts/hyperliquidWhaleBackfill.js +8 -8
- package/dist/scripts/hyperliquidWhaleIngest.js +4 -4
- package/dist/scripts/hyperliquidWhalesUpdate.js +1 -1
- package/dist/scripts/migration.js +3 -3
- package/dist/scripts/mlExport.js +1 -1607
- package/dist/scripts/replay.js +239 -1855
- package/dist/scripts/replayRunner.js +239 -1855
- package/dist/scripts/replayRuntimeEvidence.js +29 -62
- package/dist/scripts/researchAuto.js +3 -49
- package/dist/scripts/results.js +1 -1607
- package/dist/scripts/runtimeEvidence.js +18 -56
- package/dist/scripts/runtimeParity.js +121 -1746
- package/dist/scripts/runtimeScorecard.js +883 -29
- package/dist/scripts/signals.js +1178 -2724
- package/dist/scripts/signalsDaemon.js +1183 -2696
- package/dist/scripts/signalsSummary.js +4 -26
- package/dist/scripts/strategyRelease.js +1469 -0
- package/dist/workers/testerWorker.js +2 -2
- package/package.json +13 -9
|
@@ -34,8 +34,9 @@ __export(runtimeScorecard_exports, {
|
|
|
34
34
|
runtimeScorecard: () => runtimeScorecard
|
|
35
35
|
});
|
|
36
36
|
module.exports = __toCommonJS(runtimeScorecard_exports);
|
|
37
|
-
var
|
|
38
|
-
var
|
|
37
|
+
var import_promises4 = __toESM(require("fs/promises"));
|
|
38
|
+
var import_node_crypto3 = require("crypto");
|
|
39
|
+
var import_node_path4 = __toESM(require("path"));
|
|
39
40
|
var import_args = __toESM(require("args"));
|
|
40
41
|
var import_chalk = __toESM(require("chalk"));
|
|
41
42
|
|
|
@@ -215,6 +216,71 @@ var extractRuntimeRows = (artifact) => {
|
|
|
215
216
|
statsBuckets: asArray(runtime.evaluationStatsBuckets).map(asRecord2).filter((item) => item != null)
|
|
216
217
|
};
|
|
217
218
|
};
|
|
219
|
+
var buildRuntimeLineageSummary = (rows) => {
|
|
220
|
+
const lineageRows = [...rows.evaluations, ...rows.signals, ...rows.trades];
|
|
221
|
+
const lineages = lineageRows.map((row) => asRecord2(row.runtimeLineage)).filter((lineage) => lineage != null);
|
|
222
|
+
const identities = /* @__PURE__ */ new Map();
|
|
223
|
+
for (const lineage of lineages) {
|
|
224
|
+
const identity2 = {
|
|
225
|
+
compositionId: finiteString(lineage.compositionId),
|
|
226
|
+
gitSha: finiteString(lineage.gitSha),
|
|
227
|
+
gitDirty: typeof lineage.gitDirty === "boolean" ? lineage.gitDirty : null,
|
|
228
|
+
gateFingerprint: finiteString(lineage.gateFingerprint),
|
|
229
|
+
configFingerprint: finiteString(lineage.configFingerprint),
|
|
230
|
+
contextFingerprint: finiteString(lineage.contextFingerprint),
|
|
231
|
+
maxLossValue: finiteNumber(lineage.maxLossValue)
|
|
232
|
+
};
|
|
233
|
+
identities.set(JSON.stringify(identity2), identity2);
|
|
234
|
+
}
|
|
235
|
+
const identity = identities.size === 1 ? [...identities.values()][0] : null;
|
|
236
|
+
const complete = lineageRows.length > 0 && lineages.length === lineageRows.length && identity != null && identity.compositionId != null && identity.gitSha != null && identity.gitDirty === false && identity.gateFingerprint != null && identity.configFingerprint != null && identity.contextFingerprint != null && identity.maxLossValue != null;
|
|
237
|
+
return {
|
|
238
|
+
complete,
|
|
239
|
+
conflicts: identities.size > 1,
|
|
240
|
+
rows: lineageRows.length,
|
|
241
|
+
rowsWithLineage: lineages.length,
|
|
242
|
+
compositionId: finiteString(identity?.compositionId),
|
|
243
|
+
gitSha: finiteString(identity?.gitSha),
|
|
244
|
+
gitDirty: typeof identity?.gitDirty === "boolean" ? identity.gitDirty : null,
|
|
245
|
+
gateFingerprint: finiteString(identity?.gateFingerprint),
|
|
246
|
+
configFingerprint: finiteString(identity?.configFingerprint),
|
|
247
|
+
contextFingerprint: finiteString(identity?.contextFingerprint),
|
|
248
|
+
maxLossValue: finiteNumber(identity?.maxLossValue)
|
|
249
|
+
};
|
|
250
|
+
};
|
|
251
|
+
var belongsToStrategy = (row, strategy) => strategy == null || finiteString(row.strategy) === strategy;
|
|
252
|
+
var filterArtifactByStrategy = (artifact, strategy) => {
|
|
253
|
+
if (!strategy) return artifact;
|
|
254
|
+
const root = asRecord2(artifact) ?? {};
|
|
255
|
+
const runtime = runtimePayload(artifact);
|
|
256
|
+
return {
|
|
257
|
+
...root,
|
|
258
|
+
runtime: {
|
|
259
|
+
...runtime,
|
|
260
|
+
evaluations: asArray(runtime.evaluations).filter(
|
|
261
|
+
(item) => belongsToStrategy(
|
|
262
|
+
asRecord2(asRecord2(item)?.evaluation) ?? asRecord2(item) ?? {},
|
|
263
|
+
strategy
|
|
264
|
+
)
|
|
265
|
+
),
|
|
266
|
+
signals: asArray(runtime.signals).filter(
|
|
267
|
+
(item) => belongsToStrategy(
|
|
268
|
+
asRecord2(asRecord2(item)?.signal) ?? asRecord2(item) ?? {},
|
|
269
|
+
strategy
|
|
270
|
+
)
|
|
271
|
+
),
|
|
272
|
+
trades: asArray(runtime.trades).filter(
|
|
273
|
+
(item) => belongsToStrategy(
|
|
274
|
+
asRecord2(asRecord2(item)?.trade) ?? asRecord2(item) ?? {},
|
|
275
|
+
strategy
|
|
276
|
+
)
|
|
277
|
+
),
|
|
278
|
+
evaluationStatsBuckets: asArray(runtime.evaluationStatsBuckets).filter(
|
|
279
|
+
(item) => belongsToStrategy(asRecord2(item) ?? {}, strategy)
|
|
280
|
+
)
|
|
281
|
+
}
|
|
282
|
+
};
|
|
283
|
+
};
|
|
218
284
|
var countBy = (items, getter) => {
|
|
219
285
|
const counts = {};
|
|
220
286
|
for (const item of items) {
|
|
@@ -333,14 +399,43 @@ var getReplayComparison = (artifact) => {
|
|
|
333
399
|
const root = asRecord2(artifact) ?? {};
|
|
334
400
|
return asRecord2(asRecord2(root.replay)?.runtimeComparison) ?? null;
|
|
335
401
|
};
|
|
402
|
+
var replayCountsForStrategy = (replayComparison, strategy) => {
|
|
403
|
+
if (strategy) {
|
|
404
|
+
const byStrategy = asRecord2(replayComparison?.byStrategy);
|
|
405
|
+
const strategyCounts = asRecord2(byStrategy?.[strategy]);
|
|
406
|
+
if (strategyCounts) {
|
|
407
|
+
return {
|
|
408
|
+
matched: finiteNumber(strategyCounts.matched) ?? 0,
|
|
409
|
+
backtestOnly: finiteNumber(strategyCounts.backtestOnly) ?? 0,
|
|
410
|
+
runtimeOnly: finiteNumber(strategyCounts.runtimeOnly) ?? 0
|
|
411
|
+
};
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
const counts = asRecord2(replayComparison?.counts);
|
|
415
|
+
return {
|
|
416
|
+
matched: finiteNumber(counts?.matched) ?? 0,
|
|
417
|
+
backtestOnly: finiteNumber(counts?.backtestOnly) ?? 0,
|
|
418
|
+
runtimeOnly: finiteNumber(counts?.runtimeOnly) ?? 0
|
|
419
|
+
};
|
|
420
|
+
};
|
|
336
421
|
var getCalibrationSummary = (artifact) => {
|
|
337
422
|
const root = asRecord2(artifact) ?? {};
|
|
338
423
|
return asRecord2(asRecord2(root.summary)?.all) ?? null;
|
|
339
424
|
};
|
|
425
|
+
var getProspectiveSummary = (artifact) => {
|
|
426
|
+
const root = asRecord2(artifact);
|
|
427
|
+
if (!root || root.reportType !== "strategy-prospective-evidence") return null;
|
|
428
|
+
return {
|
|
429
|
+
rawCoreExpectancy: finiteNumber(root.rawCoreExpectancy),
|
|
430
|
+
aiGateExpectancy: finiteNumber(root.aiGateExpectancy),
|
|
431
|
+
regimeCoverage: finiteNumber(root.regimeCoverage)
|
|
432
|
+
};
|
|
433
|
+
};
|
|
340
434
|
var buildRuntimeScorecard = ({
|
|
341
435
|
runtimeArtifact,
|
|
342
436
|
replayEvidenceArtifact,
|
|
343
437
|
calibrationArtifact,
|
|
438
|
+
prospectiveEvidenceArtifact,
|
|
344
439
|
historyRuntimeArtifacts = [],
|
|
345
440
|
thresholds = {
|
|
346
441
|
minimumParityRatio: 0.95,
|
|
@@ -348,9 +443,19 @@ var buildRuntimeScorecard = ({
|
|
|
348
443
|
minimumClosedTrades: 20,
|
|
349
444
|
minimumExpectancy: 0
|
|
350
445
|
},
|
|
351
|
-
generatedAt = Date.now()
|
|
446
|
+
generatedAt = Date.now(),
|
|
447
|
+
strategy = null,
|
|
448
|
+
llmComparatorPolicy = "ai_approved_only"
|
|
352
449
|
}) => {
|
|
353
|
-
const
|
|
450
|
+
const scopedRuntimeArtifact = filterArtifactByStrategy(
|
|
451
|
+
runtimeArtifact,
|
|
452
|
+
strategy
|
|
453
|
+
);
|
|
454
|
+
const scopedHistoryArtifacts = historyRuntimeArtifacts.map(
|
|
455
|
+
(artifact) => filterArtifactByStrategy(artifact, strategy)
|
|
456
|
+
);
|
|
457
|
+
const rows = extractRuntimeRows(scopedRuntimeArtifact);
|
|
458
|
+
const runtimeLineage = buildRuntimeLineageSummary(rows);
|
|
354
459
|
const window = runtimeWindow(runtimeArtifact);
|
|
355
460
|
const startTime = finiteNumber(window.startTime) ?? generatedAt - 864e5;
|
|
356
461
|
const endTime = finiteNumber(window.endTime) ?? generatedAt;
|
|
@@ -365,6 +470,20 @@ var buildRuntimeScorecard = ({
|
|
|
365
470
|
})).filter(
|
|
366
471
|
({ decision }) => decision === "approved" || decision === "rejected"
|
|
367
472
|
);
|
|
473
|
+
const llmEligibleEvaluations = llmComparatorPolicy === "disabled" ? [] : llmComparatorPolicy === "ai_approved_only" ? signalEvaluations.filter(
|
|
474
|
+
(evaluation) => finiteString(asRecord2(evaluation.aiAnalysis)?.gateDecision) === "approved"
|
|
475
|
+
) : signalEvaluations;
|
|
476
|
+
const gateComparisons = llmEligibleEvaluations.map((evaluation) => {
|
|
477
|
+
const analysis = asRecord2(evaluation.aiAnalysis);
|
|
478
|
+
const gateDecision = finiteString(analysis?.gateDecision);
|
|
479
|
+
const llmDecision = finiteString(analysis?.llmDecision);
|
|
480
|
+
if (gateDecision !== "approved" && gateDecision !== "rejected" || llmDecision !== "approved" && llmDecision !== "rejected") {
|
|
481
|
+
return null;
|
|
482
|
+
}
|
|
483
|
+
return { gateDecision, llmDecision };
|
|
484
|
+
}).filter(
|
|
485
|
+
(comparison) => Boolean(comparison)
|
|
486
|
+
);
|
|
368
487
|
const allocatorDecisions = signalEvaluations.map((evaluation) => asRecord2(evaluation.allocatorDecision)).filter((item) => item != null);
|
|
369
488
|
const riskDecisions = signalEvaluations.map((evaluation) => asRecord2(evaluation.riskDecision)).filter((item) => item != null);
|
|
370
489
|
const orderAttempts = rows.signals.filter(
|
|
@@ -375,27 +494,30 @@ var buildRuntimeScorecard = ({
|
|
|
375
494
|
return trade.status === "closed" && exitTimestamp != null && exitTimestamp >= startTime && exitTimestamp < endTime;
|
|
376
495
|
});
|
|
377
496
|
const replayComparison = getReplayComparison(replayEvidenceArtifact);
|
|
378
|
-
const replayCounts =
|
|
379
|
-
const matched =
|
|
380
|
-
const backtestOnly =
|
|
381
|
-
const runtimeOnly =
|
|
497
|
+
const replayCounts = replayCountsForStrategy(replayComparison, strategy);
|
|
498
|
+
const matched = replayCounts.matched;
|
|
499
|
+
const backtestOnly = replayCounts.backtestOnly;
|
|
500
|
+
const runtimeOnly = replayCounts.runtimeOnly;
|
|
382
501
|
const comparisonTotal = matched + backtestOnly + runtimeOnly;
|
|
383
502
|
const parityRatio = comparisonTotal > 0 ? matched / comparisonTotal : null;
|
|
384
503
|
const lineage = asRecord2(replayComparison?.lineage);
|
|
385
504
|
const lineageReason = finiteString(lineage?.reason);
|
|
386
|
-
const
|
|
505
|
+
const calibrationRoot = asRecord2(calibrationArtifact) ?? {};
|
|
506
|
+
const calibrationSummary = asRecord2(calibrationRoot.summary);
|
|
507
|
+
const calibration = strategy ? asRecord2(asRecord2(calibrationSummary?.byStrategy)?.[strategy]) : getCalibrationSummary(calibrationArtifact);
|
|
387
508
|
const actualSlippageBps = finiteNumber(
|
|
388
509
|
asRecord2(calibration?.signalToFillAdverseBps)?.avg
|
|
389
510
|
);
|
|
390
511
|
const residualVsModelBps = finiteNumber(
|
|
391
512
|
asRecord2(calibration?.residualVsCurrentModelBps)?.avg
|
|
392
513
|
);
|
|
514
|
+
const prospective = getProspectiveSummary(prospectiveEvidenceArtifact);
|
|
393
515
|
const historyTrades = dedupeTrades([
|
|
394
|
-
...
|
|
395
|
-
|
|
516
|
+
...scopedHistoryArtifacts,
|
|
517
|
+
scopedRuntimeArtifact
|
|
396
518
|
]);
|
|
397
519
|
const currentDistributions = buildDistributions(rows);
|
|
398
|
-
const previousArtifact =
|
|
520
|
+
const previousArtifact = scopedHistoryArtifacts.map((artifact) => ({ artifact, window: runtimeWindow(artifact) })).filter(({ window: candidateWindow }) => {
|
|
399
521
|
const candidateEndTime = finiteNumber(candidateWindow.endTime);
|
|
400
522
|
return candidateEndTime != null && candidateEndTime <= startTime;
|
|
401
523
|
}).sort(
|
|
@@ -454,6 +576,8 @@ var buildRuntimeScorecard = ({
|
|
|
454
576
|
schemaVersion: 1,
|
|
455
577
|
generatedAt,
|
|
456
578
|
window: { startTime, endTime },
|
|
579
|
+
strategy,
|
|
580
|
+
lineage: runtimeLineage,
|
|
457
581
|
promotionStatus,
|
|
458
582
|
thresholds,
|
|
459
583
|
funnel: {
|
|
@@ -510,6 +634,24 @@ var buildRuntimeScorecard = ({
|
|
|
510
634
|
currentClosedTrades.map((trade) => finiteNumber(trade.fundingFee))
|
|
511
635
|
)
|
|
512
636
|
},
|
|
637
|
+
gateComparison: {
|
|
638
|
+
policy: llmComparatorPolicy,
|
|
639
|
+
eligible: llmEligibleEvaluations.length,
|
|
640
|
+
compared: gateComparisons.length,
|
|
641
|
+
coverage: llmEligibleEvaluations.length ? round(gateComparisons.length / llmEligibleEvaluations.length) : null,
|
|
642
|
+
agreements: gateComparisons.filter(
|
|
643
|
+
(comparison) => comparison.gateDecision === comparison.llmDecision
|
|
644
|
+
).length,
|
|
645
|
+
disagreements: gateComparisons.filter(
|
|
646
|
+
(comparison) => comparison.gateDecision !== comparison.llmDecision
|
|
647
|
+
).length,
|
|
648
|
+
gateApprovedLlmRejected: gateComparisons.filter(
|
|
649
|
+
(comparison) => comparison.gateDecision === "approved" && comparison.llmDecision === "rejected"
|
|
650
|
+
).length,
|
|
651
|
+
gateRejectedLlmApproved: gateComparisons.filter(
|
|
652
|
+
(comparison) => comparison.gateDecision === "rejected" && comparison.llmDecision === "approved"
|
|
653
|
+
).length
|
|
654
|
+
},
|
|
513
655
|
parity: {
|
|
514
656
|
available: comparisonTotal > 0,
|
|
515
657
|
matched,
|
|
@@ -523,6 +665,7 @@ var buildRuntimeScorecard = ({
|
|
|
523
665
|
actualSignalToFillSlippageBps: actualSlippageBps,
|
|
524
666
|
residualVsCurrentModelBps: residualVsModelBps
|
|
525
667
|
},
|
|
668
|
+
prospective,
|
|
526
669
|
rolling,
|
|
527
670
|
distributions: currentDistributions,
|
|
528
671
|
distributionChanges: {
|
|
@@ -575,6 +718,7 @@ Window: ${new Date(scorecard.window.startTime).toISOString()} \u2014 ${new Date(
|
|
|
575
718
|
- Evaluations: ${scorecard.funnel.evaluations}
|
|
576
719
|
- Core candidates: ${scorecard.funnel.coreCandidates}
|
|
577
720
|
- Gate approvals/rejects: ${scorecard.funnel.gate.approved}/${scorecard.funnel.gate.rejected}
|
|
721
|
+
- AI / LLM disagreement: ${scorecard.gateComparison.disagreements}/${scorecard.gateComparison.compared}
|
|
578
722
|
- Allocator approvals/rejects: ${scorecard.funnel.allocator.available ? `${scorecard.funnel.allocator.approved}/${scorecard.funnel.allocator.rejected}` : "unavailable"}
|
|
579
723
|
- Risk approvals/rejects: ${scorecard.funnel.risk.available ? `${scorecard.funnel.risk.approved}/${scorecard.funnel.risk.rejected}` : "unavailable"}
|
|
580
724
|
- Order attempts/failures: ${scorecard.funnel.orderAttempts}/${scorecard.funnel.orderFailures}
|
|
@@ -604,22 +748,649 @@ ${reactions}
|
|
|
604
748
|
`;
|
|
605
749
|
};
|
|
606
750
|
|
|
751
|
+
// src/lib/strategyRelease.ts
|
|
752
|
+
var import_node_crypto2 = require("crypto");
|
|
753
|
+
var import_promises3 = __toESM(require("fs/promises"));
|
|
754
|
+
var import_node_path3 = __toESM(require("path"));
|
|
755
|
+
var import_types = require("@tradejs/types");
|
|
756
|
+
var import_strategyReleaseEvidence = require("@tradejs/infra/strategyReleaseEvidence");
|
|
757
|
+
var SHA256_RE = /^[a-f0-9]{64}$/;
|
|
758
|
+
var LINEAGE_FINGERPRINT_RE = /^[a-f0-9]{16}$/;
|
|
759
|
+
var safeSegment2 = import_strategyReleaseEvidence.safeStrategyEvidenceSegment;
|
|
760
|
+
var compactTimestamp = import_strategyReleaseEvidence.compactStrategyEvidenceTimestamp;
|
|
761
|
+
var canonicalStrategyReleaseJson = (value) => (0, import_strategyReleaseEvidence.canonicalStrategyEvidenceJson)(value);
|
|
762
|
+
var strategyReleaseSha256 = (value) => (0, import_strategyReleaseEvidence.strategyEvidenceSha256)(value);
|
|
763
|
+
function assert(condition, message) {
|
|
764
|
+
if (!condition) throw new Error(`Invalid strategy release: ${message}`);
|
|
765
|
+
}
|
|
766
|
+
var assertFingerprint = (value, label) => assert(SHA256_RE.test(value), `${label} must be a lowercase SHA-256`);
|
|
767
|
+
var assertLineageFingerprint = (value, label) => assert(
|
|
768
|
+
LINEAGE_FINGERPRINT_RE.test(value),
|
|
769
|
+
`${label} must be a 16-character lowercase lineage fingerprint`
|
|
770
|
+
);
|
|
771
|
+
var releaseReasons = (input) => {
|
|
772
|
+
const reasons = [];
|
|
773
|
+
const requiredEvidenceKinds = [
|
|
774
|
+
"core_research",
|
|
775
|
+
"ai_gate",
|
|
776
|
+
"runtime_parity",
|
|
777
|
+
"execution_calibration"
|
|
778
|
+
];
|
|
779
|
+
if (input.evidence.some((entry) => !entry.verified) || requiredEvidenceKinds.some(
|
|
780
|
+
(kind) => !input.evidence.some((entry) => entry.kind === kind)
|
|
781
|
+
)) {
|
|
782
|
+
reasons.push("EVIDENCE_INCOMPLETE");
|
|
783
|
+
}
|
|
784
|
+
if (!input.gates.coreEdgeVerified) reasons.push("NO_VERIFIED_CORE_EDGE");
|
|
785
|
+
if (!input.gates.aiGateAddsValue) reasons.push("AI_GATE_ADDS_NO_VALUE");
|
|
786
|
+
if (!input.gates.currentMarketSuitable) {
|
|
787
|
+
reasons.push("CURRENT_REGIME_UNSUITABLE");
|
|
788
|
+
}
|
|
789
|
+
if (!input.gates.runtimeParityVerified) {
|
|
790
|
+
reasons.push("RUNTIME_PARITY_BLOCKED");
|
|
791
|
+
}
|
|
792
|
+
if (!input.gates.executionModelVerified) {
|
|
793
|
+
reasons.push("EXECUTION_MODEL_UNSAFE");
|
|
794
|
+
}
|
|
795
|
+
if (input.researchBudget.hypothesisFamilies >= 3 && !input.gates.coreEdgeVerified) {
|
|
796
|
+
reasons.push("RESEARCH_BUDGET_EXHAUSTED");
|
|
797
|
+
}
|
|
798
|
+
return [...new Set(reasons)];
|
|
799
|
+
};
|
|
800
|
+
var deriveReleaseGates = (evidence) => {
|
|
801
|
+
const asserted = (name) => {
|
|
802
|
+
const values = evidence.filter((entry) => entry.verified).map((entry) => entry.releaseAssertions?.[name]).filter((value) => typeof value === "boolean");
|
|
803
|
+
return values.length > 0 && values.every(Boolean);
|
|
804
|
+
};
|
|
805
|
+
return {
|
|
806
|
+
coreEdgeVerified: asserted("coreEdgeVerified"),
|
|
807
|
+
aiGateAddsValue: asserted("aiGateAddsValue"),
|
|
808
|
+
currentMarketSuitable: asserted("currentMarketSuitable"),
|
|
809
|
+
runtimeParityVerified: asserted("runtimeParityVerified"),
|
|
810
|
+
executionModelVerified: asserted("executionModelVerified")
|
|
811
|
+
};
|
|
812
|
+
};
|
|
813
|
+
var verifyEvidenceComposition = (strategy, composition, evidence) => {
|
|
814
|
+
const requireEqual = (actual, expected, label, artifactId) => assert(
|
|
815
|
+
actual === expected,
|
|
816
|
+
`evidence ${artifactId} ${label} does not match the frozen composition`
|
|
817
|
+
);
|
|
818
|
+
for (const reference of evidence) {
|
|
819
|
+
const lineage = reference.lineage;
|
|
820
|
+
if (reference.kind === "core_research") {
|
|
821
|
+
requireEqual(
|
|
822
|
+
lineage?.strategy,
|
|
823
|
+
strategy,
|
|
824
|
+
"strategy",
|
|
825
|
+
reference.artifactId
|
|
826
|
+
);
|
|
827
|
+
requireEqual(
|
|
828
|
+
lineage?.coreConfigSha256,
|
|
829
|
+
composition.coreConfigSha256,
|
|
830
|
+
"coreConfigSha256",
|
|
831
|
+
reference.artifactId
|
|
832
|
+
);
|
|
833
|
+
requireEqual(
|
|
834
|
+
lineage?.gitSha,
|
|
835
|
+
composition.gitSha,
|
|
836
|
+
"gitSha",
|
|
837
|
+
reference.artifactId
|
|
838
|
+
);
|
|
839
|
+
requireEqual(lineage?.gitDirty, false, "gitDirty", reference.artifactId);
|
|
840
|
+
requireEqual(
|
|
841
|
+
lineage?.maxLossValue,
|
|
842
|
+
composition.maxLossValue,
|
|
843
|
+
"MAX_LOSS_VALUE",
|
|
844
|
+
reference.artifactId
|
|
845
|
+
);
|
|
846
|
+
assert(
|
|
847
|
+
lineage?.sourceSha256s.includes(composition.coreExportSha256),
|
|
848
|
+
`evidence ${reference.artifactId} does not include the frozen core export`
|
|
849
|
+
);
|
|
850
|
+
continue;
|
|
851
|
+
}
|
|
852
|
+
if (reference.kind === "ai_gate") {
|
|
853
|
+
requireEqual(
|
|
854
|
+
lineage?.strategy,
|
|
855
|
+
strategy,
|
|
856
|
+
"strategy",
|
|
857
|
+
reference.artifactId
|
|
858
|
+
);
|
|
859
|
+
requireEqual(
|
|
860
|
+
lineage?.gitSha,
|
|
861
|
+
composition.gitSha,
|
|
862
|
+
"gitSha",
|
|
863
|
+
reference.artifactId
|
|
864
|
+
);
|
|
865
|
+
requireEqual(lineage?.gitDirty, false, "gitDirty", reference.artifactId);
|
|
866
|
+
requireEqual(
|
|
867
|
+
lineage?.gateConfigIdsFingerprint,
|
|
868
|
+
composition.gateConfigIdsFingerprint,
|
|
869
|
+
"gateConfigIdsFingerprint",
|
|
870
|
+
reference.artifactId
|
|
871
|
+
);
|
|
872
|
+
requireEqual(
|
|
873
|
+
lineage?.gateFingerprint,
|
|
874
|
+
composition.gateFingerprint,
|
|
875
|
+
"gateFingerprint",
|
|
876
|
+
reference.artifactId
|
|
877
|
+
);
|
|
878
|
+
requireEqual(
|
|
879
|
+
lineage?.gateContextFingerprint,
|
|
880
|
+
composition.gateContextFingerprint,
|
|
881
|
+
"gateContextFingerprint",
|
|
882
|
+
reference.artifactId
|
|
883
|
+
);
|
|
884
|
+
assert(
|
|
885
|
+
lineage?.sourceSha256s.includes(composition.coreExportSha256),
|
|
886
|
+
`evidence ${reference.artifactId} does not use the frozen core export`
|
|
887
|
+
);
|
|
888
|
+
continue;
|
|
889
|
+
}
|
|
890
|
+
if (reference.kind === "runtime_parity") {
|
|
891
|
+
requireEqual(
|
|
892
|
+
lineage?.strategy,
|
|
893
|
+
strategy,
|
|
894
|
+
"strategy",
|
|
895
|
+
reference.artifactId
|
|
896
|
+
);
|
|
897
|
+
requireEqual(
|
|
898
|
+
lineage?.gitSha,
|
|
899
|
+
composition.gitSha,
|
|
900
|
+
"gitSha",
|
|
901
|
+
reference.artifactId
|
|
902
|
+
);
|
|
903
|
+
requireEqual(lineage?.gitDirty, false, "gitDirty", reference.artifactId);
|
|
904
|
+
requireEqual(
|
|
905
|
+
lineage?.gateFingerprint,
|
|
906
|
+
composition.gateFingerprint,
|
|
907
|
+
"gateFingerprint",
|
|
908
|
+
reference.artifactId
|
|
909
|
+
);
|
|
910
|
+
requireEqual(
|
|
911
|
+
lineage?.runtimeConfigFingerprint,
|
|
912
|
+
composition.runtimeConfigFingerprint,
|
|
913
|
+
"runtimeConfigFingerprint",
|
|
914
|
+
reference.artifactId
|
|
915
|
+
);
|
|
916
|
+
requireEqual(
|
|
917
|
+
lineage?.runtimeContextFingerprint,
|
|
918
|
+
composition.runtimeContextFingerprint,
|
|
919
|
+
"runtimeContextFingerprint",
|
|
920
|
+
reference.artifactId
|
|
921
|
+
);
|
|
922
|
+
requireEqual(
|
|
923
|
+
lineage?.maxLossValue,
|
|
924
|
+
composition.maxLossValue,
|
|
925
|
+
"MAX_LOSS_VALUE",
|
|
926
|
+
reference.artifactId
|
|
927
|
+
);
|
|
928
|
+
continue;
|
|
929
|
+
}
|
|
930
|
+
if (reference.kind === "execution_calibration") {
|
|
931
|
+
requireEqual(
|
|
932
|
+
lineage?.strategy,
|
|
933
|
+
strategy,
|
|
934
|
+
"strategy",
|
|
935
|
+
reference.artifactId
|
|
936
|
+
);
|
|
937
|
+
requireEqual(
|
|
938
|
+
lineage?.gitSha,
|
|
939
|
+
composition.gitSha,
|
|
940
|
+
"gitSha",
|
|
941
|
+
reference.artifactId
|
|
942
|
+
);
|
|
943
|
+
requireEqual(lineage?.gitDirty, false, "gitDirty", reference.artifactId);
|
|
944
|
+
requireEqual(
|
|
945
|
+
lineage?.gateFingerprint,
|
|
946
|
+
composition.gateFingerprint,
|
|
947
|
+
"gateFingerprint",
|
|
948
|
+
reference.artifactId
|
|
949
|
+
);
|
|
950
|
+
requireEqual(
|
|
951
|
+
lineage?.runtimeConfigFingerprint,
|
|
952
|
+
composition.runtimeConfigFingerprint,
|
|
953
|
+
"runtimeConfigFingerprint",
|
|
954
|
+
reference.artifactId
|
|
955
|
+
);
|
|
956
|
+
requireEqual(
|
|
957
|
+
lineage?.runtimeContextFingerprint,
|
|
958
|
+
composition.runtimeContextFingerprint,
|
|
959
|
+
"runtimeContextFingerprint",
|
|
960
|
+
reference.artifactId
|
|
961
|
+
);
|
|
962
|
+
requireEqual(
|
|
963
|
+
lineage?.maxLossValue,
|
|
964
|
+
composition.maxLossValue,
|
|
965
|
+
"MAX_LOSS_VALUE",
|
|
966
|
+
reference.artifactId
|
|
967
|
+
);
|
|
968
|
+
}
|
|
969
|
+
}
|
|
970
|
+
};
|
|
971
|
+
var createStrategyReleaseManifest = (input) => {
|
|
972
|
+
assert(input.strategy.trim(), "strategy is required");
|
|
973
|
+
assert(
|
|
974
|
+
Number.isFinite(input.createdAt),
|
|
975
|
+
"createdAt must be a finite timestamp"
|
|
976
|
+
);
|
|
977
|
+
assert(
|
|
978
|
+
input.researchBudget.hypothesisFamilies <= 3,
|
|
979
|
+
"at most 3 hypothesis families are allowed"
|
|
980
|
+
);
|
|
981
|
+
assert(
|
|
982
|
+
input.researchBudget.maximumVariantsPerFamily <= 5,
|
|
983
|
+
"at most 5 variants per family are allowed"
|
|
984
|
+
);
|
|
985
|
+
assert(
|
|
986
|
+
input.researchBudget.isolatedLongFinalists <= 1,
|
|
987
|
+
"at most one isolated-long finalist is allowed"
|
|
988
|
+
);
|
|
989
|
+
assert(
|
|
990
|
+
input.researchBudget.aiGateTuningRounds <= 1,
|
|
991
|
+
"at most one AI-gate tuning round is allowed"
|
|
992
|
+
);
|
|
993
|
+
assert(input.marketWindow.cacheOnly, "market window must be cache-only");
|
|
994
|
+
assert(
|
|
995
|
+
input.marketWindow.endTime > input.marketWindow.startTime,
|
|
996
|
+
"market window is invalid"
|
|
997
|
+
);
|
|
998
|
+
assert(input.composition.maxLossValue > 0, "MAX_LOSS_VALUE must be positive");
|
|
999
|
+
assert(
|
|
1000
|
+
input.composition.longEnabled && input.composition.shortEnabled,
|
|
1001
|
+
"LONG and SHORT must both remain enabled"
|
|
1002
|
+
);
|
|
1003
|
+
assert(
|
|
1004
|
+
Number.isInteger(input.monitoring.minimumProspectiveClosedTrades) && input.monitoring.minimumProspectiveClosedTrades > 0,
|
|
1005
|
+
"monitoring.minimumProspectiveClosedTrades must be positive"
|
|
1006
|
+
);
|
|
1007
|
+
assert(
|
|
1008
|
+
Number.isFinite(input.monitoring.minimumParityRatio) && input.monitoring.minimumParityRatio >= 0 && input.monitoring.minimumParityRatio <= 1,
|
|
1009
|
+
"monitoring.minimumParityRatio must be between 0 and 1"
|
|
1010
|
+
);
|
|
1011
|
+
assert(
|
|
1012
|
+
Number.isFinite(input.monitoring.maximumOrderFailureRate) && input.monitoring.maximumOrderFailureRate >= 0 && input.monitoring.maximumOrderFailureRate <= 1,
|
|
1013
|
+
"monitoring.maximumOrderFailureRate must be between 0 and 1"
|
|
1014
|
+
);
|
|
1015
|
+
assert(
|
|
1016
|
+
Number.isFinite(input.monitoring.minimumRegimeCoverage) && input.monitoring.minimumRegimeCoverage >= 0 && input.monitoring.minimumRegimeCoverage <= 1,
|
|
1017
|
+
"monitoring.minimumRegimeCoverage must be between 0 and 1"
|
|
1018
|
+
);
|
|
1019
|
+
assert(
|
|
1020
|
+
input.monitoring.drawdownEnvelopes.length > 0 && input.monitoring.drawdownEnvelopes.every(
|
|
1021
|
+
(entry) => Number.isInteger(entry.days) && entry.days > 0 && Number.isFinite(entry.p95) && Number.isFinite(entry.maximum) && entry.p95 >= 0 && entry.maximum >= entry.p95
|
|
1022
|
+
),
|
|
1023
|
+
"monitoring drawdown envelopes are invalid"
|
|
1024
|
+
);
|
|
1025
|
+
assertFingerprint(input.composition.coreConfigSha256, "coreConfigSha256");
|
|
1026
|
+
assertFingerprint(input.composition.coreExportSha256, "coreExportSha256");
|
|
1027
|
+
assertLineageFingerprint(
|
|
1028
|
+
input.composition.gateConfigIdsFingerprint,
|
|
1029
|
+
"gateConfigIdsFingerprint"
|
|
1030
|
+
);
|
|
1031
|
+
assertLineageFingerprint(
|
|
1032
|
+
input.composition.runtimeConfigFingerprint,
|
|
1033
|
+
"runtimeConfigFingerprint"
|
|
1034
|
+
);
|
|
1035
|
+
assertLineageFingerprint(
|
|
1036
|
+
input.composition.gateFingerprint,
|
|
1037
|
+
"gateFingerprint"
|
|
1038
|
+
);
|
|
1039
|
+
assertLineageFingerprint(
|
|
1040
|
+
input.composition.gateContextFingerprint,
|
|
1041
|
+
"gateContextFingerprint"
|
|
1042
|
+
);
|
|
1043
|
+
assertLineageFingerprint(
|
|
1044
|
+
input.composition.runtimeContextFingerprint,
|
|
1045
|
+
"runtimeContextFingerprint"
|
|
1046
|
+
);
|
|
1047
|
+
assertFingerprint(input.marketWindow.universeSha256, "universeSha256");
|
|
1048
|
+
for (const entry of input.evidence) {
|
|
1049
|
+
assertFingerprint(entry.sha256, `evidence ${entry.artifactId}`);
|
|
1050
|
+
}
|
|
1051
|
+
verifyEvidenceComposition(input.strategy, input.composition, input.evidence);
|
|
1052
|
+
const evidenceGates = deriveReleaseGates(input.evidence);
|
|
1053
|
+
assert(
|
|
1054
|
+
canonicalStrategyReleaseJson(input.gates) === canonicalStrategyReleaseJson(evidenceGates),
|
|
1055
|
+
"release gates must be derived from verified evidence assertions"
|
|
1056
|
+
);
|
|
1057
|
+
const compositionIdentity = {
|
|
1058
|
+
strategy: input.strategy,
|
|
1059
|
+
gitSha: input.composition.gitSha,
|
|
1060
|
+
coreConfigSha256: input.composition.coreConfigSha256,
|
|
1061
|
+
coreExportSha256: input.composition.coreExportSha256,
|
|
1062
|
+
gateConfigIdsFingerprint: input.composition.gateConfigIdsFingerprint,
|
|
1063
|
+
runtimeConfigFingerprint: input.composition.runtimeConfigFingerprint,
|
|
1064
|
+
gateFingerprint: input.composition.gateFingerprint,
|
|
1065
|
+
gateContextFingerprint: input.composition.gateContextFingerprint,
|
|
1066
|
+
runtimeContextFingerprint: input.composition.runtimeContextFingerprint,
|
|
1067
|
+
maxLossValue: input.composition.maxLossValue
|
|
1068
|
+
};
|
|
1069
|
+
const compositionId = `${safeSegment2(input.strategy, "strategy")}_${strategyReleaseSha256(compositionIdentity).slice(0, 16)}`;
|
|
1070
|
+
const reasons = releaseReasons({ ...input, gates: evidenceGates });
|
|
1071
|
+
const economicFailure = reasons.some(
|
|
1072
|
+
(reason) => [
|
|
1073
|
+
"NO_VERIFIED_CORE_EDGE",
|
|
1074
|
+
"AI_GATE_ADDS_NO_VALUE",
|
|
1075
|
+
"CURRENT_REGIME_UNSUITABLE",
|
|
1076
|
+
"RESEARCH_BUDGET_EXHAUSTED"
|
|
1077
|
+
].includes(reason)
|
|
1078
|
+
);
|
|
1079
|
+
const evidenceFailure = reasons.some(
|
|
1080
|
+
(reason) => [
|
|
1081
|
+
"EVIDENCE_INCOMPLETE",
|
|
1082
|
+
"RUNTIME_PARITY_BLOCKED",
|
|
1083
|
+
"EXECUTION_MODEL_UNSAFE"
|
|
1084
|
+
].includes(reason)
|
|
1085
|
+
);
|
|
1086
|
+
const verdict = evidenceFailure ? "INSUFFICIENT_EVIDENCE" : economicFailure ? "UNSUITABLE_FOR_CURRENT_MARKET" : "READY_FOR_RUNTIME";
|
|
1087
|
+
const releaseIdentity = {
|
|
1088
|
+
strategy: input.strategy,
|
|
1089
|
+
createdAt: input.createdAt,
|
|
1090
|
+
compositionId,
|
|
1091
|
+
evidence: input.evidence.map(({ kind, artifactId, sha256: sha2562 }) => ({
|
|
1092
|
+
kind,
|
|
1093
|
+
artifactId,
|
|
1094
|
+
sha256: sha2562
|
|
1095
|
+
})),
|
|
1096
|
+
verdict
|
|
1097
|
+
};
|
|
1098
|
+
return {
|
|
1099
|
+
schema: import_types.STRATEGY_RELEASE_SCHEMA,
|
|
1100
|
+
releaseId: `${safeSegment2(input.strategy, "strategy")}_${compactTimestamp(input.createdAt)}_${strategyReleaseSha256(releaseIdentity).slice(0, 16)}`,
|
|
1101
|
+
strategy: input.strategy,
|
|
1102
|
+
createdAt: input.createdAt,
|
|
1103
|
+
composition: { ...input.composition, compositionId },
|
|
1104
|
+
marketWindow: input.marketWindow,
|
|
1105
|
+
researchBudget: input.researchBudget,
|
|
1106
|
+
evidence: input.evidence,
|
|
1107
|
+
gates: evidenceGates,
|
|
1108
|
+
monitoring: input.monitoring,
|
|
1109
|
+
verdict: {
|
|
1110
|
+
status: verdict,
|
|
1111
|
+
reasons,
|
|
1112
|
+
summary: input.summary
|
|
1113
|
+
},
|
|
1114
|
+
prospective: input.prospective
|
|
1115
|
+
};
|
|
1116
|
+
};
|
|
1117
|
+
var verifyStrategyReleaseEnvelope = async (valueOrPath) => {
|
|
1118
|
+
const value = typeof valueOrPath === "string" ? JSON.parse(await import_promises3.default.readFile(valueOrPath, "utf8")) : valueOrPath;
|
|
1119
|
+
const envelope = value;
|
|
1120
|
+
assert(
|
|
1121
|
+
envelope?.schema === "tradejs-strategy-release-envelope/v1",
|
|
1122
|
+
"release envelope schema mismatch"
|
|
1123
|
+
);
|
|
1124
|
+
assert(
|
|
1125
|
+
envelope.releaseId === envelope.manifest?.releaseId,
|
|
1126
|
+
"releaseId mismatch"
|
|
1127
|
+
);
|
|
1128
|
+
assert(
|
|
1129
|
+
strategyReleaseSha256(envelope.manifest) === envelope.manifestSha256,
|
|
1130
|
+
"release manifest checksum mismatch"
|
|
1131
|
+
);
|
|
1132
|
+
const manifest = envelope.manifest;
|
|
1133
|
+
assert(
|
|
1134
|
+
manifest.schema === import_types.STRATEGY_RELEASE_SCHEMA,
|
|
1135
|
+
"manifest schema mismatch"
|
|
1136
|
+
);
|
|
1137
|
+
const rebuilt = createStrategyReleaseManifest({
|
|
1138
|
+
strategy: manifest.strategy,
|
|
1139
|
+
createdAt: manifest.createdAt,
|
|
1140
|
+
composition: {
|
|
1141
|
+
gitSha: manifest.composition.gitSha,
|
|
1142
|
+
coreConfigSha256: manifest.composition.coreConfigSha256,
|
|
1143
|
+
coreExportSha256: manifest.composition.coreExportSha256,
|
|
1144
|
+
gateConfigIdsFingerprint: manifest.composition.gateConfigIdsFingerprint,
|
|
1145
|
+
runtimeConfigFingerprint: manifest.composition.runtimeConfigFingerprint,
|
|
1146
|
+
gateFingerprint: manifest.composition.gateFingerprint,
|
|
1147
|
+
gateContextFingerprint: manifest.composition.gateContextFingerprint,
|
|
1148
|
+
runtimeContextFingerprint: manifest.composition.runtimeContextFingerprint,
|
|
1149
|
+
maxLossValue: manifest.composition.maxLossValue,
|
|
1150
|
+
longEnabled: manifest.composition.longEnabled,
|
|
1151
|
+
shortEnabled: manifest.composition.shortEnabled
|
|
1152
|
+
},
|
|
1153
|
+
marketWindow: manifest.marketWindow,
|
|
1154
|
+
researchBudget: manifest.researchBudget,
|
|
1155
|
+
evidence: manifest.evidence,
|
|
1156
|
+
gates: manifest.gates,
|
|
1157
|
+
monitoring: manifest.monitoring,
|
|
1158
|
+
summary: manifest.verdict.summary,
|
|
1159
|
+
prospective: manifest.prospective
|
|
1160
|
+
});
|
|
1161
|
+
assert(
|
|
1162
|
+
canonicalStrategyReleaseJson(rebuilt) === canonicalStrategyReleaseJson(manifest),
|
|
1163
|
+
"release manifest contains self-declared or inconsistent derived fields"
|
|
1164
|
+
);
|
|
1165
|
+
return envelope;
|
|
1166
|
+
};
|
|
1167
|
+
var verifyStrategyEvidenceMarkerEnvelope = async (valueOrPath) => {
|
|
1168
|
+
const value = typeof valueOrPath === "string" ? JSON.parse(await import_promises3.default.readFile(valueOrPath, "utf8")) : valueOrPath;
|
|
1169
|
+
return (0, import_strategyReleaseEvidence.verifyStrategyEvidenceMarkerEnvelope)(value);
|
|
1170
|
+
};
|
|
1171
|
+
var writeJsonAtomic = async (filePath, value) => {
|
|
1172
|
+
const temporaryPath = `${filePath}.${process.pid}.${Date.now()}.tmp`;
|
|
1173
|
+
await import_promises3.default.mkdir(import_node_path3.default.dirname(filePath), { recursive: true });
|
|
1174
|
+
await import_promises3.default.writeFile(
|
|
1175
|
+
temporaryPath,
|
|
1176
|
+
`${canonicalStrategyReleaseJson(value)}
|
|
1177
|
+
`,
|
|
1178
|
+
"utf8"
|
|
1179
|
+
);
|
|
1180
|
+
await import_promises3.default.rename(temporaryPath, filePath);
|
|
1181
|
+
};
|
|
1182
|
+
var buildStrategyLiveDiagnosis = (input) => {
|
|
1183
|
+
const minimumClosedTrades = input.minimumClosedTrades ?? 20;
|
|
1184
|
+
const minimumParityRatio = input.minimumParityRatio ?? 0.95;
|
|
1185
|
+
const maximumOrderFailureRate = input.maximumOrderFailureRate ?? 0.05;
|
|
1186
|
+
const minimumRegimeCoverage = input.minimumRegimeCoverage ?? 0.5;
|
|
1187
|
+
const hasExplicitRiskScale = input.riskScaleComparable != null;
|
|
1188
|
+
const comparisonObservedDrawdown = hasExplicitRiskScale ? input.riskScaleComparable ? input.normalizedObservedDrawdown ?? null : null : input.observedDrawdown;
|
|
1189
|
+
const runtimeDivergence = !input.lineageComparable || input.parityRatio != null && input.parityRatio < minimumParityRatio || input.orderFailureRate != null && input.orderFailureRate > maximumOrderFailureRate;
|
|
1190
|
+
const insufficient = input.closedTrades < minimumClosedTrades || input.parityRatio == null || comparisonObservedDrawdown == null || input.historicalDrawdownP95 == null;
|
|
1191
|
+
const expected = !runtimeDivergence && !insufficient && comparisonObservedDrawdown <= input.historicalDrawdownP95;
|
|
1192
|
+
const verdict = runtimeDivergence ? "RUNTIME_DIVERGENCE" : insufficient ? "INSUFFICIENT_EVIDENCE" : expected ? "EXPECTED_DRAWDOWN" : "GENERALIZATION_FAILURE";
|
|
1193
|
+
const attributionIncomplete = input.rawCoreExpectancyDelta == null || input.aiGateAddedValue == null || input.regimeCoverage == null;
|
|
1194
|
+
const attributedVerdict = verdict === "GENERALIZATION_FAILURE" && attributionIncomplete ? "INSUFFICIENT_EVIDENCE" : verdict;
|
|
1195
|
+
const subtype = attributedVerdict !== "GENERALIZATION_FAILURE" ? null : input.rawCoreExpectancyDelta < 0 && input.aiGateAddedValue >= 0 ? "RAW_CORE_DECAY" : input.aiGateAddedValue < 0 ? "AI_GATE_FAILURE" : input.regimeCoverage < minimumRegimeCoverage ? "REGIME_SHIFT" : (input.overfitProbability ?? 0) >= 0.5 ? "SUSPECTED_HISTORICAL_OVERFIT" : "RAW_CORE_DECAY";
|
|
1196
|
+
const explanation = attributedVerdict === "RUNTIME_DIVERGENCE" ? "Runtime cannot be compared economically until lineage, replay parity, and order execution agree with the frozen composition." : attributedVerdict === "EXPECTED_DRAWDOWN" ? "The observed drawdown is within the preregistered equal-length historical drawdown envelope." : attributedVerdict === "GENERALIZATION_FAILURE" ? `Runtime is comparable, but the observed drawdown exceeds the historical envelope (${subtype}).` : attributionIncomplete && verdict === "GENERALIZATION_FAILURE" ? "The drawdown breaches its historical envelope, but matched shadow raw-core, deterministic-gate, or causal regime evidence is missing for attribution." : "There are not enough comparable closed trades and historical drawdown observations for attribution.";
|
|
1197
|
+
const recommendations = attributedVerdict === "RUNTIME_DIVERGENCE" ? [
|
|
1198
|
+
"Resolve lineage and replay parity mismatches before evaluating strategy economics.",
|
|
1199
|
+
"Inspect order failures and execution residuals against the frozen execution model."
|
|
1200
|
+
] : attributedVerdict === "EXPECTED_DRAWDOWN" ? [
|
|
1201
|
+
"Keep the frozen composition unchanged and continue prospective evidence collection."
|
|
1202
|
+
] : attributedVerdict === "GENERALIZATION_FAILURE" ? [
|
|
1203
|
+
"Do not retune the live cohort; open a new immutable research lineage.",
|
|
1204
|
+
"Compare raw-core decay, AI-gate added value, and causal regime coverage."
|
|
1205
|
+
] : [
|
|
1206
|
+
attributionIncomplete && verdict === "GENERALIZATION_FAILURE" ? "Collect matched shadow raw-core, deterministic-gate, and causal regime evidence without changing the composition." : "Collect more verified prospective outcomes without changing the composition."
|
|
1207
|
+
];
|
|
1208
|
+
return {
|
|
1209
|
+
schema: import_types.STRATEGY_LIVE_DIAGNOSIS_SCHEMA,
|
|
1210
|
+
strategy: input.strategy,
|
|
1211
|
+
compositionId: input.compositionId,
|
|
1212
|
+
createdAt: input.createdAt,
|
|
1213
|
+
verdict: attributedVerdict,
|
|
1214
|
+
subtype,
|
|
1215
|
+
confidence: runtimeDivergence || input.closedTrades >= 50 ? "high" : input.closedTrades >= 20 ? "medium" : "low",
|
|
1216
|
+
evidence: {
|
|
1217
|
+
lineageComparable: input.lineageComparable,
|
|
1218
|
+
riskScaleComparable: input.riskScaleComparable ?? false,
|
|
1219
|
+
releaseMaxLossValue: input.releaseMaxLossValue ?? null,
|
|
1220
|
+
runtimeMaxLossValue: input.runtimeMaxLossValue ?? null,
|
|
1221
|
+
riskScaleRatio: input.riskScaleRatio ?? null,
|
|
1222
|
+
parityRatio: input.parityRatio,
|
|
1223
|
+
orderFailureRate: input.orderFailureRate,
|
|
1224
|
+
observedDrawdown: input.observedDrawdown,
|
|
1225
|
+
normalizedObservedDrawdown: input.normalizedObservedDrawdown ?? null,
|
|
1226
|
+
historicalDrawdownP95: input.historicalDrawdownP95,
|
|
1227
|
+
historicalDrawdownMaximum: input.historicalDrawdownMaximum,
|
|
1228
|
+
closedTrades: input.closedTrades,
|
|
1229
|
+
rawCoreExpectancyDelta: input.rawCoreExpectancyDelta,
|
|
1230
|
+
aiGateAddedValue: input.aiGateAddedValue,
|
|
1231
|
+
regimeCoverage: input.regimeCoverage,
|
|
1232
|
+
overfitProbability: input.overfitProbability
|
|
1233
|
+
},
|
|
1234
|
+
explanation,
|
|
1235
|
+
recommendations
|
|
1236
|
+
};
|
|
1237
|
+
};
|
|
1238
|
+
var buildStrategyLiveDiagnosisFromScorecard = ({
|
|
1239
|
+
manifest,
|
|
1240
|
+
scorecard,
|
|
1241
|
+
days
|
|
1242
|
+
}) => {
|
|
1243
|
+
const rolling = scorecard.rolling.find((entry) => entry.days === days);
|
|
1244
|
+
const envelope = manifest.monitoring.drawdownEnvelopes.find(
|
|
1245
|
+
(entry) => entry.days === days
|
|
1246
|
+
);
|
|
1247
|
+
const orderFailureRate = scorecard.funnel.orderAttempts ? scorecard.funnel.orderFailures / scorecard.funnel.orderAttempts : null;
|
|
1248
|
+
const lineage = scorecard.lineage;
|
|
1249
|
+
const lineageComparable = scorecard.parity.lineageReason == null && lineage?.complete === true && lineage.conflicts === false && lineage.compositionId === manifest.composition.compositionId && lineage.gitDirty === false && lineage.gitSha === manifest.composition.gitSha && lineage.configFingerprint === manifest.composition.runtimeConfigFingerprint && lineage.gateFingerprint === manifest.composition.gateFingerprint && lineage.contextFingerprint === manifest.composition.runtimeContextFingerprint;
|
|
1250
|
+
const releaseMaxLossValue = manifest.composition.maxLossValue;
|
|
1251
|
+
const runtimeMaxLossValue = lineage?.maxLossValue ?? null;
|
|
1252
|
+
const riskScaleComparable = Number.isFinite(releaseMaxLossValue) && releaseMaxLossValue > 0 && runtimeMaxLossValue != null && Number.isFinite(runtimeMaxLossValue) && runtimeMaxLossValue > 0;
|
|
1253
|
+
const riskScaleRatio = riskScaleComparable ? runtimeMaxLossValue / releaseMaxLossValue : null;
|
|
1254
|
+
const normalizedObservedDrawdown = rolling != null && riskScaleRatio != null ? rolling.maxDrawdown / riskScaleRatio : null;
|
|
1255
|
+
const normalizeRuntimeEconomics = (value) => value != null && riskScaleRatio != null ? value / riskScaleRatio : null;
|
|
1256
|
+
return buildStrategyLiveDiagnosis({
|
|
1257
|
+
strategy: manifest.strategy,
|
|
1258
|
+
compositionId: manifest.composition.compositionId,
|
|
1259
|
+
createdAt: scorecard.generatedAt,
|
|
1260
|
+
lineageComparable,
|
|
1261
|
+
riskScaleComparable,
|
|
1262
|
+
releaseMaxLossValue,
|
|
1263
|
+
runtimeMaxLossValue,
|
|
1264
|
+
riskScaleRatio,
|
|
1265
|
+
parityRatio: scorecard.parity.ratio,
|
|
1266
|
+
orderFailureRate,
|
|
1267
|
+
observedDrawdown: rolling?.maxDrawdown ?? null,
|
|
1268
|
+
normalizedObservedDrawdown,
|
|
1269
|
+
historicalDrawdownP95: envelope?.p95 ?? null,
|
|
1270
|
+
historicalDrawdownMaximum: envelope?.maximum ?? null,
|
|
1271
|
+
closedTrades: rolling?.closedTrades ?? 0,
|
|
1272
|
+
rawCoreExpectancyDelta: scorecard.prospective?.rawCoreExpectancy != null && manifest.monitoring.rawCoreExpectancy != null ? normalizeRuntimeEconomics(scorecard.prospective.rawCoreExpectancy) - manifest.monitoring.rawCoreExpectancy : null,
|
|
1273
|
+
aiGateAddedValue: scorecard.prospective?.rawCoreExpectancy != null && scorecard.prospective.aiGateExpectancy != null ? normalizeRuntimeEconomics(scorecard.prospective.aiGateExpectancy) - normalizeRuntimeEconomics(scorecard.prospective.rawCoreExpectancy) : null,
|
|
1274
|
+
regimeCoverage: scorecard.prospective?.regimeCoverage ?? null,
|
|
1275
|
+
overfitProbability: manifest.monitoring.overfitProbability,
|
|
1276
|
+
minimumClosedTrades: manifest.monitoring.minimumProspectiveClosedTrades,
|
|
1277
|
+
minimumParityRatio: manifest.monitoring.minimumParityRatio,
|
|
1278
|
+
maximumOrderFailureRate: manifest.monitoring.maximumOrderFailureRate,
|
|
1279
|
+
minimumRegimeCoverage: manifest.monitoring.minimumRegimeCoverage
|
|
1280
|
+
});
|
|
1281
|
+
};
|
|
1282
|
+
var publishStrategyLiveDiagnosis = async ({
|
|
1283
|
+
rootDir,
|
|
1284
|
+
diagnosis,
|
|
1285
|
+
sourceArtifacts,
|
|
1286
|
+
composition
|
|
1287
|
+
}) => {
|
|
1288
|
+
const diagnosisSha256 = strategyReleaseSha256(diagnosis);
|
|
1289
|
+
const diagnosisId = `${safeSegment2(diagnosis.strategy, "strategy")}_${compactTimestamp(diagnosis.createdAt)}_${diagnosisSha256.slice(0, 16)}`;
|
|
1290
|
+
const envelope = {
|
|
1291
|
+
schema: "tradejs-strategy-live-diagnosis-envelope/v1",
|
|
1292
|
+
diagnosisId,
|
|
1293
|
+
diagnosisSha256,
|
|
1294
|
+
diagnosis
|
|
1295
|
+
};
|
|
1296
|
+
const common = {
|
|
1297
|
+
timestamp: diagnosis.createdAt,
|
|
1298
|
+
artifactId: diagnosisId,
|
|
1299
|
+
artifactSha256: diagnosisSha256,
|
|
1300
|
+
compositionId: diagnosis.compositionId,
|
|
1301
|
+
...composition ? {
|
|
1302
|
+
gitSha: composition.gitSha,
|
|
1303
|
+
configFingerprint: composition.runtimeConfigFingerprint,
|
|
1304
|
+
gateFingerprint: composition.gateFingerprint,
|
|
1305
|
+
contextFingerprint: composition.runtimeContextFingerprint,
|
|
1306
|
+
maxLossValue: composition.maxLossValue
|
|
1307
|
+
} : {}
|
|
1308
|
+
};
|
|
1309
|
+
const markers = [
|
|
1310
|
+
...diagnosis.evidence.runtimeMaxLossValue != null && diagnosis.evidence.runtimeMaxLossValue !== diagnosis.evidence.releaseMaxLossValue ? [
|
|
1311
|
+
{
|
|
1312
|
+
...common,
|
|
1313
|
+
id: `${diagnosisId}:loss`,
|
|
1314
|
+
type: "L",
|
|
1315
|
+
label: "Trade loss value changed",
|
|
1316
|
+
summary: `MAX_LOSS_VALUE ${diagnosis.evidence.runtimeMaxLossValue}`,
|
|
1317
|
+
maxLossValue: diagnosis.evidence.runtimeMaxLossValue,
|
|
1318
|
+
severity: "info"
|
|
1319
|
+
}
|
|
1320
|
+
] : [],
|
|
1321
|
+
...diagnosis.verdict === "RUNTIME_DIVERGENCE" ? [
|
|
1322
|
+
{
|
|
1323
|
+
...common,
|
|
1324
|
+
id: `${diagnosisId}:parity`,
|
|
1325
|
+
type: "P",
|
|
1326
|
+
label: "Runtime parity mismatch",
|
|
1327
|
+
summary: diagnosis.explanation,
|
|
1328
|
+
severity: "blocking"
|
|
1329
|
+
}
|
|
1330
|
+
] : [],
|
|
1331
|
+
{
|
|
1332
|
+
...common,
|
|
1333
|
+
id: `${diagnosisId}:recommendation`,
|
|
1334
|
+
type: "R",
|
|
1335
|
+
label: "Live diagnosis",
|
|
1336
|
+
summary: `${diagnosis.verdict}: ${diagnosis.explanation}`,
|
|
1337
|
+
severity: diagnosis.verdict === "EXPECTED_DRAWDOWN" ? "info" : "warning"
|
|
1338
|
+
}
|
|
1339
|
+
];
|
|
1340
|
+
const markerEnvelope = (0, import_strategyReleaseEvidence.createStrategyEvidenceMarkerEnvelope)({
|
|
1341
|
+
strategy: diagnosis.strategy,
|
|
1342
|
+
createdAt: diagnosis.createdAt,
|
|
1343
|
+
markers,
|
|
1344
|
+
sourceArtifacts
|
|
1345
|
+
});
|
|
1346
|
+
const diagnosisPath = import_node_path3.default.join(
|
|
1347
|
+
rootDir,
|
|
1348
|
+
"diagnoses",
|
|
1349
|
+
safeSegment2(diagnosis.strategy, "strategy"),
|
|
1350
|
+
`${diagnosisId}.json`
|
|
1351
|
+
);
|
|
1352
|
+
const markerPath = import_node_path3.default.join(
|
|
1353
|
+
rootDir,
|
|
1354
|
+
"markers",
|
|
1355
|
+
safeSegment2(diagnosis.strategy, "strategy"),
|
|
1356
|
+
`${markerEnvelope.artifactId}.json`
|
|
1357
|
+
);
|
|
1358
|
+
await Promise.all([
|
|
1359
|
+
writeJsonAtomic(diagnosisPath, envelope),
|
|
1360
|
+
writeJsonAtomic(markerPath, markerEnvelope)
|
|
1361
|
+
]);
|
|
1362
|
+
await verifyStrategyEvidenceMarkerEnvelope(markerPath);
|
|
1363
|
+
return { diagnosisPath, markerPath, envelope, markerEnvelope };
|
|
1364
|
+
};
|
|
1365
|
+
|
|
607
1366
|
// src/scripts/runtimeScorecard.ts
|
|
608
|
-
import_args.default.option("runtimeEvidence", "Verified runtime evidence JSON");
|
|
609
|
-
import_args.default.option("replayEvidence", "Replay runtime evidence JSON");
|
|
610
|
-
import_args.default.option("calibration", "Execution calibration JSON");
|
|
611
|
-
import_args.default.option("historyDir", "Directory containing verified evidence bundles");
|
|
1367
|
+
import_args.default.option(["u", "runtimeEvidence"], "Verified runtime evidence JSON");
|
|
1368
|
+
import_args.default.option(["r", "replayEvidence"], "Replay runtime evidence JSON");
|
|
1369
|
+
import_args.default.option(["c", "calibration"], "Execution calibration JSON");
|
|
612
1370
|
import_args.default.option(
|
|
613
|
-
"
|
|
1371
|
+
["q", "prospectiveEvidence"],
|
|
1372
|
+
"Verified prospective raw-core/gate/regime summary JSON"
|
|
1373
|
+
);
|
|
1374
|
+
import_args.default.option(["s", "strategy"], "Exact strategy to isolate in the scorecard");
|
|
1375
|
+
import_args.default.option(
|
|
1376
|
+
["H", "historyDir"],
|
|
1377
|
+
"Directory containing verified evidence bundles"
|
|
1378
|
+
);
|
|
1379
|
+
import_args.default.option(
|
|
1380
|
+
["E", "evidenceDir"],
|
|
614
1381
|
"Local evidence storage root used for processing receipts",
|
|
615
1382
|
process.env.RUNTIME_EVIDENCE_LOCAL_DIR || "data/runtime-evidence"
|
|
616
1383
|
);
|
|
617
1384
|
import_args.default.option(
|
|
618
|
-
"deployment",
|
|
1385
|
+
["d", "deployment"],
|
|
619
1386
|
"Deployment id used for processing receipts",
|
|
620
1387
|
process.env.RUNTIME_EVIDENCE_DEPLOYMENT_ID || "production"
|
|
621
1388
|
);
|
|
622
|
-
import_args.default.option(
|
|
1389
|
+
import_args.default.option(
|
|
1390
|
+
["M", "markProcessed"],
|
|
1391
|
+
"Write a processing receipt on success",
|
|
1392
|
+
false
|
|
1393
|
+
);
|
|
623
1394
|
import_args.default.option(
|
|
624
1395
|
["P", "minimumParityRatio"],
|
|
625
1396
|
"Minimum acceptable replay parity",
|
|
@@ -637,13 +1408,28 @@ import_args.default.option(
|
|
|
637
1408
|
);
|
|
638
1409
|
import_args.default.option(["X", "minimumExpectancy"], "Minimum acceptable 7d expectancy", 0);
|
|
639
1410
|
import_args.default.option(["o", "out"], "Output JSON path", "output/runtime-scorecard.json");
|
|
1411
|
+
import_args.default.option(
|
|
1412
|
+
["m", "releaseManifest"],
|
|
1413
|
+
"Verified strategy release envelope JSON"
|
|
1414
|
+
);
|
|
1415
|
+
import_args.default.option(["D", "diagnosisDays"], "Equal-length release drawdown window", 7);
|
|
1416
|
+
import_args.default.option(
|
|
1417
|
+
["z", "strategyReleaseRoot"],
|
|
1418
|
+
"Publish advisory diagnosis and immutable chart markers under this root"
|
|
1419
|
+
);
|
|
640
1420
|
var flags = import_args.default.parse(process.argv);
|
|
641
1421
|
var projectRoot = String(process.env.PROJECT_CWD || process.cwd()).trim() || process.cwd();
|
|
642
1422
|
var resolveOptionalPath = (value) => {
|
|
643
1423
|
const raw = String(value ?? "").trim();
|
|
644
|
-
return raw ?
|
|
1424
|
+
return raw ? import_node_path4.default.resolve(projectRoot, raw) : null;
|
|
1425
|
+
};
|
|
1426
|
+
var readJson = async (filePath) => filePath ? JSON.parse(await import_promises4.default.readFile(filePath, "utf8")) : void 0;
|
|
1427
|
+
var assertReportType = (artifact, expected, label) => {
|
|
1428
|
+
if (artifact != null && (!artifact || typeof artifact !== "object" || Array.isArray(artifact) || artifact.reportType !== expected)) {
|
|
1429
|
+
throw new Error(`${label} must be a ${expected} artifact`);
|
|
1430
|
+
}
|
|
645
1431
|
};
|
|
646
|
-
var
|
|
1432
|
+
var sha256File = async (filePath) => (0, import_node_crypto3.createHash)("sha256").update(await import_promises4.default.readFile(filePath)).digest("hex");
|
|
647
1433
|
var loadHistoryArtifacts = async (historyDir) => {
|
|
648
1434
|
if (!historyDir) return [];
|
|
649
1435
|
const bundleDirs = await discoverRuntimeEvidenceBundles(historyDir);
|
|
@@ -660,48 +1446,114 @@ var runtimeScorecard = async () => {
|
|
|
660
1446
|
}
|
|
661
1447
|
const replayEvidencePath = resolveOptionalPath(flags.replayEvidence);
|
|
662
1448
|
const calibrationPath = resolveOptionalPath(flags.calibration);
|
|
1449
|
+
const prospectiveEvidencePath = resolveOptionalPath(
|
|
1450
|
+
flags.prospectiveEvidence
|
|
1451
|
+
);
|
|
663
1452
|
const historyDir = resolveOptionalPath(flags.historyDir);
|
|
1453
|
+
const releaseManifestPath = resolveOptionalPath(flags.releaseManifest);
|
|
664
1454
|
const [
|
|
665
1455
|
runtimeArtifact,
|
|
666
1456
|
replayEvidenceArtifact,
|
|
667
1457
|
calibrationArtifact,
|
|
1458
|
+
prospectiveEvidenceArtifact,
|
|
668
1459
|
history
|
|
669
1460
|
] = await Promise.all([
|
|
670
1461
|
readJson(runtimeEvidencePath),
|
|
671
1462
|
readJson(replayEvidencePath),
|
|
672
1463
|
readJson(calibrationPath),
|
|
1464
|
+
readJson(prospectiveEvidencePath),
|
|
673
1465
|
loadHistoryArtifacts(historyDir)
|
|
674
1466
|
]);
|
|
1467
|
+
assertReportType(runtimeArtifact, "runtime-evidence", "runtimeEvidence");
|
|
1468
|
+
assertReportType(
|
|
1469
|
+
replayEvidenceArtifact,
|
|
1470
|
+
"replay-runtime-evidence",
|
|
1471
|
+
"replayEvidence"
|
|
1472
|
+
);
|
|
1473
|
+
assertReportType(calibrationArtifact, "execution-calibration", "calibration");
|
|
1474
|
+
assertReportType(
|
|
1475
|
+
prospectiveEvidenceArtifact,
|
|
1476
|
+
"strategy-prospective-evidence",
|
|
1477
|
+
"prospectiveEvidence"
|
|
1478
|
+
);
|
|
1479
|
+
const verifiedRelease = releaseManifestPath ? await verifyStrategyReleaseEnvelope(releaseManifestPath) : null;
|
|
1480
|
+
const requestedStrategy = String(flags.strategy ?? "").trim() || null;
|
|
1481
|
+
if (verifiedRelease && requestedStrategy && requestedStrategy !== verifiedRelease.manifest.strategy) {
|
|
1482
|
+
throw new Error(
|
|
1483
|
+
`Requested strategy ${requestedStrategy} does not match release ${verifiedRelease.manifest.strategy}`
|
|
1484
|
+
);
|
|
1485
|
+
}
|
|
1486
|
+
const scorecardStrategy = requestedStrategy ?? verifiedRelease?.manifest.strategy ?? null;
|
|
675
1487
|
const scorecard = buildRuntimeScorecard({
|
|
676
1488
|
runtimeArtifact,
|
|
677
1489
|
replayEvidenceArtifact,
|
|
678
1490
|
calibrationArtifact,
|
|
1491
|
+
prospectiveEvidenceArtifact,
|
|
679
1492
|
historyRuntimeArtifacts: history,
|
|
680
1493
|
thresholds: {
|
|
681
1494
|
minimumParityRatio: Number(flags.minimumParityRatio),
|
|
682
1495
|
maximumSlippageResidualBps: Number(flags.maximumSlippageResidualBps),
|
|
683
1496
|
minimumClosedTrades: Number(flags.minimumClosedTrades),
|
|
684
1497
|
minimumExpectancy: Number(flags.minimumExpectancy)
|
|
685
|
-
}
|
|
1498
|
+
},
|
|
1499
|
+
strategy: scorecardStrategy,
|
|
1500
|
+
llmComparatorPolicy: verifiedRelease?.manifest.prospective.llmComparatorPolicy ?? "ai_approved_only"
|
|
686
1501
|
});
|
|
687
|
-
const outPath =
|
|
1502
|
+
const outPath = import_node_path4.default.resolve(projectRoot, String(flags.out));
|
|
688
1503
|
const markdownPath = outPath.replace(/\.json$/i, "") + ".md";
|
|
689
|
-
await
|
|
1504
|
+
await import_promises4.default.mkdir(import_node_path4.default.dirname(outPath), { recursive: true });
|
|
690
1505
|
await Promise.all([
|
|
691
|
-
|
|
1506
|
+
import_promises4.default.writeFile(outPath, `${JSON.stringify(scorecard, null, 2)}
|
|
692
1507
|
`, "utf8"),
|
|
693
|
-
|
|
1508
|
+
import_promises4.default.writeFile(
|
|
694
1509
|
markdownPath,
|
|
695
1510
|
formatRuntimeScorecardMarkdown(scorecard),
|
|
696
1511
|
"utf8"
|
|
697
1512
|
)
|
|
698
1513
|
]);
|
|
1514
|
+
const diagnosis = verifiedRelease ? buildStrategyLiveDiagnosisFromScorecard({
|
|
1515
|
+
manifest: verifiedRelease.manifest,
|
|
1516
|
+
scorecard,
|
|
1517
|
+
days: Number(flags.diagnosisDays)
|
|
1518
|
+
}) : null;
|
|
1519
|
+
const diagnosisPath = diagnosis ? outPath.replace(/\.json$/i, "") + ".diagnosis.json" : null;
|
|
1520
|
+
const strategyReleaseRoot = resolveOptionalPath(flags.strategyReleaseRoot);
|
|
1521
|
+
if (diagnosis && diagnosisPath) {
|
|
1522
|
+
await import_promises4.default.writeFile(
|
|
1523
|
+
diagnosisPath,
|
|
1524
|
+
`${JSON.stringify(diagnosis, null, 2)}
|
|
1525
|
+
`,
|
|
1526
|
+
"utf8"
|
|
1527
|
+
);
|
|
1528
|
+
if (strategyReleaseRoot) {
|
|
1529
|
+
const sourcePaths = [
|
|
1530
|
+
runtimeEvidencePath,
|
|
1531
|
+
replayEvidencePath,
|
|
1532
|
+
calibrationPath,
|
|
1533
|
+
prospectiveEvidencePath,
|
|
1534
|
+
releaseManifestPath,
|
|
1535
|
+
outPath
|
|
1536
|
+
].filter((value) => Boolean(value));
|
|
1537
|
+
await publishStrategyLiveDiagnosis({
|
|
1538
|
+
rootDir: strategyReleaseRoot,
|
|
1539
|
+
diagnosis,
|
|
1540
|
+
composition: verifiedRelease?.manifest.composition,
|
|
1541
|
+
sourceArtifacts: await Promise.all(
|
|
1542
|
+
sourcePaths.map(async (sourcePath) => ({
|
|
1543
|
+
artifactId: import_node_path4.default.basename(sourcePath),
|
|
1544
|
+
path: sourcePath,
|
|
1545
|
+
sha256: await sha256File(sourcePath)
|
|
1546
|
+
}))
|
|
1547
|
+
)
|
|
1548
|
+
});
|
|
1549
|
+
}
|
|
1550
|
+
}
|
|
699
1551
|
let receiptPath2 = null;
|
|
700
1552
|
if (flags.markProcessed) {
|
|
701
1553
|
receiptPath2 = await markRuntimeEvidenceBundleProcessed({
|
|
702
|
-
evidenceRoot:
|
|
1554
|
+
evidenceRoot: import_node_path4.default.resolve(projectRoot, String(flags.evidenceDir)),
|
|
703
1555
|
deploymentId: String(flags.deployment),
|
|
704
|
-
bundleDir:
|
|
1556
|
+
bundleDir: import_node_path4.default.dirname(runtimeEvidencePath),
|
|
705
1557
|
scorecardPath: outPath
|
|
706
1558
|
});
|
|
707
1559
|
}
|
|
@@ -716,7 +1568,9 @@ var runtimeScorecard = async () => {
|
|
|
716
1568
|
execution: scorecard.execution,
|
|
717
1569
|
rolling: scorecard.rolling,
|
|
718
1570
|
reactions: scorecard.reactions,
|
|
719
|
-
receiptPath: receiptPath2
|
|
1571
|
+
receiptPath: receiptPath2,
|
|
1572
|
+
diagnosisPath,
|
|
1573
|
+
diagnosisVerdict: diagnosis?.verdict ?? null
|
|
720
1574
|
},
|
|
721
1575
|
null,
|
|
722
1576
|
2
|