@hunter-harness/workflow-harness 0.2.73 → 0.2.75
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 +26 -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 +3 -2
- package/harness/bundles/general/claude-code/harness-submit/SKILL.md +4 -2
- package/harness/bundles/general/claude-code/harness-sync/SKILL.md +1 -1
- package/harness/bundles/general/claude-code/harness-test/SKILL.md +3 -2
- package/harness/bundles/general/claude-code/harness-test/checklist.md +2 -0
- package/harness/bundles/general/claude-code/scripts/harness_archive.py +149 -6
- package/harness/bundles/general/claude-code/scripts/harness_change.py +95 -0
- package/harness/bundles/general/claude-code/scripts/harness_ledger.py +25 -4
- package/harness/bundles/general/claude-code/scripts/harness_profile.py +45 -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 +26 -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 +3 -2
- package/harness/bundles/general/codebuddy/harness-submit/SKILL.md +4 -2
- package/harness/bundles/general/codebuddy/harness-sync/SKILL.md +1 -1
- package/harness/bundles/general/codebuddy/harness-test/SKILL.md +3 -2
- package/harness/bundles/general/codebuddy/harness-test/checklist.md +2 -0
- package/harness/bundles/general/codebuddy/scripts/harness_archive.py +149 -6
- package/harness/bundles/general/codebuddy/scripts/harness_change.py +95 -0
- package/harness/bundles/general/codebuddy/scripts/harness_ledger.py +25 -4
- package/harness/bundles/general/codebuddy/scripts/harness_profile.py +45 -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 +26 -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 +3 -2
- package/harness/bundles/general/codex/harness-submit/SKILL.md +4 -2
- package/harness/bundles/general/codex/harness-sync/SKILL.md +1 -1
- package/harness/bundles/general/codex/harness-test/SKILL.md +3 -2
- package/harness/bundles/general/codex/harness-test/checklist.md +2 -0
- package/harness/bundles/general/codex/scripts/harness_archive.py +149 -6
- package/harness/bundles/general/codex/scripts/harness_change.py +95 -0
- package/harness/bundles/general/codex/scripts/harness_ledger.py +25 -4
- package/harness/bundles/general/codex/scripts/harness_profile.py +45 -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 +26 -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 +3 -2
- package/harness/bundles/general/cursor/harness-submit/SKILL.md +4 -2
- package/harness/bundles/general/cursor/harness-sync/SKILL.md +1 -1
- package/harness/bundles/general/cursor/harness-test/SKILL.md +3 -2
- package/harness/bundles/general/cursor/harness-test/checklist.md +2 -0
- package/harness/bundles/general/cursor/scripts/harness_archive.py +149 -6
- package/harness/bundles/general/cursor/scripts/harness_change.py +95 -0
- package/harness/bundles/general/cursor/scripts/harness_ledger.py +25 -4
- package/harness/bundles/general/cursor/scripts/harness_profile.py +45 -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 +26 -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 +3 -2
- package/harness/bundles/java/claude-code/harness-run/reference.md +348 -348
- package/harness/bundles/java/claude-code/harness-submit/SKILL.md +4 -2
- package/harness/bundles/java/claude-code/harness-sync/SKILL.md +1 -1
- package/harness/bundles/java/claude-code/harness-test/SKILL.md +3 -2
- package/harness/bundles/java/claude-code/scripts/harness_archive.py +149 -6
- package/harness/bundles/java/claude-code/scripts/harness_change.py +95 -0
- package/harness/bundles/java/claude-code/scripts/harness_ledger.py +25 -4
- package/harness/bundles/java/claude-code/scripts/harness_profile.py +45 -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 +26 -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 +3 -2
- package/harness/bundles/java/codebuddy/harness-run/reference.md +348 -348
- package/harness/bundles/java/codebuddy/harness-submit/SKILL.md +4 -2
- package/harness/bundles/java/codebuddy/harness-sync/SKILL.md +1 -1
- package/harness/bundles/java/codebuddy/harness-test/SKILL.md +3 -2
- package/harness/bundles/java/codebuddy/scripts/harness_archive.py +149 -6
- package/harness/bundles/java/codebuddy/scripts/harness_change.py +95 -0
- package/harness/bundles/java/codebuddy/scripts/harness_ledger.py +25 -4
- package/harness/bundles/java/codebuddy/scripts/harness_profile.py +45 -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 +26 -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 +3 -2
- package/harness/bundles/java/codex/harness-run/reference.md +348 -348
- package/harness/bundles/java/codex/harness-submit/SKILL.md +4 -2
- package/harness/bundles/java/codex/harness-sync/SKILL.md +1 -1
- package/harness/bundles/java/codex/harness-test/SKILL.md +3 -2
- package/harness/bundles/java/codex/scripts/harness_archive.py +149 -6
- package/harness/bundles/java/codex/scripts/harness_change.py +95 -0
- package/harness/bundles/java/codex/scripts/harness_ledger.py +25 -4
- package/harness/bundles/java/codex/scripts/harness_profile.py +45 -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 +26 -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 +3 -2
- package/harness/bundles/java/cursor/harness-run/reference.md +348 -348
- package/harness/bundles/java/cursor/harness-submit/SKILL.md +4 -2
- package/harness/bundles/java/cursor/harness-sync/SKILL.md +1 -1
- package/harness/bundles/java/cursor/harness-test/SKILL.md +3 -2
- package/harness/bundles/java/cursor/scripts/harness_archive.py +149 -6
- package/harness/bundles/java/cursor/scripts/harness_change.py +95 -0
- package/harness/bundles/java/cursor/scripts/harness_ledger.py +25 -4
- package/harness/bundles/java/cursor/scripts/harness_profile.py +45 -0
- package/harness/bundles/java/cursor/scripts/harness_runtime.py +101 -1
- package/harness/manifests/general/claude-code.json +20 -20
- package/harness/manifests/general/codebuddy.json +20 -20
- package/harness/manifests/general/codex.json +20 -20
- package/harness/manifests/general/cursor.json +20 -20
- package/harness/manifests/java/claude-code.json +22 -22
- package/harness/manifests/java/codebuddy.json +22 -22
- package/harness/manifests/java/codex.json +22 -22
- package/harness/manifests/java/cursor.json +22 -22
- package/hunter-workflow-family.json +3 -3
- package/package.json +1 -1
|
@@ -1101,6 +1101,43 @@ def _derive_verification_graph(profile: dict[str, Any]) -> None:
|
|
|
1101
1101
|
}
|
|
1102
1102
|
|
|
1103
1103
|
|
|
1104
|
+
def manual_profile_template(
|
|
1105
|
+
excluded: tuple[str, ...] | list[str], components: list[str]
|
|
1106
|
+
) -> dict[str, Any]:
|
|
1107
|
+
"""歧义时交给调用方的可填骨架。
|
|
1108
|
+
|
|
1109
|
+
骨架本身就是 `empty_profile_skeleton`——defaultsFingerprint、excludedRoots
|
|
1110
|
+
默认集、commands/verificationInputs/verificationGraph 的键都已就位;这里只是
|
|
1111
|
+
补上 commands 的条目形状与 requiredCoverage 取值,省掉调用方去源码里翻。
|
|
1112
|
+
占位值用 `<...>` 包裹,便于替换后 grep 自检。
|
|
1113
|
+
"""
|
|
1114
|
+
template = empty_profile_skeleton(excluded)
|
|
1115
|
+
template["projectType"] = (
|
|
1116
|
+
f"<从 detectedComponents 选定:{'|'.join(components)}>" if components else "<项目类型>"
|
|
1117
|
+
)
|
|
1118
|
+
template["commands"] = {
|
|
1119
|
+
"unitTest": {
|
|
1120
|
+
"command": "<在项目根可直接执行的增量测试命令>",
|
|
1121
|
+
"scope": "incremental",
|
|
1122
|
+
"inputs": ["<相对项目根的 glob,如 <component>/test/**>"],
|
|
1123
|
+
"coverage": "unitTest",
|
|
1124
|
+
"requiredCoverage": "incremental",
|
|
1125
|
+
"dependsOn": [],
|
|
1126
|
+
"source": "user",
|
|
1127
|
+
},
|
|
1128
|
+
"unitTestFull": {
|
|
1129
|
+
"command": "<在项目根可直接执行的全量测试命令>",
|
|
1130
|
+
"scope": "full",
|
|
1131
|
+
"inputs": ["<相对项目根的 glob,如 <component>/**>"],
|
|
1132
|
+
"coverage": "unitTestFull",
|
|
1133
|
+
"requiredCoverage": "module",
|
|
1134
|
+
"dependsOn": [],
|
|
1135
|
+
"source": "user",
|
|
1136
|
+
},
|
|
1137
|
+
}
|
|
1138
|
+
return template
|
|
1139
|
+
|
|
1140
|
+
|
|
1104
1141
|
def detect(project: Path) -> dict[str, Any]:
|
|
1105
1142
|
project = project.resolve()
|
|
1106
1143
|
existing = load_profile(project)
|
|
@@ -1120,6 +1157,14 @@ def detect(project: Path) -> dict[str, Any]:
|
|
|
1120
1157
|
"applied": False,
|
|
1121
1158
|
"detectedComponents": components,
|
|
1122
1159
|
"message": "multiple or nested project components require an explicit profile",
|
|
1160
|
+
# 拒绝生成可以,但不能连结构都不给:调用方否则只能反读本文件源码去凑
|
|
1161
|
+
# v3 形状。骨架不落盘——一份占位 profile 看起来"已配好"比没有更糟。
|
|
1162
|
+
"profileTemplate": manual_profile_template(excluded, components),
|
|
1163
|
+
"hint": (
|
|
1164
|
+
"手工声明一份 profile:以 profileTemplate 为骨架,按 detectedComponents "
|
|
1165
|
+
"选定产品包,commands 里的 inputs 用相对项目根的 glob(如 "
|
|
1166
|
+
f"'<component>/test/**'),填好后写入 {project / PROFILE_REL}"
|
|
1167
|
+
),
|
|
1123
1168
|
}
|
|
1124
1169
|
if project_type == "unknown" and existing:
|
|
1125
1170
|
return {
|
|
@@ -157,6 +157,35 @@ def _private_evidence_root(explicit: Path | None = None) -> Path:
|
|
|
157
157
|
Path.home() / ".harness" / "private-evidence").expanduser().resolve()
|
|
158
158
|
|
|
159
159
|
|
|
160
|
+
def private_evidence_root_for(source: Path, explicit: Path | None = None) -> Path:
|
|
161
|
+
"""私有隔离根:必须与源同一驱动器。
|
|
162
|
+
|
|
163
|
+
`os.replace` 跨驱动器会直接失败(Windows 上是 WinError 17)。默认根
|
|
164
|
+
`~/.harness/private-evidence` 在 Windows 落 C 盘,项目在别的盘时隔离必然失败
|
|
165
|
+
——而隔离是归档的硬前置,等于整条归档路被堵死。同盘时沿用配置/默认值;不同盘
|
|
166
|
+
时退到源所在驱动器根下的 `.harness-private-evidence`(天然在任何项目根之外)。
|
|
167
|
+
POSIX 上 drive 恒为空串,行为不变。
|
|
168
|
+
"""
|
|
169
|
+
source = source.expanduser().resolve()
|
|
170
|
+
preferred = _private_evidence_root(explicit)
|
|
171
|
+
if preferred.drive.lower() == source.drive.lower():
|
|
172
|
+
return preferred
|
|
173
|
+
return (Path(source.anchor) / ".harness-private-evidence").resolve()
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
def _infer_project_root(change_root: Path) -> Path | None:
|
|
177
|
+
"""change 目录形如 `<project>/.harness/changes/<name>`:取路径里 `.harness` 段的父目录。
|
|
178
|
+
|
|
179
|
+
不做"向上找存在 .harness 的目录"式搜索——用户 HOME 下通常也有 `.harness`,
|
|
180
|
+
那样会把 HOME 误判成项目根,把合法的临时隔离位置一并拒掉。
|
|
181
|
+
"""
|
|
182
|
+
parts = change_root.parts
|
|
183
|
+
for index in range(len(parts) - 1, 0, -1):
|
|
184
|
+
if parts[index] == ".harness":
|
|
185
|
+
return Path(*parts[:index])
|
|
186
|
+
return None
|
|
187
|
+
|
|
188
|
+
|
|
160
189
|
def _secure_private_path(path: Path, *, directory: bool) -> str:
|
|
161
190
|
"""Best-effort owner-only permissions, with an explicit ACL result on Windows."""
|
|
162
191
|
mode = 0o700 if directory else 0o600
|
|
@@ -288,11 +317,12 @@ def quarantine_sensitive_evidence(
|
|
|
288
317
|
change_root: Path,
|
|
289
318
|
reason: str = "sensitive evidence",
|
|
290
319
|
private_root: Path | None = None,
|
|
320
|
+
project_root: Path | None = None,
|
|
291
321
|
) -> dict[str, Any]:
|
|
292
322
|
"""Atomically move legacy plaintext evidence outside any publishable tree."""
|
|
293
323
|
source = source.expanduser().resolve()
|
|
294
324
|
change_root = change_root.expanduser().resolve()
|
|
295
|
-
private =
|
|
325
|
+
private = private_evidence_root_for(source, private_root)
|
|
296
326
|
if not source.is_file():
|
|
297
327
|
return {"ok": False, "reasonCode": "SENSITIVE_EVIDENCE_SOURCE_MISSING", "sourcePath": str(source)}
|
|
298
328
|
if private == change_root or private.is_relative_to(change_root):
|
|
@@ -301,6 +331,25 @@ def quarantine_sensitive_evidence(
|
|
|
301
331
|
"reasonCode": "SENSITIVE_EVIDENCE_QUARANTINE_FAILED",
|
|
302
332
|
"error": "private quarantine root must be outside the publishable change root",
|
|
303
333
|
}
|
|
334
|
+
# 归档的密钥扫描门禁按**项目根**判定(SECRET_SCAN_PRIVATE_PATH_IN_COPY_ROOT)。
|
|
335
|
+
# 这里此前只看 change_root,比门禁宽——于是隔离"成功"了,门禁再拒一次,调用方
|
|
336
|
+
# 得多试一轮才找到合法位置。对齐到同一条边界,错就错在当场。
|
|
337
|
+
resolved_project = (
|
|
338
|
+
project_root.expanduser().resolve()
|
|
339
|
+
if project_root is not None
|
|
340
|
+
else _infer_project_root(change_root)
|
|
341
|
+
)
|
|
342
|
+
if resolved_project is not None and (
|
|
343
|
+
private == resolved_project or private.is_relative_to(resolved_project)
|
|
344
|
+
):
|
|
345
|
+
return {
|
|
346
|
+
"ok": False,
|
|
347
|
+
"reasonCode": "SENSITIVE_EVIDENCE_QUARANTINE_FAILED",
|
|
348
|
+
"error": (
|
|
349
|
+
"private quarantine root must be outside the project root "
|
|
350
|
+
f"({resolved_project}); archive's secret scan rejects paths inside it"
|
|
351
|
+
),
|
|
352
|
+
}
|
|
304
353
|
try:
|
|
305
354
|
raw_digest = "sha256:" + hashlib.sha256(source.read_bytes()).hexdigest()
|
|
306
355
|
quarantine_dir = private / uuid.uuid4().hex
|
|
@@ -1850,6 +1899,37 @@ def doctor(project: Path, change_dir: Path, *, agent: str) -> dict[str, Any]:
|
|
|
1850
1899
|
return {"ok": True, "action": "doctor", **payload}
|
|
1851
1900
|
|
|
1852
1901
|
|
|
1902
|
+
def cmd_quarantine_evidence(args: argparse.Namespace) -> dict[str, Any]:
|
|
1903
|
+
"""隔离命令:把明文敏感证据原子移出可发布树,逐条返回结果。
|
|
1904
|
+
|
|
1905
|
+
单条失败不吞掉其余条目——归档阻断项常常一次报多个文件,逐条报告才能一轮修完。
|
|
1906
|
+
"""
|
|
1907
|
+
project = Path(args.project).expanduser().resolve()
|
|
1908
|
+
change_root = Path(args.change_dir).expanduser().resolve()
|
|
1909
|
+
private_root = Path(args.private_root) if args.private_root else None
|
|
1910
|
+
entries: list[dict[str, Any]] = []
|
|
1911
|
+
for raw in args.file:
|
|
1912
|
+
candidate = Path(raw)
|
|
1913
|
+
source = candidate if candidate.is_absolute() else change_root / candidate
|
|
1914
|
+
outcome = quarantine_sensitive_evidence(
|
|
1915
|
+
source,
|
|
1916
|
+
change_root=change_root,
|
|
1917
|
+
reason=args.reason,
|
|
1918
|
+
private_root=private_root,
|
|
1919
|
+
project_root=project,
|
|
1920
|
+
)
|
|
1921
|
+
entries.append({"file": raw, **outcome})
|
|
1922
|
+
failed = [item for item in entries if not item.get("ok")]
|
|
1923
|
+
return {
|
|
1924
|
+
"ok": not failed,
|
|
1925
|
+
"action": "quarantine-evidence",
|
|
1926
|
+
"code": "SENSITIVE_EVIDENCE_QUARANTINED" if not failed else "SENSITIVE_EVIDENCE_QUARANTINE_FAILED",
|
|
1927
|
+
"quarantined": len(entries) - len(failed),
|
|
1928
|
+
"failed": len(failed),
|
|
1929
|
+
"entries": entries,
|
|
1930
|
+
}
|
|
1931
|
+
|
|
1932
|
+
|
|
1853
1933
|
def build_parser() -> argparse.ArgumentParser:
|
|
1854
1934
|
parser = argparse.ArgumentParser(prog="harness_runtime.py")
|
|
1855
1935
|
sub = parser.add_subparsers(dest="command", required=True)
|
|
@@ -1858,6 +1938,24 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
1858
1938
|
p_doctor.add_argument("--change-dir", required=True)
|
|
1859
1939
|
p_doctor.add_argument("--agent", choices=sorted(_ADAPTERS), required=True)
|
|
1860
1940
|
p_doctor.add_argument("--json", action="store_true")
|
|
1941
|
+
# 归档的 SENSITIVE_EVIDENCE_UNQUARANTINED 是硬阻断,此前却没有任何命令行入口
|
|
1942
|
+
# ——调用方只能写 python -c + sys.path hack 去调内部函数。
|
|
1943
|
+
p_quarantine = sub.add_parser(
|
|
1944
|
+
"quarantine-evidence",
|
|
1945
|
+
help="move plaintext sensitive evidence outside the publishable tree",
|
|
1946
|
+
)
|
|
1947
|
+
p_quarantine.add_argument("--project", required=True)
|
|
1948
|
+
p_quarantine.add_argument("--change-dir", required=True)
|
|
1949
|
+
p_quarantine.add_argument(
|
|
1950
|
+
"--file", required=True, action="append",
|
|
1951
|
+
help="path to quarantine, relative to --change-dir or absolute; repeatable",
|
|
1952
|
+
)
|
|
1953
|
+
p_quarantine.add_argument("--reason", default="sensitive evidence")
|
|
1954
|
+
p_quarantine.add_argument(
|
|
1955
|
+
"--private-root", default=None,
|
|
1956
|
+
help="override the private root; must be outside the project root and on its drive",
|
|
1957
|
+
)
|
|
1958
|
+
p_quarantine.add_argument("--json", action="store_true")
|
|
1861
1959
|
p_adapter = sub.add_parser("adapter")
|
|
1862
1960
|
p_adapter.add_argument("--agent", choices=sorted(_ADAPTERS), required=True)
|
|
1863
1961
|
p_adapter.add_argument("--change", required=True)
|
|
@@ -1899,6 +1997,8 @@ def main(argv: list[str] | None = None) -> int:
|
|
|
1899
1997
|
try:
|
|
1900
1998
|
if args.command == "doctor":
|
|
1901
1999
|
result = doctor(Path(args.project), Path(args.change_dir), agent=args.agent)
|
|
2000
|
+
elif args.command == "quarantine-evidence":
|
|
2001
|
+
result = cmd_quarantine_evidence(args)
|
|
1902
2002
|
elif args.command == "adapter":
|
|
1903
2003
|
result = {"ok": True, "action": "adapter", **adapter_worktree(args.agent, args.change)}
|
|
1904
2004
|
elif args.command == "_worker":
|
|
@@ -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=24b43afeb4dc4c52; overlay=none; 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,23 @@ 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` | **plan/spec 文档、`.harness/codebase` 架构地图、`.harness/rules`**(push 会 walk 每个归档目录的 `spec/` 与 `plans/`) | `ok`/`reasonCode`/`exitCode`/`cliCode`/`detail` |
|
|
207
|
+
|
|
208
|
+
```
|
|
209
|
+
远端归档:arc_3ff325bb…(archiveStatus=durable,uploadStatus=ready,knowledgeStatus=ready,8 文件)
|
|
210
|
+
受管快照:❌ MANAGED_SNAPSHOT_UPLOAD_FAILED(exitCode=1,detail=…)→ plan/spec 与规则/架构地图未上平台,需 npx hunter-harness push --yes --non-interactive 重试
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
**第二条尤其不能省**:它是 best-effort,失败不阻断归档,于是最容易被淹没成一句警告——而平台上"分支文件里没有 plan/spec"正是它失败的后果。成功时也要写明 `submitted` 数量;`ok=false` 时必须带上 `reasonCode` 与 `detail`,不得省略或写成"已完成"。
|
|
189
214
|
|
|
190
215
|
## 渐进披露
|
|
191
216
|
|
|
@@ -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=24b43afeb4dc4c52; overlay=none; agent=codebuddy; do not edit -->
|
|
7
7
|
# harness-codebase-map — 代码库地图
|
|
8
8
|
|
|
9
9
|
## Purpose
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
name: harness-knowledge-ingest
|
|
3
3
|
description: 确认归档 ZIP 已上传并由 Hunter Platform 在服务端解包、校验和入库。客户端不再构建或维护本地知识索引。
|
|
4
4
|
---
|
|
5
|
-
<!-- generated by harness_deploy.py; core=
|
|
5
|
+
<!-- generated by harness_deploy.py; core=24b43afeb4dc4c52; 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=24b43afeb4dc4c52; 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=24b43afeb4dc4c52; overlay=none; 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=24b43afeb4dc4c52; overlay=none; 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=24b43afeb4dc4c52; overlay=none; 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=24b43afeb4dc4c52; overlay=none; 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=24b43afeb4dc4c52; overlay=none; 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`,不失败
|
|
@@ -62,7 +63,7 @@ description: 按变更簇执行 TDD 编码循环(RED→GREEN→REFACTOR→编
|
|
|
62
63
|
0. 加载上下文:普通 Run 先 `harness_context.py prepare --project . --change <id> --phase run --executor <tool> --json`(`--project` 必填,漏了直接 argparse 报错),再 **`harness_context.py begin --project . --change <id> --phase run --executor <tool> --json`** 校验交接,最后运行 **`harness_gate.py begin --phase run --change <id>`**。<br>**交接凭证不必手工补**:v2 计划的 `plan finalize` 不写 context 事务,`prepare` 会在检测到 committed 的 `meta/publication-journals/*.json` 时自动补录 `plan → run` 凭证(凭证带 `bootstrapSource=plan_publication_journal` 留痕)。仍报 `HANDOFF_REQUIRED`/`LEGACY_BOOTSTRAP_REQUIRED` 说明**没有**这份发布证据——回到 plan 阶段确认发布是否真的完成,**不得**自己拼 `classify + configure-plan + close` 造凭证。`harness_gate.py` 会从当前已安装适配器自动识别 skills root;只有执行复制到别处的脚本时才显式传 `--skills-root`。`--fixback` 不得拼装这些底层步骤,必须只调用一次 `harness_fixback.py launch-review --project . --change <id> --change-dir <change-dir> --executor <tool> --skills-root <skills-root> --product-identity <当前产品身份> --json`:该命令会先筛选结构化评审项,再原子选择 Fixback 分支、确认上下文、取得 Run 门禁并创建已填充批次。返回 `FIXBACK_NOTHING_TO_APPLY` 时直接报告“本轮评审没有需要执行的代码修复”并停止;返回阻塞码时按 `recoveryAction` 停止,不搜索实现、不试探其他参数、不创建空批次。禁止手写 `events.ndjson` / `phase.end`。已连接平台时 begin 会 best-effort 补传事件,失败只告警。
|
|
63
64
|
0.5. **测试基础设施探测**(先写 `CHECKING`,四项证据齐备后再结论)→ `reference.md` Step 0.5;测试基线已由上一步 gate begin 内部建立,不得再次执行 guard begin
|
|
64
65
|
1. **变更簇 TDD** — `protocols.md` `run-tdd-protocol`;批量 RED/GREEN;按需 `change-cluster-review-protocol`(高风险 + reviewer 预检可用)
|
|
65
|
-
2. 构建验证 + **仅**通过 `harness_ledger.py record` 写 ledger(禁止 Write/Edit `verification-ledger.json`);若本阶段创建/删除了清单、锁文件、源码根或改变技术栈,最后一次产品编辑后、写账本与关门前必须执行 `harness_preflight.py detect --project . --json` 刷新 build profile。`record --project . --profile-input <key>` 会对缺失/陈旧 profile 自动检测并从同一 target 推导 scope、coverage
|
|
66
|
+
2. 构建验证 + **仅**通过 `harness_ledger.py record` 写 ledger(禁止 Write/Edit `verification-ledger.json`);若本阶段创建/删除了清单、锁文件、源码根或改变技术栈,最后一次产品编辑后、写账本与关门前必须执行 `harness_preflight.py detect --project . --json` 刷新 build profile。`record --project . --profile-input <key>` 会对缺失/陈旧 profile 自动检测并从同一 target 推导 scope、coverage、规范命令和输入闭包;不得再手填另一套身份。detect 在嵌套/多组件仓库返回 `DETECTION_AMBIGUOUS` 时,响应里的 `profileTemplate` 就是可填骨架(已含 defaultsFingerprint、excludedRoots 默认集与 commands 条目形状),按 `hint` 填好写入 `.harness/config/build-profile.json` 即可,**不要去反读 `harness_profile.py` 源码凑结构**。`diff-hash --change-dir` 纳入 ignored tests → `reference.md` Step 2c
|
|
66
67
|
3. **场景覆盖检查**(场景表映射,禁止用用例数冒充场景数)
|
|
67
68
|
4. **关门检查**(10 项)→ 只执行一次 `harness_gate.py close`;`--to-phase` 必须取返回的 `nextPhases` 或 `plannedPhases` 中 run 的真实后继,禁止写死 test。该命令内部关闭 test guard、写 `phase.end`、释放租约、写 handoff 并补传事件;不得再单独调用 test-guard/context close。失败时按结构化 `recoveryAction` 原样重试,已完成步骤幂等复用。
|
|
68
69
|
|
|
@@ -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=24b43afeb4dc4c52; overlay=none; 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`,不失败
|
|
@@ -54,10 +55,11 @@ worktree 合并前必须运行 `harness_change.py integration-lock acquire --run
|
|
|
54
55
|
|
|
55
56
|
### 提交流程(步骤 0–7)
|
|
56
57
|
|
|
57
|
-
0. **启动准备** — `harness_context.py prepare --
|
|
58
|
+
0. **启动准备** — `harness_context.py prepare --project . --change <id> --phase submit --executor <tool> --json`(`--project` 必填)确定唯一变更与 executionRoot;`harness_context.py begin --project . --change <id> --phase submit --executor <tool> --json` 校验 review→submit receipt;**`harness_gate.py begin --phase submit --change <id>`**;读 ledger,以 `harness_ledger.py diff-hash --repo <executionRoot> --base <baseCommit> --change-dir ".harness/changes/<change-name>" --json` 计算 diffHash + post-test 7 类分类(`executionRoot` 必须直接取准备回执;禁止手写 ledger / 手工 phase.end)
|
|
58
59
|
1. **合并最新代码** — 主目录与 worktree 均**不在业务工作区 stash/pull**;远端同步由合并段 integration transaction 在隔离 integration worktree 内完成(见「worktree 合并流程」);**正常路径禁止 `git stash` / `stash pop`**
|
|
59
60
|
2. **最终验证** — ledger 复用优先;提交前只调用 `can-reuse --project . --profile-input unitTestFull --command <profile 规范命令>`。不得传入另一套 `--files`,不得把 runner 包装说明写进 `command`。`reuse=true` 时禁止重跑;只有真实输入、依赖、工具链或环境身份变化时才执行一次同一 profile 验证,并只登记一条结果。不要读取 ledger/archive 实现源码或临时编写散列脚本排查参数。
|
|
60
61
|
- 无远端 CI 且 gate-policy 未强制 remote provider 时,验证通过/复用后运行 `harness_archive.py certify-local --change-dir ... --project . --json`,从同一 ledger 生成 `local-reproducible` 产品候选收据;该命令不执行测试。
|
|
62
|
+
- **被与本变更无关的预存失败卡住时的正规出路**:用 `harness_preflight.py record-quirk --project . --action skip-not-block --pattern "<具体错误签名,≥8 字符>" --reason "<为什么与本变更无关>"` 把它声明进 build-profile 的 `knownPreexistingErrors`,certify-local 会在**该失败的 ledger 证据里确实出现该签名**时放行,并把 `{validation, pattern, reason}` 写进收据的 `verification.preexistingExemptions` 留痕。**不得**改 gate-policy 的 `candidateVerification.requiredValidations` 来绕过——那是把门禁本身拆掉。声明了但证据对不上、或签名短到能匹配一切,仍然阻断。
|
|
61
63
|
- gate-policy 要求 `remote-attested` 时不得降级成本地收据,等待远端 attestation。
|
|
62
64
|
3. **.gitignore + 精确暂存** ⚠️ — 检查 `.harness/` 在 `.gitignore`;**禁止 `git add -A`**。若存在 `evidence/test-tracking.json`,先执行 `python <skills-root>/scripts/harness_test_guard.py stage --project . --change-dir ".harness/changes/<change-name>" --json`;失败即硬停止。无 manifest 时不使用 `-f`。manifest 之外的文件按精确业务路径正常暂存,**禁止全局 force-add**。
|
|
63
65
|
4. **提交方式** — 主目录:blocking user confirmation 三选项(commit+push / 仅本地 / 取消);**worktree:固定仅本地 commit**
|
|
@@ -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=24b43afeb4dc4c52; overlay=none; 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=24b43afeb4dc4c52; overlay=none; 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`,不失败
|
|
@@ -85,7 +86,7 @@ Runner 强制同项目单实例、低调度优先级、逐命令超时、正常
|
|
|
85
86
|
|
|
86
87
|
### Phase 0:环境准备(主会话执行,需要交互确认)
|
|
87
88
|
|
|
88
|
-
先 `harness_context.py prepare --
|
|
89
|
+
先 `harness_context.py prepare --project . --change <id> --phase test --executor <tool> --json`(`--project` 必填,漏了直接 argparse 报错),再 `harness_context.py begin --project . --change <id> --phase test --executor <tool> --json` 校验最新 run→test receipt 的 artifact/hash/HEAD;然后 **`harness_gate.py begin --phase test --change <id>`**(禁止手工 phase.start / 手写 ledger)。执行各项强制环境检查 + **命令执行模式 preflight (0.1)**;只有首选执行器不可用时,才执行 fallback 执行器探测。
|
|
89
90
|
|
|
90
91
|
验证写入**仅**允许 `harness_ledger.py record` / `can-reuse`;禁止 Write/Edit `verification-ledger.json`。测试跟踪:gate begin → 执行(可选 `harness_test_guard.py mark stale-test-repair`)→ 单次 `harness_gate.py close`。`--to-phase` 取实际阶段计划的后继;Fixback 返回 run,普通流程可直接进入 Review、Submit 或 Archive。Fixback 只失效与改动文件相交的验证目标。
|
|
91
92
|
|
|
@@ -273,6 +273,8 @@ powershell.exe -NoProfile -Command "try { (Invoke-WebRequest -Uri 'http://127.0.
|
|
|
273
273
|
- [ ] 复用 → 跳过重跑,标记"✅ 复用 harness-run 单元测试结果"
|
|
274
274
|
- [ ] 不复用 → 按 profile key resolve 重跑测试命令(`harness_profile.py resolve --key unitTest`,不复制示例 `-pl` 命令),结果写回 ledger 的 `unitTest` 项
|
|
275
275
|
- [ ] HTTP/API 契约结果写入 `apiTest`;真实浏览器/真实栈 Playwright 结果写入 `browserTest`,两者不得互相覆盖
|
|
276
|
+
- [ ] **本变更没有该维度场景时**(如全部为单元场景 → 无 API 场景):用 `harness_ledger.py record --verification apiTest --status NOT_RUN --applicability NOT_APPLICABLE --applicability-reason "<为什么不适用>"` 登记,**不传 `--files`**。门禁 close 要求 requiredValidations 每项都有 entry,但**绝不能拿无关文件(比如单元测试文件)凑 `--files`**——那会让 ledger 声称该维度的输入是那些文件,是假证据
|
|
277
|
+
- [ ] `can-reuse` 返回 `reuse:false` 时直接读默认输出里的 `reason`/`executionNeed`/`detail` 定位原因,不必再补跑 `--verbose`
|
|
276
278
|
- [ ] 复用判断前以 `harness_ledger.py diff-hash --repo . --base <baseCommit> --change-dir ".harness/changes/<change-name>" --json` 重算指纹;test-tracking manifest 无效或 hash 漂移即停止
|
|
277
279
|
- [ ] 测试失败若明确为陈旧测试,仅在当前代码/批准计划/可验证历史唯一确定新契约且只改测试时自动修复;否则记录 `BLOCKED_PREEXISTING`
|
|
278
280
|
- [ ] 自动修复后立即重跑该测试与目标测试,并以 `harness_test_guard.py record ... --reason stale-test-repair` 记录精确路径
|