create-tradejs 3.1.22 → 3.1.23

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.
Files changed (40) hide show
  1. package/README.md +19 -3
  2. package/dist/index.js +36 -5
  3. package/dist/skill-bundle/.codex/skills/ai-train-local-research/SKILL.md +596 -0
  4. package/dist/skill-bundle/.codex/skills/ai-train-local-research/references/gate-ablation.md +324 -0
  5. package/dist/skill-bundle/.codex/skills/ai-train-local-research/references/reporting.md +227 -0
  6. package/dist/skill-bundle/.codex/skills/ai-train-local-research/scripts/ai-gate-ablation.mjs +5082 -0
  7. package/dist/skill-bundle/.codex/skills/ai-train-local-research/scripts/ai-gate-ablation.test.mjs +1170 -0
  8. package/dist/skill-bundle/.codex/skills/backtest-config-redis/SKILL.md +17 -0
  9. package/dist/skill-bundle/.codex/skills/backtest-config-redis/scripts/get_backtest_config.sh +21 -0
  10. package/dist/skill-bundle/.codex/skills/runtime-parity-mismatch-analysis/SKILL.md +146 -0
  11. package/dist/skill-bundle/.codex/skills/save-strategy-config-from-backtest/SKILL.md +58 -0
  12. package/dist/skill-bundle/.codex/skills/save-strategy-config-from-backtest/agents/openai.yaml +4 -0
  13. package/dist/skill-bundle/.codex/skills/strategy-backtest-research/SKILL.md +334 -0
  14. package/dist/skill-bundle/.codex/skills/strategy-backtest-research/references/research-notes.md +247 -0
  15. package/dist/skill-bundle/.codex/skills/strategy-backtest-research/scripts/backtest-run-metrics.mjs +647 -0
  16. package/dist/skill-bundle/.codex/skills/strategy-backtest-research/scripts/backtest-run-metrics.test.mjs +321 -0
  17. package/dist/skill-bundle/.codex/skills/strategy-backtest-research/scripts/fast-ai-export-metrics.mjs +744 -0
  18. package/dist/skill-bundle/.codex/skills/strategy-backtest-research/scripts/fast-ai-export-metrics.test.mjs +553 -0
  19. package/dist/skill-bundle/.codex/skills/strategy-backtest-research/scripts/research-notes-check.mjs +125 -0
  20. package/dist/skill-bundle/.codex/skills/strategy-improvement-research/SKILL.md +18 -1
  21. package/dist/skill-bundle/.codex/skills/strategy-release/SKILL.md +22 -0
  22. package/dist/skill-bundle/.codex/skills/strategy-release/agents/openai.yaml +4 -0
  23. package/dist/skill-bundle/.codex/skills/strategy-release/references/diagnose-live.md +126 -0
  24. package/dist/skill-bundle/.codex/skills/strategy-release/references/direction-policy.md +141 -0
  25. package/dist/skill-bundle/.codex/skills/strategy-release/references/directional-parameter-split.md +93 -0
  26. package/dist/skill-bundle/.codex/skills/strategy-release/references/evidence-limitations.md +76 -0
  27. package/dist/skill-bundle/.codex/skills/strategy-release/references/evidence-retention.md +157 -0
  28. package/dist/skill-bundle/.codex/skills/strategy-release/references/historical-hypothesis-audit.md +163 -0
  29. package/dist/skill-bundle/.codex/skills/strategy-release/references/professional-research-loop.md +198 -0
  30. package/dist/skill-bundle/.codex/skills/strategy-release/references/release-workflow.md +755 -0
  31. package/dist/skill-bundle/.codex/skills/strategy-release/references/research-objective.md +255 -0
  32. package/dist/skill-bundle/.codex/skills/strategy-release/references/verdict-contract.md +200 -0
  33. package/dist/skill-bundle/.codex/skills/strategy-release/scripts/direction-policy-checkpoint.mjs +137 -0
  34. package/dist/skill-bundle/.codex/skills/strategy-release/scripts/direction-policy-checkpoint.test.mjs +85 -0
  35. package/dist/skill-bundle/.codex/skills/strategy-release/scripts/directional-parameter-checkpoint.mjs +149 -0
  36. package/dist/skill-bundle/.codex/skills/strategy-release/scripts/directional-parameter-checkpoint.test.mjs +120 -0
  37. package/dist/skill-bundle/.codex/skills/strategy-release/scripts/release-progress-checkpoint.mjs +621 -0
  38. package/dist/skill-bundle/.codex/skills/strategy-release/scripts/release-progress-checkpoint.test.mjs +349 -0
  39. package/dist/skill-bundle/.codex/tradejs-skill-bundle.json +44 -3
  40. package/package.json +1 -1
