@gordon.gan/specflow 1.5.0-beta → 1.8.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 +2 -2
- package/dist/cli/commands/approval-assemble.d.ts +48 -5
- package/dist/cli/commands/approval-assemble.js +347 -34
- package/dist/core/approval/assemble.js +15 -22
- package/dist/core/approval/bundle.d.ts +9 -0
- package/dist/core/approval/bundle.js +172 -0
- package/dist/core/approval/forbidden-patterns.d.ts +6 -0
- package/dist/core/approval/forbidden-patterns.js +40 -0
- package/dist/core/approval/index-schema.d.ts +203 -0
- package/dist/core/approval/index-schema.js +47 -0
- package/dist/core/approval/index.d.ts +10 -2
- package/dist/core/approval/index.js +7 -1
- package/dist/core/approval/lint.d.ts +10 -0
- package/dist/core/approval/lint.js +302 -0
- package/dist/core/approval/paths.d.ts +5 -0
- package/dist/core/approval/paths.js +15 -0
- package/dist/core/approval/pipeline.d.ts +28 -0
- package/dist/core/approval/pipeline.js +146 -0
- package/dist/core/approval/playbook-schema.d.ts +182 -0
- package/dist/core/approval/playbook-schema.js +51 -0
- package/dist/core/approval/render.d.ts +20 -0
- package/dist/core/approval/render.js +210 -0
- package/dist/core/approval/review-pack.d.ts +26 -0
- package/dist/core/approval/review-pack.js +205 -0
- package/dist/core/approval/types.d.ts +103 -0
- package/dist/integrations/shared/capability-evidence.js +3 -0
- package/dist/integrations/shared/parity-manifest.js +3 -0
- package/package.json +2 -1
- package/prompts/approval/acp-pipeline.md +106 -0
- package/prompts/approval/ai-review.md +145 -0
- package/prompts/approval/generate.md +107 -9
- package/prompts/approval/multi-repo-guidance.md +71 -10
- package/prompts/approval/multi-repo-spoke-subagent.md +94 -0
- package/prompts/approval/runtime-guidance.md +64 -0
- package/prompts/approval/segmented-generation.md +14 -8
- package/skills/GUIDANCE_PACKS.md +1 -1
- package/skills/specflow-approval/SKILL.md +160 -22
- package/templates/approval-index.yaml +25 -4
- package/templates/approval-part.md +1 -1
- package/templates/approval-playbook-talos-scenario-job-compile.yaml +29 -0
- package/templates/approval-playbook.yaml +28 -0
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import { promises as fs } from 'node:fs';
|
|
3
|
+
import { join, dirname } from 'node:path';
|
|
4
|
+
import yaml from 'js-yaml';
|
|
5
|
+
import { parseApprovalPlaybook } from './playbook-schema.js';
|
|
6
|
+
import { readApprovalIndex } from './assemble.js';
|
|
7
|
+
import { getApprovalPartPath, getApprovalWorkspaceDir, getChangeDirectory, } from './paths.js';
|
|
8
|
+
function sha256(content) {
|
|
9
|
+
return createHash('sha256').update(content, 'utf8').digest('hex');
|
|
10
|
+
}
|
|
11
|
+
function resolveSourceChangeDir(workspaceRoot, source) {
|
|
12
|
+
const planningRoot = source.root ?? workspaceRoot;
|
|
13
|
+
return getChangeDirectory(planningRoot, source.change);
|
|
14
|
+
}
|
|
15
|
+
async function loadSourceParts(changeDir, includeParts) {
|
|
16
|
+
const index = await readApprovalIndex(changeDir);
|
|
17
|
+
const partIds = includeParts === 'all'
|
|
18
|
+
? [...index.parts_order]
|
|
19
|
+
: includeParts.filter((id) => index.parts_order.includes(id));
|
|
20
|
+
const results = [];
|
|
21
|
+
for (const partId of partIds) {
|
|
22
|
+
try {
|
|
23
|
+
const content = await fs.readFile(getApprovalPartPath(changeDir, partId), 'utf-8');
|
|
24
|
+
results.push({ partId, content });
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
// skip missing
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
return results;
|
|
31
|
+
}
|
|
32
|
+
export async function bundleApprovalDocument(options) {
|
|
33
|
+
const diagnostics = [];
|
|
34
|
+
const { workspaceRoot, changeDir, playbookPath, write = true } = options;
|
|
35
|
+
let playbook;
|
|
36
|
+
let indexOutputOverride;
|
|
37
|
+
try {
|
|
38
|
+
const raw = yaml.load(await fs.readFile(playbookPath, 'utf-8'));
|
|
39
|
+
playbook = parseApprovalPlaybook(raw);
|
|
40
|
+
try {
|
|
41
|
+
const index = await readApprovalIndex(changeDir);
|
|
42
|
+
indexOutputOverride = index.multi_repo?.bundle?.output;
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
// index optional for bundle-only dry runs
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
catch (error) {
|
|
49
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
50
|
+
return {
|
|
51
|
+
ok: false,
|
|
52
|
+
diagnostics: [
|
|
53
|
+
{ code: 'invalid_playbook', severity: 'error', message: `Invalid playbook: ${message}` },
|
|
54
|
+
],
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
const body = [];
|
|
58
|
+
body.push(`# ${playbook.title}`);
|
|
59
|
+
body.push('');
|
|
60
|
+
body.push(`> **合订视图** (read-only bundle) | change: \`${playbook.change}\``);
|
|
61
|
+
body.push(`> 生成方式: \`specflow approval bundle\` — 确定性拼接,禁止 LLM Reduce`);
|
|
62
|
+
body.push(`> 主仓: \`${playbook.primary_repo}\``);
|
|
63
|
+
body.push('');
|
|
64
|
+
body.push('---');
|
|
65
|
+
body.push('');
|
|
66
|
+
if (playbook.overlay && playbook.overlay.length > 0) {
|
|
67
|
+
const approvalDir = getApprovalWorkspaceDir(changeDir);
|
|
68
|
+
for (const overlayRel of playbook.overlay) {
|
|
69
|
+
const overlayPath = join(approvalDir, overlayRel);
|
|
70
|
+
try {
|
|
71
|
+
const content = await fs.readFile(overlayPath, 'utf-8');
|
|
72
|
+
if (playbook.assemble.inject_headers !== false) {
|
|
73
|
+
body.push(`## Overlay · ${overlayRel}`);
|
|
74
|
+
body.push('');
|
|
75
|
+
}
|
|
76
|
+
body.push(content.trim());
|
|
77
|
+
body.push('');
|
|
78
|
+
body.push('---');
|
|
79
|
+
body.push('');
|
|
80
|
+
}
|
|
81
|
+
catch {
|
|
82
|
+
diagnostics.push({
|
|
83
|
+
code: 'overlay_missing',
|
|
84
|
+
severity: 'error',
|
|
85
|
+
message: `Overlay file not found: ${overlayPath}`,
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
for (const source of playbook.sources) {
|
|
91
|
+
const sourceChangeDir = resolveSourceChangeDir(workspaceRoot, source);
|
|
92
|
+
try {
|
|
93
|
+
await fs.access(sourceChangeDir);
|
|
94
|
+
}
|
|
95
|
+
catch {
|
|
96
|
+
diagnostics.push({
|
|
97
|
+
code: 'source_change_missing',
|
|
98
|
+
severity: 'error',
|
|
99
|
+
message: `Source change not found: ${sourceChangeDir} (repo=${source.repo})`,
|
|
100
|
+
});
|
|
101
|
+
continue;
|
|
102
|
+
}
|
|
103
|
+
const includeParts = source.include_parts ?? 'all';
|
|
104
|
+
const parts = await loadSourceParts(sourceChangeDir, includeParts);
|
|
105
|
+
if (parts.length === 0) {
|
|
106
|
+
diagnostics.push({
|
|
107
|
+
code: 'source_parts_empty',
|
|
108
|
+
severity: 'warning',
|
|
109
|
+
message: `No parts loaded for source ${source.repo}/${source.change}`,
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
if (playbook.assemble.inject_headers !== false) {
|
|
113
|
+
body.push(`## 来源 · ${source.repo} (\`${source.change}\`)`);
|
|
114
|
+
body.push('');
|
|
115
|
+
}
|
|
116
|
+
for (const { partId, content } of parts) {
|
|
117
|
+
body.push(`<!-- bundle-source: ${source.repo}/${source.change}/${partId} -->`);
|
|
118
|
+
body.push(content.trim());
|
|
119
|
+
body.push('');
|
|
120
|
+
}
|
|
121
|
+
body.push('---');
|
|
122
|
+
body.push('');
|
|
123
|
+
}
|
|
124
|
+
if (diagnostics.some((d) => d.severity === 'error')) {
|
|
125
|
+
return { ok: false, diagnostics };
|
|
126
|
+
}
|
|
127
|
+
let markdown = body.join('\n').replace(/\n{3,}/g, '\n\n').trim() + '\n';
|
|
128
|
+
if (playbook.assemble.dedupe_appendix !== false) {
|
|
129
|
+
const appendixMatches = markdown.match(/^##\s+附录\s*A/mg);
|
|
130
|
+
if (appendixMatches && appendixMatches.length > 1) {
|
|
131
|
+
diagnostics.push({
|
|
132
|
+
code: 'duplicate_appendix',
|
|
133
|
+
severity: 'warning',
|
|
134
|
+
message: `Bundle output contains ${appendixMatches.length} "## 附录 A" headings`,
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
if (playbook.assemble.readonly) {
|
|
139
|
+
markdown =
|
|
140
|
+
`> **READ-ONLY VIEW** — apply 请使用各仓 \`approval.md\` 真源,勿直接消费本合订文件。\n\n` +
|
|
141
|
+
markdown;
|
|
142
|
+
}
|
|
143
|
+
const outputRel = indexOutputOverride ?? playbook.assemble.output;
|
|
144
|
+
const outputPath = join(changeDir, outputRel);
|
|
145
|
+
if (!write) {
|
|
146
|
+
return { ok: true, markdown, outputPath, diagnostics };
|
|
147
|
+
}
|
|
148
|
+
await fs.mkdir(dirname(outputPath), { recursive: true });
|
|
149
|
+
await fs.writeFile(outputPath, markdown, 'utf-8');
|
|
150
|
+
const bundleManifestPath = join(getApprovalWorkspaceDir(changeDir), 'bundle-manifest.json');
|
|
151
|
+
await fs.writeFile(bundleManifestPath, JSON.stringify({
|
|
152
|
+
schema: 'specflow.approval.bundle-manifest/v1',
|
|
153
|
+
change: playbook.change,
|
|
154
|
+
playbook: playbookPath,
|
|
155
|
+
output: outputRel,
|
|
156
|
+
sha256: sha256(markdown),
|
|
157
|
+
assembled_at: new Date().toISOString(),
|
|
158
|
+
sources: playbook.sources.map((s) => ({ repo: s.repo, change: s.change })),
|
|
159
|
+
}, null, 2), 'utf-8');
|
|
160
|
+
return { ok: true, markdown, outputPath, diagnostics };
|
|
161
|
+
}
|
|
162
|
+
export async function resolvePlaybookPath(changeDir, explicitPath) {
|
|
163
|
+
if (explicitPath) {
|
|
164
|
+
return explicitPath;
|
|
165
|
+
}
|
|
166
|
+
const index = await readApprovalIndex(changeDir);
|
|
167
|
+
const fromIndex = index.multi_repo?.bundle?.playbook;
|
|
168
|
+
if (fromIndex) {
|
|
169
|
+
return join(getApprovalWorkspaceDir(changeDir), fromIndex);
|
|
170
|
+
}
|
|
171
|
+
return join(getApprovalWorkspaceDir(changeDir), 'approval-playbook.yaml');
|
|
172
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/** Shared stub / inconsistency patterns for assemble lazy-check and approval lint. */
|
|
2
|
+
export declare const FORBIDDEN_STUB_PATTERNS: readonly RegExp[];
|
|
3
|
+
/** Semantic inconsistency hints (warnings unless strict mode) — long-document generic. */
|
|
4
|
+
export declare const INCONSISTENCY_HINT_PATTERNS: readonly RegExp[];
|
|
5
|
+
export declare function findForbiddenMatch(content: string): RegExp | null;
|
|
6
|
+
export declare function findInconsistencyHints(content: string): readonly RegExp[];
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/** Shared stub / inconsistency patterns for assemble lazy-check and approval lint. */
|
|
2
|
+
export const FORBIDDEN_STUB_PATTERNS = [
|
|
3
|
+
/\bTODO\b/i,
|
|
4
|
+
/待补充/,
|
|
5
|
+
/待 refine 澄清(?!.*\[)/,
|
|
6
|
+
/此处省略/,
|
|
7
|
+
/详见\s*(?:上文|下文|附件)(?!.*§)/,
|
|
8
|
+
/略\s*[。.]?$/,
|
|
9
|
+
/TBD\s*[。.]?$/,
|
|
10
|
+
/(?:^|\n)\s*\.{3}\s*(?:\n|$)/,
|
|
11
|
+
/(?:^|\n)\s*(?:同上|同前)\s*[。.]?\s*(?:\n|$)/,
|
|
12
|
+
/实现时(?:命名|对齐)/,
|
|
13
|
+
/(?:RPC|rpc).*暂定/,
|
|
14
|
+
/暂定\s*[`']?\w+[`']?/,
|
|
15
|
+
/\b如\s+[A-Z]\w*(?:Result|Request|Response)/,
|
|
16
|
+
/内部(?:经|调用)\s*I\d+[^.\n]{0,40}(?:一行|代替|省略)/,
|
|
17
|
+
];
|
|
18
|
+
/** Semantic inconsistency hints (warnings unless strict mode) — long-document generic. */
|
|
19
|
+
export const INCONSISTENCY_HINT_PATTERNS = [
|
|
20
|
+
/约\s*\d+\s*次/,
|
|
21
|
+
/\b(?:等|etc\.?)\s*[。.)]/,
|
|
22
|
+
/(?:status|状态|enum|枚举)[^.\n]{0,30}(?:等|etc)/i,
|
|
23
|
+
/(?:^|[\s,。;])同前(?!缀)/,
|
|
24
|
+
/(?:^|[\s,。;])同上(?!所述)/,
|
|
25
|
+
/(?:^|[\s,。;])见上(?!游)/,
|
|
26
|
+
/\b(?:待定|未定)\b/i,
|
|
27
|
+
/(?<![无未不否])\bTBD\b/i,
|
|
28
|
+
/\d+\s*[或\\/]\s*\d+\s*(?:均可|都行)/,
|
|
29
|
+
];
|
|
30
|
+
export function findForbiddenMatch(content) {
|
|
31
|
+
for (const pattern of FORBIDDEN_STUB_PATTERNS) {
|
|
32
|
+
if (pattern.test(content)) {
|
|
33
|
+
return pattern;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
export function findInconsistencyHints(content) {
|
|
39
|
+
return INCONSISTENCY_HINT_PATTERNS.filter((p) => p.test(content));
|
|
40
|
+
}
|
|
@@ -49,18 +49,50 @@ export declare const approvalIndexSchema: z.ZodObject<{
|
|
|
49
49
|
change: z.ZodString;
|
|
50
50
|
part: z.ZodString;
|
|
51
51
|
repo: z.ZodOptional<z.ZodString>;
|
|
52
|
+
layers: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
53
|
+
pairs_with: z.ZodOptional<z.ZodString>;
|
|
54
|
+
feature_slice: z.ZodOptional<z.ZodString>;
|
|
55
|
+
frozen: z.ZodOptional<z.ZodObject<{
|
|
56
|
+
rpc: z.ZodOptional<z.ZodString>;
|
|
57
|
+
http_path: z.ZodOptional<z.ZodString>;
|
|
58
|
+
status_enum: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
59
|
+
}, "strip", z.ZodTypeAny, {
|
|
60
|
+
rpc?: string | undefined;
|
|
61
|
+
http_path?: string | undefined;
|
|
62
|
+
status_enum?: string[] | undefined;
|
|
63
|
+
}, {
|
|
64
|
+
rpc?: string | undefined;
|
|
65
|
+
http_path?: string | undefined;
|
|
66
|
+
status_enum?: string[] | undefined;
|
|
67
|
+
}>>;
|
|
52
68
|
}, "strip", z.ZodTypeAny, {
|
|
53
69
|
id: string;
|
|
54
70
|
part: string;
|
|
55
71
|
short: string;
|
|
56
72
|
change: string;
|
|
57
73
|
repo?: string | undefined;
|
|
74
|
+
layers?: string[] | undefined;
|
|
75
|
+
pairs_with?: string | undefined;
|
|
76
|
+
feature_slice?: string | undefined;
|
|
77
|
+
frozen?: {
|
|
78
|
+
rpc?: string | undefined;
|
|
79
|
+
http_path?: string | undefined;
|
|
80
|
+
status_enum?: string[] | undefined;
|
|
81
|
+
} | undefined;
|
|
58
82
|
}, {
|
|
59
83
|
id: string;
|
|
60
84
|
part: string;
|
|
61
85
|
short: string;
|
|
62
86
|
change: string;
|
|
63
87
|
repo?: string | undefined;
|
|
88
|
+
layers?: string[] | undefined;
|
|
89
|
+
pairs_with?: string | undefined;
|
|
90
|
+
feature_slice?: string | undefined;
|
|
91
|
+
frozen?: {
|
|
92
|
+
rpc?: string | undefined;
|
|
93
|
+
http_path?: string | undefined;
|
|
94
|
+
status_enum?: string[] | undefined;
|
|
95
|
+
} | undefined;
|
|
64
96
|
}>, "many">>;
|
|
65
97
|
pages: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
66
98
|
id: z.ZodString;
|
|
@@ -209,6 +241,22 @@ export declare const approvalIndexSchema: z.ZodObject<{
|
|
|
209
241
|
change: string;
|
|
210
242
|
}[] | undefined;
|
|
211
243
|
}>>;
|
|
244
|
+
bundle: z.ZodOptional<z.ZodObject<{
|
|
245
|
+
enabled: z.ZodOptional<z.ZodBoolean>;
|
|
246
|
+
playbook: z.ZodOptional<z.ZodString>;
|
|
247
|
+
output: z.ZodOptional<z.ZodString>;
|
|
248
|
+
readonly: z.ZodOptional<z.ZodBoolean>;
|
|
249
|
+
}, "strip", z.ZodTypeAny, {
|
|
250
|
+
output?: string | undefined;
|
|
251
|
+
enabled?: boolean | undefined;
|
|
252
|
+
playbook?: string | undefined;
|
|
253
|
+
readonly?: boolean | undefined;
|
|
254
|
+
}, {
|
|
255
|
+
output?: string | undefined;
|
|
256
|
+
enabled?: boolean | undefined;
|
|
257
|
+
playbook?: string | undefined;
|
|
258
|
+
readonly?: boolean | undefined;
|
|
259
|
+
}>>;
|
|
212
260
|
}, "strip", z.ZodTypeAny, {
|
|
213
261
|
enabled: boolean;
|
|
214
262
|
repos: {
|
|
@@ -232,6 +280,12 @@ export declare const approvalIndexSchema: z.ZodObject<{
|
|
|
232
280
|
change: string;
|
|
233
281
|
}[] | undefined;
|
|
234
282
|
} | undefined;
|
|
283
|
+
bundle?: {
|
|
284
|
+
output?: string | undefined;
|
|
285
|
+
enabled?: boolean | undefined;
|
|
286
|
+
playbook?: string | undefined;
|
|
287
|
+
readonly?: boolean | undefined;
|
|
288
|
+
} | undefined;
|
|
235
289
|
}, {
|
|
236
290
|
enabled: boolean;
|
|
237
291
|
repos: {
|
|
@@ -255,6 +309,12 @@ export declare const approvalIndexSchema: z.ZodObject<{
|
|
|
255
309
|
change: string;
|
|
256
310
|
}[] | undefined;
|
|
257
311
|
} | undefined;
|
|
312
|
+
bundle?: {
|
|
313
|
+
output?: string | undefined;
|
|
314
|
+
enabled?: boolean | undefined;
|
|
315
|
+
playbook?: string | undefined;
|
|
316
|
+
readonly?: boolean | undefined;
|
|
317
|
+
} | undefined;
|
|
258
318
|
}>, {
|
|
259
319
|
enabled: boolean;
|
|
260
320
|
repos: {
|
|
@@ -278,6 +338,12 @@ export declare const approvalIndexSchema: z.ZodObject<{
|
|
|
278
338
|
change: string;
|
|
279
339
|
}[] | undefined;
|
|
280
340
|
} | undefined;
|
|
341
|
+
bundle?: {
|
|
342
|
+
output?: string | undefined;
|
|
343
|
+
enabled?: boolean | undefined;
|
|
344
|
+
playbook?: string | undefined;
|
|
345
|
+
readonly?: boolean | undefined;
|
|
346
|
+
} | undefined;
|
|
281
347
|
}, {
|
|
282
348
|
enabled: boolean;
|
|
283
349
|
repos: {
|
|
@@ -301,6 +367,81 @@ export declare const approvalIndexSchema: z.ZodObject<{
|
|
|
301
367
|
change: string;
|
|
302
368
|
}[] | undefined;
|
|
303
369
|
} | undefined;
|
|
370
|
+
bundle?: {
|
|
371
|
+
output?: string | undefined;
|
|
372
|
+
enabled?: boolean | undefined;
|
|
373
|
+
playbook?: string | undefined;
|
|
374
|
+
readonly?: boolean | undefined;
|
|
375
|
+
} | undefined;
|
|
376
|
+
}>>;
|
|
377
|
+
acp: z.ZodOptional<z.ZodObject<{
|
|
378
|
+
pipeline: z.ZodOptional<z.ZodEnum<["acp/v1", "acp/v2"]>>;
|
|
379
|
+
context_budget_tokens: z.ZodOptional<z.ZodNumber>;
|
|
380
|
+
output_budget_tokens: z.ZodOptional<z.ZodNumber>;
|
|
381
|
+
feature_slices: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
382
|
+
id: z.ZodString;
|
|
383
|
+
repos: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
384
|
+
interfaces: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
385
|
+
pages: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
386
|
+
design_points: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
387
|
+
parts: z.ZodArray<z.ZodString, "many">;
|
|
388
|
+
}, "strip", z.ZodTypeAny, {
|
|
389
|
+
id: string;
|
|
390
|
+
parts: string[];
|
|
391
|
+
repos?: string[] | undefined;
|
|
392
|
+
design_points?: string[] | undefined;
|
|
393
|
+
interfaces?: string[] | undefined;
|
|
394
|
+
pages?: string[] | undefined;
|
|
395
|
+
}, {
|
|
396
|
+
id: string;
|
|
397
|
+
parts: string[];
|
|
398
|
+
repos?: string[] | undefined;
|
|
399
|
+
design_points?: string[] | undefined;
|
|
400
|
+
interfaces?: string[] | undefined;
|
|
401
|
+
pages?: string[] | undefined;
|
|
402
|
+
}>, "many">>;
|
|
403
|
+
review: z.ZodOptional<z.ZodObject<{
|
|
404
|
+
enabled: z.ZodOptional<z.ZodBoolean>;
|
|
405
|
+
required_pass: z.ZodOptional<z.ZodBoolean>;
|
|
406
|
+
}, "strip", z.ZodTypeAny, {
|
|
407
|
+
enabled?: boolean | undefined;
|
|
408
|
+
required_pass?: boolean | undefined;
|
|
409
|
+
}, {
|
|
410
|
+
enabled?: boolean | undefined;
|
|
411
|
+
required_pass?: boolean | undefined;
|
|
412
|
+
}>>;
|
|
413
|
+
}, "strip", z.ZodTypeAny, {
|
|
414
|
+
review?: {
|
|
415
|
+
enabled?: boolean | undefined;
|
|
416
|
+
required_pass?: boolean | undefined;
|
|
417
|
+
} | undefined;
|
|
418
|
+
pipeline?: "acp/v1" | "acp/v2" | undefined;
|
|
419
|
+
context_budget_tokens?: number | undefined;
|
|
420
|
+
output_budget_tokens?: number | undefined;
|
|
421
|
+
feature_slices?: {
|
|
422
|
+
id: string;
|
|
423
|
+
parts: string[];
|
|
424
|
+
repos?: string[] | undefined;
|
|
425
|
+
design_points?: string[] | undefined;
|
|
426
|
+
interfaces?: string[] | undefined;
|
|
427
|
+
pages?: string[] | undefined;
|
|
428
|
+
}[] | undefined;
|
|
429
|
+
}, {
|
|
430
|
+
review?: {
|
|
431
|
+
enabled?: boolean | undefined;
|
|
432
|
+
required_pass?: boolean | undefined;
|
|
433
|
+
} | undefined;
|
|
434
|
+
pipeline?: "acp/v1" | "acp/v2" | undefined;
|
|
435
|
+
context_budget_tokens?: number | undefined;
|
|
436
|
+
output_budget_tokens?: number | undefined;
|
|
437
|
+
feature_slices?: {
|
|
438
|
+
id: string;
|
|
439
|
+
parts: string[];
|
|
440
|
+
repos?: string[] | undefined;
|
|
441
|
+
design_points?: string[] | undefined;
|
|
442
|
+
interfaces?: string[] | undefined;
|
|
443
|
+
pages?: string[] | undefined;
|
|
444
|
+
}[] | undefined;
|
|
304
445
|
}>>;
|
|
305
446
|
}, "strip", z.ZodTypeAny, {
|
|
306
447
|
schema: "specflow.approval.index/v1";
|
|
@@ -337,6 +478,14 @@ export declare const approvalIndexSchema: z.ZodObject<{
|
|
|
337
478
|
short: string;
|
|
338
479
|
change: string;
|
|
339
480
|
repo?: string | undefined;
|
|
481
|
+
layers?: string[] | undefined;
|
|
482
|
+
pairs_with?: string | undefined;
|
|
483
|
+
feature_slice?: string | undefined;
|
|
484
|
+
frozen?: {
|
|
485
|
+
rpc?: string | undefined;
|
|
486
|
+
http_path?: string | undefined;
|
|
487
|
+
status_enum?: string[] | undefined;
|
|
488
|
+
} | undefined;
|
|
340
489
|
}[];
|
|
341
490
|
pages: {
|
|
342
491
|
id: string;
|
|
@@ -383,6 +532,29 @@ export declare const approvalIndexSchema: z.ZodObject<{
|
|
|
383
532
|
change: string;
|
|
384
533
|
}[] | undefined;
|
|
385
534
|
} | undefined;
|
|
535
|
+
bundle?: {
|
|
536
|
+
output?: string | undefined;
|
|
537
|
+
enabled?: boolean | undefined;
|
|
538
|
+
playbook?: string | undefined;
|
|
539
|
+
readonly?: boolean | undefined;
|
|
540
|
+
} | undefined;
|
|
541
|
+
} | undefined;
|
|
542
|
+
acp?: {
|
|
543
|
+
review?: {
|
|
544
|
+
enabled?: boolean | undefined;
|
|
545
|
+
required_pass?: boolean | undefined;
|
|
546
|
+
} | undefined;
|
|
547
|
+
pipeline?: "acp/v1" | "acp/v2" | undefined;
|
|
548
|
+
context_budget_tokens?: number | undefined;
|
|
549
|
+
output_budget_tokens?: number | undefined;
|
|
550
|
+
feature_slices?: {
|
|
551
|
+
id: string;
|
|
552
|
+
parts: string[];
|
|
553
|
+
repos?: string[] | undefined;
|
|
554
|
+
design_points?: string[] | undefined;
|
|
555
|
+
interfaces?: string[] | undefined;
|
|
556
|
+
pages?: string[] | undefined;
|
|
557
|
+
}[] | undefined;
|
|
386
558
|
} | undefined;
|
|
387
559
|
}, {
|
|
388
560
|
schema: "specflow.approval.index/v1";
|
|
@@ -419,6 +591,14 @@ export declare const approvalIndexSchema: z.ZodObject<{
|
|
|
419
591
|
short: string;
|
|
420
592
|
change: string;
|
|
421
593
|
repo?: string | undefined;
|
|
594
|
+
layers?: string[] | undefined;
|
|
595
|
+
pairs_with?: string | undefined;
|
|
596
|
+
feature_slice?: string | undefined;
|
|
597
|
+
frozen?: {
|
|
598
|
+
rpc?: string | undefined;
|
|
599
|
+
http_path?: string | undefined;
|
|
600
|
+
status_enum?: string[] | undefined;
|
|
601
|
+
} | undefined;
|
|
422
602
|
}[] | undefined;
|
|
423
603
|
pages?: {
|
|
424
604
|
id: string;
|
|
@@ -465,6 +645,29 @@ export declare const approvalIndexSchema: z.ZodObject<{
|
|
|
465
645
|
change: string;
|
|
466
646
|
}[] | undefined;
|
|
467
647
|
} | undefined;
|
|
648
|
+
bundle?: {
|
|
649
|
+
output?: string | undefined;
|
|
650
|
+
enabled?: boolean | undefined;
|
|
651
|
+
playbook?: string | undefined;
|
|
652
|
+
readonly?: boolean | undefined;
|
|
653
|
+
} | undefined;
|
|
654
|
+
} | undefined;
|
|
655
|
+
acp?: {
|
|
656
|
+
review?: {
|
|
657
|
+
enabled?: boolean | undefined;
|
|
658
|
+
required_pass?: boolean | undefined;
|
|
659
|
+
} | undefined;
|
|
660
|
+
pipeline?: "acp/v1" | "acp/v2" | undefined;
|
|
661
|
+
context_budget_tokens?: number | undefined;
|
|
662
|
+
output_budget_tokens?: number | undefined;
|
|
663
|
+
feature_slices?: {
|
|
664
|
+
id: string;
|
|
665
|
+
parts: string[];
|
|
666
|
+
repos?: string[] | undefined;
|
|
667
|
+
design_points?: string[] | undefined;
|
|
668
|
+
interfaces?: string[] | undefined;
|
|
669
|
+
pages?: string[] | undefined;
|
|
670
|
+
}[] | undefined;
|
|
468
671
|
} | undefined;
|
|
469
672
|
}>;
|
|
470
673
|
export declare function parseApprovalIndex(raw: unknown): ApprovalIndex;
|
|
@@ -6,12 +6,21 @@ const tableRefSchema = z.object({
|
|
|
6
6
|
part: z.string().min(1),
|
|
7
7
|
repo: z.string().min(1).optional(),
|
|
8
8
|
});
|
|
9
|
+
const interfaceFrozenSchema = z.object({
|
|
10
|
+
rpc: z.string().min(1).optional(),
|
|
11
|
+
http_path: z.string().min(1).optional(),
|
|
12
|
+
status_enum: z.array(z.string().min(1)).optional(),
|
|
13
|
+
});
|
|
9
14
|
const interfaceRefSchema = z.object({
|
|
10
15
|
id: z.string().min(1),
|
|
11
16
|
short: z.string().min(1),
|
|
12
17
|
change: z.string().min(1),
|
|
13
18
|
part: z.string().min(1),
|
|
14
19
|
repo: z.string().min(1).optional(),
|
|
20
|
+
layers: z.array(z.string().min(1)).optional(),
|
|
21
|
+
pairs_with: z.string().min(1).optional(),
|
|
22
|
+
feature_slice: z.string().min(1).optional(),
|
|
23
|
+
frozen: interfaceFrozenSchema.optional(),
|
|
15
24
|
});
|
|
16
25
|
const pageRefSchema = z.object({
|
|
17
26
|
id: z.string().min(1),
|
|
@@ -49,6 +58,14 @@ const multiRepoSchema = z
|
|
|
49
58
|
per_repo: z.array(outputRefSchema).optional(),
|
|
50
59
|
})
|
|
51
60
|
.optional(),
|
|
61
|
+
bundle: z
|
|
62
|
+
.object({
|
|
63
|
+
enabled: z.boolean().optional(),
|
|
64
|
+
playbook: z.string().min(1).optional(),
|
|
65
|
+
output: z.string().min(1).optional(),
|
|
66
|
+
readonly: z.boolean().optional(),
|
|
67
|
+
})
|
|
68
|
+
.optional(),
|
|
52
69
|
})
|
|
53
70
|
.superRefine((value, ctx) => {
|
|
54
71
|
if (!value.enabled) {
|
|
@@ -75,6 +92,13 @@ const multiRepoSchema = z
|
|
|
75
92
|
path: ['primary_repo'],
|
|
76
93
|
});
|
|
77
94
|
}
|
|
95
|
+
if (value.bundle?.enabled === true && !value.primary_repo) {
|
|
96
|
+
ctx.addIssue({
|
|
97
|
+
code: z.ZodIssueCode.custom,
|
|
98
|
+
message: 'multi_repo.primary_repo is required when bundle.enabled is true',
|
|
99
|
+
path: ['primary_repo'],
|
|
100
|
+
});
|
|
101
|
+
}
|
|
78
102
|
});
|
|
79
103
|
export const approvalIndexSchema = z.object({
|
|
80
104
|
schema: z.literal('specflow.approval.index/v1'),
|
|
@@ -127,6 +151,29 @@ export const approvalIndexSchema = z.object({
|
|
|
127
151
|
})
|
|
128
152
|
.optional(),
|
|
129
153
|
multi_repo: multiRepoSchema.optional(),
|
|
154
|
+
acp: z
|
|
155
|
+
.object({
|
|
156
|
+
pipeline: z.enum(['acp/v1', 'acp/v2']).optional(),
|
|
157
|
+
context_budget_tokens: z.number().int().positive().optional(),
|
|
158
|
+
output_budget_tokens: z.number().int().positive().optional(),
|
|
159
|
+
feature_slices: z
|
|
160
|
+
.array(z.object({
|
|
161
|
+
id: z.string().min(1),
|
|
162
|
+
repos: z.array(z.string()).optional(),
|
|
163
|
+
interfaces: z.array(z.string()).optional(),
|
|
164
|
+
pages: z.array(z.string()).optional(),
|
|
165
|
+
design_points: z.array(z.string()).optional(),
|
|
166
|
+
parts: z.array(z.string().min(1)).min(1),
|
|
167
|
+
}))
|
|
168
|
+
.optional(),
|
|
169
|
+
review: z
|
|
170
|
+
.object({
|
|
171
|
+
enabled: z.boolean().optional(),
|
|
172
|
+
required_pass: z.boolean().optional(),
|
|
173
|
+
})
|
|
174
|
+
.optional(),
|
|
175
|
+
})
|
|
176
|
+
.optional(),
|
|
130
177
|
});
|
|
131
178
|
export function parseApprovalIndex(raw) {
|
|
132
179
|
return approvalIndexSchema.parse(raw);
|
|
@@ -1,4 +1,12 @@
|
|
|
1
|
-
export type { ApprovalAssembleResult, ApprovalDiagnostic, ApprovalIndex, ApprovalManifest, ApprovalGenerationMode, ApprovalMultiRepo, ApprovalDocumentMode, } from './types.js';
|
|
1
|
+
export type { ApprovalAssembleResult, ApprovalDiagnostic, ApprovalIndex, ApprovalManifest, ApprovalGenerationMode, ApprovalMultiRepo, ApprovalDocumentMode, ApprovalPlaybook, ApprovalBundleResult, ApprovalReviewPacket, ApprovalReviewResult, ApprovalAcpBlock, ApprovalFeatureSlice, } from './types.js';
|
|
2
2
|
export { parseApprovalIndex, isLightweightApproval, shouldForceSegmented, approvalIndexSchema, } from './index-schema.js';
|
|
3
|
-
export {
|
|
3
|
+
export { parseApprovalPlaybook, parseApprovalReviewResult } from './playbook-schema.js';
|
|
4
|
+
export { getChangeDirectory, getApprovalWorkspaceDir, getApprovalIndexPath, getApprovalManifestPath, getApprovalPartsDir, getApprovalPartPath, getApprovalOutputPath, getApprovalAnalysisPath, getApprovalReviewPacketPath, getApprovalReviewPath, getApprovalPlaybookPath, getApprovalBundleOutputPath, } from './paths.js';
|
|
4
5
|
export { assembleApprovalDocument, readApprovalIndex, readApprovalManifest, } from './assemble.js';
|
|
6
|
+
export { lintApprovalDocument } from './lint.js';
|
|
7
|
+
export { bundleApprovalDocument, resolvePlaybookPath } from './bundle.js';
|
|
8
|
+
export { generateReviewPacket, checkApprovalReview, computeReviewPartHashes } from './review-pack.js';
|
|
9
|
+
export { finalizeApprovalDocument } from './pipeline.js';
|
|
10
|
+
export type { FinalizeApprovalResult, FinalizeApprovalOptions, ApprovalFinalizeStage } from './pipeline.js';
|
|
11
|
+
export { renderApprovalDocument, resolveApprovalRenderPaths } from './render.js';
|
|
12
|
+
export type { RenderApprovalOptions, RenderApprovalResult } from './render.js';
|
|
@@ -1,3 +1,9 @@
|
|
|
1
1
|
export { parseApprovalIndex, isLightweightApproval, shouldForceSegmented, approvalIndexSchema, } from './index-schema.js';
|
|
2
|
-
export {
|
|
2
|
+
export { parseApprovalPlaybook, parseApprovalReviewResult } from './playbook-schema.js';
|
|
3
|
+
export { getChangeDirectory, getApprovalWorkspaceDir, getApprovalIndexPath, getApprovalManifestPath, getApprovalPartsDir, getApprovalPartPath, getApprovalOutputPath, getApprovalAnalysisPath, getApprovalReviewPacketPath, getApprovalReviewPath, getApprovalPlaybookPath, getApprovalBundleOutputPath, } from './paths.js';
|
|
3
4
|
export { assembleApprovalDocument, readApprovalIndex, readApprovalManifest, } from './assemble.js';
|
|
5
|
+
export { lintApprovalDocument } from './lint.js';
|
|
6
|
+
export { bundleApprovalDocument, resolvePlaybookPath } from './bundle.js';
|
|
7
|
+
export { generateReviewPacket, checkApprovalReview, computeReviewPartHashes } from './review-pack.js';
|
|
8
|
+
export { finalizeApprovalDocument } from './pipeline.js';
|
|
9
|
+
export { renderApprovalDocument, resolveApprovalRenderPaths } from './render.js';
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { ApprovalDiagnostic } from './types.js';
|
|
2
|
+
export interface ApprovalLintOptions {
|
|
3
|
+
readonly changeDir: string;
|
|
4
|
+
readonly strict?: boolean;
|
|
5
|
+
}
|
|
6
|
+
export interface ApprovalLintResult {
|
|
7
|
+
readonly ok: boolean;
|
|
8
|
+
readonly diagnostics: readonly ApprovalDiagnostic[];
|
|
9
|
+
}
|
|
10
|
+
export declare function lintApprovalDocument(options: ApprovalLintOptions): Promise<ApprovalLintResult>;
|