fix(anchors): lève 2 nits revue — contrat crop corrompu + workflow_id sidecar — 2026-07-03
Suite revue adversariale MERGE-READY (« non-bloquant ≠ oublié », Dom) : 1. Contrat crop CORROMPU figé par test : `materialize_anchor_from_b64` retire la clé `_anchor_image_base64` de params AVANT le try (pop). Un crop inexploitable (b64 invalide, PNG au magic cassé) → pas d'exception, anchor_id NULL, aucun VisualAnchor, ET clé retirée (pas de donnée morte volumineuse dans les params). 3 cas paramétrés verrouillent ce comportement assumé. Docstring clarifiée. 2. `workflow_id` réintroduit dans le sidecar metadata.json (parité avec la route d'origine avant refactor). Nouveau paramètre optionnel du helper, passé par les deux appelants live (route HTTP + bridge R1). 2 tests : présent quand fourni, absent sinon (pas de clé None parasite). Backfill inchangé (workflow_id optionnel). TDD : +5 tests. 29 tests unit VERTS, 0 régression. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -197,6 +197,7 @@ def materialize_anchor_from_b64(
|
||||
*,
|
||||
db_session,
|
||||
source: str = "learned_import",
|
||||
workflow_id: Optional[str] = None,
|
||||
) -> Optional[str]:
|
||||
"""Matérialise une ancre visuelle à partir de `_anchor_image_base64`.
|
||||
|
||||
@@ -206,7 +207,10 @@ def materialize_anchor_from_b64(
|
||||
|
||||
Comportement, si `params` contient une clé `_anchor_image_base64` non vide :
|
||||
1. retire la clé de `params` (`pop`) → le crop volumineux n'est PAS
|
||||
re-stocké en double dans `parameters_json` ;
|
||||
re-stocké en double dans `parameters_json`. Le `pop` a lieu AVANT le
|
||||
`try` : un crop CORROMPU (b64 invalide, PNG au magic cassé) est
|
||||
inexploitable, donc retiré quand même (pas de re-tentative possible,
|
||||
éviter de traîner une donnée morte volumineuse dans les params) ;
|
||||
2. décode le b64 → PIL.Image (pour connaître les dimensions du crop) ;
|
||||
3. `save_anchor_image(...)` écrit `original.png` + `thumbnail.jpg` sur disque ;
|
||||
4. crée une ligne `VisualAnchor` (bbox = plein crop) + `db_session.add(...)` ;
|
||||
@@ -225,6 +229,8 @@ def materialize_anchor_from_b64(
|
||||
params: dict des paramètres du step (mutable — la clé b64 est retirée).
|
||||
db_session: session SQLAlchemy (transaction gérée par l'appelant).
|
||||
source: `capture_method` / marqueur de provenance (défaut learned_import).
|
||||
workflow_id: id du workflow VWB parent — écrit dans le sidecar
|
||||
`metadata.json` (champ informatif, parité avec la route d'origine).
|
||||
|
||||
Returns:
|
||||
L'`anchor_id` créé, ou None si aucun crop exploitable.
|
||||
@@ -245,11 +251,14 @@ def materialize_anchor_from_b64(
|
||||
# bbox plein crop : le b64 est DÉJÀ le crop de la zone cible (cf.
|
||||
# stream_processor crop 80×80), pas un screenshot plein écran.
|
||||
bbox = {"x": 0, "y": 0, "width": width, "height": height}
|
||||
anchor_meta = {"source": source}
|
||||
if workflow_id:
|
||||
anchor_meta["workflow_id"] = workflow_id
|
||||
result = save_anchor_image(
|
||||
anchor_id=anchor_id,
|
||||
image_base64=clean_b64,
|
||||
bounding_box=bbox,
|
||||
metadata={"source": source},
|
||||
metadata=anchor_meta,
|
||||
)
|
||||
if not result.get("success"):
|
||||
logger.warning(
|
||||
|
||||
@@ -453,7 +453,9 @@ def import_core_workflow_to_db(
|
||||
# `materialize_anchor_from_b64` retire la clé b64 de params (pas de
|
||||
# double stockage) et ne commit pas (transaction gérée ci-dessous).
|
||||
from services.anchor_image_service import materialize_anchor_from_b64
|
||||
materialize_anchor_from_b64(step, params, db_session=db_session)
|
||||
materialize_anchor_from_b64(
|
||||
step, params, db_session=db_session, workflow_id=wf_id
|
||||
)
|
||||
step.parameters = params
|
||||
db_session.add(step)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user