@gordon.gan/specflow 1.4.6-beta → 1.5.0-beta
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/README.md +1 -1
- package/dist/core/approval/assemble.js +55 -1
- package/dist/core/approval/index-schema.d.ts +229 -0
- package/dist/core/approval/index-schema.js +56 -0
- package/dist/core/approval/index.d.ts +1 -1
- package/dist/core/approval/types.d.ts +28 -0
- package/package.json +1 -1
- package/prompts/approval/api-guidance.md +179 -0
- package/prompts/approval/generate.md +76 -10
- package/prompts/approval/multi-repo-guidance.md +202 -0
- package/prompts/approval/project-conventions-guidance.md +1 -1
- package/prompts/approval/segmented-generation.md +9 -3
- package/skills/GUIDANCE_PACKS.md +1 -1
- package/skills/specflow-approval/SKILL.md +43 -4
- package/templates/approval-index.yaml +20 -0
package/README.md
CHANGED
|
@@ -126,7 +126,7 @@ npm install -g @gordon.gan/specflow
|
|
|
126
126
|
npm install -g github:Gordon-Gan-Jiang/specflow
|
|
127
127
|
|
|
128
128
|
# 验证
|
|
129
|
-
specflow --version # 以 npm / package.json 为准(当前 1.
|
|
129
|
+
specflow --version # 以 npm / package.json 为准(当前 1.5.0-beta)
|
|
130
130
|
specflow --help
|
|
131
131
|
```
|
|
132
132
|
|
|
@@ -31,6 +31,11 @@ const LAZY_PATTERNS = [
|
|
|
31
31
|
/TBD\s*[。.]?$/,
|
|
32
32
|
/(?:^|\n)\s*\.{3}\s*(?:\n|$)/,
|
|
33
33
|
/(?:^|\n)\s*(?:同上|同前)\s*[。.]?\s*(?:\n|$)/,
|
|
34
|
+
/实现时(?:命名|对齐)/,
|
|
35
|
+
/(?:RPC|rpc).*暂定/,
|
|
36
|
+
/暂定\s*[`']?\w+[`']?/,
|
|
37
|
+
/\b如\s+[A-Z]\w*(?:Result|Request|Response)/,
|
|
38
|
+
/内部(?:经|调用)\s*I\d+[^.\n]{0,40}(?:一行|代替|省略)/,
|
|
34
39
|
];
|
|
35
40
|
const MIN_PART_BYTES = {
|
|
36
41
|
'04.4': 200,
|
|
@@ -102,8 +107,21 @@ function buildDocumentHeader(index) {
|
|
|
102
107
|
const stack = meta.tech_stack ?? 'unknown';
|
|
103
108
|
const projectMode = meta.project_mode ?? 'brownfield';
|
|
104
109
|
const modeNote = index.mode === 'segmented' ? 'segmented+assemble' : 'monolithic';
|
|
110
|
+
let multiRepoBlock = '';
|
|
111
|
+
const mr = index.multi_repo;
|
|
112
|
+
if (mr?.enabled && mr.repos.length >= 2) {
|
|
113
|
+
const mode = mr.document_mode ?? 'pending';
|
|
114
|
+
const primary = mr.primary_repo ?? '—';
|
|
115
|
+
const repoLines = mr.repos
|
|
116
|
+
.map((r) => `- ${r.label} \`${r.id}/${r.change}\``)
|
|
117
|
+
.join('\n');
|
|
118
|
+
multiRepoBlock = `
|
|
119
|
+
> **多仓范围** (${mode}${mode === 'unified' ? `, 主仓: ${primary}` : ''}):
|
|
120
|
+
${repoLines}
|
|
121
|
+
`;
|
|
122
|
+
}
|
|
105
123
|
return `# 技术方案审批文档: ${index.change}
|
|
106
|
-
|
|
124
|
+
${multiRepoBlock}
|
|
107
125
|
> 本文档由 \`/specflow:approval\` 基于 refine 收敛后的四件套 + 现有代码与 spec 基线生成,
|
|
108
126
|
> 含架构与详细设计等章节,供人工审批使用。AI 闭环/预审结论在对话中反馈,不写入本文。
|
|
109
127
|
> 生成时间: ${index.generated_at} | phase: refined | 产物语言: ${lang} | 技术栈: ${stack} | 项目模式: ${projectMode} | 生成模式: ${modeNote}
|
|
@@ -166,6 +184,42 @@ export async function assembleApprovalDocument(options) {
|
|
|
166
184
|
],
|
|
167
185
|
};
|
|
168
186
|
}
|
|
187
|
+
if (index.multi_repo?.enabled) {
|
|
188
|
+
const mr = index.multi_repo;
|
|
189
|
+
if (!mr.document_mode) {
|
|
190
|
+
diagnostics.push({
|
|
191
|
+
code: 'multi_repo_mode_missing',
|
|
192
|
+
severity: 'error',
|
|
193
|
+
message: 'multi_repo.enabled but document_mode not set; ask user unified vs per_repo before assemble',
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
if (mr.document_mode === 'unified') {
|
|
197
|
+
if (!mr.primary_repo) {
|
|
198
|
+
diagnostics.push({
|
|
199
|
+
code: 'multi_repo_primary_missing',
|
|
200
|
+
severity: 'error',
|
|
201
|
+
message: 'multi_repo.document_mode=unified requires primary_repo',
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
else {
|
|
205
|
+
const primary = mr.repos.find((r) => r.id === mr.primary_repo);
|
|
206
|
+
if (primary && primary.change !== index.change) {
|
|
207
|
+
diagnostics.push({
|
|
208
|
+
code: 'multi_repo_change_mismatch',
|
|
209
|
+
severity: 'warning',
|
|
210
|
+
message: `index.change "${index.change}" differs from primary repo change "${primary.change}"; assemble in primary planning root`,
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
if (mr.document_mode === 'per_repo' && !mr.outputs?.per_repo?.length) {
|
|
216
|
+
diagnostics.push({
|
|
217
|
+
code: 'multi_repo_outputs_missing',
|
|
218
|
+
severity: 'warning',
|
|
219
|
+
message: 'per_repo mode should list outputs.per_repo[] with each repo approval path',
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
}
|
|
169
223
|
const partsDir = getApprovalPartsDir(changeDir);
|
|
170
224
|
try {
|
|
171
225
|
await fs.access(partsDir);
|
|
@@ -29,47 +29,56 @@ export declare const approvalIndexSchema: z.ZodObject<{
|
|
|
29
29
|
name: z.ZodString;
|
|
30
30
|
action: z.ZodString;
|
|
31
31
|
part: z.ZodString;
|
|
32
|
+
repo: z.ZodOptional<z.ZodString>;
|
|
32
33
|
}, "strip", z.ZodTypeAny, {
|
|
33
34
|
id: string;
|
|
34
35
|
name: string;
|
|
35
36
|
action: string;
|
|
36
37
|
part: string;
|
|
38
|
+
repo?: string | undefined;
|
|
37
39
|
}, {
|
|
38
40
|
id: string;
|
|
39
41
|
name: string;
|
|
40
42
|
action: string;
|
|
41
43
|
part: string;
|
|
44
|
+
repo?: string | undefined;
|
|
42
45
|
}>, "many">>;
|
|
43
46
|
interfaces: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
44
47
|
id: z.ZodString;
|
|
45
48
|
short: z.ZodString;
|
|
46
49
|
change: z.ZodString;
|
|
47
50
|
part: z.ZodString;
|
|
51
|
+
repo: z.ZodOptional<z.ZodString>;
|
|
48
52
|
}, "strip", z.ZodTypeAny, {
|
|
49
53
|
id: string;
|
|
50
54
|
part: string;
|
|
51
55
|
short: string;
|
|
52
56
|
change: string;
|
|
57
|
+
repo?: string | undefined;
|
|
53
58
|
}, {
|
|
54
59
|
id: string;
|
|
55
60
|
part: string;
|
|
56
61
|
short: string;
|
|
57
62
|
change: string;
|
|
63
|
+
repo?: string | undefined;
|
|
58
64
|
}>, "many">>;
|
|
59
65
|
pages: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
60
66
|
id: z.ZodString;
|
|
61
67
|
route: z.ZodString;
|
|
62
68
|
apis: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
|
|
63
69
|
part: z.ZodString;
|
|
70
|
+
repo: z.ZodOptional<z.ZodString>;
|
|
64
71
|
}, "strip", z.ZodTypeAny, {
|
|
65
72
|
id: string;
|
|
66
73
|
part: string;
|
|
67
74
|
route: string;
|
|
68
75
|
apis: string[];
|
|
76
|
+
repo?: string | undefined;
|
|
69
77
|
}, {
|
|
70
78
|
id: string;
|
|
71
79
|
part: string;
|
|
72
80
|
route: string;
|
|
81
|
+
repo?: string | undefined;
|
|
73
82
|
apis?: string[] | undefined;
|
|
74
83
|
}>, "many">>;
|
|
75
84
|
capabilities: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
@@ -127,6 +136,172 @@ export declare const approvalIndexSchema: z.ZodObject<{
|
|
|
127
136
|
tech_stack?: string | undefined;
|
|
128
137
|
project_mode?: string | undefined;
|
|
129
138
|
}>>;
|
|
139
|
+
multi_repo: z.ZodOptional<z.ZodEffects<z.ZodObject<{
|
|
140
|
+
enabled: z.ZodBoolean;
|
|
141
|
+
document_mode: z.ZodOptional<z.ZodEnum<["unified", "per_repo"]>>;
|
|
142
|
+
primary_repo: z.ZodOptional<z.ZodString>;
|
|
143
|
+
repos: z.ZodArray<z.ZodObject<{
|
|
144
|
+
id: z.ZodString;
|
|
145
|
+
label: z.ZodString;
|
|
146
|
+
role: z.ZodOptional<z.ZodEnum<["platform", "web", "worker", "hub", "other"]>>;
|
|
147
|
+
change: z.ZodString;
|
|
148
|
+
root_hint: z.ZodOptional<z.ZodString>;
|
|
149
|
+
}, "strip", z.ZodTypeAny, {
|
|
150
|
+
id: string;
|
|
151
|
+
change: string;
|
|
152
|
+
label: string;
|
|
153
|
+
role?: "platform" | "web" | "worker" | "hub" | "other" | undefined;
|
|
154
|
+
root_hint?: string | undefined;
|
|
155
|
+
}, {
|
|
156
|
+
id: string;
|
|
157
|
+
change: string;
|
|
158
|
+
label: string;
|
|
159
|
+
role?: "platform" | "web" | "worker" | "hub" | "other" | undefined;
|
|
160
|
+
root_hint?: string | undefined;
|
|
161
|
+
}>, "many">;
|
|
162
|
+
outputs: z.ZodOptional<z.ZodObject<{
|
|
163
|
+
unified: z.ZodOptional<z.ZodObject<{
|
|
164
|
+
repo: z.ZodString;
|
|
165
|
+
change: z.ZodString;
|
|
166
|
+
path: z.ZodString;
|
|
167
|
+
}, "strip", z.ZodTypeAny, {
|
|
168
|
+
path: string;
|
|
169
|
+
repo: string;
|
|
170
|
+
change: string;
|
|
171
|
+
}, {
|
|
172
|
+
path: string;
|
|
173
|
+
repo: string;
|
|
174
|
+
change: string;
|
|
175
|
+
}>>;
|
|
176
|
+
per_repo: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
177
|
+
repo: z.ZodString;
|
|
178
|
+
change: z.ZodString;
|
|
179
|
+
path: z.ZodString;
|
|
180
|
+
}, "strip", z.ZodTypeAny, {
|
|
181
|
+
path: string;
|
|
182
|
+
repo: string;
|
|
183
|
+
change: string;
|
|
184
|
+
}, {
|
|
185
|
+
path: string;
|
|
186
|
+
repo: string;
|
|
187
|
+
change: string;
|
|
188
|
+
}>, "many">>;
|
|
189
|
+
}, "strip", z.ZodTypeAny, {
|
|
190
|
+
unified?: {
|
|
191
|
+
path: string;
|
|
192
|
+
repo: string;
|
|
193
|
+
change: string;
|
|
194
|
+
} | undefined;
|
|
195
|
+
per_repo?: {
|
|
196
|
+
path: string;
|
|
197
|
+
repo: string;
|
|
198
|
+
change: string;
|
|
199
|
+
}[] | undefined;
|
|
200
|
+
}, {
|
|
201
|
+
unified?: {
|
|
202
|
+
path: string;
|
|
203
|
+
repo: string;
|
|
204
|
+
change: string;
|
|
205
|
+
} | undefined;
|
|
206
|
+
per_repo?: {
|
|
207
|
+
path: string;
|
|
208
|
+
repo: string;
|
|
209
|
+
change: string;
|
|
210
|
+
}[] | undefined;
|
|
211
|
+
}>>;
|
|
212
|
+
}, "strip", z.ZodTypeAny, {
|
|
213
|
+
enabled: boolean;
|
|
214
|
+
repos: {
|
|
215
|
+
id: string;
|
|
216
|
+
change: string;
|
|
217
|
+
label: string;
|
|
218
|
+
role?: "platform" | "web" | "worker" | "hub" | "other" | undefined;
|
|
219
|
+
root_hint?: string | undefined;
|
|
220
|
+
}[];
|
|
221
|
+
document_mode?: "unified" | "per_repo" | undefined;
|
|
222
|
+
primary_repo?: string | undefined;
|
|
223
|
+
outputs?: {
|
|
224
|
+
unified?: {
|
|
225
|
+
path: string;
|
|
226
|
+
repo: string;
|
|
227
|
+
change: string;
|
|
228
|
+
} | undefined;
|
|
229
|
+
per_repo?: {
|
|
230
|
+
path: string;
|
|
231
|
+
repo: string;
|
|
232
|
+
change: string;
|
|
233
|
+
}[] | undefined;
|
|
234
|
+
} | undefined;
|
|
235
|
+
}, {
|
|
236
|
+
enabled: boolean;
|
|
237
|
+
repos: {
|
|
238
|
+
id: string;
|
|
239
|
+
change: string;
|
|
240
|
+
label: string;
|
|
241
|
+
role?: "platform" | "web" | "worker" | "hub" | "other" | undefined;
|
|
242
|
+
root_hint?: string | undefined;
|
|
243
|
+
}[];
|
|
244
|
+
document_mode?: "unified" | "per_repo" | undefined;
|
|
245
|
+
primary_repo?: string | undefined;
|
|
246
|
+
outputs?: {
|
|
247
|
+
unified?: {
|
|
248
|
+
path: string;
|
|
249
|
+
repo: string;
|
|
250
|
+
change: string;
|
|
251
|
+
} | undefined;
|
|
252
|
+
per_repo?: {
|
|
253
|
+
path: string;
|
|
254
|
+
repo: string;
|
|
255
|
+
change: string;
|
|
256
|
+
}[] | undefined;
|
|
257
|
+
} | undefined;
|
|
258
|
+
}>, {
|
|
259
|
+
enabled: boolean;
|
|
260
|
+
repos: {
|
|
261
|
+
id: string;
|
|
262
|
+
change: string;
|
|
263
|
+
label: string;
|
|
264
|
+
role?: "platform" | "web" | "worker" | "hub" | "other" | undefined;
|
|
265
|
+
root_hint?: string | undefined;
|
|
266
|
+
}[];
|
|
267
|
+
document_mode?: "unified" | "per_repo" | undefined;
|
|
268
|
+
primary_repo?: string | undefined;
|
|
269
|
+
outputs?: {
|
|
270
|
+
unified?: {
|
|
271
|
+
path: string;
|
|
272
|
+
repo: string;
|
|
273
|
+
change: string;
|
|
274
|
+
} | undefined;
|
|
275
|
+
per_repo?: {
|
|
276
|
+
path: string;
|
|
277
|
+
repo: string;
|
|
278
|
+
change: string;
|
|
279
|
+
}[] | undefined;
|
|
280
|
+
} | undefined;
|
|
281
|
+
}, {
|
|
282
|
+
enabled: boolean;
|
|
283
|
+
repos: {
|
|
284
|
+
id: string;
|
|
285
|
+
change: string;
|
|
286
|
+
label: string;
|
|
287
|
+
role?: "platform" | "web" | "worker" | "hub" | "other" | undefined;
|
|
288
|
+
root_hint?: string | undefined;
|
|
289
|
+
}[];
|
|
290
|
+
document_mode?: "unified" | "per_repo" | undefined;
|
|
291
|
+
primary_repo?: string | undefined;
|
|
292
|
+
outputs?: {
|
|
293
|
+
unified?: {
|
|
294
|
+
path: string;
|
|
295
|
+
repo: string;
|
|
296
|
+
change: string;
|
|
297
|
+
} | undefined;
|
|
298
|
+
per_repo?: {
|
|
299
|
+
path: string;
|
|
300
|
+
repo: string;
|
|
301
|
+
change: string;
|
|
302
|
+
}[] | undefined;
|
|
303
|
+
} | undefined;
|
|
304
|
+
}>>;
|
|
130
305
|
}, "strip", z.ZodTypeAny, {
|
|
131
306
|
schema: "specflow.approval.index/v1";
|
|
132
307
|
conventions: {
|
|
@@ -154,18 +329,21 @@ export declare const approvalIndexSchema: z.ZodObject<{
|
|
|
154
329
|
name: string;
|
|
155
330
|
action: string;
|
|
156
331
|
part: string;
|
|
332
|
+
repo?: string | undefined;
|
|
157
333
|
}[];
|
|
158
334
|
interfaces: {
|
|
159
335
|
id: string;
|
|
160
336
|
part: string;
|
|
161
337
|
short: string;
|
|
162
338
|
change: string;
|
|
339
|
+
repo?: string | undefined;
|
|
163
340
|
}[];
|
|
164
341
|
pages: {
|
|
165
342
|
id: string;
|
|
166
343
|
part: string;
|
|
167
344
|
route: string;
|
|
168
345
|
apis: string[];
|
|
346
|
+
repo?: string | undefined;
|
|
169
347
|
}[];
|
|
170
348
|
capabilities: {
|
|
171
349
|
id: string;
|
|
@@ -182,6 +360,30 @@ export declare const approvalIndexSchema: z.ZodObject<{
|
|
|
182
360
|
tech_stack?: string | undefined;
|
|
183
361
|
project_mode?: string | undefined;
|
|
184
362
|
} | undefined;
|
|
363
|
+
multi_repo?: {
|
|
364
|
+
enabled: boolean;
|
|
365
|
+
repos: {
|
|
366
|
+
id: string;
|
|
367
|
+
change: string;
|
|
368
|
+
label: string;
|
|
369
|
+
role?: "platform" | "web" | "worker" | "hub" | "other" | undefined;
|
|
370
|
+
root_hint?: string | undefined;
|
|
371
|
+
}[];
|
|
372
|
+
document_mode?: "unified" | "per_repo" | undefined;
|
|
373
|
+
primary_repo?: string | undefined;
|
|
374
|
+
outputs?: {
|
|
375
|
+
unified?: {
|
|
376
|
+
path: string;
|
|
377
|
+
repo: string;
|
|
378
|
+
change: string;
|
|
379
|
+
} | undefined;
|
|
380
|
+
per_repo?: {
|
|
381
|
+
path: string;
|
|
382
|
+
repo: string;
|
|
383
|
+
change: string;
|
|
384
|
+
}[] | undefined;
|
|
385
|
+
} | undefined;
|
|
386
|
+
} | undefined;
|
|
185
387
|
}, {
|
|
186
388
|
schema: "specflow.approval.index/v1";
|
|
187
389
|
change: string;
|
|
@@ -209,17 +411,20 @@ export declare const approvalIndexSchema: z.ZodObject<{
|
|
|
209
411
|
name: string;
|
|
210
412
|
action: string;
|
|
211
413
|
part: string;
|
|
414
|
+
repo?: string | undefined;
|
|
212
415
|
}[] | undefined;
|
|
213
416
|
interfaces?: {
|
|
214
417
|
id: string;
|
|
215
418
|
part: string;
|
|
216
419
|
short: string;
|
|
217
420
|
change: string;
|
|
421
|
+
repo?: string | undefined;
|
|
218
422
|
}[] | undefined;
|
|
219
423
|
pages?: {
|
|
220
424
|
id: string;
|
|
221
425
|
part: string;
|
|
222
426
|
route: string;
|
|
427
|
+
repo?: string | undefined;
|
|
223
428
|
apis?: string[] | undefined;
|
|
224
429
|
}[] | undefined;
|
|
225
430
|
capabilities?: {
|
|
@@ -237,6 +442,30 @@ export declare const approvalIndexSchema: z.ZodObject<{
|
|
|
237
442
|
tech_stack?: string | undefined;
|
|
238
443
|
project_mode?: string | undefined;
|
|
239
444
|
} | undefined;
|
|
445
|
+
multi_repo?: {
|
|
446
|
+
enabled: boolean;
|
|
447
|
+
repos: {
|
|
448
|
+
id: string;
|
|
449
|
+
change: string;
|
|
450
|
+
label: string;
|
|
451
|
+
role?: "platform" | "web" | "worker" | "hub" | "other" | undefined;
|
|
452
|
+
root_hint?: string | undefined;
|
|
453
|
+
}[];
|
|
454
|
+
document_mode?: "unified" | "per_repo" | undefined;
|
|
455
|
+
primary_repo?: string | undefined;
|
|
456
|
+
outputs?: {
|
|
457
|
+
unified?: {
|
|
458
|
+
path: string;
|
|
459
|
+
repo: string;
|
|
460
|
+
change: string;
|
|
461
|
+
} | undefined;
|
|
462
|
+
per_repo?: {
|
|
463
|
+
path: string;
|
|
464
|
+
repo: string;
|
|
465
|
+
change: string;
|
|
466
|
+
}[] | undefined;
|
|
467
|
+
} | undefined;
|
|
468
|
+
} | undefined;
|
|
240
469
|
}>;
|
|
241
470
|
export declare function parseApprovalIndex(raw: unknown): ApprovalIndex;
|
|
242
471
|
/** Returns true when segmented fast-path (monolithic) is allowed. */
|
|
@@ -4,23 +4,78 @@ const tableRefSchema = z.object({
|
|
|
4
4
|
name: z.string().min(1),
|
|
5
5
|
action: z.string().min(1),
|
|
6
6
|
part: z.string().min(1),
|
|
7
|
+
repo: z.string().min(1).optional(),
|
|
7
8
|
});
|
|
8
9
|
const interfaceRefSchema = z.object({
|
|
9
10
|
id: z.string().min(1),
|
|
10
11
|
short: z.string().min(1),
|
|
11
12
|
change: z.string().min(1),
|
|
12
13
|
part: z.string().min(1),
|
|
14
|
+
repo: z.string().min(1).optional(),
|
|
13
15
|
});
|
|
14
16
|
const pageRefSchema = z.object({
|
|
15
17
|
id: z.string().min(1),
|
|
16
18
|
route: z.string().min(1),
|
|
17
19
|
apis: z.array(z.string()).default([]),
|
|
18
20
|
part: z.string().min(1),
|
|
21
|
+
repo: z.string().min(1).optional(),
|
|
19
22
|
});
|
|
20
23
|
const capabilityRefSchema = z.object({
|
|
21
24
|
id: z.string().min(1),
|
|
22
25
|
part: z.string().min(1),
|
|
23
26
|
});
|
|
27
|
+
const outputRefSchema = z.object({
|
|
28
|
+
repo: z.string().min(1),
|
|
29
|
+
change: z.string().min(1),
|
|
30
|
+
path: z.string().min(1),
|
|
31
|
+
});
|
|
32
|
+
const multiRepoSchema = z
|
|
33
|
+
.object({
|
|
34
|
+
enabled: z.boolean(),
|
|
35
|
+
document_mode: z.enum(['unified', 'per_repo']).optional(),
|
|
36
|
+
primary_repo: z.string().min(1).optional(),
|
|
37
|
+
repos: z
|
|
38
|
+
.array(z.object({
|
|
39
|
+
id: z.string().min(1),
|
|
40
|
+
label: z.string().min(1),
|
|
41
|
+
role: z.enum(['platform', 'web', 'worker', 'hub', 'other']).optional(),
|
|
42
|
+
change: z.string().min(1),
|
|
43
|
+
root_hint: z.string().min(1).optional(),
|
|
44
|
+
}))
|
|
45
|
+
.min(1),
|
|
46
|
+
outputs: z
|
|
47
|
+
.object({
|
|
48
|
+
unified: outputRefSchema.optional(),
|
|
49
|
+
per_repo: z.array(outputRefSchema).optional(),
|
|
50
|
+
})
|
|
51
|
+
.optional(),
|
|
52
|
+
})
|
|
53
|
+
.superRefine((value, ctx) => {
|
|
54
|
+
if (!value.enabled) {
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
if (value.repos.length < 2) {
|
|
58
|
+
ctx.addIssue({
|
|
59
|
+
code: z.ZodIssueCode.custom,
|
|
60
|
+
message: 'multi_repo.repos must have at least 2 entries when enabled',
|
|
61
|
+
path: ['repos'],
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
if (value.document_mode === 'unified' && !value.primary_repo) {
|
|
65
|
+
ctx.addIssue({
|
|
66
|
+
code: z.ZodIssueCode.custom,
|
|
67
|
+
message: 'multi_repo.primary_repo is required when document_mode is unified',
|
|
68
|
+
path: ['primary_repo'],
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
if (value.primary_repo && !value.repos.some((r) => r.id === value.primary_repo)) {
|
|
72
|
+
ctx.addIssue({
|
|
73
|
+
code: z.ZodIssueCode.custom,
|
|
74
|
+
message: 'multi_repo.primary_repo must match a repos[].id',
|
|
75
|
+
path: ['primary_repo'],
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
});
|
|
24
79
|
export const approvalIndexSchema = z.object({
|
|
25
80
|
schema: z.literal('specflow.approval.index/v1'),
|
|
26
81
|
change: z.string().min(1),
|
|
@@ -71,6 +126,7 @@ export const approvalIndexSchema = z.object({
|
|
|
71
126
|
project_mode: z.string().optional(),
|
|
72
127
|
})
|
|
73
128
|
.optional(),
|
|
129
|
+
multi_repo: multiRepoSchema.optional(),
|
|
74
130
|
});
|
|
75
131
|
export function parseApprovalIndex(raw) {
|
|
76
132
|
return approvalIndexSchema.parse(raw);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export type { ApprovalAssembleResult, ApprovalDiagnostic, ApprovalIndex, ApprovalManifest, ApprovalGenerationMode, } from './types.js';
|
|
1
|
+
export type { ApprovalAssembleResult, ApprovalDiagnostic, ApprovalIndex, ApprovalManifest, ApprovalGenerationMode, ApprovalMultiRepo, ApprovalDocumentMode, } from './types.js';
|
|
2
2
|
export { parseApprovalIndex, isLightweightApproval, shouldForceSegmented, approvalIndexSchema, } from './index-schema.js';
|
|
3
3
|
export { getChangeDirectory, getApprovalWorkspaceDir, getApprovalIndexPath, getApprovalManifestPath, getApprovalPartsDir, getApprovalPartPath, getApprovalOutputPath, getApprovalAnalysisPath, } from './paths.js';
|
|
4
4
|
export { assembleApprovalDocument, readApprovalIndex, readApprovalManifest, } from './assemble.js';
|
|
@@ -11,23 +11,50 @@ export interface ApprovalBatching {
|
|
|
11
11
|
readonly pages_per_call: number;
|
|
12
12
|
readonly capabilities_per_call: number;
|
|
13
13
|
}
|
|
14
|
+
export type ApprovalRepoRole = 'platform' | 'web' | 'worker' | 'hub' | 'other';
|
|
15
|
+
export type ApprovalDocumentMode = 'unified' | 'per_repo';
|
|
16
|
+
export interface ApprovalMultiRepoEntry {
|
|
17
|
+
readonly id: string;
|
|
18
|
+
readonly label: string;
|
|
19
|
+
readonly role?: ApprovalRepoRole;
|
|
20
|
+
readonly change: string;
|
|
21
|
+
readonly root_hint?: string;
|
|
22
|
+
}
|
|
23
|
+
export interface ApprovalOutputRef {
|
|
24
|
+
readonly repo: string;
|
|
25
|
+
readonly change: string;
|
|
26
|
+
readonly path: string;
|
|
27
|
+
}
|
|
28
|
+
export interface ApprovalMultiRepo {
|
|
29
|
+
readonly enabled: boolean;
|
|
30
|
+
readonly document_mode?: ApprovalDocumentMode;
|
|
31
|
+
readonly primary_repo?: string;
|
|
32
|
+
readonly repos: readonly ApprovalMultiRepoEntry[];
|
|
33
|
+
readonly outputs?: {
|
|
34
|
+
readonly unified?: ApprovalOutputRef;
|
|
35
|
+
readonly per_repo?: readonly ApprovalOutputRef[];
|
|
36
|
+
};
|
|
37
|
+
}
|
|
14
38
|
export interface ApprovalTableRef {
|
|
15
39
|
readonly id: string;
|
|
16
40
|
readonly name: string;
|
|
17
41
|
readonly action: string;
|
|
18
42
|
readonly part: string;
|
|
43
|
+
readonly repo?: string;
|
|
19
44
|
}
|
|
20
45
|
export interface ApprovalInterfaceRef {
|
|
21
46
|
readonly id: string;
|
|
22
47
|
readonly short: string;
|
|
23
48
|
readonly change: string;
|
|
24
49
|
readonly part: string;
|
|
50
|
+
readonly repo?: string;
|
|
25
51
|
}
|
|
26
52
|
export interface ApprovalPageRef {
|
|
27
53
|
readonly id: string;
|
|
28
54
|
readonly route: string;
|
|
29
55
|
readonly apis: readonly string[];
|
|
30
56
|
readonly part: string;
|
|
57
|
+
readonly repo?: string;
|
|
31
58
|
}
|
|
32
59
|
export interface ApprovalCapabilityRef {
|
|
33
60
|
readonly id: string;
|
|
@@ -57,6 +84,7 @@ export interface ApprovalIndex {
|
|
|
57
84
|
readonly capabilities: readonly ApprovalCapabilityRef[];
|
|
58
85
|
readonly batching: ApprovalBatching;
|
|
59
86
|
readonly conventions: ApprovalConventionsResolved;
|
|
87
|
+
readonly multi_repo?: ApprovalMultiRepo;
|
|
60
88
|
readonly meta?: {
|
|
61
89
|
readonly language?: string;
|
|
62
90
|
readonly tech_stack?: string;
|
package/package.json
CHANGED
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
# Approval · API / RPC / OpenAPI Guidance
|
|
2
|
+
|
|
3
|
+
> Used by `/specflow:approval` before drafting **§4.5 接口设计** (after
|
|
4
|
+
> `project-conventions-guidance.md` `topic=api`).
|
|
5
|
+
>
|
|
6
|
+
> **目的**:产出**可生成**的接口契约(非 narrative stub),并避免 **Worker 面 HTTP** 与 **平台内部 RPC**
|
|
7
|
+
> 混写导致某一侧被省略。
|
|
8
|
+
>
|
|
9
|
+
> **优先级**:项目约定 + 现网 proto/OpenAPI **>** 本路由 **>** LLM。
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## 0. When to run
|
|
14
|
+
|
|
15
|
+
| 条件 | 必须执行 |
|
|
16
|
+
|------|----------|
|
|
17
|
+
| 本迭代有 **新增** HTTP 路径或 gRPC | 全文 §1–§4 |
|
|
18
|
+
| 本迭代 **修改/行为扩展** 对外或 Worker 契约 | §1 分层 + §2 锁名 + §3 变更 delta |
|
|
19
|
+
| 仅 **不变·本迭代消费** 既有 RPC/HTTP | §1 分层 + §4 完整骨架(字段/示例/错误);禁止一句带过 |
|
|
20
|
+
| 三仓/多仓合订 | §1 **每层每面一个 `In`**,禁止用「内部调用」合并条目 |
|
|
21
|
+
|
|
22
|
+
Announce after Read:
|
|
23
|
+
|
|
24
|
+
```text
|
|
25
|
+
API guidance: layering=<n surfaces> | proto=<path|draft in §4.5> | worker_openapi=<path|must draft>
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
## 1. 接口分层 — 禁止混层 (fixes B2)
|
|
31
|
+
|
|
32
|
+
一条业务链路上常有 **多个契约面**,每一面在 §4.5 清单中必须是 **独立 `In`**,各自完整展开:
|
|
33
|
+
|
|
34
|
+
| 层 | 典型调用方 → 被调方 | 协议形态 | 清单命名示例 | 常见错误 |
|
|
35
|
+
|----|---------------------|----------|--------------|----------|
|
|
36
|
+
| L1 控制台/北向 | Web → Gateway | HTTP `/api/v1/...` | I1 调试启动 | 把字段只写在 design,§4.5 无表 |
|
|
37
|
+
| L2 Worker 边车 HTTP | Worker → Gateway | HTTP `/internal/v1/...` + work 鉴权头 | I4 逐步上报 | **只写路径,无 OpenAPI schema** |
|
|
38
|
+
| L3 平台编排 RPC | Gateway/调度 → 本服务 logic | gRPC + `google.api.http` | I4 对应 RPC(与 L2 同编号或 I4a/I4b 成对) | 写「暂定 RPC,HTTP 冻结」—— **禁止** |
|
|
39
|
+
| L4 平台东向 RPC | 调度 → result/testcase/… | gRPC `client/<svc>/` | I6/I7 确保壳/写入逐步 | **用一句「内部调用 I7」代替 I4 的 RPC 详设** |
|
|
40
|
+
|
|
41
|
+
### 1.1 硬规则
|
|
42
|
+
|
|
43
|
+
1. **L2 与 L3 成对**:新增 Worker HTTP 路径时,**必须**同时写清:
|
|
44
|
+
- L2:HTTP method/path、必填头、request/response JSON 字段表、失败示例
|
|
45
|
+
- L3:冻结 RPC 全名、`message` 字段号、`option (google.api.http)`、Gateway 注册点
|
|
46
|
+
2. **L4 不得 stub**:列入清单的东向 RPC(即使 proto 已存在)仍须 **§4.5.2 完整骨架**;
|
|
47
|
+
可标注「与现网 proto 一致」但**必须**贴字段表 + 成功/失败示例 + 错误表。
|
|
48
|
+
3. **禁止**在 L2 小节写满 HTTP,再在 L4 用「经 client/result 调 I7」一行替代 I7 详设。
|
|
49
|
+
4. **调用关系图**须显式画出 L2→L3→L4,例如:
|
|
50
|
+
`Worker --L2 HTTP--> Gateway --L3 RPC--> Scheduler --L4 RPC--> Result`
|
|
51
|
+
|
|
52
|
+
### 1.2 三仓合订
|
|
53
|
+
|
|
54
|
+
| 仓 | 负责的层 | §4.5 须写 |
|
|
55
|
+
|----|----------|-----------|
|
|
56
|
+
| talos | L3/L4 + Gateway 注册 | proto 草案 + 注册文件/函数 |
|
|
57
|
+
| talos-worker | L2 | OpenAPI path + schema + 鉴权头示例 |
|
|
58
|
+
| talos-web | L1 | HTTP 消费 + TS 类型(§4.6 交叉引用 `In`) |
|
|
59
|
+
|
|
60
|
+
合订文档 **禁止**只写 talos 侧 L4 而 Worker OpenAPI 写「apply 时补」。
|
|
61
|
+
|
|
62
|
+
---
|
|
63
|
+
|
|
64
|
+
## 2. RPC / 服务名冻结 (fixes A1)
|
|
65
|
+
|
|
66
|
+
### 2.1 禁止用语 (硬)
|
|
67
|
+
|
|
68
|
+
下列写法 **一律禁止**出现在 §4.5 / §2 决策 / §4.8:
|
|
69
|
+
|
|
70
|
+
- `暂定` / `TBD` / `实现时命名` / `实现时与 OpenAPI 对齐`
|
|
71
|
+
- `如 SubmitXxx` / `例如 scheduler.SubmitXxx`
|
|
72
|
+
- `RPC 名以实现为准`
|
|
73
|
+
|
|
74
|
+
未锁名 → 视为 **BLOCKED**,不得标 READY;须 `[待 refine 澄清: RPC 名]` 或写入锁定名。
|
|
75
|
+
|
|
76
|
+
### 2.2 必须输出
|
|
77
|
+
|
|
78
|
+
| 项 | 格式 |
|
|
79
|
+
|----|------|
|
|
80
|
+
| 服务 | `package` 名,如 `scheduler` |
|
|
81
|
+
| RPC | **冻结**全名,如 `scheduler.SubmitStepResult`(无「如」) |
|
|
82
|
+
| HTTP | method + 完整 path(可含 `{job_id}`) |
|
|
83
|
+
| proto 文件 | 相对路径,如 `proto/scheduler/scheduler.proto` |
|
|
84
|
+
|
|
85
|
+
决策表新增行时使用 **Proposed → Accepted** 仅当 RPC 名已在 §4.5 冻结。
|
|
86
|
+
|
|
87
|
+
---
|
|
88
|
+
|
|
89
|
+
## 3. 可生成契约包 (fixes A2)
|
|
90
|
+
|
|
91
|
+
对 **新增** 或 **修改 HTTP 绑定** 的 RPC,§4.5 **必须**含 **「Proto / OpenAPI 契约包」** 块(可用 `**Proto 草案**` 加粗标签,非标题):
|
|
92
|
+
|
|
93
|
+
### 3.1 Proto 最小集
|
|
94
|
+
|
|
95
|
+
```protobuf
|
|
96
|
+
// 文件: proto/<svc>/<svc>.proto
|
|
97
|
+
rpc SubmitStepResult(SubmitStepResultReq) returns (SubmitStepResultResp) {
|
|
98
|
+
option (google.api.http) = {
|
|
99
|
+
post: "/internal/v1/tasks/{job_id}/step-results"
|
|
100
|
+
body: "*"
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
message SubmitStepResultReq {
|
|
104
|
+
int32 schema_version = 1;
|
|
105
|
+
string request_id = 2;
|
|
106
|
+
// … 每个字段必须有号与类型
|
|
107
|
+
}
|
|
108
|
+
message SubmitStepResultResp {
|
|
109
|
+
int32 schema_version = 1;
|
|
110
|
+
string request_id = 2;
|
|
111
|
+
bool accepted = 3;
|
|
112
|
+
bool replayed = 4;
|
|
113
|
+
}
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
要求:
|
|
117
|
+
|
|
118
|
+
- 每个字段 **字段号 + 类型 + json_name**(若与 snake 不同)
|
|
119
|
+
- `google.api.http` **完整** post/get/path/body
|
|
120
|
+
- enum/status **闭合**,禁止「等」;与现网 ingest 白名单对齐(例:逐步 ingest 仅四终态)
|
|
121
|
+
|
|
122
|
+
### 3.2 Gateway 注册 (硬)
|
|
123
|
+
|
|
124
|
+
必须写明 **其一**:
|
|
125
|
+
|
|
126
|
+
- 「与 `ReportProgress` / `SubmitResult` **同一**注册点:`<文件路径>` 函数 `<Name>`」
|
|
127
|
+
- 或「ProtoSets 清单 `<path>` 新增 `<proto>` 条目」
|
|
128
|
+
|
|
129
|
+
禁止只写「网关 ProtoSet 含新绑定」。
|
|
130
|
+
|
|
131
|
+
### 3.3 Worker OpenAPI 交叉引用
|
|
132
|
+
|
|
133
|
+
- 路径必须在 `talos-worker/contracts/v1/http.openapi.yaml`(或项目约定路径)**给出 schema 草案**
|
|
134
|
+
- 必填头须与现网 progress/result **同构列出**:
|
|
135
|
+
`Idempotency-Key`, `Authorization-date`, `Authorization: TALOS-WORK-1:...`, `Content-Type`, `Accept`
|
|
136
|
+
- HTTP 请求示例 **必须含上述头**,不得只有 JSON body
|
|
137
|
+
|
|
138
|
+
### 3.4 错误映射
|
|
139
|
+
|
|
140
|
+
| 条件 | gRPC `codes.*` | HTTP | 禁止 |
|
|
141
|
+
|------|----------------|------|------|
|
|
142
|
+
| 租约无效 | `FailedPrecondition` 或项目约定 **唯一**值 | **唯一** 403 或 409 | `403/409(与 progress 同类)` |
|
|
143
|
+
|
|
144
|
+
---
|
|
145
|
+
|
|
146
|
+
## 4. §4.5 逐接口最低深度 (含不变)
|
|
147
|
+
|
|
148
|
+
每个 `In` **必须**含 generate.md §4.5.2 全部加粗块;额外:
|
|
149
|
+
|
|
150
|
+
| 变更类型 | 额外要求 |
|
|
151
|
+
|----------|----------|
|
|
152
|
+
| 新增 L2+L3 | §3 契约包 + Gateway 注册 + Worker OpenAPI 指针 |
|
|
153
|
+
| 修改 | 字段号/changelog + 兼容缺省 |
|
|
154
|
+
| 不变·本迭代消费 | 从现网 proto/OpenAPI **摘录**字段表+示例;标注源路径;仍须失败示例 |
|
|
155
|
+
| 不变·协议不变 | 同上;禁止「详见 OpenAPI」无正文 |
|
|
156
|
+
|
|
157
|
+
---
|
|
158
|
+
|
|
159
|
+
## 5. 自检 (生成后必过)
|
|
160
|
+
|
|
161
|
+
- [ ] 清单中每个契约面有独立 `In`,无 L2/L4 混写 stub
|
|
162
|
+
- [ ] 无「暂定/如/实现时」RPC 措辞
|
|
163
|
+
- [ ] 新增 RPC 有 proto 字段号 + http option + 注册点
|
|
164
|
+
- [ ] Worker HTTP 示例含完整 work 鉴权头
|
|
165
|
+
- [ ] status/enum 闭合,与 ingest/现网校验一致
|
|
166
|
+
- [ ] 错误表无「或」「同类映射」模糊语
|
|
167
|
+
- [ ] L4 东向 RPC 非一句话 stub
|
|
168
|
+
|
|
169
|
+
---
|
|
170
|
+
|
|
171
|
+
## 6. 与 database / frontend guidance 对称
|
|
172
|
+
|
|
173
|
+
| 包 | 路由 | 章节 |
|
|
174
|
+
|----|------|------|
|
|
175
|
+
| database | `database-guidance.md` | §4.4 |
|
|
176
|
+
| frontend | `frontend-guidance.md` | §4.6 |
|
|
177
|
+
| **api** | **本文件** | **§4.5** |
|
|
178
|
+
|
|
179
|
+
`project-conventions-guidance.md` `topic=api` 解决「项目禁令与风格」;本文件解决「可生成契约与分层」。
|
|
@@ -20,8 +20,9 @@ Before running this flow, the SKILL.md has confirmed:
|
|
|
20
20
|
- Tech Stack Intake gate completed when greenfield or stack dimensions are missing
|
|
21
21
|
(user confirmed 前端 / 后端 / 数据库与缓存 / 基础设施, or marked「不涉及」)
|
|
22
22
|
- `uiInScope` resolved; when yes, FE 五元组 confirmed (or marked pending refine)
|
|
23
|
-
- Anchor files extracted from `
|
|
23
|
+
- Anchor files extracted from **each in-scope repo** when `multi_repo.enabled` (Stage 1b)
|
|
24
24
|
- Optional: `specflow/specs/` baseline exists (for Pass 7)
|
|
25
|
+
- When multi-repo: `multi_repo.document_mode` + (`primary_repo` if unified) confirmed by user
|
|
25
26
|
|
|
26
27
|
You MUST read, in this order, before any analysis:
|
|
27
28
|
|
|
@@ -37,6 +38,11 @@ You MUST read, in this order, before any analysis:
|
|
|
37
38
|
Do not skip any of the four required artifacts. If any is missing, stop and tell the
|
|
38
39
|
SKILL.md caller which are missing.
|
|
39
40
|
|
|
41
|
+
When **`multi_repo.enabled=true`**, read **`prompts/approval/multi-repo-guidance.md`** first.
|
|
42
|
+
Load four artifacts **from every repo** in `multi_repo.repos[]` before analysis. Unified mode
|
|
43
|
+
merges cross-repo content into **one** document stored under **`primary_repo`**; per_repo mode
|
|
44
|
+
writes **separate** `approval.md` per repo (see §G).
|
|
45
|
+
|
|
40
46
|
---
|
|
41
47
|
|
|
42
48
|
## Part A: Closed-Loop Verification (7 Passes)
|
|
@@ -809,7 +815,7 @@ CREATE TABLE `orders` (
|
|
|
809
815
|
**适用范围**:暴露 API / RPC / CLI 命令 / 跨模块函数接口的变更(含「协议不变但本迭代新消费」)。
|
|
810
816
|
**项目类型适配**:Web/服务 → HTTP(+RPC);CLI → commander 等命令参数;库 → 导出函数签名。
|
|
811
817
|
|
|
812
|
-
> **项目约定(先于起草)**:执行 `prompts/approval/project-conventions-guidance.md`,`topic=api
|
|
818
|
+
> **项目约定(先于起草)**:执行 `prompts/approval/project-conventions-guidance.md`,`topic=api`;再执行 **`prompts/approval/api-guidance.md`**(分层、RPC 锁名、proto/OpenAPI 可生成包)。
|
|
813
819
|
> 懒加载项目 API/错误码/鉴权/契约约定。有 UI 时 **§4.6** 另跑 `topic=frontend` + `frontend-guidance.md`
|
|
814
820
|
> (勿把页面树塞进 §4.5)。
|
|
815
821
|
> **优先级**:项目约定 + 现网 OpenAPI/proto **>** SpecFlow §4.5 骨架 **>** LLM。
|
|
@@ -859,6 +865,17 @@ CREATE TABLE `orders` (
|
|
|
859
865
|
|
|
860
866
|
> **清单规则(硬)**:本迭代**不调用、不消费**的接口 **不列入** §4.5 清单。**一旦列入清单,无论变更类型是否为「不变」,均须按 §4.5.2 完整骨架输出**,不得精简、不得只写路径、不得跳过示例。
|
|
861
867
|
|
|
868
|
+
> **分层规则(硬 — 防 B2 类混写)**:同一业务链上的 **Worker HTTP(L2)**、**平台 gRPC+HTTP 绑定(L3)**、**东向 client RPC(L4)** 必须是 **独立 `In`**,各自完整展开。禁止在 L2 写满 HTTP 后,用「内部经 I6/I7 调用」一行代替 L4 详设。见 `api-guidance.md` §1。
|
|
869
|
+
|
|
870
|
+
**2b) 契约面分层(合订/三端推荐)**:
|
|
871
|
+
|
|
872
|
+
| 层 | 面 | 清单要求 |
|
|
873
|
+
|----|-----|----------|
|
|
874
|
+
| L1 | 控制台 HTTP | 字段表 + 示例 + 错误 |
|
|
875
|
+
| L2 | Worker→Gateway HTTP | OpenAPI schema 草案 + **完整** work 鉴权头示例 |
|
|
876
|
+
| L3 | Gateway 转码 RPC | **冻结** RPC 名 + proto 字段号 + `google.api.http` + **Gateway 注册点** |
|
|
877
|
+
| L4 | 平台 `client/<svc>/` RPC | 完整骨架;不变亦须字段表+示例,禁止 stub |
|
|
878
|
+
|
|
862
879
|
**3) 通用错误码约定**(强制;按项目现网风格映射):
|
|
863
880
|
|
|
864
881
|
| 错误类别 / 状态 | 典型 HTTP 或退出码 | 含义(本迭代) |
|
|
@@ -876,6 +893,8 @@ CREATE TABLE `orders` (
|
|
|
876
893
|
3. **幂等 / 终态语义**:若存在上报类接口,写清「成功 ≠ 资源终态」等不变量(对齐 §2 决策)。
|
|
877
894
|
4. **兼容缺省**:可选字段缺省时的兼容行为写进字段表「默认」列。
|
|
878
895
|
5. **与流程对齐**:接口编号可被 §4.2/§4.3 时序与 §6 测试引用。
|
|
896
|
+
6. **RPC 锁名(硬)**:新增/修改 gRPC **禁止**「暂定」「如 XxxRpc」「实现时命名/对齐」。须写死 `package.ServiceMethod`,并在决策表或 §4.5 元信息一致。见 `api-guidance.md` §2。
|
|
897
|
+
7. **错误映射唯一(硬)**:条件 → **唯一** gRPC code → **唯一** HTTP;禁止「403/409(与 progress 同类)」「或现网」。
|
|
879
898
|
|
|
880
899
|
##### 4.5.2 逐接口详设(强制骨架)
|
|
881
900
|
|
|
@@ -888,8 +907,9 @@ CREATE TABLE `orders` (
|
|
|
888
907
|
| 应用场景 | 谁、在什么用户动作/系统时机下调用 |
|
|
889
908
|
| 协议 | 方法 + 路径(或 CLI 命令 / 导出函数签名) |
|
|
890
909
|
| Content-Type / 编码 | 如 `application/json`(若适用) |
|
|
891
|
-
| 对应 RPC / 内部名 |
|
|
910
|
+
| 对应 RPC / 内部名 | **冻结**全名(例 `scheduler.SubmitStepResult`);proto 路径;**禁止**暂定/如/实现时 |
|
|
892
911
|
| 鉴权 | 本接口鉴权要点(可引用通道表) |
|
|
912
|
+
| Gateway 注册 | 新增 HTTP 绑定时:**文件+函数名**或 ProtoSets 路径(禁止只写「含新绑定」) |
|
|
893
913
|
| 本迭代变更 | 一句话(新增字段 / 行为扩展 / 不变仅消费 …) |
|
|
894
914
|
|
|
895
915
|
2. **请求体字段**(有则写;路径参数 / Query / CLI flags 用同级加粗标签分块,如 `**Query 参数**`,仍**不要**升为标题):
|
|
@@ -912,6 +932,13 @@ CREATE TABLE `orders` (
|
|
|
912
932
|
|
|
913
933
|
7. **处理顺序**(可选):多步服务端合同用编号列表;与 §4.2/§4.3、§4.4 对齐。
|
|
914
934
|
|
|
935
|
+
8. **Proto / OpenAPI 契约包**( **新增** 或 **修改 HTTP 绑定** 的 RPC — 硬):在接口小节末尾用加粗标签 `**Proto 草案**` / `**OpenAPI 指针**` 输出:
|
|
936
|
+
- proto:`rpc` 全名 + `message` 字段号表 + `google.api.http` 原文
|
|
937
|
+
- Gateway:与现网同类 RPC **同一注册点**的文件/函数,或 ProtoSets 条目
|
|
938
|
+
- Worker:OpenAPI 路径 + schema 字段与 L3 一致;HTTP 示例含 **全部** 必填头
|
|
939
|
+
- status/enum **闭合**,与现网 ingest 校验一致(禁止「等」、禁止逐步 ingest 写 `running` 除非同步改白名单)
|
|
940
|
+
详见 `api-guidance.md` §3。
|
|
941
|
+
|
|
915
942
|
##### 4.5.3 调用关系(推荐)
|
|
916
943
|
|
|
917
944
|
用短文本或 Mermaid 概括调用方如何串起 `I1…In`(主路径一条线即可),便于实现与联调对照。
|
|
@@ -925,6 +952,8 @@ CREATE TABLE `orders` (
|
|
|
925
952
|
|
|
926
953
|
- [ ] 有通道/鉴权表(或多通道说明)+ 本迭代接口清单(编号+变更类型+场景)
|
|
927
954
|
- [ ] 有通用错误码约定 + 命名/错误风格约定
|
|
955
|
+
- [ ] **分层**:L2 Worker HTTP / L3 RPC / L4 东向 RPC 各有独立 `In`,无「内部调用」stub
|
|
956
|
+
- [ ] **RPC 已冻结**:无暂定/如/实现时;新增 RPC 含 proto 字段号 + http option + Gateway 注册点
|
|
928
957
|
- [ ] 清单中**每个**接口(含**不变**)具备:元信息、字段表、成功请求/响应示例、**失败示例(G2)**、错误表
|
|
929
958
|
- [ ] **不变**接口未因「无协议变更」而省略字段表/示例;内容与现网契约或锚点一致
|
|
930
959
|
- [ ] 无「只有路径、无字段/无示例/无错误」的偷懒写法;示例与字段表一致
|
|
@@ -1312,14 +1341,16 @@ specflow init --artifact-language <language>
|
|
|
1312
1341
|
(d) 每张表字段说明表(含「本迭代用法」)。
|
|
1313
1342
|
禁止仅用散文描述 schema。MySQL DDL 禁止省略 ENGINE/CHARSET。零 DDL 迭代仍须展示当前基线 DDL —— 当表处于读/写路径时,禁止声称「不涉及数据库」。
|
|
1314
1343
|
|
|
1315
|
-
10. **§4.5 接口章节质量(硬规则)
|
|
1344
|
+
10. **§4.5 接口章节质量(硬规则)**:起草前执行 `project-conventions-guidance.md` `topic=api` + **`api-guidance.md`**.若变更涉及对外/跨服务/跨模块接口(含新增、修改、行为扩展、**协议不变但本迭代消费或调用**),§4.5 **必须**包含:
|
|
1316
1345
|
(a) 调用方/通道 + 鉴权总览,
|
|
1317
1346
|
(b) 编号稳定的接口清单(变更类型含 新增/修改/行为扩展/不变·本迭代消费/不变·协议不变),
|
|
1318
|
-
(c)
|
|
1319
|
-
(d)
|
|
1320
|
-
|
|
1321
|
-
(f)
|
|
1322
|
-
|
|
1347
|
+
(c) **契约面分层**:Worker HTTP / 平台 RPC+HTTP 绑定 / 东向 RPC **各自独立 `In`**,禁止 L2 详写、L4 一句 stub,
|
|
1348
|
+
(d) 通用错误码映射(**唯一** gRPC code → **唯一** HTTP;禁止「或」「同类映射」),
|
|
1349
|
+
(e) 清单中**每个**接口的元信息表、字段表、≥1 组成功请求/响应示例,
|
|
1350
|
+
**(f) 每个接口 ≥1 组失败示例(G2)**,
|
|
1351
|
+
(g) 错误条件表,
|
|
1352
|
+
**(h) 新增/改 HTTP 绑定的 RPC**:冻结 RPC 全名 + proto 字段号 + `google.api.http` + Gateway 注册点 + Worker OpenAPI schema(禁止「暂定」「如」「实现时对齐」)。
|
|
1353
|
+
禁止 stub。**一旦列入清单,无论是否不变,均须完整骨架**;可标注「与现网一致」但仍须贴字段表与示例。
|
|
1323
1354
|
|
|
1324
1355
|
11. **§3 架构图须附设计要点(硬规则)**:每个架构 Mermaid 图后**必须**跟编号「设计说明 / 图要点」列表(边界/不变式/复用) —— 禁止仅复述节点名。仅有组件表不够。
|
|
1325
1356
|
|
|
@@ -1394,7 +1425,9 @@ When `approval/index.yaml` has `mode: segmented` (default for non-trivial §4),
|
|
|
1394
1425
|
| L3 | `TODO` / `待补充` / `此处省略` / bare `TBD` | Concrete text or `[待 refine 澄清: <元素>]` |
|
|
1395
1426
|
| L4 | `详见 design/tasks` without §/In/Page id | Cross-ref `§4.5 I2` / `Page·列表` / `P1` |
|
|
1396
1427
|
| L5 | New table/interface/page ids not in index | Update `index.yaml` first |
|
|
1397
|
-
| L6 | Skip DDL/字段表/失败示例 because "same as design" or "unchanged API" | G2–G6 minimum
|
|
1428
|
+
| L6 | Skip DDL/字段表/失败示例 because "same as design" or "unchanged API" | G2–G6 minimum; **不变**仍须完整 §4.5 骨架 |
|
|
1429
|
+
| L6b | Collapse L4 RPC into「内部调用 I7」; L2-only HTTP without proto | **分层**: L2/L3/L4 各独立 `In`;见 `api-guidance.md` §1 |
|
|
1430
|
+
| L6c | RPC「暂定/如/实现时命名」 | **冻结** `Service.Method` + proto 字段号;见 `api-guidance.md` §2–§3 |
|
|
1398
1431
|
| L7 | Skip IDE skills/rules scan for §4.6 | `frontend-guidance.md` §3 before Map |
|
|
1399
1432
|
| L8 | Paste skill/rule bodies verbatim | Readable Chinese + path in §4.6.1 |
|
|
1400
1433
|
| L9 | Foreign `## N.` headings in parts | `###`/`####` only; CLI injects chapter headers |
|
|
@@ -1430,3 +1463,36 @@ Do not reload entire four artifacts each batch.
|
|
|
1430
1463
|
|
|
1431
1464
|
Do not rerun Pass 1–7 unless analysis is stale.
|
|
1432
1465
|
|
|
1466
|
+
---
|
|
1467
|
+
|
|
1468
|
+
## Part G: Multi-Repo Approval (多仓)
|
|
1469
|
+
|
|
1470
|
+
> Router: `prompts/approval/multi-repo-guidance.md`
|
|
1471
|
+
|
|
1472
|
+
### G.1 Trigger
|
|
1473
|
+
|
|
1474
|
+
User natural language, workset, or cross-repo design → `multi_repo.enabled=true`.
|
|
1475
|
+
|
|
1476
|
+
### G.2 User gates (hard, before Stage 12a)
|
|
1477
|
+
|
|
1478
|
+
1. **Document mode**: `unified` (一份合订) **or** `per_repo` (多份分仓).
|
|
1479
|
+
2. If **unified**: **主仓** `primary_repo` — only this repo holds `approval/` + final `approval.md`.
|
|
1480
|
+
3. Record in `index.yaml`; do not assemble until both answered.
|
|
1481
|
+
|
|
1482
|
+
### G.3 Unified 合订
|
|
1483
|
+
|
|
1484
|
+
- Read **every** repo's four artifacts + anchors.
|
|
1485
|
+
- `index.change` **should equal** primary repo's change name.
|
|
1486
|
+
- Header lists all repos; §2.3 / 附录 B per-repo tables.
|
|
1487
|
+
- CLI: `specflow approval assemble <primary.change>` from **primary** planning root.
|
|
1488
|
+
|
|
1489
|
+
### G.4 Per-repo 分仓
|
|
1490
|
+
|
|
1491
|
+
- Separate `approval/` (or monolithic `approval.md`) **in each repo**.
|
|
1492
|
+
- Each document scoped to that repo only; cross-ref sibling paths.
|
|
1493
|
+
- Separate assemble per repo.
|
|
1494
|
+
|
|
1495
|
+
### G.5 Inventory
|
|
1496
|
+
|
|
1497
|
+
Recommend `repo` on `tables[]` / `interfaces[]` / `pages[]` entries for unified mode traceability.
|
|
1498
|
+
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
# Approval · Multi-Repo (多仓) Guidance
|
|
2
|
+
|
|
3
|
+
> Used by `/specflow:approval` when the user **自然语言指定多仓**、workset 含多个规划根、或
|
|
4
|
+
> design/tasks 明确跨 `talos` / `talos-web` / `talos-worker` 等仓库。
|
|
5
|
+
>
|
|
6
|
+
> **目的**:按各仓四件套与锚点生成技术审批文档,并 **询问用户** 产出 **一份合订** 还是 **多份分仓**;
|
|
7
|
+
> 合订时 **必须** 指定 **主仓** 存放 `approval.md` 与 `approval/` 工作区。
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## 0. When to run
|
|
12
|
+
|
|
13
|
+
| 信号 | 动作 |
|
|
14
|
+
|------|------|
|
|
15
|
+
| 用户说「三仓 / talos+web+worker / 多仓合订 / 前后端一体」 | 启用 `multi_repo.enabled=true` |
|
|
16
|
+
| `specflow workset` / 会话上下文含多个 git 规划根 | 列出候选仓,请用户确认 |
|
|
17
|
+
| 仅单仓 change,无跨仓表述 | `multi_repo.enabled=false`,走单仓流程 |
|
|
18
|
+
| Hub + Spoke 联邦 | 见 §5;各 Spoke 可有不同 change id |
|
|
19
|
+
|
|
20
|
+
Announce:
|
|
21
|
+
|
|
22
|
+
```text
|
|
23
|
+
Multi-repo approval: enabled | single-repo
|
|
24
|
+
Repos in scope: talos(scenario-job-compile), talos-web(web-scenario-cases), …
|
|
25
|
+
Document mode: (pending user) unified | per_repo
|
|
26
|
+
Primary repo (if unified): (pending user)
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
## 1. Resolve repos (硬顺序)
|
|
32
|
+
|
|
33
|
+
1. **Parse user natural language** — 仓名、别名(平台/控制台/Worker)、change 名。
|
|
34
|
+
2. **Cross-check workset** — `specflow workset list` / 用户给出的绝对路径;成员顺序 **不** 等于主仓。
|
|
35
|
+
3. **Per repo, verify** (Read-only):
|
|
36
|
+
- `specflow/changes/<change>/.specflow.yaml` → `phase: refined`
|
|
37
|
+
- 四件套:`proposal.md`, `specs/**`, `design.md`, `tasks.md`
|
|
38
|
+
4. **Record** in `approval/index.yaml` → `multi_repo.repos[]`:
|
|
39
|
+
|
|
40
|
+
```yaml
|
|
41
|
+
multi_repo:
|
|
42
|
+
enabled: true
|
|
43
|
+
document_mode: unified # unified | per_repo — 用户确认后写入
|
|
44
|
+
primary_repo: talos # unified 时必填
|
|
45
|
+
repos:
|
|
46
|
+
- id: talos
|
|
47
|
+
label: 平台
|
|
48
|
+
role: platform
|
|
49
|
+
change: scenario-job-compile
|
|
50
|
+
root_hint: /path/to/talos # agent 读取用,可选
|
|
51
|
+
- id: talos-web
|
|
52
|
+
label: 控制台
|
|
53
|
+
role: web
|
|
54
|
+
change: web-scenario-cases
|
|
55
|
+
- id: talos-worker
|
|
56
|
+
label: Worker
|
|
57
|
+
role: worker
|
|
58
|
+
change: scenario-execution
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
**禁止**在未 Read 各仓四件套前假设内容一致;change id **可以** 各仓不同。
|
|
62
|
+
|
|
63
|
+
---
|
|
64
|
+
|
|
65
|
+
## 2. User gate — 一份还是多份 (硬)
|
|
66
|
+
|
|
67
|
+
在 Pass 1 之前或 Stage 12a 之前,**必须** 询问:
|
|
68
|
+
|
|
69
|
+
```text
|
|
70
|
+
本需求跨 N 个仓库,审批技术文档如何产出?
|
|
71
|
+
|
|
72
|
+
A) 一份合订文档 (unified) — 单份 approval.md,含各仓决策/接口/前端/Worker 切片
|
|
73
|
+
B) 多份分仓文档 (per_repo) — 每个仓库各自一份 approval.md,仅写本仓范围
|
|
74
|
+
|
|
75
|
+
请选择 A 或 B。
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### 2.1 Unified(一份合订)
|
|
79
|
+
|
|
80
|
+
再询问:
|
|
81
|
+
|
|
82
|
+
```text
|
|
83
|
+
合订文档存放在哪个主仓?(approval.md 与 approval/ 工作区写在该仓 change 目录下)
|
|
84
|
+
|
|
85
|
+
1) talos (平台)
|
|
86
|
+
2) talos-web (控制台)
|
|
87
|
+
3) talos-worker (Worker)
|
|
88
|
+
…
|
|
89
|
+
|
|
90
|
+
请选主仓 id。
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
**硬规则**:
|
|
94
|
+
|
|
95
|
+
- `multi_repo.document_mode: unified`
|
|
96
|
+
- `multi_repo.primary_repo: <id>` **必填**
|
|
97
|
+
- **工作区路径**(分片模式):
|
|
98
|
+
- `specflow/changes/<primary.change>/approval/index.yaml`
|
|
99
|
+
- `specflow/changes/<primary.change>/approval/parts/*.md`
|
|
100
|
+
- 产出:`specflow/changes/<primary.change>/approval.md`
|
|
101
|
+
- CLI:`specflow approval assemble <primary.change>` **在主仓规划根**执行
|
|
102
|
+
- 文档 §1.2 / §2.3 / 附录 B **必须** 按仓分表;Pass 6 锚点 **分仓列举**
|
|
103
|
+
- 合订 **不是** 只写主仓 design;须合并各仓 specs/design/tasks 的跨端语义
|
|
104
|
+
|
|
105
|
+
写入 `multi_repo.outputs.unified`:
|
|
106
|
+
|
|
107
|
+
```yaml
|
|
108
|
+
outputs:
|
|
109
|
+
unified:
|
|
110
|
+
repo: talos
|
|
111
|
+
change: scenario-job-compile
|
|
112
|
+
path: specflow/changes/scenario-job-compile/approval.md
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### 2.2 Per-repo(多份分仓)
|
|
116
|
+
|
|
117
|
+
**硬规则**:
|
|
118
|
+
|
|
119
|
+
- `multi_repo.document_mode: per_repo`
|
|
120
|
+
- **每个** `repos[]` 条目独立:
|
|
121
|
+
- 在该仓 `specflow/changes/<repo.change>/approval/`(分片)或 `approval.md`(单体)
|
|
122
|
+
- 内容 **仅** 本仓 scope;交叉引用用「见 `<other-repo>` approval §x」
|
|
123
|
+
- 各仓分别 `specflow approval assemble <repo.change>`(若在主仓 CLI,需 `--store` 或 cd 到该根)
|
|
124
|
+
- **禁止**只在主仓写一份然后复制到其它仓
|
|
125
|
+
|
|
126
|
+
写入 `multi_repo.outputs.per_repo[]`:
|
|
127
|
+
|
|
128
|
+
```yaml
|
|
129
|
+
outputs:
|
|
130
|
+
per_repo:
|
|
131
|
+
- repo: talos
|
|
132
|
+
change: scenario-job-compile
|
|
133
|
+
path: specflow/changes/scenario-job-compile/approval.md
|
|
134
|
+
- repo: talos-web
|
|
135
|
+
change: web-scenario-cases
|
|
136
|
+
path: specflow/changes/web-scenario-cases/approval.md
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
---
|
|
140
|
+
|
|
141
|
+
## 3. Analysis & Map 上下文
|
|
142
|
+
|
|
143
|
+
| 模式 | Read 范围 | index 库存 |
|
|
144
|
+
|------|-----------|------------|
|
|
145
|
+
| unified | 各仓四件套 + 各仓锚点 + 各仓约定 | `interfaces/pages/tables` 带 `repo` 字段(推荐) |
|
|
146
|
+
| per_repo | **当前仓**四件套 + 本仓锚点 | 仅本仓实体 |
|
|
147
|
+
|
|
148
|
+
**Unified 分片建议**:
|
|
149
|
+
|
|
150
|
+
- `02-design-review` — 三仓决策表
|
|
151
|
+
- `04.5-api` — 按 `repo` 或 L1/L2/L3/L4 分批;Worker 切片须 Read `api-guidance.md` + worker OpenAPI 路径
|
|
152
|
+
- `04.6-*` — 仅 `ui_in_scope` 且 web 仓
|
|
153
|
+
- 附录 B — 按仓允许/禁止路径
|
|
154
|
+
|
|
155
|
+
**Inventory 扩展**(可选列):
|
|
156
|
+
|
|
157
|
+
```yaml
|
|
158
|
+
interfaces:
|
|
159
|
+
- id: I4
|
|
160
|
+
short: 逐步上报
|
|
161
|
+
change: scenario-job-compile
|
|
162
|
+
part: 04.5-api-worker
|
|
163
|
+
repo: talos-worker
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
---
|
|
167
|
+
|
|
168
|
+
## 4. Document header (unified)
|
|
169
|
+
|
|
170
|
+
合订 `approval.md` 头部 **必须** 声明:
|
|
171
|
+
|
|
172
|
+
```markdown
|
|
173
|
+
> **覆盖变更(多仓合订,主仓: talos)**:
|
|
174
|
+
> - 平台 `talos/scenario-job-compile`
|
|
175
|
+
> - 控制台 `talos-web/web-scenario-cases`
|
|
176
|
+
> - Worker `talos-worker/scenario-execution`
|
|
177
|
+
> **存放路径**: `specflow/changes/scenario-job-compile/approval.md`
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
---
|
|
181
|
+
|
|
182
|
+
## 5. Hub / Federated 注意
|
|
183
|
+
|
|
184
|
+
- Hub change id 可与 Spoke change id **同名或不同**;`multi_repo.repos[].change` 用 **各规划根内真实目录名**。
|
|
185
|
+
- Hub 合同摘要可读 `--store`;Spoke approval **不**替代 Hub archive。
|
|
186
|
+
- `coordination.yaml`(若有)可 Read;无则 chat 列出各仓 phase。
|
|
187
|
+
|
|
188
|
+
---
|
|
189
|
+
|
|
190
|
+
## 6. 自检
|
|
191
|
+
|
|
192
|
+
- [ ] 用户已选 unified **或** per_repo
|
|
193
|
+
- [ ] unified → `primary_repo` 已选且工作区在该仓
|
|
194
|
+
- [ ] per_repo → 每仓输出路径已写入 `outputs.per_repo`
|
|
195
|
+
- [ ] 各仓四件套已 Read;非主仓内容已进合订正文或分仓文档
|
|
196
|
+
- [ ] Gate 摘要中列出 **所有** 将写入的 `approval.md` 路径
|
|
197
|
+
|
|
198
|
+
---
|
|
199
|
+
|
|
200
|
+
## 7. 与单仓关系
|
|
201
|
+
|
|
202
|
+
`multi_repo.enabled=false` 时忽略本章;`change` 字段为当前规划根 change,行为与旧版一致。
|
|
@@ -33,7 +33,7 @@ Conflict policy: project > SpecFlow guidance > LLM
|
|
|
33
33
|
|--------------|----------------------|----------------|
|
|
34
34
|
| `architecture` | 起草 **§3** 前 | 分层、模块边界、目录约定、禁止直连 |
|
|
35
35
|
| `database` | 起草 **§4.4** 前(且有持久化) | 迁移工具禁令、命名、字符集、现网表规范 |
|
|
36
|
-
| `api` | 起草 **§4.5** 前(且有对外/跨端接口) | 错误码、鉴权、契约/OpenAPI
|
|
36
|
+
| `api` | 起草 **§4.5** 前(且有对外/跨端接口) | 错误码、鉴权、契约/OpenAPI、通道;然后 **`api-guidance.md`**(分层、RPC 锁名、proto 可生成包) |
|
|
37
37
|
| `frontend` | 起草 **§4.6** 前(且 `uiInScope=yes`) | 组件/状态/路由/API client/设计系统/**表单/测试/a11y** 等落地约定;须含 IDE skills/rules 扫描(见 `frontend-guidance.md` §3) |
|
|
38
38
|
|
|
39
39
|
每个主题 **最多 Read 5 个文件**(配置列出的优先;自动探测时取最相关的前 5 个)。细节 references 仅在入口文件点名时再读(禁止 reference 链式跳转)。
|
|
@@ -40,7 +40,9 @@ Gate → user confirms index + optional chapters + mode
|
|
|
40
40
|
| L3 | `TODO` / `待补充` / `此处省略` / bare `TBD` | Concrete text or `[待 refine 澄清: <元素>]` with reason |
|
|
41
41
|
| L4 | `详见 design/tasks` without §/In/Page id | Cross-ref `§4.5 I2` / `Page·列表` / `P1` |
|
|
42
42
|
| L5 | Invent new table/interface/page ids not in index | Update index.yaml first, then write part |
|
|
43
|
-
| L6 | Skip §4.4 DDL/字段表/失败示例 because "same as design" | Minimum
|
|
43
|
+
| L6 | Skip §4.4 DDL/字段表/失败示例 because "same as design" | Minimum per G2–G6; **不变**仍须完整 §4.5 |
|
|
44
|
+
| L6b | L2 HTTP only +「内部 I7」stub; or L4 one-liner | **分层** L2/L3/L4 各 `In`; `api-guidance.md` §1 |
|
|
45
|
+
| L6c | RPC「暂定/如/实现时」 | **冻结**名 + proto 字段号; `api-guidance.md` §2–§3 |
|
|
44
46
|
| L7 | Skip IDE skills/rules scan for §4.6 | frontend-guidance.md §3 before Map |
|
|
45
47
|
| L8 | Paste skill/rule bodies verbatim | Readable Chinese + path in §4.6.1 table |
|
|
46
48
|
| L9 | Write `## 4.` or foreign `## N.` in parts | Parts use `###`/`####`/`#####` only; CLI injects `## 4` |
|
|
@@ -86,8 +88,12 @@ For each batch, **Read only**:
|
|
|
86
88
|
|
|
87
89
|
### §4.5 part minimum (per interface)
|
|
88
90
|
|
|
89
|
-
-
|
|
90
|
-
-
|
|
91
|
+
- Run **`api-guidance.md`** before batch
|
|
92
|
+
- **One `In` per contract surface** (L2 Worker HTTP / L3 RPC+http / L4 client RPC) — no B2-style collapse
|
|
93
|
+
- meta + fields + success req/resp + **failure example (G2)** + error table (**含不变**)
|
|
94
|
+
- **新增/改 HTTP binding**: `**Proto 草案**` block — frozen RPC name, field numbers, `google.api.http`, Gateway registration, Worker OpenAPI path
|
|
95
|
+
- HTTP examples for L2: **all** required work auth headers (not body-only)
|
|
96
|
+
- **Forbidden**: 暂定 / 如 XxxRpc / 实现时命名 / 内部经 I7 一行代替 L4 详设
|
|
91
97
|
|
|
92
98
|
### §4.6 part minimum (per page)
|
|
93
99
|
|
package/skills/GUIDANCE_PACKS.md
CHANGED
|
@@ -24,7 +24,7 @@ Workflow 通过路由 `Read` 路径加载 guidance;禁止写成「invoke `/mys
|
|
|
24
24
|
| 主题 | `architecture` → §3;`database` → §4.4;`api` → §4.5;`frontend` → §4.6(有 UI;含 IDE skills/rules 落地扫描) |
|
|
25
25
|
| 上限 | 每主题约定文件最多 **3**(helper);前端专题合计约定+IDE skills/rules ≤ **5**(见 `frontend-guidance.md` §3) |
|
|
26
26
|
|
|
27
|
-
另:前端详设结构由 `prompts/approval/frontend-guidance.md`
|
|
27
|
+
另:前端详设结构由 `prompts/approval/frontend-guidance.md` 驱动;接口/RPC 可生成契约由 **`prompts/approval/api-guidance.md`** 驱动(分层 L2/L3/L4、RPC 锁名、proto/OpenAPI 包)。与 DB 的 `database-guidance.md` 对称。
|
|
28
28
|
|
|
29
29
|
**分片生成(非 trivial §4)**:`prompts/approval/segmented-generation.md` + `templates/approval-index.yaml`;Map 写 `approval/parts/`,Reduce 用 `specflow approval assemble`(禁止 LLM 拼接)。
|
|
30
30
|
|
|
@@ -103,6 +103,37 @@ uiInScope=<yes|no>, stackCoverage=<complete|partial|missing>)."
|
|
|
103
103
|
|
|
104
104
|
---
|
|
105
105
|
|
|
106
|
+
## Stage 1b: Multi-Repo Intake (多仓)
|
|
107
|
+
|
|
108
|
+
**Trigger**: user **自然语言** specifies multiple repos (talos + talos-web + worker), workset
|
|
109
|
+
with multiple planning roots, or cross-repo design/tasks.
|
|
110
|
+
|
|
111
|
+
Read **`prompts/approval/multi-repo-guidance.md`**.
|
|
112
|
+
|
|
113
|
+
1. Resolve `multi_repo.repos[]` — `id`, `label`, `role`, **per-repo `change`**, optional `root_hint`.
|
|
114
|
+
2. **Read four refined artifacts from each repo** before Pass 1.
|
|
115
|
+
3. **User gate (hard)** — ask before writing any approval file:
|
|
116
|
+
|
|
117
|
+
```text
|
|
118
|
+
跨 N 个仓库,审批技术文档如何产出?
|
|
119
|
+
A) 一份合订 (unified) — 单份 approval.md
|
|
120
|
+
B) 多份分仓 (per_repo) — 每仓各自 approval.md
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
4. If **A** — ask **主仓** (stores `approval.md` + `approval/` workspace):
|
|
124
|
+
|
|
125
|
+
```text
|
|
126
|
+
合订文档存放在哪个主仓? (talos / talos-web / talos-worker / …)
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
5. Write `approval/index.yaml` → `multi_repo` with user choices + `outputs`.
|
|
130
|
+
6. **Unified**: Stage 12 workspace + `specflow approval assemble` run in **primary** planning root only.
|
|
131
|
+
7. **Per_repo**: repeat Stage 12 + assemble **per repo** (scope = that repo only).
|
|
132
|
+
|
|
133
|
+
Single-repo → skip (`multi_repo.enabled=false`).
|
|
134
|
+
|
|
135
|
+
---
|
|
136
|
+
|
|
106
137
|
## Gate: Tech Stack Intake (0→1 / 四件套缺选型)
|
|
107
138
|
|
|
108
139
|
**Trigger** (any):
|
|
@@ -344,8 +375,11 @@ Order and hard requirements (from `generate.md` §4.1–4.9):
|
|
|
344
375
|
(2) `database-guidance.md` + SpecFlow guidance pack / `dbStack`; (3) live DDL/anchors.
|
|
345
376
|
Priority: **project + live DDL > SpecFlow pack > LLM**. §4.4.1 must list 项目约定 + DB 技能.
|
|
346
377
|
**Outline**: only `4.4.1–4.4.4` + `##### table`; DDL/字段说明/JSON = `**bold**`.
|
|
347
|
-
5. **接口设计** — Before §4.5: `project-conventions-guidance.md` `topic=api
|
|
348
|
-
|
|
378
|
+
5. **接口设计** — Before §4.5: (1) `project-conventions-guidance.md` `topic=api`;
|
|
379
|
+
(2) **`api-guidance.md`** — 契约面分层(L2/L3/L4 各独立 `In`)、RPC 锁名、proto/OpenAPI 可生成包;
|
|
380
|
+
(3) live proto/OpenAPI/anchors.
|
|
381
|
+
Then inventory + **each surface** fields + examples + errors + **Proto 草案**(新增 RPC).
|
|
382
|
+
**禁止** L2 HTTP 详写 + L4「内部调用」stub; **禁止**「暂定/如/实现时」RPC 名。
|
|
349
383
|
**Outline**: only `4.5.1–4.5.3` + `##### In`; 请求体字段/示例/错误 = `**bold**`.
|
|
350
384
|
6. **前端 / UI** — When `uiInScope=yes`: before §4.6:
|
|
351
385
|
(1) `project-conventions-guidance.md` `topic=frontend`;
|
|
@@ -459,7 +493,10 @@ Respect `index.batching.*_per_call`. After each part: update `approval/manifest.
|
|
|
459
493
|
Minimums (no shortcuts — see Part E hard rules 9–10, 18–20):
|
|
460
494
|
|
|
461
495
|
- §4.4 batch: DDL + 字段说明 + 本迭代用法 per table; G3/G4 when applicable
|
|
462
|
-
- §4.5 batch:
|
|
496
|
+
- §4.5 batch: **Read `api-guidance.md`**; one `In` per contract surface (L2 Worker HTTP / L3 RPC / L4 client RPC — no collapsing)
|
|
497
|
+
- meta + fields + success examples + **failure example (G2)** + error table per interface (**含不变**)
|
|
498
|
+
- **新增 RPC**: frozen `Service.Method` + proto field numbers + `google.api.http` + Gateway registration + Worker OpenAPI pointer
|
|
499
|
+
- **禁止** 暂定/如/实现时 RPC; **禁止** L4 stub when L2 is fully written
|
|
463
500
|
- §4.6 batch (when `uiInScope`): IDE skills/rules scan first; page/route + states + G6; §4.5 `In` refs
|
|
464
501
|
|
|
465
502
|
### 12c Optional chapters
|
|
@@ -522,7 +559,9 @@ Present a summary to the user (**chat only** — these are not document chapters
|
|
|
522
559
|
- Design quality verdict (PASS / WARNING / FAIL)
|
|
523
560
|
- Implementability overall verdict (READY / NEEDS REFINEMENT / BLOCKED)
|
|
524
561
|
- **AI pre-approval recommendation** (建议批准 / 有条件批准 / 退回 refine / 拒绝) + 理由
|
|
525
|
-
- The path where `approval.md` will be written
|
|
562
|
+
- The path where `approval.md` will be written (**all paths** if `multi_repo.document_mode=per_repo`)
|
|
563
|
+
|
|
564
|
+
If `multi_repo.enabled`, confirm document mode + primary repo (unified) are already in `index.yaml`.
|
|
526
565
|
|
|
527
566
|
Ask explicitly about **optional chapters** (record in `approval/index.yaml` → `optional.s5/s7/s8`):
|
|
528
567
|
|
|
@@ -50,3 +50,23 @@ meta:
|
|
|
50
50
|
language: zh-CN
|
|
51
51
|
tech_stack: unknown
|
|
52
52
|
project_mode: brownfield
|
|
53
|
+
|
|
54
|
+
# 多仓时启用 (见 prompts/approval/multi-repo-guidance.md)
|
|
55
|
+
# multi_repo:
|
|
56
|
+
# enabled: true
|
|
57
|
+
# document_mode: unified # unified | per_repo — 用户确认后填写
|
|
58
|
+
# primary_repo: talos # unified 时必填
|
|
59
|
+
# repos:
|
|
60
|
+
# - id: talos
|
|
61
|
+
# label: 平台
|
|
62
|
+
# role: platform
|
|
63
|
+
# change: <change-name>
|
|
64
|
+
# - id: talos-web
|
|
65
|
+
# label: 控制台
|
|
66
|
+
# role: web
|
|
67
|
+
# change: <web-change-name>
|
|
68
|
+
# outputs:
|
|
69
|
+
# unified:
|
|
70
|
+
# repo: talos
|
|
71
|
+
# change: <change-name>
|
|
72
|
+
# path: specflow/changes/<change-name>/approval.md
|