Unify processed case listing with blocked states and package downloads

This commit is contained in:
Nico committed 2026-10-23 13:59:15 +02:00
1 parent b8c0d7ec40
commit b42bafb711
8 files changed
+81 -14

No files matched your search

@@ -0,0 +1,31 @@
# Bearbeitete Fälle – 2026-10-23
Die bisher auf terminale Zustände begrenzte Ergebnisliste wurde zur gemeinsamen
Liste bearbeiteter Fälle erweitert: OUTPUT_READY, HANDED_OFF, BLOCKED und
FAILED_TO_ANALYZE. Filter für Kategorie und Projekt, Suche, Sortierung nach
Aktualisierung/Kategorie/Projekt, Routingziel, Sperrgrund und linksseitiger
Iconknopf für den bestehenden projektgeschützten Paketdownload. Blockierte Fälle
werden nicht als abgeschlossen gewertet. Analysezustände/Budgets unverändert.
Lokale Validierung:
```text
python -m pytest tests/test_dashboard.py tests/test_web_auth.py tests/test_human_web.py -q
26 passed, 1 dependency deprecation warning
node tests/dashboard-smoke.mjs
PASS, console_errors: [], overflow: false
node --check src/viano_bug_analysis/web/case-detail.js
PASS
```
Echter Browser prüft die gemeinsame Liste mit Ergebnis und blockiertem Fall,
linken Downloadknöpfen, Testerantwort-Hinweis, Kategorieauswahl und bestehender
Detailnavigation. Mobile Ansicht visuell kontrolliert. Bestehende Tests sichern
projektgebundene Detail-/ZIP-Endpunkte und Anmeldung ab. Routingdaten werden aus
dem Manifest übernommen; die Liste scannt nicht separat das Exportverzeichnis.
Vor Testbeginn Trials 16.939.129.428 Bytes, Fehlerkorpus 2.471.041 Bytes, nur kleine
temporäre UI-/Unit-Testdaten. Entwicklungsbudget weiterhin unter 20 GB.
Keine LLM-/Cloud-Aufrufe oder Kosten, keine Änderung produktiver Fälle/Dienste.
Serverinstallation, HTTPS, echter automatischer Feldtest und Betriebssicherung
bleiben Aufgaben auf dem Linux-Zielsystem.
+17 -2
View File
@@ -83,8 +83,23 @@ Diagnose sowie Coding- und Human-Dev-Auftrag. Befunde bleiben ausdrücklich
KI-Bewertungen mit Belegreferenzen. Das Belegverzeichnis zeigt Herkunft und
Abdeckungsinformationen; die vollständigen Recherchepayloads stehen im ZIP.
Ein expliziter Auftrag „Kein Auftrag“ wird von einem noch nicht erstellten Dokument
unterschieden. Die separate Ergebnisliste enthält abgeschlossene und zur
menschlichen Prüfung ausgeleitete Fälle, mit Projektfilter und Suche.
unterschieden. Die gemeinsame Liste **Bearbeitete Fälle** enthält Ergebnisse
(`OUTPUT_READY`, Ziel Outbox), bereits übergebene Fälle (`HANDED_OFF`), blockierte
Fälle (`BLOCKED`) und gescheiterte Analysen (`FAILED_TO_ANALYZE`, Ziel Review).
Blockierte Fälle bleiben ausdrücklich unvollständig und können weiterhin auch
in der Übersicht wartender Fälle erscheinen. Ein Sperrgrund wird angezeigt;
`AWAITING_TEST_FEEDBACK` erscheint als „Wartet auf Testerantwort“.
Projekt- und Kategoriefilter sowie Suche sind kombinierbar. Die Sortierung erlaubt
neueste/älteste Aktualisierung, Kategorie oder Projekt. Links in jeder Zeile lädt
ein beschrifteter Iconknopf die vollständige aktuelle Fallakte herunter, auch für
blockierte oder gescheiterte Analysen. Der Download ändert keinen Status und löst
keine Analyse aus. Die Ablagespalte zeigt das im Manifest gespeicherte Routingziel;
es ist keine unabhängige Bestandsprüfung des Outbox-Dateiverzeichnisses. Der Download
liest die verwaltete aktuelle Akte und funktioniert auch ohne separate Exportkopie.
Angezeigt wird der aktuelle Stand je Fall/Paket. Nach Wiedereröffnung erscheinen
aktive Fälle wieder in der laufenden Übersicht; ältere Revisionen bleiben in der
Detailansicht erhalten. Berechtigungen und Projektgrenzen gelten auch für Downloads.
Die Revisionsauswahl liest akzeptierte Artefakte aus der Checkpointhistorie der
aktuellen vollständigen Akte. Ergebnisse neuerer Revisionen erscheinen nicht in
+3 -1
View File
@@ -103,7 +103,9 @@ def case_view(reader):
'key':key(manifest['case_id']) + '.' + key(manifest['package_id']),
'project_id': manifest['project'].get('id'), 'title': title,
'progress': round(sum(complete) / len(complete) * 100), 'calls': calls,
'updated_at': manifest['updated_at']}
'updated_at': manifest['updated_at'],
'destination':manifest.get('routing',{}).get('destination'),
'reason':manifest.get('routing',{}).get('reason')}
def aggregate(cases):
+16 -6
View File
@@ -29,14 +29,23 @@ function panel(title,content,expanded=true){
}
function renderArchive(){
if(!overview)return;
const project=$('result-project').value,search=$('result-search').value.trim().toLocaleLowerCase('de-DE');
const list=overview.cases.filter(c=>finished.has(c.state)&&(!project||project===c.project_id)&&(!search||`${c.case_id} ${c.title}`.toLocaleLowerCase('de-DE').includes(search))).sort((a,b)=>b.updated_at.localeCompare(a.updated_at));
const project=$('result-project').value,search=$('result-search').value.trim().toLocaleLowerCase('de-DE'),status=$('result-status').value,sort=$('result-sort').value;
const labels={OUTPUT_READY:'Ergebnis bereit',HANDED_OFF:'Übergeben',BLOCKED:'Blockiert / wartet',FAILED_TO_ANALYZE:'Analyse gescheitert / Review'};
const list=overview.cases.filter(c=>(finished.has(c.state)||c.state==='BLOCKED')&&(!status||status===c.state)&&(!project||project===c.project_id)&&(!search||`${c.case_id} ${c.title}`.toLocaleLowerCase('de-DE').includes(search))).sort((a,b)=>{
const tie=b.updated_at.localeCompare(a.updated_at)||a.key.localeCompare(b.key);
if(sort==='category')return labels[a.state].localeCompare(labels[b.state],'de')||tie;
if(sort==='project')return a.project_id.localeCompare(b.project_id,'de')||tie;
return sort==='oldest'?-tie:tie;
});
$('result-count').textContent=`${list.length} ${list.length===1?'Fall':'Fälle'}`;
const body=$('result-rows');body.replaceChildren();
list.forEach(c=>{const row=document.createElement('tr'),name=document.createElement('td'),link=text('a',c.case_id,'case-link');link.href=`#case/${c.key}`;
name.append(link,text('small',c.title));row.append(name,text('td',c.project_id),text('td',({OUTPUT_READY:'Ergebnis bereit',HANDED_OFF:'Übergeben',FAILED_TO_ANALYZE:'Menschliches Review'})[c.state]),text('td',String(c.analysis_revision)),text('td',new Date(c.updated_at).toLocaleString('de-DE')));body.append(row);});
if(!list.length){const row=document.createElement('tr'),cell=text('td','Keine abgeschlossenen Fälle für diese Auswahl.','empty');cell.colSpan=5;row.append(cell);body.append(row);}
const action=document.createElement('td'),download=text('button','↓','package-icon');download.type='button';download.title=`Fallakte ${c.case_id} herunterladen`;download.setAttribute('aria-label',download.title);download.addEventListener('click',()=>downloadCase(c,download));action.append(download);
const category=text('td',labels[c.state]);if(c.reason)category.append(text('small',c.reason==='AWAITING_TEST_FEEDBACK'?'Wartet auf Testerantwort':c.reason));
name.append(link,text('small',c.title));row.append(action,name,text('td',c.project_id),category,text('td',({outbox:'Outbox',review:'Review',inbox:'Inbox',cases:'Fallablage'})[c.destination]||'Fallablage'),text('td',String(c.analysis_revision)),text('td',new Date(c.updated_at).toLocaleString('de-DE')));body.append(row);});
if(!list.length){const row=document.createElement('tr'),cell=text('td','Keine bearbeiteten Fälle für diese Auswahl.','empty');cell.colSpan=7;row.append(cell);body.append(row);}
}
function clearDetails(){selectedCase=null;detailHash=null;detailLoading=false;$('result-search').value='';}
function clearDetails(){selectedCase=null;detailHash=null;detailLoading=false;$('result-search').value='';$('result-status').value='';$('result-sort').value='newest';$('result-count').textContent='0 Fälle';}
async function downloadCase(data,button){
const generation=session,credential=token;button.disabled=true;error();
try{const response=await fetch(`/v1/cases/${data.key}/package`,{credentials:'same-origin',cache:'no-store'});
@@ -113,9 +122,10 @@ function route(){
if($('test-page'))$('test-page').hidden=true;
const detail=location.hash.startsWith('#case/'),results=location.hash==='#results',submissionPage=location.hash==='#submit';
$('overview-page').hidden=detail||results||submissionPage;$('results').hidden=!results;$('case-detail').hidden=!detail;$('submit').hidden=!submissionPage;
document.querySelector('header h1').textContent=detail?'Die Fallakte.':results?'Abgeschlossene Fälle.':submissionPage?'Auftrag übergeben.':'Analyse im Blick.';
document.querySelector('header h1').textContent=detail?'Die Fallakte.':results?'Bearbeitete Fälle.':submissionPage?'Auftrag übergeben.':'Analyse im Blick.';
document.querySelectorAll('nav a').forEach(a=>a.classList.toggle('selected',detail?a.hash==='#cases':a.hash===(results?'#results':submissionPage?'#submit':location.hash==='#cases'?'#cases':'#overview')));
if(detail)loadDetail(location.hash);if(results)renderArchive();
}
$('result-project').addEventListener('change',renderArchive);$('result-search').addEventListener('input',renderArchive);
$('result-status').addEventListener('change',renderArchive);$('result-sort').addEventListener('change',renderArchive);
window.addEventListener('hashchange',route);route();
+1
View File
@@ -4,3 +4,4 @@
.case-link{color:#4f61c7;font-weight:650;text-decoration:none}.case-link:hover{text-decoration:underline}.detail-panel>summary{font-size:18px;font-weight:650;cursor:pointer}.detail-body{margin-top:20px}.semantic-text{color:#45516a;white-space:pre-wrap;overflow-wrap:anywhere;margin:5px 0 12px}.semantic-fields{display:grid;grid-template-columns:minmax(130px,190px) minmax(0,1fr);gap:12px 22px;margin:0}.semantic-fields dt{font-size:12px;color:#7c8599;font-weight:600}.semantic-fields dd{margin:0;min-width:0}.semantic-fields .semantic-fields{grid-template-columns:130px minmax(0,1fr)}.semantic-list{display:grid;gap:10px}.semantic-item{border-left:2px solid #e6eaf6;padding-left:13px}.statement small{display:block;color:#9299a8}.reference-list{color:#7785ae;font-size:11px;overflow-wrap:anywhere}.hypothesis-card{border:1px solid #e6e9f1;border-radius:9px;padding:20px;margin-bottom:16px}.hypothesis-heading{display:flex;align-items:center;justify-content:space-between;gap:12px}.hypothesis-heading h3{font-size:16px;margin:0}.hypothesis-heading .badge{flex-shrink:0}.hypothesis-card .detail-panel{margin:16px 0 0;padding:14px;box-shadow:none}.hypothesis-card .detail-panel>summary{font-size:13px}.evidence-columns{display:grid;grid-template-columns:1fr 1fr;gap:26px;margin-top:18px}.evidence-columns h4{font-size:12px;color:#68758c}.evidence-columns>div{background:#f7f8fc;padding:16px;border-radius:7px;min-width:0}.detail-controls{display:flex;align-items:center;gap:16px}.detail-controls label{margin:0;min-width:170px}.detail-controls button{margin-top:20px}.history-note{background:#f1f3fd;color:#6472ae;padding:12px;border-radius:6px;font-size:12px}.assignment-status{font-weight:650;color:#34436a}.human-document{white-space:pre-wrap;overflow-wrap:anywhere;font:13px/1.7 system-ui}.next-actions p{color:#5f6d85}#case-detail .panel{scroll-margin-top:20px}@media(max-width:900px){.semantic-fields,.semantic-fields .semantic-fields{grid-template-columns:1fr;gap:4px}.semantic-fields dd{margin-bottom:12px}.detail-controls{flex-wrap:wrap}}@media(max-width:700px){.hypothesis-heading{align-items:flex-start;flex-direction:column}.evidence-columns{grid-template-columns:1fr}.detail-controls{width:100%}.detail-controls button{margin-top:0}.hypothesis-card{padding:14px}.rail{flex-wrap:wrap}.rail nav{flex-wrap:wrap}}
#key-projects{border:1px solid var(--line);border-radius:8px;padding:12px;margin-bottom:14px}#key-projects label{display:flex;align-items:center;gap:10px;margin:8px 0}#key-projects input{width:auto;margin:0}#key-list button{margin:10px 8px 0 0}#key-list input{font-family:monospace}
#result-rows .package-icon{width:34px;height:34px;padding:0;font-size:22px;line-height:1;border-radius:8px}#result-rows td small{display:block;max-width:320px;overflow-wrap:anywhere;color:var(--muted);font-size:11px}
+4 -4
View File
@@ -6,7 +6,7 @@
<script src="/assets/case-detail.js" defer></script><script src="/assets/operations.js" defer></script><script src="/assets/test-requests.js" defer></script></head>
<body>
<aside class="rail"><a class="brand" href="/">V<span>BA</span><small>BugAnalysis</small></a>
<nav aria-label="Navigation"><a href="#overview" class="selected">Übersicht</a><a href="#cases">Laufende Fälle</a><a href="#results">Ergebnisse</a><a href="#tests">Testanfragen</a><a href="#submit">Auftrag übergeben</a><a href="#keys">API-Schlüssel</a></nav>
<nav aria-label="Navigation"><a href="#overview" class="selected">Übersicht</a><a href="#cases">Laufende Fälle</a><a href="#results">Bearbeitete Fälle</a><a href="#tests">Testanfragen</a><a href="#submit">Auftrag übergeben</a><a href="#keys">API-Schlüssel</a></nav>
<div class="rail-bottom">ANALYSEBETRIEB<br><span id="connection">Nicht verbunden</span><button id="logout" type="button" hidden>Abmelden</button></div></aside>
<main id="overview">
<header><div><div class="eyebrow">BETRIEBSÜBERSICHT</div><h1>Analyse im Blick.</h1><p>Aktuelle Arbeit und messbarer Verbrauch.</p></div><div class="live"><span class="dot"></span><span id="updated">Warte auf Verbindung</span></div></header>
@@ -30,9 +30,9 @@
<section class="panel" id="cases"><div class="section-head"><div><div class="eyebrow">FALLAKTEN</div><h2>Aktuelle Untersuchungen</h2></div><label class="filter">Projekt <select id="project-filter"><option value="">Alle freigegebenen Projekte</option></select></label></div>
<div class="table-wrap"><table><thead><tr><th>Fall</th><th>Projekt</th><th>Zustand</th><th>Revision</th><th>Fortschritt</th></tr></thead><tbody id="case-rows"></tbody></table></div></section>
</div>
<section class="panel" id="results" hidden><div class="section-head"><div><div class="eyebrow">ERGEBNISSE</div><h2>Abgeschlossene Fälle</h2><p>Diagnosen, Aufträge und Fälle für menschliches Review.</p></div></div>
<div class="form-grid"><label>Projekt<select id="result-project"><option value="">Alle freigegebenen Projekte</option></select></label><label>Fall suchen<input id="result-search" type="search" placeholder="Bug-ID oder Fallname"></label></div>
<div class="table-wrap"><table><thead><tr><th>Fall</th><th>Projekt</th><th>Ergebnis</th><th>Revision</th><th>Aktualisiert</th></tr></thead><tbody id="result-rows"></tbody></table></div></section>
<section class="panel" id="results" hidden><div class="section-head"><div><div class="eyebrow">FALLABLAGE</div><h2>Bearbeitete Fälle</h2><p>Ergebnisse in der Outbox, übergebene, blockierte und gescheiterte Analysen. Blockiert bedeutet noch nicht abgeschlossen.</p></div><span id="result-count" class="badge">0 Fälle</span></div>
<div class="form-grid"><label>Projekt<select id="result-project"><option value="">Alle freigegebenen Projekte</option></select></label><label>Kategorie<select id="result-status"><option value="">Alle Kategorien</option><option value="OUTPUT_READY">Ergebnis bereit / Outbox</option><option value="HANDED_OFF">Übergeben</option><option value="BLOCKED">Blockiert / wartet</option><option value="FAILED_TO_ANALYZE">Analyse gescheitert / Review</option></select></label><label>Sortierung<select id="result-sort"><option value="newest">Zuletzt aktualisiert</option><option value="oldest">Älteste zuerst</option><option value="category">Kategorie</option><option value="project">Projekt</option></select></label><label>Fall suchen<input id="result-search" type="search" placeholder="Bug-ID oder Fallname"></label></div>
<div class="table-wrap"><table><thead><tr><th><span class="download-heading">Paket</span></th><th>Fall</th><th>Projekt</th><th>Kategorie / Grund</th><th>Ablage</th><th>Revision</th><th>Aktualisiert</th></tr></thead><tbody id="result-rows"></tbody></table></div></section>
<section id="case-detail" aria-live="polite" hidden></section>
<section class="panel" id="submit" hidden><div class="section-head"><div><div class="eyebrow">EINGANG</div><h2>Auftrag übergeben</h2><p>BugAnalysis erstellt und ergänzt die Fallakte.</p></div></div>
<form id="submit-form"><div class="form-grid"><label>Auftragstyp<select id="kind"><option value="new_case">Neue Analyse</option><option value="case_return">Abgeschlossene Akte zurückgeben</option></select></label>
+6
View File
@@ -69,6 +69,12 @@ try {
if(await evaluate("document.getElementById('token-count').textContent")!=='35.300')throw new Error('Project aggregation differs');
await evaluate("location.hash='#results'");
await until(()=>evaluate("!document.getElementById('results').hidden&&document.querySelector('#result-rows a')"));
if(!await evaluate("document.querySelectorAll('#result-rows .package-icon').length===2&&document.getElementById('result-rows').textContent.includes('Wartet auf Testerantwort')"))throw new Error('Processed list missing blocked case or download controls');
await evaluate("document.getElementById('result-status').value='BLOCKED';document.getElementById('result-status').dispatchEvent(new Event('change'))");
if(!await evaluate("document.querySelectorAll('#result-rows .package-icon').length===1&&document.getElementById('result-rows').textContent.includes('BUG-000020')"))throw new Error('Blocked category filter failed');
await evaluate("document.getElementById('result-status').value='OUTPUT_READY';document.getElementById('result-status').dispatchEvent(new Event('change'));document.getElementById('result-sort').value='category';document.getElementById('result-sort').dispatchEvent(new Event('change'))");
if(!await evaluate("document.querySelectorAll('#result-rows .package-icon').length===1&&document.getElementById('result-rows').textContent.includes('BUG-000017')"))throw new Error('Outbox category filter failed');
await writeFile(path.join(output,'processed-cases.png'),Buffer.from((await send('Page.captureScreenshot',{format:'png'})).data,'base64'));
await evaluate("document.querySelector('#result-rows a').click()");
await until(()=>evaluate("document.getElementById('case-detail').textContent.includes('SQL-Vorlage mit Platzhaltern')"));
if(!await evaluate("document.getElementById('case-detail').textContent.includes('Passende Spawndaten ermitteln')"))throw new Error('Human assignment absent');
+3 -1
View File
@@ -57,8 +57,10 @@ def test_correction_slice_disjoint_derived_excluded_and_thinking_not_doubled(mon
chain.append(chain[0])
monkeypatch.setattr(module,'checkpoint_chain',lambda reader:chain)
reader=Reader(docs,{'case_id':'BUG-1','package_id':'one','analysis_revision':1,'package_generation':10,
'state':'INVESTIGATING_HYPOTHESES','artifacts':{'case':'case'},'project':{'id':'p'},'updated_at':'now'})
'state':'INVESTIGATING_HYPOTHESES','artifacts':{'case':'case'},'project':{'id':'p'},'updated_at':'now',
'routing':{'destination':'inbox','reason':'AWAITING_TEST_FEEDBACK'}})
view=case_view(reader)
assert view['destination']=='inbox' and view['reason']=='AWAITING_TEST_FEEDBACK'
assert len(view['calls'])==2
usage=aggregate([view]);buckets={b['id']:b for b in usage['buckets']}
assert buckets['investigation']['input_tokens']==100