harveyz-skill 0.30.0 → 0.32.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 (191) hide show
  1. package/CHANGELOG.md +33 -0
  2. package/package.json +4 -3
  3. package/skills/coding/handoff/SKILL.md +116 -16
  4. package/skills/coding/handoff/assets/handoff-template.md +20 -7
  5. package/skills/coding/handoff/evals/evals.json +134 -38
  6. package/skills/coding/handoff/evals/fixtures/accept/{handoff-slugify.md → 2026-08-02-slugify-handoff.md} +7 -3
  7. package/skills/coding/handoff/evals/fixtures/accept-worktree/setup.sh +119 -0
  8. package/skills/coding/handoff/scripts/validate-handoff.sh +176 -0
  9. package/skills/coding/handoff/tests/validate-handoff.bats +287 -0
  10. package/skills/feed/{capture-creator → capture-opinion}/SKILL.md +9 -9
  11. package/skills/feed/{manage-roster → manage-creators}/SKILL.md +5 -5
  12. package/skills/feed/manage-creators/scripts/__pycache__/roster_locate.cpython-314.pyc +0 -0
  13. package/skills/feed/{manage-roster → manage-creators}/tests/__pycache__/test_roster_locate.cpython-314-pytest-9.1.1.pyc +0 -0
  14. package/skills/feed/sync-website/SKILL.md +192 -0
  15. package/skills/feed/sync-website/platforms/SKILL.claude.md +19 -0
  16. package/skills/feed/sync-website/platforms/SKILL.codex.md +22 -0
  17. package/skills/feed/sync-website/platforms/SKILL.hermes.md +22 -0
  18. package/skills/feed/sync-website/platforms/SKILL.pi.md +18 -0
  19. package/skills/feed/sync-website/scripts/__pycache__/archive_articles.cpython-314.pyc +0 -0
  20. package/skills/feed/sync-website/scripts/__pycache__/articles_client.cpython-314.pyc +0 -0
  21. package/skills/feed/sync-website/scripts/__pycache__/browser_fetch_cli.cpython-314.pyc +0 -0
  22. package/skills/feed/sync-website/scripts/__pycache__/browser_fetch_locate.cpython-314.pyc +0 -0
  23. package/skills/feed/sync-website/scripts/__pycache__/calibration_gate.cpython-314.pyc +0 -0
  24. package/skills/feed/sync-website/scripts/__pycache__/config.cpython-314.pyc +0 -0
  25. package/skills/feed/sync-website/scripts/__pycache__/cursor.cpython-314.pyc +0 -0
  26. package/skills/feed/sync-website/scripts/__pycache__/digest.cpython-314.pyc +0 -0
  27. package/skills/feed/sync-website/scripts/__pycache__/fetch_new_articles.cpython-314.pyc +0 -0
  28. package/skills/feed/sync-website/scripts/__pycache__/roster_client.cpython-314.pyc +0 -0
  29. package/skills/feed/{manage-roster → sync-website}/scripts/__pycache__/roster_locate.cpython-314.pyc +0 -0
  30. package/skills/feed/sync-website/scripts/__pycache__/store_config.cpython-314.pyc +0 -0
  31. package/skills/feed/sync-website/scripts/archive_articles.py +53 -0
  32. package/skills/feed/sync-website/scripts/articles_client.py +90 -0
  33. package/skills/feed/sync-website/scripts/browser_fetch_cli.py +23 -0
  34. package/skills/feed/sync-website/scripts/browser_fetch_locate.py +50 -0
  35. package/skills/feed/sync-website/scripts/calibration_gate.py +50 -0
  36. package/skills/feed/sync-website/scripts/config.py +13 -0
  37. package/skills/feed/sync-website/scripts/cursor.py +29 -0
  38. package/skills/feed/sync-website/scripts/digest.py +90 -0
  39. package/skills/feed/sync-website/scripts/fetch_new_articles.py +124 -0
  40. package/skills/feed/sync-website/scripts/roster_client.py +44 -0
  41. package/skills/feed/sync-website/scripts/roster_locate.py +46 -0
  42. package/skills/feed/sync-website/scripts/store_config.py +61 -0
  43. package/skills/feed/sync-website/tests/__pycache__/conftest.cpython-314-pytest-9.1.1.pyc +0 -0
  44. package/skills/feed/sync-website/tests/__pycache__/test_archive_articles.cpython-314-pytest-9.1.1.pyc +0 -0
  45. package/skills/feed/sync-website/tests/__pycache__/test_articles_client.cpython-314-pytest-9.1.1.pyc +0 -0
  46. package/skills/feed/sync-website/tests/__pycache__/test_browser_fetch_locate.cpython-314-pytest-9.1.1.pyc +0 -0
  47. package/skills/feed/sync-website/tests/__pycache__/test_calibration_gate.cpython-314-pytest-9.1.1.pyc +0 -0
  48. package/skills/feed/sync-website/tests/__pycache__/test_cursor.cpython-314-pytest-9.1.1.pyc +0 -0
  49. package/skills/feed/sync-website/tests/__pycache__/test_digest.cpython-314-pytest-9.1.1.pyc +0 -0
  50. package/skills/feed/sync-website/tests/__pycache__/test_fetch_new_articles.cpython-314-pytest-9.1.1.pyc +0 -0
  51. package/skills/feed/sync-website/tests/__pycache__/test_roster_client.cpython-314-pytest-9.1.1.pyc +0 -0
  52. package/skills/feed/sync-website/tests/__pycache__/test_store_config.cpython-314-pytest-9.1.1.pyc +0 -0
  53. package/skills/feed/sync-website/tests/conftest.py +27 -0
  54. package/skills/feed/sync-website/tests/test_archive_articles.py +91 -0
  55. package/skills/feed/sync-website/tests/test_articles_client.py +93 -0
  56. package/skills/feed/sync-website/tests/test_browser_fetch_locate.py +69 -0
  57. package/skills/feed/sync-website/tests/test_calibration_gate.py +77 -0
  58. package/skills/feed/sync-website/tests/test_cursor.py +47 -0
  59. package/skills/feed/sync-website/tests/test_digest.py +158 -0
  60. package/skills/feed/sync-website/tests/test_fetch_new_articles.py +185 -0
  61. package/skills/feed/sync-website/tests/test_roster_client.py +83 -0
  62. package/skills/feed/sync-website/tests/test_store_config.py +98 -0
  63. package/skills/feed/sync-xtimeline/SKILL.md +20 -18
  64. package/skills/feed/sync-xtimeline/scripts/__pycache__/archive_tweets.cpython-314.pyc +0 -0
  65. package/skills/feed/sync-xtimeline/scripts/__pycache__/config.cpython-314.pyc +0 -0
  66. package/skills/feed/sync-xtimeline/scripts/__pycache__/cursor.cpython-314.pyc +0 -0
  67. package/skills/feed/sync-xtimeline/scripts/__pycache__/fetch_new_tweets.cpython-314.pyc +0 -0
  68. package/skills/feed/sync-xtimeline/scripts/__pycache__/render_digest.cpython-314.pyc +0 -0
  69. package/skills/feed/sync-xtimeline/scripts/__pycache__/roster_client.cpython-314.pyc +0 -0
  70. package/skills/feed/sync-xtimeline/scripts/__pycache__/store_config.cpython-314.pyc +0 -0
  71. package/skills/feed/sync-xtimeline/scripts/archive_tweets.py +18 -1
  72. package/skills/feed/sync-xtimeline/scripts/config.py +5 -11
  73. package/skills/feed/sync-xtimeline/scripts/fetch_new_tweets.py +15 -40
  74. package/skills/feed/sync-xtimeline/scripts/render_digest.py +1 -8
  75. package/skills/feed/sync-xtimeline/scripts/roster_client.py +1 -6
  76. package/skills/feed/sync-xtimeline/scripts/store_config.py +61 -0
  77. package/skills/feed/sync-xtimeline/tests/__pycache__/conftest.cpython-314-pytest-9.1.1.pyc +0 -0
  78. package/skills/feed/sync-xtimeline/tests/__pycache__/test_archive_tweets.cpython-314-pytest-9.1.1.pyc +0 -0
  79. package/skills/feed/sync-xtimeline/tests/__pycache__/test_cursor.cpython-314-pytest-9.1.1.pyc +0 -0
  80. package/skills/feed/sync-xtimeline/tests/__pycache__/test_fetch_new_tweets.cpython-314-pytest-9.1.1.pyc +0 -0
  81. package/skills/feed/sync-xtimeline/tests/__pycache__/test_render_digest.cpython-314-pytest-9.1.1.pyc +0 -0
  82. package/skills/feed/sync-xtimeline/tests/__pycache__/test_store_config.cpython-314-pytest-9.1.1.pyc +0 -0
  83. package/skills/feed/sync-xtimeline/tests/conftest.py +12 -25
  84. package/skills/feed/sync-xtimeline/tests/test_archive_tweets.py +31 -8
  85. package/skills/feed/sync-xtimeline/tests/test_fetch_new_tweets.py +43 -35
  86. package/skills/feed/sync-xtimeline/tests/test_render_digest.py +14 -41
  87. package/skills/feed/sync-xtimeline/tests/test_store_config.py +98 -0
  88. package/skills/feed/sync-ytchannel/SKILL.md +21 -19
  89. package/skills/feed/sync-ytchannel/scripts/__pycache__/archive_videos.cpython-314.pyc +0 -0
  90. package/skills/feed/sync-ytchannel/scripts/__pycache__/config.cpython-314.pyc +0 -0
  91. package/skills/feed/sync-ytchannel/scripts/__pycache__/digest.cpython-314.pyc +0 -0
  92. package/skills/feed/sync-ytchannel/scripts/__pycache__/fetch_new_videos.cpython-314.pyc +0 -0
  93. package/skills/feed/sync-ytchannel/scripts/__pycache__/mcp_channel_client.cpython-314.pyc +0 -0
  94. package/skills/feed/sync-ytchannel/scripts/__pycache__/roster_client.cpython-314.pyc +0 -0
  95. package/skills/feed/sync-ytchannel/scripts/__pycache__/store_config.cpython-314.pyc +0 -0
  96. package/skills/feed/sync-ytchannel/scripts/archive_videos.py +17 -1
  97. package/skills/feed/sync-ytchannel/scripts/config.py +6 -11
  98. package/skills/feed/sync-ytchannel/scripts/digest.py +1 -8
  99. package/skills/feed/sync-ytchannel/scripts/fetch_new_videos.py +12 -40
  100. package/skills/feed/sync-ytchannel/scripts/roster_client.py +1 -6
  101. package/skills/feed/sync-ytchannel/scripts/store_config.py +61 -0
  102. package/skills/feed/sync-ytchannel/tests/__pycache__/conftest.cpython-314-pytest-9.1.1.pyc +0 -0
  103. package/skills/feed/sync-ytchannel/tests/__pycache__/test_archive_videos.cpython-314-pytest-9.1.1.pyc +0 -0
  104. package/skills/feed/sync-ytchannel/tests/__pycache__/test_digest.cpython-314-pytest-9.1.1.pyc +0 -0
  105. package/skills/feed/sync-ytchannel/tests/__pycache__/test_fetch_new_videos.cpython-314-pytest-9.1.1.pyc +0 -0
  106. package/skills/feed/sync-ytchannel/tests/__pycache__/test_store_config.cpython-314-pytest-9.1.1.pyc +0 -0
  107. package/skills/feed/sync-ytchannel/tests/conftest.py +18 -7
  108. package/skills/feed/sync-ytchannel/tests/test_archive_videos.py +38 -27
  109. package/skills/feed/sync-ytchannel/tests/test_digest.py +11 -37
  110. package/skills/feed/sync-ytchannel/tests/test_fetch_new_videos.py +36 -43
  111. package/skills/feed/sync-ytchannel/tests/test_store_config.py +98 -0
  112. package/skills/research/clip-url/SKILL.md +18 -20
  113. package/skills/research/clip-url/platforms/SKILL.claude.md +4 -4
  114. package/skills/research/clip-url/platforms/SKILL.codex.md +4 -2
  115. package/skills/research/clip-url/platforms/SKILL.hermes.md +4 -2
  116. package/skills/research/clip-url/platforms/SKILL.pi.md +4 -4
  117. package/skills/research/clip-url/scripts/__pycache__/store_config.cpython-314.pyc +0 -0
  118. package/skills/research/clip-url/scripts/__pycache__/vault_config.cpython-314.pyc +0 -0
  119. package/skills/research/clip-url/scripts/store_config.py +61 -0
  120. package/skills/research/clip-url/scripts/vault_config.py +7 -41
  121. package/skills/research/clip-url/tests/__pycache__/conftest.cpython-314-pytest-9.1.1.pyc +0 -0
  122. package/skills/research/clip-url/tests/__pycache__/test_dedup_check.cpython-314-pytest-9.1.1.pyc +0 -0
  123. package/skills/research/clip-url/tests/__pycache__/test_mcp_fetch_client.cpython-314-pytest-9.1.1.pyc +0 -0
  124. package/skills/research/clip-url/tests/__pycache__/test_store_config.cpython-314-pytest-9.1.1.pyc +0 -0
  125. package/skills/research/clip-url/tests/__pycache__/test_vault_config.cpython-314-pytest-9.1.1.pyc +0 -0
  126. package/skills/research/clip-url/tests/__pycache__/test_write_meta_and_separate.cpython-314-pytest-9.1.1.pyc +0 -0
  127. package/skills/research/clip-url/tests/conftest.py +8 -8
  128. package/skills/research/clip-url/tests/test_dedup_check.py +3 -3
  129. package/skills/research/clip-url/tests/test_mcp_fetch_client.py +7 -7
  130. package/skills/research/clip-url/tests/test_store_config.py +94 -0
  131. package/skills/research/clip-url/tests/test_vault_config.py +14 -39
  132. package/skills/research/clip-url/tests/test_write_meta_and_separate.py +3 -3
  133. package/skills/research/learn-video/SKILL.md +51 -5
  134. package/skills/research/learn-video/scripts/__pycache__/archive.cpython-314.pyc +0 -0
  135. package/skills/research/learn-video/scripts/__pycache__/store_config.cpython-314.pyc +0 -0
  136. package/skills/research/learn-video/scripts/archive.py +92 -0
  137. package/skills/research/learn-video/scripts/store_config.py +61 -0
  138. package/skills/research/learn-video/tests/__pycache__/conftest.cpython-314-pytest-9.1.1.pyc +0 -0
  139. package/skills/research/learn-video/tests/__pycache__/test_archive.cpython-314-pytest-9.1.1.pyc +0 -0
  140. package/skills/research/learn-video/tests/__pycache__/test_store_config.cpython-314-pytest-9.1.1.pyc +0 -0
  141. package/skills/research/learn-video/tests/conftest.py +20 -0
  142. package/skills/research/learn-video/tests/test_archive.py +165 -0
  143. package/skills/research/learn-video/tests/test_store_config.py +98 -0
  144. package/skills-index.json +32 -17
  145. package/tools/browser-fetch/browser_fetch/__pycache__/cli.cpython-314.pyc +0 -0
  146. package/tools/browser-fetch/browser_fetch/__pycache__/core.cpython-314.pyc +0 -0
  147. package/tools/browser-fetch/browser_fetch/__pycache__/extractors.cpython-314.pyc +0 -0
  148. package/tools/browser-fetch/browser_fetch/__pycache__/normalize.cpython-314.pyc +0 -0
  149. package/tools/browser-fetch/browser_fetch/__pycache__/site_rules.cpython-314.pyc +0 -0
  150. package/tools/browser-fetch/browser_fetch/cli.py +43 -0
  151. package/tools/browser-fetch/browser_fetch/core.py +155 -1
  152. package/tools/browser-fetch/browser_fetch/extractors.py +38 -0
  153. package/tools/browser-fetch/browser_fetch/normalize.py +42 -0
  154. package/tools/browser-fetch/browser_fetch/site_rules.py +186 -0
  155. package/tools/browser-fetch/pyproject.toml +1 -1
  156. package/tools/browser-fetch/tests/__pycache__/conftest.cpython-314-pytest-9.1.1.pyc +0 -0
  157. package/tools/browser-fetch/tests/__pycache__/test_cli_articles.cpython-314-pytest-9.1.1.pyc +0 -0
  158. package/tools/browser-fetch/tests/__pycache__/test_cli_articles_rule.cpython-314-pytest-9.1.1.pyc +0 -0
  159. package/tools/browser-fetch/tests/__pycache__/test_cli_articles_transform.cpython-314-pytest-9.1.1.pyc +0 -0
  160. package/tools/browser-fetch/tests/__pycache__/test_extractors_articles.cpython-314-pytest-9.1.1.pyc +0 -0
  161. package/tools/browser-fetch/tests/__pycache__/test_normalize.cpython-314-pytest-9.1.1.pyc +0 -0
  162. package/tools/browser-fetch/tests/__pycache__/test_site_rules.cpython-314-pytest-9.1.1.pyc +0 -0
  163. package/tools/browser-fetch/tests/__pycache__/test_site_rules_dirs.cpython-314-pytest-9.1.1.pyc +0 -0
  164. package/tools/browser-fetch/tests/conftest.py +30 -0
  165. package/tools/browser-fetch/tests/test_cli_articles.py +90 -0
  166. package/tools/browser-fetch/tests/test_cli_articles_rule.py +63 -0
  167. package/tools/browser-fetch/tests/test_cli_articles_transform.py +169 -0
  168. package/tools/browser-fetch/tests/test_extractors_articles.py +29 -0
  169. package/tools/browser-fetch/tests/test_normalize.py +55 -0
  170. package/tools/browser-fetch/tests/test_site_rules.py +86 -0
  171. package/tools/browser-fetch/tests/test_site_rules_dirs.py +235 -0
  172. package/tools/browser-fetch/tool.json +1 -1
  173. package/tools/roster/roster/__init__.py +1 -1
  174. package/tools/roster/roster/__main__.py +3 -3
  175. package/tools/roster/roster/__pycache__/__init__.cpython-314.pyc +0 -0
  176. package/tools/roster/roster/__pycache__/__main__.cpython-314.pyc +0 -0
  177. package/tools/roster/roster/__pycache__/registry.cpython-314.pyc +0 -0
  178. package/tools/roster/roster/__pycache__/urls.cpython-314.pyc +0 -0
  179. package/tools/roster/roster/registry.py +1 -1
  180. package/tools/roster/roster/urls.py +30 -0
  181. package/tools/roster/tests/__pycache__/test_cli.cpython-314-pytest-9.1.1.pyc +0 -0
  182. package/tools/roster/tests/__pycache__/test_urls.cpython-314-pytest-9.1.1.pyc +0 -0
  183. package/tools/roster/tests/test_cli.py +1 -1
  184. package/tools/roster/tests/test_urls.py +11 -2
  185. /package/skills/feed/{capture-creator → capture-opinion}/scripts/roster_locate.py +0 -0
  186. /package/skills/feed/{manage-roster → manage-creators}/scripts/__pycache__/mcp_call_locate.cpython-314.pyc +0 -0
  187. /package/skills/feed/{manage-roster → manage-creators}/scripts/__pycache__/roster_mcp_locate.cpython-314.pyc +0 -0
  188. /package/skills/feed/{manage-roster → manage-creators}/scripts/roster_locate.py +0 -0
  189. /package/skills/feed/{manage-roster → manage-creators}/tests/__pycache__/test_roster_mcp_client.cpython-314-pytest-9.1.1.pyc +0 -0
  190. /package/skills/feed/{manage-roster → manage-creators}/tests/__pycache__/test_roster_mcp_locate.cpython-314-pytest-9.1.1.pyc +0 -0
  191. /package/skills/feed/{manage-roster → manage-creators}/tests/test_roster_locate.py +0 -0
