@hunter-harness/workflow-harness 0.2.84 → 0.2.86
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/harness/bundles/general/claude-code/.harness-build.json +1 -1
- package/harness/bundles/general/claude-code/harness-archive/SKILL.md +11 -4
- package/harness/bundles/general/claude-code/harness-codebase-map/SKILL.md +1 -1
- package/harness/bundles/general/claude-code/harness-knowledge-ingest/SKILL.md +1 -1
- package/harness/bundles/general/claude-code/harness-knowledge-query/SKILL.md +1 -1
- package/harness/bundles/general/claude-code/harness-plan/SKILL.md +3 -2
- package/harness/bundles/general/claude-code/harness-plan/checklist.md +18 -79
- package/harness/bundles/general/claude-code/harness-plan/protocols.md +3 -1
- package/harness/bundles/general/claude-code/harness-plan/reference.md +37 -8
- package/harness/bundles/general/claude-code/harness-pull/SKILL.md +1 -1
- package/harness/bundles/general/claude-code/harness-push/SKILL.md +1 -1
- package/harness/bundles/general/claude-code/harness-review/SKILL.md +1 -1
- package/harness/bundles/general/claude-code/harness-run/SKILL.md +6 -4
- package/harness/bundles/general/claude-code/harness-submit/SKILL.md +1 -1
- package/harness/bundles/general/claude-code/harness-sync/SKILL.md +1 -1
- package/harness/bundles/general/claude-code/harness-test/SKILL.md +1 -1
- package/harness/bundles/general/claude-code/scripts/harness_archive.py +58 -25
- package/harness/bundles/general/claude-code/scripts/harness_change.py +25 -7
- package/harness/bundles/general/claude-code/scripts/harness_context.py +62 -19
- package/harness/bundles/general/claude-code/scripts/harness_fixback.py +4 -0
- package/harness/bundles/general/claude-code/scripts/harness_gate.py +208 -72
- package/harness/bundles/general/claude-code/scripts/harness_ledger.py +43 -0
- package/harness/bundles/general/claude-code/scripts/harness_paths.py +60 -0
- package/harness/bundles/general/claude-code/scripts/harness_phase.py +24 -6
- package/harness/bundles/general/claude-code/scripts/harness_plan_finalize.py +124 -1
- package/harness/bundles/general/claude-code/scripts/harness_review.py +25 -4
- package/harness/bundles/general/codebuddy/.harness-build.json +1 -1
- package/harness/bundles/general/codebuddy/harness-archive/SKILL.md +11 -4
- package/harness/bundles/general/codebuddy/harness-codebase-map/SKILL.md +1 -1
- package/harness/bundles/general/codebuddy/harness-knowledge-ingest/SKILL.md +1 -1
- package/harness/bundles/general/codebuddy/harness-knowledge-query/SKILL.md +1 -1
- package/harness/bundles/general/codebuddy/harness-plan/SKILL.md +3 -2
- package/harness/bundles/general/codebuddy/harness-plan/checklist.md +18 -79
- package/harness/bundles/general/codebuddy/harness-plan/protocols.md +3 -1
- package/harness/bundles/general/codebuddy/harness-plan/reference.md +37 -8
- package/harness/bundles/general/codebuddy/harness-pull/SKILL.md +1 -1
- package/harness/bundles/general/codebuddy/harness-push/SKILL.md +1 -1
- package/harness/bundles/general/codebuddy/harness-review/SKILL.md +1 -1
- package/harness/bundles/general/codebuddy/harness-run/SKILL.md +6 -4
- package/harness/bundles/general/codebuddy/harness-submit/SKILL.md +1 -1
- package/harness/bundles/general/codebuddy/harness-sync/SKILL.md +1 -1
- package/harness/bundles/general/codebuddy/harness-test/SKILL.md +1 -1
- package/harness/bundles/general/codebuddy/scripts/harness_archive.py +58 -25
- package/harness/bundles/general/codebuddy/scripts/harness_change.py +25 -7
- package/harness/bundles/general/codebuddy/scripts/harness_context.py +62 -19
- package/harness/bundles/general/codebuddy/scripts/harness_fixback.py +4 -0
- package/harness/bundles/general/codebuddy/scripts/harness_gate.py +208 -72
- package/harness/bundles/general/codebuddy/scripts/harness_ledger.py +43 -0
- package/harness/bundles/general/codebuddy/scripts/harness_paths.py +60 -0
- package/harness/bundles/general/codebuddy/scripts/harness_phase.py +24 -6
- package/harness/bundles/general/codebuddy/scripts/harness_plan_finalize.py +124 -1
- package/harness/bundles/general/codebuddy/scripts/harness_review.py +25 -4
- package/harness/bundles/general/codex/.harness-build.json +1 -1
- package/harness/bundles/general/codex/harness-archive/SKILL.md +11 -4
- package/harness/bundles/general/codex/harness-codebase-map/SKILL.md +1 -1
- package/harness/bundles/general/codex/harness-knowledge-ingest/SKILL.md +1 -1
- package/harness/bundles/general/codex/harness-knowledge-query/SKILL.md +1 -1
- package/harness/bundles/general/codex/harness-plan/SKILL.md +3 -2
- package/harness/bundles/general/codex/harness-plan/checklist.md +18 -79
- package/harness/bundles/general/codex/harness-plan/protocols.md +3 -1
- package/harness/bundles/general/codex/harness-plan/reference.md +37 -8
- package/harness/bundles/general/codex/harness-pull/SKILL.md +1 -1
- package/harness/bundles/general/codex/harness-push/SKILL.md +1 -1
- package/harness/bundles/general/codex/harness-review/SKILL.md +1 -1
- package/harness/bundles/general/codex/harness-run/SKILL.md +6 -4
- package/harness/bundles/general/codex/harness-submit/SKILL.md +1 -1
- package/harness/bundles/general/codex/harness-sync/SKILL.md +1 -1
- package/harness/bundles/general/codex/harness-test/SKILL.md +1 -1
- package/harness/bundles/general/codex/scripts/harness_archive.py +58 -25
- package/harness/bundles/general/codex/scripts/harness_change.py +25 -7
- package/harness/bundles/general/codex/scripts/harness_context.py +62 -19
- package/harness/bundles/general/codex/scripts/harness_fixback.py +4 -0
- package/harness/bundles/general/codex/scripts/harness_gate.py +208 -72
- package/harness/bundles/general/codex/scripts/harness_ledger.py +43 -0
- package/harness/bundles/general/codex/scripts/harness_paths.py +60 -0
- package/harness/bundles/general/codex/scripts/harness_phase.py +24 -6
- package/harness/bundles/general/codex/scripts/harness_plan_finalize.py +124 -1
- package/harness/bundles/general/codex/scripts/harness_review.py +25 -4
- package/harness/bundles/general/cursor/.harness-build.json +1 -1
- package/harness/bundles/general/cursor/harness-archive/SKILL.md +11 -4
- package/harness/bundles/general/cursor/harness-codebase-map/SKILL.md +1 -1
- package/harness/bundles/general/cursor/harness-knowledge-ingest/SKILL.md +1 -1
- package/harness/bundles/general/cursor/harness-knowledge-query/SKILL.md +1 -1
- package/harness/bundles/general/cursor/harness-plan/SKILL.md +3 -2
- package/harness/bundles/general/cursor/harness-plan/checklist.md +18 -79
- package/harness/bundles/general/cursor/harness-plan/protocols.md +3 -1
- package/harness/bundles/general/cursor/harness-plan/reference.md +37 -8
- package/harness/bundles/general/cursor/harness-pull/SKILL.md +1 -1
- package/harness/bundles/general/cursor/harness-push/SKILL.md +1 -1
- package/harness/bundles/general/cursor/harness-review/SKILL.md +1 -1
- package/harness/bundles/general/cursor/harness-run/SKILL.md +6 -4
- package/harness/bundles/general/cursor/harness-submit/SKILL.md +1 -1
- package/harness/bundles/general/cursor/harness-sync/SKILL.md +1 -1
- package/harness/bundles/general/cursor/harness-test/SKILL.md +1 -1
- package/harness/bundles/general/cursor/scripts/harness_archive.py +58 -25
- package/harness/bundles/general/cursor/scripts/harness_change.py +25 -7
- package/harness/bundles/general/cursor/scripts/harness_context.py +62 -19
- package/harness/bundles/general/cursor/scripts/harness_fixback.py +4 -0
- package/harness/bundles/general/cursor/scripts/harness_gate.py +208 -72
- package/harness/bundles/general/cursor/scripts/harness_ledger.py +43 -0
- package/harness/bundles/general/cursor/scripts/harness_paths.py +60 -0
- package/harness/bundles/general/cursor/scripts/harness_phase.py +24 -6
- package/harness/bundles/general/cursor/scripts/harness_plan_finalize.py +124 -1
- package/harness/bundles/general/cursor/scripts/harness_review.py +25 -4
- package/harness/bundles/java/claude-code/.harness-build.json +1 -1
- package/harness/bundles/java/claude-code/harness-apidoc/SKILL.md +1 -1
- package/harness/bundles/java/claude-code/harness-archive/SKILL.md +11 -4
- package/harness/bundles/java/claude-code/harness-codebase-map/SKILL.md +1 -1
- package/harness/bundles/java/claude-code/harness-knowledge-ingest/SKILL.md +1 -1
- package/harness/bundles/java/claude-code/harness-knowledge-query/SKILL.md +1 -1
- package/harness/bundles/java/claude-code/harness-package/SKILL.md +1 -1
- package/harness/bundles/java/claude-code/harness-plan/SKILL.md +3 -2
- package/harness/bundles/java/claude-code/harness-plan/checklist.md +18 -79
- package/harness/bundles/java/claude-code/harness-plan/protocols.md +3 -1
- package/harness/bundles/java/claude-code/harness-plan/reference.md +37 -8
- package/harness/bundles/java/claude-code/harness-pull/SKILL.md +1 -1
- package/harness/bundles/java/claude-code/harness-push/SKILL.md +1 -1
- package/harness/bundles/java/claude-code/harness-review/SKILL.md +1 -1
- package/harness/bundles/java/claude-code/harness-run/SKILL.md +6 -4
- package/harness/bundles/java/claude-code/harness-submit/SKILL.md +1 -1
- package/harness/bundles/java/claude-code/harness-sync/SKILL.md +1 -1
- package/harness/bundles/java/claude-code/harness-test/SKILL.md +1 -1
- package/harness/bundles/java/claude-code/scripts/harness_archive.py +58 -25
- package/harness/bundles/java/claude-code/scripts/harness_change.py +25 -7
- package/harness/bundles/java/claude-code/scripts/harness_context.py +62 -19
- package/harness/bundles/java/claude-code/scripts/harness_fixback.py +4 -0
- package/harness/bundles/java/claude-code/scripts/harness_gate.py +208 -72
- package/harness/bundles/java/claude-code/scripts/harness_ledger.py +43 -0
- package/harness/bundles/java/claude-code/scripts/harness_paths.py +60 -0
- package/harness/bundles/java/claude-code/scripts/harness_phase.py +24 -6
- package/harness/bundles/java/claude-code/scripts/harness_plan_finalize.py +124 -1
- package/harness/bundles/java/claude-code/scripts/harness_review.py +25 -4
- package/harness/bundles/java/codebuddy/.harness-build.json +1 -1
- package/harness/bundles/java/codebuddy/harness-apidoc/SKILL.md +1 -1
- package/harness/bundles/java/codebuddy/harness-archive/SKILL.md +11 -4
- package/harness/bundles/java/codebuddy/harness-codebase-map/SKILL.md +1 -1
- package/harness/bundles/java/codebuddy/harness-knowledge-ingest/SKILL.md +1 -1
- package/harness/bundles/java/codebuddy/harness-knowledge-query/SKILL.md +1 -1
- package/harness/bundles/java/codebuddy/harness-package/SKILL.md +1 -1
- package/harness/bundles/java/codebuddy/harness-plan/SKILL.md +3 -2
- package/harness/bundles/java/codebuddy/harness-plan/checklist.md +18 -79
- package/harness/bundles/java/codebuddy/harness-plan/protocols.md +3 -1
- package/harness/bundles/java/codebuddy/harness-plan/reference.md +37 -8
- package/harness/bundles/java/codebuddy/harness-pull/SKILL.md +1 -1
- package/harness/bundles/java/codebuddy/harness-push/SKILL.md +1 -1
- package/harness/bundles/java/codebuddy/harness-review/SKILL.md +1 -1
- package/harness/bundles/java/codebuddy/harness-run/SKILL.md +6 -4
- package/harness/bundles/java/codebuddy/harness-submit/SKILL.md +1 -1
- package/harness/bundles/java/codebuddy/harness-sync/SKILL.md +1 -1
- package/harness/bundles/java/codebuddy/harness-test/SKILL.md +1 -1
- package/harness/bundles/java/codebuddy/scripts/harness_archive.py +58 -25
- package/harness/bundles/java/codebuddy/scripts/harness_change.py +25 -7
- package/harness/bundles/java/codebuddy/scripts/harness_context.py +62 -19
- package/harness/bundles/java/codebuddy/scripts/harness_fixback.py +4 -0
- package/harness/bundles/java/codebuddy/scripts/harness_gate.py +208 -72
- package/harness/bundles/java/codebuddy/scripts/harness_ledger.py +43 -0
- package/harness/bundles/java/codebuddy/scripts/harness_paths.py +60 -0
- package/harness/bundles/java/codebuddy/scripts/harness_phase.py +24 -6
- package/harness/bundles/java/codebuddy/scripts/harness_plan_finalize.py +124 -1
- package/harness/bundles/java/codebuddy/scripts/harness_review.py +25 -4
- package/harness/bundles/java/codex/.harness-build.json +1 -1
- package/harness/bundles/java/codex/harness-apidoc/SKILL.md +1 -1
- package/harness/bundles/java/codex/harness-archive/SKILL.md +11 -4
- package/harness/bundles/java/codex/harness-codebase-map/SKILL.md +1 -1
- package/harness/bundles/java/codex/harness-knowledge-ingest/SKILL.md +1 -1
- package/harness/bundles/java/codex/harness-knowledge-query/SKILL.md +1 -1
- package/harness/bundles/java/codex/harness-package/SKILL.md +1 -1
- package/harness/bundles/java/codex/harness-plan/SKILL.md +3 -2
- package/harness/bundles/java/codex/harness-plan/checklist.md +18 -79
- package/harness/bundles/java/codex/harness-plan/protocols.md +3 -1
- package/harness/bundles/java/codex/harness-plan/reference.md +37 -8
- package/harness/bundles/java/codex/harness-pull/SKILL.md +1 -1
- package/harness/bundles/java/codex/harness-push/SKILL.md +1 -1
- package/harness/bundles/java/codex/harness-review/SKILL.md +1 -1
- package/harness/bundles/java/codex/harness-run/SKILL.md +6 -4
- package/harness/bundles/java/codex/harness-submit/SKILL.md +1 -1
- package/harness/bundles/java/codex/harness-sync/SKILL.md +1 -1
- package/harness/bundles/java/codex/harness-test/SKILL.md +1 -1
- package/harness/bundles/java/codex/scripts/harness_archive.py +58 -25
- package/harness/bundles/java/codex/scripts/harness_change.py +25 -7
- package/harness/bundles/java/codex/scripts/harness_context.py +62 -19
- package/harness/bundles/java/codex/scripts/harness_fixback.py +4 -0
- package/harness/bundles/java/codex/scripts/harness_gate.py +208 -72
- package/harness/bundles/java/codex/scripts/harness_ledger.py +43 -0
- package/harness/bundles/java/codex/scripts/harness_paths.py +60 -0
- package/harness/bundles/java/codex/scripts/harness_phase.py +24 -6
- package/harness/bundles/java/codex/scripts/harness_plan_finalize.py +124 -1
- package/harness/bundles/java/codex/scripts/harness_review.py +25 -4
- package/harness/bundles/java/cursor/.harness-build.json +1 -1
- package/harness/bundles/java/cursor/harness-apidoc/SKILL.md +1 -1
- package/harness/bundles/java/cursor/harness-archive/SKILL.md +11 -4
- package/harness/bundles/java/cursor/harness-codebase-map/SKILL.md +1 -1
- package/harness/bundles/java/cursor/harness-knowledge-ingest/SKILL.md +1 -1
- package/harness/bundles/java/cursor/harness-knowledge-query/SKILL.md +1 -1
- package/harness/bundles/java/cursor/harness-package/SKILL.md +1 -1
- package/harness/bundles/java/cursor/harness-plan/SKILL.md +3 -2
- package/harness/bundles/java/cursor/harness-plan/checklist.md +18 -79
- package/harness/bundles/java/cursor/harness-plan/protocols.md +3 -1
- package/harness/bundles/java/cursor/harness-plan/reference.md +37 -8
- package/harness/bundles/java/cursor/harness-pull/SKILL.md +1 -1
- package/harness/bundles/java/cursor/harness-push/SKILL.md +1 -1
- package/harness/bundles/java/cursor/harness-review/SKILL.md +1 -1
- package/harness/bundles/java/cursor/harness-run/SKILL.md +6 -4
- package/harness/bundles/java/cursor/harness-submit/SKILL.md +1 -1
- package/harness/bundles/java/cursor/harness-sync/SKILL.md +1 -1
- package/harness/bundles/java/cursor/harness-test/SKILL.md +1 -1
- package/harness/bundles/java/cursor/scripts/harness_archive.py +58 -25
- package/harness/bundles/java/cursor/scripts/harness_change.py +25 -7
- package/harness/bundles/java/cursor/scripts/harness_context.py +62 -19
- package/harness/bundles/java/cursor/scripts/harness_fixback.py +4 -0
- package/harness/bundles/java/cursor/scripts/harness_gate.py +208 -72
- package/harness/bundles/java/cursor/scripts/harness_ledger.py +43 -0
- package/harness/bundles/java/cursor/scripts/harness_paths.py +60 -0
- package/harness/bundles/java/cursor/scripts/harness_phase.py +24 -6
- package/harness/bundles/java/cursor/scripts/harness_plan_finalize.py +124 -1
- package/harness/bundles/java/cursor/scripts/harness_review.py +25 -4
- package/harness/manifests/general/claude-code.json +27 -27
- package/harness/manifests/general/codebuddy.json +27 -27
- package/harness/manifests/general/codex.json +27 -27
- package/harness/manifests/general/cursor.json +27 -27
- package/harness/manifests/java/claude-code.json +29 -29
- package/harness/manifests/java/codebuddy.json +29 -29
- package/harness/manifests/java/codex.json +29 -29
- package/harness/manifests/java/cursor.json +29 -29
- package/hunter-workflow-family.json +4 -4
- package/package.json +1 -1
|
@@ -3,7 +3,7 @@ name: harness-test
|
|
|
3
3
|
description: 测试执行:读取场景表,执行单元测试+API接口测试+数据兼容验证,输出测试报告。仅当用户显式调用 /harness-test
|
|
4
4
|
时使用;不得在 run 结束后自动接续执行。
|
|
5
5
|
---
|
|
6
|
-
<!-- generated by harness_deploy.py; core=
|
|
6
|
+
<!-- generated by harness_deploy.py; core=874039c57f9d1bc4; overlay=java; agent=codex; do not edit -->
|
|
7
7
|
# harness-test — 测试执行
|
|
8
8
|
|
|
9
9
|
## Purpose
|
|
@@ -2599,23 +2599,12 @@ def check_status(
|
|
|
2599
2599
|
}
|
|
2600
2600
|
)
|
|
2601
2601
|
|
|
2602
|
-
|
|
2603
|
-
|
|
2604
|
-
|
|
2605
|
-
|
|
2606
|
-
|
|
2607
|
-
|
|
2608
|
-
warnings.append({
|
|
2609
|
-
"code": "PUBLICATION_CONTENT_SCAN_FLAGGED",
|
|
2610
|
-
"message": str(content_scan.get("nextAction") or ""),
|
|
2611
|
-
})
|
|
2612
|
-
elif not content_scan.get("ok"):
|
|
2613
|
-
warnings.append({
|
|
2614
|
-
"code": str(
|
|
2615
|
-
content_scan.get("reasonCode") or "PUBLICATION_CONTENT_SCAN_UNAVAILABLE"
|
|
2616
|
-
),
|
|
2617
|
-
"message": str(content_scan.get("message") or ""),
|
|
2618
|
-
})
|
|
2602
|
+
checks["publication_content_scan"] = {
|
|
2603
|
+
"ok": True,
|
|
2604
|
+
"scan_performed": False,
|
|
2605
|
+
"findings": [],
|
|
2606
|
+
"message": "上传归档链路已停用内容敏感扫描;保留路径、凭据与 ZIP 安全校验。",
|
|
2607
|
+
}
|
|
2619
2608
|
|
|
2620
2609
|
sensitive_gate = validate_sensitive_evidence_publication_gate(
|
|
2621
2610
|
change_dir,
|
|
@@ -9408,6 +9397,9 @@ def resolve_hunter_cli_command(project_root: Path) -> list[str]:
|
|
|
9408
9397
|
"""Command prefix that runs the hunter-harness CLI, cheapest route first."""
|
|
9409
9398
|
node = shutil.which("node")
|
|
9410
9399
|
if node:
|
|
9400
|
+
current_entry = os.environ.get("HUNTER_HARNESS_CLI_ENTRY", "").strip()
|
|
9401
|
+
if current_entry and Path(current_entry).is_file():
|
|
9402
|
+
return [node, str(Path(current_entry).resolve())]
|
|
9411
9403
|
entry = _installed_cli_entry(project_root)
|
|
9412
9404
|
if entry is not None:
|
|
9413
9405
|
return [node, str(entry)]
|
|
@@ -9774,6 +9766,7 @@ def auto_push_archive_core(
|
|
|
9774
9766
|
change_key: str | None = None,
|
|
9775
9767
|
extra_entries: dict[str, tuple[str, str, bytes]] | None = None,
|
|
9776
9768
|
reuse_retained_package: bool = False,
|
|
9769
|
+
prepared_package: dict[str, Any] | None = None,
|
|
9777
9770
|
) -> dict[str, Any]:
|
|
9778
9771
|
"""Build and upload one deterministic core ZIP after finalize.
|
|
9779
9772
|
|
|
@@ -9786,11 +9779,13 @@ def auto_push_archive_core(
|
|
|
9786
9779
|
"""
|
|
9787
9780
|
project_root = project_root.resolve()
|
|
9788
9781
|
effective_change_key = change_key or archive_dir.name
|
|
9789
|
-
package =
|
|
9790
|
-
|
|
9791
|
-
|
|
9792
|
-
|
|
9793
|
-
|
|
9782
|
+
package = prepared_package
|
|
9783
|
+
if package is None:
|
|
9784
|
+
package = (
|
|
9785
|
+
load_retained_package(project_root, effective_change_key)
|
|
9786
|
+
if reuse_retained_package
|
|
9787
|
+
else None
|
|
9788
|
+
)
|
|
9794
9789
|
if package is None:
|
|
9795
9790
|
try:
|
|
9796
9791
|
package = build_archive_package(
|
|
@@ -10637,6 +10632,28 @@ def resolve_archive_dir_for_change(
|
|
|
10637
10632
|
return archive_root / matches[-1], matches
|
|
10638
10633
|
|
|
10639
10634
|
|
|
10635
|
+
def resolve_latest_unpublished_archive(project_root: Path) -> tuple[str | None, list[str]]:
|
|
10636
|
+
"""Choose the newest sealed archive without a durable local receipt."""
|
|
10637
|
+
archive_root = project_root / ".harness" / "archive"
|
|
10638
|
+
if not archive_root.is_dir():
|
|
10639
|
+
return None, []
|
|
10640
|
+
candidates: list[tuple[str, str]] = []
|
|
10641
|
+
for entry in archive_root.iterdir():
|
|
10642
|
+
if not entry.is_dir():
|
|
10643
|
+
continue
|
|
10644
|
+
change = entry.name
|
|
10645
|
+
try:
|
|
10646
|
+
summary = read_json(entry / "reports" / "final" / "summary-data.json")
|
|
10647
|
+
if isinstance(summary, dict) and isinstance(summary.get("changeName"), str):
|
|
10648
|
+
change = str(summary["changeName"]).strip() or change
|
|
10649
|
+
except (OSError, ValueError, json.JSONDecodeError):
|
|
10650
|
+
pass
|
|
10651
|
+
if read_durable_archive_receipt(project_root, change) is None:
|
|
10652
|
+
candidates.append((entry.name, change))
|
|
10653
|
+
candidates.sort(reverse=True)
|
|
10654
|
+
return (candidates[0][1] if candidates else None), [item[1] for item in candidates]
|
|
10655
|
+
|
|
10656
|
+
|
|
10640
10657
|
def _republish_knowledge_entry(archive_dir: Path) -> dict[str, tuple[str, str, bytes]]:
|
|
10641
10658
|
"""Knowledge candidates for archives sealed before the candidates step.
|
|
10642
10659
|
|
|
@@ -10707,12 +10724,26 @@ def cmd_republish(
|
|
|
10707
10724
|
"changeKey": change_key,
|
|
10708
10725
|
"dryRun": dry_run,
|
|
10709
10726
|
}
|
|
10727
|
+
root = resolve_republish_project_root(project_root, archive_dir)
|
|
10728
|
+
if change_key == "latest":
|
|
10729
|
+
selected, available = resolve_latest_unpublished_archive(root)
|
|
10730
|
+
if selected is None:
|
|
10731
|
+
payload.update({
|
|
10732
|
+
"ok": True,
|
|
10733
|
+
"reasonCode": "ARCHIVE_NO_UNPUBLISHED_ARCHIVE",
|
|
10734
|
+
"noChanges": True,
|
|
10735
|
+
"availableChanges": available,
|
|
10736
|
+
"projectRoot": str(root)
|
|
10737
|
+
})
|
|
10738
|
+
return 0, payload
|
|
10739
|
+
change_key = selected
|
|
10740
|
+
payload["changeKey"] = change_key
|
|
10741
|
+
payload["selectedChange"] = selected
|
|
10710
10742
|
if re.fullmatch(r"[A-Za-z0-9][A-Za-z0-9._-]{0,159}", change_key) is None:
|
|
10711
10743
|
payload["error"] = "change key must be a portable path segment"
|
|
10712
10744
|
payload["reasonCode"] = "ARCHIVE_CHANGE_KEY_INVALID"
|
|
10713
10745
|
return 2, payload
|
|
10714
10746
|
|
|
10715
|
-
root = resolve_republish_project_root(project_root, archive_dir)
|
|
10716
10747
|
payload["projectRoot"] = str(root)
|
|
10717
10748
|
matches: list[str] = []
|
|
10718
10749
|
if archive_dir is None:
|
|
@@ -10821,7 +10852,7 @@ def cmd_republish(
|
|
|
10821
10852
|
# distinct name; it must never be mistaken for a pending upload.
|
|
10822
10853
|
preview_path = (
|
|
10823
10854
|
root / ".harness" / "state" / "local" / "archive-packages"
|
|
10824
|
-
/ f"{change_key}.preview.zip"
|
|
10855
|
+
/ f"{change_key}{'.preview.zip' if dry_run else '.zip'}"
|
|
10825
10856
|
)
|
|
10826
10857
|
try:
|
|
10827
10858
|
package = build_archive_package(
|
|
@@ -10837,7 +10868,8 @@ def cmd_republish(
|
|
|
10837
10868
|
return 1, payload
|
|
10838
10869
|
finally:
|
|
10839
10870
|
try:
|
|
10840
|
-
|
|
10871
|
+
if dry_run:
|
|
10872
|
+
preview_path.unlink(missing_ok=True)
|
|
10841
10873
|
except OSError:
|
|
10842
10874
|
pass
|
|
10843
10875
|
|
|
@@ -10907,6 +10939,7 @@ def cmd_republish(
|
|
|
10907
10939
|
archive_dir,
|
|
10908
10940
|
change_key=change_key,
|
|
10909
10941
|
extra_entries=extra_entries,
|
|
10942
|
+
prepared_package=package,
|
|
10910
10943
|
)
|
|
10911
10944
|
payload["push"] = result
|
|
10912
10945
|
payload["ok"] = bool(result.get("ok"))
|
|
@@ -564,18 +564,36 @@ def _lease_expired(lease: dict[str, Any]) -> bool:
|
|
|
564
564
|
return dt.datetime.now().astimezone() >= exp_dt
|
|
565
565
|
|
|
566
566
|
|
|
567
|
-
def
|
|
568
|
-
"""
|
|
567
|
+
def inspect_lease_state(project_root: Path, change_id: str) -> dict[str, Any]:
|
|
568
|
+
"""Read the lease without mutating it, keeping the three failure modes apart.
|
|
569
|
+
|
|
570
|
+
``inspect_lease`` collapses absent / expired / corrupt into ``None``, so a
|
|
571
|
+
caller cannot tell "nobody ever held this" from "the phase outlived its
|
|
572
|
+
TTL". Those need different answers: an expired lease still carries the
|
|
573
|
+
runId that proves who owns the phase, a corrupt one proves nothing, and an
|
|
574
|
+
absent one may just mean the phase already closed.
|
|
575
|
+
|
|
576
|
+
state is one of ``active`` | ``expired`` | ``absent`` | ``corrupt``. The
|
|
577
|
+
lease dict is returned for ``active`` and ``expired``; ``None`` otherwise.
|
|
578
|
+
"""
|
|
569
579
|
path = _lease_path(project_root, change_id)
|
|
570
580
|
if not path.is_file():
|
|
571
|
-
return None
|
|
581
|
+
return {"state": "absent", "lease": None}
|
|
572
582
|
try:
|
|
573
583
|
lease = _read_json(path)
|
|
574
584
|
except (OSError, json.JSONDecodeError):
|
|
575
|
-
return None
|
|
576
|
-
if not isinstance(lease, dict)
|
|
577
|
-
return None
|
|
578
|
-
|
|
585
|
+
return {"state": "corrupt", "lease": None}
|
|
586
|
+
if not isinstance(lease, dict):
|
|
587
|
+
return {"state": "corrupt", "lease": None}
|
|
588
|
+
if _lease_expired(lease):
|
|
589
|
+
return {"state": "expired", "lease": lease}
|
|
590
|
+
return {"state": "active", "lease": lease}
|
|
591
|
+
|
|
592
|
+
|
|
593
|
+
def inspect_lease(project_root: Path, change_id: str) -> dict[str, Any] | None:
|
|
594
|
+
"""Return the current non-expired lease without mutating it."""
|
|
595
|
+
state = inspect_lease_state(project_root, change_id)
|
|
596
|
+
return state["lease"] if state["state"] == "active" else None
|
|
579
597
|
|
|
580
598
|
|
|
581
599
|
def _claim_lease_locked(
|
|
@@ -21,6 +21,7 @@ if str(SCRIPTS_DIR) not in sys.path:
|
|
|
21
21
|
sys.path.insert(0, str(SCRIPTS_DIR))
|
|
22
22
|
|
|
23
23
|
import harness_ledger as hl # noqa: E402
|
|
24
|
+
import harness_paths as hpaths # noqa: E402
|
|
24
25
|
|
|
25
26
|
|
|
26
27
|
PHASE_GRAPH = {
|
|
@@ -31,17 +32,8 @@ PHASE_GRAPH = {
|
|
|
31
32
|
"submit": (),
|
|
32
33
|
}
|
|
33
34
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
"run",
|
|
37
|
-
"test",
|
|
38
|
-
"review",
|
|
39
|
-
"package",
|
|
40
|
-
"apidoc",
|
|
41
|
-
"submit",
|
|
42
|
-
"merge",
|
|
43
|
-
"archive",
|
|
44
|
-
)
|
|
35
|
+
# 唯一权威清单在 harness_paths(叶子模块,harness_phase 引的是同一个对象)。
|
|
36
|
+
WORKFLOW_PHASES = hpaths.WORKFLOW_PHASES
|
|
45
37
|
|
|
46
38
|
DISPLAY_TITLE_MAX_LENGTH = 80
|
|
47
39
|
|
|
@@ -270,13 +262,24 @@ def _contract(project: Path, change: str) -> tuple[Path, dict[str, Any], Path]:
|
|
|
270
262
|
|
|
271
263
|
|
|
272
264
|
def _phase_plan(contract_root: Path) -> tuple[list[str] | None, str]:
|
|
265
|
+
"""读出阶段计划;无法采信时说明原因,而不是悄悄退成 legacy。
|
|
266
|
+
|
|
267
|
+
以前这里遇到未知阶段名一律返回 (None, "legacy"):阶段计划整个失效,
|
|
268
|
+
`_allowed_next_phases` 退回硬编码的 PHASE_GRAPH,而调用方看不出发生过这件事。
|
|
269
|
+
未知阶段名不是假想问题——阶段清单少一个 merge 时,worktree 变更的
|
|
270
|
+
plannedPhases 就会带着 merge 走到这里。
|
|
271
|
+
|
|
272
|
+
未知名先过 LEGACY_PHASE_ALIASES 读时映射(阶段改名后历史 change 靠它继续可读),
|
|
273
|
+
仍然解析不了才降级,并把原因与具体阶段名带在 source 里。
|
|
274
|
+
"""
|
|
273
275
|
policy_path = contract_root / "meta" / "gate-policy.json"
|
|
274
276
|
if not policy_path.is_file():
|
|
275
277
|
return None, "legacy"
|
|
276
278
|
try:
|
|
277
279
|
policy = _read_json(policy_path)
|
|
278
280
|
except (OSError, ValueError, json.JSONDecodeError):
|
|
279
|
-
return None, "legacy"
|
|
281
|
+
return None, "legacy:policy-unreadable"
|
|
282
|
+
unknown: list[str] = []
|
|
280
283
|
for field, source in (
|
|
281
284
|
("plannedPhases", "change"),
|
|
282
285
|
("defaultPhases", "policy-default"),
|
|
@@ -285,12 +288,17 @@ def _phase_plan(contract_root: Path) -> tuple[list[str] | None, str]:
|
|
|
285
288
|
if not isinstance(raw, list):
|
|
286
289
|
continue
|
|
287
290
|
phases = [str(item).strip() for item in raw if str(item).strip()]
|
|
288
|
-
if (
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
291
|
+
if not phases or len(phases) != len(set(phases)):
|
|
292
|
+
continue
|
|
293
|
+
resolved = [hpaths.resolve_phase_name(item) for item in phases]
|
|
294
|
+
missing = [item for item, hit in zip(phases, resolved) if hit is None]
|
|
295
|
+
if missing:
|
|
296
|
+
unknown.extend(missing)
|
|
297
|
+
continue
|
|
298
|
+
# 映射后可能出现重复(两个旧名合并成同一个新阶段),保序去重。
|
|
299
|
+
return list(dict.fromkeys(str(item) for item in resolved)), source
|
|
300
|
+
if unknown:
|
|
301
|
+
return None, "legacy:unknown-phases=" + ",".join(dict.fromkeys(unknown))
|
|
294
302
|
return None, "legacy"
|
|
295
303
|
|
|
296
304
|
|
|
@@ -405,6 +413,7 @@ def _paths(state_root: Path) -> dict[str, Path]:
|
|
|
405
413
|
"begins": runtime / "transition-begins.ndjson",
|
|
406
414
|
"current": runtime / "current-context.json",
|
|
407
415
|
"lease": runtime / "context-lease.json",
|
|
416
|
+
"adoptions": runtime / "context-adoptions.ndjson",
|
|
408
417
|
}
|
|
409
418
|
|
|
410
419
|
|
|
@@ -604,8 +613,14 @@ def _claim_prepared_context(
|
|
|
604
613
|
)
|
|
605
614
|
except (OSError, ValueError, json.JSONDecodeError):
|
|
606
615
|
return {"ok": False, "code": "CONTEXT_CURRENT_INVALID"}
|
|
616
|
+
# 跨阶段推进必须有 receipt 证明它合法;同一个阶段换执行者不需要。
|
|
617
|
+
# 以前两者都要 receipt,于是上一阶段崩溃没能正常 close 时,接手的工具
|
|
618
|
+
# 连"继续把这个阶段干完"都做不到。并发保护不受影响:租约未过期而
|
|
619
|
+
# owner 不同,上面 CONTEXT_LEASE_HELD 已经拦下了,能走到这里只剩
|
|
620
|
+
# 「租约已过期」和「租约已被上一阶段 close 删除」两种情况。
|
|
621
|
+
adoption: dict[str, Any] | None = None
|
|
607
622
|
if recovery is None and isinstance(current, dict) and (
|
|
608
|
-
current.get("
|
|
623
|
+
current.get("phase") != phase
|
|
609
624
|
):
|
|
610
625
|
valid_receipt = (
|
|
611
626
|
isinstance(latest, dict)
|
|
@@ -621,6 +636,19 @@ def _claim_prepared_context(
|
|
|
621
636
|
"toExecutor": executor,
|
|
622
637
|
"toPhase": phase,
|
|
623
638
|
}
|
|
639
|
+
executor_changed = (
|
|
640
|
+
isinstance(current, dict) and current.get("executor") != executor
|
|
641
|
+
)
|
|
642
|
+
if recovery is not None or executor_changed:
|
|
643
|
+
adoption = {
|
|
644
|
+
"previousExecutor": (
|
|
645
|
+
current.get("executor") if isinstance(current, dict) else None
|
|
646
|
+
),
|
|
647
|
+
"newExecutor": executor,
|
|
648
|
+
"reason": (
|
|
649
|
+
"lease_expired" if recovery is not None else "same_phase_handoff"
|
|
650
|
+
),
|
|
651
|
+
}
|
|
624
652
|
|
|
625
653
|
try:
|
|
626
654
|
execution_root = _execution_root(project, contract_root, state_root)
|
|
@@ -657,6 +685,20 @@ def _claim_prepared_context(
|
|
|
657
685
|
current_context["preparationId"] = preparation_id
|
|
658
686
|
_write_json_atomic(paths["lease"], lease)
|
|
659
687
|
_write_json_atomic(paths["current"], current_context)
|
|
688
|
+
if adoption is not None:
|
|
689
|
+
# 接管必须留痕。过期租约本来就允许任何执行者顶替,但以前只在返回体
|
|
690
|
+
# 里挂一个 recovery,退出进程就没了——事后查"这个阶段是谁接手的"
|
|
691
|
+
# 无据可依。收据形状照 harness_archive.adopt_existing_range。
|
|
692
|
+
adoption = {
|
|
693
|
+
"schemaVersion": 1,
|
|
694
|
+
"changeName": change,
|
|
695
|
+
"phase": phase,
|
|
696
|
+
**adoption,
|
|
697
|
+
"previousOwner": recovery.get("previousOwner") if recovery else None,
|
|
698
|
+
"expiredAt": recovery.get("expiredAt") if recovery else None,
|
|
699
|
+
"adoptedAt": now.isoformat(),
|
|
700
|
+
}
|
|
701
|
+
_append_ndjson(paths["adoptions"], adoption)
|
|
660
702
|
return {
|
|
661
703
|
"ok": True,
|
|
662
704
|
"lease": lease,
|
|
@@ -664,6 +706,7 @@ def _claim_prepared_context(
|
|
|
664
706
|
"transitions": transitions,
|
|
665
707
|
"latestTransition": latest,
|
|
666
708
|
"recovery": recovery,
|
|
709
|
+
"adoption": adoption,
|
|
667
710
|
"executionRoot": str(execution_root),
|
|
668
711
|
}
|
|
669
712
|
|
|
@@ -405,6 +405,10 @@ def _default_gate_begin(**kwargs: Any) -> dict[str, Any]:
|
|
|
405
405
|
str(kwargs["run_id"]),
|
|
406
406
|
"--note",
|
|
407
407
|
"开始处理评审中确认需要修改的代码问题。",
|
|
408
|
+
# 显式标记这一轮是 review fixback。以前靠 gate 嗅探 note 里有没有
|
|
409
|
+
# "fixback" 字样——而上面这条 note 一个都没有,于是 fixback 的 run 事件
|
|
410
|
+
# 从来没被打上 trigger/from_phase。
|
|
411
|
+
"--fixback",
|
|
408
412
|
]
|
|
409
413
|
for option, key in (
|
|
410
414
|
("--executor-tool", "executor_tool"),
|