@hunter-harness/workflow-harness 0.2.74 → 0.2.76
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 +30 -1
- 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-query/SKILL.md +1 -1
- package/harness/bundles/general/claude-code/harness-plan/SKILL.md +2 -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 +2 -1
- package/harness/bundles/general/claude-code/harness-run/SKILL.md +2 -1
- package/harness/bundles/general/claude-code/harness-submit/SKILL.md +2 -1
- package/harness/bundles/general/claude-code/harness-sync/SKILL.md +1 -1
- package/harness/bundles/general/claude-code/harness-test/SKILL.md +2 -1
- package/harness/bundles/general/claude-code/scripts/harness_archive.py +90 -10
- package/harness/bundles/general/claude-code/scripts/harness_change.py +95 -0
- package/harness/bundles/general/claude-code/scripts/harness_runtime.py +101 -1
- package/harness/bundles/general/codebuddy/.harness-build.json +1 -1
- package/harness/bundles/general/codebuddy/harness-archive/SKILL.md +30 -1
- 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-query/SKILL.md +1 -1
- package/harness/bundles/general/codebuddy/harness-plan/SKILL.md +2 -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 +2 -1
- package/harness/bundles/general/codebuddy/harness-run/SKILL.md +2 -1
- package/harness/bundles/general/codebuddy/harness-submit/SKILL.md +2 -1
- package/harness/bundles/general/codebuddy/harness-sync/SKILL.md +1 -1
- package/harness/bundles/general/codebuddy/harness-test/SKILL.md +2 -1
- package/harness/bundles/general/codebuddy/scripts/harness_archive.py +90 -10
- package/harness/bundles/general/codebuddy/scripts/harness_change.py +95 -0
- package/harness/bundles/general/codebuddy/scripts/harness_runtime.py +101 -1
- package/harness/bundles/general/codex/.harness-build.json +1 -1
- package/harness/bundles/general/codex/harness-archive/SKILL.md +30 -1
- 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-query/SKILL.md +1 -1
- package/harness/bundles/general/codex/harness-plan/SKILL.md +2 -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 +2 -1
- package/harness/bundles/general/codex/harness-run/SKILL.md +2 -1
- package/harness/bundles/general/codex/harness-submit/SKILL.md +2 -1
- package/harness/bundles/general/codex/harness-sync/SKILL.md +1 -1
- package/harness/bundles/general/codex/harness-test/SKILL.md +2 -1
- package/harness/bundles/general/codex/scripts/harness_archive.py +90 -10
- package/harness/bundles/general/codex/scripts/harness_change.py +95 -0
- package/harness/bundles/general/codex/scripts/harness_runtime.py +101 -1
- package/harness/bundles/general/cursor/.harness-build.json +1 -1
- package/harness/bundles/general/cursor/harness-archive/SKILL.md +30 -1
- 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-query/SKILL.md +1 -1
- package/harness/bundles/general/cursor/harness-plan/SKILL.md +2 -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 +2 -1
- package/harness/bundles/general/cursor/harness-run/SKILL.md +2 -1
- package/harness/bundles/general/cursor/harness-submit/SKILL.md +2 -1
- package/harness/bundles/general/cursor/harness-sync/SKILL.md +1 -1
- package/harness/bundles/general/cursor/harness-test/SKILL.md +2 -1
- package/harness/bundles/general/cursor/scripts/harness_archive.py +90 -10
- package/harness/bundles/general/cursor/scripts/harness_change.py +95 -0
- package/harness/bundles/general/cursor/scripts/harness_runtime.py +101 -1
- package/harness/bundles/java/claude-code/.harness-build.json +1 -1
- package/harness/bundles/java/claude-code/harness-apidoc/SKILL.md +2 -1
- package/harness/bundles/java/claude-code/harness-archive/SKILL.md +30 -1
- 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-query/SKILL.md +1 -1
- package/harness/bundles/java/claude-code/harness-package/SKILL.md +2 -1
- package/harness/bundles/java/claude-code/harness-plan/SKILL.md +2 -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 +2 -1
- package/harness/bundles/java/claude-code/harness-run/SKILL.md +2 -1
- package/harness/bundles/java/claude-code/harness-submit/SKILL.md +2 -1
- package/harness/bundles/java/claude-code/harness-sync/SKILL.md +1 -1
- package/harness/bundles/java/claude-code/harness-test/SKILL.md +2 -1
- package/harness/bundles/java/claude-code/scripts/harness_archive.py +90 -10
- package/harness/bundles/java/claude-code/scripts/harness_change.py +95 -0
- package/harness/bundles/java/claude-code/scripts/harness_runtime.py +101 -1
- package/harness/bundles/java/codebuddy/.harness-build.json +1 -1
- package/harness/bundles/java/codebuddy/harness-apidoc/SKILL.md +2 -1
- package/harness/bundles/java/codebuddy/harness-archive/SKILL.md +30 -1
- 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-query/SKILL.md +1 -1
- package/harness/bundles/java/codebuddy/harness-package/SKILL.md +2 -1
- package/harness/bundles/java/codebuddy/harness-plan/SKILL.md +2 -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 +2 -1
- package/harness/bundles/java/codebuddy/harness-run/SKILL.md +2 -1
- package/harness/bundles/java/codebuddy/harness-submit/SKILL.md +2 -1
- package/harness/bundles/java/codebuddy/harness-sync/SKILL.md +1 -1
- package/harness/bundles/java/codebuddy/harness-test/SKILL.md +2 -1
- package/harness/bundles/java/codebuddy/scripts/harness_archive.py +90 -10
- package/harness/bundles/java/codebuddy/scripts/harness_change.py +95 -0
- package/harness/bundles/java/codebuddy/scripts/harness_runtime.py +101 -1
- package/harness/bundles/java/codex/.harness-build.json +1 -1
- package/harness/bundles/java/codex/harness-apidoc/SKILL.md +2 -1
- package/harness/bundles/java/codex/harness-archive/SKILL.md +30 -1
- 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-query/SKILL.md +1 -1
- package/harness/bundles/java/codex/harness-package/SKILL.md +2 -1
- package/harness/bundles/java/codex/harness-plan/SKILL.md +2 -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 +2 -1
- package/harness/bundles/java/codex/harness-run/SKILL.md +2 -1
- package/harness/bundles/java/codex/harness-submit/SKILL.md +2 -1
- package/harness/bundles/java/codex/harness-sync/SKILL.md +1 -1
- package/harness/bundles/java/codex/harness-test/SKILL.md +2 -1
- package/harness/bundles/java/codex/scripts/harness_archive.py +90 -10
- package/harness/bundles/java/codex/scripts/harness_change.py +95 -0
- package/harness/bundles/java/codex/scripts/harness_runtime.py +101 -1
- package/harness/bundles/java/cursor/.harness-build.json +1 -1
- package/harness/bundles/java/cursor/harness-apidoc/SKILL.md +2 -1
- package/harness/bundles/java/cursor/harness-archive/SKILL.md +30 -1
- 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-query/SKILL.md +1 -1
- package/harness/bundles/java/cursor/harness-package/SKILL.md +2 -1
- package/harness/bundles/java/cursor/harness-plan/SKILL.md +2 -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 +2 -1
- package/harness/bundles/java/cursor/harness-run/SKILL.md +2 -1
- package/harness/bundles/java/cursor/harness-submit/SKILL.md +2 -1
- package/harness/bundles/java/cursor/harness-sync/SKILL.md +1 -1
- package/harness/bundles/java/cursor/harness-test/SKILL.md +2 -1
- package/harness/bundles/java/cursor/scripts/harness_archive.py +90 -10
- package/harness/bundles/java/cursor/scripts/harness_change.py +95 -0
- package/harness/bundles/java/cursor/scripts/harness_runtime.py +101 -1
- 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 +5 -5
- package/package.json +1 -1
|
@@ -3,7 +3,7 @@ name: harness-apidoc
|
|
|
3
3
|
description: 从 Java 后端代码生成结构化前端 API 文档(Controller/VO/ErrorCode)。仅当用户显式调用
|
|
4
4
|
/harness-apidoc 时使用,不得自动触发。
|
|
5
5
|
---
|
|
6
|
-
<!-- generated by harness_deploy.py; core=
|
|
6
|
+
<!-- generated by harness_deploy.py; core=d8613ac610bcccc8; overlay=java; agent=codebuddy; do not edit -->
|
|
7
7
|
# harness-apidoc — 前端接口文档(Java overlay)
|
|
8
8
|
|
|
9
9
|
## Purpose
|
|
@@ -17,6 +17,7 @@ description: 从 Java 后端代码生成结构化前端 API 文档(Controller/
|
|
|
17
17
|
|
|
18
18
|
## 统一读取协议
|
|
19
19
|
|
|
20
|
+
0. **脚本在 `<skills-root>/scripts/` 共享,不在每个 skill 子目录下** — 实际形态是 `.codebuddy/skills/scripts/harness_*.py`(`.claude`/`.cursor`/`.codex` 同理),**没有** `.../skills/harness-<phase>/scripts/`。plan、run/test、archive 三份执行日志里都先猜成后者、报 `No such file` 再靠 Search 找回来;照第一种写法直接用
|
|
20
21
|
1. **`.harness/changes/<change-name>/` 是唯一真相源** — 所有输入从该目录读取,产物写入对应子目录
|
|
21
22
|
2. **change-name 优先从 frontmatter 读取** — `plans/*-design.md`、`spec/*-design.md`、`plans/*-plan.md` 的 YAML `change-name`
|
|
22
23
|
3. **frontmatter 缺失时兼容旧格式** — 从路径推断,标记 `🟡 legacy-plan`,不失败
|
|
@@ -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=d8613ac610bcccc8; overlay=java; agent=codebuddy; do not edit -->
|
|
7
7
|
# harness-archive — 变更归档
|
|
8
8
|
|
|
9
9
|
## Purpose
|
|
@@ -38,6 +38,7 @@ description: 归档所有变更产出(计划/测试报告/审查/SQL/API文档
|
|
|
38
38
|
|
|
39
39
|
## 统一读取协议
|
|
40
40
|
|
|
41
|
+
0. **脚本在 `<skills-root>/scripts/` 共享,不在每个 skill 子目录下** — 实际形态是 `.codebuddy/skills/scripts/harness_*.py`(`.claude`/`.cursor`/`.codex` 同理),**没有** `.../skills/harness-<phase>/scripts/`。plan、run/test、archive 三份执行日志里都先猜成后者、报 `No such file` 再靠 Search 找回来;照第一种写法直接用
|
|
41
42
|
1. **`.harness/changes/<change-name>/` 是唯一真相源** — 所有输入从该目录读取,产物写入对应子目录
|
|
42
43
|
2. **change-name 优先从 frontmatter 读取** — `plans/*-design.md`、`spec/*-design.md`、`plans/*-plan.md` 的 YAML `change-name`
|
|
43
44
|
3. **frontmatter 缺失时兼容旧格式** — 从路径推断,标记 `🟡 legacy-plan`,不失败
|
|
@@ -125,6 +126,13 @@ description: 归档所有变更产出(计划/测试报告/审查/SQL/API文档
|
|
|
125
126
|
|
|
126
127
|
`execute` 内部运行自动门禁。实际计划最后阶段已终态、边界快照存在且 `status.archivable=true` 时直接执行;未满足时返回阻断且不移动原目录。用户取消后不得重跑。
|
|
127
128
|
|
|
129
|
+
### 二·A、两个常见阻断的正规出路(不要自己拼 python -c)
|
|
130
|
+
|
|
131
|
+
| 阻断 | 出路 |
|
|
132
|
+
|------|------|
|
|
133
|
+
| `SENSITIVE_EVIDENCE_UNQUARANTINED` | `python <skills-root>/scripts/harness_runtime.py quarantine-evidence --project . --change-dir ".harness/changes/<cn>" --file "<相对 change-dir 的路径>" --reason "<为什么是敏感证据>" --json`(`--file` 可重复)。私有根默认已与项目同盘、且在项目根之外——**不要**手工指定项目内的路径,归档的密钥扫描会以 `SECRET_SCAN_PRIVATE_PATH_IN_COPY_ROOT` 拒绝 |
|
|
134
|
+
| `DIFF_ZERO_WITH_NONEMPTY_COMMIT`(提交范围非空但 filesChanged=0) | 契约缺 `ownership.productPaths`,全部改动被判为 `foreignPaths`。用 `python <skills-root>/scripts/harness_change.py declare-ownership --change <cn> --product-path "<目录前缀或精确文件>" --json` 按计划的实际改动范围声明(可重复;只收精确路径,不支持通配)。**不要**手改 `change-context.json` |
|
|
135
|
+
|
|
128
136
|
### 三、文件移动只用内置工具或 PowerShell
|
|
129
137
|
|
|
130
138
|
移动用 Read+Write+验证 或 PowerShell;**禁止 Bash mv/cp/rm**。移动失败时不删除原目录,报错退出让用户手动处理。
|
|
@@ -186,6 +194,27 @@ git 命令通过 `powershell.exe -Command "..."` 执行;archive-meta.md 和 su
|
|
|
186
194
|
- `.harness/archive/YYYY-MM-DD-<change-name>/events.ndjson` — 结构化事件层(新流程推荐;旧 archive 可缺失)
|
|
187
195
|
- `.harness/archive/YYYY-MM-DD-<change-name>/reports/final/summary-data.json` — 最终报告数据源
|
|
188
196
|
- `.harness/archive/YYYY-MM-DD-<change-name>/evidence/archive-manifest-before.json` / `archive-manifest-after.json` — 归档前后 manifest/checksum
|
|
197
|
+
- `.harness/state/local/archive-packages/<change-name>.remote.json` — 远端上传回执(`archiveId`/`archiveStatus`/`knowledgeStatus`/`uploadStatus`/`fileCount`)
|
|
198
|
+
|
|
199
|
+
### 结束报告必须回显两条上传结果 ⚠️
|
|
200
|
+
|
|
201
|
+
归档会做**两次**独立上传,二者成败无关,必须分别如实回显:
|
|
202
|
+
|
|
203
|
+
| 上传 | 载什么 | 结果在哪 |
|
|
204
|
+
|------|--------|---------|
|
|
205
|
+
| `steps.archive_push` | 归档 ZIP(核心产物) | `archiveId`/`archiveStatus`/`uploadStatus`/`knowledgeStatus`,同时落盘 `state/local/archive-packages/<cn>.remote.json` |
|
|
206
|
+
| `steps.managed_snapshot_push` | **归档交付物(每个变更目录的 `plans/`、`spec/`、`reports/`、`docs/`)、`.harness/codebase` 架构地图、`.harness/rules`、`.harness/project.yaml`、指令入口** | `ok`/`reasonCode`/`exitCode`/`cliCode`/`detail` |
|
|
207
|
+
|
|
208
|
+
走 `harness-push`(remote-sync,产生分支快照)而非 legacy `push`(proposal 管道)。
|
|
209
|
+
归档目录下的 `runtime/`、`meta/`、`evidence/`、`logs/`、`fixback/`、`.publication-staging/`
|
|
210
|
+
等过程文件不上传;交付物目录里的 `*.log`、`*.tmp`、凭证与 `.env*` 也仍被安全规则拦下。
|
|
211
|
+
|
|
212
|
+
```
|
|
213
|
+
远端归档:arc_3ff325bb…(archiveStatus=durable,uploadStatus=ready,knowledgeStatus=ready,8 文件)
|
|
214
|
+
受管快照:❌ MANAGED_SNAPSHOT_UPLOAD_FAILED(exitCode=1,detail=…)→ 归档 plan/spec/report 与规则/架构地图未上平台,需 npx hunter-harness harness-push --scope config,rules,architecture,instructions,branch_files --yes --non-interactive 重试
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
**第二条尤其不能省**:它是 best-effort,失败不阻断归档,于是最容易被淹没成一句警告——而平台上"分支文件里没有 plan/spec"正是它失败的后果。成功时也要写明 `submitted` 数量;`ok=false` 时必须带上 `reasonCode` 与 `detail`,不得省略或写成"已完成"。
|
|
189
218
|
|
|
190
219
|
## 渐进披露
|
|
191
220
|
|
|
@@ -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=d8613ac610bcccc8; overlay=java; 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=
|
|
5
|
+
<!-- generated by harness_deploy.py; core=d8613ac610bcccc8; overlay=java; 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=d8613ac610bcccc8; overlay=java; agent=codebuddy; do not edit -->
|
|
6
6
|
# harness-knowledge-query
|
|
7
7
|
|
|
8
8
|
项目知识以 Hunter Platform 的服务端索引为唯一真源。客户端只提交查询并消费结果:
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
name: harness-package
|
|
3
3
|
description: 增量模块打包:拉取最新→编译验证→mvn package→汇总 jar/war。仅当用户显式调用 /harness-package 时使用,不得自动触发。
|
|
4
4
|
---
|
|
5
|
-
<!-- generated by harness_deploy.py; core=
|
|
5
|
+
<!-- generated by harness_deploy.py; core=d8613ac610bcccc8; overlay=java; agent=codebuddy; do not edit -->
|
|
6
6
|
# harness-package — 增量模块打包(Java overlay)
|
|
7
7
|
|
|
8
8
|
## Purpose
|
|
@@ -23,6 +23,7 @@ description: 增量模块打包:拉取最新→编译验证→mvn package→
|
|
|
23
23
|
|
|
24
24
|
## 统一读取协议
|
|
25
25
|
|
|
26
|
+
0. **脚本在 `<skills-root>/scripts/` 共享,不在每个 skill 子目录下** — 实际形态是 `.codebuddy/skills/scripts/harness_*.py`(`.claude`/`.cursor`/`.codex` 同理),**没有** `.../skills/harness-<phase>/scripts/`。plan、run/test、archive 三份执行日志里都先猜成后者、报 `No such file` 再靠 Search 找回来;照第一种写法直接用
|
|
26
27
|
1. **`.harness/changes/<change-name>/` 是唯一真相源** — 所有输入从该目录读取,产物写入对应子目录
|
|
27
28
|
2. **change-name 优先从 frontmatter 读取** — `plans/*-design.md`、`spec/*-design.md`、`plans/*-plan.md` 的 YAML `change-name`
|
|
28
29
|
3. **frontmatter 缺失时兼容旧格式** — 从路径推断,标记 `🟡 legacy-plan`,不失败
|
|
@@ -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=d8613ac610bcccc8; overlay=java; agent=codebuddy; do not edit -->
|
|
7
7
|
# harness-plan — 需求规划
|
|
8
8
|
|
|
9
9
|
## Purpose
|
|
@@ -20,6 +20,7 @@ description: 将需求转化为设计文档+实施计划+完整测试场景表
|
|
|
20
20
|
|
|
21
21
|
## 统一读取协议
|
|
22
22
|
|
|
23
|
+
0. **脚本在 `<skills-root>/scripts/` 共享,不在每个 skill 子目录下** — 实际形态是 `.codebuddy/skills/scripts/harness_*.py`(`.claude`/`.cursor`/`.codex` 同理),**没有** `.../skills/harness-<phase>/scripts/`。plan、run/test、archive 三份执行日志里都先猜成后者、报 `No such file` 再靠 Search 找回来;照第一种写法直接用
|
|
23
24
|
1. **`.harness/changes/<change-name>/` 是唯一真相源** — 所有输入从该目录读取,产物写入对应子目录
|
|
24
25
|
2. **change-name 优先从 frontmatter 读取** — `plans/*-design.md`、`spec/*-design.md`、`plans/*-plan.md` 的 YAML `change-name`
|
|
25
26
|
3. **frontmatter 缺失时兼容旧格式** — 从路径推断,标记 `🟡 legacy-plan`,不失败
|
|
@@ -3,7 +3,7 @@ name: harness-pull
|
|
|
3
3
|
description: 从 Hunter Platform 下拉配置/规则/架构/指令(及显式来源分支的分支文件恢复)。仅当用户显式调用
|
|
4
4
|
/harness-pull 或明确说'从平台拉取/恢复'时使用;不得自动触发。
|
|
5
5
|
---
|
|
6
|
-
<!-- generated by harness_deploy.py; core=
|
|
6
|
+
<!-- generated by harness_deploy.py; core=d8613ac610bcccc8; overlay=java; agent=codebuddy; do not edit -->
|
|
7
7
|
# harness-pull — 从 Hunter Platform 下拉与恢复
|
|
8
8
|
|
|
9
9
|
## Purpose
|
|
@@ -3,7 +3,7 @@ name: harness-push
|
|
|
3
3
|
description: 上传本地配置/规则/架构/指令(及显式归档)到 Hunter Platform。仅当用户显式调用 /harness-push
|
|
4
4
|
或明确说'上传到平台'时使用;不得因存在本地修改就自动触发。
|
|
5
5
|
---
|
|
6
|
-
<!-- generated by harness_deploy.py; core=
|
|
6
|
+
<!-- generated by harness_deploy.py; core=d8613ac610bcccc8; overlay=java; agent=codebuddy; do not edit -->
|
|
7
7
|
# harness-push — 上传到 Hunter Platform
|
|
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=
|
|
7
|
+
<!-- generated by harness_deploy.py; core=d8613ac610bcccc8; overlay=java; agent=codebuddy; do not edit -->
|
|
8
8
|
# harness-review — 代码审查
|
|
9
9
|
|
|
10
10
|
## Purpose
|
|
@@ -30,6 +30,7 @@ description: 6维度代码审查(架构/安全/规范/兼容/测试/性能)
|
|
|
30
30
|
|
|
31
31
|
## 统一读取协议
|
|
32
32
|
|
|
33
|
+
0. **脚本在 `<skills-root>/scripts/` 共享,不在每个 skill 子目录下** — 实际形态是 `.codebuddy/skills/scripts/harness_*.py`(`.claude`/`.cursor`/`.codex` 同理),**没有** `.../skills/harness-<phase>/scripts/`。plan、run/test、archive 三份执行日志里都先猜成后者、报 `No such file` 再靠 Search 找回来;照第一种写法直接用
|
|
33
34
|
1. **`.harness/changes/<change-name>/` 是唯一真相源** — 所有输入从该目录读取,产物写入对应子目录
|
|
34
35
|
2. **change-name 优先从 frontmatter 读取** — `plans/*-design.md`、`spec/*-design.md`、`plans/*-plan.md` 的 YAML `change-name`
|
|
35
36
|
3. **frontmatter 缺失时兼容旧格式** — 从路径推断,标记 `🟡 legacy-plan`,不失败
|
|
@@ -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=
|
|
6
|
+
<!-- generated by harness_deploy.py; core=d8613ac610bcccc8; overlay=java; agent=codebuddy; do not edit -->
|
|
7
7
|
# harness-run — 需求编码
|
|
8
8
|
|
|
9
9
|
## Purpose
|
|
@@ -40,6 +40,7 @@ description: 按变更簇执行 TDD 编码循环(RED→GREEN→REFACTOR→编
|
|
|
40
40
|
|
|
41
41
|
## 统一读取协议
|
|
42
42
|
|
|
43
|
+
0. **脚本在 `<skills-root>/scripts/` 共享,不在每个 skill 子目录下** — 实际形态是 `.codebuddy/skills/scripts/harness_*.py`(`.claude`/`.cursor`/`.codex` 同理),**没有** `.../skills/harness-<phase>/scripts/`。plan、run/test、archive 三份执行日志里都先猜成后者、报 `No such file` 再靠 Search 找回来;照第一种写法直接用
|
|
43
44
|
1. **`.harness/changes/<change-name>/` 是唯一真相源** — 所有输入从该目录读取,产物写入对应子目录
|
|
44
45
|
2. **change-name 优先从 frontmatter 读取** — `plans/*-design.md`、`spec/*-design.md`、`plans/*-plan.md` 的 YAML `change-name`
|
|
45
46
|
3. **frontmatter 缺失时兼容旧格式** — 从路径推断,标记 `🟡 legacy-plan`,不失败
|
|
@@ -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=
|
|
6
|
+
<!-- generated by harness_deploy.py; core=d8613ac610bcccc8; overlay=java; agent=codebuddy; do not edit -->
|
|
7
7
|
# harness-submit — 最终提交(含 worktree 合并)
|
|
8
8
|
|
|
9
9
|
## Purpose
|
|
@@ -29,6 +29,7 @@ description: 最终提交封装:验证→中文 commit→提交/推送;workt
|
|
|
29
29
|
|
|
30
30
|
## 统一读取协议
|
|
31
31
|
|
|
32
|
+
0. **脚本在 `<skills-root>/scripts/` 共享,不在每个 skill 子目录下** — 实际形态是 `.codebuddy/skills/scripts/harness_*.py`(`.claude`/`.cursor`/`.codex` 同理),**没有** `.../skills/harness-<phase>/scripts/`。plan、run/test、archive 三份执行日志里都先猜成后者、报 `No such file` 再靠 Search 找回来;照第一种写法直接用
|
|
32
33
|
1. **`.harness/changes/<change-name>/` 是唯一真相源** — 所有输入从该目录读取,产物写入对应子目录
|
|
33
34
|
2. **change-name 优先从 frontmatter 读取** — `plans/*-design.md`、`spec/*-design.md`、`plans/*-plan.md` 的 YAML `change-name`
|
|
34
35
|
3. **frontmatter 缺失时兼容旧格式** — 从路径推断,标记 `🟡 legacy-plan`,不失败
|
|
@@ -4,7 +4,7 @@ description: Use when the user asks to synchronize, refresh, or validate Harness
|
|
|
4
4
|
metadata, adapters, remote knowledge ownership, instruction entrypoints,
|
|
5
5
|
config origins, or CodeGraph status.
|
|
6
6
|
---
|
|
7
|
-
<!-- generated by harness_deploy.py; core=
|
|
7
|
+
<!-- generated by harness_deploy.py; core=d8613ac610bcccc8; overlay=java; agent=codebuddy; do not edit -->
|
|
8
8
|
# harness-sync
|
|
9
9
|
|
|
10
10
|
## Purpose
|
|
@@ -3,7 +3,7 @@ name: harness-test
|
|
|
3
3
|
description: 测试执行:读取场景表,执行单元测试+API接口测试+数据兼容验证,输出测试报告。仅当用户显式调用 /harness-test
|
|
4
4
|
时使用;不得在 run 结束后自动接续执行。
|
|
5
5
|
---
|
|
6
|
-
<!-- generated by harness_deploy.py; core=
|
|
6
|
+
<!-- generated by harness_deploy.py; core=d8613ac610bcccc8; overlay=java; agent=codebuddy; do not edit -->
|
|
7
7
|
# harness-test — 测试执行
|
|
8
8
|
|
|
9
9
|
## Purpose
|
|
@@ -31,6 +31,7 @@ description: 测试执行:读取场景表,执行单元测试+API接口测试
|
|
|
31
31
|
|
|
32
32
|
## 统一读取协议
|
|
33
33
|
|
|
34
|
+
0. **脚本在 `<skills-root>/scripts/` 共享,不在每个 skill 子目录下** — 实际形态是 `.codebuddy/skills/scripts/harness_*.py`(`.claude`/`.cursor`/`.codex` 同理),**没有** `.../skills/harness-<phase>/scripts/`。plan、run/test、archive 三份执行日志里都先猜成后者、报 `No such file` 再靠 Search 找回来;照第一种写法直接用
|
|
34
35
|
1. **`.harness/changes/<change-name>/` 是唯一真相源** — 所有输入从该目录读取,产物写入对应子目录
|
|
35
36
|
2. **change-name 优先从 frontmatter 读取** — `plans/*-design.md`、`spec/*-design.md`、`plans/*-plan.md` 的 YAML `change-name`
|
|
36
37
|
3. **frontmatter 缺失时兼容旧格式** — 从路径推断,标记 `🟡 legacy-plan`,不失败
|
|
@@ -8552,8 +8552,17 @@ def cmd_finalize(
|
|
|
8552
8552
|
payload["knowledgeMaintenance"] = _knowledge_maintenance_from_archive_push(
|
|
8553
8553
|
push_result
|
|
8554
8554
|
)
|
|
8555
|
-
|
|
8556
|
-
payload
|
|
8555
|
+
remote_durability = remote_durable_archive_durability(
|
|
8556
|
+
payload.get("archiveDurability"), push_result
|
|
8557
|
+
)
|
|
8558
|
+
if remote_durability is not None:
|
|
8559
|
+
payload["archiveDurability"] = remote_durability
|
|
8560
|
+
# 报告必须跟着改口:上传发生在 summary 落盘之后,不回写就会出现
|
|
8561
|
+
# "回执 durable / 报告 local-only" 的自相矛盾
|
|
8562
|
+
if not persist_archive_durability(summary_path, remote_durability):
|
|
8563
|
+
warnings.append(
|
|
8564
|
+
"远端归档已持久化,但 summary-data.json 的 archiveDurability 回写失败"
|
|
8565
|
+
)
|
|
8557
8566
|
if push_result.get("warning"):
|
|
8558
8567
|
warnings.append(str(push_result["warning"]))
|
|
8559
8568
|
|
|
@@ -9121,8 +9130,57 @@ def build_archive_package(
|
|
|
9121
9130
|
}
|
|
9122
9131
|
|
|
9123
9132
|
|
|
9133
|
+
def remote_durable_archive_durability(
|
|
9134
|
+
current: Any, push_result: Mapping[str, Any]
|
|
9135
|
+
) -> dict[str, Any] | None:
|
|
9136
|
+
"""远端上传成功后的耐久性投影;未 durable 时返回 None。
|
|
9137
|
+
|
|
9138
|
+
summary-data.json 在归档流程早期就以 ARCHIVED_LOCAL_ONLY 写盘,而 ZIP 上传在
|
|
9139
|
+
那之后才发生。此前上传成功只改内存里的 payload、还把对象覆盖成裸字符串,落盘
|
|
9140
|
+
报告从没跟着改——于是同一次归档,回执说 durable、报告说 local-only,平台和用户
|
|
9141
|
+
读到的是后者。这里保持对象形状并带上回执标识,供调用方同时更新 payload 与文件。
|
|
9142
|
+
"""
|
|
9143
|
+
if str(push_result.get("archiveStatus") or "") != "durable":
|
|
9144
|
+
return None
|
|
9145
|
+
projected = dict(current) if isinstance(current, dict) else {}
|
|
9146
|
+
projected.update({
|
|
9147
|
+
"status": "ARCHIVED_REMOTE_DURABLE",
|
|
9148
|
+
# 已远端持久化:不再声称"只存在于本地、可能随工作区丢失"
|
|
9149
|
+
"risk": None,
|
|
9150
|
+
"archiveId": push_result.get("archiveId"),
|
|
9151
|
+
"uploadStatus": push_result.get("uploadStatus"),
|
|
9152
|
+
"knowledgeStatus": push_result.get("knowledgeStatus"),
|
|
9153
|
+
})
|
|
9154
|
+
return projected
|
|
9155
|
+
|
|
9156
|
+
|
|
9157
|
+
def persist_archive_durability(summary_path: Path, durability: dict[str, Any]) -> bool:
|
|
9158
|
+
"""把耐久性结论回写到已落盘的 summary,其余字段原样保留。
|
|
9159
|
+
|
|
9160
|
+
summary-data.json 本就被排除在归档校验和覆盖之外(它在覆盖快照之后才写),
|
|
9161
|
+
所以这次回写不会破坏 manifest 对账。
|
|
9162
|
+
"""
|
|
9163
|
+
try:
|
|
9164
|
+
summary = json.loads(Path(summary_path).read_text(encoding="utf-8-sig"))
|
|
9165
|
+
except (OSError, json.JSONDecodeError):
|
|
9166
|
+
return False
|
|
9167
|
+
if not isinstance(summary, dict):
|
|
9168
|
+
return False
|
|
9169
|
+
summary["archiveDurability"] = durability
|
|
9170
|
+
try:
|
|
9171
|
+
write_json(Path(summary_path), summary)
|
|
9172
|
+
except OSError:
|
|
9173
|
+
return False
|
|
9174
|
+
return True
|
|
9175
|
+
|
|
9176
|
+
|
|
9124
9177
|
def auto_push_managed_snapshot(project_root: Path) -> dict[str, Any]:
|
|
9125
|
-
"""Best-effort upload of rules
|
|
9178
|
+
"""Best-effort upload of rules, Codebase Map and archive deliverables via remote sync.
|
|
9179
|
+
|
|
9180
|
+
走 `harness-push`(remote-sync)而非 legacy `push`(proposal)。后者写的是
|
|
9181
|
+
project_files_current,不产生分支快照,平台的「分支文件」与「项目资料」两个视图
|
|
9182
|
+
都读不到;归档交付物在那条路上还会被 policy-never 全部跳过。
|
|
9183
|
+
"""
|
|
9126
9184
|
project_root = project_root.resolve()
|
|
9127
9185
|
credentials = _resolve_archive_remote_credentials(project_root, os.environ)
|
|
9128
9186
|
if not credentials["configured"]:
|
|
@@ -9141,11 +9199,16 @@ def auto_push_managed_snapshot(project_root: Path) -> dict[str, Any]:
|
|
|
9141
9199
|
"reasonCode": "MANAGED_SNAPSHOT_UPLOAD_DEFERRED",
|
|
9142
9200
|
"warning": f"项目规则与架构地图上传已延后:{exc}",
|
|
9143
9201
|
}
|
|
9202
|
+
# 显式列出五个 scope 而非 `all`:含义固定,不随 `all` 的展开定义漂移。
|
|
9203
|
+
# branch_files 覆盖归档交付物(plans/spec/reports/docs),其余四个覆盖配置、
|
|
9204
|
+
# 规则、架构地图与指令入口。
|
|
9144
9205
|
command = [
|
|
9145
9206
|
*launcher,
|
|
9146
9207
|
"--yes",
|
|
9147
9208
|
"hunter-harness",
|
|
9148
|
-
"push",
|
|
9209
|
+
"harness-push",
|
|
9210
|
+
"--scope",
|
|
9211
|
+
"config,rules,architecture,instructions,branch_files",
|
|
9149
9212
|
"--yes",
|
|
9150
9213
|
"--non-interactive",
|
|
9151
9214
|
"--json",
|
|
@@ -9159,6 +9222,10 @@ def auto_push_managed_snapshot(project_root: Path) -> dict[str, Any]:
|
|
|
9159
9222
|
command,
|
|
9160
9223
|
cwd=str(project_root),
|
|
9161
9224
|
text=True,
|
|
9225
|
+
# 不指定 encoding 时中文 Windows 会按 cp936 解码 UTF-8,静默损坏 CLI
|
|
9226
|
+
# 的中文输出,并可能让下面的 json.loads 失败。
|
|
9227
|
+
encoding="utf-8",
|
|
9228
|
+
errors="replace",
|
|
9162
9229
|
stdout=subprocess.PIPE,
|
|
9163
9230
|
stderr=subprocess.PIPE,
|
|
9164
9231
|
timeout=180,
|
|
@@ -9176,24 +9243,37 @@ def auto_push_managed_snapshot(project_root: Path) -> dict[str, Any]:
|
|
|
9176
9243
|
except (json.JSONDecodeError, TypeError):
|
|
9177
9244
|
output = None
|
|
9178
9245
|
if completed.returncode != 0 or not isinstance(output, dict):
|
|
9179
|
-
|
|
9246
|
+
# 失败时丢掉 stdout/stderr 等于把线索一起丢了:plan/spec 与规则、架构地图
|
|
9247
|
+
# 全靠这条推送上平台,用户在平台上只会看到"东西没上去",却无从查为什么。
|
|
9248
|
+
# 保留有界的诊断片段(push 本身拒绝上传 secret,这里再截断兜底)。
|
|
9249
|
+
detail = (completed.stderr or completed.stdout or "").strip()
|
|
9250
|
+
failure: dict[str, Any] = {
|
|
9180
9251
|
"ok": False,
|
|
9181
9252
|
"skipped": False,
|
|
9182
9253
|
"exitCode": completed.returncode,
|
|
9183
9254
|
"reasonCode": "MANAGED_SNAPSHOT_UPLOAD_FAILED",
|
|
9255
|
+
"detail": detail[-2_048:],
|
|
9184
9256
|
"warning": (
|
|
9185
|
-
"
|
|
9186
|
-
"
|
|
9257
|
+
"项目规则与架构地图未能同步到平台(归档 plan/spec/report 也随这条推送上传);"
|
|
9258
|
+
"归档不受影响,稍后可运行 npx hunter-harness harness-push "
|
|
9259
|
+
"--scope config,rules,architecture,instructions,branch_files "
|
|
9260
|
+
"--yes --non-interactive 重试。"
|
|
9187
9261
|
),
|
|
9188
9262
|
}
|
|
9263
|
+
cli_code = output.get("code") or output.get("reason_code") if isinstance(output, dict) else None
|
|
9264
|
+
if isinstance(cli_code, str) and cli_code:
|
|
9265
|
+
failure["cliCode"] = cli_code
|
|
9266
|
+
return failure
|
|
9267
|
+
# harness-push 的回执用 summary.applied 计已落盘的操作数(legacy push 用 submitted)。
|
|
9189
9268
|
summary = output.get("summary")
|
|
9190
|
-
|
|
9269
|
+
applied = summary.get("applied", 0) if isinstance(summary, dict) else 0
|
|
9191
9270
|
return {
|
|
9192
9271
|
"ok": bool(output.get("ok", True)),
|
|
9193
9272
|
"skipped": False,
|
|
9194
9273
|
"projectId": output.get("project_id"),
|
|
9195
|
-
"submitted":
|
|
9196
|
-
|
|
9274
|
+
"submitted": applied if isinstance(applied, int) else 0,
|
|
9275
|
+
# 无变更时 harness-push 直接短路为 no_changes,不产生快照——即"仅在有更新时推送"。
|
|
9276
|
+
"unchanged": output.get("outcome") == "no_changes",
|
|
9197
9277
|
}
|
|
9198
9278
|
|
|
9199
9279
|
|
|
@@ -1081,6 +1081,90 @@ def cmd_resolve(args: argparse.Namespace) -> int:
|
|
|
1081
1081
|
)
|
|
1082
1082
|
|
|
1083
1083
|
|
|
1084
|
+
def declare_product_ownership(
|
|
1085
|
+
project_root: Path, change_id: str, *, product_paths: list[str]
|
|
1086
|
+
) -> dict[str, Any]:
|
|
1087
|
+
"""把 `ownership.productPaths` 写进 change 契约。
|
|
1088
|
+
|
|
1089
|
+
这个字段此前没有任何写入方:plan 的 validate_product_ownership 只校验、缺失时
|
|
1090
|
+
软放行,而归档的 compute_ownership_diff 会把全部改动判成 foreignPaths,
|
|
1091
|
+
filesChanged=0 直接触发 DIFF_ZERO_WITH_NONEMPTY_COMMIT。两端口径不一致,中间
|
|
1092
|
+
没工具能补——只能手改契约。这里给出正规入口。
|
|
1093
|
+
|
|
1094
|
+
规则与 plan 校验一致:只收精确文件或目录前缀,不接受通配。
|
|
1095
|
+
"""
|
|
1096
|
+
resolved = resolve_change(project_root, change_id)
|
|
1097
|
+
if not resolved.get("ok"):
|
|
1098
|
+
return resolved
|
|
1099
|
+
change_dir = Path(resolved["changeDir"])
|
|
1100
|
+
normalized = sorted({
|
|
1101
|
+
str(item).strip().replace("\\", "/").removeprefix("./")
|
|
1102
|
+
for item in product_paths
|
|
1103
|
+
if isinstance(item, str) and item.strip()
|
|
1104
|
+
})
|
|
1105
|
+
if not normalized:
|
|
1106
|
+
return {
|
|
1107
|
+
"ok": False,
|
|
1108
|
+
"code": "PLAN_PRODUCT_PATHS_REQUIRED",
|
|
1109
|
+
"message": "至少声明一条 productPaths(精确文件或目录前缀)",
|
|
1110
|
+
}
|
|
1111
|
+
unsupported = sorted(p for p in normalized if any(ch in p for ch in "*?[]"))
|
|
1112
|
+
if unsupported:
|
|
1113
|
+
return {
|
|
1114
|
+
"ok": False,
|
|
1115
|
+
"code": "PLAN_PRODUCT_PATHS_GLOB_UNSUPPORTED",
|
|
1116
|
+
"message": "productPaths 只接受精确文件或目录前缀,不支持通配:" + ", ".join(unsupported),
|
|
1117
|
+
"unsupportedPaths": unsupported,
|
|
1118
|
+
}
|
|
1119
|
+
|
|
1120
|
+
context_path = change_dir / CHANGE_CONTEXT_REL
|
|
1121
|
+
try:
|
|
1122
|
+
context = _read_json(context_path)
|
|
1123
|
+
except (OSError, json.JSONDecodeError) as exc:
|
|
1124
|
+
return {"ok": False, "code": "CHANGE_CONTEXT_INVALID", "message": str(exc)}
|
|
1125
|
+
if not isinstance(context, dict):
|
|
1126
|
+
return {"ok": False, "code": "CHANGE_CONTEXT_INVALID", "message": "change-context.json 不是对象"}
|
|
1127
|
+
|
|
1128
|
+
ownership = context.get("ownership")
|
|
1129
|
+
ownership = dict(ownership) if isinstance(ownership, dict) else {}
|
|
1130
|
+
if ownership.get("productPaths") == normalized:
|
|
1131
|
+
return {
|
|
1132
|
+
"ok": True,
|
|
1133
|
+
"code": "PRODUCT_OWNERSHIP_DECLARED",
|
|
1134
|
+
"idempotent": True,
|
|
1135
|
+
"changeId": change_id,
|
|
1136
|
+
"productPaths": normalized,
|
|
1137
|
+
"path": str(context_path),
|
|
1138
|
+
}
|
|
1139
|
+
ownership["productPaths"] = normalized
|
|
1140
|
+
context["ownership"] = ownership
|
|
1141
|
+
_write_json(context_path, context)
|
|
1142
|
+
return {
|
|
1143
|
+
"ok": True,
|
|
1144
|
+
"code": "PRODUCT_OWNERSHIP_DECLARED",
|
|
1145
|
+
"idempotent": False,
|
|
1146
|
+
"changeId": change_id,
|
|
1147
|
+
"productPaths": normalized,
|
|
1148
|
+
"path": str(context_path),
|
|
1149
|
+
}
|
|
1150
|
+
|
|
1151
|
+
|
|
1152
|
+
def cmd_declare_ownership(args: argparse.Namespace) -> int:
|
|
1153
|
+
project = resolve_main_project_root()
|
|
1154
|
+
payload = declare_product_ownership(
|
|
1155
|
+
project, args.change, product_paths=list(args.product_path)
|
|
1156
|
+
)
|
|
1157
|
+
if payload.get("ok"):
|
|
1158
|
+
emit(payload, as_json=bool(args.json))
|
|
1159
|
+
return 0
|
|
1160
|
+
return emit_error(
|
|
1161
|
+
str(payload.get("code", "PRODUCT_OWNERSHIP_FAILED")),
|
|
1162
|
+
str(payload.get("message", "declare ownership failed")),
|
|
1163
|
+
as_json=bool(args.json),
|
|
1164
|
+
extra={k: v for k, v in payload.items() if k not in {"ok", "message"}},
|
|
1165
|
+
)
|
|
1166
|
+
|
|
1167
|
+
|
|
1084
1168
|
def cmd_migrate(args: argparse.Namespace) -> int:
|
|
1085
1169
|
project = resolve_main_project_root()
|
|
1086
1170
|
if not args.change:
|
|
@@ -1451,6 +1535,17 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
1451
1535
|
p_migrate.add_argument("--change", required=True)
|
|
1452
1536
|
p_migrate.set_defaults(func=cmd_migrate)
|
|
1453
1537
|
|
|
1538
|
+
p_ownership = sub.add_parser(
|
|
1539
|
+
"declare-ownership", parents=[shared],
|
|
1540
|
+
help="declare ownership.productPaths so archive can project the real diff",
|
|
1541
|
+
)
|
|
1542
|
+
p_ownership.add_argument("--change", required=True)
|
|
1543
|
+
p_ownership.add_argument(
|
|
1544
|
+
"--product-path", required=True, action="append",
|
|
1545
|
+
help="exact file or directory prefix (no globs); repeatable",
|
|
1546
|
+
)
|
|
1547
|
+
p_ownership.set_defaults(func=cmd_declare_ownership)
|
|
1548
|
+
|
|
1454
1549
|
p_claim = sub.add_parser("claim", parents=[shared])
|
|
1455
1550
|
p_claim.add_argument("--change", required=True)
|
|
1456
1551
|
p_claim.add_argument("--phase", required=True)
|