@siming-org/core 0.8.3 → 0.8.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/dist/index.d.ts +213 -44
  2. package/dist/index.js +301 -41
  3. package/package.json +1 -1
package/dist/index.d.ts CHANGED
@@ -437,10 +437,16 @@ declare function assertBoundSkillsCompatible(skillRepo: SkillRepo, agentScope: A
437
437
  * (`nodeRecords.${nodeId}.checks`),id 含 `.`/`$` 会造成路径注入/错位。
438
438
  */
439
439
  declare const DagNodePhaseSchema: z.ZodString;
440
- declare const DagNodeTrackSchema: z.ZodString;
440
+ /**
441
+ * T202610120005 R7:节点轨道 union 形态——字符串(单轨)或非空字符串数组(多轨节点,
442
+ * 任一成员被任务匹配集覆盖即激活;含 'all' 成员等价全轨节点)。值域校验(枚举注册表
443
+ * active 条目)由画布校验/类型校验承载,schema 只做形态与结构必填拦截(空数组拒绝)。
444
+ * 消费端判定统一走 core/task/track-match.ts 的 effectiveTracks/trackIntersects。
445
+ */
446
+ declare const DagNodeTrackSchema: z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>;
441
447
  /**
442
448
  * 内置 phase/track 常量(seed 引用 + 文档化;注册表为运行时权威)。
443
- * T202608240003:dag_track 另有任务级专用值 mixed/research(注册表 builtin,非节点
449
+ * T202608240003:dag_track 另有任务级专用值 all/research(注册表 builtin,非节点
444
450
  * track 值)——节点 track 不使用这两个值;本常量仅覆盖节点级内置值。
445
451
  */
446
452
  declare const DAG_PHASES: readonly ["entry", "track", "test", "exit"];
@@ -495,7 +501,7 @@ declare const DagNodeSchema: z.ZodObject<{
495
501
  id: z.ZodString;
496
502
  label: z.ZodString;
497
503
  phase: z.ZodString;
498
- track: z.ZodString;
504
+ track: z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>;
499
505
  prompt: z.ZodString;
500
506
  skills: z.ZodDefault<z.ZodArray<z.ZodString>>;
501
507
  agents: z.ZodOptional<z.ZodArray<z.ZodString>>;
@@ -592,7 +598,7 @@ declare const DagTemplateSchema: z.ZodObject<{
592
598
  id: z.ZodString;
593
599
  label: z.ZodString;
594
600
  phase: z.ZodString;
595
- track: z.ZodString;
601
+ track: z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>;
596
602
  prompt: z.ZodString;
597
603
  skills: z.ZodDefault<z.ZodArray<z.ZodString>>;
598
604
  agents: z.ZodOptional<z.ZodArray<z.ZodString>>;
@@ -645,7 +651,7 @@ declare const DagTemplateCreateSchema: z.ZodObject<{
645
651
  id: z.ZodString;
646
652
  label: z.ZodString;
647
653
  phase: z.ZodString;
648
- track: z.ZodString;
654
+ track: z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>;
649
655
  prompt: z.ZodString;
650
656
  skills: z.ZodDefault<z.ZodArray<z.ZodString>>;
651
657
  agents: z.ZodOptional<z.ZodArray<z.ZodString>>;
@@ -699,7 +705,7 @@ declare const DagTemplateUpdateSchema: z.ZodObject<{
699
705
  id: z.ZodString;
700
706
  label: z.ZodString;
701
707
  phase: z.ZodString;
702
- track: z.ZodString;
708
+ track: z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>;
703
709
  prompt: z.ZodString;
704
710
  skills: z.ZodDefault<z.ZodArray<z.ZodString>>;
705
711
  agents: z.ZodOptional<z.ZodArray<z.ZodString>>;
@@ -760,6 +766,50 @@ declare function findDuplicateNodeIds(nodes: ReadonlyArray<{
760
766
  declare function assertUniqueNodeIds(nodes: ReadonlyArray<{
761
767
  id: string;
762
768
  }>): void;
769
+ /** 同轨分叉问题描述(fromNode 对同一目标 track 有多条出边) */
770
+ interface SameTrackForkIssue {
771
+ /** 分叉源节点 id */
772
+ nodeId: string;
773
+ /** 出边指向的目标节点 track(分组键) */
774
+ targetTrack: string;
775
+ /** 该组出边条数 */
776
+ count: number;
777
+ }
778
+ /**
779
+ * 静态扫描同轨分叉(D6):对每个节点的出边按【目标节点轨道集合】分组,任一组 > 1 条 → 分叉。
780
+ *
781
+ * 三方同语义单源(运行期 advance-engine findNextEdge 分组查重 / 画布 validate D6 规则 /
782
+ * 本函数写入侧前置拦截)——边本身无 track 字段,目标节点轨道即分组键(与 findNextEdge 一致)。
783
+ *
784
+ * 判定口径(与运行期消歧对齐):
785
+ * - 1 专属边(目标轨道 ≠ 'all')+ 1 all 边 → 合法(运行期专属优先消歧)
786
+ * - 2 异轨专属边(目标轨道不同)→ 合法(跨轨分叉,如 DESIGN→{CODE_BACKEND, CODE_UI})
787
+ * - 同组 > 1 条(含 1 专属 + 2 all、2 all)→ 违规(运行期该组消歧必抛错)
788
+ *
789
+ * T202610120005 R7:节点轨道 union 形态——分组键 = 轨道集合规范串(数组去重排序
790
+ * join('+');含 'all' 成员归一 'all')。轨道集合相同的节点(如 'backend' 与
791
+ * ['backend']、['godot','backend'] 与 ['backend','godot'])同组参与查重。静态扫描
792
+ * 按全量轨道集合分组(不绑定任务 matchSet)——比运行期(按交集分组)宽松:轨道
793
+ * 集合不同的节点静态异组但某任务下可能同组,由运行期 findNextEdge 最终守卫兜底
794
+ * (静态拦截是写入侧前置,不可能枚举全部任务轨道组合)。
795
+ *
796
+ * 悬挂边(to 指向不存在节点)不在此报——由基础图校验负责,避免双重报错。
797
+ * 函数级校验而非 schema 级(assertUniqueNodeIds 先例——模板级 schema 禁挂 refine)。
798
+ */
799
+ declare function findSameTrackForks(nodes: ReadonlyArray<{
800
+ id: string;
801
+ track: string | string[];
802
+ }>, edges: ReadonlyArray<{
803
+ from: string;
804
+ to: string;
805
+ }>): SameTrackForkIssue[];
806
+ declare function assertNoSameTrackFork(nodes: ReadonlyArray<{
807
+ id: string;
808
+ track: string | string[];
809
+ }>, edges: ReadonlyArray<{
810
+ from: string;
811
+ to: string;
812
+ }>): void;
763
813
 
764
814
  type DagTemplateRepo = Repo<DagTemplate> & {
765
815
  createDagTemplate(data: DagTemplateCreate): Promise<DagTemplate>;
@@ -999,7 +1049,7 @@ declare const DagInstanceNodeSchema: z.ZodObject<{
999
1049
  id: z.ZodString;
1000
1050
  label: z.ZodString;
1001
1051
  phase: z.ZodString;
1002
- track: z.ZodString;
1052
+ track: z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>;
1003
1053
  prompt: z.ZodString;
1004
1054
  skills: z.ZodDefault<z.ZodArray<z.ZodString>>;
1005
1055
  agents: z.ZodDefault<z.ZodArray<z.ZodString>>;
@@ -1020,7 +1070,7 @@ declare const DagInstanceSchema: z.ZodObject<{
1020
1070
  id: z.ZodString;
1021
1071
  label: z.ZodString;
1022
1072
  phase: z.ZodString;
1023
- track: z.ZodString;
1073
+ track: z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>;
1024
1074
  prompt: z.ZodString;
1025
1075
  skills: z.ZodDefault<z.ZodArray<z.ZodString>>;
1026
1076
  agents: z.ZodDefault<z.ZodArray<z.ZodString>>;
@@ -1117,7 +1167,7 @@ declare const TaskSchema: z.ZodObject<{
1117
1167
  id: z.ZodString;
1118
1168
  label: z.ZodString;
1119
1169
  phase: z.ZodString;
1120
- track: z.ZodString;
1170
+ track: z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>;
1121
1171
  prompt: z.ZodString;
1122
1172
  skills: z.ZodDefault<z.ZodArray<z.ZodString>>;
1123
1173
  agents: z.ZodDefault<z.ZodArray<z.ZodString>>;
@@ -1155,6 +1205,7 @@ declare const TaskSchema: z.ZodObject<{
1155
1205
  status: z.ZodString;
1156
1206
  pausedAt: z.ZodDefault<z.ZodNullable<z.ZodString>>;
1157
1207
  track: z.ZodString;
1208
+ trackMatch: z.ZodOptional<z.ZodArray<z.ZodString>>;
1158
1209
  doc: z.ZodOptional<z.ZodObject<{
1159
1210
  what: z.ZodString;
1160
1211
  why: z.ZodString;
@@ -1254,7 +1305,7 @@ type Task = z.infer<typeof TaskSchema>;
1254
1305
  type TaskPatch = Partial<Task> & Record<string, unknown>;
1255
1306
  declare const TaskCreateInputSchema: z.ZodObject<{
1256
1307
  title: z.ZodString;
1257
- track: z.ZodString;
1308
+ track: z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>;
1258
1309
  dagTemplateId: z.ZodString;
1259
1310
  skipNodes: z.ZodOptional<z.ZodArray<z.ZodString>>;
1260
1311
  type: z.ZodOptional<z.ZodEnum<{
@@ -1287,12 +1338,13 @@ declare const TaskContextSchema: z.ZodObject<{
1287
1338
  status: z.ZodString;
1288
1339
  pausedAt: z.ZodNullable<z.ZodString>;
1289
1340
  track: z.ZodString;
1341
+ trackMatch: z.ZodOptional<z.ZodArray<z.ZodString>>;
1290
1342
  }, z.core.$strip>;
1291
1343
  currentNode: z.ZodNullable<z.ZodObject<{
1292
1344
  nodeId: z.ZodString;
1293
1345
  label: z.ZodString;
1294
1346
  phase: z.ZodString;
1295
- track: z.ZodString;
1347
+ track: z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>;
1296
1348
  prompt: z.ZodString;
1297
1349
  skills: z.ZodArray<z.ZodString>;
1298
1350
  agents: z.ZodArray<z.ZodString>;
@@ -1419,6 +1471,7 @@ declare const TaskSummarySchema: z.ZodObject<{
1419
1471
  status: z.ZodString;
1420
1472
  pausedAt: z.ZodNullable<z.ZodString>;
1421
1473
  track: z.ZodString;
1474
+ trackMatch: z.ZodOptional<z.ZodArray<z.ZodString>>;
1422
1475
  progress: z.ZodObject<{
1423
1476
  completed: z.ZodNumber;
1424
1477
  total: z.ZodNumber;
@@ -1453,7 +1506,10 @@ interface TaskPathUpdate {
1453
1506
  }
1454
1507
  type TaskRepo = Repo<Task> & {
1455
1508
  /** resolvedProjectId 由路由层解析(D2:显式携带校验一致性后传入 / 缺省 = 模板 projectId),repo 不做隐式继承;session 供 convert 组事务透传(T202609150002:任务+关联原子,缺省行为不变) */
1456
- createTask(input: TaskCreateInput, template: DagTemplate, resolvedProjectId: string, session?: ClientSession): Promise<Task>;
1509
+ createTask(input: TaskCreateInput, template: DagTemplate, resolvedProjectId: string, trackInfo: {
1510
+ trackMembers: string[];
1511
+ trackAnchor: string;
1512
+ }, session?: ClientSession): Promise<Task>;
1457
1513
  getByTaskId(taskId: string): Promise<Task | null>;
1458
1514
  /** N015 F4/F5/F6 + N017 D6:分页 + 排序 + 进度概要投影(剥离 dagInstance/history;total 为过滤后总数) */
1459
1515
  listTasks(filter?: TaskListFilter): Promise<{
@@ -2021,7 +2077,7 @@ declare const NodePresetShapeSchema: z.ZodObject<{
2021
2077
  label: z.ZodString;
2022
2078
  nodeId: z.ZodString;
2023
2079
  phase: z.ZodString;
2024
- track: z.ZodString;
2080
+ track: z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>;
2025
2081
  prompt: z.ZodString;
2026
2082
  skills: z.ZodArray<z.ZodString>;
2027
2083
  agents: z.ZodArray<z.ZodString>;
@@ -2053,7 +2109,7 @@ declare const NodePresetSchema: z.ZodObject<{
2053
2109
  label: z.ZodString;
2054
2110
  nodeId: z.ZodString;
2055
2111
  phase: z.ZodString;
2056
- track: z.ZodString;
2112
+ track: z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>;
2057
2113
  prompt: z.ZodString;
2058
2114
  skills: z.ZodArray<z.ZodString>;
2059
2115
  agents: z.ZodArray<z.ZodString>;
@@ -2092,7 +2148,7 @@ declare const NodePresetCreateSchema: z.ZodObject<{
2092
2148
  project: "project";
2093
2149
  }>;
2094
2150
  projectId: z.ZodOptional<z.ZodString>;
2095
- track: z.ZodString;
2151
+ track: z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>;
2096
2152
  phase: z.ZodString;
2097
2153
  label: z.ZodString;
2098
2154
  prompt: z.ZodString;
@@ -2104,10 +2160,10 @@ declare const NodePresetCreateSchema: z.ZodObject<{
2104
2160
  id: z.ZodString;
2105
2161
  item: z.ZodString;
2106
2162
  }, z.core.$strip>>>;
2163
+ nodeId: z.ZodString;
2107
2164
  source: z.ZodOptional<z.ZodObject<{
2108
2165
  package: z.ZodString;
2109
2166
  }, z.core.$strip>>;
2110
- nodeId: z.ZodString;
2111
2167
  skills: z.ZodDefault<z.ZodArray<z.ZodString>>;
2112
2168
  agents: z.ZodDefault<z.ZodArray<z.ZodString>>;
2113
2169
  version: z.ZodDefault<z.ZodString>;
@@ -2128,7 +2184,7 @@ declare const NodePresetUpdateSchema: z.ZodObject<{
2128
2184
  projectId: z.ZodOptional<z.ZodOptional<z.ZodString>>;
2129
2185
  skills: z.ZodOptional<z.ZodArray<z.ZodString>>;
2130
2186
  agents: z.ZodOptional<z.ZodArray<z.ZodString>>;
2131
- track: z.ZodOptional<z.ZodString>;
2187
+ track: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>>;
2132
2188
  phase: z.ZodOptional<z.ZodString>;
2133
2189
  label: z.ZodOptional<z.ZodString>;
2134
2190
  prompt: z.ZodOptional<z.ZodString>;
@@ -2140,10 +2196,10 @@ declare const NodePresetUpdateSchema: z.ZodObject<{
2140
2196
  id: z.ZodString;
2141
2197
  item: z.ZodString;
2142
2198
  }, z.core.$strip>>>>;
2199
+ nodeId: z.ZodOptional<z.ZodString>;
2143
2200
  source: z.ZodOptional<z.ZodOptional<z.ZodObject<{
2144
2201
  package: z.ZodString;
2145
2202
  }, z.core.$strip>>>;
2146
- nodeId: z.ZodOptional<z.ZodString>;
2147
2203
  version: z.ZodOptional<z.ZodOptional<z.ZodString>>;
2148
2204
  }, z.core.$strip>;
2149
2205
  type NodePresetUpdate = z.infer<typeof NodePresetUpdateSchema>;
@@ -2742,7 +2798,7 @@ declare const TemplatePayloadSchema: z.ZodObject<{
2742
2798
  id: z.ZodString;
2743
2799
  label: z.ZodString;
2744
2800
  phase: z.ZodString;
2745
- track: z.ZodString;
2801
+ track: z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>;
2746
2802
  prompt: z.ZodString;
2747
2803
  skills: z.ZodDefault<z.ZodArray<z.ZodString>>;
2748
2804
  agents: z.ZodOptional<z.ZodArray<z.ZodString>>;
@@ -2850,7 +2906,7 @@ declare const ExportBundleSchema: z.ZodObject<{
2850
2906
  id: z.ZodString;
2851
2907
  label: z.ZodString;
2852
2908
  phase: z.ZodString;
2853
- track: z.ZodString;
2909
+ track: z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>;
2854
2910
  prompt: z.ZodString;
2855
2911
  skills: z.ZodDefault<z.ZodArray<z.ZodString>>;
2856
2912
  agents: z.ZodOptional<z.ZodArray<z.ZodString>>;
@@ -2948,7 +3004,7 @@ declare const ImportPlanRequestSchema: z.ZodObject<{
2948
3004
  id: z.ZodString;
2949
3005
  label: z.ZodString;
2950
3006
  phase: z.ZodString;
2951
- track: z.ZodString;
3007
+ track: z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>;
2952
3008
  prompt: z.ZodString;
2953
3009
  skills: z.ZodDefault<z.ZodArray<z.ZodString>>;
2954
3010
  agents: z.ZodOptional<z.ZodArray<z.ZodString>>;
@@ -3079,7 +3135,7 @@ declare const ImportApplyRequestSchema: z.ZodObject<{
3079
3135
  id: z.ZodString;
3080
3136
  label: z.ZodString;
3081
3137
  phase: z.ZodString;
3082
- track: z.ZodString;
3138
+ track: z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>;
3083
3139
  prompt: z.ZodString;
3084
3140
  skills: z.ZodDefault<z.ZodArray<z.ZodString>>;
3085
3141
  agents: z.ZodOptional<z.ZodArray<z.ZodString>>;
@@ -3268,6 +3324,14 @@ declare const ImportPlanResponseSchema: z.ZodObject<{
3268
3324
  missing: "missing";
3269
3325
  }>;
3270
3326
  }, z.core.$strip>>;
3327
+ directAgentChecks: z.ZodArray<z.ZodObject<{
3328
+ node: z.ZodString;
3329
+ agent: z.ZodString;
3330
+ status: z.ZodEnum<{
3331
+ ok: "ok";
3332
+ missing: "missing";
3333
+ }>;
3334
+ }, z.core.$strip>>;
3271
3335
  warnings: z.ZodArray<z.ZodString>;
3272
3336
  }, z.core.$strip>;
3273
3337
  type ImportPlanResponse = z.infer<typeof ImportPlanResponseSchema>;
@@ -3357,13 +3421,14 @@ declare const TaskPublicSchema: z.ZodObject<{
3357
3421
  status: z.ZodString;
3358
3422
  pausedAt: z.ZodNullable<z.ZodString>;
3359
3423
  track: z.ZodString;
3424
+ trackMatch: z.ZodOptional<z.ZodArray<z.ZodString>>;
3360
3425
  }, z.core.$strip>;
3361
3426
  type TaskPublic = z.infer<typeof TaskPublicSchema>;
3362
3427
  /** N017 F8:删 gates;upcomingPause 语义改为"完成当前节点后出边上的暂停点描述"(经 findNextEdge 按轨选边) */
3363
3428
  declare const NodeInfoSchema: z.ZodObject<{
3364
3429
  nodeId: z.ZodString;
3365
3430
  label: z.ZodString;
3366
- track: z.ZodString;
3431
+ track: z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>;
3367
3432
  prompt: z.ZodString;
3368
3433
  skills: z.ZodArray<z.ZodString>;
3369
3434
  agents: z.ZodArray<z.ZodString>;
@@ -3396,11 +3461,12 @@ declare const AdvanceResponseSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
3396
3461
  status: z.ZodString;
3397
3462
  pausedAt: z.ZodNullable<z.ZodString>;
3398
3463
  track: z.ZodString;
3464
+ trackMatch: z.ZodOptional<z.ZodArray<z.ZodString>>;
3399
3465
  }, z.core.$strip>;
3400
3466
  nextNode: z.ZodObject<{
3401
3467
  nodeId: z.ZodString;
3402
3468
  label: z.ZodString;
3403
- track: z.ZodString;
3469
+ track: z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>;
3404
3470
  prompt: z.ZodString;
3405
3471
  skills: z.ZodArray<z.ZodString>;
3406
3472
  agents: z.ZodArray<z.ZodString>;
@@ -3431,6 +3497,7 @@ declare const AdvanceResponseSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
3431
3497
  status: z.ZodString;
3432
3498
  pausedAt: z.ZodNullable<z.ZodString>;
3433
3499
  track: z.ZodString;
3500
+ trackMatch: z.ZodOptional<z.ZodArray<z.ZodString>>;
3434
3501
  }, z.core.$strip>;
3435
3502
  pausePoint: z.ZodObject<{
3436
3503
  type: z.ZodEnum<{
@@ -3458,6 +3525,7 @@ declare const AdvanceResponseSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
3458
3525
  status: z.ZodString;
3459
3526
  pausedAt: z.ZodNullable<z.ZodString>;
3460
3527
  track: z.ZodString;
3528
+ trackMatch: z.ZodOptional<z.ZodArray<z.ZodString>>;
3461
3529
  }, z.core.$strip>;
3462
3530
  }, z.core.$strip>], "status">;
3463
3531
  type AdvanceResponse = z.infer<typeof AdvanceResponseSchema>;
@@ -3478,11 +3546,12 @@ declare const ApproveResponseSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
3478
3546
  status: z.ZodString;
3479
3547
  pausedAt: z.ZodNullable<z.ZodString>;
3480
3548
  track: z.ZodString;
3549
+ trackMatch: z.ZodOptional<z.ZodArray<z.ZodString>>;
3481
3550
  }, z.core.$strip>;
3482
3551
  nextNode: z.ZodObject<{
3483
3552
  nodeId: z.ZodString;
3484
3553
  label: z.ZodString;
3485
- track: z.ZodString;
3554
+ track: z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>;
3486
3555
  prompt: z.ZodString;
3487
3556
  skills: z.ZodArray<z.ZodString>;
3488
3557
  agents: z.ZodArray<z.ZodString>;
@@ -3513,6 +3582,7 @@ declare const ApproveResponseSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
3513
3582
  status: z.ZodString;
3514
3583
  pausedAt: z.ZodNullable<z.ZodString>;
3515
3584
  track: z.ZodString;
3585
+ trackMatch: z.ZodOptional<z.ZodArray<z.ZodString>>;
3516
3586
  }, z.core.$strip>;
3517
3587
  guidance: z.ZodString;
3518
3588
  }, z.core.$strip>, z.ZodObject<{
@@ -3532,6 +3602,7 @@ declare const ApproveResponseSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
3532
3602
  status: z.ZodString;
3533
3603
  pausedAt: z.ZodNullable<z.ZodString>;
3534
3604
  track: z.ZodString;
3605
+ trackMatch: z.ZodOptional<z.ZodArray<z.ZodString>>;
3535
3606
  }, z.core.$strip>;
3536
3607
  }, z.core.$strip>], "status">;
3537
3608
  type ApproveResponse = z.infer<typeof ApproveResponseSchema>;
@@ -3605,6 +3676,7 @@ declare const TaskProgressPublicSchema: z.ZodObject<{
3605
3676
  status: z.ZodString;
3606
3677
  pausedAt: z.ZodNullable<z.ZodString>;
3607
3678
  track: z.ZodString;
3679
+ trackMatch: z.ZodOptional<z.ZodArray<z.ZodString>>;
3608
3680
  projectId: z.ZodString;
3609
3681
  progress: z.ZodObject<{
3610
3682
  completed: z.ZodNumber;
@@ -3644,6 +3716,7 @@ declare const WriteAckSchema: z.ZodObject<{
3644
3716
  status: z.ZodString;
3645
3717
  pausedAt: z.ZodNullable<z.ZodString>;
3646
3718
  track: z.ZodString;
3719
+ trackMatch: z.ZodOptional<z.ZodArray<z.ZodString>>;
3647
3720
  projectId: z.ZodString;
3648
3721
  progress: z.ZodObject<{
3649
3722
  completed: z.ZodNumber;
@@ -3708,6 +3781,7 @@ declare const TaskBatchAckSchema: z.ZodObject<{
3708
3781
  status: z.ZodString;
3709
3782
  pausedAt: z.ZodNullable<z.ZodString>;
3710
3783
  track: z.ZodString;
3784
+ trackMatch: z.ZodOptional<z.ZodArray<z.ZodString>>;
3711
3785
  projectId: z.ZodString;
3712
3786
  progress: z.ZodObject<{
3713
3787
  completed: z.ZodNumber;
@@ -3734,6 +3808,7 @@ declare const CreateTaskResponseSchema: z.ZodObject<{
3734
3808
  status: z.ZodString;
3735
3809
  pausedAt: z.ZodNullable<z.ZodString>;
3736
3810
  track: z.ZodString;
3811
+ trackMatch: z.ZodOptional<z.ZodArray<z.ZodString>>;
3737
3812
  projectId: z.ZodString;
3738
3813
  progress: z.ZodObject<{
3739
3814
  completed: z.ZodNumber;
@@ -3743,7 +3818,7 @@ declare const CreateTaskResponseSchema: z.ZodObject<{
3743
3818
  firstNode: z.ZodObject<{
3744
3819
  nodeId: z.ZodString;
3745
3820
  label: z.ZodString;
3746
- track: z.ZodString;
3821
+ track: z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>;
3747
3822
  prompt: z.ZodString;
3748
3823
  skills: z.ZodArray<z.ZodString>;
3749
3824
  agents: z.ZodArray<z.ZodString>;
@@ -3908,7 +3983,7 @@ declare function parseAuthEnabledEnvValue(raw: string): boolean | undefined;
3908
3983
  * Domain error types — thrown by core business logic, caught by server's app.onError().
3909
3984
  */
3910
3985
  /** N012 语义化业务错误码(可选挂载到 AppError 子类;HTTP 形状向后兼容:error 字段缺省仍为 http 级 code) */
3911
- type BizCode = 'PROJECT_ARCHIVED' | 'TEMPLATE_PROJECT_MISMATCH' | 'TEMPLATE_PROJECT_IMMUTABLE' | 'SCOPE_IMMUTABLE' | 'TASK_PROJECT_IMMUTABLE' | 'DEFAULT_PROJECT_IMMUTABLE' | 'TEMPLATE_NAME_CONFLICT' | 'TEMPLATE_CODE_CONFLICT' | 'TEMPLATE_CODE_IMMUTABLE' | 'PROJECT_NOT_FOUND' | 'AGENT_SKILL_SCOPE_CONFLICT' | 'MODEL_CODE_EXISTS' | 'MODEL_CODE_IMMUTABLE' | 'MODEL_ALIAS_IN_USE' | 'ENUM_ENTRY_BUILTIN' | 'ENUM_ENTRY_IN_USE' | 'ENUM_VALUE_CONFLICT' | 'NAME_SCOPE_CONFLICT' | 'REFERENCE_IN_USE' | 'TASK_DOC_NOT_SET' | 'TASK_DOC_NOT_FOUND' | 'TASK_DOC_SECTION_NOT_FOUND' | 'TASK_AT_PAUSE_POINT' | 'TASK_NOT_AT_PAUSE_POINT' | 'NODE_NOT_IN_INSTANCE' | 'CHECK_NOT_FOUND' | 'NODE_RECORD_NOT_WRITABLE' | 'TASK_TERMINATED' | 'TASK_NOT_TERMINATED' | 'TASK_FINALIZE_CONFIRMATION_REQUIRED' | 'TASK_PRUNE_GRAPH_INVALID' | 'TASK_SKIP_NODE_NOT_FOUND' | 'TASK_TYPE_TRACK_MISMATCH' | 'TEMPLATE_DISABLED' | 'AUTH_REQUIRED' | 'AUTH_TOKEN_INVALID' | 'AUTH_INVALID_CREDENTIALS' | 'AUTH_FORBIDDEN' | 'AUTH_ACCOUNT_EXISTS' | 'BATCH_EMPTY' | 'BATCH_LIMIT_EXCEEDED' | 'BATCH_PAYLOAD_TOO_LARGE' | 'NODE_PRESET_CODE_EXISTS' | 'NODE_PRESET_CODE_IMMUTABLE' | 'NODE_PRESET_VERSION_REQUIRED' | 'TEMPLATE_NODE_ID_DUPLICATE' | 'NODE_LIBRARY_COMPOSITION_NOT_FOUND' | 'UPGRADE_DECISION_REQUIRED' | 'UPGRADE_STALE' | 'FLOW_MANIFEST_INVALID' | 'ISSUE_NOT_FOUND' | 'ISSUE_INVALID_TRANSITION' | 'ISSUE_PROJECT_MISMATCH' | 'ISSUE_BATCH_LIMIT_EXCEEDED';
3986
+ type BizCode = 'PROJECT_ARCHIVED' | 'TEMPLATE_PROJECT_MISMATCH' | 'TEMPLATE_PROJECT_IMMUTABLE' | 'SCOPE_IMMUTABLE' | 'TASK_PROJECT_IMMUTABLE' | 'DEFAULT_PROJECT_IMMUTABLE' | 'TEMPLATE_NAME_CONFLICT' | 'TEMPLATE_CODE_CONFLICT' | 'TEMPLATE_CODE_IMMUTABLE' | 'PROJECT_NOT_FOUND' | 'AGENT_SKILL_SCOPE_CONFLICT' | 'MODEL_CODE_EXISTS' | 'MODEL_CODE_IMMUTABLE' | 'MODEL_ALIAS_IN_USE' | 'ENUM_ENTRY_BUILTIN' | 'ENUM_ENTRY_IN_USE' | 'ENUM_VALUE_CONFLICT' | 'NAME_SCOPE_CONFLICT' | 'REFERENCE_IN_USE' | 'TASK_DOC_NOT_SET' | 'TASK_DOC_NOT_FOUND' | 'TASK_DOC_SECTION_NOT_FOUND' | 'TASK_AT_PAUSE_POINT' | 'TASK_NOT_AT_PAUSE_POINT' | 'NODE_NOT_IN_INSTANCE' | 'CHECK_NOT_FOUND' | 'NODE_RECORD_NOT_WRITABLE' | 'TASK_TERMINATED' | 'TASK_NOT_TERMINATED' | 'TASK_FINALIZE_CONFIRMATION_REQUIRED' | 'TASK_PRUNE_GRAPH_INVALID' | 'TASK_SKIP_NODE_NOT_FOUND' | 'TASK_TYPE_TRACK_MISMATCH' | 'TASK_TRACK_MATCH_MISSING' | 'TEMPLATE_DISABLED' | 'AUTH_REQUIRED' | 'AUTH_TOKEN_INVALID' | 'AUTH_INVALID_CREDENTIALS' | 'AUTH_FORBIDDEN' | 'AUTH_ACCOUNT_EXISTS' | 'BATCH_EMPTY' | 'BATCH_LIMIT_EXCEEDED' | 'BATCH_PAYLOAD_TOO_LARGE' | 'NODE_PRESET_CODE_EXISTS' | 'NODE_PRESET_CODE_IMMUTABLE' | 'NODE_PRESET_VERSION_REQUIRED' | 'TEMPLATE_NODE_ID_DUPLICATE' | 'TEMPLATE_SAME_TRACK_FORK' | 'NODE_LIBRARY_COMPOSITION_NOT_FOUND' | 'UPGRADE_DECISION_REQUIRED' | 'UPGRADE_STALE' | 'FLOW_MANIFEST_INVALID' | 'ISSUE_NOT_FOUND' | 'ISSUE_INVALID_TRANSITION' | 'ISSUE_PROJECT_MISMATCH' | 'ISSUE_BATCH_LIMIT_EXCEEDED';
3912
3987
  /** bizCode → 用户可读中文 message(单一真相源,路由层抛错时引用) */
3913
3988
  declare const BIZ_CODE_MESSAGES: Record<BizCode, string>;
3914
3989
  interface AppErrorOptions {
@@ -4281,22 +4356,27 @@ declare function derivePreTerminalState(history: HistoryEntry[]): {
4281
4356
  /**
4282
4357
  * @internal 供单测直接验证的模块私有 helper(非公共 API)
4283
4358
  * N017:findNextNode → findNextEdge——返回边对象(调用方需要 pausePoint)
4284
- * T202608240003:单轨匹配扩展为匹配集(mixed → {backend,ui,all},trackMatchSet 单源);
4359
+ * T202608240003:单轨匹配扩展为匹配集(matchSetOf 单源);
4360
+ * T202610120005:第 4 参 track:string → matchSet:ReadonlySet<string>(调用方经
4361
+ * matchSetOf(task.trackMatch) 构造——快照单源,函数自身不再查 track-match 表);
4362
+ * 节点轨道 union 形态(R7 多轨节点):匹配判定与消歧分组均按「有效轨集合」
4363
+ * (effectiveTracks = 节点轨道 ∩ matchSet;数组含 'all' 成员等价 {'all'} 全轨)。
4285
4364
  * 多匹配消歧(判定顺序固定,防止 D6 保护成为不可达分支):
4286
- * a. 按目标 track 值分组查重——任一分组 > 1 条 → D6 报错(模板同轨分叉)
4287
- * b. 存在专属候选边(目标 track ≠ 'all')→ 全部专属候选中按模板边声明序取第一条
4365
+ * a. 按目标有效轨集合分组查重——任一分组 > 1 条 → D6 报错(模板同轨分叉;
4366
+ * 多轨节点与同集合字符串节点同键参与查重)
4367
+ * b. 存在专属候选边(有效轨集合 ≠ {'all'})→ 全部专属候选中按模板边声明序取第一条
4288
4368
  * c. 无专属候选 → 取 all 边(唯一性已由 a 的分组查重保证)
4289
4369
  * 单轨任务匹配集合与旧逻辑逐字等价({track,'all'})且无多匹配出边 → 行为不变(兼容)。
4290
4370
  * 注意:消歧对单轨同样生效——「同轨专属边 + all 边」混合多匹配时取专属边(D4 意图),
4291
- * 仅「同一 track 值多条边」维持 D6 报错(真分叉模板错误)。
4371
+ * 仅「同一有效轨集合多条边」维持 D6 报错(真分叉模板错误)。
4292
4372
  */
4293
- declare function findNextEdge(edges: DagEdge[], nodes: Pick<DagNode, 'id' | 'track'>[], fromNodeId: string, track: string): DagEdge | null;
4373
+ declare function findNextEdge(edges: DagEdge[], nodes: Pick<DagNode, 'id' | 'track'>[], fromNodeId: string, matchSet: ReadonlySet<string>): DagEdge | null;
4294
4374
  /** @internal 供单测直接验证的模块私有 helper(非公共 API) */
4295
4375
  declare function checkTermination(nodes: DagNode[], edges: {
4296
4376
  from: string;
4297
4377
  to: string;
4298
4378
  condition?: string | undefined;
4299
- }[], nodeStates: Record<string, NodeState>, upcomingCompletedState: NodeState, upcomingCompletedNodeId: string, track: string): boolean;
4379
+ }[], nodeStates: Record<string, NodeState>, upcomingCompletedState: NodeState, upcomingCompletedNodeId: string, matchSet: ReadonlySet<string>): boolean;
4300
4380
  /** @internal 供单测直接验证的模块私有 helper(非公共 API) */
4301
4381
  declare function toTaskPublic(task: Task): TaskPublic;
4302
4382
  /**
@@ -4325,28 +4405,115 @@ interface PruneResult {
4325
4405
  prunedNodes: string[];
4326
4406
  entryNode: DagNode;
4327
4407
  }
4408
+ /** 剪枝核心产物:保留节点 + 保留边(含桥接边) */
4409
+ interface PrunedGraph {
4410
+ nodes: DagNode[];
4411
+ edges: DagEdge[];
4412
+ }
4413
+ /**
4414
+ * 剪枝核心(轨道剔除 ∪ 显式剔除 → 删边 → 桥接)——pruneInstance 与
4415
+ * track-chains 链序模拟共享的单源实现(提取而非复制,防两处口径漂移)。
4416
+ *
4417
+ * T202610120005:第 2 参 taskTrack:string → trackMembers:readonly string[]——
4418
+ * 任务轨道成员快照(matchSetOf 构造匹配集 = 成员并集 + 'all');节点保留判定
4419
+ * 走 trackIntersects 交集语义(R7 多轨节点任一成员被覆盖即保留)。
4420
+ *
4421
+ * 不含图校验/引擎路径模拟(那是 pruneInstance 创建期职责);桥接路径断裂时
4422
+ * 返回 ok:false + reason(调用方各自决定 throw 或描述性输出),其余恒成功。
4423
+ */
4424
+ declare function buildPrunedGraph(template: Pick<DagTemplate, 'nodes' | 'edges'>, trackMembers: readonly string[], skipNodeIds?: readonly string[]): {
4425
+ ok: true;
4426
+ graph: PrunedGraph;
4427
+ } | {
4428
+ ok: false;
4429
+ reason: string;
4430
+ };
4328
4431
  /**
4329
4432
  * 剪枝主函数(技术方案 §2.2 六步)。
4330
4433
  *
4331
4434
  * @param template 模板全量快照(nodes/edges)
4332
- * @param taskTrack 任务轨道(注册表合法值,含 mixed/research)
4435
+ * @param trackMembers 任务轨道成员快照(T202610120005:组合口径——创建入口归一化产物,
4436
+ * 通配(all/mixed 历史)已展开为成员清单;matchSetOf 单源构造匹配集)
4333
4437
  * @param skipNodeIds 显式剔除清单(skill 映射矩阵产物)
4334
4438
  */
4335
- declare function pruneInstance(template: DagTemplate, taskTrack: string, skipNodeIds: string[]): PruneResult;
4439
+ declare function pruneInstance(template: DagTemplate, trackMembers: readonly string[], skipNodeIds: string[]): PruneResult;
4336
4440
 
4337
4441
  /**
4338
- * T202608240003:任务轨道 → 实例节点 track 匹配集。
4442
+ * 任务轨道匹配集单源。
4443
+ * T202610120005:词表自由组合改造——轨道词表(枚举注册表 dag_track)只管单轨,
4444
+ * 任务轨道 = 词表成员的组合(创建时解析冻结进 task.trackMatch 快照,运行时
4445
+ * 路由/剪枝/终态判定只读快照不回查注册表——升版/停用不影响在途任务)。
4446
+ * all = 任务级通配(验收反馈 2026-10-13,mixed 退役后由 all 承载):创建时展开为
4447
+ * 全部 active 节点级单轨冻结(存量任务快照自治——历史 mixed 任务迁移为旧义
4448
+ * [backend,ui]、历史 mixed 锚任务展示走含停用 label 命中,引擎无差别消费快照)。
4339
4449
  * 独立单源模块——prune(剪枝轨道剔除 + 引擎路径可达性校验)与 advance-engine
4340
4450
  * (流转路由 + 终止判定)消费同一语义,防止剪枝与路由口径漂移。
4341
- * T202608280004:web dag-editor validate 规则 4 持有本函数的本地镜像副本——
4342
- * 本函数语义演进须同步该副本。
4451
+ * T202608280004:web dag-editor validate 规则 4 持有本模块的本地镜像副本——
4452
+ * 本模块语义演进须同步该副本。
4453
+ */
4454
+ /**
4455
+ * 任务轨道成员快照 → 匹配集(成员并集 + 'all' 哨兵——all 是节点级哨兵,
4456
+ * 任何任务匹配集恒含它;PRD A2 沿用)。
4457
+ * members 为空数组 → fail-loud(任务缺轨道匹配快照,疑似未迁移或脏数据,
4458
+ * 禁止静默兜底成空集——空集会剪空整个实例图)。
4459
+ */
4460
+ declare function matchSetOf(members: readonly string[] | undefined): ReadonlySet<string>;
4461
+ /**
4462
+ * 节点轨道 ∩ 任务匹配集 → 有效轨集合(消歧分组 / 专属边判定用)。
4463
+ * 节点轨道是 union 形态(T202610120005 R7:字符串=单轨,数组=多轨节点):
4464
+ * - 字符串 'all' 或数组含 'all' 成员 → {'all'}(全轨节点,等价哨兵形态)
4465
+ * - 其余 = 数组去重 / 字符串单值后,过滤 ∈ matchSet 的成员
4466
+ * 返回空集 = 节点与任务轨道无交集(剪枝剔除)。
4343
4467
  */
4468
+ declare function effectiveTracks(nodeTrack: string | string[], matchSet: ReadonlySet<string>): ReadonlySet<string>;
4469
+ /**
4470
+ * 节点保留判定:有效轨集合非空(R7 交集语义——多轨节点任一成员被任务覆盖即保留;
4471
+ * 单轨节点等价旧 matchSet.has(track))。
4472
+ */
4473
+ declare function trackIntersects(nodeTrack: string | string[], matchSet: ReadonlySet<string>): boolean;
4474
+
4475
+ /**
4476
+ * T202610120004 A2:模板链序模拟——按任务轨道视角模拟「剪枝(含桥接)→ 引擎逐跳路由」,
4477
+ * 输出各轨道的节点链序(entry → … → terminal),供 import --plan / 导入回执展示。
4478
+ *
4479
+ * 与任务创建期 pruneInstance 的差异(刻意):
4480
+ * - 不做图完整性断言(节点≥2/可达性/terminal 等)——本模拟是「引擎会走什么」的
4481
+ * 描述性展示,不是创建期守卫;不可服务的轨道以 error 字段输出原因
4482
+ * - 只读纯函数:永不 throw(轨道内部错误捕获为 error 项),可安全用于展示层
4483
+ * 入口判定与 pruneInstance 同口径(剪后入度 0)——phase 是节点阶段类别标签,
4484
+ * 生产模板中 'entry' 类别可多点(PRD/ALIGN/DESIGN 并存),phase 判定会对合法
4485
+ * 模板输出多入口噪音(V6 实测暴露),入度判定才是引擎真实起点。
4486
+ */
4487
+ /** 单轨道链序模拟结果(error 存在 = 该轨不可服务,chain 为空) */
4488
+ interface TrackChainResult {
4489
+ /** 任务轨道(backend/ui/组合视角如 backend+ui/自定义) */
4490
+ track: string;
4491
+ /** 引擎路径节点 id 序列(entry 开头;不可服务时为空数组) */
4492
+ chain: string[];
4493
+ /** 不可服务原因(剪枝断裂/入口缺失/路由消歧失败/环) */
4494
+ error?: string;
4495
+ }
4496
+ /**
4497
+ * 模拟入口(纯函数)。轨道集合口径:模板节点轨道出现的非 'all' track 全集
4498
+ * (T202610120005 R7:多轨节点逐成员贡献;含 'all' 成员等价全轨节点不贡献),
4499
+ * backend+ui 双轨节点齐备时追加组合视角 'backend+ui'(双链串行模拟,T202610120005
4500
+ * 验收反馈:mixed 退役,组合串即模拟轨名,'+' 分隔逐段解析)。
4501
+ */
4502
+ declare function simulateTrackChains(template: Pick<DagTemplate, 'nodes' | 'edges'>): TrackChainResult[];
4503
+ /** 结构约束条目(rule=规则陈述;正反例为可执行对照——反例必须被 assertNoSameTrackFork 拒绝) */
4504
+ interface TemplateStructureConstraint {
4505
+ id: string;
4506
+ title: string;
4507
+ rule: string;
4508
+ positiveExample: string;
4509
+ negativeExample: string;
4510
+ }
4344
4511
  /**
4345
- * - backend/ui/research:匹配本轨道节点 + 'all' 哨兵
4346
- * - mixed:匹配双轨道节点 + 'all'(两链串行,链序由引擎消歧的模板边声明序决定)
4347
- * - 自定义轨道(注册表运行时权威):匹配本轨道 + 'all'(机械剪枝,语义自洽)
4512
+ * 模板结构约束速查(4 条)——解决「配置者只能读源码才知道模板怎么写」的问题。
4513
+ * 文案与 findSameTrackForks / findNextEdge / buildPrunedGraph 桥接语义同源,
4514
+ * 改动引擎语义时同步更新本表(V5 验收:反例必须被 A1 断言拒绝)。
4348
4515
  */
4349
- declare function trackMatchSet(taskTrack: string): ReadonlySet<string>;
4516
+ declare const TEMPLATE_STRUCTURE_CONSTRAINTS: readonly TemplateStructureConstraint[];
4350
4517
 
4351
4518
  /**
4352
4519
  * N020 D1:条目短 id 生成(服务端权威,客户端永不指定)。
@@ -4376,7 +4543,9 @@ interface TemplateTransferDeps {
4376
4543
  /**
4377
4544
  * 导出依赖闭包推导(T202608300002 §2.2,PRD 3.1 规则 1-4,一跳封闭):
4378
4545
  * 1. SKILL 直接引用集 = 模板所有节点 skills 字段(去重)
4379
- * 2. AGENT 集 = boundSkills 与直接引用集有交集的 agent(查询范围:global 全集 + 源项目 project 全集)
4546
+ * 2. AGENT 集 = boundSkills 与直接引用集有交集的 agent ∪ 节点 agents 直接引用的 agent
4547
+ * (T202610120001:节点可不经 skill 绑定直接引用 agent——仅按 boundSkills 反查会整层漏收;
4548
+ * 查询范围:global 全集 + 源项目 project 全集)
4380
4549
  * 3. SKILL 全集 = 直接引用 ∪ 被导出 AGENT 的 boundSkills(一跳封闭——新增 SKILL 不反查更多 AGENT)
4381
4550
  * 4. 模型映射集 = 被导出 AGENT 的 model code 对应别名
4382
4551
  *
@@ -4414,4 +4583,4 @@ declare function applyImport(deps: TemplateTransferDeps, targetProjectId: string
4414
4583
  declare const VERSION: string;
4415
4584
  declare function ping(): string;
4416
4585
 
4417
- export { AGENT_MAIN_DOC, ARTIFACT_TYPES, AUTH_COLLECTIONS, type AdvanceDeps, type AdvanceInput, type AdvanceRequest, AdvanceRequestSchema, type AdvanceResponse, AdvanceResponseSchema, type Agent, type AgentCopy, AgentCopySchema, type AgentCreate, AgentCreateSchema, type AgentFunction, AgentFunctionSchema, type AgentPayload, AgentPayloadSchema, type AgentRepo, AgentSchema, type AgentScope, AgentScopeSchema, AgentShapeSchema, type AgentUpdate, AgentUpdateSchema, type AiCheck, AiCheckSchema, AppError, type AppErrorOptions, type ApproveRequest, ApproveRequestSchema, type ApproveResponse, ApproveResponseSchema, type ArchNote, ArchNoteSchema, type Artifact, ArtifactSchema, type ArtifactType, type AssetListOpts, type AssetSetEnabled, AssetSetEnabledSchema, type AuthAccount, type AuthAccountCreate, AuthAccountCreateSchema, AuthAccountSchema, type AuthSession, type AuthStatus, AuthStatusSchema, type AuthToken, type AuthTokenGenResponse, AuthTokenGenResponseSchema, type AuthTokenKind, AuthTokenSchema, type AuthTokenStatus, AuthTokenStatusSchema, type AuthTokenType, AuthTokenTypeSchema, type AuthWhoami, AuthWhoamiSchema, BIZ_CODE_MESSAGES, BadRequestError, type BizCode, CONTEXT_VIEWS, type CancelRequest, CancelRequestSchema, type CheckItem, CheckItemSchema, type Composition, type CompositionEdge, CompositionEdgeSchema, CompositionSchema, type Confirmation, ConfirmationSchema, ConflictError, type ContextView, ContextViewSchema, type ConvertGroupOutcome, type CreateTaskResponse, CreateTaskResponseSchema, DAG_PHASES, DAG_TRACKS, DEFAULT_PROJECT_KEY, DEFAULT_VCS_DOC_PATHS, type DagEdge, DagEdgeSchema, type DagInstance, type DagInstanceNode, DagInstanceNodeSchema, DagInstanceSchema, type DagNode, DagNodePhaseSchema, DagNodeSchema, DagNodeTrackSchema, type DagTemplate, type DagTemplateCopy, DagTemplateCopySchema, type DagTemplateCreate, DagTemplateCreateSchema, type DagTemplateRepo, DagTemplateSchema, type DagTemplateUpdate, DagTemplateUpdateSchema, type Decision, DecisionSchema, type DependencyAction, DependencyActionSchema, type DependencyCheck, DependencyCheckSchema, type DependencyStatus, DependencyStatusSchema, ENTRY_ID_REGEX, EXPORT_FORMAT_VERSION, type EchoItem, EchoItemSchema, type EdgePausePoint, EdgePausePointBaseSchema, EdgePausePointSchema, type EnumEntry, EnumEntrySchema, type EnumRegistry, type EnumRegistryCategory, EnumRegistryCategorySchema, type EnumRegistryRepo, EnumRegistrySchema, type EnumRegistryUpdate, EnumRegistryUpdateSchema, type ExportBundle, ExportBundleSchema, ForbiddenError, HistoryActionSchema, type HistoryEntry, HistoryEntrySchema, ISSUE_ACTIVE_STATUSES, ISSUE_CONVERT_MODES, ISSUE_SOURCES, ISSUE_STATUSES, type ImportApplyRequest, ImportApplyRequestSchema, type ImportApplyResponse, ImportApplyResponseSchema, type ImportDecision, ImportDecisionSchema, type ImportPlanRequest, ImportPlanRequestSchema, type ImportPlanResponse, ImportPlanResponseSchema, type Issue, type IssueConvertMode, IssueConvertModeSchema, type IssueCreate, IssueCreateSchema, type IssueHistoryEntry, IssueHistoryEntrySchema, type IssueListFilter, type IssueProgress, IssueProgressSchema, type IssueRepo, IssueSchema, type IssueSource, IssueSourceSchema, type IssueStatus, type IssueStatusChange, IssueStatusSchema, type IssueTaskSnapshot, type IssueUpdate, IssueUpdateSchema, type IssueWithProgress, type LoginInput, LoginSchema, type ModelAlias, type ModelAliasCreate, ModelAliasCreateSchema, type ModelAliasPayload, ModelAliasPayloadSchema, type ModelAliasRepo, ModelAliasSchema, ModelAliasShapeSchema, type ModelAliasUpdate, ModelAliasUpdateSchema, type ModelAliasWithRefCount, ModelAliasWithRefCountSchema, NODE_CHECKS_MAX, NODE_ID_PATTERN, NODE_PRESET_CODE_PATTERN, NODE_STATUSES, type NodeContentFields, type NodeInfo, NodeInfoSchema, type NodeLibrary, type NodeLibraryRepo, NodeLibrarySchema, type NodeLibraryUpsert, NodeLibraryUpsertSchema, type NodePreset, type NodePresetCopy, NodePresetCopySchema, type NodePresetCreate, NodePresetCreateSchema, type NodePresetListFilter, type NodePresetRepo, NodePresetSchema, NodePresetShapeSchema, type NodePresetUpdate, NodePresetUpdateSchema, type NodeRecord, NodeRecordSchema, type NodeState, NodeStateSchema, NodeStatusSchema, NotFoundError, OBJECT_ID_HEX, type OmitId, PAUSE_POINT_TYPES, type PasswordChange, PasswordChangeSchema, PausePointTypeSchema, type PauseRequest, PauseRequestSchema, type Project, type ProjectCreate, ProjectCreateSchema, type ProjectRepo, ProjectSchema, type ProjectStatus, ProjectStatusSchema, type ProjectUpdate, ProjectUpdateSchema, type PruneResult, REFERENCE_LIMITS, type ReferenceDoc, type ReopenInput, type ReopenRequest, ReopenRequestSchema, type ReopenResponse, type Repo, type ResumeRequest, ResumeRequestSchema, type ReviewSummary, ReviewSummarySchema, SIMING_CODE_REGEX, SIMING_CONFIG_ENV_KEYS, SIMING_CONFIG_KEYS, SKILL_MAIN_DOC, type SimingClient, type SimingConfig, type SimingConfigKey, SimingConfigSchema, SimingLogLevelSchema, type Skill, type SkillCopy, SkillCopySchema, type SkillCreate, SkillCreateSchema, type SkillPayload, SkillPayloadSchema, type SkillRepo, SkillSchema, type SkillScope, SkillScopeSchema, SkillShapeSchema, type SkillUpdate, SkillUpdateSchema, type SourcePreset, SourcePresetSchema, TASK_PHASES, TASK_STATUSES, TASK_TYPES, TEMPLATE_CODE_MAX_LENGTH, TEMPLATE_CODE_REGEX, type Task, type TaskBatchAck, TaskBatchAckSchema, type TaskBatchEntry, TaskBatchEntrySchema, type TaskBatchFailure, TaskBatchFailureSchema, type TaskContext, TaskContextSchema, type TaskCreateInput, TaskCreateInputSchema, type TaskDocContent, TaskDocContentSchema, type TaskIssueView, type TaskLink, TaskLinkSchema, type TaskPatch, TaskPhaseSchema, type TaskProgress, type TaskProgressPublic, TaskProgressPublicSchema, TaskProgressSchema, type TaskPublic, TaskPublicSchema, type TaskRepo, TaskSchema, TaskStatusSchema, type TaskSummary, TaskSummarySchema, type TaskType, type TemplateCandidate, TemplateCandidateSchema, TemplateCodeFieldSchema, type TemplateContentChanges, type TemplateContentDiff, type TemplateContentSource, type TemplatePayload, TemplatePayloadSchema, type TemplateTransferDeps, type TestReport, TestReportSchema, type ToolCheck, ToolCheckSchema, UnauthorizedError, type UpgradeAction, UpgradeActionSchema, type UpgradeApplyOutcome, type UpgradeApplyRequest, UpgradeApplyRequestSchema, type UpgradeApplyResultItem, type UpgradeDecision, type UpgradeDecisionInput, UpgradeDecisionSchema, type UpgradeNodeStatus, UpgradeNodeStatusSchema, type UpgradePlanItem, type UpgradePlanResult, VERSION, ValidationError, type VcsConfig, VcsConfigSchema, WRITE_ACK_ENTRY_KINDS, type WithId, type WriteAck, type WriteAckEntryKind, WriteAckEntryKindSchema, WriteAckSchema, advanceTask, applyImport, applyUpgradeDecisions, approveTask, assertBoundSkillsCompatible, assertModelAliasExists, assertReferenceAggregateLimit, assertReferencesDeletable, assertUniqueAiCheckIds, assertUniqueNodeIds, buildImportPlan, buildTemplateCode, buildUpgradePlan, bumpPatch, bumpPatchOrPassthrough, cancelTask, canonicalizeReferencePath, checkTermination, composeExportBundle, computeNodeContentHash, computeUpgradeStatus, createAgentRepo, createAuthAccountRepo, createAuthSessionRepo, createAuthTokenRepo, createDagTemplateRepo, createEnumRegistryRepo, createIssueRepo, createModelAliasRepo, createMongoClient, createNodeLibraryRepo, createNodePresetRepo, createProjectRepo, createReferencesSchema, createRepo, createSkillRepo, createTaskRepo, decisionKey, derivePreTerminalState, diffTemplateContent, enabledNeFalse, escapeRegExpLiteral, excerpt, findDuplicateNodeIds, findNextEdge, findTemplateByName, generateEntryId, generateProjectKey, generateSessionId, generateToken, hasProjectDefault, hashPassword, hashToken, isLegalTemplateCode, mkHistory, normalizeNameToTemplateCode, parseAuthEnabledEnvValue, pauseTask, ping, pruneInstance, renderPrompt, reopenTask, resumeTask, sumReferenceContentChars, taskProgress, toNodeInfo, toTaskPublic, tokenTypeFromValue, trackMatchSet, verifyPassword, withTransaction };
4586
+ export { AGENT_MAIN_DOC, ARTIFACT_TYPES, AUTH_COLLECTIONS, type AdvanceDeps, type AdvanceInput, type AdvanceRequest, AdvanceRequestSchema, type AdvanceResponse, AdvanceResponseSchema, type Agent, type AgentCopy, AgentCopySchema, type AgentCreate, AgentCreateSchema, type AgentFunction, AgentFunctionSchema, type AgentPayload, AgentPayloadSchema, type AgentRepo, AgentSchema, type AgentScope, AgentScopeSchema, AgentShapeSchema, type AgentUpdate, AgentUpdateSchema, type AiCheck, AiCheckSchema, AppError, type AppErrorOptions, type ApproveRequest, ApproveRequestSchema, type ApproveResponse, ApproveResponseSchema, type ArchNote, ArchNoteSchema, type Artifact, ArtifactSchema, type ArtifactType, type AssetListOpts, type AssetSetEnabled, AssetSetEnabledSchema, type AuthAccount, type AuthAccountCreate, AuthAccountCreateSchema, AuthAccountSchema, type AuthSession, type AuthStatus, AuthStatusSchema, type AuthToken, type AuthTokenGenResponse, AuthTokenGenResponseSchema, type AuthTokenKind, AuthTokenSchema, type AuthTokenStatus, AuthTokenStatusSchema, type AuthTokenType, AuthTokenTypeSchema, type AuthWhoami, AuthWhoamiSchema, BIZ_CODE_MESSAGES, BadRequestError, type BizCode, CONTEXT_VIEWS, type CancelRequest, CancelRequestSchema, type CheckItem, CheckItemSchema, type Composition, type CompositionEdge, CompositionEdgeSchema, CompositionSchema, type Confirmation, ConfirmationSchema, ConflictError, type ContextView, ContextViewSchema, type ConvertGroupOutcome, type CreateTaskResponse, CreateTaskResponseSchema, DAG_PHASES, DAG_TRACKS, DEFAULT_PROJECT_KEY, DEFAULT_VCS_DOC_PATHS, type DagEdge, DagEdgeSchema, type DagInstance, type DagInstanceNode, DagInstanceNodeSchema, DagInstanceSchema, type DagNode, DagNodePhaseSchema, DagNodeSchema, DagNodeTrackSchema, type DagTemplate, type DagTemplateCopy, DagTemplateCopySchema, type DagTemplateCreate, DagTemplateCreateSchema, type DagTemplateRepo, DagTemplateSchema, type DagTemplateUpdate, DagTemplateUpdateSchema, type Decision, DecisionSchema, type DependencyAction, DependencyActionSchema, type DependencyCheck, DependencyCheckSchema, type DependencyStatus, DependencyStatusSchema, ENTRY_ID_REGEX, EXPORT_FORMAT_VERSION, type EchoItem, EchoItemSchema, type EdgePausePoint, EdgePausePointBaseSchema, EdgePausePointSchema, type EnumEntry, EnumEntrySchema, type EnumRegistry, type EnumRegistryCategory, EnumRegistryCategorySchema, type EnumRegistryRepo, EnumRegistrySchema, type EnumRegistryUpdate, EnumRegistryUpdateSchema, type ExportBundle, ExportBundleSchema, ForbiddenError, HistoryActionSchema, type HistoryEntry, HistoryEntrySchema, ISSUE_ACTIVE_STATUSES, ISSUE_CONVERT_MODES, ISSUE_SOURCES, ISSUE_STATUSES, type ImportApplyRequest, ImportApplyRequestSchema, type ImportApplyResponse, ImportApplyResponseSchema, type ImportDecision, ImportDecisionSchema, type ImportPlanRequest, ImportPlanRequestSchema, type ImportPlanResponse, ImportPlanResponseSchema, type Issue, type IssueConvertMode, IssueConvertModeSchema, type IssueCreate, IssueCreateSchema, type IssueHistoryEntry, IssueHistoryEntrySchema, type IssueListFilter, type IssueProgress, IssueProgressSchema, type IssueRepo, IssueSchema, type IssueSource, IssueSourceSchema, type IssueStatus, type IssueStatusChange, IssueStatusSchema, type IssueTaskSnapshot, type IssueUpdate, IssueUpdateSchema, type IssueWithProgress, type LoginInput, LoginSchema, type ModelAlias, type ModelAliasCreate, ModelAliasCreateSchema, type ModelAliasPayload, ModelAliasPayloadSchema, type ModelAliasRepo, ModelAliasSchema, ModelAliasShapeSchema, type ModelAliasUpdate, ModelAliasUpdateSchema, type ModelAliasWithRefCount, ModelAliasWithRefCountSchema, NODE_CHECKS_MAX, NODE_ID_PATTERN, NODE_PRESET_CODE_PATTERN, NODE_STATUSES, type NodeContentFields, type NodeInfo, NodeInfoSchema, type NodeLibrary, type NodeLibraryRepo, NodeLibrarySchema, type NodeLibraryUpsert, NodeLibraryUpsertSchema, type NodePreset, type NodePresetCopy, NodePresetCopySchema, type NodePresetCreate, NodePresetCreateSchema, type NodePresetListFilter, type NodePresetRepo, NodePresetSchema, NodePresetShapeSchema, type NodePresetUpdate, NodePresetUpdateSchema, type NodeRecord, NodeRecordSchema, type NodeState, NodeStateSchema, NodeStatusSchema, NotFoundError, OBJECT_ID_HEX, type OmitId, PAUSE_POINT_TYPES, type PasswordChange, PasswordChangeSchema, PausePointTypeSchema, type PauseRequest, PauseRequestSchema, type Project, type ProjectCreate, ProjectCreateSchema, type ProjectRepo, ProjectSchema, type ProjectStatus, ProjectStatusSchema, type ProjectUpdate, ProjectUpdateSchema, type PruneResult, type PrunedGraph, REFERENCE_LIMITS, type ReferenceDoc, type ReopenInput, type ReopenRequest, ReopenRequestSchema, type ReopenResponse, type Repo, type ResumeRequest, ResumeRequestSchema, type ReviewSummary, ReviewSummarySchema, SIMING_CODE_REGEX, SIMING_CONFIG_ENV_KEYS, SIMING_CONFIG_KEYS, SKILL_MAIN_DOC, type SameTrackForkIssue, type SimingClient, type SimingConfig, type SimingConfigKey, SimingConfigSchema, SimingLogLevelSchema, type Skill, type SkillCopy, SkillCopySchema, type SkillCreate, SkillCreateSchema, type SkillPayload, SkillPayloadSchema, type SkillRepo, SkillSchema, type SkillScope, SkillScopeSchema, SkillShapeSchema, type SkillUpdate, SkillUpdateSchema, type SourcePreset, SourcePresetSchema, TASK_PHASES, TASK_STATUSES, TASK_TYPES, TEMPLATE_CODE_MAX_LENGTH, TEMPLATE_CODE_REGEX, TEMPLATE_STRUCTURE_CONSTRAINTS, type Task, type TaskBatchAck, TaskBatchAckSchema, type TaskBatchEntry, TaskBatchEntrySchema, type TaskBatchFailure, TaskBatchFailureSchema, type TaskContext, TaskContextSchema, type TaskCreateInput, TaskCreateInputSchema, type TaskDocContent, TaskDocContentSchema, type TaskIssueView, type TaskLink, TaskLinkSchema, type TaskPatch, TaskPhaseSchema, type TaskProgress, type TaskProgressPublic, TaskProgressPublicSchema, TaskProgressSchema, type TaskPublic, TaskPublicSchema, type TaskRepo, TaskSchema, TaskStatusSchema, type TaskSummary, TaskSummarySchema, type TaskType, type TemplateCandidate, TemplateCandidateSchema, TemplateCodeFieldSchema, type TemplateContentChanges, type TemplateContentDiff, type TemplateContentSource, type TemplatePayload, TemplatePayloadSchema, type TemplateStructureConstraint, type TemplateTransferDeps, type TestReport, TestReportSchema, type ToolCheck, ToolCheckSchema, type TrackChainResult, UnauthorizedError, type UpgradeAction, UpgradeActionSchema, type UpgradeApplyOutcome, type UpgradeApplyRequest, UpgradeApplyRequestSchema, type UpgradeApplyResultItem, type UpgradeDecision, type UpgradeDecisionInput, UpgradeDecisionSchema, type UpgradeNodeStatus, UpgradeNodeStatusSchema, type UpgradePlanItem, type UpgradePlanResult, VERSION, ValidationError, type VcsConfig, VcsConfigSchema, WRITE_ACK_ENTRY_KINDS, type WithId, type WriteAck, type WriteAckEntryKind, WriteAckEntryKindSchema, WriteAckSchema, advanceTask, applyImport, applyUpgradeDecisions, approveTask, assertBoundSkillsCompatible, assertModelAliasExists, assertNoSameTrackFork, assertReferenceAggregateLimit, assertReferencesDeletable, assertUniqueAiCheckIds, assertUniqueNodeIds, buildImportPlan, buildPrunedGraph, buildTemplateCode, buildUpgradePlan, bumpPatch, bumpPatchOrPassthrough, cancelTask, canonicalizeReferencePath, checkTermination, composeExportBundle, computeNodeContentHash, computeUpgradeStatus, createAgentRepo, createAuthAccountRepo, createAuthSessionRepo, createAuthTokenRepo, createDagTemplateRepo, createEnumRegistryRepo, createIssueRepo, createModelAliasRepo, createMongoClient, createNodeLibraryRepo, createNodePresetRepo, createProjectRepo, createReferencesSchema, createRepo, createSkillRepo, createTaskRepo, decisionKey, derivePreTerminalState, diffTemplateContent, effectiveTracks, enabledNeFalse, escapeRegExpLiteral, excerpt, findDuplicateNodeIds, findNextEdge, findSameTrackForks, findTemplateByName, generateEntryId, generateProjectKey, generateSessionId, generateToken, hasProjectDefault, hashPassword, hashToken, isLegalTemplateCode, matchSetOf, mkHistory, normalizeNameToTemplateCode, parseAuthEnabledEnvValue, pauseTask, ping, pruneInstance, renderPrompt, reopenTask, resumeTask, simulateTrackChains, sumReferenceContentChars, taskProgress, toNodeInfo, toTaskPublic, tokenTypeFromValue, trackIntersects, verifyPassword, withTransaction };
package/dist/index.js CHANGED
@@ -136,6 +136,7 @@ var BIZ_CODE_MESSAGES = {
136
136
  TASK_PRUNE_GRAPH_INVALID: "\u526A\u679D\u540E DAG \u56FE\u4E0D\u5B8C\u6574\uFF08\u8282\u70B9\u6E05\u5355\u4E0E\u6A21\u677F\u7ED3\u6784\u4E0D\u517C\u5BB9\uFF0C\u89C1\u9519\u8BEF\u8BE6\u60C5\uFF09",
137
137
  TASK_SKIP_NODE_NOT_FOUND: "skip \u6E05\u5355\u542B\u6A21\u677F\u4E0D\u5B58\u5728\u7684\u8282\u70B9 id\uFF08\u89C1\u9519\u8BEF\u8BE6\u60C5\u7684\u5408\u6CD5\u8282\u70B9\u6E05\u5355\uFF09",
138
138
  TASK_TYPE_TRACK_MISMATCH: "\u4EFB\u52A1\u7C7B\u578B\u4E0E\u8F68\u9053\u7EC4\u5408\u975E\u6CD5\uFF08research \u8F68\u9053\u4E3A\u8C03\u7814\u7C7B\u578B\u4E13\u7528\u503C\uFF1BUI \u5FAE\u8C03\u4EC5\u9002\u7528 ui \u8F68\u9053\uFF09",
139
+ TASK_TRACK_MATCH_MISSING: "\u4EFB\u52A1\u7F3A\u5C11\u8F68\u9053\u5339\u914D\u5FEB\u7167\uFF08trackMatch\uFF09\u2014\u2014\u5B58\u91CF\u4EFB\u52A1\u8BF7\u91CD\u8DD1 server \u542F\u52A8\u8FC1\u79FB\uFF08ensureCollections\uFF09",
139
140
  // T202608290001:停用模板创建任务守卫(资产启停——停用对创建通道同样不可见)
140
141
  TEMPLATE_DISABLED: "\u6A21\u677F\u5DF2\u505C\u7528\uFF0C\u7981\u6B62\u7528\u4E8E\u521B\u5EFA\u4EFB\u52A1\uFF08Web \u7BA1\u7406\u53F0\u53EF\u91CD\u65B0\u542F\u7528\uFF09",
141
142
  // T202608310001:鉴权域(错误响应以 error: bizCode 为机器可判键)
@@ -153,6 +154,8 @@ var BIZ_CODE_MESSAGES = {
153
154
  NODE_PRESET_CODE_IMMUTABLE: "\u8282\u70B9 code \u521B\u5EFA\u540E\u4E0D\u53EF\u53D8",
154
155
  NODE_PRESET_VERSION_REQUIRED: "\u8282\u70B9\u5185\u5BB9\u53D8\u66F4\u5FC5\u987B\u540C\u6B65\u63D0\u5347\u7248\u672C\u53F7\uFF08version \u5B57\u6BB5\uFF09",
155
156
  TEMPLATE_NODE_ID_DUPLICATE: "\u6A21\u677F\u5185\u8282\u70B9 id \u91CD\u590D\uFF08\u5148\u5728\u8282\u70B9\u5E93\u4E2D\u590D\u5236\u6539\u540D\u6216\u8C03\u6574\u6A21\u677F\u8282\u70B9\u6807\u8BC6\uFF09",
157
+ // T202610120004:模板同轨分叉(D6)——写入侧统一文案(含修复指引;详情 issues 由断言函数附加)
158
+ TEMPLATE_SAME_TRACK_FORK: "\u6A21\u677F\u5B58\u5728\u540C\u8F68\u5206\u53C9\uFF08\u540C\u4E00\u8282\u70B9\u5BF9\u540C\u4E00\u76EE\u6807\u8F68\u9053\u6709\u591A\u6761\u51FA\u8FB9\uFF09\u2014\u2014\u540C\u8F68\u51FA\u8FB9\u4EC5\u5141\u8BB8\u4E00\u6761\uFF1B\u8DF3\u8FC7\u53EF\u9009\u8282\u70B9\u7531\u526A\u679D\u6865\u63A5\u673A\u5236\u81EA\u52A8\u8865\u8FB9\uFF0C\u52FF\u624B\u5DE5\u6DFB\u52A0\u65C1\u8DEF\u76F4\u8FB9",
156
159
  NODE_LIBRARY_COMPOSITION_NOT_FOUND: "\u7EC4\u5408\u5B9A\u4E49\u4E0D\u5B58\u5728\uFF08\u591A\u5E93\u540C\u540D\u7EC4\u5408\u9700\u663E\u5F0F\u6307\u5B9A\u6765\u6E90\u5E93\uFF09",
157
160
  UPGRADE_DECISION_REQUIRED: "\u672C\u5730\u5DF2\u4FEE\u6539\u7684\u8282\u70B9\u5FC5\u987B\u663E\u5F0F\u51B3\u7B56\uFF08upgrade/keep/skip\uFF09",
158
161
  UPGRADE_STALE: "\u6A21\u677F\u72B6\u6001\u4E0E\u5347\u7EA7\u8BA1\u5212\u4E0D\u7B26\uFF08\u8BA1\u5212\u540E\u6A21\u677F\u5DF2\u88AB\u7F16\u8F91\uFF09\uFF0C\u8BF7\u91CD\u65B0\u83B7\u53D6\u5347\u7EA7\u8BA1\u5212",
@@ -712,7 +715,10 @@ var SkillCopySchema = z2.object({
712
715
 
713
716
  // src/schemas/dag-template.ts
714
717
  var DagNodePhaseSchema = z3.string();
715
- var DagNodeTrackSchema = z3.string();
718
+ var DagNodeTrackSchema = z3.union([
719
+ z3.string(),
720
+ z3.array(z3.string()).min(1, "\u8282\u70B9\u8F68\u9053\u6570\u7EC4\u4E0D\u53EF\u4E3A\u7A7A\uFF08\u591A\u8F68\u8282\u70B9\u81F3\u5C11\u58F0\u660E\u4E00\u6761\u8F68\u9053\uFF09")
721
+ ]);
716
722
  var DAG_PHASES = ["entry", "track", "test", "exit"];
717
723
  var DAG_TRACKS = ["backend", "ui", "all"];
718
724
  var NODE_ID_PATTERN = /^[A-Za-z0-9_-]+$/;
@@ -868,6 +874,48 @@ function assertUniqueNodeIds(nodes) {
868
874
  );
869
875
  }
870
876
  }
877
+ function findSameTrackForks(nodes, edges) {
878
+ const trackKeyOf = (track) => {
879
+ const members = typeof track === "string" ? [track] : track;
880
+ return members.includes("all") ? "all" : [...new Set(members)].sort().join("+");
881
+ };
882
+ const trackById = new Map(nodes.map((n) => [n.id, trackKeyOf(n.track)]));
883
+ const groupsBySource = /* @__PURE__ */ new Map();
884
+ for (const edge of edges) {
885
+ const targetTrack = trackById.get(edge.to);
886
+ if (targetTrack === void 0) continue;
887
+ let groups = groupsBySource.get(edge.from);
888
+ if (groups === void 0) {
889
+ groups = /* @__PURE__ */ new Map();
890
+ groupsBySource.set(edge.from, groups);
891
+ }
892
+ groups.set(targetTrack, (groups.get(targetTrack) ?? 0) + 1);
893
+ }
894
+ const issues = [];
895
+ for (const [nodeId, groups] of groupsBySource) {
896
+ for (const [targetTrack, count] of groups) {
897
+ if (count > 1) issues.push({ nodeId, targetTrack, count });
898
+ }
899
+ }
900
+ return issues;
901
+ }
902
+ function assertNoSameTrackFork(nodes, edges) {
903
+ const issues = findSameTrackForks(nodes, edges);
904
+ if (issues.length > 0) {
905
+ const detail = issues.map((issue) => `\u8282\u70B9 ${issue.nodeId} \u6709 ${String(issue.count)} \u6761\u6307\u5411 ${issue.targetTrack} \u8F68\u8282\u70B9\u7684\u51FA\u8FB9`).join("\uFF1B");
906
+ throw new ValidationError(
907
+ `\u6A21\u677F\u5B58\u5728\u540C\u8F68\u5206\u53C9\uFF08D6\uFF09\uFF1A${detail}`,
908
+ issues.map((issue) => ({
909
+ path: ["edges"],
910
+ message: `\u8282\u70B9 ${issue.nodeId} \u5728 ${issue.targetTrack} \u8F68\u6709 ${String(issue.count)} \u6761\u51FA\u8FB9\uFF08\u540C\u8F68\u5206\u53C9\uFF09\u2014\u2014\u540C\u8F68\u51FA\u8FB9\u4EC5\u5141\u8BB8\u4E00\u6761\uFF1B\u8DF3\u8FC7\u53EF\u9009\u8282\u70B9\u7531\u526A\u679D\u6865\u63A5\u673A\u5236\u81EA\u52A8\u8865\u8FB9\uFF0C\u52FF\u624B\u5DE5\u6DFB\u52A0\u65C1\u8DEF\u76F4\u8FB9`
911
+ })),
912
+ {
913
+ bizCode: "TEMPLATE_SAME_TRACK_FORK",
914
+ message: `${BIZ_CODE_MESSAGES.TEMPLATE_SAME_TRACK_FORK}\uFF08${detail}\uFF09`
915
+ }
916
+ );
917
+ }
918
+ }
871
919
 
872
920
  // src/store/repos/dag-template.repo.ts
873
921
  function normalizeNameToTemplateCode(name) {
@@ -1141,7 +1189,8 @@ var DagInstanceNodeSchema = z4.object({
1141
1189
  label: z4.string(),
1142
1190
  // N016 F9:枚举放宽(注册表权威)
1143
1191
  phase: z4.string(),
1144
- track: z4.string(),
1192
+ // T202610120005 R7:节点轨道 union(多轨节点)——实例值拷贝冻结原样形态,模板升版不回溯
1193
+ track: DagNodeTrackSchema,
1145
1194
  prompt: z4.string(),
1146
1195
  skills: z4.array(z4.string()).default([]),
1147
1196
  agents: z4.array(z4.string()).default([]),
@@ -1192,7 +1241,18 @@ var TaskSchema = z4.object({
1192
1241
  status: TaskStatusSchema,
1193
1242
  /** 暂停位置:from 节点 id(暂停点暂停,approve 经 findNextEdge 重推导边)/ null(手动暂停) */
1194
1243
  pausedAt: z4.string().nullable().default(null),
1244
+ /**
1245
+ * 轨道展示锚(存量展示与 ?track= 精确过滤用):单轨任务=原值、组合任务=成员
1246
+ * join('+') 规范串、all 通配任务='all'(注册表 label 查找键)。
1247
+ */
1195
1248
  track: z4.string(),
1249
+ /**
1250
+ * T202610120005:任务轨道成员快照(创建时解析冻结,不含 'all' 哨兵)——运行时
1251
+ * 路由/剪枝/终态判定的唯一数据源(matchSetOf 单源构造匹配集,不回查注册表:
1252
+ * 词表升版/停用不影响在途任务)。optional 兼容迁移前存量窗口,缺失时引擎
1253
+ * fail-loud(TASK_TRACK_MATCH_MISSING)。
1254
+ */
1255
+ trackMatch: z4.array(z4.string()).optional(),
1196
1256
  /**
1197
1257
  * N020 D1:任务级结构化文档(未写时字段缺省——语义即「未写」,不做 null 占位)。
1198
1258
  * 写入口:PATCH /api/tasks/:id/doc(字段级 $set + acceptance/nonGoals 条目 $push)。
@@ -1212,7 +1272,13 @@ var TaskSchema = z4.object({
1212
1272
  });
1213
1273
  var TaskCreateInputSchema = z4.object({
1214
1274
  title: z4.string(),
1215
- track: z4.string(),
1275
+ /**
1276
+ * T202610120005:任务轨道 union——字符串(单轨/旧调用兼容)或非空字符串数组
1277
+ * (组合/多选表单产物;['all'] 单元素数组视同字符串 'all' 通配形态)。
1278
+ * 归一化(去重保序/all 展开/值域与类型绑定校验)在 server 入口三段 helper
1279
+ * 完成(track-input.ts),repo 收已归一化 { trackMembers, trackAnchor }。
1280
+ */
1281
+ track: z4.union([z4.string().min(1), z4.array(z4.string().min(1)).min(1)]),
1216
1282
  /**
1217
1283
  * T202609020002:模板引用支持 code|id 双形态——24-hex 直查数据库 id 命中即用
1218
1284
  * (hex 未命中不转 code 检索:code 必含连字符,与 hex 形态天然互斥);非 hex 在
@@ -1233,9 +1299,30 @@ var TaskCreateInputSchema = z4.object({
1233
1299
  });
1234
1300
 
1235
1301
  // src/task/track-match.ts
1236
- function trackMatchSet(taskTrack) {
1237
- if (taskTrack === "mixed") return /* @__PURE__ */ new Set(["backend", "ui", "all"]);
1238
- return /* @__PURE__ */ new Set([taskTrack, "all"]);
1302
+ function matchSetOf(members) {
1303
+ if (!members || members.length === 0) {
1304
+ throw new BadRequestError(
1305
+ "task trackMatch snapshot is empty \u2014 task missing track members (run ensureCollections migration or recreate task)",
1306
+ void 0,
1307
+ {
1308
+ bizCode: "TASK_TRACK_MATCH_MISSING",
1309
+ message: "\u4EFB\u52A1\u7F3A\u5C11\u8F68\u9053\u5339\u914D\u5FEB\u7167\uFF08trackMatch\uFF09\u2014\u2014\u7591\u4F3C\u5B58\u91CF\u4EFB\u52A1\u672A\u8FC1\u79FB\uFF0C\u91CD\u8DD1 server \u542F\u52A8\u8FC1\u79FB\uFF08ensureCollections\uFF09\u540E\u91CD\u8BD5"
1310
+ }
1311
+ );
1312
+ }
1313
+ return /* @__PURE__ */ new Set([...members, "all"]);
1314
+ }
1315
+ function effectiveTracks(nodeTrack, matchSet) {
1316
+ const members = typeof nodeTrack === "string" ? [nodeTrack] : nodeTrack;
1317
+ const result = /* @__PURE__ */ new Set();
1318
+ for (const member of members) {
1319
+ if (member === "all") return /* @__PURE__ */ new Set(["all"]);
1320
+ if (matchSet.has(member)) result.add(member);
1321
+ }
1322
+ return result;
1323
+ }
1324
+ function trackIntersects(nodeTrack, matchSet) {
1325
+ return effectiveTracks(nodeTrack, matchSet).size > 0;
1239
1326
  }
1240
1327
 
1241
1328
  // src/task/prompt-renderer.ts
@@ -1280,10 +1367,10 @@ async function advanceTask(deps, taskId, input) {
1280
1367
  task.dagInstance.edges,
1281
1368
  task.dagInstance.nodes,
1282
1369
  currentNode.id,
1283
- task.track
1370
+ matchSetOf(task.trackMatch)
1284
1371
  );
1285
1372
  if (!nextEdge) {
1286
- if (checkTermination(task.dagInstance.nodes, task.dagInstance.edges, task.dagInstance.nodeStates, completedState, currentNode.id, task.track)) {
1373
+ if (checkTermination(task.dagInstance.nodes, task.dagInstance.edges, task.dagInstance.nodeStates, completedState, currentNode.id, matchSetOf(task.trackMatch))) {
1287
1374
  if (!input?.finalize) {
1288
1375
  throw new BadRequestError(
1289
1376
  `Task is at its final node ('${currentNode.id}'). Confirm all outputs are complete, then advance with finalize=true to complete the task.`,
@@ -1404,7 +1491,7 @@ async function approveTask(deps, taskId, request) {
1404
1491
  task.dagInstance.edges,
1405
1492
  task.dagInstance.nodes,
1406
1493
  pausedNodeId,
1407
- task.track
1494
+ matchSetOf(task.trackMatch)
1408
1495
  );
1409
1496
  if (!nextEdge) {
1410
1497
  throw new Error(
@@ -1618,41 +1705,43 @@ function derivePreTerminalState(history) {
1618
1705
  }
1619
1706
  throw new Error("invariant violation: no pre-terminal state anchor found before cancelled entry");
1620
1707
  }
1621
- function findNextEdge(edges, nodes, fromNodeId, track) {
1708
+ function findNextEdge(edges, nodes, fromNodeId, matchSet) {
1622
1709
  const outEdges = edges.filter((e) => e.from === fromNodeId);
1623
- const matchSet = trackMatchSet(track);
1710
+ const effectiveKeyOf = (track) => {
1711
+ const effective = effectiveTracks(track, matchSet);
1712
+ return effective.size === 0 ? "" : [...effective].sort().join("+");
1713
+ };
1624
1714
  const matching = outEdges.filter((e) => {
1625
1715
  const target = nodes.find((n) => n.id === e.to);
1626
1716
  if (!target) return false;
1627
- return matchSet.has(target.track);
1717
+ return trackIntersects(target.track, matchSet);
1628
1718
  });
1629
1719
  if (matching.length > 1) {
1630
1720
  const byTargetTrack = /* @__PURE__ */ new Map();
1631
1721
  for (const edge of matching) {
1632
1722
  const target = nodes.find((n) => n.id === edge.to);
1633
- const key = target?.track ?? "";
1723
+ const key = target ? effectiveKeyOf(target.track) : "";
1634
1724
  const group = byTargetTrack.get(key) ?? [];
1635
1725
  group.push(edge);
1636
1726
  byTargetTrack.set(key, group);
1637
1727
  }
1638
- for (const [targetTrack, group] of byTargetTrack) {
1728
+ for (const [targetTrackKey, group] of byTargetTrack) {
1639
1729
  if (group.length > 1) {
1640
1730
  throw new BadRequestError(
1641
- `Multiple matching edges from node "${fromNodeId}" for track "${track}" \u2014 DAG template has a same-track fork (target track "${targetTrack}")`
1731
+ `Multiple matching edges from node "${fromNodeId}" (effective tracks "${targetTrackKey}") \u2014 DAG template has a same-track fork`
1642
1732
  );
1643
1733
  }
1644
1734
  }
1645
1735
  const dedicated = matching.filter((e) => {
1646
1736
  const target = nodes.find((n) => n.id === e.to);
1647
- return target?.track !== "all";
1737
+ return target ? effectiveKeyOf(target.track) !== "all" : false;
1648
1738
  });
1649
1739
  return dedicated[0] ?? matching[0];
1650
1740
  }
1651
1741
  return matching[0] ?? null;
1652
1742
  }
1653
- function checkTermination(nodes, edges, nodeStates, upcomingCompletedState, upcomingCompletedNodeId, track) {
1654
- const matchSet = trackMatchSet(track);
1655
- const trackFilter = (n) => matchSet.has(n.track);
1743
+ function checkTermination(nodes, edges, nodeStates, upcomingCompletedState, upcomingCompletedNodeId, matchSet) {
1744
+ const trackFilter = (n) => trackIntersects(n.track, matchSet);
1656
1745
  const terminalNodes = nodes.filter(
1657
1746
  (n) => trackFilter(n) && !edges.some((e) => e.from === n.id)
1658
1747
  );
@@ -1676,7 +1765,10 @@ function toTaskPublic(task) {
1676
1765
  currentPhase: task.currentPhase,
1677
1766
  status: task.status,
1678
1767
  pausedAt: task.pausedAt,
1679
- track: task.track
1768
+ track: task.track,
1769
+ // T202610120005:组合任务匹配快照进公共投影(context/advance/approve 写回执出口——
1770
+ // web 展示拼接与 ?track= 过滤的数据承载)
1771
+ ...task.trackMatch !== void 0 ? { trackMatch: task.trackMatch } : {}
1680
1772
  };
1681
1773
  }
1682
1774
  function toNodeInfo(node, task) {
@@ -1684,7 +1776,7 @@ function toNodeInfo(node, task) {
1684
1776
  task.dagInstance.edges,
1685
1777
  task.dagInstance.nodes,
1686
1778
  node.id,
1687
- task.track
1779
+ matchSetOf(task.trackMatch)
1688
1780
  );
1689
1781
  return {
1690
1782
  nodeId: node.id,
@@ -1722,12 +1814,10 @@ function graphInvalid(reason) {
1722
1814
  { bizCode: "TASK_PRUNE_GRAPH_INVALID", message: `\u526A\u679D\u540E DAG \u56FE\u4E0D\u5B8C\u6574\uFF08${reason}\uFF09\u2014\u2014\u68C0\u67E5 skip \u6E05\u5355\u4E0E\u6A21\u677F\u7ED3\u6784\u517C\u5BB9\u6027` }
1723
1815
  );
1724
1816
  }
1725
- function pruneInstance(template, taskTrack, skipNodeIds) {
1726
- if (skipNodeIds.length > 0) assertSkipNodesExist(template, skipNodeIds);
1817
+ function buildPrunedGraph(template, trackMembers, skipNodeIds = []) {
1727
1818
  const skipSet = new Set(skipNodeIds);
1728
- const matchSet = trackMatchSet(taskTrack);
1729
- const keptNodes = template.nodes.filter((n) => !skipSet.has(n.id) && matchSet.has(n.track));
1730
- const prunedNodes = template.nodes.filter((n) => skipSet.has(n.id) || !matchSet.has(n.track)).map((n) => n.id);
1819
+ const matchSet = matchSetOf(trackMembers);
1820
+ const keptNodes = template.nodes.filter((n) => !skipSet.has(n.id) && trackIntersects(n.track, matchSet));
1731
1821
  const keptIds = new Set(keptNodes.map((n) => n.id));
1732
1822
  const keptEdges = template.edges.filter((e) => keptIds.has(e.from) && keptIds.has(e.to));
1733
1823
  const edges = keptEdges.map((e) => ({ ...e }));
@@ -1741,7 +1831,7 @@ function pruneInstance(template, taskTrack, skipNodeIds) {
1741
1831
  if (templateTerminalIds.has(node.id)) continue;
1742
1832
  const nearestActive = findNearestActiveDescendant(template, node.id, keptIds);
1743
1833
  if (!nearestActive) {
1744
- graphInvalid(`\u8282\u70B9\u300C${node.id}\u300D\u526A\u679D\u540E\u51FA\u5EA6\u4E3A 0 \u4E14\u6CBF\u6A21\u677F\u540E\u4EE3\u65E0\u6FC0\u6D3B\u8282\u70B9\uFF08\u8DEF\u5F84\u65AD\u88C2\uFF09`);
1834
+ return { ok: false, reason: `\u8282\u70B9\u300C${node.id}\u300D\u526A\u679D\u540E\u51FA\u5EA6\u4E3A 0 \u4E14\u6CBF\u6A21\u677F\u540E\u4EE3\u65E0\u6FC0\u6D3B\u8282\u70B9\uFF08\u8DEF\u5F84\u65AD\u88C2\uFF09` };
1745
1835
  }
1746
1836
  const firstTemplateOutEdge = template.edges.find((e) => e.from === node.id);
1747
1837
  edges.push({
@@ -1750,6 +1840,17 @@ function pruneInstance(template, taskTrack, skipNodeIds) {
1750
1840
  ...firstTemplateOutEdge?.pausePoint ? { pausePoint: { ...firstTemplateOutEdge.pausePoint } } : {}
1751
1841
  });
1752
1842
  }
1843
+ return { ok: true, graph: { nodes: keptNodes, edges } };
1844
+ }
1845
+ function pruneInstance(template, trackMembers, skipNodeIds) {
1846
+ if (skipNodeIds.length > 0) assertSkipNodesExist(template, skipNodeIds);
1847
+ const skipSetForPruned = new Set(skipNodeIds);
1848
+ const matchSetForPruned = matchSetOf(trackMembers);
1849
+ const prunedNodes = template.nodes.filter((n) => skipSetForPruned.has(n.id) || !trackIntersects(n.track, matchSetForPruned)).map((n) => n.id);
1850
+ const core = buildPrunedGraph(template, trackMembers, skipNodeIds);
1851
+ if (!core.ok) graphInvalid(core.reason);
1852
+ const keptNodes = core.graph.nodes;
1853
+ const edges = core.graph.edges;
1753
1854
  if (keptNodes.length < 2) {
1754
1855
  graphInvalid(`\u526A\u540E\u4EC5\u5269 ${keptNodes.length} \u4E2A\u8282\u70B9\uFF08\u987B \u2265 2\uFF09\u2014\u2014skip \u6E05\u5355/\u8F68\u9053\u5254\u9664\u8FC7\u91CD`);
1755
1856
  }
@@ -1799,7 +1900,7 @@ function pruneInstance(template, taskTrack, skipNodeIds) {
1799
1900
  }
1800
1901
  let nextEdge;
1801
1902
  try {
1802
- nextEdge = findNextEdge(edges, keptNodes, cursor, taskTrack);
1903
+ nextEdge = findNextEdge(edges, keptNodes, cursor, matchSetOf(trackMembers));
1803
1904
  } catch (err) {
1804
1905
  const reason = err instanceof BadRequestError ? err.message : String(err);
1805
1906
  graphInvalid(`\u5F15\u64CE\u8DEF\u7531\u65E0\u6CD5\u6D88\u6B67\uFF08${reason}\uFF09`);
@@ -1811,7 +1912,7 @@ function pruneInstance(template, taskTrack, skipNodeIds) {
1811
1912
  const unreachableByEngine = keptNodes.filter((n) => !visitedByEngine.has(n.id));
1812
1913
  if (unreachableByEngine.length > 0) {
1813
1914
  graphInvalid(
1814
- `\u6FC0\u6D3B\u8282\u70B9\u4E0D\u5728\u5F15\u64CE\u8DEF\u5F84\u4E0A\uFF08\u5C06\u6C38\u4E45 pending\uFF0C\u68C0\u67E5\u7C7B\u578B\xD7\u8F68\u9053\u4E0E\u6A21\u677F\u7ED3\u6784\u5339\u914D\u2014\u2014\u5982 mixed \u9700\u6A21\u677F\u542B\u94FE\u95F4\u4E32\u884C\u6865\u8FB9\u3001research \u9700 skip \u6389\u4E0D\u88AB\u884C\u8D70\u7684\u5206\u652F\uFF09\uFF1A${unreachableByEngine.map((n) => n.id).join(", ")}`
1915
+ `\u6FC0\u6D3B\u8282\u70B9\u4E0D\u5728\u5F15\u64CE\u8DEF\u5F84\u4E0A\uFF08\u5C06\u6C38\u4E45 pending\uFF0C\u68C0\u67E5\u7C7B\u578B\xD7\u8F68\u9053\u4E0E\u6A21\u677F\u7ED3\u6784\u5339\u914D\u2014\u2014\u5982\u7EC4\u5408\u4EFB\u52A1\u9700\u6A21\u677F\u542B\u94FE\u95F4\u4E32\u884C\u6865\u8FB9\u3001research \u9700 skip \u6389\u4E0D\u88AB\u884C\u8D70\u7684\u5206\u652F\uFF09\uFF1A${unreachableByEngine.map((n) => n.id).join(", ")}`
1815
1916
  );
1816
1917
  }
1817
1918
  return { nodes: keptNodes, edges, prunedNodes, entryNode: entry };
@@ -1933,6 +2034,8 @@ function createTaskRepo(db) {
1933
2034
  status: task.status,
1934
2035
  pausedAt: task.pausedAt,
1935
2036
  track: task.track,
2037
+ // T202610120005:组合任务展示数据承载(web 列表拼接文案依赖;迁移前存量缺省)
2038
+ ...task.trackMatch !== void 0 ? { trackMatch: task.trackMatch } : {},
1936
2039
  progress: { completed: doc.completedCount, total: doc.totalCount },
1937
2040
  ...task.createdAt ? { createdAt: task.createdAt } : {},
1938
2041
  ...task.updatedAt ? { updatedAt: task.updatedAt } : {}
@@ -1996,11 +2099,11 @@ function createTaskRepo(db) {
1996
2099
  }
1997
2100
  return toEntity(result);
1998
2101
  },
1999
- async createTask(input, template, resolvedProjectId, session) {
2000
- if (!input.track) {
2102
+ async createTask(input, template, resolvedProjectId, trackInfo, session) {
2103
+ if (trackInfo.trackMembers.length === 0 || !trackInfo.trackAnchor) {
2001
2104
  throw new BadRequestError(`track is required`);
2002
2105
  }
2003
- const pruned = pruneInstance(template, input.track, input.skipNodes ?? []);
2106
+ const pruned = pruneInstance(template, trackInfo.trackMembers, input.skipNodes ?? []);
2004
2107
  const firstNode = pruned.entryNode;
2005
2108
  const dagInstance = {
2006
2109
  templateId: template.id ?? "",
@@ -2040,7 +2143,10 @@ function createTaskRepo(db) {
2040
2143
  currentPhase: firstNode.phase,
2041
2144
  status: "active",
2042
2145
  pausedAt: null,
2043
- track: input.track,
2146
+ // T202610120005:track=展示锚(单轨原值/组合 join('+') 规范串/all 通配锚),
2147
+ // trackMatch=成员快照(运行时路由/剪枝/终态判定的唯一数据源,创建时冻结)
2148
+ track: trackInfo.trackAnchor,
2149
+ trackMatch: trackInfo.trackMembers,
2044
2150
  nodeRecords: {},
2045
2151
  archNotes: [],
2046
2152
  history: [{
@@ -3293,6 +3399,17 @@ var ImportPlanResponseSchema = z9.object({
3293
3399
  status: z9.enum(["ok", "missing"])
3294
3400
  })
3295
3401
  ),
3402
+ /**
3403
+ * K10③:模板节点直接 AGENT 引用完整性逐项核对(node × agent 粒度)——
3404
+ * 与 directSkillChecks 对称;ok = 在 bundle 依赖 ∪ 目标环境可见集;missing = 双侧皆无(引用悬空,non-blocking warning)
3405
+ */
3406
+ directAgentChecks: z9.array(
3407
+ z9.object({
3408
+ node: z9.string(),
3409
+ agent: z9.string(),
3410
+ status: z9.enum(["ok", "missing"])
3411
+ })
3412
+ ),
3296
3413
  /** 非阻断提示(源环境悬空绑定、agent.model 引用缺失、sourcePreset 来源缺失等) */
3297
3414
  warnings: z9.array(z9.string())
3298
3415
  });
@@ -3349,12 +3466,15 @@ var TaskPublicSchema = z10.object({
3349
3466
  currentPhase: z10.string(),
3350
3467
  status: z10.string(),
3351
3468
  pausedAt: z10.string().nullable(),
3352
- track: z10.string()
3469
+ track: z10.string(),
3470
+ /** T202610120005:组合任务匹配快照(toTaskPublic 投影;迁移前存量缺省) */
3471
+ trackMatch: z10.array(z10.string()).optional()
3353
3472
  });
3354
3473
  var NodeInfoSchema = z10.object({
3355
3474
  nodeId: z10.string(),
3356
3475
  label: z10.string(),
3357
- track: z10.string(),
3476
+ // T202610120005 R7:节点轨道 union(多轨节点——值拷贝冻结原样形态)
3477
+ track: z10.union([z10.string(), z10.array(z10.string())]),
3358
3478
  prompt: z10.string(),
3359
3479
  skills: z10.array(z10.string()),
3360
3480
  agents: z10.array(z10.string()),
@@ -3428,13 +3548,16 @@ var TaskContextSchema = z11.object({
3428
3548
  currentPhase: z11.string(),
3429
3549
  status: z11.string(),
3430
3550
  pausedAt: z11.string().nullable(),
3431
- track: z11.string()
3551
+ track: z11.string(),
3552
+ // T202610120005:组合任务匹配快照(与 TaskPublicSchema 同步——context 读取出口)
3553
+ trackMatch: z11.array(z11.string()).optional()
3432
3554
  }),
3433
3555
  currentNode: z11.object({
3434
3556
  nodeId: z11.string(),
3435
3557
  label: z11.string(),
3436
3558
  phase: z11.string(),
3437
- track: z11.string(),
3559
+ // T202610120005 R7:节点轨道 union(多轨节点)
3560
+ track: z11.union([z11.string(), z11.array(z11.string())]),
3438
3561
  /** renderPrompt 渲染后(AI 推进任务的直接输入) */
3439
3562
  prompt: z11.string(),
3440
3563
  skills: z11.array(z11.string()),
@@ -3486,6 +3609,8 @@ var TaskSummarySchema = z11.object({
3486
3609
  status: z11.string(),
3487
3610
  pausedAt: z11.string().nullable(),
3488
3611
  track: z11.string(),
3612
+ // T202610120005:组合任务展示数据承载(列表页拼接文案依赖;迁移前存量可能缺省)
3613
+ trackMatch: z11.array(z11.string()).optional(),
3489
3614
  progress: z11.object({
3490
3615
  completed: z11.number(),
3491
3616
  total: z11.number()
@@ -3743,7 +3868,8 @@ var NodePresetShapeSchema = z17.object({
3743
3868
  nodeId: z17.string().regex(/^[A-Za-z0-9_-]+$/, "nodeId \u53EA\u5141\u8BB8\u5B57\u6BCD/\u6570\u5B57/\u4E0B\u5212\u7EBF/\u8FDE\u5B57\u7B26"),
3744
3869
  /** 运行时注册表判定(对齐 dag_phase/dag_track 放宽策略——Zod 只做类型级) */
3745
3870
  phase: z17.string(),
3746
- track: z17.string(),
3871
+ // T202610120005 R7:节点轨道 union(多轨节点,值拷贝冻结原样形态)
3872
+ track: DagNodeTrackSchema,
3747
3873
  prompt: z17.string().min(1),
3748
3874
  /** 绑定技能名单(by-name 引用;版本要求只存在于 npm 分发层 manifest,不侵入运行链路) */
3749
3875
  skills: uniqueNamesSchema,
@@ -4210,6 +4336,102 @@ function applyUpgradeDecisions(template, presetsByCode, decisions) {
4210
4336
  return { nodes, nextVersion: changed ? bumpPatch(template.version) : null, results };
4211
4337
  }
4212
4338
 
4339
+ // src/task/track-chains.ts
4340
+ function nodeTrackMembers(track) {
4341
+ const members = typeof track === "string" ? [track] : track;
4342
+ if (members.includes("all")) return [];
4343
+ return members;
4344
+ }
4345
+ function simulateTrackChains(template) {
4346
+ const nodeTracks = /* @__PURE__ */ new Set();
4347
+ for (const node of template.nodes) {
4348
+ for (const member of nodeTrackMembers(node.track)) nodeTracks.add(member);
4349
+ }
4350
+ if (nodeTracks.has("backend") && nodeTracks.has("ui")) nodeTracks.add("backend+ui");
4351
+ const builtinOrder = /* @__PURE__ */ new Map([
4352
+ ["backend", 0],
4353
+ ["ui", 1],
4354
+ ["backend+ui", 2]
4355
+ ]);
4356
+ const firstSeen = /* @__PURE__ */ new Map();
4357
+ for (const node of template.nodes) {
4358
+ for (const member of nodeTrackMembers(node.track)) {
4359
+ if (!firstSeen.has(member)) firstSeen.set(member, firstSeen.size);
4360
+ }
4361
+ }
4362
+ const tracks = [...nodeTracks].sort((a, b) => {
4363
+ const rankA = builtinOrder.get(a) ?? builtinOrder.size;
4364
+ const rankB = builtinOrder.get(b) ?? builtinOrder.size;
4365
+ return rankA !== rankB ? rankA - rankB : (firstSeen.get(a) ?? 0) - (firstSeen.get(b) ?? 0);
4366
+ });
4367
+ return tracks.map((track) => simulateSingleTrack(template, track));
4368
+ }
4369
+ function simulateSingleTrack(template, track) {
4370
+ const members = track.includes("+") ? track.split("+") : [track];
4371
+ const core = buildPrunedGraph(template, members);
4372
+ if (!core.ok) return { track, chain: [], error: core.reason };
4373
+ const { nodes, edges } = core.graph;
4374
+ const keptIds = new Set(nodes.map((n) => n.id));
4375
+ const hasIncoming = new Set(edges.filter((e) => keptIds.has(e.to)).map((e) => e.to));
4376
+ const entries = nodes.filter((n) => !hasIncoming.has(n.id));
4377
+ if (entries.length === 0) {
4378
+ return { track, chain: [], error: "\u526A\u679D\u540E\u65E0\u5165\u53E3\u8282\u70B9\uFF08\u5165\u5EA6 0 \u7684\u8282\u70B9\u4E0D\u5B58\u5728\uFF09" };
4379
+ }
4380
+ if (entries.length > 1) {
4381
+ return { track, chain: [], error: `\u5165\u53E3\u8282\u70B9 ${String(entries.length)} \u4E2A\uFF08\u987B\u6070\u597D 1 \u4E2A\uFF09\uFF1A${entries.map((n) => n.id).join(", ")}` };
4382
+ }
4383
+ const entry = entries[0];
4384
+ const chain = [entry.id];
4385
+ let cursor = entry.id;
4386
+ const maxHops = nodes.length;
4387
+ for (let hop = 0; hop <= maxHops && cursor !== null; hop++) {
4388
+ if (hop === maxHops) {
4389
+ return { track, chain: [], error: "\u5F15\u64CE\u8DEF\u5F84\u6A21\u62DF\u8D85\u6B65\u6570\u4E0A\u9650\uFF08\u56FE\u4E2D\u5B58\u5728\u73AF\uFF09" };
4390
+ }
4391
+ let nextEdge;
4392
+ try {
4393
+ nextEdge = findNextEdge(edges, nodes, cursor, matchSetOf(members));
4394
+ } catch (err) {
4395
+ const reason = err instanceof Error ? err.message : String(err);
4396
+ return { track, chain: [], error: `\u5F15\u64CE\u8DEF\u7531\u65E0\u6CD5\u6D88\u6B67\uFF08${reason}\uFF09` };
4397
+ }
4398
+ if (nextEdge === null) break;
4399
+ cursor = nextEdge.to;
4400
+ if (!chain.includes(cursor)) chain.push(cursor);
4401
+ }
4402
+ return { track, chain };
4403
+ }
4404
+ var TEMPLATE_STRUCTURE_CONSTRAINTS = [
4405
+ {
4406
+ id: "single-chain-per-track",
4407
+ title: "\u8F68\u9053\u5185\u5355\u94FE\uFF08D6\uFF09",
4408
+ rule: '\u540C\u4E00\u8282\u70B9\u5BF9\u540C\u4E00\u76EE\u6807\u8F68\u9053\u53EA\u5141\u8BB8\u4E00\u6761\u51FA\u8FB9\uFF08"1 \u6761\u4E13\u5C5E\u8FB9 + 1 \u6761 all \u8FB9" \u5408\u6CD5\uFF1B"1 \u4E13\u5C5E + 2 all"\u3001"2 all"\u3001\u540C\u8F68\u53CC\u4E13\u5C5E\u5747\u8FDD\u89C4\uFF09\u3002\u8DF3\u8FC7\u53EF\u9009\u8282\u70B9\u7531\u526A\u679D\u6865\u63A5\u673A\u5236\u81EA\u52A8\u8865\u8FB9\uFF0C\u4E0D\u9700\u8981\u4E5F\u4E0D\u5E94\u8BE5\u624B\u5DE5\u6DFB\u52A0\u65C1\u8DEF\u76F4\u8FB9',
4409
+ positiveExample: "\u8DE8\u8F68\u5206\u53C9\uFF1ADESIGN\u2192CODE_BACKEND \u4E0E DESIGN\u2192CODE_UI \u5E76\u5B58\uFF08\u76EE\u6807 track \u4E0D\u540C\uFF0C\u4E24\u6761\u8FB9\u5404\u5C5E\u4E00\u7EC4\uFF0C\u5408\u6CD5\uFF09",
4410
+ negativeExample: "\u540C\u8F68\u65C1\u8DEF\u76F4\u8FB9\uFF1AGUT_DEV\u2192INTEGRATION_SCENE \u4E0E GUT_DEV\u2192PLAY_SMOKE\uFF08\u540C\u4E3A godot \u8F68\u51FA\u8FB9\uFF0C\u540C\u7EC4\u5206\u53C9\u2014\u2014\u4FDD\u5B58/\u5BFC\u5165/\u5EFA\u4EFB\u52A1\u4E09\u5C42\u4E00\u81F4\u62D2\u7EDD\uFF09"
4411
+ },
4412
+ {
4413
+ id: "cross-track-disambiguation",
4414
+ title: "\u8DE8\u8F68\u5206\u53C9\u6D88\u6B67",
4415
+ rule: "\u4E00\u4E2A\u8282\u70B9\u6709\u591A\u6761\u5339\u914D\u8FB9\u65F6\uFF1A\u4E13\u5C5E\u8F68\u8FB9\uFF08\u76EE\u6807 track \u2260 all\uFF09\u4F18\u5148\u4E8E all \u8FB9\uFF1B\u591A\u6761\u4E13\u5C5E\u8FB9\u6309\u6A21\u677F\u8FB9\u58F0\u660E\u5E8F\u53D6\u7B2C\u4E00\u6761\u3002\u5BF9\u9F50\u8282\u70B9\u60F3\u540E\u7F6E/\u524D\u7F6E\u67D0\u94FE\uFF0C\u9760\u8FB9\u58F0\u660E\u5E8F\u8868\u8FBE",
4416
+ positiveExample: "\u8FB9\u58F0\u660E\u5E8F [DESIGN\u2192CODE_BACKEND, DESIGN\u2192CODE_UI]\uFF1A\u7EC4\u5408\u4EFB\u52A1\uFF08backend+ui\uFF09\u5148\u8D70 CODE_BACKEND \u540E\u8D70 CODE_UI\uFF08\u58F0\u660E\u5E8F\u53D6\u7B2C\u4E00\uFF09",
4417
+ negativeExample: "\u671F\u671B\u7EC4\u5408\u4EFB\u52A1\u5148\u8D70 UI \u94FE\u5374\u628A DESIGN\u2192CODE_UI \u58F0\u660E\u5728 DESIGN\u2192CODE_BACKEND \u4E4B\u540E\uFF08\u4E13\u5C5E\u8FB9\u53D6\u58F0\u660E\u5E8F\u7B2C\u4E00\u6761\uFF0C\u540E\u58F0\u660E\u7684\u4E0D\u4F1A\u88AB\u4F18\u5148\u9009\u4E2D\uFF09"
4418
+ },
4419
+ {
4420
+ id: "skip-bridging",
4421
+ title: "\u8DF3\u8FC7\u8282\u70B9\u6865\u63A5",
4422
+ rule: "\u526A\u679D\u540E\u51FA\u5EA6 0 \u4E14\u975E\u6A21\u677F\u7EC8\u70B9\u7684\u8282\u70B9\u81EA\u52A8\u6CBF\u6A21\u677F\u540E\u4EE3 BFS \u8865\u8FB9\u5230\u6700\u8FD1\u6FC0\u6D3B\u540E\u4EE3\uFF0C\u6682\u505C\u70B9\u7EE7\u627F\u8BE5\u8282\u70B9\u9996\u6761\u6A21\u677F\u51FA\u8FB9\u2014\u2014\u65C1\u8DEF\u76F4\u8FB9\u65E2\u5197\u4F59\u53C8\u4F1A\u6784\u6210 D6 \u5206\u53C9",
4423
+ positiveExample: "\u6A21\u677F EXIT\u2192ARCH\u2192END\uFF0C\u4EFB\u52A1 skip ARCH\uFF1A\u81EA\u52A8\u6865\u63A5 EXIT\u2192END\uFF0CARCH \u51FA\u8FB9\u7684 human_approval \u7531\u6865\u63A5\u8FB9\u7EE7\u627F\uFF0C\u65E0\u9700\u624B\u5DE5\u52A0\u8FB9",
4424
+ negativeExample: "\u4E3A\u8DF3\u8FC7 ARCH \u624B\u5DE5\u6DFB\u52A0 EXIT\u2192END \u76F4\u8FB9\uFF08\u4E0E\u6865\u63A5\u4EA7\u7269\u5E76\u5B58\u5373\u540C\u8F68\u5206\u53C9\uFF0C\u88AB D6 \u62E6\u622A\uFF09"
4425
+ },
4426
+ {
4427
+ id: "combo-serial-bridge",
4428
+ title: "\u7EC4\u5408\u94FE\u95F4\u4E32\u884C\u6865\u8FB9",
4429
+ rule: "\u7EC4\u5408\u4EFB\u52A1\uFF08\u5982 backend+ui \u53CC\u94FE\uFF09\u9700\u8981\u94FE\u95F4\u4E32\u884C\u6865\u8FB9\uFF08\u5982 CODE_BACKEND\u2192CODE_UI\uFF09\uFF0C\u5426\u5219\u526A\u679D\u6821\u9A8C\u62A5\u300C\u6FC0\u6D3B\u8282\u70B9\u4E0D\u5728\u5F15\u64CE\u8DEF\u5F84\u300D\uFF08\u53E6\u4E00\u94FE\u8282\u70B9\u6C38\u4E45 pending\uFF09",
4430
+ positiveExample: "CODE_BACKEND\u2192CODE_UI \u4E32\u884C\u6865\u8FB9\uFF1A\u7EC4\u5408\u4EFB\u52A1\u5F15\u64CE\u5148\u8D70\u5B8C backend \u94FE\u518D\u8FDB ui \u94FE\uFF0C\u6700\u540E\u5171\u540C\u6C47\u5165 INTEGRATE/ACCEPT",
4431
+ negativeExample: "backend\u3001ui \u4E24\u94FE\u5404\u81EA\u72EC\u7ACB\u6C47\u5408\u5230\u7EC8\u70B9\u800C\u65E0\u94FE\u95F4\u6865\u8FB9\uFF08\u7EC4\u5408\u4EFB\u52A1\u521B\u5EFA\u65F6 422\u300C\u6FC0\u6D3B\u8282\u70B9\u4E0D\u5728\u5F15\u64CE\u8DEF\u5F84\u4E0A\u300D\uFF09"
4432
+ }
4433
+ ];
4434
+
4213
4435
  // src/template-transfer/payload.ts
4214
4436
  function toTemplatePayload(tpl) {
4215
4437
  const { id: _id, projectId: _projectId, enabled: _enabled, createdAt: _ca, updatedAt: _ua, code: _code, ...rest } = tpl;
@@ -4274,8 +4496,15 @@ async function composeExportBundle(deps, templateId) {
4274
4496
  deps.agentRepo.listAgentsByScopeFilter("project", tpl.projectId, { includeDisabled: true })
4275
4497
  ]);
4276
4498
  const directSet = new Set(directSkillNames);
4277
- const agents = [...globalAgents, ...projectAgents].filter(
4278
- (agent) => (agent.boundSkills ?? []).some((name) => directSet.has(name))
4499
+ const directAgentNames = new Set(tpl.nodes.flatMap((node) => node.agents ?? []));
4500
+ const visibleAgents = [...globalAgents, ...projectAgents];
4501
+ for (const name of directAgentNames) {
4502
+ if (!visibleAgents.some((a) => a.name === name)) {
4503
+ warnings.push(`AGENT "${name}" \u88AB\u8282\u70B9\u76F4\u63A5\u5F15\u7528\u4F46\u5728\u6E90\u73AF\u5883\u4E2D\u4E0D\u5B58\u5728\uFF08\u5F15\u7528\u60AC\u7A7A\uFF09\uFF0C\u672A\u5305\u542B\u5728\u5BFC\u51FA\u4F9D\u8D56\u4E2D`);
4504
+ }
4505
+ }
4506
+ const agents = visibleAgents.filter(
4507
+ (agent) => directAgentNames.has(agent.name) || (agent.boundSkills ?? []).some((name) => directSet.has(name))
4279
4508
  );
4280
4509
  const allSkillNames = [.../* @__PURE__ */ new Set([...directSkillNames, ...agents.flatMap((a) => a.boundSkills ?? [])])];
4281
4510
  const skillMap = await deps.skillRepo.listSkillsByNames(allSkillNames, "project", tpl.projectId);
@@ -4415,6 +4644,28 @@ async function buildImportPlan(deps, targetProjectId, bundle) {
4415
4644
  }
4416
4645
  }
4417
4646
  }
4647
+ const directAgentChecks = [];
4648
+ const bundledAgentNames = new Set(bundle.dependencies.agents.map((a) => a.name));
4649
+ const unbundledAgentNames = [
4650
+ ...new Set(bundle.template.nodes.flatMap((n) => n.agents ?? []).filter((a) => !bundledAgentNames.has(a)))
4651
+ ];
4652
+ const envVisibleAgentNames = unbundledAgentNames.length > 0 ? await (async () => {
4653
+ const [envGlobalAgents, envProjectAgents] = await Promise.all([
4654
+ deps.agentRepo.listAgentsByScopeFilter("global", void 0, { includeDisabled: true }),
4655
+ deps.agentRepo.listAgentsByScopeFilter("project", targetProjectId, { includeDisabled: true })
4656
+ ]);
4657
+ return new Set([...envGlobalAgents, ...envProjectAgents].map((a) => a.name));
4658
+ })() : /* @__PURE__ */ new Set();
4659
+ for (const node of bundle.template.nodes) {
4660
+ for (const agentName of node.agents ?? []) {
4661
+ if (bundledAgentNames.has(agentName) || envVisibleAgentNames.has(agentName)) {
4662
+ directAgentChecks.push({ node: node.id, agent: agentName, status: "ok" });
4663
+ } else {
4664
+ directAgentChecks.push({ node: node.id, agent: agentName, status: "missing" });
4665
+ warnings.push(`\u6A21\u677F\u8282\u70B9 ${node.id} \u76F4\u63A5\u5F15\u7528\u4EE3\u7406 "${agentName}" \u4E0D\u5728\u5BFC\u51FA\u5305\u4E2D\u4E14\u76EE\u6807\u73AF\u5883\u4E0D\u5B58\u5728\uFF08\u5F15\u7528\u60AC\u7A7A\uFF09`);
4666
+ }
4667
+ }
4668
+ }
4418
4669
  const presetCodes = [...new Set(bundle.template.nodes.flatMap((n) => n.sourcePreset ? [n.sourcePreset.code] : []))];
4419
4670
  const presetsByCode = presetCodes.length > 0 ? await deps.nodePresetRepo.listByCodes(presetCodes) : /* @__PURE__ */ new Map();
4420
4671
  for (const node of bundle.template.nodes) {
@@ -4434,6 +4685,7 @@ async function buildImportPlan(deps, targetProjectId, bundle) {
4434
4685
  },
4435
4686
  dependencies: [...skillChecks, ...agentChecks, ...aliasChecks],
4436
4687
  directSkillChecks,
4688
+ directAgentChecks,
4437
4689
  warnings
4438
4690
  };
4439
4691
  }
@@ -4463,6 +4715,7 @@ async function applyImport(deps, targetProjectId, bundle, decisions) {
4463
4715
  }
4464
4716
  assertUniqueNodeIds(nodesForWrite);
4465
4717
  assertUniqueAiCheckIds(nodesForWrite);
4718
+ assertNoSameTrackFork(nodesForWrite, tplPayload.edges);
4466
4719
  const contentFields = {
4467
4720
  name: tplPayload.name,
4468
4721
  description: tplPayload.description,
@@ -4787,6 +5040,7 @@ export {
4787
5040
  TASK_TYPES,
4788
5041
  TEMPLATE_CODE_MAX_LENGTH,
4789
5042
  TEMPLATE_CODE_REGEX,
5043
+ TEMPLATE_STRUCTURE_CONSTRAINTS,
4790
5044
  TaskBatchAckSchema,
4791
5045
  TaskBatchEntrySchema,
4792
5046
  TaskBatchFailureSchema,
@@ -4823,11 +5077,13 @@ export {
4823
5077
  approveTask,
4824
5078
  assertBoundSkillsCompatible,
4825
5079
  assertModelAliasExists,
5080
+ assertNoSameTrackFork,
4826
5081
  assertReferenceAggregateLimit,
4827
5082
  assertReferencesDeletable,
4828
5083
  assertUniqueAiCheckIds,
4829
5084
  assertUniqueNodeIds,
4830
5085
  buildImportPlan,
5086
+ buildPrunedGraph,
4831
5087
  buildTemplateCode,
4832
5088
  buildUpgradePlan,
4833
5089
  bumpPatch,
@@ -4857,11 +5113,13 @@ export {
4857
5113
  decisionKey,
4858
5114
  derivePreTerminalState,
4859
5115
  diffTemplateContent,
5116
+ effectiveTracks,
4860
5117
  enabledNeFalse,
4861
5118
  escapeRegExpLiteral,
4862
5119
  excerpt,
4863
5120
  findDuplicateNodeIds,
4864
5121
  findNextEdge,
5122
+ findSameTrackForks,
4865
5123
  findTemplateByName,
4866
5124
  generateEntryId,
4867
5125
  generateProjectKey,
@@ -4871,6 +5129,7 @@ export {
4871
5129
  hashPassword,
4872
5130
  hashToken,
4873
5131
  isLegalTemplateCode,
5132
+ matchSetOf,
4874
5133
  mkHistory,
4875
5134
  normalizeNameToTemplateCode,
4876
5135
  parseAuthEnabledEnvValue,
@@ -4880,12 +5139,13 @@ export {
4880
5139
  renderPrompt,
4881
5140
  reopenTask,
4882
5141
  resumeTask,
5142
+ simulateTrackChains,
4883
5143
  sumReferenceContentChars,
4884
5144
  taskProgress,
4885
5145
  toNodeInfo,
4886
5146
  toTaskPublic,
4887
5147
  tokenTypeFromValue,
4888
- trackMatchSet,
5148
+ trackIntersects,
4889
5149
  verifyPassword,
4890
5150
  withTransaction
4891
5151
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@siming-org/core",
3
- "version": "0.8.3",
3
+ "version": "0.8.5",
4
4
  "license": "MIT",
5
5
  "author": "Wuchao",
6
6
  "engines": {