@@ -0,0 +1,255 @@
1
+ # Release research objective and candidate revalidation
2
+
3
+ ## Contents
4
+
5
+ 1. Version the objective
6
+ 2. Use a hierarchical objective
7
+ 3. Condition terminal evidence on support
8
+ 4. Revalidate historical candidates
9
+ 5. Keep one global trial ledger
10
+ 6. Required artifacts
11
+ 7. Progress checkpoint payload
12
+
13
+ ## 1. Version the objective
14
+
15
+ Every release task starts a new immutable lineage unless the user explicitly
16
+ names an existing lineage to continue. Audit prior work, but never count a
17
+ prior round, rescue child, gate ablation, report, or verdict as work completed
18
+ inside the new lineage.
19
+
20
+ Before the baseline, persist a `tradejs-release-objective/v2` artifact. Its
21
+ file SHA-256 is the `objectiveFingerprint` for the lineage. Freeze:
22
+
23
+ - the return unit and risk normalization;
24
+ - cost/slippage and adverse-cost scenarios;
25
+ - development, tuning, sealed-test, walk-forward, and terminal partitions;
26
+ - eligibility gates, Pareto dimensions, and tie-break rules;
27
+ - independent-event grouping and support bands;
28
+ - drawdown, concentration, capacity, and execution guardrails;
29
+ - the global trial-ledger reference used for DSR/PBO.
30
+
31
+ Any change to these fields creates a new objective fingerprint. Do not reuse a
32
+ ranking, verdict, or selected candidate from another fingerprint.
33
+
34
+ ## 2. Use a hierarchical objective
35
+
36
+ Do not maximize full-period PnL, Sharpe, win rate, or recent-window PnL in
37
+ isolation. Use this hierarchy:
38
+
39
+ 1. **Integrity and causality.** Complete reconciled evidence, signal-time
40
+ inputs, point-in-time execution semantics, and comparable risk/cost units
41
+ are mandatory.
42
+ 2. **Economic edge.** Require positive aggregate expectancy per unit risk after
43
+ normal costs and PF above 1 on the maximum-covered historical evaluation.
44
+ Apply the same check to every active approved side; an explicitly suppressed
45
+ side remains a zero row. Nested calendar slices do not repeat this gate.
46
+ 3. **Selection adjustment.** Report probabilistic and deflated Sharpe using
47
+ non-IID-aware returns and the complete effective trial count. A raw
48
+ per-trade annualized Sharpe is diagnostic, not selection authority.
49
+ 4. **Risk robustness.** Enforce preregistered realized MaxDD, tail/CVaR,
50
+ recovery, time-under-water, adverse-cost, concentration, and capacity
51
+ guardrails.
52
+ 5. **Temporal robustness.** Prefer non-overlapping/event-grouped walk-forward
53
+ evidence and regime coverage. Nested 3y/4y/max and recent calendar windows
54
+ are context, not independent confirmations or waiting periods.
55
+ 6. **Executable support.** Require independent events, viable cadence, and an
56
+ event-arrival rate consistent with the strategy thesis.
57
+
58
+ Rank eligible candidates on a Pareto frontier across expectancy/risk, PF,
59
+ deflated Sharpe, MaxDD/recovery, walk-forward stability, cost stress,
60
+ independent support, and cadence. Use a scalar score only as a frozen tie-break,
61
+ never to compensate for a failed integrity or economic gate.
62
+
63
+ Treat win rate only together with payoff ratio and expectancy. Treat maximum
64
+ loss streak, losing-month streak, drawdown duration, and worst rolling returns
65
+ as risk diagnostics and monitoring bounds, not optimization targets. Absolute
66
+ streaks grow mechanically with sample size and cannot fairly rank candidates
67
+ with different cadence.
68
+
69
+ ## 3. Condition terminal evidence on support
70
+
71
+ Always report continuous-run 365d/180d/90d/30d/7d rows, including zero rows,
72
+ but use them to describe current regime and cadence rather than to decide when a
73
+ prospective test may start. Classify each ALL/LONG/SHORT cohort by independent
74
+ event count:
75
+
76
+ - `underpowered`: fewer than 20 independent closed events;
77
+ - `diagnostic`: 20 through 49 independent closed events;
78
+ - `selection_grade`: at least 50 independent closed events.
79
+
80
+ An `underpowered` terminal row is `n/a` for pass/fail. It cannot reject a
81
+ candidate, prove current-market decay, justify another threshold, or force a
82
+ calendar wait. A `diagnostic` row may motivate the one preregistered causal
83
+ recent-direction repair, but cannot reject an otherwise eligible composition by
84
+ itself. A `selection_grade` row may cap a historical `READY_FOR_RUNTIME` claim
85
+ or lower the candidate's Pareto rank, but it does not block an otherwise valid
86
+ `MAX_LOSS_VALUE=1` prospective test. Hard causality, reconciliation, execution,
87
+ maximum-covered economics, and frozen portfolio-risk failures still block it.
88
+
89
+ For sparse strategies also report the last 20/50/100 independent events and
90
+ compare observed calendar cadence with the preregistered historical
91
+ event-arrival distribution. Zero trades are a cadence observation, not a loss;
92
+ classify them as abnormal only when they breach that frozen distribution. The
93
+ next review is triggered by independent events or a risk/parity breach, never
94
+ by waiting 7, 30, or 180 calendar days.
95
+
96
+ ## 4. Revalidate historical candidates
97
+
98
+ Objective changes invalidate old rankings, not the underlying evidence. Before
99
+ inventing new hypotheses, build a deduplicated inventory of every historical
100
+ core, deterministic-gate, direction-policy, rescue, shortlisted, and untested
101
+ candidate. Group semantic duplicates by behavior/config fingerprint.
102
+
103
+ For every reconstructable candidate:
104
+
105
+ 1. Recompute the new objective from retained normalized trades when window,
106
+ universe, costs, execution semantics, source behavior, and risk unit match.
107
+ 2. Otherwise rerun the exact historical behavior on the new frozen contract
108
+ with `--cacheOnly` before ranking it when it could reach the new frontier.
109
+ 3. Record `rescored`, `bridge-rerun`, `rejected`, `partial`, or
110
+ `unreconstructable`, with the source inventory entry, evidence paths, and
111
+ hashes. Use `new-trial-required` for an inventoried behavior that was never
112
+ economically tested; schedule it inside the normal new-candidate budget.
113
+ 4. Put every evaluated behavior in the global trial ledger even when it is no
114
+ longer shortlisted. Re-scoring does not erase the original selection look.
115
+ 5. Preserve exposed tails as exposed. A new lineage or objective fingerprint
116
+ never makes previously viewed data sealed again.
117
+
118
+ Metric-only re-scoring and exact bridge reruns of already-tested behavior do
119
+ not consume the new lineage's 18 causal-candidate slots. They remain part of
120
+ the global multiple-testing count. A changed threshold, feature, direction
121
+ policy, payoff rule, or other behavior is a new candidate and consumes the
122
+ normal family/rescue budget.
123
+
124
+ Finish the revalidation board before freezing the three new causal families.
125
+ An old candidate may become the matched control or finalist under the new
126
+ objective, but the task must still perform its new causal improvement rounds;
127
+ revalidation is evidence-first preparation, not a substitute for new research.
128
+
129
+ ## 5. Keep one global trial ledger
130
+
131
+ The trial ledger spans tasks, lineages, source branches, objective versions,
132
+ core variants, gate variants, directional policies, and rescue attempts.
133
+ Deduplicate exact behavior reruns but retain every distinct selection look.
134
+ Record at least:
135
+
136
+ - candidate/behavior fingerprint and parent family;
137
+ - first-seen lineage and every evaluation lineage;
138
+ - objective fingerprint;
139
+ - development/tuning/test exposure status;
140
+ - return series, sample size, skew, kurtosis, and serial-dependence method;
141
+ - raw, probabilistic, and deflated Sharpe;
142
+ - effective independent trial count and PBO/selection diagnostics.
143
+
144
+ Do not reset DSR/PBO because a new chat, Git commit, research id, or objective
145
+ was created. Repeated research is legitimate only when the ledger makes the
146
+ increasing selection burden explicit.
147
+
148
+ ## 6. Required artifacts
149
+
150
+ Before round 1, persist:
151
+
152
+ ```json
153
+ {
154
+ "schema": "tradejs-release-candidate-revalidation/v2",
155
+ "strategy": "<Strategy>",
156
+ "lineageId": "<new-lineage-id>",
157
+ "objectiveFingerprint": "<objective-artifact-sha256>",
158
+ "historyInventorySha256": "<history-inventory-sha256>",
159
+ "status": "complete",
160
+ "priorCandidateCount": 1,
161
+ "candidates": [
162
+ {
163
+ "historyEntryIndex": 0,
164
+ "disposition": "rescored|bridge-rerun|rejected|partial|unreconstructable|new-trial-required",
165
+ "behaviorFingerprint": "<sha256>",
166
+ "evidence": []
167
+ }
168
+ ],
169
+ "unresolved": [],
170
+ "trialLedger": { "path": "<path>", "sha256": "<sha256>" }
171
+ }
172
+ ```
173
+
174
+ `priorCandidateCount: 0` is valid only when the bound history inventory proves
175
+ that no prior behavior candidate exists. Refactor-only and superseded-duplicate
176
+ entries are excluded; every other deduplicated entry needs exactly one indexed
177
+ disposition. Hash this artifact and reference it from the opportunity map, all
178
+ new round specs/handoffs, the rescue board, selected composition, and final
179
+ decision.
180
+
181
+ ## 7. Progress checkpoint payload
182
+
183
+ Use `tradejs-release-progress-input/v2`. Completion claims are artifact-backed:
184
+
185
+ ```json
186
+ {
187
+ "schema": "tradejs-release-progress-input/v2",
188
+ "strategy": "<Strategy>",
189
+ "lineageId": "<new-lineage-id>",
190
+ "objectiveContract": { "artifact": { "path": "...", "sha256": "..." } },
191
+ "historyAudit": {
192
+ "complete": true,
193
+ "artifact": { "path": "...", "sha256": "..." }
194
+ },
195
+ "candidateRevalidation": {
196
+ "required": true,
197
+ "complete": true,
198
+ "artifact": { "path": "...", "sha256": "..." }
199
+ },
200
+ "baseline": {
201
+ "complete": true,
202
+ "reconciled": true,
203
+ "artifact": { "path": "...", "sha256": "..." }
204
+ },
205
+ "opportunityMap": {
206
+ "complete": true,
207
+ "artifact": { "path": "...", "sha256": "..." }
208
+ },
209
+ "hypothesisPortfolio": {
210
+ "frozen": true,
211
+ "artifact": { "path": "...", "sha256": "..." }
212
+ },
213
+ "families": [
214
+ {
215
+ "id": "<family>",
216
+ "status": "active",
217
+ "roundArtifacts": [
218
+ {
219
+ "round": 1,
220
+ "researchId": "<id>",
221
+ "manifestArtifact": { "path": "...", "sha256": "..." },
222
+ "resultArtifact": { "path": "...", "sha256": "..." },
223
+ "traceArtifacts": [{ "path": "...", "sha256": "..." }],
224
+ "handoffArtifact": { "path": "...", "sha256": "..." }
225
+ }
226
+ ]
227
+ }
228
+ ],
229
+ "rescueBoard": { "complete": false },
230
+ "directionalParameterCheckpoint": { "required": false, "complete": false },
231
+ "directionPolicyCheckpoint": { "required": false, "complete": false },
232
+ "fullAiReport": { "complete": false },
233
+ "chart": { "complete": false },
234
+ "selectedComposition": null,
235
+ "limitations": []
236
+ }
237
+ ```
238
+
239
+ Provide exactly three family entries. `roundArtifacts`, not a caller-supplied
240
+ count, determines completed rounds. Each round requires a completed matching
241
+ core manifest, result, trace declared by that result, and causal handoff whose
242
+ `resultSha256` matches the result file. A family retired before round 3 also
243
+ requires a hashed `retirementArtifact`.
244
+
245
+ A completed rescue board uses `tradejs-release-rescue-board/v2`, binds the same
246
+ strategy/lineage/objective, embeds artifact-backed child evidence, and accounts
247
+ for all three slots through `children` plus bounded `missingSlots` reasons.
248
+
249
+ After the exact chart is frozen, create
250
+ `tradejs-release-selected-composition/v2`. It binds the strategy, lineage,
251
+ candidate id, composition fingerprint, objective fingerprint, historical
252
+ matrix SHA, chart SHA, and core/gate fingerprints. Pass its reference as
253
+ `selectedComposition.artifact`. The checkpoint returns
254
+ `tradejs-release-progress/v2` and copies the verified selected identity into
255
+ its decision output for `strategy-release decide`.
@@ -0,0 +1,200 @@
1
+ # Verdict contract
2
+
3
+ Return exactly one mode-specific verdict. Put the verdict first, then the
4
+ composition/incident identity, evidence completeness, ALL/LONG/SHORT table,
5
+ causal findings, limitations, and approval-safe next action.
6
+
7
+ ## Shared rules
8
+
9
+ - Treat a verdict as an evidence classification, not an authorization token.
10
+ - Keep ALL, LONG, and SHORT statuses separate. A positive aggregate cannot hide
11
+ a failed side, and a negative side cannot be silently disabled. An explicit
12
+ tested `long_only` or `short_only` deterministic-gate policy is allowed, but
13
+ the suppressed raw side and its zero approved row must remain visible.
14
+ - Apply `evidence-first, novelty-second`. A release verdict is incomplete until
15
+ the historical commit/evidence inventory is hashed, every stronger prior
16
+ result is classified and bridged to the current window/universe/config/cost
17
+ contract, and every selected untested historical behavior has a recorded
18
+ disposition. Do not call a weaker new search exhaustive while comparable
19
+ prior evidence remains unexplained.
20
+ - Bind the verdict to one `tradejs-release-objective/v2` fingerprint. Complete
21
+ the historical-candidate revalidation board under that fingerprint and retain
22
+ every distinct old/new behavior in the global trial ledger before ranking a
23
+ finalist.
24
+ - A current gate that approves zero/negligible rows from a profitable raw side
25
+ is incomplete evidence. Do not return a final market-unsuitable verdict until
26
+ the mandatory five-variant side-rescue round has been executed and recorded.
27
+ - A raw aggregate that fails because one side is the dominant loss is not a
28
+ final verdict while the opposite side passes the frozen useful-side rule.
29
+ Complete the five-variant direction-containment checkpoint before rejecting
30
+ the composition.
31
+ - `READY_FOR_RUNTIME` requires a genuinely sealed chronological gate test:
32
+ pocket discovery may know its count and time bounds but not its PnL or feature
33
+ outcomes. A test opened before variant freeze is permanently exposed and can
34
+ support only historical diagnosis/forward-candidate selection in that
35
+ lineage.
36
+ - Treat `MAX_LOSS_VALUE` as risk scale rather than decision-logic identity.
37
+ Preserve its immutable change history, normalize monetary comparisons to the
38
+ release risk unit, and return `INSUFFICIENT_EVIDENCE` when either scale is
39
+ unavailable. Never compare unnormalized dollar drawdowns across risk scales.
40
+ - Treat local Redis config names/ids and deployment/account ids, and
41
+ server credentials as environment binding rather than composition logic.
42
+ Never infer production absence from missing local keys. A locally unbound but
43
+ otherwise verified candidate is `MICRO_FORWARD_READY` with
44
+ `requiresRuntimeBinding=true`; bind and reverify it on the runtime server.
45
+ Activation (`ENABLE`) remains execution-critical and must equal the
46
+ authorized handoff before forward execution.
47
+ - Use aggregate portfolio MaxDD for ALL and side-only realized MaxDD for each
48
+ direction.
49
+ - Report every terminal row, but apply the support classes from
50
+ [research-objective.md](research-objective.md). An underpowered row is `n/a`
51
+ and a diagnostic row cannot reject a composition by itself. A selection-grade
52
+ row may limit historical readiness and candidate rank, but no terminal
53
+ calendar row blocks an otherwise valid risk-1 prospective test. Empty recent
54
+ windows are cadence evidence, not losses.
55
+ - A complete release verdict, including `UNSUITABLE_FOR_CURRENT_MARKET`,
56
+ requires the full window matrix and the complete `$ai-train-local-research`
57
+ report. A negative verdict cannot replace omitted statistics with an artifact
58
+ link or a short rescue leaderboard.
59
+ - Prefer `INSUFFICIENT_EVIDENCE` over extrapolation when lineage, completeness,
60
+ parity, point-in-time validity, independent support, or reconciliation fails.
61
+ - This classifies the historical claim; it does not automatically mean stop.
62
+ Retrospective-universe or exposed-holdout limitations require continued
63
+ bounded research and may end in a risk-1 prospective handoff.
64
+ - Keep any unapproved next composition in forward incubation/advisory mode.
65
+ - A verdict never means “wait”. Pair it with the deterministic research action:
66
+ bounded recent-direction repair, authorized risk-1 micro-forward, a concrete
67
+ blocker, or stop. An exposed holdout blocks `READY_FOR_RUNTIME` but does not
68
+ block prospective micro-forward evidence.
69
+
70
+ ## Release verdicts
71
+
72
+ ### `READY_FOR_RUNTIME`
73
+
74
+ Use only when all conditions hold:
75
+
76
+ - the one final core plus deterministic gate composition is immutable and
77
+ completely reconciled;
78
+ - the bounded core loop used one anchor plus two evidence-driven refinement
79
+ pairs per surviving family, then completed the mandatory cadence-diverse
80
+ rescue board, never exceeded 3×5+3 candidates, and completed full
81
+ metric/match/trace analysis before each child;
82
+ - one isolated-long finalist and one gate round followed the preregistered
83
+ rules without reopening the held-out evidence;
84
+ - BOTH directions stayed enabled in raw evidence and ALL/LONG/SHORT were
85
+ reported; the approved composition either passes its explicit rules for both
86
+ sides or uses a preregistered, held-out-tested `long_only`/`short_only` gate
87
+ policy whose retained side and ALL stream pass while the suppressed side is
88
+ shown explicitly as zero approved support;
89
+ - full, terminal, cold-start/reset, concentration, capacity, causality, and
90
+ current-market evidence satisfy the release contract;
91
+ - no unresolved runtime-parity or data-lineage blocker remains.
92
+ - the progress decision, selected-composition artifact, objective fingerprint,
93
+ historical matrix, and chart all identify the same candidate/composition.
94
+
95
+ Meaning: the composition supports the strongest historical claim. The verdict
96
+ alone is not mutation authority; a `release`-mode invocation separately
97
+ authorizes the exact risk-1 rollout unless it explicitly forbids runtime
98
+ changes. Never place, cancel, or close orders manually.
99
+
100
+ ### `UNSUITABLE_FOR_CURRENT_MARKET`
101
+
102
+ Use when evidence is complete and valid, but no composition satisfies the
103
+ prospective rule: positive maximum-covered economics for every active approved
104
+ side plus frozen drawdown/tail/cost/concentration/support guardrails. A recent
105
+ calendar loss alone is not sufficient. Typical evidence includes failed active
106
+ side economics, unacceptable portfolio DD/capacity, or no qualifying isolated
107
+ finalist.
108
+
109
+ Do not use this verdict merely because the first three rounds produced no
110
+ eligible finalist. It requires the historical bridge and all available rescue
111
+ slots to be complete. It also requires the direction-policy checkpoint, the
112
+ maximum-window diagnostic handoff, the full window matrix, and the complete
113
+ AI-gate report. A stronger unbridged prior result or an unresolved
114
+ reconstructable historical hypothesis makes the evidence incomplete instead.
115
+
116
+ Meaning: preserve the composition and failures as immutable research. Keep any
117
+ continued observation advisory/forward-only; do not tune around the exposed
118
+ period inside the same lineage.
119
+
120
+ ### `INSUFFICIENT_EVIDENCE`
121
+
122
+ Use when a release conclusion cannot be supported, including partial/OOM/error
123
+ runs, missing common cached coverage, incomplete side cohorts, export/Redis
124
+ mismatch, unavailable point-in-time inputs, stale/open holdout, inadequate
125
+ independent support, missing cold-start evidence, ambiguous lineage, an
126
+ incomplete historical hypothesis audit, or a bounded trial cap reached while a
127
+ stronger reconstructable historical backlog remains.
128
+
129
+ Meaning: identify the smallest missing evidence item and its action ceiling.
130
+ Hard-invalid evidence must be repaired. Retrospective-universe, exposed-tail,
131
+ and prospective-support limitations do not justify waiting for perfect history:
132
+ complete the bounded contour and prepare/start the separately authorized
133
+ `MAX_LOSS_VALUE=1` micro-forward candidate. Do not convert uncertainty into
134
+ current-market unsuitability or historical readiness.
135
+
136
+ ## Diagnose-live verdict precedence
137
+
138
+ Apply this order:
139
+
140
+ 1. Return `INSUFFICIENT_EVIDENCE` when the released composition, incident
141
+ evidence, or comparison completeness cannot be established.
142
+ 2. Return `RUNTIME_DIVERGENCE` when material runtime/replay/config/context/
143
+ execution non-parity explains or invalidates the observed sample.
144
+ 3. With parity established, return `GENERALIZATION_FAILURE` when adequate new
145
+ post-cutoff evidence breaches the preregistered generalization bounds.
146
+ 4. With parity and adequate support established, return `EXPECTED_DRAWDOWN`
147
+ when the observation remains inside the frozen release distribution.
148
+
149
+ ### `RUNTIME_DIVERGENCE`
150
+
151
+ Require concrete mismatched evidence such as closed-candle boundary, config or
152
+ fingerprint, state restoration, causal context, deterministic gate decision,
153
+ allocator/risk/order lifecycle, fill, fee/slippage, or exit differences.
154
+
155
+ Do not call a strategy generalized or failed while material parity divergence
156
+ remains unresolved.
157
+
158
+ ### `EXPECTED_DRAWDOWN`
159
+
160
+ Require exact composition parity, adequate incident/forward support, and losses,
161
+ streak, drawdown, cadence, concentration, sides, and regimes within the frozen
162
+ release bounds. This verdict does not imply future recovery or authorize higher
163
+ risk.
164
+
165
+ ### `GENERALIZATION_FAILURE`
166
+
167
+ Require exact composition parity and adequate independent post-selection
168
+ evidence outside the release cutoff. Show which preregistered ALL/LONG/SHORT,
169
+ regime, concentration, or drawdown bound failed. Do not use an exposed tuning
170
+ window or a handful of correlated symbol rows as proof.
171
+
172
+ ### `INSUFFICIENT_EVIDENCE`
173
+
174
+ Use when runtime evidence is remote/unavailable, ids cannot be linked, the
175
+ incident window is incomplete, parity is unknown, sample/event support is too
176
+ small, release bounds are missing, or evidence has conflicting lineage.
177
+
178
+ ## Required final shape
179
+
180
+ ```text
181
+ VERDICT: <exact enum>
182
+ MODE: <release|diagnose-live>
183
+ COMPOSITION: <core fingerprint> + <deterministic gate fingerprint>
184
+ EVIDENCE: <complete|incomplete> — <one-line reason>
185
+ HISTORY AUDIT: <inventory SHA> — <bridged/excluded/unresolved counts>
186
+ PRIOR BRIDGE: <strongest prior result and current-contract disposition>
187
+ RESCUE BOARD: <up to 3 seed cadence/failure/child/result rows, or hard reason per empty slot>
188
+ DIRECTION POLICY: <trigger, five variants, selected/rejected policy, checkpoint SHA>
189
+
190
+ ALL/LONG/SHORT: N, PnL, PnL/trade, PF, WR, realized MaxDD, cadence/day
191
+ WINDOW MATRIX: <full/3y/4y/max-covered/365d/180d/90d/30d/7d for control and best aggregate/LONG/SHORT/policy attempts>
192
+ AI-GATE REPORT: <decision, qN+, full/terminal outcome, cadence/fan-out, risk-adjusted, quality/direction, validation, artifact SHA>
193
+ CAUSAL/PARITY FINDING: <bounded evidence statement>
194
+ LIMITATIONS: <material limitations or none>
195
+ NEXT ACTION: <REPAIR_RECENT_DIRECTION|START_MICRO_FORWARD|MICRO_FORWARD_READY|FORWARD_BLOCKED|STOP_RESEARCH> — <bounded reason>
196
+ ```
197
+
198
+ Also cite the immutable `tradejs-release-progress/v2` artifact. If it reports
199
+ `verdictAllowed=false`, do not emit this final shape yet; perform its next
200
+ action first.
@@ -0,0 +1,137 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { readFile } from 'node:fs/promises';
4
+ import { pathToFileURL } from 'node:url';
5
+
6
+ const DIRECTIONS = ['LONG', 'SHORT'];
7
+
8
+ function assertMetric(metric, label) {
9
+ for (const field of [
10
+ 'trades',
11
+ 'pnl',
12
+ 'pnlPerTrade',
13
+ 'profitFactor',
14
+ 'cadencePerDay',
15
+ ]) {
16
+ if (!Number.isFinite(metric?.[field])) {
17
+ throw new Error(`${label}.${field} must be finite`);
18
+ }
19
+ }
20
+ }
21
+
22
+ function isUseful(metric, rule) {
23
+ return (
24
+ metric.trades >= rule.minimumTrades &&
25
+ metric.cadencePerDay >= rule.minimumCadencePerDay &&
26
+ metric.pnl > rule.minimumPnl &&
27
+ metric.pnlPerTrade > rule.minimumPnlPerTrade &&
28
+ metric.profitFactor > rule.minimumProfitFactor
29
+ );
30
+ }
31
+
32
+ export function evaluateDirectionPolicyCheckpoint(input) {
33
+ const rule = {
34
+ minimumTrades: 0,
35
+ minimumCadencePerDay: 0,
36
+ minimumPnl: 0,
37
+ minimumPnlPerTrade: 0,
38
+ minimumProfitFactor: 1,
39
+ maximumNegligibleApprovalShare: 0.05,
40
+ ...input?.usefulSideRule,
41
+ };
42
+
43
+ assertMetric(input?.raw?.ALL, 'raw.ALL');
44
+ for (const direction of DIRECTIONS) {
45
+ assertMetric(input?.raw?.[direction], `raw.${direction}`);
46
+ }
47
+
48
+ const statuses = Object.fromEntries(
49
+ DIRECTIONS.map((direction) => [
50
+ direction,
51
+ isUseful(input.raw[direction], rule) ? 'useful' : 'failed',
52
+ ]),
53
+ );
54
+
55
+ const hidden = DIRECTIONS.find((direction) => {
56
+ if (statuses[direction] !== 'useful' || !input.gateApproved) {
57
+ return false;
58
+ }
59
+ const approved = input.gateApproved[direction];
60
+ if (!approved || !Number.isFinite(approved.trades)) {
61
+ throw new Error(`gateApproved.${direction}.trades must be finite`);
62
+ }
63
+ const share = input.raw[direction].trades
64
+ ? approved.trades / input.raw[direction].trades
65
+ : 0;
66
+ return share <= rule.maximumNegligibleApprovalShare;
67
+ });
68
+
69
+ if (hidden) {
70
+ return {
71
+ schema: 'tradejs-direction-policy-decision/v1',
72
+ trigger: 'profitable_side_hidden',
73
+ required: true,
74
+ usefulSideRule: rule,
75
+ rawSideStatuses: statuses,
76
+ retainedSide: hidden,
77
+ proposedPolicy: `${hidden.toLowerCase()}_pass_through`,
78
+ };
79
+ }
80
+
81
+ const usefulDirections = DIRECTIONS.filter(
82
+ (direction) => statuses[direction] === 'useful',
83
+ );
84
+ if (usefulDirections.length === 1) {
85
+ const retainedSide = usefulDirections[0];
86
+ const failingSide = retainedSide === 'LONG' ? 'SHORT' : 'LONG';
87
+ const contamination =
88
+ input.raw[failingSide].pnl < 0 &&
89
+ input.raw.ALL.pnl < input.raw[retainedSide].pnl;
90
+ if (contamination) {
91
+ return {
92
+ schema: 'tradejs-direction-policy-decision/v1',
93
+ trigger: 'losing_side_contamination',
94
+ required: true,
95
+ usefulSideRule: rule,
96
+ rawSideStatuses: statuses,
97
+ retainedSide,
98
+ failingSide,
99
+ proposedPolicy: `${retainedSide.toLowerCase()}_only`,
100
+ };
101
+ }
102
+ }
103
+
104
+ return {
105
+ schema: 'tradejs-direction-policy-decision/v1',
106
+ trigger: 'no_side_salvage',
107
+ required: false,
108
+ usefulSideRule: rule,
109
+ rawSideStatuses: statuses,
110
+ retainedSide: null,
111
+ proposedPolicy: 'both',
112
+ };
113
+ }
114
+
115
+ async function main() {
116
+ const inputIndex = process.argv.indexOf('--input');
117
+ if (inputIndex === -1 || !process.argv[inputIndex + 1]) {
118
+ throw new Error(
119
+ 'Usage: direction-policy-checkpoint.mjs --input <input.json>',
120
+ );
121
+ }
122
+ const input = JSON.parse(
123
+ await readFile(process.argv[inputIndex + 1], 'utf8'),
124
+ );
125
+ process.stdout.write(
126
+ `${JSON.stringify(evaluateDirectionPolicyCheckpoint(input), null, 2)}\n`,
127
+ );
128
+ }
129
+
130
+ if (import.meta.url === pathToFileURL(process.argv[1] ?? '').href) {
131
+ main().catch((error) => {
132
+ process.stderr.write(
133
+ `${error instanceof Error ? error.message : String(error)}\n`,
134
+ );
135
+ process.exitCode = 1;
136
+ });
137
+ }
@@ -0,0 +1,85 @@
1
+ import assert from 'node:assert/strict';
2
+ import test from 'node:test';
3
+
4
+ import { evaluateDirectionPolicyCheckpoint } from './direction-policy-checkpoint.mjs';
5
+
6
+ const rule = {
7
+ minimumTrades: 144,
8
+ minimumCadencePerDay: 0.1,
9
+ minimumPnl: 0,
10
+ minimumPnlPerTrade: 0,
11
+ minimumProfitFactor: 1,
12
+ };
13
+
14
+ const metric = (trades, pnl, pnlPerTrade, profitFactor, cadencePerDay) => ({
15
+ trades,
16
+ pnl,
17
+ pnlPerTrade,
18
+ profitFactor,
19
+ cadencePerDay,
20
+ });
21
+
22
+ test('requires LONG-only containment for the RelativeRotation control profile', () => {
23
+ const decision = evaluateDirectionPolicyCheckpoint({
24
+ usefulSideRule: rule,
25
+ raw: {
26
+ ALL: metric(1203, -39, -0.032, 0.995, 0.835),
27
+ LONG: metric(886, 157.67, 0.178, 1.031, 0.615),
28
+ SHORT: metric(317, -196.67, -0.62, 0.907, 0.22),
29
+ },
30
+ });
31
+
32
+ assert.equal(decision.required, true);
33
+ assert.equal(decision.trigger, 'losing_side_contamination');
34
+ assert.equal(decision.retainedSide, 'LONG');
35
+ assert.equal(decision.proposedPolicy, 'long_only');
36
+ });
37
+
38
+ test('requires recovery when a useful raw side has negligible gate approvals', () => {
39
+ const decision = evaluateDirectionPolicyCheckpoint({
40
+ usefulSideRule: rule,
41
+ raw: {
42
+ ALL: metric(500, 100, 0.2, 1.1, 0.5),
43
+ LONG: metric(250, 120, 0.48, 1.2, 0.25),
44
+ SHORT: metric(250, -20, -0.08, 0.97, 0.25),
45
+ },
46
+ gateApproved: {
47
+ LONG: { trades: 0 },
48
+ SHORT: { trades: 40 },
49
+ },
50
+ });
51
+
52
+ assert.equal(decision.required, true);
53
+ assert.equal(decision.trigger, 'profitable_side_hidden');
54
+ assert.equal(decision.proposedPolicy, 'long_pass_through');
55
+ });
56
+
57
+ test('does not manufacture a side policy when neither side passes the frozen rule', () => {
58
+ const decision = evaluateDirectionPolicyCheckpoint({
59
+ usefulSideRule: rule,
60
+ raw: {
61
+ ALL: metric(500, -200, -0.4, 0.8, 0.5),
62
+ LONG: metric(250, -80, -0.32, 0.9, 0.25),
63
+ SHORT: metric(250, -120, -0.48, 0.85, 0.25),
64
+ },
65
+ });
66
+
67
+ assert.equal(decision.required, false);
68
+ assert.equal(decision.trigger, 'no_side_salvage');
69
+ assert.equal(decision.proposedPolicy, 'both');
70
+ });
71
+
72
+ test('rejects incomplete metric inputs instead of guessing', () => {
73
+ assert.throws(
74
+ () =>
75
+ evaluateDirectionPolicyCheckpoint({
76
+ usefulSideRule: rule,
77
+ raw: {
78
+ ALL: metric(1, 1, 1, 1, 1),
79
+ LONG: metric(1, 1, 1, 1, 1),
80
+ SHORT: { trades: 1 },
81
+ },
82
+ }),
83
+ /raw\.SHORT\.pnl must be finite/,
84
+ );
85
+ });