Add audited, versioned MCP save workflow (#1452)

- `--export ... --target-version`, and explicit target_format /
  target_version on automation and protocol saves; a conversion that
  would drop unsupported records is refused unless allow_lossy is set.
- `audit` read op: dangling layers/blocks, duplicate handles, non-finite
  bounds, serialization, lossy records and the source DXF handle graph.
- `save_verified`: writes the requested version from the same snapshot a
  normal save writes, reopens it, checks the version and the entity
  manifest, audits the ASCII DXF handle graph and returns a SHA-256.
- ZOOM EXTENTS includes SDF text glyphs.
- Live MCP acceptance script and an optional reference-console audit
  runner under docs/automation.

Squashed from the PR with review fixes: merged with the DWT save path,
the save snapshot, the tool lists added since, and vendor-neutral
harness names; the dated verification record is left out.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
Luis MartinezandClaude Opus 5.5 authored and Hakan Seven committed 2026-09-24 16:49:11 +03:00
1 parent 0f99b77004
commit dddf60d79e
16 files changed
+1102 -22

No files matched your search

+24
View File
@@ -1428,6 +1428,19 @@ impl Scene {
sy += y as f64;
n += 1;
}
// SDF text is carried as textured glyph quads, not ordinary wire
// points. Include those vertices so a text-only wire participates
// in the robust-centre filter used by ZOOM EXTENTS.
for vertex in &wire.text_verts {
let x = vertex.pos[0] as f64 + vertex.pos_low[0] as f64;
let y = vertex.pos[1] as f64 + vertex.pos_low[1] as f64;
if !x.is_finite() || !y.is_finite() {
continue;
}
sx += x;
sy += y;
n += 1;
}
if n > 0 {
cents.push(WireCent {
idx,
@@ -1486,6 +1499,17 @@ impl Scene {
max = max.max(glam::Vec3::new(x, y, z));
}
}
for vertex in &wire.text_verts {
let point = glam::Vec3::new(
vertex.pos[0] + vertex.pos_low[0],
vertex.pos[1] + vertex.pos_low[1],
vertex.pos[2] + vertex.pos_low[2],
);
if point.is_finite() {
min = min.min(point);
max = max.max(point);
}
}
}
// Fold in 3D-solid mesh AABBs (not subject to the wire IQR reject).
for ([ax, ay, bx, by], [az, bz]) in &mesh_aabbs {