docs: record smooth loft and performance evidence

This commit is contained in:
2026-09-15 20:00:54 +02:00
parent 218e42ac49
commit 2626c78387
5 changed files with 117 additions and 102 deletions
+18 -46
View File
@@ -684,55 +684,27 @@ real shell). What is still absent is changing the SEQUENCE — adding a fit poin
spline, or removing one — which needs the same "remove a sketch entity" primitive entry 6's own
residue now names.
### 10. Loft is ruled only, with no tangency and no guides. — **PARTIAL, INVESTIGATED 2026-08-29, NOT A FLAG TO FLIP**
### 10. Smooth section lofts ship; tangency and guides remain absent. — **PARTIALLY RESOLVED 2026-09-15**
**Evidence.** `FeaturePayload::Loft` documents the choice explicitly and honestly: *"The
sections are **ruled** — straight between neighbours — and that is a naming decision before it
is a geometry one … Locality is what the key exists for, so ruled is what ships."*
`FeaturePayload::Loft` now persists `LoftMode::{Ruled, Smooth}`. Smooth mode
uses OCCT's exact global B-spline interpolation through every section; it is not
a per-span smoothing flag and does not polygonize spline sections. The real
shell exposes the mode for creation and editing, including ordered section
navigation, preview/cancel/apply, explicit body targets, persistence, and
STEP/STL export. The five-section ergonomic-handle workflow is the driven
acceptance path.
**Absent:** smooth/G1 continuity between sections, tangency to an adjacent face, guide rails,
a centreline, and per-section takeoff weights.
The earlier naming objection was valid and is now paid explicitly rather than
ignored. Ruled spans retain local `Bridged` keys. Smooth lateral faces use
`SmoothColumn` with the complete OCCT-reported section-edge set, naming-store
v6, and fail-closed completeness/ambiguity checks. Any section-list change may
re-mint every smooth column; that is the documented non-local identity contract.
**The trade is recorded, and this session independently re-confirmed it against the real OCCT
header rather than trusting the comment alone.** `BRepOffsetAPI_ThruSections`'s `ruled` flag
is a WHOLE-SHAPE constructor parameter, not a per-span setting — `SetContinuity`, `SetParType`
and the rest of its smoothing controls are all parameters of ONE global B-spline-surface fit
through every section at once. There is no OCCT mechanism to smooth "just between these two
adjacent sections": a smoothed loft is one continuous surface, so there is no local pair of
sections left to key `Bridged`'s identity by. This is structural, not a missing flag —
implementing it as `Loft { smooth: bool, .. }` on the existing key would be building on a
locality claim that does not hold.
**What a correct future design needs, if pursued:** either (a) a genuinely new, explicitly
NON-local `ProvenanceKey` variant naming the whole section set per column — editing *any*
section would re-mint *every* column's identity, a real and different trade-off nothing else
in this naming model has, needing its own `NAMING.md` amendment and explicit sign-off before
implementation, not an autonomous call during a feature pass; or (b) tangency to an adjacent
FACE at just the loft's start/end — a different geometric question from whole-interior
smoothing, not yet investigated, that may or may not have the same locality problem and is
worth checking on its own rather than folded into "smooth loft." Guide rails (a second picked
curve constraining the loft's shape) are separate again and unexamined either way.
**The practical answer, decided 2026-08-30: a synthetic near-boundary section, not the
non-local key.** Asked to choose between building option (a) above and deferring, the call was
to defer — the full `ProvenanceKey` variant is real and buildable, but its cost is open-ended
and ongoing (any edit to a smoothed loft's section list, anywhere, re-mints every downstream
reference to any of its faces, for as long as the feature exists), and nothing today asks for
true G1 continuity hard enough to justify paying that price speculatively (CLAUDE.md: "do not
write speculative abstraction for phases that do not exist yet"). A zero-risk alternative
already ships everything this codebase's own loft supports: insert an extra section curve close
to the real end, shaped to approximate the desired tangent direction, and loft through it —
ordinary ruled lofting, ordinary `Bridged` keys, no kernel change, no naming change, no version
bump, available today with the existing `AddLoft`/`SetLoftSections` commands **at the document
layer only** (corrected 2026-09-02: `SetLoftSections` is on `NOT_YET`, so no gesture in the
running app repoints a loft's sections, and the workaround cannot be performed there). It approximates
G1 continuity rather than proving it exactly, which is the honest trade for paying nothing.
**The full design is not lost, only shelved:** the non-local `ProvenanceKey::SmoothColumn` (or
equivalent) — its shape, its dormancy behaviour, its invasiveness, its save-format impact — was
worked out in the same research pass that produced this recommendation, and is ready to build
later, with a known price tag rather than a guessed one, if real use ever demonstrates the
synthetic-section workaround is not good enough. No implementation this pass; this entry stays
open only as a record of the decision, not as unfinished work.
Still absent are G1/G2 attachment to neighboring faces, guide rails, centerline
lofts, periodic lofts, and per-section takeoff weights. A smooth section loft
therefore promises interpolation through its sections only. Those remaining
features require separate geometry, naming, and interaction designs and are not
implied by the smooth control.
### 11. Sweep pins its path to the world XZ plane and offers no orientation control. — **NARROWED TO ONE ITEM**
+10 -6
View File
@@ -511,12 +511,16 @@ Amendment from the loft (2026-08-10):
the next person to add an N-input operation should read first:** `sources`
cannot be read off ordinal position, for any operation, without a
measurement saying it can.
- **Ruled, not smoothed, and that is a naming decision.** A smoothed loft
makes one face per column spanning *all* sections, so its `sources` would
be the whole section set and adding a section anywhere would re-mint every
side face. Ruled gives each face exactly two sources, which is the
locality the key exists for. If smoothing ever ships as a flag, flipping
it is a deliberate one-time id churn behind the format version.
- **Ruled and smooth have distinct provenance.** A ruled span keeps
`Bridged { sources: {lower, upper}, sibling }`. A smooth loft makes one
lateral face per complete column spanning every section, so it uses
`SmoothColumn { sources, sibling }` (tag byte 11) with the full
OCCT-reported section-edge set. The evaluator refuses a column unless
every section contributes exactly one edge and every reported face maps
unambiguously. Adding/removing/reordering a section therefore deliberately
re-mints smooth lateral ids; switching ruled/smooth is likewise deliberate
identity churn. Naming-store v6 makes that new spelling explicit rather
than widening persisted `Bridged` keys.
- **`sibling` ships at 0 and is unexercised**, deliberately. The façade
refuses any span claiming more than one face, so a source pair names at
most one face per operation. The field is in the key anyway because adding
+30 -26
View File
@@ -36,51 +36,55 @@ is rejected before aggregation.
The default is five samples. `--samples` accepts 1 through 20; the focused
contract uses one to keep developer feedback short.
`preview_ms` is specifically disposable command-plus-compile time on an
isolated document/store copy. It does not include egui input dispatch, worker
queueing, GPU upload, or rendering, and therefore is not an end-to-end UI
latency claim.
`preview_ms` is specifically disposable command, compile, and CPU tessellation
time on an isolated document/store copy. Schema v2 breaks that number into
`preview_edit_ms`, `preview_compile_ms`, and `preview_tessellate_ms`. It does
not include egui input dispatch, worker queueing, GPU upload, or frame
presentation, and therefore is not an end-to-end UI latency claim.
## Release result
Command:
```bash
VERNIER_GIT_REVISION='069660ca9f605795b29cf51ec1b2d191976c44ab+task11.11d123938648359b0a78d4b2233866d0fdc921e50183312dff33eac049ee88e0' \
cargo build --locked --release -p vernier-cli
target/release/vernier-cli --bench-workflows --json > target/performance/workflows.json
target/release/vernier-cli --bench-workflows --json --samples 5 \
> target/bench-workflows-5-samples.json
```
The embedded `build_revision` combines the immutable base `HEAD` with SHA-256
over the per-file SHA-256 rows of the seven Task 11 Rust source/test paths. A
normal build without `VERNIER_GIT_REVISION` reports the explicit fallback
`unknown`; this release artifact was checked to contain the exact value above,
not the fallback.
This locally built measurement binary reports the explicit fallback revision
`unknown`; it was built after the performance implementation commit `ffc1fdb`
and before script-only commit `218e42a`. The final release build and gate remain
the source-revision authority, not this descriptive timing file.
Machine/build metadata reported by the artifact: release profile, VernierCAD
0.0.1, CachyOS, Linux 7.2.3-1-cachyos, x86_64, 28 logical CPUs.
| Workflow | Cold median / max | Preview median / max | Apply median / max | Tail median / max | Peak RSS | Features / faces / triangles |
| Workflow | Cold median / max | Preview median / max | Compile / tessellate median | Apply median / max | Tail median / max | Peak RSS |
|---|---:|---:|---:|---:|---:|---:|
| L2 enclosure | 19.861 / 25.149 ms | 19.477 / 25.765 ms | 19.361 / 29.635 ms | 14.787 / 26.003 ms | 38,543,360 B | 7 / 15 / 460 |
| L3 rod holder | 192.719 / 198.463 ms | 221.036 / 230.842 ms | 219.498 / 229.333 ms | 192.881 / 198.792 ms | 57,208,832 B | 8 / 15 / 224 |
| L4 multi-body | 122.239 / 135.137 ms | 121.285 / 131.888 ms | 121.146 / 126.476 ms | 98.299 / 100.643 ms | 38,543,360 B | 40 / 80 / 280 |
| L2 enclosure | 19.814 / 19.891 ms | 22.264 / 28.670 ms | 19.573 / 2.693 ms | 19.473 / 27.767 ms | 14.867 / 15.762 ms | 38,383,616 B |
| L3 rod holder | 192.896 / 200.786 ms | 233.091 / 237.528 ms | 223.734 / 1.885 ms | 219.813 / 221.889 ms | 189.909 / 192.890 ms | 57,180,160 B |
| L4 multi-body | 122.239 / 131.595 ms | 128.830 / 143.993 ms | 121.271 / 7.455 ms | 123.606 / 124.435 ms | 98.074 / 101.065 ms | 39,227,392 B |
Raw artifact: `target/performance/workflows.json` (SHA-256
`bd3d38340a9e0b67317add851bdd8117461ca0fe28290519906620fc6756c9d8`).
The rows retain 7/15/460, 8/15/224, and 40/80/280
features/faces/triangles respectively. Raw artifact:
`target/bench-workflows-5-samples.json` (SHA-256
`73ef32318935d334f3ccf205ac4710ddd2c32176f7f39293179393fa7f85030f`).
## Reading the result
L2 medians are around 15–20 ms for the measured modeling operations. L4's
L2 medians are around 15–22 ms for the measured modeling operations. L4's
40-feature multi-body history is around 98 ms for a steady tail revert and
121–122 ms for cold compile/apply. L3 is the clear interaction bottleneck: its
121–129 ms for compile/preview. L3 is the clear interaction bottleneck: its
upstream spline edit rebuilds the exact sweep, spline-section loft, named-edge
fillet, and direct edit chain. Preview/apply medians are about 220 ms, while the
tail revert is about 193 ms.
No optimization is included in this change. The L3 cost is distributed across
the full downstream exact-geometry and naming rebuild, not isolated here to a
single small implementation path whose behavior could be changed safely. The
next performance investigation should profile that compile before selecting an
optimization; this measurement deliberately stops at identifying the
bottleneck.
The first safe profiling cycle found no qualifying 25% change. Compilation is
96% of L3 preview time; CPU tessellation is below 1%, and authoring the command
is negligible. That compile intentionally rebuilds every downstream exact OCCT
operation and its naming history after an early path-sketch edit. Persisting or
sharing live OCCT shape handles across the disposable worker checkpoint would
change ownership and isolation contracts, while skipping history work would
weaken stable-reference guarantees. Neither is a low-risk optimization. The
instrumentation stays in place so a later kernel-level profile can target a
specific evaluator without guessing; no geometry or naming shortcut was taken.
@@ -178,12 +178,48 @@ The managed targets recorded for this baseline are:
- `previous`:
`/home/nilsb/.local/share/vernier/releases/c111f856929f6c6370cb4ff5cd094bdb39f699f2`
## Next sequence — ergonomic handle and smooth lofts
## Phase 6 — ergonomic handle acceptance — focused-verified
The next acceptance part is an ergonomic handle with a mounting base. It first
drives practical editing with the existing exact ruled loft, then becomes the
acceptance part for exact smooth section interpolation. The implementation plan
The real shell now builds a mounting base, drills two through holes, authors
five closed cubic-spline sections, previews/cancels and then applies an explicit
Join loft, fillets the base, changes the upstream hole diameter, and exercises
undo/redo. A separate worker reopens the native document and naming sidecar and
independently exports matching STEP/STL geometry. A one-cubic-millimetre false
oracle fails at its exact geometry step.
## Phase 7 — measured interaction bottleneck — complete, no speculative change
The workflow report is schema v2 and separates command authoring, exact
compile/naming, and CPU tessellation. Five fresh-process samples identify L3 at
233.091 ms median preview: 223.734 ms compile and 1.885 ms tessellation. The
first safe cycle found no qualifying 25% optimization. Sharing live OCCT handles
across disposable worker checkpoints or omitting history evaluation would cross
shape-ownership or stable-reference contracts, so neither shortcut was taken.
The measurement and evidence boundary are recorded in
`docs/PERFORMANCE_2026-09-14.md`.
## Phase 8 — exact smooth loft geometry and naming — focused-verified
`LoftMode::Smooth` uses exact `BRepOffsetAPI_ThruSections` interpolation, not a
polygonal substitute. Its lateral provenance is derived from complete OCCT
history columns; missing, duplicated, or partial history refuses the operation.
Smooth and ruled provenance are deliberately distinct, with naming-store v6.
## Phase 9 — persisted smooth-loft interaction — focused-verified
Document format 42 persists `LoftMode::{Ruled, Smooth}` and migrates format 41
to `Ruled`. The real loft builder/editor exposes both modes, keeps ordered
section navigation and removal reachable, and preserves preview/apply/cancel,
explicit target-body behavior, undo/redo, save/reopen, and export semantics.
The Phase 6 handle is the driven smooth-loft acceptance part.
## Phase 10 — release certification — pending final gate
The only remaining sequence is one full integrated gate at the final source
revision, followed by a final-SHA AppImage build/smoke, staged installed-worker
smoke, and the atomic managed-launcher switch. The existing installed release
remains untouched until all of those pass, and then becomes `previous` for
rollback.
Per-phase verification was deliberately focused. The implementation checklist
is `docs/superpowers/plans/2026-09-15-usability-organic-modeling.md`.
Per-phase verification is deliberately focused: affected tests plus the relevant
driven workflow. One full gate is reserved for the release candidate.
@@ -20,35 +20,34 @@
### Task 1: Installed baseline and handle acceptance
- [ ] Record `0.0.4-alpha` at `b0232f41` as the installed baseline and retain `c111f856` as rollback.
- [ ] Add a five-section cubic-spline handle workflow with mounting base, holes, fillets, upstream editing, persistence, and STEP/STL oracles.
- [ ] Fix only interaction blockers reproduced by that workflow, with a failing regression test before each fix.
- [x] Record `0.0.4-alpha` at `b0232f41` as the installed baseline and retain `c111f856` as rollback.
- [x] Add a five-section cubic-spline handle workflow with mounting base, holes, fillets, upstream editing, persistence, and STEP/STL oracles.
- [x] Fix only interaction blockers reproduced by that workflow, with a failing regression test before each fix.
### Task 2: Interaction performance
- [ ] Add phase timing for queueing, compile/naming, tessellation, GPU upload, and visible preview acknowledgement.
- [ ] Profile the handle and L3 workflows with five samples.
- [ ] Optimize the largest safe contributor and retain exact geometry/naming signatures.
- [ ] Accept a 25% median improvement or record that the first safe profiling cycle found no qualifying change.
- [x] Add phase timing for authoring, compile/naming, and CPU tessellation, with the queue/GPU/frame boundary stated explicitly.
- [x] Profile the representative L2-L4 workflows with five fresh-process samples.
- [x] Preserve exact geometry/naming signatures while measuring the largest contributor.
- [x] Record that the first safe profiling cycle found no qualifying 25% change: exact compile/naming is 96% of L3 preview and the obvious shortcuts cross shape-ownership or reference-stability boundaries.
### Task 3: Smooth-loft geometry and provenance
- [ ] Add a failing kernel test for a smooth five-section cubic-spline solid and its OCCT history.
- [ ] Extend the bridge and evaluator with an explicit loft mode and bounded OCCT failure handling.
- [ ] Define smooth lateral-face provenance from the complete section column and refuse incomplete or ambiguous history.
- [ ] Add reorientation, upstream-edit, invalid-solid, and broken-reference tests.
- [x] Add a failing kernel test for a smooth five-section cubic-spline solid and its OCCT history.
- [x] Extend the bridge and evaluator with an explicit loft mode and bounded OCCT failure handling.
- [x] Define smooth lateral-face provenance from the complete section column and refuse incomplete or ambiguous history.
- [x] Add reorientation, upstream-edit, invalid-solid, and broken-reference tests.
### Task 4: Persisted GUI smooth lofts
- [ ] Add `LoftMode::{Ruled, Smooth}` to the document and migrate format 41 lofts to `Ruled`.
- [ ] Add `SetLoftMode`, undo/redo coverage, and compile dispatch.
- [ ] Add a ruled/smooth control to the existing loft builder and editor, including preview/apply/cancel.
- [ ] Drive the smooth handle through edit, save/reopen, STEP/STL export, and a designed negative control.
- [x] Add `LoftMode::{Ruled, Smooth}` to the document and migrate format 41 lofts to `Ruled`.
- [x] Add `SetLoftMode`, undo/redo coverage, and compile dispatch.
- [x] Add a ruled/smooth control to the existing loft builder and editor, including preview/apply/cancel.
- [x] Drive the smooth handle through edit, save/reopen, STEP/STL export, and a designed negative control.
### Task 5: Release
- [ ] Run the focused handle and smooth-loft workflows.
- [x] Run the focused handle and smooth-loft workflows.
- [ ] Run one full integrated gate on the final candidate.
- [ ] Build and smoke the AppImage with its bundled worker.
- [ ] Switch the managed launcher atomically and retain the previous release for rollback.