@gordon.gan/specflow 1.8.0-beta → 1.8.2-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 +7 -5
- package/dist/cli/commands/document-run.d.ts +98 -0
- package/dist/cli/commands/document-run.js +1064 -0
- package/dist/cli/index.js +2 -2
- package/dist/core/artifact-language.js +11 -1
- package/dist/core/document/asset-paths.d.ts +15 -0
- package/dist/core/document/asset-paths.js +38 -0
- package/dist/core/document/chapters.d.ts +6 -0
- package/dist/core/document/chapters.js +38 -0
- package/dist/core/document/coverage.d.ts +25 -0
- package/dist/core/document/coverage.js +49 -0
- package/dist/core/document/digests.d.ts +40 -0
- package/dist/core/document/digests.js +144 -0
- package/dist/core/document/engine.d.ts +101 -0
- package/dist/core/document/engine.js +722 -0
- package/dist/core/document/extract.d.ts +61 -0
- package/dist/core/document/extract.js +437 -0
- package/dist/core/{approval → document}/forbidden-patterns.d.ts +4 -2
- package/dist/core/document/forbidden-patterns.js +36 -0
- package/dist/core/document/gates.d.ts +39 -0
- package/dist/core/document/gates.js +75 -0
- package/dist/core/document/input-digest.d.ts +63 -0
- package/dist/core/document/input-digest.js +200 -0
- package/dist/core/document/input-features.d.ts +43 -0
- package/dist/core/document/input-features.js +161 -0
- package/dist/core/document/lint.d.ts +39 -0
- package/dist/core/document/lint.js +147 -0
- package/dist/core/document/llm.d.ts +28 -0
- package/dist/core/document/llm.js +22 -0
- package/dist/core/document/map.d.ts +52 -0
- package/dist/core/document/map.js +296 -0
- package/dist/core/document/outline.d.ts +29 -0
- package/dist/core/document/outline.js +171 -0
- package/dist/core/document/paths.d.ts +23 -0
- package/dist/core/document/paths.js +36 -0
- package/dist/core/document/profile-validator.d.ts +23 -0
- package/dist/core/document/profile-validator.js +155 -0
- package/dist/core/document/profiles.d.ts +23 -0
- package/dist/core/document/profiles.js +76 -0
- package/dist/core/document/render.d.ts +61 -0
- package/dist/core/document/render.js +243 -0
- package/dist/core/document/review.d.ts +57 -0
- package/dist/core/document/review.js +202 -0
- package/dist/core/document/scene-detect.d.ts +32 -0
- package/dist/core/document/scene-detect.js +237 -0
- package/dist/core/document/schemas.d.ts +1111 -0
- package/dist/core/document/schemas.js +191 -0
- package/dist/integrations/shared/capability-evidence.js +4 -9
- package/dist/integrations/shared/command-catalog.js +2 -1
- package/dist/integrations/shared/parity-manifest.js +4 -9
- package/package.json +2 -1
- package/prompts/apply/phase-a-plan.md +1 -1
- package/prompts/document/map/acceptance.md +9 -0
- package/prompts/document/map/anti-ai.md +29 -0
- package/prompts/document/map/api-design.md +26 -0
- package/prompts/document/map/architecture.md +27 -0
- package/prompts/document/map/benchmark.md +26 -0
- package/prompts/document/map/closed-loop.md +9 -0
- package/prompts/document/map/compat-migration.md +31 -0
- package/prompts/document/map/component-design.md +30 -0
- package/prompts/document/map/config-runtime.md +9 -0
- package/prompts/document/map/core-flow.md +62 -0
- package/prompts/document/map/core-logic.md +9 -0
- package/prompts/document/map/data-model.md +18 -0
- package/prompts/document/map/deploy.md +26 -0
- package/prompts/document/map/fix.md +8 -0
- package/prompts/document/map/frontend-architecture.md +35 -0
- package/prompts/document/map/goal.md +8 -0
- package/prompts/document/map/impact.md +8 -0
- package/prompts/document/map/implementability.md +9 -0
- package/prompts/document/map/migration-guide.md +36 -0
- package/prompts/document/map/mvp-boundary.md +8 -0
- package/prompts/document/map/non-goals.md +8 -0
- package/prompts/document/map/ops.md +33 -0
- package/prompts/document/map/performance.md +32 -0
- package/prompts/document/map/poc-demo.md +25 -0
- package/prompts/document/map/regression.md +8 -0
- package/prompts/document/map/reproduce.md +8 -0
- package/prompts/document/map/requirement.md +8 -0
- package/prompts/document/map/research.md +25 -0
- package/prompts/document/map/root-cause.md +8 -0
- package/prompts/document/map/signoff.md +9 -0
- package/prompts/document/map/state-management.md +23 -0
- package/prompts/document/map/tech-selection.md +29 -0
- package/prompts/document/map/test-strategy.md +24 -0
- package/prompts/document/map/ui-design.md +21 -0
- package/prompts/document/outline/general.md +46 -0
- package/prompts/document/review/ai-review.md +57 -0
- package/prompts/document/shared/grounding.md +84 -0
- package/prompts/explore/explore-session.md +1 -1
- package/prompts/propose/design-draft.md +1 -1
- package/prompts/propose/proposal.md +1 -1
- package/prompts/propose/specs.md +1 -1
- package/prompts/propose/tasks-draft.md +1 -1
- package/prompts/refine/design-output.md +1 -1
- package/prompts/refine/update-artifacts.md +2 -0
- package/prompts/shared/artifact-language.md +13 -0
- package/skills/specflow-techdoc/SKILL.md +143 -0
- package/skills/specflow-techdoc-synth/SKILL.md +115 -0
- package/templates/document/chapters/acceptance.yaml +16 -0
- package/templates/document/chapters/api-design.yaml +29 -0
- package/templates/document/chapters/architecture.yaml +21 -0
- package/templates/document/chapters/benchmark.yaml +20 -0
- package/templates/document/chapters/closed-loop.yaml +22 -0
- package/templates/document/chapters/compat-migration.yaml +21 -0
- package/templates/document/chapters/component-design.yaml +22 -0
- package/templates/document/chapters/config-runtime.yaml +16 -0
- package/templates/document/chapters/core-flow.yaml +27 -0
- package/templates/document/chapters/core-logic.yaml +16 -0
- package/templates/document/chapters/data-model.yaml +25 -0
- package/templates/document/chapters/deploy.yaml +22 -0
- package/templates/document/chapters/fix.yaml +18 -0
- package/templates/document/chapters/frontend-architecture.yaml +22 -0
- package/templates/document/chapters/goal.yaml +17 -0
- package/templates/document/chapters/impact.yaml +16 -0
- package/templates/document/chapters/implementability.yaml +22 -0
- package/templates/document/chapters/migration-guide.yaml +21 -0
- package/templates/document/chapters/mvp-boundary.yaml +20 -0
- package/templates/document/chapters/non-goals.yaml +17 -0
- package/templates/document/chapters/ops.yaml +25 -0
- package/templates/document/chapters/performance.yaml +21 -0
- package/templates/document/chapters/poc-demo.yaml +22 -0
- package/templates/document/chapters/regression.yaml +16 -0
- package/templates/document/chapters/reproduce.yaml +16 -0
- package/templates/document/chapters/requirement.yaml +18 -0
- package/templates/document/chapters/research.yaml +22 -0
- package/templates/document/chapters/root-cause.yaml +18 -0
- package/templates/document/chapters/signoff.yaml +15 -0
- package/templates/document/chapters/state-management.yaml +22 -0
- package/templates/document/chapters/tech-selection.yaml +23 -0
- package/templates/document/chapters/test-strategy.yaml +23 -0
- package/templates/document/chapters/ui-design.yaml +28 -0
- package/templates/document/profiles/0to1.yaml +44 -0
- package/templates/document/profiles/approve.yaml +22 -0
- package/templates/document/profiles/bugfix.yaml +15 -0
- package/templates/document/profiles/feature.yaml +23 -0
- package/templates/document/profiles/frontend-0to1.yaml +47 -0
- package/templates/document/profiles/migration.yaml +42 -0
- package/templates/document/profiles/poc.yaml +46 -0
- package/dist/cli/commands/approval-assemble.d.ts +0 -64
- package/dist/cli/commands/approval-assemble.js +0 -408
- package/dist/core/approval/assemble.d.ts +0 -10
- package/dist/core/approval/assemble.js +0 -384
- package/dist/core/approval/bundle.d.ts +0 -9
- package/dist/core/approval/bundle.js +0 -172
- package/dist/core/approval/forbidden-patterns.js +0 -40
- package/dist/core/approval/index-schema.d.ts +0 -676
- package/dist/core/approval/index-schema.js +0 -193
- package/dist/core/approval/index.d.ts +0 -12
- package/dist/core/approval/index.js +0 -9
- package/dist/core/approval/lint.d.ts +0 -10
- package/dist/core/approval/lint.js +0 -302
- package/dist/core/approval/paths.d.ts +0 -13
- package/dist/core/approval/paths.js +0 -43
- package/dist/core/approval/pipeline.d.ts +0 -28
- package/dist/core/approval/pipeline.js +0 -146
- package/dist/core/approval/playbook-schema.d.ts +0 -182
- package/dist/core/approval/playbook-schema.js +0 -51
- package/dist/core/approval/render.d.ts +0 -20
- package/dist/core/approval/render.js +0 -210
- package/dist/core/approval/review-pack.d.ts +0 -26
- package/dist/core/approval/review-pack.js +0 -205
- package/dist/core/approval/types.d.ts +0 -223
- package/dist/core/approval/types.js +0 -1
- package/prompts/approval/acp-pipeline.md +0 -106
- package/prompts/approval/ai-review.md +0 -145
- package/prompts/approval/api-guidance.md +0 -179
- package/prompts/approval/database-guidance.md +0 -105
- package/prompts/approval/frontend-guidance.md +0 -249
- package/prompts/approval/generate.md +0 -1596
- package/prompts/approval/multi-repo-guidance.md +0 -263
- package/prompts/approval/multi-repo-spoke-subagent.md +0 -94
- package/prompts/approval/project-conventions-guidance.md +0 -171
- package/prompts/approval/runtime-guidance.md +0 -64
- package/prompts/approval/segmented-generation.md +0 -157
- package/skills/specflow-approval/SKILL.md +0 -778
- package/templates/approval-index.yaml +0 -93
- package/templates/approval-part.md +0 -15
- package/templates/approval-playbook-talos-scenario-job-compile.yaml +0 -29
- package/templates/approval-playbook.yaml +0 -28
- package/templates/approval.md +0 -261
|
@@ -1,205 +0,0 @@
|
|
|
1
|
-
import { createHash } from 'node:crypto';
|
|
2
|
-
import { promises as fs } from 'node:fs';
|
|
3
|
-
import { parseApprovalReviewResult } from './playbook-schema.js';
|
|
4
|
-
import { readApprovalIndex } from './assemble.js';
|
|
5
|
-
import { lintApprovalDocument } from './lint.js';
|
|
6
|
-
import { getApprovalPartPath, getApprovalReviewPath, getApprovalReviewPacketPath, } from './paths.js';
|
|
7
|
-
const AI_REVIEW_CHECKLIST = [
|
|
8
|
-
'Cross-part naming: same entity/id canonical across §2, §4, diagrams, index.yaml (no 暂定/TBD for listed ids)',
|
|
9
|
-
'Decision traceability: each §2/decisions[] row reflected with concrete behavior in §4',
|
|
10
|
-
'Inventory ↔ body: every index table/interface/page/design_point has substantive content in its part',
|
|
11
|
-
'Depth parity: sibling parts / feature_slices have comparable depth (no one-line stubs next to full sections)',
|
|
12
|
-
'Cross-reference integrity: refs use stable ids (§4.5 In, P*, Page*, T*); targets exist',
|
|
13
|
-
'Diagram ↔ prose: sequence/flow diagrams match adjacent text (actors, messages, errors)',
|
|
14
|
-
'Contract completeness: listed interfaces/page-apis meet Part E minimum (incl. failure example when api_in_scope)',
|
|
15
|
-
'Enum/state closure: status/enum sets explicit and closed where defined (no open-ended 「等」)',
|
|
16
|
-
'Unclosed branches: every mentioned retry/cancel/timeout/idempotency path has outcome or [待 refine 澄清]',
|
|
17
|
-
'UI ↔ API alignment: §4.6 fields and api refs match §4.5 when ui_in_scope',
|
|
18
|
-
'Multi-repo parity: each in-scope repo slice has proportionate depth when multi_repo.enabled',
|
|
19
|
-
'Stub purge: no TODO/待补充/placeholder-only parts',
|
|
20
|
-
'Verdict coherence: §9 readiness matches §4 depth',
|
|
21
|
-
];
|
|
22
|
-
function sha256(content) {
|
|
23
|
-
return createHash('sha256').update(content, 'utf8').digest('hex');
|
|
24
|
-
}
|
|
25
|
-
export async function computeReviewPartHashes(changeDir, partIds) {
|
|
26
|
-
const hashes = [];
|
|
27
|
-
for (const partId of partIds) {
|
|
28
|
-
try {
|
|
29
|
-
const content = await fs.readFile(getApprovalPartPath(changeDir, partId), 'utf-8');
|
|
30
|
-
hashes.push({ id: partId, sha256: sha256(content) });
|
|
31
|
-
}
|
|
32
|
-
catch {
|
|
33
|
-
// missing part handled elsewhere
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
return hashes;
|
|
37
|
-
}
|
|
38
|
-
export async function generateReviewPacket(options) {
|
|
39
|
-
const diagnostics = [];
|
|
40
|
-
const { changeDir } = options;
|
|
41
|
-
let index;
|
|
42
|
-
try {
|
|
43
|
-
index = await readApprovalIndex(changeDir);
|
|
44
|
-
}
|
|
45
|
-
catch (error) {
|
|
46
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
47
|
-
return {
|
|
48
|
-
ok: false,
|
|
49
|
-
diagnostics: [
|
|
50
|
-
{ code: 'missing_index', severity: 'error', message: `Cannot read index: ${message}` },
|
|
51
|
-
],
|
|
52
|
-
};
|
|
53
|
-
}
|
|
54
|
-
const lintResult = await lintApprovalDocument({ changeDir, strict: false });
|
|
55
|
-
const parts = [];
|
|
56
|
-
for (const partId of index.parts_order) {
|
|
57
|
-
try {
|
|
58
|
-
const content = await fs.readFile(getApprovalPartPath(changeDir, partId), 'utf-8');
|
|
59
|
-
parts.push({
|
|
60
|
-
id: partId,
|
|
61
|
-
sha256: sha256(content),
|
|
62
|
-
bytes: Buffer.byteLength(content, 'utf8'),
|
|
63
|
-
});
|
|
64
|
-
}
|
|
65
|
-
catch {
|
|
66
|
-
diagnostics.push({
|
|
67
|
-
code: 'review_part_missing',
|
|
68
|
-
severity: 'warning',
|
|
69
|
-
message: `Part "${partId}" missing for review packet`,
|
|
70
|
-
part: partId,
|
|
71
|
-
});
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
const packet = {
|
|
75
|
-
schema: 'specflow.approval.review-packet/v1',
|
|
76
|
-
change: index.change,
|
|
77
|
-
generated_at: new Date().toISOString(),
|
|
78
|
-
index_summary: {
|
|
79
|
-
interfaces: index.interfaces.length,
|
|
80
|
-
pages: index.pages.length,
|
|
81
|
-
tables: index.tables.length,
|
|
82
|
-
parts: parts.length,
|
|
83
|
-
},
|
|
84
|
-
lint_diagnostics: lintResult.diagnostics,
|
|
85
|
-
parts,
|
|
86
|
-
checklist: [...AI_REVIEW_CHECKLIST],
|
|
87
|
-
instructions: [
|
|
88
|
-
'Stage 12e AI Review: Read prompts/approval/ai-review.md',
|
|
89
|
-
'1. Read approval/review-packet.json and all parts listed',
|
|
90
|
-
'2. Fix issues in parts (not in assembled approval.md)',
|
|
91
|
-
'3. Write approval/review-result.json with findings + part_hashes (specflow approval review-hashes <change> --json)',
|
|
92
|
-
'4. Re-run: specflow approval finalize <change>',
|
|
93
|
-
].join('\n'),
|
|
94
|
-
};
|
|
95
|
-
const packetPath = getApprovalReviewPacketPath(changeDir);
|
|
96
|
-
await fs.writeFile(packetPath, JSON.stringify(packet, null, 2), 'utf-8');
|
|
97
|
-
return { ok: true, packet, packetPath, diagnostics };
|
|
98
|
-
}
|
|
99
|
-
export async function checkApprovalReview(options) {
|
|
100
|
-
const diagnostics = [];
|
|
101
|
-
const { changeDir, requirePass = true } = options;
|
|
102
|
-
let index;
|
|
103
|
-
try {
|
|
104
|
-
index = await readApprovalIndex(changeDir);
|
|
105
|
-
}
|
|
106
|
-
catch (error) {
|
|
107
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
108
|
-
return {
|
|
109
|
-
ok: false,
|
|
110
|
-
diagnostics: [
|
|
111
|
-
{ code: 'missing_index', severity: 'error', message: `Cannot read index: ${message}` },
|
|
112
|
-
],
|
|
113
|
-
};
|
|
114
|
-
}
|
|
115
|
-
const reviewRequired = options.forceRequire === true ||
|
|
116
|
-
index.acp?.review?.required_pass === true ||
|
|
117
|
-
index.acp?.review?.enabled === true;
|
|
118
|
-
let review;
|
|
119
|
-
try {
|
|
120
|
-
const raw = JSON.parse(await fs.readFile(getApprovalReviewPath(changeDir), 'utf-8'));
|
|
121
|
-
review = parseApprovalReviewResult(raw);
|
|
122
|
-
}
|
|
123
|
-
catch {
|
|
124
|
-
if (reviewRequired && requirePass) {
|
|
125
|
-
return {
|
|
126
|
-
ok: false,
|
|
127
|
-
diagnostics: [
|
|
128
|
-
{
|
|
129
|
-
code: 'review_missing',
|
|
130
|
-
severity: 'error',
|
|
131
|
-
message: 'approval/review-result.json missing; run AI review (Stage 12e) and write review-result.json',
|
|
132
|
-
},
|
|
133
|
-
],
|
|
134
|
-
};
|
|
135
|
-
}
|
|
136
|
-
return { ok: true, diagnostics: [] };
|
|
137
|
-
}
|
|
138
|
-
if (review.change !== index.change) {
|
|
139
|
-
diagnostics.push({
|
|
140
|
-
code: 'review_change_mismatch',
|
|
141
|
-
severity: 'error',
|
|
142
|
-
message: `review-result change "${review.change}" != index change "${index.change}"`,
|
|
143
|
-
});
|
|
144
|
-
}
|
|
145
|
-
const openFindings = review.findings.filter((f) => f.status === 'open');
|
|
146
|
-
for (const f of openFindings) {
|
|
147
|
-
diagnostics.push({
|
|
148
|
-
code: 'review_finding_open',
|
|
149
|
-
severity: f.severity === 'critical' ? 'error' : 'warning',
|
|
150
|
-
message: `[${f.id}] ${f.message}`,
|
|
151
|
-
part: f.part,
|
|
152
|
-
});
|
|
153
|
-
}
|
|
154
|
-
const waivedWithoutReason = review.findings.filter((f) => f.status === 'waived' && !f.waiver_reason?.trim());
|
|
155
|
-
for (const f of waivedWithoutReason) {
|
|
156
|
-
diagnostics.push({
|
|
157
|
-
code: 'review_waiver_missing_reason',
|
|
158
|
-
severity: 'error',
|
|
159
|
-
message: `[${f.id}] waived finding must include waiver_reason`,
|
|
160
|
-
part: f.part,
|
|
161
|
-
});
|
|
162
|
-
}
|
|
163
|
-
if (review.verdict === 'fail') {
|
|
164
|
-
diagnostics.push({
|
|
165
|
-
code: 'review_verdict_fail',
|
|
166
|
-
severity: 'error',
|
|
167
|
-
message: 'review-result verdict is fail',
|
|
168
|
-
});
|
|
169
|
-
}
|
|
170
|
-
if (review.part_hashes && review.part_hashes.length > 0) {
|
|
171
|
-
for (const entry of review.part_hashes) {
|
|
172
|
-
try {
|
|
173
|
-
const content = await fs.readFile(getApprovalPartPath(changeDir, entry.id), 'utf-8');
|
|
174
|
-
const current = sha256(content);
|
|
175
|
-
if (current !== entry.sha256) {
|
|
176
|
-
diagnostics.push({
|
|
177
|
-
code: 'review_part_stale',
|
|
178
|
-
severity: 'error',
|
|
179
|
-
message: `Part "${entry.id}" changed after AI review; re-run Stage 12e and update review-result.json part_hashes`,
|
|
180
|
-
part: entry.id,
|
|
181
|
-
});
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
catch {
|
|
185
|
-
diagnostics.push({
|
|
186
|
-
code: 'review_part_missing',
|
|
187
|
-
severity: 'error',
|
|
188
|
-
message: `Part "${entry.id}" in review-result.part_hashes not found on disk`,
|
|
189
|
-
part: entry.id,
|
|
190
|
-
});
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
else if (reviewRequired) {
|
|
195
|
-
diagnostics.push({
|
|
196
|
-
code: 'review_part_hashes_missing',
|
|
197
|
-
severity: 'error',
|
|
198
|
-
message: 'review-result.json must include part_hashes[] (sha256 of each part after fixes); see ai-review.md §4',
|
|
199
|
-
});
|
|
200
|
-
}
|
|
201
|
-
const lintResult = await lintApprovalDocument({ changeDir, strict: true });
|
|
202
|
-
diagnostics.push(...lintResult.diagnostics);
|
|
203
|
-
const hasError = diagnostics.some((d) => d.severity === 'error');
|
|
204
|
-
return { ok: !hasError, review, diagnostics };
|
|
205
|
-
}
|
|
@@ -1,223 +0,0 @@
|
|
|
1
|
-
export type ApprovalGenerationMode = 'segmented' | 'monolithic';
|
|
2
|
-
export type ApprovalPartStatus = 'draft' | 'ok' | 'retry';
|
|
3
|
-
export interface ApprovalOptionalChapters {
|
|
4
|
-
readonly s5: boolean;
|
|
5
|
-
readonly s7: boolean;
|
|
6
|
-
readonly s8: boolean;
|
|
7
|
-
}
|
|
8
|
-
export interface ApprovalBatching {
|
|
9
|
-
readonly tables_per_call: number;
|
|
10
|
-
readonly interfaces_per_call: number;
|
|
11
|
-
readonly pages_per_call: number;
|
|
12
|
-
readonly capabilities_per_call: number;
|
|
13
|
-
}
|
|
14
|
-
export type ApprovalRepoRole = 'platform' | 'web' | 'worker' | 'hub' | 'other';
|
|
15
|
-
export type ApprovalDocumentMode = 'unified' | 'per_repo';
|
|
16
|
-
export interface ApprovalMultiRepoEntry {
|
|
17
|
-
readonly id: string;
|
|
18
|
-
readonly label: string;
|
|
19
|
-
readonly role?: ApprovalRepoRole;
|
|
20
|
-
readonly change: string;
|
|
21
|
-
readonly root_hint?: string;
|
|
22
|
-
}
|
|
23
|
-
export interface ApprovalOutputRef {
|
|
24
|
-
readonly repo: string;
|
|
25
|
-
readonly change: string;
|
|
26
|
-
readonly path: string;
|
|
27
|
-
}
|
|
28
|
-
export interface ApprovalMultiRepoBundle {
|
|
29
|
-
readonly enabled?: boolean;
|
|
30
|
-
readonly playbook?: string;
|
|
31
|
-
readonly output?: string;
|
|
32
|
-
readonly readonly?: boolean;
|
|
33
|
-
}
|
|
34
|
-
export interface ApprovalMultiRepo {
|
|
35
|
-
readonly enabled: boolean;
|
|
36
|
-
readonly document_mode?: ApprovalDocumentMode;
|
|
37
|
-
readonly primary_repo?: string;
|
|
38
|
-
readonly repos: readonly ApprovalMultiRepoEntry[];
|
|
39
|
-
readonly outputs?: {
|
|
40
|
-
readonly unified?: ApprovalOutputRef;
|
|
41
|
-
readonly per_repo?: readonly ApprovalOutputRef[];
|
|
42
|
-
};
|
|
43
|
-
readonly bundle?: ApprovalMultiRepoBundle;
|
|
44
|
-
}
|
|
45
|
-
export interface ApprovalFeatureSlice {
|
|
46
|
-
readonly id: string;
|
|
47
|
-
readonly repos?: readonly string[];
|
|
48
|
-
readonly interfaces?: readonly string[];
|
|
49
|
-
readonly pages?: readonly string[];
|
|
50
|
-
readonly design_points?: readonly string[];
|
|
51
|
-
readonly parts: readonly string[];
|
|
52
|
-
}
|
|
53
|
-
export interface ApprovalAcpReview {
|
|
54
|
-
readonly enabled?: boolean;
|
|
55
|
-
readonly required_pass?: boolean;
|
|
56
|
-
}
|
|
57
|
-
export interface ApprovalAcpBlock {
|
|
58
|
-
readonly pipeline?: 'acp/v1' | 'acp/v2';
|
|
59
|
-
readonly context_budget_tokens?: number;
|
|
60
|
-
readonly output_budget_tokens?: number;
|
|
61
|
-
readonly feature_slices?: readonly ApprovalFeatureSlice[];
|
|
62
|
-
readonly review?: ApprovalAcpReview;
|
|
63
|
-
}
|
|
64
|
-
export interface ApprovalTableRef {
|
|
65
|
-
readonly id: string;
|
|
66
|
-
readonly name: string;
|
|
67
|
-
readonly action: string;
|
|
68
|
-
readonly part: string;
|
|
69
|
-
readonly repo?: string;
|
|
70
|
-
}
|
|
71
|
-
export interface ApprovalInterfaceFrozen {
|
|
72
|
-
readonly rpc?: string;
|
|
73
|
-
readonly http_path?: string;
|
|
74
|
-
readonly status_enum?: readonly string[];
|
|
75
|
-
}
|
|
76
|
-
export interface ApprovalInterfaceRef {
|
|
77
|
-
readonly id: string;
|
|
78
|
-
readonly short: string;
|
|
79
|
-
readonly change: string;
|
|
80
|
-
readonly part: string;
|
|
81
|
-
readonly repo?: string;
|
|
82
|
-
readonly layers?: readonly string[];
|
|
83
|
-
readonly pairs_with?: string;
|
|
84
|
-
readonly feature_slice?: string;
|
|
85
|
-
readonly frozen?: ApprovalInterfaceFrozen;
|
|
86
|
-
}
|
|
87
|
-
export interface ApprovalPageRef {
|
|
88
|
-
readonly id: string;
|
|
89
|
-
readonly route: string;
|
|
90
|
-
readonly apis: readonly string[];
|
|
91
|
-
readonly part: string;
|
|
92
|
-
readonly repo?: string;
|
|
93
|
-
}
|
|
94
|
-
export interface ApprovalCapabilityRef {
|
|
95
|
-
readonly id: string;
|
|
96
|
-
readonly part: string;
|
|
97
|
-
}
|
|
98
|
-
export interface ApprovalConventionsResolved {
|
|
99
|
-
readonly architecture?: readonly string[];
|
|
100
|
-
readonly database?: readonly string[];
|
|
101
|
-
readonly api?: readonly string[];
|
|
102
|
-
readonly frontend?: readonly string[];
|
|
103
|
-
}
|
|
104
|
-
export interface ApprovalIndex {
|
|
105
|
-
readonly schema: 'specflow.approval.index/v1';
|
|
106
|
-
readonly change: string;
|
|
107
|
-
readonly generated_at: string;
|
|
108
|
-
readonly mode: ApprovalGenerationMode;
|
|
109
|
-
readonly ui_in_scope: boolean;
|
|
110
|
-
readonly db_in_scope: boolean;
|
|
111
|
-
readonly api_in_scope: boolean;
|
|
112
|
-
readonly optional: ApprovalOptionalChapters;
|
|
113
|
-
readonly parts_order: readonly string[];
|
|
114
|
-
readonly design_points: readonly string[];
|
|
115
|
-
readonly decisions: readonly string[];
|
|
116
|
-
readonly tables: readonly ApprovalTableRef[];
|
|
117
|
-
readonly interfaces: readonly ApprovalInterfaceRef[];
|
|
118
|
-
readonly pages: readonly ApprovalPageRef[];
|
|
119
|
-
readonly capabilities: readonly ApprovalCapabilityRef[];
|
|
120
|
-
readonly batching: ApprovalBatching;
|
|
121
|
-
readonly conventions: ApprovalConventionsResolved;
|
|
122
|
-
readonly multi_repo?: ApprovalMultiRepo;
|
|
123
|
-
readonly acp?: ApprovalAcpBlock;
|
|
124
|
-
readonly meta?: {
|
|
125
|
-
readonly language?: string;
|
|
126
|
-
readonly tech_stack?: string;
|
|
127
|
-
readonly project_mode?: string;
|
|
128
|
-
};
|
|
129
|
-
}
|
|
130
|
-
export interface ApprovalManifestPart {
|
|
131
|
-
readonly id: string;
|
|
132
|
-
readonly path: string;
|
|
133
|
-
readonly status: ApprovalPartStatus;
|
|
134
|
-
readonly sha256?: string;
|
|
135
|
-
readonly updated_at?: string;
|
|
136
|
-
}
|
|
137
|
-
export interface ApprovalManifest {
|
|
138
|
-
readonly schema: 'specflow.approval.manifest/v1';
|
|
139
|
-
readonly change: string;
|
|
140
|
-
readonly parts: readonly ApprovalManifestPart[];
|
|
141
|
-
readonly assembled_at?: string;
|
|
142
|
-
readonly approval_sha256?: string;
|
|
143
|
-
}
|
|
144
|
-
export type ApprovalDiagnosticSeverity = 'error' | 'warning';
|
|
145
|
-
export interface ApprovalDiagnostic {
|
|
146
|
-
readonly code: string;
|
|
147
|
-
readonly severity: ApprovalDiagnosticSeverity;
|
|
148
|
-
readonly message: string;
|
|
149
|
-
readonly part?: string;
|
|
150
|
-
}
|
|
151
|
-
export interface ApprovalReviewFinding {
|
|
152
|
-
readonly id: string;
|
|
153
|
-
readonly severity: 'critical' | 'important' | 'minor';
|
|
154
|
-
readonly category: string;
|
|
155
|
-
readonly message: string;
|
|
156
|
-
readonly part?: string;
|
|
157
|
-
readonly status: 'open' | 'fixed' | 'waived';
|
|
158
|
-
readonly waiver_reason?: string;
|
|
159
|
-
}
|
|
160
|
-
export interface ApprovalReviewResult {
|
|
161
|
-
readonly schema: 'specflow.approval.review/v1';
|
|
162
|
-
readonly change: string;
|
|
163
|
-
readonly reviewed_at: string;
|
|
164
|
-
readonly reviewer: 'ai' | 'human';
|
|
165
|
-
readonly findings: readonly ApprovalReviewFinding[];
|
|
166
|
-
readonly verdict: 'pass' | 'fail' | 'pass_with_waivers';
|
|
167
|
-
readonly summary?: string;
|
|
168
|
-
readonly part_hashes?: readonly {
|
|
169
|
-
readonly id: string;
|
|
170
|
-
readonly sha256: string;
|
|
171
|
-
}[];
|
|
172
|
-
}
|
|
173
|
-
export interface ApprovalReviewPacket {
|
|
174
|
-
readonly schema: 'specflow.approval.review-packet/v1';
|
|
175
|
-
readonly change: string;
|
|
176
|
-
readonly generated_at: string;
|
|
177
|
-
readonly index_summary: {
|
|
178
|
-
readonly interfaces: number;
|
|
179
|
-
readonly pages: number;
|
|
180
|
-
readonly tables: number;
|
|
181
|
-
readonly parts: number;
|
|
182
|
-
};
|
|
183
|
-
readonly lint_diagnostics: readonly ApprovalDiagnostic[];
|
|
184
|
-
readonly parts: readonly {
|
|
185
|
-
readonly id: string;
|
|
186
|
-
readonly sha256: string;
|
|
187
|
-
readonly bytes: number;
|
|
188
|
-
}[];
|
|
189
|
-
readonly checklist: readonly string[];
|
|
190
|
-
readonly instructions: string;
|
|
191
|
-
}
|
|
192
|
-
export interface ApprovalPlaybookSource {
|
|
193
|
-
readonly repo: string;
|
|
194
|
-
readonly change: string;
|
|
195
|
-
readonly include_parts?: 'all' | readonly string[];
|
|
196
|
-
readonly root?: string;
|
|
197
|
-
}
|
|
198
|
-
export interface ApprovalPlaybook {
|
|
199
|
-
readonly schema: 'specflow.approval.playbook/v1';
|
|
200
|
-
readonly change: string;
|
|
201
|
-
readonly title: string;
|
|
202
|
-
readonly primary_repo: string;
|
|
203
|
-
readonly sources: readonly ApprovalPlaybookSource[];
|
|
204
|
-
readonly overlay?: readonly string[];
|
|
205
|
-
readonly assemble: {
|
|
206
|
-
readonly inject_headers?: boolean;
|
|
207
|
-
readonly dedupe_appendix?: boolean;
|
|
208
|
-
readonly output: string;
|
|
209
|
-
readonly readonly?: boolean;
|
|
210
|
-
};
|
|
211
|
-
}
|
|
212
|
-
export interface ApprovalBundleResult {
|
|
213
|
-
readonly ok: boolean;
|
|
214
|
-
readonly outputPath?: string;
|
|
215
|
-
readonly diagnostics: readonly ApprovalDiagnostic[];
|
|
216
|
-
readonly markdown?: string;
|
|
217
|
-
}
|
|
218
|
-
export interface ApprovalAssembleResult {
|
|
219
|
-
readonly markdown?: string;
|
|
220
|
-
readonly outputPath?: string;
|
|
221
|
-
readonly diagnostics: readonly ApprovalDiagnostic[];
|
|
222
|
-
readonly ok: boolean;
|
|
223
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,106 +0,0 @@
|
|
|
1
|
-
# Approval · ACP Pipeline (Approval Compiler Pipeline)
|
|
2
|
-
|
|
3
|
-
> Router for `/specflow:approval` **Stage 12** when `acp.pipeline=acp/v2` (recommended for non-lightweight changes).
|
|
4
|
-
> Legacy segmented flow remains valid; ACP adds **lint**, **AI review**, and **bundle** gates.
|
|
5
|
-
|
|
6
|
-
---
|
|
7
|
-
|
|
8
|
-
## Pipeline (hard order)
|
|
9
|
-
|
|
10
|
-
```text
|
|
11
|
-
Phase 1 Analyze → analysis.json + IR planner (chat gate)
|
|
12
|
-
Phase 2 IR Freeze → index.yaml (+ interfaces[].frozen, feature_slices, acp.review)
|
|
13
|
-
Phase 3 Skeleton → parts 01–03, 04-detail-core, 06/09/10
|
|
14
|
-
Phase 4 Slot Map → batched parts 04.4–04.9 (vertical slice first)
|
|
15
|
-
Phase 5 AI Review → Stage 12e in-session (prompts/approval/ai-review.md) — **inside /specflow-approval**
|
|
16
|
-
Phase 6 finalize → specflow approval finalize <change> [--bundle] [--render]
|
|
17
|
-
Phase 7 render → specflow approval render <change> [--input] [--output]
|
|
18
|
-
|
|
19
|
-
**Preferred**: one `finalize` after Map + AI review. Subcommands (`lint`, `assemble`, …) are for CI/debug only.
|
|
20
|
-
```
|
|
21
|
-
|
|
22
|
-
**Never** skip Phase 6a–6b with LLM paste. **Never** write `approval-unified.md` by hand when bundle is enabled.
|
|
23
|
-
|
|
24
|
-
---
|
|
25
|
-
|
|
26
|
-
## index.yaml ACP block
|
|
27
|
-
|
|
28
|
-
```yaml
|
|
29
|
-
acp:
|
|
30
|
-
pipeline: acp/v2
|
|
31
|
-
context_budget_tokens: 40000
|
|
32
|
-
output_budget_tokens: 8000
|
|
33
|
-
feature_slices:
|
|
34
|
-
- id: step-reporting
|
|
35
|
-
parts: [04.5-api-worker, 04.5-api-east, 04-detail-core]
|
|
36
|
-
interfaces: [I13, I14, I16, I17]
|
|
37
|
-
review:
|
|
38
|
-
enabled: true
|
|
39
|
-
required_pass: true
|
|
40
|
-
```
|
|
41
|
-
|
|
42
|
-
## interfaces[].frozen (IR freeze before Map)
|
|
43
|
-
|
|
44
|
-
```yaml
|
|
45
|
-
interfaces:
|
|
46
|
-
- id: I14
|
|
47
|
-
short: SubmitStepResult
|
|
48
|
-
change: scenario-job-compile
|
|
49
|
-
part: 04.5-api-worker
|
|
50
|
-
pairs_with: I13
|
|
51
|
-
layers: [L3]
|
|
52
|
-
frozen:
|
|
53
|
-
rpc: scheduler.Scheduler.SubmitStepResult
|
|
54
|
-
http_path: POST /internal/v1/tasks/{job_id}/step-results
|
|
55
|
-
status_enum: [passed, failed, skipped, error]
|
|
56
|
-
```
|
|
57
|
-
|
|
58
|
-
---
|
|
59
|
-
|
|
60
|
-
## Multi-repo default
|
|
61
|
-
|
|
62
|
-
- **True source**: `per_repo` — each repo `approval/` + `approval.md`
|
|
63
|
-
- **Read-only view**: `approval bundle` from `approval-playbook.yaml`
|
|
64
|
-
- Do **not** LLM-unify three repos into one primary `approval/parts`
|
|
65
|
-
|
|
66
|
-
See `multi-repo-guidance.md` + `templates/approval-playbook.yaml` (+ example `templates/approval-playbook-talos-scenario-job-compile.yaml`).
|
|
67
|
-
|
|
68
|
-
**Orchestration**: multi-repo mode A uses **Stage 1c** subagent parallel dispatch (`multi-repo-spoke-subagent.md`) — not a single-repo `finalize` across repos.
|
|
69
|
-
|
|
70
|
-
---
|
|
71
|
-
|
|
72
|
-
## Context packet (Map batch)
|
|
73
|
-
|
|
74
|
-
Read only:
|
|
75
|
-
|
|
76
|
-
1. `index.yaml` subset for this batch (ids + frozen fields)
|
|
77
|
-
2. `analysis.json` verdicts (not full Pass essays)
|
|
78
|
-
3. design/spec **snippets** for batch entities
|
|
79
|
-
4. 1–2 anchor files for this batch
|
|
80
|
-
5. guidance router (path only, not full paste)
|
|
81
|
-
|
|
82
|
-
Do **not** reload entire four-artifact sets each batch.
|
|
83
|
-
|
|
84
|
-
---
|
|
85
|
-
|
|
86
|
-
## CLI reference
|
|
87
|
-
|
|
88
|
-
| Command | Purpose |
|
|
89
|
-
|---------|---------|
|
|
90
|
-
| `approval render` | MD → standalone HTML (tables, GFM, mermaid via CDN) |
|
|
91
|
-
| `approval finalize` | **Preferred** — full lint → review-check → check → assemble → bundle |
|
|
92
|
-
| `approval lint` | Cross-part consistency (CI / debug) |
|
|
93
|
-
| `approval review-pack` | Emit `review-packet.json` (debug; finalize auto-runs when needed) |
|
|
94
|
-
| `approval review-check` | Validate `review-result.json` (debug) |
|
|
95
|
-
| `approval check` | Part presence + lazy stub (debug) |
|
|
96
|
-
| `approval assemble` | Deterministic Reduce (debug) |
|
|
97
|
-
| `approval bundle` | Multi-repo readonly merge (debug; finalize can auto-run) |
|
|
98
|
-
|
|
99
|
-
---
|
|
100
|
-
|
|
101
|
-
## Related guidance
|
|
102
|
-
|
|
103
|
-
- `segmented-generation.md` — Map anti-lazy L1–L10
|
|
104
|
-
- `ai-review.md` — Stage 12e AI approval/revision
|
|
105
|
-
- `runtime-guidance.md` — optional async/lease/cancel (when in scope)
|
|
106
|
-
- `api-guidance.md` — optional multi-layer API contracts (when api_in_scope)
|
|
@@ -1,145 +0,0 @@
|
|
|
1
|
-
# Approval · Stage 12e AI Review (审批 / 修订)
|
|
2
|
-
|
|
3
|
-
> Runs **after** Map parts written, **before** `specflow approval finalize` completes.
|
|
4
|
-
> Purpose: catch **long-document / multi-part semantic drift** that mechanical lint cannot judge; **revise parts in place**.
|
|
5
|
-
|
|
6
|
-
**Scope**: checks apply to **any** segmented approval — not tied to a specific product, repo, or domain.
|
|
7
|
-
Use `index.yaml` + `analysis.json` as the contract; do not inject checks from unrelated case studies.
|
|
8
|
-
|
|
9
|
-
---
|
|
10
|
-
|
|
11
|
-
## 0. Trigger
|
|
12
|
-
|
|
13
|
-
Run when **any**:
|
|
14
|
-
|
|
15
|
-
- `acp.review.enabled=true` in `index.yaml` (default for non-lightweight)
|
|
16
|
-
- User asks for AI 审批 / 方案审查 / 一致性检查
|
|
17
|
-
- `specflow approval finalize` paused at `review-required`
|
|
18
|
-
|
|
19
|
-
---
|
|
20
|
-
|
|
21
|
-
## 1. Prep (hard)
|
|
22
|
-
|
|
23
|
-
```bash
|
|
24
|
-
specflow approval finalize <change> # pauses + writes review-packet.json if needed
|
|
25
|
-
```
|
|
26
|
-
|
|
27
|
-
Read:
|
|
28
|
-
|
|
29
|
-
- `approval/review-packet.json` (checklist + lint diagnostics)
|
|
30
|
-
- `approval/index.yaml` (inventory, design_points, frozen fields, parts_order)
|
|
31
|
-
- `approval/analysis.json` (Pass verdicts — gaps to close)
|
|
32
|
-
- **All** `approval/parts/*.md` in `parts_order`
|
|
33
|
-
|
|
34
|
-
Optional domain packs (only when design **actually** touches that topic):
|
|
35
|
-
|
|
36
|
-
- `runtime-guidance.md` — async job / lease / retry / cancel semantics
|
|
37
|
-
- `api-guidance.md` — multi-layer API contracts
|
|
38
|
-
- `frontend-guidance.md` — §4.6 depth
|
|
39
|
-
|
|
40
|
-
---
|
|
41
|
-
|
|
42
|
-
## 2. Review dimensions (long-document generic)
|
|
43
|
-
|
|
44
|
-
These target **failure modes of long, batched documents** — inconsistency across parts written at different times.
|
|
45
|
-
|
|
46
|
-
| ID | Category | Check (generic) |
|
|
47
|
-
|----|----------|-----------------|
|
|
48
|
-
| R1 | **Cross-part naming** | Same entity (service, table, interface, page, enum) uses **one** canonical name/id across §2, §4, diagrams, and `index.yaml`. No 「暂定 / 实现时 / 如 Xxx / TBD」 for listed inventory ids. |
|
|
49
|
-
| R2 | **Decision traceability** | Each `decisions[]` / §2 row appears in §4 with concrete behavior — not only repeated in summary. |
|
|
50
|
-
| R3 | **Inventory ↔ body** | Every `tables[]` / `interfaces[]` / `pages[]` / `design_points[]` entry in index has **substantive** content in its assigned `part`. No orphan ids; no body content for ids missing from index. |
|
|
51
|
-
| R4 | **Depth parity** | Parts in the same `feature_slices` or batch have **comparable** depth. No pattern of one part fully detailed while sibling parts are one-line stubs. |
|
|
52
|
-
| R5 | **Cross-reference integrity** | Internal refs use stable ids (`§4.5 I2`, `P3`, `Page·列表`, `T1`) — not bare 「详见 design/tasks」. Ref targets exist and match. |
|
|
53
|
-
| R6 | **Diagram ↔ prose** | Sequence / flow diagrams agree with adjacent prose (actors, message names, order, error branches). |
|
|
54
|
-
| R7 | **Contract completeness** | Each listed interface/page-api has minimum skeleton from Part E (fields, success example, **failure example**, error table) — depth scaled to `api_in_scope` / `ui_in_scope`. |
|
|
55
|
-
| R8 | **Enum / state closure** | Status, enum, and terminal-state sets are **explicit and closed** wherever the doc defines them — no 「等」 or open-ended lists unless explicitly extensible by design. |
|
|
56
|
-
| R9 | **Unclosed branches** | Every **mentioned** branch (retry, cancel, timeout, idempotency, rollback, concurrency) has a documented outcome — or is flagged `[待 refine 澄清: …]` with owner. Do **not** assume domain-specific branches (e.g. lease reclaim) unless design/analysis mentions them. |
|
|
57
|
-
| R10 | **UI ↔ API alignment** | When `ui_in_scope`: §4.6 field names, types, and api refs align with §4.5 / `pages[].apis`. |
|
|
58
|
-
| R11 | **Multi-repo parity** | When `multi_repo.enabled`: each repo slice in scope has proportionate depth; no 「主仓详、他仓 stub」 unless explicitly out of scope. |
|
|
59
|
-
| R12 | **Stub purge** | No `TODO` / `待补充` / `此处省略` / placeholder-only sections in parts slated for assemble. |
|
|
60
|
-
| R13 | **Verdict coherence** | §9 implementability / readiness matches §4 depth — no READY alongside unexplained NEEDS REFINEMENT or contradictory gates. |
|
|
61
|
-
|
|
62
|
-
**Not in default scope**: product-specific rules (talos lease wipe, ingest whitelist, L2/L3 layering) — load the matching guidance pack **only** when the change design requires it.
|
|
63
|
-
|
|
64
|
-
---
|
|
65
|
-
|
|
66
|
-
## 3. Revision rules (hard)
|
|
67
|
-
|
|
68
|
-
1. **Edit `approval/parts/*.md` only** — never edit assembled `approval.md` directly
|
|
69
|
-
2. **Update `index.yaml` first** when changing frozen ids, rpc names, or inventory
|
|
70
|
-
3. Each finding → fix in named `part` OR waive (`minor` only) with `waiver_reason`
|
|
71
|
-
4. Re-run:
|
|
72
|
-
|
|
73
|
-
```bash
|
|
74
|
-
specflow approval finalize <change>
|
|
75
|
-
```
|
|
76
|
-
|
|
77
|
-
---
|
|
78
|
-
|
|
79
|
-
## 4. Write review-result.json
|
|
80
|
-
|
|
81
|
-
Path: `approval/review-result.json`
|
|
82
|
-
|
|
83
|
-
```json
|
|
84
|
-
{
|
|
85
|
-
"schema": "specflow.approval.review/v1",
|
|
86
|
-
"change": "<change-name>",
|
|
87
|
-
"reviewed_at": "2026-08-26T12:00:00Z",
|
|
88
|
-
"reviewer": "ai",
|
|
89
|
-
"verdict": "pass",
|
|
90
|
-
"summary": "Fixed cross-part naming for I3; expanded P2 error path in 04-detail-core",
|
|
91
|
-
"part_hashes": [
|
|
92
|
-
{ "id": "04.5-api", "sha256": "<sha256-after-fixes>" },
|
|
93
|
-
{ "id": "04-detail-core", "sha256": "<sha256-after-fixes>" }
|
|
94
|
-
],
|
|
95
|
-
"findings": [
|
|
96
|
-
{
|
|
97
|
-
"id": "F1",
|
|
98
|
-
"severity": "critical",
|
|
99
|
-
"category": "cross_part_naming",
|
|
100
|
-
"message": "I3 RPC name differs between §2 and 04.5-api",
|
|
101
|
-
"part": "04.5-api",
|
|
102
|
-
"status": "fixed"
|
|
103
|
-
},
|
|
104
|
-
{
|
|
105
|
-
"id": "F2",
|
|
106
|
-
"severity": "important",
|
|
107
|
-
"category": "unclosed_branch",
|
|
108
|
-
"message": "Retry path mentioned in §4.3 but no terminal outcome",
|
|
109
|
-
"part": "04-detail-core",
|
|
110
|
-
"status": "fixed"
|
|
111
|
-
}
|
|
112
|
-
]
|
|
113
|
-
}
|
|
114
|
-
```
|
|
115
|
-
|
|
116
|
-
| verdict | When |
|
|
117
|
-
|---------|------|
|
|
118
|
-
| `pass` | All findings `fixed`; strict lint pass; **`part_hashes` match current parts** |
|
|
119
|
-
| `pass_with_waivers` | Only `minor` waived with `waiver_reason` |
|
|
120
|
-
| `fail` | Any `critical` / `important` still `open` |
|
|
121
|
-
|
|
122
|
-
**Waive policy**: only `minor` may be waived; critical/important must fix or escalate to user.
|
|
123
|
-
|
|
124
|
-
**part_hashes (required when `acp.review.enabled`)**: After fixing parts, run `specflow approval review-hashes <change> --json` and copy the array into `review-result.json`. `review-check` fails if parts change after review without updating review-result.
|
|
125
|
-
|
|
126
|
-
---
|
|
127
|
-
|
|
128
|
-
## 5. User gate
|
|
129
|
-
|
|
130
|
-
When a finding requires a **product/architecture choice** the artifacts do not resolve:
|
|
131
|
-
|
|
132
|
-
```text
|
|
133
|
-
[待用户确认] <topic>: options A / B / C — record choice as new decision + update affected parts
|
|
134
|
-
```
|
|
135
|
-
|
|
136
|
-
Stop finalize until user chooses; do not invent policy.
|
|
137
|
-
|
|
138
|
-
---
|
|
139
|
-
|
|
140
|
-
## 6. Announce
|
|
141
|
-
|
|
142
|
-
```text
|
|
143
|
-
AI Review: findings=<n> fixed=<n> waived=<n> verdict=pass|fail
|
|
144
|
-
Next: specflow approval finalize <change>
|
|
145
|
-
```
|