forked from 1b-agent/fieldcraft-upstream
* Native file pickers, background opening, drag and drop and recent files Open (models, SolidCraft designs and tagged meshes in one picker), Save, Save As, Write Deck and Recover go through the host's file pickers (rfd in the desktop app, injected as Services so the UI crate stays portable and tests answer them). Files open on a copy of the session in a worker thread and replace it only on success; files dropped on the window or named on the command line open the same way. A recent-files list, an unsaved-changes prompt, Cmd/Ctrl+O/S/Shift+S. Programmatic calls never open a picker: ui.start opens the form. Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_01NmAttHhUZTokgPeEJia8sm * Samples and one-click Solve: results reachable from the window file.sample {name} opens a ready-to-run sample (the two parts, the cantilever with static and frequency steps, the bar impact) in place of the model; solve.run solves every step in order. Help > Samples, the empty viewport's start screen and the Tools > Samples entry open them (asking first over unsaved changes); Solve runs in the background like a file opening and the Results tab comes up. --sample [parts|cantilever|impact] on the command line. Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_01NmAttHhUZTokgPeEJia8sm * The Craft-family shell: title bar, workspace ribbon, browser, context panel, timeline The window now follows SolidCraft's and VectorCraft's layout and themes: - title bar with the mark, quick actions (File menu, save, undo, redo, Solve, search, theme, help), the model's document tab and the version; integrated under the macOS traffic lights, own caption buttons and resize zones elsewhere (titlebar.rs); - workspaces (Prepare, Model, Analysis, Results) with their tabs, and panels of icon buttons whose labelled drop-downs list the whole catalogue, coming-soon entries greyed with their milestone; panels collapse, then go into a » menu, when narrow; - the browser: geometry and mesh with eye toggles, definitions with counts, results; - no permanent Commands list: command search (S, Cmd/Ctrl+K) and a command line; - the context panel on the right only with context: the command form, results controls, the selected definition's properties with Edit and Delete; - navigation bar and orientation gizmo in the viewport; the journal timeline at the bottom with undo positions; - SolidCraft's light and dark tokens; craft-ui (IconButton, Tab, WidgetPalette). Menus and the search are view state (ui.set {menu, palette}), so captures show them. Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_01NmAttHhUZTokgPeEJia8sm * Shell polish, docs and licences Menus painted as the family's (flat File menu with recent files, decks and samples), the legend sized to its text, two-line panel labels inside the ribbon, glyphs the UI font has, offscreen captures without fade-in, ui.start runs in-window file actions (samples, recover, solve) but never a native picker, ui.select. Control protocol, architecture, ROADMAP, AGENTS and ATTRIBUTION updated; THIRD-PARTY-LICENSES.txt with craft-ui and rfd. Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_01NmAttHhUZTokgPeEJia8sm * Title bar mark from the application icon; fluid steps and domains in the shell The title bar draws the app icon's bracket (assets/app-icon/icon-small.svg's faces) as SolidCraft draws its mark; the window keeps its title-bar flags after the rebase. solve.run solves fluid steps too (solve.fluid); the browser lists fluid domains. Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_01NmAttHhUZTokgPeEJia8sm * Simplify: cached per-frame data, one command per timeline roll, workspace setters - Icon textures are kept per pixel size (the shell draws several sizes a frame; the cache used to clear on every size change and rasterise again). - Opening a model or recovering one starts from an empty session instead of copying the current one; the replaced session is freed off the window thread. - Element counts, timeline items, search results and the selected definition's properties are cached until what they show changes; timeline tooltips are built on hover; long lists in properties are summarised. - edit.undo and edit.redo take a count: a timeline click is one command. - solve.run dispatches on the step's procedure (no string round trip) and reports its name. - The workspace and tab change together through UiState::select_tab/select_workspace (ui.set {workspace} alone now sticks); no per-frame syncing. - One busy guard, Kind::verbs, a fit flag for background jobs, ModelApp::home_view, the title bar's actions as an enum, dead code removed. Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_01NmAttHhUZTokgPeEJia8sm * Title bar: Solve and tools at the right end; no traffic-light inset in full screen The owner's feedback on the title bar: Solve, search, theme and help sit at its right end before the version; on macOS the left group (mark, name, quick actions) moves into the traffic lights' room in full screen, where there are none, and back when leaving it. The layout is a pure function (bar_layout) tested for both states, narrow windows and custom caption buttons. ui.fullscreen {on} for agents. Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_01NmAttHhUZTokgPeEJia8sm * Caps labels in Inter SemiBold egui has no bold weight of its default font, so the ribbon's tabs and panel labels, the workspace switcher and its menu, menu headings, the browser header, the context panel's headers and the Solve label were thin, especially in the light theme. They now use a label font family: Inter SemiBold 4.1 (SIL OFL 1.1, unmodified, fonts/OFL.txt, ATTRIBUTION.md), with egui's fonts as fallback, installed with the theme on the live and offscreen contexts. theme::label_font falls back to the default font until the family is bound (fonts take effect from the next pass), so nothing can panic. Panels widen to their label's longest word. Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_01NmAttHhUZTokgPeEJia8sm * docs: regenerate features.md after rebase Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_01NmAttHhUZTokgPeEJia8sm --------- Co-authored-by: jyviko <[email protected]> Co-authored-by: Claude Opus 5.5 <[email protected]>
130 lines
9.6 KiB
Markdown
130 lines
9.6 KiB
Markdown
# ModelCraft — instructions for agents
|
|
|
|
ModelCraft is a clean-room, open-source, pure-Rust CAE modeling application: it turns geometry
|
|
into analysis-ready finite element models (volume meshes, materials, sections, boundary
|
|
conditions, loads and steps), writes them as Abaqus input, Nastran bulk data and LS-DYNA keyword
|
|
decks, and solves linear static and modal problems with its own solver. It targets the
|
|
pre-processing done today in Abaqus/CAE, Nastran pre-processors and LS-PrePost, and aims to beat
|
|
them on meshing robustness, speed, openness and agent control.
|
|
|
|
Siblings with the same conventions: `../solidcraft` (parametric CAD, Fusion-class), and the
|
|
storytold Craft apps (`storytold/cadcraft`, `vectorcraft`, `photocraft`, …). Code adapted from
|
|
them (MIT OR Apache-2.0) is noted in ATTRIBUTION.md.
|
|
|
|
## Product name
|
|
ModelCraft everywhere: crates `modelcraft-*`, binaries `modelcraft` and `modelcraft-cli`,
|
|
`.modelcraft` models, `MODELCRAFT_*` environment variables, docs, CI and packaging.
|
|
|
|
## Start every session here
|
|
1. Read `ROADMAP.md` (milestones, what is next) and your task.
|
|
2. `docs/architecture.md`: crates, layers, the meshing pipeline.
|
|
3. `docs/adr/`: library decisions (what we may depend on, what is rejected for licence reasons).
|
|
4. Meshing work: `docs/meshing/literature.md` (the papers each algorithm comes from).
|
|
|
|
## Never crash
|
|
People trust ModelCraft with their models; a crash loses their work. **This outranks feature work.**
|
|
- **No panics in non-test code:** no `unwrap()`, `expect()`, `panic!`, `unreachable!`, `todo!`,
|
|
`unimplemented!`; no `unsafe` (`unsafe_code = "forbid"`).
|
|
- **Errors are `Result<T, E>`** through the crate's error type and `?`. An unfinished feature
|
|
returns an error ("not supported yet: …"); it never panics.
|
|
- **Input-derived numbers are hostile** (command parameters, model files, solver decks,
|
|
control-channel input): `get()` not `[i]`, finite checks, caps on sizes and loop counts.
|
|
- **Bound every iterative algorithm.** Mesh refinement, smoothing, untangling and solver
|
|
iterations have explicit iteration and element-count caps and report when they stop early.
|
|
- **SolidCraft calls are guarded:** everything that calls `solidcraft-kernel` goes through
|
|
`solidcraft_kernel::guard` inside `modelcraft-geometry`, which turns a panic into an error.
|
|
- **Last-resort guard:** `Session::execute` runs every command under `catch_unwind`; an error or
|
|
an escaped panic restores the model and reports `EngineError`.
|
|
- **Prove it:** every crash fix lands with a regression test (see `hostile_params_never_panic`).
|
|
- Every production crate root carries
|
|
`#![deny(clippy::unwrap_used, clippy::expect_used, clippy::panic, clippy::unimplemented, clippy::todo, clippy::unreachable)]`.
|
|
|
|
## Non-negotiables
|
|
- **Independent.** Behaviour is designed from public documentation, published papers, general
|
|
CAE conventions and user feedback. Never copy another product's code, icons, artwork, help
|
|
text, element or material libraries, templates or examples.
|
|
- **No commercial CAE products.** Agents must not drive, script, automate or extract data from
|
|
commercial CAE or meshing products (Abaqus, MSC/NX/Simcenter Nastran, LS-DYNA, LS-PrePost,
|
|
HyperMesh, ANSA, Femap, Patran, ANSYS, …): no test data, measurements, decks, results or ids
|
|
produced with them. Deck formats come from the vendors' publicly available reference manuals;
|
|
we write our own descriptions and our own examples, never copy manual text or example decks.
|
|
- **Verification and validation (docs/verification.md).** Every solver capability is checked
|
|
three ways, each case in `tests/oracle/` tagged with its source: **theory** (closed-form
|
|
solutions, patch tests, mesh invariants), **experiment** (measurements published in papers and
|
|
reports) and **published benchmarks** (reference results published by benchmark bodies and by
|
|
solver vendors' benchmark and verification manuals, e.g. NAFEMS, Abaqus). Owner decision
|
|
2026-10-13: published vendor results may be used as reference values, cited per case. We never
|
|
run a commercial product, never copy its input files, text or figures: each case is our own
|
|
model built from the published description, and only the published numbers are used. Once our
|
|
result matches its reference within the stated tolerance, it is frozen as a **fixture**
|
|
(`fixture.json`), so any later change that moves it fails CI (fidelity never drifts silently).
|
|
- **Algorithms come from papers.** Meshing and solver methods are implemented from published
|
|
papers and theses, cited in the module docs and in `docs/meshing/literature.md`.
|
|
- **Never read, copy or link GPL/LGPL/AGPL code**: TetGen, CGAL, Gmsh, Netgen, CalculiX,
|
|
Code_Aster, OpenRadioss, Elmer, deal.II, FreeFEM, Salome, OpenFOAM — don't even read their
|
|
sources. Permissively licensed code (MIT, BSD, Apache-2.0, MPL-2.0) may be read for reference;
|
|
anything adapted is noted in ATTRIBUTION.md with its licence.
|
|
- **Assets — absolutely essential.** ModelCraft contains **no iconography, images, fonts or
|
|
artwork from commercial CAE products — ever.** Icons are original artwork made with VectorCraft:
|
|
commit the editable `.vectorcraft` source beside the exported SVG/PNG. Any file asset must be
|
|
original or openly licensed and **must have a row in ATTRIBUTION.md** (`cargo xtask assets`
|
|
enforces it). Screenshots of commercial software are never committed.
|
|
- **Test geometry is our own.** Meshing and solver test parts are built with SolidCraft and
|
|
committed as recipes (scripts or designs) that regenerate them; analytic shapes are generated in
|
|
code. No public CAD datasets (ABC, MAMBO, Thingi10K, GrabCAD) and nothing made in a commercial
|
|
CAD or CAE product.
|
|
- **Everything is a command.** User-visible behaviour = a command in `crates/engine/src/cmd/*`
|
|
(`CommandSpec`: our own dotted, lower-case id `<area>.<thing>[.<variant>]` such as
|
|
`mesh.node.add` or `mesh.tet.generate`, never another product's keyword; label; params doc;
|
|
JSON `run`) + tests. The UI, scripts, CLI and control channel all reach the same commands.
|
|
- **Programmatic calls never open dialogs.**
|
|
- **Layering** is enforced by `cargo xtask layers` (`docs/architecture.md`): L0 `model` → L1
|
|
`geometry`, `mesh`, `fem`, `decks`, `render` → L2 solvers (`solver`, `lbm`; `explicit` later) →
|
|
L3 `fsi` (later) → L4 `doc` → L5 `io` → L6 `engine` → L7 `ui-egui`, `mcp` → apps. Nothing below
|
|
L7 depends on egui/eframe/winit/wgpu/rfd. **Only
|
|
`crates/geometry` may use `solidcraft-*`.**
|
|
- **Determinism.** The same input gives the same mesh, deck and CPU solver result, bit for bit, on
|
|
every platform: no hash-order iteration into output, no thread-count-dependent results. GPU
|
|
solver backends (wgpu, f32) are the one exception: deterministic per device, and checked against
|
|
the CPU reference within a stated tolerance.
|
|
- **Robust geometry.** Meshing decisions (orientation, in-sphere, intersection) use exact or
|
|
adaptive-precision predicates, never a bare floating-point sign.
|
|
- **Units.** The model is unit-consistent and never converts (the user picks e.g. mm, t, s, N,
|
|
MPa); geometry from SolidCraft is in millimetres. Z is up.
|
|
- **Rust only.** **Never break wasm**: everything below the apps builds for
|
|
`wasm32-unknown-unknown` (`cargo xtask wasm`, part of `cargo xtask ci`).
|
|
- **Quality gates:** GitHub CI runs `cargo xtask ci` (fmt, assets, licences, layers, clippy -D
|
|
warnings, tests, wasm) on every PR; don't run the full gate locally. Before pushing: `cargo fmt
|
|
--all`, `cargo check` the touched crates, and run the tests you added or changed. Commit after
|
|
each working step (a layer that compiles with its tests passing), not one big commit per PR.
|
|
- **Keep the PR gate fast** (under ~3 minutes on CI). A test that takes more than a few seconds
|
|
(convergence studies, the whole corpus, cross-checks between solvers) is
|
|
`#[ignore = "heavy: cargo xtask test --heavy (CI heavy job)"]`, with a fast representative
|
|
test left in the PR gate. The `heavy` CI job runs `cargo xtask test --heavy` (every ignored
|
|
test, benchmarks included, and the doc tests) on pushes to `main`, nightly and on demand
|
|
(`gh workflow run CI --ref <branch>`). Hostile-input tests run commands on sessions with small
|
|
work caps (`Session::limits`) so refusals are instant; never lower the production caps for a
|
|
test.
|
|
- **Publishing**: `origin` is github.com/jyviko/modelcraft. Changes land through pull requests:
|
|
branch, **rebase onto the latest `origin/main` right before opening the PR and before every push
|
|
that updates it** (never merge `main` into a branch), push, open a PR, let CI run. A PR merges
|
|
(`gh pr merge --squash`) only after review and only when its green CI ran on top of current
|
|
`main` (`mergeStateStatus` CLEAN; rebase again if `main` moved); after a merge, `main`'s own CI
|
|
must pass. Never force-push `main`. Never commit `plan/`, `.agent-files/`, `worktrees/` or
|
|
`target*/`; check `git ls-files` before pushing.
|
|
|
|
## Running
|
|
- `cargo run -p modelcraft-cli -- commands` lists every command with its parameters.
|
|
- `cargo run -p modelcraft-cli -- run script.json` runs a script (a JSON array of
|
|
`{"command", "params"}`) and prints the model summary.
|
|
- `cargo run --release -p modelcraft -- --sample [cantilever|impact] --control PORT` opens the desktop app with a
|
|
sample model and a control channel on `127.0.0.1:PORT` (docs/control-protocol.md). For UI work,
|
|
look at the result: `ui.capture {path}` (the whole window drawn offscreen; works with the
|
|
screen locked), `ui.screenshot {path}` (a presented frame) or `ui.render {path}` (the model
|
|
only), then read the PNG.
|
|
- Build with `CARGO_TARGET_DIR=<repo>/target`. Parallel agents: separate target dirs, one
|
|
worktree each.
|
|
|
|
## Roadmap
|
|
`ROADMAP.md` (committed) tracks milestones. Update it whenever a milestone task lands.
|