sillyspec 3.28.3 → 3.28.5

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.
Files changed (77) hide show
  1. package/.claude/skills/sillyspec-auto/SKILL.md +54 -54
  2. package/.claude/skills/sillyspec-brainstorm/SKILL.md +114 -114
  3. package/.claude/skills/sillyspec-continue/SKILL.md +17 -17
  4. package/.claude/skills/sillyspec-execute/SKILL.md +222 -222
  5. package/.claude/skills/sillyspec-knowledge/SKILL.md +40 -40
  6. package/.claude/skills/sillyspec-plan/SKILL.md +156 -156
  7. package/.claude/skills/sillyspec-propose/SKILL.md +16 -16
  8. package/.claude/skills/sillyspec-resume/SKILL.md +21 -21
  9. package/.claude/skills/sillyspec-state/SKILL.md +25 -25
  10. package/.claude/skills/sillyspec-workspace/SKILL.md +33 -33
  11. package/AGENTS.md +25 -0
  12. package/INSTRUCTIONS.md +12 -0
  13. package/package.json +1 -1
  14. package/results-review-tier.md +18 -0
  15. package/src/agent-session-log.js +4 -3
  16. package/src/archive-delta.js +82 -0
  17. package/src/change-delete.js +2 -1
  18. package/src/change-list.js +18 -3
  19. package/src/change-risk-profile.js +77 -6
  20. package/src/commit-suggest.js +52 -1
  21. package/src/config-cat.js +17 -1
  22. package/src/constants.js +3 -1
  23. package/src/decision-distill.js +45 -5
  24. package/src/decisions-io.js +96 -0
  25. package/src/design-facts.js +3 -0
  26. package/src/dispatch/probe.js +31 -8
  27. package/src/docs-check.js +102 -3
  28. package/src/doctor-diagnostics.js +441 -5
  29. package/src/foreign-declared.js +53 -2
  30. package/src/index.js +606 -22
  31. package/src/mcp-server.js +144 -0
  32. package/src/module-impact.js +84 -12
  33. package/src/modules.js +46 -34
  34. package/src/plan-adopt-waves.js +13 -5
  35. package/src/progress/change-registry.js +34 -0
  36. package/src/progress.js +75 -2
  37. package/src/quick-recommend.js +6 -1
  38. package/src/quicklog.js +70 -8
  39. package/src/review-dispatch.js +377 -0
  40. package/src/run/archive-distill.js +48 -0
  41. package/src/run/command.js +48 -13
  42. package/src/run/complete-handlers.js +215 -9
  43. package/src/run/complete.js +184 -2
  44. package/src/run/gate-snapshot.js +88 -0
  45. package/src/run/gates.js +206 -58
  46. package/src/run/prompt.js +150 -14
  47. package/src/run/quick-audit.js +38 -13
  48. package/src/run/shared.js +183 -12
  49. package/src/run/stage.js +52 -2
  50. package/src/run/verify-quality-scan.js +336 -0
  51. package/src/runtime-hygiene.js +84 -0
  52. package/src/scope-audit.js +735 -0
  53. package/src/sillyhub-mcp/client.js +58 -3
  54. package/src/spec-sync.js +63 -8
  55. package/src/stage-contract.js +83 -3
  56. package/src/stage-review-checklist.js +44 -0
  57. package/src/stage-review.js +125 -4
  58. package/src/stages/archive.js +60 -167
  59. package/src/stages/brainstorm.js +7 -17
  60. package/src/stages/doctor.js +41 -515
  61. package/src/stages/execute.js +13 -10
  62. package/src/stages/plan-postcheck.js +191 -7
  63. package/src/stages/plan.js +7 -35
  64. package/src/stages/quick.js +11 -11
  65. package/src/stages/verify.js +24 -39
  66. package/src/sync-noise.js +158 -0
  67. package/src/sync.js +93 -27
  68. package/src/task-review.js +133 -17
  69. package/src/taskcard.js +46 -3
  70. package/src/validate-artifacts.js +163 -0
  71. package/src/verify-facts-schema.js +141 -0
  72. package/src/verify-postcheck.js +347 -29
  73. package/src/verify-probes.js +275 -24
  74. package/src/workflow.js +6 -0
  75. package/src/worktree-apply.js +167 -24
  76. package/src/worktree.js +5 -1
  77. package/templates/agents-instruction.md +1 -0
@@ -14,6 +14,7 @@ import { existsSync, readFileSync, readdirSync } from 'fs'
14
14
  import { join } from 'path'
15
15
  import { parseFileChangeListDetailed } from './change-list.js'
16
16
  import { safeGit, unquoteGitPath } from './git-helper.js'
17
+ import { parseAllowedPaths, parseTargetFiles } from './stages/plan-postcheck.js'
17
18
 
