fix(launch): 会话事件流路径只留一个真相源 + crash_found 只认真实证据 + ull launch 也写 launcher.log

M211(来自 M208 实机 e2e 的 D2/D3/D5):

- D2「game/ 双写」:log_path 的契约原来有两种说法(字段文档写「相对 game/」,写入侧一直
  写「相对会话根」的 game/<sid>.jsonl),于是两处读取点各自又多 join 一层 game/,
  ull logs / ull diag / GUI 日志页对每个真实会话都报「找不到事件流」。现在解析只走 core 的
  session_index::event_file(CLI 与 presenter 共用),写入侧只走
  session_index::log_path_for。方向选的是「读取侧不再 join」:存量索引里的 log_path 从第一版
  起就是 game/<id>.jsonl,改读取侧能把用户盘上已有的旧会话一起救回来(零迁移、零改写)。
- D2 夹具:cli/tests/support 与 presenter/host/logs_tests 的索引夹具改成生产形状
  (game/<id>.jsonl)。夹具此前自己说了第三种形状,所以这个 bug 在测试里永远看不见——
  修夹具比修路径更重要。
- D3「crash_found 恒为 true」:logs/latest.log 每局都写,不再被当现场文件(只在已经找到
  权威现场时作为补充证据)。崩溃结论改由 crash_scan::has_crash_evidence 判定(权威现场文件
  / 非 0 退出 + 错误行),索引旗标由 SessionWriter::mark_crash 写入;crash_found 事件恢复
  成纯事实记录。
- D5「ull launch 从不写 launcher.log」:现在与 GUI 共用
  launcher_log::record_start / record_end(字段名与顺序只在一个地方定义),写的是同一份
  <data>/logs/launcher.log。

测试:core 新增 session_path_tests(写入形状 = 读取解析结果 = 真落点的往返)、crash_scan 的
干净退出用例、launcher_log 的行格式锁;CLI 新增 a_launch_attempt_writes_the_launcher_log。
This commit is contained in:
AskaEth committed 2026-11-09 14:12:23 +08:00
1 parent 1748e046f8
commit 4b30bf07f0
12 files changed
+542 -44

No files matched your search

+8 -22
View File
@@ -259,11 +259,7 @@ pub fn start(env: HostEnv, tx: JobSender, target: LaunchTarget) {
};
report.run_dir = request.dirs.game_dir.clone();
report.version_json = Some(env.game.version_json(&target.version_id));
report.java = format!(
"{} (major {})",
request.java.binary.display(),
request.java.major
);
report.java = launcher_log::java_label(&request.java.binary, request.java.major);
for warning in &warnings {
let _ = tx.send(HostTask::Notice(Box::new(crate::dto::NoticeView::new(
&format!("launch-warn:{}", target.id),
@@ -273,14 +269,13 @@ pub fn start(env: HostEnv, tx: JobSender, target: LaunchTarget) {
))));
}
// 命令行(脱敏版)进启动器日志:事故之后「到底用什么参数起的」是第一个要看的。
let line = format!(
"instance={} game_dir={} java={} memory={}MB",
target.id,
report.run_dir.display(),
report.java,
request.memory.max_mb.unwrap_or(0)
// M211/D5:`ull launch` 写同一个文件的同一行(格式在 core 的 `launcher_log` 里)。
launcher_log::record_start(
&target.id,
&report.run_dir,
Some((request.java.binary.as_path(), request.java.major)),
request.memory.max_mb,
);
launcher_log::record("launch", &line);
let config = LauncherConfig::new(env.sessions_root())
.with_launcher(LAUNCHER_NAME, env!("CARGO_PKG_VERSION"));
let session = match Launcher::new(config).spawn(&request) {
@@ -317,16 +312,7 @@ pub fn start(env: HostEnv, tx: JobSender, target: LaunchTarget) {
match session.finish() {
Ok(handle) => {
let outcome = super::identity::outcome_of(&handle);
let line = format!(
"instance={} session={session_id} exit={} type={} uptime={}ms outcome={:?} artifacts={}",
target.id,
handle.exit.code,
handle.exit.exit_type.as_str(),
handle.uptime_ms,
outcome,
handle.artifacts.len()
);
launcher_log::record("launch", &line);
launcher_log::record_end(&target.id, &handle);
report.secrets.push(session_id.clone());
let lines: Vec<String> = super::logs::read_lines(&handle.log_path)
.into_iter()