paseo-req-kit 1.7.0-beta.3 → 1.8.0
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/CHANGELOG.md +94 -0
- package/README.md +15 -10
- package/bin/req.mjs +4 -0
- package/config.example.json +32 -1
- package/contracts/feature-files.md +30 -0
- package/contracts/notifier-event.md +3 -2
- package/contracts/result-file.md +8 -4
- package/contracts/spec.md +3 -1
- package/contracts/state.md +7 -2
- package/contracts/verify.md +2 -0
- package/docs/adr/0023-types-gate-slices.md +54 -0
- package/docs/adr/0024-confirm-early-review-axes.md +27 -0
- package/docs/adr/0025-joint-requirements.md +27 -0
- package/docs/adr/README.md +3 -0
- package/docs/guide//344/275/277/347/224/250/350/200/205/346/214/207/345/215/227.md +18 -0
- package/docs/guide//345/256/211/350/243/205/344/270/216/345/215/207/347/272/247.md +16 -7
- package/docs/guide//347/273/264/346/212/244/350/200/205/346/214/207/345/215/227.md +1 -1
- package/docs/guide//351/241/271/347/233/256/345/256/232/345/210/266/344/270/216/350/277/220/350/241/214/350/247/204/345/210/231.md +105 -0
- package/engine/prompts/00-shared-context.md +4 -0
- package/engine/prompts/02-spec-review.md +1 -1
- package/engine/prompts/03-design.md +31 -8
- package/engine/prompts/04-design-review.md +3 -2
- package/engine/prompts/04b-design-revise.md +8 -1
- package/engine/prompts/05-implement.md +11 -5
- package/engine/prompts/06-code-review.md +13 -9
- package/engine/prompts/07-review-fix.md +1 -1
- package/engine/prompts/09-verify.md +1 -1
- package/engine/prompts/10-defect-fix.md +1 -1
- package/engine/prompts/11-report.md +20 -1
- package/engine/prompts/incremental.md +1 -0
- package/engine/prompts/joint.md +55 -0
- package/engine/prompts/related-repos.md +7 -0
- package/engine/prompts/review-axis-spec.md +10 -0
- package/engine/prompts/review-axis-standards.md +11 -0
- package/engine/prompts/types/assessment.md +41 -0
- package/engine/prompts/types/defect.md +55 -0
- package/engine/prompts/types/refactor.md +39 -0
- package/engine/src/cli-args.ts +14 -1
- package/engine/src/config.ts +140 -0
- package/engine/src/control.ts +27 -11
- package/engine/src/digest.ts +12 -3
- package/engine/src/doctor.ts +28 -1
- package/engine/src/eval.ts +1 -1
- package/engine/src/features.ts +2 -0
- package/engine/src/gate.ts +157 -0
- package/engine/src/joint.ts +173 -0
- package/engine/src/launch.ts +1 -1
- package/engine/src/lessons.ts +72 -0
- package/engine/src/main.ts +38 -5
- package/engine/src/messages.ts +87 -1
- package/engine/src/notifiers.ts +1 -1
- package/engine/src/notify-test.ts +4 -0
- package/engine/src/pipeline.ts +777 -63
- package/engine/src/playbooks.ts +46 -0
- package/engine/src/preflight.ts +41 -2
- package/engine/src/state.ts +58 -3
- package/engine/src/steps.ts +29 -5
- package/notifiers/im-template.mjs +3 -1
- package/package.json +2 -2
- package/skills/acceptance-criteria/SKILL.md +10 -1
- package/skills/req/SKILL.md +5 -3
- package/skills/req/playbooks/new.md +16 -5
- package/skills/req/playbooks/small.md +1 -1
- package/skills/req/playbooks/status.md +2 -0
- package/skills/req-retro/SKILL.md +2 -0
- package/skills/req-setup/SKILL.md +22 -5
- package/skills/requirement-interview/SKILL.md +3 -0
- package/skills/spec-writing/SKILL.md +18 -4
- package/skills/spec-writing/spec-template.md +4 -1
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
// Requirement types with their own playbook (spec "- 类型:"). A playbook keeps the
|
|
2
|
+
// pipeline's steps but changes what some of them do, through a prompt file with one
|
|
3
|
+
// section per step, and the flow skips what the type does not need. See ADR 0023.
|
|
4
|
+
//
|
|
5
|
+
// 缺陷 (defect) design becomes "reproduce and locate": a failing reproduction first;
|
|
6
|
+
// cannot reproduce → stop and ask. The fix must turn it green.
|
|
7
|
+
// 重构 (refactor) behavior is locked first (characterization tests), then small steps,
|
|
8
|
+
// each keeping every check green; review checks that behavior did not change.
|
|
9
|
+
// 评估 (assessment) read-only: research → assessment.md → independent review → report. No code.
|
|
10
|
+
|
|
11
|
+
import type { RequirementType } from "./config.js";
|
|
12
|
+
import { type PromptSources, readToolPrompt } from "./prompts.js";
|
|
13
|
+
import { STEPS, type StepId } from "./steps.js";
|
|
14
|
+
|
|
15
|
+
const TITLES: Record<RequirementType, Partial<Record<StepId, string>>> = {
|
|
16
|
+
defect: { design: "复现与定位" },
|
|
17
|
+
refactor: { design: "方案设计与行为锁定" },
|
|
18
|
+
assessment: { design: "调研评估", design_review: "评估审阅", design_revise: "评估修订", report: "评估报告" },
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
/** The step's title for this requirement type. */
|
|
22
|
+
export function stepTitle(type: RequirementType | null | undefined, id: StepId): string {
|
|
23
|
+
return (type && TITLES[type][id]) || STEPS[id].title;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* The playbook's guidance for one step: the "## 所有步骤" section plus the section whose
|
|
28
|
+
* heading names the step id in parentheses, e.g. "## 代码实现(implement)". Null when the
|
|
29
|
+
* playbook has nothing for this step.
|
|
30
|
+
*/
|
|
31
|
+
export function playbookSection(sources: PromptSources, type: RequirementType, id: StepId): string | null {
|
|
32
|
+
return promptSections(sources, `types/${type}.md`, id);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/** The same section picking for any prompt file written that way (e.g. joint.md). */
|
|
36
|
+
export function promptSections(sources: PromptSources, file: string, id: StepId): string | null {
|
|
37
|
+
const text = readToolPrompt(sources, file);
|
|
38
|
+
const [intro, ...sections] = text.split(/^## /m);
|
|
39
|
+
const picked = sections.filter((s) => {
|
|
40
|
+
const heading = s.split("\n", 1)[0];
|
|
41
|
+
return heading.startsWith("所有步骤") || heading.includes(`(${id})`) || heading.includes(`(${id})`);
|
|
42
|
+
});
|
|
43
|
+
// The common part alone is still worth giving: the type changes what every step looks for.
|
|
44
|
+
if (picked.length === 0) return null;
|
|
45
|
+
return [intro.trim(), ...picked.map((s) => `## ${s.trim()}`)].join("\n\n");
|
|
46
|
+
}
|
package/engine/src/preflight.ts
CHANGED
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
PROFILE_LABELS,
|
|
10
10
|
PROFILE_NAMES,
|
|
11
11
|
type ProfileName,
|
|
12
|
+
type RequirementType,
|
|
12
13
|
type VerifyEnvironment,
|
|
13
14
|
} from "./config.js";
|
|
14
15
|
import { gitInfo } from "./git.js";
|
|
@@ -51,6 +52,8 @@ export interface LaunchArgs {
|
|
|
51
52
|
verifyEnv: VerifyEnvironment | null;
|
|
52
53
|
/** --defects: what the verify phase does with defects (overrides phases.verify.defects). */
|
|
53
54
|
defects: DefectHandling | null;
|
|
55
|
+
/** --approve: the requester confirmed the design waiting at the confirmation point. Implies --resume. */
|
|
56
|
+
approve: boolean;
|
|
54
57
|
}
|
|
55
58
|
|
|
56
59
|
export class PreflightError extends Error {
|
|
@@ -137,6 +140,10 @@ export interface PreflightResult {
|
|
|
137
140
|
specProfile: ProfileName | null;
|
|
138
141
|
/** Execution mode named in the spec, if any. */
|
|
139
142
|
specPhases: PhaseMode | null;
|
|
143
|
+
/** Requirement type with its own playbook named in the spec, if any. */
|
|
144
|
+
specType: RequirementType | null;
|
|
145
|
+
/** Related repositories the spec says this requirement also changes. */
|
|
146
|
+
specJoint: string[];
|
|
140
147
|
/** This start begins the verify phase (the develop phase is done). */
|
|
141
148
|
entersVerify: boolean;
|
|
142
149
|
}
|
|
@@ -151,6 +158,27 @@ export function specPhases(specText: string): PhaseMode | null {
|
|
|
151
158
|
return null;
|
|
152
159
|
}
|
|
153
160
|
|
|
161
|
+
const TYPE_WORDS: Array<[RequirementType, RegExp]> = [
|
|
162
|
+
["defect", /^(缺陷|bug|defect|fix|问题修复)/i],
|
|
163
|
+
["refactor", /^(重构|技术改进|refactor)/i],
|
|
164
|
+
["assessment", /^(评估|调研|assessment|spike)/i],
|
|
165
|
+
];
|
|
166
|
+
|
|
167
|
+
/** Reads "- 类型:缺陷" (缺陷 / 重构 / 评估, or the English names); 新需求 and 小改动 give null. */
|
|
168
|
+
export function specType(specText: string): RequirementType | null {
|
|
169
|
+
const match = /^\s*-\s*类型\s*[::]\s*(\S+)/m.exec(specText);
|
|
170
|
+
if (!match) return null;
|
|
171
|
+
return TYPE_WORDS.find(([, re]) => re.test(match[1]))?.[0] ?? null;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/** Reads "- 联合开发:后端、支付" (related repositories this requirement also changes). */
|
|
175
|
+
export function specJoint(specText: string): string[] {
|
|
176
|
+
const match = /^\s*-\s*联合开发\s*[::]\s*(.+)$/m.exec(specText);
|
|
177
|
+
if (!match) return [];
|
|
178
|
+
const names = match[1].split(/[、,,;;\s]+/).map((n) => n.replace(/[((].*$/, "").trim()).filter((n) => n && n !== "无");
|
|
179
|
+
return [...new Set(names)];
|
|
180
|
+
}
|
|
181
|
+
|
|
154
182
|
/** Whether a run has finished its develop phase, so the verify phase can start. */
|
|
155
183
|
export function developDone(state: PipelineState): boolean {
|
|
156
184
|
return state.status === "phase_done" || (state.status === "completed" && state.phases?.mode === "develop-only");
|
|
@@ -222,6 +250,17 @@ export function preflight(
|
|
|
222
250
|
? "这个需求的流水线已经完成。如需重新完整运行,请加 --restart。"
|
|
223
251
|
: `这个需求已有运行记录(状态:${previous.status})。从中断处继续请加 --resume,从头开始请加 --restart。`;
|
|
224
252
|
throw new PreflightError(hint);
|
|
253
|
+
} else if (args.approve && previous.status !== "waiting_for_approval") {
|
|
254
|
+
throw new PreflightError(`这个需求没有等待确认的方案(状态:${previous.status}),不需要 --approve。继续开发请用 --resume。`);
|
|
255
|
+
} else if (previous.status === "waiting_for_approval" && currentSpecHash(specPath) !== previous.specHash) {
|
|
256
|
+
mode = { kind: "resume-from-design", previous, reason: "方案确认时需求方提出了修改,需求说明已更新,从方案设计开始增量处理" };
|
|
257
|
+
} else if (previous.status === "waiting_for_approval") {
|
|
258
|
+
if (!args.approve) {
|
|
259
|
+
throw new PreflightError(
|
|
260
|
+
"方案正在等待需求方确认(见功能目录中的 preview.md)。需求方确认后用 --approve 继续开发;要修改的话,先更新并重新确认需求说明,再用 --resume。",
|
|
261
|
+
);
|
|
262
|
+
}
|
|
263
|
+
mode = { kind: "resume-reuse", previous };
|
|
225
264
|
} else if (args.phase === "verify" && !developDone(previous) && previous.phase !== "verify") {
|
|
226
265
|
throw new PreflightError(
|
|
227
266
|
previous.status === "completed"
|
|
@@ -263,7 +302,7 @@ export function preflight(
|
|
|
263
302
|
throw new PreflightError("这个需求已经在这里运行过,不能再改为在新工作区中开发。请用 --resume 继续,或去掉 --worktree。");
|
|
264
303
|
}
|
|
265
304
|
if (!git.isRepo) throw new PreflightError("--worktree 需要 git 仓库。");
|
|
266
|
-
return { repo, featureDir, specPath, mode, branch: git.branch, specProfile: specProfile(specText), specPhases: specPhases(specText), entersVerify: false };
|
|
305
|
+
return { repo, featureDir, specPath, mode, branch: git.branch, specProfile: specProfile(specText), specPhases: specPhases(specText), specType: specType(specText), specJoint: specJoint(specText), entersVerify: false };
|
|
267
306
|
}
|
|
268
307
|
// The run commits to the branch that is checked out now, and never to the default branch.
|
|
269
308
|
if (git.isRepo && git.detached) {
|
|
@@ -283,7 +322,7 @@ export function preflight(
|
|
|
283
322
|
);
|
|
284
323
|
}
|
|
285
324
|
const entersVerify = mode.kind === "resume-reuse" && developDone(mode.previous);
|
|
286
|
-
return { repo, featureDir, specPath, mode, branch: git.branch, specProfile: specProfile(specText), specPhases: specPhases(specText), entersVerify };
|
|
325
|
+
return { repo, featureDir, specPath, mode, branch: git.branch, specProfile: specProfile(specText), specPhases: specPhases(specText), specType: specType(specText), specJoint: specJoint(specText), entersVerify };
|
|
287
326
|
}
|
|
288
327
|
|
|
289
328
|
/** Why the verify phase cannot start in this environment, or null. */
|
package/engine/src/state.ts
CHANGED
|
@@ -2,7 +2,7 @@ import crypto from "node:crypto";
|
|
|
2
2
|
import fs from "node:fs";
|
|
3
3
|
import os from "node:os";
|
|
4
4
|
import path from "node:path";
|
|
5
|
-
import type { DefectHandling, PhaseMode, ProfileName, VerifyEnvironment } from "./config.js";
|
|
5
|
+
import type { DefectHandling, PhaseMode, ProfileName, RequirementType, VerifyEnvironment } from "./config.js";
|
|
6
6
|
import type { AgentUsage } from "./usage.js";
|
|
7
7
|
import type { StepId } from "./steps.js";
|
|
8
8
|
|
|
@@ -10,8 +10,10 @@ import type { StepId } from "./steps.js";
|
|
|
10
10
|
* "phase_done": the develop phase is done and the run stopped at the gate, waiting for
|
|
11
11
|
* someone to start the verify phase (phases.mode "gated"). Engines before 1.7 treat an
|
|
12
12
|
* unknown status as an interrupted run, which continues into verification: acceptable.
|
|
13
|
+
* "waiting_for_approval" (1.8): the design waits for the requester's confirmation
|
|
14
|
+
* (designApproval); older engines continue as after an interruption, i.e. without it.
|
|
13
15
|
*/
|
|
14
|
-
export type PipelineStatus = "running" | "waiting_for_business" | "failed" | "completed" | "phase_done";
|
|
16
|
+
export type PipelineStatus = "running" | "waiting_for_business" | "failed" | "completed" | "phase_done" | "waiting_for_approval";
|
|
15
17
|
|
|
16
18
|
/** Phase names are strings so later phases need no format change. */
|
|
17
19
|
export type PhaseName = "develop" | "verify";
|
|
@@ -77,7 +79,7 @@ export interface PipelineState {
|
|
|
77
79
|
*/
|
|
78
80
|
worktree?: { workspaceId: string; sourceRepo: string } | null;
|
|
79
81
|
/** Paseo schedules this run created (reminders, automatic continuation, watchdog); cancelled by the next run. */
|
|
80
|
-
schedules?: Array<{ id: string; kind: "reminder" | "auto-resume" | "watchdog" | "verify-start"; at: string | null }>;
|
|
82
|
+
schedules?: Array<{ id: string; kind: "reminder" | "auto-resume" | "watchdog" | "verify-start" | "approval-continue"; at: string | null }>;
|
|
81
83
|
/** Automatic continuations used in this attempt. */
|
|
82
84
|
autoResumes?: { attempt: number; count: number } | null;
|
|
83
85
|
/** Set while waiting for a free slot (maxConcurrentPipelines). */
|
|
@@ -90,6 +92,59 @@ export interface PipelineState {
|
|
|
90
92
|
verifyAt?: string | null;
|
|
91
93
|
/** How many times the verify phase handed defects back to development (defects "handback"). */
|
|
92
94
|
handbacks?: number;
|
|
95
|
+
/** Requirement type with its own playbook, from the spec's "类型" line; absent: the normal flow (1.8). */
|
|
96
|
+
type?: RequirementType | null;
|
|
97
|
+
/** Commit before the attempt's first code change; the quality gate's baseline and the scope check start here (1.8). */
|
|
98
|
+
codeBase?: { attempt: number; commit: string | null } | null;
|
|
99
|
+
/**
|
|
100
|
+
* Joint requirement (1.8): other repositories this requirement also changes, each in a
|
|
101
|
+
* worktree created for it, with its own code base and quality gate. See ADR 0025.
|
|
102
|
+
*/
|
|
103
|
+
joint?: JointRepo[] | null;
|
|
104
|
+
/** Design confirmation point (1.8): which attempt's design is waiting or was confirmed. */
|
|
105
|
+
approval?: { attempt: number; status: "waiting" | "approved"; at: string; auto?: boolean } | null;
|
|
106
|
+
/** Quality gate: the checks' results before the change, and every run after a step (1.8). */
|
|
107
|
+
gate?: { baseline: GateBaseline | null; runs: GateRun[] } | null;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
export interface JointRepo {
|
|
111
|
+
/** Name in relatedRepos, e.g. "后端". */
|
|
112
|
+
name: string;
|
|
113
|
+
/** ASCII key suffix for its steps ("implement@backend"). */
|
|
114
|
+
slug: string;
|
|
115
|
+
/** The checkout the worktree was cut from. */
|
|
116
|
+
sourceRepo: string;
|
|
117
|
+
/** The worktree the steps work in. */
|
|
118
|
+
repo: string;
|
|
119
|
+
workspaceId: string;
|
|
120
|
+
branch: string;
|
|
121
|
+
/** Hash of the spec last copied there. */
|
|
122
|
+
specHash: string | null;
|
|
123
|
+
codeBase?: { attempt: number; commit: string | null } | null;
|
|
124
|
+
gate?: { baseline: GateBaseline | null; runs: GateRun[] } | null;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
export type CheckOutcome = "pass" | "fail";
|
|
128
|
+
|
|
129
|
+
export interface GateBaseline {
|
|
130
|
+
attempt: number;
|
|
131
|
+
commit: string | null;
|
|
132
|
+
at: string;
|
|
133
|
+
results: Record<string, CheckOutcome>;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
export interface GateRun {
|
|
137
|
+
/** Step key the checks ran after, e.g. "implement-s2", "review_fix-r1". */
|
|
138
|
+
key: string;
|
|
139
|
+
attempt: number;
|
|
140
|
+
at: string;
|
|
141
|
+
/** Checks that applied to the change; "skipped" ones did not touch their paths. */
|
|
142
|
+
checks: Array<{ name: string; result: CheckOutcome | "skipped"; failedBefore: boolean }>;
|
|
143
|
+
/** Checks that pass before the change and fail after it: these block. */
|
|
144
|
+
newFailures: string[];
|
|
145
|
+
/** Times the step agent was asked to fix them. */
|
|
146
|
+
fixRounds: number;
|
|
147
|
+
passed: boolean;
|
|
93
148
|
}
|
|
94
149
|
|
|
95
150
|
export interface IncrementalInfo {
|
package/engine/src/steps.ts
CHANGED
|
@@ -46,6 +46,20 @@ export interface StepDef {
|
|
|
46
46
|
|
|
47
47
|
const done: ResultSpec = { required: { status: { kind: "enum", values: ["done", "partial"] } } };
|
|
48
48
|
|
|
49
|
+
/**
|
|
50
|
+
* Optional plan fields of the design result (1.8): the implementation slices, the files the
|
|
51
|
+
* plan expects to change (for the scope check), and for a defect whether it was reproduced.
|
|
52
|
+
*/
|
|
53
|
+
const PLAN_FIELDS: Pick<ResultSpec, "optional"> = {
|
|
54
|
+
optional: {
|
|
55
|
+
slices: { kind: "string[]" },
|
|
56
|
+
files: { kind: "string[]" },
|
|
57
|
+
reproduced: { kind: "boolean" },
|
|
58
|
+
questions: { kind: "string[]" },
|
|
59
|
+
ui_change: { kind: "boolean" },
|
|
60
|
+
},
|
|
61
|
+
};
|
|
62
|
+
|
|
49
63
|
/** Review steps that can run with a review panel. */
|
|
50
64
|
export const PANEL_STEPS = ["design_review", "code_review"] as const;
|
|
51
65
|
export type PanelStep = (typeof PANEL_STEPS)[number];
|
|
@@ -88,7 +102,14 @@ export const STEPS: Record<StepId, StepDef> = {
|
|
|
88
102
|
title: "方案设计",
|
|
89
103
|
role: "builder",
|
|
90
104
|
promptFile: "03-design.md",
|
|
91
|
-
result: {
|
|
105
|
+
result: {
|
|
106
|
+
required: { has_runnable_service: { kind: "boolean" } },
|
|
107
|
+
optional: PLAN_FIELDS.optional,
|
|
108
|
+
check: (value) =>
|
|
109
|
+
value.reproduced === false && (!Array.isArray(value.questions) || value.questions.length === 0)
|
|
110
|
+
? "reproduced 为 false 时,questions 必须是非空的字符串数组:需要需求方补充什么才能复现"
|
|
111
|
+
: null,
|
|
112
|
+
},
|
|
92
113
|
},
|
|
93
114
|
design_review: {
|
|
94
115
|
id: "design_review",
|
|
@@ -111,7 +132,7 @@ export const STEPS: Record<StepId, StepDef> = {
|
|
|
111
132
|
title: "方案修订",
|
|
112
133
|
role: "builder",
|
|
113
134
|
promptFile: "04b-design-revise.md",
|
|
114
|
-
result: done,
|
|
135
|
+
result: { required: done.required, optional: { slices: { kind: "string[]" }, files: { kind: "string[]" }, ui_change: { kind: "boolean" } } },
|
|
115
136
|
},
|
|
116
137
|
implement: {
|
|
117
138
|
id: "implement",
|
|
@@ -119,7 +140,9 @@ export const STEPS: Record<StepId, StepDef> = {
|
|
|
119
140
|
title: "代码实现",
|
|
120
141
|
role: "builder",
|
|
121
142
|
promptFile: "05-implement.md",
|
|
122
|
-
|
|
143
|
+
// acceptance_tests: test files written from the AC examples, committed first (in
|
|
144
|
+
// acceptance_tests_commit); the engine tells the code review when they changed later.
|
|
145
|
+
result: { required: done.required, optional: { acceptance_tests: { kind: "string[]" }, acceptance_tests_commit: { kind: "string" } } },
|
|
123
146
|
},
|
|
124
147
|
code_review: {
|
|
125
148
|
id: "code_review",
|
|
@@ -159,7 +182,8 @@ export const STEPS: Record<StepId, StepDef> = {
|
|
|
159
182
|
title: "功能验证",
|
|
160
183
|
role: "reviewer",
|
|
161
184
|
promptFile: "09-verify.md",
|
|
162
|
-
|
|
185
|
+
// defect_repos (joint requirements): where the defects are, "本仓库" or a joint repository's name.
|
|
186
|
+
result: { required: { result: { kind: "enum", values: ["pass", "defects_found", "blocked"] } }, optional: { defect_repos: { kind: "string[]" } } },
|
|
163
187
|
},
|
|
164
188
|
defect_fix: {
|
|
165
189
|
id: "defect_fix",
|
|
@@ -175,7 +199,7 @@ export const STEPS: Record<StepId, StepDef> = {
|
|
|
175
199
|
title: "复验",
|
|
176
200
|
role: "reviewer",
|
|
177
201
|
promptFile: "10b-reverify.md",
|
|
178
|
-
result: { required: { result: { kind: "enum", values: ["pass", "defects_remaining"] } } },
|
|
202
|
+
result: { required: { result: { kind: "enum", values: ["pass", "defects_remaining"] } }, optional: { defect_repos: { kind: "string[]" } } },
|
|
179
203
|
},
|
|
180
204
|
report: {
|
|
181
205
|
id: "report",
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
// "options": { "webhook": "https://im.example.internal/hook/xxxx" } }
|
|
10
10
|
// ]
|
|
11
11
|
// module 可以是绝对路径、"~/" 开头的路径,或相对配置文件所在目录的路径。
|
|
12
|
-
// events 不写时发送全部事件:milestone、blocked、permission、failed、completed、phase_done、digest。
|
|
12
|
+
// events 不写时发送全部事件:milestone、blocked、permission、failed、completed、phase_done、approval、digest。
|
|
13
13
|
// 3. 在终端运行 `req notify-test`(或 `req notify-test --type blocked`)发一条测试通知,不需要运行流水线。
|
|
14
14
|
//
|
|
15
15
|
// 约定(详见 contracts/notifier-event.md):
|
|
@@ -31,6 +31,7 @@ export default async function notify(event, options) {
|
|
|
31
31
|
completed: "开发完成",
|
|
32
32
|
digest: "需求总览",
|
|
33
33
|
phase_done: "研发完成,可以提测",
|
|
34
|
+
approval: "请确认方案",
|
|
34
35
|
}[event.type] ?? event.type;
|
|
35
36
|
|
|
36
37
|
// 一条通用的纯文本消息,大多数 IM 机器人都能直接使用。
|
|
@@ -38,6 +39,7 @@ export default async function notify(event, options) {
|
|
|
38
39
|
const lines = event.type === "digest" ? [event.text] : [`【req-kit · ${title}】${event.feature.title}`, event.text];
|
|
39
40
|
if (event.type === "completed" && event.details.summary) lines.push("", String(event.details.summary));
|
|
40
41
|
if (event.type === "phase_done") lines.push("", `提测说明:${event.details.handoffPath}`);
|
|
42
|
+
if (event.type === "approval") lines.push("", `要确认的内容:${event.details.previewPath}`);
|
|
41
43
|
if (event.type === "blocked") lines.push("", ...event.details.questions.map((q, i) => `${i + 1}. ${q}`));
|
|
42
44
|
if (event.type !== "digest") lines.push("", `需求目录:${event.feature.dir}${event.branch ? `(分支 ${event.branch})` : ""}`);
|
|
43
45
|
const text = lines.join("\n");
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "paseo-req-kit",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.8.0",
|
|
4
4
|
"description": "req-kit: a requirement-development workflow for Paseo. /req aligns a requirement in chat; the engine runs design, implementation, review and verification as Paseo agents.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
"type": "git",
|
|
9
9
|
"url": "git+https://github.com/yiyisf/paseo-req-kit.git"
|
|
10
10
|
},
|
|
11
|
-
"homepage": "https://
|
|
11
|
+
"homepage": "https://paseo-req-kit.vercel.app/",
|
|
12
12
|
"bugs": "https://github.com/yiyisf/paseo-req-kit/issues",
|
|
13
13
|
"keywords": [
|
|
14
14
|
"paseo",
|
|
@@ -22,6 +22,7 @@ description: req-kit 方法:把功能需求(FR)拆成可以亲手试用、
|
|
|
22
22
|
|
|
23
23
|
## 要求
|
|
24
24
|
|
|
25
|
+
- **用具体的例子。** 每条 AC 至少带一组真实的数据:输入什么、得到什么("输入手机号 13800000000,60 秒内再点发送,提示'请 45 秒后重试'"),而不是"在限制时间内重复发送会被拒绝"。按规则计算的(金额、时间、状态变化),给出两三组覆盖边界的例子,可以写成表格。这些例子会直接成为测试的期望值:需求方确认的,就是测试会检查的东西。
|
|
25
26
|
- **可检查。** 不用"快速""友好""合理""正常"这类词,除非给出具体标准("3 秒内返回""错误提示为'验证码已过期,请重新获取'")。
|
|
26
27
|
- **每条 FR 至少一条 AC。** 并且不只写正常流程:输入不合法、权限不足、重复操作、边界值、依赖不可用,凡是访谈中谈到的都要有对应的 AC。
|
|
27
28
|
- **一条 AC 只检查一件事。** 一条里出现"并且……并且……"时,考虑拆开。
|
|
@@ -43,10 +44,18 @@ description: req-kit 方法:把功能需求(FR)拆成可以亲手试用、
|
|
|
43
44
|
- 操作:输入该验证码登录
|
|
44
45
|
- 预期结果:停留在登录页,提示"验证码已过期,请重新获取",不进入首页
|
|
45
46
|
- **AC-2.3** 连续输错被锁定:同一手机号连续输错 3 次后,第 4 次无论对错都提示"尝试次数过多,请 5 分钟后再试"。
|
|
47
|
+
- **AC-2.4** 重发间隔(按规则计算,用表格给出例子)
|
|
48
|
+
|
|
49
|
+
| 上次发送后过了 | 点"重新发送" | 结果 |
|
|
50
|
+
| --- | --- | --- |
|
|
51
|
+
| 15 秒 | 是 | 提示"请 45 秒后重试",不发送 |
|
|
52
|
+
| 59 秒 | 是 | 提示"请 1 秒后重试",不发送 |
|
|
53
|
+
| 60 秒 | 是 | 发送新的验证码,旧的立即失效 |
|
|
46
54
|
```
|
|
47
55
|
|
|
48
56
|
## 自查
|
|
49
57
|
|
|
50
58
|
- 每条 FR 都有 AC,异常和边界情况都有 AC
|
|
51
|
-
- 每条 AC
|
|
59
|
+
- 每条 AC 的预期结果都是能看到、能比较的东西,并且至少有一组具体的输入和期望输出
|
|
60
|
+
- 按规则计算的 AC,例子覆盖了边界(刚好达到、差一点、超过)
|
|
52
61
|
- 拿着 AC 的人不需要再问任何问题就能执行
|
package/skills/req/SKILL.md
CHANGED
|
@@ -25,7 +25,7 @@ metadata:
|
|
|
25
25
|
|
|
26
26
|
| 用户想做的事 | 典型说法 | 手册 |
|
|
27
27
|
| --- | --- | --- |
|
|
28
|
-
|
|
|
28
|
+
| 提一个新需求,包括修一个缺陷、重构代码、先评估能不能做 | "我想加一个……""这里有个 bug……""整理一下……的代码""帮我评估一下能不能做……" | `{{REQ_KIT_ROOT}}/skills/req/playbooks/new.md` |
|
|
29
29
|
| 一个范围很小的改动 | "把这个按钮的文字改成……""加一个配置项" | `{{REQ_KIT_ROOT}}/skills/req/playbooks/small.md` |
|
|
30
30
|
| 查看进度、暂停、继续 | "做到哪了""先停一下""继续开发" | `{{REQ_KIT_ROOT}}/skills/req/playbooks/status.md` |
|
|
31
31
|
| 研发完成后开始测试验证(提测) | "可以提测了""开始测试""我是测试,来测这个需求" | `{{REQ_KIT_ROOT}}/skills/req/playbooks/verify.md` |
|
|
@@ -74,6 +74,7 @@ metadata:
|
|
|
74
74
|
| 查看进度 | `node "{{PIPELINE_LAUNCHER}}" --feature <功能目录> --status` |
|
|
75
75
|
| 暂停 | `node "{{PIPELINE_LAUNCHER}}" --feature <功能目录> --stop` |
|
|
76
76
|
| 继续 | `node "{{PIPELINE_LAUNCHER}}" --feature <功能目录> --resume` |
|
|
77
|
+
| 确认方案后继续 | `node "{{PIPELINE_LAUNCHER}}" --feature <功能目录> --approve`(只在方案确认点、用户确认之后用) |
|
|
77
78
|
| 从头重来 | `node "{{PIPELINE_LAUNCHER}}" --feature <功能目录> --restart` |
|
|
78
79
|
| 列出需求 | `node "{{PIPELINE_LAUNCHER}}" --list` |
|
|
79
80
|
|
|
@@ -88,7 +89,8 @@ metadata:
|
|
|
88
89
|
1. 按 `{{REQ_KIT_ROOT}}/skills/result-report/SKILL.md` 把要点转告用户,不要原样粘贴,除非用户要求。
|
|
89
90
|
2. 按通知类型处理:
|
|
90
91
|
- **进展**(标题带"进展"):一句话转告,例如"方案已经定了,开始写代码"。
|
|
91
|
-
- **需要需求方决定**:用 `requirement-interview` 的方式(`{{REQ_KIT_ROOT}}/skills/requirement-interview/SKILL.md`)逐个提问,把答案记入 `alignment.md` 并更新 `spec.md`(版本加 1,在 `changes.md` 中记一节,原因写"方案审核提出的问题"),请用户再次确认,然后执行通知中给出的继续命令。
|
|
92
|
+
- **需要需求方决定**:用 `requirement-interview` 的方式(`{{REQ_KIT_ROOT}}/skills/requirement-interview/SKILL.md`)逐个提问,把答案记入 `alignment.md` 并更新 `spec.md`(版本加 1,在 `changes.md` 中记一节,原因写"方案审核提出的问题";缺陷复现不了时写"补充复现信息"),请用户再次确认,然后执行通知中给出的继续命令。
|
|
93
|
+
- **请确认方案**(方案确认点):按通知中的做法,把 `preview.md` 的要点讲给用户(会看到的变化、需要确认的假设;有界面原型的告诉用户在哪里看),问"按这个做,还是有要改的地方"。用户确认后执行通知中的确认命令(`--approve`);要改的,问清楚后更新需求说明(`changes.md` 中原因写"方案确认时的修改"),请用户确认,再执行继续命令(`--resume`)。不要替用户确认。
|
|
92
94
|
- **等待权限批准**:告诉用户打开通知中提到的 agent 处理批准。
|
|
93
95
|
- **流水线中断**:说明发生了什么、日志在哪里。用户能处理的(例如环境没启动)告诉他怎么做;问题解决后执行通知中给出的继续命令。涉及配置或代码的,请开发同事处理。
|
|
94
96
|
- **需求说明审阅完成**:按通知中的做法核对问题、逐个问用户,再请用户确认需求说明。
|
|
@@ -96,4 +98,4 @@ metadata:
|
|
|
96
98
|
- **排队 / 排队结束**:一句话转告。
|
|
97
99
|
- **预约的提醒、自动继续、定时检查、预约的开始时间**(由 Paseo 定时任务发来,开头写着"预约的""定时检查"):严格按消息中的步骤执行命令;消息说"不要打扰用户"的情况就不要回复用户。
|
|
98
100
|
- **研发完成(只做研发)**:转告研发结论和摘要,说明流水线没有做测试验证,需要人工测试;问是否现在按 `accept.md` 开始人工测试(以 `handoff.md` 为线索)。
|
|
99
|
-
- **流水线结束**:转告结论和摘要,读功能目录中的 `report.md`,告诉用户"需要你做的事"和"需要你确认的决定"有几条,并问是否现在开始验收(`accept.md`)。用户追问细节时,以 `report.md`
|
|
101
|
+
- **流水线结束**:转告结论和摘要,读功能目录中的 `report.md`,告诉用户"需要你做的事"和"需要你确认的决定"有几条,并问是否现在开始验收(`accept.md`)。用户追问细节时,以 `report.md` 和功能目录中的其他文件为准回答。评估类需求没有验收:转告结论和推荐的做法,问用户是否按评估结果提一个新需求(按 `new.md`,以 `assessment.md` 最后一节的需求草稿为起点,新需求用新的功能目录)。
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
1. 确认工作区
|
|
6
6
|
2. 阅读项目资料
|
|
7
7
|
3. 听需求并复述
|
|
8
|
-
4.
|
|
8
|
+
4. 判断类型和规模,确定档位和执行方式
|
|
9
9
|
5. 访谈
|
|
10
10
|
6. 写需求说明并自查
|
|
11
11
|
7. 独立审阅
|
|
@@ -37,9 +37,16 @@
|
|
|
37
37
|
|
|
38
38
|
只有用户在第 1 步选了"就在这里开发"、且当前在默认分支(如 `main`、`master`)上时,才新建并切换到分支 `feature/<功能目录名>`。选了"自动新建工作区"的,不建分支、不提交,留在当前分支上即可。不要动工作区里已有的、与本需求无关的改动。流水线不会在默认分支上提交代码。
|
|
39
39
|
|
|
40
|
-
## 4.
|
|
40
|
+
## 4. 判断类型和规模,确定档位和执行方式
|
|
41
41
|
|
|
42
|
-
|
|
42
|
+
先判断**类型**。流水线会按类型走不同的做法,所以要请用户确认,通常在复述时顺带确认即可("这是一个缺陷修复:现在的行为不对,要改回应有的样子,对吗?");拿不准时用一个选择题问:
|
|
43
|
+
|
|
44
|
+
- **新需求**:新功能,或改变现有的行为。按本手册继续。
|
|
45
|
+
- **缺陷**:现在的行为不对,要改回应有的样子。流水线会先复现、找到原因再修,复现不了会回来问。访谈重点问清**复现步骤**(环境、账号或数据、操作、看到的结果、应有的结果)和影响范围。已经上线、正在影响用户的问题,改按 `incident.md` 处理。
|
|
46
|
+
- **重构**:使用者看到的行为不变,只改进代码(整理结构、升级依赖、提高可维护性)。流水线会先用测试锁住现有行为再小步改动。访谈重点问清**改进目标**(怎么检查达成了)和**哪些行为必须保持不变**。
|
|
47
|
+
- **评估**:只想知道能不能做、怎么做、影响多大、大概要多少工作量,先不写代码。流水线只调研、写评估报告,由另一个模型审阅。访谈重点问清**要回答的问题**和评估结论用来做什么决定。评估用标准档、连续执行,下面关于档位和执行方式的问题不用问。
|
|
48
|
+
|
|
49
|
+
再判断**规模**,向用户推荐一个档位,说明理由,请用户确认:
|
|
43
50
|
|
|
44
51
|
- **快速档**:小改动,影响面窄,不涉及数据结构、权限、计费或对外接口。例如改文案或样式、加一个配置项、调整一个字段的显示。之后按 `small.md` 手册继续(从它的"访谈"一步开始)。
|
|
45
52
|
- **标准档**:其他情况,继续本手册。
|
|
@@ -56,11 +63,15 @@
|
|
|
56
63
|
|
|
57
64
|
## 5. 访谈
|
|
58
65
|
|
|
59
|
-
按 `{{REQ_KIT_ROOT}}/skills/requirement-interview/SKILL.md` 进行访谈,边谈边写 `alignment.md`,直到满足其中"标准档"
|
|
66
|
+
按 `{{REQ_KIT_ROOT}}/skills/requirement-interview/SKILL.md` 进行访谈,边谈边写 `alignment.md`,直到满足其中"标准档"的结束条件。缺陷、重构、评估按第 4 步说的重点来问。
|
|
67
|
+
|
|
68
|
+
需求涉及多个模块或另一个仓库(例如只改前端、要用后端已有的接口)时,问清这次改哪一边、参考另一边的什么。项目配置里登记了关联仓库的(`.requirement-pipeline.json` 的 `relatedRepos`),流水线的步骤可以直接读它们;没有登记的,建议请开发同事运行 `/req-setup` 登记。
|
|
69
|
+
|
|
70
|
+
**两边都要改**(例如后端加接口、前端加页面),并且另一边已经登记为关联仓库时,这个需求可以作为**联合需求**一起做:告诉用户,流水线会先设计整体方案和双方的接口约定,再在每个仓库各开一个工作区、同时开发,最后一起验证;每个仓库各有自己的分支,合并由人来做。用户同意后,需求说明头部写 `- 联合开发:<仓库名称>`(最多再加两个仓库),访谈时问清每条验收标准由哪一边负责、哪些要两边一起验证。另一边没有登记,或者用户想分开做时,拆成两个需求,各自在自己的仓库中进行。
|
|
60
71
|
|
|
61
72
|
## 6. 写需求说明并自查
|
|
62
73
|
|
|
63
|
-
按 `{{REQ_KIT_ROOT}}/skills/spec-writing/SKILL.md` 写 `spec.md`(顶部 `-
|
|
74
|
+
按 `{{REQ_KIT_ROOT}}/skills/spec-writing/SKILL.md` 写 `spec.md`(顶部 `- 档位:标准`,`- 类型:` 写第 4 步确认的类型;缺陷、重构、评估按其中"按类型的写法"),验收标准按 `{{REQ_KIT_ROOT}}/skills/acceptance-criteria/SKILL.md` 写,然后按其中的清单自查。
|
|
64
75
|
|
|
65
76
|
## 7. 独立审阅
|
|
66
77
|
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
|
|
36
36
|
## 4. 写简版需求说明
|
|
37
37
|
|
|
38
|
-
按 `{{REQ_KIT_ROOT}}/skills/spec-writing/SKILL.md` 写 `spec.md`,顶部 `- 档位:快速`、`-
|
|
38
|
+
按 `{{REQ_KIT_ROOT}}/skills/spec-writing/SKILL.md` 写 `spec.md`,顶部 `- 档位:快速`、`- 类型:小改动`(修一个明确的小问题时写 `- 类型:缺陷`,并按其中"按类型的写法"写清复现步骤:流水线会先复现再修)。只保留"背景与目标"(两三句)、"范围"、"功能需求与验收标准"、"假设"几节,其余与这次改动无关的章节省略。按其中的清单自查。
|
|
39
39
|
|
|
40
40
|
## 5. 请需求方确认
|
|
41
41
|
|
|
@@ -25,6 +25,8 @@
|
|
|
25
25
|
- 如果上次停在"需要需求方决定",或者 spec 在暂停期间改过,流水线会从方案设计开始,在已有方案和代码的基础上处理变化的部分,提前告诉用户这一点。
|
|
26
26
|
- 输出"未启动"时,按 `/req` 中的说明处理。
|
|
27
27
|
|
|
28
|
+
**等待需求方确认方案**(查看进度显示这个状态时):方案已通过审核,还没有写代码,在等用户看 `preview.md`(有界面原型的在 `prototype/`)。把要点讲给用户,问是否按这个做;用户确认后执行"确认方案后继续"命令,要改的按 `/req` 中"请确认方案"一条处理。用户只说"继续"时,先确认他看过并同意方案,不要直接执行。
|
|
29
|
+
|
|
28
30
|
**研发完成,待测试验证**(分阶段执行,查看进度显示这个状态时):告诉用户下一步是提测,转到 `verify.md` 手册。用户说"继续"时也按 `verify.md` 处理,不要直接执行继续命令。
|
|
29
31
|
|
|
30
32
|
**从头重来**:只有用户明确要求放弃之前的结果时才执行"从头重来"命令,执行前确认一次。
|
|
@@ -34,6 +34,7 @@ metadata:
|
|
|
34
34
|
- `decisions.tsv`:假设、偏离方案、驳回意见、不修复的问题
|
|
35
35
|
- `report.md`、`acceptance.md`:结论、遗留问题、验收不通过的项及原因
|
|
36
36
|
- `changes.md`、`alignment.md`:需求改过几次、为什么;哪些问题本可以在对齐时问清
|
|
37
|
+
- `lessons.tsv`:完成报告挑出的、一条项目规则就能挡住的问题。再看其他需求的 `docs/features/*/lessons.tsv`(`req digest` 的"反复出现的问题"一节也会列出):同一个类别在两个以上的需求里出现过,就是最值得变成规则的
|
|
37
38
|
|
|
38
39
|
需要时可以问用户一两个问题,例如验收不通过的真实原因、上线后出了什么问题。
|
|
39
40
|
|
|
@@ -72,6 +73,7 @@ metadata:
|
|
|
72
73
|
## 6. 落地并记录
|
|
73
74
|
|
|
74
75
|
- 批准的第 2、3 层改动:写入文件,提交到项目主分支(推荐在主目录的对话中运行复盘,这样改进对之后所有需求生效);当前在某个需求的分支上时,提醒用户改进合并之后才对其他需求生效。提交信息例如 `chore(req-kit): 复盘 <功能目录名> 的改进`。
|
|
76
|
+
- 来自 `lessons.tsv` 的类别已经变成规则的(或者决定不做规则的),在 `.requirement-pipeline/lessons-handled.txt` 中写一行这个类别的名字(文件不存在就创建),和其他改动一起提交。需求总览不再提示它。
|
|
75
77
|
- 第 4 层:写入 `~/.req-kit/prompts.local/<文件名>`,在文件开头用注释写明原因和日期。不提交(它不在仓库里)。
|
|
76
78
|
- 第 5 层:把问题报告草稿交给用户。
|
|
77
79
|
- 在功能目录中写 `retro.md`:日期、问题与根因、每条改进的落点和处理结果(已落地 / 否决 / 转为新需求 / 已提交给维护者)。随功能目录一起提交。
|
|
@@ -12,9 +12,10 @@ metadata:
|
|
|
12
12
|
| 文件 | 内容 | 谁读 |
|
|
13
13
|
| --- | --- | --- |
|
|
14
14
|
| `.requirement-pipeline/project-notes.md` | 本项目的约定:包管理工具、目录结构、不能改的地方、提交信息格式、已知的坑 | 流水线每个步骤、`/req` 访谈前 |
|
|
15
|
+
| `GLOSSARY.md` | 术语表(推荐):业务词、含义、代码中的名字。访谈和方案设计会继续补充 | `/req` 访谈、流水线每个步骤 |
|
|
15
16
|
| `.requirement-pipeline/verify.md` | 验证方法:安装依赖、启动服务、运行测试、准备测试数据,命令都实际跑过 | 实现、修复、服务启动、验证、复验步骤 |
|
|
16
17
|
| `paseo.json` | Paseo 的工作区设置(推荐):新建 worktree 时自动安装依赖、复制本地配置;把常驻服务定义为由 Paseo 管理的服务,每个 worktree 分到独立的端口 | Paseo;流水线在服务启动和验证前通过 Paseo 启动这些服务 |
|
|
17
|
-
| `.requirement-pipeline.json` |
|
|
18
|
+
| `.requirement-pipeline.json` | 项目配置:**质量门的检查命令**(`checks`,推荐)、关联仓库(`relatedRepos`,有就写);本项目用的 agent、默认档位、轮数、超时(需要时) | 流水线 |
|
|
18
19
|
|
|
19
20
|
格式见 `{{REQ_KIT_ROOT}}/contracts/verify.md`。这个入口通常由开发同事或熟悉项目的测试同事运行。
|
|
20
21
|
|
|
@@ -55,7 +56,9 @@ metadata:
|
|
|
55
56
|
- 测试账号、测试数据从哪里来(只记录位置,不要把真实口令写进文件)
|
|
56
57
|
- 外部服务(短信、支付、邮件)在本地怎么替代
|
|
57
58
|
- 端到端验证用什么方式:现有工具,还是手工按步骤走
|
|
58
|
-
- 这个项目的需求默认走哪个档位,要不要换 agent
|
|
59
|
+
- 这个项目的需求默认走哪个档位,要不要换 agent(不需要就不写这些项)
|
|
60
|
+
- 有没有相关的其他仓库(例如这是前端,接口在另一个后端仓库):在这台机器上的位置、它提供什么。登记后流水线的步骤可以只读参考它,不会改动它
|
|
61
|
+
- 多模块的仓库(例如 `web/` 和 `server/` 各有自己的测试):确认各模块的检查命令,第 5 步按模块写进 `checks`
|
|
59
62
|
- 项目已经有 `paseo.json` 时,要不要由你补充;没有时,要不要生成(推荐生成,见第 5 步)
|
|
60
63
|
|
|
61
64
|
## 4. 实际运行验证命令
|
|
@@ -70,7 +73,8 @@ metadata:
|
|
|
70
73
|
## 5. 写文件并展示
|
|
71
74
|
|
|
72
75
|
- `verify.md`:按 `{{REQ_KIT_ROOT}}/contracts/verify.md` 的结构,第一行之后写"最后核对:<日期>(/req-setup,<全部命令已运行 / 哪些没能运行>)"。只写实际跑通的命令;跑不通的写在"已知问题"。
|
|
73
|
-
- `project-notes.md`:一条一行,只写对做需求有影响的约定。例如"包管理用 pnpm,不要用 npm""数据库变更必须写成 migrations/ 下的迁移脚本""不要改动 legacy/ 目录""提交信息用 Conventional Commits 格式"
|
|
76
|
+
- `project-notes.md`:一条一行,只写对做需求有影响的约定。例如"包管理用 pnpm,不要用 npm""数据库变更必须写成 migrations/ 下的迁移脚本""不要改动 legacy/ 目录""提交信息用 Conventional Commits 格式"。再加一节**范例文件**:各类代码(接口、页面、数据访问、测试、迁移脚本等)在项目里写得最好的一个文件,例如"接口:`src/api/orders.ts`;单元测试:`test/api/orders.test.ts`"。方案设计会让每个任务照着范例写,代码更像项目原有的样子。只列你读过、确认写得规范的文件。
|
|
77
|
+
- `GLOSSARY.md`(仓库根目录):项目还没有术语表时,从代码、文档和界面文字中整理主要的业务词,每条写含义和代码中的名字(类名、字段、接口路径或表名),格式见 `{{REQ_KIT_ROOT}}/skills/requirement-interview/SKILL.md` 的"术语"一节。已有的只补充"代码中的名字"等缺失的部分,不改别人写的含义。拿不准含义的不要写,留给访谈时问。
|
|
74
78
|
- `paseo.json`(Paseo 读取,格式见 Paseo 文档的 Git worktrees 一节)。它会提交、整个团队共用,而团队成员的电脑可能是 macOS、Linux 或 Windows,Paseo 在各系统上用不同的 shell 执行其中的命令(macOS/Linux:bash 和 sh;Windows:准备命令用 PowerShell、脚本用 cmd.exe)。所以**命令一律写成各系统都一样的形式**:只调用程序本身(`npm`、`pnpm`、`node`、`python` 等),不用 shell 的语法——不写 `$变量`、`%变量%`、`$env:变量`,不用 `&&`、`||`、`cp`、`[ -f … ]`、`Copy-Item`。需要读环境变量或判断文件的逻辑交给下面两个脚本:
|
|
75
79
|
- 把 `{{REQ_KIT_ROOT}}/skills/req-setup/templates/paseo/` 中的 `with-port.mjs` 和 `copy-local-files.mjs` 复制到项目的 `scripts/paseo/`(已有就比较,内容相同不动),和 `paseo.json` 一起提交。它们只依赖 Node.js(req-kit 本来就需要)。
|
|
76
80
|
- `worktree.setup`:写成数组,一行一条命令。通常是安装依赖(用锁文件对应的命令,例如 `npm ci`、`pnpm install --frozen-lockfile`),以及复制不提交的本地配置:`node scripts/paseo/copy-local-files.mjs .env config/local.json`(来源目录里没有的文件会跳过,不会报错)。
|
|
@@ -89,13 +93,26 @@ metadata:
|
|
|
89
93
|
- 团队确定只用一种系统、并且用户明确要求时,才可以用那种系统的 shell 写法;`/req-doctor` 会提示它在其他系统上不能用。
|
|
90
94
|
- 已有 `paseo.json` 时只补充缺少的部分,不改别人写的内容。
|
|
91
95
|
- 在 `verify.md` 的启动服务一节注明:服务已定义在 `paseo.json`,由流水线通过 Paseo 启动;同时保留手工启动的命令,供没有 Paseo 时使用。
|
|
92
|
-
- `.requirement-pipeline.json
|
|
96
|
+
- `.requirement-pipeline.json`:
|
|
97
|
+
- `checks`(推荐):质量门的检查。流水线会在写代码和修复的步骤之后**自己运行**它们,只拦"改动之前通过、现在失败"的检查,并把输出发回给那一步的 agent 修复,不依赖 agent 自己说"测试通过了"。把第 4 步实际跑通、几分钟内能跑完的检查写进去:格式、lint、类型检查、单元测试、构建(端到端测试和需要启动服务的检查不要写,它们由功能验证负责)。命令同样写成各系统通用的形式。多模块的仓库按模块分开写,用 `cwd` 指定模块目录(这项检查只在该模块有改动时运行;需要更精确时用 `paths` 列出它关心的目录):
|
|
98
|
+
|
|
99
|
+
```json
|
|
100
|
+
{
|
|
101
|
+
"checks": [
|
|
102
|
+
{ "name": "lint", "command": "npm run lint" },
|
|
103
|
+
{ "name": "web 测试", "command": "npm test", "cwd": "web" },
|
|
104
|
+
{ "name": "server 测试", "command": "npm test", "cwd": "server", "timeoutMinutes": 15 }
|
|
105
|
+
]
|
|
106
|
+
}
|
|
107
|
+
```
|
|
108
|
+
- `relatedRepos`(有关联仓库时):`[{ "name": "后端", "path": "../backend", "role": "提供登录和订单接口" }]`。`path` 写相对项目目录的路径(团队约定把相关仓库放在同一个上级目录最方便),也可以写 `~/` 开头的路径。平时流水线的步骤只读不改;需求说明写了"联合开发"时,流水线会为这个需求在它那里新建工作区,一起开发。关联仓库最好也运行过 `/req-setup`,它自己的检查命令、验证方法和项目说明会用在它的步骤里。
|
|
109
|
+
- 其他设置只在需要和本机配置不同时才写,例如 `"defaultProfile": "quick"`。不要写 `daemonUrl`、`daemonPasswordEnv`、`teamConfig`,写了也会被忽略。
|
|
93
110
|
- `.gitignore`:确认包含 `docs/features/*/.pipeline/`(流水线的运行状态),没有就建议加上。
|
|
94
111
|
|
|
95
112
|
把这些文件的内容(更新时是差异)展示给用户,并说明运行验证的结果:哪些通过、哪些没能运行以及原因。
|
|
96
113
|
|
|
97
114
|
## 6. 确认后提交
|
|
98
115
|
|
|
99
|
-
用户确认后,只提交这几个文件(以及 `scripts/paseo/` 和 `.gitignore` 的改动)。`paseo.json` 必须提交:Paseo 新建 worktree 时读取的是基准分支上已提交的版本,提交信息例如 `chore(req-kit): 项目接入说明与验证方法`。不 push。
|
|
116
|
+
用户确认后,只提交这几个文件(以及 `GLOSSARY.md`、`scripts/paseo/` 和 `.gitignore` 的改动)。`paseo.json` 必须提交:Paseo 新建 worktree 时读取的是基准分支上已提交的版本,提交信息例如 `chore(req-kit): 项目接入说明与验证方法`。不 push。
|
|
100
117
|
|
|
101
118
|
最后告诉用户:之后项目的构建、测试或启动方式有变化时,再运行一次 `/req-setup`;也可以直接编辑这些文件。可以运行 `/req-doctor` 确认项目检查全部正常。
|
|
@@ -68,9 +68,12 @@ description: req-kit 方法:通过一次一问、带选项和推荐的访谈
|
|
|
68
68
|
```markdown
|
|
69
69
|
## <术语>
|
|
70
70
|
<在本项目中的确切含义。一两句话。>
|
|
71
|
+
- 代码中的名字:`<类名、字段名、接口路径或表名>`(从代码中查到的;还没有的不写,方案设计时会补上)
|
|
71
72
|
- 不要与"<容易混淆的词>"混用:<区别>
|
|
72
73
|
```
|
|
73
74
|
|
|
75
|
+
"代码中的名字"让需求方的说法和代码里的叫法对上:流水线的步骤会用它命名,评审会检查是否一致。
|
|
76
|
+
|
|
74
77
|
项目里已有 `CONTEXT.md`(旧名)时,先读它;新增的术语仍写入 `GLOSSARY.md`,两者是否合并由开发同事决定。
|
|
75
78
|
|
|
76
79
|
**重要决定。** 影响以后很多需求、并且不容易推翻的决定(例如"订单号对外展示时一律脱敏"),在 `docs/adr/` 下新建一条记录:`<四位编号>-<英文短名>.md`,写明背景、决定、理由。不确定是否算重要时,不写。
|
|
@@ -10,11 +10,13 @@ spec 是开发流水线判断"要做什么"的唯一依据。流水线的每一
|
|
|
10
10
|
## 写
|
|
11
11
|
|
|
12
12
|
1. 按模板 `{{REQ_KIT_ROOT}}/skills/spec-writing/spec-template.md` 的结构,把 `alignment.md` 的内容整理成 `<功能目录>/spec.md`。
|
|
13
|
-
2.
|
|
13
|
+
2. 填好顶部的几行。流水线读其中四行,写法要保持原样:
|
|
14
14
|
- `- 状态:草稿`(确认后改为 `- 状态:已确认(<日期>)`)
|
|
15
15
|
- `- 档位:标准` 或 `- 档位:快速`
|
|
16
|
-
- `-
|
|
17
|
-
|
|
16
|
+
- `- 类型:新需求` / `小改动` / `缺陷` / `重构` / `评估`:缺陷、重构、评估各有自己的开发做法(见下文"按类型的写法")
|
|
17
|
+
- `- 执行方式:分阶段` 或 `- 执行方式:只做研发`:只在用户选了这两种时写;连续(默认)不写这一行。评估不写
|
|
18
|
+
- `- 联合开发:后端`:这个需求还要改其他仓库时才写,写项目配置 `relatedRepos` 中登记的名称,最多两个,用顿号分开
|
|
19
|
+
其余几行(版本、格式)给人和 `/req` 看。新需求的版本是 1,每次修改需求加 1。
|
|
18
20
|
3. 快速档可以省略与这次改动无关的章节,但"范围""功能需求与验收标准""假设"必须有。
|
|
19
21
|
4. 写作要求:
|
|
20
22
|
- 只写"要什么"和"怎么算做对",不写"怎么实现"。技术上的想法只进入"技术假设"一节。
|
|
@@ -22,11 +24,23 @@ spec 是开发流水线判断"要做什么"的唯一依据。流水线的每一
|
|
|
22
24
|
- 没谈到的内容不要自己补。需要补的,回去问,或写进"假设"并说明依据。
|
|
23
25
|
- 术语与仓库的 `GLOSSARY.md` 一致。
|
|
24
26
|
|
|
27
|
+
## 按类型的写法
|
|
28
|
+
|
|
29
|
+
模板按新需求写成。另外三种类型的重点不同,流水线也会按类型走不同的做法:
|
|
30
|
+
|
|
31
|
+
- **缺陷**:背景写现象、影响范围、从什么时候开始。必须有**复现步骤**:环境、账号或数据、操作、看到的结果、应有的结果。验收标准至少一条"按复现步骤操作,应该看到……"(修复前不通过、修复后通过),再加上相邻的、不应受影响的行为。流水线会先复现再修,复现不了会回来问,所以复现步骤越具体越好。
|
|
32
|
+
- **重构**:目标写改进什么,并且可以检查(例如"订单模块不再引用旧的支付库")。第一条验收标准是**行为不变**:列出必须保持不变的主要行为(接口、页面、数据、导出的文件),然后是改进目标的验收标准。"本次不做"写明不顺带修改的行为。
|
|
33
|
+
- **评估**:功能需求写成**要回答的问题**(例如"FR-1 现有的订单接口能否支持按门店筛选"),每个问题的验收标准写清楚回答里要有什么(可选方案的对比、影响的模块和功能、工作量和风险、推荐和理由)。范围写明只做评估、不改代码,以及评估的结论用来做什么决定。可以省略"主流程""权限与数据"等与评估无关的章节。
|
|
34
|
+
|
|
35
|
+
项目有多个模块或多个仓库(例如前端和后端)时,在"范围"中写明这次涉及哪些模块;只改一边、需要参考另一边的,写明参考哪个仓库的什么(例如"后端已有的登录接口")。
|
|
36
|
+
|
|
37
|
+
**联合开发**(几个仓库都要改):头部写 `- 联合开发:<仓库名称>`;"范围"按仓库写各自要做什么;每条验收标准注明由哪个仓库负责,或者是"端到端"(几个仓库一起才能验证);双方要约定的接口(字段、错误码)在需求层面能说清的写进来,细节由方案设计写成接口约定。
|
|
38
|
+
|
|
25
39
|
## 自查
|
|
26
40
|
|
|
27
41
|
写完后逐项检查,发现问题就改:
|
|
28
42
|
|
|
29
|
-
- 每条 FR 至少有一条可以实际检查的 AC
|
|
43
|
+
- 每条 FR 至少有一条可以实际检查的 AC,每条 AC 至少有一组具体的输入和期望输出
|
|
30
44
|
- 异常和边界情况都有对应的 AC,而不是只有正常流程
|
|
31
45
|
- 没有"快速""友好""合理"这类无法检查的词,除非给出了具体标准
|
|
32
46
|
- 各条需求之间没有互相矛盾
|