package/CHANGELOG.md CHANGED
@@ -7,6 +7,39 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.32.0] - 2026-09-07
11
+
12
+ ### Added
13
+ - `sync-website`:新增追更网站文章列表页的第三种渠道类型,与 sync-xtimeline / sync-ytchannel 并列。browser-fetch 新增 per-domain selector 规则存储与 calibrate/run 流水线,roster 支持网站 URL 兜底解析。13 任务计划通过 subagent-driven-development 执行,逐任务 review + 全分支收尾 review(4 处修复:roster URL 守卫缺口、digest 里看不见的自愈、published_at 文档订正、路径穿越与 prompt-injection 加固),并对 simonwillison.net 做了真实 E2E 验证
14
+ - `browser-fetch`:site_rules 新增二档 selector+transform 抽取(`about:blank` 隔离上下文求值),归一化管线三档共用;transform 报错改为触发自愈而非永久失败,超出自愈上限才报错
15
+ - `scripts/rebuild-orphan-articles.py`:一次性重建脚本,把统一存储根迁移后遗留在 `_orphans/` 里、但 frontmatter 带 `source_url`/`origin_title`/`fetch_date` 的孤儿文章重新收进索引;只在无歧义(`hash8` 未被占用且只有一个来源文件对应)时重建,多源冲突留给人工判断
16
+
17
+ ### Changed
18
+ - `handoff`(1.3.0 → 1.8.0):一次性完成多轮迭代——工作区改由 author 建并留到验收通过(接手方与验收方都不再新建 worktree/分支)、抬头字段搬进 frontmatter 并加 `validate-handoff.sh` 机器校验、author 登记交接源节点与画布 `hands-off-to` 关系、交付时新增可粘贴的接手/验收指令(只放寻址信息不复述文档内容,避免摘要和文档变成两个真相源)。工作区约定同步写进 `CLAUDE.md` 与 `.hskill/handoff/config.md`
19
+ - `feed/manage-creators` / `sync-xtimeline` / `sync-ytchannel`:patch 版本刷新(`skill-publish` F8 contentHash 校验触发的措辞级 drift 修正,无行为变化)
20
+
21
+ ### Fixed
22
+ - `scripts/migrate-store.sh`(v0.31.0 引入的统一存储根一次性迁移脚本):在真实数据上跑 dry-run 后暴露并修复 7 处问题——旧版 `sync-xtimeline` 产物路径未读取 `config.json` 的 `DATA_DIR`、`--verify` 对旧布局的校验有两处假绿(路径解析未跟进迁移、并入语义被误判成覆盖语义)、`--verify` 未区分"目标被追加变大"(正常)与"被截断变小"(真失败)、vault 顶层孤儿译文未被搬迁、视频回填从无条件覆盖改为按字段并入以保留 vdl 自身写入的 50 份 meta.json / 483 个字段、scholia 视频卡片缺失展示字段(url/uploader/upload_date/duration 等)、`_orphans/` 未移出 `articles/` 导致孤儿被当正式文章重复列出
23
+
24
+ ## [0.31.0] - 2026-09-02
25
+
26
+ ### Added
27
+ - 统一存储根(`knowledgeRoot`):`clip-url` / `learn-video` / `sync-xtimeline` / `sync-ytchannel` 四个 skill 的长期产物收进一个可配置根目录(默认 `~/Documents/knowledge`),按「类型 × 形态」分层,清单统一收进 `feeds/`。根由 `~/.hskill/config.json` 的 `knowledgeRoot` 持有,各 skill 各带一份 `store_config.py` 副本读取,不新增必装 tool
28
+ - `scripts/migrate-store.sh`:一次性迁移脚本,把旧数据搬进统一存储根。全程只复制不删除,`--verify` 逐文件核对存在性与大小并显式断言原始数据未被删除;是否清除原件留给用户最后手工判断,脚本不提供 `--clean`
29
+
30
+ ### Changed
31
+ - **破坏性变更** `learn-video`:vdl 的 `WORK_ROOT` 直接指向 `<知识根>/videos`,不再把产物复制进知识根(避免同一份数据两处物理副本、下游仍写老路径)
32
+ - **升级路径**:执行 `scripts/migrate-store.sh` 完成一次性迁移后,用 vdl 自带的 `vdl config set work-root` 把 WORK_ROOT 指到知识根下的 `videos/` 目录;SKILL.md 初始化步骤已加 WORK_ROOT 与 `knowledgeRoot` 的一致性核对(两值分居两个配置文件,无机制保证同步)
33
+ - `sync-xtimeline` / `sync-ytchannel`:运行流程新增 `knowledgeRoot` 前置检查(新增 step 2),未配置时在归档阶段前直接失败退出,而不是跑到归档阶段才崩溃——两者本就是为 `/loop`/`schedule` 无人值守场景设计的
34
+ - `manage-roster` 更名为 `manage-creators`:`manage` 动词准确,但 `roster` 作为名词跟"关注了哪些创作者"这个心智模型不够贴,且容易被理解成排班表
35
+ - **升级路径**:旧的 `/manage-roster` 触发短语失效,改用 `/manage-creators`;`hskill update` 会通过 `renames[]` 自动迁移已安装的旧目录名,数据文件(`registry.json` 等)不受影响
36
+ - `capture-creator` 更名为 `capture-opinion`:名词位放的一直是"记的是谁"(creator)而不是"记的是什么"(判断/看法),跟 `capture-insight`/`capture-todo` 系列"名词=被记录对象"的模式不一致,也容易在改名后跟 `manage-creators` 的"新增人"操作混淆,误读成"创建一个 creator"。新名词对齐 skill 自身的触发短语("I have a take on...")
37
+ - **升级路径**:旧的 `/capture-creator` 触发短语失效,改用 `/capture-opinion`;`hskill update` 会通过 `renames[]` 自动迁移已安装的旧目录名,画像文件(`profiles/*.md`)不受影响
38
+
39
+ ### Fixed
40
+ - `sync-xtimeline` / `sync-ytchannel` 共用的抓取流程:游标推进改为归档完成后才推进,删掉 `pending.json` 断点回放机制。此前抓取成功即推进游标,若中断于渲染/归档之前会导致该批内容永久丢失,`pending.json` 只是原样回放上次结果掩盖「本次其实没抓」,调用方分辨不出两者,定时任务因此可以每天照常"成功"而上游内容一直没同步
41
+ - `store_config.py` / `migrate-store.sh`:`knowledgeRoot` 里的 `~` 未展开,SKILL.md 建议的默认值 `~/Documents/knowledge` 会被静默解析成进程 CWD 下字面量 `~` 目录
42
+
10
43
  ## [0.30.0] - 2026-08-31
