devflow-cli 10.0.2__py3-none-any.whl → 10.1.1__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 +16 -4
- devflow_cli/core/adaptive_runner.py +167 -48
- devflow_cli/core/contract_schemas.py +1 -0
- devflow_cli/core/validators.py +0 -9
- devflow_cli/resources/commands/devflow.md +13 -1
- devflow_cli/schemas/reconcile-log-v3.schema.json +82 -0
- devflow_cli-10.1.1.dist-info/METADATA +167 -0
- {devflow_cli-10.0.2.dist-info → devflow_cli-10.1.1.dist-info}/RECORD +12 -11
- devflow_cli-10.0.2.dist-info/METADATA +0 -212
- {devflow_cli-10.0.2.dist-info → devflow_cli-10.1.1.dist-info}/WHEEL +0 -0
- {devflow_cli-10.0.2.dist-info → devflow_cli-10.1.1.dist-info}/entry_points.txt +0 -0
- {devflow_cli-10.0.2.dist-info → devflow_cli-10.1.1.dist-info}/licenses/LICENSE +0 -0
devflow_cli/__init__.py
CHANGED
devflow_cli/commands/adaptive.py
CHANGED
|
@@ -367,7 +367,7 @@ def run_adaptive(
|
|
|
367
367
|
output_file,
|
|
368
368
|
permission_mode,
|
|
369
369
|
"workspace-write"
|
|
370
|
-
if "
|
|
370
|
+
if "execute" in activity_contract.allowedActions
|
|
371
371
|
else "read-only",
|
|
372
372
|
)
|
|
373
373
|
attempt["execution"] = execution
|
|
@@ -540,7 +540,10 @@ def reconcile_adaptive(
|
|
|
540
540
|
reason: Optional[str] = typer.Option(
|
|
541
541
|
None,
|
|
542
542
|
"--reason",
|
|
543
|
-
help=
|
|
543
|
+
help=(
|
|
544
|
+
"Motif obligatoire avec --result ; seul, il consigne l'artefact corrige "
|
|
545
|
+
"d'une tentative refusee invalid-artifact"
|
|
546
|
+
),
|
|
544
547
|
),
|
|
545
548
|
) -> None:
|
|
546
549
|
"""Valide sans relancer l'agent le travail d'une tentative agent echouee."""
|
|
@@ -561,8 +564,8 @@ def reconcile_adaptive(
|
|
|
561
564
|
except (OSError, UnicodeDecodeError, json.JSONDecodeError, ValueError) as exc:
|
|
562
565
|
fail(f"Resultat corrige invalide : {exc}")
|
|
563
566
|
raise typer.Exit(int(ExitCode.USAGE)) from exc
|
|
564
|
-
elif reason is not None:
|
|
565
|
-
fail("--reason ne peut etre
|
|
567
|
+
elif reason is not None and not reason.strip():
|
|
568
|
+
fail("--reason ne peut pas etre vide")
|
|
566
569
|
raise typer.Exit(int(ExitCode.USAGE))
|
|
567
570
|
|
|
568
571
|
missing_output: dict[str, str] = {}
|
|
@@ -1577,6 +1580,15 @@ def _print_run_recovery(
|
|
|
1577
1580
|
f" devflow adaptive reconcile {issue_id} --result FICHIER.json --reason MOTIF"
|
|
1578
1581
|
" # corriger la reponse refusee sans relancer l'agent"
|
|
1579
1582
|
)
|
|
1583
|
+
if reason == "invalid-artifact" and activity in DOCUMENT_ARTIFACTS:
|
|
1584
|
+
console.print(
|
|
1585
|
+
" Corriger l'artefact refuse (aucun autre fichier), puis :"
|
|
1586
|
+
)
|
|
1587
|
+
console.print(
|
|
1588
|
+
f" devflow adaptive reconcile {issue_id} --reason MOTIF"
|
|
1589
|
+
" # consigner la correction sans relancer l'agent",
|
|
1590
|
+
markup=False, soft_wrap=True,
|
|
1591
|
+
)
|
|
1580
1592
|
if reason == "agent-failed":
|
|
1581
1593
|
console.print(f" devflow adaptive reconcile {issue_id} # si la trace contient une reponse finale")
|
|
1582
1594
|
_print_check_command(" # verifier l'agent")
|
|
@@ -64,7 +64,10 @@ from devflow_cli.core.adaptive_workflow import (
|
|
|
64
64
|
)
|
|
65
65
|
from devflow_cli.adapters.agents import SUPPORTED_AGENT_NAMES
|
|
66
66
|
from devflow_cli.core.assessment import read_assessment
|
|
67
|
-
from devflow_cli.core.contract_schemas import
|
|
67
|
+
from devflow_cli.core.contract_schemas import (
|
|
68
|
+
ATTEMPT_SCHEMA_VERSION,
|
|
69
|
+
validate_contract_payload,
|
|
70
|
+
)
|
|
68
71
|
from devflow_cli.core.worklog import safe_refresh_worklog
|
|
69
72
|
from devflow_cli.core.traceability import analyze_traceability
|
|
70
73
|
from devflow_cli.core.validators import validate_artifact
|
|
@@ -407,6 +410,12 @@ def reconcile_current_activity(
|
|
|
407
410
|
f"adaptive run {shlex.quote(state.issueId)}"
|
|
408
411
|
)
|
|
409
412
|
attempt_reason = attempt["reason"]
|
|
413
|
+
if (
|
|
414
|
+
corrected_payload is None
|
|
415
|
+
and correction_reason.strip()
|
|
416
|
+
and (attempt_reason != "invalid-artifact" or activity not in DOCUMENT_ARTIFACTS)
|
|
417
|
+
):
|
|
418
|
+
raise ValueError(REASON_WITHOUT_RESULT_MESSAGE)
|
|
410
419
|
observed_files = attempt["observedFiles"]
|
|
411
420
|
execution_files = attempt["executionFiles"]
|
|
412
421
|
imputed = imputed_inputs(
|
|
@@ -446,21 +455,47 @@ def reconcile_current_activity(
|
|
|
446
455
|
raise ValueError("HEAD ou la branche a change depuis la tentative")
|
|
447
456
|
# N6 : pour une activite documentaire, un fichier de la fiche autre que
|
|
448
457
|
# l'artefact peut etre revenu a son etat de base (fichier en trop retire).
|
|
449
|
-
|
|
450
|
-
|
|
458
|
+
artifact_name = (
|
|
459
|
+
document_artifact_name(activity, reopened=contract_is_reopened_plan(contract))
|
|
451
460
|
if activity in DOCUMENT_ARTIFACTS
|
|
452
461
|
else None
|
|
453
462
|
)
|
|
463
|
+
artifact = f"{feature_path}/{artifact_name}" if artifact_name else None
|
|
454
464
|
drifted = sorted(
|
|
455
465
|
path
|
|
456
466
|
for path in set(observed_files) | set(since_base)
|
|
457
467
|
if observed_files.get(path) != since_base.get(path)
|
|
458
468
|
and (artifact is None or path == artifact or path in since_base)
|
|
459
469
|
)
|
|
460
|
-
|
|
470
|
+
# D2 : l'artefact refuse (invalid-artifact) puis corrige a la main est la
|
|
471
|
+
# seule derive acceptee, sous --reason et si le validateur de run l'accepte.
|
|
472
|
+
artifact_correction = (
|
|
473
|
+
corrected_payload is None
|
|
474
|
+
and artifact is not None
|
|
475
|
+
and attempt_reason == "invalid-artifact"
|
|
476
|
+
and drifted == [artifact]
|
|
477
|
+
and since_base.get(artifact) is not None
|
|
478
|
+
and (feature_dir / artifact_name).is_file()
|
|
479
|
+
)
|
|
480
|
+
if artifact_correction:
|
|
481
|
+
if not correction_reason.strip():
|
|
482
|
+
raise ValueError(
|
|
483
|
+
"artefact corrige depuis la tentative : --reason est obligatoire "
|
|
484
|
+
"pour consigner la correction"
|
|
485
|
+
)
|
|
486
|
+
artifact_error = _artifact_validation_error(
|
|
487
|
+
feature_dir, activity, artifact_name
|
|
488
|
+
)
|
|
489
|
+
if artifact_error is not None:
|
|
490
|
+
return AdaptiveRunOutcome(
|
|
491
|
+
"invalid-artifact", activity, activity, artifact_error
|
|
492
|
+
)
|
|
493
|
+
elif drifted:
|
|
461
494
|
raise ValueError(
|
|
462
495
|
"fichiers modifies depuis la tentative : " + ", ".join(drifted)
|
|
463
496
|
)
|
|
497
|
+
elif corrected_payload is None and correction_reason.strip():
|
|
498
|
+
raise ValueError(REASON_WITHOUT_RESULT_MESSAGE)
|
|
464
499
|
raw_output = activity_raw_file(feature_dir, activity).read_text(encoding="utf-8")
|
|
465
500
|
if _sha256(raw_output) != attempt["rawOutputSha256"]:
|
|
466
501
|
raise ValueError("trace brute modifiee depuis la tentative")
|
|
@@ -477,6 +512,28 @@ def reconcile_current_activity(
|
|
|
477
512
|
state,
|
|
478
513
|
during_execution=[*observed_files, *execution_files],
|
|
479
514
|
)
|
|
515
|
+
if artifact_correction:
|
|
516
|
+
if recovery_error:
|
|
517
|
+
return AdaptiveRunOutcome(
|
|
518
|
+
"invalid-result", activity, activity, recovery_error
|
|
519
|
+
)
|
|
520
|
+
result_error = _validation_error(
|
|
521
|
+
feature_dir, contract, recovered, sorted(observation.sinceBase)
|
|
522
|
+
)
|
|
523
|
+
if result_error is not None:
|
|
524
|
+
return AdaptiveRunOutcome(
|
|
525
|
+
"invalid-result", activity, activity, result_error
|
|
526
|
+
)
|
|
527
|
+
_record_artifact_correction(
|
|
528
|
+
feature_dir,
|
|
529
|
+
state.issueId,
|
|
530
|
+
contract,
|
|
531
|
+
artifact,
|
|
532
|
+
observed_files.get(artifact),
|
|
533
|
+
since_base[artifact],
|
|
534
|
+
correction_reason,
|
|
535
|
+
correction_author,
|
|
536
|
+
)
|
|
480
537
|
if corrected_payload is not None:
|
|
481
538
|
observed = sorted(observation.sinceBase)
|
|
482
539
|
agent_payload = (
|
|
@@ -665,21 +722,11 @@ def _apply_activity_result(
|
|
|
665
722
|
artifact_name = document_artifact_name(
|
|
666
723
|
contract.activity, reopened=contract_is_reopened_plan(contract)
|
|
667
724
|
)
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
validation = None
|
|
674
|
-
else:
|
|
675
|
-
validation = validate_artifact(
|
|
676
|
-
contract.activity, feature_dir / artifact_name
|
|
677
|
-
)
|
|
678
|
-
if validation is not None and not validation.valid:
|
|
679
|
-
return refuse(
|
|
680
|
-
"invalid-artifact",
|
|
681
|
-
"; ".join(issue.message for issue in validation.issues),
|
|
682
|
-
)
|
|
725
|
+
artifact_error = _artifact_validation_error(
|
|
726
|
+
feature_dir, contract.activity, artifact_name
|
|
727
|
+
)
|
|
728
|
+
if artifact_error is not None:
|
|
729
|
+
return refuse("invalid-artifact", artifact_error)
|
|
683
730
|
if contract.activity == "tasks":
|
|
684
731
|
try:
|
|
685
732
|
traceability = analyze_traceability(
|
|
@@ -846,6 +893,22 @@ def _validated_result(
|
|
|
846
893
|
return result
|
|
847
894
|
|
|
848
895
|
|
|
896
|
+
def _artifact_validation_error(
|
|
897
|
+
feature_dir: Path, activity: str, artifact_name: str
|
|
898
|
+
) -> str | None:
|
|
899
|
+
"""Motif de refus d'un artefact documentaire, le meme pour run et reconcile."""
|
|
900
|
+
if artifact_name == "preflight-evidence.md":
|
|
901
|
+
try:
|
|
902
|
+
validate_preflight_evidence(feature_dir / artifact_name)
|
|
903
|
+
except ValueError as exc:
|
|
904
|
+
return str(exc)
|
|
905
|
+
return None
|
|
906
|
+
validation = validate_artifact(activity, feature_dir / artifact_name)
|
|
907
|
+
if validation.valid:
|
|
908
|
+
return None
|
|
909
|
+
return "; ".join(issue.message for issue in validation.issues)
|
|
910
|
+
|
|
911
|
+
|
|
849
912
|
def _validation_error(
|
|
850
913
|
feature_dir: Path,
|
|
851
914
|
contract: ActivityContract,
|
|
@@ -1045,14 +1108,68 @@ def _sha256(content: str) -> str:
|
|
|
1045
1108
|
return hashlib.sha256(content.encode("utf-8")).hexdigest()
|
|
1046
1109
|
|
|
1047
1110
|
|
|
1048
|
-
# Schema
|
|
1049
|
-
RECONCILE_LOG_SCHEMA_VERSION =
|
|
1111
|
+
# Schema 3 : chaque entree porte kind ("result" ou "artifact", D3 de KKS-560).
|
|
1112
|
+
RECONCILE_LOG_SCHEMA_VERSION = 3
|
|
1050
1113
|
RECONCILE_LOG_LEGACY_MESSAGE = (
|
|
1051
1114
|
"reconcile-log.json est au schema 1 (devflow 8.0.0 ou anterieure) et n'est pas "
|
|
1052
1115
|
"lu par cette version : terminer ce parcours avec devflow 8.0.0 "
|
|
1053
1116
|
"(uv tool install devflow-cli==8.0.0), ou deplacer ce fichier dans attempts/ "
|
|
1054
1117
|
"pour conserver son historique et commencer un nouveau journal"
|
|
1055
1118
|
)
|
|
1119
|
+
RECONCILE_LOG_SCHEMA_2_MESSAGE = (
|
|
1120
|
+
"reconcile-log.json est au schema 2 (devflow 10.0.2 ou anterieure) et n'est pas "
|
|
1121
|
+
"lu par cette version : terminer ce parcours avec devflow 10.0.2 "
|
|
1122
|
+
"(uv tool install devflow-cli==10.0.2), ou deplacer ce fichier dans attempts/ "
|
|
1123
|
+
"pour conserver son historique et commencer un nouveau journal"
|
|
1124
|
+
)
|
|
1125
|
+
REASON_WITHOUT_RESULT_MESSAGE = (
|
|
1126
|
+
"--reason ne peut etre utilise qu'avec --result, ou sans --result pour consigner "
|
|
1127
|
+
"l'artefact corrige d'une tentative refusee invalid-artifact"
|
|
1128
|
+
)
|
|
1129
|
+
|
|
1130
|
+
|
|
1131
|
+
def _load_reconcile_log(feature_dir: Path, issue_id: str) -> tuple[Path, dict]:
|
|
1132
|
+
"""Charge le journal ou en cree un vide ; nomme l'invariant viole, sans ecrire."""
|
|
1133
|
+
path = feature_dir / "reconcile-log.json"
|
|
1134
|
+
if not path.is_file():
|
|
1135
|
+
return path, {
|
|
1136
|
+
"schemaVersion": RECONCILE_LOG_SCHEMA_VERSION,
|
|
1137
|
+
"issueId": issue_id,
|
|
1138
|
+
"entries": [],
|
|
1139
|
+
}
|
|
1140
|
+
log = json.loads(path.read_text(encoding="utf-8"))
|
|
1141
|
+
if isinstance(log, dict) and log.get("schemaVersion") == 1:
|
|
1142
|
+
raise ValueError(RECONCILE_LOG_LEGACY_MESSAGE)
|
|
1143
|
+
if isinstance(log, dict) and log.get("schemaVersion") == 2:
|
|
1144
|
+
raise ValueError(RECONCILE_LOG_SCHEMA_2_MESSAGE)
|
|
1145
|
+
if (
|
|
1146
|
+
not isinstance(log, dict)
|
|
1147
|
+
or log.get("schemaVersion") != RECONCILE_LOG_SCHEMA_VERSION
|
|
1148
|
+
or log.get("issueId") != issue_id
|
|
1149
|
+
or not isinstance(log.get("entries"), list)
|
|
1150
|
+
or not all(isinstance(entry, dict) for entry in log["entries"])
|
|
1151
|
+
):
|
|
1152
|
+
raise ValueError("reconcile-log.json invalide")
|
|
1153
|
+
return path, log
|
|
1154
|
+
|
|
1155
|
+
|
|
1156
|
+
def _append_reconcile_entry(path: Path, log: dict, entry: dict) -> None:
|
|
1157
|
+
"""Valide le journal complet contre son schema avant toute ecriture."""
|
|
1158
|
+
log["entries"].append(entry)
|
|
1159
|
+
validate_contract_payload("reconcile-log", log)
|
|
1160
|
+
_write_json_atomic(path, log)
|
|
1161
|
+
|
|
1162
|
+
|
|
1163
|
+
def _entry_header(log: dict, contract: ActivityContract, kind: str, reason: str, author: str) -> dict:
|
|
1164
|
+
return {
|
|
1165
|
+
"sequence": len(log["entries"]) + 1,
|
|
1166
|
+
"kind": kind,
|
|
1167
|
+
"contractId": contract.contractId,
|
|
1168
|
+
"activity": contract.activity,
|
|
1169
|
+
"recordedAt": datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ"),
|
|
1170
|
+
"author": author.strip() or os.environ.get("USER", "unknown"),
|
|
1171
|
+
"reason": reason.strip(),
|
|
1172
|
+
}
|
|
1056
1173
|
|
|
1057
1174
|
|
|
1058
1175
|
def _record_manual_correction(
|
|
@@ -1070,33 +1187,10 @@ def _record_manual_correction(
|
|
|
1070
1187
|
``agentPayloadSha256`` et ``changedFields`` ne figurent que si la trace contenait
|
|
1071
1188
|
une reponse de l'agent pour ce contrat (refusee par les validateurs, B1).
|
|
1072
1189
|
"""
|
|
1073
|
-
path = feature_dir
|
|
1074
|
-
if path.is_file():
|
|
1075
|
-
log = json.loads(path.read_text(encoding="utf-8"))
|
|
1076
|
-
if isinstance(log, dict) and log.get("schemaVersion") == 1:
|
|
1077
|
-
raise ValueError(RECONCILE_LOG_LEGACY_MESSAGE)
|
|
1078
|
-
if (
|
|
1079
|
-
not isinstance(log, dict)
|
|
1080
|
-
or log.get("schemaVersion") != RECONCILE_LOG_SCHEMA_VERSION
|
|
1081
|
-
or log.get("issueId") != issue_id
|
|
1082
|
-
or not isinstance(log.get("entries"), list)
|
|
1083
|
-
or not all(isinstance(entry, dict) for entry in log["entries"])
|
|
1084
|
-
):
|
|
1085
|
-
raise ValueError("reconcile-log.json invalide")
|
|
1086
|
-
else:
|
|
1087
|
-
log = {
|
|
1088
|
-
"schemaVersion": RECONCILE_LOG_SCHEMA_VERSION,
|
|
1089
|
-
"issueId": issue_id,
|
|
1090
|
-
"entries": [],
|
|
1091
|
-
}
|
|
1190
|
+
path, log = _load_reconcile_log(feature_dir, issue_id)
|
|
1092
1191
|
canonical = json.dumps(payload, sort_keys=True, ensure_ascii=False)
|
|
1093
1192
|
entry = {
|
|
1094
|
-
|
|
1095
|
-
"contractId": contract.contractId,
|
|
1096
|
-
"activity": contract.activity,
|
|
1097
|
-
"recordedAt": datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ"),
|
|
1098
|
-
"author": author.strip() or os.environ.get("USER", "unknown"),
|
|
1099
|
-
"reason": reason.strip(),
|
|
1193
|
+
**_entry_header(log, contract, "result", reason, author),
|
|
1100
1194
|
"payloadSha256": _sha256(canonical),
|
|
1101
1195
|
"payload": payload,
|
|
1102
1196
|
"source": "manual-correction",
|
|
@@ -1110,8 +1204,33 @@ def _record_manual_correction(
|
|
|
1110
1204
|
for key in set(agent_payload) | set(payload)
|
|
1111
1205
|
if agent_payload.get(key, _MISSING) != payload.get(key, _MISSING)
|
|
1112
1206
|
)
|
|
1113
|
-
log
|
|
1114
|
-
|
|
1207
|
+
_append_reconcile_entry(path, log, entry)
|
|
1208
|
+
|
|
1209
|
+
|
|
1210
|
+
def _record_artifact_correction(
|
|
1211
|
+
feature_dir: Path,
|
|
1212
|
+
issue_id: str,
|
|
1213
|
+
contract: ActivityContract,
|
|
1214
|
+
artifact: str,
|
|
1215
|
+
attempt_fingerprint: str | None,
|
|
1216
|
+
corrected_fingerprint: str,
|
|
1217
|
+
reason: str,
|
|
1218
|
+
author: str,
|
|
1219
|
+
) -> None:
|
|
1220
|
+
"""Consigne l'artefact corrige a la main apres un refus invalid-artifact (D2).
|
|
1221
|
+
|
|
1222
|
+
``attemptFingerprint`` est l'empreinte de la fiche de tentative (None si l'artefact n'y
|
|
1223
|
+
figure pas), ``correctedFingerprint`` celle de l'artefact au moment de la reprise.
|
|
1224
|
+
"""
|
|
1225
|
+
path, log = _load_reconcile_log(feature_dir, issue_id)
|
|
1226
|
+
entry = {
|
|
1227
|
+
**_entry_header(log, contract, "artifact", reason, author),
|
|
1228
|
+
"path": artifact,
|
|
1229
|
+
"attemptFingerprint": attempt_fingerprint,
|
|
1230
|
+
"correctedFingerprint": corrected_fingerprint,
|
|
1231
|
+
"source": "manual-correction",
|
|
1232
|
+
}
|
|
1233
|
+
_append_reconcile_entry(path, log, entry)
|
|
1115
1234
|
|
|
1116
1235
|
|
|
1117
1236
|
_MISSING = object()
|
|
@@ -18,6 +18,7 @@ PUBLIC_CONTRACT_SCHEMAS = {
|
|
|
18
18
|
"workflow-state": "workflow-state-v6.schema.json",
|
|
19
19
|
"activity-contract": "activity-contract-v4.schema.json",
|
|
20
20
|
"activity-result": "activity-result-v2.schema.json",
|
|
21
|
+
"reconcile-log": "reconcile-log-v3.schema.json",
|
|
21
22
|
}
|
|
22
23
|
# Fiche de tentative (<activite>-attempt.json) : seule definition (CT-04).
|
|
23
24
|
ATTEMPT_SCHEMA_VERSION = 3
|
devflow_cli/core/validators.py
CHANGED
|
@@ -71,7 +71,6 @@ ARTIFACT_RULES: dict[str, dict] = {
|
|
|
71
71
|
_PLACEHOLDER_BRACKET = re.compile(
|
|
72
72
|
r"\[(?:TBD|TODO|XXX|[A-Z][A-Z\s]{3,})\](?!\()"
|
|
73
73
|
)
|
|
74
|
-
_PLACEHOLDER_MUSTACHE = re.compile(r"(?<!\$)\{\{[^}]+\}\}")
|
|
75
74
|
_FENCE = re.compile(r"^ {0,3}(?P<marker>`{3,}|~{3,})")
|
|
76
75
|
_INLINE_CODE = re.compile(r"(?P<ticks>`+)[^\r\n]*?(?P=ticks)")
|
|
77
76
|
|
|
@@ -169,14 +168,6 @@ def validate_artifact(step: str, artifact_path: Path) -> ValidationResult:
|
|
|
169
168
|
f"{match.group()}"
|
|
170
169
|
),
|
|
171
170
|
))
|
|
172
|
-
for match in _PLACEHOLDER_MUSTACHE.finditer(prose):
|
|
173
|
-
issues.append(ValidationIssue(
|
|
174
|
-
type="placeholder",
|
|
175
|
-
message=(
|
|
176
|
-
f"Placeholder non-remplace ligne {_line_number(prose, match.start())}: "
|
|
177
|
-
f"{match.group()}"
|
|
178
|
-
),
|
|
179
|
-
))
|
|
180
171
|
|
|
181
172
|
return ValidationResult(valid=len(issues) == 0, issues=issues)
|
|
182
173
|
|
|
@@ -9,6 +9,9 @@ allowed-tools: Bash(devflow:*)
|
|
|
9
9
|
Utiliser exclusivement la machine adaptative de la CLI. Ne jamais créer ni
|
|
10
10
|
interpréter de `state.json`.
|
|
11
11
|
|
|
12
|
+
Avant toute chose, exécuter `devflow check`. En échec, ne pas utiliser
|
|
13
|
+
Devflow : le dire, avec la cause, et traiter la demande hors Devflow.
|
|
14
|
+
|
|
12
15
|
1. Lire `$ARGUMENTS` et extraire l'identifiant de feature et la cible `--until`
|
|
13
16
|
(`done` par défaut).
|
|
14
17
|
2. Exécuter `devflow status ISSUE_ID`.
|
|
@@ -17,7 +20,13 @@ interpréter de `state.json`.
|
|
|
17
20
|
l'issue) et annoncer la branche et le dossier créés.
|
|
18
21
|
4. Si la feature n'est pas encore évaluée et que `need.md` n'existe pas dans
|
|
19
22
|
son dossier, le rédiger à partir de l'issue et de la demande. En cas de
|
|
20
|
-
doute sur le besoin, poser la question avant de l'écrire.
|
|
23
|
+
doute sur le besoin, poser la question avant de l'écrire. Le besoin dit :
|
|
24
|
+
1. les constats observés, avec leur origine ;
|
|
25
|
+
2. les décisions prises, non rediscutables pendant le parcours ;
|
|
26
|
+
3. l'attendu et les tests ;
|
|
27
|
+
4. la livraison (version, fichiers de version) ;
|
|
28
|
+
5. le hors périmètre ;
|
|
29
|
+
6. les contraintes d'exécution.
|
|
21
30
|
5. Si elle n'est pas évaluée, demander les signaux nécessaires puis lancer
|
|
22
31
|
`devflow assess ISSUE_ID` avec des options explicites.
|
|
23
32
|
6. Si elle est évaluée mais non démarrée, lancer
|
|
@@ -59,3 +68,6 @@ devflow feedback add --type bilan --issue <ISSUE> --what "…" \
|
|
|
59
68
|
|
|
60
69
|
Le contrat produit par Devflow est la seule autorité sur les actions,
|
|
61
70
|
artefacts, validations et preuves de l'activité.
|
|
71
|
+
|
|
72
|
+
Si le projet a une constitution (`.specify/memory/constitution.md`), la lire
|
|
73
|
+
avant d'implémenter : elle prime sur spec, plan et tâches.
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://devflow.dev/schemas/reconcile-log/v3",
|
|
4
|
+
"title": "Devflow Reconcile Log v3",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"required": ["schemaVersion", "issueId", "entries"],
|
|
7
|
+
"properties": {
|
|
8
|
+
"schemaVersion": {"type": "integer", "const": 3},
|
|
9
|
+
"issueId": {"type": "string", "minLength": 1},
|
|
10
|
+
"entries": {
|
|
11
|
+
"type": "array",
|
|
12
|
+
"items": {
|
|
13
|
+
"oneOf": [
|
|
14
|
+
{
|
|
15
|
+
"type": "object",
|
|
16
|
+
"required": [
|
|
17
|
+
"sequence",
|
|
18
|
+
"kind",
|
|
19
|
+
"contractId",
|
|
20
|
+
"activity",
|
|
21
|
+
"recordedAt",
|
|
22
|
+
"author",
|
|
23
|
+
"reason",
|
|
24
|
+
"payloadSha256",
|
|
25
|
+
"payload",
|
|
26
|
+
"source"
|
|
27
|
+
],
|
|
28
|
+
"properties": {
|
|
29
|
+
"sequence": {"type": "integer", "minimum": 1},
|
|
30
|
+
"kind": {"type": "string", "const": "result"},
|
|
31
|
+
"contractId": {"type": "string", "minLength": 1},
|
|
32
|
+
"activity": {"type": "string", "minLength": 1},
|
|
33
|
+
"recordedAt": {"type": "string", "minLength": 1},
|
|
34
|
+
"author": {"type": "string"},
|
|
35
|
+
"reason": {"type": "string", "minLength": 1},
|
|
36
|
+
"payloadSha256": {"type": "string", "pattern": "^[0-9a-f]{64}$"},
|
|
37
|
+
"payload": {"type": "object"},
|
|
38
|
+
"source": {"type": "string", "const": "manual-correction"},
|
|
39
|
+
"agentPayloadSha256": {"type": "string", "pattern": "^[0-9a-f]{64}$"},
|
|
40
|
+
"changedFields": {
|
|
41
|
+
"type": "array",
|
|
42
|
+
"items": {"type": "string"}
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
"additionalProperties": false
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
"type": "object",
|
|
49
|
+
"required": [
|
|
50
|
+
"sequence",
|
|
51
|
+
"kind",
|
|
52
|
+
"contractId",
|
|
53
|
+
"activity",
|
|
54
|
+
"recordedAt",
|
|
55
|
+
"author",
|
|
56
|
+
"reason",
|
|
57
|
+
"path",
|
|
58
|
+
"attemptFingerprint",
|
|
59
|
+
"correctedFingerprint",
|
|
60
|
+
"source"
|
|
61
|
+
],
|
|
62
|
+
"properties": {
|
|
63
|
+
"sequence": {"type": "integer", "minimum": 1},
|
|
64
|
+
"kind": {"type": "string", "const": "artifact"},
|
|
65
|
+
"contractId": {"type": "string", "minLength": 1},
|
|
66
|
+
"activity": {"type": "string", "minLength": 1},
|
|
67
|
+
"recordedAt": {"type": "string", "minLength": 1},
|
|
68
|
+
"author": {"type": "string"},
|
|
69
|
+
"reason": {"type": "string", "minLength": 1},
|
|
70
|
+
"path": {"type": "string", "minLength": 1},
|
|
71
|
+
"attemptFingerprint": {"type": ["string", "null"]},
|
|
72
|
+
"correctedFingerprint": {"type": "string", "minLength": 1},
|
|
73
|
+
"source": {"type": "string", "const": "manual-correction"}
|
|
74
|
+
},
|
|
75
|
+
"additionalProperties": false
|
|
76
|
+
}
|
|
77
|
+
]
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
},
|
|
81
|
+
"additionalProperties": false
|
|
82
|
+
}
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: devflow-cli
|
|
3
|
+
Version: 10.1.1
|
|
4
|
+
Summary: Workflow adaptatif pour Claude Code : profils, preuves et contrats d'activité
|
|
5
|
+
Project-URL: Homepage, https://github.com/sopequenoteck/devflow
|
|
6
|
+
Project-URL: Repository, https://github.com/sopequenoteck/devflow
|
|
7
|
+
Author-email: sopequenoteck <sopequeno.tech@gmail.com>
|
|
8
|
+
License: MIT
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Keywords: adaptive,claude-code,cli,workflow
|
|
11
|
+
Classifier: Development Status :: 4 - Beta
|
|
12
|
+
Classifier: Environment :: Console
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
17
|
+
Classifier: Topic :: Software Development :: Quality Assurance
|
|
18
|
+
Requires-Python: >=3.11
|
|
19
|
+
Requires-Dist: jsonschema>=4.23
|
|
20
|
+
Requires-Dist: pyyaml>=6
|
|
21
|
+
Requires-Dist: rich>=13
|
|
22
|
+
Requires-Dist: typer>=0.12
|
|
23
|
+
Provides-Extra: dev
|
|
24
|
+
Requires-Dist: hatchling>=1.27; extra == 'dev'
|
|
25
|
+
Requires-Dist: pytest-cov>=5; extra == 'dev'
|
|
26
|
+
Requires-Dist: pytest>=8; extra == 'dev'
|
|
27
|
+
Description-Content-Type: text/markdown
|
|
28
|
+
|
|
29
|
+
# devflow
|
|
30
|
+
|
|
31
|
+
Devflow est un workflow adaptatif pour Claude Code. Il s'adresse au développeur
|
|
32
|
+
qui fait travailler Claude Code sur ses projets et veut une rigueur
|
|
33
|
+
proportionnée au risque : la CLI choisit un profil à partir de signaux
|
|
34
|
+
explicites, confie chaque activité à un agent sous un contrat écrit et ne
|
|
35
|
+
franchit une étape que sur des preuves qu'elle produit elle-même.
|
|
36
|
+
|
|
37
|
+
Un parcours produit une branche Git et un dossier de feature, un besoin écrit
|
|
38
|
+
(`need.md`), les résultats de chaque activité, des preuves exécutées
|
|
39
|
+
localement (tests, inspection du diff, revue) et un journal des retours
|
|
40
|
+
d'usage (frictions et bilan).
|
|
41
|
+
|
|
42
|
+
## Installer
|
|
43
|
+
|
|
44
|
+
Prérequis : Python 3.11+, Git et la CLI `claude` (Claude Code).
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
uv tool install --refresh devflow-cli==X.Y.Z # version publiée sur PyPI
|
|
48
|
+
devflow init
|
|
49
|
+
devflow check
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
`devflow init` installe les commandes `/devflow` et `/devflow.status` dans
|
|
53
|
+
`~/.claude` ; `devflow check` vérifie les prérequis. Installer toujours depuis
|
|
54
|
+
PyPI, à une version précise.
|
|
55
|
+
|
|
56
|
+
Après chaque mise à jour de la CLI, lancer `devflow upgrade` : il met à jour
|
|
57
|
+
les commandes `/devflow` installées dans `~/.claude`, que l'installation de la
|
|
58
|
+
CLI ne touche pas. Tant qu'elles diffèrent de la CLI, `devflow status` affiche
|
|
59
|
+
un avertissement avec cette commande ; rien n'est modifié sans vous.
|
|
60
|
+
`--refresh` évite qu'`uv` ignore une version tout juste publiée. Juste après
|
|
61
|
+
une publication, l'installation peut échouer pendant une à quatre minutes, le
|
|
62
|
+
temps que l'index se propage : réessayer.
|
|
63
|
+
|
|
64
|
+
`devflow init` et `devflow upgrade` déplacent les anciennes commandes Claude
|
|
65
|
+
connues vers `~/.claude/.devflow-legacy-backup/` sans toucher aux commandes
|
|
66
|
+
personnalisées.
|
|
67
|
+
|
|
68
|
+
Une version majeure refuse, sans migration, les parcours démarrés avec une
|
|
69
|
+
version majeure antérieure : terminez-les avant la mise à jour, voir les notes
|
|
70
|
+
de version du dépôt.
|
|
71
|
+
|
|
72
|
+
## Premier parcours
|
|
73
|
+
|
|
74
|
+
Une feature avec un identifiant d'issue passe par Devflow ; un correctif
|
|
75
|
+
ponctuel ou une tâche hors feature, non.
|
|
76
|
+
|
|
77
|
+
Avec Claude Code :
|
|
78
|
+
|
|
79
|
+
```text
|
|
80
|
+
/devflow KS-123
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
La commande crée la feature, rédige `need.md`, demande les signaux de
|
|
84
|
+
l'évaluation puis enchaîne les activités jusqu'à `done`.
|
|
85
|
+
|
|
86
|
+
À la main :
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
devflow feature KS-123 --short-name user-auth
|
|
90
|
+
# Décrire le besoin dans need.md, dans le dossier de la feature créé
|
|
91
|
+
devflow assess KS-123 --scope multiple --ambiguity medium --risk medium
|
|
92
|
+
devflow adaptive start KS-123
|
|
93
|
+
devflow adaptive run KS-123
|
|
94
|
+
devflow adaptive status KS-123
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
Chaque invocation de `adaptive run` traite une activité. `adaptive status`
|
|
98
|
+
affiche la progression, les preuves et la prochaine commande ; `--json` en
|
|
99
|
+
donne une forme exploitable par la CI.
|
|
100
|
+
|
|
101
|
+
## Décrire le besoin
|
|
102
|
+
|
|
103
|
+
`need.md` est l'entrée de tout parcours et fait foi pour chaque activité. Le
|
|
104
|
+
besoin dit :
|
|
105
|
+
|
|
106
|
+
1. les constats observés, avec leur origine ;
|
|
107
|
+
2. les décisions prises, non rediscutables pendant le parcours ;
|
|
108
|
+
3. l'attendu et les tests ;
|
|
109
|
+
4. la livraison (version, fichiers de version) ;
|
|
110
|
+
5. le hors périmètre ;
|
|
111
|
+
6. les contraintes d'exécution.
|
|
112
|
+
|
|
113
|
+
## Lire un refus
|
|
114
|
+
|
|
115
|
+
Chaque refus affiche son motif et la commande de reprise recommandée.
|
|
116
|
+
|
|
117
|
+
- Refus justifié (le travail de l'agent ne satisfait pas l'étape) : corriger
|
|
118
|
+
le travail, puis reprendre par la commande proposée.
|
|
119
|
+
- Échec de Devflow (refus à tort, message trompeur, reprise inadaptée,
|
|
120
|
+
prérequis non documenté) : le consigner aussitôt avec
|
|
121
|
+
`devflow feedback add --type friction`.
|
|
122
|
+
- En fin de parcours, terminé ou abandonné : un bilan avec
|
|
123
|
+
`devflow feedback add --type bilan`.
|
|
124
|
+
|
|
125
|
+
## Profils et garde-fous
|
|
126
|
+
|
|
127
|
+
Le profil fixe la route documentaire :
|
|
128
|
+
|
|
129
|
+
- `quick` : changement local, clair et peu risqué, sans revue.
|
|
130
|
+
- `standard` : plusieurs composants ou risque modéré ; préparation puis revue
|
|
131
|
+
systématique.
|
|
132
|
+
- `deep` : changement transversal, ambigu ou contractuel ; `spec`, `plan`,
|
|
133
|
+
`tasks`, plus `contracts` quand `--contract-change` est déclaré.
|
|
134
|
+
|
|
135
|
+
Un bloc de garde-fous s'y ajoute quand `--data-security` est déclaré ou que
|
|
136
|
+
`--risk high` : évaluation du risque, contrôle bloquant de sécurité ou de
|
|
137
|
+
données à `verify`, revue indépendante, puis vérification du retour arrière
|
|
138
|
+
avant `done`. Un bloc actif impose au minimum le profil `standard`, ne se
|
|
139
|
+
désactive par aucune option et interdit les dérogations.
|
|
140
|
+
|
|
141
|
+
La référence complète (routes, preuves, commandes, contrats d'activité) est
|
|
142
|
+
livrée dans le dépôt, sous `docs/`.
|
|
143
|
+
|
|
144
|
+
## Synchronisation Linear
|
|
145
|
+
|
|
146
|
+
Si `LINEAR_API_KEY` contient une clé d'API personnelle Linear, la CLI met à jour
|
|
147
|
+
le statut de l'issue du parcours au démarrage de chaque activité (`adaptive
|
|
148
|
+
run`, y compris avec `--until`, et clôture `done`). L'équipe Linear doit avoir
|
|
149
|
+
les statuts `Backlog`, `Design`, `In Progress`, `Review` et `Done`. Un statut
|
|
150
|
+
n'est jamais ramené en arrière ; un statut hors de cette liste (`Canceled`,
|
|
151
|
+
`Duplicate`) n'est jamais modifié.
|
|
152
|
+
|
|
153
|
+
Sans clé, ou si Linear échoue (issue inconnue, statut absent, erreur réseau ou
|
|
154
|
+
délai de 5 s dépassé), la CLI affiche un avertissement et le parcours continue
|
|
155
|
+
à l'identique. La clé est retirée de l'environnement de tout processus lancé
|
|
156
|
+
par Devflow (agents, commandes de validation, commandes git) ; elle reste
|
|
157
|
+
lisible par un agent si elle est définie dans un fichier que son shell charge
|
|
158
|
+
(`~/.zshrc`).
|
|
159
|
+
|
|
160
|
+
## Développement
|
|
161
|
+
|
|
162
|
+
```bash
|
|
163
|
+
uv sync --extra dev
|
|
164
|
+
uv run pytest -q
|
|
165
|
+
./tests/run-tests.sh
|
|
166
|
+
uv run devflow docs-sync --check
|
|
167
|
+
```
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
devflow_cli/__init__.py,sha256=
|
|
1
|
+
devflow_cli/__init__.py,sha256=NAGGk9OBe8YXw8PKPqglUP-Ky2xGiIPf0Ine3TMUXl0,871
|
|
2
2
|
devflow_cli/cli.py,sha256=DVLx3wr1Hulke2qxhgHsEvvprRuO7SVETui_VGPNQv4,1983
|
|
3
3
|
devflow_cli/adapters/__init__.py,sha256=z1J7_YJHcgak2a6dTZpOUKKwf-K7UwRqGzS8XY9duC0,299
|
|
4
4
|
devflow_cli/adapters/agents.py,sha256=-9zmNlFmQfP2pOGxA3_SSKij8ScR76J4-sgv4GMBWEI,18530
|
|
5
5
|
devflow_cli/adapters/linear.py,sha256=xHKjMuL3fFJEDBF65pPJ-yWHpXom999UAU2A0C7_QeI,8362
|
|
6
6
|
devflow_cli/commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
7
|
-
devflow_cli/commands/adaptive.py,sha256=
|
|
7
|
+
devflow_cli/commands/adaptive.py,sha256=jogZ_NE0EnuGOzU-w42HM7IxUiF2AYARZWS40HKMkLU,63295
|
|
8
8
|
devflow_cli/commands/assess.py,sha256=V-E3ft41ljCrqp2WLjR1Dqw-JtswcqsK2nD1-euNbNE,5655
|
|
9
9
|
devflow_cli/commands/check.py,sha256=nJOHVFvgT3Nq70g-4QGrSDZVwCSetu6g7snZcfLFf9I,4421
|
|
10
10
|
devflow_cli/commands/docs_sync.py,sha256=Xc0mulpNTjDyeUDumKL7VNG0eumAf3016wEM_dqXpSU,1021
|
|
@@ -16,13 +16,13 @@ devflow_cli/commands/upgrade_cmd.py,sha256=7GYe1T7SWzkDSYW9x1G-Dznw9qwKT2uI1gJUc
|
|
|
16
16
|
devflow_cli/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
17
17
|
devflow_cli/core/activity_base.py,sha256=eEC_YIr5O7WhCglA7xAEPypVzhcdC6f2h9d-SDDecVA,20475
|
|
18
18
|
devflow_cli/core/activity_contracts.py,sha256=Sxu7b6lBQNNrtid0D3b9az8zPI8W4UHVgTQsNc0Y2uI,40328
|
|
19
|
-
devflow_cli/core/adaptive_runner.py,sha256=
|
|
19
|
+
devflow_cli/core/adaptive_runner.py,sha256=AkXtcE-lOjxDnabKdxvv5knx9giZXvvhzuv9LKLS7Mc,49727
|
|
20
20
|
devflow_cli/core/adaptive_workflow.py,sha256=DU8iciNOeEDco5uwQUeX9E9Kc2WwaVPoRfWxdpRJyJ4,14416
|
|
21
21
|
devflow_cli/core/agent_failure.py,sha256=iX4q249lAHeVdD_ZF1aObmU-FX6sjfYP9dfLSnWGAD0,3771
|
|
22
22
|
devflow_cli/core/agent_run_marker.py,sha256=QW3ScA2ZpqfQdUo5ANTbenDVyY2iHpB_wTaN6VUKlzc,3451
|
|
23
23
|
devflow_cli/core/assessment.py,sha256=ACtJbXq1qZjVfeERjy8Qg6-ioLfSMUBVQm2l-mtGwgI,11522
|
|
24
24
|
devflow_cli/core/automatic_evidence.py,sha256=pqSnNXe6RHthII6fgoHlCNkGiFTKQ4yyRtErGUwbvas,42559
|
|
25
|
-
devflow_cli/core/contract_schemas.py,sha256=
|
|
25
|
+
devflow_cli/core/contract_schemas.py,sha256=M0Twbt_SVVfuPa7udC-EJuR3pTl9pWrxQUCohIsg1h0,8477
|
|
26
26
|
devflow_cli/core/docgen.py,sha256=47JoVDWetkcctgHwJZC67AVebOKgl10VB9hZ3Xr63mo,3908
|
|
27
27
|
devflow_cli/core/feedback.py,sha256=07asmhX1cG4SXkoLlnSjA_UXgYnkFYoxl4u3JmOhpm0,13382
|
|
28
28
|
devflow_cli/core/git.py,sha256=SsHL0OeJ8Bzc5cJC0moeBuyuwnvrJjtwnaI58TnzUSU,2536
|
|
@@ -38,7 +38,7 @@ devflow_cli/core/staleness.py,sha256=0JH_i1b8dYJmjDmQZuVToPnLOoNCN8ZZFgiSNrrBWOY
|
|
|
38
38
|
devflow_cli/core/telemetry.py,sha256=FVR4iU9ywXIPo_5wrqWufpsp3GmHVLYiWaReaZ_DebM,2760
|
|
39
39
|
devflow_cli/core/traceability.py,sha256=jfNE9MZzGkmaPer9Wa6Y18yA9Y3sqdeR-nzH9h_TB-8,2968
|
|
40
40
|
devflow_cli/core/validation_approvals.py,sha256=3AHOpnW3BtY3BQrRZr38K7VNYRvbxGNhmQRYAufyhSA,9279
|
|
41
|
-
devflow_cli/core/validators.py,sha256=
|
|
41
|
+
devflow_cli/core/validators.py,sha256=HKGG1-td5T8cFx3rbE0iWPtddolk73qsqYreVWVT07g,7081
|
|
42
42
|
devflow_cli/core/verification.py,sha256=3_iGhsXNFCs5cpBUyBCRTsmRsfuBAVa38lRgy-LXXww,20349
|
|
43
43
|
devflow_cli/core/waivers.py,sha256=0w38Osjr69mDT6Lsn0ljkaq6bbggVejuhqiOnYHabs4,5586
|
|
44
44
|
devflow_cli/core/worklog.py,sha256=NedQaNrydudJ_uN3bCyo7n3plAcU1-4bYcK3WbLnu9E,15834
|
|
@@ -46,17 +46,18 @@ devflow_cli/core/worktree_snapshot.py,sha256=Q2_KTkhJTGvdM6Ay4RK9OHkyPEMrHQRTb-f
|
|
|
46
46
|
devflow_cli/schemas/activity-contract-v4.schema.json,sha256=IeduWDiJ5IV6EL7FDZWnHBjzyUt1uwig2rB-SMbcpOU,2006
|
|
47
47
|
devflow_cli/schemas/activity-result-v2.schema.json,sha256=2yDf6S--mlNWMxE8h8ruwTsM3Tnaa96SJDjluc4ePbI,2772
|
|
48
48
|
devflow_cli/schemas/assessment-v4.schema.json,sha256=byFBaoG_Vu-qS7vERREKThtpQa3dzNtPiFCMyEpWFVQ,2254
|
|
49
|
+
devflow_cli/schemas/reconcile-log-v3.schema.json,sha256=TNxw6xnv3dcwdUDqi0-SAlQnYg3_zDnCxkU6jwTfZRA,2926
|
|
49
50
|
devflow_cli/schemas/workflow-state-v6.schema.json,sha256=fYyJS-6Aq-Ea7y2bVqJYSmKzdF2DvL3Nb6IdDNgFGfY,3239
|
|
50
51
|
devflow_cli/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
51
52
|
devflow_cli/utils/console.py,sha256=t7cpyeEluWj4v_O_KFSI59iqXK4GiSXPuxlsr8q5zoQ,771
|
|
52
53
|
devflow_cli/utils/logging.py,sha256=I7U9_IVvYAFnUAWi7xs5F_WGR0uSz6DmRvZ_B3xLFbs,1769
|
|
53
54
|
devflow_cli/utils/paths.py,sha256=CH81dZZTI2vjgl8mck6QcAChxoBpujmKZ0vVKhKmXEY,8176
|
|
54
55
|
devflow_cli/utils/strings.py,sha256=bfFXCwBDnFxshgZOYfy6mtysnjiaWO1wFnqBf7HLg4o,351
|
|
55
|
-
devflow_cli/resources/commands/devflow.md,sha256=
|
|
56
|
+
devflow_cli/resources/commands/devflow.md,sha256=5MjahTVYXAT_zrjEQmM25KH9s6DbRmyI1TdJCxYlrKk,3464
|
|
56
57
|
devflow_cli/resources/commands/devflow.status.md,sha256=tqjQ1IboU99ztG6C4nunBHGJCvMn3qDqx3AoF4SPjCk,338
|
|
57
58
|
devflow_cli/resources/templates/constitution-template.md,sha256=uSzNuwF-xIRx8cHt9gloynRjNgDGmnN5z2Xck6fCOsI,2102
|
|
58
|
-
devflow_cli-10.
|
|
59
|
-
devflow_cli-10.
|
|
60
|
-
devflow_cli-10.
|
|
61
|
-
devflow_cli-10.
|
|
62
|
-
devflow_cli-10.
|
|
59
|
+
devflow_cli-10.1.1.dist-info/METADATA,sha256=2J_RNk3Db7cbmZbRZxe_NfLeXFOnXtLde9bIRsOec4I,6501
|
|
60
|
+
devflow_cli-10.1.1.dist-info/WHEEL,sha256=W3fkpkm7-wf9vBI5Z-7s0eWkeM-spu78I8Neb98DeEg,87
|
|
61
|
+
devflow_cli-10.1.1.dist-info/entry_points.txt,sha256=GVJ6bMvFpqyO5589iC4nFKeGTpGIW8aKn_QPPpSZ6kU,48
|
|
62
|
+
devflow_cli-10.1.1.dist-info/licenses/LICENSE,sha256=v79PvnmqOdKhQiOa_QcWxng4q8GuGscLOwi7ygUpwpo,1070
|
|
63
|
+
devflow_cli-10.1.1.dist-info/RECORD,,
|
|
@@ -1,212 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.5
|
|
2
|
-
Name: devflow-cli
|
|
3
|
-
Version: 10.0.2
|
|
4
|
-
Summary: Spec-Driven Development workflow CLI
|
|
5
|
-
Project-URL: Homepage, https://github.com/sopequenoteck/devflow
|
|
6
|
-
Project-URL: Repository, https://github.com/sopequenoteck/devflow
|
|
7
|
-
Author-email: sopequenoteck <sopequeno.tech@gmail.com>
|
|
8
|
-
License: MIT
|
|
9
|
-
License-File: LICENSE
|
|
10
|
-
Keywords: claude-code,cli,development,spec-driven,workflow
|
|
11
|
-
Classifier: Development Status :: 4 - Beta
|
|
12
|
-
Classifier: Environment :: Console
|
|
13
|
-
Classifier: Intended Audience :: Developers
|
|
14
|
-
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
-
Classifier: Programming Language :: Python :: 3.11
|
|
16
|
-
Classifier: Programming Language :: Python :: 3.12
|
|
17
|
-
Classifier: Topic :: Software Development :: Quality Assurance
|
|
18
|
-
Requires-Python: >=3.11
|
|
19
|
-
Requires-Dist: jsonschema>=4.23
|
|
20
|
-
Requires-Dist: pyyaml>=6
|
|
21
|
-
Requires-Dist: rich>=13
|
|
22
|
-
Requires-Dist: typer>=0.12
|
|
23
|
-
Provides-Extra: dev
|
|
24
|
-
Requires-Dist: hatchling>=1.27; extra == 'dev'
|
|
25
|
-
Requires-Dist: pytest-cov>=5; extra == 'dev'
|
|
26
|
-
Requires-Dist: pytest>=8; extra == 'dev'
|
|
27
|
-
Description-Content-Type: text/markdown
|
|
28
|
-
|
|
29
|
-
# devflow
|
|
30
|
-
|
|
31
|
-
Devflow est un moteur de développement adaptatif pour Claude Code. Il choisit un niveau de rigueur à partir de signaux explicites, produit
|
|
32
|
-
des contrats d’activité portables et bloque les transitions tant que les
|
|
33
|
-
preuves attendues ne sont pas présentes.
|
|
34
|
-
|
|
35
|
-
## Profils
|
|
36
|
-
|
|
37
|
-
Le profil fixe la route documentaire :
|
|
38
|
-
|
|
39
|
-
- `quick` : changement local, clair et peu risqué, sans revue.
|
|
40
|
-
- `standard` : plusieurs composants ou risque modéré ; préparation puis revue
|
|
41
|
-
systématique (`implementation-review`).
|
|
42
|
-
- `deep` : changement transversal, ambigu ou contractuel ; `spec`, `plan`,
|
|
43
|
-
`tasks`, plus `contracts` quand `--contract-change` est déclaré.
|
|
44
|
-
|
|
45
|
-
Un bloc de garde-fous s'y ajoute quand `--data-security` est déclaré ou que
|
|
46
|
-
`--risk high` : `risk-assessment` après l'évaluation, validation bloquante de
|
|
47
|
-
sécurité ou de données dans le plan de validation (une validation protégée non
|
|
48
|
-
bloquante est refusée dès `prepare` ou `plan`), preuve
|
|
49
|
-
`security-or-data-check` produite par `verify`, revue `independent-review`,
|
|
50
|
-
puis `rollback-check` avec `rollback-plan`. Un bloc actif impose au minimum le
|
|
51
|
-
profil `standard`, ne se désactive par aucune option et interdit les
|
|
52
|
-
dérogations.
|
|
53
|
-
|
|
54
|
-
Les routes exactes et leurs preuves sont générées dans
|
|
55
|
-
[docs/pipeline-reference.generated.md](docs/pipeline-reference.generated.md).
|
|
56
|
-
|
|
57
|
-
## Installation
|
|
58
|
-
|
|
59
|
-
```bash
|
|
60
|
-
uv tool install --refresh devflow-cli==X.Y.Z # version publiée, voir CHANGELOG.md
|
|
61
|
-
devflow init
|
|
62
|
-
```
|
|
63
|
-
|
|
64
|
-
Après chaque mise à jour de la CLI, lancer `devflow upgrade` : il met à jour
|
|
65
|
-
les commandes `/devflow` installées dans `~/.claude`, que l'installation de la
|
|
66
|
-
CLI ne touche pas. `--refresh` évite qu'`uv` ignore une version tout juste
|
|
67
|
-
publiée. Juste après une publication, l'installation peut échouer pendant une
|
|
68
|
-
à quatre minutes, le temps que l'index se propage : réessayer. Tant que les commandes installées diffèrent de la CLI,
|
|
69
|
-
`devflow status` affiche un avertissement avec cette commande ; rien n'est
|
|
70
|
-
modifié sans vous.
|
|
71
|
-
|
|
72
|
-
Installer toujours depuis PyPI, à une version précise, jamais depuis le
|
|
73
|
-
dossier du dépôt. Les versions publiées sont listées dans
|
|
74
|
-
[CHANGELOG.md](CHANGELOG.md).
|
|
75
|
-
|
|
76
|
-
Python 3.11+ et Git sont requis.
|
|
77
|
-
|
|
78
|
-
## Parcours quotidien
|
|
79
|
-
|
|
80
|
-
```bash
|
|
81
|
-
devflow feature KS-123 --short-name user-auth
|
|
82
|
-
# Décrire le besoin dans need.md, dans le dossier de la feature créé
|
|
83
|
-
devflow assess KS-123 --scope multiple --ambiguity medium --risk medium
|
|
84
|
-
devflow adaptive start KS-123
|
|
85
|
-
devflow adaptive run KS-123
|
|
86
|
-
devflow adaptive status KS-123
|
|
87
|
-
```
|
|
88
|
-
|
|
89
|
-
`adaptive status --json` fournit un état exploitable par la CI. Les échecs
|
|
90
|
-
distinguent explicitement blocage métier, contrat invalide, panne agent,
|
|
91
|
-
validation échouée et conflit concurrent ; chaque refus affiche la commande de
|
|
92
|
-
reprise recommandée. Après un refus de `verify`, cette reprise dépend du motif :
|
|
93
|
-
approbations à donner, environnement à corriger, ou trois voies après un
|
|
94
|
-
contrôle échoué (rouvrir `implement` si le code est en cause, rouvrir `prepare`
|
|
95
|
-
ou `plan` si la validation l'est, relancer `verify` après un échec passager).
|
|
96
|
-
En `quick`, un lot purement documentaire (`README.md`, Markdown sous `docs/`)
|
|
97
|
-
sans contrôle détecté franchit `verify` sur l'inspection du diff.
|
|
98
|
-
|
|
99
|
-
Un plan de `prepare` ou de `plan` refusé par les validateurs se corrige sans
|
|
100
|
-
relancer l'agent : `devflow adaptive reconcile KS-123 --result FICHIER.json
|
|
101
|
-
--reason MOTIF` rejoue les mêmes validateurs et trace la correction. Les
|
|
102
|
-
validations qui attendent une approbation (`devflow adaptive approve-command`)
|
|
103
|
-
sont listées en une fois à la fin du plan et au début de `verify`. Pendant
|
|
104
|
-
qu'un agent travaille, les commandes humaines qui modifieraient l'état sont
|
|
105
|
-
refusées sans effet, et un fichier d'exécution modifié par l'agent est remis
|
|
106
|
-
dans son état d'avant l'agent.
|
|
107
|
-
|
|
108
|
-
L'agent Claude d'une activité est lancé avec
|
|
109
|
-
`CLAUDE_CODE_DISABLE_BACKGROUND_TASKS=1` : il peut déléguer à des sous-agents,
|
|
110
|
-
mais au premier plan seulement. Effet de bord : il ne peut plus lancer de
|
|
111
|
-
commande shell en arrière-plan (serveur de test, par exemple). Tous les contrats
|
|
112
|
-
demandent en plus que la sortie structurée soit le dernier message de l'agent.
|
|
113
|
-
Si un agent termine sans erreur mais sans sortie structurée, `adaptive run`
|
|
114
|
-
nomme la cause (`structured-output-missing`) et propose
|
|
115
|
-
`devflow adaptive reconcile KS-123 --result MODELE --reason MOTIF` avec un
|
|
116
|
-
modèle de résultat prérempli, écrit dans `~/.devflow/reconcile/` et à compléter.
|
|
117
|
-
|
|
118
|
-
`need.md` peut être amendé en cours de parcours. Avant une activité
|
|
119
|
-
documentaire, l'amendement est accepté avec un avertissement qui nomme les
|
|
120
|
-
activités déjà closes, à rouvrir si leur résultat en dépend ; avant `prepare`,
|
|
121
|
-
il est accepté sans cet avertissement ; pendant l'exécution d'un agent, il est refusé ;
|
|
122
|
-
avant `implement`, il périme la preuve de pré-vol ; après `implement`, il
|
|
123
|
-
périme une revue déjà faite. Pour arbitrer après un blocage d'`implement` :
|
|
124
|
-
amender `need.md`, puis `devflow adaptive reopen KS-123 preflight-evidence
|
|
125
|
-
--reason MOTIF`, refaire le pré-vol et relancer `implement`. Un refus
|
|
126
|
-
d'artefact (`artifact-files-mismatch`) nomme chaque fichier en trop et sa
|
|
127
|
-
cause : retirer ou remettre en état le fichier, puis
|
|
128
|
-
`devflow adaptive reconcile KS-123` ; la relance à l'identique n'est plus
|
|
129
|
-
proposée. Pour une entrée du besoin modifiée par un agent, la relance est
|
|
130
|
-
refusée avant le lancement de l'agent : remettre l'entrée en état, ou
|
|
131
|
-
`devflow adaptive discard-attempt KS-123 --reason MOTIF`, puis
|
|
132
|
-
`devflow adaptive run KS-123`.
|
|
133
|
-
|
|
134
|
-
Chaque invocation de `adaptive run` traite une activité. La politique est dans
|
|
135
|
-
`assessment.json`; la progression et les preuves sont dans
|
|
136
|
-
`workflow-state.json`. Les résultats agent sont validés par schéma et les
|
|
137
|
-
preuves d’exécution sensibles sont produites localement par Devflow.
|
|
138
|
-
|
|
139
|
-
Chaque profil exécute `preflight-evidence` avant `implement`. Cette
|
|
140
|
-
activité archive des faits externes reproductibles dans
|
|
141
|
-
`preflight-evidence.md`, sans secret ni donnée utilisateur.
|
|
142
|
-
|
|
143
|
-
`adaptive run` lance toujours Claude Code, sans option d'agent.
|
|
144
|
-
|
|
145
|
-
## Features créées avant devflow 9
|
|
146
|
-
|
|
147
|
-
Devflow 9 refuse, sans migration, un dossier de feature dont la racine contient
|
|
148
|
-
un `*-result.json` de `schemaVersion` 1 ou un `*-attempt.json` de schéma 2 ou
|
|
149
|
-
moins (parcours démarré avec devflow 8.x). Terminez les parcours en cours avec
|
|
150
|
-
devflow 8.5.1, ou gardez la CLI épinglée :
|
|
151
|
-
`uv tool install devflow-cli==8.5.1`. Voir les
|
|
152
|
-
[notes de version 9.0.0](docs/releases/9.0.0.md).
|
|
153
|
-
|
|
154
|
-
## Features créées avant devflow 8
|
|
155
|
-
|
|
156
|
-
Devflow 8 refuse, sans migration, un `workflow-state.json` (schéma 5) produit
|
|
157
|
-
par devflow 7.x. Terminez les parcours en cours avec devflow 7.x, ou gardez la
|
|
158
|
-
CLI épinglée : `uv tool install 'devflow-cli<8'`. Voir les
|
|
159
|
-
[notes de version 8.0.0](docs/releases/8.0.0.md).
|
|
160
|
-
|
|
161
|
-
## Features créées avant devflow 7
|
|
162
|
-
|
|
163
|
-
Devflow 7 refuse, sans migration, un `assessment.json` (schéma 3) ou un
|
|
164
|
-
`workflow-state.json` (schéma 4) produit par devflow 6.x, avec un message qui
|
|
165
|
-
nomme le fichier. Avant la mise à jour, terminez les parcours en cours avec
|
|
166
|
-
devflow 6.x, ou gardez la CLI épinglée :
|
|
167
|
-
`uv tool install 'devflow-cli<7'`. Voir
|
|
168
|
-
[ADR-0003](docs/adr/0003-v7-guardrails-rupture.md) et les
|
|
169
|
-
[notes de version 7.0.0](docs/releases/7.0.0.md).
|
|
170
|
-
|
|
171
|
-
## Features créées avant devflow 5
|
|
172
|
-
|
|
173
|
-
Devflow 5 ne lit que ses propres schémas et ne fournit aucune migration : un
|
|
174
|
-
`assessment.json`, un `workflow-state.json` ou un `state.json` créé par une
|
|
175
|
-
version antérieure est refusé. Terminez ces features avec devflow 4.x avant la
|
|
176
|
-
mise à jour. Voir [ADR-0002](docs/adr/0002-v5-rupture-and-provenance.md).
|
|
177
|
-
|
|
178
|
-
`devflow init` et `devflow upgrade` déplacent également les anciennes commandes
|
|
179
|
-
Claude connues vers `~/.claude/.devflow-legacy-backup/` sans toucher aux
|
|
180
|
-
commandes personnalisées.
|
|
181
|
-
|
|
182
|
-
## Synchronisation Linear
|
|
183
|
-
|
|
184
|
-
Si `LINEAR_API_KEY` contient une clé d'API personnelle Linear, la CLI met à jour
|
|
185
|
-
le statut de l'issue du parcours au démarrage de chaque activité (`adaptive
|
|
186
|
-
run`, y compris avec `--until`, et clôture `done`). L'équipe Linear doit avoir
|
|
187
|
-
les statuts `Backlog`, `Design`, `In Progress`, `Review` et `Done`. Un statut
|
|
188
|
-
n'est jamais ramené en arrière ; un statut hors de cette liste (`Canceled`,
|
|
189
|
-
`Duplicate`) n'est jamais modifié.
|
|
190
|
-
|
|
191
|
-
Sans clé, ou si Linear échoue (issue inconnue, statut absent, erreur réseau ou
|
|
192
|
-
délai de 5 s dépassé), la CLI affiche un avertissement et le parcours continue
|
|
193
|
-
à l'identique. La clé est retirée de l'environnement de tout processus lancé
|
|
194
|
-
par Devflow (agents, commandes de validation, commandes git) ; elle reste
|
|
195
|
-
lisible par un agent si elle est définie dans un fichier que son shell charge (`~/.zshrc`). Détails :
|
|
196
|
-
[Référence CLI](docs/cli-reference.md#synchronisation-linear).
|
|
197
|
-
|
|
198
|
-
## Documentation
|
|
199
|
-
|
|
200
|
-
- [Workflow adaptatif](docs/pipeline.md)
|
|
201
|
-
- [Référence CLI](docs/cli-reference.md)
|
|
202
|
-
- [Contrats d’activité](docs/activity-contracts.md)
|
|
203
|
-
- [Façade Claude Code](docs/commands-reference.md)
|
|
204
|
-
|
|
205
|
-
## Développement
|
|
206
|
-
|
|
207
|
-
```bash
|
|
208
|
-
uv sync --extra dev
|
|
209
|
-
uv run pytest -q
|
|
210
|
-
./tests/run-tests.sh
|
|
211
|
-
uv run devflow docs-sync --check
|
|
212
|
-
```
|
|
File without changes
|
|
File without changes
|
|
File without changes
|