@gordon.gan/specflow 1.4.6-beta → 1.7.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 +64 -17
- 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 +37 -0
- package/dist/core/approval/index-schema.d.ts +432 -0
- package/dist/core/approval/index-schema.js +103 -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 +131 -0
- package/dist/integrations/shared/capability-evidence.js +2 -0
- package/dist/integrations/shared/parity-manifest.js +2 -0
- package/package.json +2 -1
- package/prompts/approval/acp-pipeline.md +104 -0
- package/prompts/approval/ai-review.md +145 -0
- package/prompts/approval/api-guidance.md +179 -0
- package/prompts/approval/generate.md +164 -13
- package/prompts/approval/multi-repo-guidance.md +238 -0
- package/prompts/approval/project-conventions-guidance.md +1 -1
- package/prompts/approval/runtime-guidance.md +64 -0
- package/prompts/approval/segmented-generation.md +23 -11
- package/skills/GUIDANCE_PACKS.md +2 -2
- package/skills/specflow-approval/SKILL.md +80 -18
- package/templates/approval-index.yaml +41 -0
- package/templates/approval-part.md +1 -1
- package/templates/approval-playbook.yaml +28 -0
|
@@ -11,23 +11,85 @@ export interface ApprovalBatching {
|
|
|
11
11
|
readonly pages_per_call: number;
|
|
12
12
|
readonly capabilities_per_call: number;
|
|
13
13
|
}
|
|
14
|
+
export type ApprovalRepoRole = 'platform' | 'web' | 'worker' | 'hub' | 'other';
|
|
15
|
+
export type ApprovalDocumentMode = 'unified' | 'per_repo';
|
|
16
|
+
export interface ApprovalMultiRepoEntry {
|
|
17
|
+
readonly id: string;
|
|
18
|
+
readonly label: string;
|
|
19
|
+
readonly role?: ApprovalRepoRole;
|
|
20
|
+
readonly change: string;
|
|
21
|
+
readonly root_hint?: string;
|
|
22
|
+
}
|
|
23
|
+
export interface ApprovalOutputRef {
|
|
24
|
+
readonly repo: string;
|
|
25
|
+
readonly change: string;
|
|
26
|
+
readonly path: string;
|
|
27
|
+
}
|
|
28
|
+
export interface 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
|
+
}
|
|
14
64
|
export interface ApprovalTableRef {
|
|
15
65
|
readonly id: string;
|
|
16
66
|
readonly name: string;
|
|
17
67
|
readonly action: string;
|
|
18
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[];
|
|
19
75
|
}
|
|
20
76
|
export interface ApprovalInterfaceRef {
|
|
21
77
|
readonly id: string;
|
|
22
78
|
readonly short: string;
|
|
23
79
|
readonly change: string;
|
|
24
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;
|
|
25
86
|
}
|
|
26
87
|
export interface ApprovalPageRef {
|
|
27
88
|
readonly id: string;
|
|
28
89
|
readonly route: string;
|
|
29
90
|
readonly apis: readonly string[];
|
|
30
91
|
readonly part: string;
|
|
92
|
+
readonly repo?: string;
|
|
31
93
|
}
|
|
32
94
|
export interface ApprovalCapabilityRef {
|
|
33
95
|
readonly id: string;
|
|
@@ -57,6 +119,8 @@ export interface ApprovalIndex {
|
|
|
57
119
|
readonly capabilities: readonly ApprovalCapabilityRef[];
|
|
58
120
|
readonly batching: ApprovalBatching;
|
|
59
121
|
readonly conventions: ApprovalConventionsResolved;
|
|
122
|
+
readonly multi_repo?: ApprovalMultiRepo;
|
|
123
|
+
readonly acp?: ApprovalAcpBlock;
|
|
60
124
|
readonly meta?: {
|
|
61
125
|
readonly language?: string;
|
|
62
126
|
readonly tech_stack?: string;
|
|
@@ -84,6 +148,73 @@ export interface ApprovalDiagnostic {
|
|
|
84
148
|
readonly message: string;
|
|
85
149
|
readonly part?: string;
|
|
86
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
|
+
}
|
|
87
218
|
export interface ApprovalAssembleResult {
|
|
88
219
|
readonly markdown?: string;
|
|
89
220
|
readonly outputPath?: string;
|
|
@@ -12,6 +12,8 @@ const MARKER_RULES = [
|
|
|
12
12
|
{ id: 'sub.approval.no_phase_change', skill: 'approval', includes: ['does NOT advance phase', 'phase stays `refined`'] },
|
|
13
13
|
{ id: 'sub.approval.code_grounding', skill: 'approval', includes: ['Code Grounding', 'Pass 6'] },
|
|
14
14
|
{ id: 'sub.approval.design_quality', skill: 'approval', includes: ['Design Quality Assessment', 'Over-Engineering'] },
|
|
15
|
+
{ id: 'sub.approval.acp_finalize', skill: 'approval', includes: ['specflow approval finalize', 'Stage 12f'] },
|
|
16
|
+
{ id: 'sub.approval.ai_review', skill: 'approval', includes: ['12e AI Review', 'part_hashes'] },
|
|
15
17
|
{ id: 'sub.apply.phase_a', skill: 'apply', includes: ['## Phase A: Task Rewrite'] },
|
|
16
18
|
{ id: 'sub.apply.phase_b', skill: 'apply', includes: ['## Phase B: Subagent TDD Execution'] },
|
|
17
19
|
{ id: 'sub.apply.gap_detection', skill: 'apply', includes: ['Gap Detection'] },
|
|
@@ -18,6 +18,8 @@ export const CAPABILITY_MANIFEST = [
|
|
|
18
18
|
{ id: 'sub.approval.no_phase_change', commandId: 'approval', level: 'sub-capability', required: true },
|
|
19
19
|
{ id: 'sub.approval.code_grounding', commandId: 'approval', level: 'sub-capability', required: true },
|
|
20
20
|
{ id: 'sub.approval.design_quality', commandId: 'approval', level: 'sub-capability', required: true },
|
|
21
|
+
{ id: 'sub.approval.acp_finalize', commandId: 'approval', level: 'sub-capability', required: true },
|
|
22
|
+
{ id: 'sub.approval.ai_review', commandId: 'approval', level: 'sub-capability', required: true },
|
|
21
23
|
{ id: 'sub.apply.phase_a', commandId: 'apply', level: 'sub-capability', required: true },
|
|
22
24
|
{ id: 'sub.apply.phase_b', commandId: 'apply', level: 'sub-capability', required: true },
|
|
23
25
|
{ id: 'sub.apply.gap_detection', commandId: 'apply', level: 'sub-capability', required: true },
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gordon.gan/specflow",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.7.0-beta",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "SpecFlow — unified spec-driven development: OpenSpec planning + Superpowers execution in one CLI and cross-IDE workflow",
|
|
6
6
|
"keywords": [
|
|
@@ -58,6 +58,7 @@
|
|
|
58
58
|
"chalk": "^5.4.1",
|
|
59
59
|
"commander": "^13.1.0",
|
|
60
60
|
"js-yaml": "^4.1.0",
|
|
61
|
+
"marked": "^15.0.12",
|
|
61
62
|
"zod": "^3.24.4"
|
|
62
63
|
},
|
|
63
64
|
"devDependencies": {
|
|
@@ -0,0 +1,104 @@
|
|
|
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`.
|
|
67
|
+
|
|
68
|
+
---
|
|
69
|
+
|
|
70
|
+
## Context packet (Map batch)
|
|
71
|
+
|
|
72
|
+
Read only:
|
|
73
|
+
|
|
74
|
+
1. `index.yaml` subset for this batch (ids + frozen fields)
|
|
75
|
+
2. `analysis.json` verdicts (not full Pass essays)
|
|
76
|
+
3. design/spec **snippets** for batch entities
|
|
77
|
+
4. 1–2 anchor files for this batch
|
|
78
|
+
5. guidance router (path only, not full paste)
|
|
79
|
+
|
|
80
|
+
Do **not** reload entire four-artifact sets each batch.
|
|
81
|
+
|
|
82
|
+
---
|
|
83
|
+
|
|
84
|
+
## CLI reference
|
|
85
|
+
|
|
86
|
+
| Command | Purpose |
|
|
87
|
+
|---------|---------|
|
|
88
|
+
| `approval render` | MD → standalone HTML (tables, GFM, mermaid via CDN) |
|
|
89
|
+
| `approval finalize` | **Preferred** — full lint → review-check → check → assemble → bundle |
|
|
90
|
+
| `approval lint` | Cross-part consistency (CI / debug) |
|
|
91
|
+
| `approval review-pack` | Emit `review-packet.json` (debug; finalize auto-runs when needed) |
|
|
92
|
+
| `approval review-check` | Validate `review-result.json` (debug) |
|
|
93
|
+
| `approval check` | Part presence + lazy stub (debug) |
|
|
94
|
+
| `approval assemble` | Deterministic Reduce (debug) |
|
|
95
|
+
| `approval bundle` | Multi-repo readonly merge (debug; finalize can auto-run) |
|
|
96
|
+
|
|
97
|
+
---
|
|
98
|
+
|
|
99
|
+
## Related guidance
|
|
100
|
+
|
|
101
|
+
- `segmented-generation.md` — Map anti-lazy L1–L10
|
|
102
|
+
- `ai-review.md` — Stage 12e AI approval/revision
|
|
103
|
+
- `runtime-guidance.md` — optional async/lease/cancel (when in scope)
|
|
104
|
+
- `api-guidance.md` — optional multi-layer API contracts (when api_in_scope)
|
|
@@ -0,0 +1,145 @@
|
|
|
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
|
+
```
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
# Approval · API / RPC / OpenAPI Guidance
|
|
2
|
+
|
|
3
|
+
> Used by `/specflow:approval` before drafting **§4.5 接口设计** (after
|
|
4
|
+
> `project-conventions-guidance.md` `topic=api`).
|
|
5
|
+
>
|
|
6
|
+
> **目的**:产出**可生成**的接口契约(非 narrative stub),并避免 **Worker 面 HTTP** 与 **平台内部 RPC**
|
|
7
|
+
> 混写导致某一侧被省略。
|
|
8
|
+
>
|
|
9
|
+
> **优先级**:项目约定 + 现网 proto/OpenAPI **>** 本路由 **>** LLM。
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## 0. When to run
|
|
14
|
+
|
|
15
|
+
| 条件 | 必须执行 |
|
|
16
|
+
|------|----------|
|
|
17
|
+
| 本迭代有 **新增** HTTP 路径或 gRPC | 全文 §1–§4 |
|
|
18
|
+
| 本迭代 **修改/行为扩展** 对外或 Worker 契约 | §1 分层 + §2 锁名 + §3 变更 delta |
|
|
19
|
+
| 仅 **不变·本迭代消费** 既有 RPC/HTTP | §1 分层 + §4 完整骨架(字段/示例/错误);禁止一句带过 |
|
|
20
|
+
| 三仓/多仓合订 | §1 **每层每面一个 `In`**,禁止用「内部调用」合并条目 |
|
|
21
|
+
|
|
22
|
+
Announce after Read:
|
|
23
|
+
|
|
24
|
+
```text
|
|
25
|
+
API guidance: layering=<n surfaces> | proto=<path|draft in §4.5> | worker_openapi=<path|must draft>
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
## 1. 接口分层 — 禁止混层 (fixes B2)
|
|
31
|
+
|
|
32
|
+
一条业务链路上常有 **多个契约面**,每一面在 §4.5 清单中必须是 **独立 `In`**,各自完整展开:
|
|
33
|
+
|
|
34
|
+
| 层 | 典型调用方 → 被调方 | 协议形态 | 清单命名示例 | 常见错误 |
|
|
35
|
+
|----|---------------------|----------|--------------|----------|
|
|
36
|
+
| L1 控制台/北向 | Web → Gateway | HTTP `/api/v1/...` | I1 调试启动 | 把字段只写在 design,§4.5 无表 |
|
|
37
|
+
| L2 Worker 边车 HTTP | Worker → Gateway | HTTP `/internal/v1/...` + work 鉴权头 | I4 逐步上报 | **只写路径,无 OpenAPI schema** |
|
|
38
|
+
| L3 平台编排 RPC | Gateway/调度 → 本服务 logic | gRPC + `google.api.http` | I4 对应 RPC(与 L2 同编号或 I4a/I4b 成对) | 写「暂定 RPC,HTTP 冻结」—— **禁止** |
|
|
39
|
+
| L4 平台东向 RPC | 调度 → result/testcase/… | gRPC `client/<svc>/` | I6/I7 确保壳/写入逐步 | **用一句「内部调用 I7」代替 I4 的 RPC 详设** |
|
|
40
|
+
|
|
41
|
+
### 1.1 硬规则
|
|
42
|
+
|
|
43
|
+
1. **L2 与 L3 成对**:新增 Worker HTTP 路径时,**必须**同时写清:
|
|
44
|
+
- L2:HTTP method/path、必填头、request/response JSON 字段表、失败示例
|
|
45
|
+
- L3:冻结 RPC 全名、`message` 字段号、`option (google.api.http)`、Gateway 注册点
|
|
46
|
+
2. **L4 不得 stub**:列入清单的东向 RPC(即使 proto 已存在)仍须 **§4.5.2 完整骨架**;
|
|
47
|
+
可标注「与现网 proto 一致」但**必须**贴字段表 + 成功/失败示例 + 错误表。
|
|
48
|
+
3. **禁止**在 L2 小节写满 HTTP,再在 L4 用「经 client/result 调 I7」一行替代 I7 详设。
|
|
49
|
+
4. **调用关系图**须显式画出 L2→L3→L4,例如:
|
|
50
|
+
`Worker --L2 HTTP--> Gateway --L3 RPC--> Scheduler --L4 RPC--> Result`
|
|
51
|
+
|
|
52
|
+
### 1.2 三仓合订
|
|
53
|
+
|
|
54
|
+
| 仓 | 负责的层 | §4.5 须写 |
|
|
55
|
+
|----|----------|-----------|
|
|
56
|
+
| talos | L3/L4 + Gateway 注册 | proto 草案 + 注册文件/函数 |
|
|
57
|
+
| talos-worker | L2 | OpenAPI path + schema + 鉴权头示例 |
|
|
58
|
+
| talos-web | L1 | HTTP 消费 + TS 类型(§4.6 交叉引用 `In`) |
|
|
59
|
+
|
|
60
|
+
合订文档 **禁止**只写 talos 侧 L4 而 Worker OpenAPI 写「apply 时补」。
|
|
61
|
+
|
|
62
|
+
---
|
|
63
|
+
|
|
64
|
+
## 2. RPC / 服务名冻结 (fixes A1)
|
|
65
|
+
|
|
66
|
+
### 2.1 禁止用语 (硬)
|
|
67
|
+
|
|
68
|
+
下列写法 **一律禁止**出现在 §4.5 / §2 决策 / §4.8:
|
|
69
|
+
|
|
70
|
+
- `暂定` / `TBD` / `实现时命名` / `实现时与 OpenAPI 对齐`
|
|
71
|
+
- `如 SubmitXxx` / `例如 scheduler.SubmitXxx`
|
|
72
|
+
- `RPC 名以实现为准`
|
|
73
|
+
|
|
74
|
+
未锁名 → 视为 **BLOCKED**,不得标 READY;须 `[待 refine 澄清: RPC 名]` 或写入锁定名。
|
|
75
|
+
|
|
76
|
+
### 2.2 必须输出
|
|
77
|
+
|
|
78
|
+
| 项 | 格式 |
|
|
79
|
+
|----|------|
|
|
80
|
+
| 服务 | `package` 名,如 `scheduler` |
|
|
81
|
+
| RPC | **冻结**全名,如 `scheduler.SubmitStepResult`(无「如」) |
|
|
82
|
+
| HTTP | method + 完整 path(可含 `{job_id}`) |
|
|
83
|
+
| proto 文件 | 相对路径,如 `proto/scheduler/scheduler.proto` |
|
|
84
|
+
|
|
85
|
+
决策表新增行时使用 **Proposed → Accepted** 仅当 RPC 名已在 §4.5 冻结。
|
|
86
|
+
|
|
87
|
+
---
|
|
88
|
+
|
|
89
|
+
## 3. 可生成契约包 (fixes A2)
|
|
90
|
+
|
|
91
|
+
对 **新增** 或 **修改 HTTP 绑定** 的 RPC,§4.5 **必须**含 **「Proto / OpenAPI 契约包」** 块(可用 `**Proto 草案**` 加粗标签,非标题):
|
|
92
|
+
|
|
93
|
+
### 3.1 Proto 最小集
|
|
94
|
+
|
|
95
|
+
```protobuf
|
|
96
|
+
// 文件: proto/<svc>/<svc>.proto
|
|
97
|
+
rpc SubmitStepResult(SubmitStepResultReq) returns (SubmitStepResultResp) {
|
|
98
|
+
option (google.api.http) = {
|
|
99
|
+
post: "/internal/v1/tasks/{job_id}/step-results"
|
|
100
|
+
body: "*"
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
message SubmitStepResultReq {
|
|
104
|
+
int32 schema_version = 1;
|
|
105
|
+
string request_id = 2;
|
|
106
|
+
// … 每个字段必须有号与类型
|
|
107
|
+
}
|
|
108
|
+
message SubmitStepResultResp {
|
|
109
|
+
int32 schema_version = 1;
|
|
110
|
+
string request_id = 2;
|
|
111
|
+
bool accepted = 3;
|
|
112
|
+
bool replayed = 4;
|
|
113
|
+
}
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
要求:
|
|
117
|
+
|
|
118
|
+
- 每个字段 **字段号 + 类型 + json_name**(若与 snake 不同)
|
|
119
|
+
- `google.api.http` **完整** post/get/path/body
|
|
120
|
+
- enum/status **闭合**,禁止「等」;与现网 ingest 白名单对齐(例:逐步 ingest 仅四终态)
|
|
121
|
+
|
|
122
|
+
### 3.2 Gateway 注册 (硬)
|
|
123
|
+
|
|
124
|
+
必须写明 **其一**:
|
|
125
|
+
|
|
126
|
+
- 「与 `ReportProgress` / `SubmitResult` **同一**注册点:`<文件路径>` 函数 `<Name>`」
|
|
127
|
+
- 或「ProtoSets 清单 `<path>` 新增 `<proto>` 条目」
|
|
128
|
+
|
|
129
|
+
禁止只写「网关 ProtoSet 含新绑定」。
|
|
130
|
+
|
|
131
|
+
### 3.3 Worker OpenAPI 交叉引用
|
|
132
|
+
|
|
133
|
+
- 路径必须在 `talos-worker/contracts/v1/http.openapi.yaml`(或项目约定路径)**给出 schema 草案**
|
|
134
|
+
- 必填头须与现网 progress/result **同构列出**:
|
|
135
|
+
`Idempotency-Key`, `Authorization-date`, `Authorization: TALOS-WORK-1:...`, `Content-Type`, `Accept`
|
|
136
|
+
- HTTP 请求示例 **必须含上述头**,不得只有 JSON body
|
|
137
|
+
|
|
138
|
+
### 3.4 错误映射
|
|
139
|
+
|
|
140
|
+
| 条件 | gRPC `codes.*` | HTTP | 禁止 |
|
|
141
|
+
|------|----------------|------|------|
|
|
142
|
+
| 租约无效 | `FailedPrecondition` 或项目约定 **唯一**值 | **唯一** 403 或 409 | `403/409(与 progress 同类)` |
|
|
143
|
+
|
|
144
|
+
---
|
|
145
|
+
|
|
146
|
+
## 4. §4.5 逐接口最低深度 (含不变)
|
|
147
|
+
|
|
148
|
+
每个 `In` **必须**含 generate.md §4.5.2 全部加粗块;额外:
|
|
149
|
+
|
|
150
|
+
| 变更类型 | 额外要求 |
|
|
151
|
+
|----------|----------|
|
|
152
|
+
| 新增 L2+L3 | §3 契约包 + Gateway 注册 + Worker OpenAPI 指针 |
|
|
153
|
+
| 修改 | 字段号/changelog + 兼容缺省 |
|
|
154
|
+
| 不变·本迭代消费 | 从现网 proto/OpenAPI **摘录**字段表+示例;标注源路径;仍须失败示例 |
|
|
155
|
+
| 不变·协议不变 | 同上;禁止「详见 OpenAPI」无正文 |
|
|
156
|
+
|
|
157
|
+
---
|
|
158
|
+
|
|
159
|
+
## 5. 自检 (生成后必过)
|
|
160
|
+
|
|
161
|
+
- [ ] 清单中每个契约面有独立 `In`,无 L2/L4 混写 stub
|
|
162
|
+
- [ ] 无「暂定/如/实现时」RPC 措辞
|
|
163
|
+
- [ ] 新增 RPC 有 proto 字段号 + http option + 注册点
|
|
164
|
+
- [ ] Worker HTTP 示例含完整 work 鉴权头
|
|
165
|
+
- [ ] status/enum 闭合,与 ingest/现网校验一致
|
|
166
|
+
- [ ] 错误表无「或」「同类映射」模糊语
|
|
167
|
+
- [ ] L4 东向 RPC 非一句话 stub
|
|
168
|
+
|
|
169
|
+
---
|
|
170
|
+
|
|
171
|
+
## 6. 与 database / frontend guidance 对称
|
|
172
|
+
|
|
173
|
+
| 包 | 路由 | 章节 |
|
|
174
|
+
|----|------|------|
|
|
175
|
+
| database | `database-guidance.md` | §4.4 |
|
|
176
|
+
| frontend | `frontend-guidance.md` | §4.6 |
|
|
177
|
+
| **api** | **本文件** | **§4.5** |
|
|
178
|
+
|
|
179
|
+
`project-conventions-guidance.md` `topic=api` 解决「项目禁令与风格」;本文件解决「可生成契约与分层」。
|