@hunter-harness/workflow-harness 0.2.61 → 0.2.63

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 (122) hide show
  1. package/harness/bundles/general/claude-code/.harness-build.json +1 -1
  2. package/harness/bundles/general/claude-code/harness-archive/SKILL.md +1 -1
  3. package/harness/bundles/general/claude-code/harness-codebase-map/SKILL.md +1 -1
  4. package/harness/bundles/general/claude-code/harness-knowledge-ingest/SKILL.md +1 -1
  5. package/harness/bundles/general/claude-code/harness-knowledge-query/SKILL.md +1 -1
  6. package/harness/bundles/general/claude-code/harness-plan/SKILL.md +1 -1
  7. package/harness/bundles/general/claude-code/harness-review/SKILL.md +1 -1
  8. package/harness/bundles/general/claude-code/harness-run/SKILL.md +1 -1
  9. package/harness/bundles/general/claude-code/harness-submit/SKILL.md +1 -1
  10. package/harness/bundles/general/claude-code/harness-sync/SKILL.md +15 -22
  11. package/harness/bundles/general/claude-code/harness-sync/reference.md +22 -11
  12. package/harness/bundles/general/claude-code/harness-test/SKILL.md +1 -1
  13. package/harness/bundles/general/claude-code/scripts/harness_events_sync.py +191 -26
  14. package/harness/bundles/general/codebuddy/.harness-build.json +1 -1
  15. package/harness/bundles/general/codebuddy/harness-archive/SKILL.md +1 -1
  16. package/harness/bundles/general/codebuddy/harness-codebase-map/SKILL.md +1 -1
  17. package/harness/bundles/general/codebuddy/harness-knowledge-ingest/SKILL.md +1 -1
  18. package/harness/bundles/general/codebuddy/harness-knowledge-query/SKILL.md +1 -1
  19. package/harness/bundles/general/codebuddy/harness-plan/SKILL.md +1 -1
  20. package/harness/bundles/general/codebuddy/harness-review/SKILL.md +1 -1
  21. package/harness/bundles/general/codebuddy/harness-run/SKILL.md +1 -1
  22. package/harness/bundles/general/codebuddy/harness-submit/SKILL.md +1 -1
  23. package/harness/bundles/general/codebuddy/harness-sync/SKILL.md +15 -22
  24. package/harness/bundles/general/codebuddy/harness-sync/reference.md +22 -11
  25. package/harness/bundles/general/codebuddy/harness-test/SKILL.md +1 -1
  26. package/harness/bundles/general/codebuddy/scripts/harness_events_sync.py +191 -26
  27. package/harness/bundles/general/codex/.harness-build.json +1 -1
  28. package/harness/bundles/general/codex/harness-archive/SKILL.md +1 -1
  29. package/harness/bundles/general/codex/harness-codebase-map/SKILL.md +1 -1
  30. package/harness/bundles/general/codex/harness-knowledge-ingest/SKILL.md +1 -1
  31. package/harness/bundles/general/codex/harness-knowledge-query/SKILL.md +1 -1
  32. package/harness/bundles/general/codex/harness-plan/SKILL.md +1 -1
  33. package/harness/bundles/general/codex/harness-review/SKILL.md +1 -1
  34. package/harness/bundles/general/codex/harness-run/SKILL.md +1 -1
  35. package/harness/bundles/general/codex/harness-submit/SKILL.md +1 -1
  36. package/harness/bundles/general/codex/harness-sync/SKILL.md +15 -22
  37. package/harness/bundles/general/codex/harness-sync/reference.md +22 -11
  38. package/harness/bundles/general/codex/harness-test/SKILL.md +1 -1
  39. package/harness/bundles/general/codex/scripts/harness_events_sync.py +191 -26
  40. package/harness/bundles/general/cursor/.harness-build.json +1 -1
  41. package/harness/bundles/general/cursor/harness-archive/SKILL.md +1 -1
  42. package/harness/bundles/general/cursor/harness-codebase-map/SKILL.md +1 -1
  43. package/harness/bundles/general/cursor/harness-knowledge-ingest/SKILL.md +1 -1
  44. package/harness/bundles/general/cursor/harness-knowledge-query/SKILL.md +1 -1
  45. package/harness/bundles/general/cursor/harness-plan/SKILL.md +1 -1
  46. package/harness/bundles/general/cursor/harness-review/SKILL.md +1 -1
  47. package/harness/bundles/general/cursor/harness-run/SKILL.md +1 -1
  48. package/harness/bundles/general/cursor/harness-submit/SKILL.md +1 -1
  49. package/harness/bundles/general/cursor/harness-sync/SKILL.md +15 -22
  50. package/harness/bundles/general/cursor/harness-sync/reference.md +22 -11
  51. package/harness/bundles/general/cursor/harness-test/SKILL.md +1 -1
  52. package/harness/bundles/general/cursor/scripts/harness_events_sync.py +191 -26
  53. package/harness/bundles/java/claude-code/.harness-build.json +1 -1
  54. package/harness/bundles/java/claude-code/harness-apidoc/SKILL.md +1 -1
  55. package/harness/bundles/java/claude-code/harness-archive/SKILL.md +1 -1
  56. package/harness/bundles/java/claude-code/harness-codebase-map/SKILL.md +1 -1
  57. package/harness/bundles/java/claude-code/harness-knowledge-ingest/SKILL.md +1 -1
  58. package/harness/bundles/java/claude-code/harness-knowledge-query/SKILL.md +1 -1
  59. package/harness/bundles/java/claude-code/harness-package/SKILL.md +1 -1
  60. package/harness/bundles/java/claude-code/harness-plan/SKILL.md +1 -1
  61. package/harness/bundles/java/claude-code/harness-review/SKILL.md +1 -1
  62. package/harness/bundles/java/claude-code/harness-run/SKILL.md +1 -1
  63. package/harness/bundles/java/claude-code/harness-submit/SKILL.md +1 -1
  64. package/harness/bundles/java/claude-code/harness-sync/SKILL.md +15 -22
  65. package/harness/bundles/java/claude-code/harness-sync/reference.md +22 -11
  66. package/harness/bundles/java/claude-code/harness-test/SKILL.md +1 -1
  67. package/harness/bundles/java/claude-code/scripts/harness_events_sync.py +191 -26
  68. package/harness/bundles/java/codebuddy/.harness-build.json +1 -1
  69. package/harness/bundles/java/codebuddy/harness-apidoc/SKILL.md +1 -1
  70. package/harness/bundles/java/codebuddy/harness-archive/SKILL.md +1 -1
  71. package/harness/bundles/java/codebuddy/harness-codebase-map/SKILL.md +1 -1
  72. package/harness/bundles/java/codebuddy/harness-knowledge-ingest/SKILL.md +1 -1
  73. package/harness/bundles/java/codebuddy/harness-knowledge-query/SKILL.md +1 -1
  74. package/harness/bundles/java/codebuddy/harness-package/SKILL.md +1 -1
  75. package/harness/bundles/java/codebuddy/harness-plan/SKILL.md +1 -1
  76. package/harness/bundles/java/codebuddy/harness-review/SKILL.md +1 -1
  77. package/harness/bundles/java/codebuddy/harness-run/SKILL.md +1 -1
  78. package/harness/bundles/java/codebuddy/harness-submit/SKILL.md +1 -1
  79. package/harness/bundles/java/codebuddy/harness-sync/SKILL.md +15 -22
  80. package/harness/bundles/java/codebuddy/harness-sync/reference.md +22 -11
  81. package/harness/bundles/java/codebuddy/harness-test/SKILL.md +1 -1
  82. package/harness/bundles/java/codebuddy/scripts/harness_events_sync.py +191 -26
  83. package/harness/bundles/java/codex/.harness-build.json +1 -1
  84. package/harness/bundles/java/codex/harness-apidoc/SKILL.md +1 -1
  85. package/harness/bundles/java/codex/harness-archive/SKILL.md +1 -1
  86. package/harness/bundles/java/codex/harness-codebase-map/SKILL.md +1 -1
  87. package/harness/bundles/java/codex/harness-knowledge-ingest/SKILL.md +1 -1
  88. package/harness/bundles/java/codex/harness-knowledge-query/SKILL.md +1 -1
  89. package/harness/bundles/java/codex/harness-package/SKILL.md +1 -1
  90. package/harness/bundles/java/codex/harness-plan/SKILL.md +1 -1
  91. package/harness/bundles/java/codex/harness-review/SKILL.md +1 -1
  92. package/harness/bundles/java/codex/harness-run/SKILL.md +1 -1
  93. package/harness/bundles/java/codex/harness-submit/SKILL.md +1 -1
  94. package/harness/bundles/java/codex/harness-sync/SKILL.md +15 -22
  95. package/harness/bundles/java/codex/harness-sync/reference.md +22 -11
  96. package/harness/bundles/java/codex/harness-test/SKILL.md +1 -1
  97. package/harness/bundles/java/codex/scripts/harness_events_sync.py +191 -26
  98. package/harness/bundles/java/cursor/.harness-build.json +1 -1
  99. package/harness/bundles/java/cursor/harness-apidoc/SKILL.md +1 -1
  100. package/harness/bundles/java/cursor/harness-archive/SKILL.md +1 -1
  101. package/harness/bundles/java/cursor/harness-codebase-map/SKILL.md +1 -1
  102. package/harness/bundles/java/cursor/harness-knowledge-ingest/SKILL.md +1 -1
  103. package/harness/bundles/java/cursor/harness-knowledge-query/SKILL.md +1 -1
  104. package/harness/bundles/java/cursor/harness-package/SKILL.md +1 -1
  105. package/harness/bundles/java/cursor/harness-plan/SKILL.md +1 -1
  106. package/harness/bundles/java/cursor/harness-review/SKILL.md +1 -1
  107. package/harness/bundles/java/cursor/harness-run/SKILL.md +1 -1
  108. package/harness/bundles/java/cursor/harness-submit/SKILL.md +1 -1
  109. package/harness/bundles/java/cursor/harness-sync/SKILL.md +15 -22
  110. package/harness/bundles/java/cursor/harness-sync/reference.md +22 -11
  111. package/harness/bundles/java/cursor/harness-test/SKILL.md +1 -1
  112. package/harness/bundles/java/cursor/scripts/harness_events_sync.py +191 -26
  113. package/harness/manifests/general/claude-code.json +15 -15
  114. package/harness/manifests/general/codebuddy.json +15 -15
  115. package/harness/manifests/general/codex.json +15 -15
  116. package/harness/manifests/general/cursor.json +15 -15
  117. package/harness/manifests/java/claude-code.json +17 -17
  118. package/harness/manifests/java/codebuddy.json +17 -17
  119. package/harness/manifests/java/codex.json +17 -17
  120. package/harness/manifests/java/cursor.json +17 -17
  121. package/hunter-workflow-family.json +5 -5
  122. package/package.json +1 -1
