sillyspec 3.26.8 → 3.26.10
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/.claude/skills/sillyspec-archive/SKILL.md +1 -2
- package/.claude/skills/sillyspec-brainstorm/SKILL.md +1 -2
- package/.claude/skills/sillyspec-execute/SKILL.md +11 -3
- package/.claude/skills/sillyspec-explore/SKILL.md +0 -1
- package/.claude/skills/sillyspec-plan/SKILL.md +1 -2
- package/.claude/skills/sillyspec-quick/SKILL.md +4 -5
- package/.claude/skills/sillyspec-scan/SKILL.md +2 -2
- package/.claude/skills/sillyspec-status/SKILL.md +0 -1
- package/.claude/skills/sillyspec-verify/SKILL.md +4 -3
- package/README.md +5 -9
- package/package.json +2 -2
- package/packages/dashboard/server/index.js +14 -8
- package/src/change-risk-profile.js +5 -207
- package/src/config-schema.js +6 -1
- package/src/constants.js +97 -106
- package/src/db-engine.js +1 -1
- package/src/docs-check.js +34 -5
- package/src/git-helper.js +2 -2
- package/src/hooks/worktree-guard.js +1 -1
- package/src/index.js +96 -13
- package/src/init.js +14 -3
- package/src/machine-interface.js +7 -4
- package/src/progress/stage-machine.js +3 -0
- package/src/quicklog.js +203 -4
- package/src/run/command.js +101 -3
- package/src/run/complete-handlers.js +1399 -1254
- package/src/run/complete.js +9 -1
- package/src/run/concurrent-detect.js +22 -0
- package/src/run/gates.js +67 -1
- package/src/run/prompt.js +761 -713
- package/src/run/quick-audit.js +146 -137
- package/src/run/shared.js +1215 -1109
- package/src/run/stage.js +17 -1
- package/src/scan-diff.js +388 -0
- package/src/scan-staleness.js +18 -13
- package/src/spec-sync.js +319 -0
- package/src/stage-contract-spec.js +1 -1
- package/src/stage-review.js +1 -1
- package/src/stages/archive.js +2 -1
- package/src/stages/brainstorm.js +2 -2
- package/src/stages/execute.js +1003 -985
- package/src/stages/plan-postcheck.js +22 -1
- package/src/stages/plan.js +74 -40
- package/src/stages/quick.js +13 -5
- package/src/stages/scan.js +4 -3
- package/src/stages/verify.js +14 -12
- package/src/sync.js +22 -0
- package/src/task-review.js +12 -4
- package/src/workflow.js +8 -185
- package/src/worktree-apply.js +122 -0
- package/src/worktree.js +50 -6
- package/templates/claude-instruction.md +1 -1
- package/templates/prompts/taskcard-rules.md +1 -0
- package/templates/prompts/verify-probes.md +2 -2
- package/templates/skills/sillyspec-onboard/SKILL.md +0 -92
|
@@ -14,7 +14,7 @@ import { existsSync, readFileSync as _readFileSync, readdirSync, mkdtempSync, wr
|
|
|
14
14
|
// frontmatter/字段正则(`^---\n`、`allowed_paths:\s*\n…`、`^goal:` 等)失配,报「缺 frontmatter
|
|
15
15
|
// /缺字段」假错误(见缺陷 windows-python-crlf-taskcard)。读取时统一转 LF,一处覆盖全部正则。
|
|
16
16
|
const readFileSync = (filePath, encoding) => _readFileSync(filePath, encoding).replace(/\r\n/g, '\n')
|
|
17
|
-
import { join as pJoin } from 'path'
|
|
17
|
+
import { join as pJoin, basename } from 'path'
|
|
18
18
|
import { tmpdir } from 'os'
|
|
19
19
|
import jsYaml from 'js-yaml'
|
|
20
20
|
import { parseFileChangeList, pathMatches } from '../change-list.js'
|
|
@@ -1178,6 +1178,27 @@ export async function executePlanPostcheck(context) {
|
|
|
1178
1178
|
console.log(` ✅ design.md ${coverage.designFiles.length} 个文件全部被 task allowed_paths 覆盖`)
|
|
1179
1179
|
}
|
|
1180
1180
|
|
|
1181
|
+
// ── 1e. 阶段完成产物校验(plan stage contract)──
|
|
1182
|
+
// plan 完成 gate 还会跑 stage-contract.validatePlanOutputs;为免「postcheck 通过 →
|
|
1183
|
+
// stage gate 又报 module-impact 缺失/entry-point-wiring 未覆盖」的修一层撞一层,
|
|
1184
|
+
// 在此把 stage contract 的 error/warning 也聚合进本轮输出。
|
|
1185
|
+
const { validatePlanOutputs } = await import('../stage-contract.js')
|
|
1186
|
+
if (typeof validatePlanOutputs === 'function') {
|
|
1187
|
+
const contract = validatePlanOutputs(context.cwd, basename(changeDir), { specRoot })
|
|
1188
|
+
if (contract.errors.length > 0) {
|
|
1189
|
+
failures.push({
|
|
1190
|
+
name: 'plan 阶段产物契约校验(stage-contract validatePlanOutputs)',
|
|
1191
|
+
errors: contract.errors,
|
|
1192
|
+
hint: '修复方式:补缺失产物(如 module-impact.md)、在 design.md 明示不改理由、或修正 task allowed_paths 覆盖 design.md 提及的入口文件。',
|
|
1193
|
+
})
|
|
1194
|
+
}
|
|
1195
|
+
if (contract.warnings.length > 0) {
|
|
1196
|
+
for (const w of contract.warnings) {
|
|
1197
|
+
console.warn(`\n ⚠️ ${w}`)
|
|
1198
|
+
}
|
|
1199
|
+
}
|
|
1200
|
+
}
|
|
1201
|
+
|
|
1181
1202
|
// ── 聚合输出:一轮 --done 暴露全部失败项(坑6③)──
|
|
1182
1203
|
if (failures.length > 0) {
|
|
1183
1204
|
console.error(`\n❌ plan postcheck 失败(${failures.length} 类问题,已全部列出——一次修复后重跑,无需逐个迭代):`)
|
package/src/stages/plan.js
CHANGED
|
@@ -113,6 +113,9 @@ needs_human_review: true | false
|
|
|
113
113
|
|
|
114
114
|
然后列出已加载的文件清单(含 decisions.md 当前版本/未决项状态、模块文档 + 模块依赖关系摘要)。
|
|
115
115
|
|
|
116
|
+
### 落盘锚点(必须执行)
|
|
117
|
+
把 plan_level 写进 plan.md frontmatter(\`plan_level: <none|light|full>\`)——上一步分类结果在本步落盘为持久锚点,后续步骤(及 context 压缩后的恢复)一律读 plan.md frontmatter,不依赖对话记忆传递。
|
|
118
|
+
|
|
116
119
|
分类完成后,继续进入下一步。`,
|
|
117
120
|
outputHint: '复杂度分类结果 + 文件清单',
|
|
118
121
|
optional: false
|
|
@@ -125,13 +128,13 @@ needs_human_review: true | false
|
|
|
125
128
|
const stepGeneratePlan = {
|
|
126
129
|
id: 'generate_plan',
|
|
127
130
|
name: '生成分级计划',
|
|
128
|
-
prompt:
|
|
131
|
+
prompt: `根据 plan.md frontmatter 的 plan_level 结果,按对应级别生成计划。
|
|
129
132
|
|
|
130
133
|
### 操作
|
|
131
|
-
1.
|
|
134
|
+
1. 读取 plan.md frontmatter 的 \`plan_level:\` 字段(上一步已落盘为持久锚点;文件不存在或无该字段时回退读上一步输出的分类结果)
|
|
132
135
|
2. 读取 tasks.md 和 design.md 了解需求范围
|
|
133
136
|
3. 按 plan_level 选择对应模板输出
|
|
134
|
-
4. 保存 plan.md(审查在下一步"审查计划"
|
|
137
|
+
4. 保存 plan.md(审查在下一步"审查计划"独立进行,不在本步自审——避免生成与自审同一次输出);frontmatter 保留 plan_level 字段不删
|
|
135
138
|
|
|
136
139
|
---
|
|
137
140
|
|
|
@@ -283,7 +286,7 @@ full 计划的约束:
|
|
|
283
286
|
### 通用操作(所有级别)
|
|
284
287
|
1. 读取 tasks.md 获取任务列表
|
|
285
288
|
2. 读取 design.md 获取文件变更清单
|
|
286
|
-
3.
|
|
289
|
+
3. 读取 plan.md frontmatter 的 \`plan_level:\` 字段(持久锚点;缺失时回退上一步输出)
|
|
287
290
|
4. 按对应级别模板生成内容
|
|
288
291
|
5. 保存到变更目录下的 plan.md(路径格式:\`{SPEC_ROOT}/changes/<change-name>/plan.md\`,其中 <change-name> 是变更目录名,直接使用,不加子目录。正确路径示例:\`{SPEC_ROOT}/changes/2026-05-28-agent-log-streaming/plan.md\`)
|
|
289
292
|
**plan_level 为 none 时生成最小 plan.md(占位),不生成完整蓝图。**
|
|
@@ -346,15 +349,25 @@ tier: {REVIEW_TIER}({REVIEW_TIER_REASON})
|
|
|
346
349
|
### 生成 module-impact.md 首版(scale≠small 时)
|
|
347
350
|
计划审查通过后,顺带生成 module-impact.md 首版(本次变更的模块影响分析,供 execute/verify 阶段更新、archive 阶段终审)。scale=small 不生成(small 走 quick,module-impact 对 quick 无用)。
|
|
348
351
|
|
|
349
|
-
输入(此时 TaskCard/allowed_paths
|
|
352
|
+
输入(此时 TaskCard/allowed_paths 尚未生成——在下一步「生成 TaskCard」才产出,故用以下两项作输入,粒度与 archive 现状一致):
|
|
350
353
|
- design.md 的「文件变更清单」(本次计划改哪些源码文件)
|
|
351
354
|
- plan.md 的任务列表(每个 task 改的范围)
|
|
352
355
|
|
|
353
|
-
|
|
356
|
+
步骤(module-impact 的生成口径与 archive 终审一致):
|
|
354
357
|
1. 读 {SPEC_ROOT}/docs/<project>/modules/_module-map.yaml(模块→文件路径映射)。**不存在 → 降级**:生成只含 unmapped 部分的 module-impact.md + 提示「建议运行 scan 生成模块映射」,不阻断
|
|
355
358
|
2. 对照 design 文件变更清单 + plan 任务列表,逐文件匹配所属模块(命中的归 mapped,未命中的归 unmapped)
|
|
356
359
|
3. 生成模块影响矩阵(模块 × 影响类型[新增/修改/删除/依赖变更] × 说明),落盘 {SPEC_ROOT}/changes/<change>/module-impact.md
|
|
357
360
|
4. 首行标题必须用中文:# 模块影响分析(Module Impact)— <变更简述>
|
|
361
|
+
5. **必须含「更新结果」表骨架**(本表是 verify/archive 死信门控的收口目标——CLI 硬校验表内无 pending/待办行,漏写此表则 agent 只能从 gate 报错反推格式):
|
|
362
|
+
\`\`\`markdown
|
|
363
|
+
## 更新结果
|
|
364
|
+
|
|
365
|
+
| 目标 | 操作 | 状态 |
|
|
366
|
+
|------|------|------|
|
|
367
|
+
| \`modules/<id>.md\` | 更新<模块名>模块卡(本次变更涉及) | pending |
|
|
368
|
+
| \`_module-map.yaml\` | 无变化(未增删模块) | skipped |
|
|
369
|
+
\`\`\`
|
|
370
|
+
规则:每个受影响模块文档一行,状态列初始化 pending;无变化的行直接写 skipped。execute/verify 完成文档同步后把对应行回填 done;确定不同步的行改 skipped 并在操作列写明原因。**末列是状态列**(done/skipped/pending),不要在表尾追加其他列。
|
|
358
371
|
|
|
359
372
|
execute/verify 阶段会按实际代码变更更新此文档;archive 阶段会最终确认它。
|
|
360
373
|
|
|
@@ -371,8 +384,10 @@ execute/verify 阶段会按实际代码变更更新此文档;archive 阶段会
|
|
|
371
384
|
// ═══════════════════════════════════════════════════════════════
|
|
372
385
|
|
|
373
386
|
/**
|
|
374
|
-
* 构建紧凑 TaskCard
|
|
387
|
+
* 构建紧凑 TaskCard 协调器步骤(单步,子代理按 batch 并行写卡片)
|
|
375
388
|
* 每个 task 生成 20~40 行紧凑可执行卡片
|
|
389
|
+
* 允许把多个相关 task 合并为一个 batch,由单个子代理一次生成多张卡片,
|
|
390
|
+
* 在保留 plan 任务拆分完整性的同时减少子代理调用数量。
|
|
376
391
|
*/
|
|
377
392
|
export function buildCoordinatorStep(changeDir, taskNames) {
|
|
378
393
|
const taskList = taskNames.map(t => {
|
|
@@ -380,23 +395,9 @@ export function buildCoordinatorStep(changeDir, taskNames) {
|
|
|
380
395
|
return `- task-${num}: ${t.name}`
|
|
381
396
|
}).join('\n')
|
|
382
397
|
|
|
383
|
-
const
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
任务编号:task-${num}
|
|
387
|
-
任务名称:${t.name}
|
|
388
|
-
文件路径:${changeDir}/tasks/task-${num}.md
|
|
389
|
-
当前时间:<now-datetime>(frontmatter 的 created_at 使用此值)
|
|
390
|
-
当前用户:<git-user>(frontmatter 的 author 使用此值)
|
|
391
|
-
|
|
392
|
-
操作:
|
|
393
|
-
1. 读取 ${changeDir}/design.md 和 ${changeDir}/plan.md 了解上下文
|
|
394
|
-
2. 读取相关源文件了解现有代码
|
|
395
|
-
3. 生成紧凑 TaskCard(20~40 行),格式如下:
|
|
396
|
-
|
|
397
|
-
---
|
|
398
|
-
id: task-${num}
|
|
399
|
-
title: ${t.name}
|
|
398
|
+
const taskcardTemplate = `---
|
|
399
|
+
id: <task-id>
|
|
400
|
+
title: <task-name>
|
|
400
401
|
title_zh: <任务中文标题>
|
|
401
402
|
author: <git-user>
|
|
402
403
|
created_at: <now-datetime>
|
|
@@ -434,10 +435,7 @@ related_tests: # 可选。当本 task 改动会导致
|
|
|
434
435
|
reason: 旧断言假设单例,改归属键后需同步
|
|
435
436
|
---
|
|
436
437
|
|
|
437
|
-
{{include: taskcard-rules}}
|
|
438
|
-
\`\`\``
|
|
439
|
-
}).join('\n\n')
|
|
440
|
-
|
|
438
|
+
{{include: taskcard-rules}}`
|
|
441
439
|
|
|
442
440
|
const prompt = `为 plan.md 中的每个任务生成紧凑 TaskCard。
|
|
443
441
|
|
|
@@ -455,22 +453,58 @@ ${taskList}
|
|
|
455
453
|
|
|
456
454
|
## 执行方式(必须严格遵守)
|
|
457
455
|
|
|
458
|
-
**你必须使用 Agent tool
|
|
456
|
+
**你必须使用 Agent tool 启动子代理来写卡片,不要自己写。**
|
|
459
457
|
|
|
460
458
|
1. 确认 \`${changeDir}/tasks/\` 目录存在(不存在则创建)
|
|
461
|
-
2.
|
|
462
|
-
|
|
463
|
-
4
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
459
|
+
2. **按 batch 分派子代理(减少总子代理数量,而不是一个 task 一个子代理):**
|
|
460
|
+
- 把任务按「同一 Wave + 同一模块/相近能力 + 无跨 batch 强依赖」原则分组
|
|
461
|
+
- **每个 batch 包含 2~4 个 task**;Wave 内任务数 ≤4 时整个 Wave 可作为一个 batch
|
|
462
|
+
- 有提供/消费契约的 task 尽量放到同一 batch(子代理能同时看到 consumer 与 provider,避免契约字段漏配)
|
|
463
|
+
- 跨 Wave 依赖的 task 不要放在同一 batch(子代理只需读 plan.md,但 batch 内 task 的 allowed_paths 不应互相阻塞)
|
|
464
|
+
3. 为每个 batch 启动一个独立子代理(Agent tool),可并行启动多个 batch
|
|
465
|
+
4. 每个子代理使用下方「批量 TaskCard 子代理 prompt」,一次生成该 batch 的全部 task-N.md
|
|
466
|
+
5. 等待所有 batch 子代理完成
|
|
467
|
+
6. 验证每个 task-N.md 文件已生成且非空
|
|
468
|
+
|
|
469
|
+
> 设计意图:plan.md 里 task 数可以较多(能力拆分完整),但 TaskCard 生成阶段要合理合并,避免子代理数量随 task 数线性爆炸。一个子代理生成 2~4 张卡片与生成 1 张卡片的 token/时间成本接近,却能显著减少协调开销。
|
|
470
|
+
|
|
471
|
+
### 批量 TaskCard 子代理 prompt
|
|
472
|
+
\`\`\`
|
|
473
|
+
你是一个专注的 TaskCard 生成器。你的任务是为指定 batch 生成全部 TaskCard。
|
|
474
|
+
|
|
475
|
+
## 输入
|
|
476
|
+
- 变更目录:${changeDir}
|
|
477
|
+
- 当前时间:<now-datetime>(frontmatter 的 created_at 使用此值)
|
|
478
|
+
- 当前用户:<git-user>(frontmatter 的 author 使用此值)
|
|
479
|
+
- 本 batch 任务列表:
|
|
480
|
+
<由主 agent 注入:task-01: 名称 / task-02: 名称 / ...>
|
|
481
|
+
|
|
482
|
+
## 操作
|
|
483
|
+
1. 读取 ${changeDir}/design.md 和 ${changeDir}/plan.md 了解整体上下文
|
|
484
|
+
2. 读取本 batch 涉及的相关源文件
|
|
485
|
+
3. 为 batch 中的**每一个 task 独立生成**一个 task-N.md 文件
|
|
486
|
+
4. 每个文件使用如下模板(把 <task-id> / <task-name> 替换为具体编号和名称):
|
|
487
|
+
|
|
488
|
+
${taskcardTemplate}
|
|
489
|
+
|
|
490
|
+
## 约束
|
|
491
|
+
- 每个 task 20~40 行;禁止在 TaskCard 里写实现细节之外的冗长设计
|
|
492
|
+
- 同 batch 内 task 的 allowed_paths 不要互相冲突
|
|
493
|
+
- 若 task 之间有 provides/expects_from 契约,同 batch 生成时必须字段对齐
|
|
494
|
+
- 只生成本 batch 声明的任务,不要多写或少写
|
|
495
|
+
- 不要在 TaskCard 里泄露 plan.md 中未出现的实现假设
|
|
496
|
+
|
|
497
|
+
## 完成标志
|
|
498
|
+
- 本 batch 的每个 task-N.md 都已写入 ${changeDir}/tasks/
|
|
499
|
+
- 每个文件非空且 frontmatter 完整
|
|
500
|
+
\`\`\`
|
|
470
501
|
|
|
471
502
|
## 验收(生成后自查,不另开步骤)
|
|
472
503
|
- 每个 task-N.md 文件存在且非空
|
|
473
|
-
-
|
|
504
|
+
- 所有 task 都被某个 batch 覆盖、无遗漏、无重复
|
|
505
|
+
- frontmatter 字段分两组对照(与 plan-postcheck / taskcard-rules.md 同源,三处清单以此为准):
|
|
506
|
+
- **硬校验 9 字段**(缺失 plan-postcheck 直接报错阻断):id、title、title_zh、allowed_paths、goal、implementation、acceptance、verify、constraints
|
|
507
|
+
- **规范约定 5 字段**(应填但缺失只影响规范性,不阻断):author、created_at、priority、depends_on、blocks
|
|
474
508
|
- 每个 task 总长度 20~40 行
|
|
475
509
|
- **一致性自查**:
|
|
476
510
|
- allowed_paths 有无冲突
|
|
@@ -481,7 +515,7 @@ ${subagentPrompts}
|
|
|
481
515
|
|
|
482
516
|
return {
|
|
483
517
|
id: 'generate_blueprints',
|
|
484
|
-
name: '生成 TaskCard
|
|
518
|
+
name: '生成 TaskCard(子代理按 batch 并行)',
|
|
485
519
|
prompt,
|
|
486
520
|
outputHint: 'TaskCard 生成结果',
|
|
487
521
|
optional: false
|
package/src/stages/quick.js
CHANGED
|
@@ -12,6 +12,12 @@ export const definition = {
|
|
|
12
12
|
- CLI 是短进程,run 与 done 是独立进程,sessionId 靠 --change 跨进程传递
|
|
13
13
|
- **完成每个 step 必须带 --change <quick-session-id>**(命令由 CLI 在下方注入);多会话并发时不带会命中他者会话状态
|
|
14
14
|
|
|
15
|
+
### ⚠️ 危险文件预声明(预判要改 src 核心时看一眼)
|
|
16
|
+
step 3 --done 审计会把 src 核心文件(CLI / 状态机 / 注入框架等危险清单)、新增文件、删除文件判为危险变更并拦截。若你**预判**本 quick 的修复目标本身就是这类文件,重新启动 quick 时带上 --force-baseline(受保护/危险文件)/ --allow-new(新增文件)/ --allow-delete(删除文件)预声明——flag 会持久化进会话守卫,后续 --done 直接生效,省一轮拦截重跑。不预带也可:step 3 拦截后按提示带 flag 重跑 --done 同样解锁。
|
|
17
|
+
|
|
18
|
+
### 🧭 文件预声明(多 agent 并发仓库推荐)
|
|
19
|
+
--done 审计按「时间窗 diff」归属文件:quick 窗口内**并行会话**改的文件也会被算进本会话的 QUICKLOG「文件:」行。多个 agent 同时操作本仓库时,启动 quick 建议带 \`--files <a.js,b.js>\` 声明预计要改的文件——声明即归属:文件行只落声明命中的文件,窗口内未声明脏文件以「审计:」行落盘追溯(不混入、不静默丢)。不预判也行:--done 后按 QUICKLOG 核对说明手动精修文件行兜底。
|
|
20
|
+
|
|
15
21
|
### 操作
|
|
16
22
|
1. 检查关联变更(\`<linked-changes>\`,逗号分隔的变更名列表;显示「(无)」= 不关联变更),确定记录方式
|
|
17
23
|
2. 理解任务:模糊则问一个问题确认
|
|
@@ -33,7 +39,7 @@ export const definition = {
|
|
|
33
39
|
|
|
34
40
|
**你不要创建或修改任何 QUICKLOG 的条目骨架 / tasks.md 记录**——ql-ID 分配、「进行中」条目、状态翻转、task 勾选、轮转全由 CLI 接管,你无需手写。<quicklog-id> 可用于 design.md / plan.md / archive / 模块变更索引引用。
|
|
35
41
|
|
|
36
|
-
> ℹ️ step 3 --done 时,CLI 已落盘结构化 QUICKLOG
|
|
42
|
+
> ℹ️ step 3 --done 时,CLI 已落盘结构化 QUICKLOG 条目(标题从「需求:」提取——届时写一句语义化短标题,规则见 step3 结果摘要模板;正文四字段分行、文件行可用 --file-notes 写多行括注)。事后只需按需核对,详见 step 3「QUICKLOG 正文核对」。
|
|
37
43
|
|
|
38
44
|
### 输出
|
|
39
45
|
任务理解 + 上下文摘要`,
|
|
@@ -76,12 +82,14 @@ export const definition = {
|
|
|
76
82
|
\`--output\` 是 QUICKLOG「结果:」归档的唯一来源,**必须按此结构给全四项**(逐项一句话,不可用「见前述」替代):
|
|
77
83
|
|
|
78
84
|
\`\`\`
|
|
79
|
-
|
|
85
|
+
需求:一句话语义化短标题(写「改了什么」)
|
|
80
86
|
根因:为什么这样改(若纯新增/配置/样式无根因,写「无,纯新增/纯样式」)
|
|
81
87
|
方案:怎么改的
|
|
82
88
|
结果:验证情况(测试数 / lint / typecheck / 部署状态)
|
|
83
89
|
\`\`\`
|
|
84
90
|
|
|
91
|
+
⚠️ **「需求:」就是 QUICKLOG 条目标题**——CLI 提取该字段、截到首个标点(,。;,;)、超 80 字再截断。写成完整需求长句会被截成语义不完整的状语前半段(如「在 X 时,…」标题只剩「在 X 时」),事后还得手改。一句话说清改了什么(如「登录限流修复——INCR 计数误清」),需求背景放「根因:/方案:」字段。
|
|
92
|
+
|
|
85
93
|
缺任一项,\`--done\` 会被拒并提示缺失字段,补全后重跑即可(不丢进度)。
|
|
86
94
|
|
|
87
95
|
### 📎 文件括注 --file-notes(可选,省事后手改文件行)
|
|
@@ -97,7 +105,7 @@ sillyspec run quick --done --change <id> --output "需求:… 根因:… 方
|
|
|
97
105
|
|
|
98
106
|
### 收尾推荐顺序(模块文档在 --done 前,QUICKLOG 在 --done 后,别记混)
|
|
99
107
|
1. 【--done 前】命中模块 → 改模块文档 → \`git add -- <模块文档>\`(见下方「模块文档同步」)
|
|
100
|
-
2. 【--done】\`sillyspec run quick --done --change <id> --output "四字段" [--file-notes "..."]\`(CLI 自动翻完成 + 勾 task + 落盘 QUICKLOG
|
|
108
|
+
2. 【--done】\`sillyspec run quick --done --change <id> --output "四字段" [--file-notes "..."]\`(CLI 自动翻完成 + 勾 task + 落盘 QUICKLOG 标题/文件/正文;若关联变更的 tasks.md 已全部勾选,CLI 还会自动归档该关联变更到 \`changes/archive/\`,无需手动跑 archive 阶段)
|
|
101
109
|
3. 【--done 后】核对 QUICKLOG 标题(CLI 已从「需求:」自动提取,仅弱标题才手改)→ 若改了再 \`git add\`
|
|
102
110
|
|
|
103
111
|
### 操作
|
|
@@ -126,7 +134,7 @@ sillyspec run quick --done --change <id> --output "需求:… 根因:… 方
|
|
|
126
134
|
### 📝 QUICKLOG 正文核对(--done 之后,按需;多数无需手改)
|
|
127
135
|
CLI 落盘的条目已是结构化产物(非简版骨架):标题从「需求:」自动提取、正文四字段自动分行、文件行用 --file-notes 时为多行括注。--done 后只需核对,多数情况无需手改:
|
|
128
136
|
|
|
129
|
-
1. 标题:CLI
|
|
137
|
+
1. 标题:CLI 已从「需求:」提取(截到首个标点、超 80 字截断);按 step3 模板写成一句语义化短标题则无需手改,写成长句被截断时才手改。
|
|
130
138
|
2. 文件:用了 --file-notes 已是多行括注;没传则回填审计到的实际文件单行,可事后补括注(示例见下)。
|
|
131
139
|
3. 正文:四字段已分行落盘,按需扩写充实(不允许只留一段「结果:」)。
|
|
132
140
|
|
|
@@ -135,7 +143,7 @@ CLI 落盘的条目已是结构化产物(非简版骨架):标题从「需
|
|
|
135
143
|
- backend/app/modules/auth/router.py(登录端点串 check_rate_limit→assert_captcha_if_needed)
|
|
136
144
|
- backend/app/modules/auth/captcha_service.py(新建:限流 INCR + 失败计数 + Pillow 滑块生成/校验)
|
|
137
145
|
|
|
138
|
-
一条 quick = 一条独立 ql 条目;核对只动本次 <quicklog-id> 条目,不追加到旧条目。QUICKLOG 在 .sillyspec/(
|
|
146
|
+
一条 quick = 一条独立 ql 条目;核对只动本次 <quicklog-id> 条目,不追加到旧条目。QUICKLOG 在 .sillyspec/quicklog/(git 跟踪,--done 后需提交),核对不影响 --done 已通过的边界审计。`,
|
|
139
147
|
outputHint: '暂存和记录确认',
|
|
140
148
|
optional: false
|
|
141
149
|
}
|
package/src/stages/scan.js
CHANGED
|
@@ -138,7 +138,7 @@ export const definition = {
|
|
|
138
138
|
e. 等待该项目所有子代理完成后,验证文件是否生成且非空
|
|
139
139
|
f. 该项目完成后,继续下一个项目
|
|
140
140
|
3. 所有项目完成后,运行以下命令检查产物:
|
|
141
|
-
\`
|
|
141
|
+
\`sillyspec workflow check scan-docs --project <project>\`(CLI 子命令,任意项目根可用)
|
|
142
142
|
对每个项目分别执行(将 \`<project>\` 替换为实际项目名)
|
|
143
143
|
4. 如果检查报告有失败项,按报告中的角色和文件重试失败的部分
|
|
144
144
|
|
|
@@ -165,7 +165,7 @@ export const definition = {
|
|
|
165
165
|
- 断点续扫步骤列出的缺失文档列表
|
|
166
166
|
- 环境探测结果摘要(如有 _env-detect.md,直接贴入)
|
|
167
167
|
- **⚠️ 必须强调:子代理必须用 write 工具将文件写入磁盘**
|
|
168
|
-
- **文件标题用中文**(sillyhub 平台解析识别用):frontmatter 必须在文件最前(第 1 行 --- 起),frontmatter 结束的 --- 之后空一行,再写 # 中文名(English)作为标题。严禁把 # 标题放到 frontmatter 之前(否则 frontmatter
|
|
168
|
+
- **文件标题用中文**(sillyhub 平台解析识别用):frontmatter 必须在文件最前(第 1 行 --- 起),frontmatter 结束的 --- 之后空一行,再写 # 中文名(English)作为标题。严禁把 # 标题放到 frontmatter 之前(否则 frontmatter 不在头部、「检查产物完整性」步的 frontmatter 检查会报缺 author/created_at)。各文档标准标题格式:
|
|
169
169
|
- STRUCTURE.md = # 目录结构(Structure)
|
|
170
170
|
- CONVENTIONS.md = # 代码约定(Conventions)
|
|
171
171
|
- ARCHITECTURE.md = # 架构(Architecture)
|
|
@@ -415,7 +415,7 @@ doc_type: module-card
|
|
|
415
415
|
module_id: <module-id>
|
|
416
416
|
---
|
|
417
417
|
|
|
418
|
-
#
|
|
418
|
+
# <中文名>(<module-id>)
|
|
419
419
|
|
|
420
420
|
## 定位
|
|
421
421
|
(负责什么,不负责什么 — 明确边界)
|
|
@@ -437,6 +437,7 @@ module_id: <module-id>
|
|
|
437
437
|
|
|
438
438
|
规则:
|
|
439
439
|
- 不要编造接口或依赖,只写 grep/rg 能搜到的
|
|
440
|
+
- 一级标题格式 \`# <中文名>(<module-id>)\`:中文名从模块职责提炼 2-8 字(如 认证与令牌、变更中心),括号内保留 module-id 原样——sillyhub 平台解析识别用
|
|
440
441
|
- 目标长度:500-1000 字 / 80-150 行
|
|
441
442
|
- 如果模块特别复杂(状态机、多角色交互、复杂领域规则),可以在 modules/details/ 下生成扩展文档(如 details/<module-id>-flow.md),agent 默认不读
|
|
442
443
|
- 不要重复 _module-map.yaml 中的索引信息
|
package/src/stages/verify.js
CHANGED
|
@@ -75,7 +75,7 @@ export const definition = {
|
|
|
75
75
|
### Execute Evidence 传递检查
|
|
76
76
|
7. 检查 verify-required-evidence.json 是否存在(由 execute 阶段 Task Review Gate 写入)
|
|
77
77
|
- 路径:变更目录下的 verify-required-evidence.json
|
|
78
|
-
- 文件 schema:\`{ items: [{ task, verdict, evidence: string[] }] }\`——顶层是 \`items\`,每项的 \`evidence\`
|
|
78
|
+
- 文件 schema:\`{ items: [{ task, verdict, evidence: string[] }] }\`——顶层是 \`items\`,每项的 \`evidence\` 是字符串数组(不是 \`requiredEvidence\` 键)
|
|
79
79
|
- 如果存在 → 逐项读取 \`items\`,对每个 cannot_verify 任务逐条核对其 \`evidence\` 数组是否已满足
|
|
80
80
|
- 每条 evidence 必须在 verify-result.md 中给出明确结论(satisfied / missing / partial)
|
|
81
81
|
- 如果有任何 evidence 为 missing → verify 结论不能为 PASS
|
|
@@ -88,8 +88,11 @@ export const definition = {
|
|
|
88
88
|
10. 读取匹配到的 \`.sillyspec/docs/<project>/modules/<module>.md\`
|
|
89
89
|
11. **检查模块索引可信度**:如果相关模块的 needs_review 为 true,提示"该模块索引可能不可信,需要回看模块卡片或源码"
|
|
90
90
|
|
|
91
|
+
### worktree 基线锚点(CLI 注入)
|
|
92
|
+
{WORKTREE_BASELINE_INFO}
|
|
93
|
+
|
|
91
94
|
### 输出
|
|
92
|
-
文件加载确认清单(含 decisions.md 当前版本/未决项状态、模块文档 +
|
|
95
|
+
文件加载确认清单(含 decisions.md 当前版本/未决项状态、模块文档 + 索引可信度、worktree 基线锚点)`,
|
|
93
96
|
outputHint: '文件确认清单',
|
|
94
97
|
optional: false
|
|
95
98
|
},
|
|
@@ -149,9 +152,8 @@ export const definition = {
|
|
|
149
152
|
### 操作
|
|
150
153
|
1. 检查变更目录下 tasks/ 是否存在
|
|
151
154
|
2. 如果存在:
|
|
152
|
-
- 逐个读取 tasks/task-NN.md
|
|
153
|
-
-
|
|
154
|
-
- 未勾选的项列为不通过
|
|
155
|
+
- 逐个读取 tasks/task-NN.md,对照 frontmatter 的 \`acceptance:\` 列表逐条核验(TaskCard 协议的验收标准在 frontmatter YAML,正文无 checkbox)
|
|
156
|
+
- 每条 acceptance 对照实际实现/测试结果判定满足与否,未满足的项列为不通过
|
|
155
157
|
3. 如果不存在:跳过此步骤
|
|
156
158
|
|
|
157
159
|
### 输出
|
|
@@ -235,13 +237,13 @@ PASS / PASS WITH NOTES / FAIL
|
|
|
235
237
|
(自动检测的 change_risk_profile: doc-only / unit-sufficient / contract-required / integration-critical / deployment-critical;若 design.md frontmatter 有 risk_level 显式声明,以声明为准并在此注明「显式声明 = <等级>」+ 理由)
|
|
236
238
|
|
|
237
239
|
## Runtime Evidence(integration-critical / deployment-critical 必填;自报告,CLI 仅校验字面存在、不独立核验,须真实执行过)
|
|
238
|
-
|
|
239
|
-
-
|
|
240
|
-
-
|
|
241
|
-
-
|
|
242
|
-
-
|
|
243
|
-
-
|
|
244
|
-
-
|
|
240
|
+
按本次变更实际触碰的运行时组件填写(模板是通用形态——长驻进程 / 服务端点 / 生命周期状态机的证据结构;未涉及的行写「不涉及」并注明原因,勿堆砌无关关键词):
|
|
241
|
+
- 长驻进程/服务 启动命令:
|
|
242
|
+
- 服务地址(本变更触碰的端点):
|
|
243
|
+
- 触发核心路径的请求/命令(附关键响应):
|
|
244
|
+
- 进程日志关键片段(证明走了新路径、无降级/回退分支):
|
|
245
|
+
- 生命周期终态断言(本变更涉及的状态机:初始态 → 运行态 → 终态,逐个确认到达):
|
|
246
|
+
- 失败模式排除(针对本变更声明的风险,逐条说明为何未触发):
|
|
245
247
|
|
|
246
248
|
## 代码审查
|
|
247
249
|
(问题列表 + 总体评价)
|
package/src/sync.js
CHANGED
|
@@ -13,6 +13,7 @@ import { join } from 'path';
|
|
|
13
13
|
import { resolvePlatformSpecDir } from './progress.js';
|
|
14
14
|
import { safeGit } from './git-helper.js';
|
|
15
15
|
import { PLATFORM_MANAGED_FILENAME } from './run/shared.js';
|
|
16
|
+
import { syncSpecTree } from './spec-sync.js';
|
|
16
17
|
|
|
17
18
|
// sync 是 best-effort(网络失败只 warn):平台指针失效时不抛,跳过平台、回退本地。
|
|
18
19
|
function safePlatformSpecDir(cwd) {
|
|
@@ -452,6 +453,27 @@ export class SyncManager {
|
|
|
452
453
|
}
|
|
453
454
|
|
|
454
455
|
console.log(`[sync] 已同步变更: ${changeName}`);
|
|
456
|
+
|
|
457
|
+
// 2026-08-16-auto-sync-from-repo:进度上行成功后顺带推四件套文档(best-effort)。
|
|
458
|
+
// 根因:run 流程此前只推进度不推文档(本文件头注释),本地直跑 sillyspec 的产出
|
|
459
|
+
// 文档永远不自动到平台(变更中心"进度到了文档没到")。生产者直连,不依赖 daemon
|
|
460
|
+
// 缓存链路。失败仅 debugLog——文档同步失败不得影响进度上行的返回值与流程;
|
|
461
|
+
// 四件套全缺失时 syncDocuments 内部已有跳过(syncedCount===0 提前返回,不调端点)。
|
|
462
|
+
try {
|
|
463
|
+
await this.syncDocuments(changeName);
|
|
464
|
+
} catch (err) {
|
|
465
|
+
debugLog(`[sync] 文档同步失败(不影响进度): ${changeName}: ${err.message}`);
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
// 2026-08-17-spec-file-incremental-sync:进度上行+文档直推成功后顺带推整个 spec 树
|
|
469
|
+
// (plan.md、tasks/、module-impact.md 等),让变更中心文件树自动更新。
|
|
470
|
+
// 无 daemon 时 CLI 短进程无本地清单缓存,以服务器清单为锚做增量 diff。
|
|
471
|
+
try {
|
|
472
|
+
await syncSpecTree(join(this.cwd, '.sillyspec'), this._getPlatform(), changeName);
|
|
473
|
+
} catch (err) {
|
|
474
|
+
debugLog(`[sync] spec 树增量同步失败(不影响进度): ${changeName}: ${err.message}`);
|
|
475
|
+
}
|
|
476
|
+
|
|
455
477
|
return { synced: 1, errors: [] };
|
|
456
478
|
}
|
|
457
479
|
|
package/src/task-review.js
CHANGED
|
@@ -30,9 +30,9 @@ import { resolveRuntimeRoot } from './run/shared.js'
|
|
|
30
30
|
// 跨仓 task 写 review.json 可用 v2 带 repo 字段,单仓/旧 change 仍用 v1。后续可拆分 stage-review 专用
|
|
31
31
|
// 常量,本期遵循 allowed_paths 约束不扩散到 stage-review.js。
|
|
32
32
|
export const REVIEW_SCHEMA_VERSION = 1
|
|
33
|
-
|
|
33
|
+
const REVIEW_SCHEMA_VERSIONS_ACCEPTED = [1, 2]
|
|
34
34
|
// repo 缺省值(review.repo / task 卡 repo 缺省时统一按 'main' 处理)。design §7.2 / §7.4。
|
|
35
|
-
|
|
35
|
+
const DEFAULT_REPO_KEY = 'main'
|
|
36
36
|
|
|
37
37
|
/**
|
|
38
38
|
* 规范化 review.repo / task 卡 repo 字段为有效 repoKey。
|
|
@@ -53,7 +53,7 @@ export const VALID_VERDICTS = ['pass', 'fail', 'cannot_verify']
|
|
|
53
53
|
* @param {string} planContent - plan.md 文件内容
|
|
54
54
|
* @returns {string[]} task id 列表,如 ['task-01', 'task-02']
|
|
55
55
|
*/
|
|
56
|
-
|
|
56
|
+
function parseTaskIdsFromPlan(planContent) {
|
|
57
57
|
if (!planContent) return []
|
|
58
58
|
const ids = new Set()
|
|
59
59
|
const re = /^\s*[-*]\s*\[[ x]\]\s*task-(\d+)/gim
|
|
@@ -792,7 +792,15 @@ export async function generateTaskReviewDrafts({ changeName, cwd, platformOpts =
|
|
|
792
792
|
} catch {}
|
|
793
793
|
if (!executeRunId) {
|
|
794
794
|
executeRunId = generateExecuteRunId()
|
|
795
|
-
|
|
795
|
+
// D-001#1 fallback 写入点:mkdir execute-runs/<runId>/tasks 先于 marker(不变量:marker 在则目录在)。
|
|
796
|
+
// 保留 fail-open 契约(调用方 catch 降级)——只去静默:失败 console.error 留痕,不 throw
|
|
797
|
+
//(草稿兜底缺了也只是退回 gate 报缺 review.json,不比修复前差)。
|
|
798
|
+
try {
|
|
799
|
+
mkdirSync(join(runtimeRoot, 'execute-runs', executeRunId, 'tasks'), { recursive: true })
|
|
800
|
+
writeFileSync(runIdFile, executeRunId + '\n')
|
|
801
|
+
} catch (e) {
|
|
802
|
+
console.error(`[sillyspec] execute run marker/目录写入失败(降级继续,草稿落盘仍尝试): ${e.message}`)
|
|
803
|
+
}
|
|
796
804
|
}
|
|
797
805
|
|
|
798
806
|
// base..head diff 文件集(worktree-aware;null=git 不可用,[]=无 commit diff)
|