@hunter-harness/workflow-harness 0.2.54 → 0.2.56
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/README.md +3 -3
- package/harness/bundles/general/claude-code/contracts/workflow-policy.json +1 -1
- package/harness/bundles/general/claude-code/harness-archive/SKILL.md +21 -33
- package/harness/bundles/general/claude-code/harness-archive/checklist.md +6 -10
- 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 +7 -3
- package/harness/bundles/general/claude-code/harness-plan/checklist.md +2 -1
- package/harness/bundles/general/claude-code/harness-review/SKILL.md +8 -4
- package/harness/bundles/general/claude-code/harness-run/SKILL.md +9 -7
- package/harness/bundles/general/claude-code/harness-run/checklist.md +1 -1
- package/harness/bundles/general/claude-code/harness-run/reference.md +3 -3
- package/harness/bundles/general/claude-code/harness-submit/SKILL.md +6 -6
- package/harness/bundles/general/claude-code/harness-submit/checklist.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 +8 -6
- package/harness/bundles/general/claude-code/harness-test/checklist.md +1 -1
- package/harness/bundles/general/claude-code/harness-test/reference.md +2 -2
- package/harness/bundles/general/claude-code/protocols/ledger-protocol.md +3 -3
- package/harness/bundles/general/claude-code/scripts/harness_archive.py +322 -74
- package/harness/bundles/general/claude-code/scripts/harness_context.py +236 -63
- package/harness/bundles/general/claude-code/scripts/harness_events.py +86 -5
- package/harness/bundles/general/claude-code/scripts/harness_events_sync.py +201 -17
- package/harness/bundles/general/claude-code/scripts/harness_fixback.py +189 -1
- package/harness/bundles/general/claude-code/scripts/harness_gate.py +353 -3
- package/harness/bundles/general/claude-code/scripts/harness_ledger.py +195 -11
- package/harness/bundles/general/claude-code/scripts/harness_plan_finalize.py +82 -0
- package/harness/bundles/general/claude-code/scripts/harness_test_guard.py +37 -44
- package/harness/bundles/general/codebuddy/.harness-build.json +1 -1
- package/harness/bundles/general/codebuddy/README.md +3 -3
- package/harness/bundles/general/codebuddy/contracts/workflow-policy.json +1 -1
- package/harness/bundles/general/codebuddy/harness-archive/SKILL.md +21 -33
- package/harness/bundles/general/codebuddy/harness-archive/checklist.md +6 -10
- 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 +7 -3
- package/harness/bundles/general/codebuddy/harness-plan/checklist.md +2 -1
- package/harness/bundles/general/codebuddy/harness-review/SKILL.md +8 -4
- package/harness/bundles/general/codebuddy/harness-run/SKILL.md +9 -7
- package/harness/bundles/general/codebuddy/harness-run/checklist.md +1 -1
- package/harness/bundles/general/codebuddy/harness-run/reference.md +3 -3
- package/harness/bundles/general/codebuddy/harness-submit/SKILL.md +6 -6
- package/harness/bundles/general/codebuddy/harness-submit/checklist.md +1 -1
- package/harness/bundles/general/codebuddy/harness-sync/SKILL.md +1 -1
- package/harness/bundles/general/codebuddy/harness-test/SKILL.md +8 -6
- package/harness/bundles/general/codebuddy/harness-test/checklist.md +1 -1
- package/harness/bundles/general/codebuddy/harness-test/reference.md +2 -2
- package/harness/bundles/general/codebuddy/protocols/ledger-protocol.md +3 -3
- package/harness/bundles/general/codebuddy/scripts/harness_archive.py +322 -74
- package/harness/bundles/general/codebuddy/scripts/harness_context.py +236 -63
- package/harness/bundles/general/codebuddy/scripts/harness_events.py +86 -5
- package/harness/bundles/general/codebuddy/scripts/harness_events_sync.py +201 -17
- package/harness/bundles/general/codebuddy/scripts/harness_fixback.py +189 -1
- package/harness/bundles/general/codebuddy/scripts/harness_gate.py +353 -3
- package/harness/bundles/general/codebuddy/scripts/harness_ledger.py +195 -11
- package/harness/bundles/general/codebuddy/scripts/harness_plan_finalize.py +82 -0
- package/harness/bundles/general/codebuddy/scripts/harness_test_guard.py +37 -44
- package/harness/bundles/general/codex/.harness-build.json +1 -1
- package/harness/bundles/general/codex/README.md +3 -3
- package/harness/bundles/general/codex/contracts/workflow-policy.json +1 -1
- package/harness/bundles/general/codex/harness-archive/SKILL.md +21 -33
- package/harness/bundles/general/codex/harness-archive/checklist.md +6 -10
- 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 +7 -3
- package/harness/bundles/general/codex/harness-plan/checklist.md +2 -1
- package/harness/bundles/general/codex/harness-review/SKILL.md +4 -4
- package/harness/bundles/general/codex/harness-run/SKILL.md +9 -7
- package/harness/bundles/general/codex/harness-run/checklist.md +1 -1
- package/harness/bundles/general/codex/harness-run/reference.md +3 -3
- package/harness/bundles/general/codex/harness-submit/SKILL.md +6 -6
- package/harness/bundles/general/codex/harness-submit/checklist.md +1 -1
- package/harness/bundles/general/codex/harness-sync/SKILL.md +1 -1
- package/harness/bundles/general/codex/harness-test/SKILL.md +8 -6
- package/harness/bundles/general/codex/harness-test/checklist.md +1 -1
- package/harness/bundles/general/codex/harness-test/reference.md +2 -2
- package/harness/bundles/general/codex/protocols/ledger-protocol.md +3 -3
- package/harness/bundles/general/codex/scripts/harness_archive.py +322 -74
- package/harness/bundles/general/codex/scripts/harness_context.py +236 -63
- package/harness/bundles/general/codex/scripts/harness_events.py +86 -5
- package/harness/bundles/general/codex/scripts/harness_events_sync.py +201 -17
- package/harness/bundles/general/codex/scripts/harness_fixback.py +189 -1
- package/harness/bundles/general/codex/scripts/harness_gate.py +353 -3
- package/harness/bundles/general/codex/scripts/harness_ledger.py +195 -11
- package/harness/bundles/general/codex/scripts/harness_plan_finalize.py +82 -0
- package/harness/bundles/general/codex/scripts/harness_test_guard.py +37 -44
- package/harness/bundles/general/cursor/.harness-build.json +1 -1
- package/harness/bundles/general/cursor/README.md +3 -3
- package/harness/bundles/general/cursor/contracts/workflow-policy.json +1 -1
- package/harness/bundles/general/cursor/harness-archive/SKILL.md +21 -33
- package/harness/bundles/general/cursor/harness-archive/checklist.md +6 -10
- 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 +7 -3
- package/harness/bundles/general/cursor/harness-plan/checklist.md +2 -1
- package/harness/bundles/general/cursor/harness-review/SKILL.md +4 -4
- package/harness/bundles/general/cursor/harness-run/SKILL.md +9 -7
- package/harness/bundles/general/cursor/harness-run/checklist.md +1 -1
- package/harness/bundles/general/cursor/harness-run/reference.md +3 -3
- package/harness/bundles/general/cursor/harness-submit/SKILL.md +6 -6
- package/harness/bundles/general/cursor/harness-submit/checklist.md +1 -1
- package/harness/bundles/general/cursor/harness-sync/SKILL.md +1 -1
- package/harness/bundles/general/cursor/harness-test/SKILL.md +8 -6
- package/harness/bundles/general/cursor/harness-test/checklist.md +1 -1
- package/harness/bundles/general/cursor/harness-test/reference.md +2 -2
- package/harness/bundles/general/cursor/protocols/ledger-protocol.md +3 -3
- package/harness/bundles/general/cursor/scripts/harness_archive.py +322 -74
- package/harness/bundles/general/cursor/scripts/harness_context.py +236 -63
- package/harness/bundles/general/cursor/scripts/harness_events.py +86 -5
- package/harness/bundles/general/cursor/scripts/harness_events_sync.py +201 -17
- package/harness/bundles/general/cursor/scripts/harness_fixback.py +189 -1
- package/harness/bundles/general/cursor/scripts/harness_gate.py +353 -3
- package/harness/bundles/general/cursor/scripts/harness_ledger.py +195 -11
- package/harness/bundles/general/cursor/scripts/harness_plan_finalize.py +82 -0
- package/harness/bundles/general/cursor/scripts/harness_test_guard.py +37 -44
- package/harness/bundles/java/claude-code/.harness-build.json +1 -1
- package/harness/bundles/java/claude-code/README.md +3 -3
- package/harness/bundles/java/claude-code/contracts/workflow-policy.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 +21 -33
- package/harness/bundles/java/claude-code/harness-archive/checklist.md +6 -10
- 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-package/checklist.md +1 -1
- package/harness/bundles/java/claude-code/harness-plan/SKILL.md +7 -3
- package/harness/bundles/java/claude-code/harness-plan/checklist.md +2 -1
- package/harness/bundles/java/claude-code/harness-review/SKILL.md +8 -4
- package/harness/bundles/java/claude-code/harness-run/SKILL.md +9 -7
- package/harness/bundles/java/claude-code/harness-run/checklist.md +1 -1
- package/harness/bundles/java/claude-code/harness-run/reference.md +2 -2
- package/harness/bundles/java/claude-code/harness-submit/SKILL.md +6 -6
- package/harness/bundles/java/claude-code/harness-submit/checklist.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 +8 -6
- package/harness/bundles/java/claude-code/harness-test/checklist.md +1 -1
- package/harness/bundles/java/claude-code/harness-test/reference.md +1 -1
- package/harness/bundles/java/claude-code/protocols/ledger-protocol.md +3 -3
- package/harness/bundles/java/claude-code/scripts/harness_archive.py +322 -74
- package/harness/bundles/java/claude-code/scripts/harness_context.py +236 -63
- package/harness/bundles/java/claude-code/scripts/harness_events.py +86 -5
- package/harness/bundles/java/claude-code/scripts/harness_events_sync.py +201 -17
- package/harness/bundles/java/claude-code/scripts/harness_fixback.py +189 -1
- package/harness/bundles/java/claude-code/scripts/harness_gate.py +353 -3
- package/harness/bundles/java/claude-code/scripts/harness_ledger.py +195 -11
- package/harness/bundles/java/claude-code/scripts/harness_plan_finalize.py +82 -0
- package/harness/bundles/java/claude-code/scripts/harness_test_guard.py +37 -44
- package/harness/bundles/java/codebuddy/.harness-build.json +1 -1
- package/harness/bundles/java/codebuddy/README.md +3 -3
- package/harness/bundles/java/codebuddy/contracts/workflow-policy.json +1 -1
- package/harness/bundles/java/codebuddy/harness-apidoc/SKILL.md +1 -1
- package/harness/bundles/java/codebuddy/harness-archive/SKILL.md +21 -33
- package/harness/bundles/java/codebuddy/harness-archive/checklist.md +6 -10
- 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-package/checklist.md +1 -1
- package/harness/bundles/java/codebuddy/harness-plan/SKILL.md +7 -3
- package/harness/bundles/java/codebuddy/harness-plan/checklist.md +2 -1
- package/harness/bundles/java/codebuddy/harness-review/SKILL.md +8 -4
- package/harness/bundles/java/codebuddy/harness-run/SKILL.md +9 -7
- package/harness/bundles/java/codebuddy/harness-run/checklist.md +1 -1
- package/harness/bundles/java/codebuddy/harness-run/reference.md +2 -2
- package/harness/bundles/java/codebuddy/harness-submit/SKILL.md +6 -6
- package/harness/bundles/java/codebuddy/harness-submit/checklist.md +1 -1
- package/harness/bundles/java/codebuddy/harness-sync/SKILL.md +1 -1
- package/harness/bundles/java/codebuddy/harness-test/SKILL.md +8 -6
- package/harness/bundles/java/codebuddy/harness-test/checklist.md +1 -1
- package/harness/bundles/java/codebuddy/harness-test/reference.md +1 -1
- package/harness/bundles/java/codebuddy/protocols/ledger-protocol.md +3 -3
- package/harness/bundles/java/codebuddy/scripts/harness_archive.py +322 -74
- package/harness/bundles/java/codebuddy/scripts/harness_context.py +236 -63
- package/harness/bundles/java/codebuddy/scripts/harness_events.py +86 -5
- package/harness/bundles/java/codebuddy/scripts/harness_events_sync.py +201 -17
- package/harness/bundles/java/codebuddy/scripts/harness_fixback.py +189 -1
- package/harness/bundles/java/codebuddy/scripts/harness_gate.py +353 -3
- package/harness/bundles/java/codebuddy/scripts/harness_ledger.py +195 -11
- package/harness/bundles/java/codebuddy/scripts/harness_plan_finalize.py +82 -0
- package/harness/bundles/java/codebuddy/scripts/harness_test_guard.py +37 -44
- package/harness/bundles/java/codex/.harness-build.json +1 -1
- package/harness/bundles/java/codex/README.md +3 -3
- package/harness/bundles/java/codex/contracts/workflow-policy.json +1 -1
- package/harness/bundles/java/codex/harness-apidoc/SKILL.md +1 -1
- package/harness/bundles/java/codex/harness-archive/SKILL.md +21 -33
- package/harness/bundles/java/codex/harness-archive/checklist.md +6 -10
- 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-package/checklist.md +1 -1
- package/harness/bundles/java/codex/harness-plan/SKILL.md +7 -3
- package/harness/bundles/java/codex/harness-plan/checklist.md +2 -1
- package/harness/bundles/java/codex/harness-review/SKILL.md +4 -4
- package/harness/bundles/java/codex/harness-run/SKILL.md +9 -7
- package/harness/bundles/java/codex/harness-run/checklist.md +1 -1
- package/harness/bundles/java/codex/harness-run/reference.md +2 -2
- package/harness/bundles/java/codex/harness-submit/SKILL.md +6 -6
- package/harness/bundles/java/codex/harness-submit/checklist.md +1 -1
- package/harness/bundles/java/codex/harness-sync/SKILL.md +1 -1
- package/harness/bundles/java/codex/harness-test/SKILL.md +8 -6
- package/harness/bundles/java/codex/harness-test/checklist.md +1 -1
- package/harness/bundles/java/codex/harness-test/reference.md +1 -1
- package/harness/bundles/java/codex/protocols/ledger-protocol.md +3 -3
- package/harness/bundles/java/codex/scripts/harness_archive.py +322 -74
- package/harness/bundles/java/codex/scripts/harness_context.py +236 -63
- package/harness/bundles/java/codex/scripts/harness_events.py +86 -5
- package/harness/bundles/java/codex/scripts/harness_events_sync.py +201 -17
- package/harness/bundles/java/codex/scripts/harness_fixback.py +189 -1
- package/harness/bundles/java/codex/scripts/harness_gate.py +353 -3
- package/harness/bundles/java/codex/scripts/harness_ledger.py +195 -11
- package/harness/bundles/java/codex/scripts/harness_plan_finalize.py +82 -0
- package/harness/bundles/java/codex/scripts/harness_test_guard.py +37 -44
- package/harness/bundles/java/cursor/.harness-build.json +1 -1
- package/harness/bundles/java/cursor/README.md +3 -3
- package/harness/bundles/java/cursor/contracts/workflow-policy.json +1 -1
- package/harness/bundles/java/cursor/harness-apidoc/SKILL.md +1 -1
- package/harness/bundles/java/cursor/harness-archive/SKILL.md +21 -33
- package/harness/bundles/java/cursor/harness-archive/checklist.md +6 -10
- 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-package/checklist.md +1 -1
- package/harness/bundles/java/cursor/harness-plan/SKILL.md +7 -3
- package/harness/bundles/java/cursor/harness-plan/checklist.md +2 -1
- package/harness/bundles/java/cursor/harness-review/SKILL.md +4 -4
- package/harness/bundles/java/cursor/harness-run/SKILL.md +9 -7
- package/harness/bundles/java/cursor/harness-run/checklist.md +1 -1
- package/harness/bundles/java/cursor/harness-run/reference.md +2 -2
- package/harness/bundles/java/cursor/harness-submit/SKILL.md +6 -6
- package/harness/bundles/java/cursor/harness-submit/checklist.md +1 -1
- package/harness/bundles/java/cursor/harness-sync/SKILL.md +1 -1
- package/harness/bundles/java/cursor/harness-test/SKILL.md +8 -6
- package/harness/bundles/java/cursor/harness-test/checklist.md +1 -1
- package/harness/bundles/java/cursor/harness-test/reference.md +1 -1
- package/harness/bundles/java/cursor/protocols/ledger-protocol.md +3 -3
- package/harness/bundles/java/cursor/scripts/harness_archive.py +322 -74
- package/harness/bundles/java/cursor/scripts/harness_context.py +236 -63
- package/harness/bundles/java/cursor/scripts/harness_events.py +86 -5
- package/harness/bundles/java/cursor/scripts/harness_events_sync.py +201 -17
- package/harness/bundles/java/cursor/scripts/harness_fixback.py +189 -1
- package/harness/bundles/java/cursor/scripts/harness_gate.py +353 -3
- package/harness/bundles/java/cursor/scripts/harness_ledger.py +195 -11
- package/harness/bundles/java/cursor/scripts/harness_plan_finalize.py +82 -0
- package/harness/bundles/java/cursor/scripts/harness_test_guard.py +37 -44
- package/harness/manifests/general/claude-code.json +32 -32
- package/harness/manifests/general/codebuddy.json +32 -32
- package/harness/manifests/general/codex.json +32 -32
- package/harness/manifests/general/cursor.json +32 -32
- package/harness/manifests/java/claude-code.json +35 -35
- package/harness/manifests/java/codebuddy.json +35 -35
- package/harness/manifests/java/codex.json +35 -35
- package/harness/manifests/java/cursor.json +35 -35
- package/hunter-workflow-family.json +5 -4
- package/package.json +1 -1
|
@@ -51,6 +51,21 @@ WHITELIST = {
|
|
|
51
51
|
"duration_ms",
|
|
52
52
|
"schemaVersion",
|
|
53
53
|
"summary",
|
|
54
|
+
"executor_tool",
|
|
55
|
+
"executor_agent",
|
|
56
|
+
"executor_model",
|
|
57
|
+
"execution_mode",
|
|
58
|
+
"decision_reason_code",
|
|
59
|
+
"fallback_reason_code",
|
|
60
|
+
"trigger",
|
|
61
|
+
"from_phase",
|
|
62
|
+
"result_status",
|
|
63
|
+
"planned_phases",
|
|
64
|
+
"skipped_phases",
|
|
65
|
+
"next_phase",
|
|
66
|
+
"phase_plan_source",
|
|
67
|
+
"closure_disposition",
|
|
68
|
+
"closure_reason",
|
|
54
69
|
}
|
|
55
70
|
|
|
56
71
|
MAX_BATCH_SIZE = 100
|
|
@@ -70,6 +85,14 @@ PHASE_LABELS = {
|
|
|
70
85
|
"archive": "归档",
|
|
71
86
|
}
|
|
72
87
|
|
|
88
|
+
REVIEW_REASON_SUMMARIES = {
|
|
89
|
+
"REVIEW_DELEGATED": "已使用独立评审,主会话负责核验结果。",
|
|
90
|
+
"REVIEW_INLINE_UNAVAILABLE": "当前环境没有可用的隔离评审能力,已由主会话完成评审。",
|
|
91
|
+
"REVIEW_INLINE_SPAWN_FAILED": "独立评审启动失败,已由主会话完成评审。",
|
|
92
|
+
"REVIEW_INLINE_INVALID_RESULT": "独立评审未返回有效结果,已由主会话重新完成评审。",
|
|
93
|
+
"INLINE_BY_ADAPTER": "当前适配器未提供隔离评审,已由主会话完成评审。",
|
|
94
|
+
}
|
|
95
|
+
|
|
73
96
|
|
|
74
97
|
def _yaml_scalar(text: str, key: str) -> str | None:
|
|
75
98
|
match = re.search(rf"(?m)^[ \t]*{re.escape(key)}:\s*['\"]?([^'\"\n#]+)", text)
|
|
@@ -110,20 +133,56 @@ def _summary_text(value: Any) -> str:
|
|
|
110
133
|
return text[:EVENT_SUMMARY_MAX_LENGTH].rstrip()
|
|
111
134
|
|
|
112
135
|
|
|
136
|
+
_MACHINE_DETAIL_RE = re.compile(
|
|
137
|
+
r"(?:\s*[;;,,]?\s*)"
|
|
138
|
+
r"(?:artifactsHash|artifactHash|receiptHash|content_sha256|contentSha256)"
|
|
139
|
+
r"\s*[:=]\s*(?:sha256:)?[0-9a-f]{32,64}",
|
|
140
|
+
re.IGNORECASE,
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
def _readable_summary_text(value: Any) -> str:
|
|
145
|
+
text = _summary_text(value)
|
|
146
|
+
if not text:
|
|
147
|
+
return ""
|
|
148
|
+
text = _MACHINE_DETAIL_RE.sub("", text)
|
|
149
|
+
text = re.sub(
|
|
150
|
+
r"\bfinalize\s+ok\b",
|
|
151
|
+
"规划产物已校验并发布",
|
|
152
|
+
text,
|
|
153
|
+
flags=re.IGNORECASE,
|
|
154
|
+
)
|
|
155
|
+
text = re.sub(r"\s*([;;,,])\s*([;;,,])", r"\1", text)
|
|
156
|
+
return _summary_text(text.strip(" ;;,,"))
|
|
157
|
+
|
|
158
|
+
|
|
113
159
|
def build_event_summary(event: dict[str, Any]) -> str:
|
|
114
|
-
|
|
115
|
-
|
|
160
|
+
reason_code = _summary_text(
|
|
161
|
+
event.get("fallback_reason_code") or event.get("decision_reason_code")
|
|
162
|
+
)
|
|
163
|
+
reason_summary = REVIEW_REASON_SUMMARIES.get(reason_code)
|
|
164
|
+
explicit = _readable_summary_text(event.get("summary"))
|
|
165
|
+
if reason_summary and (
|
|
166
|
+
not explicit
|
|
167
|
+
or explicit == reason_code
|
|
168
|
+
or re.search(r"REVIEW_[A-Z0-9_]+|INLINE_BY_ADAPTER", explicit)
|
|
169
|
+
):
|
|
170
|
+
return reason_summary
|
|
171
|
+
if explicit and not re.search(
|
|
172
|
+
r"\b[A-Z][A-Z0-9_]{3,}\b|\b[a-z][\w.-]+\s*=\s*(?:true|false)\b",
|
|
173
|
+
explicit,
|
|
174
|
+
):
|
|
116
175
|
return explicit
|
|
117
176
|
|
|
118
177
|
event_type = _summary_text(event.get("type"))
|
|
119
178
|
phase = _summary_text(event.get("phase"))
|
|
120
179
|
phase_label = PHASE_LABELS.get(phase, phase or "当前")
|
|
121
|
-
note =
|
|
122
|
-
message =
|
|
123
|
-
decision =
|
|
124
|
-
reason =
|
|
125
|
-
name =
|
|
126
|
-
code =
|
|
180
|
+
note = _readable_summary_text(event.get("note"))
|
|
181
|
+
message = _readable_summary_text(event.get("message"))
|
|
182
|
+
decision = _readable_summary_text(event.get("decision"))
|
|
183
|
+
reason = _readable_summary_text(event.get("reason"))
|
|
184
|
+
name = _readable_summary_text(event.get("name"))
|
|
185
|
+
code = _readable_summary_text(event.get("code"))
|
|
127
186
|
status = _summary_text(event.get("status"))
|
|
128
187
|
|
|
129
188
|
if event_type == "decision" and decision:
|
|
@@ -145,6 +204,10 @@ def build_event_summary(event: dict[str, Any]) -> str:
|
|
|
145
204
|
return _summary_text(
|
|
146
205
|
f"{phase_label}阶段已结束:{detail}" if detail else f"{phase_label}阶段已结束。"
|
|
147
206
|
)
|
|
207
|
+
if event_type == "gate.blocked":
|
|
208
|
+
return note or message or "阶段门禁暂未通过,现场已保留,可按提示恢复。"
|
|
209
|
+
if event_type == "gate.recovered":
|
|
210
|
+
return note or message or "阶段门禁已恢复,流程可以继续。"
|
|
148
211
|
if event_type == "command":
|
|
149
212
|
detail = name or note
|
|
150
213
|
return _summary_text(f"执行步骤:{detail}" if detail else "执行了一项工作步骤。")
|
|
@@ -173,6 +236,48 @@ def sanitize(event: dict[str, Any]) -> dict[str, Any]:
|
|
|
173
236
|
return payload
|
|
174
237
|
|
|
175
238
|
|
|
239
|
+
def _workflow_plan(change_dir: Path) -> dict[str, Any] | None:
|
|
240
|
+
policy_path = change_dir / "meta" / "gate-policy.json"
|
|
241
|
+
if not policy_path.is_file():
|
|
242
|
+
return None
|
|
243
|
+
try:
|
|
244
|
+
policy = json.loads(policy_path.read_text(encoding="utf-8-sig"))
|
|
245
|
+
except (OSError, json.JSONDecodeError):
|
|
246
|
+
return None
|
|
247
|
+
if not isinstance(policy, dict):
|
|
248
|
+
return None
|
|
249
|
+
raw = policy.get("plannedPhases")
|
|
250
|
+
source = "change"
|
|
251
|
+
if not isinstance(raw, list) or not raw:
|
|
252
|
+
raw = policy.get("defaultPhases")
|
|
253
|
+
source = "policy-default"
|
|
254
|
+
if not isinstance(raw, list):
|
|
255
|
+
return None
|
|
256
|
+
phases = [str(item).strip() for item in raw if str(item).strip()]
|
|
257
|
+
if not phases:
|
|
258
|
+
return None
|
|
259
|
+
skipped = policy.get("skippedPhases")
|
|
260
|
+
return {
|
|
261
|
+
"planned_phases": list(dict.fromkeys(phases)),
|
|
262
|
+
"skipped_phases": skipped if isinstance(skipped, list) else [],
|
|
263
|
+
"phase_plan_source": source,
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
|
|
267
|
+
def _event_payload(event: dict[str, Any], phase_plan: dict[str, Any] | None) -> dict[str, Any]:
|
|
268
|
+
payload = sanitize(event)
|
|
269
|
+
if phase_plan is None:
|
|
270
|
+
return payload
|
|
271
|
+
payload.update(phase_plan)
|
|
272
|
+
if str(event.get("type") or "") in {"phase.end", "phase.auto_sealed"}:
|
|
273
|
+
phase = str(event.get("phase") or "").strip()
|
|
274
|
+
planned = phase_plan["planned_phases"]
|
|
275
|
+
if phase in planned:
|
|
276
|
+
index = planned.index(phase)
|
|
277
|
+
payload["next_phase"] = planned[index + 1] if index + 1 < len(planned) else None
|
|
278
|
+
return payload
|
|
279
|
+
|
|
280
|
+
|
|
176
281
|
def _valid_display_title(value: Any) -> str | None:
|
|
177
282
|
if not isinstance(value, str):
|
|
178
283
|
return None
|
|
@@ -437,6 +542,7 @@ def sync_change(
|
|
|
437
542
|
repair=True,
|
|
438
543
|
)
|
|
439
544
|
pending_raw = lines[acked:]
|
|
545
|
+
phase_plan = _workflow_plan(change_dir)
|
|
440
546
|
uploaded = 0
|
|
441
547
|
quarantined = 0
|
|
442
548
|
last_result: dict[str, Any] | None = None
|
|
@@ -468,7 +574,7 @@ def sync_change(
|
|
|
468
574
|
"producer_seq": line_number,
|
|
469
575
|
"event_type": str(event.get("type") or "unknown"),
|
|
470
576
|
"occurred_at": str(event.get("timestamp") or event.get("ts") or client_time),
|
|
471
|
-
"payload":
|
|
577
|
+
"payload": _event_payload(event, phase_plan),
|
|
472
578
|
}
|
|
473
579
|
if event.get("phase") is not None:
|
|
474
580
|
item["phase"] = event.get("phase")
|
|
@@ -695,6 +801,31 @@ class _CrossProcessLock:
|
|
|
695
801
|
self._stream = None
|
|
696
802
|
|
|
697
803
|
|
|
804
|
+
def _windowless_python_executable(
|
|
805
|
+
executable: str, base_executable: str | None
|
|
806
|
+
) -> str:
|
|
807
|
+
"""Return a Windows interpreter that cannot allocate a console window.
|
|
808
|
+
|
|
809
|
+
Virtual-environment ``python.exe`` files may be forwarding launchers. Even
|
|
810
|
+
when the first process is created with ``CREATE_NO_WINDOW``, such a launcher
|
|
811
|
+
can start the real console interpreter and make Windows Terminal flash. A
|
|
812
|
+
sibling ``pythonw.exe`` preserves the windowless subsystem across that hop.
|
|
813
|
+
"""
|
|
814
|
+
executable_path = Path(executable)
|
|
815
|
+
candidates = [executable_path.with_name("pythonw.exe")]
|
|
816
|
+
if base_executable:
|
|
817
|
+
base_path = Path(base_executable)
|
|
818
|
+
base_windowless = base_path.with_name("pythonw.exe")
|
|
819
|
+
if base_windowless not in candidates:
|
|
820
|
+
candidates.append(base_windowless)
|
|
821
|
+
for candidate in candidates:
|
|
822
|
+
if candidate.is_file():
|
|
823
|
+
return str(candidate)
|
|
824
|
+
if base_executable and Path(base_executable).is_file():
|
|
825
|
+
return str(Path(base_executable))
|
|
826
|
+
return str(executable_path)
|
|
827
|
+
|
|
828
|
+
|
|
698
829
|
def schedule_events_sync(project_root: Path, change_dir: Path) -> bool:
|
|
699
830
|
"""Wake a finite, project-local sync worker after an event is durable.
|
|
700
831
|
|
|
@@ -716,8 +847,14 @@ def schedule_events_sync(project_root: Path, change_dir: Path) -> bool:
|
|
|
716
847
|
temporary = nudge.with_name(f".{nudge.name}.{uuid.uuid4().hex}.tmp")
|
|
717
848
|
temporary.write_text(json.dumps(payload, separators=(",", ":")) + "\n", encoding="utf-8")
|
|
718
849
|
os.replace(temporary, nudge)
|
|
850
|
+
interpreter = sys.executable
|
|
851
|
+
if os.name == "nt":
|
|
852
|
+
interpreter = _windowless_python_executable(
|
|
853
|
+
sys.executable,
|
|
854
|
+
getattr(sys, "_base_executable", None),
|
|
855
|
+
)
|
|
719
856
|
command = [
|
|
720
|
-
|
|
857
|
+
interpreter,
|
|
721
858
|
str(Path(__file__).resolve()),
|
|
722
859
|
"--project",
|
|
723
860
|
str(project_root),
|
|
@@ -754,6 +891,39 @@ def _discover_changes(project_root: Path) -> list[Path]:
|
|
|
754
891
|
return sorted(path for path in root.glob("*") if path.is_dir()) if root.is_dir() else []
|
|
755
892
|
|
|
756
893
|
|
|
894
|
+
def _change_has_open_phase(change_dir: Path) -> bool:
|
|
895
|
+
"""Project the append-only stream into open/closed phase state."""
|
|
896
|
+
try:
|
|
897
|
+
path = Path(harness_paths.resolve_state_dir_for_contract(change_dir)) / "events.ndjson"
|
|
898
|
+
except (OSError, ValueError):
|
|
899
|
+
return False
|
|
900
|
+
if not path.is_file():
|
|
901
|
+
return False
|
|
902
|
+
open_phases: dict[str, bool] = {}
|
|
903
|
+
try:
|
|
904
|
+
for line in path.read_text(encoding="utf-8-sig").splitlines():
|
|
905
|
+
if not line.strip():
|
|
906
|
+
continue
|
|
907
|
+
event = json.loads(line)
|
|
908
|
+
if not isinstance(event, dict):
|
|
909
|
+
continue
|
|
910
|
+
phase = event.get("phase")
|
|
911
|
+
event_type = event.get("type")
|
|
912
|
+
if not isinstance(phase, str) or not phase:
|
|
913
|
+
continue
|
|
914
|
+
if event_type == "phase.start":
|
|
915
|
+
open_phases[phase] = True
|
|
916
|
+
elif event_type in {"phase.end", "phase.auto_sealed", "workflow.end", "run.end"}:
|
|
917
|
+
open_phases[phase] = False
|
|
918
|
+
except (OSError, json.JSONDecodeError):
|
|
919
|
+
return False
|
|
920
|
+
return any(open_phases.values())
|
|
921
|
+
|
|
922
|
+
|
|
923
|
+
def _project_has_open_phase(project_root: Path) -> bool:
|
|
924
|
+
return any(_change_has_open_phase(path) for path in _discover_changes(project_root))
|
|
925
|
+
|
|
926
|
+
|
|
757
927
|
def _run_agent_session(project_root: Path) -> dict[str, Any]:
|
|
758
928
|
attempts = 0
|
|
759
929
|
uploaded = 0
|
|
@@ -766,14 +936,28 @@ def _run_agent_session(project_root: Path) -> dict[str, Any]:
|
|
|
766
936
|
if all(item.get("ok") for item in results):
|
|
767
937
|
time.sleep(0.6)
|
|
768
938
|
if generation == _read_nudge_generation(project_root):
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
939
|
+
if not _project_has_open_phase(project_root):
|
|
940
|
+
return {
|
|
941
|
+
"ok": True,
|
|
942
|
+
"uploaded": uploaded,
|
|
943
|
+
"quarantined": quarantined,
|
|
944
|
+
"attempts": attempts + 1,
|
|
945
|
+
}
|
|
946
|
+
# Keep a finite workflow lease alive while a phase is open.
|
|
947
|
+
# A phase.end nudge is observed on the next pass and releases
|
|
948
|
+
# the worker naturally; no terminal window is created.
|
|
949
|
+
time.sleep(10.0)
|
|
950
|
+
heartbeat_results = [
|
|
951
|
+
sync_change(project_root, path, heartbeat_only=True)
|
|
952
|
+
for path in _discover_changes(project_root)
|
|
953
|
+
]
|
|
954
|
+
if all(item.get("ok") for item in heartbeat_results):
|
|
955
|
+
attempts = 0
|
|
956
|
+
continue
|
|
957
|
+
results = heartbeat_results
|
|
775
958
|
attempts = 0
|
|
776
|
-
|
|
959
|
+
if generation != _read_nudge_generation(project_root):
|
|
960
|
+
continue
|
|
777
961
|
time.sleep(min(16.0, float(2 ** attempts)))
|
|
778
962
|
attempts += 1
|
|
779
963
|
return {
|
|
@@ -11,6 +11,7 @@ import os
|
|
|
11
11
|
import re
|
|
12
12
|
import tempfile
|
|
13
13
|
import sys
|
|
14
|
+
import uuid
|
|
14
15
|
from pathlib import Path
|
|
15
16
|
from typing import Any
|
|
16
17
|
|
|
@@ -19,6 +20,7 @@ if str(SCRIPTS_DIR) not in sys.path:
|
|
|
19
20
|
sys.path.insert(0, str(SCRIPTS_DIR))
|
|
20
21
|
|
|
21
22
|
from harness_paths import resolve_state_dir_for_contract
|
|
23
|
+
import harness_events as he
|
|
22
24
|
|
|
23
25
|
|
|
24
26
|
SCHEMA_VERSION = 1
|
|
@@ -96,6 +98,171 @@ def _all_batches(change_dir: Path) -> list[dict[str, Any]]:
|
|
|
96
98
|
return batches
|
|
97
99
|
|
|
98
100
|
|
|
101
|
+
def _session_path(change_dir: Path) -> Path:
|
|
102
|
+
return _state_root(change_dir) / "runtime" / "fixback-session.json"
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def _next_step(batch: dict[str, Any]) -> str:
|
|
106
|
+
issues = [item for item in batch.get("issues", []) if isinstance(item, dict)]
|
|
107
|
+
if not issues:
|
|
108
|
+
return "add-issues"
|
|
109
|
+
if any(item.get("status") != "RESOLVED" for item in issues):
|
|
110
|
+
return "resolve-issues"
|
|
111
|
+
receipts = batch.get("receipts") if isinstance(batch.get("receipts"), dict) else {}
|
|
112
|
+
if not receipts.get("affected"):
|
|
113
|
+
return "verify-affected"
|
|
114
|
+
if not receipts.get("review"):
|
|
115
|
+
return "review"
|
|
116
|
+
return "close"
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def resume_batch(
|
|
120
|
+
change_dir: Path,
|
|
121
|
+
*,
|
|
122
|
+
batch_id: str | None,
|
|
123
|
+
product_identity: str | None,
|
|
124
|
+
root_cause: str | None,
|
|
125
|
+
run_id: str | None,
|
|
126
|
+
attempt: int | None,
|
|
127
|
+
) -> dict[str, Any]:
|
|
128
|
+
"""Open once or resume the active Fixback session with a stable run identity."""
|
|
129
|
+
opened = [item for item in _all_batches(change_dir) if item.get("status") == "OPEN"]
|
|
130
|
+
resumed = bool(opened)
|
|
131
|
+
if opened:
|
|
132
|
+
batch = sorted(opened, key=lambda item: str(item.get("openedAt") or ""))[0]
|
|
133
|
+
else:
|
|
134
|
+
if not batch_id or not product_identity or not root_cause:
|
|
135
|
+
raise ValueError(
|
|
136
|
+
"FIXBACK_RESUME_INPUT_REQUIRED: new sessions require batch-id, "
|
|
137
|
+
"product-identity and root-cause"
|
|
138
|
+
)
|
|
139
|
+
batch = open_batch(
|
|
140
|
+
change_dir,
|
|
141
|
+
batch_id=batch_id,
|
|
142
|
+
product_identity=product_identity,
|
|
143
|
+
root_cause=root_cause,
|
|
144
|
+
)
|
|
145
|
+
session_path = _session_path(change_dir)
|
|
146
|
+
existing: dict[str, Any] = {}
|
|
147
|
+
if session_path.is_file():
|
|
148
|
+
try:
|
|
149
|
+
loaded = json.loads(session_path.read_text(encoding="utf-8-sig"))
|
|
150
|
+
existing = loaded if isinstance(loaded, dict) else {}
|
|
151
|
+
except (OSError, json.JSONDecodeError):
|
|
152
|
+
existing = {}
|
|
153
|
+
same_open_session = (
|
|
154
|
+
existing.get("status") == "ACTIVE"
|
|
155
|
+
and existing.get("batchId") == batch.get("batchId")
|
|
156
|
+
)
|
|
157
|
+
effective_run_id = (
|
|
158
|
+
str(existing.get("runId"))
|
|
159
|
+
if same_open_session and existing.get("runId")
|
|
160
|
+
else str(run_id or "fixback-" + uuid.uuid4().hex)
|
|
161
|
+
)
|
|
162
|
+
effective_attempt = (
|
|
163
|
+
int(existing.get("attempt"))
|
|
164
|
+
if same_open_session and isinstance(existing.get("attempt"), int)
|
|
165
|
+
else max(1, int(attempt or 1))
|
|
166
|
+
)
|
|
167
|
+
next_step = _next_step(batch)
|
|
168
|
+
session = {
|
|
169
|
+
"schemaVersion": 1,
|
|
170
|
+
"status": "ACTIVE",
|
|
171
|
+
"batchId": batch.get("batchId"),
|
|
172
|
+
"runId": effective_run_id,
|
|
173
|
+
"attempt": effective_attempt,
|
|
174
|
+
"nextStep": next_step,
|
|
175
|
+
"updatedAt": now_iso(),
|
|
176
|
+
}
|
|
177
|
+
_write_json(session_path, session)
|
|
178
|
+
he.append_event(
|
|
179
|
+
change_dir,
|
|
180
|
+
phase="run",
|
|
181
|
+
type_="decision",
|
|
182
|
+
note=(
|
|
183
|
+
"已恢复现有修复批次,将从未完成步骤继续。"
|
|
184
|
+
if resumed
|
|
185
|
+
else "已创建修复批次,将按问题逐项执行 RED/GREEN。"
|
|
186
|
+
),
|
|
187
|
+
run_id=effective_run_id,
|
|
188
|
+
)
|
|
189
|
+
return {
|
|
190
|
+
"ok": True,
|
|
191
|
+
"code": "FIXBACK_RESUMED" if resumed else "FIXBACK_OPENED",
|
|
192
|
+
"resumed": resumed,
|
|
193
|
+
"batchId": batch.get("batchId"),
|
|
194
|
+
"runId": effective_run_id,
|
|
195
|
+
"attempt": effective_attempt,
|
|
196
|
+
"nextStep": next_step,
|
|
197
|
+
"requiredVerifications": batch.get("requiredVerifications", []),
|
|
198
|
+
"changedFiles": batch.get("changedFiles", []),
|
|
199
|
+
"sessionPath": str(session_path),
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
def invalidate_affected_evidence(
|
|
204
|
+
change_dir: Path,
|
|
205
|
+
*,
|
|
206
|
+
changed_files: list[str],
|
|
207
|
+
batch_id: str,
|
|
208
|
+
) -> dict[str, Any]:
|
|
209
|
+
"""Invalidate only verification targets whose declared inputs were changed."""
|
|
210
|
+
normalized = {
|
|
211
|
+
str(path).replace("\\", "/").lstrip("./")
|
|
212
|
+
for path in changed_files
|
|
213
|
+
if str(path).strip()
|
|
214
|
+
}
|
|
215
|
+
ledger_path = _state_root(change_dir) / "evidence" / "verification-ledger.json"
|
|
216
|
+
if not normalized or not ledger_path.is_file():
|
|
217
|
+
return {"ok": True, "targetIds": [], "validations": []}
|
|
218
|
+
try:
|
|
219
|
+
ledger = json.loads(ledger_path.read_text(encoding="utf-8-sig"))
|
|
220
|
+
except (OSError, json.JSONDecodeError):
|
|
221
|
+
return {"ok": False, "code": "FIXBACK_LEDGER_INVALID"}
|
|
222
|
+
if not isinstance(ledger, dict):
|
|
223
|
+
return {"ok": False, "code": "FIXBACK_LEDGER_INVALID"}
|
|
224
|
+
|
|
225
|
+
def affected(entry: dict[str, Any]) -> bool:
|
|
226
|
+
inputs = entry.get("inputsFiles")
|
|
227
|
+
if not isinstance(inputs, list):
|
|
228
|
+
return False
|
|
229
|
+
input_set = {
|
|
230
|
+
str(path).replace("\\", "/").lstrip("./")
|
|
231
|
+
for path in inputs
|
|
232
|
+
if str(path).strip()
|
|
233
|
+
}
|
|
234
|
+
return bool(normalized & input_set)
|
|
235
|
+
|
|
236
|
+
invalidation = {
|
|
237
|
+
"code": "FIXBACK_AFFECTED_INPUT_CHANGED",
|
|
238
|
+
"batchId": batch_id,
|
|
239
|
+
"changedFiles": sorted(normalized),
|
|
240
|
+
"invalidatedAt": now_iso(),
|
|
241
|
+
}
|
|
242
|
+
target_ids: list[str] = []
|
|
243
|
+
targets = ledger.get("verificationTargets")
|
|
244
|
+
if isinstance(targets, dict):
|
|
245
|
+
for target_id, target in targets.items():
|
|
246
|
+
if isinstance(target, dict) and affected(target):
|
|
247
|
+
target["reusable"] = False
|
|
248
|
+
target["invalidation"] = dict(invalidation)
|
|
249
|
+
target_ids.append(str(target_id))
|
|
250
|
+
validation_names: list[str] = []
|
|
251
|
+
validations = ledger.get("validations")
|
|
252
|
+
if isinstance(validations, dict):
|
|
253
|
+
for name, entry in validations.items():
|
|
254
|
+
if isinstance(entry, dict) and affected(entry):
|
|
255
|
+
entry["reusable"] = False
|
|
256
|
+
entry["invalidation"] = dict(invalidation)
|
|
257
|
+
validation_names.append(str(name))
|
|
258
|
+
_write_json(ledger_path, ledger)
|
|
259
|
+
return {
|
|
260
|
+
"ok": True,
|
|
261
|
+
"targetIds": sorted(target_ids),
|
|
262
|
+
"validations": sorted(validation_names),
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
|
|
99
266
|
def _required_verifications(issues: list[dict[str, Any]]) -> list[str]:
|
|
100
267
|
required = {"affected", "review"}
|
|
101
268
|
for issue in issues:
|
|
@@ -506,6 +673,11 @@ def resolve_issue(
|
|
|
506
673
|
}
|
|
507
674
|
)
|
|
508
675
|
batch["updatedAt"] = resolved_at
|
|
676
|
+
batch["invalidation"] = invalidate_affected_evidence(
|
|
677
|
+
change_dir,
|
|
678
|
+
changed_files=[record["path"] for record in changed_records],
|
|
679
|
+
batch_id=batch_id,
|
|
680
|
+
)
|
|
509
681
|
_write_json(_batch_path(change_dir, batch_id), batch)
|
|
510
682
|
return issue
|
|
511
683
|
|
|
@@ -652,6 +824,13 @@ def _emit(value: dict[str, Any]) -> int:
|
|
|
652
824
|
def build_parser() -> argparse.ArgumentParser:
|
|
653
825
|
parser = argparse.ArgumentParser(prog="harness_fixback.py")
|
|
654
826
|
sub = parser.add_subparsers(dest="command", required=True)
|
|
827
|
+
resume = sub.add_parser("resume")
|
|
828
|
+
resume.add_argument("--change-dir", required=True)
|
|
829
|
+
resume.add_argument("--batch-id")
|
|
830
|
+
resume.add_argument("--product-identity")
|
|
831
|
+
resume.add_argument("--root-cause")
|
|
832
|
+
resume.add_argument("--run-id")
|
|
833
|
+
resume.add_argument("--attempt", type=int)
|
|
655
834
|
opened = sub.add_parser("open")
|
|
656
835
|
opened.add_argument("--change-dir", required=True)
|
|
657
836
|
opened.add_argument("--batch-id", required=True)
|
|
@@ -692,7 +871,16 @@ def main(argv: list[str] | None = None) -> int:
|
|
|
692
871
|
args = build_parser().parse_args(argv)
|
|
693
872
|
change_dir = Path(args.change_dir)
|
|
694
873
|
try:
|
|
695
|
-
if args.command == "
|
|
874
|
+
if args.command == "resume":
|
|
875
|
+
value = resume_batch(
|
|
876
|
+
change_dir,
|
|
877
|
+
batch_id=args.batch_id,
|
|
878
|
+
product_identity=args.product_identity,
|
|
879
|
+
root_cause=args.root_cause,
|
|
880
|
+
run_id=args.run_id,
|
|
881
|
+
attempt=args.attempt,
|
|
882
|
+
)
|
|
883
|
+
elif args.command == "open":
|
|
696
884
|
value = open_batch(
|
|
697
885
|
change_dir,
|
|
698
886
|
batch_id=args.batch_id,
|