App: crash reports and a log file; Help › Report a Bug (#107)

The desktop app installs the crash reporter and a log file at start
(both in a temporary folder in --control and CI runs, #266), journals
the window's commands (background jobs included) and every control
request's method, and ends the journal on a clean exit. A start after a
crash asks: "FieldCraft closed unexpectedly", Report It… or Not Now.

Help › Report a Bug (also Tools › Help and ui.start
diagnostics.bug_report) shows exactly what the GitHub bug form will be
filled with: the version, the OS, the last error, and for a crash where
it happened. Share my session adds the replayable commands, Include the
model file the model. Open GitHub Issue opens the form in the browser
through the system's opener; files to attach are saved to
bug-reports/<time>/ and a long session goes on the clipboard. Nothing is
sent without that click and the user's submit on GitHub.

The control channel answers the new prompts (crashed: report, close;
report_bug: set, close) and ui.inspect shows the draft; it never opens
the browser.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UhhZMBTviDQxZvwnyP89fo
This commit is contained in:
jyvikoandClaude Opus 5.5 authored and Irakli Kourtis committed 2026-10-15 00:36:18 -04:00
1 parent cb7e3a2711
commit e06c85eb9e
9 files changed
+503 -8

No files matched your search

+12
View File
@@ -255,6 +255,18 @@ pub fn draft(input: &Input, version: &str) -> Draft {
Draft { title, fields, url, attachments, clipboard }
}
/// Write a draft's attachments to `bug-reports/<time>/` in the app's data folder (the user
/// attaches them from there); returns the folder.
pub fn save_attachments(draft: &Draft) -> Result<std::path::PathBuf, String> {
let root = fieldcraft_io::recovery::data_dir().ok_or("no data folder on this system")?;
let dir = root.join("bug-reports").join(crate::crash::stamp_now());
fieldcraft_io::fs::create_dir_all(&dir).map_err(|e| e.to_string())?;
for a in &draft.attachments {
fieldcraft_io::fs::write_atomic(&dir.join(&a.name), &a.bytes, false).map_err(|e| e.to_string())?;
}
Ok(dir)
}
/// The fields a link carries (for the review, when a field had to be cut).
fn decode_fields(url: &str) -> Vec<(&'static str, String)> {
let query = url.split_once('?').map_or("", |(_, q)| q);
+1
View File
@@ -240,6 +240,7 @@ impl Session {
opened.history = std::mem::take(&mut self.history);
opened.limits = self.limits;
opened.file_access = std::mem::take(&mut self.file_access);
opened.crash_journal = self.crash_journal;
opened.clear_history();
opened.mark_opened();
*self = opened;