sillyspec 3.34.1 → 3.34.2
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/skills/deploy/SKILL.md +108 -0
- package/.claude/skills/sillyspec-flow/SKILL.md +1 -1
- package/assets/command-cards/flow.md +2 -1
- package/eval/knowledge/baseline.json +12 -12
- package/package.json +1 -1
- package/src/flow.js +37 -19
- package/src/foreign-declared.js +22 -7
- package/src/hunk-attribution.js +7 -3
- package/src/index.js +1 -1
- package/src/knowledge-classify.js +230 -4
- package/src/knowledge-digest.js +19 -0
- package/src/knowledge-match.js +102 -0
- package/src/knowledge-vector.js +59 -5
- package/src/route-hindsight.js +13 -8
- package/src/spec-sync.js +139 -23
- package/src/stages/execute.js +3 -1
- package/src/stages/explore.js +4 -2
- package/src/stages/knowledge.js +93 -123
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: deploy
|
|
3
|
+
description: sillyspec 仓的 npm 发布流程——遗留暂存件归属判别、版本面 bump 与锚点测试同步、验证门、sillyspec push、npm publish 与 202 异步传播核验。用户说「发布/deploy/部署/发版/publish/升版本/发个最小版本/重新发布 npm」时必须使用本 skill;只适用于本仓(sillyspec)的 npm 发布,不适用于消费仓。
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# sillyspec npm 发布流程
|
|
7
|
+
|
|
8
|
+
发布 = 五步顺序链:暂存区清障 → 版本面 → 验证门 → 推送 → npm publish。
|
|
9
|
+
顺序有依据:暂存区不清 `sillyspec push` 拒跑;锚点测试断言新版本号所以必须先 bump 再跑门;验证回执写进 release 提交信息。
|
|
10
|
+
|
|
11
|
+
每步动手前先 `export SILLYSPEC_SESSION_ID=<会话标识>`(多会话共享仓的所有权判定依赖它)。
|
|
12
|
+
|
|
13
|
+
## ① 前置检查
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npm whoami # 未登录→停,让用户处理凭据
|
|
17
|
+
git log origin/main..HEAD --oneline # 未推送面:应只含预期提交
|
|
18
|
+
git diff --cached --name-only # 暂存区全量读取(禁过滤式核对)
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## ② 暂存区清障(push 的门)
|
|
22
|
+
|
|
23
|
+
暂存区非空时 `sillyspec push` 拒跑。逐文件归属判别,再分类处理:
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
# 归属判别:暂存 diff 里含哪个变更名就是谁的
|
|
27
|
+
git diff --cached -- <file> | grep -c '<change-name>'
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
- **既往归档变更的蒸馏产物滞留**(owning 变更从未立项/未批准):按用户指示入库——`chore(knowledge)` 提交,显式 pathspec,提交信息写明来源与授权
|
|
31
|
+
- **活跃并行会话的在途文件**:默认不动、如实报告让用户拍板;用户说「一并提交」才入库
|
|
32
|
+
- SillyHub/multi-agent-platform 等其它项目的域文件同样按归属判别处理,不机械照单全收
|
|
33
|
+
|
|
34
|
+
## ③ 版本面
|
|
35
|
+
|
|
36
|
+
**版本号**:用户指定号优先(如「3.34.1 应该是」);未指定则最小补丁位 +1(3.34.0→3.34.1)。
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
npm version <版本号> --no-git-tag-version # package.json + package-lock 双文件同步
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
**锚点测试同步**(不同步必红):
|
|
43
|
+
|
|
44
|
+
- `test/quick-retired.test.mjs` R5 断言 `pkg.version === '<新版本号>'`——精确等于,逐字改
|
|
45
|
+
- `test/flow-order-canon.test.mjs` 断言 `> 3.33.1`——推进断言,版本高于该值就不动
|
|
46
|
+
|
|
47
|
+
**提交**(先例:`chore(release): 3.34.1 版本面——…`,先跑完 ④ 验证门再提交,回执写进信息):
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
git commit -m 'chore(release): <版本号> 版本面——package.json/package-lock <旧>→<新>(用户指定发布号),quick-retired R5 精确锚同步…;lint …绿、锚点套件 …/…绿' -- package.json package-lock.json test/quick-retired.test.mjs
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
⚠️ 提交信息含反引号时用**单引号**包 `-m`——bash 双引号内反引号会被解释成命令替换。
|
|
54
|
+
|
|
55
|
+
## ④ 验证门(release 提交前跑)
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
npm run lint # check-syntax,~1111 文件
|
|
59
|
+
node --test test/quick-retired.test.mjs test/flow-order-canon.test.mjs # 版本锚点 8 例
|
|
60
|
+
node --test test/<本次交付相关护栏测试> # 变更面护栏
|
|
61
|
+
npm pack --dry-run # 体量核验:~480 文件 5.3MB 量级
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
**既有红归属判别**(全量红不阻断发布,但必须留痕):红的测试若疑似非本次引入,用 stash 法证明:
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
git stash push -- <疑似源文件> && node --test <红测试> # 仍红→与该文件无关(既有红)
|
|
68
|
+
git stash pop # 恢复,切勿忘
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
归属结论写进 release 提交信息(先例:「doc-ref-check 既有红为并行会话在途改动,stash 归属判别留痕」)。
|
|
72
|
+
|
|
73
|
+
## ⑤ 推送
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
sillyspec push # 禁裸 git push / --force / 手工 reset --soft 压扁
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
CLI 可能判定「无需压扁直推」——正常,保留原历史快进。推送输出里的「预存锚点异常 advisory」不阻断。
|
|
80
|
+
|
|
81
|
+
## ⑥ npm publish 与 202 异步核验
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
npm publish
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
**关键认知:registry 返回 202 = 异步受理,不是发布失败。** npm 打出 `+ sillyspec@<版本号>` 即受理成功;随后几分钟 registry 可能仍 404——那是传播窗口,**禁止重复 publish**。核验方法(绕过 npm view 的 CDN 缓存,用 curl 直查):
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
curl -s https://registry.npmjs.org/-/package/sillyspec/dist-tags # {"latest":"<版本号>"} 才算完成
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
不放心受理状态时,查 debug 日志确认(而非猜):
|
|
94
|
+
|
|
95
|
+
```bash
|
|
96
|
+
grep 'PUT 202' "$(ls -t C:/Users/<user>/AppData/Local/npm-cache/_logs/*.log | head -1)"
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
## 坑清单速查
|
|
100
|
+
|
|
101
|
+
| 症状 | 真相 | 处置 |
|
|
102
|
+
|---|---|---|
|
|
103
|
+
| `sillyspec push` 报「index 有 N 个已暂存文件」 | 暂存区门,防夹带并行会话文件 | 回 ② 归属判别清障 |
|
|
104
|
+
| bump 后锚点套件红 | quick-retired R5 精确断言没同步 | 逐字改成新版本号 |
|
|
105
|
+
| publish 后 registry 查不到新版本 | 202 异步传播窗口(可数分钟) | sleep+curl 直查 dist-tags,勿重发 |
|
|
106
|
+
| `npm view` 版本滞后 | CDN 缓存 | curl 直查 registry |
|
|
107
|
+
| 提交时报 unexpected EOF | 信息里的反引号被 bash 吃了 | `-m` 用单引号包 |
|
|
108
|
+
| 全量测试有个别红 | 多会话共享仓常态 | stash 归属判别→既有红留痕不阻断 |
|
|
@@ -50,7 +50,7 @@ sillyspec flow start --change <变更名> --input "<一句 10-30 字中文概括
|
|
|
50
50
|
|
|
51
51
|
### ③ 断点①:批准后才动码
|
|
52
52
|
|
|
53
|
-
把 FR 条目+design
|
|
53
|
+
用 AskUserQuestion 把 FR 条目+design 四节摘要与选项(批准开始实现 / 需要调整 / 否决)呈现给用户——不要用纯文字提问等回复 → 用户批准后运行 `sillyspec flow approve --change <名>`(agent 代跑如实声明通道 `--user-confirmed` / `--self-approved`,无 flag 按 unattested 留痕);用户明确说「直接跑完/不用问我」→ 重跑 `flow start --autopilot` 声明豁免(用户显式授权,非 agent 自选补救)。未批准且未声明 --autopilot 时 flow done 拒收。
|
|
54
54
|
|
|
55
55
|
### ④ 干活(已批准)
|
|
56
56
|
|
|
@@ -21,7 +21,8 @@ sillyspec flow start --change <变更名> --input "<一句 10-30 字中文概括
|
|
|
21
21
|
# ② 补 spec 面(动码前):requirements FR 行为句(强度词)+ 测试绑定行、design.md 四节作答
|
|
22
22
|
# (空节 flow done 拒收;列改动文件用独立「## 文件变更清单」节);tasks.md 改写为工作队列
|
|
23
23
|
# ——机器种子改写为实现步骤(每行做什么+怎么验证,保持 `- [ ] task-NN:` 行形态)。
|
|
24
|
-
#
|
|
24
|
+
# 然后过断点①:用 AskUserQuestion 把摘要与选项(批准开始实现/需要调整/否决)呈现给用户
|
|
25
|
+
# ——不要用纯文字提问等回复;用户批准后运行 sillyspec flow approve --change <名>(批准留痕;
|
|
25
26
|
# 用户显式说「直接跑完」→ 重跑 start --autopilot 豁免)。批准是动码前置义务,不是收口补救。
|
|
26
27
|
# ③ 干活(已批准):写测试、改代码(按层适用——纯逻辑先写、行为契约断用户所见、样式/纯配置不写);
|
|
27
28
|
# 执行循环:Working on task N/M → 做一件 → 测试绿后当场勾一格(sillyspec task tick --change <名> --task task-NN
|
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
"generated_by": "eval/knowledge/run.mjs",
|
|
4
4
|
"snapshot": {
|
|
5
5
|
"sillyspec": {
|
|
6
|
-
"head": "
|
|
7
|
-
"knowledge_hash": "
|
|
8
|
-
"knowledge_files":
|
|
6
|
+
"head": "c5b1d9a5a6cd83437910253ba0a6ff9ea5dd7904",
|
|
7
|
+
"knowledge_hash": "ef33189ed3662e37e286fb6827eb06e5ea449853459da0a74a99d0305b973665",
|
|
8
|
+
"knowledge_files": 141
|
|
9
9
|
}
|
|
10
10
|
},
|
|
11
11
|
"cases": {
|
|
@@ -265,13 +265,13 @@
|
|
|
265
265
|
"raw": {
|
|
266
266
|
"top1": 0.6667,
|
|
267
267
|
"top3": 0.75,
|
|
268
|
-
"mrr": 0.
|
|
268
|
+
"mrr": 0.7215,
|
|
269
269
|
"n": 12
|
|
270
270
|
},
|
|
271
271
|
"rendered": {
|
|
272
272
|
"top1": 0.6667,
|
|
273
273
|
"top3": 0.75,
|
|
274
|
-
"mrr": 0.
|
|
274
|
+
"mrr": 0.7215,
|
|
275
275
|
"n": 12
|
|
276
276
|
},
|
|
277
277
|
"truncated": [],
|
|
@@ -326,8 +326,8 @@
|
|
|
326
326
|
},
|
|
327
327
|
{
|
|
328
328
|
"id": "s-scope-04",
|
|
329
|
-
"raw_rank":
|
|
330
|
-
"rendered_rank":
|
|
329
|
+
"raw_rank": 5,
|
|
330
|
+
"rendered_rank": 5,
|
|
331
331
|
"truncated": false
|
|
332
332
|
},
|
|
333
333
|
{
|
|
@@ -360,7 +360,7 @@
|
|
|
360
360
|
"added": {
|
|
361
361
|
"top1": 0.4,
|
|
362
362
|
"top3": 0.6,
|
|
363
|
-
"mrr": 0.
|
|
363
|
+
"mrr": 0.5067,
|
|
364
364
|
"n": 5,
|
|
365
365
|
"reachable": 0.8
|
|
366
366
|
},
|
|
@@ -380,7 +380,7 @@
|
|
|
380
380
|
"raw": {
|
|
381
381
|
"top1": 0.25,
|
|
382
382
|
"top3": 0.5,
|
|
383
|
-
"mrr": 0.
|
|
383
|
+
"mrr": 0.4167,
|
|
384
384
|
"n": 4
|
|
385
385
|
},
|
|
386
386
|
"rendered": {
|
|
@@ -408,7 +408,7 @@
|
|
|
408
408
|
},
|
|
409
409
|
{
|
|
410
410
|
"id": "s-man-10",
|
|
411
|
-
"raw_rank":
|
|
411
|
+
"raw_rank": 12,
|
|
412
412
|
"rendered_rank": null,
|
|
413
413
|
"truncated": true
|
|
414
414
|
},
|
|
@@ -423,7 +423,7 @@
|
|
|
423
423
|
"approx": {
|
|
424
424
|
"top1": 0,
|
|
425
425
|
"top3": 0.3333,
|
|
426
|
-
"mrr": 0.
|
|
426
|
+
"mrr": 0.2222,
|
|
427
427
|
"n": 3,
|
|
428
428
|
"reachable": 1
|
|
429
429
|
},
|
|
@@ -441,7 +441,7 @@
|
|
|
441
441
|
"self_smoke": {
|
|
442
442
|
"sillyspec": {
|
|
443
443
|
"cases": 214,
|
|
444
|
-
"skipped":
|
|
444
|
+
"skipped": 618,
|
|
445
445
|
"own_hit_top3": 0.0187,
|
|
446
446
|
"name_only_top3": 0.028,
|
|
447
447
|
"leak_gap": -0.0093
|
package/package.json
CHANGED
package/src/flow.js
CHANGED
|
@@ -403,9 +403,13 @@ export async function flowKnowledgeDigest({ specBase, change, changeDir, input,
|
|
|
403
403
|
...(km.routeHits || []).slice(0, 3).map((e) => (e.anchor ? `${e.file}#${e.anchor}` : e.file)),
|
|
404
404
|
...scopeMatchedFiles,
|
|
405
405
|
])].slice(0, 12)
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
406
|
+
// 遥测(FR-04,2026-11-05-knowledge-search-observability):channel 单源记录(调优/画像
|
|
407
|
+
// 数据地基);真零命中也落盘(zeroHit 标记 + 空 matchedFiles——stats 文件矩阵零贡献,
|
|
408
|
+
// 真实零命中率恰是向量层的目标场景,此前「仅命中才落盘」把它定义没了)。fail-soft 不阻断注入。
|
|
409
|
+
try {
|
|
410
|
+
const { appendKnowledgeHit } = await import('./knowledge-hits.js')
|
|
411
|
+
const { resultChannel } = await import('./knowledge-vector.js')
|
|
412
|
+
if (thinMatchedFiles.length > 0 || rejected.length > 0 || scopeMatchedFiles.length > 0) {
|
|
409
413
|
appendKnowledgeHit(join(specBase, '.runtime'), {
|
|
410
414
|
type: 'inject',
|
|
411
415
|
change,
|
|
@@ -415,10 +419,24 @@ export async function flowKnowledgeDigest({ specBase, change, changeDir, input,
|
|
|
415
419
|
mentionedFiles,
|
|
416
420
|
...(scopeMatchedFiles.length > 0 ? { scopeMatchedFiles } : {}),
|
|
417
421
|
rejectedCount: rejected.length,
|
|
422
|
+
channel: resultChannel(km),
|
|
418
423
|
source: 'thin-start',
|
|
419
424
|
})
|
|
420
|
-
}
|
|
421
|
-
|
|
425
|
+
} else {
|
|
426
|
+
appendKnowledgeHit(join(specBase, '.runtime'), {
|
|
427
|
+
type: 'inject',
|
|
428
|
+
change,
|
|
429
|
+
query: kmQuery,
|
|
430
|
+
matchedFiles: [],
|
|
431
|
+
injectedFiles: [],
|
|
432
|
+
mentionedFiles: [],
|
|
433
|
+
rejectedCount: 0,
|
|
434
|
+
channel: resultChannel(km),
|
|
435
|
+
zeroHit: true,
|
|
436
|
+
source: 'thin-start',
|
|
437
|
+
})
|
|
438
|
+
}
|
|
439
|
+
} catch { /* 遥测 fail-soft(R-04):hits 落盘失败不影响注入本体 */ }
|
|
422
440
|
if (domains.length === 0 && rejected.length === 0 && !km.matched) {
|
|
423
441
|
lines.length = 0
|
|
424
442
|
lines.push(`🧠 知识注入(轻量道读取面):语料未命中知识库(无触达域依据、无否决命中、INDEX 无对齐条目)——可自行查 knowledge/INDEX.md。`)
|
|
@@ -787,7 +805,7 @@ export async function cmdFlowStart({ change, input, title: titleFlag = null, thi
|
|
|
787
805
|
? `🖼️ 原型在场(${prototypePaths.length} 个 HTML):实现前必看,界面/交互/流程按原型对齐;有出入以 design.md 承诺为准并在回复中说明。`
|
|
788
806
|
: null,
|
|
789
807
|
`【你要做的】① spec 阶段先定稿任务面:把 tasks.md 改写为工作分解(每行=可独立完成并当场验证的一步:做什么+怎么验证;全 \`- [ ]\`——代码开动前工作队列先存在);`,
|
|
790
|
-
`🛑 断点①(机器门):spec
|
|
808
|
+
`🛑 断点①(机器门):spec 面(任务面+测试绑定槽)定稿后用 AskUserQuestion 把摘要与选项(批准开始实现/需要调整/否决)呈现给用户,不要用纯文字提问等回复 → 用户批准后 flow approve --change ${change}(或用户显式 --autopilot)——批准后才动码。design 四节槽收编豁免(以 brainstorm design 为准)。`,
|
|
791
809
|
`② 执行走任务循环:对 tasks.md 每个 pending 行——展示「Working on task N/M: <任务>」→ 做 → 测试绿后当场勾一格(\`- [ ]\`→\`- [x]\`:sillyspec task tick --change ${change} --task task-NN 或直接 Edit)→ 下一行;全勾后 flow done(收口硬门拒单拍多格勾选)。design/decisions 是本变更的承诺锚(flow done 豁免 design 四节槽,以其为准)。`,
|
|
792
810
|
`requirements 测试绑定槽(收编追加)每条 FR 至少一行作答;写码前作答(动码前,与任务面定稿同批)。`,
|
|
793
811
|
`✅ 任务面归你(工作分解契约):tasks.md 是你的工作队列(机器种子可增删改,保持 \`- [ ] task-NN:\` 行形态)——CLI 以 checkbox 为进度状态机(task tick/flow status 实时读它),TodoWrite 类工具不替代;`,
|
|
@@ -1184,7 +1202,7 @@ export async function cmdFlowStart({ change, input, title: titleFlag = null, thi
|
|
|
1184
1202
|
// 顺序正典(2026-10-19-thin-order-canon):行动句必须是顺序句——实证(artifact 时间戳
|
|
1185
1203
|
// 还原+跨仓 14 例抽样)agent 跟从【你要做的】首义行动,旧句「直接干活」把动码提前、
|
|
1186
1204
|
// 文档与批准沦为收口仪式;正典=先答 spec 面 → 批准 → 才动码(读代码探究不算动码)。
|
|
1187
|
-
`【你要做的】先答 spec 面,批准后才动码:① 答 spec——requirements FR 行为句+测试绑定行、design.md 四节作答(动码前;读代码探究不算动码);②
|
|
1205
|
+
`【你要做的】先答 spec 面,批准后才动码:① 答 spec——requirements FR 行为句+测试绑定行、design.md 四节作答(动码前;读代码探究不算动码);② 断点①——用 AskUserQuestion 呈现摘要+选项(批准开始实现/需要调整/否决),用户批准后 flow approve --change ${change}(或用户显式 --autopilot);③ 才干活——写测试、改代码。治理工件骨架 CLI 已起草、收口机器做(协议记账单位=change 级)。`,
|
|
1188
1206
|
// 收口拒收红线前置(2026-10-11-flow-start-redline-frontload):钉死/空槽/绑定三面都是文档
|
|
1189
1207
|
// 先行校验,实证第一次最容易踩(四问文本被改写、区间 task-NN 写法零证据被拒)——横幅
|
|
1190
1208
|
// 头部先行告知,收口才暴露的规则前置到动笔前。
|
|
@@ -1221,16 +1239,16 @@ export async function cmdFlowStart({ change, input, title: titleFlag = null, thi
|
|
|
1221
1239
|
`💡 执行期用户插入决策方向(有实现影响、约束后续变更的取舍/否决/定调)→ 当场 sillyspec decisions add --change ${change} --id D-001 --title "…" 落盘(只留会话=收口档案不可见、跨变更复潮无防);改变承诺面的同步 design/tasks 对应节。`,
|
|
1222
1240
|
``,
|
|
1223
1241
|
`🛑 三断点纪律(顺序纪律:①未过不动码;②③为汇报点——用户没说「全跑完」就必须在每个断点向用户汇报并等确认):`,
|
|
1224
|
-
` ① spec 断点【机器门】:填完 FR 区和 design
|
|
1242
|
+
` ① spec 断点【机器门】:填完 FR 区和 design 四节后,用 AskUserQuestion 把摘要(FR 条目+盲维作答+方案概述)与选项(批准开始实现/需要调整/否决)呈现给用户——不要用纯文字提问等回复;`,
|
|
1225
1243
|
` 起草完先自查:sillyspec flow verify-drafts --change ${change}(与批准门同判定——自查通过再请批准,用户一次过);`,
|
|
1226
|
-
`
|
|
1244
|
+
` 用户在 AskUserQuestion 中确认批准后请其运行 sillyspec flow approve --change ${change}(批准留痕)再动手写代码——`,
|
|
1227
1245
|
` agent 代跑时如实声明通道:--user-confirmed(转述用户已确认)/ --self-approved(agent 自批),`,
|
|
1228
1246
|
` 无 flag 按 unattested 留痕(审计面无法区分用户确认与自批)——2026-10-12-approve-channel-attest。`,
|
|
1229
1247
|
` 方案错了返工最贵;批准是动码前置义务(未批准且未声明 --autopilot 时 flow done 亦拒收——2026-10-04-thin-docs-v2);`,
|
|
1230
1248
|
` 用户否掉方案 → flow reject --change ${change} --reason <理由>(拒后批准须显式通道再确认)。`,
|
|
1231
|
-
` ②
|
|
1232
|
-
`
|
|
1233
|
-
` ③ 归档断点:flow done
|
|
1249
|
+
` ② 执行断点:写完代码跑完测试后,用 AskUserQuestion 把测试结果(过了几个/挂了什么)与选项(确认收口/继续修/有问题)呈现给用户,`,
|
|
1250
|
+
` 等用户选择再跑收口;汇报顺带对账执行期用户决策(decisions 条目列一下,无则说明无)。`,
|
|
1251
|
+
` ③ 归档断点:flow done 跑完后(无论过/拒),用 AskUserQuestion 把结果(归档成功/被什么拦了)呈现给用户(选项:结束/处理遗留问题)。`,
|
|
1234
1252
|
` 用户明确说「直接跑完/不用问我」→ 重跑 flow start --change ${change} --autopilot 声明豁免(用户显式授权、留痕——非 agent 自选补救通道),`,
|
|
1235
1253
|
` ①②③人审全免(agent 自主跑到底)。`,
|
|
1236
1254
|
` 随时可查进度:sillyspec flow status --change ${change}`,
|
|
@@ -1283,12 +1301,12 @@ function printRecoveryBriefing({ cwd, specBase, change, changeDir, runtimeRoot,
|
|
|
1283
1301
|
`- 六子步标记:${done.length ? `已完成 ${done.join('/')}` : '(无)'}${left.length ? `;待办 ${left.join('/')}` : ';全部完成'}`,
|
|
1284
1302
|
// 顺序正典(2026-10-19-thin-order-canon):恢复面带批准状态——重入时 agent 只看简报,
|
|
1285
1303
|
// 未批准时「下一步」必须指向补批而非「继续干活」(防先码后批在恢复路径复发)。
|
|
1286
|
-
`- spec 批准:${st.spec_approved ? `✅ 已批准(channel=${approveChannelOf(st)})` : st.autopilot ? '🔑 autopilot 豁免(用户显式授权)' : '⬜
|
|
1304
|
+
`- spec 批准:${st.spec_approved ? `✅ 已批准(channel=${approveChannelOf(st)})` : st.autopilot ? '🔑 autopilot 豁免(用户显式授权)' : '⬜ 未批准——动码前先过断点①(用 AskUserQuestion 呈请批准 → flow approve)'}`,
|
|
1287
1305
|
// 红线重申(2026-10-11-flow-start-redline-frontload):重入不出首进横幅——钉死规则与
|
|
1288
1306
|
// task-NN token 形态随简报补位,断点续跑生命周期内规则不丢。
|
|
1289
1307
|
`- 红线重申:design 四问文本/FR 标题锚逐字钉死勿改写(改写即拒收);勾选证据提交消息逐个带 task-NN 或 /、,连写(「task-01..task-05」区间写法不认)。`,
|
|
1290
1308
|
`- 剩什么:${left.length === 0 && dirty === 0 ? '活已干完' : dirty > 0 ? '活未干完(继续改代码)' : '收尾待裁决'}`,
|
|
1291
|
-
`- 下一步:${left.length === 0 ? `sillyspec flow done --change ${change}` : !st.spec_approved && !st.autopilot ?
|
|
1309
|
+
`- 下一步:${left.length === 0 ? `sillyspec flow done --change ${change}` : !st.spec_approved && !st.autopilot ? `未批准先补批(用 AskUserQuestion 呈请批准 → sillyspec flow approve --change ${change});随后继续干活,干完跑 flow done(断点续)` : `继续干活;干完跑 flow done(断点续)`}`,
|
|
1292
1310
|
...(digestLines.length > 0 ? ['', ...digestLines] : []),
|
|
1293
1311
|
// 尾部原型锚(2026-10-14-guide-anchor-adopt-proto):收编/含原型变更重入时原型指针不隐身
|
|
1294
1312
|
//(首进 adopt 输出的 🖼️ 行在会话切换后已丢,简报尾部补可发现面;置于 digestLines 后=绝对
|
|
@@ -1434,7 +1452,7 @@ export async function cmdFlowDone({ change, cwd, specBase, runtimeRootOpt = null
|
|
|
1434
1452
|
// 实证下无牙;v2 起批准是收口硬前提,autopilot 是用户显式豁免通道)
|
|
1435
1453
|
if (!st.autopilot && !st.spec_approved) {
|
|
1436
1454
|
console.error(`❌ spec 断点未批准:批准是动码前置义务(不是收口补救动作)——本变更缺方案确认留痕`)
|
|
1437
|
-
console.error(`
|
|
1455
|
+
console.error(` 补批通道(留痕可审计):用 AskUserQuestion 把 FR 条目+design 四节摘要与选项(批准/需要调整/否决)呈现给用户,用户批准后由用户运行:`)
|
|
1438
1456
|
console.error(` sillyspec flow approve --change ${change} [--user-confirmed|--self-approved]`)
|
|
1439
1457
|
console.error(` 或用户显式豁免(须用户授权,非 agent 自选):重跑 flow start --change ${change} --autopilot(声明留痕)`)
|
|
1440
1458
|
reportMidFail('artifacts')
|
|
@@ -2888,7 +2906,7 @@ export async function cmdFlowDone({ change, cwd, specBase, runtimeRootOpt = null
|
|
|
2888
2906
|
}
|
|
2889
2907
|
appendTelemetry(reviewOutcome) // 成功收口(失败面已在各失败路径提前落账,appendTelemetry 单点)
|
|
2890
2908
|
|
|
2891
|
-
console.log(`📦 归档断点:全部子步完成,即将归档——agent
|
|
2909
|
+
console.log(`📦 归档断点:全部子步完成,即将归档——agent 用 AskUserQuestion 把收口结果(子步清单+评审结论+验证回执)与选项(结束/处理遗留问题)呈现给用户。`)
|
|
2892
2910
|
console.log(`✅ flow done 完成(2/2 协议调用收口):${change}——${SUBSTEPS.length} 子步 ${doneList.join('、')};change 已归档注销。`)
|
|
2893
2911
|
// Git 历史整理指引(2026-09-25-thin-freeze-git-hygiene):多轮中间提交可压扁为单提交——审计
|
|
2894
2912
|
// 真相在 change.patch(sha256 锚定)与归档产物,不依赖历史形态;baseline 在 change-patch.json。
|
|
@@ -2968,7 +2986,7 @@ async function cmdFlowVerifyDrafts({ change, cwd, specBase, runtimeRootOpt = nul
|
|
|
2968
2986
|
} else if (passed) {
|
|
2969
2987
|
for (const a of r.advisories) console.warn(`⚠️ [门柱漂移 advisory] ${a}(不阻断批准)`)
|
|
2970
2988
|
if (r.degraded) console.log(`ℹ️ 自查降级:draft-ledger 不在此机(.runtime 机本位/存量变更)——空槽兜底校验通过(v2 纯 markdown 无法对锚校验,如实降级)`)
|
|
2971
|
-
console.log(`✅ spec 面自查通过(0 violations
|
|
2989
|
+
console.log(`✅ spec 面自查通过(0 violations)——通过≠批准:用 AskUserQuestion 把摘要与选项(批准开始实现/需要调整/否决)呈现给用户,用户批准后 sillyspec flow approve --change ${change}(批准留痕后动码)`)
|
|
2972
2990
|
// 重叠路由 advisory(2026-10-28-overlap-route FR-04):批准前最后一个高频触点——当前
|
|
2973
2991
|
// 变更声明与他人活跃变更相交时一行分流提示。fail-open 静默:检测异常不拦批准不影响退出码。
|
|
2974
2992
|
try {
|
|
@@ -3016,7 +3034,7 @@ async function cmdFlowApprove({ change, cwd, specBase, by = null, channelFlag =
|
|
|
3016
3034
|
const st = readFlowState(changeDir) || {}
|
|
3017
3035
|
if (st.spec_rejected && !st.spec_rejected_superseded_at && !channelFlag) {
|
|
3018
3036
|
console.error(`❌ 本变更存在未翻越的拒绝记录(spec_rejected,reason=${String(st.spec_rejected_reason || '?').slice(0, 80)})——拒绝后的批准必须显式声明通道:`)
|
|
3019
|
-
console.error(`
|
|
3037
|
+
console.error(` 用 AskUserQuestion 征得用户重新确认后:sillyspec flow approve --change ${change} --user-confirmed(agent 转述用户确认)`)
|
|
3020
3038
|
console.error(` agent 自批如实声明:sillyspec flow approve --change ${change} --self-approved`)
|
|
3021
3039
|
console.error(` (拒绝后派生授权须显式再确认——2026-10-12-approve-channel-attest)`)
|
|
3022
3040
|
process.exit(2)
|
|
@@ -3376,7 +3394,7 @@ export async function cmdFlow(args, cwd, specDir = null, opts = {}) {
|
|
|
3376
3394
|
`📋 ${change}`,
|
|
3377
3395
|
...(changeTitle ? [` 标题:${changeTitle}`] : []),
|
|
3378
3396
|
` 阶段:${phase}`,
|
|
3379
|
-
` spec 批准:${specApproved ? '✅ 已批准' : autopilotOn ? '🔑 autopilot 豁免(用户显式授权)' : '⬜
|
|
3397
|
+
` spec 批准:${specApproved ? '✅ 已批准' : autopilotOn ? '🔑 autopilot 豁免(用户显式授权)' : '⬜ 未批准(动码前先过断点①:用 AskUserQuestion 呈请批准)'}`,
|
|
3380
3398
|
` design 槽:${designFilled ? '✅ 已填' : '⬜ 未填'}|FR 区:${frFilled ? '✅ 已填' : '⬜ 未填'}|绑定槽:${bindingsFilled}/${bindingsTotal}`,
|
|
3381
3399
|
hasTasks ? ` 任务勾选:${tasksChecked}/${tasksTotal}` : null,
|
|
3382
3400
|
...(heartbeat || []),
|
package/src/foreign-declared.js
CHANGED
|
@@ -222,8 +222,14 @@ export function parseChangeNamesFromSubject(subject) {
|
|
|
222
222
|
|
|
223
223
|
/**
|
|
224
224
|
* git log 文本(--format=%x00%s --name-only,每提交 NUL+标题+文件行)→ 文件提交归属映射。
|
|
225
|
-
* 每文件聚合:owners=触碰过它的全部提交携带的变更名;unknown
|
|
226
|
-
*
|
|
225
|
+
* 每文件聚合:owners=触碰过它的全部提交携带的变更名;unknown=被无变更名裸提交触碰过;
|
|
226
|
+
* tokened=无名 touch 中存在带 task-NN token 者(`task-\d+`——交付协议证据,thin-tasks-v3
|
|
227
|
+
* 起勾选证据=task token 提交,收口哨兵按它逐格核)。2026-11-08-bare-commit-attribution-narrow
|
|
228
|
+
* 起细分:纯裸 touch(无名无 token——release 生成器/Merge/归档 chore 形态)不再保守保留
|
|
229
|
+
* (判 foreign,见 isForeignByCommit);token 裸 touch 仍保守 own(task token 无变更名限定,
|
|
230
|
+
* 无法区分是谁的 task——fail-closed 不误剔交付)。原「unknown 恒 own」的 fail-closed 前提
|
|
231
|
+
* (自己的交付提交可能不带变更名)已随 token 协议过时——坑②(shared-repo multi-session
|
|
232
|
+
* frictions)10-28 实证:release-record 归档 chore 提交穿透冻结面。
|
|
227
233
|
*/
|
|
228
234
|
export function buildCommitAttribution(logText) {
|
|
229
235
|
const map = new Map()
|
|
@@ -234,9 +240,11 @@ export function buildCommitAttribution(logText) {
|
|
|
234
240
|
for (const f of lines.slice(1)) {
|
|
235
241
|
const file = f.replace(/\\/g, '/')
|
|
236
242
|
let e = map.get(file)
|
|
237
|
-
if (!e) { e = { owners: new Set(), unknown: false }; map.set(file, e) }
|
|
238
|
-
if (names.length === 0)
|
|
239
|
-
|
|
243
|
+
if (!e) { e = { owners: new Set(), unknown: false, tokened: false }; map.set(file, e) }
|
|
244
|
+
if (names.length === 0) {
|
|
245
|
+
e.unknown = true
|
|
246
|
+
if (/task-\d+/i.test(lines[0])) e.tokened = true
|
|
247
|
+
} else for (const n of names) e.owners.add(n)
|
|
240
248
|
}
|
|
241
249
|
}
|
|
242
250
|
return map
|
|
@@ -266,9 +274,16 @@ export function commitAttributionForChange(cwd, specBase, currentChangeName, bas
|
|
|
266
274
|
} catch { return null }
|
|
267
275
|
}
|
|
268
276
|
|
|
269
|
-
/**
|
|
277
|
+
/** 纯他侧文件判定(2026-11-08-bare-commit-attribution-narrow 细分):
|
|
278
|
+
* own 路径——①记录缺位(窗口外文件)②含本变更名 touch ③无名 touch 带 task-NN token
|
|
279
|
+
* (交付协议证据,保守不误剔——token 无变更名限定,区分不了是谁的 task)。
|
|
280
|
+
* foreign 路径——①纯他侧具名(旧语义)②纯裸 touch(无名无 token:release 生成器/
|
|
281
|
+
* Merge/归档 chore 形态——坑②污染源;误剔由收口「提交面夹带嫌疑 advisory」点名兜底)。 */
|
|
270
282
|
export function isForeignByCommit(entry, currentChangeName) {
|
|
271
|
-
|
|
283
|
+
if (!entry) return false
|
|
284
|
+
if (entry.owners.has(currentChangeName)) return false
|
|
285
|
+
if (entry.tokened) return false
|
|
286
|
+
return entry.owners.size > 0 || entry.unknown === true
|
|
272
287
|
}
|
|
273
288
|
|
|
274
289
|
/**
|
package/src/hunk-attribution.js
CHANGED
|
@@ -142,8 +142,10 @@ export async function runHunkAttributionGate({ cwd, specBase, changeName, baseli
|
|
|
142
142
|
// 补 design 自声明」双误导(F-4 实证:他侧 546720ea 已归档交付被指为在途)。按提交 message
|
|
143
143
|
// 变更名归属(commitAttributionForChange+isForeignByCommit,与 patch 冻结 filterCommittedFace
|
|
144
144
|
// 同源):窗口内全部提交均属他侧变更名 → 他侧归因(ℹ️ 信息行、不计 ok 阻断面);
|
|
145
|
-
//
|
|
146
|
-
//
|
|
145
|
+
// 2026-11-08-bare-commit-attribution-narrow 细分:纯裸提交(无名无 task token——release
|
|
146
|
+
// 生成器/Merge/归档 chore 形态)touch 同判他侧归因(坑②污染源,owners 空数组——渲染走
|
|
147
|
+
// 纯裸分支);token 裸/本变更名提交触碰的文件保持未归因(fail-closed 原口径)。切分不可得
|
|
148
|
+
// (非 git/git 失败 → null)保持全量未归因,不放宽。
|
|
147
149
|
let attr = null
|
|
148
150
|
let isForeign = null
|
|
149
151
|
try {
|
|
@@ -195,7 +197,9 @@ export function renderHunkAttributionLines(r) {
|
|
|
195
197
|
const attributed = r.faceCount - r.unattributed.length - (r.foreignByCommit || []).length
|
|
196
198
|
L.push(`- ℹ️ 提交面 ${r.faceCount} 个交付文件对账:声明面归因 ${attributed}、他侧归因(提交事实)${(r.foreignByCommit || []).length}、未归因 ${r.unattributed.length}、跨变更竞争 ${r.contended.length}、在途残留 ${r.residue.length}`)
|
|
197
199
|
for (const fb of (r.foreignByCommit || [])) {
|
|
198
|
-
L.push(
|
|
200
|
+
L.push((fb.owners || []).length > 0
|
|
201
|
+
? `- ℹ️ 他侧归因(提交事实):\`${fb.file}\` 窗口提交全部属 ${fb.owners.join('、')}——已归档他侧交付非本变更未归因,patch 冻结面同口径剔除`
|
|
202
|
+
: `- ℹ️ 他侧归因(提交事实):\`${fb.file}\` 窗口内仅纯裸提交(无名无 task token——release 生成器/Merge/归档 chore 形态)触碰——坑②污染源剔除,patch 冻结面同口径剔除;若确属本变更交付,补 design 自声明或带 task token 重新提交`)
|
|
199
203
|
}
|
|
200
204
|
for (const u of r.unattributed) {
|
|
201
205
|
L.push(`- ⚠️ 未归因:\`${u.file}\`(${u.hunks >= 0 ? `${u.hunks} hunk` : 'hunk 数不可得'})不在 design 清单与 requirements 测试绑定——属并行会话在途交付则须显式 pathspec 隔离并协调归属;属本变更则补 design 自声明`)
|
package/src/index.js
CHANGED
|
@@ -156,7 +156,7 @@ SillySpec CLI — 规范驱动开发工具包
|
|
|
156
156
|
sillyspec knowledge <search "<关键词>" [--limit N] [--include-resolved](--query 等效)
|
|
157
157
|
| inspect --id "..."
|
|
158
158
|
| validate | refresh
|
|
159
|
-
| propose --title "..." --category <name>
|
|
159
|
+
| propose --title "..." [--body-file <路径>|--body <文本>] --category <name>
|
|
160
160
|
| inbox [--json] (知识收件箱:uncategorized 待审清单+基线态)>
|
|
161
161
|
sillyspec platform <connect <url> [--token <t>]
|
|
162
162
|
| disconnect
|