11
44
 
12
45
  ### Changed
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "harveyz-skill",
3
- "version": "0.30.0",
3
+ "version": "0.32.0",
4
4
  "description": "Skill manager for Claude Code, Cursor, Codex, OpenClaw, Hermes, OpenCode, and Pi",
5
5
  "type": "module",
6
6
  "bin": {
@@ -30,6 +30,7 @@
30
30
  "skills/research/clip-url/",
31
31
  "skills/feed/sync-xtimeline/",
32
32
  "skills/feed/sync-ytchannel/",
33
+ "skills/feed/sync-website/",
33
34
  "skills/creative/capture-todo/",
34
35
  "skills/creative/capture-insight/",
35
36
  "skills/coding/init-workflow/",
@@ -59,8 +60,8 @@
59
60
  "skills/meta/sync-hotfix/",
60
61
  "skills/meta/sync-agent/",
61
62
  "skills/coding/handoff/",
62
- "skills/feed/manage-roster/",
63
- "skills/feed/capture-creator/",
63
+ "skills/feed/manage-creators/",
64
+ "skills/feed/capture-opinion/",
64
65
  "tools/hub/",
65
66
  "tools/sync-agent/",
66
67
  "tools/browser-fetch/",
@@ -1,13 +1,13 @@
1
1
  ---
2
2
  name: handoff
3
3
  description: Use when handing a task across sessions — writing a self-contained handoff doc for a fresh session to pick up (author), sanity-checking an inbound handoff before starting (verify), or accepting completed work against the criteria agreed at handoff time (accept). Triggers on phrases like "write a handoff", "hand this off", "pick up this task", "sign off on this work". Generic skill — project-specific conventions are read from .hskill/handoff/config.md.
4
- version: "1.3.0"
4
+ version: "1.8.0"
5
5
  user_invocable: true
6
6
  ---
7
7
 
8
8
  # 跨 session 任务交接(handoff)
9
9
 
10
- 产出并驱动一份自包含交接文档:接手 session 只读这一份文件即可续做,完成后由原 session 按约定判据验收。文档直接整份喂给新 session,不做可粘贴 prompt。文档内容跟着这次交接的实际目的走——不是无论目的是什么都写一份详实清单,缺了会让接手方出问题的信息才写,其余不写。
10
+ 产出并驱动一份自包含交接文档:接手 session 只读这一份文件即可续做,完成后由原 session 按约定判据验收。交付时另给一段**可粘贴的接手指令**,它只负责寻址(工作区路径、分支、文档路径),**不复述文档内容**——一旦指令里塞进摘要,接手方就会读指令不读文档,摘要与文档立刻变成两个真相源。文档内容跟着这次交接的实际目的走——不是无论目的是什么都写一份详实清单,缺了会让接手方出问题的信息才写,其余不写。
11
11
 
12
12
  ## Phase 触发判定(先做这一步)
13
13
 
@@ -26,46 +26,146 @@ user_invocable: true
26
26
  - 无 → 用通用默认(`output_dir=docs/commute/`),以后不再问。
27
27
  2. **判断目的**:从当前对话判断这次交接是为了什么——不套预设分类,一句自然语言判断即可(例如"接手方续做同一个实现任务"或"把讨论结论作为背景传给接手方去开展新话题")。这句话会写进文档开头的"交接目的",且始终存在,不可省略。
28
28
  3. **汇集上下文**:以**当前对话**为真相源。涉及代码时读 `git status` / `git diff` 核对现状、排查受影响文件(若这两类内容按第 4 步判定为必要),并作为门禁的现实校验(纯规划交接可跳过);spec/plan 作为权威指针。**不把 memory 写进文档**——memory 可能陈旧、且接手方访问不到你的 memory 目录;若某条 memory 是承载性背景,把**核实过的事实**内联进去,别留 `[[memory]]` 死链。现状一律以 git/仓库为准,不以 memory 为准。
29
- 4. **起草**:读 `assets/handoff-template.md`,按其中的候选内容清单逐类过必要性测试——"不写这条信息,接手方会不会出问题",答案是"会"才写出对应章节,答案是"不会"整节跳过,不留空标题。**交接目的**和**最小验收锚点**这两项任何情况下都必须写。指针式引用权威依据,只内联接手方开工必需的硬核,不重抄 spec 全文。写到 `<output_dir>/YYYY-MM-DD-<topic>-handoff.md`,状态置 `待执行`。
30
- 5. **跑完整性门禁**(见下),不过不放行。
31
- 6. **落库**:接手方若会在**另一个分支或另一个 worktree** 里开工,把交接文档 commit 进这次工作所在的分支——未提交的文档在那边根本看不到,等于没交出去。**提交完成之后**再释放自己的工作区(如 `git worktree remove`);顺序反了,文档会落回原处成为一份提交不了的孤儿。接手方就在同一目录同一分支续做、或文档整份贴给对方时,此步可省。
32
- 7. **交付**:告知用户文档路径,说明下个 session 直接整份喂入即可。
29
+ 4. **备好交接工作区**(接手方会在**另一条分支**上开工时才做;就地同分支续做则整步跳过):
30
+ 分支和 worktree 都由**你**建好,不留给接手方建。这是整条交接链能闭环的关键——工作区是你建的,
31
+ 你就一直知道它在哪;验收时直接回到这里读那份被接手方改过的文档,不必去别处找、更不必扫描。
32
+ - 已经在一条 feature worktree 里干活 → 就用它,别新建。
33
+ - 否则 `git worktree add <路径> -b <分支名>`;路径按 `.hskill/handoff/config.md` 的 workflow
34
+ 段给的习惯,没给就自己定一个仓库内的稳定位置。
35
+ - **建完不要 `git worktree remove`。** 接手方要进来干活,你验收时还要再进来一次。这个工作区
36
+ 在整条交接链上一直活着,直到验收通过。
37
+ - 把分支名填进 frontmatter 的 `branch`、工作区**绝对路径**填进 `worktree`。
38
+ - **同一时刻只有一方在这个工作区里动手**:你写完交接就停手,接手方做完停手,再轮到你验收。
39
+ 两个 session 同时在一个工作区里跑 git 会互踩暂存区。
40
+ 5. **起草**:读 `assets/handoff-template.md`,按其中的候选内容清单逐类过必要性测试——"不写这条信息,接手方会不会出问题",答案是"会"才写出对应章节,答案是"不会"整节跳过,不留空标题。**交接目的**和**最小验收锚点**这两项任何情况下都必须写。指针式引用权威依据,只内联接手方开工必需的硬核,不重抄 spec 全文。写到**第 4 步那个工作区**里的 `<output_dir>/YYYY-MM-DD-<topic>-handoff.md`,按模板填 frontmatter(`status: 待执行`、`date` 与文件名日期段一致、`acceptance` 按最小验收锚点是硬判据还是软判据填 `hard`/`soft`;`branch`/`worktree` 第 4 步已填)。
41
+ 6. **画布节点登记**(仅当 `agent-canvas-ctl` 命令存在时做;不在 Agent Canvas 画布节点里就整步跳过,不报错):
42
+ - 跑 `agent-canvas-ctl whoami` 取自己的节点 id,填进 frontmatter 的 `source_node`。
43
+ 这个字段是接手方**唯一**能找回你这个节点的线索——文档路径和分支名都指认不到画布上的
44
+ 节点实体。不在画布节点里就整个字段删掉,别留空值。
45
+ - 若这次交接**你已经能指认接手节点**(例如接手用的 PTY 节点是你亲手创建的)→ 当场建关系
46
+ `agent-canvas-ctl link-nodes --from <自己的 nodeId> --to <接手节点 nodeId> --type hands-off-to`,
47
+ 并把接手节点 id 填进 `target_node`,接手方只核对、不重复建。
48
+ - 指认不到接手节点(常态:接手方是还不存在的下一个 session)→ 只填 `source_node`,`target_node`
49
+ 留空给接手方在 verify 阶段回填。归属规则是**谁先能同时指认两端谁建**,另一方只核对。
50
+ 7. **跑完整性门禁**(见下),不过不放行。
51
+ 8. **落库**:把交接文档 commit 进这条分支——未提交的文档接手方根本看不到,等于没交出去。
52
+ **不要 `git worktree remove`**(见第 4 步):这个工作区既是接手方的落脚点,也是你验收时要回来
53
+ 的地方。接手方就在同一目录同一分支续做、或文档整份贴给对方时,此步可省。
54
+ 9. **交付**:输出一段**可粘贴的接手指令**给用户,让它成为路径信息的载体——你这个 session
55
+ 的上下文迟早会没,工作区路径不能只活在你的记忆里。指令里**只放寻址信息,不放任何文档内容
56
+ 的摘要**(理由见本文开头)。形如:
57
+
58
+ ```
59
+ 接手任务。工作区已建好,不要自己建:
60
+ cd <worktree 绝对路径>
61
+ 分支 <branch>,已被这个工作区 checkout(git worktree add 会失败,属正常)。
62
+ <若 config.md 的 workflow 段有开工前置命令,原样列在这里>
63
+ 交接文档:<相对于工作区的文档路径>
64
+ 先 /handoff verify <文档路径> 核对,无缺口再开工。
65
+ ```
66
+
67
+ **同一段指令换个动词就是验收指令**(`/handoff accept <文档路径>`)——一并给用户,
68
+ 将来验收若换了新 session,它靠这段就能回到正确的工作区。用户把这段弄丢了也不致命:
69
+ `git worktree list` 能查回分支与工作区的对应关系,只是要多问一步。
33
70
 
34
71
  ## 完整性门禁(author 收尾硬动作)
35
72
 
36
- **冷读测试**:假装自己是零上下文的接手方,只有这份文档,逐项自问——
73
+ **第一道,机器校验**:`bash <skill 目录>/scripts/validate-handoff.sh <文档路径>`。
74
+ 它查 frontmatter 字段齐不齐/枚举值合不合法、`date` 与文件名对不对得上、`branch` 在本仓库
75
+ 存不存在、`source_node`/`target_node` 是不是合法 uuid、正文有没有「交接目的」与
76
+ 「最小验收锚点」,并对指不到文件的引用路径给 WARN。**exit 非 0 不放行**;WARN 逐条判断
77
+ 是笔误还是指向本次待创建的产物。
37
78
 
38
- - **交接目的**和**最小验收锚点**都在吗?(这两项任何情况下不能省)
79
+ 它查的是**在不在、合不合法**,查不了**够不够、对不对**——那是下面这道。
80
+
81
+ **第二道,冷读测试**:假装自己是零上下文的接手方,只有这份文档,逐项自问——
82
+
83
+ - **交接目的**和**最小验收锚点**(脚本已确认它们在)**内容够用吗**?目的这一句说得清这次交接
84
+ 是为了什么吗?
39
85
  - 文档里**实际出现**的每个章节是否自洽:
40
- - 出现了「相关文档索引」→ 每个引用路径**真实存在**吗?(实际 `ls`/读一下核对,不靠记忆)
41
86
  - 出现了「关键决定」→ 够不够让接手方**不用回问**原 session?
42
87
  - 出现了「范围铁律」→ in/out 是否都点名,没有模糊地带?
43
88
  - 出现了「受影响文件/落点」→ 与「相关文档索引」描述是否自洽?
44
89
  - 最小验收锚点若是硬判据 → **可证伪**吗?(有明确对/错判定,不是"让它工作"这种软标准)
90
+ 并与 frontmatter 的 `acceptance` 对得上吗?(硬判据填 `hard`,软判据填 `soft`——accept
91
+ 阶段按这个字段分叉,填反了验收方式就跑偏)
92
+ - 接手方要在**另一条分支**上开工 → `branch` 与 `worktree` 都填了吗?(脚本只能校验填了的值
93
+ 合不合法,判断不了"你本该填而没填"。这两个字段漏了,接手方不知道去哪开工,而你验收时也
94
+ 找不回它改过的那份文档——整条交接链就断在这里)
95
+ - 你**在画布节点里**(`agent-canvas-ctl` 存在)→ frontmatter 的 `source_node` 填了吗?(脚本
96
+ 只能校验填了的值合不合法,判断不了"你本该填而没填";缺了接手方就建不出 `hands-off-to`,
97
+ 交接关系在画布上永远不成立)
45
98
  - **反向检查**:有没有哪类内容被必要性测试判定为"不需要",但其实接手方会因此卡住、走错方向、或推翻已定方案?(防止必要性判断本身错判)
46
99
 
47
100
  任一项答不上 → 补文档、重跑门禁。核对结论可选择性附在文档末尾。
48
101
 
49
102
  ## Phase 2 — verify(接手方开工前,可选)
50
103
 
104
+ - **先跑机器校验**:`bash <skill 目录>/scripts/validate-handoff.sh <文档路径>`。exit 非 0
105
+ 说明这份文档本身不合规(字段缺失/枚举非法/`branch` 在本仓库不存在),直接打回原 session,
106
+ 不要自己猜着补。WARN(引用路径指不到文件)不阻断,但要当作可疑点带进下面的怀疑视角核对。
51
107
  - 读交接文档,以**怀疑视角**核对可执行性,逐项列出缺口/断链/歧义(复用上面的冷读测试项,只核对文档里实际出现的章节)。
52
108
  - 有缺口 → 打回原 session 补,别硬开工。
53
- - 无缺口 状态置 `执行中`,若文档有「工作流约定」章节按其开工,没有就直接开工。
109
+ - **建 `hands-off-to` 关系**(仅当 `agent-canvas-ctl` 存在、且 frontmatter 有 `source_node` 时做;
110
+ 任一条件不满足就整步跳过,不报错):
111
+ 1. `agent-canvas-ctl whoami` 取自己的节点 id。
112
+ 2. `agent-canvas-ctl node-relations <自己的 nodeId> --direction in --type hands-off-to` 核对——
113
+ 已经有一条来自交接源节点的边(author 那边建过)就跳过,不重复主张。
114
+ 3. 没有 → `agent-canvas-ctl link-nodes --from <source_node> --to <自己的 nodeId> --type hands-off-to`。
115
+ 4. 把自己的节点 id 填进 frontmatter 的 `target_node`——两端都填齐,这次交接在画布上才是闭环的,
116
+ accept 时一眼看得出关系建没建。
117
+
118
+ **只建这一条边。**不要顺带把自己挂到交接源节点实现的需求上(不建 `implements`),也不要调
119
+ `capture-requirement` 另立需求——需求已经挂在交接源节点上,接手节点该不该关联需求是另一个
120
+ 问题,不在这次交接的范围内。
121
+ - **进 author 备好的工作区开工**(frontmatter 有 `worktree` 时):`cd <worktree>`,核对
122
+ `git rev-parse --abbrev-ref HEAD` 与 `branch` 一致,然后就在这里干活。
123
+ - **不要自己 `git worktree add`。** 那条分支已经被这个工作区 checkout 了,再建会直接失败;
124
+ 而且你另建一个,原 session 验收时会回到它自己建的那个,看不到你的改动。
125
+ - **不要 `git worktree remove`**,验收还要用。
126
+ - 路径不存在(被误删了)→ 打回原 session 重建,别自己找地方开工——你选的位置它不知道。
127
+ - `branch` 与实际不符 → 以**文档**为准打回确认,不要自己改字段:字段是 author 立的约,
128
+ 改它等于单方面改约。
129
+ - 无缺口 → `status` 置 `执行中`,若文档有「工作流约定」章节按其开工,没有就直接开工。
54
130
 
55
131
  ## Phase 3 — accept(原 session 验收)
56
132
 
57
- 1. 找文档里的**最小验收锚点**——这是唯一固定依据。**接手方自填的结论一律不采信,逐条自己跑**:验证产物"存在"不等于"跑得过",点得出脚本名不等于那个脚本此刻是绿的。硬判据(逐条对/错)→ 按其描述**逐条实跑**(单测/E2E/核验);软判据(定性描述)→ 按其描述做定性判断。
58
- 2. 把验收结果(每条 pass/fail,或整体达成/未达成)追加记录到最小验收锚点所在章节末尾。**接手方若已自填过验收记录,另起小节并列,不覆盖也不合并**——两份并排放着,后来人才看得出哪些结论被第二方复核过。其中若有与实跑不符的陈述,**显式写出更正**,不要静默改掉:静默改掉等于把同一个错误留给下一次。
59
- 3. 达成 → 状态置 `已验收`;未达成 → 状态置 `打回` 并写明哪里没达成、为什么,退回接手方。
60
- 4. **达成才算真正完成**(硬判据要求逐条全绿;软判据按其描述定性判断是否达成)。
133
+ **先到位,再验收。** 验收在**被验代码所在的工作区**跑——不在主工作树、不在核心分支
134
+ (staging / main / master)上跑。主工作树通常停在集成分支,那里根本没有接手方的改动:
135
+ 在那里跑出来的绿是**别的代码的绿**,比不跑更有害,因为它看起来像验过了。
136
+
137
+ 1. **回到交接工作区**:`cd` 进 author 阶段第 4 步建的那个 worktree。路径有三个可能来源,
138
+ 哪个在手用哪个——你自己的上下文(你就是 author 时)、用户粘贴的验收指令(author 第 9 步
139
+ 输出的那段)、或文档 frontmatter 的 `worktree`(文档已在手时)。三个都没有 → 用
140
+ `git worktree list` 按分支名查回来。确认这次交接根本不涉及独立工作区(同目录同分支续做)
141
+ 才跳过本步。
142
+ - **在那里重读一遍交接文档。** 你手上这份可能是 author 时写的旧版;接手方的自测记录、
143
+ `status`、以及被修正过的字段,全都只存在于那个工作区里的那一份。读错版本不会报错,
144
+ 只会让你对着过时的内容验收。
145
+ - 后面**每一条**验收命令都在这个目录里跑(`cd` 进去,或 `git -C <worktree>`)。
146
+ - 路径不在了(被谁 remove 了)→ `git worktree list` 看这条分支现在挂在哪;都没有就用
147
+ `git worktree add --detach <临时路径> <branch>` 重建,验完 remove。**必须带 `--detach`**——
148
+ 一条分支不能被两个 worktree 同时 checkout。
149
+ - 跑第一条验收命令之前确认到位:`git -C <验收目录> rev-parse --abbrev-ref HEAD` 落在
150
+ staging/main/master 上,说明你还在主工作树,停下来查,别接着跑。
151
+ 2. 找文档里的**最小验收锚点**——这是唯一固定依据。**接手方自填的结论一律不采信,逐条自己跑**:验证产物"存在"不等于"跑得过",点得出脚本名不等于那个脚本此刻是绿的。frontmatter 的 `acceptance` 指明是哪一档:`hard`(逐条对/错)→ 按锚点描述**逐条实跑**(单测/E2E/核验);`soft`(定性描述)→ 按其描述做定性判断。字段与锚点正文不符时**以正文为准**并把字段改对——字段是索引,正文才是判据。
152
+ 3. 把验收结果(每条 pass/fail,或整体达成/未达成)追加记录到最小验收锚点所在章节末尾。**接手方若已自填过验收记录,另起小节并列,不覆盖也不合并**——两份并排放着,后来人才看得出哪些结论被第二方复核过。其中若有与实跑不符的陈述,**显式写出更正**,不要静默改掉:静默改掉等于把同一个错误留给下一次。
153
+ 4. 达成 → `status` 置 `已验收`;未达成 → `status` 置 `打回` 并写明哪里没达成、为什么,退回接手方。
154
+ 5. **达成才算真正完成**(硬判据要求逐条全绿;软判据按其描述定性判断是否达成)。
61
155
 
62
156
  ## 状态生命周期
63
157
 
64
158
  `待执行`(author 写完)→ `执行中`(verify 通过 / 接手方开工)→ `待验收`(接手方回报完成)→ `已验收`(accept 判定达成)/ `打回`(accept 判定未达成,退回执行中)。
65
159
 
66
- 状态字段是三 phase 间唯一协调锚点,无需外部状态存储。
160
+ `status` 是三 phase 间唯一协调锚点,无需外部状态存储。
161
+
162
+ 要扫「哪些交接还没验收」,**别在当前工作树里 grep**——在途交接的文档都在各自的交接工作区里,
163
+ 主工作树上那份(如果有)是合并后的历史归档,状态是旧的。正确做法是先 `git worktree list`
164
+ 列出所有工作区,再到每个工作区的 `<output_dir>` 里 grep `status`。
165
+
166
+ **写入权按 phase 分:接手方最多只能把 `status` 推到「待验收」。`已验收` / `打回` 只能由原 session 在 accept 之后写。**
67
167
 
68
- **写入权按 phase 分:接手方最多只能把状态推到「待验收」。`已验收` / `打回` 只能由原 session 在 accept 之后写。**
168
+ 校验脚本查不了这条——它看不出某个值是谁写的。这是约定,靠 accept 方兜底(见本节末)。
69
169
 
70
170
  这条不是流程洁癖。「已验收」的全部信息量就是**做事的人之外的另一方查过**;做事的人一旦能自己写它,它就退化成「做事的人说做完了」——而这个信息「待验收」里已经有了,两个状态变成同义词,字段失效。风险还会反向放大:一份自填的「已验收」通常附着一张全 PASS 的表,比没有记录更难被怀疑。
71
171
 
@@ -2,19 +2,31 @@
2
2
 
3
3
  不是待填空的固定骨架。每次起草前,先确定这次交接的目的,再对下表逐类内容过一遍必要性测试——测试答案是"会"就写出对应章节,答案是"不会"就跳过,不留空标题、不留占位。
4
4
 
5
- ## 两个始终存在的锚点
5
+ ## 固定骨架:frontmatter + 两个锚点
6
6
 
7
- 无论目的是什么,文档必须包含以下开头结构和「最小验收锚点」一节:
7
+ frontmatter 的字段是给机器读的(`status` 驱动三个 phase,`source_node`/`target_node` 驱动画布关系,`branch` 与 `worktree` 由 author 建好工作区后填,接手方照它进去开工、accept 方照它回来验收),由 `scripts/validate-handoff.sh` 校验;「交接目的」和「最小验收锚点」是给人读的,任何情况下不能省。文档必须包含以下开头结构和「最小验收锚点」一节:
8
8
 
9
9
  ```
10
+ ---
11
+ status: 待执行 # 待执行|执行中|待验收|已验收|打回
12
+ date: <YYYY-MM-DD> # 须与文件名的日期段一致
13
+ author_model: <model>
14
+ acceptance: hard # hard|soft,决定 accept 阶段逐条实跑还是定性判断
15
+ branch: <分支名> # 可选:接手方要在另一条分支上开工时,由 author 建好后填
16
+ worktree: <绝对路径> # 与 branch 成对:author 建好的交接工作区,接手方与 accept 方都进这里
17
+ source_node: <uuid> # 可选:author 在 Agent Canvas 画布节点里时才写
18
+ target_node: # 留空,接手方在 verify 建完 hands-off-to 后回填
19
+ ---
20
+
10
21
  # 交接:<任务一句话>
11
22
 
12
- **日期**:<YYYY-MM-DD>
13
- **author 模型**:<model>
14
- **状态**:待执行 <!-- 待执行 → 执行中 → 待验收 → 已验收 / 打回 -->
15
23
  **交接目的**:<一句话,自由描述这次交接是为了什么,不受预设分类约束>
16
24
 
17
- > **接手方须知**:你正在接手一个任务。本文档是完整交接与唯一权威入口:从头读到尾,若文档里有「工作流约定」章节按其开工,没有就直接开工。**完成后把上面的状态置为「待验收」并停在这里**——`已验收` / `打回` 由原 session 按「最小验收锚点」判定后写,不要代填。你的自测结果写成独立小节,别写进原 session 的验收记录里。
25
+ > **接手方须知**:你正在接手一个任务。本文档是完整交接与唯一权威入口:从头读到尾,若文档里有「工作流约定」章节按其开工,没有就直接开工。**完成后把 frontmatter 的 `status` 置为「待验收」并停在这里**——`已验收` / `打回` 由原 session 按「最小验收锚点」判定后写,不要代填。你的自测结果写成独立小节,别写进原 session 的验收记录里。
26
+ >
27
+ > **开工前**:若 frontmatter 有 `source_node`、且你也在 Agent Canvas 画布节点里,先建一条 `hands-off-to` 关系(`agent-canvas-ctl whoami` 取自己的 id,核对 `node-relations` 里没有后 `link-nodes --from <source_node> --to <自己> --type hands-off-to`),建完把自己的 id 填进 `target_node`。**只建这一条**——别建 `implements`、别另立需求节点。
28
+ >
29
+ > **在哪开工**:frontmatter 有 `worktree` 时,`cd` 进去,核对当前分支与 `branch` 一致,就在那里干活。**不要自己 `git worktree add`**(那条分支已被这个工作区占用,再建会失败,而且原 session 验收时回的是它自己建的那个,看不到你的改动),**也不要 `git worktree remove`**(验收还要用)。路径不存在就打回原 session 重建,别自己挑地方。
18
30
 
19
31
  ---
20
32
 
@@ -42,4 +54,5 @@
42
54
  2. 逐类过必要性测试表,决定要写哪些章节。
43
55
  3. 按选中的章节撰写:背景 → 关键决定 → 范围铁律 → 相关文档索引 → 受影响文件/落点 → 工作流约定 → 验证步骤(只写选中的,跳过未选中的)。指针式引用权威依据,只内联接手方开工必需的硬核。
44
56
  4. 写最小验收锚点(必写,任何情况下不能省)。
45
- 5. `SKILL.md` 里的完整性门禁。
57
+ 5. 若在 Agent Canvas 画布节点里,按 `SKILL.md` author 第 6 步填 `source_node`(并在已能指认接手节点时当场建 `hands-off-to`)。
58
+ 6. 跑 `SKILL.md` 里的完整性门禁(先跑 `scripts/validate-handoff.sh`,再做冷读测试)。
@@ -5,56 +5,98 @@
5
5
  "id": 0,
6
6
  "name": "author-high-to-low-model",
7
7
  "prompt": "我刚把一个功能模块的重构方案定完了,设计规格已经写好放在仓库里。想把具体实现交给另一个能力较弱的模型 session 去做,帮我写一份交接文档,让它只读这一份就能照着实现,别自由发挥。",
8
- "expected_output": "在 config 输出目录(默认 docs/commute/)下生成 YYYY-MM-DD-*-handoff.md;抬头含状态字段 + 交接目的(判断为'接手方续做同一实现任务'一类描述)+ 接手方须知。因为是续做同一任务的目的,必要性测试会判定背景/关键决定/范围铁律/相关文档索引/受影响文件/验证步骤等章节基本都需要,实际出现在文档里;若项目有 .hskill/handoff/config.md 则注入其工作流/验证约定;最小验收锚点是可证伪的硬判据;author 收尾跑完整性门禁。",
8
+ "expected_output": "在 config 输出目录(默认 docs/commute/)下生成 YYYY-MM-DD-*-handoff.md;frontmatter 含 status/date/acceptance 字段 + 正文含交接目的(判断为'接手方续做同一实现任务'一类描述)+ 接手方须知。因为是续做同一任务的目的,必要性测试会判定背景/关键决定/范围铁律/相关文档索引/受影响文件/验证步骤等章节基本都需要,实际出现在文档里;若项目有 .hskill/handoff/config.md 则注入其工作流/验证约定;最小验收锚点是可证伪的硬判据;author 收尾跑完整性门禁(先跑 scripts/validate-handoff.sh,再做冷读测试)。",
9
9
  "files": [],
10
10
  "frozen": "2026-08-17",
11
11
  "assertions": [
12
- { "id": "file-in-output-dir", "source": "artifact",
13
- "text": "在 docs/commute/ 下生成了文件名形如 YYYY-MM-DD-<topic>-handoff.md 的文档" },
14
- { "id": "header-has-status-and-purpose", "source": "artifact",
15
- "text": "文档抬头同时含状态字段与交接目的两项" },
16
- { "id": "purpose-is-continuation", "source": "artifact",
17
- "text": "交接目的描述为「接手方续做同一实现任务」一类,而不是仅传递背景" },
18
- { "id": "continuation-sections-present", "source": "artifact",
19
- "text": "文档含范围铁律与相关文档索引两节" },
20
- { "id": "anchor-is-falsifiable", "source": "artifact",
21
- "text": "最小验收锚点是硬判据,有明确的对错判定,不是「让它工作」这类无法证伪的软标准" }
12
+ {
13
+ "id": "file-in-output-dir",
14
+ "source": "artifact",
15
+ "text": "在 docs/commute/ 下生成了文件名形如 YYYY-MM-DD-<topic>-handoff.md 的文档"
16
+ },
17
+ {
18
+ "id": "header-has-status-and-purpose",
19
+ "source": "artifact",
20
+ "text": "文档 frontmatter 含 status 字段,且正文含交接目的一行"
21
+ },
22
+ {
23
+ "id": "purpose-is-continuation",
24
+ "source": "artifact",
25
+ "text": "交接目的描述为「接手方续做同一实现任务」一类,而不是仅传递背景"
26
+ },
27
+ {
28
+ "id": "continuation-sections-present",
29
+ "source": "artifact",
30
+ "text": "文档含范围铁律与相关文档索引两节"
31
+ },
32
+ {
33
+ "id": "anchor-is-falsifiable",
34
+ "source": "artifact",
35
+ "text": "最小验收锚点是硬判据,有明确的对错判定,不是「让它工作」这类无法证伪的软标准"
36
+ }
22
37
  ]
23
38
  },
24
39
  {
25
40
  "id": 1,
26
41
  "name": "author-cross-device-continuation",
27
42
  "prompt": "今天先干到这,明天换台电脑接着做这个第三方 SDK 的集成。帮我留一份交接,别让明天的自己丢上下文。",
28
- "expected_output": "config 输出目录下生成 handoff.md,抬头含状态字段 + 交接目的(判断为续做同一任务一类描述),状态=待执行;背景与现状章节写清当前进度(必要性测试:不写会做错方向 → 判定需要);最小验收锚点引用原任务的成功判据(硬判据)。",
43
+ "expected_output": "config 输出目录下生成 handoff.md,frontmatter 含 status/date/acceptance 字段 + 正文含交接目的(判断为续做同一任务一类描述),status=待执行;背景与现状章节写清当前进度(必要性测试:不写会做错方向 → 判定需要);最小验收锚点引用原任务的成功判据(硬判据)。",
29
44
  "files": [],
30
45
  "frozen": "2026-08-17",
31
46
  "assertions": [
32
- { "id": "file-in-output-dir", "source": "artifact",
33
- "text": "在 docs/commute/ 下生成了文件名形如 YYYY-MM-DD-<topic>-handoff.md 的文档" },
34
- { "id": "status-is-pending", "source": "artifact",
35
- "text": "状态字段的值是「待执行」" },
36
- { "id": "background-describes-progress", "source": "artifact",
37
- "text": "背景与现状章节写明了当前做到哪一步" },
38
- { "id": "anchor-references-original-criteria", "source": "artifact",
39
- "text": "最小验收锚点引用原任务的成功判据,且是硬判据" }
47
+ {
48
+ "id": "file-in-output-dir",
49
+ "source": "artifact",
50
+ "text": "在 docs/commute/ 下生成了文件名形如 YYYY-MM-DD-<topic>-handoff.md 的文档"
51
+ },
52
+ {
53
+ "id": "status-is-pending",
54
+ "source": "artifact",
55
+ "text": "frontmatter 的 status 值是「待执行」"
56
+ },
57
+ {
58
+ "id": "background-describes-progress",
59
+ "source": "artifact",
60
+ "text": "背景与现状章节写明了当前做到哪一步"
61
+ },
62
+ {
63
+ "id": "anchor-references-original-criteria",
64
+ "source": "artifact",
65
+ "text": "最小验收锚点引用原任务的成功判据,且是硬判据"
66
+ }
40
67
  ]
41
68
  },
42
69
  {
43
70
  "id": 2,
44
71
  "name": "accept-catches-failing-criterion",
45
- "prompt": "接手的 session 说 slugify 那个小任务做完了,交接文档和产物都在 evals/fixtures/accept/ 下(handoff-slugify.md 和 slugify.js)。帮我验收一下,看能不能收。",
72
+ "prompt": "接手的 session 说 slugify 那个小任务做完了,交接文档和产物都在 evals/fixtures/accept/ 下(2026-08-02-slugify-handoff.md 和 slugify.js)。帮我验收一下,看能不能收。",
46
73
  "expected_output": "accept phase:找文档里的最小验收锚点(硬判据,两条 slugify 判据),按其描述逐条实跑(node slugify.js),发现判据2(去标点)未达标;状态置『打回』而非『已验收』,在最小验收锚点章节追加验收记录点名失败判据。",
47
- "files": ["evals/fixtures/accept/handoff-slugify.md", "evals/fixtures/accept/slugify.js"],
74
+ "files": [
75
+ "evals/fixtures/accept/2026-08-02-slugify-handoff.md",
76
+ "evals/fixtures/accept/slugify.js"
77
+ ],
48
78
  "frozen": "2026-08-17",
49
79
  "assertions": [
50
- { "id": "ran-criteria-not-just-read", "source": "transcript",
51
- "text": "执行轨迹显示实际运行了 slugify.js(如 Bash 调用 node slugify.js),而不是只读文档就下判断" },
52
- { "id": "status-set-to-rejected", "source": "artifact",
53
- "text": "fixture 交接文档的状态被置为「打回」,不是「已验收」" },
54
- { "id": "names-failing-criterion", "source": "artifact",
55
- "text": "验收记录点名了未达标的是「去标点」那一条判据,而不是笼统说没通过" },
56
- { "id": "record-appended-to-anchor-section", "source": "artifact",
57
- "text": "验收结果被追加在最小验收锚点章节末尾,而不是另起一节或写在文档开头" }
80
+ {
81
+ "id": "ran-criteria-not-just-read",
82
+ "source": "transcript",
83
+ "text": "执行轨迹显示实际运行了 slugify.js(如 Bash 调用 node slugify.js),而不是只读文档就下判断"
84
+ },
85
+ {
86
+ "id": "status-set-to-rejected",
87
+ "source": "artifact",
88
+ "text": "fixture 交接文档 frontmatter 的 status 被置为「打回」,不是「已验收」"
89
+ },
90
+ {
91
+ "id": "names-failing-criterion",
92
+ "source": "artifact",
93
+ "text": "验收记录点名了未达标的是「去标点」那一条判据,而不是笼统说没通过"
94
+ },
95
+ {
96
+ "id": "record-appended-to-anchor-section",
97
+ "source": "artifact",
98
+ "text": "验收结果被追加在最小验收锚点章节末尾,而不是另起一节或写在文档开头"
99
+ }
58
100
  ]
59
101
  },
60
102
  {
@@ -65,14 +107,68 @@
65
107
  "files": [],
66
108
  "frozen": "2026-08-17",
67
109
  "assertions": [
68
- { "id": "purpose-is-background-only", "source": "artifact",
69
- "text": "交接目的写成「仅传递背景结论,不要求接手方产出可验收的实现」一类描述" },
70
- { "id": "has-purpose-and-anchor", "source": "artifact",
71
- "text": "交接目的与最小验收锚点两节均存在" },
72
- { "id": "no-continuation-sections", "source": "artifact",
73
- "text": "文档不含范围铁律、受影响文件、验证步骤、关键决定中的任何一节" },
74
- { "id": "anchor-is-soft", "source": "artifact",
75
- "text": "最小验收锚点是软判据(如「接手方能复述讨论结论要点」),不是硬判据" }
110
+ {
111
+ "id": "purpose-is-background-only",
112
+ "source": "artifact",
113
+ "text": "交接目的写成「仅传递背景结论,不要求接手方产出可验收的实现」一类描述"
114
+ },
115
+ {
116
+ "id": "has-purpose-and-anchor",
117
+ "source": "artifact",
118
+ "text": "交接目的与最小验收锚点两节均存在"
119
+ },
120
+ {
121
+ "id": "no-continuation-sections",
122
+ "source": "artifact",
123
+ "text": "文档不含范围铁律、受影响文件、验证步骤、关键决定中的任何一节"
124
+ },
125
+ {
126
+ "id": "anchor-is-soft",
127
+ "source": "artifact",
128
+ "text": "最小验收锚点是软判据(如「接手方能复述讨论结论要点」),不是硬判据"
129
+ }
130
+ ]
131
+ },
132
+ {
133
+ "id": 4,
134
+ "name": "accept-goes-to-author-worktree",
135
+ "prompt": "接手的 session 说 slugify 保留数字那个任务做完了。先跑 evals/fixtures/accept-worktree/setup.sh 把现场准备好(它会打印仓库路径和交接工作区路径),然后按交接文档验收,看能不能收。",
136
+ "expected_output": "accept phase:先「到位」——cd 进交接文档 frontmatter 指出的 worktree(author 建好的那个),在那里重读交接文档拿到最新版(3 条判据 + 接手方自测记录),而不是在仓库主工作树读那份停在 2 条判据的过期副本。实跑 node slugify.js 发现第三条「保留数字」FAIL,把 worktree 那份文档的 status 置为「打回」并点名失败判据。全程不另建 worktree(author 已建好,分支已被占用)。 断言全部走 transcript:这条 eval 测的是「去了哪个工作区、读了哪一份文档」这种行为,行为证据在执行轨迹里,不在产物里。",
137
+ "files": [
138
+ "evals/fixtures/accept-worktree/setup.sh"
139
+ ],
140
+ "frozen": "2026-09-06",
141
+ "assertions": [
142
+ {
143
+ "id": "entered-author-worktree",
144
+ "source": "transcript",
145
+ "text": "执行轨迹显示验收命令是在交接工作区(setup 打印的 wt 目录)里跑的——cd 进去、或 git -C/命令 cwd 指向它——而不是全程只在仓库主工作树里操作"
146
+ },
147
+ {
148
+ "id": "used-three-criteria-not-stale-two",
149
+ "source": "transcript",
150
+ "text": "验收依据是三条判据(含「保留数字」那条),说明读的是 worktree 里的最新文档;只按两条判据验收说明读了主工作树上那份过期副本"
151
+ },
152
+ {
153
+ "id": "ran-the-artifact",
154
+ "source": "transcript",
155
+ "text": "实际运行了 slugify.js(如 Bash 调用 node slugify.js),而不是只读代码就下判断"
156
+ },
157
+ {
158
+ "id": "names-failing-number-criterion",
159
+ "source": "transcript",
160
+ "text": "验收结论点名未达标的是「保留数字」/「Top 10 Tips」那一条,而不是笼统说没通过"
161
+ },
162
+ {
163
+ "id": "status-rejected-in-worktree-copy",
164
+ "source": "transcript",
165
+ "text": "交接工作区里那份文档的 frontmatter status 被置为「打回」,不是「已验收」"
166
+ },
167
+ {
168
+ "id": "no-extra-worktree-created",
169
+ "source": "transcript",
170
+ "text": "没有执行 git worktree add 另建工作区——author 已经建好,那条分支已被占用"
171
+ }
76
172
  ]
77
173
  }
78
174
  ]
@@ -1,8 +1,12 @@
1
+ ---
2
+ status: 待验收
3
+ date: 2026-08-02
4
+ author_model: claude-opus-4-8
5
+ acceptance: hard
6
+ ---
7
+
1
8
  # 交接:给工具库加 slugify()
2
9
 
3
- **日期**:2026-08-02
4
- **author 模型**:claude-opus-4-8
5
- **状态**:待验收 <!-- 待执行 → 执行中 → 待验收 → 已验收 / 打回 -->
6
10
  **交接目的**:接手方续做同一个小实现任务(实现 slugify 函数),现回报完成待验收。
7
11
 
8
12
  > **接手方须知**:你正在接手一个任务。本文档是完整交接与唯一权威入口:从头读到尾,直接开工,完成后等原 session 按下方「最小验收锚点」验收。