Agent-built designs: stop redoing whole-design work on every command (#1148)

A design built over MCP is many small commands, and each one did work over the
whole design, so building or showing a 1,300-body design took minutes:

- moved components were transformed and meshed again after every change; world_state
  now reuses each placed body whose definition and placement are unchanged
- next_d_name searched the design once per candidate number
- Document::eval evaluated every parameter for every expression; now only when the
  expression names one
- param_rows' used_by was quadratic; the desktop no longer gathers completions on every
  change while no text box has focus

solidcraft-cli bench-agent builds a generated ~1,300-body bicycle command by command
and times it (241 s -> 16 s here). Eval output is unchanged apart from timings.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
bherbruckandClaude Opus 5.5 authored and GitHub committed 2026-10-14 13:48:39 -04:00
1 parent 37547f8eba
commit 6de8f6db95
11 files changed
+439 -21

No files matched your search

+21 -8
View File
@@ -173,6 +173,8 @@ struct WorldCache {
active_component: u64,
active_occurrence_path: Option<Vec<u64>>,
world: Arc<solidcraft_doc::ModelState>,
/// The moved components' bodies as placed for `world`, reused by the next world state.
placed: Arc<std::sync::Mutex<solidcraft_doc::PlacedBodies>>,
}
impl WorldCache {
@@ -395,17 +397,27 @@ impl Session {
{
return cached.world.clone();
}
let mut w = if self.pending_moves.is_empty() {
solidcraft_doc::world_state(&self.doc, &st)
} else {
let mut d = (*self.doc).clone();
for o in &mut d.occurrences {
if let Some(m) = self.pending_moves.get(&o.id) {
o.transform = *m;
// The bodies the last world state placed: unchanged ones are not transformed and meshed
// again (a design built command by command changed one body at a time).
let placed = self.world_cache.lock().ok().and_then(|c| c.as_ref().map(|c| c.placed.clone())).unwrap_or_default();
// Taken out while placing, so a scratch copy of the session sharing it never waits.
let mut reuse = placed.lock().map(|mut p| std::mem::take(&mut *p)).unwrap_or_default();
let mut w = {
if self.pending_moves.is_empty() {
solidcraft_doc::world_state_reusing(&self.doc, &st, &mut reuse)
} else {
let mut d = (*self.doc).clone();
for o in &mut d.occurrences {
if let Some(m) = self.pending_moves.get(&o.id) {
o.transform = *m;
}
}
solidcraft_doc::world_state_reusing(&d, &st, &mut reuse)
}
solidcraft_doc::world_state(&d, &st)
};
if let Ok(mut p) = placed.lock() {
*p = reuse;
}
// Sketch definitions are displayed once, at the placement currently being authored.
if self.active_occurrence_path.is_some() && self.active_component != 0 {
let first = self.occurrence_placement(&self.doc.first_path(self.active_component)).map(|p| p.world).unwrap_or(solidcraft_doc::IDENTITY);
@@ -440,6 +452,7 @@ impl Session {
active_component: self.active_component,
active_occurrence_path: self.active_occurrence_path.clone(),
world: w.clone(),
placed,
});
}
w