feat(qw4): hook safety_checks_provider + extension /replay/resume avec acquittements
Some checks failed
tests / Lint (ruff + black) (push) Successful in 16s
tests / Tests unitaires (sans GPU) (push) Failing after 13s
tests / Tests sécurité (critique) (push) Has been skipped

replay_state enrichi de safety_checks, checks_acknowledged, pause_reason,
pause_payload (audit trail).

Branche supervisée pause_for_human :
- appel build_pause_payload() avant bascule paused_need_help
- log [BUS] lea:safety_checks_generated (count, sources)
- fallback safe sur exception (pause sans checks plutôt que crash)
- déclenchement si safety_level/safety_checks déclarés OU execution_mode != autonomous
- sinon comportement legacy (skip silencieux)

POST /replay/resume :
- accepte body { acknowledged_check_ids: [...] }
- vérifie tous les checks required acquittés, sinon 400 required_checks_missing
- stocke checks_acknowledged comme audit trail
- nettoie safety_checks/pause_payload après reprise

Proxy VWB /api/v3/replay/resume → streaming /replay/{id}/resume (forward bearer
token + acknowledged_check_ids).

Backward 100% : workflows sans safety_checks → resume sans acquittement requis.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Dom
2026-05-05 23:45:22 +02:00
parent af13cd80ff
commit 65da557310
4 changed files with 182 additions and 2 deletions

View File

@@ -1113,3 +1113,45 @@ def execute_windows():
return jsonify({'error': 'Streaming server (port 5005) non disponible'}), 503
except Exception as e:
return jsonify({'error': str(e)}), 500
# ---------------------------------------------------------------------------
# QW4 — Proxy /api/v3/replay/resume → streaming /replay/{id}/resume
# Forward Bearer token + body { replay_id, acknowledged_check_ids }.
# Le frontend (PauseDialog) appelle /api/v3/replay/resume via le VWB ;
# on relaye au streaming server pour valider les acquittements safety_checks.
# ---------------------------------------------------------------------------
@api_v3_bp.route('/replay/resume', methods=['POST'])
def replay_resume_proxy():
"""Proxy QW4 vers le serveur streaming pour la reprise avec safety_checks."""
import requests as req
data = request.get_json() or {}
replay_id = data.get('replay_id')
if not replay_id:
return jsonify({'error': 'replay_id manquant'}), 400
streaming_url = os.environ.get('RPA_STREAMING_URL', 'http://localhost:5005')
token = os.environ.get('RPA_API_TOKEN', '')
headers = {'Content-Type': 'application/json'}
if token:
headers['Authorization'] = f'Bearer {token}'
# Body forwardé : uniquement acknowledged_check_ids (replay_id est dans l'URL)
forward_body = {
'acknowledged_check_ids': data.get('acknowledged_check_ids') or [],
}
try:
resp = req.post(
f'{streaming_url}/api/v1/traces/stream/replay/{replay_id}/resume',
json=forward_body,
headers=headers,
timeout=10,
)
return resp.content, resp.status_code, {'Content-Type': 'application/json'}
except req.ConnectionError:
return jsonify({'error': 'streaming_unreachable',
'detail': f'Streaming server non disponible ({streaming_url})'}), 502
except req.RequestException as e:
return jsonify({'error': 'streaming_unreachable', 'detail': str(e)}), 502