@hunter-harness/workflow-harness 0.2.33 → 0.2.34
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/README.md +25 -25
- package/harness/bundles/general/claude-code/.harness-build.json +1 -1
- package/harness/bundles/general/claude-code/contracts/workflow-policy.json +1 -0
- package/harness/bundles/general/claude-code/harness-archive/SKILL.md +1 -1
- package/harness/bundles/general/claude-code/harness-archive/templates/render-summary.mjs +215 -106
- package/harness/bundles/general/claude-code/harness-archive/templates/summary-data-template.json +8 -0
- package/harness/bundles/general/claude-code/harness-codebase-map/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-ingest/scripts/harness_knowledge.py +389 -117
- package/harness/bundles/general/claude-code/harness-knowledge-query/SKILL.md +1 -1
- package/harness/bundles/general/claude-code/harness-plan/SKILL.md +3 -3
- package/harness/bundles/general/claude-code/harness-review/SKILL.md +2 -2
- package/harness/bundles/general/claude-code/harness-run/SKILL.md +4 -4
- package/harness/bundles/general/claude-code/harness-submit/SKILL.md +2 -2
- package/harness/bundles/general/claude-code/harness-sync/SKILL.md +1 -1
- package/harness/bundles/general/claude-code/harness-test/SKILL.md +4 -4
- package/harness/bundles/general/claude-code/harness-test/checklist.md +1 -0
- package/harness/bundles/general/claude-code/harness-test/reference.md +2 -1
- package/harness/bundles/general/claude-code/protocols/ledger-protocol.md +2 -2
- package/harness/bundles/general/claude-code/scripts/harness_archive.py +404 -12
- package/harness/bundles/general/claude-code/scripts/harness_context.py +717 -0
- package/harness/bundles/general/claude-code/scripts/harness_gate.py +1 -0
- package/harness/bundles/general/claude-code/scripts/harness_ledger.py +77 -9
- package/harness/bundles/general/claude-code/scripts/harness_phase.py +1 -0
- package/harness/bundles/general/claude-code/scripts/harness_plan_aggregate.py +249 -0
- package/harness/bundles/general/claude-code/scripts/harness_profile.py +272 -15
- package/harness/bundles/general/claude-code/scripts/harness_report_model.py +420 -0
- package/harness/bundles/general/claude-code/scripts/harness_review.py +16 -1
- package/harness/bundles/general/claude-code/scripts/harness_verification.py +114 -0
- package/harness/bundles/general/codebuddy/.harness-build.json +1 -1
- package/harness/bundles/general/codebuddy/contracts/workflow-policy.json +1 -0
- package/harness/bundles/general/codebuddy/harness-archive/SKILL.md +1 -1
- package/harness/bundles/general/codebuddy/harness-archive/templates/render-summary.mjs +215 -106
- package/harness/bundles/general/codebuddy/harness-archive/templates/summary-data-template.json +8 -0
- package/harness/bundles/general/codebuddy/harness-codebase-map/SKILL.md +1 -1
- package/harness/bundles/general/codebuddy/harness-knowledge-ingest/SKILL.md +1 -1
- package/harness/bundles/general/codebuddy/harness-knowledge-ingest/scripts/harness_knowledge.py +389 -117
- package/harness/bundles/general/codebuddy/harness-knowledge-query/SKILL.md +1 -1
- package/harness/bundles/general/codebuddy/harness-plan/SKILL.md +3 -3
- package/harness/bundles/general/codebuddy/harness-review/SKILL.md +2 -2
- package/harness/bundles/general/codebuddy/harness-run/SKILL.md +4 -4
- package/harness/bundles/general/codebuddy/harness-submit/SKILL.md +2 -2
- package/harness/bundles/general/codebuddy/harness-sync/SKILL.md +1 -1
- package/harness/bundles/general/codebuddy/harness-test/SKILL.md +4 -4
- package/harness/bundles/general/codebuddy/harness-test/checklist.md +1 -0
- package/harness/bundles/general/codebuddy/harness-test/reference.md +2 -1
- package/harness/bundles/general/codebuddy/protocols/ledger-protocol.md +2 -2
- package/harness/bundles/general/codebuddy/scripts/harness_archive.py +404 -12
- package/harness/bundles/general/codebuddy/scripts/harness_context.py +717 -0
- package/harness/bundles/general/codebuddy/scripts/harness_gate.py +1 -0
- package/harness/bundles/general/codebuddy/scripts/harness_ledger.py +77 -9
- package/harness/bundles/general/codebuddy/scripts/harness_phase.py +1 -0
- package/harness/bundles/general/codebuddy/scripts/harness_plan_aggregate.py +249 -0
- package/harness/bundles/general/codebuddy/scripts/harness_profile.py +272 -15
- package/harness/bundles/general/codebuddy/scripts/harness_report_model.py +420 -0
- package/harness/bundles/general/codebuddy/scripts/harness_review.py +16 -1
- package/harness/bundles/general/codebuddy/scripts/harness_verification.py +114 -0
- package/harness/bundles/general/codex/.harness-build.json +1 -1
- package/harness/bundles/general/codex/contracts/workflow-policy.json +1 -0
- package/harness/bundles/general/codex/harness-archive/SKILL.md +1 -1
- package/harness/bundles/general/codex/harness-archive/templates/render-summary.mjs +215 -106
- package/harness/bundles/general/codex/harness-archive/templates/summary-data-template.json +8 -0
- package/harness/bundles/general/codex/harness-codebase-map/SKILL.md +1 -1
- package/harness/bundles/general/codex/harness-knowledge-ingest/SKILL.md +1 -1
- package/harness/bundles/general/codex/harness-knowledge-ingest/scripts/harness_knowledge.py +389 -117
- package/harness/bundles/general/codex/harness-knowledge-query/SKILL.md +1 -1
- package/harness/bundles/general/codex/harness-plan/SKILL.md +3 -3
- package/harness/bundles/general/codex/harness-review/SKILL.md +2 -2
- package/harness/bundles/general/codex/harness-run/SKILL.md +4 -4
- package/harness/bundles/general/codex/harness-submit/SKILL.md +2 -2
- package/harness/bundles/general/codex/harness-sync/SKILL.md +1 -1
- package/harness/bundles/general/codex/harness-test/SKILL.md +4 -4
- package/harness/bundles/general/codex/harness-test/checklist.md +1 -0
- package/harness/bundles/general/codex/harness-test/reference.md +2 -1
- package/harness/bundles/general/codex/protocols/ledger-protocol.md +2 -2
- package/harness/bundles/general/codex/scripts/harness_archive.py +404 -12
- package/harness/bundles/general/codex/scripts/harness_context.py +717 -0
- package/harness/bundles/general/codex/scripts/harness_gate.py +1 -0
- package/harness/bundles/general/codex/scripts/harness_ledger.py +77 -9
- package/harness/bundles/general/codex/scripts/harness_phase.py +1 -0
- package/harness/bundles/general/codex/scripts/harness_plan_aggregate.py +249 -0
- package/harness/bundles/general/codex/scripts/harness_profile.py +272 -15
- package/harness/bundles/general/codex/scripts/harness_report_model.py +420 -0
- package/harness/bundles/general/codex/scripts/harness_review.py +16 -1
- package/harness/bundles/general/codex/scripts/harness_verification.py +114 -0
- package/harness/bundles/general/cursor/.harness-build.json +1 -1
- package/harness/bundles/general/cursor/contracts/workflow-policy.json +1 -0
- package/harness/bundles/general/cursor/harness-archive/SKILL.md +1 -1
- package/harness/bundles/general/cursor/harness-archive/templates/render-summary.mjs +215 -106
- package/harness/bundles/general/cursor/harness-archive/templates/summary-data-template.json +8 -0
- package/harness/bundles/general/cursor/harness-codebase-map/SKILL.md +1 -1
- package/harness/bundles/general/cursor/harness-knowledge-ingest/SKILL.md +1 -1
- package/harness/bundles/general/cursor/harness-knowledge-ingest/scripts/harness_knowledge.py +389 -117
- package/harness/bundles/general/cursor/harness-knowledge-query/SKILL.md +1 -1
- package/harness/bundles/general/cursor/harness-plan/SKILL.md +3 -3
- package/harness/bundles/general/cursor/harness-review/SKILL.md +2 -2
- package/harness/bundles/general/cursor/harness-run/SKILL.md +4 -4
- package/harness/bundles/general/cursor/harness-submit/SKILL.md +2 -2
- package/harness/bundles/general/cursor/harness-sync/SKILL.md +1 -1
- package/harness/bundles/general/cursor/harness-test/SKILL.md +4 -4
- package/harness/bundles/general/cursor/harness-test/checklist.md +1 -0
- package/harness/bundles/general/cursor/harness-test/reference.md +2 -1
- package/harness/bundles/general/cursor/protocols/ledger-protocol.md +2 -2
- package/harness/bundles/general/cursor/scripts/harness_archive.py +404 -12
- package/harness/bundles/general/cursor/scripts/harness_context.py +717 -0
- package/harness/bundles/general/cursor/scripts/harness_gate.py +1 -0
- package/harness/bundles/general/cursor/scripts/harness_ledger.py +77 -9
- package/harness/bundles/general/cursor/scripts/harness_phase.py +1 -0
- package/harness/bundles/general/cursor/scripts/harness_plan_aggregate.py +249 -0
- package/harness/bundles/general/cursor/scripts/harness_profile.py +272 -15
- package/harness/bundles/general/cursor/scripts/harness_report_model.py +420 -0
- package/harness/bundles/general/cursor/scripts/harness_review.py +16 -1
- package/harness/bundles/general/cursor/scripts/harness_verification.py +114 -0
- package/harness/bundles/java/claude-code/.harness-build.json +1 -1
- package/harness/bundles/java/claude-code/contracts/workflow-policy.json +1 -0
- 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/templates/render-summary.mjs +215 -106
- package/harness/bundles/java/claude-code/harness-archive/templates/summary-data-template.json +8 -0
- package/harness/bundles/java/claude-code/harness-codebase-map/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-ingest/scripts/harness_knowledge.py +389 -117
- 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 +3 -3
- package/harness/bundles/java/claude-code/harness-review/SKILL.md +2 -2
- package/harness/bundles/java/claude-code/harness-run/SKILL.md +4 -4
- package/harness/bundles/java/claude-code/harness-submit/SKILL.md +2 -2
- package/harness/bundles/java/claude-code/harness-sync/SKILL.md +1 -1
- package/harness/bundles/java/claude-code/harness-test/SKILL.md +4 -4
- package/harness/bundles/java/claude-code/harness-test/checklist.md +1 -0
- package/harness/bundles/java/claude-code/harness-test/reference.md +2 -0
- package/harness/bundles/java/claude-code/protocols/ledger-protocol.md +2 -2
- package/harness/bundles/java/claude-code/scripts/harness_archive.py +404 -12
- package/harness/bundles/java/claude-code/scripts/harness_context.py +717 -0
- package/harness/bundles/java/claude-code/scripts/harness_gate.py +1 -0
- package/harness/bundles/java/claude-code/scripts/harness_ledger.py +77 -9
- package/harness/bundles/java/claude-code/scripts/harness_phase.py +1 -0
- package/harness/bundles/java/claude-code/scripts/harness_plan_aggregate.py +249 -0
- package/harness/bundles/java/claude-code/scripts/harness_profile.py +272 -15
- package/harness/bundles/java/claude-code/scripts/harness_report_model.py +420 -0
- package/harness/bundles/java/claude-code/scripts/harness_review.py +16 -1
- package/harness/bundles/java/claude-code/scripts/harness_verification.py +114 -0
- package/harness/bundles/java/codebuddy/.harness-build.json +1 -1
- package/harness/bundles/java/codebuddy/contracts/workflow-policy.json +1 -0
- 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/templates/render-summary.mjs +215 -106
- package/harness/bundles/java/codebuddy/harness-archive/templates/summary-data-template.json +8 -0
- package/harness/bundles/java/codebuddy/harness-codebase-map/SKILL.md +1 -1
- package/harness/bundles/java/codebuddy/harness-knowledge-ingest/SKILL.md +1 -1
- package/harness/bundles/java/codebuddy/harness-knowledge-ingest/scripts/harness_knowledge.py +389 -117
- 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 +3 -3
- package/harness/bundles/java/codebuddy/harness-review/SKILL.md +2 -2
- package/harness/bundles/java/codebuddy/harness-run/SKILL.md +4 -4
- package/harness/bundles/java/codebuddy/harness-submit/SKILL.md +2 -2
- package/harness/bundles/java/codebuddy/harness-sync/SKILL.md +1 -1
- package/harness/bundles/java/codebuddy/harness-test/SKILL.md +4 -4
- package/harness/bundles/java/codebuddy/harness-test/checklist.md +1 -0
- package/harness/bundles/java/codebuddy/harness-test/reference.md +2 -0
- package/harness/bundles/java/codebuddy/protocols/ledger-protocol.md +2 -2
- package/harness/bundles/java/codebuddy/scripts/harness_archive.py +404 -12
- package/harness/bundles/java/codebuddy/scripts/harness_context.py +717 -0
- package/harness/bundles/java/codebuddy/scripts/harness_gate.py +1 -0
- package/harness/bundles/java/codebuddy/scripts/harness_ledger.py +77 -9
- package/harness/bundles/java/codebuddy/scripts/harness_phase.py +1 -0
- package/harness/bundles/java/codebuddy/scripts/harness_plan_aggregate.py +249 -0
- package/harness/bundles/java/codebuddy/scripts/harness_profile.py +272 -15
- package/harness/bundles/java/codebuddy/scripts/harness_report_model.py +420 -0
- package/harness/bundles/java/codebuddy/scripts/harness_review.py +16 -1
- package/harness/bundles/java/codebuddy/scripts/harness_verification.py +114 -0
- package/harness/bundles/java/codex/.harness-build.json +1 -1
- package/harness/bundles/java/codex/contracts/workflow-policy.json +1 -0
- 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/templates/render-summary.mjs +215 -106
- package/harness/bundles/java/codex/harness-archive/templates/summary-data-template.json +8 -0
- package/harness/bundles/java/codex/harness-codebase-map/SKILL.md +1 -1
- package/harness/bundles/java/codex/harness-knowledge-ingest/SKILL.md +1 -1
- package/harness/bundles/java/codex/harness-knowledge-ingest/scripts/harness_knowledge.py +389 -117
- 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 +3 -3
- package/harness/bundles/java/codex/harness-review/SKILL.md +2 -2
- package/harness/bundles/java/codex/harness-run/SKILL.md +4 -4
- package/harness/bundles/java/codex/harness-submit/SKILL.md +2 -2
- package/harness/bundles/java/codex/harness-sync/SKILL.md +1 -1
- package/harness/bundles/java/codex/harness-test/SKILL.md +4 -4
- package/harness/bundles/java/codex/harness-test/checklist.md +1 -0
- package/harness/bundles/java/codex/harness-test/reference.md +2 -0
- package/harness/bundles/java/codex/protocols/ledger-protocol.md +2 -2
- package/harness/bundles/java/codex/scripts/harness_archive.py +404 -12
- package/harness/bundles/java/codex/scripts/harness_context.py +717 -0
- package/harness/bundles/java/codex/scripts/harness_gate.py +1 -0
- package/harness/bundles/java/codex/scripts/harness_ledger.py +77 -9
- package/harness/bundles/java/codex/scripts/harness_phase.py +1 -0
- package/harness/bundles/java/codex/scripts/harness_plan_aggregate.py +249 -0
- package/harness/bundles/java/codex/scripts/harness_profile.py +272 -15
- package/harness/bundles/java/codex/scripts/harness_report_model.py +420 -0
- package/harness/bundles/java/codex/scripts/harness_review.py +16 -1
- package/harness/bundles/java/codex/scripts/harness_verification.py +114 -0
- package/harness/bundles/java/cursor/.harness-build.json +1 -1
- package/harness/bundles/java/cursor/contracts/workflow-policy.json +1 -0
- 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/templates/render-summary.mjs +215 -106
- package/harness/bundles/java/cursor/harness-archive/templates/summary-data-template.json +8 -0
- package/harness/bundles/java/cursor/harness-codebase-map/SKILL.md +1 -1
- package/harness/bundles/java/cursor/harness-knowledge-ingest/SKILL.md +1 -1
- package/harness/bundles/java/cursor/harness-knowledge-ingest/scripts/harness_knowledge.py +389 -117
- 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 +3 -3
- package/harness/bundles/java/cursor/harness-review/SKILL.md +2 -2
- package/harness/bundles/java/cursor/harness-run/SKILL.md +4 -4
- package/harness/bundles/java/cursor/harness-submit/SKILL.md +2 -2
- package/harness/bundles/java/cursor/harness-sync/SKILL.md +1 -1
- package/harness/bundles/java/cursor/harness-test/SKILL.md +4 -4
- package/harness/bundles/java/cursor/harness-test/checklist.md +1 -0
- package/harness/bundles/java/cursor/harness-test/reference.md +2 -0
- package/harness/bundles/java/cursor/protocols/ledger-protocol.md +2 -2
- package/harness/bundles/java/cursor/scripts/harness_archive.py +404 -12
- package/harness/bundles/java/cursor/scripts/harness_context.py +717 -0
- package/harness/bundles/java/cursor/scripts/harness_gate.py +1 -0
- package/harness/bundles/java/cursor/scripts/harness_ledger.py +77 -9
- package/harness/bundles/java/cursor/scripts/harness_phase.py +1 -0
- package/harness/bundles/java/cursor/scripts/harness_plan_aggregate.py +249 -0
- package/harness/bundles/java/cursor/scripts/harness_profile.py +272 -15
- package/harness/bundles/java/cursor/scripts/harness_report_model.py +420 -0
- package/harness/bundles/java/cursor/scripts/harness_review.py +16 -1
- package/harness/bundles/java/cursor/scripts/harness_verification.py +114 -0
- package/harness/manifests/general/claude-code.json +42 -26
- package/harness/manifests/general/codebuddy.json +42 -26
- package/harness/manifests/general/codex.json +42 -26
- package/harness/manifests/general/cursor.json +42 -26
- package/harness/manifests/java/claude-code.json +44 -28
- package/harness/manifests/java/codebuddy.json +44 -28
- package/harness/manifests/java/codex.json +44 -28
- package/harness/manifests/java/cursor.json +44 -28
- package/hunter-workflow-family.json +4 -4
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,25 +1,25 @@
|
|
|
1
|
-
# @hunter-harness/workflow-harness
|
|
2
|
-
|
|
3
|
-
Harness 工作流族的**纯数据 npm 包**(bundles + manifests + migrations),供瘦身后 `hunter-harness` CLI 在运行时解析安装。
|
|
4
|
-
|
|
5
|
-
## 版本约定
|
|
6
|
-
|
|
7
|
-
- npm 包 `version` 与 Registry 中 **Workflow Family 发布版本** 1:1 对齐(例如 Family `1.0.0` → 本包 `1.0.0`)。
|
|
8
|
-
- `hunter-workflow-family.json` 记录族 slug、所需 profile 与 bundle 元数据,以及 `content_sha256`
|
|
9
|
-
——对 `harness/` 下所有文件按路径排序后 `{path, content}` 规范化 JSON(`canonicalJson`)取 SHA-256,
|
|
10
|
-
与 `apps/server/src/npm/publisher.ts` 的 `buildWorkflowFamilyManifest` 使用同一算法,
|
|
11
|
-
供离线/本地校验本包内容完整性。
|
|
12
|
-
|
|
13
|
-
## 布局
|
|
14
|
-
|
|
15
|
-
```
|
|
16
|
-
harness/
|
|
17
|
-
bundles/{general|java}/{claude-code|codex|cursor|codebuddy}/...
|
|
18
|
-
manifests/{general|java}/{agent}.json
|
|
19
|
-
migrations/0.1.1/{general|java}.json
|
|
20
|
-
hunter-workflow-family.json
|
|
21
|
-
```
|
|
22
|
-
|
|
23
|
-
## 生成
|
|
24
|
-
|
|
25
|
-
仓库根目录执行 `npm run sync:harness`,产物会同步写入本包的 `harness/` 目录。
|
|
1
|
+
# @hunter-harness/workflow-harness
|
|
2
|
+
|
|
3
|
+
Harness 工作流族的**纯数据 npm 包**(bundles + manifests + migrations),供瘦身后 `hunter-harness` CLI 在运行时解析安装。
|
|
4
|
+
|
|
5
|
+
## 版本约定
|
|
6
|
+
|
|
7
|
+
- npm 包 `version` 与 Registry 中 **Workflow Family 发布版本** 1:1 对齐(例如 Family `1.0.0` → 本包 `1.0.0`)。
|
|
8
|
+
- `hunter-workflow-family.json` 记录族 slug、所需 profile 与 bundle 元数据,以及 `content_sha256`
|
|
9
|
+
——对 `harness/` 下所有文件按路径排序后 `{path, content}` 规范化 JSON(`canonicalJson`)取 SHA-256,
|
|
10
|
+
与 `apps/server/src/npm/publisher.ts` 的 `buildWorkflowFamilyManifest` 使用同一算法,
|
|
11
|
+
供离线/本地校验本包内容完整性。
|
|
12
|
+
|
|
13
|
+
## 布局
|
|
14
|
+
|
|
15
|
+
```
|
|
16
|
+
harness/
|
|
17
|
+
bundles/{general|java}/{claude-code|codex|cursor|codebuddy}/...
|
|
18
|
+
manifests/{general|java}/{agent}.json
|
|
19
|
+
migrations/0.1.1/{general|java}.json
|
|
20
|
+
hunter-workflow-family.json
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## 生成
|
|
24
|
+
|
|
25
|
+
仓库根目录执行 `npm run sync:harness`,产物会同步写入本包的 `harness/` 目录。
|
|
@@ -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=a74978a5fd5c6938; overlay=none; agent=claude-code; do not edit -->
|
|
24
24
|
# harness-archive — 变更归档
|
|
25
25
|
|
|
26
26
|
## Purpose
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
//
|
|
2
|
+
// Deterministic executive renderer for normalized Harness archive reports.
|
|
3
3
|
import fs from "node:fs";
|
|
4
4
|
import path from "node:path";
|
|
5
5
|
|
|
@@ -16,124 +16,233 @@ if (!summaryPath) {
|
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
const data = JSON.parse(fs.readFileSync(summaryPath, "utf8"));
|
|
19
|
-
const esc = (value) => String(value ?? "").replace(/[&<>"']/g, (
|
|
19
|
+
const esc = (value) => String(value ?? "").replace(/[&<>"']/g, (character) => ({
|
|
20
20
|
"&": "&", "<": "<", ">": ">", '"': """, "'": "'"
|
|
21
|
-
})[
|
|
21
|
+
})[character]);
|
|
22
22
|
const list = (value) => Array.isArray(value) ? value : [];
|
|
23
23
|
const record = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
24
|
-
const
|
|
25
|
-
const
|
|
26
|
-
const
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
24
|
+
const numeric = (value) => Number.isFinite(Number(value)) ? Number(value) : null;
|
|
25
|
+
const normalized = record(data.normalizedReport);
|
|
26
|
+
const outcomes = record(normalized.outcomes);
|
|
27
|
+
const current = Object.keys(record(outcomes.current)).length
|
|
28
|
+
? record(outcomes.current)
|
|
29
|
+
: {
|
|
30
|
+
status: data.finalStatus || "UNKNOWN",
|
|
31
|
+
reasons: list(data.finalStatusReasons),
|
|
32
|
+
stages: record(data.stageStatus),
|
|
33
|
+
knownRisks: list(data.knownRisks),
|
|
34
|
+
findings: list(data.reviewFindings)
|
|
35
|
+
};
|
|
36
|
+
const history = record(outcomes.history);
|
|
37
|
+
const release = Object.keys(record(outcomes.release)).length
|
|
38
|
+
? record(outcomes.release)
|
|
39
|
+
: {
|
|
40
|
+
decision: data.archiveIntent === "record-only" ? "NOT_REQUESTED" : record(data.releaseDecision).code,
|
|
41
|
+
eligible: Boolean(record(data.releaseDecision).releaseEligible),
|
|
42
|
+
candidate: record(data.candidateVerification),
|
|
43
|
+
intent: data.archiveIntent || ""
|
|
44
|
+
};
|
|
45
|
+
const identity = Object.keys(record(normalized.identity)).length
|
|
46
|
+
? record(normalized.identity)
|
|
47
|
+
: record(data.changeIdentity);
|
|
48
|
+
const verification = Object.keys(record(normalized.verification)).length
|
|
49
|
+
? record(normalized.verification)
|
|
50
|
+
: record(data.verification);
|
|
51
|
+
const timing = Object.keys(record(normalized.timing)).length
|
|
52
|
+
? record(normalized.timing)
|
|
53
|
+
: record(data.timing);
|
|
54
|
+
const measurements = record(normalized.measurements);
|
|
55
|
+
const recordOnly = release.intent === "record-only" || data.archiveIntent === "record-only";
|
|
56
|
+
const files = list(data.changedFiles);
|
|
57
|
+
const commands = list(record(data.reportPipeline).commands);
|
|
58
|
+
const timeline = list(history.timeline).length ? list(history.timeline) : list(data.timeline);
|
|
59
|
+
const attempts = list(history.attempts).length
|
|
60
|
+
? list(history.attempts)
|
|
61
|
+
: list(timing.attempts);
|
|
62
|
+
const actions = list(data.manualActions);
|
|
63
|
+
const risks = list(current.findings).length
|
|
64
|
+
? list(current.findings)
|
|
65
|
+
: list(current.knownRisks).length
|
|
66
|
+
? list(current.knownRisks)
|
|
67
|
+
: list(data.knownRisks);
|
|
68
|
+
|
|
69
|
+
const statusTone = (raw) => {
|
|
70
|
+
const status = String(raw || "UNKNOWN").toUpperCase();
|
|
71
|
+
if (/FAIL|ERROR|BLOCKED/.test(status)) return "danger";
|
|
72
|
+
if (/WARN|CONDITIONAL|PARTIAL|NOT_RUN|UNKNOWN|SKIP/.test(status)) return "warning";
|
|
73
|
+
if (/ADVISORY|REUSED|NOT_APPLICABLE/.test(status)) return "neutral";
|
|
31
74
|
return "success";
|
|
32
75
|
};
|
|
33
|
-
const
|
|
76
|
+
const STATUS_LABELS = {
|
|
77
|
+
OK: "通过", PASS: "通过", PASSED: "通过", CONDITIONAL_OK: "有条件通过",
|
|
78
|
+
WARN: "警告", ADVISORY: "建议", FAIL: "失败", FAILED: "失败", ERROR: "错误",
|
|
79
|
+
BLOCKED: "阻塞", NOT_RUN: "未运行", SKIPPED: "已跳过",
|
|
80
|
+
NOT_APPLICABLE: "不适用", UNKNOWN: "未知"
|
|
81
|
+
};
|
|
82
|
+
const pill = (raw) => {
|
|
83
|
+
const status = String(raw || "UNKNOWN").toUpperCase();
|
|
84
|
+
return `<span class="pill ${statusTone(status)}" title="${esc(status)}">${esc(STATUS_LABELS[status] || status)}</span>`;
|
|
85
|
+
};
|
|
34
86
|
const describe = (value) => {
|
|
35
87
|
if (typeof value === "string") return value;
|
|
36
|
-
|
|
37
|
-
return
|
|
88
|
+
const item = record(value);
|
|
89
|
+
return item.title || item.message || item.summary || item.action || item.remediation || item.note || JSON.stringify(item);
|
|
38
90
|
};
|
|
39
|
-
const
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
if (value
|
|
43
|
-
|
|
91
|
+
const shortHash = (value) => String(value || "N/A").slice(0, 10);
|
|
92
|
+
const duration = (raw) => {
|
|
93
|
+
const value = numeric(raw);
|
|
94
|
+
if (value === null) return "N/A";
|
|
95
|
+
if (value < 1000) return `${Math.round(value)}ms`;
|
|
96
|
+
if (value < 60000) return `${Math.round(value / 100) / 10} 秒`;
|
|
97
|
+
if (value < 3600000) return `${Math.round(value / 6000) / 10} 分钟`;
|
|
98
|
+
return `${Math.floor(value / 3600000)} 小时 ${Math.round(value % 3600000 / 60000)} 分钟`;
|
|
44
99
|
};
|
|
45
|
-
const
|
|
46
|
-
const
|
|
47
|
-
if (
|
|
48
|
-
if (
|
|
49
|
-
|
|
50
|
-
if (
|
|
51
|
-
|
|
52
|
-
if (minutes < 60) return `${Math.round(minutes * 10) / 10} 分钟`;
|
|
53
|
-
return `${Math.floor(minutes / 60)} 小时 ${Math.round(minutes % 60)} 分钟`;
|
|
100
|
+
const measurement = (value, unit = "") => {
|
|
101
|
+
const item = record(value);
|
|
102
|
+
if (item.state === "unknown" || item.state === "not_applicable") return "N/A";
|
|
103
|
+
if (item.state === "zero") return `0${unit}`;
|
|
104
|
+
if (item.state === "known") return `${esc(item.value)}${unit}`;
|
|
105
|
+
if (value === null || value === undefined || value === "") return "N/A";
|
|
106
|
+
return `${esc(value)}${unit}`;
|
|
54
107
|
};
|
|
108
|
+
const verificationStatus = (value) => {
|
|
109
|
+
if (typeof value === "string") return value;
|
|
110
|
+
const item = record(value);
|
|
111
|
+
if (item.status) return item.status;
|
|
112
|
+
if ((numeric(item.failures) || 0) + (numeric(item.errors) || 0) > 0) return "FAIL";
|
|
113
|
+
if ((numeric(item.run) || numeric(item.total) || 0) > 0) return "OK";
|
|
114
|
+
return "NOT_RUN";
|
|
115
|
+
};
|
|
116
|
+
const verificationNote = (key, raw) => {
|
|
117
|
+
const value = record(raw);
|
|
118
|
+
if (key === "unitTests") return `${value.passRate || `${value.run ?? 0} 项`} · 失败 ${value.failures ?? 0}`;
|
|
119
|
+
if (key === "apiTests") return `${value.passRate || `${value.passed ?? 0}/${value.total ?? 0}`} · 阻塞 ${value.blocked ?? 0}`;
|
|
120
|
+
if (key === "browserE2E") return `${value.passed ?? 0}/${value.total ?? 0} 通过 · ${value.failed ?? 0} 失败 · 重试 ${value.retries ?? 0}`;
|
|
121
|
+
if (value.checks !== undefined) return `${value.checks} 项检查`;
|
|
122
|
+
if (value.coverageDisplay) return value.coverageDisplay;
|
|
123
|
+
return Object.keys(value).filter((name) => name !== "status").slice(0, 3)
|
|
124
|
+
.map((name) => `${name}=${value[name]}`).join(" · ") || "无额外指标";
|
|
125
|
+
};
|
|
126
|
+
const groups = [
|
|
127
|
+
["后端", ["unitTests", "dbCompatibility"]],
|
|
128
|
+
["Geo", ["geo"]],
|
|
129
|
+
["前端", ["frontend"]],
|
|
130
|
+
["浏览器", ["browserE2E"]],
|
|
131
|
+
["API", ["apiTests"]]
|
|
132
|
+
].map(([label, keys]) => {
|
|
133
|
+
const present = keys.filter((key) => verification[key] !== undefined);
|
|
134
|
+
const values = present.map((key) => verification[key]);
|
|
135
|
+
const statuses = values.map(verificationStatus);
|
|
136
|
+
const groupStatus = statuses.some((status) => /FAIL|ERROR|BLOCKED/.test(String(status)))
|
|
137
|
+
? "FAIL"
|
|
138
|
+
: statuses.some((status) => /WARN|CONDITIONAL|PARTIAL/.test(String(status)))
|
|
139
|
+
? "WARN"
|
|
140
|
+
: statuses.length && statuses.every((status) => /NOT_APPLICABLE/.test(String(status)))
|
|
141
|
+
? "NOT_APPLICABLE"
|
|
142
|
+
: statuses.length && statuses.every((status) => /OK|PASS|NOT_APPLICABLE/.test(String(status)))
|
|
143
|
+
? "OK"
|
|
144
|
+
: "NOT_RUN";
|
|
145
|
+
const note = present.map((key) => verificationNote(key, verification[key])).join(" · ") || "未配置该组验证";
|
|
146
|
+
return { label, status: groupStatus, note, present };
|
|
147
|
+
});
|
|
148
|
+
const passedGroups = groups.filter((group) => group.status === "OK").length;
|
|
149
|
+
const productCommit = identity.productCommit || data.productCommit || data.finalCommit;
|
|
150
|
+
const wallClock = timing.workflowWallClockMs ?? record(data.durations).totalMs;
|
|
55
151
|
|
|
56
|
-
const
|
|
57
|
-
|
|
58
|
-
const
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
const
|
|
65
|
-
const
|
|
66
|
-
const
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
const
|
|
70
|
-
|
|
71
|
-
|
|
152
|
+
const groupHtml = groups.map((group) => `
|
|
153
|
+
<article class="verify-card" title="${esc(group.present.map((key) => {
|
|
154
|
+
const value = record(verification[key]);
|
|
155
|
+
return `${key} · status=${verificationStatus(verification[key])}${value.failed === undefined ? "" : ` · failed=${value.failed}`}`;
|
|
156
|
+
}).join(" / "))}">
|
|
157
|
+
<div><span class="verify-name">${esc(group.label)}</span>${pill(group.status)}</div>
|
|
158
|
+
<p>${esc(group.note)}</p>
|
|
159
|
+
</article>`).join("");
|
|
160
|
+
const risksHtml = risks.map((item) => `<li>${esc(describe(item))}</li>`).join("") || "<li>当前没有未处置风险</li>";
|
|
161
|
+
const actionsHtml = actions.map((item) => `<li>${esc(describe(item))}</li>`).join("") || "<li>无需人工后续动作</li>";
|
|
162
|
+
const stageHtml = Object.entries(record(current.stages)).map(([name, status]) =>
|
|
163
|
+
`<tr><td>${esc(name)}</td><td>${pill(status)}</td></tr>`
|
|
164
|
+
).join("") || '<tr><td colspan="2">没有阶段状态记录</td></tr>';
|
|
165
|
+
const fileHtml = files.map((item) =>
|
|
166
|
+
`<tr><td><code>${esc(item.path || item.file)}</code></td><td class="positive">+${esc(item.insertions ?? 0)}</td><td class="negative">-${esc(item.deletions ?? 0)}</td></tr>`
|
|
167
|
+
).join("") || '<tr><td colspan="3">没有变更文件证据</td></tr>';
|
|
168
|
+
const timelineHtml = timeline.map((item) =>
|
|
169
|
+
`<tr><td>${esc(item.phase || item.stage || "-")}</td><td>${esc(item.attempt || "-")}</td><td>${pill(item.status || item.result || item.type)}</td><td>${esc(item.executorTool || item.executor_tool || item.summary || "-")}</td></tr>`
|
|
170
|
+
).join("") || '<tr><td colspan="4">没有时间线记录</td></tr>';
|
|
171
|
+
const commandHtml = commands.map((item) =>
|
|
172
|
+
`<tr><td>${esc(item.phase || "-")}</td><td><code>${esc(item.command || "")}</code></td><td>${pill(Number(item.exit_code ?? item.exitCode) === 0 ? "OK" : "FAIL")}</td></tr>`
|
|
173
|
+
).join("") || '<tr><td colspan="3">没有命令证据</td></tr>';
|
|
174
|
+
const candidate = record(release.candidate);
|
|
175
|
+
const releaseHtml = recordOnly ? "" : `
|
|
176
|
+
<article class="card"><h2>发布与候选</h2>
|
|
177
|
+
<div class="fact"><span>候选证明</span>${pill(candidate.ok ? "OK" : candidate.code || "NOT_RUN")}</div>
|
|
178
|
+
<div class="fact"><span>发布资格</span>${pill(release.eligible ? "OK" : release.decision || "BLOCKED")}</div>
|
|
179
|
+
</article>`;
|
|
180
|
+
const remoteCost = record(measurements.remoteCost);
|
|
181
|
+
const remoteCostTotals = record(remoteCost.totals);
|
|
182
|
+
const storage = record(measurements.artifactStorage);
|
|
72
183
|
const projection = record(data.projection);
|
|
73
|
-
const wallLabel = timing.workflowWallClockMs != null
|
|
74
|
-
? durationMs(timing.workflowWallClockMs)
|
|
75
|
-
: (durations.totalLabel || duration(durations.totalMinutes));
|
|
76
|
-
const timingColumnsHtml = `
|
|
77
|
-
<article class="card" id="timingColumns"><h2>时间守恒 / Timing Conservation</h2>
|
|
78
|
-
<div class="row"><div><strong>全流程墙钟</strong><small>workflowWallClock</small></div><span>${esc(durationMs(timing.workflowWallClockMs))}</span></div>
|
|
79
|
-
<div class="row"><div><strong>归因阶段并集</strong><small>attributedStageUnion</small></div><span>${esc(durationMs(timing.attributedStageUnionMs))}</span></div>
|
|
80
|
-
<div class="row"><div><strong>远端等待</strong><small>externalWait</small></div><span>${esc(durationMs(timing.externalWaitMs))}</span></div>
|
|
81
|
-
<div class="row"><div><strong>暂停</strong><small>paused</small></div><span>${esc(durationMs(timing.pausedMs))}</span></div>
|
|
82
|
-
<div class="row"><div><strong>未归因</strong><small>agentOrToolUnattributed</small></div><span>${esc(durationMs(timing.agentOrToolUnattributedMs))}</span></div>
|
|
83
|
-
<p><small>conservationDeltaMs=<code>${esc(timing.conservationDeltaMs ?? "N/A")}</code> · reportCutoffAt=<code id="reportCutoffAt">${esc(timing.reportCutoffAt || "N/A")}</code> · active-only 不冒充墙钟</small></p>
|
|
84
|
-
</article>`;
|
|
85
|
-
const maxMinutes = Math.max(1, ...durationStages.map((item) => number(item.minutes)));
|
|
86
|
-
const diff = record(data.diffStat);
|
|
87
|
-
const risks = list(data.knownRisks);
|
|
88
|
-
const actions = list(data.manualActions);
|
|
89
|
-
const files = list(data.changedFiles);
|
|
90
|
-
const commands = list(record(data.reportPipeline).commands);
|
|
91
|
-
const timeline = list(data.timeline);
|
|
92
|
-
const statusReasons = list(data.finalStatusReasons);
|
|
93
|
-
const reasonHtml = statusReasons.length
|
|
94
|
-
? `<small>${statusReasons.map((item) => esc(item)).join(" · ")}</small>`
|
|
95
|
-
: "";
|
|
96
|
-
|
|
97
|
-
const stageHtml = stages.map(([name, status]) => `<div class="row"><span>${esc(name)}</span>${pill(status)}</div>`).join("") || '<p class="empty">没有阶段状态记录</p>';
|
|
98
|
-
const verificationHtml = [
|
|
99
|
-
["单元测试", unit.status || ((number(unit.failures) + number(unit.errors)) > 0 ? "FAIL" : (number(unit.run) > 0 ? "OK" : "NOT_RUN")), `${number(unit.run)} 个 · ${number(unit.failures)} 失败 · ${number(unit.errors)} 错误 · ${number(unit.skipped)} 跳过 · ${number(unit.deselected)} 未选择`],
|
|
100
|
-
["API 测试", api.status || "NOT_RUN", `${number(api.executed)}/${number(api.total)} 已执行 · ${number(api.passed)} 通过 · ${number(api.failed)} 失败 · ${number(api.blocked)} 阻塞 · 通过率 ${api.passRate || "N/A"} · 执行率 ${api.executionRate || "N/A"}`],
|
|
101
|
-
["数据库兼容", verification.dbCompatibility || "NOT_RUN", verification.coverageDisplay || "未记录覆盖率"],
|
|
102
|
-
["性能验证", performance.status || "NOT_RUN", Object.keys(record(performance.metrics)).length ? JSON.stringify(performance.metrics) : "未记录性能指标"]
|
|
103
|
-
].map(([name, status, note]) => `<div class="row"><div><strong>${esc(name)}</strong><small>${esc(note)}</small></div>${pill(status)}</div>`).join("");
|
|
104
|
-
const durationHtml = durationStages.map((item) => {
|
|
105
|
-
const width = Math.max(2, Math.round(number(item.minutes) / maxMinutes * 100));
|
|
106
|
-
const attempts = list(item.attempts).length;
|
|
107
|
-
return `<div class="duration"><div><span>${esc(item.skill || item.stage)}</span><span>${duration(item.minutes)}${attempts > 1 ? ` · ${attempts} 次尝试` : ""}</span></div><i><b style="width:${width}%"></b></i></div>`;
|
|
108
|
-
}).join("") || '<p class="empty">没有可计算的阶段耗时</p>';
|
|
109
|
-
const riskHtml = risks.map((item) => `<li>${esc(describe(item))}</li>`).join("") || "<li>未记录已知风险</li>";
|
|
110
|
-
const actionHtml = actions.map((item) => `<li>${esc(describe(item))}</li>`).join("") || "<li>无需人工后续动作</li>";
|
|
111
|
-
const fileRows = files.map((item) => `<tr><td><code>${esc(item.path || item.file)}</code></td><td class="plus">+${number(item.insertions)}</td><td class="minus">-${number(item.deletions)}</td></tr>`).join("") || '<tr><td colspan="3">没有变更文件证据</td></tr>';
|
|
112
|
-
const timelineRows = timeline.map((item) => `<tr><td>${esc(item.phase || item.stage || "-")}</td><td>${esc(item.attempt || "-")}</td><td>${pill(item.status || item.result || item.type)}</td><td>${esc(item.executorTool || item.executor_tool || item.summary || "-")}</td></tr>`).join("") || '<tr><td colspan="4">没有时间线记录</td></tr>';
|
|
113
|
-
const commandRows = commands.map((item) => `<tr><td>${esc(item.phase || "-")}</td><td><code>${esc(item.command)}</code></td><td>${pill(number(item.exit_code) === 0 ? "OK" : "FAIL")}</td></tr>`).join("") || '<tr><td colspan="3">没有命令证据</td></tr>';
|
|
114
|
-
const releaseCheckHtml = releaseChecks.map(([name, checkValue]) => {
|
|
115
|
-
const check = record(checkValue);
|
|
116
|
-
return `<div class="row"><div><strong>${esc(name)}</strong><small>${esc(check.message || check.code || "")}</small></div>${pill(check.ok ? "OK" : "BLOCKED")}</div>`;
|
|
117
|
-
}).join("") || '<p class="empty">没有 release check 证据</p>';
|
|
118
|
-
const attemptRows = attempts.map((item) => `<tr><td>${esc(item.phase || "-")}</td><td>${esc(item.attempt || "-")}</td><td>${pill(item.terminalStatus || item.status || "UNKNOWN")}</td><td>${esc(durationMs(item.durationMs))}</td></tr>`).join("") || '<tr><td colspan="4">没有 typed attempt 记录</td></tr>';
|
|
119
|
-
const sessionRows = sessions.map((item) => `<tr><td>${esc(item.sessionId || item.id || "-")}</td><td>${esc(item.startedAt || "-")}</td><td>${esc(item.endedAt || "-")}</td><td>${esc(durationMs(item.wallClockMs || item.durationMs))}</td></tr>`).join("") || '<tr><td colspan="4">没有 session 记录</td></tr>';
|
|
120
184
|
|
|
121
|
-
const html = `<!doctype html
|
|
122
|
-
<
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
@media(
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
<
|
|
132
|
-
<
|
|
133
|
-
<
|
|
134
|
-
<
|
|
135
|
-
<
|
|
136
|
-
<
|
|
185
|
+
const html = `<!doctype html>
|
|
186
|
+
<html lang="zh-CN"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
|
|
187
|
+
<title>Harness 最终报告 · ${esc(data.changeName || "未命名变更")}</title>
|
|
188
|
+
<style>
|
|
189
|
+
:root{color-scheme:light dark;--canvas:#f4f6f8;--surface:#fff;--surface-2:#f8fafc;--ink:#172033;--muted:#667085;--line:#dfe5ec;--accent:#2457d6;--good:#087443;--warn:#9a5b00;--bad:#b42318;--shadow:0 8px 26px rgba(22,34,58,.07)}
|
|
190
|
+
@media(prefers-color-scheme:dark){:root{--canvas:#0b1119;--surface:#121a26;--surface-2:#172231;--ink:#eef3f9;--muted:#9aa8ba;--line:#2a384a;--accent:#86abff;--good:#58d59b;--warn:#efbd68;--bad:#ff8b83;--shadow:0 12px 30px rgba(0,0,0,.28)}}
|
|
191
|
+
*{box-sizing:border-box}html,body{max-width:100%;overflow-x:hidden}body{margin:0;background:var(--canvas);color:var(--ink);font:14px/1.55 Inter,"Segoe UI","Microsoft YaHei",sans-serif}main{width:min(1180px,calc(100% - 32px));margin:24px auto 48px}.hero,.card,.metric,details,.verify-card{background:var(--surface);border:1px solid var(--line);border-radius:14px;box-shadow:var(--shadow)}.hero{padding:24px 26px;border-top:4px solid var(--accent)}.kicker{color:var(--accent);font-size:12px;font-weight:800;letter-spacing:.1em}.hero h1{font-size:28px;line-height:1.25;margin:5px 0 7px;overflow-wrap:anywhere}.goal,.muted,small{color:var(--muted)}.outcome{display:flex;align-items:center;gap:10px;margin-top:14px}.record-only{display:inline-flex;margin-top:12px;padding:6px 10px;border-radius:8px;background:var(--surface-2);color:var(--muted)}.metrics{display:grid;grid-template-columns:repeat(5,minmax(0,1fr));gap:10px;margin:11px 0}.metric{min-width:0;padding:13px 14px}.metric strong{display:block;font-size:18px;margin-top:3px;overflow-wrap:anywhere}.card{padding:17px;margin-bottom:11px}.card h2,details summary{font-size:16px;font-weight:760}.card h2{margin:0 0 11px}.verification-grid{display:grid;grid-template-columns:repeat(5,minmax(0,1fr));gap:9px}.verify-card{padding:13px;box-shadow:none;background:var(--surface-2)}.verify-card>div,.fact{display:flex;justify-content:space-between;gap:9px;align-items:center}.verify-name{font-weight:760}.verify-card p{margin:8px 0 0;color:var(--muted);font-size:12px}.two-column{display:grid;grid-template-columns:1.3fr .7fr;gap:11px}.risk-actions{display:grid;grid-template-columns:1fr 1fr;gap:10px}.risk-actions section{padding:12px;border-radius:10px;background:var(--surface-2)}.risk-actions h3{font-size:13px;margin:0 0 6px}.risk-actions ul{margin:0;padding-left:18px;color:var(--muted)}.fact{padding:8px 0;border-bottom:1px solid var(--line)}.fact:last-child{border:0}.pill{display:inline-flex;flex:none;border-radius:999px;padding:3px 9px;font-size:11px;font-weight:800;white-space:nowrap}.pill.success{color:var(--good);background:color-mix(in srgb,var(--good) 13%,transparent)}.pill.warning{color:var(--warn);background:color-mix(in srgb,var(--warn) 14%,transparent)}.pill.danger{color:var(--bad);background:color-mix(in srgb,var(--bad) 13%,transparent)}.pill.neutral{color:var(--accent);background:color-mix(in srgb,var(--accent) 12%,transparent)}details{margin:9px 0}summary{cursor:pointer;padding:13px 16px}details>div{padding:0 16px 16px;overflow:auto}.table-wrap{max-width:100%;overflow:auto}table{width:100%;border-collapse:collapse}th,td{text-align:left;padding:8px 9px;border-bottom:1px solid var(--line);vertical-align:top}th{color:var(--muted);font-size:11px}code{font-family:"Cascadia Code",Consolas,monospace;color:var(--accent);overflow-wrap:anywhere}.positive{color:var(--good)}.negative{color:var(--bad)}dl{display:grid;grid-template-columns:155px minmax(0,1fr);gap:7px 12px}dt{color:var(--muted)}dd{margin:0;overflow-wrap:anywhere}
|
|
192
|
+
@media(max-width:900px){.metrics{grid-template-columns:repeat(2,minmax(0,1fr))}.verification-grid{grid-template-columns:repeat(2,minmax(0,1fr))}.two-column{grid-template-columns:1fr}}
|
|
193
|
+
@media(max-width:600px){main{width:calc(100% - 18px);margin:9px auto 30px}.hero{padding:18px}.hero h1{font-size:23px}.metrics,.verification-grid,.risk-actions{grid-template-columns:1fr}.outcome{align-items:flex-start;flex-direction:column}.card{padding:14px}dl{grid-template-columns:1fr}.metric strong{font-size:17px}}
|
|
194
|
+
</style></head>
|
|
195
|
+
<body><main>
|
|
196
|
+
<section class="hero">
|
|
197
|
+
<div class="kicker">HARNESS · 管理结论</div>
|
|
198
|
+
<h1>${esc(data.changeName || "未命名变更")}</h1>
|
|
199
|
+
<p class="goal">${esc(data.businessGoal || "未记录业务目标")}</p>
|
|
200
|
+
<div class="outcome">${pill(current.status)}<span>${esc(list(current.reasons).join(" · ") || "结论基于事件、验证账本与 Git 事实")}</span></div>
|
|
201
|
+
${recordOnly ? '<div class="record-only">归档意图:仅记录 · 未请求发布</div>' : ""}
|
|
202
|
+
</section>
|
|
203
|
+
<section class="metrics">
|
|
204
|
+
<article class="metric"><small>产品提交</small><strong><code title="${esc(productCommit || "N/A")}">${esc(shortHash(productCommit))}</code></strong></article>
|
|
205
|
+
<article class="metric"><small>验证概览</small><strong>${passedGroups}/${groups.length} 组通过</strong><small>按后端 / Geo / 前端 / 浏览器 / API</small></article>
|
|
206
|
+
<article class="metric"><small>风险与动作</small><strong>${risks.length} / ${actions.length}</strong><small>当前风险 / 人工动作</small></article>
|
|
207
|
+
<article class="metric"><small>全流程耗时</small><strong>${esc(duration(wallClock))}</strong><small>活动 ${esc(duration(timing.stageActiveExecutionMs))}</small></article>
|
|
208
|
+
<article class="metric"><small>代码范围</small><strong>${esc(record(data.diffStat).filesChanged ?? files.length)} 个文件</strong><small class="positive">+${esc(record(data.diffStat).insertions ?? 0)} · <span class="negative">-${esc(record(data.diffStat).deletions ?? 0)}</span></small></article>
|
|
209
|
+
</section>
|
|
210
|
+
<article class="card"><h2>验证概览</h2><div class="verification-grid">${groupHtml}</div></article>
|
|
211
|
+
<section class="two-column">
|
|
212
|
+
<article class="card"><h2>风险与动作</h2><div class="risk-actions"><section><h3>当前风险</h3><ul>${risksHtml}</ul></section><section><h3>人工动作</h3><ul>${actionsHtml}</ul></section></div></article>
|
|
213
|
+
<div>
|
|
214
|
+
${releaseHtml}
|
|
215
|
+
<article class="card"><h2>事实完整性</h2>
|
|
216
|
+
<div class="fact"><span>归档完整性</span>${pill(record(data.archiveIntegrity).ok === false ? "FAIL" : record(data.archiveManifest).checksumStatus || "UNKNOWN")}</div>
|
|
217
|
+
<div class="fact"><span>时间守恒</span>${pill(numeric(timing.conservationDeltaMs) === 0 ? "OK" : "WARN")}</div>
|
|
218
|
+
</article>
|
|
219
|
+
</div>
|
|
220
|
+
</section>
|
|
221
|
+
<details><summary>技术证据 · 验证与时间守恒</summary><div>
|
|
222
|
+
<div class="fact"><span>全流程墙钟 <code title="workflowWallClockMs">workflowWallClock</code></span><strong>${esc(duration(timing.workflowWallClockMs))}</strong></div>
|
|
223
|
+
<div class="fact"><span>活动执行 <code title="stageActiveExecutionMs">stageActiveExecution</code></span><strong>${esc(duration(timing.stageActiveExecutionMs))}</strong></div>
|
|
224
|
+
<div class="fact"><span>远端等待 <code title="externalWaitMs">externalWait</code></span><strong>${esc(duration(timing.externalWaitMs))}</strong></div>
|
|
225
|
+
<div class="fact"><span>暂停 <code title="pausedMs">paused</code></span><strong>${esc(duration(timing.pausedMs))}</strong></div>
|
|
226
|
+
<div class="fact"><span>未归因 <code title="agentOrToolUnattributedMs">agentOrToolUnattributed</code></span><strong>${esc(duration(timing.agentOrToolUnattributedMs))}</strong></div>
|
|
227
|
+
<div class="fact"><span>远端 runner 成本</span><strong>${measurement(remoteCostTotals.runnerMinutes ?? remoteCost.runnerMinutes, " 分钟")}</strong></div>
|
|
228
|
+
<div class="fact"><span>新增制品字节</span><strong>${measurement(storage.bytesAdded, " bytes")}</strong></div>
|
|
229
|
+
<div class="fact"><span>守恒差值 <code>conservationDeltaMs</code></span><strong>${esc(timing.conservationDeltaMs ?? "N/A")}</strong></div>
|
|
230
|
+
</div></details>
|
|
231
|
+
<details><summary>阶段状态</summary><div class="table-wrap"><table><thead><tr><th>阶段</th><th>结果</th></tr></thead><tbody>${stageHtml}</tbody></table></div></details>
|
|
232
|
+
<details><summary>变更文件(${files.length})</summary><div class="table-wrap"><table><thead><tr><th>文件</th><th>新增</th><th>删除</th></tr></thead><tbody>${fileHtml}</tbody></table></div></details>
|
|
233
|
+
<details><summary>执行时间线与工具交接(${timeline.length})</summary><div class="table-wrap"><table><thead><tr><th>阶段</th><th>尝试</th><th>状态</th><th>来源 / 摘要</th></tr></thead><tbody>${timelineHtml}</tbody></table></div></details>
|
|
234
|
+
<details><summary>命令证据(${commands.length})</summary><div class="table-wrap"><table><thead><tr><th>阶段</th><th>命令</th><th>结果</th></tr></thead><tbody>${commandHtml}</tbody></table></div></details>
|
|
235
|
+
<details><summary>技术元数据</summary><div><dl>
|
|
236
|
+
<dt>产品提交</dt><dd><code>${esc(productCommit || "N/A")}</code></dd>
|
|
237
|
+
<dt>产品树哈希</dt><dd><code>${esc(identity.productTreeHash || data.productTreeHash || "N/A")}</code></dd>
|
|
238
|
+
<dt>环境哈希</dt><dd><code>${esc(identity.environmentHash || data.environmentHash || "N/A")}</code></dd>
|
|
239
|
+
<dt>基线提交</dt><dd><code>${esc(identity.baseCommit || data.baseCommit || "N/A")}</code></dd>
|
|
240
|
+
<dt>报告数据版本</dt><dd>${esc(data.schemaVersion || "N/A")}</dd>
|
|
241
|
+
<dt>归档意图</dt><dd title="${esc(release.intent)}">${recordOnly ? "仅记录(未请求发布)" : esc(release.intent || "未声明")}</dd>
|
|
242
|
+
<dt>尝试记录</dt><dd>${attempts.length}</dd>
|
|
243
|
+
<dt>历史质量</dt><dd>${numeric(timing.unclosedAttemptCount) === 0 ? "无未闭合尝试" : `${esc(timing.unclosedAttemptCount ?? "N/A")} 个未闭合尝试`}</dd>
|
|
244
|
+
<dt>投影状态</dt><dd title="Projection / Fallback"><code>${esc(projection.code || projection.mode || "N/A")}</code></dd>
|
|
245
|
+
</dl></div></details>
|
|
137
246
|
</main></body></html>`;
|
|
138
247
|
|
|
139
248
|
fs.mkdirSync(path.dirname(outPath), { recursive: true });
|
package/harness/bundles/general/claude-code/harness-archive/templates/summary-data-template.json
CHANGED
|
@@ -82,6 +82,14 @@
|
|
|
82
82
|
"blocked": 0,
|
|
83
83
|
"passRate": "34/35"
|
|
84
84
|
},
|
|
85
|
+
"browserE2E": {
|
|
86
|
+
"status": "OK | FAIL | PARTIAL | USER_SKIPPED | NOT_RUN",
|
|
87
|
+
"total": 0,
|
|
88
|
+
"passed": 0,
|
|
89
|
+
"failed": 0,
|
|
90
|
+
"skipped": 0,
|
|
91
|
+
"retries": 0
|
|
92
|
+
},
|
|
85
93
|
"dbCompatibility": "OK | BLOCKED_BY_DBA | NOT_RUN",
|
|
86
94
|
"coverageDisplay": "例如 29/29 或 USER_SKIPPED"
|
|
87
95
|
},
|
|
@@ -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=a74978a5fd5c6938; overlay=none; agent=claude-code; do not edit -->
|
|
24
24
|
# harness-codebase-map — 代码库地图
|
|
25
25
|
|
|
26
26
|
## Purpose
|
|
@@ -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=a74978a5fd5c6938; overlay=none; agent=claude-code; do not edit -->
|
|
24
24
|
# harness-knowledge-ingest
|
|
25
25
|
|
|
26
26
|
从 `.harness/archive/**/reports/final/summary-data.json` 抽取项目知识,生成并维护本地 `.harness/knowledge/` 索引。
|