fix(app-iced): M95 r4 —— 归属判据回归 / 置信度档 / 运行中页头

代码复审 r3 的四条 p2,修三条、登记一条(P2-4 页脚余量 13px,后置)。

**P2-1(回归修复)**:r3 把归属判据写成「有选中就只认选中」,把**导入崩溃文件**与 `open_session`
这两条路径的诊断一并关掉了——`559ba20` 是**无条件**画的,所以这是 r3 引入的**代码级回归**,
不是既有缺口(今天 iced 侧没有 dispatch 站点,那些还在遗留的 Slint shell 里,故复审未判 p1)。
判据改成三分支(`app/view/logs.rs`):

| live_session | selected_session | 报告算数的条件 |
|---|---|---|
| Some(s) | 任意 | `report.session == s`(只有它在线时日志行是它在追加,别人的报告挂上来就是串场) |
| None | Some(s) | `report.session == s` **或** 报告不属于任何已知会话(导入用 `file:<文件名>`,永不在索引里) |
| None | None | 全部算数(此时日志行只可能来自 `open_session`/导入,与报告同一次调用发出) |

新增两条测试(都走真实 reducer):`an_imported_crash_file_still_shows_its_findings`、
`a_live_run_says_it_is_running_instead_of_nothing_runs`。
残留(今天不可达,已登记):`open_session(A)` 在 `selected_session` 仍停在上场 B 时会被挡掉——
真要修得让 presenter 在 `LogLinesChanged` 记下「日志行属于哪一场」。

**P2-2 置信度档(视觉变更)**:`Severity`(借 core 的 `critical/warning/notice` 词汇)→
`Confidence`(`High/Medium/Low`)+ 新 key `LogsConfidenceHigh/Medium/Low`(中英齐,标题明确写
「高/中/低置信度」)。阈值仍是 80/50,但它现在是**档位定义**而不是「严重度代理」。
摘要里的 `findings=rule:high(85)` 同步换档位 slug。DTO 补齐 `severity` 之后应换回真严重度(finding)。

**P2-3 运行中的页头**:session 索引只在**结束**时追加,运行中的那一场不在 `log_sessions` 里 ⇒
拿不到实例名与结局 ⇒ r3 之前页头在日志滚动时一直说「当前没有运行中的实例」(demo 伪造了身份,
截图照不出来)。`Logs` 新增 `live: bool`(`live_session.is_some()`),`with_status` 三级回退:
有实例+结局 → `实例 · 时间 · 结局`;运行中但没结局 → 新 key `LogsSubtitleLive`(「Running now —
logs stream as they arrive」);都没有 → `LogsSubtitleIdle`。
新增 `a_live_run_says_it_is_running_instead_of_nothing_runs`(真实 reducer:只有
`LogAppended(running=true)`,断言副标题不是 Idle 文案且含 `Running now`)。
新增 demo 状态 `ULL_LOGS_FIXTURE=running` 用于出图。

**变异实测**(每条:回退 → 红 → 放回 → 绿):见报告。

验证:`fmt --check` 通过;`clippy -p unlucky-app-iced -p unlucky-i18n --all-targets -D warnings` 干净;
`cargo test` **280 + 6 + 23 passed / 0 failed**;行数最大 `pages/mod.rs` 367、`app/view.rs` 324、
`app/view/logs.rs` 337。
This commit is contained in:
AskaEth committed 2026-11-02 22:27:01 +08:00
1 parent 5ed2bf3a7d
commit 0eb069fa37
9 files changed
+414 -267

No files matched your search

+18 -4
View File
@@ -26,7 +26,7 @@
use unlucky_i18n::Lang;
use super::model::{Finding, Level, Line, Logs, Outcome, Severity};
use super::model::{Confidence, Finding, Level, Line, Logs, Outcome};
/// 挑演示状态的环境变量。
pub const STATE_ENV: &str = "ULL_LOGS_FIXTURE";
@@ -46,15 +46,21 @@ pub enum State {
Live,
/// 崩溃现场:日志 + 诊断结论。
Crash,
/// **正在跑**的一场(页头走「正在运行」那条文案;session 索引里还没有它)。
Running,
}
impl State {
/// 从环境变量取值解析(未设 / 不认识一律 `Crash`)。
///
/// 取值:`empty`(空态)/ `live`(正常跑完的一场)/ `running`(**正在跑**:页头走「正在运行」)
/// / 其余 = `crash`。
#[must_use]
pub fn of_env_value(raw: Option<&str>) -> Self {
match raw {
Some("empty") => Self::Empty,
Some("live") => Self::Live,
Some("running") => Self::Running,
_ => Self::Crash,
}
}
@@ -74,6 +80,14 @@ impl State {
..Logs::default()
}
.with_status(lang),
// 运行中:还没有结局、也不在 session 索引里(`host::logs::sessions` 只在结束时追加),
// 所以既没有实例名也没有 outcome —— 页头只能走「正在运行」那条中性文案。
Self::Running => Logs {
live: true,
lines: parse(LIVE_SAMPLE),
..Logs::default()
}
.with_status(lang),
Self::Crash => crash().with_status(lang),
}
}
@@ -128,8 +142,8 @@ pub(super) fn parse(text: &str) -> Vec<Line> {
.collect()
}
/// 三条诊断(`rule` / `title` / `hint` 抄自规则表;`severity` 走 [`Severity::of_confidence`],
/// 也就是 DTO 缺 `severity` 时产品路径用的同一条折算)。
/// 三条诊断(`rule` / `title` / `hint` 抄自规则表;置信度档走 [`Confidence::of`],
/// 也就是 DTO 缺 `severity` 时产品路径用的同一条档位定义)。
fn findings() -> Vec<Finding> {
vec![
finding(
@@ -159,7 +173,7 @@ fn findings() -> Vec<Finding> {
fn finding(rule: &str, confidence: u8, title: &str, hint: &str, action: Option<&str>) -> Finding {
Finding {
rule: rule.to_owned(),
severity: Severity::of_confidence(confidence),
confidence_band: Confidence::of(confidence),
title: title.to_owned(),
hint: hint.to_owned(),
confidence,