@mingmaple/dsh-handover 0.9.14 → 1.0.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.
Files changed (102) hide show
  1. package/README.md +123 -92
  2. package/docs/migrate.md +52 -41
  3. package/docs/releases/0.9.12.md +2 -3
  4. package/docs/releases/0.9.14.md +1 -5
  5. package/docs/releases/0.9.22.md +34 -0
  6. package/docs/releases/1.0.0.md +33 -0
  7. package/docs/troubleshoot.md +230 -46
  8. package/package.json +74 -71
  9. package/scripts/behavior.mjs +398 -0
  10. package/scripts/blank-dsh.mjs +41 -25
  11. package/scripts/blank-smoke.mjs +278 -109
  12. package/scripts/check-docs.mjs +7 -0
  13. package/scripts/e2e-migration.mjs +2 -2
  14. package/scripts/office.mjs +3 -3
  15. package/scripts/portability.mjs +71 -0
  16. package/scripts/style-gate.mjs +571 -0
  17. package/src/client.js +1028 -955
  18. package/src/index.mjs +313 -142
  19. package/src/lib/absrefs.mjs +1 -1
  20. package/src/lib/api.mjs +18 -10
  21. package/src/lib/archive.mjs +1 -1
  22. package/src/lib/brief.mjs +8 -6
  23. package/src/lib/config.mjs +23 -8
  24. package/src/lib/consent.mjs +54 -1
  25. package/src/lib/docmap.mjs +140 -0
  26. package/src/lib/drift.mjs +1 -1
  27. package/src/lib/events.mjs +1 -1
  28. package/src/lib/facts.mjs +19 -12
  29. package/src/lib/gates.mjs +30 -6
  30. package/src/lib/global-task.mjs +69 -0
  31. package/src/lib/guide.mjs +81 -85
  32. package/src/lib/handover-io.mjs +69 -6
  33. package/src/lib/identity.mjs +330 -0
  34. package/src/lib/legacy.mjs +89 -0
  35. package/src/lib/lint.mjs +17 -13
  36. package/src/lib/manual.mjs +135 -59
  37. package/src/lib/mover.mjs +23 -19
  38. package/src/lib/names.mjs +14 -19
  39. package/src/lib/office.mjs +63 -15
  40. package/src/lib/pack-ops.mjs +11 -11
  41. package/src/lib/pathkey.mjs +24 -0
  42. package/src/lib/peer.mjs +4 -4
  43. package/src/lib/phase.mjs +8 -13
  44. package/src/lib/profile.mjs +1 -1
  45. package/src/lib/reminder.mjs +110 -0
  46. package/src/lib/role.mjs +9 -6
  47. package/src/lib/scaffold.mjs +68 -26
  48. package/src/lib/schedule.mjs +5 -6
  49. package/src/lib/session-create.mjs +3 -3
  50. package/src/lib/session-ops.mjs +80 -51
  51. package/src/lib/sessions.mjs +41 -3
  52. package/src/lib/sevenzip.mjs +10 -1
  53. package/src/lib/status.mjs +337 -176
  54. package/src/lib/succession.mjs +16 -16
  55. package/src/lib/system.mjs +364 -0
  56. package/src/lib/zstd-log.mjs +1 -1
  57. package/src/selftest.mjs +6 -6
  58. package/src/templates/Changelog.skeleton.md +3 -3
  59. package/src/templates/Charter.skeleton.md +13 -9
  60. package/src/templates/Handover.global.skeleton.md +20 -13
  61. package/src/templates/Handover.template.md +89 -0
  62. package/src/templates/Plan.skeleton.md +34 -0
  63. package/src/templates/Prompt_Templates.md +101 -20
  64. package/src/templates/Runtime_Manual.md +263 -122
  65. package/src/templates/System.skeleton.md +31 -0
  66. package/src/templates/Systems.global.skeleton.md +13 -0
  67. package/src/templates/rules/Delegation_Rules.md +45 -0
  68. package/src/templates/rules/Handover_Rules.md +46 -23
  69. package/src/templates/rules/Migration_Rules.md +7 -7
  70. package/src/templates/rules/Misc_Task_Standard.md +17 -10
  71. package/src/templates/rules/Writing_Rules.md +33 -16
  72. package/src/templates/standards/README.md +37 -0
  73. package/src/templates/standards//345/206/231/344/275/234/350/247/204/350/214/203/README.md +120 -0
  74. package/src/templates/standards//345/206/231/344/275/234/350/247/204/350/214/203//344/276/235/346/215/256_/350/256/272/346/226/207.md +389 -0
  75. package/src/tools/ho_backup.mjs +135 -0
  76. package/src/tools/ho_changelog.mjs +4 -4
  77. package/src/tools/ho_charter.mjs +7 -6
  78. package/src/tools/ho_checkpoint.mjs +36 -32
  79. package/src/tools/ho_docmap.mjs +239 -0
  80. package/src/tools/ho_gate.mjs +11 -5
  81. package/src/tools/ho_handover.mjs +61 -35
  82. package/src/tools/ho_office.mjs +46 -5
  83. package/src/tools/ho_profile.mjs +3 -3
  84. package/src/tools/ho_sessions.mjs +46 -26
  85. package/src/tools/ho_status.mjs +251 -0
  86. package/src/tools/ho_succeed.mjs +3 -3
  87. package/src/tools/ho_workspace.mjs +146 -42
  88. package/src/tools/peer.mjs +416 -0
  89. package/src/tools/ho_brief.mjs +0 -57
  90. package/src/tools/ho_diag.mjs +0 -87
  91. package/src/tools/ho_drift.mjs +0 -41
  92. package/src/tools/ho_init.mjs +0 -82
  93. package/src/tools/ho_pack.mjs +0 -38
  94. package/src/tools/ho_phase.mjs +0 -37
  95. package/src/tools/ho_snapshot.mjs +0 -104
  96. package/src/tools/ho_tokens.mjs +0 -57
  97. package/src/tools/peer_list.mjs +0 -37
  98. package/src/tools/peer_new.mjs +0 -75
  99. package/src/tools/peer_peek.mjs +0 -41
  100. package/src/tools/peer_rename.mjs +0 -31
  101. package/src/tools/peer_send.mjs +0 -52
  102. package/src/tools/peer_status.mjs +0 -46
