@alphafox/cli 0.3.22 → 0.3.24

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.
@@ -257,10 +257,9 @@ async function fetchFollowingSameSiteRedirects(fetchImpl, startUrl, init, maxHop
257
257
  const nextUrl = new URL(location, url).toString();
258
258
  if (!sameAuthSite(originOf(url), originOf(nextUrl)))
259
259
  break;
260
- if (response.status === 303 ||
261
- ((response.status === 301 || response.status === 302) &&
262
- method !== "GET" &&
263
- method !== "HEAD")) {
260
+ // 303 is the only redirect that switches to GET. Apex→www is Cloudflare 301;
261
+ // converting POST to GET drops the OAuth body and the AS returns 405.
262
+ if (response.status === 303) {
264
263
  method = "GET";
265
264
  body = undefined;
266
265
  }
@@ -1,5 +1,5 @@
1
1
  export declare const BACKTEST_RUNTIME_PROTOCOL = 1;
2
- export declare const DEFAULT_BACKTEST_WASM_MANIFEST_URL = "https://zwggllrrna54e2d6.public.blob.vercel-storage.com/engine-backtest/latest.json";
2
+ export declare const DEFAULT_BACKTEST_WASM_MANIFEST_URL = "https://api.alphafox.app/control-plane/v1/backtest/runtime-manifest";
3
3
  export declare const BLOB_RUNTIME_FILES: {
4
4
  readonly wasm: "tradingfox-backtest.wasm";
5
5
  readonly wasmExec: "wasm_exec.js";
@@ -8,6 +8,8 @@ export declare const BLOB_RUNTIME_FILES: {
8
8
  readonly node: "node.mjs";
9
9
  readonly nodeWorker: "worker-node.mjs";
10
10
  readonly nodeWorkerPath: "worker-node-path.mjs";
11
+ readonly passivbotKernel: "passivbot_kernel.wasm";
12
+ readonly passivbotKernelModule: "passivbot-kernel.mjs";
11
13
  };
12
14
  export type BlobRuntimeFileKey = keyof typeof BLOB_RUNTIME_FILES;
13
15
  export interface EngineBacktestBlobManifest {
@@ -23,6 +25,8 @@ export interface EngineBacktestBlobManifest {
23
25
  readonly node: string;
24
26
  readonly nodeWorker: string;
25
27
  readonly nodeWorkerPath: string;
28
+ readonly passivbotKernel: string;
29
+ readonly passivbotKernelModule: string;
26
30
  }
27
31
  export interface FetchRuntimeHooks {
28
32
  readonly fetch?: typeof fetch;
@@ -5,15 +5,16 @@ exports.resolveBacktestWasmManifestUrl = resolveBacktestWasmManifestUrl;
5
5
  exports.resolveRuntimeCacheDir = resolveRuntimeCacheDir;
6
6
  exports.parseEngineBacktestBlobManifest = parseEngineBacktestBlobManifest;
7
7
  exports.ensureBlobRuntime = ensureBlobRuntime;
8
+ const node_crypto_1 = require("node:crypto");
8
9
  const node_fs_1 = require("node:fs");
9
10
  const promises_1 = require("node:fs/promises");
10
11
  const node_os_1 = require("node:os");
11
12
  const node_path_1 = require("node:path");
12
- const promises_2 = require("node:stream/promises");
13
13
  const node_stream_1 = require("node:stream");
14
+ const promises_2 = require("node:stream/promises");
14
15
  const errors_1 = require("./errors");
15
16
  exports.BACKTEST_RUNTIME_PROTOCOL = 1;
16
- exports.DEFAULT_BACKTEST_WASM_MANIFEST_URL = "https://zwggllrrna54e2d6.public.blob.vercel-storage.com/engine-backtest/latest.json";
17
+ exports.DEFAULT_BACKTEST_WASM_MANIFEST_URL = "https://api.alphafox.app/control-plane/v1/backtest/runtime-manifest";
17
18
  exports.BLOB_RUNTIME_FILES = {
18
19
  wasm: "tradingfox-backtest.wasm",
19
20
  wasmExec: "wasm_exec.js",
@@ -22,10 +23,31 @@ exports.BLOB_RUNTIME_FILES = {
22
23
  node: "node.mjs",
23
24
  nodeWorker: "worker-node.mjs",
24
25
  nodeWorkerPath: "worker-node-path.mjs",
26
+ passivbotKernel: "passivbot_kernel.wasm",
27
+ passivbotKernelModule: "passivbot-kernel.mjs",
25
28
  };
29
+ const HASHED_BLOB_RUNTIME_FILES = [
30
+ exports.BLOB_RUNTIME_FILES.wasm,
31
+ exports.BLOB_RUNTIME_FILES.passivbotKernel,
32
+ exports.BLOB_RUNTIME_FILES.wasmExec,
33
+ exports.BLOB_RUNTIME_FILES.worker,
34
+ exports.BLOB_RUNTIME_FILES.passivbotKernelModule,
35
+ exports.BLOB_RUNTIME_FILES.client,
36
+ exports.BLOB_RUNTIME_FILES.node,
37
+ exports.BLOB_RUNTIME_FILES.nodeWorker,
38
+ exports.BLOB_RUNTIME_FILES.nodeWorkerPath,
39
+ ];
26
40
  function resolveBacktestWasmManifestUrl(env = process.env) {
27
- return (env.ALPHAFOX_BACKTEST_WASM_MANIFEST_URL?.trim() ||
28
- exports.DEFAULT_BACKTEST_WASM_MANIFEST_URL);
41
+ const override = env.ALPHAFOX_BACKTEST_WASM_MANIFEST_URL?.trim();
42
+ if (override)
43
+ return override;
44
+ if (env.ALPHAFOX_PROFILE === "staging") {
45
+ return "https://staging-api.alphafox.app/control-plane/v1/backtest/runtime-manifest";
46
+ }
47
+ if (env.ALPHAFOX_PROFILE === "local") {
48
+ throw new errors_1.EngineBacktestError({ type: "runtime", subtype: "runtime_manifest_unconfigured", message: "Local profile requires ALPHAFOX_BACKTEST_WASM_MANIFEST_URL or an explicit local Engine build." });
49
+ }
50
+ return exports.DEFAULT_BACKTEST_WASM_MANIFEST_URL;
29
51
  }
30
52
  function resolveRuntimeCacheDir(hash, env = process.env) {
31
53
  const override = env.ALPHAFOX_BACKTEST_RUNTIME_CACHE_DIR?.trim();
@@ -51,9 +73,19 @@ function parseEngineBacktestBlobManifest(value) {
51
73
  message: `Backtest runtime protocol is incompatible (got ${String(record.protocol)}, expected ${exports.BACKTEST_RUNTIME_PROTOCOL}).`,
52
74
  });
53
75
  }
76
+ const hash = readRequiredString(record.hash, "hash");
77
+ if (!/^[0-9a-f]{16}$/.test(hash)) {
78
+ throw new errors_1.EngineBacktestError({
79
+ type: "runtime",
80
+ subtype: "runtime_manifest_invalid",
81
+ message: "Backtest runtime manifest hash must be 16 lowercase hex characters.",
82
+ });
83
+ }
84
+ const passivbotKernel = readRequiredHttps(record.passivbotKernel, "passivbotKernel");
85
+ const passivbotKernelModule = readRequiredHttps(record.passivbotKernelModule, "passivbotKernelModule");
54
86
  return {
55
87
  version: readRequiredString(record.version, "version"),
56
- hash: readRequiredString(record.hash, "hash"),
88
+ hash,
57
89
  protocol: exports.BACKTEST_RUNTIME_PROTOCOL,
58
90
  engineSha: readOptionalString(record.engineSha),
59
91
  packageVersion: readOptionalString(record.packageVersion),
@@ -64,6 +96,8 @@ function parseEngineBacktestBlobManifest(value) {
64
96
  node: readRequiredHttps(record.node, "node"),
65
97
  nodeWorker: readRequiredHttps(record.nodeWorker, "nodeWorker"),
66
98
  nodeWorkerPath: readRequiredHttps(record.nodeWorkerPath, "nodeWorkerPath"),
99
+ passivbotKernel,
100
+ passivbotKernelModule,
67
101
  };
68
102
  }
69
103
  async function ensureBlobRuntime(env = process.env, hooks) {
@@ -78,7 +112,7 @@ async function ensureBlobRuntime(env = process.env, hooks) {
78
112
  type: "runtime",
79
113
  subtype: "runtime_manifest_unavailable",
80
114
  message: `Cannot load backtest runtime manifest: ${error instanceof Error ? error.message : String(error)}`,
81
- hint: "Check network access to Vercel Blob, or set ALPHAFOX_USE_LOCAL_BACKTEST=1 with a local Engine build.",
115
+ hint: "Check network access to the Engine runtime discovery API, or set ALPHAFOX_USE_LOCAL_BACKTEST=1 with a local Engine build.",
82
116
  details: { manifestUrl },
83
117
  });
84
118
  }
@@ -87,26 +121,72 @@ async function ensureBlobRuntime(env = process.env, hooks) {
87
121
  type: "runtime",
88
122
  subtype: "runtime_manifest_unavailable",
89
123
  message: `Backtest runtime manifest unavailable (HTTP ${response.status}).`,
90
- hint: "Check network access to Vercel Blob, or set ALPHAFOX_USE_LOCAL_BACKTEST=1 with a local Engine build.",
124
+ hint: "Check network access to the Engine runtime discovery API, or set ALPHAFOX_USE_LOCAL_BACKTEST=1 with a local Engine build.",
91
125
  details: { manifestUrl, status: response.status },
92
126
  });
93
127
  }
94
128
  const manifest = parseEngineBacktestBlobManifest(await response.json());
95
129
  const directory = hooks?.cacheDir ?? resolveRuntimeCacheDir(manifest.hash, env);
96
- await (0, promises_1.mkdir)(directory, { recursive: true });
97
- for (const key of Object.keys(exports.BLOB_RUNTIME_FILES)) {
98
- const fileName = exports.BLOB_RUNTIME_FILES[key];
99
- const target = (0, node_path_1.join)(directory, fileName);
100
- if (await fileExists(target)) {
101
- continue;
130
+ if (await runtimeMatchesHash(directory, manifest.hash)) {
131
+ return {
132
+ manifest,
133
+ directory,
134
+ nodeEntry: (0, node_path_1.join)(directory, exports.BLOB_RUNTIME_FILES.node),
135
+ };
136
+ }
137
+ await (0, promises_1.mkdir)((0, node_path_1.dirname)(directory), { recursive: true });
138
+ let actualHash = "";
139
+ for (let attempt = 0; attempt < 2; attempt += 1) {
140
+ const staging = `${directory}.${process.pid}.${(0, node_crypto_1.randomUUID)()}.tmp`;
141
+ await (0, promises_1.mkdir)(staging, { recursive: true });
142
+ try {
143
+ for (const key of Object.keys(exports.BLOB_RUNTIME_FILES)) {
144
+ await downloadFile(fetchImpl, manifest[key], (0, node_path_1.join)(staging, exports.BLOB_RUNTIME_FILES[key]));
145
+ }
146
+ if (!(await allRuntimeFilesExist(staging))) {
147
+ continue;
148
+ }
149
+ actualHash = await hashBlobRuntime(staging);
150
+ if (actualHash !== manifest.hash) {
151
+ continue;
152
+ }
153
+ await (0, promises_1.mkdir)(directory, { recursive: true });
154
+ for (const fileName of Object.values(exports.BLOB_RUNTIME_FILES)) {
155
+ await (0, promises_1.rename)((0, node_path_1.join)(staging, fileName), (0, node_path_1.join)(directory, fileName));
156
+ }
157
+ if (await runtimeMatchesHash(directory, manifest.hash)) {
158
+ return {
159
+ manifest,
160
+ directory,
161
+ nodeEntry: (0, node_path_1.join)(directory, exports.BLOB_RUNTIME_FILES.node),
162
+ };
163
+ }
164
+ }
165
+ finally {
166
+ await (0, promises_1.rm)(staging, { force: true, recursive: true });
102
167
  }
103
- await downloadFile(fetchImpl, manifest[key], target);
104
168
  }
105
- return {
106
- manifest,
107
- directory,
108
- nodeEntry: (0, node_path_1.join)(directory, exports.BLOB_RUNTIME_FILES.node),
109
- };
169
+ throw new errors_1.EngineBacktestError({
170
+ type: "runtime",
171
+ subtype: "runtime_integrity_failed",
172
+ message: `Backtest runtime hash mismatch: expected ${manifest.hash}, received ${actualHash}.`,
173
+ hint: "Retry the download; no unverified runtime was cached.",
174
+ details: { expectedHash: manifest.hash, actualHash, directory },
175
+ });
176
+ }
177
+ async function runtimeMatchesHash(directory, expectedHash) {
178
+ if (!(await allRuntimeFilesExist(directory))) {
179
+ return false;
180
+ }
181
+ try {
182
+ return (await hashBlobRuntime(directory)) === expectedHash;
183
+ }
184
+ catch {
185
+ return false;
186
+ }
187
+ }
188
+ async function allRuntimeFilesExist(directory) {
189
+ return (await Promise.all(Object.values(exports.BLOB_RUNTIME_FILES).map((fileName) => fileExists((0, node_path_1.join)(directory, fileName))))).every(Boolean);
110
190
  }
111
191
  async function downloadFile(fetchImpl, url, target) {
112
192
  let response;
@@ -127,9 +207,23 @@ async function downloadFile(fetchImpl, url, target) {
127
207
  message: `Backtest runtime file unavailable (${url} HTTP ${response.status}).`,
128
208
  });
129
209
  }
130
- const tmp = `${target}.tmp`;
131
- await (0, promises_2.pipeline)(node_stream_1.Readable.fromWeb(response.body), (0, node_fs_1.createWriteStream)(tmp));
132
- await (0, promises_1.rename)(tmp, target);
210
+ const tmp = `${target}.${process.pid}.${(0, node_crypto_1.randomUUID)()}.tmp`;
211
+ try {
212
+ await (0, promises_2.pipeline)(node_stream_1.Readable.fromWeb(response.body), (0, node_fs_1.createWriteStream)(tmp));
213
+ await (0, promises_1.rename)(tmp, target);
214
+ }
215
+ finally {
216
+ await (0, promises_1.rm)(tmp, { force: true });
217
+ }
218
+ }
219
+ async function hashBlobRuntime(directory) {
220
+ const digest = (0, node_crypto_1.createHash)("sha256");
221
+ for (const fileName of HASHED_BLOB_RUNTIME_FILES) {
222
+ for await (const chunk of (0, node_fs_1.createReadStream)((0, node_path_1.join)(directory, fileName))) {
223
+ digest.update(chunk);
224
+ }
225
+ }
226
+ return digest.digest("hex").slice(0, 16);
133
227
  }
134
228
  async function fileExists(path) {
135
229
  try {
@@ -216,7 +216,7 @@ async function executeEngineBacktestRun(args, flags, env = process.env, deps = {
216
216
  }
217
217
  else {
218
218
  const [wasmLoaded, runnerLoaded] = await Promise.all([
219
- (0, resolve_packages_1.loadBacktestWasm)(env, deps.resolveHooks),
219
+ (0, resolve_packages_1.loadBacktestWasm)({ ...env, ALPHAFOX_PROFILE: profile.name }, deps.resolveHooks),
220
220
  (0, resolve_packages_1.loadBacktestRunner)(env, deps.resolveHooks),
221
221
  ]);
222
222
  wasm = wasmLoaded.module;
@@ -82,7 +82,7 @@ async function executeEngineBacktestSweep(args, flags, env = process.env, deps =
82
82
  cwd: deps.cwd,
83
83
  readFile: deps.readFile,
84
84
  }), config, args.mode);
85
- const { wasm, runner } = await loadRuntime(deps, env);
85
+ const { wasm, runner } = await loadRuntime(deps, { ...env, ALPHAFOX_PROFILE: profile.name });
86
86
  const clients = [];
87
87
  try {
88
88
  const client = wasm.createNodeBacktestClient({ verbose: args.verbose });
@@ -146,9 +146,9 @@ async function fetchFollowingAuthRedirects(fetchImpl, startUrl, init, maxHops =
146
146
  if (!sameAuthSite(originOf(url), originOf(nextUrl))) {
147
147
  break;
148
148
  }
149
- // 303 switches to GET without body; 301/302 historically do for non-GET.
150
- if (response.status === 303 ||
151
- ((response.status === 301 || response.status === 302) && method !== "GET" && method !== "HEAD")) {
149
+ // 303 is the only redirect that switches to GET. Apex→www is Cloudflare 301;
150
+ // converting POST to GET drops the OAuth body and the AS returns 405.
151
+ if (response.status === 303) {
152
152
  method = "GET";
153
153
  body = undefined;
154
154
  }
@@ -1,153 +1,153 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
3
  "packageName": "@alphafox/cli",
4
- "packageVersion": "0.3.22",
4
+ "packageVersion": "0.3.24",
5
5
  "contractVersion": "2026-08-31",
6
- "bundleHash": "0ff8e818dd5a2f06d7d0df63d681bdf04163b9b09d8626da99c033a97aa8d438",
6
+ "bundleHash": "268fe5f1673a98d317f50887066cbdc4b9a2663ad3d8d4d1ef2264542c469d4b",
7
7
  "skills": [
8
8
  {
9
9
  "name": "alphafox",
10
- "version": "0.3.22",
10
+ "version": "0.3.24",
11
11
  "files": [
12
12
  {
13
13
  "path": "SKILL.md",
14
- "sha256": "454ddd309258874eb35bf567cd9ac3f524ecd212cd0a76599b8ae96ca50b9658",
15
- "size": 7950
14
+ "sha256": "580e26102a42d2871d1876832b542411f4aa6dd68dafa29c1a30e520c55b1e41",
15
+ "size": 6711
16
16
  }
17
17
  ],
18
- "hash": "10dc9fcb503d2e09dda7284818fcebd1223d97fb3c8e95e7757ea6db86a239b2"
18
+ "hash": "5e62600962da6d22119d06a4bd3076780ea99aeb6ec3a64e9e6b278fc42f8c74"
19
19
  },
20
20
  {
21
21
  "name": "alphafox-account",
22
- "version": "0.3.22",
22
+ "version": "0.3.24",
23
23
  "files": [
24
24
  {
25
25
  "path": "SKILL.md",
26
- "sha256": "ab83a22f7960d1b94d1f3874cd464c705c9eafb77b3b132ad3d2c092b343227b",
26
+ "sha256": "b6bb1b5c3d9f7a147981a22f036f7c15cfabfdab9159ed6185d83fb5565ff22a",
27
27
  "size": 784
28
28
  }
29
29
  ],
30
- "hash": "b0de2c06865dd85be5655358e2fe20bd54dd2db29af73c225e43e3e5c4464649"
30
+ "hash": "17d7afff57d90861b1623ebadebed3a6ca7f6f45bcf81158df59c1ec2a84f8b1"
31
31
  },
32
32
  {
33
33
  "name": "alphafox-admin",
34
- "version": "0.3.22",
34
+ "version": "0.3.24",
35
35
  "files": [
36
36
  {
37
37
  "path": "SKILL.md",
38
- "sha256": "a30aa7f40d67e32068f909481906a548da713f05dfb8562eebb05bc09d90bca7",
38
+ "sha256": "8de45c0ee0947c57afd17d719bdf5737e260d0beb535b23aab546b74b160e439",
39
39
  "size": 2078
40
40
  }
41
41
  ],
42
- "hash": "e0aa20827c3299dc996c74ef27b7708bd32275e3a61362b07be367fc565e735a"
42
+ "hash": "28149c13321f3216fff2a6448c6004b666c4ec3b28142b4769cad2d012e8f65f"
43
43
  },
44
44
  {
45
45
  "name": "alphafox-auth",
46
- "version": "0.3.22",
46
+ "version": "0.3.24",
47
47
  "files": [
48
48
  {
49
49
  "path": "SKILL.md",
50
- "sha256": "eab220c69f31de56f7bdfde7722723c2bc9a667539f62bc8faae7234642412be",
50
+ "sha256": "b6fef448fdc874516a3e8e9d63c0d37084a1b14141c02068fb9a4dd7a0f175f0",
51
51
  "size": 2371
52
52
  }
53
53
  ],
54
- "hash": "02c24ece7d90d42afd81c249184925ec970602e0226deafd290ff1d7d2e2dfa9"
54
+ "hash": "0307804078457f61d6e759025e4fe7a9177263199c2a0655b2db0b1c3859ab8c"
55
55
  },
56
56
  {
57
57
  "name": "alphafox-cache",
58
- "version": "0.3.22",
58
+ "version": "0.3.24",
59
59
  "files": [
60
60
  {
61
61
  "path": "SKILL.md",
62
- "sha256": "ebdf0007dfb04f2e2285b2e4cf738e6b70c46b40f67b22f2ec024be79e1ff895",
63
- "size": 1530
62
+ "sha256": "1542a43cf58338c4ad8b238cb81e672fd06741c5c4d5574907685cec1465c29a",
63
+ "size": 1762
64
64
  }
65
65
  ],
66
- "hash": "7ebf4b0457e8c61fdd28601dbd95ef3b36eb2d4935e1f38448273e329ae69013"
66
+ "hash": "38f55adfe15d90e1f700b01434e10315cd21a2eaf68aa81ef1cbaa6838239ae8"
67
67
  },
68
68
  {
69
69
  "name": "alphafox-engine-backtest",
70
- "version": "0.3.22",
70
+ "version": "0.3.24",
71
71
  "files": [
72
72
  {
73
73
  "path": "SKILL.md",
74
- "sha256": "2a30ee16622c6a4cf1f9f40519be544d0f089a62eba4ffdd48d726f153760de3",
75
- "size": 9792
74
+ "sha256": "63d100f1312b37905a1e0483102f0717f5a7ce4774b45c293e691cde951417e4",
75
+ "size": 9862
76
76
  }
77
77
  ],
78
- "hash": "1aecca4608c0253089e69c1d58746984c2621ab5eccbb45c4e962a7530936663"
78
+ "hash": "5f76ddce28123600785ee2bfe4e512b92c191ff9b031f721ca42d32b7644c07f"
79
79
  },
80
80
  {
81
81
  "name": "alphafox-exchange",
82
- "version": "0.3.22",
82
+ "version": "0.3.24",
83
83
  "files": [
84
84
  {
85
85
  "path": "SKILL.md",
86
- "sha256": "a065c2af77150a140838580053d688defaafa2c1093c565f9759b2e4f211e3cd",
86
+ "sha256": "82d13d7541314c13244dad557d11b28815e4dabc7a4982390d16cb4e6eae6180",
87
87
  "size": 744
88
88
  }
89
89
  ],
90
- "hash": "0dd29f8547bdbcfe89789877b32e84f130e88c886cd07cab2785d515ff807a3c"
90
+ "hash": "cd917932104e318c8a65f0f16adea15e5c7af682bd37814a06de436cbc01fdbe"
91
91
  },
92
92
  {
93
93
  "name": "alphafox-market",
94
- "version": "0.3.22",
94
+ "version": "0.3.24",
95
95
  "files": [
96
96
  {
97
97
  "path": "SKILL.md",
98
- "sha256": "ca9d73c21724cf2e9b2f4ab9128f5ca883c70530ee4dc7268ea05c3a9d226083",
99
- "size": 3079
98
+ "sha256": "b6ff2ee7956b997f89baa8043bf30c3bf0581cae202a10639acfe5eb01e97513",
99
+ "size": 3208
100
100
  }
101
101
  ],
102
- "hash": "e5582689e8c8ca0e2ffcb334ba454b12400e9591070c9728b31e39db42c276a0"
102
+ "hash": "67f726874447e1b697397bafaa2a42cc5bf271bcf5d0b981ce98acee77924f03"
103
103
  },
104
104
  {
105
105
  "name": "alphafox-notification",
106
- "version": "0.3.22",
106
+ "version": "0.3.24",
107
107
  "files": [
108
108
  {
109
109
  "path": "SKILL.md",
110
- "sha256": "05485ee0d3bec411583f57ecb711555a5353d50fbf76ab92aaa45e2de4abdf20",
110
+ "sha256": "bae0d88305ee82867798d969fe39ea7420436b02cfb37dc4837d55143e2c4e83",
111
111
  "size": 699
112
112
  }
113
113
  ],
114
- "hash": "a139b26ee840f4f6c3bf46e92809ef5db1af29b446e572b5254808a639f86f27"
114
+ "hash": "8a6e3af0edee2f3e30a5326333c7918c863da194ee6086a92ccc71d8fa057924"
115
115
  },
116
116
  {
117
117
  "name": "alphafox-shared",
118
- "version": "0.3.22",
118
+ "version": "0.3.24",
119
119
  "files": [
120
120
  {
121
121
  "path": "SKILL.md",
122
- "sha256": "b7bbdf683b12975538c26e38a05dce89d0c0f1a19a5cc73569e2c709fbb5d04b",
123
- "size": 6920
122
+ "sha256": "4fda905ccf93ae321e36de4bc41dd7508825c7073bbbd237fc5b82910094ff70",
123
+ "size": 7819
124
124
  }
125
125
  ],
126
- "hash": "7c7750ba8bf2d2dbf51c8fb134ac958076773f9572f9082497925bb8b255de13"
126
+ "hash": "b610b7f938899ce1d6327bac9a4f5788e72bedb2a033ac77cadf8ea7d0a050b5"
127
127
  },
128
128
  {
129
129
  "name": "alphafox-strategy",
130
- "version": "0.3.22",
130
+ "version": "0.3.24",
131
131
  "files": [
132
132
  {
133
133
  "path": "SKILL.md",
134
- "sha256": "01540ff5e1076eab22515991199be407263d942bc7608628c377b81eb0157cd7",
135
- "size": 7185
134
+ "sha256": "b1caecced015cb227842153660744470a1092e823d2b355570c7e396ee3c9c5d",
135
+ "size": 8039
136
136
  }
137
137
  ],
138
- "hash": "e2e0e0a6bae21e5f97ed6ba42d66e90cf51c76b6601cdad606630c3743441909"
138
+ "hash": "57df3973cfcae8accccb5d65f16efd1fa0cde6d670f03ee318077db1d57c4e03"
139
139
  },
140
140
  {
141
141
  "name": "alphafox-trading",
142
- "version": "0.3.22",
142
+ "version": "0.3.24",
143
143
  "files": [
144
144
  {
145
145
  "path": "SKILL.md",
146
- "sha256": "4b862b2f01534e0fda2c6667bc39a972f2cd9692f8967a0496b941411b745967",
147
- "size": 4921
146
+ "sha256": "ba28488c9a46ea5da006940603580a05811939d9a6abbc4f14be69d315c04b2a",
147
+ "size": 4920
148
148
  }
149
149
  ],
150
- "hash": "d8c0388da25fd4964d6ca8d4668ec0a0778669e0fbc22e85952b7eca80869205"
150
+ "hash": "a7bb8403c0a9c7e42658e85770ec1451705d5bd7a4d5283847dab667b021e50e"
151
151
  }
152
152
  ]
153
153
  }
package/dist/version.d.ts CHANGED
@@ -1,4 +1,4 @@
1
1
  export declare const CLI_NAME = "alphafox";
2
2
  export declare const CLI_PACKAGE = "@alphafox/cli";
3
- export declare const CLI_VERSION = "0.3.22";
3
+ export declare const CLI_VERSION = "0.3.24";
4
4
  export { CATALOG_VERSION as CLI_CONTRACT_VERSION } from "./catalog/operations";
package/dist/version.js CHANGED
@@ -3,6 +3,6 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.CLI_CONTRACT_VERSION = exports.CLI_VERSION = exports.CLI_PACKAGE = exports.CLI_NAME = void 0;
4
4
  exports.CLI_NAME = "alphafox";
5
5
  exports.CLI_PACKAGE = "@alphafox/cli";
6
- exports.CLI_VERSION = "0.3.22";
6
+ exports.CLI_VERSION = "0.3.24";
7
7
  var operations_1 = require("./catalog/operations");
8
8
  Object.defineProperty(exports, "CLI_CONTRACT_VERSION", { enumerable: true, get: function () { return operations_1.CATALOG_VERSION; } });
@@ -1,51 +1,5 @@
1
- # Domain Docs
1
+ # Domain references
2
2
 
3
- How the engineering skills should consume this repo's domain documentation when exploring the codebase.
3
+ For a task that changes domain terminology, behavior across contexts, or architecture, locate the relevant entry in root `CONTEXT-MAP.md` (if present), otherwise `CONTEXT.md`, and read only the relevant glossary sections and ADRs. Ordinary documentation, formatting and isolated mechanical edits do not require a domain-document tour.
4
4
 
5
- ## Before exploring, read these
6
-
7
- - **`CONTEXT.md`** at the repo root, or
8
- - **`CONTEXT-MAP.md`** at the repo root if it exists — it points at one `CONTEXT.md` per context. Read each one relevant to the topic.
9
- - **`docs/adr/`** — read ADRs that touch the area you're about to work in. In multi-context repos, also check `src/<context>/docs/adr/` for context-scoped decisions.
10
-
11
- If any of these files don't exist, **proceed silently**. Don't flag their absence; don't suggest creating them upfront. The `/domain-modeling` skill (reached via `/grill-with-docs` and `/improve-codebase-architecture`) creates them lazily when terms or decisions actually get resolved.
12
-
13
- ## File structure
14
-
15
- Single-context repo (most repos):
16
-
17
- ```
18
- /
19
- ├── CONTEXT.md
20
- ├── docs/adr/
21
- │ ├── 0001-event-sourced-orders.md
22
- │ └── 0002-postgres-for-write-model.md
23
- └── src/
24
- ```
25
-
26
- Multi-context repo (presence of `CONTEXT-MAP.md` at the root):
27
-
28
- ```
29
- /
30
- ├── CONTEXT-MAP.md
31
- ├── docs/adr/ ← system-wide decisions
32
- └── src/
33
- ├── ordering/
34
- │ ├── CONTEXT.md
35
- │ └── docs/adr/ ← context-specific decisions
36
- └── billing/
37
- ├── CONTEXT.md
38
- └── docs/adr/
39
- ```
40
-
41
- ## Use the glossary's vocabulary
42
-
43
- When your output names a domain concept (in an issue title, a refactor proposal, a hypothesis, a test name), use the term as defined in `CONTEXT.md`. Don't drift to synonyms the glossary explicitly avoids.
44
-
45
- If the concept you need isn't in the glossary yet, that's a signal — either you're inventing language the project doesn't use (reconsider) or there's a real gap (note it for `/domain-modeling`).
46
-
47
- ## Flag ADR conflicts
48
-
49
- If your output contradicts an existing ADR, surface it explicitly rather than silently overriding:
50
-
51
- > _Contradicts ADR-0007 (event-sourced orders) — but worth reopening because…_
5
+ Use established terms and surface an actual conflict with an existing ADR before changing the decision. Missing files are not a setup blocker. Add glossary entries or ADRs only when the task resolves a durable ambiguity or decision, not for every edit. Reuse unchanged material within the task.
@@ -1,45 +1,9 @@
1
- # Issue tracker: GitHub
1
+ # Issue tracker: shared Feishu Tasks
2
2
 
3
- Issues and specs for this repo live as GitHub issues. Use the `gh` CLI for all operations.
3
+ Matt engineering issues, specs and tickets for this repo live in **AlphaFox-Issues**, not GitHub Issues. Prefix titles with `[alphafox-cli]`. GitHub hosts code and PRs; a PR URL may be linked from the task.
4
4
 
5
- ## Conventions
5
+ On task operations, read the infra `docs/agents/issue-tracker.md` for canonical GUIDs, sections, Type options and dependency conventions. Resolve `alphafox-infra` in the workspace described in this repo's AGENTS.md (or its explicitly selected task worktree). Read the installed `lark-task` Skill for current commands; do not maintain a second CLI recipe here. If the shared file is unavailable, discover the named existing list using the Skill and verify its identity before writing; do not create a replacement list.
6
6
 
7
- - **Create an issue**: `gh issue create --title "..." --body "..."`. Use a heredoc for multi-line bodies.
8
- - **Read an issue**: `gh issue view <number> --comments`, filtering comments by `jq` and also fetching labels.
9
- - **List issues**: `gh issue list --state open --json number,title,body,labels,comments --jq '[.[] | {number, title, body, labels: [.labels[].name], comments: [.comments[].body]}]'` with appropriate `--label` and `--state` filters.
10
- - **Comment on an issue**: `gh issue comment <number> --body "..."`
11
- - **Apply / remove labels**: `gh issue edit <number> --add-label "..."` / `--remove-label "..."`
12
- - **Close**: `gh issue close <number> --comment "..."`
7
+ A linked task's acceptance is the specification. Without a linked task, use the explicit user request and record it in the PR; task creation is not a prerequisite for local work. Routine delivery comments stay within existing task authorization; new scope or tasklists need their own decision.
13
8
 
14
- Infer the repo from `git remote -v`; `gh` does this automatically when run inside a clone.
15
-
16
- ## Pull requests as a triage surface
17
-
18
- **PRs as a request surface: no.** _(Set to `yes` if this repo treats external PRs as feature requests; `/triage` reads this flag.)_
19
-
20
- When set to `yes`, PRs run through the same labels and states as issues, using the `gh pr` equivalents:
21
-
22
- - **Read a PR**: `gh pr view <number> --comments` and `gh pr diff <number>` for the diff.
23
- - **List external PRs for triage**: `gh pr list --state open --json number,title,body,labels,author,authorAssociation,comments` then keep only `authorAssociation` of `CONTRIBUTOR`, `FIRST_TIME_CONTRIBUTOR`, or `NONE` (drop `OWNER`/`MEMBER`/`COLLABORATOR`).
24
- - **Comment / label / close**: `gh pr comment`, `gh pr edit --add-label`/`--remove-label`, `gh pr close`.
25
-
26
- GitHub shares one number space across issues and PRs, so a bare `#42` may be either: resolve with `gh pr view 42` and fall back to `gh issue view 42`.
27
-
28
- ## When a skill says "publish to the issue tracker"
29
-
30
- Create a GitHub issue.
31
-
32
- ## When a skill says "fetch the relevant ticket"
33
-
34
- Run `gh issue view <number> --comments`.
35
-
36
- ## Wayfinding operations
37
-
38
- Used by `/wayfinder`. The **map** is a single issue with **child** issues as tickets.
39
-
40
- - **Map**: a single issue labelled `wayfinder:map`, holding the Notes / Decisions-so-far / Fog body. `gh issue create --label wayfinder:map`.
41
- - **Child ticket**: an issue linked to the map as a GitHub sub-issue (`gh api` on the sub-issues endpoint). Where sub-issues aren't enabled, add the child to a task list in the map body and put `Part of #<map>` at the top of the child body. Labels: `wayfinder:<type>` (`research`/`prototype`/`grilling`/`task`). Once claimed, the ticket is assigned to the driving dev.
42
- - **Blocking**: GitHub's **native issue dependencies**, the canonical, UI-visible representation. Add an edge with `gh api --method POST repos/<owner>/<repo>/issues/<child>/dependencies/blocked_by -F issue_id=<blocker-db-id>`, where `<blocker-db-id>` is the blocker's numeric **database id** (`gh api repos/<owner>/<repo>/issues/<n> --jq .id`, _not_ the `#number` or `node_id`). GitHub reports `issue_dependencies_summary.blocked_by` (open blockers only, the live gate). Where dependencies aren't available, fall back to a `Blocked by: #<n>, #<n>` line at the top of the child body. A ticket is unblocked when every blocker is closed.
43
- - **Frontier query**: list the map's open children (`gh issue list --state open`, scoped to the map's sub-issues / task list), drop any with an open blocker (`issue_dependencies_summary.blocked_by > 0`, or an open issue in the `Blocked by` line) or an assignee; first in map order wins.
44
- - **Claim**: `gh issue edit <n> --add-assignee @me`, the session's first write.
45
- - **Resolve**: `gh issue comment <n> --body "<answer>"`, then `gh issue close <n>`, then append a context pointer (gist + link) to the map's Decisions-so-far.
9
+ **PRs as a request surface: no.** For a specifically requested PR triage, inspect it on GitHub and keep engineering task state in Feishu.
@@ -1,15 +1,3 @@
1
- # Triage Labels
1
+ # Triage mapping
2
2
 
3
- The skills speak in terms of five canonical triage roles. This file maps those roles to the actual label strings used in this repo's issue tracker.
4
-
5
- | Label in mattpocock/skills | Label in our tracker | Meaning |
6
- | -------------------------- | -------------------- | ---------------------------------------- |
7
- | `needs-triage` | `needs-triage` | Maintainer needs to evaluate this issue |
8
- | `needs-info` | `needs-info` | Waiting on reporter for more information |
9
- | `ready-for-agent` | `ready-for-agent` | Fully specified, ready for an AFK agent |
10
- | `ready-for-human` | `ready-for-human` | Requires human implementation |
11
- | `wontfix` | `wontfix` | Will not be actioned |
12
-
13
- When a skill mentions a role (e.g. "apply the AFK-ready triage label"), use the corresponding label string from this table.
14
-
15
- Edit the right-hand column to match whatever vocabulary you actually use.
3
+ Read only when classifying a Matt engineering task. Canonical triage roles map to Feishu **sections**, not GitHub labels; categories map to the separate single-select **Type** field. The infra `docs/agents/issue-tracker.md` owns the mapping and GUIDs; resolve it through this repo's AGENTS.md. A section is not a completion status: complete/incomplete follows task acceptance. Do not rename sections or create Type options just to match a Skill's spelling.
@@ -0,0 +1,7 @@
1
+ # Runtime discovery migration
2
+
3
+ New CLI releases discover the protocol-1 runtime through the environment's Engine API and download the same nine files from GCS / Cloud CDN. SHA256 runtime verification and the existing content cache remain enabled. `--profile staging` selects staging; production selects production. A local profile requires an explicit manifest URL or local runtime build.
4
+
5
+ `ALPHAFOX_BACKTEST_WASM_MANIFEST_URL` remains an explicit override. Discovery errors are reported; no automatic switch to the old Blob URL occurs.
6
+
7
+ Historical packages keep their original Blob endpoint and frozen compatible files. The support end date is not yet defined; do not delete them based on a short period of low traffic. Upgrade with `npm install -g @alphafox/cli` after the new release has been published. A PR is not an npm release.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alphafox/cli",
3
- "version": "0.3.22",
3
+ "version": "0.3.24",
4
4
  "description": "AlphaFox CLI — Agent/Human entry for the public Application API.",
5
5
  "type": "commonjs",
6
6
  "bin": {
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: alphafox-account
3
3
  description: Account, wallet, and subscription read paths.
4
- version: 0.3.22
4
+ version: 0.3.24
5
5
  ---
6
6
 
7
7
  # Account / wallet
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: alphafox-admin
3
3
  description: Admin-only operations reusing Web role authorization.
4
- version: 0.3.22
4
+ version: 0.3.24
5
5
  ---
6
6
 
7
7
  # Admin
@@ -1,14 +1,14 @@
1
1
  ---
2
2
  name: alphafox
3
- description: AlphaFox CLI entry router. Use for any AlphaFox request install, update, login, whoami, 回测, engine backtest, 清理回测缓存 / 历史数据, strategy definitions, create/list/start/stop a running strategy (trader), ticker/标的 resolve (美股 or crypto), market data, exchange connectors, wallet, subscriptions, notifications, or admin. After 回测 or 运行策略, include the dashboard URL from the domain skill. When the user asks 排行榜, include https://www.alphafox.app/zh/dashboard/leaderboard. After a successful install and login, present the 新人引导 in this file (Lite square 带单员 + classic strategies). If a CLI command prints `[alphafox] update available`, ask the user「检测到新的版本,是否需要我帮你升级?」and only then run `alphafox update --format json --no-input`. After a large backtest, if tape cache is large, ask「回测下载的历史数据比较大,要不要我帮你清理本地缓存?」then open `alphafox-cache`. Start here, then open the routed domain skill. Do not guess alphafox-engine-backtest vs alphafox-strategy vs alphafox-trading from memory.
4
- version: 0.3.22
3
+ description: "Route AlphaFox product CLI requests: installation/auth, market data, strategy configuration, backtests, traders, connectors, accounts and notifications. Repository development or AGENTS/Skills maintenance uses repository instructions, not product CLI operations."
4
+ version: 0.3.24
5
5
  ---
6
6
 
7
7
  # AlphaFox
8
8
 
9
9
  This skill only routes. After choosing a row, **read that skill's `SKILL.md` and follow it**. Do not improvise domain procedures from this file.
10
10
 
11
- Also read `alphafox-shared` before any CLI invocation (envelope, auth, risk, schema-first writes). Always `--format json --no-input`. Never `--token`.
11
+ Read `alphafox-shared` before the first CLI invocation in this task; reuse it while unchanged (envelope, auth, risk, schema-first writes). Always `--format json --no-input`. Never `--token`.
12
12
 
13
13
  Human-mentioned tickers go through `alphafox-market` (`alphafox resolve-symbols`) **before** they enter config, backtest, or writes. Keep the operator's asset class (美股 → `equity_perp` on `binance_perp_usdt`).
14
14
 
@@ -31,7 +31,7 @@ A **trader** is a running strategy instance (paper or live), not a person. Creat
31
31
  | Notification channels | `alphafox-notification` |
32
32
  | Admin-only operations | `alphafox-admin` |
33
33
 
34
- If several rows apply, load **all** of them (typical: `alphafox-shared` + `alphafox-market` + one domain skill).
34
+ Load only rows needed for the requested operation, reusing Skills already read in this task (typical: shared + market when resolving a ticker + the operation Skill).
35
35
 
36
36
  - “帮我配/建一个网格/DCA/跟单策略” → `alphafox-strategy` (pick definition, ask knobs, validate `{common, strategy}`) **and** `alphafox-market` (resolve tickers) **and** `alphafox-trading` (create the trader, default `autoStart: true`). Hidden copy variants still create through `alphafox-trading`. After create, include the trader URL from `alphafox-shared`.
37
37
  - “帮我回测这个配置” → `alphafox-strategy` (definition + config) **and** `alphafox-engine-backtest`. After a persisted run, include the backtest URL from `alphafox-shared`.
@@ -39,25 +39,7 @@ If several rows apply, load **all** of them (typical: `alphafox-shared` + `alpha
39
39
 
40
40
  ## Upgrade reminder
41
41
 
42
- The CLI may print this on **stderr** at most once every 24 hours:
43
-
44
- ```text
45
- [alphafox] update available: 0.3.15 -> 0.3.16. After the user confirms, run: alphafox update --format json --no-input,
46
- ```
47
-
48
- If you see that notice (or `updateAvailable: true` from `alphafox update --check`):
49
-
50
- 1. Ask the user: **检测到新的版本,是否需要我帮你升级?**
51
- 2. Wait for an explicit yes. Do not upgrade on your own.
52
- 3. After they confirm:
53
-
54
- ```bash
55
- alphafox update --format json --no-input
56
- ```
57
-
58
- 4. Tell the user to **restart the AI tool** so the new Skills load.
59
-
60
- Do not install Skills from GitHub. Details and dry-run / check commands live in `alphafox-shared`.
42
+ An update notice does not interrupt the current task. Finish its deliverable first, then offer an upgrade if relevant. Use `alphafox-shared` for the co-versioned update procedure; an explicit update request already supplies intent. Do not independently install Skills from GitHub.
61
43
 
62
44
  ## After install
63
45
 
@@ -121,8 +103,4 @@ Ambiguous “帮我回测” → `alphafox-engine-backtest`, after resolving sym
121
103
 
122
104
  `engine-backtest run|sweep` downloads closed OHLCV into the local tape cache. After a long-range or 1m backtest (or whenever the operator mentions disk / 缓存), read `alphafox-cache` and run `alphafox cache status --format json --no-input`.
123
105
 
124
- If `data.tape.large` is true (tape `data.remindAfterBytes`):
125
-
126
- 1. Ask the user: **回测下载的历史数据比较大,要不要我帮你清理本地缓存?**
127
- 2. Wait for an explicit yes. Do not clean on your own.
128
- 3. Follow `alphafox-cache` (`alphafox cache clean --dry-run`, then `--yes`).
106
+ If `data.tape.large` is true, finish the backtest report first, then follow `alphafox-cache` for the optional cleanup offer. Reuse an explicit cleanup request; do not ask twice or clean an unrequested cache class.
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: alphafox-shared
3
3
  description: Shared AlphaFox CLI rules for Agents — auth, profiles, envelopes, risk gates, public operationIds, and dashboard links after 回测 / 运行策略 / 排行榜.
4
- version: 0.3.22
4
+ version: 0.3.24
5
5
  ---
6
6
 
7
7
  # AlphaFox shared Agent contract
@@ -39,7 +39,7 @@ The CLI checks npm at most once every 24 hours and only prints a notice on
39
39
 
40
40
  **检测到新的版本,是否需要我帮你升级?**
41
41
 
42
- Wait for an explicit yes. Then keep CLI and Skills co-versioned:
42
+ Finish the current task before offering an unrelated upgrade. An explicit upgrade request or approval in this task is sufficient; otherwise wait for it. Then keep CLI and Skills co-versioned:
43
43
 
44
44
  ```bash
45
45
  alphafox update --check --format json --no-input
@@ -82,7 +82,7 @@ Access tokens last ~10 minutes; the CLI refreshes them. After idle, run **one**
82
82
 
83
83
  Local browser: `alphafox auth login --browser --format json --no-input` (loopback 127.0.0.1). If the browser cannot open, copy `authorizeUrl` from the error; do not invent a Device Flow retry unless the operator is headless.
84
84
 
85
- Wrong environment / missing permission / missing `--yes`: stop. Do not retry with a different profile.
85
+ Wrong environment or missing permission blocks that operation; keep the requested profile and report the needed correction. Missing `--yes` is a confirmation gate: show the exact action and reuse an existing explicit approval only if it covers these parameters, otherwise ask once. Continue independent reads/local work where useful; do not switch profiles to evade a gate.
86
86
 
87
87
  ## Commands
88
88
 
@@ -99,13 +99,13 @@ Forbidden: `/backend`, `/control-plane`, `/signal-center`, internal secrets, non
99
99
 
100
100
  ## Writes — schema first, never invent fields
101
101
 
102
- Before every write (`POST` / `PUT` / `PATCH` / `DELETE` with a body):
102
+ Before composing a write body (`POST` / `PUT` / `PATCH` / `DELETE`), obtain its operation schema. In this task, reuse a schema for the same CLI/contract/catalog version, profile and operationId. Refresh on version/profile changes or schema-validation errors. “Read schema first” in domain Skills uses this same rule:
103
103
 
104
- 1. Run `alphafox schema <operationId> --format json --no-input`.
104
+ 1. On first use or invalidation, run `alphafox schema <operationId> --format json --no-input`.
105
105
  2. Build the body **only** from `request.body` (property names, types, enums, required). Do not guess fields from memory, from another operationId, or from training data.
106
106
  3. Small object: typed command + `--body '<json>'`.
107
107
  4. Nested / large object: write a JSON file, then `--config @./payload.json`. Do not paste 20+ fields onto argv.
108
- 5. `--dry-run` first when the risk is `write` or `high-risk-write`.
108
+ 5. Preview high-risk writes and new/changed mutation payloads with `--dry-run`. A previously verified low-risk payload shape does not require repeating discovery and dry-run on every batch item; per-request CLI validation still applies. Verify that the command actually supports dry-run.
109
109
 
110
110
  CLI validates `--body` / `--config` against the catalog **before** HTTP. `body_schema` / `body_schema_missing` (exit `64`) means the payload is wrong — re-read `schema`, do not add extra keys to “make it work”. `--body` and `--config` cannot be combined. `--body @file` is also a file (same as `--config @file`).
111
111
 
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: alphafox-auth
3
3
  description: Login, status, logout, whoami, and environment isolation for AlphaFox CLI.
4
- version: 0.3.22
4
+ version: 0.3.24
5
5
  ---
6
6
 
7
7
  # Auth Skill
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: alphafox-cache
3
3
  description: Inspect and clean local Engine backtest caches (downloaded OHLCV tape and wasm runtime). Use when the user asks to 清理缓存, free disk, or after a large historical backtest.
4
- version: 0.3.22
4
+ version: 0.3.24
5
5
  ---
6
6
 
7
7
  # Cache
@@ -18,11 +18,11 @@ alphafox cache status --format json --no-input
18
18
 
19
19
  Read `data.tape.bytes`, `data.tape.files`, `data.tape.large`, `data.remindAfterBytes`. `large` is true when tape bytes ≥ `remindAfterBytes` (512 MiB).
20
20
 
21
- If `data.tape.large` is true, ask the user:
21
+ When cleanup was not requested and `data.tape.large` is true, first deliver the backtest result, then offer:
22
22
 
23
23
  **回测下载的历史数据比较大,要不要我帮你清理本地缓存?**
24
24
 
25
- Wait for an explicit yes. Do not clean on your own.
25
+ An explicit request to clean this cache or approval of this offer authorizes the displayed scope; do not ask twice. A size notice alone does not authorize deletion. Extra runtime/all-cache deletion needs its own scope.
26
26
 
27
27
  ## Clean
28
28
 
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: alphafox-engine-backtest
3
3
  description: Local Engine WASM backtest (alphafox engine-backtest run|sweep) vs catalog experiment CRUD. After a persisted run, include https://www.alphafox.app/zh/dashboard/traders/backtest/{experimentId}.
4
- version: 0.3.22
4
+ version: 0.3.24
5
5
  ---
6
6
 
7
7
  # Engine Backtest
@@ -33,7 +33,7 @@ alphafox schema engine_backtest.experiments.byId.sweeps.create --format json --n
33
33
 
34
34
  The tape runner ships inside the CLI (plus `ccxt` for public-market pulls). The wasm / Node host is downloaded from the public Vercel Blob manifest (`engine-backtest/latest.json`) into `~/.cache/alphafox/engine-backtest/<hash>/` on first run.
35
35
 
36
- Before planning or running, follow the complete parameter review in `alphafox-strategy`, including when the operator already supplied `--config` or pasted a full command. Do not run the backtest until every applicable parameter, default status, short explanation, proposed value, and value source has been shown and the operator explicitly confirms the final proposal. User overrides win; unresolved required values stop the flow.
36
+ For a requested backtest with supplied config, preserve and validate it against the definition; do not require the live-trader execution review. Use `alphafox-strategy` to resolve unknown fields or a requested guided configuration. Ask for unresolved required values or materially ambiguous choices. Respect the requested range, data-quality mode and persistence scope; a local-only request uses `--no-persist`. Live trader creation remains a separate requested and approved action.
37
37
 
38
38
  Local overrides, in order:
39
39
 
@@ -98,7 +98,7 @@ Owner isolation and 7-day expiry are enforced by the server. Applying a coordina
98
98
  2. `engine-backtest run` (reuse `--experiment` after the first create).
99
99
  3. Read `data.metrics` / `data.engineVersion` / `data.runId` / `data.experimentId` / `data.experimentUrl`. After the run, also read `data.coverageNotice` (`warning` = mid-range candle gaps; `notice` = start / other soft gaps). When an Experiment id exists, include the backtest dashboard URL from `alphafox-shared` (`https://www.alphafox.app/zh/dashboard/traders/backtest/{experimentId}`) in the reply — do not stop at metrics or the raw CLI `experimentUrl`.
100
100
  4. Adjust parameters and run again. Do not invent a token flag if persist returns 401 — `alphafox auth login`.
101
- 5. After a long-range or 1m run, follow `alphafox-cache`: `alphafox cache status`. If `data.tape.large` is true, ask **回测下载的历史数据比较大,要不要我帮你清理本地缓存?** and wait for yes.
101
+ 5. After a long-range or 1m run, follow `alphafox-cache`: `alphafox cache status`. If `data.tape.large` is true, finish the result first, then follow `alphafox-cache` for the optional cleanup offer; reuse any explicit cleanup approval.
102
102
 
103
103
  ## Safety
104
104
 
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: alphafox-exchange
3
3
  description: Exchange connectors list and connection management via Public API.
4
- version: 0.3.22
4
+ version: 0.3.24
5
5
  ---
6
6
 
7
7
  # Exchange connectors
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: alphafox-market
3
3
  description: Market data and ticker resolution for US equity perps, RWAs, and crypto on the same perp catalog. Use when the user names 美股, NVDA, AAPL, BTC, or any 标的. Keep the operator's asset class via symbolMetadata — do not rewrite NVDA into a crypto coin.
4
- version: 0.3.22
4
+ version: 0.3.24
5
5
  ---
6
6
 
7
7
  # Market
@@ -28,7 +28,7 @@ Binance US stocks are **equity perps in the same** `binance_perp_usdt` catalog (
28
28
 
29
29
  ## Resolve tickers
30
30
 
31
- Whenever a human mentions a stock, coin, ticker, or contract including typos resolve it **before** putting a symbol into strategy config, backtest, trader settings, or any write.
31
+ Resolve a human-mentioned ticker before using it in config, backtest or a write. Reuse an exact/confirmed result for the same exchange, asset class and catalog within this task; resolve again when any of those inputs changes or the result is rejected. Mere discussion of a ticker does not require a market API call.
32
32
 
33
33
  ```bash
34
34
  alphafox resolve-symbols BTC ETH --exchange binance --format json --no-input
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: alphafox-notification
3
3
  description: Notification channels and subscriptions.
4
- version: 0.3.22
4
+ version: 0.3.24
5
5
  ---
6
6
 
7
7
  # Notification
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: alphafox-strategy
3
3
  description: Strategy definitions — list types, read a definition's contract, and validate config. Creating a running strategy is creating a trader; use alphafox-trading for that. Local Engine backtest is alphafox-engine-backtest.
4
- version: 0.3.22
4
+ version: 0.3.24
5
5
  ---
6
6
 
7
7
  # Strategy definitions
@@ -38,7 +38,9 @@ Explain the type from those fields. Missing a layer → say unknown; do not fill
38
38
 
39
39
  ## Configure with the human
40
40
 
41
- The human confirms the complete parameter set. You write JSON only after that review.
41
+ Use the complete review below when preparing an actual trader creation/start, or when the user asks for a guided parameter design. It protects execution decisions and is not a gate for inspecting a definition, validating a supplied config, or a requested local backtest. For those tasks, preserve supplied values, validate applicable fields, and ask only for unresolved required inputs or a material ambiguity. Report completion of the requested operation; do not implicitly create/start a trader.
42
+
43
+ For the execution proposal, the human confirms the complete parameter set. Reuse that confirmation while the proposal and environment are unchanged. You may prepare and validate JSON to discover errors before the final execution approval.
42
44
 
43
45
  1. Confirm the definition from `byId.get` in the operator's language (what it is, what drives it, how positions change).
44
46
  2. Use the effective `configSchema` returned by `byId.get` as the sole parameter contract. It already composes the definition's `commonModules` with `strategyConfigSchema` and may contain definition-specific customization. Walk every applicable parameter, not only required fields or familiar knobs.
@@ -61,7 +63,7 @@ The human confirms the complete parameter set. You write JSON only after that re
61
63
  A user override always wins, even when it equals neither default. Preserve explicit `false`, `0`, empty arrays, and empty strings when the schema allows them; they are not missing values.
62
64
 
63
65
  6. When there are many parameters, present them in logical groups or numbered chunks so the review remains readable. After all groups are visible, ask the operator to reply **confirm all** / “全部确认”, or override paths/numbers. One overall confirmation is sufficient, but it must cover every displayed parameter. Apply overrides, show the affected rows again, and repeat until no required value is unresolved and the operator explicitly confirms the final proposal.
64
- 7. An earlier request such as “创建策略”, “运行回测”, or a pasted command/config is input to the proposal, not confirmation of the review. Do not validate, create, or backtest until the complete parameter review is explicitly confirmed.
66
+ 7. A generic creation request is not approval of undisclosed execution settings. Obtain final approval before creating/starting a trader. Inspection, config validation and requested local backtests may proceed without this execution review; changing the approved execution parameters or environment requires renewed approval.
65
67
  8. Write `strategy-config.json` as the trader object:
66
68
 
67
69
  ```json
@@ -94,7 +96,7 @@ alphafox trading strategy_definitions byId validate_config --definitionId <id> -
94
96
 
95
97
  `body_schema` / `body_schema_missing` (exit `64`): re-read the operation schema. Server field-path errors: fix that path. Do not retry with a different envelope.
96
98
 
97
- After it validates: create with `alphafox-trading` (default `autoStart: true`), or backtest with `alphafox-engine-backtest`. Do not create or backtest from this skill. Dashboard URLs after those actions live in `alphafox-shared`.
99
+ Validation-only work is complete with the result and any field errors. Continue to `alphafox-trading` or `alphafox-engine-backtest` only if the user requested that action; validation alone does not authorize creation/start or persisted work. Preserve the trading Skill’s execution approval. Dashboard URLs after requested actions live in `alphafox-shared`.
98
100
 
99
101
  ## operationIds
100
102
 
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: alphafox-trading
3
3
  description: Running strategies (traders) — create, list, start, and stop. A trader is a live or paper strategy instance (grid, dca, copy, …), not a person. Default Engine create uses autoStart true (创建即开始). Use autoStart false only when the user asks to create without starting. After create or start, include https://www.alphafox.app/zh/dashboard/traders/{traderId}.
4
- version: 0.3.22
4
+ version: 0.3.24
5
5
  ---
6
6
 
7
7
  # Running strategies (traders)
@@ -32,7 +32,7 @@ alphafox api GET /api/v1/trading/traders --format json --no-input
32
32
 
33
33
  Read `alphafox schema <operationId>` first. Body may only include documented `request.body` fields. Large / nested bodies use `--config @file`.
34
34
 
35
- Before composing or dry-running any create request, follow the complete parameter review in `alphafox-strategy`, including when the operator already supplied a config. Do not create a trader until every applicable parameter, default status, short explanation, proposed value, and value source has been shown and the operator explicitly confirms the final proposal. User overrides win; unresolved required values stop the flow.
35
+ Before executing create, follow the complete execution-parameter review in `alphafox-strategy`, including supplied configs. JSON preparation, validation and dry-run may precede approval. Reuse the confirmed proposal while parameters and environment are unchanged; obtain approval for any changed execution settings. User overrides win; resolve required values before create. Do not create merely because validation succeeded.
36
36
 
37
37
  ```bash
38
38
  alphafox schema trading.traders.create --format json --no-input