18
19
  /**
19
20
  * 主仓未提交文件集(staged + unstaged + untracked,rename 取新路径)。
@@ -138,8 +139,53 @@ export function collectForeignDeclaredFiles(cwd, currentChangeName, opts = {}) {
138
139
  return foreign
139
140
  }
140
141
 
142
+ /**
143
+ * 本变更(currentChangeName)的 own 声明集——own 优先判据(坑
144
+ * verify-reconcile-own-file-foreign-false-positive,2026-09-09 实证):
145
+ * quick 会话 = guard.allowedFiles;变更 = design §6 清单 ∪ task 卡 allowed_paths/target_files。
146
+ * 文件被本变更与他者**双方声明**时归 own:foreign 排除的职责是「他者的工作不混进我的
147
+ * 判定」,不是「我声明并真实修改的文件因他者(尤其未归档旧变更的陈旧 design 声明)也
148
+ * 声明过就被剔出我的对账」。读不出来(无 design/无 task 卡/quick guard 损坏)→ 空集
149
+ * (退回旧 foreign-first 行为,fail-closed 不放大放行面)。
150
+ * @returns {Set<string>} 正斜杠归一后的声明路径集
151
+ */
152
+ function loadOwnDeclaredSet(specBase, runtimeRoot, currentChangeName) {
153
+ const set = new Set()
154
+ if (!currentChangeName) return set
155
+ const norm = (p) => String(p || '').replace(/\\/g, '/')
156
+ const addAll = (paths) => { for (const p of paths) { const n = norm(p); if (n) set.add(n) } }
157
+ try {
158
+ if (/^quick-[0-9a-f]{8}$/.test(currentChangeName)) {
159
+ try {
160
+ const guard = JSON.parse(readFileSync(join(runtimeRoot, 'quick-sessions', currentChangeName, 'guard.json'), 'utf8'))
161
+ addAll(Array.isArray(guard?.allowedFiles) ? guard.allowedFiles : [])
162
+ } catch { /* guard 缺失/损坏 → 空集退回旧行为 */ }
163
+ return set
164
+ }
165
+ const changeDir = join(specBase, 'changes', currentChangeName)
166
+ try { addAll(parseFileChangeListDetailed(join(changeDir, 'design.md')).map(e => e.path)) } catch {}
167
+ let taskFiles = []
168
+ try { taskFiles = readdirSync(join(changeDir, 'tasks')).filter(f => /^task-\d+\.md$/.test(f)) } catch {}
169
+ for (const tf of taskFiles) {
170
+ try {
171
+ const content = readFileSync(join(changeDir, 'tasks', tf), 'utf8')
172
+ addAll(parseAllowedPaths(content))
173
+ addAll(parseTargetFiles(content).entries.map(e => e.path))
174
+ } catch { /* 单卡损坏跳过 */ }
175
+ }
176
+ } catch { /* 整体异常 → 空集 */ }
177
+ return set
178
+ }
179
+
141
180
  /**
142
181
  * 按他者声明集切分 diff 文件(collectForeignDeclaredFiles 的消费端快捷封装)。
182
+ *
183
+ * own 优先(2026-09-09 坑 verify-reconcile-own-file-foreign-false-positive 修复):文件在
184
+ * 本变更 own 声明集(design §6 / task allowed_paths / target_files / quick guard)内时
185
+ * 永不判 foreign——哪怕他者变更(含未归档旧变更的陈旧声明,其活性被本变更自己的 dirty
186
+ * 反向喂活)也声明过。双方都声明 = 归属歧义按「自己声明优先」收敛:本变更的 dirty 是
187
+ * 自己工作的一部分,剔除它只会制造「声明未做」假红。
188
+ *
143
189
  * @param {string} cwd 项目根
144
190
  * @param {string|null} currentChangeName 本变更名
145
191
  * @param {string[]|null} diffFiles 待切分文件列表(null 透传,保持调用方 null 语义)
@@ -148,12 +194,17 @@ export function collectForeignDeclaredFiles(cwd, currentChangeName, opts = {}) {
148
194
  */
149
195
  export function splitOwnVsForeignDiffFiles(cwd, currentChangeName, diffFiles, opts = {}) {
150
196
  if (!Array.isArray(diffFiles)) return { own: diffFiles, foreign: [] }
151
- const foreignMap = collectForeignDeclaredFiles(cwd, currentChangeName, opts)
197
+ const specBase = opts.specBase || join(cwd, '.sillyspec')
198
+ const runtimeRoot = opts.runtimeRoot || join(specBase, '.runtime')
199
+ const foreignMap = collectForeignDeclaredFiles(cwd, currentChangeName, { specBase, runtimeRoot })
152
200
  if (foreignMap.size === 0) return { own: diffFiles, foreign: [] }
201
+ const ownDeclared = loadOwnDeclaredSet(specBase, runtimeRoot, currentChangeName)
153
202
  const own = []
154
203
  const foreign = []
155
204
  for (const f of diffFiles) {
156
- const owners = foreignMap.get(String(f).replace(/\\/g, '/'))
205
+ const n = String(f).replace(/\\/g, '/')
206
+ if (ownDeclared.size > 0 && ownDeclared.has(n)) { own.push(f); continue } // own 优先
207
+ const owners = foreignMap.get(n)
157
208
  if (owners) foreign.push({ file: f, owners })
158
209
  else own.push(f)
159
210
  }