@@ -0,0 +1,389 @@
1
+ # AI 多会话协作产出的书面语规范
2
+
3
+ *——面向 dsh-handover 插件体系的语体门禁依据*
4
+
5
+ ## 摘要
6
+
7
+ AI 多会话插件体系在长时间跨会话作业中产出注释、提交信息、文档、版本说明、提示段、界面文案、错误消息与台账。角色切换使文本以既有产出为范本,产生口语化、冗长、术语不统一与比喻替代术语等漂移。本文以国际风格指南、简明语言标准、使用说明标准、注释规范、中文国家标准与规范性关键词六类文献为依据,提出准确、简洁、一致、面向读者、可操作、可检验六项原则,按九类场景给出体裁规则,形成 42 条编号条款并标注自动可检或需人工复核,配套门禁、评审、例外登记与版本化机制。
8
+
9
+ **关键词**:书面语规范;语体门禁;技术写作;简明语言;代码注释;文档治理
10
+
11
+ ## 1 引言
12
+
13
+ ### 1.1 问题
14
+
15
+ dsh-handover 是一套 AI 多会话插件体系。全局、项目、杂项与普通四类会话在同一工作区内长期并行作业,各自产出源码与代码注释、提交信息、README 与文档、版本说明、提示段与工具描述、设置页文案、错误消息、交接文档、台账与审阅副本说明。这些产出物共享同一读者群:后续接手的 AI 会话与开发者。读者依据前一轮文本判断现状、决定下一步动作,文本的可读性因此直接决定协作的可靠性。
16
+
17
+ 症状集中表现为语言漂移。第一,口语化。产出中出现「搞一下」「顺手处理」「差不多可以了」一类会话用语,动作边界与完成标准随之模糊。第二,冗长。解释性废话占据篇幅,读者需在大量铺垫中提取一条结论。第三,术语不一致。同一对象在不同会话中被称为「任务」「作业」「工单」「事项」,读者无法判断所指是否同一。第四,比喻替代术语。「把口子收一收」一类说法在缺少共同语境时不可解析,且无法被脚本检查。
18
+
19
+ 漂移的成因不是单次写作失误,而是缺少共享规范时的范本继承。每一轮会话倾向以上一轮产出为风格样本,偏差被复制并放大,跨会话次数越多累积越明显。事后润色无法抑制该过程,因为润色者同样缺少可援引的判定依据。
20
+
21
+ ### 1.2 范围
22
+
23
+ 本文规范的对象是书面语的表层可控特征:用词、句式、术语、标点、数字、结构、链接与体裁要素。规范不涉及创意文案、市场宣传、法律意见与文学性表达,此类文本的语体目标与本规范不同。
24
+
25
+ 规范覆盖九类产出物:代码注释、提交信息、README 与文档、版本说明、提示段与工具描述、设置页文案、错误消息、交接文档与台账、审阅材料。覆盖的会话角色为全局、项目、杂项、普通四类,不区分模型与版本,原因是规范约束文本形态而非生成主体。
26
+
27
+ ### 1.3 目标与交付
28
+
29
+ 目标有三项。其一,给出可援引的原则,使「写得好」转化为可讨论的判断。其二,给出分场景的体裁规则,使不同产出物各有必要要素与禁止项。其三,给出编号条款,使规范可被脚本执行、被评审清单引用、被例外登记追踪。
30
+
31
+ 交付形态为规范文本加条款表。条款表是语体门禁的实现依据,评审清单是条款表的投影,二者共用编号,避免规范与检查脱节。
32
+
33
+ ## 2 依据与方法
34
+
35
+ ### 2.1 来源分级
36
+
37
+ 规范依据分为六级,分级标准为权威性、可核验性与对产出物的对应关系。
38
+
39
+ 第一级为国际风格指南,包括 Microsoft Writing Style Guide 与 Google 开发者文档风格指南,辅以 Google 工程实践文档。此类文献面向技术写作,规则具体到词与标点,可直接转化为检查项 [1][2][3][10]。
40
+
41
+ 第二级为简明语言标准,包括 ISO 24495-1:2023 与美国联邦政府简明语言指南。前者确立简明语言文件的治理原则与实施指南,适用于技术写作,且明确其对象为以文本为主的印刷或数字信息 [25];后者给出面向读者、主题句、主动语态、避免冗词等可操作条目 [21][22][23][24]。
42
+
43
+ 第三级为使用说明标准,即 IEC/IEEE 82079-1:2019。该标准为使用说明的设计与表述提供一般原则与详细要求,把信息质量与信息管理过程纳入规范部分,并给出信息可用性的经验评估方法 [26]。提示段与工具描述在功能上属于使用说明,故以此为体裁依据。
44
+
45
+ 第四级为代码注释规范,包括 Go Doc Comments、Python PEP 257、JSDoc、Oracle JavaDoc 文档注释规范与 Rust rustdoc。上述文献分别约束注释的位置、句式、必备标签与结构,共同点是要求注释面向调用者而非实现者 [13][14][15][16][17]。
46
+
47
+ 第五级为中文国家标准,包括 GB/T 15834—2011《标点符号用法》、GB/T 15835—2011《出版物上数字用法》、GB/T 7713.2—2022《学术论文编写规则》、GB/T 8567—2006《计算机软件文档编制规范》与 GB/T 1.1—2020《标准化工作导则 第 1 部分:标准化文件的结构和起草规则》[29][30][31][32][33]。前两项约束标点与数字,第三项约束学术文本结构,第四项约束软件文档的类别与内容,第五项约束条款的写法与助动词体系。
48
+
49
+ 第六级为规范性关键词,即 RFC 2119 与 RFC 8174。二者规定 MUST、SHOULD、MAY 等词的规范含义,并明确只有全大写形式才具备该含义 [27][28]。中文条款相应采用「应」「宜」「可」三级,与 GB/T 1.1—2020 的条款类型对齐 [33]。
50
+
51
+ 分级同时确定冲突消解顺序。项目内既有约定优先于本规范;本规范优先于通用风格指南;通用风格指南优先于词典与惯例。该顺序取自 Google 风格指南的参考层级,其原文即把项目特定风格置于指南之前 [3]。其意义在于:规范不追求覆盖一切情形,只保证争议有唯一裁决路径。
52
+
53
+ ### 2.2 取证方法
54
+
55
+ 本文只采用可公开访问的一手页面。每条参考文献均经实际打开核对并记录机构、标题与 URL;无法打开者不予引用;对需付费获取全文的标准,仅引其公开摘要、范围与状态,不引述未公开正文。中文国家标准多数仅公开题录,故本文对标点、数字、文档类别的论断停留在标准适用范围层面。此举降低论断颗粒度,但每条论断可回溯,且不影响条款可执行性:条款判定对象为文本表层特征,标准提供判定方向。
56
+
57
+ ### 2.3 条款化方法
58
+
59
+ 每条规则写作三段:触发条件、判定标准、处置方式。触发条件说明条款在何种文本、何种位置生效;判定标准给出可观察的特征;处置方式说明违反时的动作,分为阻断、警告与提示。
60
+
61
+ 条款措辞采用三级助动词。「应」表示要求,违反即阻断;「宜」表示推荐,违反记为警告;「可」表示允许,不构成检查项。该体系与 GB/T 1.1—2020 对条款类型的规定一致,也与 RFC 2119 对要求级别的划分同构 [27][33]。采用两套体系而非一套,是因为中文条款需要中文助动词,而对外契约文本仍需保留英文关键词的原始效力。
62
+
63
+ ## 3 基本原则
64
+
65
+ ### 3.1 准确
66
+
67
+ 准确指陈述与事实一致,且不引入无法验证的判断。Diátaxis 把准确性列为文档功能质量的首要维度,并指出功能质量各维度相互独立,准确而不完整、完整而不一致均为失败状态 [19]。IEC/IEEE 82079-1:2019 把信息质量纳入规范条款,说明质量不是风格偏好而是可要求项 [26]。
68
+
69
+ 准确原则的直接推论是禁止以比喻替代术语。比喻在缺少共同语境时不可解析,且把可验证的技术陈述替换为不可验证的意象;Google 风格指南要求避免比喻性语言,理由是其对跨文化读者与翻译不友好 [5]。另一推论是禁止描述实现细节:Go 文档注释明确要求不解释当前实现的内部细节,否则实现变更时注释即失效 [13]。
70
+
71
+ ### 3.2 简洁
72
+
73
+ 简洁指在信息量不变的前提下减少字符。Microsoft 风格指南的首要条目即「更短总是更好」,并要求删去每个多余词 [2]。简明语言指南给出更具体的处置:删去多余修饰语(absolutely、actually、completely、really、quite、totally、very 一类),删去同义重复的双联与三联表达,逐词追问其是否提供价值 [23]。
74
+
75
+ 简洁不等于省略必要信息。Google 风格指南要求删除的是占位性短语,如 please note、at this time 一类不承载信息的开场 [5]。判定标准是:删除后读者能否完成同一动作。能完成则属冗余,不能完成则属必要。
76
+
77
+ ### 3.3 一致
78
+
79
+ 一致指同一对象在同一体系内只有一个称名,同一格式在同一位置只有一种写法。简明语言指南对此表述最直接:若用某词指称某群体,全篇继续使用该词,不换成同义词;以同义词增加可读性的说法在公文与技术文本中不成立 [23]。Google 风格指南以词表方式固定术语与拼写,并要求偏离指南时全篇保持一致 [3][4]。
80
+
81
+ 一致性可自动检查,因为其判定对象是字符串等价关系。这使一致成为六项原则中最适合作为门禁首道关卡的一项。GB/T 1.1—2020 对标准内部术语统一的要求,为中文语境提供同向依据 [33]。
82
+
83
+ ### 3.4 面向读者
84
+
85
+ 面向读者指以读者的前置知识与当下任务决定详略与顺序。简明语言指南把「为读者写作」列为第一规则,并反驳「简明即降低水准」的误解:应依据读者的知识水平、专业程度与兴趣调整,而非一律简化 [22]。ISO 24495-1:2023 把读者置于简明语言治理原则的中心位置,并把适用范围明确扩展到技术写作 [25]。
86
+
87
+ 其在结构上的体现是体裁分离。Diátaxis 把文档分为教程、操作指南、参考与解释四类,四类对应四种读者需求,混写会同时破坏两类需求 [18]。本规范据此要求 README、操作步骤、参数参考与设计说明分文件或分节承载,不在同一段内切换体裁。
88
+
89
+ ### 3.5 可操作
90
+
91
+ 可操作指文本能直接转化为动作。Google 风格指南的操作步骤规则要求:步骤首句含祈使动词,说明目的或条件时把条件置于动作之前,位置信息置于动作之前,一步一个动作 [9]。IEC/IEEE 82079-1:2019 把使用说明的编制过程与可用性评估纳入规范部分,其前提即说明须支持用户完成操作 [26]。
92
+
93
+ 判定标准是:读者能否在不回读前文的情况下执行该句。不能执行则说明缺少条件、对象或位置之一。该标准同样适用于提示段与工具描述,因为二者在功能上是给执行者的操作说明。
94
+
95
+ ### 3.6 可检验
96
+
97
+ 可检验指每条要求都能对应一个观察动作与一个判定结果。Diátaxis 区分功能质量与深层质量,并指出功能质量可被客观测量,深层质量只能被判断 [19]。本规范把可自动判定的部分归入脚本,把需语境判断的部分归入人工复核,正是沿此区分。
98
+
99
+ 另一个前提是条款粒度。RFC 2119 要求规范性关键词谨慎使用,只用于互操作必需或限制有害行为之处 [27]。条款过多会使门禁失去阻断力,条款过少会使评审失去依据。本文以 42 条覆盖九类场景,即以「每类场景四至六条」为粒度上限。
100
+
101
+ ### 3.7 原则之间的关系
102
+
103
+ 六项原则构成有优先级的判定链:准确优先于简洁,一致优先于措辞变化,可操作优先于句式美观,可检验优先于原则完整性。冲突时按此顺序裁决。
104
+
105
+ ## 4 分场景规则
106
+
107
+ ### 4.1 代码注释
108
+
109
+ 代码注释分两类:解释实现的行内注释与面向调用者的文档注释。行内注释应解释「为什么」,不复述语句在做什么。Google 工程实践在评审意见规范中指出,仅在评审工具中给出的解释对未来读者无用,正确处置是改写代码或补入注释 [12];Write the Docs 以同一表述概括注释职能 [20]。
110
+
111
+ 文档注释应写完整句,首句以被声明的标识符开头,形成可独立阅读的摘要。Go 文档注释要求包注释首句以 Package 开头,类型与函数注释首句点明所声明的符号,布尔返回值用 reports whether 表述且不加 or not [13]。Python PEP 257 要求单行文档字符串为以句号结尾的祈使短语,不用 Returns 一类描述式开头;多行文档字符串的摘要行独占一行,并以空行与正文分隔 [14]。
112
+
113
+ 必备要素按语言惯例确定:参数、返回值、异常、副作用与调用限制。PEP 257 要求函数文档字符串说明这五项 [14];JSDoc 以 param、returns、throws 等标签固定这些要素 [15];Oracle 文档注释规范要求主描述首句为简洁而完整的摘要句,并要求 deprecated 标签首句说明废弃时点与替代方案 [16]。Rust 文档建议按「一句话说明—详细说明—可复制示例—进阶说明」组织,并在可能 panic 时设独立小节 [17]。
114
+
115
+ 中文注释另有三条规则。其一,使用全角标点,代码、路径、命令、标识符保持半角并加反引号。其二,不使用比喻与语气词,不写「这里有点绕」一类主观判断。其三,待办注释应带责任人与条件,不写无主的 TODO。
116
+
117
+ ### 4.2 提交信息
118
+
119
+ 提交信息的体裁由版本历史决定。Google 工程实践要求首行为对「做了什么」的简短概括,写成完整句、按祈使语气表述,其后空一行,正文说明「为什么」、背景、方案取舍、缺陷编号与设计文档链接 [11]。该文档同时给出反例:Fix bug、Fix build、Add patch、Phase 1 一类首行不提供可检索信息 [11]。
120
+
121
+ 据此确定四条规则。首行不超过 50 个字符,采用祈使式,句末不加句号。首行与正文之间空一行。正文回答三个问题:问题是什么、为何采用此方案、有何已知不足。标签可用但应短,且不宜堆叠于首行,以免遮蔽内容 [11]。
122
+
123
+ 中文提交信息另有一条约定:首行使用动词开头的短语,如「修复会话切换时台账重复写入」,不用「关于……的修改」。版本号、缺陷号、文件路径保留原形,不翻译。
124
+
125
+ ### 4.3 README 与文档
126
+
127
+ README 的功能是让读者在最短时间内判断项目是否解决其问题。Write the Docs 给出的必要内容包括:项目解决的问题、最小可运行示例、代码与问题跟踪入口、支持渠道、贡献方式、安装步骤与许可证 [20]。该指南同时提醒勿以问答集充当文档,理由是问答集易过期、内容分散、难以检索 [20]。
128
+
129
+ 文档的整体结构按 Diátaxis 四类划分:教程面向学习,操作指南面向任务,参考面向查询,解释面向理解 [18]。四类不得混写。混写的典型症状是在操作步骤中插入设计动因,或在参考条目中加入教学引导,读者被迫在两类需求间切换。
130
+
131
+ 标题写法按 Google 风格指南执行:全部使用句首大写;任务型标题以动词原形开头,概念型标题用名词短语且不以动名词开头;不以数字标示章节顺序;标题内不放链接;标题层级不跳级 [8]。中文标题相应采用动词开头的短语,不以「关于」「浅谈」起首。
132
+
133
+ ### 4.4 版本说明
134
+
135
+ 版本说明的读者是使用者而非提交者,其内容是行为变化而非代码变化。规则有四条。按新增、变更、修复、移除、安全五类分组,组内每条一句。每条以产品行为作主语,不写「我修改了」「本次提交」。不写内部重构细节,除非其影响外部行为。破坏性变更单列并给出迁移动作。
136
+
137
+ 规范性关键词在此场景有特殊约束。仅当条目构成对外契约时使用「应」「不应」「宜」「可」,且同一文档内含义稳定;其余条目用陈述句。该约束来自 RFC 2119 对关键词的限定:关键词只用于互操作必需或限制有害行为之处,且只有大写形式具备规范含义 [27][28]。
138
+
139
+ ### 4.5 提示段与工具描述
140
+
141
+ 提示段与工具描述在功能上属于使用说明,故按 IEC/IEEE 82079-1:2019 的结构处理:说明目标、前置条件、操作步骤、预期结果与异常处置 [26]。该标准把信息质量与编制过程纳入规范部分,其要求可转译为四条写作规则。
142
+
143
+ 其一,一句一指令,一句中出现两个动作时拆分为两步。其二,条件前置。Google 风格指南要求把环境、条件或目的置于动作之前,使读者可据此判断该步是否适用 [7][9]。其三,说明不适用的情形。缺少边界说明的提示段会被执行者套用到错误场景。其四,参数与工具名用反引号标注,保持原形,不翻译字段名。
144
+
145
+ 禁止项包括:以比喻描述机制,以人格化描述工具行为,以「注意」「切记」代替具体条件。此类表达在缺少共同语境时不可执行,且无法被脚本检查。
146
+
147
+ ### 4.6 设置页文案
148
+
149
+ 设置页文案分标签与说明两层。标签用名词短语,长度受界面宽度限制,不使用动词句。说明用一句话回答「改变什么」与「影响什么」,不重复标签。
150
+
151
+ 语体约束有三条。其一,不使用感叹号与「请」,Google 风格指南明确要求在操作说明中不使用 please,理由是过度客气反而增加噪声 [5][9]。其二,不使用「简单」「轻松」「一键」一类承诺性形容词,此类词在无法兑现时构成误导,风格指南亦将 simply、easy、quickly 列为应避免的词 [5]。其三,危险操作须说明后果与可逆性,不使用模糊的「谨慎操作」。
152
+
153
+ 同一设置项在不同入口的文案应完全一致。此项属一致原则的直接应用,也是可自动检查项,因为判定对象是字符串等价。
154
+
155
+ ### 4.7 错误消息
156
+
157
+ 错误消息承担三项职能:说明发生了什么、说明原因、给出下一步。Microsoft 风格指南以 Invalid ID 为例,指出应改为给出可接受格式的具体指引 [2]。该例说明错误消息的价值在于可执行性,而非礼貌程度。
158
+
159
+ 规则有五条。其一,三段式结构固定,缺少任一段即视为不完整。其二,不暴露内部标识、堆栈与数据库字段名,除非读者为开发者且该信息可直接用于处置。其三,给出一个可执行的下一步,不给出多个并列建议。其四,同一错误在同一产品内使用同一措辞,不同错误不使用同一措辞。其五,不以「出错了」「操作失败」作为唯一信息。
160
+
161
+ 错误消息不使用比喻与情绪化表达。理由是错误场景下读者认知负荷最高,任何需要解释的表达都会放大处置成本。
162
+
163
+ ### 4.8 交接文档与台账
164
+
165
+ 交接文档的读者是下一个会话,其判定标准是自足性:不查阅其他文件即可接手。必备六要素为当前状态、已完成项、未完成项、已知风险、下一步动作、相关文件路径。缺任一项即视为不合格。
166
+
167
+ 台账与交接文档体裁不同。台账是结构化记录,一行一事实,字段固定,状态取受控词表,时间采用统一格式。台账不承载叙述,不写过程描述与主观评价。GB/T 8567—2006 对软件文档的类别与内容提出要求,可作为台账字段设计的参照 [32]。
168
+
169
+ 两类文本共同禁止三项:以「基本完成」「大致可用」替代可判定的状态;以「后续再议」替代明确的下一次动作;以情绪评价替代事实陈述。
170
+
171
+ ### 4.9 审阅材料
172
+
173
+ 审阅材料包括评审意见与审阅副本说明。评审意见的规则取自 Google 工程实践:评论针对代码而非作者;说明理由;在给出明确指令与指出问题之间保持平衡;对修改意图标注级别 [12]。该文档给出的级别为 Nit、Optional、FYI 三类,作用是使作者能判断哪些必须处理 [12]。
174
+
175
+ 审阅副本说明应写四项:审阅范围、基线版本、差异摘要、未覆盖项。未覆盖项须显式声明,否则读者会默认副本已覆盖全部内容。
176
+
177
+ 中文评审意见采用三级标注:阻断、建议、提示,与 Nit、Optional、FYI 对应;级别为固定前缀,故可自动检查。
178
+
179
+ ## 5 可执行条款
180
+
181
+ 条款编号自 W-01 起,编号一经分配不再复用;条款废止时保留编号并标注废止日期。检查方式分两类:自动可检指可由脚本依据词表、正则、语法结构或文件结构判定;需人工复核指需读者语境判断,脚本仅提供线索。
182
+
183
+ ### 5.1 用词与语体
184
+
185
+ | 编号 | 条款 | 检查方式 | 主要依据 |
186
+ | --- | --- | --- | --- |
187
+ | W-01 | 应不使用口语词与语气词(啊、吧、呢、嘛、哈、啦、其实、反正) | 自动可检(词表匹配) | [5][23] |
188
+ | W-02 | 应不使用情绪化与评价性表达(完美、惊艳、糟糕、坑、丝滑、离谱) | 自动可检(词表匹配) | [5][22] |
189
+ | W-03 | 应不以比喻、拟人替代术语(打通、收口、抓手、链路、落地) | 需人工复核(词表提示) | [5][25] |
190
+ | W-04 | 应不使用填充语(值得注意的是、众所周知、简单来说、please note 类) | 自动可检(词表匹配) | [5][2] |
191
+ | W-05 | 应不使用「点」代替「点击」;界面动作统一为点击、选择、输入、按 | 自动可检(正则) | [4][9] |
192
+ | W-06 | 应不堆叠同义修饰(彻底完全、基本大致、非常重要且关键) | 自动可检(搭配词表) | [23] |
193
+ | W-07 | 应不使用网络缩略语、表情符号与装饰性符号 | 自动可检(正则) | [5] |
194
+ | W-08 | 应不使用感叹号;问号不叠用 | 自动可检(正则) | [5] |
195
+
196
+ ### 5.2 句式与句长
197
+
198
+ | 编号 | 条款 | 检查方式 | 主要依据 |
199
+ | --- | --- | --- | --- |
200
+ | W-09 | 单句汉字数宜不超过 40,超过应拆分;拆分后不得丢失限定条件 | 自动可检(分句统计) | [23][2] |
201
+ | W-10 | 一段应只含一个主题,段首宜为主题句 | 需人工复核 | [22] |
202
+ | W-11 | 条件、前提、目的应置于动作之前 | 需人工复核 | [7][9] |
203
+ | W-12 | 宜使用主动语态并明确施动者;确需强调受事时可用被动 | 自动可检(被字句统计)+人工 | [6][22] |
204
+ | W-13 | 应控制名词化,避免「进行/实施/开展+名词」替代实义动词 | 自动可检(正则) | [2][23] |
205
+ | W-14 | 一句应只含一个指令;并列动作应分条 | 需人工复核 | [9] |
206
+ | W-15 | 操作步骤首句应含祈使动词,位置信息置于动作之前 | 自动可检(句式模板)+人工 | [9] |
207
+ | W-16 | 结论应前置,首句给出完整判断,不铺垫背景 | 需人工复核 | [2][22] |
208
+
209
+ ### 5.3 术语与缩写
210
+
211
+ | 编号 | 条款 | 检查方式 | 主要依据 |
212
+ | --- | --- | --- | --- |
213
+ | W-17 | 同一对象在同一体系内应只有一个称名,禁止同义换用 | 自动可检(术语表比对) | [23][4] |
214
+ | W-18 | 术语首次出现应就地给出定义或链接,不设集中定义章 | 需人工复核 | [22][24] |
215
+ | W-19 | 缩写首次出现应写「全称(缩写)」,其后统一使用缩写 | 自动可检(首现检测) | [4][3] |
216
+ | W-20 | 应不自造缩写,不混用中英变体(如 API 与「接口」交替) | 自动可检(白名单) | [4][23] |
217
+ | W-21 | 产品名、标准号、代码标识符应保持官方写法与大小写 | 自动可检(白名单) | [3][4] |
218
+ | W-22 | 清单场景应穷举,不以「等」「之类」省略关键项 | 需人工复核 | [24] |
219
+
220
+ ### 5.4 标点与数字
221
+
222
+ | 编号 | 条款 | 检查方式 | 主要依据 |
223
+ | --- | --- | --- | --- |
224
+ | W-23 | 中文正文应使用全角标点;代码、路径、命令、标识符使用半角并加反引号 | 自动可检(字符集判定) | [29] |
225
+ | W-24 | 并列词语用顿号,并列分句用分号,不用逗号连接并列项 | 自动可检(结构启发式) | [29] |
226
+ | W-25 | 书名号、引号、括号应成对,嵌套不超过两层 | 自动可检(配对与嵌套) | [29] |
227
+ | W-26 | 计量、统计、编号用阿拉伯数字;概数与惯用语用汉字 | 自动可检(规则表) | [30] |
228
+ | W-27 | 版本号用 vX.Y.Z,日期用 YYYY-MM-DD,时间用 24 小时制 | 自动可检(正则) | [30][3] |
229
+ | W-28 | 标准号应写全,含编号、破折号与年号(GB/T 15834—2011) | 自动可检(正则) | [29][33] |
230
+
231
+ ### 5.5 结构、链接与引用
232
+
233
+ | 编号 | 条款 | 检查方式 | 主要依据 |
234
+ | --- | --- | --- | --- |
235
+ | W-29 | 每文档应只有一个一级标题,标题层级不得跳级 | 自动可检(层级栈) | [8] |
236
+ | W-30 | 标题应不带句末标点,不以编号充当层级,不置链接 | 自动可检(正则与结构) | [8][2] |
237
+ | W-31 | 标题应与内容对应,不得出现空标题或标题与正文错位 | 需人工复核 | [8] |
238
+ | W-32 | 链接文字应自述目标,禁止「点击这里」「详见」「此处」 | 自动可检(词表) | [4] |
239
+ | W-33 | 外链应可解析,宜去除追踪参数,优先一手来源 | 自动可检(可达性)+人工(权威性) | [3][31] |
240
+ | W-34 | 正文引用编号应与参考文献条目一一对应,不得悬空 | 自动可检(编号比对) | [31][33] |
241
+
242
+ ### 5.6 场景专项
243
+
244
+ | 编号 | 条款 | 检查方式 | 主要依据 |
245
+ | --- | --- | --- | --- |
246
+ | W-35 | 代码注释应解释「为什么」,不复述语句行为 | 需人工复核 | [12][20] |
247
+ | W-36 | 文档注释应含摘要句、参数、返回值、异常与副作用 | 自动可检(标签完整性) | [14][15][16] |
248
+ | W-37 | 提交信息首行宜不超过 50 个字符,用祈使式,其后空行再写正文 | 自动可检(长度与结构) | [11] |
249
+ | W-38 | 版本说明应按新增、变更、修复、移除、安全分组,每条一句 | 自动可检(结构) | [25][32] |
250
+ | W-39 | 错误消息应含现象、原因、处置三段 | 自动可检(模板)+人工 | [2] |
251
+ | W-40 | 交接文档应含状态、已完成、未完成、风险、下一步、路径六项 | 自动可检(字段) | [32] |
252
+ | W-41 | 台账应一行一事实,字段固定,状态取受控词表 | 自动可检(schema) | [32][33] |
253
+ | W-42 | 规范性关键词应全大写且仅用于契约语义;中文对应「应/宜/可」 | 自动可检(大小写与位置) | [27][28][33] |
254
+
255
+ ### 5.7 检查实现要点
256
+
257
+ 自动可检条款依赖五类技术手段。词表匹配覆盖 W-01 至 W-07、W-20、W-32;词表应分级,阻断级只收录确定违反项,警告级收录可能违反项,以免误报削弱门禁可信度。结构解析覆盖 W-23、W-25、W-29、W-30、W-36 至 W-38、W-40、W-41,文档按标题层级栈与字段模板解析,源码按语法树提取文档注释节点,台账按 schema 校验。统计判定覆盖 W-09、W-12、W-13,句长以分句为单位计汉字数,被动句以「被」「由」「经」引导结构计数,名词化以「进行/实施/开展/加以+双字名词」匹配,阈值应可配置,报告应给分布而非单一均值。一致性比对覆盖 W-17、W-19、W-21,比对术语表与白名单,输出差异对。引用校验覆盖 W-33、W-34,校验编号连续、条目非空、URL 可解析、正文引用集合与条目集合相等。
258
+
259
+ 需人工复核的条款为 W-03、W-10、W-11、W-14、W-16、W-18、W-22、W-31、W-35。此类条款的判定依赖语境,脚本只能提供候选位置。处置方式为评审清单逐项确认,结果记入评审记录,不进入例外登记。
260
+
261
+ ### 5.8 阈值说明
262
+
263
+ W-09 的 40 字、W-37 的 50 字符为工程设定值,不由所引标准规定。依据是:中文技术文本单句信息量在 40 字左右接近饱和;版本历史面板可见宽度在 50 字符左右截断。阈值可依项目调整,调整须记入变更记录并同步修订门禁配置。
264
+
265
+ ## 6 实施与治理
266
+
267
+ ### 6.1 门禁
268
+
269
+ 门禁分三道,位置不同,职责不同。
270
+
271
+ 第一道为生成时自检。会话在产出文本后、写入文件前执行阻断级条款检查,命中即改写。其价值在于成本最低,且能在漂移发生的当轮阻断,避免偏差进入范本池。
272
+
273
+ 第二道为提交时检查。脚本对暂存内容执行全部自动可检条款,阻断级违规拒绝提交,警告级违规写入报告。其价值在于覆盖人工易漏的机械项,如标点、编号、链接与标题层级。
274
+
275
+ 第三道为评审时复核。评审者按需人工复核条款逐项确认,并对警告项给出处置意见。其价值在于处理语境判断,脚本在此不具判定能力。
276
+
277
+ 三道门禁共用同一份条款表与同一套编号,任一道的结果可互相引用。门禁配置应版本化,与规范版本绑定。
278
+
279
+ ### 6.2 评审
280
+
281
+ 评审清单是条款表的投影,按产出物类型筛选适用条款。代码评审筛 W-35、W-36,提交评审筛 W-37,文档评审筛 W-29 至 W-34,交接评审筛 W-40、W-41。
282
+
283
+ 评审意见应标注级别。级别取三级:阻断指不修改不得合并;建议指宜修改,作者可说明理由后保留;提示指仅告知,不要求动作。该分级取自 Google 工程实践对评审意见级别的划分 [12]。级别标注消除「所有意见都是强制的」这一误解,使作者能正确排序工作。评审应记录条款命中情况与例外引用,前者用于统计漂移热点,后者用于追踪例外是否被滥用。
284
+
285
+ ### 6.3 例外登记
286
+
287
+ 例外指经批准后不适用某条款的情形。登记应含七项字段:例外编号、条款编号、适用文件或路径、理由、替代措施、批准人、生效期与复核日。
288
+
289
+ 约束有三条。其一,例外须绑定具体路径,不得声明全局豁免。其二,例外须有到期日,到期未复核自动失效。其三,同一路径同一原因的例外不得重复登记。目的是防止例外累积为事实上的规范废止。
290
+
291
+ 例外不适用于 W-01 至 W-08 的阻断级词表条款。理由是此类条款判定明确、误报率低,放宽将直接破坏语体一致性。
292
+
293
+ ### 6.4 版本化
294
+
295
+ 规范采用语义化版本。主版本变更指条款增删或判定标准改变,需重新评估全部门禁配置;次版本变更指条款措辞澄清或阈值调整;修订版本变更指笔误与格式修正。
296
+
297
+ 条款编号永久保留。条款废止时保留编号并标注废止日期与替代条款,不复用编号。此约定保证历史评审记录与例外登记在规范演进后仍可解析。
298
+
299
+ 规范文本、条款表、门禁配置与评审清单应同版本发布;不一致时以规范文本为准。
300
+
301
+ ### 6.5 度量
302
+
303
+ 治理需要可观察的漂移指标。建议采集五项:平均句长与句长分布、阻断级词表命中率、术语一致率、解释性段落占比、首句摘要覆盖率。五项分别反映冗长趋势、语体纯度、称名收敛程度、信息密度与结论前置的执行情况。
304
+
305
+ 指标用于观察趋势,不用于考核个体,因为文本由多会话共同产出。阈值同为工程设定值,随项目基线调整:首次运行以现有产出为基线,其后观察变化方向而非绝对值。
306
+
307
+ ## 7 结论
308
+
309
+ 本文面向 AI 多会话协作的书面语治理,给出了一套可援引、可执行、可检查的规范。主要结论如下。
310
+
311
+ 第一,语言漂移的成因是范本继承而非单次失误,治理手段必须是共享规范加自动检查。事后润色无法阻断累积过程;规范的作用是提供唯一裁决依据,使不同会话在同一争议上得到同一结论。
312
+
313
+ 第二,规范的可信度取决于来源的可核验性。本文只引用实际打开核对的一手页面,对仅公开摘要的标准只作范围层面的援引。这一限制降低论断颗粒度,但保证每条论断可回溯。
314
+
315
+ 第三,六项原则之间存在优先级,构成判定链而非并列清单。缺少优先级的原则集合在冲突时无法裁决,评审将退化为个人偏好之争。
316
+
317
+ 第四,条款必须标注检查方式。可自动判定的条款进入门禁,需语境判断的条款进入评审清单,二者共用编号。将不可判定的要求写入门禁会使门禁失去阻断力;将可判定的要求留给人工会使评审沦为形式。
318
+
319
+ 第五,治理机制与条款同等重要。门禁解决发现,评审解决判断,例外登记解决合理偏离,版本化解决演进可追溯。四者缺一,规范将在数次迭代后退化为无人执行的文档。
320
+
321
+ 后续工作有三个方向:把条款表实现为可配置的检查器并将误报样本纳入词表迭代;以现有产出为基线采集漂移指标,验证阈值设定的合理性;把规范扩展至多语言产出,重点处理中英混写与术语对译的一致性。
322
+
323
+ ## 参考文献
324
+
325
+ [1] Welcome - Microsoft Writing Style Guide. Microsoft. https://learn.microsoft.com/en-us/style-guide/welcome/
326
+
327
+ [2] Top 10 tips for Microsoft style and voice. Microsoft. https://learn.microsoft.com/en-us/style-guide/top-10-tips-style-voice
328
+
329
+ [3] About this guide - Google developer documentation style guide. Google. https://developers.google.cn/style/
330
+
331
+ [4] Word list - Google developer documentation style guide. Google. https://developers.google.cn/style/word-list
332
+
333
+ [5] Voice and tone - Google developer documentation style guide. Google. https://developers.google.cn/style/tone
334
+
335
+ [6] Active voice - Google developer documentation style guide. Google. https://developers.google.cn/style/voice
336
+
337
+ [7] Sentence structure - Google developer documentation style guide. Google. https://developers.google.cn/style/sentence-structure
338
+
339
+ [8] Headings and titles - Google developer documentation style guide. Google. https://developers.google.cn/style/headings
340
+
341
+ [9] Procedures - Google developer documentation style guide. Google. https://developers.google.cn/style/procedures
342
+
343
+ [10] Google Engineering Practices Documentation. Google. https://google.github.io/eng-practices/
344
+
345
+ [11] Writing good CL descriptions. Google. https://google.github.io/eng-practices/review/developer/cl-descriptions.html
346
+
347
+ [12] How to write code review comments. Google. https://google.github.io/eng-practices/review/reviewer/comments.html
348
+
349
+ [13] Go Doc Comments. The Go Programming Language. https://go.dev/doc/comment
350
+
351
+ [14] PEP 257 - Docstring Conventions. Python Software Foundation. https://peps.python.org/pep-0257/
352
+
353
+ [15] Use JSDoc: Index. JSDoc. https://jsdoc.app/about-jsdoc
354
+
355
+ [16] Documentation Comment Specification for the Standard Doclet (JDK 21). Oracle. https://docs.oracle.com/en/java/javase/21/docs/specs/javadoc/doc-comment-spec.html
356
+
357
+ [17] How to write documentation - The rustdoc book. The Rust Project. https://doc.rust-lang.org/rustdoc/how-to-write-documentation.html
358
+
359
+ [18] Diátaxis. Daniele Procida. https://diataxis.fr/
360
+
361
+ [19] Towards a theory of quality in documentation. Diátaxis. https://diataxis.fr/quality/
362
+
363
+ [20] How to write software documentation. Write the Docs. https://www.writethedocs.org/guide/writing/beginners-guide-to-docs/
364
+
365
+ [21] Plain language guide series. Digital.gov, U.S. General Services Administration. https://digital.gov/guides/plain-language
366
+
367
+ [22] Principles of plain language. Digital.gov, U.S. General Services Administration. https://digital.gov/guides/plain-language/principles
368
+
369
+ [23] Short and simple. Digital.gov, U.S. General Services Administration. https://digital.gov/guides/plain-language/principles/short-simple
370
+
371
+ [24] Avoid jargon. Digital.gov, U.S. General Services Administration. https://digital.gov/guides/plain-language/principles/avoid-jargon
372
+
373
+ [25] ISO 24495-1:2023 Plain language - Part 1: Governing principles and guidelines. International Organization for Standardization. https://www.iso.org/standard/78907.html
374
+
375
+ [26] IEC/IEEE 82079-1:2019 Preparation of information for use (instructions for use) of products - Part 1: Principles and general requirements. International Organization for Standardization. https://www.iso.org/standard/71620.html
376
+
377
+ [27] RFC 2119: Key words for use in RFCs to Indicate Requirement Levels. S. Bradner. IETF. https://www.rfc-editor.org/info/rfc2119/
378
+
379
+ [28] RFC 8174: Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words. B. Leiba. IETF. https://www.rfc-editor.org/info/rfc8174/
380
+
381
+ [29] GB/T 15834-2011 标点符号用法. 国家市场监督管理总局, 国家标准化管理委员会. https://std.samr.gov.cn/gb/search/gbDetailed?id=71F772D7FE25D3A7E05397BE0A0AB82A
382
+
383
+ [30] GB/T 15835-2011 出版物上数字用法. 国家市场监督管理总局, 国家标准化管理委员会. https://openstd.samr.gov.cn/bzgk/gb/std_list?p.p1=0&p.p90=circulation_date&p.p91=desc&p.p2=15835
384
+
385
+ [31] GB/T 7713.2-2022 学术论文编写规则. 国家市场监督管理总局, 国家标准化管理委员会. https://openstd.samr.gov.cn/bzgk/gb/std_list?p.p1=0&p.p90=circulation_date&p.p91=desc&p.p2=7713.2
386
+
387
+ [32] GB/T 8567-2006 计算机软件文档编制规范. 国家市场监督管理总局, 国家标准化管理委员会. https://openstd.samr.gov.cn/bzgk/gb/std_list?p.p1=0&p.p90=circulation_date&p.p91=desc&p.p2=8567
388
+
389
+ [33] GB/T 1.1-2020 标准化工作导则 第 1 部分:标准化文件的结构和起草规则. 国家市场监督管理总局, 国家标准化管理委员会. https://openstd.samr.gov.cn/bzgk/gb/std_list?p.p1=0&p.p90=circulation_date&p.p91=desc&p.p2=1.1-2020
@@ -0,0 +1,135 @@
1
+ /**
2
+ * ho_backup —— 备份与迁移:一个入口,按 action 分流。
3
+ * snapshot(默认):库快照,逐字节复制 + sha256 清单 + PROVENANCE
4
+ * check:迁移包前置闸门
5
+ * pack:打包
6
+ * verify:校包
7
+ * restore:恢复(默认仅预演)
8
+ * 原 ho_snapshot、ho_pack 合并于此。
9
+ */
10
+ import fs from 'node:fs';
11
+ import path from 'node:path';
12
+ import crypto from 'node:crypto';
13
+ import { textOutput } from '../lib/output.mjs';
14
+ import { nameOf } from '../lib/names.mjs';
15
+ import { NL, isDir } from '../lib/text.mjs';
16
+ import { assertProjectName } from '../lib/role.mjs';
17
+ import { packCheck, packNow, packVerify, packRestore } from '../lib/pack-ops.mjs';
18
+
19
+ export const ACTIONS = ['snapshot', 'check', 'pack', 'verify', 'restore'];
20
+
21
+ const SKIP_DIRS = ['__pycache__', '.git', 'node_modules', '.obsidian'];
22
+ const SKIP_FILES = ['.DS_Store', 'Thumbs.db'];
23
+
24
+ function stamp() {
25
+ const d = new Date();
26
+ const p = (n) => (n < 10 ? '0' + n : '' + n);
27
+ return d.getFullYear() + p(d.getMonth() + 1) + p(d.getDate()) + '-' + p(d.getHours()) + p(d.getMinutes());
28
+ }
29
+
30
+ function walk(root, rel, rows) {
31
+ for (const name of fs.readdirSync(path.join(root, rel))) {
32
+ const full = path.join(root, rel, name);
33
+ const st = fs.statSync(full);
34
+ const r = rel ? rel + '/' + name : name;
35
+ if (st.isDirectory()) {
36
+ if (SKIP_DIRS.indexOf(name) !== -1) continue;
37
+ walk(root, r, rows);
38
+ } else if (st.isFile()) {
39
+ if (SKIP_FILES.indexOf(name) !== -1) continue;
40
+ if (name.endsWith('.pyc')) continue;
41
+ rows.push({ rel: r, size: st.size });
42
+ }
43
+ }
44
+ return rows;
45
+ }
46
+
47
+ function snapshotText(env, args, exec) {
48
+ const ws = env.resolveWs(exec);
49
+ const project = assertProjectName(typeof args.project === 'string' ? args.project.trim() : '');
50
+ if (!project) throw new Error('action="snapshot" 需要 project(写项目名,如 Demo)');
51
+ const projDir = path.join(ws, 'Projects', project);
52
+ if (!isDir(projDir)) throw new Error('找不到项目:' + projDir);
53
+ let lib = null;
54
+ for (const name of fs.readdirSync(projDir)) {
55
+ const p = path.join(projDir, name);
56
+ if (isDir(p) && fs.existsSync(path.join(p, 'RULES.md'))) { lib = p; break; }
57
+ }
58
+ if (!lib) throw new Error('该项目下没有带 RULES.md 的库根:' + projDir);
59
+ const rows = walk(lib, '', []);
60
+ const total = rows.reduce((a, b) => a + b.size, 0);
61
+ const out = [];
62
+ out.push('源库 ' + lib);
63
+ out.push('文件 ' + rows.length + ' 个,共 ' + (total / 1048576).toFixed(2) + ' MB');
64
+ if (args.dry_run) { out.push('dry_run:不落盘。'); return out.join(NL); }
65
+ const tag = args.tag || '行为检查';
66
+ const dest = path.join(ws, '_Temp', 'Checks', project + '_' + tag + '_' + stamp());
67
+ if (fs.existsSync(dest)) throw new Error('目标已存在,不覆盖:' + dest);
68
+ const libDest = path.join(dest, 'lib');
69
+ fs.mkdirSync(libDest, { recursive: true });
70
+ const manifest = ['# 源库: ' + lib, '# 文件: ' + rows.length + ' 字节: ' + total];
71
+ let drift = 0;
72
+ for (const r of rows) {
73
+ const src = path.join(lib, r.rel);
74
+ const dst = path.join(libDest, r.rel);
75
+ fs.mkdirSync(path.dirname(dst), { recursive: true });
76
+ fs.copyFileSync(src, dst);
77
+ const h1 = crypto.createHash('sha256').update(fs.readFileSync(src)).digest('hex');
78
+ const h2 = crypto.createHash('sha256').update(fs.readFileSync(dst)).digest('hex');
79
+ if (h1 !== h2) drift++;
80
+ manifest.push(r.rel + String.fromCharCode(9) + r.size + String.fromCharCode(9) + h1);
81
+ }
82
+ fs.writeFileSync(path.join(dest, 'MANIFEST.tsv'), manifest.join(NL), 'utf8');
83
+ const prov = [
84
+ '# PROVENANCE',
85
+ '- 源库: ' + lib,
86
+ '- 生成时间: ' + new Date().toISOString(),
87
+ '- 发起会话: ' + (env.selfId(exec) || '?'),
88
+ '- 文件数/字节: ' + rows.length + ' / ' + total,
89
+ '- 排除: __pycache__ .git node_modules .obsidian *.pyc .DS_Store Thumbs.db',
90
+ '- 校验: 逐文件 sha256 比对,drift=' + drift,
91
+ '- 含个人数据,不得外发、不得进 _Archives',
92
+ ].join(NL);
93
+ fs.writeFileSync(path.join(dest, 'PROVENANCE.md'), prov, 'utf8');
94
+ out.push('已落盘 ' + dest);
95
+ out.push('校验 drift=' + drift + (drift === 0 ? '(逐文件一致)' : '(不一致,判定失败)'));
96
+ return out.join(NL);
97
+ }
98
+
99
+ export function pmBackupTool(env) {
100
+ return {
101
+ name: nameOf('backup'),
102
+ description:
103
+ '备份与迁移:action=snapshot 库快照(缺省)、check 前置闸门、pack 打包、verify 校验包、restore 恢复(缺省预演)。',
104
+ parameters: {
105
+ type: 'object',
106
+ additionalProperties: false,
107
+ properties: {
108
+ action: { type: 'string', enum: ACTIONS, description: '缺省 snapshot' },
109
+ project: { type: 'string', description: 'snapshot:项目名(Projects/<项目>/ 下)' },
110
+ tag: { type: 'string', description: 'snapshot:标签,缺省 行为检查' },
111
+ kind: { type: 'string', enum: ['project', 'task', 'global'], description: 'pack:目标类型' },
112
+ name: { type: 'string', description: 'pack:项目名或任务名' },
113
+ out: { type: 'string', description: 'pack:输出目录' },
114
+ note: { type: 'string', description: 'pack:写入 MIGRATION.md 的说明' },
115
+ items: { type: 'array', items: { type: 'string' }, description: 'check:检查的项目名(global 表示全局)' },
116
+ min_age_min: { type: 'number', description: 'check:要求静止分钟数(默认 90)' },
117
+ pack: { type: 'string', description: 'verify、restore:包路径' },
118
+ target: { type: 'string', description: 'restore:目标根(缺省当前)' },
119
+ apply: { type: 'boolean', description: 'restore:为 true 才写入' },
120
+ dry_run: { type: 'boolean', description: 'snapshot:只统计不落盘;pack、restore:只列清单' },
121
+ format: { type: 'string', enum: ['auto', '7z', 'tar'], description: 'pack:auto=有 7-Zip 用 7z,否则内置 tar.gz(缺省)' },
122
+ },
123
+ },
124
+ output: textOutput(),
125
+ async execute(args, exec) {
126
+ const action = typeof args.action === 'string' && args.action ? args.action : 'snapshot';
127
+ if (ACTIONS.indexOf(action) === -1) throw new Error('action 只能是 ' + ACTIONS.join(' | ') + '(收到 ' + String(action) + ')');
128
+ if (action === 'snapshot') return { text: snapshotText(env, args, exec) };
129
+ if (action === 'check') return { text: packCheck(env, args, exec) };
130
+ if (action === 'verify') return { text: packVerify(env, args, exec) };
131
+ if (action === 'restore') return { text: packRestore(env, args, exec) };
132
+ return { text: packNow(env, args, exec) };
133
+ },
134
+ };
135
+ }
@@ -21,9 +21,9 @@ export function pmChangelogTool(env) {
21
21
  additionalProperties: false,
22
22
  required: ['summary'],
23
23
  properties: {
24
- summary: { type: 'string', description: '这一行写什么(中文)' },
25
- executor: { type: 'string', description: '执行者(默认本会话 id)' },
26
- date: { type: 'string', description: '日期 YYYY-MM-DD(默认今天)' },
24
+ summary: { type: 'string', description: '本行内容(中文)' },
25
+ executor: { type: 'string', description: '执行者(缺省为本会话 id)' },
26
+ date: { type: 'string', description: '日期 YYYY-MM-DD(缺省为当天)' },
27
27
  },
28
28
  },
