@hunter-harness/workflow-harness 0.2.46 → 0.2.48
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/harness/bundles/general/claude-code/.harness-build.json +1 -1
- package/harness/bundles/general/claude-code/README.md +9 -8
- package/harness/bundles/general/claude-code/harness-archive/SKILL.md +10 -11
- package/harness/bundles/general/claude-code/harness-codebase-map/SKILL.md +1 -1
- package/harness/bundles/general/claude-code/harness-knowledge-ingest/SKILL.md +1 -1
- package/harness/bundles/general/claude-code/harness-knowledge-ingest/scripts/harness_knowledge.py +347 -1
- package/harness/bundles/general/claude-code/harness-knowledge-query/SKILL.md +1 -1
- package/harness/bundles/general/claude-code/harness-plan/SKILL.md +6 -3
- package/harness/bundles/general/claude-code/harness-review/SKILL.md +6 -7
- package/harness/bundles/general/claude-code/harness-run/SKILL.md +6 -3
- package/harness/bundles/general/claude-code/harness-submit/SKILL.md +5 -7
- package/harness/bundles/general/claude-code/harness-sync/SKILL.md +1 -1
- package/harness/bundles/general/claude-code/harness-test/SKILL.md +6 -3
- package/harness/bundles/general/claude-code/scripts/harness_archive.py +164 -17
- package/harness/bundles/general/claude-code/scripts/harness_events.py +30 -1
- package/harness/bundles/general/claude-code/scripts/harness_events_sync.py +269 -0
- package/harness/bundles/general/claude-code/scripts/harness_gate.py +222 -64
- package/harness/bundles/general/claude-code/scripts/harness_headless.py +37 -0
- package/harness/bundles/general/codebuddy/.harness-build.json +1 -1
- package/harness/bundles/general/codebuddy/README.md +9 -8
- package/harness/bundles/general/codebuddy/harness-archive/SKILL.md +10 -12
- package/harness/bundles/general/codebuddy/harness-codebase-map/SKILL.md +1 -1
- package/harness/bundles/general/codebuddy/harness-knowledge-ingest/SKILL.md +1 -1
- package/harness/bundles/general/codebuddy/harness-knowledge-ingest/scripts/harness_knowledge.py +347 -1
- package/harness/bundles/general/codebuddy/harness-knowledge-query/SKILL.md +1 -1
- package/harness/bundles/general/codebuddy/harness-plan/SKILL.md +6 -3
- package/harness/bundles/general/codebuddy/harness-review/SKILL.md +6 -7
- package/harness/bundles/general/codebuddy/harness-run/SKILL.md +6 -3
- package/harness/bundles/general/codebuddy/harness-submit/SKILL.md +5 -8
- package/harness/bundles/general/codebuddy/harness-sync/SKILL.md +1 -1
- package/harness/bundles/general/codebuddy/harness-test/SKILL.md +6 -3
- package/harness/bundles/general/codebuddy/scripts/harness_archive.py +164 -17
- package/harness/bundles/general/codebuddy/scripts/harness_events.py +30 -1
- package/harness/bundles/general/codebuddy/scripts/harness_events_sync.py +269 -0
- package/harness/bundles/general/codebuddy/scripts/harness_gate.py +222 -64
- package/harness/bundles/general/codebuddy/scripts/harness_headless.py +37 -0
- package/harness/bundles/general/codex/.harness-build.json +1 -1
- package/harness/bundles/general/codex/README.md +9 -8
- package/harness/bundles/general/codex/harness-archive/SKILL.md +10 -12
- package/harness/bundles/general/codex/harness-codebase-map/SKILL.md +1 -1
- package/harness/bundles/general/codex/harness-knowledge-ingest/SKILL.md +1 -1
- package/harness/bundles/general/codex/harness-knowledge-ingest/scripts/harness_knowledge.py +347 -1
- package/harness/bundles/general/codex/harness-knowledge-query/SKILL.md +1 -1
- package/harness/bundles/general/codex/harness-plan/SKILL.md +6 -3
- package/harness/bundles/general/codex/harness-review/SKILL.md +6 -7
- package/harness/bundles/general/codex/harness-run/SKILL.md +6 -3
- package/harness/bundles/general/codex/harness-submit/SKILL.md +5 -8
- package/harness/bundles/general/codex/harness-sync/SKILL.md +1 -1
- package/harness/bundles/general/codex/harness-test/SKILL.md +6 -3
- package/harness/bundles/general/codex/scripts/harness_archive.py +164 -17
- package/harness/bundles/general/codex/scripts/harness_events.py +30 -1
- package/harness/bundles/general/codex/scripts/harness_events_sync.py +269 -0
- package/harness/bundles/general/codex/scripts/harness_gate.py +222 -64
- package/harness/bundles/general/codex/scripts/harness_headless.py +37 -0
- package/harness/bundles/general/cursor/.harness-build.json +1 -1
- package/harness/bundles/general/cursor/README.md +9 -8
- package/harness/bundles/general/cursor/harness-archive/SKILL.md +10 -12
- package/harness/bundles/general/cursor/harness-codebase-map/SKILL.md +1 -1
- package/harness/bundles/general/cursor/harness-knowledge-ingest/SKILL.md +1 -1
- package/harness/bundles/general/cursor/harness-knowledge-ingest/scripts/harness_knowledge.py +347 -1
- package/harness/bundles/general/cursor/harness-knowledge-query/SKILL.md +1 -1
- package/harness/bundles/general/cursor/harness-plan/SKILL.md +6 -3
- package/harness/bundles/general/cursor/harness-review/SKILL.md +6 -7
- package/harness/bundles/general/cursor/harness-run/SKILL.md +6 -3
- package/harness/bundles/general/cursor/harness-submit/SKILL.md +5 -8
- package/harness/bundles/general/cursor/harness-sync/SKILL.md +1 -1
- package/harness/bundles/general/cursor/harness-test/SKILL.md +6 -3
- package/harness/bundles/general/cursor/scripts/harness_archive.py +164 -17
- package/harness/bundles/general/cursor/scripts/harness_events.py +30 -1
- package/harness/bundles/general/cursor/scripts/harness_events_sync.py +269 -0
- package/harness/bundles/general/cursor/scripts/harness_gate.py +222 -64
- package/harness/bundles/general/cursor/scripts/harness_headless.py +37 -0
- package/harness/bundles/java/claude-code/.harness-build.json +1 -1
- package/harness/bundles/java/claude-code/README.md +9 -8
- package/harness/bundles/java/claude-code/harness-apidoc/SKILL.md +3 -2
- package/harness/bundles/java/claude-code/harness-archive/SKILL.md +10 -11
- package/harness/bundles/java/claude-code/harness-codebase-map/SKILL.md +1 -1
- package/harness/bundles/java/claude-code/harness-knowledge-ingest/SKILL.md +1 -1
- package/harness/bundles/java/claude-code/harness-knowledge-ingest/scripts/harness_knowledge.py +347 -1
- package/harness/bundles/java/claude-code/harness-knowledge-query/SKILL.md +1 -1
- package/harness/bundles/java/claude-code/harness-package/SKILL.md +3 -2
- package/harness/bundles/java/claude-code/harness-plan/SKILL.md +6 -3
- package/harness/bundles/java/claude-code/harness-review/SKILL.md +6 -7
- package/harness/bundles/java/claude-code/harness-run/SKILL.md +6 -3
- package/harness/bundles/java/claude-code/harness-submit/SKILL.md +5 -7
- package/harness/bundles/java/claude-code/harness-sync/SKILL.md +1 -1
- package/harness/bundles/java/claude-code/harness-test/SKILL.md +6 -3
- package/harness/bundles/java/claude-code/scripts/harness_archive.py +164 -17
- package/harness/bundles/java/claude-code/scripts/harness_events.py +30 -1
- package/harness/bundles/java/claude-code/scripts/harness_events_sync.py +269 -0
- package/harness/bundles/java/claude-code/scripts/harness_gate.py +222 -64
- package/harness/bundles/java/claude-code/scripts/harness_headless.py +37 -0
- package/harness/bundles/java/codebuddy/.harness-build.json +1 -1
- package/harness/bundles/java/codebuddy/README.md +9 -8
- package/harness/bundles/java/codebuddy/harness-apidoc/SKILL.md +3 -2
- package/harness/bundles/java/codebuddy/harness-archive/SKILL.md +10 -12
- package/harness/bundles/java/codebuddy/harness-codebase-map/SKILL.md +1 -1
- package/harness/bundles/java/codebuddy/harness-knowledge-ingest/SKILL.md +1 -1
- package/harness/bundles/java/codebuddy/harness-knowledge-ingest/scripts/harness_knowledge.py +347 -1
- package/harness/bundles/java/codebuddy/harness-knowledge-query/SKILL.md +1 -1
- package/harness/bundles/java/codebuddy/harness-package/SKILL.md +2 -2
- package/harness/bundles/java/codebuddy/harness-plan/SKILL.md +6 -3
- package/harness/bundles/java/codebuddy/harness-review/SKILL.md +6 -7
- package/harness/bundles/java/codebuddy/harness-run/SKILL.md +6 -3
- package/harness/bundles/java/codebuddy/harness-submit/SKILL.md +5 -8
- package/harness/bundles/java/codebuddy/harness-sync/SKILL.md +1 -1
- package/harness/bundles/java/codebuddy/harness-test/SKILL.md +6 -3
- package/harness/bundles/java/codebuddy/scripts/harness_archive.py +164 -17
- package/harness/bundles/java/codebuddy/scripts/harness_events.py +30 -1
- package/harness/bundles/java/codebuddy/scripts/harness_events_sync.py +269 -0
- package/harness/bundles/java/codebuddy/scripts/harness_gate.py +222 -64
- package/harness/bundles/java/codebuddy/scripts/harness_headless.py +37 -0
- package/harness/bundles/java/codex/.harness-build.json +1 -1
- package/harness/bundles/java/codex/README.md +9 -8
- package/harness/bundles/java/codex/harness-apidoc/SKILL.md +3 -2
- package/harness/bundles/java/codex/harness-archive/SKILL.md +10 -12
- package/harness/bundles/java/codex/harness-codebase-map/SKILL.md +1 -1
- package/harness/bundles/java/codex/harness-knowledge-ingest/SKILL.md +1 -1
- package/harness/bundles/java/codex/harness-knowledge-ingest/scripts/harness_knowledge.py +347 -1
- package/harness/bundles/java/codex/harness-knowledge-query/SKILL.md +1 -1
- package/harness/bundles/java/codex/harness-package/SKILL.md +2 -2
- package/harness/bundles/java/codex/harness-plan/SKILL.md +6 -3
- package/harness/bundles/java/codex/harness-review/SKILL.md +6 -7
- package/harness/bundles/java/codex/harness-run/SKILL.md +6 -3
- package/harness/bundles/java/codex/harness-submit/SKILL.md +5 -8
- package/harness/bundles/java/codex/harness-sync/SKILL.md +1 -1
- package/harness/bundles/java/codex/harness-test/SKILL.md +6 -3
- package/harness/bundles/java/codex/scripts/harness_archive.py +164 -17
- package/harness/bundles/java/codex/scripts/harness_events.py +30 -1
- package/harness/bundles/java/codex/scripts/harness_events_sync.py +269 -0
- package/harness/bundles/java/codex/scripts/harness_gate.py +222 -64
- package/harness/bundles/java/codex/scripts/harness_headless.py +37 -0
- package/harness/bundles/java/cursor/.harness-build.json +1 -1
- package/harness/bundles/java/cursor/README.md +9 -8
- package/harness/bundles/java/cursor/harness-apidoc/SKILL.md +3 -2
- package/harness/bundles/java/cursor/harness-archive/SKILL.md +10 -12
- package/harness/bundles/java/cursor/harness-codebase-map/SKILL.md +1 -1
- package/harness/bundles/java/cursor/harness-knowledge-ingest/SKILL.md +1 -1
- package/harness/bundles/java/cursor/harness-knowledge-ingest/scripts/harness_knowledge.py +347 -1
- package/harness/bundles/java/cursor/harness-knowledge-query/SKILL.md +1 -1
- package/harness/bundles/java/cursor/harness-package/SKILL.md +2 -2
- package/harness/bundles/java/cursor/harness-plan/SKILL.md +6 -3
- package/harness/bundles/java/cursor/harness-review/SKILL.md +6 -7
- package/harness/bundles/java/cursor/harness-run/SKILL.md +6 -3
- package/harness/bundles/java/cursor/harness-submit/SKILL.md +5 -8
- package/harness/bundles/java/cursor/harness-sync/SKILL.md +1 -1
- package/harness/bundles/java/cursor/harness-test/SKILL.md +6 -3
- package/harness/bundles/java/cursor/scripts/harness_archive.py +164 -17
- package/harness/bundles/java/cursor/scripts/harness_events.py +30 -1
- package/harness/bundles/java/cursor/scripts/harness_events_sync.py +269 -0
- package/harness/bundles/java/cursor/scripts/harness_gate.py +222 -64
- package/harness/bundles/java/cursor/scripts/harness_headless.py +37 -0
- package/harness/manifests/general/claude-code.json +26 -18
- package/harness/manifests/general/codebuddy.json +26 -18
- package/harness/manifests/general/codex.json +26 -18
- package/harness/manifests/general/cursor.json +26 -18
- package/harness/manifests/java/claude-code.json +28 -20
- package/harness/manifests/java/codebuddy.json +28 -20
- package/harness/manifests/java/codex.json +28 -20
- package/harness/manifests/java/cursor.json +28 -20
- package/hunter-workflow-family.json +4 -4
- package/package.json +1 -1
|
@@ -124,6 +124,93 @@ def _read_json(path: Path) -> Any:
|
|
|
124
124
|
return json.loads(path.read_text(encoding="utf-8-sig"))
|
|
125
125
|
|
|
126
126
|
|
|
127
|
+
# ---------------------------------------------------------------------------
|
|
128
|
+
# Gate severity tiering (P1 controllability)
|
|
129
|
+
#
|
|
130
|
+
# Hard invariants stay fail-closed in every mode:
|
|
131
|
+
# 1. submit/merge identity & final-hash consistency (ledger/integration);
|
|
132
|
+
# 2. archive integrity (manifest/minimal set, enforced in harness_archive);
|
|
133
|
+
# 3. destructive-action protection (cleanup topology / protected paths).
|
|
134
|
+
# The sites listed in SOFT_GATE_SITES guard regenerable bookkeeping
|
|
135
|
+
# (plan-handoff projections, phase capsules, scenario coverage receipts,
|
|
136
|
+
# test-guard tracking). In "lenient" mode a failure at one of those sites is
|
|
137
|
+
# recorded as a WARN receipt (evidence/gate-warnings.ndjson) instead of
|
|
138
|
+
# blocking the phase. Release phases (submit/merge/archive/release/deploy)
|
|
139
|
+
# never downgrade.
|
|
140
|
+
# ---------------------------------------------------------------------------
|
|
141
|
+
|
|
142
|
+
GATE_RELEASE_PHASES = frozenset({"submit", "merge", "archive", "release", "deploy"})
|
|
143
|
+
SOFT_GATE_SITES = frozenset({
|
|
144
|
+
"plan-handoff",
|
|
145
|
+
"capsule",
|
|
146
|
+
"scenario-coverage",
|
|
147
|
+
"test-guard",
|
|
148
|
+
})
|
|
149
|
+
GATE_WARNINGS_REL = Path("evidence") / "gate-warnings.ndjson"
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
def gate_severity_mode(project: Path, change_dir: Path | None = None) -> str:
|
|
153
|
+
"""Resolve gate severity mode: env > change gate-policy > project config.
|
|
154
|
+
|
|
155
|
+
Default is ``lenient`` (soft regenerable sites → WARN). Release phases and
|
|
156
|
+
the three hard invariants stay fail-closed; set ``HUNTER_HARNESS_GATE_MODE``
|
|
157
|
+
or ``gate-policy.json`` ``severityMode`` to ``strict`` to force fail-closed
|
|
158
|
+
soft sites as well.
|
|
159
|
+
"""
|
|
160
|
+
env_mode = str(os.environ.get("HUNTER_HARNESS_GATE_MODE") or "").strip().lower()
|
|
161
|
+
if env_mode in {"strict", "lenient"}:
|
|
162
|
+
return env_mode
|
|
163
|
+
candidates: list[Path] = []
|
|
164
|
+
if change_dir is not None:
|
|
165
|
+
candidates.append(change_dir / "meta" / "gate-policy.json")
|
|
166
|
+
candidates.append(project / ".harness" / "config" / "gate-policy.json")
|
|
167
|
+
for path in candidates:
|
|
168
|
+
if not path.is_file():
|
|
169
|
+
continue
|
|
170
|
+
try:
|
|
171
|
+
document = _read_json(path)
|
|
172
|
+
except (OSError, json.JSONDecodeError):
|
|
173
|
+
continue
|
|
174
|
+
if not isinstance(document, dict):
|
|
175
|
+
continue
|
|
176
|
+
value = str(document.get("severityMode") or "").strip().lower()
|
|
177
|
+
if value in {"strict", "lenient"}:
|
|
178
|
+
return value
|
|
179
|
+
return "lenient"
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
def gate_soft_allowed(mode: str, phase: str, site: str) -> bool:
|
|
183
|
+
return (
|
|
184
|
+
mode == "lenient"
|
|
185
|
+
and phase not in GATE_RELEASE_PHASES
|
|
186
|
+
and site in SOFT_GATE_SITES
|
|
187
|
+
)
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
def record_gate_warning(
|
|
191
|
+
change_dir: Path,
|
|
192
|
+
*,
|
|
193
|
+
phase: str,
|
|
194
|
+
site: str,
|
|
195
|
+
code: str,
|
|
196
|
+
message: str,
|
|
197
|
+
) -> dict[str, Any]:
|
|
198
|
+
"""Append a downgraded-gate receipt; the phase continues with WARN."""
|
|
199
|
+
entry = {
|
|
200
|
+
"ts": he.now_iso(),
|
|
201
|
+
"phase": phase,
|
|
202
|
+
"site": site,
|
|
203
|
+
"code": code,
|
|
204
|
+
"message": message,
|
|
205
|
+
"mode": "lenient",
|
|
206
|
+
}
|
|
207
|
+
path = change_dir / GATE_WARNINGS_REL
|
|
208
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
209
|
+
with path.open("a", encoding="utf-8", newline="\n") as handle:
|
|
210
|
+
handle.write(json.dumps(entry, ensure_ascii=False, separators=(",", ":")) + "\n")
|
|
211
|
+
return entry
|
|
212
|
+
|
|
213
|
+
|
|
127
214
|
def _write_json(path: Path, data: Any) -> None:
|
|
128
215
|
path.parent.mkdir(parents=True, exist_ok=True)
|
|
129
216
|
text = json.dumps(data, ensure_ascii=False, indent=2) + "\n"
|
|
@@ -1561,7 +1648,8 @@ def append_phase_event(
|
|
|
1561
1648
|
he.atomic_append_line(events_file, line)
|
|
1562
1649
|
rendered = False
|
|
1563
1650
|
log_path = None
|
|
1564
|
-
if type_ == "phase.end" or auto_sealed
|
|
1651
|
+
if (type_ == "phase.end" or auto_sealed) and \
|
|
1652
|
+
he.execution_log_render_enabled(change_dir):
|
|
1565
1653
|
events = he.load_events(events_file)
|
|
1566
1654
|
content = he.render_execution_log(events)
|
|
1567
1655
|
log_path = he.write_execution_log(change_dir, content)
|
|
@@ -1882,6 +1970,8 @@ def cmd_begin(args: argparse.Namespace) -> int:
|
|
|
1882
1970
|
extra={k: v for k, v in resolved.items() if k not in {"ok", "message"}},
|
|
1883
1971
|
)
|
|
1884
1972
|
change_dir = Path(resolved["changeDir"])
|
|
1973
|
+
severity_mode = gate_severity_mode(project, change_dir)
|
|
1974
|
+
gate_warnings: list[dict[str, Any]] = []
|
|
1885
1975
|
plan_verification: dict[str, Any] | None = None
|
|
1886
1976
|
if args.phase == "run":
|
|
1887
1977
|
plan_verification = validate_plan_handoff(change_dir)
|
|
@@ -1894,16 +1984,25 @@ def cmd_begin(args: argparse.Namespace) -> int:
|
|
|
1894
1984
|
or plan_verification.get("message")
|
|
1895
1985
|
or "finalized plan verification failed"
|
|
1896
1986
|
)
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1987
|
+
if gate_soft_allowed(severity_mode, args.phase, "plan-handoff"):
|
|
1988
|
+
gate_warnings.append(record_gate_warning(
|
|
1989
|
+
change_dir,
|
|
1990
|
+
phase=args.phase,
|
|
1991
|
+
site="plan-handoff",
|
|
1992
|
+
code=code,
|
|
1993
|
+
message=message,
|
|
1994
|
+
))
|
|
1995
|
+
else:
|
|
1996
|
+
return emit_error(
|
|
1997
|
+
code,
|
|
1998
|
+
message,
|
|
1999
|
+
as_json=as_json,
|
|
2000
|
+
extra={
|
|
2001
|
+
key: value
|
|
2002
|
+
for key, value in plan_verification.items()
|
|
2003
|
+
if key not in {"ok", "code", "error", "message"}
|
|
2004
|
+
},
|
|
2005
|
+
)
|
|
1907
2006
|
concurrency_block = hc.check_concurrency_block(project, resolved["changeId"])
|
|
1908
2007
|
if concurrency_block is not None:
|
|
1909
2008
|
record_blocked_attempt(
|
|
@@ -1998,7 +2097,18 @@ def cmd_begin(args: argparse.Namespace) -> int:
|
|
|
1998
2097
|
skills_root=Path(args.skills_root).expanduser(),
|
|
1999
2098
|
)
|
|
2000
2099
|
except ValueError as exc:
|
|
2001
|
-
|
|
2100
|
+
if gate_soft_allowed(severity_mode, args.phase, "capsule"):
|
|
2101
|
+
gate_warnings.append(record_gate_warning(
|
|
2102
|
+
change_dir,
|
|
2103
|
+
phase=args.phase,
|
|
2104
|
+
site="capsule",
|
|
2105
|
+
code="PHASE_CAPSULE_MISMATCH",
|
|
2106
|
+
message=str(exc),
|
|
2107
|
+
))
|
|
2108
|
+
# Stale capsule is regenerable state: rebuild it below.
|
|
2109
|
+
capsule = None
|
|
2110
|
+
else:
|
|
2111
|
+
return emit_error("PHASE_CAPSULE_MISMATCH", str(exc), as_json=as_json)
|
|
2002
2112
|
claim = hc.claim_lease(
|
|
2003
2113
|
project,
|
|
2004
2114
|
change_id=resolved["changeId"],
|
|
@@ -2022,18 +2132,27 @@ def cmd_begin(args: argparse.Namespace) -> int:
|
|
|
2022
2132
|
if args.phase in {"run", "test"}:
|
|
2023
2133
|
guard_result = htg.begin(execution_root, change_dir)
|
|
2024
2134
|
if not guard_result.get("ok"):
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
|
|
2135
|
+
if gate_soft_allowed(severity_mode, args.phase, "test-guard"):
|
|
2136
|
+
gate_warnings.append(record_gate_warning(
|
|
2137
|
+
change_dir,
|
|
2138
|
+
phase=args.phase,
|
|
2139
|
+
site="test-guard",
|
|
2140
|
+
code=str(guard_result.get("code", "TEST_GUARD_BEGIN_FAILED")),
|
|
2141
|
+
message="test guard begin failed (downgraded to WARN)",
|
|
2142
|
+
))
|
|
2143
|
+
else:
|
|
2144
|
+
hc.release_lease(
|
|
2145
|
+
project,
|
|
2146
|
+
change_id=resolved["changeId"],
|
|
2147
|
+
phase=args.phase,
|
|
2148
|
+
run_id=run_id,
|
|
2149
|
+
)
|
|
2150
|
+
return emit_error(
|
|
2151
|
+
str(guard_result.get("code", "TEST_GUARD_BEGIN_FAILED")),
|
|
2152
|
+
"test guard begin failed",
|
|
2153
|
+
as_json=as_json,
|
|
2154
|
+
extra=guard_result,
|
|
2155
|
+
)
|
|
2037
2156
|
state_root = Path(hp.resolve_state_dir_for_contract(change_dir)).resolve()
|
|
2038
2157
|
current_head = _git_text(execution_root, "rev-parse", "--verify", "HEAD")
|
|
2039
2158
|
ledger, _ = hl.load_ledger(change_dir)
|
|
@@ -2091,6 +2210,8 @@ def cmd_begin(args: argparse.Namespace) -> int:
|
|
|
2091
2210
|
"planVerification": plan_verification,
|
|
2092
2211
|
"projection": projection,
|
|
2093
2212
|
"executionRootSource": execution_hint["source"],
|
|
2213
|
+
"gateSeverityMode": severity_mode,
|
|
2214
|
+
"gateWarnings": gate_warnings,
|
|
2094
2215
|
}
|
|
2095
2216
|
emit(payload, as_json=as_json)
|
|
2096
2217
|
return 0
|
|
@@ -2108,6 +2229,8 @@ def cmd_close(args: argparse.Namespace) -> int:
|
|
|
2108
2229
|
extra={k: v for k, v in resolved.items() if k not in {"ok", "message"}},
|
|
2109
2230
|
)
|
|
2110
2231
|
change_dir = Path(resolved["changeDir"])
|
|
2232
|
+
severity_mode = gate_severity_mode(project, change_dir)
|
|
2233
|
+
gate_warnings: list[dict[str, Any]] = []
|
|
2111
2234
|
blocked = foundation_gate_blocks(getattr(args, "task", None), change_dir)
|
|
2112
2235
|
if blocked:
|
|
2113
2236
|
return emit_error(
|
|
@@ -2204,18 +2327,27 @@ def cmd_close(args: argparse.Namespace) -> int:
|
|
|
2204
2327
|
allow_head_advance=args.phase in {"run", "submit", "merge"},
|
|
2205
2328
|
)
|
|
2206
2329
|
except ValueError as exc:
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
|
|
2211
|
-
|
|
2212
|
-
|
|
2213
|
-
|
|
2214
|
-
|
|
2215
|
-
|
|
2216
|
-
|
|
2217
|
-
|
|
2218
|
-
|
|
2330
|
+
if gate_soft_allowed(severity_mode, args.phase, "capsule"):
|
|
2331
|
+
gate_warnings.append(record_gate_warning(
|
|
2332
|
+
change_dir,
|
|
2333
|
+
phase=args.phase,
|
|
2334
|
+
site="capsule",
|
|
2335
|
+
code="PHASE_CAPSULE_MISMATCH",
|
|
2336
|
+
message=str(exc),
|
|
2337
|
+
))
|
|
2338
|
+
else:
|
|
2339
|
+
persist_close_failure(
|
|
2340
|
+
change_dir,
|
|
2341
|
+
args.phase,
|
|
2342
|
+
run_id,
|
|
2343
|
+
capsule,
|
|
2344
|
+
status="CAPSULE_VALIDATION_FAILED",
|
|
2345
|
+
error={
|
|
2346
|
+
"code": "PHASE_CAPSULE_MISMATCH",
|
|
2347
|
+
"message": str(exc),
|
|
2348
|
+
},
|
|
2349
|
+
)
|
|
2350
|
+
return emit_error("PHASE_CAPSULE_MISMATCH", str(exc), as_json=as_json)
|
|
2219
2351
|
|
|
2220
2352
|
projection = evaluate_projection_gate(project, args.phase)
|
|
2221
2353
|
if not projection.get("ok"):
|
|
@@ -2294,20 +2426,29 @@ def cmd_close(args: argparse.Namespace) -> int:
|
|
|
2294
2426
|
if args.phase in {"run", "test"}:
|
|
2295
2427
|
coverage = _validate_scenario_coverage(change_dir)
|
|
2296
2428
|
if not coverage.get("ok"):
|
|
2297
|
-
|
|
2298
|
-
|
|
2299
|
-
|
|
2300
|
-
|
|
2301
|
-
|
|
2302
|
-
|
|
2303
|
-
|
|
2304
|
-
|
|
2305
|
-
|
|
2306
|
-
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
|
|
2429
|
+
if gate_soft_allowed(severity_mode, args.phase, "scenario-coverage"):
|
|
2430
|
+
gate_warnings.append(record_gate_warning(
|
|
2431
|
+
change_dir,
|
|
2432
|
+
phase=args.phase,
|
|
2433
|
+
site="scenario-coverage",
|
|
2434
|
+
code=str(coverage.get("code", "SCENARIO_COVERAGE_FAILED")),
|
|
2435
|
+
message=str(coverage.get("message", "scenario coverage validation failed")),
|
|
2436
|
+
))
|
|
2437
|
+
else:
|
|
2438
|
+
persist_close_failure(
|
|
2439
|
+
change_dir,
|
|
2440
|
+
args.phase,
|
|
2441
|
+
run_id,
|
|
2442
|
+
capsule,
|
|
2443
|
+
status="SCENARIO_VALIDATION_FAILED",
|
|
2444
|
+
error=coverage,
|
|
2445
|
+
)
|
|
2446
|
+
return emit_error(
|
|
2447
|
+
str(coverage.get("code", "SCENARIO_COVERAGE_FAILED")),
|
|
2448
|
+
str(coverage.get("message", "scenario coverage validation failed")),
|
|
2449
|
+
as_json=as_json,
|
|
2450
|
+
extra={k: v for k, v in coverage.items() if k not in {"ok", "message", "code"}},
|
|
2451
|
+
)
|
|
2311
2452
|
|
|
2312
2453
|
close_status = args.status
|
|
2313
2454
|
close_code = "PHASE_CLOSED"
|
|
@@ -2316,6 +2457,12 @@ def cmd_close(args: argparse.Namespace) -> int:
|
|
|
2316
2457
|
# Degraded close: phase.end status must not exceed WARN (OK → WARN).
|
|
2317
2458
|
if close_status == "OK":
|
|
2318
2459
|
close_status = "WARN"
|
|
2460
|
+
if gate_warnings:
|
|
2461
|
+
# Lenient-mode downgrades: phase closes, but never better than WARN.
|
|
2462
|
+
if close_status == "OK":
|
|
2463
|
+
close_status = "WARN"
|
|
2464
|
+
if close_code == "PHASE_CLOSED":
|
|
2465
|
+
close_code = "CLOSED_WITH_WARNINGS"
|
|
2319
2466
|
|
|
2320
2467
|
close_transaction: dict[str, Any] = {}
|
|
2321
2468
|
if capsule is not None:
|
|
@@ -2340,20 +2487,29 @@ def cmd_close(args: argparse.Namespace) -> int:
|
|
|
2340
2487
|
else:
|
|
2341
2488
|
guard_result = htg.close(execution_root, change_dir)
|
|
2342
2489
|
if not guard_result.get("ok"):
|
|
2343
|
-
if
|
|
2344
|
-
|
|
2345
|
-
|
|
2346
|
-
|
|
2347
|
-
"
|
|
2348
|
-
|
|
2349
|
-
|
|
2350
|
-
|
|
2351
|
-
|
|
2352
|
-
|
|
2353
|
-
|
|
2354
|
-
|
|
2355
|
-
|
|
2356
|
-
|
|
2490
|
+
if gate_soft_allowed(severity_mode, args.phase, "test-guard"):
|
|
2491
|
+
gate_warnings.append(record_gate_warning(
|
|
2492
|
+
change_dir,
|
|
2493
|
+
phase=args.phase,
|
|
2494
|
+
site="test-guard",
|
|
2495
|
+
code=str(guard_result.get("code", "TEST_GUARD_CLOSE_FAILED")),
|
|
2496
|
+
message="test guard close failed (downgraded to WARN)",
|
|
2497
|
+
))
|
|
2498
|
+
else:
|
|
2499
|
+
if capsule is not None:
|
|
2500
|
+
close_transaction.update({
|
|
2501
|
+
"status": "GUARD_CLOSE_FAILED",
|
|
2502
|
+
"lastError": guard_result,
|
|
2503
|
+
"updatedAt": he.now_iso(),
|
|
2504
|
+
})
|
|
2505
|
+
write_phase_capsule(change_dir, args.phase, run_id, capsule)
|
|
2506
|
+
return emit_error(
|
|
2507
|
+
str(guard_result.get("code", "TEST_GUARD_CLOSE_FAILED")),
|
|
2508
|
+
"test guard close failed",
|
|
2509
|
+
as_json=as_json,
|
|
2510
|
+
extra=guard_result,
|
|
2511
|
+
)
|
|
2512
|
+
if guard_result.get("ok") and capsule is not None:
|
|
2357
2513
|
close_transaction["guardClosed"] = True
|
|
2358
2514
|
close_transaction["updatedAt"] = he.now_iso()
|
|
2359
2515
|
write_phase_capsule(change_dir, args.phase, run_id, capsule)
|
|
@@ -2437,6 +2593,8 @@ def cmd_close(args: argparse.Namespace) -> int:
|
|
|
2437
2593
|
"testGuard": guard_result,
|
|
2438
2594
|
"event": event_result,
|
|
2439
2595
|
"lease": release,
|
|
2596
|
+
"gateSeverityMode": severity_mode,
|
|
2597
|
+
"gateWarnings": gate_warnings,
|
|
2440
2598
|
}
|
|
2441
2599
|
emit(payload, as_json=as_json)
|
|
2442
2600
|
return 0
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Shared headless JSON envelope helpers for stage scripts (P5 contract)."""
|
|
3
|
+
|
|
4
|
+
from __future__ import annotations
|
|
5
|
+
|
|
6
|
+
import json
|
|
7
|
+
import sys
|
|
8
|
+
from typing import Any
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def headless_envelope(
|
|
12
|
+
*,
|
|
13
|
+
command: str,
|
|
14
|
+
ok: bool,
|
|
15
|
+
exit_code: int,
|
|
16
|
+
change: str | None = None,
|
|
17
|
+
phase: str | None = None,
|
|
18
|
+
warnings: list[Any] | None = None,
|
|
19
|
+
errors: list[Any] | None = None,
|
|
20
|
+
result: dict[str, Any] | None = None,
|
|
21
|
+
) -> dict[str, Any]:
|
|
22
|
+
return {
|
|
23
|
+
"schema_version": 1,
|
|
24
|
+
"ok": ok,
|
|
25
|
+
"command": command,
|
|
26
|
+
"change": change,
|
|
27
|
+
"phase": phase,
|
|
28
|
+
"exit_code": exit_code,
|
|
29
|
+
"warnings": warnings or [],
|
|
30
|
+
"errors": errors or [],
|
|
31
|
+
"result": result or {},
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def emit_headless(payload: dict[str, Any], *, stream=None) -> None:
|
|
36
|
+
out = stream or sys.stdout
|
|
37
|
+
print(json.dumps(payload, ensure_ascii=False, indent=2), file=out)
|
|
@@ -221,17 +221,18 @@ harness-skills/
|
|
|
221
221
|
|-------|----------|------|:--------:|:--------:|----------|
|
|
222
222
|
| harness-sync | `/harness-sync` | 10 项元数据检查(7 核心 + 3 辅助) | 初始化检查 | ✅ | 控制台报告 + 自动更新 |
|
|
223
223
|
| harness-codebase-map | `/harness-codebase-map` | 生成代码库地图(7 类文档 + summary + manifest) | `sync`/独立 | ✅ | `.harness/codebase/map/` |
|
|
224
|
-
| harness-plan | `/harness-plan` | 需求→设计→任务拆分→**测试场景表** + 自动命名变更名 | `sync` |
|
|
225
|
-
| harness-run | `/harness-run` | TDD 循环 变更簇编码 | `plan` |
|
|
226
|
-
| harness-test | `/harness-test` | 单元测试+接口测试+30条避坑 | `run` |
|
|
227
|
-
| harness-review | `/harness-review` | 6维度参考性审查(不阻塞后续流程) | `test` |
|
|
228
|
-
| harness-submit | `/harness-submit` 或 `/harness-merge`(别名) | commit+push(主目录)/ worktree:本地 commit→--no-ff 合并→push 主分支 | `review` |
|
|
229
|
-
| harness-archive | `/harness-archive` | 归档产出到 archive/YYYY-MM-DD-<cn>,释放工作区 | `submit` |
|
|
224
|
+
| harness-plan | `/harness-plan` | 需求→设计→任务拆分→**测试场景表** + 自动命名变更名 | `sync` | 手动 | `.harness/changes/<cn>/plans/` |
|
|
225
|
+
| harness-run | `/harness-run` | TDD 循环 变更簇编码 | `plan` | 手动 | `.harness/changes/<cn>/sqls/` |
|
|
226
|
+
| harness-test | `/harness-test` | 单元测试+接口测试+30条避坑 | `run` | 手动 | `.harness/changes/<cn>/reports/test/` |
|
|
227
|
+
| harness-review | `/harness-review` | 6维度参考性审查(不阻塞后续流程) | `test` | 手动 | `.harness/changes/<cn>/reports/review/` |
|
|
228
|
+
| harness-submit | `/harness-submit` 或 `/harness-merge`(别名) | commit+push(主目录)/ worktree:本地 commit→--no-ff 合并→push 主分支 | `review` | 手动 | ledger `mergeFinalHash` + 控制台报告 |
|
|
229
|
+
| harness-archive | `/harness-archive` | 归档产出到 archive/YYYY-MM-DD-<cn>,释放工作区 | `submit` | 手动 | `.harness/archive/YYYY-MM-DD-<cn>/` |
|
|
230
230
|
| harness-knowledge-query | `/harness-knowledge-query` | 查询 .harness/knowledge 历史上下文,生成需求 context pack | `sync`/独立 | ✅ | `.harness/knowledge/context-packs/` |
|
|
231
231
|
| harness-knowledge-ingest | `/harness-knowledge-ingest` | 从 archive 整理/同步/维护知识索引(promote/demote/audit) | `archive` | ✅ | `.harness/knowledge/index.json` |
|
|
232
232
|
|
|
233
|
-
>
|
|
233
|
+
> 生命周期 skill(plan → run → test → review → submit → archive)为**手动触发**:单阶段结束后停止,不自动接续下一阶段(已设 `disable-model-invocation`)。sync / codebase-map / knowledge 等辅助 skill 仍可按需被调用。`harness-run`、`harness-submit`、`harness-archive` 涉及 git 写操作、文件移动或归档,调用前必须确保前置条件已满足。
|
|
234
234
|
> `<cn>` = change-name,由 harness-plan 阶段7确定。其他 skill 自动扫描未归档变更定位。
|
|
235
|
+
> 门禁默认 `gate_severity_mode=lenient`(可再生 soft site → WARN);发布阶段与 3 类硬不变量仍 fail-closed。可用 `HUNTER_HARNESS_GATE_MODE` 或 `gate-policy.json` 的 `severityMode` 强制 `strict`。
|
|
235
236
|
|
|
236
237
|
## 关键合规约束(强制)
|
|
237
238
|
|
|
@@ -279,7 +280,7 @@ harness-skills/
|
|
|
279
280
|
| **submit hash 记录** | pre-pull local hash + final pushed hash 双标注(主目录);worktree 模式 submit 段只本地 commit,合并段产生 `mergeFinalHash`,archive 以 `mergeFinalHash` 为准(无则回退 final pushed hash) |
|
|
280
281
|
| **archive 阶段 1** | 归档前确认 commit 已 push、hash 与 submit/merge 记录一致、test/review 报告状态;阶段边界由 finalize 单进程维护,调用者不额外 append |
|
|
281
282
|
| **archive 文件移动** | 只用 PowerShell 或 Read+Write+验证,**禁止 Bash mv/cp/rm**;移动失败时不删除原目录 |
|
|
282
|
-
| **archive final-summary
|
|
283
|
+
| **archive final-summary** | 必须运行 `harness_archive.py finalize` 生成权威 `summary-data.json` 并完成 validate;`final-summary.html` 为可选展示投影(默认渲染,失败只记 warning 不回滚;`finalize --no-html` 可跳过)。无测试或无 review 时必须在 JSON 中标记 `NOT_RUN` / `ADVISORY_NOT_RUN`,禁止伪造 100% 通过率。状态演进须真实展示(✅OK / 🟡WARN / 🔁REUSED / 🔁RETESTED / 📝ADVISORY / 🧹NON_BEHAVIORAL_CLEANUP) |
|
|
283
284
|
|
|
284
285
|
### 敏感信息脱敏
|
|
285
286
|
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: harness-apidoc
|
|
3
|
-
description: 从 Java 后端代码生成结构化前端 API 文档(Controller/VO/ErrorCode
|
|
3
|
+
description: 从 Java 后端代码生成结构化前端 API 文档(Controller/VO/ErrorCode)。仅当用户显式调用
|
|
4
|
+
/harness-apidoc 时使用,不得自动触发。
|
|
4
5
|
---
|
|
5
|
-
<!-- generated by harness_deploy.py; core=
|
|
6
|
+
<!-- generated by harness_deploy.py; core=0c4a64243d3f7bfb; overlay=java; agent=codebuddy; do not edit -->
|
|
6
7
|
# harness-apidoc — 前端接口文档(Java overlay)
|
|
7
8
|
|
|
8
9
|
## Purpose
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: harness-archive
|
|
3
|
-
description: 归档所有变更产出(计划/测试报告/审查/SQL/API文档)到
|
|
4
|
-
|
|
3
|
+
description: 归档所有变更产出(计划/测试报告/审查/SQL/API文档)到 .harness/archive/,含归档元数据。仅当用户显式调用
|
|
4
|
+
/harness-archive 时使用;不得被其他阶段 skill 自动接续触发。
|
|
5
5
|
---
|
|
6
|
-
<!-- generated by harness_deploy.py; core=
|
|
6
|
+
<!-- generated by harness_deploy.py; core=0c4a64243d3f7bfb; overlay=java; agent=codebuddy; do not edit -->
|
|
7
7
|
# harness-archive — 变更归档
|
|
8
8
|
|
|
9
9
|
## Purpose
|
|
@@ -12,11 +12,9 @@ description: 归档所有变更产出(计划/测试报告/审查/SQL/API文档
|
|
|
12
12
|
|
|
13
13
|
## When to Use
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
仅当用户显式调用 `/harness-archive` 时执行。submit/merge 完成后**不自动**进入本阶段;用户口头说"归档/收尾"而未调用本 skill 时,先确认。
|
|
16
16
|
|
|
17
|
-
|
|
18
|
-
- submit 已推送、test/review 报告已就绪(或已明确标记跳过/未运行),需要封存变更产出。
|
|
19
|
-
- 变更闭环最后一站,归档后从工作区移除 `.harness/changes/<change-name>/`。
|
|
17
|
+
前置:submit 已推送、test/review 报告已就绪(或已明确标记跳过/未运行)。变更闭环最后一站,归档后从工作区移除 `.harness/changes/<change-name>/`。
|
|
20
18
|
|
|
21
19
|
跳过场景:
|
|
22
20
|
|
|
@@ -25,8 +23,8 @@ description: 归档所有变更产出(计划/测试报告/审查/SQL/API文档
|
|
|
25
23
|
|
|
26
24
|
自动调用边界:
|
|
27
25
|
|
|
28
|
-
-
|
|
29
|
-
-
|
|
26
|
+
- 归档涉及移动/删除原变更目录,**禁止被其他 skill 自动调用**;只接受用户显式 `/harness-archive` 调用。
|
|
27
|
+
- `harness_archive.py auto-gate` 只影响**本次显式调用内**是否需要归档确认对话:返回 `ARCHIVE_AUTO_GATE_SATISFIED`(Submit 或 Merge 已终态,archive-boundary snapshot 存在且全部归档门禁满足)时可跳过 AskQuestion 直接归档;其他情况仍须确认。auto-gate 不构成跨阶段自动触发的授权。
|
|
30
28
|
|
|
31
29
|
## 前置条件
|
|
32
30
|
|
|
@@ -139,7 +137,7 @@ description: 归档所有变更产出(计划/测试报告/审查/SQL/API文档
|
|
|
139
137
|
|
|
140
138
|
### 四、数据化归档门禁(先数据后渲染)
|
|
141
139
|
|
|
142
|
-
必须通过 `harness_archive.py finalize`
|
|
140
|
+
必须通过 `harness_archive.py finalize` 生成权威 `reports/final/summary-data.json` 并完成 validate;`final-summary.html` 为可选展示投影(默认渲染;渲染失败只记 warning、不回滚归档;`finalize --no-html` 可完全跳过本地 HTML)。**禁止模型临场写 500+ 行 HTML**。统计数字只能来自 summary-data、events、ledger 或 manifest。详见 `../protocols/report-pipeline-protocol.md`、`../protocols/archive-report-protocol.md`、`reference.md`。
|
|
143
141
|
|
|
144
142
|
### 五、manifest/checksum 必须存在
|
|
145
143
|
|
|
@@ -160,7 +158,7 @@ description: 归档所有变更产出(计划/测试报告/审查/SQL/API文档
|
|
|
160
158
|
|
|
161
159
|
无测试报告 → 显示"未运行测试 / 静态验证",不得 100% 通过率;无 review → "📝ADVISORY:未运行 review"。状态用 ✅OK / 🟡WARN / 🔁REUSED / 🔁RETESTED / 📝ADVISORY / 🧹NON_BEHAVIORAL_CLEANUP,复用前一阶段结果显示 🔁REUSED,**不得伪装成重新执行,不得无脑全绿**。
|
|
162
160
|
|
|
163
|
-
**final-summary
|
|
161
|
+
**final-summary 是可再生展示投影,非硬产物**:`summary-data.json` 是最终报告的唯一权威数据源,必须产出并通过 validate/adequacy。HTML 渲染默认执行(Node 渲染器失败时自动 Python fallback),但渲染失败只记 warning,**不再回滚归档**;`finalize --no-html` 可完全跳过本地 HTML(配置远端平台时由平台按 summary-data.json 渲染)。
|
|
164
162
|
|
|
165
163
|
### 九、CONDITIONAL_OK 最终状态
|
|
166
164
|
|
|
@@ -191,7 +189,7 @@ git 命令通过 `powershell.exe -Command "..."` 执行;archive-meta.md 和 fi
|
|
|
191
189
|
- `.harness/archive/YYYY-MM-DD-<change-name>/meta/archive-meta.md` — 归档元数据
|
|
192
190
|
- `.harness/archive/YYYY-MM-DD-<change-name>/events.ndjson` — 结构化事件层(新流程推荐;旧 archive 可缺失)
|
|
193
191
|
- `.harness/archive/YYYY-MM-DD-<change-name>/reports/final/summary-data.json` — 最终报告数据源
|
|
194
|
-
- `.harness/archive/YYYY-MM-DD-<change-name>/reports/final/final-summary.html` —
|
|
192
|
+
- `.harness/archive/YYYY-MM-DD-<change-name>/reports/final/final-summary.html` — 可选展示投影,由 `render-summary.mjs` 渲染(Node 失败时 Python fallback);渲染失败降级 warning,`--no-html` 可跳过
|
|
195
193
|
- `.harness/archive/YYYY-MM-DD-<change-name>/evidence/archive-manifest-before.json` / `archive-manifest-after.json` — 归档前后 manifest/checksum
|
|
196
194
|
|
|
197
195
|
## 渐进披露
|
|
@@ -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=0c4a64243d3f7bfb; overlay=java; 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=0c4a64243d3f7bfb; overlay=java; agent=codebuddy; do not edit -->
|
|
7
7
|
# harness-knowledge-ingest
|
|
8
8
|
|
|
9
9
|
从 `.harness/archive/**/reports/final/summary-data.json` 抽取项目知识,生成并维护本地 `.harness/knowledge/` 索引。
|