devflow-cli 3.0.0__py3-none-any.whl → 3.0.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/adapters/agents.py +17 -0
- devflow_cli/commands/adaptive.py +7 -1
- devflow_cli/core/activity_contracts.py +37 -6
- devflow_cli/core/adaptive_runner.py +34 -13
- devflow_cli/core/remediation.py +11 -0
- devflow_cli/core/validators.py +1 -1
- {devflow_cli-3.0.0.dist-info → devflow_cli-3.0.1.dist-info}/METADATA +1 -1
- {devflow_cli-3.0.0.dist-info → devflow_cli-3.0.1.dist-info}/RECORD +12 -12
- {devflow_cli-3.0.0.dist-info → devflow_cli-3.0.1.dist-info}/WHEEL +0 -0
- {devflow_cli-3.0.0.dist-info → devflow_cli-3.0.1.dist-info}/entry_points.txt +0 -0
- {devflow_cli-3.0.0.dist-info → devflow_cli-3.0.1.dist-info}/licenses/LICENSE +0 -0
devflow_cli/__init__.py
CHANGED
devflow_cli/adapters/agents.py
CHANGED
|
@@ -230,6 +230,23 @@ class CodexAdapter(AgentAdapter):
|
|
|
230
230
|
command.append(prompt)
|
|
231
231
|
return command
|
|
232
232
|
|
|
233
|
+
def _structured_payload(self, stdout: str, output_file: Path) -> object:
|
|
234
|
+
"""Lit la reponse finale Codex et distingue son absence d'un JSON invalide."""
|
|
235
|
+
if output_file.is_file():
|
|
236
|
+
payload = output_file.read_text(encoding="utf-8")
|
|
237
|
+
if payload.strip():
|
|
238
|
+
return json.loads(payload)
|
|
239
|
+
raise ValueError(
|
|
240
|
+
"Codex a termine sans reponse finale structuree "
|
|
241
|
+
"(--output-last-message est vide)"
|
|
242
|
+
)
|
|
243
|
+
if stdout.strip():
|
|
244
|
+
return json.loads(stdout)
|
|
245
|
+
raise ValueError(
|
|
246
|
+
"Codex n'a pas produit de reponse finale structuree "
|
|
247
|
+
"ni de sortie standard exploitable"
|
|
248
|
+
)
|
|
249
|
+
|
|
233
250
|
|
|
234
251
|
class CursorAdapter(AgentAdapter):
|
|
235
252
|
name = "cursor"
|
devflow_cli/commands/adaptive.py
CHANGED
|
@@ -32,7 +32,11 @@ from devflow_cli.core.worktree_snapshot import changed_paths, snapshot_worktree
|
|
|
32
32
|
from devflow_cli.core.automatic_evidence import run_automatic_verification
|
|
33
33
|
from devflow_cli.core.worklog import refresh_worklog, safe_refresh_worklog
|
|
34
34
|
from devflow_cli.core.waivers import create_validation_waiver
|
|
35
|
-
from devflow_cli.core.remediation import
|
|
35
|
+
from devflow_cli.core.remediation import (
|
|
36
|
+
carried_changed_files,
|
|
37
|
+
is_reopened_activity,
|
|
38
|
+
reopen_activity,
|
|
39
|
+
)
|
|
36
40
|
from devflow_cli.core.contract_schemas import diagnose_contract_file
|
|
37
41
|
from devflow_cli.core.operational import ExitCode, exit_code_for_run_reason
|
|
38
42
|
from devflow_cli.adapters.agents import get_agent_adapter
|
|
@@ -193,6 +197,8 @@ def contract(
|
|
|
193
197
|
assessment.to_dict(),
|
|
194
198
|
feature_path,
|
|
195
199
|
carried_changed_files(feature_dir, state.currentActivity),
|
|
200
|
+
reopened=is_reopened_activity(feature_dir, state.currentActivity),
|
|
201
|
+
preflight_evidence=(feature_dir / "preflight-evidence.md").is_file(),
|
|
196
202
|
)
|
|
197
203
|
except ValueError as exc:
|
|
198
204
|
fail(f"Impossible de generer le contrat : {exc}")
|
|
@@ -97,11 +97,27 @@ class ActivityResult:
|
|
|
97
97
|
return payload
|
|
98
98
|
|
|
99
99
|
|
|
100
|
+
def document_artifact_name(activity: str, *, reopened: bool = False) -> str:
|
|
101
|
+
"""Retourne l'artefact unique attendu pour une activite documentaire."""
|
|
102
|
+
if activity == "plan" and reopened:
|
|
103
|
+
return "preflight-evidence.md"
|
|
104
|
+
return DOCUMENT_ARTIFACTS[activity]
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def contract_is_reopened_plan(contract: ActivityContract) -> bool:
|
|
108
|
+
return (
|
|
109
|
+
contract.activity == "plan"
|
|
110
|
+
and f"{contract.featurePath}/reopen-log.json" in contract.inputs
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
|
|
100
114
|
def build_activity_contract(
|
|
101
115
|
state: AdaptiveWorkflowState,
|
|
102
116
|
assessment: dict[str, Any],
|
|
103
117
|
feature_path: str,
|
|
104
118
|
carried_changed_files: list[str] | None = None,
|
|
119
|
+
reopened: bool = False,
|
|
120
|
+
preflight_evidence: bool = False,
|
|
105
121
|
) -> ActivityContract:
|
|
106
122
|
"""Construit le contrat de l'activite courante sans notion d'agent."""
|
|
107
123
|
activity = state.currentActivity
|
|
@@ -126,7 +142,7 @@ def build_activity_contract(
|
|
|
126
142
|
"Signaler tout blocage au lieu de contourner une validation.",
|
|
127
143
|
]
|
|
128
144
|
if activity in DOCUMENT_ACTIVITIES:
|
|
129
|
-
artifact_name =
|
|
145
|
+
artifact_name = document_artifact_name(activity, reopened=reopened)
|
|
130
146
|
allowed_actions = ["read", "write", "execute"]
|
|
131
147
|
constraints = [
|
|
132
148
|
*common_constraints,
|
|
@@ -134,6 +150,11 @@ def build_activity_contract(
|
|
|
134
150
|
"Produire un artefact complet sans placeholder ni section vide.",
|
|
135
151
|
"Declarer uniquement cet artefact dans changedFiles.",
|
|
136
152
|
]
|
|
153
|
+
if activity == "plan" and reopened:
|
|
154
|
+
constraints.append(
|
|
155
|
+
"Archiver les faits, la source, la reproductibilite et les limites connues "
|
|
156
|
+
"dans preflight-evidence.md, sans y recopier de donnees sensibles."
|
|
157
|
+
)
|
|
137
158
|
completion_criteria = [
|
|
138
159
|
f"{artifact_name} existe et respecte la structure devflow.",
|
|
139
160
|
"Le contenu est coherent avec l'objectif et les artefacts en entree.",
|
|
@@ -301,8 +322,18 @@ def build_activity_contract(
|
|
|
301
322
|
f"{feature_path}/assessment.json",
|
|
302
323
|
f"{feature_path}/workflow-state.json",
|
|
303
324
|
]
|
|
304
|
-
if
|
|
325
|
+
if reopened:
|
|
305
326
|
inputs.append(f"{feature_path}/reopen-log.json")
|
|
327
|
+
constraints.append(
|
|
328
|
+
"Cette execution suit une reouverture auditee : lire reopen-log.json et corriger explicitement le motif qui y est consigne."
|
|
329
|
+
)
|
|
330
|
+
if activity == "plan" and reopened:
|
|
331
|
+
inputs.append(f"{feature_path}/plan.md")
|
|
332
|
+
if activity == "implement" and preflight_evidence:
|
|
333
|
+
inputs.append(f"{feature_path}/preflight-evidence.md")
|
|
334
|
+
constraints.append(
|
|
335
|
+
"Prendre en compte la preuve de pre-vol archivee et ne pas la presenter comme absente."
|
|
336
|
+
)
|
|
306
337
|
if activity == "implement" and state.profile == "standard":
|
|
307
338
|
inputs.append(f"{feature_path}/prepare-result.json")
|
|
308
339
|
if state.profile == "deep":
|
|
@@ -351,6 +382,8 @@ def build_activity_contract(
|
|
|
351
382
|
if activity == "rollback-check":
|
|
352
383
|
inputs.append(f"{feature_path}/review-result.json")
|
|
353
384
|
|
|
385
|
+
expected_output = _result_schema(activity)
|
|
386
|
+
|
|
354
387
|
return ActivityContract(
|
|
355
388
|
schemaVersion=ACTIVITY_CONTRACT_SCHEMA_VERSION,
|
|
356
389
|
contractId=f"{state.issueId}:{state.profile}:{activity}:v1",
|
|
@@ -364,7 +397,7 @@ def build_activity_contract(
|
|
|
364
397
|
constraints=constraints,
|
|
365
398
|
completionCriteria=completion_criteria,
|
|
366
399
|
requiredEvidence=required_evidence,
|
|
367
|
-
expectedOutput=
|
|
400
|
+
expectedOutput=expected_output,
|
|
368
401
|
)
|
|
369
402
|
|
|
370
403
|
|
|
@@ -432,9 +465,7 @@ def parse_activity_result(payload: dict[str, Any], contract: ActivityContract) -
|
|
|
432
465
|
if contract.activity == "prepare" and status == "completed" and not validation_plan:
|
|
433
466
|
raise ValueError("prepare complete requiert un plan de validation")
|
|
434
467
|
if contract.activity in DOCUMENT_ACTIVITIES and status == "completed":
|
|
435
|
-
expected_artifact = (
|
|
436
|
-
f"{contract.featurePath}/{DOCUMENT_ARTIFACTS[contract.activity]}"
|
|
437
|
-
)
|
|
468
|
+
expected_artifact = f"{contract.featurePath}/{document_artifact_name(contract.activity, reopened=contract_is_reopened_plan(contract))}"
|
|
438
469
|
if changed_files != [expected_artifact]:
|
|
439
470
|
raise ValueError(
|
|
440
471
|
f"{contract.activity} doit declarer uniquement {expected_artifact}"
|
|
@@ -14,6 +14,8 @@ from devflow_cli.core.activity_contracts import (
|
|
|
14
14
|
ActivityResult,
|
|
15
15
|
DOCUMENT_ARTIFACTS,
|
|
16
16
|
build_activity_contract,
|
|
17
|
+
contract_is_reopened_plan,
|
|
18
|
+
document_artifact_name,
|
|
17
19
|
parse_activity_result,
|
|
18
20
|
write_activity_contract,
|
|
19
21
|
)
|
|
@@ -30,7 +32,7 @@ from devflow_cli.core.traceability import analyze_traceability
|
|
|
30
32
|
from devflow_cli.core.validators import validate_artifact
|
|
31
33
|
from devflow_cli.core.verification import ValidationCommand, validate_command_policy
|
|
32
34
|
from devflow_cli.core.waivers import is_protected_validation
|
|
33
|
-
from devflow_cli.core.remediation import carried_changed_files
|
|
35
|
+
from devflow_cli.core.remediation import carried_changed_files, is_reopened_activity
|
|
34
36
|
|
|
35
37
|
|
|
36
38
|
@dataclass(frozen=True)
|
|
@@ -81,7 +83,12 @@ def run_current_activity(
|
|
|
81
83
|
try:
|
|
82
84
|
carried = carried_changed_files(feature_dir, state.currentActivity)
|
|
83
85
|
contract = build_activity_contract(
|
|
84
|
-
state,
|
|
86
|
+
state,
|
|
87
|
+
assessment.to_dict(),
|
|
88
|
+
feature_path,
|
|
89
|
+
carried,
|
|
90
|
+
reopened=is_reopened_activity(feature_dir, state.currentActivity),
|
|
91
|
+
preflight_evidence=(feature_dir / "preflight-evidence.md").is_file(),
|
|
85
92
|
)
|
|
86
93
|
except ValueError as exc:
|
|
87
94
|
return AdaptiveRunOutcome(
|
|
@@ -176,7 +183,9 @@ def run_current_activity(
|
|
|
176
183
|
result,
|
|
177
184
|
)
|
|
178
185
|
if contract.activity in DOCUMENT_ARTIFACTS:
|
|
179
|
-
artifact_name =
|
|
186
|
+
artifact_name = document_artifact_name(
|
|
187
|
+
contract.activity, reopened=contract_is_reopened_plan(contract)
|
|
188
|
+
)
|
|
180
189
|
expected = f"{feature_path}/{artifact_name}"
|
|
181
190
|
allowed_declared = not declared or declared == [expected]
|
|
182
191
|
allowed_observed = not observed or observed == [expected]
|
|
@@ -193,17 +202,29 @@ def run_current_activity(
|
|
|
193
202
|
result,
|
|
194
203
|
)
|
|
195
204
|
if contract.activity in DOCUMENT_ARTIFACTS and result.status == "completed":
|
|
196
|
-
artifact_name =
|
|
197
|
-
|
|
198
|
-
f"critical-{contract.activity}"
|
|
199
|
-
if contract.profile == "critical"
|
|
200
|
-
and contract.activity in {"research", "contracts"}
|
|
201
|
-
else contract.activity
|
|
202
|
-
)
|
|
203
|
-
validation = validate_artifact(
|
|
204
|
-
validator_name, feature_dir / artifact_name
|
|
205
|
+
artifact_name = document_artifact_name(
|
|
206
|
+
contract.activity, reopened=contract_is_reopened_plan(contract)
|
|
205
207
|
)
|
|
206
|
-
if
|
|
208
|
+
if artifact_name == "preflight-evidence.md":
|
|
209
|
+
try:
|
|
210
|
+
if not (feature_dir / artifact_name).read_text(encoding="utf-8").strip():
|
|
211
|
+
raise OSError("artefact vide")
|
|
212
|
+
except OSError as exc:
|
|
213
|
+
return AdaptiveRunOutcome(
|
|
214
|
+
"invalid-artifact", contract.activity, contract.activity, str(exc), result
|
|
215
|
+
)
|
|
216
|
+
validation = None
|
|
217
|
+
else:
|
|
218
|
+
validator_name = (
|
|
219
|
+
f"critical-{contract.activity}"
|
|
220
|
+
if contract.profile == "critical"
|
|
221
|
+
and contract.activity in {"research", "contracts"}
|
|
222
|
+
else contract.activity
|
|
223
|
+
)
|
|
224
|
+
validation = validate_artifact(
|
|
225
|
+
validator_name, feature_dir / artifact_name
|
|
226
|
+
)
|
|
227
|
+
if validation is not None and not validation.valid:
|
|
207
228
|
return AdaptiveRunOutcome(
|
|
208
229
|
"invalid-artifact",
|
|
209
230
|
contract.activity,
|
devflow_cli/core/remediation.py
CHANGED
|
@@ -82,6 +82,17 @@ def carried_changed_files(feature_dir: Path, activity: str) -> list[str]:
|
|
|
82
82
|
return sorted(set(files))
|
|
83
83
|
|
|
84
84
|
|
|
85
|
+
def is_reopened_activity(feature_dir: Path, activity: str) -> bool:
|
|
86
|
+
"""Indique si l'activite courante provient de la derniere reouverture auditee."""
|
|
87
|
+
try:
|
|
88
|
+
payload = json.loads((feature_dir / "reopen-log.json").read_text(encoding="utf-8"))
|
|
89
|
+
entries = payload["entries"]
|
|
90
|
+
entry = entries[-1]
|
|
91
|
+
except (OSError, json.JSONDecodeError, KeyError, IndexError, TypeError):
|
|
92
|
+
return False
|
|
93
|
+
return entry.get("targetActivity") == activity
|
|
94
|
+
|
|
95
|
+
|
|
85
96
|
def _read_log(path: Path, issue_id: str) -> dict:
|
|
86
97
|
if not path.exists():
|
|
87
98
|
return {"schemaVersion": 1, "issueId": issue_id, "entries": []}
|
devflow_cli/core/validators.py
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
devflow_cli/__init__.py,sha256=
|
|
1
|
+
devflow_cli/__init__.py,sha256=BpztDtcz1AjuyzEjqH-J9D8Bp2JXv8vfkPYNoXHB69U,938
|
|
2
2
|
devflow_cli/cli.py,sha256=HpDAnapEm2Hmnn2ltTmdjwpmoXvW7fat6ipQo3xBCks,2139
|
|
3
3
|
devflow_cli/adapters/__init__.py,sha256=RkjHPU2Lp4hSVIq2iOmyTrv2zYU9NuiIF9wEMWzLdMI,377
|
|
4
|
-
devflow_cli/adapters/agents.py,sha256=
|
|
4
|
+
devflow_cli/adapters/agents.py,sha256=RGVFNV8YJZGsVDxykh1pvnGZu5gevX6zhGeiSq9Jm14,11873
|
|
5
5
|
devflow_cli/adapters/linear.py,sha256=MyqI2tHx7NNHY1KRfeYZ_Zjc-6_xhZPWD4oS-j7wnNY,2659
|
|
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=8afSr85JilTJKJQTN8IGV9eh464jslQGBssKieZAa2k,22712
|
|
8
8
|
devflow_cli/commands/assess.py,sha256=GrWGGZjvBAgHWzSlUdtS-IfYocacA6VYxWduDvBunzg,4395
|
|
9
9
|
devflow_cli/commands/check.py,sha256=ULsMNzc-VM635Kkykt9QYVXQk_69T3E2nj4Cx-Cp6xk,5337
|
|
10
10
|
devflow_cli/commands/context.py,sha256=hwTitOHxIpJMx1X8R1jHHy9CpE4HVZpJKkyeOujxAio,6246
|
|
@@ -22,8 +22,8 @@ devflow_cli/commands/traceability.py,sha256=DhUOBzSmKVk17j1Qz0yxD5zD2-iNWaR9F133
|
|
|
22
22
|
devflow_cli/commands/upgrade_cmd.py,sha256=Dha72Cqy-7pVi7jcvpOyxe6qNp--I47OaElfztKriPg,5380
|
|
23
23
|
devflow_cli/commands/verify.py,sha256=1Fn5dxhVEvuG2C4OC7cMHv3oV4pthu3QLsFjQaCZrnA,2670
|
|
24
24
|
devflow_cli/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
25
|
-
devflow_cli/core/activity_contracts.py,sha256=
|
|
26
|
-
devflow_cli/core/adaptive_runner.py,sha256
|
|
25
|
+
devflow_cli/core/activity_contracts.py,sha256=uPk770qJYCJKvF3sJNMmueAwtBSLPjouac_JMUa8c18,30796
|
|
26
|
+
devflow_cli/core/adaptive_runner.py,sha256=-1eZYaZ2cIe_1gHGlSaOS-rOD3TsuMthXRgvCGV5SKE,13432
|
|
27
27
|
devflow_cli/core/adaptive_workflow.py,sha256=BZ3N7-hbbvUT0UjSsknqG0I1CJN6ZYuuA566GzdKM-A,10731
|
|
28
28
|
devflow_cli/core/assessment.py,sha256=_fL5bsl90-yZYjf7CSX7Nxhf1auyeAcKHsoo_reafTE,9824
|
|
29
29
|
devflow_cli/core/automatic_evidence.py,sha256=lQI7siiMUb5-c-XDhAsDn-5UdrZPbt4XIreU1qGvuNg,28723
|
|
@@ -36,11 +36,11 @@ devflow_cli/core/installer.py,sha256=MfcGZKoOcQc2qviNnGShoD0P_MqOgRuVHX7YwCrG2vw
|
|
|
36
36
|
devflow_cli/core/manifest.py,sha256=J7g2SzhXy-Vi4w9-rEPmARSZXo8KTOxfd2pEc9jNUME,5933
|
|
37
37
|
devflow_cli/core/operational.py,sha256=wJ0Q87thNyCGpUs420DdkqzFHM-nIw_1tDAuPqlXX8w,977
|
|
38
38
|
devflow_cli/core/redaction.py,sha256=o_1MDtCyvRLrk_dWlV6_7E6_mPVB1B5a0cCfTBj9j0Q,1973
|
|
39
|
-
devflow_cli/core/remediation.py,sha256=
|
|
39
|
+
devflow_cli/core/remediation.py,sha256=cReuxUi_k-xP74Hj16MFT6PgDwNBRV0jUm8asFIBztk,6265
|
|
40
40
|
devflow_cli/core/scanner.py,sha256=bcoC91Nc0Hw2P7aHpL-9ITxynLZhLTEKpOXo553a-18,4395
|
|
41
41
|
devflow_cli/core/staleness.py,sha256=Bn4jN8r601Ifn0AU8BHZy-Z-5NLPjj_JTK-V0KU9TUM,5256
|
|
42
42
|
devflow_cli/core/traceability.py,sha256=jfNE9MZzGkmaPer9Wa6Y18yA9Y3sqdeR-nzH9h_TB-8,2968
|
|
43
|
-
devflow_cli/core/validators.py,sha256=
|
|
43
|
+
devflow_cli/core/validators.py,sha256=U4Rf9sZNI75XpzixRAeiJhf53Ktexgu5OtV6SBjSM6w,8076
|
|
44
44
|
devflow_cli/core/verification.py,sha256=zChsM8m-KtoJPeJiTwqOHTwMgmbg_GR7T7FfYoyHdlE,12466
|
|
45
45
|
devflow_cli/core/waivers.py,sha256=w3bza1OicSxAViYU2hvbrylQTNVgmN7Rd9y2L6Wm2OE,11302
|
|
46
46
|
devflow_cli/core/worklog.py,sha256=VLyn_IcOiM0VEVnQScDebaAIQ3vcHQigiTGS5Ldc6LQ,11000
|
|
@@ -81,8 +81,8 @@ devflow_cli/resources/templates/contracts/api-template.md,sha256=rC5v7JBz71PznCn
|
|
|
81
81
|
devflow_cli/resources/templates/contracts/components-template.md,sha256=XIWfEXfC72sNmL5OA3V-7xIywkNDU2DBPLfyiagOWcs,484
|
|
82
82
|
devflow_cli/resources/templates/contracts/interfaces-template.md,sha256=kf8B7b6HsmN7jyRlLNU7RnZl0rTMlI5s97yVRyI-6yc,336
|
|
83
83
|
devflow_cli/resources/templates/contracts/services-template.md,sha256=cFafKCyLmq_a7aR7G0LXAaMO2z4Fuflq6iKdxfVg0vg,477
|
|
84
|
-
devflow_cli-3.0.
|
|
85
|
-
devflow_cli-3.0.
|
|
86
|
-
devflow_cli-3.0.
|
|
87
|
-
devflow_cli-3.0.
|
|
88
|
-
devflow_cli-3.0.
|
|
84
|
+
devflow_cli-3.0.1.dist-info/METADATA,sha256=Rf2yqEkAPVZmI72ovh0h4VdOeucNSHyC7-Je1ZMDlOI,3789
|
|
85
|
+
devflow_cli-3.0.1.dist-info/WHEEL,sha256=zOwg4jB6zX2kU910N-cMawjivD6tO8NEWvE12je1bVk,87
|
|
86
|
+
devflow_cli-3.0.1.dist-info/entry_points.txt,sha256=GVJ6bMvFpqyO5589iC4nFKeGTpGIW8aKn_QPPpSZ6kU,48
|
|
87
|
+
devflow_cli-3.0.1.dist-info/licenses/LICENSE,sha256=v79PvnmqOdKhQiOa_QcWxng4q8GuGscLOwi7ygUpwpo,1070
|
|
88
|
+
devflow_cli-3.0.1.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|