Skip to content
Back to skills

Project Handoff

ASecurity

Maintain a project's handoff store (.handoff/, plain text): a multi-dimensional project handoff — status, summary, actions, pitfalls, decisions, commands, scope, exit, plus a machine-checked read-back — with a next-step pointer, driven by a script-enforced `handoff` CLI (single write path, coverage statement). Use when handing off between sessions or tools, or resuming or continuing prior work on a project. Initializing a new project's store or migrating an old HANDOFF.md model is done only w...

  • 3 stars
  • 0 votes
  • 0 copies
  • 4 views
  • Added October 25, 2026
ai-agentspythonshellbashgit

Works with

  • cli

Security analysis

A100/100

Pro scans all 7 files and shows the line behind each finding

Scanned October 25, 2026

npx -y skills add NinjaSln-labs/agent-skills --skill project-handoff --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Project Handoff?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Project Handoff
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/ninjasln-labs-project-handoff/badge)](https://www.skillsdirectory.com/skills/ninjasln-labs-project-handoff)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: project-handoff
description: >-
  Maintain a project's handoff store (.handoff/, plain text): a multi-dimensional
  project handoff — status, summary, actions, pitfalls, decisions, commands, scope, exit,
  plus a machine-checked read-back — with a next-step pointer, driven by a script-enforced
  `handoff` CLI (single write path, coverage statement). Use when handing off between
  sessions or tools, or resuming or continuing prior work on a project. Initializing a
  new project's store or migrating an old HANDOFF.md model is done only when the user
  explicitly asks. NOT for: ephemeral scratch notes, or work outside a project.
version: 4.9.4
metadata:
  patches:
    - "v4.9.4:**frontmatter 切分在本件里并成一处**(停手条件② 的口径改为「每套自包含代码面各 1 处单源」——本脚本随技能分发、装进别仓时不能 import 宿主仓路径,故认自己目录内的单源)。统一前三套走法并存:`_frontmatter` 用非贪婪正则、`_fm_fields` 用字符串定位闭合、回读那段用整串切分取正文尾。**实测分歧两面**:① CRLF 行尾的决策件在 `_frontmatter`(`view --id` 与写后回读走它)里读成**空**、而在 `_fm_fields`(`import` 预校验与 `supersedes` 扫描走它)里读得到字段——同一个文件在两条命令里被解析成不同结果;② 开括号多一横的畸形件被后者**假解析出 id**、前者返回空。现三套并成 `_fm_split`+`_fm_parse`,边界语义与仓内单源一致(按行找闭合、开括号须整行、不认文档结束符、正文水平线不参与切分)。**行为面零漂移**(本仓无 CRLF 决策件,已扫):`check`/`view`/`export` 三份输出改前后**逐字节相同**、`selftest` PASS。新增三条夹具钉住该性质,**鉴别力已变异验证**:把 `_fm_split` 还原成旧正则那套 ⇒ selftest rc 0→1、红在 CRLF 那条。交接块无需更新(未改纪律,`agents-block --check` rc=0)。"
    - "v4.9.3:**关账事件进流水 + 关账时机从「提交」改成「判据满足」**。① 此前 `log.jsonl` **只有 check 事件**——本仓实测 474 次 check:ok / 38 次 check:fail、**0 条 close**,条目生命周期根本没进流水,事后查不出「哪一轮闭的、当时 exit 更新没」,`exit` 过期连痕迹都没有(`handoff-freshness-check` 的 docstring 早点了这件事,它是症状不是根因)。现 `cmd_close` 落一条 `close` 事件,带被关 id/outcome/型别 + 当时的槽位快照。落点选在 `cmd_close` 而非 `apply_close`——**第一版插错了函数**(那里拿不到 `outcome`,NameError 且事件从未落地,症状是「close 成功但 log 没变」)。② 关账时机:原纪律写「提交即关账」是**错的**(用户反例证伪:干完不提交⇒条目一直挂 open;一批活拆多次提交⇒触发器与条目对不上;结论要复验才能定终态⇒与 git 无关)。正解是**完成判据被满足时立刻 close**,且**先落账再进版本**(close 写交接事实、commit 写版本事实)。该条已随技能下沉进 `references/agents-handoff-block.md`,本仓块已同批重装(AGENTS 规则 15)。实测:沙箱 close 后 log 出现 close 事件(id/outcome/typ 齐);把 `_log` 那行变异掉后事件数归 0 ⇒ 判据有鉴别力。"
    - "v4.9.2:**把交接新鲜度判据从本仓下沉进技能**(`check` 新增 fresh-queue / fresh-stale 两维)。此前它们住在本仓 `scripts/handoff-freshness-check.py`,而**消费方是交接技能的使用方、不是本仓维护者**——每件装了本技能的项目都同样需要「open 条目在 exit 里沉默」「把已闭说成候立」这两条判据,写成库专属脚本等于让兄弟项目自己抄。两条判据本身通用、可随技能走;D3 跨日陈旧**不下沉**(它硬编码了年份,跨年就错,且自述抓不到同日闭的条目);`scripts/handoff-content-check.py` 的现态计数维**留在本仓**(判的是技能库结构,别的项目没有那套结构,抄过去就是一条判不了的判据——比不给更坏,agent 会以为自己被检查过了)。本仓副本已删,命令区同步。**同源化**:新判据扫文本 id 用 `FIND_ID_RE`,与既有死指针判据共用同一模式——原先那个脚本自己抄了一份,两处模式各自演进就会出现「一次判了、另一次没判」的双源缺口。`references/agents-handoff-block.md` 增强档 A 同步改写:原写「**仅本仓**装了判据时带上」,判据随技能走后这句已成假陈述,兄弟仓现在照抄即可。本仓 AGENTS.md 交接块已同批 `agents-block --install` 重装(AGENTS 规则 15)。"
    - "v4.9.1:**补 4.9.0 三态分流没覆盖的第二支**——「认不出标记区」时,`--install` 走的是**整节收敛**,会顶掉那个 `## 交接` 节里的非空内容;而旧提示只在 install **执行之后**才打印「N 行非空内容已被收敛」,**为时已晚**。实测触发面比想象的宽:标记行**少个空格/多空格/带属性**三种都认不出(`BEGIN_MARK` 全等匹配),用户手滑一下就会走进这条路径。实测损失:用户写的「- 我自己写的条款」被标准块顶掉(节外不动、不产生第二份节——这点 install 做对了)。现提前说清:`--check` 在这支先实算非空节行数,判「整节收敛会顶掉 N 行」并要求先备份;确认无非空交接节时才说「install 会新建一节,不丢内容」。**刻意没做「放宽容差识别」**(那正是被否决的内容戳方案要连带动的四处硬编码 + C 维度)——低频场景的正确形态是**诚实文案**,不是加复杂度。"
    - "v4.9.0:**修 `agents-block --check` 的危险修法**(独立审计+分块收敛审计,实测三态)——旧形对「块内不一致」只有一句「--install 覆盖块内内容(块外不动),别手改条目」,而 `--install` 真的覆盖块内一切:**块里有人手写的本仓条款时,照这句做就静默吃掉它**(与 4.8.6 修掉的 prev/「回退建议」同型:把静默丢弃说成正常操作)。现按**行集合**(不是行位置)分三态:① 仓里有源里没有的行 ⇒ 判「人写内容」并**明说 install 会覆盖**、点名那几行、要求先抄到块外;② 源里有仓里没有的行 ⇒ 判「版本落后/被删」并**明说补装不丢内容**;③ 两侧行集合相同、只是顺序或重复不同 ⇒ 判「手工整理过」且收敛无损。顺带**修差异定位零鉴别力**:旧形用 `zip` 按短序列截断,只在块末追加一行时 **rc≠0 但一行差异都不打**(审计发现),现按 `max(len)` 补哨兵、并从「只打首个差异」改成最多 3 处 + 余量计数。配套三条夹具(人写/落后/install 后回到一致),**每个分支都经变异验证有关联力**——第一版夹具对零差异缺陷**无鉴别力**(分流文案里也含那行人写内容,把断言喂饱了),加「差异 行」标记才转红。**候补 NO_STAMP 作废**:它建立在「内容戳」之上,而戳方案已否决(打戳即标记区残缺、install 拒绝修、C 维度静默失效),前提消失故不落。"
    - "v4.8.9:**AGENTS 交接标准块与 4.7/4.8 新纪律对齐**——**① 修一处已反的断言**:块里写「手改绕过门禁」,而 4.7.8 起 `check` 逐文件对内容哈希、手改**会被抓**(沙箱实测:手写一行 ⇒ `check` rc=1),继续挂着是**假陈述**。**② 补 `set --dry-run` 纪律**:文本槽是整槽覆写、无追加语义,而块里只说「刷新 exit」——本仓自己就因此差点丢掉整槽(`--dry-run` 报「另有 467 行将丢失」),使用方会踩同一个坑。**③ 补 `rebase --force` 出路**:判据缺陷留下的历史误报修判据也消不掉,未核对就 rebase 会把真实改动一起结案、不可追回,故写明「核对在前」。**④ 未收进去的**:`--from-file`、`sync-dims`(后者维护技能自身文件、不属使用方项目)——前者是命令形式、属正文职责。"
    - "v4.8.8:独立审计(4.8.7 版)1 高 3 中 2 低已修——**[高] 漂移检查把写事件组 4 条夹具吞进 `except`**:`try:` 误缩进到 4 空格、提前关掉 `with tempfile`,其后 4 条期望(未经 CLI 记录的写入/写前旁证/补手续同字节覆写/纯 CLI 写后无新问题)**在正常路径上一次都不打印**,而漂移检查自身仍绿 ⇒ 没有任何门禁能发现(审计独立复现确认:实跑 `ok` 比应少四条)。已归位。**[中] ① 正文 CLI 枚举补 `rebase`/`agents-block`**(4.8.6 patch ⑤ 自报过、没落地,而失败出口节正把 `rebase --force` 当正式修法交给读者);**② 补 `sync-dims` 子命令**——单源化三件套的「生成」此前没有可执行命令,正文与漂移失败文案都指向不存在的脚本路径,且 `CHECK_DIMENSIONS` 上方注释称「check 的汇总行也从它生成」与实现相反,两处一并改准;**③ patch 里的 `\\x7f` 是 YAML 合法转义**,解析后变成真 DEL 字节(审计实测 `has DEL: True`)⇒ 改 `\\x7f`。**④ 两处硬写断言条数改指路式**(4.8.5 立的规则 13,此处漏了)。**⑤ [低] 失败出口节只覆盖部分判据**、M4(源与块同步改成假陈述仍绿,机械判不了)登记为候补,本轮不加判据。"
    - "v4.8.7:判据清单单源化试点(治「只改了一半」)——「`check` 有哪些判据」此前被复述在两处(`cmd_check` 的代码段与本文正文的清单),加一条判据要改两处,本会话已因此反复分叉。现新增 `CHECK_DIMENSIONS`(判据 id 加一句描述)为**唯一产生源**,`SKILL.md` 的清单由 `render_check_dims()` **生成**(带 begin/end 标记),并加**漂移检查**:改了判据不重跑生成,`selftest` 立刻红并指出差异行。**三件套(代码维护 + 生成 + 漂移检查)缺第三件就等于只换了个地方漂移**,本条特意点明。判据的描述文案在 `check` 的运行时输出里**不复述**(避免第三处)。"
    - "v4.8.6:独立审计 1 高 2 中 2 低已修——**[高] `prev/` 的「回退建议」是死命令且危险**:被骤降/空内容守卫拒绝时**一个字节都没写**、槽仍是原内容,**根本不需要回退**;而 `prev/` 存的是**上一次成功写入**的版本,读者照那条建议复制反而会把当前内容**覆盖成上一个版本且全程无警告**(独立审计自建样例:当前 W、prev 是陈旧 V,照做 ⇒ W 被 V 覆盖)。**同批四处**:两条运行时 die 文案 + 正文「快照先于全部守卫」(与 4.8.4 实现**完全相反**)+ 失败出口节。**② [中] 分发点 hook 补夹具**(此前零夹具,而独立审计变异实测它有真实鉴别力:手改 `scope` → `scope add` → `check` 会报;只删分发点那一处则永静默)。**③ [中] patch 的「上移」改准**:实现是**复制**到分发点,各写命令内的同名调用**仍在**(变异证明已成死代码,保留为冗余兜底,但别称分发点为「唯一入口」)。**④ [低] `echo_back` decisions 分支的 `title` 是死键**(writer 只把 title 写成正文 H1,frontmatter 永不含它)——4.8.4 刚把 `title` 当死项从通用键表删掉,分支里那份漏了,半成品。**⑤ [低] 正文 CLI 枚举补 `rebase`**。selftest 断言条数以实跑 `handoff.py selftest` 的 `ok` 行数为准。"
    - "v4.8.5:本条自身按 AGENTS.md 规则 13「会变的数字只允许有一个产生源」改写——原文写「本仓 66 条」这类**会变的自报计数**,现改为**指路式**(条数以 `handoff check` 实算输出为准,勿在注释里写死计数)。规则来自 2026-10-03 的负结果实测:分类器调优三轮、覆盖 93 件 patches,现状型候选里**真漂移 0、误伤 4-5 处**(规范阈值、引用的报错文本、举例数字、方法描述)⇒ 噪声比信号多,**不做闸**、从源头消掉漂移面。"
    - "v4.8.4:独立审计第二批 3 条已修——**① 写事件覆盖面小于措辞**:`unconfirmed.jsonl` 原不在对账面(手改它 `check` 仍 rc=0),已补;`scope` 的三个写分支原**不记写事件** ⇒ `scope add` 之后**任何**写命令都把它当成「CLI 之外的改动」(**纯 CLI 即可复现**的误报),已让它们走 `write_index`;并在 `main()` **分发点**统一调一次 `note_pre_tamper()`(幂等)——逐个加是「靠人记得逐个加」的老毛病(与 p000016「新增 flag 忘了同步」同源)。**准确说法是「复制到分发点」而非「上移」**:各写命令内的同名调用**仍在**(独立审计变异证明它们已成死代码:只删分发点那一处,selftest 仍全绿)。保留它们是有意的冗余兜底,但别把分发点说成「唯一入口」。**② `echo_back` 在 decisions 路径把成功报成「异常」**(decisions 是 `decisions/<id>.md`、不在 live/closed 里,原实现查不到就报异常——**把成功报成失败**,与 P3 的本意相反),已补 decisions 专用回读(含 `body`);并按**实测**把通用键表收成 live/closed 行真实有的字段(`summary`/`topic`/`detail`/`src`/`outcome`),上一版塞进去的 `title`/`body`/`ref` 全是**死项**。**③ 副作用边界**:`--from-file` 同文件**重复键**改为报错(静默后者覆盖=静默丢弃);`prev/` 快照从「所有守卫之前」移到「真正写入之前」(`--dry-run` 与被拒的写入都**不该**留痕迹——原实现 `--dry-run` 明明输出「未写入任何内容」却改了 `prev/`);`rebase` 补 `index` 存在性守卫(与 `_log`/`log_write` 一致),非库目录不再凭空造出 `writes.jsonl`。"
    - "v4.8.3:M6「`next` 只写一次」补 **mock 覆盖**(monkeypatch `Store.set_next` 数调用次数,断言 `close` 期间**恰好被调用 1 次**且那一次写的**就是最终值**;**零生产代码改动**)。此前它「结构上不可测」——两步写与一步写的**最终值完全相同**,看落盘结果区分不了;那是**测错了层面**(该测调用序列而非终态),不是不可测。变异复测:恢复两步写 ⇒ 2 项红,**M6 由「无鉴别力」转为有鉴别力**。"
    - "v4.8.2:独立审计 7 条必须改的第一批——**① [高] 4.7.5 × 4.7.8 互伤致永久红**:`edit --domain` 换分区会「搬空即删」旧分区文件(4.7.5 的刻意修复),而写事件**只记「写」不记「删」** ⇒ `check` 把「latest 里有、落盘没有」一律判成「疑似被 CLI 之外删掉」⇒ 每次换分区后永久红,**且 `rebase` 也解不开**(它同样保留 `latest`)。根治:写事件增 `removed` 字段(记录被 CLI 删掉的文件),对账时区分「经 CLI 正常删掉」与「凭空消失」,`rebase` 的新基线把已删文件剔除。**② 正文自相矛盾**:失败出口节里「`add`/`edit` 报「含 shell 危险字符」」那条是 4.7.7 旧措辞,与实现(4.7.9 起是**提示**不拒收、输出串为「特殊字符」)矛盾,已删。**③ `note_pre_tamper()` 插在 docstring 之前** ⇒ `cmd_add`/`cmd_set`/`cmd_rm` 的 `__doc__` 变 None(独立审计实测抓到),已全部移到 docstring 之后。**④ 三项能力零夹具**(审计变异测试证明改了 selftest 仍全绿)已补夹具:`--dry-run` 必须在骤降守卫**之前**、**确实不落盘**且逐条列出将丢失的行;`rebase` 必须**真丢弃**带旁证的事件;`next` 换分区后落在最终值。selftest 断言条数以实跑输出为准,变异测试 M1/M2/M6/M7 由「无鉴别力」转为变红;M6「`next` 只写一次」用 **mock 数调用次数**覆盖(monkeypatch `Store.set_next`,**不给生产代码加测试钩子**)——光看最终态测不出来,两步写的最终值与一步写相同。"
    - "v4.8.1:**修 p000069**——`close` 拆两阶段:`plan_close()` 纯算、零落盘(`require_text`/找 live/引用守卫/**含补位候选**全部前置),`apply_close()` 纯写、无 `die`、无依赖外部状态的分支。旧形把「算补位」放在落盘**之后**,补位策略一旦抛错就是半写:条目已归档、`next` 仍指着已关条目,而 `check` 只能事后发现。两个关键实现点:**① `refill_pick`/`refill_pool` 加 `exclude`**——阶段 1 算候选时源行**还没删**、被关条目**仍是 open**,不排除它就会「把自己补成 next」(漏了这点,方案就从根治变成换一个 bug);**② `next` 只写一次**——旧形先 `set_next(空串)` 再 `set_next(nid)`,中间那个窗口正是「指针既不是原值也不是新值」的事故现场。配套 **B 兜底**:`check` 报 next 失效时**给出可执行修法**(`handoff next --auto`)而非只报错;并顺带**修了 `next --auto` 本身**:池空时旧形直接 `die`、什么也不写,于是「next 指向已关条目且池里确实无可补」时,照着 check 的修法跑完**指针依然坏着**——指引给了一条不保证有效的命令。现池空即**清空指针**(`--auto` 的语义本就是「把 next 设成按策略算出的值」,池空时那个值就是空)。"
    - "v4.8.0:修 `p000016` 的 Namespace 半写崩溃——`cmd_close` 改收**带默认值的显式形参**(`id, outcome=None, no_refill=False, **_ignored`)并同步 dispatcher 与 `unconfirmed resolve` 的内部调用点。旧形收 `argparse.Namespace`、调用点手挑字段:将来给 `close` 加 flag 忘了同步,就在访问新字段处 AttributeError,而那已在**半写状态**(条目已进 `closed`、指针仍指已关条目,要靠 `index.next` 门禁才发现)。显式形参**漏传也不崩**(Python 语义保证),不依赖人记得同步;`**_ignored` 吸收 dispatcher 传来的其余 CLI 字段。全仓**手工构造 Namespace 从 1 处降到 0**。顺带**修正 4.7.9 的 pre_tamper 修法**(第一版把快照从「每次 write_index 算」改成「每 Store 实例算一次」,可调用时机仍在**内容写完之后** ⇒ 等于没修,真源 `set exit` 仍自误报)。现由**五个写命令在入口第一行**调 `note_pre_tamper()`。配套补了一条**能区分修没修好**的夹具:在**干净 store** 上连写 3 次 `set status` 后 check 不得报 pre_tamper,并配一条**对照**(真手改后跑一次写必须报出来)——原先那条「补手续」断言期望的就是报出来,修没修好都绿,夹具形同虚设。"
    - "v4.7.9:**shell 引号族(p000019/p000041)根治的第二步**——① argv 危险字符由**硬拒降为提示**:本机实测单引号内反引号/`$` **不展开**,而 CLI 收到的值**无法区分**「双引号已被展开」与「单引号本就安全」(前者已无反引号、后者还带字面量)⇒ 硬拒必然**误拒单引号这种安全写法**,而该抓的那批又抓不到。**更正一处先前转述**:CWE-88 原文说的是「不要**只**依赖查找畸形输入」,并明确承认 denylist「对判定某些输入畸形到应当直接拒绝是有用的」——它没否定 denylist;Parameterization 才是唯一 High 效力项(即 `--from-file`)。② **新增写后回读**(`echo_back`):`add`/`edit`/`close` 写完把落盘行读回来显示(长文本前 160 字)——**这是唯一能覆盖「已被 shell 吃掉」那一面的判据**,因为它看的是**结果**而不是猜输入形态:双引号被吃掉时当场可见,单引号安全写法零干扰(已实测四个场景)。③ **修 4.7.8 的判据缺陷**:pre_tamper 原在 `write_index` 里取快照,而 `set`/`add`/`edit` 的写序是「先写内容、后 write_index」,于是**本次自己的写**也被当成外部改动 ⇒ 每次经这些命令写 `status`/`exit` 都误报(真源实测:连我自己用 `set exit` 回填一次都被报)。改为**写命令开始时**取快照。④ 新增 **`rebase --force`**:把写事件基线重建为当前落盘事实。`audit_writes` 扫全部历史事件,判据缺陷留下的误报**修判据也消不掉**(会被永久卡红),而唯一的手动出路是手改 `writes.jsonl`——那正是本工具要防的事。rebase 需显式确认、逐条打印「已结案」清单、留 `__rebase__` 标记,不是静默自愈。"
    - "v4.7.8:把 **p000025(补手续)从纪律变成判据**——新增 append-only 写事件 `writes.jsonl`:每次 CLI 写记录各受管文件的**内容哈希 + 纳秒级 mtime**;`check` 两维对账:**哈希对不上** ⇒ 存在**未经 CLI 记录的写入**(绕过 CLI 手改文件);**写之前**发现某文件 mtime 已被外部动过 ⇒ 疑似「先手改、再用 CLI 覆写同样字节」的补手续(内容哈希可能仍与记录一致,只有时间戳留下了)。时间戳用 `st_mtime_ns` 而非 `int(st_mtime)`——秒级取整会把同一秒内的多次写压成同一个值,判据当场全绿(第一版就这么废的)。顺带补上一个**漏记**:`set` 原先不调 `write_index`,写事件在最常用的写命令上**根本没产生**。"
    - "v4.7.7:**根治 shell 引号族**(p000016/p000019/p000035/p000039/p000041,本会话已踩四次)——它们到达 CLI 时**通常已被 shell 展开或截断**,所以 CLI 看见的往往不是用户想写的内容。① 写入型参数新增 **`--from-file <路径|->`**:格式为每行 `字段=值`、值可用行尾 `\\` 续行,**由程序解析、完全不经 shell**——反引号与 `$` 原样落盘(实测含反引号的代码片段与 $HOME 引用均原样入库)。② 内联值若含 **反引号或 `$`** 即**拒收并指向 `--from-file`**:denylist 单独不够(CWE-88:denylist 不可作唯一防线,parameterization 才是 High 效力缓解),但它能把「shell 吃掉了内容」当场暴露,而不是让人拿着一段被截断的文本以为写对了。③ **反斜杠刻意不拦**(只做转义、不替换内容,且 Windows 路径/Markdown/代码引用里极常见,拦它是误拒)。④ 危险字符检查**只作用于走 argv 的值**——`--from-file` 来的内容从未经过 shell,拦它同样误拒;空串守卫对两者都生效(数据丢失与来源无关)。⑤ `--from-file` 与 flag 给同一字段即报错,不静默取一个。"
    - "v4.7.6:修 `p000029`——`set` 骤降守卫原带 `len(old) > 200` 的**盲区**(2026-10-03 实测:旧内容 123B/315B 时只喂一行 → 截断到 17B 却**静默放行 rc=0**,1957B 起才拦;而小仓/新项目恰最容易被随手写一行,那条下限把它们全漏了)。现改为**旧内容非空即判**(不设字节下限)——「整槽覆写 vs 局部编辑」是语义问题、不该由文件大小决定。配套两件:**`set --dry-run`**(写入前逐条列出**将丢失的行**、不落盘) + **拒写信息直接给出可复制的恢复命令**(`prev/<slot>` → 槽位)。**已知边界(如实写明)**:极小内容上比例阈值不可靠(25 字符删到 10 字符=40% 降幅,分不清「有意删一行」与「只喂表头」),这类残余误用由 `--dry-run` 兜而非继续调阈值——阈值再调就开始误伤正常的小幅删减。"
    - "v4.7.5:**Windows 原生实测推翻了两条推演**(用户提示可连 Windows 后实测,走 Win32 `CreateFile`=CPython 同一条路)。① **撤销设备名拒绝**:`CON.jsonl`/`NUL.jsonl`/`con.jsonl`/`COM1.jsonl` 实测**全部创建成功、`File.Exists=True`、目录枚举可见** —— 设备名解析发生在 cmd/PowerShell **路径层**,不在 `CreateFile` 层(用 `New-Item` 验会因两层解析不一致得到自相矛盾的结果,第一轮差点据此下错结论)。同理撤销尾随空格/点(`com1 .txt` 实测正常创建)。② **保留 `:` 拦截**:ADS 实测写失败且不可见。③ **新增判据「同槽分区名大小写折叠后不得重名」**:NTFS 大小写不敏感(实测 `Case.jsonl` 与 `case.jsonl` 枚举只得 **1** 个)⇒ 仅大小写不同的两个 domain 会**合并成一个分区文件**、index 计数与现实脱节;**该判据在 Linux 上也判**,因为「折叠后重名」是平台无关的前提、在 Linux 上无害而在 Windows 上丢数据。④ 该判据**暴露出一处既有残留**:`edit --domain` 换分区后旧分区被搬空却**留着空文件**、仍占着分区名——已改为搬空即删。"
    - "v4.7.4:正文不再写死 `selftest` 的断言数(实测 13,且会随夹具增长)——改为「要数就跑 `selftest` 数 `ok` 行」;补齐 v4.7.3 的 changelog 条目。"
    - "v4.7.3:把 `guard_partition()` 的鉴别力**落成仓内夹具**(`selftest` 新增该组:危险名必拒 + 合法名必放行 + 被拒后不留子目录 + 跑完 `check` 仍绿)——此前只在 patches 里**自报**过例数,而自报数会随库漂移、且对没有本仓的消费者恒假。同时:`fixed` 语义 docstring **首段**更正(先前只改了尾部更正段,首段仍反着说,而人先读到的是首段);设备名 stem 补 `rstrip(' .')`(`com1 .txt`/`nul .jsonl` 原能绕过,Windows 仍按设备名处理 ⇒ 静默丢弃);控制字符判据收准为 **ASCII C0**(`\\x7f`/ZWSP/RTL 刻意不拦——不改路径指向、幽灵模式不复现,拦了属超纲)"
    - "v4.7.2:`guard_partition()` 补 **Windows 语义**——独立审计实测三种值能在 Windows 上**精确复现 p000046 的同一失效模式**(已登记、check 仍 OK、index 计数偏低、无任何报错):`C:evil`(盘符相对→写到 store 之外)、`ok:hidden`(NTFS 备用数据流→`glob` 永扫不到)、`CON`/`NUL`(保留设备名→**落盘静默丢弃**,目录里根本没有)。前一轮 docstring 自称「只禁改变路径指向的东西」,而 `:` 与设备名同样改变指向 ⇒ 那是该原则的**未完成实现**,不是超纲要求(本技能正文承诺支持 Windows)。现一并禁 `:`、保留设备名(CON/PRN/AUX/NUL/COM1-9/LPT1-9,大小写不敏感、看扩展名前的 stem 并去尾随空格点)、ASCII C0 控制字符。**双向鉴别力已落成仓内夹具**(`selftest` 的 `guard_partition` 组:14 类危险名必拒 + 9 个合法名必放行 + 被拒后不留子目录),**不靠 patches 自报数字**——自报数会随库漂移、且对没有本仓的消费者恒假"
    - "v4.7.1:`pitfall` 的 `fixed` **语义改正**——它**不是销账**。「缺陷已修、留案以免重犯」:条目仍在 live 区、仍计入各槽计数、仍被 `check`/`view` 看到,只是不进 `closed/`。**别把它读成销账**——它是**基本自述**(唯一读它的判据是 next 指针的「须为 `open`」,那是在守 next 有效性、不在销坑;**别为它加闸**)。**别把「多数坑都 open」读成「多数坑都没修」**——本库实测过一批:多数坑其实有守卫已修、有意设计的不算缺陷、真正的遗留缺陷是少数。写死这个语义是因为发现「坑槽 57 条全 open、0 条进 closed」被误读成「销账机制失效」;实测 `edit --status fixed` 完全可用,真根因是**『已修』本就不可从落盘证据机械判定**,钉不进判据、退化为自述"
    - "v4.7.0:修 `p000046` 路径注入——分区键(`--domain`/`--purpose`)会被拼成文件名(`<槽>/<值>.jsonl`),值里带 `/` 会写进**子目录**,而加载面用 `glob('*.jsonl')` **不递归** ⇒ 那条已登记的条目对 `check`/`view`/`index` **完全隐形**(实测:check 报 rc=0 OK、index 计数 0、递归扫到 1 条,无任何东西报错)。新增 `guard_partition()`,在 `add`、`edit` 换分区、`import` 三条路径统一校验。**不用字符白名单**(会把本库实际在用的 CJK domain「交接」「发布」「门禁」和 `_global` 一起拒掉),只禁**改变路径指向**的东西:分隔符/NUL、`.`/`..`、前后空白、超长名"
    - "v4.6.2:作废口径**单源化**——新增 `_voided_ids()`(=`void` ∪ `trash/*.jsonl` ∪ `trash/*.md`),由 `check` 的 D1 与 `view --id` 的已作废分支共用。原先两处各算一遍(`check` 算 void∪trash、`view` 只算 void),只差一步就会分叉成「check 说已作废、view 说查不到」——判据间的口径必须同源,否则同一事实两处结论相反。实测 trash-only(文件只在 `trash/`、不在 `void`)场景下两者同声。"
    - "v4.6.1:`view --id` 补 `trash/`·`void` 分支——对已 `rm` 过的 id 报「未找到」rc=2,与本技能「已作废 ≠ 不存在」的口径**相反**(会把人引去查一个根本不该再查的东西);改为一律先查作废,命中即打印「已作废」rc=0,真查不到才退 2。与 `view` 补 decisions 同批落地(4.6.0)。"
    - "v4.6.0:`view --id` 修**假报**——decisions 槽是 `decisions/<id>.md`(frontmatter + 正文),不在 `load_live()`/`load_closed()` 覆盖的 jsonl 槽里,故原实现对**每一条**决策记录(本仓全部;条数以 `handoff check` 实算输出为准,勿在注释里写死计数)都报「未找到(live 与 closed 均无)」并退 2。查不到的假报比查不到更坏:接收方据此以为决策丢了。现补查 decisions 目录并打印文件路径;「未找到」文案扩为「live / closed / decisions 均无」"
    - "v4.5.0:`check` 增两条**通用**对账(随技能走,所有使用方自动获得)——① 文本槽 `status`/`summary`/`exit` 点名的条目 id 必须既不在册、也不在 `trash/`·`void` 之外(死指针;`check rc=0` 而散文漂移是最常见的『看起来交接没问题』)。两个刻意的收窄:**已作废 ≠ 不存在**(`rm` 后文本写「t000105 作废」是正当历史陈述,判它死指针等于逼人把正确记录改掉);**不判 `c` 前缀**(`c` 型是 commands 槽;排除它的收益是零成本——模式长度恒为 7 字符,6 字符短 hash 够不着,`c697750` 也不匹配(因 `c` 不在字符类,与词边界无关))。② `next` 补「status 必须是 open」(原只校验存在)。**收窄 `d`(10-03 用户裁定,走停手线后落地)**:字符类里只有 `d` 是 hex 字符,故 7 字符全 hex 的 commit 短 hash 会被当 `d` 型条目——可复现的假阳性。已把 `d` 从判据字符类里去掉,并先修好 `view --id` 对 decisions 槽的假报(原先**每一条**决策记录都报「未找到」,而它们在册)——收窄的前提是「另有语义正确的检查面」,那个面当时是坏的。 另:AGENTS.md 标准块那句「check 只管结构」同步改为「结构 + 通用对账」"
    - "v4.4.0:`agents-block --install` 改为**整理覆盖**——已有 `## 交接` 整节但无标记区时收敛该节,而不是拒绝(旧行为要求人先手工并入标记区);交接标题的识别只认标题行本身(不吃提到交接的普通段落,含三级标题 `### 交接`);标记区只有 begin 或只有 end(残缺)时**拒绝并报错**,不再静默整节替换;仓里有多份标记区/多个交接节时**合并到一份**(`--check` 独立报「多份并存」,不靠 install 才有痕迹)"
    - "v4.3.2:标准交接块随 `init` 自动装(`agents-block --install`,只动块内、块外内容不变)——写入是使用方自己项目里的动作,库维护者不跨仓代劳;块内去掉写死的 `python3` 与仓内相对路径(技能装在 `~/.agents/skills/`,各仓路径不同、解释器随平台而异),命令准确形式留在正文并补 Windows 用 `python`"
    - "v4.3.1:退出码纪律 + 门禁退出码只取脚本本身(不判管道末端)"
slug: project-handoff
displayName: project-handoff
---

# 项目交接(Project Handoff · v3 存储模型)

## 角色

维护项目**交接存储** `.handoff/`(纯文本):**9 槽** = 一份「让接收方(agent / LLM)接得上」的**多维交接件**(**不止未决项**)。条目**只经**本技能自带的 CLI 写(准确命令形式见下 `## CLI`;**单一写入口** + **机检门禁**)。

- **维度闭合**:9 槽固定;槽外信息 → `unconfirmed`(未能确认),**不即兴开槽**。
- **只记不可推导项**:可读的用指针,读不到的才记——交接的意义=**免重读仓库**。
- **P1 不删 / P2 可重建 / P3 不静默**(见下)。

## 何时读哪个相位

| 意图 | 读 |
|---|---|
| 接手 / 继续之前的工作 / 新 session 进入已有项目 | `references/resume.md` |
| 交接 / 收尾 / 跨会话·跨工具移交 | `references/handoff.md` |
| 新项目 / 尚无工作存储 | `references/init.md`(**仅显式调用**,非自主命中)|
| **发现旧模型**(`HANDOFF.md` / `HANDOFF-ARCHIVE/` / `.handoff/fp.*`)| **先** `references/migrate.md`(一次性,**仅显式调用**)|

## 存储(`.handoff/`,纯文本,不假设 git)

```
index   status   summary   scope   exit   next          # 导航 + 单文件槽 + 指针
actions/<域>.jsonl    pitfalls/<域>.jsonl    commands/<用途>.jsonl
decisions/<id>.md     unconfirmed.jsonl      closed/
prev/<slot>                                     # 单文件槽覆写前快照(自动,回读用)
```

- **9 槽**:S1 `status` · S2 `summary` · S3 `actions` · S4 `pitfalls` · S5 `confirm`(运行时,不入盘)· S6 `decisions` · S7 `commands` · S8 `scope` · S9 `exit`;基础设施:`index`(脚本生成)/ `next`(「下一步唯一一条」指针)/ `unconfirmed`(未能确认账本)/ `closed`(归档)/ `log`(使用快照)。
- **条目(JSONL,键序固定)**:`{"id","created","summary",[status,blockedBy],topic?,src?,detail?}`;归档行加 `closed`/`outcome`。
- `id` = `<type><6 位>`;`type↔槽`:`t`→actions / `p`→pitfalls / `c`→commands / `d`→decisions / `u`→unconfirmed / `q`→closed(历史)。**全槽唯一、永不复用、脚本分配**。
- **决策**:`decisions/<id>.md` = frontmatter(`id/created/status/domain/topic`) + ADR 正文。
- **`index` 由脚本重建**;**永不手写**。

## CLI(技能自带脚本,路径见下方 `$H`;解释器 `python3`,Windows 用 `python`)

> 节标题**不写死仓内相对路径**——各仓装本技能的位置不同,写 `scripts/handoff.py`
> 会让别的项目的 agent 去执行一个它那儿不存在的文件。

`init` · `index` · `check` · `selftest` · `log` · `add` · `set` · `edit` · `rm` · `close` · `next` · `unconfirmed` · `scope` · `confirm` · `filter` · `view` · `export` · `import` · `rebase` · `agents-block` · `sync-dims`。

- **`check` = 相位门禁**:不过即非零退出,相位不得前进。它逐条检下列维度(**这份清单由 `CHECK_DIMENSIONS` 生成**:加判据只改代码那一处,再重跑生成;忘了会被漂移检查点名):

<!-- check-dims:begin (由 CHECK_DIMENSIONS 生成,勿手改)-->

1. **slots** — 9 槽与 `index` 齐备(status/summary/scope/exit + 四目录 + unconfirmed)
2. **id** — id 形如 `<t|p|d|c|u><6 位>`、全局唯一、**前缀与所在槽相符**
3. **status** — live 行的 `status` 在该型闭集内(**`closed` 不是可写值**,关闭只经 `close`)
4. **fields** — 未知字段 + 必填字段缺失(按型)
5. **dates** — `created`/`closed` 是合法日期、非未来日、且 `created ≤ closed ≤ today`
6. **single-line** — `summary` 单行(不得含换行/制表)
7. **next** — `next` 是合法 id、存在、且 **`status=open`**(附可执行修法)
8. **text-dangling** — 文本槽(`status`/`summary`/`exit`)点名的 `t/p/q/u` 型 id 必须既不在册、也不在 `trash/`·`void` 之外(**已作废 ≠ 不存在**;`c`/`d` 两型不参与,各有专门检查面)
9. **decision** — `decisions/*.md` 的 frontmatter id 必须等于文件名、日期合法、status 在闭集
10. **scope** — `scope` 登记指向**活路径**或 glob 有命中(机械判据:登记必须可解析)
11. **void-reuse** — id 未复用已作废 id(`void` 拦截)
12. **writes** — 写事件对账:内容哈希对不上=**未经 CLI 记录的写入**;写前发现被外部动过=**疑似补手续**(含判据缺陷留下的历史误报,用 `rebase --force` 结案)
13. **partition-case** — 同槽分区文件名**大小写折叠后不得重名**(NTFS 大小写不敏感 ⇒ 仅大小写不同的两个 domain 会合并成一个分区文件、计数与现实脱节)
14. **index** — `index` 的各槽计数与 `next` 与落盘一致(漂移即红)
15. **fresh-queue** — **交接新鲜度**:仍 open 的 `t` 型条目必须在 `exit`/`summary` 里出现(抓「新条目进队列、exit 沉默」;只取 `t` 型——p/d 进 exit 属背景叙述,逐条要求提及会淹没真信号)
16. **fresh-stale** — **陈旧陈述**:`exit` 里提及**已闭**条目的那一行必须带显式关闭标记(抓「把已闭说成候立」;**只查 exit 不查 summary**——summary 是流水式摘要、逐条列已闭 id 是它的本职,强求标记产出纯噪声)

<!-- check-dims:end -->
- **`add` / `close` = 唯一写入口**;**日期脚本盖、id 脚本分配**(LLM 无从编造)。**`add` 参数面按型分列**:`handoff add action|pitfall|command|decision --…`,每型只收本型字段——旧 `add --slot <槽>` 的**并集面**会静默丢弃本型不消费的键,现**硬报错并印新形**;`edit` 亦按条目型校验可改面(不符即报错)。
- **`next` 自动补位**:`close`(含 `unconfirmed resolve` 的内部关闭)关掉的**正是当前 `next`** → 按策略补下一条(`--no-refill` 关;`handoff next --auto` 人工触发同一策略;`next <id>` 随时覆盖补位结果)。策略**确定性可复算**:候选池=live `actions`(**仅 `status=open`/缺省入池**;blocked=等待中、其余值非法均排除);排序键 `(有效档, created↑, id↑)`;基础档 `[高]`0 / `[中]`·无前缀1 / `[低]`2(前缀亦认全角 `【高】` 与 `high`/`med`/`mid`/`medium`/`low`,大小写不敏感);**时间维**=超期每满 30 天升一档(下限 0,故陈年 `[低]` 会越过新鲜 `[高]`)。补位/留空均**打印依据**(P3 不静默)。空池或全 blocked:`close` 留空并说明,`next --auto` **拒绝且不写指针**。
- **S7 `commands` 先薄**:只收 `AGENTS` / `README` 里**没有**的非显然命令 + 环境例外(可推导的**不重复记**)→ **常空正常**。每次门禁落一条 `log` 快照(`handoff log --stats`),供日后据数据决定是否保留该槽。
- **`set <status|summary|exit>`**:单文件槽写入口(`add` 只覆盖条目槽 + `decisions`);**单文件槽唯一写路径**。**整槽覆写、无追加语义**——想改一行也必须先读回全量再写。两道守卫:空内容拒写(`--allow-empty` 才放行);旧内容非空且新内容 <60% 亦拒写(`--force` 才放行,防「把整槽覆写当局部编辑」;**已去掉原「>200 字符」下限**——那是条盲区:123B/315B 的小槽只喂一行会**静默截断**(p000029),而小仓/新项目恰最容易被随手写一行。**写入前不确定就先 `--dry-run`**:逐条列出**将丢失的行**、不落盘。拒写时错误信息直接给出**可复制的恢复命令**(`prev/<slot>` → 槽位),**易 undo 优于反复确认**——确认会被自动化掉清空槽位)。**快照在真正写入之前**(4.8.4 改):守卫拒绝与 `--dry-run` **都不在盘上留痕迹**——被拒时一个字节都没写、槽还是原内容,**不需要回退**。因此**被拒时的报错里不给「回退到 prev/」的建议**:`prev/` 存的是**上一次成功写入**的版本,照做会把当前内容覆盖成上一个版本且全程无警告(独立审计实测)。要回退请用 `git` 或自己的备份。
- **纠错入口**:`edit <id>`(改安全字段;`id`/`created`/`closed` 不可改)与 `rm <id>`(→ `.handoff/trash/` + 记 `void` 防 id 复用 + 引用守卫)——错误**不经手搓**,免 `export→改→import --force` 全量重写。
- **`status` 值域按型闭集**(`add`/`edit` 同一守卫,t000138):`action`/`command`=`open`|`blocked`;`pitfall`=`open`|`fixed`|`blocked`——**`fixed` 不是销账**,它表示「缺陷已修、**留案以免重犯**」:条目**仍在 live 区**、仍计入各槽计数、仍被 `check`/`view` 看到,只是不进 `closed/`。要销账走 `close --outcome`(另一回事)。它**基本是自述**:别把「多数坑都 `open`」读成「多数坑都没修」;缺省(不传或清空)=`open`。**`closed` 不是可写值**——关闭唯一入口=`close --outcome`(edit/add 传 `closed` 或表外值即硬报错);`check` 对 live 行同闭集硬判;补位池**仅 open 入池**(blocked=等待中、其余=非法值,均不补位)。`selftest` = 闭集守卫 + `guard_partition` 分区名的双向夹具自证(临时 store 内真 CLI;**断言数会随夹具增长,别在正文写死**——要数就跑 `selftest` 数 `ok` 行)。
- **`confirm` = 机检 read-back**:脚本出题、脚本判卷;**不 `PASS` = 交接未完成**。题面**每次调用重新随机抽**——「先出题、后作答」必须两次带**同一 `--seed`**,否则题已换而答案错位。抽查题按**视图行文本**判分(`[id] topic summary`,非 open 条目含末尾 `  (status)`),照 `handoff view` 行原样抄(去掉行首 `- `)即可,勿只抄 summary。
- **`view`**:全局视图(认识整体),默认 stdout;`--save` 存盘(默认 `<项目根>/handoff-view.md`),属**用户所有、非权威、不 `check`**。
- 无 `python3` → **询问用户安装**,或降级最小 `sh`。

## 不变量(任一相位必须守)

- **P1 不删**:只追加;`closed` 永不重写;移除仅在关闭序内原子完成。
- **P2 可重建**:`index` 由脚本重建;**不生成、不落盘第二副本**。
- **P3 不静默**:每次运行输出「**读到 / 写入 / 未能确认**」;`confirm` 为**双向确认**。

## 硬约束

- 不假设 git / 语言 / 工具 / 布局;需用则**先探测**。
- **未决语义唯一落点 `.handoff/`**(白名单硬契约);其余处的待办文本是**候选**,非未决项。
- **维度闭合**:9 槽固定,槽外→`unconfirmed`。
- **源登记表闭合**:**只有 `scope` 登记的源被读**;未登记 → **非未决源**(定义使然)。`check` 校每条登记**可解析**(路径存在 / glob 有命中);`scope scan` **仅提议**(机械候选),漏扫不影响契约成立。
- **迁移先于交接**:有旧模型时,**告知用户并请求迁移**(`init`/`migrate` **仅显式调用**);同意后先走 `migrate`(它建存储),勿先建空存储、勿在旧模型上直接交接。
- **项目约定(标准块,自动装)**:`init` 会把**标准交接块**写进**本项目**的 `AGENTS.md`(`agents-block --install .`,节外内容不动)。三种情形都收敛到**单源**:已有标记区 → 覆写块内;已有 `## 交接`(或 `### 交接`)整节但无标记区 → **整理覆盖该节**(手写副本被标准块接管,节外不动;被替换的非空行数会打印);都没有 → 新建该节。**标记区残缺**(只有 begin 或只有 end,说明被人手改坏过)→ **拒绝并报错**,不自动修:静默整节替换会把那段人写内容一起吃掉。识别交接标题只认标题行本身,不会误吃「## 交接约定」这类普通章节。唯一源=`references/agents-handoff-block.md`。**不手工编辑块内条目**——`--check` 字节比对,漂移 rc=1。
  - **写入是使用方自己的动作**:库维护者不跨仓代劳——每个项目在**自己**启用交接时安装一次。
  - **防漂移**:`agents-block --check .` 字节比对,块内被手改即 rc=1。要升级条目,改标准源再让各仓重装,**不要在仓里改条目**(复制即漂移,与 catalog 条款同病)。
  - **档位**:`--profile basic`(通用纪律)/`--profile freshness`(该仓装了交接新鲜度判据时才用,抄了却没装判据=给 agent 一条无法执行的指令)。`--check` 会按块内标记**自动识别**档位。
- 崩溃最坏产生重复(按 id 去重,closed 胜)——**绝不丢**。

## 反模式

- 手改 `.handoff/` 条目(绕过 CLI → 门禁兜不住)。
- 手填日期 / 自选 id。
- 把未决写在 `.handoff/` 之外(→ 走 `unconfirmed`,**不假装覆盖**)。
- 手写第二份「视图 / 摘要」当交接件(→ 双源必漂)。

## 最短真实样例(初始化 → 交接一轮)

前置:项目根目录、解释器可用、已获用户显式授权建存储。
**解释器**:下文示例统一写 `python3`;**Windows 上改用 `python`**(那里通常没有 `python3`)。
`$H` 指本技能目录(通常在 `~/.agents/skills/project-handoff`,各仓相对路径不同,故用绝对/家目录路径而非仓内相对路径)。

```bash
python3 "$H/scripts/handoff.py" init             # 建 .handoff/ + 自动装标准交接块;自动生成 index
python3 "$H/scripts/handoff.py" add action --summary "[高] 修复登录重定向循环" --domain auth --src src/auth/login.ts:42
python3 "$H/scripts/handoff.py" add pitfall --summary "测试库必须先 seed,否则 check 全红" --domain test
printf '已完成认证重构;测试待补\n' | python3 "$H/scripts/handoff.py" set status --file -
python3 "$H/scripts/handoff.py" check               # 门禁:非零退出 = 不得收尾
python3 "$H/scripts/handoff.py" confirm --seed 1    # 出题(两次须同 --seed)
python3 "$H/scripts/handoff.py" confirm --seed 1 --answers "<按 view 行原样逐行>"   # 判卷,PASS 才算交接完成
python3 "$H/scripts/handoff.py" agents-block --check .   # 交接块 vs 唯一源,漂移 rc=1
```

产出(摘录):`handoff check` 打印 `handoff check: OK` 并落一条 `log` 快照;`confirm` 判卷通过打 `PASS`——**不 PASS = 交接未完成**。接收方恢复上下文只读 `references/resume.md` 相位(`view` + `next` 即可接上)。

## 失败出口

- **`check` 非零退出**:按输出行修——列出的即不满足的判据(槽缺失 / index 计数不一致 / id 冲突 / 日期违规 / `status` 值域 / `next` 无效或非 `open` / **文本槽死指针**)。修法只经 CLI:缺条目 `add`,多出条目 `rm`/`close`,**不手改文件**。**文本槽死指针**(`status`/`summary`/`exit` 提到一个既不在册也不在 `trash/`·`void` 的 `t`/`p`/`q`/`u` 型 id)修法:在文本里改掉那个引用,或把条目补回库里——**别去判据里加豁免**。若是**已作废**的 id 却被报,先确认它是否真在 `trash/`/`void`(在那儿就不会被报);`c` 型不参与本判据是刻意的收窄(不是漏了)。**`c`/`d` 两型不在本判据范围内**(`d` 与 7 字符全 hex 的 commit 短 hash 形态不可区分),故这两型**不会**被这里报出——由 `view --id` 负责:查得到给记录,**已作废的明说「已作废」**(`rm` 落 `trash/` + 记 `void`,与本闸「已作废 ≠ 不存在」同口径),真正查不到才 rc=2。「改号」另由本闸的 `decisions/<file>` frontmatter-id-等于-文件名判据兜。修完重跑,`OK` 为唯一通过判据。
- **`add`/`edit`/`close` 打印了「⚠ 含 shell 特殊字符」**:不是错误,是**提醒你核对**——若你用了双引号,这段内容可能已被 shell 展开(反引号=命令替换、`$`=变量)。**单引号安全**,该警告对单引号写法是噪声,可忽略;要彻底避开请用 `--from-file`。**真正要看的是紧随其后的「↩ 回读」块**——那是被吃掉的版本唯一会露出来的地方。
- **`check` 报「未经 CLI 记录的写入」/「疑似补手续」**:`writes.jsonl` 记的是**每次 CLI 写**后各文件的内容哈希与纳秒 mtime。手改文件不会留下记录 ⇒ 哈希对不上即报。修法:**别去改判据**,把那处手改经 CLI 重做一遍(`add`/`edit`/`set`),或直接从 `prev/` 快照恢复。若报的是「写之前发现被改动过」而内容哈希仍一致,那是**同字节覆写**的补手续痕迹——CLI 看不见那次手改的内容,但文件被动过的事实留下了。
- **`check` 报出一堆「写入**之前**发现被改动过」而你确信没手改**:多半是**判据版本自身的缺陷**留下的历史误报(修判据不会消掉已写入的事件)。先**手工核对**那几个文件确实没被手改,再 `handoff rebase --force` 把基线对齐现实——它会逐条打印「已结案」清单,**被结案的结论不可追回**,所以核对在前。
- **`set` 被拒(空内容 / 骤降守卫)**:输出已说明是哪道守卫。确属要写空 → `--allow-empty`;确属大幅精简 → `--force`。**被拒时一个字节都没写、槽仍是原内容,不需要回退**——所以拒写信息里**不会**给「回退到 `prev/`」的建议(`prev/` 是**上一次成功写入**的版本,照做反而会把当前内容覆盖掉)。要回退用 `git` 或你自己的备份。**不确定就先 `set <slot> --file - --dry-run`**:逐条列出将丢失的行、不落盘(骤降判据在**极小内容**上不可靠——25 字符的槽删到 10 字符分不清「有意删一行」与「只喂了表头」,这类残余误用由 `--dry-run` 兜,而不是继续调阈值、再调就开始误伤正常删减)。
- **`add` 报「参数面不符」**:你用的是旧 `add --slot <槽>` 并集形——按报错打印的新形重输(`add action|pitfall|command|decision --…`,每型只收本型字段)。
- **`add`/`edit` 报「status 非法」**:值域按型闭集(`action`/`command`=`open|blocked`,`pitfall`=`open|fixed|blocked`);要关闭条目走 `close --outcome`,不要把 `closed` 当状态写。注意 `pitfall` 的 `fixed` **只是自述、不是销账**——它**基本是自述**:别把「多数坑都 `open`」读成「多数坑都没修」。只有 next 指针判据读它(要求 next 指向 `open` 行)——那是守 next 有效性、不是销坑,**别为它加闸**;值域校验只验「在不在闭集里」,各槽计数与补位池都不看它。
- **`confirm` 不 PASS**:答案按 `handoff view` 行文本原样抄(去行首 `- `,含末尾 `(status)`);若两次调用 `--seed` 不同,题已换,重取题再答。
- **无 `python3`**:询问用户安装;不可安装时降级最小 `sh` 约定(写入口纪律照守,门禁缺失须在交接里明示)。

## FAQ

**常见问题从哪查?** 本文「硬约束 / 反模式 / 失败出口」三节覆盖高频误用;相位细节读 `references/` 对应文件(见「何时读哪个相位」表)。

**为什么不能直接编辑 `.handoff/` 里的文件?** 单一写入口是门禁的前提:日期、id、index 全由脚本管理,手改会绕过校验且 `check` 兜不住(见反模式)。

**commands 槽老是空的,坏了吗?** 不是——S7 只收 AGENTS/README 里没有的非显然命令,常空正常(见「S7 `commands` 先薄」)。

**MFA/多工具协作也一样吗?** 是——不假设 git/语言/工具;跨工具交接同一存储,`confirm` 的双向 read-back 就是给跨工具场景设计的。

Files in this skill

  • SKILL.md51 KB
  • references/agents-handoff-block.md4.4 KB
  • references/handoff.md4.4 KB
  • references/init.md3.5 KB
  • references/migrate.md8.2 KB
  • references/resume.md3 KB
  • scripts/migrate.sh2.2 KB

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.