From e7b5febf4f71050dd5bd45d8c31b193b02891aa8 Mon Sep 17 00:00:00 2001 From: JavaZeroo <71128095+JavaZeroo@users.noreply.github.com> Date: Fri, 30 Oct 2026 06:40:39 +0800 Subject: [PATCH 1/2] diag(shell): record per-session size readiness when the shells scenario times out refs #80 The timeout path now writes role, grid, settled and marker presence for every tab, so a stage five failure names the session that was not ready. --- experiments/shell/local_shell_diagnostics.hpp | 12 ++++++++++++ experiments/shell/workspace_shells_scenario.hpp | 12 ++++++++++++ 2 files changed, 24 insertions(+) diff --git a/experiments/shell/local_shell_diagnostics.hpp b/experiments/shell/local_shell_diagnostics.hpp index 3b05e137..5b9e61ea 100644 --- a/experiments/shell/local_shell_diagnostics.hpp +++ b/experiments/shell/local_shell_diagnostics.hpp @@ -31,4 +31,16 @@ inline void local_shell_progress(const std::filesystem::path& directory, std::st raw.close(); require(bool(raw), "local shell private output write failed"); } } +inline void local_shell_size_readiness(const std::filesystem::path& directory, std::string_view role, + unsigned long columns, unsigned long rows, bool settled, + bool marker_present, unsigned long observed_bytes) { + require(role=="cmd" || role=="powershell" || role=="pwsh" || role=="wsl", "invalid local shell diagnostic role"); + std::ofstream progress(directory/"shells-size-readiness.jsonl",std::ios::binary|std::ios::app); + progress<<"{\"schema\":\"termforge-local-shell-size-readiness-v1\",\"role\":\""<render_to(frame.dc, main.left, main.top); selected(Group::bottom)->render_to(frame.dc, bottom.left, bottom.top); frame.save(image_); + // #80: which readiness condition was actually missing when the deadline hit. + const auto diagnostic_directory = std::filesystem::path(image_).parent_path(); + for (const auto group : {Group::bottom, Group::main}) for (auto& tab : group_tabs(group)) { + if (!tab.launch) continue; + const auto [columns, rows] = tab.shell->grid_dimensions(); + const std::string role(tab.launch->kind.begin(), tab.launch->kind.end()); + tf_shell_test::local_shell_size_readiness( + diagnostic_directory, role, static_cast(columns), static_cast(rows), + tab.shell->settled(), + tab.shell->contains_line("TF_SIZE_" + std::to_string(columns) + "x" + std::to_string(rows)), + static_cast(tab.shell->observed_output().size())); + } require(false, ("workspace shells scenario timeout at stage " + std::to_string(stage_)).c_str()); } const auto directory = std::filesystem::path(image_).parent_path(); From fd1d6410c014fc51c58ff621c3e87d4106420ec5 Mon Sep 17 00:00:00 2001 From: JavaZeroo <71128095+JavaZeroo@users.noreply.github.com> Date: Fri, 30 Oct 2026 06:50:56 +0800 Subject: [PATCH 2/2] test(shell): bound the clipboard setup retry in the occupied/oversize cases refs #85 The fixture write that prepares the occupied clipboard is setup, not the product behaviour under test; a short bounded retry keeps a transient desktop holder from failing the whole scenario. --- .../shell/workspace_clipboard_scenario.hpp | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/experiments/shell/workspace_clipboard_scenario.hpp b/experiments/shell/workspace_clipboard_scenario.hpp index d4f902c4..ebccd99b 100644 --- a/experiments/shell/workspace_clipboard_scenario.hpp +++ b/experiments/shell/workspace_clipboard_scenario.hpp @@ -47,11 +47,22 @@ inline void Workspace::scenario_clipboard() { } else if(stage_==3 && evidence.receiver->checkpoint()) { require(captured()==evidence.baseline(),"clipboard receiver baseline differs"); evidence.receiver->release(); + // Setup writes are not the product behaviour under test; Explorer or a + // previous case can hold the desktop clipboard for a moment, so give the + // fixture a small bounded retry instead of failing the whole scenario. + auto prepare_clipboard = [&](auto&& write) { + std::string last; + for (int attempt = 0; attempt < 4; ++attempt) { + try { write(); return; } catch (const std::exception& error) { last = error.what(); } + if (attempt + 1 != 4) Sleep(50 * (attempt + 1)); + } + throw std::runtime_error(last); + }; try { if(evidence.index==0) { - tf_shell::write_unicode_clipboard(window_,u"OCCUPIED_MUST_NOT_SEND"); + prepare_clipboard([&] { tf_shell::write_unicode_clipboard(window_,u"OCCUPIED_MUST_NOT_SEND"); }); evidence.owner=std::make_unique(false); - } else if(evidence.index==1) tf_shell_test::write_oversized_clipboard(window_); + } else if(evidence.index==1) prepare_clipboard([&] { tf_shell_test::write_oversized_clipboard(window_); }); else evidence.owner=std::make_unique(true); } catch(const std::exception& error) { throw std::runtime_error(std::string("clipboard case=")+evidence.name()+