@hunter-harness/workflow-harness 0.3.1 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/harness/bundles/general/claude-code/.harness-build.json +1 -1
- package/harness/bundles/general/claude-code/harness-archive/SKILL.md +1 -1
- package/harness/bundles/general/claude-code/harness-archive/reference.md +30 -1
- package/harness/bundles/general/claude-code/harness-archive/templates/summary-data-template.json +1 -0
- package/harness/bundles/general/claude-code/harness-codebase-map/SKILL.md +1 -1
- package/harness/bundles/general/claude-code/harness-execute/SKILL.md +1 -1
- package/harness/bundles/general/claude-code/harness-knowledge-ingest/SKILL.md +1 -1
- package/harness/bundles/general/claude-code/harness-knowledge-query/SKILL.md +1 -1
- package/harness/bundles/general/claude-code/harness-plan/SKILL.md +1 -1
- package/harness/bundles/general/claude-code/harness-pull/SKILL.md +1 -1
- package/harness/bundles/general/claude-code/harness-push/SKILL.md +1 -1
- package/harness/bundles/general/claude-code/harness-review/SKILL.md +1 -1
- package/harness/bundles/general/claude-code/harness-run/SKILL.md +1 -1
- package/harness/bundles/general/claude-code/harness-submit/SKILL.md +1 -1
- package/harness/bundles/general/claude-code/harness-sync/SKILL.md +1 -1
- package/harness/bundles/general/claude-code/harness-test/SKILL.md +1 -1
- package/harness/bundles/general/claude-code/scripts/harness_archive.py +117 -2
- package/harness/bundles/general/claude-code/scripts/harness_knowledge_candidates.py +140 -4
- package/harness/bundles/general/codebuddy/.harness-build.json +1 -1
- package/harness/bundles/general/codebuddy/harness-archive/SKILL.md +1 -1
- package/harness/bundles/general/codebuddy/harness-archive/reference.md +30 -1
- package/harness/bundles/general/codebuddy/harness-archive/templates/summary-data-template.json +1 -0
- package/harness/bundles/general/codebuddy/harness-codebase-map/SKILL.md +1 -1
- package/harness/bundles/general/codebuddy/harness-execute/SKILL.md +1 -1
- package/harness/bundles/general/codebuddy/harness-knowledge-ingest/SKILL.md +1 -1
- package/harness/bundles/general/codebuddy/harness-knowledge-query/SKILL.md +1 -1
- package/harness/bundles/general/codebuddy/harness-plan/SKILL.md +1 -1
- package/harness/bundles/general/codebuddy/harness-pull/SKILL.md +1 -1
- package/harness/bundles/general/codebuddy/harness-push/SKILL.md +1 -1
- package/harness/bundles/general/codebuddy/harness-review/SKILL.md +1 -1
- package/harness/bundles/general/codebuddy/harness-run/SKILL.md +1 -1
- package/harness/bundles/general/codebuddy/harness-submit/SKILL.md +1 -1
- package/harness/bundles/general/codebuddy/harness-sync/SKILL.md +1 -1
- package/harness/bundles/general/codebuddy/harness-test/SKILL.md +1 -1
- package/harness/bundles/general/codebuddy/scripts/harness_archive.py +117 -2
- package/harness/bundles/general/codebuddy/scripts/harness_knowledge_candidates.py +140 -4
- package/harness/bundles/general/codex/.harness-build.json +1 -1
- package/harness/bundles/general/codex/harness-archive/SKILL.md +1 -1
- package/harness/bundles/general/codex/harness-archive/reference.md +30 -1
- package/harness/bundles/general/codex/harness-archive/templates/summary-data-template.json +1 -0
- package/harness/bundles/general/codex/harness-codebase-map/SKILL.md +1 -1
- package/harness/bundles/general/codex/harness-execute/SKILL.md +1 -1
- package/harness/bundles/general/codex/harness-knowledge-ingest/SKILL.md +1 -1
- package/harness/bundles/general/codex/harness-knowledge-query/SKILL.md +1 -1
- package/harness/bundles/general/codex/harness-plan/SKILL.md +1 -1
- package/harness/bundles/general/codex/harness-pull/SKILL.md +1 -1
- package/harness/bundles/general/codex/harness-push/SKILL.md +1 -1
- package/harness/bundles/general/codex/harness-review/SKILL.md +1 -1
- package/harness/bundles/general/codex/harness-run/SKILL.md +1 -1
- package/harness/bundles/general/codex/harness-submit/SKILL.md +1 -1
- package/harness/bundles/general/codex/harness-sync/SKILL.md +1 -1
- package/harness/bundles/general/codex/harness-test/SKILL.md +1 -1
- package/harness/bundles/general/codex/scripts/harness_archive.py +117 -2
- package/harness/bundles/general/codex/scripts/harness_knowledge_candidates.py +140 -4
- package/harness/bundles/general/cursor/.harness-build.json +1 -1
- package/harness/bundles/general/cursor/harness-archive/SKILL.md +1 -1
- package/harness/bundles/general/cursor/harness-archive/reference.md +30 -1
- package/harness/bundles/general/cursor/harness-archive/templates/summary-data-template.json +1 -0
- package/harness/bundles/general/cursor/harness-codebase-map/SKILL.md +1 -1
- package/harness/bundles/general/cursor/harness-execute/SKILL.md +1 -1
- package/harness/bundles/general/cursor/harness-knowledge-ingest/SKILL.md +1 -1
- package/harness/bundles/general/cursor/harness-knowledge-query/SKILL.md +1 -1
- package/harness/bundles/general/cursor/harness-plan/SKILL.md +1 -1
- package/harness/bundles/general/cursor/harness-pull/SKILL.md +1 -1
- package/harness/bundles/general/cursor/harness-push/SKILL.md +1 -1
- package/harness/bundles/general/cursor/harness-review/SKILL.md +1 -1
- package/harness/bundles/general/cursor/harness-run/SKILL.md +1 -1
- package/harness/bundles/general/cursor/harness-submit/SKILL.md +1 -1
- package/harness/bundles/general/cursor/harness-sync/SKILL.md +1 -1
- package/harness/bundles/general/cursor/harness-test/SKILL.md +1 -1
- package/harness/bundles/general/cursor/scripts/harness_archive.py +117 -2
- package/harness/bundles/general/cursor/scripts/harness_knowledge_candidates.py +140 -4
- package/harness/bundles/java/claude-code/.harness-build.json +1 -1
- package/harness/bundles/java/claude-code/harness-apidoc/SKILL.md +1 -1
- package/harness/bundles/java/claude-code/harness-archive/SKILL.md +1 -1
- package/harness/bundles/java/claude-code/harness-archive/reference.md +30 -1
- package/harness/bundles/java/claude-code/harness-archive/templates/summary-data-template.json +1 -0
- package/harness/bundles/java/claude-code/harness-codebase-map/SKILL.md +1 -1
- package/harness/bundles/java/claude-code/harness-execute/SKILL.md +1 -1
- package/harness/bundles/java/claude-code/harness-knowledge-ingest/SKILL.md +1 -1
- package/harness/bundles/java/claude-code/harness-knowledge-query/SKILL.md +1 -1
- package/harness/bundles/java/claude-code/harness-package/SKILL.md +1 -1
- package/harness/bundles/java/claude-code/harness-plan/SKILL.md +1 -1
- package/harness/bundles/java/claude-code/harness-pull/SKILL.md +1 -1
- package/harness/bundles/java/claude-code/harness-push/SKILL.md +1 -1
- package/harness/bundles/java/claude-code/harness-review/SKILL.md +1 -1
- package/harness/bundles/java/claude-code/harness-run/SKILL.md +1 -1
- package/harness/bundles/java/claude-code/harness-submit/SKILL.md +1 -1
- package/harness/bundles/java/claude-code/harness-sync/SKILL.md +1 -1
- package/harness/bundles/java/claude-code/harness-test/SKILL.md +1 -1
- package/harness/bundles/java/claude-code/scripts/harness_archive.py +117 -2
- package/harness/bundles/java/claude-code/scripts/harness_knowledge_candidates.py +140 -4
- package/harness/bundles/java/codebuddy/.harness-build.json +1 -1
- package/harness/bundles/java/codebuddy/harness-apidoc/SKILL.md +1 -1
- package/harness/bundles/java/codebuddy/harness-archive/SKILL.md +1 -1
- package/harness/bundles/java/codebuddy/harness-archive/reference.md +30 -1
- package/harness/bundles/java/codebuddy/harness-archive/templates/summary-data-template.json +1 -0
- package/harness/bundles/java/codebuddy/harness-codebase-map/SKILL.md +1 -1
- package/harness/bundles/java/codebuddy/harness-execute/SKILL.md +1 -1
- package/harness/bundles/java/codebuddy/harness-knowledge-ingest/SKILL.md +1 -1
- package/harness/bundles/java/codebuddy/harness-knowledge-query/SKILL.md +1 -1
- package/harness/bundles/java/codebuddy/harness-package/SKILL.md +1 -1
- package/harness/bundles/java/codebuddy/harness-plan/SKILL.md +1 -1
- package/harness/bundles/java/codebuddy/harness-pull/SKILL.md +1 -1
- package/harness/bundles/java/codebuddy/harness-push/SKILL.md +1 -1
- package/harness/bundles/java/codebuddy/harness-review/SKILL.md +1 -1
- package/harness/bundles/java/codebuddy/harness-run/SKILL.md +1 -1
- package/harness/bundles/java/codebuddy/harness-submit/SKILL.md +1 -1
- package/harness/bundles/java/codebuddy/harness-sync/SKILL.md +1 -1
- package/harness/bundles/java/codebuddy/harness-test/SKILL.md +1 -1
- package/harness/bundles/java/codebuddy/scripts/harness_archive.py +117 -2
- package/harness/bundles/java/codebuddy/scripts/harness_knowledge_candidates.py +140 -4
- package/harness/bundles/java/codex/.harness-build.json +1 -1
- package/harness/bundles/java/codex/harness-apidoc/SKILL.md +1 -1
- package/harness/bundles/java/codex/harness-archive/SKILL.md +1 -1
- package/harness/bundles/java/codex/harness-archive/reference.md +30 -1
- package/harness/bundles/java/codex/harness-archive/templates/summary-data-template.json +1 -0
- package/harness/bundles/java/codex/harness-codebase-map/SKILL.md +1 -1
- package/harness/bundles/java/codex/harness-execute/SKILL.md +1 -1
- package/harness/bundles/java/codex/harness-knowledge-ingest/SKILL.md +1 -1
- package/harness/bundles/java/codex/harness-knowledge-query/SKILL.md +1 -1
- package/harness/bundles/java/codex/harness-package/SKILL.md +1 -1
- package/harness/bundles/java/codex/harness-plan/SKILL.md +1 -1
- package/harness/bundles/java/codex/harness-pull/SKILL.md +1 -1
- package/harness/bundles/java/codex/harness-push/SKILL.md +1 -1
- package/harness/bundles/java/codex/harness-review/SKILL.md +1 -1
- package/harness/bundles/java/codex/harness-run/SKILL.md +1 -1
- package/harness/bundles/java/codex/harness-submit/SKILL.md +1 -1
- package/harness/bundles/java/codex/harness-sync/SKILL.md +1 -1
- package/harness/bundles/java/codex/harness-test/SKILL.md +1 -1
- package/harness/bundles/java/codex/scripts/harness_archive.py +117 -2
- package/harness/bundles/java/codex/scripts/harness_knowledge_candidates.py +140 -4
- package/harness/bundles/java/cursor/.harness-build.json +1 -1
- package/harness/bundles/java/cursor/harness-apidoc/SKILL.md +1 -1
- package/harness/bundles/java/cursor/harness-archive/SKILL.md +1 -1
- package/harness/bundles/java/cursor/harness-archive/reference.md +30 -1
- package/harness/bundles/java/cursor/harness-archive/templates/summary-data-template.json +1 -0
- package/harness/bundles/java/cursor/harness-codebase-map/SKILL.md +1 -1
- package/harness/bundles/java/cursor/harness-execute/SKILL.md +1 -1
- package/harness/bundles/java/cursor/harness-knowledge-ingest/SKILL.md +1 -1
- package/harness/bundles/java/cursor/harness-knowledge-query/SKILL.md +1 -1
- package/harness/bundles/java/cursor/harness-package/SKILL.md +1 -1
- package/harness/bundles/java/cursor/harness-plan/SKILL.md +1 -1
- package/harness/bundles/java/cursor/harness-pull/SKILL.md +1 -1
- package/harness/bundles/java/cursor/harness-push/SKILL.md +1 -1
- package/harness/bundles/java/cursor/harness-review/SKILL.md +1 -1
- package/harness/bundles/java/cursor/harness-run/SKILL.md +1 -1
- package/harness/bundles/java/cursor/harness-submit/SKILL.md +1 -1
- package/harness/bundles/java/cursor/harness-sync/SKILL.md +1 -1
- package/harness/bundles/java/cursor/harness-test/SKILL.md +1 -1
- package/harness/bundles/java/cursor/scripts/harness_archive.py +117 -2
- package/harness/bundles/java/cursor/scripts/harness_knowledge_candidates.py +140 -4
- package/harness/manifests/general/claude-code.json +18 -18
- package/harness/manifests/general/codebuddy.json +18 -18
- package/harness/manifests/general/codex.json +18 -18
- package/harness/manifests/general/cursor.json +18 -18
- package/harness/manifests/java/claude-code.json +20 -20
- package/harness/manifests/java/codebuddy.json +20 -20
- package/harness/manifests/java/codex.json +20 -20
- package/harness/manifests/java/cursor.json +20 -20
- package/hunter-workflow-family.json +2 -2
- package/package.json +1 -1
|
@@ -21,7 +21,7 @@ disallowed-tools:
|
|
|
21
21
|
- Bash(awk *)
|
|
22
22
|
- Bash(curl *)
|
|
23
23
|
---
|
|
24
|
-
<!-- generated by harness_deploy.py; core=
|
|
24
|
+
<!-- generated by harness_deploy.py; core=d6fd3fef72cfa62b; overlay=none; agent=claude-code; do not edit -->
|
|
25
25
|
# harness-archive — 变更归档
|
|
26
26
|
|
|
27
27
|
## Purpose
|
|
@@ -67,10 +67,39 @@ powershell.exe -NoProfile -ExecutionPolicy Bypass -File "harness-skills/harness-
|
|
|
67
67
|
- `changedFiles`:path/summary/insertions/deletions —— 来自 `git diff --numstat <base>..<head>`;
|
|
68
68
|
- `reviewSummary`:red/yellow + redFixed/redConfirmed/yellowFixed/yellowDeferred 修复进度;
|
|
69
69
|
- `maintenanceNotes`:给后续维护者看的结论;
|
|
70
|
-
- `knownRisks
|
|
70
|
+
- `knownRisks`:剩余风险或人工确认项;
|
|
71
|
+
- `decisions`:已采纳的设计决策/需求/API 契约 —— 来自 `evidence/decisions.json`(见下节),生成器只做校验与透传,不从 markdown 提取。
|
|
71
72
|
|
|
72
73
|
报告必须突出业务目标和维护者结论。所有统计数字只能来自 events、summary-data、ledger 或 manifest,不得手写另一套。历史 archive 没有 `events.ndjson` 时,允许从 ledger/log/manifest 回放,并在 `reportPipeline.sources` 中记录来源。
|
|
73
74
|
|
|
75
|
+
## evidence/decisions.json(可选,知识沉淀入口)
|
|
76
|
+
|
|
77
|
+
重要设计决策、长期约束、API 契约要进入平台知识库的唯一通道:在 plan/execute/review 阶段由人或 agent 把**已采纳**的结论写入 `.harness/changes/<change>/evidence/decisions.json`(schema_version 1):
|
|
78
|
+
|
|
79
|
+
```json
|
|
80
|
+
{
|
|
81
|
+
"schema_version": 1,
|
|
82
|
+
"decisions": [
|
|
83
|
+
{
|
|
84
|
+
"id": "D-001",
|
|
85
|
+
"title": "一句话结论(必填,≤500 字)",
|
|
86
|
+
"rationale": "为什么(可选,≤4000 字)",
|
|
87
|
+
"entry_type": "decision | requirement | api-contract",
|
|
88
|
+
"status": "adopted | proposed | rejected | superseded",
|
|
89
|
+
"path": "docs/design/x.md(可选,相对路径,溯源用)",
|
|
90
|
+
"line": 42,
|
|
91
|
+
"keywords": ["可选,≤32 个"],
|
|
92
|
+
"source": "plan | review | manual | archive"
|
|
93
|
+
}
|
|
94
|
+
]
|
|
95
|
+
}
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
- 只有 `status: "adopted"` 的记录会成为知识候选(`entry_type` 原样落到知识条目上);其余状态保留在 summary 里做记录。
|
|
99
|
+
- 与 reviewFindings 的裁决门槛同源:**筛选必须在上游发生**——不要把讨论过程原文倒进来,归档时不做任何 LLM 提取。
|
|
100
|
+
- 不合 schema 的记录会被丢弃并在 `maintenanceNotes` 留一条计数说明;丢弃不无声。
|
|
101
|
+
- 未裁决(OPEN/UNKNOWN)的 RED/YELLOW findings 若导致候选为 0,finalize 会显式告警提示先裁决再 republish。
|
|
102
|
+
|
|
74
103
|
## 平台展示与数据校验
|
|
75
104
|
|
|
76
105
|
平台监控直接读取 `reports/final/summary-data.json`,归档阶段不再生成本地 HTML 报告,也不得由模型临场维护第二份展示数据。
|
|
@@ -20,7 +20,7 @@ disallowed-tools:
|
|
|
20
20
|
- Bash(awk *)
|
|
21
21
|
- Bash(curl *)
|
|
22
22
|
---
|
|
23
|
-
<!-- generated by harness_deploy.py; core=
|
|
23
|
+
<!-- generated by harness_deploy.py; core=d6fd3fef72cfa62b; overlay=none; agent=claude-code; do not edit -->
|
|
24
24
|
# harness-codebase-map — 代码库地图
|
|
25
25
|
|
|
26
26
|
## Purpose
|
|
@@ -23,7 +23,7 @@ disallowed-tools:
|
|
|
23
23
|
- Bash(node *)
|
|
24
24
|
- Bash(codegraph *)
|
|
25
25
|
---
|
|
26
|
-
<!-- generated by harness_deploy.py; core=
|
|
26
|
+
<!-- generated by harness_deploy.py; core=d6fd3fef72cfa62b; overlay=none; agent=claude-code; do not edit -->
|
|
27
27
|
# harness-execute — 执行(编码 + 验证)
|
|
28
28
|
|
|
29
29
|
> 2026-08 阶段合并:原 `run`(编码)与 `test`(验证)合并为单一 `execute` 阶段。
|
|
@@ -8,7 +8,7 @@ disallowed-tools:
|
|
|
8
8
|
- Bash(git *)
|
|
9
9
|
- Bash(curl *)
|
|
10
10
|
---
|
|
11
|
-
<!-- generated by harness_deploy.py; core=
|
|
11
|
+
<!-- generated by harness_deploy.py; core=d6fd3fef72cfa62b; 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=
|
|
11
|
+
<!-- generated by harness_deploy.py; core=d6fd3fef72cfa62b; 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=
|
|
24
|
+
<!-- generated by harness_deploy.py; core=d6fd3fef72cfa62b; overlay=none; agent=claude-code; do not edit -->
|
|
25
25
|
# harness-plan — 需求规划
|
|
26
26
|
|
|
27
27
|
## Purpose
|
|
@@ -21,7 +21,7 @@ disallowed-tools:
|
|
|
21
21
|
- Bash(awk *)
|
|
22
22
|
- Bash(curl *)
|
|
23
23
|
---
|
|
24
|
-
<!-- generated by harness_deploy.py; core=
|
|
24
|
+
<!-- generated by harness_deploy.py; core=d6fd3fef72cfa62b; overlay=none; agent=claude-code; do not edit -->
|
|
25
25
|
# harness-pull — 从 Hunter Platform 下拉与恢复
|
|
26
26
|
|
|
27
27
|
## Purpose
|
|
@@ -21,7 +21,7 @@ disallowed-tools:
|
|
|
21
21
|
- Bash(awk *)
|
|
22
22
|
- Bash(curl *)
|
|
23
23
|
---
|
|
24
|
-
<!-- generated by harness_deploy.py; core=
|
|
24
|
+
<!-- generated by harness_deploy.py; core=d6fd3fef72cfa62b; overlay=none; agent=claude-code; do not edit -->
|
|
25
25
|
# harness-push — 上传到 Hunter Platform
|
|
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=
|
|
25
|
+
<!-- generated by harness_deploy.py; core=d6fd3fef72cfa62b; 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=
|
|
25
|
+
<!-- generated by harness_deploy.py; core=d6fd3fef72cfa62b; overlay=none; agent=claude-code; do not edit -->
|
|
26
26
|
# harness-run — 需求编码
|
|
27
27
|
|
|
28
28
|
> **2026-08 阶段合并**:原 `run`(编码)与 `test`(验证)合并为单一 `execute` 阶段,
|
|
@@ -21,7 +21,7 @@ disallowed-tools:
|
|
|
21
21
|
- Bash(awk *)
|
|
22
22
|
- Bash(curl *)
|
|
23
23
|
---
|
|
24
|
-
<!-- generated by harness_deploy.py; core=
|
|
24
|
+
<!-- generated by harness_deploy.py; core=d6fd3fef72cfa62b; overlay=none; agent=claude-code; do not edit -->
|
|
25
25
|
# harness-submit — 最终提交(含 worktree 合并)
|
|
26
26
|
|
|
27
27
|
## Purpose
|
|
@@ -19,7 +19,7 @@ disallowed-tools:
|
|
|
19
19
|
- Bash(awk *)
|
|
20
20
|
- Bash(curl *)
|
|
21
21
|
---
|
|
22
|
-
<!-- generated by harness_deploy.py; core=
|
|
22
|
+
<!-- generated by harness_deploy.py; core=d6fd3fef72cfa62b; overlay=none; agent=claude-code; do not edit -->
|
|
23
23
|
# harness-sync
|
|
24
24
|
|
|
25
25
|
## Purpose
|
|
@@ -23,7 +23,7 @@ disallowed-tools:
|
|
|
23
23
|
- Bash(node *)
|
|
24
24
|
- Bash(codegraph *)
|
|
25
25
|
---
|
|
26
|
-
<!-- generated by harness_deploy.py; core=
|
|
26
|
+
<!-- generated by harness_deploy.py; core=d6fd3fef72cfa62b; overlay=none; agent=claude-code; do not edit -->
|
|
27
27
|
# harness-test — 测试执行
|
|
28
28
|
|
|
29
29
|
> **2026-08 阶段合并**:原 `run`(编码)与 `test`(验证)合并为单一 `execute` 阶段,
|
|
@@ -503,6 +503,96 @@ def _remote_ci_history_present(change_dir: Path) -> bool:
|
|
|
503
503
|
)
|
|
504
504
|
|
|
505
505
|
|
|
506
|
+
# decisions.json 单条记录的上限:防呆,不是安全边界(归档包大小限制在服务端)。
|
|
507
|
+
_DECISIONS_MAX_ENTRIES = 200
|
|
508
|
+
_DECISIONS_MAX_TITLE = 500
|
|
509
|
+
_DECISIONS_MAX_RATIONALE = 4000
|
|
510
|
+
_DECISIONS_MAX_KEYWORDS = 32
|
|
511
|
+
_DECISIONS_MAX_KEYWORD_CHARS = 80
|
|
512
|
+
|
|
513
|
+
|
|
514
|
+
def _decision_record_valid(record: dict[str, Any]) -> bool:
|
|
515
|
+
"""evidence/decisions.json 单条记录的确定性校验(与 hkc 的消费端口径一致)。"""
|
|
516
|
+
title = str(record.get("title") or "").strip()
|
|
517
|
+
if not title or len(title) > _DECISIONS_MAX_TITLE:
|
|
518
|
+
return False
|
|
519
|
+
if str(record.get("entry_type") or "").strip() not in {
|
|
520
|
+
"decision",
|
|
521
|
+
"requirement",
|
|
522
|
+
"api-contract",
|
|
523
|
+
}:
|
|
524
|
+
return False
|
|
525
|
+
if str(record.get("status") or "").strip().lower() not in {
|
|
526
|
+
"adopted",
|
|
527
|
+
"proposed",
|
|
528
|
+
"rejected",
|
|
529
|
+
"superseded",
|
|
530
|
+
}:
|
|
531
|
+
return False
|
|
532
|
+
rationale = record.get("rationale")
|
|
533
|
+
if rationale is not None and (
|
|
534
|
+
not isinstance(rationale, str) or len(rationale) > _DECISIONS_MAX_RATIONALE
|
|
535
|
+
):
|
|
536
|
+
return False
|
|
537
|
+
record_id = record.get("id")
|
|
538
|
+
if record_id is not None and (
|
|
539
|
+
not isinstance(record_id, str) or len(record_id.strip()) > 80
|
|
540
|
+
):
|
|
541
|
+
return False
|
|
542
|
+
path = record.get("path")
|
|
543
|
+
if path is not None:
|
|
544
|
+
if not isinstance(path, str) or len(path) > 500:
|
|
545
|
+
return False
|
|
546
|
+
normalized = path.replace("\\", "/")
|
|
547
|
+
if normalized.startswith("/") or ".." in normalized.split("/") or ":" in normalized:
|
|
548
|
+
return False
|
|
549
|
+
line = record.get("line")
|
|
550
|
+
if line is not None and (isinstance(line, bool) or not isinstance(line, int) or line < 1):
|
|
551
|
+
return False
|
|
552
|
+
keywords = record.get("keywords")
|
|
553
|
+
if keywords is not None:
|
|
554
|
+
if not isinstance(keywords, list) or len(keywords) > _DECISIONS_MAX_KEYWORDS:
|
|
555
|
+
return False
|
|
556
|
+
if any(
|
|
557
|
+
not isinstance(item, str) or not item.strip() or len(item) > _DECISIONS_MAX_KEYWORD_CHARS
|
|
558
|
+
for item in keywords
|
|
559
|
+
):
|
|
560
|
+
return False
|
|
561
|
+
source = record.get("source")
|
|
562
|
+
if source is not None and str(source).strip() not in {"plan", "review", "manual", "archive"}:
|
|
563
|
+
return False
|
|
564
|
+
return True
|
|
565
|
+
|
|
566
|
+
|
|
567
|
+
def load_change_decisions(change_dir: Path) -> tuple[list[dict[str, Any]], int]:
|
|
568
|
+
"""Load ``evidence/decisions.json`` — 上游(plan/execute/review)采纳的结构化决策。
|
|
569
|
+
|
|
570
|
+
Returns (valid records, dropped count). 文件缺失/损坏/版本不符视为没有决策;
|
|
571
|
+
单条不合法的记录被丢弃并计数,由调用方决定是否告警——丢弃不能无声。
|
|
572
|
+
"""
|
|
573
|
+
path = _first_archive_path(change_dir, "evidence/decisions.json")
|
|
574
|
+
if path is None:
|
|
575
|
+
return [], 0
|
|
576
|
+
try:
|
|
577
|
+
data = read_json(path)
|
|
578
|
+
except (OSError, json.JSONDecodeError):
|
|
579
|
+
return [], 0
|
|
580
|
+
if not isinstance(data, dict) or data.get("schema_version") != 1:
|
|
581
|
+
return [], 0
|
|
582
|
+
records = data.get("decisions")
|
|
583
|
+
if not isinstance(records, list):
|
|
584
|
+
return [], 0
|
|
585
|
+
valid: list[dict[str, Any]] = []
|
|
586
|
+
dropped = 0
|
|
587
|
+
for record in records[: _DECISIONS_MAX_ENTRIES]:
|
|
588
|
+
if isinstance(record, dict) and _decision_record_valid(record):
|
|
589
|
+
valid.append(record)
|
|
590
|
+
else:
|
|
591
|
+
dropped += 1
|
|
592
|
+
dropped += max(0, len(records) - _DECISIONS_MAX_ENTRIES)
|
|
593
|
+
return valid, dropped
|
|
594
|
+
|
|
595
|
+
|
|
506
596
|
def load_product_candidate_ci(change_dir: Path) -> dict[str, Any] | None:
|
|
507
597
|
"""Load platform-neutral candidate evidence, then legacy CI evidence."""
|
|
508
598
|
for relative in (
|
|
@@ -5282,6 +5372,7 @@ def collect_summary_data(
|
|
|
5282
5372
|
"changedFiles",
|
|
5283
5373
|
"artifacts",
|
|
5284
5374
|
"reviewSummary",
|
|
5375
|
+
"decisions",
|
|
5285
5376
|
"archiveManifest",
|
|
5286
5377
|
"uncommittedTestEvidence",
|
|
5287
5378
|
"maintenanceNotes",
|
|
@@ -5648,6 +5739,12 @@ def collect_summary_data(
|
|
|
5648
5739
|
)
|
|
5649
5740
|
review_status = hr.status(change_dir)
|
|
5650
5741
|
data["reviewFindings"] = list(review_status.get("items") or [])
|
|
5742
|
+
decisions_dropped = 0
|
|
5743
|
+
if not for_replay:
|
|
5744
|
+
decisions, decisions_dropped = load_change_decisions(change_dir)
|
|
5745
|
+
data["decisions"] = decisions
|
|
5746
|
+
else:
|
|
5747
|
+
data.setdefault("decisions", [])
|
|
5651
5748
|
if not for_replay:
|
|
5652
5749
|
data["timeline"] = _timeline_from_events(event_summary, projected_events)
|
|
5653
5750
|
else:
|
|
@@ -5680,6 +5777,12 @@ def collect_summary_data(
|
|
|
5680
5777
|
if note:
|
|
5681
5778
|
maintenance_notes.append(note)
|
|
5682
5779
|
data["maintenanceNotes"] = maintenance_notes
|
|
5780
|
+
if decisions_dropped:
|
|
5781
|
+
# 丢弃不能无声:decisions.json 是人/agent 显式写的上游产物。
|
|
5782
|
+
data["maintenanceNotes"].append(
|
|
5783
|
+
f"evidence/decisions.json:{decisions_dropped} 条记录不符合 "
|
|
5784
|
+
"schema(缺 title/entry_type/status 或越界)已丢弃"
|
|
5785
|
+
)
|
|
5683
5786
|
scenario_risks, scenario_actions = _risks_from_test_results(change_dir)
|
|
5684
5787
|
data["knownRisks"] = known_risks + scenario_risks
|
|
5685
5788
|
data["manualActions"] = scenario_actions
|
|
@@ -8614,15 +8717,27 @@ def cmd_finalize(
|
|
|
8614
8717
|
|
|
8615
8718
|
# --- 8b. knowledge candidates (also before the after-manifest) ---
|
|
8616
8719
|
# Soft-fail like archive-meta: an archive must never be rolled back because
|
|
8617
|
-
# knowledge extraction found nothing. An empty array is a valid outcome
|
|
8720
|
+
# knowledge extraction found nothing. An empty array is a valid outcome —
|
|
8721
|
+
# but an empty array in the presence of unadjudicated findings is a
|
|
8722
|
+
# *different* situation and must be said out loud, not silently shown as
|
|
8723
|
+
# "ready / 0 results" downstream.
|
|
8618
8724
|
try:
|
|
8619
8725
|
summary = read_json(summary_path)
|
|
8620
8726
|
candidates_path = write_knowledge_candidates(work_dir, summary)
|
|
8727
|
+
candidate_count = len(read_json(candidates_path))
|
|
8728
|
+
unadjudicated = hkc.count_unadjudicated_findings(summary)
|
|
8621
8729
|
payload["steps"]["knowledge_candidates"] = {
|
|
8622
8730
|
"ok": True,
|
|
8623
8731
|
"path": str(candidates_path),
|
|
8624
|
-
"count":
|
|
8732
|
+
"count": candidate_count,
|
|
8733
|
+
"droppedUnadjudicated": unadjudicated,
|
|
8625
8734
|
}
|
|
8735
|
+
if unadjudicated > 0 and candidate_count == 0:
|
|
8736
|
+
warnings.append(
|
|
8737
|
+
f"本次归档有 {unadjudicated} 条未裁决(OPEN/UNKNOWN)的评审发现,"
|
|
8738
|
+
"知识候选为 0:它们按现行策略被丢弃而未形成知识。"
|
|
8739
|
+
"若其中有值得沉淀的内容,请先完成裁决再 republish。"
|
|
8740
|
+
)
|
|
8626
8741
|
except Exception as exc: # noqa: BLE001 — candidates soft-fail
|
|
8627
8742
|
warnings.append(f"knowledge candidates write failed: {exc}")
|
|
8628
8743
|
payload["steps"]["knowledge_candidates"] = {"ok": False, "error": str(exc)}
|
|
@@ -2,17 +2,21 @@
|
|
|
2
2
|
"""Knowledge candidate generation from an archived change's summary-data.json.
|
|
3
3
|
|
|
4
4
|
The archive workflow has already filtered once: review findings were produced by
|
|
5
|
-
an independent reviewer and then adjudicated,
|
|
6
|
-
facts
|
|
7
|
-
|
|
5
|
+
an independent reviewer and then adjudicated, knownRisks are evidence-derived
|
|
6
|
+
facts, and decisions are structured records adopted upstream (plan/execute/
|
|
7
|
+
review). This module turns those three — and only those three — into
|
|
8
|
+
KnowledgeCandidate records for the archive package's ``candidates/knowledge.json``.
|
|
8
9
|
|
|
9
10
|
Mapping is fixed by docs/superpowers/specs/2026-08-18-three-views-data-flow-design.md
|
|
10
|
-
("知识来源的选定")::
|
|
11
|
+
("知识来源的选定"), extended 2026-08-23 with the decisions source::
|
|
11
12
|
|
|
12
13
|
disposition = FIXED -> pitfall RED 0.95 / YELLOW 0.85
|
|
13
14
|
disposition = ACCEPTED_RISK | DEFERRED -> risk RED 0.95 / YELLOW 0.85
|
|
14
15
|
knownRisks[] -> risk 0.85
|
|
16
|
+
decisions[status=adopted] -> record's entry_type (decision |
|
|
17
|
+
requirement | api-contract) 0.85
|
|
15
18
|
severity = OK | disposition = NOT_APPLICABLE -> dropped
|
|
19
|
+
decisions[status != adopted] -> kept in summary, not knowledge
|
|
16
20
|
|
|
17
21
|
Dispositions outside the adopted set (OPEN / UNKNOWN) are dropped too: an
|
|
18
22
|
unadjudicated finding is not yet knowledge. maintenanceNotes, finalStatusReasons
|
|
@@ -42,6 +46,16 @@ _DISPOSITION_ENTRY_TYPES = {
|
|
|
42
46
|
_SEVERITY_CONFIDENCE = {"RED": 0.95, "YELLOW": 0.85}
|
|
43
47
|
_KNOWN_RISK_CONFIDENCE = 0.85
|
|
44
48
|
|
|
49
|
+
# decisions[](来自 change 的 evidence/decisions.json,经 summary.decisions 传入)
|
|
50
|
+
# 与 findings 的裁决门槛同源:只有已采纳(adopted)的决策才成为候选——
|
|
51
|
+
# proposed/rejected/superseded 留在 summary 里做记录,但不构成知识。
|
|
52
|
+
# entry_type 直接取自记录,取值与 knowledgeCandidateEntryTypeSchema 对齐;
|
|
53
|
+
# source 对应 candidateProvenanceSourceKindSchema 的枚举。
|
|
54
|
+
_DECISION_ENTRY_TYPES = {"decision", "requirement", "api-contract"}
|
|
55
|
+
_DECISION_STATUSES = {"adopted", "proposed", "rejected", "superseded"}
|
|
56
|
+
_DECISION_SOURCE_KINDS = {"plan", "review", "manual", "archive"}
|
|
57
|
+
_DECISION_CONFIDENCE = 0.85
|
|
58
|
+
|
|
45
59
|
_MAX_KEYWORDS = 32
|
|
46
60
|
_MAX_KEYWORD_CHARS = 80
|
|
47
61
|
_MAX_BODY_CHARS = 20_000
|
|
@@ -204,6 +218,86 @@ def _risk_candidate(
|
|
|
204
218
|
}
|
|
205
219
|
|
|
206
220
|
|
|
221
|
+
def _decision_candidate(
|
|
222
|
+
record: dict[str, Any],
|
|
223
|
+
*,
|
|
224
|
+
change_key: str,
|
|
225
|
+
archive_id: str,
|
|
226
|
+
producer_version: str,
|
|
227
|
+
created_at: str,
|
|
228
|
+
) -> dict[str, Any] | None:
|
|
229
|
+
"""Project one adopted design decision / requirement / API contract entry.
|
|
230
|
+
|
|
231
|
+
Every field is copied from the record (written upstream by a human or an
|
|
232
|
+
agent during plan/execute/review); nothing is inferred at archive time.
|
|
233
|
+
"""
|
|
234
|
+
title = _text(record.get("title"))
|
|
235
|
+
entry_type = _text(record.get("entry_type"))
|
|
236
|
+
status = _text(record.get("status")).lower()
|
|
237
|
+
if not title or entry_type not in _DECISION_ENTRY_TYPES or status != "adopted":
|
|
238
|
+
return None
|
|
239
|
+
|
|
240
|
+
rationale = _text(record.get("rationale"))
|
|
241
|
+
path = _text(record.get("path"))
|
|
242
|
+
line = record.get("line")
|
|
243
|
+
location = _location(path, line)
|
|
244
|
+
segments = _path_segments(path)
|
|
245
|
+
record_id = _text(record.get("id"))
|
|
246
|
+
source = _text(record.get("source"))
|
|
247
|
+
if source not in _DECISION_SOURCE_KINDS:
|
|
248
|
+
source = "plan"
|
|
249
|
+
raw_keywords = record.get("keywords")
|
|
250
|
+
record_keywords = (
|
|
251
|
+
[item for item in raw_keywords if isinstance(item, str)]
|
|
252
|
+
if isinstance(raw_keywords, list)
|
|
253
|
+
else []
|
|
254
|
+
)
|
|
255
|
+
|
|
256
|
+
body_lines = [title]
|
|
257
|
+
if rationale:
|
|
258
|
+
body_lines.append(f"理由:{rationale}")
|
|
259
|
+
if location:
|
|
260
|
+
body_lines.append(f"位置:{location}")
|
|
261
|
+
body_lines.append(f"类型:{entry_type}")
|
|
262
|
+
body = "\n".join(body_lines)[:_MAX_BODY_CHARS]
|
|
263
|
+
keywords = _keywords(
|
|
264
|
+
*record_keywords,
|
|
265
|
+
segments[-1] if segments else "",
|
|
266
|
+
segments[-2] if len(segments) >= 2 else "",
|
|
267
|
+
entry_type,
|
|
268
|
+
)
|
|
269
|
+
if path and location != path:
|
|
270
|
+
source_refs = [f"{path}#L{line}"]
|
|
271
|
+
elif path:
|
|
272
|
+
source_refs = [path]
|
|
273
|
+
else:
|
|
274
|
+
source_refs = [f"archive:{archive_id}"]
|
|
275
|
+
|
|
276
|
+
return {
|
|
277
|
+
"schema_version": SCHEMA_VERSION,
|
|
278
|
+
"candidate_id": _candidate_id(
|
|
279
|
+
change_key, "decision", record_id or f"{title}\0{path}\0{line}"
|
|
280
|
+
),
|
|
281
|
+
"source_change_key": change_key,
|
|
282
|
+
"source_refs": source_refs,
|
|
283
|
+
"summary": title,
|
|
284
|
+
"reusability_scope": segments[0] if segments else "project",
|
|
285
|
+
"content_hash": _content_hash(entry_type, title, body, keywords),
|
|
286
|
+
"confidence": _DECISION_CONFIDENCE,
|
|
287
|
+
"status": "pending",
|
|
288
|
+
"entry_type": entry_type,
|
|
289
|
+
"body": body,
|
|
290
|
+
"keywords": keywords,
|
|
291
|
+
"provenance": {
|
|
292
|
+
"source_kind": source,
|
|
293
|
+
"source_ref": f"archive:{archive_id}#{record_id}" if record_id else f"archive:{archive_id}",
|
|
294
|
+
"producer": PRODUCER,
|
|
295
|
+
"producer_version": producer_version,
|
|
296
|
+
"created_at": created_at,
|
|
297
|
+
},
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
|
|
207
301
|
def build_knowledge_candidates(
|
|
208
302
|
summary: dict[str, Any],
|
|
209
303
|
*,
|
|
@@ -252,9 +346,51 @@ def build_knowledge_candidates(
|
|
|
252
346
|
created_at=created_at,
|
|
253
347
|
))
|
|
254
348
|
|
|
349
|
+
decisions = summary.get("decisions")
|
|
350
|
+
if isinstance(decisions, list):
|
|
351
|
+
for record in decisions:
|
|
352
|
+
if isinstance(record, dict):
|
|
353
|
+
collect(_decision_candidate(
|
|
354
|
+
record,
|
|
355
|
+
change_key=change_key,
|
|
356
|
+
archive_id=archive_id,
|
|
357
|
+
producer_version=producer_version,
|
|
358
|
+
created_at=created_at,
|
|
359
|
+
))
|
|
360
|
+
|
|
255
361
|
return candidates
|
|
256
362
|
|
|
257
363
|
|
|
364
|
+
# Dispositions that mean "not yet adjudicated". A finding in this state with a
|
|
365
|
+
# knowledge-carrying severity is dropped by the mapping above — correctly — but
|
|
366
|
+
# the drop must be visible, or an archive with real findings looks identical to
|
|
367
|
+
# an archive with nothing worth keeping (both ship an empty candidates file).
|
|
368
|
+
_UNADJUDICATED_DISPOSITIONS = {"", "OPEN", "UNKNOWN"}
|
|
369
|
+
|
|
370
|
+
|
|
371
|
+
def count_unadjudicated_findings(summary: dict[str, Any]) -> int:
|
|
372
|
+
"""Count RED/YELLOW reviewFindings whose disposition is not yet adjudicated.
|
|
373
|
+
|
|
374
|
+
These are the findings the mapping table silently drops. Callers use the
|
|
375
|
+
count to warn when an archive would otherwise show "ready / 0 results"
|
|
376
|
+
despite carrying real, unprocessed review signal.
|
|
377
|
+
"""
|
|
378
|
+
if not isinstance(summary, dict):
|
|
379
|
+
return 0
|
|
380
|
+
findings = summary.get("reviewFindings")
|
|
381
|
+
if not isinstance(findings, list):
|
|
382
|
+
return 0
|
|
383
|
+
count = 0
|
|
384
|
+
for finding in findings:
|
|
385
|
+
if not isinstance(finding, dict):
|
|
386
|
+
continue
|
|
387
|
+
severity = _text(finding.get("severity"))
|
|
388
|
+
disposition = _text(finding.get("disposition")).upper()
|
|
389
|
+
if severity in _SEVERITIES and disposition in _UNADJUDICATED_DISPOSITIONS:
|
|
390
|
+
count += 1
|
|
391
|
+
return count
|
|
392
|
+
|
|
393
|
+
|
|
258
394
|
def render_knowledge_candidates_json(candidates: list[dict[str, Any]]) -> str:
|
|
259
395
|
"""Deterministic bytes for the archive package entry."""
|
|
260
396
|
return json.dumps(
|
|
@@ -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=
|
|
6
|
+
<!-- generated by harness_deploy.py; core=d6fd3fef72cfa62b; overlay=none; agent=codebuddy; do not edit -->
|
|
7
7
|
# harness-archive — 变更归档
|
|
8
8
|
|
|
9
9
|
## Purpose
|
|
@@ -67,10 +67,39 @@ powershell.exe -NoProfile -ExecutionPolicy Bypass -File "harness-skills/harness-
|
|
|
67
67
|
- `changedFiles`:path/summary/insertions/deletions —— 来自 `git diff --numstat <base>..<head>`;
|
|
68
68
|
- `reviewSummary`:red/yellow + redFixed/redConfirmed/yellowFixed/yellowDeferred 修复进度;
|
|
69
69
|
- `maintenanceNotes`:给后续维护者看的结论;
|
|
70
|
-
- `knownRisks
|
|
70
|
+
- `knownRisks`:剩余风险或人工确认项;
|
|
71
|
+
- `decisions`:已采纳的设计决策/需求/API 契约 —— 来自 `evidence/decisions.json`(见下节),生成器只做校验与透传,不从 markdown 提取。
|
|
71
72
|
|
|
72
73
|
报告必须突出业务目标和维护者结论。所有统计数字只能来自 events、summary-data、ledger 或 manifest,不得手写另一套。历史 archive 没有 `events.ndjson` 时,允许从 ledger/log/manifest 回放,并在 `reportPipeline.sources` 中记录来源。
|
|
73
74
|
|
|
75
|
+
## evidence/decisions.json(可选,知识沉淀入口)
|
|
76
|
+
|
|
77
|
+
重要设计决策、长期约束、API 契约要进入平台知识库的唯一通道:在 plan/execute/review 阶段由人或 agent 把**已采纳**的结论写入 `.harness/changes/<change>/evidence/decisions.json`(schema_version 1):
|
|
78
|
+
|
|
79
|
+
```json
|
|
80
|
+
{
|
|
81
|
+
"schema_version": 1,
|
|
82
|
+
"decisions": [
|
|
83
|
+
{
|
|
84
|
+
"id": "D-001",
|
|
85
|
+
"title": "一句话结论(必填,≤500 字)",
|
|
86
|
+
"rationale": "为什么(可选,≤4000 字)",
|
|
87
|
+
"entry_type": "decision | requirement | api-contract",
|
|
88
|
+
"status": "adopted | proposed | rejected | superseded",
|
|
89
|
+
"path": "docs/design/x.md(可选,相对路径,溯源用)",
|
|
90
|
+
"line": 42,
|
|
91
|
+
"keywords": ["可选,≤32 个"],
|
|
92
|
+
"source": "plan | review | manual | archive"
|
|
93
|
+
}
|
|
94
|
+
]
|
|
95
|
+
}
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
- 只有 `status: "adopted"` 的记录会成为知识候选(`entry_type` 原样落到知识条目上);其余状态保留在 summary 里做记录。
|
|
99
|
+
- 与 reviewFindings 的裁决门槛同源:**筛选必须在上游发生**——不要把讨论过程原文倒进来,归档时不做任何 LLM 提取。
|
|
100
|
+
- 不合 schema 的记录会被丢弃并在 `maintenanceNotes` 留一条计数说明;丢弃不无声。
|
|
101
|
+
- 未裁决(OPEN/UNKNOWN)的 RED/YELLOW findings 若导致候选为 0,finalize 会显式告警提示先裁决再 republish。
|
|
102
|
+
|
|
74
103
|
## 平台展示与数据校验
|
|
75
104
|
|
|
76
105
|
平台监控直接读取 `reports/final/summary-data.json`,归档阶段不再生成本地 HTML 报告,也不得由模型临场维护第二份展示数据。
|
|
@@ -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=
|
|
6
|
+
<!-- generated by harness_deploy.py; core=d6fd3fef72cfa62b; overlay=none; agent=codebuddy; do not edit -->
|
|
7
7
|
# harness-codebase-map — 代码库地图
|
|
8
8
|
|
|
9
9
|
## Purpose
|
|
@@ -4,7 +4,7 @@ description: 执行阶段:按变更簇执行 TDD
|
|
|
4
4
|
编码循环(RED→GREEN→REFACTOR→编译验证),随后完成单元/接口/数据兼容验证并输出测试报告。仅当用户显式调用
|
|
5
5
|
/harness-execute 时使用;不得因用户提到编码/实现/测试就自动触发,也不得被其他阶段 skill 自动接续。
|
|
6
6
|
---
|
|
7
|
-
<!-- generated by harness_deploy.py; core=
|
|
7
|
+
<!-- generated by harness_deploy.py; core=d6fd3fef72cfa62b; overlay=none; agent=codebuddy; do not edit -->
|
|
8
8
|
# harness-execute — 执行(编码 + 验证)
|
|
9
9
|
|
|
10
10
|
> 2026-08 阶段合并:原 `run`(编码)与 `test`(验证)合并为单一 `execute` 阶段。
|
|
@@ -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=
|
|
5
|
+
<!-- generated by harness_deploy.py; core=d6fd3fef72cfa62b; 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=
|
|
5
|
+
<!-- generated by harness_deploy.py; core=d6fd3fef72cfa62b; 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=
|
|
6
|
+
<!-- generated by harness_deploy.py; core=d6fd3fef72cfa62b; overlay=none; agent=codebuddy; do not edit -->
|
|
7
7
|
# harness-plan — 需求规划
|
|
8
8
|
|
|
9
9
|
## Purpose
|