sillyspec 3.33.2 → 3.33.3
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/AGENTS.md +1 -1
- package/package.json +1 -1
- package/src/design-facts.js +1 -1
- package/src/flow-review-pack.js +35 -3
- package/src/flow-review.js +31 -8
- package/src/flow.js +6 -2
- package/src/run/gates.js +1 -1
- package/src/stages/brainstorm.js +1 -0
- package/src/ui-visual.js +3 -0
- package/templates/agents-instruction.md +1 -1
package/AGENTS.md
CHANGED
|
@@ -33,7 +33,7 @@ sillyspec flow start --change <YYYY-MM-DD-名> --input "<一句 10-30 字中文
|
|
|
33
33
|
1. **禁止绕过本文件规则和 SillySpec 流程**。所有变更走 sillyspec 流程,不裸改裸提交。
|
|
34
34
|
2. **改代码前必须先说明依据**——依据的文档路径(design.md / 模块文档 / file-lifecycle.md(如有))或现有代码依据,无依据不改。
|
|
35
35
|
3. **执行顺序**:文档 → 读代码 → 写测试 → 写实现 → 跑测试 → 验收 → 更新文档(轻量道即:FR 绑定+design 四节+用户批准先于动码,读代码探究不算动码)。测试按层适用:纯逻辑先写测试,行为契约断言用户可见行为,视觉样式与纯配置/文档不写测试(记「不适用:理由」,验收靠规范与人工)。
|
|
36
|
-
4. **实证核验再收口**:触及 `src`/`test` 的改动,CLI 会在收口时亲自实测(实测失败阻断收口;纯 doc/配置自动跳过)。测试面按变更动态推断(本变更测试 ∪ FR 关联回归 ∪ import 依赖,runner 自项目结构推断——无需配置);lint 走 `.sillyspec/local.yaml` 的 `commands.lint`。以落盘文件与测试结果为准,不信口头"已完成"
|
|
36
|
+
4. **实证核验再收口**:触及 `src`/`test` 的改动,CLI 会在收口时亲自实测(实测失败阻断收口;纯 doc/配置自动跳过)。测试面按变更动态推断(本变更测试 ∪ FR 关联回归 ∪ import 依赖,runner 自项目结构推断——无需配置);lint 走 `.sillyspec/local.yaml` 的 `commands.lint`。以落盘文件与测试结果为准,不信口头"已完成"。自验口径=变更测试面(新增测试+受影响既有面)+ lint 即收口证据——全量套件归 pre-push 门,勿每变更重复手跑(共享仓全量红先做归属判别,非本变更引入如实留痕不阻断);跑批输出落 `.sillyspec/.runtime/logs/` 日志回看尾部,勿整段吞掉。
|
|
37
37
|
5. **非测试逻辑本身有误时,禁止改测试来"通过"**——修逻辑,不修测试。
|
|
38
38
|
6. **git hook 拦截提交时禁止跳过**(如 `.husky/pre-push`),修复问题后再提交。
|
|
39
39
|
7. **代码必须兼容 Windows / Linux / macOS**(路径 / 换行 / 并发都要顾)。
|
package/package.json
CHANGED
package/src/design-facts.js
CHANGED
|
@@ -387,7 +387,7 @@ export function generateDesignSkeleton({ changeName, decisionsText, author, now
|
|
|
387
387
|
lines.push('- [ ] frontmatter 字段齐全(author/created_at/scale)')
|
|
388
388
|
lines.push('- [ ] 引用所有当前版本 D-xxx@vN')
|
|
389
389
|
lines.push('- [ ] 涉及生命周期关键词时含「生命周期契约表」或紧邻豁免短语')
|
|
390
|
-
lines.push('- [ ] UI 原型分级核对(涉前端文件时变更目录应有 prototype-*.html——新生成(定稿=与实现同方言的真码产物,手绘仅限粗选,输出须带素材来源声明:token
|
|
390
|
+
lines.push('- [ ] UI 原型分级核对(涉前端文件时变更目录应有 prototype-*.html——新生成(定稿=与实现同方言的真码产物,手绘仅限粗选,输出须带素材来源声明:token 源/组件来源/入口位置;产物必须可溯源到源码(管线编译/真实渲染捕获/登记复用三通道),手写 HTML 不得交付,对比稿供用户决策即定稿)或登记复用(复用须按 prototype-<名称>.html 命名,execute 才注入)或跳过原因记入风险登记;本仓形态未在 local.yaml ui_prototype 登记的,收口前补登记)')
|
|
391
391
|
lines.push('- [ ] 不确定的问题标注「⚠️ 自审存疑」')
|
|
392
392
|
lines.push('')
|
|
393
393
|
|
package/src/flow-review-pack.js
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
* ③ 摘要从冻结件本身算——change.patch 文本直解,不走活 git(review-material-pack 的
|
|
12
12
|
* extractDiffSummary 走活 git 解 base 那条路不能搬:防「包说的」与评审员手里冻结面漂移)。
|
|
13
13
|
*/
|
|
14
|
-
import { readFileSync } from 'node:fs'
|
|
14
|
+
import { readFileSync, readdirSync, existsSync } from 'node:fs'
|
|
15
15
|
import { join } from 'node:path'
|
|
16
16
|
|
|
17
17
|
/** 材料包整体体积帽(对齐 review-material-pack.js PACK_MAX_CHARS 先例——包不能成为新的全量面)。 */
|
|
@@ -140,16 +140,47 @@ export function buildFreezeAttribution({ declaredFiles, freezeFiles, foreign })
|
|
|
140
140
|
}
|
|
141
141
|
}
|
|
142
142
|
|
|
143
|
+
/**
|
|
144
|
+
* ledger 测试回执扫描(2026-10-21-review-rerun-scope):verify-runs 各 runId 目录的
|
|
145
|
+
* test-result.json 按 change 匹配取目录名时间序最新一条(CLI 收口亲测产物,符合护栏③
|
|
146
|
+
* 机械抽取——非 agent 自述)。fail-soft:目录缺席/坏 JSON/无匹配 → null(调用方零注入)。
|
|
147
|
+
*/
|
|
148
|
+
export function findLatestTestReceipt(verifyRunsDir, changeName) {
|
|
149
|
+
try {
|
|
150
|
+
if (!verifyRunsDir || !changeName || !existsSync(verifyRunsDir)) return null
|
|
151
|
+
const runIds = readdirSync(verifyRunsDir).filter((d) => /^\d{14,}$/.test(d)).sort()
|
|
152
|
+
for (let i = runIds.length - 1; i >= 0; i--) {
|
|
153
|
+
const p = join(verifyRunsDir, runIds[i], 'test-result.json')
|
|
154
|
+
try {
|
|
155
|
+
const j = JSON.parse(readFileSync(p, 'utf8'))
|
|
156
|
+
// kind:'lint' 独立回执(verify-postcheck 同名同目录落盘)不是测试面——跳过防「测试回执」标签错挂 lint 结果
|
|
157
|
+
if (j && j.change === changeName && j.kind !== 'lint' && (j.status || j.exit_code !== undefined)) return j
|
|
158
|
+
} catch { /* 坏 JSON 跳过更旧轮 */ }
|
|
159
|
+
}
|
|
160
|
+
} catch { /* 扫描失败零注入 */ }
|
|
161
|
+
return null
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/** 回执行渲染(机械——ran_at/status/exit/耗时/命令面截断)。 */
|
|
165
|
+
function testReceiptLines(r) {
|
|
166
|
+
const cmd = String(r.command || '').replace(/\s+/g, ' ').slice(0, 120)
|
|
167
|
+
const dur = Number.isFinite(r.duration_ms) ? `${Math.round(r.duration_ms / 1000)}s` : '?'
|
|
168
|
+
return [
|
|
169
|
+
` ledger 测试回执(CLI 收口亲测,${r.ran_at || '时间未知'}):${r.status || 'unknown'} exit=${r.exit_code ?? '?'} ${dur} | 面:${cmd}${String(r.command || '').length > 120 ? '…' : ''}`,
|
|
170
|
+
` (回执为 CLI 亲测——评审按复跑口径抽验变更面即可,勿整体重跑)`,
|
|
171
|
+
]
|
|
172
|
+
}
|
|
173
|
+
|
|
143
174
|
/**
|
|
144
175
|
* 装配材料包注入行(唯一组包入口):FR 表、patch 摘要、归属切分三子源各自可得各自产出、
|
|
145
176
|
* 全缺返回 null(调用方零注入)。体积帽行级截断(不切半行),截断注记置于基准面语义行之前——
|
|
146
177
|
* 语义行恒为末行;标题+正文+注记+语义行总长恒 ≤ PACK_MAX_CHARS(截断预留注记预算)。
|
|
147
178
|
* requirements.md 读失败(缺件)→ FR 子段零产出;attribution 缺席/非法 → 归属子段零产出
|
|
148
179
|
* (fail-soft 不抛穿,其余子段不受影响)。
|
|
149
|
-
* @param {{ changeDir: string, patchText: string|null, attribution?: object|null }} args
|
|
180
|
+
* @param {{ changeDir: string, patchText: string|null, attribution?: object|null, testReceipt?: object|null }} args
|
|
150
181
|
* @returns {{ lines: string[], truncated: boolean } | null}
|
|
151
182
|
*/
|
|
152
|
-
export function assembleReviewPack({ changeDir, patchText, attribution = null }) {
|
|
183
|
+
export function assembleReviewPack({ changeDir, patchText, attribution = null, testReceipt = null }) {
|
|
153
184
|
let frRows = []
|
|
154
185
|
try {
|
|
155
186
|
frRows = parseFrBindingRows(readFileSync(join(changeDir, 'requirements.md'), 'utf8'))
|
|
@@ -167,6 +198,7 @@ export function assembleReviewPack({ changeDir, patchText, attribution = null })
|
|
|
167
198
|
}
|
|
168
199
|
const attr = normalizeAttribution(attribution)
|
|
169
200
|
if (attr) body.push(...attributionLines(attr))
|
|
201
|
+
if (testReceipt && typeof testReceipt === 'object') body.push(...testReceiptLines(testReceipt))
|
|
170
202
|
const note = ` …(材料包超 ${PACK_MAX_CHARS} 字符截断——需全文时定向查证并列明)`
|
|
171
203
|
let budget = PACK_MAX_CHARS - PACK_TITLE.length - PACK_BASELINE.length - note.length
|
|
172
204
|
const lines = [PACK_TITLE]
|
package/src/flow-review.js
CHANGED
|
@@ -313,6 +313,12 @@ export function renderReviewerTaskbook({ change, changeDir, head = null, probeLi
|
|
|
313
313
|
` (逐条作答:pass 给证据锚点 / gap 给缺件 / fail 给违诺实证——与上方检查单同口径,未答=未审)`,
|
|
314
314
|
] : []),
|
|
315
315
|
``,
|
|
316
|
+
`【复跑口径(与 AGENTS 自验口径同源——2026-10-21-review-rerun-scope)】`,
|
|
317
|
+
` - 评审实测按「变更测试面」复跑:新测试 + 直接受影响既有文件;多文件合并一次 node --test 跑(逐文件跑每次付 ~10s 启动税)。`,
|
|
318
|
+
` - 全量套件禁止:ledger 收口实测已跑变更面、CI test job 与 pre-push 门(lint+docs gate)已覆其余面——评审内重复跑全量是三重保险费(2026-10-20 会话画像实证:单次评审内两轮全量 8.4min)。`,
|
|
319
|
+
` - 材料包含 ledger 测试回执时以回执为基线抽验复核,勿整体重跑。`,
|
|
320
|
+
` - 长输出落 \`.sillyspec/.runtime/logs/\` 日志回看尾部(tail -50),勿整段读入上下文。`,
|
|
321
|
+
``,
|
|
316
322
|
`【纪律】只读——禁止修改任何文件、禁止 git 操作;唯一产物是 review.json。`,
|
|
317
323
|
// 代行惯例(坑 flow-done-sentinel-token-split ②,2026-10-08 实证):宿主无已注册子代理
|
|
318
324
|
// 通道时无法照做「起子代理」——主会话按检查单代行自审可接受,reviewer 字段如实标注
|
|
@@ -341,8 +347,18 @@ export function renderReviewerTaskbook({ change, changeDir, head = null, probeLi
|
|
|
341
347
|
return taskbook
|
|
342
348
|
}
|
|
343
349
|
|
|
344
|
-
/**
|
|
345
|
-
|
|
350
|
+
/**
|
|
351
|
+
* 锚 sha 词元提取(2026-10-20-closeout-roundtrip-slim):字段含 7-40 位独立 hex 词元即通过,
|
|
352
|
+
* 返回归一化纯 sha(小写);无词元返回 null。评审代理常在 sha 后附实测注记(今晚两例被整串
|
|
353
|
+
* 校验拒收,各多一轮收口往返)——词元提取容忍注记形态。归一化只发生在内存对象/返回值,
|
|
354
|
+
* 不回写文件(校验器只读铁律)。误命中面(如连写分支名含 ≥7 位 hex 段)后果有限:
|
|
355
|
+
* reviewedAgainst 是溯源锚非门禁比对;风险评估见变更 design 风险1。
|
|
356
|
+
*/
|
|
357
|
+
const extractAnchorSha = (v) => {
|
|
358
|
+
if (typeof v !== 'string') return null
|
|
359
|
+
const m = v.match(/(?<![0-9a-fA-F])[0-9a-fA-F]{7,40}(?![0-9a-fA-F])/)
|
|
360
|
+
return m ? m[0].toLowerCase() : null
|
|
361
|
+
}
|
|
346
362
|
|
|
347
363
|
/**
|
|
348
364
|
* 变更的跨仓冻结面仓键(无 change-patch.json / 无 repos / 不可读 → null=单仓形态)。
|
|
@@ -376,16 +392,22 @@ export function validateReviewJson(path) {
|
|
|
376
392
|
if (!['PASS', 'FAIL'].includes(r.verdict)) errors.push('verdict 必须为 PASS|FAIL')
|
|
377
393
|
if (r.reviewedAgainst !== undefined) {
|
|
378
394
|
if (typeof r.reviewedAgainst === 'string') {
|
|
379
|
-
|
|
395
|
+
const _anchor = extractAnchorSha(r.reviewedAgainst)
|
|
396
|
+
if (!_anchor) errors.push('reviewedAgainst 须为 7-40 位 hex sha(git rev-parse HEAD 输出,照抄任务书标注)')
|
|
397
|
+
else r.reviewedAgainst = _anchor
|
|
380
398
|
} else if (r.reviewedAgainst && typeof r.reviewedAgainst === 'object' && !Array.isArray(r.reviewedAgainst)) {
|
|
381
|
-
|
|
399
|
+
const _ws = extractAnchorSha(r.reviewedAgainst.workspace)
|
|
400
|
+
if (!_ws) errors.push('reviewedAgainst 对象形态须含 workspace: 7-40 位 hex sha(主仓评审对象锚)')
|
|
401
|
+
else r.reviewedAgainst.workspace = _ws
|
|
382
402
|
if (r.reviewedAgainst.repos !== undefined) {
|
|
383
403
|
const repos = r.reviewedAgainst.repos
|
|
384
404
|
if (!repos || typeof repos !== 'object' || Array.isArray(repos)) {
|
|
385
405
|
errors.push('reviewedAgainst.repos 须为对象(仓键 → sha;仓键取 local.yaml repos 注册表键)')
|
|
386
406
|
} else {
|
|
387
407
|
for (const [k, v] of Object.entries(repos)) {
|
|
388
|
-
|
|
408
|
+
const _repo = extractAnchorSha(v)
|
|
409
|
+
if (!_repo) errors.push(`reviewedAgainst.repos.${k} 须为 7-40 位 hex sha(该仓 git rev-parse HEAD)`)
|
|
410
|
+
else repos[k] = _repo
|
|
389
411
|
}
|
|
390
412
|
}
|
|
391
413
|
}
|
|
@@ -425,9 +447,10 @@ export function reviewAnchoredToHead(reviewPath, head) {
|
|
|
425
447
|
try {
|
|
426
448
|
const r = JSON.parse(readFileSync(reviewPath, 'utf8'))
|
|
427
449
|
const raRaw = r?.reviewedAgainst
|
|
428
|
-
const
|
|
429
|
-
: (raRaw && typeof raRaw === 'object' && typeof raRaw.workspace === 'string' ? raRaw.workspace
|
|
430
|
-
|
|
450
|
+
const raRawNorm = typeof raRaw === 'string' ? raRaw
|
|
451
|
+
: (raRaw && typeof raRaw === 'object' && typeof raRaw.workspace === 'string' ? raRaw.workspace : '')
|
|
452
|
+
const ra = extractAnchorSha(raRawNorm)
|
|
453
|
+
if (!ra) return false
|
|
431
454
|
return h === ra || h.startsWith(ra) || ra.startsWith(h)
|
|
432
455
|
} catch { return false }
|
|
433
456
|
}
|
package/src/flow.js
CHANGED
|
@@ -1921,6 +1921,7 @@ export async function cmdFlowDone({ change, cwd, specBase, runtimeRootOpt = null
|
|
|
1921
1921
|
console.error(` ① git commit 这些文件(信息尾缀带 (${change}) 或 (${change}),重跑本命令——冻结面自动并入`)
|
|
1922
1922
|
console.error(` ② 全部归属本变更:flow done --change ${change} --freeze-dirty`)
|
|
1923
1923
|
console.error(` ③ 接受审计缺口:flow done --change ${change} --accept-dirty-gap`)
|
|
1924
|
+
console.error(` 💡 快速通道:脏文件全属并行会话在途/运行残渣且不在本变更交付面时,直接重跑带 --accept-dirty-gap(显式 pathspec 提交面已隔离归属;属本变更的文件必须走①提交)`)
|
|
1924
1925
|
reportMidFail('patch')
|
|
1925
1926
|
process.exit(1)
|
|
1926
1927
|
}
|
|
@@ -2395,8 +2396,11 @@ export async function cmdFlowDone({ change, cwd, specBase, runtimeRootOpt = null
|
|
|
2395
2396
|
} catch { /* 归属面 fail-soft:装配失败零注入该子段 */ }
|
|
2396
2397
|
let _packLines = null
|
|
2397
2398
|
try {
|
|
2398
|
-
const { assembleReviewPack } = await import('./flow-review-pack.js')
|
|
2399
|
-
|
|
2399
|
+
const { assembleReviewPack, findLatestTestReceipt } = await import('./flow-review-pack.js')
|
|
2400
|
+
// ledger 测试回执(2026-10-21-review-rerun-scope):ledger 先于 review(SUBSTEPS 序),
|
|
2401
|
+
// verify-runs 最新本变更 test-result 作回执注入——评审从「自己重跑」变「回执基线抽验」。
|
|
2402
|
+
const _receipt = findLatestTestReceipt(join(specBase, '.runtime', 'verify-runs'), change)
|
|
2403
|
+
const _pack = assembleReviewPack({ changeDir, patchText, attribution: _attribution, testReceipt: _receipt })
|
|
2400
2404
|
if (_pack && Array.isArray(_pack.lines) && _pack.lines.length > 0) _packLines = _pack.lines
|
|
2401
2405
|
} catch { /* 材料包面 fail-soft:装配失败零注入 */ }
|
|
2402
2406
|
console.log(renderReviewerTaskbook({ change, changeDir, head: taskbookHead, probeLines: _probeLines, packLines: _packLines }))
|
package/src/run/gates.js
CHANGED
|
@@ -225,7 +225,7 @@ export function warnMissingUiPrototype(stageName, changeName, currentStepName, s
|
|
|
225
225
|
const design = readFileSync(designPath, 'utf8')
|
|
226
226
|
const feExts = [...new Set((design.match(/\.(vue|svelte|astro|tsx|jsx|css|scss|sass|less|styl|html)\b/gi) || []).map(s => s.replace('.', '').toLowerCase()))]
|
|
227
227
|
if (feExts.length === 0) return
|
|
228
|
-
console.warn(` ⚠️ design.md 命中前端文件类型(${feExts.join('/')})但变更目录无 prototype-*.html——轻微样式改动可忽略;布局/交互流程级变化应补原型(sillyspec run brainstorm --reopen 回到「分段展示设计」,或直接让 agent 补生成;已有可复跑 HTML 原型则登记复用:复制进变更目录并命名 prototype-<名称>.html,execute
|
|
228
|
+
console.warn(` ⚠️ design.md 命中前端文件类型(${feExts.join('/')})但变更目录无 prototype-*.html——轻微样式改动可忽略;布局/交互流程级变化应补原型(sillyspec run brainstorm --reopen 回到「分段展示设计」,或直接让 agent 补生成;已有可复跑 HTML 原型则登记复用:复制进变更目录并命名 prototype-<名称>.html,execute 按此命名注入;产物须可溯源到源码(管线编译/真实渲染捕获/登记复用),手写 HTML 不得交付,对比稿供用户决策即定稿;本仓形态未在 local.yaml ui_prototype 登记的,收口前补登记——form: real-code 或 single-html+豁免 ruling)`)
|
|
229
229
|
}
|
|
230
230
|
|
|
231
231
|
/**
|
package/src/stages/brainstorm.js
CHANGED
|
@@ -308,6 +308,7 @@ sillyspec flow start --change <YYYY-MM-DD-名> --input "<一句 10-30 字中文
|
|
|
308
308
|
- **可复跑**:能被本项目工具链跑起来(dev server / 组件文档页 / 构建产物均可,入口 HTML 从本地 node_modules 引真实库也行)——先查 scan 文档与现有前端代码就近发现项目原型管线,已有则复用;**历史原型先例同为单文件不构成管线存在**(51 个手绘先例是惯例债不是证据);查无真码管线时**先单开一个轻量变更搭最小入口**(bootstrap 与业务变更分离),本变更按登记形态或降级声明执行
|
|
309
309
|
- **高保真=组件即实现原料,不是「看起来像」**:布局/组件/交互按真实效果呈现,execute 阶段直接对照搬组件,不经过"看图重新翻译";**生成档输出必带素材来源声明**(真值 token 源文件路径 / 组件来源 / 入口位置——缺声明=无源可查,2026-10-15 server-logs-panel 实证零声明)
|
|
310
310
|
- **手绘单文件(自造样式)仅限一次性粗选对比,不得作为定稿原型**(frontend-apple-style 实证:手绘定稿后每条意见都要反向翻译回真实代码);粗选定了方向,升级真码再收口
|
|
311
|
+
- **产物必须可溯源到源码(三通道,手写 HTML 无合法出口)**:变更目录的 prototype-*.html 必须是——①本仓原型管线的编译产物,或 ②项目真实运行面的渲染捕获产物(dev server / 预览路由渲染真实组件后离线化导出;捕获脚本可随变更交付,捕获物仍是渲染结果,禁手改),或 ③登记复用的既有真码原型。**对比稿/视觉稿同样适用:凡供用户评审决策的原型即定稿提案,无粗选豁免**(2026-10-20 dsh 会话实证:视觉对比稿手写 HTML 交付,现状凭主题默认值想象)——粗选仅限口头或一次性草稿快速比选,不落变更目录充当交付物。重做/视觉类变更的现状基线必须真实渲染捕获或用户提供的现有截图,**禁止凭主题默认值想象现状、禁止手写复刻现状**
|
|
311
312
|
- **例外与降级**:项目本身即单文件 HTML 方言(纯静态站)时单文件即真码,不适用手绘限定;无法同方言真码化(如目标平台无 HTML 运行面)时显式声明降级与理由请用户裁决留痕——静默降级会被收口「UI 视觉证据」探针拦下
|
|
312
313
|
|
|
313
314
|
### 铁律 — 必须等待用户确认设计
|
package/src/ui-visual.js
CHANGED
|
@@ -161,6 +161,9 @@ export function buildUiGuidanceLines(specBase = null, paths = []) {
|
|
|
161
161
|
`🎨 UI 变更执行须知(本变更触及前端页面或 UI——advisory 引导非门禁):`,
|
|
162
162
|
` 1. 定稿原型必须是可复跑的真码产物(与实现同方言、能被本项目工具链构建)——手绘单文件`,
|
|
163
163
|
` 仅限一次性粗选对比,不作为交付原型(2026-09-28 frontend-apple-style 实证:手绘定稿后每条意见都要反向翻译回真实代码);`,
|
|
164
|
+
` 产物必须可溯源到源码(本仓管线编译 / 项目真实运行面渲染捕获 / 登记复用,三通道)——手写 HTML`,
|
|
165
|
+
` 无合法出口;对比稿/视觉稿供用户决策即定稿,无粗选豁免;重做类现状基线须真实捕获或用户截图,`,
|
|
166
|
+
` 禁止凭主题默认值想象现状(2026-10-20 dsh 会话实证)`,
|
|
164
167
|
` 2. 开工先就近发现本项目原型管线:查本次变更触达项目自己的脚本、任务与工具链(多项目仓`,
|
|
165
168
|
` 按触达路径就近,不假设全仓共享一套);已有则复用,没有则现场搭最小管线并把入口留在`,
|
|
166
169
|
` 项目惯例位置——下一个会话能自己发现;`,
|
|
@@ -32,7 +32,7 @@ sillyspec flow start --change <YYYY-MM-DD-名> --input "<一句 10-30 字中文
|
|
|
32
32
|
1. **禁止绕过本文件规则和 SillySpec 流程**。所有变更走 sillyspec 流程,不裸改裸提交。
|
|
33
33
|
2. **改代码前必须先说明依据**——依据的文档路径(design.md / 模块文档 / file-lifecycle.md(如有))或现有代码依据,无依据不改。
|
|
34
34
|
3. **执行顺序**:文档 → 读代码 → 写测试 → 写实现 → 跑测试 → 验收 → 更新文档(轻量道即:FR 绑定+design 四节+用户批准先于动码,读代码探究不算动码)。测试按层适用:纯逻辑先写测试,行为契约断言用户可见行为,视觉样式与纯配置/文档不写测试(记「不适用:理由」,验收靠规范与人工)。
|
|
35
|
-
4. **实证核验再收口**:触及 `src`/`test` 的改动,CLI 会在收口时亲自实测(实测失败阻断收口;纯 doc/配置自动跳过)。测试面按变更动态推断(本变更测试 ∪ FR 关联回归 ∪ import 依赖,runner 自项目结构推断——无需配置);lint 走 `.sillyspec/local.yaml` 的 `commands.lint`。以落盘文件与测试结果为准,不信口头"已完成"
|
|
35
|
+
4. **实证核验再收口**:触及 `src`/`test` 的改动,CLI 会在收口时亲自实测(实测失败阻断收口;纯 doc/配置自动跳过)。测试面按变更动态推断(本变更测试 ∪ FR 关联回归 ∪ import 依赖,runner 自项目结构推断——无需配置);lint 走 `.sillyspec/local.yaml` 的 `commands.lint`。以落盘文件与测试结果为准,不信口头"已完成"。自验口径=变更测试面(新增测试+受影响既有面)+ lint 即收口证据——全量套件归 pre-push 门,勿每变更重复手跑(共享仓全量红先做归属判别,非本变更引入如实留痕不阻断);跑批输出落 `.sillyspec/.runtime/logs/` 日志回看尾部,勿整段吞掉。
|
|
36
36
|
5. **非测试逻辑本身有误时,禁止改测试来"通过"**——修逻辑,不修测试。
|
|
37
37
|
6. **git hook 拦截提交时禁止跳过**(如 `.husky/pre-push`),修复问题后再提交。
|
|
38
38
|
7. **代码必须兼容 Windows / Linux / macOS**(路径 / 换行 / 并发都要顾)。
|