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 CHANGED
@@ -1,6 +1,6 @@
1
1
  """devflow CLI — workflow de développement adaptatif."""
2
2
 
3
- VERSION = "10.0.2"
3
+ VERSION = "10.1.1"
4
4
 
5
5
  ACTIVITY_LINEAR_MAPPING = {
6
6
  "assess": "Backlog",
@@ -367,7 +367,7 @@ def run_adaptive(
367
367
  output_file,
368
368
  permission_mode,
369
369
  "workspace-write"
370
- if "write" in activity_contract.allowedActions
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="Motif obligatoire avec --result",
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 utilise qu'avec --result")
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 ATTEMPT_SCHEMA_VERSION
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
- artifact = (
450
- f"{feature_path}/{DOCUMENT_ARTIFACTS[activity]}"
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
- if drifted:
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
- if artifact_name == "preflight-evidence.md":
669
- try:
670
- validate_preflight_evidence(feature_dir / artifact_name)
671
- except ValueError as exc:
672
- return refuse("invalid-artifact", str(exc))
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 2 : entrees avec source, agentPayloadSha256 et changedFields (KKS-509).
1049
- RECONCILE_LOG_SCHEMA_VERSION = 2
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 / "reconcile-log.json"
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
- "sequence": len(log["entries"]) + 1,
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["entries"].append(entry)
1114
- _write_json_atomic(path, log)
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
@@ -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=b-svwN73BhNyuTfH9bGnIbORfC_qUL1jirIw8xLEdK8,871
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=sHRLFTOtRsMEQscm1InalbIBOBk9MSJYEwwo32wZsU4,62775
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=Znc53MmtFC2iWLR-6IjwVFtViKyY7J6oWJn_HRp2Xk0,44686
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=JXI8PVgjdagTlFxnyuIkNRRrVi319Mv8GwuMxopJ_GI,8424
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=fREjEx2fBW36q3vB6WJNqoGUM9ejYlQUXie5A9lNuPs,7440
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=R_8oExTiK2oE5-7YycePFbUeu3tpkyMpsvXVZHAegYM,2899
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.0.2.dist-info/METADATA,sha256=hYN3yu8SeHp-cF9TA4LHf0ZNVPLjIYBsfOF1IzJCidI,10116
59
- devflow_cli-10.0.2.dist-info/WHEEL,sha256=W3fkpkm7-wf9vBI5Z-7s0eWkeM-spu78I8Neb98DeEg,87
60
- devflow_cli-10.0.2.dist-info/entry_points.txt,sha256=GVJ6bMvFpqyO5589iC4nFKeGTpGIW8aKn_QPPpSZ6kU,48
61
- devflow_cli-10.0.2.dist-info/licenses/LICENSE,sha256=v79PvnmqOdKhQiOa_QcWxng4q8GuGscLOwi7ygUpwpo,1070
62
- devflow_cli-10.0.2.dist-info/RECORD,,
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
- ```