sillyspec 3.27.9 → 3.27.12

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/src/workflow.js CHANGED
@@ -583,6 +583,10 @@ export function saveWorkflowRun(result, options = {}) {
583
583
  workflow: result.workflow,
584
584
  project: result.project,
585
585
  status: result.status,
586
+ // 坑 archive-batch-31-tool-notes ①:status 按步骤口径而非 workflow 整体口径时
587
+ // 标注来源(如 step:extract-module-impact——整体 status 因下一步角色恒 fail,见
588
+ // complete-handlers.js archive 分支注释),排查者据此区分「本步失败」与「口径说明」。
589
+ ...(result.status_scope ? { status_scope: result.status_scope } : {}),
586
590
  spec_version: result.spec_version,
587
591
  roles: result.roles,
588
592
  workflow_checks: result.workflow_checks,
@@ -842,10 +842,10 @@ export function applyWorktree(changeName, { cwd, checkOnly = false, merge = fals
842
842
  const marker = `sillyspec apply ${changeName} --stash-dirty ${new Date().toISOString()}`;
843
843
  const before = (gitQuiet(projectRoot, ['stash', 'list']) || '').split('\n').filter(Boolean).length;
844
844
  // pathspec 限定 stash 范围与探针/4.5 同口径(排除 .sillyspec/ 运行时与他者 spec 产物、
845
- // .claude/docs/CLAUDE.md)——裸 push -u 会把排除项的未跟踪文件一并卷走,恢复时与
845
+ // .claude/docs/CLAUDE.md/AGENTS.md)——裸 push -u 会把排除项的未跟踪文件一并卷走,恢复时与
846
846
  // apply 期间重建的 spec 文件 already exists 互踩(实证 dbg)。
847
847
  git(projectRoot, ['stash', 'push', '--include-untracked', '-m', marker,
848
- '--', '.', ':(exclude).sillyspec/', ':(exclude).claude/', ':(exclude)docs/', ':(exclude)CLAUDE.md'], { timeout: 60000 });
848
+ '--', '.', ':(exclude).sillyspec/', ':(exclude).claude/', ':(exclude)docs/', ':(exclude)CLAUDE.md', ':(exclude)AGENTS.md'], { timeout: 60000 });
849
849
  const headEntry = ((gitQuiet(projectRoot, ['stash', 'list']) || '').split('\n')[0] || '');
850
850
  const after = (gitQuiet(projectRoot, ['stash', 'list']) || '').split('\n').filter(Boolean).length;
851
851
  const sha = gitQuiet(projectRoot, ['rev-parse', '--verify', 'stash@{0}']);
@@ -884,12 +884,12 @@ export function applyWorktree(changeName, { cwd, checkOnly = false, merge = fals
884
884
  // (这些文件 apply 前无未提交修改,还原 = apply 前状态),无关脏文件不在 patch/rollback 范围
885
885
  // 不受影响 → fail-safe,报错后 stash 重试即可。放行时 warning 提示此路径。
886
886
  // 排除非交付物的元数据/文档 churn(execute 自身改的 + 多操作者常改的 agent 指引/文档),
887
- // 否则别人改 CLAUDE.md/docs/.claude → 判定 dirty → apply 误阻断(多操作者仓库高频踩坑)。
887
+ // 否则别人改 CLAUDE.md/AGENTS.md/docs/.claude → 判定 dirty → apply 误阻断(多操作者仓库高频踩坑)。
888
888
  // 注意:排除规则必须和 computeBaselineHash (worktree.js) 一致(虽已不比对 hash,仍用同一口径判当前 dirty)。
889
889
  if (meta.baselineHash) {
890
890
  // pathspec:`--` 结束选项,`.` 包含全部,后续 :(exclude) 排除非交付物元数据/文档 churn
891
891
  // (与 computeBaselineHash 同口径)。数组形式逐元素传递,:(exclude) magic 字面直传不经 shell。
892
- const exclude = ['--', '.', ':(exclude).sillyspec/', ':(exclude).claude/', ':(exclude)docs/', ':(exclude)CLAUDE.md'];
892
+ const exclude = ['--', '.', ':(exclude).sillyspec/', ':(exclude).claude/', ':(exclude)docs/', ':(exclude)CLAUDE.md', ':(exclude)AGENTS.md'];
893
893
  const staged = gitQuiet(projectRoot, ['diff', '--cached', ...exclude]) || '';
894
894
  const unstaged = gitQuiet(projectRoot, ['diff', ...exclude]) || '';
895
895
  const untracked = gitQuiet(projectRoot, ['ls-files', '--others', '--exclude-standard', ...exclude]) || '';
@@ -1377,6 +1377,60 @@ function rollbackApply(projectRoot, trackedFiles, newFiles, rawStderr = '') {
1377
1377
  * @param {object} p.result - applyWorktree 的 result(预对齐信息写入 result.warnings,可追溯,约束④)
1378
1378
  * @returns {{ aligned: string[], commit: string|null }} aligned=本次对齐的文件(空数组=未对齐)
1379
1379
  */
1380
+ /**
1381
+ * merge 前自动 commit worktree 未提交交付物(坑 apply-merge-uncommitted-noop,2026-08-28 实证:
1382
+ * 子代理默认不 commit,分支 tip 只有 baseline checkpoint → git merge 空转「Already up to date」/
1383
+ * --no-ff 空合并,交付零落地,落地校验报错后只能手工 cherry-pick/cp 补救)。
1384
+ * 默认 patch 路径本就能吃未提交内容(git diff <base> 到工作区);只有 --merge/降级 merge 路径
1385
+ * 只看已提交——在此补上衔接。与 preAlignBaselineToMain 同款 pathspec commit(坑
1386
+ * git-commit-sweeps-prestaged:不扫入其他 staged 改动)。
1387
+ *
1388
+ * best-effort:commit 失败只 warning,交由下方落地校验兜底报错(不劣于原行为)。
1389
+ * @returns {{ committed: string[], skipped: boolean, shortHash: string|null }}
1390
+ */
1391
+ function autoCommitWorktreeWip({ meta, result, changeName }) {
1392
+ const out = { committed: [], skipped: true, shortHash: null };
1393
+ const worktreePath = meta?.worktreePath;
1394
+ if (!worktreePath || !existsSync(worktreePath)) return out;
1395
+ if (meta.mode && meta.mode !== 'worktree') return out;
1396
+
1397
+ // 未提交交付物 = tracked-modified(vs HEAD)∪ untracked,过 filterDeliverableFiles(排 meta.json/.sillyspec)
1398
+ let wip = [];
1399
+ try {
1400
+ const modRaw = gitQuiet(worktreePath, ['diff', '--name-only', 'HEAD']);
1401
+ const untrackedRaw = gitQuiet(worktreePath, ['ls-files', '--others', '--exclude-standard']);
1402
+ wip = filterDeliverableFiles([
1403
+ ...new Set([...(modRaw || '').split('\n'), ...(untrackedRaw || '').split('\n')].filter(Boolean)),
1404
+ ]);
1405
+ } catch { return out; }
1406
+ if (wip.length === 0) return out;
1407
+
1408
+ try {
1409
+ git(worktreePath, ['add', '-A', '--', ...wip]);
1410
+ try {
1411
+ git(worktreePath, ['commit', '--no-verify', '-m',
1412
+ `sillyspec: auto-commit worktree WIP (${wip.length} files, apply --merge)`, '--', ...wip]);
1413
+ } catch {
1414
+ // 仓级 git 身份缺失时 commit 拒绝——补 -c 身份重试一次(worktree 继承不到全局配置的形态)
1415
+ git(worktreePath, ['-c', 'user.name=sillyspec', '-c', 'user.email=sillyspec@apply.local',
1416
+ 'commit', '--no-verify', '-m',
1417
+ `sillyspec: auto-commit worktree WIP (${wip.length} files, apply --merge)`, '--', ...wip]);
1418
+ }
1419
+ out.committed = wip;
1420
+ out.skipped = false;
1421
+ out.shortHash = gitQuiet(worktreePath, ['rev-parse', '--short', 'HEAD']);
1422
+ result.warnings = result.warnings || [];
1423
+ result.warnings.push(
1424
+ `worktree 有 ${wip.length} 个未提交交付文件(子代理未 commit),已在分支自动 commit` +
1425
+ `${out.shortHash ? `(${out.shortHash})` : ''} 再 merge:${wip.slice(0, 5).join(', ')}${wip.length > 5 ? ' 等' : ''}`
1426
+ );
1427
+ } catch (e) {
1428
+ result.warnings = result.warnings || [];
1429
+ result.warnings.push(`worktree WIP 自动 commit 失败(merge 可能空转,落地校验将报未落地):${(e.message || '').split('\n')[0]}`);
1430
+ }
1431
+ return out;
1432
+ }
1433
+
1380
1434
  function preAlignBaselineToMain({ meta, branch, projectRoot, result }) {
1381
1435
  const pushWarning = (msg) => {
1382
1436
  result.warnings = result.warnings || [];
@@ -1495,6 +1549,10 @@ export function applyByMerge(result, changeName, projectRoot, wm, opts = {}) {
1495
1549
  // --- merge 前预对齐(D-002@v1 / task-02):baseline 并行旧文件对齐 main 版,消除 merge 冲突主因 ---
1496
1550
  preAlignBaselineToMain({ meta, branch, projectRoot, result });
1497
1551
 
1552
+ // --- merge 前自动 commit 未提交交付物(坑 apply-merge-uncommitted-noop):预对齐在前——
1553
+ // 它跳过 dirty 文件(不覆盖 WIP)且只提交对齐集;WIP commit 在后,两者不混提交 ---
1554
+ autoCommitWorktreeWip({ meta, result, changeName });
1555
+
1498
1556
  try {
1499
1557
  git(projectRoot, ['merge', '--no-ff', branch], { timeout: 30000 });
1500
1558
  } catch (e) {
package/src/worktree.js CHANGED
@@ -226,9 +226,9 @@ export function computeBaselineHash(cwd) {
226
226
  // - .sillyspec/:brainstorm/plan 蓝图 + runtime 产物
227
227
  // - .claude/:agent 配置/skills/CLAUDE.md(多操作者 agent 指引 churn)
228
228
  // - docs/:文档(非代码交付物)
229
- // - CLAUDE.md:根 agent 指引(多操作者常改)
229
+ // - CLAUDE.md / AGENTS.md:根 agent 指引(多操作者常改;AGENTS.md 为内容源、CLAUDE.md 为指针)
230
230
  // 必须和 applyWorktree step 4.5 (worktree-apply.js) 使用相同的排除规则。
231
- const exclude = ['--', '.', ':(exclude).sillyspec/', ':(exclude).claude/', ':(exclude)docs/', ':(exclude)CLAUDE.md'];
231
+ const exclude = ['--', '.', ':(exclude).sillyspec/', ':(exclude).claude/', ':(exclude)docs/', ':(exclude)CLAUDE.md', ':(exclude)AGENTS.md'];
232
232
  const staged = gitQuiet(cwd, ['diff', '--cached', ...exclude], { timeout: 30000 }) || '';
233
233
  const unstaged = gitQuiet(cwd, ['diff', ...exclude], { timeout: 30000 }) || '';
234
234
  const untracked = gitQuiet(cwd, ['ls-files', '--others', '--exclude-standard', ...exclude], { timeout: 30000 }) || '';
@@ -1632,7 +1632,9 @@ export class WorktreeManager {
1632
1632
  }
1633
1633
 
1634
1634
  /**
1635
- * DB 归档态探测(坑 doctor-reprovision-archived-change):changes 表 status='archived'。
1635
+ * DB 终态探测(坑 doctor-reprovision-archived-change):changes 表 status='archived'
1636
+ * (归档收尾)或 'deleted'(change-delete 删除,2026-08-30)——两种终态的 worktree 都是
1637
+ * 死目录,doctor 一致跳过供给、fix 走 cleanup。
1636
1638
  * 只读直查(node:sqlite)——doctor 是同步方法,惰性动态 import 不可用;DB 不存在/无行/
1637
1639
  * 读失败 → false(保守退回普通 deps 供给路径)。
1638
1640
  * @param {string} name
@@ -1645,7 +1647,7 @@ export class WorktreeManager {
1645
1647
  const db = openDatabase(dbPath, { readOnly: true });
1646
1648
  try {
1647
1649
  const row = db.prepare('SELECT status FROM changes WHERE name = ?').get(name);
1648
- return row?.status === 'archived';
1650
+ return row?.status === 'archived' || row?.status === 'deleted';
1649
1651
  } finally { try { db.close() } catch {} }
1650
1652
  } catch { return false }
1651
1653
  }
@@ -1,4 +1,4 @@
1
- # Claude Code 指引
1
+ # Agent 指引
2
2
 
3
3
  ## 项目说明
4
4
  本项目使用 **SillySpec** 管理变更,采用文档驱动开发。SillySpec 是给 Agent 调用的 CLI 流程控制器,不是给人类直接使用的产品,也不是处理业务逻辑的工具——你(Agent)通过 CLI 告诉它"我在哪",它告诉你"下一步做什么";你执行步骤,它校验产出、推进状态,人类只在关键决策点介入审批。要考虑多 agent 同时操作代码,代码随时可能变化。所有变更以稳定、可用、可维护为目标,按生产级标准处理。
@@ -15,7 +15,7 @@
15
15
  9. **中途停下不靠额外命令存进度**——进度已由上一次 `--done` 自动落盘;恢复时用 `sillyspec progress show` 查看进度,再用 `sillyspec run <stage>` 续跑,不直接 commit 半成品。
16
16
  10. **实现完成后对照文档验收**(design.md / 模块文档),并检查是否影响已有测试。
17
17
  11. **非测试逻辑本身有误时,禁止改测试来"通过"**——修逻辑,不修测试。
18
- 12. **hook 拦截提交时禁止跳过**(`.husky/pre-push`),修复问题后再提交。
18
+ 12. **git hook 拦截提交时禁止跳过**(如 `.husky/pre-push`),修复问题后再提交。
19
19
  13. **代码必须兼容 Windows / Linux / macOS**(路径 / 换行 / 并发都要顾)。
20
20
  14. **任务记录隔离**:永不重置 / reset / 清零已存在的 change;多个活跃 change 各自 `--change <名>` 隔离不重叠;quick 同一 QUICKLOG 按 ql-ID 条目追加,不冲突。
21
21
  15. **quicklog 结构化落盘**:末步 `--done` 用四参数 `--req/--cause/--solution/--result`(CLI 合成结构化 output 并自动提取标题/四段分行);文件括注用 `--file-notes "path::注 || path2"`。骨架由 CLI 接管,`--done` 后按需核对即可,勿手拼模板。
@@ -26,7 +26,8 @@ grep -rl "<关键词>" <源码目录>/ --include="*.java" --include="*.js" --inc
26
26
  6. 任意 P0/P1 unresolved/blocking 决策标记为 FAIL blocker
27
27
 
28
28
  **探针 5 补充(CLI 已出对账表,你做诚实判定)**
29
- CLI 的 missing backend endpoint 是 advisory(不硬阻断归档)——但 contract gap 是真实集成缺陷,应诚实标 FAIL 并回 execute 补端点,勿因 CLI 不拦而放行。后端 router 的端点清单可用 `sillyspec endpoints extract --change <变更名> --task <task-NN>` 重新静态提取(勿手扫装饰器)。
29
+ CLI 的 missing backend endpoint 是 advisory(不硬阻断归档)——但 contract gap 是真实集成缺陷,应诚实标 FAIL 并回 execute 补端点,勿因 CLI 不拦而放行。后端 router 的端点清单用 `sillyspec endpoints extract --change <变更名> --all-tasks` 聚合提取(逐 task 卡各自落产物,与对账聚合口径一致;勿手扫装饰器、勿只提单 task——单 task 产物对账会放大假 missing)。
30
+ 先看对账表 scope 标注:`change-diff`(含 apply-pathspec 兜底)口径可信;若显示 `full-repo`(diff 与 apply 清单都不可得时的兜底),missing 大概率是「全仓调用 × 本变更局部端点」的口径错配噪音——先收窄复跑(`sillyspec verify-probes --change <变更名>`)再判定,不要把口径噪音直接记成 contract gap。
30
31
 
31
32
  **探针 6 补充(CLI 已出三态判定,你做终审)**
32
33
  以 git 事实为准(真实 > 声明);CLI 的三态(合规/高风险/未声明)是机械判定,是否构成 FAIL blocker 由你诚实判定,务必如实记录——静默删除代码是 verify 的最大盲区。