devflow-cli 8.0.0__py3-none-any.whl → 8.1.0__py3-none-any.whl
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.
- devflow_cli/__init__.py +1 -1
- devflow_cli/commands/adaptive.py +217 -12
- devflow_cli/commands/verify.py +4 -1
- devflow_cli/core/activity_base.py +1 -0
- devflow_cli/core/adaptive_runner.py +202 -51
- devflow_cli/core/agent_run_marker.py +99 -0
- devflow_cli/core/automatic_evidence.py +85 -7
- devflow_cli/core/operational.py +3 -1
- devflow_cli/core/runtime_backup.py +119 -0
- devflow_cli/core/validation_approvals.py +20 -0
- devflow_cli/core/verification.py +98 -13
- devflow_cli/core/worklog.py +24 -0
- {devflow_cli-8.0.0.dist-info → devflow_cli-8.1.0.dist-info}/METADATA +13 -2
- {devflow_cli-8.0.0.dist-info → devflow_cli-8.1.0.dist-info}/RECORD +17 -15
- {devflow_cli-8.0.0.dist-info → devflow_cli-8.1.0.dist-info}/WHEEL +0 -0
- {devflow_cli-8.0.0.dist-info → devflow_cli-8.1.0.dist-info}/entry_points.txt +0 -0
- {devflow_cli-8.0.0.dist-info → devflow_cli-8.1.0.dist-info}/licenses/LICENSE +0 -0
devflow_cli/__init__.py
CHANGED
devflow_cli/commands/adaptive.py
CHANGED
|
@@ -38,14 +38,17 @@ from devflow_cli.core.activity_contracts import (
|
|
|
38
38
|
write_activity_contract,
|
|
39
39
|
)
|
|
40
40
|
from devflow_cli.core.adaptive_runner import (
|
|
41
|
+
MANUAL_RECONCILIATION_ACTIVITIES,
|
|
41
42
|
AgentActivityExecution,
|
|
42
43
|
reconcile_current_activity,
|
|
43
44
|
record_human_review,
|
|
44
45
|
run_current_activity,
|
|
45
46
|
)
|
|
47
|
+
from devflow_cli.core.agent_run_marker import AgentRunningError, ensure_no_agent_running
|
|
46
48
|
from devflow_cli.core import run_progress
|
|
47
49
|
from devflow_cli.core.worktree_snapshot import changed_paths, snapshot_worktree
|
|
48
50
|
from devflow_cli.core.automatic_evidence import (
|
|
51
|
+
AutomaticVerificationOutcome,
|
|
49
52
|
ensure_fresh_devflow_evidence,
|
|
50
53
|
run_automatic_verification,
|
|
51
54
|
stale_devflow_evidence,
|
|
@@ -62,6 +65,7 @@ from devflow_cli.core.remediation import (
|
|
|
62
65
|
from devflow_cli.core.redaction import redact_command
|
|
63
66
|
from devflow_cli.core.validation_approvals import (
|
|
64
67
|
approve_plan_command,
|
|
68
|
+
pending_approvals,
|
|
65
69
|
pending_plan_approval,
|
|
66
70
|
)
|
|
67
71
|
from devflow_cli.core.contract_schemas import LegacySchemaError, diagnose_contract_file
|
|
@@ -215,6 +219,7 @@ def complete(
|
|
|
215
219
|
"""Complete l'activite courante si ses gates sont satisfaites."""
|
|
216
220
|
feature_dir = _feature(feature)
|
|
217
221
|
with workflow_lock(feature_dir):
|
|
222
|
+
refuse_if_agent_running(feature_dir)
|
|
218
223
|
state = _state(feature_dir)
|
|
219
224
|
activity = activity or state.currentActivity
|
|
220
225
|
try:
|
|
@@ -257,6 +262,7 @@ def evidence(
|
|
|
257
262
|
"""Enregistre une preuve manuelle informative, sans ouvrir les gates."""
|
|
258
263
|
feature_dir = _feature(feature)
|
|
259
264
|
with workflow_lock(feature_dir):
|
|
265
|
+
refuse_if_agent_running(feature_dir)
|
|
260
266
|
state = _state(feature_dir)
|
|
261
267
|
try:
|
|
262
268
|
record_evidence(state, evidence_id, report or "")
|
|
@@ -288,6 +294,7 @@ def contract(
|
|
|
288
294
|
raise typer.Exit(int(ExitCode.INVALID_CONTRACT)) from exc
|
|
289
295
|
|
|
290
296
|
with workflow_lock(feature_dir):
|
|
297
|
+
refuse_if_agent_running(feature_dir)
|
|
291
298
|
state = _state(feature_dir)
|
|
292
299
|
try:
|
|
293
300
|
assessment = _assessment(feature_dir)
|
|
@@ -463,7 +470,7 @@ def run_adaptive(
|
|
|
463
470
|
agent,
|
|
464
471
|
)
|
|
465
472
|
|
|
466
|
-
outcome = run_current_activity(feature_dir, feature_path, execute)
|
|
473
|
+
outcome = run_current_activity(feature_dir, feature_path, execute, agent=agent)
|
|
467
474
|
_record_telemetry(
|
|
468
475
|
run_id, attempt, agent, project_root, outcome.reason,
|
|
469
476
|
json_output=json_output,
|
|
@@ -481,8 +488,10 @@ def run_adaptive(
|
|
|
481
488
|
f"Prochaine : {outcome.nextActivity}"
|
|
482
489
|
)
|
|
483
490
|
_print_warnings(outcome.warnings)
|
|
491
|
+
_print_pending_approvals(feature_dir, current_state.issueId, outcome.activity)
|
|
484
492
|
return
|
|
485
|
-
|
|
493
|
+
if outcome.reason != "agent-running": # worklog.md : fichier d'execution (B4)
|
|
494
|
+
safe_refresh_worklog(feature_dir)
|
|
486
495
|
exit_code = exit_code_for_run_reason(outcome.reason)
|
|
487
496
|
if json_output:
|
|
488
497
|
_print_execution_json(
|
|
@@ -569,7 +578,10 @@ def reconcile_adaptive(
|
|
|
569
578
|
result: Optional[Path] = typer.Option(
|
|
570
579
|
None,
|
|
571
580
|
"--result",
|
|
572
|
-
help=
|
|
581
|
+
help=(
|
|
582
|
+
"Payload JSON corrige, rejoue par les validateurs de run, quand la trace "
|
|
583
|
+
"est inexploitable ou que sa reponse est refusee par les validateurs"
|
|
584
|
+
),
|
|
573
585
|
),
|
|
574
586
|
reason: Optional[str] = typer.Option(
|
|
575
587
|
None,
|
|
@@ -615,8 +627,10 @@ def reconcile_adaptive(
|
|
|
615
627
|
f"Prochaine : {outcome.nextActivity}"
|
|
616
628
|
)
|
|
617
629
|
_print_warnings(outcome.warnings)
|
|
630
|
+
_print_pending_approvals(feature_dir, state.issueId, outcome.activity)
|
|
618
631
|
return
|
|
619
|
-
|
|
632
|
+
if outcome.reason != "agent-running": # worklog.md : fichier d'execution (B4)
|
|
633
|
+
safe_refresh_worklog(feature_dir)
|
|
620
634
|
if outcome.reason in BASE_REFUSALS:
|
|
621
635
|
fail(outcome.detail)
|
|
622
636
|
else:
|
|
@@ -625,6 +639,9 @@ def reconcile_adaptive(
|
|
|
625
639
|
+ (f" ({outcome.detail})" if outcome.detail else "")
|
|
626
640
|
)
|
|
627
641
|
_print_warnings(outcome.warnings)
|
|
642
|
+
if outcome.reason == "agent-running":
|
|
643
|
+
console.print(" Suite : attendez la fin de l'activite en cours, puis relancez.")
|
|
644
|
+
raise typer.Exit(int(exit_code_for_run_reason(outcome.reason)))
|
|
628
645
|
console.print(f" Suite : devflow adaptive status {state.issueId}")
|
|
629
646
|
if outcome.reason == "reconcile-refused":
|
|
630
647
|
console.print(
|
|
@@ -650,6 +667,7 @@ def review_adaptive(
|
|
|
650
667
|
project_root = project_root_for_feature(feature_dir)
|
|
651
668
|
feature_path = feature_dir.relative_to(project_root).as_posix()
|
|
652
669
|
with workflow_lock(feature_dir):
|
|
670
|
+
refuse_if_agent_running(feature_dir)
|
|
653
671
|
state = _state(feature_dir)
|
|
654
672
|
try:
|
|
655
673
|
report_file = report.resolve()
|
|
@@ -693,6 +711,10 @@ def verify_adaptive(
|
|
|
693
711
|
"""Genere localement les preuves requises par le profil actif."""
|
|
694
712
|
feature_dir = _feature(feature, json_output=json_output)
|
|
695
713
|
state = _state(feature_dir, json_output=json_output, issue_id=feature)
|
|
714
|
+
with workflow_lock(feature_dir):
|
|
715
|
+
refuse_if_agent_running(
|
|
716
|
+
feature_dir, json_issue_id=state.issueId if json_output else None
|
|
717
|
+
)
|
|
696
718
|
if not refresh and state.currentActivity != "verify":
|
|
697
719
|
detail = (
|
|
698
720
|
"Activite courante attendue pour verify : verify "
|
|
@@ -726,6 +748,7 @@ def approve_adaptive_command(
|
|
|
726
748
|
feature_dir = _feature(feature)
|
|
727
749
|
try:
|
|
728
750
|
with workflow_lock(feature_dir):
|
|
751
|
+
refuse_if_agent_running(feature_dir)
|
|
729
752
|
item = pending_plan_approval(feature_dir, validation_id)
|
|
730
753
|
console.print(f"Validation : {item.id}")
|
|
731
754
|
console.print(f"Commande : {shlex.join(redact_command(item.command))}")
|
|
@@ -767,6 +790,7 @@ def waive_validation(
|
|
|
767
790
|
"""Rend non bloquant un controle planifie echoue, avec audit durable."""
|
|
768
791
|
feature_dir = _feature(feature)
|
|
769
792
|
with workflow_lock(feature_dir):
|
|
793
|
+
refuse_if_agent_running(feature_dir)
|
|
770
794
|
try:
|
|
771
795
|
waiver = create_validation_waiver(
|
|
772
796
|
feature_dir, validation_id, reason, author=author
|
|
@@ -794,6 +818,7 @@ def reopen(
|
|
|
794
818
|
"""Revient a une activite anterieure sans effacer l'historique."""
|
|
795
819
|
feature_dir = _feature(feature)
|
|
796
820
|
with workflow_lock(feature_dir):
|
|
821
|
+
refuse_if_agent_running(feature_dir)
|
|
797
822
|
state = _state(feature_dir)
|
|
798
823
|
try:
|
|
799
824
|
entry = reopen_activity(
|
|
@@ -823,6 +848,7 @@ def discard_attempt_command(
|
|
|
823
848
|
"""Abandonne la tentative de l'activite courante, avec archive et audit."""
|
|
824
849
|
feature_dir = _feature(feature)
|
|
825
850
|
with workflow_lock(feature_dir):
|
|
851
|
+
refuse_if_agent_running(feature_dir)
|
|
826
852
|
state = _state(feature_dir)
|
|
827
853
|
try:
|
|
828
854
|
entry = discard_attempt(feature_dir, state, reason, author=author)
|
|
@@ -856,11 +882,15 @@ def _execute_local_verification(
|
|
|
856
882
|
if not refresh:
|
|
857
883
|
sync_activity_status(issue_id, "verify", stream)
|
|
858
884
|
outcome = run_automatic_verification(feature_dir, project_root, refresh=refresh)
|
|
885
|
+
recovery = (
|
|
886
|
+
None if outcome.passed else _verify_recovery(feature_dir, issue_id, outcome)
|
|
887
|
+
)
|
|
859
888
|
if json_output:
|
|
860
889
|
_print_execution_json(
|
|
861
890
|
issue_id, "verify", outcome.passed, outcome.reason, outcome.detail,
|
|
862
891
|
outcome.nextActivity,
|
|
863
892
|
ExitCode.OK if outcome.passed else ExitCode.VERIFICATION_FAILURE,
|
|
893
|
+
recovery=recovery,
|
|
864
894
|
)
|
|
865
895
|
if not outcome.passed:
|
|
866
896
|
raise typer.Exit(int(ExitCode.VERIFICATION_FAILURE))
|
|
@@ -874,20 +904,182 @@ def _execute_local_verification(
|
|
|
874
904
|
fail(outcome.detail or outcome.reason)
|
|
875
905
|
console.print(f" Verification automatique refusee : {outcome.reason}")
|
|
876
906
|
console.print(" Reprise guidee :")
|
|
877
|
-
console.print(f"
|
|
878
|
-
|
|
879
|
-
f"
|
|
880
|
-
f'"correction apres {outcome.reason}"'
|
|
881
|
-
)
|
|
907
|
+
console.print(f" {recovery['summary']}")
|
|
908
|
+
for step in recovery["commands"]:
|
|
909
|
+
console.print(f" {step['command']} # {step['purpose']}", markup=False)
|
|
882
910
|
raise typer.Exit(int(ExitCode.VERIFICATION_FAILURE))
|
|
883
911
|
|
|
884
912
|
|
|
913
|
+
# Motifs de refus de verify dus a l'echec d'un controle execute (B2).
|
|
914
|
+
CHECK_FAILURE_REASONS = frozenset({
|
|
915
|
+
"targeted-check-failed",
|
|
916
|
+
"targeted-tests-failed",
|
|
917
|
+
"test-suite-failed",
|
|
918
|
+
"regression-check-failed",
|
|
919
|
+
"security-or-data-check-failed",
|
|
920
|
+
})
|
|
921
|
+
PLANNED_PREFIXES = {"PREP-": "prepare", "PLAN-": "plan"}
|
|
922
|
+
|
|
923
|
+
|
|
924
|
+
def _verify_recovery(
|
|
925
|
+
feature_dir: Path, issue_id: str, outcome: AutomaticVerificationOutcome
|
|
926
|
+
) -> dict:
|
|
927
|
+
"""Reprise guidee d'un refus de verify, par motif, sans heuristique (B2).
|
|
928
|
+
|
|
929
|
+
Approbation manquante : chaque approve-command puis verify. Executable introuvable
|
|
930
|
+
(code 127) : corriger l'environnement puis verify. Echec d'un controle : deux voies,
|
|
931
|
+
le code (reopen implement) ou la validation (reopen de l'activite qui l'a inscrite).
|
|
932
|
+
"""
|
|
933
|
+
issue = shlex.quote(issue_id)
|
|
934
|
+
verify_command = {
|
|
935
|
+
"command": f"devflow adaptive verify {issue}",
|
|
936
|
+
"purpose": "relancer la verification",
|
|
937
|
+
}
|
|
938
|
+
state = read_adaptive_state(feature_dir / "workflow-state.json")
|
|
939
|
+
plan_activity = (
|
|
940
|
+
"prepare" if state is not None and state.profile == "standard" else "plan"
|
|
941
|
+
)
|
|
942
|
+
if outcome.pendingApprovals:
|
|
943
|
+
return {
|
|
944
|
+
"kind": "approval-pending",
|
|
945
|
+
"summary": "Approuver chaque validation en attente, puis relancer verify.",
|
|
946
|
+
"validations": [
|
|
947
|
+
{"id": item, "origin": plan_activity, "returncode": None}
|
|
948
|
+
for item in outcome.pendingApprovals
|
|
949
|
+
],
|
|
950
|
+
"commands": [
|
|
951
|
+
*(
|
|
952
|
+
{
|
|
953
|
+
"command": (
|
|
954
|
+
f"devflow adaptive approve-command {issue} "
|
|
955
|
+
f"{shlex.quote(item)} --reason MOTIF"
|
|
956
|
+
),
|
|
957
|
+
"purpose": f"approuver la commande exacte de {item}",
|
|
958
|
+
}
|
|
959
|
+
for item in outcome.pendingApprovals
|
|
960
|
+
),
|
|
961
|
+
verify_command,
|
|
962
|
+
],
|
|
963
|
+
}
|
|
964
|
+
status_command = {
|
|
965
|
+
"command": f"devflow adaptive status {issue}",
|
|
966
|
+
"purpose": "consulter l'etat du parcours",
|
|
967
|
+
}
|
|
968
|
+
if outcome.reason not in CHECK_FAILURE_REASONS:
|
|
969
|
+
return {
|
|
970
|
+
"kind": "status",
|
|
971
|
+
"summary": f"Refus {outcome.reason} : consulter l'etat avant toute reprise.",
|
|
972
|
+
"validations": [],
|
|
973
|
+
"commands": [status_command],
|
|
974
|
+
}
|
|
975
|
+
validations = []
|
|
976
|
+
for failure in outcome.failures:
|
|
977
|
+
validation_id, origin = failure.id, None
|
|
978
|
+
for prefix, activity in PLANNED_PREFIXES.items():
|
|
979
|
+
if failure.id.startswith(prefix):
|
|
980
|
+
validation_id, origin = failure.id.removeprefix(prefix), activity
|
|
981
|
+
if origin is not None and not _reopenable(state, origin):
|
|
982
|
+
origin = None
|
|
983
|
+
validations.append({
|
|
984
|
+
"id": validation_id,
|
|
985
|
+
"origin": origin,
|
|
986
|
+
"returncode": failure.returncode,
|
|
987
|
+
})
|
|
988
|
+
if validations and all(item["returncode"] == 127 for item in validations):
|
|
989
|
+
names = ", ".join(item["id"] for item in validations)
|
|
990
|
+
return {
|
|
991
|
+
"kind": "environment",
|
|
992
|
+
"summary": (
|
|
993
|
+
f"Executable introuvable (code 127) pour {names} : corriger "
|
|
994
|
+
"l'environnement, puis relancer verify."
|
|
995
|
+
),
|
|
996
|
+
"validations": validations,
|
|
997
|
+
"commands": [verify_command],
|
|
998
|
+
}
|
|
999
|
+
reason_text = shlex.quote(f"correction apres {outcome.reason}")
|
|
1000
|
+
commands = [
|
|
1001
|
+
status_command,
|
|
1002
|
+
{
|
|
1003
|
+
"command": f"devflow adaptive reopen {issue} implement --reason {reason_text}",
|
|
1004
|
+
"purpose": "voie 1 : le code est en cause",
|
|
1005
|
+
},
|
|
1006
|
+
]
|
|
1007
|
+
for origin in sorted({item["origin"] for item in validations if item["origin"]}):
|
|
1008
|
+
names = ", ".join(item["id"] for item in validations if item["origin"] == origin)
|
|
1009
|
+
commands.append({
|
|
1010
|
+
"command": f"devflow adaptive reopen {issue} {origin} --reason {reason_text}",
|
|
1011
|
+
"purpose": f"voie 2 : la validation {names}, inscrite par {origin}, est en cause",
|
|
1012
|
+
})
|
|
1013
|
+
return {
|
|
1014
|
+
"kind": "check-failed",
|
|
1015
|
+
"summary": (
|
|
1016
|
+
"Controle echoue"
|
|
1017
|
+
+ (f" : {', '.join(item['id'] for item in validations)}" if validations else "")
|
|
1018
|
+
+ ". Choisir la voie selon la cause ; aucune n'est choisie automatiquement."
|
|
1019
|
+
),
|
|
1020
|
+
"validations": validations,
|
|
1021
|
+
"commands": commands,
|
|
1022
|
+
}
|
|
1023
|
+
|
|
1024
|
+
|
|
1025
|
+
def _reopenable(state: AdaptiveWorkflowState | None, activity: str) -> bool:
|
|
1026
|
+
return (
|
|
1027
|
+
state is not None
|
|
1028
|
+
and activity in state.activities
|
|
1029
|
+
and activity in state.completedActivities
|
|
1030
|
+
and state.activities.index(activity) < state.activities.index(state.currentActivity)
|
|
1031
|
+
)
|
|
1032
|
+
|
|
1033
|
+
|
|
1034
|
+
def refuse_if_agent_running(feature_dir: Path, *, json_issue_id: str | None = None) -> None:
|
|
1035
|
+
"""Refuse sans effet une commande humaine pendant l'execution d'un agent (B4).
|
|
1036
|
+
|
|
1037
|
+
A appeler avant toute ecriture dans le dossier de la feature. Avec ``json_issue_id``,
|
|
1038
|
+
le refus est rendu sous la forme JSON des commandes d'execution.
|
|
1039
|
+
"""
|
|
1040
|
+
try:
|
|
1041
|
+
warning = ensure_no_agent_running(feature_dir)
|
|
1042
|
+
except AgentRunningError as exc:
|
|
1043
|
+
if json_issue_id is not None:
|
|
1044
|
+
_print_execution_json(
|
|
1045
|
+
json_issue_id, None, False, "agent-running", str(exc), None,
|
|
1046
|
+
ExitCode.CONCURRENT_CHANGE,
|
|
1047
|
+
)
|
|
1048
|
+
else:
|
|
1049
|
+
fail(str(exc))
|
|
1050
|
+
raise typer.Exit(int(ExitCode.CONCURRENT_CHANGE)) from exc
|
|
1051
|
+
if warning:
|
|
1052
|
+
warn(warning)
|
|
1053
|
+
|
|
1054
|
+
|
|
1055
|
+
def _print_pending_approvals(feature_dir: Path, issue_id: str, activity: str) -> None:
|
|
1056
|
+
"""Liste en une fois les approbations attendues a la fin de prepare ou plan."""
|
|
1057
|
+
if activity not in {"prepare", "plan"}:
|
|
1058
|
+
return
|
|
1059
|
+
try:
|
|
1060
|
+
pending = pending_approvals(feature_dir)
|
|
1061
|
+
except ValueError:
|
|
1062
|
+
return
|
|
1063
|
+
if not pending:
|
|
1064
|
+
return
|
|
1065
|
+
warn(
|
|
1066
|
+
"Approbations attendues avant verify : "
|
|
1067
|
+
+ ", ".join(item.id for item in pending)
|
|
1068
|
+
)
|
|
1069
|
+
for item in pending:
|
|
1070
|
+
console.print(
|
|
1071
|
+
f" devflow adaptive approve-command {shlex.quote(issue_id)} "
|
|
1072
|
+
f"{shlex.quote(item.id)} --reason MOTIF",
|
|
1073
|
+
markup=False,
|
|
1074
|
+
)
|
|
1075
|
+
|
|
1076
|
+
|
|
885
1077
|
def _print_execution_json(
|
|
886
1078
|
issue_id: str, activity: str | None, passed: bool, reason: str,
|
|
887
1079
|
detail: str, next_activity: str | None, exit_code: ExitCode,
|
|
888
|
-
*, warnings: tuple[str, ...] = (),
|
|
1080
|
+
*, warnings: tuple[str, ...] = (), recovery: dict | None = None,
|
|
889
1081
|
) -> None:
|
|
890
|
-
|
|
1082
|
+
payload = {
|
|
891
1083
|
"schemaVersion": 1,
|
|
892
1084
|
"issueId": issue_id,
|
|
893
1085
|
"activity": activity,
|
|
@@ -897,7 +1089,10 @@ def _print_execution_json(
|
|
|
897
1089
|
"nextActivity": next_activity,
|
|
898
1090
|
"exitCode": int(exit_code),
|
|
899
1091
|
"warnings": list(warnings),
|
|
900
|
-
}
|
|
1092
|
+
}
|
|
1093
|
+
if recovery is not None:
|
|
1094
|
+
payload["recovery"] = recovery
|
|
1095
|
+
typer.echo(json.dumps(payload, ensure_ascii=False))
|
|
901
1096
|
|
|
902
1097
|
|
|
903
1098
|
def _print_warnings(warnings: tuple[str, ...]) -> None:
|
|
@@ -978,6 +1173,8 @@ def worklog(
|
|
|
978
1173
|
) -> None:
|
|
979
1174
|
"""Regenere le journal compact depuis les artefacts du workflow."""
|
|
980
1175
|
feature_dir = _feature(feature)
|
|
1176
|
+
with workflow_lock(feature_dir):
|
|
1177
|
+
refuse_if_agent_running(feature_dir)
|
|
981
1178
|
try:
|
|
982
1179
|
output = refresh_worklog(feature_dir)
|
|
983
1180
|
except LegacySchemaError as exc:
|
|
@@ -1204,6 +1401,14 @@ def _print_run_recovery(
|
|
|
1204
1401
|
)
|
|
1205
1402
|
console.print(f" devflow adaptive run {issue_id} --agent {agent}")
|
|
1206
1403
|
return
|
|
1404
|
+
if reason == "agent-running":
|
|
1405
|
+
console.print(" Un agent execute deja cette activite : attendez sa fin.")
|
|
1406
|
+
return
|
|
1407
|
+
if reason == "invalid-result" and activity in MANUAL_RECONCILIATION_ACTIVITIES:
|
|
1408
|
+
console.print(
|
|
1409
|
+
f" devflow adaptive reconcile {issue_id} --result FICHIER.json --reason MOTIF"
|
|
1410
|
+
" # corriger la reponse refusee sans relancer l'agent"
|
|
1411
|
+
)
|
|
1207
1412
|
if reason == "agent-failed":
|
|
1208
1413
|
console.print(f" devflow adaptive reconcile {issue_id} # si la trace contient une reponse finale")
|
|
1209
1414
|
console.print(f" devflow check --ai {agent}")
|
devflow_cli/commands/verify.py
CHANGED
|
@@ -13,7 +13,8 @@ from devflow_cli.core.verification import (
|
|
|
13
13
|
parse_validation_commands,
|
|
14
14
|
run_verifications,
|
|
15
15
|
)
|
|
16
|
-
from devflow_cli.
|
|
16
|
+
from devflow_cli.commands.adaptive import refuse_if_agent_running
|
|
17
|
+
from devflow_cli.core.adaptive_workflow import read_adaptive_state, workflow_lock
|
|
17
18
|
from devflow_cli.core.automatic_evidence import run_automatic_verification
|
|
18
19
|
from devflow_cli.utils.console import console, fail, ok, warn
|
|
19
20
|
from devflow_cli.utils.paths import project_root_for_feature, resolve_feature_dir
|
|
@@ -44,6 +45,8 @@ def verify(
|
|
|
44
45
|
"ISSUE_ID VAL-ID --reason MOTIF."
|
|
45
46
|
)
|
|
46
47
|
raise typer.Exit(1)
|
|
48
|
+
with workflow_lock(feature_dir):
|
|
49
|
+
refuse_if_agent_running(feature_dir)
|
|
47
50
|
outcome = run_automatic_verification(
|
|
48
51
|
feature_dir, project_root_for_feature(feature_dir)
|
|
49
52
|
)
|
|
@@ -44,6 +44,7 @@ RUNTIME_FILE_NAMES = frozenset({
|
|
|
44
44
|
"workflow-state.json", ".workflow-state.lock", "activity-contract.json",
|
|
45
45
|
"agent-execution.json", "worklog.md", "reopen-log.json", "attempt-discard-log.json",
|
|
46
46
|
"reconcile-log.json", "validation-approvals.json", "waivers.json", "verification.json",
|
|
47
|
+
".agent-run.lock",
|
|
47
48
|
})
|
|
48
49
|
RUNTIME_FILE_SUFFIXES = ("-result.json", "-attempt.json", ".raw.txt")
|
|
49
50
|
RUNTIME_DIRECTORIES = ("attempts", "evidence")
|