@hunter-harness/workflow-harness 0.2.33 → 0.2.34
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 +25 -25
- package/harness/bundles/general/claude-code/.harness-build.json +1 -1
- package/harness/bundles/general/claude-code/contracts/workflow-policy.json +1 -0
- package/harness/bundles/general/claude-code/harness-archive/SKILL.md +1 -1
- package/harness/bundles/general/claude-code/harness-archive/templates/render-summary.mjs +215 -106
- package/harness/bundles/general/claude-code/harness-archive/templates/summary-data-template.json +8 -0
- 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 +389 -117
- package/harness/bundles/general/claude-code/harness-knowledge-query/SKILL.md +1 -1
- package/harness/bundles/general/claude-code/harness-plan/SKILL.md +3 -3
- package/harness/bundles/general/claude-code/harness-review/SKILL.md +2 -2
- package/harness/bundles/general/claude-code/harness-run/SKILL.md +4 -4
- package/harness/bundles/general/claude-code/harness-submit/SKILL.md +2 -2
- package/harness/bundles/general/claude-code/harness-sync/SKILL.md +1 -1
- package/harness/bundles/general/claude-code/harness-test/SKILL.md +4 -4
- package/harness/bundles/general/claude-code/harness-test/checklist.md +1 -0
- package/harness/bundles/general/claude-code/harness-test/reference.md +2 -1
- package/harness/bundles/general/claude-code/protocols/ledger-protocol.md +2 -2
- package/harness/bundles/general/claude-code/scripts/harness_archive.py +404 -12
- package/harness/bundles/general/claude-code/scripts/harness_context.py +717 -0
- package/harness/bundles/general/claude-code/scripts/harness_gate.py +1 -0
- package/harness/bundles/general/claude-code/scripts/harness_ledger.py +77 -9
- package/harness/bundles/general/claude-code/scripts/harness_phase.py +1 -0
- package/harness/bundles/general/claude-code/scripts/harness_plan_aggregate.py +249 -0
- package/harness/bundles/general/claude-code/scripts/harness_profile.py +272 -15
- package/harness/bundles/general/claude-code/scripts/harness_report_model.py +420 -0
- package/harness/bundles/general/claude-code/scripts/harness_review.py +16 -1
- package/harness/bundles/general/claude-code/scripts/harness_verification.py +114 -0
- package/harness/bundles/general/codebuddy/.harness-build.json +1 -1
- package/harness/bundles/general/codebuddy/contracts/workflow-policy.json +1 -0
- package/harness/bundles/general/codebuddy/harness-archive/SKILL.md +1 -1
- package/harness/bundles/general/codebuddy/harness-archive/templates/render-summary.mjs +215 -106
- package/harness/bundles/general/codebuddy/harness-archive/templates/summary-data-template.json +8 -0
- 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 +389 -117
- package/harness/bundles/general/codebuddy/harness-knowledge-query/SKILL.md +1 -1
- package/harness/bundles/general/codebuddy/harness-plan/SKILL.md +3 -3
- package/harness/bundles/general/codebuddy/harness-review/SKILL.md +2 -2
- package/harness/bundles/general/codebuddy/harness-run/SKILL.md +4 -4
- package/harness/bundles/general/codebuddy/harness-submit/SKILL.md +2 -2
- package/harness/bundles/general/codebuddy/harness-sync/SKILL.md +1 -1
- package/harness/bundles/general/codebuddy/harness-test/SKILL.md +4 -4
- package/harness/bundles/general/codebuddy/harness-test/checklist.md +1 -0
- package/harness/bundles/general/codebuddy/harness-test/reference.md +2 -1
- package/harness/bundles/general/codebuddy/protocols/ledger-protocol.md +2 -2
- package/harness/bundles/general/codebuddy/scripts/harness_archive.py +404 -12
- package/harness/bundles/general/codebuddy/scripts/harness_context.py +717 -0
- package/harness/bundles/general/codebuddy/scripts/harness_gate.py +1 -0
- package/harness/bundles/general/codebuddy/scripts/harness_ledger.py +77 -9
- package/harness/bundles/general/codebuddy/scripts/harness_phase.py +1 -0
- package/harness/bundles/general/codebuddy/scripts/harness_plan_aggregate.py +249 -0
- package/harness/bundles/general/codebuddy/scripts/harness_profile.py +272 -15
- package/harness/bundles/general/codebuddy/scripts/harness_report_model.py +420 -0
- package/harness/bundles/general/codebuddy/scripts/harness_review.py +16 -1
- package/harness/bundles/general/codebuddy/scripts/harness_verification.py +114 -0
- package/harness/bundles/general/codex/.harness-build.json +1 -1
- package/harness/bundles/general/codex/contracts/workflow-policy.json +1 -0
- package/harness/bundles/general/codex/harness-archive/SKILL.md +1 -1
- package/harness/bundles/general/codex/harness-archive/templates/render-summary.mjs +215 -106
- package/harness/bundles/general/codex/harness-archive/templates/summary-data-template.json +8 -0
- 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 +389 -117
- package/harness/bundles/general/codex/harness-knowledge-query/SKILL.md +1 -1
- package/harness/bundles/general/codex/harness-plan/SKILL.md +3 -3
- package/harness/bundles/general/codex/harness-review/SKILL.md +2 -2
- package/harness/bundles/general/codex/harness-run/SKILL.md +4 -4
- package/harness/bundles/general/codex/harness-submit/SKILL.md +2 -2
- package/harness/bundles/general/codex/harness-sync/SKILL.md +1 -1
- package/harness/bundles/general/codex/harness-test/SKILL.md +4 -4
- package/harness/bundles/general/codex/harness-test/checklist.md +1 -0
- package/harness/bundles/general/codex/harness-test/reference.md +2 -1
- package/harness/bundles/general/codex/protocols/ledger-protocol.md +2 -2
- package/harness/bundles/general/codex/scripts/harness_archive.py +404 -12
- package/harness/bundles/general/codex/scripts/harness_context.py +717 -0
- package/harness/bundles/general/codex/scripts/harness_gate.py +1 -0
- package/harness/bundles/general/codex/scripts/harness_ledger.py +77 -9
- package/harness/bundles/general/codex/scripts/harness_phase.py +1 -0
- package/harness/bundles/general/codex/scripts/harness_plan_aggregate.py +249 -0
- package/harness/bundles/general/codex/scripts/harness_profile.py +272 -15
- package/harness/bundles/general/codex/scripts/harness_report_model.py +420 -0
- package/harness/bundles/general/codex/scripts/harness_review.py +16 -1
- package/harness/bundles/general/codex/scripts/harness_verification.py +114 -0
- package/harness/bundles/general/cursor/.harness-build.json +1 -1
- package/harness/bundles/general/cursor/contracts/workflow-policy.json +1 -0
- package/harness/bundles/general/cursor/harness-archive/SKILL.md +1 -1
- package/harness/bundles/general/cursor/harness-archive/templates/render-summary.mjs +215 -106
- package/harness/bundles/general/cursor/harness-archive/templates/summary-data-template.json +8 -0
- 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 +389 -117
- package/harness/bundles/general/cursor/harness-knowledge-query/SKILL.md +1 -1
- package/harness/bundles/general/cursor/harness-plan/SKILL.md +3 -3
- package/harness/bundles/general/cursor/harness-review/SKILL.md +2 -2
- package/harness/bundles/general/cursor/harness-run/SKILL.md +4 -4
- package/harness/bundles/general/cursor/harness-submit/SKILL.md +2 -2
- package/harness/bundles/general/cursor/harness-sync/SKILL.md +1 -1
- package/harness/bundles/general/cursor/harness-test/SKILL.md +4 -4
- package/harness/bundles/general/cursor/harness-test/checklist.md +1 -0
- package/harness/bundles/general/cursor/harness-test/reference.md +2 -1
- package/harness/bundles/general/cursor/protocols/ledger-protocol.md +2 -2
- package/harness/bundles/general/cursor/scripts/harness_archive.py +404 -12
- package/harness/bundles/general/cursor/scripts/harness_context.py +717 -0
- package/harness/bundles/general/cursor/scripts/harness_gate.py +1 -0
- package/harness/bundles/general/cursor/scripts/harness_ledger.py +77 -9
- package/harness/bundles/general/cursor/scripts/harness_phase.py +1 -0
- package/harness/bundles/general/cursor/scripts/harness_plan_aggregate.py +249 -0
- package/harness/bundles/general/cursor/scripts/harness_profile.py +272 -15
- package/harness/bundles/general/cursor/scripts/harness_report_model.py +420 -0
- package/harness/bundles/general/cursor/scripts/harness_review.py +16 -1
- package/harness/bundles/general/cursor/scripts/harness_verification.py +114 -0
- package/harness/bundles/java/claude-code/.harness-build.json +1 -1
- package/harness/bundles/java/claude-code/contracts/workflow-policy.json +1 -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/templates/render-summary.mjs +215 -106
- package/harness/bundles/java/claude-code/harness-archive/templates/summary-data-template.json +8 -0
- 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 +389 -117
- 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 +3 -3
- package/harness/bundles/java/claude-code/harness-review/SKILL.md +2 -2
- package/harness/bundles/java/claude-code/harness-run/SKILL.md +4 -4
- package/harness/bundles/java/claude-code/harness-submit/SKILL.md +2 -2
- package/harness/bundles/java/claude-code/harness-sync/SKILL.md +1 -1
- package/harness/bundles/java/claude-code/harness-test/SKILL.md +4 -4
- package/harness/bundles/java/claude-code/harness-test/checklist.md +1 -0
- package/harness/bundles/java/claude-code/harness-test/reference.md +2 -0
- package/harness/bundles/java/claude-code/protocols/ledger-protocol.md +2 -2
- package/harness/bundles/java/claude-code/scripts/harness_archive.py +404 -12
- package/harness/bundles/java/claude-code/scripts/harness_context.py +717 -0
- package/harness/bundles/java/claude-code/scripts/harness_gate.py +1 -0
- package/harness/bundles/java/claude-code/scripts/harness_ledger.py +77 -9
- package/harness/bundles/java/claude-code/scripts/harness_phase.py +1 -0
- package/harness/bundles/java/claude-code/scripts/harness_plan_aggregate.py +249 -0
- package/harness/bundles/java/claude-code/scripts/harness_profile.py +272 -15
- package/harness/bundles/java/claude-code/scripts/harness_report_model.py +420 -0
- package/harness/bundles/java/claude-code/scripts/harness_review.py +16 -1
- package/harness/bundles/java/claude-code/scripts/harness_verification.py +114 -0
- package/harness/bundles/java/codebuddy/.harness-build.json +1 -1
- package/harness/bundles/java/codebuddy/contracts/workflow-policy.json +1 -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/templates/render-summary.mjs +215 -106
- package/harness/bundles/java/codebuddy/harness-archive/templates/summary-data-template.json +8 -0
- 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 +389 -117
- 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 +3 -3
- package/harness/bundles/java/codebuddy/harness-review/SKILL.md +2 -2
- package/harness/bundles/java/codebuddy/harness-run/SKILL.md +4 -4
- package/harness/bundles/java/codebuddy/harness-submit/SKILL.md +2 -2
- package/harness/bundles/java/codebuddy/harness-sync/SKILL.md +1 -1
- package/harness/bundles/java/codebuddy/harness-test/SKILL.md +4 -4
- package/harness/bundles/java/codebuddy/harness-test/checklist.md +1 -0
- package/harness/bundles/java/codebuddy/harness-test/reference.md +2 -0
- package/harness/bundles/java/codebuddy/protocols/ledger-protocol.md +2 -2
- package/harness/bundles/java/codebuddy/scripts/harness_archive.py +404 -12
- package/harness/bundles/java/codebuddy/scripts/harness_context.py +717 -0
- package/harness/bundles/java/codebuddy/scripts/harness_gate.py +1 -0
- package/harness/bundles/java/codebuddy/scripts/harness_ledger.py +77 -9
- package/harness/bundles/java/codebuddy/scripts/harness_phase.py +1 -0
- package/harness/bundles/java/codebuddy/scripts/harness_plan_aggregate.py +249 -0
- package/harness/bundles/java/codebuddy/scripts/harness_profile.py +272 -15
- package/harness/bundles/java/codebuddy/scripts/harness_report_model.py +420 -0
- package/harness/bundles/java/codebuddy/scripts/harness_review.py +16 -1
- package/harness/bundles/java/codebuddy/scripts/harness_verification.py +114 -0
- package/harness/bundles/java/codex/.harness-build.json +1 -1
- package/harness/bundles/java/codex/contracts/workflow-policy.json +1 -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/templates/render-summary.mjs +215 -106
- package/harness/bundles/java/codex/harness-archive/templates/summary-data-template.json +8 -0
- 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 +389 -117
- 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 +3 -3
- package/harness/bundles/java/codex/harness-review/SKILL.md +2 -2
- package/harness/bundles/java/codex/harness-run/SKILL.md +4 -4
- package/harness/bundles/java/codex/harness-submit/SKILL.md +2 -2
- package/harness/bundles/java/codex/harness-sync/SKILL.md +1 -1
- package/harness/bundles/java/codex/harness-test/SKILL.md +4 -4
- package/harness/bundles/java/codex/harness-test/checklist.md +1 -0
- package/harness/bundles/java/codex/harness-test/reference.md +2 -0
- package/harness/bundles/java/codex/protocols/ledger-protocol.md +2 -2
- package/harness/bundles/java/codex/scripts/harness_archive.py +404 -12
- package/harness/bundles/java/codex/scripts/harness_context.py +717 -0
- package/harness/bundles/java/codex/scripts/harness_gate.py +1 -0
- package/harness/bundles/java/codex/scripts/harness_ledger.py +77 -9
- package/harness/bundles/java/codex/scripts/harness_phase.py +1 -0
- package/harness/bundles/java/codex/scripts/harness_plan_aggregate.py +249 -0
- package/harness/bundles/java/codex/scripts/harness_profile.py +272 -15
- package/harness/bundles/java/codex/scripts/harness_report_model.py +420 -0
- package/harness/bundles/java/codex/scripts/harness_review.py +16 -1
- package/harness/bundles/java/codex/scripts/harness_verification.py +114 -0
- package/harness/bundles/java/cursor/.harness-build.json +1 -1
- package/harness/bundles/java/cursor/contracts/workflow-policy.json +1 -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/templates/render-summary.mjs +215 -106
- package/harness/bundles/java/cursor/harness-archive/templates/summary-data-template.json +8 -0
- 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 +389 -117
- 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 +3 -3
- package/harness/bundles/java/cursor/harness-review/SKILL.md +2 -2
- package/harness/bundles/java/cursor/harness-run/SKILL.md +4 -4
- package/harness/bundles/java/cursor/harness-submit/SKILL.md +2 -2
- package/harness/bundles/java/cursor/harness-sync/SKILL.md +1 -1
- package/harness/bundles/java/cursor/harness-test/SKILL.md +4 -4
- package/harness/bundles/java/cursor/harness-test/checklist.md +1 -0
- package/harness/bundles/java/cursor/harness-test/reference.md +2 -0
- package/harness/bundles/java/cursor/protocols/ledger-protocol.md +2 -2
- package/harness/bundles/java/cursor/scripts/harness_archive.py +404 -12
- package/harness/bundles/java/cursor/scripts/harness_context.py +717 -0
- package/harness/bundles/java/cursor/scripts/harness_gate.py +1 -0
- package/harness/bundles/java/cursor/scripts/harness_ledger.py +77 -9
- package/harness/bundles/java/cursor/scripts/harness_phase.py +1 -0
- package/harness/bundles/java/cursor/scripts/harness_plan_aggregate.py +249 -0
- package/harness/bundles/java/cursor/scripts/harness_profile.py +272 -15
- package/harness/bundles/java/cursor/scripts/harness_report_model.py +420 -0
- package/harness/bundles/java/cursor/scripts/harness_review.py +16 -1
- package/harness/bundles/java/cursor/scripts/harness_verification.py +114 -0
- package/harness/manifests/general/claude-code.json +42 -26
- package/harness/manifests/general/codebuddy.json +42 -26
- package/harness/manifests/general/codex.json +42 -26
- package/harness/manifests/general/cursor.json +42 -26
- package/harness/manifests/java/claude-code.json +44 -28
- package/harness/manifests/java/codebuddy.json +44 -28
- package/harness/manifests/java/codex.json +44 -28
- package/harness/manifests/java/cursor.json +44 -28
- package/hunter-workflow-family.json +4 -4
- package/package.json +1 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
|
-
"""Harness profile
|
|
2
|
+
"""Harness profile v3 — module/command graph resolver, recommender and migrator.
|
|
3
3
|
|
|
4
4
|
Change cluster 1 of harness-deterministic-performance.
|
|
5
5
|
|
|
@@ -40,9 +40,189 @@ if hasattr(sys.stderr, "reconfigure"):
|
|
|
40
40
|
sys.stderr.reconfigure(encoding="utf-8")
|
|
41
41
|
|
|
42
42
|
|
|
43
|
-
SCHEMA_VERSION =
|
|
43
|
+
SCHEMA_VERSION = 3
|
|
44
44
|
PROFILE_REL = Path(".harness") / "config" / "build-profile.json"
|
|
45
45
|
|
|
46
|
+
|
|
47
|
+
def recommend(project: Path) -> dict[str, Any]:
|
|
48
|
+
"""Return a reviewable profile recommendation without writing it."""
|
|
49
|
+
project = Path(project).resolve()
|
|
50
|
+
project_type = detect_project_type(project)
|
|
51
|
+
modules: list[dict[str, Any]] = []
|
|
52
|
+
targets: list[dict[str, Any]] = []
|
|
53
|
+
if project_type == "java-maven":
|
|
54
|
+
reactor = [item for item in find_reactor_modules(project, DEFAULT_EXCLUDED_ROOTS) if item != "."]
|
|
55
|
+
modules = [
|
|
56
|
+
{
|
|
57
|
+
"id": module,
|
|
58
|
+
"path": module,
|
|
59
|
+
"type": "java-maven",
|
|
60
|
+
"ownedPaths": [f"{module}/src/**", f"{module}/pom.xml"],
|
|
61
|
+
"dependsOn": [],
|
|
62
|
+
}
|
|
63
|
+
for module in sorted(reactor)
|
|
64
|
+
]
|
|
65
|
+
if modules:
|
|
66
|
+
targets.append(
|
|
67
|
+
{
|
|
68
|
+
"id": "unitTest-module",
|
|
69
|
+
"verification": "unitTestFull",
|
|
70
|
+
"level": "module",
|
|
71
|
+
"command": "mvn test -pl {modules} -am -o",
|
|
72
|
+
"inputs": [f"{module['path']}/src/**" for module in modules],
|
|
73
|
+
"sharedConsumer": False,
|
|
74
|
+
}
|
|
75
|
+
)
|
|
76
|
+
targets.append(
|
|
77
|
+
{
|
|
78
|
+
"id": "unitTest-candidate",
|
|
79
|
+
"verification": "unitTestFull",
|
|
80
|
+
"level": "candidate",
|
|
81
|
+
"command": "mvn -f pom.xml test -o",
|
|
82
|
+
"inputs": ["pom.xml", "**/src/**"],
|
|
83
|
+
"sharedConsumer": True,
|
|
84
|
+
}
|
|
85
|
+
)
|
|
86
|
+
elif project_type == "node":
|
|
87
|
+
try:
|
|
88
|
+
package = read_json(project / "package.json")
|
|
89
|
+
except (OSError, json.JSONDecodeError):
|
|
90
|
+
package = {}
|
|
91
|
+
workspaces = package.get("workspaces", []) if isinstance(package, dict) else []
|
|
92
|
+
if isinstance(workspaces, dict):
|
|
93
|
+
workspaces = workspaces.get("packages", [])
|
|
94
|
+
paths: set[str] = set()
|
|
95
|
+
for pattern in workspaces if isinstance(workspaces, list) else []:
|
|
96
|
+
for match in project.glob(str(pattern)):
|
|
97
|
+
if match.is_dir() and (match / "package.json").is_file():
|
|
98
|
+
paths.add(match.relative_to(project).as_posix())
|
|
99
|
+
modules = [
|
|
100
|
+
{
|
|
101
|
+
"id": path.replace("/", "-"),
|
|
102
|
+
"path": path,
|
|
103
|
+
"type": "node-workspace",
|
|
104
|
+
"ownedPaths": [f"{path}/src/**", f"{path}/test/**"],
|
|
105
|
+
"dependsOn": [],
|
|
106
|
+
}
|
|
107
|
+
for path in sorted(paths)
|
|
108
|
+
]
|
|
109
|
+
targets.extend(
|
|
110
|
+
{
|
|
111
|
+
"id": f"{module['id']}-test",
|
|
112
|
+
"verification": "unitTest",
|
|
113
|
+
"level": "module",
|
|
114
|
+
"command": f"npm test -w {module['path']}",
|
|
115
|
+
"inputs": module["ownedPaths"],
|
|
116
|
+
"sharedConsumer": False,
|
|
117
|
+
}
|
|
118
|
+
for module in modules
|
|
119
|
+
)
|
|
120
|
+
node_commands = _node_commands(project)
|
|
121
|
+
if "unitTestFull" in node_commands:
|
|
122
|
+
targets.append(
|
|
123
|
+
{
|
|
124
|
+
"id": "workspace-candidate",
|
|
125
|
+
"verification": "unitTestFull",
|
|
126
|
+
"level": "candidate",
|
|
127
|
+
"command": node_commands["unitTestFull"]["command"],
|
|
128
|
+
"inputs": node_commands["unitTestFull"]["inputs"],
|
|
129
|
+
"sharedConsumer": True,
|
|
130
|
+
}
|
|
131
|
+
)
|
|
132
|
+
boundaries_proven = bool(modules) or project_type in {"node", "java-maven"}
|
|
133
|
+
return {
|
|
134
|
+
"ok": True,
|
|
135
|
+
"code": "PROFILE_RECOMMENDED",
|
|
136
|
+
"schemaVersion": SCHEMA_VERSION,
|
|
137
|
+
"projectType": project_type,
|
|
138
|
+
"modules": modules,
|
|
139
|
+
"targets": targets,
|
|
140
|
+
"moduleGraph": {
|
|
141
|
+
"modules": modules,
|
|
142
|
+
"boundariesProven": boundaries_proven,
|
|
143
|
+
"needsReview": not boundaries_proven,
|
|
144
|
+
},
|
|
145
|
+
"needsReview": not boundaries_proven,
|
|
146
|
+
"applied": False,
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
def project_profile_v3(profile: dict[str, Any]) -> dict[str, Any]:
|
|
151
|
+
"""Compatibility projection for review before a protected v3 update."""
|
|
152
|
+
projected = json.loads(json.dumps(profile))
|
|
153
|
+
source_version = projected.get("schemaVersion")
|
|
154
|
+
if source_version == SCHEMA_VERSION:
|
|
155
|
+
return projected
|
|
156
|
+
if source_version != 2:
|
|
157
|
+
return projected
|
|
158
|
+
projected["schemaVersion"] = SCHEMA_VERSION
|
|
159
|
+
commands = projected.get("commands") if isinstance(projected.get("commands"), dict) else {}
|
|
160
|
+
projected.setdefault(
|
|
161
|
+
"moduleGraph",
|
|
162
|
+
{"modules": [], "boundariesProven": False, "needsReview": True},
|
|
163
|
+
)
|
|
164
|
+
projected["commandGraph"] = {
|
|
165
|
+
"targets": [
|
|
166
|
+
{
|
|
167
|
+
"id": key,
|
|
168
|
+
"verification": key,
|
|
169
|
+
"command": command.get("command"),
|
|
170
|
+
"scope": command.get("scope"),
|
|
171
|
+
"inputs": list(command.get("inputs") or []),
|
|
172
|
+
"coverage": command.get("coverage"),
|
|
173
|
+
"source": command.get("source"),
|
|
174
|
+
}
|
|
175
|
+
for key, command in sorted(commands.items())
|
|
176
|
+
if isinstance(command, dict)
|
|
177
|
+
]
|
|
178
|
+
}
|
|
179
|
+
projected["migration"] = {
|
|
180
|
+
"fromSchemaVersion": 2,
|
|
181
|
+
"projected": True,
|
|
182
|
+
"needsReview": True,
|
|
183
|
+
"protectedUserCommands": sorted(
|
|
184
|
+
key
|
|
185
|
+
for key, command in commands.items()
|
|
186
|
+
if isinstance(command, dict) and command.get("source") == "user"
|
|
187
|
+
),
|
|
188
|
+
}
|
|
189
|
+
return projected
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
def audit_profile(project: Path) -> dict[str, Any]:
|
|
193
|
+
path = Path(project).resolve() / PROFILE_REL
|
|
194
|
+
if not path.is_file():
|
|
195
|
+
return {
|
|
196
|
+
"ok": False,
|
|
197
|
+
"code": "PROFILE_MISSING",
|
|
198
|
+
"needsReview": True,
|
|
199
|
+
"issues": ["build-profile.json missing"],
|
|
200
|
+
}
|
|
201
|
+
try:
|
|
202
|
+
profile = read_json(path)
|
|
203
|
+
except (OSError, json.JSONDecodeError) as exc:
|
|
204
|
+
return {
|
|
205
|
+
"ok": False,
|
|
206
|
+
"code": "PROFILE_INVALID",
|
|
207
|
+
"needsReview": True,
|
|
208
|
+
"issues": [str(exc)],
|
|
209
|
+
}
|
|
210
|
+
issues: list[str] = []
|
|
211
|
+
if profile.get("schemaVersion") != SCHEMA_VERSION:
|
|
212
|
+
issues.append("profile schema requires migration")
|
|
213
|
+
module_graph = profile.get("moduleGraph")
|
|
214
|
+
if not isinstance(module_graph, dict) or module_graph.get("boundariesProven") is not True:
|
|
215
|
+
issues.append("module boundaries are not proven")
|
|
216
|
+
if not isinstance(profile.get("commandGraph"), dict):
|
|
217
|
+
issues.append("command graph missing")
|
|
218
|
+
needs_review = bool(issues)
|
|
219
|
+
return {
|
|
220
|
+
"ok": not needs_review,
|
|
221
|
+
"code": "PROFILE_REVIEW_REQUIRED" if needs_review else "PROFILE_AUDITED",
|
|
222
|
+
"needsReview": needs_review,
|
|
223
|
+
"issues": issues,
|
|
224
|
+
}
|
|
225
|
+
|
|
46
226
|
# 排除策略(spec §3.1):所有路径必须位于 project root,排除以下目录。
|
|
47
227
|
# 兄弟 worktree、构建产物、依赖目录、缓存一律不进入 verification inputs。
|
|
48
228
|
DEFAULT_EXCLUDED_ROOTS: tuple[str, ...] = (
|
|
@@ -406,6 +586,10 @@ def load_profile(project: Path) -> dict[str, Any] | None:
|
|
|
406
586
|
except (OSError, json.JSONDecodeError):
|
|
407
587
|
pass
|
|
408
588
|
|
|
589
|
+
if common_data is not None and common_data.get("schemaVersion") == 2:
|
|
590
|
+
common_data = project_profile_v3(common_data)
|
|
591
|
+
if exec_data is not None and exec_data.get("schemaVersion") == 2:
|
|
592
|
+
exec_data = project_profile_v3(exec_data)
|
|
409
593
|
if common_data is None and exec_data is None:
|
|
410
594
|
return None
|
|
411
595
|
if common_data is None:
|
|
@@ -422,6 +606,19 @@ def load_profile(project: Path) -> dict[str, Any] | None:
|
|
|
422
606
|
if isinstance(exec_cmds, dict):
|
|
423
607
|
merged_cmds.update(exec_cmds)
|
|
424
608
|
merged["buildCommands"] = merged_cmds
|
|
609
|
+
common_commands = common_data.get("commands") or {}
|
|
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
|
|
618
|
+
_derive_verification_inputs(merged)
|
|
619
|
+
for field in ("moduleGraph", "commandGraph", "migration"):
|
|
620
|
+
if field in exec_data:
|
|
621
|
+
merged[field] = exec_data[field]
|
|
425
622
|
return merged
|
|
426
623
|
|
|
427
624
|
|
|
@@ -533,6 +730,24 @@ def detect(project: Path) -> dict[str, Any]:
|
|
|
533
730
|
|
|
534
731
|
_merge_user_overrides(profile, existing)
|
|
535
732
|
_derive_verification_inputs(profile)
|
|
733
|
+
recommendation = recommend(project)
|
|
734
|
+
profile["moduleGraph"] = recommendation["moduleGraph"]
|
|
735
|
+
profile["commandGraph"] = {
|
|
736
|
+
"targets": [
|
|
737
|
+
{
|
|
738
|
+
"id": key,
|
|
739
|
+
"verification": key,
|
|
740
|
+
"command": command.get("command"),
|
|
741
|
+
"scope": command.get("scope"),
|
|
742
|
+
"inputs": list(command.get("inputs") or []),
|
|
743
|
+
"coverage": command.get("coverage"),
|
|
744
|
+
"source": command.get("source"),
|
|
745
|
+
}
|
|
746
|
+
for key, command in sorted(profile["commands"].items())
|
|
747
|
+
if isinstance(command, dict)
|
|
748
|
+
]
|
|
749
|
+
+ list(recommendation["targets"])
|
|
750
|
+
}
|
|
536
751
|
|
|
537
752
|
out_path = project / PROFILE_REL
|
|
538
753
|
write_json(out_path, profile)
|
|
@@ -735,8 +950,8 @@ def resolve_command(
|
|
|
735
950
|
# migrate(v1 → v2,dry-run/apply/备份)
|
|
736
951
|
# ---------------------------------------------------------------------------
|
|
737
952
|
|
|
738
|
-
def _backup_path(profile_path: Path) -> Path:
|
|
739
|
-
return profile_path.with_suffix(profile_path.suffix + ".
|
|
953
|
+
def _backup_path(profile_path: Path, version: int = 1) -> Path:
|
|
954
|
+
return profile_path.with_suffix(profile_path.suffix + f".v{version}.bak")
|
|
740
955
|
|
|
741
956
|
|
|
742
957
|
def migrate(project: Path, *, dry_run: bool = True) -> dict[str, Any]:
|
|
@@ -780,7 +995,7 @@ def migrate(project: Path, *, dry_run: bool = True) -> dict[str, Any]:
|
|
|
780
995
|
"needsMigration": False,
|
|
781
996
|
"reason": f"already schemaVersion {SCHEMA_VERSION}",
|
|
782
997
|
}
|
|
783
|
-
if sv
|
|
998
|
+
if sv not in {1, 2}:
|
|
784
999
|
return {
|
|
785
1000
|
"ok": False,
|
|
786
1001
|
"action": "migrate",
|
|
@@ -789,13 +1004,21 @@ def migrate(project: Path, *, dry_run: bool = True) -> dict[str, Any]:
|
|
|
789
1004
|
"error": f"unsupported schemaVersion: {sv}",
|
|
790
1005
|
}
|
|
791
1006
|
|
|
792
|
-
# v1 →
|
|
1007
|
+
# v1/v2 → v3 迁移清单
|
|
793
1008
|
changes = [
|
|
794
|
-
f"schemaVersion
|
|
795
|
-
"
|
|
796
|
-
"
|
|
797
|
-
"
|
|
1009
|
+
f"schemaVersion {sv} → {SCHEMA_VERSION}",
|
|
1010
|
+
"新增 moduleGraph(模块边界/owned paths/dependency)",
|
|
1011
|
+
"新增 commandGraph(dynamic verification targets)",
|
|
1012
|
+
"source=user 命令字节语义保持不变并列入 protectedUserCommands",
|
|
798
1013
|
]
|
|
1014
|
+
if sv == 1:
|
|
1015
|
+
changes.extend(
|
|
1016
|
+
[
|
|
1017
|
+
"buildCommands → commands(含 argvTemplate/scope/inputs/coverage/source/basis)",
|
|
1018
|
+
"excludedRoots 显式声明(.git/.harness/worktrees/target/build/node_modules/...)",
|
|
1019
|
+
"identifier 约束(pattern/maxLength/prefix)",
|
|
1020
|
+
]
|
|
1021
|
+
)
|
|
799
1022
|
dropped: list[str] = []
|
|
800
1023
|
v1_svc = existing.get("serviceStart") or {}
|
|
801
1024
|
if isinstance(v1_svc, dict):
|
|
@@ -811,23 +1034,37 @@ def migrate(project: Path, *, dry_run: bool = True) -> dict[str, Any]:
|
|
|
811
1034
|
dropped.append(f"serviceStart.{field}(无 provenance,已备份;请用 record-quirk 重配)")
|
|
812
1035
|
|
|
813
1036
|
if dry_run:
|
|
1037
|
+
projected = project_profile_v3(existing) if sv == 2 else None
|
|
814
1038
|
return {
|
|
815
1039
|
"ok": True,
|
|
1040
|
+
"code": "PROFILE_MIGRATION_PLANNED",
|
|
816
1041
|
"action": "migrate",
|
|
817
1042
|
"dry_run": True,
|
|
818
1043
|
"needsMigration": True,
|
|
819
1044
|
"changes": changes,
|
|
820
1045
|
"droppedFields": dropped,
|
|
821
1046
|
"profilePath": str(profile_path),
|
|
1047
|
+
"projectedProfile": projected,
|
|
1048
|
+
"protectedUserCommands": (
|
|
1049
|
+
projected.get("migration", {}).get("protectedUserCommands", [])
|
|
1050
|
+
if isinstance(projected, dict)
|
|
1051
|
+
else []
|
|
1052
|
+
),
|
|
822
1053
|
}
|
|
823
1054
|
|
|
824
|
-
# apply
|
|
825
|
-
backup = _backup_path(profile_path)
|
|
1055
|
+
# apply:先备份旧 profile。v2 结构兼容投影,不重新 detect,避免覆盖 user command。
|
|
1056
|
+
backup = _backup_path(profile_path, int(sv))
|
|
826
1057
|
if not backup.is_file():
|
|
827
1058
|
backup.write_text(profile_path.read_text(encoding="utf-8-sig"), encoding="utf-8", newline="\n")
|
|
828
|
-
|
|
1059
|
+
if sv == 2:
|
|
1060
|
+
projected = project_profile_v3(existing)
|
|
1061
|
+
projected.setdefault("migration", {})["appliedAt"] = now_iso()
|
|
1062
|
+
write_json(profile_path, projected)
|
|
1063
|
+
else:
|
|
1064
|
+
detect(project)
|
|
829
1065
|
return {
|
|
830
1066
|
"ok": True,
|
|
1067
|
+
"code": "PROFILE_MIGRATED",
|
|
831
1068
|
"action": "migrate",
|
|
832
1069
|
"dry_run": False,
|
|
833
1070
|
"needsMigration": False,
|
|
@@ -845,7 +1082,7 @@ def migrate(project: Path, *, dry_run: bool = True) -> dict[str, Any]:
|
|
|
845
1082
|
def build_parser() -> argparse.ArgumentParser:
|
|
846
1083
|
parser = argparse.ArgumentParser(
|
|
847
1084
|
prog="harness_profile.py",
|
|
848
|
-
description="Harness profile
|
|
1085
|
+
description="Harness profile v3: recommend/detect/audit/validate/resolve/migrate",
|
|
849
1086
|
)
|
|
850
1087
|
parser.add_argument("--json", action="store_true", help="emit JSON")
|
|
851
1088
|
sub = parser.add_subparsers(dest="command", required=True)
|
|
@@ -854,6 +1091,18 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
854
1091
|
p_detect.add_argument("--project", required=True, type=Path)
|
|
855
1092
|
p_detect.add_argument("--json", action="store_true")
|
|
856
1093
|
|
|
1094
|
+
p_recommend = sub.add_parser(
|
|
1095
|
+
"recommend", help="analyze modules/targets without writing canonical profile"
|
|
1096
|
+
)
|
|
1097
|
+
p_recommend.add_argument("--project", required=True, type=Path)
|
|
1098
|
+
p_recommend.add_argument("--json", action="store_true")
|
|
1099
|
+
|
|
1100
|
+
p_audit = sub.add_parser(
|
|
1101
|
+
"audit", help="fail closed when module or command boundaries are unproven"
|
|
1102
|
+
)
|
|
1103
|
+
p_audit.add_argument("--project", required=True, type=Path)
|
|
1104
|
+
p_audit.add_argument("--json", action="store_true")
|
|
1105
|
+
|
|
857
1106
|
p_check = sub.add_parser("check", help="missing/invalid/stale/ready")
|
|
858
1107
|
p_check.add_argument("--project", required=True, type=Path)
|
|
859
1108
|
p_check.add_argument("--json", action="store_true")
|
|
@@ -879,7 +1128,7 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
879
1128
|
)
|
|
880
1129
|
p_resolve.add_argument("--json", action="store_true")
|
|
881
1130
|
|
|
882
|
-
p_migrate = sub.add_parser("migrate", help="migrate v1 →
|
|
1131
|
+
p_migrate = sub.add_parser("migrate", help="migrate v1/v2 → v3")
|
|
883
1132
|
p_migrate.add_argument("--project", required=True, type=Path)
|
|
884
1133
|
p_migrate.add_argument("--apply", action="store_true", help="apply (default: dry-run)")
|
|
885
1134
|
p_migrate.add_argument("--json", action="store_true")
|
|
@@ -896,6 +1145,14 @@ def main(argv: list[str] | None = None) -> int:
|
|
|
896
1145
|
result = detect(args.project)
|
|
897
1146
|
sys.stdout.write(json.dumps(result, ensure_ascii=False, indent=2) + "\n")
|
|
898
1147
|
return 0 if result.get("ok") else 1
|
|
1148
|
+
if args.command == "recommend":
|
|
1149
|
+
result = recommend(args.project)
|
|
1150
|
+
sys.stdout.write(json.dumps(result, ensure_ascii=False, indent=2) + "\n")
|
|
1151
|
+
return 0 if result.get("ok") else 1
|
|
1152
|
+
if args.command == "audit":
|
|
1153
|
+
result = audit_profile(args.project)
|
|
1154
|
+
sys.stdout.write(json.dumps(result, ensure_ascii=False, indent=2) + "\n")
|
|
1155
|
+
return 0 if result.get("ok") else 1
|
|
899
1156
|
if args.command == "check":
|
|
900
1157
|
result = check(args.project)
|
|
901
1158
|
sys.stdout.write(json.dumps(result, ensure_ascii=False, indent=2) + "\n")
|