@@ -2,5 +2,5 @@
2
2
  "schemaVersion": 1,
3
3
  "overlay": "none",
4
4
  "agent": "claude-code",
5
- "coreHash": "a5e0c1d624b5fad6"
5
+ "coreHash": "f13d1bb7177b0294"
6
6
  }
@@ -21,7 +21,7 @@ disallowed-tools:
21
21
  - Bash(awk *)
22
22
  - Bash(curl *)
23
23
  ---
24
- <!-- generated by harness_deploy.py; core=a5e0c1d624b5fad6; overlay=none; agent=claude-code; do not edit -->
24
+ <!-- generated by harness_deploy.py; core=f13d1bb7177b0294; overlay=none; agent=claude-code; do not edit -->
25
25
  # harness-archive — 变更归档
26
26
 
27
27
  ## Purpose
@@ -20,7 +20,7 @@ disallowed-tools:
20
20
  - Bash(awk *)
21
21
  - Bash(curl *)
22
22
  ---
23
- <!-- generated by harness_deploy.py; core=a5e0c1d624b5fad6; overlay=none; agent=claude-code; do not edit -->
23
+ <!-- generated by harness_deploy.py; core=f13d1bb7177b0294; overlay=none; agent=claude-code; do not edit -->
24
24
  # harness-codebase-map — 代码库地图
25
25
 
26
26
  ## Purpose
