sillyspec 3.29.4 → 3.30.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/.claude/skills/sillyspec-auto/SKILL.md +54 -54
- package/.claude/skills/sillyspec-brainstorm/SKILL.md +2 -2
- package/.claude/skills/sillyspec-commit/SKILL.md +1 -1
- package/.claude/skills/sillyspec-flow/SKILL.md +57 -0
- package/.claude/skills/sillyspec-quick/SKILL.md +18 -116
- package/AGENTS.md +38 -20
- package/SKILL.md +9 -3
- package/assets/command-cards/flow.md +32 -0
- package/assets/command-cards/run-archive.md +26 -0
- package/assets/command-cards/run-brainstorm.md +33 -0
- package/assets/command-cards/run-execute.md +29 -0
- package/assets/command-cards/run-plan.md +27 -0
- package/assets/command-cards/run-quick.md +17 -0
- package/assets/command-cards/run-verify.md +29 -0
- package/assets/command-cards/status.md +27 -0
- package/package.json +2 -1
- package/packages/dashboard/server/executor.js +2 -1
- package/packages/dashboard/server/parser.js +7 -7
- package/r7-test-env/README-R7-TEST.md +93 -0
- package/r7-test-env/demo-project/lib/calc.js +6 -0
- package/r7-test-env/demo-project/package.json +1 -0
- package/r7-test-env/demo-project/test.js +10 -0
- package/r7-test-env/prompt-R7-SMOKE.md +41 -0
- package/r7-test-env/sillyspec-r7.cmd +3 -0
- package/round4/account.mjs +98 -0
- package/round4/b1-analyze-task08.mjs +40 -0
- package/round4/b1-b1-target-validation.md +32 -0
- package/round4/b3a-prompt-audit-result.md +66 -0
- package/round4/b3a-prompt-audit.mjs +89 -0
- package/round4/brief-autocompact.md +34 -0
- package/round4/brief-session-replay.md +50 -0
- package/round4/deep-read-synthesis.md +38 -0
- package/round4/extract-full.mjs +38 -0
- package/round4/extract-timeline.mjs +43 -0
- package/round4/find-sessions.mjs +13 -0
- package/round4/full-r4-l.md +4707 -0
- package/round4/full-r4-o-l.md +4300 -0
- package/round4/full-r4-os.md +1681 -0
- package/round4/full-r4-sf.md +4255 -0
- package/round4/full-r4-sq.md +1734 -0
- package/round4/gate-audit-relay-receipt.md +30 -0
- package/round4/gate-value-audit.md +52 -0
- package/round4/optimization-plan.md +165 -0
- package/round4/prereg.md +67 -0
- package/round4/prompt-R4-L.md +63 -0
- package/round4/prompt-R4-O-L.md +59 -0
- package/round4/prompt-R4-O-S.md +56 -0
- package/round4/prompt-R4-S-F.md +60 -0
- package/round4/prompt-R4-S-Q.md +66 -0
- package/round4/r4-final-report.html +347 -0
- package/round4/r4-os-timeline.txt +297 -0
- package/round4/r4-sf-timeline.txt +738 -0
- package/round4/r4-sq-timeline.txt +322 -0
- package/round4/r4l-timeline.txt +793 -0
- package/round4/r4ol-timeline.txt +729 -0
- package/round4/r4sf-timeline.txt +852 -0
- package/round4/r5-scenario-matrix.md +46 -0
- package/round4/result-S.md +171 -0
- package/round4/result-final.md +46 -0
- package/round4/result-quality.md +63 -0
- package/src/change-list.js +9 -0
- package/src/classify-change.js +6 -0
- package/src/command-cards.js +74 -0
- package/src/commit-guard.js +2 -0
- package/src/config-schema.js +64 -12
- package/src/contract-matrix.js +26 -5
- package/src/db.js +35 -4
- package/src/decision-distill.js +9 -3
- package/src/diagnostic-codes.js +2 -1
- package/src/docs-check.js +1 -1
- package/src/doctor-diagnostics.js +44 -2
- package/src/flow-draft.js +748 -0
- package/src/flow-parity.js +236 -0
- package/src/flow-review.js +184 -0
- package/src/flow.js +1492 -0
- package/src/foreign-declared.js +17 -1
- package/src/fr-index.js +396 -22
- package/src/fs-atomic.js +5 -1
- package/src/git-helper.js +2 -2
- package/src/greenfield-bootstrap.js +98 -0
- package/src/handoff.js +120 -7
- package/src/hunk-attribution.js +191 -0
- package/src/index.js +602 -21
- package/src/init.js +25 -8
- package/src/knowledge-classify.js +47 -1
- package/src/knowledge-digest.js +162 -0
- package/src/knowledge-quicklog.js +246 -0
- package/src/machine-draft.js +128 -0
- package/src/machine-interface.js +194 -7
- package/src/mcp-server.js +1 -0
- package/src/module-impact.js +51 -22
- package/src/module-resolve.js +3 -1
- package/src/preview-progress.js +160 -0
- package/src/progress/change-registry.js +11 -3
- package/src/progress/shared.js +3 -2
- package/src/progress.js +56 -13
- package/src/quicklog.js +44 -6
- package/src/redomain.js +147 -0
- package/src/review-material-pack.js +204 -1
- package/src/run/archive-distill.js +4 -2
- package/src/run/bg-sync.js +67 -0
- package/src/run/command.js +127 -10
- package/src/run/complete-handlers.js +363 -73
- package/src/run/complete.js +356 -74
- package/src/run/gate-snapshot-ledger.js +213 -0
- package/src/run/gate-snapshot.js +260 -38
- package/src/run/gates.js +482 -268
- package/src/run/green-cache.js +123 -0
- package/src/run/junction-rm.js +70 -0
- package/src/run/next.js +14 -0
- package/src/run/prompt.js +185 -55
- package/src/run/quick-audit.js +213 -14
- package/src/run/shared.js +123 -4
- package/src/run/stage.js +182 -216
- package/src/run/test-ledger.js +264 -0
- package/src/run/verify-quality-scan.js +84 -7
- package/src/scope-audit.js +6 -1
- package/src/sentinel-assertions.js +114 -0
- package/src/setup.js +3 -3
- package/src/spec-sync.js +41 -1
- package/src/stage-contract.js +26 -8
- package/src/stage-review-checklist.js +2 -0
- package/src/stage-review.js +7 -2
- package/src/stage-templates.js +157 -0
- package/src/stages/archive.js +2 -0
- package/src/stages/brainstorm.js +24 -106
- package/src/stages/execute.js +222 -85
- package/src/stages/knowledge.js +74 -1
- package/src/stages/plan-postcheck.js +385 -20
- package/src/stages/plan.js +23 -1
- package/src/stages/quick.js +10 -2
- package/src/stages/verify.js +4 -2
- package/src/sync.js +81 -23
- package/src/task-done.js +181 -0
- package/src/task-review.js +8 -55
- package/src/taskcard-frontmatter.js +57 -0
- package/src/test-bindings.js +549 -0
- package/src/timeline.js +218 -0
- package/src/ui-visual.js +175 -0
- package/src/verify-draft.js +214 -0
- package/src/verify-postcheck.js +919 -159
- package/src/verify-probes.js +103 -25
- package/src/watcher.js +1031 -0
- package/src/worktree-apply.js +44 -4
- package/src/worktree-deps.js +22 -8
- package/src/worktree-policy.js +42 -0
- package/src/worktree.js +102 -15
- package/templates/agents-instruction.md +37 -18
package/src/stages/execute.js
CHANGED
|
@@ -7,7 +7,7 @@ import { isPathASupported } from '../dispatch/backends/sillyhub-mcp.js'
|
|
|
7
7
|
import { readMcpConfig } from '../sillyhub-mcp/config.js'
|
|
8
8
|
import { gitQuiet } from '../git-helper.js'
|
|
9
9
|
import { REVIEW_CHECKLISTS } from '../stage-review-checklist.js'
|
|
10
|
-
import { parseRepo } from './plan-postcheck.js'
|
|
10
|
+
import { parseRepo, collectCardBatchFacts, recommendWaveGroups } from './plan-postcheck.js'
|
|
11
11
|
// 模块卡分级解析(token 成本优化 P0a)。注意:module-resolve.js 反向 import 本文件的
|
|
12
12
|
// parseTaskRegistry(注册表同口径)——双向引用均为函数声明 + 调用时解引(无模块初始化期
|
|
13
13
|
// 交叉求值),ESM live binding 下安全;两边都是纯读函数,无副作用序问题。
|
|
@@ -22,6 +22,7 @@ import { collectExecuteChangedFiles, computeDecisionTouches, renderDecisionTouch
|
|
|
22
22
|
// const 的 TDZ 求值(ESM 环实证 2026-09-14)。此处为同格式本地孪生,漂移由
|
|
23
23
|
// test/knowledge-inject.test.mjs 格式等价断言锁定——改 prompt.js 版必同步改此处。
|
|
24
24
|
import { matchKnowledge } from '../knowledge-match.js'
|
|
25
|
+
import { matchQuicklogContext, renderQuicklogSection } from '../knowledge-quicklog.js'
|
|
25
26
|
import { appendKnowledgeHit } from '../knowledge-hits.js'
|
|
26
27
|
|
|
27
28
|
// 机械知识注入限额(与 run/prompt.js KNOWLEDGE_INJECT_MAX_FILES/MAX_LINES 同值孪生,R-02 膨胀控制)
|
|
@@ -46,10 +47,13 @@ function buildWaveKnowledgeSection(changeDir, wave) {
|
|
|
46
47
|
.map(t => String((t && t.name) || '')).filter(Boolean).join(' ')
|
|
47
48
|
if (!query.trim()) return ''
|
|
48
49
|
const result = matchKnowledge(knowledgeDir, query)
|
|
49
|
-
|
|
50
|
+
// quicklog 检索面(知识可见性导线):与 INDEX 知识面独立,INDEX 未命中时照常注入
|
|
51
|
+
const quick = matchQuicklogContext(specBase, query)
|
|
52
|
+
if (!result.matched && quick.hits.length === 0) return ''
|
|
53
|
+
const lines = []
|
|
54
|
+
if (result.matched) {
|
|
50
55
|
// ── 以下与 run/prompt.js renderKnowledgeInjectSection 同格式孪生(含段头文案/截断标记)──
|
|
51
56
|
const ref = e => (e.anchor ? `${e.file}#${e.anchor}` : e.file)
|
|
52
|
-
const lines = []
|
|
53
57
|
lines.push(`### 📚 命中知识(CLI 按 Wave 任务名机械匹配,top-${KNOWLEDGE_INJECT_MAX_FILES}——勿自行重跑 INDEX 匹配)`)
|
|
54
58
|
lines.push(`Status: matched | Entries: ${result.entries.length} | Sources:`)
|
|
55
59
|
for (const e of result.entries) lines.push(` - ${ref(e)}`)
|
|
@@ -80,12 +84,19 @@ function buildWaveKnowledgeSection(changeDir, wave) {
|
|
|
80
84
|
lines.push(body.replace(/\n+$/, ''))
|
|
81
85
|
}
|
|
82
86
|
}
|
|
87
|
+
}
|
|
88
|
+
// quicklog 段与知识段之间空一行(run/prompt.js buildKnowledgeInjection 同款拼接口径)
|
|
89
|
+
if (quick.hits.length > 0) {
|
|
90
|
+
if (lines.length > 0) lines.push('')
|
|
91
|
+
lines.push(renderQuicklogSection(quick.hits, { h3: true }))
|
|
92
|
+
}
|
|
83
93
|
try {
|
|
84
94
|
appendKnowledgeHit(path.join(specBase, '.runtime'), {
|
|
85
95
|
type: 'inject',
|
|
86
96
|
change: path.basename(changeDir),
|
|
87
97
|
query,
|
|
88
|
-
matchedFiles: result.entries.map(
|
|
98
|
+
matchedFiles: result.matched ? result.entries.map(e => (e.anchor ? `${e.file}#${e.anchor}` : e.file)) : [],
|
|
99
|
+
quicklogIds: quick.hits.map(h => h.qlId),
|
|
89
100
|
})
|
|
90
101
|
} catch { /* 遥测 fail-soft(R-04) */ }
|
|
91
102
|
return lines.join('\n') + '\n'
|
|
@@ -427,14 +438,15 @@ const acceptanceSteps = [
|
|
|
427
438
|
### 执行方式(CLI 按变更规模判定,占位符由 run.js 注入)
|
|
428
439
|
tier: {REVIEW_TIER}({REVIEW_TIER_REASON})
|
|
429
440
|
- tier=self:当前 agent 汇总执行(对照 design.md 逐项检查 + 偏差说明)
|
|
441
|
+
> 🚪 **评审豁免(2026-09-26-review-unsupervised-exit)**:若本会话环境**无嵌套派发能力**(无 Agent/Task 类子代理工具)——不产 review.json、**不做自审表演**;改为在变更目录写一行声明文件 review-unsupervised.md(内容含 unsupervised 字样+时间+一句环境说明),Stage Review 门见声明即放行并留痕(声明随归档;Task Review 层已退役)。有派发能力时本豁免不适用(真独立评审仍优待)。
|
|
430
442
|
- tier=independent:必须用 Agent tool 启动一个独立的 QA 子代理(独立上下文,不共享实现者的分析),子代理对照 design.md 逐项检查实现一致性并输出 review.json。review.json 产物契约(CLI Stage Review Gate 将硬校验,schema + 完整示例 + docHash 算法如下,照抄改值;reviewedFiles 除主文档 design.md 外可追加 git diff 涉及的源码文件):
|
|
431
443
|
{PRIOR_REVIEW_FACTS}
|
|
432
|
-
宿主环境无 Agent tool 可用(调用报 Unknown agent / Available agents: none)→ 不卡死:主代理切换为审查者角色自审替代,reviewerNotes 首行记录「降级:环境无子代理可用」,逐条结论附源码锚点(file:line 或 grep/read 证据)补偿独立性。
|
|
433
444
|
{REVIEW_JSON_CONTRACT}
|
|
434
445
|
该 acceptance review 同时覆盖"代码审查"视角(风格/bug/安全/冗余),后续代码审查步骤仅需轻量复审。
|
|
435
446
|
|
|
436
|
-
|
|
447
|
+
**审查范围(Task Review 层已退役 2026-09-26——task 粒度不再有前置 review.json 可分层)**:QA 子代理对照 design×实际 diff 逐 task 核验实现一致性(含改动文件与 allowed_paths 对照),以下三项始终必查(task 粒度无前置评审兜底,全靠本层):
|
|
437
448
|
${REVIEW_CHECKLISTS.execute.map((item, index) => ' ' + (index + 1) + '. ' + item).join('\n')}
|
|
449
|
+
**反例测试核对**(R5 接线,2026-09-26 起随 Task Review 退役自 task 级评审铁律移驻本层):本次变更新增守卫/门控/校验分支时,核对配套反例测试——「守卫不该生效的场景确实不生效」断言(如:特性关闭时零写盘/无 spec 根零目录创建)。坑实证(R5 batch2 W1):写盘分支守卫写成死赋值,三轮污染实证才修对——有反例一轮抓死。
|
|
438
450
|
|
|
439
451
|
**产物唯一化(省重复消耗)**:本步逐项对照结论**只落盘一份**——直接写进 review.json 的 \`checklist\` 数组(item=设计要点/FR/决策,note=实现状态 ✅/⚠️/❌ + 偏差说明 + commit 锚点),reviewerNotes 写汇总。**不要**另写独立的 design-check.md 长文(同一份 design×diff 二次消费;2026-08-22 实测该重复一遍 ≈8 分钟全量重读)。
|
|
440
452
|
**执行纪律(坑 review-subagent-stall,2026-09-15 wp EHS 会话实证)**:brainstorm/plan 阶段已实证的结论(既往 stage-review checklist pass 项、file:line 锚点)直接引用勿重验;必读材料读完即逐条出结论落盘,仅结论存疑时定向补证,禁止循环扩大核验面(连续读文件 10+ 次仍零结论 = 基于已读材料立即收敛)。QA 子代理写操作持续被平台拒绝(session not in running turn 类)→ 重试 ≤3 次即停,完整结论(含 review.json 全文)回传主代理代落盘,reviewerNotes 首行留痕「代落盘:子代理写通道故障」;禁止长时间空转重试。
|
|
@@ -442,7 +454,7 @@ ${REVIEW_CHECKLISTS.execute.map((item, index) => ' ' + (index + 1) + '. ' + ite
|
|
|
442
454
|
**审查经济学三律(2026-09-20 对撞实验驱动,同 brainstorm/plan 审查步)**:①请求预算硬钳——QA 子代理请求上限 **15 次**(材料包+抽查 task 的 diff 文件+全量重审 task 的核心文件),到达即出 verdict;②FAIL 后增量复审——resume 原 QA 子代理只发修复说明+diff 验证阻断项(不全文重审,5 倍差实证),checklist 追加增量复核行;③并行化——tier=independent 时 run_in_background: true 派发后主代理继续下一知识库审阅步骤,回收在即将 --done 代码审查步骤前;④**审查只读纪律**——QA 子代理工具面只读(Read/Grep/只读 Bash),禁止 Edit/Write 被审产物(源码/四件套/review.json 之外的生成物),唯一可写 review.json——发现问题写 checklist/blockers 由主代理/对应 task 修复(2026-09-20 实证 Grill 越权改 design 15 处:独立性破坏+责任链断裂+回合爆炸;③并行化下同文件双写竞态的前置防线)。
|
|
443
455
|
|
|
444
456
|
### 操作(材料包口径——2026-09-19-review-material-cli-wiring)
|
|
445
|
-
1. **评审材料包(基准面)**:CLI 已机械组装注入下方(src/review-material-pack.js assembleStageReviewMaterials('execute-qa'):diff 摘要+design 热区+验收清单)——checklist 逐条只对包作答;包外文件可定向查证但须列明(禁全量扫读);包不足以作答→cannot_verify+requiredEvidence
|
|
457
|
+
1. **评审材料包(基准面)**:CLI 已机械组装注入下方(src/review-material-pack.js assembleStageReviewMaterials('execute-qa'):diff 摘要+design 热区+验收清单)——checklist 逐条只对包作答;包外文件可定向查证但须列明(禁全量扫读);包不足以作答→cannot_verify+requiredEvidence 列缺件。审查范围按上方「审查范围」整段执行;tier=independent 时把整包贴进 QA 子代理派发 prompt:
|
|
446
458
|
{REVIEW_MATERIALS}
|
|
447
459
|
2. 逐一对照 design.md 中的设计要点与实际代码实现
|
|
448
460
|
3. 检查接口签名、数据结构、模块划分是否一致
|
|
@@ -470,7 +482,9 @@ review.json 已落盘(checklist=逐项核验表)+ checklist 摘要与偏差
|
|
|
470
482
|
5. 汇总测试结果
|
|
471
483
|
|
|
472
484
|
### 铁律
|
|
485
|
+
- **写完不回读**——写出的代码/文档落盘即可,verify 门禁机械校验,回读是纯上下文税
|
|
473
486
|
- 长测试/构建/lint 命令必须**前台同步执行**,禁止 run_in_background:true / & / nohup / disown——后台任务易被会话生命周期回收导致中断无果
|
|
487
|
+
- **长输出跑批纪律(token 减负,R9 实证 verify 段 13.8M 输入中 4-6M 是裸输出摄取)**:全量/多文件测试与 lint 的输出重定向落文件(如 \`> .runtime/test-out.log 2>&1\`),上下文只回看尾部摘要与失败段(\`tail -50\` / \`grep -E "FAIL|✗|Error"\`)——长输出全文进上下文是单会话 token 累积大头;CLI 门禁自跑的测试已是摘要输出,不受本条约束
|
|
474
488
|
|
|
475
489
|
### 输出
|
|
476
490
|
测试结果摘要:通过/失败/跳过数量 + 失败项分析`,
|
|
@@ -722,9 +736,11 @@ function resolveTaskRepo(task, changeDir) {
|
|
|
722
736
|
* @param {string} taskFilePath - task-NN.md 绝对路径
|
|
723
737
|
* @param {'base_commit'|'head_commit'} field - 锚点字段名
|
|
724
738
|
* @param {string} commit - commit sha
|
|
725
|
-
* @
|
|
739
|
+
* @param {{keepExisting?: boolean}} [opts] - keepExisting=true 时已有非空锚点不覆写(先写先得,
|
|
740
|
+
* R5 对撞实证 F1:Wave 派发循环重入防 base 锚点漂移,见 round5/r5-collision-ehs-attribution.md P4/N5)
|
|
741
|
+
* @returns {boolean} 是否实际写入(false = 文件不存在 / 写失败 / 值未变 / keepExisting 跳过)
|
|
726
742
|
*/
|
|
727
|
-
export function writeCommitAnchorToTaskCard(taskFilePath, field, commit) {
|
|
743
|
+
export function writeCommitAnchorToTaskCard(taskFilePath, field, commit, opts = {}) {
|
|
728
744
|
if (!taskFilePath || !commit || (field !== 'base_commit' && field !== 'head_commit')) return false
|
|
729
745
|
if (!existsSync(taskFilePath)) return false
|
|
730
746
|
let content
|
|
@@ -745,6 +761,11 @@ export function writeCommitAnchorToTaskCard(taskFilePath, field, commit) {
|
|
|
745
761
|
if (lineRe.test(fm)) {
|
|
746
762
|
const existing = (fm.match(lineRe) || [''])[0]
|
|
747
763
|
if (existing === `${field}: ${commit}`) return false // 值未变,幂等跳过
|
|
764
|
+
// 先写先得(F1):keepExisting 下已有非空锚点不覆写;空值锚点(占位残留)仍允许补值
|
|
765
|
+
if (opts.keepExisting) {
|
|
766
|
+
const existingVal = existing.replace(new RegExp(`^${field}:\\s*`), '').trim()
|
|
767
|
+
if (existingVal !== '') return false
|
|
768
|
+
}
|
|
748
769
|
newFm = fm.replace(lineRe, `${field}: ${commit}`)
|
|
749
770
|
} else {
|
|
750
771
|
// 插入锚点(D-010 协议顺序 repo→base_commit→head_commit):head 跟 base 后,
|
|
@@ -768,15 +789,18 @@ export function writeCommitAnchorToTaskCard(taskFilePath, field, commit) {
|
|
|
768
789
|
}
|
|
769
790
|
}
|
|
770
791
|
|
|
771
|
-
function writeBaseCommitToTaskCard(taskFilePath, baseCommit) {
|
|
772
|
-
|
|
792
|
+
export function writeBaseCommitToTaskCard(taskFilePath, baseCommit) {
|
|
793
|
+
// keepExisting:base 锚点先写先得——Wave 派发循环重入(--done 后重渲染)时不再用实时
|
|
794
|
+
// HEAD 覆写既有锚点(F1);显式刷新走 writeCommitAnchorToTaskCard 不带 opt 的旧通道。
|
|
795
|
+
return writeCommitAnchorToTaskCard(taskFilePath, 'base_commit', baseCommit, { keepExisting: true })
|
|
773
796
|
}
|
|
774
797
|
|
|
775
798
|
/**
|
|
776
799
|
* 跨仓 task head_commit 锡点自动落盘(2026-08-21 agent-手工产出审计项③,D-010 补对称)。
|
|
777
800
|
*
|
|
778
801
|
* base_commit 已在派发时 CLI 落盘,head_commit 此前靠主 agent 按 Wave prompt 指引手跑
|
|
779
|
-
* `rev-parse`
|
|
802
|
+
* `rev-parse` 手写(漏抄/抄错直接炸原 Task Review Gate 真实性校验——该门已随
|
|
803
|
+
* 2026-09-26-task-review-retire 退役,本函数随之无活路径调用方,保留导出作历史回放兼容)。本函数在 execute
|
|
780
804
|
* --done 时机补齐另一半:扫描 task 卡,跨仓 task(repo≠main)缺 head_commit 的,实时
|
|
781
805
|
* `git -C <跨仓仓根> rev-parse HEAD` 幂等写入(**已存在不覆盖**——agent 手写的精确锚点优先)。
|
|
782
806
|
*
|
|
@@ -944,6 +968,32 @@ ${prototypes.map(p => `- \`${path.join(protoRelDir, p)}\``).join('\n')}
|
|
|
944
968
|
} catch {}
|
|
945
969
|
}
|
|
946
970
|
|
|
971
|
+
// ── execution_mode 通道(M4 / FR-04,2026-09-21-r5-efficiency-batch2 task-04,D-004@v1;
|
|
972
|
+
// 2026-09-23 R8 对撞后缺省翻转):plan.md frontmatter `execution_mode: main | dispatch`,
|
|
973
|
+
// **缺省不写/非法值一律 main**(主代理直写)。翻转依据:对撞双实证派发对清晰输入任务是
|
|
974
|
+
// 纯税(R7:A 组直写进码 7′ vs B 组派发 70′;R8:execute 80′(6 子代理+编排)vs 单上下文
|
|
975
|
+
// 同规模任务进码 14′——派发税 3.6×,来源=子代理冷启动上下文重建 887 万 token + 伪并行 +
|
|
976
|
+
// 小任务全额派发开销)。**显式 `execution_mode: dispatch` 才走子代理派发**(判据:任务真可
|
|
977
|
+
// 并行 × 单任务规模大 × 上下文需分片,三者齐备才值得付派发税)。main 时:执行方式段换直写
|
|
978
|
+
// 指引、派发段/子代理工作目录强制段/并发帽段/M3 推荐分组段全抑制;worktree 隔离/写入守卫/
|
|
979
|
+
// verify 门禁与勾选指引全保留(只换执行宿主,不换防线)。
|
|
980
|
+
// best-effort:plan 缺失/读取失败 = 缺省 main。严格小写(大小写漂移视为非法回退缺省)。
|
|
981
|
+
let executionMode = 'main'
|
|
982
|
+
try {
|
|
983
|
+
if (changeDir) {
|
|
984
|
+
const emPlanPath = path.join(changeDir, 'plan.md')
|
|
985
|
+
if (existsSync(emPlanPath)) {
|
|
986
|
+
const emPlanText = memo('plan', () => readFileSync(emPlanPath, 'utf8'))
|
|
987
|
+
const emFm = emPlanText.replace(/\r\n/g, '\n').match(/^---\n([\s\S]*?)\n---/)
|
|
988
|
+
if (emFm) {
|
|
989
|
+
const emVal = emFm[1].match(/^execution_mode:[ \t]*(\S+)[ \t]*(?:#.*)?$/m)
|
|
990
|
+
if (emVal && emVal[1] === 'dispatch') executionMode = 'dispatch'
|
|
991
|
+
}
|
|
992
|
+
}
|
|
993
|
+
}
|
|
994
|
+
} catch { /* execution_mode 解析 best-effort:失败保持缺省 main */ }
|
|
995
|
+
const mainMode = executionMode === 'main'
|
|
996
|
+
|
|
947
997
|
// ── 模块卡分级(P0a):本 Wave per-task 最优卡表,细卡优先——治「根层大卡被每个子代理
|
|
948
998
|
// 整读」(multi-agent-platform backend.md 58KB 实测单卡一项 20 万+ tokens/全流程)。
|
|
949
999
|
// best-effort:解析失败/无 map 不注入(零回归),可经 `sillyspec modules resolve` 手查。
|
|
@@ -1164,7 +1214,7 @@ ${indexLines}
|
|
|
1164
1214
|
**跨仓 worktree task(repo 已建 worktree 隔离)——像主仓 task 一样工作:**
|
|
1165
1215
|
- 子代理在 workdir(跨仓 worktree)内改+commit(git add + git commit 到 worktree 当前分支),**不要碰跨仓主工作副本**。
|
|
1166
1216
|
- apply 阶段 CLI 统一把各跨仓 worktree 的交付 patch 回对应跨仓主工作区(勿手工 apply)。
|
|
1167
|
-
- base 已锚 worktree meta.baseHash
|
|
1217
|
+
- base 已锚 worktree meta.baseHash(创建时快照);task 卡锡点供归档回放/对账解析。` : ''
|
|
1168
1218
|
const crossLegacySection = crossLegacyItems.length > 0 ? `
|
|
1169
1219
|
|
|
1170
1220
|
**派发跨仓 task 前(base 锡点,CLI 已在 prompt 构造时落盘):**
|
|
@@ -1178,15 +1228,35 @@ ${indexLines}
|
|
|
1178
1228
|
|
|
1179
1229
|
${crossLines}${crossWorktreeSection}${crossLegacySection}
|
|
1180
1230
|
|
|
1181
|
-
**回收跨仓 task(
|
|
1182
|
-
- 子代理完成 commit
|
|
1183
|
-
-
|
|
1231
|
+
**回收跨仓 task(Task Review 层已退役,2026-09-26):**
|
|
1232
|
+
- 子代理完成 commit 后,手动勾选 tasks.md 对应 checkbox(完成=实现+测试绿+wt-commit 即勾,同 thin 工作单元语义)。
|
|
1233
|
+
- head 锡点自动落盘已随 enforceReviewJsonGate 退役移除;跨仓 task 卡如需 \`head_commit:\` 锚点(归档回放/对账用)可手写 \`git -C <跨仓仓根> rev-parse HEAD\`(base 锡点仍在派发时由 CLI 落盘)。
|
|
1184
1234
|
`
|
|
1185
1235
|
}
|
|
1186
1236
|
|
|
1237
|
+
// 工作目录「注意」尾段(单仓口径,M4 抽出共享):主代理直写变体与子代理派发变体逐字
|
|
1238
|
+
// 共用——蓝图主仓路径指引 / spec 产物主仓铁律 / Python 导入链陷阱与执行宿主无关。
|
|
1239
|
+
const worktreeNoticeSingle = `### 注意
|
|
1240
|
+
蓝图文件(tasks.md / design.md / proposal.md / requirements.md)在主工作区 {SPEC_ROOT}/changes/<change>/ 下(CLI 已替换为主仓绝对路径),它们可能不在 worktree 中。读取蓝图时使用主工作区路径,不要拼接到 worktree 路径下。
|
|
1241
|
+
|
|
1242
|
+
⚠️ **铁律:spec 流程产物只写主仓 {SPEC_ROOT},绝不写进 worktree 副本**——包括 module-impact.md / knowledge 条目 / 模块卡与 \`<module>.changelog.md\` sidecar。在 worktree 内发现 \`.sillyspec/\` 目录是 checkout 副本,写进去的任何内容都会随 worktree cleanup 整目录蒸发(2026-09-10 实证:模块文档写副本、归档被迫 checkout 补救)。子代理 prompt 中涉及此类产物时,必须原样带上主仓绝对路径。
|
|
1243
|
+
|
|
1244
|
+
⚠️ **Python 导入链陷阱(venv editable install,2026-09-18 实证)**:worktree 内跑任何 \`import <项目包>\` / dump_openapi / 代码生成类命令时,若用的是主仓 venv(junction 链接或裸调主仓 python),其 editable install(\`_editable_impl_*.pth\`)指向**主仓**绝对路径——项目包 import 会静默解析到主仓旧代码,产出旧 schema/旧行为且零报错(看似「改动没生效」)。**跑生成链前设 \`PYTHONPATH=<worktree>/<项目源码根>\`**(PYTHONPATH 优先于 .pth),或用 worktree 自建 venv;\`sillyspec worktree doctor\` 的 editable-install-escape 检查可提前暴露。`
|
|
1245
|
+
|
|
1187
1246
|
// worktreeSection:无 ctx / 无跨仓 task → 旧单值(零回归);有 ctx 含跨仓 task → per-task 多值表
|
|
1188
1247
|
let worktreeSection
|
|
1189
|
-
if (ctx && hasCrossRepoWave && perTaskWorkdirs) {
|
|
1248
|
+
if (mainMode && worktreePath && !(ctx && hasCrossRepoWave && perTaskWorkdirs)) {
|
|
1249
|
+
// M4 main 单仓直写变体:无子代理 workdir JSON(无子代理),注意尾段与派发变体逐字共享。
|
|
1250
|
+
// 跨仓 + main 仍走下方 per-task 表(跨仓 workdir 归属信息与执行宿主无关;M4 判据=清晰
|
|
1251
|
+
// 输入 × 文件正交,跨仓 ctx 场景罕见,本批不扩面)。
|
|
1252
|
+
worktreeSection = `
|
|
1253
|
+
### 工作目录(主代理直写)
|
|
1254
|
+
|
|
1255
|
+
本变更在隔离 worktree 内实现:\`${worktreePath}\`——你(主代理)直接在该 worktree 内改代码并逐任务 commit(\`sillyspec wt-commit --change <change-name> -- <task-files>\`),不经子代理派发。
|
|
1256
|
+
|
|
1257
|
+
${worktreeNoticeSingle}
|
|
1258
|
+
`
|
|
1259
|
+
} else if (ctx && hasCrossRepoWave && perTaskWorkdirs) {
|
|
1190
1260
|
const workdirLines = perTaskWorkdirs.map(i =>
|
|
1191
1261
|
` - task-${i.taskNum} (repo: ${i.repo}) → workdir: "${i.workdir}"`
|
|
1192
1262
|
).join('\n')
|
|
@@ -1232,12 +1302,7 @@ ${workdirLines}
|
|
|
1232
1302
|
}
|
|
1233
1303
|
\`\`\`
|
|
1234
1304
|
|
|
1235
|
-
|
|
1236
|
-
蓝图文件(tasks.md / design.md / proposal.md / requirements.md)在主工作区 {SPEC_ROOT}/changes/<change>/ 下(CLI 已替换为主仓绝对路径),它们可能不在 worktree 中。读取蓝图时使用主工作区路径,不要拼接到 worktree 路径下。
|
|
1237
|
-
|
|
1238
|
-
⚠️ **铁律:spec 流程产物只写主仓 {SPEC_ROOT},绝不写进 worktree 副本**——包括 module-impact.md / knowledge 条目 / 模块卡与 \`<module>.changelog.md\` sidecar。在 worktree 内发现 \`.sillyspec/\` 目录是 checkout 副本,写进去的任何内容都会随 worktree cleanup 整目录蒸发(2026-09-10 实证:模块文档写副本、归档被迫 checkout 补救)。子代理 prompt 中涉及此类产物时,必须原样带上主仓绝对路径。
|
|
1239
|
-
|
|
1240
|
-
⚠️ **Python 导入链陷阱(venv editable install,2026-09-18 实证)**:worktree 内跑任何 \`import <项目包>\` / dump_openapi / 代码生成类命令时,若用的是主仓 venv(junction 链接或裸调主仓 python),其 editable install(\`_editable_impl_*.pth\`)指向**主仓**绝对路径——项目包 import 会静默解析到主仓旧代码,产出旧 schema/旧行为且零报错(看似「改动没生效」)。**跑生成链前设 \`PYTHONPATH=<worktree>/<项目源码根>\`**(PYTHONPATH 优先于 .pth),或用 worktree 自建 venv;\`sillyspec worktree doctor\` 的 editable-install-escape 检查可提前暴露。
|
|
1305
|
+
${worktreeNoticeSingle}
|
|
1241
1306
|
`
|
|
1242
1307
|
: ''
|
|
1243
1308
|
}
|
|
@@ -1250,7 +1315,7 @@ ${workdirLines}
|
|
|
1250
1315
|
// (无 worktree 无谓派发指令)。测试/调用方可经 options.dispatchMode 覆盖,避免 env 污染。
|
|
1251
1316
|
const dispatchMode = options.dispatchMode || getDispatchMode()
|
|
1252
1317
|
let dispatchSection = ''
|
|
1253
|
-
if (worktreePath && dispatchMode === 'sillyhub') {
|
|
1318
|
+
if (!mainMode && worktreePath && dispatchMode === 'sillyhub') {
|
|
1254
1319
|
// 路径A 落地后:注入完整 SillyHub 派发指令(renderDispatchInstruction 已含 Local 兜底全文)
|
|
1255
1320
|
const contract = {
|
|
1256
1321
|
brief: '本 Wave 任务(见上方任务摘要与 tasks/task-XX.md)',
|
|
@@ -1262,12 +1327,76 @@ ${workdirLines}
|
|
|
1262
1327
|
}
|
|
1263
1328
|
const { instruction } = renderDispatchInstruction(contract, { available: true })
|
|
1264
1329
|
dispatchSection = `\n### 派发后端:SillyHub MCP(探测可用,一 Wave 一 mission)\n\n本次 Wave 派发经 SillyHub MCP。按以下派发指令执行(含 create_mission / dispatch_worker / 轮询 list_workers / 超时 kill lease 防双写 / 回收 + Local 兜底):\n\n${instruction}\n`
|
|
1265
|
-
} else if (worktreePath && dispatchMode === 'local-fallback') {
|
|
1330
|
+
} else if (!mainMode && worktreePath && dispatchMode === 'local-fallback') {
|
|
1266
1331
|
// 有 MCP 配置但路径A 未落地:加短提示,派发仍走 Local(与默认行为一致)
|
|
1267
1332
|
dispatchSection = `\n### 派发后端提示:SillyHub MCP 已配置但路径A 未落地\n\n检测到 local.yaml mcp 段或 env 配置,但 SillyHub \`dispatch_worker\` 尚不支持 \`worktree_path\`(路径A 跨仓未落地)。本次派发走 Local(本机 Agent tool),与默认行为一致——上方「执行方式」与「工作目录」段适用。\n`
|
|
1268
1333
|
}
|
|
1269
1334
|
// dispatchMode === 'local'(无配置)或 worktreePath 为空 → dispatchSection = '' → 输出与改前字节一致(零回归)
|
|
1270
1335
|
|
|
1336
|
+
// ── 任务材料包引用行(2026-09-21-r5-efficiency-batch1 task-02,B-④):options.materials 为
|
|
1337
|
+
// taskId → 材料包路径映射(调用方用 assembleExecuteTaskMaterials 落盘后传入;execute 主链
|
|
1338
|
+
// 暂不主动生成——接线最小化,生成调用留待后续)。无映射或本 Wave 无命中 → ''(零回归:
|
|
1339
|
+
// prompt 与改前字节一致)。编号:全局硬约束恒为第 9 条(既有钉),材料包无冲突时占 9、
|
|
1340
|
+
// 并存时顺延 10——两条路径(有/无 gc)编号均单调。
|
|
1341
|
+
// (插在派发段之后:docs/sillyspec/platform-interface-map.md 锚 execute.js:1246 关键词窗口
|
|
1342
|
+
// 止于上方 dispatchMode 判定行——本块置于其后不推移该锚。)
|
|
1343
|
+
const materialsMap = options.materials || {}
|
|
1344
|
+
const waveMaterialHits = wave.tasks.map((t, ti) => {
|
|
1345
|
+
const taskNum = String(t.index || (ti + 1)).padStart(2, '0')
|
|
1346
|
+
const p = materialsMap[`task-${taskNum}`]
|
|
1347
|
+
return p ? `task-${taskNum} → \`${String(p)}\`` : null
|
|
1348
|
+
}).filter(Boolean)
|
|
1349
|
+
let materialsPoint = ''
|
|
1350
|
+
if (waveMaterialHits.length > 0) {
|
|
1351
|
+
const itemNo = globalConstraintsBlock ? '10.' : '9.'
|
|
1352
|
+
materialsPoint = `\n${itemNo} **任务材料包(CLI 预装配,稳定段先行)**:${waveMaterialHits.join(';')}——先读材料包,按锚点回源核对;材料包只摘不译,冲突以源文件为准`
|
|
1353
|
+
}
|
|
1354
|
+
|
|
1355
|
+
// ── 推荐分组(M3 / FR-03,2026-09-21-r5-efficiency-batch2 task-03,D-003@v1):CLI 按
|
|
1356
|
+
// 第 1 批三条件机械预计算本 Wave 推荐分组,注入派发段——派发交接单位从 task 升组,摊平
|
|
1357
|
+
// per-task 子代理的重复上下文重建(P13/GSD)。两道零回归门:
|
|
1358
|
+
// ① dispatchMode === 'sillyhub' 零注入(一 Wave 一 mission,分组语义不适用——与下方
|
|
1359
|
+
// 「SillyHub 派发互斥」行同口径;local / local-fallback 实际派发走 Local 照注入);
|
|
1360
|
+
// ② recommendWaveGroups 无 ≥2 组(无可并/卡缺失不可判/护栏预算 0)→ recommendBlock=''
|
|
1361
|
+
// → 模板插入位相邻字节形态不变,输出与改前逐字节一致。
|
|
1362
|
+
// ③ execution_mode=main 零注入(M4:无派发即无分组语义,Grill P3-2——与 SillyHub 互斥
|
|
1363
|
+
// 同款模式互斥口径)。
|
|
1364
|
+
// facts 逐卡 memoMap(一次 buildExecuteSteps 期间卡内容不变,跨 Wave 共享);分组本体与
|
|
1365
|
+
// plan-postcheck checkBatchAdvisory 判定①共用 recommendWaveGroups 纯函数(无二源)。──
|
|
1366
|
+
let recommendBlock = ''
|
|
1367
|
+
if (changeDir && dispatchMode !== 'sillyhub' && !mainMode) {
|
|
1368
|
+
try {
|
|
1369
|
+
const groupTasks = wave.tasks.map((t, ti) => {
|
|
1370
|
+
const id = `task-${String(t.index || (ti + 1)).padStart(2, '0')}`
|
|
1371
|
+
const facts = memoMap('batchFacts', id, () => {
|
|
1372
|
+
const cardPath = path.join(changeDir, 'tasks', `${id}.md`)
|
|
1373
|
+
if (!existsSync(cardPath)) return null
|
|
1374
|
+
return collectCardBatchFacts(readFileSync(cardPath, 'utf8'))
|
|
1375
|
+
})
|
|
1376
|
+
return {
|
|
1377
|
+
id,
|
|
1378
|
+
files: facts && facts.fileSet ? [...facts.fileSet] : null,
|
|
1379
|
+
deps: facts ? facts.deps : [],
|
|
1380
|
+
expectsProviders: facts ? facts.expectsProviders : [],
|
|
1381
|
+
}
|
|
1382
|
+
})
|
|
1383
|
+
const recGroups = recommendWaveGroups(groupTasks)
|
|
1384
|
+
if (recGroups.some(g => g.length >= 2)) {
|
|
1385
|
+
const display = recGroups.map(g => `[${g.join(',')}]`).join(' / ')
|
|
1386
|
+
recommendBlock = `\n**推荐分组(CLI 已按上述三条件预计算)**:${display}\n按推荐分组派发与回收(组内逐 task 审查照旧、勾选手动,主代理按组一次回收);可偏离推荐分组,但偏离须在 Wave 完成摘要中披露理由——机械建议不夺裁决权。\n`
|
|
1387
|
+
}
|
|
1388
|
+
} catch { /* 推荐分组 fail-open:计算失败零注入,不阻断 Wave prompt 组装 */ }
|
|
1389
|
+
}
|
|
1390
|
+
|
|
1391
|
+
// ── 派发契约(2026-09-21-r5-efficiency-batch1 task-03,FR-03 / B-⑥ + C-1):轮数纪律三行 +
|
|
1392
|
+
// 子代理返回契约,无条件常驻注入(与要点 1-8 同级,不随材料包/硬约束缺位消失)。编号接在
|
|
1393
|
+
// 材料包之后保持单调:全局硬约束恒 9(既有钉)→ 材料包在位占 10 → 本两条顺延;缺位前移,
|
|
1394
|
+
// 四种组合(gc×材料包在否)编号均单调不撞车。轮数纪律打「轮数 × 上下文」的轮数乘数——验靶
|
|
1395
|
+
// 实证新鲜内容仅 3.2%,逐处小 Edit / TodoWrite 连发 / 逐文件跑测是轮数膨胀三大源头。──
|
|
1396
|
+
const roundDisciplineNo = 9 + (globalConstraintsBlock ? 1 : 0) + (waveMaterialHits.length > 0 ? 1 : 0)
|
|
1397
|
+
const roundDisciplinePoint = `\n${roundDisciplineNo}. **轮数纪律(省 token 铁律)**:相邻同文件的多个改动合并为一次 Edit 批量提交(不逐处小改);TodoWrite 只在阶段边界用、实现中途不连发;测试验证合并单次 Bash 跑完(多查一条命令串联),不逐文件逐轮跑`
|
|
1398
|
+
const returnContractPoint = `\n${roundDisciplineNo + 1}. **返回契约**:子代理最终返回 ≤25 行结构化摘要——verdict(done|blocked)/ 触碰文件数 / 测试一行结果 / 偏差说明;实现细节不贴正文,落盘文件按需 Read`
|
|
1399
|
+
|
|
1271
1400
|
// ── 隐式 Wave 串行化(2026-09-17-feedback-hardening D-003@v1):plan.md 无显式 Wave 划分时
|
|
1272
1401
|
// parseWavesFromPlan 合成的 implicit Wave 未做过文件正交/契约链核查,并行不安全——prompt 层
|
|
1273
1402
|
// 下发串行调度指令(头标注 + 角色清单 + 调度要求铁律 + batch 条件 2 括注收敛,防同 prompt
|
|
@@ -1281,14 +1410,28 @@ ${workdirLines}
|
|
|
1281
1410
|
: `- 组内任意两个 task 之间无 provides / expects_from 契约链(契约 task 禁止同批——串行实现会读到半成品,契约 task 由独立子代理并行处理或落在不同 Wave)`
|
|
1282
1411
|
const roleItem1 = implicit
|
|
1283
1412
|
? `1. 为每个任务启动一个子代理(Agent tool),或按上述三条件把多个任务合并为一个 batch 子代理,逐个完成(串行——启动一个、等它完成并审查后再启动下一个,见「调度要求」串行铁律)`
|
|
1284
|
-
: `1. 为每个任务启动一个子代理(Agent tool),或按上述三条件把多个任务合并为一个 batch 子代理,同 Wave
|
|
1285
|
-
const scheduleItem1 =
|
|
1286
|
-
? `1.
|
|
1287
|
-
:
|
|
1413
|
+
: `1. 为每个任务启动一个子代理(Agent tool),或按上述三条件把多个任务合并为一个 batch 子代理,同 Wave 内可并行——但同时在飞子代理 ≤3(见「调度要求」并发帽,超出的 batch 排队错峰)`
|
|
1414
|
+
const scheduleItem1 = mainMode
|
|
1415
|
+
? `1. **主代理直写纪律(execution_mode: main)**:逐任务串行闭环(读 task 卡 → worktree 内实现 → 跑该 task verify 命令 → wt-commit 逐任务提交 → 手动勾 tasks.md checkbox → 下一任务),不派子代理、不并行——直写模式串行是本通道契约;防线(worktree 隔离/写入守卫/verify 门禁)与模式无关恒在。`
|
|
1416
|
+
: implicit
|
|
1417
|
+
? `1. **隐式 Wave 串行铁律**:本 Wave 由 plan.md 无显式 Wave 划分时合成——任务一律逐个完成(单子代理串行逐个实现,或逐个启动子代理并等待其完成再启动下一个),**禁止并行启动**(未做过文件正交/契约链核查,并行不安全;需要并行收益请在 plan.md 显式划分 Wave)。`
|
|
1418
|
+
: `1. **同一 Wave 的多个子代理(独立或 batch)并行启动、batch 内部串行,且同时在飞 ≤3**(batch 分组仅按文件正交 / 无契约链判定,不改变 Wave 依赖语义——Wave 定义=无依赖可并行;有依赖应在 plan.md 的不同 Wave 中。并发帽 3:Wave 内子代理超 3 个时分两批错峰——首批完成后回收槽位再派第二批;4 路齐发实证触发平台额度/限流耗尽,整 Wave 中断 17 分钟由用户手动恢复,得不偿失)。`
|
|
1288
1419
|
|
|
1289
|
-
|
|
1420
|
+
// ── M4 模式段变体(2026-09-21-r5-efficiency-batch2 task-04):dispatch 分支为既有模板
|
|
1421
|
+
// 逐字移植(缺省/非法回退 dispatch → 输出与改前逐字节一致,零回归由改动前后黄金快照
|
|
1422
|
+
// diff + 既有钉测试双保证);main 分支为直写指引——派发段/子代理工作目录强制段/并发帽
|
|
1423
|
+
// 段/M3 分组段全抑制,防线与勾选指引全保留(只换执行宿主,不换防线)。──
|
|
1424
|
+
const mainExecSection = `## 执行方式
|
|
1425
|
+
|
|
1426
|
+
**主代理直写(execution_mode: main)——你直接实现,不派子代理。**
|
|
1427
|
+
|
|
1428
|
+
逐任务闭环(串行,完成一个再下一个):读 task 卡(路径见下方任务摘要)→ 在 worktree 内实现(TDD:先写测试再实现)→ 跑该 task 卡 verify 命令 → \`sillyspec wt-commit --change <change-name> -- <task-files>\` 逐任务提交 → 手动勾选 tasks.md 对应 checkbox(完成=实现+测试绿+wt-commit 即勾,2026-09-26 Task Review 退役起勾选回归 agent 手动,同 thin 工作单元语义)→ 下一任务。
|
|
1429
|
+
|
|
1430
|
+
worktree 隔离 / 写入守卫(allowed_paths)/ verify 门禁全部保留——只换执行宿主,不换任何防线。
|
|
1290
1431
|
|
|
1291
|
-
|
|
1432
|
+
**评审派发保留(2026-09-23 R9 教训)**:直写只免「实现」的子代理派发——Stage Review(tier=independent)仍须派独立子代理执行(harness 有 Agent/Task 类工具必派:小上下文干重读活,省主会话 token 且保审查实效,R8 基线实证审查抓过真缺口);确实无派发工具的环境按上方评审豁免处理(写 review-unsupervised.md 声明,不自审表演)。`
|
|
1433
|
+
|
|
1434
|
+
const dispatchExecSection = `## 执行方式
|
|
1292
1435
|
|
|
1293
1436
|
**默认每个任务由独立子代理执行,你不要自己写代码。**
|
|
1294
1437
|
|
|
@@ -1296,24 +1439,38 @@ ${workdirLines}
|
|
|
1296
1439
|
- 组内任意两个 task 的 allowed_paths 无交集(文件正交)
|
|
1297
1440
|
${batchCond2}
|
|
1298
1441
|
- 组大小不超过 3 个 task
|
|
1299
|
-
|
|
1442
|
+
${recommendBlock}
|
|
1300
1443
|
任一条件不满足,该 task 走独立子代理(默认形态);拿不准就不合并。无论独立还是 batch,实现一律由子代理完成,你不要自己写代码。
|
|
1301
1444
|
|
|
1302
1445
|
你的角色是调度者 + 审查者(batch 只合并实现、不合并审查):
|
|
1303
1446
|
${roleItem1}
|
|
1304
|
-
2. 子代理完成后审查结果——batch 子代理只做实现与自验,task
|
|
1305
|
-
3.
|
|
1306
|
-
4.
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
**SillyHub 派发互斥**:SillyHub 派发模式下按派发段执行(一 Wave 一 mission),不按 batch 分组;batch 分组指导仅适用于本地 Agent tool 派发。
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1447
|
+
2. 子代理完成后审查结果——batch 子代理只做实现与自验,task 审查与 checkbox 勾选仍归你(主 agent),在子代理返回后逐 task 进行;审查 batch 报告时逐 task 对照 allowed_paths 检查改动文件清单有无越权
|
|
1448
|
+
3. 完成一个任务(子代理返回+审查通过+测试绿+wt-commit)即手动勾选 tasks.md 对应 checkbox(2026-09-26 Task Review 退役起勾选回归 agent 手动;勾选是批量完成与中断续跑的事实锚)
|
|
1449
|
+
4. 记录改动文件和测试结果`
|
|
1450
|
+
|
|
1451
|
+
// main 模式无派发——SillyHub 互斥行随派发段一同抑制(无 batch 分组语义可互斥)
|
|
1452
|
+
const sillyhubMutexLine = mainMode ? '' : '**SillyHub 派发互斥**:SillyHub 派发模式下按派发段执行(一 Wave 一 mission),不按 batch 分组;batch 分组指导仅适用于本地 Agent tool 派发。'
|
|
1453
|
+
|
|
1454
|
+
// 任务摘要引导行 + 要点块的模式变体(M4):dispatch 分支逐字原文;main 分支去子代理
|
|
1455
|
+
// 语境(无 prompt 组装、无 batch 协议),铁律/模块卡/测试设计/增量落盘/边界铁律五条保留。
|
|
1456
|
+
const summaryIntroLine = mainMode
|
|
1457
|
+
? '逐任务实现前先读该 task 卡(目标/边界/验收都在卡上,按需再读 design 对应节):'
|
|
1458
|
+
: '为每个任务启动子代理时,**只需告知任务目标和蓝图文件路径,让子代理按需读取**:'
|
|
1459
|
+
|
|
1460
|
+
const modulePointMain = moduleSection
|
|
1461
|
+
? '**模块卡分级**:按上方「模块卡分级」表读该 task 命中的卡(细卡整读;粗大卡只读「契约摘要/注意事项/定位」节)——勿按目录漫游或整读根层大卡'
|
|
1462
|
+
: '如存在模块文档({SPEC_ROOT}/docs/*/modules/),按需读取涉及模块的 <module>.md 参考接口约定和数据流——读主仓路径(CLI 已替换为绝对路径),不要读 worktree 副本'
|
|
1463
|
+
|
|
1464
|
+
const mainPointsSection = `实现要点:
|
|
1465
|
+
1. 编码铁律:先读后写、TDD、不编造方法、只做蓝图里写的事、遵守边界处理规则、不超出 allowed_paths
|
|
1466
|
+
2. ${modulePointMain}
|
|
1467
|
+
3. 任务含测试代码时,按下方「测试用例设计」整节设计测试用例
|
|
1468
|
+
4. **增量落盘与中断接手指引**:每完成一个可见产出(代码/测试/文档),立即写盘并执行一次最小验证(如语法检查、单跑相关测试)。工作过程中如被 429/API 配额/会话中断,应在最终回复里输出「已完成清单」(含文件路径、测试命令、当前卡点),不要只输出结论——续跑时依据磁盘产物和该清单判断哪些已完成,避免重做已落盘的工作
|
|
1469
|
+
**中间验证定向优先:node --test <本任务测试文件>;全量 npm test 留 task 收口与 verify --done**
|
|
1470
|
+
**输出纪律**:测试/安装/构建类长输出命令一律重定向落盘(\`> {SPEC_ROOT}/.runtime/logs/<名>.log 2>&1\`,CLI 已替换为绝对路径)后 \`tail -n 50\` 回看结论、按需 grep——勿把全量输出读进上下文(长输出逐轮回放是 execute 段 token 膨胀主源,R12 对撞实证均轮 134K vs 其他 181-234K)
|
|
1471
|
+
5. **任务边界铁律**:严格只实现本 task 的 \`allowed_paths\` 内文件;若 design.md/plan.md 明确指定了接口/回调/钩子接入位置,必须逐字遵守;不允许顺手实现其他 task 的内容。发现必须改其他 task 文件才能继续,先停下回到 plan 层重分任务边界,禁止私自越界`
|
|
1472
|
+
|
|
1473
|
+
const subagentPointsSection = `子代理 prompt 要点:
|
|
1317
1474
|
1. 任务目标(简短描述)
|
|
1318
1475
|
2. 蓝图文件路径(让子代理自行读取详情)
|
|
1319
1476
|
3. 编码铁律:先读后写、TDD、不编造方法、只做蓝图里写的事、遵守边界处理规则、不超出 allowed_paths
|
|
@@ -1322,8 +1479,22 @@ ${taskSummary}
|
|
|
1322
1479
|
6. **增量落盘与中断接手指引**:每完成一个可见产出(代码/测试/文档),立即写盘并执行一次最小验证(如语法检查、单跑相关测试)。工作过程中如被 429/API 配额/会话中断,应在最终回复里输出「已完成清单」(含文件路径、测试命令、当前卡点),不要只输出结论——主代理会依据磁盘产物和该清单判断哪些部分已完成,哪些需接手补做,避免重做已落盘的工作
|
|
1323
1480
|
**中间验证定向优先:node --test <本任务测试文件>;全量 npm test 留 task 收口与 verify --done**(测选路引导,2026-09-18-preflight-slimming task-04:中间验证只跑本任务相关测试文件,全量套件留给 task 收口与 verify --done,防每步全量测试拖慢执行;与 taskcard-rules.md verify 段同款文案)
|
|
1324
1481
|
7. **任务边界铁律**:严格只实现本 task 的 \`allowed_paths\` 内文件;若 design.md/plan.md 明确指定了接口/回调/钩子接入位置,必须逐字遵守;不允许顺手实现其他 task 的内容(如 task-01 不要把 task-02 的接入也做了)。如发现必须改其他 task 文件才能继续,先回到主代理由主代理决定是否重分 Wave 或调整 plan,禁止子代理私自越界
|
|
1325
|
-
8. **batch 子代理协议**(仅当按「执行方式」节条件合并 batch 时附加进该子代理 prompt):按 batch 内 task 顺序逐个完成实现闭环——读取 tasks/task-N.md → 实现 → 跑该 task 的 verify 命令 → 记录该 task 报告(改动文件清单 / verify 结果 / 卡点)→ 才开始下一个 task;最终回复输出逐 task
|
|
1326
|
-
|
|
1482
|
+
8. **batch 子代理协议**(仅当按「执行方式」节条件合并 batch 时附加进该子代理 prompt):按 batch 内 task 顺序逐个完成实现闭环——读取 tasks/task-N.md → 实现 → 跑该 task 的 verify 命令 → 记录该 task 报告(改动文件清单 / verify 结果 / 卡点)→ 才开始下一个 task;最终回复输出逐 task 报告清单。禁止勾选 tasks.md checkbox——task 审查与勾选归主 agent,在子代理返回后逐 task 进行。越权即停:发现必须改 batch 内其他 task 或任何 batch 外 task 的 allowed_paths 文件 → 立即停止本 task 及后续,报告冲突文件与卡点,回主 agent 裁决(重分 Wave / 调整 plan / 回退独立子代理)。第 7 条任务边界铁律在 batch 语境下的「本 task」= 当前正在实现的 task`
|
|
1483
|
+
|
|
1484
|
+
return `${waveHeader}
|
|
1485
|
+
|
|
1486
|
+
${mainMode ? mainExecSection : dispatchExecSection}
|
|
1487
|
+
|
|
1488
|
+
${worktreeSection}${crossRepoCommitSection}${dispatchSection}
|
|
1489
|
+
${sillyhubMutexLine}
|
|
1490
|
+
${moduleSection}
|
|
1491
|
+
### 任务摘要(按需读取完整蓝图)
|
|
1492
|
+
${summaryIntroLine}
|
|
1493
|
+
|
|
1494
|
+
${taskSummary}
|
|
1495
|
+
|
|
1496
|
+
${mainMode ? mainPointsSection : subagentPointsSection}
|
|
1497
|
+
${globalConstraintsBlock}${materialsPoint}${roundDisciplinePoint}${returnContractPoint}
|
|
1327
1498
|
{{include: testcase-design}}
|
|
1328
1499
|
|
|
1329
1500
|
${designHotzone}${decisionTouchSection}${waveKnowledgeSection}
|
|
@@ -1359,49 +1530,15 @@ ${scheduleItem1}
|
|
|
1359
1530
|
- 用户明确要求编译时
|
|
1360
1531
|
4. 每个任务完成后:
|
|
1361
1532
|
- **使用 \`sillyspec wt-commit --change <change-name> -- <task-files>\` 串行提交**(坑 wt-parallel-commit-race:同一 Wave 多子代理共享 worktree,裸 \`git add -A\` 互卷 WIP / 撞 index.lock;\`git add -A\` 还会把并行子代理未提交的半成品一并暂存——**禁 \`git add -A\`**;sillyspec wt-commit 自动处理分支切换与序列化,无需手动 git 操作;未 commit 的新文件不在 base commit 也不在 index,apply --3way 报 "does not exist in index")
|
|
1362
|
-
- commit 后 base..HEAD diff 完整、apply
|
|
1363
|
-
-
|
|
1364
|
-
-
|
|
1533
|
+
- commit 后 base..HEAD diff 完整、apply 顺畅(apply 与对账都吃真实提交面)
|
|
1534
|
+
- **手动勾选 tasks.md 对应 checkbox**(完成=实现+测试绿+wt-commit 即勾;勾选是批量完成与中断续跑的事实锚)
|
|
1535
|
+
- **任务边界上报(每任务一次,主仓根目录)**:勾选 checkbox 后跑一次 \`sillyspec platform sync --change <change-name>\`——以任务粒度把「最后信号」(last_pushed_at)与 tasks.md 勾选状态推上平台(变更中心「进行中」可见性);未连接平台时该命令静默跳过,无需先检查连接状态
|
|
1365
1536
|
- **既跑 lint check 也跑 formatter**:凡变更涉及的源码跑项目的 lint 检查 **和** 格式化(如 \`ruff format\` / \`prettier --write\`),不要只跑 check——只 check 不 format 会把格式问题留到 commit 时被 pre-commit hook 拦截(worktree 内二进制可能缺失,先 \`which <bin>\` 确认,缺则 \`uv tool install\` / \`uv sync\`)
|
|
1366
1537
|
- 记录改动文件和测试结果
|
|
1367
1538
|
5. 遇到 BLOCKED → 记录原因,选择:重试/跳过/停止
|
|
1368
1539
|
|
|
1369
|
-
### Task Review Gate
|
|
1370
|
-
|
|
1371
|
-
每个子代理完成后,你必须创建 task review(review write 落盘后 CLI 自动勾选 checkbox——P2-f 起勾选唯一写入者是 CLI)。
|
|
1372
|
-
|
|
1373
|
-
**操作步骤:**
|
|
1374
|
-
1. 读取当前 task 的 git diff(从 task 开始到完成的变更)
|
|
1375
|
-
2. 对照 plan.md 中该 task 的描述和 tasks/task-XX.md(如果存在)检查实现是否符合要求
|
|
1376
|
-
3. 写入 review.json 文件
|
|
1377
|
-
4. **禁止手动勾选 tasks.md 的 checkbox**(P2-f task 真源归一:review.json verdict 是唯一真源,tasks.md 勾选是它的显示态,唯一写入者是 CLI——review write 落盘即勾、execute --done 时 autoCheckPlanFromReviews 兜底;agent 手勾与机器勾的漂移面就此退役)
|
|
1378
|
-
|
|
1379
|
-
**review.json 路径:**
|
|
1380
|
-
|
|
1381
|
-
task-XX 对应:{SPEC_ROOT}/.runtime/execute-runs/{EXECUTE_RUN_ID}/tasks/task-XX/review.json
|
|
1382
|
-
|
|
1383
|
-
本 execute run 的固定 ID 是:{EXECUTE_RUN_ID}
|
|
1384
|
-
**所有 task 的 review.json 必须使用这个 ID,不要自行创建新目录。**
|
|
1385
|
-
|
|
1386
|
-
**review.json 必填字段:**
|
|
1387
|
-
|
|
1388
|
-
{ "name_zh": "任务评审", "schemaVersion": {REVIEW_SCHEMA_VERSION}, "task": "task-XX", "base": "<git-base-commit>", "head": "<git-head-commit>",
|
|
1389
|
-
"changedFiles": ["src/foo.js"], "specVerdict": "pass|fail|cannot_verify",
|
|
1390
|
-
"qualityVerdict": "pass|fail|cannot_verify", "reviewerNotes": "评审说明",
|
|
1391
|
-
"requiredEvidence": [] }
|
|
1392
|
-
|
|
1393
|
-
**base/head 两种取法(按提交模式选其一):**
|
|
1394
|
-
- **per-task commit 模式**(默认,子代理每 task 一提交):base=本 task 开始前的 commit,head=本 task 的 commit——base..head 天然就是本 task 的 diff,无需 diffPaths。
|
|
1395
|
-
- **统一 commit 模式**(主代理统一实现/收尾一次性 commit,全部 task 一个提交):base=基线 commit(worktree meta 的 baseHash),head=统一 commit,并**必填 \`diffPaths\`(本 task 卡的 allowed_paths 原样数组)**——评审与 CLI 校验的 diff 都是 \`git diff base..head -- diffPaths\` 的路径限定切片,任务边界由 diffPaths 机器可验,不再只靠 changedFiles 归属说明。changedFiles 填切片内实际改动的文件。
|
|
1396
|
-
|
|
1397
|
-
**评审铁律:**
|
|
1398
|
-
- 不信任 implementer 自报结果,对照 diff 和 task brief 验证
|
|
1399
|
-
- 只看当前 task 的 diff(统一 commit 模式=路径限定切片),不做全仓库漫游审查
|
|
1400
|
-
- \`cannot_verify\` 只在确实无法验证且有待补充证据时使用,且 requiredEvidence 必须非空
|
|
1401
|
-
- \`sillyspec run execute --done\` 会校验所有 task 的 review.json,缺失或 fail 会阻断完成
|
|
1402
|
-
|
|
1403
1540
|
### module-impact.md 更新(主代理在本 Wave 所有 task 完成后汇总)
|
|
1404
|
-
本 Wave 内所有 task
|
|
1541
|
+
本 Wave 内所有 task 完成、checkbox 勾好后,**由你(主代理/调度者)**汇总本 Wave 的实际代码变更,更新 {SPEC_ROOT}/changes/<change>/module-impact.md(plan 阶段已生成首版):
|
|
1405
1542
|
- 基于本 Wave 各 task 的实际 git diff(不是计划)+ {SPEC_ROOT}/docs/<project>/modules/_module-map.yaml 对照
|
|
1406
1543
|
- 更新受影响模块的影响类型/说明(实际改动可能与 plan 首版预估不同,据实修正)
|
|
1407
1544
|
- **不由各 task 子代理分别改**(同 Wave 并行子代理改同一文件会互相覆盖)——只由主代理在 Wave 收尾统一更新一次
|
package/src/stages/knowledge.js
CHANGED
|
@@ -28,6 +28,16 @@ function resolveKnowledgeDir(dir, specDir) {
|
|
|
28
28
|
return join(base, 'knowledge')
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
+
/**
|
|
32
|
+
* 锚点/标题容错归一(坑 knowledge-hits-anchor-drift T4):锚点三代规则并存(CLI slug /
|
|
33
|
+
* INDEX 手写 / 平台 slugify_anchor)对 emoji 前缀、点号、斜杠、首尾中划线的处理互不一致
|
|
34
|
+
* ——归一到「仅字母数字 + 小写」后比对,三代产物归一相等;截断(60 字符)由调用方前缀
|
|
35
|
+
* 容忍处理。仅用于 validate 告警,不参与命中判定(单一源方向见 T2)。
|
|
36
|
+
*/
|
|
37
|
+
function normalizeAnchorForCompare(s) {
|
|
38
|
+
return String(s || '').replace(/[^\p{L}\p{N}]+/gu, '').toLowerCase()
|
|
39
|
+
}
|
|
40
|
+
|
|
31
41
|
function output(ok, data, error) {
|
|
32
42
|
const result = { ok, ...data }
|
|
33
43
|
if (error) result.error = error
|
|
@@ -223,6 +233,41 @@ export async function cmdValidate(dir, args, opts = {}) {
|
|
|
223
233
|
}
|
|
224
234
|
}
|
|
225
235
|
|
|
236
|
+
// 2c. 路由行锚点与目标文件标题的一致性(坑 knowledge-hits-anchor-drift T4,2026-09-25
|
|
237
|
+
// 实证:INDEX 锚点与文件标题静默漂移——标题事后被改/emoji 前缀/点号差异使命中落空成
|
|
238
|
+
// 「幽灵锚」,平台知识页不计覆盖)。容错归一比对(剥 emoji/点号/斜杠/中划线/空白 +
|
|
239
|
+
// 小写 + 截断前缀容忍),**不**引入第四套严格 slug 规则(T2 单一源方向是平台侧归一,
|
|
240
|
+
// 此处只做告警不参与命中判定);漂移只告警不报错(INDEX 手写面,修复由人拍板)。
|
|
241
|
+
for (const entry of entries) {
|
|
242
|
+
if (!entry.anchor) continue
|
|
243
|
+
const refPath = join(knowledgeDir, entry.file)
|
|
244
|
+
if (!existsSync(refPath)) continue // broken_reference 已报,不重复
|
|
245
|
+
const headings = []
|
|
246
|
+
try {
|
|
247
|
+
const content = readFileSync(refPath, 'utf8')
|
|
248
|
+
for (const m of content.matchAll(/^#{1,6}\s+(.+?)\s*$/gm)) headings.push(m[1])
|
|
249
|
+
} catch { /* 读失败留给上层报 */ }
|
|
250
|
+
const target = normalizeAnchorForCompare(entry.anchor)
|
|
251
|
+
if (!target || headings.length === 0) continue
|
|
252
|
+
const hit = headings.some((h) => {
|
|
253
|
+
const hn = normalizeAnchorForCompare(h)
|
|
254
|
+
if (!hn) return false
|
|
255
|
+
if (hn === target) return true
|
|
256
|
+
// 截断容忍(锚点生成侧有 60 字符截断):短的一方是长方前缀即视为同源
|
|
257
|
+
const [shorter, longer] = hn.length <= target.length ? [hn, target] : [target, hn]
|
|
258
|
+
return shorter.length >= 6 && longer.startsWith(shorter)
|
|
259
|
+
})
|
|
260
|
+
if (!hit) {
|
|
261
|
+
warnings.push({
|
|
262
|
+
code: 'anchor_drift',
|
|
263
|
+
path: join('knowledge', entry.file),
|
|
264
|
+
anchor: entry.anchor,
|
|
265
|
+
display: entry.display,
|
|
266
|
+
hint: '路由行锚点在目标文件无对应标题(标题已改或锚点生成规则漂移)——命中会落空成幽灵锚,按文件现标题修正 INDEX 路由行',
|
|
267
|
+
})
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
|
|
226
271
|
// 2b. flag 形态关键词(坑 knowledge-flag-as-value):classify 曾把 flag-as-value 的
|
|
227
272
|
// 字面量 '--file' 写进路由行——目标文件存在、行格式合法,既有检查放行,此处补侦测
|
|
228
273
|
for (const entry of entries) {
|
|
@@ -575,6 +620,16 @@ export async function cmdKnowledge(args, dir, opts = {}) {
|
|
|
575
620
|
}
|
|
576
621
|
return mod.cmdKnowledgeClassify(dir, args.slice(1), opts)
|
|
577
622
|
}
|
|
623
|
+
case 'inbox': {
|
|
624
|
+
// 知识收件箱(2026-09-23 可见性 quick):uncategorized 待审清单+基线状态按需视图,
|
|
625
|
+
// --json 结构化。纯读零副作用——配套 quick --done/归档收尾的收件箱横幅(complete-handlers)。
|
|
626
|
+
const mod = await tryImportSubcommandImpl('../knowledge-classify.js', 'knowledge-classify.js')
|
|
627
|
+
if (!mod || typeof mod.cmdKnowledgeInbox !== 'function') {
|
|
628
|
+
output(false, {}, { code: 'not_implemented', subcommand: 'inbox' })
|
|
629
|
+
return
|
|
630
|
+
}
|
|
631
|
+
return mod.cmdKnowledgeInbox(dir, args.slice(1), opts)
|
|
632
|
+
}
|
|
578
633
|
case 'stats': {
|
|
579
634
|
const mod = await tryImportSubcommandImpl('../knowledge-stats.js', 'knowledge-stats.js')
|
|
580
635
|
if (!mod || typeof mod.cmdKnowledgeStats !== 'function') {
|
|
@@ -587,11 +642,29 @@ export async function cmdKnowledge(args, dir, opts = {}) {
|
|
|
587
642
|
}
|
|
588
643
|
return mod.cmdKnowledgeStats(dir, args.slice(1), opts)
|
|
589
644
|
}
|
|
645
|
+
case 'digest': {
|
|
646
|
+
// 治理信号摘要(2026-09-27-knowledge-digest):四类沉睡信号(rot 待复核/收件箱积压/
|
|
647
|
+
// 伪域落库/绑定解析失败)超阈才见人——安静即健康态。--json 供平台 RPC 消费(信号卡)。
|
|
648
|
+
const specBase = opts.specDir || join(dir, '.sillyspec')
|
|
649
|
+
const rest = args.slice(1)
|
|
650
|
+
try {
|
|
651
|
+
const { collectKnowledgeDigest, renderKnowledgeDigestText } = await import('../knowledge-digest.js')
|
|
652
|
+
const d = collectKnowledgeDigest({ specBase, projectRoot: dir })
|
|
653
|
+
if (opts.json || rest.includes('--json')) { // --json 是全局旗标(index.js 顶层吞掉),opts.json 为正道,rest 兜底
|
|
654
|
+
output(true, { subcommand: 'digest', ...d }) // digest 进 data 面(第三参是 error 位)
|
|
655
|
+
} else {
|
|
656
|
+
console.log(renderKnowledgeDigestText(d))
|
|
657
|
+
}
|
|
658
|
+
} catch (e) {
|
|
659
|
+
output(false, {}, { code: 'digest_failed', message: String((e && e.message) || e) })
|
|
660
|
+
}
|
|
661
|
+
return
|
|
662
|
+
}
|
|
590
663
|
default:
|
|
591
664
|
output(false, {}, {
|
|
592
665
|
code: 'unknown_subcommand',
|
|
593
666
|
subcommand: subCommand,
|
|
594
|
-
available: ['search', 'inspect', 'validate', 'refresh', 'propose', 'classify', 'stats'],
|
|
667
|
+
available: ['search', 'inspect', 'validate', 'refresh', 'propose', 'classify', 'inbox', 'stats', 'digest'],
|
|
595
668
|
})
|
|
596
669
|
}
|
|
597
670
|
}
|