@gordon.gan/specflow 1.8.3-beta → 1.8.5-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.
Files changed (34) hide show
  1. package/dist/cli/commands/document-run.js +6 -3
  2. package/dist/core/document/engine.js +46 -24
  3. package/dist/core/document/gates.js +33 -7
  4. package/dist/core/document/lint.d.ts +37 -1
  5. package/dist/core/document/lint.js +201 -6
  6. package/dist/core/document/render.d.ts +7 -4
  7. package/dist/core/document/render.js +100 -53
  8. package/dist/core/document/schemas.d.ts +100 -0
  9. package/dist/core/document/schemas.js +12 -1
  10. package/package.json +1 -1
  11. package/prompts/document/map/api-design.md +11 -3
  12. package/prompts/document/map/architecture.md +1 -1
  13. package/prompts/document/map/component-design.md +2 -0
  14. package/prompts/document/map/core-flow.md +4 -1
  15. package/prompts/document/map/core-logic.md +10 -3
  16. package/prompts/document/map/data-model.md +5 -2
  17. package/prompts/document/map/requirement.md +6 -1
  18. package/prompts/document/map/test-strategy.md +1 -1
  19. package/prompts/document/outline/general.md +9 -0
  20. package/prompts/document/shared/grounding.md +7 -0
  21. package/prompts/shared/artifact-language.md +9 -0
  22. package/skills/specflow-techdoc-synth/SKILL.md +12 -9
  23. package/templates/document/chapters/api-design.yaml +14 -5
  24. package/templates/document/chapters/architecture.yaml +1 -2
  25. package/templates/document/chapters/core-flow.yaml +2 -0
  26. package/templates/document/chapters/core-logic.yaml +8 -6
  27. package/templates/document/chapters/data-model.yaml +9 -5
  28. package/templates/document/chapters/mvp-boundary.yaml +0 -3
  29. package/templates/document/chapters/requirement.yaml +5 -7
  30. package/templates/document/chapters/tech-selection.yaml +0 -3
  31. package/templates/document/chapters/test-strategy.yaml +2 -5
  32. package/templates/document/chapters/ui-design.yaml +0 -3
  33. package/templates/document/profiles/approve.yaml +3 -2
  34. package/templates/document/profiles/feature.yaml +1 -1
@@ -31,32 +31,45 @@ export function escapeInline(s) {
31
31
  }
32
32
  // ============= Contract entity → Markdown (T9.1) =============
