@guanlin4924/devflow-pi 0.1.105 → 0.1.107
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 +10 -8
- package/package.json +1 -1
- package/plugins/devflow-core/extensions/devflow-core.ts +287 -394
- package/plugins/devflow-core/runtime/hooks/context_sync_gate.py +3 -1
- package/plugins/devflow-core/runtime/hooks/hook_common.py +6 -5
- package/plugins/devflow-core/runtime/hooks/index_gate.py +19 -26
- package/plugins/devflow-core/skills/wireframe-proposal/SKILL.md +2 -0
- package/plugins/devflow-core/skills/wireframe-render/SKILL.md +2 -0
- package/plugins/devflow-core/skills/wireframe-review/SKILL.md +2 -0
- package/plugins/devflow-core/skills/wireframe-validate/SKILL.md +2 -0
- package/plugins/devflow-core/skills/workflow-dynamic/SKILL.md +41 -0
- package/plugins/devflow-core/skills/workflow-orchestrator/SKILL.md +37 -50
- package/plugins/devflow-core/skills/workflow-orchestrator/scripts/advance_state.rb +46 -191
- package/plugins/devflow-core/skills/workflow-orchestrator/scripts/artifact_specs.rb +12 -0
- package/plugins/devflow-core/skills/workflow-orchestrator/scripts/dynamic_plan.rb +504 -0
- package/plugins/devflow-core/skills/workflow-orchestrator/scripts/execution_contract.rb +38 -1
- package/plugins/devflow-core/skills/workflow-orchestrator/scripts/gate_policy.rb +11 -0
- package/plugins/devflow-core/skills/workflow-orchestrator/scripts/gate_result.rb +12 -0
- package/plugins/devflow-core/skills/workflow-orchestrator/scripts/gate_runtime.rb +4 -9
- package/plugins/devflow-core/skills/workflow-orchestrator/scripts/inspect_state.rb +19 -86
- package/plugins/devflow-core/skills/workflow-orchestrator/scripts/parallel_recover.rb +24 -13
- package/plugins/devflow-core/skills/workflow-orchestrator/scripts/repair_control_plane.rb +4 -4
- package/plugins/devflow-core/skills/workflow-orchestrator/scripts/sync_artifacts.rb +7 -1
- package/plugins/devflow-core/skills/workflow-orchestrator/scripts/validate_state.rb +17 -39
- package/plugins/devflow-core/skills/workflow-orchestrator/scripts/wireframe_commit.rb +39 -3
- package/plugins/devflow-core/skills/workflow-orchestrator/templates/feature-development.yaml +2 -3
- package/plugins/devflow-core/skills/workflow-orchestrator/templates/regression-acceptance.yaml +0 -1
- package/plugins/devflow-core/skills/workflow-pipeline/SKILL.md +13 -5
- package/plugins/devflow-core/skills/workflow-pipeline/references/pipeline-flow.md +1 -1
- package/plugins/devflow-core/skills/workflow-project-context/SKILL.md +2 -0
- package/plugins/devflow-design/skills/workflow-api-call-guide/SKILL.md +2 -0
- package/plugins/devflow-design/skills/workflow-api-contract/SKILL.md +3 -1
- package/plugins/devflow-design/skills/workflow-api-supplement/SKILL.md +2 -0
- package/plugins/devflow-design/skills/workflow-architecture-feasibility/SKILL.md +2 -0
- package/plugins/devflow-design/skills/workflow-backend-tech-spec/SKILL.md +6 -4
- package/plugins/devflow-design/skills/workflow-design-rule-pack/SKILL.md +2 -0
- package/plugins/devflow-design/skills/workflow-feature-breakdown/SKILL.md +2 -0
- package/plugins/devflow-design/skills/workflow-frontend-tech-spec/SKILL.md +6 -4
- package/plugins/devflow-design/skills/workflow-integration-verify/SKILL.md +3 -1
- package/plugins/devflow-design/skills/workflow-ui-spec/SKILL.md +4 -2
- package/plugins/devflow-implementation/agents/implementation-worker.md +2 -8
- package/plugins/devflow-implementation/skills/workflow-bug-fix/SKILL.md +5 -0
- package/plugins/devflow-implementation/skills/workflow-change-plan/SKILL.md +3 -1
- package/plugins/devflow-implementation/skills/workflow-execute-phase/SKILL.md +6 -1
- package/plugins/devflow-intel/skills/code-index-query/SKILL.md +2 -0
- package/plugins/devflow-intel/skills/workflow-code-index/SKILL.md +2 -0
- package/plugins/devflow-intel/skills/workflow-context-sync/SKILL.md +2 -0
- package/plugins/devflow-intel/skills/workflow-update-navigation/SKILL.md +3 -1
- package/plugins/devflow-quality/agents/code-reviewer.md +2 -0
- package/plugins/devflow-quality/skills/code-review/SKILL.md +7 -2
- package/plugins/devflow-quality/skills/workflow-acceptance-test-design/SKILL.md +3 -1
- package/plugins/devflow-quality/skills/workflow-gen-test-guide/SKILL.md +2 -0
- package/plugins/devflow-quality/skills/workflow-generate-mainflow-cases/SKILL.md +2 -0
- package/plugins/devflow-quality/skills/workflow-human-acceptance/SKILL.md +2 -0
- package/plugins/devflow-quality/skills/workflow-regression-test-design/SKILL.md +2 -0
- package/plugins/devflow-quality/skills/workflow-release-closeout/SKILL.md +7 -5
- package/plugins/devflow-quality/skills/workflow-run-acceptance-tests/SKILL.md +11 -6
- package/plugins/devflow-requirements/skills/requirement-feasibility-review/SKILL.md +2 -0
- package/plugins/devflow-requirements/skills/workflow-demand-clarify/SKILL.md +2 -0
- package/plugins/devflow-requirements/skills/workflow-domain-modeling/SKILL.md +2 -0
- package/plugins/devflow-requirements/skills/workflow-requirement-baseline/SKILL.md +6 -2
- package/plugins/devflow-requirements/skills/workflow-requirement-triage/SKILL.md +5 -0
- package/plugins/devflow-requirements/skills/workflow-wayfinder/SKILL.md +2 -0
- package/src/constants.ts +1 -1
- package/src/dynamic-workflow.ts +31 -0
- package/templates/omp/AGENTS.md +8 -3
|
@@ -9,10 +9,18 @@ require "open3"
|
|
|
9
9
|
require "optparse"
|
|
10
10
|
require "yaml"
|
|
11
11
|
require_relative "control_io"
|
|
12
|
+
require_relative "artifact_specs"
|
|
13
|
+
require_relative "gate_policy"
|
|
12
14
|
require_relative "validate_state"
|
|
13
15
|
require_relative "phase_ownership"
|
|
14
16
|
require_relative "requirement_graph"
|
|
15
17
|
require_relative "gate_runtime"
|
|
18
|
+
require_relative "dynamic_plan"
|
|
19
|
+
|
|
20
|
+
if DynamicPlan.read_state(Dir.pwd)&.dig("current_flow") == "dynamic"
|
|
21
|
+
puts JSON.generate({ "ok" => false, "reason" => "dynamic_plan_requires_evidence", "message" => "Use devflow_plan; legacy complete-step cannot complete dynamic tasks." })
|
|
22
|
+
exit 1
|
|
23
|
+
end
|
|
16
24
|
|
|
17
25
|
options = {
|
|
18
26
|
state: nil,
|
|
@@ -84,9 +92,7 @@ def gate_contract_enabled?(workflow)
|
|
|
84
92
|
workflow.dig("governance", "gate_contract_version").to_i >= 3
|
|
85
93
|
end
|
|
86
94
|
|
|
87
|
-
|
|
88
|
-
workflow.dig("governance", "hard_gate_mode").to_s.empty? ? "chain" : workflow.dig("governance", "hard_gate_mode").to_s
|
|
89
|
-
end
|
|
95
|
+
|
|
90
96
|
|
|
91
97
|
# Build the runtime view from the existing workflow declaration. This keeps
|
|
92
98
|
# governance.step_checkers as the single checker namespace and avoids creating
|
|
@@ -125,7 +131,6 @@ def transition_gate_contract(workflow, step)
|
|
|
125
131
|
"required_inputs" => Array(step["inputs"]).select { |input| input.is_a?(Hash) && input["required_at_completion"] == true }
|
|
126
132
|
},
|
|
127
133
|
"quality" => { "checker_ids" => Array(workflow.dig("governance", "step_checkers", step["id"])) },
|
|
128
|
-
"hard_gate_mode" => core_hard_gate_mode(workflow),
|
|
129
134
|
"concurrency" => { "project_lock" => "required", "optimistic_revision" => "required", "registry_view" => "candidate_overlay" },
|
|
130
135
|
"recovery" => { "allowed" => ["sync_artifacts", "repair_outputs"], "max_attempts" => 2 }
|
|
131
136
|
}
|
|
@@ -419,19 +424,7 @@ def track_allowed_steps(track)
|
|
|
419
424
|
end
|
|
420
425
|
|
|
421
426
|
def next_pending_step_id(state, pending_ids, step_by_id, completed_ids)
|
|
422
|
-
|
|
423
|
-
unless track
|
|
424
|
-
return pending_ids.find do |step_id|
|
|
425
|
-
step_by_id[step_id] && dependencies_satisfied?(step_by_id[step_id], completed_ids)
|
|
426
|
-
end
|
|
427
|
-
end
|
|
428
|
-
|
|
429
|
-
allowed = track_allowed_steps(track)
|
|
430
|
-
pending_ids.find do |step_id|
|
|
431
|
-
allowed.include?(step_id) &&
|
|
432
|
-
step_by_id[step_id] &&
|
|
433
|
-
dependencies_satisfied?(step_by_id[step_id], completed_ids)
|
|
434
|
-
end
|
|
427
|
+
pending_ids.find { |id| step_by_id[id] && dependencies_satisfied?(step_by_id[id], completed_ids) } || pending_ids.find { |id| step_by_id[id] }
|
|
435
428
|
end
|
|
436
429
|
|
|
437
430
|
def conditional_skip_policies(workflow, checkpoint_id)
|
|
@@ -535,7 +528,11 @@ def join_parallel_window(root, state, workflow, current_revision)
|
|
|
535
528
|
raise ArgumentError, "parallel run not found" unless record
|
|
536
529
|
branches = Array(record["branches"])
|
|
537
530
|
incomplete = branches.reject { |branch| branch["status"].to_s == "succeeded" }
|
|
538
|
-
raise ArgumentError, "parallel
|
|
531
|
+
raise ArgumentError, "parallel run has no branches" if branches.empty?
|
|
532
|
+
if incomplete.any?
|
|
533
|
+
$devflow_advisories << { "reason" => "parallel_branches_not_succeeded", "branches" => incomplete.map { |branch| branch.slice("branch_id", "status") } }
|
|
534
|
+
record["completion_with_warnings"] = true
|
|
535
|
+
end
|
|
539
536
|
|
|
540
537
|
branch_steps = branches.map { |branch| branch.fetch("workflow_step").to_s }
|
|
541
538
|
state["completed_steps"] = (Array(state["completed_steps"]) + branch_steps).uniq
|
|
@@ -597,45 +594,19 @@ end
|
|
|
597
594
|
# a small safety floor for malformed control-plane state and unsafe writes, but
|
|
598
595
|
# Core-generated test/traceability/merge findings no longer strand the serial
|
|
599
596
|
# workflow before the AI review can decide what to do.
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
gate_target_step_unknown
|
|
608
|
-
gate_current_step_mismatch
|
|
609
|
-
parallel_transition_requires_write
|
|
610
|
-
feature_not_active
|
|
611
|
-
cancel_reason_required
|
|
612
|
-
session_rollover_in_progress
|
|
613
|
-
failure_branch_incomplete
|
|
614
|
-
parallel_machine_evidence_failed
|
|
615
|
-
parallel_join_failed
|
|
616
|
-
parallel_branch_transition_failed
|
|
617
|
-
conditional_step_skip_failed
|
|
618
|
-
cross_track_consistency_failed
|
|
619
|
-
business_cross_model_check_failed
|
|
620
|
-
gate_boundary_rejected
|
|
621
|
-
acceptance_results_not_passed
|
|
622
|
-
execution_surface_mismatch
|
|
623
|
-
approval_invalid
|
|
624
|
-
human_result_unapproved
|
|
625
|
-
dsl_not_approved
|
|
626
|
-
environment_profile_not_approved
|
|
627
|
-
runtime_integrity_error
|
|
628
|
-
acceptance_test_suite_freeze_failed
|
|
629
|
-
acceptance_test_suite_freeze_attestation_invalid
|
|
630
|
-
acceptance_test_suite_freeze_attestation_failed
|
|
631
|
-
acceptance_test_suite_candidate_binding_failed
|
|
632
|
-
acceptance_test_suite_freeze_recovery_failed
|
|
597
|
+
OPERATION_FAILURE_REASONS = %w[
|
|
598
|
+
missing_state_file missing_state_front_matter stale_revision invalid_control_path
|
|
599
|
+
unknown_step gate_target_step_unknown gate_current_step_mismatch
|
|
600
|
+
parallel_transition_requires_write feature_not_active cancel_reason_required
|
|
601
|
+
session_rollover_in_progress parallel_branch_not_found gate_boundary_rejected
|
|
602
|
+
parallel_join_failed parallel_branch_transition_failed state_validation_failed
|
|
603
|
+
phase_ownership_claim_failed phase_ownership_completion_failed not_waiting_checkpoint no_pending_step
|
|
633
604
|
].freeze
|
|
634
605
|
$devflow_advisories = []
|
|
635
606
|
|
|
636
607
|
def fail_result(reason, details = {})
|
|
637
608
|
failure = { "ok" => false, "reason" => reason, "details" => details }
|
|
638
|
-
if
|
|
609
|
+
if !OPERATION_FAILURE_REASONS.include?(reason.to_s)
|
|
639
610
|
$devflow_advisories << failure
|
|
640
611
|
return nil
|
|
641
612
|
end
|
|
@@ -660,8 +631,8 @@ rescue JSON::ParserError => error
|
|
|
660
631
|
{ "ok" => false, "reason" => "test_task_binding_#{stage}_check_failed", "errors" => ["checker returned invalid JSON: #{error.message}"] }
|
|
661
632
|
end
|
|
662
633
|
|
|
663
|
-
#
|
|
664
|
-
#
|
|
634
|
+
# Always re-check acceptance results. Soft mode retains failures as advisories
|
|
635
|
+
# without changing the canonical test verdict.
|
|
665
636
|
ACCEPTANCE_RESULT_GATED_STEPS = %w[run-acceptance-tests release-closeout].freeze
|
|
666
637
|
|
|
667
638
|
def acceptance_results_check(root, feature)
|
|
@@ -956,7 +927,9 @@ result =
|
|
|
956
927
|
!%w[prepared claimed].include?(state.dig("rollover", "status")) &&
|
|
957
928
|
(options[:complete_step] || options[:confirm_checkpoint] || options[:enter_failure_branch] || options[:exit_failure_branch] || options[:parallel_action])
|
|
958
929
|
topology_errors = WorkflowGateContract.validate(workflow)
|
|
959
|
-
|
|
930
|
+
if !topology_errors.empty?
|
|
931
|
+
$devflow_advisories.concat(topology_errors)
|
|
932
|
+
elsif !topology_errors.empty?
|
|
960
933
|
result = fail_result("gate_boundary_rejected", "gate" => {
|
|
961
934
|
"verdict" => "deny",
|
|
962
935
|
"reason" => "workflow_gate_contract_invalid",
|
|
@@ -1014,7 +987,8 @@ result =
|
|
|
1014
987
|
transition_kind: transition_kind,
|
|
1015
988
|
checkpoint_authorized: options[:runtime_confirmed]
|
|
1016
989
|
)
|
|
1017
|
-
|
|
990
|
+
$devflow_advisories.concat(Array(gate_result && gate_result["warnings"]))
|
|
991
|
+
if gate_result && !gate_result["ok"]
|
|
1018
992
|
result = fail_result("gate_boundary_rejected", "gate" => gate_result)
|
|
1019
993
|
end
|
|
1020
994
|
end
|
|
@@ -1129,24 +1103,22 @@ result =
|
|
|
1129
1103
|
state.delete("checkpoint_decision")
|
|
1130
1104
|
|
|
1131
1105
|
if step["checkpoint"]
|
|
1132
|
-
|
|
1133
|
-
state["
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1106
|
+
$devflow_advisories << { "reason" => "checkpoint_advisory", "checkpoint" => step["checkpoint"], "approval_recorded" => false }
|
|
1107
|
+
state["checkpoint_decision"] = "continued_with_warning"
|
|
1108
|
+
begin
|
|
1109
|
+
apply_conditional_step_skips!(root, state, workflow, step["checkpoint"], current_revision + 1)
|
|
1110
|
+
pending = Array(state["pending_steps"])
|
|
1111
|
+
completed = Array(state["completed_steps"])
|
|
1112
|
+
rescue JSON::ParserError, KeyError, ArgumentError => error
|
|
1113
|
+
$devflow_advisories << { "reason" => "conditional_step_skip_failed", "message" => error.message }
|
|
1114
|
+
end
|
|
1115
|
+
end
|
|
1116
|
+
if (parallel_window = parallel_window_for(workflow, step_id))
|
|
1138
1117
|
state["current_step"] = parallel_window.fetch("join_step")
|
|
1139
1118
|
state["status"] = "in_progress"
|
|
1140
1119
|
state.delete("checkpoint")
|
|
1141
1120
|
state.delete("checkpoint_label")
|
|
1142
1121
|
action_description = "Step completed: #{step["label"]} (#{step_id}). Entered parallel window #{parallel_window["id"]}; awaiting Core prepare."
|
|
1143
|
-
elsif track_boundary?(state, pending)
|
|
1144
|
-
state["current_step"] = step_id
|
|
1145
|
-
state["status"] = "blocked"
|
|
1146
|
-
state["block_reason"] = "track_scope_complete"
|
|
1147
|
-
state.delete("checkpoint")
|
|
1148
|
-
state.delete("checkpoint_label")
|
|
1149
|
-
action_description = "Step completed: #{step["label"]} (#{step_id}). Active track output scope is complete."
|
|
1150
1122
|
elsif (next_id = next_pending_step_id(state, pending, step_by_id, completed))
|
|
1151
1123
|
state["current_step"] = next_id
|
|
1152
1124
|
state["status"] = "in_progress"
|
|
@@ -1178,125 +1150,6 @@ result =
|
|
|
1178
1150
|
elsif Array(state["pending_steps"]).empty?
|
|
1179
1151
|
result = fail_result("no_pending_step")
|
|
1180
1152
|
else
|
|
1181
|
-
# CP-API turns the shared candidate contract into confirmed graph
|
|
1182
|
-
# fact inside the same user checkpoint. Skills cannot self-confirm
|
|
1183
|
-
# API nodes and downstream generated TC imports can require a
|
|
1184
|
-
# reviewed contract revision.
|
|
1185
|
-
if state["checkpoint"] == "CP-API" && steps.any? { |step| step["id"] == "api-contract" }
|
|
1186
|
-
feature = state.fetch("current_feature")
|
|
1187
|
-
graph, = RequirementGraph.load_graph(root, feature)
|
|
1188
|
-
schema_check = api_test_schema_check(root, feature, graph)
|
|
1189
|
-
if !schema_check["ok"]
|
|
1190
|
-
result = fail_result("api_test_schema_incomplete", "api_test_schema" => schema_check)
|
|
1191
|
-
else
|
|
1192
|
-
api_confirmation = RequirementGraph.confirm_api_contracts!(root, graph, "CP-API: state revision #{current_revision} approved")
|
|
1193
|
-
if api_confirmation["ok"]
|
|
1194
|
-
actions << "api_contract_confirmed" unless Array(api_confirmation["confirmed_api_ids"]).empty?
|
|
1195
|
-
else
|
|
1196
|
-
result = fail_result("api_contract_confirmation_failed", "api_contract" => api_confirmation)
|
|
1197
|
-
end
|
|
1198
|
-
end
|
|
1199
|
-
end
|
|
1200
|
-
|
|
1201
|
-
# CP-3 authorizes exactly one immutable TC freeze. It is deliberately
|
|
1202
|
-
# inside the checkpoint transition so the user does not have to
|
|
1203
|
-
# approve an extra model-issued dialog and a Skill cannot freeze its
|
|
1204
|
-
# own candidates early.
|
|
1205
|
-
if result.nil? && state["checkpoint"] == "CP-3" && steps.any? { |step| %w[acceptance-test-design regression-test-design].include?(step["id"]) }
|
|
1206
|
-
feature = state.fetch("current_feature")
|
|
1207
|
-
graph, = RequirementGraph.load_graph(root, feature)
|
|
1208
|
-
regression = regression_workflow?(state, steps)
|
|
1209
|
-
attestation_path, = test_suite_freeze_attestation_path(root, feature)
|
|
1210
|
-
|
|
1211
|
-
if File.exist?(attestation_path)
|
|
1212
|
-
frozen_check = if regression
|
|
1213
|
-
regression_freeze_attestation_check(graph, feature, attestation_path)
|
|
1214
|
-
else
|
|
1215
|
-
test_task_binding_check(root, feature, "frozen")
|
|
1216
|
-
end
|
|
1217
|
-
unless frozen_check["ok"]
|
|
1218
|
-
result = fail_result("acceptance_test_suite_freeze_attestation_invalid", "test_suite" => frozen_check)
|
|
1219
|
-
else
|
|
1220
|
-
actions << "acceptance_test_suite_freeze_attestation_verified"
|
|
1221
|
-
end
|
|
1222
|
-
elsif cp3_freeze_provenance?(graph, current_revision)
|
|
1223
|
-
# A process can die after graph/view writes but before the
|
|
1224
|
-
# attestation is published. Recover only if each TC proves that
|
|
1225
|
-
# this exact CP-3 already froze it; never use this path to bless
|
|
1226
|
-
# a generic confirmed suite.
|
|
1227
|
-
recovery_check = if regression
|
|
1228
|
-
{ "ok" => true, "regression" => true, "candidate_test_suite" => graph.dig("views", "tests") }
|
|
1229
|
-
else
|
|
1230
|
-
test_task_binding_check(root, feature, "recovery")
|
|
1231
|
-
end
|
|
1232
|
-
unless recovery_check["ok"]
|
|
1233
|
-
result = fail_result("acceptance_test_suite_freeze_recovery_failed", "test_suite" => recovery_check)
|
|
1234
|
-
else
|
|
1235
|
-
recovered = {
|
|
1236
|
-
"ok" => true,
|
|
1237
|
-
"tests_view" => graph.dig("views", "tests"),
|
|
1238
|
-
"frozen_case_ids" => Array(graph["nodes"]).select { |node| node.is_a?(Hash) && node["kind"] == "test_case" }.map { |node| node["id"] }
|
|
1239
|
-
}
|
|
1240
|
-
begin
|
|
1241
|
-
write_test_suite_freeze_attestation(root, feature, current_revision, recovery_check, recovered)
|
|
1242
|
-
frozen_check = if regression
|
|
1243
|
-
regression_freeze_attestation_check(graph, feature, attestation_path)
|
|
1244
|
-
else
|
|
1245
|
-
test_task_binding_check(root, feature, "frozen")
|
|
1246
|
-
end
|
|
1247
|
-
if frozen_check["ok"]
|
|
1248
|
-
actions << "acceptance_test_suite_freeze_attestation_recovered"
|
|
1249
|
-
else
|
|
1250
|
-
result = fail_result("acceptance_test_suite_freeze_attestation_invalid", "test_suite" => frozen_check)
|
|
1251
|
-
end
|
|
1252
|
-
rescue DevflowControlIO::InvalidControlPath, SystemCallError => error
|
|
1253
|
-
result = fail_result("acceptance_test_suite_freeze_attestation_failed", "message" => error.message)
|
|
1254
|
-
end
|
|
1255
|
-
end
|
|
1256
|
-
else
|
|
1257
|
-
matrix_check = api_test_matrix_check(root, feature, graph)
|
|
1258
|
-
# A new freeze requires every TC to declare a consistent
|
|
1259
|
-
# execution_surface; this is a hard failure even in soft mode.
|
|
1260
|
-
surface_check = matrix_check["ok"] ? execution_surface_check(root, feature) : nil
|
|
1261
|
-
unless matrix_check["ok"]
|
|
1262
|
-
result = fail_result("api_test_matrix_incomplete", "api_test_matrix" => matrix_check)
|
|
1263
|
-
else
|
|
1264
|
-
result = fail_result("execution_surface_mismatch", "execution_surface" => surface_check) unless surface_check["ok"]
|
|
1265
|
-
end
|
|
1266
|
-
if matrix_check["ok"] && surface_check["ok"]
|
|
1267
|
-
candidate_check = if regression
|
|
1268
|
-
{ "ok" => true, "regression" => true, "candidate_test_suite" => graph.dig("views", "tests") }
|
|
1269
|
-
else
|
|
1270
|
-
test_task_binding_check(root, feature, "candidate")
|
|
1271
|
-
end
|
|
1272
|
-
unless candidate_check["ok"]
|
|
1273
|
-
result = fail_result("acceptance_test_suite_candidate_binding_failed", "test_suite" => candidate_check)
|
|
1274
|
-
else
|
|
1275
|
-
frozen = RequirementGraph.freeze_test_cases!(
|
|
1276
|
-
root,
|
|
1277
|
-
graph,
|
|
1278
|
-
"CP-3: state revision #{current_revision} approved",
|
|
1279
|
-
confirm_prerequisites: options[:confirm_test_prerequisites] && options[:runtime_confirmed],
|
|
1280
|
-
regression: regression,
|
|
1281
|
-
persist: false
|
|
1282
|
-
)
|
|
1283
|
-
unless frozen["ok"]
|
|
1284
|
-
result = fail_result("acceptance_test_suite_freeze_failed", "test_suite" => frozen)
|
|
1285
|
-
else
|
|
1286
|
-
actions << "acceptance_test_prerequisites_confirmed" unless Array(frozen["confirmed_prerequisite_ids"]).empty?
|
|
1287
|
-
test_suite_freeze_to_write = {
|
|
1288
|
-
"feature" => feature,
|
|
1289
|
-
"state_revision" => current_revision,
|
|
1290
|
-
"graph" => graph,
|
|
1291
|
-
"candidate_check" => candidate_check,
|
|
1292
|
-
"frozen" => frozen
|
|
1293
|
-
}
|
|
1294
|
-
actions << "acceptance_test_suite_frozen"
|
|
1295
|
-
end
|
|
1296
|
-
end
|
|
1297
|
-
end
|
|
1298
|
-
end
|
|
1299
|
-
end
|
|
1300
1153
|
if result.nil?
|
|
1301
1154
|
begin
|
|
1302
1155
|
applied_skips = apply_conditional_step_skips!(root, state, workflow, state["checkpoint"], current_revision + 1)
|
|
@@ -1313,7 +1166,7 @@ result =
|
|
|
1313
1166
|
completed = Array(state["completed_steps"])
|
|
1314
1167
|
next_id = next_pending_step_id(state, pending, step_by_id, completed)
|
|
1315
1168
|
if next_id.nil? && track_boundary?(state, pending)
|
|
1316
|
-
state["checkpoint_decision"] = "
|
|
1169
|
+
state["checkpoint_decision"] = "continued_with_warning"
|
|
1317
1170
|
state["status"] = "blocked"
|
|
1318
1171
|
state["block_reason"] = "track_scope_complete"
|
|
1319
1172
|
state.delete("checkpoint")
|
|
@@ -1323,7 +1176,7 @@ result =
|
|
|
1323
1176
|
action_description = "Checkpoint confirmed. Active track output scope is complete."
|
|
1324
1177
|
next_intent_requested = true
|
|
1325
1178
|
else
|
|
1326
|
-
state["checkpoint_decision"] = "
|
|
1179
|
+
state["checkpoint_decision"] = "continued_with_warning"
|
|
1327
1180
|
state["current_step"] = next_id || pending.first
|
|
1328
1181
|
state["status"] = "in_progress"
|
|
1329
1182
|
state.delete("checkpoint")
|
|
@@ -1408,7 +1261,9 @@ result =
|
|
|
1408
1261
|
phase_owner_override: prospective_owner
|
|
1409
1262
|
)
|
|
1410
1263
|
|
|
1411
|
-
|
|
1264
|
+
$devflow_advisories.concat(Array(validation["warnings"]))
|
|
1265
|
+
state["gate_advisories"] = JSON.parse(JSON.generate(Array(state["gate_advisories"]) + $devflow_advisories)) if changed
|
|
1266
|
+
if changed && !validation["ok"]
|
|
1412
1267
|
result = fail_result("state_validation_failed", "validation" => validation)
|
|
1413
1268
|
elsif changed && options[:write]
|
|
1414
1269
|
ownership = PhaseOwnership.claim(
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ArtifactSpecs
|
|
4
|
+
module_function
|
|
5
|
+
|
|
6
|
+
def for_step(step)
|
|
7
|
+
outputs = Array(step["outputs"]).select { |entry| entry.is_a?(Hash) }.map(&:dup)
|
|
8
|
+
meta = step["meta_output"]
|
|
9
|
+
outputs << { "type" => "json" }.merge(meta).merge("required" => true) if meta.is_a?(Hash) && meta["path"]
|
|
10
|
+
outputs.uniq { |entry| entry["path"] }
|
|
11
|
+
end
|
|
12
|
+
end
|