Docs: crash reports, the log file, Report a Bug and privacy (#107)

docs/crash-reports.md: what is kept where, what a bug report carries
(and only when ticked), the CLI and commands for agents; README's
privacy note; the control protocol's new prompt answers.

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 e06c85eb9e
commit b19277ae7a
3 files changed
+95 -1

No files matched your search

+7
View File
@@ -135,6 +135,13 @@ Rust 1.95 or later, with the `wasm32-unknown-unknown` target for `cargo xtask ci
- [Releasing](docs/releasing.md): how a release is built, verified and published
- [Contributing](CONTRIBUTING.md) and [agent instructions](AGENTS.md)
## Privacy
FieldCraft sends nothing anywhere on its own: no analytics, no telemetry, no automatic crash
upload. Crash reports and the log stay on your computer; Help › Report a Bug opens a GitHub issue
you review and submit yourself, with your session or model attached only if you tick them
([docs/crash-reports.md](docs/crash-reports.md)).
## License
Dual-licensed under [MIT](LICENSE-MIT) or [Apache-2.0](LICENSE-APACHE), at your option. Notices
+1 -1
View File
@@ -50,7 +50,7 @@ commands (files, undo, `model.info`) leave the ribbon where it is.
| `ui.select` | `kind` (`set`, `material`, `section`, `step`, `boundary_condition`, `load`, `reference_point`, `coupling`, `fluid_domain`), `name`; nothing to deselect | selects a definition as a browser click does; its properties (with Edit and Delete) show in the context panel |
| `ui.select` (viewport) | any of `filter`, `box_mode`, `mode`, `clear`, `at`, `box`, `geometry`, `nodes`, `elements`, `faces` (and no `kind`) | changes the viewport's selection (see [Selection](#selection)); returns `ui.selection`'s reply |
| `ui.selection` | — | the viewport's selection: `filter`, `box_mode` (box selection on), `count`, `capped`, `summary`, `items`, `params` (the selection as a set's members), `hover` (what a click would pick under the pointer) |
| `ui.prompt` | `answer`: for `unsaved` `save`, `discard` or `cancel`; for `design` `new` (a new model with the design), `keep` (the design over the current model) or `cancel`; for `replace_mesh` `replace` (runs the form, replacing the mesh) or `cancel` (the form stays open); for `delete` (the item menu's Delete… or the Delete key: `{delete: question, kind, name, users, allowed}`) `delete` (with a datum's partitions when it has any; refused when `allowed` is false) or `cancel`; for `recover` `recover` (with `id` from `file.recovery.list`) or `close`; for `samples` `open` (with `name`) or `close` | answers the window's question (`ui.inspect`'s `prompt`) |
| `ui.prompt` | `answer`: for `unsaved` `save`, `discard` or `cancel`; for `design` `new` (a new model with the design), `keep` (the design over the current model) or `cancel`; for `replace_mesh` `replace` (runs the form, replacing the mesh) or `cancel` (the form stays open); for `delete` (the item menu's Delete… or the Delete key: `{delete: question, kind, name, users, allowed}`) `delete` (with a datum's partitions when it has any; refused when `allowed` is false) or `cancel`; for `recover` `recover` (with `id` from `file.recovery.list`) or `close`; for `samples` `open` (with `name`) or `close`; for `crashed` (the start after a crash: `{crashed: [reports]}`) `report` (opens Report a Bug for the newest) or `close`; for `report_bug` (`{report_bug: draft}`) `set` (any of `title`, `observed`, `share_session`, `include_model`; replies with the draft) or `close` — the browser opens only from a click (docs/crash-reports.md) | answers the window's question (`ui.inspect`'s `prompt`) |
| `ui.item` | `kind` (`body`, `surface`, or a definition kind as `ui.select` takes it, `datum`, `partition`), `name`, `action`: `edit`, `hide`, `show`, `isolate`, `show_all`, `delete`, or `activate`/`deactivate` with `step` | an item menu's entry, as a right click on the item and a click on the entry: runs the same command (`delete` opens the delete prompt, answered with `ui.prompt`) |
| `ui.dialog` | `params?` (JSON object, fills the form), `use_selection?`, `run?`, `submit?`, `cancel?` | drives the open command form, in that order; `use_selection` is the form's Target row (see [Selection](#selection)); `run` returns the command's result and closes the form on success; `submit` is the form's OK button: a mesher that would replace a body's mesh first asks (`prompt` `replace_mesh`, with the question and what else goes: sets listing the old labels, solved results), and meshers run in the background (`busy`); a form opened by another form's **Create …** button (a reference field with nothing to pick) goes back to that form on `run` (with the new item picked) or `cancel` |
| `ui.click` | `x`, `y` (screen points), `button?`: `left`/`right`/`middle`, `shift?`, `ctrl?`, `alt?`, `double?` | real pointer input; in the viewport a click selects what is under it (Shift adds, Ctrl/Cmd adds or removes, a click on nothing clears the selection), and a click on a results plot also probes the nearest node of the face under it (`solve.probe`; `ui.inspect` shows the values) |
+87
View File
@@ -0,0 +1,87 @@
# Crash reports, the log file and Report a Bug
## Privacy
FieldCraft sends nothing anywhere on its own: no analytics, no background telemetry, no
automatic crash upload. Crash reports and the log stay on this computer. The only way anything
leaves it is **Help › Report a Bug** (or **Report It…** after a crash), which opens a GitHub issue
form in your browser; you read it there and submit it, or not.
What a bug report carries:
| Always (you can edit it) | Only with **Share my session** | Only with **Include the model file** as well |
|---|---|---|
| the version and OS, what happened (the last error by default), for a crash its message, place and backtrace top; your home folder is shown as `~` | the commands that built the model (`fieldcraft-session.json`, replayable with `fieldcraft-cli run`); they can name your files | the model (`model.fieldcraft`), which you attach yourself |
The form shows exactly what will be filled in before you click **Open GitHub Issue**. GitHub
refuses long new-issue links, so a session that does not fit goes out as a file instead: it is
saved to `bug-reports/<time>/` in the data folder below and copied to the clipboard, and the issue
text says to attach or paste it. The model is always a file you attach.
## Where things are
The app's data folder holds `recovery/`, `crashes/`, `logs/` and `bug-reports/`:
| System | Folder |
|---|---|
| macOS | `~/Library/Application Support/FieldCraft` |
| Linux | `~/.local/share/fieldcraft` (or `$XDG_DATA_HOME/fieldcraft`) |
| Windows | `%LOCALAPPDATA%\FieldCraft` |
Runs driven by automation (`--control`, or `CI` set) use `fieldcraft-automation/` in the
temporary folder instead, so test runs never reach your folders. `FIELDCRAFT_CRASH_DIR` moves the
crash folder, `FIELDCRAFT_RECOVERY_DIR` the recovery folder. The browser build keeps no crash
folder and logs to the browser console.
## The log file
`logs/fieldcraft.log`: one line per record, `time LEVEL [pid] target: message`. Past 2 MB it
becomes `fieldcraft.log.1` and a new one starts. Warnings and errors also go to the standard
error stream.
## Crash reports
`crashes/<YYYYMMDD-HHMMSS>-<signature>/`:
- `report.json` (`"format": "fieldcraft-crash/1"`): `kind` (`command_panic`: a command hit an
internal error, caught, the model left unchanged; `recovered_panic`: one caught inside a command;
`panic`: an internal error outside commands; `unclean_exit`: the app ended without a clean exit
— an abort, a stack overflow, a crash in a graphics driver, the process being killed — found at
the next start), `message`, `location`, `thread`, `backtrace`, `app`, `system`, `command`,
`recent_commands` (up to 32), `last_activity` (the last control request, such as
`control ui.capture`), `base`, `count`, `first_seen`, `last_seen`, `notes`
- `model.fieldcraft`: the model the replay starts from
- `replay.json`: `{"commands": [{command, params}, …]}`
- `log.txt`: the crashed run's lines of the log
The next start after a crash asks "FieldCraft closed unexpectedly": **Report It…** opens Report a
Bug for that report.
### For agents
| From | List | One report | Replay |
|---|---|---|---|
| Command (MCP `execute`, control channel, scripts) | `diagnostics.crash_reports` | `diagnostics.crash_report {id}` | |
| CLI | `fieldcraft-cli crashes [--json]` | `fieldcraft-cli crashes show <id>` | `fieldcraft-cli crashes replay <id>` |
`crashes replay` opens the report's model, runs its commands (files they write can only go to a
scratch folder) and says whether the same internal error happens again. `crashes dir` prints the
folder, `crashes clear [<id>]` deletes reports. `diagnostics.bug_report` returns the draft (link,
fields, attachments) without opening anything; over the control channel `ui.start
diagnostics.bug_report` opens the form, `ui.prompt {"answer": "set", …}` sets its fields, and
`ui.inspect` shows the draft. Only a click opens the browser.
**Check that reporting works:** with `FIELDCRAFT_CRASH_TEST=1`, `diagnostics.crash_test {mode}`
crashes on purpose: `panic`, `recovered` or `abort` (reported at the next start).
### How it stays cheap and small
The app keeps a journal of the window's top-level commands (`crashes/sessions/*.jsonl`, at most
512 KB, two small writes per command that changes the model, none for commands that only read),
restarted from each autosave and each model opened. A clean exit deletes it; it is locked while its
app runs, so a journal nobody holds belongs to an app that died. A crash is identified by its
place and message, numbers ignored: repeats only raise `count`. At most 5 new reports per run,
20 reports, 100 MB, 90 days; a model over 20 MB is not copied. Tests, the CLI and the browser build
never install the hook.
Adapted from SolidCraft's crash reports (MIT OR Apache-2.0, see ATTRIBUTION.md).