newbee-sdd 1.0.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/README.md +167 -0
- package/SKILL.md +128 -0
- package/bin/newbee-sdd.js +120 -0
- package/lib/index.js +12 -0
- package/lib/installer.js +178 -0
- package/lib/updater.js +40 -0
- package/lib/verifier.js +197 -0
- package/package.json +39 -0
- package/references/architecture-guide.md +45 -0
- package/references/checklists.md +75 -0
- package/references/schemes.md +54 -0
- package/references/stages.md +75 -0
- package/scripts/newbee-sdd-check.ps1 +242 -0
- package/templates/CONVENTIONS.md +23 -0
- package/templates/INDEX.md +11 -0
- package/templates/plan.md +74 -0
- package/templates/spec.md +59 -0
- package/templates/tasks.md +28 -0
- package/upgrade.md +46 -0
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
#Requires -Version 5.1
|
|
2
|
+
<#
|
|
3
|
+
.SYNOPSIS
|
|
4
|
+
newbee-sdd 机器检查脚本 v1.0(scheme: newbee-sdd v1)
|
|
5
|
+
.DESCRIPTION
|
|
6
|
+
模式:detect(方案地图)/ resolve(工件根)/ check(单项目覆盖+模糊词)/ verify(多项目对账)/ migrate(scheme 行迁移辅助)。
|
|
7
|
+
编码:UTF-8 读写,CRLF/LF 均容忍。路径不硬编码用户名。
|
|
8
|
+
.NOTES
|
|
9
|
+
机器算数字,模型做语义判断。覆盖率以本脚本输出为准。
|
|
10
|
+
#>
|
|
11
|
+
[CmdletBinding()]
|
|
12
|
+
param(
|
|
13
|
+
[Parameter(Mandatory = $true, Position = 0)]
|
|
14
|
+
[ValidateSet('detect', 'resolve', 'check', 'verify', 'migrate')]
|
|
15
|
+
[string]$Mode,
|
|
16
|
+
|
|
17
|
+
[string[]]$ProjectRoots, # verify 用(多个);其他模式可用 -ProjectRoot
|
|
18
|
+
[string]$ProjectRoot, # detect/resolve/check/migrate 用(单个)
|
|
19
|
+
|
|
20
|
+
[string[]]$VagueWords, # 额外模糊词(来自 CONVENTIONS extra-vague-words)
|
|
21
|
+
[string]$FromScheme = 'v1',
|
|
22
|
+
[string]$ToScheme, # 例:v2
|
|
23
|
+
[switch]$Quiet
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
$ErrorActionPreference = 'Stop'
|
|
27
|
+
$OutputEncoding = [Console]::OutputEncoding = [Text.UTF8Encoding]::new($false)
|
|
28
|
+
|
|
29
|
+
# ---------- 通用 ----------
|
|
30
|
+
$DefaultVagueWords = @('快速', '适当地', '必要时', '尽量', '合理地', 'ASAP', '尽快', '等等', '及时', '高效')
|
|
31
|
+
|
|
32
|
+
function Read-Text([string]$Path) {
|
|
33
|
+
if (-not (Test-Path -LiteralPath $Path)) { return $null }
|
|
34
|
+
return [IO.File]::ReadAllText($Path, [Text.UTF8Encoding]::new($false))
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function Get-Scheme([string]$Path) {
|
|
38
|
+
$t = Read-Text $Path
|
|
39
|
+
if ($null -eq $t) { return $null }
|
|
40
|
+
$m = [regex]::Match($t, 'scheme:\s*newbee-sdd\s*(v[\w\.\-]+)')
|
|
41
|
+
if ($m.Success) { return $m.Groups[1].Value }
|
|
42
|
+
return $null
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function Resolve-SpecsRoot([string]$Root) {
|
|
46
|
+
# 优先链:.newbee-specs-path(绝对路径,fail-loud)→ <root>\newbee-specs\
|
|
47
|
+
$decl = Join-Path $Root '.newbee-specs-path'
|
|
48
|
+
if (Test-Path -LiteralPath $decl) {
|
|
49
|
+
$p = (Read-Text $decl).Trim()
|
|
50
|
+
if (-not $p) { throw "FAIL-LOUD: $decl 存在但为空。按契约报错停下,不回落默认位置。" }
|
|
51
|
+
if (-not [IO.Path]::IsPathRooted($p)) { throw "FAIL-LOUD: $decl 必须是绝对路径,当前值:$p" }
|
|
52
|
+
if (-not (Test-Path -LiteralPath $p -PathType Container)) { throw "FAIL-LOUD: $decl 指向不存在的目录:$p。按契约报错停下,不回落默认位置。" }
|
|
53
|
+
return [pscustomobject]@{ Root = (Resolve-Path -LiteralPath $p).Path; Via = 'override'; Decl = $decl }
|
|
54
|
+
}
|
|
55
|
+
return [pscustomobject]@{ Root = (Join-Path $Root 'newbee-specs'); Via = 'default'; Decl = $null }
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function Get-FeatureDirs([string]$SpecsRoot) {
|
|
59
|
+
if (-not (Test-Path -LiteralPath $SpecsRoot)) { return @() }
|
|
60
|
+
return Get-ChildItem -LiteralPath $SpecsRoot -Directory |
|
|
61
|
+
Where-Object { $_.Name -match '^\d{3}-' }
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
$script:Issues = New-Object System.Collections.Generic.List[string]
|
|
65
|
+
function Add-Issue([string]$Severity, [string]$Message) {
|
|
66
|
+
$script:Issues.Add("[$Severity] $Message")
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
# ---------- detect ----------
|
|
70
|
+
function Invoke-Detect([string]$Root) {
|
|
71
|
+
Write-Output "== 方案地图:$Root"
|
|
72
|
+
$markers = @(
|
|
73
|
+
@{ n = 'newbee(本方案)'; p = 'newbee-specs' },
|
|
74
|
+
@{ n = 'newbee 路径覆盖'; p = '.newbee-specs-path' },
|
|
75
|
+
@{ n = 'gen3(Spec Kit 风)'; p = 'specs' },
|
|
76
|
+
@{ n = 'gen1(Kiro 风)'; p = 'spec' },
|
|
77
|
+
@{ n = 'gen2(opencode Pack)'; p = 'docs\sdd' },
|
|
78
|
+
@{ n = 'constitution(项目宪章)'; p = 'constitution.md' }
|
|
79
|
+
)
|
|
80
|
+
foreach ($m in $markers) {
|
|
81
|
+
$exists = Test-Path -LiteralPath (Join-Path $Root $m.p)
|
|
82
|
+
if ($exists) { Write-Output (" 存在:{0,-20} -> {1}" -f $m.n, $m.p) }
|
|
83
|
+
}
|
|
84
|
+
$agents = Join-Path $Root 'AGENTS.md'
|
|
85
|
+
if (Test-Path -LiteralPath $agents) {
|
|
86
|
+
$t = Read-Text $agents
|
|
87
|
+
if ($t -match '(?i)(sdd|spec[- ]driven)') { Write-Output ' 存在:AGENTS.md 声明 SDD 相关内容(以它为项目权威)' }
|
|
88
|
+
}
|
|
89
|
+
Write-Output ' 原则:一仓一案;非 newbee 正本不碰;点名 newbee 则房客模式(工件只落 newbee 领地)。'
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
# ---------- resolve ----------
|
|
93
|
+
function Invoke-Resolve([string]$Root) {
|
|
94
|
+
$r = Resolve-SpecsRoot $Root
|
|
95
|
+
Write-Output ("工件根:{0}(via {1})" -f $r.Root, $r.Via)
|
|
96
|
+
$dirs = Get-FeatureDirs $r.Root
|
|
97
|
+
Write-Output ("功能夹层:{0} 个" -f $dirs.Count)
|
|
98
|
+
$max = 0
|
|
99
|
+
foreach ($d in $dirs) { if ([int]($d.Name.Substring(0,3)) -gt $max) { $max = [int]($d.Name.Substring(0,3)) } }
|
|
100
|
+
Write-Output ("下一个编号:{0:D3}" -f ($max + 1))
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
# ---------- check ----------
|
|
104
|
+
function Invoke-Check([string]$Root, [string[]]$ExtraVague) {
|
|
105
|
+
$r = Resolve-SpecsRoot $Root
|
|
106
|
+
$specRoot = $r.Root
|
|
107
|
+
if (-not (Test-Path -LiteralPath $specRoot)) { throw "工件根不存在:$specRoot(先建夹层或检查 .newbee-specs-path)" }
|
|
108
|
+
|
|
109
|
+
$req = @{}; $rule = @{}; $tc = @{}; $task = @{}
|
|
110
|
+
$files = Get-ChildItem -LiteralPath $specRoot -Recurse -File -Filter '*.md' |
|
|
111
|
+
Where-Object { $_.Name -ne 'INDEX.md' }
|
|
112
|
+
$vagueHits = New-Object System.Collections.Generic.List[string]
|
|
113
|
+
$reqCoveredByTc = New-Object System.Collections.Generic.List[string]
|
|
114
|
+
$ruleWithReqSource = New-Object System.Collections.Generic.List[string]
|
|
115
|
+
$vague = $DefaultVagueWords + @($ExtraVague | Where-Object { $_ })
|
|
116
|
+
$schemeMissing = New-Object System.Collections.Generic.List[string]
|
|
117
|
+
|
|
118
|
+
foreach ($f in $files) {
|
|
119
|
+
$t = Read-Text $f.FullName
|
|
120
|
+
if ($null -eq $t) { continue }
|
|
121
|
+
if (-not (Get-Scheme $f.FullName)) { $schemeMissing.Add($f.FullName) | Out-Null }
|
|
122
|
+
|
|
123
|
+
foreach ($m in [regex]::Matches($t, '\bREQ-(\d{3})\b')) { $req["REQ-$($m.Groups[1].Value)"] = $f.Name }
|
|
124
|
+
foreach ($m in [regex]::Matches($t, '\bRULE-(\d{3})\b')) { $rule["RULE-$($m.Groups[1].Value)"] = $f.Name }
|
|
125
|
+
foreach ($m in [regex]::Matches($t, '\bTC-(\d{3})\b')) { $tc["TC-$($m.Groups[1].Value)"] = $f.Name }
|
|
126
|
+
foreach ($m in [regex]::Matches($t, '\bTASK-(\d{3})\b')) { $task["TASK-$($m.Groups[1].Value)"] = $f.Name }
|
|
127
|
+
|
|
128
|
+
foreach ($ln in ($t -split "`r?`n")) {
|
|
129
|
+
if ($ln -match '^\s*\|\s*TC-\d{3}') {
|
|
130
|
+
foreach ($m in [regex]::Matches($ln, '\bREQ-\d{3}\b')) { $reqCoveredByTc.Add($m.Value) | Out-Null }
|
|
131
|
+
}
|
|
132
|
+
elseif ($ln -match '^\s*\|\s*RULE-\d{3}') {
|
|
133
|
+
if ([regex]::IsMatch($ln, '\bREQ-\d{3}\b')) {
|
|
134
|
+
$ruleWithReqSource.Add([regex]::Match($ln, '^\s*\|\s*(RULE-\d{3})').Groups[1].Value) | Out-Null
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
foreach ($w in $vague) {
|
|
140
|
+
foreach ($m in [regex]::Matches($t, [regex]::Escape($w))) {
|
|
141
|
+
$line = ($t.Substring(0, $m.Index) -split "`n").Count
|
|
142
|
+
$vagueHits.Add("$($f.Name):$line → '$w'") | Out-Null
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function Get-DefinedIds($map, $defFile) { return @($map.Keys | Where-Object { $map[$_] -eq $defFile }) }
|
|
148
|
+
|
|
149
|
+
$specDefinedReq = Get-DefinedIds $req 'spec.md'
|
|
150
|
+
$specDefinedRule = Get-DefinedIds $rule 'spec.md'
|
|
151
|
+
|
|
152
|
+
$reqNoTc = @($specDefinedReq | Where-Object { -not ($reqCoveredByTc -ccontains $_) })
|
|
153
|
+
$orphanRule = @($specDefinedRule | Where-Object { -not ($ruleWithReqSource -ccontains $_) })
|
|
154
|
+
|
|
155
|
+
$coveredReqPct = if ($specDefinedReq.Count) { [math]::Round(100 * ($specDefinedReq.Count - $reqNoTc.Count) / $specDefinedReq.Count) } else { -1 }
|
|
156
|
+
$orphanRulePct = if ($specDefinedRule.Count) { [math]::Round(100 * ($specDefinedRule.Count - $orphanRule.Count) / $specDefinedRule.Count) } else { -1 }
|
|
157
|
+
|
|
158
|
+
Write-Output "== check:$specRoot"
|
|
159
|
+
Write-Output ("REQ 定义 {0} 条;TC 覆盖率 {1}%(无 TC:{2})" -f $specDefinedReq.Count, $coveredReqPct, ($reqNoTc -join ', '))
|
|
160
|
+
Write-Output ("RULE 定义 {0} 条;有 REQ 引用 {1}%(孤儿:{2})" -f $specDefinedRule.Count, $orphanRulePct, ($orphanRule -join ', '))
|
|
161
|
+
|
|
162
|
+
$seen = @{}
|
|
163
|
+
foreach ($d in (Get-FeatureDirs $specRoot)) {
|
|
164
|
+
$n = $d.Name.Substring(0, 3)
|
|
165
|
+
if ($seen.ContainsKey($n)) { Add-Issue 'FAIL' "编号重复:$($seen[$n]) 与 $($d.Name)" }
|
|
166
|
+
$seen[$n] = $d.Name
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
if ($schemeMissing.Count) { Add-Issue 'WARN' ("scheme 行缺失(手工建档未走模板?):" + ($schemeMissing -join ', ')) }
|
|
170
|
+
if ($vagueHits.Count) { Add-Issue 'FAIL' ("模糊词 {0} 处:" + ($vagueHits -join ';')) }
|
|
171
|
+
if ($reqNoTc.Count) { Add-Issue 'FAIL' ("REQ 无 TC 覆盖:" + ($reqNoTc -join ', ')) }
|
|
172
|
+
if ($orphanRule.Count) { Add-Issue 'FAIL' ("孤儿 RULE(无 REQ 引用):" + ($orphanRule -join ', ')) }
|
|
173
|
+
|
|
174
|
+
$indexPath = Join-Path $specRoot 'INDEX.md'
|
|
175
|
+
if (Test-Path -LiteralPath $indexPath) {
|
|
176
|
+
$it = Read-Text $indexPath
|
|
177
|
+
foreach ($d in (Get-FeatureDirs $specRoot)) {
|
|
178
|
+
$code = $d.Name.Substring(0, 3)
|
|
179
|
+
if ($it -notmatch [regex]::Escape($code)) { Add-Issue 'WARN' "INDEX.md 未登记夹层 $($d.Name)" }
|
|
180
|
+
}
|
|
181
|
+
} else {
|
|
182
|
+
Add-Issue 'WARN' "缺 INDEX.md(建议从 templates/INDEX.md 复制)"
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
if (-not $Quiet) {
|
|
186
|
+
if ($script:Issues.Count) { Write-Output '--- 发现 ---'; $script:Issues | ForEach-Object { Write-Output $_ } }
|
|
187
|
+
else { Write-Output '--- 发现 ---(无)' }
|
|
188
|
+
}
|
|
189
|
+
if ($script:Issues -match '^\[FAIL\]' ) { exit 1 }
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
# ---------- verify ----------
|
|
193
|
+
function Invoke-Verify([string[]]$Roots) {
|
|
194
|
+
$declMap = @{}
|
|
195
|
+
foreach ($root in $Roots) {
|
|
196
|
+
Write-Output "== verify:$root"
|
|
197
|
+
try { $r = Resolve-SpecsRoot $root } catch { Add-Issue 'FAIL' "[$root] $($_.Exception.Message)"; continue }
|
|
198
|
+
if ($r.Via -eq 'override') {
|
|
199
|
+
if ($declMap.ContainsKey($r.Root)) { Add-Issue 'FAIL' ("重复指向:{0} 与 {1} 都指向 {2}" -f $declMap[$r.Root], $root, $r.Root) }
|
|
200
|
+
else { $declMap[$r.Root] = $root }
|
|
201
|
+
}
|
|
202
|
+
$schemeCounts = @{}
|
|
203
|
+
if (Test-Path -LiteralPath $r.Root) {
|
|
204
|
+
foreach ($f in (Get-ChildItem -LiteralPath $r.Root -Recurse -File -Filter '*.md')) {
|
|
205
|
+
$s = Get-Scheme $f.FullName
|
|
206
|
+
if ($s) { if ($schemeCounts.ContainsKey($s)) { $schemeCounts[$s]++ } else { $schemeCounts[$s] = 1 } }
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
$summary = ($schemeCounts.GetEnumerator() | Sort-Object Name | ForEach-Object { "{0}:{1} 档" -f $_.Key, $_.Value }) -join ','
|
|
210
|
+
Write-Output (" 工件根:{0}(via {1})scheme 统计:{2}" -f $r.Root, $r.Via, ($(if ($summary) { $summary } else { '(无工件或无 scheme 行)' })))
|
|
211
|
+
}
|
|
212
|
+
if ($script:Issues.Count) { Write-Output '--- 发现 ---'; $script:Issues | ForEach-Object { Write-Output $_ }; exit 1 }
|
|
213
|
+
Write-Output '--- 发现 ---(无)'
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
# ---------- migrate ----------
|
|
217
|
+
function Invoke-Migrate([string]$Root, [string]$From, [string]$To) {
|
|
218
|
+
if (-not $To) { throw "migrate 需要 -ToScheme(例:v2)" }
|
|
219
|
+
$r = Resolve-SpecsRoot $Root
|
|
220
|
+
$files = Get-ChildItem -LiteralPath $r.Root -Recurse -File -Filter '*.md'
|
|
221
|
+
$changed = 0
|
|
222
|
+
foreach ($f in $files) {
|
|
223
|
+
$t = Read-Text $f.FullName
|
|
224
|
+
if ($null -eq $t) { continue }
|
|
225
|
+
$new = [regex]::Replace($t, "(scheme:\s*newbee-sdd\s*)$([regex]::Escape($From))", "`$1$To")
|
|
226
|
+
if ($new -ne $t) {
|
|
227
|
+
[IO.File]::WriteAllText($f.FullName, $new, [Text.UTF8Encoding]::new($false))
|
|
228
|
+
$changed++
|
|
229
|
+
Write-Output " updated: $($f.FullName)"
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
Write-Output "migrate 完成:$changed 个文件 scheme 行 $From → $To(结构变更请按 upgrade.md 指导另行执行;迁移前请先 git commit)"
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
# ---------- 入口 ----------
|
|
236
|
+
switch ($Mode) {
|
|
237
|
+
'detect' { if (-not $ProjectRoot) { throw "detect 需要 -ProjectRoot" }; Invoke-Detect $ProjectRoot }
|
|
238
|
+
'resolve' { if (-not $ProjectRoot) { throw "resolve 需要 -ProjectRoot" }; Invoke-Resolve $ProjectRoot }
|
|
239
|
+
'check' { if (-not $ProjectRoot) { throw "check 需要 -ProjectRoot" }; Invoke-Check $ProjectRoot $VagueWords }
|
|
240
|
+
'verify' { if (-not $ProjectRoots) { throw "verify 需要 -ProjectRoots(一个以上)" }; Invoke-Verify $ProjectRoots }
|
|
241
|
+
'migrate' { if (-not $ProjectRoot) { throw "migrate 需要 -ProjectRoot" }; Invoke-Migrate $ProjectRoot $FromScheme $ToScheme }
|
|
242
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# newbee-sdd 项目规范(CONVENTIONS)
|
|
2
|
+
|
|
3
|
+
<!--
|
|
4
|
+
放在工件根(newbee-specs/ 或 .newbee-specs-path 覆盖处)。
|
|
5
|
+
本文件是「项目对 newbee 的定制」:可附加/加严,不可删减核心契约(见 skill references/schemes.md)。
|
|
6
|
+
不需要定制可删除本文件,全部走 skill 默认值。
|
|
7
|
+
-->
|
|
8
|
+
|
|
9
|
+
scheme: newbee-sdd v1
|
|
10
|
+
|
|
11
|
+
```newbee-conventions
|
|
12
|
+
language: zh-CN # 工件正文语言
|
|
13
|
+
extra-status: # 加严式新增状态(可选,逗号分隔)
|
|
14
|
+
extra-sections: # spec.md 新增必需章节(可选,逗号分隔)
|
|
15
|
+
extra-vague-words: # 增补模糊词(并入审查扫描,可选,逗号分隔)
|
|
16
|
+
ticket-link: optional # required = RTM 必须挂票号,状态以票系为权威
|
|
17
|
+
gates: # 追加闸门说明(自由文本,只能加严)
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## 说明(自由文本)
|
|
21
|
+
|
|
22
|
+
- 本项目特殊约定:…
|
|
23
|
+
- 引用的项目权威源(如敏捷看板/Issue 追踪系统/CI 规范):…
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# newbee-specs 索引表(INDEX)
|
|
2
|
+
|
|
3
|
+
<!--
|
|
4
|
+
放在工件根(newbee-specs/ 或 .newbee-specs-path 覆盖处)。
|
|
5
|
+
本文件维护所有 feature 夹层的当前状态,与各 spec.md frontmatter 保持对账一致。
|
|
6
|
+
-->
|
|
7
|
+
|
|
8
|
+
scheme: newbee-sdd v1
|
|
9
|
+
|
|
10
|
+
| 编号 | 功能名称 | 轨道 (S/L) | 状态 (draft/approved/implementing/done) | 负责人 | 更新日期 | 备注 |
|
|
11
|
+
|---|---|---|---|---|---|---|
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
---
|
|
2
|
+
scheme: newbee-sdd v1
|
|
3
|
+
feature: "NNN-…(与 spec.md 一致)"
|
|
4
|
+
status: draft
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# NNN 技术方案(plan)
|
|
8
|
+
|
|
9
|
+
> 仅 L 轨需要。基于已冻结(approved)的 spec.md;关键选型与架构设计先与用户确认再定稿。
|
|
10
|
+
|
|
11
|
+
## 1. 约束盘点
|
|
12
|
+
|
|
13
|
+
- 非功能约束:NFR-### …
|
|
14
|
+
- 现有技术栈 / 团队技能 / 运维能力:…
|
|
15
|
+
- 既定决策约束:DEC-### …
|
|
16
|
+
|
|
17
|
+
## 2. 候选与依据(research)
|
|
18
|
+
|
|
19
|
+
| 候选方案 | 依据(官方文档链接,不凭印象) | 已知风险 |
|
|
20
|
+
|---|---|---|
|
|
21
|
+
| 方案 A | … | … |
|
|
22
|
+
| 方案 B | … | … |
|
|
23
|
+
|
|
24
|
+
## 3. 评分矩阵(technology-selection)
|
|
25
|
+
|
|
26
|
+
| 维度(权重) | 方案 A | 方案 B | 方案 C |
|
|
27
|
+
|---|---|---|---|
|
|
28
|
+
| 性能与伸缩性 (30%) | | | |
|
|
29
|
+
| 维护与改造成本 (25%) | | | |
|
|
30
|
+
| 复杂度与风险 (20%) | | | |
|
|
31
|
+
| 团队匹配度 (25%) | | | |
|
|
32
|
+
| **加权总分** | | | |
|
|
33
|
+
|
|
34
|
+
**结论**:最终选定方案 X,满足 NFR-### / DEC-###,并沉淀为 DEC-###。
|
|
35
|
+
|
|
36
|
+
## 4. 架构设计要点(system-design & architecture)
|
|
37
|
+
|
|
38
|
+
### 4.1 架构分层与组件图(C4 Container / Component)
|
|
39
|
+
|
|
40
|
+
```mermaid
|
|
41
|
+
graph TD
|
|
42
|
+
User[用户/上游] --> Gateway[API 网关/接入层]
|
|
43
|
+
Gateway --> MOD1[MOD-001 核心业务模块]
|
|
44
|
+
MOD1 --> DB[(主数据库)]
|
|
45
|
+
MOD1 --> ExtService[外部依赖服务]
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### 4.2 模块职责清单(MOD)
|
|
49
|
+
|
|
50
|
+
| 模块 ID | 模块名称 | 单一职责 | 依赖关系 | 错误处理策略 |
|
|
51
|
+
|---|---|---|---|---|
|
|
52
|
+
| MOD-001 | … | … | MOD-### | … |
|
|
53
|
+
|
|
54
|
+
### 4.3 关键流程时序图
|
|
55
|
+
|
|
56
|
+
```mermaid
|
|
57
|
+
sequenceDiagram
|
|
58
|
+
autonumber
|
|
59
|
+
Client->>Service: 请求
|
|
60
|
+
Service->>DB: 读写
|
|
61
|
+
DB-->>Service: 响应
|
|
62
|
+
Service-->>Client: 返回结果
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### 4.4 失败模式与容灾设计(外部依赖必填)
|
|
66
|
+
|
|
67
|
+
| 外部依赖 | 超时时间 | 重试策略 | 幂等性保证 | 熔断/降级方案 | 补偿机制 |
|
|
68
|
+
|---|---|---|---|---|---|
|
|
69
|
+
| … | … ms | 指数退避 (最多 3 次) | 唯一业务流水号 | 降级默认返回值 / 本地缓存 | 人工对账队列 |
|
|
70
|
+
|
|
71
|
+
## 5. 验证与落地安排
|
|
72
|
+
|
|
73
|
+
- 关键技术假设验证 → PoC/spike 任务:…
|
|
74
|
+
- 阶段交付物与自测方式:…
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
---
|
|
2
|
+
scheme: newbee-sdd v1
|
|
3
|
+
feature: "NNN-kebab-case-英文短名"
|
|
4
|
+
status: draft # draft | approved | implementing | done | blocked | deprecated
|
|
5
|
+
owner: "@用户"
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# NNN 功能名(可用中文)
|
|
9
|
+
|
|
10
|
+
> 使用说明:本模板复制到 `<工件根>\NNN-名\spec.md` 后填写;占位符 `…` 全部替换,不留空段落与 [TODO]。
|
|
11
|
+
|
|
12
|
+
## 1. 背景与动机
|
|
13
|
+
|
|
14
|
+
(一段话:为什么要做,不做会怎样。)
|
|
15
|
+
|
|
16
|
+
## 2. 用户故事
|
|
17
|
+
|
|
18
|
+
- 作为 **角色**,我需要 …,以便 …。
|
|
19
|
+
|
|
20
|
+
## 3. 功能需求(REQ)
|
|
21
|
+
|
|
22
|
+
| ID | 需求 | 优先级(MoSCoW) | 验收条件(Given/When/Then) |
|
|
23
|
+
|---|---|---|---|
|
|
24
|
+
| REQ-001 | … | Must | Given … When … Then … |
|
|
25
|
+
|
|
26
|
+
### 非功能需求(NFR)
|
|
27
|
+
|
|
28
|
+
| ID | 需求 | 目标 | 量测方式 |
|
|
29
|
+
|---|---|---|---|
|
|
30
|
+
| NFR-001 | … | 数值+单位 | 如何量测(日志/压测/监控) |
|
|
31
|
+
|
|
32
|
+
## 4. 业务规则(RULE)
|
|
33
|
+
|
|
34
|
+
| ID | 规则 | 触发条件 → 动作 | 来源 |
|
|
35
|
+
|---|---|---|---|
|
|
36
|
+
| RULE-001 | … | … → … | REQ-### |
|
|
37
|
+
|
|
38
|
+
## 5. 决策记录(DEC)
|
|
39
|
+
|
|
40
|
+
| ID | 决策 | Context | Consequences |
|
|
41
|
+
|---|---|---|---|
|
|
42
|
+
| DEC-001 | … | … | … |
|
|
43
|
+
|
|
44
|
+
## 6. 测试用例(TC)
|
|
45
|
+
|
|
46
|
+
| ID | 前置 | 步骤 | 预期 | 来源 |
|
|
47
|
+
|---|---|---|---|---|
|
|
48
|
+
| TC-001 | … | … | … | REQ-001 / RULE-001 |
|
|
49
|
+
|
|
50
|
+
> S 轨至此闭环;L 轨继续 plan.md 与 tasks.md。
|
|
51
|
+
|
|
52
|
+
## 7. 契约外引用(可选)
|
|
53
|
+
|
|
54
|
+
- 引用的项目权威源(票系/既有文档/架构设计):…
|
|
55
|
+
|
|
56
|
+
## 8. 变更记录(approved 之后的每次修改记一行)
|
|
57
|
+
|
|
58
|
+
| 日期 | 变更 | 原因 | 影响面 |
|
|
59
|
+
|---|---|---|---|
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
---
|
|
2
|
+
scheme: newbee-sdd v1
|
|
3
|
+
feature: "NNN-…(与 spec.md 一致)"
|
|
4
|
+
status: draft
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# NNN 任务拆解(tasks)
|
|
8
|
+
|
|
9
|
+
## Part 1 — 任务清单
|
|
10
|
+
|
|
11
|
+
| TASK ID | 标题 | 来源 | 类型(code/test/infra/doc/spike) | 依赖项 | 验收条件 |
|
|
12
|
+
|---|---|---|---|---|---|
|
|
13
|
+
| TASK-001 | … | REQ-001, MOD-001 | code | — | 可核验的具体验收条件(拒绝“完成开发”空话) |
|
|
14
|
+
|
|
15
|
+
> 粒度原则:单一职责、可独立验收、单人 0.5~2 天内可闭环。
|
|
16
|
+
|
|
17
|
+
## Part 2 — RTM 需求追溯矩阵(Requirement Traceability Matrix)
|
|
18
|
+
|
|
19
|
+
| REQ | RULE | DEC | TC | TASK | Implementation (PR/commit 落实回填) | Release 状态 (pending/in-progress/done/blocked) | External ID (关联外部工单/Issue) |
|
|
20
|
+
|---|---|---|---|---|---|---|---|
|
|
21
|
+
| REQ-001 | RULE-001 | DEC-001 | TC-001 | TASK-001 | (待回填) | pending | (票号) |
|
|
22
|
+
|
|
23
|
+
> 状态以项目实际版本控制与工单系统为权威,RTM 构成全链路可对账视图。
|
|
24
|
+
|
|
25
|
+
## Part 3 — 未追溯项 [untracked] 清单
|
|
26
|
+
|
|
27
|
+
| 候选项目 | 说明 | 裁决动作(补来源 ID / 或显式废弃删除) |
|
|
28
|
+
|---|---|---|
|
package/upgrade.md
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# newbee-sdd 升级与版本迁移指南
|
|
2
|
+
|
|
3
|
+
> 本指南适用于从旧版本或历史 SDD 目录升级至 `newbee-sdd` v1.0.0+。
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 升级理念:规则升级,资产不动
|
|
8
|
+
|
|
9
|
+
`newbee-sdd` 的升级机制严格遵循**「代码与规范升级、业务资产隔离保护」**原则:
|
|
10
|
+
* 执行升级只会更新规范主入口(`SKILL.md`)、审查清单(`references/`)、模板(`templates/`)和校验 CLI 工具。
|
|
11
|
+
* 任何时候**绝不自动修改或覆盖**您项目中已经生成的 `specs/`、`newbee-specs/` 或 `docs/sdd/` 规格文件。
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## 一键升级命令
|
|
16
|
+
|
|
17
|
+
如果您是通过 npm 安装或直接使用 npx:
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
# 自动检测本机已安装的宿主(dsh, opencode, claude, cursor, codex)并一键无痛升级规则
|
|
21
|
+
npx newbee-sdd update
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## 历史项目工件迁移说明
|
|
27
|
+
|
|
28
|
+
如果您手头有在旧版(如原 SDD 10 步流程或自研规格)下生成的存量工件:
|
|
29
|
+
|
|
30
|
+
1. **直接兼容(无需迁移)**:
|
|
31
|
+
`newbee-sdd` 支持向后兼容模式。只需在项目中运行:
|
|
32
|
+
```bash
|
|
33
|
+
npx newbee-sdd verify
|
|
34
|
+
```
|
|
35
|
+
工具会自动识别 `specs/`、`docs/sdd/` 或根目录的 `spec.md` 并执行追溯链闭环校验。
|
|
36
|
+
|
|
37
|
+
2. **正式纳管到 newbee 体系(可选)**:
|
|
38
|
+
在已有工件的 frontmatter 中加入身份声明:
|
|
39
|
+
```markdown
|
|
40
|
+
---
|
|
41
|
+
scheme: newbee-sdd v1
|
|
42
|
+
feature: "001-your-feature-name"
|
|
43
|
+
status: approved
|
|
44
|
+
---
|
|
45
|
+
```
|
|
46
|
+
即可完整享受全链路 ID 机器对账和 Fresh-Context 子代理复审能力。
|