@@ -8,7 +8,7 @@ disallowed-tools:
8
8
  - Bash(git *)
9
9
  - Bash(curl *)
10
10
  ---
11
- <!-- generated by harness_deploy.py; core=a5e0c1d624b5fad6; overlay=none; agent=claude-code; do not edit -->
11
+ <!-- generated by harness_deploy.py; core=f13d1bb7177b0294; overlay=none; agent=claude-code; do not edit -->
12
12
  # harness-knowledge-ingest
13
13
 
14
14
  知识 ingest 完全由 Hunter Platform 负责。归档完成时,客户端生成一个确定性 ZIP;
@@ -8,7 +8,7 @@ disallowed-tools:
8
8
  - Bash(git *)
9
9
  - Bash(curl *)
10
10
  ---
11
- <!-- generated by harness_deploy.py; core=a5e0c1d624b5fad6; overlay=none; agent=claude-code; do not edit -->
11
+ <!-- generated by harness_deploy.py; core=f13d1bb7177b0294; overlay=none; agent=claude-code; do not edit -->
12
12
  # harness-knowledge-query
13
13
 
14
14
  项目知识以 Hunter Platform 的服务端索引为唯一真源。客户端只提交查询并消费结果:
@@ -21,7 +21,7 @@ disallowed-tools:
21
21
  - Bash(awk *)
22
22
  - Bash(curl *)
23
23
  ---
24
- <!-- generated by harness_deploy.py; core=a5e0c1d624b5fad6; overlay=none; agent=claude-code; do not edit -->
24
+ <!-- generated by harness_deploy.py; core=f13d1bb7177b0294; overlay=none; agent=claude-code; do not edit -->
25
25
  # harness-plan — 需求规划
26
26
 
27
27
  ## Purpose
@@ -22,7 +22,7 @@ disallowed-tools:
22
22
  - Bash(curl *)
23
23
  - Bash(codegraph *)
24
24
  ---
25
- <!-- generated by harness_deploy.py; core=a5e0c1d624b5fad6; overlay=none; agent=claude-code; do not edit -->
25
+ <!-- generated by harness_deploy.py; core=f13d1bb7177b0294; overlay=none; agent=claude-code; do not edit -->
26
26
  # harness-review — 代码审查
27
27
 
28
28
  ## Purpose
@@ -22,7 +22,7 @@ disallowed-tools:
22
22
  - Bash(curl *)
23
23
  - Bash(codegraph *)
24
24
  ---
25
- <!-- generated by harness_deploy.py; core=a5e0c1d624b5fad6; overlay=none; agent=claude-code; do not edit -->
25
+ <!-- generated by harness_deploy.py; core=f13d1bb7177b0294; overlay=none; agent=claude-code; do not edit -->
26
26
  # harness-run — 需求编码
27
27
 
28
28
  ## Purpose
@@ -21,7 +21,7 @@ disallowed-tools:
21
21
  - Bash(awk *)
22
22
  - Bash(curl *)
23
23
  ---
24
- <!-- generated by harness_deploy.py; core=a5e0c1d624b5fad6; overlay=none; agent=claude-code; do not edit -->
24
+ <!-- generated by harness_deploy.py; core=f13d1bb7177b0294; overlay=none; agent=claude-code; do not edit -->
25
25
  # harness-submit — 最终提交(含 worktree 合并)
26
26
 
27
27
  ## Purpose
@@ -19,12 +19,12 @@ disallowed-tools:
19
19
  - Bash(awk *)
20
20
  - Bash(curl *)
21
21
  ---
22
- <!-- generated by harness_deploy.py; core=a5e0c1d624b5fad6; overlay=none; agent=claude-code; do not edit -->
22
+ <!-- generated by harness_deploy.py; core=f13d1bb7177b0294; overlay=none; agent=claude-code; do not edit -->
23
23
  # harness-sync
24
24
 
25
25
  ## Purpose
26
26
 
27
- 通过一个有界入口刷新 Harness Adapter 与元数据、检查远端知识职责和项目指令状态,并输出可验证的组件收据。`sync` 不直接改写 AGENTS/CLAUDE/CODEBUDDY 或规则;文档优化使用远端审计提案。
27
+ 通过一个有界入口刷新 Harness Adapter 与元数据、检查远端知识职责和项目指令状态,并在当前命令输出中给出组件结果。`sync` 是维护命令,不是 change 生命周期阶段:不追加 change 事件、不调用 `events-sync`、不上传运行监控。`sync` 不直接改写 AGENTS/CLAUDE/CODEBUDDY 或规则;文档优化使用远端审计提案。
28
28
 
29
29
  ## Before running
30
30
 
@@ -41,10 +41,7 @@ npx hunter-harness sync --project <项目路径> --profile interactive --progres
41
41
 
42
42
  CLI 负责 Python runtime 解析、Adapter 事务、远端知识职责、文档审计提示、map、指令图、配置来源、change 状态及 CodeGraph 证据汇总。禁止直接调用旧知识 Python 脚本,禁止使用固定 `HEAD~5`,禁止自动全量重建 CodeGraph。
43
43
 
44
- 只读诊断使用 `--check`(`--dry-run` 为兼容别名);它不得写报告、receipt 或投影。
45
- 长阶段的 heartbeat 写 stderr;stdout 只保留紧凑摘要。完整报告写入摘要中的
46
- `reportPath`,并带 `reportSha256`。需要组件级证据时显式加 `--verbose`,不得默认把完整
47
- JSON 报告回显到对话。
44
+ 只读诊断使用 `--check`(`--dry-run` 为兼容别名);它不得写 receipt 或投影。普通模式只允许执行用户请求的 Adapter 事务,不写入 `.harness/runtime/sync/`。长阶段的 heartbeat 写 stderr;stdout 只保留紧凑摘要。需要组件级证据时显式加 `--verbose`;`reportPath` 和 `reportSha256` 固定为 `null`。
48
45
 
49
46
  摘要中的 `remediations[]` 是稳定修复契约。先用对应 `previewCommand` 预览;低风险修复用
50
47
  `--apply safe`,指定修复用 `--fix <id>`。需覆盖受管投影的修复必须同时提供 `--yes`,
