@hunter-harness/workflow-harness 0.2.20 → 0.2.22
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/CONTEXT.md +20 -0
- package/harness/bundles/general/claude-code/harness-archive/SKILL.md +1 -1
- package/harness/bundles/general/claude-code/harness-archive/checklist.md +14 -0
- package/harness/bundles/general/claude-code/harness-archive/templates/render-summary.mjs +24 -3
- 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-review/SKILL.md +1 -1
- package/harness/bundles/general/claude-code/harness-run/SKILL.md +1 -1
- package/harness/bundles/general/claude-code/harness-run/checklist.md +6 -0
- package/harness/bundles/general/claude-code/harness-submit/SKILL.md +1 -1
- package/harness/bundles/general/claude-code/harness-submit/checklist.md +12 -0
- 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/harness-test/checklist.md +6 -0
- package/harness/bundles/general/claude-code/protocols/model-routing-protocol.md +22 -0
- package/harness/bundles/general/claude-code/scripts/harness_archive.py +606 -28
- package/harness/bundles/general/claude-code/scripts/harness_environment.py +459 -0
- package/harness/bundles/general/claude-code/scripts/harness_events.py +252 -15
- package/harness/bundles/general/claude-code/scripts/harness_integration.py +34 -0
- package/harness/bundles/general/claude-code/scripts/harness_ledger.py +127 -0
- package/harness/bundles/general/claude-code/scripts/harness_migration_head.py +220 -0
- package/harness/bundles/general/codebuddy/.harness-build.json +1 -1
- package/harness/bundles/general/codebuddy/CONTEXT.md +20 -0
- package/harness/bundles/general/codebuddy/harness-archive/SKILL.md +1 -1
- package/harness/bundles/general/codebuddy/harness-archive/checklist.md +14 -0
- package/harness/bundles/general/codebuddy/harness-archive/templates/render-summary.mjs +24 -3
- 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-review/SKILL.md +1 -1
- package/harness/bundles/general/codebuddy/harness-run/SKILL.md +1 -1
- package/harness/bundles/general/codebuddy/harness-run/checklist.md +6 -0
- package/harness/bundles/general/codebuddy/harness-submit/SKILL.md +1 -1
- package/harness/bundles/general/codebuddy/harness-submit/checklist.md +12 -0
- 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/harness-test/checklist.md +6 -0
- package/harness/bundles/general/codebuddy/protocols/model-routing-protocol.md +22 -0
- package/harness/bundles/general/codebuddy/scripts/harness_archive.py +606 -28
- package/harness/bundles/general/codebuddy/scripts/harness_environment.py +459 -0
- package/harness/bundles/general/codebuddy/scripts/harness_events.py +252 -15
- package/harness/bundles/general/codebuddy/scripts/harness_integration.py +34 -0
- package/harness/bundles/general/codebuddy/scripts/harness_ledger.py +127 -0
- package/harness/bundles/general/codebuddy/scripts/harness_migration_head.py +220 -0
- package/harness/bundles/general/codex/.harness-build.json +1 -1
- package/harness/bundles/general/codex/CONTEXT.md +20 -0
- package/harness/bundles/general/codex/harness-archive/SKILL.md +1 -1
- package/harness/bundles/general/codex/harness-archive/checklist.md +14 -0
- package/harness/bundles/general/codex/harness-archive/templates/render-summary.mjs +24 -3
- 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-review/SKILL.md +1 -1
- package/harness/bundles/general/codex/harness-run/SKILL.md +1 -1
- package/harness/bundles/general/codex/harness-run/checklist.md +6 -0
- package/harness/bundles/general/codex/harness-submit/SKILL.md +1 -1
- package/harness/bundles/general/codex/harness-submit/checklist.md +12 -0
- 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/harness-test/checklist.md +6 -0
- package/harness/bundles/general/codex/protocols/model-routing-protocol.md +22 -0
- package/harness/bundles/general/codex/scripts/harness_archive.py +606 -28
- package/harness/bundles/general/codex/scripts/harness_environment.py +459 -0
- package/harness/bundles/general/codex/scripts/harness_events.py +252 -15
- package/harness/bundles/general/codex/scripts/harness_integration.py +34 -0
- package/harness/bundles/general/codex/scripts/harness_ledger.py +127 -0
- package/harness/bundles/general/codex/scripts/harness_migration_head.py +220 -0
- package/harness/bundles/general/cursor/.harness-build.json +1 -1
- package/harness/bundles/general/cursor/CONTEXT.md +20 -0
- package/harness/bundles/general/cursor/harness-archive/SKILL.md +1 -1
- package/harness/bundles/general/cursor/harness-archive/checklist.md +14 -0
- package/harness/bundles/general/cursor/harness-archive/templates/render-summary.mjs +24 -3
- 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-review/SKILL.md +1 -1
- package/harness/bundles/general/cursor/harness-run/SKILL.md +1 -1
- package/harness/bundles/general/cursor/harness-run/checklist.md +6 -0
- package/harness/bundles/general/cursor/harness-submit/SKILL.md +1 -1
- package/harness/bundles/general/cursor/harness-submit/checklist.md +12 -0
- 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/harness-test/checklist.md +6 -0
- package/harness/bundles/general/cursor/protocols/model-routing-protocol.md +22 -0
- package/harness/bundles/general/cursor/scripts/harness_archive.py +606 -28
- package/harness/bundles/general/cursor/scripts/harness_environment.py +459 -0
- package/harness/bundles/general/cursor/scripts/harness_events.py +252 -15
- package/harness/bundles/general/cursor/scripts/harness_integration.py +34 -0
- package/harness/bundles/general/cursor/scripts/harness_ledger.py +127 -0
- package/harness/bundles/general/cursor/scripts/harness_migration_head.py +220 -0
- package/harness/bundles/java/claude-code/.harness-build.json +1 -1
- package/harness/bundles/java/claude-code/CONTEXT.md +20 -0
- package/harness/bundles/java/claude-code/harness-apidoc/SKILL.md +1 -1
- package/harness/bundles/java/claude-code/harness-archive/SKILL.md +1 -1
- package/harness/bundles/java/claude-code/harness-archive/checklist.md +14 -0
- package/harness/bundles/java/claude-code/harness-archive/templates/render-summary.mjs +24 -3
- 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-review/SKILL.md +1 -1
- package/harness/bundles/java/claude-code/harness-run/SKILL.md +1 -1
- package/harness/bundles/java/claude-code/harness-submit/SKILL.md +1 -1
- package/harness/bundles/java/claude-code/harness-submit/checklist.md +12 -0
- 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/protocols/model-routing-protocol.md +22 -0
- package/harness/bundles/java/claude-code/scripts/harness_archive.py +606 -28
- package/harness/bundles/java/claude-code/scripts/harness_environment.py +459 -0
- package/harness/bundles/java/claude-code/scripts/harness_events.py +252 -15
- package/harness/bundles/java/claude-code/scripts/harness_integration.py +34 -0
- package/harness/bundles/java/claude-code/scripts/harness_ledger.py +127 -0
- package/harness/bundles/java/claude-code/scripts/harness_migration_head.py +220 -0
- package/harness/bundles/java/codebuddy/.harness-build.json +1 -1
- package/harness/bundles/java/codebuddy/CONTEXT.md +20 -0
- package/harness/bundles/java/codebuddy/harness-apidoc/SKILL.md +1 -1
- package/harness/bundles/java/codebuddy/harness-archive/SKILL.md +1 -1
- package/harness/bundles/java/codebuddy/harness-archive/checklist.md +14 -0
- package/harness/bundles/java/codebuddy/harness-archive/templates/render-summary.mjs +24 -3
- 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-review/SKILL.md +1 -1
- package/harness/bundles/java/codebuddy/harness-run/SKILL.md +1 -1
- package/harness/bundles/java/codebuddy/harness-submit/SKILL.md +1 -1
- package/harness/bundles/java/codebuddy/harness-submit/checklist.md +12 -0
- 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/protocols/model-routing-protocol.md +22 -0
- package/harness/bundles/java/codebuddy/scripts/harness_archive.py +606 -28
- package/harness/bundles/java/codebuddy/scripts/harness_environment.py +459 -0
- package/harness/bundles/java/codebuddy/scripts/harness_events.py +252 -15
- package/harness/bundles/java/codebuddy/scripts/harness_integration.py +34 -0
- package/harness/bundles/java/codebuddy/scripts/harness_ledger.py +127 -0
- package/harness/bundles/java/codebuddy/scripts/harness_migration_head.py +220 -0
- package/harness/bundles/java/codex/.harness-build.json +1 -1
- package/harness/bundles/java/codex/CONTEXT.md +20 -0
- package/harness/bundles/java/codex/harness-apidoc/SKILL.md +1 -1
- package/harness/bundles/java/codex/harness-archive/SKILL.md +1 -1
- package/harness/bundles/java/codex/harness-archive/checklist.md +14 -0
- package/harness/bundles/java/codex/harness-archive/templates/render-summary.mjs +24 -3
- 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-review/SKILL.md +1 -1
- package/harness/bundles/java/codex/harness-run/SKILL.md +1 -1
- package/harness/bundles/java/codex/harness-submit/SKILL.md +1 -1
- package/harness/bundles/java/codex/harness-submit/checklist.md +12 -0
- 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/protocols/model-routing-protocol.md +22 -0
- package/harness/bundles/java/codex/scripts/harness_archive.py +606 -28
- package/harness/bundles/java/codex/scripts/harness_environment.py +459 -0
- package/harness/bundles/java/codex/scripts/harness_events.py +252 -15
- package/harness/bundles/java/codex/scripts/harness_integration.py +34 -0
- package/harness/bundles/java/codex/scripts/harness_ledger.py +127 -0
- package/harness/bundles/java/codex/scripts/harness_migration_head.py +220 -0
- package/harness/bundles/java/cursor/.harness-build.json +1 -1
- package/harness/bundles/java/cursor/CONTEXT.md +20 -0
- package/harness/bundles/java/cursor/harness-apidoc/SKILL.md +1 -1
- package/harness/bundles/java/cursor/harness-archive/SKILL.md +1 -1
- package/harness/bundles/java/cursor/harness-archive/checklist.md +14 -0
- package/harness/bundles/java/cursor/harness-archive/templates/render-summary.mjs +24 -3
- 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-review/SKILL.md +1 -1
- package/harness/bundles/java/cursor/harness-run/SKILL.md +1 -1
- package/harness/bundles/java/cursor/harness-submit/SKILL.md +1 -1
- package/harness/bundles/java/cursor/harness-submit/checklist.md +12 -0
- 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/protocols/model-routing-protocol.md +22 -0
- package/harness/bundles/java/cursor/scripts/harness_archive.py +606 -28
- package/harness/bundles/java/cursor/scripts/harness_environment.py +459 -0
- package/harness/bundles/java/cursor/scripts/harness_events.py +252 -15
- package/harness/bundles/java/cursor/scripts/harness_integration.py +34 -0
- package/harness/bundles/java/cursor/scripts/harness_ledger.py +127 -0
- package/harness/bundles/java/cursor/scripts/harness_migration_head.py +220 -0
- package/harness/manifests/general/claude-code.json +33 -21
- package/harness/manifests/general/codebuddy.json +33 -21
- package/harness/manifests/general/codex.json +33 -21
- package/harness/manifests/general/cursor.json +33 -21
- package/harness/manifests/java/claude-code.json +33 -21
- package/harness/manifests/java/codebuddy.json +33 -21
- package/harness/manifests/java/codex.json +33 -21
- package/harness/manifests/java/cursor.json +33 -21
- package/hunter-workflow-family.json +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Migration head single-source consistency check (Wave-2 H-7).
|
|
3
|
+
|
|
4
|
+
Canonical file (default ``.harness/config/migration-head.json``)::
|
|
5
|
+
|
|
6
|
+
{
|
|
7
|
+
"schemaVersion": 1,
|
|
8
|
+
"head": "005_example",
|
|
9
|
+
"sources": [
|
|
10
|
+
{"path": "backend/app/config.py", "pattern": "EXPECTED_ALEMBIC_HEAD\\s*=\\s*[\\\"']([^\\\"']+)[\\\"']"},
|
|
11
|
+
{"path": "integration/manifest.json", "jsonPointer": "/migrationHead"}
|
|
12
|
+
]
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
Each source must resolve to the same ``head`` value. This harness repo may not
|
|
16
|
+
ship alembic; consumer projects declare the canonical file and run::
|
|
17
|
+
|
|
18
|
+
python harness_migration_head.py check --project . --json
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
from __future__ import annotations
|
|
22
|
+
|
|
23
|
+
import argparse
|
|
24
|
+
import json
|
|
25
|
+
import re
|
|
26
|
+
import sys
|
|
27
|
+
from pathlib import Path
|
|
28
|
+
from typing import Any
|
|
29
|
+
|
|
30
|
+
DEFAULT_REL = ".harness/config/migration-head.json"
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def emit_json(payload: dict[str, Any], *, as_json: bool) -> None:
|
|
34
|
+
if as_json:
|
|
35
|
+
sys.stdout.write(json.dumps(payload, ensure_ascii=False, indent=2) + "\n")
|
|
36
|
+
else:
|
|
37
|
+
code = payload.get("code") or ("OK" if payload.get("ok") else "FAIL")
|
|
38
|
+
sys.stdout.write(f"{code}\n")
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def emit_error(code: str, message: str, *, as_json: bool, extra: dict[str, Any] | None = None) -> int:
|
|
42
|
+
payload: dict[str, Any] = {"ok": False, "code": code, "message": message}
|
|
43
|
+
if extra:
|
|
44
|
+
payload.update(extra)
|
|
45
|
+
stream = sys.stdout if as_json else sys.stderr
|
|
46
|
+
if as_json:
|
|
47
|
+
stream.write(json.dumps(payload, ensure_ascii=False, indent=2) + "\n")
|
|
48
|
+
else:
|
|
49
|
+
stream.write(f"{code}: {message}\n")
|
|
50
|
+
return 1
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def _read_json_pointer(data: Any, pointer: str) -> Any:
|
|
54
|
+
if not pointer.startswith("/"):
|
|
55
|
+
raise ValueError(f"jsonPointer must start with /: {pointer}")
|
|
56
|
+
cur = data
|
|
57
|
+
for part in pointer.lstrip("/").split("/"):
|
|
58
|
+
part = part.replace("~1", "/").replace("~0", "~")
|
|
59
|
+
if isinstance(cur, dict) and part in cur:
|
|
60
|
+
cur = cur[part]
|
|
61
|
+
else:
|
|
62
|
+
raise KeyError(pointer)
|
|
63
|
+
return cur
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def _extract_source(project: Path, source: dict[str, Any]) -> str:
|
|
67
|
+
rel = str(source.get("path") or "").strip().replace("\\", "/")
|
|
68
|
+
if not rel or rel.startswith("/") or ".." in rel.split("/"):
|
|
69
|
+
raise ValueError(f"invalid source path: {rel}")
|
|
70
|
+
target = (project / rel).resolve()
|
|
71
|
+
try:
|
|
72
|
+
target.relative_to(project.resolve())
|
|
73
|
+
except ValueError as exc:
|
|
74
|
+
raise ValueError(f"source escapes project: {rel}") from exc
|
|
75
|
+
if not target.is_file():
|
|
76
|
+
raise FileNotFoundError(rel)
|
|
77
|
+
text = target.read_text(encoding="utf-8-sig")
|
|
78
|
+
if "jsonPointer" in source and source["jsonPointer"]:
|
|
79
|
+
data = json.loads(text)
|
|
80
|
+
value = _read_json_pointer(data, str(source["jsonPointer"]))
|
|
81
|
+
if value is None or (isinstance(value, str) and not value.strip()):
|
|
82
|
+
raise ValueError(f"empty value at {source['jsonPointer']} in {rel}")
|
|
83
|
+
return str(value).strip()
|
|
84
|
+
pattern = str(source.get("pattern") or "").strip()
|
|
85
|
+
if not pattern:
|
|
86
|
+
raise ValueError(f"source {rel} requires pattern or jsonPointer")
|
|
87
|
+
match = re.search(pattern, text, flags=re.MULTILINE)
|
|
88
|
+
if not match:
|
|
89
|
+
raise ValueError(f"pattern did not match in {rel}")
|
|
90
|
+
return str(match.group(1) if match.lastindex else match.group(0)).strip()
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def check_migration_head(
|
|
94
|
+
project: Path,
|
|
95
|
+
*,
|
|
96
|
+
config_rel: str = DEFAULT_REL,
|
|
97
|
+
) -> dict[str, Any]:
|
|
98
|
+
project = project.resolve()
|
|
99
|
+
config_path = (project / config_rel).resolve()
|
|
100
|
+
try:
|
|
101
|
+
config_path.relative_to(project)
|
|
102
|
+
except ValueError:
|
|
103
|
+
return {
|
|
104
|
+
"ok": False,
|
|
105
|
+
"code": "MIGRATION_HEAD_CONFIG_ESCAPE",
|
|
106
|
+
"message": f"config path escapes project: {config_rel}",
|
|
107
|
+
}
|
|
108
|
+
if not config_path.is_file():
|
|
109
|
+
return {
|
|
110
|
+
"ok": False,
|
|
111
|
+
"code": "MIGRATION_HEAD_MISSING",
|
|
112
|
+
"message": f"canonical migration-head file missing: {config_rel}",
|
|
113
|
+
"configPath": str(config_path),
|
|
114
|
+
}
|
|
115
|
+
try:
|
|
116
|
+
data = json.loads(config_path.read_text(encoding="utf-8-sig"))
|
|
117
|
+
except (OSError, json.JSONDecodeError) as exc:
|
|
118
|
+
return {
|
|
119
|
+
"ok": False,
|
|
120
|
+
"code": "MIGRATION_HEAD_UNREADABLE",
|
|
121
|
+
"message": str(exc),
|
|
122
|
+
"configPath": str(config_path),
|
|
123
|
+
}
|
|
124
|
+
if not isinstance(data, dict) or data.get("schemaVersion") != 1:
|
|
125
|
+
return {
|
|
126
|
+
"ok": False,
|
|
127
|
+
"code": "MIGRATION_HEAD_SCHEMA_INVALID",
|
|
128
|
+
"message": "migration-head.json must be schemaVersion 1 object",
|
|
129
|
+
"configPath": str(config_path),
|
|
130
|
+
}
|
|
131
|
+
head = str(data.get("head") or "").strip()
|
|
132
|
+
sources = data.get("sources")
|
|
133
|
+
if not head:
|
|
134
|
+
return {
|
|
135
|
+
"ok": False,
|
|
136
|
+
"code": "MIGRATION_HEAD_EMPTY",
|
|
137
|
+
"message": "head must be a non-empty string",
|
|
138
|
+
"configPath": str(config_path),
|
|
139
|
+
}
|
|
140
|
+
if not isinstance(sources, list) or not sources:
|
|
141
|
+
return {
|
|
142
|
+
"ok": False,
|
|
143
|
+
"code": "MIGRATION_HEAD_SOURCES_EMPTY",
|
|
144
|
+
"message": "sources must be a non-empty array",
|
|
145
|
+
"configPath": str(config_path),
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
observed: list[dict[str, Any]] = []
|
|
149
|
+
drifts: list[dict[str, Any]] = []
|
|
150
|
+
for index, source in enumerate(sources):
|
|
151
|
+
if not isinstance(source, dict):
|
|
152
|
+
return {
|
|
153
|
+
"ok": False,
|
|
154
|
+
"code": "MIGRATION_HEAD_SOURCE_INVALID",
|
|
155
|
+
"message": f"sources[{index}] must be an object",
|
|
156
|
+
}
|
|
157
|
+
try:
|
|
158
|
+
value = _extract_source(project, source)
|
|
159
|
+
except (OSError, ValueError, KeyError, json.JSONDecodeError) as exc:
|
|
160
|
+
return {
|
|
161
|
+
"ok": False,
|
|
162
|
+
"code": "MIGRATION_HEAD_SOURCE_UNREADABLE",
|
|
163
|
+
"message": str(exc),
|
|
164
|
+
"index": index,
|
|
165
|
+
"path": source.get("path"),
|
|
166
|
+
}
|
|
167
|
+
item = {"path": source.get("path"), "value": value}
|
|
168
|
+
observed.append(item)
|
|
169
|
+
if value != head:
|
|
170
|
+
drifts.append({**item, "expected": head})
|
|
171
|
+
|
|
172
|
+
if drifts:
|
|
173
|
+
return {
|
|
174
|
+
"ok": False,
|
|
175
|
+
"code": "MIGRATION_HEAD_DRIFT",
|
|
176
|
+
"message": "one or more sources disagree with canonical head",
|
|
177
|
+
"head": head,
|
|
178
|
+
"drifts": drifts,
|
|
179
|
+
"observed": observed,
|
|
180
|
+
"configPath": str(config_path),
|
|
181
|
+
}
|
|
182
|
+
return {
|
|
183
|
+
"ok": True,
|
|
184
|
+
"code": "MIGRATION_HEAD_OK",
|
|
185
|
+
"head": head,
|
|
186
|
+
"observed": observed,
|
|
187
|
+
"configPath": str(config_path),
|
|
188
|
+
"sourceCount": len(observed),
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
def build_parser() -> argparse.ArgumentParser:
|
|
193
|
+
parser = argparse.ArgumentParser(prog="harness_migration_head.py")
|
|
194
|
+
sub = parser.add_subparsers(dest="command", required=True)
|
|
195
|
+
check = sub.add_parser("check", help="verify sources match canonical head")
|
|
196
|
+
check.add_argument("--project", required=True)
|
|
197
|
+
check.add_argument("--config", default=DEFAULT_REL)
|
|
198
|
+
check.add_argument("--json", action="store_true")
|
|
199
|
+
return parser
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
def main(argv: list[str] | None = None) -> int:
|
|
203
|
+
args = build_parser().parse_args(argv)
|
|
204
|
+
as_json = bool(getattr(args, "json", False))
|
|
205
|
+
if args.command == "check":
|
|
206
|
+
result = check_migration_head(Path(args.project), config_rel=str(args.config))
|
|
207
|
+
if not result.get("ok"):
|
|
208
|
+
return emit_error(
|
|
209
|
+
str(result.get("code") or "MIGRATION_HEAD_FAIL"),
|
|
210
|
+
str(result.get("message") or "check failed"),
|
|
211
|
+
as_json=as_json,
|
|
212
|
+
extra={k: v for k, v in result.items() if k not in {"ok", "code", "message"}},
|
|
213
|
+
)
|
|
214
|
+
emit_json(result, as_json=as_json)
|
|
215
|
+
return 0
|
|
216
|
+
return emit_error("UNKNOWN_COMMAND", f"unknown command: {args.command}", as_json=as_json)
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
if __name__ == "__main__":
|
|
220
|
+
raise SystemExit(main())
|
|
@@ -48,6 +48,26 @@ _Avoid_: 每次重新探测, skill 内硬编码项目参数
|
|
|
48
48
|
一项验证(compile/install/unitTest/apiTest)所依赖源文件集合的内容哈希。指纹一致且证据完整才允许 🔁REUSED;无法证明即保守重跑。
|
|
49
49
|
_Avoid_: 仅凭 diffHash 复用, 无证据跳过
|
|
50
50
|
|
|
51
|
+
**验证图(verification graph)**:
|
|
52
|
+
`evidence/verification-graph.json` 按 canonical identity 持久化验证节点;`can-reuse` 返回 `invalidationCode`(如 `INPUT_CHANGED`);profile `mergeVerification.requiredOnMerge` 进入 integration verify。详见 ledger / integration 脚本。
|
|
53
|
+
_Avoid_: 冻结后无因重跑全量
|
|
54
|
+
|
|
55
|
+
**Migration head 单源**:
|
|
56
|
+
消费项目用 `.harness/config/migration-head.json` 声明 canonical head 与联动 sources;`harness_migration_head.py check` 做静态一致性门禁。
|
|
57
|
+
_Avoid_: config/CI/manifest 多处手写 head
|
|
58
|
+
|
|
59
|
+
**事件批处理**:
|
|
60
|
+
同阶段多事件用 `harness_events.py batch-append`(单锁多行);`phase.end` 仍走单次 append。
|
|
61
|
+
_Avoid_: 机械事件逐条起进程
|
|
62
|
+
|
|
63
|
+
**能力级模型路由**:
|
|
64
|
+
任务用 `economy` / `balanced` / `frontier` 表述;禁止在通用 harness 规则硬编码供应商模型名。详见 `protocols/model-routing-protocol.md`。
|
|
65
|
+
_Avoid_: skill 内写死 claude-/gpt- 模型 id
|
|
66
|
+
|
|
67
|
+
**Overlay 所有权**:
|
|
68
|
+
Bundle 安装目标由安装器管理;项目自有配置放 `.harness/project-overlay/`(或 project.yaml 侧)。`--force-managed` 必须配合 `--yes`/`--confirmed`,冲突 fail closed。
|
|
69
|
+
_Avoid_: 静默 force-managed
|
|
70
|
+
|
|
51
71
|
**服务会话**:
|
|
52
72
|
`runtime/service-session.json` 记录的 AI 启动服务生命周期。指纹匹配静默复用、不匹配自动重启、归档收尾统一停止;只有用户自启服务占端口才询问。
|
|
53
73
|
_Avoid_: 每轮测试后必杀服务
|
|
@@ -3,7 +3,7 @@ name: harness-archive
|
|
|
3
3
|
description: 归档所有变更产出(计划/测试报告/审查/SQL/API文档)到
|
|
4
4
|
.harness/archive/,含归档元数据和可视化最终总结。使用场景:归档、完成归档、收尾、打包产出物
|
|
5
5
|
---
|
|
6
|
-
<!-- generated by harness_deploy.py; core=
|
|
6
|
+
<!-- generated by harness_deploy.py; core=f211142684412a01; overlay=none; agent=codebuddy; do not edit -->
|
|
7
7
|
# harness-archive — 变更归档
|
|
8
8
|
|
|
9
9
|
## Purpose
|
|
@@ -14,6 +14,20 @@ description: harness-archive 的归档前检查项和归档后验证项。仅在
|
|
|
14
14
|
- before/after checksum 不一致时,不得删除原目录。
|
|
15
15
|
- 默认渲染器 `templates/render-summary.mjs`(finalize 内嵌调用)。
|
|
16
16
|
|
|
17
|
+
### Wave-A 状态机与身份(IA-1 / IA-4)
|
|
18
|
+
|
|
19
|
+
硬顺序:feature frozen → local gates → product candidate CI → **CI green** → merge → authoritative CI → archive product candidate → archive-only governance → release。
|
|
20
|
+
|
|
21
|
+
- [ ] `evidence/product-candidate-ci.json`(或等价 ledger 字段)`conclusion=success`,含 `runUrl` + `commit`;否则 `PRODUCT_CI_NOT_GREEN` 阻断
|
|
22
|
+
- [ ] summary/identity 含 `productCommit` / `productTreeHash` / `archiveCommit`;`productTreeHash` 排除 `.harness/**`
|
|
23
|
+
- [ ] 产品输入在 archive 后变化 → `ARCHIVE_EVIDENCE_REOPEN_REQUIRED`,旧 archive 不可作发布证据
|
|
24
|
+
- [ ] Manifest:coverage 字节在最终 snapshot 后不得静默漂移;后写报告必须 `exclusionReasons`,禁止假绿 `checksumStatus=OK`
|
|
25
|
+
|
|
26
|
+
### Environment 合同(IA-3)
|
|
27
|
+
|
|
28
|
+
- [ ] 使用可写环境栈前:`harness_environment.py fingerprint` → `acquire` lease → run → `release`
|
|
29
|
+
- [ ] 租约落在主仓 `.harness/runtime/env-leases/`;跨 change 默认不得共享可写 volume
|
|
30
|
+
|
|
17
31
|
## 归档前检查(Phase 1)
|
|
18
32
|
|
|
19
33
|
> ⚠️ **单一所有权**:finalize 内部负责且仅负责一次 `phase.start` / `phase.end`。归档前检查不得自行追加 archive 阶段边界。
|
|
@@ -42,6 +42,16 @@ const duration = (minutes) => {
|
|
|
42
42
|
if (value < 60) return `${Math.round(value * 10) / 10} 分钟`;
|
|
43
43
|
return `${Math.floor(value / 60)} 小时 ${Math.round(value % 60)} 分钟`;
|
|
44
44
|
};
|
|
45
|
+
const durationMs = (ms) => {
|
|
46
|
+
const value = number(ms);
|
|
47
|
+
if (value <= 0) return "0 秒";
|
|
48
|
+
if (value < 1000) return `${value}ms`;
|
|
49
|
+
const seconds = value / 1000;
|
|
50
|
+
if (seconds < 60) return `${Math.round(seconds * 10) / 10} 秒`;
|
|
51
|
+
const minutes = seconds / 60;
|
|
52
|
+
if (minutes < 60) return `${Math.round(minutes * 10) / 10} 分钟`;
|
|
53
|
+
return `${Math.floor(minutes / 60)} 小时 ${Math.round(minutes % 60)} 分钟`;
|
|
54
|
+
};
|
|
45
55
|
|
|
46
56
|
const stages = Object.entries(record(data.stageStatus));
|
|
47
57
|
const verification = record(data.verification);
|
|
@@ -49,6 +59,17 @@ const unit = record(verification.unitTests);
|
|
|
49
59
|
const api = record(verification.apiTests);
|
|
50
60
|
const durations = record(data.durations);
|
|
51
61
|
const durationStages = list(durations.stages);
|
|
62
|
+
const timing = record(data.timing);
|
|
63
|
+
const wallLabel = timing.workflowWallClockMs != null
|
|
64
|
+
? durationMs(timing.workflowWallClockMs)
|
|
65
|
+
: (durations.totalLabel || duration(durations.totalMinutes));
|
|
66
|
+
const timingColumnsHtml = `
|
|
67
|
+
<article class="card" id="timingColumns"><h2>时长(三列)</h2>
|
|
68
|
+
<div class="row"><div><strong>活动执行</strong><small>stageActiveExecution</small></div><span>${esc(durationMs(timing.stageActiveExecutionMs))}</span></div>
|
|
69
|
+
<div class="row"><div><strong>阶段墙钟</strong><small>stageWallClockSpan</small></div><span>${esc(durationMs(timing.stageWallClockSpanMs))}</span></div>
|
|
70
|
+
<div class="row"><div><strong>全流程墙钟</strong><small>workflowWallClock</small></div><span>${esc(durationMs(timing.workflowWallClockMs))}</span></div>
|
|
71
|
+
<p><small>reportCutoffAt=<code id="reportCutoffAt">${esc(timing.reportCutoffAt || "N/A")}</code> · durations.totalMinutes 仅为活动执行,不冒充墙钟</small></p>
|
|
72
|
+
</article>`;
|
|
52
73
|
const maxMinutes = Math.max(1, ...durationStages.map((item) => number(item.minutes)));
|
|
53
74
|
const diff = record(data.diffStat);
|
|
54
75
|
const risks = list(data.knownRisks);
|
|
@@ -86,12 +107,12 @@ const html = `<!doctype html><html lang="zh-CN"><head><meta charset="utf-8"><met
|
|
|
86
107
|
@media(max-width:800px){.metrics{grid-template-columns:1fr 1fr}.grid,.risk{grid-template-columns:1fr}}@media(max-width:480px){main{width:calc(100% - 18px);margin-top:9px}.metrics{grid-template-columns:1fr}.hero{padding:19px}h1{font-size:23px}dl{grid-template-columns:1fr}}
|
|
87
108
|
</style></head><body><main>
|
|
88
109
|
<section class="hero"><div class="eyebrow">HARNESS EXECUTION REPORT</div><h1>${esc(data.changeName || "未命名变更")}</h1><p class="goal">${esc(data.businessGoal || "未记录业务目标")}</p><div class="status">${pill(data.finalStatus)}<span>基于事件、验证账本与 Git 证据生成${data.riskTier ? ` · riskTier=${esc(data.riskTier)}` : ""}</span></div>${reasonHtml ? `<div class="status">${reasonHtml}</div>` : ""}</section>
|
|
89
|
-
<section class="metrics"><article class="metric"><small
|
|
90
|
-
<section class="grid"><div><article class="card"><h2>验证结论</h2>${verificationHtml}</article><article class="card"><h2
|
|
110
|
+
<section class="metrics"><article class="metric"><small>产品提交</small><strong><code title="${esc(data.productCommit || data.finalCommit)}">${esc(shortHash(data.productCommit || data.finalCommit))}</code></strong><small>archive=${esc(shortHash(data.archiveCommit || data.finalCommit))}</small></article><article class="metric"><small>代码范围</small><strong>${number(diff.filesChanged)} 个文件</strong><small><span class="plus">+${number(diff.insertions)}</span> · <span class="minus">-${number(diff.deletions)}</span></small></article><article class="metric"><small>全流程墙钟</small><strong>${esc(wallLabel)}</strong><small>活动=${esc(durationMs(timing.stageActiveExecutionMs))} · ${durationStages.length} 阶段</small></article><article class="metric"><small>归档完整性</small><strong>${esc(record(data.archiveManifest).checksumStatus || "UNKNOWN")}</strong><small>${number(record(data.archiveManifest).totalArchiveFiles)} 个归档文件</small></article></section>
|
|
111
|
+
<section class="grid"><div><article class="card"><h2>验证结论</h2>${verificationHtml}</article><article class="card"><h2>阶段耗时(活动)</h2>${durationHtml}</article>${timingColumnsHtml}</div><div><article class="card"><h2>阶段状态</h2>${stageHtml}</article><article class="card"><h2>风险与后续</h2><div class="risk"><div><h3>已知风险</h3><ul>${riskHtml}</ul></div><div><h3>人工动作</h3><ul>${actionHtml}</ul></div></div></article></div></section>
|
|
91
112
|
<details><summary>变更文件(${files.length})</summary><div><table><thead><tr><th>文件</th><th>新增</th><th>删除</th></tr></thead><tbody>${fileRows}</tbody></table></div></details>
|
|
92
113
|
<details><summary>执行时间线与工具交接(${timeline.length})</summary><div><table><thead><tr><th>阶段</th><th>尝试</th><th>状态</th><th>来源 / 摘要</th></tr></thead><tbody>${timelineRows}</tbody></table></div></details>
|
|
93
114
|
<details><summary>命令证据(${commands.length})</summary><div><table><thead><tr><th>阶段</th><th>命令</th><th>结果</th></tr></thead><tbody>${commandRows}</tbody></table></div></details>
|
|
94
|
-
<details><summary>技术元数据</summary><div><dl><dt
|
|
115
|
+
<details><summary>技术元数据</summary><div><dl><dt>产品提交</dt><dd><code>${esc(data.productCommit || data.finalCommit || "N/A")}</code></dd><dt>产品树哈希</dt><dd><code>${esc(data.productTreeHash || "N/A")}</code></dd><dt>归档提交</dt><dd><code>${esc(data.archiveCommit || data.finalCommit || "N/A")}</code></dd><dt>基线提交</dt><dd><code>${esc(data.baseCommit || "N/A")}</code></dd><dt>Git 范围</dt><dd><code>${esc(diff.range || "N/A")}</code></dd><dt>报告数据版本</dt><dd>${esc(data.schemaVersion || "N/A")}</dd><dt>事实来源</dt><dd>${esc(list(record(data.reportPipeline).sources).join(" · ") || "N/A")}</dd></dl></div></details>
|
|
95
116
|
</main></body></html>`;
|
|
96
117
|
|
|
97
118
|
fs.mkdirSync(path.dirname(outPath), { recursive: true });
|
|
@@ -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=f211142684412a01; overlay=none; agent=codebuddy; do not edit -->
|
|
7
7
|
# harness-codebase-map — 代码库地图
|
|
8
8
|
|
|
9
9
|
## Purpose
|
|
@@ -3,7 +3,7 @@ name: harness-knowledge-ingest
|
|
|
3
3
|
description: 从 .harness/archive 归档整理、同步和维护项目知识索引。适用场景:ingest knowledge、sync
|
|
4
4
|
knowledge、rebuild knowledge index、promote knowledge、确认知识条目、检查知识库是否过期。
|
|
5
5
|
---
|
|
6
|
-
<!-- generated by harness_deploy.py; core=
|
|
6
|
+
<!-- generated by harness_deploy.py; core=f211142684412a01; overlay=none; agent=codebuddy; do not edit -->
|
|
7
7
|
# harness-knowledge-ingest
|
|
8
8
|
|
|
9
9
|
从 `.harness/archive/**/reports/final/summary-data.json` 抽取项目知识,生成并维护本地 `.harness/knowledge/` 索引。
|
|
@@ -3,7 +3,7 @@ name: harness-knowledge-query
|
|
|
3
3
|
description: 根据新需求、设计任务、代码修改请求或问题排查,在规划和编码前查询 .harness/knowledge
|
|
4
4
|
历史上下文。适用场景:query knowledge、查找历史需求、根据归档理解需求、继续之前类似开发、生成需求上下文包。
|
|
5
5
|
---
|
|
6
|
-
<!-- generated by harness_deploy.py; core=
|
|
6
|
+
<!-- generated by harness_deploy.py; core=f211142684412a01; overlay=none; agent=codebuddy; do not edit -->
|
|
7
7
|
# harness-knowledge-query
|
|
8
8
|
|
|
9
9
|
并行 change 中查询必须传 `--change <change-id>`;成功后以该 change 的 `meta/knowledge-context.json` 为稳定指针。全局 `context-packs/latest.json` 仅向后兼容,不得用于判断多个 active change 的上下文归属。
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
name: harness-plan
|
|
3
3
|
description: 将需求转化为设计文档+实施计划+完整测试场景表,必须在编码前完成。使用场景:需求分析、feature plan、技术方案设计、实现方案规划
|
|
4
4
|
---
|
|
5
|
-
<!-- generated by harness_deploy.py; core=
|
|
5
|
+
<!-- generated by harness_deploy.py; core=f211142684412a01; overlay=none; agent=codebuddy; do not edit -->
|
|
6
6
|
# harness-plan — 需求规划
|
|
7
7
|
|
|
8
8
|
## Purpose
|
|
@@ -3,7 +3,7 @@ name: harness-review
|
|
|
3
3
|
description: 6维度代码审查(架构/安全/规范/兼容/测试/性能),对照项目规则(见
|
|
4
4
|
.harness/context-index.json)和测试场景表,在隔离上下文运行。使用场景:代码审查、提交前检查、合并评审
|
|
5
5
|
---
|
|
6
|
-
<!-- generated by harness_deploy.py; core=
|
|
6
|
+
<!-- generated by harness_deploy.py; core=f211142684412a01; overlay=none; agent=codebuddy; do not edit -->
|
|
7
7
|
# harness-review — 代码审查
|
|
8
8
|
|
|
9
9
|
## Purpose
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
name: harness-run
|
|
3
3
|
description: 按变更簇执行 TDD 编码循环(RED→GREEN→REFACTOR→编译验证),逐变更簇实现计划中的任务。使用场景:开始编码、实现功能、写代码、TDD 编码
|
|
4
4
|
---
|
|
5
|
-
<!-- generated by harness_deploy.py; core=
|
|
5
|
+
<!-- generated by harness_deploy.py; core=f211142684412a01; overlay=none; agent=codebuddy; do not edit -->
|
|
6
6
|
# harness-run — 需求编码
|
|
7
7
|
|
|
8
8
|
## Purpose
|
|
@@ -267,3 +267,9 @@ description: harness-run 的执行检查清单。仅在编码执行时读取。
|
|
|
267
267
|
- [ ] 如果新增筛选参数,设计中已明确非法值行为
|
|
268
268
|
- [ ] 如果采用“忽略并不过滤”,必须有用户确认记录
|
|
269
269
|
- [ ] 测试场景覆盖非法值
|
|
270
|
+
|
|
271
|
+
|
|
272
|
+
## Wave-2 — Migration head(H-7)
|
|
273
|
+
|
|
274
|
+
- [ ] 若变更触及 alembic/versions 或项目约定 migration 路径:列出 config / CI / env / manifest 联动文件
|
|
275
|
+
- [ ] 消费项目存在 .harness/config/migration-head.json 时,跑 harness_migration_head.py check --project . --json
|
|
@@ -3,7 +3,7 @@ name: harness-submit
|
|
|
3
3
|
description: 最终提交封装:验证→中文 commit→提交/推送;worktree 模式含 --no-ff
|
|
4
4
|
合并回主分支。使用场景:提交代码、commit、push、合并分支、merge to main、完成开发
|
|
5
5
|
---
|
|
6
|
-
<!-- generated by harness_deploy.py; core=
|
|
6
|
+
<!-- generated by harness_deploy.py; core=f211142684412a01; overlay=none; agent=codebuddy; do not edit -->
|
|
7
7
|
# harness-submit — 最终提交(含 worktree 合并)
|
|
8
8
|
|
|
9
9
|
## Purpose
|
|
@@ -15,6 +15,12 @@ description: harness-submit 的8 步工作流详细步骤和执行日志记录
|
|
|
15
15
|
- blocking user confirmation 参数错误时不得自行绕过,必须改用固定模板重问。
|
|
16
16
|
- staged diff 是唯一提交依据。
|
|
17
17
|
|
|
18
|
+
### Wave-A 发布身份与环境边界
|
|
19
|
+
|
|
20
|
+
- [ ] 产品候选身份与归档治理身份分离:`productCommit`/`productTreeHash` 绑产品验证;`archiveCommit` 可晚于产品
|
|
21
|
+
- [ ] 进入 candidate CI / archive 前确认 product candidate CI 证据可被 `harness_archive status` 读取为 green
|
|
22
|
+
- [ ] 需要可写 DB/栈时走 `harness_environment` lease;禁止跨 change 默认可写共享
|
|
23
|
+
|
|
18
24
|
|
|
19
25
|
## 步骤 0:启动准备
|
|
20
26
|
|
|
@@ -420,3 +426,9 @@ ledger 顶层写入 `"mergeFinalHash": "<journal pushedHead>"`;经 `harness_ga
|
|
|
420
426
|
- worktree: 已清理(迁根后)
|
|
421
427
|
- 下一步: `/harness-archive`
|
|
422
428
|
```
|
|
429
|
+
|
|
430
|
+
|
|
431
|
+
## Wave-2 — 验证复用与 merge 面(H-9)
|
|
432
|
+
|
|
433
|
+
- [ ] 代码冻结后,同 identity 的 unitTestFull 须 can-reuse;禁止无 invalidationCode 整套重跑
|
|
434
|
+
- [ ] integration verify 会合并 profile mergeVerification.requiredOnMerge(若配置)
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
name: harness-sync
|
|
3
3
|
description: 检查并更新项目AI元数据(CodeGraph索引、harness-codebase-map分析、CLAUDE.md、AGENTS.md、.harness/完整性),确保Claude对代码库的理解与最新代码一致。当用户说'同步/更新索引/刷新元数据/检查一致性'时使用
|
|
4
4
|
---
|
|
5
|
-
<!-- generated by harness_deploy.py; core=
|
|
5
|
+
<!-- generated by harness_deploy.py; core=f211142684412a01; overlay=none; agent=codebuddy; do not edit -->
|
|
6
6
|
# harness-sync — 元数据同步
|
|
7
7
|
|
|
8
8
|
## Purpose
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
name: harness-test
|
|
3
3
|
description: 测试执行:读取场景表,执行单元测试+API接口测试+数据兼容验证,输出测试报告。当用户说'跑测试/验证/跑用例/接口测试/单元测试'时使用
|
|
4
4
|
---
|
|
5
|
-
<!-- generated by harness_deploy.py; core=
|
|
5
|
+
<!-- generated by harness_deploy.py; core=f211142684412a01; overlay=none; agent=codebuddy; do not edit -->
|
|
6
6
|
# harness-test — 测试执行
|
|
7
7
|
|
|
8
8
|
## Purpose
|
|
@@ -6,6 +6,12 @@ description: harness-test 的 Phase 0 环境准备 + Playwright 探测 + 关门
|
|
|
6
6
|
|
|
7
7
|
以下检查项确认完了再开始测试。每一项背后都有踩坑经历。
|
|
8
8
|
|
|
9
|
+
### Wave-A 环境合同(IA-3)
|
|
10
|
+
|
|
11
|
+
- [ ] 需要可写 DB/栈时:先 `harness_environment.py fingerprint` → `acquire` lease → 再跑测试 → `release`
|
|
12
|
+
- [ ] 无 lease 不得占用可写栈(`ENVIRONMENT_LEASE_REQUIRED`);跨 change 默认可写共享拒绝(`ENVIRONMENT_LEASE_CROSS_CHANGE`)
|
|
13
|
+
- [ ] 租约目录:主仓 `.harness/runtime/env-leases/`(与 change 状态目录分离)
|
|
14
|
+
|
|
9
15
|
#### 0.0 加载项目配置(如存在)与确定变更名
|
|
10
16
|
|
|
11
17
|
先检查当前项目的测试配置文件(按优先级查找):
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# model-routing-protocol — 能力级模型路由(Wave-2 H-16)
|
|
2
|
+
|
|
3
|
+
> 本协议定义 **economy / balanced / frontier** 三档能力级。通用 Harness 规则与 skill **禁止**硬编码供应商模型名(如 `claude-*`、`gpt-*`、`gemini-*`)。宿主把能力级映射到具体模型;映射表属于项目/宿主配置,不属于本协议正文。
|
|
4
|
+
|
|
5
|
+
## 能力级
|
|
6
|
+
|
|
7
|
+
| 级别 | 适用任务 | 示例 |
|
|
8
|
+
|------|----------|------|
|
|
9
|
+
| `economy` | 机械提取、日志压缩、等待监控、格式化摘要 | 解析测试输出行、压缩长日志、轮询服务就绪 |
|
|
10
|
+
| `balanced` | 常规实现、常规修复、常规文档修订 | 按 plan 实现 CRUD、修 YELLOW、补 checklist |
|
|
11
|
+
| `frontier` | 架构取舍、安全/并发/迁移、最终审查、高风险判断 | 设计审批、RED 安全项、schema 迁移方案、merge 前终审 |
|
|
12
|
+
|
|
13
|
+
## 路由规则
|
|
14
|
+
|
|
15
|
+
1. Skill/任务描述用能力级(`--capability economy|balanced|frontier` 或文案「本任务=frontier」),不写供应商模型 id。
|
|
16
|
+
2. 宿主或 `HUNTER_HARNESS_MODEL` 仅作**遥测**(`executor_model`),不得反过来驱动通用规则分支。
|
|
17
|
+
3. **降级**(frontier→balanced→economy)必须由确定性命令结果或更高能力审查验收;不得用更低能力级的模型输出替代测试/门禁证据。
|
|
18
|
+
4. 无法映射某能力级时:fail closed,列出可用级别,禁止静默落到未知模型。
|
|
19
|
+
|
|
20
|
+
## 与验证的关系
|
|
21
|
+
|
|
22
|
+
能力级不改变 ledger / can-reuse / verification graph 合同。模型便宜不能成为跳过 `unitTestFull` 或忽略 `requiredOnMerge` 的理由。
|