dsh-task-ledger 0.0.128 → 0.0.130

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.
@@ -5,6 +5,9 @@ import { failureOf, runLedgerScript } from "./runner.js";
5
5
  function toRecord(raw, workspace) {
6
6
  const src = typeof raw.src === "string" ? raw.src : "";
7
7
  const line = typeof raw.line === "number" ? raw.line : 0;
8
+ // history --json 的 src 相对 repo 根(ledger/history/YYYYMM.md),
9
+ // skill 的 delete --src 只收相对 ledger/ 的路径(history/YYYYMM.md)⇒ 在这里收口一次。
10
+ const deleteSrc = src.startsWith("ledger/") ? src.slice("ledger/".length) : src;
8
11
  const text = (value) => typeof value === "string" ? value : "";
9
12
  return {
10
13
  // ⚠️ 必须带工作区:各仓的 src 都是 `ledger/history/YYYYMM.md`、line 各自编号,
@@ -30,6 +33,10 @@ function toRecord(raw, workspace) {
30
33
  filePath: text(raw.worklog) === ""
31
34
  ? ""
32
35
  : path.join(workspace.path, "ledger", "history", text(raw.worklog)),
36
+ src: deleteSrc,
37
+ line,
38
+ // 整行指纹:删除时原样回传给 skill(`delete --expect`),行已变就会被拒,防止行号漂移删错行。
39
+ fingerprint: text(raw.fingerprint),
33
40
  };
34
41
  }
35
42
  export async function readHistory(workspace, options) {
@@ -1,12 +1,17 @@
1
- /** 界面行内按钮对应的动作:认领 / 释放 / 取消归档 / 正常结案。 */
2
- export type TaskAction = "claim" | "release" | "cancel" | "close";
1
+ /** 界面行内按钮对应的动作:认领 / 释放 / 取消归档 / 正常结案 / 删除。 */
2
+ export type TaskAction = "claim" | "release" | "cancel" | "close" | "delete";
3
3
  /** 写命令的阶段 key(与 skill `runner.go` 里的 `stepf(...)` 一一对应)。 */
4
4
  export type TaskActionStep = "check" | "sync" | "write" | "commit" | "push";
5
5
  /**
6
6
  * 跑一条任务写命令。
7
7
  *
8
- * @param summary - 「取消」与「结案」都用:一句话理由 / 结案摘要,写进历史行与执行记录。
9
- * 留空就不传 `--summary`(skill 允许取消不带摘要;正常结案由界面强制填)。
8
+ * @param summary - 取消 / 结案的摘要、删除的留言;留空就不传 `--summary`。
10
9
  * @param onStep - 过程步骤回调(逐条转发给界面做进度翻牌),不传就不让 skill 打标记。
10
+ * @param history - 给就是删历史行(`delete --src/--line [--expect]`),不给就是删在办任务(`delete --id`)。
11
+ * `fingerprint` 是该行指纹:skill 写前核对,行已变(行号漂移 / 别处改过)就整条拒绝。
11
12
  */
12
- export declare function runTaskAction(repo: string, id: string, action: TaskAction, summary?: string, onStep?: (step: TaskActionStep) => void): Promise<void>;
13
+ export declare function runTaskAction(repo: string, id: string, action: TaskAction, summary?: string, onStep?: (step: TaskActionStep) => void, history?: {
14
+ readonly src: string;
15
+ readonly line: number;
16
+ readonly fingerprint?: string;
17
+ }): Promise<void>;
@@ -12,15 +12,17 @@ const ACTION_NAME = {
12
12
  release: "释放",
13
13
  cancel: "取消",
14
14
  close: "结案",
15
+ delete: "删除",
15
16
  };
16
17
  /**
17
18
  * 跑一条任务写命令。
18
19
  *
19
- * @param summary - 「取消」与「结案」都用:一句话理由 / 结案摘要,写进历史行与执行记录。
20
- * 留空就不传 `--summary`(skill 允许取消不带摘要;正常结案由界面强制填)。
20
+ * @param summary - 取消 / 结案的摘要、删除的留言;留空就不传 `--summary`。
21
21
  * @param onStep - 过程步骤回调(逐条转发给界面做进度翻牌),不传就不让 skill 打标记。
22
+ * @param history - 给就是删历史行(`delete --src/--line [--expect]`),不给就是删在办任务(`delete --id`)。
23
+ * `fingerprint` 是该行指纹:skill 写前核对,行已变(行号漂移 / 别处改过)就整条拒绝。
22
24
  */
23
- export async function runTaskAction(repo, id, action, summary = "", onStep) {
25
+ export async function runTaskAction(repo, id, action, summary = "", onStep, history) {
24
26
  const opts = {
25
27
  timeoutMs: WRITE_TIMEOUT_MS,
26
28
  ...(onStep === undefined
@@ -57,6 +59,25 @@ export async function runTaskAction(repo, id, action, summary = "", onStep) {
57
59
  repo,
58
60
  ], opts);
59
61
  break;
62
+ case "delete":
63
+ // 留言可选;删除时刻由 skill 外壳统一算,插件不补录。
64
+ result = await runLedgerCommand("delete", [
65
+ ...(history === undefined
66
+ ? ["--id", id]
67
+ : [
68
+ "--src",
69
+ history.src,
70
+ "--line",
71
+ String(history.line),
72
+ ...((history.fingerprint ?? "") === ""
73
+ ? []
74
+ : ["--expect", history.fingerprint ?? ""]),
75
+ ]),
76
+ ...(summary === "" ? [] : ["--summary", summary]),
77
+ "--repo",
78
+ repo,
79
+ ], opts);
80
+ break;
60
81
  }
61
82
  if (!result.ok)
62
83
  throw failureOf(result, `${ACTION_NAME[action]}失败:`);
@@ -271,21 +271,50 @@ export function registerRoutes(webServer, deps) {
271
271
  if (action !== "claim" &&
272
272
  action !== "release" &&
273
273
  action !== "cancel" &&
274
- action !== "close")
275
- throw new Error(`${BAD_REQUEST}action 只能是 claim / release / cancel / close`);
276
- // 摘要 / 理由:一句话就够,超长的截掉(历史行不该被一段小作文撑开)
274
+ action !== "close" &&
275
+ action !== "delete")
276
+ throw new Error(`${BAD_REQUEST}action 只能是 claim / release / cancel / close / delete`);
277
+ // 摘要 / 留言:一句话就够,超长的截掉(历史行不该被一段小作文撑开)
277
278
  const summary = typeof body.summary === "string" ? body.summary.trim().slice(0, 200) : "";
278
- // 正常结案必须有摘要(取消可以没有):空着也要在界面先挡住,这里兜一层
279
+ // 正常结案必须有摘要(取消 / 删除可以没有):空着也要在界面先挡住,这里兜一层
279
280
  if (action === "close" && summary === "")
280
281
  throw new Error(`${BAD_REQUEST}结案必须填一句摘要`);
281
282
  const id = typeof body.id === "string" ? body.id.trim() : "";
282
- if (id === "")
283
+ // 删历史行不用任务号,改用 skill 的 delete --src/--line 定位(src 相对 ledger/)
284
+ const src = typeof body.src === "string" ? body.src.trim() : "";
285
+ const line = typeof body.line === "number" &&
286
+ Number.isInteger(body.line) &&
287
+ body.line > 0
288
+ ? body.line
289
+ : 0;
290
+ // 行指纹(skill `history --json` 的 fingerprint):删历史行必须带 ——
291
+ // 行号只是坐标,只有整行指纹能证明「要删的还是用户看到的那一条」。
292
+ const fingerprint = typeof body.fingerprint === "string" ? body.fingerprint.trim() : "";
293
+ const historyDelete = action === "delete" && src !== "";
294
+ if (historyDelete) {
295
+ if (!src.startsWith("history/") ||
296
+ !src.endsWith(".md") ||
297
+ src.includes(".."))
298
+ throw new Error(`${BAD_REQUEST}历史文件路径不合法`);
299
+ if (line === 0)
300
+ throw new Error(`${BAD_REQUEST}历史行号不合法`);
301
+ if (!/^[0-9a-f]{12}$/.test(fingerprint))
302
+ throw new Error(`${BAD_REQUEST}历史行指纹不合法(缺少行身份,删除已拒绝)`);
303
+ }
304
+ else if (id === "") {
283
305
  throw new Error(`${BAD_REQUEST}必须给任务号`);
306
+ }
284
307
  const workspaceId = typeof body.workspace === "string" ? body.workspace : "";
285
308
  const workspace = (await workspacesOf(deps)).find((item) => item.id === workspaceId);
286
309
  if (workspace === undefined)
287
310
  throw new Error(`${BAD_REQUEST}找不到这个工作区,改不了任务`);
288
- input = { action, repo: workspace.path, id, summary };
311
+ input = {
312
+ action,
313
+ repo: workspace.path,
314
+ id,
315
+ summary,
316
+ ...(historyDelete ? { src, line, fingerprint } : {}),
317
+ };
289
318
  }
290
319
  catch (error) {
291
320
  sendWriteFailure(res, error);
@@ -5,7 +5,12 @@ export interface TaskActionInput {
5
5
  /** 任务所在工作区根(skill 定位台账用)。 */
6
6
  readonly repo: string;
7
7
  readonly id: string;
8
- /** 取消时的一句话理由(可选;进历史摘要与执行记录)。 */
8
+ /** 删除历史行时的定位(相对 ledger/ 的路径 + 原文行号);不给就是删在办任务。 */
9
+ readonly src?: string;
10
+ readonly line?: number;
11
+ /** 该行指纹(skill `history --json` 的 fingerprint):写前核对,行已变则拒绝删。 */
12
+ readonly fingerprint?: string;
13
+ /** 取消 / 删除时的一句话留言(可选;进过程文档执行记录)。 */
9
14
  readonly summary?: string;
10
15
  }
11
16
  /**
@@ -10,6 +10,8 @@ import { runTaskAction, } from "../repositories/task.repository.js";
10
10
  * 命令跑完 / 失败前都会先收到前面那几个阶段,界面据此换文案。
11
11
  */
12
12
  export async function actOnTask(input, onStep) {
13
- await runTaskAction(input.repo, input.id, input.action, input.summary ?? "", onStep);
13
+ await runTaskAction(input.repo, input.id, input.action, input.summary ?? "", onStep, input.src !== undefined && input.line !== undefined
14
+ ? { src: input.src, line: input.line, fingerprint: input.fingerprint ?? "" }
15
+ : undefined);
14
16
  invalidate();
15
17
  }
@@ -236,6 +236,12 @@ export interface HistoryRecord {
236
236
  readonly finished: string;
237
237
  /** 过程文档绝对路径;空串 = 这行没有 worklog,不可点开。 */
238
238
  readonly filePath: string;
239
+ /** 历史文件相对 ledger/ 的路径(skill `delete --src` 口径);空 = 定位不到,不给删。 */
240
+ readonly src: string;
241
+ /** 原文行号(1 起);0 = 定位不到,不给删。 */
242
+ readonly line: number;
243
+ /** 整行指纹(skill `history --json` 的 fingerprint):删这行时回传,skill 写前核对;空 = 不给删。 */
244
+ readonly fingerprint: string;
239
245
  }
240
246
  /** `/history/list` 响应:选中那个月的结案记录(按结案时间正序)。
241
247
  * 分页不在这里做 —— 整月一次下发,前端按行号做虚拟滚动。 */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-task-ledger",
3
- "version": "0.0.128",
3
+ "version": "0.0.130",
4
4
  "description": "Task ledger board for dsh (DeepSeek Harness): read-only board over task-ledger skill workspaces. The task-ledger skill is bundled — install the plugin, get the skill globally.",
5
5
  "keywords": [
6
6
  "deepseek",
@@ -57,6 +57,6 @@
57
57
  }
58
58
  },
59
59
  "dependencies": {
60
- "dsh-plugin-core": "^0.29.2"
60
+ "dsh-plugin-core": "^0.30.2"
61
61
  }
62
62
  }
package/skill/README.md CHANGED
@@ -8,7 +8,7 @@
8
8
 
9
9
  - **谁在做、做到哪**:状态只在台账 JSON —— `ledger/pool.json`(公共池)+ 每人一份 `ledger/owners/<hash>.json` 分片,索引 `ledger/owners.json`。
10
10
  - **过程正文**:全在 `ledger/worklog/<年月>/<月日>-<时分>-<说明>-<码>.md`(六节),agent 直接编辑前五节,末节「执行记录」只由脚本写。
11
- - **已结案**:一行一条在 `ledger/history/<年月>.md`(12 列),只由脚本写。
11
+ - **已结案**:一行一条在 `ledger/history/<年月>.md`(16 列),只由脚本写。
12
12
 
13
13
  状态改动与正文重写分开、全部经脚本改 —— 不另建副本、不手改状态表和历史。覆盖动作:建任务、认领、释放、结案、查在办、查历史、标签归并、体检。
14
14
 
@@ -20,7 +20,7 @@
20
20
  | `ledger/owners.json` | 索引:身份 uuid → 分片文件(`name` 只做显示 / 模糊筛) |
21
21
  | `ledger/owners/<hash>.json` | 每人一份分片:自己认领的任务 |
22
22
  | `ledger/worklog/<年月>/*.md` | 任务正文(六节) |
23
- | `ledger/history/<年月>.md` | 已结案(一行一条,12 列) |
23
+ | `ledger/history/<年月>.md` | 已结案(一行一条,16 列) |
24
24
 
25
25
  ## 为什么这么设计
26
26
 
@@ -38,7 +38,7 @@
38
38
 
39
39
  **不需要安装任何东西**:直接执行上面那个路径就行。它是个 `sh` 脚本——若某个安装环节丢了可执行位(报 `Permission denied`),前面加 `sh ` 照样能跑:`sh <SKILL.md 所在目录>/scripts/progress <子命令> --repo <工作区根>`。
40
40
 
41
- 子命令:`new` `claim` `set` `log` `close` `list` `history` `show` `whoami` `validate`。命令参考(参数 / 一句话 / 举例)、任务生命周期、纪律(数据完整性 / 人类授权 / 故障处置)全在 `SKILL.md`;参数细节看 `<子命令> --help`(不要凭记忆编)。
41
+ 子命令:`new` `claim` `set` `log` `close` `delete` `list` `history` `show` `whoami` `validate`。命令参考(参数 / 一句话 / 举例)、任务生命周期、纪律(数据完整性 / 人类授权 / 故障处置)全在 `SKILL.md`;参数细节看 `<子命令> --help`(不要凭记忆编)。
42
42
 
43
43
  ## 深入参考(references/,按需读)
44
44
 
package/skill/SKILL.md CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: task-ledger
3
- description: 本地任务台账,跨会话记录在办任务的归属、进度与起止。当用户要登记一件新的事(新建任务 / 建个坑 / 这活我领了)、记录已有任务的进展(接着做 / 记一条进展 / 这轮先记下)、查看任务状态或历史(现在该做什么 / 这活谁在做 / 上周做过什么)、给任务结案 / 取消作废 / 退回(做完了 / 验收通过 / 取消 / 作废 / 退回待认领)、整理标签或检查台账一致性时加载;只是改代码、讨论方案时不要用。
4
- version: 1.3.40
3
+ description: 本地任务台账,跨会话记录在办任务的归属、进度与起止。当用户要登记一件新的事(新建任务 / 建个坑 / 这活我领了)、记录已有任务的进展(接着做 / 记一条进展 / 这轮先记下)、查看任务状态或历史(现在该做什么 / 这活谁在做 / 上周做过什么)、给任务结案 / 取消作废 / 退回(做完了 / 验收通过 / 取消 / 作废 / 退回待认领)、删除建错的或垃圾任务(只认「删除」二字)、整理标签或检查台账一致性时加载;只是改代码、讨论方案时不要用。
4
+ version: 1.3.53
5
5
  allowed-tools: Read,Edit,Bash,Grep
6
6
  ---
7
7
 
@@ -22,7 +22,7 @@ allowed-tools: Read,Edit,Bash,Grep
22
22
 
23
23
  ## 命令参考
24
24
 
25
- 十四个子命令:`init`(初始化本机身份,一台机器一次)+ 前五个写命令(`new`/`claim`/`set`/`log`/`close`)+ 后八个只读命令(`list` / `stats` / `owners` / `show` / `history` / `root` / `whoami` / `validate`);何时用哪个见「任务生命周期」。参数语法看 `<子命令> --help`;下表说每个参数干什么、何时用。
25
+ 十五个子命令:`init`(初始化本机身份,一台机器一次)+ 前六个写命令(`new`/`claim`/`set`/`log`/`close`/`delete`)+ 后八个只读命令(`list` / `stats` / `owners` / `show` / `history` / `root` / `whoami` / `validate`);何时用哪个见「任务生命周期」。参数语法看 `<子命令> --help`;下表说每个参数干什么、何时用。
26
26
 
27
27
  只读命令里只有 `root` 特殊:**它在定位仓库之前跑**(「这个目录有没有台账」不能要求先有台账),不认 `--repo`、不读台账内容,只按结构判定。其余七个都先定位工作区根(带 `--repo` 或往上找)。
28
28
 
@@ -44,13 +44,14 @@ allowed-tools: Read,Edit,Bash,Grep
44
44
  | `--id` | 自定义任务号(一般不传;只能用小写字母、数字、汉字和连字符) |
45
45
  | `--created` | 给过去漏记的任务补建时,补录实际创建日期(`YYYY-MM-DD`) |
46
46
  | `--self` | 建并认领:直接归自己名下、状态「进行中」。仅当用户明确说「并开始做 / 并执行」时才用(判定见生命周期第 2 条) |
47
- | `--category` / `--tags` | 分类 / 标签,**只在建任务时能设分类**,口径见本节末尾 |
47
+ | `--tags <a、b>` | 标签,**必填**(不给直接报错);口径见本节末尾 |
48
+ | `--category` | 分类,**只在建任务时能设**(之后谁都改不了) |
48
49
 
49
50
  干什么:建任务、按模板建好过程文档。不加 `--self` = 只建,落公共池等认领;加 `--self` = 建并认领。
50
51
 
51
- 分类 / 标签口径(`--category` 只有 `new` 有;`--tags` 三个命令都有):`--category` 答「是什么类型」,单值,中文 ≤4 字 / 纯英文 ≤3 词,吃不准写「其他」,**建任务时给一次,之后谁都改不了**(`set` / `close` 没有这个参数,历史行里就是建任务时那个值;标题与创建时间同理,建完即不可变)。`--tags` 答「涉及什么」,0~5 个、逗号分隔,建任务时能想到就给(传空串等于没传);在办期间用 `set --tags` 补;**结案是最该给终值的一段** —— 任务做完了、全貌最清楚,用 `close --tags` 定下来,不传就沿用原标签。**分类只进台账与历史**(`list` / `history` 看得到),**不写进过程文档**;过程文档头部只有标签行,标签可以涵盖分类(「建设」「修改」这类词直接当标签写)。查询用 tags(包含匹配,见 `list --tag`)。名词性短语、中文优先。正例:`--category 缺陷修复 --tags "镜像拉取,超时重试"`;反例:`--category 已完成登录修复`、`--tags 修 bug`。
52
+ 分类 / 标签口径(`--category` 只有 `new` 有;`--tags` 三个命令都有):`--category` 答「是什么类型」,单值,中文 ≤4 字 / 纯英文 ≤3 词,吃不准写「其他」,**建任务时给一次,之后谁都改不了**(`set` / `close` 没有这个参数,历史行里就是建任务时那个值;标题与创建时间同理,建完即不可变)。`--tags` 答「涉及什么」,1~5 个、逗号分隔,**建任务必填**(没标签的任务事后没人补,建之前就想清涉及什么,别拿标题词硬凑);在办期间用 `set --tags` 改;**结案是最该给终值的一段** —— 任务做完了、全貌最清楚,用 `close --tags` 定下来,不传就沿用原标签。**分类只进台账与历史**(`list` / `history` 看得到),**不写进过程文档**;过程文档头部只有标签行,标签可以涵盖分类(「建设」「修改」这类词直接当标签写)。查询用 tags(包含匹配,见 `list --tag`)。名词性短语、中文优先。正例:`--category 缺陷修复 --tags "镜像拉取,超时重试"`;反例:`--category 已完成登录修复`、`--tags 修 bug`。
52
53
 
53
- 举例:用户说「新建个任务:把发布流程走通」→ `new --title "把发布流程走通"`(只建,不认领);用户明确说「新建个任务并开始做」→ `new --title "把发布流程走通" --self`。
54
+ 举例:用户说「新建个任务:把发布流程走通」→ `new --title "把发布流程走通" --tags 发布流程,CI`(只建,不认领);用户明确说「新建个任务并开始做」→ 同样带上 `--tags` 再加 `--self`。
54
55
 
55
56
  ### claim — 认领
56
57
 
@@ -126,6 +127,22 @@ allowed-tools: Read,Edit,Bash,Grep
126
127
  - 正常结案:`close --id <任务号> --summary "发布流程走通,验证 25 项全过"`
127
128
  - 取消(用户明确说「取消 / 作废」):「这个任务作废掉」→ `close --id <任务号> --cancel --summary "需求已被「xxx」做掉"`
128
129
 
130
+ ### delete — 删除(建错的 / 垃圾任务)
131
+
132
+ **只认一个触发词:用户原话明确说「删除」。**「删掉 / 清掉 / 清理 / 移除」都不算,**不要推测意图、也不要用反问把它们转成删除**,只回一句「要删除请明确说『删除』」;「取消 / 作废」走第 9 条,「不做了 / 先放一放」走第 5 条。
133
+
134
+ **二次确认(硬门槛,一次一条)**:即使用户明确说了「删除」,也**不许直接跑**。先复述要删的是哪一条(在办:任务号 + 标题;历史:`--src` + `--line` + 标题),再问一句「确定要删除这个东西吗?」;**只有用户明确答复确认**(「确定」「删吧」「是」)才执行 `delete`。含糊、改口、没答复、答非所问 ⇒ 不删、停下,不反复追问、不替用户脑补同意。一次确认只对这一条、这一次有效;删下一条要重新问。删了没有恢复入口,原始数据仍留在台账文件里。
135
+
136
+ | 参数 | 含义 |
137
+ |---|---|
138
+ | `--id <任务号>` | 删除在办任务(公共池或自己名下分片):移进历史,结果列留空、删除四列落值(含删除人显示名);公共池任务不必先 `claim` |
139
+ | `--src <历史文件> --line <行号>` | 删除历史表格里那一行。`--src` 收相对 `ledger/` 的路径(如 `history/202610.md`),`--line` 是 `history --json` 给出的 `line`;行上原来的结果列与摘要一律不动 |
140
+ | `--expect <指纹>` | 可省,配 `--src` 用:把 `history --json` 的 `fingerprint` 原样回传。写前核对这一行还是不是它(行号只是坐标,文件被改写过就会漂移),**对不上就整条拒绝、一个字节都不写**。建议凡拿过 `fingerprint` 就带上 |
141
+ | `--summary <留言>` | 可省;只进过程文档执行记录,不写历史摘要 |
142
+ | `--repo <工作区根>` | 同上 |
143
+
144
+ `--id` 与 `--src` 二选一。删除后该行从所有列表、统计、趋势中整行消失,**没有开关、没有恢复、没有查询入口**;原始数据仍留在台账文件里。删除是数据纠错,取消仍是「这单被正式作废」的业务事实、照常计入结案统计 —— 别把两者混用。
145
+
129
146
  ### list — 查在办
130
147
 
131
148
  | 参数 | 含义 |
@@ -322,8 +339,8 @@ allowed-tools: Read,Edit,Bash,Grep
322
339
 
323
340
  用户要「做某件事」时按这条线走。
324
341
 
325
- 1. **建(只建,不认领)—— 默认**:「新建一个任务」「新建个任务,我们来干 X」「新建个任务,我需要处理 Y」→ `new --title "…"`。落公共池、待认领。
326
- 2. **建并认领 —— 仅限明确说法**:用户明确说「新建一个任务**并开始做 / 并执行**」这类带着「现在就开始」的话 → `new --title "…" --self`。直接归自己名下、「进行中」。**除这种明确说法,其他一律按第 1 条只建** —— 别替用户决定他要开始做;拿不定,问一句。
342
+ 1. **建(只建,不认领)—— 默认**:「新建一个任务」「新建个任务,我们来干 X」「新建个任务,我需要处理 Y」→ `new --title "…" --tags a,b`(`--tags` 必填)。落公共池、待认领。
343
+ 2. **建并认领 —— 仅限明确说法**:用户明确说「新建一个任务**并开始做 / 并执行**」这类带着「现在就开始」的话 → `new --title "…" --tags a,b --self`。直接归自己名下、「进行中」。**除这种明确说法,其他一律按第 1 条只建** —— 别替用户决定他要开始做;拿不定,问一句。
327
344
  3. **找已有的**:用户说「我们来做 X」→ 先 `list --mine` 找自己名下的,在做的直接接着做;没有 → `list --pool` 里找,找到先问用户:「X 还没认领,认领它吗?」→ 用户点头再 `claim`;哪儿都没有 → **不擅自建**:问「要为 X 建个任务吗?」,同意才按第 1/2 条建,不同意就只干活、不登记。
328
345
  4. **认领**:`claim --id <任务号>` —— 认到自己名下,状态即「进行中」。
329
346
  5. **推进**:改 worklog 前五节 → 按「过程文档」节的收工三步记 `log`。卡住也 `log`(写清卡在哪,状态不动);「先放一放 / 现在不做了,一会儿做」同理 —— 是暂停,不是释放,更不是取消(取消只有用户明确说「取消 / 作废」才算)。
@@ -331,7 +348,8 @@ allowed-tools: Read,Edit,Bash,Grep
331
348
  7. **释放**:仅当用户明确说「退回去 / 释放掉」→ `set --id <任务号> --release`。任务回公共池,别人可认领。
332
349
  8. **正常结案(做完了)**:仅当用户明确说「把这个任务结束掉 / close 掉」→ `close --id <任务号> --summary "…"`,**不带 `--cancel`**;说得含糊(「做完了」「验收通过」)先反问「是要结束这个任务吗?」。收尾顺序:**先补完正文 + 记 log,最后才 close** —— 关了就不能再写。
333
350
  9. **取消(作废)**:**只有**用户明确说「取消 / 作废」→ `close --id <任务号> --cancel [--summary "原因"]`。用户说「关闭 / 结案 / 结束」一律走第 8 条,**不许加 `--cancel`**;「不做了 / 先放一放 / 以后再做」是一暂停、不是取消(走第 5 条,状态不动);「需求不成立」只是理由、不是命令。含糊先问「是要**取消(作废)**还是**正常结案(做完了)**?」。公共池任务(未认领)只有这条入口 —— 想正常结案得先 `claim`。
334
- 10. **查询**:在办 `list` / 单条 `show` / 已结案 `history`(默认只近 3 个自然月,更早的要 `--months` / `--since`)。
351
+ 10. **删除**:**只有**用户原话明确说「删除」才进入这条;「删掉 / 清掉 / 清理 / 移除」不算(判定见「delete」节),「取消 / 作废」走第 9 条,「不做了 / 先放一放」走第 5 条。**先二次确认**:复述要删哪条 → 问「确定要删除这个东西吗?」→ 拿到用户明确答复确认,才跑在办 `delete --id <任务号> [--summary "留言"]`(公共池不必先 claim)或历史行 `delete --src <历史文件> --line <行号> [--summary "留言"]`。删了就删了,没有开关、恢复或查询入口;一次确认只对这一条这一次有效。
352
+ 11. **查询**:在办 `list` / 单条 `show` / 已结案 `history`(默认只近 3 个自然月,更早的要 `--months` / `--since`)。
335
353
 
336
354
  ## 过程文档
337
355
 
@@ -363,7 +381,7 @@ allowed-tools: Read,Edit,Bash,Grep
363
381
  - **Sealed**: 2026-10-27 09:20:10 · @某人 · 8d557c6d-…
364
382
  ```
365
383
 
366
- 标签行与封卷行不一定有(没给标签、没结案就没有)。旧中文头部(`- **标签**:…` / 「**分类**」段)是旧格式,不再兼容,将统一迁移;`show --json` 不再标 `legacy`。
384
+ 标签行新建必有(`--tags` 必填;早于这条规则的老文档可能没有);封卷行只在结案后有。旧中文头部(`- **标签**:…` / 「**分类**」段)是旧格式,不再兼容,将统一迁移;`show --json` 不再标 `legacy`。
367
385
  - 执行记录行格式 `- 时间(UTC) · 类型 · @谁 · 说明`,`@谁` 是这次动手的人、不是任务现归属;只追加、不改旧行。行首「类型」是脚本内部动作名(会见到 `release`,它就是 `set --release`);认不出就别当成新命令,按 `--help` 查。
368
386
  - 只改某条任务的正文:`show --id <任务号>` 拿路径直接改,不另建 md;已结案的用 `history --months 12 --json` 找回链接(**人话输出里没有链接,必须加 `--json`**)——默认只查近 3 个月,结案久了的要显式放大窗口;只读不改。
369
387
 
@@ -379,7 +397,7 @@ allowed-tools: Read,Edit,Bash,Grep
379
397
 
380
398
  ### 数据完整性(脚本管不了的,违反了数据就脏)
381
399
 
382
- - **执行记录禁手改**:只由脚本按 `new / claim / set / release / log / close` 追加,只增不改(**交接记的是 `set`**,看不到 `handoff` 是正常的)。
400
+ - **执行记录禁手改**:只由脚本按 `new / claim / set / release / log / close / cancel / delete` 追加,只增不改(**交接记的是 `set`**,看不到 `handoff` 是正常的;取消记 `cancel`、删除记 `delete`)。
383
401
  - **改正文必配一条日志**:动了 worklog 的正文(改一句、补一节、改标题、修错字都算)就必须立刻跑一次 `log`。
384
402
  - **换人只能「释放 → 认领」**:`set --release` 放回公共池,对方自己 `claim`;不许替别人挂名字。`set --owner <别人>`(预留转接通道,agent 一律不用)。
385
403
  - **身份由脚本从本机 identity 文件自取,agent 全程不传身份**:跑命令前不用查身份;只有当命令**退出码为 100** 时,才按 `references/init-identity.md` 引导用户跑 `init`(一台机器一次)。查自己用 `--mine`,查别人才传**对方 uuid**(`list --owner <uuid>`);释放用 `--release`,不传身份。
@@ -395,6 +413,7 @@ allowed-tools: Read,Edit,Bash,Grep
395
413
  | `log` | 免确认 | 本任务的推进 / 卡点 / 交付记录直接跑,跑完一句话交代「已记执行记录:…」;要替用户下结论,先问(任务不在自己名下会直接被拒) |
396
414
  | `close`(正常结案) | 独立授权 | 仅认明确说「结束掉 / close 掉」(完整判定见生命周期第 8 条);**不带 `--cancel`**;说得含糊必须反问「是要结束这个任务吗?」;**agent 自判「干完了」不算** |
397
415
  | `close --cancel`(取消) | 独立授权 | **只有用户明确说「取消 / 作废」才跑**(生命周期第 9 条);用户没说「取消 / 作废」一律不许加 `--cancel`;「不做了 / 先放一放」是暂停,不是取消;含糊先问「取消还是正常结案」 |
416
+ | `delete`(删除) | 独立授权 + **二次确认** | **只认用户原话明确说「删除」**(生命周期第 10 条 +「delete」节);「删掉 / 清掉 / 清理 / 移除」不算,**不许反问把它们转成删除**;即使说了也**必须先复述要删哪条、问「确定要删除这个东西吗?」**,拿到明确答复确认才跑;「取消 / 作废」走 `close --cancel`,「不做了 / 先放一放」是暂停;一次确认只对一条一次有效,删了没有恢复入口 |
398
417
  | `list` / `stats` / `owners` / `show` / `history` / `root` / `whoami` / `validate` | 只读 | 直接跑 |
399
418
 
400
419
  ### 故障处置(跑之后出事怎么办)
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "task-ledger",
3
- "version": "1.3.49",
3
+ "version": "1.3.53",
4
4
  "description": "Task-ledger skill for coding agents: per-workspace progress tracking (ledger/pool.json + owners/<hash>.json shards + owners.json index, history, worklog) driven by a CLI script. Standalone install for CodeBuddy and other agents; also bundled inside the dsh-task-ledger DSH plugin.",
5
5
  "keywords": [
6
6
  "skill",
@@ -30,7 +30,7 @@ close → 历史层 + 封卷 + 移出自己名下
30
30
  - 进门:`--since` / `--until` / `--created` / `--finished` 按本机墙钟给,脚本内部转 UTC 再比较 / 落盘;除这四个参数没有任何地方要调用方传时间。
31
31
  - **过程文档正文不动**:`show --json` 的 `worklog.text` 是原文,里面的时间是当时写进去的 UTC。
32
32
  - 存量老数据写的是写入者本地时间、无标记,无法与 UTC 区分(JSON 是 2026-10-22 前,执行记录行是 2026-10-27 前)。
33
- - **history 文件 12 列**:Finished / Created / Title / Category / Tags / Owner / Owner Name / Creator / Creator Name / Summary / Result / Worklog(2026-10-29 起写英文表头;11 列旧文件不再兼容:读时跳过该行并告警,结案往里追加会直接报错要求先迁移);排序与 `--since` / `--until` 按**结案时间**。(`history` 命令的人话输出只展示其中几列:结案时间、负责人、标题。)
33
+ - **history 文件 16 列**:Finished / Created / Title / Category / Tags / Owner / Owner Name / Creator / Creator Name / Summary / Result / Worklog / Deleted / Deleted By / Deleted By Name / Deleted At(2026-10-29 起写英文表头);只认 16 列,列数不符的行静默跳过,不告警、不兼容、不提示迁移。末四列是删除标记 `yes` / 删除人 uuid / 删除人显示名快照(`@名字`,读时剥 `@`)/ UTC 删除时刻,未删留空;`Deleted == "yes"` 的行从所有列表、统计、趋势中整行忽略,原始行仍留在文件里。排序与 `--since` / `--until` 按**结案时间**。(`history` 命令的人话输出只展示其中几列:结案时间、负责人、标题。)
34
34
  - **owner / creator 两列存的是 uuid**(与任务里一致,身份只认 uuid);紧随其后的 `owner_name` / `creator_name` 两列**落盘**结案那一刻的显示名 —— 改名不回写旧行,看历史看到的就是当时那个名字。老行(升级前落的)没有名字时按名册兜底补一次展示值,**不回写文件**。
35
35
 
36
36
  ## 任务号
@@ -47,7 +47,7 @@ close → 历史层 + 封卷 + 移出自己名下
47
47
 
48
48
  ## 历史层
49
49
 
50
- - 按**结案时间**分月存放,12 列(列序:Owner 之后是 `Owner Name`,再是 Creator、`Creator Name`,Summary 之后是 Result,末列 Worklog);`close` 写历史行时:分类用任务原值(**建任务后不可改**),标签用终值。
50
+ - 按**结案时间**分月存放,16 列(列序:Owner 之后是 `Owner Name`,再是 Creator、`Creator Name`,Summary 之后是 Result,然后是 Worklog,末尾四列是 `Deleted` / `Deleted By` / `Deleted By Name` / `Deleted At`);`close` 写历史行时:分类用任务原值(**建任务后不可改**),标签用终值;`delete` 删在办任务时结果列留空、删除四列落值。
51
51
  - 存量平铺历史文件只读兼容、永不重写;`history` 连同它在内、按下面的月份区间合并。
52
52
  - **查询区间**:`history` 先由时间参数算出要读的月份文件,再逐个「存在才读、缺月跳过」,不扫磁盘。无时间参数默认最近 3 个自然月;`--months N`(近 N 月)与 `--since/--until`(结案时间闭区间)二选一;一次最多读 13 个历史文件(按月分文件,不区分连续/分散,只数总数),超出直接报错、不读任何文件。末列文档链接只按**文件名**消费(取 basename),链接里的 `../worklog/<年月>/` 前缀只为阅读器跳转服务,解析时忽略。
53
- - `history --json` 直接按文件现状解析,旧行没有的列不编造(名字列为空就按名册补展示值,不回写);存量九列的历史文件按 `docs/history-creator-upgrade.md` 一次性升级,脚本不认旧列数。
53
+ - `history --json` 直接按文件现状解析,名字列为空就按名册补展示值,不回写;只认 16 列,脚本不写迁移 / 兼容程序,老工作区由 agent 逐个手工改为 16 列。