@@ -55,17 +52,28 @@ JSON 报告回显到对话。
55
52
 
56
53
  - `OK`:所有可验证组件通过。
57
54
  - `ADVISORY`:例如需要运行中文指令审计提案;退出码仍为 0,且 `sync` 本身没有改写文档。
58
- - `WARN`:存在过期、冲突、待评审或 `UNKNOWN` 证据;按 `reportPath` 中的 `nextAction` 处理。
55
+ - `WARN`:存在过期、冲突、待评审或 `UNKNOWN` 证据;按组件的中文 `nextAction` 处理。
59
56
  - `FAIL`:组件执行失败;不得宣称同步完成。
60
57
  - `BLOCKED`:runtime、项目状态或能力契约阻塞;先修复阻塞条件。
61
58
  - `UNKNOWN`:证据不足,不等于成功,也不触发无界重建。
62
59
 
63
60
  非交互或 CI 使用 `--check --profile general --progress jsonl --json`。交互式运行若报告 `INSTRUCTION_AUDIT_REQUIRED`,运行 `hunter-harness instructions audit`;规则候选只进入提案,永不自动应用。
64
61
 
62
+ ## 交互后续动作
63
+
64
+ 交互式宿主支持选择器时,必须使用**多选复选框**,保持用户熟悉的勾选形式;只显示与本次非 OK 组件对应的选项,再附加“保持现状”。不要改成自由输入或普通编号问答。选项标题和说明优先使用通俗中文,机器原因码只放在技术详情中。
65
+
66
+ - **生成 Codebase Map**:仅在 `codebase-map` 缺失或过期时显示。说明它会扫描项目的技术栈、外部集成、架构、目录结构、编码约定、测试方式和风险,生成 `.harness/codebase/map/` 下的 `STACK.md`、`INTEGRATIONS.md`、`ARCHITECTURE.md`、`STRUCTURE.md`、`CONVENTIONS.md`、`TESTING.md`、`CONCERNS.md`,以及 `map-summary.md` 和 `map-manifest.json`;不会修改源码。
67
+ - **运行指令审计**:仅在 `rules` 需要审计时显示。说明服务端会结合项目类型、现有指令文档、Codebase Map 和近期变更生成中文优化提案,不会直接修改项目文档;用户审阅后才可显式应用。
68
+ - **检查 CodeGraph 后台同步**:仅在 watcher 未验证、停用或存在待同步源码时显示。若索引可用且待同步数为 0,应说明当前查询仍可用,检查 watcher 只是为了确认后续源码能否自动增量更新。
69
+ - **保持现状**:接受本次提示,不执行任何后续动作。
70
+
65
71
  ## Safety
66
72
 
67
73
  同步不得直接修改项目指令文档或规则。配置真源与生成投影存在漂移时只报告,不静默覆盖真源。change 清理先 dry-run,仅对已验证归档收据执行安全清理。知识查询和 ingest 均为远端职责,不创建 `.harness/knowledge`。
68
74
 
75
+ 无论结果如何,`sync` 都不写持久同步报告、不追加 change 事件、不调用 `events-sync`,也不上传监控;组件详情仅存在于本次 stdout。Adapter 事务本身的必要备份和回滚证据不属于 sync 日志,不得因此禁用。
76
+
69
77
  ## P0 执行可信度规则
70
78
 
71
79
  - 命令结果不得靠猜测;普通 Bash 被拒 → 立即改用等价 PowerShell 重试一次
@@ -77,18 +85,3 @@ JSON 报告回显到对话。
77
85
  - sync/ingest 等生成的文档、规则、知识条目、架构说明一律**优先使用中文**撰写(标识符、命令、代码、API 字段名保持原文)
78
86
  - 面向平台展示的标题/摘要/正文默认中文;仅当用户明确要求或目标系统强制时才用英文
79
87
  > 片段:p0-trust
80
-
81
- ## 执行日志
82
-
83
- `events.ndjson` 为唯一事实源(schema_version 3,兼容读取 v1/v2;`note` 承载人类可读摘要);`logs/execution-log.md` 由 `harness_events.py` 渲染,**禁止用 Write/Edit 直接维护**。直接修改的内容会在 `phase.end` 或 finalize 时被完整重建覆盖,属于数据丢失;需要保留的详情必须进入事件 `note`。结构 → [[../protocols/report-pipeline-protocol.md|report-pipeline-protocol]]
84
-
85
- ```powershell
86
- python <skills-root>/scripts/harness_events.py append --change-dir ".harness/changes/<change-name>" --phase <phase> --type phase.start --run-id <phase-run-id> --attempt <n> --note "<触发指令>"
87
- ```
88
-
89
- > **脚本接线**:`harness_events.py append`;`harness_archive.py finalize`;`harness_preflight.py check`;`harness_ledger.py can-reuse`;`harness_service.py ensure/stop`(须 `--files`/`serviceStart.inputFiles`)。JSON 输出按 D13 护栏解读。
90
-
91
- > **Task 4 §6.1 写入契约**:普通 `append` = 加锁 -> 追加一行 -> fsync -> 解锁,**不 load 历史、不渲染**(O(1),跨进程锁 `events.ndjson.lock`,UUID 用完整 `uuid4().hex` 无需去重扫描)。仅 `--type phase.end` append 在追加成功后渲染一次 `execution-log.md`;显式 `harness_events.py render` 随时从完整 events 重建;`harness_archive.py finalize` 在 collect 前强制 render 一次。高频 command append 期间 log 可能滞后,phase 边界保持最新。
92
-
93
- 每个阶段的 `phase.start` 与对应 `phase.end` 必须复用同一 `--run-id` / `--attempt`;阶段结束必须写 `--status OK|WARN|FAIL|BLOCKED`。重试同一阶段时生成新的 run-id 并增加 `--attempt <n>`,不得覆盖或伪装成一次执行。跨工具继续执行时写 `--executor-tool <codex|claude-code|codebuddy|cursor>`,并在接棒事件写 `--handoff-from-tool` / `--handoff-reason`;也可由 `HUNTER_HARNESS_TOOL/AGENT/MODEL/RUN_ID` 环境变量统一注入。
94
- > 片段:logging · phase=`sync`
@@ -46,7 +46,8 @@ npx hunter-harness sync --check --project <项目路径> --profile general --pro
46
46
  ```
47
47
 
48
48
  `--check` 是严格只读检查;`--dry-run` 是兼容别名。两者都不生成持久报告、receipt 或
49
- 投影。每个长阶段通过 stderr 输出受限 heartbeat;stdout 默认只输出:
49
+ 投影。普通模式可以执行明确请求的 Adapter 事务,但同样不写持久同步报告。每个长阶段
50
+ 通过 stderr 输出受限 heartbeat;stdout 默认只输出:
50
51
 
51
52
  ```json
