@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.
- package/README.md +123 -92
- package/docs/migrate.md +52 -41
- package/docs/releases/0.9.12.md +2 -3
- package/docs/releases/0.9.14.md +1 -5
- package/docs/releases/0.9.22.md +34 -0
- package/docs/releases/1.0.0.md +33 -0
- package/docs/troubleshoot.md +230 -46
- package/package.json +74 -71
- package/scripts/behavior.mjs +398 -0
- package/scripts/blank-dsh.mjs +41 -25
- package/scripts/blank-smoke.mjs +278 -109
- package/scripts/check-docs.mjs +7 -0
- package/scripts/e2e-migration.mjs +2 -2
- package/scripts/office.mjs +3 -3
- package/scripts/portability.mjs +71 -0
- package/scripts/style-gate.mjs +571 -0
- package/src/client.js +1028 -955
- package/src/index.mjs +313 -142
- package/src/lib/absrefs.mjs +1 -1
- package/src/lib/api.mjs +18 -10
- package/src/lib/archive.mjs +1 -1
- package/src/lib/brief.mjs +8 -6
- package/src/lib/config.mjs +23 -8
- package/src/lib/consent.mjs +54 -1
- package/src/lib/docmap.mjs +140 -0
- package/src/lib/drift.mjs +1 -1
- package/src/lib/events.mjs +1 -1
- package/src/lib/facts.mjs +19 -12
- package/src/lib/gates.mjs +30 -6
- package/src/lib/global-task.mjs +69 -0
- package/src/lib/guide.mjs +81 -85
- package/src/lib/handover-io.mjs +69 -6
- package/src/lib/identity.mjs +330 -0
- package/src/lib/legacy.mjs +89 -0
- package/src/lib/lint.mjs +17 -13
- package/src/lib/manual.mjs +135 -59
- package/src/lib/mover.mjs +23 -19
- package/src/lib/names.mjs +14 -19
- package/src/lib/office.mjs +63 -15
- package/src/lib/pack-ops.mjs +11 -11
- package/src/lib/pathkey.mjs +24 -0
- package/src/lib/peer.mjs +4 -4
- package/src/lib/phase.mjs +8 -13
- package/src/lib/profile.mjs +1 -1
- package/src/lib/reminder.mjs +110 -0
- package/src/lib/role.mjs +9 -6
- package/src/lib/scaffold.mjs +68 -26
- package/src/lib/schedule.mjs +5 -6
- package/src/lib/session-create.mjs +3 -3
- package/src/lib/session-ops.mjs +80 -51
- package/src/lib/sessions.mjs +41 -3
- package/src/lib/sevenzip.mjs +10 -1
- package/src/lib/status.mjs +337 -176
- package/src/lib/succession.mjs +16 -16
- package/src/lib/system.mjs +364 -0
- package/src/lib/zstd-log.mjs +1 -1
- package/src/selftest.mjs +6 -6
- package/src/templates/Changelog.skeleton.md +3 -3
- package/src/templates/Charter.skeleton.md +13 -9
- package/src/templates/Handover.global.skeleton.md +20 -13
- package/src/templates/Handover.template.md +89 -0
- package/src/templates/Plan.skeleton.md +34 -0
- package/src/templates/Prompt_Templates.md +101 -20
- package/src/templates/Runtime_Manual.md +263 -122
- package/src/templates/System.skeleton.md +31 -0
- package/src/templates/Systems.global.skeleton.md +13 -0
- package/src/templates/rules/Delegation_Rules.md +45 -0
- package/src/templates/rules/Handover_Rules.md +46 -23
- package/src/templates/rules/Migration_Rules.md +7 -7
- package/src/templates/rules/Misc_Task_Standard.md +17 -10
- package/src/templates/rules/Writing_Rules.md +33 -16
- package/src/templates/standards/README.md +37 -0
- package/src/templates/standards//345/206/231/344/275/234/350/247/204/350/214/203/README.md +120 -0
- 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
- package/src/tools/ho_backup.mjs +135 -0
- package/src/tools/ho_changelog.mjs +4 -4
- package/src/tools/ho_charter.mjs +7 -6
- package/src/tools/ho_checkpoint.mjs +36 -32
- package/src/tools/ho_docmap.mjs +239 -0
- package/src/tools/ho_gate.mjs +11 -5
- package/src/tools/ho_handover.mjs +61 -35
- package/src/tools/ho_office.mjs +46 -5
- package/src/tools/ho_profile.mjs +3 -3
- package/src/tools/ho_sessions.mjs +46 -26
- package/src/tools/ho_status.mjs +251 -0
- package/src/tools/ho_succeed.mjs +3 -3
- package/src/tools/ho_workspace.mjs +146 -42
- package/src/tools/peer.mjs +416 -0
- package/src/tools/ho_brief.mjs +0 -57
- package/src/tools/ho_diag.mjs +0 -87
- package/src/tools/ho_drift.mjs +0 -41
- package/src/tools/ho_init.mjs +0 -82
- package/src/tools/ho_pack.mjs +0 -38
- package/src/tools/ho_phase.mjs +0 -37
- package/src/tools/ho_snapshot.mjs +0 -104
- package/src/tools/ho_tokens.mjs +0 -57
- package/src/tools/peer_list.mjs +0 -37
- package/src/tools/peer_new.mjs +0 -75
- package/src/tools/peer_peek.mjs +0 -41
- package/src/tools/peer_rename.mjs +0 -31
- package/src/tools/peer_send.mjs +0 -52
- package/src/tools/peer_status.mjs +0 -46
|
@@ -0,0 +1,364 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 体系注册与文档定位(1.0):每个任务一份注册文件,写目录结构、规范文件与计划;
|
|
3
|
+
* 全局总注册只列各任务体系文件的路径,不承载正文。
|
|
4
|
+
*
|
|
5
|
+
* 路径约定:
|
|
6
|
+
* - 全局总注册:_Control_Center/records/systems.md
|
|
7
|
+
* - 项目:_Project_Meta/<项目>/System.md
|
|
8
|
+
* - 杂项:_Misc/tasks/<任务>/System.md
|
|
9
|
+
* - 全局任务:_Control_Center/tasks/<任务>/System.md
|
|
10
|
+
*
|
|
11
|
+
* 定位:注册只给路径与用途,是否读正文由会话判断;空注册不注入正文,只注入一句提示。
|
|
12
|
+
*/
|
|
13
|
+
import fs from 'node:fs';
|
|
14
|
+
import path from 'node:path';
|
|
15
|
+
import { NL, readUtf8, isFile, isDir } from './text.mjs';
|
|
16
|
+
import { projectNameOk, taskNameOk } from './role.mjs';
|
|
17
|
+
import { readProfile } from './profile.mjs';
|
|
18
|
+
|
|
19
|
+
export const GLOBAL_REGISTRY_REL = path.join('_Control_Center', 'records', 'systems.md');
|
|
20
|
+
export const REGISTRY_NAME = 'System.md';
|
|
21
|
+
|
|
22
|
+
const TICK = String.fromCharCode(96);
|
|
23
|
+
|
|
24
|
+
export function lineageOf(info) {
|
|
25
|
+
const role = info && info.role ? info.role : '';
|
|
26
|
+
if (role === 'global') return { kind: 'global', project: '', task: '', key: '全局' };
|
|
27
|
+
if (role === 'project' && info.project) return { kind: 'project', project: String(info.project), task: '', key: String(info.project) };
|
|
28
|
+
if (role === 'misc' && info.task) return { kind: 'misc', project: '', task: String(info.task), key: String(info.task) };
|
|
29
|
+
if (role === 'global-task' && info.task) return { kind: 'global-task', project: '', task: String(info.task), key: String(info.task) };
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function lineageLabel(info) {
|
|
34
|
+
const lin = lineageOf(info);
|
|
35
|
+
if (!lin) return '';
|
|
36
|
+
if (lin.kind === 'project') return '项目「' + lin.project + '」';
|
|
37
|
+
if (lin.kind === 'misc') return '杂项任务「' + lin.task + '」';
|
|
38
|
+
if (lin.kind === 'global-task') return '全局任务「' + lin.task + '」';
|
|
39
|
+
return '全局';
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** 任务库根(相对工作区)。项目优先取骨架档案登记的根,其次 `<项目>库`,最后项目目录。 */
|
|
43
|
+
export function taskRoot(ws, info) {
|
|
44
|
+
const lin = lineageOf(info);
|
|
45
|
+
if (!lin) return '';
|
|
46
|
+
if (lin.kind === 'project') {
|
|
47
|
+
const p = lin.project;
|
|
48
|
+
try {
|
|
49
|
+
const pf = readProfile(ws, { role: 'project', project: p });
|
|
50
|
+
if (pf && pf.exists && pf.profile && pf.profile.root) return String(pf.profile.root);
|
|
51
|
+
} catch { /* 骨架读不到时按目录规则推断 */ }
|
|
52
|
+
const lib = path.join('Projects', p, p + '库');
|
|
53
|
+
return isDir(path.join(String(ws || ''), lib)) ? lib : path.join('Projects', p);
|
|
54
|
+
}
|
|
55
|
+
if (lin.kind === 'misc') return path.join('_Misc', 'tasks', lin.task);
|
|
56
|
+
if (lin.kind === 'global-task') return path.join('_Control_Center', 'tasks', lin.task);
|
|
57
|
+
return '_Control_Center';
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/** 注册文件相对路径。 */
|
|
61
|
+
export function registryRelPath(info) {
|
|
62
|
+
const lin = lineageOf(info);
|
|
63
|
+
if (!lin) return null;
|
|
64
|
+
if (lin.kind === 'global') return GLOBAL_REGISTRY_REL;
|
|
65
|
+
if (lin.kind === 'project') return path.join('_Project_Meta', lin.project, REGISTRY_NAME);
|
|
66
|
+
if (lin.kind === 'misc') return path.join('_Misc', 'tasks', lin.task, REGISTRY_NAME);
|
|
67
|
+
if (lin.kind === 'global-task') return path.join('_Control_Center', 'tasks', lin.task, REGISTRY_NAME);
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export function registryAbsPath(ws, info) {
|
|
72
|
+
const rel = registryRelPath(info);
|
|
73
|
+
return rel ? path.join(String(ws || ''), rel) : null;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export function planRelPath(ws, info) {
|
|
77
|
+
const root = taskRoot(ws, info);
|
|
78
|
+
return root ? path.join(root, 'PLAN.md') : '';
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function normRel(p) {
|
|
82
|
+
return String(p || '').replace(/\\/g, '/').replace(/^\.\//, '');
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function cellsOf(line) {
|
|
86
|
+
return line.replace(/^\|/, '').replace(/\|$/, '').split('|').map((c) => c.replace(new RegExp(TICK, 'g'), '').trim());
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/** 解析任务级注册:返回 { title, structure: [{path, about}], files: [{path, about, when}], plan }。 */
|
|
90
|
+
export function parseRegistry(text) {
|
|
91
|
+
const out = { title: '', structure: [], files: [], plan: '' };
|
|
92
|
+
let section = '';
|
|
93
|
+
for (const raw of String(text || '').split(/\r?\n/)) {
|
|
94
|
+
const line = raw.trim();
|
|
95
|
+
if (line.startsWith('# ')) { out.title = line.slice(2).trim(); continue; }
|
|
96
|
+
if (line.startsWith('## ')) { section = line.slice(3).trim(); continue; }
|
|
97
|
+
if (section.indexOf('计划') !== -1) {
|
|
98
|
+
const m = /^[-*]\s*`?([^`::]+\.md)`?/.exec(line);
|
|
99
|
+
if (m) out.plan = m[1].trim();
|
|
100
|
+
continue;
|
|
101
|
+
}
|
|
102
|
+
if (!line.startsWith('|')) continue;
|
|
103
|
+
const cells = cellsOf(line);
|
|
104
|
+
if (cells.length < 2) continue;
|
|
105
|
+
if (cells[0] === '路径') continue;
|
|
106
|
+
if (cells.every((c) => /^[-:\s]*$/.test(c))) continue;
|
|
107
|
+
if (section.indexOf('目录') !== -1) out.structure.push({ path: cells[0], about: cells[1] || '' });
|
|
108
|
+
else if (section.indexOf('规范') !== -1 || section.indexOf('文件') !== -1) {
|
|
109
|
+
out.files.push({ path: cells[0], about: cells[1] || '', when: cells[2] || '' });
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
return out;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/** 渲染任务级注册文件。 */
|
|
116
|
+
export function renderRegistry(info, data) {
|
|
117
|
+
const label = lineageLabel(info);
|
|
118
|
+
const lines = ['# 体系注册(' + label + ')', '', '## 目录结构', '', '| 路径 | 用途 |', '|---|---|'];
|
|
119
|
+
for (const e of data.structure || []) lines.push('| ' + wrap(e.path) + ' | ' + e.about + ' |');
|
|
120
|
+
lines.push('', '## 规范文件', '', '| 路径 | 用途 | 何时读 |', '|---|---|---|');
|
|
121
|
+
for (const e of data.files || []) lines.push('| ' + wrap(e.path) + ' | ' + e.about + ' | ' + (e.when || '') + ' |');
|
|
122
|
+
lines.push('', '## 计划', '');
|
|
123
|
+
if (data.plan) lines.push('- ' + wrap(data.plan) + ':当前行动计划');
|
|
124
|
+
lines.push('');
|
|
125
|
+
lines.push('只给路径与用途:是否读正文由会话判断,不得无目的通读。');
|
|
126
|
+
return lines.join(NL) + NL;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function wrap(p) {
|
|
130
|
+
return TICK + normRel(p) + TICK;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/** 读取任务级注册;不存在返回 exists=false。 */
|
|
134
|
+
export function readRegistry(ws, info) {
|
|
135
|
+
const file = registryAbsPath(ws, info);
|
|
136
|
+
if (!file) return { file: null, exists: false, data: { structure: [], files: [], plan: '' } };
|
|
137
|
+
if (!isFile(file)) return { file, exists: false, data: { structure: [], files: [], plan: '' } };
|
|
138
|
+
let text = '';
|
|
139
|
+
try { text = readUtf8(file); } catch { return { file, exists: true, error: '读取失败', data: { structure: [], files: [], plan: '' } }; }
|
|
140
|
+
return { file, exists: true, data: parseRegistry(text), text };
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/** 路径合法且存在:只收工作区相对路径。 */
|
|
144
|
+
export function pathProblem(ws, p) {
|
|
145
|
+
const raw = normRel(p);
|
|
146
|
+
if (!raw) return '路径为空';
|
|
147
|
+
if (/^[a-zA-Z]:/.test(raw) || raw.startsWith('/') || raw.startsWith('\\')) return '须写工作区相对路径,不得写绝对路径';
|
|
148
|
+
const norm = path.normalize(raw).replace(/\\/g, '/');
|
|
149
|
+
if (norm === '..' || norm.startsWith('../')) return '不得指向工作区之外';
|
|
150
|
+
const abs = path.join(String(ws || ''), norm);
|
|
151
|
+
if (!isFile(abs) && !isDir(abs)) return '不存在:' + norm;
|
|
152
|
+
return '';
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
export function validateEntries(ws, entries) {
|
|
156
|
+
const bad = [];
|
|
157
|
+
for (const e of entries) {
|
|
158
|
+
const problem = pathProblem(ws, e.path);
|
|
159
|
+
if (problem) bad.push({ path: e.path, problem });
|
|
160
|
+
}
|
|
161
|
+
return bad;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/** 解析全局总注册:返回 [{ task, kind, path }]。 */
|
|
165
|
+
export function parseGlobalRegistry(text) {
|
|
166
|
+
const rows = [];
|
|
167
|
+
for (const raw of String(text || '').split(/\r?\n/)) {
|
|
168
|
+
const line = raw.trim();
|
|
169
|
+
if (!line.startsWith('|')) continue;
|
|
170
|
+
const cells = cellsOf(line);
|
|
171
|
+
if (cells.length < 3) continue;
|
|
172
|
+
if (cells[0] === '任务') continue;
|
|
173
|
+
if (cells.every((c) => /^[-:\s]*$/.test(c))) continue;
|
|
174
|
+
rows.push({ task: cells[0], kind: cells[1] || '', path: cells[2] });
|
|
175
|
+
}
|
|
176
|
+
return rows;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
const KIND_LABEL = { project: '项目', misc: '杂项', 'global-task': '全局任务' };
|
|
180
|
+
|
|
181
|
+
/** 扫描磁盘上的任务注册,重建全局总注册。返回 { rows, file, changed }。 */
|
|
182
|
+
export function syncGlobalRefs(ws) {
|
|
183
|
+
const root = String(ws || '');
|
|
184
|
+
const rows = [];
|
|
185
|
+
const pushFrom = (base, kind, nameOf) => {
|
|
186
|
+
let names = [];
|
|
187
|
+
try { names = fs.readdirSync(base); } catch { return; }
|
|
188
|
+
for (const name of names) {
|
|
189
|
+
const dir = path.join(base, name);
|
|
190
|
+
if (fs.existsSync(path.join(dir, '.deregistered.json'))) continue;
|
|
191
|
+
const file = path.join(dir, REGISTRY_NAME);
|
|
192
|
+
if (!isFile(file)) continue;
|
|
193
|
+
rows.push({ task: nameOf(name), kind: KIND_LABEL[kind] || kind, path: path.join(base, name, REGISTRY_NAME), mtime: 0 });
|
|
194
|
+
}
|
|
195
|
+
};
|
|
196
|
+
pushFrom(path.join(root, '_Project_Meta'), 'project', (n) => n);
|
|
197
|
+
pushFrom(path.join(root, '_Misc', 'tasks'), 'misc', (n) => n);
|
|
198
|
+
pushFrom(path.join(root, '_Control_Center', 'tasks'), 'global-task', (n) => n);
|
|
199
|
+
rows.sort((a, b) => (a.kind === b.kind ? a.task.localeCompare(b.task, 'zh') : a.kind.localeCompare(b.kind)));
|
|
200
|
+
const file = path.join(root, GLOBAL_REGISTRY_REL);
|
|
201
|
+
const lines = ['# 体系注册(全局)', '', '| 任务 | 类别 | 体系文件 |', '|---|---|---|'];
|
|
202
|
+
for (const r of rows) lines.push('| ' + r.task + ' | ' + r.kind + ' | ' + wrap(r.path) + ' |');
|
|
203
|
+
lines.push('');
|
|
204
|
+
lines.push('本文件只列各任务体系文件的路径;各任务体系正文不注入全局提示段。');
|
|
205
|
+
const body = lines.join(NL) + NL;
|
|
206
|
+
let changed = true;
|
|
207
|
+
try { changed = readUtf8(file) !== body; } catch { changed = true; }
|
|
208
|
+
if (changed) {
|
|
209
|
+
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
210
|
+
const tmp = file + '.tmp-' + process.pid;
|
|
211
|
+
fs.writeFileSync(tmp, body, 'utf8');
|
|
212
|
+
fs.renameSync(tmp, file);
|
|
213
|
+
}
|
|
214
|
+
return { rows, file, changed, body };
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/** 全局总注册正文;不存在时按磁盘重建。 */
|
|
218
|
+
export function readGlobalRegistry(ws) {
|
|
219
|
+
const synced = syncGlobalRefs(ws);
|
|
220
|
+
return { file: synced.file, exists: true, text: synced.body, rows: synced.rows };
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/** 注册内容注入块:全局只注入引用清单;项目与杂项注入本任务注册全文(压缩为一行一条)。 */
|
|
224
|
+
export function systemBlock(ws, info) {
|
|
225
|
+
const lin = lineageOf(info);
|
|
226
|
+
if (!lin) return '';
|
|
227
|
+
if (lin.kind === 'global') {
|
|
228
|
+
const g = readGlobalRegistry(ws);
|
|
229
|
+
if (!g.rows.length) return '体系注册(全局):暂无任务体系;新建任务后由插件登记路径。';
|
|
230
|
+
const lines = ['体系注册(全局;只列路径,不注入各任务正文)'];
|
|
231
|
+
for (const r of g.rows) lines.push('- ' + normRel(r.path) + ' —— ' + r.kind + '「' + r.task + '」');
|
|
232
|
+
return lines.join(NL);
|
|
233
|
+
}
|
|
234
|
+
const reg = readRegistry(ws, info);
|
|
235
|
+
if (!reg.exists) return '体系注册:未设置(首轮讨论体系后用 ho_docmap(action="write") 注册)';
|
|
236
|
+
const d = reg.data;
|
|
237
|
+
const lines = ['体系注册(' + lineageLabel(info) + ')'];
|
|
238
|
+
for (const e of d.structure) lines.push('- 目录 ' + normRel(e.path) + ':' + (e.about || ''));
|
|
239
|
+
for (const e of d.files) lines.push('- 规范 ' + normRel(e.path) + ':' + (e.about || '') + (e.when ? '(' + e.when + ')' : ''));
|
|
240
|
+
if (d.plan) lines.push('- 计划 ' + normRel(d.plan));
|
|
241
|
+
return lines.join(NL);
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/** 注册文件签名:文件不存在为 'none';用于热更新判断。 */
|
|
245
|
+
export function registrySignature(ws, info) {
|
|
246
|
+
const lin = lineageOf(info);
|
|
247
|
+
if (!lin) return '';
|
|
248
|
+
if (lin.kind === 'global') {
|
|
249
|
+
try {
|
|
250
|
+
const st = fs.statSync(path.join(String(ws || ''), GLOBAL_REGISTRY_REL));
|
|
251
|
+
return 'g:' + st.size + ':' + Math.round(st.mtimeMs);
|
|
252
|
+
} catch { return 'g:none'; }
|
|
253
|
+
}
|
|
254
|
+
const file = registryAbsPath(ws, info);
|
|
255
|
+
if (!file) return '';
|
|
256
|
+
try {
|
|
257
|
+
const st = fs.statSync(file);
|
|
258
|
+
return 'r:' + st.size + ':' + Math.round(st.mtimeMs);
|
|
259
|
+
} catch { return 'r:none'; }
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/** 体检:校验某类别的注册路径;不传 info 时检查全部。返回 { rows, missing }。 */
|
|
263
|
+
export function checkRegistries(ws, info) {
|
|
264
|
+
const targets = [];
|
|
265
|
+
if (info) targets.push(info);
|
|
266
|
+
else {
|
|
267
|
+
try {
|
|
268
|
+
for (const name of fs.readdirSync(path.join(String(ws || ''), '_Project_Meta'))) {
|
|
269
|
+
if (isFile(path.join(ws, '_Project_Meta', name, REGISTRY_NAME))) targets.push({ role: 'project', project: name });
|
|
270
|
+
}
|
|
271
|
+
} catch { /* 无项目元数据 */ }
|
|
272
|
+
try {
|
|
273
|
+
for (const name of fs.readdirSync(path.join(String(ws || ''), '_Misc', 'tasks'))) {
|
|
274
|
+
if (isFile(path.join(ws, '_Misc', 'tasks', name, REGISTRY_NAME))) targets.push({ role: 'misc', task: name });
|
|
275
|
+
}
|
|
276
|
+
} catch { /* 无杂项任务 */ }
|
|
277
|
+
try {
|
|
278
|
+
for (const name of fs.readdirSync(path.join(String(ws || ''), '_Control_Center', 'tasks'))) {
|
|
279
|
+
if (isFile(path.join(ws, '_Control_Center', 'tasks', name, REGISTRY_NAME))) targets.push({ role: 'global-task', task: name });
|
|
280
|
+
}
|
|
281
|
+
} catch { /* 无全局任务 */ }
|
|
282
|
+
}
|
|
283
|
+
const rows = [];
|
|
284
|
+
let missing = 0;
|
|
285
|
+
for (const t of targets) {
|
|
286
|
+
const reg = readRegistry(ws, t);
|
|
287
|
+
const entries = reg.data.structure.concat(reg.data.files);
|
|
288
|
+
const bad = validateEntries(ws, entries);
|
|
289
|
+
const planBad = reg.data.plan && pathProblem(ws, reg.data.plan) ? [{ path: reg.data.plan, problem: pathProblem(ws, reg.data.plan) }] : [];
|
|
290
|
+
missing += bad.length + planBad.length;
|
|
291
|
+
rows.push({ label: lineageLabel(t), rel: normRel(registryRelPath(t) || ''), exists: reg.exists, entries: entries.length, bad: bad.concat(planBad) });
|
|
292
|
+
}
|
|
293
|
+
return { rows, missing };
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
/** 写任务级注册:原子写,写前备份。 */
|
|
297
|
+
export function writeRegistry(ws, info, data) {
|
|
298
|
+
const file = registryAbsPath(ws, info);
|
|
299
|
+
if (!file) throw new Error('没有可写的注册路径:角色未定');
|
|
300
|
+
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
301
|
+
let backup = '';
|
|
302
|
+
if (isFile(file)) {
|
|
303
|
+
backup = file + '.bak';
|
|
304
|
+
try { fs.copyFileSync(file, backup); } catch { backup = ''; }
|
|
305
|
+
}
|
|
306
|
+
const body = renderRegistry(info, data);
|
|
307
|
+
const tmp = file + '.tmp-' + process.pid;
|
|
308
|
+
fs.writeFileSync(tmp, body, 'utf8');
|
|
309
|
+
fs.renameSync(tmp, file);
|
|
310
|
+
return { file, backup, bytes: Buffer.byteLength(body, 'utf8') };
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
// ---------------------------------------------------------------- 计划
|
|
314
|
+
|
|
315
|
+
function stamp() {
|
|
316
|
+
const d = new Date();
|
|
317
|
+
const p = (n) => String(n).padStart(2, '0');
|
|
318
|
+
return d.getFullYear() + p(d.getMonth() + 1) + p(d.getDate()) + '_' + p(d.getHours()) + p(d.getMinutes()) + p(d.getSeconds());
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
/** 归档旧计划到任务根 _archive/,返回归档路径;没有旧计划返回空串。 */
|
|
322
|
+
export function archivePlan(ws, info) {
|
|
323
|
+
const rel = planRelPath(ws, info);
|
|
324
|
+
if (!rel) return '';
|
|
325
|
+
const abs = path.join(String(ws || ''), rel);
|
|
326
|
+
if (!isFile(abs)) return '';
|
|
327
|
+
const dir = path.join(path.dirname(abs), '_archive');
|
|
328
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
329
|
+
const dst = path.join(dir, 'PLAN_' + stamp() + '.md');
|
|
330
|
+
fs.renameSync(abs, dst);
|
|
331
|
+
return dst;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
export function readPlan(ws, info) {
|
|
335
|
+
const rel = planRelPath(ws, info);
|
|
336
|
+
const abs = rel ? path.join(String(ws || ''), rel) : '';
|
|
337
|
+
if (!abs || !isFile(abs)) return { rel, exists: false, text: '' };
|
|
338
|
+
try { return { rel, exists: true, text: readUtf8(abs) }; }
|
|
339
|
+
catch { return { rel, exists: true, text: '', error: '读取失败' }; }
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
/** 写计划:先归档旧计划,再原子写入。返回 { rel, archived, bytes }。 */
|
|
343
|
+
export function writePlan(ws, info, text) {
|
|
344
|
+
const rel = planRelPath(ws, info);
|
|
345
|
+
if (!rel) throw new Error('没有可写的计划路径:角色未定');
|
|
346
|
+
const archived = archivePlan(ws, info);
|
|
347
|
+
const abs = path.join(String(ws || ''), rel);
|
|
348
|
+
fs.mkdirSync(path.dirname(abs), { recursive: true });
|
|
349
|
+
const body = String(text || '');
|
|
350
|
+
const tmp = abs + '.tmp-' + process.pid;
|
|
351
|
+
fs.writeFileSync(tmp, body, 'utf8');
|
|
352
|
+
fs.renameSync(tmp, abs);
|
|
353
|
+
return { rel, archived, bytes: Buffer.byteLength(body, 'utf8') };
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
/** 把计划写入本任务注册的「计划」一节;注册不存在时返回 false。 */
|
|
357
|
+
export function pointRegistryToPlan(ws, info) {
|
|
358
|
+
const reg = readRegistry(ws, info);
|
|
359
|
+
if (!reg.exists) return false;
|
|
360
|
+
const rel = planRelPath(ws, info);
|
|
361
|
+
const data = { structure: reg.data.structure, files: reg.data.files, plan: rel };
|
|
362
|
+
writeRegistry(ws, info, data);
|
|
363
|
+
return true;
|
|
364
|
+
}
|
package/src/lib/zstd-log.mjs
CHANGED
package/src/selftest.mjs
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* 脱离 DSH 的自测台:用假 env 直接跑工具 execute,打印只读结果,供人工比对;不参与发布门禁。
|
|
3
|
-
* 用法:node selftest.mjs [--ws <工作区根>] [--session <session id>] [--tool
|
|
3
|
+
* 用法:node selftest.mjs [--ws <工作区根>] [--session <session id>] [--tool status|gate|sessions|charter|office|succeed|all]
|
|
4
4
|
*/
|
|
5
5
|
import { resolveConfig } from './lib/config.mjs';
|
|
6
6
|
import { projCacheDir, pressurePct } from './lib/sessions.mjs';
|
|
7
7
|
import { readJsonDir, isDir } from './lib/text.mjs';
|
|
8
|
-
import {
|
|
8
|
+
import { pmStatusTool } from './tools/ho_status.mjs';
|
|
9
9
|
import { pmGateTool } from './tools/ho_gate.mjs';
|
|
10
10
|
import { pmSessionsTool } from './tools/ho_sessions.mjs';
|
|
11
|
-
import { pmPhaseTool } from './tools/ho_phase.mjs';
|
|
12
11
|
import { pmCharterTool } from './tools/ho_charter.mjs';
|
|
13
12
|
import { pmOfficeTool } from './tools/ho_office.mjs';
|
|
14
13
|
import { pmSucceedTool } from './tools/ho_succeed.mjs';
|
|
@@ -65,10 +64,11 @@ const envSucceed = Object.assign({}, env, {
|
|
|
65
64
|
|
|
66
65
|
const exec = null;
|
|
67
66
|
const cases = [
|
|
68
|
-
{ t:
|
|
67
|
+
{ t: pmStatusTool(env, { schedulerReady: () => false, schedulerError: () => 'selftest' }), a: { view: "brief" }, flag: "status" },
|
|
68
|
+
{ t: pmStatusTool(env, { schedulerReady: () => false, schedulerError: () => 'selftest' }), a: { view: "phase" }, flag: "status" },
|
|
69
|
+
{ t: pmStatusTool(env, { schedulerReady: () => false, schedulerError: () => 'selftest' }), a: { view: "diag" }, flag: "status" },
|
|
69
70
|
{ t: pmGateTool(env), a: { scope: "all" }, flag: "gate" },
|
|
70
71
|
{ t: pmSessionsTool(env), a: {}, flag: "sessions" },
|
|
71
|
-
{ t: pmPhaseTool(env), a: {}, flag: "phase" },
|
|
72
72
|
{ t: pmCharterTool(env), a: { doc: "charter", section: "8.1.1.4", maxLines: 30 }, flag: "charter" },
|
|
73
73
|
{ t: pmOfficeTool(env), a: { query: "全局" }, flag: "office" },
|
|
74
74
|
{ t: pmSucceedTool(envSucceed), a: {}, flag: "succeed", want: /没有授权/ },
|
|
@@ -77,7 +77,7 @@ const cases = [
|
|
|
77
77
|
let failed = 0;
|
|
78
78
|
for (const c of cases) {
|
|
79
79
|
if (only !== "all" && only !== c.flag) continue;
|
|
80
|
-
const bar = "====
|
|
80
|
+
const bar = "==== " + c.t.name + " " + JSON.stringify(c.a) + " ====";
|
|
81
81
|
console.log(bar);
|
|
82
82
|
try {
|
|
83
83
|
const out = await c.t.execute(c.a, exec);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
#
|
|
1
|
+
# 全局交接台账
|
|
2
2
|
|
|
3
|
-
>
|
|
4
|
-
> 由插件 ho_changelog 按格式追加(新行插在最新一行之前)。
|
|
3
|
+
> 追加式历史记录。每次修改 `_Control_Center` 的全局规则、结构或 `Global_Handover.md` 本身时,追加一行。
|
|
4
|
+
> 由插件 `ho_changelog` 按格式追加(新行插在最新一行之前)。
|
|
5
5
|
|
|
6
6
|
## 变更记录
|
|
7
7
|
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
# Workspace Charter(骨架)
|
|
2
2
|
|
|
3
|
-
> 本文件由 dsh-handover
|
|
4
|
-
> 铁律:§8.1.1.4
|
|
3
|
+
> 本文件由 dsh-handover 首次安装向导生成,只含可运行所需的最小内容;其余内容按需补充。
|
|
4
|
+
> 铁律:§8.1.1.4 的 24 行是唯一正文来源,任何项目库的 RULES.md 都须逐字复制该节(外加一行「上下文水位自查」)。修改本节须同步修改全部库。
|
|
5
5
|
|
|
6
6
|
## 8. 工程约束
|
|
7
7
|
|
|
8
|
-
### 8.1.1.4
|
|
8
|
+
### 8.1.1.4 项目侧通用约束条(可直接粘贴;修改本节须重新同步全部项目)
|
|
9
9
|
|
|
10
|
-
>
|
|
10
|
+
> 同步方式:将下列代码块内容逐行复制进 `Projects/<项目名>/<库名>/RULES.md`,并追加一行「上下文水位自查」。
|
|
11
11
|
|
|
12
12
|
```markdown
|
|
13
13
|
**运行期省 token(通用约束;项目无关,全部在役项目共用这一份正文;数值按 27 个退役会话跨项目实测定)**
|
|
@@ -41,13 +41,17 @@
|
|
|
41
41
|
|
|
42
42
|
## 9. 上下文水位自查
|
|
43
43
|
|
|
44
|
-
每个项目库的 `RULES.md`
|
|
45
|
-
|
|
44
|
+
每个项目库的 `RULES.md` 应含一行「上下文水位自查」。水位 35% 写毕交接,水位 50% 不再新开任务,水位 85% 为硬顶;压缩至多 3 次,第 4 次换任。
|
|
45
|
+
`ho_gate` 会逐库检查:标题行存在、24 行正文一行不缺、水位自查存在。缺任何一项,该库判定为未通过。
|
|
46
46
|
|
|
47
47
|
## 10. 工作区约定
|
|
48
48
|
|
|
49
|
-
- 全局交接:`_Control_Center/records/Global_Handover.md
|
|
50
|
-
- 台账:`_Control_Center/records/Global_Changelog.md
|
|
51
|
-
- 会话提示词模板:`_Control_Center/templates/Prompt_Templates.md
|
|
49
|
+
- 全局交接:`_Control_Center/records/Global_Handover.md`(由 `ho_handover` 读写,整篇重写前先归档)
|
|
50
|
+
- 台账:`_Control_Center/records/Global_Changelog.md`(由 `ho_changelog` 按格式追加)
|
|
51
|
+
- 会话提示词模板:`_Control_Center/templates/Prompt_Templates.md`(由 `ho_sessions` 的 `action=prompt` 渲染)
|
|
52
|
+
- 体系注册:全局总注册 `_Control_Center/records/systems.md`(只列各任务体系文件路径),各任务一份 `System.md`(全局任务 `_Control_Center/tasks/<任务名>/System.md`、项目 `_Project_Meta/<项目名>/System.md`、杂项 `_Misc/tasks/<任务名>/System.md`)
|
|
53
|
+
- 行动计划:任务根 `PLAN.md`;新建计划前归档旧计划到任务根 `_archive/`
|
|
54
|
+
- 暂任提示词:任务根 `Delegation_Prompt.md`;规则见 `_Control_Center/rules/Delegation_Rules.md`
|
|
55
|
+
- 写作规范:`_Control_Center/standards/写作规范/README.md`(W-01 至 W-42)
|
|
52
56
|
- 项目库:`Projects/<项目名>/<库名>/`,每库一份 `RULES.md`;项目交接在 `_Project_Meta/<项目名>/Handover.md`
|
|
53
57
|
- 杂项:`_Misc/tasks/<任务短名>/`;归档 `_Archives/`;临时与快照 `_Temp/`
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
#
|
|
1
|
+
# 全局交接文档
|
|
2
2
|
|
|
3
|
-
> 本文件由
|
|
3
|
+
> 本文件由 dsh-handover 首次安装向导生成。§7 中带 session id 的行是占位,第一个全局会话接手时将其换成本机真实的 id。
|
|
4
4
|
|
|
5
5
|
## 0. 身份与寻址
|
|
6
6
|
|
|
@@ -12,21 +12,23 @@
|
|
|
12
12
|
| 接任 | 无(首任) |
|
|
13
13
|
| 上级、报告对象 | 用户 |
|
|
14
14
|
| 本文件 | _Control_Center/records/Global_Handover.md |
|
|
15
|
-
| 冲突以谁为准 | rules/Workspace_Charter.md |
|
|
15
|
+
| 冲突以谁为准 | _Control_Center/rules/Workspace_Charter.md |
|
|
16
16
|
| 修订 | 首次安装向导生成 |
|
|
17
17
|
|
|
18
18
|
## 1. 正在做的事
|
|
19
19
|
|
|
20
20
|
| 项 | 现状 |
|
|
21
21
|
|---|---|
|
|
22
|
-
| 初始化 |
|
|
22
|
+
| 初始化 | 工作区骨架已生成;等待第一个标题含「全局」的会话接手 |
|
|
23
|
+
| 体系注册 | 全局总注册已建立路径清单,尚无任务入口;任务建立后由全局增行 |
|
|
23
24
|
|
|
24
25
|
## 2. 下一步
|
|
25
26
|
|
|
26
27
|
| # | 动作 | 落点 | 完成判据 |
|
|
27
28
|
|---|---|---|---|
|
|
28
|
-
| 1 |
|
|
29
|
-
| 2 | 按需新建项目库 | Projects/ | 各库 RULES.md 24/24
|
|
29
|
+
| 1 | 全局会话接手:核对闸门、补全 §0 身份信息 | 本文件 §0 | `ho_gate` 3 项全部通过 |
|
|
30
|
+
| 2 | 按需新建项目库 | Projects/ | 各库 RULES.md 24/24 且含上下文水位自查 |
|
|
31
|
+
| 3 | 建立任务体系注册并登记全局总注册 | `_Control_Center/records/systems.md` | `ho_docmap` 检查路径全部有效 |
|
|
30
32
|
|
|
31
33
|
## 3. 待用户决定
|
|
32
34
|
|
|
@@ -36,18 +38,23 @@
|
|
|
36
38
|
|
|
37
39
|
| # | 用户原话要点 | 状态 | 落在哪 |
|
|
38
40
|
|---|---|---|---|
|
|
39
|
-
| 1 |
|
|
41
|
+
| 1 | 安装插件并在指定目录建立全局根 | 已落地 | 本文件 |
|
|
40
42
|
|
|
41
|
-
## 5.
|
|
43
|
+
## 5. 风险与禁区
|
|
42
44
|
|
|
43
|
-
-
|
|
45
|
+
- 禁区:修改 §8.1.1.4 的 24 行正文后,各项目库的 `RULES.md` 会与本章程不一致;确需修改须同步全部库。
|
|
46
|
+
- 风险:项目库内文件由该项目会话自行修改;全局代写会使交接与库内事实分离。
|
|
44
47
|
|
|
45
48
|
## 6. 关键路径
|
|
46
49
|
|
|
47
|
-
-
|
|
48
|
-
-
|
|
49
|
-
-
|
|
50
|
-
-
|
|
50
|
+
- 交接:`_Control_Center/records/Global_Handover.md`
|
|
51
|
+
- 规则:`_Control_Center/rules/Workspace_Charter.md`
|
|
52
|
+
- 体系注册:`_Control_Center/records/systems.md`(全局总注册)、`_Control_Center/tasks/` 下各全局任务的 `System.md`
|
|
53
|
+
- 计划:各全局任务根 `PLAN.md`
|
|
54
|
+
- 暂任规则:`_Control_Center/rules/Delegation_Rules.md`
|
|
55
|
+
- 写作规范:`_Control_Center/standards/写作规范/README.md`
|
|
56
|
+
- 台账:`_Control_Center/records/Global_Changelog.md`
|
|
57
|
+
- 提示词模板:`_Control_Center/templates/Prompt_Templates.md`
|
|
51
58
|
|
|
52
59
|
## 7. 会话 id 对照
|
|
53
60
|
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# 交接文档模板
|
|
2
|
+
|
|
3
|
+
> 本文件是 dsh-handover 随插件发布的交接模板。写交接前先读第 1 节的固定流程,再按第 2 节的总则逐节填写。
|
|
4
|
+
> 本文件只规定写法;各节填写细则见第 3 至第 11 节,写完按第 12 节自检。
|
|
5
|
+
|
|
6
|
+
## 1 固定流程(强约束)
|
|
7
|
+
|
|
8
|
+
交接一律重写,不得在旧文件上续写、删改或增行。4 步按顺序执行,不得跳步。
|
|
9
|
+
|
|
10
|
+
| 步 | 动作 | 命令与判据 |
|
|
11
|
+
|---|---|---|
|
|
12
|
+
| 1 | 读上一份交接 | `ho_handover(action="read")`;文件不存在则跳过第 2 步 |
|
|
13
|
+
| 2 | 归档上一份交接 | `ho_handover(action="archive")`;输出「已归档」且 sha256 一致 |
|
|
14
|
+
| 3 | 读本模板 | 读 `_Control_Center/templates/Handover.md`,读完再动笔 |
|
|
15
|
+
| 4 | 新建空白文件并写入 | `ho_handover(action="new", text=…)`;输出「已归档」「已新建」「体检 通过」 |
|
|
16
|
+
|
|
17
|
+
禁止事项:
|
|
18
|
+
|
|
19
|
+
- 不用 `ho_handover(action="write")` 改写已有交接,该动作只用于首次创建。
|
|
20
|
+
- 不用 `ho_handover(action="append")` 追加已完成经过;`append` 只用于会话中途补一行推进状态。
|
|
21
|
+
- 不手工编辑交接文件;不把旧交接的章节搬进新文件。
|
|
22
|
+
- 不跳过归档直接写;旧文件内容只进归档,新交接只写在途。
|
|
23
|
+
|
|
24
|
+
体积不设上限:不规定字节、行数、页数。体积由结构控制,只写在途与指针;过期内容即删,经历一段时间后按归档重写。
|
|
25
|
+
|
|
26
|
+
## 2 总则:只写在途
|
|
27
|
+
|
|
28
|
+
| 应当写 | 不应写 |
|
|
29
|
+
|---|---|
|
|
30
|
+
| 正在做到哪一步、下一步、待用户决定、风险与禁区、关键路径 | 已完成经过、验证记录、需求全文、逐版构建明细、里程碑 |
|
|
31
|
+
| 指向别处的路径(台账、归档、计划、体系注册、规则文件) | 别处已写的内容副本 |
|
|
32
|
+
| 状态用表格或短句,动作写「动作、落点、判据」 | 叙述性过程与理由解释 |
|
|
33
|
+
|
|
34
|
+
已完成经过写 `_Control_Center/records/Global_Changelog.md`;项目与杂项写本任务台账或提交说明。需求全文写任务根 `PLAN.md` 或任务库文档。旧交接归档到 `_Archives/<范围>/Handover/`;杂项归档在 `_Misc/tasks/<任务>/_archive/`。
|
|
35
|
+
|
|
36
|
+
## 3 阅读顺序与填写顺序
|
|
37
|
+
|
|
38
|
+
- 阅读顺序:§0 → §1 → §2 → §4 → §3 → §5 → §6 → §7。
|
|
39
|
+
- 填写顺序:§1 → §2 → §4 → §3 → §5 → §6 → §7 → §0。
|
|
40
|
+
- 交叉引用只指向已填写的小节;回填后复核编号。
|
|
41
|
+
|
|
42
|
+
## 4 §0 身份与寻址
|
|
43
|
+
|
|
44
|
+
写明交接双方与按 id 寻址的对象。全局交接必填 7 项:会话类型、本任、链内代数、接任、本文件、冲突以谁为准、修订。
|
|
45
|
+
|
|
46
|
+
- 「本任」写标题、session id、交接时刻与上下文使用率。
|
|
47
|
+
- 「链内代数」写「第 N 代」,每换一任递增 1。
|
|
48
|
+
- 「修订」只记本次重写改了什么;其他小节不得回写。
|
|
49
|
+
- 不写职责、权限与任务边界,相关内容见章程与规范文件。
|
|
50
|
+
|
|
51
|
+
## 5 §1 正在做的事
|
|
52
|
+
|
|
53
|
+
只写状态:已推进至哪一步、尚差哪一步。动作写在 §2,本节不写下一步。存在多个在途事项时,一项一行,标明各自状态。
|
|
54
|
+
|
|
55
|
+
## 6 §2 下一步
|
|
56
|
+
|
|
57
|
+
每行一个动作,3 项齐备:动作、落点(文件或命令)、完成判据。按执行顺序排列;判据应可当场验证,不写「继续完善」这类没有终点的条目。
|
|
58
|
+
|
|
59
|
+
## 7 §3 待用户决定
|
|
60
|
+
|
|
61
|
+
只列需由用户决定的事项,一项一行,给出选项与建议,并写明该事项阻塞的下一步条目。建议不构成授权,未决定前不得按建议行动。
|
|
62
|
+
|
|
63
|
+
## 8 §4 用户要求逐条落位
|
|
64
|
+
|
|
65
|
+
从最近一轮用户消息回溯,只列未闭环的 4 类:待验收、已反馈待处理、未落地、未定。每条带用户原话摘要与落点;已验收只认用户明确表态,沉默不算验收。与 §2 同源的条目写「见 §2-N」,不重复描述动作。
|
|
66
|
+
|
|
67
|
+
## 9 §5 风险与禁区
|
|
68
|
+
|
|
69
|
+
- 5.1 风险:一条一行「现象 → 正确做法」,只写本会话遇到或核对过的事项。
|
|
70
|
+
- 5.2 禁区:一条一行「禁止动作 → 原因」,防止接任者出于好意而误操作。
|
|
71
|
+
- 章程或规范文件已载明的内容,只给一行路径,不复制正文。
|
|
72
|
+
|
|
73
|
+
## 10 §6 关键路径
|
|
74
|
+
|
|
75
|
+
只写下一步要直接使用的绝对路径与命令,不列目录树与文件清单。每条须在写交接前当场核对;无法核对时注明「未核,待确认」。
|
|
76
|
+
|
|
77
|
+
## 11 §7 会话 id 对照
|
|
78
|
+
|
|
79
|
+
全局必填,项目与杂项可删本节。只列在任与仍需联络的会话,不列已交出的;每行必须带 session id。
|
|
80
|
+
|
|
81
|
+
## 12 写完自检
|
|
82
|
+
|
|
83
|
+
1. 4 步流程是否照做,旧交接是否已归档。
|
|
84
|
+
2. §1 与 §2 是否一致,是否有已完成经过混入。
|
|
85
|
+
3. 每个下一步是否都有落点与可验证的判据。
|
|
86
|
+
4. §6 的路径与命令是否当场核过。
|
|
87
|
+
5. §0 的代数、修订与本任 id 是否准确。
|
|
88
|
+
6. 通读全文,确认接任者可一次读完。
|
|
89
|
+
7. 体检是否通过:`ho_gate` 或写入回执中的「体检 通过」。
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# 行动计划(<任务名>)
|
|
2
|
+
|
|
3
|
+
> 本文件是任务当前唯一的计划,位于任务根 `PLAN.md`。新建计划前将旧计划移入任务根 `_archive/PLAN_<YYYYMMDD_HHMMSS>.md`,归档后不读旧计划。
|
|
4
|
+
|
|
5
|
+
## 1 目标
|
|
6
|
+
|
|
7
|
+
<用一句话说明本计划要达成的结果,并写明判定完成的客观依据>
|
|
8
|
+
|
|
9
|
+
## 2 步骤
|
|
10
|
+
|
|
11
|
+
| # | 步骤 | 落点 | 完成判据 |
|
|
12
|
+
|---|---|---|---|
|
|
13
|
+
| 1 | <做什么> | `<路径>` | <执行什么、观察到什么可判定完成> |
|
|
14
|
+
| 2 | <做什么> | `<路径>` | <完成判据> |
|
|
15
|
+
|
|
16
|
+
## 3 验收
|
|
17
|
+
|
|
18
|
+
- <可机械判定的验收条件一>
|
|
19
|
+
- <可机械判定的验收条件二>
|
|
20
|
+
- 未执行的验证不写入验收结论;区分「已完成待验收」与「要求了未落地」。
|
|
21
|
+
|
|
22
|
+
## 4 风险
|
|
23
|
+
|
|
24
|
+
- <风险:已识别的问题与应对>
|
|
25
|
+
- <禁区:本次不得修改的文件或目录>
|
|
26
|
+
|
|
27
|
+
## 5 状态
|
|
28
|
+
|
|
29
|
+
| 项 | 值 |
|
|
30
|
+
|---|---|
|
|
31
|
+
| 状态 | <未开始、进行中、暂停、完成> |
|
|
32
|
+
| 最近更新 | <YYYY-MM-DD HH:MM +0800> |
|
|
33
|
+
| 关联交接 | <交接绝对路径> |
|
|
34
|
+
| 关联体系注册 | <System.md 绝对路径> |
|