sillyspec 3.33.4 → 3.33.6
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-config/SKILL.md +70 -0
- package/.claude/skills/sillyspec-push/SKILL.md +79 -0
- package/AGENTS.md +1 -0
- package/README.md +2 -0
- package/SKILL.md +2 -0
- package/assets/command-cards/push.md +38 -0
- package/package.json +2 -2
- package/src/command-cards.js +1 -1
- package/src/config-schema.js +50 -0
- package/src/config-validate.js +180 -0
- package/src/deleted-ledger.js +3 -1
- package/src/flow-parity.js +62 -2
- package/src/flow-preflight.js +67 -0
- package/src/flow-review.js +19 -3
- package/src/flow.js +318 -70
- package/src/git-helper.js +22 -6
- package/src/index.js +48 -5
- package/src/knowledge-scope.js +82 -0
- package/src/progress/change-registry.js +15 -1
- package/src/push.js +934 -0
- package/src/review-dispatch.js +99 -0
- package/src/run/complete-handlers.js +49 -14
- package/src/run/quick-audit.js +4 -2
- package/src/run/shared.js +1 -1
- package/src/stages/knowledge.js +44 -1
- package/src/verify-postcheck.js +177 -10
- package/templates/agents-instruction.md +2 -1
package/src/git-helper.js
CHANGED
|
@@ -96,23 +96,31 @@ function gitChildEnv(env) {
|
|
|
96
96
|
* 安全执行 git:失败不抛,返回 { value, error } 结构。
|
|
97
97
|
* @param {string} cwd
|
|
98
98
|
* @param {string[]} args
|
|
99
|
-
* @param {{ trim?: boolean, timeout?: number, retryOnTimeout?: boolean }} [opts]
|
|
99
|
+
* @param {{ trim?: boolean, timeout?: number, retryOnTimeout?: boolean, input?: string|Buffer, stdio?: string[] }} [opts]
|
|
100
100
|
* - trim:false 保留原样输出(git status --porcelain 首行前导空格
|
|
101
101
|
* 是状态码的一部分,trim 会削掉致 parsePorcelainPath 丢首字符,坑见 auditQuickCompletion 注释)
|
|
102
102
|
* - timeout 默认 5000ms,长操作(大 diff / worktree list)可传更大值
|
|
103
103
|
* - retryOnTimeout:true 时,ETIMEDOUT 用 2× timeout 重试一次(默认 false 向后兼容)。
|
|
104
104
|
* 机器忙时 git 子进程启动慢易瞬时超时;审计锚点 git 调用失败即 blocked 中断 quick,
|
|
105
105
|
* 重试一次能消化绝大多数瞬时抖动,免去用户手工重跑。权限 / 损坏等非超时错误不重试(救不回)。
|
|
106
|
+
* - input: 写入子进程 stdin;传入时自动把 stdio[0] 设为 pipe(否则 Node 静默丢弃 input)
|
|
107
|
+
* - stdio: 显式覆盖子进程 stdio
|
|
106
108
|
* @returns {{ value: string|null, error: string|null }}
|
|
107
109
|
*/
|
|
108
110
|
export function safeGit(cwd, args, opts = {}) {
|
|
109
|
-
const { trim = true, timeout = 5000, retryOnTimeout = false } = opts
|
|
111
|
+
const { trim = true, timeout = 5000, retryOnTimeout = false, input, stdio = null } = opts
|
|
110
112
|
const fullArgs = buildFullArgs(cwd, args)
|
|
113
|
+
// stdin 形态:input 传入时必须把 stdin 设为 pipe——Node 的 execFileSync 在
|
|
114
|
+
// stdio[0]='ignore' 时会静默丢弃 input(commit-tree -F - 会收到空消息)。
|
|
115
|
+
const childStdio = stdio || (input === undefined ? ['ignore', 'pipe', 'pipe'] : ['pipe', 'pipe', 'pipe'])
|
|
111
116
|
// 单次尝试:返回 { value, errorObj }(errorObj 为原始 Error 或 null),不在内部格式化 message。
|
|
112
117
|
// 格式化(取首行)推迟到最终返回,避免重试分支重复格式化,并保留原始 code 供重试判定。
|
|
113
118
|
const attempt = (t) => {
|
|
114
119
|
try {
|
|
115
|
-
let value = execFileSync('git', fullArgs, {
|
|
120
|
+
let value = execFileSync('git', fullArgs, {
|
|
121
|
+
encoding: 'utf8', timeout: t, maxBuffer: GIT_MAX_BUFFER, stdio: childStdio,
|
|
122
|
+
...(input === undefined ? {} : { input }), env: gitChildEnv(), windowsHide: true,
|
|
123
|
+
})
|
|
116
124
|
if (trim) value = value.trim()
|
|
117
125
|
return { value, errorObj: null }
|
|
118
126
|
} catch (e) {
|
|
@@ -131,18 +139,26 @@ export function safeGit(cwd, args, opts = {}) {
|
|
|
131
139
|
* 调用方自行 catch;不经 shell,参数以数组元素传递。
|
|
132
140
|
* @param {string} cwd
|
|
133
141
|
* @param {string[]} args
|
|
134
|
-
* @param {{ trim?: boolean, timeout?: number, encoding?: string }} [opts]
|
|
142
|
+
* @param {{ trim?: boolean, timeout?: number, encoding?: string, env?: NodeJS.ProcessEnv, input?: string|Buffer, stdio?: string[] }} [opts]
|
|
135
143
|
* - encoding:'buffer' 返回原始 Buffer(git diff --binary 等**二进制输出**专用——
|
|
136
144
|
* utf8 解码会破坏 NUL 字节使补丁不可应用;Buffer 输出跳过 trim)
|
|
145
|
+
* - input: 写入子进程 stdin(如 commit-tree -F - 的多行消息);传入时自动把 stdio[0] 设为 pipe
|
|
146
|
+
* - stdio: 显式覆盖子进程 stdio(push 需要实时透传 hook 输出时用 inherit)
|
|
137
147
|
* @returns {string|Buffer}
|
|
138
148
|
*/
|
|
139
149
|
export function git(cwd, args, opts = {}) {
|
|
140
|
-
const { trim = true, timeout = 5000, encoding = 'utf8', env } = opts
|
|
150
|
+
const { trim = true, timeout = 5000, encoding = 'utf8', env, input, stdio = null } = opts
|
|
141
151
|
const fullArgs = buildFullArgs(cwd, args)
|
|
142
152
|
// env(终批-③):可选注入(如 baseline checkpoint 的临时 GIT identity)——调用方必须
|
|
143
153
|
// 用 { ...process.env, GIT_xxx } 展开形式,裸替换会丢 SystemRoot/USERPROFILE/TEMP(Windows);
|
|
144
154
|
// 最终经 gitChildEnv 展开合并并权威注入 GIT_OPTIONAL_LOCKS=0(调用方 GIT 键照常透传)
|
|
145
|
-
|
|
155
|
+
// stdio:input 传入时 stdin 必须 pipe——Node 在 stdio[0]='ignore' 下会静默丢弃 input
|
|
156
|
+
// (commit-tree -F - 收到空消息);push 用 inherit 透传 pre-push hook 输出。
|
|
157
|
+
const childStdio = stdio || (input === undefined ? ['ignore', 'pipe', 'pipe'] : ['pipe', 'pipe', 'pipe'])
|
|
158
|
+
const value = execFileSync('git', fullArgs, {
|
|
159
|
+
encoding, timeout, maxBuffer: GIT_MAX_BUFFER, stdio: childStdio,
|
|
160
|
+
...(input === undefined ? {} : { input }), env: gitChildEnv(env), windowsHide: true,
|
|
161
|
+
})
|
|
146
162
|
if (Buffer.isBuffer(value)) return value
|
|
147
163
|
return trim ? value.trim() : value
|
|
148
164
|
}
|
package/src/index.js
CHANGED
|
@@ -119,7 +119,7 @@ SillySpec CLI — 规范驱动开发工具包
|
|
|
119
119
|
sillyspec fourpiece-init --change <name> 生成 proposal/requirements/decisions 骨架(frontmatter+章节+模板占位;已存在不覆盖)
|
|
120
120
|
sillyspec delta --change <name> [--json] 生成变更 delta.md(Before/Delta/After 三段式聚合;幂等覆盖重跑即刷新)
|
|
121
121
|
sillyspec next 项目状态探测:输出当前状态 + 下一步命令 + 依据(吸收 continue/resume 手工探测表)
|
|
122
|
-
sillyspec commit [--json] 智能提交建议:收集 QUICKLOG/已勾 task/阶段产出语义,生成建议 message
|
|
122
|
+
sillyspec commit [--json] 智能提交建议:收集 QUICKLOG/已勾 task/阶段产出语义,生成建议 message(只建议不执行);sillyspec push [--dry-run] [--mode by-change|one|none] [--change <名>] [--no-push] [--undo] [--json] 推送前压扁本地未推送提交并快进推送(默认按变更分段;不改写已推送历史、不 force)
|
|
123
123
|
sillyspec verify-probes --change <name> [--init [--force]] verify 机械探针(TODO 标记/测试覆盖/API 对账/删除对账);--init 生成 verify-result.md 骨架(--force 覆盖重生成,手填内容会重置)
|
|
124
124
|
sillyspec module-impact --change <name> 生成 module-impact.md 骨架(文件×模块归属按 module-map 预填 + 未匹配清单)
|
|
125
125
|
sillyspec scope-audit --change <name> [--json] 变更范围对账:计划×实际三态全表 + 行数(✓ 计划内/⚠️ 计划外/⚠️ 计划未动;quick 会话传 quick-<id> 出归属表;跨仓条目按 local.yaml repos 分仓真实对账——--json 出 repos[] 仓库维度汇总;已归档变更可查——快照记录态;advisory 只读不设门禁)
|
|
@@ -458,7 +458,7 @@ async function main() {
|
|
|
458
458
|
// 1-2 个交互来回)。守卫建议的事(cd 回主仓根重跑)自动做掉:⚠️警告 + 重锚定主仓根执行——
|
|
459
459
|
// 写入恒落主仓(单源不变量延续,worktree 副本零 CLI 写入);刻意以本 worktree 作独立实例跑
|
|
460
460
|
// 的显式路径(--allow-worktree-cwd --spec-dir)在警告中原样保留;wt-commit 豁免不变。
|
|
461
|
-
if (!args.includes('--allow-worktree-cwd') && command !== 'wt-commit') {
|
|
461
|
+
if (!args.includes('--allow-worktree-cwd') && command !== 'wt-commit' && command !== 'push') {
|
|
462
462
|
const guardDir = args.includes('--dir') ? dir : process.cwd();
|
|
463
463
|
const wtInfo = detectCwdInsideWorktree(guardDir);
|
|
464
464
|
if (wtInfo) {
|
|
@@ -5395,6 +5395,7 @@ SillySpec config — local.yaml 配置键速查
|
|
|
5395
5395
|
sillyspec config known-failures list 列 known_failures 豁免键
|
|
5396
5396
|
sillyspec config known-failures add <key…> 增豁免键(行级手术保注释、原子写;坏 YAML 拒改)
|
|
5397
5397
|
sillyspec config known-failures remove <key…> 删豁免键(未命中静默忽略)
|
|
5398
|
+
sillyspec config validate [--json] 逐键校验 local.yaml(错误 exit 1;缺文件=零配置可用 exit 0)
|
|
5398
5399
|
|
|
5399
5400
|
说明:
|
|
5400
5401
|
- 数据源:src/config-schema.js(唯一真相;reader 见各键「读取点」)
|
|
@@ -5436,6 +5437,17 @@ SillySpec config — local.yaml 配置键速查
|
|
|
5436
5437
|
if (!content.endsWith('\n')) process.stdout.write('\n')
|
|
5437
5438
|
break;
|
|
5438
5439
|
}
|
|
5440
|
+
// config validate(2026-10-23-config-validate-skill):逐键校验(schema 单一源;YAML/
|
|
5441
|
+
// 类型/枚举/段内必填凭据闭合,未知键警告)——agent 写完配置的硬验证门(技能末步=本命令
|
|
5442
|
+
// exit 0 才算完成);缺文件=零配置可用 info exit 0(69 键仅凭据族真必填)。
|
|
5443
|
+
if (configSub === 'validate') {
|
|
5444
|
+
let specBase = null
|
|
5445
|
+
try { specBase = resolvePlatformSpecDir(dir, specDir) } catch { /* 同 cat:pointer 异常走祖先链 */ }
|
|
5446
|
+
const { cmdConfigValidate } = await import('./config-validate.js')
|
|
5447
|
+
const exitCode = cmdConfigValidate({ dir, specBase, json })
|
|
5448
|
+
process.exitCode = exitCode
|
|
5449
|
+
break;
|
|
5450
|
+
}
|
|
5439
5451
|
// config known-failures(2026-10-21-p0-p1-hardening FR-07 / dsh z2 实证:known_failures
|
|
5440
5452
|
// 无 CLI 管理面 → agent 手改 local.yaml,PS5.1 编码把 50KB 文件写成 mojibake 等用户
|
|
5441
5453
|
// 2h13m)。行级增删查:保注释、原子写、坏 YAML 拒改——消灭手改场景。
|
|
@@ -5456,10 +5468,10 @@ SillySpec config — local.yaml 配置键速查
|
|
|
5456
5468
|
break;
|
|
5457
5469
|
}
|
|
5458
5470
|
if (!isSchema) {
|
|
5459
|
-
const sug = didYouMean(configSub, ['schema', 'cat', 'known-failures']);
|
|
5471
|
+
const sug = didYouMean(configSub, ['schema', 'cat', 'validate', 'known-failures']);
|
|
5460
5472
|
console.error(`❌ 未知子命令: config ${configSub}`);
|
|
5461
5473
|
if (sug) console.error(` 你是想输入「config ${sug}」吗?`);
|
|
5462
|
-
console.error('用法: sillyspec config [schema|cat|known-failures] [--json] 打印配置键 / 读内容 / 管理 known_failures 键');
|
|
5474
|
+
console.error('用法: sillyspec config [schema|cat|validate|known-failures] [--json] 打印配置键 / 读内容 / 校验 / 管理 known_failures 键');
|
|
5463
5475
|
process.exit(2);
|
|
5464
5476
|
}
|
|
5465
5477
|
const { renderSchemaHuman, renderSchemaJson } = await import('./config-schema.js');
|
|
@@ -5762,8 +5774,39 @@ SillySpec pull — 拉取服务器 spec 快照到本地(X2 / FR-07)
|
|
|
5762
5774
|
printed = wAll ? wRes.events.length : wRes.warnings.length;
|
|
5763
5775
|
}
|
|
5764
5776
|
}
|
|
5777
|
+
case 'push': {
|
|
5778
|
+
// 推送前压扁(2026-10-23-push-squash):显式命令,不接 pre-push hook 自动改写。
|
|
5779
|
+
// 默认 by-change,只处理 base..HEAD 本地未推送段;commit-tree + update-ref 保 index/工作区
|
|
5780
|
+
// 零变化,pre/post 备份 ref 可 undo;绝不 force / 不 --no-verify。--json 已由全局层剥离。
|
|
5781
|
+
// 放在 switch 末尾:避免插行造成 docs 里 index.js:N 的行号引用整体漂移。
|
|
5782
|
+
const { parsePushArgs, runPush, PUSH_USAGE } = await import('./push.js');
|
|
5783
|
+
const parsed = parsePushArgs(filteredArgs.slice(1));
|
|
5784
|
+
if (!parsed.ok) {
|
|
5785
|
+
for (const e of parsed.errors) console.error(`❌ ${e}`);
|
|
5786
|
+
console.error(PUSH_USAGE);
|
|
5787
|
+
process.exit(2);
|
|
5788
|
+
}
|
|
5789
|
+
if (parsed.options.help) {
|
|
5790
|
+
console.log(PUSH_USAGE);
|
|
5791
|
+
break;
|
|
5792
|
+
}
|
|
5793
|
+
const r = await runPush({ cwd: dir, specDir, ...parsed.options, json });
|
|
5794
|
+
if (json) {
|
|
5795
|
+
console.log(JSON.stringify(r.envelope, null, 2));
|
|
5796
|
+
for (const w of r.warnings || []) console.error(`⚠️ ${w}`);
|
|
5797
|
+
process.exitCode = r.exitCode;
|
|
5798
|
+
break;
|
|
5799
|
+
}
|
|
5800
|
+
for (const line of r.humanLines || []) console.log(line);
|
|
5801
|
+
for (const w of r.warnings || []) console.warn(`⚠️ ${w}`);
|
|
5802
|
+
if (!r.ok) {
|
|
5803
|
+
for (const e of r.errors || []) console.error(`❌ ${e}`);
|
|
5804
|
+
process.exitCode = r.exitCode || 1;
|
|
5805
|
+
}
|
|
5806
|
+
break;
|
|
5807
|
+
}
|
|
5765
5808
|
default: {
|
|
5766
|
-
const topCommands = ['init', 'setup', 'run', 'progress', 'worktree', 'dispatch', 'agent-log', 'local', 'workflow', 'gate', 'derive', 'backfill-reviews', 'register-stage-review', 'modules', 'change-rename', 'change-delete', 'knowledge', 'platform', 'pull', 'scan', 'brainstorm', 'plan', 'execute', 'verify', 'archive', 'quick', 'explore', 'status', 'doctor', 'auto', 'runtime', 'watcher'];
|
|
5809
|
+
const topCommands = ['init', 'setup', 'run', 'progress', 'worktree', 'dispatch', 'agent-log', 'local', 'workflow', 'gate', 'derive', 'backfill-reviews', 'register-stage-review', 'modules', 'change-rename', 'change-delete', 'knowledge', 'platform', 'pull', 'scan', 'brainstorm', 'plan', 'execute', 'verify', 'archive', 'quick', 'explore', 'status', 'doctor', 'auto', 'runtime', 'watcher', 'commit', 'push'];
|
|
5767
5810
|
const suggestion = didYouMean(command, topCommands);
|
|
5768
5811
|
console.error(`❌ 未知命令: ${command}`);
|
|
5769
5812
|
if (command === '--status') {
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* knowledge-scope.js — 知识注入作用域解析(2026-10-23-knowledge-scope-inject)
|
|
3
|
+
*
|
|
4
|
+
* 背景:知识注入此前只有「触达域 FR + 关键词命中」两类——AGENTS 规约 00(不枚举定义
|
|
5
|
+
* 开放世界/不锁死开发语言框架)一直在指令文件里,但决策时点没有「每次都注入」通道,
|
|
6
|
+
* agent 在配置/机制设计上犯语言锁定即实证(py_runner_flags 一案)。本模块让知识条目
|
|
7
|
+
* 声明作用域,注入逻辑按类分流:
|
|
8
|
+
* - always:每次变更都注入(跨仓通用规约,如工具中立性)
|
|
9
|
+
* - repo:本仓库每次变更都注入(仓级约束)
|
|
10
|
+
* - module:<域>[,<域>]:仅当触达域(resolveTouchedDomains 同源)命中时注入
|
|
11
|
+
* - 无标记:关键词命中现状(不恒注入)
|
|
12
|
+
*
|
|
13
|
+
* 标记形态(HTML 注释,主观内容用 HTML 注释不污染渲染;置于 section heading 之后的
|
|
14
|
+
* 本节内任意位置有效):
|
|
15
|
+
* <!-- scope: always -->
|
|
16
|
+
* <!-- scope: repo -->
|
|
17
|
+
* <!-- scope: module:cli-entry,core-engine -->
|
|
18
|
+
*
|
|
19
|
+
* 条目粒度 = section(heading)——knowledge 的 INDEX 路由与命中面都以 file#heading 为
|
|
20
|
+
* 单位,标记跟随原文、旧知识零迁移。纯读 fail-soft(坏文件/不可读跳过,异常返回空态)。
|
|
21
|
+
*/
|
|
22
|
+
import { readdirSync, readFileSync } from 'fs'
|
|
23
|
+
import { join } from 'path'
|
|
24
|
+
|
|
25
|
+
const SCOPE_RE = /<!--\s*scope\s*:\s*(always|repo|module\s*:\s*([^>]+?))\s*-->/i
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* 扫描 knowledgeRoot 顶层 *.md(INDEX.md 除外)收集作用域条目。
|
|
29
|
+
* @returns {{ always: Array, repo: Array, modules: Record<string, Array> }}
|
|
30
|
+
* 条目形如 { id: 'file.md#heading', file, heading, summary, scope, domains? }
|
|
31
|
+
*/
|
|
32
|
+
export function collectScopedKnowledge(knowledgeRoot) {
|
|
33
|
+
const out = { always: [], repo: [], modules: {} }
|
|
34
|
+
let entries = []
|
|
35
|
+
try { entries = readdirSync(knowledgeRoot) } catch { return out }
|
|
36
|
+
for (const f of entries) {
|
|
37
|
+
if (!String(f).endsWith('.md') || f === 'INDEX.md') continue
|
|
38
|
+
let text
|
|
39
|
+
try { text = readFileSync(join(knowledgeRoot, f), 'utf8') } catch { continue }
|
|
40
|
+
let cur = null // { heading, scope, domains, summary }
|
|
41
|
+
const flush = () => {
|
|
42
|
+
if (!cur || !cur.scope) return
|
|
43
|
+
const entry = {
|
|
44
|
+
id: `${f}#${cur.heading}`,
|
|
45
|
+
file: f,
|
|
46
|
+
heading: cur.heading,
|
|
47
|
+
summary: cur.summary || '',
|
|
48
|
+
scope: cur.scope,
|
|
49
|
+
...(cur.scope === 'module' ? { domains: cur.domains } : {}),
|
|
50
|
+
}
|
|
51
|
+
if (cur.scope === 'always') out.always.push(entry)
|
|
52
|
+
else if (cur.scope === 'repo') out.repo.push(entry)
|
|
53
|
+
else for (const d of cur.domains) (out.modules[d] ||= []).push(entry)
|
|
54
|
+
}
|
|
55
|
+
for (const line of String(text).split(/\r?\n/)) {
|
|
56
|
+
const h = /^(#{1,6})\s+(.+?)\s*$/.exec(line)
|
|
57
|
+
if (h) { flush(); cur = { heading: h[2].trim(), scope: null, domains: [], summary: '' }; continue }
|
|
58
|
+
if (!cur) continue
|
|
59
|
+
const m = SCOPE_RE.exec(line)
|
|
60
|
+
if (m) {
|
|
61
|
+
const kind = m[1].toLowerCase()
|
|
62
|
+
if (kind === 'always' || kind === 'repo') { cur.scope = kind; cur.domains = [] }
|
|
63
|
+
else {
|
|
64
|
+
cur.scope = 'module'
|
|
65
|
+
cur.domains = String(m[2] || '').split(',').map((s) => s.trim()).filter(Boolean)
|
|
66
|
+
}
|
|
67
|
+
continue
|
|
68
|
+
}
|
|
69
|
+
// 首行有效正文作摘要(跳空行/注释/代码围栏),≤80 字
|
|
70
|
+
if (!cur.summary) {
|
|
71
|
+
const t = line.trim()
|
|
72
|
+
if (t && !t.startsWith('<!--') && !t.startsWith('```') && !t.startsWith('|')) {
|
|
73
|
+
cur.summary = t.slice(0, 80)
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
flush()
|
|
78
|
+
}
|
|
79
|
+
return out
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export default { collectScopedKnowledge }
|
|
@@ -513,6 +513,9 @@ export class ChangeRegistry {
|
|
|
513
513
|
* @param {string} cwd - 项目根目录
|
|
514
514
|
* @param {string} oldName - 旧变更名
|
|
515
515
|
* @param {string} newName - 新变更名
|
|
516
|
+
*
|
|
517
|
+
* 成功后旧名落删除台账 status='deleted'(2026-10-23-rename-platform-tombstone):
|
|
518
|
+
* 平台按 change_key 建行,旧平台名行靠 sync 台账墓碑分支收敛,防幽灵永挂。
|
|
516
519
|
*/
|
|
517
520
|
renameChange(cwd, oldName, newName) {
|
|
518
521
|
if (!oldName || !newName) {
|
|
@@ -588,7 +591,18 @@ export class ChangeRegistry {
|
|
|
588
591
|
} else {
|
|
589
592
|
mkdirSync(newDir, { recursive: true });
|
|
590
593
|
}
|
|
591
|
-
if (renamed)
|
|
594
|
+
if (renamed) {
|
|
595
|
+
console.log(`✅ 变更已重命名:${oldName} → ${newName}`);
|
|
596
|
+
// 平台旧名墓碑凭证(2026-10-23-rename-platform-tombstone,坑9 幽灵家族第三变种):
|
|
597
|
+
// 平台按 change_key 建行而 rename 只改本地,旧平台名行无终态凭证会永挂 active
|
|
598
|
+
// (2026-10-23-new-change-43a74c51 实证:自动建名 16 秒后被改名,平台幽灵)。
|
|
599
|
+
// 旧名对平台即终态弃名,语义用既有 'deleted' 承载(平台 _apply_cli_tombstone 只认
|
|
600
|
+
// deleted/archived,不扩表面);daemon reconcile / 显式 platform sync --change 旧名
|
|
601
|
+
// 时由 sync 台账分支合成墓碑收敛。仅 renamed(DB+目录双成)落账:missing/conflict/
|
|
602
|
+
// stolen/FS 回滚分支到此为止(行还叫旧名时不得有其终态台账)。fail-open 同删除/归档。
|
|
603
|
+
appendDeletedLedger(this.pm._getSpecDir(cwd), oldName, 'deleted');
|
|
604
|
+
console.log('ℹ️ 旧名已记删除台账(status=deleted),平台旧名行将由墓碑同步收敛');
|
|
605
|
+
}
|
|
592
606
|
}
|
|
593
607
|
|
|
594
608
|
/**
|