diff --git a/docs/brush-tree-target.md b/docs/brush-tree-target.md index c50fa4a..a2c4bd7 100644 --- a/docs/brush-tree-target.md +++ b/docs/brush-tree-target.md @@ -1,256 +1,148 @@ -# Brush tree: target +# Brush tree: target (r2) -Campaign `brush-tree`, area geometry, owned by the Aetheria/Ghostlight Self -(`aetheria-release:ruling:brokkr-ctl-job-to-ag-self`). Drafted by Imagination -(`imagination-brush-tree-target`, session `self-2026-10-10-ag2`) on 2026-10-10. -This is step 0: the ends, not the means. It contains no cut plan. +Campaign `brush-tree`, owned by the Aetheria/Ghostlight Self. Drafted by Imagination +(`imagination-brush-tree-target-r2`, session `self-2026-10-11-ag2`) on 2026-10-12. +It supersedes r1 (`49948c8b`, the CSG-only target). This page holds the ends, not the means: +no cut plan. The typed `brush-tree:target:r2` holds the invariants, the not-in-scope list and the +canonical implementations; this page holds why, and the numbers behind the bars. -- **Title:** Brush tree: OpenSCAD-shaped CSG trees authored in Blender through `brokkr-ctl`. -- **Repos:** `GameCult/GameCult.Geometry.Csg` (the truth and the kernel), - `GameCult/Brokkr` (the agent's hands in Blender), `GameCult/Aetheria` - (the first consumer), and `GameCult/CultLib`, only if a ruling sends work - there (the cultcache-py directory store, Q `blender-transport`). - All four remotes are GitHub today; Aetheria moves to Forgejo under - `aetheria-release:ruling:forgejo-primary-github-mirror`. -- **Working branch:** `eureka/brush-tree` in each repo it touches. -- **This document's home:** `GameCult.Geometry.Csg/docs/brush-tree-target.md`. - The crate owns the truth the campaign builds, and two of the four research - passes already live beside it in `docs/research/`. +- **Repos:** `GameCult/GameCult.Geometry.Csg` (the tree, the field, the kernel, the scorer), + `GameCult/Brokkr` (the agent's hands in Blender), `GameCult/Aetheria` (the first consumer). +- **Working branch:** `eureka/brush-tree`. -## Rationale +## The rulings this target rests on -The operator rebuilds Tripo-generated hard-surface hulls by hand: retopology, -seams and bevels, at about 30 minutes per part. The campaign replaces that with -a collaborative loop in Blender. The work is split by what each party does -well: +Read them in the mind by id; their operator quotes are the authority, not this page. -- **The operator** points at part of the messy hull, using her Edit Mode - selection plus the viewport framing. -- **The model** proposes a parametric form, or builds the one she names ("these - pipes are tubes", "a sphere, solidified, with a prism cut out"). It does the - naming, grouping, symmetry and composition. -- **A classical fitter** does the numbers: least-squares or RANSAC primitive - fitting, plus GlobFit-style relations. -- **Feedback** is a deviation heatmap plus a viewport render, iterated. +- `brush-tree:ruling:representation-per-hull`: subdivision limit surfaces for continuous bodies like + Dexter Quiet, primitives for mechanical hulls like the squatter mech ship; both paths, per hull or + per part. It narrows the "mesh CSG is the main representation" clause of + `settled-direction-2026-10-10` to mechanical hulls. +- `brush-tree:ruling:pursue-sdf-composition-remeshing`: the continuous path is an SDF composition + remeshed automatically; viability rests on the remesher; the operator's own Dexter Quiet rebuild is + the reference it is measured against. +- `brush-tree:ruling:shippable-part-bar`: clean unbroken flow, part-coherent low-distortion UVs, + one-segment bevels with face-weighted normals. +- `brush-tree:ruling:no-unownable-tools-in-loop`: only code we can own is in the loop. +- `brush-tree:ruling:spike-sdf-smin-to-cage`, `:spike-instant-meshes-field-features`: the spikes + whose results (runs 4 and 7) this target reads. +- Unchanged from r1 and still in force: `openscad-target`, `brokkr-beats-blender-mcp`, `cli-not-mcp`, + and the defaulted rulings on the Blender sink, `run`, docs lookup, look files, the per-ship tree + `.cc`, persistence through cultcache-rs, the Package path, Studio and impostors. -The output is a typed CSG brush tree. From that one tree come the lowered mesh, -its UVs, its topology near cuts and its microbevels. +## What changed from r1, in one paragraph -Why this shape, by the evidence: +r1 lowered every hull through mesh CSG with field-derived microbevels. The probes showed CSG cannot +build Dexter Quiet's continuous curvature (run 2), and the operator ruled the representation per hull. +A hand-designed cage (run 3, v7: chamfer 0.228 %) beat every automated route; SDF composition with +smooth-min fuses the body and its sweeps "without breaking a sweat" (run 4) but its program fidelity +(0.52 %) and the remeshers' flow (runs 5-7: open field loops, 38-69 poles per 1000 vertices, UV area +distortion 6-10x the designed cage's) fall short. Run 7 also found that the fidelity wall belonged to +the SDF program, not the remesher: measured against the SDF itself, every remesh sat at 0.01-0.07 %. +Microbevels from the field are gone: the bar names one-segment mesh bevels. -- **Model fluency.** CAD-Recode and MeshCoder emit code (CadQuery, Blender - Python) precisely because language models write code fluently - (CultLib `docs/research/inverse-procedural-modelling-prior-art.md` §1, - 6bdc23c9). OpenSCAD is a CSG tree written as text, with a large public - corpus. -- **Text-only loops fall short.** BlenderGym shows the best VLM still far - behind humans on geometry editing (Chamfer 1.120 vs 0.334, same doc §4). - BenchCAD reports single-literal errors at high IoU. That is why numbers go to - a fitter and judgement goes to the model and the operator, not to unaided - generation. -- **Failure without coherence.** VibeGeometry's recorded failure was "local - plausibility without global coherence" (same doc §0b). The defence here is a - typed tree with one owner, scored against the target by one distance function. -- **Brushes are the durable design surface.** The crate's own doctrine says "the - brush tree is the authority; the mesh is cache and evidence" - (`docs/realtime-csg-doctrine.md`, Core Model). This campaign makes that true - of persisted state, not only of the in-memory assembler. -- **The bar is the Blender MCP servers, and Brokkr exceeds it.** They offer - viewport sight, code execution and API docs, but none reads selected Edit Mode - geometry, and none has auth or admission (Brokkr - `docs/research/blender-mcp-capability-bar.md` §A(b), §B(b), f97d4dc). Brokkr - already has typed intents, receipts and admission on its Unity side - (`docs/provider-contract.md`, Admission). +## What the pipeline turns a brush tree into -## Body facts the target rests on (probed 2026-10-10) +**The tree** is one typed document per ship (`tree-store-per-ship-cc`): primitives, transforms, +scoped booleans, and smooth unions with a named radius. Every part (top-level branch) declares its +representation, `field` or `kernel`. The declaration is authored, never inferred from the shape. -- **GameCult.Geometry.Csg** (main at `5b5dc34`): - - Five primitives: Box, OrientedBox, CylinderZ, DomeCapZ, FloretArm (`src/brush.rs`). - - The polygon kernel runs CSG only for Box and OrientedBox cutters. It skips - other cutters with `SubtractIgnoredForNonBox` / `IntersectIgnoredForNonBox` - (`src/assembler.rs` ~803, ~836). - - The assembler and `field.rs` consume one flat ordered stream in which - Intersect is global. `CsgTree` branches exist in `src/tree.rs`, but the - assembler does not honour them as scoped nodes. - - `field.rs` lowers the stream to a signed-distance bound for every op on - every primitive. It has no smooth blend. - - `TriangleMesh` gives every quad 0-1 UVs. It carries per-triangle material - only: no brush id, surface id, smoothing or tangent (`src/mesh.rs`). - - `Brush` and `Primitive` derive no serde. - - The "CultCache records" in `src/cult_geometry.rs` are `rmp_serde` - msgpack tuples with schema-string constants. They are not written through - CultLib's `cultcache-rs`, and no record holds brushes. -- **Brokkr** (main at `f97d4dc`): - - The Blender add-on admits eight object-level commands - (`surfaces/blender/brokkr_bridge/blender_target.py:402-423`). Its snapshot - reports mesh counts only (`:568-575`). It has no render, no mesh read, no - Edit Mode read and no code execution. - - Its store is the cultcache-py single-file store. cultcache-py has no - directory store (`CultLib/packages/cultcache-py/src/cultcache_py/stores.py:22,85`). - - Its optional CultNet server binds 127.0.0.1 with no auth token - (`blender_target.py:288-350`). - - `brokkr-command` is a .NET, Unity-only caller (`brokkr-command/Program.cs`). - `brokkr-ctl` does not exist. - - The Unity side's rulings F4 to F6 settle a file transport, a sink that is - off by default, and single-use command ids - (`docs/brokkr-adoption-cut.md`, Rulings). -- **CultMath:** `BoundedLeastSquares` exists in C# - (`CultLib/packages/cultmath/src/CultMath/BoundedLeastSquares.cs`). -- **Mesh probe:** the probe running under rulings `mesh-probe-exploit-not-measure` - and `mesh-probe-both-vocabularies` has not written `run1/RESULT.md` yet. - Its scratch `demo-hull.scad` already shows the model reaching for top-level - variables, `module`, `hull()`, `minkowski()`, `polyhedron`, `mirror` and - `cylinder(r1, r2)`. +**A `field` part** (continuous body, Dexter Quiet): -## Design truths the campaign produces +1. Field: the part's signed-distance program, every primitive and blend defined once in Geometry.Csg. + Hard edges the author wants crisp are declared as creases (a hard `max`, run 4's wing rim), not + detected afterwards. +2. Cage: a coarse quad-dominant control cage whose crease edges lie on the declared creases and form + closed loops, refit so its Catmull-Clark limit surface lies on the field's zero set (run 4's refit, + run 3's least-squares in a mirror-reduced space). +3. Evaluated mesh: the operator's own stack, Mirror, Subdivision (limit surface), Bevel (one segment, + on the creases), Weighted Normal (`dexter-quiet-cage/README.md`). Seams lie on the closed crease + loops and part boundaries; run 7 showed seams through a broken crease join wing top and bottom. +4. Output: the cage with its stack, as an object in the ship's `.blend`; Package applies and exports it. -1. Each hull rebuilt through the loop exists as one typed brush-tree document in - CultCache. Everything else is derived from it: text, mesh, UVs, bevels, - heatmap, projections into Blender. -2. The model reads and writes that tree as OpenSCAD-shaped text. The operator - reads and writes it through Blender. Both edits land through one commit path. -3. An agent drives Blender with `brokkr-ctl blender look | selection | fit - | run