@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.
- package/dist/cli/commands/document-run.js +6 -3
- package/dist/core/document/engine.js +46 -24
- package/dist/core/document/gates.js +33 -7
- package/dist/core/document/lint.d.ts +37 -1
- package/dist/core/document/lint.js +201 -6
- package/dist/core/document/render.d.ts +7 -4
- package/dist/core/document/render.js +100 -53
- package/dist/core/document/schemas.d.ts +100 -0
- package/dist/core/document/schemas.js +12 -1
- package/package.json +1 -1
- package/prompts/document/map/api-design.md +11 -3
- package/prompts/document/map/architecture.md +1 -1
- package/prompts/document/map/component-design.md +2 -0
- package/prompts/document/map/core-flow.md +4 -1
- package/prompts/document/map/core-logic.md +10 -3
- package/prompts/document/map/data-model.md +5 -2
- package/prompts/document/map/requirement.md +6 -1
- package/prompts/document/map/test-strategy.md +1 -1
- package/prompts/document/outline/general.md +9 -0
- package/prompts/document/shared/grounding.md +7 -0
- package/prompts/shared/artifact-language.md +9 -0
- package/skills/specflow-techdoc-synth/SKILL.md +12 -9
- package/templates/document/chapters/api-design.yaml +14 -5
- package/templates/document/chapters/architecture.yaml +1 -2
- package/templates/document/chapters/core-flow.yaml +2 -0
- package/templates/document/chapters/core-logic.yaml +8 -6
- package/templates/document/chapters/data-model.yaml +9 -5
- package/templates/document/chapters/mvp-boundary.yaml +0 -3
- package/templates/document/chapters/requirement.yaml +5 -7
- package/templates/document/chapters/tech-selection.yaml +0 -3
- package/templates/document/chapters/test-strategy.yaml +2 -5
- package/templates/document/chapters/ui-design.yaml +0 -3
- package/templates/document/profiles/approve.yaml +3 -2
- 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
|
|
34
|
+
const meta = [
|
|
35
35
|
`##### ${i.id} · ${escapeInline(i.name)}(${i.method} ${i.path})`,
|
|
36
36
|
'',
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
'',
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
'
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
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
|
|
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
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
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
|
|
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
|
-
//
|
|
193
|
-
|
|
194
|
-
|
|
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
|
-
//
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
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,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.
|
|
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.
|
|
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.
|
|
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.
|
|
20
|
+
7. **契约-表映射(强制)**:本章(或并入的接口与数据设计章)必须给出契约-表映射表——每个相关接口一行(契约 / 读写 / 表·列 / 关键说明),字段与列对齐。
|
|
21
|
+
8. **优先级**:项目约定 > SpecFlow guidance > LLM。项目禁令(如「本迭代禁止迁移」)不得被通用规则覆盖。
|