@hunter-harness/workflow-harness 0.2.82 → 0.2.84
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 +32 -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 +1 -1
- package/harness/bundles/general/claude-code/harness-pull/SKILL.md +1 -1
- package/harness/bundles/general/claude-code/harness-push/SKILL.md +1 -1
- package/harness/bundles/general/claude-code/harness-review/SKILL.md +1 -1
- package/harness/bundles/general/claude-code/harness-run/SKILL.md +22 -2
- package/harness/bundles/general/claude-code/harness-run/protocols.md +60 -0
- package/harness/bundles/general/claude-code/harness-run/reference.md +1 -1
- package/harness/bundles/general/claude-code/harness-submit/SKILL.md +1 -1
- package/harness/bundles/general/claude-code/harness-sync/SKILL.md +1 -1
- package/harness/bundles/general/claude-code/harness-test/SKILL.md +1 -1
- package/harness/bundles/general/claude-code/scripts/harness_archive.py +273 -3
- package/harness/bundles/general/claude-code/scripts/harness_fixback.py +451 -37
- package/harness/bundles/general/claude-code/scripts/harness_gate.py +88 -3
- package/harness/bundles/general/claude-code/scripts/harness_integration.py +156 -39
- package/harness/bundles/general/claude-code/scripts/harness_review.py +43 -5
- package/harness/bundles/general/claude-code/scripts/harness_runtime.py +125 -10
- package/harness/bundles/general/codebuddy/.harness-build.json +1 -1
- package/harness/bundles/general/codebuddy/harness-archive/SKILL.md +32 -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 +1 -1
- package/harness/bundles/general/codebuddy/harness-pull/SKILL.md +1 -1
- package/harness/bundles/general/codebuddy/harness-push/SKILL.md +1 -1
- package/harness/bundles/general/codebuddy/harness-review/SKILL.md +1 -1
- package/harness/bundles/general/codebuddy/harness-run/SKILL.md +22 -2
- package/harness/bundles/general/codebuddy/harness-run/protocols.md +60 -0
- package/harness/bundles/general/codebuddy/harness-run/reference.md +1 -1
- package/harness/bundles/general/codebuddy/harness-submit/SKILL.md +1 -1
- package/harness/bundles/general/codebuddy/harness-sync/SKILL.md +1 -1
- package/harness/bundles/general/codebuddy/harness-test/SKILL.md +1 -1
- package/harness/bundles/general/codebuddy/scripts/harness_archive.py +273 -3
- package/harness/bundles/general/codebuddy/scripts/harness_fixback.py +451 -37
- package/harness/bundles/general/codebuddy/scripts/harness_gate.py +88 -3
- package/harness/bundles/general/codebuddy/scripts/harness_integration.py +156 -39
- package/harness/bundles/general/codebuddy/scripts/harness_review.py +43 -5
- package/harness/bundles/general/codebuddy/scripts/harness_runtime.py +125 -10
- package/harness/bundles/general/codex/.harness-build.json +1 -1
- package/harness/bundles/general/codex/harness-archive/SKILL.md +32 -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 +1 -1
- package/harness/bundles/general/codex/harness-pull/SKILL.md +1 -1
- package/harness/bundles/general/codex/harness-push/SKILL.md +1 -1
- package/harness/bundles/general/codex/harness-review/SKILL.md +1 -1
- package/harness/bundles/general/codex/harness-run/SKILL.md +22 -2
- package/harness/bundles/general/codex/harness-run/protocols.md +60 -0
- package/harness/bundles/general/codex/harness-run/reference.md +1 -1
- package/harness/bundles/general/codex/harness-submit/SKILL.md +1 -1
- package/harness/bundles/general/codex/harness-sync/SKILL.md +1 -1
- package/harness/bundles/general/codex/harness-test/SKILL.md +1 -1
- package/harness/bundles/general/codex/scripts/harness_archive.py +273 -3
- package/harness/bundles/general/codex/scripts/harness_fixback.py +451 -37
- package/harness/bundles/general/codex/scripts/harness_gate.py +88 -3
- package/harness/bundles/general/codex/scripts/harness_integration.py +156 -39
- package/harness/bundles/general/codex/scripts/harness_review.py +43 -5
- package/harness/bundles/general/codex/scripts/harness_runtime.py +125 -10
- package/harness/bundles/general/cursor/.harness-build.json +1 -1
- package/harness/bundles/general/cursor/harness-archive/SKILL.md +32 -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 +1 -1
- package/harness/bundles/general/cursor/harness-pull/SKILL.md +1 -1
- package/harness/bundles/general/cursor/harness-push/SKILL.md +1 -1
- package/harness/bundles/general/cursor/harness-review/SKILL.md +1 -1
- package/harness/bundles/general/cursor/harness-run/SKILL.md +22 -2
- package/harness/bundles/general/cursor/harness-run/protocols.md +60 -0
- package/harness/bundles/general/cursor/harness-run/reference.md +1 -1
- package/harness/bundles/general/cursor/harness-submit/SKILL.md +1 -1
- package/harness/bundles/general/cursor/harness-sync/SKILL.md +1 -1
- package/harness/bundles/general/cursor/harness-test/SKILL.md +1 -1
- package/harness/bundles/general/cursor/scripts/harness_archive.py +273 -3
- package/harness/bundles/general/cursor/scripts/harness_fixback.py +451 -37
- package/harness/bundles/general/cursor/scripts/harness_gate.py +88 -3
- package/harness/bundles/general/cursor/scripts/harness_integration.py +156 -39
- package/harness/bundles/general/cursor/scripts/harness_review.py +43 -5
- package/harness/bundles/general/cursor/scripts/harness_runtime.py +125 -10
- package/harness/bundles/java/claude-code/.harness-build.json +1 -1
- package/harness/bundles/java/claude-code/harness-apidoc/SKILL.md +1 -1
- package/harness/bundles/java/claude-code/harness-archive/SKILL.md +32 -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 +1 -1
- package/harness/bundles/java/claude-code/harness-plan/SKILL.md +1 -1
- package/harness/bundles/java/claude-code/harness-pull/SKILL.md +1 -1
- package/harness/bundles/java/claude-code/harness-push/SKILL.md +1 -1
- package/harness/bundles/java/claude-code/harness-review/SKILL.md +1 -1
- package/harness/bundles/java/claude-code/harness-run/SKILL.md +22 -2
- package/harness/bundles/java/claude-code/harness-run/protocols.md +60 -0
- package/harness/bundles/java/claude-code/harness-submit/SKILL.md +1 -1
- package/harness/bundles/java/claude-code/harness-sync/SKILL.md +1 -1
- package/harness/bundles/java/claude-code/harness-test/SKILL.md +1 -1
- package/harness/bundles/java/claude-code/scripts/harness_archive.py +273 -3
- package/harness/bundles/java/claude-code/scripts/harness_fixback.py +451 -37
- package/harness/bundles/java/claude-code/scripts/harness_gate.py +88 -3
- package/harness/bundles/java/claude-code/scripts/harness_integration.py +156 -39
- package/harness/bundles/java/claude-code/scripts/harness_review.py +43 -5
- package/harness/bundles/java/claude-code/scripts/harness_runtime.py +125 -10
- package/harness/bundles/java/codebuddy/.harness-build.json +1 -1
- package/harness/bundles/java/codebuddy/harness-apidoc/SKILL.md +1 -1
- package/harness/bundles/java/codebuddy/harness-archive/SKILL.md +32 -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 +1 -1
- package/harness/bundles/java/codebuddy/harness-plan/SKILL.md +1 -1
- package/harness/bundles/java/codebuddy/harness-pull/SKILL.md +1 -1
- package/harness/bundles/java/codebuddy/harness-push/SKILL.md +1 -1
- package/harness/bundles/java/codebuddy/harness-review/SKILL.md +1 -1
- package/harness/bundles/java/codebuddy/harness-run/SKILL.md +22 -2
- package/harness/bundles/java/codebuddy/harness-run/protocols.md +60 -0
- package/harness/bundles/java/codebuddy/harness-submit/SKILL.md +1 -1
- package/harness/bundles/java/codebuddy/harness-sync/SKILL.md +1 -1
- package/harness/bundles/java/codebuddy/harness-test/SKILL.md +1 -1
- package/harness/bundles/java/codebuddy/scripts/harness_archive.py +273 -3
- package/harness/bundles/java/codebuddy/scripts/harness_fixback.py +451 -37
- package/harness/bundles/java/codebuddy/scripts/harness_gate.py +88 -3
- package/harness/bundles/java/codebuddy/scripts/harness_integration.py +156 -39
- package/harness/bundles/java/codebuddy/scripts/harness_review.py +43 -5
- package/harness/bundles/java/codebuddy/scripts/harness_runtime.py +125 -10
- package/harness/bundles/java/codex/.harness-build.json +1 -1
- package/harness/bundles/java/codex/harness-apidoc/SKILL.md +1 -1
- package/harness/bundles/java/codex/harness-archive/SKILL.md +32 -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 +1 -1
- package/harness/bundles/java/codex/harness-plan/SKILL.md +1 -1
- package/harness/bundles/java/codex/harness-pull/SKILL.md +1 -1
- package/harness/bundles/java/codex/harness-push/SKILL.md +1 -1
- package/harness/bundles/java/codex/harness-review/SKILL.md +1 -1
- package/harness/bundles/java/codex/harness-run/SKILL.md +22 -2
- package/harness/bundles/java/codex/harness-run/protocols.md +60 -0
- package/harness/bundles/java/codex/harness-submit/SKILL.md +1 -1
- package/harness/bundles/java/codex/harness-sync/SKILL.md +1 -1
- package/harness/bundles/java/codex/harness-test/SKILL.md +1 -1
- package/harness/bundles/java/codex/scripts/harness_archive.py +273 -3
- package/harness/bundles/java/codex/scripts/harness_fixback.py +451 -37
- package/harness/bundles/java/codex/scripts/harness_gate.py +88 -3
- package/harness/bundles/java/codex/scripts/harness_integration.py +156 -39
- package/harness/bundles/java/codex/scripts/harness_review.py +43 -5
- package/harness/bundles/java/codex/scripts/harness_runtime.py +125 -10
- package/harness/bundles/java/cursor/.harness-build.json +1 -1
- package/harness/bundles/java/cursor/harness-apidoc/SKILL.md +1 -1
- package/harness/bundles/java/cursor/harness-archive/SKILL.md +32 -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 +1 -1
- package/harness/bundles/java/cursor/harness-plan/SKILL.md +1 -1
- package/harness/bundles/java/cursor/harness-pull/SKILL.md +1 -1
- package/harness/bundles/java/cursor/harness-push/SKILL.md +1 -1
- package/harness/bundles/java/cursor/harness-review/SKILL.md +1 -1
- package/harness/bundles/java/cursor/harness-run/SKILL.md +22 -2
- package/harness/bundles/java/cursor/harness-run/protocols.md +60 -0
- package/harness/bundles/java/cursor/harness-submit/SKILL.md +1 -1
- package/harness/bundles/java/cursor/harness-sync/SKILL.md +1 -1
- package/harness/bundles/java/cursor/harness-test/SKILL.md +1 -1
- package/harness/bundles/java/cursor/scripts/harness_archive.py +273 -3
- package/harness/bundles/java/cursor/scripts/harness_fixback.py +451 -37
- package/harness/bundles/java/cursor/scripts/harness_gate.py +88 -3
- package/harness/bundles/java/cursor/scripts/harness_integration.py +156 -39
- package/harness/bundles/java/cursor/scripts/harness_review.py +43 -5
- package/harness/bundles/java/cursor/scripts/harness_runtime.py +125 -10
- package/harness/manifests/general/claude-code.json +23 -23
- package/harness/manifests/general/codebuddy.json +23 -23
- package/harness/manifests/general/codex.json +23 -23
- package/harness/manifests/general/cursor.json +23 -23
- package/harness/manifests/java/claude-code.json +24 -24
- package/harness/manifests/java/codebuddy.json +24 -24
- package/harness/manifests/java/codex.json +24 -24
- package/harness/manifests/java/cursor.json +24 -24
- package/hunter-workflow-family.json +5 -5
- package/package.json +1 -1
|
@@ -18,6 +18,8 @@ import threading
|
|
|
18
18
|
import time
|
|
19
19
|
import uuid
|
|
20
20
|
from pathlib import Path
|
|
21
|
+
import fnmatch
|
|
22
|
+
from collections.abc import Sequence
|
|
21
23
|
from typing import Any, Callable
|
|
22
24
|
|
|
23
25
|
SCRIPTS_DIR = Path(__file__).resolve().parent
|
|
@@ -35,6 +37,11 @@ RUN_TERMINAL_STATUSES = {"OK", "FAIL", "INCOMPLETE", "CANCELLED"}
|
|
|
35
37
|
_CHANGE_ID = re.compile(r"^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$")
|
|
36
38
|
SECRET_SCAN_RULES_VERSION = "secret-scan-v2"
|
|
37
39
|
SECRET_SCAN_RECEIPT_REL = Path("meta") / "secret-scan-receipt.json"
|
|
40
|
+
# 变更目录里只有一部分内容会被发布:归档包成员是 reports/final/summary-data.json、
|
|
41
|
+
# spec/**.md、plans/**.md、candidates/knowledge.json、meta/archive-meta.md、
|
|
42
|
+
# meta/change-context.json。runtime/ 是各阶段的草稿(review 的 diff、临时校验
|
|
43
|
+
# 脚本、命令输出重定向),从不入包——拿它挡发布,挡的是永远不会离开本机的字节。
|
|
44
|
+
PUBLICATION_EXCLUDED_DIRS: tuple[str, ...] = ("runtime",)
|
|
38
45
|
_SENSITIVE_ASSIGNMENT = re.compile(
|
|
39
46
|
rb"(?i)[\"']?(?:password|passwd|token|secret|cookie|database[_-]?url)"
|
|
40
47
|
rb"[\"']?\s*[:=]\s*[\"']?(?P<value>[^\s\"']{4,})"
|
|
@@ -126,7 +133,15 @@ def _sha256_json(value: Any) -> str:
|
|
|
126
133
|
return "sha256:" + hashlib.sha256(encoded).hexdigest()
|
|
127
134
|
|
|
128
135
|
|
|
129
|
-
def
|
|
136
|
+
def _is_excluded(relative: str, exclude_dirs: Sequence[str]) -> bool:
|
|
137
|
+
"""Path is inside one of the excluded top-level directories."""
|
|
138
|
+
head = relative.split("/", 1)[0]
|
|
139
|
+
return head in exclude_dirs
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
def _publishable_tree_digest(
|
|
143
|
+
root: Path, *, exclude_dirs: Sequence[str] = ()
|
|
144
|
+
) -> str:
|
|
130
145
|
"""Hash publishable bytes while excluding the self-referential scan receipt."""
|
|
131
146
|
root = root.expanduser().resolve()
|
|
132
147
|
digest = hashlib.sha256()
|
|
@@ -136,6 +151,10 @@ def _publishable_tree_digest(root: Path) -> str:
|
|
|
136
151
|
for path in sorted(root.rglob("*")):
|
|
137
152
|
if not path.is_file() or path.resolve() == receipt:
|
|
138
153
|
continue
|
|
154
|
+
if exclude_dirs and _is_excluded(
|
|
155
|
+
path.relative_to(root).as_posix(), exclude_dirs
|
|
156
|
+
):
|
|
157
|
+
continue
|
|
139
158
|
relative = path.relative_to(root).as_posix()
|
|
140
159
|
data = path.read_bytes()
|
|
141
160
|
digest.update(relative.encode("utf-8"))
|
|
@@ -215,8 +234,73 @@ def _secure_private_path(path: Path, *, directory: bool) -> str:
|
|
|
215
234
|
return "WINDOWS_ACL_UNVERIFIED"
|
|
216
235
|
|
|
217
236
|
|
|
218
|
-
|
|
219
|
-
|
|
237
|
+
# runtime/ 下哪些是**过程草稿**。白名单式判断:只删明确列举的形态,
|
|
238
|
+
# 其余一律保留——门禁、租约、恢复和证据都住在这棵树里,宁可留垃圾也不能误删。
|
|
239
|
+
SCRATCH_PATTERNS: tuple[str, ...] = (
|
|
240
|
+
"*.patch",
|
|
241
|
+
"*.diff",
|
|
242
|
+
"*-input.json",
|
|
243
|
+
"*-input[0-9].json",
|
|
244
|
+
"findings-rereview.json",
|
|
245
|
+
"archive-out*.json",
|
|
246
|
+
"fixback-*-check.*",
|
|
247
|
+
)
|
|
248
|
+
# 即便命中上面的形态也必须留下的名字:它们是运行态,不是草稿。
|
|
249
|
+
SCRATCH_KEEP: frozenset[str] = frozenset({
|
|
250
|
+
"context-lease.json",
|
|
251
|
+
"fixback-session.json",
|
|
252
|
+
"preflight.json",
|
|
253
|
+
})
|
|
254
|
+
|
|
255
|
+
|
|
256
|
+
def sweep_scratch(change_root: Path, *, dry_run: bool = False) -> dict[str, Any]:
|
|
257
|
+
"""清掉 runtime/ 顶层的过程草稿,保留运行态、会话、证据与报告。
|
|
258
|
+
|
|
259
|
+
一轮流程下来 runtime/ 会堆满 diff、临时校验脚本和命令输出重定向。没人清,
|
|
260
|
+
而且它们会被其他门禁读到——2026-08-19 那次,review 自己生成的两个 diff
|
|
261
|
+
直接把归档挡住了。
|
|
262
|
+
|
|
263
|
+
只扫 runtime/ 顶层文件:run-sessions/ 等子目录是托管会话证据,不进扫描。
|
|
264
|
+
"""
|
|
265
|
+
change_root = change_root.expanduser().resolve()
|
|
266
|
+
runtime_dir = change_root / "runtime"
|
|
267
|
+
removed: list[str] = []
|
|
268
|
+
if not runtime_dir.is_dir():
|
|
269
|
+
return {"ok": True, "code": "SCRATCH_SWEPT", "removed": [], "dryRun": dry_run}
|
|
270
|
+
errors: list[dict[str, str]] = []
|
|
271
|
+
for path in sorted(runtime_dir.iterdir()):
|
|
272
|
+
if not path.is_file() or path.name in SCRATCH_KEEP:
|
|
273
|
+
continue
|
|
274
|
+
if not any(fnmatch.fnmatch(path.name, pattern) for pattern in SCRATCH_PATTERNS):
|
|
275
|
+
continue
|
|
276
|
+
relative = path.relative_to(change_root).as_posix()
|
|
277
|
+
if dry_run:
|
|
278
|
+
removed.append(relative)
|
|
279
|
+
continue
|
|
280
|
+
try:
|
|
281
|
+
path.unlink()
|
|
282
|
+
except OSError as exc:
|
|
283
|
+
errors.append({"path": relative, "error": str(exc)})
|
|
284
|
+
continue
|
|
285
|
+
removed.append(relative)
|
|
286
|
+
return {
|
|
287
|
+
"ok": not errors,
|
|
288
|
+
"code": "SCRATCH_SWEPT" if not errors else "SCRATCH_SWEEP_PARTIAL",
|
|
289
|
+
"removed": removed,
|
|
290
|
+
"errors": errors,
|
|
291
|
+
"dryRun": dry_run,
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
|
|
295
|
+
def _sensitive_candidates(
|
|
296
|
+
root: Path, *, exclude_dirs: Sequence[str] = ()
|
|
297
|
+
) -> list[dict[str, Any]]:
|
|
298
|
+
"""Find high-confidence plaintext assignments without treating prose as a secret.
|
|
299
|
+
|
|
300
|
+
``exclude_dirs`` scopes the scan to what a caller actually publishes. The
|
|
301
|
+
default stays whole-tree: the quarantine flow depends on seeing legacy
|
|
302
|
+
plaintext anywhere under the change, including scratch directories.
|
|
303
|
+
"""
|
|
220
304
|
# Compare by relative path, not by resolve() per file: this loop covers the
|
|
221
305
|
# whole change tree, and on Windows each resolve() is a realpath syscall.
|
|
222
306
|
receipt_rel = SECRET_SCAN_RECEIPT_REL.as_posix()
|
|
@@ -224,7 +308,10 @@ def _sensitive_candidates(root: Path) -> list[dict[str, Any]]:
|
|
|
224
308
|
for path in sorted(root.rglob("*")):
|
|
225
309
|
if not path.is_file():
|
|
226
310
|
continue
|
|
227
|
-
|
|
311
|
+
relative = path.relative_to(root).as_posix()
|
|
312
|
+
if relative == receipt_rel:
|
|
313
|
+
continue
|
|
314
|
+
if exclude_dirs and _is_excluded(relative, exclude_dirs):
|
|
228
315
|
continue
|
|
229
316
|
try:
|
|
230
317
|
raw = path.read_bytes()
|
|
@@ -263,17 +350,21 @@ def _is_sensitive_placeholder(value: bytes) -> bool:
|
|
|
263
350
|
)
|
|
264
351
|
|
|
265
352
|
|
|
266
|
-
def publishable_tree_digest(
|
|
353
|
+
def publishable_tree_digest(
|
|
354
|
+
root: Path, *, exclude_dirs: Sequence[str] = ()
|
|
355
|
+
) -> str:
|
|
267
356
|
"""Public digest helper shared by the archive publication gate."""
|
|
268
|
-
return _publishable_tree_digest(root)
|
|
357
|
+
return _publishable_tree_digest(root, exclude_dirs=exclude_dirs)
|
|
269
358
|
|
|
270
359
|
|
|
271
360
|
def sensitive_evidence_receipt_path(change_root: Path) -> Path:
|
|
272
361
|
return _sensitive_evidence_receipt_path(change_root)
|
|
273
362
|
|
|
274
363
|
|
|
275
|
-
def sensitive_evidence_candidates(
|
|
276
|
-
|
|
364
|
+
def sensitive_evidence_candidates(
|
|
365
|
+
root: Path, *, exclude_dirs: Sequence[str] = ()
|
|
366
|
+
) -> list[dict[str, Any]]:
|
|
367
|
+
return _sensitive_candidates(root, exclude_dirs=exclude_dirs)
|
|
277
368
|
|
|
278
369
|
|
|
279
370
|
def _build_sensitive_scan_receipt(
|
|
@@ -282,6 +373,7 @@ def _build_sensitive_scan_receipt(
|
|
|
282
373
|
entries: list[dict[str, Any]],
|
|
283
374
|
unresolved: list[dict[str, Any]],
|
|
284
375
|
status: str,
|
|
376
|
+
exclude_dirs: Sequence[str] = (),
|
|
285
377
|
) -> dict[str, Any]:
|
|
286
378
|
change_root = change_root.expanduser().resolve()
|
|
287
379
|
return {
|
|
@@ -291,7 +383,12 @@ def _build_sensitive_scan_receipt(
|
|
|
291
383
|
"status": status,
|
|
292
384
|
"unresolvedFailures": unresolved,
|
|
293
385
|
"entries": entries,
|
|
294
|
-
"publishableTreeDigest": _publishable_tree_digest(
|
|
386
|
+
"publishableTreeDigest": _publishable_tree_digest(
|
|
387
|
+
change_root, exclude_dirs=exclude_dirs
|
|
388
|
+
),
|
|
389
|
+
# 收据自述扫描范围:复算摘要的一方据此排除同样的目录,
|
|
390
|
+
# 否则两侧口径不同会误报 digest 漂移。
|
|
391
|
+
"publicationExcludedDirs": list(exclude_dirs),
|
|
295
392
|
"publicationExcluded": True,
|
|
296
393
|
"recordedAt": now_iso(),
|
|
297
394
|
}
|
|
@@ -303,6 +400,7 @@ def _write_sensitive_scan_receipt(
|
|
|
303
400
|
entries: list[dict[str, Any]],
|
|
304
401
|
unresolved: list[dict[str, Any]],
|
|
305
402
|
status: str,
|
|
403
|
+
exclude_dirs: Sequence[str] = (),
|
|
306
404
|
) -> dict[str, Any]:
|
|
307
405
|
change_root = change_root.expanduser().resolve()
|
|
308
406
|
receipt = _build_sensitive_scan_receipt(
|
|
@@ -310,6 +408,7 @@ def _write_sensitive_scan_receipt(
|
|
|
310
408
|
entries=entries,
|
|
311
409
|
unresolved=unresolved,
|
|
312
410
|
status=status,
|
|
411
|
+
exclude_dirs=exclude_dirs,
|
|
313
412
|
)
|
|
314
413
|
atomic_write_json(_sensitive_evidence_receipt_path(change_root), receipt)
|
|
315
414
|
return receipt
|
|
@@ -463,6 +562,7 @@ def refresh_sensitive_evidence_scan_receipt(
|
|
|
463
562
|
change_root: Path,
|
|
464
563
|
*,
|
|
465
564
|
persist: bool = True,
|
|
565
|
+
exclude_dirs: Sequence[str] = (),
|
|
466
566
|
) -> dict[str, Any]:
|
|
467
567
|
"""Rescan current bytes while preserving valid quarantine audit entries."""
|
|
468
568
|
change_root = change_root.expanduser().resolve()
|
|
@@ -508,7 +608,7 @@ def refresh_sensitive_evidence_scan_receipt(
|
|
|
508
608
|
}
|
|
509
609
|
entries = list(prior_entries)
|
|
510
610
|
|
|
511
|
-
unresolved = _sensitive_candidates(change_root)
|
|
611
|
+
unresolved = _sensitive_candidates(change_root, exclude_dirs=exclude_dirs)
|
|
512
612
|
status = "FAIL" if unresolved else "QUARANTINED" if entries else "OK"
|
|
513
613
|
if persist:
|
|
514
614
|
try:
|
|
@@ -517,6 +617,7 @@ def refresh_sensitive_evidence_scan_receipt(
|
|
|
517
617
|
entries=entries,
|
|
518
618
|
unresolved=unresolved,
|
|
519
619
|
status=status,
|
|
620
|
+
exclude_dirs=exclude_dirs,
|
|
520
621
|
)
|
|
521
622
|
except OSError as exc:
|
|
522
623
|
return {
|
|
@@ -531,6 +632,7 @@ def refresh_sensitive_evidence_scan_receipt(
|
|
|
531
632
|
entries=entries,
|
|
532
633
|
unresolved=unresolved,
|
|
533
634
|
status=status,
|
|
635
|
+
exclude_dirs=exclude_dirs,
|
|
534
636
|
)
|
|
535
637
|
return {
|
|
536
638
|
"ok": not unresolved,
|
|
@@ -2019,6 +2121,15 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
2019
2121
|
help="override the private root; must be outside the project root and on its drive",
|
|
2020
2122
|
)
|
|
2021
2123
|
p_quarantine.add_argument("--json", action="store_true")
|
|
2124
|
+
p_sweep = sub.add_parser(
|
|
2125
|
+
"sweep-scratch",
|
|
2126
|
+
help="删除 runtime/ 顶层的过程草稿(diff、临时输入、命令输出)",
|
|
2127
|
+
)
|
|
2128
|
+
p_sweep.add_argument("--change-dir", required=True)
|
|
2129
|
+
p_sweep.add_argument(
|
|
2130
|
+
"--dry-run", action="store_true", help="只列出会删什么,不真删"
|
|
2131
|
+
)
|
|
2132
|
+
p_sweep.add_argument("--json", action="store_true")
|
|
2022
2133
|
p_adapter = sub.add_parser("adapter")
|
|
2023
2134
|
p_adapter.add_argument("--agent", choices=sorted(_ADAPTERS), required=True)
|
|
2024
2135
|
p_adapter.add_argument("--change", required=True)
|
|
@@ -2069,6 +2180,10 @@ def main(argv: list[str] | None = None) -> int:
|
|
|
2069
2180
|
result = doctor(Path(args.project), Path(args.change_dir), agent=args.agent)
|
|
2070
2181
|
elif args.command == "quarantine-evidence":
|
|
2071
2182
|
result = cmd_quarantine_evidence(args)
|
|
2183
|
+
elif args.command == "sweep-scratch":
|
|
2184
|
+
result = sweep_scratch(
|
|
2185
|
+
Path(args.change_dir), dry_run=bool(args.dry_run)
|
|
2186
|
+
)
|
|
2072
2187
|
elif args.command == "adapter":
|
|
2073
2188
|
result = {"ok": True, "action": "adapter", **adapter_worktree(args.agent, args.change)}
|
|
2074
2189
|
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=697a20734610e985; overlay=none; agent=cursor; do not edit -->
|
|
7
7
|
# harness-archive — 变更归档
|
|
8
8
|
|
|
9
9
|
## Purpose
|
|
@@ -133,6 +133,37 @@ description: 归档所有变更产出(计划/测试报告/审查/SQL/API文档
|
|
|
133
133
|
| `SENSITIVE_EVIDENCE_UNQUARANTINED`(默认只告警,`HUNTER_HARNESS_SENSITIVE_SCAN=block` 时才阻断) | `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
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
135
|
|
|
136
|
+
### 二·A·1、发布内容预检与服务端 422
|
|
137
|
+
|
|
138
|
+
`status`(`execute` 的预检)会用**与服务端同源的规则**(`packages/core` 的
|
|
139
|
+
`scanSensitiveFiles`)扫描**真正会进 ZIP 的那批文件**——`reports/final/summary-data.json`、
|
|
140
|
+
`spec/**.md`、`plans/**.md`、`candidates/knowledge.json`、`meta/archive-meta.md`、
|
|
141
|
+
`meta/change-context.json`。`runtime/` 是过程草稿,从不入包,也不进这道扫描。
|
|
142
|
+
|
|
143
|
+
结果在 `checks.publication_content_scan`:
|
|
144
|
+
|
|
145
|
+
- `blocked=false` → 上传不会因内容被拒
|
|
146
|
+
- `blocked=true` → **服务端会以 HTTP 422 `archive contains sensitive content` 拒收**。
|
|
147
|
+
归档不阻断(服务端策略不归本地裁决),但必须在此时处理,别等跑完全流程才发现
|
|
148
|
+
- `reasonCode=PUBLICATION_CONTENT_SCAN_UNAVAILABLE` → CLI 不可用,预检没跑;归档继续,
|
|
149
|
+
上传仍可能 422
|
|
150
|
+
|
|
151
|
+
每条 finding 都带 `rule_id / path / line / column / overridable / recovery_action`。
|
|
152
|
+
|
|
153
|
+
**处置:**
|
|
154
|
+
|
|
155
|
+
| 命中类型 | 出路 |
|
|
156
|
+
|---|---|
|
|
157
|
+
| `overridable=true`(medium/low,如 `HH_INTERNAL_ADDRESS` 内网地址、`HH_WINDOWS_ABSOLUTE_PATH`) | 若确属设计固有内容,在源文件该行附近加行内标注:`<!-- hunter-harness-ignore: <RULE_ID> reason=<简短理由> -->`,然后 `republish` 重建重传 |
|
|
158
|
+
| `overridable=false`(high,如私钥、真实 token) | **不提供豁免。** 必须真正脱敏后重新打包 |
|
|
159
|
+
|
|
160
|
+
> ⚠️ 行内标注是**申报**,不是绕过:它把"这是设计决策"写进文档本身,可审计、可追溯。
|
|
161
|
+
> **不要**为了过扫描去删改设计文档的事实内容——那是篡改证据。
|
|
162
|
+
>
|
|
163
|
+
> ⚠️ 已知限制:`hunter-harness-ignore` 在本地扫描器上确认生效;**服务端是否认这条标注尚未验证**
|
|
164
|
+
> (归档上传端点只收裸 ZIP,没有独立的豁免申报通道)。若加了标注仍被 422 拒收,
|
|
165
|
+
> 剩余动作是在平台侧对该规则/内容加白,本地无法自解。
|
|
166
|
+
|
|
136
167
|
### 二·B、归档补传(上传失败或历史归档缺条目)
|
|
137
168
|
|
|
138
169
|
归档上传成功后 ZIP 与回执会被清理,旧版本产生的归档从未入 outbox——所以
|
|
@@ -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=697a20734610e985; overlay=none; agent=cursor; 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=697a20734610e985; overlay=none; agent=cursor; 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=697a20734610e985; overlay=none; agent=cursor; 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=697a20734610e985; overlay=none; agent=cursor; do not edit -->
|
|
7
7
|
# harness-plan — 需求规划
|
|
8
8
|
|
|
9
9
|
## Purpose
|
|
@@ -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=697a20734610e985; overlay=none; agent=cursor; 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=697a20734610e985; overlay=none; agent=cursor; 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=697a20734610e985; overlay=none; agent=cursor; do not edit -->
|
|
8
8
|
# harness-review — 代码审查
|
|
9
9
|
|
|
10
10
|
## Purpose
|
|
@@ -3,7 +3,7 @@ name: harness-run
|
|
|
3
3
|
description: 按变更簇执行 TDD 编码循环(RED→GREEN→REFACTOR→编译验证),逐变更簇实现计划中的任务。仅当用户显式调用
|
|
4
4
|
/harness-run 时使用;不得因用户提到编码/实现就自动触发,也不得被其他阶段 skill 自动接续。
|
|
5
5
|
---
|
|
6
|
-
<!-- generated by harness_deploy.py; core=
|
|
6
|
+
<!-- generated by harness_deploy.py; core=697a20734610e985; overlay=none; agent=cursor; do not edit -->
|
|
7
7
|
# harness-run — 需求编码
|
|
8
8
|
|
|
9
9
|
## Purpose
|
|
@@ -73,6 +73,24 @@ description: 按变更簇执行 TDD 编码循环(RED→GREEN→REFACTOR→编
|
|
|
73
73
|
|
|
74
74
|
**Fixback**:入口只用 `launch-review`,后续问题处理通过 `resolve-issue/close` 驱动,不得把修复说明当成新的普通 Run。只读取返回的受影响问题和文件;验证仅失效与 `changedFiles` 相交的目标,其他 Test/Review 证据继续复用。RED 优先;`manual`、`workflow` 或未选用的建议不进入代码批次,使用中文记录处理结论。
|
|
75
75
|
|
|
76
|
+
**构建/测试执行入口**:所有构建与测试命令(mvn / gradle / npm test / pytest 等)必须经 `harness_test_runner.py exec` 发起,**禁止**用 `powershell.exe -Command` 直接裸跑:
|
|
77
|
+
|
|
78
|
+
```text
|
|
79
|
+
python <skills-root>/scripts/harness_test_runner.py exec --project . --timeout-seconds <预估上限> -- <构建命令及参数>
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
该入口持有项目级互斥锁并托管进程树。裸跑构建在宿主工具超时后会被推入后台,多个后台构建同时写同一个 `target/`、`build/` 或 `node_modules/.cache`,表现为编译产物被清空、日志文件被占用、"增量编译抖动"——这些都不是代码缺陷,是并发自相残杀,排查成本极高。`--timeout-seconds` 按最慢的一次全量构建估(默认 300 秒偏短),宁可给足也不要让命令被中途掐断。
|
|
83
|
+
|
|
84
|
+
返回 `TEST_RUN_ALREADY_ACTIVE`(退出码 3)说明**已有构建在跑**:等它结束或用 `lock-status` 查明持有者,**不得**改用裸跑绕开锁,也不得另起一个并行构建。确认持有进程已消失时才用 `lock-reap` 回收。
|
|
85
|
+
|
|
86
|
+
**长阶段租约续期**:`gate begin` 的租约默认 TTL 3600 秒,而 run 阶段常常跑得更久。租约过期不会中断执行,只会让最后的 `gate close` 报 `LEASE_ABSENT`。凡预计超过 1 小时的阶段,每完成一个变更簇就用本阶段**原 run-id** 续租一次(同 run-id 重复 claim 即刷新,不会新开 attempt):
|
|
87
|
+
|
|
88
|
+
```text
|
|
89
|
+
python <skills-root>/scripts/harness_change.py claim --change <id> --phase run --run-id <本阶段 run-id> --ttl-seconds 3600 --json
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
已经报了 `LEASE_ABSENT` 也按同一条命令恢复——错误响应的 `resumeRunId` 就是要用的 run-id;**不要**重跑 `gate begin`,那会新开 attempt 并丢失本轮 capsule。
|
|
93
|
+
|
|
76
94
|
**执行器边界**:优先使用项目 build profile 和已有测试入口。禁止为了绕过 ESM、路径或参数问题临时生成 `.js`、`require` 脚本;需要文件式 runner 时使用项目已有入口,确需新增时遵循项目模块类型(例如 ESM 使用 `.mjs`)。runner 包装说明写入 `runnerCommand` 元数据,不得拼进账本的规范 `command`。
|
|
77
95
|
|
|
78
96
|
**Foundation Gate**:若 `meta/implementation-checkpoints.json` 中 `foundation-gate` 为 pending,不得开始 plan 中任务 6+;由 `harness_gate.py` 硬阻断。
|
|
@@ -104,7 +122,9 @@ description: 按变更簇执行 TDD 编码循环(RED→GREEN→REFACTOR→编
|
|
|
104
122
|
| **预存变更** | 保留 → baseline 隔离;存在则最终 ≥ 🟡WARN |
|
|
105
123
|
| **关门/状态** | 10 项关门检查;持久化 run-task-status;仅 run-owned P0 静态-only 导致 WARN;test-owned 待办正常移交 |
|
|
106
124
|
| **Worktree** | `requested=true` 时代码只写 worktree |
|
|
107
|
-
|
|
|
125
|
+
| **构建/测试** | 一律经 `harness_test_runner.py exec`;禁止裸跑 mvn/gradle/npm test;`TEST_RUN_ALREADY_ACTIVE` 表示已有构建在跑,等待而非另起 |
|
|
126
|
+
| **租约** | 阶段超 1 小时按变更簇用原 run-id 续租;`LEASE_ABSENT` 用 `harness_change.py claim` 恢复,不重跑 begin |
|
|
127
|
+
| **PowerShell** | 所有 git 经 `powershell.exe -NoProfile -Command` |
|
|
108
128
|
|
|
109
129
|
### 陈旧测试安全修复与精确跟踪
|
|
110
130
|
|
|
@@ -79,3 +79,63 @@ REFACTOR 只允许不改变行为的整理。若重构改变行为,必须回
|
|
|
79
79
|
```
|
|
80
80
|
|
|
81
81
|
RED 问题必须在当前 run 中处理或明确记录为未处理风险;YELLOW 问题可交给后续 `/harness-review`。
|
|
82
|
+
|
|
83
|
+
## fixback 证据契约
|
|
84
|
+
|
|
85
|
+
> `launch-review` 的返回体里带 `evidenceContract` 字段,内容与本节一致。这里写一份是因为
|
|
86
|
+
> 光靠运行时返回,调用方往往已经动手改代码了才看到。
|
|
87
|
+
|
|
88
|
+
### 命令链
|
|
89
|
+
|
|
90
|
+
```text
|
|
91
|
+
run-start(修复前,采 RED)→ 改代码 → run-start(修复后,采 GREEN)
|
|
92
|
+
→ evidence-template ×2 → register-evidence ×2 → resolve-issue
|
|
93
|
+
→ run-start(受影响链)→ evidence-template --kind verification
|
|
94
|
+
→ evidence-template --kind review → close
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### 别手写证据 JSON
|
|
98
|
+
|
|
99
|
+
证据文件的 `provenance` 需要 `sessionId` / `commandHash` / `resultDigest` / `runReceiptPath`
|
|
100
|
+
四个字段,全部能从 `session.json` 直接读出来。用模板生成,不要手抄:
|
|
101
|
+
|
|
102
|
+
```text
|
|
103
|
+
python <skills-root>/scripts/harness_fixback.py evidence-template \
|
|
104
|
+
--change-dir <change-dir> --kind red|green|verification|review \
|
|
105
|
+
--session <sessionId> --out <证据 JSON 路径> --json
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
输出即可直接喂给 `register-evidence`。撞 `FIXBACK_RUN_PROVENANCE_INVALID` 时,错误体会点名
|
|
109
|
+
**哪个字段、期望什么、实际什么**——按它改,不要去读实现反推。
|
|
110
|
+
|
|
111
|
+
### `--product-identity`
|
|
112
|
+
|
|
113
|
+
产品身份指纹,把 RED/GREEN 绑定到同一产品状态。规范取值是**当前 git HEAD**。省略即自动推导,
|
|
114
|
+
`launch-review` 会在返回体的 `resolvedProductIdentity` 里回显;`close --final-product-identity`
|
|
115
|
+
**原样引用那个值**,不要另行推导——推导方式不同就会撞 `FIXBACK_GREEN_IDENTITY_MISMATCH`。
|
|
116
|
+
|
|
117
|
+
### RED 必须在修复之前
|
|
118
|
+
|
|
119
|
+
RED 证明问题真实存在,GREEN 证明修复真的生效。先改完再把改动回退来凑 RED,证明的只是回退后的
|
|
120
|
+
状态,不是原始缺陷,中途还会留下脏工作树。
|
|
121
|
+
|
|
122
|
+
### close 的两张收据与 review-findings.json
|
|
123
|
+
|
|
124
|
+
`close` 要 `--affected-receipt`(`kind=verification`,受影响链的托管会话)和 `--review-receipt`
|
|
125
|
+
(`kind=review`,指向 `reports/review/review-findings.json`)。
|
|
126
|
+
|
|
127
|
+
review 收据的放行规则按 **disposition**,不按 severity:
|
|
128
|
+
|
|
129
|
+
| disposition | 是否阻塞 | 说明 |
|
|
130
|
+
|---|---|---|
|
|
131
|
+
| `OPEN` / 未登记 | ❌ 阻塞 | 还没人处置,必须先给结论 |
|
|
132
|
+
| `FIXED` / `NOT_APPLICABLE` | ✅ 放行 | 已闭环 |
|
|
133
|
+
| `ACCEPTED_RISK` / `DEFERRED` | ✅ 放行 | 记入收据 `residualRisks` 留痕 |
|
|
134
|
+
|
|
135
|
+
> ⚠️ **不要为了让 close 通过而清空或删改 `review-findings.json`。** 它是发现的真相源,写空等于
|
|
136
|
+
> 抹掉整轮审查的审计轨迹。处置结论属于另一个 sidecar:
|
|
137
|
+
>
|
|
138
|
+
> ```text
|
|
139
|
+
> python <skills-root>/scripts/harness_review.py write-dispositions \
|
|
140
|
+
> --change-dir <change-dir> --input <dispositions.json>
|
|
141
|
+
> ```
|
|
@@ -94,7 +94,7 @@ requested=true + path missing
|
|
|
94
94
|
6. **读取测试场景表**:`.harness/changes/<change-name>/plans/<change-name>-test-scenarios.md` → 获取测试真相源
|
|
95
95
|
7. **读取验证账本**:通过 state layout resolver 定位 `evidence/verification-ledger.json`(如存在)→ 复用已有 compile/unitTest 结果
|
|
96
96
|
8. **读取任务状态**:`.harness/changes/<change-name>/evidence/run-task-status.md`(如存在)→ 恢复上次运行状态
|
|
97
|
-
9. **读取 review fixback**:用户传入 `--fixback` 或要求修复 review 问题时,读取最新 `.harness/changes/<change-name>/reports/review/fixback-*.md`,并用 `harness_fixback.py` 将相关 RED/YELLOW 条目合并为一个批次;每条问题保留 RED/GREEN 证据,批次关闭后只触发一次 affected verification 与一次 review
|
|
97
|
+
9. **读取 review fixback**:用户传入 `--fixback` 或要求修复 review 问题时,读取最新 `.harness/changes/<change-name>/reports/review/fixback-*.md`,并用 `harness_fixback.py` 将相关 RED/YELLOW 条目合并为一个批次;每条问题保留 RED/GREEN 证据,批次关闭后只触发一次 affected verification 与一次 review。证据契约见下方[fixback 证据契约](#fixback-证据契约)——**动手前先读**,它决定了 RED 必须在修复之前采集
|
|
98
98
|
10. 确认 `项目规则(见 .harness/context-index.json)/` 规则已加载
|
|
99
99
|
11. **执行测试基础设施探测**(见下方"步骤 0.5")
|
|
100
100
|
12. 确认构建环境正常(构建命令按技术栈,见项目 CLAUDE.md 或 `.harness/config/harness-build-config.md`;如 Java 的 `powershell.exe -Command "mvn compile -pl <module> -o -q"`)
|
|
@@ -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=697a20734610e985; overlay=none; agent=cursor; do not edit -->
|
|
7
7
|
# harness-submit — 最终提交(含 worktree 合并)
|
|
8
8
|
|
|
9
9
|
## Purpose
|
|
@@ -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=697a20734610e985; overlay=none; agent=cursor; 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=697a20734610e985; overlay=none; agent=cursor; do not edit -->
|
|
7
7
|
# harness-test — 测试执行
|
|
8
8
|
|
|
9
9
|
## Purpose
|