29
29
  output: textOutput(),
@@ -48,7 +48,7 @@ export function pmChangelogTool(env) {
48
48
  const date = args.date || stamp();
49
49
  const who = args.executor || env.selfId(exec) || '?';
50
50
  const summary = typeof args.summary === 'string' ? args.summary.trim() : '';
51
- if (!summary) throw new Error('summary 不能为空(写清「改了什么、为什么」)');
51
+ if (!summary) throw new Error('summary 不能为空(写明变更内容与原因)');
52
52
  const safe = summary.split('|').join(String.fromCharCode(92) + '|');
53
53
  const row = '| ' + date + ' | ' + safe + ' | ' + who + ' |';
54
54
  lines.splice(sepIdx + 1, 0, row);
@@ -11,6 +11,7 @@ const FILES = [
11
11
  { key: 'handover_rules', rel: ['_Control_Center', 'rules', 'Handover_Rules.md'], tpl: 'rules/Handover_Rules.md' },
12
12
  { key: 'migration_rules', rel: ['_Control_Center', 'rules', 'Migration_Rules.md'], tpl: 'rules/Migration_Rules.md' },
13
13
  { key: 'misc_standard', rel: ['_Control_Center', 'rules', 'Misc_Task_Standard.md'], tpl: 'rules/Misc_Task_Standard.md' },
14
+ { key: 'delegation_rules', rel: ['_Control_Center', 'rules', 'Delegation_Rules.md'], tpl: 'rules/Delegation_Rules.md' },
14
15
  { key: 'writing_rules', rel: ['_Control_Center', 'user', 'Writing_Rules.md'], tpl: 'rules/Writing_Rules.md' },
15
16
  ];
16
17
 
@@ -29,12 +30,12 @@ export function pmCharterTool(env) {
29
30
  return {
30
31
  name: nameOf('charter'),
31
32
  description:
32
- '按节取规则正文,避免整篇读;doc="runtime" 取完整流程。只读。',
33
+ '按节取规则正文,避免整篇读取;doc="runtime" 取完整流程手册。只读。',
33
34
  parameters: {
34
35
  type: 'object',
35
36
  additionalProperties: false,
36
37
  properties: {
37
- doc: { type: 'string', enum: DOC_KEYS, description: '默认 charter;runtime=插件完整流程手册' },
38
+ doc: { type: 'string', enum: DOC_KEYS, description: '缺省 charter;runtime=插件完整流程手册' },
38
39
  section: { type: 'string', description: '节标题片段;留空则只返回目录(## 行清单)' },
39
40
  maxLines: { type: 'number', description: '最多返回多少行(默认 120)' },
40
41
  },
@@ -67,7 +68,7 @@ export function pmCharterTool(env) {
67
68
  fromBundle = true;
68
69
  }
69
70
  // 口径:内置=默认本体,工作区同名文件=本地化覆盖。所以这里不写「没有/缺失」,只标注来源。
70
- const note = fromBundle ? '(插件内置默认口径 · 要本地化就写到 ' + file + ')' : '';
71
+ const note = fromBundle ? '(插件内置默认口径;如需本地化,写入 ' + file + ')' : '';
71
72
  if (!section) {
72
73
  const heads = text.split(NL).filter((l) => l.startsWith("## "));
73
74
  return { text: [note, file, "共 " + heads.length + " 节:", ...heads.slice(0, max)].filter(Boolean).join(NL) };
@@ -77,12 +78,12 @@ export function pmCharterTool(env) {
77
78
  const cp = charterPath(ws);
78
79
  const src = isFile(cp) ? readUtf8(cp) : bundledTemplate('Charter.skeleton.md');
79
80
  const blk = canonicalBlock(src);
80
- if (!blk) throw new Error("无法获取 §8.1.1.4 正文(结构变了)");
81
- const head = isFile(cp) ? '' : '(工作区没有 ' + cp + ' —— 下面是插件自带骨架里的通用约束条。)' + NL + NL;
81
+ if (!blk) throw new Error("无法取得 §8.1.1.4 正文(结构已变更)");
82
+ const head = isFile(cp) ? '' : '(工作区无 ' + cp + ',以下为插件自带骨架中的通用约束条。)' + NL + NL;
82
83
  return { text: head + blk.body.slice(0, max).join(NL) };
83
84
  }
84
85
  const lines = sectionLines(text, section);
85
- if (!lines) throw new Error("未找到节:" + section + "(可先不带 section 调一次看目录)");
86
+ if (!lines) throw new Error("未找到节:" + section + "(可先不带 section 调用一次,查看目录)");
86
87
  return { text: lines.slice(0, max).join(NL) };
87
88
  },
88
89
  };