@siming-org/cli 0.8.5 → 0.8.7

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/index.js CHANGED
@@ -80,6 +80,12 @@ function userConfigPath() {
80
80
  }
81
81
  var SIMING_NO_CONFIG_FILES_ENV = "SIMING_NO_CONFIG_FILES";
82
82
  var immunityNotified = false;
83
+ var warnedInvalidPortOnce = /* @__PURE__ */ new Set();
84
+ function warnInvalidPortOnce(kind, message) {
85
+ if (warnedInvalidPortOnce.has(kind)) return;
86
+ warnedInvalidPortOnce.add(kind);
87
+ console.error(message);
88
+ }
83
89
  var SOURCE_FORM_MARK = "/packages/cli/";
84
90
  var NODE_MODULES_MARK = "/node_modules/";
85
91
  function notifyImmunityOnce() {
@@ -231,7 +237,7 @@ function resolveLocalSimingPort(env = process.env, paths) {
231
237
  if (portEnv !== void 0) {
232
238
  const n = Number(portEnv);
233
239
  if (Number.isInteger(n) && n >= 1 && n <= 65535) return n;
234
- console.error(`[siming] \u5FFD\u7565\u975E\u6CD5 PORT="${portEnv}"\uFF08\u5E94\u4E3A 1-65535 \u6574\u6570\uFF09`);
240
+ warnInvalidPortOnce("invalid-port-env", `[siming] \u5FFD\u7565\u975E\u6CD5 PORT="${portEnv}"\uFF08\u5E94\u4E3A 1-65535 \u6574\u6570\uFF09`);
235
241
  }
236
242
  const userPath = paths?.user ?? userConfigPath();
237
243
  const systemPath = paths?.system ?? SYSTEM_CONFIG_PATH;
@@ -242,7 +248,7 @@ function resolveLocalSimingPort(env = process.env, paths) {
242
248
  if (raw === void 0) continue;
243
249
  const n = typeof raw === "number" ? raw : Number(raw);
244
250
  if (Number.isInteger(n) && n >= 1 && n <= 65535) return n;
245
- console.error(`[siming] \u5FFD\u7565 ${absPath} \u4E2D\u975E\u6CD5 port="${String(raw)}"`);
251
+ warnInvalidPortOnce(`invalid-port-file:${absPath}`, `[siming] \u5FFD\u7565 ${absPath} \u4E2D\u975E\u6CD5 port="${String(raw)}"`);
246
252
  }
247
253
  }
248
254
  return 7777;
@@ -254,6 +260,12 @@ function maskMongoUri(uri) {
254
260
  // src/config/local-config.ts
255
261
  var CONFIG_DIR = join2(homedir2(), ".siming");
256
262
  var CONFIG_PATH = join2(CONFIG_DIR, "config.json");
263
+ var warnedOnce = /* @__PURE__ */ new Set();
264
+ function warnOnce(kind, message) {
265
+ if (warnedOnce.has(kind)) return;
266
+ warnedOnce.add(kind);
267
+ console.error(message);
268
+ }
257
269
  var ServerUrlSchema = z.string().regex(/^https?:\/\/\S+$/, "must be an http(s):// URL");
258
270
  function readLocalConfig(env = process.env, opts = {}) {
259
271
  if (opts.ignoreImmunity !== true && shouldSkipConfigFiles(env)) return null;
@@ -263,12 +275,12 @@ function readLocalConfig(env = process.env, opts = {}) {
263
275
  if (typeof parsed.serverUrl !== "string" || parsed.serverUrl.length === 0) return null;
264
276
  const r = ServerUrlSchema.safeParse(parsed.serverUrl);
265
277
  if (!r.success) {
266
- console.error(`[siming] \u5FFD\u7565\u975E\u6CD5 config.json serverUrl="${parsed.serverUrl}"\uFF0C\u964D\u7EA7\u5230\u9ED8\u8BA4`);
278
+ warnOnce("local-invalid-serverurl", `[siming] \u5FFD\u7565\u975E\u6CD5 config.json serverUrl="${parsed.serverUrl}"\uFF0C\u964D\u7EA7\u5230\u9ED8\u8BA4`);
267
279
  return null;
268
280
  }
269
281
  return { serverUrl: parsed.serverUrl };
270
282
  } catch (e) {
271
- console.error(`[siming] \u8BFB\u53D6 ~/.siming/config.json \u5931\u8D25: ${e instanceof Error ? e.message : String(e)}`);
283
+ warnOnce("local-read-failed", `[siming] \u8BFB\u53D6 ~/.siming/config.json \u5931\u8D25: ${e instanceof Error ? e.message : String(e)}`);
272
284
  return null;
273
285
  }
274
286
  }
@@ -290,16 +302,42 @@ function getLocalServerUrl() {
290
302
  }
291
303
 
292
304
  // src/config/workspace-config.ts
293
- import { existsSync as existsSync3, mkdirSync as mkdirSync2, readFileSync as readFileSync3, writeFileSync as writeFileSync2, rmSync, renameSync } from "fs";
294
- import { join as join3, dirname } from "path";
305
+ import { existsSync as existsSync3, mkdirSync as mkdirSync2, readFileSync as readFileSync3, statSync, writeFileSync as writeFileSync2, rmSync, renameSync } from "fs";
306
+ import { join as join3, dirname, resolve as resolve2 } from "path";
295
307
  import { z as z2 } from "zod";
296
308
  var WORKSPACE_DIR_NAME = ".siming";
297
309
  var CONFIG_FILE = "config.json";
310
+ var warnedOnce2 = /* @__PURE__ */ new Set();
311
+ function warnOnce2(kind, message) {
312
+ if (warnedOnce2.has(kind)) return;
313
+ warnedOnce2.add(kind);
314
+ console.error(message);
315
+ }
298
316
  var DefaultProjectSchema = z2.string().regex(/^(?![a-f0-9]{24}$)[a-z0-9][a-z0-9-]{1,30}$/, "must be a project key (kebab-case, 2-31 chars; not a MongoDB id)");
317
+ function mainRepoRootOf(dir) {
318
+ try {
319
+ const st = statSync(join3(dir, ".git"));
320
+ if (!st.isFile()) return null;
321
+ } catch {
322
+ return null;
323
+ }
324
+ try {
325
+ const gitLine = readFileSync3(join3(dir, ".git"), "utf-8").trim();
326
+ if (!gitLine.startsWith("gitdir:")) return null;
327
+ const gitdir = resolve2(dir, gitLine.slice("gitdir:".length).trim());
328
+ const commondir = readFileSync3(join3(gitdir, "commondir"), "utf-8").trim();
329
+ if (commondir.length === 0) return null;
330
+ return dirname(resolve2(gitdir, commondir));
331
+ } catch {
332
+ return null;
333
+ }
334
+ }
299
335
  function findWorkspaceDir(start) {
300
336
  let dir = start !== void 0 && start.length > 0 ? start : process.cwd();
301
337
  for (; ; ) {
302
338
  if (existsSync3(join3(dir, WORKSPACE_DIR_NAME))) return dir;
339
+ const mainRoot = mainRepoRootOf(dir);
340
+ if (mainRoot !== null && existsSync3(join3(mainRoot, WORKSPACE_DIR_NAME))) return mainRoot;
303
341
  const parent = dirname(dir);
304
342
  if (parent === dir) return null;
305
343
  dir = parent;
@@ -319,16 +357,16 @@ function readWorkspaceConfig(workspaceDir) {
319
357
  if (typeof parsed.serverUrl === "string" && parsed.serverUrl.length > 0) {
320
358
  const r = ServerUrlSchema.safeParse(parsed.serverUrl);
321
359
  if (r.success) cfg.serverUrl = r.data;
322
- else console.error(`[siming] \u5FFD\u7565\u975E\u6CD5 .siming/config.json serverUrl="${parsed.serverUrl}"`);
360
+ else warnOnce2("workspace-invalid-serverurl", `[siming] \u5FFD\u7565\u975E\u6CD5 .siming/config.json serverUrl="${parsed.serverUrl}"`);
323
361
  }
324
362
  if (typeof parsed.defaultProject === "string" && parsed.defaultProject.length > 0) {
325
363
  const r = DefaultProjectSchema.safeParse(parsed.defaultProject);
326
364
  if (r.success) cfg.defaultProject = r.data;
327
- else console.error(`[siming] \u5FFD\u7565\u975E\u6CD5 .siming/config.json defaultProject="${parsed.defaultProject}"`);
365
+ else warnOnce2("workspace-invalid-defaultproject", `[siming] \u5FFD\u7565\u975E\u6CD5 .siming/config.json defaultProject="${parsed.defaultProject}"`);
328
366
  }
329
367
  return Object.keys(cfg).length > 0 ? cfg : null;
330
368
  } catch (e) {
331
- console.error(`[siming] \u8BFB\u53D6\u5DE5\u4F5C\u533A\u914D\u7F6E\u5931\u8D25: ${e instanceof Error ? e.message : String(e)}`);
369
+ warnOnce2("workspace-read-failed", `[siming] \u8BFB\u53D6\u5DE5\u4F5C\u533A\u914D\u7F6E\u5931\u8D25: ${e instanceof Error ? e.message : String(e)}`);
332
370
  return null;
333
371
  }
334
372
  }
@@ -522,18 +560,23 @@ function writeGlobalAuthToken(token, path) {
522
560
 
523
561
  // src/client/http-client.ts
524
562
  var SimingHttpClient = class {
525
- /** T202609150002:通道 source 标识(x-siming-source header 单一注入口;Web 通道由 web client 固定携带 web) */
563
+ /** T202609150002:通道 source 标识(x-siming-source header 单一注入口;Web 通道由 web client 固定携带 web)。
564
+ * baseUrl 可传常量或 resolver(T202610150001):长驻进程(mcp-serve)传 resolver 每请求现解析——
565
+ * 与 token 每请求解析(request() 内 resolveAuthToken)同策略,配置补齐/变更后自愈,无需重启。 */
526
566
  constructor(baseUrl, source = "cli") {
527
567
  this.baseUrl = baseUrl;
528
568
  this.source = source;
529
569
  }
530
570
  baseUrl;
531
571
  source;
532
- /** server base URL(诊断输出用——auth whoami 展示目标地址) */
572
+ resolveBase() {
573
+ return typeof this.baseUrl === "string" ? this.baseUrl : this.baseUrl();
574
+ }
575
+ /** server base URL(诊断输出用——auth whoami 展示目标地址);每访问现解析 */
533
576
  get url() {
534
- return this.baseUrl;
577
+ return this.resolveBase();
535
578
  }
536
- async request(method, path, body) {
579
+ async request(method, path, body, baseOverride) {
537
580
  const token = resolveAuthToken();
538
581
  const headers = {
539
582
  ...token !== void 0 ? { Authorization: `Bearer ${token}` } : {},
@@ -541,7 +584,8 @@ var SimingHttpClient = class {
541
584
  // T202609150002:issue 域通道注入(服务端按 header 判定 source,缺省 web)
542
585
  "x-siming-source": this.source
543
586
  };
544
- return fetch(`${this.baseUrl}${path}`, {
587
+ const base = baseOverride ?? this.resolveBase();
588
+ return fetch(`${base}${path}`, {
545
589
  method,
546
590
  headers,
547
591
  ...body !== void 0 ? { body: JSON.stringify(body) } : {}
@@ -561,25 +605,27 @@ var SimingHttpClient = class {
561
605
  }
562
606
  /** DELETE 返回 204 无 body,成功即 ok(void) */
563
607
  async delete(path) {
608
+ const base = this.resolveBase();
564
609
  try {
565
- const res = await this.request("DELETE", path);
610
+ const res = await this.request("DELETE", path, void 0, base);
566
611
  if (res.ok) return ok2(void 0);
567
612
  const body = await res.json().catch(() => void 0);
568
613
  return fail(extractError(res, body));
569
614
  } catch (e) {
570
- return fail(networkError(e));
615
+ return fail(networkError(e, `${base}${path}`));
571
616
  }
572
617
  }
573
618
  async json(method, path, body) {
619
+ const base = this.resolveBase();
574
620
  try {
575
- const res = await this.request(method, path, body);
621
+ const res = await this.request(method, path, body, base);
576
622
  const data = await res.json().catch(() => void 0);
577
623
  if (!res.ok) {
578
624
  return fail(extractError(res, data));
579
625
  }
580
626
  return ok2(data);
581
627
  } catch (e) {
582
- return fail(networkError(e));
628
+ return fail(networkError(e, `${base}${path}`));
583
629
  }
584
630
  }
585
631
  };
@@ -614,31 +660,55 @@ function extractError(res, body) {
614
660
  }
615
661
  return error;
616
662
  }
617
- function networkError(e) {
618
- return { message: e instanceof Error ? e.message : String(e), httpStatus: 0 };
663
+ var warnedOnce3 = /* @__PURE__ */ new Set();
664
+ function warnOnce3(kind, message) {
665
+ if (warnedOnce3.has(kind)) return;
666
+ warnedOnce3.add(kind);
667
+ console.error(message);
668
+ }
669
+ function networkError(e, target) {
670
+ const base = e instanceof Error ? e.message : String(e);
671
+ let message = base;
672
+ if (e instanceof Error && e.cause !== void 0 && e.cause !== null) {
673
+ const cause = e.cause;
674
+ const code = cause !== null && typeof cause === "object" && "code" in cause ? cause.code : void 0;
675
+ const causeMsg = typeof code === "string" && code.length > 0 ? code : cause instanceof Error ? cause.message : String(cause);
676
+ message = `${base}\uFF08${causeMsg}\uFF09`;
677
+ }
678
+ if (target !== void 0) message = `${message} [${target}]`;
679
+ return { message, httpStatus: 0 };
680
+ }
681
+ function resolveServerUrlDetailed(baseUrl) {
682
+ if (baseUrl !== void 0) return { url: baseUrl.replace(/\/$/, ""), source: "flag" };
683
+ const envUrl = process.env.SIMING_SERVER_URL;
684
+ if (envUrl !== void 0) {
685
+ const r = ServerUrlSchema.safeParse(envUrl);
686
+ if (r.success) return { url: r.data.replace(/\/$/, ""), source: "env" };
687
+ warnOnce3("invalid-env-serverurl", `[siming] \u5FFD\u7565\u975E\u6CD5 SIMING_SERVER_URL="${envUrl}"\uFF0C\u964D\u7EA7\u5230\u672C\u5730\u914D\u7F6E/\u9ED8\u8BA4`);
688
+ }
689
+ const workspaceUrl = readWorkspaceConfig()?.serverUrl;
690
+ if (workspaceUrl !== void 0) return { url: workspaceUrl.replace(/\/$/, ""), source: "workspace" };
691
+ const local = readLocalConfig();
692
+ if (local !== null) return { url: local.serverUrl.replace(/\/$/, ""), source: "global" };
693
+ return { url: `http://localhost:${resolveLocalSimingPort()}`, source: "default" };
619
694
  }
620
695
  function resolveServerUrl(baseUrl) {
621
- let url = baseUrl;
622
- if (url === void 0) {
623
- const envUrl = process.env.SIMING_SERVER_URL;
624
- if (envUrl !== void 0) {
625
- const r = ServerUrlSchema.safeParse(envUrl);
626
- if (r.success) {
627
- url = r.data;
628
- } else {
629
- console.error(`[siming] \u5FFD\u7565\u975E\u6CD5 SIMING_SERVER_URL="${envUrl}"\uFF0C\u964D\u7EA7\u5230\u672C\u5730\u914D\u7F6E/\u9ED8\u8BA4`);
630
- }
631
- }
632
- }
633
- url = url ?? readWorkspaceConfig()?.serverUrl ?? getLocalServerUrl();
634
- return url.replace(/\/$/, "");
696
+ return resolveServerUrlDetailed(baseUrl).url;
635
697
  }
636
698
  function createClient(baseUrl, source) {
637
- return new SimingHttpClient(resolveServerUrl(baseUrl), source);
699
+ if (baseUrl !== void 0) return new SimingHttpClient(resolveServerUrl(baseUrl), source);
700
+ return new SimingHttpClient(() => resolveServerUrl(), source);
638
701
  }
639
702
 
640
703
  // src/commands/healthcheck.ts
641
704
  var HEALTHCHECK_TIMEOUT_MS = 2e3;
705
+ var SOURCE_LABEL = {
706
+ flag: "CLI \u53C2\u6570 --url",
707
+ env: "\u73AF\u5883\u53D8\u91CF SIMING_SERVER_URL",
708
+ workspace: "\u5DE5\u4F5C\u533A .siming/config.json",
709
+ global: "\u5168\u5C40 ~/.siming/config.json",
710
+ default: "\u9ED8\u8BA4\u63A8\u5BFC"
711
+ };
642
712
  async function runHealthcheck(opts) {
643
713
  try {
644
714
  const res = await fetch(`${opts.url}/health`, { signal: AbortSignal.timeout(HEALTHCHECK_TIMEOUT_MS) });
@@ -652,12 +722,21 @@ async function runHealthcheck(opts) {
652
722
  }
653
723
  }
654
724
  async function healthcheckCommand(opts) {
655
- const result = await runHealthcheck({ url: resolveServerUrl(opts.url) });
725
+ const resolved = resolveServerUrlDetailed(opts.url);
726
+ const result = await runHealthcheck({ url: resolved.url });
656
727
  if (result.ok) {
657
- ok(`server healthy (v${result.version})`);
728
+ ok(`server healthy (v${result.version}) [${resolved.url}\uFF08\u6765\u6E90: ${SOURCE_LABEL[resolved.source]}\uFF09]`);
729
+ } else if (resolved.source === "default") {
730
+ err(
731
+ `\u672A\u53D1\u73B0\u4EFB\u4F55 siming \u914D\u7F6E\uFF08env/\u5DE5\u4F5C\u533A/\u5168\u5C40\u5C42\u5747\u672A\u8BBE\u7F6E serverUrl\uFF09\uFF0C\u6309\u9ED8\u8BA4\u5730\u5740 ${resolved.url} \u63A2\u6D4B\u5931\u8D25: ${result.error ?? "unknown"}`
732
+ );
733
+ info(
734
+ "hint: server \u672A\u542F\u52A8\u5219\u5148\u8FD0\u884C `siming server start`\uFF1Bserver \u5DF2\u542F\u52A8\u4F46\u7AEF\u53E3\u4E0D\u540C\uFF0C\u5219\u5728\u9879\u76EE\u6839\u521B\u5EFA .siming/config.json \u914D\u7F6E serverUrl\uFF0C\u6216\u8BBE\u7F6E\u73AF\u5883\u53D8\u91CF SIMING_SERVER_URL"
735
+ );
736
+ process.exitCode = 1;
658
737
  } else {
659
- err(`server unreachable: ${result.error ?? "unknown"}`);
660
- info("hint: run `siming server start` (Phase-1 setup) or check the URL");
738
+ err(`server \u4E0D\u53EF\u8FBE\uFF08${resolved.url}\uFF0C\u6765\u6E90: ${SOURCE_LABEL[resolved.source]}\uFF09: ${result.error ?? "unknown"}`);
739
+ info("hint: \u786E\u8BA4\u8BE5\u5730\u5740\u4E0A\u7684 server \u662F\u5426\u5B58\u6D3B\uFF08siming server status\uFF09\uFF1B\u5730\u5740\u4E0D\u5BF9\u5219\u4FEE\u6B63\u5BF9\u5E94\u6765\u6E90\u5C42\u7684 serverUrl \u914D\u7F6E");
661
740
  process.exitCode = 1;
662
741
  }
663
742
  }
@@ -1089,7 +1168,18 @@ ${ctx.reposScanDetail}`
1089
1168
  const leakByRepo = [];
1090
1169
  const errRepos = [];
1091
1170
  for (const repo of hitRepos) {
1092
- const r = await ctx.runGit(["diff", "--name-only", `${sha}..${base}`], repo);
1171
+ const cnt = await ctx.runGit(["rev-list", "--count", `${base}..${sha}`], repo);
1172
+ if (!cnt.ok) {
1173
+ errRepos.push(repo);
1174
+ continue;
1175
+ }
1176
+ const n = Number.parseInt(cnt.stdout.trim(), 10);
1177
+ if (!Number.isSafeInteger(n) || n < 0) {
1178
+ errRepos.push(repo);
1179
+ continue;
1180
+ }
1181
+ if (n === 0) continue;
1182
+ const r = await ctx.runGit(["diff", "--name-only", `${base}...${sha}`], repo);
1093
1183
  if (!r.ok) {
1094
1184
  errRepos.push(repo);
1095
1185
  continue;
@@ -1098,7 +1188,7 @@ ${ctx.reposScanDetail}`
1098
1188
  if (leaked.length > 0) leakByRepo.push({ repo, leaked });
1099
1189
  }
1100
1190
  if (errRepos.length > 0) {
1101
- return fail3(type, `\u7248\u672C diff \u5931\u8D25\uFF08\u4ED3 ${errRepos.join("\u3001")}\uFF09`, `\u68C0\u67E5 verifiedCommit \u4E0E\u57FA\u7840\u5206\u652F\u53EF\u7528\u6027
1191
+ return fail3(type, `\u7248\u672C\u5224\u5B9A\u5931\u8D25\uFF08\u4ED3 ${errRepos.join("\u3001")}\uFF09`, `\u68C0\u67E5 verifiedCommit \u4E0E\u57FA\u7840\u5206\u652F\u53EF\u7528\u6027
1102
1192
  ${ctx.reposScanDetail}`);
1103
1193
  }
1104
1194
  if (leakByRepo.length > 0) {
@@ -1106,8 +1196,8 @@ ${ctx.reposScanDetail}`);
1106
1196
  const suffix = leakByRepo.length > 3 ? `\uFF08\u53E6\u6709 ${leakByRepo.length - 3} \u4ED3\u6CC4\u6F0F\u89C1\u9010\u4ED3\u91CD\u67E5\uFF09` : "";
1107
1197
  return fail3(
1108
1198
  type,
1109
- `verifiedCommit \u4E0E ${base} \u5B58\u5728\u975E\u6587\u6863\u5DEE\u5F02\u2014\u2014${lines.join("\uFF1B")}${suffix}`,
1110
- `\u4EE3\u7801\u6539\u52A8\u987B\u5408\u5E76\u8FDB\u57FA\u7840\u5206\u652F\uFF08\u542B\u5E76\u884C\u4EFB\u52A1 base \u524D\u8FDB\u573A\u666F\uFF1A\u91CD\u8D70\u5408\u5E76\u6216\u7ECF\u7528\u6237\u660E\u793A\u540E force \u63A8\u8FDB\u5E76\u7559\u75D5\uFF09
1199
+ `verifiedCommit \u672A\u5B8C\u5168\u8FDB\u5165\u57FA\u7840\u5206\u652F ${base}\u2014\u2014${lines.join("\uFF1B")}${suffix}`,
1200
+ `\u5C06\u542B\u9A8C\u8BC1\u7248\u672C\u7684\u4EFB\u52A1\u5206\u652F\u5408\u5E76\u8FDB\u57FA\u7840\u5206\u652F\u540E\u91CD\u8BD5\uFF08\u5E76\u884C\u4EFB\u52A1\u81F4\u57FA\u7840\u5206\u652F\u524D\u8FDB\u7684\u5206\u53C9\u573A\u666F\uFF1A\u91CD\u8D70\u5408\u5E76\uFF1B\u65E0\u6CD5\u53EF\u9760\u5408\u5E76\u65F6\u7ECF\u7528\u6237\u660E\u793A\u540E force \u63A8\u8FDB\u5E76\u7559\u75D5\uFF09
1111
1201
  ${ctx.reposScanDetail}`
1112
1202
  );
1113
1203
  }
@@ -1116,7 +1206,7 @@ ${ctx.reposScanDetail}`
1116
1206
  return {
1117
1207
  type,
1118
1208
  status: "pass",
1119
- detail: `verifiedCommit ${sha} \u4E0E ${base} \u4E00\u81F4\uFF08\u547D\u4E2D\u4ED3 ${hitRepos.join("\u3001")}\uFF1B\u6587\u6863\u8DEF\u5F84\u8C41\u514D ${ctx.docPaths.join(" ")}${naPart}\uFF09
1209
+ detail: `verifiedCommit ${sha} \u5DF2\u5305\u542B\u4E8E\u57FA\u7840\u5206\u652F ${base}\uFF08\u57FA\u7840\u5206\u652F\u524D\u8FDB\u4E0D\u5224\u6CC4\u6F0F\uFF1B\u547D\u4E2D\u4ED3 ${hitRepos.join("\u3001")}\uFF1B\u6587\u6863\u8DEF\u5F84\u8C41\u514D ${ctx.docPaths.join(" ")}${naPart}\uFF09
1120
1210
  ${ctx.reposScanDetail}`
1121
1211
  };
1122
1212
  }
@@ -1222,13 +1312,13 @@ function auditAiChecks(aiChecks, record) {
1222
1312
  results.push({ id: check.id, item: check.item, status: "pass", detail: "\u5B8C\u6210\u5224\u5B9A\u9879\u5DF2\u81EA\u68C0\u901A\u8FC7\uFF08refId \u5173\u8054\uFF09" });
1223
1313
  continue;
1224
1314
  }
1225
- const rejected = (record?.checks ?? []).some((c) => c.refId === check.id && c.passed === false);
1315
+ const rejected = (record?.checks ?? []).find((c) => c.refId === check.id && c.passed === false);
1226
1316
  if (rejected) {
1227
1317
  results.push({
1228
1318
  id: check.id,
1229
1319
  item: check.item,
1230
1320
  status: "fail",
1231
- detail: `\u7559\u75D5\u7ED3\u8BBA\u672A\u901A\u8FC7\uFF08check ${check.id} passed=false\uFF09\u3002\u4FEE\u590D\uFF1A\u91CD\u505A\u540E\u7FFB\u7EFF\uFF08record-check-patch --check ${check.id} --passed\uFF09\u6216\u5982\u5B9E\u8865\u5199\u4E0D\u9002\u7528`
1321
+ detail: `\u7559\u75D5\u7ED3\u8BBA\u672A\u901A\u8FC7\uFF08entry ${rejected.id} refId=${check.id} passed=false\uFF09\u3002\u4FEE\u590D\uFF1A\u91CD\u505A\u540E\u6309 entry \u7FFB\u7EFF\uFF08CLI record-check-patch --check ${rejected.id}\uFF1BMCP action=record-check-patch checkId=${rejected.id} passed=true\uFF09\uFF0C\u6216\u91CD\u505A\u540E record-check-add \u5E26 refId=${check.id} \u91CD\u8BB0`
1232
1322
  });
1233
1323
  continue;
1234
1324
  }
@@ -1242,12 +1332,20 @@ function auditAiChecks(aiChecks, record) {
1242
1332
  id: check.id,
1243
1333
  item: check.item,
1244
1334
  status: "fail",
1245
- detail: `item \u8FC7\u77ED\uFF08${check.item.length} < ${MIN_AI_CHECK_ITEM_LENGTH} \u5B57\u7B26\uFF0C\u6700\u5C0F\u5F62\u6001\u8981\u6C42\u53EF\u6838\u5BF9\u7684\u5177\u4F53\u4E8B\u5B9E\uFF09`
1335
+ // T202610130004 C3:过短分支同构补 item 原文与操作指引(同模糊语分支——无留痕时 patch 无目标,指引指向 add 带 refId)
1336
+ detail: `item\u300C${check.item}\u300D\u8FC7\u77ED\uFF08${check.item.length} < ${MIN_AI_CHECK_ITEM_LENGTH} \u5B57\u7B26\uFF0C\u6700\u5C0F\u5F62\u6001\u8981\u6C42\u53EF\u6838\u5BF9\u7684\u5177\u4F53\u4E8B\u5B9E\uFF09\u3002\u4FEE\u590D\uFF1Arecord-check-add \u5E26 refId=${check.id}\uFF08CLI --ref-id\uFF09+ \u5177\u4F53\u4E8B\u5B9E + passed=true`
1246
1337
  });
1247
1338
  continue;
1248
1339
  }
1249
1340
  if (VAGUE_AI_CHECK_BLACKLIST.some((w) => lowered.includes(w))) {
1250
- results.push({ id: check.id, item: check.item, status: "fail", detail: `item \u4E3A\u6A21\u7CCA\u5B8C\u6210\u8BED\uFF08\u547D\u4E2D\u9ED1\u540D\u5355\u300C${VAGUE_AI_CHECK_BLACKLIST.join("/")}\u300D\uFF09\uFF0C\u987B\u6539\u5199\u4E3A\u53EF\u6838\u5BF9\u7684\u5177\u4F53\u4E8B\u5B9E` });
1341
+ results.push({
1342
+ id: check.id,
1343
+ item: check.item,
1344
+ status: "fail",
1345
+ // T202610130004 C3(I202610120001):模糊语分支仅在无 refId 留痕时触发,record-check-patch 无可修补目标(按 entry id 寻址)——
1346
+ // 指引指向 add 带 refId 重记;补 item 原文让被打回方定位是哪条自检项被判模糊
1347
+ detail: `item\u300C${check.item}\u300D\u4E3A\u6A21\u7CCA\u5B8C\u6210\u8BED\uFF08\u547D\u4E2D\u9ED1\u540D\u5355\u300C${VAGUE_AI_CHECK_BLACKLIST.join("/")}\u300D\uFF09\u3002\u4FEE\u590D\uFF1A\u6539\u5199\u4E3A\u53EF\u6838\u5BF9\u7684\u5177\u4F53\u4E8B\u5B9E\u540E record-check-add \u5E26 refId=${check.id}\uFF08CLI --ref-id\uFF09+ passed=true \u91CD\u8BB0`
1348
+ });
1251
1349
  continue;
1252
1350
  }
1253
1351
  results.push({
@@ -1353,7 +1451,7 @@ function scanWorkspaceRepos(cwd, scanDepthOverride) {
1353
1451
  var execFileAsync = promisify(execFile);
1354
1452
  var defaultGitRunner = async (args, cwd) => {
1355
1453
  try {
1356
- const { stdout, stderr } = await execFileAsync("git", args, { cwd, timeout: 1e4 });
1454
+ const { stdout, stderr } = await execFileAsync("git", ["-c", "core.quotepath=false", ...args], { cwd, timeout: 1e4 });
1357
1455
  return { ok: true, stdout, stderr };
1358
1456
  } catch (err2) {
1359
1457
  const e = err2;
@@ -2195,13 +2293,13 @@ function writeWorkspaceLayer(token, kind, opts) {
2195
2293
  return ok2({ path: devJsonPath, kind, warnedDirectStart: !hadIsolationKeys, overwrote: hadToken });
2196
2294
  }
2197
2295
  function readStdinLine(stream) {
2198
- return new Promise((resolve3) => {
2296
+ return new Promise((resolve4) => {
2199
2297
  let data = "";
2200
2298
  const finish = (value) => {
2201
2299
  stream.removeAllListeners("readable");
2202
2300
  stream.removeAllListeners("end");
2203
2301
  stream.destroy?.();
2204
- resolve3(value);
2302
+ resolve4(value);
2205
2303
  };
2206
2304
  stream.setEncoding("utf8");
2207
2305
  stream.on("end", () => finish(data));
@@ -2680,7 +2778,7 @@ function createMcpServer() {
2680
2778
  const client = createClient(void 0, "mcp");
2681
2779
  server.tool(
2682
2780
  "siming_task",
2683
- 'task \u8D44\u6E90\u57DF\u805A\u5408\u5DE5\u5177\uFF08\u4EFB\u52A1\u5168\u64CD\u4F5C\u7ECF\u672C\u5DE5\u5177 + action \u53C2\u6570\uFF09\u3002action \u5FC5\u586B\uFF1B\u8C03\u7528\u65B9\u5E94\u5728 fieldMask \u4E2D\u5217\u51FA\u672C\u6B21\u771F\u5B9E\u63D0\u4F9B\u7684\u53EF\u9009\u5B57\u6BB5\uFF0Cmask \u5916\u7531\u5BBF\u4E3B\u81EA\u52A8\u586B\u5145\u7684\u7A7A\u4E32\u3001\u7A7A\u6570\u7EC4\u3001false\u30010 \u5747\u5FFD\u7565\uFF0Cmask \u5185\u7A7A\u6570\u7EC4/false \u8868\u793A\u663E\u5F0F\u6E05\u7A7A\u6216\u5173\u95ED\uFF1B\u5404 action \u56FA\u5B9A\u5FC5\u586B\u5B57\u6BB5\u65E0\u9700\u5199\u5165 mask\u3002\u5176\u4F59\u53C2\u6570\u5168\u53EF\u9009\u3001\u6309 action \u8FD0\u884C\u65F6\u6821\u9A8C\u5FC5\u586B\uFF08\u7F3A\u5931\u8FD4\u56DE\u5931\u8D25\u4E09\u8981\u7D20\uFF1A\u5BF9\u8C61+\u6839\u56E0+\u4FEE\u590D\u6307\u5F15\uFF09\u3002projectId \u4E3A key|id \u53CC\u5F62\u6001\uFF0C\u901A\u9053\u5C42\u81EA\u52A8\u5F52\u4E00\u4E3A 24-hex id\uFF1B\u663E\u5F0F\u6307\u5B9A\u89E3\u6790\u5931\u8D25\u62A5\u9519\u4E0D\u515C\u5E95\uFF1B\u7F3A\u7701\u8BED\u4E49\u6309 action\uFF1Acreate \u843D\u9ED8\u8BA4\u9879\u76EE\u94FE\uFF08\u5DE5\u4F5C\u533A defaultProject \u5931\u6548\u62A5\u9519\uFF0C\u65E0\u914D\u7F6E\u4E0D\u4F20\u7531 server \u6309\u6A21\u677F\u5F52\u5C5E\u515C\u5E95\uFF09\uFF0Cquery \u4E0D\u8FC7\u6EE4\uFF08\u5168\u5C40\u67E5\u8BE2\uFF09\u3002\u5199\u64CD\u4F5C\uFF08create/doc-*/record-*/archnote-add/pause/resume/cancel\uFF09\u54CD\u5E94\u4E3A\u8F7B\u91CF\u56DE\u6267\uFF1A{taskId, updated(\u89E6\u53CA\u5B57\u6BB5\u8DEF\u5F84), entry(\u65B0\u6761\u76EE id+kind\uFF0C\u4F9B\u540E\u7EED patch \u5B9A\u4F4D), echo(\u5199\u5165\u5185\u5BB9\u56DE\u663E), updatedAt, task(\u4EFB\u52A1\u8FDB\u5EA6\u6458\u8981 progress: {completed,total})}\u2014\u2014\u4E0D\u8FD4\u56DE\u5B8C\u6574\u4EFB\u52A1\u5B9E\u4F53\u3002action \u6E05\u5355\uFF08\u62EC\u53F7\u5185\u4E3A\u8BE5 action \u5FC5\u586B\u53C2\u6570\uFF09\uFF1Acreate(templateId,taskTitle,track\uFF1Btrack \u652F\u6301\u7EC4\u5408\u8F68\u9053\u2014\u2014\u6570\u7EC4\u5F62\u6001\uFF08\u5982 ["backend","godot"]\uFF0C\u6210\u5458=\u6CE8\u518C\u8868 active \u5355\u8F68\uFF09\u6216\u5355\u503C string\uFF1Ball \u4E3A\u4EFB\u52A1\u7EA7\u901A\u914D\uFF08\u6FC0\u6D3B\u521B\u5EFA\u65F6\u70B9\u5168\u90E8\u542F\u7528\u5355\u8F68\uFF0C\u5355\u503C\u4F7F\u7528\uFF09\uFF1B\u53EF\u9009 type=feature|bugfix|ui-tweak|research\u3001skipNodes\u3001projectId\uFF1BtemplateId \u652F\u6301 code|id \u53CC\u5F62\u6001\u2014\u2014code \u4E3A\u6A21\u677F\u4E1A\u52A1\u6807\u8BC6\uFF08\u9879\u76EE\u5185\u552F\u4E00\u3001kebab-case \u5FC5\u542B\u8FDE\u5B57\u7B26\uFF0C\u5982 full-workflow\uFF09\uFF0C\u63A8\u8350\u5F15\u7528\u5F62\u6001\uFF0C\u5BFB\u5740\u987B\u914D projectId \u6307\u5B9A\u9879\u76EE\u57DF\uFF1B24-hex \u6570\u636E\u5E93 id \u76F4\u67E5\u517C\u5BB9\uFF1Bcode \u5F62\u6001\u4E0D\u5B58\u5728\u65F6\u9519\u8BEF\u542B candidates \u76F8\u8FD1\u5019\u9009\u6E05\u5355\uFF08id/code/name \u22645 \u6761\uFF09\uFF1B\u54CD\u5E94 {task, firstNode}\u2014\u2014firstNode \u5373\u9996\u8282\u70B9\u5B8C\u6574\u8D44\u6599\u5305\uFF0C\u514D\u8C03 context)\uFF1Bquery(taskId \u8FD4\u56DE\u5355\u4EFB\u52A1\u8BE6\u60C5\uFF0C\u7F3A\u7701\u8FD4\u56DE\u5217\u8868\uFF0C\u53EF\u9009 status/track/projectId/page/limit)\uFF1Bcontext(taskId\uFF1B\u53EF\u9009 view=basic|full\u2014\u2014basic \u7F3A\u7701\u4E0D\u643A\u5E26 artifact \u5168\u6587\u5FEB\u7167\uFF0Cfull \u65AD\u70B9\u7EED\u8DD1\u9700\u5386\u53F2\u4EA7\u7269\u5168\u6587\u65F6\u7528\u3002\u26A0 \u514D\u91CD\u8BFB\uFF1Aadvance/approve \u6D41\u8F6C\u54CD\u5E94\u5DF2\u542B\u4E0B\u4E00\u8282\u70B9\u5B8C\u6574\u8D44\u6599\u5305\uFF0C\u540C\u4F1A\u8BDD\u5185\u6D41\u8F6C\u540E\u76F4\u63A5\u5F00\u5DE5\u3001\u65E0\u9700\u518D\u8C03 context\uFF1B\u4EC5\u65B0\u4F1A\u8BDD/\u65AD\u70B9\u7EED\u8DD1\u8C03\u7528)\uFF1Bnode(taskId,view=prompt|skills\uFF1BnodeId \u7F3A\u7701\u7528\u5F53\u524D\u8282\u70B9\uFF1B\u26A0 view \u503C\u968F action \u4E0D\u540C\uFF1Anode\u2192prompt|skills\uFF0Ccontext\u2192basic|full\uFF0C\u9519\u914D\u62A5\u9519)\uFF1Badvance(taskId\uFF1Bnote \u5199 history\u3001summary \u5199 nodeRecords\uFF0C\u4E24\u4F4D\u72EC\u7ACB\uFF1B\u54CD\u5E94\u5DF2\u542B\u4E0B\u4E00\u8282\u70B9\u8D44\u6599\u5305\uFF0C\u540C\u4F1A\u8BDD\u514D\u91CD\u8BFB context\u3002\u26A0 T202609230001 \u8282\u70B9\u95E8\u7981\uFF1A\u914D\u7F6E\u4E86 toolChecks/aiChecks \u7684\u8282\u70B9\u63A8\u8FDB\u524D\u81EA\u52A8\u6267\u884C\u68C0\u67E5\u2014\u2014fail \u4F1A\u88AB\u6253\u56DE\u5E76\u8FD4\u56DE\u9010\u9879\u4FEE\u590D\u6307\u5F15\uFF08\u7ED3\u679C\u5DF2\u5199 history checks_rejected\uFF09\uFF0C\u6309\u6307\u5F15\u4FEE\u590D\u6216\u83B7\u7528\u6237\u8C41\u514D\u7559\u75D5\u540E\u91CD\u63A8\uFF1B\u975E\u4EBA\u5DE5\u5BA1\u6279\u8282\u70B9\u7981\u6B62\u56E0\u95E8\u7981\u4E2D\u65AD\u4EFB\u52A1\uFF08\u6253\u56DE\u4FE1\u606F\u5373\u7EE7\u7EED\u4F9D\u636E\uFF09\u3002force+forceReason \u6210\u5BF9\u63D0\u4F9B\u65F6\u53EF\u8D8A\u8FC7 fail \u5F3A\u5236\u63A8\u8FDB\u2014\u2014\u4EC5\u51ED\u7528\u6237\u660E\u793A\u6307\u4EE4\uFF0CAI \u7981\u6B62\u81EA\u51B3\uFF0C\u7406\u7531\u539F\u6587\u843D history \u5BA1\u8BA1)\uFF1Bcheck(taskId\uFF1B\u95E8\u7981\u53EA\u8BFB\u9884\u68C0\u2014\u2014\u9010\u9879\u62A5\u544A\u4E0D\u5199\u4EFB\u4F55\u72B6\u6001\uFF0Cadvance \u524D\u81EA\u67E5\u7528)\uFF1Bapprove(taskId,decision=approved|rejected\uFF1B\u53EF\u9009 comment\uFF1B\u54CD\u5E94\u5DF2\u542B\u4E0B\u4E00\u8282\u70B9\u8D44\u6599\u5305\uFF0C\u540C\u4F1A\u8BDD\u514D\u91CD\u8BFB context)\uFF1Bpause(taskId\uFF1B\u53EF\u9009 reason)\uFF1Bresume(taskId\uFF1B\u53EF\u9009 resumeNote)\uFF1Bcancel(taskId\uFF1B\u53EF\u9009 reason 1-500 \u5B57\u7B26\uFF1B\u26A0 \u7EC8\u6001\u4E0D\u53EF\u9006\u2014\u2014active/paused \u53EF\u53D6\u6D88\uFF0Ccompleted/cancelled \u62D2\u7EDD\u5E76\u8FD4\u56DE TASK_TERMINATED\uFF1B\u53D6\u6D88\u540E\u7ECF\u672C\u5DE5\u5177\u7684\u6D41\u8F6C\u4E0E\u8BB0\u5F55\u5199\u64CD\u4F5C\u88AB\u62D2)\uFF1Bhistory(taskId)\uFF1Binstance(taskId\u2014\u2014\u67E5\u770B\u4EFB\u52A1 DAG \u5B9E\u4F8B\u5FEB\u7167\uFF1A\u521B\u5EFA\u4EFB\u52A1\u65F6\u51BB\u7ED3\u7684\u6A21\u677F\u5B9E\u4F8B\uFF0C\u6A21\u677F\u540E\u7EED\u53D8\u66F4\u4E0D\u56DE\u6EAF)\u3002\u8BB0\u5F55\u5199\u5165\uFF1Arecord-set(taskId,node\uFF1Bsummary \u4E0E\u516D\u4E2A\u7ED3\u6784\u5316\u5B57\u6BB5\uFF08baseBranch/verifiedCommit/fixesApplied/testReport/deferredIssues/evidenceFiles\uFF09\u81F3\u5C11\u5176\u4E00\u2014\u2014\u7ED3\u6784\u5316\u5B57\u6BB5\u6309\u8282\u70B9 prompt\u300C\u7ED3\u6784\u5316\u5B57\u6BB5\u5199\u5165\u8981\u6C42\u300D\u9010\u5B57\u6BB5\u843D\u5E93\uFF0CPATCH \u8BED\u4E49=\u4EC5\u66F4\u65B0\u63D0\u4F9B\u5B57\u6BB5)\uFF1Brecord-check-add(taskId,node,item\uFF1B\u53EF\u9009 passed \u7F3A\u7701 true\u3001refId=aiChecks \u81EA\u68C0\u7559\u75D5\u951A\uFF08\u5BA1\u8BA1\u8BA4 refId===aiCheck.id \u4E14 passed=true\uFF0C\u7F3A\u7559\u75D5\u7684\u5B8C\u6210\u5224\u5B9A\u5BA1\u8BA1 fail\uFF09\u3001source=self|tool \u672C\u7248\u5DE5\u5177\u7F3A\u7701\u4E0D\u5199\uFF1B\u6279\u91CF checkItems \u5143\u7D20\u540C\u6784\uFF1B\u56DE\u6267 entry.id \u4F9B record-check-patch \u5B9A\u4F4D)\uFF1Brecord-check-patch(taskId,node,checkId\uFF1B\u53EF\u9009 passed/item/refId/source\uFF0C\u4EC5\u66F4\u65B0\u63D0\u4F9B\u5B57\u6BB5)\uFF1Brecord-artifact-add(taskId,node,artifactType=prd|tech|code|test|doc|other\uFF1Bpath \u7EAF\u5F15\u7528 / file \u8BFB\u672C\u673A\u6587\u4EF6 / content \u76F4\u4F20\u5168\u6587\u2014\u2014file \u4E0E content\u3001file \u4E0E path \u4E92\u65A5\uFF0Ccontent \u53EF\u4E0E path \u6210\u5BF9\u6216\u5355\u72EC\uFF0C\u5355\u72EC\u65F6\u767B\u8BB0\u503C\u5408\u6210 mcp-inline/<type>\uFF1B\u53EF\u9009 note)\uFF1Brecord-confirm(taskId,node,quote\u2014\u2014\u6682\u505C\u70B9\u7528\u6237\u786E\u8BA4\u539F\u6587\u9010\u5B57)\uFF1Brecord-decision-add(taskId,node,topic,decisionText)\uFF1Brecord-review-set(taskId,node,verdict=pass|fail,rounds,critical)\u3002\u9700\u6C42\u6587\u6863\uFF1Adoc-set(taskId\uFF1Bwhat/why/trackNote \u81F3\u5C11\u4F20\u4E00)\uFF1Bdoc-acceptance-add(taskId,text)\uFF1Bdoc-non-goal-add(taskId,text)\u3002\u7D20\u6750\uFF1Aarchnote-add(taskId,text)\u3002\u6279\u91CF\u5F62\u6001\uFF08\u63A8\u8350\uFF0C\u516D\u7C7B\u540C\u6784\uFF09\uFF1Arecord-check-add \u4F20 checkItems=[{item,passed?}]\u3001record-artifact-add \u4F20 artifactEntries=[{artifactType,path?/file?/content?,note?}]\u3001record-decision-add \u4F20 decisionEntries=[{topic,decisionText}]\u3001doc-acceptance-add/doc-non-goal-add/archnote-add \u4F20 textItems=[string]\u2014\u2014\u6279\u91CF\u53C2\u6570\u4E0E\u5355\u6761\u53C2\u6570\u4E8C\u9009\u4E00\uFF0C\u4E00\u6B21\u8C03\u7528\u5199\u591A\u6761\uFF08\u4E0A\u9650 50 \u6761/\u6279\uFF0Cartifact \u5168\u6587\u5FEB\u7167\u603B\u91CF \u22642M \u5B57\u7B26/\u6279\uFF09\uFF1B\u90E8\u5206\u5931\u8D25\u4FDD\u7559\u6210\u529F\u6761\u76EE\u5E76\u9010\u6761\u62A5\u544A\uFF08failures[].index=\u539F\u59CB\u63D0\u4EA4\u5E8F\uFF0C\u53EA\u91CD\u8BD5\u5931\u8D25\u9879\uFF09\uFF0Capplied=0 \u6574\u4F53\u62A5\u5931\u8D25\u3002',
2781
+ 'task \u8D44\u6E90\u57DF\u805A\u5408\u5DE5\u5177\uFF08\u4EFB\u52A1\u5168\u64CD\u4F5C\u7ECF\u672C\u5DE5\u5177 + action \u53C2\u6570\uFF09\u3002action \u5FC5\u586B\uFF1B\u8C03\u7528\u65B9\u5E94\u5728 fieldMask \u4E2D\u5217\u51FA\u672C\u6B21\u771F\u5B9E\u63D0\u4F9B\u7684\u53EF\u9009\u5B57\u6BB5\uFF0Cmask \u5916\u7531\u5BBF\u4E3B\u81EA\u52A8\u586B\u5145\u7684\u7A7A\u4E32\u3001\u7A7A\u6570\u7EC4\u3001false\u30010 \u5747\u5FFD\u7565\uFF0Cmask \u5185\u7A7A\u6570\u7EC4/false \u8868\u793A\u663E\u5F0F\u6E05\u7A7A\u6216\u5173\u95ED\uFF1B\u5404 action \u56FA\u5B9A\u5FC5\u586B\u5B57\u6BB5\u65E0\u9700\u5199\u5165 mask\u3002\u5176\u4F59\u53C2\u6570\u5168\u53EF\u9009\u3001\u6309 action \u8FD0\u884C\u65F6\u6821\u9A8C\u5FC5\u586B\uFF08\u7F3A\u5931\u8FD4\u56DE\u5931\u8D25\u4E09\u8981\u7D20\uFF1A\u5BF9\u8C61+\u6839\u56E0+\u4FEE\u590D\u6307\u5F15\uFF09\u3002projectId \u4E3A key|id \u53CC\u5F62\u6001\uFF0C\u901A\u9053\u5C42\u81EA\u52A8\u5F52\u4E00\u4E3A 24-hex id\uFF1B\u663E\u5F0F\u6307\u5B9A\u89E3\u6790\u5931\u8D25\u62A5\u9519\u4E0D\u515C\u5E95\uFF1B\u7F3A\u7701\u8BED\u4E49\u6309 action\uFF1Acreate \u843D\u9ED8\u8BA4\u9879\u76EE\u94FE\uFF08\u5DE5\u4F5C\u533A defaultProject \u5931\u6548\u62A5\u9519\uFF0C\u65E0\u914D\u7F6E\u4E0D\u4F20\u7531 server \u6309\u6A21\u677F\u5F52\u5C5E\u515C\u5E95\uFF09\uFF0Cquery \u4E0D\u8FC7\u6EE4\uFF08\u5168\u5C40\u67E5\u8BE2\uFF09\u3002\u5199\u64CD\u4F5C\uFF08create/doc-*/record-*/archnote-add/pause/resume/cancel\uFF09\u54CD\u5E94\u4E3A\u8F7B\u91CF\u56DE\u6267\uFF1A{taskId, updated(\u89E6\u53CA\u5B57\u6BB5\u8DEF\u5F84), entry(\u65B0\u6761\u76EE id+kind\uFF0C\u4F9B\u540E\u7EED patch \u5B9A\u4F4D), echo(\u5199\u5165\u5185\u5BB9\u56DE\u663E), updatedAt, task(\u4EFB\u52A1\u8FDB\u5EA6\u6458\u8981 progress: {completed,total})}\u2014\u2014\u4E0D\u8FD4\u56DE\u5B8C\u6574\u4EFB\u52A1\u5B9E\u4F53\u3002action \u6E05\u5355\uFF08\u62EC\u53F7\u5185\u4E3A\u8BE5 action \u5FC5\u586B\u53C2\u6570\uFF09\uFF1Acreate(templateId,taskTitle,track\uFF1Btrack \u652F\u6301\u7EC4\u5408\u8F68\u9053\u2014\u2014\u6570\u7EC4\u5F62\u6001\uFF08\u5982 ["backend","godot"]\uFF0C\u6210\u5458=\u6CE8\u518C\u8868 active \u5355\u8F68\uFF09\u6216\u5355\u503C string\uFF1Ball \u4E3A\u4EFB\u52A1\u7EA7\u901A\u914D\uFF08\u6FC0\u6D3B\u521B\u5EFA\u65F6\u70B9\u5168\u90E8\u542F\u7528\u5355\u8F68\uFF0C\u5355\u503C\u4F7F\u7528\uFF09\uFF1B\u53EF\u9009 type=feature|bugfix|ui-tweak|research\u3001skipNodes\u3001projectId\uFF1BtemplateId \u652F\u6301 code|id \u53CC\u5F62\u6001\u2014\u2014code \u4E3A\u6A21\u677F\u4E1A\u52A1\u6807\u8BC6\uFF08\u9879\u76EE\u5185\u552F\u4E00\u3001kebab-case \u5FC5\u542B\u8FDE\u5B57\u7B26\uFF0C\u5982 full-workflow\uFF09\uFF0C\u63A8\u8350\u5F15\u7528\u5F62\u6001\uFF0C\u5BFB\u5740\u987B\u914D projectId \u6307\u5B9A\u9879\u76EE\u57DF\uFF1B24-hex \u6570\u636E\u5E93 id \u76F4\u67E5\u517C\u5BB9\uFF1Bcode \u5F62\u6001\u4E0D\u5B58\u5728\u65F6\u9519\u8BEF\u542B candidates \u76F8\u8FD1\u5019\u9009\u6E05\u5355\uFF08id/code/name \u22645 \u6761\uFF09\uFF1B\u54CD\u5E94 {task, firstNode}\u2014\u2014firstNode \u5373\u9996\u8282\u70B9\u5B8C\u6574\u8D44\u6599\u5305\uFF0C\u514D\u8C03 context)\uFF1Bquery(taskId \u8FD4\u56DE\u5355\u4EFB\u52A1\u8BE6\u60C5\uFF0C\u7F3A\u7701\u8FD4\u56DE\u5217\u8868\uFF0C\u53EF\u9009 status/track/projectId/page/limit)\uFF1Bcontext(taskId\uFF1B\u53EF\u9009 view=basic|full\u2014\u2014basic \u7F3A\u7701\u4E0D\u643A\u5E26 artifact \u5168\u6587\u5FEB\u7167\uFF0Cfull \u65AD\u70B9\u7EED\u8DD1\u9700\u5386\u53F2\u4EA7\u7269\u5168\u6587\u65F6\u7528\u3002\u26A0 \u514D\u91CD\u8BFB\uFF1Aadvance/approve \u6D41\u8F6C\u54CD\u5E94\u5DF2\u542B\u4E0B\u4E00\u8282\u70B9\u5B8C\u6574\u8D44\u6599\u5305\uFF0C\u540C\u4F1A\u8BDD\u5185\u6D41\u8F6C\u540E\u76F4\u63A5\u5F00\u5DE5\u3001\u65E0\u9700\u518D\u8C03 context\uFF1B\u4EC5\u65B0\u4F1A\u8BDD/\u65AD\u70B9\u7EED\u8DD1\u8C03\u7528)\uFF1Bnode(taskId,view=prompt|skills\uFF1BnodeId \u7F3A\u7701\u7528\u5F53\u524D\u8282\u70B9\uFF1B\u26A0 view \u503C\u968F action \u4E0D\u540C\uFF1Anode\u2192prompt|skills\uFF0Ccontext\u2192basic|full\uFF0C\u9519\u914D\u62A5\u9519)\uFF1Badvance(taskId\uFF1Bnote \u5199 history\u3001summary \u5199 nodeRecords\uFF0C\u4E24\u4F4D\u72EC\u7ACB\uFF1B\u26A0 T202610130004 \u7EC8\u6001\u6536\u5C3E\uFF1A\u4EFB\u52A1\u5904\u4E8E\u7EC8\u8282\u70B9\u65F6 advance \u5FC5\u5E26 finalize=true \u4F5C\u6536\u5C3E\u786E\u8BA4\uFF08\u7F3A\u7701\u88AB\u62D2\u5E76\u8FD4\u56DE\u63D0\u793A\uFF09\uFF0C\u4E2D\u95F4\u8282\u70B9\u5FFD\u7565\u8BE5\u53C2\u6570\uFF1B\u54CD\u5E94\u5DF2\u542B\u4E0B\u4E00\u8282\u70B9\u8D44\u6599\u5305\uFF0C\u540C\u4F1A\u8BDD\u514D\u91CD\u8BFB context\u3002\u26A0 T202609230001 \u8282\u70B9\u95E8\u7981\uFF1A\u914D\u7F6E\u4E86 toolChecks/aiChecks \u7684\u8282\u70B9\u63A8\u8FDB\u524D\u81EA\u52A8\u6267\u884C\u68C0\u67E5\u2014\u2014fail \u4F1A\u88AB\u6253\u56DE\u5E76\u8FD4\u56DE\u9010\u9879\u4FEE\u590D\u6307\u5F15\uFF08\u7ED3\u679C\u5DF2\u5199 history checks_rejected\uFF09\uFF0C\u6309\u6307\u5F15\u4FEE\u590D\u6216\u83B7\u7528\u6237\u8C41\u514D\u7559\u75D5\u540E\u91CD\u63A8\uFF1B\u975E\u4EBA\u5DE5\u5BA1\u6279\u8282\u70B9\u7981\u6B62\u56E0\u95E8\u7981\u4E2D\u65AD\u4EFB\u52A1\uFF08\u6253\u56DE\u4FE1\u606F\u5373\u7EE7\u7EED\u4F9D\u636E\uFF09\u3002force+forceReason \u6210\u5BF9\u63D0\u4F9B\u65F6\u53EF\u8D8A\u8FC7 fail \u5F3A\u5236\u63A8\u8FDB\u2014\u2014\u4EC5\u51ED\u7528\u6237\u660E\u793A\u6307\u4EE4\uFF0CAI \u7981\u6B62\u81EA\u51B3\uFF0C\u7406\u7531\u539F\u6587\u843D history \u5BA1\u8BA1)\uFF1Bcheck(taskId\uFF1B\u95E8\u7981\u53EA\u8BFB\u9884\u68C0\u2014\u2014\u9010\u9879\u62A5\u544A\u4E0D\u5199\u4EFB\u4F55\u72B6\u6001\uFF0Cadvance \u524D\u81EA\u67E5\u7528)\uFF1Bapprove(taskId,decision=approved|rejected\uFF1B\u53EF\u9009 comment\uFF1B\u54CD\u5E94\u5DF2\u542B\u4E0B\u4E00\u8282\u70B9\u8D44\u6599\u5305\uFF0C\u540C\u4F1A\u8BDD\u514D\u91CD\u8BFB context)\uFF1Bpause(taskId\uFF1B\u53EF\u9009 reason)\uFF1Bresume(taskId\uFF1B\u53EF\u9009 resumeNote)\uFF1Bcancel(taskId\uFF1B\u53EF\u9009 reason 1-500 \u5B57\u7B26\uFF1B\u26A0 \u7EC8\u6001\u4E0D\u53EF\u9006\u2014\u2014active/paused \u53EF\u53D6\u6D88\uFF0Ccompleted/cancelled \u62D2\u7EDD\u5E76\u8FD4\u56DE TASK_TERMINATED\uFF1B\u53D6\u6D88\u540E\u7ECF\u672C\u5DE5\u5177\u7684\u6D41\u8F6C\u4E0E\u8BB0\u5F55\u5199\u64CD\u4F5C\u88AB\u62D2)\uFF1Bhistory(taskId)\uFF1Binstance(taskId\u2014\u2014\u67E5\u770B\u4EFB\u52A1 DAG \u5B9E\u4F8B\u5FEB\u7167\uFF1A\u521B\u5EFA\u4EFB\u52A1\u65F6\u51BB\u7ED3\u7684\u6A21\u677F\u5B9E\u4F8B\uFF0C\u6A21\u677F\u540E\u7EED\u53D8\u66F4\u4E0D\u56DE\u6EAF)\u3002\u8BB0\u5F55\u5199\u5165\uFF1Arecord-set(taskId,node\uFF1Bsummary \u4E0E\u516D\u4E2A\u7ED3\u6784\u5316\u5B57\u6BB5\uFF08baseBranch/verifiedCommit/fixesApplied/testReport/deferredIssues/evidenceFiles\uFF09\u81F3\u5C11\u5176\u4E00\u2014\u2014\u7ED3\u6784\u5316\u5B57\u6BB5\u6309\u8282\u70B9 prompt\u300C\u7ED3\u6784\u5316\u5B57\u6BB5\u5199\u5165\u8981\u6C42\u300D\u9010\u5B57\u6BB5\u843D\u5E93\uFF0CPATCH \u8BED\u4E49=\u4EC5\u66F4\u65B0\u63D0\u4F9B\u5B57\u6BB5)\uFF1Brecord-check-add(taskId,node,item\uFF1B\u53EF\u9009 passed \u7F3A\u7701 true\u3001refId=aiChecks \u81EA\u68C0\u7559\u75D5\u951A\uFF08\u5BA1\u8BA1\u8BA4 refId===aiCheck.id \u4E14 passed=true\uFF0C\u7F3A\u7559\u75D5\u7684\u5B8C\u6210\u5224\u5B9A\u5BA1\u8BA1 fail\uFF09\u3001source=self|tool \u672C\u7248\u5DE5\u5177\u7F3A\u7701\u4E0D\u5199\uFF1B\u6279\u91CF checkItems \u5143\u7D20\u540C\u6784\uFF1B\u56DE\u6267 entry.id \u4F9B record-check-patch \u5B9A\u4F4D)\uFF1Brecord-check-patch(taskId,node,checkId\uFF1B\u53EF\u9009 passed/item/refId/source\uFF0C\u4EC5\u66F4\u65B0\u63D0\u4F9B\u5B57\u6BB5)\uFF1Brecord-artifact-add(taskId,node,artifactType=prd|tech|code|test|doc|other\uFF1Bpath \u7EAF\u5F15\u7528 / file \u8BFB\u672C\u673A\u6587\u4EF6 / content \u76F4\u4F20\u5168\u6587\u2014\u2014file \u4E0E content\u3001file \u4E0E path \u4E92\u65A5\uFF0Ccontent \u53EF\u4E0E path \u6210\u5BF9\u6216\u5355\u72EC\uFF0C\u5355\u72EC\u65F6\u767B\u8BB0\u503C\u5408\u6210 mcp-inline/<type>\uFF1B\u53EF\u9009 note)\uFF1Brecord-confirm(taskId,node,quote\u2014\u2014\u6682\u505C\u70B9\u7528\u6237\u786E\u8BA4\u539F\u6587\u9010\u5B57)\uFF1Brecord-decision-add(taskId,node,topic,decisionText)\uFF1Brecord-review-set(taskId,node,verdict=pass|fail,rounds,critical)\u3002\u9700\u6C42\u6587\u6863\uFF1Adoc-set(taskId\uFF1Bwhat/why/trackNote \u81F3\u5C11\u4F20\u4E00)\uFF1Bdoc-acceptance-add(taskId,text)\uFF1Bdoc-non-goal-add(taskId,text)\u3002\u7D20\u6750\uFF1Aarchnote-add(taskId,text)\u3002\u6279\u91CF\u5F62\u6001\uFF08\u63A8\u8350\uFF0C\u516D\u7C7B\u540C\u6784\uFF09\uFF1Arecord-check-add \u4F20 checkItems=[{item,passed?}]\u3001record-artifact-add \u4F20 artifactEntries=[{artifactType,path?/file?/content?,note?}]\u3001record-decision-add \u4F20 decisionEntries=[{topic,decisionText}]\u3001doc-acceptance-add/doc-non-goal-add/archnote-add \u4F20 textItems=[string]\u2014\u2014\u6279\u91CF\u53C2\u6570\u4E0E\u5355\u6761\u53C2\u6570\u4E8C\u9009\u4E00\uFF0C\u4E00\u6B21\u8C03\u7528\u5199\u591A\u6761\uFF08\u4E0A\u9650 50 \u6761/\u6279\uFF0Cartifact \u5168\u6587\u5FEB\u7167\u603B\u91CF \u22642M \u5B57\u7B26/\u6279\uFF09\uFF1B\u90E8\u5206\u5931\u8D25\u4FDD\u7559\u6210\u529F\u6761\u76EE\u5E76\u9010\u6761\u62A5\u544A\uFF08failures[].index=\u539F\u59CB\u63D0\u4EA4\u5E8F\uFF0C\u53EA\u91CD\u8BD5\u5931\u8D25\u9879\uFF09\uFF0Capplied=0 \u6574\u4F53\u62A5\u5931\u8D25\u3002',
2684
2782
  taskArgs,
2685
2783
  async (rawArgs) => {
2686
2784
  const masked = applyFieldMask(rawArgs, TASK_FIELD_SPECS);
@@ -3163,7 +3261,16 @@ function createMcpServer() {
3163
3261
  }
3164
3262
 
3165
3263
  // src/commands/mcp-serve.ts
3264
+ var SOURCE_LABEL2 = {
3265
+ flag: "CLI \u53C2\u6570 --url",
3266
+ env: "\u73AF\u5883\u53D8\u91CF SIMING_SERVER_URL",
3267
+ workspace: "\u5DE5\u4F5C\u533A .siming/config.json",
3268
+ global: "\u5168\u5C40 ~/.siming/config.json",
3269
+ default: "\u9ED8\u8BA4\u63A8\u5BFC"
3270
+ };
3166
3271
  async function mcpServeCommand() {
3272
+ const resolved = resolveServerUrlDetailed();
3273
+ console.error(`[siming] mcp-serve \u5DF2\u542F\u52A8\uFF1Aserver=${resolved.url}\uFF08\u6765\u6E90: ${SOURCE_LABEL2[resolved.source]}\uFF09\uFF1BserverUrl \u6BCF\u8BF7\u6C42\u73B0\u89E3\u6790\uFF0C\u914D\u7F6E\u53D8\u66F4\u65E0\u9700\u91CD\u542F`);
3167
3274
  const server = createMcpServer();
3168
3275
  const transport = new StdioServerTransport();
3169
3276
  await server.connect(transport);
@@ -9296,13 +9403,13 @@ function createSettingsCommand() {
9296
9403
  return settings;
9297
9404
  }
9298
9405
  function readStdin() {
9299
- return new Promise((resolve3) => {
9406
+ return new Promise((resolve4) => {
9300
9407
  let data = "";
9301
9408
  process.stdin.setEncoding("utf8");
9302
9409
  process.stdin.on("data", (chunk) => {
9303
9410
  data += chunk;
9304
9411
  });
9305
- process.stdin.on("end", () => resolve3(data));
9412
+ process.stdin.on("end", () => resolve4(data));
9306
9413
  });
9307
9414
  }
9308
9415
 
@@ -9314,7 +9421,7 @@ import { VERSION as VERSION4, SIMING_CONFIG_KEYS as SIMING_CONFIG_KEYS2 } from "
9314
9421
  // src/commands/server/daemon.ts
9315
9422
  import { execFileSync } from "child_process";
9316
9423
  import { closeSync, existsSync as existsSync11, lstatSync, mkdirSync as mkdirSync6, openSync, readFileSync as readFileSync17, renameSync as renameSync5, rmSync as rmSync4, writeFileSync as writeFileSync6 } from "fs";
9317
- import { join as join11, resolve as resolve2 } from "path";
9424
+ import { join as join11, resolve as resolve3 } from "path";
9318
9425
  import { spawn } from "child_process";
9319
9426
 
9320
9427
  // src/commands/server/probe.ts
@@ -9523,7 +9630,7 @@ function readLogTail(lines = 30) {
9523
9630
  return all.slice(-lines);
9524
9631
  }
9525
9632
  function spawnDaemon(configEnv) {
9526
- const entry = resolve2(process.argv[1] ?? "");
9633
+ const entry = resolve3(process.argv[1] ?? "");
9527
9634
  if (entry.length === 0 || !existsSync11(entry)) {
9528
9635
  throw new Error(`\u65E0\u6CD5\u5B9A\u4F4D siming \u5165\u53E3\u811A\u672C\uFF08process.argv[1]=${process.argv[1] ?? "(\u7A7A)"}\uFF09`);
9529
9636
  }
@@ -9848,12 +9955,12 @@ async function logsAction(opts) {
9848
9955
  }
9849
9956
  if (tail.length > 0) console.log(tail.join("\n"));
9850
9957
  if (!opts.follow) return 0;
9851
- await new Promise((resolve3) => {
9958
+ await new Promise((resolve4) => {
9852
9959
  const child = spawn2("tail", ["-n", "0", "-f", logfilePath()], { stdio: "inherit" });
9853
- child.on("exit", () => resolve3());
9960
+ child.on("exit", () => resolve4());
9854
9961
  child.on("error", (e) => {
9855
9962
  err(`\u65E0\u6CD5\u542F\u52A8 tail: ${e.message}`);
9856
- resolve3();
9963
+ resolve4();
9857
9964
  });
9858
9965
  });
9859
9966
  return 0;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@siming-org/cli",
3
- "version": "0.8.5",
3
+ "version": "0.8.7",
4
4
  "license": "MIT",
5
5
  "author": "Wuchao",
6
6
  "engines": {
@@ -27,8 +27,8 @@
27
27
  "picocolors": "^1.1.1",
28
28
  "yaml": "^2.6.0",
29
29
  "zod": "^4.4.3",
30
- "@siming-org/core": "0.8.5",
31
- "@siming-org/server": "0.8.5"
30
+ "@siming-org/core": "0.8.7",
31
+ "@siming-org/server": "0.8.7"
32
32
  },
33
33
  "devDependencies": {
34
34
  "tsup": "^8.3.5",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "exportFormatVersion": "1.0",
3
- "exportedAt": "2026-10-12T09:03:35.947Z",
3
+ "exportedAt": "2026-10-14T08:44:31.828Z",
4
4
  "warnings": [],
5
5
  "template": {
6
6
  "name": "full_workflow",
@@ -25,7 +25,9 @@
25
25
  "agents": [
26
26
  "prd-reviewer"
27
27
  ]
28
- }
28
+ },
29
+ "toolChecks": [],
30
+ "aiChecks": []
29
31
  },
30
32
  {
31
33
  "id": "ALIGN",
@@ -73,11 +75,13 @@
73
75
  "sourcePreset": {
74
76
  "code": "interaction-design",
75
77
  "version": "1.2.0",
76
- "contentHash": "43263b2e99bd969a",
78
+ "contentHash": "fdefbe0ca7834e9b",
77
79
  "agents": [
78
80
  "alignment-reviewer"
79
81
  ]
80
- }
82
+ },
83
+ "toolChecks": [],
84
+ "aiChecks": []
81
85
  },
82
86
  {
83
87
  "id": "DESIGN",
@@ -95,8 +99,8 @@
95
99
  ],
96
100
  "sourcePreset": {
97
101
  "code": "design-tech",
98
- "version": "1.8.0",
99
- "contentHash": "c304f4b00638f9c0",
102
+ "version": "1.8.1",
103
+ "contentHash": "c1baf274770d39b2",
100
104
  "agents": [
101
105
  "alignment-reviewer",
102
106
  "arch-reviewer"
@@ -140,8 +144,8 @@
140
144
  ],
141
145
  "sourcePreset": {
142
146
  "code": "code-backend",
143
- "version": "1.5.0",
144
- "contentHash": "53cebe02565babcc",
147
+ "version": "1.5.1",
148
+ "contentHash": "e0d1fe3a22acf0a0",
145
149
  "agents": [
146
150
  "code-reviewer"
147
151
  ]
@@ -242,7 +246,9 @@
242
246
  "agents": [
243
247
  "test-reviewer"
244
248
  ]
245
- }
249
+ },
250
+ "toolChecks": [],
251
+ "aiChecks": []
246
252
  },
247
253
  {
248
254
  "id": "UT_DEV",
@@ -304,7 +310,9 @@
304
310
  "agents": [
305
311
  "test-reviewer"
306
312
  ]
307
- }
313
+ },
314
+ "toolChecks": [],
315
+ "aiChecks": []
308
316
  },
309
317
  {
310
318
  "id": "UI_UT_DEV",
@@ -471,15 +479,15 @@
471
479
  "label": "发布验收",
472
480
  "phase": "exit",
473
481
  "track": "all",
474
- "prompt": "# 发布验收\n\n## 节点职责\n\n核对最终集成验证证据与当前版本是否一致,向用户呈现可独立理解的验收摘要,并按本节点在流程中的位置承载确认与代码归档:存在后继审批边时,用户确认由流转边暂停点承载,本节点不重复确认;本节点为任务终点时,节点内确认是任务收尾唯一授权门。本节点不重复执行测试、代码评审或回归分析。\n\n## 依赖信息\n\n先检查当前任务上下文,读取需求、验收标准、非目标、已激活节点的完成记录、`test-integrate` 的测试范围与结果、日志以及已验证版本标识,并读取项目分支与归档策略。加载 `workflow-discipline`。\n\n缺少集成验证证据、版本标识或前序完成记录时,先从任务上下文补齐;证据仍不完整或无法对应当前版本时停止验收,不推断通过。\n\n## 执行流程\n\n1. 确认所有已激活的前序节点均已完成,不写死某一模板路径下的节点清单。\n2. 比较当前生产代码与 `test-integrate` 绑定的已验证版本。存在新的生产代码变化时,退回集成验证重新判断范围并回归;纯文档或记录变化说明为何不影响证据。\n3. 逐条核对验收标准对应的实现位置和前序验证证据,并确认非目标未被误实现、范围变化已说明、所有 reviewer 的阻塞问题已解决。\n4. 汇总集成测试范围与统计、未授权跳过、实现与验收标准对应关系、范围变化、已知问题和风险,形成简洁且不依赖任务内部术语的验收摘要。\n5. 判定本节点位置:读取任务上下文中的任务结构(DAG 节点集与各节点状态)——本节点是任务最后一个节点(如快速流程形态,已完成数加一等于节点总数)即为终态形态;存在后继节点(如架构归档节点)即为非终态形态。advance 响应的 nextNode 资料包可作交叉印证。两种形态的确认与归档时序不同,按后续步骤分支执行,判定结果写入节点记录。\n6. **终态形态(本节点为任务终点,无后继审批边)**:向用户呈现验收摘要并在节点内取得明确决定。只有明确的通过、确认归档或等价肯定表达构成确认;提问、条件句、部分认同、沉默或模糊表态不构成确认。确认后逐字记录确认原文,并按项目声明完成提交、合并、推送或其他代码归档动作。无项目策略时采用保留历史且可回退的常规方式,并记录采用的默认。\n7. **非终态形态(存在后继审批边)**:向用户呈现验收摘要(作为暂停点审批材料);推进前在节点记录写入显式「归档待办」条目(归档动作清单:目标分支、合并方式、推送范围);然后推进进入暂停点等待用户审批——审批通过即验收确认,确认原文以审批记录留痕(须逐字引用用户确认表态,不得转述失真),本节点不重复确认。审批通过后、开始下一节点工作前,先完成归档待办中的代码归档动作(提交/合并/推送),完成后回写归档结果闭环该待办;归档待办在归档完成前不得闭环。\n8. 归档过程中遇到冲突或失败时先检查实际版本控制状态并自主诊断;无法可靠解决时说明当前状态和卡点,不猜测操作结果。\n\n## 产出与检查\n\n推进前确认:\n\n- 所有已激活前序节点已完成;\n- 集成验证证据完整,所有适用测试通过,失败为 0,未授权跳过为 0;\n- 当前生产代码与已验证版本一致;\n- 每条验收标准都有实现和验证证据,非目标、范围变化与已知问题已说明;\n- 没有未解决的 BLOCKER、HIGH 或其他流程定义的阻塞问题;\n- 终态形态:用户已明确确认,确认原文已逐字记录,代码已按项目策略归档;\n- 非终态形态:验收摘要已呈现,归档待办已写入且动作清单明确(归档动作待审批通过后执行,不在本节点提前归档);\n- 最终提交、分支和远程状态可追溯。\n\n## 任务差异处理\n\n任务没有代码或项目没有分支归档流程时,执行适用的验收确认并说明不适用项。用户要求附条件通过或接受具体风险时,终态形态在节点内记录条件、风险和确认原文;非终态形态该表态在暂停点审批中给出,审批记录即留痕。未被明确接受的阻塞问题不得归档。证据过期、验收标准缺失或代码归档失败时,按实际问题返回相应节点处理,不在本节点重新执行其职责。\n\n## 节点记录与推进\n\n推进前将验收摘要、证据时效、验收标准对照、范围变化、已知问题和位置判定结果写入当前节点记录;非终态形态同时写入归档待办,并在归档完成前保持其未闭环。终态形态在用户确认后追加确认原文与归档结果;非终态形态在暂停点审批通过并完成归档后,追加审批引用、归档策略与结果、最终提交和远程状态,闭环归档待办。完成推进前检查后推进下一节点;批后追加动作发生在推进之后,不阻塞推进。",
482
+ "prompt": "# 发布验收\n\n## 节点职责\n\n核对最终集成验证证据与当前版本是否一致,向用户呈现可独立理解的验收摘要,并按本节点在流程中的位置承载确认与代码归档:存在后继审批边时,用户确认由流转边暂停点承载,本节点不重复确认;本节点为任务终点时,节点内确认是任务收尾唯一授权门。本节点不重复执行测试、代码评审或回归分析。\n\n## 依赖信息\n\n先检查当前任务上下文,读取需求、验收标准、非目标、已激活节点的完成记录、`test-integrate` 的测试范围与结果、日志以及已验证版本标识,并读取项目分支与归档策略。加载 `workflow-discipline`。\n\n缺少集成验证证据、版本标识或前序完成记录时,先从任务上下文补齐;证据仍不完整或无法对应当前版本时停止验收,不推断通过。\n\n## 执行流程\n\n1. 确认所有已激活的前序节点均已完成,不写死某一模板路径下的节点清单。\n2. 比较当前生产代码与 `test-integrate` 绑定的已验证版本。存在新的生产代码变化时,退回集成验证重新判断范围并回归;纯文档或记录变化说明为何不影响证据。\n3. 逐条核对验收标准对应的实现位置和前序验证证据,并确认非目标未被误实现、范围变化已说明、所有 reviewer 的阻塞问题已解决。\n4. 汇总集成测试范围与统计、未授权跳过、实现与验收标准对应关系、范围变化、已知问题和风险,形成简洁且不依赖任务内部术语的验收摘要。\n5. 判定本节点位置:读取任务上下文中的任务结构(DAG 节点集与各节点状态)——本节点是任务最后一个节点(如快速流程形态,已完成数加一等于节点总数)即为终态形态;存在后继节点(如架构归档节点)即为非终态形态。advance 响应的 nextNode 资料包可作交叉印证。两种形态的确认与归档时序不同,按后续步骤分支执行,判定结果写入节点记录。\n6. **终态形态(本节点为任务终点,无后继审批边)**:向用户呈现验收摘要并在节点内取得明确决定。只有明确的通过、确认归档或等价肯定表达构成确认;提问、条件句、部分认同、沉默或模糊表态不构成确认。确认后逐字记录确认原文,并按项目声明完成提交、合并、推送或其他代码归档动作。无项目策略时采用保留历史且可回退的常规方式,并记录采用的默认。\n7. **非终态形态(存在后继审批边)**:向用户呈现验收摘要(作为暂停点审批材料);推进前在节点记录写入显式「归档待办」条目(归档动作清单:目标分支、合并方式、推送范围);然后推进进入暂停点等待用户审批——审批通过即验收确认,确认原文以审批记录留痕(须逐字引用用户确认表态,不得转述失真),本节点不重复确认。审批通过后、开始下一节点工作前,先完成归档待办中的代码归档动作(提交/合并/推送),完成后回写归档结果闭环该待办;归档待办在归档完成前不得闭环。\n8. 归档过程中遇到冲突或失败时先检查实际版本控制状态并自主诊断;无法可靠解决时说明当前状态和卡点,不猜测操作结果。\n\n## 产出与检查\n\n推进前确认:\n\n- 所有已激活前序节点已完成;\n- 集成验证证据完整,所有适用测试通过,失败为 0,未授权跳过为 0;\n- 当前生产代码与已验证版本一致;\n- 每条验收标准都有实现和验证证据,非目标、范围变化与已知问题已说明;\n- 没有未解决的 BLOCKER、HIGH 或其他流程定义的阻塞问题;\n- 终态形态:用户已明确确认,确认原文已逐字记录,代码已按项目策略归档;\n- 非终态形态:验收摘要已呈现,归档待办已写入且动作清单明确(归档动作待审批通过后执行,不在本节点提前归档);\n- 最终提交、分支和远程状态可追溯。\n\n## 任务差异处理\n\n任务没有代码或项目没有分支归档流程时,执行适用的验收确认并说明不适用项。用户要求附条件通过或接受具体风险时,终态形态在节点内记录条件、风险和确认原文;非终态形态该表态在暂停点审批中给出,审批记录即留痕。未被明确接受的阻塞问题不得归档。证据过期、验收标准缺失或代码归档失败时,按实际问题返回相应节点处理,不在本节点重新执行其职责。\n\n## 节点记录与推进\n\n推进前将验收摘要、证据时效、验收标准对照、范围变化、已知问题和位置判定结果写入当前节点记录;非终态形态同时写入归档待办,并在归档完成前保持其未闭环。终态形态在用户确认后追加确认原文与归档结果;非终态形态在暂停点审批通过并完成归档后,追加审批引用、归档策略与结果、最终提交和远程状态,闭环归档待办。完成推进前检查后推进下一节点——终态形态的推进必须带收尾确认参数(CLI `--finalize` / MCP `finalize=true`),终节点缺省 advance 会被拒绝并提示;非终态形态不带该参数(携带也会被忽略);批后追加动作发生在推进之后,不阻塞推进。",
475
483
  "skills": [
476
484
  "workflow-discipline"
477
485
  ],
478
486
  "agents": [],
479
487
  "sourcePreset": {
480
488
  "code": "release-accept",
481
- "version": "1.5.0",
482
- "contentHash": "d9ef06ee6f469ce2",
489
+ "version": "1.5.1",
490
+ "contentHash": "abab1a2557e55c73",
483
491
  "agents": []
484
492
  },
485
493
  "toolChecks": [
@@ -508,7 +516,7 @@
508
516
  "label": "架构信息归档",
509
517
  "phase": "exit",
510
518
  "track": "all",
511
- "prompt": "# 架构信息归档\n\n## 节点职责\n\n从任务决策中筛选对未来设计持续有帮助的项目重大信息,形成架构信息修改计划,经独立 `arch-info-reviewer` 评审和用户明确确认后写入项目架构文档。没有合格条目是正常结果,本节点完成后任务正式收敛。\n\n## 依赖信息\n\n先检查当前任务上下文,读取需求、关键决策、技术方案、各节点记录、架构候选素材和发布验收结果,并读取项目及架构文档目录的提示词、记录标准、索引和相关现有条目。加载 `workflow-discipline`,调用独立 `arch-info-reviewer` 评审修改计划。\n\n项目已有架构文档体系时遵循其分类、命名、索引和写入规则。没有体系时先判断是否确有合格条目,不为“无新增”创建空目录或空文档。\n\n开工自检:检查前序节点记录中存在未闭环的归档待办(如发布验收节点审批通过后尚未完成的代码归档)时,先完成该归档并回写闭环,再开始本节点工作。\n\n## 执行流程\n\n1. 汇总任务中的候选决策,逐条判断它是否会约束未来多个设计或实现,是否属于项目特有的重要业务规则、模块边界、技术选型、兼容策略或跨任务约束。\n2. 排除常规功能演进、局部实现、缺陷修复过程、工作流水和通用工程常识;说明不收录理由及更合适的载体,没有其他载体时保留在任务记录。\n3. 检索架构索引和相关正文,判断候选是新增主题、现有主题演进、替代或退役,避免创建重复或冲突条目。\n4. 形成修改计划:建议项写明主题、决策、未来约束、适用范围、目标文档及新增或演进方式;不收录项写明门槛结论。允许建议项为零。\n5. 将项目记录标准、现有相关文档、客观决策素材和修改计划交给 `arch-info-reviewer`,不提供撰写者结论或预期结果。修复 BLOCKER、HIGH Findings 后重新评审,直至获得 PASS;MEDIUM、LOW 逐项处理或记录保留理由。\n6. 向用户同时呈现修改计划和独立评审结论。只有明确批准、调整后批准、放弃或确认无新增构成决定;逐字记录用户原文。\n7. 仅写入用户批准的条目:按项目组织更新正文和索引,同主题优先演进;无批准条目时不创建或修改架构文档。\n8. 核对批准条目与实际写入、正文与索引、演进与替代关系。有文档变化时按项目策略提交并推送;无变化时记录跳过。\n\n## 产出与检查\n\n推进前确认:\n\n- 任务决策和候选素材已完整读取;\n- 所有候选均经过记录门槛判断,修改计划包含建议项和不收录理由;\n- 相关现有条目已检索,新增、演进、替代和退役关系明确;\n- `arch-info-reviewer` 最终结论为 PASS,所有 Findings 均已处理或说明;\n- 修改计划和评审结论已呈现用户,确认原文已逐字记录;\n- 仅有用户批准的条目被写入,正文与索引一致;\n- 无新增时未创建空文档,有变化时提交与远程状态可追溯;\n- 当前节点记录完整,任务可以正式收敛。\n\n## 任务差异处理\n\n项目没有架构体系但有获批的重大决策时,优先使用项目声明的文档根建立最小结构;无文档根声明时使用 `docs/architecture/`,只创建承载获批内容所需的文件。用户未批准修改计划时保持暂停,不提前写入。候选全部不达门槛时,呈现“无新增”及主要理由,经用户确认后正常完成。\n\n## 节点记录与推进\n\n将架构归档结论、候选过滤、修改计划、独立评审结论、用户确认原文、写入或不收录决定、架构文档与索引引用、提交和远程状态写入当前节点记录。完成上述检查后推进终态,使任务完成;在任务完成前不开始其他任务。",
519
+ "prompt": "# 架构信息归档\n\n## 节点职责\n\n从任务决策中筛选对未来设计持续有帮助的项目重大信息,形成架构信息修改计划,经独立 `arch-info-reviewer` 评审和用户明确确认后写入项目架构文档。没有合格条目是正常结果,本节点完成后任务正式收敛。\n\n## 依赖信息\n\n先检查当前任务上下文,读取需求、关键决策、技术方案、各节点记录、架构候选素材和发布验收结果,并读取项目及架构文档目录的提示词、记录标准、索引和相关现有条目。加载 `workflow-discipline`,调用独立 `arch-info-reviewer` 评审修改计划。\n\n项目已有架构文档体系时遵循其分类、命名、索引和写入规则。没有体系时先判断是否确有合格条目,不为“无新增”创建空目录或空文档。\n\n开工自检:检查前序节点记录中存在未闭环的归档待办(如发布验收节点审批通过后尚未完成的代码归档)时,先完成该归档并回写闭环,再开始本节点工作。\n\n## 执行流程\n\n1. 汇总任务中的候选决策,逐条判断它是否会约束未来多个设计或实现,是否属于项目特有的重要业务规则、模块边界、技术选型、兼容策略或跨任务约束。\n2. 排除常规功能演进、局部实现、缺陷修复过程、工作流水和通用工程常识;说明不收录理由及更合适的载体,没有其他载体时保留在任务记录。\n3. 检索架构索引和相关正文,判断候选是新增主题、现有主题演进、替代或退役,避免创建重复或冲突条目。\n4. 形成修改计划:建议项写明主题、决策、未来约束、适用范围、目标文档及新增或演进方式;不收录项写明门槛结论。允许建议项为零。\n5. 将项目记录标准、现有相关文档、客观决策素材和修改计划交给 `arch-info-reviewer`,不提供撰写者结论或预期结果。修复 BLOCKER、HIGH Findings 后重新评审,直至获得 PASS;MEDIUM、LOW 逐项处理或记录保留理由。\n6. 向用户同时呈现修改计划和独立评审结论。只有明确批准、调整后批准、放弃或确认无新增构成决定;逐字记录用户原文。\n7. 仅写入用户批准的条目:按项目组织更新正文和索引,同主题优先演进;无批准条目时不创建或修改架构文档。\n8. 核对批准条目与实际写入、正文与索引、演进与替代关系。有文档变化时按项目策略提交并推送;无变化时记录跳过。\n\n## 产出与检查\n\n推进前确认:\n\n- 任务决策和候选素材已完整读取;\n- 所有候选均经过记录门槛判断,修改计划包含建议项和不收录理由;\n- 相关现有条目已检索,新增、演进、替代和退役关系明确;\n- `arch-info-reviewer` 最终结论为 PASS,所有 Findings 均已处理或说明;\n- 修改计划和评审结论已呈现用户,确认原文已逐字记录;\n- 仅有用户批准的条目被写入,正文与索引一致;\n- 无新增时未创建空文档,有变化时提交与远程状态可追溯;\n- 当前节点记录完整,任务可以正式收敛。\n\n## 任务差异处理\n\n项目没有架构体系但有获批的重大决策时,优先使用项目声明的文档根建立最小结构;无文档根声明时使用 `docs/architecture/`,只创建承载获批内容所需的文件。用户未批准修改计划时保持暂停,不提前写入。候选全部不达门槛时,呈现“无新增”及主要理由,经用户确认后正常完成。\n\n## 节点记录与推进\n\n将架构归档结论、候选过滤、修改计划、独立评审结论、用户确认原文、写入或不收录决定、架构文档与索引引用、提交和远程状态写入当前节点记录。完成上述检查后推进终态,使任务完成——终节点推进必须带收尾确认参数(CLI `--finalize` / MCP `finalize=true`),缺省 advance 会被拒绝并提示;在任务完成前不开始其他任务。",
512
520
  "skills": [
513
521
  "workflow-discipline"
514
522
  ],
@@ -517,8 +525,8 @@
517
525
  ],
518
526
  "sourcePreset": {
519
527
  "code": "archive-architecture",
520
- "version": "1.5.0",
521
- "contentHash": "a585464156b6179c",
528
+ "version": "1.5.1",
529
+ "contentHash": "b9e065c450df6f67",
522
530
  "agents": [
523
531
  "arch-info-reviewer"
524
532
  ]
@@ -660,25 +668,25 @@
660
668
  "x": 110,
661
669
  "y": 184
662
670
  },
663
- "DESIGN": {
664
- "x": 110,
671
+ "INTERACTION": {
672
+ "x": 180,
665
673
  "y": 328
666
674
  },
667
- "INTERACTION": {
675
+ "DESIGN": {
668
676
  "x": 110,
669
677
  "y": 472
670
678
  },
671
679
  "CODE_BACKEND": {
672
- "x": 178.71533661918517,
673
- "y": 618.5693267616297
680
+ "x": 180,
681
+ "y": 616
674
682
  },
675
683
  "CODE_UI": {
676
- "x": -150.13018036059404,
677
- "y": 622.7764209502931
684
+ "x": 40,
685
+ "y": 1048
678
686
  },
679
687
  "UI_VISUAL": {
680
- "x": -150.130180360594,
681
- "y": 1041.9297871421447
688
+ "x": 40,
689
+ "y": 1480
682
690
  },
683
691
  "UT_DESIGN": {
684
692
  "x": 180,
@@ -689,36 +697,36 @@
689
697
  "y": 904
690
698
  },
691
699
  "UI_UT_DESIGN": {
692
- "x": -150.130180360594,
693
- "y": 759.0684406654042
700
+ "x": 40,
701
+ "y": 1192
694
702
  },
695
703
  "UI_UT_DEV": {
696
- "x": -150.130180360594,
697
- "y": 905.6377674270339
704
+ "x": 40,
705
+ "y": 1336
698
706
  },
699
707
  "E2E_DESIGN": {
700
708
  "x": 110,
701
- "y": 1191.0684406654043
709
+ "y": 1624
702
710
  },
703
711
  "E2E_DEV": {
704
712
  "x": 110,
705
- "y": 1319.6524800956263
713
+ "y": 1768
706
714
  },
707
715
  "INTEGRATE": {
708
716
  "x": 110,
709
- "y": 1462.3678167148114
717
+ "y": 1912
710
718
  },
711
719
  "ACCEPT": {
712
- "x": 111.28466338081483,
713
- "y": 1599.9444998107372
720
+ "x": 110,
721
+ "y": 2056
714
722
  },
715
723
  "ARCHIVE": {
716
724
  "x": 110,
717
- "y": 1745.229163191552
725
+ "y": 2200
718
726
  }
719
727
  },
720
728
  "isDefault": false,
721
- "version": "3.7.0"
729
+ "version": "3.7.3"
722
730
  },
723
731
  "dependencies": {
724
732
  "skills": [
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "exportFormatVersion": "1.0",
3
- "exportedAt": "2026-10-12T09:03:35.957Z",
3
+ "exportedAt": "2026-10-14T08:44:31.836Z",
4
4
  "warnings": [],
5
5
  "template": {
6
6
  "name": "quick_workflow",
@@ -159,15 +159,15 @@
159
159
  "label": "发布验收",
160
160
  "phase": "exit",
161
161
  "track": "all",
162
- "prompt": "# 发布验收\n\n## 节点职责\n\n核对最终集成验证证据与当前版本是否一致,向用户呈现可独立理解的验收摘要,并按本节点在流程中的位置承载确认与代码归档:存在后继审批边时,用户确认由流转边暂停点承载,本节点不重复确认;本节点为任务终点时,节点内确认是任务收尾唯一授权门。本节点不重复执行测试、代码评审或回归分析。\n\n## 依赖信息\n\n先检查当前任务上下文,读取需求、验收标准、非目标、已激活节点的完成记录、`test-integrate` 的测试范围与结果、日志以及已验证版本标识,并读取项目分支与归档策略。加载 `workflow-discipline`。\n\n缺少集成验证证据、版本标识或前序完成记录时,先从任务上下文补齐;证据仍不完整或无法对应当前版本时停止验收,不推断通过。\n\n## 执行流程\n\n1. 确认所有已激活的前序节点均已完成,不写死某一模板路径下的节点清单。\n2. 比较当前生产代码与 `test-integrate` 绑定的已验证版本。存在新的生产代码变化时,退回集成验证重新判断范围并回归;纯文档或记录变化说明为何不影响证据。\n3. 逐条核对验收标准对应的实现位置和前序验证证据,并确认非目标未被误实现、范围变化已说明、所有 reviewer 的阻塞问题已解决。\n4. 汇总集成测试范围与统计、未授权跳过、实现与验收标准对应关系、范围变化、已知问题和风险,形成简洁且不依赖任务内部术语的验收摘要。\n5. 判定本节点位置:读取任务上下文中的任务结构(DAG 节点集与各节点状态)——本节点是任务最后一个节点(如快速流程形态,已完成数加一等于节点总数)即为终态形态;存在后继节点(如架构归档节点)即为非终态形态。advance 响应的 nextNode 资料包可作交叉印证。两种形态的确认与归档时序不同,按后续步骤分支执行,判定结果写入节点记录。\n6. **终态形态(本节点为任务终点,无后继审批边)**:向用户呈现验收摘要并在节点内取得明确决定。只有明确的通过、确认归档或等价肯定表达构成确认;提问、条件句、部分认同、沉默或模糊表态不构成确认。确认后逐字记录确认原文,并按项目声明完成提交、合并、推送或其他代码归档动作。无项目策略时采用保留历史且可回退的常规方式,并记录采用的默认。\n7. **非终态形态(存在后继审批边)**:向用户呈现验收摘要(作为暂停点审批材料);推进前在节点记录写入显式「归档待办」条目(归档动作清单:目标分支、合并方式、推送范围);然后推进进入暂停点等待用户审批——审批通过即验收确认,确认原文以审批记录留痕(须逐字引用用户确认表态,不得转述失真),本节点不重复确认。审批通过后、开始下一节点工作前,先完成归档待办中的代码归档动作(提交/合并/推送),完成后回写归档结果闭环该待办;归档待办在归档完成前不得闭环。\n8. 归档过程中遇到冲突或失败时先检查实际版本控制状态并自主诊断;无法可靠解决时说明当前状态和卡点,不猜测操作结果。\n\n## 产出与检查\n\n推进前确认:\n\n- 所有已激活前序节点已完成;\n- 集成验证证据完整,所有适用测试通过,失败为 0,未授权跳过为 0;\n- 当前生产代码与已验证版本一致;\n- 每条验收标准都有实现和验证证据,非目标、范围变化与已知问题已说明;\n- 没有未解决的 BLOCKER、HIGH 或其他流程定义的阻塞问题;\n- 终态形态:用户已明确确认,确认原文已逐字记录,代码已按项目策略归档;\n- 非终态形态:验收摘要已呈现,归档待办已写入且动作清单明确(归档动作待审批通过后执行,不在本节点提前归档);\n- 最终提交、分支和远程状态可追溯。\n\n## 任务差异处理\n\n任务没有代码或项目没有分支归档流程时,执行适用的验收确认并说明不适用项。用户要求附条件通过或接受具体风险时,终态形态在节点内记录条件、风险和确认原文;非终态形态该表态在暂停点审批中给出,审批记录即留痕。未被明确接受的阻塞问题不得归档。证据过期、验收标准缺失或代码归档失败时,按实际问题返回相应节点处理,不在本节点重新执行其职责。\n\n## 节点记录与推进\n\n推进前将验收摘要、证据时效、验收标准对照、范围变化、已知问题和位置判定结果写入当前节点记录;非终态形态同时写入归档待办,并在归档完成前保持其未闭环。终态形态在用户确认后追加确认原文与归档结果;非终态形态在暂停点审批通过并完成归档后,追加审批引用、归档策略与结果、最终提交和远程状态,闭环归档待办。完成推进前检查后推进下一节点;批后追加动作发生在推进之后,不阻塞推进。",
162
+ "prompt": "# 发布验收\n\n## 节点职责\n\n核对最终集成验证证据与当前版本是否一致,向用户呈现可独立理解的验收摘要,并按本节点在流程中的位置承载确认与代码归档:存在后继审批边时,用户确认由流转边暂停点承载,本节点不重复确认;本节点为任务终点时,节点内确认是任务收尾唯一授权门。本节点不重复执行测试、代码评审或回归分析。\n\n## 依赖信息\n\n先检查当前任务上下文,读取需求、验收标准、非目标、已激活节点的完成记录、`test-integrate` 的测试范围与结果、日志以及已验证版本标识,并读取项目分支与归档策略。加载 `workflow-discipline`。\n\n缺少集成验证证据、版本标识或前序完成记录时,先从任务上下文补齐;证据仍不完整或无法对应当前版本时停止验收,不推断通过。\n\n## 执行流程\n\n1. 确认所有已激活的前序节点均已完成,不写死某一模板路径下的节点清单。\n2. 比较当前生产代码与 `test-integrate` 绑定的已验证版本。存在新的生产代码变化时,退回集成验证重新判断范围并回归;纯文档或记录变化说明为何不影响证据。\n3. 逐条核对验收标准对应的实现位置和前序验证证据,并确认非目标未被误实现、范围变化已说明、所有 reviewer 的阻塞问题已解决。\n4. 汇总集成测试范围与统计、未授权跳过、实现与验收标准对应关系、范围变化、已知问题和风险,形成简洁且不依赖任务内部术语的验收摘要。\n5. 判定本节点位置:读取任务上下文中的任务结构(DAG 节点集与各节点状态)——本节点是任务最后一个节点(如快速流程形态,已完成数加一等于节点总数)即为终态形态;存在后继节点(如架构归档节点)即为非终态形态。advance 响应的 nextNode 资料包可作交叉印证。两种形态的确认与归档时序不同,按后续步骤分支执行,判定结果写入节点记录。\n6. **终态形态(本节点为任务终点,无后继审批边)**:向用户呈现验收摘要并在节点内取得明确决定。只有明确的通过、确认归档或等价肯定表达构成确认;提问、条件句、部分认同、沉默或模糊表态不构成确认。确认后逐字记录确认原文,并按项目声明完成提交、合并、推送或其他代码归档动作。无项目策略时采用保留历史且可回退的常规方式,并记录采用的默认。\n7. **非终态形态(存在后继审批边)**:向用户呈现验收摘要(作为暂停点审批材料);推进前在节点记录写入显式「归档待办」条目(归档动作清单:目标分支、合并方式、推送范围);然后推进进入暂停点等待用户审批——审批通过即验收确认,确认原文以审批记录留痕(须逐字引用用户确认表态,不得转述失真),本节点不重复确认。审批通过后、开始下一节点工作前,先完成归档待办中的代码归档动作(提交/合并/推送),完成后回写归档结果闭环该待办;归档待办在归档完成前不得闭环。\n8. 归档过程中遇到冲突或失败时先检查实际版本控制状态并自主诊断;无法可靠解决时说明当前状态和卡点,不猜测操作结果。\n\n## 产出与检查\n\n推进前确认:\n\n- 所有已激活前序节点已完成;\n- 集成验证证据完整,所有适用测试通过,失败为 0,未授权跳过为 0;\n- 当前生产代码与已验证版本一致;\n- 每条验收标准都有实现和验证证据,非目标、范围变化与已知问题已说明;\n- 没有未解决的 BLOCKER、HIGH 或其他流程定义的阻塞问题;\n- 终态形态:用户已明确确认,确认原文已逐字记录,代码已按项目策略归档;\n- 非终态形态:验收摘要已呈现,归档待办已写入且动作清单明确(归档动作待审批通过后执行,不在本节点提前归档);\n- 最终提交、分支和远程状态可追溯。\n\n## 任务差异处理\n\n任务没有代码或项目没有分支归档流程时,执行适用的验收确认并说明不适用项。用户要求附条件通过或接受具体风险时,终态形态在节点内记录条件、风险和确认原文;非终态形态该表态在暂停点审批中给出,审批记录即留痕。未被明确接受的阻塞问题不得归档。证据过期、验收标准缺失或代码归档失败时,按实际问题返回相应节点处理,不在本节点重新执行其职责。\n\n## 节点记录与推进\n\n推进前将验收摘要、证据时效、验收标准对照、范围变化、已知问题和位置判定结果写入当前节点记录;非终态形态同时写入归档待办,并在归档完成前保持其未闭环。终态形态在用户确认后追加确认原文与归档结果;非终态形态在暂停点审批通过并完成归档后,追加审批引用、归档策略与结果、最终提交和远程状态,闭环归档待办。完成推进前检查后推进下一节点——终态形态的推进必须带收尾确认参数(CLI `--finalize` / MCP `finalize=true`),终节点缺省 advance 会被拒绝并提示;非终态形态不带该参数(携带也会被忽略);批后追加动作发生在推进之后,不阻塞推进。",
163
163
  "skills": [
164
164
  "workflow-discipline"
165
165
  ],
166
166
  "agents": [],
167
167
  "sourcePreset": {
168
168
  "code": "release-accept",
169
- "version": "1.5.0",
170
- "contentHash": "d9ef06ee6f469ce2",
169
+ "version": "1.5.1",
170
+ "contentHash": "abab1a2557e55c73",
171
171
  "agents": []
172
172
  },
173
173
  "toolChecks": [
@@ -233,7 +233,7 @@
233
233
  }
234
234
  ],
235
235
  "isDefault": false,
236
- "version": "2.0.6"
236
+ "version": "2.0.7"
237
237
  },
238
238
  "dependencies": {
239
239
  "skills": [
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "exportFormatVersion": "1.0",
3
- "exportedAt": "2026-10-12T09:03:35.954Z",
3
+ "exportedAt": "2026-10-14T08:44:31.834Z",
4
4
  "warnings": [],
5
5
  "template": {
6
6
  "name": "research_workflow",
@@ -45,15 +45,15 @@
45
45
  "label": "调研验收",
46
46
  "phase": "exit",
47
47
  "track": "all",
48
- "prompt": "# 调研验收\n\n## 节点职责\n\n对照已确认的调研范围核对报告完整性和证据状态,向用户呈现可独立判断的验收摘要;用户明确确认后,按项目策略归档报告并完成任务。\n\n## 依赖信息\n\n先检查当前任务上下文,读取已确认的调研问题、范围、证据标准、停止条件、调研报告、来源与实验记录、限制和开放问题,以及项目的归档约定。加载 `workflow-discipline`。\n\n本节点不重新执行调研,不虚构独立 reviewer,也不做代码测试或代码评审。报告存在缺失、越界或证据不匹配时,返回 `research-execute` 补充或修订。\n\n## 执行流程\n\n1. 建立“调研问题 → 结论或无法结论 → 证据 → 报告位置”的对应关系,确认已确认范围中的问题没有遗漏。\n2. 核对结论是否能追溯到报告所列代码、原始资料或实验,事实、推断、建议、限制和开放问题是否清楚区分。\n3. 检查问题校正和新增发现是否改变了已确认范围;属于范围外的重要事项只列为后续议题,不在本任务中静默扩展。\n4. 发现问题遗漏、证据无法支持结论、限制被隐瞒或报告不可复核时,记录具体缺口并返回调研执行修订;修订后重新核对受影响内容。\n5. 形成验收摘要,逐项呈现问题与结论、关键证据、无法结论、可信度与适用限制、开放问题和建议边界,并说明报告归档位置。\n6. 请求用户对本次调研结果作明确确认。提问、条件句、模糊表态或沉默不视为确认;用户未确认时保持当前节点。\n7. 用户确认后记录确认原文,按项目约定归档调研报告和相关记录,完成任务。\n\n## 产出与检查\n\n完成前确认:\n\n- 已确认范围中的每个问题都有结论或明确的无法结论及原因;\n- 结论可追溯到对应证据,报告位置明确;\n- 事实、推断、建议、限制和开放问题已清楚区分;\n- 问题校正与新增发现没有静默改变任务范围;\n- 验收摘要足以让用户判断调研结果及其边界;\n- 用户已明确确认,确认原文已记录;\n- 调研报告已按项目策略归档,归档结果可追溯。\n\n## 任务差异处理\n\n调研报告可以包含无法结论,前提是原因、已尝试方法和缺失证据如实说明。项目没有特殊归档流程时,采用现有文档和版本控制惯例保存报告,不擅自建立新流程。报告之外出现生产代码变更时,说明任务越界并暂停,不在调研验收中处理。用户要求改变调研目标或补充新的范围时,记录为范围变更并回到适当节点处理。\n\n## 节点记录与推进\n\n将问题覆盖关系、证据核对结果、退回修订及结果、无法结论、限制和开放问题、验收摘要、用户确认原文、报告与归档引用写入当前节点记录。所有检查完成后结束任务;任何检查未满足时保留在当前节点或返回相应节点处理。",
48
+ "prompt": "# 调研验收\n\n## 节点职责\n\n对照已确认的调研范围核对报告完整性和证据状态,向用户呈现可独立判断的验收摘要;用户明确确认后,按项目策略归档报告并完成任务。\n\n## 依赖信息\n\n先检查当前任务上下文,读取已确认的调研问题、范围、证据标准、停止条件、调研报告、来源与实验记录、限制和开放问题,以及项目的归档约定。加载 `workflow-discipline`。\n\n本节点不重新执行调研,不虚构独立 reviewer,也不做代码测试或代码评审。报告存在缺失、越界或证据不匹配时,返回 `research-execute` 补充或修订。\n\n## 执行流程\n\n1. 建立“调研问题 → 结论或无法结论 → 证据 → 报告位置”的对应关系,确认已确认范围中的问题没有遗漏。\n2. 核对结论是否能追溯到报告所列代码、原始资料或实验,事实、推断、建议、限制和开放问题是否清楚区分。\n3. 检查问题校正和新增发现是否改变了已确认范围;属于范围外的重要事项只列为后续议题,不在本任务中静默扩展。\n4. 发现问题遗漏、证据无法支持结论、限制被隐瞒或报告不可复核时,记录具体缺口并返回调研执行修订;修订后重新核对受影响内容。\n5. 形成验收摘要,逐项呈现问题与结论、关键证据、无法结论、可信度与适用限制、开放问题和建议边界,并说明报告归档位置。\n6. 请求用户对本次调研结果作明确确认。提问、条件句、模糊表态或沉默不视为确认;用户未确认时保持当前节点。\n7. 用户确认后记录确认原文,按项目约定归档调研报告和相关记录,完成任务。\n\n## 产出与检查\n\n完成前确认:\n\n- 已确认范围中的每个问题都有结论或明确的无法结论及原因;\n- 结论可追溯到对应证据,报告位置明确;\n- 事实、推断、建议、限制和开放问题已清楚区分;\n- 问题校正与新增发现没有静默改变任务范围;\n- 验收摘要足以让用户判断调研结果及其边界;\n- 用户已明确确认,确认原文已记录;\n- 调研报告已按项目策略归档,归档结果可追溯。\n\n## 任务差异处理\n\n调研报告可以包含无法结论,前提是原因、已尝试方法和缺失证据如实说明。项目没有特殊归档流程时,采用现有文档和版本控制惯例保存报告,不擅自建立新流程。报告之外出现生产代码变更时,说明任务越界并暂停,不在调研验收中处理。用户要求改变调研目标或补充新的范围时,记录为范围变更并回到适当节点处理。\n\n## 节点记录与推进\n\n将问题覆盖关系、证据核对结果、退回修订及结果、无法结论、限制和开放问题、验收摘要、用户确认原文、报告与归档引用写入当前节点记录。所有检查完成后结束任务——终节点推进必须带收尾确认参数(CLI `--finalize` / MCP `finalize=true`),缺省 advance 会被拒绝并提示;任何检查未满足时保留在当前节点或返回相应节点处理。",
49
49
  "skills": [
50
50
  "workflow-discipline"
51
51
  ],
52
52
  "agents": [],
53
53
  "sourcePreset": {
54
54
  "code": "research-accept",
55
- "version": "1.3.0",
56
- "contentHash": "26e6f298b87ec0bf",
55
+ "version": "1.3.1",
56
+ "contentHash": "19e18ecbc644e994",
57
57
  "agents": []
58
58
  }
59
59
  }
@@ -88,7 +88,7 @@
88
88
  }
89
89
  },
90
90
  "isDefault": false,
91
- "version": "1.0.3"
91
+ "version": "1.0.4"
92
92
  },
93
93
  "dependencies": {
94
94
  "skills": [