52
53
  {
@@ -59,14 +60,15 @@ npx hunter-harness sync --check --project <项目路径> --profile general --pro
59
60
  "adapterBundleVersions": {}
60
61
  },
61
62
  "remediations": [],
62
- "reportPath": ".harness/runtime/sync/<run-id>/reports/sync-report.json",
63
- "reportSha256": "<sha256>"
63
+ "reportPath": null,
64
+ "reportSha256": null
64
65
  }
65
66
  ```
66
67
 
67
- 只读检查的 `reportPath/reportSha256` `null`。详细报告必须受大小限制,并包含每个组件
68
- `status`、`reasonCode`、`observedAt`、`durationMs`、输入/输出 hash、证据、是否自动
69
- 修复及 `nextAction`;只有显式 `--verbose` 才把组件级详情写到 stdout。
68
+ 所有模式的 `reportPath` 和 `reportSha256` 固定为 `null`。只有显式 `--verbose` 才把每个
69
+ 组件的 `status`、`reasonCode`、`observedAt`、`durationMs`、证据、是否自动修复及
70
+ `nextAction` 写到 stdout。`sync` 不写 `.harness/runtime/sync/`,不追加 change 生命周期
71
+ 事件,不调用 `events-sync`,也不上传运行监控。
70
72
 
71
73
  ### 结构化修复
72
74
 
@@ -97,17 +99,25 @@ npx hunter-harness sync --check --project <项目路径> --profile general --pro
97
99
 
98
100
  ## 5. Git 与 CodeGraph
99
101
 
100
- 增量基线来自上次成功 sync receipt `headCommit`。首次运行或 receipt 不可用时,仅收集当前 HEAD 和有界文件统计;禁止固定 `HEAD~5`。
102
+ 增量基线只使用当前分支的 upstream merge-base;没有 upstream 时仅收集当前 HEAD 和有界文件统计。不得读取旧 `.harness/runtime/sync/last-success.json`,也禁止固定使用 `HEAD~5`。
101
103
 
102
104
  CodeGraph 状态探测优先读取 `codegraph status --json` 的权威 pending 列表;只有该 API
103
105
  不可用时才退回受限文件扫描,并把来源标成 `database-scan` 或 `unverified`。`.agents/`、
104
106
  `.cursor/`、`.claude/` 等 Adapter 投影和 Markdown 文档不计入源码 pending。daemon log
105
- mtime 只写入 `watcherObservedAt`,不能冒充 `indexObservedAt`。只有服务可达、watcher
106
- 启用且权威 `pendingFileCount=0` 时为 `CURRENT`。不要在 sync 内执行全量索引。
107
+ mtime 只写入 `watcherObservedAt`,不能冒充 `indexObservedAt`。服务可达、watcher 已启用
108
+ 且权威 `pendingFileCount=0` 时为 `CURRENT`。如果 API 不可用,但本地索引存在且受限扫描
109
+ 确认 `pendingFileCount=0`,返回 `ADVISORY / INDEX_PRESENT_UNVERIFIED`:索引仍可用于查询,
110
+ 只是无法证明 watcher 正在持续运行。存在待同步源码,或已确认 watcher 停用/服务不可达,
111
+ 才返回 `WARN`。不要在 sync 内执行全量索引。
107
112
 
108
113
  ## 6. Instruction graph
109
114
 
110
- 验证 `AGENTS.md`、`CLAUDE.md`、`CODEBUDDY.md` `.harness/context-index.json` 的引用图:
115
+ 只验证当前已启用 Agent 对应的入口文档及其引用图,而不是固定要求所有 Adapter 文件:
116
+
117
+ - `AGENTS.md` 是共享入口,始终验证。
118
+ - 仅启用 Claude Code 时验证 `CLAUDE.md`。
119
+ - 仅启用 CodeBuddy 时验证 `CODEBUDDY.md`。
120
+ - Codex 和 Cursor 不要求额外生成 `CLAUDE.md` 或 `CODEBUDDY.md`。
111
121
 
112
122
  - Claude 可单向引用 AGENTS,共享约束保持单一真源。
113
123
  - 禁止 AGENTS 反向引用 CLAUDE 形成环。
@@ -156,10 +166,11 @@ npx hunter-harness instructions apply --proposal <proposal.json> --yes --json
156
166
 
157
167
  只有以下条件同时满足才能宣称同步成功:
158
168
 
159
- - stdout 摘要与详细报告 hash 一致;
169
+ - stdout 摘要完整给出全局状态;使用 `--verbose` 时包含组件结果;
160
170
  - 没有 `FAIL` 或 `BLOCKED`;
161
171
  - 所有自动修复均有 post-transaction 证据;
162
172
  - knowledge 组件明确为 remote-only,且未创建或刷新本地索引;
163
173
  - 文档和规则组件未直接改写项目文件;
174
+ - 未写持久 sync 报告、change 事件或监控上传记录;
164
175
  - 未把 `UNKNOWN` 描述成已验证;
165
176
  - 第二次无输入变化的运行不产生投影 churn。
@@ -23,7 +23,7 @@ disallowed-tools:
23
23
  - Bash(node *)
24
24
  - Bash(codegraph *)
25
25
  ---
26
- <!-- generated by harness_deploy.py; core=a5e0c1d624b5fad6; overlay=none; agent=claude-code; do not edit -->
26
+ <!-- generated by harness_deploy.py; core=f13d1bb7177b0294; overlay=none; agent=claude-code; do not edit -->
27
27
  # harness-test — 测试执行
28
28
 
29
29
  ## Purpose
@@ -350,36 +350,97 @@ def _state_dir(change_dir: Path) -> Path:
350
350
  return Path(harness_paths.resolve_state_dir_for_contract(change_dir))
351
351
 
352
352
 
353
- def load_cursor(
353
+ def _cursor_values(path: Path) -> tuple[int, int, bool]:
354
+ if not path.is_file():
355
+ return 0, 0, True
356
+ try:
357
+ data = json.loads(path.read_text(encoding="utf-8"))
358
+ except (OSError, json.JSONDecodeError):
359
+ return 0, 0, False
360
+ raw_acked = data.get("acked_lines") if isinstance(data, dict) else None
361
+ raw_base = data.get("producer_seq_base", 0) if isinstance(data, dict) else None
362
+ acked_valid = (
363
+ not isinstance(raw_acked, bool)
364
+ and isinstance(raw_acked, int)
365
+ and raw_acked >= 0
366
+ )
367
+ base_valid = (
368
+ not isinstance(raw_base, bool)
369
+ and isinstance(raw_base, int)
370
+ and raw_base >= 0
371
+ )
372
+ return (
373
+ raw_acked if acked_valid else 0,
374
+ raw_base if base_valid else 0,
375
+ acked_valid and base_valid,
376
+ )
377
+
378
+
379
+ def _complete_line_count(path: Path) -> int:
380
+ if not path.is_file():
381
+ return 0
382
+ try:
383
+ data = path.read_bytes()
384
+ except OSError:
385
+ return 0
386
+ if not data:
387
+ return 0
388
+ return data.count(b"\n")
389
+
390
+
391
+ def _initial_producer_seq_base(change_dir: Path) -> int:
392
+ """Keep producer sequence monotonic when a legacy stream becomes split-v1."""
393
+ state_dir = _state_dir(change_dir).resolve()
394
+ contract_dir = change_dir.resolve()
395
+ if state_dir == contract_dir:
396
+ return 0
397
+ legacy_acked, legacy_base, _ = _cursor_values(
398
+ contract_dir / "meta" / "events-sync-cursor.json"
399
+ )
400
+ legacy_lines = _complete_line_count(contract_dir / "events.ndjson")
401
+ return legacy_base + max(legacy_acked, legacy_lines)
402
+
403
+
404
+ def load_cursor_state(
354
405
  change_dir: Path,
355
406
  *,
356
407
  maximum_lines: int | None = None,
357
408
  repair: bool = False,
358
- ) -> int:
409
+ ) -> dict[str, int]:
359
410
  path = cursor_path(change_dir)
360
- if not path.is_file():
361
- return 0
362
- valid = True
363
- try:
364
- data = json.loads(path.read_text(encoding="utf-8"))
365
- except (OSError, json.JSONDecodeError):
366
- data = None
367
- valid = False
368
- raw = data.get("acked_lines") if isinstance(data, dict) else None
369
- if isinstance(raw, bool) or not isinstance(raw, int) or raw < 0:
370
- valid = False
371
- value = 0
372
- else:
373
- value = raw
411
+ exists = path.is_file()
412
+ acked, producer_seq_base, valid = _cursor_values(path)
413
+ if not exists:
414
+ producer_seq_base = _initial_producer_seq_base(change_dir)
374
415
  if maximum_lines is not None:
375
416
  if maximum_lines < 0:
376
417
  raise ValueError("maximum_lines must be non-negative")
377
- if value > maximum_lines:
418
+ if acked > maximum_lines:
419
+ acked = 0
378
420
  valid = False
379
- value = 0
380
- if not valid and repair:
381
- save_cursor(change_dir, 0)
382
- return value
421
+ if (not valid or not exists) and repair:
422
+ save_cursor(
423
+ change_dir,
424
+ acked,
425
+ producer_seq_base=producer_seq_base,
426
+ )
427
+ return {
428
+ "acked_lines": acked,
429
+ "producer_seq_base": producer_seq_base,
430
+ }
431
+
432
+
433
+ def load_cursor(
434
+ change_dir: Path,
435
+ *,
436
+ maximum_lines: int | None = None,
437
+ repair: bool = False,
438
+ ) -> int:
439
+ return load_cursor_state(
440
+ change_dir,
441
+ maximum_lines=maximum_lines,
442
+ repair=repair,
443
+ )["acked_lines"]
383
444
 
384
445
 
385
446
  def _fsync_parent(path: Path) -> None:
@@ -392,14 +453,31 @@ def _fsync_parent(path: Path) -> None:
392
453
  os.close(descriptor)
393
454
 
394
455
 
395
- def save_cursor(change_dir: Path, acked_lines: int) -> None:
456
+ def save_cursor(
457
+ change_dir: Path,
458
+ acked_lines: int,
459
+ *,
460
+ producer_seq_base: int | None = None,
461
+ ) -> None:
396
462
  if isinstance(acked_lines, bool) or not isinstance(acked_lines, int) or acked_lines < 0:
397
463
  raise ValueError("acked_lines must be a non-negative integer")
398
464
  path = cursor_path(change_dir)
465
+ if producer_seq_base is None:
466
+ _, producer_seq_base, _ = _cursor_values(path)
467
+ if (
468
+ isinstance(producer_seq_base, bool)
469
+ or not isinstance(producer_seq_base, int)
470
+ or producer_seq_base < 0
471
+ ):
472
+ raise ValueError("producer_seq_base must be a non-negative integer")
399
473
  path.parent.mkdir(parents=True, exist_ok=True)
400
474
  temporary = path.with_name(f".{path.name}.{uuid.uuid4().hex}.tmp")
401
475
  encoded = (
402
- json.dumps({"schemaVersion": 1, "acked_lines": acked_lines}, indent=2) + "\n"
476
+ json.dumps({
477
+ "schemaVersion": 2,
478
+ "acked_lines": acked_lines,
479
+ "producer_seq_base": producer_seq_base,
480
+ }, indent=2) + "\n"
403
481
  ).encode("utf-8")
404
482
  descriptor = os.open(
405
483
  str(temporary), os.O_CREAT | os.O_EXCL | os.O_WRONLY, 0o600
@@ -489,6 +567,85 @@ def _event_lines_snapshot(events_file: Path) -> list[str]:
489
567
  return data.decode("utf-8-sig").splitlines()
490
568
 
491
569
 
570
+ def _complete_stream_bytes(path: Path) -> bytes:
571
+ data = path.read_bytes() if path.is_file() else b""
572
+ if data and not data.endswith(b"\n"):
573
+ last_newline = data.rfind(b"\n")
574
+ data = b"" if last_newline < 0 else data[:last_newline + 1]
575
+ return data
576
+
577
+
578
+ def _atomic_replace_bytes(path: Path, data: bytes) -> None:
579
+ path.parent.mkdir(parents=True, exist_ok=True)
580
+ temporary = path.with_name(f".{path.name}.{uuid.uuid4().hex}.tmp")
581
+ descriptor = os.open(
582
+ str(temporary), os.O_CREAT | os.O_EXCL | os.O_WRONLY, 0o600
583
+ )
584
+ try:
585
+ with os.fdopen(descriptor, "wb") as stream:
586
+ descriptor = -1
587
+ stream.write(data)
588
+ stream.flush()
589
+ os.fsync(stream.fileno())
590
+ os.replace(temporary, path)
591
+ _fsync_parent(path)
592
+ finally:
593
+ if descriptor >= 0:
594
+ os.close(descriptor)
595
+ try:
596
+ temporary.unlink()
597
+ except FileNotFoundError:
598
+ pass
599
+
600
+
601
+ def _migrate_pending_legacy_stream(change_dir: Path) -> None:
602
+ """Prefix unsent colocated events before the first split-v1 state upload."""
603
+ contract_dir = change_dir.resolve()
604
+ state_dir = _state_dir(change_dir).resolve()
605
+ legacy_events = contract_dir / "events.ndjson"
606
+ target_events = state_dir / "events.ndjson"
607
+ target_cursor = state_dir / "meta" / "events-sync-cursor.json"
608
+ if state_dir == contract_dir or not legacy_events.is_file() or target_cursor.exists():
609
+ return
610
+ legacy_cursor = contract_dir / "meta" / "events-sync-cursor.json"
611
+ legacy_acked, _, _ = _cursor_values(legacy_cursor)
612
+ legacy_line_count = _complete_line_count(legacy_events)
613
+ if legacy_acked >= legacy_line_count:
614
+ return
615
+
616
+ locks = [
617
+ _CrossProcessLock(path.with_name(path.name + ".lock"))
618
+ for path in sorted((legacy_events, target_events), key=lambda item: str(item).casefold())
619
+ ]
620
+ for lock in locks:
621
+ lock.acquire(blocking=True)
622
+ try:
623
+ if target_cursor.exists() or not legacy_events.is_file():
624
+ return
625
+ legacy_data = _complete_stream_bytes(legacy_events)
626
+ target_data = _complete_stream_bytes(target_events)
627
+ if target_data.startswith(legacy_data):
628
+ merged = target_data
629
+ else:
630
+ merged = legacy_data + target_data
631
+ _atomic_replace_bytes(target_events, merged)
632
+
633
+ target_cursor.parent.mkdir(parents=True, exist_ok=True)
634
+ if legacy_cursor.is_file():
635
+ os.replace(legacy_cursor, target_cursor)
636
+ else:
637
+ save_cursor(change_dir, 0, producer_seq_base=0)
638
+
639
+ legacy_quarantine = contract_dir / "meta" / "events-sync-quarantine.ndjson"
640
+ target_quarantine = state_dir / "meta" / "events-sync-quarantine.ndjson"
641
+ if legacy_quarantine.is_file() and not target_quarantine.exists():
642
+ os.replace(legacy_quarantine, target_quarantine)
643
+ legacy_events.unlink()
644
+ finally:
645
+ for lock in reversed(locks):
646
+ lock.release()
647
+
648
+
492
649
  def sync_change(
493
650
  project: Path,
494
651
  change_dir: Path,
@@ -525,6 +682,7 @@ def sync_change(
525
682
  if heartbeat_only:
526
683
  return {"ok": True, "heartbeat": True, "run_id": rid, "change_key": resolved_key}
527
684
 
685
+ _migrate_pending_legacy_stream(change_dir)
528
686
  events_file = _state_dir(change_dir) / "events.ndjson"
529
687
  if not events_file.is_file():
530
688
  return {
@@ -536,11 +694,13 @@ def sync_change(
536
694
  }
537
695
 
538
696
  lines = _event_lines_snapshot(events_file)
539
- acked = load_cursor(
697
+ cursor = load_cursor_state(
540
698
  change_dir,
541
699
  maximum_lines=len(lines),
542
700
  repair=True,
543
701
  )
702
+ acked = cursor["acked_lines"]
703
+ producer_seq_base = cursor["producer_seq_base"]
544
704
  pending_raw = lines[acked:]
545
705
  phase_plan = _workflow_plan(change_dir)
546
706
  uploaded = 0
@@ -571,7 +731,7 @@ def sync_change(
571
731
  event_id = str(event.get("id") or f"{resolved_key}:{line_number}")
572
732
  item: dict[str, Any] = {
573
733
  "event_id": event_id,
574
- "producer_seq": line_number,
734
+ "producer_seq": producer_seq_base + line_number,
575
735
  "event_type": str(event.get("type") or "unknown"),
576
736
  "occurred_at": str(event.get("timestamp") or event.get("ts") or client_time),
577
737
  "payload": _event_payload(event, phase_plan),
@@ -664,7 +824,11 @@ def sync_change(
664
824
  status="local_invalid",
665
825
  )
666
826
  quarantined += 1
667
- save_cursor(change_dir, acked + chunk_start + len(raw_chunk))
827
+ save_cursor(
828
+ change_dir,
829
+ acked + chunk_start + len(raw_chunk),
830
+ producer_seq_base=producer_seq_base,
831
+ )
668
832
 
669
833
  return {
670
834
  "ok": True,
@@ -674,6 +838,7 @@ def sync_change(
674
838
  "quarantined": quarantined,
675
839
  "batch_size": min(MAX_BATCH_SIZE, len(pending_raw)),
676
840
  "acked_lines": len(lines),
841
+ "producer_seq_base": producer_seq_base,
677
842
  "server": last_result,
678
843
  }
679
844
 
@@ -2,5 +2,5 @@
2
2
  "schemaVersion": 1,
3
3
  "overlay": "none",
4
4
  "agent": "codebuddy",
5
- "coreHash": "a5e0c1d624b5fad6"
5
+ "coreHash": "f13d1bb7177b0294"
6
6
  }
@@ -3,7 +3,7 @@ name: harness-archive
3
3
  description: 归档所有变更产出(计划/测试报告/审查/SQL/API文档)到 .harness/archive/,含归档元数据。仅当用户显式调用
4
4
  /harness-archive 时使用;不得被其他阶段 skill 自动接续触发。
5
5
  ---
6
- <!-- generated by harness_deploy.py; core=a5e0c1d624b5fad6; overlay=none; agent=codebuddy; do not edit -->
6
+ <!-- generated by harness_deploy.py; core=f13d1bb7177b0294; overlay=none; agent=codebuddy; do not edit -->
7
7
  # harness-archive — 变更归档
8
8
 
9
9
  ## Purpose
@@ -3,7 +3,7 @@ name: harness-codebase-map
3
3
  description: 分析现有代码库并生成 .harness/codebase/map/
4
4
  下的结构化代码库地图。用于陌生项目理解、重大改造前梳理、harness-sync 提示 map 过期后的人工刷新。
5
5
  ---
6
- <!-- generated by harness_deploy.py; core=a5e0c1d624b5fad6; overlay=none; agent=codebuddy; do not edit -->
6
+ <!-- generated by harness_deploy.py; core=f13d1bb7177b0294; overlay=none; agent=codebuddy; do not edit -->
7
7
  # harness-codebase-map — 代码库地图
8
8
 
9
9
  ## Purpose
@@ -2,7 +2,7 @@
2
2
  name: harness-knowledge-ingest
3
3
  description: 确认归档 ZIP 已上传并由 Hunter Platform 在服务端解包、校验和入库。客户端不再构建或维护本地知识索引。
4
4
  ---
5
- <!-- generated by harness_deploy.py; core=a5e0c1d624b5fad6; overlay=none; agent=codebuddy; do not edit -->
5
+ <!-- generated by harness_deploy.py; core=f13d1bb7177b0294; overlay=none; agent=codebuddy; do not edit -->
6
6
  # harness-knowledge-ingest
7
7
 
8
8
  知识 ingest 完全由 Hunter Platform 负责。归档完成时,客户端生成一个确定性 ZIP;
@@ -2,7 +2,7 @@
2
2
  name: harness-knowledge-query
3
3
  description: 在规划、实现或排查前,通过 hunter-harness CLI 查询远端项目知识库。远端不可用时直接报告不可用,不建立本地索引或离线回退。
4
4
  ---
5
- <!-- generated by harness_deploy.py; core=a5e0c1d624b5fad6; overlay=none; agent=codebuddy; do not edit -->
5
+ <!-- generated by harness_deploy.py; core=f13d1bb7177b0294; overlay=none; agent=codebuddy; do not edit -->
6
6
  # harness-knowledge-query
7
7
 
8
8
  项目知识以 Hunter Platform 的服务端索引为唯一真源。客户端只提交查询并消费结果:
@@ -3,7 +3,7 @@ name: harness-plan
3
3
  description: 将需求转化为设计文档+实施计划+完整测试场景表,必须在编码前完成。仅当用户显式调用 /harness-plan 或明确要求进入
4
4
  Harness 规划阶段时使用;不得因用户描述需求就自动触发。
5
5
  ---
6
- <!-- generated by harness_deploy.py; core=a5e0c1d624b5fad6; overlay=none; agent=codebuddy; do not edit -->
6
+ <!-- generated by harness_deploy.py; core=f13d1bb7177b0294; overlay=none; agent=codebuddy; do not edit -->
7
7
  # harness-plan — 需求规划
8
8
 
9
9
  ## Purpose
@@ -4,7 +4,7 @@ description: 6维度代码审查(架构/安全/规范/兼容/测试/性能)
4
4
  .harness/context-index.json)和测试场景表,在隔离上下文运行。仅当用户显式调用 /harness-review 时使用;不得在
5
5
  test 结束后自动接续执行。
6
6
  ---
7
- <!-- generated by harness_deploy.py; core=a5e0c1d624b5fad6; overlay=none; agent=codebuddy; do not edit -->
7
+ <!-- generated by harness_deploy.py; core=f13d1bb7177b0294; overlay=none; agent=codebuddy; do not edit -->
8
8
  # harness-review — 代码审查
9
9
 
10
10
  ## Purpose
@@ -3,7 +3,7 @@ name: harness-run
3
3
  description: 按变更簇执行 TDD 编码循环(RED→GREEN→REFACTOR→编译验证),逐变更簇实现计划中的任务。仅当用户显式调用
4
4
  /harness-run 时使用;不得因用户提到编码/实现就自动触发,也不得被其他阶段 skill 自动接续。
5
5
  ---
6
- <!-- generated by harness_deploy.py; core=a5e0c1d624b5fad6; overlay=none; agent=codebuddy; do not edit -->
6
+ <!-- generated by harness_deploy.py; core=f13d1bb7177b0294; overlay=none; agent=codebuddy; do not edit -->
7
7
  # harness-run — 需求编码
8
8
 
9
9
  ## Purpose
@@ -3,7 +3,7 @@ name: harness-submit
3
3
  description: 最终提交封装:验证→中文 commit→提交/推送;worktree 模式含 --no-ff 合并回主分支。仅当用户显式调用
4
4
  /harness-submit(或 /harness-merge 重入合并段)时使用;用户口头说'提交/commit/push'时必须先确认,不得自动触发。
5
5
  ---
6
- <!-- generated by harness_deploy.py; core=a5e0c1d624b5fad6; overlay=none; agent=codebuddy; do not edit -->
6
+ <!-- generated by harness_deploy.py; core=f13d1bb7177b0294; overlay=none; agent=codebuddy; do not edit -->
7
7
  # harness-submit — 最终提交(含 worktree 合并)
8
8
 
9
9
  ## Purpose