33
33
  export function renderInterface(i) {
34
- const rows = [
34
+ const meta = [
35
35
  `##### ${i.id} · ${escapeInline(i.name)}(${i.method} ${i.path})`,
36
36
  '',
37
- '**字段表**',
38
- '',
39
- '| 字段 | 类型 | 必填 | 说明 |',
40
- '|------|------|------|------|',
41
- ...[...i.request_fields ?? [], ...i.response_fields ?? []].map((f) => `| ${escapeTableCell(f.name)} | ${escapeTableCell(f.type)} | ${f.required ? '是' : '否'} | ${escapeTableCell(f.desc ?? '')} |`),
42
- '',
43
- '**成功示例**',
44
- '',
45
- '```http',
46
- `${i.method} ${escapeFenceContent(i.path)}`,
47
- '```',
48
- '',
49
- '**失败示例**',
50
- '',
51
- ...i.failure_examples.map((ex) => `- ${escapeInline(ex)}`),
52
- '',
37
+ ...(i.side || (i.consumers?.length ?? 0) > 0 || (i.producers?.length ?? 0) > 0
38
+ ? [`**面**:${escapeInline(i.side ?? '')}${(i.consumers?.length ?? 0) > 0 ? ` | **消费方**:${(i.consumers ?? []).map(escapeInline).join(', ')}` : ''}${(i.producers?.length ?? 0) > 0 ? ` | **生产方**:${(i.producers ?? []).map(escapeInline).join(', ')}` : ''}`, '']
39
+ : []),
40
+ ];
41
+ const fieldRows = (fields) => fields.map((f) => `| ${escapeTableCell(f.name)} | ${escapeTableCell(f.type)} | ${f.required ? '是' : '否'} | ${escapeTableCell(f.desc ?? '')} |`);
42
+ const fieldTables = [];
43
+ if ((i.request_fields ?? []).length > 0) {
44
+ fieldTables.push('**请求字段**', '', '| 字段 | 类型 | 必填 | 说明 |', '|------|------|------|------|', ...fieldRows(i.request_fields ?? []));
45
+ }
46
+ if ((i.response_fields ?? []).length > 0) {
47
+ fieldTables.push('**响应字段**', '', '| 字段 | 类型 | 必填 | 说明 |', '|------|------|------|------|', ...fieldRows(i.response_fields ?? []));
48
+ }
49
+ const examples = [];
50
+ if (i.request_example) {
51
+ examples.push('**请求示例**', '', '```json', escapeFenceContent(i.request_example), '```');
52
+ }
53
+ if (i.success_response_example) {
54
+ examples.push('**成功响应示例**', '', '```json', escapeFenceContent(i.success_response_example), '```');
55
+ }
56
+ if ((i.failure_examples?.length ?? 0) > 0 || i.failure_response_example) {
57
+ examples.push('**失败示例**', '');
58
+ if ((i.failure_examples?.length ?? 0) > 0) {
59
+ examples.push(...(i.failure_examples ?? []).map((ex) => `- ${escapeInline(ex)}`));
60
+ }
61
+ if (i.failure_response_example) {
62
+ examples.push('', '```json', escapeFenceContent(i.failure_response_example), '```');
63
+ }
64
+ }
65
+ const errors = [
53
66
  '**错误**',
54
67
  '',
55
- '| 条件 | 状态 | 说明 |',
56
- '|------|------|------|',
57
- ...i.errors.map((e) => `| ${escapeTableCell(e.condition)} | ${escapeTableCell(e.status)} | ${escapeTableCell(e.note ?? '')} |`),
68
+ '| 条件 | 状态 | 类别 | 说明 |',
69
+ '|------|------|------|------|',
70
+ ...i.errors.map((e) => `| ${escapeTableCell(e.condition)} | ${escapeTableCell(e.status)} | ${escapeTableCell(e.kind ?? 'backend')} | ${escapeTableCell(e.note ?? '')} |`),
58
71
  ];
59
- return rows.join('\n');
72
+ return [...meta, ...fieldTables, ...examples, ...errors].join('\n');
60
73
  }
61
74
  export function renderTable(t) {
62
75
  return [
@@ -70,28 +83,36 @@ export function renderTable(t) {
70
83
  ...(t.rollback ? ['**回滚兼容**', '', t.rollback, ''] : []),
71
84
  ].join('\n');
72
85
  }
73
- export function renderDecisions(decisions) {
74
- if (decisions.length === 0)
75
- return '';
76
- return [
77
- '**决策记录**',
78
- '',
79
- '| 决策 | 结论 |',
80
- '|------|------|',
81
- ...decisions.map((d) => `| ${escapeTableCell(d.id)} | ${escapeTableCell(d.text)} |`),
82
- ].join('\n');
83
- }
84
86
  export function renderEntities(entities) {
87
+ return [renderInterfaces(entities.interfaces), renderTables(entities.tables)].filter(Boolean).join('\n\n');
88
+ }
89
+ /** 契约实体按「面」分组渲染(Console 面 / 内部与 Worker 面),供接口与数据设计章就地展开。 */
90
+ export function renderInterfaces(interfaces) {
85
91
  const parts = [];
86
- for (const i of entities.interfaces)
87
- parts.push(renderInterface(i));
88
- for (const t of entities.tables)
89
- parts.push(renderTable(t));
90
- const decisionsMd = renderDecisions(entities.decisions);
91
- if (decisionsMd)
92
- parts.push(decisionsMd);
92
+ const groups = new Map();
93
+ for (const i of interfaces) {
94
+ const key = i.side ?? 'other';
95
+ if (!groups.has(key))
96
+ groups.set(key, []);
97
+ groups.get(key).push(i);
98
+ }
99
+ const sideTitle = {
100
+ console: 'Console 面契约',
101
+ internal: '内部 / Worker 面契约',
102
+ worker: 'Worker 面契约',
103
+ other: '契约',
104
+ };
105
+ for (const key of ['console', 'internal', 'worker', 'other']) {
106
+ const list = groups.get(key);
107
+ if (!list || list.length === 0)
108
+ continue;
109
+ parts.push(`### ${sideTitle[key]}`, '', list.map(renderInterface).join('\n\n'));
110
+ }
93
111
  return parts.join('\n\n');
94
112
  }
113
+ export function renderTables(tables) {
114
+ return tables.map(renderTable).join('\n\n');
115
+ }
95
116
  /** Strip engine repair notes (`<!-- review-fix ... -->`) from the FINAL render. The audit trail
96
117
  * stays in `chapters/<id>.md` and `review-result.json`; the deliverable document must not show
97
118
  * internal fix notes. */
@@ -179,30 +200,56 @@ export function demoteHeadings(text, levels = 1) {
179
200
  return out.join('\n');
180
201
  }
181
202
  export function renderDocument(input) {
182
- const { outline, entities, narratives, profile, appendixAntiAI } = input;
203
+ const { outline, entities, narratives, profile, appendixAntiAI, repos, includeProvenance } = input;
183
204
  const body = [`# 方案文档:${escapeInline(outline.profile)}`, ''];
205
+ // 文档头元信息(P7):确定性渲染,不依赖 LLM。
206
+ const meta = [];
207
+ if (repos && repos.length > 0)
208
+ meta.push(`| 参与仓 | ${repos.map(escapeTableCell).join('、')} |`);
209
+ if (meta.length > 0) {
210
+ body.push('| 项 | 值 |', '|------|------|', ...meta, '');
211
+ }
212
+ // 目录(P7):按大纲章节序确定性生成。
213
+ body.push('## 目录', '');
214
+ let tocNo = 0;
215
+ outline.chapters.forEach((ch) => {
216
+ tocNo += 1;
217
+ body.push(`${tocNo}. ${escapeInline(ch.title)}`);
218
+ });
219
+ body.push('');
220
+ // 契约明细按章节组件落位:
221
+ // - 接口实体(请求/响应字段表、示例、错误表)内联进「接口与数据设计」章(接口明细);
222
+ // - 数据库表(DDL/存量填充/回滚)必须由「数据模型」章组件承载(数据表),不并入接口章。
223
+ const interfacesMd = renderInterfaces(entities.interfaces);
224
+ const tablesMd = renderTables(entities.tables);
225
+ const hasApiDesign = outline.chapters.some((c) => c.id === 'api-design');
226
+ const hasDataModel = outline.chapters.some((c) => c.id === 'data-model');
184
227
  for (const ch of outline.chapters) {
185
228
  body.push(`## ${escapeInline(ch.title)}`, '');
186
229
  // 依次净化:剥内嵌契约 JSON 块(机器中间格式)→ 剥与章节标题重复的首行 H1 → 剥引擎修复注记,
187
- // 再把叙述内标题降级一级(章节头已占 H2,叙述小节 H2→H3、分仓 H3→H4),保证目录层级正确。
230
+ // 再把叙述内标题降级一级(章节头已占 H2,叙述小节 H2→H3),保证目录层级正确。
188
231
  const narrative = demoteHeadings(stripLeadingTitle(stripEntityJsonBlocks(stripReviewNotes(narratives.get(ch.id) ?? '')), ch.title));
189
232
  if (narrative.trim())
190
233
  body.push(narrative.trim(), '');
234
+ if (ch.id === 'api-design' && interfacesMd.trim())
235
+ body.push('### 接口明细', '', interfacesMd, '');
236
+ if (ch.id === 'data-model' && tablesMd.trim())
237
+ body.push('### 数据表', '', tablesMd, '');
191
238
  }
192
- // Contract entities: append to the chapters that reference them (simplified: append in a
193
- // final "契约" section, since entities are cross-chapter).
194
- const contractMd = renderEntities(entities);
195
- if (contractMd.trim()) {
196
- body.push('---', '', '## 契约实体', '', contractMd, '');
239
+ // api-design / data-model 章时的兜底(如 bugfix profile 的契约实体仍须呈现)。
240
+ if (!hasApiDesign && !hasDataModel && (interfacesMd.trim() || tablesMd.trim())) {
241
+ body.push('---', '', '## 附录 A:契约实体明细', '', [interfacesMd, tablesMd].filter(Boolean).join('\n\n'), '');
197
242
  }
198
- // 产物溯源附录 (borrowed from approval · appendix A): each chapter's data source.
199
- body.push('---', '', '## 附录 A: 产物溯源', '');
200
- body.push('| 章节 | 数据来源 | 处理方式 |');
201
- body.push('|------|---------|---------|');
202
- for (const ch of outline.chapters) {
203
- body.push(`| ${escapeTableCell(ch.title)} | 输入素材 / 项目规约 / 大纲要点 | LLM 生成 + 引擎校验 |`);
243
+ // 产物溯源附录(P8):默认不渲染(synth 下每章来源雷同,属噪声);--signoff/调试时开启。
244
+ if (includeProvenance) {
245
+ body.push('---', '', '## 附录:产物溯源', '');
246
+ body.push('| 章节 | 数据来源 | 处理方式 |');
247
+ body.push('|------|---------|---------|');
248
+ for (const ch of outline.chapters) {
249
+ body.push(`| ${escapeTableCell(ch.title)} | 输入素材 / 项目规约 / 大纲要点 | LLM 生成 + 引擎校验 |`);
250
+ }
251
+ body.push('', '');
204
252
  }
205
- body.push('', '');
206
253
  // 附录 B/C: rendered from profile.appendices (decision: deterministic templates, §7.3).
207
254
  const appendices = profile?.appendices ?? [];
208
255
  if (appendices.includes('anti-ai')) {
@@ -34,13 +34,17 @@ export declare const ErrorSpecSchema: z.ZodObject<{
34
34
  condition: z.ZodString;
35
35
  status: z.ZodString;
36
36
  note: z.ZodOptional<z.ZodString>;
37
+ /** 错误类别:backend=gRPC→HTTP 映射;guard=前端守卫行为;internal=Worker 内部语义(P5 拆分)。 */
38
+ kind: z.ZodOptional<z.ZodEnum<["backend", "guard", "internal"]>>;
37
39
  }, "strip", z.ZodTypeAny, {
38
40
  status: string;
39
41
  condition: string;
42
+ kind?: "backend" | "guard" | "internal" | undefined;
40
43
  note?: string | undefined;
41
44
  }, {
42
45
  status: string;
43
46
  condition: string;
47
+ kind?: "backend" | "guard" | "internal" | undefined;
44
48
  note?: string | undefined;
45
49
  }>;
46
50
  export declare const InterfaceEntitySchema: z.ZodObject<{
@@ -90,16 +94,26 @@ export declare const InterfaceEntitySchema: z.ZodObject<{
90
94
  condition: z.ZodString;
91
95
  status: z.ZodString;
92
96
  note: z.ZodOptional<z.ZodString>;
97
+ /** 错误类别:backend=gRPC→HTTP 映射;guard=前端守卫行为;internal=Worker 内部语义(P5 拆分)。 */
98
+ kind: z.ZodOptional<z.ZodEnum<["backend", "guard", "internal"]>>;
93
99
  }, "strip", z.ZodTypeAny, {
94
100
  status: string;
95
101
  condition: string;
102
+ kind?: "backend" | "guard" | "internal" | undefined;
96
103
  note?: string | undefined;
97
104
  }, {
98
105
  status: string;
99
106
  condition: string;
107
+ kind?: "backend" | "guard" | "internal" | undefined;
100
108
  note?: string | undefined;
101
109
  }>, "many">;
102
110
  failure_examples: z.ZodArray<z.ZodString, "many">;
111
+ request_example: z.ZodOptional<z.ZodString>;
112
+ success_response_example: z.ZodOptional<z.ZodString>;
113
+ failure_response_example: z.ZodOptional<z.ZodString>;
114
+ side: z.ZodOptional<z.ZodEnum<["console", "worker", "internal"]>>;
115
+ consumers: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
116
+ producers: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
103
117
  }, "strip", z.ZodTypeAny, {
104
118
  path: string;
105
119
  id: string;
@@ -107,6 +121,7 @@ export declare const InterfaceEntitySchema: z.ZodObject<{
107
121
  errors: {
108
122
  status: string;
109
123
  condition: string;
124
+ kind?: "backend" | "guard" | "internal" | undefined;
110
125
  note?: string | undefined;
111
126
  }[];
112
127
  method: "GET" | "POST" | "PUT" | "PATCH" | "DELETE";
@@ -125,6 +140,12 @@ export declare const InterfaceEntitySchema: z.ZodObject<{
125
140
  default?: string | undefined;
126
141
  desc?: string | undefined;
127
142
  }[] | undefined;
143
+ request_example?: string | undefined;
144
+ success_response_example?: string | undefined;
145
+ failure_response_example?: string | undefined;
146
+ side?: "internal" | "console" | "worker" | undefined;
147
+ consumers?: string[] | undefined;
148
+ producers?: string[] | undefined;
128
149
  }, {
129
150
  path: string;
130
151
  id: string;
@@ -132,6 +153,7 @@ export declare const InterfaceEntitySchema: z.ZodObject<{
132
153
  errors: {
133
154
  status: string;
134
155
  condition: string;
156
+ kind?: "backend" | "guard" | "internal" | undefined;
135
157
  note?: string | undefined;
136
158
  }[];
137
159
  method: "GET" | "POST" | "PUT" | "PATCH" | "DELETE";
@@ -150,6 +172,12 @@ export declare const InterfaceEntitySchema: z.ZodObject<{
150
172
  required?: boolean | undefined;
151
173
  desc?: string | undefined;
152
174
  }[] | undefined;
175
+ request_example?: string | undefined;
176
+ success_response_example?: string | undefined;
177
+ failure_response_example?: string | undefined;
178
+ side?: "internal" | "console" | "worker" | undefined;
179
+ consumers?: string[] | undefined;
180
+ producers?: string[] | undefined;
153
181
  }>;
154
182
  export type InterfaceEntity = z.infer<typeof InterfaceEntitySchema>;
155
183
  export declare const TableEntitySchema: z.ZodObject<{
@@ -208,12 +236,18 @@ export type TableEntity = z.infer<typeof TableEntitySchema>;
208
236
  export declare const DecisionEntitySchema: z.ZodObject<{
209
237
  id: z.ZodString;
210
238
  text: z.ZodString;
239
+ background: z.ZodOptional<z.ZodString>;
240
+ alternative: z.ZodOptional<z.ZodString>;
211
241
  }, "strip", z.ZodTypeAny, {
212
242
  id: string;
213
243
  text: string;
244
+ background?: string | undefined;
245
+ alternative?: string | undefined;
214
246
  }, {
215
247
  id: string;
216
248
  text: string;
249
+ background?: string | undefined;
250
+ alternative?: string | undefined;
217
251
  }>;
218
252
  export type DecisionEntity = z.infer<typeof DecisionEntitySchema>;
219
253
  export declare const EntitiesSchema: z.ZodObject<{
@@ -264,16 +298,26 @@ export declare const EntitiesSchema: z.ZodObject<{
264
298
  condition: z.ZodString;
265
299
  status: z.ZodString;
266
300
  note: z.ZodOptional<z.ZodString>;
301
+ /** 错误类别:backend=gRPC→HTTP 映射;guard=前端守卫行为;internal=Worker 内部语义(P5 拆分)。 */
302
+ kind: z.ZodOptional<z.ZodEnum<["backend", "guard", "internal"]>>;
267
303
  }, "strip", z.ZodTypeAny, {
268
304
  status: string;
269
305
  condition: string;
306
+ kind?: "backend" | "guard" | "internal" | undefined;
270
307
  note?: string | undefined;
271
308
  }, {
272
309
  status: string;
273
310
  condition: string;
311
+ kind?: "backend" | "guard" | "internal" | undefined;
274
312
  note?: string | undefined;
275
313
  }>, "many">;
276
314
  failure_examples: z.ZodArray<z.ZodString, "many">;
315
+ request_example: z.ZodOptional<z.ZodString>;
316
+ success_response_example: z.ZodOptional<z.ZodString>;
317
+ failure_response_example: z.ZodOptional<z.ZodString>;
318
+ side: z.ZodOptional<z.ZodEnum<["console", "worker", "internal"]>>;
319
+ consumers: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
320
+ producers: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
277
321
  }, "strip", z.ZodTypeAny, {
278
322
  path: string;
279
323
  id: string;
@@ -281,6 +325,7 @@ export declare const EntitiesSchema: z.ZodObject<{
281
325
  errors: {
282
326
  status: string;
283
327
  condition: string;
328
+ kind?: "backend" | "guard" | "internal" | undefined;
284
329
  note?: string | undefined;
285
330
  }[];
286
331
  method: "GET" | "POST" | "PUT" | "PATCH" | "DELETE";
@@ -299,6 +344,12 @@ export declare const EntitiesSchema: z.ZodObject<{
299
344
  default?: string | undefined;
300
345
  desc?: string | undefined;
301
346
  }[] | undefined;
347
+ request_example?: string | undefined;
348
+ success_response_example?: string | undefined;
349
+ failure_response_example?: string | undefined;
350
+ side?: "internal" | "console" | "worker" | undefined;
351
+ consumers?: string[] | undefined;
352
+ producers?: string[] | undefined;
302
353
  }, {
303
354
  path: string;
304
355
  id: string;
@@ -306,6 +357,7 @@ export declare const EntitiesSchema: z.ZodObject<{
306
357
  errors: {
307
358
  status: string;
308
359
  condition: string;
360
+ kind?: "backend" | "guard" | "internal" | undefined;
309
361
  note?: string | undefined;
310
362
  }[];
311
363
  method: "GET" | "POST" | "PUT" | "PATCH" | "DELETE";
@@ -324,6 +376,12 @@ export declare const EntitiesSchema: z.ZodObject<{
324
376
  required?: boolean | undefined;
325
377
  desc?: string | undefined;
326
378
  }[] | undefined;
379
+ request_example?: string | undefined;
380
+ success_response_example?: string | undefined;
381
+ failure_response_example?: string | undefined;
382
+ side?: "internal" | "console" | "worker" | undefined;
383
+ consumers?: string[] | undefined;
384
+ producers?: string[] | undefined;
327
385
  }>, "many">>, {
328
386
  path: string;
329
387
  id: string;
@@ -331,6 +389,7 @@ export declare const EntitiesSchema: z.ZodObject<{
331
389
  errors: {
332
390
  status: string;
333
391
  condition: string;
392
+ kind?: "backend" | "guard" | "internal" | undefined;
334
393
  note?: string | undefined;
335
394
  }[];
336
395
  method: "GET" | "POST" | "PUT" | "PATCH" | "DELETE";
@@ -349,6 +408,12 @@ export declare const EntitiesSchema: z.ZodObject<{
349
408
  default?: string | undefined;
350
409
  desc?: string | undefined;
351
410
  }[] | undefined;
411
+ request_example?: string | undefined;
412
+ success_response_example?: string | undefined;
413
+ failure_response_example?: string | undefined;
414
+ side?: "internal" | "console" | "worker" | undefined;
415
+ consumers?: string[] | undefined;
416
+ producers?: string[] | undefined;
352
417
  }[], {
353
418
  path: string;
354
419
  id: string;
@@ -356,6 +421,7 @@ export declare const EntitiesSchema: z.ZodObject<{
356
421
  errors: {
357
422
  status: string;
358
423
  condition: string;
424
+ kind?: "backend" | "guard" | "internal" | undefined;
359
425
  note?: string | undefined;
360
426
  }[];
361
427
  method: "GET" | "POST" | "PUT" | "PATCH" | "DELETE";
@@ -374,6 +440,12 @@ export declare const EntitiesSchema: z.ZodObject<{
374
440
  required?: boolean | undefined;
375
441
  desc?: string | undefined;
376
442
  }[] | undefined;
443
+ request_example?: string | undefined;
444
+ success_response_example?: string | undefined;
445
+ failure_response_example?: string | undefined;
446
+ side?: "internal" | "console" | "worker" | undefined;
447
+ consumers?: string[] | undefined;
448
+ producers?: string[] | undefined;
377
449
  }[] | undefined>;
378
450
  tables: z.ZodEffects<z.ZodDefault<z.ZodArray<z.ZodObject<{
379
451
  id: z.ZodString;
@@ -456,18 +528,28 @@ export declare const EntitiesSchema: z.ZodObject<{
456
528
  decisions: z.ZodEffects<z.ZodDefault<z.ZodArray<z.ZodObject<{
457
529
  id: z.ZodString;
458
530
  text: z.ZodString;
531
+ background: z.ZodOptional<z.ZodString>;
532
+ alternative: z.ZodOptional<z.ZodString>;
459
533
  }, "strip", z.ZodTypeAny, {
460
534
  id: string;
461
535
  text: string;
536
+ background?: string | undefined;
537
+ alternative?: string | undefined;
462
538
  }, {
463
539
  id: string;
464
540
  text: string;
541
+ background?: string | undefined;
542
+ alternative?: string | undefined;
465
543
  }>, "many">>, {
466
544
  id: string;
467
545
  text: string;
546
+ background?: string | undefined;
547
+ alternative?: string | undefined;
468
548
  }[], {
469
549
  id: string;
470
550
  text: string;
551
+ background?: string | undefined;
552
+ alternative?: string | undefined;
471
553
  }[] | undefined>;
472
554
  }, "strip", z.ZodTypeAny, {
473
555
  interfaces: {
@@ -477,6 +559,7 @@ export declare const EntitiesSchema: z.ZodObject<{
477
559
  errors: {
478
560
  status: string;
479
561
  condition: string;
562
+ kind?: "backend" | "guard" | "internal" | undefined;
480
563
  note?: string | undefined;
481
564
  }[];
482
565
  method: "GET" | "POST" | "PUT" | "PATCH" | "DELETE";
@@ -495,6 +578,12 @@ export declare const EntitiesSchema: z.ZodObject<{
495
578
  default?: string | undefined;
496
579
  desc?: string | undefined;
497
580
  }[] | undefined;
581
+ request_example?: string | undefined;
582
+ success_response_example?: string | undefined;
583
+ failure_response_example?: string | undefined;
584
+ side?: "internal" | "console" | "worker" | undefined;
585
+ consumers?: string[] | undefined;
586
+ producers?: string[] | undefined;
498
587
  }[];
499
588
  tables: {
500
589
  id: string;
@@ -513,6 +602,8 @@ export declare const EntitiesSchema: z.ZodObject<{
513
602
  decisions: {
514
603
  id: string;
515
604
  text: string;
605
+ background?: string | undefined;
606
+ alternative?: string | undefined;
516
607
  }[];
517
608
  }, {
518
609
  interfaces?: {
@@ -522,6 +613,7 @@ export declare const EntitiesSchema: z.ZodObject<{
522
613
  errors: {
523
614
  status: string;
524
615
  condition: string;
616
+ kind?: "backend" | "guard" | "internal" | undefined;
525
617
  note?: string | undefined;
526
618
  }[];
527
619
  method: "GET" | "POST" | "PUT" | "PATCH" | "DELETE";
@@ -540,6 +632,12 @@ export declare const EntitiesSchema: z.ZodObject<{
540
632
  required?: boolean | undefined;
541
633
  desc?: string | undefined;
542
634
  }[] | undefined;
635
+ request_example?: string | undefined;
636
+ success_response_example?: string | undefined;
637
+ failure_response_example?: string | undefined;
638
+ side?: "internal" | "console" | "worker" | undefined;
639
+ consumers?: string[] | undefined;
640
+ producers?: string[] | undefined;
543
641
  }[] | undefined;
544
642
  tables?: {
545
643
  id: string;
@@ -558,6 +656,8 @@ export declare const EntitiesSchema: z.ZodObject<{
558
656
  decisions?: {
559
657
  id: string;
560
658
  text: string;
659
+ background?: string | undefined;
660
+ alternative?: string | undefined;
561
661
  }[] | undefined;
562
662
  }>;
563
663
  export type Entities = z.infer<typeof EntitiesSchema>;
@@ -25,6 +25,8 @@ export const ErrorSpecSchema = z.object({
25
25
  condition: z.string().min(1),
26
26
  status: z.string().min(1),
27
27
  note: z.string().optional(),
28
+ /** 错误类别:backend=gRPC→HTTP 映射;guard=前端守卫行为;internal=Worker 内部语义(P5 拆分)。 */
29
+ kind: z.enum(['backend', 'guard', 'internal']).optional(),
28
30
  });
29
31
  // ============= Contract entities =============
30
32
  export const InterfaceEntitySchema = z.object({
@@ -36,6 +38,13 @@ export const InterfaceEntitySchema = z.object({
36
38
  response_fields: z.array(FieldSchema).optional(),
37
39
  errors: z.array(ErrorSpecSchema).min(1), // G2: errors required
38
40
  failure_examples: z.array(z.string()).min(1), // G2 hard gate
41
+ // 整体性文档扩展(P3/P4/P11):真实示例 body + 契约面与消费方/生产方(契约单一定义)。
42
+ request_example: z.string().optional(), // JSON body(无请求体方法省略或标注"无请求体")
43
+ success_response_example: z.string().optional(),
44
+ failure_response_example: z.string().optional(),
45
+ side: z.enum(['console', 'worker', 'internal']).optional(),
46
+ consumers: z.array(z.string()).optional(), // 消费方仓
47
+ producers: z.array(z.string()).optional(), // 生产方仓
39
48
  });
40
49
  export const TableEntitySchema = z.object({
41
50
  id: z.string().trim().min(1), // stable id, e.g. "T1"
@@ -47,7 +56,9 @@ export const TableEntitySchema = z.object({
47
56
  });
48
57
  export const DecisionEntitySchema = z.object({
49
58
  id: z.string().trim().min(1), // e.g. "D1"
50
- text: z.string().min(1),
59
+ text: z.string().min(1), // 结论
60
+ background: z.string().optional(), // ADR 背景(P6:决策前移为独立章节)
61
+ alternative: z.string().optional(), // 备选(已否决)
51
62
  });
52
63
  export const EntitiesSchema = z.object({
53
64
  interfaces: nullish(z.array(InterfaceEntitySchema).default([])),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gordon.gan/specflow",
3
- "version": "1.8.3-beta",
3
+ "version": "1.8.5-beta",
4
4
  "type": "module",
5
5
  "description": "SpecFlow — unified spec-driven development: OpenSpec planning + Superpowers execution in one CLI and cross-IDE workflow",
6
6
  "keywords": [
@@ -1,19 +1,27 @@
1
- # 章节填充:api-design(接口设计 · 契约 + 前端对接)
1
+ # 章节填充:api-design(接口与数据设计 · 接口契约 + 前端对接)
2
2
 
3
3
  你是方案文档撰写员。按大纲要点"填空题"式展开本章,逐要点填充,不自由发挥。
4
4
  - kind=entity|mixed 的要点 → 产出结构化契约实体(JSON 块 ```json {"interfaces": [...], "tables": [...], "decisions": [...]} ```)
5
5
  - kind=narrative|mixed 的要点 → 产出叙述 Markdown
6
- - 遵循 prompts/shared/artifact-language.md 的中文叙述规范(简要;怎么做用 1. 2. 3.)
6
+ - 遵循 prompts/shared/artifact-language.md 的中文叙述规范(简要;怎么做用 1. 2. 3.;**正文中文化**;**叙述分条 + 图文结合**)
7
7
  - 禁止 stub(TODO/待补充/此处省略);禁止含糊词
8
8
  - **事实从工程数据源提取,意图向用户询问,推理由你完成**(见 `prompts/document/shared/grounding.md`):本章涉及的接口字段/表列/组件名/依赖版本必须来自工程,缺失的关键信息列入缺口清单一次性询问用户,禁止编造
9
9
 
10
+ ## 本章定位:接口契约承载(P11/P13)
11
+
12
+ 本章承载**接口契约**(渲染为「接口明细」),与「数据模型」章分工:
13
+
14
+ 1. 固定顺序:接口总览(含面/消费方/生产方)→ 请求字段表 → 请求示例 → 响应字段表 → 响应示例 → 失败示例 → 错误表(按类别)。
15
+ 2. **数据库表不由本章承载**:表结构与 DDL、存量填充(G3)、回滚兼容(G4)、**契约-表映射**全部由「数据模型」章给出;本章在涉及表实体时给一句指引(如"契约-表映射见数据模型章"),**不**在本章写建表语句。
16
+ 3. **跨仓合成(≥2 仓)**:接口**单一定义**——同一 method+path 只定义一个实体,用 `side`(console/worker/internal)与 `consumers`/`producers`(仓名)标注;`<repo>_<id>` 仅作追溯别名,禁止按仓重复。仓归属用 `[repo]` 内联标注或表格「归属」列。
17
+
10
18
  ## 接口章节硬规则(借鉴 approval api-guidance + F1/F2 前端对接合并)
11
19
 
12
20
  1. **分层契约,禁止混层**:每个契约面对应一个独立 `In`(L2 Worker HTTP / L3 RPC+HTTP / L4 客户端 RPC),禁止「内部经 I7 一行代替 L4 详设」。
13
21
  2. **RPC / 服务名冻结**:禁止「暂定 / 如 Xxx / 实现时命名」;给出冻结的 RPC 名 + proto 字段号 + http_path。
14
22
  3. **proto 最小集**:新接口尽量给出可生成的 Proto 草案(rpc 名 / message / field 编号 / google.api.http)。
15
23
  4. **G2 失败示例(强制)**:清单中每个接口(含「不变」)除成功示例外,必须 ≥1 组失败示例(参数校验失败/租约过期/未认证),附完整 HTTP 或等价示例。只有错误码表不合格。
16
- 5. **固定顺序**:元信息请求体字段(或路径/Query/CLI flags)→ 请求示例成功响应字段响应示例(成功)响应示例(失败)(G2)错误表 →(可选)处理顺序。
24
+ 5. **固定顺序**:元信息(面/消费方/生产方)→ **请求字段表** **请求示例(真实 JSON body,禁止空壳;无请求体方法显式标注「无请求体」)****响应字段表****成功响应示例(真实 JSON)** **失败示例(G2,可附失败响应 body)** **错误表(backend/guard/internal 分类)**。
17
25
  6. **接口清单稳定编号**:`In` 稳定,供页面引用与跨章交叉引用。
18
26
 
19
27
  ## 前端对接维度(O5–O7,F1 §2.2 合并「契约+对接」)
@@ -18,7 +18,7 @@
18
18
 
19
19
  3. **C4 分层(AR4)**:按需给 Context(系统上下文,外部系统/用户)/ Container(可部署单元:Web/API/DB/消息)/ Component(模块组件)分层;每层给依赖方向。与 core-flow 的组件层交互时序对接(R5,交叉引用冻结 id)。
20
20
 
21
- 4. **ADR 决策记录(AR5)**:把 tech-selection 的关键决策沉淀为 ADR——背景 / 决策 / 后果 / 备选(为什么不用备选)。引用 tech-selection 的 decisions 实体 id,不另起名。
21
+ 4. **决策理由就地沉淀(AR5,可读性整改)**:关键取舍(如"为什么不用备选方案")写在技术选型章的否决/备选列与风险章的缓解列,**不设独立的 ADR/决策记录章节**。
22
22
 
23
23
  5. **架构一致性自检(AR3)**:对照目标(可扩展/可维护/性能),检查架构是否满足,给出自检结论。
24
24
 
@@ -27,4 +27,6 @@
27
27
 
28
28
  6. **禁臆造组件树(README §1.1)**:组件树必须能追溯到页面/路由清单与设计输入;不存在于输入或推断来源的页面树禁止臆造。
29
29
 
30
+ 7. **只列真实存在的组件(跨仓整体性,P1)**:只列出实际存在的组件/模块(前端组件树、后端逻辑模块、Worker 模块),每个标注归属仓(如 `[web]`/`[talos]`);**某仓在某维度没有内容就不出现**(禁止「某仓无 UI」「某仓无组件」类占位小节)。
31
+
30
32
  > 优先级:项目约定 > SpecFlow guidance > LLM。项目禁令不得被通用规则覆盖。
@@ -8,7 +8,10 @@
8
8
 
9
9
  ## 时序图硬规则(README §1.7 架构图规范 + seqdiagram 精神)
10
10
 
11
- 1. **必须有图**:主链路时序图用 Mermaid `sequenceDiagram` 源码块(diagram as code,可进 git/diff)。禁止用文字描述代替图,也禁止贴图片。
11
+ 1. **必须有图(≥2 张,P12)**:
12
+ - **整体流程图**:Mermaid `flowchart` 源码块,覆盖「编排 → 编译 → 入队 → 执行 → 上报 → 展示」全链路,**含失败分支**(编译失败不写库 / 执行失败 / 上报失败 abort)。
13
+ - **主链路时序图**:Mermaid `sequenceDiagram` 源码块(diagram as code,可进 git/diff)。禁止用文字描述代替图,也禁止贴图片。
14
+ - **失败路径决策图**:执行失败 vs 上报失败的分支单独成图(可并入整体流程图,但分支必须可见)。
12
15
 
13
16
  2. **必须有文字说明图(图 + 文成对)**:
14
17
  - 图前写一段「本图说明」:这条链路在讲什么、参与者(lifeline)分别是谁。
@@ -1,9 +1,16 @@
1
- # 章节填充:core-logic
1
+ # 章节填充:core-logic(关键实现规则)
2
2
 
3
3
  你是方案文档撰写员。按大纲要点"填空题"式展开本章,逐要点填充,不自由发挥。
4
- - kind=entity|mixed 的要点 → 产出结构化契约实体(JSON 块 ```json {"interfaces": [...], "tables": [...], "decisions": [...]} ```)
5
4
  - kind=narrative|mixed 的要点 → 产出叙述 Markdown
6
- - 遵循 prompts/shared/artifact-language.md 的中文叙述规范(简要;怎么做用 1. 2. 3.)
5
+ - 遵循 prompts/shared/artifact-language.md 的中文叙述规范(简要;怎么做用 1. 2. 3.;**正文中文化**)
7
6
  - 禁止 stub(TODO/待补充/此处省略);禁止含糊词
8
7
  - **事实从工程数据源提取,意图向用户询问,推理由你完成**(见 `prompts/document/shared/grounding.md`):本章涉及的接口字段/表列/组件名/依赖版本必须来自工程,缺失的关键信息列入缺口清单一次性询问用户,禁止编造
9
8
  - 涉及项目规约/DB/API/前端约束时,遵循「项目约定 > SpecFlow guidance > LLM」优先级
9
+
10
+ ## 关键实现规则章节硬规则(可读性整改:承接主流程、只写图中未覆盖的规则)
11
+
12
+ 1. **开头必须承接上下文(禁孤立开头)**:第一段用一两句话说明"本章规则承接主业务流程(核心链路/失败分支),回答'每一步在实现上怎么落地'"。禁止直接从"校验 xxx"开始。
13
+ 2. **只写主流程图/时序图未覆盖的细节**:链路、失败分支、幂等并发已在主业务流程章画图并说明的,本章**不得整段重复**——重复内容改用交叉引用(如"失败分叉见主业务流程·失败路径决策图")。
14
+ 3. **按环节分节**:编译规则(排序/展开/校验/信封冻结)→ 执行规则(信封校验/共享变量/失败策略细节)→ 上报规则(写入顺序/幂等键/abort 语义)。每节 1. 2. 3. 编号,一条一句。
15
+ 4. **正文中文化**:正文用中文业务术语(如「运行时信封」「步骤序号」「租约世代」),代码标识符只允许在术语表/字段表/示例中(见 artifact-language.md)。
16
+ 5. **位置**:本章属于主业务流程的"下一环节"——渲染时紧随主业务流程之后;若你发现本章内容 90% 以上已在别章讲过,则应大幅精简并只保留真正新增的规则。
@@ -1,4 +1,6 @@
1
- # 章节填充:data-model(数据结构/数据模型变更)
1
+ # 章节填充:data-model(数据结构/数据模型变更 —— 数据库表的唯一承载章节组件)
2
+
3
+ > **分工说明(P11/P13)**:涉及数据库的表时本组件必选。表结构与 DDL、存量填充(G3)、回滚兼容(G4)、**契约-表映射**全部由本章承载;接口契约本体在「接口与数据设计」章(接口明细)。无论是否跨仓合成,**契约-表映射**(每接口 ↔ 读写表/列)都必须在本章给出,禁止接口讲接口、表讲表。
2
4
 
3
5
  你是方案文档撰写员。按大纲要点"填空题"式展开本章,逐要点填充,不自由发挥。
4
6
  - kind=entity|mixed 的要点 → 产出结构化契约实体(JSON 块 ```json {"interfaces": [...], "tables": [...], "decisions": [...]} ```)
@@ -15,4 +17,5 @@
15
17
  4. **G3 存量填充**:JSON 形状变更或新增列时必须写存量默认值填充策略(回填 SQL/读时默认值/禁止空读/上线顺序)。
16
18
  5. **G4 回滚兼容**:回滚后旧版本能否安全跳过/忽略新数据?写明机制(omitempty/忽略未知键/version 分派)。禁止只写「回滚应用」。
17
19
  6. **零 DDL 迭代**:仍须展示现网 DDL,禁止假装「不涉及数据库」。
18
- 7. **优先级**:项目约定 > SpecFlow guidance > LLM。项目禁令(如「本迭代禁止迁移」)不得被通用规则覆盖。
20
+ 7. **契约-表映射(强制)**:本章(或并入的接口与数据设计章)必须给出契约-表映射表——每个相关接口一行(契约 / 读写 / 表·列 / 关键说明),字段与列对齐。
21
+ 8. **优先级**:项目约定 > SpecFlow guidance > LLM。项目禁令(如「本迭代禁止迁移」)不得被通用规则覆盖。