fix(dashboard,worker): vérité produit P0 — dashboard+worker+VWB export
Some checks failed
tests / Lint (ruff + black) (push) Failing after 1m46s
tests / Tests unitaires (sans GPU) (push) Failing after 2m0s
tests / Tests sécurité (critique) (push) Has been skipped

War-room clôture DGX 2026-06-18 (recadrage Dom : graphe/apprentissage/mémoire/dashboard = surface produit P0).
Le dashboard et le statut worker affichaient des états faux ; corrige pour refléter la vérité du produit.

- dashboard FAISS: distingue index brut / metadata HMAC invalide / runtime / absent (plus de faux "inactif")
- dashboard process-mining: 503 explicite missing_dependency (plus de message trompeur)
- dashboard /api/workflows + system/status: lecture DB VWB v3 canonique (total réel = 24, plus de 0)
- worker /processing/status: véridique (lit _worker_health.json) + statut "idle/armé (lazy)" distinct de "dégradé (échec)"
- VWB export: N steps -> N actions/edges (dernière action n'est plus perdue)
- tests: dashboard routes, worker status truthfulness, export VWB

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Dom
2026-06-18 17:50:12 +02:00
parent 6d5ef51c60
commit ec1fb81054
8 changed files with 626 additions and 56 deletions

View File

@@ -1392,7 +1392,13 @@
// Status indicator
const faissStatusEl = document.getElementById('faissStatus');
if (faiss.error) {
if (faiss.status === 'metadata_invalid') {
faissStatusEl.textContent = '⚠️';
faissStatusEl.title = 'Index brut présent, métadonnées invalides';
} else if (faiss.status === 'metadata_missing') {
faissStatusEl.textContent = '⚠️';
faissStatusEl.title = 'Index brut présent, métadonnées absentes';
} else if (faiss.error) {
faissStatusEl.textContent = '❌';
faissStatusEl.title = faiss.error;
} else if (faiss.status === 'index_not_found') {
@@ -1419,6 +1425,8 @@
if (faiss.nlist) details.push(`nlist: ${faiss.nlist}`);
if (faiss.nprobe) details.push(`nprobe: ${faiss.nprobe}`);
if (faiss.metadata_count) details.push(`Métadonnées: ${faiss.metadata_count}`);
if (faiss.metadata_status) details.push(`Metadata: ${faiss.metadata_status}`);
if (faiss.metadata_error) details.push(`Metadata error: ${faiss.metadata_error}`);
document.getElementById('faissDetails').textContent = details.length > 0 ?
details.join(' • ') : (faiss.error || faiss.status || 'Aucune info disponible');
@@ -1434,6 +1442,12 @@
if (faiss.status === 'index_not_found') {
recommendations.push('📝 Traitez des sessions pour créer l\'index FAISS');
}
if (faiss.status === 'metadata_invalid') {
recommendations.push('⚠️ Index brut présent mais métadonnées invalides : re-signer ou régénérer les métadonnées FAISS');
}
if (faiss.status === 'metadata_missing') {
recommendations.push('⚠️ Index brut présent sans métadonnées : reconstruire les métadonnées FAISS');
}
if (recommendations.length > 0) {
recoEl.innerHTML = recommendations.join('<br>');
recoEl.style.display = 'block';
@@ -2818,10 +2832,29 @@
const detailsEl = document.getElementById('streamServerDetails');
try {
const data = await fetchJSON(`${STREAMING_BASE}/stats`);
const [data, processing] = await Promise.all([
fetchJSON(`${STREAMING_BASE}/stats`),
fetchJSON(`${STREAMING_BASE}/processing/status`).catch(e => ({error: e.message}))
]);
statusEl.innerHTML = '<span style="color:#22c55e;">✅</span>';
statusEl.title = 'Serveur streaming en ligne';
const processingReady = processing && processing.processing_ready === true;
// « En veille » (armé/lazy) ≠ « dégradé » : un worker neuf sans
// session a tous ses composants à false par design (chargement à la
// 1re session), ce n'est PAS une panne. Seul status==='degraded'
// (init tentée et en échec) est une vraie alerte.
const processingArmed = processing && (processing.armed === true || processing.status === 'idle');
const processingDegraded = processing && !processing.error && processing.status === 'degraded';
const statusHint = (processing && processing.status_hint) || '';
statusEl.innerHTML = processingDegraded
? '<span style="color:#f59e0b;">⚠️</span>'
: processingArmed
? '<span style="color:#3b82f6;">⏸️</span>'
: '<span style="color:#22c55e;">✅</span>';
statusEl.title = processingDegraded
? `Streaming en ligne, worker apprentissage dégradé${statusHint ? ' — ' + statusHint : ''}`
: processingArmed
? `Streaming en ligne, worker en veille${statusHint ? ' — ' + statusHint : ''}`
: 'Serveur streaming en ligne';
document.getElementById('streamActiveSessions').textContent = data.active_sessions || 0;
document.getElementById('streamTotalEvents').textContent = data.total_events || 0;
@@ -2837,6 +2870,31 @@
if (data.events_per_second !== undefined) rows.push({label: 'Événements/sec', value: (data.events_per_second || 0).toFixed(2)});
if (data.memory_usage_mb !== undefined) rows.push({label: 'Mémoire utilisée', value: Math.round(data.memory_usage_mb) + ' MB'});
if (data.server_version) rows.push({label: 'Version serveur', value: data.server_version});
if (processing && !processing.error) {
const status = processing.status || 'unknown';
const workerIcon = processingReady ? '✅' : (processingArmed ? '⏸️' : '⚠️');
rows.push({
label: 'Worker apprentissage',
value: `${workerIcon} ${status}`
});
rows.push({
label: 'Composants intelligence',
value: processing.components_ready
? 'prêts'
: (processingArmed ? 'en veille (chargés à la 1re session)' : 'non prêts')
});
if (statusHint) {
rows.push({label: 'Détail worker', value: statusHint});
}
if (processing.queue_length !== undefined) {
rows.push({label: 'Queue apprentissage', value: processing.queue_length});
}
if (processing.last_cycle) {
rows.push({label: 'Dernier cycle worker', value: new Date(processing.last_cycle).toLocaleString('fr-FR')});
}
} else if (processing && processing.error) {
rows.push({label: 'Worker apprentissage', value: `${processing.error}`});
}
if (rows.length === 0) {
// Afficher les données brutes si les clés attendues ne sont pas présentes