@hunter-harness/workflow-harness 0.2.35 → 0.2.37
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/contracts/build-profile-v3.schema.json +147 -0
- package/harness/bundles/general/claude-code/contracts/doctor-capability.schema.json +43 -0
- package/harness/bundles/general/claude-code/contracts/external-platform-observation.schema.json +62 -0
- package/harness/bundles/general/claude-code/harness-archive/SKILL.md +1 -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-ingest/scripts/harness_knowledge.py +24 -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-submit/SKILL.md +1 -1
- package/harness/bundles/general/claude-code/harness-sync/SKILL.md +1 -1
- package/harness/bundles/general/claude-code/harness-sync/reference.md +2 -2
- package/harness/bundles/general/claude-code/harness-test/SKILL.md +1 -1
- package/harness/bundles/general/claude-code/harness-test/pitfalls.md +1 -1
- package/harness/bundles/general/claude-code/protocols/ci-layering-protocol.md +22 -0
- package/harness/bundles/general/claude-code/protocols/external-platform-protocol.md +27 -0
- package/harness/bundles/general/claude-code/protocols/memory-closeout-protocol.md +21 -0
- package/harness/bundles/general/claude-code/protocols/registry-governance-protocol.md +31 -0
- package/harness/bundles/general/claude-code/protocols/verification-graph-protocol.md +26 -0
- package/harness/bundles/general/claude-code/scripts/harness_archive.py +13 -9
- package/harness/bundles/general/claude-code/scripts/harness_ledger.py +121 -12
- package/harness/bundles/general/claude-code/scripts/harness_preflight.py +2 -2
- package/harness/bundles/general/claude-code/scripts/harness_profile.py +308 -23
- package/harness/bundles/general/codebuddy/.harness-build.json +1 -1
- package/harness/bundles/general/codebuddy/contracts/build-profile-v3.schema.json +147 -0
- package/harness/bundles/general/codebuddy/contracts/doctor-capability.schema.json +43 -0
- package/harness/bundles/general/codebuddy/contracts/external-platform-observation.schema.json +62 -0
- package/harness/bundles/general/codebuddy/harness-archive/SKILL.md +1 -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-ingest/scripts/harness_knowledge.py +24 -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-submit/SKILL.md +1 -1
- package/harness/bundles/general/codebuddy/harness-sync/SKILL.md +1 -1
- package/harness/bundles/general/codebuddy/harness-sync/reference.md +2 -2
- package/harness/bundles/general/codebuddy/harness-test/SKILL.md +1 -1
- package/harness/bundles/general/codebuddy/harness-test/pitfalls.md +1 -1
- package/harness/bundles/general/codebuddy/protocols/ci-layering-protocol.md +22 -0
- package/harness/bundles/general/codebuddy/protocols/external-platform-protocol.md +27 -0
- package/harness/bundles/general/codebuddy/protocols/memory-closeout-protocol.md +21 -0
- package/harness/bundles/general/codebuddy/protocols/registry-governance-protocol.md +31 -0
- package/harness/bundles/general/codebuddy/protocols/verification-graph-protocol.md +26 -0
- package/harness/bundles/general/codebuddy/scripts/harness_archive.py +13 -9
- package/harness/bundles/general/codebuddy/scripts/harness_ledger.py +121 -12
- package/harness/bundles/general/codebuddy/scripts/harness_preflight.py +2 -2
- package/harness/bundles/general/codebuddy/scripts/harness_profile.py +308 -23
- package/harness/bundles/general/codex/.harness-build.json +1 -1
- package/harness/bundles/general/codex/contracts/build-profile-v3.schema.json +147 -0
- package/harness/bundles/general/codex/contracts/doctor-capability.schema.json +43 -0
- package/harness/bundles/general/codex/contracts/external-platform-observation.schema.json +62 -0
- package/harness/bundles/general/codex/harness-archive/SKILL.md +1 -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-ingest/scripts/harness_knowledge.py +24 -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-submit/SKILL.md +1 -1
- package/harness/bundles/general/codex/harness-sync/SKILL.md +1 -1
- package/harness/bundles/general/codex/harness-sync/reference.md +2 -2
- package/harness/bundles/general/codex/harness-test/SKILL.md +1 -1
- package/harness/bundles/general/codex/harness-test/pitfalls.md +1 -1
- package/harness/bundles/general/codex/protocols/ci-layering-protocol.md +22 -0
- package/harness/bundles/general/codex/protocols/external-platform-protocol.md +27 -0
- package/harness/bundles/general/codex/protocols/memory-closeout-protocol.md +21 -0
- package/harness/bundles/general/codex/protocols/registry-governance-protocol.md +31 -0
- package/harness/bundles/general/codex/protocols/verification-graph-protocol.md +26 -0
- package/harness/bundles/general/codex/scripts/harness_archive.py +13 -9
- package/harness/bundles/general/codex/scripts/harness_ledger.py +121 -12
- package/harness/bundles/general/codex/scripts/harness_preflight.py +2 -2
- package/harness/bundles/general/codex/scripts/harness_profile.py +308 -23
- package/harness/bundles/general/cursor/.harness-build.json +1 -1
- package/harness/bundles/general/cursor/contracts/build-profile-v3.schema.json +147 -0
- package/harness/bundles/general/cursor/contracts/doctor-capability.schema.json +43 -0
- package/harness/bundles/general/cursor/contracts/external-platform-observation.schema.json +62 -0
- package/harness/bundles/general/cursor/harness-archive/SKILL.md +1 -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-ingest/scripts/harness_knowledge.py +24 -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-submit/SKILL.md +1 -1
- package/harness/bundles/general/cursor/harness-sync/SKILL.md +1 -1
- package/harness/bundles/general/cursor/harness-sync/reference.md +2 -2
- package/harness/bundles/general/cursor/harness-test/SKILL.md +1 -1
- package/harness/bundles/general/cursor/harness-test/pitfalls.md +1 -1
- package/harness/bundles/general/cursor/protocols/ci-layering-protocol.md +22 -0
- package/harness/bundles/general/cursor/protocols/external-platform-protocol.md +27 -0
- package/harness/bundles/general/cursor/protocols/memory-closeout-protocol.md +21 -0
- package/harness/bundles/general/cursor/protocols/registry-governance-protocol.md +31 -0
- package/harness/bundles/general/cursor/protocols/verification-graph-protocol.md +26 -0
- package/harness/bundles/general/cursor/scripts/harness_archive.py +13 -9
- package/harness/bundles/general/cursor/scripts/harness_ledger.py +121 -12
- package/harness/bundles/general/cursor/scripts/harness_preflight.py +2 -2
- package/harness/bundles/general/cursor/scripts/harness_profile.py +308 -23
- package/harness/bundles/java/claude-code/.harness-build.json +1 -1
- package/harness/bundles/java/claude-code/contracts/build-profile-v3.schema.json +147 -0
- package/harness/bundles/java/claude-code/contracts/doctor-capability.schema.json +43 -0
- package/harness/bundles/java/claude-code/contracts/external-platform-observation.schema.json +62 -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-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 +24 -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-sync/SKILL.md +1 -1
- package/harness/bundles/java/claude-code/harness-sync/reference.md +2 -2
- package/harness/bundles/java/claude-code/harness-test/SKILL.md +1 -1
- package/harness/bundles/java/claude-code/harness-test/pitfalls.md +1 -1
- package/harness/bundles/java/claude-code/protocols/ci-layering-protocol.md +22 -0
- package/harness/bundles/java/claude-code/protocols/external-platform-protocol.md +27 -0
- package/harness/bundles/java/claude-code/protocols/memory-closeout-protocol.md +21 -0
- package/harness/bundles/java/claude-code/protocols/registry-governance-protocol.md +31 -0
- package/harness/bundles/java/claude-code/protocols/verification-graph-protocol.md +26 -0
- package/harness/bundles/java/claude-code/scripts/harness_archive.py +13 -9
- package/harness/bundles/java/claude-code/scripts/harness_ledger.py +121 -12
- package/harness/bundles/java/claude-code/scripts/harness_preflight.py +2 -2
- package/harness/bundles/java/claude-code/scripts/harness_profile.py +308 -23
- package/harness/bundles/java/codebuddy/.harness-build.json +1 -1
- package/harness/bundles/java/codebuddy/contracts/build-profile-v3.schema.json +147 -0
- package/harness/bundles/java/codebuddy/contracts/doctor-capability.schema.json +43 -0
- package/harness/bundles/java/codebuddy/contracts/external-platform-observation.schema.json +62 -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-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 +24 -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-sync/SKILL.md +1 -1
- package/harness/bundles/java/codebuddy/harness-sync/reference.md +2 -2
- package/harness/bundles/java/codebuddy/harness-test/SKILL.md +1 -1
- package/harness/bundles/java/codebuddy/harness-test/pitfalls.md +1 -1
- package/harness/bundles/java/codebuddy/protocols/ci-layering-protocol.md +22 -0
- package/harness/bundles/java/codebuddy/protocols/external-platform-protocol.md +27 -0
- package/harness/bundles/java/codebuddy/protocols/memory-closeout-protocol.md +21 -0
- package/harness/bundles/java/codebuddy/protocols/registry-governance-protocol.md +31 -0
- package/harness/bundles/java/codebuddy/protocols/verification-graph-protocol.md +26 -0
- package/harness/bundles/java/codebuddy/scripts/harness_archive.py +13 -9
- package/harness/bundles/java/codebuddy/scripts/harness_ledger.py +121 -12
- package/harness/bundles/java/codebuddy/scripts/harness_preflight.py +2 -2
- package/harness/bundles/java/codebuddy/scripts/harness_profile.py +308 -23
- package/harness/bundles/java/codex/.harness-build.json +1 -1
- package/harness/bundles/java/codex/contracts/build-profile-v3.schema.json +147 -0
- package/harness/bundles/java/codex/contracts/doctor-capability.schema.json +43 -0
- package/harness/bundles/java/codex/contracts/external-platform-observation.schema.json +62 -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-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 +24 -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-sync/SKILL.md +1 -1
- package/harness/bundles/java/codex/harness-sync/reference.md +2 -2
- package/harness/bundles/java/codex/harness-test/SKILL.md +1 -1
- package/harness/bundles/java/codex/harness-test/pitfalls.md +1 -1
- package/harness/bundles/java/codex/protocols/ci-layering-protocol.md +22 -0
- package/harness/bundles/java/codex/protocols/external-platform-protocol.md +27 -0
- package/harness/bundles/java/codex/protocols/memory-closeout-protocol.md +21 -0
- package/harness/bundles/java/codex/protocols/registry-governance-protocol.md +31 -0
- package/harness/bundles/java/codex/protocols/verification-graph-protocol.md +26 -0
- package/harness/bundles/java/codex/scripts/harness_archive.py +13 -9
- package/harness/bundles/java/codex/scripts/harness_ledger.py +121 -12
- package/harness/bundles/java/codex/scripts/harness_preflight.py +2 -2
- package/harness/bundles/java/codex/scripts/harness_profile.py +308 -23
- package/harness/bundles/java/cursor/.harness-build.json +1 -1
- package/harness/bundles/java/cursor/contracts/build-profile-v3.schema.json +147 -0
- package/harness/bundles/java/cursor/contracts/doctor-capability.schema.json +43 -0
- package/harness/bundles/java/cursor/contracts/external-platform-observation.schema.json +62 -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-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 +24 -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-sync/SKILL.md +1 -1
- package/harness/bundles/java/cursor/harness-sync/reference.md +2 -2
- package/harness/bundles/java/cursor/harness-test/SKILL.md +1 -1
- package/harness/bundles/java/cursor/harness-test/pitfalls.md +1 -1
- package/harness/bundles/java/cursor/protocols/ci-layering-protocol.md +22 -0
- package/harness/bundles/java/cursor/protocols/external-platform-protocol.md +27 -0
- package/harness/bundles/java/cursor/protocols/memory-closeout-protocol.md +21 -0
- package/harness/bundles/java/cursor/protocols/registry-governance-protocol.md +31 -0
- package/harness/bundles/java/cursor/protocols/verification-graph-protocol.md +26 -0
- package/harness/bundles/java/cursor/scripts/harness_archive.py +13 -9
- package/harness/bundles/java/cursor/scripts/harness_ledger.py +121 -12
- package/harness/bundles/java/cursor/scripts/harness_preflight.py +2 -2
- package/harness/bundles/java/cursor/scripts/harness_profile.py +308 -23
- package/harness/manifests/general/claude-code.json +59 -21
- package/harness/manifests/general/codebuddy.json +59 -21
- package/harness/manifests/general/codex.json +59 -21
- package/harness/manifests/general/cursor.json +59 -21
- package/harness/manifests/java/claude-code.json +61 -23
- package/harness/manifests/java/codebuddy.json +61 -23
- package/harness/manifests/java/codex.json +61 -23
- package/harness/manifests/java/cursor.json +61 -23
- package/hunter-workflow-family.json +18 -6
- package/package.json +1 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
|
-
"""Harness profile v3 — module/command graph resolver, recommender and migrator.
|
|
2
|
+
"""Harness profile v3 — module/command/verification graph resolver, recommender and migrator.
|
|
3
3
|
|
|
4
4
|
Change cluster 1 of harness-deterministic-performance.
|
|
5
5
|
|
|
@@ -7,8 +7,8 @@ Spec §3.1:每个 verification 声明 command/argvTemplate/scope/inputs/covera
|
|
|
7
7
|
source/basis;持久 profile 只保存模板,不含具体 change-name/worktree 路径或已解析
|
|
8
8
|
overlay;运行期 resolve 结果写入 change runtime/session。
|
|
9
9
|
|
|
10
|
-
兼容性:保留顶层 `verificationInputs`(派生自 commands.<key>.inputs
|
|
11
|
-
|
|
10
|
+
兼容性:保留顶层 `verificationInputs`(派生自 commands.<key>.inputs)供旧调用方
|
|
11
|
+
展开输入闭包;`verificationGraph.targets` 是动态验证目标的权威声明。
|
|
12
12
|
|
|
13
13
|
Python 3.10+ stdlib only. UTF-8 without BOM. Windows path friendly.
|
|
14
14
|
"""
|
|
@@ -157,6 +157,15 @@ def project_profile_v3(profile: dict[str, Any]) -> dict[str, Any]:
|
|
|
157
157
|
return projected
|
|
158
158
|
projected["schemaVersion"] = SCHEMA_VERSION
|
|
159
159
|
commands = projected.get("commands") if isinstance(projected.get("commands"), dict) else {}
|
|
160
|
+
legacy_verification_inputs = {
|
|
161
|
+
key: list(patterns)
|
|
162
|
+
for key, patterns in (
|
|
163
|
+
projected.get("verificationInputs")
|
|
164
|
+
if isinstance(projected.get("verificationInputs"), dict)
|
|
165
|
+
else {}
|
|
166
|
+
).items()
|
|
167
|
+
if isinstance(key, str) and isinstance(patterns, list)
|
|
168
|
+
}
|
|
160
169
|
projected.setdefault(
|
|
161
170
|
"moduleGraph",
|
|
162
171
|
{"modules": [], "boundariesProven": False, "needsReview": True},
|
|
@@ -186,6 +195,13 @@ def project_profile_v3(profile: dict[str, Any]) -> dict[str, Any]:
|
|
|
186
195
|
if isinstance(command, dict) and command.get("source") == "user"
|
|
187
196
|
),
|
|
188
197
|
}
|
|
198
|
+
projected["defaultsFingerprint"] = profile_defaults_fingerprint()
|
|
199
|
+
_derive_verification_inputs(projected)
|
|
200
|
+
projected["verificationInputs"] = {
|
|
201
|
+
**legacy_verification_inputs,
|
|
202
|
+
**projected["verificationInputs"],
|
|
203
|
+
}
|
|
204
|
+
_derive_verification_graph(projected)
|
|
189
205
|
return projected
|
|
190
206
|
|
|
191
207
|
|
|
@@ -266,6 +282,7 @@ VERIFICATION_KEYS: tuple[str, ...] = (
|
|
|
266
282
|
)
|
|
267
283
|
|
|
268
284
|
VALID_SOURCES = ("detected", "user")
|
|
285
|
+
TARGET_NAME = re.compile(r"^[A-Za-z][A-Za-z0-9_-]{0,63}$")
|
|
269
286
|
|
|
270
287
|
|
|
271
288
|
def now_iso() -> str:
|
|
@@ -328,6 +345,16 @@ def is_path_excluded(rel_path: str, excluded: tuple[str, ...] | list[str]) -> bo
|
|
|
328
345
|
def detect_project_type(project: Path) -> str:
|
|
329
346
|
if (project / "pom.xml").is_file():
|
|
330
347
|
return "java-maven"
|
|
348
|
+
if any(
|
|
349
|
+
(project / marker).is_file()
|
|
350
|
+
for marker in (
|
|
351
|
+
"build.gradle",
|
|
352
|
+
"build.gradle.kts",
|
|
353
|
+
"settings.gradle",
|
|
354
|
+
"settings.gradle.kts",
|
|
355
|
+
)
|
|
356
|
+
):
|
|
357
|
+
return "java-gradle"
|
|
331
358
|
if (project / "package.json").is_file():
|
|
332
359
|
return "node"
|
|
333
360
|
return "unknown"
|
|
@@ -341,6 +368,8 @@ def discover_nested_components(
|
|
|
341
368
|
"pyproject.toml": "python",
|
|
342
369
|
"package.json": "node",
|
|
343
370
|
"pom.xml": "java-maven",
|
|
371
|
+
"build.gradle": "java-gradle",
|
|
372
|
+
"build.gradle.kts": "java-gradle",
|
|
344
373
|
}
|
|
345
374
|
found: list[tuple[str, str]] = []
|
|
346
375
|
for marker, kind in markers.items():
|
|
@@ -412,6 +441,8 @@ def _java_commands(reactor_modules: list[str], pom_hash: str) -> dict[str, Any]:
|
|
|
412
441
|
"scope": "module",
|
|
413
442
|
"inputs": _cmd_inputs(reactor_modules, pom=True, main=True, test=False),
|
|
414
443
|
"coverage": "compile",
|
|
444
|
+
"dependsOn": [],
|
|
445
|
+
"requiredCapabilities": ["java", "maven"],
|
|
415
446
|
"source": "detected",
|
|
416
447
|
"basis": dict(basis),
|
|
417
448
|
},
|
|
@@ -421,6 +452,8 @@ def _java_commands(reactor_modules: list[str], pom_hash: str) -> dict[str, Any]:
|
|
|
421
452
|
"scope": "incremental",
|
|
422
453
|
"inputs": _cmd_inputs(reactor_modules, pom=True, main=False, test=True),
|
|
423
454
|
"coverage": "unitTest",
|
|
455
|
+
"dependsOn": ["compile"],
|
|
456
|
+
"requiredCapabilities": ["java", "maven"],
|
|
424
457
|
"source": "detected",
|
|
425
458
|
"basis": dict(basis),
|
|
426
459
|
},
|
|
@@ -430,6 +463,8 @@ def _java_commands(reactor_modules: list[str], pom_hash: str) -> dict[str, Any]:
|
|
|
430
463
|
"scope": "full",
|
|
431
464
|
"inputs": full_inputs,
|
|
432
465
|
"coverage": "unitTestFull",
|
|
466
|
+
"dependsOn": ["compile"],
|
|
467
|
+
"requiredCapabilities": ["java", "maven"],
|
|
433
468
|
"source": "detected",
|
|
434
469
|
"basis": dict(basis),
|
|
435
470
|
},
|
|
@@ -439,6 +474,8 @@ def _java_commands(reactor_modules: list[str], pom_hash: str) -> dict[str, Any]:
|
|
|
439
474
|
"scope": "module-am",
|
|
440
475
|
"inputs": _cmd_inputs(reactor_modules, pom=True, main=False, test=False),
|
|
441
476
|
"coverage": "install",
|
|
477
|
+
"dependsOn": ["compile"],
|
|
478
|
+
"requiredCapabilities": ["java", "maven"],
|
|
442
479
|
"source": "detected",
|
|
443
480
|
"basis": dict(basis),
|
|
444
481
|
},
|
|
@@ -448,12 +485,96 @@ def _java_commands(reactor_modules: list[str], pom_hash: str) -> dict[str, Any]:
|
|
|
448
485
|
"scope": "module",
|
|
449
486
|
"inputs": _cmd_inputs(reactor_modules, pom=True, main=True, test=True),
|
|
450
487
|
"coverage": "package",
|
|
488
|
+
"dependsOn": ["compile"],
|
|
489
|
+
"requiredCapabilities": ["java", "maven"],
|
|
451
490
|
"source": "detected",
|
|
452
491
|
"basis": dict(basis),
|
|
453
492
|
},
|
|
454
493
|
}
|
|
455
494
|
|
|
456
495
|
|
|
496
|
+
def _gradle_commands(project: Path) -> dict[str, Any]:
|
|
497
|
+
if (project / "gradlew").is_file():
|
|
498
|
+
executable = "./gradlew"
|
|
499
|
+
required_capabilities = ["java"]
|
|
500
|
+
elif (project / "gradlew.bat").is_file():
|
|
501
|
+
executable = "gradlew.bat"
|
|
502
|
+
required_capabilities = ["java"]
|
|
503
|
+
else:
|
|
504
|
+
executable = "gradle"
|
|
505
|
+
required_capabilities = ["java", "gradle"]
|
|
506
|
+
build_inputs = [
|
|
507
|
+
"build.gradle",
|
|
508
|
+
"build.gradle.kts",
|
|
509
|
+
"settings.gradle",
|
|
510
|
+
"settings.gradle.kts",
|
|
511
|
+
"gradle.properties",
|
|
512
|
+
"gradle/wrapper/**",
|
|
513
|
+
"gradlew",
|
|
514
|
+
"gradlew.bat",
|
|
515
|
+
]
|
|
516
|
+
full_inputs = build_inputs + ["src/main/**", "src/test/**"]
|
|
517
|
+
basis = {
|
|
518
|
+
"buildSystem": "gradle",
|
|
519
|
+
"wrapper": executable != "gradle",
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
def command(
|
|
523
|
+
task: str,
|
|
524
|
+
*,
|
|
525
|
+
scope: str,
|
|
526
|
+
inputs: list[str],
|
|
527
|
+
coverage: str,
|
|
528
|
+
depends_on: list[str],
|
|
529
|
+
extra: list[str] | None = None,
|
|
530
|
+
) -> dict[str, Any]:
|
|
531
|
+
argv = [executable, task, *(extra or []), "--offline"]
|
|
532
|
+
return {
|
|
533
|
+
"command": " ".join(argv),
|
|
534
|
+
"argvTemplate": argv,
|
|
535
|
+
"scope": scope,
|
|
536
|
+
"inputs": inputs,
|
|
537
|
+
"coverage": coverage,
|
|
538
|
+
"dependsOn": depends_on,
|
|
539
|
+
"requiredCapabilities": list(required_capabilities),
|
|
540
|
+
"source": "detected",
|
|
541
|
+
"basis": dict(basis),
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
return {
|
|
545
|
+
"compile": command(
|
|
546
|
+
"classes",
|
|
547
|
+
scope="module",
|
|
548
|
+
inputs=build_inputs + ["src/main/**"],
|
|
549
|
+
coverage="compile",
|
|
550
|
+
depends_on=[],
|
|
551
|
+
),
|
|
552
|
+
"unitTest": command(
|
|
553
|
+
"test",
|
|
554
|
+
scope="incremental",
|
|
555
|
+
inputs=build_inputs + ["src/main/**", "src/test/**"],
|
|
556
|
+
coverage="unitTest",
|
|
557
|
+
depends_on=["compile"],
|
|
558
|
+
extra=["--tests", "{testClasses}"],
|
|
559
|
+
),
|
|
560
|
+
"unitTestFull": command(
|
|
561
|
+
"test",
|
|
562
|
+
scope="full",
|
|
563
|
+
inputs=full_inputs,
|
|
564
|
+
coverage="unitTestFull",
|
|
565
|
+
depends_on=["compile"],
|
|
566
|
+
),
|
|
567
|
+
"package": command(
|
|
568
|
+
"build",
|
|
569
|
+
scope="module",
|
|
570
|
+
inputs=full_inputs,
|
|
571
|
+
coverage="package",
|
|
572
|
+
depends_on=["compile"],
|
|
573
|
+
extra=["-x", "test"],
|
|
574
|
+
),
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
|
|
457
578
|
def _node_commands(project: Path) -> dict[str, Any]:
|
|
458
579
|
"""commands for a node project: unitTestFull = `npm run check` (or `npm test`)
|
|
459
580
|
with an input closure covering TS sources/tests + config (+ harness Python/.mjs
|
|
@@ -473,9 +594,21 @@ def _node_commands(project: Path) -> dict[str, Any]:
|
|
|
473
594
|
if not script_key:
|
|
474
595
|
return {}
|
|
475
596
|
full_cmd = scripts[script_key]
|
|
597
|
+
declared_package_manager = data.get("packageManager")
|
|
598
|
+
package_manager = (
|
|
599
|
+
"pnpm"
|
|
600
|
+
if (
|
|
601
|
+
isinstance(declared_package_manager, str)
|
|
602
|
+
and declared_package_manager.split("@", 1)[0] == "pnpm"
|
|
603
|
+
)
|
|
604
|
+
or (project / "pnpm-lock.yaml").is_file()
|
|
605
|
+
else "npm"
|
|
606
|
+
)
|
|
476
607
|
# Precise globs (avoid node_modules/** which would make the inputs hash unstable).
|
|
477
608
|
inputs = [
|
|
478
609
|
"package.json",
|
|
610
|
+
"package-lock.json",
|
|
611
|
+
"pnpm-lock.yaml",
|
|
479
612
|
"tsconfig.json",
|
|
480
613
|
"tsconfig.*.json",
|
|
481
614
|
"vitest.config.*",
|
|
@@ -511,8 +644,13 @@ def _node_commands(project: Path) -> dict[str, Any]:
|
|
|
511
644
|
"scope": "full",
|
|
512
645
|
"inputs": inputs,
|
|
513
646
|
"coverage": "unitTestFull",
|
|
647
|
+
"dependsOn": [],
|
|
648
|
+
"requiredCapabilities": ["node", package_manager],
|
|
514
649
|
"source": "detected",
|
|
515
|
-
"basis": {
|
|
650
|
+
"basis": {
|
|
651
|
+
"packageScript": script_key,
|
|
652
|
+
"packageManager": package_manager,
|
|
653
|
+
},
|
|
516
654
|
}
|
|
517
655
|
}
|
|
518
656
|
|
|
@@ -523,11 +661,17 @@ def empty_profile_skeleton(excluded: tuple[str, ...] | list[str]) -> dict[str, A
|
|
|
523
661
|
"defaultsFingerprint": profile_defaults_fingerprint(),
|
|
524
662
|
"detectedAt": "",
|
|
525
663
|
"projectType": "unknown",
|
|
526
|
-
"toolPaths": {"node": "", "mvn": ""},
|
|
664
|
+
"toolPaths": {"node": "", "mvn": "", "gradle": "", "npm": "", "pnpm": ""},
|
|
527
665
|
"excludedRoots": list(excluded),
|
|
528
666
|
"commands": {},
|
|
529
|
-
#
|
|
667
|
+
# 兼容字段:由 commands 派生,供旧调用方展开输入闭包。
|
|
530
668
|
"verificationInputs": {},
|
|
669
|
+
"verificationGraph": {
|
|
670
|
+
"schemaVersion": 1,
|
|
671
|
+
"source": "detected",
|
|
672
|
+
"candidateTarget": None,
|
|
673
|
+
"targets": {},
|
|
674
|
+
},
|
|
531
675
|
"serviceStart": {
|
|
532
676
|
"command": "",
|
|
533
677
|
"healthUrl": "",
|
|
@@ -597,8 +741,18 @@ def load_profile(project: Path) -> dict[str, Any] | None:
|
|
|
597
741
|
if exec_data is None:
|
|
598
742
|
return common_data
|
|
599
743
|
|
|
600
|
-
# Merge: start from common, overlay execution's
|
|
744
|
+
# Merge: start from common, overlay execution's current commands.
|
|
601
745
|
merged = dict(common_data)
|
|
746
|
+
common_current = common_data.get("commands") or {}
|
|
747
|
+
exec_current = exec_data.get("commands") or {}
|
|
748
|
+
if common_current or exec_current:
|
|
749
|
+
merged_current = (
|
|
750
|
+
dict(common_current) if isinstance(common_current, dict) else {}
|
|
751
|
+
)
|
|
752
|
+
if isinstance(exec_current, dict):
|
|
753
|
+
merged_current.update(exec_current)
|
|
754
|
+
merged["commands"] = merged_current
|
|
755
|
+
# Legacy v1 aliases remain readable during migration.
|
|
602
756
|
common_cmds = common_data.get("buildCommands") or {}
|
|
603
757
|
exec_cmds = exec_data.get("buildCommands") or {}
|
|
604
758
|
if common_cmds or exec_cmds:
|
|
@@ -606,19 +760,14 @@ def load_profile(project: Path) -> dict[str, Any] | None:
|
|
|
606
760
|
if isinstance(exec_cmds, dict):
|
|
607
761
|
merged_cmds.update(exec_cmds)
|
|
608
762
|
merged["buildCommands"] = merged_cmds
|
|
609
|
-
|
|
610
|
-
exec_commands = exec_data.get("commands") or {}
|
|
611
|
-
if common_commands or exec_commands:
|
|
612
|
-
merged_commands = (
|
|
613
|
-
dict(common_commands) if isinstance(common_commands, dict) else {}
|
|
614
|
-
)
|
|
615
|
-
if isinstance(exec_commands, dict):
|
|
616
|
-
merged_commands.update(exec_commands)
|
|
617
|
-
merged["commands"] = merged_commands
|
|
763
|
+
if isinstance(merged.get("commands"), dict):
|
|
618
764
|
_derive_verification_inputs(merged)
|
|
619
765
|
for field in ("moduleGraph", "commandGraph", "migration"):
|
|
620
766
|
if field in exec_data:
|
|
621
767
|
merged[field] = exec_data[field]
|
|
768
|
+
exec_graph = exec_data.get("verificationGraph")
|
|
769
|
+
if isinstance(exec_graph, dict):
|
|
770
|
+
merged["verificationGraph"] = dict(exec_graph)
|
|
622
771
|
return merged
|
|
623
772
|
|
|
624
773
|
|
|
@@ -644,8 +793,11 @@ def _merge_user_overrides(
|
|
|
644
793
|
test_tracking = existing.get("testTracking")
|
|
645
794
|
if isinstance(test_tracking, dict) and test_tracking.get("source") == "user":
|
|
646
795
|
profile["testTracking"] = dict(test_tracking)
|
|
647
|
-
|
|
648
|
-
|
|
796
|
+
graph = existing.get("verificationGraph")
|
|
797
|
+
if isinstance(graph, dict) and graph.get("source") == "user":
|
|
798
|
+
profile["verificationGraph"] = dict(graph)
|
|
799
|
+
# verificationInputs 由 commands 重新派生。knownPreexistingErrors /
|
|
800
|
+
# shellQuirks 是人工标注,保留。
|
|
649
801
|
|
|
650
802
|
|
|
651
803
|
def _derive_verification_inputs(profile: dict[str, Any]) -> None:
|
|
@@ -658,6 +810,61 @@ def _derive_verification_inputs(profile: dict[str, Any]) -> None:
|
|
|
658
810
|
profile["verificationInputs"] = vi
|
|
659
811
|
|
|
660
812
|
|
|
813
|
+
def _required_coverage(scope: Any) -> str:
|
|
814
|
+
value = str(scope or "").strip()
|
|
815
|
+
if value == "incremental":
|
|
816
|
+
return "incremental"
|
|
817
|
+
if value == "module-am":
|
|
818
|
+
return "module-am"
|
|
819
|
+
if value in {"module", "full"}:
|
|
820
|
+
# A full command may satisfy the module-or-broader candidate gate.
|
|
821
|
+
return "module"
|
|
822
|
+
return "module"
|
|
823
|
+
|
|
824
|
+
|
|
825
|
+
def _derive_verification_graph(profile: dict[str, Any]) -> None:
|
|
826
|
+
existing = profile.get("verificationGraph")
|
|
827
|
+
if isinstance(existing, dict) and existing.get("source") == "user":
|
|
828
|
+
return
|
|
829
|
+
commands = profile.get("commands")
|
|
830
|
+
targets: dict[str, dict[str, Any]] = {}
|
|
831
|
+
if isinstance(commands, dict):
|
|
832
|
+
for key, command in sorted(commands.items()):
|
|
833
|
+
if not isinstance(key, str) or not TARGET_NAME.fullmatch(key):
|
|
834
|
+
continue
|
|
835
|
+
if not isinstance(command, dict):
|
|
836
|
+
continue
|
|
837
|
+
depends_on = command.get("dependsOn")
|
|
838
|
+
if not isinstance(depends_on, list):
|
|
839
|
+
depends_on = []
|
|
840
|
+
required_capabilities = command.get("requiredCapabilities")
|
|
841
|
+
if not isinstance(required_capabilities, list):
|
|
842
|
+
required_capabilities = []
|
|
843
|
+
targets[key] = {
|
|
844
|
+
"commandKey": key,
|
|
845
|
+
"dependsOn": [
|
|
846
|
+
item
|
|
847
|
+
for item in depends_on
|
|
848
|
+
if isinstance(item, str) and TARGET_NAME.fullmatch(item)
|
|
849
|
+
],
|
|
850
|
+
"requiredCoverage": _required_coverage(command.get("scope")),
|
|
851
|
+
"candidate": key == "unitTestFull",
|
|
852
|
+
"requiredCapabilities": [
|
|
853
|
+
item
|
|
854
|
+
for item in required_capabilities
|
|
855
|
+
if isinstance(item, str) and item.strip()
|
|
856
|
+
],
|
|
857
|
+
}
|
|
858
|
+
profile["verificationGraph"] = {
|
|
859
|
+
"schemaVersion": 1,
|
|
860
|
+
"source": "detected",
|
|
861
|
+
"candidateTarget": (
|
|
862
|
+
"unitTestFull" if "unitTestFull" in targets else None
|
|
863
|
+
),
|
|
864
|
+
"targets": targets,
|
|
865
|
+
}
|
|
866
|
+
|
|
867
|
+
|
|
661
868
|
def detect(project: Path) -> dict[str, Any]:
|
|
662
869
|
project = project.resolve()
|
|
663
870
|
existing = load_profile(project)
|
|
@@ -696,7 +903,13 @@ def detect(project: Path) -> dict[str, Any]:
|
|
|
696
903
|
|
|
697
904
|
node_path = which_tool("node")
|
|
698
905
|
mvn_path = which_tool("mvn")
|
|
699
|
-
profile["toolPaths"] = {
|
|
906
|
+
profile["toolPaths"] = {
|
|
907
|
+
"node": node_path,
|
|
908
|
+
"mvn": mvn_path,
|
|
909
|
+
"gradle": which_tool("gradle"),
|
|
910
|
+
"npm": which_tool("npm"),
|
|
911
|
+
"pnpm": which_tool("pnpm"),
|
|
912
|
+
}
|
|
700
913
|
|
|
701
914
|
pom_hash = sha256_file(project / "pom.xml") if (project / "pom.xml").is_file() else ""
|
|
702
915
|
profile["fingerprint"] = {"mvnVersion": "", "nodeVersion": "", "pomHash": pom_hash}
|
|
@@ -709,6 +922,9 @@ def detect(project: Path) -> dict[str, Any]:
|
|
|
709
922
|
"src/test/**" if module == "." else f"{module}/src/test/**"
|
|
710
923
|
for module in reactor_modules
|
|
711
924
|
)
|
|
925
|
+
elif project_type == "java-gradle":
|
|
926
|
+
profile["commands"] = _gradle_commands(project)
|
|
927
|
+
profile["testTracking"]["paths"] = ["src/test/**"]
|
|
712
928
|
elif project_type == "node":
|
|
713
929
|
node_cmds = _node_commands(project)
|
|
714
930
|
if node_cmds:
|
|
@@ -748,6 +964,7 @@ def detect(project: Path) -> dict[str, Any]:
|
|
|
748
964
|
]
|
|
749
965
|
+ list(recommendation["targets"])
|
|
750
966
|
}
|
|
967
|
+
_derive_verification_graph(profile)
|
|
751
968
|
|
|
752
969
|
out_path = project / PROFILE_REL
|
|
753
970
|
write_json(out_path, profile)
|
|
@@ -863,6 +1080,11 @@ def check(project: Path) -> dict[str, Any]:
|
|
|
863
1080
|
stale = True
|
|
864
1081
|
issues.append(f"toolPaths.{tool_name} missing: {p}")
|
|
865
1082
|
|
|
1083
|
+
structural_issues = validate_profile(profile, project)
|
|
1084
|
+
if structural_issues:
|
|
1085
|
+
stale = True
|
|
1086
|
+
issues.extend(structural_issues)
|
|
1087
|
+
|
|
866
1088
|
status = "stale" if stale else "ready"
|
|
867
1089
|
return {
|
|
868
1090
|
"ok": not stale,
|
|
@@ -897,6 +1119,66 @@ def validate_profile(profile: dict[str, Any], project: Path) -> list[str]:
|
|
|
897
1119
|
issues.append(
|
|
898
1120
|
f"command {key} input in excluded root: {inp}"
|
|
899
1121
|
)
|
|
1122
|
+
graph = profile.get("verificationGraph")
|
|
1123
|
+
targets = graph.get("targets") if isinstance(graph, dict) else None
|
|
1124
|
+
if (
|
|
1125
|
+
not isinstance(graph, dict)
|
|
1126
|
+
or graph.get("schemaVersion") != 1
|
|
1127
|
+
or not isinstance(targets, dict)
|
|
1128
|
+
):
|
|
1129
|
+
issues.append("verificationGraph schemaVersion=1 and targets object required")
|
|
1130
|
+
return issues
|
|
1131
|
+
dependencies: dict[str, list[str]] = {}
|
|
1132
|
+
for name, target in targets.items():
|
|
1133
|
+
if not isinstance(name, str) or not TARGET_NAME.fullmatch(name):
|
|
1134
|
+
issues.append(f"verification target name invalid: {name}")
|
|
1135
|
+
continue
|
|
1136
|
+
if not isinstance(target, dict):
|
|
1137
|
+
issues.append(f"verification target {name} must be an object")
|
|
1138
|
+
continue
|
|
1139
|
+
command_key = target.get("commandKey")
|
|
1140
|
+
if command_key not in commands:
|
|
1141
|
+
issues.append(
|
|
1142
|
+
f"verification target {name} references missing command {command_key}"
|
|
1143
|
+
)
|
|
1144
|
+
required = target.get("requiredCoverage")
|
|
1145
|
+
if required not in {"incremental", "module", "module-am", "full"}:
|
|
1146
|
+
issues.append(
|
|
1147
|
+
f"verification target {name} requiredCoverage invalid: {required}"
|
|
1148
|
+
)
|
|
1149
|
+
raw_dependencies = target.get("dependsOn")
|
|
1150
|
+
if not isinstance(raw_dependencies, list):
|
|
1151
|
+
issues.append(f"verification target {name} dependsOn must be a list")
|
|
1152
|
+
continue
|
|
1153
|
+
dependencies[name] = []
|
|
1154
|
+
for dependency in raw_dependencies:
|
|
1155
|
+
if dependency not in targets:
|
|
1156
|
+
issues.append(
|
|
1157
|
+
f"verification target {name} depends on unknown target {dependency}"
|
|
1158
|
+
)
|
|
1159
|
+
elif isinstance(dependency, str):
|
|
1160
|
+
dependencies[name].append(dependency)
|
|
1161
|
+
candidate = graph.get("candidateTarget")
|
|
1162
|
+
if candidate is not None and candidate not in targets:
|
|
1163
|
+
issues.append(f"verificationGraph candidateTarget missing: {candidate}")
|
|
1164
|
+
|
|
1165
|
+
visiting: set[str] = set()
|
|
1166
|
+
visited: set[str] = set()
|
|
1167
|
+
|
|
1168
|
+
def visit(name: str) -> None:
|
|
1169
|
+
if name in visiting:
|
|
1170
|
+
issues.append(f"verification target dependency cycle at {name}")
|
|
1171
|
+
return
|
|
1172
|
+
if name in visited:
|
|
1173
|
+
return
|
|
1174
|
+
visiting.add(name)
|
|
1175
|
+
for dependency in dependencies.get(name, []):
|
|
1176
|
+
visit(dependency)
|
|
1177
|
+
visiting.remove(name)
|
|
1178
|
+
visited.add(name)
|
|
1179
|
+
|
|
1180
|
+
for target_name in dependencies:
|
|
1181
|
+
visit(target_name)
|
|
900
1182
|
return issues
|
|
901
1183
|
|
|
902
1184
|
|
|
@@ -947,11 +1229,13 @@ def resolve_command(
|
|
|
947
1229
|
|
|
948
1230
|
|
|
949
1231
|
# ---------------------------------------------------------------------------
|
|
950
|
-
# migrate(v1 →
|
|
1232
|
+
# migrate(v1/v2 → v3,dry-run/apply/备份)
|
|
951
1233
|
# ---------------------------------------------------------------------------
|
|
952
1234
|
|
|
953
|
-
def _backup_path(profile_path: Path,
|
|
954
|
-
return profile_path.with_suffix(
|
|
1235
|
+
def _backup_path(profile_path: Path, schema_version: int) -> Path:
|
|
1236
|
+
return profile_path.with_suffix(
|
|
1237
|
+
profile_path.suffix + f".v{schema_version}.bak"
|
|
1238
|
+
)
|
|
955
1239
|
|
|
956
1240
|
|
|
957
1241
|
def migrate(project: Path, *, dry_run: bool = True) -> dict[str, Any]:
|
|
@@ -1019,6 +1303,7 @@ def migrate(project: Path, *, dry_run: bool = True) -> dict[str, Any]:
|
|
|
1019
1303
|
"identifier 约束(pattern/maxLength/prefix)",
|
|
1020
1304
|
]
|
|
1021
1305
|
)
|
|
1306
|
+
changes.append("commands → verificationGraph.targets 动态验证图")
|
|
1022
1307
|
dropped: list[str] = []
|
|
1023
1308
|
v1_svc = existing.get("serviceStart") or {}
|
|
1024
1309
|
if isinstance(v1_svc, dict):
|
|
@@ -1082,7 +1367,7 @@ def migrate(project: Path, *, dry_run: bool = True) -> dict[str, Any]:
|
|
|
1082
1367
|
def build_parser() -> argparse.ArgumentParser:
|
|
1083
1368
|
parser = argparse.ArgumentParser(
|
|
1084
1369
|
prog="harness_profile.py",
|
|
1085
|
-
description="Harness
|
|
1370
|
+
description="Harness Build Profile v3: recommend/detect/audit/validate/resolve/migrate",
|
|
1086
1371
|
)
|
|
1087
1372
|
parser.add_argument("--json", action="store_true", help="emit JSON")
|
|
1088
1373
|
sub = parser.add_subparsers(dest="command", required=True)
|